Summary
Under elephc --web, using PDO sqlite from a request handler causes
non-deterministic heap corruption in the worker process: requests return
empty responses (curl exit 52), and macOS crash reports show a double-free
during HTTP response writing. In CLI mode (no --web) the identical PDO
code compiles and runs stably. This blocked shipping PDO-backed storage for
a pastebin app and forced a move to filesystem-backed storage instead.
Environment
- elephc v0.26.0 (release, commit
892ff162), macOS ARM64
- PHP 8.4 (
php -S): the identical code is stable
Crash signature
EXC_BREAKPOINT / SIGTRAP (or SIGABRT "pointer being freed was not allocated")
mfm_free -> bytes::bytes::shared_drop -> hyper poll_write -> elephc_web::worker::serve
Minimal reproduction
<?php
if (isset($_SERVER['REQUEST_METHOD'])) {
$pdo = new PDO('sqlite:/tmp/x.db');
$pdo->exec('CREATE TABLE IF NOT EXISTS n (a INTEGER PRIMARY KEY, b INTEGER)');
$st = $pdo->prepare('INSERT OR REPLACE INTO n (a, b) VALUES (?, ?)');
$st->execute([1, 2]); // <-- trigger
header('Content-Type: text/plain');
echo "ok\n";
}
./app --listen 127.0.0.1:8080 --workers 1
curl http://127.0.0.1:8080/
Measurements (30 requests, single worker, clean port each run)
| variant |
crash rate |
handler with no PDO (echo, $_POST, header) |
0/30 |
new PDO + exec() (INSERT via exec() too) |
0/30 |
prepare() without execute() |
0/30 |
prepare() + execute([int, int]) INSERT |
up to 30/30 |
prepare() + execute() + fetch() SELECT |
intermittent (1/30 to 25/25) |
query() + fetch() |
intermittent (3/25 to 25/25) |
The crash rate is layout-dependent: the exact same logic with one line more
or fewer moves from ~4% to 100% crash, and varies between a worker and its
respawned replacement. This is heap corruption (something frees the response
buffer or allocator metadata out of turn), not a deterministic logic error.
Things tried without success
--heap-size 32M / 64M / 128M / 256M (at 256M workers die on startup
instead)
--ir-opt=off
--regalloc=stack
--null-repr=tagged
- Rewriting with
exec()/query()/quote() instead of prepared
statements+execute()
- A warm-up request before the measured ones
--ast-backend --web doesn't even link:
Undefined symbols: _fn_header referenced from _fn_setcookie
Impact
No app-level workaround was found to be reliable: e2e and byte-parity checks
against php -S fail non-deterministically (typically 5–6 ok / 10–11 fail
out of 16 runs). The only workaround that worked was abandoning PDO for
filesystem-backed storage under --web entirely.
Expected
PDO sqlite operations that go through prepare()/execute() (or query())
under --web should not corrupt the heap or crash workers; behavior should
match CLI mode.
Notes
Found building a pastebin single-binary app (--web) for the Discord
contest, where PDO sqlite was the original storage design.
Summary
Under
elephc --web, using PDO sqlite from a request handler causesnon-deterministic heap corruption in the worker process: requests return
empty responses (curl exit 52), and macOS crash reports show a double-free
during HTTP response writing. In CLI mode (no
--web) the identical PDOcode compiles and runs stably. This blocked shipping PDO-backed storage for
a pastebin app and forced a move to filesystem-backed storage instead.
Environment
892ff162), macOS ARM64php -S): the identical code is stableCrash signature
Minimal reproduction
Measurements (30 requests, single worker, clean port each run)
$_POST,header)new PDO+exec()(INSERT viaexec()too)prepare()withoutexecute()prepare()+execute([int, int])INSERTprepare()+execute()+fetch()SELECTquery()+fetch()The crash rate is layout-dependent: the exact same logic with one line more
or fewer moves from ~4% to 100% crash, and varies between a worker and its
respawned replacement. This is heap corruption (something frees the response
buffer or allocator metadata out of turn), not a deterministic logic error.
Things tried without success
--heap-size32M / 64M / 128M / 256M (at 256M workers die on startupinstead)
--ir-opt=off--regalloc=stack--null-repr=taggedexec()/query()/quote()instead of preparedstatements+
execute()--ast-backend --webdoesn't even link:Undefined symbols: _fn_headerreferenced from_fn_setcookieImpact
No app-level workaround was found to be reliable: e2e and byte-parity checks
against
php -Sfail non-deterministically (typically 5–6 ok / 10–11 failout of 16 runs). The only workaround that worked was abandoning PDO for
filesystem-backed storage under
--webentirely.Expected
PDO sqlite operations that go through
prepare()/execute()(orquery())under
--webshould not corrupt the heap or crash workers; behavior shouldmatch CLI mode.
Notes
Found building a pastebin single-binary app (
--web) for the Discordcontest, where PDO sqlite was the original storage design.