diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..2320d2baca9c2 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,197 @@ +version: 2.1 + +jobs: + arm: + resource_class: arm.medium + docker: + - image: cimg/base:current-22.04 + - image: mysql:8 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: true + MYSQL_ROOT_PASSWORD: '' + MYSQL_DATABASE: test + - image: postgres:16 + environment: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test + environment: + LANGUAGE: '' + LANG: en_US.UTF-8 + MYSQL_TEST_HOST: '127.0.0.1' + MYSQL_TEST_PASSWD: '' + MYSQL_TEST_USER: root + PDO_MYSQL_TEST_DSN: 'mysql:host=127.0.0.1;dbname=test' + PDO_MYSQL_TEST_PASS: '' + PDO_MYSQL_TEST_USER: root + PDO_PGSQL_TEST_DSN: 'pgsql:host=127.0.0.1 port=5432 dbname=test user=postgres password=postgres' + steps: + - checkout + - run: + name: apt + command: | + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update -y + sudo apt-get install -y \ + gcc \ + g++ \ + autoconf \ + bison \ + re2c \ + locales \ + locales-all \ + ldap-utils \ + openssl \ + slapd \ + libgmp-dev \ + libicu-dev \ + libtidy-dev \ + libenchant-2-dev \ + libaspell-dev \ + libpspell-dev \ + libsasl2-dev \ + libxpm-dev \ + libzip-dev \ + libbz2-dev \ + libsqlite3-dev \ + libwebp-dev \ + libonig-dev \ + libkrb5-dev \ + libgssapi-krb5-2 \ + libcurl4-openssl-dev \ + libxml2-dev \ + libxslt1-dev \ + libpq-dev \ + libreadline-dev \ + libldap2-dev \ + libsodium-dev \ + libargon2-0-dev \ + libmm-dev \ + libsnmp-dev \ + snmpd \ + `#snmp-mibs-downloader` \ + freetds-dev \ + `#unixodbc-dev` \ + libc-client-dev \ + dovecot-core \ + dovecot-pop3d \ + dovecot-imapd \ + sendmail \ + firebird-dev \ + liblmdb-dev \ + libtokyocabinet-dev \ + libdb-dev \ + libqdbm-dev \ + libjpeg-dev \ + libpng-dev \ + libfreetype6-dev + - run: + name: ./configure + command: | + ./buildconf -f + ./configure \ + --enable-debug \ + --enable-zts \ + --enable-option-checking=fatal \ + --prefix=/usr \ + --enable-phpdbg \ + --enable-fpm \ + --enable-opcache \ + --with-pdo-mysql=mysqlnd \ + --with-mysqli=mysqlnd \ + --with-pgsql \ + --with-pdo-pgsql \ + --with-pdo-sqlite \ + --enable-intl \ + --without-pear \ + --enable-gd \ + --with-jpeg \ + --with-webp \ + --with-freetype \ + --with-xpm \ + --enable-exif \ + --with-zip \ + --with-zlib \ + --with-zlib-dir=/usr \ + --enable-soap \ + --enable-xmlreader \ + --with-xsl \ + --with-tidy \ + --enable-sysvsem \ + --enable-sysvshm \ + --enable-shmop \ + --enable-pcntl \ + --with-readline \ + --enable-mbstring \ + --with-curl \ + --with-gettext \ + --enable-sockets \ + --with-bz2 \ + --with-openssl \ + --with-gmp \ + --enable-bcmath \ + --enable-calendar \ + --enable-ftp \ + --with-pspell=/usr \ + --with-enchant=/usr \ + --with-kerberos \ + --enable-sysvmsg \ + --with-ffi \ + --enable-zend-test \ + --enable-dl-test=shared \ + --with-ldap \ + --with-ldap-sasl \ + --with-password-argon2 \ + --with-mhash \ + --with-sodium \ + --enable-dba \ + --with-cdb \ + --enable-flatfile \ + --enable-inifile \ + --with-tcadb \ + --with-lmdb \ + --with-qdbm \ + --with-snmp \ + `#--with-unixODBC` \ + --with-imap \ + --with-kerberos \ + --with-imap-ssl \ + `#--with-pdo-odbc=unixODBC,/usr` \ + `#--with-pdo-oci=shared,instantclient,/opt/oracle/instantclient` \ + `#--with-oci8=shared,instantclient,/opt/oracle/instantclient` \ + --with-config-file-path=/etc \ + --with-config-file-scan-dir=/etc/php.d \ + --with-pdo-firebird \ + `#--with-pdo-dblib` \ + --disable-phpdbg \ + `#--enable-werror` + - run: + name: make + command: make -j2 > /dev/null + - run: + name: make install + command: | + sudo make install + sudo mkdir -p /etc/php.d + sudo chmod 777 /etc/php.d + echo opcache.enable_cli=1 > /etc/php.d/opcache.ini + echo opcache.protect_memory=1 >> /etc/php.d/opcache.ini + - run: + name: Test + command: | + sapi/cli/php run-tests.php \ + -d zend_extension=opcache.so \ + -d opcache.enable_cli=1 \ + -d opcache.jit_buffer_size=16M \ + -d opcache.jit=tracing \ + -P -q -x -j2 \ + -g FAIL,BORK,LEAK,XLEAK \ + --offline \ + --show-diff \ + --show-slow 1000 \ + --set-timeout 120 \ + --repeat 2 + +workflows: + push-workflow: + jobs: + - arm diff --git a/.cirrus.yml b/.cirrus.yml index 9ef70fb59191c..b04d91b9d45b2 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -3,7 +3,6 @@ env: freebsd_task: name: FREEBSD_DEBUG_NTS - only_if: $CIRRUS_CRON == 'nightly' || $CIRRUS_CHANGE_TITLE =~ '.*\[ci freebsd\].*' freebsd_instance: image_family: freebsd-13-2 env: @@ -27,203 +26,3 @@ freebsd_task: - export SKIP_IO_CAPTURE_TESTS=1 - export CI_NO_IPV6=1 - sapi/cli/php run-tests.php -P -q -j2 -g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP --offline --show-diff --show-slow 1000 --set-timeout 120 -d zend_extension=opcache.so - -arm_task: - name: ARM_DEBUG_NTS - only_if: $CIRRUS_CRON == 'nightly' || $CIRRUS_CHANGE_TITLE =~ '.*\[ci arm\].*' - arm_container: - image: debian:11 - additional_containers: - - name: mysql - image: mysql:8 - port: 3306 - cpu: 1.0 - memory: 1G - env: - MYSQL_ALLOW_EMPTY_PASSWORD: true - MYSQL_ROOT_PASSWORD: "" - MYSQL_DATABASE: "test" - - name: postgres - image: postgres:latest - port: 5432 - env: - POSTGRES_PASSWORD: "postgres" - POSTGRES_DB: "test" - install_script: - - export DEBIAN_FRONTEND=noninteractive - - apt-get update -y - - >- - apt-get install -y - gcc - g++ - autoconf - bison - re2c - locales - locales-all - ldap-utils - openssl - slapd - libgmp-dev - libicu-dev - libtidy-dev - libenchant-2-dev - libaspell-dev - libpspell-dev - libsasl2-dev - libxpm-dev - libzip-dev - libbz2-dev - libsqlite3-dev - libwebp-dev - libonig-dev - libkrb5-dev - libgssapi-krb5-2 - libcurl4-openssl-dev - libxml2-dev - libxslt1-dev - libpq-dev - libreadline-dev - libldap2-dev - libsodium-dev - libargon2-0-dev - libmm-dev - libsnmp-dev - snmpd - `#snmp-mibs-downloader` - freetds-dev - `#unixodbc-dev` - libc-client-dev - dovecot-core - dovecot-pop3d - dovecot-imapd - sendmail - firebird-dev - liblmdb-dev - libtokyocabinet-dev - libdb-dev - libqdbm-dev - libjpeg-dev - libpng-dev - libfreetype6-dev - build_script: - - ./buildconf -f - - >- - ./configure - --enable-debug - --enable-zts - --enable-option-checking=fatal - --prefix=/usr - --enable-phpdbg - --enable-fpm - --enable-opcache - --with-pdo-mysql=mysqlnd - --with-mysqli=mysqlnd - --with-pgsql - --with-pdo-pgsql - --with-pdo-sqlite - --enable-intl - --without-pear - --enable-gd - --with-jpeg - --with-webp - --with-freetype - --with-xpm - --enable-exif - --with-zip - --with-zlib - --with-zlib-dir=/usr - --enable-soap - --enable-xmlreader - --with-xsl - --with-tidy - --enable-sysvsem - --enable-sysvshm - --enable-shmop - --enable-pcntl - --with-readline - --enable-mbstring - --with-curl - --with-gettext - --enable-sockets - --with-bz2 - --with-openssl - --with-gmp - --enable-bcmath - --enable-calendar - --enable-ftp - --with-pspell=/usr - --with-enchant=/usr - --with-kerberos - --enable-sysvmsg - --with-ffi - --enable-zend-test - --enable-dl-test=shared - --with-ldap - --with-ldap-sasl - --with-password-argon2 - --with-mhash - --with-sodium - --enable-dba - --with-cdb - --enable-flatfile - --enable-inifile - --with-tcadb - --with-lmdb - --with-qdbm - --with-snmp - `#--with-unixODBC` - --with-imap - --with-kerberos - --with-imap-ssl - `#--with-pdo-odbc=unixODBC,/usr` - `#--with-pdo-oci=shared,instantclient,/opt/oracle/instantclient` - `#--with-oci8=shared,instantclient,/opt/oracle/instantclient` - --with-config-file-path=/etc - --with-config-file-scan-dir=/etc/php.d - --with-pdo-firebird - `#--with-pdo-dblib` - --disable-phpdbg - `#--enable-werror` - - make -j2 > /dev/null - - make install - - mkdir -p /etc/php.d - - echo opcache.enable_cli=1 > /etc/php.d/opcache.ini - - echo opcache.protect_memory=1 >> /etc/php.d/opcache.ini - # Specify opcache.preload_user as we're running as root. - - echo opcache.preload_user=root >> /etc/php.d/opcache.ini - tests_script: - - export SKIP_IO_CAPTURE_TESTS=1 - - export CI_NO_IPV6=1 - - export MYSQL_TEST_HOST=127.0.0.1 - - export MYSQL_TEST_USER=root - - export MYSQL_TEST_PASSWD= - - export PDO_MYSQL_TEST_DSN="mysql:host=127.0.0.1;dbname=test" - - export PDO_MYSQL_TEST_USER=root - - export PDO_MYSQL_TEST_PASS= - - export PDO_PGSQL_TEST_DSN="pgsql:host=127.0.0.1 port=5432 dbname=test user=postgres password=postgres" - - >- - sapi/cli/php run-tests.php - -d zend_extension=opcache.so - -d opcache.enable_cli=1 - -d opcache.jit_buffer_size=16M - -d opcache.jit=function - -P -q -x -j2 - -g FAIL,BORK,LEAK,XLEAK - --offline - --show-diff - --show-slow 1000 - --set-timeout 120 - - >- - sapi/cli/php run-tests.php - -d zend_extension=opcache.so - -d opcache.enable_cli=1 - -d opcache.jit_buffer_size=16M - -d opcache.jit=tracing - -P -q -x -j2 - -g FAIL,BORK,LEAK,XLEAK - --offline - --show-diff - --show-slow 1000 - --set-timeout 120 - --repeat 2 diff --git a/.github/actions/test-linux/action.yml b/.github/actions/test-linux/action.yml index 7d66d2235208d..1dfba39478740 100644 --- a/.github/actions/test-linux/action.yml +++ b/.github/actions/test-linux/action.yml @@ -31,6 +31,7 @@ runs: export SKIP_IO_CAPTURE_TESTS=1 sapi/cli/php run-tests.php -P -q ${{ inputs.runTestsParameters }} \ -d opcache.jit=${{ inputs.jitType }} \ + -d opcache.jit_buffer_size=16M \ -j$(/usr/bin/nproc) \ -g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP \ --offline \ diff --git a/.github/actions/test-macos/action.yml b/.github/actions/test-macos/action.yml index 6036ce1e5a9b6..3bf0fdb7097af 100644 --- a/.github/actions/test-macos/action.yml +++ b/.github/actions/test-macos/action.yml @@ -16,6 +16,7 @@ runs: export CI_NO_IPV6=1 sapi/cli/php run-tests.php -P -q ${{ inputs.runTestsParameters }} \ -d opcache.jit=${{ inputs.jitType }} \ + -d opcache.jit_buffer_size=16M \ -j$(sysctl -n hw.ncpu) \ -g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP \ --offline \ diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7283f10672da3..f945ec23c9e3e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -89,7 +89,6 @@ jobs: ${{ matrix.run_tests_parameters }} -d zend_extension=opcache.so -d opcache.enable_cli=1 - -d opcache.jit_buffer_size=16M - name: Test OpCache uses: ./.github/actions/test-linux with: @@ -108,7 +107,6 @@ jobs: ${{ matrix.run_tests_parameters }} -d zend_extension=opcache.so -d opcache.enable_cli=1 - -d opcache.jit_buffer_size=16M - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files - name: Notify Slack @@ -171,7 +169,6 @@ jobs: ${{ matrix.run_tests_parameters }} -d zend_extension=opcache.so -d opcache.enable_cli=1 - -d opcache.jit_buffer_size=16M - name: Test OpCache uses: ./.github/actions/test-linux with: @@ -187,7 +184,6 @@ jobs: ${{ matrix.run_tests_parameters }} -d zend_extension=opcache.so -d opcache.enable_cli=1 - -d opcache.jit_buffer_size=16M - name: Notify Slack if: failure() uses: ./.github/actions/notify-slack @@ -233,7 +229,6 @@ jobs: -d zend_extension=opcache.so -d opcache.enable_cli=1 -d opcache.protect_memory=1 - -d opcache.jit_buffer_size=16M - name: Test OpCache uses: ./.github/actions/test-macos with: @@ -249,7 +244,6 @@ jobs: -d zend_extension=opcache.so -d opcache.enable_cli=1 -d opcache.protect_memory=1 - -d opcache.jit_buffer_size=16M - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files - name: Notify Slack @@ -347,7 +341,7 @@ jobs: git rev-parse HEAD php /usr/bin/composer install --no-progress --ignore-platform-reqs # Hack to disable a test that hangs - php -r '$c = file_get_contents("tests/Filesystem/FilesystemTest.php"); $c = str_replace("*/\n public function testSharedGet()", "* @group skip\n */\n public function testSharedGet()", $c); file_put_contents("tests/Filesystem/FilesystemTest.php", $c);' + php -r '$c = file_get_contents("tests/Filesystem/FilesystemTest.php"); $c = str_replace("public function testSharedGet()", "#[\\PHPUnit\\Framework\\Attributes\\Group('"'"'skip'"'"')]\n public function testSharedGet()", $c); file_put_contents("tests/Filesystem/FilesystemTest.php", $c);' export ASAN_OPTIONS=exitcode=139 php vendor/bin/phpunit --exclude-group skip || EXIT_CODE=$? if [ $EXIT_CODE -gt 128 ]; then @@ -625,7 +619,7 @@ jobs: - name: Build mysql-5.7 uses: ./.github/actions/build-libmysqlclient with: - libmysql: mysql-5.7.41-linux-glibc2.12-x86_64.tar.gz + libmysql: mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz withMysqli: ${{ matrix.branch.ref == 'PHP-8.1' }} - name: Test mysql-5.7 uses: ./.github/actions/test-libmysqlclient diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 37bdccd884a90..52f70f809e9ce 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -12,6 +12,7 @@ on: - .cirrus.yml - .travis.yml - travis/* + - .circleci/* branches: - PHP-7.4 - PHP-8.0 @@ -29,6 +30,7 @@ on: - .cirrus.yml - .travis.yml - travis/* + - .circleci/* branches: - '**' concurrency: @@ -89,7 +91,6 @@ jobs: runTestsParameters: >- -d zend_extension=opcache.so -d opcache.enable_cli=1 - -d opcache.jit_buffer_size=16M - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files MACOS_DEBUG_NTS: @@ -122,7 +123,6 @@ jobs: -d zend_extension=opcache.so -d opcache.enable_cli=1 -d opcache.protect_memory=1 - -d opcache.jit_buffer_size=16M - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files WINDOWS: diff --git a/NEWS b/NEWS index 694493cca265a..8a17587c29fc7 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,71 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.1.25 +23 Nov 2023, PHP 8.1.26 + +- Core: + . Fixed bug GH-12468 (Double-free of doc_comment when overriding static + property via trait). (ilutov) + . Fixed segfault caused by weak references to FFI objects. (sj-i) + . Fixed max_execution_time: don't delete an unitialized timer. (Kévin Dunglas) + +- DOM: + . Fix registerNodeClass with abstract class crashing. (nielsdos) + . Add missing NULL pointer error check. (icy17) + . Fix validation logic of php:function() callbacks. (nielsdos) + +- Fiber: + . Fixed bug GH-11121 (ReflectionFiber segfault). (danog, trowski, bwoebi) + +- FPM: + . Fixed bug GH-9921 (Loading ext in FPM config does not register module + handlers). (Jakub Zelenka) + . Fixed bug GH-12232 (FPM: segfault dynamically loading extension without + opcache). (Jakub Zelenka) + +- Intl: + . Removed the BC break on IntlDateFormatter::construct which threw an + exception with an invalid locale. (David Carlier) + +- Opcache: + . Added warning when JIT cannot be enabled. (danog) + . Fixed bug GH-8143 (Crashes in zend_accel_inheritance_cache_find since + upgrading to 8.1.3 due to corrupt on-disk file cache). (turchanov) + +- OpenSSL: + . Fixed bug GH-12489 (Missing sigbio creation checking in openssl_cms_verify). + (Jakub Zelenka) + +- PCRE: + . Fixed bug GH-11374 (Backport upstream fix, Different preg_match result + with -d pcre.jit=0). (mvorisek) + +- SOAP: + . Fixed bug GH-12392 (Segmentation fault on SoapClient::__getTypes). + (nielsdos) + . Fixed bug #66150 (SOAP WSDL cache race condition causes Segmentation + Fault). (nielsdos) + . Fixed bug #67617 (SOAP leaves incomplete cache file on ENOSPC). (nielsdos) + . Fix incorrect uri check in SOAP caching. (nielsdos) + . Fix segfault and assertion failure with refcounted props and arrays. + (nielsdos) + . Fix potential crash with an edge case of persistent encoders. (nielsdos) + . Fixed bug #75306 (Memleak in SoapClient). (nielsdos) + +- Streams: + . Fixed bug #75708 (getimagesize with "&$imageinfo" fails on StreamWrappers). + (Jakub Zelenka) + +- XMLReader: + . Add missing NULL pointer error check. (icy17) + +- XMLWriter: + . Add missing NULL pointer error check. (icy17) + +- XSL: + . Add missing module dependency. (nielsdos) + . Fix validation logic of php:function() callbacks. (nielsdos) + +26 Oct 2023, PHP 8.1.25 - Core: . Fixed bug GH-12207 (memory leak when class using trait with doc block). diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c index 72441c000e660..f0aaeb229351b 100644 --- a/Zend/Optimizer/block_pass.c +++ b/Zend/Optimizer/block_pass.c @@ -172,6 +172,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array && opline->opcode != ZEND_SWITCH_LONG && opline->opcode != ZEND_SWITCH_STRING && opline->opcode != ZEND_MATCH + && opline->opcode != ZEND_MATCH_ERROR && zend_optimizer_update_op1_const(op_array, opline, &c)) { VAR_SOURCE(op1) = NULL; if (opline->opcode != ZEND_JMP_NULL diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 4cc9f70c27f05..084a7abf9f7a7 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -77,11 +77,12 @@ #define CHECK_SCC_VAR(var2) \ do { \ if (!ssa->vars[var2].no_val) { \ - if (dfs[var2] < 0) { \ - zend_ssa_check_scc_var(op_array, ssa, var2, index, dfs, root, stack); \ + if (ssa->vars[var2].scc < 0) { \ + zend_ssa_check_scc_var(op_array, ssa, var2, index, stack); \ } \ - if (ssa->vars[var2].scc < 0 && dfs[root[var]] >= dfs[root[var2]]) { \ - root[var] = root[var2]; \ + if (ssa->vars[var2].scc < ssa->vars[var].scc) { \ + ssa->vars[var].scc = ssa->vars[var2].scc; \ + is_root = 0; \ } \ } \ } while (0) @@ -172,15 +173,17 @@ static inline bool sub_will_overflow(zend_long a, zend_long b) { } #endif -static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, int *dfs, int *root, zend_worklist_stack *stack) /* {{{ */ +#if 0 +/* Recursive Pearce's SCC algorithm implementation */ +static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack) /* {{{ */ { + int is_root = 1; #ifdef SYM_RANGE zend_ssa_phi *p; #endif - dfs[var] = *index; + ssa->vars[var].scc = *index; (*index)++; - root[var] = var; FOR_EACH_VAR_USAGE(var, CHECK_SCC_VAR); @@ -193,17 +196,20 @@ static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, } #endif - if (root[var] == var) { - ssa->vars[var].scc = ssa->sccs; + if (is_root) { + ssa->sccs--; while (stack->len > 0) { int var2 = zend_worklist_stack_peek(stack); - if (dfs[var2] <= dfs[var]) { + if (ssa->vars[var2].scc < ssa->vars[var].scc) { break; } zend_worklist_stack_pop(stack); ssa->vars[var2].scc = ssa->sccs; + (*index)--; } - ssa->sccs++; + ssa->vars[var].scc = ssa->sccs; + ssa->vars[var].scc_entry = 1; + (*index)--; } else { zend_worklist_stack_push(stack, var); } @@ -212,50 +218,277 @@ static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ { - int index = 0, *dfs, *root; + int index = 0; zend_worklist_stack stack; int j; - ALLOCA_FLAG(dfs_use_heap) - ALLOCA_FLAG(root_use_heap) ALLOCA_FLAG(stack_use_heap) - dfs = do_alloca(sizeof(int) * ssa->vars_count, dfs_use_heap); - memset(dfs, -1, sizeof(int) * ssa->vars_count); - root = do_alloca(sizeof(int) * ssa->vars_count, root_use_heap); ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); - /* Find SCCs using Tarjan's algorithm. */ + /* Find SCCs using Pearce's algorithm. */ + ssa->sccs = ssa->vars_count; for (j = 0; j < ssa->vars_count; j++) { - if (!ssa->vars[j].no_val && dfs[j] < 0) { - zend_ssa_check_scc_var(op_array, ssa, j, &index, dfs, root, &stack); + if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { + zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack); } } - /* Revert SCC order. This results in a topological order. */ + if (ssa->sccs) { + /* Shift SCC indexes. */ + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + ssa->vars[j].scc -= ssa->sccs; + } + } + } + ssa->sccs = ssa->vars_count - ssa->sccs; + for (j = 0; j < ssa->vars_count; j++) { if (ssa->vars[j].scc >= 0) { - ssa->vars[j].scc = ssa->sccs - (ssa->vars[j].scc + 1); + int var = j; + FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); + } + } + + ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); + return SUCCESS; +} +/* }}} */ + +#else +/* Iterative Pearce's SCC algorithm implementation */ + +typedef struct _zend_scc_iterator { + int state; + int last; + union { + int use; + zend_ssa_phi *phi; + }; +} zend_scc_iterator; + +static int zend_scc_next(const zend_op_array *op_array, zend_ssa *ssa, int var, zend_scc_iterator *iterator) /* {{{ */ +{ + zend_ssa_phi *phi; + int use, var2; + + switch (iterator->state) { + case 0: goto state_0; + case 1: use = iterator->use; goto state_1; + case 2: use = iterator->use; goto state_2; + case 3: use = iterator->use; goto state_3; + case 4: use = iterator->use; goto state_4; + case 5: use = iterator->use; goto state_5; + case 6: use = iterator->use; goto state_6; + case 7: use = iterator->use; goto state_7; + case 8: use = iterator->use; goto state_8; + case 9: phi = iterator->phi; goto state_9; +#ifdef SYM_RANGE + case 10: phi = iterator->phi; goto state_10; +#endif + case 11: goto state_11; + } + +state_0: + use = ssa->vars[var].use_chain; + while (use >= 0) { + iterator->use = use; + var2 = ssa->ops[use].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 1; + return var2; + } +state_1: + var2 = ssa->ops[use].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 2; + return var2; + } +state_2: + var2 = ssa->ops[use].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 3; + return var2; + } +state_3: + if (op_array->opcodes[use].opcode == ZEND_OP_DATA) { + var2 = ssa->ops[use-1].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 4; + return var2; + } +state_4: + var2 = ssa->ops[use-1].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 5; + return var2; + } +state_5: + var2 = ssa->ops[use-1].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 8; + return var2; + } + } else if ((uint32_t)use+1 < op_array->last && + op_array->opcodes[use+1].opcode == ZEND_OP_DATA) { + var2 = ssa->ops[use+1].op1_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 6; + return var2; + } +state_6: + var2 = ssa->ops[use+1].op2_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 7; + return var2; + } +state_7: + var2 = ssa->ops[use+1].result_def; + if (var2 >= 0 && !ssa->vars[var2].no_val) { + iterator->state = 8; + return var2; + } + } +state_8: + use = zend_ssa_next_use(ssa->ops, var, use); + } + + phi = ssa->vars[var].phi_use_chain; + while (phi) { + var2 = phi->ssa_var; + if (!ssa->vars[var2].no_val) { + iterator->state = 9; + iterator->phi = phi; + return var2; + } +state_9: + phi = zend_ssa_next_use_phi(ssa, var, phi); + } + +#ifdef SYM_RANGE + /* Process symbolic control-flow constraints */ + phi = ssa->vars[var].sym_use_chain; + while (phi) { + var2 = phi->ssa_var; + if (!ssa->vars[var2].no_val) { + iterator->state = 10; + iterator->phi = phi; + return var2; + } +state_10: + phi = phi->sym_use_chain; + } +#endif + + iterator->state = 11; +state_11: + return -1; +} +/* }}} */ + +static void zend_ssa_check_scc_var(const zend_op_array *op_array, zend_ssa *ssa, int var, int *index, zend_worklist_stack *stack, zend_worklist_stack *vstack, zend_scc_iterator *iterators) /* {{{ */ +{ +restart: + zend_worklist_stack_push(vstack, var); + iterators[var].state = 0; + iterators[var].last = -1; + ssa->vars[var].scc_entry = 1; + ssa->vars[var].scc = *index; + (*index)++; + + while (vstack->len > 0) { + var = zend_worklist_stack_peek(vstack); + while (1) { + int var2; + + if (iterators[var].last >= 0) { + /* finish edge */ + var2 = iterators[var].last; + if (ssa->vars[var2].scc < ssa->vars[var].scc) { + ssa->vars[var].scc = ssa->vars[var2].scc; + ssa->vars[var].scc_entry = 0; + } + } + var2 = zend_scc_next(op_array, ssa, var, iterators + var); + iterators[var].last = var2; + if (var2 < 0) break; + /* begin edge */ + if (ssa->vars[var2].scc < 0) { + var = var2; + goto restart; + } + } + + /* finish visiting */ + zend_worklist_stack_pop(vstack); + if (ssa->vars[var].scc_entry) { + ssa->sccs--; + while (stack->len > 0) { + int var2 = zend_worklist_stack_peek(stack); + if (ssa->vars[var2].scc < ssa->vars[var].scc) { + break; + } + zend_worklist_stack_pop(stack); + ssa->vars[var2].scc = ssa->sccs; + (*index)--; + } + ssa->vars[var].scc = ssa->sccs; + (*index)--; + } else { + zend_worklist_stack_push(stack, var); + } + } +} +/* }}} */ + +ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ +{ + int index = 0; + zend_worklist_stack stack, vstack; + zend_scc_iterator *iterators; + int j; + ALLOCA_FLAG(stack_use_heap) + ALLOCA_FLAG(vstack_use_heap) + ALLOCA_FLAG(iterators_use_heap) + + iterators = do_alloca(sizeof(zend_scc_iterator) * ssa->vars_count, iterators_use_heap); + ZEND_WORKLIST_STACK_ALLOCA(&vstack, ssa->vars_count, vstack_use_heap); + ZEND_WORKLIST_STACK_ALLOCA(&stack, ssa->vars_count, stack_use_heap); + + /* Find SCCs using Pearce's algorithm. */ + ssa->sccs = ssa->vars_count; + for (j = 0; j < ssa->vars_count; j++) { + if (!ssa->vars[j].no_val && ssa->vars[j].scc < 0) { + zend_ssa_check_scc_var(op_array, ssa, j, &index, &stack, &vstack, iterators); } } + if (ssa->sccs) { + /* Shift SCC indexes. */ + for (j = 0; j < ssa->vars_count; j++) { + if (ssa->vars[j].scc >= 0) { + ssa->vars[j].scc -= ssa->sccs; + } + } + } + ssa->sccs = ssa->vars_count - ssa->sccs; + for (j = 0; j < ssa->vars_count; j++) { if (ssa->vars[j].scc >= 0) { int var = j; - if (root[j] == j) { - ssa->vars[j].scc_entry = 1; - } FOR_EACH_VAR_USAGE(var, CHECK_SCC_ENTRY); } } ZEND_WORKLIST_STACK_FREE_ALLOCA(&stack, stack_use_heap); - free_alloca(root, root_use_heap); - free_alloca(dfs, dfs_use_heap); - + ZEND_WORKLIST_STACK_FREE_ALLOCA(&vstack, vstack_use_heap); + free_alloca(iterators, iterators_use_heap); return SUCCESS; } /* }}} */ +#endif + ZEND_API int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa) /* {{{ */ { zend_ssa_var *ssa_vars = ssa->vars; @@ -1746,6 +1979,12 @@ static uint32_t get_ssa_alias_types(zend_ssa_alias_kind alias) { } \ if (__var >= 0) { \ zend_ssa_var *__ssa_var = &ssa_vars[__var]; \ + if (__ssa_var->var < op_array->num_args) { \ + if (__type & MAY_BE_RC1) { \ + /* TODO: may be captured by exception backtreace */ \ + __type |= MAY_BE_RCN; \ + } \ + } \ if (__ssa_var->var < op_array->last_var) { \ if (__type & (MAY_BE_REF|MAY_BE_RCN)) { \ __type |= MAY_BE_RC1 | MAY_BE_RCN; \ @@ -2291,7 +2530,8 @@ static zend_always_inline int _zend_update_type_info( * unreachable code. Propagate the empty result early, so that that the following * code may assume that operands have at least one type. */ if (!(t1 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS)) - || !(t2 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS))) { + || !(t2 & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS)) + || (ssa_op->result_use >= 0 && !(RES_USE_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_CLASS)))) { tmp = 0; if (ssa_op->result_def >= 0 && !(ssa_var_info[ssa_op->result_def].type & MAY_BE_REF)) { UPDATE_SSA_TYPE(tmp, ssa_op->result_def); @@ -3306,10 +3546,18 @@ static zend_always_inline int _zend_update_type_info( zend_uchar opcode; if (!ssa_opcodes) { - ZEND_ASSERT(j == (opline - op_array->opcodes) + 1 && "Use must be in next opline"); + if (j != (opline - op_array->opcodes) + 1) { + /* Use must be in next opline */ + tmp |= key_type | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; + break; + } opcode = op_array->opcodes[j].opcode; } else { - ZEND_ASSERT(ssa_opcodes[j] == opline + 1 && "Use must be in next opline"); + if (ssa_opcodes[j] != opline + 1) { + /* Use must be in next opline */ + tmp |= key_type | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; + break; + } opcode = ssa_opcodes[j]->opcode; } switch (opcode) { @@ -3368,7 +3616,10 @@ static zend_always_inline int _zend_update_type_info( EMPTY_SWITCH_DEFAULT_CASE() } j = zend_ssa_next_use(ssa->ops, ssa_op->result_def, j); - ZEND_ASSERT(j < 0 && "There should only be one use"); + if (j >= 0) { + tmp |= key_type | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; + break; + } } } if (((tmp & MAY_BE_ARRAY) && (tmp & MAY_BE_ARRAY_KEY_ANY)) @@ -3415,7 +3666,9 @@ static zend_always_inline int _zend_update_type_info( UPDATE_SSA_TYPE(tmp, ssa_op->result_def); break; case ZEND_FETCH_THIS: - UPDATE_SSA_OBJ_TYPE(op_array->scope, 1, ssa_op->result_def); + if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { + UPDATE_SSA_OBJ_TYPE(op_array->scope, 1, ssa_op->result_def); + } UPDATE_SSA_TYPE(MAY_BE_RCN|MAY_BE_OBJECT, ssa_op->result_def); break; case ZEND_FETCH_OBJ_R: diff --git a/Zend/tests/fibers/negative_stack_size.phpt b/Zend/tests/fibers/negative_stack_size.phpt new file mode 100644 index 0000000000000..591a8f28878d1 --- /dev/null +++ b/Zend/tests/fibers/negative_stack_size.phpt @@ -0,0 +1,16 @@ +--TEST-- +fiber.stack_size must be a positive number +--FILE-- +start(); +} catch (Throwable $e) { + echo "Exception: " . $e->getMessage()."\n"; +} +?> +DONE +--EXPECTF-- +Warning: fiber.stack_size must be a positive number in %snegative_stack_size.php on line 2 +DONE diff --git a/Zend/tests/gc_047.phpt b/Zend/tests/gc_047.phpt new file mode 100644 index 0000000000000..08403d1e99bc9 --- /dev/null +++ b/Zend/tests/gc_047.phpt @@ -0,0 +1,20 @@ +--TEST-- +GC 047: Leak after GC inside a foreach loop +--INI-- +zend.enable_gc=1 +--FILE-- + +--EXPECT-- +int(2) diff --git a/Zend/tests/gh12468_1.phpt b/Zend/tests/gh12468_1.phpt new file mode 100644 index 0000000000000..a02a28c3e354c --- /dev/null +++ b/Zend/tests/gh12468_1.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-12468: Double-free of doc_comment when overriding static property via trait +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/tests/gh12468_2.phpt b/Zend/tests/gh12468_2.phpt new file mode 100644 index 0000000000000..3097cf532e22c --- /dev/null +++ b/Zend/tests/gh12468_2.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-12468: Double-free of doc_comment when overriding static property via trait +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/zend.c b/Zend/zend.c index 351e9ac5eea75..e6f92410c0881 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -177,7 +177,12 @@ static ZEND_INI_MH(OnSetExceptionStringParamMaxLen) /* {{{ */ static ZEND_INI_MH(OnUpdateFiberStackSize) /* {{{ */ { if (new_value) { - EG(fiber_stack_size) = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + zend_long tmp = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + if (tmp < 0) { + zend_error(E_WARNING, "fiber.stack_size must be a positive number"); + return FAILURE; + } + EG(fiber_stack_size) = tmp; } else { EG(fiber_stack_size) = ZEND_FIBER_DEFAULT_C_STACK_SIZE; } diff --git a/Zend/zend.h b/Zend/zend.h index bc9862de9f687..b971a1fbb103c 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.1.25-dev" +#define ZEND_VERSION "4.1.26" #define ZEND_ENGINE_3 diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 1178133046679..5fd38a25c8b78 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2271,7 +2271,8 @@ ZEND_API void zend_collect_module_handlers(void) /* {{{ */ post_deactivate_count++; } } ZEND_HASH_FOREACH_END(); - module_request_startup_handlers = (zend_module_entry**)malloc( + module_request_startup_handlers = (zend_module_entry**)realloc( + module_request_startup_handlers, sizeof(zend_module_entry*) * (startup_count + 1 + shutdown_count + 1 + @@ -2303,7 +2304,8 @@ ZEND_API void zend_collect_module_handlers(void) /* {{{ */ } } ZEND_HASH_FOREACH_END(); - class_cleanup_handlers = (zend_class_entry**)malloc( + class_cleanup_handlers = (zend_class_entry**)realloc( + class_cleanup_handlers, sizeof(zend_class_entry*) * (class_count + 1)); class_cleanup_handlers[class_count] = NULL; @@ -2329,7 +2331,9 @@ ZEND_API void zend_startup_modules(void) /* {{{ */ ZEND_API void zend_destroy_modules(void) /* {{{ */ { free(class_cleanup_handlers); + class_cleanup_handlers = NULL; free(module_request_startup_handlers); + module_request_startup_handlers = NULL; zend_hash_graceful_reverse_destroy(&module_registry); } /* }}} */ @@ -4120,7 +4124,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z (property_info_ptr->flags & ZEND_ACC_STATIC) != 0) { property_info->offset = property_info_ptr->offset; zval_ptr_dtor(&ce->default_static_members_table[property_info->offset]); - if (property_info_ptr->doc_comment) { + if (property_info_ptr->doc_comment && property_info_ptr->ce == ce) { zend_string_release(property_info_ptr->doc_comment); } zend_hash_del(&ce->properties_info, name); @@ -4145,7 +4149,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z (property_info_ptr->flags & ZEND_ACC_STATIC) == 0) { property_info->offset = property_info_ptr->offset; zval_ptr_dtor(&ce->default_properties_table[OBJ_PROP_TO_NUM(property_info->offset)]); - if (property_info_ptr->doc_comment) { + if (property_info_ptr->doc_comment && property_info_ptr->ce == ce) { zend_string_release_ex(property_info_ptr->doc_comment, 1); } zend_hash_del(&ce->properties_info, name); diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 7fd41f3b1ab96..c41f6118607e2 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -1914,7 +1914,7 @@ static zend_mm_heap *zend_mm_init(void) heap->peak = 0; #endif #if ZEND_MM_LIMIT - heap->limit = ((size_t)Z_L(-1) >> (size_t)Z_L(1)); + heap->limit = (size_t)Z_L(-1) >> 1; heap->overflow = 0; #endif #if ZEND_MM_CUSTOM @@ -2859,7 +2859,7 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) zend_mm_heap *mm_heap = alloc_globals->mm_heap = malloc(sizeof(zend_mm_heap)); memset(mm_heap, 0, sizeof(zend_mm_heap)); mm_heap->use_custom_heap = ZEND_MM_CUSTOM_HEAP_STD; - mm_heap->limit = ((size_t)Z_L(-1) >> (size_t)Z_L(1)); + mm_heap->limit = (size_t)Z_L(-1) >> 1; mm_heap->overflow = 0; if (!tracked) { @@ -3048,7 +3048,7 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void heap->peak = 0; #endif #if ZEND_MM_LIMIT - heap->limit = (Z_L(-1) >> Z_L(1)); + heap->limit = (size_t)Z_L(-1) >> 1; heap->overflow = 0; #endif #if ZEND_MM_CUSTOM diff --git a/Zend/zend_fibers.c b/Zend/zend_fibers.c index 59aa63ba21b43..c80d65868d37e 100644 --- a/Zend/zend_fibers.c +++ b/Zend/zend_fibers.c @@ -554,6 +554,10 @@ static zend_always_inline zend_fiber_transfer zend_fiber_resume(zend_fiber *fibe { zend_fiber *previous = EG(active_fiber); + if (previous) { + previous->execute_data = EG(current_execute_data); + } + fiber->caller = EG(current_fiber_context); EG(active_fiber) = fiber; @@ -571,6 +575,7 @@ static zend_always_inline zend_fiber_transfer zend_fiber_suspend(zend_fiber *fib zend_fiber_context *caller = fiber->caller; fiber->previous = EG(current_fiber_context); fiber->caller = NULL; + fiber->execute_data = EG(current_execute_data); return zend_fiber_switch_to(caller, value, false); } @@ -741,7 +746,6 @@ ZEND_METHOD(Fiber, suspend) ZEND_ASSERT(fiber->context.status == ZEND_FIBER_STATUS_RUNNING || fiber->context.status == ZEND_FIBER_STATUS_SUSPENDED); - fiber->execute_data = EG(current_execute_data); fiber->stack_bottom->prev_execute_data = NULL; zend_fiber_transfer transfer = zend_fiber_suspend(fiber, value); diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index f062747eaeb63..c9034ba5e34c6 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -1696,7 +1696,7 @@ static void zend_gc_root_tmpvars(void) { } uint32_t kind = range->var & ZEND_LIVE_MASK; - if (kind == ZEND_LIVE_TMPVAR) { + if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) { uint32_t var_num = range->var & ~ZEND_LIVE_MASK; zval *var = ZEND_CALL_VAR(ex, var_num); if (Z_REFCOUNTED_P(var)) { diff --git a/Zend/zend_ini.c b/Zend/zend_ini.c index 1b4710eb503a0..866e1b9c2097c 100644 --- a/Zend/zend_ini.c +++ b/Zend/zend_ini.c @@ -211,7 +211,6 @@ ZEND_API zend_result zend_register_ini_entries_ex(const zend_ini_entry_def *ini_ * lead to death. */ if (directives != EG(ini_directives)) { - ZEND_ASSERT(module_type == MODULE_TEMPORARY); directives = EG(ini_directives); } else { ZEND_ASSERT(module_type == MODULE_PERSISTENT); diff --git a/Zend/zend_list.c b/Zend/zend_list.c index b3409e33ce845..369eb08d16107 100644 --- a/Zend/zend_list.c +++ b/Zend/zend_list.c @@ -275,6 +275,7 @@ ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_fu ZVAL_PTR(&zv, lde); if (zend_hash_next_index_insert(&list_destructors, &zv) == NULL) { + free(lde); return FAILURE; } return list_destructors.nNextFreeElement-1; diff --git a/Zend/zend_max_execution_timer.c b/Zend/zend_max_execution_timer.c index b1c83e9cbb3a6..480631dcb169a 100644 --- a/Zend/zend_max_execution_timer.c +++ b/Zend/zend_max_execution_timer.c @@ -41,12 +41,13 @@ ZEND_API void zend_max_execution_timer_init(void) /* {{{ */ sev.sigev_signo = SIGRTMIN; sev.sigev_notify_thread_id = (pid_t) syscall(SYS_gettid); - EG(pid) = getpid(); // Measure wall time instead of CPU time as originally planned now that it is possible https://github.com/php/php-src/pull/6504#issuecomment-1370303727 if (timer_create(CLOCK_BOOTTIME, &sev, &EG(max_execution_timer_timer)) != 0) { zend_strerror_noreturn(E_ERROR, errno, "Could not create timer"); } + EG(pid) = getpid(); + # ifdef MAX_EXECUTION_TIMERS_DEBUG fprintf(stderr, "Timer %#jx created on thread %d\n", (uintmax_t) EG(max_execution_timer_timer), sev.sigev_notify_thread_id); # endif diff --git a/configure.ac b/configure.ac index 07fdab313cd4b..782c61d951b1c 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.25-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.1.26],[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/dl_test/dl_test.c b/ext/dl_test/dl_test.c index 88a298b0ec88a..fed881b2e994c 100644 --- a/ext/dl_test/dl_test.c +++ b/ext/dl_test/dl_test.c @@ -69,6 +69,10 @@ PHP_MINIT_FUNCTION(dl_test) REGISTER_INI_ENTRIES(); } + if (getenv("PHP_DL_TEST_MODULE_DEBUG")) { + fprintf(stderr, "DL TEST MINIT\n"); + } + return SUCCESS; } /* }}} */ @@ -83,6 +87,10 @@ static PHP_MSHUTDOWN_FUNCTION(dl_test) UNREGISTER_INI_ENTRIES(); } + if (getenv("PHP_DL_TEST_MODULE_DEBUG")) { + fprintf(stderr, "DL TEST MSHUTDOWN\n"); + } + return SUCCESS; } /* }}} */ @@ -94,6 +102,21 @@ PHP_RINIT_FUNCTION(dl_test) ZEND_TSRMLS_CACHE_UPDATE(); #endif + if (getenv("PHP_DL_TEST_MODULE_DEBUG")) { + fprintf(stderr, "DL TEST RINIT\n"); + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION */ +PHP_RSHUTDOWN_FUNCTION(dl_test) +{ + if (getenv("PHP_DL_TEST_MODULE_DEBUG")) { + fprintf(stderr, "DL TEST RSHUTDOWN\n"); + } + return SUCCESS; } /* }}} */ @@ -127,7 +150,7 @@ zend_module_entry dl_test_module_entry = { PHP_MINIT(dl_test), PHP_MSHUTDOWN(dl_test), PHP_RINIT(dl_test), - NULL, + PHP_RSHUTDOWN(dl_test), PHP_MINFO(dl_test), PHP_DL_TEST_VERSION, PHP_MODULE_GLOBALS(dl_test), diff --git a/ext/dom/document.c b/ext/dom/document.c index 64da4f051be2c..59f00897a69aa 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -1148,6 +1148,9 @@ char *_dom_get_valid_file_path(char *source, char *resolved_path, int resolved_p int isFileUri = 0; uri = xmlCreateURI(); + if (uri == NULL) { + return NULL; + } escsource = xmlURIEscapeStr((xmlChar *) source, (xmlChar *) ":"); xmlParseURIReference(uri, (char *) escsource); xmlFree(escsource); @@ -2096,6 +2099,10 @@ PHP_METHOD(DOMDocument, registerNodeClass) } if (ce == NULL || instanceof_function(ce, basece)) { + if (UNEXPECTED(ce != NULL && (ce->ce_flags & ZEND_ACC_ABSTRACT))) { + zend_argument_value_error(2, "must not be an abstract class"); + RETURN_THROWS(); + } DOM_GET_OBJ(docp, id, xmlDocPtr, intern); dom_set_doc_classmap(intern->document, basece, ce); RETURN_TRUE; diff --git a/ext/dom/tests/php_function_edge_cases.phpt b/ext/dom/tests/php_function_edge_cases.phpt new file mode 100644 index 0000000000000..1091b50ce19bd --- /dev/null +++ b/ext/dom/tests/php_function_edge_cases.phpt @@ -0,0 +1,27 @@ +--TEST-- +php:function() edge cases +--EXTENSIONS-- +dom +--FILE-- +loadHTML('hello'); +$xpath = new DOMXpath($doc); +$xpath->registerNamespace("php", "http://php.net/xpath"); +$xpath->registerPHPFunctions(); +try { + $xpath->query("//a[php:function(3)]"); +} catch (TypeError $e) { + echo $e->getMessage(), "\n"; +} +try { + $xpath->query("//a[php:function()]"); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +Handler name must be a string +Function name must be passed as the first argument diff --git a/ext/dom/tests/registerNodeClass_abstract_class.phpt b/ext/dom/tests/registerNodeClass_abstract_class.phpt new file mode 100644 index 0000000000000..24124d712ea09 --- /dev/null +++ b/ext/dom/tests/registerNodeClass_abstract_class.phpt @@ -0,0 +1,24 @@ +--TEST-- +registerNodeClass() with an abstract class should fail +--EXTENSIONS-- +dom +--FILE-- +registerNodeClass("DOMElement", "Test"); +} catch (ValueError $e) { + echo "ValueError: ", $e->getMessage(), "\n"; +} + +$dom->createElement("foo"); + +?> +--EXPECT-- +ValueError: DOMDocument::registerNodeClass(): Argument #2 ($extendedClass) must not be an abstract class diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index 62e11f6b99bfb..73ceb49362781 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -70,12 +70,17 @@ static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, return; } + if (UNEXPECTED(nargs == 0)) { + zend_throw_error(NULL, "Function name must be passed as the first argument"); + return; + } + fci.param_count = nargs - 1; if (fci.param_count > 0) { fci.params = safe_emalloc(fci.param_count, sizeof(zval), 0); } /* Reverse order to pop values off ctxt stack */ - for (i = nargs - 2; i >= 0; i--) { + for (i = fci.param_count - 1; i >= 0; i--) { obj = valuePop(ctxt); switch (obj->type) { case XPATH_STRING: @@ -128,11 +133,12 @@ static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, fci.size = sizeof(fci); + /* Last element of the stack is the function name */ obj = valuePop(ctxt); if (obj->stringval == NULL) { zend_type_error("Handler name must be a string"); xmlXPathFreeObject(obj); - goto cleanup; + goto cleanup_no_callable; } ZVAL_STRING(&fci.function_name, (char *) obj->stringval); xmlXPathFreeObject(obj); @@ -177,6 +183,7 @@ static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, cleanup: zend_string_release_ex(callable, 0); zval_ptr_dtor_nogc(&fci.function_name); +cleanup_no_callable: if (fci.param_count > 0) { for (i = 0; i < nargs - 1; i++) { zval_ptr_dtor(&fci.params[i]); diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index efb4524c76b1a..a2779b65b120c 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -24,6 +24,7 @@ #include "php_scandir.h" #include "zend_exceptions.h" #include "zend_closures.h" +#include "zend_weakrefs.h" #include "main/SAPI.h" #include "ffi_arginfo.h" @@ -2143,6 +2144,10 @@ static void zend_ffi_ctype_free_obj(zend_object *object) /* {{{ */ zend_ffi_ctype *ctype = (zend_ffi_ctype*)object; zend_ffi_type_dtor(ctype->type); + + if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) { + zend_weakrefs_notify(object); + } } /* }}} */ @@ -2369,6 +2374,10 @@ static void zend_ffi_free_obj(zend_object *object) /* {{{ */ zend_hash_destroy(ffi->tags); efree(ffi->tags); } + + if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) { + zend_weakrefs_notify(object); + } } /* }}} */ @@ -2377,6 +2386,10 @@ static void zend_ffi_cdata_free_obj(zend_object *object) /* {{{ */ zend_ffi_cdata *cdata = (zend_ffi_cdata*)object; zend_ffi_cdata_dtor(cdata); + + if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) { + zend_weakrefs_notify(object); + } } /* }}} */ diff --git a/ext/ffi/tests/weak_reference_001.phpt b/ext/ffi/tests/weak_reference_001.phpt new file mode 100644 index 0000000000000..56f0be981f629 --- /dev/null +++ b/ext/ffi/tests/weak_reference_001.phpt @@ -0,0 +1,17 @@ +--TEST-- +Weak reference to \FFI +--EXTENSIONS-- +ffi +--INI-- +ffi.enable=1 +--FILE-- +get() === $ffi); +unset($ffi); +var_dump($ref->get() === null); +?> +--EXPECTF-- +bool(true) +bool(true) diff --git a/ext/ffi/tests/weak_reference_002.phpt b/ext/ffi/tests/weak_reference_002.phpt new file mode 100644 index 0000000000000..5c36cfa703455 --- /dev/null +++ b/ext/ffi/tests/weak_reference_002.phpt @@ -0,0 +1,36 @@ +--TEST-- +Weak reference to \FFI\CData +--EXTENSIONS-- +ffi +--INI-- +ffi.enable=1 +--FILE-- +get() === $cdata_value); +var_dump($ref_array->get() === $cdata_array); +var_dump($ref_free->get() === $cdata_free); + +unset($cdata_value); +unset($cdata_array); +unset($cdata_free); + +var_dump($ref_value->get() === null); +var_dump($ref_array->get() === null); +var_dump($ref_free->get() === null); +?> +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/ffi/tests/weak_reference_003.phpt b/ext/ffi/tests/weak_reference_003.phpt new file mode 100644 index 0000000000000..15b9397a6856b --- /dev/null +++ b/ext/ffi/tests/weak_reference_003.phpt @@ -0,0 +1,17 @@ +--TEST-- +Weak reference to \FFI\CType +--EXTENSIONS-- +ffi +--INI-- +ffi.enable=1 +--FILE-- +get() === $ctype); +unset($ctype); +var_dump($ref->get() === null); +?> +--EXPECTF-- +bool(true) +bool(true) diff --git a/ext/ffi/tests/weak_reference_004.phpt b/ext/ffi/tests/weak_reference_004.phpt new file mode 100644 index 0000000000000..500776fa21865 --- /dev/null +++ b/ext/ffi/tests/weak_reference_004.phpt @@ -0,0 +1,36 @@ +--TEST-- +Using FFI Types for keys of a WeakMap +--EXTENSIONS-- +ffi +--INI-- +ffi.enable=1 +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) diff --git a/ext/intl/dateformat/dateformat_create.cpp b/ext/intl/dateformat/dateformat_create.cpp index e14eb4f1a2f8a..5c96f41fadf35 100644 --- a/ext/intl/dateformat/dateformat_create.cpp +++ b/ext/intl/dateformat/dateformat_create.cpp @@ -113,8 +113,7 @@ static zend_result datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS, zend_error_handlin locale = Locale::createFromName(locale_str); /* get*Name accessors being set does not preclude being bogus */ if (locale.isBogus() || strlen(locale.getISO3Language()) == 0) { - intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_create: invalid locale", 0); - return FAILURE; + goto error; } /* process calendar */ diff --git a/ext/intl/tests/gh12282.phpt b/ext/intl/tests/gh12282.phpt index 62d090ba6166b..a30899a08c7d0 100644 --- a/ext/intl/tests/gh12282.phpt +++ b/ext/intl/tests/gh12282.phpt @@ -5,17 +5,18 @@ intl --FILE-- getMessage(); -} +)); +Locale::setDefault('xx'); +var_dump(new IntlDateFormatter(Locale::getDefault())); --EXPECT-- -datefmt_create: invalid locale: U_ILLEGAL_ARGUMENT_ERROR +object(IntlDateFormatter)#1 (0) { +} +object(IntlDateFormatter)#1 (0) { +} diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 429eadf023eb0..530620f6259ff 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3270,6 +3270,11 @@ static zend_result accel_post_startup(void) || zend_jit_startup(ZSMMG(reserved), jit_size, reattached) != SUCCESS) { JIT_G(enabled) = 0; JIT_G(on) = 0; + /* The JIT is implicitly disabled with opcache.jit_buffer_size=0, so we don't want to + * emit a warning here. */ + if (JIT_G(buffer_size) != 0) { + zend_accel_error(ACCEL_LOG_WARNING, "Could not enable JIT!"); + } } } #endif diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index d969f744e6b4b..4d2baddb90090 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -5113,7 +5113,7 @@ ZEND_EXT_API void zend_jit_activate(void) ZEND_EXT_API void zend_jit_deactivate(void) { - if (zend_jit_profile_counter) { + if (zend_jit_profile_counter && !CG(unclean_shutdown)) { zend_class_entry *ce; zend_shared_alloc_lock(); @@ -5131,9 +5131,9 @@ ZEND_EXT_API void zend_jit_deactivate(void) zend_jit_protect(); SHM_PROTECT(); zend_shared_alloc_unlock(); - - zend_jit_profile_counter = 0; } + + zend_jit_profile_counter = 0; } static void zend_jit_restart_preloaded_op_array(zend_op_array *op_array) diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index 0a0bd23c3160a..1583706855d41 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -14932,6 +14932,7 @@ static bool zend_jit_fetch_indirect_var(dasm_State **Dst, const zend_op *opline, if (opline->op1_type != IS_VAR || (opline-1)->result_type != IS_VAR || (opline-1)->result.var != opline->op1.var || + (opline-1)->op1_type == IS_VAR || (opline-1)->op2_type == IS_VAR || (opline-1)->op2_type == IS_TMP_VAR) { | GET_ZVAL_PTR FCARG1x, var_addr, TMP1 diff --git a/ext/opcache/jit/zend_jit_helpers.c b/ext/opcache/jit/zend_jit_helpers.c index 447e2269aa3d0..fc52986ea197e 100644 --- a/ext/opcache/jit/zend_jit_helpers.c +++ b/ext/opcache/jit/zend_jit_helpers.c @@ -1938,7 +1938,7 @@ static void ZEND_FASTCALL zend_jit_fetch_obj_is_dynamic(zend_object *zobj, intpt if (EXPECTED(retval)) { intptr_t idx = (char*)retval - (char*)zobj->properties->arData; CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx)); - ZVAL_COPY(result, retval); + ZVAL_COPY_DEREF(result, retval); return; } } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 0e103c83570a8..4a57986130666 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4935,14 +4935,11 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } op2_addr = OP2_REG_ADDR(); op2_info = OP2_INFO(); - if (ra - && ssa_op->op2_def >= 0 - && (!ssa->vars[ssa_op->op2_def].no_val - || (zend_jit_trace_type_to_info(STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var))) & MAY_BE_ANY) != - (op2_info & MAY_BE_ANY))) { - op2_def_addr = OP2_DEF_REG_ADDR(); - } else { + + if (ssa_op->op2_def < 0 || (Z_MODE(op2_addr) == IS_REG && ssa->vars[ssa_op->op2_def].no_val)) { op2_def_addr = op2_addr; + } else { + op2_def_addr = OP2_DEF_REG_ADDR(); } CHECK_OP2_TRACE_TYPE(); op1_info = OP1_INFO(); @@ -5038,12 +5035,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par ZEND_FALLTHROUGH; case ZEND_QM_ASSIGN: op1_addr = OP1_REG_ADDR(); - if (ra - && ssa_op->op1_def >= 0 - && !ssa->vars[ssa_op->op1_def].no_val) { - op1_def_addr = OP1_DEF_REG_ADDR(); - } else { + if (ssa_op->op1_def < 0 || (Z_MODE(op1_addr) == IS_REG && ssa->vars[ssa_op->op1_def].no_val)) { op1_def_addr = op1_addr; + } else { + op1_def_addr = OP1_DEF_REG_ADDR(); } op1_info = OP1_INFO(); CHECK_OP1_TRACE_TYPE(); @@ -5134,12 +5129,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par break; } op1_addr = OP1_REG_ADDR(); - if (ra - && ssa_op->op1_def >= 0 - && !ssa->vars[ssa_op->op1_def].no_val) { - op1_def_addr = OP1_DEF_REG_ADDR(); - } else { + if (ssa_op->op1_def < 0 || (Z_MODE(op1_addr) == IS_REG && ssa->vars[ssa_op->op1_def].no_val)) { op1_def_addr = op1_addr; + } else { + op1_def_addr = OP1_DEF_REG_ADDR(); } op1_info = OP1_INFO(); CHECK_OP1_TRACE_TYPE(); @@ -6353,7 +6346,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op1.var), type, (gen_handler || type == IS_UNKNOWN || !ra || (!ra[ssa_op->op1_def] && - (opline->opcode == ZEND_ASSIGN || !ssa->vars[ssa_op->op1_def].no_val)))); + !(ssa->vars[ssa_op->op1_def].no_val && + Z_MODE(OP1_REG_ADDR()) == IS_REG && + (opline->opcode == ZEND_QM_ASSIGN || + opline->opcode == ZEND_SEND_VAR || + opline->opcode == ZEND_SEND_VAR_EX || + opline->opcode == ZEND_SEND_VAR_NO_REF || + opline->opcode == ZEND_SEND_VAR_NO_REF_EX || + opline->opcode == ZEND_SEND_FUNC_ARG))))); if (type != IS_UNKNOWN) { ssa->var_info[ssa_op->op1_def].type &= ~MAY_BE_GUARD; if (ra && ra[ssa_op->op1_def]) { @@ -6399,7 +6399,10 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par } SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var), type, (gen_handler || type == IS_UNKNOWN || !ra || - (!ra[ssa_op->op2_def] && !ssa->vars[ssa_op->op2_def].no_val))); + (!ra[ssa_op->op2_def] && + !(ssa->vars[ssa_op->op2_def].no_val && + Z_MODE(OP2_REG_ADDR()) == IS_REG && + opline->opcode == ZEND_ASSIGN)))); if (type != IS_UNKNOWN) { ssa->var_info[ssa_op->op2_def].type &= ~MAY_BE_GUARD; if (ra && ra[ssa_op->op2_def]) { diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index dcb32d92b5e8b..b3989a4ae254d 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -5157,7 +5157,7 @@ static int zend_jit_long_math_helper(dasm_State **Dst, result_reg = ZREG_R0; } else { /* ASSIGN_DIM_OP */ - if (sizeof(void*) == 4 + if (ZREG_FCARG1 == ZREG_RCX && (opcode == ZEND_SL || opcode == ZEND_SR) && Z_MODE(op2_addr) != IS_CONST_ZVAL) { result_reg = ZREG_R2; @@ -15885,6 +15885,7 @@ static bool zend_jit_fetch_indirect_var(dasm_State **Dst, const zend_op *opline, if (opline->op1_type != IS_VAR || (opline-1)->result_type != IS_VAR || (opline-1)->result.var != opline->op1.var || + (opline-1)->op1_type == IS_VAR || (opline-1)->op2_type == IS_VAR || (opline-1)->op2_type == IS_TMP_VAR) { | GET_ZVAL_PTR FCARG1a, var_addr diff --git a/ext/opcache/tests/jit/assign_dim_017.phpt b/ext/opcache/tests/jit/assign_dim_017.phpt new file mode 100644 index 0000000000000..cc5eef1265c59 --- /dev/null +++ b/ext/opcache/tests/jit/assign_dim_017.phpt @@ -0,0 +1,20 @@ +--TEST-- +JIT ASSIGN_DIM: 017 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--FILE-- + +DONE +--EXPECT-- +DONE \ No newline at end of file diff --git a/ext/opcache/tests/jit/gh11917.phpt b/ext/opcache/tests/jit/gh11917.phpt new file mode 100644 index 0000000000000..a66105d7a8f7a --- /dev/null +++ b/ext/opcache/tests/jit/gh11917.phpt @@ -0,0 +1,61 @@ +--TEST-- +GH-11917: primitives seem to be passed via reference instead of by value under some conditions when JIT is enabled on windows +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--FILE-- +>= $split; + if (!$overflow) { + $remaining -= $split; + $overflow = $split <= $remaining ? 0 : $split - $remaining; + + if (!$remaining) { + $i++; + $remaining = 31; + $overflow = 0; + } + } elseif (++$i != $len) { + $tempmask = (1 << $overflow) - 1; + $digit |= ($val[$i] & $tempmask) << $remaining; + $val[$i] >>= $overflow; + $remaining = 31 - $overflow; + $overflow = $split <= $remaining ? 0 : $split - $remaining; + } + + $vals[] = $digit; + } + + while ($vals[count($vals) - 1] == 0) { + unset($vals[count($vals) - 1]); + } + + return array_reverse($vals); +} +?> +--EXPECT-- +48207660 +48207660 +48207660 +48207660 diff --git a/ext/opcache/tests/jit/gh12428.phpt b/ext/opcache/tests/jit/gh12428.phpt new file mode 100644 index 0000000000000..48c6a30fc1b88 --- /dev/null +++ b/ext/opcache/tests/jit/gh12428.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-12428: Assertion with function/tracing JIT +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--FILE-- +a; + $value->a ?? null; + } +} + +validate((object) []); +validate((object) []); +validate((object) ['b' => 0]); +?> +DONE +--EXPECT-- +DONE diff --git a/ext/opcache/tests/jit/gh12482.phpt b/ext/opcache/tests/jit/gh12482.phpt new file mode 100644 index 0000000000000..021f7ce4526a3 --- /dev/null +++ b/ext/opcache/tests/jit/gh12482.phpt @@ -0,0 +1,57 @@ +--TEST-- +GH-12482: Invalid type inference +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit_hot_func=2 +--FILE-- +x = 42; + return $cloned; + } +} +class A { + use T; + public $a = 1; + public $b = 2; + public $c = 3; + public $x = 4; +} +class B { + use T; + public $x = 5; +} +$a = new A; +var_dump($a->foo()); +var_dump($a->foo()); +$b = new B; +var_dump($b->foo()); +?> +--EXPECT-- +object(A)#2 (4) { + ["a"]=> + int(1) + ["b"]=> + int(2) + ["c"]=> + int(3) + ["x"]=> + int(42) +} +object(A)#2 (4) { + ["a"]=> + int(1) + ["b"]=> + int(2) + ["c"]=> + int(3) + ["x"]=> + int(42) +} +object(B)#3 (1) { + ["x"]=> + int(42) +} diff --git a/ext/opcache/tests/jit/gh12509.phpt b/ext/opcache/tests/jit/gh12509.phpt new file mode 100644 index 0000000000000..ae8bdee661967 --- /dev/null +++ b/ext/opcache/tests/jit/gh12509.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-12509: JIT assertion when running php-parser tests +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--FILE-- + +--EXPECT-- +array(3) { + [1]=> + int(1) + [2]=> + int(2) + [3]=> + int(3) +} \ No newline at end of file diff --git a/ext/opcache/tests/jit_warning_with_zero_buffer.phpt b/ext/opcache/tests/jit_warning_with_zero_buffer.phpt new file mode 100644 index 0000000000000..1b791e2f1ae8a --- /dev/null +++ b/ext/opcache/tests/jit_warning_with_zero_buffer.phpt @@ -0,0 +1,16 @@ +--TEST-- +JIT should not emit warning with opcache.jit_buffer_size=0 +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=tracing +opcache.jit_buffer_size=0 +opcache.log_verbosity_level=2 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +bool(false) diff --git a/ext/opcache/tests/opt/block_pass_006.phpt b/ext/opcache/tests/opt/block_pass_006.phpt new file mode 100644 index 0000000000000..e70689c65abc1 --- /dev/null +++ b/ext/opcache/tests/opt/block_pass_006.phpt @@ -0,0 +1,15 @@ +--TEST-- +Block Pass 006: Inorrect QM_ASSIGN elimination +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- +''}){} +?> +--EXPECTF-- +Fatal error: Uncaught Error: Undefined constant "c" in %sblock_pass_006.php:2 +Stack trace: +#0 {main} + thrown in %sblock_pass_006.php on line 2 diff --git a/ext/opcache/tests/opt/gh10008.phpt b/ext/opcache/tests/opt/gh10008.phpt new file mode 100644 index 0000000000000..02f6feff9b023 --- /dev/null +++ b/ext/opcache/tests/opt/gh10008.phpt @@ -0,0 +1,26 @@ +--TEST-- +GH-10008: Narrowing occurred during type inference of ZEND_ADD_ARRAY_ELEMENT +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--EXTENSIONS-- +opcache +--FILE-- + $bool_or_int, $string_key => 123]; + } + + $bool_or_int = 0; + } +} +?> +DONE +--EXPECT-- +DONE diff --git a/ext/opcache/tests/opt/inference_022.phpt b/ext/opcache/tests/opt/inference_022.phpt new file mode 100644 index 0000000000000..b745499f6021a --- /dev/null +++ b/ext/opcache/tests/opt/inference_022.phpt @@ -0,0 +1,17 @@ +--TEST-- +Type inference 022: FETCH_DIM_W +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--FILE-- + +DONE +--EXPECT-- +DONE diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 3d4a9f8c68750..2c6f14f9ea588 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -71,7 +71,7 @@ static ZEND_INI_MH(OnUpdateMemoryConsumption) return FAILURE; } if (UNEXPECTED(memsize > ZEND_LONG_MAX / (1024 * 1024))) { - *p = ZEND_LONG_MAX; + *p = ZEND_LONG_MAX & ~(1024 * 1024 - 1); } else { *p = memsize * (1024 * 1024); } diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index bcee38079b353..7b4b4cbb12112 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -867,6 +867,8 @@ static void zend_file_cache_serialize_class(zval *zv, ZEND_MAP_PTR_INIT(ce->static_members_table, NULL); ZEND_MAP_PTR_INIT(ce->mutable_data, NULL); + + ce->inheritance_cache = NULL; } static void zend_file_cache_serialize_warnings( diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c index 37f6fea9199a7..f8644121572b5 100644 --- a/ext/opcache/zend_shared_alloc.c +++ b/ext/opcache/zend_shared_alloc.c @@ -252,7 +252,7 @@ int zend_shared_alloc_startup(size_t requested_size, size_t reserved_size) free(ZSMMG(shared_segments)); ZSMMG(shared_segments) = tmp_shared_segments; - ZSMMG(shared_memory_state).positions = (int *)zend_shared_alloc(sizeof(int) * ZSMMG(shared_segments_count)); + ZSMMG(shared_memory_state).positions = (size_t *)zend_shared_alloc(sizeof(size_t) * ZSMMG(shared_segments_count)); if (!ZSMMG(shared_memory_state).positions) { zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!"); return ALLOC_FAILURE; diff --git a/ext/opcache/zend_shared_alloc.h b/ext/opcache/zend_shared_alloc.h index 4e090b98ddadf..3b2da46cb3b33 100644 --- a/ext/opcache/zend_shared_alloc.h +++ b/ext/opcache/zend_shared_alloc.h @@ -95,7 +95,7 @@ typedef struct _handler_entry { } zend_shared_memory_handler_entry; typedef struct _zend_shared_memory_state { - int *positions; /* current positions for each segment */ + size_t *positions; /* current positions for each segment */ size_t shared_free; /* amount of free shared memory */ } zend_shared_memory_state; diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 10af453c89519..31baa2d0e0250 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -5900,12 +5900,15 @@ PHP_FUNCTION(openssl_cms_verify) goto clean_exit; } if (sigfile && (flags & CMS_DETACHED)) { - sigbio = php_openssl_bio_new_file(sigfile, sigfile_len, 1, PHP_OPENSSL_BIO_MODE_R(flags)); if (encoding == ENCODING_SMIME) { php_error_docref(NULL, E_WARNING, "Detached signatures not possible with S/MIME encoding"); goto clean_exit; } + sigbio = php_openssl_bio_new_file(sigfile, sigfile_len, 1, PHP_OPENSSL_BIO_MODE_R(flags)); + if (sigbio == NULL) { + goto clean_exit; + } } else { sigbio = in; /* non-detached signature */ } diff --git a/ext/openssl/tests/gh12489.phpt b/ext/openssl/tests/gh12489.phpt new file mode 100644 index 0000000000000..4ebeb09784db9 --- /dev/null +++ b/ext/openssl/tests/gh12489.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-12489: Missing sigbio creation checking in openssl_cms_verify +--EXTENSIONS-- +openssl +--FILE-- + "test@test", "Subject" => "testing openssl_cms_sign()"); +$headers = array("test@test", "testing openssl_cms_sign()"); + +var_dump(openssl_cms_sign($infile, $outfile, openssl_x509_read($single_cert), $privkey, $headers, + OPENSSL_CMS_DETACHED|OPENSSL_CMS_BINARY,OPENSSL_ENCODING_PEM)); +ini_set('open_basedir', __DIR__); +var_dump(openssl_cms_verify($infile,OPENSSL_CMS_NOVERIFY|OPENSSL_CMS_DETACHED|OPENSSL_CMS_BINARY, + NULL, array(), NULL, $vout, NULL, "../test.cms", OPENSSL_ENCODING_PEM)); +var_dump(openssl_error_string()); +?> +--CLEAN-- + +--EXPECTF-- +bool(true) + +Warning: openssl_cms_verify(): open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s) in %s on line %d +bool(false) +bool(false) diff --git a/ext/pcre/pcre2lib/pcre2_match.c b/ext/pcre/pcre2lib/pcre2_match.c index f28cdbb47a515..e62659a556145 100644 --- a/ext/pcre/pcre2lib/pcre2_match.c +++ b/ext/pcre/pcre2lib/pcre2_match.c @@ -5640,7 +5640,7 @@ fprintf(stderr, "++ op=%d\n", *Fecode); { P = (heapframe *)((char *)N - frame_size); memcpy((char *)F + offsetof(heapframe, ovector), P->ovector, - P->offset_top * sizeof(PCRE2_SIZE)); + Foffset_top * sizeof(PCRE2_SIZE)); Foffset_top = P->offset_top; Fcapture_last = P->capture_last; Fcurrent_recurse = P->current_recurse; diff --git a/ext/pcre/tests/gh11374.phpt b/ext/pcre/tests/gh11374.phpt new file mode 100644 index 0000000000000..07f8f4bccfd0a --- /dev/null +++ b/ext/pcre/tests/gh11374.phpt @@ -0,0 +1,60 @@ +--TEST-- +GH-11374 (PCRE regular expression without JIT enabled gives different result) +--FILE-- + + (?: + (?:\{ (?&types) \}) + | (a) + ) + (\*?) + ) +'; + +ini_set('pcre.jit', '0'); +$res = preg_match('{^' . $regex . '$}x', '{a}', $matches, PREG_OFFSET_CAPTURE); +ini_set('pcre.jit', '1'); +// regex must be different to prevent regex cache, so just add 2nd "x" modifier +$res2 = preg_match('{^' . $regex . '$}xx', '{a}', $matches2, PREG_OFFSET_CAPTURE); + +var_dump($matches === $matches2); +print_r($matches); + +?> +--EXPECT-- +bool(true) +Array +( + [0] => Array + ( + [0] => {a} + [1] => 0 + ) + + [types] => Array + ( + [0] => {a} + [1] => 0 + ) + + [1] => Array + ( + [0] => {a} + [1] => 0 + ) + + [2] => Array + ( + [0] => + [1] => -1 + ) + + [3] => Array + ( + [0] => + [1] => 3 + ) + +) diff --git a/ext/pdo_mysql/tests/pdo_mysql___construct.phpt b/ext/pdo_mysql/tests/pdo_mysql___construct.phpt index 1f009185267c5..aa52764dea2c7 100644 --- a/ext/pdo_mysql/tests/pdo_mysql___construct.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql___construct.phpt @@ -6,7 +6,7 @@ pdo_mysql --FILE-- getTrace()); + }))->start(); +} + +$f = new Fiber(function() { f(); max(...[1,2,3,4,5,6,7,8,9,10,11,12]); g(); }); +$f->start(); +$f->resume(); + +?> +--EXPECTF-- +array(3) { + [0]=> + array(7) { + ["file"]=> + string(%d) "%sReflectionFiber_bug_gh11121_1.php" + ["line"]=> + int(10) + ["function"]=> + string(5) "start" + ["class"]=> + string(5) "Fiber" + ["object"]=> + object(Fiber)#3 (0) { + } + ["type"]=> + string(2) "->" + ["args"]=> + array(0) { + } + } + [1]=> + array(4) { + ["file"]=> + string(%d) "%sReflectionFiber_bug_gh11121_1.php" + ["line"]=> + int(13) + ["function"]=> + string(1) "g" + ["args"]=> + array(0) { + } + } + [2]=> + array(2) { + ["function"]=> + string(9) "{closure}" + ["args"]=> + array(0) { + } + } +} diff --git a/ext/reflection/tests/ReflectionFiber_bug_gh11121_2.phpt b/ext/reflection/tests/ReflectionFiber_bug_gh11121_2.phpt new file mode 100644 index 0000000000000..b7affb2ca0b1c --- /dev/null +++ b/ext/reflection/tests/ReflectionFiber_bug_gh11121_2.phpt @@ -0,0 +1,63 @@ +--TEST-- +GH-11121: Segfault when using ReflectionFiber +--FILE-- +getTrace()); + }))->start(); +} + +$f = new Fiber(function() { f(); g(); }); +$f->start(); +$f->resume(); + +?> +--EXPECTF-- +array(3) { + [0]=> + array(7) { + ["file"]=> + string(%d) "%sReflectionFiber_bug_gh11121_2.php" + ["line"]=> + int(11) + ["function"]=> + string(5) "start" + ["class"]=> + string(5) "Fiber" + ["object"]=> + object(Fiber)#3 (0) { + } + ["type"]=> + string(2) "->" + ["args"]=> + array(0) { + } + } + [1]=> + array(4) { + ["file"]=> + string(%d) "%sReflectionFiber_bug_gh11121_2.php" + ["line"]=> + int(14) + ["function"]=> + string(1) "g" + ["args"]=> + array(0) { + } + } + [2]=> + array(2) { + ["function"]=> + string(9) "{closure}" + ["args"]=> + array(0) { + } + } +} diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 3a4626aa5beee..a5fbd3df9dd9c 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -1561,10 +1561,12 @@ static zval *to_zval_object_ex(zval *ret, encodeTypePtr type, xmlNodePtr data, z if (Z_TYPE_P(prop) != IS_ARRAY) { /* Convert into array */ array_init(&arr); - Z_ADDREF_P(prop); + Z_TRY_ADDREF_P(prop); add_next_index_zval(&arr, prop); set_zval_property(ret, (char*)trav->name, &arr); prop = &arr; + } else { + SEPARATE_ARRAY(prop); } /* Add array element */ add_next_index_zval(prop, &tmpVal); diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c index e93679a55ea39..521c5d0f1d9ed 100644 --- a/ext/soap/php_schema.c +++ b/ext/soap/php_schema.c @@ -2261,17 +2261,23 @@ static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type) schema_content_model_fixup(ctx, type->model); } if (type->attributes) { - zend_string *str_key; - zend_ulong index; + HashPosition pos; + zend_hash_internal_pointer_reset_ex(type->attributes, &pos); - ZEND_HASH_FOREACH_KEY_PTR(type->attributes, index, str_key, attr) { - if (str_key) { + while ((attr = zend_hash_get_current_data_ptr_ex(type->attributes, &pos)) != NULL) { + zend_string *str_key; + zend_ulong index; + + if (zend_hash_get_current_key_ex(type->attributes, &str_key, &index, &pos) == HASH_KEY_IS_STRING) { schema_attribute_fixup(ctx, attr); + zend_result result = zend_hash_move_forward_ex(type->attributes, &pos); + ZEND_ASSERT(result == SUCCESS); } else { schema_attributegroup_fixup(ctx, attr, type->attributes); - zend_hash_index_del(type->attributes, index); + zend_result result = zend_hash_index_del(type->attributes, index); + ZEND_ASSERT(result == SUCCESS); } - } ZEND_HASH_FOREACH_END(); + } } } diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c index 3dd8e6c5d76e4..651eab23b7aad 100644 --- a/ext/soap/php_sdl.c +++ b/ext/soap/php_sdl.c @@ -22,6 +22,7 @@ #include "ext/standard/md5.h" #include "zend_virtual_cwd.h" +#include "main/php_open_temporary_file.h" #include #include @@ -154,7 +155,7 @@ encodePtr get_encoder(sdlPtr sdl, const char *ns, const char *type) } if (sdl->encoders == NULL) { sdl->encoders = pemalloc(sizeof(HashTable), sdl->is_persistent); - zend_hash_init(sdl->encoders, 0, NULL, delete_encoder, sdl->is_persistent); + zend_hash_init(sdl->encoders, 0, NULL, sdl->is_persistent ? delete_encoder_persistent : delete_encoder, sdl->is_persistent); } zend_hash_str_update_ptr(sdl->encoders, nscat, len, new_enc); enc = new_enc; @@ -1536,7 +1537,7 @@ static HashTable* sdl_deserialize_parameters(encodePtr *encoders, sdlTypePtr *ty return ht; } -static sdlPtr get_sdl_from_cache(const char *fn, const char *uri, time_t t, time_t *cached) +static sdlPtr get_sdl_from_cache(const char *fn, const char *uri, size_t uri_len, time_t t, time_t *cached) { sdlPtr sdl; time_t old_t; @@ -1583,7 +1584,7 @@ static sdlPtr get_sdl_from_cache(const char *fn, const char *uri, time_t t, time *cached = old_t; WSDL_CACHE_GET_INT(i, &in); - if (i == 0 && strncmp(in, uri, i) != 0) { + if (i != uri_len || strncmp(in, uri, i) != 0) { unlink(fn); efree(buf); return NULL; @@ -2119,7 +2120,10 @@ static void add_sdl_to_cache(const char *fn, const char *uri, time_t t, sdlPtr s HashTable tmp_bindings; HashTable tmp_functions; - f = open(fn,O_CREAT|O_WRONLY|O_EXCL|O_BINARY,S_IREAD|S_IWRITE); + /* To avoid race conditions, we first create a temporary file and then rename it atomically + * at the end of the function. (see bug #66150) */ + zend_string *temp_file_path; + f = php_open_temporary_fd_ex(SOAP_GLOBAL(cache_dir), "tmp.wsdl.", &temp_file_path, PHP_TMP_FILE_SILENT); if (f < 0) {return;} @@ -2371,13 +2375,21 @@ static void add_sdl_to_cache(const char *fn, const char *uri, time_t t, sdlPtr s } ZEND_HASH_FOREACH_END(); } - php_ignore_value(write(f, ZSTR_VAL(buf.s), ZSTR_LEN(buf.s))); + bool valid_file = write(f, ZSTR_VAL(buf.s), ZSTR_LEN(buf.s)) == ZSTR_LEN(buf.s); close(f); + + /* Make sure that incomplete files (e.g. due to disk space issues, see bug #66150) are not utilised. */ + if (valid_file) { + /* This is allowed to fail, this means that another process was raced to create the file. */ + (void) VCWD_RENAME(ZSTR_VAL(temp_file_path), fn); + } + smart_str_free(&buf); zend_hash_destroy(&tmp_functions); zend_hash_destroy(&tmp_bindings); zend_hash_destroy(&tmp_encoders); zend_hash_destroy(&tmp_types); + zend_string_release_ex(temp_file_path, false); } @@ -3232,7 +3244,7 @@ sdlPtr get_sdl(zval *this_ptr, char *uri, zend_long cache_wsdl) } memcpy(key+len,md5str,sizeof(md5str)); - if ((sdl = get_sdl_from_cache(key, uri, t-SOAP_GLOBAL(cache_ttl), &cached)) != NULL) { + if ((sdl = get_sdl_from_cache(key, uri, uri_len, t-SOAP_GLOBAL(cache_ttl), &cached)) != NULL) { t = cached; efree(key); goto cache_in_memory; @@ -3243,6 +3255,9 @@ sdlPtr get_sdl(zval *this_ptr, char *uri, zend_long cache_wsdl) tmp = Z_CLIENT_STREAM_CONTEXT_P(this_ptr); if (Z_TYPE_P(tmp) == IS_RESOURCE) { context = php_stream_context_from_zval(tmp, 0); + /* Share a reference with new_context down below. + * For new contexts, the reference is only in new_context so that doesn't need extra refcounting. */ + GC_ADDREF(context->res); } tmp = Z_CLIENT_USER_AGENT_P(this_ptr); @@ -3311,7 +3326,7 @@ sdlPtr get_sdl(zval *this_ptr, char *uri, zend_long cache_wsdl) } if (context) { - php_stream_context_to_zval(context, &new_context); + ZVAL_RES(&new_context, context->res); php_libxml_switch_context(&new_context, &orig_context); } diff --git a/ext/soap/tests/bug75306.phpt b/ext/soap/tests/bug75306.phpt new file mode 100644 index 0000000000000..7501fde59e6b8 --- /dev/null +++ b/ext/soap/tests/bug75306.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #75306 (Memleak in SoapClient) +--EXTENSIONS-- +soap +--FILE-- + WSDL_CACHE_NONE); +// Need a warm-up for globals +for ($i = 0; $i < 10; $i++) { + $client = new SoapClient("ext/soap/tests/test.wsdl", $options); +} +$usage = memory_get_usage(); +for ($i = 0; $i < 10; $i++) { + $client = new SoapClient("ext/soap/tests/test.wsdl", $options); +} +$usage_delta = memory_get_usage() - $usage; +var_dump($usage_delta); +?> +--EXPECT-- +int(0) diff --git a/ext/soap/tests/bugs/segfault_assertion_props.phpt b/ext/soap/tests/bugs/segfault_assertion_props.phpt new file mode 100644 index 0000000000000..9d496d72967b0 --- /dev/null +++ b/ext/soap/tests/bugs/segfault_assertion_props.phpt @@ -0,0 +1,51 @@ +--TEST-- +Segfault and assertion failure with refcounted props and arrays +--INI-- +soap.wsdl_cache_enabled=0 +--EXTENSIONS-- +soap +--FILE-- + + + + Hello + World + + +EOF; + } +} + +trait A { + public $a = [self::class . 'a']; + public $b = self::class . 'b'; +} + +class DummyClass { + use A; +} + +$client = new TestSoapClient(__DIR__."/../classmap.wsdl", ['classmap' => ['Struct' => 'DummyClass']]); +var_dump($client->dotest2("???")); +?> +--EXPECT-- +object(DummyClass)#2 (2) { + ["a"]=> + array(2) { + [0]=> + string(11) "DummyClassa" + [1]=> + string(5) "Hello" + } + ["b"]=> + array(2) { + [0]=> + string(11) "DummyClassb" + [1]=> + string(5) "World" + } +} diff --git a/ext/soap/tests/gh12392.phpt b/ext/soap/tests/gh12392.phpt new file mode 100644 index 0000000000000..8a234ba025be9 --- /dev/null +++ b/ext/soap/tests/gh12392.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-12392 (Segmentation fault on SoapClient::__getTypes) +--EXTENSIONS-- +soap +--FILE-- + WSDL_CACHE_NONE]); +echo 'Client created!' . "\n"; + +$types = $client->__getTypes(); +echo 'Got types!' . "\n"; + +var_dump($types); + +?> +--EXPECT-- +Client created! +Got types! +array(1) { + [0]=> + string(62) "struct dummy { + string foo; + string a; + string b; + string c; +}" +} diff --git a/ext/soap/tests/gh12392.wsdl b/ext/soap/tests/gh12392.wsdl new file mode 100644 index 0000000000000..5e9a7ea094fb8 --- /dev/null +++ b/ext/soap/tests/gh12392.wsdl @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/standard/image.c b/ext/standard/image.c index 85ecda2f3d70b..5200295f3af28 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -425,6 +425,20 @@ static int php_skip_variable(php_stream * stream) } /* }}} */ +static size_t php_read_stream_all_chunks(php_stream *stream, char *buffer, size_t length) +{ + size_t read_total = 0; + do { + ssize_t read_now = php_stream_read(stream, buffer, length - read_total); + read_total += read_now; + if (read_now < stream->chunk_size && read_total != length) { + return 0; + } + } while (read_total < length); + + return read_total; +} + /* {{{ php_read_APP */ static int php_read_APP(php_stream * stream, unsigned int marker, zval *info) { @@ -441,7 +455,7 @@ static int php_read_APP(php_stream * stream, unsigned int marker, zval *info) buffer = emalloc(length); - if (php_stream_read(stream, buffer, (size_t) length) != length) { + if (php_read_stream_all_chunks(stream, buffer, length) != length) { efree(buffer); return 0; } diff --git a/ext/standard/tests/file/bug52820.phpt b/ext/standard/tests/file/bug52820.phpt index 78cfeb61fa988..ff816178cef67 100644 --- a/ext/standard/tests/file/bug52820.phpt +++ b/ext/standard/tests/file/bug52820.phpt @@ -12,7 +12,7 @@ curl_setopt($handle, CURLOPT_VERBOSE, true); curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); if (!@curl_setopt($handle, CURLOPT_STDERR, fopen("php://memory", "w+"))) die("skip fopencookie not supported on this platform"); -if (getenv('CIRRUS_CI')) die('xfail Broken on Cirrus+ARM'); +if (getenv('CIRCLECI')) die('xfail Broken on CircleCI'); ?> --FILE-- handle = fopen(str_replace('fs://', __DIR__ . '/', $file), $mode); + return true; + } + function stream_read($count) { + return fread($this->handle, $count); + } + function stream_eof() { + return feof($this->handle); + } + function stream_seek($offset, $whence) { + return fseek($this->handle, $offset, $whence) === 0; + } + function stream_stat() { + return fstat($this->handle); + } + function url_stat($file) { + return stat(str_replace('fs://', '', $file)); + } + function stream_tell() { + return ftell($this->handle); + } + function stream_close() { + fclose($this->handle); + } +} + +stream_register_wrapper('fs', 'FSStreamWrapper'); + +var_dump(getimagesize('fs://bug75708.jpg', $info)); + +?> +--EXPECT-- +array(7) { + [0]=> + int(10) + [1]=> + int(10) + [2]=> + int(2) + [3]=> + string(22) "width="10" height="10"" + ["bits"]=> + int(8) + ["channels"]=> + int(3) + ["mime"]=> + string(10) "image/jpeg" +} + diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index 12be9ada01194..3484cbdc5d9aa 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -211,6 +211,9 @@ char *_xmlreader_get_valid_file_path(char *source, char *resolved_path, int reso int isFileUri = 0; uri = xmlCreateURI(); + if (uri == NULL) { + return NULL; + } escsource = xmlURIEscapeStr((xmlChar *)source, (xmlChar *)":"); xmlParseURIReference(uri, (const char *)escsource); xmlFree(escsource); diff --git a/ext/xmlwriter/php_xmlwriter.c b/ext/xmlwriter/php_xmlwriter.c index 24ce414034af5..2966747fc52da 100644 --- a/ext/xmlwriter/php_xmlwriter.c +++ b/ext/xmlwriter/php_xmlwriter.c @@ -110,6 +110,9 @@ static char *_xmlwriter_get_valid_file_path(char *source, char *resolved_path, i int isFileUri = 0; uri = xmlCreateURI(); + if (uri == NULL) { + return NULL; + } escsource = xmlURIEscapeStr((xmlChar *)source, (xmlChar *) ":"); xmlParseURIReference(uri, (char *)escsource); xmlFree(escsource); diff --git a/ext/xsl/php_xsl.c b/ext/xsl/php_xsl.c index 1e98a6ce88bbe..6330ea5906535 100644 --- a/ext/xsl/php_xsl.c +++ b/ext/xsl/php_xsl.c @@ -29,6 +29,7 @@ static zend_object_handlers xsl_object_handlers; static const zend_module_dep xsl_deps[] = { ZEND_MOD_REQUIRED("libxml") + ZEND_MOD_REQUIRED("dom") ZEND_MOD_END }; diff --git a/ext/xsl/tests/php_function_edge_cases.phpt b/ext/xsl/tests/php_function_edge_cases.phpt new file mode 100644 index 0000000000000..23a06b111bb50 --- /dev/null +++ b/ext/xsl/tests/php_function_edge_cases.phpt @@ -0,0 +1,45 @@ +--TEST-- +php:function() edge cases +--EXTENSIONS-- +xsl +--FILE-- +loadXML(' + + + + + '); + + $inputdom = new DomDocument(); + $inputdom->loadXML(' + '); + + $proc = new XsltProcessor(); + $proc->registerPhpFunctions(); + $xsl = $proc->importStylesheet($xsl); + try { + $proc->transformToDoc($inputdom); + } catch (Exception $e) { + echo $e->getMessage(), "\n"; + } +} + +try { + test(""); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +test("3"); + +?> +--EXPECTF-- +Function name must be passed as the first argument + +Warning: XSLTProcessor::transformToDoc(): Handler name must be a string in %s on line %d diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c index 0ddef864f050f..22d56c41faebc 100644 --- a/ext/xsl/xsltprocessor.c +++ b/ext/xsl/xsltprocessor.c @@ -141,12 +141,17 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t return; } + if (UNEXPECTED(nargs == 0)) { + zend_throw_error(NULL, "Function name must be passed as the first argument"); + return; + } + fci.param_count = nargs - 1; if (fci.param_count > 0) { args = safe_emalloc(fci.param_count, sizeof(zval), 0); } /* Reverse order to pop values off ctxt stack */ - for (i = nargs - 2; i >= 0; i--) { + for (i = fci.param_count - 1; i >= 0; i--) { obj = valuePop(ctxt); if (obj == NULL) { ZVAL_NULL(&args[i]); @@ -221,7 +226,7 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t fci.params = NULL; } - + /* Last element of the stack is the function name */ obj = valuePop(ctxt); if (obj == NULL || obj->stringval == NULL) { php_error_docref(NULL, E_WARNING, "Handler name must be a string"); diff --git a/main/php_version.h b/main/php_version.h index 35782be060ad3..f6efeacd7ea6f 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 25 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.1.25-dev" -#define PHP_VERSION_ID 80125 +#define PHP_RELEASE_VERSION 26 +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.1.26" +#define PHP_VERSION_ID 80126 diff --git a/sapi/fpm/fpm/fpm_php.c b/sapi/fpm/fpm/fpm_php.c index 92b189668206e..dae98bdfc08f9 100644 --- a/sapi/fpm/fpm/fpm_php.c +++ b/sapi/fpm/fpm/fpm_php.c @@ -77,6 +77,11 @@ static void fpm_php_disable(char *value, int (*zend_disable)(const char *, size_ } /* }}} */ +#define FPM_PHP_INI_ALTERING_ERROR -1 +#define FPM_PHP_INI_APPLIED 1 +#define FPM_PHP_INI_EXTENSION_FAILED 0 +#define FPM_PHP_INI_EXTENSION_LOADED 2 + int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */ { @@ -87,46 +92,60 @@ int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */ if (!strcmp(name, "extension") && *value) { zval zv; + zend_interned_strings_switch_storage(0); php_dl(value, MODULE_PERSISTENT, &zv, 1); - return Z_TYPE(zv) == IS_TRUE; + zend_interned_strings_switch_storage(1); + return Z_TYPE(zv) == IS_TRUE ? FPM_PHP_INI_EXTENSION_LOADED : FPM_PHP_INI_EXTENSION_FAILED; } if (fpm_php_zend_ini_alter_master(name, name_len, value, value_len, mode, PHP_INI_STAGE_ACTIVATE) == FAILURE) { - return -1; + return FPM_PHP_INI_ALTERING_ERROR; } if (!strcmp(name, "disable_functions") && *value) { zend_disable_functions(value); - return 1; + return FPM_PHP_INI_APPLIED; } if (!strcmp(name, "disable_classes") && *value) { char *v = strdup(value); PG(disable_classes) = v; fpm_php_disable(v, zend_disable_class); - return 1; + return FPM_PHP_INI_APPLIED; } - return 1; + return FPM_PHP_INI_APPLIED; } /* }}} */ static int fpm_php_apply_defines(struct fpm_worker_pool_s *wp) /* {{{ */ { struct key_value_s *kv; + int apply_result; + bool extension_loaded = false; for (kv = wp->config->php_values; kv; kv = kv->next) { - if (fpm_php_apply_defines_ex(kv, ZEND_INI_USER) == -1) { + apply_result = fpm_php_apply_defines_ex(kv, ZEND_INI_USER); + if (apply_result == FPM_PHP_INI_ALTERING_ERROR) { zlog(ZLOG_ERROR, "Unable to set php_value '%s'", kv->key); + } else if (apply_result == FPM_PHP_INI_EXTENSION_LOADED) { + extension_loaded = true; } } for (kv = wp->config->php_admin_values; kv; kv = kv->next) { - if (fpm_php_apply_defines_ex(kv, ZEND_INI_SYSTEM) == -1) { + apply_result = fpm_php_apply_defines_ex(kv, ZEND_INI_SYSTEM); + if (apply_result == FPM_PHP_INI_ALTERING_ERROR) { zlog(ZLOG_ERROR, "Unable to set php_admin_value '%s'", kv->key); + } else if (apply_result == FPM_PHP_INI_EXTENSION_LOADED) { + extension_loaded = true; } } + if (extension_loaded) { + zend_collect_module_handlers(); + } + return 0; } /* }}} */ diff --git a/sapi/fpm/tests/gh12232-php-value-extension.phpt b/sapi/fpm/tests/gh12232-php-value-extension.phpt new file mode 100644 index 0000000000000..207bd7a677974 --- /dev/null +++ b/sapi/fpm/tests/gh12232-php-value-extension.phpt @@ -0,0 +1,51 @@ +--TEST-- +FPM: gh12232 - loading shared ext in FPM config +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->request()->expectBody(['bool(true)', 'string(5) "hello"', 'string(4) "test"']); +$tester->request()->expectBody(['bool(true)', 'string(5) "hello"', 'string(4) "test"']); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$tester->request()->expectBody('bool(true)'); +$tester->expectLogPattern('/DL TEST MINIT/'); +$tester->expectLogPattern('/DL TEST RINIT/'); +$tester->expectLogPattern('/DL TEST RSHUTDOWN/'); +$tester->request()->expectBody('bool(true)'); +$tester->expectLogPattern('/DL TEST RINIT/'); +$tester->expectLogPattern('/DL TEST RSHUTDOWN/'); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + +