[php-src] Issue #21730: ext/random: Mt19937::__debugInfo() leaks state HashTable when the serialize callback fails
[email protected] (iliaal)
| Newsgroups | php.bugs |
|---|---|
| Message-ID | <[email protected]> |
Issue: https://github.com/php/php-src/issues/21730
Author: iliaal
## Description
`Random\Engine\Mt19937::__debugInfo()` allocates a temporary `HashTable` with `array_init(&t)` and then calls `engine->engine.algo->serialize()`. If the callback returns `false`, the method throws "Engine serialize failed" and returns via `RETURN_THROWS()` before `t` gets inserted anywhere, so the HashTable leaks. `PcgOneseq128XslRr64::__debugInfo()` and `Xoshiro256StarStar::__debugInfo()` alias the same implementation and inherit the same leak.
Niels Dossche fixed this exact pattern in `__serialize()` in #20383 (720e0069829, "random: Fix memory leak when serialization fails"). That cleanup didn't touch `__debugInfo()`.
The path is latent in stock PHP because the three built-in `serialize` callbacks (mt19937, pcg, xoshiro) all return `true`, so no user code can reach the leak today. I'm filing it anyway for symmetry with the #20383 fix and to keep the pattern from regressing if a future engine's `serialize` grows a failure mode.
## Reproduction
Force the latent path with a one-line patch:
~~~diff
--- a/ext/random/engine_mt19937.c
+++ b/ext/random/engine_mt19937.c
@@ -174,6 +174,7 @@ static bool serialize(void *state, HashTable *data)
{
php_random_status_state_mt19937 *s = state;
zval t;
+ return false;
~~~
Run:
~~~php
<?php
(new Random\Engine\Mt19937(1234))->__debugInfo();
~~~
under valgrind:
~~~
USE_ZEND_ALLOC=0 valgrind --leak-check=full --show-leak-kinds=definite \
sapi/cli/php repro.php
~~~
Output:
~~~
==3067603== 56 bytes in 1 blocks are definitely lost in loss record 1 of 1
==3067603== at 0x4848899: malloc
==3067603== by 0x70B5CC: __zend_malloc (zend_alloc.c:3544)
==3067603== by 0x7B011C: _zend_new_array_0 (zend_hash.c:283)
==3067603== by 0x59AAB4: zim_Random_Engine_Mt19937___debugInfo (engine_mt19937.c:396)
~~~
## PHP Version
master (PHP 8.6.0-dev). The block hasn't changed in 8.3/8.4/8.5 either, so all supported branches carry the same latent leak.