Coverage Report

Created: 2026-09-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/random/random.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Sammy Kaye Powers <me@sammyk.me>                            |
12
   |          Go Kudo <zeriyoshi@php.net>                                 |
13
   |          Tim Düsterhus <timwolla@php.net>                            |
14
   +----------------------------------------------------------------------+
15
*/
16
17
#ifdef HAVE_CONFIG_H
18
# include "config.h"
19
#endif
20
21
#include <stdlib.h>
22
#include <sys/stat.h>
23
#include <fcntl.h>
24
#include <math.h>
25
26
#include "php.h"
27
28
#include "Zend/zend_exceptions.h"
29
30
#include "php_random.h"
31
#include "php_random_csprng.h"
32
#include "ext/standard/sha1.h"
33
34
#ifdef HAVE_UNISTD_H
35
# include <unistd.h>
36
#endif
37
38
#ifdef PHP_WIN32
39
# include "win32/time.h"
40
# include "win32/winutil.h"
41
# include <process.h>
42
#else
43
# include <sys/time.h>
44
#endif
45
46
#include "random_arginfo.h"
47
48
PHPAPI ZEND_DECLARE_MODULE_GLOBALS(random)
49
50
PHPAPI zend_class_entry *random_ce_Random_Engine;
51
PHPAPI zend_class_entry *random_ce_Random_CryptoSafeEngine;
52
53
PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937;
54
PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64;
55
PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar;
56
PHPAPI zend_class_entry *random_ce_Random_Engine_Secure;
57
58
PHPAPI zend_class_entry *random_ce_Random_Randomizer;
59
60
PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary;
61
62
PHPAPI zend_class_entry *random_ce_Random_RandomError;
63
PHPAPI zend_class_entry *random_ce_Random_BrokenRandomEngineError;
64
PHPAPI zend_class_entry *random_ce_Random_RandomException;
65
66
static zend_object_handlers random_engine_mt19937_object_handlers;
67
static zend_object_handlers random_engine_pcgoneseq128xslrr64_object_handlers;
68
static zend_object_handlers random_engine_xoshiro256starstar_object_handlers;
69
static zend_object_handlers random_engine_secure_object_handlers;
70
static zend_object_handlers random_randomizer_object_handlers;
71
72
PHPAPI uint32_t php_random_range32(php_random_algo_with_state engine, uint32_t umax)
73
3.39k
{
74
3.39k
  const php_random_algo *algo = engine.algo;
75
3.39k
  void *state = engine.state;
76
77
3.39k
  uint32_t result;
78
3.39k
  size_t total_size;
79
80
3.39k
  result = 0;
81
3.39k
  total_size = 0;
82
3.39k
  do {
83
3.39k
    php_random_result r = algo->generate(state);
84
3.39k
    result = result | (((uint32_t) r.result) << (total_size * 8));
85
3.39k
    total_size += r.size;
86
3.39k
    if (EG(exception)) {
87
0
      return 0;
88
0
    }
89
3.39k
  } while (total_size < sizeof(uint32_t));
90
91
  /* Special case where no modulus is required */
92
3.39k
  if (UNEXPECTED(umax == UINT32_MAX)) {
93
0
    return result;
94
0
  }
95
96
  /* Increment the max so range is inclusive of max */
97
3.39k
  umax++;
98
99
  /* Powers of two are not biased */
100
3.39k
  if ((umax & (umax - 1)) == 0) {
101
216
    return result & (umax - 1);
102
216
  }
103
104
  /* Ceiling under which UINT32_MAX % max == 0 */
105
3.18k
  uint32_t limit = UINT32_MAX - (UINT32_MAX % umax) - 1;
106
107
  /* Discard numbers over the limit to avoid modulo bias */
108
3.18k
  uint32_t count = 0;
109
3.18k
  while (UNEXPECTED(result > limit)) {
110
    /* If the requirements cannot be met in a cycles, return fail */
111
0
    if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
112
0
      zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
113
0
      return 0;
114
0
    }
115
116
0
    result = 0;
117
0
    total_size = 0;
118
0
    do {
119
0
      php_random_result r = algo->generate(state);
120
0
      result = result | (((uint32_t) r.result) << (total_size * 8));
121
0
      total_size += r.size;
122
0
      if (EG(exception)) {
123
0
        return 0;
124
0
      }
125
0
    } while (total_size < sizeof(uint32_t));
126
0
  }
