/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 | } |