Skip to content

Commit d1048a0

Browse files
arnaud-lbTimWolla
andcommitted
Add zend_random_bytes(), zend_random_bytes_insecure() functions (#14054)
Co-authored-by: Tim Düsterhus <[email protected]>
1 parent d545b1d commit d1048a0

File tree

10 files changed

+169
-12
lines changed

10 files changed

+169
-12
lines changed

Zend/zend.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ ZEND_API char *(*zend_getenv)(const char *name, size_t name_len);
9494
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;
97+
ZEND_ATTRIBUTE_NONNULL ZEND_API zend_result (*zend_random_bytes)(void *bytes, size_t size, char *errstr, size_t errstr_size) = NULL;
98+
ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(zend_random_bytes_insecure_state *state, void *bytes, size_t size) = NULL;
9799

98100
/* This callback must be signal handler safe! */
99101
void (*zend_on_timeout)(int seconds);
@@ -912,6 +914,10 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
912914
php_win32_cp_set_by_id(65001);
913915
#endif
914916

917+
/* Set up early utility functions. */
918+
zend_random_bytes = utility_functions->random_bytes_function;
919+
zend_random_bytes_insecure = utility_functions->random_bytes_insecure_function;
920+
915921
start_memory_manager();
916922

917923
virtual_cwd_startup(); /* Could use shutdown to free the main cwd but it would just slow it down for CGI */

Zend/zend.h

Lines changed: 15 additions & 0 deletions
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_random_bytes_insecure_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,6 +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);
256+
zend_result (*random_bytes_function)(void *bytes, size_t size, char *errstr, size_t errstr_size);
257+
void (*random_bytes_insecure_function)(zend_random_bytes_insecure_state *state, void *bytes, size_t size);
251258
} zend_utility_functions;
252259

