Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -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
========================
Expand Down
22 changes: 22 additions & 0 deletions Zend/tests/weakrefs/gh17442_1.phpt
Original file line number Diff line number Diff line change
@@ -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 changes: 35 additions & 0 deletions Zend/tests/weakrefs/gh17442_2.phpt
Original file line number Diff line number Diff line change
@@ -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
3 changes: 1 addition & 2 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
37 changes: 17 additions & 20 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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;
}
Expand Down
15 changes: 15 additions & 0 deletions Zend/zend_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,21 @@ 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);
if (!GC_DELREF(ref)) {
rc_dtor_func(ref);
} else {
gc_check_possible_root(ref);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use gc_check_possible_root_no_ref() or GC_DTOR_NO_REF()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I don't know why I forgot this.

}
} 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);
Expand Down
14 changes: 14 additions & 0 deletions Zend/zend_variables.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_variables.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
Loading