@@ -118,10 +118,16 @@ vms_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
118118
119119#if !defined(MS_WINDOWS ) && !defined(__VMS )
120120
121+ static struct {
122+ int fd ;
123+ dev_t st_dev ;
124+ ino_t st_ino ;
125+ } urandom_cache = { -1 };
126+
121127/* Read size bytes from /dev/urandom into buffer.
122128 Call Py_FatalError() on error. */
123129static void
124- dev_urandom_noraise (char * buffer , Py_ssize_t size )
130+ dev_urandom_noraise (unsigned char * buffer , Py_ssize_t size )
125131{
126132 int fd ;
127133 Py_ssize_t n ;
@@ -156,22 +162,56 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
156162{
157163 int fd ;
158164 Py_ssize_t n ;
165+ struct stat st ;
159166
160167 if (size <= 0 )
161168 return 0 ;
162169
163- Py_BEGIN_ALLOW_THREADS
164- fd = open ("/dev/urandom" , O_RDONLY );
165- Py_END_ALLOW_THREADS
166- if (fd < 0 )
167- {
168- if (errno == ENOENT || errno == ENXIO ||
169- errno == ENODEV || errno == EACCES )
170- PyErr_SetString (PyExc_NotImplementedError ,
171- "/dev/urandom (or equivalent) not found" );
172- else
173- PyErr_SetFromErrno (PyExc_OSError );
174- return -1 ;
170+ if (urandom_cache .fd >= 0 ) {
171+ /* Does the fd point to the same thing as before? (issue #21207) */
172+ if (fstat (urandom_cache .fd , & st )
173+ || st .st_dev != urandom_cache .st_dev
174+ || st .st_ino != urandom_cache .st_ino ) {
175+ /* Something changed: forget the cached fd (but don't close it,
176+ since it probably points to something important for some
177+ third-party code). */
178+ urandom_cache .fd = -1 ;
179+ }
180+ }
181+ if (urandom_cache .fd >= 0 )
182+ fd = urandom_cache .fd ;
183+ else {
184+ Py_BEGIN_ALLOW_THREADS
185+ fd = open ("/dev/urandom" , O_RDONLY );
186+ Py_END_ALLOW_THREADS
187+ if (fd < 0 )
188+ {
189+ if (errno == ENOENT || errno == ENXIO ||
190+ errno == ENODEV || errno == EACCES )
191+ PyErr_SetString (PyExc_NotImplementedError ,
192+ "/dev/urandom (or equivalent) not found" );
193+ else
194+ PyErr_SetFromErrno (PyExc_OSError );
195+ return -1 ;
196+ }
197+ if (urandom_cache .fd >= 0 ) {
198+ /* urandom_fd was initialized by another thread while we were
199+ not holding the GIL, keep it. */
200+ close (fd );
201+ fd = urandom_cache .fd ;
202+ }
203+ else {
204+ if (fstat (fd , & st )) {
205+ PyErr_SetFromErrno (PyExc_OSError );
206+ close (fd );
207+ return -1 ;
208+ }
209+ else {
210+ urandom_cache .fd = fd ;
211+ urandom_cache .st_dev = st .st_dev ;
212+ urandom_cache .st_ino = st .st_ino ;
213+ }
214+ }
175215 }
176216
177217 Py_BEGIN_ALLOW_THREADS
@@ -195,12 +235,21 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
195235 PyErr_Format (PyExc_RuntimeError ,
196236 "Failed to read %zi bytes from /dev/urandom" ,
197237 size );
198- close (fd );
199238 return -1 ;
200239 }
201- close (fd );
202240 return 0 ;
203241}
242+
243+ static void
244+ dev_urandom_close (void )
245+ {
246+ if (urandom_cache .fd >= 0 ) {
247+ close (urandom_cache .fd );
248+ urandom_cache .fd = -1 ;
249+ }
250+ }
251+
252+
204253#endif /* !defined(MS_WINDOWS) && !defined(__VMS) */
205254
206255/* Fill buffer with pseudo-random bytes generated by a linear congruent
@@ -305,8 +354,21 @@ _PyRandom_Init(void)
305354# ifdef __VMS
306355 vms_urandom ((unsigned char * )secret , secret_size , 0 );
307356# else
308- dev_urandom_noraise ((char * )secret , secret_size );
357+ dev_urandom_noraise ((unsigned char * )secret , secret_size );
309358# endif
310359#endif
311360 }
312361}
362+
363+ void
364+ _PyRandom_Fini (void )
365+ {
366+ #ifdef MS_WINDOWS
367+ if (hCryptProv ) {
368+ CryptReleaseContext (hCryptProv , 0 );
369+ hCryptProv = 0 ;
370+ }
371+ #else
372+ dev_urandom_close ();
373+ #endif
374+ }
0 commit comments