Repository navigation
Fiber: hold the object a callable resolved to in the creating frame - #24134
Conversation
Inside a method of A, new Fiber([A::class, 'm']) or new Fiber("A::m")
resolves the callable to the method's $this. Fiber::__construct() kept
that object in fci.object and fci_cache.object without a reference, so
once the A died before start(), the fiber called A::m() on freed memory.
The fiber now holds fci.object for every callable, releases it with the
callable (after the call, or when the fiber is freed unstarted) and
reports it to the GC, so a fiber stored in a property of that object is
a collectable cycle.
A __call trampoline is not covered: it is resolved again at start(),
against the frame that calls start().
Done. Thank you |
|
Seems there is a conflict, mind rebasing? |
@Girgias |
GitHub was showing it as having a conflict and thus non-mergeable.... so I guess it was just drunk. |
* PHP-8.6:
Update NEWS
ext/standard/tests: work around Windows quoting with tempfiles.
.github/scripts/windows: pass an extension_dir to the test runner.
{ext/standard,sapi/cli}/tests: pass TEST_PHP_EXTRA_ARGS to php
ext/standard/tests: pass TEST_PHP_EXTRA_ARGS to some proc_open() tests
sapi/cli/tests/023.phpt: support TEST_PHP_EXTRA_ARGS
std: Fix memory leak of the last response headers after a nested HTTP request (#24201)
Fiber: hold the object a callable resolved to in the creating frame (#24134)
* PHP-8.4:
Update NEWS
ext/standard/tests: work around Windows quoting with tempfiles.
.github/scripts/windows: pass an extension_dir to the test runner.
{ext/standard,sapi/cli}/tests: pass TEST_PHP_EXTRA_ARGS to php
ext/standard/tests: pass TEST_PHP_EXTRA_ARGS to some proc_open() tests
sapi/cli/tests/023.phpt: support TEST_PHP_EXTRA_ARGS
std: Fix memory leak of the last response headers after a nested HTTP request (php#24201)
Fiber: hold the object a callable resolved to in the creating frame (php#24134)
* PHP-8.5:
Update NEWS
ext/standard/tests: work around Windows quoting with tempfiles.
.github/scripts/windows: pass an extension_dir to the test runner.
{ext/standard,sapi/cli}/tests: pass TEST_PHP_EXTRA_ARGS to php
ext/standard/tests: pass TEST_PHP_EXTRA_ARGS to some proc_open() tests
sapi/cli/tests/023.phpt: support TEST_PHP_EXTRA_ARGS
std: Fix memory leak of the last response headers after a nested HTTP request (php#24201)
Fiber: hold the object a callable resolved to in the creating frame (php#24134)
* up/PHP-8.4: (3595 commits) Fix Windows quoting in proc_open tests (php#24343) Pin macOS CI to OpenSSL 3 (php#24332) Zend: zend_call_stack_get_netbsd_pthread() pthread attribute leak. Revert "ext/standard/tests: work around Windows quoting with tempfiles." Update NEWS ext/standard/tests: work around Windows quoting with tempfiles. .github/scripts/windows: pass an extension_dir to the test runner. {ext/standard,sapi/cli}/tests: pass TEST_PHP_EXTRA_ARGS to php ext/standard/tests: pass TEST_PHP_EXTRA_ARGS to some proc_open() tests sapi/cli/tests/023.phpt: support TEST_PHP_EXTRA_ARGS std: Fix memory leak of the last response headers after a nested HTTP request (php#24201) Fiber: hold the object a callable resolved to in the creating frame (php#24134) Fix v142 ARM64 hash and floating point results (php#24317) Use the default archive names for Windows ARM64 builds (php#24313) ext/gd: Drop useless and doubtful MSVC specific code (libgd/libgd@f1480ab) (php#24316) ext/session: do not reuse session state a save handler tore down Fix phpGH-24293: Segfault with an empty password in MariaDB PAM (php#24294) Fix phpGH-24289: finfo::file() reports the wrong argument number Fix phpGH-24260: querySelectorAll() returns duplicate elements for selector lists Fix escape analysis for objects implementing ArrayAccess ...
Upstream master moved to 8.7.0-dev; true-async stays on 8.6, so it now follows PHP-8.6 instead of master. Conflicts: - Zend/zend_atomic.h: kept the fork's fetch_add/fetch_sub helpers next to upstream's ZEND_ATOMIC_FENCE_* macros. - Zend/zend_fibers.c: upstream holds the object a callable resolved to (php#24134); applied to the non-coroutine path. The coroutine path keeps zend_fcall_t, which does not hold fci.object yet. - ext/pdo/pdo_dbh.c: added upstream's persistent refcount guard (phpGH-23962) to the fork's in_txn check, which avoids dispatching to the pool. - ext/standard/http_fopen_wrapper.c: took upstream's single release of the last response headers after the request; it also covers headers stored by another coroutine. - main/streams/cast.c: the fork's buffer locking with the new PHP_STREAM_AS_FD_FOR_POLL exemption. - main/streams/streams.c: flush/close results propagate (bug #60110) while the lock-free flush on close and pending_free stay.
Upstream php#24134 makes a Fiber hold fci.object, the creating method's $this for [A::class, 'm'] or "A::m". The PHP-8.6 merge applied it to the plain fiber path only: a coroutine-backed Fiber keeps its callable in a zend_fcall_t, which holds the function name but not fci.object, so once the object died before start() the coroutine called the method on freed memory (valgrind: invalid read in zend_call_function from coroutine_entry_point). The fiber now holds the object in fiber->fci.object, releases it when the call returns or with the fiber, and reports it to the GC. The test's last block expects gc_collect_cycles() to collect synchronously; under TrueAsync the destructors run in the GC coroutine, so it expects 0 and the destructor afterwards, as gc-cycle-callback.phpt does.
Fix use-after-free when a Fiber callable resolves to the creating method's $this
Problem
Inside a method of a class,
[A::class, 'm']and"A::m"resolve to a call ofm()on the current$this.Fiber::__construct()stores that object infci.objectandfci_cache.objectbut takes no reference to it. If the object dies beforeFiber::start(), the fiber calls the method on freed memory.PHP 8.3.6 and PHP-8.4 print
releasedbeforem on a1: the destructor has run and the object is freed whenm()executes. Valgrind reports invalid reads. A new object allocated betweenmake()andstart()can take the freed slot.Cause
Fiber::__construct()(Zend/zend_fibers.c) keeps a reference only to the callable zval:For
[A::class, 'm']the zval holds a class name and a method name, not the object. The object comes from the calling frame's$this, so nothing keeps it alive.On PHP-8.4 a fiber suspended inside
m()hits the same bug: if the last reference to the object goes while the fiber is suspended,m()resumes on freed memory.Fix
Fiber::__construct()adds a reference tofci.objectwhenever it is set, for every kind of callable, so the release and the GC report need no condition.zend_fiber_release_callable()releases the callable zval and that object together, after the call returns or when an unstarted fiber is freed.zend_fiber_object_gc()reports the object, so a fiber stored in a property of the same object is a collectable cycle.Not covered: a callable that resolves to
__callis resolved again atstart(), against the frame that callsstart(). That is a separate issue.Tests
Zend/tests/fibers/fiber-callable-object-outlives-caller.phptcovers five cases:"A::m";gc_collect_cycles()collects.Verified on a
--disable-all --enable-debugbuild of PHP-8.4:Zend/tests/fiberspass and the new test is clean under valgrind (run-tests.php -m).The same change on master passes the 111 tests in
Zend/tests/fibers.