Coverage Report

Created: 2025-09-27 06:26

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