Commit b3a8e07fe56 for php
commit b3a8e07fe561f8300ba7a68c10a3bfd75ccdcf7a
Author: Nicolas Grekas <nicolas.grekas@gmail.com>
Date: Sun Nov 8 04:32:05 2026 +0000
Fix use-after-free in zend_compile_const_decl() under opcache (#25302)
When compiling a constant declaration with OPcache, the first interning
attempt may miss a string that another thread or process adds to shared
memory before the constant name is interned again for ZEND_DECLARE_CONST.
The second attempt then releases the original string, leaving
zend_register_seen_symbol() to read and addref freed memory.
Register the constant as a seen symbol before passing its name to the
literal so that the symbol table keeps the string alive during interning.
The race was observed with FrankenPHP when restarting workers that compile
the same files concurrently against fresh shared memory.
Closes #25302
diff --git a/NEWS b/NEWS
index 9ae8c7add12..a84629e1877 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,8 @@ PHP NEWS
hooked subclass). (Lazizbek Ergashev)
. Fixed bug GH-25299 (Exception string code is lost after unserialize()).
(Lazizbek Ergashev, David Carlier)
+ . Fixed bug GH-25302 (Use-after-free in zend_compile_const_decl() with
+ OPcache during concurrent compilation). (Nicolas Grekas)
- Curl:
. Fixed curl_reset() retaining CURLOPT_PRIVATE after reset. (lacatoire)
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 7e7e602f589..d3538a240a7 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -9483,12 +9483,15 @@ static void zend_compile_const_decl(zend_ast *ast) /* {{{ */
}
}
+ /* Register before the literal takes the name: interning it there may
+ * release it, when opcache has put the same string in shared memory
+ * since the call above. */
+ zend_register_seen_symbol(name, ZEND_SYMBOL_CONST);
+
name_node.op_type = IS_CONST;
ZVAL_STR(&name_node.u.constant, name);
zend_emit_op(NULL, ZEND_DECLARE_CONST, &name_node, &value_node);
-
- zend_register_seen_symbol(name, ZEND_SYMBOL_CONST);
}
}
/* }}}*/