Fix GH-22060 and GH-22122: pin $this in zend_call_function#22151
Conversation
DanielEScherzer
left a comment
There was a problem hiding this comment.
okay for ext/reflection, don't know about the other parts
Girgias
left a comment
There was a problem hiding this comment.
I was trying to understand why we would need to add ref the fcc.object, and my understanding is that it represents the $this value, so I guess this is valid.
However, we are once again fixing bugs and affecting performance for idiosyncratic code. I would rather prefer a solution that bans unsetting such callables within the calls. Similar to a fix we recently did for an unserialising routine in SPL.
|
Pinning is two refcount ops against a full call-frame setup, so the per-call cost is negligible. Banning is the bigger change: the autoload loop is deliberately built to allow add/remove mid-iteration, and disabling the authorizer mid-call is a one-shot the sqlite3 test relies on. Turning either into a thrown error is a behavior change that can't go in a stable branch. If the stricter semantics are worth having, that's a master-only follow-up. |
Such things will come up more and more with the raise of AI. A while ago when fixing a similar issue, @TimWolla asked me if This should target master anyway (we agreed to not fixing such bugs on stable branches that are unlikely to cause issues in the real world). So maybe let's just go with the above instead. |
02a264f to
7720c11
Compare
|
The central pin doesn't work out. Output handlers dispatch straight through |
|
GH-20352 is a fuzzer finding, so it doesn't really matter whether its behavior changes. The behavior actually seems correct now, the output handler won't be freed until we exit its |
|
Checked the other zend_call_function callers as well (spl, sqlite3, pdo_sqlite, dom, reflection, pcre, phar, mbstring) and nothing else changes, so the central pin covers it. |
a85e9ba to
d7ab370
Compare
d7ab370 to
2435139
Compare
|
@iliaal Does adjusting |
2435139 to
64d5cd0
Compare
TimWolla
left a comment
There was a problem hiding this comment.
LGTM now, but please get someone to double-check.
|
It works, moved the pin to |
|
Looks correct, but would be nice if tests ran, especially for the valgrind benchmark. |
zend_call_function() ran the callback with fci_cache->object as $this without holding a reference, so a callback that released the last reference to its own receiver (an autoloader unregistering itself, a SQLite3 authorizer calling setAuthorizer(null)) freed $this while its frame was still executing. Hold a reference across the call. Fixes phpGH-22060 Fixes phpGH-22122
|
@iluuu1994 tests run, silly NEWS conflict blocked them before :/ |
Both reports are the same use-after-free: the callback ran with
fci_cache->objectas$thiswithout holding a reference, so a callback that released the last reference to its own receiver (autoloader self-unregistering, SQLite3 authorizer calling setAuthorizer(null)) freed it mid-call. Pinning the receiver across the call inzend_call_function()covers both reports and every other site that dispatches through it. gh20352's expected output shifts with it: the output handler's destructor now fires after__invoke()returns rather than mid-deactivation.