Coverage Report

Created: 2026-04-28 06:29

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