GCC Code Coverage Report


Directory: ./
File: submodules/json-c/random_seed.c
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 30 0.0%
Branches: 0 24 0.0%

Line Branch Exec Source
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 {
181 DEBUG_SEED("get_getrandom_seed");
182
183 ssize_t ret;
184
185 do
186 {
187 ret = getrandom(seed, sizeof(*seed), GRND_NONBLOCK);
188 } while ((ret == -1) && (errno == EINTR));
189
190 if (ret == -1)
191 {
192 if (errno == ENOSYS) /* syscall not available in kernel */
193 return -1;
194 if (errno == EAGAIN) /* entropy not yet initialized */
195 return -1;
196
197 fprintf(stderr, "error from getrandom(): %s", strerror(errno));
198 return -1;
199 }
200
201 if (ret != sizeof(*seed))
202 return -1;
203
204 return 0;
205 }
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 {
224 DEBUG_SEED("get_dev_random_seed");
225
226 struct stat buf;
227 if (stat(dev_random_file, &buf))
228 return -1;
229 if ((buf.st_mode & S_IFCHR) == 0)
230 return -1;
231
232 int fd = open(dev_random_file, O_RDONLY);
233 if (fd < 0)
234 {
235 fprintf(stderr, "error opening %s: %s", dev_random_file, strerror(errno));
236 return -1;
237 }
238
239 ssize_t nread = read(fd, seed, sizeof(*seed));
240
241 close(fd);
242
243 if (nread != sizeof(*seed))
244 {
245 fprintf(stderr, "error short read %s: %s", dev_random_file, strerror(errno));
246 return -1;
247 }
248
249 return 0;
250 }
251
252 #endif
253
254 /* get_cryptgenrandom_seed */
255
256 #ifdef WIN32
257
258 #define HAVE_CRYPTGENRANDOM 1
259
260 /* clang-format off */
261 #include <windows.h>
262
263 /* Caution: these blank lines must remain so clang-format doesn't reorder
264 includes to put windows.h after wincrypt.h */
265
266 #include <wincrypt.h>
267 /* clang-format on */
268 #ifndef __GNUC__
269 #pragma comment(lib, "advapi32.lib")
270 #endif
271
272 static int get_cryptgenrandom_seed(int *seed)
273 {
274 HCRYPTPROV hProvider = 0;
275 DWORD dwFlags = CRYPT_VERIFYCONTEXT;
276
277 DEBUG_SEED("get_cryptgenrandom_seed");
278
279 /* WinNT 4 and Win98 do no support CRYPT_SILENT */
280 if (LOBYTE(LOWORD(GetVersion())) > 4)
281 dwFlags |= CRYPT_SILENT;
282
283 if (!CryptAcquireContextA(&hProvider, 0, 0, PROV_RSA_FULL, dwFlags))
284 {
285 fprintf(stderr, "error CryptAcquireContextA 0x%08lx", GetLastError());
286 return -1;
287 }
288 else
289 {
290 BOOL ret = CryptGenRandom(hProvider, sizeof(*seed), (BYTE *)seed);
291 CryptReleaseContext(hProvider, 0);
292 if (!ret)
293 {
294 fprintf(stderr, "error CryptGenRandom 0x%08lx", GetLastError());
295 return -1;
296 }
297 }
298
299 return 0;
300 }
301
302 #endif
303
304 /* get_time_seed */
305
306 #ifndef HAVE_ARC4RANDOM
307 #include <time.h>
308
309 static int get_time_seed(void)
310 {
311 DEBUG_SEED("get_time_seed");
312
313 /* coverity[store_truncates_time_t] */
314 return (unsigned)time(NULL) * 433494437;
315 }
316 #endif
317
318 /* json_c_get_random_seed */
319
320 int json_c_get_random_seed(void)
321 {
322 #ifdef OVERRIDE_GET_RANDOM_SEED
323 OVERRIDE_GET_RANDOM_SEED;
324 #endif
325 #if defined HAVE_RDRAND && HAVE_RDRAND
326 if (has_rdrand())
327 return get_rdrand_seed();
328 #endif
329 #ifdef HAVE_ARC4RANDOM
330 /* arc4random never fails, so use it if it's available */
331 return arc4random();
332 #else
333 #ifdef HAVE_GETRANDOM
334 {
335 int seed = 0;
336 if (get_getrandom_seed(&seed) == 0)
337 return seed;
338 }
339 #endif
340 #if defined HAVE_DEV_RANDOM && HAVE_DEV_RANDOM
341 {
342 int seed = 0;
343 if (get_dev_random_seed(&seed) == 0)
344 return seed;
345 }
346 #endif
347 #if defined HAVE_CRYPTGENRANDOM && HAVE_CRYPTGENRANDOM
348 {
349 int seed = 0;
350 if (get_cryptgenrandom_seed(&seed) == 0)
351 return seed;
352 }
353 #endif
354 return get_time_seed();
355 #endif /* !HAVE_ARC4RANDOM */
356 }
357