From 57057a6624e1b12beb61eb2f817f9de95bb7d47a Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 28 Aug 2014 12:30:00 -0400 Subject: [PATCH] PEP 466: backport persistent urandom fd (closes #21305) Patch from Alex Gaynor. --- Include/pythonrun.h | 1 + Misc/NEWS | 3 ++ Python/pythonrun.c | 1 + Python/random.c | 94 +++++++++++++++++++++++++++++++++++++-------- 4 files changed, 83 insertions(+), 16 deletions(-) diff --git a/Include/pythonrun.h b/Include/pythonrun.h index c319c520d47..57ef2bb7155 100644 --- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -145,6 +145,7 @@ PyAPI_FUNC(void) PyInt_Fini(void); PyAPI_FUNC(void) PyFloat_Fini(void); PyAPI_FUNC(void) PyOS_FiniInterrupts(void); PyAPI_FUNC(void) PyByteArray_Fini(void); +PyAPI_FUNC(void) _PyRandom_Fini(void); PyAPI_DATA(PyThreadState *) _Py_Finalizing; diff --git a/Misc/NEWS b/Misc/NEWS index 88af5a406c4..6ca9692cb11 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -19,6 +19,9 @@ Core and Builtins Library ------- +- Issue #21305: os.urandom now caches a fd to /dev/urandom. This is a PEP 466 + backport from Python 3. + - Issue #21307: As part of PEP 466, backport hashlib.algorithms_guaranteed and hashlib.algorithms_available. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 748a63b9559..05bb62b64a2 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -536,6 +536,7 @@ Py_Finalize(void) PyInt_Fini(); PyFloat_Fini(); PyDict_Fini(); + _PyRandom_Fini(); #ifdef Py_USING_UNICODE /* Cleanup Unicode implementation */ diff --git a/Python/random.c b/Python/random.c index d615923dd2f..0776bfcc739 100644 --- a/Python/random.c +++ b/Python/random.c @@ -118,10 +118,16 @@ vms_urandom(unsigned char *buffer, Py_ssize_t size, int raise) #if !defined(MS_WINDOWS) && !defined(__VMS) +static struct { + int fd; + dev_t st_dev; + ino_t st_ino; +} urandom_cache = { -1 }; + /* Read size bytes from /dev/urandom into buffer. Call Py_FatalError() on error. */ static void -dev_urandom_noraise(char *buffer, Py_ssize_t size) +dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) { int fd; Py_ssize_t n; @@ -156,22 +162,56 @@ dev_urandom_python(char *buffer, Py_ssize_t size) { int fd; Py_ssize_t n; + struct stat st; if (size <= 0) return 0; - Py_BEGIN_ALLOW_THREADS - fd = open("/dev/urandom", O_RDONLY); - Py_END_ALLOW_THREADS - if (fd < 0) - { - if (errno == ENOENT || errno == ENXIO || - errno == ENODEV || errno == EACCES) - PyErr_SetString(PyExc_NotImplementedError, - "/dev/urandom (or equivalent) not found"); - else - PyErr_SetFromErrno(PyExc_OSError); - return -1; + if (urandom_cache.fd >= 0) { + /* Does the fd point to the same thing as before? (issue #21207) */ + if (fstat(urandom_cache.fd, &st) + || st.st_dev != urandom_cache.st_dev + || st.st_ino != urandom_cache.st_ino) { + /* Something changed: forget the cached fd (but don't close it, + since it probably points to something important for some + third-party code). */ + urandom_cache.fd = -1; + } + } + if (urandom_cache.fd >= 0) + fd = urandom_cache.fd; + else { + Py_BEGIN_ALLOW_THREADS + fd = open("/dev/urandom", O_RDONLY); + Py_END_ALLOW_THREADS + if (fd < 0) + { + if (errno == ENOENT || errno == ENXIO || + errno == ENODEV || errno == EACCES) + PyErr_SetString(PyExc_NotImplementedError, + "/dev/urandom (or equivalent) not found"); + else + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + if (urandom_cache.fd >= 0) { + /* urandom_fd was initialized by another thread while we were + not holding the GIL, keep it. */ + close(fd); + fd = urandom_cache.fd; + } + else { + if (fstat(fd, &st)) { + PyErr_SetFromErrno(PyExc_OSError); + close(fd); + return -1; + } + else { + urandom_cache.fd = fd; + urandom_cache.st_dev = st.st_dev; + urandom_cache.st_ino = st.st_ino; + } + } } Py_BEGIN_ALLOW_THREADS @@ -195,12 +235,21 @@ dev_urandom_python(char *buffer, Py_ssize_t size) PyErr_Format(PyExc_RuntimeError, "Failed to read %zi bytes from /dev/urandom", size); - close(fd); return -1; } - close(fd); return 0; } + +static void +dev_urandom_close(void) +{ + if (urandom_cache.fd >= 0) { + close(urandom_cache.fd); + urandom_cache.fd = -1; + } +} + + #endif /* !defined(MS_WINDOWS) && !defined(__VMS) */ /* Fill buffer with pseudo-random bytes generated by a linear congruent @@ -305,8 +354,21 @@ _PyRandom_Init(void) # ifdef __VMS vms_urandom((unsigned char *)secret, secret_size, 0); # else - dev_urandom_noraise((char*)secret, secret_size); + dev_urandom_noraise((unsigned char*)secret, secret_size); # endif #endif } } + +void +_PyRandom_Fini(void) +{ +#ifdef MS_WINDOWS + if (hCryptProv) { + CryptReleaseContext(hCryptProv, 0); + hCryptProv = 0; + } +#else + dev_urandom_close(); +#endif +}