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/engine_xoshiro256starstar.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
   | Author: Go Kudo <zeriyoshi@php.net>                                  |
12
   |                                                                      |
13
   | Based on code from: David Blackman                                   |
14
   |                     Sebastiano Vigna <vigna@acm.org>                 |
15
   +----------------------------------------------------------------------+
16
*/
17
18
#ifdef HAVE_CONFIG_H
19
# include "config.h"
20
#endif
21
22
#include "php.h"
23
#include "php_random.h"
24
#include "php_random_csprng.h"
25
26
#include "Zend/zend_exceptions.h"
27
28
static inline uint64_t splitmix64(uint64_t *seed)
29
0
{
30
0
  uint64_t r;
31
32
0
  r = (*seed += 0x9e3779b97f4a7c15ULL);
33
0
  r = (r ^ (r >> 30)) * 0xbf58476d1ce4e5b9ULL;
34
0
  r = (r ^ (r >> 27)) * 0x94d049bb133111ebULL;
35
0
  return (r ^ (r >> 31));
36
0
}
37
38
ZEND_ATTRIBUTE_CONST static inline uint64_t rotl(const uint64_t x, int k)
39
0
{
40
0
  return (x << k) | (x >> (64 - k));
41
0
}
42
43
static inline uint64_t generate_state(php_random_status_state_xoshiro256starstar *s)
44
0
{
45
0
  const uint64_t r = rotl(s->state[1] * 5, 7) * 9;
46
0
  const uint64_t t = s->state[1] << 17;
47
48
0
  s->state[2] ^= s->state[0];
49
0
  s->state[3] ^= s->state[1];
50
0
  s->state[1] ^= s->state[2];
51
0
  s->state[0] ^= s->state[3];
52
53
0
  s->state[2] ^= t;
54
55
0
  s->state[3] = rotl(s->state[3], 45);
56
57
0
  return r;
58
0
}
59
60
static inline void jump(php_random_status_state_xoshiro256starstar *state, const uint64_t *jmp)
61
0
{
62
0
  uint64_t s0 = 0, s1 = 0, s2 = 0, s3 = 0;
63
64
0
  for (uint32_t i = 0; i < 4; i++) {
65
0
    for (uint32_t j = 0; j < 64; j++) {
66
0
      if (jmp[i] & 1ULL << j) {
67
0
        s0 ^= state->state[0];
68
0
        s1 ^= state->state[1];
69
0
        s2 ^= state->state[2];
70
0
        s3 ^= state->state[3];
71
0
      }
72
73
0
      generate_state(state);
74
0
    }
75
0
  }
76
77
0
  state->state[0] = s0;
78
0
  state->state[1] = s1;
79
0
  state->state[2] = s2;
80
0
  state->state[3] = s3;
81
0
}
82
83
PHPAPI inline void php_random_xoshiro256starstar_seed256(php_random_status_state_xoshiro256starstar *state, uint64_t s0, uint64_t s1, uint64_t s2, uint64_t s3)
84
2
{
85
2
  state->state[0] = s0;
86
2
  state->state[1] = s1;
87
2
  state->state[2] = s2;
88
2
  state->state[3] = s3;
89
2
}
90
91
PHPAPI inline void php_random_xoshiro256starstar_seed64(php_random_status_state_xoshiro256starstar *state, uint64_t seed)
92
0
{
93
0
  uint64_t s[4];
94
95
0
  s[0] = splitmix64(&seed);
96
0
  s[1] = splitmix64(&seed);
97
0
  s[2] = splitmix64(&seed);
98
0
  s[3] = splitmix64(&seed);
99
100
0
  php_random_xoshiro256starstar_seed256(state, s[0], s[1], s[2], s[3]);
101
0
}
102
103
static php_random_result generate(void *state)
104
0
{
105
0
  return (php_random_result){
106
0
    .size = sizeof(uint64_t),
107
0
    .result = generate_state(state),
108
0
  };
109
0
}
110
111
static zend_long range(void *state, zend_long min, zend_long max)
112
0
{
113
0
  return php_random_range((php_random_algo_with_state){
114
0
    .algo = &php_random_algo_xoshiro256starstar,
115
0
    .state = state,
116
0
  }, min, max);
117
0
}
118
119
static bool serialize(void *state, HashTable *data)
120
0
{
121
0
  php_random_status_state_xoshiro256starstar *s = state;
122
0
  zval t;
123
124
0
  for (uint32_t i = 0; i < 4; i++) {
125
0
    ZVAL_STR(&t, php_random_bin2hex_le(&s->state[i], sizeof(uint64_t)));
126
0
    zend_hash_next_index_insert(data, &t);
127
0
  }
128
129
0
  return true;
130
0
}
131
132
static bool unserialize(void *state, HashTable *data)
133
0
{
134
0
  php_random_status_state_xoshiro256starstar *s = state;
135
0
  zval *t;
136
137
  /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
138
0
  if (zend_hash_num_elements(data) != 4) {
139
0
    return false;
140
0
  }
141
142
0
  for (uint32_t i = 0; i < 4; i++) {
143
0
    t = zend_hash_index_find(data, i);
144
0
    if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint64_t))) {
145
0
      return false;
146
0
    }
