Skip to content

Commit ef1e489

Browse files
committed
Fix bug #76047
Unlink the current stack frame before freeing CVs or extra args. This means it will no longer show up in back traces that are generated during CV destruction. We already did this prior to destructing the object/closure, presumably for the same reason.
1 parent 8226e70 commit ef1e489

File tree

5 files changed

+81
-12
lines changed

5 files changed

+81
-12
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ PHP NEWS
77
supported). (Nikita)
88
. Fixed bug ##79146 (cscript can fail to run on some systems). (clarodeus)
99
. Fixed bug #78323 (Code 0 is returned on invalid options). (Ivan Mikheykin)
10+
. Fixed bug #76047 (Use-after-free when accessing already destructed
11+
backtrace arguments). (Nikita)
1012

1113
- CURL:
1214
. Fixed bug #79078 (Hypothetical use-after-free in curl_multi_add_handle()).

Zend/tests/bug52361.phpt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ try {
2525
--EXPECTF--
2626
1. Exception: aaa in %sbug52361.php:5
2727
Stack trace:
28-
#0 %sbug52361.php(13): aaa->__destruct()
29-
#1 %sbug52361.php(16): bbb()
30-
#2 {main}
28+
#0 %sbug52361.php(16): aaa->__destruct()
29+
#1 {main}
3130
2. Exception: bbb in %sbug52361.php:13
3231
Stack trace:
3332
#0 %sbug52361.php(16): bbb()

Zend/tests/bug76047.phpt

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
--TEST--
2+
Bug #76047: Use-after-free when accessing already destructed backtrace arguments
3+
--FILE--
4+
<?php
5+
6+
class Vuln {
7+
public $a;
8+
public function __destruct() {
9+
unset($this->a);
10+
$backtrace = (new Exception)->getTrace();
11+
var_dump($backtrace);
12+
}
13+
}
14+
15+
function test($arg) {
16+
$arg = str_shuffle(str_repeat('A', 79));
17+
$vuln = new Vuln();
18+
$vuln->a = $arg;
19+
}
20+
21+
function test2($arg) {
22+
$$arg = 1; // Trigger symbol table
23+
$arg = str_shuffle(str_repeat('A', 79));
24+
$vuln = new Vuln();
25+
$vuln->a = $arg;
26+
}
27+
28+
test('x');
29+
test2('x');
30+
31+
?>
32+
--EXPECTF--
33+
array(1) {
34+
[0]=>
35+
array(6) {
36+
["file"]=>
37+
string(%d) "%s"
38+
["line"]=>
39+
int(%d)
40+
["function"]=>
41+
string(10) "__destruct"
42+
["class"]=>
43+
string(4) "Vuln"
44+
["type"]=>
45+
string(2) "->"
46+
["args"]=>
47+
array(0) {
48+
}
49+
}
50+
}
51+
array(1) {
52+
[0]=>
53+
array(6) {
54+
["file"]=>
55+
string(%d) "%s"
56+
["line"]=>
57+
int(%d)
58+
["function"]=>
59+
string(10) "__destruct"
60+
["class"]=>
61+
string(4) "Vuln"
62+
["type"]=>
63+
string(2) "->"
64+
["args"]=>
65+
array(0) {
66+
}
67+
}
68+
}

Zend/zend_vm_def.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,9 +2398,9 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
23982398
uint32_t call_info = EX_CALL_INFO();
23992399

24002400
if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED)) == 0)) {
2401+
EG(current_execute_data) = EX(prev_execute_data);
24012402
i_free_compiled_variables(execute_data);
24022403

2403-
EG(current_execute_data) = EX(prev_execute_data);
24042404
if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
24052405
zend_object *object = Z_OBJ(execute_data->This);
24062406
#if 0
@@ -2426,12 +2426,12 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
24262426
LOAD_NEXT_OPLINE();
24272427
ZEND_VM_LEAVE();
24282428
} else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) {
2429+
EG(current_execute_data) = EX(prev_execute_data);
24292430
i_free_compiled_variables(execute_data);
24302431

24312432
if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
24322433
zend_clean_and_cache_symbol_table(EX(symbol_table));
24332434
}
2434-
EG(current_execute_data) = EX(prev_execute_data);
24352435