127
128
3.18k
  return result % umax;
129
3.18k
}
130
131
PHPAPI uint64_t php_random_range64(php_random_algo_with_state engine, uint64_t umax)
132
3
{
133
3
  const php_random_algo *algo = engine.algo;
134
3
  void *state = engine.state;
135
136
3
  uint64_t result;
137
3
  size_t total_size;
138
139
3
  result = 0;
140
3
  total_size = 0;
141
6
  do {
142
6
    php_random_result r = algo->generate(state);
143
6
    result = result | (r.result << (total_size * 8));
144
6
    total_size += r.size;
145
6
    if (EG(exception)) {
146
0
      return 0;
147
0
    }
148
6
  } while (total_size < sizeof(uint64_t));
149
150
  /* Special case where no modulus is required */
151
3
  if (UNEXPECTED(umax == UINT64_MAX)) {
152
0
    return result;
153
0
  }
154
155
  /* Increment the max so range is inclusive of max */
156
3
  umax++;
157
158
  /* Powers of two are not biased */
159
3
  if ((umax & (umax - 1)) == 0) {
160
0
    return result & (umax - 1);
161
0
  }
162
163
  /* Ceiling under which UINT64_MAX % max == 0 */
164
3
  uint64_t limit = UINT64_MAX - (UINT64_MAX % umax) - 1;
165
166
  /* Discard numbers over the limit to avoid modulo bias */
167
3
  uint32_t count = 0;
168
3
  while (UNEXPECTED(result > limit)) {
169
    /* If the requirements cannot be met in a cycles, return fail */
170
0
    if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
171
0
      zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
172
0
      return 0;
173
0
    }
174
175
0
    result = 0;
176
0
    total_size = 0;
177
0
    do {
178
0
      php_random_result r = algo->generate(state);
179
0
      result = result | (r.result << (total_size * 8));
180
0
      total_size += r.size;
181
0
      if (EG(exception)) {
182
0
        return 0;
183
0
      }
184
0
    } while (total_size < sizeof(uint64_t));
185
0
  }
186
187
3
  return result % umax;
