Coverage Report

Created: 2026-07-16 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libsodium/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c
Line
Count
Source
1
#include <assert.h>
2
#include <errno.h>
3
#include <fcntl.h>
4
#include <limits.h>
5
#include <stdint.h>
6
#include <string.h>
7
#ifndef _WIN32
8
# include <unistd.h>
9
#endif
10
#include <stdlib.h>
11
12
#include <sys/types.h>
13
#ifndef _WIN32
14
# include <sys/stat.h>
15
# include <sys/time.h>
16
#endif
17
#ifdef __linux__
18
# define _LINUX_SOURCE
19
#endif
20
#ifdef HAVE_SYS_RANDOM_H
21
# include <sys/random.h>
22
#endif
23
#ifdef __linux__
24
# ifdef HAVE_GETRANDOM
25
#  define HAVE_LINUX_COMPATIBLE_GETRANDOM
26
# else
27
#  include <sys/syscall.h>
28
#  if defined(SYS_getrandom) && defined(__NR_getrandom)
29
#   define getrandom(B, S, F) syscall(SYS_getrandom, (B), (int) (S), (F))
30
#   define HAVE_LINUX_COMPATIBLE_GETRANDOM
31
#  endif
32
# endif
33
#elif defined(__FreeBSD__) || defined(__DragonFly__)
34
# include <sys/param.h>
35
# if (defined(__FreeBSD_version) && __FreeBSD_version >= 1200000) || \
36
     (defined(__DragonFly_version) && __DragonFly_version >= 500700)
37
#  define HAVE_LINUX_COMPATIBLE_GETRANDOM
38
# endif
39
#endif
40
#if !defined(NO_BLOCKING_RANDOM_POLL) && defined(__linux__)
41
# define BLOCK_ON_DEV_RANDOM
42
#endif
43
#ifdef BLOCK_ON_DEV_RANDOM
44
# include <poll.h>
45
#endif
46
47
#include "core.h"
48
#include "private/common.h"
49
#include "randombytes.h"
50
#include "randombytes_sysrandom.h"
51
#include "utils.h"
52
53
#ifdef _WIN32
54
/* `RtlGenRandom` is used over `CryptGenRandom` on Microsoft Windows based systems:
55
 *  - `CryptGenRandom` requires pulling in `CryptoAPI` which causes unnecessary
56
 *     memory overhead if this API is not being used for other purposes
57
 *  - `RtlGenRandom` is thus called directly instead. A detailed explanation
58
 *     can be found here: https://blogs.msdn.microsoft.com/michael_howard/2005/01/14/cryptographically-secure-random-number-on-windows-without-using-cryptoapi/
59
 *
60
 * In spite of the disclaimer on the `RtlGenRandom` documentation page that was
61
 * written back in the Windows XP days, this function is here to stay. The CRT
62
 * function `rand_s()` directly depends on it, so touching it would break many
63
 * applications released since Windows XP.
64
 *
65
 * Also note that Rust, Firefox and BoringSSL (thus, Google Chrome and everything
66
 * based on Chromium) also depend on it, and that libsodium allows the RNG to be
67
 * replaced without patching nor recompiling the library.
68
 */
