diff --git a/.github/actions/apt-x32/action.yml b/.github/actions/apt-x32/action.yml index 2cde60fce55b0..879300f992747 100644 --- a/.github/actions/apt-x32/action.yml +++ b/.github/actions/apt-x32/action.yml @@ -13,6 +13,7 @@ runs: apt-get install -y \ autoconf \ bison \ + curl \ g++-multilib \ gcc-multilib \ language-pack-de \ diff --git a/.github/actions/brew/action.yml b/.github/actions/brew/action.yml index 8bdbeec7ab6b8..51d37aa56d470 100644 --- a/.github/actions/brew/action.yml +++ b/.github/actions/brew/action.yml @@ -12,6 +12,7 @@ runs: re2c brew install \ openssl@1.1 \ + curl \ krb5 \ bzip2 \ enchant \ diff --git a/.github/actions/configure-macos/action.yml b/.github/actions/configure-macos/action.yml index 57a9b63a06ea2..317d705645985 100644 --- a/.github/actions/configure-macos/action.yml +++ b/.github/actions/configure-macos/action.yml @@ -11,6 +11,7 @@ runs: set -x export PATH="/usr/local/opt/bison/bin:$PATH" export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/openssl@1.1/lib/pkgconfig" + export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/curl/lib/pkgconfig" export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/krb5/lib/pkgconfig" export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/libffi/lib/pkgconfig" export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/libxml2/lib/pkgconfig" diff --git a/.github/actions/notify-slack/action.yml b/.github/actions/notify-slack/action.yml new file mode 100644 index 0000000000000..1ff425b51c6ac --- /dev/null +++ b/.github/actions/notify-slack/action.yml @@ -0,0 +1,10 @@ +name: Notify Slack +inputs: + token: + required: true +runs: + using: composite + steps: + - shell: bash + run: >- + curl -X POST -H 'Content-type: application/json' --data '{"attachments": [{"text": "Job in *nightly* failed", "footer": "", "color": "danger", "mrkdwn_in": ["text"]}]}' ${{ inputs.token }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 22dbe8d061b9b..63fce11ac0225 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -30,6 +30,11 @@ jobs: - name: Generate Matrix id: set-matrix run: php .github/nightly_matrix.php "${{ github.event_name }}" "${{ github.run_attempt }}" + - name: Notify Slack + if: failure() + uses: ./.github/actions/notify-slack + with: + token: ${{ secrets.ACTION_MONITORING_SLACK }} LINUX_X64: needs: GENERATE_MATRIX if: ${{ needs.GENERATE_MATRIX.outputs.branches != '[]' }} @@ -104,6 +109,11 @@ jobs: -d opcache.jit=1205 - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files + - name: Notify Slack + if: failure() + uses: ./.github/actions/notify-slack + with: + token: ${{ secrets.ACTION_MONITORING_SLACK }} LINUX_X32: needs: GENERATE_MATRIX if: ${{ needs.GENERATE_MATRIX.outputs.branches != '[]' }} @@ -175,6 +185,11 @@ jobs: -d opcache.enable_cli=1 -d opcache.jit_buffer_size=16M -d opcache.jit=1205 + - name: Notify Slack + if: failure() + uses: ./.github/actions/notify-slack + with: + token: ${{ secrets.ACTION_MONITORING_SLACK }} MACOS: needs: GENERATE_MATRIX if: ${{ needs.GENERATE_MATRIX.outputs.branches != '[]' }} @@ -233,6 +248,11 @@ jobs: -d opcache.jit=1205 - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files + - name: Notify Slack + if: failure() + uses: ./.github/actions/notify-slack + with: + token: ${{ secrets.ACTION_MONITORING_SLACK }} COVERAGE_DEBUG_NTS: if: github.repository_owner == 'php' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-20.04 @@ -267,6 +287,11 @@ jobs: - name: Upload Test Coverage to Codecov.io if: always() run: bash <(curl -s https://codecov.io/bash) + - name: Notify Slack + if: failure() + uses: ./.github/actions/notify-slack + with: + token: ${{ secrets.ACTION_MONITORING_SLACK }} COMMUNITY: needs: GENERATE_MATRIX if: ${{ needs.GENERATE_MATRIX.outputs.branches != '[]' }} @@ -382,6 +407,11 @@ jobs: if [ $EXIT_CODE -gt 128 ]; then exit 1 fi + - name: Notify Slack + if: failure() + uses: ./.github/actions/notify-slack + with: + token: ${{ secrets.ACTION_MONITORING_SLACK }} OPCACHE_VARIATION: needs: GENERATE_MATRIX if: ${{ needs.GENERATE_MATRIX.outputs.branches != '[]' }} @@ -453,6 +483,11 @@ jobs: -d opcache.file_cache_only=1 - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files + - name: Notify Slack + if: failure() + uses: ./.github/actions/notify-slack + with: + token: ${{ secrets.ACTION_MONITORING_SLACK }} MSAN: needs: GENERATE_MATRIX if: ${{ needs.GENERATE_MATRIX.outputs.branches != '[]' }} @@ -545,6 +580,11 @@ jobs: -d opcache.enable_cli=1 - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files + - name: Notify Slack + if: failure() + uses: ./.github/actions/notify-slack + with: + token: ${{ secrets.ACTION_MONITORING_SLACK }} LIBMYSQLCLIENT: needs: GENERATE_MATRIX if: ${{ needs.GENERATE_MATRIX.outputs.branches != '[]' }} @@ -600,3 +640,8 @@ jobs: withMysqli: ${{ matrix.branch.ref == 'PHP-8.1' }} - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files + - name: Notify Slack + if: failure() + uses: ./.github/actions/notify-slack + with: + token: ${{ secrets.ACTION_MONITORING_SLACK }} diff --git a/NEWS b/NEWS index fb5882a513443..2c177710bf783 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,39 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.1.18 +11 May 2023, PHP 8.1.19 + +- Core: + . Fix inconsistent float negation in constant expressions. (ilutov) + . Fixed bug GH-8841 (php-cli core dump calling a badly formed function). + (nielsdos) + . Fixed bug GH-10737 (PHP 8.1.16 segfaults on line 597 of + sapi/apache2handler/sapi_apache2.c). (nielsdos, ElliotNB) + . Fixed bug GH-11028 (Heap Buffer Overflow in zval_undefined_cv.). (nielsdos) + . Fixed bug GH-11108 (Incorrect CG(memoize_mode) state after bailout in ??=). + (ilutov) + +- DOM: + . Fixed bug #80602 (Segfault when using DOMChildNode::before()). + (Nathan Freeman) + . Fixed incorrect error handling in dom_zvals_to_fragment(). (nielsdos) + +- Exif: + . Fixed bug GH-9397 (exif read : warnings and errors : Potentially invalid + endianess, Illegal IFD size and Undefined index). (nielsdos) + +- Intl: + . Fixed bug GH-11071 (TZData version not displayed anymore). (Remi) + +- PCRE: + . Fixed bug GH-10968 (Segfault in preg_replace_callback_array()). (ilutov) + +- Standard: + . Fixed bug GH-10990 (mail() throws TypeError after iterating over + $additional_headers array by reference). (nielsdos) + . Fixed bug GH-9775 (Duplicates returned by array_unique when using enums). + (ilutov) + +13 Apr 2023, PHP 8.1.18 - Core: . Added optional support for max_execution_time in ZTS/Linux builds diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c index 7cd924318ed4c..62be0a4214815 100644 --- a/TSRM/TSRM.c +++ b/TSRM/TSRM.c @@ -161,6 +161,23 @@ TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debu return 1; }/*}}}*/ +static void ts_free_resources(tsrm_tls_entry *thread_resources) +{ + /* Need to destroy in reverse order to respect dependencies. */ + for (int i = thread_resources->count - 1; i >= 0; i--) { + if (!resource_types_table[i].done) { + if (resource_types_table[i].dtor) { + resource_types_table[i].dtor(thread_resources->storage[i]); + } + + if (!resource_types_table[i].fast_offset) { + free(thread_resources->storage[i]); + } + } + } + + free(thread_resources->storage); +} /* Shutdown TSRM (call once for the entire process) */ TSRM_API void tsrm_shutdown(void) @@ -183,25 +200,13 @@ TSRM_API void tsrm_shutdown(void) tsrm_tls_entry *p = tsrm_tls_table[i], *next_p; while (p) { - int j; - next_p = p->next; - for (j=0; jcount; j++) { - if (p->storage[j]) { - if (resource_types_table) { - if (!resource_types_table[j].done) { - if (resource_types_table[j].dtor) { - resource_types_table[j].dtor(p->storage[j]); - } - - if (!resource_types_table[j].fast_offset) { - free(p->storage[j]); - } - } - } - } + if (resource_types_table) { + /* This call will already free p->storage for us */ + ts_free_resources(p); + } else { + free(p->storage); } - free(p->storage); free(p); p = next_p; } @@ -367,7 +372,13 @@ TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, siz return *rsrc_id; }/*}}}*/ +static void set_thread_local_storage_resource_to(tsrm_tls_entry *thread_resource) +{ + tsrm_tls_set(thread_resource); + TSRMLS_CACHE = thread_resource; +} +/* Must be called with tsmm_mutex held */ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id) {/*{{{*/ int i; @@ -383,8 +394,7 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_ (*thread_resources_ptr)->next = NULL; /* Set thread local storage to this new thread resources structure */ - tsrm_tls_set(*thread_resources_ptr); - TSRMLS_CACHE = *thread_resources_ptr; + set_thread_local_storage_resource_to(*thread_resources_ptr); if (tsrm_new_thread_begin_handler) { tsrm_new_thread_begin_handler(thread_id); @@ -407,17 +417,14 @@ static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_ if (tsrm_new_thread_end_handler) { tsrm_new_thread_end_handler(thread_id); } - - tsrm_mutex_unlock(tsmm_mutex); }/*}}}*/ - /* fetches the requested resource for the current thread */ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) {/*{{{*/ THREAD_T thread_id; int hash_value; - tsrm_tls_entry *thread_resources; + tsrm_tls_entry *thread_resources, **last_thread_resources; if (!th_id) { /* Fast path for looking up the resources for the current @@ -448,25 +455,55 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) if (!thread_resources) { allocate_new_resource(&tsrm_tls_table[hash_value], thread_id); + tsrm_mutex_unlock(tsmm_mutex); return ts_resource_ex(id, &thread_id); } else { - do { - if (thread_resources->thread_id == thread_id) { - break; - } + last_thread_resources = &tsrm_tls_table[hash_value]; + while (thread_resources->thread_id != thread_id) { + last_thread_resources = &thread_resources->next; if (thread_resources->next) { thread_resources = thread_resources->next; } else { allocate_new_resource(&thread_resources->next, thread_id); + tsrm_mutex_unlock(tsmm_mutex); return ts_resource_ex(id, &thread_id); - /* - * thread_resources = thread_resources->next; - * break; - */ } - } while (thread_resources); + } + } + + /* It's possible that the current thread resources are requested, and that we get here. + * This means that the TSRM key pointer and cached pointer are NULL, but there is still + * a thread resource associated with this ID in the hashtable. This can occur if a thread + * goes away, but its resources are never cleaned up, and then that thread ID is reused. + * Since we don't always have a way to know when a thread goes away, we can't clean up + * the thread's resources before the new thread spawns. + * To solve this issue, we'll free up the old thread resources gracefully (gracefully + * because there might still be resources open like database connection which need to + * be shut down cleanly). After freeing up, we'll create the new resources for this thread + * as if the stale resources never existed in the first place. From that point forward, + * it is as if that situation never occurred. + * The fact that this situation happens isn't that bad because a child process containing + * threads will eventually be respawned anyway by the SAPI, so the stale threads won't last + * forever. */ + TSRM_ASSERT(thread_resources->thread_id == thread_id); + if (thread_id == tsrm_thread_id() && !tsrm_tls_get()) { + tsrm_tls_entry *next = thread_resources->next; + /* In case that extensions don't use the pointer passed from the dtor, but incorrectly + * use the global pointer, we need to setup the global pointer temporarily here. */ + set_thread_local_storage_resource_to(thread_resources); + /* Free up the old resource from the old thread instance */ + ts_free_resources(thread_resources); + free(thread_resources); + /* Allocate a new resource at the same point in the linked list, and relink the next pointer */ + allocate_new_resource(last_thread_resources, thread_id); + thread_resources = *last_thread_resources; + thread_resources->next = next; + /* We don't have to tail-call ts_resource_ex, we can take the fast path to the return + * because we already have the correct pointer. */ } + tsrm_mutex_unlock(tsmm_mutex); + /* Read a specific resource from the thread's resources. * This is called outside of a mutex, so have to be aware about external * changes to the structure as we read it. @@ -479,7 +516,6 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) void ts_free_thread(void) {/*{{{*/ tsrm_tls_entry *thread_resources; - int i; THREAD_T thread_id = tsrm_thread_id(); int hash_value; tsrm_tls_entry *last=NULL; @@ -492,17 +528,7 @@ void ts_free_thread(void) while (thread_resources) { if (thread_resources->thread_id == thread_id) { - for (i=0; icount; i++) { - if (resource_types_table[i].dtor) { - resource_types_table[i].dtor(thread_resources->storage[i]); - } - } - for (i=0; icount; i++) { - if (!resource_types_table[i].fast_offset) { - free(thread_resources->storage[i]); - } - } - free(thread_resources->storage); + ts_free_resources(thread_resources); if (last) { last->next = thread_resources->next; } else { diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index f85a1adea701e..ba7c302582f64 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3436,6 +3436,9 @@ static zend_always_inline int _zend_update_type_info( tmp |= zend_fetch_prop_type(script, prop_info, &ce); if (opline->result_type == IS_VAR) { tmp |= MAY_BE_REF | MAY_BE_INDIRECT; + if ((opline->extended_value & ZEND_FETCH_OBJ_FLAGS) == ZEND_FETCH_DIM_WRITE) { + tmp |= MAY_BE_UNDEF; + } } else if (!(opline->op1_type & (IS_VAR|IS_TMP_VAR)) || !(t1 & MAY_BE_RC1)) { zend_class_entry *ce = NULL; @@ -3473,6 +3476,9 @@ static zend_always_inline int _zend_update_type_info( zend_fetch_static_prop_info(script, op_array, ssa, opline), &ce); if (opline->result_type == IS_VAR) { tmp |= MAY_BE_REF | MAY_BE_INDIRECT; + if ((opline->extended_value & ZEND_FETCH_OBJ_FLAGS) == ZEND_FETCH_DIM_WRITE) { + tmp |= MAY_BE_UNDEF; + } } else { if (!result_may_be_separated(ssa, ssa_op)) { tmp &= ~MAY_BE_RC1; diff --git a/Zend/tests/arginfo_zpp_mismatch.inc b/Zend/tests/arginfo_zpp_mismatch.inc index 221c347aaa915..023bfefa5d501 100644 --- a/Zend/tests/arginfo_zpp_mismatch.inc +++ b/Zend/tests/arginfo_zpp_mismatch.inc @@ -9,6 +9,7 @@ function skipFunction($function): bool { /* intentionally violate invariants */ || $function === 'zend_create_unterminated_string' || $function === 'zend_test_array_return' + || $function === 'zend_test_crash' || $function === 'zend_leak_bytes' /* mess with output */ || (is_string($function) && str_starts_with($function, 'ob_')) diff --git a/Zend/tests/bug39542.phpt b/Zend/tests/bug39542.phpt index 79c1c56e9185b..d2023673ef0b5 100644 --- a/Zend/tests/bug39542.phpt +++ b/Zend/tests/bug39542.phpt @@ -1,5 +1,7 @@ --TEST-- Bug #39542 (Behaviour of require_once/include_once different to < 5.2.0) +--INI-- +error_log= --FILE-- 0; + } finally { + return []; + } +} + +function test($msg, $x) { + echo "yield $msg\n"; + try { + var_dump([...generator($x)]); + } catch (Throwable $e) { + echo $e->getMessage(), "\n"; + } +} + +test("null", null); +test("false", false); +test("true", true); +test("object", new stdClass); +?> +--EXPECT-- +yield null +Keys must be of type int|string during array unpacking +yield false +Keys must be of type int|string during array unpacking +yield true +Keys must be of type int|string during array unpacking +yield object +Keys must be of type int|string during array unpacking diff --git a/Zend/tests/generators/gh11028_2.phpt b/Zend/tests/generators/gh11028_2.phpt new file mode 100644 index 0000000000000..27b36c711f5c1 --- /dev/null +++ b/Zend/tests/generators/gh11028_2.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-11028 (Heap Buffer Overflow in zval_undefined_cv with generators) - original variant +--FILE-- + 0; + } finally { + return []; + } + })()), + ]; +})()[0]; +?> +--EXPECTF-- +Warning: Undefined variable $a in %s on line %d + +Fatal error: Uncaught Error: Keys must be of type int|string during array unpacking in %s:%d +Stack trace: +#0 %s(%d): {closure}() +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/generators/gh11028_3.phpt b/Zend/tests/generators/gh11028_3.phpt new file mode 100644 index 0000000000000..7ea1aac6f6cfc --- /dev/null +++ b/Zend/tests/generators/gh11028_3.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-11028 (Heap Buffer Overflow in zval_undefined_cv with generators) - throw in finally variant +--FILE-- + 0; + } finally { + throw new Exception("exception"); + return []; + } +} + +try { + var_dump([...generator()]); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +exception diff --git a/Zend/tests/gh11108.phpt b/Zend/tests/gh11108.phpt new file mode 100644 index 0000000000000..efbd12dc367fa --- /dev/null +++ b/Zend/tests/gh11108.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-11108: Incorrect CG(memoize_mode) state after bailout in ??= +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use [] for reading in %s on line %d diff --git a/Zend/tests/gh11108_shutdown.inc b/Zend/tests/gh11108_shutdown.inc new file mode 100644 index 0000000000000..34f8131d4a840 --- /dev/null +++ b/Zend/tests/gh11108_shutdown.inc @@ -0,0 +1,5 @@ + +--EXPECT-- +array(1) { + [0]=> + &string(4) "test" +} diff --git a/Zend/tests/gh8841.phpt b/Zend/tests/gh8841.phpt new file mode 100644 index 0000000000000..d99ca62c28773 --- /dev/null +++ b/Zend/tests/gh8841.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-8841 (php-cli core dump calling a badly formed function) +--FILE-- + +--EXPECTF-- +Fatal error: A void function must not return a value in %s on line %d +Before calling g() +After calling g() +Before calling f() + +Fatal error: Uncaught Error: Call to undefined function f() in %s:%d +Stack trace: +#0 [internal function]: {closure}() +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/gh9775_1.phpt b/Zend/tests/gh9775_1.phpt new file mode 100644 index 0000000000000..e2ea5287f5df9 --- /dev/null +++ b/Zend/tests/gh9775_1.phpt @@ -0,0 +1,56 @@ +--TEST-- +GH-9775: Backed enum in array_unique() +--FILE-- + +--EXPECT-- +array(8) { + [0]=> + enum(Test::COURSES_ADMIN) + [1]=> + enum(Test::COURSES_REPORTING_ACCESS) + [2]=> + enum(Test::BUNDLES_ADMIN) + [3]=> + enum(Test::USERS_ADMIN) + [4]=> + enum(Test::B2B_DASHBOARD_ACCESS) + [6]=> + enum(Test::INSTRUCTORS_ADMIN) + [8]=> + enum(Test::COUPONS_ADMIN) + [9]=> + enum(Test::AUTHENTICATED) +} diff --git a/Zend/tests/gh9775_2.phpt b/Zend/tests/gh9775_2.phpt new file mode 100644 index 0000000000000..94ef0029fa93d --- /dev/null +++ b/Zend/tests/gh9775_2.phpt @@ -0,0 +1,56 @@ +--TEST-- +GH-9775: Pure enum in array_unique() +--FILE-- + +--EXPECT-- +array(8) { + [0]=> + enum(Test::COURSES_ADMIN) + [1]=> + enum(Test::COURSES_REPORTING_ACCESS) + [2]=> + enum(Test::BUNDLES_ADMIN) + [3]=> + enum(Test::USERS_ADMIN) + [4]=> + enum(Test::B2B_DASHBOARD_ACCESS) + [6]=> + enum(Test::INSTRUCTORS_ADMIN) + [8]=> + enum(Test::COUPONS_ADMIN) + [9]=> + enum(Test::AUTHENTICATED) +} diff --git a/Zend/tests/oss_fuzz_58181.phpt b/Zend/tests/oss_fuzz_58181.phpt new file mode 100644 index 0000000000000..36a0ba16d623e --- /dev/null +++ b/Zend/tests/oss_fuzz_58181.phpt @@ -0,0 +1,14 @@ +--TEST-- +oss-fuzz #58181: Fix unexpected reference returned from CallbackFilterIterator::accept() +--FILE-- + true); + $iterator->rewind(); +} + +test(['a', 'b']); +?> +--EXPECTF-- +Notice: Only variable references should be returned by reference in %s on line %d diff --git a/Zend/tests/unary_minus_const_expr_consistency.phpt b/Zend/tests/unary_minus_const_expr_consistency.phpt new file mode 100644 index 0000000000000..c8175df8bed3a --- /dev/null +++ b/Zend/tests/unary_minus_const_expr_consistency.phpt @@ -0,0 +1,16 @@ +--TEST-- +Unary minus constant expression consistency +--FILE-- + +--EXPECT-- +float(-0) +float(-0) diff --git a/Zend/zend.c b/Zend/zend.c index b14b2c701a5b2..a1b4e3ee39730 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1192,6 +1192,7 @@ ZEND_API ZEND_COLD ZEND_NORETURN void _zend_bailout(const char *filename, uint32 CG(unclean_shutdown) = 1; CG(active_class_entry) = NULL; CG(in_compilation) = 0; + CG(memoize_mode) = 0; EG(current_execute_data) = NULL; LONGJMP(*EG(bailout), FAILURE); } diff --git a/Zend/zend.h b/Zend/zend.h index 5633f935628f3..b35c92a84ce9e 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.1.18-dev" +#define ZEND_VERSION "4.1.19" #define ZEND_ENGINE_3 diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 0e5e5ebac1688..409993c89edfa 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -662,8 +662,8 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[0], scope) != SUCCESS)) { ret = FAILURE; } else { - ZVAL_LONG(&op1, 0); - ret = sub_function(result, &op1, &op2); + ZVAL_LONG(&op1, -1); + ret = mul_function(result, &op1, &op2); zval_ptr_dtor_nogc(&op2); } break; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 0774fb6d19a25..14888722e13cc 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7126,7 +7126,7 @@ static uint32_t zend_add_dynamic_func_def(zend_op_array *def) { return def_offset; } -static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, bool toplevel) /* {{{ */ +static zend_string *zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_ast_decl *decl, bool toplevel) /* {{{ */ { zend_string *unqualified_name, *name, *lcname; zend_op *opline; @@ -7157,11 +7157,7 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as zend_register_seen_symbol(lcname, ZEND_SYMBOL_FUNCTION); if (toplevel) { - if (UNEXPECTED(zend_hash_add_ptr(CG(function_table), lcname, op_array) == NULL)) { - do_bind_function_error(lcname, op_array, 1); - } - zend_string_release_ex(lcname, 0); - return; + return lcname; } uint32_t func_ref = zend_add_dynamic_func_def(op_array); @@ -7175,7 +7171,8 @@ static void zend_begin_func_decl(znode *result, zend_op_array *op_array, zend_as LITERAL_STR(opline->op1, zend_string_copy(lcname)); opline->op2.num = func_ref; } - zend_string_release_ex(lcname, 0); + + return lcname; } /* }}} */ @@ -7187,7 +7184,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) zend_ast *stmt_ast = decl->child[2]; zend_ast *return_type_ast = decl->child[3]; bool is_method = decl->kind == ZEND_AST_METHOD; - zend_string *method_lcname = NULL; + zend_string *lcname; zend_class_entry *orig_class_entry = CG(active_class_entry); zend_op_array *orig_op_array = CG(active_op_array); @@ -7219,9 +7216,9 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) if (is_method) { bool has_body = stmt_ast != NULL; - method_lcname = zend_begin_method_decl(op_array, decl->name, has_body); + lcname = zend_begin_method_decl(op_array, decl->name, has_body); } else { - zend_begin_func_decl(result, op_array, decl, toplevel); + lcname = zend_begin_func_decl(result, op_array, decl, toplevel); if (decl->kind == ZEND_AST_ARROW_FUNC) { find_implicit_binds(&info, params_ast, stmt_ast); compile_implicit_lexical_binds(&info, result, op_array); @@ -7264,7 +7261,7 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) } zend_compile_params(params_ast, return_type_ast, - is_method && zend_string_equals_literal(method_lcname, ZEND_TOSTRING_FUNC_NAME) ? IS_STRING : 0); + is_method && zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME) ? IS_STRING : 0); if (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { zend_mark_function_as_generator(); zend_emit_op(NULL, ZEND_GENERATOR_CREATE, NULL, NULL); @@ -7280,9 +7277,14 @@ static void zend_compile_func_decl(znode *result, zend_ast *ast, bool toplevel) if (is_method) { CG(zend_lineno) = decl->start_lineno; zend_check_magic_method_implementation( - CG(active_class_entry), (zend_function *) op_array, method_lcname, E_COMPILE_ERROR); - zend_string_release_ex(method_lcname, 0); + CG(active_class_entry), (zend_function *) op_array, lcname, E_COMPILE_ERROR); + } else if (toplevel) { + /* Only register the function after a successful compile */ + if (UNEXPECTED(zend_hash_add_ptr(CG(function_table), lcname, op_array) == NULL)) { + do_bind_function_error(lcname, op_array, true); + } } + zend_string_release_ex(lcname, 0); /* put the implicit return on the really last line */ CG(zend_lineno) = decl->end_lineno; diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index a46a93ad96fcc..ad4396e5d9e07 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -3087,7 +3087,7 @@ static zend_never_inline bool zend_handle_fetch_obj_flags( return 1; } -static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type, uint32_t flags, bool init_undef OPLINE_DC EXECUTE_DATA_DC) +static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type, uint32_t flags OPLINE_DC EXECUTE_DATA_DC) { zval *ptr; zend_object *zobj; @@ -3203,9 +3203,6 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c } } } - if (init_undef && UNEXPECTED(Z_TYPE_P(ptr) == IS_UNDEF)) { - ZVAL_NULL(ptr); - } end: if (prop_op_type != IS_CONST) { @@ -3219,7 +3216,7 @@ static zend_always_inline void zend_assign_to_property_reference(zval *container void **cache_addr = (prop_op_type == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_RETURNS_FUNCTION) : NULL; zend_fetch_property_address(variable_ptr, container, container_op_type, prop_ptr, prop_op_type, - cache_addr, BP_VAR_W, 0, 0 OPLINE_CC EXECUTE_DATA_CC); + cache_addr, BP_VAR_W, 0 OPLINE_CC EXECUTE_DATA_CC); if (EXPECTED(Z_TYPE_P(variable_ptr) == IS_INDIRECT)) { variable_ptr = Z_INDIRECT_P(variable_ptr); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 5d7bef3854f5e..a3610fa8f7b13 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -279,14 +279,25 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ ZEND_CALL_VAR(ex, ex->func->op_array.opcodes[try_catch->finally_end].op1.var); zend_generator_cleanup_unfinished_execution(generator, ex, try_catch->finally_op); - Z_OBJ_P(fast_call) = EG(exception); + zend_object *old_exception = EG(exception); + const zend_op *old_opline_before_exception = EG(opline_before_exception); EG(exception) = NULL; + Z_OBJ_P(fast_call) = NULL; Z_OPLINE_NUM_P(fast_call) = (uint32_t)-1; ex->opline = &ex->func->op_array.opcodes[try_catch->finally_op]; generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; zend_generator_resume(generator); + if (old_exception) { + EG(opline_before_exception) = old_opline_before_exception; + if (EG(exception)) { + zend_exception_set_previous(EG(exception), old_exception); + } else { + EG(exception) = old_exception; + } + } + /* TODO: If we hit another yield inside try/finally, * should we also jump to the next finally block? */ break; diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 60d27b7c91145..8f75aa0bd6903 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1037,6 +1037,8 @@ ZEND_API zval *zend_std_get_property_ptr_ptr(zend_object *zobj, zend_string *nam } else if (prop_info && UNEXPECTED(prop_info->flags & ZEND_ACC_READONLY)) { /* Readonly property, delegate to read_property + write_property. */ retval = NULL; + } else if (!prop_info || !ZEND_TYPE_IS_SET(prop_info->type)) { + ZVAL_NULL(retval); } } else { /* we do have getter - fail and let it try again with usual get/set */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index e6ace6ce3a9ae..a2db0e30da7ac 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2157,7 +2157,7 @@ ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, FETCH zend_fetch_property_address( result, container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC); FREE_OP2(); if (OP1_TYPE == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -2174,7 +2174,7 @@ ZEND_VM_HANDLER(88, ZEND_FETCH_OBJ_RW, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACH container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW); property = GET_OP2_ZVAL_PTR(BP_VAR_R); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC); FREE_OP2(); if (OP1_TYPE == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -2321,7 +2321,7 @@ ZEND_VM_HANDLER(97, ZEND_FETCH_OBJ_UNSET, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, C container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_UNSET); property = GET_OP2_ZVAL_PTR(BP_VAR_R); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0 OPLINE_CC EXECUTE_DATA_CC); FREE_OP2(); if (OP1_TYPE == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 61921b608aaf3..388d19e3d692b 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -22808,7 +22808,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_CONST_HAN zend_fetch_property_address( result, container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -22825,7 +22825,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_CONST_HA container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); property = RT_CONSTANT(opline, opline->op2); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -22859,7 +22859,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CONST container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); property = RT_CONSTANT(opline, opline->op2); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_VAR, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -25506,7 +25506,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_TMPVAR_HA zend_fetch_property_address( result, container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -25523,7 +25523,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_TMPVAR_H container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); property = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -25557,7 +25557,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_TMPVA container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); property = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_VAR, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -29714,7 +29714,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_VAR_CV_HANDLE zend_fetch_property_address( result, container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -29731,7 +29731,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_VAR_CV_HANDL container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); property = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -29765,7 +29765,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_VAR_CV_HA container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); property = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_VAR, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_VAR == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -32151,7 +32151,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_CONST_ zend_fetch_property_address( result, container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -32168,7 +32168,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CONST container = &EX(This); property = RT_CONSTANT(opline, opline->op2); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -32315,7 +32315,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CO container = &EX(This); property = RT_CONSTANT(opline, opline->op2); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_UNUSED, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -34004,7 +34004,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_TMPVAR zend_fetch_property_address( result, container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -34021,7 +34021,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_TMPVA container = &EX(This); property = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -34168,7 +34168,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_TM container = &EX(This); property = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_UNUSED, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -36474,7 +36474,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_UNUSED_CV_HAN zend_fetch_property_address( result, container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -36491,7 +36491,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_UNUSED_CV_HA container = &EX(This); property = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -36638,7 +36638,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_UNUSED_CV container = &EX(This); property = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_UNUSED, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_UNUSED == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -40674,7 +40674,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_CONST_HAND zend_fetch_property_address( result, container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -40691,7 +40691,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_CONST_HAN container = EX_VAR(opline->op1.var); property = RT_CONSTANT(opline, opline->op2); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -40838,7 +40838,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_CONST_ container = EX_VAR(opline->op1.var); property = RT_CONSTANT(opline, opline->op2); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_CV, property, IS_CONST, ((IS_CONST == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -44452,7 +44452,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_TMPVAR_HAN zend_fetch_property_address( result, container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -44469,7 +44469,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_TMPVAR_HA container = EX_VAR(opline->op1.var); property = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -44616,7 +44616,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_TMPVAR container = EX_VAR(opline->op1.var); property = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_CV, property, (IS_TMP_VAR|IS_VAR), (((IS_TMP_VAR|IS_VAR) == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0 OPLINE_CC EXECUTE_DATA_CC); zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -49767,7 +49767,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_W_SPEC_CV_CV_HANDLER zend_fetch_property_address( result, container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) : NULL), - BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS, 1 OPLINE_CC EXECUTE_DATA_CC); + BP_VAR_W, opline->extended_value & ZEND_FETCH_OBJ_FLAGS OPLINE_CC EXECUTE_DATA_CC); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -49784,7 +49784,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_RW_SPEC_CV_CV_HANDLE container = EX_VAR(opline->op1.var); property = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); @@ -49931,7 +49931,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_OBJ_UNSET_SPEC_CV_CV_HAN container = EX_VAR(opline->op1.var); property = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); result = EX_VAR(opline->result.var); - zend_fetch_property_address(result, container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC); + zend_fetch_property_address(result, container, IS_CV, property, IS_CV, ((IS_CV == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0 OPLINE_CC EXECUTE_DATA_CC); if (IS_CV == IS_VAR) { FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(opline->op1.var); diff --git a/configure.ac b/configure.ac index f4ffc8b00ee32..fd6fe3f24bbf8 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.1.18-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.1.19],[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/tests/CONFLICTS b/ext/curl/tests/CONFLICTS index 13368f82902d8..0702cb5bfbb01 100644 --- a/ext/curl/tests/CONFLICTS +++ b/ext/curl/tests/CONFLICTS @@ -1 +1 @@ -curl +all diff --git a/ext/curl/tests/bug45161.phpt b/ext/curl/tests/bug45161.phpt index 9ba8f5f90249c..7e67b6d741ed5 100644 --- a/ext/curl/tests/bug45161.phpt +++ b/ext/curl/tests/bug45161.phpt @@ -2,6 +2,10 @@ Bug #45161 (Reusing a curl handle leaks memory) --EXTENSIONS-- curl +--SKIPIF-- + --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- ---XFAIL-- -Various bugs exist --FILE-- add($expect_interval); $result_end_date = $start->format('Y-m-d H:i:s T'); echo "ADD: $start_date + $expect_spec = **$result_end_date**\n"; - // echo "ADD: $start_date + $expect_spec = **$end_date**\n"; } if (PHPT_DATETIME_SHOW == PHPT_DATETIME_SHOW_SUB) { $end->sub($expect_interval); diff --git a/ext/date/tests/rfc-datetime_and_daylight_saving_time-type3-fa.phpt b/ext/date/tests/rfc-datetime_and_daylight_saving_time-type3-fa.phpt index df67b5a89db75..45d0d13ce921a 100644 --- a/ext/date/tests/rfc-datetime_and_daylight_saving_time-type3-fa.phpt +++ b/ext/date/tests/rfc-datetime_and_daylight_saving_time-type3-fa.phpt @@ -25,6 +25,12 @@ $interval = new DateInterval($interval_spec); echo 'fa2 ' . $start->format($date_format) . " + $interval_spec = " . $start->add($interval)->format($date_format) . "\n"; +$start = new DateTime('2010-03-13 04:30:00'); +$interval_spec = 'PT23H'; +$interval = new DateInterval($interval_spec); +echo 'fa2.5 ' . $start->format($date_format) . " + $interval_spec = " + . $start->add($interval)->format($date_format) . "\n"; + $start = new DateTime('2010-03-13 04:30:00'); $interval_spec = 'PT22H'; $interval = new DateInterval($interval_spec); @@ -52,6 +58,7 @@ echo 'fa6 ' . $start->format($date_format) . " + $interval_spec = " --EXPECT-- fa1 2010-03-14 01:59:59 EST America/New_York + PT1S = 2010-03-14 03:00:00 EDT America/New_York fa2 2010-03-13 04:30:00 EST America/New_York + P1D = 2010-03-14 04:30:00 EDT America/New_York +fa2.5 2010-03-13 04:30:00 EST America/New_York + PT23H = 2010-03-14 04:30:00 EDT America/New_York fa3 2010-03-13 04:30:00 EST America/New_York + PT22H = 2010-03-14 03:30:00 EDT America/New_York fa4 2010-03-13 04:30:00 EST America/New_York + PT21H = 2010-03-14 01:30:00 EST America/New_York fa5 2010-03-13 01:30:00 EST America/New_York + P1D = 2010-03-14 01:30:00 EST America/New_York diff --git a/ext/dom/parentnode.c b/ext/dom/parentnode.c index 3d7e5e44a4b2c..cf823057d22ae 100644 --- a/ext/dom/parentnode.c +++ b/ext/dom/parentnode.c @@ -162,9 +162,8 @@ xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNod newNode = dom_object_get_node(newNodeObj); if (newNode->doc != documentNode) { - xmlFree(fragment); php_dom_throw_error(WRONG_DOCUMENT_ERR, stricterror); - return NULL; + goto err; } if (newNode->parent != NULL) { @@ -175,25 +174,29 @@ xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNod xmlSetTreeDoc(newNode, documentNode); if (newNode->type == XML_ATTRIBUTE_NODE) { - xmlFree(fragment); + goto hierarchy_request_err; + } - php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror); - return NULL; + /* + * xmlNewDocText function will always returns same address to the second parameter if the parameters are greater than or equal to three. + * If it's text, that's fine, but if it's an object, it can cause invalid pointer because many new nodes point to the same memory address. + * So we must copy the new node to avoid this situation. + */ + if (nodesc > 1) { + newNode = xmlCopyNode(newNode, 1); } if (!xmlAddChild(fragment, newNode)) { - xmlFree(fragment); - - php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror); - return NULL; + if (nodesc > 1) { + xmlFreeNode(newNode); + } + goto hierarchy_request_err; } continue; } else { - xmlFree(fragment); - zend_argument_type_error(i + 1, "must be of type DOMNode|string, %s given", zend_zval_type_name(&nodes[i])); - return NULL; + goto err; } } else if (Z_TYPE(nodes[i]) == IS_STRING) { newNode = xmlNewDocText(documentNode, (xmlChar *) Z_STRVAL(nodes[i])); @@ -201,20 +204,22 @@ xmlNode* dom_zvals_to_fragment(php_libxml_ref_obj *document, xmlNode *contextNod xmlSetTreeDoc(newNode, documentNode); if (!xmlAddChild(fragment, newNode)) { - xmlFree(fragment); - - return NULL; + xmlFreeNode(newNode); + goto hierarchy_request_err; } } else { - xmlFree(fragment); - zend_argument_type_error(i + 1, "must be of type DOMNode|string, %s given", zend_zval_type_name(&nodes[i])); - - return NULL; + goto err; } } return fragment; + +hierarchy_request_err: + php_dom_throw_error(HIERARCHY_REQUEST_ERR, stricterror); +err: + xmlFreeNode(fragment); + return NULL; } static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fragment) @@ -302,7 +307,9 @@ void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc) { xmlNode *prevsib = dom_object_get_node(context); xmlNodePtr newchild, parentNode; - xmlNode *fragment; + xmlNode *fragment, *nextsib; + xmlDoc *doc; + bool afterlastchild; int stricterror = dom_get_strict_error(context->document); @@ -311,7 +318,10 @@ void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc) return; } + doc = prevsib->doc; parentNode = prevsib->parent; + nextsib = prevsib->next; + afterlastchild = (nextsib == NULL); fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); if (fragment == NULL) { @@ -321,13 +331,42 @@ void dom_parent_node_after(dom_object *context, zval *nodes, int nodesc) newchild = fragment->children; if (newchild) { - fragment->last->next = prevsib->next; - prevsib->next = newchild; + /* first node and last node are both both parameters to DOMElement::after() method so nextsib and prevsib are null. */ + if (!parentNode->children) { + prevsib = nextsib = NULL; + } else if (afterlastchild) { + /* + * The new node will be inserted after last node, prevsib is last node. + * The first node is the parameter to DOMElement::after() if parentNode->children == prevsib is true + * and prevsib does not change, otherwise prevsib is parentNode->last (first node). + */ + prevsib = parentNode->children == prevsib ? prevsib : parentNode->last; + } else { + /* + * The new node will be inserted after first node, prevsib is first node. + * The first node is not the parameter to DOMElement::after() if parentNode->children == prevsib is true + * and prevsib does not change otherwise prevsib is null to mean that parentNode->children is the new node. + */ + prevsib = parentNode->children == prevsib ? prevsib : NULL; + } - newchild->prev = prevsib; + if (prevsib) { + fragment->last->next = prevsib->next; + if (prevsib->next) { + prevsib->next->prev = fragment->last; + } + prevsib->next = newchild; + } else { + parentNode->children = newchild; + if (nextsib) { + fragment->last->next = nextsib; + nextsib->prev = fragment->last; + } + } + newchild->prev = prevsib; dom_fragment_assign_parent_node(parentNode, fragment); - dom_reconcile_ns(prevsib->doc, newchild); + dom_reconcile_ns(doc, newchild); } xmlFree(fragment); @@ -337,10 +376,15 @@ void dom_parent_node_before(dom_object *context, zval *nodes, int nodesc) { xmlNode *nextsib = dom_object_get_node(context); xmlNodePtr newchild, prevsib, parentNode; - xmlNode *fragment; + xmlNode *fragment, *afternextsib; + xmlDoc *doc; + bool beforefirstchild; + doc = nextsib->doc; prevsib = nextsib->prev; + afternextsib = nextsib->next; parentNode = nextsib->parent; + beforefirstchild = !prevsib; fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); if (fragment == NULL) { @@ -350,19 +394,40 @@ void dom_parent_node_before(dom_object *context, zval *nodes, int nodesc) newchild = fragment->children; if (newchild) { + /* first node and last node are both both parameters to DOMElement::before() method so nextsib is null. */ + if (!parentNode->children) { + nextsib = NULL; + } else if (beforefirstchild) { + /* + * The new node will be inserted before first node, nextsib is first node and afternextsib is last node. + * The first node is not the parameter to DOMElement::before() if parentNode->children == nextsib is true + * and nextsib does not change, otherwise nextsib is the last node. + */ + nextsib = parentNode->children == nextsib ? nextsib : afternextsib; + } else { + /* + * The new node will be inserted before last node, prevsib is first node and nestsib is last node. + * The first node is not the parameter to DOMElement::before() if parentNode->children == prevsib is true + * but last node may be, so use prevsib->next to determine the value of nextsib, otherwise nextsib does not change. + */ + nextsib = parentNode->children == prevsib ? prevsib->next : nextsib; + } + if (parentNode->children == nextsib) { parentNode->children = newchild; } else { prevsib->next = newchild; } + fragment->last->next = nextsib; - nextsib->prev = fragment->last; + if (nextsib) { + nextsib->prev = fragment->last; + } newchild->prev = prevsib; dom_fragment_assign_parent_node(parentNode, fragment); - - dom_reconcile_ns(nextsib->doc, newchild); + dom_reconcile_ns(doc, newchild); } xmlFree(fragment); diff --git a/ext/dom/tests/bug80602.phpt b/ext/dom/tests/bug80602.phpt new file mode 100644 index 0000000000000..9f041f686f516 --- /dev/null +++ b/ext/dom/tests/bug80602.phpt @@ -0,0 +1,178 @@ +--TEST-- +Bug #80602 (Segfault when using DOMChildNode::before()) +--FILE-- +loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before($target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before($target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before($doc->documentElement->lastChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before($doc->documentElement->firstChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before($target, $doc->documentElement->lastChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before($doc->documentElement->lastChild, $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before($target, $doc->documentElement->firstChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before($doc->documentElement->firstChild, $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before('bar','baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before('bar','baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before($target, 'bar','baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before('bar', $target, 'baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before('bar', 'baz', $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before($target, 'bar','baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before('bar', $target, 'baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before('bar', 'baz', $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before('bar', $target, $doc->documentElement->lastChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before($target, 'bar', $doc->documentElement->lastChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->before($target, $doc->documentElement->lastChild, 'bar'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before('bar', $doc->documentElement->firstChild, $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before($doc->documentElement->firstChild, 'bar', $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->before($doc->documentElement->firstChild, $target, 'bar'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +?> +--EXPECTF-- +foo +foo +foo +foo +foo +foo +foo +foo +barbazfoo +foobarbaz +foobarbaz +barfoobaz +barbazfoo +foobarbaz +foobarbaz +foobarbaz +barfoo +foobar +foobar +barfoo +foobar +foobar diff --git a/ext/dom/tests/bug80602_2.phpt b/ext/dom/tests/bug80602_2.phpt new file mode 100644 index 0000000000000..1151417c0f845 --- /dev/null +++ b/ext/dom/tests/bug80602_2.phpt @@ -0,0 +1,178 @@ +--TEST-- +Bug #80602 (Segfault when using DOMChildNode::after()) +--FILE-- +loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after($target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after($target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after($doc->documentElement->lastChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after($doc->documentElement->firstChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after($target, $doc->documentElement->lastChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after($doc->documentElement->lastChild, $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after($target, $doc->documentElement->firstChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after($doc->documentElement->firstChild, $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after('bar','baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after('bar','baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after($target, 'bar','baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after('bar', $target, 'baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after('bar', 'baz', $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after($target, 'bar','baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after('bar', $target, 'baz'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after('bar', 'baz', $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after('bar', $target, $doc->documentElement->lastChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after($target, 'bar', $doc->documentElement->lastChild); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->firstChild; +$target->after($target, $doc->documentElement->lastChild, 'bar'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after('bar', $doc->documentElement->firstChild, $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after($doc->documentElement->firstChild, 'bar', $target); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + + +$doc = new \DOMDocument(); +$doc->loadXML('foo'); +$target = $doc->documentElement->lastChild; +$target->after($doc->documentElement->firstChild, $target, 'bar'); +echo $doc->saveXML($doc->documentElement).PHP_EOL; + +?> +--EXPECTF-- +foo +foo +foo +foo +foo +foo +foo +foo +foobarbaz +foobarbaz +foobarbaz +barfoobaz +barbazfoo +foobarbaz +foobarbaz +foobarbaz +barfoo +foobar +foobar +barfoo +foobar +foobar diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 74dba4bf6985f..273149ccbb845 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -1277,6 +1277,9 @@ typedef struct { mn_offset_mode_t offset_mode; } maker_note_type; +/* Some maker notes (e.g. DJI info tag) require custom parsing */ +#define REQUIRES_CUSTOM_PARSING NULL + /* Remember to update PHP_MINFO if updated */ static const maker_note_type maker_note_array[] = { { tag_table_VND_CANON, "Canon", NULL, 0, 0, MN_ORDER_INTEL, MN_OFFSET_NORMAL}, @@ -1287,6 +1290,7 @@ static const maker_note_type maker_note_array[] = { { tag_table_VND_OLYMPUS, "OLYMPUS OPTICAL CO.,LTD", "OLYMP\x00\x01\x00", 8, 8, MN_ORDER_NORMAL, MN_OFFSET_NORMAL}, { tag_table_VND_SAMSUNG, "SAMSUNG", NULL, 0, 0, MN_ORDER_NORMAL, MN_OFFSET_NORMAL}, { tag_table_VND_PANASONIC, "Panasonic", "Panasonic\x00\x00\x00", 12, 12, MN_ORDER_NORMAL, MN_OFFSET_NORMAL}, + { REQUIRES_CUSTOM_PARSING, "DJI", "[ae_dbg_info:", 13, 13, MN_ORDER_MOTOROLA, MN_OFFSET_NORMAL}, { tag_table_VND_DJI, "DJI", NULL, 0, 0, MN_ORDER_NORMAL, MN_OFFSET_NORMAL}, { tag_table_VND_SONY, "SONY", "SONY DSC \x00\x00\x00", 12, 12, MN_ORDER_NORMAL, MN_OFFSET_NORMAL}, { tag_table_VND_SONY, "SONY", NULL, 0, 0, MN_ORDER_NORMAL, MN_OFFSET_NORMAL}, @@ -3168,10 +3172,16 @@ static bool exif_process_IFD_in_MAKERNOTE(image_info_type *ImageInfo, char * val return true; } + if (UNEXPECTED(maker_note->tag_table == REQUIRES_CUSTOM_PARSING)) { + /* Custom parsing required, which is not implemented at this point + * Return true so that other metadata can still be parsed. */ + return true; + } + dir_start = value_ptr + maker_note->offset; #ifdef EXIF_DEBUG - exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process %s @x%04X + 0x%04X=%d: %s", exif_get_sectionname(section_index), (intptr_t)dir_start-(intptr_t)info->offset_base+maker_note->offset+displacement, value_len, value_len, exif_char_dump(value_ptr, value_len, (intptr_t)dir_start-(intptr_t)info->offset_base+maker_note->offset+displacement)); + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Process %s @0x%04X + 0x%04X=%d: %s", exif_get_sectionname(section_index), (intptr_t)dir_start-(intptr_t)info->offset_base+maker_note->offset+displacement, value_len, value_len, exif_char_dump(value_ptr, value_len, (intptr_t)dir_start-(intptr_t)info->offset_base+maker_note->offset+displacement)); #endif ImageInfo->sections_found |= FOUND_MAKERNOTE; @@ -3330,7 +3340,7 @@ static bool exif_process_IFD_TAG_impl(image_info_type *ImageInfo, char *dir_entr #ifdef EXIF_DEBUG dump_data = exif_dump_data(&dump_free, format, components, ImageInfo->motorola_intel, value_ptr); exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, - "Process tag(x%04X=%s,@x%04X + x%04X(=%d)): %s%s %s", + "Process tag(x%04X=%s,@0x%04X + x%04X(=%d)): %s%s %s", tag, exif_get_tagname_debug(tag, tag_table), offset_val+displacement, byte_count, byte_count, (components>1)&&format!=TAG_FMT_UNDEFINED&&format!=TAG_FMT_STRING?"ARRAY OF ":"", exif_get_tagformat(format), dump_data); if (dump_free) { efree(dump_data); @@ -4173,7 +4183,7 @@ static bool exif_process_IFD_in_TIFF_impl(image_info_type *ImageInfo, size_t dir } entry_offset = php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel); #ifdef EXIF_DEBUG - exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Next IFD: %s @x%04X", exif_get_sectionname(sub_section_index), entry_offset); + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Next IFD: %s @0x%04X", exif_get_sectionname(sub_section_index), entry_offset); #endif exif_process_IFD_in_TIFF(ImageInfo, entry_offset, sub_section_index); if (section_index!=SECTION_THUMBNAIL && entry_tag==TAG_SUB_IFD) { diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c index 5826c2e3372f1..a15e166041334 100644 --- a/ext/intl/php_intl.c +++ b/ext/intl/php_intl.c @@ -286,7 +286,7 @@ PHP_RSHUTDOWN_FUNCTION( intl ) /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION( intl ) { -#ifndef UCONFIG_NO_FORMATTING +#if !UCONFIG_NO_FORMATTING UErrorCode status = U_ZERO_ERROR; const char *tzdata_ver = NULL; #endif @@ -297,7 +297,7 @@ PHP_MINFO_FUNCTION( intl ) #ifdef U_ICU_DATA_VERSION php_info_print_table_row( 2, "ICU Data version", U_ICU_DATA_VERSION ); #endif -#ifndef UCONFIG_NO_FORMATTING +#if !UCONFIG_NO_FORMATTING tzdata_ver = ucal_getTZDataVersion(&status); if (U_ZERO_ERROR == status) { php_info_print_table_row( 2, "ICU TZData version", tzdata_ver); diff --git a/ext/mysqli/tests/bug73462.phpt b/ext/mysqli/tests/bug73462.phpt index aa54dbbe5d5cc..a5ae94acffa80 100644 --- a/ext/mysqli/tests/bug73462.phpt +++ b/ext/mysqli/tests/bug73462.phpt @@ -11,14 +11,14 @@ require_once('skipifconnectfailure.inc'); require_once("connect.inc"); /* Initial persistent connection */ - $mysql_1 = new mysqli('p:'.$host, $user, $passwd, $db); + $mysql_1 = new mysqli('p:'.$host, $user, $passwd, $db, $port); $result = $mysql_1->query("SHOW STATUS LIKE 'Connections'"); $c1 = $result->fetch_row(); $result->free(); $mysql_1->close(); /* Failed connection to invalid host */ - $mysql_2 = @new mysqli(' !!! invalid !!! ', $user, $passwd, $db); + $mysql_2 = @new mysqli(' !!! invalid !!! ', $user, $passwd, $db, $port); try { $mysql_2->close(); } catch (Error $exception) { @@ -26,7 +26,7 @@ require_once('skipifconnectfailure.inc'); } /* Re-use persistent connection */ - $mysql_3 = new mysqli('p:'.$host, $user, $passwd, $db); + $mysql_3 = new mysqli('p:'.$host, $user, $passwd, $db, $port); $error = mysqli_connect_errno(); $result = $mysql_3->query("SHOW STATUS LIKE 'Connections'"); $c3 = $result->fetch_row(); diff --git a/ext/mysqli/tests/bug73949.phpt b/ext/mysqli/tests/bug73949.phpt index b706ab20e8819..7c5364c274730 100644 --- a/ext/mysqli/tests/bug73949.phpt +++ b/ext/mysqli/tests/bug73949.phpt @@ -14,7 +14,7 @@ class cc{ function __construct($c=null){ } }; -$i=mysqli_connect('p:'.$host, $user, $passwd, $db); +$i=mysqli_connect('p:'.$host, $user, $passwd, $db, $port); $res=mysqli_query($i, "SHOW STATUS LIKE 'Connections'"); $t=array(new stdClass); while($db= mysqli_fetch_object($res,'cc',$t)){} diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index eac9719a98c2c..d2e3e1e1c3185 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4979,6 +4979,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par res_addr = 0; } else { res_addr = RES_REG_ADDR(); + if (Z_MODE(res_addr) != IS_REG + && zend_jit_trace_next_is_send_result(opline, p, frame)) { + send_result = 1; + res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var); + if (!zend_jit_reuse_ip(&dasm_state)) { + goto jit_failure; + } + } } if (!zend_jit_assign_to_typed_ref(&dasm_state, opline, opline->op2_type, op2_addr, res_addr, 1)) { goto jit_failure; @@ -7297,7 +7305,7 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa fprintf(stderr, " op1(%sobject of class %s)", ref, ZSTR_VAL(p->ce->name)); } else { - const char *type = (op1_type == 0) ? "undef" : zend_get_type_by_const(op1_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED)); + const char *type = ((op1_type & ~IS_TRACE_INDIRECT) == 0) ? "undef" : zend_get_type_by_const(op1_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT|IS_TRACE_PACKED)); fprintf(stderr, " op1(%s%s%s)", ref, (op1_type & IS_TRACE_PACKED) ? "packed " : "", type); } } @@ -7310,7 +7318,7 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa fprintf(stderr, " op2(%sobject of class %s)", ref, ZSTR_VAL(p->ce->name)); } else { - const char *type = (op2_type == 0) ? "undef" : zend_get_type_by_const(op2_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)); + const char *type = ((op2_type & ~IS_TRACE_INDIRECT) == 0) ? "undef" : zend_get_type_by_const(op2_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)); fprintf(stderr, " op2(%s%s)", ref, type); } } @@ -7318,7 +7326,7 @@ static void zend_jit_dump_trace(zend_jit_trace_rec *trace_buffer, zend_ssa *tssa const char *ref = (op3_type & IS_TRACE_INDIRECT) ? ((op3_type & IS_TRACE_REFERENCE) ? "*&" : "*") : ((op3_type & IS_TRACE_REFERENCE) ? "&" : ""); - const char *type = (op3_type == 0) ? "undef" : zend_get_type_by_const(op3_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)); + const char *type = ((op3_type & ~IS_TRACE_INDIRECT) == 0) ? "undef" : zend_get_type_by_const(op3_type & ~(IS_TRACE_REFERENCE|IS_TRACE_INDIRECT)); fprintf(stderr, " op3(%s%s)", ref, type); } } diff --git a/ext/opcache/tests/jit/assign_056.phpt b/ext/opcache/tests/jit/assign_056.phpt new file mode 100644 index 0000000000000..5ad56213c305e --- /dev/null +++ b/ext/opcache/tests/jit/assign_056.phpt @@ -0,0 +1,21 @@ +--TEST-- +JIT ASSIGN: ASSING+SEND and typed reference +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +opcache.protect_memory=1 +--FILE-- + +--EXPECT-- +object(Test)#2 (0) { +} diff --git a/ext/opcache/tests/jit/bug80861.phpt b/ext/opcache/tests/jit/bug80861.phpt index 387f6725eef7f..cb98a3d78f5c3 100644 --- a/ext/opcache/tests/jit/bug80861.phpt +++ b/ext/opcache/tests/jit/bug80861.phpt @@ -1,6 +1,7 @@ --TEST-- Bug #80839: PHP problem with JIT --INI-- +error_log= opcache.enable=1 opcache.enable_cli=1 opcache.jit_buffer_size=1M diff --git a/ext/opcache/tests/jit/fetch_static_prop_001.phpt b/ext/opcache/tests/jit/fetch_static_prop_001.phpt new file mode 100644 index 0000000000000..db781ac993cea --- /dev/null +++ b/ext/opcache/tests/jit/fetch_static_prop_001.phpt @@ -0,0 +1,20 @@ +--TEST-- +FETCH_STATIC_PROP_W should not return UNDEF +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + int(2) +} diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index a8d3559ef5beb..6249a80797076 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -2479,7 +2479,12 @@ PHP_FUNCTION(preg_replace_callback_array) } if (subject_ht) { - RETURN_ARR(subject_ht); + RETVAL_ARR(subject_ht); + // Unset the type_flags of immutable arrays to prevent the VM from performing refcounting + if (GC_FLAGS(subject_ht) & IS_ARRAY_IMMUTABLE) { + Z_TYPE_FLAGS_P(return_value) = 0; + } + return; } else { RETURN_STR(subject_str); } diff --git a/ext/pcre/tests/gh10968.phpt b/ext/pcre/tests/gh10968.phpt new file mode 100644 index 0000000000000..873d17e79da86 --- /dev/null +++ b/ext/pcre/tests/gh10968.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-10968: preg_replace_callback_array() segmentation fault +--FILE-- + +--EXPECT-- +array(0) { +} +string(0) "" diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 9c7ca4e324481..97253cfe93283 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -1787,6 +1787,8 @@ PHP_METHOD(CallbackFilterIterator, accept) if (zend_call_function(fci, fcc) != SUCCESS || Z_ISUNDEF_P(return_value)) { RETURN_FALSE; + } else if (Z_ISREF_P(return_value)) { + zend_unwrap_reference(return_value); } } /* }}} */ diff --git a/ext/standard/array.c b/ext/standard/array.c index 9a814ea07da47..fb705cd34c4e8 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -345,7 +345,27 @@ static zend_always_inline int php_array_key_compare_string_locale_unstable_i(Buc static zend_always_inline int php_array_data_compare_unstable_i(Bucket *f, Bucket *s) /* {{{ */ { - return zend_compare(&f->val, &s->val); + int result = zend_compare(&f->val, &s->val); + /* Special enums handling for array_unique. We don't want to add this logic to zend_compare as + * that would be observable via comparison operators. */ + zval *rhs = &s->val; + ZVAL_DEREF(rhs); + if (UNEXPECTED(Z_TYPE_P(rhs) == IS_OBJECT) + && result == ZEND_UNCOMPARABLE + && (Z_OBJCE_P(rhs)->ce_flags & ZEND_ACC_ENUM)) { + zval *lhs = &f->val; + ZVAL_DEREF(lhs); + if (Z_TYPE_P(lhs) == IS_OBJECT && (Z_OBJCE_P(lhs)->ce_flags & ZEND_ACC_ENUM)) { + // Order doesn't matter, we just need to group the same enum values + uintptr_t lhs_uintptr = (uintptr_t)Z_OBJ_P(lhs); + uintptr_t rhs_uintptr = (uintptr_t)Z_OBJ_P(rhs); + return lhs_uintptr == rhs_uintptr ? 0 : (lhs_uintptr < rhs_uintptr ? -1 : 1); + } else { + // Shift enums to the end of the array + return -1; + } + } + return result; } /* }}} */ diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 003fad3ea18c5..4299c10bc2e8e 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -136,6 +136,7 @@ static void php_mail_build_headers_elems(smart_str *s, zend_string *key, zval *v zend_type_error("Header \"%s\" must only contain numeric keys, \"%s\" found", ZSTR_VAL(key), ZSTR_VAL(tmp_key)); break; } + ZVAL_DEREF(tmp_val); if (Z_TYPE_P(tmp_val) != IS_STRING) { zend_type_error("Header \"%s\" must only contain values of type string, %s found", ZSTR_VAL(key), zend_zval_type_name(tmp_val)); break; @@ -157,6 +158,7 @@ PHPAPI zend_string *php_mail_build_headers(HashTable *headers) zend_type_error("Header name cannot be numeric, " ZEND_LONG_FMT " given", idx); break; } + ZVAL_DEREF(val); /* https://tools.ietf.org/html/rfc2822#section-3.6 */ if (zend_string_equals_literal_ci(key, "orig-date")) { PHP_MAIL_BUILD_HEADER_CHECK("orig-date", s, key, val); diff --git a/ext/standard/tests/file/bug60120.phpt b/ext/standard/tests/file/bug60120.phpt index 0236e9e1ea7c7..811ac786c8e5f 100644 --- a/ext/standard/tests/file/bug60120.phpt +++ b/ext/standard/tests/file/bug60120.phpt @@ -6,6 +6,7 @@ $php = getenv('TEST_PHP_EXECUTABLE'); if (!$php) { die("skip No php executable defined\n"); } +if (PHP_OS_FAMILY === 'Windows') die('skip not for Windows'); ?> --FILE-- = $stdinLen) { fclose($writePipes[0]); @@ -58,12 +62,21 @@ while ($pipes || $writePipes) { foreach ($r as $pipe) { $type = array_search($pipe, $pipes); $data = fread($pipe, 8192); - if (false === $data || feof($pipe)) { + if (feof($pipe)) { fclose($pipe); unset($pipes[$type]); + } elseif (false === $data) { + die('Failed to read from pipe'); + } else { + $procOutput[$type] = ($procOutput[$type] ?? '') . $data; } } } +foreach ($procOutput as $output) { + if ($output !== $stdin) { + die('Output does not match input: ' . $output); + } +} echo "OK."; ?> --EXPECT-- diff --git a/ext/standard/tests/mail/gh10990.phpt b/ext/standard/tests/mail/gh10990.phpt new file mode 100644 index 0000000000000..4f74c17c22bda --- /dev/null +++ b/ext/standard/tests/mail/gh10990.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-10990 (mail() throws TypeError after iterating over $additional_headers array by reference) +--INI-- +sendmail_path=rubbish 2>/dev/null +--SKIPIF-- + +--FILE-- + &$from]; +var_dump(mail('test@example.com', 'Test', 'Test', $headers)); +?> +--EXPECT-- +bool(false) diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index d80ed25f933f0..32fd153ac2d8c 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -327,6 +327,23 @@ static ZEND_FUNCTION(zend_get_map_ptr_last) RETURN_LONG(CG(map_ptr_last)); } +static ZEND_FUNCTION(zend_test_crash) +{ + zend_string *message = NULL; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(message) + ZEND_PARSE_PARAMETERS_END(); + + if (message) { + php_printf("%s", ZSTR_VAL(message)); + } + + char *invalid = (char *) 1; + php_printf("%s", invalid); +} + static zend_object *zend_test_class_new(zend_class_entry *class_type) { zend_object *obj = zend_objects_new(class_type); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index c95c650ad7207..fc35f5743ef28 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -119,6 +119,8 @@ function zend_get_current_func_name(): string {} function zend_call_method(string $class, string $method, mixed $arg1 = UNKNOWN, mixed $arg2 = UNKNOWN): mixed {} function zend_get_map_ptr_last(): int {} + + function zend_test_crash(?string $message = null): void {} } namespace ZendTestNS { diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 766c0b08dd158..08b2d9d8e87f9 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: 614310958c6e2acde46c9b7932ba894caf72d6df */ + * Stub hash: 47eb58d644268f4fdce7a6b5007f7755ebfcb197 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -82,6 +82,10 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_get_map_ptr_last, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_crash, 0, 0, IS_VOID, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_ZendSubNS_namespaced_func, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -143,6 +147,7 @@ static ZEND_FUNCTION(zend_test_parameter_with_attribute); static ZEND_FUNCTION(zend_get_current_func_name); static ZEND_FUNCTION(zend_call_method); static ZEND_FUNCTION(zend_get_map_ptr_last); +static ZEND_FUNCTION(zend_test_crash); static ZEND_FUNCTION(namespaced_func); static ZEND_METHOD(_ZendTestClass, is_object); static ZEND_METHOD(_ZendTestClass, __toString); @@ -182,6 +187,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_get_current_func_name, arginfo_zend_get_current_func_name) ZEND_FE(zend_call_method, arginfo_zend_call_method) ZEND_FE(zend_get_map_ptr_last, arginfo_zend_get_map_ptr_last) + ZEND_FE(zend_test_crash, arginfo_zend_test_crash) ZEND_NS_FE("ZendTestNS2\\ZendSubNS", namespaced_func, arginfo_ZendTestNS2_ZendSubNS_namespaced_func) ZEND_FE_END }; diff --git a/main/main.c b/main/main.c index 3d7ca29138387..4868d2df40039 100644 --- a/main/main.c +++ b/main/main.c @@ -1936,7 +1936,7 @@ static void core_globals_dtor(php_core_globals *core_globals) free(core_globals->php_binary); } - php_shutdown_ticks(); + php_shutdown_ticks(core_globals); } /* }}} */ diff --git a/main/php_ticks.c b/main/php_ticks.c index 004314583bdb0..70201ddecd08d 100644 --- a/main/php_ticks.c +++ b/main/php_ticks.c @@ -34,9 +34,9 @@ void php_deactivate_ticks(void) zend_llist_clean(&PG(tick_functions)); } -void php_shutdown_ticks(void) +void php_shutdown_ticks(php_core_globals *core_globals) { - zend_llist_destroy(&PG(tick_functions)); + zend_llist_destroy(&core_globals->tick_functions); } static int php_compare_tick_functions(void *elem1, void *elem2) diff --git a/main/php_ticks.h b/main/php_ticks.h index 5edf7a483bbac..270ea5348fd2a 100644 --- a/main/php_ticks.h +++ b/main/php_ticks.h @@ -19,7 +19,7 @@ int php_startup_ticks(void); void php_deactivate_ticks(void); -void php_shutdown_ticks(void); +void php_shutdown_ticks(php_core_globals *core_globals); void php_run_ticks(int count); BEGIN_EXTERN_C() diff --git a/main/php_version.h b/main/php_version.h index 30f096bb8cc64..27904da197533 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 1 -#define PHP_RELEASE_VERSION 18 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.1.18-dev" -#define PHP_VERSION_ID 80118 +#define PHP_RELEASE_VERSION 19 +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.1.19" +#define PHP_VERSION_ID 80119 diff --git a/sapi/fpm/tests/fcgi-env-nopif-apache-handler-with-pi-with-pt-pd.phpt b/sapi/fpm/tests/fcgi-env-nopif-apache-handler-with-pi-with-pt-pd.phpt new file mode 100644 index 0000000000000..a2aa4164fc3bc --- /dev/null +++ b/sapi/fpm/tests/fcgi-env-nopif-apache-handler-with-pi-with-pt-pd.phpt @@ -0,0 +1,58 @@ +--TEST-- +FPM: FastCGI env var without path info fix for Apache handler with PATH_INFO, PATH_TRANSLATED and path discard +--SKIPIF-- + +--FILE-- +createSourceFileAndScriptName(); +$tester->start(); +$tester->expectLogStartNotices(); +$tester + ->request( + headers: [ + 'PATH_INFO' => '/pinfo', + 'PATH_TRANSLATED' => __DIR__ . '/pinfo', + ], + uri: $scriptName . '/pinfo', + scriptFilename: "proxy:fcgi://" . $tester->getAddr() . $sourceFilePath, + scriptName: $scriptName, + ) + ->expectBody([$scriptName, $sourceFilePath, '/pinfo', '/pinfo']); +$tester->terminate(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/fcgi-env-pif-apache-handler-uds.phpt b/sapi/fpm/tests/fcgi-env-pif-apache-handler-uds.phpt new file mode 100644 index 0000000000000..b80fdef3ffdf6 --- /dev/null +++ b/sapi/fpm/tests/fcgi-env-pif-apache-handler-uds.phpt @@ -0,0 +1,53 @@ +--TEST-- +FPM: FastCGI env var path info fix for Apache handler using Unix Domain Socket +--SKIPIF-- + +--FILE-- +createSourceFileAndScriptName(); +$tester->start(); +$tester->expectLogStartNotices(); +$tester + ->request( + uri: $scriptName, + address: '{{ADDR:UDS}}', + scriptFilename: "proxy:fcgi://localhost" . $sourceFilePath, + scriptName: $scriptName, + ) + ->expectBody([$scriptName, $sourceFilePath, $scriptName]); +$tester->terminate(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/fcgi-env-pif-apache-handler-basic.phpt b/sapi/fpm/tests/fcgi-env-pif-apache-handler-with-pi.phpt similarity index 94% rename from sapi/fpm/tests/fcgi-env-pif-apache-handler-basic.phpt rename to sapi/fpm/tests/fcgi-env-pif-apache-handler-with-pi.phpt index a99f373cf3321..2d8433123cb2d 100644 --- a/sapi/fpm/tests/fcgi-env-pif-apache-handler-basic.phpt +++ b/sapi/fpm/tests/fcgi-env-pif-apache-handler-with-pi.phpt @@ -1,5 +1,5 @@ --TEST-- -FPM: FastCGI env var path info fix for Apache handler basic +FPM: FastCGI env var path info fix for Apache handler with PATH_INFO set --SKIPIF-- --FILE--