diff --git a/NEWS b/NEWS index d79257caf334..eff773f9248e 100644 --- a/NEWS +++ b/NEWS @@ -230,6 +230,8 @@ PHP NEWS . Fixed bug GH-24988 (Incorrect opcode range check causes bugs with exceptions in function JIT). (ndossche, Jorg Sowa) . Fix opcache phar check. (ndossche) + . Fixed memory leak in JIT ASSIGN_DIM when the assignment fails. + (jorgsowa) - OpenSSL: . Fixed build and test compatibility with OpenSSL 4.0 and 4.1. diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 9504b5bf79ec..b52f2cc83636 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -922,10 +922,6 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim ZVAL_NULL(EX_VAR(opline->result.var)); } } - if (opline->opcode == ZEND_ASSIGN_DIM - && ((opline+1)->op1_type & (IS_VAR | IS_TMP_VAR))) { - zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); - } return NULL; } ZEND_FALLTHROUGH; @@ -1003,13 +999,6 @@ static zval* ZEND_FASTCALL zend_jit_fetch_dim_w_helper(zend_array *ht, zval *dim default: zend_illegal_container_offset(ZSTR_KNOWN(ZEND_STR_ARRAY), dim, BP_VAR_R); undef_result_after_exception(); - if (EG(opline_before_exception) - && (EG(opline_before_exception)+1)->opcode == ZEND_OP_DATA - && ((EG(opline_before_exception)+1)->op1_type & (IS_VAR|IS_TMP_VAR))) { - zend_execute_data *execute_data = EG(current_execute_data); - - zval_ptr_dtor_nogc(EX_VAR((EG(opline_before_exception)+1)->op1.var)); - } return NULL; } @@ -1472,6 +1461,20 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_obj_rw_helper(zval *object_ptr, zva // zend_jit_fetch_dim_obj_helper(object_ptr, dim, result, BP_VAR_UNSET); //} +static zval* ZEND_FASTCALL zend_jit_assign_dim_fetch_w_helper(zend_array *ht, zval *dim) +{ + zval *retval = zend_jit_fetch_dim_w_helper(ht, dim); + + if (UNEXPECTED(!retval)) { + zend_execute_data *execute_data = EG(current_execute_data); + /* Throwing replaces EX(opline) with HANDLE_EXCEPTION */ + const zend_op *opline = EG(exception) ? EG(opline_before_exception) : EX(opline); + + zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); + } + return retval; +} + static void ZEND_FASTCALL zend_jit_assign_dim_helper(zval *object_ptr, zval *dim, zval *value, zval *result) { if (EXPECTED(Z_TYPE_P(object_ptr) == IS_OBJECT)) { diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 53959a536a4a..351f6d601a1a 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -3139,6 +3139,7 @@ static void zend_jit_setup_disasm(void) REGISTER_HELPER(zend_jit_fetch_dim_isset_helper); REGISTER_HELPER(zend_jit_fetch_dim_rw_helper); REGISTER_HELPER(zend_jit_fetch_dim_w_helper); + REGISTER_HELPER(zend_jit_assign_dim_fetch_w_helper); REGISTER_HELPER(zend_jit_fetch_dim_str_offset_r_helper); REGISTER_HELPER(zend_jit_fetch_dim_str_r_helper); REGISTER_HELPER(zend_jit_fetch_dim_str_is_helper); @@ -12437,7 +12438,11 @@ static int zend_jit_fetch_dimension_address_inner(zend_jit_ctx *jit, ir_END_list(*end_inputs); break; case BP_VAR_W: - ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_dim_w_helper), ht_ref, ref); + if (opline->opcode == ZEND_ASSIGN_DIM && ((opline+1)->op1_type & (IS_VAR|IS_TMP_VAR))) { + ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_assign_dim_fetch_w_helper), ht_ref, ref); + } else { + ref = ir_CALL_2(IR_ADDR, ir_CONST_FC_FUNC(zend_jit_fetch_dim_w_helper), ht_ref, ref); + } if_found = ir_IF(ref); ir_IF_TRUE(if_found); ir_refs_add(found_inputs, ir_END()); @@ -13373,6 +13378,7 @@ static int zend_jit_assign_dim(zend_jit_ctx *jit, // JIT: zend_throw_error(NULL, "Cannot add element to the array as the next element is already occupied"); jit_SET_EX_OPLINE(jit, opline); ir_CALL(IR_VOID, jit_STUB_FUNC_ADDR(jit, jit_stub_cannot_add_element, IR_FASTCALL_FUNC)); + jit_FREE_OP(jit, (opline+1)->op1_type, (opline+1)->op1, val_info, NULL); ir_END_list(end_inputs); diff --git a/ext/opcache/tests/jit/assign_dim_018.phpt b/ext/opcache/tests/jit/assign_dim_018.phpt new file mode 100644 index 000000000000..3fee8d5a70ac --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_018.phpt @@ -0,0 +1,31 @@ +--TEST-- +JIT ASSIGN_DIM: OP_DATA is freed when appending to a full array fails +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- + 1]; + $a[] = new D; +} + +try { + test(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} +?> +--EXPECT-- +dtor +Error: Cannot add element to the array as the next element is already occupied diff --git a/ext/opcache/tests/jit/assign_dim_019.phpt b/ext/opcache/tests/jit/assign_dim_019.phpt new file mode 100644 index 000000000000..284757362a2d --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_019.phpt @@ -0,0 +1,34 @@ +--TEST-- +JIT ASSIGN_DIM: OP_DATA is freed when a float key conversion throws +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECT-- +dtor +Exception: Implicit conversion from float 1.5 to int loses precision diff --git a/ext/opcache/tests/jit/assign_dim_020.phpt b/ext/opcache/tests/jit/assign_dim_020.phpt new file mode 100644 index 000000000000..98f5f9842809 --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_020.phpt @@ -0,0 +1,34 @@ +--TEST-- +JIT ASSIGN_DIM: OP_DATA is freed when a resource key conversion throws +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECTF-- +dtor +Exception: Resource ID#%d used as offset, casting to integer (%d) diff --git a/ext/opcache/tests/jit/assign_dim_021.phpt b/ext/opcache/tests/jit/assign_dim_021.phpt new file mode 100644 index 000000000000..2d3c89c67079 --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_021.phpt @@ -0,0 +1,42 @@ +--TEST-- +JIT ASSIGN_DIM: OP_DATA is freed once when a float key conversion throws on a false container +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +zend.exception_ignore_args=1 +--EXTENSIONS-- +opcache +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECT-- +dtor +Exception: Implicit conversion from float 1.5 to int loses precision diff --git a/ext/opcache/tests/jit/assign_dim_022.phpt b/ext/opcache/tests/jit/assign_dim_022.phpt new file mode 100644 index 000000000000..de263bcb00c9 --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_022.phpt @@ -0,0 +1,42 @@ +--TEST-- +JIT ASSIGN_DIM: OP_DATA is freed once when a resource key conversion throws on a false container +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +zend.exception_ignore_args=1 +--EXTENSIONS-- +opcache +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECTF-- +dtor +Exception: Resource ID#%d used as offset, casting to integer (%d) diff --git a/ext/opcache/tests/jit/assign_dim_023.phpt b/ext/opcache/tests/jit/assign_dim_023.phpt new file mode 100644 index 000000000000..a398ba9f4872 --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_023.phpt @@ -0,0 +1,30 @@ +--TEST-- +JIT ASSIGN_DIM: OP_DATA is freed when the key is an illegal offset +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECT-- +dtor +TypeError: Cannot access offset of type array on array diff --git a/ext/opcache/tests/jit/assign_dim_024.phpt b/ext/opcache/tests/jit/assign_dim_024.phpt new file mode 100644 index 000000000000..2111a2d2dcec --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_024.phpt @@ -0,0 +1,34 @@ +--TEST-- +JIT ASSIGN_DIM: OP_DATA is freed when an undefined key warning throws +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECT-- +dtor +Exception: Undefined variable $k diff --git a/ext/opcache/tests/jit/assign_dim_025.phpt b/ext/opcache/tests/jit/assign_dim_025.phpt new file mode 100644 index 000000000000..0b5a48c1684c --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_025.phpt @@ -0,0 +1,34 @@ +--TEST-- +JIT ASSIGN_DIM: OP_DATA is freed when the array is destroyed by a float key notice +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +dtor +NULL diff --git a/ext/opcache/tests/jit/assign_dim_026.phpt b/ext/opcache/tests/jit/assign_dim_026.phpt new file mode 100644 index 000000000000..005fd427d36f --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_026.phpt @@ -0,0 +1,34 @@ +--TEST-- +JIT ASSIGN_DIM: OP_DATA is freed when the array is destroyed by an undefined key warning +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +dtor +NULL diff --git a/ext/opcache/tests/jit/assign_dim_027.phpt b/ext/opcache/tests/jit/assign_dim_027.phpt new file mode 100644 index 000000000000..b516b431e4a8 --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_027.phpt @@ -0,0 +1,32 @@ +--TEST-- +JIT ASSIGN_DIM: OP_DATA is freed when appending to a full array fails and the result is used +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- + 1]; + $x = ($a[] = new D); + var_dump($x); +} + +try { + test(); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} +?> +--EXPECT-- +dtor +Error: Cannot add element to the array as the next element is already occupied