Skip to content

Commit d65d3f5

Browse files
committed
Fix bug #79108
Don't expose references in debug_backtrace() or exception traces. This is regardless of whether the argument is by-reference or not. As a side-effect of this change, exception traces may now acquire the interior value of a reference, which may be unexpected for some internal functions. This is what necessitated the change in the spl_array sort implementation.
1 parent 27ad19c commit d65d3f5

File tree

9 files changed

+52
-38
lines changed

9 files changed

+52
-38
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ PHP NEWS
55
- Core:
66
. Fixed bug #78236 (convert error on receiving variables when duplicate [).
77
(cmb)
8+
. Fixed bug #79108 (Referencing argument in a function makes it a reference
9+
in the stack trace). (Nikita)
810

911
- JIT:
1012
. Fixed bug #79864 (JIT segfault in Symfony OptionsResolver). (Dmitry)

UPGRADING

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ PHP 8.0 UPGRADE NOTES
210210
RFC: https://wiki.php.net/rfc/namespaced_names_as_token
211211
. Nested ternaries now require explicit parentheses.
212212
RFC: https://wiki.php.net/rfc/ternary_associativity
213+
. debug_backtrace() and Exception::getTrace() will no longer provide
214+
references to arguments. It will not be possible to change function
215+
arguments through the backtrace.
213216

214217
- COM:
215218
. Removed the ability to import case-insensitive constants from type