24362436
/* Free extra args before releasing the closure,
24372437
* as that may free the op_array. */
@@ -2481,14 +2481,14 @@ ZEND_VM_HOT_HELPER(zend_leave_helper, ANY, ANY)
24812481
ZEND_VM_LEAVE();
24822482
} else {
24832483
if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) {
2484+
EG(current_execute_data) = EX(prev_execute_data);
24842485
i_free_compiled_variables(execute_data);
24852486
if (UNEXPECTED(call_info & (ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS))) {
24862487
if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
24872488
zend_clean_and_cache_symbol_table(EX(symbol_table));
24882489
}
24892490
zend_vm_stack_free_extra_args_ex(call_info, execute_data);
24902491
}
2491-
EG(current_execute_data) = EX(prev_execute_data);
24922492
if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
24932493
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
24942494
}

Zend/zend_vm_execute.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -507,9 +507,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_
507507
uint32_t call_info = EX_CALL_INFO();
508508

509509
if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED)) == 0)) {
510+
EG(current_execute_data) = EX(prev_execute_data);
510511
i_free_compiled_variables(execute_data);
511512

512-
EG(current_execute_data) = EX(prev_execute_data);
513513
if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
514514
zend_object *object = Z_OBJ(execute_data->This);
515515
#if 0
@@ -535,12 +535,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_
535535
LOAD_NEXT_OPLINE();
536536
ZEND_VM_LEAVE();
537537
} else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) {
538+
EG(current_execute_data) = EX(prev_execute_data);
538539
i_free_compiled_variables(execute_data);
539540

540541
if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
541542
zend_clean_and_cache_symbol_table(EX(symbol_table));
542543
}
543-
EG(current_execute_data) = EX(prev_execute_data);
544544

545545
/* Free extra args before releasing the closure,
546546
* as that may free the op_array. */
@@ -590,14 +590,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_
590590
ZEND_VM_LEAVE();
591591
} else {
592592
if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) {
593+
EG(current_execute_data) = EX(prev_execute_data);
593594
i_free_compiled_variables(execute_data);
594595
if (UNEXPECTED(call_info & (ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS))) {
595596
if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
596597
zend_clean_and_cache_symbol_table(EX(symbol_table));
597598
}
598599
zend_vm_stack_free_extra_args_ex(call_info, execute_data);
599600
}
600-
EG(current_execute_data) = EX(prev_execute_data);
601601
if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
602602
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
603603
}
@@ -55379,9 +55379,9 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5537955379
uint32_t call_info = EX_CALL_INFO();
5538055380

5538155381
if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED)) == 0)) {
55382+
EG(current_execute_data) = EX(prev_execute_data);
5538255383
i_free_compiled_variables(execute_data);
5538355384

55384-
EG(current_execute_data) = EX(prev_execute_data);
5538555385
if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) {
5538655386
zend_object *object = Z_OBJ(execute_data->This);
5538755387
#if 0
@@ -55407,12 +55407,12 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5540755407
LOAD_NEXT_OPLINE();
5540855408
ZEND_VM_LEAVE();
5540955409
} else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) {
55410+
EG(current_execute_data) = EX(prev_execute_data);
5541055411
i_free_compiled_variables(execute_data);
5541155412

5541255413
if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
5541355414
zend_clean_and_cache_symbol_table(EX(symbol_table));
5541455415
}
55415-
EG(current_execute_data) = EX(prev_execute_data);
5541655416

5541755417
/* Free extra args before releasing the closure,
5541855418
* as that may free the op_array. */
@@ -55462,14 +55462,14 @@ ZEND_API void execute_ex(zend_execute_data *ex)
5546255462
ZEND_VM_LEAVE();
5546355463
} else {
5546455464
if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) {
55465+
EG(current_execute_data) = EX(prev_execute_data);
5546555466
i_free_compiled_variables(execute_data);
5546655467
if (UNEXPECTED(call_info & (ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS))) {
5546755468
if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) {
5546855469
zend_clean_and_cache_symbol_table(EX(symbol_table));
5546955470
}
5547055471
zend_vm_stack_free_extra_args_ex(call_info, execute_data);
5547155472
}
55472-
EG(current_execute_data) = EX(prev_execute_data);
5547355473
if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) {
5547455474
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(EX(func)));
5547555475
}

0 commit comments

Comments
 (0)