Coverage Report

Created: 2025-06-13 06:43

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