Skip to content

Commit

Permalink
Fix GH-10011 (Trampoline autoloader will get reregistered and cannot …
Browse files Browse the repository at this point in the history
…be unregistered)

There are two issues to resolve:
 1. The FCC is not refetch when trying to unregister a trampoline
 2. Comparing the function pointer of trampolines is meaningless as they are reallocated, thus we need to compare the name of the function

Found while working on GH-8294

Closes GH-10033
  • Loading branch information
Girgias committed Dec 2, 2022
1 parent 93592ea commit 608ddb0
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 1 deletion.
3 changes: 2 additions & 1 deletion NEWS
Expand Up @@ -9,7 +9,6 @@ PHP NEWS
README.REDIST.BINS file). (Akama Hitoshi)
. Fixed bug GH-9650 (Can't initialize heap: [0x000001e7]). (Michael Voříšek)
. Fixed potentially undefined behavior in Windows ftok(3) emulation. (cmb)
. Fixed GH-9769 (Misleading error message for unpacking of objects). (jhdxr)

- Date:
. Fixed bug GH-9699 (DateTimeImmutable::diff differences in 8.1.10 onwards -
Expand Down Expand Up @@ -55,6 +54,8 @@ PHP NEWS

- SPL:
. Fixed GH-9883 (SplFileObject::__toString() reads next line). (Girgias)
. Fixed GH-10011 (Trampoline autoloader will get reregistered and cannot be
unregistered). (Girgias)

24 Nov 2022, PHP 8.1.13

Expand Down
17 changes: 17 additions & 0 deletions ext/spl/php_spl.c
Expand Up @@ -402,6 +402,16 @@ static autoload_func_info *autoload_func_info_from_fci(

static bool autoload_func_info_equals(
const autoload_func_info *alfi1, const autoload_func_info *alfi2) {
if (UNEXPECTED(
(alfi1->func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) &&
(alfi2->func_ptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)
)) {
return alfi1->obj == alfi2->obj
&& alfi1->ce == alfi2->ce
&& alfi1->closure == alfi2->closure
&& zend_string_equals(alfi1->func_ptr->common.function_name, alfi2->func_ptr->common.function_name)
;
}
return alfi1->func_ptr == alfi2->func_ptr
&& alfi1->obj == alfi2->obj
&& alfi1->ce == alfi2->ce
Expand Down Expand Up @@ -580,6 +590,13 @@ PHP_FUNCTION(spl_autoload_unregister)
RETURN_TRUE;
}

if (!fcc.function_handler) {
/* Call trampoline has been cleared by zpp. Refetch it, because we want to deal
* with it outselves. It is important that it is not refetched on every call,
* because calls may occur from different scopes. */
zend_is_callable_ex(&fci.function_name, NULL, 0, NULL, &fcc, NULL);
}

autoload_func_info *alfi = autoload_func_info_from_fci(&fci, &fcc);
Bucket *p = spl_find_registered_function(alfi);
autoload_func_info_destroy(alfi);
Expand Down
59 changes: 59 additions & 0 deletions ext/spl/tests/gh10011.phpt
@@ -0,0 +1,59 @@
--TEST--
Bug GH-10011 (Trampoline autoloader will get reregistered and cannot be unregistered)
--FILE--
<?php

class TrampolineTest {
public function __call(string $name, array $arguments) {
echo 'Trampoline for ', $name, PHP_EOL;
}
}
$o = new TrampolineTest();
$callback1 = [$o, 'trampoline1'];
$callback2 = [$o, 'trampoline2'];

spl_autoload_register($callback1);
spl_autoload_register($callback2);
spl_autoload_register($callback1); // 2nd call ignored

var_dump(spl_autoload_functions());

var_dump(class_exists("TestClass", true));

echo "Unregister trampoline:\n";
var_dump(spl_autoload_unregister($callback1));
var_dump(spl_autoload_unregister($callback1));
var_dump(spl_autoload_unregister($callback2));

var_dump(spl_autoload_functions());
var_dump(class_exists("TestClass", true));
?>
--EXPECT--
array(2) {
[0]=>
array(2) {
[0]=>
object(TrampolineTest)#1 (0) {
}
[1]=>
string(11) "trampoline1"
}
[1]=>
array(2) {
[0]=>
object(TrampolineTest)#1 (0) {
}
[1]=>
string(11) "trampoline2"
}
}
Trampoline for trampoline1
Trampoline for trampoline2
bool(false)
Unregister trampoline:
bool(true)
bool(false)
bool(true)
array(0) {
}
bool(false)

0 comments on commit 608ddb0

Please sign in to comment.