Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ PHP NEWS
. Deprecate specifying a nullable return type for __debugInfo(). (timwolla)
. Fixed bug GH-22142 (Assertion failure in zendi_try_get_long() on IS_UNDEF).
(David Carlier)
. Fixed GH-22060 (Use-after-free when an autoloader unregisters itself
during dispatch). (iliaal)

- BCMath:
. Added NUL-byte validation to BCMath functions. (jorgsowa)
Expand Down Expand Up @@ -200,6 +202,8 @@ PHP NEWS

- Sqlite3:
. Fix NUL byte truncation in sqlite3 TEXT column handling. (ndossche)
. Fixed GH-22122 (Use-after-free in the authorizer callback when it releases
the authorizer). (iliaal)

- Standard:
. Fixed bug GH-19926 (reset internal pointer earlier while splicing array
Expand Down
7 changes: 7 additions & 0 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,14 @@ static zend_always_inline void zend_call_known_fcc(
memcpy(func, fcc->function_handler, sizeof(zend_function));
zend_string_addref(func->op_array.function_name);
}
zend_object *pinned_object = fcc->object;
if (pinned_object) {
GC_ADDREF(pinned_object);
}
zend_call_known_function(func, fcc->object, fcc->called_scope, retval_ptr, param_count, params, named_params);
if (pinned_object) {
OBJ_RELEASE(pinned_object);
}
}

/* Call the provided zend_function instance method on an object. */
Expand Down
40 changes: 40 additions & 0 deletions ext/pdo_sqlite/tests/gh22122.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
GH-22122 (Use-after-free in Pdo\Sqlite authorizer when callback releases the authorizer)
--EXTENSIONS--
pdo_sqlite
--FILE--
<?php
$db = Pdo\Sqlite::connect('sqlite::memory:');

class Auth {
public string $state = "alive";

public function authorize(int $action, ...$args): int {
global $db;
$db->setAuthorizer(null);
echo "method: ", $this->state, "\n";
return Pdo\Sqlite::OK;
}
}
$auth = new Auth();
$db->setAuthorizer([$auth, 'authorize']);
unset($auth);
$db->exec('SELECT 1');

$capture = "closure-alive";
$closure = function (int $action, ...$args) use (&$capture, $db): int {
$db->setAuthorizer(null);
echo "closure: ", $capture, "\n";
return Pdo\Sqlite::OK;
};
$db->setAuthorizer($closure);
unset($closure);
$db->exec('SELECT 2');

$db->exec('SELECT 3');
echo "post-disable query ok\n";
?>
--EXPECT--
method: alive
closure: closure-alive
post-disable query ok
27 changes: 27 additions & 0 deletions ext/spl/tests/autoloading/gh22060.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
GH-22060 (Class autoloader $this freed via spl_autoload_unregister during dispatch)
--FILE--
<?php

class Loader {
public string $data = "loader-data";

public function load(string $class): void {
spl_autoload_unregister([$this, 'load']);
echo $this->data, "\n";
}
}

$obj = new Loader();
spl_autoload_register([$obj, 'load']);
unset($obj);

try {
new NonExistentClass42();
} catch (\Throwable $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}
?>
--EXPECT--
loader-data
Error: Class "NonExistentClass42" not found
40 changes: 40 additions & 0 deletions ext/sqlite3/tests/gh22122.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
GH-22122 (Use-after-free in SQLite3 authorizer when callback releases the authorizer)
--EXTENSIONS--
sqlite3
--FILE--
<?php
$db = new SQLite3(':memory:');

class Auth {
public string $state = "alive";

public function authorize(int $action, ...$args): int {
global $db;
$db->setAuthorizer(null);
echo "method: ", $this->state, "\n";
return SQLite3::OK;
}
}
$auth = new Auth();
$db->setAuthorizer([$auth, 'authorize']);
unset($auth);
$db->exec('SELECT 1');

$capture = "closure-alive";
$closure = function (int $action, ...$args) use (&$capture, $db): int {
$db->setAuthorizer(null);
echo "closure: ", $capture, "\n";
return SQLite3::OK;
};
$db->setAuthorizer($closure);
unset($closure);
$db->exec('SELECT 2');

$db->exec('SELECT 3');
echo "post-disable query ok\n";
?>
--EXPECT--
method: alive
closure: closure-alive
post-disable query ok
Loading