From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-yb1-xb2a.google.com (mail-yb1-xb2a.google.com [IPv6:2607:f8b0:4864:20::b2a]) by sourceware.org (Postfix) with ESMTPS id AB4FD3858C53 for ; Thu, 14 Apr 2022 18:01:36 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org AB4FD3858C53 Received: by mail-yb1-xb2a.google.com with SMTP id m132so10846716ybm.4 for ; Thu, 14 Apr 2022 11:01:36 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=ibR1/t0vMKAVXFBsVbY7SDaFPVGT/MJIGGl9fWnz7SY=; b=J+p1Tgk65Ia4QH/9hNAdO3kp5EMA59XuRrCVJq6Obp/LnWlmA+6RtP1PvDzj4YjKSK cXjHtPLhFfUDS7g5HbsXIyiC4t6ES7Zsbb73vg3Pb+mOw+LaokWnGWgEAX/gnyWvUROe 5BbwmgHAxq8F0SKBLJXYqwFKYckqVw+yv2y4DDmlA1DHd+vu3WkNNL+cZeXWvsGU3eZD UzoA80qEshJ+XNB1m7TVGAi1sg7gBgtG1CZcdcAVHWTjECKwbU3KiACz3FGb4ttbr4EH 79leLpnWSy5E3Z4TUfHUfzZpc0EZ6WIag5G2KNKtKjVP2hz6ckDN6g/fHsTIapxCjPVl ZdjA== X-Gm-Message-State: AOAM531cl8o3dNmUANqxFR5fWPhI8ZQYhwI4ZhbOBgEzmEYiEoQIc4ji 6lIvalDSsAYPfLARHcQOIzctZkTUxXdSg4t/q1k= X-Google-Smtp-Source: ABdhPJyeSfM0mJIIBZ5uMiP0vE0jE8HpmysMeHGDaVQ49tpR3iNzoJGkxR2d9nheqA49kzV9HVeWrM23tPv8SiOtYMk= X-Received: by 2002:a5b:f83:0:b0:63d:a251:2c51 with SMTP id q3-20020a5b0f83000000b0063da2512c51mr2495876ybh.594.1649959295429; Thu, 14 Apr 2022 11:01:35 -0700 (PDT) MIME-Version: 1.0 References: <20220413202401.408267-1-adhemerval.zanella@linaro.org> <20220413202401.408267-3-adhemerval.zanella@linaro.org> In-Reply-To: <20220413202401.408267-3-adhemerval.zanella@linaro.org> From: Noah Goldstein Date: Thu, 14 Apr 2022 13:01:24 -0500 Message-ID: Subject: Re: [PATCH 2/7] stdlib: Add arc4random tests To: Adhemerval Zanella Cc: GNU C Library , Florian Weimer Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-8.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 14 Apr 2022 18:01:41 -0000 On Wed, Apr 13, 2022 at 3:25 PM Adhemerval Zanella via Libc-alpha wrote: > > The basic tst-arc4random-chacha20.c checks if the output of ChaCha20 > implementation matches the reference test vectors from RFC8439. > > The tst-arc4random-fork.c check if subprocesses generate distinct > streams of randomness (if fork handling is done correctly). > > The tst-arc4random-stats.c is a statistical test to the randomness of > arc4random, arc4random_buf, and arc4random_uniform. > > The tst-arc4random-thread.c check if threads generate distinct streams > of randomness (if function are thread-safe). > > Checked on x86_64-linux-gnu, aarch64-linux, and powerpc64le-linux-gnu. > > Co-authored-by: Florian Weimer > --- > stdlib/Makefile | 4 + > stdlib/tst-arc4random-chacha20.c | 225 +++++++++++++++++++++++++ > stdlib/tst-arc4random-fork.c | 174 +++++++++++++++++++ > stdlib/tst-arc4random-stats.c | 146 ++++++++++++++++ > stdlib/tst-arc4random-thread.c | 278 +++++++++++++++++++++++++++++++ > 5 files changed, 827 insertions(+) > create mode 100644 stdlib/tst-arc4random-chacha20.c > create mode 100644 stdlib/tst-arc4random-fork.c > create mode 100644 stdlib/tst-arc4random-stats.c > create mode 100644 stdlib/tst-arc4random-thread.c > > diff --git a/stdlib/Makefile b/stdlib/Makefile > index 9f9cc1bd7f..4862d008ab 100644 > --- a/stdlib/Makefile > +++ b/stdlib/Makefile > @@ -183,6 +183,9 @@ tests := \ > testmb2 \ > testrand \ > testsort \ > + tst-arc4random-fork \ > + tst-arc4random-stats \ > + tst-arc4random-thread \ > tst-at_quick_exit \ > tst-atexit \ > tst-atof1 \ > @@ -252,6 +255,7 @@ tests-internal := \ > # tests-internal > > tests-static := \ > + tst-arc4random-chacha20 \ > tst-secure-getenv \ > # tests-static > > diff --git a/stdlib/tst-arc4random-chacha20.c b/stdlib/tst-arc4random-chacha20.c > new file mode 100644 > index 0000000000..c5876d3f3b > --- /dev/null > +++ b/stdlib/tst-arc4random-chacha20.c > @@ -0,0 +1,225 @@ > +/* Basic tests for chacha20 cypher used in arc4random. > + Copyright (C) 2022 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + . */ > + > +#include > +#include > + > +static int > +do_test (void) > +{ > + /* Reference ChaCha20 encryption test vectors from RFC8439. */ > + > + /* Test vector #1. */ > + { > + struct chacha20_state state; > + > + uint8_t key[CHACHA20_KEY_SIZE] = > + { > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, > + }; > + uint8_t iv[CHACHA20_IV_SIZE] = > + { > + 0x0, 0x0, 0x0, 0x0, > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, > + }; > + const uint8_t plaintext[CHACHA20_BLOCK_SIZE] = { 0 }; > + uint8_t ciphertext[CHACHA20_BLOCK_SIZE]; > + Can you remove this whitespace. > + chacha20_init (&state, key, iv); > + chacha20_crypt (&state, ciphertext, plaintext, sizeof plaintext); > + > + const uint8_t expected[] = > + { > + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, > + 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, > + 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, > + 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7, > + 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, > + 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, > + 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, > + 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86 > + }; > + TEST_COMPARE_BLOB (ciphertext, sizeof ciphertext, > + expected, sizeof expected); > + } > + > + /* Test vector #2. */ > + { > + struct chacha20_state state; > + > + uint8_t key[CHACHA20_KEY_SIZE] = > + { > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, > + }; > + uint8_t iv[CHACHA20_IV_SIZE] = > + { > + 0x1, 0x0, 0x0, 0x0, /* Block counter is a LE uint32_t */ > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 > + }; > + const uint8_t plaintext[] = > + { > + 0x41, 0x6e, 0x79, 0x20, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x73, 0x73, > + 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, > + 0x49, 0x45, 0x54, 0x46, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, > + 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, > + 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x20, > + 0x66, 0x6f, 0x72, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x61, > + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x73, 0x20, 0x61, 0x6c, 0x6c, > + 0x20, 0x6f, 0x72, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, > + 0x20, 0x61, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x49, 0x6e, > + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2d, 0x44, 0x72, 0x61, 0x66, > + 0x74, 0x20, 0x6f, 0x72, 0x20, 0x52, 0x46, 0x43, 0x20, 0x61, 0x6e, > + 0x64, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, > + 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x77, > + 0x69, 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, > + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, > + 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46, 0x20, 0x61, 0x63, 0x74, 0x69, > + 0x76, 0x69, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, > + 0x73, 0x69, 0x64, 0x65, 0x72, 0x65, 0x64, 0x20, 0x61, 0x6e, 0x20, > + 0x22, 0x49, 0x45, 0x54, 0x46, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, > + 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2e, 0x20, 0x53, > + 0x75, 0x63, 0x68, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, > + 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, > + 0x20, 0x6f, 0x72, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, > + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x45, > + 0x54, 0x46, 0x20, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, > + 0x2c, 0x20, 0x61, 0x73, 0x20, 0x77, 0x65, 0x6c, 0x6c, 0x20, 0x61, > + 0x73, 0x20, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x61, > + 0x6e, 0x64, 0x20, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x6f, 0x6e, > + 0x69, 0x63, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, > + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x61, 0x64, 0x65, > + 0x20, 0x61, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x74, 0x69, 0x6d, > + 0x65, 0x20, 0x6f, 0x72, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x2c, > + 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x61, 0x72, 0x65, 0x20, > + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, > + 0x6f, > + }; > + uint8_t ciphertext[sizeof plaintext]; > + > + chacha20_init (&state, key, iv); > + chacha20_crypt (&state, ciphertext, plaintext, sizeof plaintext); > + > + const uint8_t expected[] = > + { > + 0xa3, 0xfb, 0xf0, 0x7d, 0xf3, 0xfa, 0x2f, 0xde, 0x4f, 0x37, 0x6c, > + 0xa2, 0x3e, 0x82, 0x73, 0x70, 0x41, 0x60, 0x5d, 0x9f, 0x4f, 0x4f, > + 0x57, 0xbd, 0x8c, 0xff, 0x2c, 0x1d, 0x4b, 0x79, 0x55, 0xec, 0x2a, > + 0x97, 0x94, 0x8b, 0xd3, 0x72, 0x29, 0x15, 0xc8, 0xf3, 0xd3, 0x37, > + 0xf7, 0xd3, 0x70, 0x05, 0x0e, 0x9e, 0x96, 0xd6, 0x47, 0xb7, 0xc3, > + 0x9f, 0x56, 0xe0, 0x31, 0xca, 0x5e, 0xb6, 0x25, 0x0d, 0x40, 0x42, > + 0xe0, 0x27, 0x85, 0xec, 0xec, 0xfa, 0x4b, 0x4b, 0xb5, 0xe8, 0xea, > + 0xd0, 0x44, 0x0e, 0x20, 0xb6, 0xe8, 0xdb, 0x09, 0xd8, 0x81, 0xa7, > + 0xc6, 0x13, 0x2f, 0x42, 0x0e, 0x52, 0x79, 0x50, 0x42, 0xbd, 0xfa, > + 0x77, 0x73, 0xd8, 0xa9, 0x05, 0x14, 0x47, 0xb3, 0x29, 0x1c, 0xe1, > + 0x41, 0x1c, 0x68, 0x04, 0x65, 0x55, 0x2a, 0xa6, 0xc4, 0x05, 0xb7, > + 0x76, 0x4d, 0x5e, 0x87, 0xbe, 0xa8, 0x5a, 0xd0, 0x0f, 0x84, 0x49, > + 0xed, 0x8f, 0x72, 0xd0, 0xd6, 0x62, 0xab, 0x05, 0x26, 0x91, 0xca, > + 0x66, 0x42, 0x4b, 0xc8, 0x6d, 0x2d, 0xf8, 0x0e, 0xa4, 0x1f, 0x43, > + 0xab, 0xf9, 0x37, 0xd3, 0x25, 0x9d, 0xc4, 0xb2, 0xd0, 0xdf, 0xb4, > + 0x8a, 0x6c, 0x91, 0x39, 0xdd, 0xd7, 0xf7, 0x69, 0x66, 0xe9, 0x28, > + 0xe6, 0x35, 0x55, 0x3b, 0xa7, 0x6c, 0x5c, 0x87, 0x9d, 0x7b, 0x35, > + 0xd4, 0x9e, 0xb2, 0xe6, 0x2b, 0x08, 0x71, 0xcd, 0xac, 0x63, 0x89, > + 0x39, 0xe2, 0x5e, 0x8a, 0x1e, 0x0e, 0xf9, 0xd5, 0x28, 0x0f, 0xa8, > + 0xca, 0x32, 0x8b, 0x35, 0x1c, 0x3c, 0x76, 0x59, 0x89, 0xcb, 0xcf, > + 0x3d, 0xaa, 0x8b, 0x6c, 0xcc, 0x3a, 0xaf, 0x9f, 0x39, 0x79, 0xc9, > + 0x2b, 0x37, 0x20, 0xfc, 0x88, 0xdc, 0x95, 0xed, 0x84, 0xa1, 0xbe, > + 0x05, 0x9c, 0x64, 0x99, 0xb9, 0xfd, 0xa2, 0x36, 0xe7, 0xe8, 0x18, > + 0xb0, 0x4b, 0x0b, 0xc3, 0x9c, 0x1e, 0x87, 0x6b, 0x19, 0x3b, 0xfe, > + 0x55, 0x69, 0x75, 0x3f, 0x88, 0x12, 0x8c, 0xc0, 0x8a, 0xaa, 0x9b, > + 0x63, 0xd1, 0xa1, 0x6f, 0x80, 0xef, 0x25, 0x54, 0xd7, 0x18, 0x9c, > + 0x41, 0x1f, 0x58, 0x69, 0xca, 0x52, 0xc5, 0xb8, 0x3f, 0xa3, 0x6f, > + 0xf2, 0x16, 0xb9, 0xc1, 0xd3, 0x00, 0x62, 0xbe, 0xbc, 0xfd, 0x2d, > + 0xc5, 0xbc, 0xe0, 0x91, 0x19, 0x34, 0xfd, 0xa7, 0x9a, 0x86, 0xf6, > + 0xe6, 0x98, 0xce, 0xd7, 0x59, 0xc3, 0xff, 0x9b, 0x64, 0x77, 0x33, > + 0x8f, 0x3d, 0xa4, 0xf9, 0xcd, 0x85, 0x14, 0xea, 0x99, 0x82, 0xcc, > + 0xaf, 0xb3, 0x41, 0xb2, 0x38, 0x4d, 0xd9, 0x02, 0xf3, 0xd1, 0xab, > + 0x7a, 0xc6, 0x1d, 0xd2, 0x9c, 0x6f, 0x21, 0xba, 0x5b, 0x86, 0x2f, > + 0x37, 0x30, 0xe3, 0x7c, 0xfd, 0xc4, 0xfd, 0x80, 0x6c, 0x22, 0xf2, > + 0x21, > + }; > + TEST_COMPARE_BLOB (ciphertext, sizeof ciphertext, > + expected, sizeof expected); > + } > + > + /* Test vector #3. */ > + { > + struct chacha20_state state; > + > + uint8_t key[CHACHA20_KEY_SIZE] = > + { > + 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, > + 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0, > + 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, > + 0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0 > + }; > + uint8_t iv[CHACHA20_IV_SIZE] = > + { > + 0x2a, 0x0, 0x0, 0x0, /* Block counter is a LE uint32_t */ > + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2 > + }; > + > + uint8_t plaintext[] = > + { > + 0x27, 0x54, 0x77, 0x61, 0x73, 0x20, 0x62, 0x72, 0x69, 0x6c, 0x6c, > + 0x69, 0x67, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, > + 0x20, 0x73, 0x6c, 0x69, 0x74, 0x68, 0x79, 0x20, 0x74, 0x6f, 0x76, > + 0x65, 0x73, 0x0a, 0x44, 0x69, 0x64, 0x20, 0x67, 0x79, 0x72, 0x65, > + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x67, 0x69, 0x6d, 0x62, 0x6c, 0x65, > + 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x61, 0x62, > + 0x65, 0x3a, 0x0a, 0x41, 0x6c, 0x6c, 0x20, 0x6d, 0x69, 0x6d, 0x73, > + 0x79, 0x20, 0x77, 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, > + 0x62, 0x6f, 0x72, 0x6f, 0x67, 0x6f, 0x76, 0x65, 0x73, 0x2c, 0x0a, > + 0x41, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x6d, > + 0x65, 0x20, 0x72, 0x61, 0x74, 0x68, 0x73, 0x20, 0x6f, 0x75, 0x74, > + 0x67, 0x72, 0x61, 0x62, 0x65, 0x2e, > + }; > + uint8_t ciphertext[sizeof plaintext]; > + > + chacha20_init (&state, key, iv); > + chacha20_crypt (&state, ciphertext, plaintext, sizeof plaintext); > + > + const uint8_t expected[] = > + { > + 0x62, 0xe6, 0x34, 0x7f, 0x95, 0xed, 0x87, 0xa4, 0x5f, 0xfa, 0xe7, > + 0x42, 0x6f, 0x27, 0xa1, 0xdf, 0x5f, 0xb6, 0x91, 0x10, 0x04, 0x4c, > + 0x0d, 0x73, 0x11, 0x8e, 0xff, 0xa9, 0x5b, 0x01, 0xe5, 0xcf, 0x16, > + 0x6d, 0x3d, 0xf2, 0xd7, 0x21, 0xca, 0xf9, 0xb2, 0x1e, 0x5f, 0xb1, > + 0x4c, 0x61, 0x68, 0x71, 0xfd, 0x84, 0xc5, 0x4f, 0x9d, 0x65, 0xb2, > + 0x83, 0x19, 0x6c, 0x7f, 0xe4, 0xf6, 0x05, 0x53, 0xeb, 0xf3, 0x9c, > + 0x64, 0x02, 0xc4, 0x22, 0x34, 0xe3, 0x2a, 0x35, 0x6b, 0x3e, 0x76, > + 0x43, 0x12, 0xa6, 0x1a, 0x55, 0x32, 0x05, 0x57, 0x16, 0xea, 0xd6, > + 0x96, 0x25, 0x68, 0xf8, 0x7d, 0x3f, 0x3f, 0x77, 0x04, 0xc6, 0xa8, > + 0xd1, 0xbc, 0xd1, 0xbf, 0x4d, 0x50, 0xd6, 0x15, 0x4b, 0x6d, 0xa7, > + 0x31, 0xb1, 0x87, 0xb5, 0x8d, 0xfd, 0x72, 0x8a, 0xfa, 0x36, 0x75, > + 0x7a, 0x79, 0x7a, 0xc1, 0x88, 0xd1, > + }; > + > + TEST_COMPARE_BLOB (ciphertext, sizeof ciphertext, > + expected, sizeof expected); > + } > + > + return 0; > +} > + > +#include > diff --git a/stdlib/tst-arc4random-fork.c b/stdlib/tst-arc4random-fork.c > new file mode 100644 > index 0000000000..cd8852c8d3 > --- /dev/null > +++ b/stdlib/tst-arc4random-fork.c > @@ -0,0 +1,174 @@ > +/* Test that subprocesses generate distinct streams of randomness. > + Copyright (C) 2022 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + . */ > + > +/* Collect random data from subprocesses and check that all the > + results are unique. */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* Perform multiple runs. The subsequent runs start with an > + already-initialized random number generator. (The number 1500 was > + seen to reproduce failures reliable in case of a race condition in > + the fork detection code.) */ > +enum { runs = 1500 }; > + > +/* One hundred processes in total. This should be high enough to > + expose any issues, but low enough not to tax the overall system too > + much. */ > +enum { subprocesses = 49 }; > + > +/* The total number of processes. */ > +enum { processes = subprocesses + 1 }; > + > +/* Number of bytes of randomness to generate per process. Large > + enough to make false positive duplicates extremely unlikely. */ > +enum { random_size = 16 }; > + > +/* Generated bytes of randomness. */ > +struct result > +{ > + unsigned char bytes[random_size]; > +}; > + > +/* Shared across all processes. */ > +static struct shared_data > +{ > + pthread_barrier_t barrier; > + struct result results[runs][processes]; > +} *shared_data; > + > +/* Invoked to collect data from a subprocess. */ > +static void > +subprocess (int run, int process_index) > +{ > + xpthread_barrier_wait (&shared_data->barrier); > + arc4random_buf (shared_data->results[run][process_index].bytes, random_size); > +} > + > +/* Used to sort the results. */ > +struct index > +{ > + int run; > + int process_index; > +}; > + > +/* Used to sort an array of struct index values. */ > +static int > +index_compare (const void *left1, const void *right1) > +{ > + const struct index *left = left1; > + const struct index *right = right1; > + > + return memcmp (shared_data->results[left->run][left->process_index].bytes, > + shared_data->results[right->run][right->process_index].bytes, > + random_size); > +} > + > +static int > +do_test (void) > +{ > + shared_data = support_shared_allocate (sizeof (*shared_data)); > + { > + pthread_barrierattr_t attr; > + xpthread_barrierattr_init (&attr); > + xpthread_barrierattr_setpshared (&attr, PTHREAD_PROCESS_SHARED); > + xpthread_barrier_init (&shared_data->barrier, &attr, processes); > + xpthread_barrierattr_destroy (&attr); > + } > + > + /* Collect random data. */ > + for (int run = 0; run < runs; ++run) > + { > +#if 0 > + if (run == runs / 2) > + { > + /* In the middle, desynchronize the block cache by consuming > + an odd number of bytes. */ > + char buf; > + arc4random_buf (&buf, 1); > + } > +#endif > + > + pid_t pids[subprocesses]; > + for (int process_index = 0; process_index < subprocesses; > + ++process_index) > + { > + pids[process_index] = xfork (); > + if (pids[process_index] == 0) > + { > + subprocess (run, process_index); > + _exit (0); > + } > + } > + > + /* Trigger all subprocesses. Also add data from the parent > + process. */ > + subprocess (run, subprocesses); > + > + for (int process_index = 0; process_index < subprocesses; > + ++process_index) > + { > + int status; > + xwaitpid (pids[process_index], &status, 0); > + if (status != 0) > + FAIL_EXIT1 ("subprocess index %d (PID %d) exit status %d\n", > + process_index, (int) pids[process_index], status); > + } > + } > + > + /* Check for duplicates. */ > + struct index indexes[runs * processes]; > + for (int run = 0; run < runs; ++run) > + for (int process_index = 0; process_index < processes; ++process_index) > + indexes[run * processes + process_index] > + = (struct index) { .run = run, .process_index = process_index }; > + qsort (indexes, array_length (indexes), sizeof (indexes[0]), index_compare); > + for (size_t i = 1; i < array_length (indexes); ++i) > + { > + if (index_compare (indexes + i - 1, indexes + i) == 0) > + { > + support_record_failure (); > + unsigned char *bytes > + = shared_data->results[indexes[i].run] > + [indexes[i].process_index].bytes; > + char *quoted = support_quote_blob (bytes, random_size); > + printf ("error: duplicate randomness data: \"%s\"\n" > + " run %d, subprocess %d\n" > + " run %d, subprocess %d\n", > + quoted, indexes[i - 1].run, indexes[i - 1].process_index, > + indexes[i].run, indexes[i].process_index); > + free (quoted); > + } > + } > + > + xpthread_barrier_destroy (&shared_data->barrier); > + support_shared_free (shared_data); > + shared_data = NULL; > + > + return 0; > +} > + > +#include > diff --git a/stdlib/tst-arc4random-stats.c b/stdlib/tst-arc4random-stats.c > new file mode 100644 > index 0000000000..9747180c99 > --- /dev/null > +++ b/stdlib/tst-arc4random-stats.c > @@ -0,0 +1,146 @@ > +/* Statistical tests for arc4random-related functions. > + Copyright (C) 2022 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + . */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +enum > +{ > + arc4random_key_size = 32 > +}; > + > +struct key > +{ > + unsigned char data[arc4random_key_size]; > +}; > + > +/* With 12,000 keys, the probability that a byte in a predetermined > + position does not have a predetermined value in all generated keys > + is about 4e-21. The probability that this happens with any of the > + 16 * 256 possible byte position/values is 1.6e-17. This results in > + an acceptably low false-positive rate. */ > +enum { key_count = 12000 }; > + > +static struct key keys[key_count]; > + > +/* Used to perform the distribution check. */ > +static int byte_counts[arc4random_key_size][256]; > + > +/* Bail out after this many failures. */ > +enum { failure_limit = 100 }; > + > +static void > +find_stuck_bytes (bool (*func) (unsigned char *key)) > +{ > + memset (&keys, 0xcc, sizeof (keys)); > + > + int failures = 0; > + for (int key = 0; key < key_count; ++key) > + { > + while (true) > + { > + if (func (keys[key].data)) > + break; > + ++failures; > + if (failures >= failure_limit) > + { > + printf ("warning: bailing out after %d failures\n", failures); > + return; > + } > + } > + } > + printf ("info: key generation finished with %d failures\n", failures); > + > + memset (&byte_counts, 0, sizeof (byte_counts)); > + for (int key = 0; key < key_count; ++key) > + for (int pos = 0; pos < arc4random_key_size; ++pos) > + ++byte_counts[pos][keys[key].data[pos]]; > + > + for (int pos = 0; pos < arc4random_key_size; ++pos) > + for (int byte = 0; byte < 256; ++byte) > + if (byte_counts[pos][byte] == 0) > + { > + support_record_failure (); > + printf ("error: byte %d never appeared at position %d\n", byte, pos); > + } > +} > + > +/* Test adapter for arc4random. */ > +static bool > +generate_arc4random (unsigned char *key) > +{ > + uint32_t words[arc4random_key_size / 4]; > + _Static_assert (sizeof (words) == arc4random_key_size, "sizeof (words)"); > + > + for (int i = 0; i < array_length (words); ++i) > + words[i] = arc4random (); > + memcpy (key, &words, arc4random_key_size); > + return true; > +} > + > +/* Test adapter for arc4random_buf. */ > +static bool > +generate_arc4random_buf (unsigned char *key) > +{ > + arc4random_buf (key, arc4random_key_size); > + return true; > +} > + > +/* Test adapter for arc4random_uniform. */ > +static bool > +generate_arc4random_uniform (unsigned char *key) > +{ > + for (int i = 0; i < arc4random_key_size; ++i) > + key[i] = arc4random_uniform (256); > + return true; > +} > + > +/* Test adapter for arc4random_uniform with argument 257. This means > + that byte 0 happens more often, but we do not perform such a > + statistcal check, so the test will still pass */ > +static bool > +generate_arc4random_uniform_257 (unsigned char *key) > +{ > + for (int i = 0; i < arc4random_key_size; ++i) > + key[i] = arc4random_uniform (257); > + return true; > +} > + > +static int > +do_test (void) > +{ > + puts ("info: arc4random implementation test"); > + find_stuck_bytes (generate_arc4random); > + > + puts ("info: arc4random_buf implementation test"); > + find_stuck_bytes (generate_arc4random_buf); > + > + puts ("info: arc4random_uniform implementation test"); > + find_stuck_bytes (generate_arc4random_uniform); > + > + puts ("info: arc4random_uniform implementation test (257 variant)"); > + find_stuck_bytes (generate_arc4random_uniform_257); > + > + return 0; > +} > + > +#include > diff --git a/stdlib/tst-arc4random-thread.c b/stdlib/tst-arc4random-thread.c > new file mode 100644 > index 0000000000..b122eaa826 > --- /dev/null > +++ b/stdlib/tst-arc4random-thread.c > @@ -0,0 +1,278 @@ > +/* Test that threads generate distinct streams of randomness. > + Copyright (C) 2018 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + . */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* Number of arc4random_buf calls per thread. */ > +enum { count_per_thread = 5000 }; > + > +/* Number of threads computing randomness. */ > +enum { inner_threads = 5 }; > + > +/* Number of threads launching other threads. Chosen as to not to > + overload the system. */ > +enum { outer_threads = 7 }; > + > +/* Number of launching rounds performed by the outer threads. */ > +enum { outer_rounds = 10 }; > + > +/* Maximum number of bytes generated in an arc4random call. */ > +enum { max_size = 32 }; > + > +/* Sizes generated by threads. Must be long enough to be unique with > + high probability. */ > +static const int sizes[] = { 12, 15, 16, 17, 24, 31, max_size }; > + > +/* Data structure to capture randomness results. */ > +struct blob > +{ > + unsigned int size; > + int thread_id; > + unsigned int index; > + unsigned char bytes[max_size]; > +}; > + > +#define DYNARRAY_STRUCT dynarray_blob > +#define DYNARRAY_ELEMENT struct blob > +#define DYNARRAY_PREFIX dynarray_blob_ > +#include > + > +/* Sort blob elements by length first, then by comparing the data > + member. */ > +static int > +compare_blob (const void *left1, const void *right1) > +{ > + const struct blob *left = left1; > + const struct blob *right = right1; > + > + if (left->size != right->size) > + /* No overflow due to limited range. */ > + return left->size - right->size; > + return memcmp (left->bytes, right->bytes, left->size); > +} > + > +/* Used to store the global result. */ > +static pthread_mutex_t global_result_lock = PTHREAD_MUTEX_INITIALIZER; > +static struct dynarray_blob global_result; > + > +/* Copy data to the global result, with locking. */ > +static void > +copy_result_to_global (struct dynarray_blob *result) > +{ > + xpthread_mutex_lock (&global_result_lock); > + size_t old_size = dynarray_blob_size (&global_result); > + TEST_VERIFY_EXIT > + (dynarray_blob_resize (&global_result, > + old_size + dynarray_blob_size (result))); > + memcpy (dynarray_blob_begin (&global_result) + old_size, > + dynarray_blob_begin (result), > + dynarray_blob_size (result) * sizeof (struct blob)); > + xpthread_mutex_unlock (&global_result_lock); > +} > + > +/* Used to assign unique thread IDs. Accessed atomically. */ > +static int next_thread_id; > + > +static void * > +inner_thread (void *unused) > +{ > + /* Use local result to avoid global lock contention while generating > + randomness. */ > + struct dynarray_blob result; > + dynarray_blob_init (&result); > + > + int thread_id = __atomic_fetch_add (&next_thread_id, 1, __ATOMIC_RELAXED); > + > + /* Determine the sizes to be used by this thread. */ > + int size_slot = thread_id % (array_length (sizes) + 1); > + bool switch_sizes = size_slot == array_length (sizes); > + if (switch_sizes) > + size_slot = 0; > + > + /* Compute the random blobs. */ > + for (int i = 0; i < count_per_thread; ++i) > + { > + struct blob *place = dynarray_blob_emplace (&result); > + TEST_VERIFY_EXIT (place != NULL); > + place->size = sizes[size_slot]; > + place->thread_id = thread_id; > + place->index = i; > + arc4random_buf (place->bytes, place->size); > + > + if (switch_sizes) > + size_slot = (size_slot + 1) % array_length (sizes); > + } > + > + /* Store the blobs in the global result structure. */ > + copy_result_to_global (&result); > + > + dynarray_blob_free (&result); > + > + return NULL; > +} > + > +/* Launch the inner threads and wait for their termination. */ > +static void * > +outer_thread (void *unused) > +{ > + for (int round = 0; round < outer_rounds; ++round) > + { > + pthread_t threads[inner_threads]; > + > + for (int i = 0; i < inner_threads; ++i) > + threads[i] = xpthread_create (NULL, inner_thread, NULL); > + > + for (int i = 0; i < inner_threads; ++i) > + xpthread_join (threads[i]); > + } > + > + return NULL; > +} > + > +static bool termination_requested; > + > +/* Call arc4random_buf to fill one blob with 16 bytes. */ > +static void * > +get_one_blob_thread (void *closure) > +{ > + struct blob *result = closure; > + result->size = 16; > + arc4random_buf (result->bytes, result->size); > + return NULL; > +} > + > +/* Invoked from fork_thread to actually obtain randomness data. */ > +static void > +fork_thread_subprocess (void *closure) > +{ > + struct blob *shared_result = closure; > + > + pthread_t thr1 = xpthread_create > + (NULL, get_one_blob_thread, shared_result + 1); > + pthread_t thr2 = xpthread_create > + (NULL, get_one_blob_thread, shared_result + 2); > + get_one_blob_thread (shared_result); > + xpthread_join (thr1); > + xpthread_join (thr2); > +} > + > +/* Continuously fork subprocesses to obtain a little bit of > + randomness. */ > +static void * > +fork_thread (void *unused) > +{ > + struct dynarray_blob result; > + dynarray_blob_init (&result); > + > + /* Three blobs from each subprocess. */ > + struct blob *shared_result > + = support_shared_allocate (3 * sizeof (*shared_result)); > + > + while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED)) > + { > + /* Obtain the results from a subprocess. */ > + support_isolate_in_subprocess (fork_thread_subprocess, shared_result); > + > + for (int i = 0; i < 3; ++i) > + { > + struct blob *place = dynarray_blob_emplace (&result); > + TEST_VERIFY_EXIT (place != NULL); > + place->size = shared_result[i].size; > + place->thread_id = -1; > + place->index = i; > + memcpy (place->bytes, shared_result[i].bytes, place->size); > + } > + } > + > + support_shared_free (shared_result); > + > + copy_result_to_global (&result); > + dynarray_blob_free (&result); > + > + return NULL; > +} > + > +/* Launch the outer threads and wait for their termination. */ > +static void > +run_outer_threads (void) > +{ > + /* Special thread that continuously calls fork. */ > + pthread_t fork_thread_id = xpthread_create (NULL, fork_thread, NULL); > + > + pthread_t threads[outer_threads]; > + for (int i = 0; i < outer_threads; ++i) > + threads[i] = xpthread_create (NULL, outer_thread, NULL); > + > + for (int i = 0; i < outer_threads; ++i) > + xpthread_join (threads[i]); > + > + __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED); > + xpthread_join (fork_thread_id); > +} > + > +static int > +do_test (void) > +{ > + dynarray_blob_init (&global_result); > + int expected_blobs > + = count_per_thread * inner_threads * outer_threads * outer_rounds; > + printf ("info: minimum of %d blob results expected\n", expected_blobs); > + > + run_outer_threads (); > + > + /* The forking thread delivers a non-deterministic number of > + results, which is why expected_blobs is only a minimun number of > + results. */ > + printf ("info: %zu blob results observed\n", > + dynarray_blob_size (&global_result)); > + TEST_VERIFY (dynarray_blob_size (&global_result) >= expected_blobs); > + > + /* Verify that there are no duplicates. */ > + qsort (dynarray_blob_begin (&global_result), > + dynarray_blob_size (&global_result), > + sizeof (struct blob), compare_blob); > + struct blob *end = dynarray_blob_end (&global_result); > + for (struct blob *p = dynarray_blob_begin (&global_result) + 1; > + p < end; ++p) > + { > + if (compare_blob (p - 1, p) == 0) > + { > + support_record_failure (); > + char *quoted = support_quote_blob (p->bytes, p->size); > + printf ("error: duplicate blob: \"%s\" (%d bytes)\n", > + quoted, (int) p->size); > + printf (" first source: thread %d, index %u\n", > + p[-1].thread_id, p[-1].index); > + printf (" second source: thread %d, index %u\n", > + p[0].thread_id, p[0].index); > + free (quoted); > + } > + } > + > + dynarray_blob_free (&global_result); > + > + return 0; > +} > + > +#include > -- > 2.32.0 >