Coverage Report

Created: 2026-07-25 06:39

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