Fix return value memory leaks upon exceptions in opcode operand freeing

This commit is contained in:
Bob Weinand 2016-11-28 23:14:38 +01:00
parent c406d1e9b5
commit 9ad9d7ae37
8 changed files with 679 additions and 301 deletions

View File

@ -6,6 +6,7 @@ XORing strings
$s = "123";
$s1 = "test";
$s2 = "45345some";
$s3 = "f";
$s ^= 22;
var_dump($s);
@ -16,6 +17,9 @@ var_dump($s1);
$s2 ^= 33;
var_dump($s2);
$s3 ^= " ";
var_dump($s3);
echo "Done\n";
?>
--EXPECTF--
@ -26,4 +30,5 @@ int(11)
Notice: A non well formed numeric value encountered in %s on line %d
int(45312)
string(1) "F"
Done

19
Zend/tests/shift_003.phpt Normal file
View File

@ -0,0 +1,19 @@
--TEST--
Test behavior of failing bitshift assign
--FILE--
<?php
try {
$a = 1;
$a >>= -1;
} catch (Error $e) { var_dump($a); }
try {
$a = 1;
$a <<= -1;
} catch (Error $e) { var_dump($a); }
?>
--EXPECT--
int(1)
int(1)

View File

@ -0,0 +1,311 @@
--TEST--
Exceptions thrown in operand cleaning must cause leak of return value
--FILE--
<?php
try {
var_dump(new class {
function __toString() { return "a"; }
function __destruct() { throw new Exception; }
} . "foo");
} catch (Exception $e) { print "caught Exception 1\n"; }
try {
var_dump([0] + [new class {
function __destruct() { throw new Exception; }
}]);
} catch (Exception $e) { print "caught Exception 2\n"; }
try {
$foo = [0];
var_dump($foo += [new class {
function __destruct() { throw new Exception; }
}]);
} catch (Exception $e) { print "caught Exception 3\n"; }
try {
$foo = (object)["foo" => [0]];
var_dump($foo->foo += [new class {
function __destruct() { throw new Exception; }
}]);
} catch (Exception $e) { print "caught Exception 4\n"; }
try {
$foo = new class {
function __get($x) { return [0]; }
function __set($x, $y) {}
};
var_dump($foo->foo += [new class {
function __destruct() { throw new Exception; }
}]);
} catch (Exception $e) { print "caught Exception 5\n"; }
try {
$foo = new class {
public $bar = [0];
function &__get($x) { return $this->bar; }
};
var_dump($foo->foo += [new class {
function __destruct() { throw new Exception; }
}]);
} catch (Exception $e) { print "caught Exception 6\n"; }
try {
$foo = new class implements ArrayAccess {
function offsetGet($x) { return [0]; }
function offsetSet($x, $y) {}
function offsetExists($x) { return true; }
function offsetUnset($x) {}
};
var_dump($foo[0] += [new class {
function __destruct() { throw new Exception; }
}]);
} catch (Exception $e) { print "caught Exception 7\n"; }
try {
$foo = new class implements ArrayAccess {
public $foo = [0];
function &offsetGet($x) { return $this->foo; }
function offsetSet($x, $y) {}
function offsetExists($x) { return true; }
function offsetUnset($x) {}
};
var_dump($foo[0] += [new class {
function __destruct() { throw new Exception; }
}]);
} catch (Exception $e) { print "caught Exception 8\n"; }
try {
var_dump((function() { return new class {
function __construct() { $this->foo = new stdClass; }
function __destruct() { throw new Exception; }
}; })()->foo++);
} catch (Exception $e) { print "caught Exception 9\n"; }
try {
var_dump((function() { return new class {
function __get($x) { return new stdClass; }
function __set($x, $y) {}
function __destruct() { throw new Exception; }
}; })()->foo++);
} catch (Exception $e) { print "caught Exception 10\n"; }
try {
var_dump((function() { return new class {
function __construct() { $this->bar = new stdClass; }
function &__get($x) { return $this->bar; }
function __destruct() { throw new Exception; }
}; })()->foo++);
} catch (Exception $e) { print "caught Exception 11\n"; }
try {
var_dump(++(function() { return new class {
function __construct() { $this->foo = new stdClass; }
function __destruct() { throw new Exception; }
}; })()->foo);
} catch (Exception $e) { print "caught Exception 12\n"; }
try {
var_dump(++(function() { return new class {
function __get($x) { return new stdClass; }
function __set($x, $y) {}
function __destruct() { throw new Exception; }
}; })()->foo);
} catch (Exception $e) { print "caught Exception 13\n"; }
try {
var_dump(++(function() { return new class {
function __construct() { $this->bar = new stdClass; }
function &__get($x) { return $this->bar; }
function __destruct() { throw new Exception; }
}; })()->foo);
} catch (Exception $e) { print "caught Exception 14\n"; }
try {
var_dump((function() { return new class implements ArrayAccess {
function offsetGet($x) { return [new stdClass]; }
function offsetSet($x, $y) {}
function offsetExists($x) { return true; }
function offsetUnset($x) {}
function __destruct() { throw new Exception; }
}; })()[0]++);
} catch (Exception $e) { print "caught Exception 15\n"; }
try {
var_dump(++(function() { return new class implements ArrayAccess {
function offsetGet($x) { return [new stdClass]; }
function offsetSet($x, $y) {}
function offsetExists($x) { return true; }
function offsetUnset($x) {}
function __destruct() { throw new Exception; }
}; })()[0]);
} catch (Exception $e) { print "caught Exception 16\n"; }
try {
var_dump((new class {
function __construct() { $this->foo = new stdClass; }
function __destruct() { throw new Exception; }
})->foo);
} catch (Exception $e) { print "caught Exception 17\n"; }
try {
var_dump((new class {
function __get($x) { return new stdClass; }
function __set($x, $y) {}
function __destruct() { throw new Exception; }
})->foo);
} catch (Exception $e) { print "caught Exception 18\n"; }
try {
var_dump((new class implements ArrayAccess {
function offsetGet($x) { return [new stdClass]; }
function offsetSet($x, $y) {}
function offsetExists($x) { return true; }
function offsetUnset($x) {}
function __destruct() { throw new Exception; }
})[0]);
} catch (Exception $e) { print "caught Exception 19\n"; }
try {
var_dump(isset((new class {
function __construct() { $this->foo = new stdClass; }
function __destruct() { throw new Exception; }
})->foo->bar));
} catch (Exception $e) { print "caught Exception 20\n"; }
try {
var_dump(isset((new class {
function __get($x) { return new stdClass; }
function __set($x, $y) {}
function __destruct() { throw new Exception; }
})->foo->bar));
} catch (Exception $e) { print "caught Exception 21\n"; }
try {
var_dump(isset((new class implements ArrayAccess {
function offsetGet($x) { return [new stdClass]; }
function offsetSet($x, $y) {}
function offsetExists($x) { return true; }
function offsetUnset($x) {}
function __destruct() { throw new Exception; }
})[0]->bar));
} catch (Exception $e) { print "caught Exception 22\n"; }
try {
$foo = new class {
function __destruct() { throw new Exception; }
};
var_dump($foo = new stdClass);
} catch (Exception $e) { print "caught Exception 23\n"; }
try {
$foo = [new class {
function __destruct() { throw new Exception; }
}];
var_dump($foo[0] = new stdClass);
} catch (Exception $e) { print "caught Exception 24\n"; }
try {
$foo = (object) ["foo" => new class {
function __destruct() { throw new Exception; }
}];
var_dump($foo->foo = new stdClass);
} catch (Exception $e) { print "caught Exception 25\n"; }
try {
$foo = new class {
function __get($x) {}
function __set($x, $y) { throw new Exception; }
};
var_dump($foo->foo = new stdClass);
} catch (Exception $e) { print "caught Exception 26\n"; }
try {
$foo = new class implements ArrayAccess {
function offsetGet($x) {}
function offsetSet($x, $y) { throw new Exception; }
function offsetExists($x) { return true; }
function offsetUnset($x) {}
};
var_dump($foo[0] = new stdClass);
} catch (Exception $e) { print "caught Exception 27\n"; }
try {
$foo = new class {
function __destruct() { throw new Exception; }
};
$bar = new stdClass;
var_dump($foo = &$bar);
} catch (Exception $e) { print "caught Exception 28\n"; }
try {
$f = function() {
return new class {
function __toString() { return "a"; }
function __destruct() { throw new Exception; }
};
};
var_dump("{$f()}foo");
} catch (Exception $e) { print "caught Exception 29\n"; }
try {
$f = function() {
return new class {
function __toString() { return "a"; }
function __destruct() { throw new Exception; }
};
};
var_dump("bar{$f()}foo");
} catch (Exception $e) { print "caught Exception 30\n"; }
try {
var_dump((string) new class {
function __toString() { $x = "Z"; return ++$x; }
function __destruct() { throw new Exception; }
});
} catch (Exception $e) { print "caught Exception 31\n"; }
try {
var_dump(clone (new class {
function __clone() { throw new Exception; }
}));
} catch (Exception $e) { print "caught Exception 32\n"; }
?>
--EXPECTF--
caught Exception 1
caught Exception 2
caught Exception 3
caught Exception 4
caught Exception 5
caught Exception 6
caught Exception 7
caught Exception 8
caught Exception 9
caught Exception 10
caught Exception 11
caught Exception 12
caught Exception 13
caught Exception 14
Notice: Indirect modification of overloaded element of class@anonymous has no effect in %s on line %d
caught Exception 15
Notice: Indirect modification of overloaded element of class@anonymous has no effect in %s on line %d
caught Exception 16
caught Exception 17
caught Exception 18
caught Exception 19
caught Exception 20
caught Exception 21
caught Exception 22
caught Exception 23
caught Exception 24
caught Exception 25
caught Exception 26
caught Exception 27
caught Exception 28
caught Exception 29
caught Exception 30
caught Exception 31
caught Exception 32

