Coverage Report

Created: 2025-12-05 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/c-ares/src/lib/util/ares_rand.c
Line
Count
Source
1
/* MIT License
2
 *
3
 * Copyright (c) 2023 Brad House
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining a copy
6
 * of this software and associated documentation files (the "Software"), to deal
7
 * in the Software without restriction, including without limitation the rights
8
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
 * copies of the Software, and to permit persons to whom the Software is
10
 * furnished to do so, subject to the following conditions:
11
 *
12
 * The above copyright notice and this permission notice (including the next
13
 * paragraph) shall be included in all copies or substantial portions of the
14
 * Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 *
24
 * SPDX-License-Identifier: MIT
25
 */
26
27
#include "ares_private.h"
28
#include <stdlib.h>
29
30
/* Older MacOS versions require including AvailabilityMacros.h before
31
 * sys/random.h */
32
#ifdef HAVE_AVAILABILITYMACROS_H
33
#  include <AvailabilityMacros.h>
34
#endif
35
36
#ifdef HAVE_SYS_RANDOM_H
37
#  include <sys/random.h>
38
#endif
39
40
41
typedef enum {
42
  ARES_RAND_OS   = 1 << 0, /* OS-provided such as RtlGenRandom or arc4random */
43
  ARES_RAND_FILE = 1 << 1, /* OS file-backed random number generator */
44
  ARES_RAND_RC4  = 1 << 2  /* Internal RC4 based PRNG */
45
} ares_rand_backend;
46
47
4.18k
#define ARES_RC4_KEY_LEN 32 /* 256 bits */
48
49
typedef struct ares_rand_rc4 {
50
  unsigned char S[256];
51
  size_t        i;
52
  size_t        j;
53
} ares_rand_rc4;
54
55
static unsigned int ares_u32_from_ptr(void *addr)
56
0
{
57
0
  /* LCOV_EXCL_START: FallbackCode */
58
0
  if (ares_is_64bit()) {
59
0
    return (unsigned int)((((ares_uint64_t)addr >> 32) & 0xFFFFFFFF) ^
60
0
                          ((ares_uint64_t)addr & 0xFFFFFFFF));
61
0
  }
62
0
  return (unsigned int)((size_t)addr & 0xFFFFFFFF);
63
0
  /* LCOV_EXCL_STOP */
64
0
}
65
66
/* initialize an rc4 key as the last possible fallback. */
67
static void ares_rc4_generate_key(ares_rand_rc4 *rc4_state, unsigned char *key,
68
                                  size_t key_len)
69
4.18k
{
70
  /* LCOV_EXCL_START: FallbackCode */
71
4.18k
  size_t         i;
72
4.18k
  size_t         len = 0;
73
4.18k
  unsigned int   data;
74
4.18k
  ares_timeval_t tv;
75
76
4.18k
  if (key_len != ARES_RC4_KEY_LEN) {
77
0
    return;
78
0
  }
79
80
4.18k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
81
  /* For fuzzing, random should be deterministic */
82
4.18k
  srand(0);
83
#else
84
  /* Randomness is hard to come by.  Maybe the system randomizes heap and stack
85
   * addresses. Maybe the current timestamp give us some randomness. Use
86
   * rc4_state (heap), &i (stack), and ares_tvnow()
87
   */
88
  data = ares_u32_from_ptr(rc4_state);
89
  memcpy(key + len, &data, sizeof(data));
90
  len += sizeof(data);
91
92
  data = ares_u32_from_ptr(&i);
93
  memcpy(key + len, &data, sizeof(data));
94
  len += sizeof(data);
95
96
  ares_tvnow(&tv);
97
  data = (unsigned int)((tv.sec ^ tv.usec) & 0xFFFFFFFF);
98
  memcpy(key + len, &data, sizeof(data));
99
  len += sizeof(data);
100
101
  srand(ares_u32_from_ptr(rc4_state) ^ ares_u32_from_ptr(&i) ^
102
        (unsigned int)((tv.sec ^ tv.usec) & 0xFFFFFFFF));
103
#endif
104
105
138k
  for (i = len; i < key_len; i++) {
106
133k
    key[i] = (unsigned char)(rand() % 256); /* LCOV_EXCL_LINE */
107
133k
  }
108
  /* LCOV_EXCL_STOP */
109
4.18k
}
110
111
#define ARES_SWAP_BYTE(a, b)           \
112
2.14M
  do {                                 \
113
2.14M
    unsigned char swapByte = *(a);     \
114
2.14M
    *(a)                   = *(b);     \
115
2.14M
    *(b)                   = swapByte; \
116
2.14M
  } while (0)
