mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3: Prevent operands from being released during comparison
This commit is contained in:
2
NEWS
2
NEWS
@@ -13,6 +13,8 @@ PHP NEWS
|
|||||||
unpacking). (ilutov)
|
unpacking). (ilutov)
|
||||||
. Fixed OSS-Fuzz #434346548 (Failed assertion with throwing __toString in
|
. Fixed OSS-Fuzz #434346548 (Failed assertion with throwing __toString in
|
||||||
binary const expr). (ilutov)
|
binary const expr). (ilutov)
|
||||||
|
. Fixed bug GH-19305 (Operands may be being released during comparison).
|
||||||
|
(Arnaud)
|
||||||
|
|
||||||
- FTP:
|
- FTP:
|
||||||
. Fix theoretical issues with hrtime() not being available. (nielsdos)
|
. Fix theoretical issues with hrtime() not being available. (nielsdos)
|
||||||
|
|||||||
27
Zend/tests/gh19305-001.phpt
Normal file
27
Zend/tests/gh19305-001.phpt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-19305 001: Operands may be released during comparison
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$a = (object)[
|
||||||
|
'foo' => 'test',
|
||||||
|
'bar' => 2,
|
||||||
|
];
|
||||||
|
$b = (object)[
|
||||||
|
'foo' => new class {
|
||||||
|
public function __toString() {
|
||||||
|
global $a, $b;
|
||||||
|
$a = $b = null;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'bar' => 2,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Comparison of $a->foo and $b->foo calls __toString(), which releases
|
||||||
|
// both $a and $b.
|
||||||
|
var_dump($a > $b);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
27
Zend/tests/gh19305-002.phpt
Normal file
27
Zend/tests/gh19305-002.phpt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-19305 002: Operands may be released during comparison
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$a = [
|
||||||
|
'foo' => 'test',
|
||||||
|
'bar' => 2,
|
||||||
|
];
|
||||||
|
$b = [
|
||||||
|
'foo' => new class {
|
||||||
|
public function __toString() {
|
||||||
|
global $a, $b;
|
||||||
|
$a = $b = null;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'bar' => 2,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Comparison of $a['foo'] and $b['foo'] calls __toString(), which releases
|
||||||
|
// both $a and $b.
|
||||||
|
var_dump($a > $b);
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(true)
|
||||||
28
Zend/tests/gh19305-003.phpt
Normal file
28
Zend/tests/gh19305-003.phpt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
--TEST--
|
||||||
|
GH-19305 003: Operands may be released during comparison
|
||||||
|
--SKIPIF--
|
||||||
|
<?php
|
||||||
|
if (!method_exists('ReflectionClass', 'newLazyGhost')) {
|
||||||
|
die('skip No lazy objects');
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class C
|
||||||
|
{
|
||||||
|
public $s;
|
||||||
|
}
|
||||||
|
$r = new ReflectionClass(C::class);
|
||||||
|
$o = $r->newLazyProxy(function () { return new C; });
|
||||||
|
|
||||||
|
// Comparison calls initializers, which releases $o
|
||||||
|
var_dump($o >
|
||||||
|
$r->newLazyGhost(function () {
|
||||||
|
global $o;
|
||||||
|
$o = null;
|
||||||
|
}));
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
bool(false)
|
||||||
@@ -2202,6 +2202,10 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
|
|||||||
}
|
}
|
||||||
Z_PROTECT_RECURSION_P(o1);
|
Z_PROTECT_RECURSION_P(o1);
|
||||||
|
|
||||||
|
GC_ADDREF(zobj1);
|
||||||
|
GC_ADDREF(zobj2);
|
||||||
|
int ret;
|
||||||
|
|
||||||
for (i = 0; i < zobj1->ce->default_properties_count; i++) {
|
for (i = 0; i < zobj1->ce->default_properties_count; i++) {
|
||||||
zval *p1, *p2;
|
zval *p1, *p2;
|
||||||
|
|
||||||
@@ -2216,31 +2220,45 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
|
|||||||
|
|
||||||
if (Z_TYPE_P(p1) != IS_UNDEF) {
|
if (Z_TYPE_P(p1) != IS_UNDEF) {
|
||||||
if (Z_TYPE_P(p2) != IS_UNDEF) {
|
if (Z_TYPE_P(p2) != IS_UNDEF) {
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = zend_compare(p1, p2);
|
ret = zend_compare(p1, p2);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
Z_UNPROTECT_RECURSION_P(o1);
|
Z_UNPROTECT_RECURSION_P(o1);
|
||||||
return ret;
|
goto done;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Z_UNPROTECT_RECURSION_P(o1);
|
Z_UNPROTECT_RECURSION_P(o1);
|
||||||
return 1;
|
ret = 1;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (Z_TYPE_P(p2) != IS_UNDEF) {
|
if (Z_TYPE_P(p2) != IS_UNDEF) {
|
||||||
Z_UNPROTECT_RECURSION_P(o1);
|
Z_UNPROTECT_RECURSION_P(o1);
|
||||||
return 1;
|
ret = 1;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Z_UNPROTECT_RECURSION_P(o1);
|
Z_UNPROTECT_RECURSION_P(o1);
|
||||||
return 0;
|
ret = 0;
|
||||||
|
|
||||||
|
done:
|
||||||
|
OBJ_RELEASE(zobj1);
|
||||||
|
OBJ_RELEASE(zobj2);
|
||||||
|
|
||||||
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
return zend_compare_symbol_tables(
|
GC_ADDREF(zobj1);
|
||||||
|
GC_ADDREF(zobj2);
|
||||||
|
|
||||||
|
int ret = zend_compare_symbol_tables(
|
||||||
zend_std_get_properties_ex(zobj1),
|
zend_std_get_properties_ex(zobj1),
|
||||||
zend_std_get_properties_ex(zobj2));
|
zend_std_get_properties_ex(zobj2));
|
||||||
|
|
||||||
|
OBJ_RELEASE(zobj1);
|
||||||
|
OBJ_RELEASE(zobj2);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|||||||
@@ -3405,7 +3405,19 @@ static int hash_zval_compare_function(zval *z1, zval *z2) /* {{{ */
|
|||||||
|
|
||||||
ZEND_API int ZEND_FASTCALL zend_compare_symbol_tables(HashTable *ht1, HashTable *ht2) /* {{{ */
|
ZEND_API int ZEND_FASTCALL zend_compare_symbol_tables(HashTable *ht1, HashTable *ht2) /* {{{ */
|
||||||
{
|
{
|
||||||
return ht1 == ht2 ? 0 : zend_hash_compare(ht1, ht2, (compare_func_t) hash_zval_compare_function, 0);
|
if (ht1 == ht2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GC_TRY_ADDREF(ht1);
|
||||||
|
GC_TRY_ADDREF(ht2);
|
||||||
|
|
||||||
|
int ret = zend_hash_compare(ht1, ht2, (compare_func_t) hash_zval_compare_function, 0);
|
||||||
|
|
||||||
|
GC_TRY_DTOR_NO_REF(ht1);
|
||||||
|
GC_TRY_DTOR_NO_REF(ht2);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
|||||||
@@ -733,6 +733,18 @@ static zend_always_inline uint8_t zval_get_type(const zval* pz) {
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define GC_TRY_DTOR_NO_REF(p) \
|
||||||
|
do { \
|
||||||
|
zend_refcounted_h *_p = &(p)->gc; \
|
||||||
|
if (!(_p->u.type_info & GC_IMMUTABLE)) { \
|
||||||
|
if (zend_gc_delref(_p) == 0) { \
|
||||||
|
rc_dtor_func((zend_refcounted *)_p); \
|
||||||
|
} else { \
|
||||||
|
gc_check_possible_root_no_ref((zend_refcounted *)_p); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define GC_TYPE_MASK 0x0000000f
|
#define GC_TYPE_MASK 0x0000000f
|
||||||
#define GC_FLAGS_MASK 0x000003f0
|
#define GC_FLAGS_MASK 0x000003f0
|
||||||
#define GC_INFO_MASK 0xfffffc00
|
#define GC_INFO_MASK 0xfffffc00
|
||||||
|
|||||||
Reference in New Issue
Block a user