mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 20:40:05 +00:00
stdlib: Add arc4random tests
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 <fweimer@redhat.com> Checked on x86_64-linux-gnu and aarch64-linux-gnu.
This commit is contained in:
parent
6f4e0fcfa2
commit
8dd890d96f
@ -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 \
|
||||
@ -243,6 +246,7 @@ tests := \
|
||||
# tests
|
||||
|
||||
tests-internal := \
|
||||
tst-arc4random-chacha20 \
|
||||
tst-strtod1i \
|
||||
tst-strtod3 \
|
||||
tst-strtod4 \
|
||||
@ -252,6 +256,7 @@ tests-internal := \
|
||||
# tests-internal
|
||||
|
||||
tests-static := \
|
||||
tst-arc4random-chacha20 \
|
||||
tst-secure-getenv \
|
||||
# tests-static
|
||||
|
||||
@ -271,6 +276,8 @@ LDLIBS-test-cxa_atexit-race = $(shared-thread-library)
|
||||
LDLIBS-test-cxa_atexit-race2 = $(shared-thread-library)
|
||||
LDLIBS-test-on_exit-race = $(shared-thread-library)
|
||||
LDLIBS-tst-canon-bz26341 = $(shared-thread-library)
|
||||
LDLIBS-tst-arc4random-fork = $(shared-thread-library)
|
||||
LDLIBS-tst-arc4random-thread = $(shared-thread-library)
|
||||
|
||||
LDLIBS-test-dlclose-exit-race = $(shared-thread-library)
|
||||
LDFLAGS-test-dlclose-exit-race = $(LDFLAGS-rdynamic)
|
||||
|
167
stdlib/tst-arc4random-chacha20.c
Normal file
167
stdlib/tst-arc4random-chacha20.c
Normal file
@ -0,0 +1,167 @@
|
||||
/* 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <arc4random.h>
|
||||
#include <support/check.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
/* The test does not define CHACHA20_XOR_FINAL to mimic what arc4random
|
||||
actual does. */
|
||||
#include <chacha20.c>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
const 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,
|
||||
};
|
||||
const 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 expected1[CHACHA20_BUFSIZE] =
|
||||
{
|
||||
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, 0x9f, 0x07,
|
||||
0xe7, 0xbe, 0x55, 0x51, 0x38, 0x7a, 0x98, 0xba, 0x97, 0x7c, 0x73,
|
||||
0x2d, 0x08, 0x0d, 0xcb, 0x0f, 0x29, 0xa0, 0x48, 0xe3, 0x65, 0x69,
|
||||
0x12, 0xc6, 0x53, 0x3e, 0x32, 0xee, 0x7a, 0xed, 0x29, 0xb7, 0x21,
|
||||
0x76, 0x9c, 0xe6, 0x4e, 0x43, 0xd5, 0x71, 0x33, 0xb0, 0x74, 0xd8,
|
||||
0x39, 0xd5, 0x31, 0xed, 0x1f, 0x28, 0x51, 0x0a, 0xfb, 0x45, 0xac,
|
||||
0xe1, 0x0a, 0x1f, 0x4b, 0x79, 0x4d, 0x6f, 0x2d, 0x09, 0xa0, 0xe6,
|
||||
0x63, 0x26, 0x6c, 0xe1, 0xae, 0x7e, 0xd1, 0x08, 0x19, 0x68, 0xa0,
|
||||
0x75, 0x8e, 0x71, 0x8e, 0x99, 0x7b, 0xd3, 0x62, 0xc6, 0xb0, 0xc3,
|
||||
0x46, 0x34, 0xa9, 0xa0, 0xb3, 0x5d, 0x01, 0x27, 0x37, 0x68, 0x1f,
|
||||
0x7b, 0x5d, 0x0f, 0x28, 0x1e, 0x3a, 0xfd, 0xe4, 0x58, 0xbc, 0x1e,
|
||||
0x73, 0xd2, 0xd3, 0x13, 0xc9, 0xcf, 0x94, 0xc0, 0x5f, 0xf3, 0x71,
|
||||
0x62, 0x40, 0xa2, 0x48, 0xf2, 0x13, 0x20, 0xa0, 0x58, 0xd7, 0xb3,
|
||||
0x56, 0x6b, 0xd5, 0x20, 0xda, 0xaa, 0x3e, 0xd2, 0xbf, 0x0a, 0xc5,
|
||||
0xb8, 0xb1, 0x20, 0xfb, 0x85, 0x27, 0x73, 0xc3, 0x63, 0x97, 0x34,
|
||||
0xb4, 0x5c, 0x91, 0xa4, 0x2d, 0xd4, 0xcb, 0x83, 0xf8, 0x84, 0x0d,
|
||||
0x2e, 0xed, 0xb1, 0x58, 0x13, 0x10, 0x62, 0xac, 0x3f, 0x1f, 0x2c,
|
||||
0xf8, 0xff, 0x6d, 0xcd, 0x18, 0x56, 0xe8, 0x6a, 0x1e, 0x6c, 0x31,
|
||||
0x67, 0x16, 0x7e, 0xe5, 0xa6, 0x88, 0x74, 0x2b, 0x47, 0xc5, 0xad,
|
||||
0xfb, 0x59, 0xd4, 0xdf, 0x76, 0xfd, 0x1d, 0xb1, 0xe5, 0x1e, 0xe0,
|
||||
0x3b, 0x1c, 0xa9, 0xf8, 0x2a, 0xca, 0x17, 0x3e, 0xdb, 0x8b, 0x72,
|
||||
0x93, 0x47, 0x4e, 0xbe, 0x98, 0x0f, 0x90, 0x4d, 0x10, 0xc9, 0x16,
|
||||
0x44, 0x2b, 0x47, 0x83, 0xa0, 0xe9, 0x84, 0x86, 0x0c, 0xb6, 0xc9,
|
||||
0x57, 0xb3, 0x9c, 0x38, 0xed, 0x8f, 0x51, 0xcf, 0xfa, 0xa6, 0x8a,
|
||||
0x4d, 0xe0, 0x10, 0x25, 0xa3, 0x9c, 0x50, 0x45, 0x46, 0xb9, 0xdc,
|
||||
0x14, 0x06, 0xa7, 0xeb, 0x28, 0x15, 0x1e, 0x51, 0x50, 0xd7, 0xb2,
|
||||
0x04, 0xba, 0xa7, 0x19, 0xd4, 0xf0, 0x91, 0x02, 0x12, 0x17, 0xdb,
|
||||
0x5c, 0xf1, 0xb5, 0xc8, 0x4c, 0x4f, 0xa7, 0x1a, 0x87, 0x96, 0x10,
|
||||
0xa1, 0xa6, 0x95, 0xac, 0x52, 0x7c, 0x5b, 0x56, 0x77, 0x4a, 0x6b,
|
||||
0x8a, 0x21, 0xaa, 0xe8, 0x86, 0x85, 0x86, 0x8e, 0x09, 0x4c, 0xf2,
|
||||
0x9e, 0xf4, 0x09, 0x0a, 0xf7, 0xa9, 0x0c, 0xc0, 0x7e, 0x88, 0x17,
|
||||
0xaa, 0x52, 0x87, 0x63, 0x79, 0x7d, 0x3c, 0x33, 0x2b, 0x67, 0xca,
|
||||
0x4b, 0xc1, 0x10, 0x64, 0x2c, 0x21, 0x51, 0xec, 0x47, 0xee, 0x84,
|
||||
0xcb, 0x8c, 0x42, 0xd8, 0x5f, 0x10, 0xe2, 0xa8, 0xcb, 0x18, 0xc3,
|
||||
0xb7, 0x33, 0x5f, 0x26, 0xe8, 0xc3, 0x9a, 0x12, 0xb1, 0xbc, 0xc1,
|
||||
0x70, 0x71, 0x77, 0xb7, 0x61, 0x38, 0x73, 0x2e, 0xed, 0xaa, 0xb7,
|
||||
0x4d, 0xa1, 0x41, 0x0f, 0xc0, 0x55, 0xea, 0x06, 0x8c, 0x99, 0xe9,
|
||||
0x26, 0x0a, 0xcb, 0xe3, 0x37, 0xcf, 0x5d, 0x3e, 0x00, 0xe5, 0xb3,
|
||||
0x23, 0x0f, 0xfe, 0xdb, 0x0b, 0x99, 0x07, 0x87, 0xd0, 0xc7, 0x0e,
|
||||
0x0b, 0xfe, 0x41, 0x98, 0xea, 0x67, 0x58, 0xdd, 0x5a, 0x61, 0xfb,
|
||||
0x5f, 0xec, 0x2d, 0xf9, 0x81, 0xf3, 0x1b, 0xef, 0xe1, 0x53, 0xf8,
|
||||
0x1d, 0x17, 0x16, 0x17, 0x84, 0xdb
|
||||
};
|
||||
|
||||
const uint8_t expected2[CHACHA20_BUFSIZE] =
|
||||
{
|
||||
0x1c, 0x88, 0x22, 0xd5, 0x3c, 0xd1, 0xee, 0x7d, 0xb5, 0x32, 0x36,
|
||||
0x48, 0x28, 0xbd, 0xf4, 0x04, 0xb0, 0x40, 0xa8, 0xdc, 0xc5, 0x22,
|
||||
0xf3, 0xd3, 0xd9, 0x9a, 0xec, 0x4b, 0x80, 0x57, 0xed, 0xb8, 0x50,
|
||||
0x09, 0x31, 0xa2, 0xc4, 0x2d, 0x2f, 0x0c, 0x57, 0x08, 0x47, 0x10,
|
||||
0x0b, 0x57, 0x54, 0xda, 0xfc, 0x5f, 0xbd, 0xb8, 0x94, 0xbb, 0xef,
|
||||
0x1a, 0x2d, 0xe1, 0xa0, 0x7f, 0x8b, 0xa0, 0xc4, 0xb9, 0x19, 0x30,
|
||||
0x10, 0x66, 0xed, 0xbc, 0x05, 0x6b, 0x7b, 0x48, 0x1e, 0x7a, 0x0c,
|
||||
0x46, 0x29, 0x7b, 0xbb, 0x58, 0x9d, 0x9d, 0xa5, 0xb6, 0x75, 0xa6,
|
||||
0x72, 0x3e, 0x15, 0x2e, 0x5e, 0x63, 0xa4, 0xce, 0x03, 0x4e, 0x9e,
|
||||
0x83, 0xe5, 0x8a, 0x01, 0x3a, 0xf0, 0xe7, 0x35, 0x2f, 0xb7, 0x90,
|
||||
0x85, 0x14, 0xe3, 0xb3, 0xd1, 0x04, 0x0d, 0x0b, 0xb9, 0x63, 0xb3,
|
||||
0x95, 0x4b, 0x63, 0x6b, 0x5f, 0xd4, 0xbf, 0x6d, 0x0a, 0xad, 0xba,
|
||||
0xf8, 0x15, 0x7d, 0x06, 0x2a, 0xcb, 0x24, 0x18, 0xc1, 0x76, 0xa4,
|
||||
0x75, 0x51, 0x1b, 0x35, 0xc3, 0xf6, 0x21, 0x8a, 0x56, 0x68, 0xea,
|
||||
0x5b, 0xc6, 0xf5, 0x4b, 0x87, 0x82, 0xf8, 0xb3, 0x40, 0xf0, 0x0a,
|
||||
0xc1, 0xbe, 0xba, 0x5e, 0x62, 0xcd, 0x63, 0x2a, 0x7c, 0xe7, 0x80,
|
||||
0x9c, 0x72, 0x56, 0x08, 0xac, 0xa5, 0xef, 0xbf, 0x7c, 0x41, 0xf2,
|
||||
0x37, 0x64, 0x3f, 0x06, 0xc0, 0x99, 0x72, 0x07, 0x17, 0x1d, 0xe8,
|
||||
0x67, 0xf9, 0xd6, 0x97, 0xbf, 0x5e, 0xa6, 0x01, 0x1a, 0xbc, 0xce,
|
||||
0x6c, 0x8c, 0xdb, 0x21, 0x13, 0x94, 0xd2, 0xc0, 0x2d, 0xd0, 0xfb,
|
||||
0x60, 0xdb, 0x5a, 0x2c, 0x17, 0xac, 0x3d, 0xc8, 0x58, 0x78, 0xa9,
|
||||
0x0b, 0xed, 0x38, 0x09, 0xdb, 0xb9, 0x6e, 0xaa, 0x54, 0x26, 0xfc,
|
||||
0x8e, 0xae, 0x0d, 0x2d, 0x65, 0xc4, 0x2a, 0x47, 0x9f, 0x08, 0x86,
|
||||
0x48, 0xbe, 0x2d, 0xc8, 0x01, 0xd8, 0x2a, 0x36, 0x6f, 0xdd, 0xc0,
|
||||
0xef, 0x23, 0x42, 0x63, 0xc0, 0xb6, 0x41, 0x7d, 0x5f, 0x9d, 0xa4,
|
||||
0x18, 0x17, 0xb8, 0x8d, 0x68, 0xe5, 0xe6, 0x71, 0x95, 0xc5, 0xc1,
|
||||
0xee, 0x30, 0x95, 0xe8, 0x21, 0xf2, 0x25, 0x24, 0xb2, 0x0b, 0xe4,
|
||||
0x1c, 0xeb, 0x59, 0x04, 0x12, 0xe4, 0x1d, 0xc6, 0x48, 0x84, 0x3f,
|
||||
0xa9, 0xbf, 0xec, 0x7a, 0x3d, 0xcf, 0x61, 0xab, 0x05, 0x41, 0x57,
|
||||
0x33, 0x16, 0xd3, 0xfa, 0x81, 0x51, 0x62, 0x93, 0x03, 0xfe, 0x97,
|
||||
0x41, 0x56, 0x2e, 0xd0, 0x65, 0xdb, 0x4e, 0xbc, 0x00, 0x50, 0xef,
|
||||
0x55, 0x83, 0x64, 0xae, 0x81, 0x12, 0x4a, 0x28, 0xf5, 0xc0, 0x13,
|
||||
0x13, 0x23, 0x2f, 0xbc, 0x49, 0x6d, 0xfd, 0x8a, 0x25, 0x68, 0x65,
|
||||
0x7b, 0x68, 0x6d, 0x72, 0x14, 0x38, 0x2a, 0x1a, 0x00, 0x90, 0x30,
|
||||
0x17, 0xdd, 0xa9, 0x69, 0x87, 0x84, 0x42, 0xba, 0x5a, 0xff, 0xf6,
|
||||
0x61, 0x3f, 0x55, 0x3c, 0xbb, 0x23, 0x3c, 0xe4, 0x6d, 0x9a, 0xee,
|
||||
0x93, 0xa7, 0x87, 0x6c, 0xf5, 0xe9, 0xe8, 0x29, 0x12, 0xb1, 0x8c,
|
||||
0xad, 0xf0, 0xb3, 0x43, 0x27, 0xb2, 0xe0, 0x42, 0x7e, 0xcf, 0x66,
|
||||
0xb7, 0xce, 0xb7, 0xc0, 0x91, 0x8d, 0xc4, 0x7b, 0xdf, 0xf1, 0x2a,
|
||||
0x06, 0x2a, 0xdf, 0x07, 0x13, 0x30, 0x09, 0xce, 0x7a, 0x5e, 0x5c,
|
||||
0x91, 0x7e, 0x01, 0x68, 0x30, 0x61, 0x09, 0xb7, 0xcb, 0x49, 0x65,
|
||||
0x3a, 0x6d, 0x2c, 0xae, 0xf0, 0x05, 0xde, 0x78, 0x3a, 0x9a, 0x9b,
|
||||
0xfe, 0x05, 0x38, 0x1e, 0xd1, 0x34, 0x8d, 0x94, 0xec, 0x65, 0x88,
|
||||
0x6f, 0x9c, 0x0b, 0x61, 0x9c, 0x52, 0xc5, 0x53, 0x38, 0x00, 0xb1,
|
||||
0x6c, 0x83, 0x61, 0x72, 0xb9, 0x51, 0x82, 0xdb, 0xc5, 0xee, 0xc0,
|
||||
0x42, 0xb8, 0x9e, 0x22, 0xf1, 0x1a, 0x08, 0x5b, 0x73, 0x9a, 0x36,
|
||||
0x11, 0xcd, 0x8d, 0x83, 0x60, 0x18
|
||||
};
|
||||
|
||||
/* Check with the expected internal arc4random keystream buffer. Some
|
||||
architecture optimizations expects a buffer with a minimum size which
|
||||
is a multiple of then ChaCha20 blocksize, so they might not be prepared
|
||||
to handle smaller buffers. */
|
||||
|
||||
uint8_t output[CHACHA20_BUFSIZE];
|
||||
|
||||
uint32_t state[CHACHA20_STATE_LEN];
|
||||
chacha20_init (state, key, iv);
|
||||
|
||||
/* Check with the initial state. */
|
||||
uint8_t input[CHACHA20_BUFSIZE] = { 0 };
|
||||
|
||||
chacha20_crypt (state, output, input, CHACHA20_BUFSIZE);
|
||||
TEST_COMPARE_BLOB (output, sizeof output, expected1, CHACHA20_BUFSIZE);
|
||||
|
||||
/* And on the next round. */
|
||||
chacha20_crypt (state, output, input, CHACHA20_BUFSIZE);
|
||||
TEST_COMPARE_BLOB (output, sizeof output, expected2, CHACHA20_BUFSIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
198
stdlib/tst-arc4random-fork.c
Normal file
198
stdlib/tst-arc4random-fork.c
Normal file
@ -0,0 +1,198 @@
|
||||
/* 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Collect random data from subprocesses and check that all the
|
||||
results are unique. */
|
||||
|
||||
#include <array_length.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
#include <support/support.h>
|
||||
#include <support/xthread.h>
|
||||
#include <support/xunistd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* 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;
|
||||
|
||||
static void
|
||||
generate_arc4random (unsigned char *bytes)
|
||||
{
|
||||
for (int i = 0; i < random_size / sizeof (uint32_t); i++)
|
||||
{
|
||||
uint32_t x = arc4random ();
|
||||
memcpy (&bytes[4 * i], &x, sizeof x);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
generate_arc4random_buf (unsigned char *bytes)
|
||||
{
|
||||
arc4random_buf (bytes, random_size);
|
||||
}
|
||||
|
||||
static void
|
||||
generate_arc4random_uniform (unsigned char *bytes)
|
||||
{
|
||||
for (int i = 0; i < random_size; i++)
|
||||
bytes[i] = arc4random_uniform (256);
|
||||
}
|
||||
|
||||
/* Invoked to collect data from a subprocess. */
|
||||
static void
|
||||
subprocess (int run, int process_index, void (*func)(unsigned char *))
|
||||
{
|
||||
xpthread_barrier_wait (&shared_data->barrier);
|
||||
func (shared_data->results[run][process_index].bytes);
|
||||
}
|
||||
|
||||
/* 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_func (void (*func)(unsigned char *bytes))
|
||||
{
|
||||
/* Collect random data. */
|
||||
for (int run = 0; run < runs; ++run)
|
||||
{
|
||||
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, func);
|
||||
_exit (0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Trigger all subprocesses. Also add data from the parent
|
||||
process. */
|
||||
subprocess (run, subprocesses, func);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
do_test_func (generate_arc4random);
|
||||
do_test_func (generate_arc4random_buf);
|
||||
do_test_func (generate_arc4random_uniform);
|
||||
|
||||
xpthread_barrier_destroy (&shared_data->barrier);
|
||||
support_shared_free (shared_data);
|
||||
shared_data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TIMEOUT 40
|
||||
#include <support/test-driver.c>
|
147
stdlib/tst-arc4random-stats.c
Normal file
147
stdlib/tst-arc4random-stats.c
Normal file
@ -0,0 +1,147 @@
|
||||
/* 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <array_length.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <support/check.h>
|
||||
|
||||
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 <support/test-driver.c>
|
341
stdlib/tst-arc4random-thread.c
Normal file
341
stdlib/tst-arc4random-thread.c
Normal file
@ -0,0 +1,341 @@
|
||||
/* Test that threads 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
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <array_length.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <support/check.h>
|
||||
#include <support/namespace.h>
|
||||
#include <support/support.h>
|
||||
#include <support/xthread.h>
|
||||
|
||||
/* 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];
|
||||
};
|
||||
|
||||
struct subprocess_args
|
||||
{
|
||||
struct blob *blob;
|
||||
void (*func)(unsigned char *, size_t);
|
||||
};
|
||||
|
||||
static void
|
||||
generate_arc4random (unsigned char *bytes, size_t size)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < size / sizeof (uint32_t); i++)
|
||||
{
|
||||
uint32_t x = arc4random ();
|
||||
memcpy (&bytes[4 * i], &x, sizeof x);
|
||||
}
|
||||
int rem = size % sizeof (uint32_t);
|
||||
if (rem > 0)
|
||||
{
|
||||
uint32_t x = arc4random ();
|
||||
memcpy (&bytes[4 * i], &x, rem);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
generate_arc4random_buf (unsigned char *bytes, size_t size)
|
||||
{
|
||||
arc4random_buf (bytes, size);
|
||||
}
|
||||
|
||||
static void
|
||||
generate_arc4random_uniform (unsigned char *bytes, size_t size)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
bytes[i] = arc4random_uniform (256);
|
||||
}
|
||||
|
||||
#define DYNARRAY_STRUCT dynarray_blob
|
||||
#define DYNARRAY_ELEMENT struct blob
|
||||
#define DYNARRAY_PREFIX dynarray_blob_
|
||||
#include <malloc/dynarray-skeleton.c>
|
||||
|
||||
/* 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 *closure)
|
||||
{
|
||||
void (*func) (unsigned char *, size_t) = closure;
|
||||
|
||||
/* 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;
|
||||
func (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 *closure)
|
||||
{
|
||||
void (*func) (unsigned char *, size_t) = closure;
|
||||
|
||||
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, func);
|
||||
|
||||
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 subprocess_args *arg = closure;
|
||||
struct blob *result = arg->blob;
|
||||
|
||||
result->size = 16;
|
||||
arg->func (result->bytes, result->size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Invoked from fork_thread to actually obtain randomness data. */
|
||||
static void
|
||||
fork_thread_subprocess (void *closure)
|
||||
{
|
||||
struct subprocess_args *arg = closure;
|
||||
struct blob *shared_result = arg->blob;
|
||||
|
||||
struct subprocess_args args[3] =
|
||||
{
|
||||
{ shared_result + 0, arg->func },
|
||||
{ shared_result + 1, arg->func },
|
||||
{ shared_result + 2, arg->func }
|
||||
};
|
||||
|
||||
pthread_t thr1 = xpthread_create (NULL, get_one_blob_thread, &args[1]);
|
||||
pthread_t thr2 = xpthread_create (NULL, get_one_blob_thread, &args[2]);
|
||||
get_one_blob_thread (&args[0]);
|
||||
xpthread_join (thr1);
|
||||
xpthread_join (thr2);
|
||||
}
|
||||
|
||||
/* Continuously fork subprocesses to obtain a little bit of
|
||||
randomness. */
|
||||
static void *
|
||||
fork_thread (void *closure)
|
||||
{
|
||||
void (*func)(unsigned char *, size_t) = closure;
|
||||
|
||||
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. */
|
||||
struct subprocess_args arg = { shared_result, func };
|
||||
support_isolate_in_subprocess (fork_thread_subprocess, &arg);
|
||||
|
||||
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 (*func)(unsigned char *, size_t))
|
||||
{
|
||||
/* Special thread that continuously calls fork. */
|
||||
pthread_t fork_thread_id = xpthread_create (NULL, fork_thread, func);
|
||||
|
||||
pthread_t threads[outer_threads];
|
||||
for (int i = 0; i < outer_threads; ++i)
|
||||
threads[i] = xpthread_create (NULL, outer_thread, func);
|
||||
|
||||
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_func (const char *fname, void (*func)(unsigned char *, size_t))
|
||||
{
|
||||
dynarray_blob_init (&global_result);
|
||||
int expected_blobs
|
||||
= count_per_thread * inner_threads * outer_threads * outer_rounds;
|
||||
printf ("info: %s: minimum of %d blob results expected\n",
|
||||
fname, expected_blobs);
|
||||
|
||||
run_outer_threads (func);
|
||||
|
||||
/* The forking thread delivers a non-deterministic number of
|
||||
results, which is why expected_blobs is only a minimun number of
|
||||
results. */
|
||||
printf ("info: %s: %zu blob results observed\n", fname,
|
||||
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: %s: duplicate blob: \"%s\" (%d bytes)\n",
|
||||
fname, 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;
|
||||
}
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
do_test_func ("arc4random", generate_arc4random);
|
||||
do_test_func ("arc4random_buf", generate_arc4random_buf);
|
||||
do_test_func ("arc4random_uniform", generate_arc4random_uniform);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
Loading…
Reference in New Issue
Block a user