[php-src] PHP-8.4: ext/opcache: opcache.interned_strings_buffer per FPM pool crashed on restart.

David Carlier <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: David Carlier (devnexen)
Date: 2026-08-20T22:27:04+01:00

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

ext/opcache: opcache.interned_strings_buffer per FPM pool crashed on restart.

Fix #23288

The directive was still accepted once the shared interned string table
had been sized from the master php.ini, so a diverging pool value made
the next restart run accel_interned_strings_restore_state() against a
table that was never allocated. Reject post-startup changes like
opcache.memory_consumption and gate the restore on the shared table
state instead of the per-process directive.

Close GH-23309

Changed paths:
  A  sapi/fpm/tests/gh23288-opcache-interned-strings-buffer-pool.phpt
  M  NEWS
  M  ext/opcache/ZendAccelerator.c
  M  ext/opcache/zend_accelerator_module.c


Diff:

diff --git a/NEWS b/NEWS
index a672360f0aef..86b6bd3e2e0c 100644
--- a/NEWS
+++ b/NEWS
@@ -31,6 +31,8 @@ PHP                                                                        NEWS
 
 - Opcache:
   . Fixed opcache.protect_memory race under ZTS. (realFlowControl)
+  . Fixed bug GH-23288 (Crash on restart when opcache.interned_strings_buffer
+    is overridden in an individual FPM pool). (David Carlier)
 
 - PDO:
   . Fixed a leak when a persistent connection failed a liveness check
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index a2d964c15070..221cc55d9f6d 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -2715,7 +2715,7 @@ ZEND_RINIT_FUNCTION(zend_accelerator)
 				zend_reset_cache_vars();
 				zend_accel_hash_clean(&ZCSG(hash));
 
-				if (ZCG(accel_directives).interned_strings_buffer) {
+				if (ZCSG(interned_strings).saved_top) {
 					accel_interned_strings_restore_state();
 				}
 
@@ -3444,6 +3444,8 @@ void accel_shutdown(void)
 	if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
 		ini_entry->on_modify = orig_include_path_on_modify;
 	}
+
+	accel_startup_ok = false;
 }
 
 void zend_accel_schedule_restart(zend_accel_restart_reason reason)
diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c
index ffa09aaf9e67..23192e8950bf 100644
--- a/ext/opcache/zend_accelerator_module.c
+++ b/ext/opcache/zend_accelerator_module.c
@@ -93,6 +93,15 @@ static ZEND_INI_MH(OnUpdateMemoryConsumption)
 
 static ZEND_INI_MH(OnUpdateInternedStringsBuffer)
 {
+	if (accel_startup_ok) {
+		if (strcmp(sapi_module.name, "fpm-fcgi") == 0) {
+			zend_accel_error(ACCEL_LOG_WARNING, "opcache.interned_strings_buffer cannot be changed when OPcache is already set up. Are you using php_admin_value[opcache.interned_strings_buffer] in an individual pool's configuration?\n");
+		} else {
+			zend_accel_error(ACCEL_LOG_WARNING, "opcache.interned_strings_buffer cannot be changed when OPcache is already set up.\n");
+		}
+		return FAILURE;
+	}
+
 	zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
 	zend_long size = zend_ini_parse_quantity_warn(new_value, entry->name);
 
diff --git a/sapi/fpm/tests/gh23288-opcache-interned-strings-buffer-pool.phpt b/sapi/fpm/tests/gh23288-opcache-interned-strings-buffer-pool.phpt
new file mode 100644
index 000000000000..8f48b3a8c209
--- /dev/null
+++ b/sapi/fpm/tests/gh23288-opcache-interned-strings-buffer-pool.phpt
@@ -0,0 +1,50 @@
+--TEST--
+FPM: GH-23288 - opcache.interned_strings_buffer overridden per pool must not crash on restart
+--EXTENSIONS--
+opcache
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+require_once "tester.inc";
+
+$cfg = <<<EOT
+[global]
+error_log = {{FILE:LOG}}
+[unconfined]
+listen = {{ADDR}}
+pm = static
+pm.max_children = 1
+php_admin_value[opcache.interned_strings_buffer] = 8
+EOT;
+
+$code = <<<EOT
+<?php
+opcache_reset();
+opcache_reset();
+echo "ok";
+EOT;
+
+$opcache = ini_get('extension_dir') . DIRECTORY_SEPARATOR . 'opcache.' . PHP_SHLIB_SUFFIX;
+$extraArgs = is_file($opcache) ? ['-dzend_extension=' . $opcache] : [];
+
+$tester = new FPM\Tester($cfg, $code);
+$tester->start($extraArgs, iniEntries: ['opcache.interned_strings_buffer' => '0']);
+$tester->expectLogStartNotices();
+$tester->request()->expectBody('ok');
+$tester->request()->expectBody('ok');
+$tester->request()->expectBody('ok');
+$tester->terminate();
+$tester->expectLogTerminatingNotices();
+$tester->close();
+
+?>
+Done
+--EXPECT--
+Done
+--CLEAN--
+<?php
+require_once "tester.inc";
+FPM\Tester::clean();
+?>
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.