Coverage Report

Created: 2026-08-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/lib/randutils.c
Line
Count
Source
1
/*
2
 * SPDX-License-Identifier: BSD-3-Clause
3
 *
4
 * General purpose random utilities. Based on libuuid code.
5
 *
6
 * This code is free software; you can redistribute it and/or modify it under
7
 * the terms of the Modified BSD License. The complete text of the license is
8
 * available in the Documentation/licenses/COPYING.BSD-3-Clause file.
9
 */
10
#include <stdio.h>
11
#include <unistd.h>
12
#include <fcntl.h>
13
#include <stdlib.h>
14
#include <string.h>
15
#include <sys/time.h>
16
#ifdef HAVE_SYS_SYSCALL_H
17
#include <sys/syscall.h>
18
#endif
19
#include "c.h"
20
#include "timeutils.h"
21
#include "randutils.h"
22
#include "nls.h"
23
24
25
#ifdef HAVE_GETRANDOM
26
# include <sys/random.h>
27
#elif defined (__linux__)
28
# if !defined(SYS_getrandom) && defined(__NR_getrandom)
29
   /* usable kernel-headers, but old glibc-headers */
30
#  define SYS_getrandom __NR_getrandom
31
# endif
32
#endif
33
34
#if !defined(HAVE_GETRANDOM) && defined(SYS_getrandom)
35
/* libc without function, but we have syscall */
36
#define GRND_NONBLOCK 0x01
37
#define GRND_RANDOM 0x02
38
static int getrandom(void *buf, size_t buflen, unsigned int flags)
39
{
40
  return (syscall(SYS_getrandom, buf, buflen, flags));
41
}
42
# define HAVE_GETRANDOM
43
#endif
44
45
#ifdef HAVE_SRANDOM
46
0
#define srand(x)  srandom(x)
47
0
#define rand()    random()
48
#endif
49
50
#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
51
#define DO_JRAND_MIX
52
THREAD_LOCAL unsigned short ul_jrand_seed[3];
53
#endif
54
55
static void crank_random(void)
56
0
{
57
0
  int i;
58
0
  struct timeval tv;
59
0
  unsigned int n_pid, n_uid;
60
61
0
  gettimeofday(&tv, NULL);
62
0
  n_pid = getpid();
63
0
  n_uid = getuid();
64
0
  srand((n_pid << 16) ^ n_uid ^ tv.tv_sec ^ tv.tv_usec);
65
66
0
#ifdef DO_JRAND_MIX
67
0
  ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
68
0
  ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
69
0
  ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
70
0
#endif
71
  /* Crank the random number generator a few times */
72
0
  gettimeofday(&tv, NULL);
73
0
  for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
74
0
    rand();
75
0
}
76
77
static int random_get_fd(ul_random_src_t *src)
78
0
{
79
0
  int fd;
80
81
0
  *src = UL_RAND_URANDOM;
82
0
  fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
83
0
  if (fd == -1) {
84
0
    *src = UL_RAND_RANDOM;
85
0
    fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
86
0
  }
87
0
  crank_random();
88
0
  return fd;
89
0
}
90
91
/*
92
 * Generate a stream of random nbytes into buf.
93
 * Use /dev/urandom if possible, and if not,
94
 * use glibc pseudo-random functions.
95
 */
96
0
#define UL_RAND_READ_ATTEMPTS 8
97
0
#define UL_RAND_READ_DELAY  125000  /* microseconds */
98
99
/*
100
 * Write @nbytes random bytes into @buf.
101
 *
102
 * Returns source of random bytes (0 for weak quality).
103
 */