Zend/tests/bug70547.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ array(4) {
3333
[0]=>
3434
string(3) "1st"
3535
[1]=>
36-
&string(3) "2nd"
36+
string(3) "2nd"
3737
[2]=>
3838
string(3) "3th"
3939
[3]=>
@@ -57,7 +57,7 @@ array(4) {
5757
[0]=>
5858
string(3) "1st"
5959
[1]=>
60-
&string(3) "2nd"
60+
string(3) "2nd"
6161
[2]=>
6262
NULL
6363
[3]=>
@@ -77,7 +77,7 @@ array(4) {
7777
[0]=>
7878
NULL
7979
[1]=>
80-
&string(3) "2nd"
80+
string(3) "2nd"
8181
[2]=>
8282
NULL
8383
[3]=>

Zend/tests/bug79108.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Bug #79108: Referencing argument in a function makes it a reference in the stack trace
3+
--FILE--
4+
<?php
5+
6+
function test(string $val) {
7+
$a = &$val;
8+
hackingHere();
9+
print_r($val);
10+
}
11+
12+
function hackingHere() {
13+
// we're able to modify the $val from here, even though the arg was not a reference
14+
debug_backtrace()[1]['args'][0] = 'Modified';
15+
}
16+
17+
test('Original');
18+
19+
?>
20+
--EXPECT--
21+
Original

Zend/zend_builtin_functions.c

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,16 +1591,12 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
15911591
* and we have to access them through symbol_table
15921592
* See: https://bugs.php.net/bug.php?id=73156
15931593
*/
1594-
zend_string *arg_name;
1595-
zval *arg;
1596-
15971594
while (i < first_extra_arg) {
1598-
arg_name = call->func->op_array.vars[i];
1599-
arg = zend_hash_find_ex_ind(call->symbol_table, arg_name, 1);
1595+
zend_string *arg_name = call->func->op_array.vars[i];
1596+
zval *arg = zend_hash_find_ex_ind(call->symbol_table, arg_name, 1);
16001597
if (arg) {
1601-
if (Z_OPT_REFCOUNTED_P(arg)) {
1602-
Z_ADDREF_P(arg);
1603-
}
1598+
ZVAL_DEREF(arg);
1599+
Z_TRY_ADDREF_P(arg);
16041600
ZEND_HASH_FILL_SET(arg);
16051601
} else {
16061602
ZEND_HASH_FILL_SET_NULL();
@@ -1611,10 +1607,10 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
16111607
} else {
16121608
while (i < first_extra_arg) {
16131609
if (EXPECTED(Z_TYPE_INFO_P(p) != IS_UNDEF)) {
1614-
if (Z_OPT_REFCOUNTED_P(p)) {
1615-
Z_ADDREF_P(p);
1616-
}
1617-
ZEND_HASH_FILL_SET(p);
1610+
zval *arg = p;
1611+
ZVAL_DEREF(arg);
1612+
Z_TRY_ADDREF_P(arg);
1613+
ZEND_HASH_FILL_SET(arg);
16181614
} else {
16191615
ZEND_HASH_FILL_SET_NULL();
16201616
}
@@ -1628,10 +1624,10 @@ static void debug_backtrace_get_args(zend_execute_data *call, zval *arg_array) /
16281624

16291625
while (i < num_args) {
16301626
if (EXPECTED(Z_TYPE_INFO_P(p) != IS_UNDEF)) {
1631-
if (Z_OPT_REFCOUNTED_P(p)) {
1632-
Z_ADDREF_P(p);
1633-
}
1634-
ZEND_HASH_FILL_SET(p);
1627+
zval *arg = p;
1628+
ZVAL_DEREF(arg);
1629+
Z_TRY_ADDREF_P(arg);
1630+
ZEND_HASH_FILL_SET(arg);
16351631
} else {
16361632
ZEND_HASH_FILL_SET_NULL();
16371633
}

ext/mbstring/tests/mb_ereg1.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ array(3) {
5353
[1]=>
5454
int(1)
5555
[2]=>
56-
&string(0) ""
56+
string(0) ""
5757
}
5858
mb_ereg(): Argument #2 ($string) must be of type string, array given
5959
array(3) {
@@ -63,7 +63,7 @@ array(3) {
6363
array(0) {
6464
}
6565
[2]=>
66-
&string(0) ""
66+
string(0) ""
6767
}
6868
bool(false)
6969
array(3) {

ext/spl/spl_array.c

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,6 @@ static inline HashTable *spl_array_get_hash_table(spl_array_object* intern) { /*
111111
}
112112
/* }}} */
113113

114-
static inline void spl_array_replace_hash_table(spl_array_object* intern, HashTable *ht) { /* {{{ */
115-
HashTable **ht_ptr = spl_array_get_hash_table_ptr(intern);
116-
zend_array_destroy(*ht_ptr);
117-
*ht_ptr = ht;
118-
}
119-
/* }}} */
120-
121114
static inline zend_bool spl_array_is_object(spl_array_object *intern) /* {{{ */
122115
{
123116
while (intern->ar_flags & SPL_ARRAY_USE_OTHER) {
@@ -1412,7 +1405,8 @@ PHP_METHOD(ArrayObject, count)
14121405
static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fname_len, int use_arg) /* {{{ */
14131406
{
14141407
spl_array_object *intern = Z_SPLARRAY_P(ZEND_THIS);
1415-
HashTable *aht = spl_array_get_hash_table(intern);
1408+
HashTable **ht_ptr = spl_array_get_hash_table_ptr(intern);
1409+
HashTable *aht = *ht_ptr;
14161410
zval function_name, params[2], *arg = NULL;
14171411

14181412
ZVAL_STRINGL(&function_name, fname, fname_len);
@@ -1451,13 +1445,11 @@ static void spl_array_method(INTERNAL_FUNCTION_PARAMETERS, char *fname, int fnam
14511445

14521446
exit:
14531447
{
1454-
HashTable *new_ht = Z_ARRVAL_P(Z_REFVAL(params[0]));
1455-
if (aht != new_ht) {
1456-
spl_array_replace_hash_table(intern, new_ht);
1457-
} else {
1458-
GC_DELREF(aht);
1459-
}
1460-
ZVAL_NULL(Z_REFVAL(params[0]));
1448+
zval *ht_zv = Z_REFVAL(params[0]);
1449+
zend_array_release(*ht_ptr);
1450+
SEPARATE_ARRAY(ht_zv);
1451+
*ht_ptr = Z_ARRVAL_P(ht_zv);
1452+
ZVAL_NULL(ht_zv);
14611453
zval_ptr_dtor(&params[0]);
14621454
zend_string_free(Z_STR(function_name));
14631455
}

ext/standard/tests/array/array_walk_closure.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ array(2) {
223223
["args"]=>
224224
array(2) {
225225
[0]=>
226-
&array(3) {
226+
array(3) {
227227
["one"]=>
228228
int(1)
229229
["two"]=>

ext/standard/tests/array/bug79839.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ var_dump($test);
2222
Cannot assign array to reference held by property Test::$prop of type int
2323
object(Test)#1 (1) {
2424
["prop"]=>
25-
&int(42)
25+
int(42)
2626
}

0 commit comments

Comments
 (0)