@@ -393,14 +393,19 @@ static zend_long php_session_gc(bool immediate)
393393 /* GC must be done before reading session data. */
394394 if ((PS (mod_data ) || PS (mod_user_implemented ))) {
395395 if (!collect && PS (gc_probability ) > 0 ) {
396- /* Draw fresh entropy per request instead of using the per-process
397- * PS(random) state. The latter is seeded once in GINIT, which runs
398- * before SAPIs such as PHP-FPM fork their workers, so every worker
399- * would inherit and replay the same GC-decision sequence. */
400- uint32_t rand_val ;
401- if (php_random_bytes_silent (& rand_val , sizeof (rand_val )) == SUCCESS ) {
402- collect = (rand_val % (uint32_t ) PS (gc_divisor )) < (uint32_t ) PS (gc_probability );
396+ /* Seed lazily on first GC draw per process. */
397+ if (UNEXPECTED (!PS (random_seeded ))) {
398+ php_random_uint128_t seed ;
399+ if (php_random_bytes_silent (& seed , sizeof (seed )) == FAILURE ) {
400+ seed = php_random_uint128_constant (
401+ php_random_generate_fallback_seed (),
402+ php_random_generate_fallback_seed ()
403+ );
404+ }
405+ php_random_pcgoneseq128xslrr64_seed128 (PS (random ).state , seed );
406+ PS (random_seeded ) = true;
403407 }
408+ collect = php_random_range (PS (random ), 0 , PS (gc_divisor ) - 1 ) < PS (gc_probability );
404409 }
405410
406411 if (collect ) {
@@ -2892,6 +2897,12 @@ static PHP_GINIT_FUNCTION(ps)
28922897 ZVAL_UNDEF (& ps_globals -> mod_user_names .ps_validate_sid );
28932898 ZVAL_UNDEF (& ps_globals -> mod_user_names .ps_update_timestamp );
28942899 ZVAL_UNDEF (& ps_globals -> http_session_vars );
2900+
2901+ ps_globals -> random = (php_random_algo_with_state ){
2902+ .algo = & php_random_algo_pcgoneseq128xslrr64 ,
2903+ .state = & ps_globals -> random_state ,
2904+ };
2905+ ps_globals -> random_seeded = false;
28952906}
28962907
28972908static PHP_MINIT_FUNCTION (session )
0 commit comments