diff --git a/.github/scripts/download-bundled/uriparser.sh b/.github/scripts/download-bundled/uriparser.sh index 8973962fb5d4..2b8d61a2d56f 100755 --- a/.github/scripts/download-bundled/uriparser.sh +++ b/.github/scripts/download-bundled/uriparser.sh @@ -5,7 +5,7 @@ cd "$(dirname "$0")/../../.." tmp_dir=/tmp/php-src-download-bundled/uriparser rm -rf "$tmp_dir" -revision=refs/tags/uriparser-1.0.1 +revision=refs/tags/uriparser-1.0.2 git clone --depth 1 --revision="$revision" https://github.com/uriparser/uriparser.git "$tmp_dir" diff --git a/NEWS b/NEWS index 239becd194f6..1200d086067c 100644 --- a/NEWS +++ b/NEWS @@ -181,6 +181,8 @@ PHP NEWS with re-entrant getHash()). (Pratik Bhujel) . Fix bugs GH-8561, GH-8562, GH-8563, and GH-8564 (Fixing various SplFileObject iterator desync bugs). (iliaal) + . Fix bug GH-22062 (SplDoublyLinkedList iterator UAF + via destructor releasing next node). (David Carlier) - Sqlite3: . Fix NUL byte truncation in sqlite3 TEXT column handling. (ndossche) diff --git a/Zend/zend.c b/Zend/zend.c index f83389a96a6e..f16b1a30dbbc 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -811,6 +811,7 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ executor_globals->user_error_handler_error_reporting = 0; ZVAL_UNDEF(&executor_globals->user_error_handler); ZVAL_UNDEF(&executor_globals->user_exception_handler); + ZVAL_UNDEF(&executor_globals->last_fatal_error_backtrace); executor_globals->current_execute_data = NULL; executor_globals->current_module = NULL; executor_globals->exit_status = 0; diff --git a/Zend/zend_vm.h b/Zend/zend_vm.h index 093b1158bd82..ebb542eb35bc 100644 --- a/Zend/zend_vm.h +++ b/Zend/zend_vm.h @@ -31,6 +31,7 @@ ZEND_API void ZEND_FASTCALL zend_serialize_opcode_handler(zend_op *op); ZEND_API void ZEND_FASTCALL zend_deserialize_opcode_handler(zend_op *op); ZEND_API const void* ZEND_FASTCALL zend_get_opcode_handler_func(const zend_op *op); ZEND_API const zend_op *zend_get_halt_op(void); +ZEND_API const zend_op *zend_get_interrupt_op(void); ZEND_API int ZEND_FASTCALL zend_vm_call_opcode_handler(zend_execute_data *ex); ZEND_API int zend_vm_kind(void); ZEND_API bool zend_gcc_global_regs(void); @@ -38,6 +39,10 @@ ZEND_API bool zend_gcc_global_regs(void); void zend_vm_init(void); void zend_vm_dtor(void); +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL +const struct _zend_op *zend_vm_handle_interrupt(struct _zend_execute_data *execute_data, const struct _zend_op *opline); +#endif + END_EXTERN_C() #define ZEND_VM_SET_OPCODE_HANDLER(opline) zend_vm_set_opcode_handler(opline) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 05923bbc2b61..1de7a7cd4195 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -10624,7 +10624,12 @@ ZEND_VM_DEFINE_OP(137, ZEND_OP_DATA); ZEND_VM_HELPER(zend_interrupt_helper, ANY, ANY) { zend_atomic_bool_store_ex(&EG(vm_interrupt), false); +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + /* opline is &call_interrupt_op. Load orig opline. */ + LOAD_OPLINE(); +#else SAVE_OPLINE(); +#endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 2b5b9e5fcd47..5b52f1941845 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -330,6 +330,11 @@ static zend_op hybrid_halt_op; static zend_vm_opcode_handler_func_t const * zend_opcode_handler_funcs; #endif + +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL +static const zend_op call_interrupt_op; +#endif + #if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID && ZEND_VM_KIND != ZEND_VM_KIND_TAILCALL) || !ZEND_VM_SPEC static zend_vm_opcode_handler_t zend_vm_get_opcode_handler(uint8_t opcode, const zend_op* op); #endif @@ -4027,7 +4032,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_J static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) { zend_atomic_bool_store_ex(&EG(vm_interrupt), false); +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + /* opline is &call_interrupt_op. Load orig opline. */ + LOAD_OPLINE(); +#else SAVE_OPLINE(); +#endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { @@ -53488,6 +53498,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NULL_HANDLER( zend_error_noreturn(E_ERROR, "Invalid opcode %d/%d/%d.", OPLINE->opcode, OPLINE->op1_type, OPLINE->op2_type); } +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL +static ZEND_COLD zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt(ZEND_OPCODE_HANDLER_ARGS) { + SAVE_OPLINE(); + return &call_interrupt_op; +} +#endif #if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) # undef ZEND_VM_TAIL_CALL @@ -53518,11 +53534,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NULL_HANDLER( ZEND_VM_TAIL_CALL(opline->handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); \ } while (0) # define ZEND_VM_DISPATCH_TO_LEAVE_HELPER(helper) opline = &call_leave_op; SAVE_OPLINE(); ZEND_VM_CONTINUE() -# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) +# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) # define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE() # define ZEND_VM_LEAVE() ZEND_VM_CONTINUE() static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS); +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt(ZEND_OPCODE_HANDLER_ARGS); +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS); static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NULL_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS); static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HALT_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS); static zend_never_inline const zend_op *ZEND_OPCODE_HANDLER_CCONV zend_leave_helper_SPEC_TAILCALL(zend_execute_data *ex, const zend_op *opline); @@ -53533,6 +53551,9 @@ static const zend_op call_halt_op = { static const zend_op call_leave_op = { .handler = zend_leave_helper_SPEC_TAILCALL, }; +static const zend_op call_interrupt_op = { + .handler = zend_interrupt_helper_SPEC_TAILCALL, +}; static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV_EX zend_add_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2); static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV_EX zend_sub_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2); @@ -56685,7 +56706,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_JMP_FO static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS) { zend_atomic_bool_store_ex(&EG(vm_interrupt), false); +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + /* opline is &call_interrupt_op. Load orig opline. */ + LOAD_OPLINE(); +#else SAVE_OPLINE(); +#endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { @@ -105847,13 +105873,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HALT_TAILCALL_HAND return (zend_op*) ZEND_VM_ENTER_BIT; } +static ZEND_COLD zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS) { + SAVE_OPLINE(); + ZEND_VM_TAIL_CALL(zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); +} /* The following helpers can not tailcall due to signature mismatch. Redefine some macros so they do not enforce tailcall. */ #pragma push_macro("ZEND_VM_CONTINUE") #undef ZEND_VM_CONTINUE #pragma push_macro("ZEND_VM_INTERRUPT") #undef ZEND_VM_INTERRUPT #define ZEND_VM_CONTINUE(handler) return opline -#define ZEND_VM_INTERRUPT() return zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) +#define ZEND_VM_INTERRUPT() return zend_interrupt(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV_EX zend_add_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2) { USE_OPLINE @@ -122896,6 +122926,22 @@ ZEND_API const zend_op *zend_get_halt_op(void) #endif } +ZEND_API const zend_op *zend_get_interrupt_op(void) +{ +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + return &call_interrupt_op; +#else + return NULL; +#endif +} + +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL +ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_vm_handle_interrupt(ZEND_OPCODE_HANDLER_ARGS) +{ + return zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); +} +#endif + ZEND_API int zend_vm_kind(void) { return ZEND_VM_KIND; diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index 3237a696a695..beabe36688df 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -161,6 +161,22 @@ ZEND_API const zend_op *zend_get_halt_op(void) #endif } +ZEND_API const zend_op *zend_get_interrupt_op(void) +{ +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + return &call_interrupt_op; +#else + return NULL; +#endif +} + +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL +ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_vm_handle_interrupt(ZEND_OPCODE_HANDLER_ARGS) +{ + return zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); +} +#endif + ZEND_API int zend_vm_kind(void) { return ZEND_VM_KIND; diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index d26d6a166dda..8e65bc0a28ea 100755 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -1591,6 +1591,19 @@ function gen_halt_handler($f, $kind) { out($f,"}\n\n"); } +function gen_interrupt_func($f, $kind, $spec) { + $cconv = $kind === ZEND_VM_KIND_TAILCALL ? 'ZEND_OPCODE_HANDLER_CCONV' : 'ZEND_OPCODE_HANDLER_FUNC_CCONV'; + $variant = $kind === ZEND_VM_KIND_TAILCALL ? '_TAILCALL' : ''; + out($f, "static ZEND_COLD zend_never_inline ZEND_OPCODE_HANDLER_RET {$cconv} zend_interrupt{$variant}(ZEND_OPCODE_HANDLER_ARGS) {\n"); + out($f,"\tSAVE_OPLINE();\n"); + if ($kind === ZEND_VM_KIND_TAILCALL) { + out($f,"\tZEND_VM_TAIL_CALL(zend_interrupt_helper".($spec?"_SPEC":"")."_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));\n"); + } else { + out($f, "\treturn &call_interrupt_op;\n"); + } + out($f, "}\n"); +} + function extra_spec_name($extra_spec) { global $prefix; @@ -1801,10 +1814,14 @@ function gen_executor_code($f, $spec, $kind, $prolog, &$switch_labels = array()) switch ($kind) { case ZEND_VM_KIND_CALL: gen_null_handler($f, $kind); + out($f, "#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL\n"); + gen_interrupt_func($f, $kind, $spec); + out($f, "#endif\n"); break; case ZEND_VM_KIND_TAILCALL: gen_null_handler($f, $kind); gen_halt_handler($f, $kind); + gen_interrupt_func($f, $kind, $spec); break; case ZEND_VM_KIND_SWITCH: out($f,"default: ZEND_NULL_LABEL:\n"); @@ -1840,7 +1857,7 @@ function gen_executor_code($f, $spec, $kind, $prolog, &$switch_labels = array()) out($f, "#pragma push_macro(\"ZEND_VM_INTERRUPT\")\n"); out($f, "#undef ZEND_VM_INTERRUPT\n"); out($f, "#define ZEND_VM_CONTINUE(handler) return opline\n"); - out($f, "#define ZEND_VM_INTERRUPT() return zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)\n"); + out($f, "#define ZEND_VM_INTERRUPT() return zend_interrupt(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)\n"); out($f, $delayed_helpers); out($f, "#pragma pop_macro(\"ZEND_VM_INTERRUPT\")\n"); out($f, "#pragma pop_macro(\"ZEND_VM_CONTINUE\")\n"); @@ -1895,7 +1912,10 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) if ($kind == ZEND_VM_KIND_HYBRID || $kind == ZEND_VM_KIND_CALL) { out($f,"#if ZEND_VM_KIND == ZEND_VM_KIND_HYBRID || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL\n\n"); out($f,"static zend_vm_opcode_handler_func_t const * zend_opcode_handler_funcs;\n"); - out($f,"#endif\n"); + out($f,"#endif\n\n"); + out($f,"#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL\n"); + out($f,"static const zend_op call_interrupt_op;\n"); + out($f,"#endif\n\n"); } out($f,"#if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID && ZEND_VM_KIND != ZEND_VM_KIND_TAILCALL) || !ZEND_VM_SPEC\n"); out($f,"static zend_vm_opcode_handler_t zend_vm_get_opcode_handler(uint8_t opcode, const zend_op* op);\n"); @@ -2136,11 +2156,13 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f," ZEND_VM_TAIL_CALL(opline->handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); \\\n"); out($f," } while (0)\n"); out($f,"# define ZEND_VM_DISPATCH_TO_LEAVE_HELPER(helper) opline = &call_leave_op; SAVE_OPLINE(); ZEND_VM_CONTINUE()\n"); - out($f,"# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_helper".($spec?"_SPEC":"")."_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))\n"); + out($f,"# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))\n"); out($f,"# define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n"); out($f,"# define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n"); out($f,"\n"); out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper".($spec?"_SPEC":"")."_TAILCALL(ZEND_OPCODE_HANDLER_ARGS);\n"); + out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt(ZEND_OPCODE_HANDLER_ARGS);\n"); + out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS);\n"); out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NULL_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS);\n"); out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HALT_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS);\n"); out($f,"static zend_never_inline const zend_op *ZEND_OPCODE_HANDLER_CCONV zend_leave_helper_SPEC_TAILCALL(zend_execute_data *ex, const zend_op *opline);\n"); @@ -2151,6 +2173,9 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f,"static const zend_op call_leave_op = {\n"); out($f," .handler = zend_leave_helper_SPEC_TAILCALL,\n"); out($f,"};\n"); + out($f,"static const zend_op call_interrupt_op = {\n"); + out($f," .handler = zend_interrupt_helper_SPEC_TAILCALL,\n"); + out($f,"};\n"); out($f,"\n"); gen_executor_code($f, $spec, ZEND_VM_KIND_TAILCALL, $m[1]); diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 18ac305e8430..c77489af1b0d 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -29,13 +29,7 @@ #include "win32/time.h" #endif -#ifdef PHP_WIN32 -static __inline __int64 php_date_llabs( __int64 i ) { return i >= 0? i: -i; } -#elif defined(__GNUC__) && __GNUC__ < 3 -static __inline __int64_t php_date_llabs( __int64_t i ) { return i >= 0 ? i : -i; } -#else -static inline long long php_date_llabs( long long i ) { return i >= 0 ? i : -i; } -#endif +static inline uint64_t php_date_llabs(int64_t i) { return i >= 0 ? (uint64_t)i : -(uint64_t)i; } #ifdef PHP_WIN32 #define DATE_I64_BUF_LEN 65 @@ -740,9 +734,9 @@ static zend_string *date_format(const char *format, size_t format_len, const tim /* year */ case 'L': length = slprintf(buffer, sizeof(buffer), "%d", timelib_is_leap((int) t->y)); break; case 'y': length = slprintf(buffer, sizeof(buffer), "%02d", (int) (t->y % 100)); break; - case 'Y': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : "", php_date_llabs((timelib_sll) t->y)); break; - case 'x': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : (t->y >= 10000 ? "+" : ""), php_date_llabs((timelib_sll) t->y)); break; - case 'X': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : "+", php_date_llabs((timelib_sll) t->y)); break; + case 'Y': length = slprintf(buffer, sizeof(buffer), "%s%04" PRIu64, t->y < 0 ? "-" : "", php_date_llabs((timelib_sll) t->y)); break; + case 'x': length = slprintf(buffer, sizeof(buffer), "%s%04" PRIu64, t->y < 0 ? "-" : (t->y >= 10000 ? "+" : ""), php_date_llabs((timelib_sll) t->y)); break; + case 'X': length = slprintf(buffer, sizeof(buffer), "%s%04" PRIu64, t->y < 0 ? "-" : "+", php_date_llabs((timelib_sll) t->y)); break; /* time */ case 'a': length = slprintf(buffer, sizeof(buffer), "%s", t->h >= 12 ? "pm" : "am"); break; diff --git a/ext/date/tests/gh18422.phpt b/ext/date/tests/gh18422.phpt new file mode 100644 index 000000000000..643476615277 --- /dev/null +++ b/ext/date/tests/gh18422.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-18422 (int overflow in Date extension) +--FILE-- +format("Y"), "\n"; +echo $dto->format("x"), "\n"; +echo $dto->format("X"), "\n"; + +echo date_create("2024-06-15")->format("Y"), "\n"; +echo date_create("-0042-01-01")->format("Y"), "\n"; +?> +--EXPECTF-- +-%d +-%d +-%d +2024 +-0042 diff --git a/ext/dba/libinifile/inifile.c b/ext/dba/libinifile/inifile.c index f5355b5a990c..fa2b05a715e2 100644 --- a/ext/dba/libinifile/inifile.c +++ b/ext/dba/libinifile/inifile.c @@ -109,7 +109,7 @@ void inifile_free(inifile *dba, int persistent) key_type inifile_key_split(const char *group_name) { key_type key; - char *name; + const char *name; if (group_name[0] == '[' && (name = strchr(group_name, ']')) != NULL) { key.group = estrndup(group_name+1, name - (group_name + 1)); diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 738f204c5026..fbbfab6b243c 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -76,6 +76,7 @@ int zend_jit_profile_counter_rid = -1; int16_t zend_jit_hot_counters[ZEND_HOT_COUNTERS_COUNT]; const zend_op *zend_jit_halt_op = NULL; +const zend_op *zend_jit_interrupt_op = NULL; #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP static int zend_write_protect = 1; #endif @@ -3776,6 +3777,7 @@ int zend_jit_check_support(void) void zend_jit_startup(void *buf, size_t size, bool reattached) { zend_jit_halt_op = zend_get_halt_op(); + zend_jit_interrupt_op = zend_get_interrupt_op(); zend_jit_profile_counter_rid = zend_get_op_array_extension_handle(ACCELERATOR_PRODUCT_NAME); #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index be27bcaf5341..7b5f93f70db7 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -175,6 +175,7 @@ typedef struct _zend_jit_op_array_hot_extension { zend_jit_hash((op_array)->opcodes) extern const zend_op *zend_jit_halt_op; +extern const zend_op *zend_jit_interrupt_op; #ifdef HAVE_GCC_GLOBAL_REGS # define EXECUTE_DATA_D void diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 012edd194e31..271d923598d9 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -1070,6 +1070,11 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (UNEXPECTED(opline == zend_jit_halt_op)) { #else opline = handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); +# if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + while (UNEXPECTED(opline == zend_jit_interrupt_op)) { + opline = zend_vm_handle_interrupt(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } +# endif if (UNEXPECTED(((uintptr_t)opline & ~ZEND_VM_ENTER_BIT) == 0)) { #endif if (prev_opline->opcode == ZEND_YIELD || prev_opline->opcode == ZEND_YIELD_FROM) { diff --git a/ext/readline/readline_cli.c b/ext/readline/readline_cli.c index 01c997a1ccaa..9f42b33596c0 100644 --- a/ext/readline/readline_cli.c +++ b/ext/readline/readline_cli.c @@ -527,7 +527,8 @@ static char *cli_completion_generator(const char *text, int index) /* {{{ */ } else if (text[0] == '#' && text[1] != '[') { retval = cli_completion_generator_ini(text, textlen, &cli_completion_state); } else { - char *lc_text, *class_name_end; + char *lc_text; + const char *class_name_end; zend_string *class_name = NULL; zend_class_entry *ce = NULL; diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index eaadec03cf84..be4f13cfcfc5 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -798,6 +798,7 @@ static void spl_dllist_it_helper_move_forward(spl_ptr_llist_element **traverse_p if (flags & SPL_DLLIST_IT_LIFO) { *traverse_pointer_ptr = old->prev; + SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr); (*traverse_position_ptr)--; if (flags & SPL_DLLIST_IT_DELETE) { @@ -808,6 +809,7 @@ static void spl_dllist_it_helper_move_forward(spl_ptr_llist_element **traverse_p } } else { *traverse_pointer_ptr = old->next; + SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr); if (flags & SPL_DLLIST_IT_DELETE) { zval prev; @@ -820,7 +822,6 @@ static void spl_dllist_it_helper_move_forward(spl_ptr_llist_element **traverse_p } SPL_LLIST_DELREF(old); - SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr); } } /* }}} */ diff --git a/ext/spl/tests/gh22062.phpt b/ext/spl/tests/gh22062.phpt new file mode 100644 index 000000000000..ea67a9983a3d --- /dev/null +++ b/ext/spl/tests/gh22062.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-22062 (SplDoublyLinkedList iterator UAF via destructor releasing next node) +--FILE-- +setIteratorMode( + SplDoublyLinkedList::IT_MODE_FIFO | + SplDoublyLinkedList::IT_MODE_DELETE +); + +$list->push(new class($list) { + public function __construct(private SplDoublyLinkedList $list) {} + public function __destruct() { + if ($this->list->count() > 0) { + $this->list->offsetUnset(0); + } + } +}); + +$list->push(new stdClass()); + +foreach ($list as $item) { + unset($item); +} + +var_dump($list->count()); +?> +--EXPECT-- +int(0) diff --git a/ext/uri/uriparser/include/uriparser/Uri.h b/ext/uri/uriparser/include/uriparser/Uri.h index f041f8113531..ddbbd26f9e44 100644 --- a/ext/uri/uriparser/include/uriparser/Uri.h +++ b/ext/uri/uriparser/include/uriparser/Uri.h @@ -1,4 +1,4 @@ -/* 53c1cb9f2f728652fe001dc72fa0fa7a0e9fa0b8baaaa9e37561c6cdf88ac4df (1.0.1+) +/* c9d94656d067288e474df19a062d487c736b0fa8517d2ef7bbeb8dcd5a70c05b (1.0.2+) * * uriparser - RFC 3986 URI parsing library * diff --git a/ext/uri/uriparser/include/uriparser/UriBase.h b/ext/uri/uriparser/include/uriparser/UriBase.h index abadcae0dbdc..1cc91ceab772 100644 --- a/ext/uri/uriparser/include/uriparser/UriBase.h +++ b/ext/uri/uriparser/include/uriparser/UriBase.h @@ -52,7 +52,7 @@ /* Version */ # define URI_VER_MAJOR 1 # define URI_VER_MINOR 0 -# define URI_VER_RELEASE 1 +# define URI_VER_RELEASE 2 # define URI_VER_SUFFIX_ANSI "" # define URI_VER_SUFFIX_UNICODE URI_ANSI_TO_UNICODE(URI_VER_SUFFIX_ANSI) diff --git a/ext/uri/uriparser/src/UriCommon.c b/ext/uri/uriparser/src/UriCommon.c index 00256f201f7e..e9a6992b01c2 100644 --- a/ext/uri/uriparser/src/UriCommon.c +++ b/ext/uri/uriparser/src/UriCommon.c @@ -67,6 +67,7 @@ # include # include +# include // SIZE_MAX /*extern*/ const URI_CHAR * const URI_FUNC(SafeToPointTo) = _UT("X"); /*extern*/ const URI_CHAR * const URI_FUNC(ConstPwd) = _UT("."); @@ -105,46 +106,35 @@ int URI_FUNC(FreeUriPath)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { } /* Compares two text ranges for equal text content */ -int URI_FUNC(CompareRange)(const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b) { - int diff; - ptrdiff_t lenA; - ptrdiff_t lenB; - +bool URI_FUNC(RangeEquals)(const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b) { /* NOTE: Both NULL means equal! */ if ((a == NULL) || (b == NULL)) { - return ((a == NULL) ? 0 : 1) - ((b == NULL) ? 0 : 1); + return a == b; } /* NOTE: Both NULL means equal! */ if ((a->first == NULL) || (b->first == NULL)) { - return ((a->first == NULL) ? 0 : 1) - ((b->first == NULL) ? 0 : 1); - } - - lenA = a->afterLast - a->first; - lenB = b->afterLast - b->first; - - if (lenA > lenB) { - return 1; - } else if (lenA < lenB) { - return -1; + return a->first == b->first; } - diff = URI_STRNCMP(a->first, b->first, (size_t)lenA); + const size_t lenA = a->afterLast - a->first; + const size_t lenB = b->afterLast - b->first; - if (diff > 0) { - return 1; - } else if (diff < 0) { - return -1; + if (lenA != lenB) { + return false; } - return diff; + return URI_STRNCMP(a->first, b->first, lenA) == 0; } UriBool URI_FUNC(CopyRange)(URI_TYPE(TextRange) * destRange, const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory) { - const int lenInChars = (int)(sourceRange->afterLast - sourceRange->first); - const int lenInBytes = lenInChars * sizeof(URI_CHAR); + const size_t lenInChars = sourceRange->afterLast - sourceRange->first; + if (lenInChars > SIZE_MAX / sizeof(URI_CHAR)) { // detect integer overflow + return URI_FALSE; + } + const size_t lenInBytes = lenInChars * sizeof(URI_CHAR); URI_CHAR * dup = memory->malloc(memory, lenInBytes); if (dup == NULL) { return URI_FALSE; @@ -183,7 +173,7 @@ UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri, UriBool relative, walker->reserved = NULL; /* Prev pointer */ do { UriBool removeSegment = URI_FALSE; - int len = (int)(walker->text.afterLast - walker->text.first); + const size_t len = walker->text.afterLast - walker->text.first; switch (len) { case 1: if ((walker->text.first)[0] == _UT('.')) { diff --git a/ext/uri/uriparser/src/UriCommon.h b/ext/uri/uriparser/src/UriCommon.h index d141935f19e5..12c691a73386 100644 --- a/ext/uri/uriparser/src/UriCommon.h +++ b/ext/uri/uriparser/src/UriCommon.h @@ -67,6 +67,8 @@ # include # endif +# include + /* Used to point to from empty path segments. * X.first and X.afterLast must be the same non-NULL value then. */ extern const URI_CHAR * const URI_FUNC(SafeToPointTo); @@ -77,7 +79,7 @@ void URI_FUNC(ResetUri)(URI_TYPE(Uri) * uri); int URI_FUNC(FreeUriPath)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); -int URI_FUNC(CompareRange)(const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b); +bool URI_FUNC(RangeEquals)(const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b); UriBool URI_FUNC(CopyRange)(URI_TYPE(TextRange) * destRange, const URI_TYPE(TextRange) * sourceRange, diff --git a/ext/uri/uriparser/src/UriCompare.c b/ext/uri/uriparser/src/UriCompare.c index 781100e7e6f4..f9def86f6669 100644 --- a/ext/uri/uriparser/src/UriCompare.c +++ b/ext/uri/uriparser/src/UriCompare.c @@ -72,17 +72,17 @@ UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b) { } /* scheme */ - if (URI_FUNC(CompareRange)(&(a->scheme), &(b->scheme))) { + if (!URI_FUNC(RangeEquals)(&(a->scheme), &(b->scheme))) { return URI_FALSE; } - /* absolutePath */ - if ((a->scheme.first == NULL) && (a->absolutePath != b->absolutePath)) { + /* absolutePath -- not meaningful for URIs with a host set! */ + if (!URI_FUNC(HasHost)(a) && (a->absolutePath != b->absolutePath)) { return URI_FALSE; } /* userInfo */ - if (URI_FUNC(CompareRange)(&(a->userInfo), &(b->userInfo))) { + if (!URI_FUNC(RangeEquals)(&(a->userInfo), &(b->userInfo))) { return URI_FALSE; } @@ -107,20 +107,20 @@ UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b) { } if (a->hostData.ipFuture.first != NULL) { - if (URI_FUNC(CompareRange)(&(a->hostData.ipFuture), &(b->hostData.ipFuture))) { + if (!URI_FUNC(RangeEquals)(&(a->hostData.ipFuture), &(b->hostData.ipFuture))) { return URI_FALSE; } } if ((a->hostData.ip4 == NULL) && (a->hostData.ip6 == NULL) && (a->hostData.ipFuture.first == NULL)) { - if (URI_FUNC(CompareRange)(&(a->hostText), &(b->hostText))) { + if (!URI_FUNC(RangeEquals)(&(a->hostText), &(b->hostText))) { return URI_FALSE; } } /* portText */ - if (URI_FUNC(CompareRange)(&(a->portText), &(b->portText))) { + if (!URI_FUNC(RangeEquals)(&(a->portText), &(b->portText))) { return URI_FALSE; } @@ -133,7 +133,7 @@ UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b) { URI_TYPE(PathSegment) * walkA = a->pathHead; URI_TYPE(PathSegment) * walkB = b->pathHead; do { - if (URI_FUNC(CompareRange)(&(walkA->text), &(walkB->text))) { + if (!URI_FUNC(RangeEquals)(&(walkA->text), &(walkB->text))) { return URI_FALSE; } if ((walkA->next == NULL) != (walkB->next == NULL)) { @@ -145,12 +145,12 @@ UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b) { } /* query */ - if (URI_FUNC(CompareRange)(&(a->query), &(b->query))) { + if (!URI_FUNC(RangeEquals)(&(a->query), &(b->query))) { return URI_FALSE; } /* fragment */ - if (URI_FUNC(CompareRange)(&(a->fragment), &(b->fragment))) { + if (!URI_FUNC(RangeEquals)(&(a->fragment), &(b->fragment))) { return URI_FALSE; } diff --git a/ext/uri/uriparser/src/UriCopy.c b/ext/uri/uriparser/src/UriCopy.c index 3bc6b0dbe3f4..fbe6340faa42 100644 --- a/ext/uri/uriparser/src/UriCopy.c +++ b/ext/uri/uriparser/src/UriCopy.c @@ -199,6 +199,12 @@ int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, const URI_TYPE(Uri) * sourceUri if (URI_FUNC(CopyRangeAsNeeded)(&destWalker->text, &sourceWalker->text, memory) == URI_FALSE) { + // Unless wired to `destUri` above, `destWalker` may be hanging + // in the air now + if (destUri->pathHead != destWalker) { + memory->free(memory, destWalker); + } + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } diff --git a/ext/uri/uriparser/src/UriFile.c b/ext/uri/uriparser/src/UriFile.c index 7510e701c9f5..0af88480b0ad 100644 --- a/ext/uri/uriparser/src/UriFile.c +++ b/ext/uri/uriparser/src/UriFile.c @@ -63,7 +63,8 @@ # include # endif -# include /* for size_t, avoiding stddef.h for older MSVCs */ +# include // size_t +# include // SIZE_MAX static URI_INLINE int URI_FUNC(FilenameToUriString)(const URI_CHAR * filename, URI_CHAR * uriString, @@ -90,6 +91,11 @@ static URI_INLINE int URI_FUNC(FilenameToUriString)(const URI_CHAR * filename, : _UT("file:///"); const size_t prefixLen = URI_STRLEN(prefix); + // Detect and avoid integer overflow + if (prefixLen > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_OUTPUT_TOO_LARGE; + } + /* Copy prefix */ memcpy(uriString, prefix, prefixLen * sizeof(URI_CHAR)); output += prefixLen; @@ -103,7 +109,13 @@ static URI_INLINE int URI_FUNC(FilenameToUriString)(const URI_CHAR * filename, if (lastSep + 1 < input) { if (!fromUnix && absolute && (firstSegment == URI_TRUE)) { /* Quick hack to not convert "C:" to "C%3A" */ - const int charsToCopy = (int)(input - (lastSep + 1)); + const size_t charsToCopy = input - (lastSep + 1); + + // Detect and avoid integer overflow + if (charsToCopy > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_OUTPUT_TOO_LARGE; + } + memcpy(output, lastSep + 1, charsToCopy * sizeof(URI_CHAR)); output += charsToCopy; } else { diff --git a/ext/uri/uriparser/src/UriNormalize.c b/ext/uri/uriparser/src/UriNormalize.c index 8c812d37a0a7..c73e22b28879 100644 --- a/ext/uri/uriparser/src/UriNormalize.c +++ b/ext/uri/uriparser/src/UriNormalize.c @@ -73,6 +73,7 @@ # endif # include +# include // SIZE_MAX static int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask, unsigned int * outMask, @@ -254,20 +255,22 @@ URI_FUNC(LowercaseInplaceExceptPercentEncoding)(const URI_CHAR * first, static URI_INLINE UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first, const URI_CHAR ** afterLast, UriMemoryManager * memory) { - int lenInChars; const int lowerUpperDiff = (_UT('a') - _UT('A')); URI_CHAR * buffer; - int i = 0; + size_t i = 0; if ((first == NULL) || (afterLast == NULL) || (*first == NULL) || (*afterLast == NULL)) { return URI_FALSE; } - lenInChars = (int)(*afterLast - *first); + const size_t lenInChars = *afterLast - *first; if (lenInChars == 0) { return URI_TRUE; - } else if (lenInChars < 0) { + } + + // Detect and avoid integer overflow + if (lenInChars > SIZE_MAX / sizeof(URI_CHAR)) { return URI_FALSE; } @@ -295,8 +298,8 @@ URI_FUNC(FixPercentEncodingEngine)(const URI_CHAR * inFirst, const URI_CHAR * in const URI_CHAR * outFirst, const URI_CHAR ** outAfterLast) { URI_CHAR * write = (URI_CHAR *)outFirst; - const int lenInChars = (int)(inAfterLast - inFirst); - int i = 0; + const size_t lenInChars = inAfterLast - inFirst; + size_t i = 0; /* All but last two */ for (; i + 2 < lenInChars; i++) { @@ -350,7 +353,6 @@ static URI_INLINE void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * firs static URI_INLINE UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** first, const URI_CHAR ** afterLast, UriMemoryManager * memory) { - int lenInChars; URI_CHAR * buffer; /* Death checks */ @@ -360,10 +362,13 @@ static URI_INLINE UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** f } /* Old text length */ - lenInChars = (int)(*afterLast - *first); + const size_t lenInChars = *afterLast - *first; if (lenInChars == 0) { return URI_TRUE; - } else if (lenInChars < 0) { + } + + // Detect and avoid integer overflow + if (lenInChars > SIZE_MAX / sizeof(URI_CHAR)) { return URI_FALSE; } diff --git a/ext/uri/uriparser/src/UriQuery.c b/ext/uri/uriparser/src/UriQuery.c index 801a237a3a1b..de3a040de042 100644 --- a/ext/uri/uriparser/src/UriQuery.c +++ b/ext/uri/uriparser/src/UriQuery.c @@ -67,6 +67,7 @@ # include # include /* size_t */ +# include // SIZE_MAX static int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList, @@ -254,7 +255,14 @@ int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest, const URI_TYPE(QueryList) * qu if (dest != NULL) { write[0] = _UT('\0'); if (charsWritten != NULL) { - *charsWritten = (int)(write - dest) + 1; /* .. for terminator */ + const size_t lenInChars = write - dest; + + // Detect and avoid integer overflow + if (lenInChars > INT_MAX - 1) { + return URI_ERROR_OUTPUT_TOO_LARGE; + } + + *charsWritten = (int)(lenInChars + 1); /* .. for terminator */ } } @@ -267,8 +275,8 @@ UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) * *prevNext, int * itemCou const URI_CHAR * valueAfter, UriBool plusToSpace, UriBreakConversion breakConversion, UriMemoryManager * memory) { - const int keyLen = (int)(keyAfter - keyFirst); - const int valueLen = (int)(valueAfter - valueFirst); + const size_t keyLen = keyAfter - keyFirst; + const size_t valueLen = valueAfter - valueFirst; URI_CHAR * key; URI_CHAR * value; @@ -285,6 +293,13 @@ UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) * *prevNext, int * itemCou } (*prevNext)->next = NULL; + // Detect integer overflow + if ((keyLen > SIZE_MAX - 1) || (keyLen + 1 > SIZE_MAX / sizeof(URI_CHAR))) { + memory->free(memory, *prevNext); + *prevNext = NULL; + return URI_FALSE; // Raises malloc error + } + /* Fill key */ key = memory->malloc(memory, (keyLen + 1) * sizeof(URI_CHAR)); if (key == NULL) { @@ -305,6 +320,14 @@ UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) * *prevNext, int * itemCou /* Fill value */ if (valueFirst != NULL) { + // Detect integer overflow + if ((valueLen > SIZE_MAX - 1) || (valueLen + 1 > SIZE_MAX / sizeof(URI_CHAR))) { + memory->free(memory, key); + memory->free(memory, *prevNext); + *prevNext = NULL; + return URI_FALSE; // Raises malloc error + } + value = memory->malloc(memory, (valueLen + 1) * sizeof(URI_CHAR)); if (value == NULL) { memory->free(memory, key); diff --git a/ext/uri/uriparser/src/UriRecompose.c b/ext/uri/uriparser/src/UriRecompose.c index 61ffee77248f..055e0483d3f2 100644 --- a/ext/uri/uriparser/src/UriRecompose.c +++ b/ext/uri/uriparser/src/UriRecompose.c @@ -64,6 +64,9 @@ # include "UriCommon.h" # endif +# include // INT_MAX +# include // SIZE_MAX + static int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, int maxChars, int * charsWritten, int * charsRequired); @@ -116,10 +119,19 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U /* clang-format off */ /* [03/19] append scheme to result; */ /* clang-format on */ - const int charsToWrite = - (int)(uri->scheme.afterLast - uri->scheme.first); + const size_t charsToWrite = uri->scheme.afterLast - uri->scheme.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->scheme.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -131,6 +143,11 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - *charsRequired) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite; } /* clang-format off */ @@ -180,10 +197,20 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U /* clang-format on */ /* UserInfo */ if (uri->userInfo.first != NULL) { - const int charsToWrite = - (int)(uri->userInfo.afterLast - uri->userInfo.first); + const size_t charsToWrite = + uri->userInfo.afterLast - uri->userInfo.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->userInfo.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -206,6 +233,13 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if ((charsToWrite > (size_t)INT_MAX - 1) + || (charsToWrite + 1 + > (size_t)INT_MAX - *charsRequired)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite + 1; } } @@ -334,8 +368,8 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U } } else if (uri->hostData.ipFuture.first != NULL) { /* IPvFuture */ - const int charsToWrite = (int)(uri->hostData.ipFuture.afterLast - - uri->hostData.ipFuture.first); + const size_t charsToWrite = uri->hostData.ipFuture.afterLast + - uri->hostData.ipFuture.first; if (dest != NULL) { if (written + 1 <= maxChars) { memcpy(dest + written, _UT("["), 1 * sizeof(URI_CHAR)); @@ -348,7 +382,17 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->hostData.ipFuture.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -371,14 +415,31 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if ((charsToWrite > (size_t)INT_MAX - 1 - 1) + || (1 + charsToWrite + 1 + > (size_t)INT_MAX - *charsRequired)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += 1 + charsToWrite + 1; } } else if (uri->hostText.first != NULL) { /* Regname */ - const int charsToWrite = - (int)(uri->hostText.afterLast - uri->hostText.first); + const size_t charsToWrite = + uri->hostText.afterLast - uri->hostText.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->hostText.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -390,14 +451,19 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - *charsRequired) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite; } } /* Port */ if (uri->portText.first != NULL) { - const int charsToWrite = - (int)(uri->portText.afterLast - uri->portText.first); + const size_t charsToWrite = + uri->portText.afterLast - uri->portText.first; if (dest != NULL) { /* Leading ':' */ if (written + 1 <= maxChars) { @@ -411,8 +477,18 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + /* Port number */ - if (written + charsToWrite <= maxChars) { + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->portText.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -424,6 +500,13 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if ((charsToWrite > (size_t)INT_MAX - 1) + || (1 + charsToWrite + > (size_t)INT_MAX - *charsRequired)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += 1 + charsToWrite; } } @@ -456,10 +539,20 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U if (uri->pathHead != NULL) { URI_TYPE(PathSegment) * walker = uri->pathHead; do { - const int charsToWrite = - (int)(walker->text.afterLast - walker->text.first); + const size_t charsToWrite = + walker->text.afterLast - walker->text.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, walker->text.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -471,6 +564,11 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - *charsRequired) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite; } @@ -520,10 +618,19 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U /* clang-format off */ /* [13/19] append query to result; */ /* clang-format on */ - const int charsToWrite = - (int)(uri->query.afterLast - uri->query.first); + const size_t charsToWrite = uri->query.afterLast - uri->query.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->query.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -535,6 +642,11 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - *charsRequired) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite; } /* clang-format off */ @@ -565,10 +677,20 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U /* clang-format off */ /* [17/19] append fragment to result; */ /* clang-format on */ - const int charsToWrite = - (int)(uri->fragment.afterLast - uri->fragment.first); + const size_t charsToWrite = + uri->fragment.afterLast - uri->fragment.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->fragment.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -580,6 +702,11 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - *charsRequired) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite; } /* clang-format off */ diff --git a/ext/uri/uriparser/src/UriResolve.c b/ext/uri/uriparser/src/UriResolve.c index 302665d21cd6..90c8bfd778dc 100644 --- a/ext/uri/uriparser/src/UriResolve.c +++ b/ext/uri/uriparser/src/UriResolve.c @@ -183,9 +183,8 @@ static int URI_FUNC(AddBaseUriImpl)(URI_TYPE(Uri) * absDest, if ((options & URI_RESOLVE_IDENTICAL_SCHEME_COMPAT) && (absBase->scheme.first != NULL) && (relSource->scheme.first != NULL) - && (0 - == URI_FUNC(CompareRange)(&(absBase->scheme), - &(relSource->scheme)))) { + && (URI_FUNC(RangeEquals)(&(absBase->scheme), + &(relSource->scheme)))) { /* clang-format off */ /* [00/32] undefine(R.scheme); */ /* clang-format on */ diff --git a/ext/uri/uriparser/src/UriShorten.c b/ext/uri/uriparser/src/UriShorten.c index 548b0b4157dd..001bd7db9990 100644 --- a/ext/uri/uriparser/src/UriShorten.c +++ b/ext/uri/uriparser/src/UriShorten.c @@ -111,14 +111,14 @@ static URI_INLINE UriBool URI_FUNC(EqualsAuthority)(const URI_TYPE(Uri) * first, /* IPvFuture */ if (first->hostData.ipFuture.first != NULL) { return ((second->hostData.ipFuture.first != NULL) - && !URI_FUNC(CompareRange)(&first->hostData.ipFuture, - &second->hostData.ipFuture)) + && URI_FUNC(RangeEquals)(&first->hostData.ipFuture, + &second->hostData.ipFuture)) ? URI_TRUE : URI_FALSE; } - return !URI_FUNC(CompareRange)(&first->hostText, &second->hostText) ? URI_TRUE - : URI_FALSE; + return URI_FUNC(RangeEquals)(&first->hostText, &second->hostText) ? URI_TRUE + : URI_FALSE; } static int URI_FUNC(RemoveBaseUriImpl)(URI_TYPE(Uri) * dest, @@ -152,7 +152,7 @@ static int URI_FUNC(RemoveBaseUriImpl)(URI_TYPE(Uri) * dest, /* clang-format off */ /* [01/50] if (A.scheme != Base.scheme) then */ /* clang-format on */ - if (URI_FUNC(CompareRange)(&absSource->scheme, &absBase->scheme)) { + if (!URI_FUNC(RangeEquals)(&absSource->scheme, &absBase->scheme)) { /* clang-format off */ /* [02/50] T.scheme = A.scheme; */ /* clang-format on */ @@ -255,8 +255,7 @@ static int URI_FUNC(RemoveBaseUriImpl)(URI_TYPE(Uri) * dest, /* clang-format on */ while ( (sourceSeg != NULL) && (baseSeg != NULL) - && !URI_FUNC(CompareRange)(&sourceSeg->text, - &baseSeg->text) + && URI_FUNC(RangeEquals)(&sourceSeg->text, &baseSeg->text) && !((sourceSeg->text.first == sourceSeg->text.afterLast) && ((sourceSeg->next == NULL) != (baseSeg->next == NULL)))) { diff --git a/ext/zend_test/object_handlers.c b/ext/zend_test/object_handlers.c index a5038dcba6dd..6b8bf4ab9912 100644 --- a/ext/zend_test/object_handlers.c +++ b/ext/zend_test/object_handlers.c @@ -230,6 +230,44 @@ ZEND_METHOD(NumericCastableNoOperations, __construct) ZVAL_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), n); } +static zend_class_entry *vm_interrupt_comparable_ce; +static zend_object_handlers vm_interrupt_comparable_object_handlers; + +static zend_object* vm_interrupt_comparable_object_create_ex(zend_class_entry* ce, zend_long l) { + zend_object *obj = zend_objects_new(ce); + object_properties_init(obj, ce); + obj->handlers = &vm_interrupt_comparable_object_handlers; + ZVAL_LONG(OBJ_PROP_NUM(obj, 0), l); + return obj; +} + +static zend_object *vm_interrupt_comparable_object_create(zend_class_entry *ce) +{ + return vm_interrupt_comparable_object_create_ex(ce, 0); +} + +static int vm_interrupt_comparable_compare(zval *op1, zval *op2) +{ + ZEND_COMPARE_OBJECTS_FALLBACK(op1, op2); + + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + + return ZEND_THREEWAY_COMPARE( + Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op1), 0)), + Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op2), 0))); +} + +ZEND_METHOD(VmInterruptComparable, __construct) +{ + zend_long l; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(l) + ZEND_PARSE_PARAMETERS_END(); + + ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), l); +} + static zend_class_entry *dimension_handlers_no_ArrayAccess_ce; static zend_object_handlers dimension_handlers_no_ArrayAccess_object_handlers; @@ -300,6 +338,11 @@ void zend_test_object_handlers_init(void) memcpy(&numeric_castable_no_operation_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); numeric_castable_no_operation_object_handlers.cast_object = numeric_castable_no_operation_cast_object; + vm_interrupt_comparable_ce = register_class_VmInterruptComparable(); + vm_interrupt_comparable_ce->create_object = vm_interrupt_comparable_object_create; + memcpy(&vm_interrupt_comparable_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + vm_interrupt_comparable_object_handlers.compare = vm_interrupt_comparable_compare; + dimension_handlers_no_ArrayAccess_ce = register_class_DimensionHandlersNoArrayAccess(); dimension_handlers_no_ArrayAccess_ce->create_object = dimension_handlers_no_ArrayAccess_object_create; memcpy(&dimension_handlers_no_ArrayAccess_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/zend_test/object_handlers.stub.php b/ext/zend_test/object_handlers.stub.php index a474908b1095..8c8e7f9bfe9b 100644 --- a/ext/zend_test/object_handlers.stub.php +++ b/ext/zend_test/object_handlers.stub.php @@ -23,6 +23,11 @@ final class NumericCastableNoOperations { public function __construct(int|float $val) {} } +final class VmInterruptComparable { + private int $val; + public function __construct(int $val) {} +} + class DimensionHandlersNoArrayAccess { public bool $read = false; public bool $write = false; diff --git a/ext/zend_test/object_handlers_arginfo.h b/ext/zend_test/object_handlers_arginfo.h index 34008c33bc0c..995bd62dc478 100644 --- a/ext/zend_test/object_handlers_arginfo.h +++ b/ext/zend_test/object_handlers_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit object_handlers.stub.php instead. - * Stub hash: 81be60f2c465ffe5c036739d072ab80d9c388907 */ + * Stub hash: 1a70ed60c5af38539b1222a979f97fddf7d1826e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DoOperationNoCast___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, val, IS_LONG, 0) @@ -15,10 +15,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_NumericCastableNoOperations___construct, 0, ZEND_ARG_TYPE_MASK(0, val, MAY_BE_LONG|MAY_BE_DOUBLE, NULL) ZEND_END_ARG_INFO() +#define arginfo_class_VmInterruptComparable___construct arginfo_class_DoOperationNoCast___construct + static ZEND_METHOD(DoOperationNoCast, __construct); static ZEND_METHOD(LongCastableNoOperations, __construct); static ZEND_METHOD(FloatCastableNoOperations, __construct); static ZEND_METHOD(NumericCastableNoOperations, __construct); +static ZEND_METHOD(VmInterruptComparable, __construct); static const zend_function_entry class_DoOperationNoCast_methods[] = { ZEND_ME(DoOperationNoCast, __construct, arginfo_class_DoOperationNoCast___construct, ZEND_ACC_PUBLIC) @@ -40,6 +43,11 @@ static const zend_function_entry class_NumericCastableNoOperations_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_VmInterruptComparable_methods[] = { + ZEND_ME(VmInterruptComparable, __construct, arginfo_class_VmInterruptComparable___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_DoOperationNoCast(void) { zend_class_entry ce, *class_entry; @@ -104,6 +112,22 @@ static zend_class_entry *register_class_NumericCastableNoOperations(void) return class_entry; } +static zend_class_entry *register_class_VmInterruptComparable(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "VmInterruptComparable", class_VmInterruptComparable_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL); + + zval property_val_default_value; + ZVAL_UNDEF(&property_val_default_value); + zend_string *property_val_name = zend_string_init("val", sizeof("val") - 1, true); + zend_declare_typed_property(class_entry, property_val_name, &property_val_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release_ex(property_val_name, true); + + return class_entry; +} + static zend_class_entry *register_class_DimensionHandlersNoArrayAccess(void) { zend_class_entry ce, *class_entry; diff --git a/ext/zend_test/tests/observer_vm_interrupt_tailcall_helper.phpt b/ext/zend_test/tests/observer_vm_interrupt_tailcall_helper.phpt new file mode 100644 index 000000000000..d0178bdbf614 --- /dev/null +++ b/ext/zend_test/tests/observer_vm_interrupt_tailcall_helper.phpt @@ -0,0 +1,26 @@ +--TEST-- +Observer: VM interrupt during tailcall helper dispatch +--DESCRIPTION-- +This exercises a VM interrupt raised while an opcode handler dispatches to an +extra-argument helper. On the tailcall VM, the helper may return an opline +tagged with ZEND_VM_ENTER_BIT; treating that tagged value as a zend_op * before +tailcalling the next handler can crash. +--EXTENSIONS-- +zend_test +--INI-- +opcache.jit=0 +zend_test.observer.set_vm_interrupt_on_begin=1 +--FILE-- + +--EXPECT-- +stdClass