cpython: 86d0d74bc2e1 (original) (raw)
--- a/Python/random.c +++ b/Python/random.c @@ -77,7 +77,7 @@ win32_urandom(unsigned char buffer, Py_ } / Issue #25003: Don't use getentropy() on Solaris (available since
#elif defined(HAVE_GETENTROPY) && !defined(sun) #define PY_GETENTROPY 1 @@ -119,25 +119,32 @@ py_getentropy(char buffer, Py_ssize_t s #if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL) #define PY_GETRANDOM 1 +/ Call getrandom()
getrandom() failed with EINTR and the Python signal handler raised an[](#l1.20)
exception, or getrandom() failed with a different error. */[](#l1.21)
static int py_getrandom(void buffer, Py_ssize_t size, int raise) { / Is getrandom() supported by the running kernel?
* Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */[](#l1.26)
static int getrandom_works = 1; /* getrandom() on Linux will block if called before the kernel hasNeed Linux kernel 3.17 or newer, or Solaris 11.3 or newer */[](#l1.27)
* initialized the urandom entropy pool. This will cause Python[](#l1.31)
* to hang on startup if called very early in the boot process -[](#l1.32)
* see https://bugs.python.org/issue26839. To avoid this, use the[](#l1.33)
* GRND_NONBLOCK flag. */[](#l1.34)
initialized the urandom entropy pool. This will cause Python[](#l1.35)
to hang on startup if called very early in the boot process -[](#l1.36)
see https://bugs.python.org/issue26839. To avoid this, use the[](#l1.37)
const int flags = GRND_NONBLOCK; char *dest; long n;GRND_NONBLOCK flag. */[](#l1.38)
dest = buffer; while (0 < size) { @@ -161,8 +168,8 @@ py_getrandom(void buffer, Py_ssize_t si } #else / On Linux, use the syscall() function because the GNU libc doesn't
* expose the Linux getrandom() syscall yet. See:[](#l1.55)
* https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */[](#l1.56)
expose the Linux getrandom() syscall yet. See:[](#l1.57)
https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */[](#l1.58) if (raise) {[](#l1.59) Py_BEGIN_ALLOW_THREADS[](#l1.60) n = syscall(SYS_getrandom, dest, n, flags);[](#l1.61)
@@ -180,12 +187,12 @@ py_getrandom(void buffer, Py_ssize_t si } if (errno == EAGAIN) { / If we failed with EAGAIN, the entropy pool was
* uninitialized. In this case, we return failure to fall[](#l1.66)
* back to reading from /dev/urandom.[](#l1.67)
*[](#l1.68)
* Note: In this case the data read will not be random so[](#l1.69)
* should not be used for cryptographic purposes. Retaining[](#l1.70)
* the existing semantics for practical purposes. */[](#l1.71)
uninitialized. In this case, we return failure to fall[](#l1.72)
back to reading from /dev/urandom.[](#l1.73)
Note: In this case the data read will not be random so[](#l1.75)
should not be used for cryptographic purposes. Retaining[](#l1.76)
the existing semantics for practical purposes. */[](#l1.77) getrandom_works = 0;[](#l1.78) return 0;[](#l1.79) }[](#l1.80)
@@ -221,130 +228,117 @@ static struct { } urandom_cache = { -1 }; -/* Read size bytes from /dev/urandom into buffer.
- Return 0 success, or return -1 on error. / +/ Read 'size' random bytes from getrandom(). Fall back on reading from
- /dev/urandom if getrandom() is not available. +
- Return 0 on success. Raise an exception (if raise is non-zero) and return -1
- on error. */ static int -dev_urandom_noraise(char *buffer, Py_ssize_t size) +dev_urandom(char *buffer, Py_ssize_t size, int raise) { int fd; Py_ssize_t n; -
- if (py_getrandom(buffer, size, 0) == 1) {
return 0;[](#l1.103)
- }
- /* getrandom() is not supported by the running kernel, fall back
* on reading /dev/urandom */[](#l1.106)
- while (0 < size)
- {
do {[](#l1.116)
n = read(fd, buffer, (size_t)size);[](#l1.117)
} while (n < 0 && errno == EINTR);[](#l1.118)
if (n <= 0) {[](#l1.120)
/* stop on error or if read(size) returned 0 */[](#l1.121)
return -1;[](#l1.122)
}[](#l1.123)
-} - -/* Read size bytes from /dev/urandom into buffer.
- Return 0 on success, raise an exception and return -1 on error. */ -static int -dev_urandom_python(char *buffer, Py_ssize_t size) -{
- int fd;
- Py_ssize_t n;
- struct _Py_stat_struct st;
#ifdef PY_GETRANDOM int res; #endif
* on reading /dev/urandom */[](#l1.161)
on reading /dev/urandom */[](#l1.162)
- if (urandom_cache.fd >= 0) {
/* Does the fd point to the same thing as before? (issue #21207) */[](#l1.166)
if (_Py_fstat_noraise(urandom_cache.fd, &st)[](#l1.167)
|| st.st_dev != urandom_cache.st_dev[](#l1.168)
|| st.st_ino != urandom_cache.st_ino) {[](#l1.169)
/* Something changed: forget the cached fd (but don't close it,[](#l1.170)
since it probably points to something important for some[](#l1.171)
third-party code). */[](#l1.172)
urandom_cache.fd = -1;[](#l1.173)
if (urandom_cache.fd >= 0) {[](#l1.178)
/* Does the fd point to the same thing as before? (issue #21207) */[](#l1.179)
if (_Py_fstat_noraise(urandom_cache.fd, &st)[](#l1.180)
|| st.st_dev != urandom_cache.st_dev[](#l1.181)
|| st.st_ino != urandom_cache.st_ino) {[](#l1.182)
/* Something changed: forget the cached fd (but don't close it,[](#l1.183)
since it probably points to something important for some[](#l1.184)
third-party code). */[](#l1.185)
urandom_cache.fd = -1;[](#l1.186)
}[](#l1.187) }[](#l1.188)
- }
- if (urandom_cache.fd >= 0)
fd = urandom_cache.fd;[](#l1.191)
- else {
fd = _Py_open("/dev/urandom", O_RDONLY);[](#l1.193)
if (fd < 0) {[](#l1.194)
if (errno == ENOENT || errno == ENXIO ||[](#l1.195)
errno == ENODEV || errno == EACCES)[](#l1.196)
PyErr_SetString(PyExc_NotImplementedError,[](#l1.197)
"/dev/urandom (or equivalent) not found");[](#l1.198)
/* otherwise, keep the OSError exception raised by _Py_open() */[](#l1.199)
return -1;[](#l1.200)
}[](#l1.201)
if (urandom_cache.fd >= 0) {[](#l1.202)
/* urandom_fd was initialized by another thread while we were[](#l1.203)
not holding the GIL, keep it. */[](#l1.204)
close(fd);[](#l1.205)
if (urandom_cache.fd >= 0)[](#l1.206) fd = urandom_cache.fd;[](#l1.207)
}[](#l1.208) else {[](#l1.209)
if (_Py_fstat(fd, &st)) {[](#l1.210)
close(fd);[](#l1.211)
fd = _Py_open("/dev/urandom", O_RDONLY);[](#l1.212)
if (fd < 0) {[](#l1.213)
if (errno == ENOENT || errno == ENXIO ||[](#l1.214)
errno == ENODEV || errno == EACCES)[](#l1.215)
PyErr_SetString(PyExc_NotImplementedError,[](#l1.216)
"/dev/urandom (or equivalent) not found");[](#l1.217)
/* otherwise, keep the OSError exception raised by _Py_open() */[](#l1.218) return -1;[](#l1.219) }[](#l1.220)
if (urandom_cache.fd >= 0) {[](#l1.221)
/* urandom_fd was initialized by another thread while we were[](#l1.222)
not holding the GIL, keep it. */[](#l1.223)
close(fd);[](#l1.224)
fd = urandom_cache.fd;[](#l1.225)
}[](#l1.226) else {[](#l1.227)
urandom_cache.fd = fd;[](#l1.228)
urandom_cache.st_dev = st.st_dev;[](#l1.229)
urandom_cache.st_ino = st.st_ino;[](#l1.230)
if (_Py_fstat(fd, &st)) {[](#l1.231)
close(fd);[](#l1.232)
return -1;[](#l1.233)
}[](#l1.234)
else {[](#l1.235)
urandom_cache.fd = fd;[](#l1.236)
urandom_cache.st_dev = st.st_dev;[](#l1.237)
urandom_cache.st_ino = st.st_ino;[](#l1.238)
}[](#l1.239) }[](#l1.240) }[](#l1.241)
- do {
n = _Py_read(fd, buffer, (size_t)size);[](#l1.245)
if (n == -1)[](#l1.246)
return -1;[](#l1.247)
if (n == 0) {[](#l1.248)
PyErr_Format(PyExc_RuntimeError,[](#l1.249)
"Failed to read %zi bytes from /dev/urandom",[](#l1.250)
size);[](#l1.251)
do {[](#l1.252)
n = _Py_read(fd, buffer, (size_t)size);[](#l1.253)
if (n == -1)[](#l1.254)
return -1;[](#l1.255)
if (n == 0) {[](#l1.256)
PyErr_Format(PyExc_RuntimeError,[](#l1.257)
"Failed to read %zi bytes from /dev/urandom",[](#l1.258)
size);[](#l1.259)
return -1;[](#l1.260)
}[](#l1.261)
buffer += n;[](#l1.263)
size -= n;[](#l1.264)
} while (0 < size);[](#l1.265)
- }
- else {
fd = _Py_open_noraise("/dev/urandom", O_RDONLY);[](#l1.268)
if (fd < 0) {[](#l1.269) return -1;[](#l1.270) }[](#l1.271)
while (0 < size)[](#l1.276)
{[](#l1.277)
do {[](#l1.278)
n = read(fd, buffer, (size_t)size);[](#l1.279)
} while (n < 0 && errno == EINTR);[](#l1.280)
if (n <= 0) {[](#l1.282)
/* stop on error or if read(size) returned 0 */[](#l1.283)
return -1;[](#l1.284)
}[](#l1.285)
@@ -381,10 +375,10 @@ lcg_urandom(unsigned int x0, unsigned ch } /* If raise is zero:
syscall)[](#l1.305)
#elif defined(PY_GETENTROPY) return py_getentropy(buffer, size, raise); #else
- if (raise) {
return dev_urandom_python(buffer, size);[](#l1.315)
- }
- else {
return dev_urandom_noraise(buffer, size);[](#l1.318)
- }
#endif } @@ -466,7 +455,7 @@ void int res; /* _PyRandom_Init() is called very early in the Python initialization
* and so exceptions cannot be used. */[](#l1.328)
and so exceptions cannot be used (use raise=0). */[](#l1.329) res = pyurandom(secret, secret_size, 0);[](#l1.330) if (res < 0) {[](#l1.331) Py_FatalError("failed to get random numbers to initialize Python");[](#l1.332)