[PHP-CVS] [php-src] master: Zend: is_callable() wrongly accepts objects with no get_closure handler.

[email protected] (David Carlier)
Newsgroups php.cvs
Message-ID <[email protected]>
Author: David Carlier (devnexen)
Date: 2026-08-08T18:50:19+01:00

Commit: https://github.com/php/php-src/commit/bb0ab98b9d2f415caa35398099cecff8f3247ac3
Raw diff: https://github.com/php/php-src/commit/bb0ab98b9d2f415caa35398099cecff8f3247ac3.diff

Zend: is_callable() wrongly accepts objects with no get_closure handler.

Fix #23121

zend_is_callable_at_frame() tested `get_closure && get_closure(...) == FAILURE`,
so a NULL handler short circuited past the error branch into the success path,
leaving fcc->function_handler NULL. zend_fcc_addref() then asserted and
call_user_func() dereferenced the null zend_function.

Close GH-23123

Changed paths:
  A  Zend/tests/gh23121.phpt
  M  NEWS
  M  Zend/zend_API.c


Diff:

diff --git a/NEWS b/NEWS
index 8c84bee9f71f..447d052e21dc 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,8 @@ PHP                                                                        NEWS
     possible. (NickSdot)
   . Fixed GH-23083 (SEGV build_trace_args in zend_exceptions.c with
     -d error_include_args=On). (David Carlier)
+  . Fixed GH-23121 (is_callable() wrongly accepts objects with no get_closure
+    handler). (David Carlier)
 
 - Curl:
   . Improved cURL option validation errors to include the option name.
diff --git a/Zend/tests/gh23121.phpt b/Zend/tests/gh23121.phpt
new file mode 100644
index 000000000000..158794ba04e3
--- /dev/null
+++ b/Zend/tests/gh23121.phpt
@@ -0,0 +1,36 @@
+--TEST--
+GH-23121 (Assertion failure in zend_fcc_addref() for an object whose class has no get_closure handler)
+--EXTENSIONS--
+simplexml
+--FILE--
+<?php
+$sxe = new SimpleXMLElement('<root/>');
+
+var_dump(is_callable($sxe));
+
+try {
+    $sxe();
+} catch (Error $e) {
+    echo $e->getMessage(), PHP_EOL;
+}
+
+try {
+    call_user_func($sxe);
+} catch (TypeError $e) {
+    echo $e->getMessage(), PHP_EOL;
+}
+
+try {
+    libxml_set_external_entity_loader($sxe);
+} catch (TypeError $e) {
+    echo $e->getMessage(), PHP_EOL;
+}
+
+var_dump(libxml_get_external_entity_loader());
+?>
+--EXPECT--
+bool(false)
+Object of type SimpleXMLElement is not callable
+call_user_func(): Argument #1 ($callback) must be a valid callback, no array or string given
+libxml_set_external_entity_loader(): Argument #1 ($resolver_function) must be a valid callback or null, no array or string given
+NULL
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index de3570c5e848..7d407f7e9053 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -4240,7 +4240,7 @@ ZEND_API bool zend_is_callable_at_frame(
 			}
 
 		case IS_OBJECT:
-			if (Z_OBJ_HANDLER_P(callable, get_closure) && Z_OBJ_HANDLER_P(callable, get_closure)(Z_OBJ_P(callable), &fcc->calling_scope, &fcc->function_handler, &fcc->object, 1) == FAILURE) {
+			if (!Z_OBJ_HANDLER_P(callable, get_closure) || Z_OBJ_HANDLER_P(callable, get_closure)(Z_OBJ_P(callable), &fcc->calling_scope, &fcc->function_handler, &fcc->object, 1) == FAILURE) {
 				if (error) *error = estrdup("no array or string given");
 				return 0;
 			}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.