Avoid use after free in internal prop type verification

This issue only applies to debug builds: read_property can free
the object, but we'd try to check the object handlers afterwards.
Rewrite the check in a way that only accessed the object before
the read_property call.

Fixes oss-fuzz #38297.
This commit is contained in:
Nikita Popov 2021-09-09 15:28:06 +02:00
parent b514e550a2
commit 6381a16f3f
4 changed files with 158 additions and 35 deletions

View File

@ -0,0 +1,15 @@
--TEST--
Destroy object in magic __get()
--FILE--
<?php
class Test {
function __get($name) {
$GLOBALS["x"] = null;
}
}
$x = new Test;
var_dump($x->prop);
?>
--EXPECT--
NULL

View File

@ -1194,15 +1194,6 @@ ZEND_API ZEND_COLD void zend_internal_call_arginfo_violation(zend_function *fbc)
ZSTR_VAL(fbc->common.function_name));
}
static void zend_verify_internal_read_property_type(zend_object *obj, zend_string *name, zval *val)
{
zend_property_info *prop_info =
zend_get_property_info(obj->ce, name, /* silent */ true);
if (prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO && ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, val, /* strict */ true);
}
}
#ifndef ZEND_VERIFY_FUNC_INFO
# define ZEND_VERIFY_FUNC_INFO 0
#endif

View File

@ -2104,10 +2104,19 @@ ZEND_VM_C_LABEL(fetch_obj_r_fast_copy):
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif

View File

@ -6288,10 +6288,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -8608,10 +8617,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -10963,10 +10981,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -15386,10 +15413,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -16809,10 +16845,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -18124,10 +18169,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -31428,10 +31482,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -33286,10 +33349,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -35756,10 +35828,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -39895,10 +39976,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -43531,10 +43621,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif
@ -48562,10 +48661,19 @@ fetch_obj_r_fast_copy:
}
}
#if ZEND_DEBUG
/* For non-standard object handlers, verify a declared property type in debug builds.
* Fetch prop_info before calling read_property(), as it may deallocate the object. */
zend_property_info *prop_info = NULL;
if (zobj->handlers->read_property != zend_std_read_property) {
prop_info = zend_get_property_info(zobj->ce, name, /* silent */ true);
}
#endif
retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, EX_VAR(opline->result.var));
#if ZEND_DEBUG
if (!EG(exception) && zobj->handlers->read_property != zend_std_read_property) {
zend_verify_internal_read_property_type(zobj, name, retval);
if (!EG(exception) && prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO
&& ZEND_TYPE_IS_SET(prop_info->type)) {
zend_verify_property_type(prop_info, retval, /* strict */ true);
}
#endif