147
0
    if (!php_random_hex2bin_le(Z_STR_P(t), &s->state[i])) {
148
0
      return false;
149
0
    }
150
0
  }
151
152
0
  if (UNEXPECTED(s->state[0] == 0 && s->state[1] == 0 && s->state[2] == 0 && s->state[3] == 0)) {
153
0
    return false;
154
0
  }
155
156
0
  return true;
157
0
}
158
159
PHPAPI const php_random_algo php_random_algo_xoshiro256starstar = {
160
  sizeof(php_random_status_state_xoshiro256starstar),
161
  generate,
162
  range,
163
  serialize,
164
  unserialize
165
};
166
167
PHPAPI void php_random_xoshiro256starstar_jump(php_random_status_state_xoshiro256starstar *state)
168
0
{
169
0
  static const uint64_t jmp[] = {0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c};
170
0
  jump(state, jmp);
171
0
}
172
173
PHPAPI void php_random_xoshiro256starstar_jump_long(php_random_status_state_xoshiro256starstar *state)
174
0
{
175
0
  static const uint64_t jmp[] = {0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635};
176
0
  jump(state, jmp);
177
0
}
178
179
/* {{{ Random\Engine\Xoshiro256StarStar::jump() */
180
PHP_METHOD(Random_Engine_Xoshiro256StarStar, jump)
181
0
{
182
0
  php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
183
0
  php_random_status_state_xoshiro256starstar *state = engine.state;
184
185
0
  ZEND_PARSE_PARAMETERS_NONE();
186
187
0
  php_random_xoshiro256starstar_jump(state);
188
0
}
189
/* }}} */
190
191
/* {{{ Random\Engine\Xoshiro256StarStar::jumpLong() */
192
PHP_METHOD(Random_Engine_Xoshiro256StarStar, jumpLong)
193
0
{
194
0
  php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
195
0
  php_random_status_state_xoshiro256starstar *state = engine.state;
196
197
0
  ZEND_PARSE_PARAMETERS_NONE();
198
199
0
  php_random_xoshiro256starstar_jump_long(state);
200
0
}
201
/* }}} */
202
203
/* {{{ Random\Engine\Xoshiro256StarStar::__construct */
204
PHP_METHOD(Random_Engine_Xoshiro256StarStar, __construct)
205
2
{
206
2
  php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
207
2
  php_random_status_state_xoshiro256starstar *state = engine.state;
208
2
  zend_string *str_seed = NULL;
209
2
  zend_long int_seed = 0;
210
2
  bool seed_is_null = true;
211
212
6
  ZEND_PARSE_PARAMETERS_START(0, 1)
213
6
    Z_PARAM_OPTIONAL;
214
6
    Z_PARAM_STR_OR_LONG_OR_NULL(str_seed, int_seed, seed_is_null);
215
4
  ZEND_PARSE_PARAMETERS_END();
216
217
2
  if (seed_is_null) {
218
2
    uint64_t t[4];
219
220
2
    do {
221
2
      if (php_random_bytes_throw(&t, sizeof(t)) == FAILURE) {
222
0
        zend_throw_exception(random_ce_Random_RandomException, "Failed to generate a random seed", 0);
223
0
        RETURN_THROWS();
224
0
      }
225
2
    } while (UNEXPECTED(t[0] == 0 && t[1] == 0 && t[2] == 0 && t[3] == 0));
226
227
2
    php_random_xoshiro256starstar_seed256(state, t[0], t[1], t[2], t[3]);
228
2
  } else {
229
0
    if (str_seed) {
230
      /* char (byte: 8 bit) * 32 = 256 bits */
231
0
      if (ZSTR_LEN(str_seed) == 32) {
232
0
        uint64_t t[4];
233
234
        /* Endianness safe copy */
235
0
        for (uint32_t i = 0; i < 4; i++) {
236
0
          t[i] = 0;
237
0
          for (uint32_t j = 0; j < 8; j++) {
238
0
            t[i] += ((uint64_t) (unsigned char) ZSTR_VAL(str_seed)[(i * 8) + j]) << (j * 8);
239
0
          }
240
0
        }
241
242
0
        if (UNEXPECTED(t[0] == 0 && t[1] == 0 && t[2] == 0 && t[3] == 0)) {
243
0
          zend_argument_value_error(1, "must not consist entirely of NUL bytes");
244
0
          RETURN_THROWS();
245
0
        }
246
247
0
        php_random_xoshiro256starstar_seed256(state, t[0], t[1], t[2], t[3]);
248
0
      } else {
249
0
        zend_argument_value_error(1, "must be a 32 byte (256 bit) string");
250
0
        RETURN_THROWS();
251
0
      }
252
0
    } else {
253
0
      php_random_xoshiro256starstar_seed64(state, (uint64_t) int_seed);
254
0
    }
255
0
  }
256
2
}
257
/* }}} */