LCOV - code coverage report
Current view: top level - Python - bootstrap_hash.c (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 54 125 43.2 %
Date: 2022-07-07 18:19:46 Functions: 8 9 88.9 %

          Line data    Source code
       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      173576 : 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      173576 :     if (!getrandom_works) {
      97           0 :         return 0;
      98             :     }
      99             : 
     100      173576 :     flags = blocking ? 0 : GRND_NONBLOCK;
     101      173576 :     dest = buffer;
     102      347152 :     while (0 < size) {
     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      173576 :         n = Py_MIN(size, LONG_MAX);
     110             : #endif
     111             : 
     112      173576 :         errno = 0;
     113             : #ifdef HAVE_GETRANDOM
     114      173576 :         if (raise) {
     115      170672 :             Py_BEGIN_ALLOW_THREADS
     116      170672 :             n = getrandom(dest, n, flags);
     117      170672 :             Py_END_ALLOW_THREADS
     118             :         }
     119             :         else {
     120        2904 :             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      173576 :         if (n < 0) {
     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           0 :             if (errno == ENOSYS || errno == EPERM) {
     146           0 :                 getrandom_works = 0;
     147           0 :                 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           0 :             if (errno == EAGAIN && !raise && !blocking) {
     156           0 :                 return 0;
     157             :             }
     158             : 
     159           0 :             if (errno == EINTR) {
     160           0 :                 if (raise) {
     161           0 :                     if (PyErr_CheckSignals()) {
     162           0 :                         return -1;
     163             :                     }
     164             :                 }
     165             : 
     166             :                 /* retry getrandom() if it was interrupted by a signal */
     167           0 :                 continue;
     168             :             }
     169             : 
     170           0 :             if (raise) {
     171           0 :                 PyErr_SetFromErrno(PyExc_OSError);
     172             :             }
     173           0 :             return -1;
     174             :         }
     175             : 
     176      173576 :         dest += n;
     177      173576 :         size -= n;
     178             :     }
     179      173576 :     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           0 : dev_urandom(char *buffer, Py_ssize_t size, int raise)
     301             : {
     302             :     int fd;
     303             :     Py_ssize_t n;
     304             : 
     305           0 :     if (raise) {
     306             :         struct _Py_stat_struct st;
     307             :         int fstat_result;
     308             : 
     309           0 :         if (urandom_cache.fd >= 0) {
     310           0 :             Py_BEGIN_ALLOW_THREADS
     311           0 :             fstat_result = _Py_fstat_noraise(urandom_cache.fd, &st);
     312           0 :             Py_END_ALLOW_THREADS
     313             : 
     314             :             /* Does the fd point to the same thing as before? (issue #21207) */
     315           0 :             if (fstat_result
     316           0 :                 || st.st_dev != urandom_cache.st_dev
     317           0 :                 || st.st_ino != urandom_cache.st_ino) {
     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           0 :                 urandom_cache.fd = -1;
     322             :             }
     323             :         }
     324           0 :         if (urandom_cache.fd >= 0)
     325           0 :             fd = urandom_cache.fd;
     326             :         else {
     327           0 :             fd = _Py_open("/dev/urandom", O_RDONLY);
     328           0 :             if (fd < 0) {
     329           0 :                 if (errno == ENOENT || errno == ENXIO ||
     330           0 :                     errno == ENODEV || errno == EACCES) {
     331           0 :                     PyErr_SetString(PyExc_NotImplementedError,
     332             :                                     "/dev/urandom (or equivalent) not found");
     333             :                 }
     334             :                 /* otherwise, keep the OSError exception raised by _Py_open() */
     335           0 :                 return -1;
     336             :             }
     337           0 :             if (urandom_cache.fd >= 0) {
     338             :                 /* urandom_fd was initialized by another thread while we were
     339             :                    not holding the GIL, keep it. */
     340           0 :                 close(fd);
     341           0 :                 fd = urandom_cache.fd;
     342             :             }
     343             :             else {
     344           0 :                 if (_Py_fstat(fd, &st)) {
     345           0 :                     close(fd);
     346           0 :                     return -1;
     347             :                 }
     348             :                 else {
     349           0 :                     urandom_cache.fd = fd;
     350           0 :                     urandom_cache.st_dev = st.st_dev;
     351           0 :                     urandom_cache.st_ino = st.st_ino;
     352             :                 }
     353             :             }
     354             :         }
     355             : 
     356             :         do {
     357           0 :             n = _Py_read(fd, buffer, (size_t)size);
     358           0 :             if (n == -1)
     359           0 :                 return -1;
     360           0 :             if (n == 0) {
     361           0 :                 PyErr_Format(PyExc_RuntimeError,
     362             :                         "Failed to read %zi bytes from /dev/urandom",
     363             :                         size);
     364           0 :                 return -1;
     365             :             }
     366             : 
     367           0 :             buffer += n;
     368           0 :             size -= n;
     369           0 :         } while (0 < size);
     370             :     }
     371             :     else {
     372           0 :         fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
     373           0 :         if (fd < 0) {
     374           0 :             return -1;
     375             :         }
     376             : 
     377           0 :         while (0 < size)
     378             :         {
     379             :             do {
     380           0 :                 n = read(fd, buffer, (size_t)size);
     381           0 :             } while (n < 0 && errno == EINTR);
     382             : 
     383           0 :             if (n <= 0) {
     384             :                 /* stop on error or if read(size) returned 0 */
     385           0 :                 close(fd);
     386           0 :                 return -1;
     387             :             }
     388             : 
     389           0 :             buffer += n;
     390           0 :             size -= n;
     391             :         }
     392           0 :         close(fd);
     393             :     }
     394           0 :     return 0;
     395             : }
     396             : 
     397             : static void
     398        2951 : dev_urandom_close(void)
     399             : {
     400        2951 :     if (urandom_cache.fd >= 0) {
     401           0 :         close(urandom_cache.fd);
     402           0 :         urandom_cache.fd = -1;
     403             :     }
     404        2951 : }
     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          16 : lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
     416             : {
     417             :     size_t index;
     418             :     unsigned int x;
     419             : 
     420          16 :     x = x0;
     421         400 :     for (index=0; index < size; index++) {
     422         384 :         x *= 214013;
     423         384 :         x += 2531011;
     424             :         /* modulo 2 ^ (8 * sizeof(int)) */
     425         384 :         buffer[index] = (x >> 16) & 0xff;
     426             :     }
     427          16 : }
     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      173579 : 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      173579 :     if (size < 0) {
     480           0 :         if (raise) {
     481           0 :             PyErr_Format(PyExc_ValueError,
     482             :                          "negative argument not allowed");
     483             :         }
     484           0 :         return -1;
     485             :     }
     486             : 
     487      173579 :     if (size == 0) {
     488           3 :         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      173576 :         res = py_getrandom(buffer, size, blocking, raise);
     499             : #else
     500             :         res = py_getentropy(buffer, size, raise);
     501             : #endif
     502      173576 :         if (res < 0) {
     503           0 :             return -1;
     504             :         }
     505      173576 :         if (res == 1) {
     506      173576 :             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           0 :     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      168920 : _PyOS_URandom(void *buffer, Py_ssize_t size)
     528             : {
     529      168920 :     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        1755 : _PyOS_URandomNonblock(void *buffer, Py_ssize_t size)
     542             : {
     543        1755 :     return pyurandom(buffer, size, 0, 1);
     544             : }
     545             : 
     546             : 
     547             : PyStatus
     548        2963 : _Py_HashRandomization_Init(const PyConfig *config)
     549             : {
     550        2963 :     void *secret = &_Py_HashSecret;
     551        2963 :     Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
     552             : 
     553        2963 :     if (_Py_HashSecret_Initialized) {
     554          34 :         return _PyStatus_OK();
     555             :     }
     556        2929 :     _Py_HashSecret_Initialized = 1;
     557             : 
     558        2929 :     if (config->use_hash_seed) {
     559          25 :         if (config->hash_seed == 0) {
     560             :             /* disable the randomized hash */
     561           9 :             memset(secret, 0, secret_size);
     562             :         }
     563             :         else {
     564             :             /* use the specified hash seed */
     565          16 :             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        2904 :         res = pyurandom(secret, secret_size, 0, 0);
     578        2904 :         if (res < 0) {
     579           0 :             return _PyStatus_ERR("failed to get random numbers "
     580             :                                  "to initialize Python");
     581             :         }
     582             :     }
     583        2929 :     return _PyStatus_OK();
     584             : }
     585             : 
     586             : 
     587             : void
     588        2951 : _Py_HashRandomization_Fini(void)
     589             : {
     590             : #ifndef MS_WINDOWS
     591        2951 :     dev_urandom_close();
     592             : #endif
     593        2951 : }

Generated by: LCOV version 1.14