117
118
static void ares_rc4_init(ares_rand_rc4 *rc4_state)
119
4.18k
{
120
  /* LCOV_EXCL_START: FallbackCode */
121
4.18k
  unsigned char key[ARES_RC4_KEY_LEN];
122
4.18k
  size_t        i;
123
4.18k
  size_t        j;
124
125
4.18k
  ares_rc4_generate_key(rc4_state, key, sizeof(key));
126
127
1.07M
  for (i = 0; i < sizeof(rc4_state->S); i++) {
128
1.07M
    rc4_state->S[i] = i & 0xFF;
129
1.07M
  }
130
131
1.07M
  for (i = 0, j = 0; i < 256; i++) {
132
1.07M
    j = (j + rc4_state->S[i] + key[i % sizeof(key)]) % 256;
133
1.07M
    ARES_SWAP_BYTE(&rc4_state->S[i], &rc4_state->S[j]);
134
1.07M
  }
135
136
4.18k
  rc4_state->i = 0;
137
4.18k
  rc4_state->j = 0;
138
  /* LCOV_EXCL_STOP */
139
4.18k
}
140
141
/* Just outputs the key schedule, no need to XOR with any data since we have
142
 * none */
143
static void ares_rc4_prng(ares_rand_rc4 *rc4_state, unsigned char *buf,
144
                          size_t len)
145
4.18k
{
146
  /* LCOV_EXCL_START: FallbackCode */
147
4.18k
  unsigned char *S = rc4_state->S;
148
4.18k
  size_t         i = rc4_state->i;
149
4.18k
  size_t         j = rc4_state->j;
150
4.18k
  size_t         cnt;
151
152
1.07M
  for (cnt = 0; cnt < len; cnt++) {
153
1.07M
    i = (i + 1) % 256;
154
1.07M
    j = (j + S[i]) % 256;
155
156
1.07M
    ARES_SWAP_BYTE(&S[i], &S[j]);
157
1.07M
    buf[cnt] = S[(S[i] + S[j]) % 256];
158
1.07M
  }
159
160
4.18k
  rc4_state->i = i;
161
4.18k
  rc4_state->j = j;
162
  /* LCOV_EXCL_STOP */
163
4.18k
}
164
165
struct ares_rand_state {
166
  ares_rand_backend type;
167
  ares_rand_backend bad_backends;
168
169
  union {
170
    FILE         *rand_file;
171
    ares_rand_rc4 rc4;
172
  } state;
173
174
  /* Since except for RC4, random data will likely result in a syscall, lets
175
   * pre-pull 256 bytes at a time.  Every query will pull 2 bytes off this so
176
   * that means we should only need a syscall every 128 queries. 256bytes
177
   * appears to be a sweet spot that may be able to be served without
178
   * interruption */
179
  unsigned char cache[256];
180
  size_t        cache_remaining;
181
};
182
183
/* Define RtlGenRandom = SystemFunction036.  This is in advapi32.dll.  There is
184
 * no need to dynamically load this, other software used widely does not.
185
 * http://blogs.msdn.com/michael_howard/archive/2005/01/14/353379.aspx
186
 * https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
187
 */
