Backport of https://git.php.net/?p=php-src.git;a=commit;h=ef1e4891b47949c8dc0f9482eef9454a0ecdfa1d --- a/Zend/tests/bug52361.phpt +++ b/Zend/tests/bug52361.phpt @@ -25,9 +25,8 @@ try { --EXPECTF-- 1. Exception: aaa in %sbug52361.php:5 Stack trace: -#0 %sbug52361.php(13): aaa->__destruct() -#1 %sbug52361.php(16): bbb() -#2 {main} +#0 %sbug52361.php(16): aaa->__destruct() +#1 {main} 2. Exception: bbb in %sbug52361.php:13 Stack trace: #0 %sbug52361.php(16): bbb() --- /dev/null +++ b/Zend/tests/bug76047.phpt @@ -0,0 +1,68 @@ +--TEST-- +Bug #76047: Use-after-free when accessing already destructed backtrace arguments +--FILE-- +a); + $backtrace = (new Exception)->getTrace(); + var_dump($backtrace); + } +} + +function test($arg) { + $arg = str_shuffle(str_repeat('A', 79)); + $vuln = new Vuln(); + $vuln->a = $arg; +} + +function test2($arg) { + $$arg = 1; // Trigger symbol table + $arg = str_shuffle(str_repeat('A', 79)); + $vuln = new Vuln(); + $vuln->a = $arg; +} + +test('x'); +test2('x'); + +?> +--EXPECTF-- +array(1) { + [0]=> + array(6) { + ["file"]=> + string(%d) "%s" + ["line"]=> + int(%d) + ["function"]=> + string(10) "__destruct" + ["class"]=> + string(4) "Vuln" + ["type"]=> + string(2) "->" + ["args"]=> + array(0) { + } + } +} +array(1) { + [0]=> + array(6) { + ["file"]=> + string(%d) "%s" + ["line"]=> + int(%d) + ["function"]=> + string(10) "__destruct" + ["class"]=> + string(4) "Vuln" + ["type"]=> + string(2) "->" + ["args"]=> + array(0) { + } + } +} --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2366,9 +2366,9 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) uint32_t call_info = EX_CALL_INFO(); if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED)) == 0)) { + EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); - EG(current_execute_data) = EX(prev_execute_data); if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { zend_object *object = Z_OBJ(execute_data->This); #if 0 @@ -2394,12 +2394,12 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) LOAD_NEXT_OPLINE(); ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) { + EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_clean_and_cache_symbol_table(EX(symbol_table)); } - EG(current_execute_data) = EX(prev_execute_data); /* Free extra args before releasing the closure, * as that may free the op_array. */ @@ -2449,6 +2449,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) ZEND_VM_LEAVE(); } else { if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) { + EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); if (UNEXPECTED(call_info & (ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS))) { if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) { @@ -2456,7 +2457,6 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) } zend_vm_stack_free_extra_args_ex(call_info, execute_data); } - EG(current_execute_data) = EX(prev_execute_data); if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE((zend_object*)EX(func)->op_array.prototype); } --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -434,9 +434,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_ uint32_t call_info = EX_CALL_INFO(); if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP|ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS|ZEND_CALL_ALLOCATED)) == 0)) { + EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); - EG(current_execute_data) = EX(prev_execute_data); if (UNEXPECTED(call_info & ZEND_CALL_RELEASE_THIS)) { zend_object *object = Z_OBJ(execute_data->This); #if 0 @@ -462,12 +462,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_ LOAD_NEXT_OPLINE(); ZEND_VM_LEAVE(); } else if (EXPECTED((call_info & (ZEND_CALL_CODE|ZEND_CALL_TOP)) == 0)) { + EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_clean_and_cache_symbol_table(EX(symbol_table)); } - EG(current_execute_data) = EX(prev_execute_data); /* Free extra args before releasing the closure, * as that may free the op_array. */ @@ -517,6 +517,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_ ZEND_VM_LEAVE(); } else { if (EXPECTED((call_info & ZEND_CALL_CODE) == 0)) { + EG(current_execute_data) = EX(prev_execute_data); i_free_compiled_variables(execute_data); if (UNEXPECTED(call_info & (ZEND_CALL_HAS_SYMBOL_TABLE|ZEND_CALL_FREE_EXTRA_ARGS))) { if (UNEXPECTED(call_info & ZEND_CALL_HAS_SYMBOL_TABLE)) { @@ -524,7 +525,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_ } zend_vm_stack_free_extra_args_ex(call_info, execute_data); } - EG(current_execute_data) = EX(prev_execute_data); if (UNEXPECTED(call_info & ZEND_CALL_CLOSURE)) { OBJ_RELEASE((zend_object*)EX(func)->op_array.prototype); }