69
# include <windows.h>
70
# define RtlGenRandom SystemFunction036
71
# if defined(__cplusplus)
72
extern "C"
73
# endif
74
BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
75
# pragma comment(lib, "advapi32.lib")
76
#endif
77
78
#if defined(__OpenBSD__) || defined(__CloudABI__) || defined(__wasi__)
79
# define HAVE_SAFE_ARC4RANDOM 1
80
#endif
81
82
#ifndef SSIZE_MAX
83
# define SSIZE_MAX (SIZE_MAX / 2 - 1)
84
#endif
85
86
#ifdef HAVE_SAFE_ARC4RANDOM
87
88
static uint32_t
89
randombytes_sysrandom(void)
90
{
91
    return arc4random();
92
}
93
94
static void
95
randombytes_sysrandom_stir(void)
96
{
97
}
98
99
static void
100
randombytes_sysrandom_buf(void * const buf, const size_t size)
101
{
102
    arc4random_buf(buf, size);
103
}
104
105
static int
106
randombytes_sysrandom_close(void)
107
{
108
    return 0;
109
}
110
111
#else /* HAVE_SAFE_ARC4RANDOM */
112
113
typedef struct SysRandom_ {
114
    int random_data_source_fd;
115
    int initialized;
116
    int getrandom_available;
117
} SysRandom;
118
119
static SysRandom stream = {
120
    SODIUM_C99(.random_data_source_fd =) -1,
121
    SODIUM_C99(.initialized =) 0,
122
    SODIUM_C99(.getrandom_available =) 0
123
};
124
125
# ifndef _WIN32
126
static ssize_t
127
safe_read(const int fd, void * const buf_, size_t size)
128
0
{
129
0
    unsigned char *buf = (unsigned char *) buf_;
130
0
    ssize_t        readnb;
131
132
0
    assert(size > (size_t) 0U);
133
0
    assert(size <= SSIZE_MAX);
134
0
    do {
135
0
        while ((readnb = read(fd, buf, size)) < (ssize_t) 0 &&
136
0
               (errno == EINTR || errno == EAGAIN)); /* LCOV_EXCL_LINE */
137
0
        if (readnb < (ssize_t) 0) {
138
0
            return readnb; /* LCOV_EXCL_LINE */
139
0
        }
140
0
        if (readnb == (ssize_t) 0) {
141
0
            break; /* LCOV_EXCL_LINE */
142
0
        }
143
0
        size -= (size_t) readnb;
144
0
        buf += readnb;
145
0
    } while (size > (ssize_t) 0);
146
147
0
    return (ssize_t) (buf - (unsigned char *) buf_);
148
0
}
149
150
#  ifdef BLOCK_ON_DEV_RANDOM
151
static int
152
randombytes_block_on_dev_random(void)
153
0
{
154
0
    struct pollfd pfd;
155
0
    int           fd;
156
0
    int           pret;
157
158
0
    fd = open("/dev/random", O_RDONLY);
159
0
    if (fd == -1) {
160
0
        return 0;
161
0
    }
162
0
    pfd.fd = fd;
163
0
    pfd.events = POLLIN;
164
0
    pfd.revents = 0;
165
0
    do {
166
0
        pret = poll(&pfd, 1, -1);
167
0
    } while (pret < 0 && (errno == EINTR || errno == EAGAIN));
168
0
    if (pret != 1) {
169
0
        (void) close(fd);
170
0
        errno = EIO;
171
0
        return -1;
172
0
    }
173
0
    return close(fd);
174
0
}
175
#  endif /* BLOCK_ON_DEV_RANDOM */
176
177
static int
178
randombytes_sysrandom_random_dev_open(void)
179
0
{
180
/* LCOV_EXCL_START */
181
0
    struct stat        st;
182
0
    static const char *devices[] = {
183
0
#  ifndef USE_BLOCKING_RANDOM
184
0
        "/dev/urandom",
185
0
#  endif
186
0
        "/dev/random", NULL
187
0
    };
188
0
    const char       **device = devices;
189
0
    int                fd;
190
191
0
#  ifdef BLOCK_ON_DEV_RANDOM
192
0
    if (randombytes_block_on_dev_random() != 0) {
193
0
        return -1;
194
0
    }
195
0
#  endif
196
0
    do {
197
0
        fd = open(*device, O_RDONLY);
198
0
        if (fd != -1) {
199
0
            if (fstat(fd, &st) == 0 &&
200
#  ifdef __COMPCERT__
201
                1
202
#  elif defined(S_ISNAM)
203
                (S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode))
204
#  else
205
0
                S_ISCHR(st.st_mode)
206
0
#  endif
207
0
               ) {
208
0
#  if defined(F_SETFD) && defined(FD_CLOEXEC)
209
0
                (void) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
210
0
#  endif
211
0
                return fd;
212
0
            }
213
0
            (void) close(fd);
214
0
        } else if (errno == EINTR) {
215
0
            continue;
216
0
        }
217
0
        device++;
218
0
    } while (*device != NULL);
219
220
0
    errno = EIO;
221
0
    return -1;
222
/* LCOV_EXCL_STOP */
223
0
}
224
225
#  ifdef HAVE_LINUX_COMPATIBLE_GETRANDOM
226
static int
227
_randombytes_linux_getrandom(void * const buf, const size_t size)
228
2
{
229
2
    int readnb;
230
231
2
    assert(size <= 256U);
232
2
    do {
233
2
        readnb = getrandom(buf, size, 0);
234
2
    } while (readnb < 0 && (errno == EINTR || errno == EAGAIN));
235
236
2
    return (readnb == (int) size) - 1;
237
2
}
238
239
static int
240
randombytes_linux_getrandom(void * const buf_, size_t size)
241
2
{
242
2
    unsigned char *buf = (unsigned char *) buf_;
243
2
    size_t         chunk_size = 256U;
244
245
2
    do {
246
2
        if (size < chunk_size) {
247
2
            chunk_size = size;
248
2
            assert(chunk_size > (size_t) 0U);
249
2
        }
250
2
        if (_randombytes_linux_getrandom(buf, chunk_size) != 0) {
251
0
            return -1;
252
0
        }
253
2
        size -= chunk_size;
254
2
        buf += chunk_size;
255
2
    } while (size > (size_t) 0U);
256
257
2
    return 0;
258
2
}
259
#  endif /* HAVE_LINUX_COMPATIBLE_GETRANDOM */
260
261
static void
262
randombytes_sysrandom_init(void)
263
1
{
264
1
    const int     errno_save = errno;
265
266
1
#  ifdef HAVE_LINUX_COMPATIBLE_GETRANDOM
267
1
    {
268
1
        unsigned char fodder[16];
269
270
1
        if (randombytes_linux_getrandom(fodder, sizeof fodder) == 0) {
271
1
            stream.getrandom_available = 1;
272
1
            errno = errno_save;
273
1
            return;
274
1
        }
275
0
        stream.getrandom_available = 0;
276
0
    }
277
0
#  endif
278
279
0
    if ((stream.random_data_source_fd =
280
0
         randombytes_sysrandom_random_dev_open()) == -1) {
281
0
        sodium_misuse(); /* LCOV_EXCL_LINE */
282
0
    }
283
0
    errno = errno_save;
284
0
}
285
286
# else /* _WIN32 */
287
288
static void
289
randombytes_sysrandom_init(void)
290
{
291
}
292
# endif /* _WIN32 */
293
294
static void
295
randombytes_sysrandom_stir(void)
296
2
{
297
2
    if (stream.initialized == 0) {
298
1
        randombytes_sysrandom_init();
299
1
        stream.initialized = 1;
300
1
    }
301
2
}
302
303
static void
304
randombytes_sysrandom_stir_if_needed(void)
305
1
{
306
1
    if (stream.initialized == 0) {
307
0
        randombytes_sysrandom_stir();
308
0
    }
309
1
}
310
311
static int
312
randombytes_sysrandom_close(void)
313
991
{
314
991
    int ret = -1;
315
316
991
# ifndef _WIN32
317
991
    if (stream.random_data_source_fd != -1 &&
318
0
        close(stream.random_data_source_fd) == 0) {
319
0
        stream.random_data_source_fd = -1;
320
0
        stream.initialized = 0;
321
0
        ret = 0;
322
0
    }
323
991
#  ifdef HAVE_LINUX_COMPATIBLE_GETRANDOM
324
991
    if (stream.getrandom_available != 0) {
325
991
        ret = 0;
326
991
    }
327
991
#  endif
328
# else /* _WIN32 */
329
    if (stream.initialized != 0) {
330
        stream.initialized = 0;
331
        ret = 0;
332
    }
333
# endif /* _WIN32 */
334
991
    return ret;
335
991
}
336
337
static void
338
randombytes_sysrandom_buf(void * const buf, const size_t size)
339
1
{
340
1
    randombytes_sysrandom_stir_if_needed();
341
1
# if defined(ULLONG_MAX) && defined(SIZE_MAX)
342
#  if SIZE_MAX > ULLONG_MAX
343
    /* coverity[result_independent_of_operands] */
344
    assert(size <= ULLONG_MAX);
345
#  endif
346
1
# endif
347
1
# ifndef _WIN32
348
1
#  ifdef HAVE_LINUX_COMPATIBLE_GETRANDOM
349
1
    if (stream.getrandom_available != 0) {
350
1
        if (randombytes_linux_getrandom(buf, size) != 0) {
351
0
            sodium_misuse(); /* LCOV_EXCL_LINE */
352
0
        }
353
1
        return;
354
1
    }
355
0
#  endif
356
0
    if (stream.random_data_source_fd == -1 ||
357
0
        safe_read(stream.random_data_source_fd, buf, size) != (ssize_t) size) {
358
0
        sodium_misuse(); /* LCOV_EXCL_LINE */
359
0
    }
360
# else /* _WIN32 */
361
    COMPILER_ASSERT(randombytes_BYTES_MAX <= 0xffffffffUL);
362
    if (size > (size_t) 0xffffffffUL) {
363
        sodium_misuse(); /* LCOV_EXCL_LINE */
364
    }
365
    if (! RtlGenRandom((PVOID) buf, (ULONG) size)) {
366
        sodium_misuse(); /* LCOV_EXCL_LINE */
367
    }
368
# endif /* _WIN32 */
369
0
}
370
371
static uint32_t
372
randombytes_sysrandom(void)
373
0
{
374
0
    uint32_t r;
375
376
0
    randombytes_sysrandom_buf(&r, sizeof r);
377
378
0
    return r;
379
0
}
380
381
#endif /* HAVE_SAFE_ARC4RANDOM */
382
383
static const char *
384
randombytes_sysrandom_implementation_name(void)
385
0
{
386
0
    return "sysrandom";
387
0
}
388
389
struct randombytes_implementation randombytes_sysrandom_implementation = {
390
    SODIUM_C99(.implementation_name =) randombytes_sysrandom_implementation_name,
391
    SODIUM_C99(.random =) randombytes_sysrandom,
392
    SODIUM_C99(.stir =) randombytes_sysrandom_stir,
393
    SODIUM_C99(.uniform =) NULL,
394
    SODIUM_C99(.buf =) randombytes_sysrandom_buf,
395
    SODIUM_C99(.close =) randombytes_sysrandom_close
396
};