diff options
| author | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-01-22 11:17:26 +0000 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-01-23 12:05:21 +0000 |
| commit | a0336965aa2e2215a274ff7d2ebae8c40443b1c5 (patch) | |
| tree | 3972ef50c9ced030e13fd09a1656e4902f278482 /meta/recipes-devtools/python/python3/upstream-random-fixes.patch | |
| parent | 6cea270c544ceb14cbd44f4e5f4e51c3a3dcb143 (diff) | |
| download | poky-a0336965aa2e2215a274ff7d2ebae8c40443b1c5.tar.gz | |
python3: Add upstream random.c fixes for recent glibc
python3 fails to work with recent glibc versions on older hosts, giving
errors like:
Fatal Python error: getentropy() failed
Aborted
This breaks buildtools-tarball and hence eSDK. This patch backports the
changes to random.c from upstream that address the problem.
(From OE-Core rev: 126b2c47b1806b53fbd9a4706bc48bc7c4efd3be)
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/recipes-devtools/python/python3/upstream-random-fixes.patch')
| -rw-r--r-- | meta/recipes-devtools/python/python3/upstream-random-fixes.patch | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/meta/recipes-devtools/python/python3/upstream-random-fixes.patch b/meta/recipes-devtools/python/python3/upstream-random-fixes.patch new file mode 100644 index 0000000000..0d9152ccd7 --- /dev/null +++ b/meta/recipes-devtools/python/python3/upstream-random-fixes.patch | |||
| @@ -0,0 +1,721 @@ | |||
| 1 | This patch updates random.c to match upstream python's code at revision | ||
| 2 | 8125d9a8152b. This addresses various issues around problems with glibc 2.24 | ||
| 3 | and 2.25 such that python would fail to start with: | ||
| 4 | |||
| 5 | [rpurdie@centos7 ~]$ /tmp/t2/sysroots/x86_64-pokysdk-linux/usr/bin/python3 | ||
| 6 | Fatal Python error: getentropy() failed | ||
| 7 | Aborted | ||
| 8 | |||
| 9 | (taken from our buildtools-tarball also breaks eSDK) | ||
| 10 | |||
| 11 | Upstream-Status: Backport | ||
| 12 | |||
| 13 | # HG changeset patch | ||
| 14 | # User Victor Stinner <victor.stinner@gmail.com> | ||
| 15 | # Date 1483957133 -3600 | ||
| 16 | # Node ID 8125d9a8152b79e712cb09c7094b9129b9bcea86 | ||
| 17 | # Parent 337461574c90281630751b6095c4e1baf380cf7d | ||
| 18 | Issue #29157: Prefer getrandom() over getentropy() | ||
| 19 | |||
| 20 | Copy and then adapt Python/random.c from default branch. Difference between 3.5 | ||
| 21 | and default branches: | ||
| 22 | |||
| 23 | * Python 3.5 only uses getrandom() in non-blocking mode: flags=GRND_NONBLOCK | ||
| 24 | * If getrandom() fails with EAGAIN: py_getrandom() immediately fails and | ||
| 25 | remembers that getrandom() doesn't work. | ||
| 26 | * Python 3.5 has no _PyOS_URandomNonblock() function: _PyOS_URandom() | ||
| 27 | works in non-blocking mode on Python 3.5 | ||
| 28 | |||
| 29 | RP 2017/1/22 | ||
| 30 | |||
| 31 | Index: Python-3.5.2/Python/random.c | ||
| 32 | =================================================================== | ||
| 33 | --- Python-3.5.2.orig/Python/random.c | ||
| 34 | +++ Python-3.5.2/Python/random.c | ||
| 35 | @@ -1,6 +1,9 @@ | ||
| 36 | #include "Python.h" | ||
| 37 | #ifdef MS_WINDOWS | ||
| 38 | # include <windows.h> | ||
| 39 | +/* All sample MSDN wincrypt programs include the header below. It is at least | ||
| 40 | + * required with Min GW. */ | ||
| 41 | +# include <wincrypt.h> | ||
| 42 | #else | ||
| 43 | # include <fcntl.h> | ||
| 44 | # ifdef HAVE_SYS_STAT_H | ||
| 45 | @@ -36,10 +39,9 @@ win32_urandom_init(int raise) | ||
| 46 | return 0; | ||
| 47 | |||
| 48 | error: | ||
| 49 | - if (raise) | ||
| 50 | + if (raise) { | ||
| 51 | PyErr_SetFromWindowsErr(0); | ||
| 52 | - else | ||
| 53 | - Py_FatalError("Failed to initialize Windows random API (CryptoGen)"); | ||
| 54 | + } | ||
| 55 | return -1; | ||
| 56 | } | ||
| 57 | |||
| 58 | @@ -52,8 +54,9 @@ win32_urandom(unsigned char *buffer, Py_ | ||
| 59 | |||
| 60 | if (hCryptProv == 0) | ||
| 61 | { | ||
| 62 | - if (win32_urandom_init(raise) == -1) | ||
| 63 | + if (win32_urandom_init(raise) == -1) { | ||
| 64 | return -1; | ||
| 65 | + } | ||
| 66 | } | ||
| 67 | |||
| 68 | while (size > 0) | ||
| 69 | @@ -62,11 +65,9 @@ win32_urandom(unsigned char *buffer, Py_ | ||
| 70 | if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer)) | ||
| 71 | { | ||
| 72 | /* CryptGenRandom() failed */ | ||
| 73 | - if (raise) | ||
| 74 | + if (raise) { | ||
| 75 | PyErr_SetFromWindowsErr(0); | ||
| 76 | - else | ||
| 77 | - Py_FatalError("Failed to initialized the randomized hash " | ||
| 78 | - "secret using CryptoGen)"); | ||
| 79 | + } | ||
| 80 | return -1; | ||
| 81 | } | ||
| 82 | buffer += chunk; | ||
| 83 | @@ -75,55 +76,29 @@ win32_urandom(unsigned char *buffer, Py_ | ||
| 84 | return 0; | ||
| 85 | } | ||
| 86 | |||
| 87 | -/* Issue #25003: Don't use getentropy() on Solaris (available since | ||
| 88 | - * Solaris 11.3), it is blocking whereas os.urandom() should not block. */ | ||
| 89 | -#elif defined(HAVE_GETENTROPY) && !defined(sun) | ||
| 90 | -#define PY_GETENTROPY 1 | ||
| 91 | - | ||
| 92 | -/* Fill buffer with size pseudo-random bytes generated by getentropy(). | ||
| 93 | - Return 0 on success, or raise an exception and return -1 on error. | ||
| 94 | - | ||
| 95 | - If fatal is nonzero, call Py_FatalError() instead of raising an exception | ||
| 96 | - on error. */ | ||
| 97 | -static int | ||
| 98 | -py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal) | ||
| 99 | -{ | ||
| 100 | - while (size > 0) { | ||
| 101 | - Py_ssize_t len = Py_MIN(size, 256); | ||
| 102 | - int res; | ||
| 103 | - | ||
| 104 | - if (!fatal) { | ||
| 105 | - Py_BEGIN_ALLOW_THREADS | ||
| 106 | - res = getentropy(buffer, len); | ||
| 107 | - Py_END_ALLOW_THREADS | ||
| 108 | - | ||
| 109 | - if (res < 0) { | ||
| 110 | - PyErr_SetFromErrno(PyExc_OSError); | ||
| 111 | - return -1; | ||
| 112 | - } | ||
| 113 | - } | ||
| 114 | - else { | ||
| 115 | - res = getentropy(buffer, len); | ||
| 116 | - if (res < 0) | ||
| 117 | - Py_FatalError("getentropy() failed"); | ||
| 118 | - } | ||
| 119 | - | ||
| 120 | - buffer += len; | ||
| 121 | - size -= len; | ||
| 122 | - } | ||
| 123 | - return 0; | ||
| 124 | -} | ||
| 125 | - | ||
| 126 | -#else | ||
| 127 | +#else /* !MS_WINDOWS */ | ||
| 128 | |||
| 129 | #if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL) | ||
| 130 | #define PY_GETRANDOM 1 | ||
| 131 | |||
| 132 | +/* Call getrandom() to get random bytes: | ||
| 133 | + | ||
| 134 | + - Return 1 on success | ||
| 135 | + - Return 0 if getrandom() is not available (failed with ENOSYS or EPERM), | ||
| 136 | + or if getrandom(GRND_NONBLOCK) failed with EAGAIN (system urandom not | ||
| 137 | + initialized yet). | ||
| 138 | + - Raise an exception (if raise is non-zero) and return -1 on error: | ||
| 139 | + if getrandom() failed with EINTR, raise is non-zero and the Python signal | ||
| 140 | + handler raised an exception, or if getrandom() failed with a different | ||
| 141 | + error. | ||
| 142 | + | ||
| 143 | + getrandom() is retried if it failed with EINTR: interrupted by a signal. */ | ||
| 144 | static int | ||
| 145 | py_getrandom(void *buffer, Py_ssize_t size, int raise) | ||
| 146 | { | ||
| 147 | - /* Is getrandom() supported by the running kernel? | ||
| 148 | - * Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */ | ||
| 149 | + /* Is getrandom() supported by the running kernel? Set to 0 if getrandom() | ||
| 150 | + failed with ENOSYS or EPERM. Need Linux kernel 3.17 or newer, or Solaris | ||
| 151 | + 11.3 or newer */ | ||
| 152 | static int getrandom_works = 1; | ||
| 153 | |||
| 154 | /* getrandom() on Linux will block if called before the kernel has | ||
| 155 | @@ -132,84 +107,165 @@ py_getrandom(void *buffer, Py_ssize_t si | ||
| 156 | * see https://bugs.python.org/issue26839. To avoid this, use the | ||
| 157 | * GRND_NONBLOCK flag. */ | ||
| 158 | const int flags = GRND_NONBLOCK; | ||
| 159 | - int n; | ||
| 160 | + char *dest; | ||
| 161 | + long n; | ||
| 162 | |||
| 163 | - if (!getrandom_works) | ||
| 164 | + if (!getrandom_works) { | ||
| 165 | return 0; | ||
| 166 | + } | ||
| 167 | |||
| 168 | + dest = buffer; | ||
| 169 | while (0 < size) { | ||
| 170 | #ifdef sun | ||
| 171 | /* Issue #26735: On Solaris, getrandom() is limited to returning up | ||
| 172 | - to 1024 bytes */ | ||
| 173 | + to 1024 bytes. Call it multiple times if more bytes are | ||
| 174 | + requested. */ | ||
| 175 | n = Py_MIN(size, 1024); | ||
| 176 | #else | ||
| 177 | - n = size; | ||
| 178 | + n = Py_MIN(size, LONG_MAX); | ||
| 179 | #endif | ||
| 180 | |||
| 181 | errno = 0; | ||
| 182 | #ifdef HAVE_GETRANDOM | ||
| 183 | if (raise) { | ||
| 184 | Py_BEGIN_ALLOW_THREADS | ||
| 185 | - n = getrandom(buffer, n, flags); | ||
| 186 | + n = getrandom(dest, n, flags); | ||
| 187 | Py_END_ALLOW_THREADS | ||
| 188 | } | ||
| 189 | else { | ||
| 190 | - n = getrandom(buffer, n, flags); | ||
| 191 | + n = getrandom(dest, n, flags); | ||
| 192 | } | ||
| 193 | #else | ||
| 194 | /* On Linux, use the syscall() function because the GNU libc doesn't | ||
| 195 | - * expose the Linux getrandom() syscall yet. See: | ||
| 196 | - * https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */ | ||
| 197 | + expose the Linux getrandom() syscall yet. See: | ||
| 198 | + https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */ | ||
| 199 | if (raise) { | ||
| 200 | Py_BEGIN_ALLOW_THREADS | ||
| 201 | - n = syscall(SYS_getrandom, buffer, n, flags); | ||
| 202 | + n = syscall(SYS_getrandom, dest, n, flags); | ||
| 203 | Py_END_ALLOW_THREADS | ||
| 204 | } | ||
| 205 | else { | ||
| 206 | - n = syscall(SYS_getrandom, buffer, n, flags); | ||
| 207 | + n = syscall(SYS_getrandom, dest, n, flags); | ||
| 208 | } | ||
| 209 | #endif | ||
| 210 | |||
| 211 | if (n < 0) { | ||
| 212 | - if (errno == ENOSYS) { | ||
| 213 | + /* ENOSYS: the syscall is not supported by the kernel. | ||
| 214 | + EPERM: the syscall is blocked by a security policy (ex: SECCOMP) | ||
| 215 | + or something else. */ | ||
| 216 | + if (errno == ENOSYS || errno == EPERM) { | ||
| 217 | getrandom_works = 0; | ||
| 218 | return 0; | ||
| 219 | } | ||
| 220 | + | ||
| 221 | if (errno == EAGAIN) { | ||
| 222 | - /* If we failed with EAGAIN, the entropy pool was | ||
| 223 | - * uninitialized. In this case, we return failure to fall | ||
| 224 | - * back to reading from /dev/urandom. | ||
| 225 | - * | ||
| 226 | - * Note: In this case the data read will not be random so | ||
| 227 | - * should not be used for cryptographic purposes. Retaining | ||
| 228 | - * the existing semantics for practical purposes. */ | ||
| 229 | + /* getrandom(GRND_NONBLOCK) fails with EAGAIN if the system | ||
| 230 | + urandom is not initialiazed yet. In this case, fall back on | ||
| 231 | + reading from /dev/urandom. | ||
| 232 | + | ||
| 233 | + Note: In this case the data read will not be random so | ||
| 234 | + should not be used for cryptographic purposes. Retaining | ||
| 235 | + the existing semantics for practical purposes. */ | ||
| 236 | getrandom_works = 0; | ||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | |||
| 240 | if (errno == EINTR) { | ||
| 241 | - if (PyErr_CheckSignals()) { | ||
| 242 | - if (!raise) | ||
| 243 | - Py_FatalError("getrandom() interrupted by a signal"); | ||
| 244 | - return -1; | ||
| 245 | + if (raise) { | ||
| 246 | + if (PyErr_CheckSignals()) { | ||
| 247 | + return -1; | ||
| 248 | + } | ||
| 249 | } | ||
| 250 | - /* retry getrandom() */ | ||
| 251 | + | ||
| 252 | + /* retry getrandom() if it was interrupted by a signal */ | ||
| 253 | continue; | ||
| 254 | } | ||
| 255 | |||
| 256 | - if (raise) | ||
| 257 | + if (raise) { | ||
| 258 | PyErr_SetFromErrno(PyExc_OSError); | ||
| 259 | - else | ||
| 260 | - Py_FatalError("getrandom() failed"); | ||
| 261 | + } | ||
| 262 | return -1; | ||
| 263 | } | ||
| 264 | |||
| 265 | - buffer += n; | ||
| 266 | + dest += n; | ||
| 267 | size -= n; | ||
| 268 | } | ||
| 269 | return 1; | ||
| 270 | } | ||
| 271 | -#endif | ||
| 272 | + | ||
| 273 | +#elif defined(HAVE_GETENTROPY) | ||
| 274 | +#define PY_GETENTROPY 1 | ||
| 275 | + | ||
| 276 | +/* Fill buffer with size pseudo-random bytes generated by getentropy(): | ||
| 277 | + | ||
| 278 | + - Return 1 on success | ||
| 279 | + - Return 0 if getentropy() syscall is not available (failed with ENOSYS or | ||
| 280 | + EPERM). | ||
| 281 | + - Raise an exception (if raise is non-zero) and return -1 on error: | ||
| 282 | + if getentropy() failed with EINTR, raise is non-zero and the Python signal | ||
| 283 | + handler raised an exception, or if getentropy() failed with a different | ||
| 284 | + error. | ||
| 285 | + | ||
| 286 | + getentropy() is retried if it failed with EINTR: interrupted by a signal. */ | ||
| 287 | +static int | ||
| 288 | +py_getentropy(char *buffer, Py_ssize_t size, int raise) | ||
| 289 | +{ | ||
| 290 | + /* Is getentropy() supported by the running kernel? Set to 0 if | ||
| 291 | + getentropy() failed with ENOSYS or EPERM. */ | ||
| 292 | + static int getentropy_works = 1; | ||
| 293 | + | ||
| 294 | + if (!getentropy_works) { | ||
| 295 | + return 0; | ||
| 296 | + } | ||
| 297 | + | ||
| 298 | + while (size > 0) { | ||
| 299 | + /* getentropy() is limited to returning up to 256 bytes. Call it | ||
| 300 | + multiple times if more bytes are requested. */ | ||
| 301 | + Py_ssize_t len = Py_MIN(size, 256); | ||
| 302 | + int res; | ||
| 303 | + | ||
| 304 | + if (raise) { | ||
| 305 | + Py_BEGIN_ALLOW_THREADS | ||
| 306 | + res = getentropy(buffer, len); | ||
| 307 | + Py_END_ALLOW_THREADS | ||
| 308 | + } | ||
| 309 | + else { | ||
| 310 | + res = getentropy(buffer, len); | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + if (res < 0) { | ||
| 314 | + /* ENOSYS: the syscall is not supported by the running kernel. | ||
| 315 | + EPERM: the syscall is blocked by a security policy (ex: SECCOMP) | ||
| 316 | + or something else. */ | ||
| 317 | + if (errno == ENOSYS || errno == EPERM) { | ||
| 318 | + getentropy_works = 0; | ||
| 319 | + return 0; | ||
| 320 | + } | ||
| 321 | + | ||
| 322 | + if (errno == EINTR) { | ||
| 323 | + if (raise) { | ||
| 324 | + if (PyErr_CheckSignals()) { | ||
| 325 | + return -1; | ||
| 326 | + } | ||
| 327 | + } | ||
| 328 | + | ||
| 329 | + /* retry getentropy() if it was interrupted by a signal */ | ||
| 330 | + continue; | ||
| 331 | + } | ||
| 332 | + | ||
| 333 | + if (raise) { | ||
| 334 | + PyErr_SetFromErrno(PyExc_OSError); | ||
| 335 | + } | ||
| 336 | + return -1; | ||
| 337 | + } | ||
| 338 | + | ||
| 339 | + buffer += len; | ||
| 340 | + size -= len; | ||
| 341 | + } | ||
| 342 | + return 1; | ||
| 343 | +} | ||
| 344 | +#endif /* defined(HAVE_GETENTROPY) && !defined(sun) */ | ||
| 345 | + | ||
| 346 | |||
| 347 | static struct { | ||
| 348 | int fd; | ||
| 349 | @@ -217,127 +273,123 @@ static struct { | ||
| 350 | ino_t st_ino; | ||
| 351 | } urandom_cache = { -1 }; | ||
| 352 | |||
| 353 | +/* Read random bytes from the /dev/urandom device: | ||
| 354 | |||
| 355 | -/* Read size bytes from /dev/urandom into buffer. | ||
| 356 | - Call Py_FatalError() on error. */ | ||
| 357 | -static void | ||
| 358 | -dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size) | ||
| 359 | -{ | ||
| 360 | - int fd; | ||
| 361 | - Py_ssize_t n; | ||
| 362 | + - Return 0 on success | ||
| 363 | + - Raise an exception (if raise is non-zero) and return -1 on error | ||
| 364 | |||
| 365 | - assert (0 < size); | ||
| 366 | + Possible causes of errors: | ||
| 367 | |||
| 368 | -#ifdef PY_GETRANDOM | ||
| 369 | - if (py_getrandom(buffer, size, 0) == 1) | ||
| 370 | - return; | ||
| 371 | - /* getrandom() is not supported by the running kernel, fall back | ||
| 372 | - * on reading /dev/urandom */ | ||
| 373 | -#endif | ||
| 374 | + - open() failed with ENOENT, ENXIO, ENODEV, EACCES: the /dev/urandom device | ||
| 375 | + was not found. For example, it was removed manually or not exposed in a | ||
| 376 | + chroot or container. | ||
| 377 | + - open() failed with a different error | ||
| 378 | + - fstat() failed | ||
| 379 | + - read() failed or returned 0 | ||
| 380 | |||
| 381 | - fd = _Py_open_noraise("/dev/urandom", O_RDONLY); | ||
| 382 | - if (fd < 0) | ||
| 383 | - Py_FatalError("Failed to open /dev/urandom"); | ||
| 384 | + read() is retried if it failed with EINTR: interrupted by a signal. | ||
| 385 | |||
| 386 | - while (0 < size) | ||
| 387 | - { | ||
| 388 | - do { | ||
| 389 | - n = read(fd, buffer, (size_t)size); | ||
| 390 | - } while (n < 0 && errno == EINTR); | ||
| 391 | - if (n <= 0) | ||
| 392 | - { | ||
| 393 | - /* stop on error or if read(size) returned 0 */ | ||
| 394 | - Py_FatalError("Failed to read bytes from /dev/urandom"); | ||
| 395 | - break; | ||
| 396 | - } | ||
| 397 | - buffer += n; | ||
| 398 | - size -= (Py_ssize_t)n; | ||
| 399 | - } | ||
| 400 | - close(fd); | ||
| 401 | -} | ||
| 402 | + The file descriptor of the device is kept open between calls to avoid using | ||
| 403 | + many file descriptors when run in parallel from multiple threads: | ||
| 404 | + see the issue #18756. | ||
| 405 | + | ||
| 406 | + st_dev and st_ino fields of the file descriptor (from fstat()) are cached to | ||
| 407 | + check if the file descriptor was replaced by a different file (which is | ||
| 408 | + likely a bug in the application): see the issue #21207. | ||
| 409 | |||
| 410 | -/* Read size bytes from /dev/urandom into buffer. | ||
| 411 | - Return 0 on success, raise an exception and return -1 on error. */ | ||
| 412 | + If the file descriptor was closed or replaced, open a new file descriptor | ||
| 413 | + but don't close the old file descriptor: it probably points to something | ||
| 414 | + important for some third-party code. */ | ||
| 415 | static int | ||
| 416 | -dev_urandom_python(char *buffer, Py_ssize_t size) | ||
| 417 | +dev_urandom(char *buffer, Py_ssize_t size, int raise) | ||
| 418 | { | ||
| 419 | int fd; | ||
| 420 | Py_ssize_t n; | ||
| 421 | - struct _Py_stat_struct st; | ||
| 422 | -#ifdef PY_GETRANDOM | ||
| 423 | - int res; | ||
| 424 | -#endif | ||
| 425 | |||
| 426 | - if (size <= 0) | ||
| 427 | - return 0; | ||
| 428 | + if (raise) { | ||
| 429 | + struct _Py_stat_struct st; | ||
| 430 | |||
| 431 | -#ifdef PY_GETRANDOM | ||
| 432 | - res = py_getrandom(buffer, size, 1); | ||
| 433 | - if (res < 0) | ||
| 434 | - return -1; | ||
| 435 | - if (res == 1) | ||
| 436 | - return 0; | ||
| 437 | - /* getrandom() is not supported by the running kernel, fall back | ||
| 438 | - * on reading /dev/urandom */ | ||
| 439 | -#endif | ||
| 440 | - | ||
| 441 | - if (urandom_cache.fd >= 0) { | ||
| 442 | - /* Does the fd point to the same thing as before? (issue #21207) */ | ||
| 443 | - if (_Py_fstat_noraise(urandom_cache.fd, &st) | ||
| 444 | - || st.st_dev != urandom_cache.st_dev | ||
| 445 | - || st.st_ino != urandom_cache.st_ino) { | ||
| 446 | - /* Something changed: forget the cached fd (but don't close it, | ||
| 447 | - since it probably points to something important for some | ||
| 448 | - third-party code). */ | ||
| 449 | - urandom_cache.fd = -1; | ||
| 450 | - } | ||
| 451 | - } | ||
| 452 | - if (urandom_cache.fd >= 0) | ||
| 453 | - fd = urandom_cache.fd; | ||
| 454 | - else { | ||
| 455 | - fd = _Py_open("/dev/urandom", O_RDONLY); | ||
| 456 | - if (fd < 0) { | ||
| 457 | - if (errno == ENOENT || errno == ENXIO || | ||
| 458 | - errno == ENODEV || errno == EACCES) | ||
| 459 | - PyErr_SetString(PyExc_NotImplementedError, | ||
| 460 | - "/dev/urandom (or equivalent) not found"); | ||
| 461 | - /* otherwise, keep the OSError exception raised by _Py_open() */ | ||
| 462 | - return -1; | ||
| 463 | - } | ||
| 464 | if (urandom_cache.fd >= 0) { | ||
| 465 | - /* urandom_fd was initialized by another thread while we were | ||
| 466 | - not holding the GIL, keep it. */ | ||
| 467 | - close(fd); | ||
| 468 | - fd = urandom_cache.fd; | ||
| 469 | + /* Does the fd point to the same thing as before? (issue #21207) */ | ||
| 470 | + if (_Py_fstat_noraise(urandom_cache.fd, &st) | ||
| 471 | + || st.st_dev != urandom_cache.st_dev | ||
| 472 | + || st.st_ino != urandom_cache.st_ino) { | ||
| 473 | + /* Something changed: forget the cached fd (but don't close it, | ||
| 474 | + since it probably points to something important for some | ||
| 475 | + third-party code). */ | ||
| 476 | + urandom_cache.fd = -1; | ||
| 477 | + } | ||
| 478 | } | ||
| 479 | + if (urandom_cache.fd >= 0) | ||
| 480 | + fd = urandom_cache.fd; | ||
| 481 | else { | ||
| 482 | - if (_Py_fstat(fd, &st)) { | ||
| 483 | - close(fd); | ||
| 484 | + fd = _Py_open("/dev/urandom", O_RDONLY); | ||
| 485 | + if (fd < 0) { | ||
| 486 | + if (errno == ENOENT || errno == ENXIO || | ||
| 487 | + errno == ENODEV || errno == EACCES) { | ||
| 488 | + PyErr_SetString(PyExc_NotImplementedError, | ||
| 489 | + "/dev/urandom (or equivalent) not found"); | ||
| 490 | + } | ||
| 491 | + /* otherwise, keep the OSError exception raised by _Py_open() */ | ||
| 492 | return -1; | ||
| 493 | } | ||
| 494 | + if (urandom_cache.fd >= 0) { | ||
| 495 | + /* urandom_fd was initialized by another thread while we were | ||
| 496 | + not holding the GIL, keep it. */ | ||
| 497 | + close(fd); | ||
| 498 | + fd = urandom_cache.fd; | ||
| 499 | + } | ||
| 500 | else { | ||
| 501 | - urandom_cache.fd = fd; | ||
| 502 | - urandom_cache.st_dev = st.st_dev; | ||
| 503 | - urandom_cache.st_ino = st.st_ino; | ||
| 504 | + if (_Py_fstat(fd, &st)) { | ||
| 505 | + close(fd); | ||
| 506 | + return -1; | ||
| 507 | + } | ||
| 508 | + else { | ||
| 509 | + urandom_cache.fd = fd; | ||
| 510 | + urandom_cache.st_dev = st.st_dev; | ||
| 511 | + urandom_cache.st_ino = st.st_ino; | ||
| 512 | + } | ||
| 513 | } | ||
| 514 | } | ||
| 515 | - } | ||
| 516 | |||
| 517 | - do { | ||
| 518 | - n = _Py_read(fd, buffer, (size_t)size); | ||
| 519 | - if (n == -1) | ||
| 520 | - return -1; | ||
| 521 | - if (n == 0) { | ||
| 522 | - PyErr_Format(PyExc_RuntimeError, | ||
| 523 | - "Failed to read %zi bytes from /dev/urandom", | ||
| 524 | - size); | ||
| 525 | + do { | ||
| 526 | + n = _Py_read(fd, buffer, (size_t)size); | ||
| 527 | + if (n == -1) | ||
| 528 | + return -1; | ||
| 529 | + if (n == 0) { | ||
| 530 | + PyErr_Format(PyExc_RuntimeError, | ||
| 531 | + "Failed to read %zi bytes from /dev/urandom", | ||
| 532 | + size); | ||
| 533 | + return -1; | ||
| 534 | + } | ||
| 535 | + | ||
| 536 | + buffer += n; | ||
| 537 | + size -= n; | ||
| 538 | + } while (0 < size); | ||
| 539 | + } | ||
| 540 | + else { | ||
| 541 | + fd = _Py_open_noraise("/dev/urandom", O_RDONLY); | ||
| 542 | + if (fd < 0) { | ||
| 543 | return -1; | ||
| 544 | } | ||
| 545 | |||
| 546 | - buffer += n; | ||
| 547 | - size -= n; | ||
| 548 | - } while (0 < size); | ||
| 549 | + while (0 < size) | ||
| 550 | + { | ||
| 551 | + do { | ||
| 552 | + n = read(fd, buffer, (size_t)size); | ||
| 553 | + } while (n < 0 && errno == EINTR); | ||
| 554 | |||
| 555 | + if (n <= 0) { | ||
| 556 | + /* stop on error or if read(size) returned 0 */ | ||
| 557 | + close(fd); | ||
| 558 | + return -1; | ||
| 559 | + } | ||
| 560 | + | ||
| 561 | + buffer += n; | ||
| 562 | + size -= n; | ||
| 563 | + } | ||
| 564 | + close(fd); | ||
| 565 | + } | ||
| 566 | return 0; | ||
| 567 | } | ||
| 568 | |||
| 569 | @@ -349,8 +401,8 @@ dev_urandom_close(void) | ||
| 570 | urandom_cache.fd = -1; | ||
| 571 | } | ||
| 572 | } | ||
| 573 | +#endif /* !MS_WINDOWS */ | ||
| 574 | |||
| 575 | -#endif | ||
| 576 | |||
| 577 | /* Fill buffer with pseudo-random bytes generated by a linear congruent | ||
| 578 | generator (LCG): | ||
| 579 | @@ -373,29 +425,98 @@ lcg_urandom(unsigned int x0, unsigned ch | ||
| 580 | } | ||
| 581 | } | ||
| 582 | |||
| 583 | -/* Fill buffer with size pseudo-random bytes from the operating system random | ||
| 584 | - number generator (RNG). It is suitable for most cryptographic purposes | ||
| 585 | - except long living private keys for asymmetric encryption. | ||
| 586 | +/* Read random bytes: | ||
| 587 | |||
| 588 | - Return 0 on success, raise an exception and return -1 on error. */ | ||
| 589 | -int | ||
| 590 | -_PyOS_URandom(void *buffer, Py_ssize_t size) | ||
| 591 | + - Return 0 on success | ||
| 592 | + - Raise an exception (if raise is non-zero) and return -1 on error | ||
| 593 | + | ||
| 594 | + Used sources of entropy ordered by preference, preferred source first: | ||
| 595 | + | ||
| 596 | + - CryptGenRandom() on Windows | ||
| 597 | + - getrandom() function (ex: Linux and Solaris): call py_getrandom() | ||
| 598 | + - getentropy() function (ex: OpenBSD): call py_getentropy() | ||
| 599 | + - /dev/urandom device | ||
| 600 | + | ||
| 601 | + Read from the /dev/urandom device if getrandom() or getentropy() function | ||
| 602 | + is not available or does not work. | ||
| 603 | + | ||
| 604 | + Prefer getrandom() over getentropy() because getrandom() supports blocking | ||
| 605 | + and non-blocking mode and Python requires non-blocking RNG at startup to | ||
| 606 | + initialize its hash secret: see the PEP 524. | ||
| 607 | + | ||
| 608 | + Prefer getrandom() and getentropy() over reading directly /dev/urandom | ||
| 609 | + because these functions don't need file descriptors and so avoid ENFILE or | ||
| 610 | + EMFILE errors (too many open files): see the issue #18756. | ||
| 611 | + | ||
| 612 | + Only use RNG running in the kernel. They are more secure because it is | ||
| 613 | + harder to get the internal state of a RNG running in the kernel land than a | ||
| 614 | + RNG running in the user land. The kernel has a direct access to the hardware | ||
| 615 | + and has access to hardware RNG, they are used as entropy sources. | ||
| 616 | + | ||
| 617 | + Note: the OpenSSL RAND_pseudo_bytes() function does not automatically reseed | ||
| 618 | + its RNG on fork(), two child processes (with the same pid) generate the same | ||
| 619 | + random numbers: see issue #18747. Kernel RNGs don't have this issue, | ||
| 620 | + they have access to good quality entropy sources. | ||
| 621 | + | ||
| 622 | + If raise is zero: | ||
| 623 | + | ||
| 624 | + - Don't raise an exception on error | ||
| 625 | + - Don't call the Python signal handler (don't call PyErr_CheckSignals()) if | ||
| 626 | + a function fails with EINTR: retry directly the interrupted function | ||
| 627 | + - Don't release the GIL to call functions. | ||
| 628 | +*/ | ||
| 629 | +static int | ||
| 630 | +pyurandom(void *buffer, Py_ssize_t size, int raise) | ||
| 631 | { | ||
| 632 | +#if defined(PY_GETRANDOM) || defined(PY_GETENTROPY) | ||
| 633 | + int res; | ||
| 634 | +#endif | ||
| 635 | + | ||
| 636 | if (size < 0) { | ||
| 637 | - PyErr_Format(PyExc_ValueError, | ||
| 638 | - "negative argument not allowed"); | ||
| 639 | + if (raise) { | ||
| 640 | + PyErr_Format(PyExc_ValueError, | ||
| 641 | + "negative argument not allowed"); | ||
| 642 | + } | ||
| 643 | return -1; | ||
| 644 | } | ||
| 645 | - if (size == 0) | ||
| 646 | + | ||
| 647 | + if (size == 0) { | ||
| 648 | return 0; | ||
| 649 | + } | ||
| 650 | |||
| 651 | #ifdef MS_WINDOWS | ||
| 652 | - return win32_urandom((unsigned char *)buffer, size, 1); | ||
| 653 | -#elif defined(PY_GETENTROPY) | ||
| 654 | - return py_getentropy(buffer, size, 0); | ||
| 655 | + return win32_urandom((unsigned char *)buffer, size, raise); | ||
| 656 | +#else | ||
| 657 | + | ||
| 658 | +#if defined(PY_GETRANDOM) || defined(PY_GETENTROPY) | ||
| 659 | +#ifdef PY_GETRANDOM | ||
| 660 | + res = py_getrandom(buffer, size, raise); | ||
| 661 | #else | ||
| 662 | - return dev_urandom_python((char*)buffer, size); | ||
| 663 | + res = py_getentropy(buffer, size, raise); | ||
| 664 | #endif | ||
| 665 | + if (res < 0) { | ||
| 666 | + return -1; | ||
| 667 | + } | ||
| 668 | + if (res == 1) { | ||
| 669 | + return 0; | ||
| 670 | + } | ||
| 671 | + /* getrandom() or getentropy() function is not available: failed with | ||
| 672 | + ENOSYS, EPERM or EAGAIN. Fall back on reading from /dev/urandom. */ | ||
| 673 | +#endif | ||
| 674 | + | ||
| 675 | + return dev_urandom(buffer, size, raise); | ||
| 676 | +#endif | ||
| 677 | +} | ||
| 678 | + | ||
| 679 | +/* Fill buffer with size pseudo-random bytes from the operating system random | ||
| 680 | + number generator (RNG). It is suitable for most cryptographic purposes | ||
| 681 | + except long living private keys for asymmetric encryption. | ||
| 682 | + | ||
| 683 | + Return 0 on success. Raise an exception and return -1 on error. */ | ||
| 684 | +int | ||
| 685 | +_PyOS_URandom(void *buffer, Py_ssize_t size) | ||
| 686 | +{ | ||
| 687 | + return pyurandom(buffer, size, 1); | ||
| 688 | } | ||
| 689 | |||
| 690 | void | ||
| 691 | @@ -436,13 +557,14 @@ _PyRandom_Init(void) | ||
| 692 | } | ||
| 693 | } | ||
| 694 | else { | ||
| 695 | -#ifdef MS_WINDOWS | ||
| 696 | - (void)win32_urandom(secret, secret_size, 0); | ||
| 697 | -#elif defined(PY_GETENTROPY) | ||
| 698 | - (void)py_getentropy(secret, secret_size, 1); | ||
| 699 | -#else | ||
| 700 | - dev_urandom_noraise(secret, secret_size); | ||
| 701 | -#endif | ||
| 702 | + int res; | ||
| 703 | + | ||
| 704 | + /* _PyRandom_Init() is called very early in the Python initialization | ||
| 705 | + and so exceptions cannot be used (use raise=0). */ | ||
| 706 | + res = pyurandom(secret, secret_size, 0); | ||
| 707 | + if (res < 0) { | ||
| 708 | + Py_FatalError("failed to get random numbers to initialize Python"); | ||
| 709 | + } | ||
| 710 | } | ||
| 711 | } | ||
| 712 | |||
| 713 | @@ -454,8 +576,6 @@ _PyRandom_Fini(void) | ||
| 714 | CryptReleaseContext(hCryptProv, 0); | ||
| 715 | hCryptProv = 0; | ||
| 716 | } | ||
| 717 | -#elif defined(PY_GETENTROPY) | ||
| 718 | - /* nothing to clean */ | ||
| 719 | #else | ||
| 720 | dev_urandom_close(); | ||
| 721 | #endif | ||
