Coverage Report

Created: 2025-08-29 06:41

/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