Skip to content

Commit d56ec0a

Browse files
committed
Merge branch 'PHP-8.1'
* PHP-8.1: Fixed bug #81607 (CE_CACHE allocation with concurrent access)
2 parents 976f569 + 76548e5 commit d56ec0a

File tree

8 files changed

+65
-21
lines changed

8 files changed

+65
-21
lines changed

Zend/zend_execute_API.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,10 +1062,12 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
10621062
zval *zv;
10631063
zend_string *lc_name;
10641064
zend_string *autoload_name;
1065+
uint32_t ce_cache = 0;
10651066

1066-
if (ZSTR_HAS_CE_CACHE(name)) {
1067-
ce = ZSTR_GET_CE_CACHE(name);
1068-
if (ce) {
1067+
if (ZSTR_HAS_CE_CACHE(name) && ZSTR_VALID_CE_CACHE(name)) {
1068+
ce_cache = GC_REFCOUNT(name);
1069+
ce = GET_CE_CACHE(ce_cache);
1070+
if (EXPECTED(ce)) {
10691071
return ce;
10701072
}
10711073
}
@@ -1106,9 +1108,9 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
11061108
}
11071109
/* Don't populate CE_CACHE for mutable classes during compilation.
11081110
* The class may be freed while persisting. */
1109-
if (ZSTR_HAS_CE_CACHE(name) &&
1111+
if (ce_cache &&
11101112
(!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) {
1111-
ZSTR_SET_CE_CACHE(name, ce);
1113+
SET_CE_CACHE(ce_cache, ce);
11121114
}
11131115
return ce;
11141116
}
@@ -1164,8 +1166,8 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
11641166
}
11651167
if (ce) {
11661168
ZEND_ASSERT(!CG(in_compilation));
1167-
if (ZSTR_HAS_CE_CACHE(name)) {
1168-
ZSTR_SET_CE_CACHE(name, ce);
1169+
if (ce_cache) {
1170+
SET_CE_CACHE(ce_cache, ce);
11691171
}
11701172
}
11711173
return ce;

Zend/zend_inheritance.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2768,9 +2768,6 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
27682768
}
27692769
zv = zend_hash_find_known_hash(CG(class_table), key);
27702770
Z_CE_P(zv) = ret;
2771-
if (ZSTR_HAS_CE_CACHE(ret->name)) {
2772-
ZSTR_SET_CE_CACHE(ret->name, ret);
2773-
}
27742771
return ret;
27752772
}
27762773

@@ -2995,9 +2992,6 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
29952992
if (UNEXPECTED(!register_early_bound_ce(delayed_early_binding, lcname, ret))) {
29962993
return NULL;
29972994
}
2998-
if (ZSTR_HAS_CE_CACHE(ret->name)) {
2999-
ZSTR_SET_CE_CACHE(ret->name, ret);
3000-
}
30012995
return ret;
30022996
}
30032997
} else {

Zend/zend_types.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -726,10 +726,25 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
726726

727727
/* Fast class cache */
728728
#define ZSTR_HAS_CE_CACHE(s) (GC_FLAGS(s) & IS_STR_CLASS_NAME_MAP_PTR)
729-
#define ZSTR_GET_CE_CACHE(s) \
730-
(*(zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(GC_REFCOUNT(s)))
731-
#define ZSTR_SET_CE_CACHE(s, ce) do { \
732-
*((zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(GC_REFCOUNT(s))) = ce; \
729+
#define ZSTR_GET_CE_CACHE(s) ZSTR_GET_CE_CACHE_EX(s, 1)
730+
#define ZSTR_SET_CE_CACHE(s, ce) ZSTR_SET_CE_CACHE_EX(s, ce, 1)
731+
732+
#define ZSTR_VALID_CE_CACHE(s) EXPECTED((GC_REFCOUNT(s)-1)/sizeof(void *) < CG(map_ptr_last))
733+
734+
#define ZSTR_GET_CE_CACHE_EX(s, validate) \
735+
((!(validate) || ZSTR_VALID_CE_CACHE(s)) ? GET_CE_CACHE(GC_REFCOUNT(s)) : NULL)
736+
737+
#define ZSTR_SET_CE_CACHE_EX(s, ce, validate) do { \
738+
if (!(validate) || ZSTR_VALID_CE_CACHE(s)) { \
739+
SET_CE_CACHE(GC_REFCOUNT(s), ce); \
740+
} \
741+
} while (0)
742+
743+
#define GET_CE_CACHE(ce_cache) \
744+
(*(zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache))
745+
746+
#define SET_CE_CACHE(ce_cache, ce) do { \
747+
*((zend_class_entry **)ZEND_MAP_PTR_OFFSET2PTR(ce_cache)) = ce; \
733748
} while (0)
734749

735750
/* Recursion protection macros must be used only for arrays and objects */

ext/opcache/ZendAccelerator.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,11 +2286,15 @@ static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce,
22862286
entry = zend_accel_inheritance_cache_find(entry, ce, parent, traits_and_interfaces, &needs_autoload);
22872287
if (entry) {
22882288
if (!needs_autoload) {
2289+
replay_warnings(entry->num_warnings, entry->warnings);
22892290
if (ZCSG(map_ptr_last) > CG(map_ptr_last)) {
22902291
zend_map_ptr_extend(ZCSG(map_ptr_last));
22912292
}
2292-
replay_warnings(entry->num_warnings, entry->warnings);
2293-
return entry->ce;
2293+
ce = entry->ce;
2294+
if (ZSTR_HAS_CE_CACHE(ce->name)) {
2295+
ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0);
2296+
}
2297+
return ce;
22942298
}
22952299

22962300
for (i = 0; i < entry->dependencies_count; i++) {

ext/opcache/tests/bug81607.inc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
class FooBar {}

ext/opcache/tests/bug81607.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Bug #81607: CE_CACHE allocation with concurrent access
3+
--EXTENSIONS--
4+
opcache
5+
pcntl
6+
--INI--
7+
opcache.enable_cli=1
8+
--FILE--
9+
<?php
10+
11+
$pid = pcntl_fork();
12+
if ($pid == 0) {
13+
// Child: Declare class FooBar {} to allocate CE cache slot.
14+
require __DIR__ . '/bug81607.inc';
15+
} else if ($pid > 0) {
16+
pcntl_wait($status);
17+
var_dump(new FooBar);
18+
} else {
19+
echo "pcntl_fork() failed\n";
20+
}
21+
22+
?>
23+
--EXPECTF--
24+
Fatal error: Uncaught Error: Class "FooBar" not found in %s:%d
25+
Stack trace:
26+
#0 {main}
27+
thrown in %s on line %d

ext/opcache/zend_accelerator_util_funcs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ static void zend_accel_class_hash_copy(HashTable *target, HashTable *source)
220220
if ((ce->ce_flags & ZEND_ACC_LINKED)
221221
&& ZSTR_HAS_CE_CACHE(ce->name)
222222
&& ZSTR_VAL(p->key)[0]) {
223-
ZSTR_SET_CE_CACHE(ce->name, ce);
223+
ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0);
224224
}
225225
}
226226
}

ext/opcache/zend_persist.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,7 @@ zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce)
889889

890890
if (!(ce->ce_flags & ZEND_ACC_CACHED)) {
891891
if (ZSTR_HAS_CE_CACHE(ce->name)) {
892-
ZSTR_SET_CE_CACHE(ce->name, NULL);
892+
ZSTR_SET_CE_CACHE_EX(ce->name, NULL, 0);
893893
}
894894
zend_accel_store_interned_string(ce->name);
895895
if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)

0 commit comments

Comments
 (0)