Fix GH-22060: UAF in class-autoloader dispatch on self-unregister#22085
Open
iliaal wants to merge 1 commit into
Open
Fix GH-22060: UAF in class-autoloader dispatch on self-unregister#22085iliaal wants to merge 1 commit into
iliaal wants to merge 1 commit into
Conversation
zend_perform_class_autoload walked the autoloader hashtable and called zend_call_known_fcc on each FCC without pinning its refcounted owners. When the autoloader's PHP body called spl_autoload_unregister([$this, 'load']), the hash bucket destructor released the last ref on $this and freed it mid-call; the subsequent $this->data read landed in freed memory. Pin object and closure manually around the call. Don't use zend_fcc_dup/zend_fcc_dtor: zend_fcc_dtor frees trampoline storage based on ZEND_ACC_CALL_VIA_TRAMPOLINE, which would double-free the hash entry's trampoline copy for __call-based autoloaders. Fixes phpGH-22060
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A method-bound autoloader registered as
[$obj, 'load']can callspl_autoload_unregister([$this, 'load'])from inside its own callback. The hash entry's destructor releases the last ref on the object, frees it, and the rest of the PHP body (e.g.echo $this->data) reads freed memory. The dispatch loop inzend_perform_class_autoloadcallszend_call_known_fcc(func_info, ...), butzend_call_functionreadsfci_cache->objectintoEX(This)without bumping the refcount; the call convention is that the caller holds the ref, and here the only ref was the hash entry's.Fix pins
objectandclosureacross the call withGC_ADDREF/OBJ_RELEASE. The pointers are saved locally before the call so the releases work even whenfunc_infoitself is freed mid-call by self-unregister.