Skip to content

Commit b068c2f

Browse files
committed
Fix GH-17442: Engine UAF with reference assign and dtor
Closes GH-17443.
1 parent 09791ed commit b068c2f

9 files changed

+108
-22
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ PHP NEWS
1717
. Implement GH-15680 (Enhance zend_dump_op_array to properly represent
1818
non-printable characters in string literals). (nielsdos, WangYihang)
1919
. Add support for backtraces for fatal errors. (enorris)
20+
. Fixed bug GH-17442 (Engine UAF with reference assign and dtor). (nielsdos)
2021

2122
- Curl:
2223
. Added curl_multi_get_handles(). (timwolla)

UPGRADING.INTERNALS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ PHP 8.5 INTERNALS UPGRADE NOTES
1414
1. Internal API changes
1515
========================
1616

17+
- Zend
18+
. Added zend_safe_assign_to_variable_noref() function to safely assign
19+
a value to a non-reference zval.
20+
. Added zval_ptr_safe_dtor() to safely destroy a zval when a destructor
21+
could interfere.
22+
1723
========================
1824
2. Build system changes
1925
========================

Zend/tests/weakrefs/gh17442_1.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
GH-17442 (Engine UAF with reference assign and dtor) - untyped
3+
--CREDITS--
4+
YuanchengJiang
5+
--FILE--
6+
<?php
7+
$map = new WeakMap;
8+
$obj = new stdClass;
9+
$map[$obj] = new class {
10+
function __destruct() {
11+
throw new Exception("Test");
12+
}
13+
};
14+
headers_sent($obj,$generator);
15+
?>
16+
--EXPECTF--
17+
Fatal error: Uncaught Exception: Test in %s:%d
18+
Stack trace:
19+
#0 [internal function]: class@anonymous->__destruct()
20+
#1 %s(%d): headers_sent(NULL, 0)
21+
#2 {main}
22+
thrown in %s on line %d

Zend/tests/weakrefs/gh17442_2.phpt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
GH-17442 (Engine UAF with reference assign and dtor) - typed
3+
--CREDITS--
4+
YuanchengJiang
5+
nielsdos
6+
--FILE--
7+
<?php
8+
$map = new WeakMap;
9+
10+
class Test {
11+
public stdClass|string $obj;
12+
}
13+
14+
$test = new Test;
15+
$test->obj = new stdClass;
16+
17+
$map[$test->obj] = new class {
18+
function __destruct() {
19+
global $test;
20+
var_dump($test->obj);
21+
throw new Exception("Test");
22+
}
23+
};
24+
25+
headers_sent($test->obj);
26+
?>
27+
--EXPECTF--
28+
string(0) ""
29+
30+
Fatal error: Uncaught Exception: Test in %s:%d
31+
Stack trace:
32+
#0 [internal function]: class@anonymous->__destruct()
33+
#1 %s(%d): headers_sent('')
34+
#2 {main}
35+
thrown in %s on line %d

Zend/zend_API.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4666,8 +4666,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_ex(zend_reference *ref, zval *val
46664666
zval_ptr_dtor(val);
46674667
return FAILURE;
46684668
} else {
4669-
zval_ptr_dtor(&ref->val);
4670-
ZVAL_COPY_VALUE(&ref->val, val);
4669+
zend_safe_assign_to_variable_noref(&ref->val, val);
46714670
return SUCCESS;
46724671
}
46734672
}

Zend/zend_API.h

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
11071107
} \
11081108
_zv = &ref->val; \
11091109
} \
1110-
zval_ptr_dtor(_zv); \
1110+
zval_ptr_safe_dtor(_zv); \
11111111
ZVAL_NULL(_zv); \
11121112
} while (0)
11131113

@@ -1129,7 +1129,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
11291129
} \
11301130
_zv = &ref->val; \
11311131
} \
1132-
zval_ptr_dtor(_zv); \
1132+
zval_ptr_safe_dtor(_zv); \
11331133
ZVAL_FALSE(_zv); \
11341134
} while (0)
11351135