View File

@ -1369,6 +1369,7 @@ static zend_never_inline void zend_post_incdec_overloaded_property(zval *object,
z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv);
if (UNEXPECTED(EG(exception))) {
OBJ_RELEASE(Z_OBJ(obj));
ZVAL_UNDEF(result);
return;
}
@ -1414,6 +1415,7 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object,
zptr = z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv);
if (UNEXPECTED(EG(exception))) {
OBJ_RELEASE(Z_OBJ(obj));
ZVAL_UNDEF(result);
return;
}
@ -1459,6 +1461,9 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z
z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv);
if (UNEXPECTED(EG(exception))) {
OBJ_RELEASE(Z_OBJ(obj));
if (result) {
ZVAL_UNDEF(result);
}
return;
}
if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) {
@ -2941,12 +2946,16 @@ ZEND_API int ZEND_FASTCALL zend_do_fcall_overloaded(zend_execute_data *call, zva
#define ZEND_VM_SET_RELATIVE_OPCODE(opline, offset) \
ZEND_VM_SET_OPCODE(ZEND_OFFSET_TO_OPLINE(opline, offset))
#define ZEND_VM_JMP_NO_EXCEPTION(new_op) do { \
ZEND_VM_SET_OPCODE(new_op); \
ZEND_VM_CONTINUE(); \
} while (0)
#define ZEND_VM_JMP(new_op) do { \
if (UNEXPECTED(EG(exception))) { \
HANDLE_EXCEPTION(); \
} \
ZEND_VM_SET_OPCODE(new_op); \
ZEND_VM_CONTINUE(); \
ZEND_VM_JMP_NO_EXCEPTION(new_op); \
} while (0)
#define ZEND_VM_INC_OPCODE() \
@ -3024,6 +3033,11 @@ ZEND_API int ZEND_FASTCALL zend_do_fcall_overloaded(zend_execute_data *call, zva
#define GET_OP2_UNDEF_CV(ptr, type) \
_get_zval_cv_lookup_ ## type(ptr, opline->op2.var, execute_data)
#define ZEND_VM_UNDEF_RETVAL() \
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { \
ZVAL_UNDEF(EX_VAR(opline->result.var)); \
}
#include "zend_vm_execute.h"
ZEND_API int zend_set_user_opcode_handler(zend_uchar opcode, user_opcode_handler_t handler)

View File

@ -926,6 +926,9 @@ ZEND_API int ZEND_FASTCALL add_function(zval *result, zval *op1, zval *op2) /* {
zendi_convert_scalar_to_number(op2, op2_copy, result, 0);
converted = 1;
} else {
if (result != op1) {
ZVAL_UNDEF(result);
}
zend_throw_error(NULL, "Unsupported operand types");
return FAILURE; /* unknown datatype */
}
@ -968,6 +971,9 @@ ZEND_API int ZEND_FASTCALL sub_function(zval *result, zval *op1, zval *op2) /* {
zendi_convert_scalar_to_number(op2, op2_copy, result, 0);
converted = 1;
} else {
if (result != op1) {
ZVAL_UNDEF(result);
}
zend_throw_error(NULL, "Unsupported operand types");
return FAILURE; /* unknown datatype */
}
@ -1015,6 +1021,9 @@ ZEND_API int ZEND_FASTCALL mul_function(zval *result, zval *op1, zval *op2) /* {
zendi_convert_scalar_to_number(op2, op2_copy, result, 0);
converted = 1;
} else {
if (result != op1) {
ZVAL_UNDEF(result);
}
zend_throw_error(NULL, "Unsupported operand types");
return FAILURE; /* unknown datatype */
}
@ -1103,6 +1112,9 @@ ZEND_API int ZEND_FASTCALL pow_function(zval *result, zval *op1, zval *op2) /* {
}
converted = 1;
} else {
if (result != op1) {
ZVAL_UNDEF(result);
}
zend_throw_error(NULL, "Unsupported operand types");
return FAILURE;
}
@ -1168,6 +1180,9 @@ ZEND_API int ZEND_FASTCALL div_function(zval *result, zval *op1, zval *op2) /* {
zendi_convert_scalar_to_number(op2, op2_copy, result, 0);
converted = 1;
} else {
if (result != op1) {
ZVAL_UNDEF(result);
}
zend_throw_error(NULL, "Unsupported operand types");
return FAILURE; /* unknown datatype */
}
@ -1182,10 +1197,6 @@ ZEND_API int ZEND_FASTCALL mod_function(zval *result, zval *op1, zval *op2) /* {
convert_op1_op2_long(op1, op1_lval, op2, op2_lval, ZEND_MOD, mod_function);
if (op1 == result) {
zval_dtor(result);
}
if (op2_lval == 0) {
/* modulus by zero */
if (EG(current_execute_data) && !CG(in_compilation)) {
@ -1193,10 +1204,16 @@ ZEND_API int ZEND_FASTCALL mod_function(zval *result, zval *op1, zval *op2) /* {
} else {
zend_error_noreturn(E_ERROR, "Modulo by zero");
}
ZVAL_UNDEF(result);
if (op1 != result) {
ZVAL_UNDEF(result);
}
return FAILURE;
}
if (op1 == result) {
zval_dtor(result);
}
if (op2_lval == -1) {
/* Prevent overflow error/crash if op1==LONG_MIN */
ZVAL_LONG(result, 0);
@ -1318,6 +1335,9 @@ try_again:
default:
ZEND_TRY_UNARY_OBJECT_OPERATION(ZEND_BW_NOT);
if (result != op1) {
ZVAL_UNDEF(result);
}
zend_throw_error(NULL, "Unsupported operand types");
return FAILURE;
}
@ -1344,6 +1364,9 @@ ZEND_API int ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1, zval *op
if (EXPECTED(Z_STRLEN_P(op1) >= Z_STRLEN_P(op2))) {
if (EXPECTED(Z_STRLEN_P(op1) == Z_STRLEN_P(op2)) && Z_STRLEN_P(op1) == 1) {
zend_uchar or = (zend_uchar) (*Z_STRVAL_P(op1) | *Z_STRVAL_P(op2));
if (result==op1) {
zend_string_release(Z_STR_P(result));
}
if (CG(one_char_string)[or]) {
ZVAL_INTERNED_STR(result, CG(one_char_string)[or]);
} else {
@ -1411,6 +1434,9 @@ ZEND_API int ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1, zval *o
if (EXPECTED(Z_STRLEN_P(op1) >= Z_STRLEN_P(op2))) {
if (EXPECTED(Z_STRLEN_P(op1) == Z_STRLEN_P(op2)) && Z_STRLEN_P(op1) == 1) {
zend_uchar and = (zend_uchar) (*Z_STRVAL_P(op1) & *Z_STRVAL_P(op2));
if (result==op1) {
zend_string_release(Z_STR_P(result));
}
if (CG(one_char_string)[and]) {
ZVAL_INTERNED_STR(result, CG(one_char_string)[and]);
} else {
@ -1478,6 +1504,9 @@ ZEND_API int ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1, zval *o
if (EXPECTED(Z_STRLEN_P(op1) >= Z_STRLEN_P(op2))) {
if (EXPECTED(Z_STRLEN_P(op1) == Z_STRLEN_P(op2)) && Z_STRLEN_P(op1) == 1) {
zend_uchar xor = (zend_uchar) (*Z_STRVAL_P(op1) ^ *Z_STRVAL_P(op2));
if (result==op1) {
zend_string_release(Z_STR_P(result));
}
if (CG(one_char_string)[xor]) {
ZVAL_INTERNED_STR(result, CG(one_char_string)[xor]);
} else {
@ -1531,13 +1560,13 @@ ZEND_API int ZEND_FASTCALL shift_left_function(zval *result, zval *op1, zval *op
convert_op1_op2_long(op1, op1_lval, op2, op2_lval, ZEND_SL, shift_left_function);
if (op1 == result) {
zval_dtor(result);
}
/* prevent wrapping quirkiness on some processors where << 64 + x == << x */
if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) {
if (EXPECTED(op2_lval > 0)) {
if (op1 == result) {
zval_dtor(result);
}
ZVAL_LONG(result, 0);
return SUCCESS;
} else {
@ -1546,11 +1575,17 @@ ZEND_API int ZEND_FASTCALL shift_left_function(zval *result, zval *op1, zval *op
} else {
zend_error_noreturn(E_ERROR, "Bit shift by negative number");
}
ZVAL_UNDEF(result);
if (op1 != result) {
ZVAL_UNDEF(result);
}
return FAILURE;
}
}
if (op1 == result) {
zval_dtor(result);
}
ZVAL_LONG(result, op1_lval << op2_lval);
return SUCCESS;
}
@ -1562,13 +1597,13 @@ ZEND_API int ZEND_FASTCALL shift_right_function(zval *result, zval *op1, zval *o
convert_op1_op2_long(op1, op1_lval, op2, op2_lval, ZEND_SR, shift_right_function);
if (op1 == result) {
zval_dtor(result);
}
/* prevent wrapping quirkiness on some processors where >> 64 + x == >> x */
if (UNEXPECTED((zend_ulong)op2_lval >= SIZEOF_ZEND_LONG * 8)) {
if (EXPECTED(op2_lval > 0)) {
if (op1 == result) {
zval_dtor(result);
}
ZVAL_LONG(result, (op1_lval < 0) ? -1 : 0);
return SUCCESS;
} else {
@ -1577,11 +1612,17 @@ ZEND_API int ZEND_FASTCALL shift_right_function(zval *result, zval *op1, zval *o
} else {
zend_error_noreturn(E_ERROR, "Bit shift by negative number");
}
ZVAL_UNDEF(result);
if (op1 != result) {
ZVAL_UNDEF(result);
}
return FAILURE;
}
}
if (op1 == result) {
zval_dtor(result);
}
ZVAL_LONG(result, op1_lval >> op2_lval);
return SUCCESS;
}
@ -1589,7 +1630,7 @@ ZEND_API int ZEND_FASTCALL shift_right_function(zval *result, zval *op1, zval *o
ZEND_API int ZEND_FASTCALL concat_function(zval *result, zval *op1, zval *op2) /* {{{ */
{
zval op1_copy, op2_copy;
zval op1_copy, op2_copy, orig_op1;
int use_copy1 = 0, use_copy2 = 0;
do {
@ -1605,10 +1646,12 @@ ZEND_API int ZEND_FASTCALL concat_function(zval *result, zval *op1, zval *op2) /
* we have to free it.
*/
if (result == op1) {
zval_dtor(op1);
ZVAL_COPY_VALUE(&orig_op1, result);
if (UNEXPECTED(op1 == op2)) {
op2 = &op1_copy;
}
} else {
ZVAL_UNDEF(&orig_op1);
}
op1 = &op1_copy;
}
@ -1636,7 +1679,19 @@ ZEND_API int ZEND_FASTCALL concat_function(zval *result, zval *op1, zval *op2) /
if (UNEXPECTED(op1_len > SIZE_MAX - op2_len)) {
zend_throw_error(NULL, "String size overflow");
ZVAL_FALSE(result);
if (UNEXPECTED(use_copy1)) {
zval_dtor(op1);
if (Z_ISUNDEF(orig_op1)) {
ZVAL_UNDEF(result);
}
} else if (result != op1) {
ZVAL_UNDEF(result);
}
if (UNEXPECTED(use_copy2)) {
zval_dtor(op2);
}
return FAILURE;
}
@ -1659,6 +1714,7 @@ ZEND_API int ZEND_FASTCALL concat_function(zval *result, zval *op1, zval *op2) /
if (UNEXPECTED(use_copy1)) {
zval_dtor(op1);
zval_dtor(&orig_op1);
}
if (UNEXPECTED(use_copy2)) {
zval_dtor(op2);

View File

@ -893,6 +893,7 @@ ZEND_VM_C_LABEL(assign_dim_op_convert_to_array):
zend_check_string_offset(dim, BP_VAR_RW);
zend_wrong_string_offset();
}
ZVAL_UNDEF(EX_VAR(opline->result.var));
} else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) {
ZEND_VM_C_GOTO(assign_dim_op_convert_to_array);
} else {
@ -1554,9 +1555,11 @@ ZEND_VM_C_LABEL(fetch_this):
break;
case BP_VAR_RW:
case BP_VAR_W:
ZVAL_UNDEF(result);
zend_throw_error(NULL, "Cannot re-assign $this");
break;
case BP_VAR_UNSET:
ZVAL_UNDEF(result);
zend_throw_error(NULL, "Cannot unset $this");
break;
EMPTY_SWITCH_DEFAULT_CASE()
@ -1737,6 +1740,7 @@ ZEND_VM_HELPER(zend_fetch_static_prop_helper, CONST|TMPVAR|CV, UNUSED|CONST|VAR,
ZEND_VM_C_GOTO(fetch_static_prop_return);
}
}
retval = zend_std_get_static_property(ce, name, 0);
if (UNEXPECTED(retval == NULL)) {
ZEND_ASSERT(EG(exception));
@ -1746,6 +1750,7 @@ ZEND_VM_HELPER(zend_fetch_static_prop_helper, CONST|TMPVAR|CV, UNUSED|CONST|VAR,
FREE_OP1();
HANDLE_EXCEPTION();
}
if (OP1_TYPE == IS_CONST && retval) {
CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce, retval);
}
@ -2360,7 +2365,7 @@ ZEND_VM_C_LABEL(fast_assign_obj):
Z_OBJ_HT_P(object)->write_property(object, property_name, value, (OP2_TYPE == IS_CONST) ? CACHE_ADDR(Z_CACHE_SLOT_P(property_name)) : NULL);
if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
FREE_OP_DATA();
@ -2422,7 +2427,7 @@ ZEND_VM_C_LABEL(try_assign_dim_array):
zend_assign_to_object_dim(object_ptr, dim, value);
if (UNEXPECTED(RETURN_VALUE_USED(opline)) && EXPECTED(!EG(exception))) {
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
@ -2574,11 +2579,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
execute_data = EX(prev_execute_data);
if (UNEXPECTED(EG(exception) != NULL)) {
const zend_op *old_opline = EX(opline);
zend_throw_exception_internal(NULL);
if (RETURN_VALUE_USED(old_opline)) {
zval_ptr_dtor(EX_VAR(old_opline->result.var));
}
HANDLE_EXCEPTION_LEAVE();
}
@ -2612,11 +2613,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
zend_vm_stack_free_call_frame_ex(call_info, old_execute_data);
if (UNEXPECTED(EG(exception) != NULL)) {
const zend_op *old_opline = EX(opline);
zend_throw_exception_internal(NULL);
if (RETURN_VALUE_USED(old_opline)) {
zval_ptr_dtor(EX_VAR(old_opline->result.var));
}
HANDLE_EXCEPTION_LEAVE();
}
@ -2697,8 +2694,7 @@ ZEND_VM_HANDLER(43, ZEND_JMPZ, CONST|TMPVAR|CV, JMP_ADDR)
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else {
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op2));
ZEND_VM_CONTINUE();
ZEND_VM_JMP_NO_EXCEPTION(OP_JMP_ADDR(opline, opline->op2));
}
}
@ -2721,8 +2717,7 @@ ZEND_VM_HANDLER(44, ZEND_JMPNZ, CONST|TMPVAR|CV, JMP_ADDR)
val = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
if (Z_TYPE_INFO_P(val) == IS_TRUE) {
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op2));
ZEND_VM_CONTINUE();
ZEND_VM_JMP_NO_EXCEPTION(OP_JMP_ADDR(opline, opline->op2));
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
@ -2755,15 +2750,12 @@ ZEND_VM_HANDLER(45, ZEND_JMPZNZ, CONST|TMPVAR|CV, JMP_ADDR, JMP_ADDR)
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
if (OP1_TYPE == IS_CV) {
if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
}
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else {
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op2));
ZEND_VM_CONTINUE();
ZEND_VM_JMP_NO_EXCEPTION(OP_JMP_ADDR(opline, opline->op2));
}
}
@ -2788,19 +2780,15 @@ ZEND_VM_HANDLER(46, ZEND_JMPZ_EX, CONST|TMPVAR|CV, JMP_ADDR)
if (Z_TYPE_INFO_P(val) == IS_TRUE) {
ZVAL_TRUE(EX_VAR(opline->result.var));
ZEND_VM_SET_NEXT_OPCODE(opline + 1);
ZEND_VM_CONTINUE();
ZEND_VM_NEXT_OPCODE();
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
ZVAL_FALSE(EX_VAR(opline->result.var));
if (OP1_TYPE == IS_CV) {
if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
}
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
SAVE_OPLINE();
GET_OP1_UNDEF_CV(val, BP_VAR_R);
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
} else {
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op2));
ZEND_VM_CONTINUE();
ZEND_VM_JMP_NO_EXCEPTION(OP_JMP_ADDR(opline, opline->op2));
}
}
@ -2828,8 +2816,7 @@ ZEND_VM_HANDLER(47, ZEND_JMPNZ_EX, CONST|TMPVAR|CV, JMP_ADDR)
if (Z_TYPE_INFO_P(val) == IS_TRUE) {
ZVAL_TRUE(EX_VAR(opline->result.var));
ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline, opline->op2));
ZEND_VM_CONTINUE();
ZEND_VM_JMP_NO_EXCEPTION(OP_JMP_ADDR(opline, opline->op2));
} else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) {
ZVAL_FALSE(EX_VAR(opline->result.var));
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) {
@ -3942,12 +3929,6 @@ ZEND_VM_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV, UNUSED)
retval_ptr = retval_ref;
}
zend_verify_return_type(EX(func), retval_ptr, CACHE_ADDR(opline->op2.num));
if (UNEXPECTED(EG(exception) != NULL)) {
if (OP1_TYPE == IS_CONST) {
zval_ptr_dtor_nogc(retval_ptr);
}
}
#endif
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@ -5019,7 +5000,6 @@ ZEND_VM_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY)
USE_OPLINE
zend_free_op free_op1;
zval *obj;
zend_object *clone_obj;
zend_class_entry *ce, *scope;
zend_function *clone;
zend_object_clone_obj_t clone_call;
@ -5084,12 +5064,7 @@ ZEND_VM_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY)
}
}
clone_obj = clone_call(obj);
if (EXPECTED(EG(exception) == NULL)) {
ZVAL_OBJ(EX_VAR(opline->result.var), clone_obj);
} else {
OBJ_RELEASE(clone_obj);
}
ZVAL_OBJ(EX_VAR(opline->result.var), clone_call(obj));
FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
@ -5745,7 +5720,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, JMP_ADDR)
Z_FE_POS_P(result) = 0;
FREE_OP1_IF_VAR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
HashPosition pos = 0;
@ -5898,7 +5873,7 @@ ZEND_VM_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, JMP_ADDR)
Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos);
FREE_OP1_VAR_PTR();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
@ -6850,11 +6825,15 @@ ZEND_VM_HANDLER(169, ZEND_COALESCE, CONST|TMP|VAR|CV, JMP_ADDR)
Z_ADDREF_P(result);
}
}
ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2));
if (UNEXPECTED(EG(exception))) {
zval_ptr_dtor_nogc(result);
HANDLE_EXCEPTION();
}
ZEND_VM_JMP_NO_EXCEPTION(OP_JMP_ADDR(opline, opline->op2));
}
FREE_OP1();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(22, ZEND_QM_ASSIGN, CONST|TMP|VAR|CV, ANY)
@ -7217,7 +7196,8 @@ ZEND_VM_HELPER(zend_dispatch_try_catch_finally_helper, ANY, ANY, uint32_t try_ca
ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
{
uint32_t throw_op_num = EG(opline_before_exception) - EX(func)->op_array.opcodes;
const zend_op *throw_op = EG(opline_before_exception);
uint32_t throw_op_num = throw_op - EX(func)->op_array.opcodes;
int i, current_try_catch_offset = -1;
{
@ -7246,6 +7226,27 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
cleanup_unfinished_calls(execute_data, throw_op_num);
if (throw_op->result_type & (IS_VAR | IS_TMP_VAR)) {
switch (throw_op->opcode) {
case ZEND_ADD_ARRAY_ELEMENT:
case ZEND_ROPE_ADD:
break; /* exception while building structures, live range handling will free those */
case ZEND_FETCH_CLASS:
case ZEND_DECLARE_CLASS:
case ZEND_DECLARE_INHERITED_CLASS:
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
break; /* return value is zend_class_entry pointer */
case ZEND_JMP_SET:
break; /* takes care of handling itself */
default:
zval_ptr_dtor_nogc(EX_VAR(throw_op->result.var));
}
}
ZEND_VM_DISPATCH_TO_HELPER(zend_dispatch_try_catch_finally_helper, try_catch_offset, current_try_catch_offset, op_num, throw_op_num);
}
@ -7849,7 +7850,7 @@ ZEND_VM_HANDLER(151, ZEND_ASSERT_CHECK, ANY, JMP_ADDR)
if (RETURN_VALUE_USED(opline)) {
ZVAL_TRUE(EX_VAR(opline->result.var));
}
ZEND_VM_JMP(target);
ZEND_VM_JMP_NO_EXCEPTION(target);
} else {
ZEND_VM_NEXT_OPCODE();
}
@ -8008,9 +8009,6 @@ ZEND_VM_C_LABEL(call_trampoline_end):
if (UNEXPECTED(EG(exception) != NULL)) {
zend_throw_exception_internal(NULL);
if (RETURN_VALUE_USED(opline)) {
zval_ptr_dtor(EX_VAR(opline->result.var));
}
HANDLE_EXCEPTION_LEAVE();
}

File diff suppressed because it is too large Load Diff

View File

@ -1607,7 +1607,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"#endif\n");
out($f,"#undef HANDLE_EXCEPTION\n");
out($f,"#undef HANDLE_EXCEPTION_LEAVE\n");
out($f,"#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
out($f,"#define HANDLE_EXCEPTION() ZEND_VM_UNDEF_RETVAL(); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
out($f,"#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n");
out($f,"#if defined(ZEND_VM_FP_GLOBAL_REG)\n");
out($f,"# define ZEND_VM_ENTER() execute_data = EG(current_execute_data); LOAD_OPLINE(); ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n");
@ -1646,7 +1646,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"#define SAVE_OPLINE() EX(opline) = opline\n");
out($f,"#undef HANDLE_EXCEPTION\n");
out($f,"#undef HANDLE_EXCEPTION_LEAVE\n");
out($f,"#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
out($f,"#define HANDLE_EXCEPTION() ZEND_VM_UNDEF_RETVAL(); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
out($f,"#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n");
out($f,"#define ZEND_VM_CONTINUE() goto zend_vm_continue\n");
out($f,"#define ZEND_VM_RETURN() return\n");
@ -1678,10 +1678,10 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name)
out($f,"#undef HANDLE_EXCEPTION\n");
out($f,"#undef HANDLE_EXCEPTION_LEAVE\n");
if (ZEND_VM_SPEC) {
out($f,"#define HANDLE_EXCEPTION() goto ZEND_HANDLE_EXCEPTION_SPEC_HANDLER\n");
out($f,"#define HANDLE_EXCEPTION() ZEND_VM_UNDEF_RETVAL(); goto ZEND_HANDLE_EXCEPTION_SPEC_HANDLER\n");
out($f,"#define HANDLE_EXCEPTION_LEAVE() goto ZEND_HANDLE_EXCEPTION_SPEC_HANDLER\n");
} else {
out($f,"#define HANDLE_EXCEPTION() goto ZEND_HANDLE_EXCEPTION_HANDLER\n");
out($f,"#define HANDLE_EXCEPTION() ZEND_VM_UNDEF_RETVAL(); goto ZEND_HANDLE_EXCEPTION_HANDLER\n");
out($f,"#define HANDLE_EXCEPTION_LEAVE() goto ZEND_HANDLE_EXCEPTION_HANDLER\n");
}
out($f,"#define ZEND_VM_CONTINUE() goto *(void**)(OPLINE->handler)\n");
@ -2441,7 +2441,7 @@ function gen_vm($def, $skel) {
out($f,"#define SAVE_OPLINE()\n");
out($f,"#undef HANDLE_EXCEPTION\n");
out($f,"#undef HANDLE_EXCEPTION_LEAVE\n");
out($f,"#define HANDLE_EXCEPTION() LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
out($f,"#define HANDLE_EXCEPTION() ZEND_VM_UNDEF_RETVAL(); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n");
out($f,"#define HANDLE_EXCEPTION_LEAVE() LOAD_OPLINE(); ZEND_VM_LEAVE()\n");
out($f,"#undef ZEND_VM_CONTINUE\n");
out($f,"#undef ZEND_VM_RETURN\n");