188
3
}
189
190
static zend_object *php_random_engine_mt19937_new(zend_class_entry *ce)
191
16
{
192
16
  return &php_random_engine_common_init(ce, &php_random_algo_mt19937)->std;
193
16
}
194
195
static zend_object *php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry *ce)
196
5
{
197
5
  return &php_random_engine_common_init(ce, &php_random_algo_pcgoneseq128xslrr64)->std;
198
5
}
199
200
static zend_object *php_random_engine_xoshiro256starstar_new(zend_class_entry *ce)
201
127
{
202
127
  return &php_random_engine_common_init(ce, &php_random_algo_xoshiro256starstar)->std;
203
127
}
204
205
static zend_object *php_random_engine_secure_new(zend_class_entry *ce)
206
15
{
207
15
  return &php_random_engine_common_init(ce, &php_random_algo_secure)->std;
208
15
}
209
210
static zend_object *php_random_randomizer_new(zend_class_entry *ce)
211
39
{
212
39
  php_random_randomizer *randomizer = zend_object_alloc(sizeof(php_random_randomizer), ce);
213
214
39
  zend_object_std_init(&randomizer->std, ce);
215
39
  object_properties_init(&randomizer->std, ce);
216
217
39
  return &randomizer->std;
218
39
}
219
220
39
static void randomizer_free_obj(zend_object *object) {
221
39
  php_random_randomizer *randomizer = php_random_randomizer_from_obj(object);
222
223
39
  if (randomizer->is_userland_algo) {
224
0
    php_random_status_free(randomizer->engine.state, false);
225
0
  }
226
227
39
  zend_object_std_dtor(&randomizer->std);
228
39
}
229
230
PHPAPI void *php_random_status_alloc(const php_random_algo *algo, const bool persistent)
231
163
{
232
163
  return algo->state_size > 0 ? pecalloc(1, algo->state_size, persistent) : NULL;
233
163
}
234
235
PHPAPI void *php_random_status_copy(const php_random_algo *algo, void *old_status, void *new_status)
236
7
{
237
7
  return memcpy(new_status, old_status, algo->state_size);
238
7
}
239
240
PHPAPI void php_random_status_free(void *status, const bool persistent)
241
163
{
242
163
  pefree(status, persistent);
243
163
}
244
245
PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, const php_random_algo *algo)
246
163
{
247
163
  php_random_engine *engine = zend_object_alloc(sizeof(php_random_engine), ce);
248
249
163
  zend_object_std_init(&engine->std, ce);
250
163
  object_properties_init(&engine->std, ce);
251
252
163
  engine->engine = (php_random_algo_with_state){
253
163
    .algo = algo,
254
163
    .state = php_random_status_alloc(algo, false)
255
163
  };
256
257
163
  return engine;
258
163
}
259
260
PHPAPI void php_random_engine_common_free_object(zend_object *object)
261
163
{
262
163
  php_random_engine *engine = php_random_engine_from_obj(object);
263
264
163
  php_random_status_free(engine->engine.state, false);
265
163
  zend_object_std_dtor(object);
266
163
}
267
268
PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object)
269
7
{
270
7
  const php_random_engine *old_engine = php_random_engine_from_obj(object);
271
7
  php_random_engine *new_engine = php_random_engine_from_obj(old_engine->std.ce->create_object(old_engine->std.ce));
272
273
7
  new_engine->engine.algo = old_engine->engine.algo;
274
7
  if (old_engine->engine.state) {
275
7
    new_engine->engine.state = php_random_status_copy(old_engine->engine.algo, old_engine->engine.state, new_engine->engine.state);
276
7
  }
277
278
7
  zend_objects_clone_members(&new_engine->std, &old_engine->std);
279
280
7
  return &new_engine->std;
281
7
}
282
283
/* {{{ php_random_range */
284
PHPAPI zend_long php_random_range(php_random_algo_with_state engine, zend_long min, zend_long max)
285
3.40k
{
286
3.40k
  zend_ulong umax = (zend_ulong) max - (zend_ulong) min;
287
288
3.40k
  if (umax > UINT32_MAX) {
289
3
    return (zend_long) (php_random_range64(engine, umax) + min);
290
3
  }
291
292
3.39k
  return (zend_long) (php_random_range32(engine, umax) + min);
293
3.40k
}
294
/* }}} */
295
296
/* {{{ php_random_default_algo */
297
PHPAPI const php_random_algo *php_random_default_algo(void)
298
35
{
299
35
  return &php_random_algo_mt19937;
300
35
}
301
/* }}} */
302
303
/* {{{ php_random_default_status */
304
PHPAPI void *php_random_default_status(void)
305
111
{
306
111
  php_random_status_state_mt19937 *state = &RANDOM_G(mt19937);
307
308
111
  if (!RANDOM_G(mt19937_seeded)) {
309
47
    state->mode = MT_RAND_MT19937;
310
47
    php_random_mt19937_seed_default(state);
311
47
    RANDOM_G(mt19937_seeded) = true;
312
47
  }
313
314
111
  return state;
315
111
}
316
/* }}} */
317
318
/* this is read-only, so it's ok */
319
ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef";
320
321
/* {{{ php_random_bin2hex_le */
322
/* stolen from standard/string.c */
323
PHPAPI zend_string *php_random_bin2hex_le(const void *ptr, const size_t len)
324
0
{
325
0
  zend_string *str;
326
0
  size_t i;
327
328
0
  str = zend_string_safe_alloc(len, 2 * sizeof(char), 0, 0);
329
330
0
  i = 0;
331
#ifdef WORDS_BIGENDIAN
332
  /* force little endian */
333
  for (size_t h = len; 0 < h; h--) {
334
    size_t j = h-1;
335
#else
336
0
  for (size_t j = 0; j < len; j++) {
337
0
#endif
338
0
    ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] >> 4];
339
0
    ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] & 15];
340
0
  }
341
0
  ZSTR_VAL(str)[i] = '\0';
342
343
0
  return str;
