Skip to content

socket_accept() returning a freed Socket when its operation throws - #7

Merged
bukka merged 1 commit into
bukka:io_hooks_pocfrom
true-async:io-hooks-socket-accept-exception
Oct 13, 2026
Merged

bukka merged 1 commit into
bukka:io_hooks_pocfrom
true-async:io-hooks-socket-accept-exception

Conversation

@EdmondDantes

Copy link
Copy Markdown

When the accept operation fails with an exception (a provider's run() throws, or a scheduler cancels the coroutine waiting in socket_accept()), socket_accept() releases the new Socket and returns through RETURN_THROWS() with that object still in return_value. The caller releases it a second time: an assertion failure in zend_objects_store_del() on a debug and an ASAN build (a double free on a release build, read from the code, not run).

Reproduction (debug build, ext/sockets):

Io\Hooks\set_hooks(new class implements Io\Hooks\Hooks {
    public function getCapabilities(): array { return []; }
    public function add(Io\Registration $registration): void {}
    public function remove(Io\Registration $registration): void {}
    public function run(Io\Operation $op): Io\Completion {
        throw new RuntimeException("cancelled");
    }
});

$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '127.0.0.1', 0);
socket_listen($server);

try {
    socket_accept($server);
} catch (RuntimeException $e) {
    echo $e->getMessage(), "\n";
}

Expected: cancelled. Actual: Assertion ... failed in zend_objects_store_del().

The exception path now clears return_value after the release, as zend_builtin_functions.c does before its RETURN_THROWS().

Tests

  • ext/sockets/tests/socket_io_hooks_accept_throws.phpt: the reproduction above, with the operation's class in the message.

It fails without the fix and passes with it; ext/sockets/tests: 106 passed, 0 failed, on a debug and an ASAN build (ZTS, Linux, io_uring), this commit on TrueAsync's core async-core-io-2026-10-09-3, which carries io_hooks_poc 566a683; socket_accept() is the same on 8e470c2.

The new Socket was released and left in return_value, so the caller
released it a second time.
@bukka
bukka merged commit 2e046e2 into bukka:io_hooks_poc Oct 13, 2026
15 of 17 checks passed
@EdmondDantes
EdmondDantes deleted the io-hooks-socket-accept-exception branch October 13, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants