Coverage Report

Created: 2025-06-13 06:36

/src/util-linux/lib/randutils.c
Line
Count
Source (jump to first uncovered line)
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
#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
45
#define DO_JRAND_MIX
46
THREAD_LOCAL unsigned short ul_jrand_seed[3];
47
#endif
48
49
int rand_get_number(int low_n, int high_n)
50
0
{
51
0
  return rand() % (high_n - low_n + 1) + low_n;
52
0
}
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
int random_get_fd(void)
77
0
{
78
0
  int fd;
79
80
0
  fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
81
0
  if (fd == -1)
82
0
    fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
83
0
  crank_random();
84
0
  return fd;
85
0
}
86
87
/*
88
 * Generate a stream of random nbytes into buf.
89
 * Use /dev/urandom if possible, and if not,
90
 * use glibc pseudo-random functions.
91
 */
92
0
#define UL_RAND_READ_ATTEMPTS 8
93
0
#define UL_RAND_READ_DELAY  125000  /* microseconds */
94
95
/*
96
 * Write @nbytes random bytes into @buf.
97
 *
98
 * Returns 0 for good quality of random bytes or 1 for weak quality.
99
 */
100
int ul_random_get_bytes(void *buf, size_t nbytes)
101
0
{
102
0
  unsigned char *cp = (unsigned char *)buf;
103
0
  size_t i, n = nbytes;
104
0
  int lose_counter = 0;
105
106
0
#ifdef HAVE_GETRANDOM
107
0
  while (n > 0) {
108
0
    int x;
109
110
0
    errno = 0;
111
0
    x = getrandom(cp, n, GRND_NONBLOCK);
112
0
    if (x > 0) {     /* success */
113
0
           n -= x;
114
0
           cp += x;
115
0
           lose_counter = 0;
116
0
           errno = 0;
117
0
    } else if (errno == ENOSYS) { /* kernel without getrandom() */
118
0
      break;
119
120
0
    } else if (errno == EAGAIN && lose_counter < UL_RAND_READ_ATTEMPTS) {
121
0
      xusleep(UL_RAND_READ_DELAY);  /* no entropy, wait and try again */
122
0
      lose_counter++;
123
0
    } else
124
0
      break;
125
0
  }
126
127
0
  if (errno == ENOSYS)
128
0
#endif
129
  /*
130
   * We've been built against headers that support getrandom, but the
131
   * running kernel does not.  Fallback to reading from /dev/{u,}random
132
   * as before
133
   */
134
0
  {
135
0
    int fd = random_get_fd();
136
137
0
    lose_counter = 0;
138
0
    if (fd >= 0) {
139
0
      while (n > 0) {
140
0
        ssize_t x = read(fd, cp, n);
141
0
        if (x <= 0) {
142
0
          if (lose_counter++ > UL_RAND_READ_ATTEMPTS)
143
0
            break;
144
0
          xusleep(UL_RAND_READ_DELAY);
145
0
          continue;
146
0
        }
147
0
        n -= x;
148
0
        cp += x;
149
0
        lose_counter = 0;
150
0
      }
151
152
0
      close(fd);
153
0
    }
154
0
  }
155
  /*
156
   * We do this all the time, but this is the only source of
157
   * randomness if /dev/random/urandom is out to lunch.
158
   */
159
0
  crank_random();
160
0
  for (cp = buf, i = 0; i < nbytes; i++)
161
0
    *cp++ ^= (rand() >> 7) & 0xFF;
162
163
0
#ifdef DO_JRAND_MIX
164
0
  {
165
0
    unsigned short tmp_seed[3];
166
167
0
    memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
168
0
    ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
169
0
    for (cp = buf, i = 0; i < nbytes; i++)
170
0
      *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
171
0
    memcpy(ul_jrand_seed, tmp_seed,
172
0
           sizeof(ul_jrand_seed)-sizeof(unsigned short));
173
0
  }
174
0
#endif
175
176
0
  return n != 0;
177
0
}
178
179
180
/*
181
 * Tell source of randomness.
182
 */
183
const char *random_tell_source(void)
184
0
{
185
0
#ifdef HAVE_GETRANDOM
186
0
  return _("getrandom() function");
187
#else
188
  size_t i;
189
  static const char *const random_sources[] = {
190
    "/dev/urandom",
191
    "/dev/random"
192
  };
193
194
  for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
195
    if (!access(random_sources[i], R_OK))
196
      return random_sources[i];
197
  }
198
#endif
199
0
  return _("libc pseudo-random functions");
200
0
}
201
202
#ifdef TEST_PROGRAM_RANDUTILS
203
#include <inttypes.h>
204
205
int main(int argc, char *argv[])
206
{
207
  size_t i, n;
208
  int64_t *vp, v;
209
  char *buf;
210
  size_t bufsz;
211
212
  n = argc == 1 ? 16 : atoi(argv[1]);
213
214
  printf("Multiple random calls:\n");
215
  for (i = 0; i < n; i++) {
216
    ul_random_get_bytes(&v, sizeof(v));
217
    printf("#%02zu: %25"PRIu64"\n", i, v);
218
  }
219
220
221
  printf("One random call:\n");
222
  bufsz = n * sizeof(*vp);
223
  buf = malloc(bufsz);
224
  if (!buf)
225
    err(EXIT_FAILURE, "failed to allocate buffer");
226
227
  ul_random_get_bytes(buf, bufsz);
228
  for (i = 0; i < n; i++) {
229
    vp = (int64_t *) (buf + (i * sizeof(*vp)));
230
    printf("#%02zu: %25"PRIu64"\n", i, *vp);
231
  }
232
233
  return EXIT_SUCCESS;
234
}
235
#endif /* TEST_PROGRAM_RANDUTILS */