Coverage Report

Created: 2025-06-13 06:36

/src/json-c/random_seed.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * random_seed.c
3
 *
4
 * Copyright (c) 2013 Metaparadigm Pte. Ltd.
5
 * Michael Clark <michael@metaparadigm.com>
6
 *
7
 * This library is free software; you can redistribute it and/or modify
8
 * it under the terms of the MIT license. See COPYING for details.
9
 *
10
 */
11
12
#include "random_seed.h"
13
#include "config.h"
14
#include "strerror_override.h"
15
#include <stdio.h>
16
#include <stdlib.h>
17
#ifdef HAVE_BSD_STDLIB_H
18
#include <bsd/stdlib.h>
19
#endif
20
21
#define DEBUG_SEED(s)
22
23
#if defined(__APPLE__) || defined(__unix__) || defined(__linux__)
24
#define HAVE_DEV_RANDOM 1
25
#endif
26
27
#ifdef HAVE_ARC4RANDOM
28
#undef HAVE_GETRANDOM
29
#undef HAVE_DEV_RANDOM
30
#undef HAVE_CRYPTGENRANDOM
31
#endif
32
33
#if defined ENABLE_RDRAND
34
35
/* cpuid */
36
37
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
38
#define HAS_X86_CPUID 1
39
40
static void do_cpuid(int regs[], int h)
41
{
42
  /* clang-format off */
43
    __asm__ __volatile__("cpuid"
44
                         : "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
45
                         : "a"(h));
46
  /* clang-format on */
47
}
48
49
#elif defined _MSC_VER
50
51
#define HAS_X86_CPUID 1
52
#define do_cpuid __cpuid
53
54
#endif
55
56
/* has_rdrand */
57
58
#if HAS_X86_CPUID
59
60
static int get_rdrand_seed(void);
61
62
/* Valid values are -1 (haven't tested), 0 (no), and 1 (yes). */
63
static int _has_rdrand = -1;
64
65
static int has_rdrand(void)
66
{
67
  if (_has_rdrand != -1)
68
  {
69
    return _has_rdrand;
70
  }
71
72
  /* CPUID.01H:ECX.RDRAND[bit 30] == 1 */
73
  int regs[4];
74
  do_cpuid(regs, 1);
75
  if (!(regs[2] & (1 << 30)))
76
  {
77
    _has_rdrand = 0;
78
    return 0;
79
  }
80
81
  /*
82
   * Some CPUs advertise RDRAND in CPUID, but return 0xFFFFFFFF
83
   * unconditionally. To avoid locking up later, test RDRAND here. If over
84
   * 3 trials RDRAND has returned the same value, declare it broken.
85
   * Example CPUs are AMD Ryzen 3000 series
86
   * and much older AMD APUs, such as the E1-1500
87
   * https://github.com/systemd/systemd/issues/11810
88
   * https://linuxreviews.org/RDRAND_stops_returning_random_values_on_older_AMD_CPUs_after_suspend
89
   */
90
  _has_rdrand = 0;
91
  int prev = get_rdrand_seed();
92
  for (int i = 0; i < 3; i++)
93
  {
94
    int temp = get_rdrand_seed();
95
    if (temp != prev)
96
    {
97
      _has_rdrand = 1;
98
      break;
99
    }
100
101
    prev = temp;
102
  }
103
104
  return _has_rdrand;
105
}
106
107
#endif
108
109
/* get_rdrand_seed - GCC x86 and X64 */
110
111
#if defined __GNUC__ && (defined __i386__ || defined __x86_64__)
112
113
#define HAVE_RDRAND 1
114
115
static int get_rdrand_seed(void)
116
{
117
  DEBUG_SEED("get_rdrand_seed");
118
  int _eax;
119
  /* rdrand eax */
120
  /* clang-format off */
121
  __asm__ __volatile__("1: .byte 0x0F\n"
122
                       "   .byte 0xC7\n"
123
                       "   .byte 0xF0\n"
124
                       "   jnc 1b;\n"
125
                       : "=a" (_eax));
126
  /* clang-format on */
127
  return _eax;
128
}
129
130
#endif
131
132
#if defined _MSC_VER
133
134
#if _MSC_VER >= 1700
135
#define HAVE_RDRAND 1
136
137
/* get_rdrand_seed - Visual Studio 2012 and above */
138
139
static int get_rdrand_seed(void)
140
{
141
  DEBUG_SEED("get_rdrand_seed");
142
  int r;
143
  while (_rdrand32_step(&r) == 0)
144
    ;
145
  return r;
146
}
147
148
#elif defined _M_IX86
149
#define HAVE_RDRAND 1
150
151
/* get_rdrand_seed - Visual Studio 2010 and below - x86 only */
152
153
/* clang-format off */
154
static int get_rdrand_seed(void)
155
{
156
  DEBUG_SEED("get_rdrand_seed");
157
  int _eax;
158
retry:
159
  /* rdrand eax */
160
  __asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0
161
  __asm jnc retry
162
  __asm mov _eax, eax
163
  return _eax;
164
}
165
/* clang-format on */
166
167
#endif
168
#endif
169
170
#endif /* defined ENABLE_RDRAND */
171
172
#ifdef HAVE_GETRANDOM
173
174
#include <stdlib.h>
175
#ifdef HAVE_SYS_RANDOM_H
176
#include <sys/random.h>
177
#endif
178
179
static int get_getrandom_seed(int *seed)
180
1
{
181
1
  DEBUG_SEED("get_getrandom_seed");
182
183
1
  ssize_t ret;
184
185
1
  do
186
1
  {
187
1
    ret = getrandom(seed, sizeof(*seed), GRND_NONBLOCK);
188
1
  } while ((ret == -1) && (errno == EINTR));
189
190
1
  if (ret == -1)
191
0
  {
192
0
    if (errno == ENOSYS) /* syscall not available in kernel */
193
0
      return -1;
194
0
    if (errno == EAGAIN) /* entropy not yet initialized */
195
0
      return -1;
196
197
0
    fprintf(stderr, "error from getrandom(): %s", strerror(errno));
198
0
    return -1;
199
0
  }
200
201
1
  if (ret != sizeof(*seed))
202
0
    return -1;
203
204
1
  return 0;
205
1
}
206
#endif /* defined HAVE_GETRANDOM */
207
208
/* get_dev_random_seed */
209
210
#ifdef HAVE_DEV_RANDOM
211
212
#include <fcntl.h>
213
#include <string.h>
214
#if HAVE_UNISTD_H
215
#include <unistd.h>
216
#endif /* HAVE_UNISTD_H */
217
#include <stdlib.h>
218
#include <sys/stat.h>
219
220
static const char *dev_random_file = "/dev/urandom";
221
222
static int get_dev_random_seed(int *seed)
223
0
{
224
0
  DEBUG_SEED("get_dev_random_seed");
225
226
0
  struct stat buf;
227
0
  if (stat(dev_random_file, &buf))
228
0
    return -1;
229
0
  if ((buf.st_mode & S_IFCHR) == 0)
230
0
    return -1;
231
232
  /* coverity[toctou] */
233
0
  int fd = open(dev_random_file, O_RDONLY);
234
0
  if (fd < 0)
235
0
  {
236
0
    fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno));
