diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index c47ba2577b6c3..0000000000000 --- a/.cirrus.yml +++ /dev/null @@ -1,30 +0,0 @@ -env: - CIRRUS_CLONE_DEPTH: 1 - -freebsd_task: - name: FREEBSD_DEBUG_NTS - skip: "changesIncludeOnly('NEWS', 'EXTENSIONS', 'UPGRADING', 'UPGRADING.INTERNALS', '**.md', 'docs/*', 'docs-old/*', '**/README.*', 'CONTRIBUTING.md', 'CODING_STANDARDS.md')" - freebsd_instance: - image_family: freebsd-13-3 - env: - ARCH: amd64 - install_script: - #- sed -i -e 's/quarterly/latest/g' /etc/pkg/FreeBSD.conf - #- pkg upgrade -y - - kldload accf_http - - pkg install -y autoconf bison gmake re2c icu libiconv png freetype2 enchant2 bzip2 krb5 t1lib gmp tidyp libsodium libzip libxml2 libxslt openssl oniguruma pkgconf webp libavif - script: - - ./buildconf -f - - ./configure --prefix=/usr/local --enable-debug --enable-option-checking=fatal --enable-fpm --with-pdo-sqlite --without-pear --with-bz2 --with-avif --with-jpeg --with-webp --with-freetype --enable-gd --enable-exif --with-zip --with-zlib --enable-soap --enable-xmlreader --with-xsl --with-libxml --enable-shmop --enable-pcntl --enable-mbstring --with-curl --enable-sockets --with-openssl --with-iconv=/usr/local --enable-bcmath --enable-calendar --enable-ftp --with-kerberos --with-ffi --enable-zend-test --enable-dl-test=shared --enable-intl --with-mhash --with-sodium --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d - - gmake -j2 - - mkdir /etc/php.d - - gmake install - - 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 STACK_LIMIT_DEFAULTS_CHECK=1 - - sapi/cli/php run-tests.php -P -q -j2 -g FAIL,BORK,LEAK,XLEAK --no-progress --offline --show-diff --show-slow 1000 --set-timeout 120 -d zend_extension=opcache.so diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0c064cf935047..ba67073c6afb3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,7 +30,8 @@ /ext/json @bukka /ext/libxml @nielsdos /ext/mbstring @alexdowad @youkidearitai -/ext/mysqlnd @SakiTakamachi +/ext/mysqli @bukka @kamil-tekiela +/ext/mysqlnd @bukka @kamil-tekiela @SakiTakamachi /ext/odbc @NattyNarwhal /ext/opcache @dstogov /ext/openssl @bukka @@ -38,7 +39,7 @@ /ext/pdo @SakiTakamachi /ext/pdo_dblib @SakiTakamachi /ext/pdo_firebird @SakiTakamachi -/ext/pdo_mysql @SakiTakamachi +/ext/pdo_mysql @kamil-tekiela @SakiTakamachi /ext/pdo_odbc @NattyNarwhal @SakiTakamachi /ext/pdo_pgsql @devnexen @SakiTakamachi /ext/pdo_sqlite @SakiTakamachi diff --git a/.github/actions/freebsd/action.yml b/.github/actions/freebsd/action.yml new file mode 100644 index 0000000000000..22df39f03b428 --- /dev/null +++ b/.github/actions/freebsd/action.yml @@ -0,0 +1,104 @@ +name: FreeBSD +runs: + using: composite + steps: + - name: FreeBSD + uses: vmactions/freebsd-vm@v1 + with: + release: '13.3' + usesh: true + copyback: false + # Temporarily disable sqlite, as FreeBSD ships it with disabled double quotes. We'll need to fix our tests. + # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=269889 + prepare: | + cd $GITHUB_WORKSPACE + + kldload accf_http + pkg install -y \ + autoconf \ + bison \ + gmake \ + re2c \ + icu \ + libiconv \ + png \ + freetype2 \ + enchant2 \ + bzip2 \ + t1lib \ + gmp \ + tidyp \ + libsodium \ + libzip \ + libxml2 \ + libxslt \ + openssl \ + oniguruma \ + pkgconf \ + webp \ + libavif \ + `#sqlite3` \ + curl + + ./buildconf -f + ./configure \ + --prefix=/usr/local \ + --enable-debug \ + --enable-option-checking=fatal \ + --enable-fpm \ + `#--with-pdo-sqlite` \ + --without-sqlite3 \ + --without-pdo-sqlite \ + --without-pear \ + --with-bz2 \ + --with-avif \ + --with-jpeg \ + --with-webp \ + --with-freetype \ + --enable-gd \ + --enable-exif \ + --with-zip \ + --with-zlib \ + --enable-soap \ + --enable-xmlreader \ + --with-xsl \ + --with-libxml \ + --enable-shmop \ + --enable-pcntl \ + --enable-mbstring \ + --with-curl \ + --enable-sockets \ + --with-openssl \ + --with-iconv=/usr/local \ + --enable-bcmath \ + --enable-calendar \ + --enable-ftp \ + --with-ffi \ + --enable-zend-test \ + --enable-dl-test=shared \ + --enable-intl \ + --with-mhash \ + --with-sodium \ + --with-config-file-path=/etc \ + --with-config-file-scan-dir=/etc/php.d + gmake -j2 + mkdir /etc/php.d + gmake install > /dev/null + echo opcache.enable_cli=1 > /etc/php.d/opcache.ini + echo opcache.protect_memory=1 >> /etc/php.d/opcache.ini + echo opcache.preload_user=root >> /etc/php.d/opcache.ini + run: | + cd $GITHUB_WORKSPACE + + export SKIP_IO_CAPTURE_TESTS=1 + export CI_NO_IPV6=1 + export STACK_LIMIT_DEFAULTS_CHECK=1 + sapi/cli/php run-tests.php \ + -P -q -j2 \ + -g FAIL,BORK,LEAK,XLEAK \ + --no-progress \ + --offline \ + --show-diff \ + --show-slow 1000 \ + --set-timeout 120 \ + -d zend_extension=opcache.so diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b87b7389ef02d..90e9a1d7b760c 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -814,12 +814,14 @@ jobs: with: withMysqli: ${{ inputs.libmysqlclient_with_mysqli }} - name: Build mysql-8.4 + if: ${{ !inputs.libmysqlclient_with_mysqli }} uses: ./.github/actions/build-libmysqlclient with: configurationParameters: ${{ !inputs.libmysqlclient_with_mysqli && '--enable-werror' || '' }} libmysql: mysql-8.4.0-linux-glibc2.28-x86_64.tar.xz withMysqli: ${{ inputs.libmysqlclient_with_mysqli }} - name: Test mysql-8.4 + if: ${{ !inputs.libmysqlclient_with_mysqli }} uses: ./.github/actions/test-libmysqlclient with: withMysqli: ${{ inputs.libmysqlclient_with_mysqli }} @@ -979,3 +981,13 @@ jobs: run: .github/scripts/windows/build.bat - name: Test run: .github/scripts/windows/test.bat + FREEBSD: + name: FREEBSD + runs-on: ubuntu-latest + steps: + - name: git checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch }} + - name: FreeBSD + uses: ./.github/actions/freebsd diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 871f02fd0ac03..4dad4feea846b 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -336,3 +336,11 @@ jobs: ${{ github.sha }} \ $(git merge-base ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) \ > $GITHUB_STEP_SUMMARY + FREEBSD: + name: FREEBSD + runs-on: ubuntu-latest + steps: + - name: git checkout + uses: actions/checkout@v4 + - name: FreeBSD + uses: ./.github/actions/freebsd diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml index f526e9bea30d5..cefabd0394a46 100644 --- a/.github/workflows/root.yml +++ b/.github/workflows/root.yml @@ -46,12 +46,17 @@ jobs: matrix: branch: ${{ fromJson(needs.GENERATE_MATRIX.outputs.branches) }} with: - asan_ubuntu_version: '20.04' + asan_ubuntu_version: ${{ + (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '24.04') + || '20.04' }} branch: ${{ matrix.branch.ref }} community_verify_type_inference: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9 }} libmysqlclient_with_mysqli: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] == 1) }} run_alpine: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9 }} run_macos_arm64: ${{ (matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9 }} - ubuntu_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 3) || matrix.branch.version[0] >= 9) && '22.04' || '20.04' }} + ubuntu_version: ${{ + (((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 5) || matrix.branch.version[0] >= 9) && '24.04') + || ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 3) && '22.04') + || '20.04' }} windows_version: ${{ ((matrix.branch.version[0] == 8 && matrix.branch.version[1] >= 4) || matrix.branch.version[0] >= 9) && '2022' || '2019' }} secrets: inherit diff --git a/NEWS b/NEWS index c28e6e8b56464..209a30ce9e009 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,95 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -21 Nov 2024, PHP 8.3.14 +19 Dec 2024, PHP 8.3.15 + +- Calendar: + . Fixed jdtogregorian overflow. (David Carlier) + . Fixed cal_to_jd julian_days argument overflow. (David Carlier) + +- COM: + . Fixed bug GH-16991 (Getting typeinfo of non DISPATCH variant segfaults). + (cmb) + +- Core: + . Fail early in *nix configuration build script. (hakre) + . Fixed bug GH-16727 (Opcache bad signal 139 crash in ZTS bookworm + (frankenphp)). (nielsdos) + . Fixed bug GH-16799 (Assertion failure at Zend/zend_vm_execute.h:7469). + (nielsdos) + . Fixed bug GH-16630 (UAF in lexer with encoding translation and heredocs). + (nielsdos) + . Fix is_zend_ptr() huge block comparison. (nielsdos) + . Fixed potential OOB read in zend_dirname() on Windows. (cmb) + +- Curl: + . Fixed bug GH-16802 (open_basedir bypass using curl extension). (nielsdos) + . Fix various memory leaks in curl mime handling. (nielsdos) + +- DOM: + . Fixed bug GH-16777 (Calling the constructor again on a DOM object after it + is in a document causes UAF). (nielsdos) + . Fixed bug GH-16906 (Reloading document can cause UAF in iterator). + (nielsdos) + +- FPM: + . Fixed GH-16432 (PHP-FPM 8.2 SIGSEGV in fpm_get_status). (Jakub Zelenka) + +- GD: + . Fixed GH-16776 (imagecreatefromstring overflow). (David Carlier) + +- GMP: + . Fixed bug GH-16890 (array_sum() with GMP can loose precision (LLP64)). + (cmb) + +- Hash: + . Fixed GH-16711: Segfault in mhash(). (Girgias) + +- Opcache: + . Fixed bug GH-16770 (Tracing JIT type mismatch when returning UNDEF). + (nielsdos, Dmitry) + . Fixed bug GH-16851 (JIT_G(enabled) not set correctly on other threads). + (dktapps) + . Fixed bug GH-16902 (Set of opcache tests fail zts+aarch64). (nielsdos) + +- OpenSSL: + . Prevent unexpected array entry conversion when reading key. (nielsdos) + . Fix various memory leaks related to openssl exports. (nielsdos) + . Fix memory leak in php_openssl_pkey_from_zval(). (nielsdos) + +- PDO: + . Fixed memory leak of `setFetchMode()`. (SakiTakamachi) + +- Phar: + . Fixed bug GH-16695 (phar:// tar parser and zero-length file header blocks). + (nielsdos, Hans Krentel) + +- PHPDBG: + . Fixed bug GH-15208 (Segfault with breakpoint map and phpdbg_clear()). + (nielsdos) + +- SAPI: + . Fixed bug GH-16998 (UBSAN warning in rfc1867). (nielsdos) + +- SimpleXML: + . Fixed bug GH-16808 (Segmentation fault in RecursiveIteratorIterator + ->current() with a xml element input). (nielsdos) + +- SOAP: + . Fix make check being invoked in ext/soap. (Ma27) + +- Standard: + . Fixed bug GH-16905 (Internal iterator functions can't handle UNDEF + properties). (nielsdos) + . Fixed bug GH-16957 (Assertion failure in array_shift with + self-referencing array). (nielsdos) + +- Streams: + . Fixed network connect poll interuption handling. (Jakub Zelenka) + +- Windows: + . Fixed bug GH-16849 (Error dialog causes process to hang). (cmb) + +07 Nov 2024, PHP 8.3.14RC1 - CLI: . Fixed bug GH-16373 (Shebang is not skipped for router script in cli-server @@ -22,6 +111,8 @@ PHP NEWS . Fixed bug GH-16508 (Incorrect line number in inheritance errors of delayed early bound classes). (ilutov) . Fixed bug GH-16648 (Use-after-free during array sorting). (ilutov) + . Fixed bug GH-15915 (overflow with a high value for precision INI). + (David Carlier / cmb) - Curl: . Fixed bug GH-16302 (CurlMultiHandle holds a reference to CurlHandle if diff --git a/Zend/tests/gh16630.phpt b/Zend/tests/gh16630.phpt new file mode 100644 index 0000000000000..62d6c9956a7eb --- /dev/null +++ b/Zend/tests/gh16630.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-16630 (UAF in lexer with encoding translation and heredocs) +--EXTENSIONS-- +mbstring +--INI-- +zend.multibyte=On +zend.script_encoding=ISO-8859-1 +internal_encoding=EUC-JP +--FILE-- + +--EXPECT-- +heredoc +text diff --git a/Zend/tests/gh16799.phpt b/Zend/tests/gh16799.phpt new file mode 100644 index 0000000000000..9348c38fc3031 --- /dev/null +++ b/Zend/tests/gh16799.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-16799 (Assertion failure at Zend/zend_vm_execute.h) +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception: Use of "static" in callables is deprecated in %s:%d +Stack trace: +#0 %s(%d): {closure}(%d, 'Use of "static"...', %s, %d) +#1 %s(%d): Test::test() +#2 {main} + thrown in %s on line %d diff --git a/Zend/zend.h b/Zend/zend.h index 0427ab48ffa00..346183374ca7d 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.14" +#define ZEND_VERSION "4.3.15" #define ZEND_ENGINE_3 diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 541613ea32709..5bff8fe7405ed 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2486,8 +2486,8 @@ ZEND_API bool is_zend_ptr(const void *ptr) zend_mm_huge_list *block = AG(mm_heap)->huge_list; while (block) { - if (ptr >= (void*)block - && ptr < (void*)((char*)block + block->size)) { + if (ptr >= block->ptr + && ptr < (void*)((char*)block->ptr + block->size)) { return 1; } block = block->next; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f90a4d14abb09..307fd76688dd1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2122,7 +2122,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) } /* Strip trailing slashes */ - while (end >= path && IS_SLASH_P(end)) { + while (end >= path && IS_SLASH_P_EX(end, end == path)) { end--; } if (end < path) { @@ -2133,7 +2133,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) } /* Strip filename */ - while (end >= path && !IS_SLASH_P(end)) { + while (end >= path && !IS_SLASH_P_EX(end, end == path)) { end--; } if (end < path) { @@ -2144,7 +2144,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) } /* Strip slashes which came before the file name */ - while (end >= path && IS_SLASH_P(end)) { + while (end >= path && IS_SLASH_P_EX(end, end == path)) { end--; } if (end < path) { @@ -8220,7 +8220,13 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) } opline->op1_type = IS_CONST; - LITERAL_STR(opline->op1, lcname); + /* It's possible that `lcname` is not an interned string because it was not yet in the interned string table. + * However, by this point another thread may have caused `lcname` to be added in the interned string table. + * This will cause `lcname` to get freed once it is found in the interned string table. If we were to use + * LITERAL_STR() here we would not change the `lcname` pointer to the new value, and it would point to the + * now-freed string. This will cause issues when we use `lcname` in the code below. We solve this by using + * zend_add_literal_string() which gives us the new value. */ + opline->op1.constant = zend_add_literal_string(&lcname); if (decl->flags & ZEND_ACC_ANON_CLASS) { opline->opcode = ZEND_DECLARE_ANON_CLASS; diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 4b5bb67adbd04..9b8cc1eb7c8d4 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -276,7 +276,7 @@ ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state) CG(zend_lineno) = lex_state->lineno; zend_restore_compiled_filename(lex_state->filename); - if (SCNG(script_filtered)) { + if (SCNG(script_filtered) && SCNG(script_filtered) != lex_state->script_filtered) { efree(SCNG(script_filtered)); SCNG(script_filtered) = NULL; } diff --git a/Zend/zend_strtod.c b/Zend/zend_strtod.c index bfd84ed6e02d3..e53783deca124 100644 --- a/Zend/zend_strtod.c +++ b/Zend/zend_strtod.c @@ -3616,13 +3616,20 @@ rv_alloc(i) int i; rv_alloc(int i) #endif { - int k, *r; - size_t j = sizeof(ULong); + int j, k, *r; + size_t rem; + + rem = sizeof(Bigint) - sizeof(ULong) - sizeof(int); + + + j = sizeof(ULong); + if (i > ((INT_MAX >> 2) + rem)) + i = (INT_MAX >> 2) + rem; for(k = 0; - sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (size_t)i; - j <<= 1) + rem + j <= (size_t)i; j <<= 1) k++; + r = (int*)Balloc(k); *r = k; return diff --git a/Zend/zend_virtual_cwd.h b/Zend/zend_virtual_cwd.h index 48a7fea09f43b..c438ae62dd405 100644 --- a/Zend/zend_virtual_cwd.h +++ b/Zend/zend_virtual_cwd.h @@ -79,8 +79,11 @@ typedef unsigned short mode_t; #define DEFAULT_SLASH '\\' #define DEFAULT_DIR_SEPARATOR ';' #define IS_SLASH(c) ((c) == '/' || (c) == '\\') +// IS_SLASH_P() may read the previous char on Windows, which may be OOB; use IS_SLASH_P_EX() instead #define IS_SLASH_P(c) (*(c) == '/' || \ (*(c) == '\\' && !IsDBCSLeadByte(*(c-1)))) +#define IS_SLASH_P_EX(c, first_byte) (*(c) == '/' || \ + (*(c) == '\\' && ((first_byte) || !IsDBCSLeadByte(*(c-1))))) /* COPY_WHEN_ABSOLUTE is 2 under Win32 because by chance both regular absolute paths in the file system and UNC paths need copying of two characters */ @@ -114,7 +117,9 @@ typedef unsigned short mode_t; #endif #define IS_SLASH(c) ((c) == '/') +// IS_SLASH_P() may read the previous char on Windows, which may be OOB; use IS_SLASH_P_EX() instead #define IS_SLASH_P(c) (*(c) == '/') +#define IS_SLASH_P_EX(c, first_byte) IS_SLASH_P(c) #endif diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 2c604d9724971..53aa7a821f697 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3850,6 +3850,16 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV, NUM) function_name = GET_OP2_ZVAL_PTR(BP_VAR_R); if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { ZEND_ASSERT(!error); + + /* Deprecation can be emitted from zend_is_callable_ex(), which can + * invoke a user error handler and throw an exception. + * For the CONST and CV case we reuse the same exception block below + * to make sure we don't increase VM size too much. */ + if (!(OP2_TYPE & (IS_TMP_VAR|IS_VAR)) && UNEXPECTED(EG(exception))) { + FREE_OP2(); + HANDLE_EXCEPTION(); + } + func = fcc.function_handler; object_or_called_scope = fcc.called_scope; if (func->common.fn_flags & ZEND_ACC_CLOSURE) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 0f221688fdcd6..2c86a94134c08 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7076,6 +7076,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS function_name = RT_CONSTANT(opline, opline->op2); if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { ZEND_ASSERT(!error); + + /* Deprecation can be emitted from zend_is_callable_ex(), which can + * invoke a user error handler and throw an exception. + * For the CONST and CV case we reuse the same exception block below + * to make sure we don't increase VM size too much. */ + if (!(IS_CONST & (IS_TMP_VAR|IS_VAR)) && UNEXPECTED(EG(exception))) { + + HANDLE_EXCEPTION(); + } + func = fcc.function_handler; object_or_called_scope = fcc.called_scope; if (func->common.fn_flags & ZEND_ACC_CLOSURE) { @@ -9596,6 +9606,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV function_name = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { ZEND_ASSERT(!error); + + /* Deprecation can be emitted from zend_is_callable_ex(), which can + * invoke a user error handler and throw an exception. + * For the CONST and CV case we reuse the same exception block below + * to make sure we don't increase VM size too much. */ + if (!((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) && UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + HANDLE_EXCEPTION(); + } + func = fcc.function_handler; object_or_called_scope = fcc.called_scope; if (func->common.fn_flags & ZEND_ACC_CLOSURE) { @@ -11983,6 +12003,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H function_name = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { ZEND_ASSERT(!error); + + /* Deprecation can be emitted from zend_is_callable_ex(), which can + * invoke a user error handler and throw an exception. + * For the CONST and CV case we reuse the same exception block below + * to make sure we don't increase VM size too much. */ + if (!(IS_CV & (IS_TMP_VAR|IS_VAR)) && UNEXPECTED(EG(exception))) { + + HANDLE_EXCEPTION(); + } + func = fcc.function_handler; object_or_called_scope = fcc.called_scope; if (func->common.fn_flags & ZEND_ACC_CLOSURE) { diff --git a/build/genif.sh b/build/genif.sh index 697bef95912e4..f6d6fff953476 100755 --- a/build/genif.sh +++ b/build/genif.sh @@ -32,7 +32,7 @@ header_list= olddir=$(pwd) # Go to project root. -cd $(CDPATH= cd -- "$(dirname -- "$0")/../" && pwd -P) +cd "$(CDPATH='' cd -- "$(dirname -- "$0")/../" && pwd -P)" || exit module_ptrs="$(echo $extensions | $AWK -f ./build/order_by_dep.awk)" diff --git a/buildconf b/buildconf index 51ef423abcf2d..0586cbf0cd274 100755 --- a/buildconf +++ b/buildconf @@ -8,9 +8,9 @@ force=0 debug=0 # Go to project root. -cd $(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) +cd "$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd -P)" || exit -php_extra_version=$(grep '^AC_INIT(' configure.ac) +php_extra_version=$(grep '^AC_INIT(' configure.ac) || exit case "$php_extra_version" in *-dev*) dev=1 diff --git a/configure.ac b/configure.ac index 29a19fcf1e630..a93bdf9bf92cc 100644 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.3.14],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.15],[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/calendar/gregor.c b/ext/calendar/gregor.c index dab12e5187df5..3aef7ae6ac505 100644 --- a/ext/calendar/gregor.c +++ b/ext/calendar/gregor.c @@ -148,16 +148,25 @@ void SdnToGregorian( int dayOfYear; if (sdn <= 0 || - sdn > (LONG_MAX - 4 * GREGOR_SDN_OFFSET) / 4) { + sdn > (ZEND_LONG_MAX - 4 * GREGOR_SDN_OFFSET) / 4) { goto fail; } temp = (sdn + GREGOR_SDN_OFFSET) * 4 - 1; + if (temp < 0 || (temp / DAYS_PER_400_YEARS) > INT_MAX) { + goto fail; + } + /* Calculate the century (year/100). */ century = temp / DAYS_PER_400_YEARS; /* Calculate the year and day of year (1 <= dayOfYear <= 366). */ temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3; + + if (century > ((INT_MAX / 100) - (temp / DAYS_PER_4_YEARS))) { + goto fail; + } + year = (century * 100) + (temp / DAYS_PER_4_YEARS); dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1; diff --git a/ext/calendar/tests/gh16235.phpt b/ext/calendar/tests/gh16235.phpt new file mode 100644 index 0000000000000..6b8856209828c --- /dev/null +++ b/ext/calendar/tests/gh16235.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-16235 (jdtogregorian overflow on argument) +--EXTENSIONS-- +calendar +--FILE-- + +--EXPECT-- +DONE diff --git a/ext/calendar/tests/gh16834.phpt b/ext/calendar/tests/gh16834.phpt new file mode 100644 index 0000000000000..ea9b8d0079a81 --- /dev/null +++ b/ext/calendar/tests/gh16834.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-16834 (cal_from_jd from julian_day argument) +--EXTENSIONS-- +calendar +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +array(9) { + ["date"]=> + string(5) "0/0/0" + ["month"]=> + int(0) + ["day"]=> + int(0) + ["year"]=> + int(0) + ["dow"]=> + int(%d) + ["abbrevdayname"]=> + string(3) "%s" + ["dayname"]=> + string(9) "%s" + ["abbrevmonth"]=> + string(0) "" + ["monthname"]=> + string(0) "" +} diff --git a/ext/com_dotnet/com_typeinfo.c b/ext/com_dotnet/com_typeinfo.c index ccdcc3ff7e8c8..e120dc5446989 100644 --- a/ext/com_dotnet/com_typeinfo.c +++ b/ext/com_dotnet/com_typeinfo.c @@ -331,7 +331,7 @@ ITypeInfo *php_com_locate_typeinfo(zend_string *type_lib_name, php_com_dotnet_ob if (obj->typeinfo) { ITypeInfo_AddRef(obj->typeinfo); return obj->typeinfo; - } else { + } else if (V_VT(&obj->v) == VT_DISPATCH) { IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &typeinfo); if (typeinfo) { return typeinfo; diff --git a/ext/com_dotnet/tests/gh16991.phpt b/ext/com_dotnet/tests/gh16991.phpt new file mode 100644 index 0000000000000..3623f1f3c4a63 --- /dev/null +++ b/ext/com_dotnet/tests/gh16991.phpt @@ -0,0 +1,10 @@ +--TEST-- +GH-16991 (Getting typeinfo of non DISPATCH variant segfaults) +--EXTENSIONS-- +com_dotnet +--FILE-- + +--EXPECTF-- +Warning: com_print_typeinfo(): Unable to find typeinfo using the parameters supplied in %s on line %d diff --git a/ext/curl/interface.c b/ext/curl/interface.c index c5d747311cbc4..936a6f74b7240 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -1445,7 +1445,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo postval = Z_STR_P(prop); if (php_check_open_basedir(ZSTR_VAL(postval))) { - return FAILURE; + goto out_string; } prop = zend_read_property(curl_CURLFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv); @@ -1471,15 +1471,18 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo seekfunc = NULL; } + part = curl_mime_addpart(mime); + if (part == NULL) { + if (stream) { + php_stream_close(stream); + } + goto out_string; + } + cb_arg = emalloc(sizeof *cb_arg); cb_arg->filename = zend_string_copy(postval); cb_arg->stream = stream; - part = curl_mime_addpart(mime); - if (part == NULL) { - zend_string_release_ex(string_key, 0); - return FAILURE; - } if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK || (form_error = curl_mime_data_cb(part, filesize, read_cb, seekfunc, free_cb, cb_arg)) != CURLE_OK || (form_error = curl_mime_filename(part, filename ? filename : ZSTR_VAL(postval))) != CURLE_OK @@ -1513,8 +1516,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "postname", sizeof("postname")-1, 0, &rv); if (EG(exception)) { - zend_string_release_ex(string_key, 0); - return FAILURE; + goto out_string; } ZVAL_DEREF(prop); ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); @@ -1523,8 +1525,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "mime", sizeof("mime")-1, 0, &rv); if (EG(exception)) { - zend_string_release_ex(string_key, 0); - return FAILURE; + goto out_string; } ZVAL_DEREF(prop); ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); @@ -1533,8 +1534,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo prop = zend_read_property(curl_CURLStringFile_class, Z_OBJ_P(current), "data", sizeof("data")-1, 0, &rv); if (EG(exception)) { - zend_string_release_ex(string_key, 0); - return FAILURE; + goto out_string; } ZVAL_DEREF(prop); ZEND_ASSERT(Z_TYPE_P(prop) == IS_STRING); @@ -1547,8 +1547,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo part = curl_mime_addpart(mime); if (part == NULL) { - zend_string_release_ex(string_key, 0); - return FAILURE; + goto out_string; } if ((form_error = curl_mime_name(part, ZSTR_VAL(string_key))) != CURLE_OK || (form_error = curl_mime_data(part, ZSTR_VAL(postval), ZSTR_LEN(postval))) != CURLE_OK @@ -1604,7 +1603,7 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo SAVE_CURL_ERROR(ch, error); if (error != CURLE_OK) { - return FAILURE; + goto out_mime; } if ((*ch->clone) == 1) { @@ -1620,6 +1619,16 @@ static inline zend_result build_mime_structure_from_hash(php_curl *ch, zval *zpo SAVE_CURL_ERROR(ch, error); return error == CURLE_OK ? SUCCESS : FAILURE; + +out_string: + zend_string_release_ex(string_key, false); +out_mime: +#if LIBCURL_VERSION_NUM >= 0x073800 /* 7.56.0 */ + curl_mime_free(mime); +#else + curl_formfree(first); +#endif + return FAILURE; } /* }}} */ @@ -1978,7 +1987,10 @@ static zend_result _php_curl_setopt(php_curl *ch, zend_long option, zval *zvalue zend_string *str = zval_get_tmp_string(zvalue, &tmp_str); #if LIBCURL_VERSION_NUM >= 0x075500 /* Available since 7.85.0 */ if ((option == CURLOPT_PROTOCOLS_STR || option == CURLOPT_REDIR_PROTOCOLS_STR) && - (PG(open_basedir) && *PG(open_basedir)) && php_memnistr(ZSTR_VAL(str), "file", sizeof("file") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL) { + (PG(open_basedir) && *PG(open_basedir)) + && (php_memnistr(ZSTR_VAL(str), "file", sizeof("file") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL + || php_memnistr(ZSTR_VAL(str), "all", sizeof("all") - 1, ZSTR_VAL(str) + ZSTR_LEN(str)) != NULL)) { + zend_tmp_string_release(tmp_str); php_error_docref(NULL, E_WARNING, "The FILE protocol cannot be activated when an open_basedir is set"); return FAILURE; } diff --git a/ext/curl/tests/gh16802.phpt b/ext/curl/tests/gh16802.phpt new file mode 100644 index 0000000000000..0fb3d4dedb170 --- /dev/null +++ b/ext/curl/tests/gh16802.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-16802 (open_basedir bypass using curl extension) +--EXTENSIONS-- +curl +--SKIPIF-- + +--INI-- +open_basedir=/nowhere +--FILE-- + +--EXPECTF-- +Warning: curl_setopt(): The FILE protocol cannot be activated when an open_basedir is set in %s on line %d + +Warning: curl_setopt(): The FILE protocol cannot be activated when an open_basedir is set in %s on line %d + +Warning: curl_setopt(): The FILE protocol cannot be activated when an open_basedir is set in %s on line %d + +Warning: curl_setopt(): The FILE protocol cannot be activated when an open_basedir is set in %s on line %d +bool(false) diff --git a/ext/dba/tests/dba_flatfile.phpt b/ext/dba/tests/dba_flatfile.phpt index 9d989e9069b8d..1061e0a00e704 100644 --- a/ext/dba/tests/dba_flatfile.phpt +++ b/ext/dba/tests/dba_flatfile.phpt @@ -29,12 +29,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 key4: Another Content String key5: The last content string name9: Content String 9 -[key10]name10: Content String 10 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/dba_gdbm.phpt b/ext/dba/tests/dba_gdbm.phpt index 480e6063b5212..7e3d43a4f1709 100644 --- a/ext/dba/tests/dba_gdbm.phpt +++ b/ext/dba/tests/dba_gdbm.phpt @@ -35,12 +35,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) -key4: Another Content String +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 +key4: Another Content String key5: The last content string -[key10]name10: Content String 10 name9: Content String 9 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y @@ -81,12 +81,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) -key4: Another Content String +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 +key4: Another Content String key5: The last content string -[key10]name10: Content String 10 name9: Content String 9 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/dba_inifile.phpt b/ext/dba/tests/dba_inifile.phpt index 06be22c085ff6..c2d638a747581 100644 --- a/ext/dba/tests/dba_inifile.phpt +++ b/ext/dba/tests/dba_inifile.phpt @@ -30,14 +30,14 @@ bool(true) bool(true) Try to remove key 1 again bool(false) -key2: Content String 2 -key4: Another Content String -key5: The last content string -name9: Content String 9 [key10]: [key10]name10: Content String 10 [key30]: [key30]name30: Content String 30 +key2: Content String 2 +key4: Another Content String +key5: The last content string +name9: Content String 9 Total keys: 8 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/dba_ndbm.phpt b/ext/dba/tests/dba_ndbm.phpt index dcf368ff1a36f..730932966cfef 100644 --- a/ext/dba/tests/dba_ndbm.phpt +++ b/ext/dba/tests/dba_ndbm.phpt @@ -36,12 +36,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) -key4: Another Content String +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 +key4: Another Content String key5: The last content string -[key10]name10: Content String 10 name9: Content String 9 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y @@ -82,12 +82,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) -key4: Another Content String +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 +key4: Another Content String key5: The last content string -[key10]name10: Content String 10 name9: Content String 9 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/dba_qdbm.phpt b/ext/dba/tests/dba_qdbm.phpt index e4321e7dc3750..fad229c368ee4 100644 --- a/ext/dba/tests/dba_qdbm.phpt +++ b/ext/dba/tests/dba_qdbm.phpt @@ -35,12 +35,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 key4: Another Content String key5: The last content string name9: Content String 9 -[key10]name10: Content String 10 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y @@ -81,12 +81,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 key4: Another Content String key5: The last content string name9: Content String 9 -[key10]name10: Content String 10 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/dba_tcadb.phpt b/ext/dba/tests/dba_tcadb.phpt index d0a0e34cb8e37..657f4287268bb 100644 --- a/ext/dba/tests/dba_tcadb.phpt +++ b/ext/dba/tests/dba_tcadb.phpt @@ -30,12 +30,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 key4: Another Content String key5: The last content string name9: Content String 9 -[key10]name10: Content String 10 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/dba/tests/setup/setup_dba_tests.inc b/ext/dba/tests/setup/setup_dba_tests.inc index 3e79ed3d54ce1..2ffac29e69782 100644 --- a/ext/dba/tests/setup/setup_dba_tests.inc +++ b/ext/dba/tests/setup/setup_dba_tests.inc @@ -102,14 +102,29 @@ function run_standard_tests_ex(string $handler, string $name, LockFlag $lock, bo echo 'Try to remove key 1 again', \PHP_EOL; var_dump(dba_delete("key1", $db_writer)); - // Fetch data + // Fetch and sort data. We sort to guarantee that the output is + // consistent across invocations and architectures. When iterating + // with firstkey() and nextkey(), several engines (GDBM, LMDB, + // QDBM) make no promise about the iteration order. Others (TCADB, + // DBM) explicitly state that the order is arbitrary. With GDBM at + // least, the order appears platform-dependent -- we have a report + // in Github issue 14786. GDBM's own test suite sorts this output, + // suggesting that sorting is a reasonable workaround for the issue. + $output = []; + $key = dba_firstkey($db_writer); $total_keys = 0; while ($key) { - echo $key, ': ', dba_fetch($key, $db_writer), \PHP_EOL; + $output[] = $key . ': ' . dba_fetch($key, $db_writer) . \PHP_EOL; $key = dba_nextkey($db_writer); $total_keys++; } + + sort($output, SORT_STRING); + foreach ($output as $line) { + echo $line; + } + echo 'Total keys: ', $total_keys, \PHP_EOL; for ($i = 1; $i < 6; $i++) { echo "Key $i exists? ", dba_exists("key$i", $db_writer) ? 'Y' : 'N', \PHP_EOL; diff --git a/ext/dom/node.c b/ext/dom/node.c index cc693cd8a36cf..07c6859d73017 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -1024,6 +1024,7 @@ PHP_METHOD(DOMNode, insertBefore) } if (child->doc == NULL && parentp->doc != NULL) { + xmlSetTreeDoc(child, parentp->doc); dom_set_document_ref_pointers(child, intern->document); } @@ -1188,6 +1189,7 @@ PHP_METHOD(DOMNode, replaceChild) } if (newchild->doc == NULL && nodep->doc != NULL) { + xmlSetTreeDoc(newchild, nodep->doc); dom_set_document_ref_pointers(newchild, intern->document); } @@ -1291,6 +1293,7 @@ PHP_METHOD(DOMNode, appendChild) } if (child->doc == NULL && nodep->doc != NULL) { + xmlSetTreeDoc(child, nodep->doc); dom_set_document_ref_pointers(child, intern->document); } diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 5c7aacefba5d0..7ec107dd712e3 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1018,6 +1018,10 @@ void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xml mapptr->baseobj = basenode; mapptr->nodetype = ntype; mapptr->ht = ht; + if (EXPECTED(doc != NULL)) { + mapptr->dict = doc->dict; + xmlDictReference(doc->dict); + } const xmlChar* tmp; @@ -1128,6 +1132,7 @@ void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */ if (!Z_ISUNDEF(objmap->baseobj_zv)) { zval_ptr_dtor(&objmap->baseobj_zv); } + xmlDictFree(objmap->dict); efree(objmap); intern->ptr = NULL; } @@ -1158,6 +1163,7 @@ zend_object *dom_nnodemap_objects_new(zend_class_entry *class_type) objmap->cached_length = -1; objmap->cached_obj = NULL; objmap->cached_obj_index = 0; + objmap->dict = NULL; return &intern->std; } diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index 19e6fab6329a8..2cdbe649a7834 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -90,6 +90,7 @@ typedef struct _dom_nnodemap_object { php_libxml_cache_tag cache_tag; dom_object *cached_obj; zend_long cached_obj_index; + xmlDictPtr dict; bool free_local : 1; bool free_ns : 1; } dom_nnodemap_object; diff --git a/ext/dom/tests/gh16777_1.phpt b/ext/dom/tests/gh16777_1.phpt new file mode 100644 index 0000000000000..d821842ace4e9 --- /dev/null +++ b/ext/dom/tests/gh16777_1.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-16777 (Calling the constructor again on a DOM object after it is in a document causes UAF) +--EXTENSIONS-- +dom +--FILE-- +appendChild($text); +$text->__construct('my new value'); +$doc->appendChild($text); +echo $doc->saveXML(); +$dom2 = new DOMDocument(); +try { + $dom2->appendChild($text); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- + +my value +my new value +Wrong Document Error diff --git a/ext/dom/tests/gh16777_2.phpt b/ext/dom/tests/gh16777_2.phpt new file mode 100644 index 0000000000000..a6f3a59621b45 --- /dev/null +++ b/ext/dom/tests/gh16777_2.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-16777 (Calling the constructor again on a DOM object after it is in a document causes UAF) +--EXTENSIONS-- +dom +--FILE-- +append($child = new DOMElement('child')); +$doc = new DOMDocument(); +$doc->appendChild($el); +$el->__construct('newname'); +$doc->appendChild($el); +echo $doc->saveXML(); +$dom2 = new DOMDocument(); +try { + $dom2->appendChild($el); +} catch (DOMException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($child->ownerDocument === $doc); +?> +--EXPECT-- + + + +Wrong Document Error +bool(true) diff --git a/ext/dom/tests/gh16906.phpt b/ext/dom/tests/gh16906.phpt new file mode 100644 index 0000000000000..791ca13b390e0 --- /dev/null +++ b/ext/dom/tests/gh16906.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-16906 (Reloading document can cause UAF in iterator) +--EXTENSIONS-- +dom +--FILE-- +loadXML(''); +$list = $doc->getElementsByTagName('strong'); +$doc->load(__DIR__."/book.xml"); +var_dump($list); +?> +--EXPECT-- +object(DOMNodeList)#2 (1) { + ["length"]=> + int(0) +} diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 7ac64444fa7f9..0ce2498f3a3a2 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -925,13 +925,13 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ if (flags & FILTER_FLAG_GLOBAL_RANGE) { if ( - (ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127 ) || - (ip[0] == 192 && ip[1] == 0 && ip[2] == 0 ) || - (ip[0] == 192 && ip[1] == 0 && ip[2] == 2 ) || - (ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19 ) || - (ip[0] == 198 && ip[1] == 51 && ip[2] == 100 ) || - (ip[0] == 203 && ip[1] == 0 && ip[2] == 113 ) - ) { + (ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127 ) || + (ip[0] == 192 && ip[1] == 0 && ip[2] == 0 ) || + (ip[0] == 192 && ip[1] == 0 && ip[2] == 2 ) || + (ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19 ) || + (ip[0] == 198 && ip[1] == 51 && ip[2] == 100 ) || + (ip[0] == 203 && ip[1] == 0 && ip[2] == 113 ) + ) { RETURN_VALIDATION_FAILED } } @@ -952,23 +952,24 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ } } if (flags & FILTER_FLAG_NO_RES_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { - if ((ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 - && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1)) - || (ip[0] == 0x5f) - || (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) - || (ip[0] == 0x2001 && (ip[1] == 0x0db8 || (ip[1] >= 0x0010 && ip[1] <= 0x001f))) - || (ip[0] == 0x3ff3) - ) { - RETURN_VALIDATION_FAILED - } + if ( + (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1)) || + (ip[0] == 0x5f) || + (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) || + (ip[0] == 0x2001 && (ip[1] == 0x0db8 || (ip[1] >= 0x0010 && ip[1] <= 0x001f))) || + (ip[0] == 0x3ff3) + ) { + RETURN_VALIDATION_FAILED + } } if (flags & FILTER_FLAG_GLOBAL_RANGE) { - if ((ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) || - (ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) || - (ip[0] == 0x2001 && ip[1] <= 0x01ff) || - (ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) || - (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) - ) { + if ( + (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) || + (ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) || + (ip[0] == 0x2001 && ip[1] <= 0x01ff) || + (ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) || + (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) + ) { RETURN_VALIDATION_FAILED } } diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 4e140ef5e39dc..13da099b33506 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -1322,7 +1322,7 @@ static int _php_ctx_getmbi(gdIOCtx *ctx) do { i = (ctx->getC)(ctx); - if (i < 0) { + if (i < 0 || mbi > (INT_MAX >> 7)) { return -1; } mbi = (mbi << 7) | (i & 0x7f); diff --git a/ext/gd/tests/gh16771.phpt b/ext/gd/tests/gh16771.phpt new file mode 100644 index 0000000000000..232317cec11b5 --- /dev/null +++ b/ext/gd/tests/gh16771.phpt @@ -0,0 +1,10 @@ +--TEST-- +GH-16771 (UBSan abort in ext/gd/libgd/gd.c:1372) +--EXTENSIONS-- +gd +--FILE-- +num; - if (mpz_fits_slong_p(gmpnum)) { + if (mpz_fits_si_p(gmpnum)) { ZVAL_LONG(writeobj, mpz_get_si(gmpnum)); } else { ZVAL_DOUBLE(writeobj, mpz_get_d(gmpnum)); @@ -1350,26 +1354,13 @@ ZEND_FUNCTION(gmp_pow) RETURN_THROWS(); } - double powmax = log((double)ZEND_LONG_MAX); - if (Z_TYPE_P(base_arg) == IS_LONG && Z_LVAL_P(base_arg) >= 0) { INIT_GMP_RETVAL(gmpnum_result); - if ((log(Z_LVAL_P(base_arg)) * exp) > powmax) { - zend_value_error("base and exponent overflow"); - RETURN_THROWS(); - } mpz_ui_pow_ui(gmpnum_result, Z_LVAL_P(base_arg), exp); } else { mpz_ptr gmpnum_base; - zend_ulong gmpnum; FETCH_GMP_ZVAL(gmpnum_base, base_arg, temp_base, 1); INIT_GMP_RETVAL(gmpnum_result); - gmpnum = mpz_get_ui(gmpnum_base); - if ((log(gmpnum) * exp) > powmax) { - FREE_GMP_TEMP(temp_base); - zend_value_error("base and exponent overflow"); - RETURN_THROWS(); - } mpz_pow_ui(gmpnum_result, gmpnum_base, exp); FREE_GMP_TEMP(temp_base); } diff --git a/ext/gmp/tests/gh16890.phpt b/ext/gmp/tests/gh16890.phpt new file mode 100644 index 0000000000000..08fc060559625 --- /dev/null +++ b/ext/gmp/tests/gh16890.phpt @@ -0,0 +1,11 @@ +--TEST-- +GH-16890 (array_sum() with GMP can loose precision (LLP64)) +--EXTENSIONS-- +gmp +--FILE-- + +--EXPECT-- +bool(true) diff --git a/ext/gmp/tests/gmp_pow.phpt b/ext/gmp/tests/gmp_pow.phpt index 1d77bd5e96c80..f42e44e31abed 100644 --- a/ext/gmp/tests/gmp_pow.phpt +++ b/ext/gmp/tests/gmp_pow.phpt @@ -2,8 +2,6 @@ gmp_pow() basic tests --EXTENSIONS-- gmp ---SKIPIF-- - --FILE-- ---FILE-- -getMessage() . "\n"; -} -var_dump(gmp_strval(gmp_pow("-2",10))); -try { - gmp_pow(20,10); -} catch (ValueError $exception) { - echo $exception->getMessage() . "\n"; -} -try { - gmp_pow(50,10); -} catch (ValueError $exception) { - echo $exception->getMessage() . "\n"; -} -try { - gmp_pow(50,-5); -} catch (ValueError $exception) { - echo $exception->getMessage() . "\n"; -} -try { - $n = gmp_init("20"); - gmp_pow($n,10); -} catch (ValueError $exception) { - echo $exception->getMessage() . "\n"; -} -try { - $n = gmp_init("-20"); - gmp_pow($n,10); -} catch (ValueError $exception) { - echo $exception->getMessage() . "\n"; -} -try { - var_dump(gmp_pow(2,array())); -} catch (TypeError $e) { - echo $e->getMessage(), "\n"; -} - -try { - var_dump(gmp_pow(array(),10)); -} catch (\TypeError $e) { - echo $e->getMessage() . \PHP_EOL; -} - -echo "Done\n"; -?> ---EXPECT-- -string(4) "1024" -string(4) "1024" -string(5) "-2048" -string(4) "1024" -string(1) "1" -gmp_pow(): Argument #2 ($exponent) must be greater than or equal to 0 -string(4) "1024" -base and exponent overflow -base and exponent overflow -gmp_pow(): Argument #2 ($exponent) must be greater than or equal to 0 -base and exponent overflow -base and exponent overflow -gmp_pow(): Argument #2 ($exponent) must be of type int, array given -gmp_pow(): Argument #1 ($num) must be of type GMP|string|int, array given -Done diff --git a/ext/gmp/tests/gmp_pow_fpe.phpt b/ext/gmp/tests/gmp_pow_fpe.phpt deleted file mode 100644 index 248922e80514d..0000000000000 --- a/ext/gmp/tests/gmp_pow_fpe.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -gmp_pow() floating point exception ---EXTENSIONS-- -gmp ---FILE-- -getMessage() . PHP_EOL; -} -try { - gmp_pow(256, PHP_INT_MAX); -} catch (\ValueError $e) { - echo $e->getMessage() . PHP_EOL; -} - -try { - gmp_pow(gmp_add(gmp_mul(gmp_init(PHP_INT_MAX), gmp_init(PHP_INT_MAX)), 3), 256); -} catch (\ValueError $e) { - echo $e->getMessage() . PHP_EOL; -} -try { - gmp_pow(gmp_init(PHP_INT_MAX), 256); -} catch (\ValueError $e) { - echo $e->getMessage(); -} -?> ---EXPECTF-- -base and exponent overflow -base and exponent overflow -base and exponent overflow -base and exponent overflow diff --git a/ext/hash/hash.c b/ext/hash/hash.c index e0024daf90d77..19d72ed7699b1 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -1213,7 +1213,11 @@ PHP_FUNCTION(mhash) struct mhash_bc_entry algorithm_lookup = mhash_to_hash[algorithm]; if (algorithm_lookup.hash_name) { algo = zend_string_init(algorithm_lookup.hash_name, strlen(algorithm_lookup.hash_name), 0); + } else { + RETURN_FALSE; } + } else { + RETURN_FALSE; } if (key) { diff --git a/ext/hash/tests/gh16711_1.phpt b/ext/hash/tests/gh16711_1.phpt new file mode 100644 index 0000000000000..be4257cbfc708 --- /dev/null +++ b/ext/hash/tests/gh16711_1.phpt @@ -0,0 +1,98 @@ +--TEST-- +GH-16711: Segmentation fault in mhash() +--SKIPIF-- + +--FILE-- +getConstants()); + +var_dump(mhash(133, 1086849124, 133)); +?> +--EXPECTF-- +array(40) { + ["HASH_HMAC"]=> + int(1) + ["MHASH_CRC32"]=> + int(0) + ["MHASH_MD5"]=> + int(1) + ["MHASH_SHA1"]=> + int(2) + ["MHASH_HAVAL256"]=> + int(3) + ["MHASH_RIPEMD160"]=> + int(5) + ["MHASH_TIGER"]=> + int(7) + ["MHASH_GOST"]=> + int(8) + ["MHASH_CRC32B"]=> + int(9) + ["MHASH_HAVAL224"]=> + int(10) + ["MHASH_HAVAL192"]=> + int(11) + ["MHASH_HAVAL160"]=> + int(12) + ["MHASH_HAVAL128"]=> + int(13) + ["MHASH_TIGER128"]=> + int(14) + ["MHASH_TIGER160"]=> + int(15) + ["MHASH_MD4"]=> + int(16) + ["MHASH_SHA256"]=> + int(17) + ["MHASH_ADLER32"]=> + int(18) + ["MHASH_SHA224"]=> + int(19) + ["MHASH_SHA512"]=> + int(20) + ["MHASH_SHA384"]=> + int(21) + ["MHASH_WHIRLPOOL"]=> + int(22) + ["MHASH_RIPEMD128"]=> + int(23) + ["MHASH_RIPEMD256"]=> + int(24) + ["MHASH_RIPEMD320"]=> + int(25) + ["MHASH_SNEFRU256"]=> + int(27) + ["MHASH_MD2"]=> + int(28) + ["MHASH_FNV132"]=> + int(29) + ["MHASH_FNV1A32"]=> + int(30) + ["MHASH_FNV164"]=> + int(31) + ["MHASH_FNV1A64"]=> + int(32) + ["MHASH_JOAAT"]=> + int(33) + ["MHASH_CRC32C"]=> + int(34) + ["MHASH_MURMUR3A"]=> + int(35) + ["MHASH_MURMUR3C"]=> + int(36) + ["MHASH_MURMUR3F"]=> + int(37) + ["MHASH_XXH32"]=> + int(38) + ["MHASH_XXH64"]=> + int(39) + ["MHASH_XXH3"]=> + int(40) + ["MHASH_XXH128"]=> + int(41) +} + +Deprecated: Function mhash() is deprecated in %s on line %d +bool(false) diff --git a/ext/hash/tests/gh16711_2.phpt b/ext/hash/tests/gh16711_2.phpt new file mode 100644 index 0000000000000..c48137958ed6d --- /dev/null +++ b/ext/hash/tests/gh16711_2.phpt @@ -0,0 +1,98 @@ +--TEST-- +GH-16711: Segmentation fault in mhash() +--SKIPIF-- + +--FILE-- +getConstants()); + +var_dump(mhash(4, 1086849124, 133)); +?> +--EXPECTF-- +array(40) { + ["HASH_HMAC"]=> + int(1) + ["MHASH_CRC32"]=> + int(0) + ["MHASH_MD5"]=> + int(1) + ["MHASH_SHA1"]=> + int(2) + ["MHASH_HAVAL256"]=> + int(3) + ["MHASH_RIPEMD160"]=> + int(5) + ["MHASH_TIGER"]=> + int(7) + ["MHASH_GOST"]=> + int(8) + ["MHASH_CRC32B"]=> + int(9) + ["MHASH_HAVAL224"]=> + int(10) + ["MHASH_HAVAL192"]=> + int(11) + ["MHASH_HAVAL160"]=> + int(12) + ["MHASH_HAVAL128"]=> + int(13) + ["MHASH_TIGER128"]=> + int(14) + ["MHASH_TIGER160"]=> + int(15) + ["MHASH_MD4"]=> + int(16) + ["MHASH_SHA256"]=> + int(17) + ["MHASH_ADLER32"]=> + int(18) + ["MHASH_SHA224"]=> + int(19) + ["MHASH_SHA512"]=> + int(20) + ["MHASH_SHA384"]=> + int(21) + ["MHASH_WHIRLPOOL"]=> + int(22) + ["MHASH_RIPEMD128"]=> + int(23) + ["MHASH_RIPEMD256"]=> + int(24) + ["MHASH_RIPEMD320"]=> + int(25) + ["MHASH_SNEFRU256"]=> + int(27) + ["MHASH_MD2"]=> + int(28) + ["MHASH_FNV132"]=> + int(29) + ["MHASH_FNV1A32"]=> + int(30) + ["MHASH_FNV164"]=> + int(31) + ["MHASH_FNV1A64"]=> + int(32) + ["MHASH_JOAAT"]=> + int(33) + ["MHASH_CRC32C"]=> + int(34) + ["MHASH_MURMUR3A"]=> + int(35) + ["MHASH_MURMUR3C"]=> + int(36) + ["MHASH_MURMUR3F"]=> + int(37) + ["MHASH_XXH32"]=> + int(38) + ["MHASH_XXH64"]=> + int(39) + ["MHASH_XXH3"]=> + int(40) + ["MHASH_XXH128"]=> + int(41) +} + +Deprecated: Function mhash() is deprecated in %s on line %d +bool(false) diff --git a/ext/ldap/tests/ldap_set_rebind_proc_basic.phpt b/ext/ldap/tests/ldap_set_rebind_proc_basic.phpt index 00c7bb81d9dd3..a328912a5e168 100644 --- a/ext/ldap/tests/ldap_set_rebind_proc_basic.phpt +++ b/ext/ldap/tests/ldap_set_rebind_proc_basic.phpt @@ -6,7 +6,10 @@ Patrick Allaert --EXTENSIONS-- ldap --SKIPIF-- - + --FILE-- --EXTENSIONS-- ldap --SKIPIF-- - + --FILE-- close(); ?> ---EXPECT-- +--EXPECTF-- string(3) "foo" -Unknown column 'invalid' in 'field list' +Unknown column 'invalid' in '%r(SELECT|field list)%r' diff --git a/ext/mysqli/tests/bug71863.phpt b/ext/mysqli/tests/bug71863.phpt index 4fc872bc9df59..f017fd68a58f6 100644 --- a/ext/mysqli/tests/bug71863.phpt +++ b/ext/mysqli/tests/bug71863.phpt @@ -30,4 +30,4 @@ if (!mysqli_query($link, "DROP TABLE IF EXISTS test_bug_71863")) mysqli_close($link); ?> --EXPECTF-- -%AUnknown column 'owner_id' in 'where clause' +%AUnknown column 'owner_id' in '%r(WHERE|where clause)%r' diff --git a/ext/mysqli/tests/fake_server.inc b/ext/mysqli/tests/fake_server.inc index b02fabc584c5d..1127f6c00e3f9 100644 --- a/ext/mysqli/tests/fake_server.inc +++ b/ext/mysqli/tests/fake_server.inc @@ -552,8 +552,8 @@ class my_mysqli_fake_server_conn public function read($bytes_len = 1024) { - // wait 10ms to fill the buffer - usleep(10000); + // wait 20ms to fill the buffer + usleep(20000); $data = fread($this->conn, $bytes_len); if ($data) { fprintf(STDERR, "[*] Received: %s\n", bin2hex($data)); diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt index db54a6c0177a1..279aec6a2cba1 100644 --- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt +++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt @@ -6,7 +6,7 @@ mysqli ---EXPECT-- +--EXPECTF-- bool(true) bool(true) ---- Row 1 @@ -80,7 +80,7 @@ NULL ALTER bool(true) bool(false) -string(34) "Unknown column 'a' in 'field list'" +string(%d) "Unknown column 'a' in '%r(SELECT|field list)%r'" ---- Row 1 bool(false) int(2) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index baa82ce29d352..7a1c91f10da9f 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3219,6 +3219,8 @@ static zend_result accel_post_startup(void) if (JIT_G(buffer_size) != 0) { zend_accel_error(ACCEL_LOG_WARNING, "Could not enable JIT!"); } + } else { + zend_jit_startup_ok = true; } } #endif diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index cd7f3f6195ff0..df797880c33f4 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -105,6 +105,8 @@ typedef struct _zend_jit_stub { #define JIT_STUB(name, offset, adjustment) \ {JIT_STUB_PREFIX #name, zend_jit_ ## name ## _stub, offset, adjustment} +bool zend_jit_startup_ok = false; + zend_ulong zend_jit_profile_counter = 0; int zend_jit_profile_counter_rid = -1; @@ -3193,7 +3195,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); op1_addr = 0; on_this = 1; } else { @@ -3241,7 +3243,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); op1_addr = 0; on_this = 1; } else { @@ -3282,7 +3284,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); op1_addr = 0; on_this = 1; } else { @@ -3785,7 +3787,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; op1_addr = 0; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); on_this = 1; } else { op1_info = OP1_INFO(); @@ -3936,7 +3938,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; op1_addr = 0; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); on_this = 1; } else { op1_info = OP1_INFO(); @@ -5106,6 +5108,13 @@ static void zend_jit_reset_counters(void) ZEND_EXT_API void zend_jit_activate(void) { +#ifdef ZTS + if (!zend_jit_startup_ok) { + JIT_G(enabled) = 0; + JIT_G(on) = 0; + return; + } +#endif zend_jit_profile_counter = 0; if (JIT_G(on)) { if (JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) { diff --git a/ext/opcache/jit/zend_jit.h b/ext/opcache/jit/zend_jit.h index b5871e6669365..fd8800e603f6a 100644 --- a/ext/opcache/jit/zend_jit.h +++ b/ext/opcache/jit/zend_jit.h @@ -88,6 +88,8 @@ typedef struct _zend_jit_trace_rec zend_jit_trace_rec; typedef struct _zend_jit_trace_stack_frame zend_jit_trace_stack_frame; typedef struct _sym_node zend_sym_node; +extern bool zend_jit_startup_ok; + typedef struct _zend_jit_globals { bool enabled; bool on; diff --git a/ext/opcache/jit/zend_jit_arm64.dasc b/ext/opcache/jit/zend_jit_arm64.dasc index fef4d0ddddc92..c351c482d51cb 100644 --- a/ext/opcache/jit/zend_jit_arm64.dasc +++ b/ext/opcache/jit/zend_jit_arm64.dasc @@ -504,7 +504,11 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size) ||#else | .long 0xd53bd051 // TODO: hard-coded: mrs TMP3, tpidr_el0 || if (tsrm_ls_cache_tcb_offset == 0) { +||#ifdef __MUSL__ +| ldr TMP3, [TMP3, #-8] +||#else | ldr TMP3, [TMP3, #0] +||#endif | MEM_ACCESS_64_WITH_UOFFSET_64 ldr, TMP3, TMP3, tsrm_tls_index, TMP1 | MEM_ACCESS_64_WITH_UOFFSET_64 ldr, reg, TMP3, tsrm_tls_offset, TMP1 || } else { @@ -1548,6 +1552,9 @@ static bool logical_immediate_p(uint64_t value, uint32_t reg_size) || } | IF_GC_MAY_NOT_LEAK FCARG1x, >4, Rw(tmp_reg1), Rw(tmp_reg2) | // gc_possible_root(Z_COUNTED_P(z)) +|| if (opline) { +| SET_EX_OPLINE opline, TMP1 +|| } | EXT_CALL gc_possible_root, Rx(tmp_reg1) || } || if (cold && ((op_info) & ((MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_INDIRECT|MAY_BE_GUARD)-(MAY_BE_OBJECT|MAY_BE_RESOURCE))) != 0) { @@ -2787,6 +2794,20 @@ static int zend_jit_setup(void) /* Index is offset by 1 on FreeBSD (https://github.com/freebsd/freebsd-src/blob/22ca6db50f4e6bd75a141f57cf953d8de6531a06/lib/libc/gen/tls.c#L88) */ tsrm_tls_index = (tlsdesc->index + 1) * 8; } +# elif defined(__MUSL__) + if (tsrm_ls_cache_tcb_offset == 0) { + size_t **where; + + __asm__( + "adrp %0, :tlsdesc:_tsrm_ls_cache\n" + "add %0, %0, :tlsdesc_lo12:_tsrm_ls_cache\n" + : "=r" (where)); + /* See https://github.com/ARM-software/abi-aa/blob/2a70c42d62e9c3eb5887fa50b71257f20daca6f9/aaelf64/aaelf64.rst */ + size_t *tlsdesc = where[1]; + + tsrm_tls_offset = tlsdesc[1]; + tsrm_tls_index = tlsdesc[0] * 8; + } # else ZEND_ASSERT(tsrm_ls_cache_tcb_offset != 0); # endif @@ -5970,6 +5991,9 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) { |4: | IF_GC_MAY_NOT_LEAK FCARG1x, >8, TMP1w, TMP2w + if (opline) { + | SET_EX_OPLINE opline, REG0 + } | EXT_CALL gc_possible_root, REG0 if (in_cold) { | b >8 @@ -5997,6 +6021,9 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, | GET_ZVAL_PTR FCARG1x, var_use_addr, TMP1 | GC_DELREF FCARG1x, TMP1w | IF_GC_MAY_NOT_LEAK FCARG1x, >5, TMP1w, TMP2w + if (opline) { + | SET_EX_OPLINE opline, TMP1 + } | EXT_CALL gc_possible_root, TMP1 if (Z_REG(var_use_addr) != ZREG_FP) { | ldr Rx(Z_REG(var_use_addr)), T1 // restore @@ -11857,6 +11884,9 @@ static int zend_jit_bind_global(dasm_State **Dst, const zend_op *opline, uint32_ |3: | // GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr) | IF_GC_MAY_NOT_LEAK FCARG1x, >5, TMP1w, TMP2w + if (opline) { + | SET_EX_OPLINE opline, REG0 + } | EXT_CALL gc_possible_root, REG0 | b >5 } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 2e662b718dcdf..5d824c207d18f 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -4652,7 +4652,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); op1_addr = 0; on_this = 1; } else { @@ -4743,7 +4743,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); op1_addr = 0; on_this = 1; } else { @@ -4823,7 +4823,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); op1_addr = 0; on_this = 1; } else { @@ -5408,6 +5408,9 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par res_type = Z_TYPE_P(RT_CONSTANT(opline, opline->op1)); } else if (op1_type != IS_UNKNOWN) { res_type = op1_type; + if (res_type == IS_UNDEF) { + res_type = IS_NULL; + } } if (op_array->type == ZEND_EVAL_CODE // TODO: support for top-level code @@ -5843,7 +5846,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); op1_addr = 0; on_this = 1; } else { @@ -6122,7 +6125,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (opline->op1_type == IS_UNUSED) { op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; ce = op_array->scope; - ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0; + ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); op1_addr = 0; on_this = 1; } else { @@ -7178,6 +7181,11 @@ static zend_jit_trace_stop zend_jit_compile_root_trace(zend_jit_trace_rec *trace t->polymorphism = 0; t->jmp_table_size = 0; t->op_array = trace_buffer[0].op_array; + if (!(t->op_array->fn_flags & ZEND_ACC_IMMUTABLE)) { + zend_jit_op_array_trace_extension *jit_extension = + (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(t->op_array); + t->op_array = jit_extension->op_array; + } t->opline = trace_buffer[1].opline; t->exit_info = exit_info; t->stack_map = NULL; diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index daa2608518ab1..c6f374dbced03 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -926,7 +926,15 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array); if (UNEXPECTED(!jit_extension) || UNEXPECTED(!(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE))) { - stop = ZEND_JIT_TRACE_STOP_INTERPRETER; +#ifdef HAVE_GCC_GLOBAL_REGS + if (execute_data->prev_execute_data != prev_execute_data) { +#else + if (rc < 0) { +#endif + stop = ZEND_JIT_TRACE_STOP_RETURN; + } else { + stop = ZEND_JIT_TRACE_STOP_INTERPRETER; + } break; } offset = jit_extension->offset; diff --git a/ext/opcache/jit/zend_jit_x86.dasc b/ext/opcache/jit/zend_jit_x86.dasc index 0c6a4c822eece..33277958843bf 100644 --- a/ext/opcache/jit/zend_jit_x86.dasc +++ b/ext/opcache/jit/zend_jit_x86.dasc @@ -1443,6 +1443,9 @@ static size_t tsrm_tls_offset; |1: || } | IF_GC_MAY_NOT_LEAK FCARG1a, >4 +|| if (opline) { +| SET_EX_OPLINE opline, r0 +|| } | // gc_possible_root(Z_COUNTED_P(z)) | EXT_CALL gc_possible_root, r0 || } @@ -6522,6 +6525,9 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, if (RC_MAY_BE_N(var_info) && (var_info & (MAY_BE_ARRAY|MAY_BE_OBJECT)) != 0) { |4: | IF_GC_MAY_NOT_LEAK FCARG1a, >8 + if (opline) { + | SET_EX_OPLINE opline, r0 + } | EXT_CALL gc_possible_root, r0 if (in_cold) { | jmp >8 @@ -6549,6 +6555,9 @@ static int zend_jit_assign_to_variable(dasm_State **Dst, | GET_ZVAL_PTR FCARG1a, var_use_addr | GC_DELREF FCARG1a | IF_GC_MAY_NOT_LEAK FCARG1a, >5 + if (opline) { + | SET_EX_OPLINE opline, r0 + } | EXT_CALL gc_possible_root, r0 if (Z_REG(var_use_addr) != ZREG_FP) { | mov Ra(Z_REG(var_use_addr)), T1 // restore @@ -12617,6 +12626,9 @@ static int zend_jit_bind_global(dasm_State **Dst, const zend_op *opline, uint32_ |3: | // GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr) | IF_GC_MAY_NOT_LEAK FCARG1a, >5 + if (opline) { + | SET_EX_OPLINE opline, r0 + } | EXT_CALL gc_possible_root, r1 | jmp >5 } diff --git a/ext/opcache/tests/jit/assign_obj_005.phpt b/ext/opcache/tests/jit/assign_obj_005.phpt new file mode 100644 index 0000000000000..cbda0b0c17948 --- /dev/null +++ b/ext/opcache/tests/jit/assign_obj_005.phpt @@ -0,0 +1,38 @@ +--TEST-- +JIT ASSIGN_OBJ: Typed & not-typed property +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--FILE-- +x = $x; + } +} +class C2 extends C1 { + public $x = 0; +} +class C3 extends C1 { + public int $x = 0; +} +$o = new C2("abcd"); +var_dump($o->x); +$o = new C3(42); +var_dump($o->x); +$o = new C3("abcd"); +var_dump($o->x); +?> +--EXPECTF-- +string(4) "abcd" +int(42) + +Fatal error: Uncaught TypeError: Cannot assign string to property C3::$x of type int in %sassign_obj_005.php:6 +Stack trace: +#0 %sassign_obj_005.php(19): C1->__construct('abcd') +#1 {main} + thrown in %sassign_obj_005.php on line 6 diff --git a/ext/opcache/tests/jit/gh16770.phpt b/ext/opcache/tests/jit/gh16770.phpt new file mode 100644 index 0000000000000..71d796ade8ed9 --- /dev/null +++ b/ext/opcache/tests/jit/gh16770.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-16770 (Tracing JIT type mismatch when returning UNDEF) +--INI-- +opcache.jit=1254 +opcache.jit_hot_loop=1 +opcache.jit_buffer_size=32M +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECTF-- +Warning: Undefined variable $undefined in %s on line %d + +Warning: Undefined variable $undefined in %s on line %d + +Warning: Undefined variable $undefined in %s on line %d + +Warning: Undefined variable $undefined in %s on line %d + +Warning: Undefined variable $undefined in %s on line %d + +Warning: Undefined variable $undefined in %s on line %d + +Warning: Undefined variable $undefined in %s on line %d + +Warning: Undefined variable $undefined in %s on line %d + +Warning: Undefined variable $undefined in %s on line %d + +Warning: Undefined variable $undefined in %s on line %d +NULL diff --git a/ext/opcache/tests/jit/gh16829.phpt b/ext/opcache/tests/jit/gh16829.phpt new file mode 100644 index 0000000000000..d8bee06c90fcf --- /dev/null +++ b/ext/opcache/tests/jit/gh16829.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-16829 (Segmentation fault with opcache.jit=tracing enabled on aarch64) +--INI-- +opcache.jit_buffer_size=32M +--EXTENSIONS-- +opcache +--FILE-- + +DONE +--EXPECT-- +DONE diff --git a/ext/opcache/tests/jit/gh16829_1.inc b/ext/opcache/tests/jit/gh16829_1.inc new file mode 100644 index 0000000000000..cc4411d827b75 --- /dev/null +++ b/ext/opcache/tests/jit/gh16829_1.inc @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/ext/opcache/tests/jit/gh16829_2.inc b/ext/opcache/tests/jit/gh16829_2.inc new file mode 100644 index 0000000000000..8fddb035431ba --- /dev/null +++ b/ext/opcache/tests/jit/gh16829_2.inc @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index eee361cc86edb..c5720ee97e38c 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -1538,7 +1538,7 @@ PHP_FUNCTION(openssl_x509_export_to_file) } if (!php_openssl_check_path(filename, filename_len, file_path, 2)) { - return; + goto exit_cleanup_cert; } bio_out = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY)); @@ -1556,13 +1556,14 @@ PHP_FUNCTION(openssl_x509_export_to_file) php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path); } - if (cert_str) { - X509_free(cert); - } - if (!BIO_free(bio_out)) { php_openssl_store_errors(); } + +exit_cleanup_cert: + if (cert_str) { + X509_free(cert); + } } /* }}} */ @@ -3110,7 +3111,7 @@ PHP_FUNCTION(openssl_csr_export_to_file) } if (!php_openssl_check_path(filename, filename_len, file_path, 2)) { - return; + goto exit_cleanup; } bio_out = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_W(PKCS7_BINARY)); @@ -3130,6 +3131,7 @@ PHP_FUNCTION(openssl_csr_export_to_file) php_error_docref(NULL, E_WARNING, "Error opening file %s", file_path); } +exit_cleanup: if (csr_str) { X509_REQ_free(csr); } @@ -3571,6 +3573,7 @@ static EVP_PKEY *php_openssl_pkey_from_zval( } else { ZVAL_COPY(&tmp, zphrase); if (!try_convert_to_string(&tmp)) { + zval_ptr_dtor(&tmp); return NULL; } @@ -3617,12 +3620,14 @@ static EVP_PKEY *php_openssl_pkey_from_zval( if (!(Z_TYPE_P(val) == IS_STRING || Z_TYPE_P(val) == IS_OBJECT)) { TMP_CLEAN; } - if (!try_convert_to_string(val)) { + zend_string *val_str = zval_try_get_string(val); + if (!val_str) { TMP_CLEAN; } - if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) { - if (!php_openssl_check_path_str(Z_STR_P(val), file_path, arg_num)) { + if (ZSTR_LEN(val_str) > 7 && memcmp(ZSTR_VAL(val_str), "file://", sizeof("file://") - 1) == 0) { + if (!php_openssl_check_path_str(val_str, file_path, arg_num)) { + zend_string_release_ex(val_str, false); TMP_CLEAN; } is_file = true; @@ -3630,7 +3635,7 @@ static EVP_PKEY *php_openssl_pkey_from_zval( /* it's an X509 file/cert of some kind, and we need to extract the data from that */ if (public_key) { php_openssl_errors_set_mark(); - cert = php_openssl_x509_from_str(Z_STR_P(val), arg_num, false, NULL); + cert = php_openssl_x509_from_str(val_str, arg_num, false, NULL); if (cert) { free_cert = 1; @@ -3641,10 +3646,11 @@ static EVP_PKEY *php_openssl_pkey_from_zval( if (is_file) { in = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY)); } else { - in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val)); + in = BIO_new_mem_buf(ZSTR_VAL(val_str), (int)ZSTR_LEN(val_str)); } if (in == NULL) { php_openssl_store_errors(); + zend_string_release_ex(val_str, false); TMP_CLEAN; } key = PEM_read_bio_PUBKEY(in, NULL,NULL, NULL); @@ -3657,10 +3663,11 @@ static EVP_PKEY *php_openssl_pkey_from_zval( if (is_file) { in = BIO_new_file(file_path, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY)); } else { - in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val)); + in = BIO_new_mem_buf(ZSTR_VAL(val_str), (int)ZSTR_LEN(val_str)); } if (in == NULL) { + zend_string_release_ex(val_str, false); TMP_CLEAN; } if (passphrase == NULL) { @@ -3673,6 +3680,8 @@ static EVP_PKEY *php_openssl_pkey_from_zval( } BIO_free(in); } + + zend_string_release_ex(val_str, false); } if (key == NULL) { @@ -4749,7 +4758,7 @@ PHP_FUNCTION(openssl_pkey_export_to_file) } if (!php_openssl_check_path(filename, filename_len, file_path, 2)) { - RETURN_FALSE; + goto clean_exit_key; } PHP_SSL_REQ_INIT(&req); @@ -4785,8 +4794,9 @@ PHP_FUNCTION(openssl_pkey_export_to_file) clean_exit: PHP_SSL_REQ_DISPOSE(&req); - EVP_PKEY_free(key); BIO_free(bio_out); +clean_exit_key: + EVP_PKEY_free(key); } /* }}} */ diff --git a/ext/openssl/tests/openssl_csr_export_to_file_leak.phpt b/ext/openssl/tests/openssl_csr_export_to_file_leak.phpt new file mode 100644 index 0000000000000..e6ce373d355b1 --- /dev/null +++ b/ext/openssl/tests/openssl_csr_export_to_file_leak.phpt @@ -0,0 +1,14 @@ +--TEST-- +openssl_csr_export_to_file memory leak +--EXTENSIONS-- +openssl +--FILE-- + +--EXPECTF-- +Warning: openssl_csr_export_to_file(output_filename): must be a valid file path %s +bool(false) diff --git a/ext/openssl/tests/openssl_pkey_export_to_file_leak.phpt b/ext/openssl/tests/openssl_pkey_export_to_file_leak.phpt new file mode 100644 index 0000000000000..5e2bdff6b48fc --- /dev/null +++ b/ext/openssl/tests/openssl_pkey_export_to_file_leak.phpt @@ -0,0 +1,15 @@ +--TEST-- +openssl_pkey_export_to_file memory leak +--EXTENSIONS-- +openssl +--FILE-- + +--EXPECTF-- +Warning: openssl_pkey_export_to_file(output_filename): must be a valid file path %s +bool(false) diff --git a/ext/openssl/tests/openssl_pkey_export_to_file_object_to_string.phpt b/ext/openssl/tests/openssl_pkey_export_to_file_object_to_string.phpt new file mode 100644 index 0000000000000..0e504bfa4ac63 --- /dev/null +++ b/ext/openssl/tests/openssl_pkey_export_to_file_object_to_string.phpt @@ -0,0 +1,27 @@ +--TEST-- +openssl_pkey_export_to_file object to string conversion +--EXTENSIONS-- +openssl +--FILE-- + +--EXPECT-- +array(2) { + [0]=> + object(Test)#1 (0) { + } + [1]=> + string(0) "" +} diff --git a/ext/openssl/tests/openssl_x509_export_to_file_leak.phpt b/ext/openssl/tests/openssl_x509_export_to_file_leak.phpt new file mode 100644 index 0000000000000..5775c2597c3e0 --- /dev/null +++ b/ext/openssl/tests/openssl_x509_export_to_file_leak.phpt @@ -0,0 +1,14 @@ +--TEST-- +openssl_x509_export_to_file memory leak +--EXTENSIONS-- +openssl +--FILE-- + +--EXPECTF-- +Warning: openssl_x509_export_to_file(output_filename): must be a valid file path %s +bool(false) diff --git a/ext/openssl/tests/php_openssl_pkey_from_zval_leak.phpt b/ext/openssl/tests/php_openssl_pkey_from_zval_leak.phpt new file mode 100644 index 0000000000000..2b19dd311150a --- /dev/null +++ b/ext/openssl/tests/php_openssl_pkey_from_zval_leak.phpt @@ -0,0 +1,23 @@ +--TEST-- +php_openssl_pkey_from_zval memory leak +--EXTENSIONS-- +openssl +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +create a leak diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index a1a16b0c834b9..a7d898221f881 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2076,6 +2076,23 @@ static zend_function *dbstmt_method_get(zend_object **object_pp, zend_string *me return fbc; } +static HashTable *dbstmt_get_gc(zend_object *object, zval **gc_data, int *gc_count) +{ + pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object); + *gc_data = &stmt->fetch.into; + *gc_count = 1; + + /** + * If there are no dynamic properties and the default property is 1 (that is, there is only one property + * of string that does not participate in GC), there is no need to call zend_std_get_properties(). + */ + if (object->properties == NULL && object->ce->default_properties_count <= 1) { + return NULL; + } else { + return zend_std_get_properties(object); + } +} + zend_object_handlers pdo_dbstmt_object_handlers; zend_object_handlers pdo_row_object_handlers; @@ -2492,6 +2509,7 @@ void pdo_stmt_init(void) pdo_dbstmt_object_handlers.get_method = dbstmt_method_get; pdo_dbstmt_object_handlers.compare = zend_objects_not_comparable; pdo_dbstmt_object_handlers.clone_obj = NULL; + pdo_dbstmt_object_handlers.get_gc = dbstmt_get_gc; pdo_row_ce = register_class_PDORow(); pdo_row_ce->create_object = pdo_row_new; diff --git a/ext/pdo/tests/gh16703.phpt b/ext/pdo/tests/gh16703.phpt new file mode 100644 index 0000000000000..e5b4b8640389e --- /dev/null +++ b/ext/pdo/tests/gh16703.phpt @@ -0,0 +1,48 @@ +--TEST-- +GH-16703: Memory leak of setFetchMode() +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- + PDO::CASE_LOWER, + PDO::ATTR_STATEMENT_CLASS => [TestStmt::class], + ], +); + +$db->exec('CREATE TABLE gh16703 (name varchar(255))'); +$db->exec("INSERT INTO gh16703 (name) VALUES ('test_name')"); + +$stmt = $db->query('SELECT name FROM gh16703'); +$t = $stmt; +$stmt->setFetchMode(PDO::FETCH_INTO, $stmt); +$stmt->fetch(); +echo "done!\n"; +?> +--CLEAN-- +exec('DROP TABLE gh16703'); +?> +--EXPECT-- +done! diff --git a/ext/pgsql/tests/80_bug14383.phpt b/ext/pgsql/tests/80_bug14383.phpt index f17af830d7041..c14b2f414d9bd 100644 --- a/ext/pgsql/tests/80_bug14383.phpt +++ b/ext/pgsql/tests/80_bug14383.phpt @@ -39,12 +39,12 @@ bool(true) bool(true) Try to remove key 1 again bool(false) +[key10]name10: Content String 10 +[key30]name30: Content String 30 key2: Content String 2 key4: Another Content String key5: The last content string name9: Content String 9 -[key10]name10: Content String 10 -[key30]name30: Content String 30 Total keys: 6 Key 1 exists? N Key 2 exists? Y diff --git a/ext/phar/phar.c b/ext/phar/phar.c index b08bd1eab1630..6f52fc714d6b7 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1774,7 +1774,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error); } - if (got > 512) { + if (got >= 512) { if (phar_is_tar(pos, fname)) { php_stream_rewind(fp); return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, is_data, compression, error); diff --git a/ext/phar/tar.c b/ext/phar/tar.c index ce8ee0a369882..e259fb6f3dece 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -247,9 +247,8 @@ int phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, char *alia entry.is_tar = 1; entry.is_crc_checked = 1; entry.phar = myphar; - pos += sizeof(buf); - do { + while (true) { phar_entry_info *newentry; pos = php_stream_tell(fp); @@ -590,6 +589,11 @@ int phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, char *alia } } + /* Only read next header if we're not yet at the end */ + if (php_stream_tell(fp) == totalsize) { + break; + } + read = php_stream_read(fp, buf, sizeof(buf)); if (read != sizeof(buf)) { @@ -600,7 +604,7 @@ int phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, char *alia phar_destroy_phar_data(myphar); return FAILURE; } - } while (!php_stream_eof(fp)); + } if (zend_hash_str_exists(&(myphar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) { myphar->is_data = 0; diff --git a/ext/phar/tests/tar/gh16695_1.phpt b/ext/phar/tests/tar/gh16695_1.phpt new file mode 100644 index 0000000000000..8ce82bcf28dd9 --- /dev/null +++ b/ext/phar/tests/tar/gh16695_1.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-16695 (phar:// tar parser and zero-length file header blocks) +--CREDITS-- +hakre +--EXTENSIONS-- +phar +--INI-- +phar.require_hash=0 +--FILE-- + +--CLEAN-- + +--EXPECTF-- +int(512) + +Warning: file_get_contents(%stls): Failed to open stream: phar error: path "tls" is a directory in %s on line %d +bool(false) diff --git a/ext/phar/tests/tar/gh16695_2.phpt b/ext/phar/tests/tar/gh16695_2.phpt new file mode 100644 index 0000000000000..5b7200398c496 --- /dev/null +++ b/ext/phar/tests/tar/gh16695_2.phpt @@ -0,0 +1,26 @@ +--TEST-- +GH-16695 (phar:// tar parser and zero-length file header blocks) +--CREDITS-- +hakre +--EXTENSIONS-- +phar +--INI-- +phar.require_hash=0 +--FILE-- + +--CLEAN-- + +--EXPECT-- +int(1024) +string(122) "{"Name":"default","Metadata":{},"Endpoints":{"docker":{"Host":"unix:///run/user/1000/docker.sock","SkipTLSVerify":false}}}" diff --git a/ext/phar/tests/tar/gh16695_3.phpt b/ext/phar/tests/tar/gh16695_3.phpt new file mode 100644 index 0000000000000..eddf697b0137c --- /dev/null +++ b/ext/phar/tests/tar/gh16695_3.phpt @@ -0,0 +1,26 @@ +--TEST-- +GH-16695 (phar:// tar parser and zero-length file header blocks) +--CREDITS-- +hakre +--EXTENSIONS-- +phar +--INI-- +phar.require_hash=0 +--FILE-- + +--CLEAN-- + +--EXPECT-- +int(512) +string(0) "" diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index e0c1bf7926e9f..6a5a514f9a3d3 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -2532,7 +2532,11 @@ static zval *php_sxe_iterator_current_data(zend_object_iterator *iter) /* {{{ */ { php_sxe_iterator *iterator = (php_sxe_iterator *)iter; - return &iterator->sxe->iter.data; + zval *data = &iterator->sxe->iter.data; + if (Z_ISUNDEF_P(data)) { + return NULL; + } + return data; } /* }}} */ diff --git a/ext/simplexml/tests/gh16808.phpt b/ext/simplexml/tests/gh16808.phpt new file mode 100644 index 0000000000000..be0bc59fb655a --- /dev/null +++ b/ext/simplexml/tests/gh16808.phpt @@ -0,0 +1,12 @@ +--TEST-- +GH-16808 (Segmentation fault in RecursiveIteratorIterator->current() with a xml element input) +--EXTENSIONS-- +simplexml +--FILE-- +"); +$test = new RecursiveIteratorIterator($sxe); +var_dump($test->current()); +?> +--EXPECT-- +NULL diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 617d07ab7822a..2e18d8e0128a6 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -624,6 +624,31 @@ static void php_snmp_internal(INTERNAL_FUNCTION_PARAMETERS, int st, } /* }}} */ +static void php_snmp_zend_string_release_from_char_pointer(char *ptr) { + if (ptr) { + zend_string *pptr = (zend_string *)(ptr - XtOffsetOf(zend_string, val)); + zend_string_release(pptr); + } +} + +static void php_free_objid_query(struct objid_query *objid_query, HashTable* oid_ht, const HashTable *value_ht, int st) { + if (oid_ht) { + uint32_t count = zend_hash_num_elements(oid_ht); + + for (uint32_t i = 0; i < count; i ++) { + snmpobjarg *arg = &objid_query->vars[i]; + if (!arg->oid) { + break; + } + if (value_ht) { + php_snmp_zend_string_release_from_char_pointer(arg->value); + } + php_snmp_zend_string_release_from_char_pointer(arg->oid); + } + } + efree(objid_query->vars); +} + /* {{{ php_snmp_parse_oid * * OID parser (and type, value for SNMP_SET command) @@ -672,10 +697,15 @@ static bool php_snmp_parse_oid( return false; } objid_query->vars = (snmpobjarg *)safe_emalloc(sizeof(snmpobjarg), zend_hash_num_elements(oid_ht), 0); + memset(objid_query->vars, 0, sizeof(snmpobjarg) * zend_hash_num_elements(oid_ht)); objid_query->array_output = (st & SNMP_CMD_SET) == 0; ZEND_HASH_FOREACH_VAL(oid_ht, tmp_oid) { - convert_to_string(tmp_oid); - objid_query->vars[objid_query->count].oid = Z_STRVAL_P(tmp_oid); + zend_string *tmp = zval_try_get_string(tmp_oid); + if (!tmp) { + php_free_objid_query(objid_query, oid_ht, value_ht, st); + return false; + } + objid_query->vars[objid_query->count].oid = ZSTR_VAL(tmp); if (st & SNMP_CMD_SET) { if (type_str) { pptr = ZSTR_VAL(type_str); @@ -699,18 +729,24 @@ static bool php_snmp_parse_oid( } } if (idx_type < type_ht->nNumUsed) { - convert_to_string(tmp_type); - if (Z_STRLEN_P(tmp_type) != 1) { + zend_string *type = zval_try_get_string(tmp_type); + if (!type) { + php_free_objid_query(objid_query, oid_ht, value_ht, st); + return false; + } + size_t len = ZSTR_LEN(type); + char ptype = *ZSTR_VAL(type); + zend_string_release(type); + if (len != 1) { zend_value_error("Type must be a single character"); - efree(objid_query->vars); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } - pptr = Z_STRVAL_P(tmp_type); - objid_query->vars[objid_query->count].type = *pptr; + objid_query->vars[objid_query->count].type = ptype; idx_type++; } else { - php_error_docref(NULL, E_WARNING, "'%s': no type set", Z_STRVAL_P(tmp_oid)); - efree(objid_query->vars); + php_error_docref(NULL, E_WARNING, "'%s': no type set", ZSTR_VAL(tmp)); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } } @@ -736,12 +772,16 @@ static bool php_snmp_parse_oid( } } if (idx_value < value_ht->nNumUsed) { - convert_to_string(tmp_value); - objid_query->vars[objid_query->count].value = Z_STRVAL_P(tmp_value); + zend_string *tmp = zval_try_get_string(tmp_value); + if (!tmp) { + php_free_objid_query(objid_query, oid_ht, value_ht, st); + return false; + } + objid_query->vars[objid_query->count].value = ZSTR_VAL(tmp); idx_value++; } else { - php_error_docref(NULL, E_WARNING, "'%s': no value set", Z_STRVAL_P(tmp_oid)); - efree(objid_query->vars); + php_error_docref(NULL, E_WARNING, "'%s': no value set", ZSTR_VAL(tmp)); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } } @@ -754,14 +794,14 @@ static bool php_snmp_parse_oid( if (st & SNMP_CMD_WALK) { if (objid_query->count > 1) { php_snmp_error(object, PHP_SNMP_ERRNO_OID_PARSING_ERROR, "Multi OID walks are not supported!"); - efree(objid_query->vars); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } objid_query->vars[0].name_length = MAX_NAME_LEN; if (strlen(objid_query->vars[0].oid)) { /* on a walk, an empty string means top of tree - no error */ if (!snmp_parse_oid(objid_query->vars[0].oid, objid_query->vars[0].name, &(objid_query->vars[0].name_length))) { php_snmp_error(object, PHP_SNMP_ERRNO_OID_PARSING_ERROR, "Invalid object identifier: %s", objid_query->vars[0].oid); - efree(objid_query->vars); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } } else { @@ -773,7 +813,7 @@ static bool php_snmp_parse_oid( objid_query->vars[objid_query->offset].name_length = MAX_OID_LEN; if (!snmp_parse_oid(objid_query->vars[objid_query->offset].oid, objid_query->vars[objid_query->offset].name, &(objid_query->vars[objid_query->offset].name_length))) { php_snmp_error(object, PHP_SNMP_ERRNO_OID_PARSING_ERROR, "Invalid object identifier: %s", objid_query->vars[objid_query->offset].oid); - efree(objid_query->vars); + php_free_objid_query(objid_query, oid_ht, value_ht, st); return false; } } @@ -1250,12 +1290,12 @@ static void php_snmp(INTERNAL_FUNCTION_PARAMETERS, int st, int version) if (session_less_mode) { if (!netsnmp_session_init(&session, version, a1, a2, timeout, retries)) { - efree(objid_query.vars); + php_free_objid_query(&objid_query, oid_ht, value_ht, st); netsnmp_session_free(&session); RETURN_FALSE; } if (version == SNMP_VERSION_3 && !netsnmp_session_set_security(session, a3, a4, a5, a6, a7, NULL, NULL)) { - efree(objid_query.vars); + php_free_objid_query(&objid_query, oid_ht, value_ht, st); netsnmp_session_free(&session); /* Warning message sent already, just bail out */ RETURN_FALSE; @@ -1266,7 +1306,7 @@ static void php_snmp(INTERNAL_FUNCTION_PARAMETERS, int st, int version) session = snmp_object->session; if (!session) { zend_throw_error(NULL, "Invalid or uninitialized SNMP object"); - efree(objid_query.vars); + php_free_objid_query(&objid_query, oid_ht, value_ht, st); RETURN_THROWS(); } @@ -1292,7 +1332,7 @@ static void php_snmp(INTERNAL_FUNCTION_PARAMETERS, int st, int version) php_snmp_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, st, session, &objid_query); - efree(objid_query.vars); + php_free_objid_query(&objid_query, oid_ht, value_ht, st); if (session_less_mode) { netsnmp_session_free(&session); diff --git a/ext/snmp/tests/gh16959.phpt b/ext/snmp/tests/gh16959.phpt new file mode 100644 index 0000000000000..ce647b15b9dac --- /dev/null +++ b/ext/snmp/tests/gh16959.phpt @@ -0,0 +1,69 @@ +--TEST-- +snmpget() modifies object_id array source +--EXTENSIONS-- +snmp +--SKIPIF-- + +--FILE-- + 077, -066 => -066, -0345 => -0345, 0 => 0 +); +var_dump($bad_object_ids); +var_dump(snmpget($hostname, "", $bad_object_ids) === false); +// The array should remain unmodified +var_dump($bad_object_ids); +try { + snmpget($hostname, "", [0 => new stdClass()]); +} catch (Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} + +try { + snmp2_set($hostname, $communityWrite, $bad_object_ids, array(new stdClass()), array(null)); +} catch (Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + snmp2_set($hostname, $communityWrite, $bad_object_ids, array("toolongtype"), array(null)); +} catch (Throwable $e) { + echo $e->getMessage() . PHP_EOL; +} +try { + snmp2_set($hostname, $communityWrite, $bad_object_ids, array(str_repeat("onetoomuch", random_int(1, 1))), array(null)); +} catch (Throwable $e) { + echo $e->getMessage(); +} +?> +--EXPECTF-- +array(4) { + [63]=> + int(63) + [-54]=> + int(-54) + [-229]=> + int(-229) + [0]=> + int(0) +} + +Warning: snmpget(): Invalid object identifier: -54 in %s on line %d +bool(true) +array(4) { + [63]=> + int(63) + [-54]=> + int(-54) + [-229]=> + int(-229) + [0]=> + int(0) +} +Object of class stdClass could not be converted to string +Object of class stdClass could not be converted to string +Type must be a single character +Type must be a single character diff --git a/ext/soap/tests/gh15711.phpt b/ext/soap/tests/gh15711.phpt index a49ff280fee59..b72251cc6f95b 100644 --- a/ext/soap/tests/gh15711.phpt +++ b/ext/soap/tests/gh15711.phpt @@ -33,7 +33,7 @@ class TestSoapClient extends SoapClient { } } -$client = new TestSoapClient('ext/soap/tests/gh15711.wsdl', ['classmap' => ['book' => 'book']]); +$client = new TestSoapClient(__DIR__ . '/gh15711.wsdl', ['classmap' => ['book' => 'book']]); echo "--- Test with backed enum ---\n"; diff --git a/ext/standard/array.c b/ext/standard/array.c index 46181f8593e05..57a3c66c59ede 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1030,11 +1030,50 @@ static inline HashTable *get_ht_for_iap(zval *zv, bool separate) { return zobj->handlers->get_properties(zobj); } +static zval *php_array_iter_seek_current(HashTable *array, bool forward_direction) +{ + zval *entry; + + while (true) { + if ((entry = zend_hash_get_current_data(array)) == NULL) { + return NULL; + } + + ZVAL_DEINDIRECT(entry); + + /* Possible with an uninitialized typed property */ + if (UNEXPECTED(Z_TYPE_P(entry) == IS_UNDEF)) { + zend_result result; + if (forward_direction) { + result = zend_hash_move_forward(array); + } else { + result = zend_hash_move_backwards(array); + } + if (result != SUCCESS) { + return NULL; + } + } else { + break; + } + } + + return entry; +} + +static void php_array_iter_return_current(zval *return_value, HashTable *array, bool forward_direction) +{ + zval *entry = php_array_iter_seek_current(array, forward_direction); + if (EXPECTED(entry)) { + RETURN_COPY_DEREF(entry); + } else { + RETURN_FALSE; + } +} + /* {{{ Advances array argument's internal pointer to the last element and return it */ PHP_FUNCTION(end) { zval *array_zv; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) @@ -1048,15 +1087,7 @@ PHP_FUNCTION(end) zend_hash_internal_pointer_end(array); if (USED_RET()) { - if ((entry = zend_hash_get_current_data(array)) == NULL) { - RETURN_FALSE; - } - - if (Z_TYPE_P(entry) == IS_INDIRECT) { - entry = Z_INDIRECT_P(entry); - } - - RETURN_COPY_DEREF(entry); + php_array_iter_return_current(return_value, array, false); } } /* }}} */ @@ -1065,7 +1096,6 @@ PHP_FUNCTION(end) PHP_FUNCTION(prev) { zval *array_zv; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) @@ -1079,15 +1109,7 @@ PHP_FUNCTION(prev) zend_hash_move_backwards(array); if (USED_RET()) { - if ((entry = zend_hash_get_current_data(array)) == NULL) { - RETURN_FALSE; - } - - if (Z_TYPE_P(entry) == IS_INDIRECT) { - entry = Z_INDIRECT_P(entry); - } - - RETURN_COPY_DEREF(entry); + php_array_iter_return_current(return_value, array, false); } } /* }}} */ @@ -1096,7 +1118,6 @@ PHP_FUNCTION(prev) PHP_FUNCTION(next) { zval *array_zv; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) @@ -1110,15 +1131,7 @@ PHP_FUNCTION(next) zend_hash_move_forward(array); if (USED_RET()) { - if ((entry = zend_hash_get_current_data(array)) == NULL) { - RETURN_FALSE; - } - - if (Z_TYPE_P(entry) == IS_INDIRECT) { - entry = Z_INDIRECT_P(entry); - } - - RETURN_COPY_DEREF(entry); + php_array_iter_return_current(return_value, array, true); } } /* }}} */ @@ -1127,7 +1140,6 @@ PHP_FUNCTION(next) PHP_FUNCTION(reset) { zval *array_zv; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_OR_OBJECT_EX(array_zv, 0, 1) @@ -1141,15 +1153,7 @@ PHP_FUNCTION(reset) zend_hash_internal_pointer_reset(array); if (USED_RET()) { - if ((entry = zend_hash_get_current_data(array)) == NULL) { - RETURN_FALSE; - } - - if (Z_TYPE_P(entry) == IS_INDIRECT) { - entry = Z_INDIRECT_P(entry); - } - - RETURN_COPY_DEREF(entry); + php_array_iter_return_current(return_value, array, true); } } /* }}} */ @@ -1158,22 +1162,13 @@ PHP_FUNCTION(reset) PHP_FUNCTION(current) { zval *array_zv; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_OR_OBJECT(array_zv) ZEND_PARSE_PARAMETERS_END(); HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); - if ((entry = zend_hash_get_current_data(array)) == NULL) { - RETURN_FALSE; - } - - if (Z_TYPE_P(entry) == IS_INDIRECT) { - entry = Z_INDIRECT_P(entry); - } - - RETURN_COPY_DEREF(entry); + php_array_iter_return_current(return_value, array, true); } /* }}} */ @@ -1187,7 +1182,10 @@ PHP_FUNCTION(key) ZEND_PARSE_PARAMETERS_END(); HashTable *array = get_ht_for_iap(array_zv, /* separate */ false); - zend_hash_get_current_key_zval(array, return_value); + zval *entry = php_array_iter_seek_current(array, true); + if (EXPECTED(entry)) { + zend_hash_get_current_key_zval(array, return_value); + } } /* }}} */ @@ -3514,7 +3512,8 @@ PHP_FUNCTION(array_shift) } idx++; } - RETVAL_COPY_DEREF(val); + RETVAL_COPY_VALUE(val); + ZVAL_UNDEF(val); /* Delete the first value */ zend_hash_packed_del_val(Z_ARRVAL_P(stack), val); @@ -3568,7 +3567,8 @@ PHP_FUNCTION(array_shift) } idx++; } - RETVAL_COPY_DEREF(val); + RETVAL_COPY_VALUE(val); + ZVAL_UNDEF(val); /* Delete the first value */ zend_hash_del_bucket(Z_ARRVAL_P(stack), p); @@ -3592,6 +3592,10 @@ PHP_FUNCTION(array_shift) } zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack)); + + if (Z_ISREF_P(return_value)) { + zend_unwrap_reference(return_value); + } } /* }}} */ diff --git a/ext/standard/tests/array/gh16905.phpt b/ext/standard/tests/array/gh16905.phpt new file mode 100644 index 0000000000000..89d11575789e4 --- /dev/null +++ b/ext/standard/tests/array/gh16905.phpt @@ -0,0 +1,92 @@ +--TEST-- +GH-16905 (Internal iterator functions can't handle UNDEF properties) +--FILE-- +b = 1; +$x->c = 2; + +var_dump(reset($x)); +var_dump(current($x)); +var_dump(end($x)); + +var_dump(reset($x)); +var_dump(next($x)); + +var_dump(end($x)); +var_dump(prev($x)); + +var_dump(key($x)); +var_dump(current($x)); + +$x = new TestAllUndef; +var_dump(key($x)); +var_dump(current($x)); + +$x->a = 1; +var_dump(key($x)); +var_dump(current($x)); +reset($x); +var_dump(key($x)); +var_dump(current($x)); + +?> +--EXPECTF-- +Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d +int(1) + +Deprecated: current(): Calling current() on an object is deprecated in %s on line %d +int(1) + +Deprecated: end(): Calling end() on an object is deprecated in %s on line %d +int(2) + +Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d +int(1) + +Deprecated: next(): Calling next() on an object is deprecated in %s on line %d +int(2) + +Deprecated: end(): Calling end() on an object is deprecated in %s on line %d +int(2) + +Deprecated: prev(): Calling prev() on an object is deprecated in %s on line %d +int(1) + +Deprecated: key(): Calling key() on an object is deprecated in %s on line %d +string(1) "b" + +Deprecated: current(): Calling current() on an object is deprecated in %s on line %d +int(1) + +Deprecated: key(): Calling key() on an object is deprecated in %s on line %d +NULL + +Deprecated: current(): Calling current() on an object is deprecated in %s on line %d +bool(false) + +Deprecated: key(): Calling key() on an object is deprecated in %s on line %d +NULL + +Deprecated: current(): Calling current() on an object is deprecated in %s on line %d +bool(false) + +Deprecated: reset(): Calling reset() on an object is deprecated in %s on line %d + +Deprecated: key(): Calling key() on an object is deprecated in %s on line %d +string(1) "a" + +Deprecated: current(): Calling current() on an object is deprecated in %s on line %d +int(1) diff --git a/ext/standard/tests/array/gh16957.phpt b/ext/standard/tests/array/gh16957.phpt new file mode 100644 index 0000000000000..a716228249e7d --- /dev/null +++ b/ext/standard/tests/array/gh16957.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-16957 (Assertion failure in array_shift with self-referencing array) +--FILE-- + 1, 300 => 'two'); +var_dump($shifted = array_shift($new_array2)); +var_dump($new_array2); +var_dump($new_array2 === $shifted); +?> +--EXPECT-- +array(2) { + [0]=> + int(1) + [1]=> + string(3) "two" +} +array(2) { + [0]=> + int(1) + [1]=> + string(3) "two" +} +bool(true) +array(2) { + [0]=> + int(1) + [1]=> + string(3) "two" +} +array(2) { + [0]=> + int(1) + [1]=> + string(3) "two" +} +bool(true) diff --git a/ext/standard/tests/file/copy_variation5-win32.phpt b/ext/standard/tests/file/copy_variation5-win32.phpt index d3f75262a1857..af352dbe07322 100644 --- a/ext/standard/tests/file/copy_variation5-win32.phpt +++ b/ext/standard/tests/file/copy_variation5-win32.phpt @@ -22,9 +22,9 @@ fclose($file_handle); $dest_files = array( /* Checking case sensitiveness */ - "COPY.tmp", - "COPY.TMP", - "CopY.TMP" + "COPY5.tmp", + "COPY5.TMP", + "CopY5.TMP" ); echo "Size of the source file before copy operation => "; @@ -80,25 +80,25 @@ Size of the source file before copy operation => int(1500) -- Iteration 1 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/COPY.tmp +Destination file name => %s/COPY5.tmp Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 2 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/COPY.TMP +Destination file name => %s/COPY5.TMP Size of source file => int(1500) Size of destination file => int(1500) -- Iteration 3 -- Copy operation => bool(true) Existence of destination file => bool(true) -Destination file name => %s/CopY.TMP +Destination file name => %s/CopY5.TMP Size of source file => int(1500) Size of destination file => int(1500) -Warning: unlink(%s/COPY.TMP): No such file or directory in %s on line %d +Warning: unlink(%s/COPY5.TMP): No such file or directory in %s on line %d -Warning: unlink(%s/CopY.TMP): No such file or directory in %s on line %d +Warning: unlink(%s/CopY5.TMP): No such file or directory in %s on line %d *** Done *** diff --git a/ext/standard/tests/file/file_put_contents_variation7.phpt b/ext/standard/tests/file/file_put_contents_variation7.phpt index 4e3d2e766f4dc..fe9cfe57894ac 100644 --- a/ext/standard/tests/file/file_put_contents_variation7.phpt +++ b/ext/standard/tests/file/file_put_contents_variation7.phpt @@ -2,6 +2,11 @@ Test file_put_contents() function : usage variation - various absolute and relative paths --CREDITS-- Dave Kelsey +--SKIPIF-- + --FILE-- foo()->x ??= 42; +?> +--EXPECTF-- +Notice: Only variable references should be returned by reference in %s on line %d + +Fatal error: Uncaught Error: Attempt to assign property "x" on null in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/main/main.c b/main/main.c index e8a24ca659fc1..b11c29dbda354 100644 --- a/main/main.c +++ b/main/main.c @@ -2085,8 +2085,9 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi _set_invalid_parameter_handler(old_invalid_parameter_handler); } - /* Disable the message box for assertions.*/ + /* Disable the message box for assertions and errors.*/ _CrtSetReportMode(_CRT_ASSERT, 0); + _CrtSetReportMode(_CRT_ERROR, 0); #endif #ifdef ZTS diff --git a/main/network.c b/main/network.c index d5fa851b4f0fd..d4938a4a08c1e 100644 --- a/main/network.c +++ b/main/network.c @@ -299,6 +299,35 @@ typedef int php_non_blocking_flags_t; fcntl(sock, F_SETFL, save) #endif +#if HAVE_GETTIMEOFDAY +/* Subtract times */ +static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result) +{ + result->tv_usec = a.tv_usec - b.tv_usec; + if (result->tv_usec < 0L) { + a.tv_sec--; + result->tv_usec += 1000000L; + } + result->tv_sec = a.tv_sec - b.tv_sec; + if (result->tv_sec < 0L) { + result->tv_sec++; + result->tv_usec -= 1000000L; + } +} + +static inline void php_network_set_limit_time(struct timeval *limit_time, + struct timeval *timeout) +{ + gettimeofday(limit_time, NULL); + limit_time->tv_sec += timeout->tv_sec; + limit_time->tv_usec += timeout->tv_usec; + if (limit_time->tv_usec >= 1000000) { + limit_time->tv_usec -= 1000000; + limit_time->tv_sec++; + } +} +#endif + /* Connect to a socket using an interruptible connect with optional timeout. * Optionally, the connect can be made asynchronously, which will implicitly * enable non-blocking mode on the socket. @@ -351,25 +380,53 @@ PHPAPI int php_network_connect_socket(php_socket_t sockfd, * expected when a connection is actively refused. This way, * php_pollfd_for will return a mask with POLLOUT if the connection * is successful and with POLLPRI otherwise. */ - if ((n = php_pollfd_for(sockfd, POLLOUT|POLLPRI, timeout)) == 0) { + int events = POLLOUT|POLLPRI; #else - if ((n = php_pollfd_for(sockfd, PHP_POLLREADABLE|POLLOUT, timeout)) == 0) { + int events = PHP_POLLREADABLE|POLLOUT; +#endif + struct timeval working_timeout; +#if HAVE_GETTIMEOFDAY + struct timeval limit_time, time_now; +#endif + if (timeout) { + memcpy(&working_timeout, timeout, sizeof(working_timeout)); +#if HAVE_GETTIMEOFDAY + php_network_set_limit_time(&limit_time, &working_timeout); #endif - error = PHP_TIMEOUT_ERROR_VALUE; } - if (n > 0) { - len = sizeof(error); - /* - BSD-derived systems set errno correctly - Solaris returns -1 from getsockopt in case of error - */ - if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char*)&error, &len) != 0) { + while (true) { + n = php_pollfd_for(sockfd, events, timeout ? &working_timeout : NULL); + if (n < 0) { + if (errno == EINTR) { +#if HAVE_GETTIMEOFDAY + if (timeout) { + gettimeofday(&time_now, NULL); + + if (!timercmp(&time_now, &limit_time, <)) { + /* time limit expired; no need for another poll */ + error = PHP_TIMEOUT_ERROR_VALUE; + break; + } else { + /* work out remaining time */ + sub_times(limit_time, time_now, &working_timeout); + } + } +#endif + continue; + } ret = -1; + } else if (n == 0) { + error = PHP_TIMEOUT_ERROR_VALUE; + } else { + len = sizeof(error); + /* BSD-derived systems set errno correctly. + * Solaris returns -1 from getsockopt in case of error. */ + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char*)&error, &len) != 0) { + ret = -1; + } } - } else { - /* whoops: sockfd has disappeared */ - ret = -1; + break; } ok: @@ -392,22 +449,6 @@ PHPAPI int php_network_connect_socket(php_socket_t sockfd, } /* }}} */ -/* {{{ sub_times */ -static inline void sub_times(struct timeval a, struct timeval b, struct timeval *result) -{ - result->tv_usec = a.tv_usec - b.tv_usec; - if (result->tv_usec < 0L) { - a.tv_sec--; - result->tv_usec += 1000000L; - } - result->tv_sec = a.tv_sec - b.tv_sec; - if (result->tv_sec < 0L) { - result->tv_sec++; - result->tv_usec -= 1000000L; - } -} -/* }}} */ - /* Bind to a local IP address. * Returns the bound socket, or -1 on failure. * */ @@ -777,7 +818,6 @@ PHPAPI php_socket_t php_network_accept_incoming(php_socket_t srvsock, } /* }}} */ - /* Connect to a remote host using an interruptible connect with optional timeout. * Optionally, the connect can be made asynchronously, which will implicitly * enable non-blocking mode on the socket. @@ -809,13 +849,7 @@ php_socket_t php_network_connect_socket_to_host(const char *host, unsigned short if (timeout) { memcpy(&working_timeout, timeout, sizeof(working_timeout)); #if HAVE_GETTIMEOFDAY - gettimeofday(&limit_time, NULL); - limit_time.tv_sec += working_timeout.tv_sec; - limit_time.tv_usec += working_timeout.tv_usec; - if (limit_time.tv_usec >= 1000000) { - limit_time.tv_usec -= 1000000; - limit_time.tv_sec++; - } + php_network_set_limit_time(&limit_time, &working_timeout); #endif } diff --git a/main/php_version.h b/main/php_version.h index ff2c38e0ceedc..40c5e60f2d610 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 3 -#define PHP_RELEASE_VERSION 14 +#define PHP_RELEASE_VERSION 15 #define PHP_EXTRA_VERSION "" -#define PHP_VERSION "8.3.14" -#define PHP_VERSION_ID 80314 +#define PHP_VERSION "8.3.15" +#define PHP_VERSION_ID 80315 diff --git a/main/rfc1867.c b/main/rfc1867.c index b1019d471561a..7a085bdfe50bd 100644 --- a/main/rfc1867.c +++ b/main/rfc1867.c @@ -317,8 +317,8 @@ static char *next_line(multipart_buffer *self) } /* return entire buffer as a partial line */ line[self->bufsize] = 0; - self->buf_begin = ptr; self->bytes_in_buffer = 0; + /* Let fill_buffer() handle the reset of self->buf_begin */ } return line; diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c index e9b6ce2d95cdb..80be5fb6f476e 100644 --- a/sapi/fpm/fpm/fpm_status.c +++ b/sapi/fpm/fpm/fpm_status.c @@ -62,7 +62,8 @@ int fpm_status_export_to_zval(zval *status) /* copy the scoreboard not to bother other processes */ scoreboard = *scoreboard_p; - struct fpm_scoreboard_proc_s procs[scoreboard.nprocs]; + struct fpm_scoreboard_proc_s *procs = safe_emalloc( + sizeof(struct fpm_scoreboard_proc_s), scoreboard.nprocs, 0); struct fpm_scoreboard_proc_s *proc_p; for(i=0; i +--FILE-- +createSourceFileAndScriptName(); +$tester->start(); +$tester->expectLogStartNotices(); +$tester->request()->expectBody('bool(false)'); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/gh16628.phpt b/sapi/fpm/tests/gh16628.phpt index e2df0c8cb84de..b160bb180ffb1 100644 --- a/sapi/fpm/tests/gh16628.phpt +++ b/sapi/fpm/tests/gh16628.phpt @@ -32,7 +32,7 @@ for ($i = 1; $i < 100; $i++) { EOT; $tester = new FPM\Tester($cfg, $code); -$tester->start(); +$tester->start(extensions: ['zend_test']); $tester->expectLogStartNotices(); $tester->request()->expectEmptyBody(); for ($i = 1; $i < 100; $i++) { diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c index a58385456b04d..9bedff03af202 100644 --- a/sapi/phpdbg/phpdbg.c +++ b/sapi/phpdbg/phpdbg.c @@ -369,6 +369,7 @@ PHP_FUNCTION(phpdbg_clear) zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_OPLINE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]); + zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]); zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]); } /* }}} */ diff --git a/sapi/phpdbg/tests/gh15208.phpt b/sapi/phpdbg/tests/gh15208.phpt new file mode 100644 index 0000000000000..4fa63a61c5262 --- /dev/null +++ b/sapi/phpdbg/tests/gh15208.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-15208 (Segfault with breakpoint map and phpdbg_clear()) +--PHPDBG-- +r +q +--FILE-- + +--EXPECTF-- +[Successful compilation of %s] +prompt> [Breakpoint #0 added at foo::bar] +[Script ended normally] +prompt> diff --git a/scripts/dev/credits b/scripts/dev/credits index e59cc109b88d7..783bc5f18f79d 100755 --- a/scripts/dev/credits +++ b/scripts/dev/credits @@ -3,7 +3,7 @@ # Generate credits_*.h headers from the ext/*/CREDITS and sapi/*/CREDITS files. # Go to project root directory -cd $(CDPATH= cd -- "$(dirname -- "$0")/../../" && pwd -P) +cd "$(CDPATH='' cd -- "$(dirname -- "$0")/../../" && pwd -P)" || exit awkprog=' BEGIN { FS = "\n|\r\n|\r"; RS = "" } diff --git a/scripts/dev/genfiles b/scripts/dev/genfiles index 3e085c3e5397f..32b4c8851e2d1 100755 --- a/scripts/dev/genfiles +++ b/scripts/dev/genfiles @@ -41,7 +41,7 @@ SED=${SED:-sed} MAKE=${MAKE:-make} # Go to project root. -cd $(CDPATH= cd -- "$(dirname -- "$0")/../../" && pwd -P) +cd "$(CDPATH='' cd -- "$(dirname -- "$0")/../../" && pwd -P)" || exit # Check required bison version from the configure.ac file. required_bison_version=$($SED -n 's/PHP_PROG_BISON(\[\([0-9\.]*\)\].*/\1/p' configure.ac) diff --git a/scripts/dev/makedist b/scripts/dev/makedist index ffdf536907651..c9ad6059004be 100755 --- a/scripts/dev/makedist +++ b/scripts/dev/makedist @@ -15,7 +15,7 @@ if [[ $($tar --version) == *"bsdtar"* ]]; then fi # Go to project root directory. -cd $(CDPATH= cd -- "$(dirname -- "$0")/../../" && pwd -P) +cd "$(CDPATH='' cd -- "$(dirname -- "$0")/../../" && pwd -P)" || exit # Process options and arguments. while :; do diff --git a/tests/basic/gh16998.phpt b/tests/basic/gh16998.phpt new file mode 100644 index 0000000000000..8bfcbbda99dd0 --- /dev/null +++ b/tests/basic/gh16998.phpt @@ -0,0 +1,49 @@ +--TEST-- +GH-16998 (UBSAN warning in rfc1867) +--SKIPIF-- + +--FILE-- + '1', + 'CONTENT_TYPE' => "multipart/form-data; boundary=", + 'CONTENT_LENGTH' => strlen($body), + 'REQUEST_METHOD' => 'POST', + 'SCRIPT_FILENAME' => __DIR__ . '/GHSA-9pqp-7h25-4f32.inc', +]); +$spec = [ + 0 => ['pipe', 'r'], + 1 => STDOUT, + 2 => STDOUT, +]; +$pipes = []; +$handle = proc_open($cmd, $spec, $pipes, getcwd(), $env); +fwrite($pipes[0], $body); +proc_close($handle); +?> +--EXPECTF-- +X-Powered-By: PHP/%s +Content-type: text/html; charset=UTF-8 + +Hello world +array(0) { +}