Skip to content

Commit 24d561e

Browse files
committed
Add zend_general_random_bytes function
1 parent ddf18e3 commit 24d561e

File tree

7 files changed

+117
-108
lines changed

7 files changed

+117
-108
lines changed

Zend/zend.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ ZEND_API zend_string *(*zend_resolve_path)(zend_string *filename);
9595
ZEND_API zend_result (*zend_post_startup_cb)(void) = NULL;
9696
ZEND_API void (*zend_post_shutdown_cb)(void) = NULL;
9797
ZEND_API zend_result (*zend_os_csprng_random_bytes)(void *bytes, size_t size, char *errstr, size_t errstr_size) = NULL;
98+
ZEND_API zend_result (*zend_general_random_bytes)(zend_utility_general_random_state *state, void *bytes, size_t size) = NULL;
9899

99100
/* This callback must be signal handler safe! */
100101
void (*zend_on_timeout)(int seconds);
@@ -914,8 +915,9 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
914915
#endif
915916

916917
/* Set up early utility functions. zend_mm depends on
917-
* zend_os_csprng_random_bytes */
918-
zend_os_csprng_random_bytes = utility_functions->os_csprng_randomn_bytes_function;
918+
* zend_general_random_bytes */
919+
zend_os_csprng_random_bytes = utility_functions->os_csprng_random_bytes_function;
920+
zend_general_random_bytes = utility_functions->general_random_bytes_function;
919921

920922
start_memory_manager();
921923

Zend/zend.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@ struct _zend_class_entry {
234234
} info;
235235
};
236236

237+
typedef union {
238+
zend_max_align_t align;
239+
uint64_t opaque[5];
240+
} zend_utility_general_random_state;
241+
237242
typedef struct _zend_utility_functions {
238243
void (*error_function)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message);
239244
size_t (*printf_function)(const char *format, ...) ZEND_ATTRIBUTE_PTR_FORMAT(printf, 1, 2);
@@ -248,7 +253,8 @@ typedef struct _zend_utility_functions {
248253
void (*printf_to_smart_str_function)(smart_str *buf, const char *format, va_list ap);
249254
char *(*getenv_function)(const char *name, size_t name_len);
250255
zend_string *(*resolve_path_function)(zend_string *filename);
251-
zend_result (*os_csprng_randomn_bytes_function)(void *bytes, size_t size, char *errstr, size_t errstr_size);
256+
zend_result (*os_csprng_random_bytes_function)(void *bytes, size_t size, char *errstr, size_t errstr_size);
257+
zend_result (*general_random_bytes_function)(zend_utility_general_random_state *state, void *bytes, size_t size);
252258
} zend_utility_functions;
253259

