diff --git a/.cirrus.yml b/.cirrus.yml index 55632482a71fc..8536d17596631 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -4,7 +4,7 @@ env: freebsd_task: name: FREEBSD_DEBUG_NTS freebsd_instance: - image_family: freebsd-13-2 + image_family: freebsd-13-3 env: ARCH: amd64 install_script: @@ -14,7 +14,7 @@ freebsd_task: - 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 --enable-werror --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d + - ./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 diff --git a/CODEOWNERS b/.github/CODEOWNERS similarity index 91% rename from CODEOWNERS rename to .github/CODEOWNERS index 40ec5651f1c59..9f631ccf19185 100644 --- a/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,7 +8,6 @@ # For more information, see the GitHub CODEOWNERS documentation: # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners -*.stub.php @kocsismate /.github @iluuu1994 @TimWolla /build/gen_stub.php @kocsismate /ext/bcmath @Girgias @@ -17,19 +16,26 @@ /ext/dba @Girgias /ext/dom @nielsdos /ext/ffi @dstogov +/ext/gettext @devnexen /ext/gmp @Girgias /ext/imap @Girgias +/ext/intl @devnexen /ext/json @bukka /ext/libxml @nielsdos /ext/mbstring @alexdowad +/ext/odbc @NattyNarwhal /ext/opcache @dstogov @iluuu1994 /ext/openssl @bukka +/ext/pdo_odbc @NattyNarwhal +/ext/pdo_pgsql @devnexen /ext/pgsql @devnexen /ext/random @TimWolla @zeriyoshi /ext/session @Girgias /ext/sockets @devnexen /ext/spl @Girgias /ext/standard @bukka +/ext/xmlreader @nielsdos +/ext/xsl @nielsdos /main @bukka /sapi/fpm @bukka /Zend @iluuu1994 @@ -53,3 +59,4 @@ /Zend/zend_type*.h @dstogov /Zend/zend_variables.* @dstogov /Zend/zend_vm* @dstogov +*.stub.php @kocsismate diff --git a/.github/actions/brew/action.yml b/.github/actions/brew/action.yml index 51d37aa56d470..5868d3917b80c 100644 --- a/.github/actions/brew/action.yml +++ b/.github/actions/brew/action.yml @@ -5,6 +5,12 @@ runs: - shell: bash run: | set -x + + # Patch brew to overwrite always + formula_installer="$(brew --repo)"/Library/Homebrew/formula_installer.rb + code=" keg.link\(verbose: verbose\?" + sudo sed -Ei '' "s/$code.*/$code, overwrite: true\)/" "$formula_installer" + brew install \ pkg-config \ autoconf \ diff --git a/.github/actions/test-libmysqlclient/action.yml b/.github/actions/test-libmysqlclient/action.yml index e408d0608f1c3..74474225e6705 100644 --- a/.github/actions/test-libmysqlclient/action.yml +++ b/.github/actions/test-libmysqlclient/action.yml @@ -14,7 +14,6 @@ runs: export PDO_MYSQL_TEST_HOST=127.0.0.1 export PDO_MYSQL_TEST_USER=root export PDO_MYSQL_TEST_PASS=root - export REPORT_EXIT_STATUS=no sapi/cli/php run-tests.php -P -q \ -g FAIL,BORK,LEAK,XLEAK \ --no-progress --offline --show-diff --show-slow 1000 --set-timeout 120 \ diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index eb8c4929a1305..58e32a2a847a3 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -72,7 +72,7 @@ jobs: zts: true asan: true name: "LINUX_X64_${{ matrix.debug && 'DEBUG' || 'RELEASE' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}${{ matrix.asan && '_ASAN' || '' }}" - runs-on: ubuntu-22.04 + runs-on: ubuntu-${{ !matrix.asan && '22' || '20' }}.04 container: image: ${{ matrix.asan && 'ubuntu:23.04' || null }} steps: diff --git a/NEWS b/NEWS index 70894828371bd..c5d4f776956b1 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,72 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.4 +11 Apr 2024, PHP 8.3.5 + +- Core: + . Fixed GH-13569 (GC buffer unnecessarily grows up to GC_MAX_BUF_SIZE when + scanning WeakMaps). (Arnaud) + . Fixed bug GH-13612 (Corrupted memory in destructor with weak references). + (nielsdos) + . Fixed bug GH-13446 (Restore exception handler after it finishes). (ilutov) + . Fixed bug GH-13784 (AX_GCC_FUNC_ATTRIBUTE failure). (Remi) + . Fixed bug GH-13670 (GC does not scale well with a lot of objects created in + destructor). (Arnaud) + +- DOM: + . Add some missing ZPP checks. (nielsdos) + . Fix potential memory leak in XPath evaluation results. (nielsdos) + +- FPM: + . Fixed GH-11086 (FPM: config test runs twice in daemonised mode). + (Jakub Zelenka) + . Fix incorrect check in fpm_shm_free(). (nielsdos) + +- GD: + . Fixed bug GH-12019 (add GDLIB_CFLAGS in feature tests). (Michael Orlitzky) + +- Gettext: + . Fixed sigabrt raised with dcgettext/dcngettext calls with gettext 0.22.5 + with category set to LC_ALL. (David Carlier) + +- MySQLnd: + . Fix GH-13452 (Fixed handshake response [mysqlnd]). (Saki Takamachi) + . Fix incorrect charset length in check_mb_eucjpms(). (nielsdos) + +- Opcache: + . Fixed GH-13508 (JITed QM_ASSIGN may be optimized out when op1 is null). + (Arnaud, Dmitry) + . Fixed GH-13712 (Segmentation fault for enabled observers when calling trait + method of internal trait when opcache is loaded). (Bob) + +- Random: + . Fixed bug GH-13544 (Pre-PHP 8.2 compatibility for mt_srand with unknown + modes). (timwolla) + . Fixed bug GH-13690 (Global Mt19937 is not properly reset in-between + requests when MT_RAND_PHP is used). (timwolla) + +- Session: + . Fixed bug GH-13680 (Segfault with session_decode and compilation error). + (nielsdos) + +- SPL: + . Fixed bug GH-13685 (Unexpected null pointer in zend_string.h). (nielsdos) + +- Standard: + . Fixed bug GH-11808 (Live filesystem modified by tests). (nielsdos) + . Fixed GH-13402 (Added validation of `\n` in $additional_headers of mail()). + (SakiTakamachi) + . Fixed bug GH-13203 (file_put_contents fail on strings over 4GB on Windows). + (divinity76) + . Fixed bug GHSA-pc52-254m-w9w7 (Command injection via array-ish $command + parameter of proc_open). (CVE-2024-1874) (Jakub Zelenka) + . Fixed bug GHSA-wpj3-hf5j-x4v4 (__Host-/__Secure- cookie bypass due to + partial CVE-2022-31629 fix). (CVE-2024-2756) (nielsdos) + . Fixed bug GHSA-h746-cjrr-wfmr (password_verify can erroneously return true, + opening ATO risk). (CVE-2024-3096) (Jakub Zelenka) + Fixed bug GHSA-fjp9-9hwx-59fq (mb_encode_mimeheader runs endlessly for some + inputs). (CVE-2024-2757) (Alex Dowad) + +14 Mar 2024, PHP 8.3.4 - Core: . Fix ZTS persistent resource crashes on shutdown. (nielsdos) diff --git a/Zend/tests/gh13446_1.phpt b/Zend/tests/gh13446_1.phpt new file mode 100644 index 0000000000000..cb51d041c969f --- /dev/null +++ b/Zend/tests/gh13446_1.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-13446: Exception handler is restored after is has finished +--FILE-- +getMessage(), "\n"; +} +set_exception_handler('exception_handler'); + +register_shutdown_function(function () { + echo set_exception_handler(null), "\n"; + restore_exception_handler(); +}); + +throw new Exception('Test'); +?> +--EXPECT-- +Exception caught: Test +exception_handler diff --git a/Zend/tests/gh13446_2.phpt b/Zend/tests/gh13446_2.phpt new file mode 100644 index 0000000000000..c1bab44ce80bf --- /dev/null +++ b/Zend/tests/gh13446_2.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-13446: Exception handler attempting to free itself +--FILE-- + +--EXPECT-- +object(stdClass)#1 (0) { +} diff --git a/Zend/tests/gh13446_3.phpt b/Zend/tests/gh13446_3.phpt new file mode 100644 index 0000000000000..eb56eb1b32d89 --- /dev/null +++ b/Zend/tests/gh13446_3.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-13446: Exception handler isn't restored if it is previously modified +--FILE-- + +--EXPECT-- +Handler 1 +exception_handler_2 diff --git a/Zend/tests/gh13446_4.phpt b/Zend/tests/gh13446_4.phpt new file mode 100644 index 0000000000000..634d16f38a0a3 --- /dev/null +++ b/Zend/tests/gh13446_4.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-13446: Exception handler isn't restored if stack is empty +--FILE-- + +--EXPECT-- +Handler +NULL diff --git a/Zend/tests/gh13569.phpt b/Zend/tests/gh13569.phpt new file mode 100644 index 0000000000000..8e5f74d666659 --- /dev/null +++ b/Zend/tests/gh13569.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-13569: GC buffer grows up to GC_MAX_BUF_SIZE when scanning WeakMaps +--FILE-- + +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/tests/gh13670_001.phpt b/Zend/tests/gh13670_001.phpt new file mode 100644 index 0000000000000..f1cd32ea8bc0e --- /dev/null +++ b/Zend/tests/gh13670_001.phpt @@ -0,0 +1,48 @@ +--TEST-- +GH-13670 001 +--SKIPIF-- + +--FILE-- +self = $this; + } + public function __destruct() { + global $shutdown; + if (!$shutdown) { + new Cycle(); + } + } +} + +$defaultThreshold = gc_status()['threshold']; +for ($i = 0; $i < $defaultThreshold+1; $i++) { + new Cycle(); +} + +$objs = []; +for ($i = 0; $i < 100; $i++) { + $obj = new stdClass; + $objs[] = $obj; +} + +$st = gc_status(); + +if ($st['runs'] > 10) { + var_dump($st); +} +?> +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/tests/gh13670_002.phpt b/Zend/tests/gh13670_002.phpt new file mode 100644 index 0000000000000..3ae0456e7aae9 --- /dev/null +++ b/Zend/tests/gh13670_002.phpt @@ -0,0 +1,71 @@ +--TEST-- +GH-13670 002 +--SKIPIF-- + +--FILE-- +self = $this; + } +} + +class Canary { + public $self; + public function __construct() { + $this->self = $this; + } + public function __destruct() { + global $shutdown; + if (!$shutdown) { + work(); + } + } +} + +function work() { + global $objs, $defaultThreshold; + new Canary(); + // Create some collectable garbage so the next run will not adjust + // threshold + for ($i = 0; $i < 100; $i++) { + new Cycle(); + } + // Add potential garbage to buffer + foreach (array_slice($objs, 0, $defaultThreshold) as $obj) { + $o = $obj; + } +} + +$defaultThreshold = gc_status()['threshold']; +$objs = []; +for ($i = 0; $i < $defaultThreshold*2; $i++) { + $obj = new stdClass; + $objs[] = $obj; +} + +work(); + +foreach ($objs as $obj) { + $o = $obj; +} + +$st = gc_status(); + +if ($st['runs'] > 10) { + var_dump($st); +} +?> +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/tests/gh13670_003.phpt b/Zend/tests/gh13670_003.phpt new file mode 100644 index 0000000000000..e8cbb701ae0b5 --- /dev/null +++ b/Zend/tests/gh13670_003.phpt @@ -0,0 +1,73 @@ +--TEST-- +GH-13670 003 +--SKIPIF-- + +--FILE-- +self = $this; + } +} + +class Canary { + public $self; + public function __construct() { + $this->self = $this; + } + public function __destruct() { + global $shutdown; + if (!$shutdown) { + work(); + } + } +} + +function work() { + global $objs, $defaultThreshold; + new Canary(); + // Create some collectable garbage so the next run will not adjust + // threshold + for ($i = 0; $i < 100; $i++) { + new Cycle(); + } + // Add potential garbage to buffer + foreach (array_slice($objs, 0, $defaultThreshold) as $obj) { + $o = $obj; + } +} + +$defaultThreshold = gc_status()['threshold']; +$objs = []; +for ($i = 0; $i < $defaultThreshold*2; $i++) { + $obj = new stdClass; + $objs[] = $obj; +} + +work(); + +// Result of array_slice() is a tmpvar that will be checked by +// zend_gc_check_root_tmpvars() +foreach (array_slice($objs, -10) as $obj) { + $o = $obj; +} + +$st = gc_status(); + +if ($st['runs'] > 10) { + var_dump($st); +} +?> +==DONE== +--EXPECT-- +==DONE== diff --git a/Zend/tests/weakrefs/gh13612.phpt b/Zend/tests/weakrefs/gh13612.phpt new file mode 100644 index 0000000000000..4ca4c92508765 --- /dev/null +++ b/Zend/tests/weakrefs/gh13612.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-13612 (Corrupted memory in destructor with weak references) +--FILE-- +weakAnalysingMap = \WeakReference::create($analysingMap); + } + + public function __destruct() + { + var_dump($this->weakAnalysingMap->get()); + } + }; + + $this->destroyed[] = 1; + $this->ownerDestructorHandlers[] = $handler; + } +} + +new WeakAnalysingMapRepro(); + +echo "Done\n"; + +?> +--EXPECT-- +NULL +Done diff --git a/Zend/zend.c b/Zend/zend.c index d154c67382a72..28922647bba1d 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1839,7 +1839,9 @@ ZEND_API ZEND_COLD void zend_user_exception_handler(void) /* {{{ */ old_exception = EG(exception); EG(exception) = NULL; ZVAL_OBJ(¶ms[0], old_exception); + ZVAL_COPY_VALUE(&orig_user_exception_handler, &EG(user_exception_handler)); + zend_stack_push(&EG(user_exception_handlers), &orig_user_exception_handler); ZVAL_UNDEF(&EG(user_exception_handler)); if (call_user_function(CG(function_table), NULL, &orig_user_exception_handler, &retval2, 1, params) == SUCCESS) { @@ -1853,7 +1855,13 @@ ZEND_API ZEND_COLD void zend_user_exception_handler(void) /* {{{ */ EG(exception) = old_exception; } - zval_ptr_dtor(&orig_user_exception_handler); + if (Z_TYPE(EG(user_exception_handler)) == IS_UNDEF) { + zval *tmp = zend_stack_top(&EG(user_exception_handlers)); + if (tmp) { + ZVAL_COPY_VALUE(&EG(user_exception_handler), tmp); + zend_stack_del_top(&EG(user_exception_handlers)); + } + } } /* }}} */ ZEND_API zend_result zend_execute_scripts(int type, zval *retval, int file_count, ...) /* {{{ */ diff --git a/Zend/zend.h b/Zend/zend.h index 0d574976542bf..3afafa2ca6161 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.4-dev" +#define ZEND_VERSION "4.3.5" #define ZEND_ENGINE_3 diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index 4051dd82f8a85..3016ff8a1af04 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -608,7 +608,7 @@ static void gc_adjust_threshold(int count) /* TODO Very simple heuristic for dynamic GC buffer resizing: * If there are "too few" collections, increase the collection threshold * by a fixed step */ - if (count < GC_THRESHOLD_TRIGGER) { + if (count < GC_THRESHOLD_TRIGGER || GC_G(num_roots) >= GC_G(gc_threshold)) { /* increase */ if (GC_G(gc_threshold) < GC_THRESHOLD_MAX) { new_threshold = GC_G(gc_threshold) + GC_THRESHOLD_STEP; @@ -718,7 +718,7 @@ static void ZEND_FASTCALL gc_extra_root(zend_refcounted *ref) if (EXPECTED(GC_HAS_UNUSED())) { idx = GC_FETCH_UNUSED(); - } else if (EXPECTED(GC_HAS_NEXT_UNUSED_UNDER_THRESHOLD())) { + } else if (EXPECTED(GC_HAS_NEXT_UNUSED())) { idx = GC_FETCH_NEXT_UNUSED(); } else { gc_grow_root_buffer(); @@ -1990,7 +1990,13 @@ ZEND_API int zend_gc_collect_cycles(void) finish: zend_get_gc_buffer_release(); + + /* Prevent GC from running during zend_gc_check_root_tmpvars, before + * gc_threshold is adjusted, as this may result in unbounded recursion */ + GC_G(gc_active) = 1; zend_gc_check_root_tmpvars(); + GC_G(gc_active) = 0; + GC_G(collector_time) += zend_hrtime() - start_time; return total_count; } diff --git a/Zend/zend_hrtime.c b/Zend/zend_hrtime.c index a28ba3f470508..bcf11964f1cea 100644 --- a/Zend/zend_hrtime.c +++ b/Zend/zend_hrtime.c @@ -31,13 +31,13 @@ # define WIN32_LEAN_AND_MEAN -double zend_hrtime_timer_scale = .0; +ZEND_API double zend_hrtime_timer_scale = .0; #elif ZEND_HRTIME_PLATFORM_APPLE # include # include -mach_timebase_info_data_t zend_hrtime_timerlib_info = { +ZEND_API mach_timebase_info_data_t zend_hrtime_timerlib_info = { .numer = 0, .denom = 1, }; diff --git a/Zend/zend_hrtime.h b/Zend/zend_hrtime.h index 6bba076738a91..1449c4e443cf2 100644 --- a/Zend/zend_hrtime.h +++ b/Zend/zend_hrtime.h @@ -60,13 +60,13 @@ BEGIN_EXTERN_C() #if ZEND_HRTIME_PLATFORM_WINDOWS -extern double zend_hrtime_timer_scale; +ZEND_API extern double zend_hrtime_timer_scale; #elif ZEND_HRTIME_PLATFORM_APPLE # include # include -extern mach_timebase_info_data_t zend_hrtime_timerlib_info; +ZEND_API extern mach_timebase_info_data_t zend_hrtime_timerlib_info; #endif diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index ea8cc219a23a3..7f05db05b83ef 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1993,9 +1993,9 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ } else { new_fn = zend_arena_alloc(&CG(arena), sizeof(zend_op_array)); memcpy(new_fn, fn, sizeof(zend_op_array)); - new_fn->op_array.fn_flags |= ZEND_ACC_TRAIT_CLONE; new_fn->op_array.fn_flags &= ~ZEND_ACC_IMMUTABLE; } + new_fn->common.fn_flags |= ZEND_ACC_TRAIT_CLONE; /* Reassign method name, in case it is an alias. */ new_fn->common.function_name = name; diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 4c4b3cf30c13d..af4d1f265897a 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -50,6 +50,10 @@ ZEND_API void zend_object_std_dtor(zend_object *object) { zval *p, *end; + if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) { + zend_weakrefs_notify(object); + } + if (object->properties) { if (EXPECTED(!(GC_FLAGS(object->properties) & IS_ARRAY_IMMUTABLE))) { if (EXPECTED(GC_DELREF(object->properties) == 0) @@ -88,10 +92,6 @@ ZEND_API void zend_object_std_dtor(zend_object *object) FREE_HASHTABLE(guards); } } - - if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) { - zend_weakrefs_notify(object); - } } ZEND_API void zend_objects_destroy_object(zend_object *object) diff --git a/Zend/zend_signal.c b/Zend/zend_signal.c index f6ca3f40a3e57..2b7f6f173c1f9 100644 --- a/Zend/zend_signal.c +++ b/Zend/zend_signal.c @@ -183,8 +183,7 @@ static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context) zend_signal_entry_t p_sig; #ifdef ZTS if (tsrm_is_shutdown() || !tsrm_is_managed_thread()) { - p_sig.flags = 0; - p_sig.handler = SIG_DFL; + p_sig = global_orig_handlers[signo-1]; } else #endif p_sig = SIGG(handlers)[signo-1]; diff --git a/build/ax_gcc_func_attribute.m4 b/build/ax_gcc_func_attribute.m4 index 79478f56b8bfd..79f3eef82bc06 100644 --- a/build/ax_gcc_func_attribute.m4 +++ b/build/ax_gcc_func_attribute.m4 @@ -216,7 +216,7 @@ AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ static int bar( void ) __attribute__(($1("foo"))); ], [target], [ - static int bar( void ) __attribute__(($1("sse2"))); + int bar( void ) __attribute__(($1("sse2"))); ], [ m4_warn([syntax], [Unsupported attribute $1, the test may fail]) diff --git a/build/libtool.m4 b/build/libtool.m4 index 8ee7b453010f1..5b518d50e4ef5 100644 --- a/build/libtool.m4 +++ b/build/libtool.m4 @@ -277,7 +277,7 @@ dnl This sometimes fails to find confdefs.h, for some reason. dnl [#]line __oline__ "[$]0" [#]line __oline__ "configure" #include "confdefs.h" -int main() { +int main(void) { ; return 0; } EOF if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then @@ -978,8 +978,8 @@ else # endif #endif -void fnord() { int i=42;} -int main () +void fnord(void) { int i=42;} +int main (void) { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; @@ -2750,7 +2750,7 @@ _LT_AC_TAGVAR(objext, $1)=$objext lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests -lt_simple_link_test_code='int main(){return(0);}' +lt_simple_link_test_code='int main(void){return(0);}' _LT_AC_SYS_COMPILER @@ -4640,7 +4640,7 @@ void nm_test_func(){} #ifdef __cplusplus } #endif -int main(){nm_test_var='a';nm_test_func();return(0);} +int main(void){nm_test_var='a';nm_test_func();return(0);} EOF if AC_TRY_EVAL(ac_compile); then diff --git a/build/ltmain.sh b/build/ltmain.sh index 2f1c8c9dc80f0..ff1685c8a89f9 100755 --- a/build/ltmain.sh +++ b/build/ltmain.sh @@ -3598,7 +3598,7 @@ EOF # whether they linked in statically or dynamically with ldd. $rm conftest.c cat > conftest.c <]], [[struct tm *(*func)() = localtime_r]])],[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[struct tm *(*func)(void) = localtime_r]])],[ : ],[ AC_DEFINE(MISSING_LOCALTIME_R_DECL,1,[Whether localtime_r is declared]) ]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[struct tm *(*func)() = gmtime_r]])],[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[struct tm *(*func)(void) = gmtime_r]])],[ : ],[ AC_DEFINE(MISSING_GMTIME_R_DECL,1,[Whether gmtime_r is declared]) ]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[char *(*func)() = asctime_r]])],[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[char *(*func)(void) = asctime_r]])],[ : ],[ AC_DEFINE(MISSING_ASCTIME_R_DECL,1,[Whether asctime_r is declared]) ]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[char *(*func)() = ctime_r]])],[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[char *(*func)(void) = ctime_r]])],[ : ],[ AC_DEFINE(MISSING_CTIME_R_DECL,1,[Whether ctime_r is declared]) ]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[char *(*func)() = strtok_r]])],[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[char *(*func)(void) = strtok_r]])],[ : ],[ AC_DEFINE(MISSING_STRTOK_R_DECL,1,[Whether strtok_r is declared]) @@ -1315,7 +1315,7 @@ dnl See if we have broken header files like SunOS has. dnl AC_DEFUN([PHP_MISSING_FCLOSE_DECL],[ AC_MSG_CHECKING([for fclose declaration]) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int (*func)() = fclose]])],[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int (*func)(void) = fclose]])],[ AC_DEFINE(MISSING_FCLOSE_DECL,0,[ ]) AC_MSG_RESULT([ok]) ],[ @@ -1643,7 +1643,7 @@ AC_DEFUN([PHP_TEST_BUILD], [ LIBS="$4 $LIBS" AC_LINK_IFELSE([AC_LANG_SOURCE([[ $5 - char $1(); + char $1(void); int main(void) { $1(); return 0; diff --git a/configure.ac b/configure.ac index 12383bfea1a9c..ac90dd9c10c88 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.3.4-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.5],[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 @@ -1135,7 +1135,7 @@ case $host_alias in save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-zcommon-page-size=2097152 -Wl,-zmax-page-size=2097152" AC_RUN_IFELSE( - [AC_LANG_SOURCE([[int main() {return 0;}]])], + [AC_LANG_PROGRAM()], [ac_cv_common_page_size=yes], [ac_cv_common_page_size=no], [ac_cv_common_page_size=no]) @@ -1149,7 +1149,7 @@ case $host_alias in save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-zmax-page-size=2097152" AC_RUN_IFELSE( - [AC_LANG_SOURCE([[int main() {return 0;}]])], + [AC_LANG_PROGRAM()], [ac_cv_max_page_size=yes], [ac_cv_max_page_size=no], [ac_cv_max_page_size=no]) diff --git a/ext/curl/tests/curl_setopt_ssl.phpt b/ext/curl/tests/curl_setopt_ssl.phpt index 2e28466a84057..11d8fff702a88 100644 --- a/ext/curl/tests/curl_setopt_ssl.phpt +++ b/ext/curl/tests/curl_setopt_ssl.phpt @@ -9,6 +9,7 @@ exec('openssl version', $out, $code); if ($code > 0) die("skip couldn't locate openssl binary"); if (PHP_OS_FAMILY === 'Windows') die('skip not for Windows'); if (PHP_OS_FAMILY === 'Darwin') die('skip Fails intermittently on macOS'); +if (PHP_OS === 'FreeBSD') die('skip proc_open seems to be stuck on FreeBSD'); $curl_version = curl_version(); if ($curl_version['version_number'] < 0x074700) { die("skip: blob options not supported for curl < 7.71.0"); @@ -237,4 +238,4 @@ bool(true) bool(true) bool(true) client cert subject not in response -CURL ERROR: 83 \ No newline at end of file +CURL ERROR: 83 diff --git a/ext/dom/node.c b/ext/dom/node.c index b95bf6c40fb4c..43f03f5e96b66 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -2066,12 +2066,20 @@ PHP_METHOD(DOMNode, getRootNode) PHP_METHOD(DOMNode, __sleep) { + if (zend_parse_parameters_none() != SUCCESS) { + RETURN_THROWS(); + } + zend_throw_exception_ex(NULL, 0, "Serialization of '%s' is not allowed, unless serialization methods are implemented in a subclass", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name)); RETURN_THROWS(); } PHP_METHOD(DOMNode, __wakeup) { + if (zend_parse_parameters_none() != SUCCESS) { + RETURN_THROWS(); + } + zend_throw_exception_ex(NULL, 0, "Unserialization of '%s' is not allowed, unless unserialization methods are implemented in a subclass", ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name)); RETURN_THROWS(); } diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index 73ceb49362781..22a7e0ecc5a91 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -125,8 +125,11 @@ static void dom_xpath_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, } } break; - default: - ZVAL_STRING(&fci.params[i], (char *)xmlXPathCastToString(obj)); + default: { + str = (char *)xmlXPathCastToString(obj); + ZVAL_STRING(&fci.params[i], str); + xmlFree(str); + } } xmlXPathFreeObject(obj); } diff --git a/ext/gd/config.m4 b/ext/gd/config.m4 index 6ca36fae1dcee..b31c9b2c0bdf7 100644 --- a/ext/gd/config.m4 +++ b/ext/gd/config.m4 @@ -150,6 +150,8 @@ dnl or run test insufficient. AC_DEFUN([PHP_GD_CHECK_FORMAT],[ old_LIBS="${LIBS}" LIBS="${LIBS} ${GD_SHARED_LIBADD}" + old_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} ${GDLIB_CFLAGS}" AC_MSG_CHECKING([for working gdImageCreateFrom$1 in libgd]) AC_LANG_PUSH([C]) AC_RUN_IFELSE([AC_LANG_SOURCE([ @@ -180,6 +182,7 @@ int main(int argc, char** argv) { AC_MSG_RESULT([no]) ]) AC_LANG_POP([C]) + CFLAGS="${old_CFLAGS}" LIBS="${old_LIBS}" ]) diff --git a/ext/gettext/gettext.c b/ext/gettext/gettext.c index 05f41552c63fc..15af3cb9b57ff 100644 --- a/ext/gettext/gettext.c +++ b/ext/gettext/gettext.c @@ -23,6 +23,7 @@ #ifdef HAVE_LIBINTL #include +#include #include "ext/standard/info.h" #include "php_gettext.h" #include "gettext_arginfo.h" @@ -146,6 +147,9 @@ PHP_FUNCTION(dcgettext) PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain)) PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid)) + if (category == LC_ALL) { + RETURN_STR_COPY(msgid); + } msgstr = dcgettext(ZSTR_VAL(domain), ZSTR_VAL(msgid), category); @@ -260,6 +264,9 @@ PHP_FUNCTION(dcngettext) PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len) PHP_GETTEXT_LENGTH_CHECK(2, msgid1_len) PHP_GETTEXT_LENGTH_CHECK(3, msgid2_len) + if (category == LC_ALL) { + RETURN_STRING(msgid1); + } msgstr = dcngettext(domain, msgid1, msgid2, count, category); diff --git a/ext/gettext/tests/dcgettext_lcall.phpt b/ext/gettext/tests/dcgettext_lcall.phpt new file mode 100644 index 0000000000000..f44ff29884639 --- /dev/null +++ b/ext/gettext/tests/dcgettext_lcall.phpt @@ -0,0 +1,12 @@ +--TEST-- +dcgettext with LC_ALL is undefined behavior. +--EXTENSIONS-- +gettext +--FILE-- + +--EXPECT-- +string(4) "item" +string(4) "item" diff --git a/ext/gettext/tests/dcngettext.phpt b/ext/gettext/tests/dcngettext.phpt index 715281ec8296b..2f356b1cf0c6a 100644 --- a/ext/gettext/tests/dcngettext.phpt +++ b/ext/gettext/tests/dcngettext.phpt @@ -11,10 +11,10 @@ if (!function_exists("dcngettext")) die("skip dcngettext() doesn't exist"); var_dump(dcngettext(1,1,1,1,1)); var_dump(dcngettext("test","test","test",1,1)); -var_dump(dcngettext("test","test","test",0,0)); +var_dump(dcngettext("test","test","test",0,1)); var_dump(dcngettext("test","test","test",-1,-1)); var_dump(dcngettext("","","",1,1)); -var_dump(dcngettext("","","",0,0)); +var_dump(dcngettext("","","",0,1)); echo "Done\n"; ?> diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index cf4c1c32c5915..618fff55362e1 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -5858,6 +5858,9 @@ static zend_string* mb_mime_header_encode(zend_string *input, const mbfl_encodin unsigned char *in = (unsigned char*)ZSTR_VAL(input); size_t in_len = ZSTR_LEN(input); + ZEND_ASSERT(outcode->mime_name != NULL); + ZEND_ASSERT(outcode->mime_name[0] != '\0'); + if (!in_len) { return zend_empty_string; } @@ -5880,7 +5883,8 @@ static zend_string* mb_mime_header_encode(zend_string *input, const mbfl_encodin unsigned int state = 0; /* wchar_buf should be big enough that when it is full, we definitely have enough * wchars to fill an entire line of output */ - uint32_t wchar_buf[80]; + const size_t wchar_buf_len = 90; + uint32_t wchar_buf[wchar_buf_len]; uint32_t *p, *e; /* What part of wchar_buf is filled with still-unprocessed data which should not * be overwritten? */ @@ -5891,7 +5895,7 @@ static zend_string* mb_mime_header_encode(zend_string *input, const mbfl_encodin * spaces), just pass it through unchanged */ bool checking_leading_spaces = true; while (in_len) { - size_t out_len = incode->to_wchar(&in, &in_len, wchar_buf, 80, &state); + size_t out_len = incode->to_wchar(&in, &in_len, wchar_buf, wchar_buf_len, &state); p = wchar_buf; e = wchar_buf + out_len; @@ -5925,9 +5929,9 @@ no_passthrough: ; * do so all the way to the end of the string */ while (in_len) { /* Decode part of the input string, refill wchar_buf */ - ZEND_ASSERT(offset < 80); - size_t out_len = incode->to_wchar(&in, &in_len, wchar_buf + offset, 80 - offset, &state); - ZEND_ASSERT(out_len <= 80 - offset); + ZEND_ASSERT(offset + MBSTRING_MIN_WCHAR_BUFSIZE <= wchar_buf_len); + size_t out_len = incode->to_wchar(&in, &in_len, wchar_buf + offset, wchar_buf_len - offset, &state); + ZEND_ASSERT(out_len <= wchar_buf_len - offset); p = wchar_buf; e = wchar_buf + offset + out_len; /* ASCII output is broken into space-delimited 'words' @@ -5948,6 +5952,7 @@ no_passthrough: ; * If we are already too far along on a line to include Base64/QPrint encoded data * on the same line (without overrunning max line length), then add a line feed * right now */ +feed_and_mime_encode: if (mb_convert_buf_len(&buf) - line_start + indent + strlen(outcode->mime_name) > 55) { MB_CONVERT_BUF_ENSURE(&buf, buf.out, buf.limit, (e - word_start) + linefeed_len + 1); buf.out = mb_convert_buf_appendn(buf.out, linefeed, linefeed_len); @@ -5985,7 +5990,13 @@ no_passthrough: ; if (in_len) { /* Copy chars which are part of an incomplete 'word' to the beginning - * of wchar_buf and reprocess them on the next iteration */ + * of wchar_buf and reprocess them on the next iteration. + * But first make sure that the incomplete 'word' isn't so big that + * there will be no space to add any more decoded wchars in the buffer + * (which could lead to an infinite loop) */ + if ((word_start - wchar_buf) < MBSTRING_MIN_WCHAR_BUFSIZE) { + goto feed_and_mime_encode; + } offset = e - word_start; if (offset) { memmove(wchar_buf, word_start, offset * sizeof(uint32_t)); @@ -6027,17 +6038,17 @@ mime_encoding_needed: ; /* Do we need to refill wchar_buf to make sure we don't run out of wchars * in the middle of a line? */ - if (p == wchar_buf) { + offset = e - p; + if (wchar_buf_len - offset < MBSTRING_MIN_WCHAR_BUFSIZE) { goto start_new_line; } - offset = e - p; memmove(wchar_buf, p, offset * sizeof(uint32_t)); while(true) { refill_wchar_buf: ; - ZEND_ASSERT(offset < 80); - size_t out_len = incode->to_wchar(&in, &in_len, wchar_buf + offset, 80 - offset, &state); - ZEND_ASSERT(out_len <= 80 - offset); + ZEND_ASSERT(offset + MBSTRING_MIN_WCHAR_BUFSIZE <= wchar_buf_len); + size_t out_len = incode->to_wchar(&in, &in_len, wchar_buf + offset, wchar_buf_len - offset, &state); + ZEND_ASSERT(out_len <= wchar_buf_len - offset); p = wchar_buf; e = wchar_buf + offset + out_len; @@ -6112,22 +6123,18 @@ start_new_line: ; indent = 0; /* Indent argument must only affect the first line */ - if (in_len) { - /* We still have more of input string remaining to decode */ + if (in_len || p < e) { + /* We still have more input to process */ buf.out = mb_convert_buf_appendn(buf.out, linefeed, linefeed_len); buf.out = mb_convert_buf_add(buf.out, ' '); line_start = mb_convert_buf_len(&buf); - /* Copy remaining wchars to beginning of buffer so they will be - * processed on the next iteration of outer 'do' loop */ offset = e - p; - memmove(wchar_buf, p, offset * sizeof(uint32_t)); - goto refill_wchar_buf; - } else if (p < e) { - /* Input string is finished, but we still have trailing wchars - * remaining to be processed in wchar_buf */ - buf.out = mb_convert_buf_appendn(buf.out, linefeed, linefeed_len); - buf.out = mb_convert_buf_add(buf.out, ' '); - line_start = mb_convert_buf_len(&buf); + if (in_len && (wchar_buf_len - offset >= MBSTRING_MIN_WCHAR_BUFSIZE)) { + /* Copy any remaining wchars to beginning of buffer and refill + * the rest of the buffer */ + memmove(wchar_buf, p, offset * sizeof(uint32_t)); + goto refill_wchar_buf; + } goto start_new_line; } else { /* We are done! */ @@ -6165,7 +6172,7 @@ PHP_FUNCTION(mb_encode_mimeheader) charset = php_mb_get_encoding(charset_name, 2); if (!charset) { RETURN_THROWS(); - } else if (charset->mime_name == NULL || charset->mime_name[0] == '\0') { + } else if (charset->mime_name == NULL || charset->mime_name[0] == '\0' || charset == &mbfl_encoding_qprint) { zend_argument_value_error(2, "\"%s\" cannot be used for MIME header encoding", ZSTR_VAL(charset_name)); RETURN_THROWS(); } diff --git a/ext/mbstring/tests/mb_encode_mimeheader_basic4.phpt b/ext/mbstring/tests/mb_encode_mimeheader_basic4.phpt index 7bf05b43ae36b..02206efab3586 100644 --- a/ext/mbstring/tests/mb_encode_mimeheader_basic4.phpt +++ b/ext/mbstring/tests/mb_encode_mimeheader_basic4.phpt @@ -2,6 +2,8 @@ Test mb_encode_mimeheader() function : test cases found by fuzzer --EXTENSIONS-- mbstring +--INI-- +error_reporting=E_ALL^E_DEPRECATED --FILE-- getMessage() . \PHP_EOL; +} echo "Done"; ?> @@ -156,5 +176,11 @@ string(75) " 111111111111111111111111111111111111111111111111111111111111111111 string(33) "=?HZ-GB-2312?Q?=7E=7Bs=5B=7E=7D?=" string(77) "2 !3" string(282) "=?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20!=33=20?=" -string(296) "2 =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20!=33?=" +string(344) "2 =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?GB18030?Q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?GB18030?Q?=20=20=20=20=20=20=20=20=20!=33?=" +string(135) "=?UTF-8?B?LDk4Njg5NDksOTg2ODk3OCw5ODY5MDE1LDk2ODkxMDAsOTg2OTEyMSw5ODY5?= + =?UTF-8?B?NjE1LDk4NzA2OTAsOTg2NzExNiw5ODU1ODExOTg2MTE4My4g?=" +string(142) "xx =?UTF-8?B?QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB?= + =?UTF-8?B?QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBIA==?=" +string(690) "=?ISO-2022-JP?B?bmU/?=A =?ISO-2022-JP?B?GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/cxskQiFEGyhCPw==?=A =?ISO-2022-JP?B?GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/?=A =?ISO-2022-JP?B?GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/?=A =?ISO-2022-JP?B?GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/?=A =?ISO-2022-JP?B?GyRCIUQbKEI/GyRCIUQbKEI/MRskQiFEGyhCPxskQiFEGyhCPw==?=A =?ISO-2022-JP?B?GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/?=A =?ISO-2022-JP?B?GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/?=A =?ISO-2022-JP?B?GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/?=A =?ISO-2022-JP?B?GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/GyRCIUQbKEI/?=A =?ISO-2022-JP?B?GyRCIUQbKEI/GyRCIUQbKEI/MQ==?=" +mb_encode_mimeheader(): Argument #2 ($charset) "Quoted-Printable" cannot be used for MIME header encoding Done diff --git a/ext/mysqli/tests/bug34810.phpt b/ext/mysqli/tests/bug34810.phpt index 07812503062a8..b7bd163cdecef 100644 --- a/ext/mysqli/tests/bug34810.phpt +++ b/ext/mysqli/tests/bug34810.phpt @@ -84,7 +84,7 @@ object(mysqli)#%d (%d) { ["host_info"]=> string(%d) "%s" ["info"]=> - NULL + %s ["insert_id"]=> int(0) ["server_info"]=> diff --git a/ext/mysqlnd/mysqlnd_charset.c b/ext/mysqlnd/mysqlnd_charset.c index 802822128be2e..2d826002686b4 100644 --- a/ext/mysqlnd/mysqlnd_charset.c +++ b/ext/mysqlnd/mysqlnd_charset.c @@ -270,7 +270,7 @@ static unsigned int check_mb_eucjpms(const char * const start, const char * cons } if (valid_eucjpms_ss3(start[0]) && (end - start) > 2 && valid_eucjpms(start[1]) && valid_eucjpms(start[2])) { - return 2; + return 3; } return 0; } diff --git a/ext/mysqlnd/mysqlnd_charset.h b/ext/mysqlnd/mysqlnd_charset.h index d7f8053dd8606..a2b1e2f148316 100644 --- a/ext/mysqlnd/mysqlnd_charset.h +++ b/ext/mysqlnd/mysqlnd_charset.h @@ -19,6 +19,9 @@ #ifndef MYSQLND_CHARSET_H #define MYSQLND_CHARSET_H +#define MYSQLND_UTF8_MB3_DEFAULT_ID 33 +#define MYSQLND_UTF8_MB4_DEFAULT_ID 45 + PHPAPI zend_ulong mysqlnd_cset_escape_quotes(const MYSQLND_CHARSET * const charset, char * newstr, const char * escapestr, const size_t escapestr_len); diff --git a/ext/mysqlnd/mysqlnd_commands.c b/ext/mysqlnd/mysqlnd_commands.c index ba214e5ff776a..27a5f13a30746 100644 --- a/ext/mysqlnd/mysqlnd_commands.c +++ b/ext/mysqlnd/mysqlnd_commands.c @@ -22,6 +22,7 @@ #include "mysqlnd_auth.h" #include "mysqlnd_wireprotocol.h" #include "mysqlnd_debug.h" +#include "mysqlnd_charset.h" /* {{{ mysqlnd_command::set_option */ @@ -613,13 +614,12 @@ MYSQLND_METHOD(mysqlnd_command, handshake)(MYSQLND_CONN_DATA * const conn, const conn->protocol_version = greet_packet.protocol_version; conn->server_version = mnd_pestrdup(greet_packet.server_version, conn->persistent); - conn->greet_charset = mysqlnd_find_charset_nr(greet_packet.charset_no); - if (!conn->greet_charset) { - char * msg; - mnd_sprintf(&msg, 0, "Server sent charset (%d) unknown to the client. Please, report to the developers", greet_packet.charset_no); - SET_CLIENT_ERROR(conn->error_info, CR_NOT_IMPLEMENTED, UNKNOWN_SQLSTATE, msg); - mnd_sprintf_free(msg); - goto err; + const MYSQLND_CHARSET *read_charset = mysqlnd_find_charset_nr(greet_packet.charset_no); + if (!read_charset) { + greet_packet.charset_no = conn->m->get_server_version(conn) >= 50500 ? MYSQLND_UTF8_MB4_DEFAULT_ID : MYSQLND_UTF8_MB3_DEFAULT_ID; + conn->greet_charset = mysqlnd_find_charset_nr(greet_packet.charset_no); + } else { + conn->greet_charset = read_charset; } conn->server_capabilities = greet_packet.server_capabilities; diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index cc672f10db2d4..28e3bd1a137a0 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5074,8 +5074,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par CHECK_OP1_TRACE_TYPE(); res_info = RES_INFO(); res_use_info = zend_jit_trace_type_to_info( - STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var))) - & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE); + STACK_MEM_TYPE(stack, EX_VAR_TO_NUM(opline->result.var))); res_addr = RES_REG_ADDR(); if (Z_MODE(res_addr) != IS_REG && STACK_TYPE(stack, EX_VAR_TO_NUM(opline->result.var)) != diff --git a/ext/opcache/shared_alloc_mmap.c b/ext/opcache/shared_alloc_mmap.c index d5e4612bc104e..92d089bb96cc8 100644 --- a/ext/opcache/shared_alloc_mmap.c +++ b/ext/opcache/shared_alloc_mmap.c @@ -52,7 +52,7 @@ static void *find_prefered_mmap_base(size_t requested_size) { size_t huge_page_size = 2 * 1024 * 1024; - uintptr_t last_free_addr = 0; + uintptr_t last_free_addr = huge_page_size; uintptr_t last_candidate = (uintptr_t)MAP_FAILED; uintptr_t start, end, text_start = 0; #if defined(__linux__) @@ -197,9 +197,6 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_ #ifdef PROT_MAX flags |= PROT_MAX(PROT_READ | PROT_WRITE | PROT_EXEC); #endif -#ifdef MAP_JIT - flags |= MAP_JIT; -#endif #if (defined(__linux__) || defined(__FreeBSD__)) && (defined(__x86_64__) || defined (__aarch64__)) && !defined(__SANITIZE_ADDRESS__) void *hint = find_prefered_mmap_base(requested_size); if (hint != MAP_FAILED) { diff --git a/ext/opcache/tests/gh13712.phpt b/ext/opcache/tests/gh13712.phpt new file mode 100644 index 0000000000000..e770375e33e46 --- /dev/null +++ b/ext/opcache/tests/gh13712.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-13712 (Segmentation fault for enabled observers when calling trait method of internal trait when opcache is loaded) +--EXTENSIONS-- +opcache +zend_test +--INI-- +zend_test.observer.enabled=1 +opcache.enable=1 +opcache.enable_cli=1 +--FILE-- +testMethod()); +?> +--EXPECTF-- + + + +bool(true) diff --git a/ext/opcache/tests/jit/qm_assign_004.phpt b/ext/opcache/tests/jit/qm_assign_004.phpt new file mode 100644 index 0000000000000..975d46be41cec --- /dev/null +++ b/ext/opcache/tests/jit/qm_assign_004.phpt @@ -0,0 +1,39 @@ +--TEST-- +JIT QM_ASSIGN: 004 missing type store +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit_buffer_size=1M +--FILE-- + +DONE +--EXPECT-- +array(2) { + [0]=> + string(0) "" + [1]=> + string(0) "" +} +array(2) { + [0]=> + string(0) "" + [1]=> + string(0) "" +} +DONE diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index b10e5a8046ab1..5033b90a8cece 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -729,7 +729,7 @@ static void zend_persist_class_method(zval *zv, zend_class_entry *ce) } // Real dynamically created internal functions like enum methods must have their own run_time_cache pointer. They're always on the same scope as their defining class. // However, copies - as caused by inheritance of internal methods - must retain the original run_time_cache pointer, shared with the source function. - if (!op_array->scope || op_array->scope == ce) { + if (!op_array->scope || (op_array->scope == ce && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE))) { ZEND_MAP_PTR_NEW(op_array->run_time_cache); } } diff --git a/ext/openssl/tests/openssl_error_string_basic.phpt b/ext/openssl/tests/openssl_error_string_basic.phpt index 02e8b3fbc49d1..86a6306cf03d9 100644 --- a/ext/openssl/tests/openssl_error_string_basic.phpt +++ b/ext/openssl/tests/openssl_error_string_basic.phpt @@ -6,6 +6,7 @@ openssl = 0x30000000) die('skip For OpenSSL < 3.0'); ?> +--XFAIL-- --FILE-- = 3.0'); ?> +--XFAIL-- --FILE-- setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true); + $pdo->exec(<<<'SQL' CREATE TABLE `test_gh11550` ( `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, @@ -51,9 +53,9 @@ array(1) { [0]=> array(4) { ["id"]=> - int(1) + string(1) "1" [0]=> - int(1) + string(1) "1" ["name"]=> string(5) "test1" [1]=> @@ -66,9 +68,9 @@ array(1) { [0]=> array(6) { ["id"]=> - int(1) + string(1) "1" [0]=> - int(1) + string(1) "1" ["name"]=> string(5) "test1" [1]=> diff --git a/ext/random/random.c b/ext/random/random.c index f34d55f340727..57f8f183b2867 100644 --- a/ext/random/random.c +++ b/ext/random/random.c @@ -315,6 +315,7 @@ PHPAPI php_random_status *php_random_default_status(void) php_random_status *status = RANDOM_G(mt19937); if (!RANDOM_G(mt19937_seeded)) { + ((php_random_status_state_mt19937 *)status->state)->mode = MT_RAND_MT19937; php_random_mt19937_seed_default(status->state); RANDOM_G(mt19937_seeded) = true; } @@ -486,11 +487,13 @@ PHP_FUNCTION(mt_srand) Z_PARAM_LONG(mode) ZEND_PARSE_PARAMETERS_END(); - state->mode = mode; - - /* Anything that is not MT_RAND_MT19937 was interpreted as MT_RAND_PHP. */ - if (state->mode != MT_RAND_MT19937) { + switch (mode) { + case MT_RAND_PHP: + state->mode = MT_RAND_PHP; zend_error(E_DEPRECATED, "The MT_RAND_PHP variant of Mt19937 is deprecated"); + break; + default: + state->mode = MT_RAND_MT19937; } if (seed_is_null) { diff --git a/ext/random/tests/01_functions/mt_srand_unknown_mode.phpt b/ext/random/tests/01_functions/mt_srand_unknown_mode.phpt new file mode 100644 index 0000000000000..dc6129aaf10fa --- /dev/null +++ b/ext/random/tests/01_functions/mt_srand_unknown_mode.phpt @@ -0,0 +1,24 @@ +--TEST-- +mt_srand(): Test unknown modes +--FILE-- + +--EXPECTF-- +int(895547922) + +Deprecated: The MT_RAND_PHP variant of Mt19937 is deprecated in %s on line %d +int(1244335972) +int(895547922) +int(895547922) diff --git a/ext/session/session.c b/ext/session/session.c index 0c869b7e57d82..fe1095189d456 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -271,16 +271,17 @@ static zend_result php_session_decode(zend_string *data) /* {{{ */ php_error_docref(NULL, E_WARNING, "Unknown session.serialize_handler. Failed to decode session object"); return FAILURE; } + zend_result result = SUCCESS; zend_try { if (PS(serializer)->decode(ZSTR_VAL(data), ZSTR_LEN(data)) == FAILURE) { php_session_cancel_decode(); - return FAILURE; + result = FAILURE; } } zend_catch { php_session_cancel_decode(); zend_bailout(); } zend_end_try(); - return SUCCESS; + return result; } /* }}} */ diff --git a/ext/session/tests/gh13680.phpt b/ext/session/tests/gh13680.phpt new file mode 100644 index 0000000000000..a95583ee34598 --- /dev/null +++ b/ext/session/tests/gh13680.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-13680 (Segfault with session_decode and compilation error) +--EXTENSIONS-- +session +--SKIPIF-- + +--INI-- +session.use_cookies=0 +session.use_strict_mode=0 +session.cache_limiter= +session.serialize_handler=php_serialize +session.save_handler=files +error_reporting=E_ALL +--FILE-- + +--EXPECTF-- +Warning: session_decode(): Failed to decode session object. Session has been destroyed in %s on line %d + +Fatal error: Could not check compatibility between Test::createFromFormat($format, $datetime, $timezone = null): Wrong and DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime|false, because class Wrong is not available in %s on line %d diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index 3c02e9b1930b6..5f684066a41c0 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -921,7 +921,7 @@ PHP_FUNCTION(socket_read) PHP_FUNCTION(socket_getsockname) { zval *arg1, *addr, *port = NULL; - php_sockaddr_storage sa_storage; + php_sockaddr_storage sa_storage = {0}; php_socket *php_sock; struct sockaddr *sa; struct sockaddr_in *sin; @@ -998,7 +998,7 @@ PHP_FUNCTION(socket_getsockname) PHP_FUNCTION(socket_getpeername) { zval *arg1, *arg2, *arg3 = NULL; - php_sockaddr_storage sa_storage; + php_sockaddr_storage sa_storage = {0}; php_socket *php_sock; struct sockaddr *sa; struct sockaddr_in *sin; diff --git a/ext/sockets/tests/gh13603.phpt b/ext/sockets/tests/gh13603.phpt new file mode 100644 index 0000000000000..9207a992fac3c --- /dev/null +++ b/ext/sockets/tests/gh13603.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-13603 - socket_getsockname - invalid characters +--EXTENSIONS-- +sockets +--FILE-- +file_name)); +} + static zend_result spl_filesystem_file_read_ex(spl_filesystem_object *intern, bool silent, zend_long line_add, bool csv) { char *buf; @@ -1868,7 +1873,7 @@ static zend_result spl_filesystem_file_read_ex(spl_filesystem_object *intern, bo if (php_stream_eof(intern->u.file.stream)) { if (!silent) { - zend_throw_exception_ex(spl_ce_RuntimeException, 0, "Cannot read from file %s", ZSTR_VAL(intern->file_name)); + spl_filesystem_file_cannot_read(intern); } return FAILURE; } @@ -1923,10 +1928,10 @@ static bool is_line_empty(spl_filesystem_object *intern) || (current_line_len == 2 && current_line[0] == '\r' && current_line[1] == '\n')))); } -static zend_result spl_filesystem_file_read_csv(spl_filesystem_object *intern, char delimiter, char enclosure, int escape, zval *return_value) /* {{{ */ +static zend_result spl_filesystem_file_read_csv(spl_filesystem_object *intern, char delimiter, char enclosure, int escape, zval *return_value, bool silent) /* {{{ */ { do { - zend_result ret = spl_filesystem_file_read(intern, /* silent */ true, /* csv */ true); + zend_result ret = spl_filesystem_file_read(intern, silent, /* csv */ true); if (ret != SUCCESS) { return ret; } @@ -1952,19 +1957,21 @@ static zend_result spl_filesystem_file_read_csv(spl_filesystem_object *intern, c } /* }}} */ -/* Call to this function reads a line in a "silent" fashion and does not throw an exception */ -static zend_result spl_filesystem_file_read_line_ex(zval * this_ptr, spl_filesystem_object *intern) /* {{{ */ +static zend_result spl_filesystem_file_read_line_ex(zval * this_ptr, spl_filesystem_object *intern, bool silent) /* {{{ */ { zval retval; /* 1) use fgetcsv? 2) overloaded call the function, 3) do it directly */ if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV)) { - return spl_filesystem_file_read_csv(intern, intern->u.file.delimiter, intern->u.file.enclosure, intern->u.file.escape, NULL); + return spl_filesystem_file_read_csv(intern, intern->u.file.delimiter, intern->u.file.enclosure, intern->u.file.escape, NULL, silent); } if (intern->u.file.func_getCurr->common.scope != spl_ce_SplFileObject) { spl_filesystem_file_free_line(intern); if (php_stream_eof(intern->u.file.stream)) { + if (!silent) { + spl_filesystem_file_cannot_read(intern); + } return FAILURE; } zend_call_method_with_0_params(Z_OBJ_P(this_ptr), Z_OBJCE_P(this_ptr), &intern->u.file.func_getCurr, "getCurrentLine", &retval); @@ -1988,18 +1995,17 @@ static zend_result spl_filesystem_file_read_line_ex(zval * this_ptr, spl_filesys zval_ptr_dtor(&retval); return SUCCESS; } else { - return spl_filesystem_file_read(intern, /* silent */ true, /* csv */ false); + return spl_filesystem_file_read(intern, silent, /* csv */ false); } } /* }}} */ -/* Call to this function reads a line in a "silent" fashion and does not throw an exception */ -static zend_result spl_filesystem_file_read_line(zval * this_ptr, spl_filesystem_object *intern) /* {{{ */ +static zend_result spl_filesystem_file_read_line(zval * this_ptr, spl_filesystem_object *intern, bool silent) /* {{{ */ { - zend_result ret = spl_filesystem_file_read_line_ex(this_ptr, intern); + zend_result ret = spl_filesystem_file_read_line_ex(this_ptr, intern, silent); while (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_SKIP_EMPTY) && ret == SUCCESS && is_line_empty(intern)) { spl_filesystem_file_free_line(intern); - ret = spl_filesystem_file_read_line_ex(this_ptr, intern); + ret = spl_filesystem_file_read_line_ex(this_ptr, intern, silent); } return ret; @@ -2021,7 +2027,7 @@ static void spl_filesystem_file_rewind(zval * this_ptr, spl_filesystem_object *i intern->u.file.current_line_num = 0; if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) { - spl_filesystem_file_read_line(this_ptr, intern); + spl_filesystem_file_read_line(this_ptr, intern, true); } } /* }}} */ @@ -2175,7 +2181,7 @@ PHP_METHOD(SplFileObject, current) CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); if (!intern->u.file.current_line && Z_ISUNDEF(intern->u.file.current_zval)) { - spl_filesystem_file_read_line(ZEND_THIS, intern); + spl_filesystem_file_read_line(ZEND_THIS, intern, true); } if (intern->u.file.current_line && (!SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_CSV) || Z_ISUNDEF(intern->u.file.current_zval))) { RETURN_STRINGL(intern->u.file.current_line, intern->u.file.current_line_len); @@ -2214,7 +2220,7 @@ PHP_METHOD(SplFileObject, next) spl_filesystem_file_free_line(intern); if (SPL_HAS_FLAG(intern->flags, SPL_FILE_OBJECT_READ_AHEAD)) { - spl_filesystem_file_read_line(ZEND_THIS, intern); + spl_filesystem_file_read_line(ZEND_THIS, intern, true); } intern->u.file.current_line_num++; } /* }}} */ @@ -2332,7 +2338,7 @@ PHP_METHOD(SplFileObject, fgetcsv) } } - if (spl_filesystem_file_read_csv(intern, delimiter, enclosure, escape, return_value) == FAILURE) { + if (spl_filesystem_file_read_csv(intern, delimiter, enclosure, escape, return_value, true) == FAILURE) { RETURN_FALSE; } } @@ -2713,7 +2719,7 @@ PHP_METHOD(SplFileObject, seek) spl_filesystem_file_rewind(ZEND_THIS, intern); for (i = 0; i < line_pos; i++) { - if (spl_filesystem_file_read_line(ZEND_THIS, intern) == FAILURE) { + if (spl_filesystem_file_read_line(ZEND_THIS, intern, true) == FAILURE) { return; } } @@ -2733,8 +2739,12 @@ PHP_METHOD(SplFileObject, __toString) CHECK_SPL_FILE_OBJECT_IS_INITIALIZED(intern); - if (!intern->u.file.current_line && Z_ISUNDEF(intern->u.file.current_zval)) { - spl_filesystem_file_read_line(ZEND_THIS, intern); + if (!intern->u.file.current_line) { + ZEND_ASSERT(Z_ISUNDEF(intern->u.file.current_zval)); + zend_result result = spl_filesystem_file_read_line(ZEND_THIS, intern, false); + if (UNEXPECTED(result != SUCCESS)) { + RETURN_THROWS(); + } } RETURN_STRINGL(intern->u.file.current_line, intern->u.file.current_line_len); diff --git a/ext/spl/tests/gh13685.phpt b/ext/spl/tests/gh13685.phpt new file mode 100644 index 0000000000000..0f679d0e93fc9 --- /dev/null +++ b/ext/spl/tests/gh13685.phpt @@ -0,0 +1,52 @@ +--TEST-- +GH-13685 (Unexpected null pointer in zend_string.h) +--FILE-- +fwrite($contents); +$file->rewind(); +while (($data = $file->fgetcsv(',', '"', ''))) { + var_dump((string) $file); +} +try { + var_dump((string) $file); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +echo "--- Use csv control ---\n"; + +$file = new SplTempFileObject; +$file->fwrite($contents); +$file->rewind(); +$file->setFlags(SplFileObject::READ_CSV); +$file->setCsvControl(',', '"', ''); +foreach ($file as $row) { + var_dump((string) $file); +} +try { + var_dump((string) $file); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECT-- +--- Directly call fgetcsv --- +string(14) ""A", "B", "C" +" +string(13) ""D", "E", "F"" +Cannot read from file php://temp +--- Use csv control --- +string(14) ""A", "B", "C" +" +string(13) ""D", "E", "F"" +Cannot read from file php://temp diff --git a/ext/standard/mail.c b/ext/standard/mail.c index b1e0f14b8d5e2..d886e893309d9 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -57,7 +57,7 @@ extern zend_long php_getuid(void); -static bool php_mail_build_headers_check_field_value(zval *val) +static php_mail_header_value_error_type php_mail_build_headers_check_field_value(zval *val) { size_t len = 0; zend_string *value = Z_STR_P(val); @@ -66,20 +66,39 @@ static bool php_mail_build_headers_check_field_value(zval *val) /* https://tools.ietf.org/html/rfc2822#section-2.2.3 */ while (len < value->len) { if (*(value->val+len) == '\r') { + if (*(value->val+len+1) != '\n') { + return CONTAINS_CR_ONLY; + } + if (value->len - len >= 3 - && *(value->val+len+1) == '\n' && (*(value->val+len+2) == ' ' || *(value->val+len+2) == '\t')) { len += 3; continue; } - return FAILURE; + + return CONTAINS_CRLF; + } + /** + * The RFC does not allow using LF alone for folding. However, LF is + * often treated similarly to CRLF, and there are likely many user + * environments that use LF for folding. + * Therefore, considering such an environment, folding with LF alone + * is allowed. + */ + if (*(value->val+len) == '\n') { + if (value->len - len >= 2 + && (*(value->val+len+1) == ' ' || *(value->val+len+1) == '\t')) { + len += 2; + continue; + } + return CONTAINS_LF_ONLY; } if (*(value->val+len) == '\0') { - return FAILURE; + return CONTAINS_NULL; } len++; } - return SUCCESS; + return NO_HEADER_ERROR; } @@ -108,9 +127,27 @@ static void php_mail_build_headers_elem(smart_str *s, zend_string *key, zval *va zend_value_error("Header name \"%s\" contains invalid characters", ZSTR_VAL(key)); return; } - if (php_mail_build_headers_check_field_value(val) != SUCCESS) { - zend_value_error("Header \"%s\" has invalid format, or contains invalid characters", ZSTR_VAL(key)); - return; + + php_mail_header_value_error_type error_type = php_mail_build_headers_check_field_value(val); + switch (error_type) { + case NO_HEADER_ERROR: + break; + case CONTAINS_LF_ONLY: + zend_value_error("Header \"%s\" contains LF character that is not allowed in the header", ZSTR_VAL(key)); + return; + case CONTAINS_CR_ONLY: + zend_value_error("Header \"%s\" contains CR character that is not allowed in the header", ZSTR_VAL(key)); + return; + case CONTAINS_CRLF: + zend_value_error("Header \"%s\" contains CRLF characters that are used as a line separator and are not allowed in the header", ZSTR_VAL(key)); + return; + case CONTAINS_NULL: + zend_value_error("Header \"%s\" contains NULL character that is not allowed in the header", ZSTR_VAL(key)); + return; + default: + // fallback + zend_value_error("Header \"%s\" has invalid format, or contains invalid characters", ZSTR_VAL(key)); + return; } smart_str_append(s, key); smart_str_appendl(s, ": ", 2); diff --git a/ext/standard/password.c b/ext/standard/password.c index c3f7f73821839..6eedd8abcc152 100644 --- a/ext/standard/password.c +++ b/ext/standard/password.c @@ -180,6 +180,11 @@ static zend_string* php_password_bcrypt_hash(const zend_string *password, zend_a zval *zcost; zend_long cost = PHP_PASSWORD_BCRYPT_COST; + if (memchr(ZSTR_VAL(password), '\0', ZSTR_LEN(password))) { + zend_value_error("Bcrypt password must not contain null character"); + return NULL; + } + if (options && (zcost = zend_hash_str_find(options, "cost", sizeof("cost")-1)) != NULL) { cost = zval_get_long(zcost); } diff --git a/ext/standard/php_mail.h b/ext/standard/php_mail.h index 9536d1f62616d..338f7831c6b87 100644 --- a/ext/standard/php_mail.h +++ b/ext/standard/php_mail.h @@ -49,5 +49,12 @@ do { \ } \ } while(0) +typedef enum { + NO_HEADER_ERROR, + CONTAINS_LF_ONLY, + CONTAINS_CR_ONLY, + CONTAINS_CRLF, + CONTAINS_NULL +} php_mail_header_value_error_type; #endif /* PHP_MAIL_H */ diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index 78d2db999eab6..3feac72122276 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -538,11 +538,32 @@ static void append_backslashes(smart_str *str, size_t num_bs) } } -/* See https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments */ -static void append_win_escaped_arg(smart_str *str, zend_string *arg) +const char *special_chars = "()!^\"<>&|%"; + +static bool is_special_character_present(const zend_string *arg) +{ + for (size_t i = 0; i < ZSTR_LEN(arg); ++i) { + if (strchr(special_chars, ZSTR_VAL(arg)[i]) != NULL) { + return true; + } + } + return false; +} + +/* See https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments and + * https://learn.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way */ +static void append_win_escaped_arg(smart_str *str, zend_string *arg, bool is_cmd_argument) { size_t num_bs = 0; + bool has_special_character = false; + if (is_cmd_argument) { + has_special_character = is_special_character_present(arg); + if (has_special_character) { + /* Escape double quote with ^ if executed by cmd.exe. */ + smart_str_appendc(str, '^'); + } + } smart_str_appendc(str, '"'); for (size_t i = 0; i < ZSTR_LEN(arg); ++i) { char c = ZSTR_VAL(arg)[i]; @@ -556,18 +577,71 @@ static void append_win_escaped_arg(smart_str *str, zend_string *arg) num_bs = num_bs * 2 + 1; } append_backslashes(str, num_bs); + if (has_special_character && strchr(special_chars, c) != NULL) { + /* Escape special chars with ^ if executed by cmd.exe. */ + smart_str_appendc(str, '^'); + } smart_str_appendc(str, c); num_bs = 0; } append_backslashes(str, num_bs * 2); + if (has_special_character) { + /* Escape double quote with ^ if executed by cmd.exe. */ + smart_str_appendc(str, '^'); + } smart_str_appendc(str, '"'); } +static inline int stricmp_end(const char* suffix, const char* str) { + size_t suffix_len = strlen(suffix); + size_t str_len = strlen(str); + + if (suffix_len > str_len) { + return -1; /* Suffix is longer than string, cannot match. */ + } + + /* Compare the end of the string with the suffix, ignoring case. */ + return _stricmp(str + (str_len - suffix_len), suffix); +} + +static bool is_executed_by_cmd(const char *prog_name) +{ + /* If program name is cmd.exe, then return true. */ + if (_stricmp("cmd.exe", prog_name) == 0 || _stricmp("cmd", prog_name) == 0 + || stricmp_end("\\cmd.exe", prog_name) == 0 || stricmp_end("\\cmd", prog_name) == 0) { + return true; + } + + /* Find the last occurrence of the directory separator (backslash or forward slash). */ + char *last_separator = strrchr(prog_name, '\\'); + char *last_separator_fwd = strrchr(prog_name, '/'); + if (last_separator_fwd && (!last_separator || last_separator < last_separator_fwd)) { + last_separator = last_separator_fwd; + } + + /* Find the last dot in the filename after the last directory separator. */ + char *extension = NULL; + if (last_separator != NULL) { + extension = strrchr(last_separator, '.'); + } else { + extension = strrchr(prog_name, '.'); + } + + if (extension == NULL || extension == prog_name) { + /* No file extension found, it is not batch file. */ + return false; + } + + /* Check if the file extension is ".bat" or ".cmd" which is always executed by cmd.exe. */ + return _stricmp(extension, ".bat") == 0 || _stricmp(extension, ".cmd") == 0; +} + static zend_string *create_win_command_from_args(HashTable *args) { smart_str str = {0}; zval *arg_zv; - bool is_prog_name = 1; + bool is_prog_name = true; + bool is_cmd_execution = false; int elem_num = 0; ZEND_HASH_FOREACH_VAL(args, arg_zv) { @@ -577,11 +651,13 @@ static zend_string *create_win_command_from_args(HashTable *args) return NULL; } - if (!is_prog_name) { + if (is_prog_name) { + is_cmd_execution = is_executed_by_cmd(ZSTR_VAL(arg_str)); + } else { smart_str_appendc(&str, ' '); } - append_win_escaped_arg(&str, arg_str); + append_win_escaped_arg(&str, arg_str, !is_prog_name && is_cmd_execution); is_prog_name = 0; zend_string_release(arg_str); diff --git a/ext/standard/tests/file/006_error.phpt b/ext/standard/tests/file/006_error.phpt index 98caa4d1e11e2..26c977b9ea5c3 100644 --- a/ext/standard/tests/file/006_error.phpt +++ b/ext/standard/tests/file/006_error.phpt @@ -11,20 +11,9 @@ require __DIR__ . '/../skipif_root.inc'; --FILE-- +--INI-- +memory_limit=6G +--FILE-- + +--CLEAN-- + +--EXPECT-- +File written successfully. diff --git a/ext/standard/tests/file/mkdir-004.phpt b/ext/standard/tests/file/mkdir-004.phpt index 7de0e45894d43..1974faa0b6036 100644 --- a/ext/standard/tests/file/mkdir-004.phpt +++ b/ext/standard/tests/file/mkdir-004.phpt @@ -1,24 +1,20 @@ --TEST-- -recursive mkdir() tests ---SKIPIF-- - +recursive mkdir() with unclean paths --FILE-- --EXPECT-- -bool(true) -bool(true) -bool(true) -Done +Ok. diff --git a/ext/standard/tests/file/mkdir-005.phpt b/ext/standard/tests/file/mkdir-005.phpt deleted file mode 100644 index 383f8b18e0527..0000000000000 --- a/ext/standard/tests/file/mkdir-005.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -recursive mkdir() tests ---SKIPIF-- - ---FILE-- - ---EXPECT-- -bool(true) -bool(true) -bool(true) -Done diff --git a/ext/standard/tests/file/mkdir-006.phpt b/ext/standard/tests/file/mkdir-006.phpt deleted file mode 100644 index 1974faa0b6036..0000000000000 --- a/ext/standard/tests/file/mkdir-006.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -recursive mkdir() with unclean paths ---FILE-- - ---EXPECT-- -Ok. diff --git a/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_1.phpt b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_1.phpt new file mode 100644 index 0000000000000..8d0939cdf1bc7 --- /dev/null +++ b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_1.phpt @@ -0,0 +1,29 @@ +--TEST-- +GHSA-54hq-v5wp-fqgv - proc_open does not correctly escape args for bat files +--SKIPIF-- + +--FILE-- + +--EXPECT-- +"¬epad.exe +--CLEAN-- + diff --git a/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_2.phpt b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_2.phpt new file mode 100644 index 0000000000000..a1e39d7ef9ba0 --- /dev/null +++ b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_2.phpt @@ -0,0 +1,29 @@ +--TEST-- +GHSA-54hq-v5wp-fqgv - proc_open does not correctly escape args for cmd files +--SKIPIF-- + +--FILE-- +^()!.exe"], $descriptorspec, $pipes); +proc_close($proc); + +?> +--EXPECT-- +"¬epad<>^()!.exe +--CLEAN-- + diff --git a/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_3.phpt b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_3.phpt new file mode 100644 index 0000000000000..69f12d7b358d2 --- /dev/null +++ b/ext/standard/tests/general_functions/ghsa-pc52-254m-w9w7_3.phpt @@ -0,0 +1,29 @@ +--TEST-- +GHSA-54hq-v5wp-fqgv - proc_open does not correctly escape args for cmd executing batch files +--SKIPIF-- + +--FILE-- + +--EXPECT-- +"¬epad.exe +--CLEAN-- + diff --git a/ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt b/ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt new file mode 100644 index 0000000000000..77fcb68089488 --- /dev/null +++ b/ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt @@ -0,0 +1,63 @@ +--TEST-- +ghsa-wpj3-hf5j-x4v4 (__Host-/__Secure- cookie bypass due to partial CVE-2022-31629 fix) +--COOKIE-- +..Host-test=ignore_1; +._Host-test=ignore_2; +.[Host-test=ignore_3; +_.Host-test=ignore_4; +__Host-test=ignore_5; +_[Host-test=ignore_6; +[.Host-test=ignore_7; +[_Host-test=ignore_8; +[[Host-test=ignore_9; +..Host-test[]=ignore_10; +._Host-test[]=ignore_11; +.[Host-test[]=ignore_12; +_.Host-test[]=ignore_13; +__Host-test[]=legitimate_14; +_[Host-test[]=legitimate_15; +[.Host-test[]=ignore_16; +[_Host-test[]=ignore_17; +[[Host-test[]=ignore_18; +..Secure-test=ignore_1; +._Secure-test=ignore_2; +.[Secure-test=ignore_3; +_.Secure-test=ignore_4; +__Secure-test=ignore_5; +_[Secure-test=ignore_6; +[.Secure-test=ignore_7; +[_Secure-test=ignore_8; +[[Secure-test=ignore_9; +..Secure-test[]=ignore_10; +._Secure-test[]=ignore_11; +.[Secure-test[]=ignore_12; +_.Secure-test[]=ignore_13; +__Secure-test[]=legitimate_14; +_[Secure-test[]=legitimate_15; +[.Secure-test[]=ignore_16; +[_Secure-test[]=ignore_17; +[[Secure-test[]=ignore_18; +--FILE-- + +--EXPECT-- +array(3) { + ["__Host-test"]=> + array(1) { + [0]=> + string(13) "legitimate_14" + } + ["_"]=> + array(2) { + ["Host-test["]=> + string(13) "legitimate_15" + ["Secure-test["]=> + string(13) "legitimate_15" + } + ["__Secure-test"]=> + array(1) { + [0]=> + string(13) "legitimate_14" + } +} diff --git a/ext/standard/tests/mail/gh13415.phpt b/ext/standard/tests/mail/gh13415.phpt new file mode 100644 index 0000000000000..f507259a8e267 --- /dev/null +++ b/ext/standard/tests/mail/gh13415.phpt @@ -0,0 +1,52 @@ +--TEST-- +GH-13415 (Added validation of line breaks \n in $additional_headers of mail()) +--INI-- +sendmail_path={MAIL:gh13415.out} +--FILE-- + "foo@example.com \nCc: hacker@example.com"]); +} catch (Throwable $e) { + echo $e->getMessage()."\n\n"; +} + +echo "CR only:\n"; +try { + mail('to@example.com', 'Test Subject', 'A Message', ['Reply-To' => "foo@example.com \rCc: hacker@example.com"]); +} catch (Throwable $e) { + echo $e->getMessage()."\n\n"; +} + +echo "CRLF:\n"; +try { + mail('to@example.com', 'Test Subject', 'A Message', ['Reply-To' => "foo@example.com \r\nCc: hacker@example.com"]); +} catch (Throwable $e) { + echo $e->getMessage()."\n\n"; +} + +echo "NULL:\n"; +try { + mail('to@example.com', 'Test Subject', 'A Message', ['Reply-To' => "foo@example.com \0Cc: hacker@example.com"]); +} catch (Throwable $e) { + echo $e->getMessage()."\n\n"; +} +?> +--CLEAN-- + +--EXPECTF-- +LF only: +Header "Reply-To" contains LF character that is not allowed in the header + +CR only: +Header "Reply-To" contains CR character that is not allowed in the header + +CRLF: +Header "Reply-To" contains CRLF characters that are used as a line separator and are not allowed in the header + +NULL: +Header "Reply-To" contains NULL character that is not allowed in the header diff --git a/ext/standard/tests/mail/mail_basic7.phpt b/ext/standard/tests/mail/mail_basic7.phpt index a43617befc981..ffc3fd241f006 100644 --- a/ext/standard/tests/mail/mail_basic7.phpt +++ b/ext/standard/tests/mail/mail_basic7.phpt @@ -258,4 +258,4 @@ Subject: Test Subject foo9: %&$#! A Message -ValueError: Header "foo10" has invalid format, or contains invalid characters +ValueError: Header "foo10" contains NULL character that is not allowed in the header diff --git a/ext/standard/tests/password/password_bcrypt_errors.phpt b/ext/standard/tests/password/password_bcrypt_errors.phpt index 10c3483f5a80d..5d823cba0217d 100644 --- a/ext/standard/tests/password/password_bcrypt_errors.phpt +++ b/ext/standard/tests/password/password_bcrypt_errors.phpt @@ -14,7 +14,14 @@ try { } catch (ValueError $exception) { echo $exception->getMessage() . "\n"; } + +try { + var_dump(password_hash("null\0password", PASSWORD_BCRYPT)); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} ?> --EXPECT-- Invalid bcrypt cost parameter specified: 3 Invalid bcrypt cost parameter specified: 32 +Bcrypt password must not contain null character diff --git a/ext/xsl/config.m4 b/ext/xsl/config.m4 index 14af536fa7352..8d45e8eacb9a3 100644 --- a/ext/xsl/config.m4 +++ b/ext/xsl/config.m4 @@ -29,4 +29,5 @@ if test "$PHP_XSL" != "no"; then PHP_NEW_EXTENSION(xsl, php_xsl.c xsltprocessor.c, $ext_shared) PHP_SUBST(XSL_SHARED_LIBADD) PHP_ADD_EXTENSION_DEP(xsl, libxml) + PHP_ADD_EXTENSION_DEP(xsl, dom) fi diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 45b48ac61ff5e..1dbefe5c687ea 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -1365,6 +1365,9 @@ PHP_ZEND_TEST_API int gh11934b_ffi_var_test_cdata; /** * This function allows us to simulate early return of copy_file_range by setting the limit_copy_file_range ini setting. */ +#ifdef __MUSL__ +typedef off_t off64_t; +#endif PHP_ZEND_TEST_API ssize_t copy_file_range(int fd_in, off64_t *off_in, int fd_out, off64_t *off_out, size_t len, unsigned int flags) { ssize_t (*original_copy_file_range)(int, off64_t *, int, off64_t *, size_t, unsigned int) = dlsym(RTLD_NEXT, "copy_file_range"); diff --git a/ext/zlib/tests/bug48725_2.phpt b/ext/zlib/tests/bug48725_2.phpt index 3481983e859b6..67b584719d1e1 100644 --- a/ext/zlib/tests/bug48725_2.phpt +++ b/ext/zlib/tests/bug48725_2.phpt @@ -7,7 +7,7 @@ zlib $stream = fopen('data://text/plain;base64,' . base64_encode('Foo bar baz'), 'r'); stream_filter_append($stream, 'zlib.deflate', STREAM_FILTER_READ); -print bin2hex(stream_get_contents($stream)); +print gzinflate(stream_get_contents($stream)); ?> --EXPECT-- -72cbcf57484a2c02e22a00000000ffff0300 +Foo bar baz diff --git a/ext/zlib/tests/bug71417.phpt b/ext/zlib/tests/bug71417.phpt index eac0d7efed1de..8d871a329e7fe 100644 --- a/ext/zlib/tests/bug71417.phpt +++ b/ext/zlib/tests/bug71417.phpt @@ -75,7 +75,7 @@ read: bool(false) gzdecode(): Warning: gzdecode(): data error in %s on line %d -read: string(32) "The quick brown fox jumps over t" +read: string(3%d) "The quick brown fox jumps over%s" gzdecode(): Warning: gzdecode(): data error in %s on line %d diff --git a/ext/zlib/tests/gzcompress_basic1.phpt b/ext/zlib/tests/gzcompress_basic1.phpt index b5ad22341ed74..b37aebe33d704 100644 --- a/ext/zlib/tests/gzcompress_basic1.phpt +++ b/ext/zlib/tests/gzcompress_basic1.phpt @@ -25,7 +25,6 @@ $smallstring = "A small string to compress\n"; for($i = -1; $i < 10; $i++) { echo "-- Compression level $i --\n"; $output = gzcompress($data, $i); - var_dump(md5($output)); var_dump(strcmp(gzuncompress($output), $data)); } @@ -33,83 +32,61 @@ for($i = -1; $i < 10; $i++) { for($i = -1; $i < 10; $i++) { echo "-- Compression level $i --\n"; $output = gzcompress($smallstring, $i); - var_dump(bin2hex($output)); var_dump(strcmp(gzuncompress($output), $smallstring)); } // Calling gzcompress() with mandatory arguments echo "\n-- Testing with no specified compression level --\n"; -var_dump( bin2hex(gzcompress($smallstring) )); +$output = gzcompress($smallstring); + var_dump(strcmp(gzuncompress($output), $smallstring)); ?> --EXPECT-- *** Testing gzcompress() : basic functionality *** -- Compression level -1 -- -string(32) "764809aef15bb34cb73ad49ecb600d99" int(0) -- Compression level 0 -- -string(32) "d0136b3fb5424142c0eb26dfec8f56fe" int(0) -- Compression level 1 -- -string(32) "c2e070f4320d1f674965eaab95b53d9c" int(0) -- Compression level 2 -- -string(32) "36922f486410d08209d0d0d21b26030e" int(0) -- Compression level 3 -- -string(32) "a441a2f5169bb303cd45b860a5a9dbf9" int(0) -- Compression level 4 -- -string(32) "d5b7451e9de2864beccc9de1fc55eb87" int(0) -- Compression level 5 -- -string(32) "32ba4a01120449ec25508cabfad41f56" int(0) -- Compression level 6 -- -string(32) "764809aef15bb34cb73ad49ecb600d99" int(0) -- Compression level 7 -- -string(32) "e083e7e8d05471fed3c2182b9cd0d9eb" int(0) -- Compression level 8 -- -string(32) "e083e7e8d05471fed3c2182b9cd0d9eb" int(0) -- Compression level 9 -- -string(32) "e083e7e8d05471fed3c2182b9cd0d9eb" int(0) -- Compression level -1 -- -string(70) "789c735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" int(0) -- Compression level 0 -- -string(76) "7801011b00e4ff4120736d616c6c20737472696e6720746f20636f6d70726573730a87a509cb" int(0) -- Compression level 1 -- -string(70) "7801735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" int(0) -- Compression level 2 -- -string(70) "785e735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" int(0) -- Compression level 3 -- -string(70) "785e735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" int(0) -- Compression level 4 -- -string(70) "785e735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" int(0) -- Compression level 5 -- -string(70) "785e735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" int(0) -- Compression level 6 -- -string(70) "789c735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" int(0) -- Compression level 7 -- -string(70) "78da735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" int(0) -- Compression level 8 -- -string(70) "78da735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" int(0) -- Compression level 9 -- -string(70) "78da735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" int(0) -- Testing with no specified compression level -- -string(70) "789c735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" +int(0) diff --git a/ext/zlib/tests/gzcompress_variation1.phpt b/ext/zlib/tests/gzcompress_variation1.phpt index ce64b5e1a29a5..81dafa737c305 100644 --- a/ext/zlib/tests/gzcompress_variation1.phpt +++ b/ext/zlib/tests/gzcompress_variation1.phpt @@ -10,13 +10,13 @@ echo "*** Testing gzcompress() : variation ***\n"; echo "\n-- Testing multiple compression --\n"; $output = gzcompress($data); -var_dump( md5($output)); -var_dump(md5(gzcompress($output))); +var_dump(strlen($output)); +var_dump(strlen(gzcompress($output))); ?> ---EXPECT-- +--EXPECTF-- *** Testing gzcompress() : variation *** -- Testing multiple compression -- -string(32) "764809aef15bb34cb73ad49ecb600d99" -string(32) "eba942bc2061f23ea8688cc5101872a4" +int(1%d) +int(1%d) diff --git a/ext/zlib/tests/gzdeflate_basic1.phpt b/ext/zlib/tests/gzdeflate_basic1.phpt index 73eb9af3a4b73..0945783800118 100644 --- a/ext/zlib/tests/gzdeflate_basic1.phpt +++ b/ext/zlib/tests/gzdeflate_basic1.phpt @@ -25,7 +25,6 @@ $smallstring = "A small string to compress\n"; for($i = -1; $i < 10; $i++) { echo "-- Compression level $i --\n"; $output = gzdeflate($data, $i); - var_dump(md5($output)); var_dump(strcmp(gzinflate($output), $data)); } @@ -33,83 +32,61 @@ for($i = -1; $i < 10; $i++) { for($i = -1; $i < 10; $i++) { echo "-- Compression level $i --\n"; $output = gzdeflate($smallstring, $i); - var_dump(bin2hex($output)); var_dump(strcmp(gzinflate($output), $smallstring)); } // Calling gzdeflate() with just mandatory arguments echo "\n-- Testing with no specified compression level --\n"; -var_dump( bin2hex(gzdeflate($smallstring) )); +$output = gzdeflate($smallstring); +var_dump(strcmp(gzinflate($output), $smallstring)); ?> --EXPECT-- *** Testing gzdeflate() : basic functionality *** -- Compression level -1 -- -string(32) "078554fe65e06f6ff01eab51cfc7ae9b" int(0) -- Compression level 0 -- -string(32) "a71e54d2499aff9e48643cb1c260b60c" int(0) -- Compression level 1 -- -string(32) "05e80f4dc0d422e1f333cbed555d381f" int(0) -- Compression level 2 -- -string(32) "0fb33656e4ed0750f977df83246fce7a" int(0) -- Compression level 3 -- -string(32) "bc6e9c1dccc3e951e006315ee669ee08" int(0) -- Compression level 4 -- -string(32) "a61727d7a28c634470eb6e97a4a81b24" int(0) -- Compression level 5 -- -string(32) "a2a1a14b7542c82e8943200d093d5f27" int(0) -- Compression level 6 -- -string(32) "078554fe65e06f6ff01eab51cfc7ae9b" int(0) -- Compression level 7 -- -string(32) "078554fe65e06f6ff01eab51cfc7ae9b" int(0) -- Compression level 8 -- -string(32) "078554fe65e06f6ff01eab51cfc7ae9b" int(0) -- Compression level 9 -- -string(32) "078554fe65e06f6ff01eab51cfc7ae9b" int(0) -- Compression level -1 -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" int(0) -- Compression level 0 -- -string(64) "011b00e4ff4120736d616c6c20737472696e6720746f20636f6d70726573730a" int(0) -- Compression level 1 -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" int(0) -- Compression level 2 -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" int(0) -- Compression level 3 -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" int(0) -- Compression level 4 -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" int(0) -- Compression level 5 -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" int(0) -- Compression level 6 -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" int(0) -- Compression level 7 -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" int(0) -- Compression level 8 -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" int(0) -- Compression level 9 -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" int(0) -- Testing with no specified compression level -- -string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" +int(0) diff --git a/ext/zlib/tests/gzdeflate_variation1.phpt b/ext/zlib/tests/gzdeflate_variation1.phpt index 93de54075f5b4..21456a9887659 100644 --- a/ext/zlib/tests/gzdeflate_variation1.phpt +++ b/ext/zlib/tests/gzdeflate_variation1.phpt @@ -12,13 +12,13 @@ echo "*** Testing gzdeflate() : variation ***\n"; echo "\n-- Testing multiple compression --\n"; $output = gzdeflate($data); -var_dump( md5($output)); -var_dump(md5(gzdeflate($output))); +var_dump(strlen($output)); +var_dump(strlen(gzdeflate($output))); ?> ---EXPECT-- +--EXPECTF-- *** Testing gzdeflate() : variation *** -- Testing multiple compression -- -string(32) "078554fe65e06f6ff01eab51cfc7ae9b" -string(32) "86b9f895ef1377da5269ec3cb2729f71" +int(17%d) +int(17%d) diff --git a/ext/zlib/tests/gzencode_basic1.phpt b/ext/zlib/tests/gzencode_basic1.phpt index 346c5b4aa8362..751c21532055e 100644 --- a/ext/zlib/tests/gzencode_basic1.phpt +++ b/ext/zlib/tests/gzencode_basic1.phpt @@ -25,81 +25,74 @@ $smallstring = "A small string to compress\n"; for($i = -1; $i < 10; $i++) { echo "-- Compression level $i --\n"; $output = gzencode($data, $i); - - // Clear OS byte before encode - $output[9] = "\x00"; - - var_dump(md5($output)); + var_dump(strcmp(gzdecode($output), $data)===0); } // Compressing a smaller string for($i = -1; $i < 10; $i++) { echo "-- Compression level $i --\n"; $output = gzencode($smallstring, $i); - - // Clear OS byte before encode - $output[9] = "\x00"; - - var_dump(md5($output)); + var_dump(strcmp(gzdecode($output), $smallstring)===0); } // Calling gzencode() with mandatory arguments echo "\n-- Testing with no specified compression level --\n"; -var_dump(bin2hex(gzencode($smallstring))); +$output = gzencode($smallstring); +var_dump(strcmp(gzdecode($output), $smallstring)===0); echo "\n-- Testing gzencode with mode specified --\n"; -var_dump(bin2hex(gzencode($smallstring, -1, FORCE_GZIP))); - +$outupt = gzencode($smallstring, -1, FORCE_GZIP); +var_dump(strcmp(gzdecode($output), $smallstring)===0); ?> --EXPECTF-- *** Testing gzencode() : basic functionality *** -- Compression level -1 -- -string(32) "d9ede02415ce91d21e5a94274e2b9c42" +bool(true) -- Compression level 0 -- -string(32) "bbf32d5508e5f1f4e6d42790489dae15" +bool(true) -- Compression level 1 -- -string(32) "0bfaaa7a5a57f8fb533074fca6c85eeb" +bool(true) -- Compression level 2 -- -string(32) "7ddbfed63a76c42808722b66f1c133fc" +bool(true) -- Compression level 3 -- -string(32) "ca2b85d194dfa2a4e8a162b646c99265" +bool(true) -- Compression level 4 -- -string(32) "cfe28033eaf260bc33ddc04b53d3ba39" +bool(true) -- Compression level 5 -- -string(32) "ae357fada2b515422f8bea0aa3bcc48f" +bool(true) -- Compression level 6 -- -string(32) "d9ede02415ce91d21e5a94274e2b9c42" +bool(true) -- Compression level 7 -- -string(32) "d9ede02415ce91d21e5a94274e2b9c42" +bool(true) -- Compression level 8 -- -string(32) "d9ede02415ce91d21e5a94274e2b9c42" +bool(true) -- Compression level 9 -- -string(32) "0f220a09e9895bcb3a1308d2bc99cfdf" +bool(true) -- Compression level -1 -- -string(32) "f77bd31e1e4dd11d12828fb661a08010" +bool(true) -- Compression level 0 -- -string(32) "9c5005db88490d6fe102ea2c233b2872" +bool(true) -- Compression level 1 -- -string(32) "d24ff7c4c20cef69b9c3abd603368db9" +bool(true) -- Compression level 2 -- -string(32) "f77bd31e1e4dd11d12828fb661a08010" +bool(true) -- Compression level 3 -- -string(32) "f77bd31e1e4dd11d12828fb661a08010" +bool(true) -- Compression level 4 -- -string(32) "f77bd31e1e4dd11d12828fb661a08010" +bool(true) -- Compression level 5 -- -string(32) "f77bd31e1e4dd11d12828fb661a08010" +bool(true) -- Compression level 6 -- -string(32) "f77bd31e1e4dd11d12828fb661a08010" +bool(true) -- Compression level 7 -- -string(32) "f77bd31e1e4dd11d12828fb661a08010" +bool(true) -- Compression level 8 -- -string(32) "f77bd31e1e4dd11d12828fb661a08010" +bool(true) -- Compression level 9 -- -string(32) "8849e9a1543c04b3f882b5ce20839ed2" +bool(true) -- Testing with no specified compression level -- -string(94) "1f8b08000000000000%c%c735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200edc4e40b1b000000" +bool(true) -- Testing gzencode with mode specified -- -string(94) "1f8b08000000000000%c%c735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200edc4e40b1b000000" +bool(true) diff --git a/ext/zlib/tests/gzencode_variation1.phpt b/ext/zlib/tests/gzencode_variation1.phpt index 288c14a0b39d4..bbc6dc0c6a8cf 100644 --- a/ext/zlib/tests/gzencode_variation1.phpt +++ b/ext/zlib/tests/gzencode_variation1.phpt @@ -26,8 +26,8 @@ $output = gzencode($data); var_dump(bin2hex(gzencode($output))); ?> ---EXPECT-- +--EXPECTF-- *** Testing gzencode() : variation *** -- Testing multiple compression -- -string(3658) "1f8b0800000000000003010e07f1f81f8b08000000000000036d574d6fe4c80dbdeb57d4ad2f3dfe01eb83e1ec22980e309b4562c067b64449159754dafab0b6e7d7e73d96da1e4c72184c4b2ab2c8f7c847fa25baabba98dc1a8b2b7c38bb324b713ee37f757f56cdc5c7f5b17b9d152f923b157c5ae335e0b75fedd0e2d781c6b98ea3a6ee05affe1dfc3a6527f8f09c52dcb38ba38bb5249934d6ecfe1e53a9ab76ff4c342cf2a64ed2028349fc9a8b139755685352acb82b9fbb67f8bade5cdcb698e1fcec94b7ceba3cb897e806cfc8114350dd1ebbdfa35b62d2478b0056d23ed809b9b95d696d91ce2aa97c911e3fa539c43f84c887554a4d125c9e63ff96711cc08c0866263cb37a0bbe2122ae8f6baecb2284abfb4ddf916db8354cddeef37c1afe5fa02fc7afb3db34f5b3acbdf2eb905490d8f38d7468d253a323d5ebb903760d7944d3b2024e834a99ddce77669bdd823cfbb8e899d4ad4c799677452e6029e80023a03b2374005590641f7d3877df2ad09f3c0e82a54d6a5644fd63049a37ed4bc362016fd9f51264f1e5c630727421ae930b7ed416e93e47b7c71a400390361ffbecb7561bb98f69b5da289e91becc27f08b3b724cb8704f9144d366431d0cb870c56b205deaa2e17636063761a911039fb7e4bf9f06c4f0aecd2ec80e8b41831ca7515e31286166458ea3ef71f2ce7cde2ae269c96d60525724a9c9170b713ed5750758f3cd2a361fc8b288fc92358ce884692e8ea0fe59bd969a0da2eed5831b715749eaae7178f3ebd30fb88c92105f367cce2c882955dc6bf8eca0d5d57540b3092894743ba0fd5b2dad021836191f1afc0bba14dde1642cb0b1aa6879c38907dcefa0720082b801bec61417469219175267dfa047df35b0bd1332001c28cdfafd3bcabe91e74368cdd8d8478e494c190e7ee90c67f2bde288e68ab6b15e883c995be4f8feb6c6dda4278e4f38578ddbdc7be36788daf0c3cb1d1819c73822f7000a0d1813fa94153b572315e51343b536bc64977dff163cebfd8418773261f524017e251fccc60ae29a5770ae097594d52e9c1229d87ce967a36401c46b69945afb249d101c9d420ffa9a123e232c20e76467d5d169202a2dd4c582949e013e745df7958d4b0cc4fd4377a737cd4feea7974070000f314d423e0634cb9a618fdf5dc64fd422181fd59c9230c9f6f9d18dc8fc23e9cccbc7188733b04aa57de83ebea0be3633cff5fa1ff83269be7f44f5a8d84550cc703255fd345dd402034d0b3e11a73ec6e3d4a77f4f685b614329f1b3132ae7af33d02e1e55e291fa6574b758d1f0200e7423dbc852211818043a7c9ce80aa9d59fce0401959f5ea2cf71fde90824f8c9192dbe9d329db143794675ddcf257dd7755273b67340414e3ccad12e3f661f8aad9cf9957dc1275d10a51d3934fa81e68dc6768fb8ee23e373936c8e13feab8b0f50d227f7af76f561fb0950f3d099bbc316c3892a42fb36806d8660e800fa4f43fd4b962d2097d71933a54b77ff948677848eb17bb3a88b621682cfb3bbb49cf42fed6b3944124ad8358ca688aa44dd5f2144c7c9ab16f25b9aca9654ef357ec9ad55c40d324d6cc3d9e3920b863c231d31a95d937fb5520f9c816c79b7dcecc593fb9593cc05a51ebb1eeddd5b49eb437769738d0f64adc579d372b8b7f7c0208487ee3915ebf5766e148ebd77cf4e01f3ec285047011e55838968b6494d517fe29224777b24dd3ddf933101695b102e87db805eef291b74dcfd91628fb2a53f93dbd2968ef2e598746c9204f89fba1f0246fc671610a0591806e46a1346f77c40d910a47c5e20ffb23f003c04b648327a4ed98032c1965bd35bb0044f5344248f56fdb99aa61d6451d68e33489a83bffbe6573541b2da5f64681ea12090f778b2075374778810f73965fa3626a9d41f4df2f83f7c34658cec921b5a9bde49dd5007ec882b02adc514f81aa85898b5cc98e1b137733c0a8789b7f5648d2d231b80bf74978f25d61ce08a8abd11801fd8f995e066676307192ff7641f1cc6e0dee68565b8b22ac3889cd067bf732754a6b270af1044c6a8776811a4f6d8bd0477a9f516064201b920b92d7cd4dc7eee13e6b3eb3528a82f9abb3f388ebe6a8f871393461b73816ec54c99d604174bc5a6801de13908f86aea6a7d0fea107d682bcf1ec348b83872e6b8a316ecd02eb8f8dc86a609bf59a2dd03f1dfa4079436d55e24617be1a2854d008b2b2b1705e2078a7f3946318df1c24f6bf70d4b456eca286ec2b585b28262cc048a098c3e2d5f325a92bb36f691afdc14c822da1b116c9c1c07bb362eb0a04b78834c812134230ebf2044ac2e3c0e3ad00f848dc5010f3bf917ec2fc700b7bf26dacea8440620e04f90f4d97d6dd77cfde8a05c7d3930f1e5811fb8ec5c70964dcc8187ec90e32fdd6b64eec7586413b7d55bed65c4cce39a9b6c15e70e9da94e53fc904e6286f01f5b5562c94211befbc23507e01b2a3865e2f45b5d7b591f290087a5605b82495b4e393f31aa5b37211ec40241a746d903c5eebf117a4d3ddb0d00007b64cbc70e070000" +string(36%d) "%s" diff --git a/ext/zlib/tests/gzinflate-bug42663.phpt b/ext/zlib/tests/gzinflate-bug42663.phpt index cdaf53fb85d15..afd18c62b804a 100644 --- a/ext/zlib/tests/gzinflate-bug42663.phpt +++ b/ext/zlib/tests/gzinflate-bug42663.phpt @@ -19,7 +19,7 @@ var_dump(gzinflate($truncated)); ?> --EXPECTF-- int(168890) -int(66743) +int(667%d) int(65535) Warning: gzinflate(): data error in %s on line %d diff --git a/ext/zlib/tests/zlib_filter_deflate.phpt b/ext/zlib/tests/zlib_filter_deflate.phpt index 7c78d82b33215..da764cad71909 100644 --- a/ext/zlib/tests/zlib_filter_deflate.phpt +++ b/ext/zlib/tests/zlib_filter_deflate.phpt @@ -13,5 +13,5 @@ fwrite($fp, $text); fclose($fp); ?> ---EXPECT-- -HctBDoAgDETRq8zOjfEeHKOGATG0TRpC4u1Vdn/xX4IoxkVMxgP1zA4vkJVhULk9UGkM6TvSNolmxUNlNLePVQ45O3eINf0fsQxtCxwv +--EXPECTF-- +HctB%s diff --git a/main/php_variables.c b/main/php_variables.c index 22d8ff73fe3a8..dc413a461b2f1 100644 --- a/main/php_variables.c +++ b/main/php_variables.c @@ -89,6 +89,21 @@ PHPAPI void php_register_known_variable(const char *var_name, size_t var_name_le php_register_variable_quick(var_name, var_name_len, value, symbol_table); } +/* Discard variable if mangling made it start with __Host-, where pre-mangling it did not start with __Host- + * Discard variable if mangling made it start with __Secure-, where pre-mangling it did not start with __Secure- */ +static bool php_is_forbidden_variable_name(const char *mangled_name, size_t mangled_name_len, const char *pre_mangled_name) +{ + if (mangled_name_len >= sizeof("__Host-")-1 && strncmp(mangled_name, "__Host-", sizeof("__Host-")-1) == 0 && strncmp(pre_mangled_name, "__Host-", sizeof("__Host-")-1) != 0) { + return true; + } + + if (mangled_name_len >= sizeof("__Secure-")-1 && strncmp(mangled_name, "__Secure-", sizeof("__Secure-")-1) == 0 && strncmp(pre_mangled_name, "__Secure-", sizeof("__Secure-")-1) != 0) { + return true; + } + + return false; +} + PHPAPI void php_register_variable_ex(const char *var_name, zval *val, zval *track_vars_array) { char *p = NULL; @@ -139,20 +154,6 @@ PHPAPI void php_register_variable_ex(const char *var_name, zval *val, zval *trac } var_len = p - var; - /* Discard variable if mangling made it start with __Host-, where pre-mangling it did not start with __Host- */ - if (strncmp(var, "__Host-", sizeof("__Host-")-1) == 0 && strncmp(var_name, "__Host-", sizeof("__Host-")-1) != 0) { - zval_ptr_dtor_nogc(val); - free_alloca(var_orig, use_heap); - return; - } - - /* Discard variable if mangling made it start with __Secure-, where pre-mangling it did not start with __Secure- */ - if (strncmp(var, "__Secure-", sizeof("__Secure-")-1) == 0 && strncmp(var_name, "__Secure-", sizeof("__Secure-")-1) != 0) { - zval_ptr_dtor_nogc(val); - free_alloca(var_orig, use_heap); - return; - } - if (var_len==0) { /* empty variable name, or variable name with a space in it */ zval_ptr_dtor_nogc(val); free_alloca(var_orig, use_heap); @@ -256,6 +257,12 @@ PHPAPI void php_register_variable_ex(const char *var_name, zval *val, zval *trac return; } } else { + if (php_is_forbidden_variable_name(index, index_len, var_name)) { + zval_ptr_dtor_nogc(val); + free_alloca(var_orig, use_heap); + return; + } + gpc_element_p = zend_symtable_str_find(symtable1, index, index_len); if (!gpc_element_p) { zval tmp; @@ -293,6 +300,12 @@ PHPAPI void php_register_variable_ex(const char *var_name, zval *val, zval *trac zval_ptr_dtor_nogc(val); } } else { + if (php_is_forbidden_variable_name(index, index_len, var_name)) { + zval_ptr_dtor_nogc(val); + free_alloca(var_orig, use_heap); + return; + } + zend_ulong idx; /* diff --git a/main/php_version.h b/main/php_version.h index 2093b37395a45..53858862da367 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 4 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.4-dev" -#define PHP_VERSION_ID 80304 +#define PHP_RELEASE_VERSION 5 +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.3.5" +#define PHP_VERSION_ID 80305 diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 86c517132482e..b1d0f00f4b1fa 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -40,6 +40,7 @@ # include "win32/time.h" # include "win32/ioutil.h" # include "win32/readdir.h" +# include #endif #define php_stream_fopen_from_fd_int(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC) @@ -353,11 +354,7 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun if (data->fd >= 0) { #ifdef PHP_WIN32 - ssize_t bytes_written; - if (ZEND_SIZE_T_UINT_OVFL(count)) { - count = UINT_MAX; - } - bytes_written = _write(data->fd, buf, (unsigned int)count); + ssize_t bytes_written = _write(data->fd, buf, (unsigned int)(count > INT_MAX ? INT_MAX : count)); #else ssize_t bytes_written = write(data->fd, buf, count); #endif diff --git a/sapi/fpm/fpm/fpm_shm.c b/sapi/fpm/fpm/fpm_shm.c index 386fef8c49fa2..7b27978084367 100644 --- a/sapi/fpm/fpm/fpm_shm.c +++ b/sapi/fpm/fpm/fpm_shm.c @@ -50,7 +50,7 @@ int fpm_shm_free(void *mem, size_t size) /* {{{ */ return 0; } - if (fpm_shm_size - size > 0) { + if (fpm_shm_size > size) { fpm_shm_size -= size; } else { fpm_shm_size = 0; diff --git a/sapi/fpm/fpm/fpm_stdio.c b/sapi/fpm/fpm/fpm_stdio.c index a225d3357dd99..55139a0e02ec1 100644 --- a/sapi/fpm/fpm/fpm_stdio.c +++ b/sapi/fpm/fpm/fpm_stdio.c @@ -153,7 +153,7 @@ int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ close(fpm_globals.error_log_fd); } fpm_globals.error_log_fd = -1; - zlog_set_fd(-1); + zlog_set_fd(-1, 0); return 0; } @@ -374,13 +374,14 @@ int fpm_stdio_open_error_log(int reopen) /* {{{ */ php_openlog(fpm_global_config.syslog_ident, LOG_PID | LOG_CONS, fpm_global_config.syslog_facility); fpm_globals.error_log_fd = ZLOG_SYSLOG; if (fpm_use_error_log()) { - zlog_set_fd(fpm_globals.error_log_fd); + zlog_set_fd(fpm_globals.error_log_fd, 0); } return 0; } #endif fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); + if (0 > fd) { zlog(ZLOG_SYSERROR, "failed to open error_log (%s)", fpm_global_config.error_log); return -1; @@ -393,7 +394,11 @@ int fpm_stdio_open_error_log(int reopen) /* {{{ */ } else { fpm_globals.error_log_fd = fd; if (fpm_use_error_log()) { - zlog_set_fd(fpm_globals.error_log_fd); + bool is_stderr = ( + strcmp(fpm_global_config.error_log, "/dev/stderr") == 0 || + strcmp(fpm_global_config.error_log, "/proc/self/fd/2") == 0 + ); + zlog_set_fd(fpm_globals.error_log_fd, is_stderr); } } if (0 > fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)) { diff --git a/sapi/fpm/fpm/zlog.c b/sapi/fpm/fpm/zlog.c index e5e49f8385d21..3903c6eb24178 100644 --- a/sapi/fpm/fpm/zlog.c +++ b/sapi/fpm/fpm/zlog.c @@ -25,6 +25,7 @@ #define EXTRA_SPACE_FOR_PREFIX 128 static int zlog_fd = -1; +static bool zlog_fd_is_stderr = false; static int zlog_level = ZLOG_NOTICE; static int zlog_limit = ZLOG_DEFAULT_LIMIT; static zlog_bool zlog_buffering = ZLOG_DEFAULT_BUFFERING; @@ -88,11 +89,13 @@ size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len) /* } /* }}} */ -int zlog_set_fd(int new_fd) /* {{{ */ +int zlog_set_fd(int new_fd, zlog_bool is_stderr) /* {{{ */ { int old_fd = zlog_fd; zlog_fd = new_fd; + zlog_fd_is_stderr = is_stderr; + return old_fd; } /* }}} */ @@ -244,7 +247,7 @@ void vzlog(const char *function, int line, int flags, const char *fmt, va_list a zend_quiet_write(zlog_fd > -1 ? zlog_fd : STDERR_FILENO, buf, len); } - if (zlog_fd != STDERR_FILENO && zlog_fd != -1 && + if (!zlog_fd_is_stderr && zlog_fd != -1 && !launched && (flags & ZLOG_LEVEL_MASK) >= ZLOG_NOTICE) { zend_quiet_write(STDERR_FILENO, buf, len); } diff --git a/sapi/fpm/fpm/zlog.h b/sapi/fpm/fpm/zlog.h index 679996041b32e..be22acc32f3ca 100644 --- a/sapi/fpm/fpm/zlog.h +++ b/sapi/fpm/fpm/zlog.h @@ -17,7 +17,7 @@ typedef unsigned char zlog_bool; #define ZLOG_FALSE 0 void zlog_set_external_logger(void (*logger)(int, char *, size_t)); -int zlog_set_fd(int new_fd); +int zlog_set_fd(int new_fd, zlog_bool is_stderr); int zlog_set_level(int new_value); int zlog_set_limit(int new_value); int zlog_set_buffering(zlog_bool buffering); diff --git a/sapi/fpm/tests/gh-11086-daemonized-logs-duplicated.phpt b/sapi/fpm/tests/gh-11086-daemonized-logs-duplicated.phpt new file mode 100644 index 0000000000000..5c435aafc2598 --- /dev/null +++ b/sapi/fpm/tests/gh-11086-daemonized-logs-duplicated.phpt @@ -0,0 +1,34 @@ +--TEST-- +FPM: gh68591 - daemonized mode duplicated logs +--SKIPIF-- + +--FILE-- +testConfig(dumpConfig: false, printOutput: true); + +?> +Done +--EXPECTF-- +%sNOTICE: configuration file %s test is successful +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc index 05a33737ebb9e..d5dbdd36ff1a2 100644 --- a/sapi/fpm/tests/tester.inc +++ b/sapi/fpm/tests/tester.inc @@ -440,12 +440,22 @@ class Tester * @return null|array * @throws \Exception */ - public function testConfig($silent = false, array|string|null $expectedPattern = null): ?array - { - $configFile = $this->createConfig(); - $cmd = self::findExecutable() . ' -n -tt -y ' . $configFile . ' 2>&1'; + public function testConfig( + $silent = false, + array|string|null $expectedPattern = null, + $dumpConfig = true, + $printOutput = false + ): ?array { + $configFile = $this->createConfig(); + $configTestArg = $dumpConfig ? '-tt' : '-t'; + $cmd = self::findExecutable() . " -n $configTestArg -y $configFile 2>&1"; $this->trace('Testing config using command', $cmd, true); exec($cmd, $output, $code); + if ($printOutput) { + foreach ($output as $outputLine) { + echo $outputLine . "\n"; + } + } $found = 0; if ($expectedPattern !== null) { $expectedPatterns = is_array($expectedPattern) ? $expectedPattern : [$expectedPattern];