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)