254260
typedef struct _zend_utility_values {
@@ -342,6 +348,7 @@ extern void (*zend_printf_to_smart_str)(smart_str *buf, const char *format, va_l
342348
extern ZEND_API char *(*zend_getenv)(const char *name, size_t name_len);
343349
extern ZEND_API zend_string *(*zend_resolve_path)(zend_string *filename);
344350
extern ZEND_API zend_result (*zend_os_csprng_random_bytes)(void *bytes, size_t size, char *errstr, size_t errstr_size);
351+
extern ZEND_API zend_result (*zend_general_random_bytes)(zend_utility_general_random_state *state, void *bytes, size_t size);
345352

346353
/* These two callbacks are especially for opcache */
347354
extern ZEND_API zend_result (*zend_post_startup_cb)(void);

Zend/zend_alloc.c

Lines changed: 3 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,6 @@ typedef struct _zend_mm_huge_list zend_mm_huge_list;
263263

264264
static bool zend_mm_use_huge_pages = false;
265265

266-
typedef struct _zend_mm_rand_state {
267-
uint32_t state[4];
268-
} zend_mm_rand_state;
269-
270266
/*
271267
* Memory is retrieved from OS by chunks of fixed size 2MB.
272268
* Inside chunk it's managed by pages of fixed size 4096B.
@@ -342,7 +338,7 @@ struct _zend_mm_heap {
342338
HashTable *tracked_allocs;
343339
#endif
344340
pid_t pid;
345-
zend_mm_rand_state rand_state;
341+
zend_utility_general_random_state rand_state;
346342
};
347343

348344
struct _zend_mm_chunk {
@@ -2043,105 +2039,18 @@ static void zend_mm_free_huge(zend_mm_heap *heap, void *ptr ZEND_FILE_LINE_DC ZE
20432039
#endif
20442040
}
20452041

2046-
/********/
2047-
/* Rand */
2048-
/********/
2049-
2050-
/* Xoshiro256** PRNG based on implementation from Go Kudo in
2051-
* ext/random/engine_xoshiro256starstar.c, based on code from David Blackman,
2052-
* Sebastiano Vigna. */
2053-
2054-
static inline uint64_t splitmix64(uint64_t *seed)
2055-
{
2056-
uint64_t r;
2057-
2058-
r = (*seed += 0x9e3779b97f4a7c15ULL);
2059-
r = (r ^ (r >> 30)) * 0xbf58476d1ce4e5b9ULL;
2060-
r = (r ^ (r >> 27)) * 0x94d049bb133111ebULL;
2061-
return (r ^ (r >> 31));
2062-
}
2063-
2064-
ZEND_ATTRIBUTE_CONST static inline uint64_t rotl(const uint64_t x, int k)
2065-
{
2066-
return (x << k) | (x >> (64 - k));
2067-
}
2068-
2069-
static inline uint64_t zend_mm_rand_generate(zend_mm_rand_state *s)
2070-
{
2071-
const uint64_t r = rotl(s->state[1] * 5, 7) * 9;
2072-
const uint64_t t = s->state[1] << 17;
2073-
2074-
s->state[2] ^= s->state[0];
2075-
s->state[3] ^= s->state[1];
2076-
s->state[1] ^= s->state[2];
2077-
s->state[0] ^= s->state[3];
2078-
2079-
s->state[2] ^= t;
2080-
2081-
s->state[3] = rotl(s->state[3], 45);
2082-
2083-
return r;
2084-
}
2085-
2086-
static inline void zend_mm_rand_seed256(zend_mm_rand_state *state, uint64_t s0, uint64_t s1, uint64_t s2, uint64_t s3)
2087-
{
2088-
state->state[0] = s0;
2089-
state->state[1] = s1;
2090-
state->state[2] = s2;
2091-
state->state[3] = s3;
2092-
}
2093-
2094-
static inline void zend_mm_rand_seed64(zend_mm_rand_state *state, uint64_t seed)
2095-
{
2096-
uint64_t s[4];
2097-
2098-
s[0] = splitmix64(&seed);
2099-
s[1] = splitmix64(&seed);
2100-
s[2] = splitmix64(&seed);
2101-
s[3] = splitmix64(&seed);
2102-
2103-
zend_mm_rand_seed256(state, s[0], s[1], s[2], s[3]);
2104-
}
2105-
21062042
/******************/
21072043
/* Initialization */
21082044
/******************/
21092045

21102046
static zend_result zend_mm_refresh_key(zend_mm_heap *heap)
21112047
{
2112-
heap->shadow_key = (uintptr_t) zend_mm_rand_generate(&heap->rand_state);
2113-
2114-
return SUCCESS;
2048+
return zend_general_random_bytes(&heap->rand_state, &heap->shadow_key, sizeof(heap->shadow_key));
21152049
}
21162050

21172051
static zend_result zend_mm_init_key(zend_mm_heap *heap)
21182052
{
2119-
uint64_t seed[4];
2120-
char errstr[128];
2121-
if (zend_os_csprng_random_bytes(&seed, sizeof(seed), errstr, sizeof(errstr)) == SUCCESS) {
2122-
zend_mm_rand_seed256(&heap->rand_state,
2123-
seed[0], seed[1], seed[2], seed[3]);
2124-
} else {
2125-
/* Fallback to weak seed generation */
2126-
#if ZEND_MM_ERROR
2127-
fprintf(stderr, "Could not generate secure random seed: %s\n", errstr);
2128-
#endif
2129-
zend_hrtime_t nanotime = zend_hrtime();
2130-
uint64_t v = 0;
2131-
v ^= (uint64_t) nanotime;
2132-
splitmix64(&v);
2133-
v ^= (uint64_t) getpid();
2134-
splitmix64(&v);
2135-
#ifdef ZTS
2136-
THREAD_T tid = tsrm_thread_id();
2137-
uint64_t tmp = 0;
2138-
memcpy(&tmp, &tid, MIN(sizeof(tmp), sizeof(tid)));
2139-
v ^= tmp;
2140-
splitmix64(&v);
2141-
#endif
2142-
zend_mm_rand_seed64(&heap->rand_state, v);
2143-
}
2144-
2053+
memset(&heap->rand_state, 0, sizeof(heap->rand_state));
21452054
return zend_mm_refresh_key(heap);
21462055
}
21472056

Zend/zend_portability.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,4 +791,20 @@ extern "C++" {
791791
# define ZEND_STATIC_ASSERT(c, m)
792792
#endif
793793

794+
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) /* C11 */ \
795+
|| (defined(__cplusplus) && __cplusplus >= 201103L) /* C++11 */
796+
typedef max_align_t zend_max_align_t;
797+
#else
798+
typedef union {
799+
char c;
800+
short s;
801+
int i;
802+
long l;
803+
float f;
804+
double d;
805+
void *p;
806+
void (*fun)();
807+
} zend_max_align_t;
808+
#endif
809+
794810
#endif /* ZEND_PORTABILITY_H */

ext/random/php_random.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@
3737

3838
PHPAPI double php_combined_lcg(void);
3939

40+
typedef struct _php_random_fallback_seed_state php_random_fallback_seed_state;
41+
typedef struct _php_random_state_for_zend php_random_state_for_zend;
42+
4043
PHPAPI uint64_t php_random_generate_fallback_seed(void);
44+
PHPAPI uint64_t php_random_generate_fallback_seed_ex(php_random_fallback_seed_state *state);
45+
PHPAPI zend_result php_general_random_bytes_for_zend(zend_utility_general_random_state *state, void *bytes, size_t size);
4146

4247
static inline zend_long GENERATE_SEED(void)
4348
{
@@ -108,6 +113,18 @@ typedef struct _php_random_algo_with_state {
108113
void *state;
109114
} php_random_algo_with_state;
110115

116+
typedef struct _php_random_fallback_seed_state {
117+
bool initialized;
118+
unsigned char seed[20];
119+
} php_random_fallback_seed_state;
120+
121+
typedef struct _php_random_state_for_zend {
122+
bool initialized;
123+
php_random_status_state_xoshiro256starstar xoshiro256starstar_state;
124+
} php_random_state_for_zend;
125+
126+
ZEND_STATIC_ASSERT(sizeof(zend_utility_general_random_state) >= sizeof(php_random_state_for_zend), "");
127+
111128
extern PHPAPI const php_random_algo php_random_algo_combinedlcg;
112129
extern PHPAPI const php_random_algo php_random_algo_mt19937;
113130
extern PHPAPI const php_random_algo php_random_algo_pcgoneseq128xslrr64;
@@ -206,8 +223,7 @@ PHP_RINIT_FUNCTION(random);
206223
ZEND_BEGIN_MODULE_GLOBALS(random)
207224
bool combined_lcg_seeded;
208225
bool mt19937_seeded;
209-
bool fallback_seed_initialized;
210-
unsigned char fallback_seed[20];
226+
php_random_fallback_seed_state fallback_seed_state;
211227
php_random_status_state_combinedlcg combined_lcg;
212228
php_random_status_state_mt19937 mt19937;
213229
ZEND_END_MODULE_GLOBALS(random)

ext/random/random.c

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ static inline void fallback_seed_add(PHP_SHA1_CTX *c, void *p, size_t l){
622622
PHP_SHA1Update(c, p, l);
623623
}
624624

625-
uint64_t php_random_generate_fallback_seed(void)
625+
uint64_t php_random_generate_fallback_seed_ex(php_random_fallback_seed_state *state)
626626
{
627627
/* Mix various values using SHA-1 as a PRF to obtain as
628628
* much entropy as possible, hopefully generating an
@@ -640,7 +640,7 @@ uint64_t php_random_generate_fallback_seed(void)
640640
char buf[64 + 1];
641641

642642
PHP_SHA1Init(&c);
643-
if (!RANDOM_G(fallback_seed_initialized)) {
643+
if (!state->initialized) {
644644
/* Current time. */
645645
gettimeofday(&tv, NULL);
646646
fallback_seed_add(&c, &tv, sizeof(tv));
@@ -656,7 +656,7 @@ uint64_t php_random_generate_fallback_seed(void)
656656
fallback_seed_add(&c, &tid, sizeof(tid));
657657
#endif
658658
/* Pointer values to benefit from ASLR. */
659-
pointer = &RANDOM_G(fallback_seed_initialized);
659+
pointer = state;
660660
fallback_seed_add(&c, &pointer, sizeof(pointer));
661661
pointer = &c;
662662
fallback_seed_add(&c, &pointer, sizeof(pointer));
@@ -680,24 +680,82 @@ uint64_t php_random_generate_fallback_seed(void)
680680
gettimeofday(&tv, NULL);
681681
fallback_seed_add(&c, &tv, sizeof(tv));
682682
/* Previous state. */
683-
fallback_seed_add(&c, RANDOM_G(fallback_seed), 20);
683+
fallback_seed_add(&c, state->seed, 20);
684684
}
685-
PHP_SHA1Final(RANDOM_G(fallback_seed), &c);
686-
RANDOM_G(fallback_seed_initialized) = true;
685+
PHP_SHA1Final(state->seed, &c);
686+
state->initialized = true;
687687

688688
uint64_t result = 0;
689689

690690
for (int i = 0; i < sizeof(result); i++) {
691-
result = result | (((uint64_t)RANDOM_G(fallback_seed)[i]) << (i * 8));
691+
result = result | (((uint64_t)state->seed[i]) << (i * 8));
692692
}
693693

694694
return result;
695695
}
696696

697+
uint64_t php_random_generate_fallback_seed(void)
698+
{
699+
return php_random_generate_fallback_seed_ex(&RANDOM_G(fallback_seed_state));
700+
}
701+
702+
static zend_result php_general_random_bytes_for_zend_initialize(php_random_state_for_zend *state)
703+
{
704+
uint64_t t[4];
705+
706+
do {
707+
char errstr[128];
708+
if (php_random_bytes_ex(&t, sizeof(t), errstr, sizeof(errstr)) == FAILURE) {
709+
#if ZEND_DEBUG
710+
fprintf(stderr, "php_random_bytes_ex: Failed to generate a random seed: %s\n", errstr);
711+
#endif
712+
return FAILURE;
713+
}
714+
} while (UNEXPECTED(t[0] == 0 && t[1] == 0 && t[2] == 0 && t[3] == 0));
715+
716+
php_random_xoshiro256starstar_seed256(&state->xoshiro256starstar_state, t[0], t[1], t[2], t[3]);
717+
718+
return SUCCESS;
719+
}
720+
721+
static void php_general_random_bytes_for_zend_initialize_fallback(php_random_state_for_zend *state)
722+
{
723+
uint64_t t;
724+
php_random_fallback_seed_state fallback_state;
725+
726+
do {
727+
t = php_random_generate_fallback_seed_ex(&fallback_state);
728+
} while (UNEXPECTED(t == 0));
729+
730+
php_random_xoshiro256starstar_seed64(&state->xoshiro256starstar_state, t);
731+
}
732+
733+
PHPAPI zend_result php_general_random_bytes_for_zend(zend_utility_general_random_state *opaque_state, void *bytes, size_t size)
734+
{
735+
php_random_state_for_zend *state = (php_random_state_for_zend*) opaque_state;
736+
737+
if (!state->initialized) {
738+
if (php_general_random_bytes_for_zend_initialize(state) == FAILURE) {
739+
php_general_random_bytes_for_zend_initialize_fallback(state);
740+
}
741+
state->initialized = true;
742+
}
743+
744+
while (size > 0) {
745+
php_random_result result = php_random_algo_xoshiro256starstar.generate(&state->xoshiro256starstar_state);
746+
size_t chunk_size = MIN(size, sizeof(result.size));
747+
memcpy(bytes, &result.result, chunk_size);
748+
size -= chunk_size;
749+
bytes += chunk_size;
750+
}
751+
752+
return SUCCESS;
753+
}
754+
697755
/* {{{ PHP_GINIT_FUNCTION */
698756
static PHP_GINIT_FUNCTION(random)
699757
{
700-
random_globals->fallback_seed_initialized = false;
758+
random_globals->fallback_seed_state.initialized = false;
701759
}
702760
/* }}} */
703761

main/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2115,7 +2115,8 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
21152115
zuf.printf_to_smart_str_function = php_printf_to_smart_str;
21162116
zuf.getenv_function = sapi_getenv;
21172117
zuf.resolve_path_function = php_resolve_path_for_zend;
2118-
zuf.os_csprng_randomn_bytes_function = php_random_bytes_ex;
2118+
zuf.os_csprng_random_bytes_function = php_random_bytes_ex;
2119+
zuf.general_random_bytes_function = php_general_random_bytes_for_zend;
21192120
zend_startup(&zuf);
21202121
zend_reset_lc_ctype_locale();
21212122
zend_update_current_locale();

0 commit comments

Comments
 (0)