mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00
Fix GH-17442: Engine UAF with reference assign and dtor
Closes GH-17443.
This commit is contained in:
parent
09791ed1d1
commit
b068c2ff94
9 changed files with 108 additions and 22 deletions
1
NEWS
1
NEWS
|
@ -17,6 +17,7 @@ PHP NEWS
|
|||
. Implement GH-15680 (Enhance zend_dump_op_array to properly represent
|
||||
non-printable characters in string literals). (nielsdos, WangYihang)
|
||||
. Add support for backtraces for fatal errors. (enorris)
|
||||
. Fixed bug GH-17442 (Engine UAF with reference assign and dtor). (nielsdos)
|
||||
|
||||
- Curl:
|
||||
. Added curl_multi_get_handles(). (timwolla)
|
||||
|
|
|
@ -14,6 +14,12 @@ PHP 8.5 INTERNALS UPGRADE NOTES
|
|||
1. Internal API changes
|
||||
========================
|
||||
|
||||
- Zend
|
||||
. Added zend_safe_assign_to_variable_noref() function to safely assign
|
||||
a value to a non-reference zval.
|
||||
. Added zval_ptr_safe_dtor() to safely destroy a zval when a destructor
|
||||
could interfere.
|
||||
|
||||
========================
|
||||
2. Build system changes
|
||||
========================
|
||||
|
|
22
Zend/tests/weakrefs/gh17442_1.phpt
Normal file
22
Zend/tests/weakrefs/gh17442_1.phpt
Normal file
|
@ -0,0 +1,22 @@
|
|||
--TEST--
|
||||
GH-17442 (Engine UAF with reference assign and dtor) - untyped
|
||||
--CREDITS--
|
||||
YuanchengJiang
|
||||
--FILE--
|
||||
<?php
|
||||
$map = new WeakMap;
|
||||
$obj = new stdClass;
|
||||
$map[$obj] = new class {
|
||||
function __destruct() {
|
||||
throw new Exception("Test");
|
||||
}
|
||||
};
|
||||
headers_sent($obj,$generator);
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Uncaught Exception: Test in %s:%d
|
||||
Stack trace:
|
||||
#0 [internal function]: class@anonymous->__destruct()
|
||||
#1 %s(%d): headers_sent(NULL, 0)
|
||||
#2 {main}
|
||||
thrown in %s on line %d
|
35
Zend/tests/weakrefs/gh17442_2.phpt
Normal file
35
Zend/tests/weakrefs/gh17442_2.phpt
Normal file
|
@ -0,0 +1,35 @@
|
|||
--TEST--
|
||||
GH-17442 (Engine UAF with reference assign and dtor) - typed
|
||||
--CREDITS--
|
||||
YuanchengJiang
|
||||
nielsdos
|
||||
--FILE--
|
||||
<?php
|
||||
$map = new WeakMap;
|
||||
|
||||
class Test {
|
||||
public stdClass|string $obj;
|
||||
}
|
||||
|
||||
$test = new Test;
|
||||
$test->obj = new stdClass;
|
||||
|
||||
$map[$test->obj] = new class {
|
||||
function __destruct() {
|
||||
global $test;
|
||||
var_dump($test->obj);
|
||||
throw new Exception("Test");
|
||||
}
|
||||
};
|
||||
|
||||
headers_sent($test->obj);
|
||||
?>
|
||||
--EXPECTF--
|
||||
string(0) ""
|
||||
|
||||
Fatal error: Uncaught Exception: Test in %s:%d
|
||||
Stack trace:
|
||||
#0 [internal function]: class@anonymous->__destruct()
|
||||
#1 %s(%d): headers_sent('')
|
||||
#2 {main}
|
||||
thrown in %s on line %d
|
|
@ -4666,8 +4666,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *val
|
|||
zval_ptr_dtor(val);
|
||||
return FAILURE;
|
||||
} else {
|
||||
zval_ptr_dtor(&ref->val);
|
||||
ZVAL_COPY_VALUE(&ref->val, val);
|
||||
zend_safe_assign_to_variable_noref(&ref->val, val);
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1107,7 +1107,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_NULL(_zv); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1129,7 +1129,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_FALSE(_zv); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1151,7 +1151,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_TRUE(_zv); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1173,7 +1173,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_BOOL(_zv, bval); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1195,7 +1195,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_LONG(_zv, lval); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1217,7 +1217,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_DOUBLE(_zv, dval); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1239,7 +1239,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_EMPTY_STRING(_zv); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1261,7 +1261,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_STR(_zv, str); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1283,7 +1283,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_NEW_STR(_zv, str); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1305,7 +1305,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_STRING(_zv, string); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1327,7 +1327,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_STRINGL(_zv, string, len); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1349,7 +1349,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_ARR(_zv, arr); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1371,7 +1371,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_RES(_zv, res); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1393,7 +1393,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_COPY_VALUE(_zv, other_zv); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1415,7 +1415,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_COPY_VALUE(_zv, other_zv); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1447,7 +1447,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
|
|||
} \
|
||||
_zv = &ref->val; \
|
||||
} \
|
||||
zval_ptr_dtor(_zv); \
|
||||
zval_ptr_safe_dtor(_zv); \
|
||||
ZVAL_COPY_VALUE(_zv, other_zv); \
|
||||
} while (0)
|
||||
|
||||
|
@ -1485,10 +1485,7 @@ static zend_always_inline zval *zend_try_array_init_size(zval *zv, uint32_t size
|
|||
}
|
||||
zv = &ref->val;
|
||||
}
|
||||
zval garbage;
|
||||
ZVAL_COPY_VALUE(&garbage, zv);
|
||||
ZVAL_NULL(zv);
|
||||
zval_ptr_dtor(&garbage);
|
||||
zval_ptr_safe_dtor(zv);
|
||||
ZVAL_ARR(zv, arr);
|
||||
return zv;
|
||||
}
|
||||
|
|
|
@ -207,6 +207,17 @@ static zend_always_inline zval* zend_assign_to_variable_ex(zval *variable_ptr, z
|
|||
return variable_ptr;
|
||||
}
|
||||
|
||||
static zend_always_inline void zend_safe_assign_to_variable_noref(zval *variable_ptr, zval *value) {
|
||||
if (Z_REFCOUNTED_P(variable_ptr)) {
|
||||
ZEND_ASSERT(Z_TYPE_P(variable_ptr) != IS_REFERENCE);
|
||||
zend_refcounted *ref = Z_COUNTED_P(variable_ptr);
|
||||
ZVAL_COPY_VALUE(variable_ptr, value);
|
||||
GC_DTOR_NO_REF(ref);
|
||||
} else {
|
||||
ZVAL_COPY_VALUE(variable_ptr, value);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API zend_result ZEND_FASTCALL zval_update_constant(zval *pp);
|
||||
ZEND_API zend_result ZEND_FASTCALL zval_update_constant_ex(zval *pp, zend_class_entry *scope);
|
||||
ZEND_API zend_result ZEND_FASTCALL zval_update_constant_with_ctx(zval *pp, zend_class_entry *scope, zend_ast_evaluate_ctx *ctx);
|
||||
|
|
|
@ -85,6 +85,20 @@ ZEND_API void zval_ptr_dtor(zval *zval_ptr) /* {{{ */
|
|||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API void zval_ptr_safe_dtor(zval *zval_ptr)
|
||||
{
|
||||
if (Z_REFCOUNTED_P(zval_ptr)) {
|
||||
zend_refcounted *ref = Z_COUNTED_P(zval_ptr);
|
||||
|
||||
if (GC_DELREF(ref) == 0) {
|
||||
ZVAL_NULL(zval_ptr);
|
||||
rc_dtor_func(ref);
|
||||
} else {
|
||||
gc_check_possible_root(ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API void zval_internal_ptr_dtor(zval *zval_ptr) /* {{{ */
|
||||
{
|
||||
if (Z_REFCOUNTED_P(zval_ptr)) {
|
||||
|
|
|
@ -78,6 +78,7 @@ static zend_always_inline void zval_ptr_dtor_str(zval *zval_ptr)
|
|||
}
|
||||
|
||||
ZEND_API void zval_ptr_dtor(zval *zval_ptr);
|
||||
ZEND_API void zval_ptr_safe_dtor(zval *zval_ptr);
|
||||
ZEND_API void zval_internal_ptr_dtor(zval *zvalue);
|
||||
|
||||
/* Kept for compatibility */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue