# HG changeset patch # User Benjamin Peterson # Date 1409243400 14400 # Node ID 3e7f8855078855a9409bc2c1372de89cb021d6c8 # Parent 3f73c44b1fd1d442d6841493328e9756fb5e7ef5 PEP 466: backport persistent urandom fd (closes #21305) Patch from Alex Gaynor. diff --git a/Python/pythonrun.c b/Python/pythonrun.c --- 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 -up Python-2.7.5/Include/pythonrun.h.urandom Python-2.7.5/Include/pythonrun.h --- Python-2.7.5/Include/pythonrun.h.urandom 2015-03-06 08:16:47.638584015 +0100 +++ Python-2.7.5/Include/pythonrun.h 2015-03-06 08:21:48.009485462 +0100 @@ -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); /* Stuff with no proper home (yet) */ PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *); diff -up Python-2.7.5/Python/random.c.urandom Python-2.7.5/Python/random.c --- Python-2.7.5/Python/random.c.urandom 2015-03-06 08:22:10.244699950 +0100 +++ Python-2.7.5/Python/random.c 2015-03-06 08:24:57.907317272 +0100 @@ -118,10 +118,16 @@ vms_urandom(unsigned char *buffer, Py_ss #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,18 +162,56 @@ dev_urandom_python(char *buffer, Py_ssiz { 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) - { - PyErr_SetString(PyExc_NotImplementedError, - "/dev/urandom (or equivalent) not found"); - 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 @@ -191,12 +235,21 @@ dev_urandom_python(char *buffer, Py_ssiz 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 @@ -300,8 +353,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 +}