253260
typedef struct _zend_utility_values {
@@ -340,6 +347,14 @@ extern void (*zend_printf_to_smart_string)(smart_string *buf, const char *format
340347
extern void (*zend_printf_to_smart_str)(smart_str *buf, const char *format, va_list ap);
341348
extern ZEND_API char *(*zend_getenv)(const char *name, size_t name_len);
342349
extern ZEND_API zend_string *(*zend_resolve_path)(zend_string *filename);
350+
/* Generate 'size' random bytes into 'bytes' with the OS CSPRNG. */
351+
extern ZEND_ATTRIBUTE_NONNULL ZEND_API zend_result (*zend_random_bytes)(
352+
void *bytes, size_t size, char *errstr, size_t errstr_size);
353+
/* Generate 'size' random bytes into 'bytes' with a general purpose PRNG (not
354+
* crypto safe). 'state' must be zeroed before the first call and can be reused.
355+
*/
356+
extern ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(
357+
zend_random_bytes_insecure_state *state, void *bytes, size_t size);
343358

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

Zend/zend_portability.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,4 +791,24 @@ 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+
#if SIZEOF_LONG_LONG
804+
long long ll;
805+
#endif
806+
float f;
807+
double d;
808+
long double ld;
809+
void *p;
810+
void (*fun)();
811+
} zend_max_align_t;
812+
#endif
813+
794814
#endif /* ZEND_PORTABILITY_H */

ext/random/config.m4

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ PHP_NEW_EXTENSION(random,
2727
engine_secure.c \
2828
engine_user.c \
2929
gammasection.c \
30-
randomizer.c,
30+
randomizer.c \
31+
zend_utils.c,
3132
no,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
3233
PHP_INSTALL_HEADERS([ext/random], [php_random.h php_random_csprng.h php_random_uint128.h])

ext/random/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
EXTENSION("random", "random.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
22
PHP_RANDOM="yes";
3-
ADD_SOURCES(configure_module_dirname, "csprng.c engine_combinedlcg.c engine_mt19937.c engine_pcgoneseq128xslrr64.c engine_xoshiro256starstar.c engine_secure.c engine_user.c gammasection.c randomizer.c", "random");
3+
ADD_SOURCES(configure_module_dirname, "csprng.c engine_combinedlcg.c engine_mt19937.c engine_pcgoneseq128xslrr64.c engine_xoshiro256starstar.c engine_secure.c engine_user.c gammasection.c randomizer.c zend_utils.c", "random");
44
PHP_INSTALL_HEADERS("ext/random", "php_random.h php_random_csprng.h php_random_uint128.h");

ext/random/php_random.h

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

3838
PHPAPI double php_combined_lcg(void);
3939

40+
typedef struct _php_random_fallback_seed_state php_random_fallback_seed_state;
41+
4042
PHPAPI uint64_t php_random_generate_fallback_seed(void);
43+
PHPAPI uint64_t php_random_generate_fallback_seed_ex(php_random_fallback_seed_state *state);
4144

4245
static inline zend_long GENERATE_SEED(void)
4346
{
@@ -99,6 +102,11 @@ typedef struct _php_random_algo_with_state {
99102
void *state;
100103
} php_random_algo_with_state;
101104

105+
typedef struct _php_random_fallback_seed_state {
106+
bool initialized;
107+
unsigned char seed[20];
108+
} php_random_fallback_seed_state;
109+
102110
extern PHPAPI const php_random_algo php_random_algo_combinedlcg;
103111
extern PHPAPI const php_random_algo php_random_algo_mt19937;
104112
extern PHPAPI const php_random_algo php_random_algo_pcgoneseq128xslrr64;
@@ -197,8 +205,7 @@ PHP_RINIT_FUNCTION(random);
197205
ZEND_BEGIN_MODULE_GLOBALS(random)
198206
bool combined_lcg_seeded;
199207
bool mt19937_seeded;
200-
bool fallback_seed_initialized;
201-
unsigned char fallback_seed[20];
208+
php_random_fallback_seed_state fallback_seed_state;
202209
php_random_status_state_combinedlcg combined_lcg;
203210
php_random_status_state_mt19937 mt19937;
204211
ZEND_END_MODULE_GLOBALS(random)

ext/random/php_random_zend_utils.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
| [email protected] so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Authors: Arnaud Le Blanc <[email protected]> |
14+
| Tim Düsterhus <[email protected]> |
15+
+----------------------------------------------------------------------+
16+
*/
17+
18+
#ifndef PHP_RANDOM_ZEND_UTILS_H
19+
# define PHP_RANDOM_ZEND_UTILS_H
20+
21+
# include "php.h"
22+
# include "php_random.h"
23+
# include "zend.h"
24+
25+
typedef struct _php_random_bytes_insecure_state_for_zend {
26+
bool initialized;
27+
php_random_status_state_xoshiro256starstar xoshiro256starstar_state;
28+
} php_random_bytes_insecure_state_for_zend;
29+
30+
ZEND_STATIC_ASSERT(sizeof(zend_random_bytes_insecure_state) >= sizeof(php_random_bytes_insecure_state_for_zend), "");
31+
32+
ZEND_ATTRIBUTE_NONNULL PHPAPI void php_random_bytes_insecure_for_zend(
33+
zend_random_bytes_insecure_state *state, void *bytes, size_t size);
34+
35+
#endif /* PHP_RANDOM_ZEND_UTILS_H */

ext/random/random.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ static inline void fallback_seed_add(PHP_SHA1_CTX *c, void *p, size_t l){
610610
PHP_SHA1Update(c, p, l);
611611
}
612612

613-
PHPAPI uint64_t php_random_generate_fallback_seed(void)
613+
PHPAPI uint64_t php_random_generate_fallback_seed_ex(php_random_fallback_seed_state *state)
614614
{
615615
/* Mix various values using SHA-1 as a PRF to obtain as
616616
* much entropy as possible, hopefully generating an
@@ -628,7 +628,7 @@ PHPAPI uint64_t php_random_generate_fallback_seed(void)
628628
char buf[64 + 1];
629629

630630
PHP_SHA1Init(&c);
631-
if (!RANDOM_G(fallback_seed_initialized)) {
631+
if (!state->initialized) {
632632
/* Current time. */
633633
gettimeofday(&tv, NULL);
634634
fallback_seed_add(&c, &tv, sizeof(tv));
@@ -644,7 +644,7 @@ PHPAPI uint64_t php_random_generate_fallback_seed(void)
644644
fallback_seed_add(&c, &tid, sizeof(tid));
645645
#endif
646646
/* Pointer values to benefit from ASLR. */
647-
pointer = &RANDOM_G(fallback_seed_initialized);
647+
pointer = &state;
648648
fallback_seed_add(&c, &pointer, sizeof(pointer));
649649
pointer = &c;
650650
fallback_seed_add(&c, &pointer, sizeof(pointer));
@@ -668,24 +668,29 @@ PHPAPI uint64_t php_random_generate_fallback_seed(void)
668668
gettimeofday(&tv, NULL);
669669
fallback_seed_add(&c, &tv, sizeof(tv));
670670
/* Previous state. */
671-
fallback_seed_add(&c, RANDOM_G(fallback_seed), 20);
671+
fallback_seed_add(&c, state->seed, 20);
672672
}
673-
PHP_SHA1Final(RANDOM_G(fallback_seed), &c);
674-
RANDOM_G(fallback_seed_initialized) = true;
673+
PHP_SHA1Final(state->seed, &c);
674+
state->initialized = true;
675675

676676
uint64_t result = 0;
677677

678678
for (size_t i = 0; i < sizeof(result); i++) {
679-
result = result | (((uint64_t)RANDOM_G(fallback_seed)[i]) << (i * 8));
679+
result = result | (((uint64_t)state->seed[i]) << (i * 8));
680680
}
681681

682682
return result;
683683
}
684684

685+
PHPAPI uint64_t php_random_generate_fallback_seed(void)
686+
{
687+
return php_random_generate_fallback_seed_ex(&RANDOM_G(fallback_seed_state));
688+
}
689+
685690
/* {{{ PHP_GINIT_FUNCTION */
686691
static PHP_GINIT_FUNCTION(random)
687692
{
688-
random_globals->fallback_seed_initialized = false;
693+
random_globals->fallback_seed_state.initialized = false;
689694
}
690695
/* }}} */
691696

ext/random/zend_utils.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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+
| [email protected] so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Authors: Arnaud Le Blanc <[email protected]> |
14+
| Tim Düsterhus <[email protected]> |
15+
+----------------------------------------------------------------------+
16+
*/
17+
18+
#ifdef HAVE_CONFIG_H
19+
# include "config.h"
20+
#endif
21+
22+
#include "php_random_zend_utils.h"
23+
24+
ZEND_ATTRIBUTE_NONNULL PHPAPI void php_random_bytes_insecure_for_zend(
25+
zend_random_bytes_insecure_state *opaque_state, void *bytes, size_t size)
26+
{
27+
php_random_bytes_insecure_state_for_zend *state = (php_random_bytes_insecure_state_for_zend*) opaque_state;
28+
29+
if (UNEXPECTED(!state->initialized)) {
30+
uint64_t t[4];
31+
php_random_fallback_seed_state fallback_state;
32+
fallback_state.initialized = false;
33+
34+
do {
35+
/* Skip the CSPRNG if it has already failed */
36+
if (!fallback_state.initialized) {
37+
char errstr[128];
38+
if (php_random_bytes_ex(&t, sizeof(t), errstr, sizeof(errstr)) == FAILURE) {
39+
#if ZEND_DEBUG
40+
fprintf(stderr, "php_random_bytes_ex: Failed to generate a random seed: %s\n", errstr);
41+
#endif
42+
goto fallback;
43+
}
44+
} else {
45+
fallback:
46+
t[0] = php_random_generate_fallback_seed_ex(&fallback_state);
47+
t[1] = php_random_generate_fallback_seed_ex(&fallback_state);
48+
t[2] = php_random_generate_fallback_seed_ex(&fallback_state);
49+
t[3] = php_random_generate_fallback_seed_ex(&fallback_state);
50+
}
51+
} while (UNEXPECTED(t[0] == 0 && t[1] == 0 && t[2] == 0 && t[3] == 0));
52+
53+
php_random_xoshiro256starstar_seed256(&state->xoshiro256starstar_state, t[0], t[1], t[2], t[3]);
54+
state->initialized = true;
55+
}
56+
57+
while (size > 0) {
58+
php_random_result result = php_random_algo_xoshiro256starstar.generate(&state->xoshiro256starstar_state);
59+
ZEND_ASSERT(result.size == 8 && sizeof(result.result) == 8);
60+
size_t chunk_size = MIN(size, 8);
61+
bytes = zend_mempcpy(bytes, &result.result, chunk_size);
62+
size -= chunk_size;
63+
}
64+
}

main/main.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
#include "fopen_wrappers.h"
5050
#include "ext/standard/php_standard.h"
5151
#include "ext/date/php_date.h"
52+
#include "ext/random/php_random_csprng.h"
53+
#include "ext/random/php_random_zend_utils.h"
5254
#include "php_variables.h"
5355
#include "ext/standard/credits.h"
5456
#ifdef PHP_WIN32
@@ -2119,6 +2121,8 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
21192121
zuf.printf_to_smart_str_function = php_printf_to_smart_str;
21202122
zuf.getenv_function = sapi_getenv;
21212123
zuf.resolve_path_function = php_resolve_path_for_zend;
2124+
zuf.random_bytes_function = php_random_bytes_ex;
2125+
zuf.random_bytes_insecure_function = php_random_bytes_insecure_for_zend;
21222126
zend_startup(&zuf);
21232127
zend_reset_lc_ctype_locale();
21242128
zend_update_current_locale();

0 commit comments

Comments
 (0)