/src/jansson/src/hashtable_seed.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Generate sizeof(uint32_t) bytes of as random data as possible to seed |
2 | | the hash function. |
3 | | */ |
4 | | |
5 | | #ifdef HAVE_CONFIG_H |
6 | | #include <jansson_private_config.h> |
7 | | #endif |
8 | | |
9 | | #include <stdio.h> |
10 | | #include <time.h> |
11 | | |
12 | | #ifdef HAVE_STDINT_H |
13 | | #include <stdint.h> |
14 | | #endif |
15 | | |
16 | | #ifdef HAVE_FCNTL_H |
17 | | #include <fcntl.h> |
18 | | #endif |
19 | | |
20 | | #ifdef HAVE_SCHED_H |
21 | | #include <sched.h> |
22 | | #endif |
23 | | |
24 | | #ifdef HAVE_UNISTD_H |
25 | | #include <unistd.h> |
26 | | #endif |
27 | | |
28 | | #ifdef HAVE_SYS_STAT_H |
29 | | #include <sys/stat.h> |
30 | | #endif |
31 | | |
32 | | #ifdef HAVE_SYS_TIME_H |
33 | | #include <sys/time.h> |
34 | | #endif |
35 | | |
36 | | #ifdef HAVE_SYS_TYPES_H |
37 | | #include <sys/types.h> |
38 | | #endif |
39 | | |
40 | | #if defined(_WIN32) |
41 | | /* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */ |
42 | | #include <windows.h> |
43 | | #endif |
44 | | |
45 | | #include "jansson.h" |
46 | | |
47 | 1 | static uint32_t buf_to_uint32(char *data) { |
48 | 1 | size_t i; |
49 | 1 | uint32_t result = 0; |
50 | | |
51 | 5 | for (i = 0; i < sizeof(uint32_t); i++) |
52 | 4 | result = (result << 8) | (unsigned char)data[i]; |
53 | | |
54 | 1 | return result; |
55 | 1 | } |
56 | | |
57 | | /* /dev/urandom */ |
58 | | #if !defined(_WIN32) && defined(USE_URANDOM) |
59 | 1 | static int seed_from_urandom(uint32_t *seed) { |
60 | | /* Use unbuffered I/O if we have open(), close() and read(). Otherwise |
61 | | fall back to fopen() */ |
62 | | |
63 | 1 | char data[sizeof(uint32_t)]; |
64 | 1 | int ok; |
65 | | |
66 | 1 | #if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ) |
67 | 1 | int urandom; |
68 | 1 | urandom = open("/dev/urandom", O_RDONLY); |
69 | 1 | if (urandom == -1) |
70 | 0 | return 1; |
71 | | |
72 | 1 | ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t); |
73 | 1 | close(urandom); |
74 | | #else |
75 | | FILE *urandom; |
76 | | |
77 | | urandom = fopen("/dev/urandom", "rb"); |
78 | | if (!urandom) |
79 | | return 1; |
80 | | |
81 | | ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t); |
82 | | fclose(urandom); |
83 | | #endif |
84 | | |
85 | 1 | if (!ok) |
86 | 0 | return 1; |
87 | | |
88 | 1 | *seed = buf_to_uint32(data); |
89 | 1 | return 0; |
90 | 1 | } |
91 | | #endif |
92 | | |
93 | | /* Windows Crypto API */ |
94 | | #if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) |
95 | | #include <wincrypt.h> |
96 | | |
97 | | typedef BOOL(WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, |
98 | | LPCSTR pszProvider, DWORD dwProvType, |
99 | | DWORD dwFlags); |
100 | | typedef BOOL(WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer); |
101 | | typedef BOOL(WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags); |
102 | | |
103 | | static int seed_from_windows_cryptoapi(uint32_t *seed) { |
104 | | HINSTANCE hAdvAPI32 = NULL; |
105 | | CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL; |
106 | | CRYPTGENRANDOM pCryptGenRandom = NULL; |
107 | | CRYPTRELEASECONTEXT pCryptReleaseContext = NULL; |
108 | | HCRYPTPROV hCryptProv = 0; |
109 | | BYTE data[sizeof(uint32_t)]; |
110 | | int ok; |
111 | | |
112 | | hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll")); |
113 | | if (hAdvAPI32 == NULL) |
114 | | return 1; |
115 | | |
116 | | pCryptAcquireContext = |
117 | | (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA"); |
118 | | if (!pCryptAcquireContext) |
119 | | return 1; |
120 | | |
121 | | pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom"); |
122 | | if (!pCryptGenRandom) |
123 | | return 1; |
124 | | |
125 | | pCryptReleaseContext = |
126 | | (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext"); |
127 | | if (!pCryptReleaseContext) |
128 | | return 1; |
129 | | |
130 | | if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, |
131 | | CRYPT_VERIFYCONTEXT)) |
132 | | return 1; |
133 | | |
134 | | ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data); |
135 | | pCryptReleaseContext(hCryptProv, 0); |
136 | | |
137 | | if (!ok) |
138 | | return 1; |
139 | | |
140 | | *seed = buf_to_uint32((char *)data); |
141 | | return 0; |
142 | | } |
143 | | #endif |
144 | | |
145 | | /* gettimeofday() and getpid() */ |
146 | 0 | static int seed_from_timestamp_and_pid(uint32_t *seed) { |
147 | 0 | #ifdef HAVE_GETTIMEOFDAY |
148 | | /* XOR of seconds and microseconds */ |
149 | 0 | struct timeval tv; |
150 | 0 | gettimeofday(&tv, NULL); |
151 | 0 | *seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec; |
152 | | #else |
153 | | /* Seconds only */ |
154 | | *seed = (uint32_t)time(NULL); |
155 | | #endif |
156 | | |
157 | | /* XOR with PID for more randomness */ |
158 | | #if defined(_WIN32) |
159 | | *seed ^= (uint32_t)GetCurrentProcessId(); |
160 | | #elif defined(HAVE_GETPID) |
161 | | *seed ^= (uint32_t)getpid(); |
162 | 0 | #endif |
163 | |
|
164 | 0 | return 0; |
165 | 0 | } |
166 | | |
167 | 1 | static uint32_t generate_seed() { |
168 | 1 | uint32_t seed = 0; |
169 | 1 | int done = 0; |
170 | | |
171 | 1 | #if !defined(_WIN32) && defined(USE_URANDOM) |
172 | 1 | if (seed_from_urandom(&seed) == 0) |
173 | 1 | done = 1; |
174 | 1 | #endif |
175 | | |
176 | | #if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) |
177 | | if (seed_from_windows_cryptoapi(&seed) == 0) |
178 | | done = 1; |
179 | | #endif |
180 | | |
181 | 1 | if (!done) { |
182 | | /* Fall back to timestamp and PID if no better randomness is |
183 | | available */ |
184 | 0 | seed_from_timestamp_and_pid(&seed); |
185 | 0 | } |
186 | | |
187 | | /* Make sure the seed is never zero */ |
188 | 1 | if (seed == 0) |
189 | 0 | seed = 1; |
190 | | |
191 | 1 | return seed; |
192 | 1 | } |
193 | | |
194 | | volatile uint32_t hashtable_seed = 0; |
195 | | |
196 | | #if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) |
197 | | static volatile char seed_initialized = 0; |
198 | | |
199 | 1 | void json_object_seed(size_t seed) { |
200 | 1 | uint32_t new_seed = (uint32_t)seed; |
201 | | |
202 | 1 | if (hashtable_seed == 0) { |
203 | 1 | if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) { |
204 | | /* Do the seeding ourselves */ |
205 | 1 | if (new_seed == 0) |
206 | 1 | new_seed = generate_seed(); |
207 | | |
208 | 1 | __atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE); |
209 | 1 | } else { |
210 | | /* Wait for another thread to do the seeding */ |
211 | 0 | do { |
212 | 0 | #ifdef HAVE_SCHED_YIELD |
213 | 0 | sched_yield(); |
214 | 0 | #endif |
215 | 0 | } while (__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0); |
216 | 0 | } |
217 | 1 | } |
218 | 1 | } |
219 | | #elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32)) |
220 | | void json_object_seed(size_t seed) { |
221 | | uint32_t new_seed = (uint32_t)seed; |
222 | | |
223 | | if (hashtable_seed == 0) { |
224 | | if (new_seed == 0) { |
225 | | /* Explicit synchronization fences are not supported by the |
226 | | __sync builtins, so every thread getting here has to |
227 | | generate the seed value. |
228 | | */ |
229 | | new_seed = generate_seed(); |
230 | | } |
231 | | |
232 | | do { |
233 | | if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) { |
234 | | /* We were the first to seed */ |
235 | | break; |
236 | | } else { |
237 | | /* Wait for another thread to do the seeding */ |
238 | | #ifdef HAVE_SCHED_YIELD |
239 | | sched_yield(); |
240 | | #endif |
241 | | } |
242 | | } while (hashtable_seed == 0); |
243 | | } |
244 | | } |
245 | | #elif defined(_WIN32) |
246 | | static long seed_initialized = 0; |
247 | | void json_object_seed(size_t seed) { |
248 | | uint32_t new_seed = (uint32_t)seed; |
249 | | |
250 | | if (hashtable_seed == 0) { |
251 | | if (InterlockedIncrement(&seed_initialized) == 1) { |
252 | | /* Do the seeding ourselves */ |
253 | | if (new_seed == 0) |
254 | | new_seed = generate_seed(); |
255 | | |
256 | | hashtable_seed = new_seed; |
257 | | } else { |
258 | | /* Wait for another thread to do the seeding */ |
259 | | do { |
260 | | SwitchToThread(); |
261 | | } while (hashtable_seed == 0); |
262 | | } |
263 | | } |
264 | | } |
265 | | #else |
266 | | /* Fall back to a thread-unsafe version */ |
267 | | void json_object_seed(size_t seed) { |
268 | | uint32_t new_seed = (uint32_t)seed; |
269 | | |
270 | | if (hashtable_seed == 0) { |
271 | | if (new_seed == 0) |
272 | | new_seed = generate_seed(); |
273 | | |
274 | | hashtable_seed = new_seed; |
275 | | } |
276 | | } |
277 | | #endif |