188
#ifdef _WIN32
189
BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength);
190
#  ifndef RtlGenRandom
191
#    define RtlGenRandom(a, b) SystemFunction036(a, b)
192
#  endif
193
#endif
194
195
196
static ares_bool_t ares_init_rand_engine(ares_rand_state *state)
197
4.18k
{
198
4.18k
  state->cache_remaining = 0;
199
200
4.18k
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
201
  /* For fuzzing, random should be deterministic */
202
4.18k
  state->bad_backends |= ARES_RAND_OS | ARES_RAND_FILE;
203
4.18k
#endif
204
205
4.18k
#if defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_GETRANDOM) || defined(_WIN32)
206
4.18k
  if (!(state->bad_backends & ARES_RAND_OS)) {
207
0
    state->type = ARES_RAND_OS;
208
0
    return ARES_TRUE;
209
0
  }
210
4.18k
#endif
211
212
4.18k
#if defined(CARES_RANDOM_FILE)
213
  /* LCOV_EXCL_START: FallbackCode */
214
4.18k
  if (!(state->bad_backends & ARES_RAND_FILE)) {
215
0
    state->type            = ARES_RAND_FILE;
216
0
    state->state.rand_file = fopen(CARES_RANDOM_FILE, "rb");
217
0
    if (state->state.rand_file) {
218
0
      setvbuf(state->state.rand_file, NULL, _IONBF, 0);
219
0
      return ARES_TRUE;
220
0
    }
221
0
  }
222
  /* LCOV_EXCL_STOP */
223
224
  /* Fall-Thru on failure to RC4 */
225
4.18k
#endif
226
227
  /* LCOV_EXCL_START: FallbackCode */
228
4.18k
  state->type = ARES_RAND_RC4;
229
4.18k
  ares_rc4_init(&state->state.rc4);
230
  /* LCOV_EXCL_STOP */
231
232
  /* Currently cannot fail */
233
4.18k
  return ARES_TRUE; /* LCOV_EXCL_LINE: UntestablePath */
234
4.18k
}
235
236
ares_rand_state *ares_init_rand_state(void)
237
4.18k
{
238
4.18k
  ares_rand_state *state = NULL;
239
240
4.18k
  state = ares_malloc_zero(sizeof(*state));
241
4.18k
  if (!state) {
242
0
    return NULL;
243
0
  }
244
245
4.18k
  if (!ares_init_rand_engine(state)) {
246
0
    ares_free(state); /* LCOV_EXCL_LINE: UntestablePath */
247
0
    return NULL;      /* LCOV_EXCL_LINE: UntestablePath */
248
0
  }
249
250
4.18k
  return state;
251
4.18k
}
252
253
static void ares_clear_rand_state(ares_rand_state *state)
254
4.18k
{
255
4.18k
  if (!state) {
256
0
    return; /* LCOV_EXCL_LINE: DefensiveCoding */
257
0
  }
258
259
4.18k
  switch (state->type) {
260
0
    case ARES_RAND_OS:
261
0
      break;
262
    /* LCOV_EXCL_START: FallbackCode */
263
0
    case ARES_RAND_FILE:
264
0
      fclose(state->state.rand_file);
265
0
      break;
266
4.18k
    case ARES_RAND_RC4:
267
4.18k
      break;
268
      /* LCOV_EXCL_STOP */
269
4.18k
  }
270
4.18k
}
271
272
static void ares_reinit_rand(ares_rand_state *state)
273
0
{
274
  /* LCOV_EXCL_START: UntestablePath */
275
0
  ares_clear_rand_state(state);
276
0
  ares_init_rand_engine(state);
277
  /* LCOV_EXCL_STOP */
278
0
}
279
280
void ares_destroy_rand_state(ares_rand_state *state)
281
4.18k
{
282
4.18k
  if (!state) {
283
0
    return;
284
0
  }
285
286
4.18k
  ares_clear_rand_state(state);
287
4.18k
  ares_free(state);
288
4.18k
}
289
290
static void ares_rand_bytes_fetch(ares_rand_state *state, unsigned char *buf,
291
                                  size_t len)
