From 09309e09ecfa663cb22c8a210521704df0f18a5d Mon Sep 17 00:00:00 2001 From: Janne Blomqvist Date: Fri, 19 Aug 2016 17:19:51 +0300 Subject: [PATCH] Use a XOR cipher instead of byte shuffling to protect against bad seeds. libgfortran: 2016-08-19 Janne Blomqvist * intrinsics/random.c (xor_keys): New array with "secret" keys. (scramble_seed): XOR given seed with xor_keys array rather than shuffling bytes. (unscramble_seed): Remove function. (random_seed_i4): Use new scramble_seed. (random_seed_i8): Likewise. frontend: 2016-08-19 Janne Blomqvist * intrinsics.texi (RANDOM_NUMBER): Remove reference to init_random_seed in example. (RANDOM_SEED): Remove warning to not set all seed values to 0. From-SVN: r239613 --- gcc/fortran/ChangeLog | 6 ++++ gcc/fortran/intrinsic.texi | 4 --- libgfortran/ChangeLog | 11 ++++++- libgfortran/intrinsics/random.c | 53 +++++++++++++++++++-------------- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 4d1c2ad4eab..cfc70c1b9e8 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,9 @@ +2016-08-19 Janne Blomqvist + + * intrinsics.texi (RANDOM_NUMBER): Remove reference to + init_random_seed in example. + (RANDOM_SEED): Remove warning to not set all seed values to 0. + 2016-08-18 David Malcolm * error.c (gfc_diagnostic_starter): Update for change to diff --git a/gcc/fortran/intrinsic.texi b/gcc/fortran/intrinsic.texi index 67b02315344..8cca9b162bf 100644 --- a/gcc/fortran/intrinsic.texi +++ b/gcc/fortran/intrinsic.texi @@ -11155,7 +11155,6 @@ Subroutine @smallexample program test_random_number REAL :: r(5,5) - CALL init_random_seed() ! see example of RANDOM_SEED CALL RANDOM_NUMBER(r) end program @end smallexample @@ -11192,9 +11191,6 @@ alias any other stream in the system, where @var{N} is the number of threads that have used @code{RANDOM_NUMBER} so far during the program execution. -Note that setting any of the seed values to zero should be avoided as -it can result in poor quality random numbers being generated. - @item @emph{Standard}: Fortran 95 and later diff --git a/libgfortran/ChangeLog b/libgfortran/ChangeLog index 6fd12228645..10dc07af130 100644 --- a/libgfortran/ChangeLog +++ b/libgfortran/ChangeLog @@ -1,4 +1,13 @@ -2016-08-16 Janne Blomqvist +2016-08-19 Janne Blomqvist + + * intrinsics/random.c (xor_keys): New array with "secret" keys. + (scramble_seed): XOR given seed with xor_keys array rather than + shuffling bytes. + (unscramble_seed): Remove function. + (random_seed_i4): Use new scramble_seed. + (random_seed_i8): Likewise. + +2016-08-19 Janne Blomqvist * intrinsics/random.c (master_init): New variable. (init_rand_state): Move below getosrandom (), maybe initialize diff --git a/libgfortran/intrinsics/random.c b/libgfortran/intrinsics/random.c index 35c76113b1a..739dbeb1e98 100644 --- a/libgfortran/intrinsics/random.c +++ b/libgfortran/intrinsics/random.c @@ -722,28 +722,33 @@ arandom_r16 (gfc_array_r16 *x) #endif +/* Number of elements in master_state array. */ +#define SZU64 (sizeof (master_state) / sizeof (uint64_t)) -static void -scramble_seed (unsigned char *dest, unsigned char *src, int size) -{ - int i; - for (i = 0; i < size; i++) - dest[(i % 2) * (size / 2) + i / 2] = src[i]; -} +/* Keys for scrambling the seed in order to avoid poor seeds. */ +static const uint64_t xor_keys[] = { + 0xbd0c5b6e50c2df49ULL, 0xd46061cd46e1df38ULL, 0xbb4f4d4ed6103544ULL, + 0x114a583d0756ad39ULL, 0x4b5ad8623d0aaab6ULL, 0x3f2ed7afbe0c0f21ULL, + 0xdec83fd65f113445ULL, 0x3824f8fbc4f10d24ULL, 0x5d9025af05878911ULL, + 0x500bc46b540340e9ULL, 0x8bd53298e0d00530ULL, 0x57886e40a952e06aULL, + 0x926e76c88e31cdb6ULL, 0xbd0724dac0a3a5f9ULL, 0xc5c8981b858ab796ULL, + 0xbb12ab2694c2b32cULL +}; + + +/* Since a XOR cipher is symmetric, we need only one routine, and we + can use it both for encryption and decryption. */ static void -unscramble_seed (unsigned char *dest, unsigned char *src, int size) +scramble_seed (uint64_t *dest, const uint64_t *src) { - int i; - - for (i = 0; i < size; i++) - dest[i] = src[(i % 2) * (size / 2) + i / 2]; + for (int i = 0; i < (int) SZU64; i++) + dest[i] = src[i] ^ xor_keys[i]; } - /* random_seed is used to seed the PRNG with either a default set of seeds or user specified set of seeds. random_seed must be called with no argument or exactly one argument. */ @@ -751,7 +756,7 @@ unscramble_seed (unsigned char *dest, unsigned char *src, int size) void random_seed_i4 (GFC_INTEGER_4 *size, gfc_array_i4 *put, gfc_array_i4 *get) { - unsigned char seed[sizeof (master_state)]; + uint64_t seed[SZU64]; #define SZ (sizeof (master_state) / sizeof (GFC_INTEGER_4)) /* Check that we only have one argument present. */ @@ -778,12 +783,12 @@ random_seed_i4 (GFC_INTEGER_4 *size, gfc_array_i4 *put, gfc_array_i4 *get) init_rand_state (rs, false); /* Unscramble the seed. */ - unscramble_seed (seed, (unsigned char *) rs->s, sizeof seed); + scramble_seed (seed, rs->s); /* Then copy it back to the user variable. */ for (size_t i = 0; i < SZ ; i++) memcpy (&(get->base_addr[(SZ - 1 - i) * GFC_DESCRIPTOR_STRIDE(get,0)]), - seed + i * sizeof(GFC_UINTEGER_4), + (unsigned char*) seed + i * sizeof(GFC_UINTEGER_4), sizeof(GFC_UINTEGER_4)); /* Finally copy the value of p after the seed. */ @@ -814,13 +819,13 @@ random_seed_i4 (GFC_INTEGER_4 *size, gfc_array_i4 *put, gfc_array_i4 *get) /* We copy the seed given by the user. */ for (size_t i = 0; i < SZ; i++) - memcpy (seed + i * sizeof(GFC_UINTEGER_4), + memcpy ((unsigned char*) seed + i * sizeof(GFC_UINTEGER_4), &(put->base_addr[(SZ - 1 - i) * GFC_DESCRIPTOR_STRIDE(put,0)]), sizeof(GFC_UINTEGER_4)); /* We put it after scrambling the bytes, to paper around users who provide seeds with quality only in the lower or upper part. */ - scramble_seed ((unsigned char *) master_state, seed, sizeof seed); + scramble_seed (master_state, seed); njumps = 0; master_init = true; init_rand_state (rs, true); @@ -828,8 +833,6 @@ random_seed_i4 (GFC_INTEGER_4 *size, gfc_array_i4 *put, gfc_array_i4 *get) rs->p = put->base_addr[SZ * GFC_DESCRIPTOR_STRIDE(put, 0)] & 15; } - - __gthread_mutex_unlock (&random_lock); } #undef SZ @@ -840,6 +843,8 @@ iexport(random_seed_i4); void random_seed_i8 (GFC_INTEGER_8 *size, gfc_array_i8 *put, gfc_array_i8 *get) { + uint64_t seed[SZU64]; + /* Check that we only have one argument present. */ if ((size ? 1 : 0) + (put ? 1 : 0) + (get ? 1 : 0) > 1) runtime_error ("RANDOM_SEED should have at most one argument present."); @@ -864,9 +869,12 @@ random_seed_i8 (GFC_INTEGER_8 *size, gfc_array_i8 *put, gfc_array_i8 *get) if (!rs->init) init_rand_state (rs, false); + /* Unscramble the seed. */ + scramble_seed (seed, rs->s); + /* This code now should do correct strides. */ for (size_t i = 0; i < SZ; i++) - memcpy (&(get->base_addr[i * GFC_DESCRIPTOR_STRIDE(get,0)]), &rs->s[i], + memcpy (&(get->base_addr[i * GFC_DESCRIPTOR_STRIDE(get,0)]), &seed[i], sizeof (GFC_UINTEGER_8)); get->base_addr[SZ * GFC_DESCRIPTOR_STRIDE(get, 0)] = rs->p; @@ -896,9 +904,10 @@ random_seed_i8 (GFC_INTEGER_8 *size, gfc_array_i8 *put, gfc_array_i8 *get) /* This code now should do correct strides. */ for (size_t i = 0; i < SZ; i++) - memcpy (&master_state[i], &(put->base_addr[i * GFC_DESCRIPTOR_STRIDE(put,0)]), + memcpy (&seed[i], &(put->base_addr[i * GFC_DESCRIPTOR_STRIDE(put,0)]), sizeof (GFC_UINTEGER_8)); + scramble_seed (master_state, seed); njumps = 0; master_init = true; init_rand_state (rs, true); -- 2.30.2