session: Stop using php_combined_lcg()

The CombinedLCG is a terrible RNG with a questionable API and should ideally
not be used anymore. While in the case of ext/session it is only used for
probabilistic garbage collection where the quality of the RNG is not of
particular importance, there are better choices.

Replace the RNG used for garbage collection by an ext/session specific instance
of PcgOneseq128XslRr64. Its 16 Byte state nicely fits into the memory freed up
by the previous reordering of the session globals struct, even allowing to the
storage of the php_random_algo_with_state struct, making using the RNG a little
nicer.

Instead multiplying the float returned by the CombinedLCG by the GC Divisor to
obtain an integer between 0 and the divisor we can just use `php_random_range`
to directly generate an appropriate integer, completely avoiding the floating
point maths, making it easier to verify the code for correctness.
This commit is contained in:
Tim Düsterhus 2024-03-01 18:15:48 +01:00 committed by Gina Peter Banyard
parent 4df911efcb
commit f6c38fc952
2 changed files with 18 additions and 6 deletions

View file

@ -19,6 +19,7 @@
#include "ext/standard/php_var.h" #include "ext/standard/php_var.h"
#include "ext/hash/php_hash.h" #include "ext/hash/php_hash.h"
#include "ext/random/php_random.h"
#define PHP_SESSION_API 20161017 #define PHP_SESSION_API 20161017
@ -157,6 +158,8 @@ typedef struct _php_ps_globals {
zend_string *session_started_filename; zend_string *session_started_filename;
uint32_t session_started_lineno; uint32_t session_started_lineno;
int module_number; int module_number;
php_random_status_state_pcgoneseq128xslrr64 random_state;
php_random_algo_with_state random;
zend_long gc_probability; zend_long gc_probability;
zend_long gc_divisor; zend_long gc_divisor;
zend_long gc_maxlifetime; zend_long gc_maxlifetime;

View file

@ -387,17 +387,16 @@ PHPAPI zend_result php_session_valid_key(const char *key) /* {{{ */
static zend_long php_session_gc(bool immediate) /* {{{ */ static zend_long php_session_gc(bool immediate) /* {{{ */
{ {
int nrand;
zend_long num = -1; zend_long num = -1;
bool collect = immediate;
/* GC must be done before reading session data. */ /* GC must be done before reading session data. */
if ((PS(mod_data) || PS(mod_user_implemented))) { if ((PS(mod_data) || PS(mod_user_implemented))) {
if (immediate) { if (!collect && PS(gc_probability) > 0) {
PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &num); collect = php_random_range(PS(random), 0, PS(gc_divisor) - 1) < PS(gc_probability);
return num;
} }
nrand = (zend_long) ((float) PS(gc_divisor) * php_combined_lcg());
if (PS(gc_probability) > 0 && nrand < PS(gc_probability)) { if (collect) {
PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &num); PS(mod)->s_gc(&PS(mod_data), PS(gc_maxlifetime), &num);
} }
} }
@ -2872,6 +2871,16 @@ static PHP_GINIT_FUNCTION(ps) /* {{{ */
ZVAL_UNDEF(&ps_globals->mod_user_names.ps_validate_sid); ZVAL_UNDEF(&ps_globals->mod_user_names.ps_validate_sid);
ZVAL_UNDEF(&ps_globals->mod_user_names.ps_update_timestamp); ZVAL_UNDEF(&ps_globals->mod_user_names.ps_update_timestamp);
ZVAL_UNDEF(&ps_globals->http_session_vars); ZVAL_UNDEF(&ps_globals->http_session_vars);
ps_globals->random = (php_random_algo_with_state){
.algo = &php_random_algo_pcgoneseq128xslrr64,
.state = &ps_globals->random_state,
};
php_random_uint128_t seed;
if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
seed = php_random_uint128_constant(GENERATE_SEED(), GENERATE_SEED());
}
php_random_pcgoneseq128xslrr64_seed128(ps_globals->random.state, seed);
} }
/* }}} */ /* }}} */