[GIT-PULLS] [php-src] PR #22790: Fix double-free of closure run_time_cache in partial application

[email protected] (iliaal) Fri, 17 Jul 2026 15:14:26 +0000
Newsgroups php.git-pulls
Message-ID <[email protected]>
Pull Request: https://github.com/php/php-src/pull/22790
Author: iliaal

A partial built over a closure whose op_array is not persistent (created in eval() or via the CLI -r path, or when opcache is not caching the declaring script) makes zend_accel_compile_pfa shallow-copy the closure op_array into the per-request PFA cache as a lifetime anchor. The copy inherited ZEND_ACC_HEAP_RT_CACHE and the run_time_cache pointer, so destroy_op_array freed the shared run_time_cache twice: once when the closure was released and again at PFA cache teardown, corrupting the heap. Use function_add_ref() on the copy, the canonical shallow-copy refcount helper, which resets the per-request run_time_cache and static_variables_ptr map pointers so only the live closure owns and frees the cache.

Reproducer, invalid free under valgrind (USE_ZEND_ALLOC=0) before the fix, clean after:

php -d opcache.enable_cli=1 -r 'class C { function m($x,$y){ return $x+$y; } } $cl = Closure::fromCallable([new C,"m"]); $pc = $cl(10,?); var_dump($pc(5));'