292
4.18k
{
293
4.18k
  while (1) {
294
4.18k
    size_t bytes_read = 0;
295
296
4.18k
    switch (state->type) {
297
0
      case ARES_RAND_OS:
298
#ifdef _WIN32
299
        RtlGenRandom(buf, (ULONG)len);
300
        return;
301
#elif defined(HAVE_ARC4RANDOM_BUF)
302
        arc4random_buf(buf, len);
303
        return;
304
#elif defined(HAVE_GETRANDOM)
305
0
        while (1) {
306
0
          size_t  n = len - bytes_read;
307
          /* getrandom() on Linux always succeeds and is never
308
           * interrupted by a signal when requesting <= 256 bytes.
309
           */
310
0
          ssize_t rv = getrandom(buf + bytes_read, n > 256 ? 256 : n, 0);
311
0
          if (rv <= 0) {
312
            /* We need to fall back to another backend */
313
0
            if (errno == ENOSYS) {
314
0
              state->bad_backends |= ARES_RAND_OS;
315
0
              break;
316
0
            }
317
0
            continue; /* Just retry. */
318
0
          }
319
320
0
          bytes_read += (size_t)rv;
321
0
          if (bytes_read == len) {
322
0
            return;
323
0
          }
324
0
        }
325
0
        break;
326
#else
327
        /* Shouldn't be possible to be here */
328
        break;
329
#endif
330
331
        /* LCOV_EXCL_START: FallbackCode */
332
333
0
      case ARES_RAND_FILE:
334
0
        while (1) {
335
0
          size_t rv = fread(buf + bytes_read, 1, len - bytes_read,
336
0
                            state->state.rand_file);
337
0
          if (rv == 0) {
338
0
            break; /* critical error, will reinit rand state */
339
0
          }
340
341
0
          bytes_read += rv;
342
0
          if (bytes_read == len) {
343
0
            return;
344
0
          }
345
0
        }
346
0
        break;
347
348
4.18k
      case ARES_RAND_RC4:
349
4.18k
        ares_rc4_prng(&state->state.rc4, buf, len);
350
4.18k
        return;
351
352
        /* LCOV_EXCL_STOP */
353
4.18k
    }
354
355
    /* If we didn't return before we got here, that means we had a critical rand
356
     * failure and need to reinitialized */
357
0
    ares_reinit_rand(state); /* LCOV_EXCL_LINE: UntestablePath */
358
0
  }
359
4.18k
}
360
361
void ares_rand_bytes(ares_rand_state *state, unsigned char *buf, size_t len)
362
4.59k
{
363
  /* See if we need to refill the cache to serve the request, but if len is
364
   * excessive, we're not going to update our cache or serve from cache */
365
4.59k
  if (len > state->cache_remaining && len < sizeof(state->cache)) {
366
4.18k
    size_t fetch_size = sizeof(state->cache) - state->cache_remaining;
367
4.18k
    ares_rand_bytes_fetch(state, state->cache, fetch_size);
368
4.18k
    state->cache_remaining = sizeof(state->cache);
369
4.18k
  }
370
371
  /* Serve from cache */
372
4.59k
  if (len <= state->cache_remaining) {
373
4.59k
    size_t offset = sizeof(state->cache) - state->cache_remaining;
374
4.59k
    memcpy(buf, state->cache + offset, len);
375
4.59k
    state->cache_remaining -= len;
376
4.59k
    return;
377
4.59k
  }
378
379
  /* Serve direct due to excess size of request */
380
0
  ares_rand_bytes_fetch(state, buf, len);
381
0
}
382
383
unsigned short ares_generate_new_id(ares_rand_state *state)
384
0
{
385
0
  unsigned short r = 0;
386
387
0
  ares_rand_bytes(state, (unsigned char *)&r, sizeof(r));
388
0
  return r;
389
0
}