Skip to content

Fix GH-22122: UAF in SQLite3/Pdo\Sqlite authorizer when callback releases it#22126

Open
iliaal wants to merge 1 commit into
php:masterfrom
iliaal:fix/gh-22122-sqlite3-authorizer-uaf
Open

Fix GH-22122: UAF in SQLite3/Pdo\Sqlite authorizer when callback releases it#22126
iliaal wants to merge 1 commit into
php:masterfrom
iliaal:fix/gh-22122-sqlite3-authorizer-uaf

Conversation

@iliaal
Copy link
Copy Markdown
Contributor

@iliaal iliaal commented May 22, 2026

zend_call_known_fcc does not addref fcc->object/fcc->closure. When the authorizer callback invokes $db->setAuthorizer(null), zend_fcc_dtor releases the bound $this and the object is freed mid-call; the method body then dereferences freed memory. Snapshot object and closure before the call, addref via zend_fcc_addref, release the snapshots after. Saved pointers are required because the callback can replace db_obj->authorizer_fcc.

Same fix applied to Pdo\Sqlite, which has the identical reachable bug via its own setAuthorizer(null). SQLite3's Z_ISUNDEF(retval) branch now matches Pdo\Sqlite's ZEND_ASSERT(EG(exception)) shape, so a throwing callback no longer triggers the misleading "An error occurred while invoking the authorizer callback" warning.

zend_fcc_dup is not safe here: shallow-copy of function_handler would leave a trampoline owned by two FCCs, and dtor of the second would double-free.

Fixes #22122

…eleases it

zend_call_known_fcc does not addref fcc->object or fcc->closure. When
the authorizer callback invokes $db->setAuthorizer(null), zend_fcc_dtor
frees the bound $this mid-call. Snapshot object/closure before
zend_fcc_addref and release the snapshots after the call. Same fix in
Pdo\Sqlite. Replace the misleading "An error occurred" warning on
Z_ISUNDEF(retval) with ZEND_ASSERT(EG(exception)) to match Pdo\Sqlite's
existing pattern.

Fixes phpGH-22122
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SQLite3 authorizer callback $this freed via setAuthorizer during execution (Use After Free)

1 participant