From patchwork Thu Jun 21 12:00:22 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 27979 Received: (qmail 118299 invoked by alias); 21 Jun 2018 12:00:54 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 118169 invoked by uid 89); 21 Jun 2018 12:00:47 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-23.0 required=5.0 tests=GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, SPF_HELO_PASS, TIME_LIMIT_EXCEEDED, UNSUBSCRIBE_BODY autolearn=unavailable version=3.3.2 spammy=1500, stephen, UD:tk, Stephen X-HELO: mx1.redhat.com Subject: Re: [PATCH] arc4random implementation To: libc-alpha@sourceware.org References: <6edcc7d8-56fd-ab1f-8f65-0ed12f45618f@redhat.com> From: Florian Weimer Message-ID: <811dad4f-e4d0-6eec-51f5-19b28d145bd5@redhat.com> Date: Thu, 21 Jun 2018 14:00:22 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.8.0 MIME-Version: 1.0 In-Reply-To: <6edcc7d8-56fd-ab1f-8f65-0ed12f45618f@redhat.com> I rebased the patch on current master. Additional changes: I added a missing TEMP_FAILURE_RETRY to the /dev/urandom-based reseeding. I changed the x86 hardware implementation not to us a separate flag variable, so this implementation doesn't need any CPU-specific state after all. Thanks, Florian Subject: [PATCH] Add arc4random, arc4random_buf, arc4random_uniform [BZ #4417] To: libc-alpha@sourceware.org 2018-05-31 Florian Weimer [BZ #4417] Add arc4random, arc4random_buf, arc4random_uniform. * LICENSES: Mention libgcrypt. * stdlib/Makefile (headers): Add bits/arc4random.h. (routines): Add arc4random, arc4random_buf, arc4random_uniform, arc4random-thread, arc4random-forkdetect arc4random-reseed, arc4random-aes. (tests): Add tst-arc4random-fork, tst-arc4random-thread. (tests-arc4random-internal): New variable. (tests-internal, tests-static): Add tst-arc4random-aes, tst-arc4random-forkdetect, tst-arc4random-stats. * stdlib/Versions (GLIBC_2.28): Export arc4random, arc4random_buf, arc4random_uniform. (GLIBC_PRIVATE): Export __libc_arc4random_buf, __libc_arc4random_uniform. * stdlib/stdlib.h [__USE_MISC]: Include . * stdlib/sys/random.h: Likewise. * sysdeps/generic/Makefile [$(subdir) == stdlib] (sysdep_routines): Add arc4random-cpu, arc4random-kernel. * sysdeps/nptl/fork.c (__libc_fork): Call __arc4random_after_fork_reinit. * sysdeps/mach/hurd/fork.c (__fork): Likewise. * sysdeps/x86/cpu-features.h (bit_cpu_RDRAND, bit_cpu_AES) (index_cpu_RDRAND, index_cpu_AES, reg_RDRAND, reg_AES): Define. * include/bits/arc4random.h: New file. * stdlib/arc4random-aes.c: Likewise. * stdlib/arc4random-forkdetect.c: Likewise. * stdlib/arc4random-forkdetect.h: Likewise. * stdlib/arc4random-private.h: Likewise. * stdlib/arc4random-reseed.c: Likewise. * stdlib/arc4random-thread.h: Likewise. * stdlib/arc4random.c: Likewise. * stdlib/arc4random_buf.c: Likewise. * stdlib/arc4random_uniform.c: Likewise. * stdlib/bits/arc4random.h: Likewise. * stdlib/tst-arc4random-aes.c: Likewise. * stdlib/tst-arc4random-fork.c: Likewise. * stdlib/tst-arc4random-forkdetect.c: Likewise. * stdlib/tst-arc4random-stats.c: Likewise. * stdlib/tst-arc4random-thread.c: Likewise. * sysdeps/generic/arc4random-cpu-data.h: Likewise. * sysdeps/generic/arc4random-cpu.c: Likewise. * sysdeps/generic/arc4random-cpu.h: Likewise. * sysdeps/generic/arc4random-kernel.c: Likewise. * sysdeps/generic/arc4random-kernel.h: Likewise. * sysdeps/unix/sysv/linux/arc4random-kernel.c: Likewise. * sysdeps/unix/sysv/linux/arc4random-kernel.h: Likewise. * sysdeps/x86/arc4random-cpu.c: Likewise. * sysdeps/x86/arc4random-cpu.h: Likewise. * sysdeps/**/libc*.abilist: Add arc4random, arc4random_buf, arc4random_uniform. diff --git a/LICENSES b/LICENSES index b29efe0108..1f12e1b121 100644 --- a/LICENSES +++ b/LICENSES @@ -398,3 +398,59 @@ Copyright 2001 by Stephen L. Moshier You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . */ + +stdlib/arc4random-aes.c imports code from libgcrypt, with the +following notices: + +Rijndael (AES) for GnuPG +Copyright (C) 2000, 2001, 2002, 2003, 2007, + 2008, 2011, 2012 Free Software Foundation, Inc. + +This file is part of Libgcrypt. + +Libgcrypt 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. + +Libgcrypt 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 this program; if not, see . +****************************************************************** +The code here is based on the optimized implementation taken from +http://www.esat.kuleuven.ac.be/~rijmen/rijndael/ on Oct 2, 2000, +which carries this notice: +------------------------------------------ +rijndael-alg-fst.c v2.3 April '2000 + +Optimised ANSI C code + +authors: v1.0: Antoon Bosselaers + v2.0: Vincent Rijmen + v2.3: Paulo Barreto + +This code is placed in the public domain. +------------------------------------------ + +rijndael-tables.h - Rijndael (AES) for GnuPG, +Copyright (C) 2000, 2001, 2002, 2003, 2007, + 2008 Free Software Foundation, Inc. + +This file is part of Libgcrypt. + +Libgcrypt 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. + +Libgcrypt 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 this program; if not, see . diff --git a/NEWS b/NEWS index d51fa09544..f64300a0be 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,8 @@ Major new features: - fdiv, fdivl, ddivl and corresponding fMdivfN, fMdivfNx, fMxdivfN and fMxdivfNx functions. +* The functions arc4random, arc4random_buf, arc4random_uniform are provided. + * Nominative and genitive month names are now supported for the following languages: Catalan, Czech, Scottish Gaelic, Upper Sorbian, and Walloon. The Catalan and Greek languages now support abbreviated alternative diff --git a/include/bits/arc4random.h b/include/bits/arc4random.h new file mode 100644 index 0000000000..c6bcb0eb76 --- /dev/null +++ b/include/bits/arc4random.h @@ -0,0 +1,37 @@ +/* Wrapper header for . + 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 + +#ifndef _ISOMAC +__typeof__ (arc4random) __libc_arc4random; +libc_hidden_proto (__libc_arc4random) + +__typeof__ (arc4random_buf) __libc_arc4random_buf; +libc_hidden_proto (__libc_arc4random_buf) + +__typeof__ (arc4random_uniform) __libc_arc4random_uniform; +libc_hidden_proto (__libc_arc4random_uniform) + +/* Called from the glibc fork function to reinitialize the subsystem + lock in the child process. This avoids deadlocks if fork is called + in multi-threaded processes. Fork detection is handled by other + means; see arc4random-forkdetect.h. */ +void __arc4random_after_fork_reinit (void) attribute_hidden; + +#endif diff --git a/stdlib/Makefile b/stdlib/Makefile index 808a8ceab7..2d6ccfb090 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -29,7 +29,7 @@ headers := stdlib.h bits/stdlib.h bits/stdlib-ldbl.h bits/stdlib-float.h \ ucontext.h sys/ucontext.h \ alloca.h fmtmsg.h \ bits/stdlib-bsearch.h sys/random.h bits/stdint-intn.h \ - bits/stdint-uintn.h + bits/stdint-uintn.h bits/arc4random.h routines := \ atof atoi atol atoll \ @@ -57,7 +57,10 @@ routines := \ a64l l64a \ rpmatch strfmon strfmon_l getsubopt xpg_basename fmtmsg \ strtoimax strtoumax wcstoimax wcstoumax \ - getcontext setcontext makecontext swapcontext + getcontext setcontext makecontext swapcontext \ + arc4random arc4random_buf arc4random_uniform arc4random-forkdetect \ + arc4random-reseed arc4random-aes + aux = grouping groupingwc tens_in_limb # These routines will be omitted from the libc shared object. @@ -84,12 +87,23 @@ tests := tst-strtol tst-strtod testmb testrand testsort testdiv \ tst-cxa_atexit tst-on_exit test-atexit-race \ test-at_quick_exit-race test-cxa_atexit-race \ test-on_exit-race test-dlclose-exit-race \ - tst-makecontext-align test-bz22786 tst-strtod-nan-sign + tst-makecontext-align test-bz22786 tst-strtod-nan-sign \ + tst-arc4random-fork tst-arc4random-thread tests-internal := tst-strtod1i tst-strtod3 tst-strtod4 tst-strtod5i \ tst-tls-atexit tst-tls-atexit-nodelete tests-static := tst-secure-getenv +# Thse arc4random tests access hidden symbols, so they need to be +# internal, statically linked tests. +tests-arc4random-internal = \ + tst-arc4random-aes \ + tst-arc4random-forkdetect \ + tst-arc4random-stats \ + +tests-internal += $(tests-arc4random-internal) +tests-static += $(tests-arc4random-internal) + ifeq ($(build-hardcoded-path-in-tests),yes) tests += tst-empty-env endif @@ -239,3 +253,6 @@ $(objpfx)tst-setcontext3.out: tst-setcontext3.sh $(objpfx)tst-setcontext3 $(evaluate-test) $(objpfx)tst-makecontext: $(libdl) +$(objpfx)tst-arc4random-fork: $(shared-thread-library) +$(objpfx)tst-arc4random-forkdetect: $(static-thread-library) +$(objpfx)tst-arc4random-thread: $(shared-thread-library) diff --git a/stdlib/Versions b/stdlib/Versions index a2dfa322ed..c624f4c2fc 100644 --- a/stdlib/Versions +++ b/stdlib/Versions @@ -139,6 +139,9 @@ libc { strtof32; strtof64; strtof32x; strtof32_l; strtof64_l; strtof32x_l; } + GLIBC_2.28 { + arc4random; arc4random_buf; arc4random_uniform; + } GLIBC_PRIVATE { # functions which have an additional interface since they are # are cancelable. @@ -149,5 +152,6 @@ libc { __libc_secure_getenv; __call_tls_dtors; __strtof_nan; __strtod_nan; __strtold_nan; + __libc_arc4random_buf; __libc_arc4random_uniform; } } diff --git a/stdlib/arc4random-aes.c b/stdlib/arc4random-aes.c new file mode 100644 index 0000000000..04a150ab57 --- /dev/null +++ b/stdlib/arc4random-aes.c @@ -0,0 +1,407 @@ +/* AES implementation for arc4random. + 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 + +/* Forward declarations for the code imported from libcrypt. */ +static void do_setkey (struct arc4random_data *, + const unsigned char *); +static void +do_encrypt_fn (const struct arc4random_data *data, + struct arc4random_block *b, + const struct arc4random_block *a); +static const uint32_t encT[256]; +static const uint32_t rcon[30]; + +void +__arc4random_schedule (struct arc4random_data *data, + const unsigned char *key) +{ + if (__arc4random_cpu_schedule (data, key)) + return; + do_setkey (data, key); +} + +void +__arc4random_block (const struct arc4random_data *data, + struct arc4random_personalization personalization, + struct arc4random_block *output) +{ + if (__arc4random_cpu_block (data, personalization, output)) + return; + + union + { + struct arc4random_personalization personalization; + struct arc4random_block block; + } u; + u.personalization = personalization; + do_encrypt_fn (data, output, &u.block); +} + +/* Reimplemented helper functions used by the code imported from + libgcrypt below. */ + +static inline uint32_t +le_bswap32 (uint32_t value) +{ +#if __BYTE_ORDER == __BIG_ENDIAN + return __builtin_bswap32 (value); +#elif __BYTE_ORDER == __LITTLE_ENDIAN + return value; +#else +# error invalid __BYTE_ORDER +#endif +} + +static inline uint32_t +rol (uint32_t value, unsigned int shift) +{ + return (value << (shift & 31)) | (value >> ((32 - shift) & 31)); +} + +/* The do_setkey and do_encrypt_fn functions are based on rijndael.c + from libgcrypt 1.8.1, which has the following copyright + information. */ + +/* Rijndael (AES) for GnuPG + * Copyright (C) 2000, 2001, 2002, 2003, 2007, + * 2008, 2011, 2012 Free Software Foundation, Inc. + * + * This file is part of Libgcrypt. + * + * Libgcrypt 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. + * + * Libgcrypt 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 this program; if not, see . + ******************************************************************* + * The code here is based on the optimized implementation taken from + * http://www.esat.kuleuven.ac.be/~rijmen/rijndael/ on Oct 2, 2000, + * which carries this notice: + *------------------------------------------ + * rijndael-alg-fst.c v2.3 April '2000 + * + * Optimised ANSI C code + * + * authors: v1.0: Antoon Bosselaers + * v2.0: Vincent Rijmen + * v2.3: Paulo Barreto + * + * This code is placed in the public domain. + *------------------------------------------ + * + * The SP800-38a document is available at: + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + * + */ + +static void +do_setkey (struct arc4random_data *data, const unsigned char *key) +{ + enum { KC = 4, keylen = 16 }; + const unsigned char *sbox = ((const unsigned char *) encT) + 1; + union + { + unsigned char tk[KC][4]; + uint32_t tk_u32[KC]; + } u; + int rconpointer = 0; + + typedef uint32_t unaligned_uint32_t __attribute__ ((aligned (1))); + const unaligned_uint32_t *key32 = (const unaligned_uint32_t *) key; + + for (int j = 0; j < KC; ++j) + u.tk_u32[j] = key32[j]; + + int r = 0; + int t = 0; + /* Copy values into round key array. */ + for (int j = 0; j < KC && r < arc4random_aes_rounds + 1; ) + { + for (; j < KC && t < 4; j++, t++) + data->generic[r][t] = le_bswap32 (u.tk_u32[j]); + if (t == 4) + { + r++; + t = 0; + } + } + + while (r < arc4random_aes_rounds + 1) + { + /* While not enough round key material calculated calculate + new values. */ + u.tk[0][0] ^= sbox[u.tk[KC - 1][1] * 4]; + u.tk[0][1] ^= sbox[u.tk[KC - 1][2] * 4]; + u.tk[0][2] ^= sbox[u.tk[KC - 1][3] * 4]; + u.tk[0][3] ^= sbox[u.tk[KC - 1][0] * 4]; + u.tk[0][0] ^= rcon[rconpointer++]; + + for (int j = 1; j < KC; j++) + u.tk_u32[j] ^= u.tk_u32[j - 1]; + + /* Copy values into round key array. */ + for (int j = 0; j < KC && r < arc4random_aes_rounds + 1; ) + { + for (; j < KC && t < 4; j++, t++) + data->generic[r][t] = le_bswap32 (u.tk_u32[j]); + if (t == 4) + { + r++; + t = 0; + } + } + } +} + +static void +do_encrypt_fn (const struct arc4random_data *data, + struct arc4random_block *b, + const struct arc4random_block *a) +{ + const unsigned char *sbox = ((const unsigned char *)encT) + 1; + int r; + uint32_t sa[4]; + uint32_t sb[4]; + + sb[0] = le_bswap32 (a->data[0]); + sb[1] = le_bswap32 (a->data[1]); + sb[2] = le_bswap32 (a->data[2]); + sb[3] = le_bswap32 (a->data[3]); + + sa[0] = sb[0] ^ data->generic[0][0]; + sa[1] = sb[1] ^ data->generic[0][1]; + sa[2] = sb[2] ^ data->generic[0][2]; + sa[3] = sb[3] ^ data->generic[0][3]; + + sb[0] = rol (encT[(uint8_t) (sa[0] >> (0 * 8))], (0 * 8)); + sb[3] = rol (encT[(uint8_t) (sa[0] >> (1 * 8))], (1 * 8)); + sb[2] = rol (encT[(uint8_t) (sa[0] >> (2 * 8))], (2 * 8)); + sb[1] = rol (encT[(uint8_t) (sa[0] >> (3 * 8))], (3 * 8)); + sa[0] = data->generic[1][0] ^ sb[0]; + + sb[1] ^= rol (encT[(uint8_t) (sa[1] >> (0 * 8))], (0 * 8)); + sa[0] ^= rol (encT[(uint8_t) (sa[1] >> (1 * 8))], (1 * 8)); + sb[3] ^= rol (encT[(uint8_t) (sa[1] >> (2 * 8))], (2 * 8)); + sb[2] ^= rol (encT[(uint8_t) (sa[1] >> (3 * 8))], (3 * 8)); + sa[1] = data->generic[1][1] ^ sb[1]; + + sb[2] ^= rol (encT[(uint8_t) (sa[2] >> (0 * 8))], (0 * 8)); + sa[1] ^= rol (encT[(uint8_t) (sa[2] >> (1 * 8))], (1 * 8)); + sa[0] ^= rol (encT[(uint8_t) (sa[2] >> (2 * 8))], (2 * 8)); + sb[3] ^= rol (encT[(uint8_t) (sa[2] >> (3 * 8))], (3 * 8)); + sa[2] = data->generic[1][2] ^ sb[2]; + + sb[3] ^= rol (encT[(uint8_t) (sa[3] >> (0 * 8))], (0 * 8)); + sa[2] ^= rol (encT[(uint8_t) (sa[3] >> (1 * 8))], (1 * 8)); + sa[1] ^= rol (encT[(uint8_t) (sa[3] >> (2 * 8))], (2 * 8)); + sa[0] ^= rol (encT[(uint8_t) (sa[3] >> (3 * 8))], (3 * 8)); + sa[3] = data->generic[1][3] ^ sb[3]; + + for (r = 2; r < arc4random_aes_rounds; r++) + { + sb[0] = rol (encT[(uint8_t) (sa[0] >> (0 * 8))], (0 * 8)); + sb[3] = rol (encT[(uint8_t) (sa[0] >> (1 * 8))], (1 * 8)); + sb[2] = rol (encT[(uint8_t) (sa[0] >> (2 * 8))], (2 * 8)); + sb[1] = rol (encT[(uint8_t) (sa[0] >> (3 * 8))], (3 * 8)); + sa[0] = data->generic[r][0] ^ sb[0]; + + sb[1] ^= rol (encT[(uint8_t) (sa[1] >> (0 * 8))], (0 * 8)); + sa[0] ^= rol (encT[(uint8_t) (sa[1] >> (1 * 8))], (1 * 8)); + sb[3] ^= rol (encT[(uint8_t) (sa[1] >> (2 * 8))], (2 * 8)); + sb[2] ^= rol (encT[(uint8_t) (sa[1] >> (3 * 8))], (3 * 8)); + sa[1] = data->generic[r][1] ^ sb[1]; + + sb[2] ^= rol (encT[(uint8_t) (sa[2] >> (0 * 8))], (0 * 8)); + sa[1] ^= rol (encT[(uint8_t) (sa[2] >> (1 * 8))], (1 * 8)); + sa[0] ^= rol (encT[(uint8_t) (sa[2] >> (2 * 8))], (2 * 8)); + sb[3] ^= rol (encT[(uint8_t) (sa[2] >> (3 * 8))], (3 * 8)); + sa[2] = data->generic[r][2] ^ sb[2]; + + sb[3] ^= rol (encT[(uint8_t) (sa[3] >> (0 * 8))], (0 * 8)); + sa[2] ^= rol (encT[(uint8_t) (sa[3] >> (1 * 8))], (1 * 8)); + sa[1] ^= rol (encT[(uint8_t) (sa[3] >> (2 * 8))], (2 * 8)); + sa[0] ^= rol (encT[(uint8_t) (sa[3] >> (3 * 8))], (3 * 8)); + sa[3] = data->generic[r][3] ^ sb[3]; + + r++; + + sb[0] = rol (encT[(uint8_t) (sa[0] >> (0 * 8))], (0 * 8)); + sb[3] = rol (encT[(uint8_t) (sa[0] >> (1 * 8))], (1 * 8)); + sb[2] = rol (encT[(uint8_t) (sa[0] >> (2 * 8))], (2 * 8)); + sb[1] = rol (encT[(uint8_t) (sa[0] >> (3 * 8))], (3 * 8)); + sa[0] = data->generic[r][0] ^ sb[0]; + + sb[1] ^= rol (encT[(uint8_t) (sa[1] >> (0 * 8))], (0 * 8)); + sa[0] ^= rol (encT[(uint8_t) (sa[1] >> (1 * 8))], (1 * 8)); + sb[3] ^= rol (encT[(uint8_t) (sa[1] >> (2 * 8))], (2 * 8)); + sb[2] ^= rol (encT[(uint8_t) (sa[1] >> (3 * 8))], (3 * 8)); + sa[1] = data->generic[r][1] ^ sb[1]; + + sb[2] ^= rol (encT[(uint8_t) (sa[2] >> (0 * 8))], (0 * 8)); + sa[1] ^= rol (encT[(uint8_t) (sa[2] >> (1 * 8))], (1 * 8)); + sa[0] ^= rol (encT[(uint8_t) (sa[2] >> (2 * 8))], (2 * 8)); + sb[3] ^= rol (encT[(uint8_t) (sa[2] >> (3 * 8))], (3 * 8)); + sa[2] = data->generic[r][2] ^ sb[2]; + + sb[3] ^= rol (encT[(uint8_t) (sa[3] >> (0 * 8))], (0 * 8)); + sa[2] ^= rol (encT[(uint8_t) (sa[3] >> (1 * 8))], (1 * 8)); + sa[1] ^= rol (encT[(uint8_t) (sa[3] >> (2 * 8))], (2 * 8)); + sa[0] ^= rol (encT[(uint8_t) (sa[3] >> (3 * 8))], (3 * 8)); + sa[3] = data->generic[r][3] ^ sb[3]; + } + + /* Last round is special. */ + + sb[0] = (sbox[(uint8_t) (sa[0] >> (0 * 8)) * 4]) << (0 * 8); + sb[3] = (sbox[(uint8_t) (sa[0] >> (1 * 8)) * 4]) << (1 * 8); + sb[2] = (sbox[(uint8_t) (sa[0] >> (2 * 8)) * 4]) << (2 * 8); + sb[1] = (sbox[(uint8_t) (sa[0] >> (3 * 8)) * 4]) << (3 * 8); + sa[0] = data->generic[r][0] ^ sb[0]; + + sb[1] ^= (sbox[(uint8_t) (sa[1] >> (0 * 8)) * 4]) << (0 * 8); + sa[0] ^= (sbox[(uint8_t) (sa[1] >> (1 * 8)) * 4]) << (1 * 8); + sb[3] ^= (sbox[(uint8_t) (sa[1] >> (2 * 8)) * 4]) << (2 * 8); + sb[2] ^= (sbox[(uint8_t) (sa[1] >> (3 * 8)) * 4]) << (3 * 8); + sa[1] = data->generic[r][1] ^ sb[1]; + + sb[2] ^= (sbox[(uint8_t) (sa[2] >> (0 * 8)) * 4]) << (0 * 8); + sa[1] ^= (sbox[(uint8_t) (sa[2] >> (1 * 8)) * 4]) << (1 * 8); + sa[0] ^= (sbox[(uint8_t) (sa[2] >> (2 * 8)) * 4]) << (2 * 8); + sb[3] ^= (sbox[(uint8_t) (sa[2] >> (3 * 8)) * 4]) << (3 * 8); + sa[2] = data->generic[r][2] ^ sb[2]; + + sb[3] ^= (sbox[(uint8_t) (sa[3] >> (0 * 8)) * 4]) << (0 * 8); + sa[2] ^= (sbox[(uint8_t) (sa[3] >> (1 * 8)) * 4]) << (1 * 8); + sa[1] ^= (sbox[(uint8_t) (sa[3] >> (2 * 8)) * 4]) << (2 * 8); + sa[0] ^= (sbox[(uint8_t) (sa[3] >> (3 * 8)) * 4]) << (3 * 8); + sa[3] = data->generic[r][3] ^ sb[3]; + + b->data[0] = le_bswap32 (sa[0]); + b->data[1] = le_bswap32 (sa[1]); + b->data[2] = le_bswap32 (sa[2]); + b->data[3] = le_bswap32 (sa[3]); +} + + +/* The encT table is derived from the rijndael-tables.h file in + libgcrypt 1.8.1. */ + +/* rijndael-tables.h - Rijndael (AES) for GnuPG, + * Copyright (C) 2000, 2001, 2002, 2003, 2007, + * 2008 Free Software Foundation, Inc. + * + * This file is part of Libgcrypt. + * + * Libgcrypt 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. + * + * Libgcrypt 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 this program; if not, see . + */ + +static const uint32_t encT[256] = + { + 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, + 0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, + 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, + 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, + 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, + 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, + 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, + 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, + 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, + 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, + 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, + 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, + 0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, + 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, + 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, + 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, + 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, + 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, + 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, + 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, + 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, + 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, + 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, + 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, + 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, + 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, + 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, + 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, + 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, + 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, + 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, + 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, + 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, + 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, + 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, + 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, + 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, + 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, + 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, + 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, + 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, + 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, + 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, + 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, + 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, + 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, + 0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, + 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, + 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, + 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, + 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, + 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, + 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, + 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c, + 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, + 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, + 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, + 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, + 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, + 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, + 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, + 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, + 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, + 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c + }; + +static const uint32_t rcon[30] = + { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, + 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, + 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 + }; diff --git a/stdlib/arc4random-forkdetect.c b/stdlib/arc4random-forkdetect.c new file mode 100644 index 0000000000..6b9208bbed --- /dev/null +++ b/stdlib/arc4random-forkdetect.c @@ -0,0 +1,520 @@ +/* Fork detection support for arc4random. + 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 + . */ + + +/* Fork detection comes in two major modes: + + If the kernel supports MADV_WIPEONFORK, fork_detect_data will point + to a private mapping which is cleared on fork. The not_forked + member is set to 1 during initialization, and the kernel will set + it to zero on fork. The forkdetect_pre_wipeonfork function will + check this value and reseed if necessary (setting not_forked to + non-zero again). forkdetect_post_wipeonfork only checks for + concurrent reseeding (which is not actually performed by the + current implementation; this may change in the future based on + certification requirements). The advantage of MADV_WIPEONFORK mode + is that no lock is needed in the common case, and, after + initialization, as long as the kernel has a working getrandom + implementation, arc4random cannot fail and terminate the process + due to a memory-allocation failure. + + Without MADV_WIPEONFORK support, things are considerably more + difficult. fork_detect_data points to a MAP_SHARED mapping. Fork + detection is performed by comparing the two values of + fork_detect_counter, one in the MAP_SHARED mapping (as a struct + member) and one as a global data variable in this file (in a + MAP_PRIVATE mapping). The counter is incremented while the + subsystem lock (arc4random_lock) is acquired. Atomic memory + access is used for the counter in the shared mapping. If the + counter values diverge, it means that some other process has + incremented the counter in the shared mapping, and we need to + reseed. Upon reseeding, the shared mapping is replaced with a new + MAP_SHARED mapping (in remap_reseed), and the fork detection + counter is reset to zero. Both forkdetect_pre_shared and + forkdetect_post_shared need to check and increment the fork + detection counter. This covers the case of a concurrent fork in a + multi-threaded program: + + Thread A Thread B Subprocess + + arc4random called + … + pre function called + fork + arc4random called + … + pre function called + (a) + … (compute randomness) + post function called + (b) + + The counter increment at (a) allows the post function at (b) to + detect counter divergence and trigger reseeding in thread A of the + parent process. tst-arc4random-forkdetect simulates various cases + of such interleaved executions. + + A minor variant occurs in both shared and wipe-on-fork mode because + reseed_counter is 64 bits, so a lock is needed if 64-bit atomics + are not avaliable. In MAP_SHARED mode, accesses always occur under + the subsystem lock, arc4random_lock. + + For the MAP_SHARED mode, only a 32-bit counter is used on the + shared mapping, plus an overflow check to trigger reseeding (see + FORK_DETECT_COUNTER_LIMIT). This construction is used with 64-bit + atomics available as well, to keep the 32-bit and 64-bit + implementations consistent. + + In MADV_WIPEONFORK mode, arc4random_lock is not used. See + fork_detect_lock below. Instead, the lock is located in the + MADV_WIPEONFORK memory, so that the kernel resets it on fork. (In + MAP_SHARED mode, resetting the lock on fork will only happen when + the glibc fork function is called, not for direct system calls; + this can lead to deadlocks in the subprocess if a multi-threaded + process forks with a direct system call, and the subprocess calls + arc4random.) + + As an optimization, the AES key schedule (in struct + arc4random_data) is stored in the mapping as well because it would + be a waste to perform a separate allocation for it. Concurrent + writes to the key schedule during reseeding may cause + __arc4random_block to operate on bad data, but the pre/post retry + loop will retry the computation in case of such reseeding. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Non-zero if MADV_WIPEONFORK is used for fork detection. + Initialized by fork_detect_alloc below. Used as a boolean. + (Atomic memory access requires the int type.) */ +static int wipeonfork_mode; + +/* The subsystem lock. Not always used; see fork_detect_lock + below. */ +__libc_lock_define_initialized (static, arc4random_lock) + +/* Definition for the declaration in . */ +__thread struct arc4random_perthread __arc4random_perthread; + +struct fork_detect_data +{ + /* This should be aligned on a page boundary. */ + struct arc4random_data data; + + /* False in MADV_WIPEONFORK mode after fork, otherwise true. Not a + boolean due to atomic memory access. */ + int not_forked; + + /* Lock used in MADV_WIPEONFORK mode, in preference of + arc4random_lock. The advantage is that it is automatically + reinitialized by the kernel on fork. */ + __libc_lock_define (, lock); + + uint32_t fork_detect_counter; /* For !wipeonfork_mode mode. */ +}; + +/* Used with allocate_once. Pointer to the magic mapped data used for + fork detection. */ +static void *fork_detect_data; + +/* Used to detect concurrent reseeding in + __arc4random_forkdetect_post. With wipeonfork_mode and 64-bit + atomics support, atomic memory accesses are used. Otherwise, the + subsystem lock (see fork_detect_lock) needs to be acquired around + accesses. (Writes happen during reseeding, which always acquires + the lock.) */ +static uint64_t reseed_counter; + +/* Used to assign unique numbers to individual threads calling + arc4random. Atomic access with 64-bit atomics; without them, the + subsystem lock is needed around access. */ +static uint64_t previous_thread_id; + +/* Used with !wipeonfork_mode to detect forks. Protected by the + arc4random_lock subsystem lock. */ +static uint32_t fork_detect_counter; + +/* Limit of a 32-bit fork counter value, to prevent overflow. (Can be + reduced for testing.) */ +#define FORK_DETECT_COUNTER_LIMIT 0XFFFFFFFEU + +/* Value for fork_detect_counter which forces reseeding because it + will never equal the counter on the shared page. */ +#define FORK_DETECT_COUNTER_RESEED 0XFFFFFFFFU + +/* Called to report mmap-related failures. */ +static void +__attribute__ ((noreturn)) +mmap_failure (void) +{ + __libc_fatal ("Fatal glibc error: Cannot allocate memory for arc4random\n"); +} + +/* Lock the subsystem lock. This arc4random_lock in shared mode, + and the lock on the (private) mapping in wipe-on-fork mode. */ +static inline void +fork_detect_lock (struct fork_detect_data *fd_data) +{ + if (wipeonfork_mode) + __libc_lock_lock (fd_data->lock); + else + __libc_lock_lock (arc4random_lock); +} + +/* Undo the efect of fork_detect_lock. */ +static inline void +fork_detect_unlock (struct fork_detect_data *fd_data) +{ + if (wipeonfork_mode) + __libc_lock_unlock (fd_data->lock); + else + __libc_lock_unlock (arc4random_lock); +} + +/* Used with allocate_once. */ +static void +fork_detect_free (void *unused, void *ptr) +{ + if (__munmap (ptr, sizeof (struct fork_detect_data)) != 0) + mmap_failure (); +} + +/* Used with allocate_once. Attempts to allocate a MAP_PRIVATE + mapping and set MADV_WIPEONFORK on it. If that fails, use a + MAP_SHARED mapping. Initializes wipeonfork_mode as a side + effect. */ +static void * +fork_detect_alloc (void *unused) +{ + void *ptr; +#ifdef MADV_WIPEONFORK + /* First attempt a private mapping with MADV_WIPEONFORK. */ + ptr = __mmap (NULL, sizeof (struct fork_detect_data), + PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (ptr == MAP_FAILED) + mmap_failure (); + { + int ret = __madvise (ptr, sizeof (struct fork_detect_data), + MADV_WIPEONFORK); + if (ret == 0) + { + /* Kernel supports fork detection. */ + + /* Relaxed MO store due to potentially racing writes from + parallel invocation of fork_detect_alloc. */ + atomic_store_relaxed (&wipeonfork_mode, 1); + + /* Atomic access for consistency. */ + struct fork_detect_data *fd_data = ptr; + atomic_store_relaxed (&fd_data->not_forked, 1); + + /* fd_data->lock is implicitly zero-initialized. */ + + __arc4random_reseed (&fd_data->data); + return fd_data; + } + else if (errno == EINVAL) + { + /* Kernel does not support MADV_WIPEONFORK. We need to use a + MAP_SHARED page for the detection. */ + fork_detect_free (NULL, ptr); + /* Fall through to MAP_SHARED allocation below. */ + } + else + mmap_failure (); + } +#endif + + ptr = __mmap (NULL, sizeof (struct fork_detect_data), + PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); + if (ptr == MAP_FAILED) + mmap_failure (); + + struct fork_detect_data *fd_data = ptr; + __arc4random_reseed (&fd_data->data); + return fd_data; +} + +/* Perform the reseeding: Obtain randomness and perform the key + schedule (stored in FD_DATA->data). Invalidate the per-thread + cache. Must be called with the subsystem lock acquired; see + fork_detect_lock. */ +static void +reseed (struct arc4random_forkdetect *fd, + struct fork_detect_data *fd_data) +{ + /* This may concurrently write to the arc4random data, but we ensure + that reads are consistent with help from the reseed_counter. */ + __arc4random_reseed (&fd_data->data); + + /* The caller has obtained a lock, but with 64-bit atomics, we still + need to synchronize with the acquire MO load in + forkdetect_pre_wipeonfork and forkdetect_post_wipeonfork. */ +#if __HAVE_64B_ATOMICS + fd->reseed_counter = atomic_load_relaxed (&reseed_counter) + 1; + if (fd->reseed_counter == 0) + __libc_fatal ("Fatal glibc error: arc4random reseed counter overflow\n"); + atomic_store_release (&reseed_counter, fd->reseed_counter); +#else + fd->reseed_counter = ++reseed_counter; +#endif + + /* Discard the thread cache. */ + __arc4random_thread_discard_cache (); +} + +/* Create a new shared mapping in place of the old one and initialize + it. This is fork MAP_SHARED-based fork detection after an + arc4random operation in another process has been detected (because + of counter divergence). The caller must have acquired the system + lock; see fork_detect_lock. */ +static void +remap_reseed (struct arc4random_forkdetect *fd, + struct fork_detect_data *fd_data) +{ + assert (!wipeonfork_mode); + void *ptr = __mmap (fd_data, sizeof (struct fork_detect_data), + PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED | MAP_FIXED, -1, 0); + if (ptr != fd_data) + mmap_failure (); + /* The mmap call above replaced the shared fork detection counter + with zero. Adjust the local counter. */ + fork_detect_counter = 0; + reseed (fd, fd_data); +} + +/* Pre-function for wipeonfork_mode. */ +static void +forkdetect_pre_wipeonfork (struct arc4random_forkdetect *fd, + struct fork_detect_data *fd_data) +{ + /* Synchronizes with the release MO store in below. Alternatively, + if a fork happened before a call to this function, the value will + be zero, too. */ + if (!atomic_load_acquire (&fd_data->not_forked)) + /* This indicates that the page was wiped on fork. We need to + reseed. */ + { + fork_detect_lock (fd_data); + if (atomic_load_relaxed (&fd_data->not_forked)) + /* Double-checked locking optimization. Another thread + performed reseeding. No need to redo it. Fall through + to the code below. */ + fork_detect_unlock (fd_data); + else + { + /* Actually perform the reseeding. */ + reseed (fd, fd_data); + + /* Synchronizes with the acquired MO load above. */ + atomic_store_release (&fd_data->not_forked, 1); + + fork_detect_unlock (fd_data); + return; + } + } + + /* Copy the reseed counter. */ +#if __HAVE_64B_ATOMICS + /* Synchronizes with the release MO store in reseed. */ + fd->reseed_counter = atomic_load_acquire (&reseed_counter); +#else + fork_detect_lock (fd_data); + fd->reseed_counter = reseed_counter; + fork_detect_unlock (fd_data); +#endif +} + +/* Post-function for wipeonfork_mode. */ +static bool +forkdetect_post_wipeonfork (struct arc4random_forkdetect *fd, + struct fork_detect_data *fd_data) +{ + uint64_t local_reseed_counter; +#if __HAVE_64B_ATOMICS + /* Synchronizes with the release MO store in reseed. */ + local_reseed_counter = atomic_load_acquire (&reseed_counter); +#else + fork_detect_lock (fd_data); + local_reseed_counter = reseed_counter; + fork_detect_unlock (fd_data); +#endif + + /* Retry in case of concurrent reseeding (after discarding the + per-thread cache). */ + if (local_reseed_counter != fd->reseed_counter) + { + fd->reseed_counter = local_reseed_counter; + __arc4random_thread_discard_cache (); + return true; + } + else + return false; +} + +/* Pre-function for !wipeonfork_mode. */ +static void +forkdetect_pre_shared (struct arc4random_forkdetect *fd, + struct fork_detect_data *fd_data) +{ + fork_detect_lock (fd_data); + + /* Increment the non-shared counter. */ + uint32_t local_old = fork_detect_counter++; + + /* Atomically increment the counter on the shared page, but guard + against overflow. */ + uint32_t shared_old = atomic_load_relaxed (&fd_data->fork_detect_counter); + do + if (shared_old != local_old || shared_old == FORK_DETECT_COUNTER_LIMIT) + { + /* The counters diverged or are about to overflow. We must + reseed. */ + remap_reseed (fd, fd_data); + fork_detect_unlock (fd_data); + return; + } + /* Retry if the values are not equal. */ + while (!atomic_compare_exchange_weak_relaxed + (&fd_data->fork_detect_counter, &shared_old, shared_old + 1)); + + fd->reseed_counter = reseed_counter; + fork_detect_unlock (fd_data); +} + +/* Post-function for !wipeonfork_mode. */ +static bool +forkdetect_post_shared (struct arc4random_forkdetect *fd, + struct fork_detect_data *fd_data) +{ + /* Detect current random number generator use by other + processes. */ + struct arc4random_forkdetect fd2; + forkdetect_pre_shared (&fd2, fd_data); + + if (fd2.reseed_counter != fd->reseed_counter) + { + /* Concurrent reseeding happened in another thread. Update the + reseed counter for the next attempt, discard the thread + cache, and try again. */ + fd->reseed_counter = fd2.reseed_counter; + __arc4random_thread_discard_cache (); + return true; + } + else + /* No fork detected, no reseeding observed. Use the computed + result. */ + return false; +} + +/* Use previous_thread_id to compute a thread ID for the current + thread, if necessary. */ +static void +assign_thread_id (struct fork_detect_data *fd_data) +{ + if (__arc4random_perthread.personalization.thread_id != 0) + /* Thread ID has already been assigned. */ + return; + + uint64_t new_id; +#if __HAVE_64B_ATOMICS + /* Should be atomic_add_fetch_relaxed, but it is missing. */ + new_id = atomic_fetch_add_relaxed (&previous_thread_id, 1) + 1; +#else + fork_detect_lock (fd_data); + new_id = ++previous_thread_id; + fork_detect_unlock (fd_data); +#endif + + if (new_id == 0) + __libc_fatal ("Fatal glibc error: arc4random thread counter overflow\n"); + + __arc4random_perthread.personalization.thread_id = new_id; +} + +struct arc4random_data * +__arc4random_forkdetect_pre (struct arc4random_forkdetect *fd) +{ + struct fork_detect_data *fd_data = allocate_once + (&fork_detect_data, fork_detect_alloc, fork_detect_free, NULL); + assign_thread_id (fd_data); + + if (wipeonfork_mode) + forkdetect_pre_wipeonfork (fd, fd_data); + else + forkdetect_pre_shared (fd, fd_data); + return &fd_data->data; +} + +bool +__arc4random_forkdetect_post (struct arc4random_forkdetect *fd) +{ + /* Use relaxed MO for consistency. allocate_once has been called + before from this thread, via __arc4random_forkdetect_pre, so + initialization has happened. */ + struct fork_detect_data *fd_data = atomic_load_relaxed (&fork_detect_data); + + + /* wipeonfork_mode has been initialized by the preceeding call to + __arc4random_forkdetect_pre. */ + if (wipeonfork_mode) + return forkdetect_post_wipeonfork (fd, fd_data); + else + return forkdetect_post_shared (fd, fd_data); +} + +void +__arc4random_after_fork_reinit (void) +{ + /* We are single-threaded at this point, and the current thread is + the only running thread. */ + + /* Use relaxed MO load for consistency. */ + struct fork_detect_data *fd_data = atomic_load_relaxed (&fork_detect_data); + if (fd_data == NULL) + /* The arc4random subsystem has not been initialized yet, so there + is nothing to do. */ + return; + + if (!wipeonfork_mode) + { + /* In MAP_SHARED mode, the kernel has not reset the subsystem + lock for us. Do it now, and also force reseeding in the + subprocess. (We could try to acquire the lock and reset only + if we fail, but always reseeding makes the behavior more + consistent in both modes.) */ + __libc_lock_init (arc4random_lock); + fork_detect_counter = FORK_DETECT_COUNTER_RESEED; + } + + /* We are reseeding in the subprocess, which means that we can reset + the thread number and reseed counter. Without 64-bit atomics, + the state of the 64-bit counters previous_thread_id, + reseed_counter could be corrupted. */ + previous_thread_id = 1; + __arc4random_perthread.personalization.thread_id = 1; + reseed_counter = 0; +} diff --git a/stdlib/arc4random-forkdetect.h b/stdlib/arc4random-forkdetect.h new file mode 100644 index 0000000000..a7b94bb863 --- /dev/null +++ b/stdlib/arc4random-forkdetect.h @@ -0,0 +1,53 @@ +/* Fork detection support for arc4random. + 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 + . */ + +#ifndef _ARC4RANDOM_FORKDETECT +#define _ARC4RANDOM_FORKDETECT + +#include +#include + +/* Internal randomness-generating functions need to be wrapped in + calls to __arc4random_forkdetect_pre and + __arc4random_forkdetect_post like this: + + struct arc4random_forkdetect fd; + struct arc4random_data *data = __arc4random_forkdetect_pre (&fd); + + do + ... + while (__arc4random_forkdetect_post (&fd)); + + This ensures that the randomness-generating process is restarted if + a fork (or reseeding) operating is detected. + + The data pointer can be passed to __arc4random_buf_internal (which + does not perform fork detection on its own). */ + +struct arc4random_forkdetect +{ + /* For internal use in arc4random-forkdetect.c only. */ + uint64_t reseed_counter; +}; + +struct arc4random_data *__arc4random_forkdetect_pre + (struct arc4random_forkdetect *) attribute_hidden; +bool __arc4random_forkdetect_post (struct arc4random_forkdetect *) + attribute_hidden; + +#endif /* _ARC4RANDOM_FORKDETECT */ diff --git a/stdlib/arc4random-private.h b/stdlib/arc4random-private.h new file mode 100644 index 0000000000..b2d4f2230d --- /dev/null +++ b/stdlib/arc4random-private.h @@ -0,0 +1,110 @@ +/* Declarations and definitions used by the arc4random implementation. + 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 + . */ + +#ifndef _ARC4RANDOM_PRIVATE_H +#define _ARC4RANDOM_PRIVATE_H + +#include +#include + +#include + +/* AES-128 is specified to use 10 rounds. */ +enum { arc4random_aes_rounds = 10 }; + +/* AES-128 produces output blocks of 16 bytes. */ +enum { arc4random_block_size = 16 }; + +/* AES-128 has a key size of 16 bytes. */ +enum { arc4random_key_size = 16 }; + +/* Output from AES. */ +struct arc4random_block +{ + uint32_t data[4]; +}; +_Static_assert (sizeof (struct arc4random_block) == arc4random_block_size, + "AES-128 block size must match struct arc4random_block"); + +/* Key schedule for AES-128. Global data shared by the entire + process. */ +struct arc4random_data +{ + /* AES key schedule. Increase alignment to help with concurrent + read access. */ + uint32_t generic[arc4random_aes_rounds + 1][4] + __attribute__ ((aligned (128))); + + struct arc4random_cpu_data cpu; +}; + +/* Generate a key and initialize the key schedule in *DATA. */ +void __arc4random_reseed (struct arc4random_data *data) attribute_hidden; + +/* Initialize the AES-128 key schedule, either DATA->generic or + DATA->cpu, depending on CPU support. KEY must point to 16 bytes of + key material. This is only supposed to be called by + __arc4random_reseed. */ +void __arc4random_schedule (struct arc4random_data *data, + const unsigned char *key) + attribute_hidden; + +/* The data which is encrypted using AES-128. */ +struct arc4random_personalization +{ + /* Unique number assigned to this thread. Note that the ID is *not* + necessarily unique across threads in different processes. + Therefore, it is still necessary to ensure divergence of the + random bit streams by other means. */ + uint64_t thread_id; + + /* The block number within a single thread. This must be advanced + each time a new block of randomness is obtained. */ + uint64_t block_number; +}; +_Static_assert (sizeof (struct arc4random_personalization) + == arc4random_block_size, + "personalization size matches AES-128 block size"); + +/* Computes one block of random data and stores it in *OUTPUT. Can + use DATA->cpu if it has been initialized by the CPU-specific + code. */ +void __arc4random_block (const struct arc4random_data *data, + const struct arc4random_personalization, + struct arc4random_block *output) + attribute_hidden; + +/* Include these last, so that the inline function implementations can + use the above. */ +#include +#include + +/* POSIX-based fallback implementation of key generation, using + /dev/urandom. For use in __arc4random_reseed. */ +void __arc4random_fallback_generate_key (unsigned char *key) attribute_hidden; + + +/* Use personalization data for the current thread to compute random + bytes and store them in the range [buffer, end). Fork detection + needs to be performed by the caller. */ +void __arc4random_buf_internal (struct arc4random_data *data, + unsigned char *buffer, unsigned char *end) + attribute_hidden; + + +#endif /* _ARC4RANDOM_PRIVATE_H */ diff --git a/stdlib/arc4random-reseed.c b/stdlib/arc4random-reseed.c new file mode 100644 index 0000000000..a1a417daf5 --- /dev/null +++ b/stdlib/arc4random-reseed.c @@ -0,0 +1,58 @@ +/* Reseeding the arc4random generator. + 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 + +void +__arc4random_fallback_generate_key (unsigned char *key) +{ + int fd = __open_nocancel ("/dev/urandom", O_RDONLY); + if (fd < 0) + __libc_fatal ("Fatal glibc error: cannot open /dev/urandom\n"); + + unsigned char *p = key; + unsigned char *end = key + arc4random_key_size; + do + { + ssize_t ret = TEMP_FAILURE_RETRY (__read_nocancel (fd, p, end - p)); + if (ret <= 0) + __libc_fatal ("Fatal glibc error: cannot read from /dev/urandom\n"); + p += ret; + } + while (p < end); + + if (__close_nocancel (fd) != 0) + __libc_fatal ("Fatal glibc error: cannot close /dev/urandom\n"); +} + +void +__arc4random_reseed (struct arc4random_data *data) +{ + unsigned char key[arc4random_key_size]; + /* Prefer the kernel and CPU generators before fallback via + /dev/urandon. */ + if (!(__arc4random_kernel_generate_key (key) + || __arc4random_cpu_generate_key (key))) + __arc4random_fallback_generate_key (key); + + __arc4random_schedule (data, key); +} diff --git a/stdlib/arc4random-thread.h b/stdlib/arc4random-thread.h new file mode 100644 index 0000000000..1ca5fff02b --- /dev/null +++ b/stdlib/arc4random-thread.h @@ -0,0 +1,67 @@ +/* Multi-threading support for arc4random. + 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 + . */ + +#ifndef _ARC4RANDOM_THREAD_H +#define _ARC4RANDOM_THREAD_H + +#include + +/* Subystem lock used to protect internal data structures (fork + detection, thread number). */ +__libc_lock_define (extern, __arc4random_lock attribute_hidden) + +/* Per-thread data used by the arc4random subsystem. */ +struct arc4random_perthread +{ + /* Data to make AES-128 output thread-specific. */ + struct arc4random_personalization personalization; + + /* Cached output from AES-128. */ + struct + { + unsigned char unused_bytes; + unsigned char bytes[arc4random_block_size - 1]; + } cache; +}; + +extern __thread struct arc4random_perthread __arc4random_perthread + attribute_tls_model_ie attribute_hidden; + +/* Slow path for __arc4random_thread_init below. */ +void __arc4random_thread_init_slow (void) attribute_hidden; + +/* Assign the per-thread personalization number in the + __arc4random_perthread.personalization.thread_id field of the + current thread. */ +static inline void +__arc4random_thread_init (void) +{ + if (__arc4random_perthread.personalization.thread_id == 0) + __arc4random_thread_init_slow (); +} + +/* Discard the data in the per-thread cache. Needs to be called after + reseeding (on all threads eventually). */ +static inline void +__arc4random_thread_discard_cache (void) +{ + /* Mark all bytes as used. */ + __arc4random_perthread.cache.unused_bytes = 0; +} + +#endif /* _ARC4RANDOM_THREAD_H */ diff --git a/stdlib/arc4random.c b/stdlib/arc4random.c new file mode 100644 index 0000000000..57800b8822 --- /dev/null +++ b/stdlib/arc4random.c @@ -0,0 +1,31 @@ +/* Unpredictable random numbers between 0 and 2**-31 (inclusive). + 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 + +_Static_assert (sizeof (unsigned int) == 4, "32-bit unsigned int"); + +unsigned int +__libc_arc4random (void) +{ + unsigned int result; + __libc_arc4random_buf (&result, sizeof (result)); + return result; +} +libc_hidden_def (__libc_arc4random) +weak_alias (__libc_arc4random, arc4random) diff --git a/stdlib/arc4random_buf.c b/stdlib/arc4random_buf.c new file mode 100644 index 0000000000..6b294951b0 --- /dev/null +++ b/stdlib/arc4random_buf.c @@ -0,0 +1,79 @@ +/* Fill a buffer with unpredictable random numbers. + 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 +#include + +void +__arc4random_buf_internal (struct arc4random_data *data, + unsigned char *buffer, unsigned char *end) +{ + while (buffer < end) + { + if (__arc4random_perthread.cache.unused_bytes == 0) + { + /* No cached data. Replenish it by computing another + block. */ + struct arc4random_block block; + ++__arc4random_perthread.personalization.block_number; + __arc4random_block (data, __arc4random_perthread.personalization, + &block); + + _Static_assert (sizeof (__arc4random_perthread.cache) + == sizeof (block), "padding in cache"); + memcpy (&__arc4random_perthread.cache, &block, sizeof (block)); + + /* One byte goes into the buffer, so that the next iteration + of the loop uses the else branch below. Use the byte in + the unused_bytes position for that (which needs to be + initialized). */ + *buffer = __arc4random_perthread.cache.unused_bytes; + ++buffer; + _Static_assert (sizeof (block) <= 256, "block size"); + __arc4random_perthread.cache.unused_bytes = sizeof (block) - 1; + } + else + { + size_t to_copy = end - buffer; + if (to_copy > __arc4random_perthread.cache.unused_bytes) + to_copy = __arc4random_perthread.cache.unused_bytes; + memcpy (buffer, __arc4random_perthread.cache.bytes, to_copy); + buffer += to_copy; + __arc4random_perthread.cache.unused_bytes -= to_copy; + } + } +} + +void +__libc_arc4random_buf (void *buffer, size_t length) +{ + struct arc4random_forkdetect fd; + struct arc4random_data *data = __arc4random_forkdetect_pre (&fd); + + do + __arc4random_buf_internal (data, buffer, buffer + length); + while (__arc4random_forkdetect_post (&fd)); +} +libc_hidden_def (__libc_arc4random_buf) +weak_alias (__libc_arc4random_buf, arc4random_buf) diff --git a/stdlib/arc4random_uniform.c b/stdlib/arc4random_uniform.c new file mode 100644 index 0000000000..2ac5f720d9 --- /dev/null +++ b/stdlib/arc4random_uniform.c @@ -0,0 +1,159 @@ +/* An unpredictable number up to a certain limit. + 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 + +/* Return the number of bytes which cover values up to the limit. */ +__attribute__ ((const)) +static uint32_t +byte_count (uint32_t n) +{ + if (n <= (1U << 8)) + return 1; + else if (n <= (1U << 16)) + return 2; + else if (n <= (1U << 24)) + return 3; + else + return 4; +} + +/* Fill the lower bits of the result with randomness, according to the + number of bytes requested. */ +static uint32_t +random_bytes (struct arc4random_data *data, uint32_t byte_count) +{ + uint32_t result = 0; + unsigned char *ptr = (unsigned char *) &result; + if (__BYTE_ORDER == __BIG_ENDIAN) + ptr += 4 - byte_count; + __arc4random_buf_internal (data, ptr, ptr + byte_count); + return result; +} + +static uint32_t +compute_uniform (struct arc4random_data *data, uint32_t n) +{ + if (n <= 1) + /* There is no valid return value for a zero limit, and 0 is the + only possible result for limit 1. */ + return 0; + + /* The bits variable serves as a source for bits. Prefetch the + minimum number of bytes needed. */ + uint32_t bits; + uint32_t bits_length; + { + unsigned count = byte_count (n); + bits = random_bytes (data, count); + bits_length = count * 8; + } + + /* Powers of two are easy. */ + if (powerof2 (n)) + return bits & (n - 1); + + /* The general case. This algorithm follows Jérémie Lumbroso, + Optimal Discrete Uniform Generation from Coin Flips, and + Applications (2013), who credits Donald E. Knuth and Andrew + C. Yao, The complexity of nonuniform random number generation + (1976), for solving the general case. + + The implementation below unrolls the initialization stage of the + loop, where v is less than n. */ + + /* Use 64-bit variables even though the intermediate results are + never larger that 33 bits. This ensures the code easier to + compile on 64-bit architectures. */ + uint64_t v; + uint64_t c; + + /* Initialize v and c. v is the smallest power of 2 which is larger + than n.*/ + { + uint32_t log2p1 = 32 - __builtin_clz (n); + v = 1ULL << log2p1; + c = bits & (v - 1); + bits >>= log2p1; + bits_length -= log2p1; + } + + /* At the start of the loop, c is uniformly distributed within the + half-open interval [0, v), and v < 2n < 2**33. */ + while (true) + { + if (v >= n) + { + /* If the candidate is less than n, accept it. */ + if (c < n) + /* c is uniformly distributed on [0, n). */ + return c; + else + { + /* c is uniformly distributed on [n, v). */ + v -= n; + c -= n; + /* The distribution was shifted, so c is uniformly + distributed on [0, v) again. */ + } + } + /* v < n here. */ + + /* Replenish the bit source if necessary. */ + if (bits_length == 0) + { + unsigned char *target = (unsigned char *) &bits; + /* Overwrite the least significant byte. */ + if (__BYTE_ORDER == __BIG_ENDIAN) + target += 3; + __arc4random_buf_internal (data, target, target + 1); + bits_length = 8; + } + + /* Double the range. No overflow because v < n < 2**32. */ + v *= 2; + /* v < 2n here. */ + + /* Extract a bit and append it to c. c remains less than v and + thus 2**33. */ + c = (c << 1) | (bits & 1); + bits >>= 1; + --bits_length; + + /* At this point, c is uniformly distributed on [0, v) again, + and v < 2n < 2**33. */ + } +} + +uint32_t +__libc_arc4random_uniform (uint32_t n) +{ + uint32_t result; + struct arc4random_forkdetect fd; + struct arc4random_data *data = __arc4random_forkdetect_pre (&fd); + + do + result = compute_uniform (data, n); + while (__arc4random_forkdetect_post (&fd)); + + return result; +} +libc_hidden_def (__libc_arc4random_uniform) +weak_alias (__libc_arc4random_uniform, arc4random_uniform) diff --git a/stdlib/bits/arc4random.h b/stdlib/bits/arc4random.h new file mode 100644 index 0000000000..5b8f9a5273 --- /dev/null +++ b/stdlib/bits/arc4random.h @@ -0,0 +1,31 @@ +/* arc4random interfaces. + 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 + . */ + +#if ! (defined (_STDLIB_H) || defined (_SYS_RANDOM_H)) +# error "Never use directly; include instead." +#endif + +/* Return a random integer between zero and 2**31-1 (inclusive). */ +extern unsigned int arc4random (void) __THROW __wur; + +/* Fill the buffer with random data. */ +extern void arc4random_buf (void *, size_t) __THROW __nonnull ((1)); + +/* Return a random number between zero (inclusive) and the specified + limit (exclusive). */ +extern unsigned int arc4random_uniform (unsigned int) __THROW __wur; diff --git a/stdlib/stdlib.h b/stdlib/stdlib.h index 6b1ead31e0..fc9e565c1c 100644 --- a/stdlib/stdlib.h +++ b/stdlib/stdlib.h @@ -532,6 +532,9 @@ extern int seed48_r (unsigned short int __seed16v[3], extern int lcong48_r (unsigned short int __param[7], struct drand48_data *__buffer) __THROW __nonnull ((1, 2)); + +# include + # endif /* Use misc. */ #endif /* Use misc or X/Open. */ diff --git a/stdlib/sys/random.h b/stdlib/sys/random.h index 056312ca3b..9f69693b12 100644 --- a/stdlib/sys/random.h +++ b/stdlib/sys/random.h @@ -37,6 +37,8 @@ ssize_t getrandom (void *__buffer, size_t __length, success or -1 on error. */ int getentropy (void *__buffer, size_t __length) __wur; +#include + __END_DECLS #endif /* _SYS_RANDOM_H */ diff --git a/stdlib/tst-arc4random-aes.c b/stdlib/tst-arc4random-aes.c new file mode 100644 index 0000000000..c5ff2e0a83 --- /dev/null +++ b/stdlib/tst-arc4random-aes.c @@ -0,0 +1,70 @@ +/* Test for low-level AES-128 routines for the arc4random PRNG. + 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 + . */ + +/* This test uses a test vector to make sure that the low-level + AES-128 generator produces the expected result. */ + +#include +#include +#include +#include + +static int +do_test (void) +{ + static const unsigned char at_random[16] + = { 1, 2, 3, 4, 5, 6, 7, 8, + 131, 132, 133, 134, 135, 136, 137, 138}; + TEST_COMPARE (sizeof (at_random), arc4random_key_size); + + /* Test overall version. */ + + struct arc4random_data data; + memset (&data, 0, sizeof (data)); + __arc4random_schedule (&data, at_random); + + struct arc4random_personalization personalization; + const char *personalization_data + = "\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\x00\x01\x02\x03\x04\x05\x06\x07"; + _Static_assert (sizeof (personalization) == 16, "sizeof (personalization)"); + memcpy (&personalization, personalization_data, sizeof (personalization)); + struct arc4random_block result; + __arc4random_block (&data, personalization, &result); + + const char *expected + = "\xa8\x0b\xa2\x8a\xdd\x9ew\\\000aK\xdc/\xa7\xd5\x16"; + TEST_COMPARE_BLOB (&result, sizeof (result), expected, 16); + + /* Test CPU-specific version. */ + + memset (&data, 0, sizeof (data)); + if (__arc4random_cpu_schedule (&data, at_random)) + { + puts ("info: CPU support active"); + memcpy (&personalization, personalization_data, sizeof (personalization)); + TEST_VERIFY (__arc4random_cpu_block (&data, personalization, &result)); + TEST_COMPARE_BLOB (&result, sizeof (result), expected, 16); + } + else + puts ("info: CPU support not active"); + + return 0; +} + +#include diff --git a/stdlib/tst-arc4random-fork.c b/stdlib/tst-arc4random-fork.c new file mode 100644 index 0000000000..6cfb2345b2 --- /dev/null +++ b/stdlib/tst-arc4random-fork.c @@ -0,0 +1,172 @@ +/* Test that subprocesses 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 + . */ + +/* 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 (run == runs / 2) + { + /* In the middle, desynchronize the block cache by consuming + an odd number of bytes. */ + char buf; + arc4random_buf (&buf, 1); + } + + 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-forkdetect.c b/stdlib/tst-arc4random-forkdetect.c new file mode 100644 index 0000000000..63f998ffc3 --- /dev/null +++ b/stdlib/tst-arc4random-forkdetect.c @@ -0,0 +1,437 @@ +/* Low-level tests fork arc4random fork detection. + 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 + . */ + +/* This test attempts to cover interleaving of the pre and post + fork-detect functions in a parent process and a subprocess. The + behavior depends on the internals of the fork detection + implementation (MAP_SHARED mode vs MADV_WIPEONFORK mode), and + adjustments will be needed to this test if glibc initializes the + arc4random subsystem as part of process startup. + + The test is not multi-threaded and assumes that + __arc4random_after_fork_reinit is not called during fork for such + processes. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Test without interference from another process. */ +static void +test_quiet (void *unused) +{ + struct arc4random_forkdetect fd; + + /* Some arbitrary number of invocations. Low enough to finish + quickly, and not to trigger reseeding. */ + for (int i = 0; i < 100; ++i) + { + memset (&fd, 0xcc, sizeof (fd)); + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, 0); + TEST_VERIFY (!__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, 0); + } + + /* Inject a different reseed counter and check that + __arc4random_forkdetect_post indicates a re-run of the randomness + generation. This simulates concurrent reseeding by another + thread. */ + memset (&fd, 0xcc, sizeof (fd)); + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, 0); + fd.reseed_counter = -1; + /* Indicate that the cache has data. */ + __arc4random_perthread.cache.unused_bytes = 1; + TEST_VERIFY (__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, 0); + /* The cache must have been consumed. */ + TEST_COMPARE (__arc4random_perthread.cache.unused_bytes, 0); +} + +/* Data allocated using support_shared_allocate, for passing data back + and forth between processes. */ +struct shared_data +{ + /* Used to pass back data from test_noninterleaved_subprocess_1. */ + uint64_t uninterleaved_reseed_counter_subprocess; + + /* Used to serialize execution between parent process and + subprocess. */ + pthread_barrier_t barrier; + + /* If true, MAP_SHARED mode, otherwise MADV_WIPEONFORK mode. */ + bool shared_mode; + + /* Used to pass along data for certain tests. */ + int test_mode; +}; + +/* Helper for test_noninterleaved_subprocess below. */ +static void +test_noninterleaved_subprocess_1 (void *closure) +{ + struct shared_data *shared_data = closure; + + struct arc4random_forkdetect fd; + memset (&fd, 0xcc, sizeof (fd)); + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + shared_data->uninterleaved_reseed_counter_subprocess = fd.reseed_counter; + TEST_VERIFY (!__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, + shared_data->uninterleaved_reseed_counter_subprocess); +} + +/* Test sequential execution in two processes. */ +static void +test_noninterleaved_subprocess (void *closure) +{ + struct shared_data *shared_data = closure; + + struct arc4random_forkdetect fd; + memset (&fd, 0xcc, sizeof (fd)); + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, 0); + TEST_VERIFY (!__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, 0); + + support_isolate_in_subprocess (test_noninterleaved_subprocess_1, + shared_data); + + memset (&fd, 0xcc, sizeof (fd)); + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + shared_data->shared_mode = fd.reseed_counter != 0; + if (shared_data->shared_mode) + { + TEST_COMPARE (fd.reseed_counter, 1); + TEST_COMPARE (shared_data->uninterleaved_reseed_counter_subprocess, 0); + } + else + { + TEST_COMPARE (fd.reseed_counter, 0); + TEST_COMPARE (shared_data->uninterleaved_reseed_counter_subprocess, 1); + } + + TEST_VERIFY (!__arc4random_forkdetect_post (&fd)); + if (shared_data->shared_mode) + TEST_COMPARE (fd.reseed_counter, 1); + else + TEST_COMPARE (fd.reseed_counter, 0); +} + +/* Run a test where the parent process calls the pre function before + the subprocess. Three test modes: + 0: No initialization in parent. + 1: Pre function called in parent before fork. + 2: Pre and post function called in parent, and pre again after fork. */ +static void +test_parent_first (void *closure) +{ + struct shared_data *shared_data = closure; + printf ("info: %s: test mode %d\n", __func__, shared_data->test_mode); + + struct arc4random_forkdetect fd; + memset (&fd, 0xcc, sizeof (fd)); + + bool init_parent_after_fork; + bool subprocess_will_reseed; + bool parent_will_retry; + + switch (shared_data->test_mode) + { + case 0: + /* Do not perform any initialization in the parent process + before the fork. This means that the subprocess will not + increment the reseed counter. */ + init_parent_after_fork = true; + parent_will_retry = false; + subprocess_will_reseed = false; + break; + case 1: + /* Call the pre function function. */ + init_parent_after_fork = false; + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, 0); + + if (shared_data->shared_mode) + { + /* In shared mapping mode, the subprocess sees consistent + counters after the fork, so it will not reseed. */ + subprocess_will_reseed = false; + /* The parent will have to reseed in this case. */ + parent_will_retry = true; + } + else + { + /* Without a shared mapping, the subprocess will see a wiped + mapping and reseed. */ + subprocess_will_reseed = true; + /* This will not affect the parent. */ + parent_will_retry = false; + } + break; + case 2: + /* Perform a full cycle to attain initialization. */ + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, 0); + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, 0); + memset (&fd, 0xcc, sizeof (fd)); + init_parent_after_fork = true; + + /* The subprocess will either see a wiped mapping or diverged + counters, and will reseed. */ + subprocess_will_reseed = true; + /* This does not affect the parent. */ + parent_will_retry = false; + break; + default: + FAIL_EXIT1 ("%s: invalid test mode: %d", + __func__, shared_data->test_mode); + } + + pid_t pid = xfork (); + if (pid != 0) + { + /* Parent process. */ + + if (init_parent_after_fork) + { + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, 0); + } + + /* Signal the subprocess. */ + xpthread_barrier_wait (&shared_data->barrier); + + /* Wait for the subprocess to execute the pre function. */ + xpthread_barrier_wait (&shared_data->barrier); + + if (parent_will_retry) + { + /* The subprocess did not reseed, so the parent has to. */ + TEST_VERIFY (__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, 1); + TEST_VERIFY (!__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, 1); + } + else + { + /* The subprocess reseeded itself, replacing the shared + mapping (if it was shared in the place). This means that + the parent process does not need to reseed again, + irrespective of shared_data->shared_mode. */ + TEST_VERIFY (!__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, 0); + } + + /* Run post function in subprocess. */ + xpthread_barrier_wait (&shared_data->barrier); + + int status; + xwaitpid (pid, &status, 0); + TEST_COMPARE (status, 0); + } + else + { + /* Subprocess. Wait for parent to execute the pre function. */ + xpthread_barrier_wait (&shared_data->barrier); + + /* Pretend that there is some data in the cache that needs to be + discarded (when actually reseeding). */ + if (subprocess_will_reseed) + __arc4random_perthread.cache.unused_bytes = 1; + + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, subprocess_will_reseed); + + /* The cache is always empty at this point. */ + TEST_COMPARE (__arc4random_perthread.cache.unused_bytes, 0); + + /* Signal the parent. */ + xpthread_barrier_wait (&shared_data->barrier); + + /* Wait for parent to execute the post function. */ + xpthread_barrier_wait (&shared_data->barrier); + + /* The subprocess isolated itself from the parent through + reseeding. No retry needed. */ + TEST_VERIFY (!__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, subprocess_will_reseed); + + _exit (0); + } +} + +/* Run a test where the subprocess process calls the pre function + before the parent. Four test modes: + + 0: no initialization before fork, post in subprocess goes first + 1: initialization before fork, post in subprocess goes first + 2: no initialization before fork, post in parent goes first + 3: initialization before fork, post in parent goes first */ +static void +test_subprocess_first (void *closure) +{ + struct shared_data *shared_data = closure; + printf ("info: %s: test mode %d\n", __func__, shared_data->test_mode); + + struct arc4random_forkdetect fd; + memset (&fd, 0xcc, sizeof (fd)); + + bool subprocess_will_reseed; + bool parent_will_reseed; + + TEST_VERIFY (shared_data->test_mode >= 0); + TEST_VERIFY (shared_data->test_mode <= 3); + + if (shared_data->test_mode & 1) + { + /* Do not perform any initialization in the parent process. + Neither process will reseed because they will simply + initialize. */ + subprocess_will_reseed = false; + parent_will_reseed = false; + } + else + { + /* Complete initialization in the parent process. */ + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, 0); + TEST_VERIFY (!__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, 0); + memset (&fd, 0xcc, sizeof (fd)); + + if (shared_data->shared_mode) + { + /* The subprocess will not reseed because it sees consistent + counters. */ + subprocess_will_reseed = false; + /* But the parent will see counter divergence. */ + parent_will_reseed = true; + } + else + { + /* The subprocess will see a wiped mapping. */ + subprocess_will_reseed = true; + /* The parent process is not affected by this. */ + parent_will_reseed = false; + } + } + + pid_t pid = xfork (); + if (pid != 0) + { + /* Parent process. Wait for the pre function to run in the + subprocess. */ + xpthread_barrier_wait (&shared_data->barrier); + + /* When reseeding, pretend that there is some data in the cache + that needs to be invalidated. */ + if (parent_will_reseed) + __arc4random_perthread.cache.unused_bytes = 1; + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, parent_will_reseed); + + /* The cache is always empty at this point. */ + TEST_COMPARE (__arc4random_perthread.cache.unused_bytes, 0); + + if (shared_data->test_mode & 2) + /* Signal the subprocess. */ + xpthread_barrier_wait (&shared_data->barrier); + + TEST_VERIFY (!__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, parent_will_reseed); + + if (!(shared_data->test_mode & 2)) + /* Signal the subprocess. */ + xpthread_barrier_wait (&shared_data->barrier); + + int status; + xwaitpid (pid, &status, 0); + TEST_COMPARE (status, 0); + } + else + { + /* When reseeding, pretend that there is some cached data to + invalidate. */ + if (subprocess_will_reseed) + __arc4random_perthread.cache.unused_bytes = 1; + + TEST_VERIFY (__arc4random_forkdetect_pre (&fd) != NULL); + TEST_COMPARE (fd.reseed_counter, subprocess_will_reseed); + + /* The cache is always empty at this point. */ + TEST_COMPARE (__arc4random_perthread.cache.unused_bytes, 0); + + /* Signal the parent. */ + xpthread_barrier_wait (&shared_data->barrier); + + /* Wait for parent to execute the pre function in the + parent. */ + xpthread_barrier_wait (&shared_data->barrier); + + TEST_VERIFY (!__arc4random_forkdetect_post (&fd)); + TEST_COMPARE (fd.reseed_counter, subprocess_will_reseed); + + _exit (0); + } +} + +static int +do_test (void) +{ + struct shared_data *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, 2); + xpthread_barrierattr_destroy (&attr); + } + + support_isolate_in_subprocess (test_quiet, NULL); + + support_isolate_in_subprocess (test_noninterleaved_subprocess, shared_data); + if (shared_data->shared_mode) + puts ("info: MAP_SHARED mode"); + else + puts ("info: MADV_WIPEONFORK mode"); + + for (shared_data->test_mode = 0; shared_data->test_mode < 3; + ++shared_data->test_mode) + support_isolate_in_subprocess (test_parent_first, shared_data); + + for (shared_data->test_mode = 0; shared_data->test_mode < 4; + ++shared_data->test_mode) + support_isolate_in_subprocess (test_subprocess_first, shared_data); + + xpthread_barrier_destroy (&shared_data->barrier); + support_shared_free (shared_data); + return 0; +} + +#include diff --git a/stdlib/tst-arc4random-stats.c b/stdlib/tst-arc4random-stats.c new file mode 100644 index 0000000000..22ac1569a7 --- /dev/null +++ b/stdlib/tst-arc4random-stats.c @@ -0,0 +1,158 @@ +/* Statistical tests for arc4random-related functions. + 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 + +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[16][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_fallback_generate_key. */ +static bool +generate_fallback (unsigned char *key) +{ + __arc4random_fallback_generate_key (key); + return true; +} + +/* 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: CPU implementation test"); + find_stuck_bytes (__arc4random_cpu_generate_key); + + puts ("info: kernel implementation test"); + find_stuck_bytes (__arc4random_kernel_generate_key); + + puts ("info: POSIX fallback implementation test"); + find_stuck_bytes (generate_fallback); + + 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 diff --git a/sysdeps/generic/Makefile b/sysdeps/generic/Makefile index d287bfafc6..065135de76 100644 --- a/sysdeps/generic/Makefile +++ b/sysdeps/generic/Makefile @@ -26,3 +26,8 @@ sysdep_routines += framestate unwind-pe shared-only-routines += framestate unwind-pe endif endif + +ifeq ($(subdir),stdlib) +sysdep_routines += arc4random-cpu +sysdep_routines += arc4random-kernel +endif diff --git a/sysdeps/generic/arc4random-cpu-data.h b/sysdeps/generic/arc4random-cpu-data.h new file mode 100644 index 0000000000..ed023e6526 --- /dev/null +++ b/sysdeps/generic/arc4random-cpu-data.h @@ -0,0 +1,25 @@ +/* CPU support for arc4random, type definitions. Generic version. + 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 + . */ + +#ifndef _ARC4RANDOM_CPU_DATA_H +#define _ARC4RANDOM_CPU_DATA_H + +/* The generic version does not have any CPU-specific data. */ +struct arc4random_cpu_data { }; + +#endif /* _ARC4RANDOM_CPU_DATA_H */ diff --git a/sysdeps/generic/arc4random-cpu.c b/sysdeps/generic/arc4random-cpu.c new file mode 100644 index 0000000000..846b587721 --- /dev/null +++ b/sysdeps/generic/arc4random-cpu.c @@ -0,0 +1,20 @@ +/* CPU support for arc4random. Generic version. + 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 + . */ + +/* The generic implementation uses inline functions, so there is no + code here. */ diff --git a/sysdeps/generic/arc4random-cpu.h b/sysdeps/generic/arc4random-cpu.h new file mode 100644 index 0000000000..978c790fc8 --- /dev/null +++ b/sysdeps/generic/arc4random-cpu.h @@ -0,0 +1,49 @@ +/* CPU support for arc4random. Generic version. + 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 + . */ + +#ifndef _ARC4RANDOM_CPU_H +#define _ARC4RANDOM_CPU_H + +#include + +/* In the generic implementation, CPU-specific reseeding always + fails. */ +static inline bool +__arc4random_cpu_generate_key (unsigned char *key) +{ + return false; +} + +/* The generic implementation does nothing. */ +static inline bool +__arc4random_cpu_schedule (struct arc4random_data *data, + const unsigned char *key) +{ + return false; +} + +/* The generic implementation does nothing. */ +static inline bool +__arc4random_cpu_block (const struct arc4random_data *data, + struct arc4random_personalization personalization, + struct arc4random_block *output) +{ + return false; +} + +#endif /* _ARC4RANDOM_CPU_H */ diff --git a/sysdeps/generic/arc4random-kernel.c b/sysdeps/generic/arc4random-kernel.c new file mode 100644 index 0000000000..d8d4ea715b --- /dev/null +++ b/sysdeps/generic/arc4random-kernel.c @@ -0,0 +1,20 @@ +/* Kernel support for arc4random. Generic version. + 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 + . */ + +/* The generic implementation uses inline functions, so there is no + code here. */ diff --git a/sysdeps/generic/arc4random-kernel.h b/sysdeps/generic/arc4random-kernel.h new file mode 100644 index 0000000000..0f9ac54647 --- /dev/null +++ b/sysdeps/generic/arc4random-kernel.h @@ -0,0 +1,30 @@ +/* Kernel support for arc4random. Generic version. + 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 + . */ + +#ifndef _ARC4RANDOM_KERNEL_H +#define _ARC4RANDOM_KERNEL_H + +/* In the generic implementation, kernel-specific reseeding always + fails. */ +static inline bool +__arc4random_kernel_generate_key (unsigned char *unused) +{ + return false; +} + +#endif /* _ARC4RANDOM_KERNEL_H */ diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c index 2d1e64c8d1..57e1056602 100644 --- a/sysdeps/mach/hurd/fork.c +++ b/sysdeps/mach/hurd/fork.c @@ -642,6 +642,9 @@ __fork (void) /* Forking clears the trace flag. */ __sigemptyset (&_hurdsig_traced); + /* Reinitialize the arc4random lock. */ + call_function_static_weak (__arc4random_after_fork_reinit); + /* Release malloc locks. */ _hurd_malloc_fork_child (); call_function_static_weak (__malloc_fork_unlock_child); diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist index 2cb507052b..5dddcde5af 100644 --- a/sysdeps/mach/hurd/i386/libc.abilist +++ b/sysdeps/mach/hurd/i386/libc.abilist @@ -2033,6 +2033,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c index ec56a827eb..92517d238a 100644 --- a/sysdeps/nptl/fork.c +++ b/sysdeps/nptl/fork.c @@ -120,6 +120,9 @@ __libc_fork (void) /* Reset the lock state in the multi-threaded case. */ if (multiple_threads) { + /* Reinitialize the arc4random lock. */ + call_function_static_weak (__arc4random_after_fork_reinit); + /* Release malloc locks. */ call_function_static_weak (__malloc_fork_unlock_child); diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist index 80cdb98e1c..3934723139 100644 --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist @@ -2131,3 +2131,6 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist index c761f61c43..3b879336a7 100644 --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist @@ -2026,6 +2026,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/arc4random-kernel.c b/sysdeps/unix/sysv/linux/arc4random-kernel.c new file mode 100644 index 0000000000..0c58bda7a9 --- /dev/null +++ b/sysdeps/unix/sysv/linux/arc4random-kernel.c @@ -0,0 +1,38 @@ +/* Kernel support for arc4random. Linux version. + 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 + +bool +__arc4random_kernel_generate_key (unsigned char *key) +{ +#ifdef __NR_getrandom + /* Use a direct system call to avoid cancellation. arc4random must + not block, so use GRND_NONBLOCK. */ + + INTERNAL_SYSCALL_DECL (err); + int ret = INTERNAL_SYSCALL (getrandom, err, 3, key, arc4random_key_size, + GRND_NONBLOCK); + if (!INTERNAL_SYSCALL_ERROR_P (ret, err) && ret == arc4random_key_size) + return true; + +#endif + return false; +} diff --git a/sysdeps/unix/sysv/linux/arc4random-kernel.h b/sysdeps/unix/sysv/linux/arc4random-kernel.h new file mode 100644 index 0000000000..3568c9b030 --- /dev/null +++ b/sysdeps/unix/sysv/linux/arc4random-kernel.h @@ -0,0 +1,24 @@ +/* Kernel support for arc4random. Linux version. + 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 + . */ + +#ifndef _ARC4RANDOM_KERNEL_H +#define _ARC4RANDOM_KERNEL_H + +bool __arc4random_kernel_generate_key (unsigned char *) attribute_hidden; + +#endif /* _ARC4RANDOM_KERNEL_H */ diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist index 6aa58c3ca7..35a31bd85d 100644 --- a/sysdeps/unix/sysv/linux/arm/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist @@ -115,6 +115,9 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.4 _Exit F GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist index d10695b7d3..532716fc58 100644 --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist @@ -1872,6 +1872,9 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist index 23092ab6d7..6e2dd0f9d0 100644 --- a/sysdeps/unix/sysv/linux/i386/libc.abilist +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist @@ -2037,6 +2037,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist index 7bf259e86c..c243b08a81 100644 --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist @@ -1907,6 +1907,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist index 4673bcd79b..70792dfac6 100644 --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist @@ -116,6 +116,9 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.4 _Exit F GLIBC_2.4 _IO_2_1_stderr_ D 0x98 GLIBC_2.4 _IO_2_1_stdin_ D 0x98 diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist index 1f8ac40399..5eeb85c794 100644 --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist @@ -1981,6 +1981,9 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist index 09277f5954..4bc644436e 100644 --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist @@ -2122,3 +2122,6 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist index f562e20f23..fc7feb00c1 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist @@ -1959,6 +1959,9 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist index ceb7388829..9ecca7ae7e 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist @@ -1957,6 +1957,9 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist index 5765f487a2..434000aa16 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist @@ -1965,6 +1965,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist index a84bb45a38..dd5a9141d8 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist @@ -1961,6 +1961,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist index e43295986c..e476c6ac4c 100644 --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist @@ -2163,3 +2163,6 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist index a5f2b23068..12715d8598 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist @@ -1985,6 +1985,9 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist index e4cbe36279..bbe72d56d4 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist @@ -1989,6 +1989,9 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist index 9869feb56b..8cee0565ec 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc-le.abilist @@ -2221,3 +2221,6 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist index e526dc4627..7388b75a32 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist @@ -116,6 +116,9 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 _Exit F GLIBC_2.3 _IO_2_1_stderr_ D 0xe0 GLIBC_2.3 _IO_2_1_stdin_ D 0xe0 diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist index e6319eef8d..50d9f8c768 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist @@ -2093,3 +2093,6 @@ GLIBC_2.27 xdrstdio_create F GLIBC_2.27 xencrypt F GLIBC_2.27 xprt_register F GLIBC_2.27 xprt_unregister F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist index 41cdda0c2e..df1747a916 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist @@ -1994,6 +1994,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist index 8a756cf287..abe2ef27c5 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist @@ -1900,6 +1900,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist index 999bddd1db..a0f324505c 100644 --- a/sysdeps/unix/sysv/linux/sh/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist @@ -1876,6 +1876,9 @@ GLIBC_2.27 wcstof32x F GLIBC_2.27 wcstof32x_l F GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist index 7c4296fc10..da4e91e914 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist @@ -1988,6 +1988,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist index dafe9d74b7..7f4c1b98d4 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist @@ -1930,6 +1930,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist index f72d494920..aa172dd463 100644 --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist @@ -1888,6 +1888,9 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F GLIBC_2.3 __ctype_b_loc F GLIBC_2.3 __ctype_tolower_loc F GLIBC_2.3 __ctype_toupper_loc F diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist index 96c9fa050e..184021af09 100644 --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist @@ -2139,3 +2139,6 @@ GLIBC_2.27 wcstof64 F GLIBC_2.27 wcstof64_l F GLIBC_2.27 wcstof64x F GLIBC_2.27 wcstof64x_l F +GLIBC_2.28 arc4random F +GLIBC_2.28 arc4random_buf F +GLIBC_2.28 arc4random_uniform F diff --git a/sysdeps/x86/arc4random-cpu.c b/sysdeps/x86/arc4random-cpu.c new file mode 100644 index 0000000000..59ae40d275 --- /dev/null +++ b/sysdeps/x86/arc4random-cpu.c @@ -0,0 +1,152 @@ +/* CPU support for arc4random. x86 version. + 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 + +/* This can be changed to "rdseed" for failure testing (on + implementations where the instruction occasionally fails). */ +#define RAND_INSTRUCTION "rdrand" + +bool +__arc4random_cpu_generate_key (unsigned char *key) +{ + const struct cpu_features *cpu_features = __get_cpu_features (); + if (!CPU_FEATURES_CPU_P (cpu_features, RDRAND)) + return false; + + _Static_assert (arc4random_key_size == 16, "key size"); + + bool success; + uint32_t reg; + __asm__ (RAND_INSTRUCTION " %0\n\t" + "jnc 1f\n\t" + "movl %0, (%2)\n\t" + RAND_INSTRUCTION " %0\n\t" + "jnc 1f\n\t" + "movl %0, 4(%2)\n\t" + RAND_INSTRUCTION " %0\n\t" + "jnc 1f\n\t" + "movl %0, 8(%2)\n\t" + RAND_INSTRUCTION " %0\n\t" + "jnc 1f\n\t" + "movl %0, 12(%2)\n\t" + "mov $1, %%eax\n\t" + "jmp 2f\n\t" + "1:\n\t" + "xor %%eax, %%eax\n\t" + "2:\n\t" + : "=&r" (reg), "=a" (success) + : "r" (key) + : "cc", "memory"); + return success; +} + +/* Separate function to enable SSE2 register support in the + compiler. */ +static void +__attribute__ ((target ("sse2"))) +sse2_schedule (struct arc4random_data *data, const unsigned char *key) +{ +#define KEYGEN_ROUND(n, code) \ + "aeskeygenassist $ " #code ", %%xmm0, %%xmm1\n\t" \ + "pshufd $0xff, %%xmm1, %%xmm1\n\t" \ + "movdqa %%xmm0, %%xmm2\n\t" \ + "pslldq $4, %%xmm2\n\t" \ + "pxor %%xmm2, %%xmm0\n\t" \ + "pslldq $4, %%xmm2\n\t" \ + "pxor %%xmm2, %%xmm0\n\t" \ + "pslldq $4, %%xmm2\n\t" \ + "pxor %%xmm2, %%xmm1\n\t" \ + "pxor %%xmm1, %%xmm0\n\t" \ + "movdqa %%xmm0, " #n " * 16(%1)\n\t" + + asm ("movdqu (%0), %%xmm0\n\t" + "movdqa %%xmm0, 0x00(%1)\n\t" + KEYGEN_ROUND (1, 0x01) + KEYGEN_ROUND (2, 0x02) + KEYGEN_ROUND (3, 0x04) + KEYGEN_ROUND (4, 0x08) + KEYGEN_ROUND (5, 0x10) + KEYGEN_ROUND (6, 0x20) + KEYGEN_ROUND (7, 0x40) + KEYGEN_ROUND (8, 0x80) + KEYGEN_ROUND (9, 0x1b) + KEYGEN_ROUND (10, 0x36) + : + : "r" (key), "r" (&data->generic) + : "cc", "memory", "xmm0", "xmm1", "xmm2"); +#undef KEYGEN_ROUND +} + +bool +__arc4random_cpu_schedule (struct arc4random_data *data, + const unsigned char *key) +{ + const struct cpu_features *cpu_features = __get_cpu_features (); + if (!CPU_FEATURES_CPU_P (cpu_features, AES)) + return false; + + /* We could use the generic implementation because the data layout + is the same, but to avoid side-channel issues, we use + aeskeygenassist (which is also faster). */ + sse2_schedule (data, key); + + return true; +} + +/* Separate function to enable SSE2 register support in the + compiler. */ +static void +__attribute__ ((target ("sse2"))) +block_sse2 (const struct arc4random_data *data, + struct arc4random_personalization *personalization, + struct arc4random_block *output) +{ + __asm__ ("movdqu (%0), %%xmm0\n\t" + "pxor (%1), %%xmm0\n\t" + "aesenc 0x10(%1), %%xmm0\n\t" + "aesenc 0x20(%1), %%xmm0\n\t" + "aesenc 0x30(%1), %%xmm0\n\t" + "aesenc 0x40(%1), %%xmm0\n\t" + "aesenc 0x50(%1), %%xmm0\n\t" + "aesenc 0x60(%1), %%xmm0\n\t" + "aesenc 0x70(%1), %%xmm0\n\t" + "aesenc 0x80(%1), %%xmm0\n\t" + "aesenc 0x90(%1), %%xmm0\n\t" + "aesenclast 0xa0(%1), %%xmm0\n\t" + "movdqu %%xmm0, (%2)" + : + : "r" (personalization), "r" (&data->generic), "r" (output) + : "cc", "memory", "xmm0"); +} + +bool +__arc4random_cpu_block (const struct arc4random_data *data, + struct arc4random_personalization personalization, + struct arc4random_block *output) +{ + const struct cpu_features *cpu_features = __get_cpu_features (); + if (!CPU_FEATURES_CPU_P (cpu_features, RDRAND)) + return false; + + block_sse2 (data, &personalization, output); + + return true; +} diff --git a/sysdeps/x86/arc4random-cpu.h b/sysdeps/x86/arc4random-cpu.h new file mode 100644 index 0000000000..eab2c3c157 --- /dev/null +++ b/sysdeps/x86/arc4random-cpu.h @@ -0,0 +1,33 @@ +/* CPU support for arc4random. x86 version. + 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 + . */ + +#ifndef _ARC4RANDOM_CPU_H +#define _ARC4RANDOM_CPU_H + +#include + +/* These return true if the CPU supports AES instructions, and perform + the operation. */ +bool __arc4random_cpu_generate_key (unsigned char *key) attribute_hidden; +bool __arc4random_cpu_schedule (struct arc4random_data *data, + const unsigned char *key) attribute_hidden; +bool __arc4random_cpu_block (const struct arc4random_data *data, + struct arc4random_personalization personalization, + struct arc4random_block *output) attribute_hidden; + +#endif /* _ARC4RANDOM_CPU_H */ diff --git a/sysdeps/x86/cpu-features.h b/sysdeps/x86/cpu-features.h index 624e681e96..1493615e34 100644 --- a/sysdeps/x86/cpu-features.h +++ b/sysdeps/x86/cpu-features.h @@ -61,6 +61,8 @@ #define bit_cpu_LZCNT (1 << 5) #define bit_cpu_MOVBE (1 << 22) #define bit_cpu_POPCNT (1 << 23) +#define bit_cpu_RDRAND (1 << 30) +#define bit_cpu_AES (1 << 30) /* COMMON_CPUID_INDEX_7. */ #define bit_cpu_BMI1 (1 << 3) @@ -210,6 +212,8 @@ extern const struct cpu_features *__get_cpu_features (void) # define index_cpu_IBT COMMON_CPUID_INDEX_7 # define index_cpu_SHSTK COMMON_CPUID_INDEX_7 # define index_cpu_FSRM COMMON_CPUID_INDEX_7 +# define index_cpu_RDRAND COMMON_CPUID_INDEX_1 +# define index_cpu_AES COMMON_CPUID_INDEX_1 # define reg_CX8 edx # define reg_CMOV edx @@ -242,6 +246,8 @@ extern const struct cpu_features *__get_cpu_features (void) # define reg_IBT edx # define reg_SHSTK ecx # define reg_FSRM edx +# define reg_RDRAND ecx +# define reg_AES ecx # define index_arch_Fast_Rep_String FEATURE_INDEX_1 # define index_arch_Fast_Copy_Backward FEATURE_INDEX_1