1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
|
/* random.c -- Functions for managing 16-bit and 32-bit random numbers. */
/* Copyright (C) 2020 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "bashtypes.h"
#if defined (HAVE_SYS_RANDOM_H)
# include <sys/random.h>
#endif
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "filecntl.h"
#include <stdio.h>
#include "bashansi.h"
#include "shell.h"
extern time_t shell_start_time;
extern int last_random_value;
static u_bits32_t intrand32 PARAMS((u_bits32_t));
static u_bits32_t genseed PARAMS((void));
static u_bits32_t brand32 PARAMS((void));
static void sbrand32 PARAMS((u_bits32_t));
static void perturb_rand32 PARAMS((void));
/* The random number seed. You can change this by setting RANDOM. */
static u_bits32_t rseed = 1;
/* Returns a 32-bit pseudo-random number. */
static u_bits32_t
intrand32 (last)
u_bits32_t last;
{
/* Minimal Standard generator from
"Random number generators: good ones are hard to find",
Park and Miller, Communications of the ACM, vol. 31, no. 10,
October 1988, p. 1195. Filtered through FreeBSD.
x(n+1) = 16807 * x(n) mod (m).
We split up the calculations to avoid overflow.
h = last / q; l = x - h * q; t = a * l - h * r
m = 2147483647, a = 16807, q = 127773, r = 2836
There are lots of other combinations of constants to use; look at
https://www.gnu.org/software/gsl/manual/html_node/Other-random-number-generators.html#Other-random-number-generators */
bits32_t h, l, t;
u_bits32_t ret;
/* Can't seed with 0. */
ret = (last == 0) ? 123459876 : last;
h = ret / 127773;
l = ret - (127773 * h);
t = 16807 * l - 2836 * h;
ret = (t < 0) ? t + 0x7fffffff : t;
return (ret);
}
static u_bits32_t
genseed ()
{
struct timeval tv;
u_bits32_t iv;
gettimeofday (&tv, NULL);
iv = (u_bits32_t)seedrand; /* let the compiler truncate */
iv = tv.tv_sec ^ tv.tv_usec ^ getpid () ^ getppid () ^ current_user.uid ^ iv;
return (iv);
}
#define BASH_RAND_MAX 32767 /* 0x7fff - 16 bits */
/* Returns a pseudo-random number between 0 and 32767. */
int
brand ()
{
unsigned int ret;
rseed = intrand32 (rseed);
if (shell_compatibility_level > 50)
ret = (rseed >> 16) ^ (rseed & 65535);
else
ret = rseed;
return (ret & BASH_RAND_MAX);
}
/* Set the random number generator seed to SEED. */
void
sbrand (seed)
unsigned long seed;
{
rseed = seed;
last_random_value = 0;
}
void
seedrand ()
{
u_bits32_t iv;
iv = genseed ();
sbrand (iv);
}
static u_bits32_t rseed32 = 1073741823;
static int last_rand32;
static int urandfd = -1;
#define BASH_RAND32_MAX 0x7fffffff /* 32 bits */
/* Returns a 32-bit pseudo-random number between 0 and 4294967295. */
static u_bits32_t
brand32 ()
{
u_bits32_t ret;
rseed32 = intrand32 (rseed32);
return (rseed32 & BASH_RAND32_MAX);
}
static void
sbrand32 (seed)
u_bits32_t seed;
{
last_rand32 = rseed32 = seed;
}
void
seedrand32 ()
{
u_bits32_t iv;
iv = genseed ();
sbrand32 (iv);
}
static void
perturb_rand32 ()
{
rseed32 ^= genseed ();
}
/* Force another attempt to open /dev/urandom on the next call to get_urandom32 */
void
urandom_close ()
{
if (urandfd >= 0)
close (urandfd);
urandfd = -1;
}
#if !defined (HAVE_GETRANDOM)
/* Imperfect emulation of getrandom(2). */
#ifndef GRND_NONBLOCK
# define GRND_NONBLOCK 1
# define GRND_RANDOM 2
#endif
static ssize_t
getrandom (buf, len, flags)
void *buf;
size_t len;
unsigned int flags;
{
int oflags;
ssize_t r;
static int urand_unavail = 0;
#if HAVE_GETENTROPY
r = getentropy (buf, len);
return (r == 0) ? len : -1;
#endif
if (urandfd == -1 && urand_unavail == 0)
{
oflags = O_RDONLY;
if (flags & GRND_NONBLOCK)
oflags |= O_NONBLOCK;
urandfd = open ("/dev/urandom", oflags, 0);
if (urandfd >= 0)
SET_CLOSE_ON_EXEC (urandfd);
else
{
urand_unavail = 1;
return -1;
}
}
if (urandfd >= 0 && (r = read (urandfd, buf, len)) == len)
return (r);
return -1;
}
#endif
u_bits32_t
get_urandom32 ()
{
u_bits32_t ret;
if (getrandom ((void *)&ret, sizeof (ret), GRND_NONBLOCK) == sizeof (ret))
return (last_rand32 = ret);
#if defined (HAVE_ARC4RANDOM)
ret = arc4random ();
#else
if (subshell_environment)
perturb_rand32 ();
do
ret = brand32 ();
while (ret == last_rand32);
#endif
return (last_rand32 = ret);
}
|