Coverage Report

Created: 2022-07-08 09:39

/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
}