diff --git a/.gdbinit b/.gdbinit index c4705b2f59a95..4477828265e0e 100644 --- a/.gdbinit +++ b/.gdbinit @@ -42,7 +42,7 @@ define print_cvs printf "Compiled variables count: %d\n\n", $cv_count while $cv_idx < $cv_count - printf "[%d] '%s'\n", $cv_idx, $cv[$cv_idx].val + printf "[%d] '$%s'\n", $cv_idx, $cv[$cv_idx].val@$cv[$cv_idx].len set $zvalue = ((zval *) $cv_ex_ptr) + $callFrameSize + $cv_idx printzv $zvalue set $cv_idx = $cv_idx + 1 diff --git a/.github/actions/apk/action.yml b/.github/actions/apk/action.yml index da909a367864f..0cda4963a6a9c 100644 --- a/.github/actions/apk/action.yml +++ b/.github/actions/apk/action.yml @@ -7,6 +7,9 @@ runs: set -x OPCACHE_TLS_TESTS_DEPS="clang gcc binutils-gold lld" + # compiler-rt provides libclang_rt.asan-x86_64.a for clang20 + # https://pkgs.alpinelinux.org/contents?file=libclang_rt.asan-x86_64.a&path=&name=&branch=v3.22 + ASAN_DEPS="clang20 compiler-rt" apk update -q apk add \ @@ -52,8 +55,9 @@ runs: net-snmp-dev \ openldap-dev \ unixodbc-dev \ - postgresql14-dev \ + postgresql-dev \ tzdata \ musl-locales \ musl-locales-lang \ - $OPCACHE_TLS_TESTS_DEPS + $OPCACHE_TLS_TESTS_DEPS \ + $ASAN_DEPS diff --git a/.github/nightly_matrix.php b/.github/nightly_matrix.php index 311176ef5e53e..846c60e8fd56d 100644 --- a/.github/nightly_matrix.php +++ b/.github/nightly_matrix.php @@ -1,7 +1,8 @@ 'master', 'version' => [8, 5]], + ['ref' => 'master', 'version' => [8, 6]], + ['ref' => 'PHP-8.5', 'version' => [8, 5]], ['ref' => 'PHP-8.4', 'version' => [8, 4]], ['ref' => 'PHP-8.3', 'version' => [8, 3]], ['ref' => 'PHP-8.2', 'version' => [8, 2]], diff --git a/.github/scripts/windows/find-target-branch.bat b/.github/scripts/windows/find-target-branch.bat index a0b47f2488946..44b0bde1ec8ca 100644 --- a/.github/scripts/windows/find-target-branch.bat +++ b/.github/scripts/windows/find-target-branch.bat @@ -3,6 +3,6 @@ for /f "usebackq tokens=3" %%i in (`findstr PHP_MAJOR_VERSION main\php_version.h`) do set BRANCH=%%i for /f "usebackq tokens=3" %%i in (`findstr PHP_MINOR_VERSION main\php_version.h`) do set BRANCH=%BRANCH%.%%i -if /i "%BRANCH%" equ "8.5" ( +if /i "%BRANCH%" equ "8.6" ( set BRANCH=master ) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 62ea796502127..e0322e3b1be2e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -44,6 +44,9 @@ on: skip_wordpress: required: true type: boolean + variation_enable_zend_max_execution_timers: + required: true + type: boolean permissions: contents: read jobs: @@ -90,9 +93,9 @@ jobs: ALPINE: if: inputs.run_alpine name: ALPINE_X64_ASAN_UBSAN_DEBUG_ZTS - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 container: - image: 'alpine:3.20.1' + image: 'alpine:3.22' steps: - name: git checkout uses: actions/checkout@v5 @@ -100,11 +103,6 @@ jobs: ref: ${{ inputs.branch }} - name: apk uses: ./.github/actions/apk - - name: LLVM 17 (ASAN-only) - # libclang_rt.asan-x86_64.a is provided by compiler-rt, and only for clang17: - # https://pkgs.alpinelinux.org/contents?file=libclang_rt.asan-x86_64.a&path=&name=&branch=v3.20 - run: | - apk add clang17 compiler-rt - name: System info run: | echo "::group::Show host CPU info" @@ -119,8 +117,8 @@ jobs: configurationParameters: >- CFLAGS="-fsanitize=undefined,address -fno-sanitize=function -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address -fno-sanitize=function" - CC=clang-17 - CXX=clang++-17 + CC=clang-20 + CXX=clang++-20 --enable-debug --enable-zts skipSlow: true # FIXME: This should likely include slow extensions @@ -199,6 +197,7 @@ jobs: zts: true configuration_parameters: >- CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE" + ${{ inputs.variation_enable_zend_max_execution_timers && '--enable-zend-max-execution-timers' || '' }} run_tests_parameters: -d zend_test.observer.enabled=1 -d zend_test.observer.show_output=0 timeout_minutes: 360 test_function_jit: true diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 5e112eed5cb66..ac1ac8be7b06d 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -40,6 +40,47 @@ env: CC: ccache gcc CXX: ccache g++ jobs: + ALPINE: + if: github.repository == 'php/php-src' || github.event_name == 'pull_request' + name: ALPINE_X64_ASAN_UBSAN_DEBUG_ZTS + runs-on: ubuntu-24.04 + container: + image: 'alpine:3.22' + steps: + - name: git checkout + uses: actions/checkout@v5 + - name: apk + uses: ./.github/actions/apk + - name: System info + run: | + echo "::group::Show host CPU info" + lscpu + echo "::endgroup::" + echo "::group::Show installed package versions" + apk list + echo "::endgroup::" + - name: ./configure + uses: ./.github/actions/configure-alpine + with: + configurationParameters: >- + CFLAGS="-fsanitize=undefined,address -fno-sanitize=function -DZEND_TRACK_ARENA_ALLOC" + LDFLAGS="-fsanitize=undefined,address -fno-sanitize=function" + CC=clang-20 + CXX=clang++-20 + --enable-debug + --enable-zts + skipSlow: true # FIXME: This should likely include slow extensions + - name: make + run: make -j$(/usr/bin/nproc) >/dev/null + - name: make install + uses: ./.github/actions/install-alpine + - name: Test Tracing JIT + uses: ./.github/actions/test-alpine + with: + jitType: tracing + runTestsParameters: >- + --asan -x + -d opcache.enable_cli=1 LINUX_X64: if: github.repository == 'php/php-src' || github.event_name == 'pull_request' services: @@ -163,6 +204,7 @@ jobs: with: key: "${{github.job}}-${{hashFiles('main/php_version.h')}}" append-timestamp: false + save: ${{ github.event_name != 'pull_request' }} - name: ./configure uses: ./.github/actions/configure-x32 with: diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index 16418b1aa1d3a..b70b026eced07 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -64,4 +64,5 @@ jobs: skip_laravel: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_symfony: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} skip_wordpress: ${{ matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1 }} + variation_enable_zend_max_execution_timers: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 3) || matrix.branch.version[0] >= 9 }} secrets: inherit diff --git a/NEWS b/NEWS index a6a5c63891cc9..dcea861d94142 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,97 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.4.13 +09 Oct 2025, PHP 8.4.14 + +- Core: + . Fixed bug GH-19765 (object_properties_load() bypasses readonly property + checks). (timwolla) + . Fixed hard_timeout with --enable-zend-max-execution-timers. (Appla) + . Fixed bug GH-19792 (SCCP causes UAF for return value if both warning and + exception are triggered). (nielsdos) + . Fixed bug GH-19653 (Closure named argument unpacking between temporary + closures can cause a crash). (nielsdos, Arnaud, Bob) + . Fixed bug GH-19839 (Incorrect HASH_FLAG_HAS_EMPTY_IND flag on userland + array). (ilutov) + . Fixed bug GH-19480 (error_log php.ini cannot be unset when open_basedir is + configured). (nielsdos) + . Fixed bug GH-20002 (Broken build on *BSD with MSAN). (outtersg) + +- CLI: + . Fix useless "Failed to poll event" error logs due to EAGAIN in CLI server + with PHP_CLI_SERVER_WORKERS. (leotaku) + +- Curl: + . Fix cloning of CURLOPT_POSTFIELDS when using the clone operator instead + of the curl_copy_handle() function to clone a CurlHandle. (timwolla) + . Fix curl build and test failures with version 8.16. + (nielsdos, ilutov, Jakub Zelenka) + +- Date: + . Fixed GH-17159: "P" format for ::createFromFormat swallows string literals. + (nielsdos) + +- DOM: + . Fix macro name clash on macOS. (Ruoyu Zhong) + . Fixed bug GH-20022 (docker-php-ext-install DOM failed). (nielsdos) + +- GD: + . Fixed GH-19955 (imagefttext() memory leak). (David Carlier) + +- MySQLnd: + . Fixed bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress + as parameter). (nielsdos) + +- Opcache: + . Fixed bug GH-19669 (assertion failure in zend_jit_trace_type_to_info_ex). + (Arnaud) + . Fixed bug GH-19831 (function JIT may not deref property value). (Arnaud) + . Fixed bug GH-19889 (race condition in zend_runtime_jit(), + zend_jit_hot_func()). (Arnaud) + +- Phar: + . Fix memory leak and invalid continuation after tar header writing fails. + (nielsdos) + . Fix memory leaks when creating temp file fails when applying zip signature. + (nielsdos) + +- SimpleXML: + . Fixed bug GH-19988 (zend_string_init with NULL pointer in simplexml (UB)). + (nielsdos) + +- Soap: + . Fixed bug GH-19784 (SoapServer memory leak). (nielsdos) + . Fixed bug GH-20011 (Array of SoapVar of unknown type causes crash). + (nielsdos) + +- Standard: + . Fixed bug GH-12265 (Cloning an object breaks serialization recursion). + (nielsdos) + . Fixed bug GH-19701 (Serialize/deserialize loses some data). (nielsdos) + . Fixed bug GH-19801 (leaks in var_dump() and debug_zval_dump()). + (alexandre-daubois) + . Fixed bug GH-20043 (array_unique assertion failure with RC1 array + causing an exception on sort). (nielsdos) + . Fixed bug GH-19926 (reset internal pointer earlier while splicing array + while COW violation flag is still set). (alexandre-daubois) + . Fixed bug GH-19570 (unable to fseek in /dev/zero and /dev/null). + (nielsdos, divinity76) + +- Streams: + . Fixed bug GH-19248 (Use strerror_r instead of strerror in main). + (Jakub Zelenka) + . Fixed bug GH-17345 (Bug #35916 was not completely fixed). (nielsdos) + . Fixed bug GH-19705 (segmentation when attempting to flush on non seekable + stream. (bukka/David Carlier) + +- XMLReader: + . Fixed bug GH-20009 (XMLReader leak on RelaxNG schema failure). (nielsdos) + +- Zip: + . Fixed bug GH-19688 (Remove pattern overflow in zip addGlob()). (nielsdos) + . Fixed bug GH-19932 (Memory leak in zip setEncryptionName()/setEncryptionIndex()). + (David Carlier) + +25 Sep 2025, PHP 8.4.13 - Core: . Fixed bug GH-18850 (Repeated inclusion of file with __halt_compiler() diff --git a/Zend/Optimizer/sccp.c b/Zend/Optimizer/sccp.c index c86672a8dd248..c1faf2a3fbe03 100644 --- a/Zend/Optimizer/sccp.c +++ b/Zend/Optimizer/sccp.c @@ -838,9 +838,7 @@ static inline zend_result ct_eval_func_call_ex( zval_ptr_dtor(result); zend_clear_exception(); retval = FAILURE; - } - - if (EG(capture_warnings_during_sccp) > 1) { + } else if (EG(capture_warnings_during_sccp) > 1) { zval_ptr_dtor(result); retval = FAILURE; } diff --git a/Zend/Optimizer/zend_func_info.h b/Zend/Optimizer/zend_func_info.h index b53683bdf5e70..db00d843ee10e 100644 --- a/Zend/Optimizer/zend_func_info.h +++ b/Zend/Optimizer/zend_func_info.h @@ -39,7 +39,7 @@ #define ZEND_FUNC_JIT_ON_PROF_REQUEST (1<<14) /* used by JIT */ #define ZEND_FUNC_JIT_ON_HOT_COUNTERS (1<<15) /* used by JIT */ #define ZEND_FUNC_JIT_ON_HOT_TRACE (1<<16) /* used by JIT */ - +#define ZEND_FUNC_JITED (1<<17) /* used by JIT */ typedef struct _zend_func_info zend_func_info; typedef struct _zend_call_info zend_call_info; diff --git a/Zend/tests/closures/gh19653_1.phpt b/Zend/tests/closures/gh19653_1.phpt new file mode 100644 index 0000000000000..93b119eb57826 --- /dev/null +++ b/Zend/tests/closures/gh19653_1.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-19653 (Closure named argument unpacking between temporary closures can cause a crash) +--CREDITS-- +ivan-u7n +--FILE-- + +--EXPECT-- +usage1() func1() a1=a1 a2=a2 a3=m3+ +usage1() [function] func1() a1=a1 a2=m2+ a3=m3+ diff --git a/Zend/tests/closures/gh19653_2.phpt b/Zend/tests/closures/gh19653_2.phpt new file mode 100644 index 0000000000000..7eb837dd22c4d --- /dev/null +++ b/Zend/tests/closures/gh19653_2.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-19653 (Closure named argument unpacking between temporary closures can cause a crash) - eval variation +--CREDITS-- +arnaud-lb +--FILE-- + +--EXPECTF-- +int(1) + +Fatal error: Uncaught Error: Unknown named parameter $a in %s:%d +Stack trace: +#0 %s(%d): usage1(Object(Closure)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh19839.phpt b/Zend/tests/gh19839.phpt new file mode 100644 index 0000000000000..cc589ce0605f1 --- /dev/null +++ b/Zend/tests/gh19839.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-19839: Incorrect HASH_FLAG_HAS_EMPTY_IND flag on userland array +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/zend.h b/Zend/zend.h index 1cf82f437d301..f747eb1b8d9a0 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.13-dev" +#define ZEND_VERSION "4.4.14RC1" #define ZEND_ENGINE_3 diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 7bfe9ba7f5f2f..e529c48b5ac2a 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1761,6 +1761,14 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) property_info && (property_info->flags & ZEND_ACC_STATIC) == 0) { zval *slot = OBJ_PROP(object, property_info->offset); + if (UNEXPECTED((property_info->flags & ZEND_ACC_READONLY) && !Z_ISUNDEF_P(slot))) { + if (Z_PROP_FLAG_P(slot) & IS_PROP_REINITABLE) { + Z_PROP_FLAG_P(slot) &= ~IS_PROP_REINITABLE; + } else { + zend_readonly_property_modification_error(property_info); + return; + } + } zval_ptr_dtor(slot); ZVAL_COPY_VALUE(slot, prop); zval_add_ref(slot); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index f5399ad28dcb2..c2d4e57c02db6 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -5250,7 +5250,12 @@ static zend_never_inline zend_result ZEND_FASTCALL zend_quick_check_constant( static zend_always_inline uint32_t zend_get_arg_offset_by_name( zend_function *fbc, zend_string *arg_name, void **cache_slot) { - if (EXPECTED(*cache_slot == fbc)) { + /* Due to closures, the `fbc` address isn't unique if the memory address is reused. + * The argument info will be however and uniquely positions the arguments. + * We do support NULL arg_info, so we have to distinguish that from an uninitialized cache slot. */ + void *unique_id = (void *) ((uintptr_t) fbc->common.arg_info | 1); + + if (EXPECTED(*cache_slot == unique_id)) { return *(uintptr_t *)(cache_slot + 1); } @@ -5259,10 +5264,12 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) || EXPECTED(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { for (uint32_t i = 0; i < num_args; i++) { - zend_arg_info *arg_info = &fbc->op_array.arg_info[i]; + zend_arg_info *arg_info = &fbc->common.arg_info[i]; if (zend_string_equals(arg_name, arg_info->name)) { - *cache_slot = fbc; - *(uintptr_t *)(cache_slot + 1) = i; + if (fbc->type == ZEND_USER_FUNCTION && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) { + *cache_slot = unique_id; + *(uintptr_t *)(cache_slot + 1) = i; + } return i; } } @@ -5271,7 +5278,7 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[i]; size_t len = strlen(arg_info->name); if (zend_string_equals_cstr(arg_name, arg_info->name, len)) { - *cache_slot = fbc; + *cache_slot = unique_id; *(uintptr_t *)(cache_slot + 1) = i; return i; } @@ -5279,8 +5286,13 @@ static zend_always_inline uint32_t zend_get_arg_offset_by_name( } if (fbc->common.fn_flags & ZEND_ACC_VARIADIC) { - *cache_slot = fbc; - *(uintptr_t *)(cache_slot + 1) = fbc->common.num_args; + if ((fbc->type == ZEND_USER_FUNCTION + && (!fbc->op_array.refcount || !(fbc->op_array.fn_flags & ZEND_ACC_CLOSURE))) + || (fbc->type == ZEND_INTERNAL_FUNCTION + && !(fbc->common.fn_flags & ZEND_ACC_USER_ARG_INFO))) { + *cache_slot = unique_id; + *(uintptr_t *)(cache_slot + 1) = fbc->common.num_args; + } return fbc->common.num_args; } diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 48e61c6839063..6f790df869814 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1570,7 +1570,9 @@ static void zend_set_timeout_ex(zend_long seconds, bool reset_signals) /* {{{ */ return; } #elif defined(ZEND_MAX_EXECUTION_TIMERS) - zend_max_execution_timer_settime(seconds); + if (seconds > 0) { + zend_max_execution_timer_settime(seconds); + } if (reset_signals) { sigset_t sigset; @@ -1657,7 +1659,9 @@ void zend_unset_timeout(void) /* {{{ */ tq_timer = NULL; } #elif defined(ZEND_MAX_EXECUTION_TIMERS) - zend_max_execution_timer_settime(0); + if (EG(timeout_seconds)) { + zend_max_execution_timer_settime(0); + } #elif defined(HAVE_SETITIMER) if (EG(timeout_seconds)) { struct itimerval no_timeout; diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index cebb3258aefec..1f6f992f2795a 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -2465,6 +2465,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) target->nTableSize = HT_MIN_SIZE; HT_SET_DATA_ADDR(target, &uninitialized_bucket); } else if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) { + ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_HAS_EMPTY_IND)); HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK; target->nTableMask = source->nTableMask; target->nNumUsed = source->nNumUsed; @@ -2481,6 +2482,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) memcpy(HT_GET_DATA_ADDR(target), HT_GET_DATA_ADDR(source), HT_USED_SIZE(source)); } } else if (HT_IS_PACKED(source)) { + ZEND_ASSERT(!(HT_FLAGS(source) & HASH_FLAG_HAS_EMPTY_IND)); HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK; target->nTableMask = HT_MIN_MASK; target->nNumUsed = source->nNumUsed; @@ -2500,7 +2502,8 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) zend_array_dup_packed_elements(source, target, 1); } } else { - HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK; + /* Indirects are removed during duplication, remove HASH_FLAG_HAS_EMPTY_IND accordingly. */ + HT_FLAGS(target) = HT_FLAGS(source) & (HASH_FLAG_MASK & ~HASH_FLAG_HAS_EMPTY_IND); target->nTableMask = source->nTableMask; target->nNextFreeElement = source->nNextFreeElement; target->nInternalPointer = diff --git a/Zend/zend_string.c b/Zend/zend_string.c index dfe059359aa53..e9ebee08f4393 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -505,8 +505,10 @@ ZEND_API zend_string *zend_string_concat3( return res; } -/* strlcpy and strlcat are not intercepted by msan, so we need to do it ourselves. */ -#if __has_feature(memory_sanitizer) +/* strlcpy and strlcat are not always intercepted by msan, so we need to do it + * ourselves. Apply a simple heuristic to determine the platforms that need it. + * See https://github.com/php/php-src/issues/20002. */ +#if __has_feature(memory_sanitizer) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) static size_t (*libc_strlcpy)(char *__restrict, const char *__restrict, size_t); size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t n) { diff --git a/configure.ac b/configure.ac index d2250cae43331..5d78afefd4610 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.4.13-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.14RC1],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 965e4971267e6..db249eee057bc 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -453,7 +453,7 @@ static zend_object *curl_clone_obj(zend_object *object) { clone_ch->cp = cp; _php_setup_easy_copy_handlers(clone_ch, ch); - postfields = &clone_ch->postfields; + postfields = &ch->postfields; if (Z_TYPE_P(postfields) != IS_UNDEF) { if (build_mime_structure_from_hash(clone_ch, postfields) == FAILURE) { zend_throw_exception(NULL, "Failed to clone CurlHandle", 0); @@ -643,10 +643,10 @@ static int curl_fnmatch(void *ctx, const char *pattern, const char *string) /* }}} */ /* {{{ curl_progress */ -static size_t curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) +static int curl_progress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { php_curl *ch = (php_curl *)clientp; - size_t rval = 0; + int rval = 0; #if PHP_CURL_DEBUG fprintf(stderr, "curl_progress() called\n"); @@ -681,10 +681,10 @@ static size_t curl_progress(void *clientp, double dltotal, double dlnow, double /* }}} */ /* {{{ curl_xferinfo */ -static size_t curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +static int curl_xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { php_curl *ch = (php_curl *)clientp; - size_t rval = 0; + int rval = 0; #if PHP_CURL_DEBUG fprintf(stderr, "curl_xferinfo() called\n"); @@ -1190,8 +1190,8 @@ static void _php_curl_set_default_options(php_curl *ch) { char *cainfo; - curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); + curl_easy_setopt(ch->cp, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L); curl_easy_setopt(ch->cp, CURLOPT_ERRORBUFFER, ch->err.str); curl_easy_setopt(ch->cp, CURLOPT_WRITEFUNCTION, curl_write); curl_easy_setopt(ch->cp, CURLOPT_FILE, (void *) ch); @@ -1199,8 +1199,8 @@ static void _php_curl_set_default_options(php_curl *ch) curl_easy_setopt(ch->cp, CURLOPT_INFILE, (void *) ch); curl_easy_setopt(ch->cp, CURLOPT_HEADERFUNCTION, curl_write_header); curl_easy_setopt(ch->cp, CURLOPT_WRITEHEADER, (void *) ch); - curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120); - curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20); /* prevent infinite redirects */ + curl_easy_setopt(ch->cp, CURLOPT_DNS_CACHE_TIMEOUT, 120L); + curl_easy_setopt(ch->cp, CURLOPT_MAXREDIRS, 20L); /* prevent infinite redirects */ cainfo = INI_STR("openssl.cafile"); if (!(cainfo && cainfo[0] != '\0')) { @@ -1211,7 +1211,7 @@ static void _php_curl_set_default_options(php_curl *ch) } #ifdef ZTS - curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(ch->cp, CURLOPT_NOSIGNAL, 1L); #endif } /* }}} */ @@ -1701,7 +1701,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue lval = zval_get_long(zvalue); if (lval == 1) { php_error_docref(NULL, E_NOTICE, "CURLOPT_SSL_VERIFYHOST no longer accepts the value 1, value 2 will be used instead"); - error = curl_easy_setopt(ch->cp, option, 2); + error = curl_easy_setopt(ch->cp, option, 2L); break; } ZEND_FALLTHROUGH; @@ -2207,7 +2207,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue case CURLOPT_FOLLOWLOCATION: lval = zend_is_true(zvalue); - error = curl_easy_setopt(ch->cp, option, lval); + error = curl_easy_setopt(ch->cp, option, (long) lval); break; case CURLOPT_POSTFIELDS: @@ -2216,7 +2216,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue /* no need to build the mime structure for empty hashtables; also works around https://github.com/curl/curl/issues/6455 */ curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDS, ""); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTFIELDSIZE, 0L); } else { return build_mime_structure_from_hash(ch, zvalue); } @@ -2249,7 +2249,7 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue case CURLOPT_POSTREDIR: lval = zval_get_long(zvalue); - error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, lval & CURL_REDIR_POST_ALL); + error = curl_easy_setopt(ch->cp, CURLOPT_POSTREDIR, (long) (lval & CURL_REDIR_POST_ALL)); break; /* the following options deal with files, therefore the open_basedir check @@ -2289,11 +2289,11 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue if (zend_is_true(zvalue)) { curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, curl_debug); curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, (void *)ch); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 1L); } else { curl_easy_setopt(ch->cp, CURLOPT_DEBUGFUNCTION, NULL); curl_easy_setopt(ch->cp, CURLOPT_DEBUGDATA, NULL); - curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0); + curl_easy_setopt(ch->cp, CURLOPT_VERBOSE, 0L); } break; diff --git a/ext/curl/tests/curl_copy_handle_variation3_clone.phpt b/ext/curl/tests/curl_copy_handle_variation3_clone.phpt new file mode 100644 index 0000000000000..7951cb0258111 --- /dev/null +++ b/ext/curl/tests/curl_copy_handle_variation3_clone.phpt @@ -0,0 +1,34 @@ +--TEST-- +clone() allows to post CURLFile multiple times +--EXTENSIONS-- +curl +--FILE-- + $file); +var_dump(curl_setopt($ch1, CURLOPT_POSTFIELDS, $params)); + +$ch2 = clone($ch1); + +var_dump(curl_exec($ch1)); + +var_dump(curl_exec($ch2)); +?> +--EXPECTF-- +bool(true) +string(%d) "curl_copy_handle_variation3_clone.txt|application/octet-stream|5" +string(%d) "curl_copy_handle_variation3_clone.txt|application/octet-stream|5" +--CLEAN-- + diff --git a/ext/curl/tests/curl_setopt_ssl.phpt b/ext/curl/tests/curl_setopt_ssl.phpt index 11d8fff702a88..ff08528321a0f 100644 --- a/ext/curl/tests/curl_setopt_ssl.phpt +++ b/ext/curl/tests/curl_setopt_ssl.phpt @@ -18,9 +18,13 @@ if ($curl_version['version_number'] < 0x074700) { --FILE-- 0 && **ptr == ')') { ++*ptr; + paren_count--; } return retval; } @@ -1018,11 +1021,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) std: s->tok = cursor; s->len = 0; -#line 1151 "ext/date/lib/parse_date.re" +#line 1154 "ext/date/lib/parse_date.re" -#line 1026 "" +#line 1029 "ext/date/lib/parse_date.c" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -1203,23 +1206,23 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(2, *YYCURSOR); ++YYCURSOR; YYDEBUG(3, *YYCURSOR); -#line 1984 "ext/date/lib/parse_date.re" +#line 1987 "ext/date/lib/parse_date.re" { s->pos = cursor; s->line++; goto std; } -#line 1212 "" +#line 1215 "ext/date/lib/parse_date.c" yy4: YYDEBUG(4, *YYCURSOR); ++YYCURSOR; yy5: YYDEBUG(5, *YYCURSOR); -#line 1990 "ext/date/lib/parse_date.re" +#line 1993 "ext/date/lib/parse_date.re" { add_error(s, TIMELIB_ERR_UNEXPECTED_CHARACTER, "Unexpected character"); goto std; } -#line 1223 "" +#line 1226 "ext/date/lib/parse_date.c" yy6: YYDEBUG(6, *YYCURSOR); yyaccept = 0; @@ -1234,11 +1237,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy58; yy8: YYDEBUG(8, *YYCURSOR); -#line 1979 "ext/date/lib/parse_date.re" +#line 1982 "ext/date/lib/parse_date.re" { goto std; } -#line 1242 "" +#line 1245 "ext/date/lib/parse_date.c" yy9: YYDEBUG(9, *YYCURSOR); yych = *++YYCURSOR; @@ -1272,11 +1275,11 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(11, *YYCURSOR); ++YYCURSOR; YYDEBUG(12, *YYCURSOR); -#line 1974 "ext/date/lib/parse_date.re" +#line 1977 "ext/date/lib/parse_date.re" { goto std; } -#line 1280 "" +#line 1283 "ext/date/lib/parse_date.c" yy13: YYDEBUG(13, *YYCURSOR); yyaccept = 1; @@ -1777,7 +1780,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy20: YYDEBUG(20, *YYCURSOR); -#line 1889 "ext/date/lib/parse_date.re" +#line 1892 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("tzcorrection | tz"); @@ -1791,7 +1794,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIMEZONE; } -#line 1795 "" +#line 1798 "ext/date/lib/parse_date.c" yy21: YYDEBUG(21, *YYCURSOR); yych = *++YYCURSOR; @@ -3596,7 +3599,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy81: YYDEBUG(81, *YYCURSOR); -#line 1636 "ext/date/lib/parse_date.re" +#line 1639 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datenoyearrev"); TIMELIB_INIT; @@ -3607,7 +3610,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 3611 "" +#line 3614 "ext/date/lib/parse_date.c" yy82: YYDEBUG(82, *YYCURSOR); yych = *++YYCURSOR; @@ -4122,7 +4125,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } if (yych == '.') goto yy289; YYDEBUG(114, *YYCURSOR); -#line 1211 "ext/date/lib/parse_date.re" +#line 1214 "ext/date/lib/parse_date.re" { timelib_ull i; @@ -4147,7 +4150,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 4151 "" +#line 4154 "ext/date/lib/parse_date.c" yy115: YYDEBUG(115, *YYCURSOR); ++YYCURSOR; @@ -5873,7 +5876,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy177: YYDEBUG(177, *YYCURSOR); -#line 1377 "ext/date/lib/parse_date.re" +#line 1380 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("timetiny24 | timeshort24 | timelong24 | iso8601long"); @@ -5900,7 +5903,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIME24_WITH_ZONE; } -#line 5904 "" +#line 5907 "ext/date/lib/parse_date.c" yy178: YYDEBUG(178, *YYCURSOR); yyaccept = 4; @@ -6929,7 +6932,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy224: YYDEBUG(224, *YYCURSOR); -#line 1471 "ext/date/lib/parse_date.re" +#line 1474 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("americanshort | american"); @@ -6944,7 +6947,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_AMERICAN; } -#line 6948 "" +#line 6951 "ext/date/lib/parse_date.c" yy225: YYDEBUG(225, *YYCURSOR); yyaccept = 5; @@ -7187,7 +7190,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy431; yy251: YYDEBUG(251, *YYCURSOR); -#line 1553 "ext/date/lib/parse_date.re" +#line 1556 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datefull"); @@ -7201,7 +7204,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_FULL; } -#line 7205 "" +#line 7208 "ext/date/lib/parse_date.c" yy252: YYDEBUG(252, *YYCURSOR); yyaccept = 3; @@ -7315,7 +7318,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych == 'e') goto yy440; yy260: YYDEBUG(260, *YYCURSOR); -#line 1958 "ext/date/lib/parse_date.re" +#line 1961 "ext/date/lib/parse_date.re" { timelib_ull i; DEBUG_OUTPUT("relative"); @@ -7330,7 +7333,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 7334 "" +#line 7337 "ext/date/lib/parse_date.c" yy261: YYDEBUG(261, *YYCURSOR); yych = *++YYCURSOR; @@ -7776,7 +7779,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy471; yy290: YYDEBUG(290, *YYCURSOR); -#line 1237 "ext/date/lib/parse_date.re" +#line 1240 "ext/date/lib/parse_date.re" { timelib_sll i; timelib_ull us; @@ -7815,7 +7818,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 7819 "" +#line 7822 "ext/date/lib/parse_date.c" yy291: YYDEBUG(291, *YYCURSOR); yych = *++YYCURSOR; @@ -7840,7 +7843,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy293: YYDEBUG(293, *YYCURSOR); -#line 1799 "ext/date/lib/parse_date.re" +#line 1802 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("ago"); TIMELIB_INIT; @@ -7860,7 +7863,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_AGO; } -#line 7864 "" +#line 7867 "ext/date/lib/parse_date.c" yy294: YYDEBUG(294, *YYCURSOR); yyaccept = 7; @@ -7899,7 +7902,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy295: YYDEBUG(295, *YYCURSOR); -#line 1879 "ext/date/lib/parse_date.re" +#line 1882 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("monthtext"); TIMELIB_INIT; @@ -7908,7 +7911,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 7912 "" +#line 7915 "ext/date/lib/parse_date.c" yy296: YYDEBUG(296, *YYCURSOR); yyaccept = 7; @@ -8483,7 +8486,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy315: YYDEBUG(315, *YYCURSOR); -#line 1820 "ext/date/lib/parse_date.re" +#line 1823 "ext/date/lib/parse_date.re" { const timelib_relunit* relunit; DEBUG_OUTPUT("daytext"); @@ -8500,7 +8503,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_WEEKDAY; } -#line 8504 "" +#line 8507 "ext/date/lib/parse_date.c" yy316: YYDEBUG(316, *YYCURSOR); yych = *++YYCURSOR; @@ -8768,7 +8771,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy325: YYDEBUG(325, *YYCURSOR); -#line 1622 "ext/date/lib/parse_date.re" +#line 1625 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datetextual | datenoyear"); @@ -8781,7 +8784,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_TEXT; } -#line 8785 "" +#line 8788 "ext/date/lib/parse_date.c" yy326: YYDEBUG(326, *YYCURSOR); yyaccept = 10; @@ -9475,7 +9478,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy351: YYDEBUG(351, *YYCURSOR); -#line 1168 "ext/date/lib/parse_date.re" +#line 1171 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("now"); TIMELIB_INIT; @@ -9483,7 +9486,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 9487 "" +#line 9490 "ext/date/lib/parse_date.c" yy352: YYDEBUG(352, *YYCURSOR); yyaccept = 2; @@ -10986,7 +10989,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy420: YYDEBUG(420, *YYCURSOR); -#line 1405 "ext/date/lib/parse_date.re" +#line 1408 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("gnunocolon"); TIMELIB_INIT; @@ -11008,7 +11011,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_GNU_NOCOLON; } -#line 11012 "" +#line 11015 "ext/date/lib/parse_date.c" yy421: YYDEBUG(421, *YYCURSOR); yyaccept = 13; @@ -11089,7 +11092,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy422: YYDEBUG(422, *YYCURSOR); -#line 1790 "ext/date/lib/parse_date.re" +#line 1793 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("year4"); TIMELIB_INIT; @@ -11097,7 +11100,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_CLF; } -#line 11101 "" +#line 11104 "ext/date/lib/parse_date.c" yy423: YYDEBUG(423, *YYCURSOR); yyaccept = 3; @@ -11704,7 +11707,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(456, *YYCURSOR); ++YYCURSOR; YYDEBUG(457, *YYCURSOR); -#line 1339 "ext/date/lib/parse_date.re" +#line 1342 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("timetiny12 | timeshort12 | timelong12"); TIMELIB_INIT; @@ -11721,7 +11724,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIME12; } -#line 11725 "" +#line 11728 "ext/date/lib/parse_date.c" yy458: YYDEBUG(458, *YYCURSOR); yych = *++YYCURSOR; @@ -13048,7 +13051,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy526: YYDEBUG(526, *YYCURSOR); -#line 1177 "ext/date/lib/parse_date.re" +#line 1180 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("noon"); TIMELIB_INIT; @@ -13059,7 +13062,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 13063 "" +#line 13066 "ext/date/lib/parse_date.c" yy527: YYDEBUG(527, *YYCURSOR); yyaccept = 2; @@ -14105,7 +14108,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy567: YYDEBUG(567, *YYCURSOR); -#line 1539 "ext/date/lib/parse_date.re" +#line 1542 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("gnudateshort"); @@ -14118,7 +14121,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 14122 "" +#line 14125 "ext/date/lib/parse_date.c" yy568: YYDEBUG(568, *YYCURSOR); yyaccept = 15; @@ -14569,7 +14572,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy600: YYDEBUG(600, *YYCURSOR); -#line 1608 "ext/date/lib/parse_date.re" +#line 1611 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datenodayrev"); @@ -14582,7 +14585,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_NO_DAY; } -#line 14586 "" +#line 14589 "ext/date/lib/parse_date.c" yy601: YYDEBUG(601, *YYCURSOR); yych = *++YYCURSOR; @@ -15957,7 +15960,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(696, *YYCURSOR); ++YYCURSOR; YYDEBUG(697, *YYCURSOR); -#line 1594 "ext/date/lib/parse_date.re" +#line 1597 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("datenoday"); @@ -15970,7 +15973,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_NO_DAY; } -#line 15974 "" +#line 15977 "ext/date/lib/parse_date.c" yy698: YYDEBUG(698, *YYCURSOR); yych = *++YYCURSOR; @@ -16531,7 +16534,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy722: YYDEBUG(722, *YYCURSOR); -#line 1189 "ext/date/lib/parse_date.re" +#line 1192 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("midnight | today"); TIMELIB_INIT; @@ -16540,7 +16543,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 16544 "" +#line 16547 "ext/date/lib/parse_date.c" yy723: YYDEBUG(723, *YYCURSOR); yych = *++YYCURSOR; @@ -16850,7 +16853,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy897; yy739: YYDEBUG(739, *YYCURSOR); -#line 1580 "ext/date/lib/parse_date.re" +#line 1583 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pointed date YY"); @@ -16863,7 +16866,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_FULL_POINTED; } -#line 16867 "" +#line 16870 "ext/date/lib/parse_date.c" yy740: YYDEBUG(740, *YYCURSOR); yyaccept = 15; @@ -16975,7 +16978,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy752: YYDEBUG(752, *YYCURSOR); -#line 1525 "ext/date/lib/parse_date.re" +#line 1528 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("gnudateshorter"); @@ -16988,7 +16991,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 16992 "" +#line 16995 "ext/date/lib/parse_date.c" yy753: YYDEBUG(753, *YYCURSOR); yyaccept = 18; @@ -17237,7 +17240,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy777: YYDEBUG(777, *YYCURSOR); -#line 1451 "ext/date/lib/parse_date.re" +#line 1454 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("iso8601nocolon"); @@ -17256,7 +17259,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_NOCOLON; } -#line 17260 "" +#line 17263 "ext/date/lib/parse_date.c" yy778: YYDEBUG(778, *YYCURSOR); yyaccept = 19; @@ -18484,7 +18487,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy849: YYDEBUG(849, *YYCURSOR); -#line 1928 "ext/date/lib/parse_date.re" +#line 1931 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("dateshortwithtimeshort | dateshortwithtimelong | dateshortwithtimelongtz"); @@ -18513,7 +18516,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_SHORTDATE_WITH_TIME; } -#line 18517 "" +#line 18520 "ext/date/lib/parse_date.c" yy850: YYDEBUG(850, *YYCURSOR); yyaccept = 20; @@ -19557,7 +19560,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy926: YYDEBUG(926, *YYCURSOR); -#line 1686 "ext/date/lib/parse_date.re" +#line 1689 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pgydotd"); @@ -19570,7 +19573,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_PG_YEARDAY; } -#line 19574 "" +#line 19577 "ext/date/lib/parse_date.c" yy927: YYDEBUG(927, *YYCURSOR); yyaccept = 21; @@ -19824,7 +19827,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '7') goto yy1059; yy942: YYDEBUG(942, *YYCURSOR); -#line 1719 "ext/date/lib/parse_date.re" +#line 1722 "ext/date/lib/parse_date.re" { timelib_sll w, d; DEBUG_OUTPUT("isoweek"); @@ -19842,7 +19845,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_WEEK; } -#line 19846 "" +#line 19849 "ext/date/lib/parse_date.c" yy943: YYDEBUG(943, *YYCURSOR); yych = *++YYCURSOR; @@ -20318,7 +20321,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych == 'e') goto yy1094; yy982: YYDEBUG(982, *YYCURSOR); -#line 1862 "ext/date/lib/parse_date.re" +#line 1865 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -20334,7 +20337,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 20338 "" +#line 20341 "ext/date/lib/parse_date.c" yy983: YYDEBUG(983, *YYCURSOR); yych = *++YYCURSOR; @@ -20681,7 +20684,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1020, *YYCURSOR); ++YYCURSOR; YYDEBUG(1021, *YYCURSOR); -#line 1568 "ext/date/lib/parse_date.re" +#line 1571 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("pointed date YYYY"); TIMELIB_INIT; @@ -20692,7 +20695,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_FULL_POINTED; } -#line 20696 "" +#line 20699 "ext/date/lib/parse_date.c" yy1022: YYDEBUG(1022, *YYCURSOR); ++YYCURSOR; @@ -20721,7 +20724,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1025: YYDEBUG(1025, *YYCURSOR); -#line 1499 "ext/date/lib/parse_date.re" +#line 1502 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("iso8601date2"); @@ -20734,7 +20737,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 20738 "" +#line 20741 "ext/date/lib/parse_date.c" yy1026: YYDEBUG(1026, *YYCURSOR); yyaccept = 15; @@ -20954,7 +20957,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1043: YYDEBUG(1043, *YYCURSOR); -#line 1487 "ext/date/lib/parse_date.re" +#line 1490 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("iso8601date4 | iso8601date2 | iso8601dateslash | dateslash"); TIMELIB_INIT; @@ -20965,7 +20968,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 20969 "" +#line 20972 "ext/date/lib/parse_date.c" yy1044: YYDEBUG(1044, *YYCURSOR); yyaccept = 26; @@ -21080,7 +21083,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1048: YYDEBUG(1048, *YYCURSOR); -#line 1648 "ext/date/lib/parse_date.re" +#line 1651 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("datenocolon"); TIMELIB_INIT; @@ -21091,7 +21094,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_DATE_NOCOLON; } -#line 21095 "" +#line 21098 "ext/date/lib/parse_date.c" yy1049: YYDEBUG(1049, *YYCURSOR); yych = *++YYCURSOR; @@ -21161,7 +21164,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1059, *YYCURSOR); ++YYCURSOR; YYDEBUG(1060, *YYCURSOR); -#line 1700 "ext/date/lib/parse_date.re" +#line 1703 "ext/date/lib/parse_date.re" { timelib_sll w, d; DEBUG_OUTPUT("isoweekday"); @@ -21179,7 +21182,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_WEEK; } -#line 21183 "" +#line 21186 "ext/date/lib/parse_date.c" yy1061: YYDEBUG(1061, *YYCURSOR); yych = *++YYCURSOR; @@ -21242,7 +21245,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy1143; yy1070: YYDEBUG(1070, *YYCURSOR); -#line 1738 "ext/date/lib/parse_date.re" +#line 1741 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pgtextshort"); @@ -21255,7 +21258,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_PG_TEXT; } -#line 21259 "" +#line 21262 "ext/date/lib/parse_date.c" yy1071: YYDEBUG(1071, *YYCURSOR); yych = *++YYCURSOR; @@ -21728,7 +21731,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) ++YYCURSOR; yy1107: YYDEBUG(1107, *YYCURSOR); -#line 1199 "ext/date/lib/parse_date.re" +#line 1202 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("tomorrow"); TIMELIB_INIT; @@ -21739,7 +21742,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 21743 "" +#line 21746 "ext/date/lib/parse_date.c" yy1108: YYDEBUG(1108, *YYCURSOR); yyaccept = 28; @@ -22076,7 +22079,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1140, *YYCURSOR); ++YYCURSOR; YYDEBUG(1141, *YYCURSOR); -#line 1752 "ext/date/lib/parse_date.re" +#line 1755 "ext/date/lib/parse_date.re" { int length = 0; DEBUG_OUTPUT("pgtextreverse"); @@ -22089,7 +22092,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_PG_TEXT; } -#line 22093 "" +#line 22096 "ext/date/lib/parse_date.c" yy1142: YYDEBUG(1142, *YYCURSOR); ++YYCURSOR; @@ -22133,7 +22136,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1145: YYDEBUG(1145, *YYCURSOR); -#line 1294 "ext/date/lib/parse_date.re" +#line 1297 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("backof | frontof"); TIMELIB_INIT; @@ -22155,7 +22158,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_LF_DAY_OF_MONTH; } -#line 22159 "" +#line 22162 "ext/date/lib/parse_date.c" yy1146: YYDEBUG(1146, *YYCURSOR); yyaccept = 29; @@ -22479,7 +22482,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) } yy1172: YYDEBUG(1172, *YYCURSOR); -#line 1838 "ext/date/lib/parse_date.re" +#line 1841 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -22502,7 +22505,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 22506 "" +#line 22509 "ext/date/lib/parse_date.c" yy1173: YYDEBUG(1173, *YYCURSOR); yych = *++YYCURSOR; @@ -22514,7 +22517,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) ++YYCURSOR; yy1175: YYDEBUG(1175, *YYCURSOR); -#line 1156 "ext/date/lib/parse_date.re" +#line 1159 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("yesterday"); TIMELIB_INIT; @@ -22525,7 +22528,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_RELATIVE; } -#line 22529 "" +#line 22532 "ext/date/lib/parse_date.c" yy1176: YYDEBUG(1176, *YYCURSOR); yyaccept = 31; @@ -23018,7 +23021,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1222, *YYCURSOR); ++YYCURSOR; YYDEBUG(1223, *YYCURSOR); -#line 1904 "ext/date/lib/parse_date.re" +#line 1907 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("dateshortwithtimeshort12 | dateshortwithtimelong12"); TIMELIB_INIT; @@ -23041,7 +23044,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_SHORTDATE_WITH_TIME; } -#line 23045 "" +#line 23048 "ext/date/lib/parse_date.c" yy1224: YYDEBUG(1224, *YYCURSOR); yych = *++YYCURSOR; @@ -23543,7 +23546,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1268, *YYCURSOR); ++YYCURSOR; YYDEBUG(1269, *YYCURSOR); -#line 1317 "ext/date/lib/parse_date.re" +#line 1320 "ext/date/lib/parse_date.re" { timelib_sll i; int behavior = 0; @@ -23564,7 +23567,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_WEEK_DAY_OF_MONTH; } -#line 23568 "" +#line 23571 "ext/date/lib/parse_date.c" yy1270: YYDEBUG(1270, *YYCURSOR); yyaccept = 24; @@ -23611,7 +23614,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1273, *YYCURSOR); ++YYCURSOR; YYDEBUG(1274, *YYCURSOR); -#line 1277 "ext/date/lib/parse_date.re" +#line 1280 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("firstdayof | lastdayof"); TIMELIB_INIT; @@ -23627,12 +23630,12 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_LF_DAY_OF_MONTH; } -#line 23631 "" +#line 23634 "ext/date/lib/parse_date.c" yy1275: YYDEBUG(1275, *YYCURSOR); ++YYCURSOR; YYDEBUG(1276, *YYCURSOR); -#line 1513 "ext/date/lib/parse_date.re" +#line 1516 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("iso8601datex"); TIMELIB_INIT; @@ -23643,7 +23646,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 23647 "" +#line 23650 "ext/date/lib/parse_date.c" yy1277: YYDEBUG(1277, *YYCURSOR); yych = *++YYCURSOR; @@ -23746,7 +23749,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) YYDEBUG(1290, *YYCURSOR); ++YYCURSOR; YYDEBUG(1291, *YYCURSOR); -#line 1357 "ext/date/lib/parse_date.re" +#line 1360 "ext/date/lib/parse_date.re" { DEBUG_OUTPUT("mssqltime"); TIMELIB_INIT; @@ -23765,7 +23768,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_TIME24_WITH_ZONE; } -#line 23769 "" +#line 23772 "ext/date/lib/parse_date.c" yy1292: YYDEBUG(1292, *YYCURSOR); yych = *++YYCURSOR; @@ -24189,7 +24192,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= '9') goto yy1331; yy1329: YYDEBUG(1329, *YYCURSOR); -#line 1660 "ext/date/lib/parse_date.re" +#line 1663 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("xmlrpc | xmlrpcnocolon | soap | wddx | exif"); @@ -24214,7 +24217,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_XMLRPC_SOAP; } -#line 24218 "" +#line 24221 "ext/date/lib/parse_date.c" yy1330: YYDEBUG(1330, *YYCURSOR); yych = *++YYCURSOR; @@ -24584,7 +24587,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych <= ':') goto yy1383; yy1375: YYDEBUG(1375, *YYCURSOR); -#line 1766 "ext/date/lib/parse_date.re" +#line 1769 "ext/date/lib/parse_date.re" { int tz_not_found; DEBUG_OUTPUT("clf"); @@ -24607,7 +24610,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) TIMELIB_DEINIT; return TIMELIB_CLF; } -#line 24611 "" +#line 24614 "ext/date/lib/parse_date.c" yy1376: YYDEBUG(1376, *YYCURSOR); yyaccept = 33; @@ -24839,7 +24842,7 @@ static int scan(Scanner *s, timelib_tz_get_wrapper tz_get_wrapper) if (yych == ':') goto yy1286; goto yy1329; } -#line 1994 "ext/date/lib/parse_date.re" +#line 1997 "ext/date/lib/parse_date.re" } diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re index d32be9bfe7be7..c40a5c07c9aa1 100644 --- a/ext/date/lib/parse_date.re +++ b/ext/date/lib/parse_date.re @@ -939,10 +939,12 @@ timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int { timelib_tzinfo *res; timelib_long retval = 0; + size_t paren_count = 0; *tz_not_found = 0; while (**ptr == ' ' || **ptr == '\t' || **ptr == '(') { + paren_count += **ptr == '('; ++*ptr; } if ((*ptr)[0] == 'G' && (*ptr)[1] == 'M' && (*ptr)[2] == 'T' && ((*ptr)[3] == '+' || (*ptr)[3] == '-')) { @@ -991,8 +993,9 @@ timelib_long timelib_parse_zone(const char **ptr, int *dst, timelib_time *t, int *tz_not_found = (found == 0); retval = offset; } - while (**ptr == ')') { + while (paren_count > 0 && **ptr == ')') { ++*ptr; + paren_count--; } return retval; } diff --git a/ext/date/lib/parse_iso_intervals.c b/ext/date/lib/parse_iso_intervals.c index cdc329431ec45..a9fca1b609413 100644 --- a/ext/date/lib/parse_iso_intervals.c +++ b/ext/date/lib/parse_iso_intervals.c @@ -1,4 +1,4 @@ -/* Generated by re2c 1.0.3 on Wed Sep 11 17:29:40 2024 */ +/* Generated by re2c 1.0.3 on Mon Sep 15 10:38:16 2025 */ #line 1 "ext/date/lib/parse_iso_intervals.re" /* * The MIT License (MIT) @@ -180,7 +180,7 @@ static int scan(Scanner *s) -#line 184 "" +#line 184 "ext/date/lib/parse_iso_intervals.c" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -252,7 +252,7 @@ static int scan(Scanner *s) s->pos = cursor; s->line++; goto std; } -#line 256 "" +#line 256 "ext/date/lib/parse_iso_intervals.c" yy4: YYDEBUG(4, *YYCURSOR); ++YYCURSOR; @@ -263,7 +263,7 @@ static int scan(Scanner *s) add_error(s, "Unexpected character"); goto std; } -#line 267 "" +#line 267 "ext/date/lib/parse_iso_intervals.c" yy6: YYDEBUG(6, *YYCURSOR); ++YYCURSOR; @@ -272,7 +272,7 @@ static int scan(Scanner *s) { goto std; } -#line 276 "" +#line 276 "ext/date/lib/parse_iso_intervals.c" yy8: YYDEBUG(8, *YYCURSOR); yyaccept = 0; @@ -330,7 +330,7 @@ static int scan(Scanner *s) TIMELIB_DEINIT; return TIMELIB_PERIOD; } -#line 334 "" +#line 334 "ext/date/lib/parse_iso_intervals.c" yy11: YYDEBUG(11, *YYCURSOR); yych = *++YYCURSOR; @@ -399,7 +399,7 @@ static int scan(Scanner *s) s->have_recurrences = 1; return TIMELIB_PERIOD; } -#line 403 "" +#line 403 "ext/date/lib/parse_iso_intervals.c" yy19: YYDEBUG(19, *YYCURSOR); yych = *++YYCURSOR; @@ -917,7 +917,7 @@ static int scan(Scanner *s) TIMELIB_DEINIT; return TIMELIB_ISO_DATE; } -#line 921 "" +#line 921 "ext/date/lib/parse_iso_intervals.c" yy91: YYDEBUG(91, *YYCURSOR); yych = *++YYCURSOR; @@ -956,7 +956,7 @@ static int scan(Scanner *s) TIMELIB_DEINIT; return TIMELIB_PERIOD; } -#line 960 "" +#line 960 "ext/date/lib/parse_iso_intervals.c" } #line 321 "ext/date/lib/parse_iso_intervals.re" diff --git a/ext/date/lib/timelib.h b/ext/date/lib/timelib.h index a2c976af7ed9c..ef57a7ee783f0 100644 --- a/ext/date/lib/timelib.h +++ b/ext/date/lib/timelib.h @@ -1,7 +1,7 @@ /* * The MIT License (MIT) * - * Copyright (c) 2015-2024 Derick Rethans + * Copyright (c) 2015-2025 Derick Rethans * Copyright (c) 2018,2021 MongoDB, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -30,9 +30,9 @@ # include "timelib_config.h" #endif -#define TIMELIB_VERSION 202212 -#define TIMELIB_EXTENDED_VERSION 20221201 -#define TIMELIB_ASCII_VERSION "2022.12" +#define TIMELIB_VERSION 202214 +#define TIMELIB_EXTENDED_VERSION 20221401 +#define TIMELIB_ASCII_VERSION "2022.14" #include #include diff --git a/ext/dba/dba.c b/ext/dba/dba.c index 0944e6e0b5f16..29e996e4f3207 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -1070,6 +1070,11 @@ PHP_FUNCTION(dba_fetch) ZEND_PARSE_PARAMETERS_END(); } + if (ZEND_LONG_EXCEEDS_INT(skip)) { + zend_argument_value_error(3, "must be between %d and %d", INT_MIN, INT_MAX); + RETURN_THROWS(); + } + info = Z_DBA_INFO_P(id); CHECK_DBA_CONNECTION(info); diff --git a/ext/dba/tests/gh19885.phpt b/ext/dba/tests/gh19885.phpt new file mode 100644 index 0000000000000..987aea4f175a2 --- /dev/null +++ b/ext/dba/tests/gh19885.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-19885 (dba_fetch() segfault on large skip values) +--EXTENSIONS-- +dba +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +try { + dba_fetch("https://redirect.github.com/1", $db, PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} +// negative skip needs to remain acceptable albeit corrected down the line +var_dump(dba_fetch("https://redirect.github.com/1", $db, -1000000)); +?> +--EXPECTF-- +dba_fetch(): Argument #3 ($skip) must be between -%d and %d +dba_fetch(): Argument #3 ($skip) must be between -%d and %d + +Notice: dba_fetch(): Handler cdb accepts only skip values greater than or equal to zero, using skip=0 in %s on line %d +string(1) "1" diff --git a/ext/dom/attr.c b/ext/dom/attr.c index dfe3abc1a885c..5a0900d657eea 100644 --- a/ext/dom/attr.c +++ b/ext/dom/attr.c @@ -201,10 +201,10 @@ PHP_METHOD(DOMAttr, isId) bool dom_compare_value(const xmlAttr *attr, const xmlChar *value) { - bool free; - xmlChar *attr_value = php_libxml_attr_value(attr, &free); + bool should_free; + xmlChar *attr_value = php_libxml_attr_value(attr, &should_free); bool result = xmlStrEqual(attr_value, value); - if (free) { + if (should_free) { xmlFree(attr_value); } return result; diff --git a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c index 2bb41e43bec5d..4e094f632ef79 100644 --- a/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c +++ b/ext/dom/lexbor/lexbor/selectors-adapted/selectors.c @@ -13,7 +13,7 @@ #include #include -#include "ext/dom/lexbor/lexbor/selectors-adapted/selectors.h" +#include "lexbor/selectors-adapted/selectors.h" #include "../../../namespace_compat.h" #include "../../../domexception.h" #include "../../../php_dom.h" diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 3242529d8842c..2438a081351a0 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -2396,10 +2396,10 @@ void php_dom_get_content_into_zval(const xmlNode *nodep, zval *return_value, boo } case XML_ATTRIBUTE_NODE: { - bool free; - xmlChar *value = php_libxml_attr_value((const xmlAttr *) nodep, &free); + bool should_free; + xmlChar *value = php_libxml_attr_value((const xmlAttr *) nodep, &should_free); RETVAL_STRING_FAST((const char *) value); - if (free) { + if (should_free) { xmlFree(value); } return; diff --git a/ext/gd/libgd/gdkanji.c b/ext/gd/libgd/gdkanji.c index 21bc2280982a8..ef769f89badda 100644 --- a/ext/gd/libgd/gdkanji.c +++ b/ext/gd/libgd/gdkanji.c @@ -368,6 +368,8 @@ do_convert (unsigned char *to, unsigned char *from, const char *code) else error ("something happen"); strcpy ((char *) to, (const char *) from); + if (iconv_close (cd) != 0) + error ("iconv_close() error"); return; } diff --git a/ext/gd/tests/gh19955.phpt b/ext/gd/tests/gh19955.phpt new file mode 100644 index 0000000000000..a4b58e403cf40 --- /dev/null +++ b/ext/gd/tests/gh19955.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-19955: (imagefttext() memory leak) +--EXTENSIONS-- +gd +--CREDITS-- +YuanchengJiang +--FILE-- + +--EXPECT-- +OK diff --git a/ext/mysqli/tests/bug67563.phpt b/ext/mysqli/tests/bug67563.phpt new file mode 100644 index 0000000000000..72a51ecdccd2c --- /dev/null +++ b/ext/mysqli/tests/bug67563.phpt @@ -0,0 +1,40 @@ +--TEST-- +Fix bug #67563 (mysqli compiled with mysqlnd does not take ipv6 adress as parameter) +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--INI-- +max_execution_time=240 +--FILE-- +close(); + } +} + +print "done!"; +?> +--EXPECT-- +done! diff --git a/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt b/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt index 594980ec0f829..81c15d53b84cb 100644 --- a/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt +++ b/ext/mysqli/tests/fetch/mysqli_fetch_all_data_types_variation.phpt @@ -4,6 +4,9 @@ mysqli_fetch_all() data types variation mysqli --SKIPIF-- diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index 8582e376e92a6..a27defdf9d6bd 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -513,6 +513,16 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn, } /* }}} */ +/* ipv6 addresses have at least two colons, which is how we can differentiate between domain names and addresses */ +static bool mysqlnd_fast_is_ipv6_address(const char *s) +{ + const char *first_colon = strchr(s, ':'); + if (!first_colon) { + return false; + } + return strchr(first_colon + 1, ':') != NULL; +} + /* {{{ mysqlnd_conn_data::get_scheme */ static MYSQLND_STRING MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_CSTRING hostname, MYSQLND_CSTRING *socket_or_pipe, unsigned int port, bool * unix_socket, bool * named_pipe) @@ -542,7 +552,13 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_ if (!port) { port = 3306; } - transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port); + + /* ipv6 addresses are in the format [address]:port */ + if (hostname.s[0] != '[' && mysqlnd_fast_is_ipv6_address(hostname.s)) { + transport.l = mnd_sprintf(&transport.s, 0, "tcp://[%s]:%u", hostname.s, port); + } else { + transport.l = mnd_sprintf(&transport.s, 0, "tcp://%s:%u", hostname.s, port); + } } DBG_INF_FMT("transport=%s", transport.s? transport.s:"OOM"); DBG_RETURN(transport); diff --git a/ext/opcache/jit/ir/LICENSE b/ext/opcache/jit/ir/LICENSE index c43a12a770f8f..340f9c37225f6 100644 --- a/ext/opcache/jit/ir/LICENSE +++ b/ext/opcache/jit/ir/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2022 Zend by Perforce +Copyright (c) 2025 Dmitry Stogov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ext/opcache/jit/ir/ir.c b/ext/opcache/jit/ir/ir.c index 97d32680e4fdd..81621ce11bd36 100644 --- a/ext/opcache/jit/ir/ir.c +++ b/ext/opcache/jit/ir/ir.c @@ -227,6 +227,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted #define ir_op_flag_d0 ir_op_flag_d #define ir_op_flag_d1 (ir_op_flag_d | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_d1X1 (ir_op_flag_d | 1 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_d1X2 (ir_op_flag_d | 1 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_d2 (ir_op_flag_d | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_d2C (ir_op_flag_d | IR_OP_FLAG_COMMUTATIVE | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_d3 (ir_op_flag_d | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) @@ -270,6 +271,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted #define ir_op_flag_s3 (ir_op_flag_s | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_x1 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_x2 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 2 | (2 << IR_OP_FLAG_OPERANDS_SHIFT)) +#define ir_op_flag_x2X1 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 2 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_x3 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | 3 | (3 << IR_OP_FLAG_OPERANDS_SHIFT)) #define ir_op_flag_xN (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_CALL | IR_OP_FLAG_VAR_INPUTS) #define ir_op_flag_a1 (IR_OP_FLAG_CONTROL|IR_OP_FLAG_MEM|IR_OP_FLAG_MEM_ALLOC | 1 | (1 << IR_OP_FLAG_OPERANDS_SHIFT)) @@ -392,6 +394,8 @@ void ir_init(ir_ctx *ctx, uint32_t flags, ir_ref consts_limit, ir_ref insns_limi ctx->insns_limit = insns_limit; ctx->consts_count = -(IR_TRUE - 1); ctx->consts_limit = consts_limit; + ctx->const_hash = ctx->_const_hash; + ctx->const_hash_mask = IR_CONST_HASH_SIZE - 1; ctx->fold_cse_limit = IR_UNUSED + 1; ctx->flags = flags; @@ -414,6 +418,9 @@ void ir_free(ir_ctx *ctx) { ir_insn *buf = ctx->ir_base - ctx->consts_limit; ir_mem_free(buf); + if (ctx->value_params) { + ir_mem_free(ctx->value_params); + } if (ctx->strtab.data) { ir_strtab_free(&ctx->strtab); } @@ -468,6 +475,10 @@ void ir_free(ir_ctx *ctx) ir_list_free((ir_list*)ctx->osr_entry_loads); ir_mem_free(ctx->osr_entry_loads); } + + if (ctx->const_hash_mask != IR_CONST_HASH_SIZE - 1) { + ir_mem_free(ctx->const_hash); + } } ir_ref ir_unique_const_addr(ir_ctx *ctx, uintptr_t addr) @@ -479,72 +490,64 @@ ir_ref ir_unique_const_addr(ir_ctx *ctx, uintptr_t addr) insn->val.u64 = addr; /* don't insert into constants chain */ insn->prev_const = IR_UNUSED; -#if 0 - insn->prev_const = ctx->prev_const_chain[IR_ADDR]; - ctx->prev_const_chain[IR_ADDR] = ref; -#endif -#if 0 - ir_insn *prev_insn, *next_insn; - ir_ref next; - - prev_insn = NULL; - next = ctx->prev_const_chain[IR_ADDR]; - while (next) { - next_insn = &ctx->ir_base[next]; - if (UNEXPECTED(next_insn->val.u64 >= addr)) { - break; - } - prev_insn = next_insn; - next = next_insn->prev_const; - } - - if (prev_insn) { - insn->prev_const = prev_insn->prev_const; - prev_insn->prev_const = ref; - } else { - insn->prev_const = ctx->prev_const_chain[IR_ADDR]; - ctx->prev_const_chain[IR_ADDR] = ref; - } -#endif return ref; } +IR_ALWAYS_INLINE uintptr_t ir_const_hash(ir_val val, uint32_t optx) +{ + return (val.u64 ^ (val.u64 >> 32) ^ optx); +} + +static IR_NEVER_INLINE void ir_const_hash_rehash(ir_ctx *ctx) +{ + ir_insn *insn; + ir_ref ref; + uintptr_t hash; + + if (ctx->const_hash_mask != IR_CONST_HASH_SIZE - 1) { + ir_mem_free(ctx->const_hash); + } + ctx->const_hash_mask = (ctx->const_hash_mask + 1) * 2 - 1; + ctx->const_hash = ir_mem_calloc(ctx->const_hash_mask + 1, sizeof(ir_ref)); + for (ref = IR_TRUE - 1; ref > -ctx->consts_count; ref--) { + insn = &ctx->ir_base[ref]; + hash = ir_const_hash(insn->val, insn->optx) & ctx->const_hash_mask; + insn->prev_const = ctx->const_hash[hash]; + ctx->const_hash[hash] = ref; + } +} + ir_ref ir_const_ex(ir_ctx *ctx, ir_val val, uint8_t type, uint32_t optx) { - ir_insn *insn, *prev_insn; + ir_insn *insn; ir_ref ref, prev; + uintptr_t hash; if (type == IR_BOOL) { return val.u64 ? IR_TRUE : IR_FALSE; } else if (type == IR_ADDR && val.u64 == 0) { return IR_NULL; } - prev_insn = NULL; - ref = ctx->prev_const_chain[type]; + + hash = ir_const_hash(val, optx) & ctx->const_hash_mask; + ref = ctx->const_hash[hash]; while (ref) { insn = &ctx->ir_base[ref]; - if (UNEXPECTED(insn->val.u64 >= val.u64)) { - if (insn->val.u64 == val.u64) { - if (insn->optx == optx) { - return ref; - } - } else { - break; - } + if (insn->val.u64 == val.u64 && insn->optx == optx) { + return ref; } - prev_insn = insn; ref = insn->prev_const; } - if (prev_insn) { - prev = prev_insn->prev_const; - prev_insn->prev_const = -ctx->consts_count; - } else { - prev = ctx->prev_const_chain[type]; - ctx->prev_const_chain[type] = -ctx->consts_count; + if ((uintptr_t)ctx->consts_count > ctx->const_hash_mask) { + ir_const_hash_rehash(ctx); + hash = ir_const_hash(val, optx) & ctx->const_hash_mask; } + prev = ctx->const_hash[hash]; + ctx->const_hash[hash] = -ctx->consts_count; + ref = ir_next_const(ctx); insn = &ctx->ir_base[ref]; insn->prev_const = prev; @@ -624,7 +627,7 @@ ir_ref ir_const_bool(ir_ctx *ctx, bool c) ir_ref ir_const_char(ir_ctx *ctx, char c) { ir_val val; - val.i64 = c; + val.i64 = (signed char)c; return ir_const(ctx, val, IR_CHAR); } @@ -2092,10 +2095,10 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_vload_i(ir_ctx *ctx, ir_ref ref, ir_typ if (insn->type == type) { return ref; /* load forwarding (L2L) */ } else if (ir_type_size[insn->type] == ir_type_size[type]) { - return ir_fold1(ctx, IR_OPT(IR_BITCAST, type), ref); /* load forwarding with bitcast (L2L) */ + return ref; /* load forwarding with bitcast (L2L) */ } else if (ir_type_size[insn->type] > ir_type_size[type] && IR_IS_TYPE_INT(type) && IR_IS_TYPE_INT(insn->type)) { - return ir_fold1(ctx, IR_OPT(IR_TRUNC, type), ref); /* partial load forwarding (L2L) */ + return ref; /* partial load forwarding (L2L) */ } } } else if (insn->op == IR_VSTORE) { @@ -2105,10 +2108,10 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_vload_i(ir_ctx *ctx, ir_ref ref, ir_typ if (type2 == type) { return insn->op3; /* store forwarding (S2L) */ } else if (ir_type_size[type2] == ir_type_size[type]) { - return ir_fold1(ctx, IR_OPT(IR_BITCAST, type), insn->op3); /* store forwarding with bitcast (S2L) */ + return insn->op3; /* store forwarding with bitcast (S2L) */ } else if (ir_type_size[type2] > ir_type_size[type] && IR_IS_TYPE_INT(type) && IR_IS_TYPE_INT(type2)) { - return ir_fold1(ctx, IR_OPT(IR_TRUNC, type), insn->op3); /* partial store forwarding (S2L) */ + return insn->op3; /* partial store forwarding (S2L) */ } else { break; } @@ -3214,6 +3217,13 @@ ir_ref _ir_VA_ARG(ir_ctx *ctx, ir_type type, ir_ref list) return ctx->control = ir_emit2(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list); } +ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size) +{ + IR_ASSERT(ctx->control); + IR_ASSERT(size <= 0x7fffffff); + return ctx->control = ir_emit3(ctx, IR_OPT(IR_VA_ARG, type), ctx->control, list, (ir_ref)size); +} + ir_ref _ir_BLOCK_BEGIN(ir_ctx *ctx) { IR_ASSERT(ctx->control); diff --git a/ext/opcache/jit/ir/ir.h b/ext/opcache/jit/ir/ir.h index 9575348ff5450..52cbc06b153ee 100644 --- a/ext/opcache/jit/ir/ir.h +++ b/ext/opcache/jit/ir/ir.h @@ -310,6 +310,8 @@ typedef enum _ir_type { _(PHI, pN, reg, def, def) /* SSA Phi function */ \ _(COPY, d1X1, def, opt, ___) /* COPY (last foldable op) */ \ _(PI, p2, reg, def, ___) /* e-SSA Pi constraint ??? */ \ + _(ARGVAL, d1X2, def, num, num) /* pass struct arg by value */ \ + /* (op2 - size, op3 - align) */ \ /* (USE, RENAME) */ \ \ /* data ops */ \ @@ -343,7 +345,8 @@ typedef enum _ir_type { _(VA_START, x2, src, def, ___) /* va_start(va_list) */ \ _(VA_END, x2, src, def, ___) /* va_end(va_list) */ \ _(VA_COPY, x3, src, def, def) /* va_copy(dst, stc) */ \ - _(VA_ARG, x2, src, def, ___) /* va_arg(va_list) */ \ + _(VA_ARG, x2X1, src, def, opt) /* va_arg(va_list) */ \ + /* op3 - (size<<3)+log2(align) */ \ \ /* guards */ \ _(GUARD, c3, src, def, def) /* IF without second successor */ \ @@ -583,12 +586,22 @@ typedef struct _ir_code_buffer { void *pos; } ir_code_buffer; +typedef struct { + int size; + int align; + int offset; +} ir_value_param; + +#define IR_CONST_HASH_SIZE 64 + struct _ir_ctx { ir_insn *ir_base; /* two directional array - instructions grow down, constants grow up */ ir_ref insns_count; /* number of instructions stored in instructions buffer */ ir_ref insns_limit; /* size of allocated instructions buffer (it's extended when overflow) */ ir_ref consts_count; /* number of constants stored in constants buffer */ ir_ref consts_limit; /* size of allocated constants buffer (it's extended when overflow) */ + uintptr_t const_hash_mask; + ir_ref *const_hash; uint32_t flags; /* IR context flags (see IR_* defines above) */ uint32_t flags2; /* IR context private flags (see IR_* defines in ir_private.h) */ ir_type ret_type; /* Function return type */ @@ -596,6 +609,7 @@ struct _ir_ctx { int32_t status; /* non-zero error code (see IR_ERROR_... macros), app may use negative codes */ ir_ref fold_cse_limit; /* CSE finds identical insns backward from "insn_count" to "fold_cse_limit" */ ir_insn fold_insn; /* temporary storage for folding engine */ + ir_value_param *value_params; /* information about "by-val" struct parameters */ ir_hashtab *binding; ir_use_list *use_lists; /* def->use lists for each instruction */ ir_ref *use_edges; /* the actual uses: use = ctx->use_edges[ctx->use_lists[def].refs + n] */ @@ -655,7 +669,7 @@ struct _ir_ctx { ir_loader *loader; ir_strtab strtab; ir_ref prev_insn_chain[IR_LAST_FOLDABLE_OP + 1]; - ir_ref prev_const_chain[IR_LAST_TYPE]; + ir_ref _const_hash[IR_CONST_HASH_SIZE]; }; /* Basic IR Construction API (implementation in ir.c) */ @@ -896,6 +910,7 @@ int ir_load_llvm_asm(ir_loader *loader, const char *filename); #define IR_SAVE_SAFE_NAMES (1<<5) /* add '@' prefix to symbol names */ void ir_print_proto(const ir_ctx *ctx, ir_ref proto, FILE *f); +void ir_print_proto_ex(uint8_t flags, ir_type ret_type, uint32_t params_count, const uint8_t *param_types, FILE *f); void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f); /* IR debug dump API (implementation in ir_dump.c) */ diff --git a/ext/opcache/jit/ir/ir_aarch64.dasc b/ext/opcache/jit/ir/ir_aarch64.dasc index 476529555e93e..2caabb3ebebbb 100644 --- a/ext/opcache/jit/ir/ir_aarch64.dasc +++ b/ext/opcache/jit/ir/ir_aarch64.dasc @@ -397,6 +397,15 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co n++; } break; + case IR_INT2FP: + case IR_FP2INT: + insn = &ctx->ir_base[ref]; + n = 0; + if (IR_IS_CONST_REF(insn->op1)) { + constraints->tmp_regs[n] = IR_TMP_REG(1, ctx->ir_base[insn->op1].type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); + n++; + } + break; case IR_MUL_PWR2: case IR_DIV_PWR2: case IR_MOD_PWR2: @@ -404,8 +413,6 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co case IR_SHIFT_CONST: case IR_OP_INT: case IR_OP_FP: - case IR_INT2FP: - case IR_FP2INT: case IR_FP2FP: insn = &ctx->ir_base[ref]; n = 0; @@ -567,6 +574,10 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co constraints->tmp_regs[n] = IR_TMP_REG(3, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n++; break; + case IR_ARGVAL: + constraints->tmp_regs[0] = IR_SCRATCH_REG(IR_REG_SCRATCH, IR_DEF_SUB_REF - IR_SUB_REFS_COUNT, IR_USE_SUB_REF); + n = 1; + break; case IR_CALL: insn = &ctx->ir_base[ref]; constraints->def_reg = (IR_IS_TYPE_INT(insn->type)) ? IR_REG_INT_RET1 : IR_REG_FP_RET1; @@ -1096,6 +1107,8 @@ binop_fp: } } return IR_SKIPPED | IR_NOP; + case IR_ARGVAL: + return IR_FUSED | IR_ARGVAL; case IR_NOP: return IR_SKIPPED | IR_NOP; default: @@ -1379,6 +1392,12 @@ static void ir_emit_load_mem(ir_ctx *ctx, ir_type type, ir_reg reg, ir_mem mem) } } +static int32_t ir_local_offset(ir_ctx *ctx, ir_insn *insn) +{ + IR_ASSERT(insn->op == IR_VAR || insn->op == IR_ALLOCA || insn->op == IR_VADDR); + return IR_SPILL_POS_TO_OFFSET(insn->op3); +} + static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) { ir_backend_data *data = ctx->data; @@ -1392,13 +1411,12 @@ static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) if (var_insn->op == IR_VADDR) { var_insn = &ctx->ir_base[var_insn->op1]; } - IR_ASSERT(var_insn->op == IR_VAR || var_insn->op == IR_ALLOCA); - offset = IR_SPILL_POS_TO_OFFSET(var_insn->op3); + offset = ir_local_offset(ctx, var_insn); if (aarch64_may_encode_imm12(offset)) { | add Rx(reg), Rx(base), #offset } else { ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); - | add sp, sp, Rx(IR_REG_INT_TMP) + | add Rx(reg), sp, Rx(IR_REG_INT_TMP) } } @@ -1587,19 +1605,31 @@ static void ir_emit_prologue(ir_ctx *ctx) offset = -(ctx->stack_frame_size+16); if (aarch64_may_encode_imm7_addr_offset(offset, 8)) { | stp x29, x30, [sp, #offset]! - } else { + } else if (aarch64_may_encode_imm12(ctx->stack_frame_size+16)) { | sub sp, sp, #(ctx->stack_frame_size+16) | stp x29, x30, [sp] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->stack_frame_size+16); + | sub sp, sp, Rx(IR_REG_INT_TMP) + | stp x29, x30, [sp] } | mov x29, sp if (ctx->call_stack_size) { - | sub sp, sp, #(ctx->call_stack_size) + if (aarch64_may_encode_imm12(ctx->call_stack_size)) { + | sub sp, sp, #(ctx->call_stack_size) + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->call_stack_size); + | sub sp, sp, Rx(IR_REG_INT_TMP) + } } } else if (ctx->stack_frame_size + ctx->call_stack_size) { if (ctx->fixed_stack_red_zone) { IR_ASSERT(ctx->stack_frame_size + ctx->call_stack_size <= ctx->fixed_stack_red_zone); + } else if (aarch64_may_encode_imm12(ctx->stack_frame_size + ctx->call_stack_size)) { + | sub sp, sp, #(ctx->stack_frame_size + ctx->call_stack_size) } else { - | sub sp, sp, #(ctx->stack_frame_size + ctx->call_stack_size) + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->stack_frame_size + ctx->call_stack_size); + | sub sp, sp, Rx(IR_REG_INT_TMP) } } if (ctx->used_preserved_regs) { @@ -1623,26 +1653,41 @@ static void ir_emit_prologue(ir_ctx *ctx) offset -= sizeof(void*) * 2; if (aarch64_may_encode_imm7_addr_offset(offset, 8)) { | stp Rx(prev), Rx(i), [Rx(fp), #offset] - } else { - IR_ASSERT(aarch64_may_encode_addr_offset(offset, 8)); + } else if (aarch64_may_encode_addr_offset(offset + 8, 8)) { | str Rx(prev), [Rx(fp), #offset] | str Rx(i), [Rx(fp), #(offset+8)] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + | add Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 + | str Rx(i), [Rx(fp), Rx(IR_REG_INT_TMP)] } prev = IR_REG_NONE; } else { if (prev < IR_REG_FP_FIRST) { offset -= sizeof(void*); - | str Rx(prev), [Rx(fp), #offset] - offset -= sizeof(void*); - | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | str Rx(prev), [Rx(fp), #offset] + offset -= sizeof(void*); + | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + | sub Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 + | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } else { offset -= sizeof(void*) * 2; if (aarch64_may_encode_imm7_addr_offset(offset, 8)) { | stp Rd(prev-IR_REG_FP_FIRST), Rd(i-IR_REG_FP_FIRST), [Rx(fp), #offset] - } else { - IR_ASSERT(aarch64_may_encode_addr_offset(offset, 8)); + } else if (aarch64_may_encode_addr_offset(offset + 8, 8)) { | str Rd(prev-IR_REG_FP_FIRST), [Rx(fp), #offset] | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), #(offset+8)] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rd(prev-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] + | add Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 + | str Rd(i-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] } } prev = IR_REG_NONE; @@ -1652,10 +1697,20 @@ static void ir_emit_prologue(ir_ctx *ctx) if (prev != IR_REG_NONE) { if (prev < IR_REG_FP_FIRST) { offset -= sizeof(void*); - | str Rx(prev), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | str Rx(prev), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } else { offset -= sizeof(void*); - | str Rd(prev-IR_REG_FP_FIRST), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset, 8)) { + | str Rd(prev-IR_REG_FP_FIRST), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rd(prev-IR_REG_FP_FIRST), [Rx(fp), Rx(IR_REG_INT_TMP)] + } } } } @@ -1685,10 +1740,14 @@ static void ir_emit_prologue(ir_ctx *ctx) if (prev != IR_REG_NONE) { if (aarch64_may_encode_imm7_addr_offset(offset, 8)) { | stp Rx(prev), Rx(int_reg_params[i]), [Rx(fp), #offset] - } else { - IR_ASSERT(aarch64_may_encode_addr_offset(offset, 8)); + } else if (aarch64_may_encode_addr_offset(offset + 8, 8)) { | str Rx(prev), [Rx(fp), #offset] | str Rx(int_reg_params[i]), [Rx(fp), #(offset+8)] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + | add Rx(IR_REG_INT_TMP), Rx(IR_REG_INT_TMP), #8 + | str Rx(int_reg_params[i]), [Rx(fp), Rx(IR_REG_INT_TMP)] } prev = IR_REG_NONE; offset += sizeof(void*) * 2; @@ -1697,7 +1756,12 @@ static void ir_emit_prologue(ir_ctx *ctx) } } if (prev != IR_REG_NONE) { - | str Rx(prev), [Rx(fp), #offset] + if (aarch64_may_encode_addr_offset(offset + 8, 8)) { + | str Rx(prev), [Rx(fp), #offset] + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, offset); + | str Rx(prev), [Rx(fp), Rx(IR_REG_INT_TMP)] + } offset += sizeof(void*); } } @@ -1782,15 +1846,22 @@ static void ir_emit_epilogue(ir_ctx *ctx) } if (aarch64_may_encode_imm7_addr_offset(ctx->stack_frame_size+16, 8)) { | ldp x29, x30, [sp], #(ctx->stack_frame_size+16) - } else { + } else if (aarch64_may_encode_imm12(ctx->stack_frame_size+16)) { | ldp x29, x30, [sp] | add sp, sp, #(ctx->stack_frame_size+16) + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->stack_frame_size+16); + | ldp x29, x30, [sp] + | add sp, sp, Rx(IR_REG_INT_TMP) } } else if (ctx->stack_frame_size + ctx->call_stack_size) { if (ctx->fixed_stack_red_zone) { IR_ASSERT(ctx->stack_frame_size + ctx->call_stack_size <= ctx->fixed_stack_red_zone); - } else { + } else if (aarch64_may_encode_imm12(ctx->stack_frame_size + ctx->call_stack_size)) { | add sp, sp, #(ctx->stack_frame_size + ctx->call_stack_size) + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, ctx->stack_frame_size + ctx->call_stack_size); + | add sp, sp, Rx(IR_REG_INT_TMP) } } } @@ -3798,7 +3869,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) IR_ASSERT(!IR_IS_SYM_CONST(ctx->ir_base[addr_insn->op2].op)); if (ir_rule(ctx, addr_insn->op1) == IR_STATIC_ALLOCA) { reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[addr_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[addr_insn->op1]); offset += ctx->ir_base[addr_insn->op2].val.i32; return IR_MEM_BO(reg, offset); } else { @@ -3816,7 +3887,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) } else { IR_ASSERT(addr_insn->op == IR_ALLOCA || addr_insn->op == IR_VADDR); reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[ref].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[ref]); return IR_MEM_BO(reg, offset); } } @@ -4205,7 +4276,7 @@ static void ir_emit_va_start(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (ctx->flags & IR_USE_FRAME_POINTER) { @@ -4237,7 +4308,7 @@ static void ir_emit_va_start(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (ctx->flags & IR_USE_FRAME_POINTER) { @@ -4304,7 +4375,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op2_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + op2_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (op3_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op3_reg)) { @@ -4315,7 +4386,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op3) == IR_STATIC_ALLOCA); op3_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op3_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op3].op3); + op3_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op3]); } | ldr Rx(tmp_reg), [Rx(op3_reg), #op3_offset] | str Rx(tmp_reg), [Rx(op2_reg), #op2_offset] @@ -4337,7 +4408,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op2_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + op2_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (op3_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op3_reg)) { @@ -4348,7 +4419,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op3) == IR_STATIC_ALLOCA); op3_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op3_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op3].op3); + op3_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op3]); } | ldr Rx(tmp_reg), [Rx(op3_reg), #op3_offset] | str Rx(tmp_reg), [Rx(op2_reg), #op2_offset] @@ -4386,7 +4457,7 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } | ldr Rx(tmp_reg), [Rx(op2_reg), #offset] ir_emit_load_mem(ctx, type, def_reg, IR_MEM_BO(tmp_reg, 0)); @@ -4418,7 +4489,7 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (IR_IS_TYPE_INT(type)) { | ldr Rw(tmp_reg), [Rx(op2_reg), #(offset+offsetof(ir_va_list, gr_offset))] @@ -4681,7 +4752,7 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) } } -static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) +static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn, int32_t *copy_stack_ptr) { int j, n; ir_type type; @@ -4689,7 +4760,7 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) int fp_param = 0; int int_reg_params_count = IR_REG_INT_ARGS; int fp_reg_params_count = IR_REG_FP_ARGS; - int32_t used_stack = 0; + int32_t used_stack = 0, copy_stack = 0; #ifdef __APPLE__ const ir_proto_t *proto = ir_call_proto(ctx, insn); int last_named_input = (proto && (proto->flags & IR_VARARG_FUNC)) ? proto->params_count + 2 : insn->inputs_count; @@ -4697,7 +4768,16 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) n = insn->inputs_count; for (j = 3; j <= n; j++) { - type = ctx->ir_base[ir_insn_op(insn, j)].type; + ir_insn *arg = &ctx->ir_base[ir_insn_op(insn, j)]; + type = arg->type; + if (arg->op == IR_ARGVAL) { + int size = arg->op2; + int align = arg->op3; + copy_stack += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack = IR_ALIGNED_SIZE(copy_stack, align); + type = IR_ADDR; + } #ifdef __APPLE__ if (j > last_named_input) { used_stack += IR_MAX(sizeof(void*), ir_type_size[type]); @@ -4717,7 +4797,9 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) } } - return used_stack; + copy_stack = IR_ALIGNED_SIZE(copy_stack, 16); + *copy_stack_ptr = copy_stack; + return used_stack + copy_stack; } static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg tmp_reg) @@ -4736,7 +4818,7 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg int fp_reg_params_count = IR_REG_FP_ARGS; const int8_t *int_reg_params = _ir_int_reg_params; const int8_t *fp_reg_params = _ir_fp_reg_params; - int32_t used_stack, stack_offset = 0; + int32_t used_stack, copy_stack = 0, stack_offset = 0, copy_stack_offset = 0; ir_copy *copies; bool do_pass3 = 0; /* For temporaries we may use any scratch registers except for registers used for parameters */ @@ -4755,7 +4837,7 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg // TODO: support for preallocated stack used_stack = 0; } else { - used_stack = ir_call_used_stack(ctx, insn); + used_stack = ir_call_used_stack(ctx, insn, ©_stack); /* Stack must be 16 byte aligned */ used_stack = IR_ALIGNED_SIZE(used_stack, 16); if (ctx->fixed_call_stack_size && used_stack <= ctx->fixed_call_stack_size) { @@ -4778,6 +4860,48 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg int last_named_input = (proto && (proto->flags & IR_VARARG_FUNC)) ? proto->params_count + 2 : insn->inputs_count; #endif + if (copy_stack) { + /* Copy struct arguments */ + for (j = 3; j <= n; j++) { + arg = ir_insn_op(insn, j); + src_reg = ir_get_alocated_reg(ctx, def, j); + arg_insn = &ctx->ir_base[arg]; + type = arg_insn->type; + + if (arg_insn->op == IR_ARGVAL) { + /* make a stack copy */ + void *addr = memcpy; + int size = arg_insn->op2; + int align = arg_insn->op3; + + copy_stack_offset += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack_offset = IR_ALIGNED_SIZE(copy_stack_offset, align); + src_reg = ctx->regs[arg][1]; + + | add Rx(IR_REG_INT_ARG1), sp, #(used_stack - copy_stack_offset) + if (src_reg != IR_REG_NONE) { + if (IR_REG_SPILLED(src_reg)) { + src_reg = IR_REG_NUM(src_reg); + ir_emit_load(ctx, IR_ADDR, src_reg, arg_insn->op1); + } + | mov Rx(IR_REG_INT_ARG2), Rx(src_reg) + } else { + ir_emit_load(ctx, IR_ADDR, IR_REG_INT_ARG2, arg_insn->op1); + } + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_ARG3, size); + + if (aarch64_may_use_b(ctx->code_buffer, addr)) { + | bl &addr + } else { + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_INT_TMP, (intptr_t)addr); + | blr Rx(IR_REG_INT_TMP) + } + } + } + copy_stack_offset = 0; + } + /* 1. move all register arguments that should be passed through stack * and collect arguments that should be passed through registers */ copies = ir_mem_malloc((n - 2) * sizeof(ir_copy)); @@ -4786,8 +4910,13 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg src_reg = ir_get_alocated_reg(ctx, def, j); arg_insn = &ctx->ir_base[arg]; type = arg_insn->type; + #ifdef __APPLE__ if (j > last_named_input) { + if (arg_insn->op == IR_ARGVAL) { + do_pass3 = 1; + continue; + } dst_reg = IR_REG_NONE; /* pass argument through stack */ } else #endif @@ -4798,6 +4927,10 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg dst_reg = IR_REG_NONE; /* pass argument through stack */ } int_param++; + if (arg_insn->op == IR_ARGVAL) { + do_pass3 = 1; + continue; + } } else { IR_ASSERT(IR_IS_TYPE_FP(type)); if (fp_param < fp_reg_params_count) { @@ -4808,7 +4941,9 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg fp_param++; } if (dst_reg != IR_REG_NONE) { - if (src_reg == IR_REG_NONE) { + if (IR_IS_CONST_REF(arg) || + src_reg == IR_REG_NONE || + (IR_REG_SPILLED(src_reg) && !IR_REGSET_IN(IR_REGSET_PRESERVED, IR_REG_NUM(src_reg)))) { /* delay CONST->REG and MEM->REG moves to third pass */ do_pass3 = 1; } else { @@ -4852,6 +4987,31 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg src_reg = ir_get_alocated_reg(ctx, def, j); arg_insn = &ctx->ir_base[arg]; type = arg_insn->type; + if (arg_insn->op == IR_ARGVAL) { + /* pass pointer to the copy on stack */ + int size = arg_insn->op2; + int align = arg_insn->op3; + + copy_stack_offset += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack_offset = IR_ALIGNED_SIZE(copy_stack_offset, align); +#ifdef __APPLE__ + if (j > last_named_input) { + | add Rx(tmp_reg), sp, #(used_stack - copy_stack_offset) + ir_emit_store_mem_int(ctx, IR_ADDR, IR_MEM_BO(IR_REG_STACK_POINTER, stack_offset), tmp_reg); + } else +#endif + if (int_param < int_reg_params_count) { + dst_reg = int_reg_params[int_param]; + | add Rx(dst_reg), sp, #(used_stack - copy_stack_offset) + } else { + | add Rx(tmp_reg), sp, #(used_stack - copy_stack_offset) + ir_emit_store_mem_int(ctx, IR_ADDR, IR_MEM_BO(IR_REG_STACK_POINTER, stack_offset), tmp_reg); + stack_offset += sizeof(void*); + } + int_param++; + continue; + } #ifdef __APPLE__ if (j > last_named_input) { dst_reg = IR_REG_NONE; /* pass argument through stack */ @@ -4874,7 +5034,9 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg fp_param++; } if (dst_reg != IR_REG_NONE) { - if (src_reg == IR_REG_NONE) { + if (IR_IS_CONST_REF(arg) || + src_reg == IR_REG_NONE || + (IR_REG_SPILLED(src_reg) && !IR_REGSET_IN(IR_REGSET_PRESERVED, IR_REG_NUM(src_reg)))) { if (IR_IS_CONST_REF(arg) && IR_IS_TYPE_INT(type)) { if (ir_type_size[type] == 1) { type = IR_ADDR; @@ -5473,7 +5635,8 @@ static void ir_emit_load_params(ir_ctx *ctx) int32_t stack_offset = 0; if (ctx->flags & IR_USE_FRAME_POINTER) { - stack_offset = sizeof(void*) * 2; /* skip old frame pointer and return address */ + /* skip old frame pointer and return address */ + stack_offset = sizeof(void*) * 2 + ctx->stack_frame_size + ctx->call_stack_size; } else { stack_offset = ctx->stack_frame_size + ctx->call_stack_size; } @@ -5831,7 +5994,8 @@ static void ir_preallocate_call_stack(ir_ctx *ctx) for (i = 1, insn = ctx->ir_base + 1; i < ctx->insns_count;) { if (insn->op == IR_CALL) { - call_stack_size = ir_call_used_stack(ctx, insn); + int32_t copy_stack; + call_stack_size = ir_call_used_stack(ctx, insn, ©_stack); if (call_stack_size > peak_call_stack_size) { peak_call_stack_size = call_stack_size; } diff --git a/ext/opcache/jit/ir/ir_aarch64.h b/ext/opcache/jit/ir/ir_aarch64.h index 183d3ec2e1d88..9da64b9249f72 100644 --- a/ext/opcache/jit/ir/ir_aarch64.h +++ b/ext/opcache/jit/ir/ir_aarch64.h @@ -174,8 +174,8 @@ typedef struct _ir_tmp_reg { int8_t reg; }; uint8_t type; - uint8_t start; - uint8_t end; + int8_t start; + int8_t end; } ir_tmp_reg; struct _ir_target_constraints { diff --git a/ext/opcache/jit/ir/ir_builder.h b/ext/opcache/jit/ir/ir_builder.h index 358ae241e2dd4..c1dcffdbaa084 100644 --- a/ext/opcache/jit/ir/ir_builder.h +++ b/ext/opcache/jit/ir/ir_builder.h @@ -586,6 +586,7 @@ extern "C" { #define ir_VA_END(_list) _ir_VA_END(_ir_CTX, _list) #define ir_VA_COPY(_dst, _src) _ir_VA_COPY(_ir_CTX, _dst, _src) #define ir_VA_ARG(_list, _type) _ir_VA_ARG(_ir_CTX, _type, _list) +#define ir_VA_ARG_EX(_list, _type, size) _ir_VA_ARG_EX(_ir_CTX, _type, _list, size) #define ir_START() _ir_START(_ir_CTX) #define ir_ENTRY(_src, _num) _ir_ENTRY(_ir_CTX, (_src), (_num)) @@ -661,6 +662,7 @@ void _ir_VA_START(ir_ctx *ctx, ir_ref list); void _ir_VA_END(ir_ctx *ctx, ir_ref list); void _ir_VA_COPY(ir_ctx *ctx, ir_ref dst, ir_ref src); ir_ref _ir_VA_ARG(ir_ctx *ctx, ir_type type, ir_ref list); +ir_ref _ir_VA_ARG_EX(ir_ctx *ctx, ir_type type, ir_ref list, size_t size); void _ir_START(ir_ctx *ctx); void _ir_ENTRY(ir_ctx *ctx, ir_ref src, ir_ref num); void _ir_BEGIN(ir_ctx *ctx, ir_ref src); diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c index 13d66a7130283..00923387bb21c 100644 --- a/ext/opcache/jit/ir/ir_cfg.c +++ b/ext/opcache/jit/ir/ir_cfg.c @@ -605,7 +605,7 @@ static void compute_postnum(const ir_ctx *ctx, uint32_t *cur, uint32_t b) /* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by * Cooper, Harvey and Kennedy. */ -static int ir_build_dominators_tree_slow(ir_ctx *ctx) +static IR_NEVER_INLINE int ir_build_dominators_tree_slow(ir_ctx *ctx) { uint32_t blocks_count, b, postnum; ir_block *blocks, *bb; @@ -690,28 +690,13 @@ static int ir_build_dominators_tree_slow(ir_ctx *ctx) /* Build dominators tree */ blocks[1].idom = 0; blocks[1].dom_depth = 0; - for (b = 2, bb = &blocks[2]; b <= blocks_count; b++, bb++) { - uint32_t idom = bb->idom; - ir_block *idom_bb = &blocks[idom]; + /* Construct children lists sorted by block number */ + for (b = blocks_count, bb = &blocks[b]; b >= 2; b--, bb--) { + ir_block *idom_bb = &blocks[bb->idom]; bb->dom_depth = 0; - /* Sort by block number to traverse children in pre-order */ - if (idom_bb->dom_child == 0) { - idom_bb->dom_child = b; - } else if (b < idom_bb->dom_child) { - bb->dom_next_child = idom_bb->dom_child; - idom_bb->dom_child = b; - } else { - int child = idom_bb->dom_child; - ir_block *child_bb = &blocks[child]; - - while (child_bb->dom_next_child > 0 && b > child_bb->dom_next_child) { - child = child_bb->dom_next_child; - child_bb = &blocks[child]; - } - bb->dom_next_child = child_bb->dom_next_child; - child_bb->dom_next_child = b; - } + bb->dom_next_child = idom_bb->dom_child; + idom_bb->dom_child = b; } /* Recalculate dom_depth for all blocks */ @@ -769,6 +754,7 @@ int ir_build_dominators_tree(ir_ctx *ctx) ctx->flags2 &= ~IR_NO_LOOPS; // IR_ASSERT(k > 1 && "Wrong blocks order: BB is before its single predecessor"); if (UNEXPECTED(k <= 1)) { +slow_case: ir_list_free(&worklist); return ir_build_dominators_tree_slow(ctx); } @@ -780,7 +766,9 @@ int ir_build_dominators_tree(ir_ctx *ctx) if (idom < b) { break; } - IR_ASSERT(k > 0); + if (UNEXPECTED(k == 0)) { + goto slow_case; + } ir_list_push(&worklist, idom); } } @@ -808,25 +796,14 @@ int ir_build_dominators_tree(ir_ctx *ctx) } bb->idom = idom; idom_bb = &blocks[idom]; - bb->dom_depth = idom_bb->dom_depth + 1; - /* Sort by block number to traverse children in pre-order */ - if (idom_bb->dom_child == 0) { - idom_bb->dom_child = b; - } else if (b < idom_bb->dom_child) { - bb->dom_next_child = idom_bb->dom_child; - idom_bb->dom_child = b; - } else { - int child = idom_bb->dom_child; - ir_block *child_bb = &blocks[child]; + } - while (child_bb->dom_next_child > 0 && b > child_bb->dom_next_child) { - child = child_bb->dom_next_child; - child_bb = &blocks[child]; - } - bb->dom_next_child = child_bb->dom_next_child; - child_bb->dom_next_child = b; - } + /* Construct children lists sorted by block number */ + for (b = blocks_count, bb = &blocks[b]; b >= 2; b--, bb--) { + ir_block *idom_bb = &blocks[bb->idom]; + bb->dom_next_child = idom_bb->dom_child; + idom_bb->dom_child = b; } blocks[1].idom = 0; @@ -945,23 +922,13 @@ static int ir_build_dominators_tree_iterative(ir_ctx *ctx) ir_block *idom_bb = &blocks[idom]; bb->dom_depth = idom_bb->dom_depth + 1; - /* Sort by block number to traverse children in pre-order */ - if (idom_bb->dom_child == 0) { - idom_bb->dom_child = b; - } else if (b < idom_bb->dom_child) { - bb->dom_next_child = idom_bb->dom_child; - idom_bb->dom_child = b; - } else { - int child = idom_bb->dom_child; - ir_block *child_bb = &blocks[child]; + } - while (child_bb->dom_next_child > 0 && b > child_bb->dom_next_child) { - child = child_bb->dom_next_child; - child_bb = &blocks[child]; - } - bb->dom_next_child = child_bb->dom_next_child; - child_bb->dom_next_child = b; - } + /* Construct children lists sorted by block number */ + for (b = blocks_count, bb = &blocks[b]; b >= 2; b--, bb--) { + ir_block *idom_bb = &blocks[bb->idom]; + bb->dom_next_child = idom_bb->dom_child; + idom_bb->dom_child = b; } return 1; diff --git a/ext/opcache/jit/ir/ir_dump.c b/ext/opcache/jit/ir/ir_dump.c index 54fddf50ac066..a501d261f30a7 100644 --- a/ext/opcache/jit/ir/ir_dump.c +++ b/ext/opcache/jit/ir/ir_dump.c @@ -660,6 +660,12 @@ void ir_dump_codegen(const ir_ctx *ctx, FILE *f) } if (first) { fprintf(f, ";"); + } else if (ctx->value_params + && insn->op == IR_PARAM + && ctx->value_params[insn->op3 - 1].align) { + fprintf(f, ") ByVal(%d, %d);", + ctx->value_params[insn->op3 - 1].size, + ctx->value_params[insn->op3 - 1].align); } else { fprintf(f, ");"); } diff --git a/ext/opcache/jit/ir/ir_emit.c b/ext/opcache/jit/ir/ir_emit.c index fab9f56228d80..c9e65229c3969 100644 --- a/ext/opcache/jit/ir/ir_emit.c +++ b/ext/opcache/jit/ir/ir_emit.c @@ -167,11 +167,24 @@ static ir_reg ir_get_param_reg(const ir_ctx *ctx, ir_ref ref) if (insn->op == IR_PARAM) { if (IR_IS_TYPE_INT(insn->type)) { if (use == ref) { +#if defined(IR_TARGET_X64) || defined(IR_TARGET_X86) + if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { + /* struct passed by value on stack */ + return IR_REG_NONE; + } else +#endif if (int_param < int_reg_params_count) { return int_reg_params[int_param]; } else { return IR_REG_NONE; } +#if defined(IR_TARGET_X64) || defined(IR_TARGET_X86) + } else { + if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { + /* struct passed by value on stack */ + continue; + } +#endif } int_param++; #ifdef _WIN64 @@ -222,9 +235,12 @@ static int ir_get_args_regs(const ir_ctx *ctx, const ir_insn *insn, int8_t *regs n = insn->inputs_count; n = IR_MIN(n, IR_MAX_REG_ARGS + 2); for (j = 3; j <= n; j++) { - type = ctx->ir_base[ir_insn_op(insn, j)].type; + ir_insn *arg = &ctx->ir_base[ir_insn_op(insn, j)]; + type = arg->type; if (IR_IS_TYPE_INT(type)) { - if (int_param < int_reg_params_count) { + if (arg->op == IR_ARGVAL) { + continue; + } else if (int_param < int_reg_params_count) { regs[j] = int_reg_params[int_param]; count = j + 1; } else { diff --git a/ext/opcache/jit/ir/ir_fold.h b/ext/opcache/jit/ir/ir_fold.h index 2f5be6ca2e00b..7ae6ca539da8d 100644 --- a/ext/opcache/jit/ir/ir_fold.h +++ b/ext/opcache/jit/ir/ir_fold.h @@ -1703,6 +1703,11 @@ IR_FOLD(SUB_OV(_, C_ADDR)) { if (op2_insn->val.u64 == 0) { /* a +/- 0 => a */ + if (op1_insn->type != IR_OPT_TYPE(opt)) { + opt = IR_BITCAST | (opt & IR_OPT_TYPE_MASK); + op2 = IR_UNUSED; + IR_FOLD_RESTART; + } IR_FOLD_COPY(op1); } IR_FOLD_NEXT; @@ -1721,6 +1726,12 @@ IR_FOLD(ADD(C_ADDR, _)) { if (op1_insn->val.u64 == 0) { /* 0 + a => a */ + if (op2_insn->type != IR_OPT_TYPE(opt)) { + opt = IR_BITCAST | (opt & IR_OPT_TYPE_MASK); + op1 = op2; + op2 = IR_UNUSED; + IR_FOLD_RESTART; + } IR_FOLD_COPY(op2); } IR_FOLD_NEXT; @@ -2927,7 +2938,7 @@ IR_FOLD(SUB(C_ADDR, SUB)) /* c1 - (x - c2) => (c1 + c2) - x */ val.u64 = op1_insn->val.u64 + ctx->ir_base[op2_insn->op2].val.u64; op2 = op2_insn->op1; - op1 = ir_const(ctx, val, op1_insn->op1); + op1 = ir_const(ctx, val, op1_insn->type); IR_FOLD_RESTART; } else if (IR_IS_CONST_REF(op2_insn->op1) && !IR_IS_SYM_CONST(ctx->ir_base[op2_insn->op1].op)) { /* c1 - (c2 - x) => x + (c1 - c2) */ diff --git a/ext/opcache/jit/ir/ir_gcm.c b/ext/opcache/jit/ir/ir_gcm.c index 3ea78850249ab..c170fa47476ec 100644 --- a/ext/opcache/jit/ir/ir_gcm.c +++ b/ext/opcache/jit/ir/ir_gcm.c @@ -792,21 +792,26 @@ IR_ALWAYS_INLINE bool ir_is_good_bb_order(ir_ctx *ctx, uint32_t b, ir_block *bb, ir_ref *p = insn->ops + 1; if (n == 1) { - return *p < start; + return ctx->cfg_map[*p] < b; } else { IR_ASSERT(n > 1); for (; n > 0; p++, n--) { ir_ref input = *p; - if (input < start) { - /* ordered */ - } else if ((bb->flags & IR_BB_LOOP_HEADER) - && (ctx->cfg_map[input] == b || ctx->cfg_blocks[ctx->cfg_map[input]].loop_header == b)) { - /* back-edge of reducible loop */ - } else if ((bb->flags & IR_BB_IRREDUCIBLE_LOOP) - && (ctx->cfg_blocks[ctx->cfg_map[input]].loop_header == ctx->cfg_blocks[b].loop_header)) { - /* closing edge of irreducible loop */ - } else { - return 0; + + if (!IR_IS_CONST_REF(input)) { + uint32_t input_b = ctx->cfg_map[input]; + + if (input_b < b) { + /* ordered */ + } else if ((bb->flags & IR_BB_LOOP_HEADER) + && (input_b == b || ctx->cfg_blocks[input_b].loop_header == b)) { + /* back-edge of reducible loop */ + } else if ((bb->flags & IR_BB_IRREDUCIBLE_LOOP) + && (ctx->cfg_blocks[input_b].loop_header == bb->loop_header)) { + /* closing edge of irreducible loop */ + } else { + return 0; + } } } return 1; @@ -925,121 +930,54 @@ int ir_schedule(ir_ctx *ctx) ir_ref *_xlat; ir_ref *edges; ir_ref prev_b_end; - uint32_t b, prev_b; + uint32_t b; uint32_t *_blocks = ctx->cfg_map; ir_ref *_next = ir_mem_malloc(ctx->insns_count * sizeof(ir_ref)); ir_ref *_prev = ir_mem_malloc(ctx->insns_count * sizeof(ir_ref)); - ir_ref _move_down = 0; ir_block *bb; ir_insn *insn, *new_insn; ir_use_list *lists, *use_list, *new_list; bool bad_bb_order = 0; + /* Create a double-linked list of nodes ordered by BB, respecting BB->start and BB->end */ IR_ASSERT(_blocks[1] == 1); - prev_b = 1; - prev_b_end = ctx->cfg_blocks[1].end; + + /* link BB boundaries */ _prev[1] = 0; - _prev[prev_b_end] = 0; - for (i = 2, j = 1; i < ctx->insns_count; i++) { - b = _blocks[i]; - IR_ASSERT((int32_t)b >= 0); - if (b == prev_b && i <= prev_b_end) { - /* add to the end of the list */ - _next[j] = i; - _prev[i] = j; - j = i; - } else if (b > prev_b) { - bb = &ctx->cfg_blocks[b]; - if (i == bb->start) { - if (bb->end > bb->start) { - prev_b = b; - prev_b_end = bb->end; - /* add to the end of the list */ - _next[j] = i; - _prev[i] = j; - j = i; - } else { - prev_b = 0; - prev_b_end = 0; - k = bb->end; - while (_blocks[_prev[k]] == b) { - k = _prev[k]; - } - /* insert before "k" */ - _prev[i] = _prev[k]; - _next[i] = k; - _next[_prev[k]] = i; - _prev[k] = i; - } - if (!ir_is_good_bb_order(ctx, b, bb, i)) { - bad_bb_order = 1; - } - } else if (i != bb->end) { - /* move down late (see the following loop) */ - _next[i] = _move_down; - _move_down = i; - } else { - prev_b = 0; - prev_b_end = 0; - if (bb->start > bb->end) { - /* add to the end of the list */ - _next[j] = i; - _prev[i] = j; - j = i; - } else { - k = bb->start; - while (_blocks[_next[k]] == b) { - k = _next[k]; - } - /* insert after "k" */ - _next[i] = _next[k]; - _prev[i] = k; - _prev[_next[k]] = i; - _next[k] = i; - } - } - } else if (b) { - bb = &ctx->cfg_blocks[b]; - IR_ASSERT(i != bb->start); - if (i > bb->end) { - /* move up, insert before the end of the already scheduled BB */ - k = bb->end; - } else { - IR_ASSERT(i > bb->start); - /* move up, insert at the end of the block */ - k = ctx->cfg_blocks[b + 1].start; - } - /* insert before "k" */ - _prev[i] = _prev[k]; - _next[i] = k; - _next[_prev[k]] = i; - _prev[k] = i; + prev_b_end = ctx->cfg_blocks[1].end; + _next[1] = prev_b_end; + _prev[prev_b_end] = 1; + for (b = 2, bb = ctx->cfg_blocks + 2; b <= ctx->cfg_blocks_count; b++, bb++) { + _next[prev_b_end] = bb->start; + _prev[bb->start] = prev_b_end; + _next[bb->start] = bb->end; + _prev[bb->end] = bb->start; + prev_b_end = bb->end; + if (!ir_is_good_bb_order(ctx, b, bb, bb->start)) { + bad_bb_order = 1; } } - _next[j] = 0; + _next[prev_b_end] = 0; - while (_move_down) { - i = _move_down; - _move_down = _next[i]; + /* insert intermediate BB nodes */ + for (i = 2, j = 1; i < ctx->insns_count; i++) { b = _blocks[i]; + if (!b) continue; bb = &ctx->cfg_blocks[b]; - k = _next[bb->start]; - - if (bb->flags & (IR_BB_HAS_PHI|IR_BB_HAS_PI|IR_BB_HAS_PARAM|IR_BB_HAS_VAR)) { - /* insert after the start of the block and all PARAM, VAR, PI, PHI */ - insn = &ctx->ir_base[k]; - while (insn->op == IR_PHI || insn->op == IR_PARAM || insn->op == IR_VAR || insn->op == IR_PI) { - k = _next[k]; - insn = &ctx->ir_base[k]; - } + if (i != bb->start && i != bb->end) { + /* insert before "end" */ + ir_ref n = bb->end; + ir_ref p = _prev[n]; + _prev[i] = p; + _next[i] = n; + _next[p] = i; + _prev[n] = i; } + } - /* insert before "k" */ - _prev[i] = _prev[k]; - _next[i] = k; - _next[_prev[k]] = i; - _prev[k] = i; + if (bad_bb_order) { + ir_fix_bb_order(ctx, _prev, _next); } #ifdef IR_DEBUG @@ -1051,10 +989,6 @@ int ir_schedule(ir_ctx *ctx) } #endif - if (bad_bb_order) { - ir_fix_bb_order(ctx, _prev, _next); - } - _xlat = ir_mem_calloc((ctx->consts_count + ctx->insns_count), sizeof(ir_ref)); _xlat += ctx->consts_count; _xlat[IR_TRUE] = IR_TRUE; @@ -1168,7 +1102,11 @@ int ir_schedule(ir_ctx *ctx) if (end->op == IR_IF) { /* Move condition closer to IF */ input = end->op2; - if (input > 0 && _blocks[input] == b && !_xlat[input] && _prev[j] != input) { + if (input > 0 + && _blocks[input] == b + && !_xlat[input] + && _prev[j] != input + && (!(ir_op_flags[ctx->ir_base[input].op] & IR_OP_FLAG_CONTROL) || end->op1 == input)) { if (input == i) { i = _next[i]; insn = &ctx->ir_base[i]; @@ -1188,6 +1126,7 @@ int ir_schedule(ir_ctx *ctx) ir_ref n, j, *p, input; restart: + IR_ASSERT(_blocks[i] == b); n = insn->inputs_count; for (j = n, p = insn->ops + 1; j > 0; p++, j--) { input = *p; @@ -1221,6 +1160,7 @@ int ir_schedule(ir_ctx *ctx) } _xlat[i] = insns_count; insns_count += ir_insn_inputs_to_len(n); + IR_ASSERT(_next[i] != IR_UNUSED); i = _next[i]; insn = &ctx->ir_base[i]; } @@ -1274,6 +1214,7 @@ int ir_schedule(ir_ctx *ctx) new_ctx.insns_count = insns_count; new_ctx.flags2 = ctx->flags2; new_ctx.ret_type = ctx->ret_type; + new_ctx.value_params = ctx->value_params; new_ctx.mflags = ctx->mflags; new_ctx.spill_base = ctx->spill_base; new_ctx.fixed_stack_red_zone = ctx->fixed_stack_red_zone; @@ -1511,6 +1452,7 @@ int ir_schedule(ir_ctx *ctx) new_ctx.cfg_edges = ctx->cfg_edges; ctx->cfg_blocks = NULL; ctx->cfg_edges = NULL; + ctx->value_params = NULL; ir_code_buffer *saved_code_buffer = ctx->code_buffer; ir_free(ctx); diff --git a/ext/opcache/jit/ir/ir_ra.c b/ext/opcache/jit/ir/ir_ra.c index 0c0e8dec3b47a..21c7ee3ac64e5 100644 --- a/ext/opcache/jit/ir/ir_ra.c +++ b/ext/opcache/jit/ir/ir_ra.c @@ -1193,7 +1193,7 @@ static void ir_add_fusion_ranges(ir_ctx *ctx, ir_ref ref, ir_ref input, ir_block n = IR_INPUT_EDGES_COUNT(flags); j = 1; p = insn->ops + j; - if (flags & IR_OP_FLAG_CONTROL) { + if (flags & (IR_OP_FLAG_CONTROL|IR_OP_FLAG_PINNED)) { j++; p++; } @@ -1340,7 +1340,7 @@ int ir_compute_live_ranges(ir_ctx *ctx) || (ctx->rules[ref] & IR_RULE_MASK) == IR_ALLOCA) && ctx->use_lists[ref].count > 0) { insn = &ctx->ir_base[ref]; - if (insn->op != IR_VADDR) { + if (insn->op != IR_VADDR && insn->op != IR_PARAM) { insn->op3 = ctx->vars; ctx->vars = ref; } @@ -1630,6 +1630,10 @@ static void ir_vregs_join(ir_ctx *ctx, uint32_t r1, uint32_t r2) if (ctx->ir_base[IR_LIVE_POS_TO_REF(ctx->live_intervals[r1]->use_pos->pos)].op != IR_VLOAD) { ctx->live_intervals[r1]->flags &= ~IR_LIVE_INTERVAL_MEM_LOAD; } + if (ival->flags & IR_LIVE_INTERVAL_MEM_PARAM) { + IR_ASSERT(!(ctx->live_intervals[r1]->flags & IR_LIVE_INTERVAL_MEM_PARAM)); + ctx->live_intervals[r1]->flags |= IR_LIVE_INTERVAL_MEM_PARAM; + } ctx->live_intervals[r2] = NULL; // TODO: remember to reuse ??? diff --git a/ext/opcache/jit/ir/ir_save.c b/ext/opcache/jit/ir/ir_save.c index ea787f162ec1f..5ba986fadd481 100644 --- a/ext/opcache/jit/ir/ir_save.c +++ b/ext/opcache/jit/ir/ir_save.c @@ -10,31 +10,35 @@ void ir_print_proto(const ir_ctx *ctx, ir_ref func_proto, FILE *f) { - ir_ref j; - if (func_proto) { const ir_proto_t *proto = (const ir_proto_t *)ir_get_str(ctx, func_proto); + ir_print_proto_ex(proto->flags, proto->ret_type, proto->params_count, proto->param_types, f); + } else { + fprintf(f, "(): int32_t"); + } +} - fprintf(f, "("); - if (proto->params_count > 0) { - fprintf(f, "%s", ir_type_cname[proto->param_types[0]]); - for (j = 1; j < proto->params_count; j++) { - fprintf(f, ", %s", ir_type_cname[proto->param_types[j]]); - } - if (proto->flags & IR_VARARG_FUNC) { - fprintf(f, ", ..."); - } - } else if (proto->flags & IR_VARARG_FUNC) { - fprintf(f, "..."); +void ir_print_proto_ex(uint8_t flags, ir_type ret_type, uint32_t params_count, const uint8_t *param_types, FILE *f) +{ + uint32_t j; + + fprintf(f, "("); + if (params_count > 0) { + fprintf(f, "%s", ir_type_cname[param_types[0]]); + for (j = 1; j < params_count; j++) { + fprintf(f, ", %s", ir_type_cname[param_types[j]]); } - fprintf(f, "): %s", ir_type_cname[proto->ret_type]); - if (proto->flags & IR_FASTCALL_FUNC) { - fprintf(f, " __fastcall"); - } else if (proto->flags & IR_BUILTIN_FUNC) { - fprintf(f, " __builtin"); + if (flags & IR_VARARG_FUNC) { + fprintf(f, ", ..."); } - } else { - fprintf(f, "(): int32_t"); + } else if (flags & IR_VARARG_FUNC) { + fprintf(f, "..."); + } + fprintf(f, "): %s", ir_type_cname[ret_type]); + if (flags & IR_FASTCALL_FUNC) { + fprintf(f, " __fastcall"); + } else if (flags & IR_BUILTIN_FUNC) { + fprintf(f, " __builtin"); } } @@ -280,6 +284,12 @@ void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f) } if (first) { fprintf(f, ";"); + } else if (ctx->value_params + && insn->op == IR_PARAM + && ctx->value_params[insn->op3 - 1].align) { + fprintf(f, ") ByVal(%d, %d);", + ctx->value_params[insn->op3 - 1].size, + ctx->value_params[insn->op3 - 1].align); } else { fprintf(f, ");"); } diff --git a/ext/opcache/jit/ir/ir_sccp.c b/ext/opcache/jit/ir/ir_sccp.c index 58de0d726f781..48659cd4bd71b 100644 --- a/ext/opcache/jit/ir/ir_sccp.c +++ b/ext/opcache/jit/ir/ir_sccp.c @@ -1875,6 +1875,7 @@ static ir_ref ir_ext_const(ir_ctx *ctx, ir_insn *val_insn, ir_op op, ir_type typ case IR_I8: case IR_U8: case IR_BOOL: + case IR_CHAR: if (op == IR_SEXT) { new_val.i64 = (int64_t)val_insn->val.i8; } else { @@ -1928,7 +1929,7 @@ static ir_ref ir_ext_ref(ir_ctx *ctx, ir_ref var_ref, ir_ref src_ref, ir_op op, return ref; } -static uint32_t _ir_estimated_control(ir_ctx *ctx, ir_ref val) +static uint32_t _ir_estimated_control(ir_ctx *ctx, ir_ref val, ir_ref loop) { ir_insn *insn; ir_ref n, *p, input, result, ctrl; @@ -1953,7 +1954,8 @@ static uint32_t _ir_estimated_control(ir_ctx *ctx, ir_ref val) result = 1; for (; n > 0; p++, n--) { input = *p; - ctrl = _ir_estimated_control(ctx, input); + ctrl = _ir_estimated_control(ctx, input, loop); + if (ctrl >= loop) return ctrl; if (ctrl > result) { // TODO: check dominance depth instead of order result = ctrl; } @@ -1963,7 +1965,7 @@ static uint32_t _ir_estimated_control(ir_ctx *ctx, ir_ref val) static bool ir_is_loop_invariant(ir_ctx *ctx, ir_ref ref, ir_ref loop) { - ref = _ir_estimated_control(ctx, ref); + ref = _ir_estimated_control(ctx, ref, loop); return ref < loop; // TODO: check dominance instead of order } diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index d9967d24dbea2..781c8e5269c29 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -1387,6 +1387,12 @@ op2_const: constraints->tmp_regs[n] = IR_TMP_REG(3, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n++; break; + case IR_ARGVAL: + constraints->tmp_regs[0] = IR_SCRATCH_REG(IR_REG_RSI, IR_DEF_SUB_REF - IR_SUB_REFS_COUNT, IR_USE_SUB_REF); + constraints->tmp_regs[1] = IR_SCRATCH_REG(IR_REG_RDI, IR_DEF_SUB_REF - IR_SUB_REFS_COUNT, IR_USE_SUB_REF); + constraints->tmp_regs[2] = IR_SCRATCH_REG(IR_REG_RCX, IR_DEF_SUB_REF - IR_SUB_REFS_COUNT, IR_USE_SUB_REF); + n = 3; + break; case IR_CALL: insn = &ctx->ir_base[ref]; if (IR_IS_TYPE_INT(insn->type)) { @@ -2431,6 +2437,11 @@ binop_fp: case IR_VAR: return IR_SKIPPED | IR_VAR; case IR_PARAM: +#ifndef _WIN64 + if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { + return IR_STATIC_ALLOCA; + } +#endif return ctx->use_lists[ref].count > 0 ? IR_PARAM : IR_SKIPPED | IR_PARAM; case IR_ALLOCA: /* alloca() may be used only in functions */ @@ -2976,6 +2987,8 @@ store_int: } } return IR_SKIPPED | IR_NOP; + case IR_ARGVAL: + return IR_FUSED | IR_ARGVAL; case IR_NOP: return IR_SKIPPED | IR_NOP; default: @@ -3153,6 +3166,17 @@ static void ir_emit_load_mem(ir_ctx *ctx, ir_type type, ir_reg reg, ir_mem mem) } } +static int32_t ir_local_offset(ir_ctx *ctx, ir_insn *insn) +{ + if (insn->op != IR_PARAM) { + IR_ASSERT(insn->op == IR_VAR || insn->op == IR_ALLOCA || insn->op == IR_VADDR); + return IR_SPILL_POS_TO_OFFSET(insn->op3); + } else { + IR_ASSERT(ctx->value_params && ctx->value_params[insn->op3 - 1].align); + return IR_SPILL_POS_TO_OFFSET(ctx->value_params[insn->op3 - 1].offset); + } +} + static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) { ir_backend_data *data = ctx->data; @@ -3166,8 +3190,7 @@ static void ir_load_local_addr(ir_ctx *ctx, ir_reg reg, ir_ref src) if (var_insn->op == IR_VADDR) { var_insn = &ctx->ir_base[var_insn->op1]; } - IR_ASSERT(var_insn->op == IR_VAR || var_insn->op == IR_ALLOCA); - offset = IR_SPILL_POS_TO_OFFSET(var_insn->op3); + offset = ir_local_offset(ctx, var_insn); if (offset == 0) { | mov Ra(reg), Ra(base) } else { @@ -3385,7 +3408,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) case IR_LEA_OB: offset_insn = insn; if (ir_rule(ctx, insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; } else { @@ -3407,12 +3430,12 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) break; case IR_LEA_IB: if (ir_rule(ctx, insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = ref * sizeof(ir_ref) + 2; } else if (ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = ref * sizeof(ir_ref) + 1; @@ -3428,12 +3451,12 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) offset_insn = op1_insn; scale = 1; if (ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = insn->op1 * sizeof(ir_ref) + 1; } else if (ir_rule(ctx, op1_insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op1_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op1_insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = ref * sizeof(ir_ref) + 2; @@ -3447,12 +3470,12 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) offset_insn = op2_insn; scale = 1; if (ir_rule(ctx, insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = insn->op2 * sizeof(ir_ref) + 1; } else if (ir_rule(ctx, op2_insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op2_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op2_insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = ref * sizeof(ir_ref) + 1; @@ -3479,12 +3502,12 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) offset_insn = insn; scale = 1; if (ir_rule(ctx, op1_insn->op2) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op1_insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op1_insn->op2]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = insn->op1 * sizeof(ir_ref) + 1; } else if (ir_rule(ctx, op1_insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op1_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op1_insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; index_reg_ref = insn->op1 * sizeof(ir_ref) + 2; @@ -3500,7 +3523,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) op2_insn = &ctx->ir_base[insn->op2]; scale = ctx->ir_base[op2_insn->op2].val.i32; if (ir_rule(ctx, op1_insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op1_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op1_insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; } else { @@ -3514,7 +3537,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) op2_insn = &ctx->ir_base[insn->op2]; offset_insn = op2_insn; if (ir_rule(ctx, op2_insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[op2_insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[op2_insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; } else { @@ -3523,7 +3546,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) break; case IR_LEA_B_SI: if (ir_rule(ctx, insn->op1) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op1].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op1]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; } else { @@ -3537,7 +3560,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) case IR_LEA_SI_B: index_reg_ref = insn->op1 * sizeof(ir_ref) + 1; if (ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = IR_UNUSED; } else { @@ -3580,7 +3603,7 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) offset_insn = NULL; break; case IR_ALLOCA: - offset = IR_SPILL_POS_TO_OFFSET(insn->op3); + offset = ir_local_offset(ctx, insn); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; base_reg_ref = index_reg_ref = IR_UNUSED; scale = 1; @@ -8306,7 +8329,7 @@ static void ir_emit_va_start(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (ctx->flags & IR_USE_FRAME_POINTER) { @@ -8340,7 +8363,7 @@ static void ir_emit_va_start(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (ctx->flags & IR_USE_FRAME_POINTER) { @@ -8407,7 +8430,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op2_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + op2_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (op3_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op3_reg)) { @@ -8418,7 +8441,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op3) == IR_STATIC_ALLOCA); op3_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op3_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op3].op3); + op3_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op3]); } | mov Ra(tmp_reg), aword [Ra(op3_reg)+op3_offset] | mov aword [Ra(op2_reg)+op2_offset], Ra(tmp_reg) @@ -8441,7 +8464,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op2_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + op2_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } if (op3_reg != IR_REG_NONE) { if (IR_REG_SPILLED(op3_reg)) { @@ -8452,7 +8475,7 @@ static void ir_emit_va_copy(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op3) == IR_STATIC_ALLOCA); op3_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - op3_offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op3].op3); + op3_offset = ir_local_offset(ctx, &ctx->ir_base[insn->op3]); } | mov Rd(tmp_reg), dword [Ra(op3_reg)+(op3_offset+offsetof(ir_va_list, gp_offset))] | mov dword [Ra(op2_reg)+(op2_offset+offsetof(ir_va_list, gp_offset))], Rd(tmp_reg) @@ -8493,11 +8516,29 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } | mov Ra(tmp_reg), aword [Ra(op2_reg)+offset] +#ifdef _WIN64 ir_emit_load_mem(ctx, type, def_reg, IR_MEM_B(tmp_reg)); | add Ra(tmp_reg), IR_MAX(ir_type_size[type], sizeof(void*)) +#else + if (!insn->op3) { + ir_emit_load_mem(ctx, type, def_reg, IR_MEM_B(tmp_reg)); + | add Ra(tmp_reg), IR_MAX(ir_type_size[type], sizeof(void*)) + } else { + IR_ASSERT(type == IR_ADDR); + int align = 1U << (insn->op3 & 0x7); + int size = (uint32_t)insn->op3 >> 3; + + if (align > (int)sizeof(void*)) { + | add Ra(tmp_reg), (align-1) + | and Ra(tmp_reg), ~(align-1) + } + | mov Ra(def_reg), Ra(tmp_reg) + | add Ra(tmp_reg), IR_ALIGNED_SIZE(size, sizeof(void*)) + } +#endif | mov aword [Ra(op2_reg)+offset], Ra(tmp_reg) if (IR_REG_SPILLED(ctx->regs[def][0])) { ir_emit_store(ctx, type, def, def_reg); @@ -8526,9 +8567,23 @@ static void ir_emit_va_arg(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else { IR_ASSERT(ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA); op2_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; - offset = IR_SPILL_POS_TO_OFFSET(ctx->ir_base[insn->op2].op3); + offset = ir_local_offset(ctx, &ctx->ir_base[insn->op2]); } - if (IR_IS_TYPE_INT(type)) { + if (insn->op3) { + /* long struct arguemnt */ + IR_ASSERT(type == IR_ADDR); + int align = 1U << (insn->op3 & 0x7); + int size = (uint32_t)insn->op3 >> 3; + + | mov Ra(tmp_reg), aword [Ra(op2_reg)+(offset+offsetof(ir_va_list, overflow_arg_area))] + if (align > (int)sizeof(void*)) { + | add Ra(tmp_reg), (align-1) + | and Ra(tmp_reg), ~(align-1) + } + | mov Ra(def_reg), Ra(tmp_reg) + | add Ra(tmp_reg), IR_ALIGNED_SIZE(size, sizeof(void*)) + | mov aword [Ra(op2_reg)+(offset+offsetof(ir_va_list, overflow_arg_area))], Ra(tmp_reg) + } else if (IR_IS_TYPE_INT(type)) { | mov Rd(tmp_reg), dword [Ra(op2_reg)+(offset+offsetof(ir_va_list, gp_offset))] | cmp Rd(tmp_reg), sizeof(void*)*IR_REG_INT_ARGS | jge >1 @@ -8847,7 +8902,7 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) } } -static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) +static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn, int *copy_stack_ptr) { int j, n; ir_type type; @@ -8856,6 +8911,9 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) int int_reg_params_count = IR_REG_INT_ARGS; int fp_reg_params_count = IR_REG_FP_ARGS; int32_t used_stack = 0; +#ifdef _WIN64 + int32_t copy_stack = 0; +#endif #ifdef IR_HAVE_FASTCALL if (sizeof(void*) == 4 && ir_is_fastcall(ctx, insn)) { @@ -8866,8 +8924,26 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) n = insn->inputs_count; for (j = 3; j <= n; j++) { - type = ctx->ir_base[ir_insn_op(insn, j)].type; + ir_insn *arg = &ctx->ir_base[ir_insn_op(insn, j)]; + type = arg->type; if (IR_IS_TYPE_INT(type)) { + if (arg->op == IR_ARGVAL) { + int size = arg->op2; + int align = arg->op3; + +#ifdef _WIN64 + copy_stack += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack = IR_ALIGNED_SIZE(copy_stack, align); + type = IR_ADDR; +#else + align = IR_MAX((int)sizeof(void*), align); + used_stack = IR_ALIGNED_SIZE(used_stack, align); + used_stack += size; + used_stack = IR_ALIGNED_SIZE(used_stack, sizeof(void*)); + continue; +#endif + } if (int_param >= int_reg_params_count) { used_stack += IR_MAX(sizeof(void*), ir_type_size[type]); } @@ -8892,6 +8968,14 @@ static int32_t ir_call_used_stack(ir_ctx *ctx, ir_insn *insn) /* Reserved "home space" or "shadow store" for register arguments (used in Windows64 ABI) */ used_stack += IR_SHADOW_ARGS; +#ifdef _WIN64 + copy_stack = IR_ALIGNED_SIZE(copy_stack, 16); + used_stack += copy_stack; + *copy_stack_ptr = copy_stack; +#else + *copy_stack_ptr = 0; +#endif + return used_stack; } @@ -8911,7 +8995,7 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg int fp_reg_params_count = IR_REG_FP_ARGS; const int8_t *int_reg_params = _ir_int_reg_params; const int8_t *fp_reg_params = _ir_fp_reg_params; - int32_t used_stack, stack_offset = IR_SHADOW_ARGS; + int32_t used_stack, copy_stack = 0, stack_offset = IR_SHADOW_ARGS; ir_copy *copies; bool do_pass3 = 0; /* For temporaries we may use any scratch registers except for registers used for parameters */ @@ -8942,9 +9026,13 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg #endif ) { // TODO: support for preallocated stack +#ifdef _WIN64 + used_stack = ir_call_used_stack(ctx, insn, ©_stack); +#else used_stack = 0; +#endif } else { - used_stack = ir_call_used_stack(ctx, insn); + used_stack = ir_call_used_stack(ctx, insn, ©_stack); if (IR_SHADOW_ARGS && insn->op == IR_TAILCALL && used_stack == IR_SHADOW_ARGS) { @@ -8967,6 +9055,46 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg } } +#ifdef _WIN64 +|.if X64 + if (copy_stack) { + /* Copy struct arguments */ + int copy_stack_offset = 0; + + for (j = 3; j <= n; j++) { + arg = ir_insn_op(insn, j); + src_reg = ir_get_alocated_reg(ctx, def, j); + arg_insn = &ctx->ir_base[arg]; + type = arg_insn->type; + + if (arg_insn->op == IR_ARGVAL) { + /* make a stack copy */ + int size = arg_insn->op2; + int align = arg_insn->op3; + + copy_stack_offset += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack_offset = IR_ALIGNED_SIZE(copy_stack_offset, align); + src_reg = ctx->regs[arg][1]; + + | lea rdi, [rsp + (used_stack - copy_stack_offset)] + if (src_reg != IR_REG_NONE) { + if (IR_REG_SPILLED(src_reg)) { + src_reg = IR_REG_NUM(src_reg); + ir_emit_load(ctx, IR_ADDR, src_reg, arg_insn->op1); + } + | mov rsi, Ra(src_reg) + } else { + ir_emit_load(ctx, IR_ADDR, IR_REG_RSI, arg_insn->op1); + } + ir_emit_load_imm_int(ctx, IR_ADDR, IR_REG_RCX, size); + | rep; movsb + } + } + } +|.endif +#endif + /* 1. move all register arguments that should be passed through stack * and collect arguments that should be passed through registers */ copies = ir_mem_malloc((n - 2) * sizeof(ir_copy)); @@ -8976,6 +9104,55 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg arg_insn = &ctx->ir_base[arg]; type = arg_insn->type; if (IR_IS_TYPE_INT(type)) { +#ifndef _WIN64 + if (arg_insn->op == IR_ARGVAL) { + int size = arg_insn->op2; + int align = arg_insn->op3; + align = IR_MAX((int)sizeof(void*), align); + stack_offset = IR_ALIGNED_SIZE(stack_offset, align); + if (size) { + src_reg = ctx->regs[arg][1]; + if (src_reg != IR_REG_NONE) { + if (IR_REG_SPILLED(src_reg)) { + src_reg = IR_REG_NUM(src_reg); + ir_emit_load(ctx, IR_ADDR, src_reg, arg_insn->op1); + } + if (src_reg != IR_REG_RSI) { + |.if X64 + | mov rsi, Ra(src_reg) + |.else + | mov esi, Ra(src_reg) + |.endif + } + } else { + ir_emit_load(ctx, IR_ADDR, IR_REG_RSI, arg_insn->op1); + } + if (stack_offset == 0) { + |.if X64 + | mov rdi, rsp + |.else + | mov edi, esp + |.endif + } else { + |.if X64 + | lea rdi, [rsp+stack_offset] + |.else + | lea edi, [esp+stack_offset] + |.endif + } + |.if X64 + | mov rcx, size + | rep; movsb + |.else + | mov ecx, size + | rep; movsb + |.endif + } + stack_offset += size; + stack_offset = IR_ALIGNED_SIZE(stack_offset, sizeof(void*)); + continue; + } +#endif if (int_param < int_reg_params_count) { dst_reg = int_reg_params[int_param]; } else { @@ -8985,6 +9162,10 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg #ifdef _WIN64 /* WIN64 calling convention use common couter for int and fp registers */ fp_param++; + if (arg_insn->op == IR_ARGVAL) { + do_pass3 = 3; + continue; + } #endif } else { IR_ASSERT(IR_IS_TYPE_FP(type)); @@ -9000,7 +9181,9 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg #endif } if (dst_reg != IR_REG_NONE) { - if (src_reg == IR_REG_NONE) { + if (IR_IS_CONST_REF(arg) || + src_reg == IR_REG_NONE || + (IR_REG_SPILLED(src_reg) && !IR_REGSET_IN(IR_REGSET_PRESERVED, IR_REG_NUM(src_reg)))) { /* delay CONST->REG and MEM->REG moves to third pass */ do_pass3 = 1; } else { @@ -9035,6 +9218,10 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg /* 3. move the remaining memory and immediate values */ if (do_pass3) { +#ifdef _WIN64 + int copy_stack_offset = 0; +#endif + stack_offset = IR_SHADOW_ARGS; int_param = 0; fp_param = 0; @@ -9044,6 +9231,37 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg arg_insn = &ctx->ir_base[arg]; type = arg_insn->type; if (IR_IS_TYPE_INT(type)) { + if (arg_insn->op == IR_ARGVAL) { + int size = arg_insn->op2; + int align = arg_insn->op3; + +#ifndef _WIN64 + align = IR_MAX((int)sizeof(void*), align); + stack_offset = IR_ALIGNED_SIZE(stack_offset, align); + stack_offset += size; + stack_offset = IR_ALIGNED_SIZE(stack_offset, sizeof(void*)); + continue; +#else +|.if X64 + /* pass pointer to the copy on stack */ + copy_stack_offset += size; + align = IR_MAX((int)sizeof(void*), align); + copy_stack_offset = IR_ALIGNED_SIZE(copy_stack_offset, align); + if (int_param < int_reg_params_count) { + dst_reg = int_reg_params[int_param]; + | lea Ra(dst_reg), [rsp + (used_stack - copy_stack_offset)] + } else { + | lea Ra(tmp_reg), [rsp + (used_stack - copy_stack_offset)] + ir_emit_store_mem_int(ctx, IR_ADDR, IR_MEM_BO(IR_REG_STACK_POINTER, stack_offset), tmp_reg); + stack_offset += sizeof(void*); + } + int_param++; + /* WIN64 calling convention use common couter for int and fp registers */ + fp_param++; + continue; +|.endif +#endif + } if (int_param < int_reg_params_count) { dst_reg = int_reg_params[int_param]; } else { @@ -9068,7 +9286,9 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg #endif } if (dst_reg != IR_REG_NONE) { - if (src_reg == IR_REG_NONE) { + if (IR_IS_CONST_REF(arg) || + src_reg == IR_REG_NONE || + (IR_REG_SPILLED(src_reg) && !IR_REGSET_IN(IR_REGSET_PRESERVED, IR_REG_NUM(src_reg)))) { if (IR_IS_TYPE_INT(type)) { if (IR_IS_CONST_REF(arg)) { if (type == IR_I8 || type == IR_I16) { @@ -9157,6 +9377,9 @@ static int32_t ir_emit_arguments(ir_ctx *ctx, ir_ref def, ir_insn *insn, ir_reg } } } + if (insn->op == IR_CALL && (ctx->flags & IR_PREALLOCATED_STACK)) { + used_stack = 0; + } #endif #ifdef IR_REG_VARARG_FP_REGS /* set hidden argument to specify the number of vector registers used */ @@ -10226,7 +10449,16 @@ static void ir_emit_load_params(ir_ctx *ctx) insn = &ctx->ir_base[use]; if (insn->op == IR_PARAM) { if (IR_IS_TYPE_INT(insn->type)) { - if (int_param_num < int_reg_params_count) { + if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { + /* struct passed by value on stack */ + size_t align = ctx->value_params[insn->op3 - 1].align; + + align = IR_MAX(sizeof(void*), align); + stack_offset = IR_ALIGNED_SIZE(stack_offset, align); + stack_offset += ctx->value_params[insn->op3 - 1].size; + stack_offset = IR_ALIGNED_SIZE(stack_offset, sizeof(void*)); + continue; + } else if (int_param_num < int_reg_params_count) { src_reg = int_reg_params[int_param_num]; } else { src_reg = IR_REG_NONE; @@ -10354,6 +10586,19 @@ static void ir_fix_param_spills(ir_ctx *ctx) insn = &ctx->ir_base[use]; if (insn->op == IR_PARAM) { if (IR_IS_TYPE_INT(insn->type)) { +#ifndef _WIN64 + if (ctx->value_params && ctx->value_params[insn->op3 - 1].align) { + /* struct passed by value on stack */ + size_t align = ctx->value_params[insn->op3 - 1].align; + + align = IR_MAX(sizeof(void*), align); + stack_offset = IR_ALIGNED_SIZE(stack_offset, align); + ctx->value_params[insn->op3 - 1].offset = stack_start + stack_offset; + stack_offset += ctx->value_params[insn->op3 - 1].size; + stack_offset = IR_ALIGNED_SIZE(stack_offset, sizeof(void*)); + continue; + } +#endif if (int_param_num < int_reg_params_count) { src_reg = int_reg_params[int_param_num]; } else { @@ -10614,13 +10859,13 @@ static void ir_allocate_unique_spill_slots(ir_ctx *ctx) static void ir_preallocate_call_stack(ir_ctx *ctx) { - int call_stack_size, peak_call_stack_size = 0; + int call_stack_size, copy_stack, peak_call_stack_size = 0; ir_ref i, n; ir_insn *insn; for (i = 1, insn = ctx->ir_base + 1; i < ctx->insns_count;) { if (insn->op == IR_CALL) { - call_stack_size = ir_call_used_stack(ctx, insn); + call_stack_size = ir_call_used_stack(ctx, insn, ©_stack); if (call_stack_size > peak_call_stack_size #ifdef IR_HAVE_FASTCALL && !ir_is_fastcall(ctx, insn) /* fast call functions restore stack pointer */ diff --git a/ext/opcache/jit/ir/ir_x86.h b/ext/opcache/jit/ir/ir_x86.h index 4b86c291bdfc6..06bfa951cf21d 100644 --- a/ext/opcache/jit/ir/ir_x86.h +++ b/ext/opcache/jit/ir/ir_x86.h @@ -218,8 +218,8 @@ typedef struct _ir_tmp_reg { int8_t reg; }; uint8_t type; - uint8_t start; - uint8_t end; + int8_t start; + int8_t end; } ir_tmp_reg; struct _ir_target_constraints { diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index b0423dc06bd1e..19e5520b1569e 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -2945,8 +2945,9 @@ static int ZEND_FASTCALL zend_runtime_jit(void) bool do_bailout = 0; zend_shared_alloc_lock(); + jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array); - if (ZEND_FUNC_INFO(op_array)) { + if (jit_extension && !(jit_extension->func_info.flags & ZEND_FUNC_JITED)) { SHM_UNPROTECT(); zend_jit_unprotect(); @@ -2958,11 +2959,12 @@ static int ZEND_FASTCALL zend_runtime_jit(void) opline++; } } - jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array); - opline->handler = jit_extension->orig_handler; + ((zend_op*)opline)->handler = jit_extension->orig_handler; /* perform real JIT for this function */ zend_real_jit_func(op_array, NULL, NULL, ZEND_JIT_ON_FIRST_EXEC); + + jit_extension->func_info.flags |= ZEND_FUNC_JITED; } zend_catch { do_bailout = true; } zend_end_try(); @@ -3024,7 +3026,7 @@ void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend zend_shared_alloc_lock(); jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array); - if (jit_extension) { + if (jit_extension && !(jit_extension->func_info.flags & ZEND_FUNC_JITED)) { SHM_UNPROTECT(); zend_jit_unprotect(); @@ -3039,6 +3041,8 @@ void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend /* perform real JIT for this function */ zend_real_jit_func(op_array, NULL, opline, ZEND_JIT_ON_HOT_COUNTERS); + + jit_extension->func_info.flags |= ZEND_FUNC_JITED; } zend_catch { do_bailout = 1; } zend_end_try(); diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 0f5e1b11c3938..a98b9ebc77606 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1965,8 +1965,13 @@ static zval* ZEND_FASTCALL zend_jit_fetch_obj_r_slow_ex(zend_object *zobj) void **cache_slot = CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS); retval = zobj->handlers->read_property(zobj, name, BP_VAR_R, cache_slot, result); - if (retval == result && UNEXPECTED(Z_ISREF_P(retval))) { - zend_unwrap_reference(retval); + if (UNEXPECTED(Z_ISREF_P(retval))) { + if (retval == result) { + zend_unwrap_reference(retval); + } else { + retval = Z_REFVAL_P(retval); + } + ZEND_ASSERT(!Z_REFCOUNTED_P(retval)); } return retval; } diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 1a2c9f122a367..cd75856acb126 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -4902,33 +4902,30 @@ static int zend_jit_inc_dec(zend_jit_ctx *jit, const zend_op *opline, uint32_t o int32_t exit_point; const void *exit_addr; zend_jit_trace_stack *stack; - uint32_t old_res_info = 0; + uint32_t old_res_info = 0, old_op1_info = 0; stack = JIT_G(current_frame)->stack; if (opline->result_type != IS_UNUSED) { old_res_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var)); + SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0); if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) { - SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var), IS_LONG, 0); + SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->result.var), ref); + } else { + SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->result.var), op1_lval_ref); } } + old_op1_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var)); + SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), IS_LONG, 0); + SET_STACK_REF(stack, EX_VAR_TO_NUM(opline->op1.var), ref); + exit_point = zend_jit_trace_get_exit_point(opline + 1, 0); exit_addr = zend_jit_trace_get_exit_addr(exit_point); - if ((opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_PRE_DEC) && - opline->result_type != IS_UNUSED) { - if_overflow = ir_IF(ir_OVERFLOW(ref)); - ir_IF_FALSE_cold(if_overflow); - jit_set_Z_LVAL(jit, res_addr, ref); - if (Z_MODE(res_addr) != IS_REG) { - jit_set_Z_TYPE_INFO(jit, res_addr, IS_LONG); - } - jit_SIDE_EXIT(jit, ir_CONST_ADDR(exit_addr)); - ir_IF_TRUE(if_overflow); - } else { - ir_GUARD(ir_OVERFLOW(ref), ir_CONST_ADDR(exit_addr)); - } + ir_GUARD(ir_OVERFLOW(ref), ir_CONST_ADDR(exit_addr)); + if (opline->result_type != IS_UNUSED) { SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->result.var), old_res_info); } + SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_op1_info); } else { if_overflow = ir_IF(ir_OVERFLOW(ref)); ir_IF_FALSE(if_overflow); diff --git a/ext/opcache/tests/jit/gh19669-001.phpt b/ext/opcache/tests/jit/gh19669-001.phpt new file mode 100644 index 0000000000000..7d63643bb0157 --- /dev/null +++ b/ext/opcache/tests/jit/gh19669-001.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-19669: assertion failure zend_jit_trace_type_to_info_ex +--CREDITS-- +YuanchengJiang +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(-3) diff --git a/ext/opcache/tests/jit/gh19669-002.phpt b/ext/opcache/tests/jit/gh19669-002.phpt new file mode 100644 index 0000000000000..373356bcd0612 --- /dev/null +++ b/ext/opcache/tests/jit/gh19669-002.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-19669 002: assertion failure zend_jit_trace_type_to_info_ex +--CREDITS-- +YuanchengJiang +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(-10) diff --git a/ext/opcache/tests/jit/gh19831_001.phpt b/ext/opcache/tests/jit/gh19831_001.phpt new file mode 100644 index 0000000000000..c83ca6daa50d5 --- /dev/null +++ b/ext/opcache/tests/jit/gh19831_001.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-19831 001: fetch obj slow R REG + reference +--CREDITS-- +dktapps +--ENV-- +RT_COND=1 +--INI-- +opcache.jit=1203 +--FILE-- +layers; + } +} + +$t = new Test(); +$a = &$t->layers; +var_dump($t->getLayers()); + +?> +--EXPECT-- +int(1) diff --git a/ext/opcache/tests/jit/gh19831_002.phpt b/ext/opcache/tests/jit/gh19831_002.phpt new file mode 100644 index 0000000000000..25b596a3decb3 --- /dev/null +++ b/ext/opcache/tests/jit/gh19831_002.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-19831 002: fetch obj slow R REG + __get + reference +--CREDITS-- +dktapps +--ENV-- +RT_COND=1 +--INI-- +opcache.jit=1203 +--FILE-- +layers; + } +} + +$t = new Test(); +unset($t->layers); +var_dump($t->getLayers()); + +?> +--EXPECT-- +int(1) diff --git a/ext/opcache/tests/opt/gh19792.phpt b/ext/opcache/tests/opt/gh19792.phpt new file mode 100644 index 0000000000000..edd805ca57a19 --- /dev/null +++ b/ext/opcache/tests/opt/gh19792.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-19792 (SCCP causes UAF for return value if both warning and exception are triggered) +--EXTENSIONS-- +opcache +zend_test +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECTF-- +Warning: a warning in %s on line %d +an exception diff --git a/ext/phar/tar.c b/ext/phar/tar.c index b0273daed3c5e..63a9cdbf88fd1 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -1170,7 +1170,16 @@ void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def } zend_hash_apply_with_argument(&phar->manifest, phar_tar_writeheaders, (void *) &pass); - /* TODO: memory leak and incorrect continuation if phar_tar_writeheaders fails? */ + + if (error && *error) { + if (must_close_old_file) { + php_stream_close(oldfile); + } + + /* on error in the hash iterator above, error is set */ + php_stream_close(newfile); + return; + } /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */ if (!phar->is_data || phar->sig_flags) { diff --git a/ext/phar/tests/tar_flush_too_long_filename.phpt b/ext/phar/tests/tar_flush_too_long_filename.phpt new file mode 100644 index 0000000000000..be18f5e481688 --- /dev/null +++ b/ext/phar/tests/tar_flush_too_long_filename.phpt @@ -0,0 +1,41 @@ +--TEST-- +Tar flush with too long file name +--EXTENSIONS-- +phar +--SKIPIF-- + +--INI-- +phar.require_hash=0 +--FILE-- +addEmptyDir('blah1/'); +$phar->setSignatureAlgorithm(Phar::OPENSSL, "randomcrap"); +try { + $phar->addEmptyDir('blah2/' . str_repeat('X', 1000)); +} catch (PharException $e) { + echo $e->getMessage(); +} + +?> +--CLEAN-- + +--EXPECTF-- +tar-based phar "%s" cannot be created, filename "%s" is too long for tar file format diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 5c7cd08bd6314..9a98b541ff605 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -1223,7 +1223,9 @@ static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pas entry.fp_type = PHAR_MOD; entry.is_modified = 1; if (entry.fp == NULL) { + efree(signature); spprintf(pass->error, 0, "phar error: unable to create temporary file for signature"); + php_stream_close(newfile); return FAILURE; } @@ -1441,11 +1443,12 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def phar_metadata_tracker_try_ensure_has_serialized_data(&phar->metadata_tracker, phar->is_persistent); if (temperr) { +temperror: if (error) { spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr); } efree(temperr); -temperror: +notemperror: php_stream_close(pass.centralfp); nocentralerror: php_stream_close(pass.filefp); @@ -1473,7 +1476,7 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def if (error) { spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname); } - goto temperror; + goto notemperror; } } diff --git a/ext/random/tests/03_randomizer/gh_19765_unserialize.phpt b/ext/random/tests/03_randomizer/gh_19765_unserialize.phpt new file mode 100644 index 0000000000000..24abfca293fc5 --- /dev/null +++ b/ext/random/tests/03_randomizer/gh_19765_unserialize.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-19765: object_properties_load() bypasses readonly property checks +--FILE-- +__unserialize([['engine' => new PcgOneseq128XslRr64()]]); +} catch (Exception $error) { + echo $error->getMessage() . "\n"; +} +var_dump($r->engine::class); + +?> +--EXPECT-- +Invalid serialization data for Random\Randomizer object +string(21) "Random\Engine\Mt19937" diff --git a/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt b/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt index fe1acb2f74be4..edadb7380bd1f 100644 --- a/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt +++ b/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt @@ -1,5 +1,5 @@ --TEST-- -Fix GH-9186 @strict-properties can be bypassed using unserialization +GH-9186: @strict-properties can be bypassed using unserialization --FILE-- ns && xmlStrEqual(attr->ns->href, (const xmlChar *) "http://www.w3.org/2000/xmlns/")) { const char *prefix = attr->ns->prefix ? (const char *) attr->name : ""; - bool free; - xmlChar *href = php_libxml_attr_value(attr, &free); + bool should_free; + xmlChar *href = php_libxml_attr_value(attr, &should_free); sxe_add_namespace_name_raw(return_value, prefix, (const char *) href); - if (free) { + if (should_free) { xmlFree(href); } } @@ -1629,7 +1629,7 @@ PHP_METHOD(SimpleXMLElement, getName) node = php_sxe_get_first_node_non_destructive(sxe, node); if (node) { namelen = xmlStrlen(node->name); - RETURN_STRINGL((char*)node->name, namelen); + RETURN_STRINGL_FAST((const char *) node->name, namelen); } else { RETURN_EMPTY_STRING(); } diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index b829ce047a6c7..73245fd1a1818 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -3610,6 +3610,11 @@ static encodePtr get_array_type(xmlNodePtr node, zval *array, smart_str *type) soap_error0(E_ERROR, "Encoding: SoapVar has no 'enc_type' property"); } cur_type = Z_LVAL_P(ztype); + if (cur_type == UNKNOWN_TYPE) { + /* Mimic guess_xml_convert() where we use the type of the data. + * UNDEFs are handled transparently as it will error out upon encoding the data. */ + cur_type = Z_TYPE_P(Z_VAR_ENC_VALUE_P(tmp)); + } zval *zstype = Z_VAR_ENC_STYPE_P(tmp); if (Z_TYPE_P(zstype) == IS_STRING) { diff --git a/ext/soap/soap.c b/ext/soap/soap.c index afd4be8c24c0a..ac14dee76cf03 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -1002,7 +1002,13 @@ PHP_METHOD(SoapServer, __construct) service->soap_functions.ft = zend_new_array(0); if (wsdl) { - service->sdl = get_sdl(ZEND_THIS, ZSTR_VAL(wsdl), cache_wsdl); + zend_try { + service->sdl = get_sdl(ZEND_THIS, ZSTR_VAL(wsdl), cache_wsdl); + } zend_catch { + xmlCharEncCloseFunc(service->encoding); + service->encoding = NULL; + zend_bailout(); + } zend_end_try(); if (service->uri == NULL) { if (service->sdl->target_ns) { service->uri = estrdup(service->sdl->target_ns); diff --git a/ext/soap/tests/bugs/gh19784.phpt b/ext/soap/tests/bugs/gh19784.phpt new file mode 100644 index 0000000000000..1f718e74c4598 --- /dev/null +++ b/ext/soap/tests/bugs/gh19784.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-19784 (SoapServer memory leak) +--EXTENSIONS-- +soap +--FILE-- + $v_5257); +new SoapServer('foobarbaz',$v_5238,); +?> +--EXPECTF-- + +%s%a diff --git a/ext/soap/tests/bugs/gh20011.phpt b/ext/soap/tests/bugs/gh20011.phpt new file mode 100644 index 0000000000000..cf40d813e6178 --- /dev/null +++ b/ext/soap/tests/bugs/gh20011.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-20011 (Array of SoapVar of unknown type causes crash) +--EXTENSIONS-- +soap +--FILE-- + 'test://', 'uri' => 'http://soapinterop.org/']); +$client->echoStringArray($array); +?> +--EXPECT-- + +test string diff --git a/ext/standard/array.c b/ext/standard/array.c index 7a1930696c83c..13d17bff82c2e 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3497,6 +3497,12 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H HT_SET_ITERATORS_COUNT(in_hash, 0); in_hash->pDestructor = NULL; + /* Set internal pointer to 0 directly instead of calling zend_hash_internal_pointer_reset(). + * This avoids the COW violation assertion and delays advancing to the first valid position + * until after we've switched to the new array structure (out_hash). The iterator will be + * advanced when actually accessed, at which point it will find valid indexes in the new array. */ + in_hash->nInternalPointer = 0; + if (UNEXPECTED(GC_DELREF(in_hash) == 0)) { /* Array was completely deallocated during the operation */ zend_array_destroy(in_hash); @@ -3515,8 +3521,6 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H in_hash->nNextFreeElement = out_hash.nNextFreeElement; in_hash->arData = out_hash.arData; in_hash->pDestructor = out_hash.pDestructor; - - zend_hash_internal_pointer_reset(in_hash); } /* }}} */ @@ -5027,6 +5031,11 @@ PHP_FUNCTION(array_unique) ZVAL_UNDEF(&arTmp[i].b.val); zend_sort((void *) arTmp, i, sizeof(struct bucketindex), (compare_func_t) cmp, (swap_func_t) array_bucketindex_swap); + + if (UNEXPECTED(EG(exception))) { + goto out; + } + /* go through the sorted array and delete duplicates from the copy */ lastkept = arTmp; for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) { @@ -5046,6 +5055,8 @@ PHP_FUNCTION(array_unique) } } } + +out: pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT); if (in_place) { diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index bad12e819721e..855356a52b07c 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -2015,10 +2015,8 @@ PHP_FUNCTION(ini_set) #define _CHECK_PATH(var, var_len, ini) php_ini_check_path(var, var_len, ini, sizeof(ini)) /* open basedir check */ if (PG(open_basedir)) { - if (_CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "error_log") || - _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.class.path") || + if (_CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.class.path") || _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.home") || - _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "mail.log") || _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "java.library.path") || _CHECK_PATH(ZSTR_VAL(varname), ZSTR_LEN(varname), "vpopmail.directory")) { if (php_check_open_basedir(ZSTR_VAL(new_value_str))) { diff --git a/ext/standard/tests/array/gh19926.phpt b/ext/standard/tests/array/gh19926.phpt new file mode 100644 index 0000000000000..b714db9eb2b32 --- /dev/null +++ b/ext/standard/tests/array/gh19926.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-19926 (Assertion failure zend_hash_internal_pointer_reset_ex) +--FILE-- + +--EXPECT-- +Exception caught, no assertion failure diff --git a/ext/standard/tests/array/gh19926_pointer.phpt b/ext/standard/tests/array/gh19926_pointer.phpt new file mode 100644 index 0000000000000..c134f3b594b3d --- /dev/null +++ b/ext/standard/tests/array/gh19926_pointer.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-19926 (internal pointer behavior after array_splice) +--FILE-- + +--EXPECT-- +Before array_splice: int(3) +After array_splice: int(999) diff --git a/ext/standard/tests/array/gh20043.phpt b/ext/standard/tests/array/gh20043.phpt new file mode 100644 index 0000000000000..d5c7e06417f18 --- /dev/null +++ b/ext/standard/tests/array/gh20043.phpt @@ -0,0 +1,12 @@ +--TEST-- +GH-20043 (array_unique assertion failure with RC1 array causing an exception on sort) +--FILE-- +getMessage(); +} +?> +--EXPECT-- +Object of class stdClass could not be converted to string diff --git a/ext/standard/tests/file/bug46347.phpt b/ext/standard/tests/file/bug46347.phpt index 903a6e35cc2b2..e337ade9eb8fb 100644 --- a/ext/standard/tests/file/bug46347.phpt +++ b/ext/standard/tests/file/bug46347.phpt @@ -8,14 +8,14 @@ $str = <<< EOF part1.*.part2 = 1 EOF; -$file = __DIR__ . '/parse.ini'; +$file = __DIR__ . '/bug46347.ini'; file_put_contents($file, $str); var_dump(parse_ini_file($file)); ?> --CLEAN-- --EXPECT-- array(1) { diff --git a/ext/standard/tests/filters/convert.phpt b/ext/standard/tests/filters/convert.phpt new file mode 100644 index 0000000000000..a99579b5dd8fe --- /dev/null +++ b/ext/standard/tests/filters/convert.phpt @@ -0,0 +1,130 @@ +--TEST-- +convert stream filter tests +--FILE-- + 20, 'line-break-chars' => "\n"); +test_roundtrip("Long text that will be wrapped", "convert.base64-encode", "convert.base64-decode", $opts); + +$opts2 = array('binary' => true); +test_roundtrip("Text\t\r\n", "convert.quoted-printable-encode", "convert.quoted-printable-decode", $opts2); + +$fp = tmpfile(); +fwrite($fp, "Test"); +rewind($fp); +stream_filter_prepend($fp, 'convert.base64-encode', STREAM_FILTER_READ); +$result = stream_get_contents($fp); +fclose($fp); +var_dump($result === base64_encode("Test")); + +?> +--EXPECTF-- +Filter: convert.base64-encode +Original: Hello World! +Encoded: SGVsbG8gV29ybGQh +Decoded: Hello World! +bool(true) + +Filter: convert.base64-encode +Original (hex): 414243ff +Encoded: QUJD/w== +Decoded (hex): 414243ff +bool(true) + +Filter: convert.base64-encode +Original: +Encoded: +Decoded: +bool(true) + +Filter: convert.base64-encode +Original: A +Encoded: QQ== +Decoded: A +bool(true) + +Filter: convert.quoted-printable-encode +Original: Hello World! +Encoded: Hello World! +Decoded: Hello World! +bool(true) + +Filter: convert.quoted-printable-encode +Original: Line1 +Line2 +Encoded: Line1=0D=0ALine2 +Decoded: Line1 +Line2 +bool(true) + +Filter: convert.base64-encode +Original: Long text that will be wrapped +Encoded: TG9uZyB0ZXh0IHRoYXQg +d2lsbCBiZSB3cmFwcGVk +Decoded: Long text that will be wrapped +bool(true) + +Filter: convert.quoted-printable-encode +Original: Text + +Encoded: Text=09=0D=0A +Decoded: Text + +bool(true) + +bool(true) diff --git a/ext/standard/tests/filters/gh17345.phpt b/ext/standard/tests/filters/gh17345.phpt new file mode 100644 index 0000000000000..f64659390a14f --- /dev/null +++ b/ext/standard/tests/filters/gh17345.phpt @@ -0,0 +1,49 @@ +--TEST-- +GH-17345 (Bug #35916 was not completely fixed) +--FILE-- +data = strtoupper($bucket->data); + $consumed += $bucket->datalen; + stream_bucket_prepend($out, $bucket); + // Interleave new bucket + stream_bucket_prepend($out, clone $bucket); + stream_bucket_prepend($out, $bucket); + } + return PSFS_PASS_ON; + } + + function onCreate(): bool + { + echo "fffffffffff\n"; + return true; + } + + function onClose(): void + { + echo "hello\n"; + } +} + +stream_filter_register("strtoupper", "strtoupper_filter"); +$fp=fopen($file, "w"); +stream_filter_append($fp, "strtoupper"); +fread($fp, 1024); +fwrite($fp, "Thank you\n"); +fclose($fp); +readfile($file); +unlink($file); +?> +--EXPECTF-- +fffffffffff + +Notice: fread(): Read of 8192 bytes failed with errno=9 Bad file descriptor in %s on line %d +hello +THANK YOU diff --git a/ext/standard/tests/general_functions/debug_zval_dump_gh19801_memory_leak.phpt b/ext/standard/tests/general_functions/debug_zval_dump_gh19801_memory_leak.phpt new file mode 100644 index 0000000000000..cf2f1d444f1dc --- /dev/null +++ b/ext/standard/tests/general_functions/debug_zval_dump_gh19801_memory_leak.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-19801 (debug_zval_dump() leak with __debugInfo() that modifies circular references) +--FILE-- +a = null; + gc_collect_cycles(); + return []; + } + }, +]; + +$b = new stdClass; +$b->a = &$a; + +debug_zval_dump($b); +?> +--EXPECTF-- +object(stdClass)#2 (1) refcount(%d){ + ["a"]=> + reference refcount(%d) { + array(1) packed refcount(%d){ + [0]=> + object(class@anonymous)#1 (0) refcount(%d){ + } + } + } +} diff --git a/ext/standard/tests/general_functions/var_dump_gh19801_memory_leak.phpt b/ext/standard/tests/general_functions/var_dump_gh19801_memory_leak.phpt new file mode 100644 index 0000000000000..f0522916de4e6 --- /dev/null +++ b/ext/standard/tests/general_functions/var_dump_gh19801_memory_leak.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-19801 (var_dump() memory leak with __debugInfo() that modifies circular references) +--FILE-- +a = null; + gc_collect_cycles(); + return []; + } + }, +]; + +$b = new stdClass; +$b->a = &$a; + +var_dump($b); +?> +--EXPECTF-- +object(stdClass)#2 (1) { + ["a"]=> + &array(1) { + [0]=> + object(class@anonymous)#1 (0) { + } + } +} diff --git a/ext/standard/tests/serialize/gh12265.phpt b/ext/standard/tests/serialize/gh12265.phpt new file mode 100644 index 0000000000000..5f49a62fee29f --- /dev/null +++ b/ext/standard/tests/serialize/gh12265.phpt @@ -0,0 +1,47 @@ +--TEST-- +GH-12265 (Cloning an object breaks serialization recursion) - __serialize variation +--FILE-- + new A($this)]; + } +} + +class C { + public B $b; + + public function __construct() { + $this->b = new B; + } +} + +$b = new B(); +$sb = serialize($b); +$stb = serialize(new B); + +printf("serialized original: %s\n", $sb); +printf("serialized temp : %s\n", $stb); + +$c = new C; +$sc = serialize($c); +$stc = serialize(new C); + +printf("serialized original: %s\n", $sc); +printf("serialized temp : %s\n", $stc); + +?> +--EXPECT-- +serialized original: O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +serialized temp : O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +serialized original: O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}} +serialized temp : O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}} diff --git a/ext/standard/tests/serialize/gh12265b.phpt b/ext/standard/tests/serialize/gh12265b.phpt new file mode 100644 index 0000000000000..2b9d80a848cee --- /dev/null +++ b/ext/standard/tests/serialize/gh12265b.phpt @@ -0,0 +1,48 @@ +--TEST-- +GH-12265 (Cloning an object breaks serialization recursion) - __sleep variation +--FILE-- +a = new A($this); + return ['a']; + } +} + +class C { + public B $b; + + public function __construct() { + $this->b = new B; + } +} + +$b = new B(); +$sb = serialize($b); +$stb = serialize(new B); + +printf("serialized original: %s\n", $sb); +printf("serialized temp : %s\n", $stb); + +$c = new C; +$sc = serialize($c); +$stc = serialize(new C); + +printf("serialized original: %s\n", $sc); +printf("serialized temp : %s\n", $stc); + +?> +--EXPECT-- +serialized original: O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +serialized temp : O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:1;}} +serialized original: O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}} +serialized temp : O:1:"C":1:{s:1:"b";O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"x";r:2;}}} diff --git a/ext/standard/tests/serialize/gh19701.phpt b/ext/standard/tests/serialize/gh19701.phpt new file mode 100644 index 0000000000000..a1a3fd5c64bcc --- /dev/null +++ b/ext/standard/tests/serialize/gh19701.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-19701 (Serialize/deserialize loses some data) +--CREDITS-- +cuchac +DanielEScherzer +--FILE-- +parent = $baseProduct; +$baseProduct->children = [ $child ]; + +$data = [clone $baseProduct, $baseProduct]; + +echo serialize($data), "\n"; + +?> +--EXPECT-- +a:2:{i:0;O:4:"Item":2:{s:8:"children";a:1:{i:0;O:4:"Item":2:{s:8:"children";a:0:{}s:6:"parent";O:4:"Item":2:{s:8:"children";a:1:{i:0;r:4;}s:6:"parent";N;}}}s:6:"parent";N;}i:1;r:6;} diff --git a/ext/standard/tests/streams/gh19570.phpt b/ext/standard/tests/streams/gh19570.phpt new file mode 100644 index 0000000000000..e3d486ea83cf9 --- /dev/null +++ b/ext/standard/tests/streams/gh19570.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-19570 (unable to fseek in /dev/zero and /dev/null) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +int(0) +int(0) +int(0) diff --git a/ext/standard/tests/streams/gh19705.phpt b/ext/standard/tests/streams/gh19705.phpt new file mode 100644 index 0000000000000..d34cfc700596d --- /dev/null +++ b/ext/standard/tests/streams/gh19705.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-19705 segmentation fault with non writable stream at stream_filter_append call. +--EXTENSIONS-- +zlib +--FILE-- + +--EXPECTF-- +resource(%d) of type (stream filter) diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index ca5d25dc62568..c2e5e854572fe 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -408,17 +408,19 @@ static void php_stream_bucket_attach(int append, INTERNAL_FUNCTION_PARAMETERS) memcpy(bucket->buf, Z_STRVAL_P(pzdata), bucket->buflen); } + /* If the bucket is already on a brigade we have to unlink it first to keep the + * linked list consistent. Furthermore, we can transfer the refcount in that case. */ + if (bucket->brigade) { + php_stream_bucket_unlink(bucket); + } else { + bucket->refcount++; + } + if (append) { php_stream_bucket_append(brigade, bucket); } else { php_stream_bucket_prepend(brigade, bucket); } - /* This is a hack necessary to accommodate situations where bucket is appended to the stream - * multiple times. See bug35916.phpt for reference. - */ - if (bucket->refcount == 1) { - bucket->refcount++; - } } /* }}} */ diff --git a/ext/standard/var.c b/ext/standard/var.c index 1c2b0eb164a1c..cff865055e7df 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -153,7 +153,7 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */ } ZEND_HASH_FOREACH_END(); if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) { GC_UNPROTECT_RECURSION(myht); - GC_DELREF(myht); + GC_DTOR_NO_REF(myht); } if (level > 1) { php_printf("%*c", level-1, ' '); @@ -354,7 +354,7 @@ PHPAPI void php_debug_zval_dump(zval *struc, int level) /* {{{ */ } ZEND_HASH_FOREACH_END(); if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) { GC_UNPROTECT_RECURSION(myht); - GC_DELREF(myht); + GC_DTOR_NO_REF(myht); } if (level > 1) { php_printf("%*c", level - 1, ' '); @@ -737,7 +737,10 @@ static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var, b return 0; } else if (!in_rcn_array && Z_REFCOUNT_P(var) == 1 - && (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1)) { + && (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1) + /* __serialize and __sleep may arbitrarily increase the refcount */ + && Z_OBJCE_P(var)->__serialize == NULL + && zend_hash_find_known_hash(&Z_OBJCE_P(var)->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP)) == NULL) { return 0; } @@ -998,18 +1001,11 @@ static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable /* we should still add element even if it's not OK, * since we already wrote the length of the array before */ if (Z_TYPE_P(data) == IS_ARRAY) { - if (UNEXPECTED(Z_IS_RECURSIVE_P(data)) - || UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))) { + if (UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))) { php_add_var_hash(var_hash, struc, in_rcn_array); smart_str_appendl(buf, "N;", 2); } else { - if (Z_REFCOUNTED_P(data)) { - Z_PROTECT_RECURSION_P(data); - } php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false); - if (Z_REFCOUNTED_P(data)) { - Z_UNPROTECT_RECURSION_P(data); - } } } else { php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false); diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index 0432e7c757e80..3d2624b9c6ca4 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -542,6 +542,7 @@ static void php_xmlreader_set_relaxng_schema(INTERNAL_FUNCTION_PARAMETERS, int t RETURN_TRUE; } else { + xmlRelaxNGFree(schema); php_error_docref(NULL, E_WARNING, "Schema contains errors"); RETURN_FALSE; } diff --git a/ext/xsl/tests/gh19988.phpt b/ext/xsl/tests/gh19988.phpt new file mode 100644 index 0000000000000..174af282f9c09 --- /dev/null +++ b/ext/xsl/tests/gh19988.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-19988 (zend_string_init with NULL pointer in simplexml (UB)) +--EXTENSIONS-- +simplexml +xsl +--CREDITS-- +YuanchengJiang +--FILE-- +load(__DIR__ . '/53965/collection.xsl'); +$processor->importStylesheet($dom); +$result = $processor->transformToDoc($sxe, SimpleXMLElement::class); +var_dump($result->getName()); +?> +--EXPECT-- +string(0) "" diff --git a/ext/zend_test/observer.c b/ext/zend_test/observer.c index d2a91d16840e0..31052ec830f73 100644 --- a/ext/zend_test/observer.c +++ b/ext/zend_test/observer.c @@ -155,7 +155,7 @@ static void observer_show_init(zend_function *fbc) php_printf("%*s\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(fbc->common.function_name)); } } else { - php_printf("%*s\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(fbc->op_array.filename)); + php_printf("%*s\n", 2 * ZT_G(observer_nesting_depth), "", fbc->op_array.filename ? ZSTR_VAL(fbc->op_array.filename) : "[no active file]"); } } @@ -178,7 +178,7 @@ static void observer_show_init_backtrace(zend_execute_data *execute_data) php_printf("%*s%s()\n", indent, "", ZSTR_VAL(fbc->common.function_name)); } } else { - php_printf("%*s{main} %s\n", indent, "", ZSTR_VAL(fbc->op_array.filename)); + php_printf("%*s{main} %s\n", indent, "", fbc->op_array.filename ? ZSTR_VAL(fbc->op_array.filename) : "[no active file]"); } } while ((ex = ex->prev_execute_data) != NULL); php_printf("%*s-->\n", 2 * ZT_G(observer_nesting_depth), ""); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 388ce31b163f1..59b2c79edf672 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1588,3 +1588,12 @@ static PHP_FUNCTION(zend_test_gh18756) zend_mm_gc(heap); zend_mm_shutdown(heap, true, false); } + +static PHP_FUNCTION(zend_test_gh19792) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + RETVAL_STRING("this is a non-interned string"); + zend_error(E_WARNING, "a warning"); + zend_throw_error(NULL, "an exception"); +} diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index fe1083ee942cc..1be61b7a17028 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -318,6 +318,9 @@ function zend_test_is_zend_ptr(int $addr): bool {} function zend_test_log_err_debug(string $str): void {} function zend_test_gh18756(): void {} + + /** @compile-time-eval */ + function zend_test_gh19792(): void {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 00d45739ca119..b38a354651816 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e6374018fbb4fa793905bd5cb34e5a56b9e310fe */ + * Stub hash: 6f76138d313c37244148004e2691ee47534f87a4 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -171,6 +171,8 @@ ZEND_END_ARG_INFO() #define arginfo_zend_test_gh18756 arginfo_zend_test_void_return +#define arginfo_zend_test_gh19792 arginfo_zend_test_void_return + #define arginfo_ZendTestNS2_namespaced_func arginfo_zend_test_is_pcre_bundled #define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_void_return @@ -306,6 +308,7 @@ static ZEND_FUNCTION(zend_test_cast_fread); static ZEND_FUNCTION(zend_test_is_zend_ptr); static ZEND_FUNCTION(zend_test_log_err_debug); static ZEND_FUNCTION(zend_test_gh18756); +static ZEND_FUNCTION(zend_test_gh19792); static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); @@ -412,6 +415,15 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_test_is_zend_ptr, arginfo_zend_test_is_zend_ptr) ZEND_FE(zend_test_log_err_debug, arginfo_zend_test_log_err_debug) ZEND_FE(zend_test_gh18756, arginfo_zend_test_gh18756) +#if (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("zend_test_gh19792", zif_zend_test_gh19792, arginfo_zend_test_gh19792, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) +#else +#if (PHP_VERSION_ID >= 80200) + ZEND_RAW_FENTRY("zend_test_gh19792", zif_zend_test_gh19792, arginfo_zend_test_gh19792, ZEND_ACC_COMPILE_TIME_EVAL) +#elif (PHP_VERSION_ID >= 80000) + ZEND_RAW_FENTRY("zend_test_gh19792", zif_zend_test_gh19792, arginfo_zend_test_gh19792, 0) +#endif +#endif #if (PHP_VERSION_ID >= 80400) ZEND_RAW_FENTRY(ZEND_NS_NAME("ZendTestNS2", "namespaced_func"), zif_ZendTestNS2_namespaced_func, arginfo_ZendTestNS2_namespaced_func, 0, NULL, NULL) #else diff --git a/ext/zend_test/tests/observer_fiber_backtrace_crash.phpt b/ext/zend_test/tests/observer_fiber_backtrace_crash.phpt new file mode 100644 index 0000000000000..3d3629af45bdb --- /dev/null +++ b/ext/zend_test/tests/observer_fiber_backtrace_crash.phpt @@ -0,0 +1,48 @@ +--TEST-- +GH-16319 (Fiber backtrace with null filename should not crash) +--EXTENSIONS-- +zend_test +--INI-- +zend_test.observer.enabled=1 +zend_test.observer.show_init_backtrace=1 +zend_test.observer.show_output=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_opcode=0 +opcache.jit=0 +--FILE-- +start(); +echo "Test completed without crash\n"; +?> +--EXPECTF-- + + + + + + + + + + + + + <{closure:%s:%d}> + + +Test completed without crash + diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 659dd48ae2927..f6ccb40d798bf 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -1790,7 +1790,7 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* basename = php_basename(Z_STRVAL_P(zval_file), Z_STRLEN_P(zval_file), NULL, 0); file_stripped = ZSTR_VAL(basename); file_stripped_len = ZSTR_LEN(basename); - } else if (opts.remove_path && !memcmp(Z_STRVAL_P(zval_file), opts.remove_path, opts.remove_path_len)) { + } else if (opts.remove_path && Z_STRLEN_P(zval_file) > opts.remove_path_len && !memcmp(Z_STRVAL_P(zval_file), opts.remove_path, opts.remove_path_len)) { if (IS_SLASH(Z_STRVAL_P(zval_file)[opts.remove_path_len])) { file_stripped = Z_STRVAL_P(zval_file) + opts.remove_path_len + 1; file_stripped_len = Z_STRLEN_P(zval_file) - opts.remove_path_len - 1; @@ -2386,6 +2386,11 @@ PHP_METHOD(ZipArchive, setEncryptionName) RETURN_FALSE; } + if (UNEXPECTED(zip_file_set_encryption(intern, idx, ZIP_EM_NONE, NULL) < 0)) { + php_error_docref(NULL, E_WARNING, "password reset failed"); + RETURN_FALSE; + } + if (zip_file_set_encryption(intern, idx, (zip_uint16_t)method, password)) { RETURN_FALSE; } @@ -2409,6 +2414,11 @@ PHP_METHOD(ZipArchive, setEncryptionIndex) ZIP_FROM_OBJECT(intern, self); + if (UNEXPECTED(zip_file_set_encryption(intern, index, ZIP_EM_NONE, NULL) < 0)) { + php_error_docref(NULL, E_WARNING, "password reset failed"); + RETURN_FALSE; + } + if (zip_file_set_encryption(intern, index, (zip_uint16_t)method, password)) { RETURN_FALSE; } diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index 99409d3fc89d4..e08905e319450 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -39,7 +39,7 @@ extern zend_module_entry zip_module_entry; /* Additionnal flags not from libzip */ #define ZIP_FL_OPEN_FILE_NOW (1u<<30) -#define PHP_ZIP_VERSION "1.22.6" +#define PHP_ZIP_VERSION "1.22.7" #ifdef HAVE_LIBZIP_VERSION #define LIBZIP_VERSION_STR zip_libzip_version() diff --git a/ext/zip/tests/gh19688.phpt b/ext/zip/tests/gh19688.phpt new file mode 100644 index 0000000000000..7e2a2800b2553 --- /dev/null +++ b/ext/zip/tests/gh19688.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-19688 (Remove pattern overflow in zip addGlob()) +--EXTENSIONS-- +zip +--FILE-- +open($filename, ZipArchive::CREATE | ZipArchive::OVERWRITE); +$options = array('remove_path' => $dir . 'a very long string here that will overrun'); +$zip->addGlob($testfile, 0, $options); +var_dump($zip->getNameIndex(0)); +?> +--CLEAN-- + +--EXPECTF-- +string(%d) "%s001.phpt" diff --git a/ext/zip/tests/gh19932.phpt b/ext/zip/tests/gh19932.phpt new file mode 100644 index 0000000000000..760fa1c9e766d --- /dev/null +++ b/ext/zip/tests/gh19932.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-19932 (ZipArchive::setEncryptionName()/setEncryptionIndex() memory leak) +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +open(__DIR__ . "/gh19932.zip", ZipArchive::CREATE); +$zip->addFromString("test.txt", "test"); +$zip->setEncryptionName("test.txt", ZipArchive::EM_AES_256, "password"); +$zip->setEncryptionName("test.txt", ZipArchive::EM_AES_256, "password"); +$zip->setEncryptionIndex("0", ZipArchive::EM_AES_256, "password"); +$zip->setEncryptionIndex("0", ZipArchive::EM_AES_256, "password"); +$zip->close(); +echo "OK"; +?> +--CLEAN-- + +--EXPECT-- +OK + diff --git a/ext/zlib/tests/gh19922.phpt b/ext/zlib/tests/gh19922.phpt new file mode 100644 index 0000000000000..71644512e6656 --- /dev/null +++ b/ext/zlib/tests/gh19922.phpt @@ -0,0 +1,12 @@ +--TEST-- +GH-19922 (gzopen double free on debug build and unseekable stream) +--EXTENSIONS-- +zlib +--FILE-- + +--EXPECTF-- + +Warning: gzopen(php://output): could not make seekable - php://output in %s on line %d +bool(false) diff --git a/main/debug_gdb_scripts.c b/main/debug_gdb_scripts.c index 384a1c8b46ee8..032dd09491bac 100644 --- a/main/debug_gdb_scripts.c +++ b/main/debug_gdb_scripts.c @@ -55,7 +55,7 @@ asm( ".ascii \"\\n\"\n" ".ascii \" printf \\\"Compiled variables count: %d\\\\\\\\n\\\\\\\\n\\\", $cv_count\\n\"\n" ".ascii \" while $cv_idx < $cv_count\\n\"\n" - ".ascii \" printf \\\"[%d] \\\\'%s\\\\'\\\\\\\\n\\\", $cv_idx, $cv[$cv_idx].val\\n\"\n" + ".ascii \" printf \\\"[%d] \\\\'$%s\\\\'\\\\\\\\n\\\", $cv_idx, $cv[$cv_idx].val@$cv[$cv_idx].len\\n\"\n" ".ascii \" set $zvalue = ((zval *) $cv_ex_ptr) + $callFrameSize + $cv_idx\\n\"\n" ".ascii \" printzv $zvalue\\n\"\n" ".ascii \" set $cv_idx = $cv_idx + 1\\n\"\n" diff --git a/main/main.c b/main/main.c index 3a9299285f4e3..d8b8c0f161b36 100644 --- a/main/main.c +++ b/main/main.c @@ -667,12 +667,13 @@ static PHP_INI_MH(OnUpdateErrorLog) { /* Only do the safemode/open_basedir check at runtime */ if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && - new_value && zend_string_equals_literal(new_value, "syslog")) { + new_value && !zend_string_equals_literal(new_value, "syslog") && ZSTR_LEN(new_value) > 0) { if (PG(open_basedir) && php_check_open_basedir(ZSTR_VAL(new_value))) { return FAILURE; } } - OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); + char **p = (char **) ZEND_INI_GET_ADDR(); + *p = new_value && ZSTR_LEN(new_value) > 0 ? ZSTR_VAL(new_value) : NULL; return SUCCESS; } /* }}} */ @@ -680,13 +681,14 @@ static PHP_INI_MH(OnUpdateErrorLog) /* {{{ PHP_INI_MH */ static PHP_INI_MH(OnUpdateMailLog) { - /* Only do the safemode/open_basedir check at runtime */ - if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && new_value) { + /* Only do the open_basedir check at runtime */ + if ((stage == PHP_INI_STAGE_RUNTIME || stage == PHP_INI_STAGE_HTACCESS) && new_value && ZSTR_LEN(new_value) > 0) { if (PG(open_basedir) && php_check_open_basedir(ZSTR_VAL(new_value))) { return FAILURE; } } - OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); + char **p = (char **) ZEND_INI_GET_ADDR(); + *p = new_value && ZSTR_LEN(new_value) > 0 ? ZSTR_VAL(new_value) : NULL; return SUCCESS; } /* }}} */ diff --git a/main/network.c b/main/network.c index 14f4ca4dff987..b553714167e7f 100644 --- a/main/network.c +++ b/main/network.c @@ -1036,6 +1036,28 @@ PHPAPI socklen_t php_sockaddr_size(php_sockaddr_storage *addr) } /* }}} */ +#ifdef PHP_WIN32 +char *php_socket_strerror_s(long err, char *buf, size_t bufsize) +{ + if (buf == NULL) { + char ebuf[1024]; + errno_t res = strerror_s(ebuf, sizeof(ebuf), err); + if (res == 0) { + buf = estrdup(ebuf); + } else { + buf = estrdup("Unknown error"); + } + } else { + errno_t res = strerror_s(buf, bufsize, err); + if (res != 0) { + strncpy(buf, "Unknown error", bufsize); + buf[bufsize?(bufsize-1):0] = 0; + } + } + return buf; +} +#endif + /* Given a socket error code, if buf == NULL: * emallocs storage for the error message and returns * else @@ -1045,16 +1067,40 @@ PHPAPI socklen_t php_sockaddr_size(php_sockaddr_storage *addr) PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize) { #ifndef PHP_WIN32 - char *errstr; - - errstr = strerror(err); +# ifdef HAVE_STRERROR_R + if (buf == NULL) { + char ebuf[1024]; +# ifdef STRERROR_R_CHAR_P + char *errstr = strerror_r(err, ebuf, sizeof(ebuf)); + buf = estrdup(errstr); +# else + int res = (int) strerror_r(err, ebuf, sizeof(ebuf)); + if (res == 0) { + buf = estrdup(ebuf); + } else { + buf = estrdup("Unknown error"); + } +# endif + } else { +# ifdef STRERROR_R_CHAR_P + buf = strerror_r(err, buf, bufsize); +# else + int res = (int) strerror_r(err, buf, bufsize); + if (res != 0) { + strncpy(buf, "Unknown error", bufsize); + buf[bufsize?(bufsize-1):0] = 0; + } +# endif + } +# else + char *errstr = strerror(err); if (buf == NULL) { buf = estrdup(errstr); } else { strncpy(buf, errstr, bufsize); buf[bufsize?(bufsize-1):0] = 0; } - return buf; +# endif #else char *sysbuf = php_win32_error_to_msg(err); if (!sysbuf[0]) { @@ -1069,9 +1115,8 @@ PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize) } php_win32_error_msg_free(sysbuf); - - return buf; #endif + return buf; } /* }}} */ @@ -1079,9 +1124,22 @@ PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize) PHPAPI zend_string *php_socket_error_str(long err) { #ifndef PHP_WIN32 - char *errstr; - - errstr = strerror(err); +# ifdef HAVE_STRERROR_R + char ebuf[1024]; +# ifdef STRERROR_R_CHAR_P + char *errstr = strerror_r(err, ebuf, sizeof(ebuf)); +# else + const char *errstr; + int res = (int) strerror_r(err, ebuf, sizeof(ebuf)); + if (res == 0) { + errstr = ebuf; + } else { + errstr = "Unknown error"; + } +# endif +# else + char *errstr = strerror(err); +# endif return zend_string_init(errstr, strlen(errstr), 0); #else zend_string *ret; diff --git a/main/php_network.h b/main/php_network.h index 0a0da4cf7084a..d75689d337efc 100644 --- a/main/php_network.h +++ b/main/php_network.h @@ -64,6 +64,11 @@ * unless buf is not NULL. * Also works sensibly for win32 */ BEGIN_EXTERN_C() +#ifdef PHP_WIN32 +char *php_socket_strerror_s(long err, char *buf, size_t bufsize); +#else +#define php_socket_strerror_s php_socket_strerror +#endif PHPAPI char *php_socket_strerror(long err, char *buf, size_t bufsize); PHPAPI zend_string *php_socket_error_str(long err); END_EXTERN_C() diff --git a/main/php_version.h b/main/php_version.h index dfc15c4c33bc8..1154ef4f1c516 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 4 -#define PHP_RELEASE_VERSION 13 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.13-dev" -#define PHP_VERSION_ID 80413 +#define PHP_RELEASE_VERSION 14 +#define PHP_EXTRA_VERSION "RC1" +#define PHP_VERSION "8.4.14RC1" +#define PHP_VERSION_ID 80414 diff --git a/main/rfc1867.c b/main/rfc1867.c index 84b8788bbf7c3..f6ffb6fabc7f1 100644 --- a/main/rfc1867.c +++ b/main/rfc1867.c @@ -1048,7 +1048,8 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) if (wlen == (size_t)-1) { /* write failed */ #if DEBUG_FILE_UPLOAD - sapi_module.sapi_error(E_NOTICE, "write() failed - %s", strerror(errno)); + char errstr[256]; + sapi_module.sapi_error(E_NOTICE, "write() failed - %s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); #endif cancel_upload = PHP_UPLOAD_ERROR_F; } else if (wlen < blen) { diff --git a/main/streams/filter.c b/main/streams/filter.c index a1d63c15f0a52..1ce03d41513cd 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -173,6 +173,7 @@ PHPAPI void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_st PHPAPI void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket) { + /* TODO: this was added as a bad workaround for bug #35916 and should be removed in the future. */ if (brigade->tail == bucket) { return; } diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 85771eaf5ef3a..2f64c904dce41 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -43,6 +43,10 @@ # include #endif +#ifdef __linux__ +# include +#endif + #define php_stream_fopen_from_fd_int(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC) #define php_stream_fopen_from_fd_int_rel(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_REL_CC) #define php_stream_fopen_from_file_int(file, mode) _php_stream_fopen_from_file_int((file), (mode) STREAMS_CC) @@ -255,7 +259,28 @@ PHPAPI php_stream *_php_stream_fopen_tmpfile(int dummy STREAMS_DC) static void detect_is_seekable(php_stdio_stream_data *self) { #if defined(S_ISFIFO) && defined(S_ISCHR) if (self->fd >= 0 && do_fstat(self, 0) == 0) { +#ifdef __linux__ + if (S_ISCHR(self->sb.st_mode)) { + /* Some character devices are exceptions, check their major/minor ID + * https://www.kernel.org/doc/Documentation/admin-guide/devices.txt */ + if (major(self->sb.st_rdev) == 1) { + unsigned m = minor(self->sb.st_rdev); + self->is_seekable = + m == 1 || /* /dev/mem */ + m == 2 || /* /dev/kmem */ + m == 3 || /* /dev/null */ + m == 4 || /* /dev/port (seekable, offset = I/O port) */ + m == 5 || /* /dev/zero */ + m == 7; /* /dev/full */ + } else { + self->is_seekable = false; + } + } else { + self->is_seekable = !S_ISFIFO(self->sb.st_mode); + } +#else self->is_seekable = !(S_ISFIFO(self->sb.st_mode) || S_ISCHR(self->sb.st_mode)); +#endif self->is_pipe = S_ISFIFO(self->sb.st_mode); } #elif defined(PHP_WIN32) @@ -368,7 +393,9 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun return bytes_written; } if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { - php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", count, errno, strerror(errno)); + char errstr[256]; + php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", + count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } } else { @@ -444,7 +471,9 @@ static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count) /* TODO: Should this be treated as a proper error or not? */ } else { if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { - php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", count, errno, strerror(errno)); + char errstr[256]; + php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", + count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } /* TODO: Remove this special-case? */ @@ -1278,7 +1307,9 @@ static int php_plain_files_unlink(php_stream_wrapper *wrapper, const char *url, ret = VCWD_UNLINK(url); if (ret == -1) { if (options & REPORT_ERRORS) { - php_error_docref1(NULL, url, E_WARNING, "%s", strerror(errno)); + char errstr[256]; + php_error_docref1(NULL, url, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1324,6 +1355,7 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f if (ret == -1) { #ifndef PHP_WIN32 + char errstr[256]; # ifdef EXDEV if (errno == EXDEV) { zend_stat_t sb; @@ -1344,7 +1376,8 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f * access to the file in the meantime. */ if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } @@ -1352,7 +1385,8 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f if (success) { if (VCWD_CHMOD(url_to, sb.st_mode)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } @@ -1363,10 +1397,12 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f VCWD_UNLINK(url_from); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); } # if !defined(ZTS) && !defined(TSRM_WIN32) umask(oldmask); @@ -1379,7 +1415,8 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f #ifdef PHP_WIN32 php_win32_docref2_from_error(GetLastError(), url_from, url_to); #else - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", strerror(errno)); + php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); #endif return 0; } @@ -1459,11 +1496,12 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i if (!p) { p = buf; } + char errstr[256]; while (true) { int ret = VCWD_MKDIR(buf, (mode_t) mode); if (ret < 0 && errno != EEXIST) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); + php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1483,7 +1521,7 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i /* issue a warning to client when the last directory was created failed */ if (ret < 0) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); + php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1502,15 +1540,16 @@ static int php_plain_files_rmdir(php_stream_wrapper *wrapper, const char *url, i return 0; } + char errstr[256]; #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT)); + php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif if (VCWD_RMDIR(url) < 0) { - php_error_docref1(NULL, url, E_WARNING, "%s", strerror(errno)); + php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } @@ -1529,10 +1568,11 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url #endif mode_t mode; int ret = 0; + char errstr[256]; #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", strerror(ENOENT)); + php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif @@ -1551,7 +1591,8 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url if (VCWD_ACCESS(url, F_OK) != 0) { FILE *file = VCWD_FOPEN(url, "w"); if (file == NULL) { - php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, strerror(errno)); + php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, + php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } fclose(file); @@ -1594,7 +1635,8 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url return 0; } if (ret == -1) { - php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", strerror(errno)); + php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", + php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } php_clear_stat_cache(0, NULL, 0); diff --git a/main/streams/streams.c b/main/streams/streams.c index af050cae5242c..201e59446cf41 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -154,6 +154,7 @@ static void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const { char *tmp; char *msg; + char errstr[256]; int free_msg = 0; if (EG(exception)) { @@ -203,7 +204,7 @@ static void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const free_msg = 1; } else { if (wrapper == &php_plain_files_wrapper) { - msg = strerror(errno); /* TODO: not ts on linux */ + msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); } else { msg = "operation failed"; } @@ -1294,7 +1295,7 @@ PHPAPI int _php_stream_flush(php_stream *stream, int closing) { int ret = 0; - if (stream->writefilters.head) { + if (stream->writefilters.head && stream->ops->write) { _php_stream_write_filtered(stream, NULL, 0, closing ? PSFS_FLAG_FLUSH_CLOSE : PSFS_FLAG_FLUSH_INC ); } @@ -2219,7 +2220,6 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod int persistent = options & STREAM_OPEN_PERSISTENT; zend_string *path_str = NULL; zend_string *resolved_path = NULL; - char *copy_of_path = NULL; if (opened_path) { if (options & STREAM_OPEN_FOR_ZEND_STREAM) { @@ -2296,8 +2296,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (stream->orig_path) { pefree(stream->orig_path, persistent); } - copy_of_path = pestrdup(path, persistent); - stream->orig_path = copy_of_path; + stream->orig_path = pestrdup(path, persistent); #if ZEND_DEBUG stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; @@ -2356,11 +2355,6 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod } } php_stream_tidy_wrapper_error_log(wrapper); -#if ZEND_DEBUG - if (stream == NULL && copy_of_path != NULL) { - pefree(copy_of_path, persistent); - } -#endif if (resolved_path) { zend_string_release_ex(resolved_path, 0); } diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index 8623c11be004c..2178e0a39e993 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -678,9 +678,10 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t * if (sock->socket == SOCK_ERR) { if (xparam->want_errortext) { + char errstr[256]; xparam->outputs.error_text = strpprintf(0, "Failed to create unix%s socket %s", stream->ops == &php_stream_unix_socket_ops ? "" : "datagram", - strerror(errno)); + php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return -1; } diff --git a/run-tests.php b/run-tests.php index 4654ead998f9a..c80dd71688fc9 100755 --- a/run-tests.php +++ b/run-tests.php @@ -648,6 +648,12 @@ function main(): void $environment['SKIP_ONLINE_TESTS'] = $online ? '0' : '1'; } + if (!defined('STDIN') || !stream_isatty(STDIN) + || !defined('STDOUT') || !stream_isatty(STDOUT) + || !defined('STDERR') || !stream_isatty(STDERR)) { + $environment['SKIP_IO_CAPTURE_TESTS'] = '1'; + } + if ($selected_tests && count($test_files) === 0) { echo "No tests found.\n"; return; diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 10c077879571a..68c4b3b411d24 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -2711,14 +2711,16 @@ static zend_result php_cli_server_do_event_for_each_fd_callback(void *_params, p struct sockaddr *sa = pemalloc(server->socklen, 1); client_sock = accept(server->server_sock, sa, &socklen); if (!ZEND_VALID_SOCKET(client_sock)) { - int err = php_socket_errno(); - if (err != SOCK_EAGAIN && php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { + pefree(sa, 1); + if (php_socket_errno() == SOCK_EAGAIN) { + return SUCCESS; + } + if (php_cli_server_log_level >= PHP_CLI_SERVER_LOG_ERROR) { char *errstr = php_socket_strerror(php_socket_errno(), NULL, 0); php_cli_server_logf(PHP_CLI_SERVER_LOG_ERROR, "Failed to accept a client (reason: %s)", errstr); efree(errstr); } - pefree(sa, 1); return FAILURE; } if (SUCCESS != php_set_sock_blocking(client_sock, 0)) { diff --git a/sapi/cli/tests/php_cli_server_ipv4_error_message.phpt b/sapi/cli/tests/php_cli_server_ipv4_error_message.phpt index 71280179cccfc..d00dd3ec9278f 100644 --- a/sapi/cli/tests/php_cli_server_ipv4_error_message.phpt +++ b/sapi/cli/tests/php_cli_server_ipv4_error_message.phpt @@ -2,6 +2,9 @@ IPv4 address error message formatting --SKIPIF-- --FILE-- +--EXPECT-- +string(0) "" +string(6) "syslog"