104
ul_random_src_t ul_random_get_bytes(void *buf, size_t nbytes)
105
0
{
106
0
  unsigned char *cp = (unsigned char *)buf;
107
0
  size_t i, n = nbytes;
108
0
  ul_random_src_t src;
109
0
  int lose_counter = 0;
110
111
0
#ifdef HAVE_GETRANDOM
112
0
  while (n > 0) {
113
0
    int x;
114
115
0
    errno = 0;
116
0
    x = getrandom(cp, n, GRND_NONBLOCK);
117
0
    if (x > 0) {     /* success */
118
0
           n -= x;
119
0
           cp += x;
120
0
           lose_counter = 0;
121
0
           errno = 0;
122
0
    } else if (errno == ENOSYS) { /* kernel without getrandom() */
123
0
      break;
124
125
0
    } else if (errno == EAGAIN && lose_counter < UL_RAND_READ_ATTEMPTS) {
126
0
      xusleep(UL_RAND_READ_DELAY);  /* no entropy, wait and try again */
127
0
      lose_counter++;
128
0
    } else
129
0
      break;
130
0
  }
131
0
  if (n == 0)
132
0
    return UL_RAND_GETRANDOM;
133
134
0
  if (errno == ENOSYS)
135
  /*
136
   * We've been built against headers that support getrandom, but the
137
   * running kernel does not.  Fallback to reading from /dev/{u,}random
138
   * as before
139
   */
140
0
#endif
141
0
  {
142
0
    int fd = random_get_fd(&src);
143
144
0
    lose_counter = 0;
145
0
    if (fd >= 0) {
146
0
      while (n > 0) {
147
0
        ssize_t x = read(fd, cp, n);
148
0
        if (x <= 0) {
149
0
          if (lose_counter++ > UL_RAND_READ_ATTEMPTS)
150
0
            break;
151
0
          xusleep(UL_RAND_READ_DELAY);
152
0
          continue;
153
0
        }
154
0
        n -= x;
155
0
        cp += x;
156
0
        lose_counter = 0;
157
0
      }
158
159
0
      close(fd);
160
0
    }
161
0
    if (n == 0)
162
0
      return src;
163
0
  }
164
  /*
165
   * We do this all the time, but this is the only source of
166
   * randomness if /dev/random/urandom is out to lunch.
167
   */
168
0
  crank_random();
169
0
  for (cp = buf, i = 0; i < nbytes; i++)
170
0
    *cp++ ^= (rand() >> 7) & 0xFF;
171
172
0
#ifdef DO_JRAND_MIX
173
0
  {
174
0
    unsigned short tmp_seed[3];
175
176
0
    memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
177
0
    ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
178
0
    for (cp = buf, i = 0; i < nbytes; i++)
179
0
      *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
180
0
    memcpy(ul_jrand_seed, tmp_seed,
181
0
           sizeof(ul_jrand_seed)-sizeof(unsigned short));
182
0
  }
183
0
#endif
184
185
0
  return UL_RAND_WEAK;
186
0
}
187
188
189
/*
190
 * Tell source of randomness.
191
 */
192
const char *ul_random_tell_source(ul_random_src_t src)
193
0
{
194
0
  const char *s;
195
196
0
  switch (src) {
197
0
  case UL_RAND_GETRANDOM:
198
0
    s = _("getrandom() function");
199
0
    break;
200
0
  case UL_RAND_RANDOM:
201
0
    s = "/dev/random";
202
0
    break;
203
0
  case UL_RAND_URANDOM:
204
0
    s = "/dev/urandom";
205
0
    break;
206
0
  default:
207
0
    s = _("libc pseudo-random functions");
208
0
    break;
209
0
  }
210
211
0
  return s;
212
0
}
213
214
#ifdef TEST_PROGRAM_RANDUTILS
215
#include <inttypes.h>
216
217
int main(int argc, char *argv[])
218
{
219
  size_t i, n;
220
  int64_t *vp, v;
221
  char *buf;
222
  size_t bufsz;
223
224
  n = argc == 1 ? 16 : atoi(argv[1]);
225
226
  printf("Multiple random calls:\n");
227
  for (i = 0; i < n; i++) {
228
    ul_random_get_bytes(&v, sizeof(v));
229
    printf("#%02zu: %25"PRId64"\n", i, v);
230
  }
231
232
233
  printf("One random call:\n");
234
  bufsz = n * sizeof(*vp);
235
  buf = malloc(bufsz);
236
  if (!buf)
237
    err(EXIT_FAILURE, "failed to allocate buffer");
238
239
  ul_random_get_bytes(buf, bufsz);
240
  for (i = 0; i < n; i++) {
241
    vp = (int64_t *) (buf + (i * sizeof(*vp)));
242
    printf("#%02zu: %25"PRId64"\n", i, *vp);
243
  }
244
245
  return EXIT_SUCCESS;
246
}
247
#endif /* TEST_PROGRAM_RANDUTILS */