diff --git a/Zend/tests/nullsafe_operator/040.phpt b/Zend/tests/nullsafe_operator/040.phpt new file mode 100644 index 00000000000..1bfdaf753df --- /dev/null +++ b/Zend/tests/nullsafe_operator/040.phpt @@ -0,0 +1,19 @@ +--TEST-- +Memory leak in JMP_NULL +--FILE-- +null); +} + +$foo2 = &returns_ref2(); +$foo2 = 'foo'; +var_dump($foo); +?> +--EXPECT-- +string(3) "foo" diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 6920bc7632a..c689929822d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7513,39 +7513,44 @@ ZEND_VM_COLD_CONST_HANDLER(169, ZEND_COALESCE, CONST|TMP|VAR|CV, JMP_ADDR) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HOT_NOCONST_HANDLER(198, ZEND_JMP_NULL, CONST|TMPVARCV, JMP_ADDR) +ZEND_VM_HOT_NOCONST_HANDLER(198, ZEND_JMP_NULL, CONST|TMP|VAR|CV, JMP_ADDR) { USE_OPLINE - zval *val; + zval *val, *result; val = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); - if (OP1_TYPE != IS_CONST) { - ZVAL_DEREF(val); - } - if (Z_TYPE_INFO_P(val) > IS_NULL) { - ZEND_VM_NEXT_OPCODE(); - } else { - zval *result = EX_VAR(opline->result.var); - - if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) { - ZVAL_NULL(result); - if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) { - SAVE_OPLINE(); - ZVAL_UNDEFINED_OP1(); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); + if (Z_TYPE_P(val) > IS_NULL) { + do { + if ((OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) && Z_TYPE_P(val) == IS_REFERENCE) { + val = Z_REFVAL_P(val); + if (Z_TYPE_P(val) <= IS_NULL) { + FREE_OP1(); + break; } } - } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) { - ZVAL_FALSE(result); - } else { - ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY); - ZVAL_TRUE(result); - } - - ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0); + ZEND_VM_NEXT_OPCODE(); + } while (0); } + + result = EX_VAR(opline->result.var); + if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) { + ZVAL_NULL(result); + if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) { + SAVE_OPLINE(); + ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + } + } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) { + ZVAL_FALSE(result); + } else { + ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY); + ZVAL_TRUE(result); + } + + ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0); } ZEND_VM_HOT_HANDLER(31, ZEND_QM_ASSIGN, CONST|TMP|VAR|CV, ANY) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 42f5310cd02..ac165c3b22f 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -5236,36 +5236,41 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_CON static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zval *val; + zval *val, *result; val = RT_CONSTANT(opline, opline->op1); - if (IS_CONST != IS_CONST) { - ZVAL_DEREF(val); - } - if (Z_TYPE_INFO_P(val) > IS_NULL) { - ZEND_VM_NEXT_OPCODE(); - } else { - zval *result = EX_VAR(opline->result.var); + if (Z_TYPE_P(val) > IS_NULL) { + do { + if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) && Z_TYPE_P(val) == IS_REFERENCE) { + val = Z_REFVAL_P(val); + if (Z_TYPE_P(val) <= IS_NULL) { - if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) { - ZVAL_NULL(result); - if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) { - SAVE_OPLINE(); - ZVAL_UNDEFINED_OP1(); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); + break; } } - } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) { - ZVAL_FALSE(result); - } else { - ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY); - ZVAL_TRUE(result); - } - - ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0); + ZEND_VM_NEXT_OPCODE(); + } while (0); } + + result = EX_VAR(opline->result.var); + if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) { + ZVAL_NULL(result); + if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) { + SAVE_OPLINE(); + ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + } + } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) { + ZVAL_FALSE(result); + } else { + ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY); + ZVAL_TRUE(result); + } + + ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0); } static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) @@ -12045,41 +12050,6 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BW_NOT_SPEC_TMPVAR ZEND_VM_TAIL_CALL(zend_bw_not_helper_SPEC(op1 ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_CC)); } -static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - zval *val; - - val = EX_VAR(opline->op1.var); - if ((IS_TMP_VAR|IS_VAR|IS_CV) != IS_CONST) { - ZVAL_DEREF(val); - } - - if (Z_TYPE_INFO_P(val) > IS_NULL) { - ZEND_VM_NEXT_OPCODE(); - } else { - zval *result = EX_VAR(opline->result.var); - - if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) { - ZVAL_NULL(result); - if (UNEXPECTED(Z_TYPE_INFO_P(val) == IS_UNDEF)) { - SAVE_OPLINE(); - ZVAL_UNDEFINED_OP1(); - if (UNEXPECTED(EG(exception) != NULL)) { - HANDLE_EXCEPTION(); - } - } - } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) { - ZVAL_FALSE(result); - } else { - ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY); - ZVAL_TRUE(result); - } - - ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0); - } -} - static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -19353,6 +19323,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_TMP_HANDLER(ZEND ZEND_VM_NEXT_OPCODE(); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *val, *result; + + val = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC); + + if (Z_TYPE_P(val) > IS_NULL) { + do { + if ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) && Z_TYPE_P(val) == IS_REFERENCE) { + val = Z_REFVAL_P(val); + if (Z_TYPE_P(val) <= IS_NULL) { + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + break; + } + } + ZEND_VM_NEXT_OPCODE(); + } while (0); + } + + result = EX_VAR(opline->result.var); + if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) { + ZVAL_NULL(result); + if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) { + SAVE_OPLINE(); + ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + } + } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) { + ZVAL_FALSE(result); + } else { + ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY); + ZVAL_TRUE(result); + } + + ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -22200,6 +22210,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_VAR_HANDLER(ZEND ZEND_VM_NEXT_OPCODE(); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *val, *result; + + val = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); + + if (Z_TYPE_P(val) > IS_NULL) { + do { + if ((IS_VAR == IS_CV || IS_VAR == IS_VAR) && Z_TYPE_P(val) == IS_REFERENCE) { + val = Z_REFVAL_P(val); + if (Z_TYPE_P(val) <= IS_NULL) { + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); + break; + } + } + ZEND_VM_NEXT_OPCODE(); + } while (0); + } + + result = EX_VAR(opline->result.var); + if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) { + ZVAL_NULL(result); + if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) { + SAVE_OPLINE(); + ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + } + } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) { + ZVAL_FALSE(result); + } else { + ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY); + ZVAL_TRUE(result); + } + + ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -38801,6 +38851,46 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COALESCE_SPEC_CV_HANDLER(ZEND_ ZEND_VM_NEXT_OPCODE(); } +static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + zval *val, *result; + + val = EX_VAR(opline->op1.var); + + if (Z_TYPE_P(val) > IS_NULL) { + do { + if ((IS_CV == IS_CV || IS_CV == IS_VAR) && Z_TYPE_P(val) == IS_REFERENCE) { + val = Z_REFVAL_P(val); + if (Z_TYPE_P(val) <= IS_NULL) { + + break; + } + } + ZEND_VM_NEXT_OPCODE(); + } while (0); + } + + result = EX_VAR(opline->result.var); + if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) { + ZVAL_NULL(result); + if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) { + SAVE_OPLINE(); + ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception) != NULL)) { + HANDLE_EXCEPTION(); + } + } + } else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) { + ZVAL_FALSE(result); + } else { + ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY); + ZVAL_TRUE(result); + } + + ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0); +} + static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_QM_ASSIGN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -54176,10 +54266,10 @@ ZEND_API void execute_ex(zend_execute_data *ex) (void*)&&ZEND_NULL_LABEL, (void*)&&ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED_LABEL, (void*)&&ZEND_JMP_NULL_SPEC_CONST_LABEL, - (void*)&&ZEND_JMP_NULL_SPEC_TMPVARCV_LABEL, - (void*)&&ZEND_JMP_NULL_SPEC_TMPVARCV_LABEL, + (void*)&&ZEND_JMP_NULL_SPEC_TMP_LABEL, + (void*)&&ZEND_JMP_NULL_SPEC_VAR_LABEL, (void*)&&ZEND_NULL_LABEL, - (void*)&&ZEND_JMP_NULL_SPEC_TMPVARCV_LABEL, + (void*)&&ZEND_JMP_NULL_SPEC_CV_LABEL, (void*)&&ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_LABEL, (void*)&&ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_LABEL, (void*)&&ZEND_VERIFY_NEVER_TYPE_SPEC_UNUSED_UNUSED_LABEL, @@ -56344,10 +56434,6 @@ zend_leave_helper_SPEC_LABEL: VM_TRACE(ZEND_BW_NOT_SPEC_TMPVARCV) ZEND_BW_NOT_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); - HYBRID_CASE(ZEND_JMP_NULL_SPEC_TMPVARCV): - VM_TRACE(ZEND_JMP_NULL_SPEC_TMPVARCV) - ZEND_JMP_NULL_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); - HYBRID_BREAK(); HYBRID_CASE(ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV): VM_TRACE(ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV) ZEND_QM_ASSIGN_LONG_SPEC_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -57249,6 +57335,10 @@ zend_leave_helper_SPEC_LABEL: VM_TRACE(ZEND_COALESCE_SPEC_TMP) ZEND_COALESCE_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); + HYBRID_CASE(ZEND_JMP_NULL_SPEC_TMP): + VM_TRACE(ZEND_JMP_NULL_SPEC_TMP) + ZEND_JMP_NULL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + HYBRID_BREAK(); HYBRID_CASE(ZEND_QM_ASSIGN_SPEC_TMP): VM_TRACE(ZEND_QM_ASSIGN_SPEC_TMP) ZEND_QM_ASSIGN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -57554,6 +57644,10 @@ zend_leave_helper_SPEC_LABEL: VM_TRACE(ZEND_COALESCE_SPEC_VAR) ZEND_COALESCE_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); + HYBRID_CASE(ZEND_JMP_NULL_SPEC_VAR): + VM_TRACE(ZEND_JMP_NULL_SPEC_VAR) + ZEND_JMP_NULL_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + HYBRID_BREAK(); HYBRID_CASE(ZEND_QM_ASSIGN_SPEC_VAR): VM_TRACE(ZEND_QM_ASSIGN_SPEC_VAR) ZEND_QM_ASSIGN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -58691,6 +58785,10 @@ zend_leave_helper_SPEC_LABEL: VM_TRACE(ZEND_COALESCE_SPEC_CV) ZEND_COALESCE_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); HYBRID_BREAK(); + HYBRID_CASE(ZEND_JMP_NULL_SPEC_CV): + VM_TRACE(ZEND_JMP_NULL_SPEC_CV) + ZEND_JMP_NULL_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + HYBRID_BREAK(); HYBRID_CASE(ZEND_QM_ASSIGN_SPEC_CV): VM_TRACE(ZEND_QM_ASSIGN_SPEC_CV) ZEND_QM_ASSIGN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -62221,10 +62319,10 @@ void zend_vm_init(void) ZEND_NULL_HANDLER, ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED_HANDLER, ZEND_JMP_NULL_SPEC_CONST_HANDLER, - ZEND_JMP_NULL_SPEC_TMPVARCV_HANDLER, - ZEND_JMP_NULL_SPEC_TMPVARCV_HANDLER, + ZEND_JMP_NULL_SPEC_TMP_HANDLER, + ZEND_JMP_NULL_SPEC_VAR_HANDLER, ZEND_NULL_HANDLER, - ZEND_JMP_NULL_SPEC_TMPVARCV_HANDLER, + ZEND_JMP_NULL_SPEC_CV_HANDLER, ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED_HANDLER, ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED_HANDLER, ZEND_VERIFY_NEVER_TYPE_SPEC_UNUSED_UNUSED_HANDLER, diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index a5796187e11..2c73140f434 100644 --- a/Zend/zend_vm_handlers.h +++ b/Zend/zend_vm_handlers.h @@ -1348,9 +1348,9 @@ _(2538, ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED) \ _(2540, ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED) \ _(2541, ZEND_JMP_NULL_SPEC_CONST) \ - _(2542, ZEND_JMP_NULL_SPEC_TMPVARCV) \ - _(2543, ZEND_JMP_NULL_SPEC_TMPVARCV) \ - _(2545, ZEND_JMP_NULL_SPEC_TMPVARCV) \ + _(2542, ZEND_JMP_NULL_SPEC_TMP) \ + _(2543, ZEND_JMP_NULL_SPEC_VAR) \ + _(2545, ZEND_JMP_NULL_SPEC_CV) \ _(2546, ZEND_CHECK_UNDEF_ARGS_SPEC_UNUSED_UNUSED) \ _(2547, ZEND_FETCH_GLOBALS_SPEC_UNUSED_UNUSED) \ _(2548, ZEND_VERIFY_NEVER_TYPE_SPEC_UNUSED_UNUSED) \ diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 085c473d595..42cfa60418f 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -427,7 +427,7 @@ static uint32_t zend_vm_opcodes_flags[203] = { 0x0300030b, 0x00000301, 0x0000010b, - 0x0000200b, + 0x00002003, 0x00000101, 0x00000101, 0x00000101,