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) {
+}