@@ -1151,7 +1151,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
11511151
} \
11521152
_zv = &ref->val; \
11531153
} \
1154-
zval_ptr_dtor(_zv); \
1154+
zval_ptr_safe_dtor(_zv); \
11551155
ZVAL_TRUE(_zv); \
11561156
} while (0)
11571157

@@ -1173,7 +1173,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
11731173
} \
11741174
_zv = &ref->val; \
11751175
} \
1176-
zval_ptr_dtor(_zv); \
1176+
zval_ptr_safe_dtor(_zv); \
11771177
ZVAL_BOOL(_zv, bval); \
11781178
} while (0)
11791179

@@ -1195,7 +1195,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
11951195
} \
11961196
_zv = &ref->val; \
11971197
} \
1198-
zval_ptr_dtor(_zv); \
1198+
zval_ptr_safe_dtor(_zv); \
11991199
ZVAL_LONG(_zv, lval); \
12001200
} while (0)
12011201

@@ -1217,7 +1217,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
12171217
} \
12181218
_zv = &ref->val; \
12191219
} \
1220-
zval_ptr_dtor(_zv); \
1220+
zval_ptr_safe_dtor(_zv); \
12211221
ZVAL_DOUBLE(_zv, dval); \
12221222
} while (0)
12231223

@@ -1239,7 +1239,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
12391239
} \
12401240
_zv = &ref->val; \
12411241
} \
1242-
zval_ptr_dtor(_zv); \
1242+
zval_ptr_safe_dtor(_zv); \
12431243
ZVAL_EMPTY_STRING(_zv); \
12441244
} while (0)
12451245

@@ -1261,7 +1261,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
12611261
} \
12621262
_zv = &ref->val; \
12631263
} \
1264-
zval_ptr_dtor(_zv); \
1264+
zval_ptr_safe_dtor(_zv); \
12651265
ZVAL_STR(_zv, str); \
12661266
} while (0)
12671267

@@ -1283,7 +1283,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
12831283
} \
12841284
_zv = &ref->val; \
12851285
} \
1286-
zval_ptr_dtor(_zv); \
1286+
zval_ptr_safe_dtor(_zv); \
12871287
ZVAL_NEW_STR(_zv, str); \
12881288
} while (0)
12891289

@@ -1305,7 +1305,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
13051305
} \
13061306
_zv = &ref->val; \
13071307
} \
1308-
zval_ptr_dtor(_zv); \
1308+
zval_ptr_safe_dtor(_zv); \
13091309
ZVAL_STRING(_zv, string); \
13101310
} while (0)
13111311

@@ -1327,7 +1327,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
13271327
} \
13281328
_zv = &ref->val; \
13291329
} \
1330-
zval_ptr_dtor(_zv); \
1330+
zval_ptr_safe_dtor(_zv); \
13311331
ZVAL_STRINGL(_zv, string, len); \
13321332
} while (0)
13331333

@@ -1349,7 +1349,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
13491349
} \
13501350
_zv = &ref->val; \
13511351
} \
1352-
zval_ptr_dtor(_zv); \
1352+
zval_ptr_safe_dtor(_zv); \
13531353
ZVAL_ARR(_zv, arr); \
13541354
} while (0)
13551355

@@ -1371,7 +1371,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
13711371
} \
13721372
_zv = &ref->val; \
13731373
} \
1374-
zval_ptr_dtor(_zv); \
1374+
zval_ptr_safe_dtor(_zv); \
13751375
ZVAL_RES(_zv, res); \
13761376
} while (0)
13771377

@@ -1393,7 +1393,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
13931393
} \
13941394
_zv = &ref->val; \
13951395
} \
1396-
zval_ptr_dtor(_zv); \
1396+
zval_ptr_safe_dtor(_zv); \
13971397
ZVAL_COPY_VALUE(_zv, other_zv); \
13981398
} while (0)
13991399

