Skip to content

Commit 37bf0db

Browse files
committed
ext/opcache: Mark request-time persistent strings as thread-local
During a request, opcache's interned-string handlers return a plain persistent string when the name is not already in shared memory. dl() registers a temporary module's function, class and property names this way, so under ZEND_RC_DEBUG the first refcount change on one of them (building an argument TypeError, ReflectionFunction::getName(), get_object_vars()) trips the persistent-string assertion. The strings belong to the request's temporary module; mark them GC_PERSISTENT_LOCAL.
1 parent d5696b8 commit 37bf0db

5 files changed

Lines changed: 63 additions & 7 deletions

File tree

‎ext/dl_test/dl_test.stub.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public function test(string $str = ""): string {}
2020

2121
class DlTestSuperClass {
2222
public int $a;
23+
public int $temporary = 0;
2324
public function test(string $str = ""): string {}
2425
}
2526

‎ext/dl_test/dl_test_arginfo.h‎

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
try {
3+
dl_test_test2([]);
4+
} catch (TypeError $e) {
5+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
6+
}
7+
8+
try {
9+
(new DlTest)->test([]);
10+
} catch (TypeError $e) {
11+
echo $e::class, ": ", $e->getMessage(), PHP_EOL;
12+
}
13+
14+
var_dump((new ReflectionFunction('dl_test_test2'))->getName());
15+
16+
var_dump(get_object_vars(new DlTestSubClass));
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
dl() under opcache: refcounting names declared by a temporary module
3+
--SKIPIF--
4+
<?php include __DIR__ . "/skip.inc"; ?>
5+
--EXTENSIONS--
6+
opcache
7+
--INI--
8+
enable_dl=1
9+
opcache.enable_cli=1
10+
--FILE--
11+
<?php
12+
dl(PHP_OS_FAMILY === 'Windows' ? 'php_dl_test.dll' : 'dl_test.so');
13+
include __DIR__ . '/opcache_temporary_strings.inc';
14+
?>
15+
--EXPECT--
16+
TypeError: dl_test_test2(): Argument #1 ($str) must be of type string, array given
17+
TypeError: DlTest::test(): Argument #1 ($str) must be of type string, array given
18+
string(13) "dl_test_test2"
19+
array(1) {
20+
["temporary"]=>
21+
int(0)
22+
}

‎ext/opcache/ZendAccelerator.c‎

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,9 @@ static zend_string* ZEND_FASTCALL accel_new_interned_string_for_php(zend_string
578578
return ret;
579579
}
580580
}
581+
if (!ZSTR_IS_INTERNED(str) && (GC_FLAGS(str) & GC_PERSISTENT)) {
582+
GC_MAKE_PERSISTENT_LOCAL(str);
583+
}
581584
return str;
582585
}
583586

@@ -602,19 +605,27 @@ static zend_always_inline zend_string *accel_find_interned_string_ex(zend_ulong
602605

603606
static zend_string* ZEND_FASTCALL accel_init_interned_string_for_php(const char *str, size_t size, bool permanent)
604607
{
608+
zend_string *ret;
609+
605610
if (ZCG(counted)) {
606611
zend_ulong h = zend_inline_hash_func(str, size);
607-
zend_string *ret = accel_find_interned_string_ex(h, str, size);
612+
ret = accel_find_interned_string_ex(h, str, size);
608613

609-
if (!ret) {
610-
ret = zend_string_init(str, size, permanent);
611-
ZSTR_H(ret) = h;
614+
if (ret) {
615+
return ret;
612616
}
613617

614-
return ret;
618+
ret = zend_string_init(str, size, permanent);
619+
ZSTR_H(ret) = h;
620+
} else {
621+
ret = zend_string_init(str, size, permanent);
622+
}
623+
624+
if (permanent) {
625+
GC_MAKE_PERSISTENT_LOCAL(ret);
615626
}
616627

617-
return zend_string_init(str, size, permanent);
628+
return ret;
618629
}
619630

620631
static inline void accel_copy_permanent_list_types(

0 commit comments

Comments
 (0)