Skip to content

Commit 8538e2f

Browse files
committed
Merge branch 'PHP-8.4'
* PHP-8.4: Prevent operands from being released during comparison
2 parents 6600d0e + 28ed4e6 commit 8538e2f

File tree

7 files changed

+136
-8
lines changed

7 files changed

+136
-8
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.5.0beta1
44

5+
- Core:
6+
. Fixed bug GH-19305 (Operands may be being released during comparison).
7+
(Arnaud)
8+
59
- Opcache:
610
. Fixed bug GH-19301 (opcache build failure). (Remi)
711

Zend/tests/gh19305-001.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
GH-19305 001: Operands may be released during comparison
3+
--FILE--
4+
<?php
5+
6+
$a = (object)[
7+
'foo' => 'test',
8+
'bar' => 2,
9+
];
10+
$b = (object)[
11+
'foo' => new class {
12+
public function __toString() {
13+
global $a, $b;
14+
$a = $b = null;
15+
return '';
16+
}
17+
},
18+
'bar' => 2,
19+
];
20+
21+
// Comparison of $a->foo and $b->foo calls __toString(), which releases
22+
// both $a and $b.
23+
var_dump($a > $b);
24+
25+
?>
26+
--EXPECT--
27+
bool(true)

Zend/tests/gh19305-002.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
GH-19305 002: Operands may be released during comparison
3+
--FILE--
4+
<?php
5+
6+
$a = [
7+
'foo' => 'test',
8+
'bar' => 2,
9+
];
10+
$b = [
11+
'foo' => new class {
12+
public function __toString() {
13+
global $a, $b;
14+
$a = $b = null;
15+
return '';
16+
}
17+
},
18+
'bar' => 2,
19+
];
20+
21+
// Comparison of $a['foo'] and $b['foo'] calls __toString(), which releases
22+
// both $a and $b.
23+
var_dump($a > $b);
24+
25+
?>
26+
--EXPECT--
27+
bool(true)

Zend/tests/gh19305-003.phpt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
GH-19305 003: Operands may be released during comparison
3+
--SKIPIF--
4+
<?php
5+
if (!method_exists('ReflectionClass', 'newLazyGhost')) {
6+
die('skip No lazy objects');
7+
}
8+
?>
9+
--FILE--
10+
<?php
11+
12+
class C
13+
{
14+
public $s;
15+
}
16+
$r = new ReflectionClass(C::class);
17+
$o = $r->newLazyProxy(function () { return new C; });
18+
19+
// Comparison calls initializers, which releases $o
20+
var_dump($o >
21+
$r->newLazyGhost(function () {
22+
global $o;
23+
$o = null;
24+
}));
25+
26+
?>
27+
--EXPECT--
28+
bool(false)

Zend/zend_object_handlers.c

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,6 +2204,10 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
22042204
}
22052205
Z_PROTECT_RECURSION_P(o1);
22062206

2207+
GC_ADDREF(zobj1);
2208+
GC_ADDREF(zobj2);
2209+
int ret;
2210+
22072211
for (i = 0; i < zobj1->ce->default_properties_count; i++) {
22082212
zval *p1, *p2;
22092213

@@ -2218,31 +2222,45 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
22182222

22192223
if (Z_TYPE_P(p1) != IS_UNDEF) {
22202224
if (Z_TYPE_P(p2) != IS_UNDEF) {
2221-
int ret;
2222-
22232225
ret = zend_compare(p1, p2);
22242226
if (ret != 0) {
22252227
Z_UNPROTECT_RECURSION_P(o1);
2226-
return ret;
2228+
goto done;
22272229
}
22282230
} else {
22292231
Z_UNPROTECT_RECURSION_P(o1);
2230-
return 1;
2232+
ret = 1;
2233+
goto done;
22312234
}
22322235
} else {
22332236
if (Z_TYPE_P(p2) != IS_UNDEF) {
22342237
Z_UNPROTECT_RECURSION_P(o1);
2235-
return 1;
2238+
ret = 1;
2239+
goto done;
22362240
}
22372241
}
22382242
}
22392243

22402244
Z_UNPROTECT_RECURSION_P(o1);
2241-
return 0;
2245+
ret = 0;
2246+
2247+
done:
2248+
OBJ_RELEASE(zobj1);
2249+
OBJ_RELEASE(zobj2);
2250+
2251+
return ret;
22422252
} else {
2243-
return zend_compare_symbol_tables(
2253+
GC_ADDREF(zobj1);
2254+
GC_ADDREF(zobj2);
2255+
2256+
int ret = zend_compare_symbol_tables(
22442257
zend_std_get_properties_ex(zobj1),
22452258
zend_std_get_properties_ex(zobj2));
2259+
2260+
OBJ_RELEASE(zobj1);
2261+
OBJ_RELEASE(zobj2);
2262+
2263+
return ret;
22462264
}
22472265
}
22482266
/* }}} */

Zend/zend_operators.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3422,7 +3422,19 @@ static int hash_zval_compare_function(zval *z1, zval *z2) /* {{{ */
34223422

34233423
ZEND_API int ZEND_FASTCALL zend_compare_symbol_tables(HashTable *ht1, HashTable *ht2) /* {{{ */
34243424
{
3425-
return ht1 == ht2 ? 0 : zend_hash_compare(ht1, ht2, (compare_func_t) hash_zval_compare_function, 0);
3425+
if (ht1 == ht2) {
3426+
return 0;
3427+
}
3428+
3429+
GC_TRY_ADDREF(ht1);
3430+
GC_TRY_ADDREF(ht2);
3431+
3432+
int ret = zend_hash_compare(ht1, ht2, (compare_func_t) hash_zval_compare_function, 0);
3433+
3434+
GC_TRY_DTOR_NO_REF(ht1);
3435+
GC_TRY_DTOR_NO_REF(ht2);
3436+
3437+
return ret;
34263438
}
34273439
/* }}} */
34283440

Zend/zend_types.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,18 @@ static zend_always_inline uint8_t zval_get_type(const zval* pz) {
753753
} \
754754
} while (0)
755755

756+
#define GC_TRY_DTOR_NO_REF(p) \
757+
do { \
758+
zend_refcounted_h *_p = &(p)->gc; \
759+
if (!(_p->u.type_info & GC_IMMUTABLE)) { \
760+
if (zend_gc_delref(_p) == 0) { \
761+
rc_dtor_func((zend_refcounted *)_p); \
762+
} else { \
763+
gc_check_possible_root_no_ref((zend_refcounted *)_p); \
764+
} \
765+
} \
766+
} while (0)
767+
756768
#define GC_TYPE_MASK 0x0000000f
757769
#define GC_FLAGS_MASK 0x000003f0
758770
#define GC_INFO_MASK 0xfffffc00

0 commit comments

Comments
 (0)