237
0
    return -1;
238
0
  }
239
240
0
  ssize_t nread = read(fd, seed, sizeof(*seed));
241
242
0
  close(fd);
243
244
0
  if (nread != sizeof(*seed))
245
0
  {
246
0
    fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno));
247
0
    return -1;
248
0
  }
249
250
0
  return 0;
251
0
}
252
253
#endif
254
255
/* get_cryptgenrandom_seed */
256
257
#ifdef _WIN32
258
259
#define HAVE_CRYPTGENRANDOM 1
260
261
/* clang-format off */
262
#include <windows.h>
263
264
/* Caution: these blank lines must remain so clang-format doesn't reorder
265
   includes to put windows.h after wincrypt.h */
266
267
#include <wincrypt.h>
268
/* clang-format on */
269
#ifndef __GNUC__
270
#pragma comment(lib, "advapi32.lib")
271
#endif
272
273
static int get_cryptgenrandom_seed(int *seed)
274
{
275
  HCRYPTPROV hProvider = 0;
276
  DWORD dwFlags = CRYPT_VERIFYCONTEXT;
277
278
  DEBUG_SEED("get_cryptgenrandom_seed");
279
280
  /* WinNT 4 and Win98 do no support CRYPT_SILENT */
281
  if (LOBYTE(LOWORD(GetVersion())) > 4)
282
    dwFlags |= CRYPT_SILENT;
283
284
  if (!CryptAcquireContextA(&hProvider, 0, 0, PROV_RSA_FULL, dwFlags))
285
  {
286
    fprintf(stderr, "error CryptAcquireContextA 0x%08lx", GetLastError());
287
    return -1;
288
  }
289
  else
290
  {
291
    BOOL ret = CryptGenRandom(hProvider, sizeof(*seed), (BYTE *)seed);
292
    CryptReleaseContext(hProvider, 0);
293
    if (!ret)
294
    {
295
      fprintf(stderr, "error CryptGenRandom 0x%08lx", GetLastError());
296
      return -1;
297
    }
298
  }
299
300
  return 0;
301
}
302
303
#endif
304
305
/* get_time_seed */
306
307
#ifndef HAVE_ARC4RANDOM
308
#include <time.h>
309
310
static int get_time_seed(void)
311
0
{
312
0
  DEBUG_SEED("get_time_seed");
313
314
  /* coverity[store_truncates_time_t] */
315
0
  return (unsigned)time(NULL) * 433494437;
316
0
}
317
#endif
318
319
/* json_c_get_random_seed */
320
321
int json_c_get_random_seed(void)
322
1
{
323
#ifdef OVERRIDE_GET_RANDOM_SEED
324
  OVERRIDE_GET_RANDOM_SEED;
325
#endif
326
#if defined HAVE_RDRAND && HAVE_RDRAND
327
  if (has_rdrand())
328
    return get_rdrand_seed();
329
#endif
330
#ifdef HAVE_ARC4RANDOM
331
  /* arc4random never fails, so use it if it's available */
332
  return arc4random();
333
#else
334
1
#ifdef HAVE_GETRANDOM
335
1
  {
336
1
    int seed = 0;
337
1
    if (get_getrandom_seed(&seed) == 0)
338
1
      return seed;
339
1
  }
340
0
#endif
341
0
#if defined HAVE_DEV_RANDOM && HAVE_DEV_RANDOM
342
0
  {
343
0
    int seed = 0;
344
0
    if (get_dev_random_seed(&seed) == 0)
345
0
      return seed;
346
0
  }
347
0
#endif
348
#if defined HAVE_CRYPTGENRANDOM && HAVE_CRYPTGENRANDOM
349
  {
350
    int seed = 0;
351
    if (get_cryptgenrandom_seed(&seed) == 0)
352
      return seed;
353
  }
354
#endif
355
0
  return get_time_seed();
356
0
#endif /* !HAVE_ARC4RANDOM */
357
0
}