Commit b25e63d95c0 for php.net
commit b25e63d95c0a07c76c84fa2ec40bdbe659fef879
Author: Gina Peter Banyard <girgias@php.net>
Date: Fri Aug 7 12:22:12 2026 +0100
Fix GH-23082: unserialize_callback_func can no longer be reset to its empty default at runtime (#23096)
By using OnUpdateStr INI handler rather than OnUpdateStrNotEmpty
diff --git a/ext/standard/tests/serialize/unserialize_callback_func/gh23082.phpt b/ext/standard/tests/serialize/unserialize_callback_func/gh23082.phpt
new file mode 100644
index 00000000000..adbc5e89265
--- /dev/null
+++ b/ext/standard/tests/serialize/unserialize_callback_func/gh23082.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Bug GH-23082: unserialize_callback_func can no longer be reset to its empty default at runtime
+--FILE--
+<?php
+
+function my_callback($name) { echo "callback fired for $name\n"; }
+
+// Save the current value and install our own, the usual save/restore idiom.
+$prev = ini_set('unserialize_callback_func', 'my_callback');
+var_dump($prev);
+
+// Restore it. $prev is "" here, since that is the default value.
+var_dump(ini_set('unserialize_callback_func', $prev));
+var_dump(ini_get('unserialize_callback_func'));
+
+// The callback is still installed and fires for unrelated code.
+$o = unserialize('O:20:"SomeNotExistingClass":0:{}');
+
+?>
+--EXPECT--
+string(0) ""
+string(11) "my_callback"
+string(0) ""
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index 27647c907d3..eca9660c560 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -1249,7 +1249,7 @@ object ":" uiv ":" ["] {
}
/* Check for unserialize callback */
- if (PG(unserialize_callback_func) == NULL) {
+ if (PG(unserialize_callback_func) == NULL || zend_string_equals(PG(unserialize_callback_func), zend_empty_string)) {
incomplete_class = 1;
ce = PHP_IC_ENTRY;
break;
diff --git a/main/main.c b/main/main.c
index 2eb55c5cff0..0539220de36 100644
--- a/main/main.c
+++ b/main/main.c
@@ -824,7 +824,7 @@ PHP_INI_BEGIN()
STD_PHP_INI_BOOLEAN("auto_globals_jit", "1", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateBool, auto_globals_jit, php_core_globals, core_globals)
STD_PHP_INI_BOOLEAN("short_open_tag", DEFAULT_SHORT_OPEN_TAG, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, short_tags, zend_compiler_globals, compiler_globals)
- STD_PHP_INI_ENTRY("unserialize_callback_func", NULL, PHP_INI_ALL, OnUpdateStrNotEmpty, unserialize_callback_func, php_core_globals, core_globals)
+ STD_PHP_INI_ENTRY("unserialize_callback_func", NULL, PHP_INI_ALL, OnUpdateStr, unserialize_callback_func, php_core_globals, core_globals)
STD_PHP_INI_ENTRY("serialize_precision", "-1", PHP_INI_ALL, OnSetSerializePrecision, serialize_precision, php_core_globals, core_globals)
STD_PHP_INI_ENTRY("arg_separator.output", "&", PHP_INI_ALL, OnUpdateStrNotEmpty, arg_separator.output, php_core_globals, core_globals)
STD_PHP_INI_ENTRY("arg_separator.input", "&", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateStrNotEmpty, arg_separator.input, php_core_globals, core_globals)