/home/mdboom/Work/builds/cpython/Python/bootstrap_hash.c
Line | Count | Source (jump to first uncovered line) |
1 | #include "Python.h" |
2 | #include "pycore_initconfig.h" |
3 | #include "pycore_fileutils.h" // _Py_fstat_noraise() |
4 | |
5 | #ifdef MS_WINDOWS |
6 | # include <windows.h> |
7 | # include <bcrypt.h> |
8 | #else |
9 | # include <fcntl.h> |
10 | # ifdef HAVE_SYS_STAT_H |
11 | # include <sys/stat.h> |
12 | # endif |
13 | # ifdef HAVE_LINUX_RANDOM_H |
14 | # include <linux/random.h> |
15 | # endif |
16 | # if defined(HAVE_SYS_RANDOM_H) && (defined(HAVE_GETRANDOM) || defined(HAVE_GETENTROPY)) |
17 | # include <sys/random.h> |
18 | # endif |
19 | # if !defined(HAVE_GETRANDOM) && defined(HAVE_GETRANDOM_SYSCALL) |
20 | # include <sys/syscall.h> |
21 | # endif |
22 | #endif |
23 | |
24 | #ifdef _Py_MEMORY_SANITIZER |
25 | # include <sanitizer/msan_interface.h> |
26 | #endif |
27 | |
28 | #if defined(__APPLE__) && defined(__has_builtin) |
29 | # if __has_builtin(__builtin_available) |
30 | # define HAVE_GETENTRYPY_GETRANDOM_RUNTIME __builtin_available(macOS 10.12, iOS 10.10, tvOS 10.0, watchOS 3.0, *) |
31 | # endif |
32 | #endif |
33 | #ifndef HAVE_GETENTRYPY_GETRANDOM_RUNTIME |
34 | # define HAVE_GETENTRYPY_GETRANDOM_RUNTIME 1 |
35 | #endif |
36 | |
37 | |
38 | #ifdef Py_DEBUG |
39 | int _Py_HashSecret_Initialized = 0; |
40 | #else |
41 | static int _Py_HashSecret_Initialized = 0; |
42 | #endif |
43 | |
44 | #ifdef MS_WINDOWS |
45 | |
46 | /* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen |
47 | API. Return 0 on success, or raise an exception and return -1 on error. */ |
48 | static int |
49 | win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise) |
50 | { |
51 | while (size > 0) |
52 | { |
53 | DWORD chunk = (DWORD)Py_MIN(size, PY_DWORD_MAX); |
54 | NTSTATUS status = BCryptGenRandom(NULL, buffer, chunk, BCRYPT_USE_SYSTEM_PREFERRED_RNG); |
55 | if (!BCRYPT_SUCCESS(status)) { |
56 | /* BCryptGenRandom() failed */ |
57 | if (raise) { |
58 | PyErr_SetFromWindowsErr(0); |
59 | } |
60 | return -1; |
61 | } |
62 | buffer += chunk; |
63 | size -= chunk; |
64 | } |
65 | return 0; |
66 | } |
67 | |
68 | #else /* !MS_WINDOWS */ |
69 | |
70 | #if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL) |
71 | #define PY_GETRANDOM 1 |
72 | |
73 | /* Call getrandom() to get random bytes: |
74 | |
75 | - Return 1 on success |
76 | - Return 0 if getrandom() is not available (failed with ENOSYS or EPERM), |
77 | or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom not |
78 | initialized yet) and raise=0. |
79 | - Raise an exception (if raise is non-zero) and return -1 on error: |
80 | if getrandom() failed with EINTR, raise is non-zero and the Python signal |
81 | handler raised an exception, or if getrandom() failed with a different |
82 | error. |
83 | |
84 | getrandom() is retried if it failed with EINTR: interrupted by a signal. */ |
85 | static int |
86 | py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise) |
87 | { |
88 | /* Is getrandom() supported by the running kernel? Set to 0 if getrandom() |
89 | failed with ENOSYS or EPERM. Need Linux kernel 3.17 or newer, or Solaris |
90 | 11.3 or newer */ |
91 | static int getrandom_works = 1; |
92 | int flags; |
93 | char *dest; |
94 | long n; |
95 | |
96 | if (!getrandom_works) { Branch (96:9): [True: 0, False: 166k]
|
97 | return 0; |
98 | } |
99 | |
100 | flags = blocking ? 0166k : GRND_NONBLOCK; Branch (100:13): [True: 166k, False: 143]
|
101 | dest = buffer; |
102 | while (0 < size) { Branch (102:12): [True: 166k, False: 166k]
|
103 | #if defined(__sun) && defined(__SVR4) |
104 | /* Issue #26735: On Solaris, getrandom() is limited to returning up |
105 | to 1024 bytes. Call it multiple times if more bytes are |
106 | requested. */ |
107 | n = Py_MIN(size, 1024); |
108 | #else |
109 | n = Py_MIN(size, LONG_MAX); |
110 | #endif |
111 | |
112 | errno = 0; |
113 | #ifdef HAVE_GETRANDOM |
114 | if (raise) { Branch (114:13): [True: 166k, False: 70]
|
115 | Py_BEGIN_ALLOW_THREADS |
116 | n = getrandom(dest, n, flags); |
117 | Py_END_ALLOW_THREADS |
118 | } |
119 | else { |
120 | n = getrandom(dest, n, flags); |
121 | } |
122 | #else |
123 | /* On Linux, use the syscall() function because the GNU libc doesn't |
124 | expose the Linux getrandom() syscall yet. See: |
125 | https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */ |
126 | if (raise) { |
127 | Py_BEGIN_ALLOW_THREADS |
128 | n = syscall(SYS_getrandom, dest, n, flags); |
129 | Py_END_ALLOW_THREADS |
130 | } |
131 | else { |
132 | n = syscall(SYS_getrandom, dest, n, flags); |
133 | } |
134 | # ifdef _Py_MEMORY_SANITIZER |
135 | if (n > 0) { |
136 | __msan_unpoison(dest, n); |
137 | } |
138 | # endif |
139 | #endif |
140 | |
141 | if (n < 0) { Branch (141:13): [True: 0, False: 166k]
|
142 | /* ENOSYS: the syscall is not supported by the kernel. |
143 | EPERM: the syscall is blocked by a security policy (ex: SECCOMP) |
144 | or something else. */ |
145 | if (errno == ENOSYS || errno == EPERM) { Branch (145:17): [True: 0, False: 0]
Branch (145:36): [True: 0, False: 0]
|
146 | getrandom_works = 0; |
147 | return 0; |
148 | } |
149 | |
150 | /* getrandom(GRND_NONBLOCK) fails with EAGAIN if the system urandom |
151 | is not initialized yet. For _PyRandom_Init(), we ignore the |
152 | error and fall back on reading /dev/urandom which never blocks, |
153 | even if the system urandom is not initialized yet: |
154 | see the PEP 524. */ |
155 | if (errno == EAGAIN && !raise && !blocking) { Branch (155:17): [True: 0, False: 0]
Branch (155:36): [True: 0, False: 0]
Branch (155:46): [True: 0, False: 0]
|
156 | return 0; |
157 | } |
158 | |
159 | if (errno == EINTR) { Branch (159:17): [True: 0, False: 0]
|
160 | if (raise) { Branch (160:21): [True: 0, False: 0]
|
161 | if (PyErr_CheckSignals()) { Branch (161:25): [True: 0, False: 0]
|
162 | return -1; |
163 | } |
164 | } |
165 | |
166 | /* retry getrandom() if it was interrupted by a signal */ |
167 | continue; |
168 | } |
169 | |
170 | if (raise) { Branch (170:17): [True: 0, False: 0]
|
171 | PyErr_SetFromErrno(PyExc_OSError); |
172 | } |
173 | return -1; |
174 | } |
175 | |
176 | dest += n; |
177 | size -= n; |
178 | } |
179 | return 1; |
180 | } |
181 | |
182 | #elif defined(HAVE_GETENTROPY) |
183 | #define PY_GETENTROPY 1 |
184 | |
185 | /* Fill buffer with size pseudo-random bytes generated by getentropy(): |
186 | |
187 | - Return 1 on success |
188 | - Return 0 if getentropy() syscall is not available (failed with ENOSYS or |
189 | EPERM). |
190 | - Raise an exception (if raise is non-zero) and return -1 on error: |
191 | if getentropy() failed with EINTR, raise is non-zero and the Python signal |
192 | handler raised an exception, or if getentropy() failed with a different |
193 | error. |
194 | |
195 | getentropy() is retried if it failed with EINTR: interrupted by a signal. */ |
196 | |
197 | #if defined(__APPLE__) && defined(__has_attribute) && __has_attribute(availability) |
198 | static int |
199 | py_getentropy(char *buffer, Py_ssize_t size, int raise) |
200 | __attribute__((availability(macos,introduced=10.12))) |
201 | __attribute__((availability(ios,introduced=10.0))) |
202 | __attribute__((availability(tvos,introduced=10.0))) |
203 | __attribute__((availability(watchos,introduced=3.0))); |
204 | #endif |
205 | |
206 | static int |
207 | py_getentropy(char *buffer, Py_ssize_t size, int raise) |
208 | { |
209 | /* Is getentropy() supported by the running kernel? Set to 0 if |
210 | getentropy() failed with ENOSYS or EPERM. */ |
211 | static int getentropy_works = 1; |
212 | |
213 | if (!getentropy_works) { |
214 | return 0; |
215 | } |
216 | |
217 | while (size > 0) { |
218 | /* getentropy() is limited to returning up to 256 bytes. Call it |
219 | multiple times if more bytes are requested. */ |
220 | Py_ssize_t len = Py_MIN(size, 256); |
221 | int res; |
222 | |
223 | if (raise) { |
224 | Py_BEGIN_ALLOW_THREADS |
225 | res = getentropy(buffer, len); |
226 | Py_END_ALLOW_THREADS |
227 | } |
228 | else { |
229 | res = getentropy(buffer, len); |
230 | } |
231 | |
232 | if (res < 0) { |
233 | /* ENOSYS: the syscall is not supported by the running kernel. |
234 | EPERM: the syscall is blocked by a security policy (ex: SECCOMP) |
235 | or something else. */ |
236 | if (errno == ENOSYS || errno == EPERM) { |
237 | getentropy_works = 0; |
238 | return 0; |
239 | } |
240 | |
241 | if (errno == EINTR) { |
242 | if (raise) { |
243 | if (PyErr_CheckSignals()) { |
244 | return -1; |
245 | } |
246 | } |
247 | |
248 | /* retry getentropy() if it was interrupted by a signal */ |
249 | continue; |
250 | } |
251 | |
252 | if (raise) { |
253 | PyErr_SetFromErrno(PyExc_OSError); |
254 | } |
255 | return -1; |
256 | } |
257 | |
258 | buffer += len; |
259 | size -= len; |
260 | } |
261 | return 1; |
262 | } |
263 | #endif /* defined(HAVE_GETENTROPY) && !(defined(__sun) && defined(__SVR4)) */ |
264 | |
265 | |
266 | static struct { |
267 | int fd; |
268 | dev_t st_dev; |
269 | ino_t st_ino; |
270 | } urandom_cache = { -1 }; |
271 | |
272 | /* Read random bytes from the /dev/urandom device: |
273 | |
274 | - Return 0 on success |
275 | - Raise an exception (if raise is non-zero) and return -1 on error |
276 | |
277 | Possible causes of errors: |
278 | |
279 | - open() failed with ENOENT, ENXIO, ENODEV, EACCES: the /dev/urandom device |
280 | was not found. For example, it was removed manually or not exposed in a |
281 | chroot or container. |
282 | - open() failed with a different error |
283 | - fstat() failed |
284 | - read() failed or returned 0 |
285 | |
286 | read() is retried if it failed with EINTR: interrupted by a signal. |
287 | |
288 | The file descriptor of the device is kept open between calls to avoid using |
289 | many file descriptors when run in parallel from multiple threads: |
290 | see the issue #18756. |
291 | |
292 | st_dev and st_ino fields of the file descriptor (from fstat()) are cached to |
293 | check if the file descriptor was replaced by a different file (which is |
294 | likely a bug in the application): see the issue #21207. |
295 | |
296 | If the file descriptor was closed or replaced, open a new file descriptor |
297 | but don't close the old file descriptor: it probably points to something |
298 | important for some third-party code. */ |
299 | static int |
300 | dev_urandom(char *buffer, Py_ssize_t size, int raise) |
301 | { |
302 | int fd; |
303 | Py_ssize_t n; |
304 |
|
305 | if (raise) { Branch (305:9): [True: 0, False: 0]
|
306 | struct _Py_stat_struct st; |
307 | int fstat_result; |
308 |
|
309 | if (urandom_cache.fd >= 0) { Branch (309:13): [True: 0, False: 0]
|
310 | Py_BEGIN_ALLOW_THREADS |
311 | fstat_result = _Py_fstat_noraise(urandom_cache.fd, &st); |
312 | Py_END_ALLOW_THREADS |
313 | |
314 | /* Does the fd point to the same thing as before? (issue #21207) */ |
315 | if (fstat_result Branch (315:17): [True: 0, False: 0]
|
316 | || st.st_dev != urandom_cache.st_dev Branch (316:20): [True: 0, False: 0]
|
317 | || st.st_ino != urandom_cache.st_ino) { Branch (317:20): [True: 0, False: 0]
|
318 | /* Something changed: forget the cached fd (but don't close it, |
319 | since it probably points to something important for some |
320 | third-party code). */ |
321 | urandom_cache.fd = -1; |
322 | } |
323 | } |
324 | if (urandom_cache.fd >= 0) Branch (324:13): [True: 0, False: 0]
|
325 | fd = urandom_cache.fd; |
326 | else { |
327 | fd = _Py_open("/dev/urandom", O_RDONLY); |
328 | if (fd < 0) { Branch (328:17): [True: 0, False: 0]
|
329 | if (errno == ENOENT || errno == ENXIO || Branch (329:21): [True: 0, False: 0]
Branch (329:40): [True: 0, False: 0]
|
330 | errno == ENODEV || errno == EACCES) { Branch (330:21): [True: 0, False: 0]
Branch (330:40): [True: 0, False: 0]
|
331 | PyErr_SetString(PyExc_NotImplementedError, |
332 | "/dev/urandom (or equivalent) not found"); |
333 | } |
334 | /* otherwise, keep the OSError exception raised by _Py_open() */ |
335 | return -1; |
336 | } |
337 | if (urandom_cache.fd >= 0) { Branch (337:17): [True: 0, False: 0]
|
338 | /* urandom_fd was initialized by another thread while we were |
339 | not holding the GIL, keep it. */ |
340 | close(fd); |
341 | fd = urandom_cache.fd; |
342 | } |
343 | else { |
344 | if (_Py_fstat(fd, &st)) { Branch (344:21): [True: 0, False: 0]
|
345 | close(fd); |
346 | return -1; |
347 | } |
348 | else { |
349 | urandom_cache.fd = fd; |
350 | urandom_cache.st_dev = st.st_dev; |
351 | urandom_cache.st_ino = st.st_ino; |
352 | } |
353 | } |
354 | } |
355 | |
356 | do { |
357 | n = _Py_read(fd, buffer, (size_t)size); |
358 | if (n == -1) Branch (358:17): [True: 0, False: 0]
|
359 | return -1; |
360 | if (n == 0) { Branch (360:17): [True: 0, False: 0]
|
361 | PyErr_Format(PyExc_RuntimeError, |
362 | "Failed to read %zi bytes from /dev/urandom", |
363 | size); |
364 | return -1; |
365 | } |
366 | |
367 | buffer += n; |
368 | size -= n; |
369 | } while (0 < size); Branch (369:18): [True: 0, False: 0]
|
370 | } |
371 | else { |
372 | fd = _Py_open_noraise("/dev/urandom", O_RDONLY); |
373 | if (fd < 0) { Branch (373:13): [True: 0, False: 0]
|
374 | return -1; |
375 | } |
376 | |
377 | while (0 < size) Branch (377:16): [True: 0, False: 0]
|
378 | { |
379 | do { |
380 | n = read(fd, buffer, (size_t)size); |
381 | } while (n < 0 && errno == EINTR); Branch (381:22): [True: 0, False: 0]
Branch (381:31): [True: 0, False: 0]
|
382 |
|
383 | if (n <= 0) { Branch (383:17): [True: 0, False: 0]
|
384 | /* stop on error or if read(size) returned 0 */ |
385 | close(fd); |
386 | return -1; |
387 | } |
388 | |
389 | buffer += n; |
390 | size -= n; |
391 | } |
392 | close(fd); |
393 | } |
394 | return 0; |
395 | } |
396 | |
397 | static void |
398 | dev_urandom_close(void) |
399 | { |
400 | if (urandom_cache.fd >= 0) { Branch (400:9): [True: 0, False: 103]
|
401 | close(urandom_cache.fd); |
402 | urandom_cache.fd = -1; |
403 | } |
404 | } |
405 | #endif /* !MS_WINDOWS */ |
406 | |
407 | |
408 | /* Fill buffer with pseudo-random bytes generated by a linear congruent |
409 | generator (LCG): |
410 | |
411 | x(n+1) = (x(n) * 214013 + 2531011) % 2^32 |
412 | |
413 | Use bits 23..16 of x(n) to generate a byte. */ |
414 | static void |
415 | lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size) |
416 | { |
417 | size_t index; |
418 | unsigned int x; |
419 | |
420 | x = x0; |
421 | for (index=0; index < size; index++72 ) { Branch (421:19): [True: 72, False: 3]
|
422 | x *= 214013; |
423 | x += 2531011; |
424 | /* modulo 2 ^ (8 * sizeof(int)) */ |
425 | buffer[index] = (x >> 16) & 0xff; |
426 | } |
427 | } |
428 | |
429 | /* Read random bytes: |
430 | |
431 | - Return 0 on success |
432 | - Raise an exception (if raise is non-zero) and return -1 on error |
433 | |
434 | Used sources of entropy ordered by preference, preferred source first: |
435 | |
436 | - BCryptGenRandom() on Windows |
437 | - getrandom() function (ex: Linux and Solaris): call py_getrandom() |
438 | - getentropy() function (ex: OpenBSD): call py_getentropy() |
439 | - /dev/urandom device |
440 | |
441 | Read from the /dev/urandom device if getrandom() or getentropy() function |
442 | is not available or does not work. |
443 | |
444 | Prefer getrandom() over getentropy() because getrandom() supports blocking |
445 | and non-blocking mode: see the PEP 524. Python requires non-blocking RNG at |
446 | startup to initialize its hash secret, but os.urandom() must block until the |
447 | system urandom is initialized (at least on Linux 3.17 and newer). |
448 | |
449 | Prefer getrandom() and getentropy() over reading directly /dev/urandom |
450 | because these functions don't need file descriptors and so avoid ENFILE or |
451 | EMFILE errors (too many open files): see the issue #18756. |
452 | |
453 | Only the getrandom() function supports non-blocking mode. |
454 | |
455 | Only use RNG running in the kernel. They are more secure because it is |
456 | harder to get the internal state of a RNG running in the kernel land than a |
457 | RNG running in the user land. The kernel has a direct access to the hardware |
458 | and has access to hardware RNG, they are used as entropy sources. |
459 | |
460 | Note: the OpenSSL RAND_pseudo_bytes() function does not automatically reseed |
461 | its RNG on fork(), two child processes (with the same pid) generate the same |
462 | random numbers: see issue #18747. Kernel RNGs don't have this issue, |
463 | they have access to good quality entropy sources. |
464 | |
465 | If raise is zero: |
466 | |
467 | - Don't raise an exception on error |
468 | - Don't call the Python signal handler (don't call PyErr_CheckSignals()) if |
469 | a function fails with EINTR: retry directly the interrupted function |
470 | - Don't release the GIL to call functions. |
471 | */ |
472 | static int |
473 | pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise) |
474 | { |
475 | #if defined(PY_GETRANDOM) || defined(PY_GETENTROPY) |
476 | int res; |
477 | #endif |
478 | |
479 | if (size < 0) { Branch (479:9): [True: 0, False: 166k]
|
480 | if (raise) { Branch (480:13): [True: 0, False: 0]
|
481 | PyErr_Format(PyExc_ValueError, |
482 | "negative argument not allowed"); |
483 | } |
484 | return -1; |
485 | } |
486 | |
487 | if (size == 0) { Branch (487:9): [True: 3, False: 166k]
|
488 | return 0; |
489 | } |
490 | |
491 | #ifdef MS_WINDOWS |
492 | return win32_urandom((unsigned char *)buffer, size, raise); |
493 | #else |
494 | |
495 | #if defined(PY_GETRANDOM) || defined(PY_GETENTROPY) |
496 | if (HAVE_GETENTRYPY_GETRANDOM_RUNTIME) { |
497 | #ifdef PY_GETRANDOM |
498 | res = py_getrandom(buffer, size, blocking, raise); |
499 | #else |
500 | res = py_getentropy(buffer, size, raise); |
501 | #endif |
502 | if (res < 0) { Branch (502:13): [True: 0, False: 166k]
|
503 | return -1; |
504 | } |
505 | if (res == 1) { Branch (505:13): [True: 166k, False: 0]
|
506 | return 0; |
507 | } |
508 | /* getrandom() or getentropy() function is not available: failed with |
509 | ENOSYS or EPERM. Fall back on reading from /dev/urandom. */ |
510 | } /* end of availability block */ |
511 | #endif |
512 | |
513 | return dev_urandom(buffer, size, raise); |
514 | #endif |
515 | } |
516 | |
517 | /* Fill buffer with size pseudo-random bytes from the operating system random |
518 | number generator (RNG). It is suitable for most cryptographic purposes |
519 | except long living private keys for asymmetric encryption. |
520 | |
521 | On Linux 3.17 and newer, the getrandom() syscall is used in blocking mode: |
522 | block until the system urandom entropy pool is initialized (128 bits are |
523 | collected by the kernel). |
524 | |
525 | Return 0 on success. Raise an exception and return -1 on error. */ |
526 | int |
527 | _PyOS_URandom(void *buffer, Py_ssize_t size) |
528 | { |
529 | return pyurandom(buffer, size, 1, 1); |
530 | } |
531 | |
532 | /* Fill buffer with size pseudo-random bytes from the operating system random |
533 | number generator (RNG). It is not suitable for cryptographic purpose. |
534 | |
535 | On Linux 3.17 and newer (when getrandom() syscall is used), if the system |
536 | urandom is not initialized yet, the function returns "weak" entropy read |
537 | from /dev/urandom. |
538 | |
539 | Return 0 on success. Raise an exception and return -1 on error. */ |
540 | int |
541 | _PyOS_URandomNonblock(void *buffer, Py_ssize_t size) |
542 | { |
543 | return pyurandom(buffer, size, 0, 1); |
544 | } |
545 | |
546 | |
547 | PyStatus |
548 | _Py_HashRandomization_Init(const PyConfig *config) |
549 | { |
550 | void *secret = &_Py_HashSecret; |
551 | Py_ssize_t secret_size = sizeof(_Py_HashSecret_t); |
552 | |
553 | if (_Py_HashSecret_Initialized) { Branch (553:9): [True: 34, False: 73]
|
554 | return _PyStatus_OK(); |
555 | } |
556 | _Py_HashSecret_Initialized = 1; |
557 | |
558 | if (config->use_hash_seed) { Branch (558:9): [True: 3, False: 70]
|
559 | if (config->hash_seed == 0) { Branch (559:13): [True: 0, False: 3]
|
560 | /* disable the randomized hash */ |
561 | memset(secret, 0, secret_size); |
562 | } |
563 | else { |
564 | /* use the specified hash seed */ |
565 | lcg_urandom(config->hash_seed, secret, secret_size); |
566 | } |
567 | } |
568 | else { |
569 | /* use a random hash seed */ |
570 | int res; |
571 | |
572 | /* _PyRandom_Init() is called very early in the Python initialization |
573 | and so exceptions cannot be used (use raise=0). |
574 | |
575 | _PyRandom_Init() must not block Python initialization: call |
576 | pyurandom() is non-blocking mode (blocking=0): see the PEP 524. */ |
577 | res = pyurandom(secret, secret_size, 0, 0); |
578 | if (res < 0) { Branch (578:13): [True: 0, False: 70]
|
579 | return _PyStatus_ERR("failed to get random numbers " |
580 | "to initialize Python"); |
581 | } |
582 | } |
583 | return _PyStatus_OK(); |
584 | } |
585 | |
586 | |
587 | void |
588 | _Py_HashRandomization_Fini(void) |
589 | { |
590 | #ifndef MS_WINDOWS |
591 | dev_urandom_close(); |
592 | #endif |
593 | } |