@@ -1415,7 +1415,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
14151415
} \
14161416
_zv = &ref->val; \
14171417
} \
1418-
zval_ptr_dtor(_zv); \
1418+
zval_ptr_safe_dtor(_zv); \
14191419
ZVAL_COPY_VALUE(_zv, other_zv); \
14201420
} while (0)
14211421

@@ -1447,7 +1447,7 @@ ZEND_API zend_result zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval
14471447
} \
14481448
_zv = &ref->val; \
14491449
} \
1450-
zval_ptr_dtor(_zv); \
1450+
zval_ptr_safe_dtor(_zv); \
14511451
ZVAL_COPY_VALUE(_zv, other_zv); \
14521452
} while (0)
14531453

@@ -1485,10 +1485,7 @@ static zend_always_inline zval *zend_try_array_init_size(zval *zv, uint32_t size
14851485
}
14861486
zv = &ref->val;
14871487
}
1488-
zval garbage;
1489-
ZVAL_COPY_VALUE(&garbage, zv);
1490-
ZVAL_NULL(zv);
1491-
zval_ptr_dtor(&garbage);
1488+
zval_ptr_safe_dtor(zv);
14921489
ZVAL_ARR(zv, arr);
14931490
return zv;
14941491
}

Zend/zend_execute.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,17 @@ static zend_always_inline zval* zend_assign_to_variable_ex(zval *variable_ptr, z
207207
return variable_ptr;
208208
}
209209

210+
static zend_always_inline void zend_safe_assign_to_variable_noref(zval *variable_ptr, zval *value) {
211+
if (Z_REFCOUNTED_P(variable_ptr)) {
212+
ZEND_ASSERT(Z_TYPE_P(variable_ptr) != IS_REFERENCE);
213+
zend_refcounted *ref = Z_COUNTED_P(variable_ptr);
214+
ZVAL_COPY_VALUE(variable_ptr, value);
215+
GC_DTOR_NO_REF(ref);
216+
} else {
217+
ZVAL_COPY_VALUE(variable_ptr, value);
218+
}
219+
}
220+
210221
ZEND_API zend_result ZEND_FASTCALL zval_update_constant(zval *pp);
211222
ZEND_API zend_result ZEND_FASTCALL zval_update_constant_ex(zval *pp, zend_class_entry *scope);
212223
ZEND_API zend_result ZEND_FASTCALL zval_update_constant_with_ctx(zval *pp, zend_class_entry *scope, zend_ast_evaluate_ctx *ctx);

Zend/zend_variables.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ ZEND_API void zval_ptr_dtor(zval *zval_ptr) /* {{{ */
8585
}
8686
/* }}} */
8787

88+
ZEND_API void zval_ptr_safe_dtor(zval *zval_ptr)
89+
{
90+
if (Z_REFCOUNTED_P(zval_ptr)) {
91+
zend_refcounted *ref = Z_COUNTED_P(zval_ptr);
92+
93+
if (GC_DELREF(ref) == 0) {
94+
ZVAL_NULL(zval_ptr);
95+
rc_dtor_func(ref);
96+
} else {
97+
gc_check_possible_root(ref);
98+
}
99+
}
100+
}
101+
88102
ZEND_API void zval_internal_ptr_dtor(zval *zval_ptr) /* {{{ */
89103
{
90104
if (Z_REFCOUNTED_P(zval_ptr)) {

Zend/zend_variables.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ static zend_always_inline void zval_ptr_dtor_str(zval *zval_ptr)
7878
}
7979

8080
ZEND_API void zval_ptr_dtor(zval *zval_ptr);
81+
ZEND_API void zval_ptr_safe_dtor(zval *zval_ptr);
8182
ZEND_API void zval_internal_ptr_dtor(zval *zvalue);
8283

8384
/* Kept for compatibility */

0 commit comments

Comments
 (0)