From a60a9b4a89b4fa40f8980a0a35a7088860cb8a4d Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Tue, 1 Feb 2022 16:24:22 +0300 Subject: [PATCH] Fix memory leak Fixes oss-fuzz #44222 --- Zend/tests/nullsafe_operator/040.phpt | 19 +++ Zend/zend_vm_def.h | 55 +++--- Zend/zend_vm_execute.h | 234 ++++++++++++++++++-------- Zend/zend_vm_handlers.h | 6 +- Zend/zend_vm_opcodes.c | 2 +- 5 files changed, 219 insertions(+), 97 deletions(-) create mode 100644 Zend/tests/nullsafe_operator/040.phpt 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 fca5af71da7..e0687a23fd0 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7422,39 +7422,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 f569ff6ba8f..922ed92e560 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -5084,36 +5084,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) @@ -11855,41 +11860,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 @@ -19137,6 +19107,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 @@ -22034,6 +22044,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 @@ -38421,6 +38471,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 @@ -53574,10 +53664,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_RECV_NOTYPE_SPEC_LABEL, (void*)&&ZEND_JMP_FORWARD_SPEC_LABEL, @@ -55732,10 +55822,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); @@ -56637,6 +56723,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); @@ -56942,6 +57032,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); @@ -58067,6 +58161,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); @@ -61597,10 +61695,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_RECV_NOTYPE_SPEC_HANDLER, ZEND_JMP_FORWARD_SPEC_HANDLER, diff --git a/Zend/zend_vm_handlers.h b/Zend/zend_vm_handlers.h index a2e85dde9b1..ceab588d105 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_RECV_NOTYPE_SPEC) \ _(2548, ZEND_JMP_FORWARD_SPEC) \ diff --git a/Zend/zend_vm_opcodes.c b/Zend/zend_vm_opcodes.c index 56a31aa19a0..0afcc8ecc8b 100644 --- a/Zend/zend_vm_opcodes.c +++ b/Zend/zend_vm_opcodes.c @@ -424,7 +424,7 @@ static uint32_t zend_vm_opcodes_flags[200] = { 0x0300030b, 0x00000301, 0x0000010b, - 0x0000200b, + 0x00002003, 0x00000101, };