Skip to content

Commit 02ee521

Browse files
committed
Fix phpGH-16727: Opcache bad signal 139 crash in ZTS bookworm (frankenphp)
Reproducer: php#16727 (comment) The root cause is a data race between two different threads: 1) We allocate a lower cased name for an anonymous class here: https://github.com/php/php-src/blob/f97353f228e21dcc2db24d7edf08c1cb3678b0fd/Zend/zend_compile.c#L8109 2) This gets looked up as an interned string here: https://github.com/php/php-src/blob/f97353f228e21dcc2db24d7edf08c1cb3678b0fd/Zend/zend_compile.c#L8112 Assuming that there are uppercase symbols in the string and therefore `lcname != name` and that `lcname` is not yet in the interned string table, the pointer value of `lcname` won't change. 3) Here we add the string into the interned string table: https://github.com/php/php-src/blob/f97353f228e21dcc2db24d7edf08c1cb3678b0fd/Zend/zend_compile.c#L8223 However, in the meantime another thread could've added the string into the interned string table. This means that the following code will run, indirectly called via the `LITERAL_STR` macro, freeing `lcname`: https://github.com/php/php-src/blob/62e53e6f4965f37d379a3fd21f65a4210c5c86b5/ext/opcache/ZendAccelerator.c#L572-L575 4) In the reproducer we then access the freed `lcname` string here: https://github.com/php/php-src/blob/f97353f228e21dcc2db24d7edf08c1cb3678b0fd/Zend/zend_compile.c#L8229 This is solved in my patch by retrieving the interned string pointer and putting it in `lcname`. Closes phpGH-16748.
1 parent 1b379f5 commit 02ee521

File tree

2 files changed

+9
-1
lines changed

2 files changed

+9
-1
lines changed

NEWS

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ PHP NEWS
77

88
- Core:
99
. Fail early in *nix configuration build script. (hakre)
10+
. Fixed bug GH-16727 (Opcache bad signal 139 crash in ZTS bookworm
11+
(frankenphp)). (nielsdos)
1012

1113
- FPM:
1214
. Fixed GH-16432 (PHP-FPM 8.2 SIGSEGV in fpm_get_status). (Jakub Zelenka)

Zend/zend_compile.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -8043,7 +8043,13 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel)
80438043
}
80448044

80458045
opline->op1_type = IS_CONST;
8046-
LITERAL_STR(opline->op1, lcname);
8046+
/* It's possible that `lcname` is not an interned string because it was not yet in the interned string table.
8047+
* However, by this point another thread may have caused `lcname` to be added in the interned string table.
8048+
* This will cause `lcname` to get freed once it is found in the interned string table. If we were to use
8049+
* LITERAL_STR() here we would not change the `lcname` pointer to the new value, and it would point to the
8050+
* now-freed string. This will cause issues when we use `lcname` in the code below. We solve this by using
8051+
* zend_add_literal_string() which gives us the new value. */
8052+
opline->op1.constant = zend_add_literal_string(&lcname);
80478053

80488054
if (decl->flags & ZEND_ACC_ANON_CLASS) {
80498055
opline->opcode = ZEND_DECLARE_ANON_CLASS;

0 commit comments

Comments
 (0)