Skip to content

Commit

Permalink
Fix phpGH-17442: Engine UAF with reference assign and dtor
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsdos committed Jan 26, 2025
1 parent 75d7684 commit 6852055
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 22 deletions.
5 changes: 5 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ PHP 8.5 INTERNALS UPGRADE NOTES
1. Internal API changes
========================

- Zend
. Added ZEND_SAFE_ASSIGN_VALUE() macro to safely assign a value to a zval.
. Added zval_ptr_safe_dtor() to safely destroy a zval when a destructor
could interfere.

========================
2. Build system changes
========================
Expand Down
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_VALUE(&ref->val, val);
return SUCCESS;
}
}
Expand Down
53 changes: 33 additions & 20 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,22 @@ ZEND_API zend_result zend_try_assign_typed_ref_res(zend_reference *ref, zend_res
ZEND_API zend_result zend_try_assign_typed_ref_zval(zend_reference *ref, zval *zv);
ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, bool strict);

#define ZEND_SAFE_ASSIGN_VALUE(zv, value) do { \
zval *_zv = zv; \
zval *_value = value; \
if (Z_REFCOUNTED_P(_zv)) { \
zend_refcounted *rc = Z_COUNTED_P(_zv); \
ZVAL_COPY_VALUE(_zv, _value); \
if (!GC_DELREF(rc)) { \
rc_dtor_func(rc); \
} else { \
gc_check_possible_root(rc); \
} \
} else { \
ZVAL_COPY_VALUE(_zv, _value); \
} \
} while (0)

#define _ZEND_TRY_ASSIGN_NULL(zv, is_ref) do { \
zval *_zv = zv; \
if (is_ref || UNEXPECTED(Z_ISREF_P(_zv))) { \
Expand All @@ -1107,7 +1123,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 +1145,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 +1167,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 +1189,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 +1211,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 +1233,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 +1255,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 +1277,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 +1299,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 +1321,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 +1343,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 +1365,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 +1387,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 +1409,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 +1431,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 +1463,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 +1501,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
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

0 comments on commit 6852055

Please sign in to comment.