Coverage Report

Created: 2024-05-21 06:33

/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
#ifdef HAVE_TLS
24
#define THREAD_LOCAL static __thread
25
#else
26
#define THREAD_LOCAL static
27
#endif
28
29
#ifdef HAVE_GETRANDOM
30
# include <sys/random.h>
31
#elif defined (__linux__)
32
# if !defined(SYS_getrandom) && defined(__NR_getrandom)
33
   /* usable kernel-headers, but old glibc-headers */
34
#  define SYS_getrandom __NR_getrandom
35
# endif
36
#endif
37
38
#if !defined(HAVE_GETRANDOM) && defined(SYS_getrandom)
39
/* libc without function, but we have syscall */
40
#define GRND_NONBLOCK 0x01
41
#define GRND_RANDOM 0x02
42
static int getrandom(void *buf, size_t buflen, unsigned int flags)
43
{
44
  return (syscall(SYS_getrandom, buf, buflen, flags));
45
}
46
# define HAVE_GETRANDOM
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
int rand_get_number(int low_n, int high_n)
55
0
{
56
0
  return rand() % (high_n - low_n + 1) + low_n;
57
0
}
58
59
static void crank_random(void)
60
0
{
61
0
  int i;
62
0
  struct timeval tv;
63
0
  unsigned int n_pid, n_uid;
64
65
0
  gettimeofday(&tv, NULL);
66
0
  n_pid = getpid();
67
0
  n_uid = getuid();
68
0
  srand((n_pid << 16) ^ n_uid ^ tv.tv_sec ^ tv.tv_usec);
69
70
0
#ifdef DO_JRAND_MIX
71
0
  ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
72
0
  ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
73
0
  ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
74
0
#endif
75
  /* Crank the random number generator a few times */
76
0
  gettimeofday(&tv, NULL);
77
0
  for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
78
0
    rand();
79
0
}
80
81
int random_get_fd(void)
82
0
{
83
0
  int fd;
84
85
0
  fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
86
0
  if (fd == -1)
87
0
    fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
88
0
  crank_random();
89
0
  return fd;
90
0
}
91
92
/*
93
 * Generate a stream of random nbytes into buf.
94
 * Use /dev/urandom if possible, and if not,
95
 * use glibc pseudo-random functions.
96
 */
97
0
#define UL_RAND_READ_ATTEMPTS 8
98
0
#define UL_RAND_READ_DELAY  125000  /* microseconds */
99
100
/*
101
 * Write @nbytes random bytes into @buf.
102
 *
103
 * Returns 0 for good quality of random bytes or 1 for weak quality.
104
 */
105
int ul_random_get_bytes(void *buf, size_t nbytes)
106
0
{
107
0
  unsigned char *cp = (unsigned char *)buf;
108
0
  size_t i, n = nbytes;
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
132
0
  if (errno == ENOSYS)
133
0
#endif
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
  {
140
0
    int fd = random_get_fd();
141
142
0
    lose_counter = 0;
143
0
    if (fd >= 0) {
144
0
      while (n > 0) {
145
0
        ssize_t x = read(fd, cp, n);
146
0
        if (x <= 0) {
147
0
          if (lose_counter++ > UL_RAND_READ_ATTEMPTS)
148
0
            break;
149
0
          xusleep(UL_RAND_READ_DELAY);
150
0
          continue;
151
0
        }
152
0
        n -= x;
153
0
        cp += x;
154
0
        lose_counter = 0;
155
0
      }
156
157
0
      close(fd);
158
0
    }
159
0
  }
160
  /*
161
   * We do this all the time, but this is the only source of
162
   * randomness if /dev/random/urandom is out to lunch.
163
   */
164
0
  crank_random();
165
0
  for (cp = buf, i = 0; i < nbytes; i++)
166
0
    *cp++ ^= (rand() >> 7) & 0xFF;
167
168
0
#ifdef DO_JRAND_MIX
169
0
  {
170
0
    unsigned short tmp_seed[3];
171
172
0
    memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
173
0
    ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
174
0
    for (cp = buf, i = 0; i < nbytes; i++)
175
0
      *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
176
0
    memcpy(ul_jrand_seed, tmp_seed,
177
0
           sizeof(ul_jrand_seed)-sizeof(unsigned short));
178
0
  }
179
0
#endif
180
181
0
  return n != 0;
182
0
}
183
184
185
/*
186
 * Tell source of randomness.
187
 */
188
const char *random_tell_source(void)
189
0
{
190
0
#ifdef HAVE_GETRANDOM
191
0
  return _("getrandom() function");
192
#else
193
  size_t i;
194
  static const char *random_sources[] = {
195
    "/dev/urandom",
196
    "/dev/random"
197
  };
198
199
  for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
200
    if (!access(random_sources[i], R_OK))
201
      return random_sources[i];
202
  }
203
#endif
204
0
  return _("libc pseudo-random functions");
205
0
}
206
207
#ifdef TEST_PROGRAM_RANDUTILS
208
#include <inttypes.h>
209
210
int main(int argc, char *argv[])
211
{
212
  size_t i, n;
213
  int64_t *vp, v;
214
  char *buf;
215
  size_t bufsz;
216
217
  n = argc == 1 ? 16 : atoi(argv[1]);
218
219
  printf("Multiple random calls:\n");
220
  for (i = 0; i < n; i++) {
221
    ul_random_get_bytes(&v, sizeof(v));
222
    printf("#%02zu: %25"PRIu64"\n", i, v);
223
  }
224
225
226
  printf("One random call:\n");
227
  bufsz = n * sizeof(*vp);
228
  buf = malloc(bufsz);
229
  if (!buf)
230
    err(EXIT_FAILURE, "failed to allocate buffer");
231
232
  ul_random_get_bytes(buf, bufsz);
233
  for (i = 0; i < n; i++) {
234
    vp = (int64_t *) (buf + (i * sizeof(*vp)));
235
    printf("#%02zu: %25"PRIu64"\n", i, *vp);
236
  }
237
238
  return EXIT_SUCCESS;
239
}
240
#endif /* TEST_PROGRAM_RANDUTILS */