0
|
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 |
|
48 static uint32_t buf_to_uint32(char *data) { |
|
49 size_t i; |
|
50 uint32_t result = 0; |
|
51 |
|
52 for (i = 0; i < sizeof(uint32_t); i++) |
|
53 result = (result << 8) | (unsigned char)data[i]; |
|
54 |
|
55 return result; |
|
56 } |
|
57 |
|
58 |
|
59 |
|
60 /* /dev/urandom */ |
|
61 #if !defined(_WIN32) && defined(USE_URANDOM) |
|
62 static int seed_from_urandom(uint32_t *seed) { |
|
63 /* Use unbuffered I/O if we have open(), close() and read(). Otherwise |
|
64 fall back to fopen() */ |
|
65 |
|
66 char data[sizeof(uint32_t)]; |
|
67 int ok; |
|
68 |
|
69 #if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ) |
|
70 int urandom; |
|
71 urandom = open("/dev/urandom", O_RDONLY); |
|
72 if (urandom == -1) |
|
73 return 1; |
|
74 |
|
75 ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t); |
|
76 close(urandom); |
|
77 #else |
|
78 FILE *urandom; |
|
79 |
|
80 urandom = fopen("/dev/urandom", "rb"); |
|
81 if (!urandom) |
|
82 return 1; |
|
83 |
|
84 ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t); |
|
85 fclose(urandom); |
|
86 #endif |
|
87 |
|
88 if (!ok) |
|
89 return 1; |
|
90 |
|
91 *seed = buf_to_uint32(data); |
|
92 return 0; |
|
93 } |
|
94 #endif |
|
95 |
|
96 /* Windows Crypto API */ |
|
97 #if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) |
|
98 #include <wincrypt.h> |
|
99 |
|
100 typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags); |
|
101 typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer); |
|
102 typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags); |
|
103 |
|
104 static int seed_from_windows_cryptoapi(uint32_t *seed) |
|
105 { |
|
106 HINSTANCE hAdvAPI32 = NULL; |
|
107 CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL; |
|
108 CRYPTGENRANDOM pCryptGenRandom = NULL; |
|
109 CRYPTRELEASECONTEXT pCryptReleaseContext = NULL; |
|
110 HCRYPTPROV hCryptProv = 0; |
|
111 BYTE data[sizeof(uint32_t)]; |
|
112 int ok; |
|
113 |
|
114 hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll")); |
|
115 if(hAdvAPI32 == NULL) |
|
116 return 1; |
|
117 |
|
118 pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA"); |
|
119 if (!pCryptAcquireContext) |
|
120 return 1; |
|
121 |
|
122 pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom"); |
|
123 if (!pCryptGenRandom) |
|
124 return 1; |
|
125 |
|
126 pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext"); |
|
127 if (!pCryptReleaseContext) |
|
128 return 1; |
|
129 |
|
130 if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) |
|
131 return 1; |
|
132 |
|
133 ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data); |
|
134 pCryptReleaseContext(hCryptProv, 0); |
|
135 |
|
136 if (!ok) |
|
137 return 1; |
|
138 |
|
139 *seed = buf_to_uint32((char *)data); |
|
140 return 0; |
|
141 } |
|
142 #endif |
|
143 |
|
144 /* gettimeofday() and getpid() */ |
|
145 static int seed_from_timestamp_and_pid(uint32_t *seed) { |
|
146 #ifdef HAVE_GETTIMEOFDAY |
|
147 /* XOR of seconds and microseconds */ |
|
148 struct timeval tv; |
|
149 gettimeofday(&tv, NULL); |
|
150 *seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec; |
|
151 #else |
|
152 /* Seconds only */ |
|
153 *seed = (uint32_t)time(NULL); |
|
154 #endif |
|
155 |
|
156 /* XOR with PID for more randomness */ |
|
157 #if defined(_WIN32) |
|
158 *seed ^= (uint32_t)GetCurrentProcessId(); |
|
159 #elif defined(HAVE_GETPID) |
|
160 *seed ^= (uint32_t)getpid(); |
|
161 #endif |
|
162 |
|
163 return 0; |
|
164 } |
|
165 |
|
166 static uint32_t generate_seed() { |
|
167 uint32_t seed; |
|
168 int done = 0; |
|
169 |
|
170 #if !defined(_WIN32) && defined(USE_URANDOM) |
|
171 if (!done && seed_from_urandom(&seed) == 0) |
|
172 done = 1; |
|
173 #endif |
|
174 |
|
175 #if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI) |
|
176 if (!done && seed_from_windows_cryptoapi(&seed) == 0) |
|
177 done = 1; |
|
178 #endif |
|
179 |
|
180 if (!done) { |
|
181 /* Fall back to timestamp and PID if no better randomness is |
|
182 available */ |
|
183 seed_from_timestamp_and_pid(&seed); |
|
184 } |
|
185 |
|
186 /* Make sure the seed is never zero */ |
|
187 if (seed == 0) |
|
188 seed = 1; |
|
189 |
|
190 return seed; |
|
191 } |
|
192 |
|
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 void json_object_seed(size_t seed) { |
|
200 uint32_t new_seed = (uint32_t)seed; |
|
201 |
|
202 if (hashtable_seed == 0) { |
|
203 if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) { |
|
204 /* Do the seeding ourselves */ |
|
205 if (new_seed == 0) |
|
206 new_seed = generate_seed(); |
|
207 |
|
208 __atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE); |
|
209 } else { |
|
210 /* Wait for another thread to do the seeding */ |
|
211 do { |
|
212 #ifdef HAVE_SCHED_YIELD |
|
213 sched_yield(); |
|
214 #endif |
|
215 } while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0); |
|
216 } |
|
217 } |
|
218 } |
|
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 |