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 
buf_to_uint32(char * data)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)
seed_from_urandom(uint32_t * seed)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 
seed_from_windows_cryptoapi(uint32_t * seed)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() */
seed_from_timestamp_and_pid(uint32_t * seed)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 
generate_seed()166 static uint32_t generate_seed() {
167     uint32_t seed;
168     int done = 0;
169 
170 #if !defined(_WIN32) && defined(USE_URANDOM)
171     if (seed_from_urandom(&seed) == 0)
172         done = 1;
173 #endif
174 
175 #if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
176     if (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 
json_object_seed(size_t seed)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))
json_object_seed(size_t seed)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;
json_object_seed(size_t seed)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 */
json_object_seed(size_t seed)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
278