From 09547c64c28f81957d986b9c6b995b28d1098db6 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 2 Dec 2021 00:24:05 +0300 Subject: [PATCH] Fix clobering of operand by error handler in assignment to string offset In some cases new code requires two reallocations insead of one. Fixes oss-fuzz #31716, #36196, #39739 and #40002 --- Zend/tests/str_offset_006.phpt | 16 ++++ Zend/tests/str_offset_007.phpt | 16 ++++ Zend/zend_execute.c | 161 ++++++++++++++++++++++----------- Zend/zend_vm_def.h | 4 +- Zend/zend_vm_execute.h | 48 +++++----- Zend/zend_vm_gen.php | 14 ++- 6 files changed, 177 insertions(+), 82 deletions(-) create mode 100644 Zend/tests/str_offset_006.phpt create mode 100644 Zend/tests/str_offset_007.phpt diff --git a/Zend/tests/str_offset_006.phpt b/Zend/tests/str_offset_006.phpt new file mode 100644 index 00000000000..3cdf91656bd --- /dev/null +++ b/Zend/tests/str_offset_006.phpt @@ -0,0 +1,16 @@ +--TEST-- +string offset 006 indirect string modification by error handler +--FILE-- + +--EXPECT-- +Err: Undefined variable $y +Err: Undefined variable $y +Err: String offset cast occurred +NULL diff --git a/Zend/tests/str_offset_007.phpt b/Zend/tests/str_offset_007.phpt new file mode 100644 index 00000000000..3998ac401f6 --- /dev/null +++ b/Zend/tests/str_offset_007.phpt @@ -0,0 +1,16 @@ +--TEST-- +string offset 007 indirect string modification by error handler +--FILE-- + +--EXPECT-- +Err: Undefined variable $d +Err: String offset cast occurred +string(0) "" diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 49ac8027290..280c5aa5076 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1368,44 +1368,40 @@ static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type zend_long offset; try_again: - if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) { - switch(Z_TYPE_P(dim)) { - case IS_STRING: - { - bool trailing_data = false; - /* For BC reasons we allow errors so that we can warn on leading numeric string */ - if (IS_LONG == is_numeric_string_ex(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL, - /* allow errors */ true, NULL, &trailing_data)) { - if (UNEXPECTED(trailing_data) && type != BP_VAR_UNSET) { - zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim)); - } - return offset; + switch(Z_TYPE_P(dim)) { + case IS_LONG: + return Z_LVAL_P(dim); + case IS_STRING: + { + bool trailing_data = false; + /* For BC reasons we allow errors so that we can warn on leading numeric string */ + if (IS_LONG == is_numeric_string_ex(Z_STRVAL_P(dim), Z_STRLEN_P(dim), &offset, NULL, + /* allow errors */ true, NULL, &trailing_data)) { + if (UNEXPECTED(trailing_data) && type != BP_VAR_UNSET) { + zend_error(E_WARNING, "Illegal string offset \"%s\"", Z_STRVAL_P(dim)); } - zend_illegal_string_offset(dim); - return 0; + return offset; } - case IS_UNDEF: - ZVAL_UNDEFINED_OP2(); - case IS_DOUBLE: - case IS_NULL: - case IS_FALSE: - case IS_TRUE: - zend_error(E_WARNING, "String offset cast occurred"); - break; - case IS_REFERENCE: - dim = Z_REFVAL_P(dim); - goto try_again; - default: - zend_illegal_string_offset(dim); - return 0; + zend_illegal_string_offset(dim); + return 0; } - - offset = zval_get_long_func(dim); - } else { - offset = Z_LVAL_P(dim); + case IS_UNDEF: + ZVAL_UNDEFINED_OP2(); + case IS_DOUBLE: + case IS_NULL: + case IS_FALSE: + case IS_TRUE: + zend_error(E_WARNING, "String offset cast occurred"); + break; + case IS_REFERENCE: + dim = Z_REFVAL_P(dim); + goto try_again; + default: + zend_illegal_string_offset(dim); + return 0; } - return offset; + return zval_get_long_func(dim); } static zend_never_inline ZEND_COLD void zend_wrong_string_offset(EXECUTE_DATA_D) @@ -1535,17 +1531,43 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zend_uchar c; size_t string_len; zend_long offset; + zend_string *s; - offset = zend_check_string_offset(dim, BP_VAR_W EXECUTE_DATA_CC); - /* Illegal offset assignment */ - if (UNEXPECTED(EG(exception) != NULL)) { - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - } - return; + /* separate string */ + if (Z_REFCOUNTED_P(str) && Z_REFCOUNT_P(str) == 1) { + s = Z_STR_P(str); + } else { + s = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0); + ZSTR_H(s) = ZSTR_H(Z_STR_P(str)); + ZVAL_NEW_STR(str, s); } - if (offset < -(zend_long)Z_STRLEN_P(str)) { + if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) { + offset = Z_LVAL_P(dim); + } else { + /* The string may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(s); + } + offset = zend_check_string_offset(dim, BP_VAR_W EXECUTE_DATA_CC); + if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE) && GC_DELREF(s) == 0) { + zend_string_efree(s); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + return; + } + /* Illegal offset assignment */ + if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return; + } + } + + if (UNEXPECTED(offset < -(zend_long)ZSTR_LEN(s))) { /* Error on negative offset */ zend_error(E_WARNING, "Illegal string offset " ZEND_LONG_FMT, offset); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -1554,9 +1576,28 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, return; } - if (Z_TYPE_P(value) != IS_STRING) { + if (offset < 0) { /* Handle negative offset */ + offset += (zend_long)ZSTR_LEN(s); + } + + if (UNEXPECTED(Z_TYPE_P(value) != IS_STRING)) { + /* The string may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(s); + } + if (UNEXPECTED(Z_TYPE_P(value) == IS_UNDEF)) { + zval_undefined_cv((opline+1)->op1.var EXECUTE_DATA_CC); + } /* Convert to string, just the time to pick the 1st byte */ zend_string *tmp = zval_try_get_string_func(value); + if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE) && GC_DELREF(s) == 0) { + zend_string_efree(s); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + return; + } if (UNEXPECTED(!tmp)) { if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -1572,7 +1613,7 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, c = (zend_uchar)Z_STRVAL_P(value)[0]; } - if (string_len != 1) { + if (UNEXPECTED(string_len != 1)) { if (string_len == 0) { /* Error on empty input string */ zend_throw_error(NULL, "Cannot assign an empty string to a string offset"); @@ -1582,24 +1623,34 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, return; } + /* The string may be destroyed while throwing the notice. + * Temporarily increase the refcount to detect this situation. */ + if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(s); + } zend_error(E_WARNING, "Only the first byte will be assigned to the string offset"); + if (!(GC_FLAGS(s) & IS_ARRAY_IMMUTABLE) && GC_DELREF(s) == 0) { + zend_string_efree(s); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + return; + } + /* Illegal offset assignment */ + if (UNEXPECTED(EG(exception) != NULL)) { + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return; + } } - if (offset < 0) { /* Handle negative offset */ - offset += (zend_long)Z_STRLEN_P(str); - } - - if ((size_t)offset >= Z_STRLEN_P(str)) { + if ((size_t)offset >= ZSTR_LEN(s)) { /* Extend string if needed */ - zend_long old_len = Z_STRLEN_P(str); - ZVAL_NEW_STR(str, zend_string_extend(Z_STR_P(str), (size_t)offset + 1, 0)); + zend_long old_len = ZSTR_LEN(s); + ZVAL_NEW_STR(str, zend_string_extend(s, (size_t)offset + 1, 0)); memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len); Z_STRVAL_P(str)[offset+1] = 0; - } else if (!Z_REFCOUNTED_P(str)) { - ZVAL_NEW_STR(str, zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0)); - } else if (Z_REFCOUNT_P(str) > 1) { - Z_DELREF_P(str); - ZVAL_NEW_STR(str, zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0)); } else { zend_string_forget_hash_val(Z_STR_P(str)); } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 9b517a1a876..c13fc7baa73 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2590,8 +2590,8 @@ ZEND_VM_C_LABEL(try_assign_dim_array): FREE_OP_DATA(); UNDEF_RESULT(); } else { - dim = GET_OP2_ZVAL_PTR(BP_VAR_R); - value = GET_OP_DATA_ZVAL_PTR_DEREF(BP_VAR_R); + dim = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R); + value = GET_OP_DATA_ZVAL_PTR_UNDEF(BP_VAR_R); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); FREE_OP_DATA(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 1c2cbd008a1..9aea2d2b499 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -23476,7 +23476,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = RT_CONSTANT(opline, opline->op2); - value = _get_zval_ptr_var_deref((opline+1)->op1.var EXECUTE_DATA_CC); + value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } @@ -23588,7 +23588,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = RT_CONSTANT(opline, opline->op2); - value = _get_zval_ptr_cv_deref_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC); + value = EX_VAR((opline+1)->op1.var); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); } @@ -25988,7 +25988,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); - value = _get_zval_ptr_var_deref((opline+1)->op1.var EXECUTE_DATA_CC); + value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } @@ -26100,7 +26100,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); - value = _get_zval_ptr_cv_deref_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC); + value = EX_VAR((opline+1)->op1.var); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); } @@ -27356,7 +27356,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = NULL; - value = _get_zval_ptr_var_deref((opline+1)->op1.var EXECUTE_DATA_CC); + value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } @@ -27468,7 +27468,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = NULL; - value = _get_zval_ptr_cv_deref_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC); + value = EX_VAR((opline+1)->op1.var); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); } @@ -29656,7 +29656,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { - dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); + dim = EX_VAR(opline->op2.var); value = RT_CONSTANT((opline+1), (opline+1)->op1); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); @@ -29769,7 +29769,7 @@ try_assign_dim_array: zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); + dim = EX_VAR(opline->op2.var); value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); @@ -29882,8 +29882,8 @@ try_assign_dim_array: zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); - value = _get_zval_ptr_var_deref((opline+1)->op1.var EXECUTE_DATA_CC); + dim = EX_VAR(opline->op2.var); + value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } @@ -29994,8 +29994,8 @@ try_assign_dim_array: UNDEF_RESULT(); } else { - dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); - value = _get_zval_ptr_cv_deref_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC); + dim = EX_VAR(opline->op2.var); + value = EX_VAR((opline+1)->op1.var); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); } @@ -40734,7 +40734,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = RT_CONSTANT(opline, opline->op2); - value = _get_zval_ptr_var_deref((opline+1)->op1.var EXECUTE_DATA_CC); + value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } @@ -40846,7 +40846,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = RT_CONSTANT(opline, opline->op2); - value = _get_zval_ptr_cv_deref_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC); + value = EX_VAR((opline+1)->op1.var); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); } @@ -44320,7 +44320,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); - value = _get_zval_ptr_var_deref((opline+1)->op1.var EXECUTE_DATA_CC); + value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } @@ -44432,7 +44432,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); - value = _get_zval_ptr_cv_deref_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC); + value = EX_VAR((opline+1)->op1.var); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); } @@ -46134,7 +46134,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = NULL; - value = _get_zval_ptr_var_deref((opline+1)->op1.var EXECUTE_DATA_CC); + value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } @@ -46246,7 +46246,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { dim = NULL; - value = _get_zval_ptr_cv_deref_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC); + value = EX_VAR((opline+1)->op1.var); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); } @@ -49102,7 +49102,7 @@ try_assign_dim_array: UNDEF_RESULT(); } else { - dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); + dim = EX_VAR(opline->op2.var); value = RT_CONSTANT((opline+1), (opline+1)->op1); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); @@ -49215,7 +49215,7 @@ try_assign_dim_array: zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); + dim = EX_VAR(opline->op2.var); value = _get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); @@ -49328,8 +49328,8 @@ try_assign_dim_array: zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); UNDEF_RESULT(); } else { - dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); - value = _get_zval_ptr_var_deref((opline+1)->op1.var EXECUTE_DATA_CC); + dim = EX_VAR(opline->op2.var); + value = _get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR((opline+1)->op1.var)); } @@ -49440,8 +49440,8 @@ try_assign_dim_array: UNDEF_RESULT(); } else { - dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); - value = _get_zval_ptr_cv_deref_BP_VAR_R((opline+1)->op1.var EXECUTE_DATA_CC); + dim = EX_VAR(opline->op2.var); + value = EX_VAR((opline+1)->op1.var); zend_assign_to_string_offset(object_ptr, dim, value OPLINE_CC EXECUTE_DATA_CC); } diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index a8bcf96ef2d..98415a7cd01 100755 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -516,6 +516,17 @@ $op_data_get_zval_ptr = array( "TMPVARCV" => "???", ); +$op_data_get_zval_ptr_undef = array( + "ANY" => "get_op_data_zval_ptr_undef((opline+1)->op1_type, (opline+1)->op1)", + "TMP" => "_get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC)", + "VAR" => "_get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC)", + "CONST" => "RT_CONSTANT((opline+1), (opline+1)->op1)", + "UNUSED" => "NULL", + "CV" => "EX_VAR((opline+1)->op1.var)", + "TMPVAR" => "_get_zval_ptr_var((opline+1)->op1.var EXECUTE_DATA_CC)", + "TMPVARCV" => "EX_VAR((opline+1)->op1.var)", +); + $op_data_get_zval_ptr_deref = array( "ANY" => "get_op_data_zval_ptr_deref_r((opline+1)->op1_type, (opline+1)->op1)", "TMP" => "_get_zval_ptr_tmp((opline+1)->op1.var EXECUTE_DATA_CC)", @@ -719,7 +730,7 @@ function gen_code($f, $spec, $kind, $code, $op1, $op2, $name, $extra_spec=null) $op1_free_unfetched, $op2_free_unfetched, $op1_free_op, $op2_free_op, $op1_free_op_if_var, $op2_free_op_if_var, $op1_free_op_var_ptr, $op2_free_op_var_ptr, $prefix, - $op_data_type, $op_data_get_zval_ptr, + $op_data_type, $op_data_get_zval_ptr, $op_data_get_zval_ptr_undef, $op_data_get_zval_ptr_deref, $op_data_get_zval_ptr_ptr, $op_data_free_op, $op_data_free_op_var_ptr, $op_data_free_unfetched; @@ -763,6 +774,7 @@ function gen_code($f, $spec, $kind, $code, $op1, $op2, $name, $extra_spec=null) "/^#(\s*)elif\s+0\s*&&.*[^\\\\]$/m" => "#\\1elif 0", "/OP_DATA_TYPE/" => $op_data_type[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"], "/GET_OP_DATA_ZVAL_PTR\(([^)]*)\)/" => $op_data_get_zval_ptr[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"], + "/GET_OP_DATA_ZVAL_PTR_UNDEF\(([^)]*)\)/" => $op_data_get_zval_ptr_undef[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"], "/GET_OP_DATA_ZVAL_PTR_DEREF\(([^)]*)\)/" => $op_data_get_zval_ptr_deref[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"], "/GET_OP_DATA_ZVAL_PTR_PTR\(([^)]*)\)/" => $op_data_get_zval_ptr_ptr[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"], "/FREE_OP_DATA\(\)/" => $op_data_free_op[isset($extra_spec['OP_DATA']) ? $extra_spec['OP_DATA'] : "ANY"],