344
0
}
345
/* }}} */
346
347
/* {{{ php_random_hex2bin_le */
348
/* stolen from standard/string.c */
349
PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest)
350
0
{
351
0
  size_t len = hexstr->len >> 1;
352
0
  unsigned char *str = (unsigned char *) hexstr->val, c, l, d;
353
0
  unsigned char *ptr = (unsigned char *) dest;
354
0
  int is_letter, i = 0;
355
356
#ifdef WORDS_BIGENDIAN
357
  /* force little endian */
358
  for (size_t h = len; 0 < h; h--) {
359
    size_t j = h-1;
360
#else
361
0
  for (size_t j = 0; j < len; j++) {
362
0
#endif
363
0
    c = str[i++];
364
0
    l = c & ~0x20;
365
0
    is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
366
367
    /* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
368
0
    if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
369
0
      d = (l - 0x10 - 0x27 * is_letter) << 4;
370
0
    } else {
371
0
      return false;
372
0
    }
373
0
    c = str[i++];
374
0
    l = c & ~0x20;
375
0
    is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
376
0
    if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
377
0
      d |= l - 0x10 - 0x27 * is_letter;
378
0
    } else {
379
0
      return false;
380
0
    }
381
0
    ptr[j] = d;
382
0
  }
383
0
  return true;
384
0
}
385
/* }}} */
386
387
/* {{{ php_combined_lcg */
388
PHPAPI double php_combined_lcg(void)
389
0
{
390
0
  int32_t *state = RANDOM_G(combined_lcg);
391
392
0
  if (!RANDOM_G(combined_lcg_seeded)) {
393
0
    uint64_t seed = 0;
394
395
0
    if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
396
0
      seed = php_random_generate_fallback_seed();
397
0
    }
398
399
0
    state[0] = seed & 0xffffffffU;
400
0
    state[1] = seed >> 32;
401
0
    RANDOM_G(combined_lcg_seeded) = true;
402
0
  }
403
404
  /*
405
   * combinedLCG() returns a pseudo random number in the range of (0, 1).
406
   * The function combines two CGs with periods of
407
   * 2^31 - 85 - 1 and 2^31 - 249 - 1. The period of this function
408
   * is equal to the product of the two underlying periods, divided
409
   * by factors shared by the underlying periods, i.e. 2.3 * 10^18.
410
   *
411
   * see: https://library.sciencemadness.org/lanl1_a/lib-www/numerica/f7-1.pdf
412
   */
413
0
#define PHP_COMBINED_LCG_MODMULT(a, b, c, m, s) q = s / a; s = b * (s - a * q) - c * q; if (s < 0) s += m
414
415
0
  int32_t q, z;
416
417
  /* state[0] = (state[0] * 40014) % 2147483563; */
418
0
  PHP_COMBINED_LCG_MODMULT(53668, 40014, 12211, 2147483563L, state[0]);
419
  /* state[1] = (state[1] * 40692) % 2147483399; */
420
0
  PHP_COMBINED_LCG_MODMULT(52774, 40692, 3791, 2147483399L, state[1]);
421
422
0
  z = state[0] - state[1];
423
0
  if (z < 1) {
424
0
    z += 2147483562;
425
0
  }
426
427
0
  return ((uint64_t)z) * 4.656613e-10;
428
0
}
429
/* }}} */
430
431
/* {{{ php_mt_srand */
432
PHPAPI void php_mt_srand(uint32_t seed)
433
0
{
434
0
  php_random_mt19937_seed32(php_random_default_status(), seed);
435
0
}
436
/* }}} */
437
438
/* {{{ php_mt_rand */
439
PHPAPI uint32_t php_mt_rand(void)
440
0
{
441
0
  return (uint32_t) php_random_algo_mt19937.generate(php_random_default_status()).result;
442
0
}
443
/* }}} */
444
445
/* {{{ php_mt_rand_range */
446
PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max)
447
38
{
448
38
  return php_random_algo_mt19937.range(php_random_default_status(), min, max);
449
38
}
450
/* }}} */
451
452
/* {{{ php_mt_rand_common
453
 * rand() allows min > max, mt_rand does not */
454
PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max)
455
38
{
456
38
  php_random_status_state_mt19937 *s = php_random_default_status();
457
458
38
  if (s->mode == MT_RAND_MT19937) {
459
38
    return php_mt_rand_range(min, max);
460
38
  }
461
462
0
  uint64_t r = php_random_algo_mt19937.generate(php_random_default_status()).result >> 1;
463
464
  /* This is an inlined version of the RAND_RANGE_BADSCALING macro that does not invoke UB when encountering
465
   * (max - min) > ZEND_LONG_MAX.
466
   */
467
0
  zend_ulong offset = (double) ( (double) max - min + 1.0) * (r / (PHP_MT_RAND_MAX + 1.0));
468
469
0
  return (zend_long) (offset + min);
470
38
}
471
/* }}} */
472
473
/* {{{ Returns a value from the combined linear congruential generator */
474
PHP_FUNCTION(lcg_value)
475
0
{
476
0
  ZEND_PARSE_PARAMETERS_NONE();
477
478
0
  RETURN_DOUBLE(php_combined_lcg());
479
0
}
480
/* }}} */
481
482
/* {{{ Seeds Mersenne Twister random number generator */
483
PHP_FUNCTION(mt_srand)
484
3
{
485
3
  zend_long seed = 0;
486
3
  bool seed_is_null = true;
487
3
  zend_long mode = MT_RAND_MT19937;
488
3
  php_random_status_state_mt19937 *state = &RANDOM_G(mt19937);
489
490
9
  ZEND_PARSE_PARAMETERS_START(0, 2)
491
9
    Z_PARAM_OPTIONAL
492
12
    Z_PARAM_LONG_OR_NULL(seed, seed_is_null)
493
9
    Z_PARAM_LONG(mode)
494
3
  ZEND_PARSE_PARAMETERS_END();
495
496
3
  switch (mode) {
497
0
  case MT_RAND_PHP:
498
0
    state->mode = MT_RAND_PHP;
499
0
    zend_error(E_DEPRECATED, "The MT_RAND_PHP variant of Mt19937 is deprecated");
500
0
    break;
501
3
  default:
502
3
    state->mode = MT_RAND_MT19937;
503
3
  }
504
505
3
  if (seed_is_null) {
506
0
    php_random_mt19937_seed_default(state);
507
3
  } else {
508
3
    php_random_mt19937_seed32(state, (uint64_t) seed);
509
3
  }
510
3
  RANDOM_G(mt19937_seeded) = true;
511
3
}
512
/* }}} */
513
514
/* {{{ Returns a random number from Mersenne Twister */
515
PHP_FUNCTION(mt_rand)
516
3
{
517
3
  zend_long min, max;
518
3
  int argc = ZEND_NUM_ARGS();
519
520
3
  if (argc == 0) {
521
    /* genrand_int31 in mt19937ar.c performs a right shift */
522
0
    RETURN_LONG(php_mt_rand() >> 1);
523
0
  }
524
525
9
  ZEND_PARSE_PARAMETERS_START(2, 2)
526
12
    Z_PARAM_LONG(min)
527
15
    Z_PARAM_LONG(max)
528
3
  ZEND_PARSE_PARAMETERS_END();
529
530
3
  if (UNEXPECTED(max < min)) {
531
0
    zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
532
0
    RETURN_THROWS();
533
0
  }
534
535
3
  RETURN_LONG(php_mt_rand_common(min, max));
536
3
}
537
/* }}} */
538
539
/* {{{ Returns the maximum value a random number from Mersenne Twister can have */
540
PHP_FUNCTION(mt_getrandmax)
541
0
{
542
0
  ZEND_PARSE_PARAMETERS_NONE();
543
544
  /*
545
   * Melo: it could be 2^^32, but we only use 2^^31 to maintain
546
   * compatibility with the previous php_rand
547
   */
548
0
  RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
549
0
}
550
/* }}} */
551
552
/* {{{ Returns a random number from Mersenne Twister */
553
PHP_FUNCTION(rand)
554
37
{
555
37
  zend_long min, max;
556
37
  int argc = ZEND_NUM_ARGS();
557
558
37
  if (argc == 0) {
559
    /* genrand_int31 in mt19937ar.c performs a right shift */
560
0
    RETURN_LONG(php_mt_rand() >> 1);
561
0
  }
562
563
109
  ZEND_PARSE_PARAMETERS_START(2, 2)
564
140
    Z_PARAM_LONG(min)
565
175
    Z_PARAM_LONG(max)
566
37
  ZEND_PARSE_PARAMETERS_END();
567
568
35
  if (max < min) {
569
3
    RETURN_LONG(php_mt_rand_common(max, min));
570
3
  }
571
572
32
  RETURN_LONG(php_mt_rand_common(min, max));
573
32
}
574
/* }}} */
575
576
/* {{{ Return an arbitrary length of pseudo-random bytes as binary string */
577
PHP_FUNCTION(random_bytes)
578
0
{
579
0
  zend_long size;
580
0
  zend_string *bytes;
581
582
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
583
0
    Z_PARAM_LONG(size)
584
0
  ZEND_PARSE_PARAMETERS_END();
585
586
0
  if (size < 1) {
587
0
    zend_argument_value_error(1, "must be greater than 0");
588
0
    RETURN_THROWS();
589
0
  }
590
591
0
  bytes = zend_string_alloc(size, 0);
592
593
0
  if (php_random_bytes_throw(ZSTR_VAL(bytes), size) == FAILURE) {
594
0
    zend_string_release_ex(bytes, 0);
595
0
    RETURN_THROWS();
596
0
  }
597
598
0
  ZSTR_VAL(bytes)[size] = '\0';
599
600
0
  RETURN_STR(bytes);
601
0
}
602
/* }}} */
603
604
/* {{{ Return an arbitrary pseudo-random integer */
605
PHP_FUNCTION(random_int)
606
84
{
607
84
  zend_long min, max, result;
608
609
249
  ZEND_PARSE_PARAMETERS_START(2, 2)
610
324
    Z_PARAM_LONG(min)
611
390
    Z_PARAM_LONG(max)
612
84
  ZEND_PARSE_PARAMETERS_END();
613
614
75
  if (min > max) {
615
6
    zend_argument_value_error(1, "must be less than or equal to argument #2 ($max)");
616
6
    RETURN_THROWS();
617
6
  }
618
619
69
  if (php_random_int_throw(min, max, &result) == FAILURE) {
620
0
    RETURN_THROWS();
621
0
  }
622
623
69
  RETURN_LONG(result);
624
69
}
625
/* }}} */
626
627
0
static inline void fallback_seed_add(PHP_SHA1_CTX *c, void *p, size_t l){
628
  /* Wrapper around PHP_SHA1Update allowing to pass
629
   * arbitrary pointers without (unsigned char*) casts
630
   * everywhere.
631
   */
632
0
  PHP_SHA1Update(c, p, l);
633
0
}
634
635
PHPAPI uint64_t php_random_generate_fallback_seed_ex(php_random_fallback_seed_state *state)
636
0
{
637
  /* Mix various values using SHA-1 as a PRF to obtain as
638
   * much entropy as possible, hopefully generating an
639
   * unpredictable and independent uint64_t. Nevertheless,
640
   * the output of this function MUST NOT be treated as
641
   * being cryptographically safe.
642
   */
643
0
  PHP_SHA1_CTX c;
644
0
  struct timeval tv;
645
0
  void *pointer;
646
0
  pid_t pid;
647
#ifdef ZTS
648
  THREAD_T tid;
649
#endif
650
0
  char buf[64 + 1];
651
652
0
  PHP_SHA1Init(&c);
653
0
  if (!state->initialized) {
654
    /* Current time. */
655
0
    gettimeofday(&tv, NULL);
656
0
    fallback_seed_add(&c, &tv, sizeof(tv));
657
    /* Various PIDs. */
658
0
    pid = getpid();
659
0
    fallback_seed_add(&c, &pid, sizeof(pid));
660
0
#ifndef PHP_WIN32
661
0
    pid = getppid();
662
0
    fallback_seed_add(&c, &pid, sizeof(pid));
663
0
#endif
664
#ifdef ZTS
665
    tid = tsrm_thread_id();
666
    fallback_seed_add(&c, &tid, sizeof(tid));
667
#endif
668
    /* Pointer values to benefit from ASLR. */
669
0
    pointer = &state;
670
0
    fallback_seed_add(&c, &pointer, sizeof(pointer));
671
0
    pointer = &c;
672
0
    fallback_seed_add(&c, &pointer, sizeof(pointer));
673
    /* Updated time. */
674
0
    gettimeofday(&tv, NULL);
675
0
    fallback_seed_add(&c, &tv, sizeof(tv));
676
    /* Hostname. */
677
0
    memset(buf, 0, sizeof(buf));
678
0
    if (gethostname(buf, sizeof(buf) - 1) == 0) {
679
0
      fallback_seed_add(&c, buf, strlen(buf));
680
0
    }
681
    /* CSPRNG. */
682
0
    if (php_random_bytes_silent(buf, 16) == SUCCESS) {
683
0
      fallback_seed_add(&c, buf, 16);
684
0
    }
685
    /* Updated time. */
686
0
    gettimeofday(&tv, NULL);
687
0
    fallback_seed_add(&c, &tv, sizeof(tv));
688
0
  } else {
689
    /* Current time. */
690
0
    gettimeofday(&tv, NULL);
691
0
    fallback_seed_add(&c, &tv, sizeof(tv));
692
    /* Previous state. */
693
0
    fallback_seed_add(&c, state->seed, 20);
694
0
  }
695
0
  PHP_SHA1Final(state->seed, &c);
696
0
  state->initialized = true;
697
698
0
  uint64_t result = 0;
699
700
0
  for (size_t i = 0; i < sizeof(result); i++) {
701
0
    result = result | (((uint64_t)state->seed[i]) << (i * 8));
702
0
  }
703
704
0
  return result;
705
0
}
706
707
PHPAPI uint64_t php_random_generate_fallback_seed(void)
708
0
{
709
0
  return php_random_generate_fallback_seed_ex(&RANDOM_G(fallback_seed_state));
710
0
}
711
712
/* {{{ PHP_GINIT_FUNCTION */
713
static PHP_GINIT_FUNCTION(random)
714
16
{
715
16
  random_globals->fallback_seed_state.initialized = false;
716
16
}
717
/* }}} */
718
719
/* {{{ PHP_MINIT_FUNCTION */
720
PHP_MINIT_FUNCTION(random)
721
16
{
722
  /* Random\Engine */
723
16
  random_ce_Random_Engine = register_class_Random_Engine();
724
725
  /* Random\CryptoSafeEngine */
726
16
  random_ce_Random_CryptoSafeEngine = register_class_Random_CryptoSafeEngine(random_ce_Random_Engine);
727
728
  /* Random\RandomError */
729
16
  random_ce_Random_RandomError = register_class_Random_RandomError(zend_ce_error);
730
731
  /* Random\BrokenRandomEngineError */
732
16
  random_ce_Random_BrokenRandomEngineError = register_class_Random_BrokenRandomEngineError(random_ce_Random_RandomError);
733
734
  /* Random\RandomException */
735
16
  random_ce_Random_RandomException = register_class_Random_RandomException(zend_ce_exception);
736
737
  /* Random\Engine\Mt19937 */
738
16
  random_ce_Random_Engine_Mt19937 = register_class_Random_Engine_Mt19937(random_ce_Random_Engine);
739
16
  random_ce_Random_Engine_Mt19937->create_object = php_random_engine_mt19937_new;
740
16
  random_ce_Random_Engine_Mt19937->default_object_handlers = &random_engine_mt19937_object_handlers;
741
16
  memcpy(&random_engine_mt19937_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
742
16
  random_engine_mt19937_object_handlers.offset = offsetof(php_random_engine, std);
743
16
  random_engine_mt19937_object_handlers.free_obj = php_random_engine_common_free_object;
744
16
  random_engine_mt19937_object_handlers.clone_obj = php_random_engine_common_clone_object;
745
746
  /* Random\Engine\PcgOnseq128XslRr64 */
747
16
  random_ce_Random_Engine_PcgOneseq128XslRr64 = register_class_Random_Engine_PcgOneseq128XslRr64(random_ce_Random_Engine);
748
16
  random_ce_Random_Engine_PcgOneseq128XslRr64->create_object = php_random_engine_pcgoneseq128xslrr64_new;
749
16
  random_ce_Random_Engine_PcgOneseq128XslRr64->default_object_handlers = &random_engine_pcgoneseq128xslrr64_object_handlers;
750
16
  memcpy(&random_engine_pcgoneseq128xslrr64_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
751
16
  random_engine_pcgoneseq128xslrr64_object_handlers.offset = offsetof(php_random_engine, std);
752
16
  random_engine_pcgoneseq128xslrr64_object_handlers.free_obj = php_random_engine_common_free_object;
753
16
  random_engine_pcgoneseq128xslrr64_object_handlers.clone_obj = php_random_engine_common_clone_object;
754
755
  /* Random\Engine\Xoshiro256StarStar */
756
16
  random_ce_Random_Engine_Xoshiro256StarStar = register_class_Random_Engine_Xoshiro256StarStar(random_ce_Random_Engine);
757
16
  random_ce_Random_Engine_Xoshiro256StarStar->create_object = php_random_engine_xoshiro256starstar_new;
758
16
  random_ce_Random_Engine_Xoshiro256StarStar->default_object_handlers = &random_engine_xoshiro256starstar_object_handlers;
759
16
  memcpy(&random_engine_xoshiro256starstar_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
760
16
  random_engine_xoshiro256starstar_object_handlers.offset = offsetof(php_random_engine, std);
761
16
  random_engine_xoshiro256starstar_object_handlers.free_obj = php_random_engine_common_free_object;
762
16
  random_engine_xoshiro256starstar_object_handlers.clone_obj = php_random_engine_common_clone_object;
763
764
  /* Random\Engine\Secure */
765
16
  random_ce_Random_Engine_Secure = register_class_Random_Engine_Secure(random_ce_Random_CryptoSafeEngine);
766
16
  random_ce_Random_Engine_Secure->create_object = php_random_engine_secure_new;
767
16
  random_ce_Random_Engine_Secure->default_object_handlers = &random_engine_secure_object_handlers;
768
16
  memcpy(&random_engine_secure_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
769
16
  random_engine_secure_object_handlers.offset = offsetof(php_random_engine, std);
770
16
  random_engine_secure_object_handlers.free_obj = php_random_engine_common_free_object;
771
16
  random_engine_secure_object_handlers.clone_obj = NULL;
772
773
  /* Random\Randomizer */
774
16
  random_ce_Random_Randomizer = register_class_Random_Randomizer();
775
16
  random_ce_Random_Randomizer->create_object = php_random_randomizer_new;
776
16
  random_ce_Random_Randomizer->default_object_handlers = &random_randomizer_object_handlers;
777
16
  memcpy(&random_randomizer_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
778
16
  random_randomizer_object_handlers.offset = offsetof(php_random_randomizer, std);
779
16
  random_randomizer_object_handlers.free_obj = randomizer_free_obj;
780
16
  random_randomizer_object_handlers.clone_obj = NULL;
781
782
  /* Random\IntervalBoundary */
783
16
  random_ce_Random_IntervalBoundary = register_class_Random_IntervalBoundary();
784
785
16
  register_random_symbols(module_number);
786
787
16
  return SUCCESS;
788
16
}
789
/* }}} */
790
791
/* {{{ PHP_MSHUTDOWN_FUNCTION */
792
PHP_MSHUTDOWN_FUNCTION(random)
793
0
{
794
0
  php_random_csprng_shutdown();
795
796
0
  return SUCCESS;
797
0
}
798
/* }}} */
799
800
/* {{{ PHP_RINIT_FUNCTION */
801
PHP_RINIT_FUNCTION(random)
802
295k
{
803
295k
  RANDOM_G(combined_lcg_seeded) = false;
804
295k
  RANDOM_G(mt19937_seeded) = false;
805
806
295k
  return SUCCESS;
807
295k
}
808
/* }}} */
809
810
/* {{{ random_module_entry */
811
zend_module_entry random_module_entry = {
812
  STANDARD_MODULE_HEADER,
813
  "random",         /* Extension name */
814
  ext_functions,        /* zend_function_entry */
815
  PHP_MINIT(random),      /* PHP_MINIT - Module initialization */
816
  PHP_MSHUTDOWN(random),    /* PHP_MSHUTDOWN - Module shutdown */
817
  PHP_RINIT(random),      /* PHP_RINIT - Request initialization */
818
  NULL,           /* PHP_RSHUTDOWN - Request shutdown */
819
  NULL,           /* PHP_MINFO - Module info */
820
  PHP_VERSION,        /* Version */
821
  PHP_MODULE_GLOBALS(random), /* ZTS Module globals */
822
  PHP_GINIT(random),      /* PHP_GINIT - Global initialization */
823
  NULL,           /* PHP_GSHUTDOWN - Global shutdown */
824
  NULL,           /* Post deactivate */
825
  STANDARD_MODULE_PROPERTIES_EX
826
};
827
/* }}} */