Coverage Report

Created: 2026-06-02 06:36

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