diff --git a/.github/actions/setup-windows/action.yml b/.github/actions/setup-windows/action.yml index e0a3fd4e7d31..a5fa4cdddcb1 100644 --- a/.github/actions/setup-windows/action.yml +++ b/.github/actions/setup-windows/action.yml @@ -16,5 +16,6 @@ runs: - name: Setup PostgreSQL shell: pwsh run: | - Set-Service -Name "postgresql-x64-14" -StartupType manual -Status Running + $postgresService = if ($env:PHP_BUILD_CRT -eq "vs18") { "postgresql-x64-17" } else { "postgresql-x64-14" } + Set-Service -Name $postgresService -StartupType manual -Status Running pwsh -Command { $env:PGPASSWORD="root"; & "$env:PGBIN\psql" -U postgres -c "ALTER USER postgres WITH PASSWORD 'Password12!';" } diff --git a/.github/matrix.php b/.github/matrix.php index eb10492d17f5..7970442d7050 100644 --- a/.github/matrix.php +++ b/.github/matrix.php @@ -61,7 +61,6 @@ function select_jobs($repository, $trigger, $nightly, $labels, $php_version, $re $test_macos = in_array('CI: macOS', $labels, true); $test_msan = in_array('CI: MSAN', $labels, true); $test_opcache_variation = in_array('CI: Opcache Variation', $labels, true); - $test_pecl = in_array('CI: PECL', $labels, true); $test_solaris = in_array('CI: Solaris', $labels, true); $test_windows = in_array('CI: Windows', $labels, true); @@ -137,9 +136,6 @@ function select_jobs($repository, $trigger, $nightly, $labels, $php_version, $re if ($all_jobs || $test_opcache_variation) { $jobs['OPCACHE_VARIATION'] = true; } - if (($all_jobs && $ref === 'master') || $test_pecl) { - $jobs['PECL'] = true; - } if (version_compare($php_version, '8.6', '>=') && ($all_jobs || $test_solaris)) { $jobs['SOLARIS'] = true; } @@ -153,9 +149,11 @@ function select_jobs($repository, $trigger, $nightly, $labels, $php_version, $re } } $jobs['WINDOWS']['matrix'] = ['include' => $matrix]; - $jobs['WINDOWS']['config'] = version_compare($php_version, '8.4', '>=') - ? ['vs_crt_version' => 'vs17'] - : ['vs_crt_version' => 'vs16']; + $jobs['WINDOWS']['config'] = match (true) { + version_compare($php_version, '8.6', '>=') => ['vs_crt_version' => 'vs18', 'runs_on' => 'windows-2025-vs2026'], + version_compare($php_version, '8.4', '>=') => ['vs_crt_version' => 'vs17', 'runs_on' => 'windows-2022'], + default => ['vs_crt_version' => 'vs16', 'runs_on' => 'windows-2022'], + }; } if ($all_jobs || !$no_jobs || $test_freebsd) { $jobs['FREEBSD']['matrix'] = $all_variations && version_compare($php_version, '8.3', '>=') diff --git a/.github/scripts/download-bundled/uriparser.sh b/.github/scripts/download-bundled/uriparser.sh index 8820a67333f0..2b8d61a2d56f 100755 --- a/.github/scripts/download-bundled/uriparser.sh +++ b/.github/scripts/download-bundled/uriparser.sh @@ -5,7 +5,7 @@ cd "$(dirname "$0")/../../.." tmp_dir=/tmp/php-src-download-bundled/uriparser rm -rf "$tmp_dir" -revision=refs/tags/uriparser-1.0.0 +revision=refs/tags/uriparser-1.0.2 git clone --depth 1 --revision="$revision" https://github.com/uriparser/uriparser.git "$tmp_dir" diff --git a/.github/scripts/windows/find-vs-toolset.bat b/.github/scripts/windows/find-vs-toolset.bat index 2d9e68e73031..ecaca6775b07 100644 --- a/.github/scripts/windows/find-vs-toolset.bat +++ b/.github/scripts/windows/find-vs-toolset.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion if "%~1"=="" ( - echo ERROR: Usage: %~nx0 [vc14^|vc15^|vs16^|vs17] + echo ERROR: Usage: %~nx0 [vc14^|vc15^|vs16^|vs17^|vs18] exit /b 1 ) @@ -11,6 +11,7 @@ set "toolsets_vc14=14.0" set "toolsets_vc15=" set "toolsets_vs16=" set "toolsets_vs17=" +set "toolsets_vs18=" for /f "usebackq tokens=*" %%I in (`vswhere.exe -latest -find "VC\Tools\MSVC"`) do set "MSVCDIR=%%I" @@ -30,8 +31,10 @@ for /f "delims=" %%D in ('dir /b /ad "%MSVCDIR%"') do ( set "toolsets_vc15=%%D" ) else if !min! LEQ 29 ( set "toolsets_vs16=%%D" - ) else ( + ) else if !min! LEQ 49 ( set "toolsets_vs17=%%D" + ) else ( + set "toolsets_vs18=%%D" ) ) ) diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 4905dcb9ccbc..7b1850aa1075 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -827,126 +827,18 @@ jobs: uses: ./.github/actions/test-libmysqlclient - name: Verify generated files are up to date uses: ./.github/actions/verify-generated-files - PECL: - if: ${{ fromJson(inputs.branch).jobs.PECL }} - runs-on: ubuntu-24.04 - steps: - - name: git checkout PHP - uses: actions/checkout@v6 - with: - path: php - ref: ${{ fromJson(inputs.branch).ref }} - # Used for ccache action - - name: Move .github - run: mv php/.github . - - name: git checkout apcu - uses: actions/checkout@v6 - with: - repository: krakjoe/apcu - path: apcu - - name: git checkout imagick - uses: actions/checkout@v6 - with: - repository: Imagick/imagick - path: imagick - - name: git checkout memcached - uses: actions/checkout@v6 - with: - repository: php-memcached-dev/php-memcached - path: memcached - - name: git checkout redis - if: ${{ false }} - uses: actions/checkout@v6 - with: - repository: phpredis/phpredis - path: redis - - name: git checkout xdebug - uses: actions/checkout@v6 - with: - repository: xdebug/xdebug - path: xdebug - - name: git checkout yaml - uses: actions/checkout@v6 - with: - repository: php/pecl-file_formats-yaml - path: yaml - - name: apt - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends \ - ccache \ - libmemcached-dev \ - imagemagick \ - libmagickwand-dev \ - bison \ - re2c - - name: ccache - uses: ./.github/actions/ccache - with: - name: "${{ github.job }}" - php_directory: php - - name: build PHP - run: | - cd php - ./buildconf --force - ./configure \ - --enable-option-checking=fatal \ - --prefix=/opt/php \ - --enable-cli \ - --disable-all \ - --enable-session \ - --enable-werror - make -j$(/usr/bin/nproc) - sudo make install - - name: build apcu - run: | - cd apcu - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) - - name: build imagick - run: | - cd imagick - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) - - name: build memcached - run: | - cd memcached - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) - - name: build redis - if: ${{ false }} - run: | - cd redis - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) - - name: build xdebug - run: | - cd xdebug - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) - - name: build yaml - run: | - cd yaml - /opt/php/bin/phpize - ./configure --prefix=/opt/php --with-php-config=/opt/php/bin/php-config - make -j$(/usr/bin/nproc) WINDOWS: if: ${{ fromJson(inputs.branch).jobs.WINDOWS }} strategy: fail-fast: false matrix: ${{ fromJson(inputs.branch).jobs.WINDOWS.matrix }} name: "WINDOWS_${{ matrix.x64 && 'X64' || 'X86' }}_${{ matrix.zts && 'ZTS' || 'NTS' }}${{ matrix.asan && '_ASAN' || ''}}${{ matrix.clang && '_CLANG' || ''}}" - runs-on: windows-2022 + runs-on: ${{ fromJson(inputs.branch).jobs.WINDOWS.config.runs_on }} env: PHP_BUILD_CACHE_BASE_DIR: C:\build-cache PHP_BUILD_OBJ_DIR: C:\obj PHP_BUILD_CACHE_SDK_DIR: C:\build-cache\sdk - PHP_BUILD_SDK_BRANCH: php-sdk-2.5.0 + PHP_BUILD_SDK_BRANCH: php-sdk-2.7.1 PHP_BUILD_CRT: ${{ fromJson(inputs.branch).jobs.WINDOWS.config.vs_crt_version }} PLATFORM: ${{ matrix.x64 && 'x64' || 'x86' }} THREAD_SAFE: "${{ matrix.zts && '1' || '0' }}" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0e13285dd735..e6bff3d6a9ca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,7 +91,7 @@ repository. Mailing list subscription is explained on the [mailing lists page](https://www.php.net/mailing-lists.php). You may also want to read -[The Mysterious PHP RFC Process](https://blogs.oracle.com/opal/post/the-mysterious-php-rfc-process-and-how-you-can-change-the-web) +[The Mysterious PHP RFC Process](https://web.archive.org/web/20210621140006/https://blogs.oracle.com/opal/the-mysterious-php-rfc-process-and-how-you-can-change-the-web) for additional notes on the best way to approach submitting an RFC. ## Technical resources diff --git a/NEWS b/NEWS index 5be5ffd18ac4..4fbc7e89eb11 100644 --- a/NEWS +++ b/NEWS @@ -18,10 +18,16 @@ PHP NEWS initialization). (Arnaud) . Enabled the TAILCALL VM on Windows when compiling with Clang >= 19 x86_64. (henderkes) + . Deprecate specifying a nullable return type for __debugInfo(). (timwolla) + . Fixed bug GH-22142 (Assertion failure in zendi_try_get_long() on IS_UNDEF). + (David Carlier) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) +- BZ2: + . Reject oversized input in bzdecompress(). (arshidkv12) + - Date: . Update timelib to 2022.16. (Derick) @@ -38,6 +44,10 @@ PHP NEWS values, and textContent returns NULL per the DOM specification. (jordikroon) +- EXIF: + . Added support for reading EXIF metadata from WebP images (GH-19904). + (iliaal) + - Fileinfo: . Fixed bug GH-20679 (finfo_file() doesn't work on remote resources). (ndossche) @@ -51,17 +61,29 @@ PHP NEWS . gmp_fact() reject values larger than unsigned long. (David Carlier) . gmp_pow/binomial/root/rootrem and shift/pow operators reject values larger than unsigned long. (David Carlier) + . GMP exponentiation and shift operators now emit a deprecation warning + when converting a float right operand to int loses precision. (Weilin Du) - Hash: . Upgrade xxHash to 0.8.2. (timwolla) - Intl: + . Fixed malformed ResourceBundle::get() error message when fallback is + disabled. (Weilin Du) . Added IntlNumberRangeFormatter class to format an interval of two numbers with a given skeleton, locale, collapse type and identity fallback. (BogdanUngureanu) . Fixed bug GH-20426 (Spoofchecker::setRestrictionLevel() error message suggests missing constants). (DanielEScherzer) . Added grapheme_strrev (Yuya Hamada) + . Passing a non-stringable object as a time zone to Intl time zone + argument handling now raises TypeError instead of Error. (Weilin Du) + . IntlBreakIterator::getLocale() now raises ValueError for invalid locale + types. (Weilin Du) + . Fixed MessageFormatter::parse() and parseMessage() returning PHP_INT_MIN + as float rather than int on 64-bit platforms. (Weilin Du) + . Fixed UConverter::transcode() silently truncating from_subst and to_subst + option lengths greater than 127 bytes. (Weilin Du) - JSON: . Enriched JSON last error / exception message with error location. @@ -83,6 +105,7 @@ PHP NEWS recursive array references). (alexandre-daubois) . Fixed bug GH-21223; mb_guess_encoding no longer crashes when passed huge list of candidate encodings (with 200,000+ entries). (Jordi Kroon) + . mbregex has been deprecated. (youkidearitai) - Mysqli: . Added mysqli_quote_string() and mysqli::quote_string(). (Kamil Tekiela) @@ -95,6 +118,19 @@ PHP NEWS . Added AES-SIV support. (jordikroon) . Implemented GH-20310 (No critical extension indication in openssl_x509_parse() output). (StephenWall) + . Added TLS session resumption support for streams with new context options + and Openssl\Session class. (Jakub Zelenka) + . Added TLS external PSK support for streams with new context options and + Openssl\Psk class. (Jakub Zelenka) + . Added stream crypto status for exposing OpenSSL WANT_READ / WANT_WRITE. + (Jakub Zelenka) + +- PCNTL: + . pcntl_exec() now throws a ValueError if the $args array is not a list + array. (Weilin Du) + +- PDO_DBLIB; + . Added dblib_handle_check_liveness handler. (freddy77) - PDO_PGSQL: . Clear session-local state disconnect-equivalent processing. @@ -103,11 +139,19 @@ PHP NEWS - PGSQL: . Enabled 64 bits support for pg_lo_truncate()/pg_lo_tell() if the server supports it. (KentarouTakeda) + . pg_fetch_object() now surfaces non-instantiable class errors + before fetching, resolves the constructor via the get_constructor + handler, and reports the empty-constructor ValueError on the + $constructor_args argument. (David Carlier) - Phar: . Support reference values in Phar::mungServer(). (ndossche) . Invalid values now throw in Phar::mungServer() instead of being silently ignored. (ndossche) + . Fixed a bypass of the magic ".phar" directory protection in + Phar::addEmptyDir() for paths starting with "/.phar". (Weilin Du) + . Phar::addEmptyDir() now allows non-magic directory names that merely + share the ".phar" prefix. (Weilin Du) . Support overridden methods in SplFileInfo for getMTime() and getPathname() when building a phar. (ndossche) . Mark Phar::buildFromIterator() base directory argument as a path. @@ -129,12 +173,25 @@ PHP NEWS - Session: . Fixed bug 71162 (updateTimestamp never called when session data is empty). (Girgias) + . Null bytes in session.cookie_path, session.cookie_domain, and + session.cache_limiter are now rejected with a warning. (jorgsowa) + . session.cookie_samesite now rejects invalid values with a warning; only + "Strict", "Lax", "None", or "" are accepted. (jorgsowa) + . session.cookie_lifetime now rejects non-integer and out-of-range values + with a warning. (jorgsowa) + . Session file GC now recursively cleans nested subdirectories when + session.save_path uses the dirdepth prefix. (jorgsowa) + . Changed defaults of session.use_strict_mode (now 1), session.cookie_httponly + (now 1) and session.cookie_samesite (now "Lax"). (jorgsowa) - Soap: . Soap::__setCookie() when cookie name is a digit is now not stored and represented as a string anymore but a int. (David Carlier) . Fixed bug GH-21421 (SoapClient typemap property breaks engine assumptions). (ndossche) + . WSDL/XML Schema parsing now rejects out-of-range integer values for + occurrence constraints and integer restriction facets. Negative minOccurs + and maxOccurs values are rejected as well. (Weilin Du) - Sockets: . Added the TCP_USER_TIMEOUT constant for Linux to set the maximum time in @@ -150,8 +207,12 @@ PHP NEWS - SPL: . DirectoryIterator key can now work better with filesystem supporting larger directory indexing. (David Carlier) + . Fixed bug GH-21831 (SplObjectStorage::removeAllExcept() use-after-free + with re-entrant getHash()). (Pratik Bhujel) . Fix bugs GH-8561, GH-8562, GH-8563, and GH-8564 (Fixing various SplFileObject iterator desync bugs). (iliaal) + . Fix bug GH-22062 (SplDoublyLinkedList iterator UAF + via destructor releasing next node). (David Carlier) - Sqlite3: . Fix NUL byte truncation in sqlite3 TEXT column handling. (ndossche) @@ -179,12 +240,25 @@ PHP NEWS (Weilin Du) . getenv() and putenv() now raises a ValueError when the first argument contains null bytes. (Weilin Du) + . parse_str() now raises a ValueError when the $string argument contains + null bytes. (Weilin Du) + . proc_open() now raises a ValueError when the $cwd argument contains + null bytes. (Weilin Du) + . ini_get_all() now includes the built-in default value in the details. + (sebastian) + . Fixed bug GH-22171 (Invalid auth header generation in + http(s) stream wrapper). (David Carlier) - Streams: + . Added new stream errors API including new StreamException, StreamError + classes, StreamErrorStore, StreamErrorMode, StreamErrorCode enums, + stream_last_errors() and stream_clear_errors() functions, error_mode, + error_store and error_handler stream context options and extending some + stream functions with context param. (Jakub Zelenka) . Added so_keepalive, tcp_keepidle, tcp_keepintvl and tcp_keepcnt stream - socket context options. + socket context options. (Jakub Zelenka) . Added so_reuseaddr streams context socket option that allows disabling - address reuse. + address reuse. (Jakub Zelenka) . Fixed bug GH-20370 (User stream filters could violate typed property constraints). (alexandre-daubois) . Allowed filtered streams to be casted as fd for select. (Jakub Zelenka) @@ -194,6 +268,12 @@ PHP NEWS . Fixed bug #49874 (ftell() and fseek() inconsistency when using stream filters). (Jakub Zelenka) +- URI: + . Added Uri\Rfc3986\Uri:getUriType() and Uri\WhatWg\Url:isSpecialScheme(). + (kocsismate) + . Added Uri\Rfc3986\Uri:getHostType() and Uri\WhatWg\Url:getHostType(). + (kocsismate) + - Zip: . Fixed ZipArchive callback being called after executor has shut down. (ilutov) @@ -204,6 +284,9 @@ PHP NEWS - Zlib: . deflate_init() now raises a TypeError when the value for option - "strategy" is not of type int. (Weilin Du) + "level", "memory", "window", or "strategy" is not of type int. + (Weilin Du) + . inflate_init() now raises a TypeError when the value for option + "window" is not of type int. (Weilin Du) <<< NOTE: Insert NEWS from last stable release here prior to actual release! >>> diff --git a/SECURITY.md b/SECURITY.md index deb5a7a950a4..24801b3b4e43 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,6 +11,31 @@ Vulnerability reports remain private until published. When published, you will be credited as a contributor, and your contribution will reflect the MITRE Credit System. +# Classification + +Issues commonly reported that are _not_ considered security issues include (but +are not limited to): + +- Invocation of specially crafted, malicious code intended to cause memory + violations. This commonly includes malicious error handlers, destructors or + `__toString()` functions. PHP does not offer sandboxing, and the execution of + untrusted code is always considered unsafe. Such issues are bugs, but not + security issues. They may still be reported, though please avoid reporting + the known issues. + +- Passing malicious arguments to functions clearly not intended to receive + unsanitized values, e.g. `mysqli_query()`. `escapeshellarg()` on the other + hand should clearly be hardened against unsafe inputs. + +- The use of legacy APIs or settings known to be insecure, particularly those + documented as such, or those with a secure alternative. + +- The use of FFI. + +- `open_basedir` or `disable_functions` bypasses. + +- Malicious `unserialize()` inputs. + # Vulnerability Policy Our full policy is described at diff --git a/UPGRADING b/UPGRADING index 6c6115ebcc84..2ec6ce92aa52 100644 --- a/UPGRADING +++ b/UPGRADING @@ -34,9 +34,28 @@ PHP 8.6 UPGRADE NOTES array arguments types/values and raise a TypeError/ValueError accordingly. +- Intl: + . Passing a non-stringable object as a time zone to Intl APIs that accept + time zone objects or strings now raises a TypeError instead of an Error. + . IntlBreakIterator::getLocale() now raises a ValueError when the type is + neither Locale::ACTUAL_LOCALE nor Locale::VALID_LOCALE instead of + returning false. + . MessageFormatter::parse() and parseMessage() now return PHP_INT_MIN as + int, rather than float, on 64-bit platforms when parsing integer values. + . The $type parameter of IntlBreakIterator::getPartsIterator() has been + changed from string to int to match the underlying implementation. + . UConverter::transcode() now rejects from_subst and to_subst option values + longer than 127 bytes instead of silently truncating the length before + passing it to ICU. + . ResourceBundle::get() and resourcebundle_get() now report fallback-disabled + resource lookups with "without fallback to " instead of the + malformed "without fallback from to ". + - PCNTL: . pcntl_alarm() now raises a ValueError if the seconds argument is lower than zero or greater than platform's UINT_MAX. + . pcntl_exec() now raises a ValueError if the $args argument is not a list + array. - PCRE: . preg_grep() now returns false instead of a partial array when a PCRE @@ -46,6 +65,18 @@ PHP 8.6 UPGRADE NOTES - Phar: . Phar::mungServer() now raises a ValueError when an invalid argument value is passed instead of being silently ignored. + . Phar::addEmptyDir() now rejects `/.phar` paths in addition to `.phar` + paths, and raises the same BadMethodCallException for attempts to create + the reserved magic ".phar" directory through that form. + . Phar::addEmptyDir() now treats non-magic names that merely share the + `.phar` prefix as ordinary directories. + +- PGSQL: + . pg_fetch_object() now reports the ValueError for a non-empty + $constructor_args on a class without a constructor on the + $constructor_args argument instead of $class. Errors raised when + the requested class is not instantiable (abstract, interface, enum) + now surface before the row is fetched. - Posix: . posix_access() now raises a ValueError when an invalid $flags @@ -54,6 +85,11 @@ PHP 8.6 UPGRADE NOTES argument value is passed. - Session: + . Setting session.cookie_path, session.cookie_domain, or session.cache_limiter + to a value containing null bytes now emits a warning and leaves the setting + unchanged. Previously, null bytes were silently accepted: for cookie_path and + cookie_domain this caused the SAPI to drop the Set-Cookie header; for + cache_limiter the value was silently truncated at the null byte. . A ValueError is not thrown if $name is a string containing null bytes in session_module_name(). . session_encode() now returns an empty string instead of false for empty @@ -68,8 +104,33 @@ PHP 8.6 UPGRADE NOTES comparison. Custom session handlers that rely on write() being called with empty data (e.g. to destroy the session) should implement the same logic in their updateTimestamp() method. + . The defaults of three session INI settings have changed to provide secure + behavior out of the box: + - session.use_strict_mode is now 1 (was 0). Strict mode rejects + uninitialized session IDs, mitigating session fixation. Custom session + handlers that previously relied on accepting externally supplied IDs + without a corresponding storage entry must either implement + validateId() / create_sid() or explicitly set this to 0. + - session.cookie_httponly is now 1 (was 0). Session cookies are no + longer accessible to JavaScript via document.cookie. Applications + that read the session cookie from JavaScript must explicitly set + this to 0. + - session.cookie_samesite is now "Lax" (was unset). Session cookies + are no longer sent on cross-site requests other than top-level + navigations using safe HTTP methods. Applications that depend on + session cookies being sent on cross-site POST submissions must + explicitly set this to "None" (and also set session.cookie_secure + to 1). + RFC: https://wiki.php.net/rfc/session_security_defaults + +- SOAP: + . WSDL/XML Schema parsing now rejects out-of-range integer values for + occurrence constraints and integer restriction facets. Negative minOccurs + and maxOccurs values are rejected as well. - SPL: + . SplObjectStorage::getHash() implementations may no longer mutate any + SplObjectStorage instance. Attempting to do so now throws an Error. . SplFileObject::next() now advances the stream when no prior current() call has cached a line. A subsequent current() call returns the new line rather than the previous one. @@ -90,11 +151,15 @@ PHP 8.6 UPGRADE NOTES argument value is passed. . getenv() and putenv() now raises a ValueError when the first argument contains null bytes. + . parse_str() now raises a ValueError when the $string argument contains + null bytes. . linkinfo() now raises a ValueError when the $path argument is empty. . pathinfo() now raises a ValueError when an invalid $flag argument value is passed. . scandir() now raises a ValueError when an invalid $sorting_order argument value is passed. + . proc_open() now raises a ValueError when the $cwd argument contains + null bytes. - Zip: . ZipArchive::extractTo now raises a TypeError for the @@ -103,7 +168,9 @@ PHP 8.6 UPGRADE NOTES - Zlib: . deflate_init() now raises a TypeError when the value for option - "strategy" is not of type int. + "level", "memory", "window", or "strategy" is not of type int. + . inflate_init() now raises a TypeError when the value for option + "window" is not of type int. ======================================== 2. New Features @@ -112,6 +179,8 @@ PHP 8.6 UPGRADE NOTES - Core: . It is now possible to use reference assign on WeakMap without the key needing to be present beforehand. + . It is now possible to define the `__debugInfo()` magic method on enums. + RFC: https://wiki.php.net/rfc/debugable-enums - Fileinfo: . finfo_file() now works with remote streams. @@ -132,12 +201,27 @@ PHP 8.6 UPGRADE NOTES . Added extra info about error location to the JSON error messages returned from json_last_error_msg() and JsonException message. +- OpenSSL: + . Added TLS session resumption support for streams with new stream context + options: session_data, session_new_cb, session_cache, session_cache_size, + session_timeout, session_id_context, session_get_cb, session_remove_cb, + and num_tickets. This allows saving and restoring client sessions across + requests, implementing custom server-side session storage, and controlling + session cache behavior. + RFC: https://wiki.php.net/rfc/tls_session_resumption + . Added TLS external PSK support for streams with new strem context options: + psk_client_cb and psk_server_cb. This allows setting and receiving PSK. + - Phar: . Overriding the getMTime() and getPathname() methods of SplFileInfo now influences the result of the phar buildFrom family of functions. This makes it possible to override the timestamp and names of files. - Streams: + . Added new stream errors API including new classes, enums, functions and + internal API. It is controlled using error_mode, error_store and + error_handler stream context options. + RFC: https://wiki.php.net/rfc/stream_errors . Added stream socket context option so_reuseaddr that allows disabling address reuse (SO_REUSEADDR) and explicitly uses SO_EXCLUSIVEADDRUSE on Windows. @@ -146,6 +230,12 @@ PHP 8.6 UPGRADE NOTES options. . Allowed casting casting filtered streams as file descriptor for select. +- URI: + . Added Uri\Rfc3986\Uri:getUriType() and Uri\WhatWg\Url:isSpecialScheme(). + RFC: https://wiki.php.net/rfc/uri_followup#uri_type_detection + . Added Uri\Rfc3986\Uri:getHostType() and Uri\WhatWg\Url:getHostType(). + RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection + ======================================== 3. Changes in SAPI modules ======================================== @@ -154,6 +244,20 @@ PHP 8.6 UPGRADE NOTES 4. Deprecated Functionality ======================================== +- Core: + . Specifying a return type of array|null / ?array for __debugInfo() is now + deprecated. Specify array instead. + +- GMP + . The shift (<<, >>) and exponentiation (**) operators on GMP objects now + emit a deprecation warning when converting a float right operand to int + loses precision. + +- Mbstring: + . Mbregex has been deprecated, because the underlying Oniguruma library + is no longer maintained. + RFC: https://wiki.php.net/rfc/eol-oniguruma + ======================================== 5. Changed Functions ======================================== @@ -179,6 +283,10 @@ PHP 8.6 UPGRADE NOTES . Output of openssl_x509_parse() contains criticalExtensions listing all critical certificate extensions. +- PDO_DBLIB: + . When using persistent connections, there is now a liveness check in the + constructor. + - Phar: . Phar::mungServer() now supports reference values. @@ -187,6 +295,12 @@ PHP 8.6 UPGRADE NOTES when not null, and on failure, gives the error code (one of the EAI_* constants). +- Standard: + . ini_get_all() now includes a "builtin_default_value" element for each + directive when $details is true. It holds the built-in default value of + the directive (or null if it has none), independent of values set in + php.ini, on the command line, or at runtime. + ======================================== 6. New Functions ======================================== @@ -210,17 +324,33 @@ PHP 8.6 UPGRADE NOTES . `clamp()` returns the given value if in range, else return the nearest bound. RFC: https://wiki.php.net/rfc/clamp_v2 + . `stream_last_errors()` and `stream_clear_errors()`. + RFC: https://wiki.php.net/rfc/stream_errors + . stream_socket_get_crypto_status() - Zip: . Added ZipArchive::openString() method. + . Added ZipArchive::closeString() method. ======================================== 7. New Classes and Interfaces ======================================== +- OpenSSL: + . Openssl\OpensslException + . Openssl\Session + RFC: https://wiki.php.net/rfc/tls_session_resumption + . Openssl\Psk + - Standard: . enum SortDirection RFC: https://wiki.php.net/rfc/sort_direction_enum + . StreamError + . StreamException + . enum StreamErrorStore + . enum StreamErrorMode + . enum StreamErrorCode + RFC: https://wiki.php.net/rfc/stream_errors ======================================== 8. Removed Extensions and SAPIs @@ -263,6 +393,9 @@ PHP 8.6 UPGRADE NOTES - Standard . ARRAY_FILTER_USE_VALUE. + . STREAM_CRYPTO_STATUS_NONE + . STREAM_CRYPTO_STATUS_WANT_READ + . STREAM_CRYPTO_STATUS_WANT_WRITE ======================================== 11. Changes to INI File Handling @@ -322,13 +455,18 @@ PHP 8.6 UPGRADE NOTES - Standard: . Improved performance of array_fill_keys(). . Improved performance of array_map() with multiple arrays passed. + . Improved performance of array_sum() and array_product() for + integer-only arrays. . Improved performance of array_unshift(). . Improved performance of array_walk(). . Improved performance of intval('+0b...', 2) and intval('0b...', 2). . Improved performance of str_split(). - URI: - . Reduced allocations when reading RFC3986 IPv6/IPFuture hosts and paths. + . Reduced allocations when reading IPv6/IPFuture hosts and paths with + Uri\Rfc3986\Uri. + . Improved performance and memory consumption when using normalizing + (non-raw) getters on already-normalized URIs with Uri\Rfc3986\Uri. - Zip: . Avoid string copies in ZipArchive::addFromString(). diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index cfeaf9c30afe..c340ba64833c 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -96,6 +96,20 @@ PHP 8.6 INTERNALS UPGRADE NOTES and ZEND_PARSE_PARAMS_THROW have been removed due to being misleading, since ZPP always throws, unless ZEND_PARSE_PARAMS_QUIET is given. Use the non-throw versions. + . The XtOffsetOf() alias of C’s offsetof() macro has been removed. Use + offsetof() directly. + . The deprecated Z_COPYABLE(), Z_COPYABLE_P(), Z_OPT_COPYABLE(), and + Z_OPT_COPYABLE_P() macros have been removed. Check for IS_ARRAY directly. + . The deprecated Z_IMMUTABLE(), Z_IMMUTABLE_P(), Z_OPT_IMMUTABLE(), and + Z_OPT_IMMUTABLE_P() macros have been removed. Check for + IS_ARRAY && !REFCOUNTED directly. + . Added zend_fcall_info.consumed_args together with + zend_fci_consumed_arg(), which allows moving a selected callback argument + instead of copying it in zend_call_function(). Currently only a single + consumed argument is supported. + . Added ZEND_CONTAINER_OF(). + . The OPENBASEDIR_CHECKPATH() compatibility macro has been removed, instead + use php_check_open_basedir() directly. ======================== 2. Build system changes diff --git a/Zend/Optimizer/block_pass.c b/Zend/Optimizer/block_pass.c index c85b444640a8..02c28ead33e1 100644 --- a/Zend/Optimizer/block_pass.c +++ b/Zend/Optimizer/block_pass.c @@ -344,59 +344,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array } break; -#if 0 - /* pre-evaluate functions: - constant(x) - function_exists(x) - extension_loaded(x) - BAD: interacts badly with Accelerator - */ - if((opline->op1_type & IS_VAR) && - VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL && - VAR_SOURCE(opline->op1)->extended_value == 1) { - zend_op *fcall = VAR_SOURCE(opline->op1); - zend_op *sv = fcall-1; - if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL && - sv->op1_type == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING && - Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1 - ) { - zval *arg = &OPLINE_OP1_LITERAL(sv); - char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name; - size_t flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len; - if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) || - (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0) - ) { - zend_function *function; - if((function = zend_hash_find_ptr(EG(function_table), Z_STR_P(arg))) != NULL) { - literal_dtor(arg); - MAKE_NOP(sv); - MAKE_NOP(fcall); - LITERAL_BOOL(opline->op1, 1); - opline->op1_type = IS_CONST; - } - } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) { - zval c; - if (zend_optimizer_get_persistent_constant(Z_STR_P(arg), &c, true ELS_CC)) { - literal_dtor(arg); - MAKE_NOP(sv); - MAKE_NOP(fcall); - ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c); - /* no copy ctor - get already copied it */ - opline->op1_type = IS_CONST; - } - } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) { - if(zend_hash_exists(&module_registry, Z_STR_P(arg))) { - literal_dtor(arg); - MAKE_NOP(sv); - MAKE_NOP(fcall); - LITERAL_BOOL(opline->op1, 1); - opline->op1_type = IS_CONST; - } - } - } - } -#endif - case ZEND_FETCH_LIST_R: case ZEND_FETCH_LIST_W: if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index b7b118c710c5..2a9353c9b86e 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -596,6 +596,7 @@ static const func_info_t func_infos[] = { F1("stream_get_line", MAY_BE_STRING|MAY_BE_FALSE), F1("stream_resolve_include_path", MAY_BE_STRING|MAY_BE_FALSE), F1("stream_get_wrappers", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), + FN("stream_last_errors", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_OBJECT), F1("stream_get_transports", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), #if defined(HAVE_GETTIMEOFDAY) F1("uniqid", MAY_BE_STRING), diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index 59a87823eb56..d10b4d83fc3e 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -780,7 +780,7 @@ static bool zend_optimizer_ignore_class(zval *ce_zv, const zend_string *filename if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) { return true; } - const Bucket *ce_bucket = (const Bucket*)((uintptr_t)ce_zv - XtOffsetOf(Bucket, val)); + const Bucket *ce_bucket = ZEND_CONTAINER_OF(ce_zv, Bucket, val); size_t offset = ce_bucket - EG(class_table)->arData; if (offset < EG(persistent_classes_count)) { return false; @@ -801,7 +801,7 @@ static bool zend_optimizer_ignore_function(zval *fbc_zv, const zend_string *file if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) { return true; } - const Bucket *fbc_bucket = (const Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val)); + const Bucket *fbc_bucket = ZEND_CONTAINER_OF(fbc_zv, Bucket, val); size_t offset = fbc_bucket - EG(function_table)->arData; if (offset < EG(persistent_functions_count)) { return false; @@ -943,7 +943,7 @@ zend_function *zend_optimizer_get_called_func( if (ce) { zend_string *func_name = Z_STR_P(CRT_CONSTANT(opline->op2) + 1); zend_function *fbc = zend_hash_find_ptr(&ce->function_table, func_name); - if (fbc) { + if (fbc && !(fbc->common.fn_flags & ZEND_ACC_ABSTRACT)) { bool is_public = (fbc->common.fn_flags & ZEND_ACC_PUBLIC) != 0; bool same_scope = fbc->common.scope == op_array->scope; if (is_public || same_scope) { diff --git a/Zend/asm/save_xmm_x86_64_ms_masm.asm b/Zend/asm/save_xmm_x86_64_ms_masm.asm index 1569d6bdb0e8..0af7c346095d 100644 --- a/Zend/asm/save_xmm_x86_64_ms_masm.asm +++ b/Zend/asm/save_xmm_x86_64_ms_masm.asm @@ -9,34 +9,34 @@ EXTERN execute_ex_real:PROC ; save the preserved registers when re-entering the VM from JIT code. ; See GH-18136. execute_ex PROC EXPORT FRAME - ; 10 floating points numbers + ; 10 XMM registers ; 32 bytes shadow space ; 8 bytes to align after the return address - sub rsp, 8*10 + 32 + 8 - .allocstack 8*10 + 32 + 8 + sub rsp, 16*10 + 32 + 8 + .allocstack 16*10 + 32 + 8 .endprolog - movsd qword ptr [rsp + 32 + 8*0], xmm6 - movsd qword ptr [rsp + 32 + 8*1], xmm7 - movsd qword ptr [rsp + 32 + 8*2], xmm8 - movsd qword ptr [rsp + 32 + 8*3], xmm9 - movsd qword ptr [rsp + 32 + 8*4], xmm10 - movsd qword ptr [rsp + 32 + 8*5], xmm11 - movsd qword ptr [rsp + 32 + 8*6], xmm12 - movsd qword ptr [rsp + 32 + 8*7], xmm13 - movsd qword ptr [rsp + 32 + 8*8], xmm14 - movsd qword ptr [rsp + 32 + 8*9], xmm15 + movaps xmmword ptr [rsp + 32 + 16*0], xmm6 + movaps xmmword ptr [rsp + 32 + 16*1], xmm7 + movaps xmmword ptr [rsp + 32 + 16*2], xmm8 + movaps xmmword ptr [rsp + 32 + 16*3], xmm9 + movaps xmmword ptr [rsp + 32 + 16*4], xmm10 + movaps xmmword ptr [rsp + 32 + 16*5], xmm11 + movaps xmmword ptr [rsp + 32 + 16*6], xmm12 + movaps xmmword ptr [rsp + 32 + 16*7], xmm13 + movaps xmmword ptr [rsp + 32 + 16*8], xmm14 + movaps xmmword ptr [rsp + 32 + 16*9], xmm15 call execute_ex_real - movsd xmm6, qword ptr [rsp + 32 + 8*0] - movsd xmm7, qword ptr [rsp + 32 + 8*1] - movsd xmm8, qword ptr [rsp + 32 + 8*2] - movsd xmm9, qword ptr [rsp + 32 + 8*3] - movsd xmm10, qword ptr [rsp + 32 + 8*4] - movsd xmm11, qword ptr [rsp + 32 + 8*5] - movsd xmm12, qword ptr [rsp + 32 + 8*6] - movsd xmm13, qword ptr [rsp + 32 + 8*7] - movsd xmm14, qword ptr [rsp + 32 + 8*8] - movsd xmm15, qword ptr [rsp + 32 + 8*9] - add rsp, 8*10 + 32 + 8 + movaps xmm6, xmmword ptr [rsp + 32 + 16*0] + movaps xmm7, xmmword ptr [rsp + 32 + 16*1] + movaps xmm8, xmmword ptr [rsp + 32 + 16*2] + movaps xmm9, xmmword ptr [rsp + 32 + 16*3] + movaps xmm10, xmmword ptr [rsp + 32 + 16*4] + movaps xmm11, xmmword ptr [rsp + 32 + 16*5] + movaps xmm12, xmmword ptr [rsp + 32 + 16*6] + movaps xmm13, xmmword ptr [rsp + 32 + 16*7] + movaps xmm14, xmmword ptr [rsp + 32 + 16*8] + movaps xmm15, xmmword ptr [rsp + 32 + 16*9] + add rsp, 16*10 + 32 + 8 ret execute_ex ENDP diff --git a/Zend/tests/GHSA-wm6j-2649-pv75.phpt b/Zend/tests/GHSA-wm6j-2649-pv75.phpt new file mode 100644 index 000000000000..c1035938bd7b --- /dev/null +++ b/Zend/tests/GHSA-wm6j-2649-pv75.phpt @@ -0,0 +1,24 @@ +--TEST-- +GHSA-wm6j-2649-pv75: Null pointer dereference in php_mb_check_encoding() via mb_ereg_search_init() +--CREDITS-- +vi3tL0u1s +--EXTENSIONS-- +mbstring +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Fatal error: Uncaught ValueError: mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "iso-8859-11" given in %s:%d +Stack trace: +#0 %s(%d): mb_regex_encoding('iso-8859-11') +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info.phpt b/Zend/tests/debug_info/debug_info.phpt index bff5876777ba..e7bb4d48aec6 100644 --- a/Zend/tests/debug_info/debug_info.phpt +++ b/Zend/tests/debug_info/debug_info.phpt @@ -21,6 +21,12 @@ class Bar { } } +class Baz { + public function __debugInfo(): ?array { + return null; + } +} + $f = new Foo; var_dump($f); @@ -28,6 +34,7 @@ $b = new Bar; var_dump($b); ?> --EXPECTF-- +Deprecated: Returning null from Baz::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d object(Foo)#%d (3) { ["a"]=> int(1) diff --git a/Zend/tests/display_error_function_args.phpt b/Zend/tests/display_error_function_args.phpt new file mode 100644 index 000000000000..c28a4a2808b4 --- /dev/null +++ b/Zend/tests/display_error_function_args.phpt @@ -0,0 +1,30 @@ +--TEST-- +Displaying function arguments in errors +--INI-- +error_include_args=On +--FILE-- + "123456789012345678901" . chr(0), "cost" => 4]; +password_hash("test", PASSWORD_BCRYPT, $flags); + +ini_set("error_include_args", "Off"); + +unlink('/'); +password_hash("test", PASSWORD_BCRYPT, $flags); + +?> +--EXPECTF-- +Warning: unlink('/'): %s in %s on line %d + +Warning: password_hash(Object(SensitiveParameterValue), '2y', Array): The "salt" option has been ignored, since providing a custom salt is no longer supported in %s on line %d + +Warning: unlink(/): %s in %s on line %d + +Warning: password_hash(): The "salt" option has been ignored, since providing a custom salt is no longer supported in %s on line %d diff --git a/Zend/tests/enum/__debugInfo.phpt b/Zend/tests/enum/__debugInfo.phpt deleted file mode 100644 index 33e0f49851f9..000000000000 --- a/Zend/tests/enum/__debugInfo.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -Enum __debugInfo ---FILE-- -cases(); - } -} - -?> ---EXPECTF-- -Fatal error: Enum Foo cannot include magic method __debugInfo in %s on line %d diff --git a/Zend/tests/enum/debugInfo/backed_enum_value.phpt b/Zend/tests/enum/debugInfo/backed_enum_value.phpt new file mode 100644 index 000000000000..2bf56eb3a59e --- /dev/null +++ b/Zend/tests/enum/debugInfo/backed_enum_value.phpt @@ -0,0 +1,21 @@ +--TEST-- +Enum with __debugInfo() magic method - backed enum value accessible +--FILE-- +name . ' = ' . $this->value]; + } +} + +var_dump(Foo::Bar); + +?> +--EXPECT-- +enum(Foo::Bar) (1) { + [0]=> + string(14) "Foo::Bar = Baz" +} diff --git a/Zend/tests/enum/debugInfo/magic_method.phpt b/Zend/tests/enum/debugInfo/magic_method.phpt new file mode 100644 index 000000000000..4c5963f73b26 --- /dev/null +++ b/Zend/tests/enum/debugInfo/magic_method.phpt @@ -0,0 +1,21 @@ +--TEST-- +Enum supports __debugInfo() magic method +--FILE-- +name . ' is a case of the ' . __CLASS__ . ' enum']; + } +} + +var_dump(Foo::Bar); + +?> +--EXPECT-- +enum(Foo::Bar) (1) { + [0]=> + string(29) "Bar is a case of the Foo enum" +} diff --git a/Zend/tests/enum/debugInfo/missing_magic.phpt b/Zend/tests/enum/debugInfo/missing_magic.phpt new file mode 100644 index 000000000000..9cb5b0c69e52 --- /dev/null +++ b/Zend/tests/enum/debugInfo/missing_magic.phpt @@ -0,0 +1,14 @@ +--TEST-- +When an enum does not have __debugInfo() it is printed nicely +--FILE-- + +--EXPECT-- +enum(Foo::Bar) diff --git a/Zend/tests/enum/debugInfo/param_validation.phpt b/Zend/tests/enum/debugInfo/param_validation.phpt new file mode 100644 index 000000000000..84c60730822e --- /dev/null +++ b/Zend/tests/enum/debugInfo/param_validation.phpt @@ -0,0 +1,18 @@ +--TEST-- +Enum with __debugInfo() magic method - param validation +--FILE-- + +--EXPECTF-- +Fatal error: Method Foo::__debugInfo() cannot take arguments in %s on line %d diff --git a/Zend/tests/enum/debugInfo/return_validation.phpt b/Zend/tests/enum/debugInfo/return_validation.phpt new file mode 100644 index 000000000000..5583dcadf409 --- /dev/null +++ b/Zend/tests/enum/debugInfo/return_validation.phpt @@ -0,0 +1,18 @@ +--TEST-- +Enum with __debugInfo() magic method - return validation +--FILE-- + +--EXPECTF-- +Fatal error: Foo::__debugInfo(): Return type must be ?array when declared in %s on line %d diff --git a/Zend/tests/enum/debugInfo/visibility_validation.phpt b/Zend/tests/enum/debugInfo/visibility_validation.phpt new file mode 100644 index 000000000000..97e2f2c7456b --- /dev/null +++ b/Zend/tests/enum/debugInfo/visibility_validation.phpt @@ -0,0 +1,20 @@ +--TEST-- +Enum with __debugInfo() magic method - visibility validation +--FILE-- + +--EXPECTF-- +Warning: The magic method Foo::__debugInfo() must have public visibility in %s on line %d +enum(Foo::Bar) (0) { +} diff --git a/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt b/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt index 6bdcafb1bfb5..a4076ee00ce2 100644 --- a/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt +++ b/Zend/tests/magic_methods/magic_methods_inheritance_rules.phpt @@ -66,5 +66,6 @@ class WidenedArgumentType extends NarrowedReturnType { echo 'No problems!'; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Returning null from ValidMagicMethods::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d No problems! diff --git a/Zend/tests/return_types/042.phpt b/Zend/tests/return_types/042.phpt index 069e0228ff99..21483047d4f2 100644 --- a/Zend/tests/return_types/042.phpt +++ b/Zend/tests/return_types/042.phpt @@ -20,5 +20,10 @@ class JustAnArray { echo 'No problems!'; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Returning null from UnionType::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d + +Deprecated: Returning null from UnionType2::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d + +Deprecated: Returning null from UnionTypeOldStyle::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead in %s on line %d No problems! diff --git a/Zend/zend.c b/Zend/zend.c index f4236053af3d..f16b1a30dbbc 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -811,6 +811,7 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{ executor_globals->user_error_handler_error_reporting = 0; ZVAL_UNDEF(&executor_globals->user_error_handler); ZVAL_UNDEF(&executor_globals->user_exception_handler); + ZVAL_UNDEF(&executor_globals->last_fatal_error_backtrace); executor_globals->current_execute_data = NULL; executor_globals->current_module = NULL; executor_globals->exit_status = 0; @@ -1303,7 +1304,7 @@ ZEND_API void zend_append_version_info(const zend_extension *extension) /* {{{ * snprintf(new_info, new_info_length, " with %s v%s, %s, by %s\n", extension->name, extension->version, extension->copyright, extension->author); - zend_version_info = (char *) realloc(zend_version_info, zend_version_info_length+new_info_length + 1); + zend_version_info = (char *) perealloc(zend_version_info, zend_version_info_length+new_info_length + 1, true); strncat(zend_version_info, new_info, new_info_length); zend_version_info_length += new_info_length; free(new_info); diff --git a/Zend/zend_API.c b/Zend/zend_API.c index c97d9308e208..65834adbafff 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -509,35 +509,33 @@ static ZEND_COLD bool zend_null_arg_deprecated(const char *fallback_type, uint32 return !EG(exception); } -ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(const zval *arg, bool *dest, uint32_t arg_num) /* {{{ */ +ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_weak(const zval *arg, uint32_t arg_num) /* {{{ */ { if (EXPECTED(Z_TYPE_P(arg) <= IS_STRING)) { if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("bool", arg_num)) { - return 0; + return ZPP_PARSE_BOOL_STATUS_ERROR; } - *dest = zend_is_true(arg); - } else { - return 0; + return zend_is_true(arg); } - return 1; + return ZPP_PARSE_BOOL_STATUS_ERROR; } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(const zval *arg, bool *dest, uint32_t arg_num) /* {{{ */ +ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_slow(const zval *arg, uint32_t arg_num) /* {{{ */ { if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { - return 0; + return ZPP_PARSE_BOOL_STATUS_ERROR; } - return zend_parse_arg_bool_weak(arg, dest, arg_num); + return zend_parse_arg_bool_weak(arg, arg_num); } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_bool_slow(const zval *arg, bool *dest, uint32_t arg_num) +ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_flf_parse_arg_bool_slow(const zval *arg, uint32_t arg_num) { if (UNEXPECTED(ZEND_FLF_ARG_USES_STRICT_TYPES())) { - return 0; + return ZPP_PARSE_BOOL_STATUS_ERROR; } - return zend_parse_arg_bool_weak(arg, dest, arg_num); + return zend_parse_arg_bool_weak(arg, arg_num); } ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(const zval *arg, zend_long *dest, uint32_t arg_num) /* {{{ */ @@ -624,44 +622,46 @@ ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_long_slow(const zval *arg, zend_l return zend_parse_arg_long_weak(arg, dest, arg_num); } -ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, double *dest, uint32_t arg_num) /* {{{ */ +ZEND_API double ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, uint32_t arg_num) /* {{{ */ { if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) { - *dest = (double)Z_LVAL_P(arg); + return (double)Z_LVAL_P(arg); } else if (EXPECTED(Z_TYPE_P(arg) == IS_STRING)) { zend_long l; + double dval; uint8_t type; - if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), &l, dest)) != IS_DOUBLE)) { + if (UNEXPECTED((type = is_numeric_str_function(Z_STR_P(arg), &l, &dval)) != IS_DOUBLE)) { if (EXPECTED(type != 0)) { - *dest = (double)(l); + return (double)(l); } else { - return 0; + return NAN; } + } else { + return dval; } } else if (EXPECTED(Z_TYPE_P(arg) < IS_TRUE)) { if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("float", arg_num)) { - return 0; + return NAN; } - *dest = 0.0; + return 0.0; } else if (EXPECTED(Z_TYPE_P(arg) == IS_TRUE)) { - *dest = 1.0; + return 1.0; } else { - return 0; + return NAN; } - return 1; } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, double *dest, uint32_t arg_num) /* {{{ */ +ZEND_API double ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, uint32_t arg_num) /* {{{ */ { if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) { /* SSTH Exception: IS_LONG may be accepted instead as IS_DOUBLE */ - *dest = (double)Z_LVAL_P(arg); + return (double)Z_LVAL_P(arg); } else if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { - return 0; + return NAN; } - return zend_parse_arg_double_weak(arg, dest, arg_num); + return zend_parse_arg_double_weak(arg, arg_num); } /* }}} */ @@ -728,58 +728,58 @@ ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_or_str_slow(zval *arg, zval ** return true; } -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */ +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, uint32_t arg_num) /* {{{ */ { if (EXPECTED(Z_TYPE_P(arg) < IS_STRING)) { if (UNEXPECTED(Z_TYPE_P(arg) == IS_NULL) && !zend_null_arg_deprecated("string", arg_num)) { - return 0; + return NULL; } convert_to_string(arg); - *dest = Z_STR_P(arg); + return Z_STR_P(arg); } else if (UNEXPECTED(Z_TYPE_P(arg) == IS_OBJECT)) { zend_object *zobj = Z_OBJ_P(arg); zval obj; if (zobj->handlers->cast_object(zobj, &obj, IS_STRING) == SUCCESS) { OBJ_RELEASE(zobj); ZVAL_COPY_VALUE(arg, &obj); - *dest = Z_STR_P(arg); - return 1; + return Z_STR_P(arg); } - return 0; + return NULL; } else { - return 0; + return NULL; } - return 1; } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num) /* {{{ */ +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, uint32_t arg_num) /* {{{ */ { if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { - return 0; + return NULL; } - return zend_parse_arg_str_weak(arg, dest, arg_num); + return zend_parse_arg_str_weak(arg, arg_num); } /* }}} */ -ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num) +ZEND_API zend_string* ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, uint32_t arg_num) { if (UNEXPECTED(ZEND_FLF_ARG_USES_STRICT_TYPES())) { - return 0; + return NULL; } - return zend_parse_arg_str_weak(arg, dest, arg_num); + return zend_parse_arg_str_weak(arg, arg_num); } ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num) /* {{{ */ { + zend_string *str; if (UNEXPECTED(ZEND_ARG_USES_STRICT_TYPES())) { return 0; } if (zend_parse_arg_long_weak(arg, dest_long, arg_num)) { *dest_str = NULL; return 1; - } else if (zend_parse_arg_str_weak(arg, dest_str, arg_num)) { + } else if ((str = zend_parse_arg_str_weak(arg, arg_num)) != NULL) { *dest_long = 0; + *dest_str = str; return 1; } else { return 0; @@ -2504,19 +2504,19 @@ ZEND_API void zend_collect_module_handlers(void) /* {{{ */ dl_loaded_count++; } } ZEND_HASH_FOREACH_END(); - module_request_startup_handlers = (zend_module_entry**)realloc( + module_request_startup_handlers = (zend_module_entry**)perealloc( module_request_startup_handlers, sizeof(zend_module_entry*) * (startup_count + 1 + shutdown_count + 1 + - post_deactivate_count + 1)); + post_deactivate_count + 1), true); module_request_startup_handlers[startup_count] = NULL; module_request_shutdown_handlers = module_request_startup_handlers + startup_count + 1; module_request_shutdown_handlers[shutdown_count] = NULL; module_post_deactivate_handlers = module_request_shutdown_handlers + shutdown_count + 1; module_post_deactivate_handlers[post_deactivate_count] = NULL; /* Cannot reuse module_request_startup_handlers because it is freed in zend_destroy_modules, which happens before zend_unload_modules. */ - modules_dl_loaded = realloc(modules_dl_loaded, sizeof(zend_module_entry*) * (dl_loaded_count + 1)); + modules_dl_loaded = perealloc(modules_dl_loaded, sizeof(zend_module_entry*) * (dl_loaded_count + 1), true); modules_dl_loaded[dl_loaded_count] = NULL; startup_count = 0; @@ -2543,10 +2543,10 @@ ZEND_API void zend_collect_module_handlers(void) /* {{{ */ } } ZEND_HASH_FOREACH_END(); - class_cleanup_handlers = (zend_class_entry**)realloc( + class_cleanup_handlers = (zend_class_entry**)perealloc( class_cleanup_handlers, sizeof(zend_class_entry*) * - (class_count + 1)); + (class_count + 1), true); class_cleanup_handlers[class_count] = NULL; if (class_count) { @@ -2825,6 +2825,10 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, zend_check_magic_method_non_static(ce, fptr, error_type); zend_check_magic_method_public(ce, fptr); zend_check_magic_method_return_type(ce, fptr, error_type, (MAY_BE_ARRAY | MAY_BE_NULL)); + if ((fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && ZEND_TYPE_PURE_MASK(fptr->common.arg_info[-1].type) & MAY_BE_NULL) { + zend_error(E_DEPRECATED, "Returning null from %s::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead", + ZSTR_VAL(ce->name)); + } } else if (zend_string_equals_literal(lcname, "__serialize")) { zend_check_magic_method_args(0, ce, fptr, error_type); zend_check_magic_method_non_static(ce, fptr, error_type); @@ -3143,7 +3147,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend } lowercase_name = zend_string_tolower_ex(internal_function->function_name, type == MODULE_PERSISTENT); lowercase_name = zend_new_interned_string(lowercase_name); - reg_function = malloc(sizeof(zend_internal_function)); + reg_function = pemalloc(sizeof(zend_internal_function), true); memcpy(reg_function, &function, sizeof(zend_internal_function)); if (zend_hash_add_ptr(target_function_table, lowercase_name, reg_function) == NULL) { unload=1; @@ -3161,8 +3165,8 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend zend_flf_capacity *= 2; } /* +1 for NULL terminator */ - zend_flf_handlers = realloc(zend_flf_handlers, (zend_flf_capacity + 1) * sizeof(void *)); - zend_flf_functions = realloc(zend_flf_functions, (zend_flf_capacity + 1) * sizeof(zend_function *)); + zend_flf_handlers = perealloc(zend_flf_handlers, (zend_flf_capacity + 1) * sizeof(void *), true); + zend_flf_functions = perealloc(zend_flf_functions, (zend_flf_capacity + 1) * sizeof(zend_function *), true); } zend_flf_handlers[zend_flf_count] = flf_info->handler; zend_flf_functions[zend_flf_count] = (zend_function *)reg_function; @@ -3208,7 +3212,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend /* Treat return type as an extra argument */ num_args++; - new_arg_info = malloc(sizeof(zend_arg_info) * num_args); + new_arg_info = pemalloc(sizeof(zend_arg_info) * num_args, true); reg_function->arg_info = new_arg_info + 1; for (i = 0; i < num_args; i++) { zend_convert_internal_arg_info(&new_arg_info[i], &arg_info[i], @@ -3493,7 +3497,7 @@ ZEND_API int zend_next_free_module(void) /* {{{ */ static zend_class_entry *do_register_internal_class(const zend_class_entry *orig_class_entry, uint32_t ce_flags) /* {{{ */ { - zend_class_entry *class_entry = malloc(sizeof(zend_class_entry)); + zend_class_entry *class_entry = pemalloc(sizeof(zend_class_entry), true); zend_string *lowercase_name; *class_entry = *orig_class_entry; @@ -4243,6 +4247,7 @@ ZEND_API zend_result zend_fcall_info_init(const zval *callable, uint32_t check_f fci->param_count = 0; fci->params = NULL; fci->named_params = NULL; + fci->consumed_args = 0; return SUCCESS; } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 7990726f7e69..2487c8b632f2 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -48,6 +48,7 @@ typedef struct _zend_fcall_info { zval *params; zend_object *object; uint32_t param_count; + uint32_t consumed_args; /* This hashtable can also contain positional arguments (with integer keys), * which will be appended to the normal params[]. This makes it easier to * integrate APIs like call_user_func_array(). The usual restriction that @@ -341,6 +342,13 @@ typedef struct _zend_fcall_info_cache { #define ZEND_FCI_INITIALIZED(fci) ((fci).size != 0) #define ZEND_FCC_INITIALIZED(fcc) ((fcc).function_handler != NULL) +static zend_always_inline uint32_t zend_fci_consumed_arg(uint32_t arg_index) { + return arg_index < 32 ? (UINT32_C(1) << arg_index) : UINT32_C(0); +} +static zend_always_inline bool zend_fci_is_consumed_arg(uint32_t consumed_args, uint32_t arg_index) { + return arg_index < 32 && (consumed_args & (UINT32_C(1) << arg_index)); +} + ZEND_API int zend_next_free_module(void); BEGIN_EXTERN_C() @@ -524,8 +532,16 @@ ZEND_API const char *zend_get_type_by_const(int type); #define DLEXPORT #endif -#define array_init(arg) ZVAL_ARR((arg), zend_new_array(0)) -#define array_init_size(arg, size) ZVAL_ARR((arg), zend_new_array(size)) +static zend_always_inline void array_init_size(zval *arg, uint32_t size) +{ + ZVAL_ARR(arg, zend_new_array(size)); +} + +static zend_always_inline void array_init(zval *arg) +{ + array_init_size(arg, 0); +} + ZEND_API void object_init(zval *arg); ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *ce); ZEND_API zend_result object_init_with_constructor(zval *arg, zend_class_entry *class_type, uint32_t param_count, zval *params, HashTable *named_params); @@ -2171,21 +2187,27 @@ ZEND_API ZEND_COLD void zend_class_redeclaration_error_ex(int type, zend_string /* Inlined implementations shared by new and old parameter parsing APIs */ +typedef enum zpp_parse_bool_status { + ZPP_PARSE_BOOL_STATUS_FALSE = 0, + ZPP_PARSE_BOOL_STATUS_TRUE = 1, + ZPP_PARSE_BOOL_STATUS_ERROR = 2, +} zpp_parse_bool_status; + ZEND_API bool ZEND_FASTCALL zend_parse_arg_class(zval *arg, zend_class_entry **pce, uint32_t num, bool check_null); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_slow(const zval *arg, bool *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_bool_weak(const zval *arg, bool *dest, uint32_t arg_num); +ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_slow(const zval *arg, uint32_t arg_num); +ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_parse_arg_bool_weak(const zval *arg, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_slow(const zval *arg, zend_long *dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_long_weak(const zval *arg, zend_long *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, double *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, double *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, zend_string **dest, uint32_t arg_num); +ZEND_API double ZEND_FASTCALL zend_parse_arg_double_slow(const zval *arg, uint32_t arg_num); +ZEND_API double ZEND_FASTCALL zend_parse_arg_double_weak(const zval *arg, uint32_t arg_num); +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_slow(zval *arg, uint32_t arg_num); +ZEND_API zend_string* ZEND_FASTCALL zend_parse_arg_str_weak(zval *arg, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_slow(zval *arg, zval **dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_number_or_str_slow(zval *arg, zval **dest, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_parse_arg_str_or_long_slow(zval *arg, zend_string **dest_str, zend_long *dest_long, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_bool_slow(const zval *arg, bool *dest, uint32_t arg_num); -ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, zend_string **dest, uint32_t arg_num); +ZEND_API zpp_parse_bool_status ZEND_FASTCALL zend_flf_parse_arg_bool_slow(const zval *arg, uint32_t arg_num); +ZEND_API zend_string* ZEND_FASTCALL zend_flf_parse_arg_str_slow(zval *arg, uint32_t arg_num); ZEND_API bool ZEND_FASTCALL zend_flf_parse_arg_long_slow(const zval *arg, zend_long *dest, uint32_t arg_num); static zend_always_inline bool zend_parse_arg_bool_ex(const zval *arg, bool *dest, bool *is_null, bool check_null, uint32_t arg_num, bool frameless) @@ -2201,11 +2223,16 @@ static zend_always_inline bool zend_parse_arg_bool_ex(const zval *arg, bool *des *is_null = 1; *dest = 0; } else { + zpp_parse_bool_status result; if (frameless) { - return zend_flf_parse_arg_bool_slow(arg, dest, arg_num); + result = zend_flf_parse_arg_bool_slow(arg, arg_num); } else { - return zend_parse_arg_bool_slow(arg, dest, arg_num); + result = zend_parse_arg_bool_slow(arg, arg_num); } + if (UNEXPECTED(result == ZPP_PARSE_BOOL_STATUS_ERROR)) { + return false; + } + *dest = result; } return 1; } @@ -2251,7 +2278,8 @@ static zend_always_inline bool zend_parse_arg_double(const zval *arg, double *de *is_null = 1; *dest = 0.0; } else { - return zend_parse_arg_double_slow(arg, dest, arg_num); + *dest = zend_parse_arg_double_slow(arg, arg_num); + return !zend_isnan(*dest); } return 1; } @@ -2288,12 +2316,13 @@ static zend_always_inline bool zend_parse_arg_str_ex(zval *arg, zend_string **de *dest = NULL; } else { if (frameless) { - return zend_flf_parse_arg_str_slow(arg, dest, arg_num); + *dest = zend_flf_parse_arg_str_slow(arg, arg_num); } else { - return zend_parse_arg_str_slow(arg, dest, arg_num); + *dest = zend_parse_arg_str_slow(arg, arg_num); } + return *dest != NULL; } - return 1; + return true; } static zend_always_inline bool zend_parse_arg_str(zval *arg, zend_string **dest, bool check_null, uint32_t arg_num) @@ -2524,7 +2553,8 @@ static zend_always_inline bool zend_parse_arg_array_ht_or_str( *dest_str = NULL; } else { *dest_ht = NULL; - return zend_parse_arg_str_slow(arg, dest_str, arg_num); + *dest_str = zend_parse_arg_str_slow(arg, arg_num); + return *dest_str != NULL; } return 1; } diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 32882f5205f7..24b77d7d3493 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -358,7 +358,7 @@ typedef void (*zend_ast_apply_func)(zend_ast **ast_ptr, void *context); ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn, void *context); static zend_always_inline size_t zend_ast_size(uint32_t children) { - return XtOffsetOf(zend_ast, child) + (sizeof(zend_ast *) * children); + return offsetof(zend_ast, child) + (sizeof(zend_ast *) * children); } static zend_always_inline bool zend_ast_is_special(const zend_ast *ast) { diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 314abede4063..840d2dbe32e1 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -154,6 +154,7 @@ ZEND_METHOD(Closure, call) fci_cache.object = fci.object = newobj; fci.size = sizeof(fci); + fci.consumed_args = 0; ZVAL_OBJ(&fci.function_name, &closure->std); ZVAL_UNDEF(&closure_result); fci.retval = &closure_result; @@ -829,7 +830,7 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en /* wrap internal function handler to avoid memory leak */ if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) { /* avoid infinity recursion, by taking handler from nested closure */ - zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func)); + zend_closure *nested = ZEND_CONTAINER_OF(func, zend_closure, func); ZEND_ASSERT(nested->std.ce == zend_ce_closure); closure->orig_internal_handler = nested->orig_internal_handler; } else { diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 96433e16e713..105f99d24171 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2198,7 +2198,7 @@ ZEND_API size_t zend_dirname(char *path, size_t len) /* Note that on Win32 CWD is per drive (heritage from CP/M). * This means dirname("c:foo") maps to "c:." or "c:" - which means CWD on C: drive. */ - if ((2 <= len) && isalpha((int)((unsigned char *)path)[0]) && (':' == path[1])) { + if ((2 <= len) && isalpha((unsigned char)path[0]) && (':' == path[1])) { /* Skip over the drive spec (if any) so as not to change */ path += 2; len_adjust += 2; @@ -5175,7 +5175,7 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg opline->lineno = lineno; opline->extended_value = (2 << 16) | IS_ARRAY; const zval *fbc_zv = zend_hash_find(CG(function_table), lcname); - const Bucket *fbc_bucket = (const Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val)); + const Bucket *fbc_bucket = ZEND_CONTAINER_OF(fbc_zv, Bucket, val); Z_EXTRA_P(CT_CONSTANT(opline->op1)) = fbc_bucket - CG(function_table)->arData; /* Initialize the result array. */ @@ -5472,7 +5472,7 @@ static void zend_compile_call(znode *result, const zend_ast *ast, uint32_t type) /* Store offset to function from symbol table in op2.extra. */ if (fbc->type == ZEND_INTERNAL_FUNCTION) { - const Bucket *fbc_bucket = (const Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val)); + const Bucket *fbc_bucket = ZEND_CONTAINER_OF(fbc_zv, Bucket, val); Z_EXTRA_P(CT_CONSTANT(opline->op2)) = fbc_bucket - CG(function_table)->arData; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8f67fee2a52b..0e31332c97f0 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -481,7 +481,7 @@ typedef struct _zend_property_info { #define OBJ_PROP_NUM(obj, num) \ (&(obj)->properties_table[(num)]) #define OBJ_PROP_TO_OFFSET(num) \ - ((uint32_t)(XtOffsetOf(zend_object, properties_table) + sizeof(zval) * (num))) + ((uint32_t)(offsetof(zend_object, properties_table) + sizeof(zval) * (num))) #define OBJ_PROP_TO_NUM(offset) \ (((offset) - OBJ_PROP_TO_OFFSET(0)) / sizeof(zval)) #define OBJ_PROP_SLOT_TO_OFFSET(obj, slot) \ diff --git a/Zend/zend_cpuinfo.h b/Zend/zend_cpuinfo.h index 513dacfd08f5..855e245beafe 100644 --- a/Zend/zend_cpuinfo.h +++ b/Zend/zend_cpuinfo.h @@ -272,7 +272,10 @@ static zend_always_inline int zend_cpu_supports_avx512_vbmi(void) { #endif /* __builtin_cpu_supports has pclmul from gcc9 and clang 19 */ -#if defined(PHP_HAVE_BUILTIN_CPU_SUPPORTS) && (!defined(__GNUC__) || (defined(__clang__) && __clang_major__ >= 19) || (ZEND_GCC_VERSION >= 9000)) +#if defined(PHP_HAVE_BUILTIN_CPU_SUPPORTS) && (defined(__x86_64__) || defined(__i386__)) && \ + ( \ + (!defined(__GNUC__) || (defined(__clang__) && __clang_major__ >= 19) || (ZEND_GCC_VERSION >= 9000)) \ + ) ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_pclmul(void) { #ifdef PHP_HAVE_BUILTIN_CPU_INIT @@ -287,7 +290,11 @@ static inline int zend_cpu_supports_pclmul(void) { #endif /* __builtin_cpu_supports has cldemote from gcc11 and clang 19 */ -#if defined(PHP_HAVE_BUILTIN_CPU_SUPPORTS) && ((defined(__clang__) && (__clang_major__ >= 19)) || (!defined(__clang__) && defined(__GNUC__) && (ZEND_GCC_VERSION >= 11000))) +#if defined(PHP_HAVE_BUILTIN_CPU_SUPPORTS) && (defined(__x86_64__) || defined(__i386__)) && \ + ( \ + (defined(__clang__) && (__clang_major__ >= 19)) || \ + (!defined(__clang__) && defined(__GNUC__) && (ZEND_GCC_VERSION >= 11000)) \ + ) #define HAVE_ZEND_CPU_SUPPORTS_CLDEMOTE 1 ZEND_NO_SANITIZE_ADDRESS static inline int zend_cpu_supports_cldemote(void) { diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c index f175e3e6bcc9..a5091f6c1b6f 100644 --- a/Zend/zend_enum.c +++ b/Zend/zend_enum.c @@ -90,7 +90,7 @@ static void zend_verify_enum_properties(const zend_class_entry *ce) static void zend_verify_enum_magic_methods(const zend_class_entry *ce) { - // Only __get, __call and __invoke are allowed + // Only __get, __call, __debugInfo and __invoke are allowed ZEND_ENUM_DISALLOW_MAGIC_METHOD(constructor, "__construct"); ZEND_ENUM_DISALLOW_MAGIC_METHOD(destructor, "__destruct"); @@ -100,7 +100,6 @@ static void zend_verify_enum_magic_methods(const zend_class_entry *ce) ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unset, "__unset"); ZEND_ENUM_DISALLOW_MAGIC_METHOD(__isset, "__isset"); ZEND_ENUM_DISALLOW_MAGIC_METHOD(__tostring, "__toString"); - ZEND_ENUM_DISALLOW_MAGIC_METHOD(__debugInfo, "__debugInfo"); ZEND_ENUM_DISALLOW_MAGIC_METHOD(__serialize, "__serialize"); ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unserialize, "__unserialize"); @@ -176,7 +175,7 @@ void zend_register_enum_ce(void) zend_ce_backed_enum->interface_gets_implemented = zend_implement_backed_enum; memcpy(&zend_enum_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - zend_enum_object_handlers.offset = XtOffsetOf(zend_enum_obj, std); + zend_enum_object_handlers.offset = offsetof(zend_enum_obj, std); zend_enum_object_handlers.clone_obj = NULL; zend_enum_object_handlers.compare = zend_objects_not_comparable; } diff --git a/Zend/zend_enum.h b/Zend/zend_enum.h index 96c9317d47c0..6bffb8866507 100644 --- a/Zend/zend_enum.h +++ b/Zend/zend_enum.h @@ -36,7 +36,7 @@ typedef struct zend_enum_obj { static inline zend_enum_obj *zend_enum_obj_from_obj(zend_object *zobj) { ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM); - return (zend_enum_obj*)((char*)(zobj) - XtOffsetOf(zend_enum_obj, std)); + return ZEND_CONTAINER_OF(zobj, zend_enum_obj, std); } void zend_enum_startup(void); diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index d23fb647af9d..a1301b8c20b2 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -506,7 +506,7 @@ ZEND_METHOD(ErrorException, getSeverity) } \ } while (0) -static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */ +static void build_trace_args(zval *arg, smart_str *str) /* {{{ */ { /* the trivial way would be to do * convert_to_string(arg); @@ -516,24 +516,21 @@ static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */ ZVAL_DEREF(arg); - if (smart_str_append_zval(str, arg, EG(exception_string_param_max_len)) == SUCCESS) { - smart_str_appends(str, ", "); - } else { + if (smart_str_append_zval(str, arg, EG(exception_string_param_max_len)) != SUCCESS) { switch (Z_TYPE_P(arg)) { case IS_RESOURCE: smart_str_appends(str, "Resource id #"); smart_str_append_long(str, Z_RES_HANDLE_P(arg)); - smart_str_appends(str, ", "); break; case IS_ARRAY: - smart_str_appends(str, "Array, "); + smart_str_appends(str, "Array"); break; case IS_OBJECT: { zend_string *class_name = Z_OBJ_HANDLER_P(arg, get_class_name)(Z_OBJ_P(arg)); smart_str_appends(str, "Object("); /* cut off on NULL byte ... class@anonymous */ smart_str_appends(str, ZSTR_VAL(class_name)); - smart_str_appends(str, "), "); + smart_str_appends(str, ")"); zend_string_release_ex(class_name, 0); break; } @@ -542,7 +539,30 @@ static void _build_trace_args(zval *arg, smart_str *str) /* {{{ */ } /* }}} */ -static void _build_trace_string(smart_str *str, const HashTable *ht, uint32_t num) /* {{{ */ +static void build_trace_args_list(zval *tmp, smart_str *str) /* {{{ */ +{ + if (UNEXPECTED(Z_TYPE_P(tmp) != IS_ARRAY)) { + /* only happens w/ reflection abuse (Zend/tests/bug63762.phpt) */ + zend_error(E_WARNING, "args element is not an array"); + return; + } + + bool first = true; + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), zend_string *name, zval *arg) { + if (!first) { + smart_str_appends(str, ", "); + } + first = false; + if (name) { + smart_str_append(str, name); + smart_str_appends(str, ": "); + } + build_trace_args(arg, str); + } ZEND_HASH_FOREACH_END(); +} +/* }}} */ + +static void build_trace_string(smart_str *str, const HashTable *ht, uint32_t num) /* {{{ */ { zval *file, *tmp; @@ -588,27 +608,40 @@ static void _build_trace_string(smart_str *str, const HashTable *ht, uint32_t nu smart_str_appendc(str, '('); tmp = zend_hash_find_known_hash(ht, ZSTR_KNOWN(ZEND_STR_ARGS)); if (tmp) { - if (EXPECTED(Z_TYPE_P(tmp) == IS_ARRAY)) { - size_t last_len = ZSTR_LEN(str->s); - zend_string *name; - zval *arg; - - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(tmp), name, arg) { - if (name) { - smart_str_append(str, name); - smart_str_appends(str, ": "); - } - _build_trace_args(arg, str); - } ZEND_HASH_FOREACH_END(); + build_trace_args_list(tmp, str); + } + smart_str_appends(str, ")\n"); +} +/* }}} */ - if (last_len != ZSTR_LEN(str->s)) { - ZSTR_LEN(str->s) -= 2; /* remove last ', ' */ - } - } else { - zend_error(E_WARNING, "args element is not an array"); +/* {{{ Gets the function arguments printed as a string from a backtrace frame. */ +ZEND_API zend_string *zend_trace_function_args_to_string(const HashTable *frame) { + smart_str str = {0}; + + zval *tmp = zend_hash_find_known_hash(frame, ZSTR_KNOWN(ZEND_STR_ARGS)); + if (tmp) { + build_trace_args_list(tmp, &str); + } + + return smart_str_extract(&str); +} +/* }}} */ + +/* {{{ Gets the currently executing function's arguments as a string. Used by php_verror. */ +ZEND_API zend_string *zend_trace_current_function_args_string(void) { + zend_string *dynamic_params = NULL; + /* get a backtrace to snarf function args */ + zval backtrace; + zend_fetch_debug_backtrace(&backtrace, /* skip_last */ 0, /* options */ 0, /* limit */ 1); + /* can fail esp if low memory condition */ + if (Z_TYPE(backtrace) == IS_ARRAY) { + zval *first_frame = zend_hash_index_find(Z_ARRVAL(backtrace), 0); + if (first_frame) { + dynamic_params = zend_trace_function_args_to_string(Z_ARRVAL_P(first_frame)); } } - smart_str_appends(str, ")\n"); + zval_ptr_dtor(&backtrace); + return dynamic_params; } /* }}} */ @@ -624,7 +657,7 @@ ZEND_API zend_string *zend_trace_to_string(const HashTable *trace, bool include_ continue; } - _build_trace_string(&str, Z_ARRVAL_P(frame), num++); + build_trace_string(&str, Z_ARRVAL_P(frame), num++); } ZEND_HASH_FOREACH_END(); if (include_main) { diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h index f9b472598012..7ef9ef016393 100644 --- a/Zend/zend_exceptions.h +++ b/Zend/zend_exceptions.h @@ -65,6 +65,8 @@ ZEND_API zend_result zend_update_exception_properties(zend_execute_data *execute /* show an exception using zend_error(severity,...), severity should be E_ERROR */ ZEND_API ZEND_COLD zend_result zend_exception_error(zend_object *exception, int severity); ZEND_NORETURN void zend_exception_uncaught_error(const char *prefix, ...) ZEND_ATTRIBUTE_FORMAT(printf, 1, 2); +ZEND_API zend_string *zend_trace_function_args_to_string(const HashTable *frame); +ZEND_API zend_string *zend_trace_current_function_args_string(void); ZEND_API zend_string *zend_trace_to_string(const HashTable *trace, bool include_main); ZEND_API ZEND_COLD zend_object *zend_create_unwind_exit(void); diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 85461eaa1569..4253037fda52 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -733,8 +733,6 @@ static bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) { zend_long lval; double dval; - zend_string *str; - bool bval; /* Type preference order: int -> float -> string -> bool */ if (type_mask & MAY_BE_LONG) { @@ -760,16 +758,23 @@ static bool zend_verify_weak_scalar_type_hint(uint32_t type_mask, zval *arg) return false; } } - if ((type_mask & MAY_BE_DOUBLE) && zend_parse_arg_double_weak(arg, &dval, 0)) { - zval_ptr_dtor(arg); - ZVAL_DOUBLE(arg, dval); - return true; + if (type_mask & MAY_BE_DOUBLE) { + dval = zend_parse_arg_double_weak(arg, 0); + if (EXPECTED(!zend_isnan(dval))) { + zval_ptr_dtor(arg); + ZVAL_DOUBLE(arg, dval); + return true; + } } - if ((type_mask & MAY_BE_STRING) && zend_parse_arg_str_weak(arg, &str, 0)) { + if ((type_mask & MAY_BE_STRING) && zend_parse_arg_str_weak(arg, 0)) { /* on success "arg" is converted to IS_STRING */ return true; } - if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL && zend_parse_arg_bool_weak(arg, &bval, 0)) { + if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL) { + zpp_parse_bool_status bval = zend_parse_arg_bool_weak(arg, 0); + if (UNEXPECTED(bval == ZPP_PARSE_BOOL_STATUS_ERROR)) { + return false; + } zval_ptr_dtor(arg); ZVAL_BOOL(arg, bval); return true; @@ -793,21 +798,19 @@ static bool can_convert_to_string(const zval *zv) { static bool zend_verify_weak_scalar_type_hint_no_sideeffect(uint32_t type_mask, const zval *arg) { zend_long lval; - double dval; - bool bval; /* Pass (uint32_t)-1 as arg_num to indicate to ZPP not to emit any deprecation notice, * this is needed because the version with side effects also uses 0 (e.g. for typed properties) */ if ((type_mask & MAY_BE_LONG) && zend_parse_arg_long_weak(arg, &lval, (uint32_t)-1)) { return true; } - if ((type_mask & MAY_BE_DOUBLE) && zend_parse_arg_double_weak(arg, &dval, (uint32_t)-1)) { + if ((type_mask & MAY_BE_DOUBLE) && !zend_isnan(zend_parse_arg_double_weak(arg, (uint32_t)-1))) { return true; } if ((type_mask & MAY_BE_STRING) && can_convert_to_string(arg)) { return true; } - if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL && zend_parse_arg_bool_weak(arg, &bval, (uint32_t)-1)) { + if ((type_mask & MAY_BE_BOOL) == MAY_BE_BOOL && zend_parse_arg_bool_weak(arg, (uint32_t)-1) != ZPP_PARSE_BOOL_STATUS_ERROR) { return true; } return false; diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 0022eb4a1df8..71e0c56a51c8 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -795,6 +795,7 @@ zend_result _call_user_function_impl(zval *object, zval *function_name, zval *re fci.param_count = param_count; fci.params = params; fci.named_params = named_params; + fci.consumed_args = 0; return zend_call_function(&fci, NULL); } @@ -862,6 +863,9 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ call = zend_vm_stack_push_call_frame(call_info, func, fci->param_count, object_or_called_scope); + uint32_t consumed_args = fci->param_count ? fci->consumed_args : 0; + + ZEND_ASSERT((consumed_args & (consumed_args - 1)) == 0); for (uint32_t i = 0; i < fci->param_count; i++) { zval *param = ZEND_CALL_ARG(call, i+1); @@ -903,7 +907,15 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ } if (EXPECTED(!must_wrap)) { - ZVAL_COPY(param, arg); + if (EXPECTED(consumed_args == 0) + || !zend_fci_is_consumed_arg(consumed_args, i) + || Z_ISREF_P(arg) + || arg != &fci->params[i]) { + ZVAL_COPY(param, arg); + } else { + ZVAL_COPY_VALUE(param, arg); + ZVAL_UNDEF(arg); + } } else { Z_TRY_ADDREF_P(arg); ZVAL_NEW_REF(param, arg); @@ -1092,6 +1104,7 @@ ZEND_API void zend_call_known_function( fci.param_count = param_count; fci.params = params; fci.named_params = named_params; + fci.consumed_args = 0; ZVAL_UNDEF(&fci.function_name); /* Unused */ fcic.function_handler = fn; diff --git a/Zend/zend_fibers.h b/Zend/zend_fibers.h index d4726f97ddd2..c72ffdc8f18e 100644 --- a/Zend/zend_fibers.h +++ b/Zend/zend_fibers.h @@ -154,7 +154,7 @@ static zend_always_inline zend_fiber *zend_fiber_from_context(zend_fiber_context { ZEND_ASSERT(context->kind == zend_ce_fiber && "Fiber context does not belong to a Zend fiber"); - return (zend_fiber *)(((char *) context) - XtOffsetOf(zend_fiber, context)); + return ZEND_CONTAINER_OF(context, zend_fiber, context); } static zend_always_inline zend_fiber_context *zend_fiber_get_context(zend_fiber *fiber) diff --git a/Zend/zend_frameless_function.h b/Zend/zend_frameless_function.h index 241507aa99e7..b6f361f104b0 100644 --- a/Zend/zend_frameless_function.h +++ b/Zend/zend_frameless_function.h @@ -64,7 +64,7 @@ dest_ht = NULL; \ ZVAL_COPY(&str_tmp, arg ## arg_num); \ arg ## arg_num = &str_tmp; \ - if (!zend_flf_parse_arg_str_slow(arg ## arg_num, &dest_str, arg_num)) { \ + if (!(dest_str = zend_flf_parse_arg_str_slow(arg ## arg_num, arg_num))) { \ zend_wrong_parameter_type_error(arg_num, Z_EXPECTED_ARRAY_OR_STRING, arg ## arg_num); \ goto flf_clean; \ } \ diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 6a695aeef449..6c0d4a241a1b 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -404,14 +404,14 @@ static zend_always_inline bool _zend_handle_numeric_str(const char *key, size_t const char *tmp = key; if (EXPECTED(*tmp > '9')) { - return 0; + return false; } else if (*tmp < '0') { if (*tmp != '-') { - return 0; + return false; } tmp++; if (*tmp > '9' || *tmp < '0') { - return 0; + return false; } } return _zend_handle_numeric_str_ex(key, length, idx); @@ -1605,30 +1605,30 @@ static zend_always_inline bool zend_array_is_list(const zend_array *array) zend_string* str_idx; /* Empty arrays are lists */ if (zend_hash_num_elements(array) == 0) { - return 1; + return true; } /* Packed arrays are lists */ if (HT_IS_PACKED(array)) { if (HT_IS_WITHOUT_HOLES(array)) { - return 1; + return true; } /* Check if the list could theoretically be repacked */ ZEND_HASH_PACKED_FOREACH_KEY(array, num_idx) { if (num_idx != expected_idx++) { - return 0; + return false; } } ZEND_HASH_FOREACH_END(); } else { /* Check if the list could theoretically be repacked */ ZEND_HASH_MAP_FOREACH_KEY(array, num_idx, str_idx) { if (str_idx != NULL || num_idx != expected_idx++) { - return 0; + return false; } } ZEND_HASH_FOREACH_END(); } - return 1; + return true; } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index bf4c84404a67..e85b4ea42250 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -188,9 +188,9 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ if (ce->constructor) { if (parent->constructor && UNEXPECTED(parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) { - zend_error_noreturn(E_ERROR, "Cannot override final %s::%s() with %s::%s()", - ZSTR_VAL(parent->name), ZSTR_VAL(parent->constructor->common.function_name), - ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name)); + zend_error_noreturn(E_ERROR, "Cannot override final %s::__construct() with %s::__construct()", + ZSTR_VAL(parent->name), + ZSTR_VAL(ce->name)); } return; } @@ -1595,11 +1595,7 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en ce_num = ce->num_interfaces; - if (ce->type == ZEND_INTERNAL_CLASS) { - ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num)); - } else { - ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num)); - } + ce->interfaces = (zend_class_entry **) perealloc(ce->interfaces, sizeof(zend_class_entry *) * (ce_num + if_num), ce->type == ZEND_INTERNAL_CLASS); /* Inherit the interfaces, only if they're not already inherited by the class */ while (if_num--) { @@ -2234,11 +2230,7 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry } ZEND_HASH_FOREACH_END(); } else { if (ce->num_interfaces >= current_iface_num) { - if (ce->type == ZEND_INTERNAL_CLASS) { - ce->interfaces = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num)); - } else { - ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num)); - } + ce->interfaces = (zend_class_entry **) perealloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num), ce->type == ZEND_INTERNAL_CLASS); } ce->interfaces[ce->num_interfaces++] = iface; diff --git a/Zend/zend_ini.c b/Zend/zend_ini.c index e4aec28c7b07..093683526d31 100644 --- a/Zend/zend_ini.c +++ b/Zend/zend_ini.c @@ -561,6 +561,11 @@ ZEND_API zend_string *zend_ini_get_value(zend_string *name) /* {{{ */ ZEND_API bool zend_ini_parse_bool(const zend_string *str) { + /* May happen if an unknown INI setting is queried via zend_ini_bool_literal(), + * as zend_ini_str() would return NULL */ + if (UNEXPECTED(str == NULL)) { + return false; + } if (zend_string_equals_literal_ci(str, "true") || zend_string_equals_literal_ci(str, "yes") || zend_string_equals_literal_ci(str, "on") @@ -584,7 +589,7 @@ static const char *zend_ini_consume_quantity_prefix(const char *const digits, co ++digits_consumed; } - if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) { + if (digits_consumed[0] == '0' && !isdigit((unsigned char)digits_consumed[1])) { /* Value is just 0 */ if ((digits_consumed+1) == str_end) { return digits_consumed; @@ -642,7 +647,7 @@ static zend_ulong zend_ini_parse_quantity_internal(const zend_string *value, zen } /* if there is no digit after +/- */ - if (!isdigit(digits[0])) { + if (!isdigit((unsigned char)digits[0])) { /* Escape the string to avoid null bytes and to make non-printable chars * visible */ smart_str_append_escaped(&invalid, ZSTR_VAL(value), ZSTR_LEN(value)); @@ -656,7 +661,7 @@ static zend_ulong zend_ini_parse_quantity_internal(const zend_string *value, zen } int base = 0; - if (digits[0] == '0' && !isdigit(digits[1])) { + if (digits[0] == '0' && !isdigit((unsigned char)digits[1])) { /* Value is just 0 */ if ((digits+1) == str_end) { *errstr = NULL; diff --git a/Zend/zend_ini.h b/Zend/zend_ini.h index 16bc234d5849..dbe650675b66 100644 --- a/Zend/zend_ini.h +++ b/Zend/zend_ini.h @@ -182,18 +182,18 @@ END_EXTERN_C() #ifdef ZTS #define STD_ZEND_INI_ENTRY(name, default_value, modifiable, on_modify, property_name, struct_type, struct_ptr) \ - ZEND_INI_ENTRY2(name, default_value, modifiable, on_modify, (void *) XtOffsetOf(struct_type, property_name), (void *) &struct_ptr##_id) + ZEND_INI_ENTRY2(name, default_value, modifiable, on_modify, (void *) offsetof(struct_type, property_name), (void *) &struct_ptr##_id) #define STD_ZEND_INI_ENTRY_EX(name, default_value, modifiable, on_modify, property_name, struct_type, struct_ptr, displayer) \ - ZEND_INI_ENTRY2_EX(name, default_value, modifiable, on_modify, (void *) XtOffsetOf(struct_type, property_name), (void *) &struct_ptr##_id, displayer) + ZEND_INI_ENTRY2_EX(name, default_value, modifiable, on_modify, (void *) offsetof(struct_type, property_name), (void *) &struct_ptr##_id, displayer) #define STD_ZEND_INI_BOOLEAN(name, default_value, modifiable, on_modify, property_name, struct_type, struct_ptr) \ - ZEND_INI_ENTRY3_EX(name, default_value, modifiable, on_modify, (void *) XtOffsetOf(struct_type, property_name), (void *) &struct_ptr##_id, NULL, zend_ini_boolean_displayer_cb) + ZEND_INI_ENTRY3_EX(name, default_value, modifiable, on_modify, (void *) offsetof(struct_type, property_name), (void *) &struct_ptr##_id, NULL, zend_ini_boolean_displayer_cb) #else #define STD_ZEND_INI_ENTRY(name, default_value, modifiable, on_modify, property_name, struct_type, struct_ptr) \ - ZEND_INI_ENTRY2(name, default_value, modifiable, on_modify, (void *) XtOffsetOf(struct_type, property_name), (void *) &struct_ptr) + ZEND_INI_ENTRY2(name, default_value, modifiable, on_modify, (void *) offsetof(struct_type, property_name), (void *) &struct_ptr) #define STD_ZEND_INI_ENTRY_EX(name, default_value, modifiable, on_modify, property_name, struct_type, struct_ptr, displayer) \ - ZEND_INI_ENTRY2_EX(name, default_value, modifiable, on_modify, (void *) XtOffsetOf(struct_type, property_name), (void *) &struct_ptr, displayer) + ZEND_INI_ENTRY2_EX(name, default_value, modifiable, on_modify, (void *) offsetof(struct_type, property_name), (void *) &struct_ptr, displayer) #define STD_ZEND_INI_BOOLEAN(name, default_value, modifiable, on_modify, property_name, struct_type, struct_ptr) \ - ZEND_INI_ENTRY3_EX(name, default_value, modifiable, on_modify, (void *) XtOffsetOf(struct_type, property_name), (void *) &struct_ptr, NULL, zend_ini_boolean_displayer_cb) + ZEND_INI_ENTRY3_EX(name, default_value, modifiable, on_modify, (void *) offsetof(struct_type, property_name), (void *) &struct_ptr, NULL, zend_ini_boolean_displayer_cb) #endif #define REGISTER_INI_ENTRIES() zend_register_ini_entries_ex(ini_entries, module_number, type) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 571aa9e92abc..e7ddd466a51a 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -2176,12 +2176,16 @@ ZEND_API ZEND_COLD bool zend_std_unset_static_property(const zend_class_entry *c static ZEND_COLD zend_never_inline void zend_bad_constructor_call(const zend_function *constructor, const zend_class_entry *scope) /* {{{ */ { if (scope) { - zend_throw_error(NULL, "Call to %s %s::%s() from scope %s", - zend_visibility_string(constructor->common.fn_flags), ZSTR_VAL(constructor->common.scope->name), - ZSTR_VAL(constructor->common.function_name), ZSTR_VAL(scope->name) + zend_throw_error(NULL, "Call to %s %s::__construct() from scope %s", + zend_visibility_string(constructor->common.fn_flags), + ZSTR_VAL(constructor->common.scope->name), + ZSTR_VAL(scope->name) ); } else { - zend_throw_error(NULL, "Call to %s %s::%s() from global scope", zend_visibility_string(constructor->common.fn_flags), ZSTR_VAL(constructor->common.scope->name), ZSTR_VAL(constructor->common.function_name)); + zend_throw_error(NULL, "Call to %s %s::__construct() from global scope", + zend_visibility_string(constructor->common.fn_flags), + ZSTR_VAL(constructor->common.scope->name) + ); } } /* }}} */ diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index ec71c427ffce..d0dd804e8a41 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -204,7 +204,7 @@ typedef zend_result (*zend_object_do_operation_t)(uint8_t opcode, zval *result, struct _zend_object_handlers { /* offset of real object header (usually zero) */ - int offset; + size_t offset; /* object handlers */ zend_object_free_obj_t free_obj; /* required */ zend_object_dtor_obj_t dtor_obj; /* required */ diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 9fe46b6b6d2a..2fc264742cd1 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -193,14 +193,14 @@ ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce) return object; } -ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object) +ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, const zend_object *old_object) { bool has_clone_method = old_object->ce->clone != NULL; if (old_object->ce->default_properties_count) { - zval *src = old_object->properties_table; + const zval *src = old_object->properties_table; zval *dst = new_object->properties_table; - zval *end = src + old_object->ce->default_properties_count; + const zval *end = src + old_object->ce->default_properties_count; do { i_zval_ptr_dtor(dst); diff --git a/Zend/zend_objects.h b/Zend/zend_objects.h index 44a8038a0fa7..0930fa043101 100644 --- a/Zend/zend_objects.h +++ b/Zend/zend_objects.h @@ -24,7 +24,7 @@ BEGIN_EXTERN_C() ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce); ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce); -ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object); +ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, const zend_object *old_object); ZEND_API void zend_object_std_dtor(zend_object *object); ZEND_API void zend_objects_destroy_object(zend_object *object); diff --git a/Zend/zend_observer.c b/Zend/zend_observer.c index f5f90c3c1f13..1cb4373f4c07 100644 --- a/Zend/zend_observer.c +++ b/Zend/zend_observer.c @@ -167,7 +167,7 @@ static bool zend_observer_remove_handler(void **first_handler, const void *old_h *next_handler = NULL; } else { if (cur_handler != last_handler) { - memmove(cur_handler, cur_handler + 1, sizeof(cur_handler) * (last_handler - cur_handler)); + memmove(cur_handler, cur_handler + 1, sizeof(*cur_handler) * (last_handler - cur_handler)); } *last_handler = NULL; *next_handler = *cur_handler; @@ -218,7 +218,7 @@ ZEND_API void zend_observer_add_end_handler(const zend_function *function, zend_ if (*end_handler != ZEND_OBSERVER_NOT_OBSERVED) { // there's no space for new handlers, then it's forbidden to call this function ZEND_ASSERT(end_handler[registered_observers - 1] == NULL); - memmove(end_handler + 1, end_handler, sizeof(end_handler) * (registered_observers - 1)); + memmove(end_handler + 1, end_handler, sizeof(*end_handler) * (registered_observers - 1)); } else if (*begin_handler == ZEND_OBSERVER_NONE_OBSERVED) { *begin_handler = ZEND_OBSERVER_NOT_OBSERVED; } diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 8d861adb394e..a43fdcc9a48b 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -448,6 +448,7 @@ static zend_never_inline zend_long ZEND_FASTCALL zendi_try_get_long(const zval * ZEND_ASSERT(Z_TYPE(dst) == IS_LONG); return Z_LVAL(dst); } + case IS_UNDEF: case IS_RESOURCE: case IS_ARRAY: *failed = true; @@ -3321,8 +3322,8 @@ ZEND_API int ZEND_FASTCALL zend_binary_strcasecmp_l(const char *s1, size_t len1, len = MIN(len1, len2); while (len--) { - c1 = zend_tolower((int)*(unsigned char *)s1++); - c2 = zend_tolower((int)*(unsigned char *)s2++); + c1 = zend_tolower((unsigned char)*(s1++)); + c2 = zend_tolower((unsigned char)*(s2++)); if (c1 != c2) { return c1 - c2; } @@ -3342,8 +3343,8 @@ ZEND_API int ZEND_FASTCALL zend_binary_strncasecmp_l(const char *s1, size_t len1 } len = MIN(length, MIN(len1, len2)); while (len--) { - c1 = zend_tolower((int)*(unsigned char *)s1++); - c2 = zend_tolower((int)*(unsigned char *)s2++); + c1 = zend_tolower((unsigned char)*(s1++)); + c2 = zend_tolower((unsigned char)*(s2++)); if (c1 != c2) { return c1 - c2; } diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index cd26aa174712..ccad24682fdb 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -130,7 +130,9 @@ #endif /* pseudo fallthrough keyword; */ -#if defined(__GNUC__) && __GNUC__ >= 7 +#if __STDC_VERSION__ >= 202311L || defined(__cplusplus) +# define ZEND_FALLTHROUGH [[fallthrough]] +#elif defined(__GNUC__) && __GNUC__ >= 7 # define ZEND_FALLTHROUGH __attribute__((__fallthrough__)) #else # define ZEND_FALLTHROUGH ((void)0) @@ -385,6 +387,34 @@ char *alloca(); #define ZEND_ELEMENT_COUNT(m) #endif +#if __cplusplus +extern "C++" { +# include + template + const T* zend_container_of(const M *ptr, size_t offset) { + return reinterpret_cast(reinterpret_cast(ptr) - offset); + } + template + T* zend_container_of(M *ptr, size_t offset) { + return reinterpret_cast(reinterpret_cast(ptr) - offset); + } + +# define ZEND_CONTAINER_OF(ptr, Type, member) zend_container_of(ptr, offsetof(Type, member)) +} +#elif __STDC_VERSION__ >= 202311L || ZEND_GCC_VERSION +/* typeof is C23 or a GCC extension */ +# define ZEND_CONTAINER_OF(ptr, Type, member) \ + _Generic( \ + (ptr), \ + const typeof(((Type*)0)->member) *: ((const Type*)((char*)(ptr) - offsetof(Type, member))), \ + typeof(((Type*)0)->member) *: ((Type*)((char*)(ptr) - offsetof(Type, member))) \ + ) +#else +/* Define a variant that does not keep const-ness for older compilers. Mismatches + * are expected to be caught by CI running modern compilers. */ +# define ZEND_CONTAINER_OF(ptr, Type, member) ((Type*)((char*)(ptr) - offsetof(Type, member))) +#endif + #ifdef HAVE_BUILTIN_CONSTANT_P # define ZEND_CONST_COND(_condition, _default) \ (__builtin_constant_p(_condition) ? (_condition) : (_default)) @@ -430,10 +460,6 @@ char *alloca(); # define UNEXPECTED(condition) (condition) #endif -#ifndef XtOffsetOf -# define XtOffsetOf(s_type, field) offsetof(s_type, field) -#endif - #ifndef ZEND_WIN32 # define SETJMP(a) sigsetjmp(a, 0) # define LONGJMP(a,b) siglongjmp(a, b) diff --git a/Zend/zend_string.c b/Zend/zend_string.c index a9e1a7dea099..52fca0cd4346 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -17,6 +17,7 @@ #include "zend.h" #include "zend_globals.h" +#include "zend_multiply.h" #ifdef HAVE_VALGRIND # include "valgrind/callgrind.h" @@ -473,8 +474,7 @@ ZEND_API zend_string *zend_string_concat2( const char *str1, size_t str1_len, const char *str2, size_t str2_len) { - size_t len = str1_len + str2_len; - zend_string *res = zend_string_alloc(len, 0); + zend_string *res = zend_string_safe_alloc(1, str1_len, str2_len, 0); char *p = ZSTR_VAL(res); p = zend_mempcpy(p, str1, str1_len); @@ -489,7 +489,8 @@ ZEND_API zend_string *zend_string_concat3( const char *str2, size_t str2_len, const char *str3, size_t str3_len) { - size_t len = str1_len + str2_len + str3_len; + size_t tmp_len = zend_safe_address_guarded(1, str1_len, str2_len); + size_t len = zend_safe_address_guarded(1, tmp_len, str3_len); zend_string *res = zend_string_alloc(len, 0); char *p = ZSTR_VAL(res); diff --git a/Zend/zend_string.h b/Zend/zend_string.h index d2b382390009..3f0c9abd2596 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -115,7 +115,7 @@ static zend_always_inline zend_string *ZSTR_KNOWN(size_t idx) { return zend_known_strings[idx]; } -#define _ZSTR_HEADER_SIZE XtOffsetOf(zend_string, val) +#define _ZSTR_HEADER_SIZE offsetof(zend_string, val) #define _ZSTR_STRUCT_SIZE(len) (_ZSTR_HEADER_SIZE + len + 1) diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 30d77da67323..cfff3b942c4b 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -944,16 +944,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_COLLECTABLE(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_COLLECTABLE) != 0) #define Z_COLLECTABLE_P(zval_p) Z_COLLECTABLE(*(zval_p)) -/* deprecated: (COPYABLE is the same as IS_ARRAY) */ -#define Z_COPYABLE(zval) (Z_TYPE(zval) == IS_ARRAY) -#define Z_COPYABLE_P(zval_p) Z_COPYABLE(*(zval_p)) - -/* deprecated: (IMMUTABLE is the same as IS_ARRAY && !REFCOUNTED) */ -#define Z_IMMUTABLE(zval) (Z_TYPE_INFO(zval) == IS_ARRAY) -#define Z_IMMUTABLE_P(zval_p) Z_IMMUTABLE(*(zval_p)) -#define Z_OPT_IMMUTABLE(zval) Z_IMMUTABLE(zval_p) -#define Z_OPT_IMMUTABLE_P(zval_p) Z_IMMUTABLE(*(zval_p)) - /* the following Z_OPT_* macros make better code when Z_TYPE_INFO accessed before */ #define Z_OPT_TYPE(zval) (Z_TYPE_INFO(zval) & Z_TYPE_MASK) #define Z_OPT_TYPE_P(zval_p) Z_OPT_TYPE(*(zval_p)) @@ -967,10 +957,6 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_OPT_COLLECTABLE(zval) ((Z_TYPE_INFO(zval) & (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT)) != 0) #define Z_OPT_COLLECTABLE_P(zval_p) Z_OPT_COLLECTABLE(*(zval_p)) -/* deprecated: (COPYABLE is the same as IS_ARRAY) */ -#define Z_OPT_COPYABLE(zval) (Z_OPT_TYPE(zval) == IS_ARRAY) -#define Z_OPT_COPYABLE_P(zval_p) Z_OPT_COPYABLE(*(zval_p)) - #define Z_OPT_ISREF(zval) (Z_OPT_TYPE(zval) == IS_REFERENCE) #define Z_OPT_ISREF_P(zval_p) Z_OPT_ISREF(*(zval_p)) diff --git a/Zend/zend_virtual_cwd.c b/Zend/zend_virtual_cwd.c index f8dabd507783..da27e9fd5426 100644 --- a/Zend/zend_virtual_cwd.c +++ b/Zend/zend_virtual_cwd.c @@ -193,7 +193,7 @@ void virtual_cwd_main_cwd_init(uint8_t reinit) /* {{{ */ main_cwd_state.cwd_length = strlen(cwd); #ifdef ZEND_WIN32 if (main_cwd_state.cwd_length >= 2 && cwd[1] == ':') { - cwd[0] = toupper(cwd[0]); + cwd[0] = toupper((unsigned char)cwd[0]); } #endif main_cwd_state.cwd = strdup(cwd); @@ -269,7 +269,7 @@ CWD_API char *virtual_getcwd_ex(size_t *length) /* {{{ */ *length = state->cwd_length+1; retval = (char *) emalloc(*length+1); memcpy(retval, state->cwd, *length); - retval[0] = toupper(retval[0]); + retval[0] = toupper((unsigned char)retval[0]); retval[*length-1] = DEFAULT_SLASH; retval[*length] = '\0'; return retval; @@ -1112,7 +1112,7 @@ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func if (resolved_path[start] == 0) { goto verify; } - resolved_path[start] = toupper(resolved_path[start]); + resolved_path[start] = toupper((unsigned char)resolved_path[start]); start++; } resolved_path[start++] = DEFAULT_SLASH; @@ -1120,13 +1120,13 @@ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func if (resolved_path[start] == 0) { goto verify; } - resolved_path[start] = toupper(resolved_path[start]); + resolved_path[start] = toupper((unsigned char)resolved_path[start]); start++; } resolved_path[start++] = DEFAULT_SLASH; } else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) { /* skip DRIVE name */ - resolved_path[0] = toupper(resolved_path[0]); + resolved_path[0] = toupper((unsigned char)resolved_path[0]); resolved_path[2] = DEFAULT_SLASH; if (path_length == 2) { resolved_path[3] = '\0'; diff --git a/Zend/zend_virtual_cwd.h b/Zend/zend_virtual_cwd.h index 0c543bba5c68..ad30c0323892 100644 --- a/Zend/zend_virtual_cwd.h +++ b/Zend/zend_virtual_cwd.h @@ -85,7 +85,7 @@ typedef unsigned short mode_t; #define IS_UNC_PATH(path, len) \ (len >= 2 && IS_SLASH(path[0]) && IS_SLASH(path[1])) #define IS_ABSOLUTE_PATH(path, len) \ - (len >= 2 && (/* is local */isalpha(path[0]) && path[1] == ':' || /* is UNC */IS_SLASH(path[0]) && IS_SLASH(path[1]))) + (len >= 2 && (/* is local */isalpha((unsigned char)(path)[0]) && path[1] == ':' || /* is UNC */IS_SLASH(path[0]) && IS_SLASH(path[1]))) #else #ifdef HAVE_DIRENT_H diff --git a/Zend/zend_vm.h b/Zend/zend_vm.h index 093b1158bd82..4f4a48c92205 100644 --- a/Zend/zend_vm.h +++ b/Zend/zend_vm.h @@ -31,6 +31,7 @@ ZEND_API void ZEND_FASTCALL zend_serialize_opcode_handler(zend_op *op); ZEND_API void ZEND_FASTCALL zend_deserialize_opcode_handler(zend_op *op); ZEND_API const void* ZEND_FASTCALL zend_get_opcode_handler_func(const zend_op *op); ZEND_API const zend_op *zend_get_halt_op(void); +ZEND_API const zend_op *zend_get_interrupt_op(void); ZEND_API int ZEND_FASTCALL zend_vm_call_opcode_handler(zend_execute_data *ex); ZEND_API int zend_vm_kind(void); ZEND_API bool zend_gcc_global_regs(void); @@ -38,6 +39,10 @@ ZEND_API bool zend_gcc_global_regs(void); void zend_vm_init(void); void zend_vm_dtor(void); +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL +const struct _zend_op *ZEND_FASTCALL zend_vm_handle_interrupt(struct _zend_execute_data *execute_data, const struct _zend_op *opline); +#endif + END_EXTERN_C() #define ZEND_VM_SET_OPCODE_HANDLER(opline) zend_vm_set_opcode_handler(opline) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 1f7e09d1be35..1de7a7cd4195 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8843,7 +8843,7 @@ ZEND_VM_COLD_CONST_HANDLER(121, ZEND_STRLEN, CONST|TMP|CV, ANY) } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -10624,7 +10624,12 @@ ZEND_VM_DEFINE_OP(137, ZEND_OP_DATA); ZEND_VM_HELPER(zend_interrupt_helper, ANY, ANY) { zend_atomic_bool_store_ex(&EG(vm_interrupt), false); +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + /* opline is &call_interrupt_op. Load orig opline. */ + LOAD_OPLINE(); +#else SAVE_OPLINE(); +#endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index d5860da23b4c..5b52f1941845 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -330,6 +330,11 @@ static zend_op hybrid_halt_op; static zend_vm_opcode_handler_func_t const * zend_opcode_handler_funcs; #endif + +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL +static const zend_op call_interrupt_op; +#endif + #if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID && ZEND_VM_KIND != ZEND_VM_KIND_TAILCALL) || !ZEND_VM_SPEC static zend_vm_opcode_handler_t zend_vm_get_opcode_handler(uint8_t opcode, const zend_op* op); #endif @@ -4027,7 +4032,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_J static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) { zend_atomic_bool_store_ex(&EG(vm_interrupt), false); +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + /* opline is &call_interrupt_op. Load orig opline. */ + LOAD_OPLINE(); +#else SAVE_OPLINE(); +#endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { @@ -6083,7 +6093,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -18167,7 +18177,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_STRLEN_SPEC_T } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -40758,7 +40768,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_STRLEN_SPEC_C } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -53488,6 +53498,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NULL_HANDLER( zend_error_noreturn(E_ERROR, "Invalid opcode %d/%d/%d.", OPLINE->opcode, OPLINE->op1_type, OPLINE->op2_type); } +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL +static ZEND_COLD zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt(ZEND_OPCODE_HANDLER_ARGS) { + SAVE_OPLINE(); + return &call_interrupt_op; +} +#endif #if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID) # undef ZEND_VM_TAIL_CALL @@ -53518,11 +53534,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_NULL_HANDLER( ZEND_VM_TAIL_CALL(opline->handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); \ } while (0) # define ZEND_VM_DISPATCH_TO_LEAVE_HELPER(helper) opline = &call_leave_op; SAVE_OPLINE(); ZEND_VM_CONTINUE() -# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) +# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) # define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE() # define ZEND_VM_LEAVE() ZEND_VM_CONTINUE() static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS); +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt(ZEND_OPCODE_HANDLER_ARGS); +static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS); static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NULL_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS); static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HALT_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS); static zend_never_inline const zend_op *ZEND_OPCODE_HANDLER_CCONV zend_leave_helper_SPEC_TAILCALL(zend_execute_data *ex, const zend_op *opline); @@ -53533,6 +53551,9 @@ static const zend_op call_halt_op = { static const zend_op call_leave_op = { .handler = zend_leave_helper_SPEC_TAILCALL, }; +static const zend_op call_interrupt_op = { + .handler = zend_interrupt_helper_SPEC_TAILCALL, +}; static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV_EX zend_add_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2); static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV_EX zend_sub_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2); @@ -56685,7 +56706,12 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_JMP_FO static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS) { zend_atomic_bool_store_ex(&EG(vm_interrupt), false); +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + /* opline is &call_interrupt_op. Load orig opline. */ + LOAD_OPLINE(); +#else SAVE_OPLINE(); +#endif if (zend_atomic_bool_load_ex(&EG(timed_out))) { zend_timeout(); } else if (zend_interrupt_function) { @@ -58741,7 +58767,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_STRLE } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -70723,7 +70749,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_STRLEN_SPEC_TMP_TA } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -93214,7 +93240,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_STRLEN_SPEC_CV_TAI } ZVAL_COPY(&tmp, value); - if (zend_parse_arg_str_weak(&tmp, &str, 1)) { + if ((str = zend_parse_arg_str_weak(&tmp, 1)) != NULL) { ZVAL_LONG(EX_VAR(opline->result.var), ZSTR_LEN(str)); zval_ptr_dtor(&tmp); break; @@ -105847,13 +105873,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HALT_TAILCALL_HAND return (zend_op*) ZEND_VM_ENTER_BIT; } +static ZEND_COLD zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS) { + SAVE_OPLINE(); + ZEND_VM_TAIL_CALL(zend_interrupt_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); +} /* The following helpers can not tailcall due to signature mismatch. Redefine some macros so they do not enforce tailcall. */ #pragma push_macro("ZEND_VM_CONTINUE") #undef ZEND_VM_CONTINUE #pragma push_macro("ZEND_VM_INTERRUPT") #undef ZEND_VM_INTERRUPT #define ZEND_VM_CONTINUE(handler) return opline -#define ZEND_VM_INTERRUPT() return zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) +#define ZEND_VM_INTERRUPT() return zend_interrupt(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU) static zend_never_inline ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV_EX zend_add_helper_SPEC_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_EX zval *op_1, zval *op_2) { USE_OPLINE @@ -122896,6 +122926,22 @@ ZEND_API const zend_op *zend_get_halt_op(void) #endif } +ZEND_API const zend_op *zend_get_interrupt_op(void) +{ +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + return &call_interrupt_op; +#else + return NULL; +#endif +} + +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL +ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_vm_handle_interrupt(ZEND_OPCODE_HANDLER_ARGS) +{ + return zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); +} +#endif + ZEND_API int zend_vm_kind(void) { return ZEND_VM_KIND; diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index 3237a696a695..beabe36688df 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -161,6 +161,22 @@ ZEND_API const zend_op *zend_get_halt_op(void) #endif } +ZEND_API const zend_op *zend_get_interrupt_op(void) +{ +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + return &call_interrupt_op; +#else + return NULL; +#endif +} + +#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL +ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_vm_handle_interrupt(ZEND_OPCODE_HANDLER_ARGS) +{ + return zend_interrupt_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); +} +#endif + ZEND_API int zend_vm_kind(void) { return ZEND_VM_KIND; diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index d26d6a166dda..8e65bc0a28ea 100755 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -1591,6 +1591,19 @@ function gen_halt_handler($f, $kind) { out($f,"}\n\n"); } +function gen_interrupt_func($f, $kind, $spec) { + $cconv = $kind === ZEND_VM_KIND_TAILCALL ? 'ZEND_OPCODE_HANDLER_CCONV' : 'ZEND_OPCODE_HANDLER_FUNC_CCONV'; + $variant = $kind === ZEND_VM_KIND_TAILCALL ? '_TAILCALL' : ''; + out($f, "static ZEND_COLD zend_never_inline ZEND_OPCODE_HANDLER_RET {$cconv} zend_interrupt{$variant}(ZEND_OPCODE_HANDLER_ARGS) {\n"); + out($f,"\tSAVE_OPLINE();\n"); + if ($kind === ZEND_VM_KIND_TAILCALL) { + out($f,"\tZEND_VM_TAIL_CALL(zend_interrupt_helper".($spec?"_SPEC":"")."_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU));\n"); + } else { + out($f, "\treturn &call_interrupt_op;\n"); + } + out($f, "}\n"); +} + function extra_spec_name($extra_spec) { global $prefix; @@ -1801,10 +1814,14 @@ function gen_executor_code($f, $spec, $kind, $prolog, &$switch_labels = array()) switch ($kind) { case ZEND_VM_KIND_CALL: gen_null_handler($f, $kind); + out($f, "#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL\n"); + gen_interrupt_func($f, $kind, $spec); + out($f, "#endif\n"); break; case ZEND_VM_KIND_TAILCALL: gen_null_handler($f, $kind); gen_halt_handler($f, $kind); + gen_interrupt_func($f, $kind, $spec); break; case ZEND_VM_KIND_SWITCH: out($f,"default: ZEND_NULL_LABEL:\n"); @@ -1840,7 +1857,7 @@ function gen_executor_code($f, $spec, $kind, $prolog, &$switch_labels = array()) out($f, "#pragma push_macro(\"ZEND_VM_INTERRUPT\")\n"); out($f, "#undef ZEND_VM_INTERRUPT\n"); out($f, "#define ZEND_VM_CONTINUE(handler) return opline\n"); - out($f, "#define ZEND_VM_INTERRUPT() return zend_interrupt_helper".($spec?"_SPEC":"")."(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)\n"); + out($f, "#define ZEND_VM_INTERRUPT() return zend_interrupt(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)\n"); out($f, $delayed_helpers); out($f, "#pragma pop_macro(\"ZEND_VM_INTERRUPT\")\n"); out($f, "#pragma pop_macro(\"ZEND_VM_CONTINUE\")\n"); @@ -1895,7 +1912,10 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) if ($kind == ZEND_VM_KIND_HYBRID || $kind == ZEND_VM_KIND_CALL) { out($f,"#if ZEND_VM_KIND == ZEND_VM_KIND_HYBRID || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL\n\n"); out($f,"static zend_vm_opcode_handler_func_t const * zend_opcode_handler_funcs;\n"); - out($f,"#endif\n"); + out($f,"#endif\n\n"); + out($f,"#if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL\n"); + out($f,"static const zend_op call_interrupt_op;\n"); + out($f,"#endif\n\n"); } out($f,"#if (ZEND_VM_KIND != ZEND_VM_KIND_HYBRID && ZEND_VM_KIND != ZEND_VM_KIND_TAILCALL) || !ZEND_VM_SPEC\n"); out($f,"static zend_vm_opcode_handler_t zend_vm_get_opcode_handler(uint8_t opcode, const zend_op* op);\n"); @@ -2136,11 +2156,13 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f," ZEND_VM_TAIL_CALL(opline->handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); \\\n"); out($f," } while (0)\n"); out($f,"# define ZEND_VM_DISPATCH_TO_LEAVE_HELPER(helper) opline = &call_leave_op; SAVE_OPLINE(); ZEND_VM_CONTINUE()\n"); - out($f,"# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_helper".($spec?"_SPEC":"")."_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))\n"); + out($f,"# define ZEND_VM_INTERRUPT() ZEND_VM_TAIL_CALL(zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU))\n"); out($f,"# define ZEND_VM_ENTER_EX() ZEND_VM_INTERRUPT_CHECK(); ZEND_VM_CONTINUE()\n"); out($f,"# define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n"); out($f,"\n"); out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_helper".($spec?"_SPEC":"")."_TAILCALL(ZEND_OPCODE_HANDLER_ARGS);\n"); + out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_interrupt(ZEND_OPCODE_HANDLER_ARGS);\n"); + out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_interrupt_TAILCALL(ZEND_OPCODE_HANDLER_ARGS);\n"); out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_NULL_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS);\n"); out($f,"static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_HALT_TAILCALL_HANDLER(ZEND_OPCODE_HANDLER_ARGS);\n"); out($f,"static zend_never_inline const zend_op *ZEND_OPCODE_HANDLER_CCONV zend_leave_helper_SPEC_TAILCALL(zend_execute_data *ex, const zend_op *opline);\n"); @@ -2151,6 +2173,9 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name) out($f,"static const zend_op call_leave_op = {\n"); out($f," .handler = zend_leave_helper_SPEC_TAILCALL,\n"); out($f,"};\n"); + out($f,"static const zend_op call_interrupt_op = {\n"); + out($f," .handler = zend_interrupt_helper_SPEC_TAILCALL,\n"); + out($f,"};\n"); out($f,"\n"); gen_executor_code($f, $spec, ZEND_VM_KIND_TAILCALL, $m[1]); diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 7303226a5d42..ad0308f44e20 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -58,10 +58,10 @@ static zend_class_entry *zend_ce_weakmap; static zend_object_handlers zend_weakref_handlers; static zend_object_handlers zend_weakmap_handlers; -#define zend_weakref_from(o) ((zend_weakref*)(((char*) o) - XtOffsetOf(zend_weakref, std))) +#define zend_weakref_from(o) (ZEND_CONTAINER_OF(o, zend_weakref, std)) #define zend_weakref_fetch(z) zend_weakref_from(Z_OBJ_P(z)) -#define zend_weakmap_from(o) ((zend_weakmap*)(((char*) o) - XtOffsetOf(zend_weakmap, std))) +#define zend_weakmap_from(o) (ZEND_CONTAINER_OF(o, zend_weakmap, std)) #define zend_weakmap_fetch(z) zend_weakmap_from(Z_OBJ_P(z)) static inline void zend_weakref_unref_single( @@ -796,7 +796,7 @@ void zend_register_weakref_ce(void) /* {{{ */ zend_ce_weakref->default_object_handlers = &zend_weakref_handlers; memcpy(&zend_weakref_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - zend_weakref_handlers.offset = XtOffsetOf(zend_weakref, std); + zend_weakref_handlers.offset = offsetof(zend_weakref, std); zend_weakref_handlers.free_obj = zend_weakref_free; zend_weakref_handlers.get_debug_info = zend_weakref_get_debug_info; @@ -809,7 +809,7 @@ void zend_register_weakref_ce(void) /* {{{ */ zend_ce_weakmap->default_object_handlers = &zend_weakmap_handlers; memcpy(&zend_weakmap_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - zend_weakmap_handlers.offset = XtOffsetOf(zend_weakmap, std); + zend_weakmap_handlers.offset = offsetof(zend_weakmap, std); zend_weakmap_handlers.free_obj = zend_weakmap_free_obj; zend_weakmap_handlers.read_dimension = zend_weakmap_read_dimension; zend_weakmap_handlers.write_dimension = zend_weakmap_write_dimension; diff --git a/build/gen_stub.php b/build/gen_stub.php index 619c4c905b60..34bbd34fd37f 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3385,6 +3385,7 @@ public function __construct( private /* readonly */ array $propertyInfos, public array $funcInfos, private readonly array $enumCaseInfos, + private readonly bool $generateCNameTable, public readonly ?string $cond, public ?int $phpVersionIdMinimumCompatibility, public readonly bool $isUndocumentable, @@ -3599,6 +3600,25 @@ public function getCDeclarations(): string $code .= "} {$cEnumName};\n"; + $caseCount = count($this->enumCaseInfos); + + if ($this->generateCNameTable && $caseCount > 0) { + $cPrefix = 'ZEND_ENUM_' . str_replace('\\', '_', $this->name->toString()); + $useGuard = $cPrefix . '_USE_NAME_TABLE'; + + $code .= "\n#define {$cPrefix}_CASE_COUNT {$caseCount}\n"; + $code .= "\n#ifdef {$useGuard}\n"; + $code .= "static const char *{$cEnumName}_case_names[{$cPrefix}_CASE_COUNT + 1] = {\n"; + + foreach ($this->enumCaseInfos as $case) { + $cName = $cPrefix . '_' . $case->name->case; + $code .= "\t[{$cName}] = \"{$case->name->case}\",\n"; + } + + $code .= "};\n"; + $code .= "#endif\n"; + } + if ($this->cond) { $code .= "#endif\n"; } @@ -5149,6 +5169,7 @@ function parseClass( $isStrictProperties = array_key_exists('strict-properties', $tagMap); $isNotSerializable = array_key_exists('not-serializable', $tagMap); $isUndocumentable = $isUndocumentable || array_key_exists('undocumentable', $tagMap); + $generateCNameTable = array_key_exists('c-name-table', $tagMap); foreach ($tags as $tag) { if ($tag->name === 'alias') { $alias = $tag->getValue(); @@ -5210,6 +5231,7 @@ function parseClass( $properties, $methods, $enumCases, + $generateCNameTable, $cond, $minimumPhpVersionIdCompatibility, $isUndocumentable diff --git a/configure.ac b/configure.ac index 6c517ecc0a1e..7b1fc3af7847 100644 --- a/configure.ac +++ b/configure.ac @@ -1689,6 +1689,7 @@ PHP_ADD_SOURCES([main/streams], m4_normalize([ memory.c mmap.c plain_wrapper.c + stream_errors.c streams.c transports.c userspace.c diff --git a/docs/release-process.md b/docs/release-process.md index 7dd009676dd4..c07f328f2d8c 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -301,12 +301,27 @@ slightly different steps. We'll call attention where the steps differ. > Only release tags should have version numbers in these files that do not > end in `-dev` (e.g., `8.1.7`, `8.1.7RC1`, `8.2.0alpha1`, etc.). - Do not forget to merge up PHP-X.Y all the way to master. When resolving - the conflicts, ignore the changes from PHP-X.Y in higher branches. It - means using something like `git checkout --ours .` when on PHP.X.Y+1 or - master after the merge resulting in the conflicts. + Do not forget to merge up PHP-X.Y all the way to master. - Be sure to set up a merge driver for the NEWS file as described in + ```shell + git switch PHP-X.Y+1 # starting from your release branch + git merge PHP-X.Y + # repeat # Merge up all the way + git switch master + git merge PHP-X.Y+n # latest release branch + ``` + + When resolving the conflicts, ignore the changes from PHP-X.Y in higher + branches when on PHP.X.Y+1 or master after the merge resulting in the + conflicts. + + ```shell + git checkout --ours main/php_version.h Zend/zend.h configure.ac + git add main/php_version.h Zend/zend.h configure.ac + git merge --continue + ``` + + Be sure to set up a merge driver for the `NEWS` file as described in the [Git FAQ page on the PHP wiki][gitfaq-mandatory]. 11. Push the changes to the `php-src`. diff --git a/docs/source/core/data-structures/zend_string.rst b/docs/source/core/data-structures/zend_string.rst index 2b07611e3425..5b1d3ac0ccfb 100644 --- a/docs/source/core/data-structures/zend_string.rst +++ b/docs/source/core/data-structures/zend_string.rst @@ -29,7 +29,7 @@ in bytes, and the ``val`` field contains the actual string data. You may wonder why the ``val`` field is declared as ``char val[1]``. This is called the `struct hack`_ in C. It is used to create structs with a flexible size, namely by allowing the last element -to be expanded arbitrarily. In this case, the size of ``zend_string`` depends on the strings length, +to be expanded arbitrarily. In this case, the size of ``zend_string`` depends on the string's length, which is determined at runtime (see ``_ZSTR_STRUCT_SIZE``). When allocating the string, we append enough bytes to the allocation to hold the strings content. diff --git a/ext/bcmath/bcmath.c b/ext/bcmath/bcmath.c index a1e6d48be523..0e0a9a23b94a 100644 --- a/ext/bcmath/bcmath.c +++ b/ext/bcmath/bcmath.c @@ -155,6 +155,16 @@ static zend_always_inline zend_result bcmath_check_scale(zend_long scale, uint32 return SUCCESS; } +static zend_result bcmath_check_precision(zend_long precision, uint32_t arg_num) +{ + if (ZEND_LONG_INT_OVFL(precision)) { + zend_argument_value_error(arg_num, "must be between " ZEND_LONG_FMT " and %d", + (zend_long) ZEND_LONG_MIN, INT_MAX); + return FAILURE; + } + return SUCCESS; +} + static void php_long2num(bc_num *num, zend_long lval) { *num = bc_long2num(lval); @@ -795,6 +805,10 @@ PHP_FUNCTION(bcround) Z_PARAM_ENUM(rounding_mode, rounding_mode_ce) ZEND_PARSE_PARAMETERS_END(); + if (bcmath_check_precision(precision, 2) == FAILURE) { + RETURN_THROWS(); + } + switch (rounding_mode) { case ZEND_ENUM_RoundingMode_HalfAwayFromZero: case ZEND_ENUM_RoundingMode_HalfTowardsZero: @@ -875,10 +889,7 @@ static int bcmath_number_compare(zval *op1, zval *op2); #endif #define CHECK_SCALE_OVERFLOW(scale) (scale > INT_MAX) -static zend_always_inline bcmath_number_obj_t *get_bcmath_number_from_obj(const zend_object *obj) -{ - return (bcmath_number_obj_t*)((char*)(obj) - XtOffsetOf(bcmath_number_obj_t, std)); -} +#define get_bcmath_number_from_obj(obj) ZEND_CONTAINER_OF(obj, bcmath_number_obj_t, std) static zend_always_inline bcmath_number_obj_t *get_bcmath_number_from_zval(const zval *zv) { @@ -1024,7 +1035,7 @@ static void bcmath_number_register_class(void) bcmath_number_ce->default_object_handlers = &bcmath_number_obj_handlers; memcpy(&bcmath_number_obj_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - bcmath_number_obj_handlers.offset = XtOffsetOf(bcmath_number_obj_t, std); + bcmath_number_obj_handlers.offset = offsetof(bcmath_number_obj_t, std); bcmath_number_obj_handlers.free_obj = bcmath_number_free; bcmath_number_obj_handlers.clone_obj = bcmath_number_clone; bcmath_number_obj_handlers.do_operation = bcmath_number_do_operation; @@ -1214,7 +1225,7 @@ static zend_result bc_num_from_obj_or_str_or_long( bc_num *num, size_t *full_scale, const zend_object *obj, const zend_string *str, zend_long lval) { if (obj) { - bcmath_number_obj_t *intern = get_bcmath_number_from_obj(obj); + const bcmath_number_obj_t *intern = get_bcmath_number_from_obj(obj); *num = intern->num; if (full_scale) { *full_scale = intern->scale; @@ -1797,6 +1808,10 @@ PHP_METHOD(BcMath_Number, round) Z_PARAM_ENUM(rounding_mode, rounding_mode_ce); ZEND_PARSE_PARAMETERS_END(); + if (bcmath_check_precision(precision, 1) == FAILURE) { + RETURN_THROWS(); + } + switch (rounding_mode) { case ZEND_ENUM_RoundingMode_HalfAwayFromZero: case ZEND_ENUM_RoundingMode_HalfTowardsZero: diff --git a/ext/bcmath/libbcmath/src/round.c b/ext/bcmath/libbcmath/src/round.c index bbca7e758fb7..86cc7d085ec2 100644 --- a/ext/bcmath/libbcmath/src/round.c +++ b/ext/bcmath/libbcmath/src/round.c @@ -67,12 +67,10 @@ size_t bc_round(bc_num num, zend_long precision, zend_enum_RoundingMode mode, bc return 0; } - /* If precision is -3, it becomes 1000. */ - if (UNEXPECTED(precision == ZEND_LONG_MIN)) { - *result = bc_new_num((size_t) ZEND_LONG_MAX + 2, 0); - } else { - *result = bc_new_num(-precision + 1, 0); - } + /* If precision is -3, it becomes 1000. Negate in unsigned space so + * precision == ZEND_LONG_MIN doesn't overflow signed long. */ + zend_ulong magnitude = -(zend_ulong) precision; + *result = bc_new_num((size_t) magnitude + 1, 0); (*result)->n_value[0] = 1; (*result)->n_sign = num->n_sign; return 0; diff --git a/ext/bcmath/tests/bcround_precision_bounds.phpt b/ext/bcmath/tests/bcround_precision_bounds.phpt new file mode 100644 index 000000000000..4c4f910476a7 --- /dev/null +++ b/ext/bcmath/tests/bcround_precision_bounds.phpt @@ -0,0 +1,28 @@ +--TEST-- +bcround() and BcMath\Number::round() reject $precision above INT_MAX +--EXTENSIONS-- +bcmath +--SKIPIF-- + +--FILE-- +getMessage() . \PHP_EOL; +} +try { + (new BcMath\Number('1'))->round(PHP_INT_MAX); +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + (new BcMath\Number('1'))->round(2147483648); // INT_MAX + 1 +} catch (\ValueError $e) { + echo $e->getMessage() . \PHP_EOL; +} +?> +--EXPECTF-- +bcround(): Argument #2 ($precision) must be between %i and %d +BcMath\Number::round(): Argument #1 ($precision) must be between %i and %d +BcMath\Number::round(): Argument #1 ($precision) must be between %i and %d diff --git a/ext/bz2/bz2.c b/ext/bz2/bz2.c index c505005ab00a..512632fe8a22 100644 --- a/ext/bz2/bz2.c +++ b/ext/bz2/bz2.c @@ -519,11 +519,15 @@ PHP_FUNCTION(bzdecompress) bzs.bzalloc = NULL; bzs.bzfree = NULL; + if (source_len > UINT_MAX) { + zend_argument_value_error(1, "must have a length less than or equal to %u", UINT_MAX); + RETURN_THROWS(); + } + if (BZ2_bzDecompressInit(&bzs, 0, (int)small) != BZ_OK) { RETURN_FALSE; } - // TODO Check source string length fits in unsigned int bzs.next_in = source; bzs.avail_in = source_len; diff --git a/ext/bz2/bz2_filter.c b/ext/bz2/bz2_filter.c index e1b24f6319f2..09c49fa76687 100644 --- a/ext/bz2/bz2_filter.c +++ b/ext/bz2/bz2_filter.c @@ -381,9 +381,14 @@ static const php_stream_filter_ops php_bz2_compress_ops = { static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *filterparams, bool persistent) { const php_stream_filter_ops *fops = NULL; + php_stream_filter_seekable_t write_seekable; php_bz2_filter_data *data; int status = BZ_OK; + if (php_stream_filter_parse_write_seek_mode(filterparams, &write_seekable) == FAILURE) { + return NULL; + } + /* Create this filter */ data = pecalloc(1, sizeof(php_bz2_filter_data), persistent); @@ -476,7 +481,7 @@ static php_stream_filter *php_bz2_filter_create(const char *filtername, zval *fi return NULL; } - return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START); + return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START, write_seekable); } const php_stream_filter_factory php_bz2_filter_factory = { diff --git a/ext/bz2/tests/bz2_filter_seek_compress.phpt b/ext/bz2/tests/bz2_filter_seek_compress.phpt index 0656b244484d..557021c9fd0d 100644 --- a/ext/bz2/tests/bz2_filter_seek_compress.phpt +++ b/ext/bz2/tests/bz2_filter_seek_compress.phpt @@ -1,55 +1,51 @@ --TEST-- -bzip2.compress filter with seek to start +bzip2.compress write filter is not reset on seek --EXTENSIONS-- bz2 --FILE-- $size1 ? "YES" : "NO") . "\n"; - +/* Seek to middle also succeeds */ $result = fseek($fp, 50, SEEK_SET); echo "Seek to middle: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n"; fclose($fp); +/* Verify the compressed output is still valid */ $fp = fopen($file, 'r'); stream_filter_append($fp, 'bzip2.decompress', STREAM_FILTER_READ); $content = stream_get_contents($fp); fclose($fp); -echo "Decompressed content matches text2: " . ($content === $text2 ? "YES" : "NO") . "\n"; +echo "Decompressed content matches: " . ($content === $text ? "YES" : "NO") . "\n"; ?> --CLEAN-- --EXPECTF-- -Size after first write: 40 +Size after write: %d Seek to start: SUCCESS -Size after second write: 98 -Second write is larger: YES - -Warning: fseek(): Stream filter bzip2.compress is seekable only to start position in %s on line %d -Seek to middle: FAILURE -Decompressed content matches text2: YES +Seek to middle: SUCCESS +Decompressed content matches: YES diff --git a/ext/bz2/tests/bz2_filter_write_seek_modes.phpt b/ext/bz2/tests/bz2_filter_write_seek_modes.phpt new file mode 100644 index 000000000000..b3b4fa39eb16 --- /dev/null +++ b/ext/bz2/tests/bz2_filter_write_seek_modes.phpt @@ -0,0 +1,54 @@ +--TEST-- +bzip2.compress write filter: write_seek_mode parameter +--EXTENSIONS-- +bz2 +--FILE-- + 'reset']); +fwrite($fp, $text1); +ftruncate($fp, 0); +var_dump(fseek($fp, 0, SEEK_SET) === 0); +fwrite($fp, $text2); +fclose($fp); + +$fp = fopen($file, 'r'); +stream_filter_append($fp, 'bzip2.decompress', STREAM_FILTER_READ); +$decoded = stream_get_contents($fp); +fclose($fp); +var_dump($decoded === $text2); + +/* "strict" */ +$fp = fopen($file, 'w+'); +stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, + ['write_seek_mode' => 'strict']); +fwrite($fp, $text1); +var_dump(@fseek($fp, 0, SEEK_SET) === -1); +fclose($fp); + +/* Invalid mode: ValueError */ +$fp = fopen($file, 'w+'); +stream_filter_append($fp, 'bzip2.compress', STREAM_FILTER_WRITE, + ['write_seek_mode' => 'nope']); +fclose($fp); + +?> +--CLEAN-- + +--EXPECTF-- +bool(true) +bool(true) +bool(true) + +Warning: stream_filter_append(): "write_seek_mode" filter parameter must be one of "preserve", "reset", or "strict" in %s + +Warning: stream_filter_append(): Unable to create or locate filter "bzip2.compress" in %s diff --git a/ext/bz2/tests/bzdecompress_input_too_large.phpt b/ext/bz2/tests/bzdecompress_input_too_large.phpt new file mode 100644 index 000000000000..88c93d366c54 --- /dev/null +++ b/ext/bz2/tests/bzdecompress_input_too_large.phpt @@ -0,0 +1,24 @@ +--TEST-- +bzdecompress() rejects input larger than 4294967296 +--EXTENSIONS-- +bz2 +--INI-- +memory_limit=8G +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECT-- +bzdecompress(): Argument #1 ($data) must have a length less than or equal to 4294967295 diff --git a/ext/com_dotnet/com_extension.c b/ext/com_dotnet/com_extension.c index 1fe34cce06b5..e9c6e46884ce 100644 --- a/ext/com_dotnet/com_extension.c +++ b/ext/com_dotnet/com_extension.c @@ -116,11 +116,11 @@ static PHP_INI_MH(OnTypeLibFileUpdate) } /* Remove leading/training white spaces on search_string */ - while (isspace(*typelib_name)) {/* Ends on '\0' in worst case */ + while (isspace((unsigned char)*typelib_name)) {/* Ends on '\0' in worst case */ typelib_name ++; } ptr = typelib_name + strlen(typelib_name) - 1; - while ((ptr != typelib_name) && isspace(*ptr)) { + while ((ptr != typelib_name) && isspace((unsigned char)*ptr)) { *ptr = '\0'; ptr--; } diff --git a/ext/com_dotnet/tests/variants.phpt b/ext/com_dotnet/tests/variants.phpt index 8547e65efb33..a145fec87ae2 100644 --- a/ext/com_dotnet/tests/variants.phpt +++ b/ext/com_dotnet/tests/variants.phpt @@ -43,7 +43,7 @@ foreach ($values as $t => $val) { echo "OK!"; ?> ---EXPECT-- +--EXPECTF-- -- add: 84 cat: 4242 @@ -142,8 +142,8 @@ mul: 0 and: 0 div: variant_div(42, ) - exception Division by zero - code 80020012 + exception %s + code 800200%x eqv: -43 idiv: @@ -258,8 +258,8 @@ mul: 0 and: 0 div: variant_div(3.5, ) - exception Division by zero - code 80020012 + exception %s + code 800200%x eqv: -5 idiv: diff --git a/ext/curl/config.w32 b/ext/curl/config.w32 index c3a55652b638..567699f3b748 100644 --- a/ext/curl/config.w32 +++ b/ext/curl/config.w32 @@ -12,11 +12,18 @@ if (PHP_CURL != "no") { SETUP_ZLIB_LIB("curl", PHP_CURL) && (CHECK_LIB("normaliz.lib", "curl", PHP_CURL) && CHECK_LIB("libssh2.lib", "curl", PHP_CURL) && - CHECK_LIB("nghttp2.lib", "curl", PHP_CURL) && - CHECK_LIB("brotlidec.lib", "curl", PHP_CURL) && - CHECK_LIB("brotlicommon.lib", "curl", PHP_CURL) && - CHECK_LIB("libzstd.lib", "curl", PHP_CURL)) + CHECK_LIB("nghttp2.lib", "curl", PHP_CURL)) ) { + if (!(CHECK_HEADER("brotli/decode.h", "CFLAGS_CURL") && + CHECK_LIB("brotlidec.lib;brotlidec-static.lib", "curl", PHP_CURL) && + CHECK_LIB("brotlicommon.lib;brotlicommon-static.lib", "curl", PHP_CURL) + )) { + WARNING("brotli in curl not enabled; libraries or headers not found"); + } + if (!(CHECK_LIB("libzstd.lib;libzstd_a.lib", "curl", PHP_CURL) + )) { + WARNING("zstd in curl not enabled; library not found"); + } EXTENSION("curl", "interface.c multi.c share.c curl_file.c"); AC_DEFINE('HAVE_CURL', 1, "Define to 1 if the PHP extension 'curl' is available."); ADD_FLAG("CFLAGS_CURL", "/D PHP_CURL_EXPORTS=1"); diff --git a/ext/curl/curl_private.h b/ext/curl/curl_private.h index 7058e9df9241..77b0628ee42a 100644 --- a/ext/curl/curl_private.h +++ b/ext/curl/curl_private.h @@ -151,15 +151,11 @@ void _php_setup_easy_copy_handlers(php_curl *ch, php_curl *source); /* Consumes `zv` */ zend_long php_curl_get_long(zval *zv); -static inline php_curl *curl_from_obj(zend_object *obj) { - return (php_curl *)((char *)(obj) - XtOffsetOf(php_curl, std)); -} +#define curl_from_obj(obj) ZEND_CONTAINER_OF(obj, php_curl, std) #define Z_CURL_P(zv) curl_from_obj(Z_OBJ_P(zv)) -static inline php_curlsh *curl_share_from_obj(zend_object *obj) { - return (php_curlsh *)((char *)(obj) - XtOffsetOf(php_curlsh, std)); -} +#define curl_share_from_obj(obj) ZEND_CONTAINER_OF(obj, php_curlsh, std) #define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv)) diff --git a/ext/curl/interface.c b/ext/curl/interface.c index 33f8cebd5a6c..ed544866a886 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -357,7 +357,7 @@ PHP_MINIT_FUNCTION(curl) curl_ce->default_object_handlers = &curl_object_handlers; memcpy(&curl_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - curl_object_handlers.offset = XtOffsetOf(php_curl, std); + curl_object_handlers.offset = offsetof(php_curl, std); curl_object_handlers.free_obj = curl_free_obj; curl_object_handlers.get_gc = curl_get_gc; curl_object_handlers.get_constructor = curl_get_constructor; diff --git a/ext/curl/multi.c b/ext/curl/multi.c index 98a5e62b5564..0e329e58f977 100644 --- a/ext/curl/multi.c +++ b/ext/curl/multi.c @@ -48,9 +48,7 @@ zend_class_entry *curl_multi_ce; -static inline php_curlm *curl_multi_from_obj(zend_object *obj) { - return (php_curlm *)((char *)(obj) - XtOffsetOf(php_curlm, std)); -} +#define curl_multi_from_obj(obj) ZEND_CONTAINER_OF(obj, php_curlm, std) #define Z_CURL_MULTI_P(zv) curl_multi_from_obj(Z_OBJ_P(zv)) @@ -605,7 +603,7 @@ void curl_multi_register_handlers(void) { curl_multi_ce->default_object_handlers = &curl_multi_handlers; memcpy(&curl_multi_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - curl_multi_handlers.offset = XtOffsetOf(php_curlm, std); + curl_multi_handlers.offset = offsetof(php_curlm, std); curl_multi_handlers.free_obj = curl_multi_free_obj; curl_multi_handlers.get_gc = curl_multi_get_gc; curl_multi_handlers.get_constructor = curl_multi_get_constructor; diff --git a/ext/curl/share.c b/ext/curl/share.c index bfd12238eec1..56cd804658a4 100644 --- a/ext/curl/share.c +++ b/ext/curl/share.c @@ -303,7 +303,7 @@ void curl_share_register_handlers(void) { curl_share_ce->default_object_handlers = &curl_share_handlers; memcpy(&curl_share_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - curl_share_handlers.offset = XtOffsetOf(php_curlsh, std); + curl_share_handlers.offset = offsetof(php_curlsh, std); curl_share_handlers.free_obj = curl_share_free_obj; curl_share_handlers.get_constructor = curl_share_get_constructor; curl_share_handlers.clone_obj = NULL; @@ -324,7 +324,7 @@ void curl_share_persistent_register_handlers(void) { curl_share_persistent_ce->default_object_handlers = &curl_share_persistent_handlers; memcpy(&curl_share_persistent_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - curl_share_persistent_handlers.offset = XtOffsetOf(php_curlsh, std); + curl_share_persistent_handlers.offset = offsetof(php_curlsh, std); curl_share_persistent_handlers.get_constructor = curl_share_persistent_get_constructor; curl_share_persistent_handlers.clone_obj = NULL; curl_share_persistent_handlers.compare = zend_objects_not_comparable; diff --git a/ext/date/lib/parse_date.c b/ext/date/lib/parse_date.c index 65d99806642d..eb324f8430c5 100644 --- a/ext/date/lib/parse_date.c +++ b/ext/date/lib/parse_date.c @@ -518,7 +518,7 @@ static timelib_sll timelib_get_nr(const char **ptr, int max_length) static void timelib_skip_day_suffix(const char **ptr) { - if (isspace(**ptr)) { + if (isspace((unsigned char)**ptr)) { return; } if (!timelib_strncasecmp(*ptr, "nd", 2) || !timelib_strncasecmp(*ptr, "rd", 2) ||!timelib_strncasecmp(*ptr, "st", 2) || !timelib_strncasecmp(*ptr, "th", 2)) { @@ -859,7 +859,7 @@ static timelib_long timelib_parse_tz_cor(const char **ptr, int *tz_not_found) *tz_not_found = 1; - while (isdigit(**ptr) || **ptr == ':') { + while (isdigit((unsigned char)**ptr) || **ptr == ':') { ++*ptr; } end = *ptr; @@ -924,7 +924,7 @@ static timelib_long timelib_parse_tz_minutes(const char **ptr, timelib_time *t) } ++*ptr; - while (isdigit(**ptr)) { + while (isdigit((unsigned char)**ptr)) { ++*ptr; } diff --git a/ext/date/lib/parse_date.re b/ext/date/lib/parse_date.re index ffb3e8e35913..f4a392753249 100644 --- a/ext/date/lib/parse_date.re +++ b/ext/date/lib/parse_date.re @@ -516,7 +516,7 @@ static timelib_sll timelib_get_nr(const char **ptr, int max_length) static void timelib_skip_day_suffix(const char **ptr) { - if (isspace(**ptr)) { + if (isspace((unsigned char)**ptr)) { return; } if (!timelib_strncasecmp(*ptr, "nd", 2) || !timelib_strncasecmp(*ptr, "rd", 2) ||!timelib_strncasecmp(*ptr, "st", 2) || !timelib_strncasecmp(*ptr, "th", 2)) { @@ -857,7 +857,7 @@ static timelib_long timelib_parse_tz_cor(const char **ptr, int *tz_not_found) *tz_not_found = 1; - while (isdigit(**ptr) || **ptr == ':') { + while (isdigit((unsigned char)**ptr) || **ptr == ':') { ++*ptr; } end = *ptr; @@ -922,7 +922,7 @@ static timelib_long timelib_parse_tz_minutes(const char **ptr, timelib_time *t) } ++*ptr; - while (isdigit(**ptr)) { + while (isdigit((unsigned char)**ptr)) { ++*ptr; } diff --git a/ext/date/lib/parse_iso_intervals.c b/ext/date/lib/parse_iso_intervals.c index 1fc0ede9679c..5dcbfe350639 100644 --- a/ext/date/lib/parse_iso_intervals.c +++ b/ext/date/lib/parse_iso_intervals.c @@ -985,10 +985,10 @@ void timelib_strtointerval(const char *s, size_t len, in.errors->error_messages = NULL; if (len > 0) { - while (isspace(*s) && s < e) { + while (isspace((unsigned char)*s) && s < e) { s++; } - while (isspace(*e) && e > s) { + while (isspace((unsigned char)*e) && e > s) { e--; } } diff --git a/ext/date/lib/parse_iso_intervals.re b/ext/date/lib/parse_iso_intervals.re index 2a394156f98d..009420077513 100644 --- a/ext/date/lib/parse_iso_intervals.re +++ b/ext/date/lib/parse_iso_intervals.re @@ -343,10 +343,10 @@ void timelib_strtointerval(const char *s, size_t len, in.errors->error_messages = NULL; if (len > 0) { - while (isspace(*s) && s < e) { + while (isspace((unsigned char)*s) && s < e) { s++; } - while (isspace(*e) && e > s) { + while (isspace((unsigned char)*e) && e > s) { e--; } } diff --git a/ext/date/lib/timelib.c b/ext/date/lib/timelib.c index 6473a2798a80..faf383a5fa12 100644 --- a/ext/date/lib/timelib.c +++ b/ext/date/lib/timelib.c @@ -126,7 +126,7 @@ void timelib_time_tz_abbr_update(timelib_time* tm, const char* tz_abbr) TIMELIB_TIME_FREE(tm->tz_abbr); tm->tz_abbr = timelib_strdup(tz_abbr); for (i = 0; i < tz_abbr_len; i++) { - tm->tz_abbr[i] = toupper(tz_abbr[i]); + tm->tz_abbr[i] = toupper((unsigned char)tz_abbr[i]); } } diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 6c5dc29627d4..fc4a18e455c2 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -29,13 +29,7 @@ #include "win32/time.h" #endif -#ifdef PHP_WIN32 -static __inline __int64 php_date_llabs( __int64 i ) { return i >= 0? i: -i; } -#elif defined(__GNUC__) && __GNUC__ < 3 -static __inline __int64_t php_date_llabs( __int64_t i ) { return i >= 0 ? i : -i; } -#else -static inline long long php_date_llabs( long long i ) { return i >= 0 ? i : -i; } -#endif +static inline uint64_t php_date_llabs(int64_t i) { return i >= 0 ? (uint64_t)i : -(uint64_t)i; } #ifdef PHP_WIN32 #define DATE_I64_BUF_LEN 65 @@ -740,9 +734,9 @@ static zend_string *date_format(const char *format, size_t format_len, const tim /* year */ case 'L': length = slprintf(buffer, sizeof(buffer), "%d", timelib_is_leap((int) t->y)); break; case 'y': length = slprintf(buffer, sizeof(buffer), "%02d", (int) (t->y % 100)); break; - case 'Y': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : "", php_date_llabs((timelib_sll) t->y)); break; - case 'x': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : (t->y >= 10000 ? "+" : ""), php_date_llabs((timelib_sll) t->y)); break; - case 'X': length = slprintf(buffer, sizeof(buffer), "%s%04lld", t->y < 0 ? "-" : "+", php_date_llabs((timelib_sll) t->y)); break; + case 'Y': length = slprintf(buffer, sizeof(buffer), "%s%04" PRIu64, t->y < 0 ? "-" : "", php_date_llabs((timelib_sll) t->y)); break; + case 'x': length = slprintf(buffer, sizeof(buffer), "%s%04" PRIu64, t->y < 0 ? "-" : (t->y >= 10000 ? "+" : ""), php_date_llabs((timelib_sll) t->y)); break; + case 'X': length = slprintf(buffer, sizeof(buffer), "%s%04" PRIu64, t->y < 0 ? "-" : "+", php_date_llabs((timelib_sll) t->y)); break; /* time */ case 'a': length = slprintf(buffer, sizeof(buffer), "%s", t->h >= 12 ? "pm" : "am"); break; @@ -1754,7 +1748,7 @@ static void date_register_classes(void) /* {{{ */ date_ce_date->create_object = date_object_new_date; date_ce_date->default_object_handlers = &date_object_handlers_date; memcpy(&date_object_handlers_date, &std_object_handlers, sizeof(zend_object_handlers)); - date_object_handlers_date.offset = XtOffsetOf(php_date_obj, std); + date_object_handlers_date.offset = offsetof(php_date_obj, std); date_object_handlers_date.free_obj = date_object_free_storage_date; date_object_handlers_date.clone_obj = date_object_clone_date; date_object_handlers_date.compare = date_object_compare_date; @@ -1774,7 +1768,7 @@ static void date_register_classes(void) /* {{{ */ date_ce_timezone->create_object = date_object_new_timezone; date_ce_timezone->default_object_handlers = &date_object_handlers_timezone; memcpy(&date_object_handlers_timezone, &std_object_handlers, sizeof(zend_object_handlers)); - date_object_handlers_timezone.offset = XtOffsetOf(php_timezone_obj, std); + date_object_handlers_timezone.offset = offsetof(php_timezone_obj, std); date_object_handlers_timezone.free_obj = date_object_free_storage_timezone; date_object_handlers_timezone.clone_obj = date_object_clone_timezone; date_object_handlers_timezone.get_properties_for = date_object_get_properties_for_timezone; @@ -1786,7 +1780,7 @@ static void date_register_classes(void) /* {{{ */ date_ce_interval->create_object = date_object_new_interval; date_ce_interval->default_object_handlers = &date_object_handlers_interval; memcpy(&date_object_handlers_interval, &std_object_handlers, sizeof(zend_object_handlers)); - date_object_handlers_interval.offset = XtOffsetOf(php_interval_obj, std); + date_object_handlers_interval.offset = offsetof(php_interval_obj, std); date_object_handlers_interval.free_obj = date_object_free_storage_interval; date_object_handlers_interval.clone_obj = date_object_clone_interval; date_object_handlers_interval.has_property = date_interval_has_property; @@ -1802,7 +1796,7 @@ static void date_register_classes(void) /* {{{ */ date_ce_period->default_object_handlers = &date_object_handlers_period; date_ce_period->get_iterator = date_object_period_get_iterator; memcpy(&date_object_handlers_period, &std_object_handlers, sizeof(zend_object_handlers)); - date_object_handlers_period.offset = XtOffsetOf(php_period_obj, std); + date_object_handlers_period.offset = offsetof(php_period_obj, std); date_object_handlers_period.free_obj = date_object_free_storage_period; date_object_handlers_period.clone_obj = date_object_clone_period; date_object_handlers_period.get_gc = date_object_get_gc_period; @@ -1837,7 +1831,7 @@ static zend_object *date_object_new_date(zend_class_entry *class_type) /* {{{ */ static zend_object *date_object_clone_date(zend_object *this_ptr) /* {{{ */ { - php_date_obj *old_obj = php_date_obj_from_obj(this_ptr); + const php_date_obj *old_obj = php_date_obj_from_obj(this_ptr); php_date_obj *new_obj = php_date_obj_from_obj(date_object_new_date(old_obj->std.ce)); zend_objects_clone_members(&new_obj->std, &old_obj->std); @@ -1994,7 +1988,7 @@ static zend_object *date_object_new_timezone(zend_class_entry *class_type) /* {{ static zend_object *date_object_clone_timezone(zend_object *this_ptr) /* {{{ */ { - php_timezone_obj *old_obj = php_timezone_obj_from_obj(this_ptr); + const php_timezone_obj *old_obj = php_timezone_obj_from_obj(this_ptr); php_timezone_obj *new_obj = php_timezone_obj_from_obj(date_object_new_timezone(old_obj->std.ce)); zend_objects_clone_members(&new_obj->std, &old_obj->std); @@ -2137,7 +2131,7 @@ static zend_object *date_object_new_interval(zend_class_entry *class_type) /* {{ static zend_object *date_object_clone_interval(zend_object *this_ptr) /* {{{ */ { - php_interval_obj *old_obj = php_interval_obj_from_obj(this_ptr); + const php_interval_obj *old_obj = php_interval_obj_from_obj(this_ptr); php_interval_obj *new_obj = php_interval_obj_from_obj(date_object_new_interval(old_obj->std.ce)); zend_objects_clone_members(&new_obj->std, &old_obj->std); @@ -2228,7 +2222,7 @@ static zend_object *date_object_new_period(zend_class_entry *class_type) /* {{{ static zend_object *date_object_clone_period(zend_object *this_ptr) /* {{{ */ { - php_period_obj *old_obj = php_period_obj_from_obj(this_ptr); + const php_period_obj *old_obj = php_period_obj_from_obj(this_ptr); php_period_obj *new_obj = php_period_obj_from_obj(date_object_new_period(old_obj->std.ce)); zend_objects_clone_members(&new_obj->std, &old_obj->std); diff --git a/ext/date/php_date.h b/ext/date/php_date.h index 4553d3e93c72..f26617c15ea9 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -58,9 +58,7 @@ struct _php_date_obj { zend_object std; }; -static inline php_date_obj *php_date_obj_from_obj(zend_object *obj) { - return (php_date_obj*)((char*)(obj) - XtOffsetOf(php_date_obj, std)); -} +#define php_date_obj_from_obj(obj) ZEND_CONTAINER_OF(obj, php_date_obj, std) #define Z_PHPDATE_P(zv) php_date_obj_from_obj(Z_OBJ_P((zv))) @@ -75,9 +73,7 @@ struct _php_timezone_obj { zend_object std; }; -static inline php_timezone_obj *php_timezone_obj_from_obj(zend_object *obj) { - return (php_timezone_obj*)((char*)(obj) - XtOffsetOf(php_timezone_obj, std)); -} +#define php_timezone_obj_from_obj(obj) ZEND_CONTAINER_OF(obj, php_timezone_obj, std) #define Z_PHPTIMEZONE_P(zv) php_timezone_obj_from_obj(Z_OBJ_P((zv))) @@ -93,9 +89,7 @@ struct _php_interval_obj { zend_object std; }; -static inline php_interval_obj *php_interval_obj_from_obj(zend_object *obj) { - return (php_interval_obj*)((char*)(obj) - XtOffsetOf(php_interval_obj, std)); -} +#define php_interval_obj_from_obj(obj) ZEND_CONTAINER_OF(obj, php_interval_obj, std) #define Z_PHPINTERVAL_P(zv) php_interval_obj_from_obj(Z_OBJ_P((zv))) @@ -112,9 +106,7 @@ struct _php_period_obj { zend_object std; }; -static inline php_period_obj *php_period_obj_from_obj(zend_object *obj) { - return (php_period_obj*)((char*)(obj) - XtOffsetOf(php_period_obj, std)); -} +#define php_period_obj_from_obj(obj) ZEND_CONTAINER_OF(obj, php_period_obj, std) #define Z_PHPPERIOD_P(zv) php_period_obj_from_obj(Z_OBJ_P((zv))) diff --git a/ext/date/tests/gh18422.phpt b/ext/date/tests/gh18422.phpt new file mode 100644 index 000000000000..643476615277 --- /dev/null +++ b/ext/date/tests/gh18422.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-18422 (int overflow in Date extension) +--FILE-- +format("Y"), "\n"; +echo $dto->format("x"), "\n"; +echo $dto->format("X"), "\n"; + +echo date_create("2024-06-15")->format("Y"), "\n"; +echo date_create("-0042-01-01")->format("Y"), "\n"; +?> +--EXPECTF-- +-%d +-%d +-%d +2024 +-0042 diff --git a/ext/dba/dba.c b/ext/dba/dba.c index a70951467be5..c0688714fe7c 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -335,10 +335,7 @@ static zend_result dba_connection_cast_object(zend_object *obj, zval *result, in return zend_std_cast_object_tostring(obj, result, type); } -static inline dba_connection *dba_connection_from_obj(zend_object *obj) -{ - return (dba_connection *)((char *)(obj) - XtOffsetOf(dba_connection, std)); -} +#define dba_connection_from_obj(obj) ZEND_CONTAINER_OF(obj, dba_connection, std) #define Z_DBA_CONNECTION_P(zv) dba_connection_from_obj(Z_OBJ_P(zv)) #define Z_DBA_INFO_P(zv) Z_DBA_CONNECTION_P(zv)->info @@ -409,7 +406,7 @@ PHP_MINIT_FUNCTION(dba) dba_connection_ce->default_object_handlers = &dba_connection_object_handlers; memcpy(&dba_connection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - dba_connection_object_handlers.offset = XtOffsetOf(dba_connection, std); + dba_connection_object_handlers.offset = offsetof(dba_connection, std); dba_connection_object_handlers.free_obj = dba_connection_free_obj; dba_connection_object_handlers.get_constructor = dba_connection_get_constructor; dba_connection_object_handlers.clone_obj = NULL; diff --git a/ext/dba/libinifile/inifile.c b/ext/dba/libinifile/inifile.c index f5355b5a990c..fa2b05a715e2 100644 --- a/ext/dba/libinifile/inifile.c +++ b/ext/dba/libinifile/inifile.c @@ -109,7 +109,7 @@ void inifile_free(inifile *dba, int persistent) key_type inifile_key_split(const char *group_name) { key_type key; - char *name; + const char *name; if (group_name[0] == '[' && (name = strchr(group_name, ']')) != NULL) { key.group = estrndup(group_name+1, name - (group_name + 1)); diff --git a/ext/dom/CREDITS b/ext/dom/CREDITS index 106446a4730b..0c567fca9828 100644 --- a/ext/dom/CREDITS +++ b/ext/dom/CREDITS @@ -1,2 +1,2 @@ DOM -Christian Stocker, Rob Richards, Marcus Boerger, Niels Dossche +Christian Stocker, Rob Richards, Marcus Boerger, Nora Dossche diff --git a/ext/dom/config.w32 b/ext/dom/config.w32 index e045e29c6bab..e1cbccecf608 100644 --- a/ext/dom/config.w32 +++ b/ext/dom/config.w32 @@ -36,8 +36,7 @@ if (PHP_DOM == "yes") { "dom_ce.h " + "namespace_compat.h " + "xml_common.h " + - "xpath_callbacks.h " + - "lexbor/selectors-adapted/selectors.h " + "xpath_callbacks.h " ); } else { WARNING("dom support can't be enabled, libxml is not enabled") diff --git a/ext/dom/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch b/ext/dom/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch deleted file mode 100644 index 32d9d42d2bf1..000000000000 --- a/ext/dom/lexbor/patches/0001-Expose-line-and-column-information-for-use-in-PHP.patch +++ /dev/null @@ -1,188 +0,0 @@ -From 0cd2add6c46400b808329442f81451b369863983 Mon Sep 17 00:00:00 2001 -From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> -Date: Sat, 26 Aug 2023 15:08:59 +0200 -Subject: [PATCH 1/6] Expose line and column information for use in PHP - ---- - source/lexbor/dom/interfaces/node.h | 2 ++ - source/lexbor/html/token.h | 2 ++ - source/lexbor/html/tokenizer.c | 24 +++++++++++++++++++++++- - source/lexbor/html/tokenizer.h | 2 ++ - source/lexbor/html/tokenizer/state.h | 2 ++ - source/lexbor/html/tree.c | 11 +++++++++++ - source/lexbor/html/tree/error.c | 5 +++-- - source/lexbor/html/tree/error.h | 5 +++-- - 8 files changed, 48 insertions(+), 5 deletions(-) - -diff --git a/source/lexbor/dom/interfaces/node.h b/source/lexbor/dom/interfaces/node.h -index 6c74ac5..b95373c 100644 ---- a/source/lexbor/dom/interfaces/node.h -+++ b/source/lexbor/dom/interfaces/node.h -@@ -86,6 +86,8 @@ struct lxb_dom_node { - - lxb_dom_node_type_t type; - -+ size_t line; -+ - #ifdef LXB_DOM_NODE_USER_VARIABLES - LXB_DOM_NODE_USER_VARIABLES - #endif /* LXB_DOM_NODE_USER_VARIABLES */ -diff --git a/source/lexbor/html/token.h b/source/lexbor/html/token.h -index 79accd0..0b7f4fd 100644 ---- a/source/lexbor/html/token.h -+++ b/source/lexbor/html/token.h -@@ -33,6 +33,8 @@ enum lxb_html_token_type { - typedef struct { - const lxb_char_t *begin; - const lxb_char_t *end; -+ size_t line; -+ size_t column; - - const lxb_char_t *text_start; - const lxb_char_t *text_end; -diff --git a/source/lexbor/html/tokenizer.c b/source/lexbor/html/tokenizer.c -index 22b88ed..1d9f378 100644 ---- a/source/lexbor/html/tokenizer.c -+++ b/source/lexbor/html/tokenizer.c -@@ -92,6 +92,7 @@ lxb_html_tokenizer_init(lxb_html_tokenizer_t *tkz) - - tkz->pos = tkz->start; - tkz->end = tkz->start + LXB_HTML_TKZ_TEMP_SIZE; -+ /* current_line & current_column already initialized by calloc (zero-based) */ - - tkz->tree = NULL; - tkz->tags = NULL; -@@ -153,6 +154,8 @@ lxb_html_tokenizer_inherit(lxb_html_tokenizer_t *tkz_to, - tkz_to->start = tkz_from->start; - tkz_to->end = tkz_from->end; - tkz_to->pos = tkz_to->start; -+ tkz_to->current_line = tkz_from->current_line; -+ tkz_to->current_column = tkz_from->current_column; - - return LXB_STATUS_OK; - } -@@ -571,7 +574,26 @@ lxb_html_tokenizer_chunk(lxb_html_tokenizer_t *tkz, const lxb_char_t *data, - tkz->last = end; - - while (data < end) { -- data = tkz->state(tkz, data, end); -+ size_t current_column = tkz->current_column; -+ const lxb_char_t *new_data = tkz->state(tkz, data, end); -+ while (data < new_data) { -+ /* Codepoints < 0x80 are encoded the same as their ASCII counterpart, so '\n' will uniquely identify a newline. */ -+ if (*data == '\n') { -+ tkz->current_line++; -+ current_column = 0; -+ } else { -+ /* Other characters can be mapped back to the unicode codepoint offset because UTF-8 is a prefix code. -+ * Continuation bytes start with 0b10XXXXXX so we can skip those to only get the start of an encoded code point. */ -+ if ((*data & 0b11000000) == 0b10000000) { -+ /* Continuation byte, do nothing */ -+ } else { -+ /* First byte for a codepoint */ -+ current_column++; -+ } -+ } -+ data++; -+ } -+ tkz->current_column = current_column; - } - - return tkz->status; -diff --git a/source/lexbor/html/tokenizer.h b/source/lexbor/html/tokenizer.h -index 12b7c81..aa1ac37 100644 ---- a/source/lexbor/html/tokenizer.h -+++ b/source/lexbor/html/tokenizer.h -@@ -79,6 +79,8 @@ struct lxb_html_tokenizer { - const lxb_char_t *end; - const lxb_char_t *begin; - const lxb_char_t *last; -+ size_t current_line; -+ size_t current_column; - - /* Entities */ - const lexbor_sbst_entry_static_t *entity; -diff --git a/source/lexbor/html/tokenizer/state.h b/source/lexbor/html/tokenizer/state.h -index 5e91444..52eaa9a 100644 ---- a/source/lexbor/html/tokenizer/state.h -+++ b/source/lexbor/html/tokenizer/state.h -@@ -90,6 +90,8 @@ extern "C" { - do { \ - tkz->pos = tkz->start; \ - tkz->token->begin = v_begin; \ -+ tkz->token->line = tkz->current_line; \ -+ tkz->token->column = tkz->current_column; \ - } \ - while (0) - -diff --git a/source/lexbor/html/tree.c b/source/lexbor/html/tree.c -index 062ea56..3f4c18d 100644 ---- a/source/lexbor/html/tree.c -+++ b/source/lexbor/html/tree.c -@@ -431,6 +431,9 @@ lxb_html_tree_create_element_for_token(lxb_html_tree_t *tree, - return NULL; - } - -+ node->line = token->line; -+ /* We only expose line number in PHP DOM */ -+ - lxb_status_t status; - lxb_dom_element_t *element = lxb_dom_interface_element(node); - -@@ -767,6 +770,11 @@ lxb_html_tree_insert_character_for_data(lxb_html_tree_t *tree, - - lxb_dom_interface_text(text)->char_data.data = *str; - -+ if (tree->tkz_ref) { -+ text->line = tree->tkz_ref->token->line; -+ /* We only expose line number in PHP DOM */ -+ } -+ - if (ret_node != NULL) { - *ret_node = text; - } -@@ -806,6 +814,9 @@ lxb_html_tree_insert_comment(lxb_html_tree_t *tree, - return NULL; - } - -+ node->line = token->line; -+ /* We only expose line number in PHP DOM */ -+ - tree->status = lxb_html_token_make_text(token, &comment->char_data.data, - tree->document->dom_document.text); - if (tree->status != LXB_STATUS_OK) { -diff --git a/source/lexbor/html/tree/error.c b/source/lexbor/html/tree/error.c -index ffdc55c..ef36eab 100644 ---- a/source/lexbor/html/tree/error.c -+++ b/source/lexbor/html/tree/error.c -@@ -22,8 +22,9 @@ lxb_html_tree_error_add(lexbor_array_obj_t *parse_errors, - } - - entry->id = id; -- entry->begin = token->begin; -- entry->end = token->end; -+ entry->line = token->line; -+ entry->column = token->column; -+ entry->length = token->end - token->begin; - - return entry; - } -diff --git a/source/lexbor/html/tree/error.h b/source/lexbor/html/tree/error.h -index 7a212af..b186772 100644 ---- a/source/lexbor/html/tree/error.h -+++ b/source/lexbor/html/tree/error.h -@@ -109,8 +109,9 @@ lxb_html_tree_error_id_t; - - typedef struct { - lxb_html_tree_error_id_t id; -- const lxb_char_t *begin; -- const lxb_char_t *end; -+ size_t line; -+ size_t column; -+ size_t length; - } - lxb_html_tree_error_t; - --- -2.51.2 - diff --git a/ext/dom/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch b/ext/dom/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch deleted file mode 100644 index 1902abf96e3a..000000000000 --- a/ext/dom/lexbor/patches/0002-Track-implied-added-nodes-for-options-use-in-PHP.patch +++ /dev/null @@ -1,67 +0,0 @@ -From a4c29ba8d1ea1065ce6bd4a34382d53140cf1924 Mon Sep 17 00:00:00 2001 -From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> -Date: Mon, 14 Aug 2023 20:18:51 +0200 -Subject: [PATCH 2/6] Track implied added nodes for options use in PHP - ---- - source/lexbor/html/tree.h | 3 +++ - source/lexbor/html/tree/insertion_mode/after_head.c | 1 + - source/lexbor/html/tree/insertion_mode/before_head.c | 2 ++ - source/lexbor/html/tree/insertion_mode/before_html.c | 2 ++ - 4 files changed, 8 insertions(+) - -diff --git a/source/lexbor/html/tree.h b/source/lexbor/html/tree.h -index 4912efb..7b2c620 100644 ---- a/source/lexbor/html/tree.h -+++ b/source/lexbor/html/tree.h -@@ -55,6 +55,9 @@ struct lxb_html_tree { - bool foster_parenting; - bool frameset_ok; - bool scripting; -+ bool has_explicit_html_tag; -+ bool has_explicit_head_tag; -+ bool has_explicit_body_tag; - - lxb_html_tree_insertion_mode_f mode; - lxb_html_tree_insertion_mode_f original_mode; -diff --git a/source/lexbor/html/tree/insertion_mode/after_head.c b/source/lexbor/html/tree/insertion_mode/after_head.c -index ad551b5..1448654 100644 ---- a/source/lexbor/html/tree/insertion_mode/after_head.c -+++ b/source/lexbor/html/tree/insertion_mode/after_head.c -@@ -71,6 +71,7 @@ lxb_html_tree_insertion_mode_after_head_open(lxb_html_tree_t *tree, - return lxb_html_tree_process_abort(tree); - } - -+ tree->has_explicit_body_tag = true; - tree->frameset_ok = false; - tree->mode = lxb_html_tree_insertion_mode_in_body; - -diff --git a/source/lexbor/html/tree/insertion_mode/before_head.c b/source/lexbor/html/tree/insertion_mode/before_head.c -index 14621f2..cd2ac2a 100644 ---- a/source/lexbor/html/tree/insertion_mode/before_head.c -+++ b/source/lexbor/html/tree/insertion_mode/before_head.c -@@ -67,6 +67,8 @@ lxb_html_tree_insertion_mode_before_head_open(lxb_html_tree_t *tree, - return lxb_html_tree_process_abort(tree); - } - -+ tree->has_explicit_head_tag = true; -+ - tree->mode = lxb_html_tree_insertion_mode_in_head; - - break; -diff --git a/source/lexbor/html/tree/insertion_mode/before_html.c b/source/lexbor/html/tree/insertion_mode/before_html.c -index 05fe738..1e09cda 100644 ---- a/source/lexbor/html/tree/insertion_mode/before_html.c -+++ b/source/lexbor/html/tree/insertion_mode/before_html.c -@@ -78,6 +78,8 @@ lxb_html_tree_insertion_mode_before_html_open(lxb_html_tree_t *tree, - return lxb_html_tree_process_abort(tree); - } - -+ tree->has_explicit_html_tag = true; -+ - tree->mode = lxb_html_tree_insertion_mode_before_head; - - break; --- -2.51.2 - diff --git a/ext/dom/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch b/ext/dom/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch deleted file mode 100644 index 51f77483bc6e..000000000000 --- a/ext/dom/lexbor/patches/0003-Patch-utilities-and-data-structure-to-be-able-to-gen.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 46fc776449252e74795569759a19d13857a59069 Mon Sep 17 00:00:00 2001 -From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> -Date: Thu, 24 Aug 2023 22:57:48 +0200 -Subject: [PATCH 3/6] Patch utilities and data structure to be able to generate - smaller lookup tables - -Changed the generation script to check if everything fits in 32-bits. -And change the actual field types to 32-bits. This decreases the hash -tables in size. ---- - source/lexbor/core/shs.h | 4 ++-- - utils/lexbor/encoding/single-byte.py | 4 ++-- - utils/lexbor/lexbor/LXB.py | 12 +++++++++--- - 3 files changed, 13 insertions(+), 7 deletions(-) - -diff --git a/source/lexbor/core/shs.h b/source/lexbor/core/shs.h -index 7a63a07..c84dfaa 100644 ---- a/source/lexbor/core/shs.h -+++ b/source/lexbor/core/shs.h -@@ -27,9 +27,9 @@ lexbor_shs_entry_t; - - typedef struct { - uint32_t key; -- void *value; -+ uint32_t value; - -- size_t next; -+ uint32_t next; - } - lexbor_shs_hash_t; - -diff --git a/utils/lexbor/encoding/single-byte.py b/utils/lexbor/encoding/single-byte.py -index d7d1bb2..5420c16 100755 ---- a/utils/lexbor/encoding/single-byte.py -+++ b/utils/lexbor/encoding/single-byte.py -@@ -128,7 +128,7 @@ class SingleByte: - entries = values[idx] - key_id = entries[1].decode('utf-8') - -- hash_key.append(key_id, '(void *) {}'.format(idx + 0x80)) -+ hash_key.append(key_id, idx + 0x80) - - return hash_key.create(rate = 1) - -@@ -161,7 +161,7 @@ def toHex(s): - lst = [] - - for ch in bytes(s, 'utf-8'): -- hv = hex(ch).replace('0x', '\\\\x') -+ hv = hex(ch).replace('0x', '\\x') - lst.append("'{}'".format(hv)) - - return ', '.join(lst) -diff --git a/utils/lexbor/lexbor/LXB.py b/utils/lexbor/lexbor/LXB.py -index 3e75812..2370c66 100755 ---- a/utils/lexbor/lexbor/LXB.py -+++ b/utils/lexbor/lexbor/LXB.py -@@ -94,7 +94,7 @@ class HashKey: - def append(self, key_id, value): - self.buffer.append([self.hash_id(int(key_id, 0)), value]) - -- def create(self, terminate_value = '{0, NULL, 0}', rate = 2, is_const = True, data_before = None): -+ def create(self, terminate_value = '{0, 0, 0}', rate = 2, is_const = True, data_before = None): - test = self.test(int(self.max_table_size / 1.2), int(self.max_table_size * 1.2)) - - rate_dn = rate - 1 -@@ -142,9 +142,12 @@ class HashKey: - entry = table[idx] - - if entry: -+ assert entry[0] < 2**32 -+ assert entry[1] < 2**32 -+ assert entry[2] < 2**32 - result.append("{{{}, {}, {}}},".format(entry[0], entry[1], entry[2])) - else: -- result.append("{0, NULL, 0},") -+ result.append("{0, 0, 0},") - - if int(idx) % rate == rate_dn: - result.append("\n ") -@@ -154,9 +157,12 @@ class HashKey: - if len(table): - entry = table[-1] - if entry: -+ assert entry[0] < 2**32 -+ assert entry[1] < 2**32 -+ assert entry[2] < 2**32 - result.append("{{{}, {}, {}}}\n".format(entry[0], entry[1], entry[2])) - else: -- result.append("{0, NULL, 0}\n") -+ result.append("{0, 0, 0}\n") - - result.append("};") - --- -2.51.2 - diff --git a/ext/dom/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch b/ext/dom/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch deleted file mode 100644 index 6cb6658a164b..000000000000 --- a/ext/dom/lexbor/patches/0004-Remove-unused-upper-case-tag-static-data.patch +++ /dev/null @@ -1,53 +0,0 @@ -From ae9d7254ac129cc3be34de6fd34af27baf3bb396 Mon Sep 17 00:00:00 2001 -From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> -Date: Wed, 29 Nov 2023 21:26:47 +0100 -Subject: [PATCH 4/6] Remove unused upper case tag static data - ---- - source/lexbor/tag/res.h | 2 ++ - source/lexbor/tag/tag.c | 2 ++ - 2 files changed, 4 insertions(+) - -diff --git a/source/lexbor/tag/res.h b/source/lexbor/tag/res.h -index 604757f..5672d4a 100644 ---- a/source/lexbor/tag/res.h -+++ b/source/lexbor/tag/res.h -@@ -226,6 +226,7 @@ static const lxb_tag_data_t lxb_tag_res_data_default[LXB_TAG__LAST_ENTRY] = - {{.u.short_str = "xmp", .length = 3, .next = NULL}, LXB_TAG_XMP, 1, true} - }; - -+#if 0 - static const lxb_tag_data_t lxb_tag_res_data_upper_default[LXB_TAG__LAST_ENTRY] = - { - {{.u.short_str = "#UNDEF", .length = 6, .next = NULL}, LXB_TAG__UNDEF, 1, true}, -@@ -427,6 +428,7 @@ static const lxb_tag_data_t lxb_tag_res_data_upper_default[LXB_TAG__LAST_ENTRY] - {{.u.short_str = "WBR", .length = 3, .next = NULL}, LXB_TAG_WBR, 1, true}, - {{.u.short_str = "XMP", .length = 3, .next = NULL}, LXB_TAG_XMP, 1, true} - }; -+#endif - - static const lexbor_shs_entry_t lxb_tag_res_shs_data_default[263] = - { -diff --git a/source/lexbor/tag/tag.c b/source/lexbor/tag/tag.c -index 780bc47..be5bb30 100644 ---- a/source/lexbor/tag/tag.c -+++ b/source/lexbor/tag/tag.c -@@ -92,6 +92,7 @@ lxb_tag_data_by_name(lexbor_hash_t *hash, const lxb_char_t *name, size_t len) - lexbor_hash_search_lower, name, len); - } - -+#if 0 - const lxb_tag_data_t * - lxb_tag_data_by_name_upper(lexbor_hash_t *hash, - const lxb_char_t *name, size_t len) -@@ -114,6 +115,7 @@ lxb_tag_data_by_name_upper(lexbor_hash_t *hash, - return (const lxb_tag_data_t *) lexbor_hash_search(hash, - lexbor_hash_search_upper, name, len); - } -+#endif - - /* - * No inline functions for ABI. --- -2.51.2 - diff --git a/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch b/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch deleted file mode 100644 index 9ef6e305e498..000000000000 --- a/ext/dom/lexbor/patches/0005-Shrink-size-of-static-binary-search-tree.patch +++ /dev/null @@ -1,116 +0,0 @@ -From 19cf6183813e013dfe0eb2303c15eaf6e01b9faf Mon Sep 17 00:00:00 2001 -From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> -Date: Wed, 29 Nov 2023 21:29:31 +0100 -Subject: [PATCH 5/6] Shrink size of static binary search tree - -This also makes it more efficient on the data cache. ---- - source/lexbor/core/sbst.h | 19 ++++++++++++++----- - source/lexbor/html/tokenizer/state.c | 2 +- - utils/lexbor/html/tokenizer_entities_bst.py | 8 ++++---- - utils/lexbor/lexbor/LXB.py | 2 +- - 4 files changed, 20 insertions(+), 11 deletions(-) - -diff --git a/source/lexbor/core/sbst.h b/source/lexbor/core/sbst.h -index b0fbc54..15a1d40 100644 ---- a/source/lexbor/core/sbst.h -+++ b/source/lexbor/core/sbst.h -@@ -15,16 +15,25 @@ extern "C" { - - #include "lexbor/core/base.h" - -+#ifdef __has_attribute -+# if __has_attribute(nonstring) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 15 -+# define LXB_NONSTRING __attribute__((nonstring)) -+# else -+# define LXB_NONSTRING -+# endif -+#else -+# define LXB_NONSTRING -+#endif - - typedef struct { - lxb_char_t key; - -- void *value; -- size_t value_len; -+ lxb_char_t value[6] LXB_NONSTRING; -+ unsigned char value_len; - -- size_t left; -- size_t right; -- size_t next; -+ unsigned short left; -+ unsigned short right; -+ unsigned short next; - } - lexbor_sbst_entry_static_t; - -diff --git a/source/lexbor/html/tokenizer/state.c b/source/lexbor/html/tokenizer/state.c -index db362c6..6c3cbeb 100644 ---- a/source/lexbor/html/tokenizer/state.c -+++ b/source/lexbor/html/tokenizer/state.c -@@ -1825,7 +1825,7 @@ lxb_html_tokenizer_state_char_ref_named(lxb_html_tokenizer_t *tkz, - goto done; - } - -- if (entry->value != NULL) { -+ if (entry->value[0] != 0) { - tkz->entity_end = (tkz->pos + (data - begin)) - tkz->start; - tkz->entity_match = entry; - } -diff --git a/utils/lexbor/html/tokenizer_entities_bst.py b/utils/lexbor/html/tokenizer_entities_bst.py -index b34bca1..2bfea81 100755 ---- a/utils/lexbor/html/tokenizer_entities_bst.py -+++ b/utils/lexbor/html/tokenizer_entities_bst.py -@@ -1,6 +1,6 @@ - - import json --import sys, re, os -+import sys, os - - # Find and append run script run dir to module search path - ABS_PATH = os.path.dirname(os.path.abspath(__file__)) -@@ -62,7 +62,7 @@ def entities_bst_create_layer(name, entry, index): - - def entities_bst_create(index): - bst = {} -- bst[0] = ["\0", 0, 0, 0, "NULL"] -+ bst[0] = ["\0", 0, 0, 0, "{0}"] - - begin = 1 - idx = end = entities_bst_create_tree(index, bst, begin) -@@ -114,7 +114,7 @@ def entities_bst_create_tree(index, bst, idx): - assert len(index[ split[0] ]['values']) < 2, 'Double values' - - if len(index[ split[0] ]['values']) == 0: -- value = "NULL" -+ value = "{0}" - else: - value = '"{}"'.format(toHex(index[ split[0] ]['values'][0]['characters'])) - -@@ -146,7 +146,7 @@ def toHex(s): - lst = [] - - for ch in bytes(s, 'utf-8'): -- hv = hex(ch).replace('0x', '\\\\x') -+ hv = hex(ch).replace('0x', '\\x') - lst.append(hv) - - return ''.join(lst) -diff --git a/utils/lexbor/lexbor/LXB.py b/utils/lexbor/lexbor/LXB.py -index 2370c66..c41e645 100755 ---- a/utils/lexbor/lexbor/LXB.py -+++ b/utils/lexbor/lexbor/LXB.py -@@ -27,7 +27,7 @@ class Temp: - - for line in fh: - for name in self.patterns: -- line = re.sub(name, '\n'.join(self.patterns[name]), line) -+ line = line.replace(name, '\n'.join(self.patterns[name])) - self.buffer.append(line) - fh.close() - --- -2.51.2 - diff --git a/ext/dom/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch b/ext/dom/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch deleted file mode 100644 index a643f9716488..000000000000 --- a/ext/dom/lexbor/patches/0006-Patch-out-unused-CSS-style-code.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 54399ee441d922d89c32909e2028f899f6091cd6 Mon Sep 17 00:00:00 2001 -From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> -Date: Sun, 7 Jan 2024 21:59:28 +0100 -Subject: [PATCH 6/6] Patch out unused CSS style code - ---- - source/lexbor/css/rule.h | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/source/lexbor/css/rule.h b/source/lexbor/css/rule.h -index 308dced..d192a01 100644 ---- a/source/lexbor/css/rule.h -+++ b/source/lexbor/css/rule.h -@@ -361,6 +361,7 @@ lxb_css_rule_ref_dec(lxb_css_rule_t *rule) - lxb_inline void - lxb_css_rule_ref_dec_destroy(lxb_css_rule_t *rule) - { -+#if 0 - if (rule->ref_count > 0) { - rule->ref_count--; - } -@@ -368,6 +369,7 @@ lxb_css_rule_ref_dec_destroy(lxb_css_rule_t *rule) - if (rule->ref_count == 0) { - (void) lxb_css_rule_destroy(rule, true); - } -+#endif - } - - lxb_inline void --- -2.51.2 - diff --git a/ext/dom/namespace_compat.c b/ext/dom/namespace_compat.c index 569daabc78ea..377abbd01dc7 100644 --- a/ext/dom/namespace_compat.c +++ b/ext/dom/namespace_compat.c @@ -59,7 +59,7 @@ static HashTable *php_dom_libxml_ns_mapper_ensure_prefix_map(php_dom_libxml_ns_m zend_hash_add_new(&mapper->uri_to_prefix_map, *uri, &zv_prefix_map); } else { /* cast to Bucket* only works if this holds, I would prefer a static assert but we're stuck at C99. */ - ZEND_ASSERT(XtOffsetOf(Bucket, val) == 0); + ZEND_ASSERT(offsetof(Bucket, val) == 0); ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY); Bucket *bucket = (Bucket *) zv; /* Make sure we take the value from the key string that lives long enough. */ diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index ec1a2ea1c6cf..eccbe4ed2984 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -792,7 +792,7 @@ HashTable *dom_xpath_get_gc(zend_object *object, zval **table, int *n); PHP_MINIT_FUNCTION(dom) { memcpy(&dom_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - dom_object_handlers.offset = XtOffsetOf(dom_object, std); + dom_object_handlers.offset = offsetof(dom_object, std); dom_object_handlers.free_obj = dom_objects_free_storage; dom_object_handlers.read_property = dom_read_property; dom_object_handlers.write_property = dom_write_property; @@ -837,12 +837,12 @@ PHP_MINIT_FUNCTION(dom) dom_html_collection_object_handlers.get_gc = dom_html_collection_get_gc; memcpy(&dom_object_namespace_node_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); - dom_object_namespace_node_handlers.offset = XtOffsetOf(dom_object_namespace_node, dom.std); + dom_object_namespace_node_handlers.offset = offsetof(dom_object_namespace_node, dom.std); dom_object_namespace_node_handlers.free_obj = dom_object_namespace_node_free_storage; dom_object_namespace_node_handlers.clone_obj = dom_object_namespace_node_clone_obj; memcpy(&dom_token_list_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); - dom_token_list_object_handlers.offset = XtOffsetOf(dom_token_list_object, dom.std); + dom_token_list_object_handlers.offset = offsetof(dom_token_list_object, dom.std); dom_token_list_object_handlers.free_obj = dom_token_list_free_obj; /* The Web IDL (Web Interface Description Language - https://webidl.spec.whatwg.org) has the [SameObject] constraint * for this object, which is incompatible with cloning because it imposes that there is only one instance @@ -1333,7 +1333,7 @@ PHP_MINIT_FUNCTION(dom) #ifdef LIBXML_XPATH_ENABLED memcpy(&dom_xpath_object_handlers, &dom_object_handlers, sizeof(zend_object_handlers)); - dom_xpath_object_handlers.offset = XtOffsetOf(dom_xpath_object, dom) + XtOffsetOf(dom_object, std); + dom_xpath_object_handlers.offset = offsetof(dom_xpath_object, dom) + offsetof(dom_object, std); dom_xpath_object_handlers.free_obj = dom_xpath_objects_free_storage; dom_xpath_object_handlers.get_gc = dom_xpath_get_gc; dom_xpath_object_handlers.clone_obj = NULL; @@ -1570,7 +1570,7 @@ zend_object *dom_xpath_objects_new(zend_class_entry *class_type) /* The char pointer MUST refer to the char* of a zend_string struct */ static void dom_zend_string_release_from_char_pointer(xmlChar *ptr) { - zend_string_release((zend_string*) (ptr - XtOffsetOf(zend_string, val))); + zend_string_release((zend_string*) (ptr - offsetof(zend_string, val))); } void dom_nnodemap_objects_free_storage(zend_object *object) /* {{{ */ diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h index d424b26cc694..2cb306f1f5d1 100644 --- a/ext/dom/php_dom.h +++ b/ext/dom/php_dom.h @@ -68,7 +68,7 @@ typedef struct dom_xpath_object { static inline dom_xpath_object *php_xpath_obj_from_obj(zend_object *obj) { return (dom_xpath_object*)((char*)(obj) - - XtOffsetOf(dom_xpath_object, dom) - XtOffsetOf(dom_object, std)); + - offsetof(dom_xpath_object, dom) - offsetof(dom_object, std)); } #define Z_XPATHOBJ_P(zv) php_xpath_obj_from_obj(Z_OBJ_P((zv))) @@ -94,7 +94,7 @@ struct php_dom_libxml_ns_mapper; typedef struct php_dom_libxml_ns_mapper php_dom_libxml_ns_mapper; static inline dom_object_namespace_node *php_dom_namespace_node_obj_from_obj(zend_object *obj) { - return (dom_object_namespace_node*)((char*)(obj) - XtOffsetOf(dom_object_namespace_node, dom.std)); + return (dom_object_namespace_node*)((char*)(obj) - offsetof(dom_object_namespace_node, dom.std)); } #include "domexception.h" diff --git a/ext/dom/tests/gh22077.phpt b/ext/dom/tests/gh22077.phpt new file mode 100644 index 000000000000..fd4e42cc8aaf --- /dev/null +++ b/ext/dom/tests/gh22077.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-22077 (UAF in custom XPath function) +--FILE-- +registerNamespace("my", "my.ns"); +$xpath->registerPHPFunctionNS('my.ns', 'include', function(): DOMElement { + $includedDocument = new DOMDocument; + $includedDocument->loadXML(''); + return $includedDocument->documentElement; +}); +$nodeset = $xpath->query('my:include()/uaf'); +$node = $nodeset->item(0); +var_dump($nodeset->length); +var_dump($node->ownerDocument->saveXML($node)); +?> +--EXPECT-- +int(2) +string(6) "" diff --git a/ext/dom/token_list.c b/ext/dom/token_list.c index 28dfc695ddc7..de449465b768 100644 --- a/ext/dom/token_list.c +++ b/ext/dom/token_list.c @@ -575,7 +575,7 @@ PHP_METHOD(Dom_TokenList, toggle) HashTable *token_set = TOKEN_LIST_GET_SET(intern); zval *found_token = zend_hash_find(token_set, token); if (found_token != NULL) { - ZEND_ASSERT(XtOffsetOf(Bucket, val) == 0 && "the cast only works if this is true"); + ZEND_ASSERT(offsetof(Bucket, val) == 0 && "the cast only works if this is true"); Bucket *bucket = (Bucket *) found_token; /* 3.1. If force is either not given or is false, then remove token from this’s token set, @@ -625,7 +625,7 @@ PHP_METHOD(Dom_TokenList, replace) } /* 4. Replace token in this’s token set with newToken. */ - ZEND_ASSERT(XtOffsetOf(Bucket, val) == 0 && "the cast only works if this is true"); + ZEND_ASSERT(offsetof(Bucket, val) == 0 && "the cast only works if this is true"); Bucket *bucket = (Bucket *) found_token; if (zend_hash_set_bucket_key(token_set, bucket, new_token) == NULL) { /* It already exists, remove token instead. */ diff --git a/ext/dom/token_list.h b/ext/dom/token_list.h index d5eb071f1882..7bddd6c9b462 100644 --- a/ext/dom/token_list.h +++ b/ext/dom/token_list.h @@ -25,12 +25,12 @@ typedef struct dom_token_list_object { static inline dom_token_list_object *php_dom_token_list_from_obj(zend_object *obj) { - return (dom_token_list_object *)((char *) obj - XtOffsetOf(dom_token_list_object, dom.std)); + return (dom_token_list_object *)((char *) obj - offsetof(dom_token_list_object, dom.std)); } static inline dom_token_list_object *php_dom_token_list_from_dom_obj(dom_object *obj) { - return (dom_token_list_object *)((char *) obj - XtOffsetOf(dom_token_list_object, dom)); + return ZEND_CONTAINER_OF(obj, dom_token_list_object, dom); } void dom_ordered_set_parser(HashTable *token_set, const char *position, bool to_lowercase); diff --git a/ext/dom/xml_common.h b/ext/dom/xml_common.h index 419886bae4f7..ec45fcc08342 100644 --- a/ext/dom/xml_common.h +++ b/ext/dom/xml_common.h @@ -27,9 +27,7 @@ typedef struct _dom_object { zend_object std; } dom_object; -static inline dom_object *php_dom_obj_from_obj(zend_object *obj) { - return (dom_object*)((char*)(obj) - XtOffsetOf(dom_object, std)); -} +#define php_dom_obj_from_obj(obj) ZEND_CONTAINER_OF(obj, dom_object, std) #define Z_DOMOBJ_P(zv) php_dom_obj_from_obj(Z_OBJ_P((zv))) diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index 012fa9789e0d..bd9947b4ad10 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -33,6 +33,24 @@ #ifdef LIBXML_XPATH_ENABLED +static dom_object *dom_xpath_intern_for_doc(dom_xpath_object *xpath_obj, xmlDocPtr doc) +{ + if (xpath_obj->dom.document && xpath_obj->dom.document->ptr == doc) { + return &xpath_obj->dom; + } + HashTable *node_list = xpath_obj->xpath_callbacks.node_list; + if (node_list) { + zval *entry; + ZEND_HASH_PACKED_FOREACH_VAL(node_list, entry) { + dom_object *obj = Z_DOMOBJ_P(entry); + if (obj->document && obj->document->ptr == doc) { + return obj; + } + } ZEND_HASH_FOREACH_END(); + } + return &xpath_obj->dom; +} + void dom_xpath_objects_free_storage(zend_object *object) { dom_xpath_object *intern = php_xpath_obj_from_obj(object); @@ -189,7 +207,7 @@ zend_result dom_xpath_document_read(dom_object *obj, zval *retval) /* {{{ registerNodeNamespaces bool*/ static inline dom_xpath_object *php_xpath_obj_from_dom_obj(dom_object *obj) { - return (dom_xpath_object*)((char*)(obj) - XtOffsetOf(dom_xpath_object, dom)); + return ZEND_CONTAINER_OF(obj, dom_xpath_object, dom); } zend_result dom_xpath_register_node_ns_read(dom_object *obj, zval *retval) @@ -352,7 +370,8 @@ static void php_xpath_eval(INTERNAL_FUNCTION_PARAMETERS, int type, bool modern) node = php_dom_create_fake_namespace_decl(nsparent, original, &child, parent_intern); } else { - php_dom_create_object(node, &child, &intern->dom); + dom_object *parent = dom_xpath_intern_for_doc(intern, node->doc); + php_dom_create_object(node, &child, parent); } add_next_index_zval(&retval, &child); } diff --git a/ext/dom/xpath_callbacks.c b/ext/dom/xpath_callbacks.c index b1a2f808ca66..90395dc15f2e 100644 --- a/ext/dom/xpath_callbacks.c +++ b/ext/dom/xpath_callbacks.c @@ -406,6 +406,7 @@ static zend_result php_dom_xpath_callback_dispatch(php_dom_xpath_callbacks *xpat fci.param_count = param_count; fci.params = params; fci.named_params = NULL; + fci.consumed_args = 0; ZVAL_STRINGL(&fci.function_name, function_name, function_name_length); zend_call_function(&fci, NULL); diff --git a/ext/enchant/enchant.c b/ext/enchant/enchant.c index 3c09be9077b0..a109c24062f0 100644 --- a/ext/enchant/enchant.c +++ b/ext/enchant/enchant.c @@ -48,9 +48,7 @@ typedef struct _dict_struct { zend_class_entry *enchant_broker_ce; static zend_object_handlers enchant_broker_handlers; -static inline enchant_broker *enchant_broker_from_obj(zend_object *obj) { - return (enchant_broker *)((char *)(obj) - XtOffsetOf(enchant_broker, std)); -} +#define enchant_broker_from_obj(obj) ZEND_CONTAINER_OF(obj, enchant_broker, std) #define Z_ENCHANT_BROKER_P(zv) enchant_broker_from_obj(Z_OBJ_P(zv)) @@ -66,9 +64,7 @@ static zend_object *enchant_broker_create_object(zend_class_entry *class_type) { zend_class_entry *enchant_dict_ce; static zend_object_handlers enchant_dict_handlers; -static inline enchant_dict *enchant_dict_from_obj(zend_object *obj) { - return (enchant_dict *)((char *)(obj) - XtOffsetOf(enchant_dict, std)); -} +#define enchant_dict_from_obj(obj) ZEND_CONTAINER_OF(obj, enchant_dict, std) #define Z_ENCHANT_DICT_P(zv) enchant_dict_from_obj(Z_OBJ_P(zv)) @@ -190,7 +186,7 @@ PHP_MINIT_FUNCTION(enchant) enchant_broker_ce->default_object_handlers = &enchant_broker_handlers; memcpy(&enchant_broker_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - enchant_broker_handlers.offset = XtOffsetOf(enchant_broker, std); + enchant_broker_handlers.offset = offsetof(enchant_broker, std); enchant_broker_handlers.free_obj = php_enchant_broker_free; enchant_broker_handlers.clone_obj = NULL; enchant_broker_handlers.compare = zend_objects_not_comparable; @@ -200,7 +196,7 @@ PHP_MINIT_FUNCTION(enchant) enchant_dict_ce->default_object_handlers = &enchant_dict_handlers; memcpy(&enchant_dict_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - enchant_dict_handlers.offset = XtOffsetOf(enchant_dict, std); + enchant_dict_handlers.offset = offsetof(enchant_dict, std); enchant_dict_handlers.free_obj = php_enchant_dict_free; enchant_dict_handlers.clone_obj = NULL; enchant_dict_handlers.compare = zend_objects_not_comparable; diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 91be074a0be3..1b8bd1f76784 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -69,7 +69,7 @@ PHP_MINFO_FUNCTION(exif) php_info_print_table_start(); php_info_print_table_row(2, "EXIF Support", "enabled"); php_info_print_table_row(2, "Supported EXIF Version", "0220"); - php_info_print_table_row(2, "Supported filetypes", "JPEG, TIFF"); + php_info_print_table_row(2, "Supported filetypes", "JPEG, TIFF, HEIF, WebP"); if (USE_MBSTRING) { php_info_print_table_row(2, "Multibyte decoding support using mbstring", "enabled"); @@ -4445,6 +4445,54 @@ static bool exif_scan_HEIF_header(image_info_type *ImageInfo, unsigned char *buf return ret; } +static bool exif_scan_WEBP_header(image_info_type *ImageInfo, size_t riff_size) +{ + /* "Exif\0\0" identifier code */ + static const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; + unsigned char chunk_header[8]; + size_t offset = 12; + size_t riff_end = riff_size <= ImageInfo->FileSize - 8 ? riff_size + 8 : ImageInfo->FileSize; + + while (offset + 8 <= riff_end) { + if ((php_stream_seek(ImageInfo->infile, offset, SEEK_SET) < 0) || + (exif_read_from_stream_file_looped(ImageInfo->infile, (char*)chunk_header, 8) != 8)) { + return false; + } + + size_t chunk_size = php_ifd_get32u(chunk_header + 4, 0); + size_t payload_offset = offset + 8; + + if (chunk_size > riff_end - payload_offset) { + return false; + } + + if (!memcmp(chunk_header, "EXIF", 4)) { + size_t skip = 0; + bool ret = false; + + if (chunk_size < 8) { + return false; + } + + char *data = emalloc(chunk_size); + if (exif_read_from_stream_file_looped(ImageInfo->infile, data, chunk_size) == chunk_size) { + if (chunk_size >= sizeof(ExifHeader) + 8 && !memcmp(data, ExifHeader, sizeof(ExifHeader))) { + skip = sizeof(ExifHeader); + } + exif_process_TIFF_in_JPEG(ImageInfo, data + skip, chunk_size - skip, payload_offset + skip); + ret = true; + } + efree(data); + return ret; + } + + /* RIFF chunks are word-aligned: an odd payload is followed by a pad byte. */ + offset = payload_offset + chunk_size + (chunk_size & 1); + } + + return false; +} + /* {{{ exif_scan_FILE_header * Parse the marker stream until SOS or EOI is seen; */ static bool exif_scan_FILE_header(image_info_type *ImageInfo) @@ -4521,6 +4569,17 @@ static bool exif_scan_FILE_header(image_info_type *ImageInfo) exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid HEIF file"); return false; } + } else if ((ImageInfo->FileSize >= 16) && + (!memcmp(file_header, "RIFF", 4)) && + (exif_read_from_stream_file_looped(ImageInfo->infile, (char*)(file_header + 8), 4) == 4) && + (!memcmp(file_header + 8, "WEBP", 4))) { + if (exif_scan_WEBP_header(ImageInfo, php_ifd_get32u(file_header + 4, 0))) { + ImageInfo->FileType = IMAGE_FILETYPE_WEBP; + return true; + } else { + exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid WebP file"); + return false; + } } else { exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "File not supported"); return false; diff --git a/ext/exif/tests/gh19904.phpt b/ext/exif/tests/gh19904.phpt new file mode 100644 index 000000000000..cc13d88dd98b --- /dev/null +++ b/ext/exif/tests/gh19904.phpt @@ -0,0 +1,82 @@ +--TEST-- +GH-19904 (exif_read_data() reads EXIF metadata from WebP images) +--EXTENSIONS-- +exif +--INI-- +output_handler= +zlib.output_compression=0 +--FILE-- + +--EXPECTF-- +array(26) { + ["FileName"]=> + string(12) "gh19904.webp" + ["FileDateTime"]=> + int(%d) + ["FileSize"]=> + int(526) + ["FileType"]=> + int(18) + ["MimeType"]=> + string(10) "image/webp" + ["SectionsFound"]=> + string(24) "ANY_TAG, IFD0, EXIF, GPS" + ["COMPUTED"]=> + array(4) { + ["IsColor"]=> + int(0) + ["ByteOrderMotorola"]=> + int(0) + ["UserComment"]=> + string(17) "Created with GIMP" + ["UserCommentEncoding"]=> + string(9) "UNDEFINED" + } + ["ImageWidth"]=> + int(100) + ["ImageLength"]=> + int(100) + ["BitsPerSample"]=> + array(3) { + [0]=> + int(8) + [1]=> + int(8) + [2]=> + int(8) + } + ["ImageDescription"]=> + string(17) "Created with GIMP" + ["XResolution"]=> + string(5) "300/1" + ["YResolution"]=> + string(5) "300/1" + ["ResolutionUnit"]=> + int(2) + ["Software"]=> + string(10) "GIMP 3.0.4" + ["DateTime"]=> + string(19) "2025:09:21 15:30:30" + ["Exif_IFD_Pointer"]=> + int(250) + ["GPS_IFD_Pointer"]=> + int(430) + ["DateTimeOriginal"]=> + string(19) "2025:09:21 15:29:27" + ["DateTimeDigitized"]=> + string(19) "2025:09:21 15:29:27" + ["OffsetTime"]=> + string(6) "+02:00" + ["OffsetTimeOriginal"]=> + string(6) "+02:00" + ["OffsetTimeDigitized"]=> + string(6) "+02:00" + ["UserComment"]=> + string(25) "%sCreated with GIMP" + ["ColorSpace"]=> + int(1) + ["GPSAltitude"]=> + string(5) "0/100" +} diff --git a/ext/exif/tests/gh19904.webp b/ext/exif/tests/gh19904.webp new file mode 100644 index 000000000000..12dbe96a845c Binary files /dev/null and b/ext/exif/tests/gh19904.webp differ diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index cc0c4c297447..779b41fcad3d 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -948,6 +948,7 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v fci.object = NULL; fci.param_count = callback_data->arg_count; fci.named_params = NULL; + fci.consumed_args = 0; if (callback_data->type->func.args) { int n = 0; diff --git a/ext/fileinfo/fileinfo.c b/ext/fileinfo/fileinfo.c index 6aa4520ffcc8..4c5c0e88c5ce 100644 --- a/ext/fileinfo/fileinfo.c +++ b/ext/fileinfo/fileinfo.c @@ -43,9 +43,7 @@ typedef struct _finfo_object { zend_object zo; } finfo_object; -static inline finfo_object *php_finfo_fetch_object(zend_object *obj) { - return (finfo_object *)((char*)(obj) - XtOffsetOf(finfo_object, zo)); -} +#define php_finfo_fetch_object(obj) ZEND_CONTAINER_OF(obj, finfo_object, zo) #define Z_FINFO_P(zv) php_finfo_fetch_object(Z_OBJ_P((zv))) @@ -82,7 +80,7 @@ PHP_MINIT_FUNCTION(finfo) /* copy the standard object handlers to you handler table */ memcpy(&finfo_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - finfo_object_handlers.offset = XtOffsetOf(finfo_object, zo); + finfo_object_handlers.offset = offsetof(finfo_object, zo); finfo_object_handlers.free_obj = finfo_objects_free; finfo_object_handlers.clone_obj = NULL; diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index c6653f29e3d7..45acf6d47541 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -533,21 +533,21 @@ static bool php_filter_validate_domain_ex(const zend_string *domain, zend_long f } /* First char must be alphanumeric */ - if(*s == '.' || (hostname && !isalnum((int)*(unsigned char *)s))) { + if(*s == '.' || (hostname && !isalnum((unsigned char)*s))) { return false; } while (s < e) { if (*s == '.') { /* The first and the last character of a label must be alphanumeric */ - if (*(s + 1) == '.' || (hostname && (!isalnum((int)*(unsigned char *)(s - 1)) || !isalnum((int)*(unsigned char *)(s + 1))))) { + if (*(s + 1) == '.' || (hostname && (!isalnum((unsigned char)s[-1]) || !isalnum((unsigned char)s[1])))) { return false; } /* Reset label length counter */ i = 1; } else { - if (i > 63 || (hostname && (*s != '-' || *(s + 1) == '\0') && !isalnum((int)*(unsigned char *)s))) { + if (i > 63 || (hostname && (*s != '-' || *(s + 1) == '\0') && !isalnum((unsigned char)*s))) { return false; } @@ -575,9 +575,9 @@ static bool is_userinfo_valid(const zend_string *str) const char *p = ZSTR_VAL(str); while (p - ZSTR_VAL(str) < ZSTR_LEN(str)) { static const char *valid = "-._~!$&'()*+,;=:"; - if (isalpha(*p) || isdigit(*p) || strchr(valid, *p)) { + if (isalpha((unsigned char)*p) || isdigit((unsigned char)*p) || strchr(valid, *p)) { p++; - } else if (*p == '%' && p - ZSTR_VAL(str) <= ZSTR_LEN(str) - 3 && isdigit(*(p+1)) && isxdigit(*(p+2))) { + } else if (*p == '%' && p - ZSTR_VAL(str) <= ZSTR_LEN(str) - 3 && isdigit((unsigned char)p[1]) && isxdigit((unsigned char)p[2])) { p += 3; } else { return false; diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index 7172ac3e960f..66a05a95f4ed 100644 --- a/ext/ftp/ftp.c +++ b/ext/ftp/ftp.c @@ -483,7 +483,7 @@ void ftp_raw(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, zval *return_ array_init(return_value); while (ftp_readline(ftp)) { add_next_index_string(return_value, ftp->inbuf); - if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') { + if (isdigit((unsigned char)ftp->inbuf[0]) && isdigit((unsigned char)ftp->inbuf[1]) && isdigit((unsigned char)ftp->inbuf[2]) && ftp->inbuf[3] == ' ') { return; } } @@ -789,7 +789,7 @@ bool ftp_pasv(ftpbuf_t *ftp, int pasv) return false; } /* parse out the IP and port */ - for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++); + for (ptr = ftp->inbuf; *ptr && !isdigit((unsigned char)*ptr); ptr++); n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]); if (n != 6) { return false; @@ -1100,7 +1100,7 @@ time_t ftp_mdtm(ftpbuf_t *ftp, const char *path, const size_t path_len) return -1; } /* parse out the timestamp */ - for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++); + for (ptr = ftp->inbuf; *ptr && !isdigit((unsigned char)*ptr); ptr++); n = sscanf(ptr, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); if (n != 6) { return -1; @@ -1276,13 +1276,13 @@ static bool ftp_getresp(ftpbuf_t *ftp) } /* Break out when the end-tag is found */ - if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') { + if (isdigit((unsigned char)ftp->inbuf[0]) && isdigit((unsigned char)ftp->inbuf[1]) && isdigit((unsigned char)ftp->inbuf[2]) && ftp->inbuf[3] == ' ') { break; } } /* translate the tag */ - if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) { + if (!isdigit((unsigned char)ftp->inbuf[0]) || !isdigit((unsigned char)ftp->inbuf[1]) || !isdigit((unsigned char)ftp->inbuf[2])) { return false; } diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c index 8285f36a5628..501ce9bc68d7 100644 --- a/ext/ftp/php_ftp.c +++ b/ext/ftp/php_ftp.c @@ -99,7 +99,7 @@ PHP_MINIT_FUNCTION(ftp) php_ftp_ce->create_object = ftp_object_create; memcpy(&ftp_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - ftp_object_handlers.offset = XtOffsetOf(php_ftp_object, std); + ftp_object_handlers.offset = offsetof(php_ftp_object, std); ftp_object_handlers.get_constructor = ftp_object_get_constructor; ftp_object_handlers.free_obj = ftp_object_destroy; ftp_object_handlers.clone_obj = NULL; diff --git a/ext/gd/gd.c b/ext/gd/gd.c index c0cf361be055..19f83e3140be 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -144,14 +144,7 @@ static zend_function *php_gd_image_object_get_constructor(zend_object *object) return NULL; } -/** - * Returns the underlying php_gd_image_object from a zend_object - */ - -static zend_always_inline php_gd_image_object* php_gd_exgdimage_from_zobj_p(zend_object* obj) -{ - return (php_gd_image_object *) ((char *) (obj) - XtOffsetOf(php_gd_image_object, std)); -} +#define php_gd_exgdimage_from_zobj_p(obj) ZEND_CONTAINER_OF(obj, php_gd_image_object, std) /** * Converts an extension GdImage instance contained within a zval into the gdImagePtr @@ -206,7 +199,7 @@ static void php_gd_object_minit_helper(void) php_gd_image_object_handlers.free_obj = php_gd_image_object_free; php_gd_image_object_handlers.get_constructor = php_gd_image_object_get_constructor; php_gd_image_object_handlers.compare = zend_objects_not_comparable; - php_gd_image_object_handlers.offset = XtOffsetOf(php_gd_image_object, std); + php_gd_image_object_handlers.offset = offsetof(php_gd_image_object, std); } static zend_class_entry *gd_font_ce = NULL; @@ -271,7 +264,7 @@ static void php_gd_font_minit_helper(void) php_gd_font_object_handlers.clone_obj = NULL; php_gd_font_object_handlers.free_obj = php_gd_font_object_free; php_gd_font_object_handlers.get_constructor = php_gd_font_object_get_constructor; - php_gd_font_object_handlers.offset = XtOffsetOf(php_gd_font_object, std); + php_gd_font_object_handlers.offset = offsetof(php_gd_font_object, std); } /********************************************************* diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c index 71a0e9ea63f2..47c4dbc0d770 100644 --- a/ext/gd/libgd/gd.c +++ b/ext/gd/libgd/gd.c @@ -1783,6 +1783,9 @@ void gdImageEllipse(gdImagePtr im, int mx, int my, int w, int h, int c) a=w>>1; b=h>>1; + if (overflowMul3(a, b, b) || overflowMul3(b, a, a)) { + return; + } gdImageSetPixel(im,mx+a, my, c); gdImageSetPixel(im,mx-a, my, c); mx1 = mx-a;my1 = my; @@ -1824,7 +1827,9 @@ void gdImageFilledEllipse (gdImagePtr im, int mx, int my, int w, int h, int c) a=w>>1; b=h>>1; - + if (overflowMul3(a, b, b) || overflowMul3(b, a, a)) { + return; + } for (x = mx-a; x <= mx+a; x++) { gdImageSetPixel(im, x, my, c); } @@ -2879,12 +2884,12 @@ void gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c) void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels) { - if (im->style) { - gdFree(im->style); - } if (overflow2(sizeof (int), noOfPixels)) { return; } + if (im->style) { + gdFree(im->style); + } im->style = (int *) gdMalloc(sizeof(int) * noOfPixels); memcpy(im->style, style, sizeof(int) * noOfPixels); im->styleLength = noOfPixels; diff --git a/ext/gd/libgd/gd_filter.c b/ext/gd/libgd/gd_filter.c index d567898548d2..97dea5c552cd 100644 --- a/ext/gd/libgd/gd_filter.c +++ b/ext/gd/libgd/gd_filter.c @@ -7,6 +7,7 @@ #else # include #endif +#include #include #include @@ -20,6 +21,20 @@ /* Begin filters function */ #define GET_PIXEL_FUNCTION(src)(src->trueColor?gdImageGetTrueColorPixel:gdImageGetPixel) +static int gdClampFloatToByte(float value) +{ + if (!isfinite(value)) { + return value > 0.0f ? 255 : 0; + } + if (value > 255.0f) { + return 255; + } + if (value < 0.0f) { + return 0; + } + return (int)value; +} + #ifdef _WIN32 # define GD_SCATTER_SEED() (unsigned int)(time(0) * GetCurrentProcessId()) #else @@ -417,6 +432,7 @@ int gdImageConvolution(gdImagePtr src, float filter[3][3], float filter_div, flo for ( y=0; ysy; y++) { for(x=0; xsx; x++) { + int new_ri, new_gi, new_bi; new_r = new_g = new_b = 0; pxl = f(srcback, x, y); new_a = gdImageAlpha(srcback, pxl); @@ -435,13 +451,13 @@ int gdImageConvolution(gdImagePtr src, float filter[3][3], float filter_div, flo new_g = (new_g/filter_div)+offset; new_b = (new_b/filter_div)+offset; - new_r = (new_r > 255.0f)? 255.0f : ((new_r < 0.0f)? 0.0f:new_r); - new_g = (new_g > 255.0f)? 255.0f : ((new_g < 0.0f)? 0.0f:new_g); - new_b = (new_b > 255.0f)? 255.0f : ((new_b < 0.0f)? 0.0f:new_b); + new_ri = gdClampFloatToByte(new_r); + new_gi = gdClampFloatToByte(new_g); + new_bi = gdClampFloatToByte(new_b); - new_pxl = gdImageColorAllocateAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a); + new_pxl = gdImageColorAllocateAlpha(src, new_ri, new_gi, new_bi, new_a); if (new_pxl == -1) { - new_pxl = gdImageColorClosestAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a); + new_pxl = gdImageColorClosestAlpha(src, new_ri, new_gi, new_bi, new_a); } gdImageSetPixel (src, x, y, new_pxl); } diff --git a/ext/gd/libgd/gd_interpolation.c b/ext/gd/libgd/gd_interpolation.c index f8ba9087910f..7731d47a0c7c 100644 --- a/ext/gd/libgd/gd_interpolation.c +++ b/ext/gd/libgd/gd_interpolation.c @@ -57,6 +57,7 @@ #include #include #include +#include #include "gd.h" #include "gdhelpers.h" @@ -1873,7 +1874,10 @@ int gdTransformAffineGetImage(gdImagePtr *dst, src_area = &area_full; } - gdTransformAffineBoundingBox(src_area, affine, &bbox); + if (gdTransformAffineBoundingBox(src_area, affine, &bbox) != GD_TRUE) { + *dst = NULL; + return GD_FALSE; + } *dst = gdImageCreateTrueColor(bbox.width, bbox.height); if (*dst == NULL) { @@ -2049,6 +2053,8 @@ int gdTransformAffineCopy(gdImagePtr dst, int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox) { gdPointF extent[4], min, max, point; + double width, height; + int bbox_x, bbox_y, bbox_width, bbox_height; int i; extent[0].x=0.0; @@ -2079,10 +2085,29 @@ int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPt if (max.y < extent[i].y) max.y=extent[i].y; } - bbox->x = (int) min.x; - bbox->y = (int) min.y; - bbox->width = (int) floor(max.x - min.x) - 1; - bbox->height = (int) floor(max.y - min.y); + width = floor(max.x - min.x); + height = floor(max.y - min.y); + if (!isfinite(min.x) || !isfinite(min.y) || !isfinite(width) || !isfinite(height) + || min.x <= INT_MIN || min.x > INT_MAX + || min.y <= INT_MIN || min.y > INT_MAX + || width < 1.0 || width > INT_MAX + || height < 0.0 || height > INT_MAX) { + return GD_FALSE; + } + bbox_x = (int) min.x; + bbox_y = (int) min.y; + bbox_width = (int) width - 1; + bbox_height = (int) height; + if ((bbox_x < 0 && bbox_width > INT_MAX + bbox_x) + || (bbox_x > 0 && bbox_width > INT_MAX - bbox_x) + || (bbox_y < 0 && bbox_height > INT_MAX + bbox_y) + || (bbox_y > 0 && bbox_height > INT_MAX - bbox_y)) { + return GD_FALSE; + } + bbox->x = bbox_x; + bbox->y = bbox_y; + bbox->width = bbox_width; + bbox->height = bbox_height; return GD_TRUE; } diff --git a/ext/gd/libgd/gd_security.c b/ext/gd/libgd/gd_security.c index 438a564ff17c..cbc4872d2fce 100644 --- a/ext/gd/libgd/gd_security.c +++ b/ext/gd/libgd/gd_security.c @@ -14,6 +14,7 @@ #include #include +#include #include #include "gd.h" #include "gd_errors.h" @@ -30,3 +31,20 @@ int overflow2(int a, int b) } return 0; } + +int overflowMul3(int a, int b, int c) +{ + if (a < 0 || b < 0 || c < 0) { + return 1; + } + if (a == 0 || b == 0 || c == 0) { + return 0; + } + if (a > INT_MAX / b) { + return 1; + } + if ((int64_t)a * b > INT64_MAX / c) { + return 1; + } + return 0; +} diff --git a/ext/gd/libgd/gd_xbm.c b/ext/gd/libgd/gd_xbm.c index 36eff58725dd..bd81b8685b20 100644 --- a/ext/gd/libgd/gd_xbm.c +++ b/ext/gd/libgd/gd_xbm.c @@ -189,7 +189,7 @@ void gdImageXbmCtx(gdImagePtr image, char* file_name, int fg, gdIOCtx * out) } else { for (i=0; i --FILE-- --FILE-- +--EXPECT-- +bool(true) +array(4) { + ["red"]=> + int(0) + ["green"]=> + int(0) + ["blue"]=> + int(0) + ["alpha"]=> + int(0) +} diff --git a/ext/gd/tests/gh19730.phpt b/ext/gd/tests/gh19730.phpt new file mode 100644 index 000000000000..5ea4a2846a9e --- /dev/null +++ b/ext/gd/tests/gh19730.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-19730 (undefined behavior in gd_interpolation.c) +--EXTENSIONS-- +gd +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(false) diff --git a/ext/gd/tests/gh19739.phpt b/ext/gd/tests/gh19739.phpt new file mode 100644 index 000000000000..7dce387b780e --- /dev/null +++ b/ext/gd/tests/gh19739.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-19739 (integer overflow in imageellipse / imagefilledellipse) +--EXTENSIONS-- +gd +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +done diff --git a/ext/gd/tests/imagebmp_basic.phpt b/ext/gd/tests/imagebmp_basic.phpt index ff302a8b0742..82b14f06b3cd 100644 --- a/ext/gd/tests/imagebmp_basic.phpt +++ b/ext/gd/tests/imagebmp_basic.phpt @@ -2,6 +2,12 @@ imagebmp() - basic functionality --EXTENSIONS-- gd +--SKIPIF-- + --FILE-- ce)); zend_objects_clone_members( &new_object->std, &old_object->std); @@ -332,11 +332,11 @@ static zend_result shift_operator_helper(gmp_binary_ui_op_t op, zval *return_val if (UNEXPECTED(Z_TYPE_P(op2) != IS_LONG)) { if (UNEXPECTED(!IS_GMP(op2))) { - // For PHP 8.3 and up use zend_try_get_long() + bool failed; switch (Z_TYPE_P(op2)) { case IS_DOUBLE: - shift = zval_get_long(op2); - if (UNEXPECTED(EG(exception))) { + shift = zval_try_get_long(op2, &failed); + if (UNEXPECTED(failed)) { return FAILURE; } break; @@ -586,7 +586,7 @@ ZEND_MINIT_FUNCTION(gmp) gmp_ce->unserialize = gmp_unserialize; memcpy(&gmp_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - gmp_object_handlers.offset = XtOffsetOf(gmp_object, std); + gmp_object_handlers.offset = offsetof(gmp_object, std); gmp_object_handlers.free_obj = gmp_free_object_storage; gmp_object_handlers.cast_object = gmp_cast_object; gmp_object_handlers.get_debug_info = gmp_get_debug_info; @@ -632,7 +632,7 @@ static zend_result convert_zstr_to_gmp(mpz_t gmp_number, const zend_string *val, bool skip_lead = false; size_t num_len = ZSTR_LEN(val); - while (isspace(*num_str)) { + while (isspace((unsigned char)*num_str)) { ++num_str; --num_len; } diff --git a/ext/gmp/php_gmp_int.h b/ext/gmp/php_gmp_int.h index d6d0a6163ac4..d563a43ba553 100644 --- a/ext/gmp/php_gmp_int.h +++ b/ext/gmp/php_gmp_int.h @@ -35,9 +35,7 @@ typedef struct _gmp_object { zend_object std; } gmp_object; -static inline gmp_object *php_gmp_object_from_zend_object(zend_object *zobj) { - return (gmp_object *)( ((char *)zobj) - XtOffsetOf(gmp_object, std) ); -} +#define php_gmp_object_from_zend_object(zobj) ZEND_CONTAINER_OF(zobj, gmp_object, std) PHP_GMP_API zend_class_entry *php_gmp_class_entry(void); diff --git a/ext/gmp/tests/overloading_with_float_fractional.phpt b/ext/gmp/tests/overloading_with_float_fractional.phpt index fc078eeec3e9..a86356a9dd54 100644 --- a/ext/gmp/tests/overloading_with_float_fractional.phpt +++ b/ext/gmp/tests/overloading_with_float_fractional.phpt @@ -100,6 +100,8 @@ object(GMP)#2 (1) { ["num"]=> string(1) "0" } + +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d object(GMP)#2 (1) { ["num"]=> string(69) "150130937545296572356771972164254457814047970568738777235893533016064" @@ -122,10 +124,14 @@ object(GMP)#2 (1) { ["num"]=> string(1) "0" } + +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d object(GMP)#2 (1) { ["num"]=> string(15) "184717953466368" } + +Deprecated: Implicit conversion from float 42.5 to int loses precision in %s on line %d object(GMP)#2 (1) { ["num"]=> string(1) "0" diff --git a/ext/hash/hash.c b/ext/hash/hash.c index 0f7d24275722..96adc561c228 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -1397,7 +1397,7 @@ static void php_hashcontext_free(zend_object *obj) { /* {{{ php_hashcontext_clone */ static zend_object *php_hashcontext_clone(zend_object *zobj) { - php_hashcontext_object *oldobj = php_hashcontext_from_object(zobj); + const php_hashcontext_object *oldobj = php_hashcontext_from_object(zobj); zend_object *znew = php_hashcontext_create(zobj->ce); php_hashcontext_object *newobj = php_hashcontext_from_object(znew); @@ -1652,7 +1652,7 @@ PHP_MINIT_FUNCTION(hash) memcpy(&php_hashcontext_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - php_hashcontext_handlers.offset = XtOffsetOf(php_hashcontext_object, std); + php_hashcontext_handlers.offset = offsetof(php_hashcontext_object, std); php_hashcontext_handlers.free_obj = php_hashcontext_free; php_hashcontext_handlers.clone_obj = php_hashcontext_clone; diff --git a/ext/iconv/iconv.c b/ext/iconv/iconv.c index 5dfc5d9a1906..d9b54388a8f8 100644 --- a/ext/iconv/iconv.c +++ b/ext/iconv/iconv.c @@ -2633,9 +2633,14 @@ static const php_stream_filter_ops php_iconv_stream_filter_ops = { static php_stream_filter *php_iconv_stream_filter_factory_create(const char *name, zval *params, bool persistent) { php_iconv_stream_filter *inst; + php_stream_filter_seekable_t write_seekable; const char *from_charset = NULL, *to_charset = NULL; size_t from_charset_len, to_charset_len; + if (php_stream_filter_parse_write_seek_mode(params, &write_seekable) == FAILURE) { + return NULL; + } + if ((from_charset = strchr(name, '.')) == NULL) { return NULL; } @@ -2663,7 +2668,7 @@ static php_stream_filter *php_iconv_stream_filter_factory_create(const char *nam } return php_stream_filter_alloc(&php_iconv_stream_filter_ops, inst, persistent, - PSFS_SEEKABLE_START); + PSFS_SEEKABLE_START, write_seekable); } /* }}} */ diff --git a/ext/intl/ERROR_CONVENTIONS.md b/ext/intl/ERROR_CONVENTIONS.md index 4b29101a0a86..5c1b4eca86b8 100644 --- a/ext/intl/ERROR_CONVENTIONS.md +++ b/ext/intl/ERROR_CONVENTIONS.md @@ -6,14 +6,14 @@ conventions are enumerated in this document. * The last error is always stored globally. The global error code can be obtained in userland with `intl_get_error_code()`. -This is a `U_*` error code defined by ICU, but it does not have necessarily to -be returned obtained after a call to an ICU function. That is to say, the +This is a `U_*` error code defined by ICU, but it is not necessarily obtained +from a call to an ICU function. That is to say, the internal PHP wrapper functions can set these error codes when appropriate. For instance, in response to bad arguments (e.g. `zend_parse_parameters()` failure), the PHP wrapper function should set the global error code to -`U_ILLEGAL_ARGUMENT_ERROR`). +`U_ILLEGAL_ARGUMENT_ERROR`. -The error code (an integer) can be converter to the corresponding enum name +The error code (an integer) can be converted to the corresponding enum name string in userland with `intl_error_name()`. The associated message can be obtained with `intl_get_error_message()`. This is @@ -60,7 +60,7 @@ void intl_errors_set(intl_error* err, UErrorCode code, char* msg, int copyMsg); ``` by passing a pointer to the object's `intl_error` as the first parameter. -Node the extra `s` in the functions' names (`errors`, not `error`). +Note the extra `s` in the functions' names (`errors`, not `error`). Static methods should only set the global error. @@ -68,15 +68,15 @@ Static methods should only set the global error. `getErrorMessage()` methods. These methods are used to retrieve the error codes stored in the object's -private `intl_error` structured and mirror the global `intl_get_error_code()` +private `intl_error` structure and mirror the global `intl_get_error_code()` and `intl_get_error_message()`. * Intl methods and functions should return `FALSE` on error (even argument parsing errors), not `NULL`. Constructors and factory methods are the exception; these should return `NULL`, not `FALSE`. -Note that constructors in Intl generally (always?) don't throws exceptions. They -instead destroy the object to that the result of new `IntlClass()` can be +Note that constructors in Intl generally (always?) do not throw exceptions. They +instead destroy the object so that the result of new `IntlClass()` can be `NULL`. This may be surprising. * Intl functions and methods should reset the global error before doing anything diff --git a/ext/intl/breakiterator/breakiterator_class.cpp b/ext/intl/breakiterator/breakiterator_class.cpp index 901ed1015536..4d5793696cbb 100644 --- a/ext/intl/breakiterator/breakiterator_class.cpp +++ b/ext/intl/breakiterator/breakiterator_class.cpp @@ -96,7 +96,7 @@ static int BreakIterator_compare_objects(zval *object1, /* {{{ clone handler for BreakIterator */ static zend_object *BreakIterator_clone_obj(zend_object *object) { - BreakIterator_object *bio_orig = php_intl_breakiterator_fetch_object(object); + const BreakIterator_object *bio_orig = php_intl_breakiterator_fetch_object(object); zend_object *ret_val = BreakIterator_ce_ptr->create_object(object->ce); BreakIterator_object *bio_new = php_intl_breakiterator_fetch_object(ret_val); @@ -212,7 +212,7 @@ U_CFUNC void breakiterator_register_BreakIterator_class(void) memcpy(&BreakIterator_handlers, &std_object_handlers, sizeof BreakIterator_handlers); - BreakIterator_handlers.offset = XtOffsetOf(BreakIterator_object, zo); + BreakIterator_handlers.offset = offsetof(BreakIterator_object, zo); BreakIterator_handlers.compare = BreakIterator_compare_objects; BreakIterator_handlers.clone_obj = BreakIterator_clone_obj; BreakIterator_handlers.get_debug_info = BreakIterator_get_debug_info; diff --git a/ext/intl/breakiterator/breakiterator_class.h b/ext/intl/breakiterator/breakiterator_class.h index 96d8bba626e2..cb4f072139ec 100644 --- a/ext/intl/breakiterator/breakiterator_class.h +++ b/ext/intl/breakiterator/breakiterator_class.h @@ -41,9 +41,7 @@ typedef struct { zend_object zo; } BreakIterator_object; -static inline BreakIterator_object *php_intl_breakiterator_fetch_object(zend_object *obj) { - return (BreakIterator_object *)((char*)(obj) - XtOffsetOf(BreakIterator_object, zo)); -} +#define php_intl_breakiterator_fetch_object(obj) ZEND_CONTAINER_OF(obj, BreakIterator_object, zo) #define Z_INTL_BREAKITERATOR_P(zv) php_intl_breakiterator_fetch_object(Z_OBJ_P(zv)) #define BREAKITER_ERROR(bio) (bio)->err diff --git a/ext/intl/breakiterator/breakiterator_methods.cpp b/ext/intl/breakiterator/breakiterator_methods.cpp index 972cd28cf38e..66a35e65ac6a 100644 --- a/ext/intl/breakiterator/breakiterator_methods.cpp +++ b/ext/intl/breakiterator/breakiterator_methods.cpp @@ -297,11 +297,9 @@ U_CFUNC PHP_METHOD(IntlBreakIterator, getLocale) Z_PARAM_LONG(locale_type) ZEND_PARSE_PARAMETERS_END(); - /* TODO: Change to ValueError? */ if (locale_type != ULOC_ACTUAL_LOCALE && locale_type != ULOC_VALID_LOCALE) { - intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, - "invalid locale type"); - RETURN_FALSE; + zend_argument_value_error(1, "must be either Locale::ACTUAL_LOCALE or Locale::VALID_LOCALE"); + RETURN_THROWS(); } BREAKITER_METHOD_FETCH_OBJECT; diff --git a/ext/intl/calendar/calendar_class.cpp b/ext/intl/calendar/calendar_class.cpp index d09f04454790..63b203d08d44 100644 --- a/ext/intl/calendar/calendar_class.cpp +++ b/ext/intl/calendar/calendar_class.cpp @@ -77,7 +77,7 @@ U_CFUNC void calendar_object_construct(zval *object, /* {{{ clone handler for Calendar */ static zend_object *Calendar_clone_obj(zend_object *object) { - Calendar_object *co_orig = php_intl_calendar_fetch_object(object); + const Calendar_object *co_orig = php_intl_calendar_fetch_object(object); zend_object *ret_val = Calendar_ce_ptr->create_object(object->ce); Calendar_object *co_new = php_intl_calendar_fetch_object(ret_val); @@ -256,7 +256,7 @@ void calendar_register_IntlCalendar_class(void) memcpy( &Calendar_handlers, &std_object_handlers, sizeof Calendar_handlers); - Calendar_handlers.offset = XtOffsetOf(Calendar_object, zo); + Calendar_handlers.offset = offsetof(Calendar_object, zo); Calendar_handlers.clone_obj = Calendar_clone_obj; Calendar_handlers.get_debug_info = Calendar_get_debug_info; Calendar_handlers.free_obj = Calendar_objects_free; diff --git a/ext/intl/calendar/calendar_class.h b/ext/intl/calendar/calendar_class.h index 540646ba3049..7c74aede9475 100644 --- a/ext/intl/calendar/calendar_class.h +++ b/ext/intl/calendar/calendar_class.h @@ -38,9 +38,7 @@ typedef struct { zend_object zo; } Calendar_object; -static inline Calendar_object *php_intl_calendar_fetch_object(zend_object *obj) { - return (Calendar_object *)((char*)(obj) - XtOffsetOf(Calendar_object, zo)); -} +#define php_intl_calendar_fetch_object(obj) ZEND_CONTAINER_OF(obj, Calendar_object, zo) #define Z_INTL_CALENDAR_P(zv) php_intl_calendar_fetch_object(Z_OBJ_P(zv)) #define CALENDAR_ERROR(co) (co)->err diff --git a/ext/intl/calendar/calendar_methods.cpp b/ext/intl/calendar/calendar_methods.cpp index 4828c6417300..fe4749d6d62d 100644 --- a/ext/intl/calendar/calendar_methods.cpp +++ b/ext/intl/calendar/calendar_methods.cpp @@ -85,7 +85,7 @@ U_CFUNC PHP_FUNCTION(intlcal_create_instance) Z_PARAM_STRING_OR_NULL(locale_str, locale_len) ZEND_PARSE_PARAMETERS_END(); - TimeZone *timeZone = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr); + TimeZone *timeZone = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr, 1); if (timeZone == nullptr) { RETURN_NULL(); } @@ -203,7 +203,7 @@ U_CFUNC PHP_FUNCTION(intlcal_get_available_locales) int32_t count; const Locale *availLocales = Calendar::getAvailableLocales(count); - array_init(return_value); + array_init_size(return_value, count); for (int i = 0; i < count; i++) { Locale locale = availLocales[i]; add_next_index_string(return_value, locale.getName()); @@ -316,7 +316,7 @@ U_CFUNC PHP_FUNCTION(intlcal_set_time_zone) } TimeZone *timeZone = timezone_process_timezone_argument( - timezone_object, timezone_string, CALENDAR_ERROR_P(co)); + timezone_object, timezone_string, CALENDAR_ERROR_P(co), 2); if (timeZone == nullptr) { RETURN_FALSE; } @@ -345,7 +345,7 @@ U_CFUNC PHP_METHOD(IntlCalendar, setTimeZone) } TimeZone *timeZone = timezone_process_timezone_argument( - timezone_object, timezone_string, CALENDAR_ERROR_P(co)); + timezone_object, timezone_string, CALENDAR_ERROR_P(co), 1); if (timeZone == nullptr) { RETURN_FALSE; } @@ -373,7 +373,7 @@ static void _php_intlcal_before_after( when_co = Z_INTL_CALENDAR_P(when_object); if (when_co->ucal == NULL) { - zend_argument_error(NULL, 2, "is uninitialized"); + zend_argument_error(NULL, hasThis() ? 1 : 2, "is uninitialized"); RETURN_THROWS(); } @@ -419,8 +419,8 @@ U_CFUNC PHP_FUNCTION(intlcal_set) } for (int i = 0; i < arg_num; i++) { - /* Arguments start at 1 */ - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(args[i], i + 1); + /* Count from intlcal_set($calendar, ...), so date/time arguments start at #2. */ + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(args[i], i + 2); } CALENDAR_METHOD_FETCH_OBJECT; @@ -455,9 +455,10 @@ U_CFUNC PHP_METHOD(IntlCalendar, setDate) RETURN_THROWS(); } - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 1); - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 2); - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 3); + /* These method-only APIs parse the object first, so the API argument positions are offset by +1. */ + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 2); + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 3); + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 4); CALENDAR_METHOD_FETCH_OBJECT; @@ -478,18 +479,19 @@ U_CFUNC PHP_METHOD(IntlCalendar, setDateTime) RETURN_THROWS(); } - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 1); - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 2); - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 3); - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(hour, 4); - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(minute, 5); + /* These method-only APIs parse the object first, so the API argument positions are offset by +1. */ + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 2); + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 3); + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 4); + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(hour, 5); + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(minute, 6); CALENDAR_METHOD_FETCH_OBJECT; if (second_is_null) { co->ucal->set((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute); } else { - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(second, 6); + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(second, 7); co->ucal->set((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute, (int32_t) second); } } @@ -794,7 +796,7 @@ U_CFUNC PHP_FUNCTION(intlcal_is_equivalent_to) other_co = Z_INTL_CALENDAR_P(other_object); if (other_co->ucal == NULL) { - zend_argument_error(NULL, 2, "is uninitialized"); + zend_argument_error(NULL, hasThis() ? 1 : 2, "is uninitialized"); RETURN_THROWS(); } @@ -931,7 +933,7 @@ U_CFUNC PHP_FUNCTION(intlcal_equals) CALENDAR_METHOD_FETCH_OBJECT; other_co = Z_INTL_CALENDAR_P(other_object); if (other_co->ucal == NULL) { - zend_argument_error(NULL, 2, "is uninitialized"); + zend_argument_error(NULL, hasThis() ? 1 : 2, "is uninitialized"); RETURN_THROWS(); } diff --git a/ext/intl/calendar/gregoriancalendar_methods.cpp b/ext/intl/calendar/gregoriancalendar_methods.cpp index b94548b54a65..aeb728e29de1 100644 --- a/ext/intl/calendar/gregoriancalendar_methods.cpp +++ b/ext/intl/calendar/gregoriancalendar_methods.cpp @@ -143,7 +143,7 @@ static void _php_intlgregcal_constructor_body(INTERNAL_FUNCTION_PARAMETERS, bool if (variant <= 2) { // From timezone and locale (0 to 2 arguments) - TimeZone *tz = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr); + TimeZone *tz = timezone_process_timezone_argument(timezone_object, timezone_string, nullptr, 1); if (tz == nullptr) { // TODO: Exception should always occur already? if (!EG(exception)) { @@ -176,7 +176,7 @@ static void _php_intlgregcal_constructor_body(INTERNAL_FUNCTION_PARAMETERS, bool // From date/time (3, 5 or 6 arguments) GregorianCalendar *tmp; for (int i = 0; i < variant; i++) { - ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(largs[i], hasThis() ? (i-1) : i); + ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(largs[i], i + 1); } if (variant == 3) { diff --git a/ext/intl/collator/collator_class.cpp b/ext/intl/collator/collator_class.cpp index 33c25845a864..4fd991bd156c 100644 --- a/ext/intl/collator/collator_class.cpp +++ b/ext/intl/collator/collator_class.cpp @@ -76,7 +76,7 @@ U_CFUNC void collator_register_Collator_symbols(int module_number) sizeof Collator_handlers); /* Collator has no usable clone semantics - ucol_cloneBinary/ucol_openBinary require binary buffer for which we don't have the place to keep */ - Collator_handlers.offset = XtOffsetOf(Collator_object, zo); + Collator_handlers.offset = offsetof(Collator_object, zo); Collator_handlers.clone_obj = nullptr; Collator_handlers.free_obj = Collator_objects_free; } diff --git a/ext/intl/collator/collator_class.h b/ext/intl/collator/collator_class.h index af564f31e51b..637d5dc490ae 100644 --- a/ext/intl/collator/collator_class.h +++ b/ext/intl/collator/collator_class.h @@ -47,9 +47,7 @@ typedef struct { #define COLLATOR_ERROR_CODE(co) INTL_ERROR_CODE(COLLATOR_ERROR(co)) #define COLLATOR_ERROR_CODE_P(co) &(INTL_ERROR_CODE(COLLATOR_ERROR(co))) -static inline Collator_object *php_intl_collator_fetch_object(zend_object *obj) { - return (Collator_object *)((char*)(obj) - XtOffsetOf(Collator_object, zo)); -} +#define php_intl_collator_fetch_object(obj) ZEND_CONTAINER_OF(obj, Collator_object, zo) #define Z_INTL_COLLATOR_P(zv) php_intl_collator_fetch_object(Z_OBJ_P(zv)) #ifdef __cplusplus diff --git a/ext/intl/common/common_date.cpp b/ext/intl/common/common_date.cpp index 0a7aa023b43c..fb5ca6edaebe 100644 --- a/ext/intl/common/common_date.cpp +++ b/ext/intl/common/common_date.cpp @@ -202,7 +202,6 @@ U_CFUNC double intl_zval_to_millis(zval *z, intl_error *err) } } } else { - /* TODO: try with cast(), get() to obtain a number */ intl_errors_set(err, U_ILLEGAL_ARGUMENT_ERROR, "invalid object type for date/time " "(only IntlCalendar and DateTimeInterface permitted)"); diff --git a/ext/intl/common/common_enum.cpp b/ext/intl/common/common_enum.cpp index 114dabdcc00a..379561866941 100644 --- a/ext/intl/common/common_enum.cpp +++ b/ext/intl/common/common_enum.cpp @@ -294,7 +294,7 @@ U_CFUNC void intl_register_common_symbols(int module_number) memcpy(&IntlIterator_handlers, &std_object_handlers, sizeof IntlIterator_handlers); - IntlIterator_handlers.offset = XtOffsetOf(IntlIterator_object, zo); + IntlIterator_handlers.offset = offsetof(IntlIterator_object, zo); IntlIterator_handlers.clone_obj = NULL; IntlIterator_handlers.dtor_obj = IntlIterator_objects_dtor; IntlIterator_handlers.free_obj = IntlIterator_objects_free; diff --git a/ext/intl/common/common_enum.h b/ext/intl/common/common_enum.h index 5c1f7495762f..e4a8b5461df6 100644 --- a/ext/intl/common/common_enum.h +++ b/ext/intl/common/common_enum.h @@ -51,10 +51,7 @@ typedef struct { zend_object zo; } IntlIterator_object; - -static inline IntlIterator_object *php_intl_iterator_fetch_object(zend_object *obj) { - return (IntlIterator_object *)((char*)(obj) - XtOffsetOf(IntlIterator_object, zo)); -} +#define php_intl_iterator_fetch_object(obj) ZEND_CONTAINER_OF(obj, IntlIterator_object, zo) #define Z_INTL_ITERATOR_P(zv) php_intl_iterator_fetch_object(Z_OBJ_P(zv)) typedef struct { diff --git a/ext/intl/converter/converter.cpp b/ext/intl/converter/converter.cpp index 8921728fc9d7..88f05136bbbd 100644 --- a/ext/intl/converter/converter.cpp +++ b/ext/intl/converter/converter.cpp @@ -37,7 +37,7 @@ typedef struct _php_converter_object { static inline php_converter_object *php_converter_fetch_object(zend_object *obj) { - return (php_converter_object *)((char*)(obj) - XtOffsetOf(php_converter_object, obj)); + return (php_converter_object *)((char*)(obj) - offsetof(php_converter_object, obj)); } #define Z_INTL_CONVERTER_P(zv) php_converter_fetch_object(Z_OBJ_P(zv)) @@ -167,9 +167,9 @@ static void php_converter_append_toUnicode_target(zval *val, UConverterToUnicode if (lval > 0xFFFF) { /* Supplemental planes U+010000 - U+10FFFF */ if (TARGET_CHECK(args, 2)) { - /* TODO: Find the ICU call which does this properly */ - *(args->target++) = (UChar)(((lval - 0x10000) >> 10) | 0xD800); - *(args->target++) = (UChar)(((lval - 0x10000) & 0x3FF) | 0xDC00); + int32_t offset = 0; + U16_APPEND_UNSAFE(args->target, offset, lval); + args->target += offset; } return; } @@ -682,6 +682,16 @@ static zend_string* php_converter_do_convert(UConverter *dest_cnv, } /* }}} */ +static void php_converter_set_subst_chars(UConverter *cnv, zend_string *subst, UErrorCode *error) +{ + if (ZSTR_LEN(subst) > SCHAR_MAX) { + *error = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + ucnv_setSubstChars(cnv, ZSTR_VAL(subst), (int8_t) ZSTR_LEN(subst), error); +} + /* {{{ */ #define UCNV_REASON_CASE(v) case (UCNV_ ## v) : RETURN_STRINGL( "REASON_" #v , sizeof( "REASON_" #v ) - 1); PHP_METHOD(UConverter, reasonText) { @@ -761,13 +771,13 @@ PHP_METHOD(UConverter, transcode) { (tmpzval = zend_hash_str_find_deref(Z_ARRVAL_P(options), "from_subst", sizeof("from_subst") - 1)) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) { error = U_ZERO_ERROR; - ucnv_setSubstChars(src_cnv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval) & 0x7F, &error); + php_converter_set_subst_chars(src_cnv, Z_STR_P(tmpzval), &error); } if (U_SUCCESS(error) && (tmpzval = zend_hash_str_find_deref(Z_ARRVAL_P(options), "to_subst", sizeof("to_subst") - 1)) != NULL && Z_TYPE_P(tmpzval) == IS_STRING) { error = U_ZERO_ERROR; - ucnv_setSubstChars(dest_cnv, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval) & 0x7F, &error); + php_converter_set_subst_chars(dest_cnv, Z_STR_P(tmpzval), &error); } } @@ -934,7 +944,8 @@ static zend_object *php_converter_create_object(zend_class_entry *ce) { } static zend_object *php_converter_clone_object(zend_object *object) { - php_converter_object *objval, *oldobj = php_converter_fetch_object(object); + const php_converter_object *oldobj = php_converter_fetch_object(object); + php_converter_object *objval; zend_object *retval = php_converter_object_ctor(object->ce, &objval); UErrorCode error = U_ZERO_ERROR; @@ -975,7 +986,7 @@ U_CFUNC int php_converter_minit(INIT_FUNC_ARGS) { php_converter_ce->create_object = php_converter_create_object; php_converter_ce->default_object_handlers = &php_converter_object_handlers; memcpy(&php_converter_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - php_converter_object_handlers.offset = XtOffsetOf(php_converter_object, obj); + php_converter_object_handlers.offset = offsetof(php_converter_object, obj); php_converter_object_handlers.clone_obj = php_converter_clone_object; php_converter_object_handlers.free_obj = php_converter_free_object; diff --git a/ext/intl/dateformat/dateformat_attrcpp.cpp b/ext/intl/dateformat/dateformat_attrcpp.cpp index 1391823ff0fe..1c8fb88a3d51 100644 --- a/ext/intl/dateformat/dateformat_attrcpp.cpp +++ b/ext/intl/dateformat/dateformat_attrcpp.cpp @@ -94,7 +94,7 @@ U_CFUNC PHP_FUNCTION(datefmt_set_timezone) DATE_FORMAT_METHOD_FETCH_OBJECT; TimeZone *timezone = timezone_process_timezone_argument( - timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo)); + timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo), 2); if (timezone == nullptr) { RETURN_FALSE; } @@ -119,7 +119,7 @@ U_CFUNC PHP_METHOD(IntlDateFormatter, setTimeZone) DATE_FORMAT_METHOD_FETCH_OBJECT; TimeZone *timezone = timezone_process_timezone_argument( - timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo)); + timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo), 1); if (timezone == nullptr) { RETURN_FALSE; } diff --git a/ext/intl/dateformat/dateformat_class.cpp b/ext/intl/dateformat/dateformat_class.cpp index 03f2bc9a2d6a..e9869612b873 100644 --- a/ext/intl/dateformat/dateformat_class.cpp +++ b/ext/intl/dateformat/dateformat_class.cpp @@ -66,7 +66,7 @@ zend_object *IntlDateFormatter_object_create(zend_class_entry *ce) /* {{{ IntlDateFormatter_object_clone */ zend_object *IntlDateFormatter_object_clone(zend_object *object) { - IntlDateFormatter_object *dfo = php_intl_dateformatter_fetch_object(object); + const IntlDateFormatter_object *dfo = php_intl_dateformatter_fetch_object(object); zend_object *new_obj = IntlDateFormatter_ce_ptr->create_object(object->ce); IntlDateFormatter_object *new_dfo = php_intl_dateformatter_fetch_object(new_obj); @@ -104,7 +104,7 @@ void dateformat_register_IntlDateFormatter_class( void ) memcpy(&IntlDateFormatter_handlers, &std_object_handlers, sizeof IntlDateFormatter_handlers); - IntlDateFormatter_handlers.offset = XtOffsetOf(IntlDateFormatter_object, zo); + IntlDateFormatter_handlers.offset = offsetof(IntlDateFormatter_object, zo); IntlDateFormatter_handlers.clone_obj = IntlDateFormatter_object_clone; IntlDateFormatter_handlers.free_obj = IntlDateFormatter_object_free; } diff --git a/ext/intl/dateformat/dateformat_class.h b/ext/intl/dateformat/dateformat_class.h index 458313a92502..bef8f6dd6146 100644 --- a/ext/intl/dateformat/dateformat_class.h +++ b/ext/intl/dateformat/dateformat_class.h @@ -36,9 +36,7 @@ typedef struct { zend_object zo; } IntlDateFormatter_object; -static inline IntlDateFormatter_object *php_intl_dateformatter_fetch_object(zend_object *obj) { - return (IntlDateFormatter_object *)((char*)(obj) - XtOffsetOf(IntlDateFormatter_object, zo)); -} +#define php_intl_dateformatter_fetch_object(obj) ZEND_CONTAINER_OF(obj, IntlDateFormatter_object, zo) #define Z_INTL_DATEFORMATTER_P(zv) php_intl_dateformatter_fetch_object(Z_OBJ_P(zv)) #ifdef __cplusplus diff --git a/ext/intl/dateformat/dateformat_create.cpp b/ext/intl/dateformat/dateformat_create.cpp index 4b055fc88eba..df98c7178fc6 100644 --- a/ext/intl/dateformat/dateformat_create.cpp +++ b/ext/intl/dateformat/dateformat_create.cpp @@ -99,7 +99,7 @@ static zend_result datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS) return FAILURE; } if (date_type == UDAT_PATTERN && time_type != UDAT_PATTERN) { - intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "time format must be UDAT_PATTERN if date format is UDAT_PATTERN"); + intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "datefmt_create: time format must be IntlDateFormatter::PATTERN if date format is IntlDateFormatter::PATTERN"); return FAILURE; } @@ -131,7 +131,7 @@ static zend_result datefmt_ctor(INTERNAL_FUNCTION_PARAMETERS) if (explicit_tz || calendar_owned ) { //we have an explicit time zone or a non-object calendar - timezone = timezone_process_timezone_argument(timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo)); + timezone = timezone_process_timezone_argument(timezone_object, timezone_string, INTL_DATA_ERROR_P(dfo), 4); if (timezone == nullptr) { goto error; } diff --git a/ext/intl/dateformat/dateformat_parse.cpp b/ext/intl/dateformat/dateformat_parse.cpp index 667bbf98ac62..13cf56ad7d82 100644 --- a/ext/intl/dateformat/dateformat_parse.cpp +++ b/ext/intl/dateformat/dateformat_parse.cpp @@ -108,7 +108,7 @@ static void internal_parse_to_localtime(IntlDateFormatter_object *dfo, char* tex INTL_METHOD_CHECK_STATUS( dfo, "Date parsing failed" ); - array_init( return_value ); + array_init_size( return_value, 9 ); /* Add entries from various fields of the obtained parsed_calendar */ add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_SECOND, CALENDAR_SEC); add_to_localtime_arr( dfo, return_value, parsed_calendar, UCAL_MINUTE, CALENDAR_MIN); diff --git a/ext/intl/dateformat/datepatterngenerator_class.cpp b/ext/intl/dateformat/datepatterngenerator_class.cpp index 6a218fab72ff..4a137d916f12 100644 --- a/ext/intl/dateformat/datepatterngenerator_class.cpp +++ b/ext/intl/dateformat/datepatterngenerator_class.cpp @@ -36,7 +36,7 @@ zend_object_handlers IntlDatePatternGenerator_handlers; static zend_object *IntlDatePatternGenerator_object_clone(zend_object *object) { - IntlDatePatternGenerator_object *dtpgo_orig = php_intl_datepatterngenerator_fetch_object(object); + const IntlDatePatternGenerator_object *dtpgo_orig = php_intl_datepatterngenerator_fetch_object(object); zend_object *ret_val = IntlDatePatternGenerator_ce_ptr->create_object(object->ce); IntlDatePatternGenerator_object *dtpgo_new = php_intl_datepatterngenerator_fetch_object(ret_val); @@ -106,7 +106,7 @@ void dateformat_register_IntlDatePatternGenerator_class( void ) memcpy(&IntlDatePatternGenerator_handlers, &std_object_handlers, sizeof IntlDatePatternGenerator_handlers); - IntlDatePatternGenerator_handlers.offset = XtOffsetOf(IntlDatePatternGenerator_object, zo); + IntlDatePatternGenerator_handlers.offset = offsetof(IntlDatePatternGenerator_object, zo); IntlDatePatternGenerator_handlers.clone_obj = IntlDatePatternGenerator_object_clone; IntlDatePatternGenerator_handlers.free_obj = IntlDatePatternGenerator_object_free; } diff --git a/ext/intl/dateformat/datepatterngenerator_class.h b/ext/intl/dateformat/datepatterngenerator_class.h index 017bea2cca07..c2c280e2df2c 100644 --- a/ext/intl/dateformat/datepatterngenerator_class.h +++ b/ext/intl/dateformat/datepatterngenerator_class.h @@ -36,9 +36,7 @@ typedef struct { zend_object zo; } IntlDatePatternGenerator_object; -static inline IntlDatePatternGenerator_object *php_intl_datepatterngenerator_fetch_object(zend_object *obj) { - return (IntlDatePatternGenerator_object *)((char*)(obj) - XtOffsetOf(IntlDatePatternGenerator_object, zo)); -} +#define php_intl_datepatterngenerator_fetch_object(obj) ZEND_CONTAINER_OF(obj, IntlDatePatternGenerator_object, zo) #define Z_INTL_DATEPATTERNGENERATOR_P(zv) php_intl_datepatterngenerator_fetch_object(Z_OBJ_P(zv)) #define DTPATTERNGEN_ERROR(dtpgo) (dtpgo)->err diff --git a/ext/intl/formatter/formatter_attr.cpp b/ext/intl/formatter/formatter_attr.cpp index 01108351a5d4..d21873ecdabc 100644 --- a/ext/intl/formatter/formatter_attr.cpp +++ b/ext/intl/formatter/formatter_attr.cpp @@ -16,13 +16,16 @@ #include #endif +#include +#include "../intl_convertcpp.h" +#include "formatter_class.h" + extern "C" { #include "php_intl.h" #include "intl_convert.h" } -#include "formatter_class.h" -#include +#define FORMATTER_UNUM(nfo) reinterpret_cast(FORMATTER_OBJECT(nfo)) /* {{{ Get formatter attribute value. */ U_CFUNC PHP_FUNCTION( numfmt_get_attribute ) @@ -62,7 +65,7 @@ U_CFUNC PHP_FUNCTION( numfmt_get_attribute ) case UNUM_MIN_SIGNIFICANT_DIGITS: case UNUM_MAX_SIGNIFICANT_DIGITS: case UNUM_LENIENT_PARSE: - value = unum_getAttribute(FORMATTER_OBJECT(nfo), attribute); + value = unum_getAttribute(FORMATTER_UNUM(nfo), attribute); if(value == -1) { INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR; } else { @@ -71,7 +74,7 @@ U_CFUNC PHP_FUNCTION( numfmt_get_attribute ) break; case UNUM_ROUNDING_INCREMENT: { - double value_double = unum_getDoubleAttribute(FORMATTER_OBJECT(nfo), attribute); + double value_double = unum_getDoubleAttribute(FORMATTER_UNUM(nfo), attribute); if(value_double == -1) { INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR; } else { @@ -110,12 +113,12 @@ U_CFUNC PHP_FUNCTION( numfmt_get_text_attribute ) UNumberFormatTextAttribute attribute = static_cast(lattribute); - length = unum_getTextAttribute( FORMATTER_OBJECT(nfo), attribute, value, value_buf_size, &INTL_DATA_ERROR_CODE(nfo) ); + length = unum_getTextAttribute( FORMATTER_UNUM(nfo), attribute, value, value_buf_size, &INTL_DATA_ERROR_CODE(nfo) ); if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= value_buf_size) { ++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */ INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR; value = eumalloc(length); - length = unum_getTextAttribute( FORMATTER_OBJECT(nfo), attribute, value, length, &INTL_DATA_ERROR_CODE(nfo) ); + length = unum_getTextAttribute( FORMATTER_UNUM(nfo), attribute, value, length, &INTL_DATA_ERROR_CODE(nfo) ); if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) { efree(value); value = value_buf; @@ -166,10 +169,10 @@ U_CFUNC PHP_FUNCTION( numfmt_set_attribute ) case UNUM_MIN_SIGNIFICANT_DIGITS: case UNUM_MAX_SIGNIFICANT_DIGITS: case UNUM_LENIENT_PARSE: - unum_setAttribute(FORMATTER_OBJECT(nfo), attribute, zval_get_long(value)); + unum_setAttribute(FORMATTER_UNUM(nfo), attribute, zval_get_long(value)); break; case UNUM_ROUNDING_INCREMENT: - unum_setDoubleAttribute(FORMATTER_OBJECT(nfo), attribute, zval_get_double(value)); + unum_setDoubleAttribute(FORMATTER_UNUM(nfo), attribute, zval_get_double(value)); break; default: INTL_DATA_ERROR_CODE(nfo) = U_UNSUPPORTED_ERROR; @@ -185,8 +188,6 @@ U_CFUNC PHP_FUNCTION( numfmt_set_attribute ) /* {{{ Get formatter attribute value. */ U_CFUNC PHP_FUNCTION( numfmt_set_text_attribute ) { - int32_t slength = 0; - UChar *svalue = NULL; zend_long attribute; char *value; size_t len; @@ -203,14 +204,12 @@ U_CFUNC PHP_FUNCTION( numfmt_set_text_attribute ) FORMATTER_METHOD_FETCH_OBJECT; /* Convert given attribute value to UTF-16. */ - intl_convert_utf8_to_utf16(&svalue, &slength, value, len, &INTL_DATA_ERROR_CODE(nfo)); + UnicodeString svalue; + intl_stringFromChar(svalue, value, len, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Error converting attribute value to UTF-16" ); /* Actually set new attribute value. */ - unum_setTextAttribute(FORMATTER_OBJECT(nfo), static_cast(attribute), svalue, slength, &INTL_DATA_ERROR_CODE(nfo)); - if (svalue) { - efree(svalue); - } + unum_setTextAttribute(FORMATTER_UNUM(nfo), static_cast(attribute), svalue.getBuffer(), svalue.length(), &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Error setting text attribute" ); RETURN_TRUE; @@ -243,12 +242,12 @@ U_CFUNC PHP_FUNCTION( numfmt_get_symbol ) /* Fetch the object. */ FORMATTER_METHOD_FETCH_OBJECT; - length = unum_getSymbol(FORMATTER_OBJECT(nfo), symbol, value_buf, length, &INTL_DATA_ERROR_CODE(nfo)); + length = unum_getSymbol(FORMATTER_UNUM(nfo), symbol, value_buf, length, &INTL_DATA_ERROR_CODE(nfo)); if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value_buf )) { ++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */ INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR; value = eumalloc(length); - length = unum_getSymbol(FORMATTER_OBJECT(nfo), symbol, value, length, &INTL_DATA_ERROR_CODE(nfo)); + length = unum_getSymbol(FORMATTER_UNUM(nfo), symbol, value, length, &INTL_DATA_ERROR_CODE(nfo)); if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) { efree(value); value = value_buf; @@ -266,8 +265,6 @@ U_CFUNC PHP_FUNCTION( numfmt_set_symbol ) zend_long lsymbol; char* value = NULL; size_t value_len = 0; - UChar* svalue = 0; - int32_t slength = 0; FORMATTER_METHOD_INIT_VARS; /* Parse parameters. */ @@ -288,14 +285,12 @@ U_CFUNC PHP_FUNCTION( numfmt_set_symbol ) FORMATTER_METHOD_FETCH_OBJECT; /* Convert given symbol to UTF-16. */ - intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(nfo)); + UnicodeString svalue; + intl_stringFromChar(svalue, value, value_len, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Error converting symbol value to UTF-16" ); /* Actually set the symbol. */ - unum_setSymbol(FORMATTER_OBJECT(nfo), symbol, svalue, slength, &INTL_DATA_ERROR_CODE(nfo)); - if (svalue) { - efree(svalue); - } + unum_setSymbol(FORMATTER_UNUM(nfo), symbol, svalue.getBuffer(), svalue.length(), &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Error setting symbol value" ); RETURN_TRUE; @@ -320,12 +315,12 @@ U_CFUNC PHP_FUNCTION( numfmt_get_pattern ) /* Fetch the object. */ FORMATTER_METHOD_FETCH_OBJECT; - length = unum_toPattern(FORMATTER_OBJECT(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo)); + length = unum_toPattern(FORMATTER_UNUM(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo)); if(INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR && length >= USIZE( value_buf )) { ++length; /* to avoid U_STRING_NOT_TERMINATED_WARNING */ INTL_DATA_ERROR_CODE(nfo) = U_ZERO_ERROR; value = eumalloc(length); - length = unum_toPattern( FORMATTER_OBJECT(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo) ); + length = unum_toPattern( FORMATTER_UNUM(nfo), 0, value, length, &INTL_DATA_ERROR_CODE(nfo) ); if(U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) { efree(value); value = value_buf; @@ -342,8 +337,6 @@ U_CFUNC PHP_FUNCTION( numfmt_set_pattern ) { char* value = NULL; size_t value_len = 0; - int32_t slength = 0; - UChar* svalue = NULL; UParseError spattern_error = {0}; FORMATTER_METHOD_INIT_VARS; @@ -357,13 +350,11 @@ U_CFUNC PHP_FUNCTION( numfmt_set_pattern ) FORMATTER_METHOD_FETCH_OBJECT; /* Convert given pattern to UTF-16. */ - intl_convert_utf8_to_utf16(&svalue, &slength, value, value_len, &INTL_DATA_ERROR_CODE(nfo)); + UnicodeString svalue; + intl_stringFromChar(svalue, value, value_len, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Error converting pattern to UTF-16" ); - unum_applyPattern(FORMATTER_OBJECT(nfo), 0, svalue, slength, &spattern_error, &INTL_DATA_ERROR_CODE(nfo)); - if (svalue) { - efree(svalue); - } + unum_applyPattern(FORMATTER_UNUM(nfo), 0, svalue.getBuffer(), svalue.length(), &spattern_error, &INTL_DATA_ERROR_CODE(nfo)); if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) { char *msg; spprintf(&msg, 0, "Error setting pattern value at line %d, offset %d", spattern_error.line, spattern_error.offset); @@ -393,7 +384,7 @@ U_CFUNC PHP_FUNCTION( numfmt_get_locale ) /* Fetch the object. */ FORMATTER_METHOD_FETCH_OBJECT; - loc = unum_getLocaleByType(FORMATTER_OBJECT(nfo), static_cast(type), &INTL_DATA_ERROR_CODE(nfo)); + loc = unum_getLocaleByType(FORMATTER_UNUM(nfo), static_cast(type), &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Error getting locale" ); RETURN_STRING(loc); } diff --git a/ext/intl/formatter/formatter_class.cpp b/ext/intl/formatter/formatter_class.cpp index 82843ba90911..5b82b53c6d62 100644 --- a/ext/intl/formatter/formatter_class.cpp +++ b/ext/intl/formatter/formatter_class.cpp @@ -12,8 +12,6 @@ +----------------------------------------------------------------------+ */ -#include - #include "formatter_class.h" extern "C" { #include "php_intl.h" @@ -64,7 +62,7 @@ U_CFUNC zend_object *NumberFormatter_object_create(zend_class_entry *ce) /* {{{ NumberFormatter_object_clone */ U_CFUNC zend_object *NumberFormatter_object_clone(zend_object *object) { - NumberFormatter_object *nfo = php_intl_number_format_fetch_object(object); + const NumberFormatter_object *nfo = php_intl_number_format_fetch_object(object); zend_object *new_obj = NumberFormatter_ce_ptr->create_object(object->ce); NumberFormatter_object *new_nfo = php_intl_number_format_fetch_object(new_obj); @@ -72,10 +70,9 @@ U_CFUNC zend_object *NumberFormatter_object_clone(zend_object *object) zend_objects_clone_members(&new_nfo->zo, &nfo->zo); /* clone formatter object. It may fail, the destruction code must handle this case */ - if (FORMATTER_OBJECT(nfo) != NULL) { - UErrorCode error = U_ZERO_ERROR; - FORMATTER_OBJECT(new_nfo) = unum_clone(FORMATTER_OBJECT(nfo), &error); - if (U_FAILURE(error)) { + if (FORMATTER_OBJECT(nfo) != nullptr) { + FORMATTER_OBJECT(new_nfo) = FORMATTER_OBJECT(nfo)->clone(); + if (FORMATTER_OBJECT(new_nfo) == nullptr) { zend_throw_error(NULL, "Failed to clone NumberFormatter"); } } else { @@ -101,7 +98,7 @@ U_CFUNC void formatter_register_class( void ) memcpy(&NumberFormatter_handlers, &std_object_handlers, sizeof(NumberFormatter_handlers)); - NumberFormatter_handlers.offset = XtOffsetOf(NumberFormatter_object, zo); + NumberFormatter_handlers.offset = offsetof(NumberFormatter_object, zo); NumberFormatter_handlers.clone_obj = NumberFormatter_object_clone; NumberFormatter_handlers.free_obj = NumberFormatter_object_free; } diff --git a/ext/intl/formatter/formatter_class.h b/ext/intl/formatter/formatter_class.h index 1d3393fdd522..44bdfae8debe 100644 --- a/ext/intl/formatter/formatter_class.h +++ b/ext/intl/formatter/formatter_class.h @@ -15,7 +15,7 @@ #ifndef FORMATTER_CLASS_H #define FORMATTER_CLASS_H -#include +#include "formatter_data.h" #ifdef __cplusplus extern "C" { @@ -23,7 +23,6 @@ extern "C" { #include "intl_common.h" #include "intl_error.h" #include "intl_data.h" -#include "formatter_data.h" #ifdef __cplusplus } #endif @@ -33,9 +32,7 @@ typedef struct { zend_object zo; } NumberFormatter_object; -static inline NumberFormatter_object *php_intl_number_format_fetch_object(zend_object *obj) { - return (NumberFormatter_object *)((char*)(obj) - XtOffsetOf(NumberFormatter_object, zo)); -} +#define php_intl_number_format_fetch_object(obj) ZEND_CONTAINER_OF(obj, NumberFormatter_object, zo) #define Z_INTL_NUMBERFORMATTER_P(zv) php_intl_number_format_fetch_object(Z_OBJ_P(zv)) #ifdef __cplusplus diff --git a/ext/intl/formatter/formatter_data.cpp b/ext/intl/formatter/formatter_data.cpp index 593d746f1006..506972e72088 100644 --- a/ext/intl/formatter/formatter_data.cpp +++ b/ext/intl/formatter/formatter_data.cpp @@ -26,7 +26,7 @@ void formatter_data_init( formatter_data* nf_data ) if( !nf_data ) return; - nf_data->unum = NULL; + nf_data->unum = nullptr; intl_error_reset( &nf_data->error ); } /* }}} */ @@ -39,10 +39,8 @@ void formatter_data_free( formatter_data* nf_data ) if( !nf_data ) return; - if( nf_data->unum ) - unum_close( nf_data->unum ); - - nf_data->unum = NULL; + delete nf_data->unum; + nf_data->unum = nullptr; intl_error_reset( &nf_data->error ); } /* }}} */ diff --git a/ext/intl/formatter/formatter_data.h b/ext/intl/formatter/formatter_data.h index 183682bee6ae..1208970471bf 100644 --- a/ext/intl/formatter/formatter_data.h +++ b/ext/intl/formatter/formatter_data.h @@ -15,9 +15,21 @@ #ifndef FORMATTER_DATA_H #define FORMATTER_DATA_H +#ifdef __cplusplus +extern "C" { +#endif #include +#ifdef __cplusplus +} +#endif + +#include -#include +#ifdef __cplusplus +using icu::NumberFormat; +#else +typedef void NumberFormat; +#endif #ifdef __cplusplus extern "C" { @@ -32,7 +44,7 @@ typedef struct { intl_error error; // formatter handling - UNumberFormat* unum; + NumberFormat* unum; } formatter_data; #ifdef __cplusplus diff --git a/ext/intl/formatter/formatter_format.cpp b/ext/intl/formatter/formatter_format.cpp index 6b2651e4ead2..48edabc8ccde 100644 --- a/ext/intl/formatter/formatter_format.cpp +++ b/ext/intl/formatter/formatter_format.cpp @@ -16,24 +16,21 @@ #include #endif +#include +#include +#include "../intl_convertcpp.h" +#include "formatter_class.h" +#include "formatter_format.h" + extern "C" { #include "php_intl.h" -#include "intl_convert.h" } -#include - -#include "formatter_class.h" -#include "formatter_format.h" - /* {{{ Format a number. */ U_CFUNC PHP_FUNCTION( numfmt_format ) { zval *number; zend_long type = FORMAT_TYPE_DEFAULT; - UChar format_buf[32]; - UChar* formatted = format_buf; - int32_t formatted_len = USIZE(format_buf); FORMATTER_METHOD_INIT_VARS; /* Parse parameters. */ @@ -59,50 +56,27 @@ U_CFUNC PHP_FUNCTION( numfmt_format ) } } + icu::UnicodeString result; + icu::FieldPosition pos; + switch(type) { case FORMAT_TYPE_INT32: convert_to_long(number); - formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number), - formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo)); - if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) { - intl_error_reset(INTL_DATA_ERROR_P(nfo)); - formatted = eumalloc(formatted_len); - formatted_len = unum_format(FORMATTER_OBJECT(nfo), (int32_t)Z_LVAL_P(number), - formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo)); - if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) { - efree(formatted); - } - } + FORMATTER_OBJECT(nfo)->format((int32_t)Z_LVAL_P(number), result, pos, INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" ); break; case FORMAT_TYPE_INT64: { int64_t value = (Z_TYPE_P(number) == IS_DOUBLE)?(int64_t)Z_DVAL_P(number):Z_LVAL_P(number); - formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo)); - if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) { - intl_error_reset(INTL_DATA_ERROR_P(nfo)); - formatted = eumalloc(formatted_len); - formatted_len = unum_formatInt64(FORMATTER_OBJECT(nfo), value, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo)); - if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) { - efree(formatted); - } - } + FORMATTER_OBJECT(nfo)->format(value, result, pos, INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" ); } break; case FORMAT_TYPE_DOUBLE: convert_to_double(number); - formatted_len = unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo)); - if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) { - intl_error_reset(INTL_DATA_ERROR_P(nfo)); - formatted = eumalloc(formatted_len); - unum_formatDouble(FORMATTER_OBJECT(nfo), Z_DVAL_P(number), formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo)); - if (U_FAILURE( INTL_DATA_ERROR_CODE(nfo) ) ) { - efree(formatted); - } - } + FORMATTER_OBJECT(nfo)->format(Z_DVAL_P(number), result, pos, INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Number formatting failed" ); break; case FORMAT_TYPE_CURRENCY: @@ -120,7 +94,9 @@ U_CFUNC PHP_FUNCTION( numfmt_format ) RETURN_THROWS(); } - INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) ); + zend_string *u8str = intl_charFromString(result, &INTL_DATA_ERROR_CODE(nfo)); + INTL_METHOD_CHECK_STATUS(nfo, "Error converting result to UTF-8"); + RETVAL_STR(u8str); } /* }}} */ @@ -128,13 +104,8 @@ U_CFUNC PHP_FUNCTION( numfmt_format ) U_CFUNC PHP_FUNCTION( numfmt_format_currency ) { double number; - UChar format_buf[32]; - UChar* formatted = format_buf; - int32_t formatted_len = USIZE(format_buf); char* currency = NULL; size_t currency_len = 0; - UChar* scurrency = NULL; - int32_t scurrency_len = 0; FORMATTER_METHOD_INIT_VARS; /* Parse parameters. */ @@ -148,36 +119,31 @@ U_CFUNC PHP_FUNCTION( numfmt_format_currency ) FORMATTER_METHOD_FETCH_OBJECT; /* Convert currency to UTF-16. */ - intl_convert_utf8_to_utf16(&scurrency, &scurrency_len, currency, currency_len, &INTL_DATA_ERROR_CODE(nfo)); + icu::UnicodeString ucurrency; + intl_stringFromChar(ucurrency, currency, currency_len, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-16 failed" ); - /* Format the number using a fixed-length buffer. */ - formatted_len = unum_formatDoubleCurrency(FORMATTER_OBJECT(nfo), number, scurrency, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo)); - - /* If the buffer turned out to be too small - * then allocate another buffer dynamically - * and use it to format the number. - */ - if (INTL_DATA_ERROR_CODE(nfo) == U_BUFFER_OVERFLOW_ERROR) { - intl_error_reset(INTL_DATA_ERROR_P(nfo)); - formatted = eumalloc(formatted_len); - unum_formatDoubleCurrency(FORMATTER_OBJECT(nfo), number, scurrency, formatted, formatted_len, NULL, &INTL_DATA_ERROR_CODE(nfo)); + /* Format using CurrencyAmount. */ + icu::CurrencyAmount *currAmt = new icu::CurrencyAmount(number, ucurrency.getTerminatedBuffer(), INTL_DATA_ERROR_CODE(nfo)); + if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) { + delete currAmt; + intl_errors_set_custom_msg(INTL_DATA_ERROR_P(nfo), "Number formatting failed"); + RETURN_FALSE; } - - if( U_FAILURE( INTL_DATA_ERROR_CODE((nfo)) ) ) { - intl_error_set_code( NULL, INTL_DATA_ERROR_CODE((nfo)) ); - intl_errors_set_custom_msg( INTL_DATA_ERROR_P(nfo), "Number formatting failed"); - RETVAL_FALSE; - if (formatted != format_buf) { - efree(formatted); - } - } else { - INTL_METHOD_RETVAL_UTF8( nfo, formatted, formatted_len, ( formatted != format_buf ) ); + icu::Formattable fmt; + fmt.adoptObject(currAmt); + icu::UnicodeString result; + icu::FieldPosition pos; + FORMATTER_OBJECT(nfo)->format(fmt, result, pos, INTL_DATA_ERROR_CODE(nfo)); + + if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) { + intl_errors_set_custom_msg(INTL_DATA_ERROR_P(nfo), "Number formatting failed"); + RETURN_FALSE; } - if(scurrency) { - efree(scurrency); - } + zend_string *u8str = intl_charFromString(result, &INTL_DATA_ERROR_CODE(nfo)); + INTL_METHOD_CHECK_STATUS(nfo, "Error converting result to UTF-8"); + RETVAL_STR(u8str); } /* }}} */ diff --git a/ext/intl/formatter/formatter_main.cpp b/ext/intl/formatter/formatter_main.cpp index 31c4cdcc485d..79750714a5f0 100644 --- a/ext/intl/formatter/formatter_main.cpp +++ b/ext/intl/formatter/formatter_main.cpp @@ -16,14 +16,17 @@ #include #endif -#include -#include +#include +#include +#include +#include +#include +#include "../intl_convertcpp.h" +#include "formatter_class.h" extern "C" { #include "php_intl.h" -#include "intl_convert.h" } -#include "formatter_class.h" /* {{{ */ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS) @@ -32,8 +35,7 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS) char* pattern = NULL; size_t locale_len = 0, pattern_len = 0; zend_long style; - UChar* spattern = NULL; - int32_t spattern_len = 0; + UnicodeString upattern; FORMATTER_METHOD_INIT_VARS; ZEND_PARSE_PARAMETERS_START(2, 3) @@ -53,7 +55,7 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS) /* Convert pattern (if specified) to UTF-16. */ if(pattern && pattern_len) { - intl_convert_utf8_to_utf16(&spattern, &spattern_len, pattern, pattern_len, &INTL_DATA_ERROR_CODE(nfo)); + intl_stringFromChar(upattern, pattern, pattern_len, &INTL_DATA_ERROR_CODE(nfo)); INTL_CTOR_CHECK_STATUS(nfo, "error converting pattern to UTF-16"); } @@ -61,7 +63,7 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS) locale = (char *)intl_locale_get_default(); } - if (strlen(uloc_getISO3Language(locale)) == 0) { + if (icu::Locale(locale).getISO3Language()[0] == '\0') { zend_argument_value_error(1, "\"%s\" is invalid", locale); return FAILURE; } @@ -70,12 +72,55 @@ static int numfmt_ctor(INTERNAL_FUNCTION_PARAMETERS) const char* final_locale = canonicalized_locale ? canonicalized_locale : locale; /* Create an ICU number formatter. */ - FORMATTER_OBJECT(nfo) = unum_open(static_cast(style), spattern, spattern_len, final_locale, nullptr, &INTL_DATA_ERROR_CODE(nfo)); - - if (spattern) { - efree(spattern); + icu::Locale loc(final_locale); + switch (style) { + case UNUM_PATTERN_DECIMAL: { + icu::DecimalFormatSymbols *syms = new icu::DecimalFormatSymbols(loc, INTL_DATA_ERROR_CODE(nfo)); + if (U_FAILURE(INTL_DATA_ERROR_CODE(nfo))) { + delete syms; + break; + } + FORMATTER_OBJECT(nfo) = new icu::DecimalFormat(upattern, syms, INTL_DATA_ERROR_CODE(nfo)); + if (FORMATTER_OBJECT(nfo) == nullptr) { + delete syms; + } + break; + } + case UNUM_PATTERN_RULEBASED: { + UParseError parseError; + FORMATTER_OBJECT(nfo) = new icu::RuleBasedNumberFormat(upattern, loc, parseError, INTL_DATA_ERROR_CODE(nfo)); + break; + } + case UNUM_SPELLOUT: + FORMATTER_OBJECT(nfo) = new icu::RuleBasedNumberFormat(icu::URBNF_SPELLOUT, loc, INTL_DATA_ERROR_CODE(nfo)); + break; + case UNUM_ORDINAL: + FORMATTER_OBJECT(nfo) = new icu::RuleBasedNumberFormat(icu::URBNF_ORDINAL, loc, INTL_DATA_ERROR_CODE(nfo)); + break; + case UNUM_DURATION: + FORMATTER_OBJECT(nfo) = new icu::RuleBasedNumberFormat(icu::URBNF_DURATION, loc, INTL_DATA_ERROR_CODE(nfo)); + break; + case UNUM_NUMBERING_SYSTEM: { + UErrorCode localErr = U_ZERO_ERROR; + int32_t keywordLength = loc.getKeywordValue("numbers", nullptr, 0, localErr); + if (keywordLength > 0) { + FORMATTER_OBJECT(nfo) = NumberFormat::createInstance(loc, UNUM_DEFAULT, INTL_DATA_ERROR_CODE(nfo)); + } else { + FORMATTER_OBJECT(nfo) = new icu::RuleBasedNumberFormat(icu::URBNF_NUMBERING_SYSTEM, loc, INTL_DATA_ERROR_CODE(nfo)); + } + break; + } + case UNUM_DECIMAL_COMPACT_SHORT: + FORMATTER_OBJECT(nfo) = icu::CompactDecimalFormat::createInstance(loc, UNUM_SHORT, INTL_DATA_ERROR_CODE(nfo)); + break; + case UNUM_DECIMAL_COMPACT_LONG: + FORMATTER_OBJECT(nfo) = icu::CompactDecimalFormat::createInstance(loc, UNUM_LONG, INTL_DATA_ERROR_CODE(nfo)); + break; + default: + FORMATTER_OBJECT(nfo) = NumberFormat::createInstance(loc, static_cast(style), INTL_DATA_ERROR_CODE(nfo)); + break; } - + if (canonicalized_locale) { efree(canonicalized_locale); } diff --git a/ext/intl/formatter/formatter_parse.cpp b/ext/intl/formatter/formatter_parse.cpp index b2dc2e8dcbdf..7bbc461516bc 100644 --- a/ext/intl/formatter/formatter_parse.cpp +++ b/ext/intl/formatter/formatter_parse.cpp @@ -16,16 +16,18 @@ #include #endif +#include +#include +#include "../intl_convertcpp.h" +#include "formatter_class.h" +#include "formatter_format.h" + extern "C" { #include "php_intl.h" -#include "intl_convert.h" } -#include #include - -#include "formatter_class.h" -#include "formatter_format.h" +#include #define ICU_LOCALE_BUG 1 @@ -33,14 +35,9 @@ extern "C" { U_CFUNC PHP_FUNCTION( numfmt_parse ) { zend_long type = FORMAT_TYPE_DOUBLE; - UChar* sstr = NULL; - int32_t sstr_len = 0; char* str = NULL; size_t str_len; - int32_t val32, position = 0; - int64_t val64; - double val_double; - int32_t* position_p = NULL; + int32_t position = 0; zval *zposition = NULL; char *oldlocale; FORMATTER_METHOD_INIT_VARS; @@ -54,14 +51,14 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) if (zposition) { position = (int32_t) zval_get_long(zposition); - position_p = &position; } /* Fetch the object. */ FORMATTER_METHOD_FETCH_OBJECT; /* Convert given string to UTF-16. */ - intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo)); + icu::UnicodeString ustr; + intl_stringFromChar(ustr, str, str_len, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" ); #if ICU_LOCALE_BUG && defined(LC_NUMERIC) @@ -72,21 +69,38 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) switch(type) { case FORMAT_TYPE_INT32: - val32 = unum_parse(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo)); - RETVAL_LONG(val32); - break; case FORMAT_TYPE_INT64: - val64 = unum_parseInt64(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo)); - if(val64 > ZEND_LONG_MAX || val64 < ZEND_LONG_MIN) { - RETVAL_DOUBLE(val64); + case FORMAT_TYPE_DOUBLE: + { + icu::Formattable result; + icu::ParsePosition pp(position); + FORMATTER_OBJECT(nfo)->parse(ustr, result, pp); + + if (pp.getErrorIndex() >= 0) { + INTL_DATA_ERROR_CODE(nfo) = U_PARSE_ERROR; } else { - RETVAL_LONG((zend_long)val64); + position = pp.getIndex(); + switch(type) { + case FORMAT_TYPE_INT32: + RETVAL_LONG(result.getLong(INTL_DATA_ERROR_CODE(nfo))); + break; + case FORMAT_TYPE_INT64: + { + int64_t val64 = result.getInt64(INTL_DATA_ERROR_CODE(nfo)); + if(val64 > ZEND_LONG_MAX || val64 < ZEND_LONG_MIN) { + RETVAL_DOUBLE(val64); + } else { + RETVAL_LONG((zend_long)val64); + } + break; + } + case FORMAT_TYPE_DOUBLE: + RETVAL_DOUBLE(result.getDouble(INTL_DATA_ERROR_CODE(nfo))); + break; + } } break; - case FORMAT_TYPE_DOUBLE: - val_double = unum_parseDouble(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, &INTL_DATA_ERROR_CODE(nfo)); - RETVAL_DOUBLE(val_double); - break; + } case FORMAT_TYPE_CURRENCY: if (hasThis()) { const char *space; @@ -113,10 +127,6 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) efree(oldlocale); #endif - if (sstr) { - efree(sstr); - } - INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" ); } /* }}} */ @@ -124,14 +134,8 @@ U_CFUNC PHP_FUNCTION( numfmt_parse ) /* {{{ Parse a number as currency. */ U_CFUNC PHP_FUNCTION( numfmt_parse_currency ) { - double number; - UChar currency[5] = {0}; - UChar* sstr = NULL; - int32_t sstr_len = 0; - zend_string *u8str; char *str; size_t str_len; - int32_t* position_p = NULL; int32_t position = 0; zval *zcurrency, *zposition = NULL; FORMATTER_METHOD_INIT_VARS; @@ -147,27 +151,34 @@ U_CFUNC PHP_FUNCTION( numfmt_parse_currency ) FORMATTER_METHOD_FETCH_OBJECT; /* Convert given string to UTF-16. */ - intl_convert_utf8_to_utf16(&sstr, &sstr_len, str, str_len, &INTL_DATA_ERROR_CODE(nfo)); + icu::UnicodeString ustr; + intl_stringFromChar(ustr, str, str_len, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "String conversion to UTF-16 failed" ); if(zposition) { position = (int32_t) zval_get_long(zposition); - position_p = &position; } - number = unum_parseDoubleCurrency(FORMATTER_OBJECT(nfo), sstr, sstr_len, position_p, currency, &INTL_DATA_ERROR_CODE(nfo)); - if(zposition) { - ZEND_TRY_ASSIGN_REF_LONG(zposition, position); + icu::ParsePosition pp(position); + std::unique_ptr currAmt(FORMATTER_OBJECT(nfo)->parseCurrency(ustr, pp)); + + if (currAmt == nullptr || pp.getErrorIndex() >= 0) { + INTL_DATA_ERROR_CODE(nfo) = U_PARSE_ERROR; + INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" ); } - if (sstr) { - efree(sstr); + + if(zposition) { + ZEND_TRY_ASSIGN_REF_LONG(zposition, pp.getIndex()); } - INTL_METHOD_CHECK_STATUS( nfo, "Number parsing failed" ); + + double number = currAmt->getNumber().getDouble(INTL_DATA_ERROR_CODE(nfo)); /* Convert parsed currency to UTF-8 and pass it back to caller. */ - u8str = intl_convert_utf16_to_utf8(currency, u_strlen(currency), &INTL_DATA_ERROR_CODE(nfo)); + icu::UnicodeString ucurrency(currAmt->getISOCurrency()); + + zend_string *u8str = intl_charFromString(ucurrency, &INTL_DATA_ERROR_CODE(nfo)); INTL_METHOD_CHECK_STATUS( nfo, "Currency conversion to UTF-8 failed" ); - ZEND_TRY_ASSIGN_REF_NEW_STR(zcurrency, u8str); + ZEND_TRY_ASSIGN_REF_STR(zcurrency, u8str); RETVAL_DOUBLE( number ); } diff --git a/ext/intl/intl_convertcpp.cpp b/ext/intl/intl_convertcpp.cpp index cd7614b3a6c6..70d28e5c23ca 100644 --- a/ext/intl/intl_convertcpp.cpp +++ b/ext/intl/intl_convertcpp.cpp @@ -72,7 +72,7 @@ zend_string* intl_charFromString(const UnicodeString &from, UErrorCode *status) const UChar *utf16buf = from.getBuffer(); int32_t actual_len; - u_strToUTF8WithSub(ZSTR_VAL(u8res), capacity, &actual_len, utf16buf, from.length(), + u_strToUTF8WithSub(ZSTR_VAL(u8res), capacity + 1, &actual_len, utf16buf, from.length(), U_SENTINEL, NULL, status); if (U_FAILURE(*status)) { diff --git a/ext/intl/listformatter/listformatter_class.cpp b/ext/intl/listformatter/listformatter_class.cpp index d50374393cce..950eb81b7e15 100644 --- a/ext/intl/listformatter/listformatter_class.cpp +++ b/ext/intl/listformatter/listformatter_class.cpp @@ -14,22 +14,27 @@ extern "C" { #include "php.h" +} + +#include +#include +#include "../intl_convertcpp.h" + +extern "C" { #include "php_intl.h" -#include "intl_convert.h" } -#include #include "listformatter_class.h" #include "listformatter_arginfo.h" +#include + static zend_object_handlers listformatter_handlers; static void listformatter_free_obj(zend_object *object) { ListFormatter_object *obj = php_intl_listformatter_fetch_object(object); - if( obj->lf_data.ulistfmt ) - ulistfmt_close( obj->lf_data.ulistfmt ); - + delete obj->lf_data.ulistfmt; obj->lf_data.ulistfmt = nullptr; intl_error_reset( &obj->lf_data.error ); @@ -78,7 +83,7 @@ PHP_METHOD(IntlListFormatter, __construct) RETURN_THROWS(); } - if (strlen(uloc_getISO3Language(locale)) == 0) { + if (icu::Locale(locale).getISO3Language()[0] == '\0') { zend_argument_value_error(1, "\"%s\" is invalid", locale); RETURN_THROWS(); } @@ -89,13 +94,13 @@ PHP_METHOD(IntlListFormatter, __construct) zend_argument_value_error(2, "must be one of IntlListFormatter::TYPE_AND, IntlListFormatter::TYPE_OR, or IntlListFormatter::TYPE_UNITS"); RETURN_THROWS(); } - + if (width != ULISTFMT_WIDTH_WIDE && width != ULISTFMT_WIDTH_SHORT && width != ULISTFMT_WIDTH_NARROW) { zend_argument_value_error(3, "must be one of IntlListFormatter::WIDTH_WIDE, IntlListFormatter::WIDTH_SHORT, or IntlListFormatter::WIDTH_NARROW"); RETURN_THROWS(); } - LISTFORMATTER_OBJECT(obj) = ulistfmt_openForType(locale, static_cast(type), static_cast(width), &status); + LISTFORMATTER_OBJECT(obj) = ListFormatter::createInstance(icu::Locale(locale), static_cast(type), static_cast(width), status); #else if (type != INTL_LISTFORMATTER_FALLBACK_TYPE_AND) { zend_argument_value_error(2, "contains an unsupported type. ICU 66 and below only support IntlListFormatter::TYPE_AND"); @@ -107,7 +112,7 @@ PHP_METHOD(IntlListFormatter, __construct) RETURN_THROWS(); } - LISTFORMATTER_OBJECT(obj) = ulistfmt_open(locale, &status); + LISTFORMATTER_OBJECT(obj) = ListFormatter::createInstance(icu::Locale(locale), status); #endif if (U_FAILURE(status)) { @@ -126,89 +131,54 @@ PHP_METHOD(IntlListFormatter, format) Z_PARAM_ARRAY_HT(ht) ZEND_PARSE_PARAMETERS_END(); + intl_errors_reset(LISTFORMATTER_ERROR_P(obj)); + uint32_t count = zend_hash_num_elements(ht); if (count == 0) { RETURN_EMPTY_STRING(); } - const UChar **items = (const UChar **)safe_emalloc(count, sizeof(const UChar *), 0); - int32_t *itemLengths = (int32_t *)safe_emalloc(count, sizeof(int32_t), 0); + std::unique_ptr items(new UnicodeString[count]); uint32_t i = 0; zval *val; ZEND_HASH_FOREACH_VAL(ht, val) { zend_string *str_val, *tmp_str; - - str_val = zval_get_tmp_string(val, &tmp_str); - - // Convert PHP string to UTF-16 - UChar *ustr = nullptr; - int32_t ustr_len = 0; - UErrorCode status = U_ZERO_ERROR; - - intl_convert_utf8_to_utf16(&ustr, &ustr_len, ZSTR_VAL(str_val), ZSTR_LEN(str_val), &status); + UErrorCode conv_status = U_ZERO_ERROR; + + str_val = zval_try_get_tmp_string(val, &tmp_str); + if (UNEXPECTED(!str_val)) { + RETURN_THROWS(); + } + intl_stringFromChar(items[i], ZSTR_VAL(str_val), ZSTR_LEN(str_val), &conv_status); zend_tmp_string_release(tmp_str); - if (U_FAILURE(status)) { - // We can't use goto cleanup because items and itemLengths are incompletely allocated - for (uint32_t j = 0; j < i; j++) { - efree((void *)items[j]); - } - efree(items); - efree(itemLengths); - intl_error_set(nullptr, status, "Failed to convert string to UTF-16"); + if (U_FAILURE(conv_status)) { + intl_errors_set(LISTFORMATTER_ERROR_P(obj), conv_status, "Failed to convert string to UTF-16"); RETURN_FALSE; } - - items[i] = ustr; - itemLengths[i] = ustr_len; + i++; } ZEND_HASH_FOREACH_END(); UErrorCode status = U_ZERO_ERROR; - int32_t resultLength; - UChar *result = nullptr; - zend_string *ret = nullptr; - - resultLength = ulistfmt_format(LISTFORMATTER_OBJECT(obj), items, itemLengths, count, nullptr, 0, &status); - - if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) { - intl_error_set(nullptr, status, "Failed to format list"); - RETVAL_FALSE; - goto cleanup; - } + UnicodeString result; - // Allocate buffer and try again - status = U_ZERO_ERROR; - result = (UChar *)safe_emalloc(resultLength + 1, sizeof(UChar), 0); - ulistfmt_format(LISTFORMATTER_OBJECT(obj), items, itemLengths, count, result, resultLength, &status); + LISTFORMATTER_OBJECT(obj)->format(items.get(), count, result, status); if (U_FAILURE(status)) { - if (result) { - efree(result); - } - intl_error_set(nullptr, status, "Failed to format list"); - RETVAL_FALSE; - goto cleanup; + intl_errors_set(LISTFORMATTER_ERROR_P(obj), status, "Failed to format list"); + RETURN_FALSE; } - // Convert result back to UTF-8 - ret = intl_convert_utf16_to_utf8(result, resultLength, &status); - efree(result); - + zend_string *ret = intl_charFromString(result, &status); + if (!ret) { - intl_error_set(nullptr, status, "Failed to convert result to UTF-8"); - RETVAL_FALSE; - } else { - RETVAL_NEW_STR(ret); + intl_errors_set(LISTFORMATTER_ERROR_P(obj), status, "Failed to convert result to UTF-8"); + RETURN_FALSE; } -cleanup: - for (i = 0; i < count; i++) { - efree((void *)items[i]); - } - efree(items); - efree(itemLengths); + RETVAL_STR(ret); } PHP_METHOD(IntlListFormatter, getErrorCode) @@ -236,9 +206,9 @@ void listformatter_register_class(void) { zend_class_entry *class_entry = register_class_IntlListFormatter(); class_entry->create_object = listformatter_create_object; - + memcpy(&listformatter_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - listformatter_handlers.offset = XtOffsetOf(ListFormatter_object, zo); + listformatter_handlers.offset = offsetof(ListFormatter_object, zo); listformatter_handlers.free_obj = listformatter_free_obj; listformatter_handlers.clone_obj = nullptr; } diff --git a/ext/intl/listformatter/listformatter_class.h b/ext/intl/listformatter/listformatter_class.h index 6aa43740604e..7e7fd29207fc 100644 --- a/ext/intl/listformatter/listformatter_class.h +++ b/ext/intl/listformatter/listformatter_class.h @@ -21,14 +21,20 @@ #include "intl_error.h" #include "intl_data.h" -#include +#include + +#ifdef __cplusplus +using icu::ListFormatter; +#else +typedef void ListFormatter; +#endif typedef struct { // error handling intl_error error; // formatter handling - UListFormatter* ulistfmt; + ListFormatter* ulistfmt; } listformatter_data; typedef struct { @@ -36,9 +42,7 @@ typedef struct { zend_object zo; } ListFormatter_object; -static inline ListFormatter_object *php_intl_listformatter_fetch_object(zend_object *obj) { - return (ListFormatter_object *)((char*)(obj) - XtOffsetOf(ListFormatter_object, zo)); -} +#define php_intl_listformatter_fetch_object(obj) ZEND_CONTAINER_OF(obj, ListFormatter_object, zo) #define Z_INTL_LISTFORMATTER_P(zv) php_intl_listformatter_fetch_object(Z_OBJ_P(zv)) #define LISTFORMATTER_ERROR(lfo) (lfo)->lf_data.error diff --git a/ext/intl/locale/locale_methods.cpp b/ext/intl/locale/locale_methods.cpp index 6370b1282af8..1ee32a2f094e 100644 --- a/ext/intl/locale/locale_methods.cpp +++ b/ext/intl/locale/locale_methods.cpp @@ -457,7 +457,7 @@ static zend_string* get_icu_value_internal( const char* loc_name , const char* t efree( mod_loc_name); } - tag_value->len = strlen(tag_value->val); + tag_value->len = buflen; return tag_value; } /* }}} */ @@ -736,7 +736,7 @@ U_CFUNC PHP_FUNCTION( locale_get_keywords ) Z_PARAM_PATH(loc_name, loc_name_len) ZEND_PARSE_PARAMETERS_END(); - INTL_CHECK_LOCALE_LEN(strlen(loc_name)); + INTL_CHECK_LOCALE_LEN(loc_name_len); if(loc_name_len == 0) { loc_name = (char *)intl_locale_get_default(); @@ -1127,7 +1127,7 @@ U_CFUNC PHP_FUNCTION(locale_parse) Z_PARAM_PATH(loc_name, loc_name_len) ZEND_PARSE_PARAMETERS_END(); - INTL_CHECK_LOCALE_LEN(strlen(loc_name)); + INTL_CHECK_LOCALE_LEN(loc_name_len); if(loc_name_len == 0) { loc_name = (char *)intl_locale_get_default(); @@ -1217,7 +1217,7 @@ static int strToMatch(const char* str ,char *retstr) if( *str == '-' ){ *retstr = '_'; } else { - *retstr = tolower(*str); + *retstr = tolower((unsigned char)*str); } str++; retstr++; @@ -1318,7 +1318,7 @@ U_CFUNC PHP_FUNCTION(locale_filter_matches) if( token && (token==cur_lang_tag) ){ /* check if the char. after match is SEPARATOR */ - chrcheck = token + (strlen(cur_loc_range)); + chrcheck = token + can_loc_range->len; if( isIDSeparator(*chrcheck) || isKeywordSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){ efree( cur_lang_tag ); efree( cur_loc_range ); @@ -1350,14 +1350,14 @@ U_CFUNC PHP_FUNCTION(locale_filter_matches) } /* end of if isCanonical */ else{ /* Convert to lower case for case-insensitive comparison */ - cur_lang_tag = reinterpret_cast(ecalloc( 1, strlen(lang_tag ) + 1)); + cur_lang_tag = reinterpret_cast(ecalloc(1, lang_tag_len + 1)); result = strToMatch( lang_tag , cur_lang_tag); if( result == 0) { efree( cur_lang_tag ); RETURN_FALSE; } - cur_loc_range = reinterpret_cast(ecalloc( 1, strlen(loc_range ) + 1)); + cur_loc_range = reinterpret_cast(ecalloc(1, loc_range_len + 1)); result = strToMatch( loc_range , cur_loc_range ); if( result == 0) { efree( cur_lang_tag ); @@ -1370,7 +1370,7 @@ U_CFUNC PHP_FUNCTION(locale_filter_matches) if( token && (token==cur_lang_tag) ){ /* check if the char. after match is SEPARATOR */ - chrcheck = token + (strlen(cur_loc_range)); + chrcheck = token + loc_range_len; if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){ efree( cur_lang_tag ); efree( cur_loc_range ); diff --git a/ext/intl/msgformat/msgformat_class.cpp b/ext/intl/msgformat/msgformat_class.cpp index 06ba7fa6fa67..c4d74924b04a 100644 --- a/ext/intl/msgformat/msgformat_class.cpp +++ b/ext/intl/msgformat/msgformat_class.cpp @@ -58,7 +58,7 @@ U_CFUNC zend_object *MessageFormatter_object_create(zend_class_entry *ce) /* {{{ MessageFormatter_object_clone */ U_CFUNC zend_object *MessageFormatter_object_clone(zend_object *object) { - MessageFormatter_object *mfo = php_intl_messageformatter_fetch_object(object); + const MessageFormatter_object *mfo = php_intl_messageformatter_fetch_object(object); zend_object *new_obj = MessageFormatter_ce_ptr->create_object(object->ce); MessageFormatter_object *new_mfo = php_intl_messageformatter_fetch_object(new_obj); @@ -96,7 +96,7 @@ void msgformat_register_class( void ) memcpy(&MessageFormatter_handlers, &std_object_handlers, sizeof MessageFormatter_handlers); - MessageFormatter_handlers.offset = XtOffsetOf(MessageFormatter_object, zo); + MessageFormatter_handlers.offset = offsetof(MessageFormatter_object, zo); MessageFormatter_handlers.clone_obj = MessageFormatter_object_clone; MessageFormatter_handlers.free_obj = MessageFormatter_object_free; } diff --git a/ext/intl/msgformat/msgformat_class.h b/ext/intl/msgformat/msgformat_class.h index e80c75cf2e17..849c45c14729 100644 --- a/ext/intl/msgformat/msgformat_class.h +++ b/ext/intl/msgformat/msgformat_class.h @@ -30,10 +30,7 @@ typedef struct { zend_object zo; } MessageFormatter_object; - -static inline MessageFormatter_object *php_intl_messageformatter_fetch_object(zend_object *obj) { - return (MessageFormatter_object *)((char*)(obj) - XtOffsetOf(MessageFormatter_object, zo)); -} +#define php_intl_messageformatter_fetch_object(obj) ZEND_CONTAINER_OF(obj, MessageFormatter_object, zo) #define Z_INTL_MESSAGEFORMATTER_P(zv) php_intl_messageformatter_fetch_object(Z_OBJ_P(zv)) void msgformat_register_class( void ); diff --git a/ext/intl/msgformat/msgformat_helpers.cpp b/ext/intl/msgformat/msgformat_helpers.cpp index 399db1e8c0b0..12e05af57219 100644 --- a/ext/intl/msgformat/msgformat_helpers.cpp +++ b/ext/intl/msgformat/msgformat_helpers.cpp @@ -341,7 +341,7 @@ static void umsg_set_timezone(MessageFormatter_object *mfo, } if (used_tz == NULL) { - used_tz = timezone_process_timezone_argument(nullptr, nullptr, &err); + used_tz = timezone_process_timezone_argument(nullptr, nullptr, &err, 1); if (used_tz == NULL) { continue; } @@ -649,7 +649,7 @@ U_CFUNC void umsg_parse_helper(UMessageFormat *fmt, int *count, zval **args, UCh case Formattable::kInt64: aInt64 = fargs[i].getInt64(); - if(aInt64 > ZEND_LONG_MAX || aInt64 < -ZEND_LONG_MAX) { + if(aInt64 > ZEND_LONG_MAX || aInt64 < ZEND_LONG_MIN) { ZVAL_DOUBLE(&(*args)[i], (double)aInt64); } else { ZVAL_LONG(&(*args)[i], (zend_long)aInt64); diff --git a/ext/intl/rangeformatter/rangeformatter_class.cpp b/ext/intl/rangeformatter/rangeformatter_class.cpp index e80e3bc02079..2df252d1986b 100644 --- a/ext/intl/rangeformatter/rangeformatter_class.cpp +++ b/ext/intl/rangeformatter/rangeformatter_class.cpp @@ -22,6 +22,7 @@ extern "C" { #include #include #include +#include #include "../intl_convertcpp.h" extern "C" { @@ -30,7 +31,6 @@ extern "C" { #include "../intl_data.h" #include "rangeformatter_arginfo.h" #include "rangeformatter_class.h" - #include "intl_convert.h" } using icu::number::NumberRangeFormatter; @@ -60,6 +60,19 @@ zend_object *IntlNumberRangeFormatter_object_create(zend_class_entry *ce) return &intern->zo; } +static icu::Formattable rangeformatter_create_formattable(zval *number) +{ + icu::Formattable formattable; + + if (Z_TYPE_P(number) == IS_DOUBLE) { + formattable.setDouble(Z_DVAL_P(number)); + } else { + formattable.setInt64(static_cast(Z_LVAL_P(number))); + } + + return formattable; +} + U_CFUNC PHP_METHOD(IntlNumberRangeFormatter, __construct) { ZEND_PARSE_PARAMETERS_NONE(); @@ -91,7 +104,7 @@ U_CFUNC PHP_METHOD(IntlNumberRangeFormatter, createFromSkeleton) RETURN_THROWS(); } - if (strlen(uloc_getISO3Language(locale)) == 0) { + if (icu::Locale(locale).getISO3Language()[0] == '\0') { zend_argument_value_error(2, "\"%s\" is invalid", locale); RETURN_THROWS(); } @@ -154,8 +167,8 @@ U_CFUNC PHP_METHOD(IntlNumberRangeFormatter, format) UErrorCode error = U_ZERO_ERROR; - icu::Formattable start_formattable(Z_TYPE_P(start) == IS_DOUBLE ? Z_DVAL_P(start) : Z_LVAL_P(start)); - icu::Formattable end_formattable(Z_TYPE_P(end) == IS_DOUBLE ? Z_DVAL_P(end) : Z_LVAL_P(end)); + icu::Formattable start_formattable = rangeformatter_create_formattable(start); + icu::Formattable end_formattable = rangeformatter_create_formattable(end); UnicodeString result = RANGEFORMATTER_OBJECT(obj)->formatFormattableRange(start_formattable, end_formattable, error).toString(error); @@ -219,7 +232,7 @@ void rangeformatter_register_class(void) class_entry_IntlNumberRangeFormatter->create_object = IntlNumberRangeFormatter_object_create; memcpy(&rangeformatter_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - rangeformatter_handlers.offset = XtOffsetOf(IntlNumberRangeFormatter_object, zo); + rangeformatter_handlers.offset = offsetof(IntlNumberRangeFormatter_object, zo); rangeformatter_handlers.free_obj = IntlNumberRangeFormatter_object_free; rangeformatter_handlers.clone_obj = NULL; } diff --git a/ext/intl/rangeformatter/rangeformatter_class.h b/ext/intl/rangeformatter/rangeformatter_class.h index 6e911403be3f..494a6d09a05f 100644 --- a/ext/intl/rangeformatter/rangeformatter_class.h +++ b/ext/intl/rangeformatter/rangeformatter_class.h @@ -36,9 +36,7 @@ typedef struct { zend_object zo; } IntlNumberRangeFormatter_object; -static inline IntlNumberRangeFormatter_object *php_intl_numberrangeformatter_fetch_object(zend_object *obj) { - return (IntlNumberRangeFormatter_object *)((char*)(obj) - XtOffsetOf(IntlNumberRangeFormatter_object, zo)); -} +#define php_intl_numberrangeformatter_fetch_object(obj) ZEND_CONTAINER_OF(obj, IntlNumberRangeFormatter_object, zo) #define Z_INTL_RANGEFORMATTER_P(zv) php_intl_numberrangeformatter_fetch_object(Z_OBJ_P(zv)) diff --git a/ext/intl/resourcebundle/resourcebundle_class.cpp b/ext/intl/resourcebundle/resourcebundle_class.cpp index b6e62f2a4d4f..2a89b14cc6e5 100644 --- a/ext/intl/resourcebundle/resourcebundle_class.cpp +++ b/ext/intl/resourcebundle/resourcebundle_class.cpp @@ -220,12 +220,12 @@ static zval *resource_bundle_array_fetch( } if (!fallback && (INTL_DATA_ERROR_CODE(rb) == U_USING_FALLBACK_WARNING || INTL_DATA_ERROR_CODE(rb) == U_USING_DEFAULT_WARNING)) { - UErrorCode icuerror; + UErrorCode icuerror = U_ZERO_ERROR; const char * locale = ures_getLocaleByType( rb->me, ULOC_ACTUAL_LOCALE, &icuerror ); if (is_numeric) { - spprintf(&pbuf, 0, "Cannot load element %d without fallback from to %s", index, locale); + spprintf(&pbuf, 0, "Cannot load element %d without fallback to %s", index, locale); } else { - spprintf(&pbuf, 0, "Cannot load element '%s' without fallback from to %s", key, locale); + spprintf(&pbuf, 0, "Cannot load element '%s' without fallback to %s", key, locale); } intl_errors_set_custom_msg( INTL_DATA_ERROR_P(rb), pbuf); efree(pbuf); @@ -424,7 +424,7 @@ U_CFUNC void resourcebundle_register_class( void ) ResourceBundle_ce_ptr->get_iterator = resourcebundle_get_iterator; ResourceBundle_object_handlers = std_object_handlers; - ResourceBundle_object_handlers.offset = XtOffsetOf(ResourceBundle_object, zend); + ResourceBundle_object_handlers.offset = offsetof(ResourceBundle_object, zend); ResourceBundle_object_handlers.clone_obj = NULL; /* ICU ResourceBundle has no clone implementation */ ResourceBundle_object_handlers.free_obj = ResourceBundle_object_free; ResourceBundle_object_handlers.read_dimension = resourcebundle_array_get; diff --git a/ext/intl/resourcebundle/resourcebundle_class.h b/ext/intl/resourcebundle/resourcebundle_class.h index f21ac5690930..1ec2ac592222 100644 --- a/ext/intl/resourcebundle/resourcebundle_class.h +++ b/ext/intl/resourcebundle/resourcebundle_class.h @@ -36,7 +36,7 @@ typedef struct { } ResourceBundle_object; static inline ResourceBundle_object *php_intl_resourcebundle_fetch_object(zend_object *obj) { - return (ResourceBundle_object *)((char*)(obj) - XtOffsetOf(ResourceBundle_object, zend)); + return ZEND_CONTAINER_OF(obj, ResourceBundle_object, zend); } #define Z_INTL_RESOURCEBUNDLE_P(zv) php_intl_resourcebundle_fetch_object(Z_OBJ_P(zv)) diff --git a/ext/intl/spoofchecker/spoofchecker.stub.php b/ext/intl/spoofchecker/spoofchecker.stub.php index 0141252d478d..51a9c7d39074 100644 --- a/ext/intl/spoofchecker/spoofchecker.stub.php +++ b/ext/intl/spoofchecker/spoofchecker.stub.php @@ -19,7 +19,6 @@ class Spoofchecker public const int INVISIBLE = UNKNOWN; /** @cvalue USPOOF_CHAR_LIMIT */ public const int CHAR_LIMIT = UNKNOWN; -#if U_ICU_VERSION_MAJOR_NUM >= 58 /** @cvalue USPOOF_ASCII */ public const int ASCII = UNKNOWN; /** @cvalue USPOOF_HIGHLY_RESTRICTIVE */ @@ -34,7 +33,6 @@ class Spoofchecker public const int SINGLE_SCRIPT_RESTRICTIVE = UNKNOWN; /** @cvalue USPOOF_MIXED_NUMBERS */ public const int MIXED_NUMBERS = UNKNOWN; -#endif #if U_ICU_VERSION_MAJOR_NUM >= 62 /** @cvalue USPOOF_HIDDEN_OVERLAY */ public const int HIDDEN_OVERLAY = UNKNOWN; @@ -71,9 +69,7 @@ public function setAllowedLocales(string $locales): void {} /** @tentative-return-type */ public function setChecks(int $checks): void {} -#if U_ICU_VERSION_MAJOR_NUM >= 58 /** @tentative-return-type */ public function setRestrictionLevel(int $level): void {} -#endif public function setAllowedChars(string $pattern, int $patternOptions = 0): void {} } diff --git a/ext/intl/spoofchecker/spoofchecker_arginfo.h b/ext/intl/spoofchecker/spoofchecker_arginfo.h index 536798914428..8704065e479b 100644 --- a/ext/intl/spoofchecker/spoofchecker_arginfo.h +++ b/ext/intl/spoofchecker/spoofchecker_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit spoofchecker.stub.php instead. - * Stub hash: 4834be57a3f0cb74dbc4422e609846139f09f6cb */ + * Stub hash: 4634f8ef9157fb3670a2ddc5e3246340660fc68c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Spoofchecker___construct, 0, 0, 0) ZEND_END_ARG_INFO() @@ -23,11 +23,9 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Spoofchecker_set ZEND_ARG_TYPE_INFO(0, checks, IS_LONG, 0) ZEND_END_ARG_INFO() -#if U_ICU_VERSION_MAJOR_NUM >= 58 ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Spoofchecker_setRestrictionLevel, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, level, IS_LONG, 0) ZEND_END_ARG_INFO() -#endif ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Spoofchecker_setAllowedChars, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, pattern, IS_STRING, 0) @@ -39,9 +37,7 @@ ZEND_METHOD(Spoofchecker, isSuspicious); ZEND_METHOD(Spoofchecker, areConfusable); ZEND_METHOD(Spoofchecker, setAllowedLocales); ZEND_METHOD(Spoofchecker, setChecks); -#if U_ICU_VERSION_MAJOR_NUM >= 58 ZEND_METHOD(Spoofchecker, setRestrictionLevel); -#endif ZEND_METHOD(Spoofchecker, setAllowedChars); static const zend_function_entry class_Spoofchecker_methods[] = { @@ -50,9 +46,7 @@ static const zend_function_entry class_Spoofchecker_methods[] = { ZEND_ME(Spoofchecker, areConfusable, arginfo_class_Spoofchecker_areConfusable, ZEND_ACC_PUBLIC) ZEND_ME(Spoofchecker, setAllowedLocales, arginfo_class_Spoofchecker_setAllowedLocales, ZEND_ACC_PUBLIC) ZEND_ME(Spoofchecker, setChecks, arginfo_class_Spoofchecker_setChecks, ZEND_ACC_PUBLIC) -#if U_ICU_VERSION_MAJOR_NUM >= 58 ZEND_ME(Spoofchecker, setRestrictionLevel, arginfo_class_Spoofchecker_setRestrictionLevel, ZEND_ACC_PUBLIC) -#endif ZEND_ME(Spoofchecker, setAllowedChars, arginfo_class_Spoofchecker_setAllowedChars, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -105,7 +99,6 @@ static zend_class_entry *register_class_Spoofchecker(void) zend_string *const_CHAR_LIMIT_name = zend_string_init_interned("CHAR_LIMIT", sizeof("CHAR_LIMIT") - 1, true); zend_declare_typed_class_constant(class_entry, const_CHAR_LIMIT_name, &const_CHAR_LIMIT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release_ex(const_CHAR_LIMIT_name, true); -#if U_ICU_VERSION_MAJOR_NUM >= 58 zval const_ASCII_value; ZVAL_LONG(&const_ASCII_value, USPOOF_ASCII); @@ -148,7 +141,6 @@ static zend_class_entry *register_class_Spoofchecker(void) zend_string *const_MIXED_NUMBERS_name = zend_string_init_interned("MIXED_NUMBERS", sizeof("MIXED_NUMBERS") - 1, true); zend_declare_typed_class_constant(class_entry, const_MIXED_NUMBERS_name, &const_MIXED_NUMBERS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release_ex(const_MIXED_NUMBERS_name, true); -#endif #if U_ICU_VERSION_MAJOR_NUM >= 62 zval const_HIDDEN_OVERLAY_value; diff --git a/ext/intl/spoofchecker/spoofchecker_class.cpp b/ext/intl/spoofchecker/spoofchecker_class.cpp index e3bc04766610..6f072081fa78 100644 --- a/ext/intl/spoofchecker/spoofchecker_class.cpp +++ b/ext/intl/spoofchecker/spoofchecker_class.cpp @@ -63,7 +63,7 @@ U_CFUNC zend_object *Spoofchecker_object_create(zend_class_entry *ce) static zend_object *spoofchecker_clone_obj(zend_object *object) /* {{{ */ { - Spoofchecker_object *spoofchecker_orig = php_intl_spoofchecker_fetch_object(object); + const Spoofchecker_object *spoofchecker_orig = php_intl_spoofchecker_fetch_object(object); zend_object *new_obj_val = Spoofchecker_ce_ptr->create_object(object->ce); Spoofchecker_object *spoofchecker_new = php_intl_spoofchecker_fetch_object(new_obj_val); @@ -98,7 +98,7 @@ U_CFUNC void spoofchecker_register_Spoofchecker_class(void) memcpy(&Spoofchecker_handlers, &std_object_handlers, sizeof Spoofchecker_handlers); - Spoofchecker_handlers.offset = XtOffsetOf(Spoofchecker_object, zo); + Spoofchecker_handlers.offset = offsetof(Spoofchecker_object, zo); Spoofchecker_handlers.clone_obj = spoofchecker_clone_obj; Spoofchecker_handlers.free_obj = Spoofchecker_objects_free; } diff --git a/ext/intl/spoofchecker/spoofchecker_class.h b/ext/intl/spoofchecker/spoofchecker_class.h index 0b0244c69528..8871ca2a5b24 100644 --- a/ext/intl/spoofchecker/spoofchecker_class.h +++ b/ext/intl/spoofchecker/spoofchecker_class.h @@ -42,9 +42,7 @@ typedef struct { zend_object zo; } Spoofchecker_object; -static inline Spoofchecker_object *php_intl_spoofchecker_fetch_object(zend_object *obj) { - return (Spoofchecker_object *)((char*)(obj) - XtOffsetOf(Spoofchecker_object, zo)); -} +#define php_intl_spoofchecker_fetch_object(obj) ZEND_CONTAINER_OF(obj, Spoofchecker_object, zo) #define Z_INTL_SPOOFCHECKER_P(zv) php_intl_spoofchecker_fetch_object((Z_OBJ_P(zv))) #define SPOOFCHECKER_ERROR(co) (co)->err diff --git a/ext/intl/spoofchecker/spoofchecker_main.cpp b/ext/intl/spoofchecker/spoofchecker_main.cpp index e3e21ef72b79..73e14cabeaf1 100644 --- a/ext/intl/spoofchecker/spoofchecker_main.cpp +++ b/ext/intl/spoofchecker/spoofchecker_main.cpp @@ -142,7 +142,7 @@ U_CFUNC PHP_METHOD(Spoofchecker, setChecks) } /* }}} */ -#if U_ICU_VERSION_MAJOR_NUM >= 58 +/* TODO Document this method on PHP.net */ /* {{{ Set the loosest restriction level allowed for strings. */ U_CFUNC PHP_METHOD(Spoofchecker, setRestrictionLevel) { @@ -170,7 +170,6 @@ U_CFUNC PHP_METHOD(Spoofchecker, setRestrictionLevel) uspoof_setRestrictionLevel(co->uspoof, (URestrictionLevel)level); } /* }}} */ -#endif U_CFUNC PHP_METHOD(Spoofchecker, setAllowedChars) { @@ -214,9 +213,9 @@ U_CFUNC PHP_METHOD(Spoofchecker, setAllowedChars) #endif pattern_option != (USET_IGNORE_SPACE|USET_CASE_INSENSITIVE) && pattern_option != (USET_IGNORE_SPACE|USET_ADD_CASE_MAPPINGS)) { - zend_argument_value_error(2, "must be a valid pattern option, 0 or (SpoofChecker::IGNORE_SPACE|( or SpoofChecker::USET_CASE_INSENSITIVE or SpoofChecker::USET_ADD_CASE_MAPPINGS" + zend_argument_value_error(2, "must be a valid pattern option, 0 or (SpoofChecker::IGNORE_SPACE|( or SpoofChecker::CASE_INSENSITIVE or SpoofChecker::ADD_CASE_MAPPINGS" #if U_ICU_VERSION_MAJOR_NUM >= 73 - " or SpoofChecker::USET_SIMPLE_CASE_INSENSITIVE" + " or SpoofChecker::SIMPLE_CASE_INSENSITIVE" #endif "))" ); diff --git a/ext/intl/tests/breakiter_getLocale_error.phpt b/ext/intl/tests/breakiter_getLocale_error.phpt new file mode 100644 index 000000000000..07d57dbc4265 --- /dev/null +++ b/ext/intl/tests/breakiter_getLocale_error.phpt @@ -0,0 +1,25 @@ +--TEST-- +IntlBreakIterator::getLocale(): bad arguments +--EXTENSIONS-- +intl +--FILE-- +getLocale(2); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + $bi->getLocale(-1); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +ValueError: IntlBreakIterator::getLocale(): Argument #1 ($type) must be either Locale::ACTUAL_LOCALE or Locale::VALID_LOCALE +ValueError: IntlBreakIterator::getLocale(): Argument #1 ($type) must be either Locale::ACTUAL_LOCALE or Locale::VALID_LOCALE diff --git a/ext/intl/tests/bug75317.phpt b/ext/intl/tests/bug75317.phpt index 4df45f37bf80..a38fa2578b9c 100644 --- a/ext/intl/tests/bug75317.phpt +++ b/ext/intl/tests/bug75317.phpt @@ -29,7 +29,7 @@ var_dump($c->setSourceEncoding('utf-32')); printResult($c->getDestinationEncoding(), $utf8); printResult($c->getSourceEncoding(), $utf32); -// test invalid inputs dont change values +// test invalid inputs don't change values var_dump($c->setDestinationEncoding('foobar') === false); var_dump($c->setSourceEncoding('foobar') === false); printResult($c->getDestinationEncoding(), $utf8); diff --git a/ext/intl/tests/calendar_set_date_out_of_bounds.phpt b/ext/intl/tests/calendar_set_date_out_of_bounds.phpt new file mode 100644 index 000000000000..db9d18275ae8 --- /dev/null +++ b/ext/intl/tests/calendar_set_date_out_of_bounds.phpt @@ -0,0 +1,32 @@ +--TEST-- +IntlCalendar::setDate(): out-of-bounds arguments report correct positions +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +setDate(99999999999, 1, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + $cal->setDate(1, 99999999999, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + $cal->setDate(1, 1, 99999999999); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +IntlCalendar::setDate(): Argument #1 ($year) must be between -2147483648 and 2147483647 +IntlCalendar::setDate(): Argument #2 ($month) must be between -2147483648 and 2147483647 +IntlCalendar::setDate(): Argument #3 ($dayOfMonth) must be between -2147483648 and 2147483647 diff --git a/ext/intl/tests/calendar_set_date_time_out_of_bounds.phpt b/ext/intl/tests/calendar_set_date_time_out_of_bounds.phpt new file mode 100644 index 000000000000..798ab1ebb01f --- /dev/null +++ b/ext/intl/tests/calendar_set_date_time_out_of_bounds.phpt @@ -0,0 +1,53 @@ +--TEST-- +IntlCalendar::setDateTime(): out-of-bounds arguments report correct positions +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +setDateTime(99999999999, 1, 1, 1, 1, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + $cal->setDateTime(1, 99999999999, 1, 1, 1, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + $cal->setDateTime(1, 1, 99999999999, 1, 1, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + $cal->setDateTime(1, 1, 1, 99999999999, 1, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + $cal->setDateTime(1, 1, 1, 1, 99999999999, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + $cal->setDateTime(1, 1, 1, 1, 1, 99999999999); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +IntlCalendar::setDateTime(): Argument #1 ($year) must be between -2147483648 and 2147483647 +IntlCalendar::setDateTime(): Argument #2 ($month) must be between -2147483648 and 2147483647 +IntlCalendar::setDateTime(): Argument #3 ($dayOfMonth) must be between -2147483648 and 2147483647 +IntlCalendar::setDateTime(): Argument #4 ($hour) must be between -2147483648 and 2147483647 +IntlCalendar::setDateTime(): Argument #5 ($minute) must be between -2147483648 and 2147483647 +IntlCalendar::setDateTime(): Argument #6 ($second) must be between -2147483648 and 2147483647 diff --git a/ext/intl/tests/calendar_set_out_of_bounds.phpt b/ext/intl/tests/calendar_set_out_of_bounds.phpt new file mode 100644 index 000000000000..1ca407d3f713 --- /dev/null +++ b/ext/intl/tests/calendar_set_out_of_bounds.phpt @@ -0,0 +1,55 @@ +--TEST-- +IntlCalendar::set(): out-of-bounds date/time arguments report correct positions +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +set(99999999999, 1, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + intlcal_set($cal, 1, 99999999999, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + $cal->set(1, 1, 1, 99999999999, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + $cal->set(1, 1, 1, 1, 99999999999, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + intlcal_set($cal, 1, 1, 1, 1, 1, 99999999999); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECTF-- +Deprecated: Calling IntlCalendar::set() with more than 2 arguments is deprecated, use either IntlCalendar::setDate() or IntlCalendar::setDateTime() instead in %s on line %d +IntlCalendar::set(): Argument #1 ($year) must be between -2147483648 and 2147483647 + +Deprecated: Function intlcal_set() is deprecated since 8.4, use IntlCalendar::set(), IntlCalendar::setDate(), or IntlCalendar::setDateTime() instead in %s on line %d +intlcal_set(): Argument #3 ($month) must be between -2147483648 and 2147483647 + +Deprecated: Calling IntlCalendar::set() with more than 2 arguments is deprecated, use either IntlCalendar::setDate() or IntlCalendar::setDateTime() instead in %s on line %d +IntlCalendar::set(): Argument #4 ($hour) must be between -2147483648 and 2147483647 + +Deprecated: Calling IntlCalendar::set() with more than 2 arguments is deprecated, use either IntlCalendar::setDate() or IntlCalendar::setDateTime() instead in %s on line %d +IntlCalendar::set(): Argument #5 ($minute) must be between -2147483648 and 2147483647 + +Deprecated: Function intlcal_set() is deprecated since 8.4, use IntlCalendar::set(), IntlCalendar::setDate(), or IntlCalendar::setDateTime() instead in %s on line %d +intlcal_set(): Argument #7 ($second) must be between -2147483648 and 2147483647 diff --git a/ext/intl/tests/calendar_uninitialized_argument_position.phpt b/ext/intl/tests/calendar_uninitialized_argument_position.phpt new file mode 100644 index 000000000000..f92367180549 --- /dev/null +++ b/ext/intl/tests/calendar_uninitialized_argument_position.phpt @@ -0,0 +1,36 @@ +--TEST-- +IntlCalendar methods report the correct argument position for uninitialized calendar arguments +--EXTENSIONS-- +intl +--FILE-- +newInstanceWithoutConstructor(); + +foreach (['equals', 'before', 'after', 'isEquivalentTo'] as $method) { + try { + $calendar->$method($uninitialized); + } catch (Error $e) { + echo $method, ': ', $e->getMessage(), PHP_EOL; + } +} + +foreach (['intlcal_equals', 'intlcal_before', 'intlcal_after', 'intlcal_is_equivalent_to'] as $function) { + try { + $function($calendar, $uninitialized); + } catch (Error $e) { + echo $function, ': ', $e->getMessage(), PHP_EOL; + } +} + +?> +--EXPECT-- +equals: IntlCalendar::equals(): Argument #1 ($other) is uninitialized +before: IntlCalendar::before(): Argument #1 ($other) is uninitialized +after: IntlCalendar::after(): Argument #1 ($other) is uninitialized +isEquivalentTo: IntlCalendar::isEquivalentTo(): Argument #1 ($other) is uninitialized +intlcal_equals: intlcal_equals(): Argument #2 ($other) is uninitialized +intlcal_before: intlcal_before(): Argument #2 ($other) is uninitialized +intlcal_after: intlcal_after(): Argument #2 ($other) is uninitialized +intlcal_is_equivalent_to: intlcal_is_equivalent_to(): Argument #2 ($other) is uninitialized diff --git a/ext/intl/tests/formatter_fail.phpt b/ext/intl/tests/formatter_fail.phpt index 53f6b4ac7c7b..678b87b8c1b8 100644 --- a/ext/intl/tests/formatter_fail.phpt +++ b/ext/intl/tests/formatter_fail.phpt @@ -137,9 +137,9 @@ TypeError: numfmt_create(): Argument #1 ($locale) must be of type string, array 'U_ZERO_ERROR' IntlException: NumberFormatter::__construct(): number formatter creation failed -'NumberFormatter::__construct(): number formatter creation failed: U_UNSUPPORTED_ERROR' -'NumberFormatter::create(): number formatter creation failed: U_UNSUPPORTED_ERROR' -'numfmt_create(): number formatter creation failed: U_UNSUPPORTED_ERROR' +'NumberFormatter::__construct(): number formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR' +'NumberFormatter::create(): number formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR' +'numfmt_create(): number formatter creation failed: U_ILLEGAL_ARGUMENT_ERROR' IntlException: NumberFormatter::__construct(): number formatter creation failed 'NumberFormatter::__construct(): number formatter creation failed: U_MEMORY_ALLOCATION_ERROR' diff --git a/ext/intl/tests/gh12243.phpt b/ext/intl/tests/gh12243.phpt index 786a9bd1ee8f..324ed9a65661 100644 --- a/ext/intl/tests/gh12243.phpt +++ b/ext/intl/tests/gh12243.phpt @@ -1,5 +1,5 @@ --TEST-- -GitHub #12043 segfault with IntlDateFormatter::dateType where it equals to UDAT_PATTERN (icu 50) but +GitHub #12043 segfault with IntlDateFormatter::dateType where it equals to IntlDateFormatter::PATTERN (icu 50) but IntldateFormatter::timeType needs to be set as such. --EXTENSIONS-- intl @@ -21,4 +21,4 @@ try { ?> --EXPECT-- -IntlDateFormatter::__construct(): time format must be UDAT_PATTERN if date format is UDAT_PATTERN +IntlDateFormatter::__construct(): datefmt_create: time format must be IntlDateFormatter::PATTERN if date format is IntlDateFormatter::PATTERN diff --git a/ext/intl/tests/gh21998.phpt b/ext/intl/tests/gh21998.phpt new file mode 100644 index 000000000000..392336dab522 --- /dev/null +++ b/ext/intl/tests/gh21998.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-21998 (NumberFormatter::format(INF) leaves a non-NUL-terminated zend_string) +--EXTENSIONS-- +intl +--FILE-- +format(INF)); +var_dump($fmt->format(-INF)); +var_dump($fmt->format(NAN)); +?> +--EXPECT-- +string(3) "∞" +string(4) "-∞" +string(3) "NaN" diff --git a/ext/intl/tests/gregoriancalendar___construct_out_of_bounds.phpt b/ext/intl/tests/gregoriancalendar___construct_out_of_bounds.phpt new file mode 100644 index 000000000000..5b084b25d869 --- /dev/null +++ b/ext/intl/tests/gregoriancalendar___construct_out_of_bounds.phpt @@ -0,0 +1,53 @@ +--TEST-- +IntlGregorianCalendar::__construct(): out-of-bounds date/time arguments report correct positions +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; +} + +try { + intlgregcal_create_instance(1, 99999999999, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + new IntlGregorianCalendar(1, 1, 1, 99999999999, 1); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + new IntlGregorianCalendar(1, 1, 1, 1, 99999999999); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +try { + intlgregcal_create_instance(1, 1, 1, 1, 1, 99999999999); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECTF-- +Deprecated: Calling IntlGregorianCalendar::__construct() with more than 2 arguments is deprecated, use either IntlGregorianCalendar::createFromDate() or IntlGregorianCalendar::createFromDateTime() instead in %s on line %d +IntlGregorianCalendar::__construct(): Argument #1 ($timezoneOrYear) must be between -2147483648 and 2147483647 + +Deprecated: Function intlgregcal_create_instance() is deprecated since 8.4, use IntlGregorianCalendar::__construct(), IntlGregorianCalendar::createFromDate(), or IntlGregorianCalendar::createFromDateTime() instead in %s on line %d +intlgregcal_create_instance(): Argument #2 ($localeOrMonth) must be between -2147483648 and 2147483647 + +Deprecated: Calling IntlGregorianCalendar::__construct() with more than 2 arguments is deprecated, use either IntlGregorianCalendar::createFromDate() or IntlGregorianCalendar::createFromDateTime() instead in %s on line %d +IntlGregorianCalendar::__construct(): Argument #4 ($hour) must be between -2147483648 and 2147483647 + +Deprecated: Calling IntlGregorianCalendar::__construct() with more than 2 arguments is deprecated, use either IntlGregorianCalendar::createFromDate() or IntlGregorianCalendar::createFromDateTime() instead in %s on line %d +IntlGregorianCalendar::__construct(): Argument #5 ($minute) must be between -2147483648 and 2147483647 + +Deprecated: Function intlgregcal_create_instance() is deprecated since 8.4, use IntlGregorianCalendar::__construct(), IntlGregorianCalendar::createFromDate(), or IntlGregorianCalendar::createFromDateTime() instead in %s on line %d +intlgregcal_create_instance(): Argument #6 ($second) must be between -2147483648 and 2147483647 diff --git a/ext/intl/tests/listformatter/listformatter_get_error.phpt b/ext/intl/tests/listformatter/listformatter_get_error.phpt new file mode 100644 index 000000000000..a2a23b3473d2 --- /dev/null +++ b/ext/intl/tests/listformatter/listformatter_get_error.phpt @@ -0,0 +1,25 @@ +--TEST-- +IntlListFormatter getErrorCode()/getErrorMessage() reflect format() failures +--EXTENSIONS-- +intl +--FILE-- +format(["\x80"])); +var_dump($formatter->getErrorCode() === U_INVALID_CHAR_FOUND); +var_dump($formatter->getErrorMessage()); + +var_dump($formatter->format(['a', 'b'])); +var_dump($formatter->getErrorCode() === U_ZERO_ERROR); +var_dump($formatter->getErrorMessage()); + +?> +--EXPECT-- +bool(false) +bool(true) +string(85) "IntlListFormatter::format(): Failed to convert string to UTF-16: U_INVALID_CHAR_FOUND" +string(7) "a and b" +bool(true) +string(12) "U_ZERO_ERROR" diff --git a/ext/intl/tests/msgfmt_parse_int64_min_64.phpt b/ext/intl/tests/msgfmt_parse_int64_min_64.phpt new file mode 100644 index 000000000000..2d003667813c --- /dev/null +++ b/ext/intl/tests/msgfmt_parse_int64_min_64.phpt @@ -0,0 +1,26 @@ +--TEST-- +MessageFormatter::parse() with PHP_INT_MIN on 64-bit platform +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +parse('-9,223,372,036,854,775,808'); +var_dump($parsed); + +$parsed = MessageFormatter::parseMessage('en_US', '{0,number,integer}', '-9,223,372,036,854,775,808'); +var_dump($parsed); + +?> +--EXPECT-- +array(1) { + [0]=> + int(-9223372036854775808) +} +array(1) { + [0]=> + int(-9223372036854775808) +} diff --git a/ext/intl/tests/rangeformatter/rangeformatter_format_int64.phpt b/ext/intl/tests/rangeformatter/rangeformatter_format_int64.phpt new file mode 100644 index 000000000000..9cefbcfb1ea8 --- /dev/null +++ b/ext/intl/tests/rangeformatter/rangeformatter_format_int64.phpt @@ -0,0 +1,27 @@ +--TEST-- +IntlNumberRangeFormatter::format() preserves int64 precision +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +format(9007199254740993, 9007199254740993); +var_dump(preg_replace('/[^0-9]/', '', $formatted)); +?> +--EXPECT-- +string(16) "9007199254740993" diff --git a/ext/intl/tests/resourcebundle_get_without_fallback.phpt b/ext/intl/tests/resourcebundle_get_without_fallback.phpt new file mode 100644 index 000000000000..9f9941227972 --- /dev/null +++ b/ext/intl/tests/resourcebundle_get_without_fallback.phpt @@ -0,0 +1,48 @@ +--TEST-- +ResourceBundle::get() rejects fallback results when fallback is disabled with correct error message +--EXTENSIONS-- +intl +--FILE-- +get('teststring', false)); + +$bundle = resourcebundle_create('en', $fixture); +echo debug(resourcebundle_get($bundle, 'teststring', false)); +?> +--EXPECTF-- +NULL + %i: ResourceBundle::get(): Cannot load element 'teststring' without fallback to %s: U_USING_%s_WARNING +NULL + %i: resourcebundle_get(): Cannot load element 'teststring' without fallback to %s: U_USING_%s_WARNING +--CLEAN-- + diff --git a/ext/intl/tests/spoofchecker_007.phpt b/ext/intl/tests/spoofchecker_007.phpt index b5fc5814ddf8..5f3dbb47f192 100644 --- a/ext/intl/tests/spoofchecker_007.phpt +++ b/ext/intl/tests/spoofchecker_007.phpt @@ -4,12 +4,6 @@ spoofchecker with restriction level intl --SKIPIF-- -getConstant("SINGLE_SCRIPT_RESTRICTIVE")) { - die("skip Incompatible ICU version"); - } -?> --FILE-- or SpoofChecker::USET_CASE_INSENSITIVE%s)) +Spoofchecker::setAllowedChars(): Argument #2 ($patternOptions) must be a valid pattern option, 0 or (SpoofChecker::IGNORE_SPACE|( or SpoofChecker::CASE_INSENSITIVE%s)) Spoofchecker::setAllowedChars(): Argument #1 ($pattern) must be a valid regular expression character set pattern Spoofchecker::setAllowedChars(): Argument #1 ($pattern) must be a valid regular expression character set pattern diff --git a/ext/intl/tests/spoofchecker_supported_icu57_apis.phpt b/ext/intl/tests/spoofchecker_supported_icu57_apis.phpt new file mode 100644 index 000000000000..56cc194991ee --- /dev/null +++ b/ext/intl/tests/spoofchecker_supported_icu57_apis.phpt @@ -0,0 +1,36 @@ +--TEST-- +Spoofchecker exposes restriction-level APIs on all supported ICU versions +--EXTENSIONS-- +intl +--SKIPIF-- + +--FILE-- +getConstant("ASCII"); +$singleScriptRestrictive = $r->getConstant("SINGLE_SCRIPT_RESTRICTIVE"); +$mixedNumbers = $r->getConstant("MIXED_NUMBERS"); + +var_dump($ascii !== false); +var_dump($singleScriptRestrictive !== false); +var_dump($mixedNumbers !== false); +var_dump(is_int($ascii)); +var_dump(is_int($singleScriptRestrictive)); +var_dump(is_int($mixedNumbers)); +var_dump($ascii !== $singleScriptRestrictive); +var_dump($ascii !== $mixedNumbers); +var_dump($singleScriptRestrictive !== $mixedNumbers); +var_dump($r->hasMethod("setRestrictionLevel")); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/intl/tests/spoofchecker_unknown_restriction_level.phpt b/ext/intl/tests/spoofchecker_unknown_restriction_level.phpt index 6b7e9474755d..187be350f594 100644 --- a/ext/intl/tests/spoofchecker_unknown_restriction_level.phpt +++ b/ext/intl/tests/spoofchecker_unknown_restriction_level.phpt @@ -5,8 +5,6 @@ intl --SKIPIF-- --FILE-- getMessage(), PHP_EOL; + } +} + +$std = new stdClass(); +$calendar = IntlCalendar::createInstance(); +$formatter = new IntlDateFormatter(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE); + +dump_exception(fn() => intlcal_create_instance($std)); +dump_exception(fn() => IntlCalendar::createInstance($std)); +dump_exception(fn() => intlcal_set_time_zone($calendar, $std)); +dump_exception(fn() => $calendar->setTimeZone($std)); +dump_exception(fn() => new IntlGregorianCalendar($std)); +dump_exception(fn() => datefmt_create(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $std)); +dump_exception(fn() => IntlDateFormatter::create(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $std)); +dump_exception(fn() => new IntlDateFormatter(null, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $std)); +dump_exception(fn() => datefmt_set_timezone($formatter, $std)); +dump_exception(fn() => $formatter->setTimeZone($std)); + +?> +--EXPECT-- +TypeError: intlcal_create_instance(): Argument #1 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlCalendar::createInstance(): Argument #1 ($timezone) Object of class stdClass could not be converted to string +TypeError: intlcal_set_time_zone(): Argument #2 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlCalendar::setTimeZone(): Argument #1 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlGregorianCalendar::__construct(): Argument #1 ($timezoneOrYear) Object of class stdClass could not be converted to string +TypeError: datefmt_create(): Argument #4 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlDateFormatter::create(): Argument #4 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlDateFormatter::__construct(): Argument #4 ($timezone) Object of class stdClass could not be converted to string +TypeError: datefmt_set_timezone(): Argument #2 ($timezone) Object of class stdClass could not be converted to string +TypeError: IntlDateFormatter::setTimeZone(): Argument #1 ($timezone) Object of class stdClass could not be converted to string diff --git a/ext/intl/tests/uconverter_transcode_subst_length.phpt b/ext/intl/tests/uconverter_transcode_subst_length.phpt new file mode 100644 index 000000000000..93001b16680c --- /dev/null +++ b/ext/intl/tests/uconverter_transcode_subst_length.phpt @@ -0,0 +1,20 @@ +--TEST-- +UConverter::transcode() rejects too long substitution strings +--EXTENSIONS-- +intl +--INI-- +intl.use_exceptions=false +--FILE-- + $subst])); +echo intl_get_error_message(), "\n"; +var_dump(UConverter::transcode('abc', 'UTF-8', 'ASCII', ['to_subst' => $subst])); +echo intl_get_error_message(), "\n"; +?> +--EXPECT-- +bool(false) +UConverter::transcode(): returned error 1: U_ILLEGAL_ARGUMENT_ERROR: U_ILLEGAL_ARGUMENT_ERROR +bool(false) +UConverter::transcode(): returned error 1: U_ILLEGAL_ARGUMENT_ERROR: U_ILLEGAL_ARGUMENT_ERROR diff --git a/ext/intl/timezone/timezone_class.cpp b/ext/intl/timezone/timezone_class.cpp index cf528f1cc09d..319e81c9a77d 100644 --- a/ext/intl/timezone/timezone_class.cpp +++ b/ext/intl/timezone/timezone_class.cpp @@ -121,7 +121,7 @@ static void timezone_throw_exception_with_call_location(const char *msg, const c /* {{{ timezone_process_timezone_argument * TimeZone argument processor. outside_error may be nullptr (for static functions/constructors) */ U_CFUNC TimeZone *timezone_process_timezone_argument( - zend_object *timezone_object, zend_string *timezone_string, intl_error *outside_error) + zend_object *timezone_object, zend_string *timezone_string, intl_error *outside_error, uint32_t arg_num) { std::unique_ptr timeZone; bool free_string = false; @@ -160,8 +160,9 @@ U_CFUNC TimeZone *timezone_process_timezone_argument( free_string = true; } else { if (!EG(exception)) { - // TODO Proper type error - zend_throw_error(nullptr, "Object of class %s could not be converted to string", ZSTR_VAL(timezone_object->ce->name)); + zend_argument_type_error(arg_num, + "Object of class %s could not be converted to string", + ZSTR_VAL(timezone_object->ce->name)); } return nullptr; } @@ -206,7 +207,7 @@ U_CFUNC TimeZone *timezone_process_timezone_argument( /* {{{ clone handler for TimeZone */ static zend_object *TimeZone_clone_obj(zend_object *object) { - TimeZone_object *to_orig = php_intl_timezone_fetch_object(object); + const TimeZone_object *to_orig = php_intl_timezone_fetch_object(object); zend_object *ret_val = TimeZone_ce_ptr->create_object(object->ce); TimeZone_object *to_new = php_intl_timezone_fetch_object(ret_val); @@ -357,7 +358,7 @@ U_CFUNC void timezone_register_IntlTimeZone_class(void) memcpy(&TimeZone_handlers, &std_object_handlers, sizeof TimeZone_handlers); - TimeZone_handlers.offset = XtOffsetOf(TimeZone_object, zo); + TimeZone_handlers.offset = offsetof(TimeZone_object, zo); TimeZone_handlers.clone_obj = TimeZone_clone_obj; TimeZone_handlers.compare = TimeZone_compare_objects; TimeZone_handlers.get_debug_info = TimeZone_get_debug_info; diff --git a/ext/intl/timezone/timezone_class.h b/ext/intl/timezone/timezone_class.h index d1f1c5f8fe2c..cb74709c965a 100644 --- a/ext/intl/timezone/timezone_class.h +++ b/ext/intl/timezone/timezone_class.h @@ -46,9 +46,7 @@ typedef struct { zend_object zo; } TimeZone_object; -static inline TimeZone_object *php_intl_timezone_fetch_object(zend_object *obj) { - return (TimeZone_object *)((char*)(obj) - XtOffsetOf(TimeZone_object, zo)); -} +#define php_intl_timezone_fetch_object(obj) ZEND_CONTAINER_OF(obj, TimeZone_object, zo) #define Z_INTL_TIMEZONE_P(zv) php_intl_timezone_fetch_object(Z_OBJ_P(zv)) #define TIMEZONE_ERROR(to) (to)->err @@ -67,7 +65,7 @@ static inline TimeZone_object *php_intl_timezone_fetch_object(zend_object *obj) } zval *timezone_convert_to_datetimezone(const TimeZone *timeZone, intl_error *outside_error, zval *ret); -TimeZone *timezone_process_timezone_argument(zend_object *timezone_object, zend_string *timezone_string, intl_error *error); +TimeZone *timezone_process_timezone_argument(zend_object *timezone_object, zend_string *timezone_string, intl_error *error, uint32_t arg_num); void timezone_object_construct(const TimeZone *zone, zval *object, int owned); diff --git a/ext/intl/transliterator/transliterator_class.cpp b/ext/intl/transliterator/transliterator_class.cpp index 1da1dbdab8ea..737c51fcf4dd 100644 --- a/ext/intl/transliterator/transliterator_class.cpp +++ b/ext/intl/transliterator/transliterator_class.cpp @@ -130,7 +130,7 @@ static zend_object *Transliterator_object_create( zend_class_entry *ce ) /* {{{ clone handler for Transliterator */ static zend_object *Transliterator_clone_obj( zend_object *object ) { - Transliterator_object *to_orig = php_intl_transliterator_fetch_object(object); + const Transliterator_object *to_orig = php_intl_transliterator_fetch_object(object); zend_object *ret_val = Transliterator_ce_ptr->create_object(object->ce); Transliterator_object *to_new = php_intl_transliterator_fetch_object(ret_val); @@ -167,7 +167,7 @@ U_CFUNC void transliterator_register_Transliterator_class( void ) Transliterator_ce_ptr->create_object = Transliterator_object_create; Transliterator_ce_ptr->default_object_handlers = &Transliterator_handlers; memcpy( &Transliterator_handlers, &std_object_handlers, sizeof Transliterator_handlers ); - Transliterator_handlers.offset = XtOffsetOf(Transliterator_object, zo); + Transliterator_handlers.offset = offsetof(Transliterator_object, zo); Transliterator_handlers.free_obj = Transliterator_objects_free; Transliterator_handlers.clone_obj = Transliterator_clone_obj; } diff --git a/ext/intl/transliterator/transliterator_class.h b/ext/intl/transliterator/transliterator_class.h index 9d41f0ea2bbc..1bdcd60eadc5 100644 --- a/ext/intl/transliterator/transliterator_class.h +++ b/ext/intl/transliterator/transliterator_class.h @@ -38,9 +38,7 @@ typedef struct { zend_object zo; } Transliterator_object; -static inline Transliterator_object *php_intl_transliterator_fetch_object(zend_object *obj) { - return (Transliterator_object *)((char*)(obj) - XtOffsetOf(Transliterator_object, zo)); -} +#define php_intl_transliterator_fetch_object(obj) ZEND_CONTAINER_OF(obj, Transliterator_object, zo) #define Z_INTL_TRANSLITERATOR_P(zv) php_intl_transliterator_fetch_object(Z_OBJ_P(zv)) #define TRANSLITERATOR_FORWARD UTRANS_FORWARD diff --git a/ext/intl/uchar/uchar.cpp b/ext/intl/uchar/uchar.cpp index ccb642b9d416..4f403cba818e 100644 --- a/ext/intl/uchar/uchar.cpp +++ b/ext/intl/uchar/uchar.cpp @@ -541,8 +541,8 @@ IC_METHOD(getFC_NFKC_Closure) { error = U_ZERO_ERROR; u8str = intl_convert_utf16_to_utf8(closure, closure_len, &error); - INTL_CHECK_STATUS(error, "Failed converting output to UTF8"); efree(closure); + INTL_CHECK_STATUS(error, "Failed converting output to UTF8"); RETVAL_NEW_STR(u8str); } /* }}} */ diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index f97761a42a2d..da0c4b216ffe 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -100,9 +100,7 @@ ZEND_TSRMLS_CACHE_DEFINE() ZEND_GET_MODULE(ldap) #endif -static inline ldap_linkdata *ldap_link_from_obj(zend_object *obj) { - return (ldap_linkdata *)((char *)(obj) - XtOffsetOf(ldap_linkdata, std)); -} +#define ldap_link_from_obj(obj) ZEND_CONTAINER_OF(obj, ldap_linkdata, std) #define Z_LDAP_LINK_P(zv) ldap_link_from_obj(Z_OBJ_P(zv)) @@ -147,9 +145,7 @@ static void ldap_link_free_obj(zend_object *obj) zend_object_std_dtor(&ld->std); } -static inline ldap_resultdata *ldap_result_from_obj(zend_object *obj) { - return (ldap_resultdata *)((char *)(obj) - XtOffsetOf(ldap_resultdata, std)); -} +#define ldap_result_from_obj(obj) ZEND_CONTAINER_OF(obj, ldap_resultdata, std) #define Z_LDAP_RESULT_P(zv) ldap_result_from_obj(Z_OBJ_P(zv)) @@ -184,9 +180,7 @@ static void ldap_result_free_obj(zend_object *obj) zend_object_std_dtor(&result->std); } -static inline ldap_result_entry *ldap_result_entry_from_obj(zend_object *obj) { - return (ldap_result_entry *)((char *)(obj) - XtOffsetOf(ldap_result_entry, std)); -} +#define ldap_result_entry_from_obj(obj) ZEND_CONTAINER_OF(obj, ldap_result_entry, std) #define Z_LDAP_RESULT_ENTRY_P(zv) ldap_result_entry_from_obj(Z_OBJ_P(zv)) @@ -271,7 +265,7 @@ static zend_string* php_ldap_try_get_ldap_value_from_zval(zval *zv) { /* The char pointer MUST refer to the char* of a zend_string struct */ static void php_ldap_zend_string_release_from_char_pointer(char *ptr) { - zend_string_release((zend_string*) (ptr - XtOffsetOf(zend_string, val))); + zend_string_release((zend_string*) (ptr - offsetof(zend_string, val))); } /* {{{ Parse controls from and to arrays */ @@ -877,7 +871,7 @@ PHP_MINIT_FUNCTION(ldap) ldap_link_ce->default_object_handlers = &ldap_link_object_handlers; memcpy(&ldap_link_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - ldap_link_object_handlers.offset = XtOffsetOf(ldap_linkdata, std); + ldap_link_object_handlers.offset = offsetof(ldap_linkdata, std); ldap_link_object_handlers.free_obj = ldap_link_free_obj; ldap_link_object_handlers.get_constructor = ldap_link_get_constructor; ldap_link_object_handlers.clone_obj = NULL; @@ -888,7 +882,7 @@ PHP_MINIT_FUNCTION(ldap) ldap_result_ce->default_object_handlers = &ldap_result_object_handlers; memcpy(&ldap_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - ldap_result_object_handlers.offset = XtOffsetOf(ldap_resultdata, std); + ldap_result_object_handlers.offset = offsetof(ldap_resultdata, std); ldap_result_object_handlers.free_obj = ldap_result_free_obj; ldap_result_object_handlers.get_constructor = ldap_result_get_constructor; ldap_result_object_handlers.clone_obj = NULL; @@ -899,7 +893,7 @@ PHP_MINIT_FUNCTION(ldap) ldap_result_entry_ce->default_object_handlers = &ldap_result_entry_object_handlers; memcpy(&ldap_result_entry_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - ldap_result_entry_object_handlers.offset = XtOffsetOf(ldap_result_entry, std); + ldap_result_entry_object_handlers.offset = offsetof(ldap_result_entry, std); ldap_result_entry_object_handlers.free_obj = ldap_result_entry_free_obj; ldap_result_entry_object_handlers.get_constructor = ldap_result_entry_get_constructor; ldap_result_entry_object_handlers.clone_obj = NULL; diff --git a/ext/lexbor/config.m4 b/ext/lexbor/config.m4 index 21fabcd0ddb0..a75f490e77cc 100644 --- a/ext/lexbor/config.m4 +++ b/ext/lexbor/config.m4 @@ -1,4 +1,4 @@ -PHP_LEXBOR_CFLAGS="-I@ext_srcdir@/" +PHP_LEXBOR_CFLAGS="-Wno-unknown-warning-option -Wno-unterminated-string-initialization -I@ext_srcdir@/" LEXBOR_DIR="lexbor" AC_DEFINE([HAVE_LEXBOR], [1], [Define to 1 if the PHP extension 'lexbor' is available.]) diff --git a/ext/lexbor/config.w32 b/ext/lexbor/config.w32 index 44ddfe1ffae4..e75798e06187 100644 --- a/ext/lexbor/config.w32 +++ b/ext/lexbor/config.w32 @@ -11,10 +11,10 @@ ADD_SOURCES("ext/lexbor/lexbor/html/tree", "active_formatting.c open_elements.c ADD_SOURCES("ext/lexbor/lexbor/html/tree/insertion_mode", "after_after_body.c after_after_frameset.c after_body.c after_frameset.c after_head.c before_head.c before_html.c foreign_content.c in_body.c in_caption.c in_cell.c in_column_group.c in_frameset.c in_head.c in_head_noscript.c initial.c in_row.c in_table_body.c in_table.c in_table_text.c in_template.c text.c","lexbor"); ADD_SOURCES("ext/lexbor/lexbor/html", "encoding.c interface.c parser.c tag.c token.c token_attr.c tokenizer.c tree.c","lexbor"); ADD_SOURCES("ext/lexbor/lexbor/encoding", "decode.c encode.c encoding.c multi.c range.c res.c single.c","lexbor"); -ADD_SOURCES("ext/lexbor/lexbor/html/interfaces", "anchor_element.c area_element.c audio_element.c base_element.c body_element.c br_element.c button_element.c canvas_element.c data_element.c data_list_element.c details_element.c dialog_element.c directory_element.c div_element.c d_list_element.c document.c element.c embed_element.c field_set_element.c font_element.c form_element.c frame_element.c frame_set_element.c head_element.c heading_element.c hr_element.c html_element.c iframe_element.c image_element.c input_element.c label_element.c legend_element.c li_element.c link_element.c map_element.c marquee_element.c media_element.c menu_element.c meta_element.c meter_element.c mod_element.c object_element.c o_list_element.c opt_group_element.c option_element.c output_element.c paragraph_element.c param_element.c picture_element.c pre_element.c progress_element.c quote_element.c script_element.c search_element.c select_element.c selectedcontent_element.c slot_element.c source_element.c span_element.c style_element.c table_caption_element.c table_cell_element.c table_col_element.c table_element.c table_row_element.c table_section_element.c template_element.c text_area_element.c time_element.c title_element.c track_element.c u_list_element.c unknown_element.c video_element.c window.c", "dom"); -ADD_SOURCES("ext/lexbor/lexbor/css", "at_rule.c blank.c css.c declaration.c log.c parser.c property.c rule.c state.c stylesheet.c unit.c value.c", "dom"); -ADD_SOURCES("ext/lexbor/lexbor/css/at_rule", "state.c", "dom"); -ADD_SOURCES("ext/lexbor/lexbor/css/property", "state.c", "dom"); +ADD_SOURCES("ext/lexbor/lexbor/html/interfaces", "anchor_element.c area_element.c audio_element.c base_element.c body_element.c br_element.c button_element.c canvas_element.c data_element.c data_list_element.c details_element.c dialog_element.c directory_element.c div_element.c d_list_element.c document.c element.c embed_element.c field_set_element.c font_element.c form_element.c frame_element.c frame_set_element.c head_element.c heading_element.c hr_element.c html_element.c iframe_element.c image_element.c input_element.c label_element.c legend_element.c li_element.c link_element.c map_element.c marquee_element.c media_element.c menu_element.c meta_element.c meter_element.c mod_element.c object_element.c o_list_element.c opt_group_element.c option_element.c output_element.c paragraph_element.c param_element.c picture_element.c pre_element.c progress_element.c quote_element.c script_element.c search_element.c select_element.c selectedcontent_element.c slot_element.c source_element.c span_element.c style_element.c table_caption_element.c table_cell_element.c table_col_element.c table_element.c table_row_element.c table_section_element.c template_element.c text_area_element.c time_element.c title_element.c track_element.c u_list_element.c unknown_element.c video_element.c window.c", "lexbor"); +ADD_SOURCES("ext/lexbor/lexbor/css", "at_rule.c blank.c css.c declaration.c log.c parser.c property.c rule.c state.c stylesheet.c unit.c value.c", "lexbor"); +ADD_SOURCES("ext/lexbor/lexbor/css/at_rule", "state.c", "lexbor"); +ADD_SOURCES("ext/lexbor/lexbor/css/property", "state.c", "lexbor"); ADD_SOURCES("ext/lexbor/lexbor/css/selectors", "state.c selectors.c selector.c pseudo_state.c pseudo.c","lexbor"); ADD_SOURCES("ext/lexbor/lexbor/css/syntax", "state.c parser.c syntax.c anb.c tokenizer.c token.c","lexbor"); ADD_SOURCES("ext/lexbor/lexbor/css/syntax/tokenizer", "error.c","lexbor"); diff --git a/ext/lexbor/lexbor/url/url.c b/ext/lexbor/lexbor/url/url.c index 5a1143469d1a..a5b323f2df09 100644 --- a/ext/lexbor/lexbor/url/url.c +++ b/ext/lexbor/lexbor/url/url.c @@ -860,7 +860,7 @@ lxb_url_is_url_codepoint(lxb_codepoint_t cp) return lxb_url_codepoint_alphanumeric[(lxb_char_t) cp] != 0xFF; } -lxb_inline bool +bool lxb_url_is_special(const lxb_url_t *url) { return url->scheme.type != LXB_URL_SCHEMEL_TYPE__UNKNOWN; diff --git a/ext/lexbor/lexbor/url/url.h b/ext/lexbor/lexbor/url/url.h index 4ed3f32aa646..6cc6f1081c8a 100644 --- a/ext/lexbor/lexbor/url/url.h +++ b/ext/lexbor/lexbor/url/url.h @@ -763,6 +763,15 @@ LXB_API lxb_status_t lxb_url_search_params_serialize(lxb_url_search_params_t *search_params, lexbor_callback_f cb, void *ctx); +/** + * Returns whether the URL is special. + * + * @param[in] lxb_url_t *. Cannot be NULL. + * @return true if URL is special, false otherwise. + */ +LXB_API bool +lxb_url_is_special(const lxb_url_t *url); + /* * Inline functions. */ diff --git a/ext/lexbor/patches/0007-Add-Is_Special_Url_Support.patch b/ext/lexbor/patches/0007-Add-Is_Special_Url_Support.patch new file mode 100644 index 000000000000..6f5e126336d0 --- /dev/null +++ b/ext/lexbor/patches/0007-Add-Is_Special_Url_Support.patch @@ -0,0 +1,44 @@ +From 9181fce509ab9b37c02994545f3971687433e770 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= +Date: Sun, 17 May 2026 22:17:14 +0200 +Subject: [PATCH] Add lxb_url_is_special() to the public API (#362) + +As https://wiki.php.net/rfc/uri_followup#uri_type_detection relies on this information. +--- + source/lexbor/url/url.c | 2 +- + source/lexbor/url/url.h | 9 +++++++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/source/lexbor/url/url.c b/source/lexbor/url/url.c +index 5a114346..a5b323f2 100644 +--- a/source/lexbor/url/url.c ++++ b/source/lexbor/url/url.c +@@ -860,7 +860,7 @@ lxb_url_is_url_codepoint(lxb_codepoint_t cp) + return lxb_url_codepoint_alphanumeric[(lxb_char_t) cp] != 0xFF; + } + +-lxb_inline bool ++bool + lxb_url_is_special(const lxb_url_t *url) + { + return url->scheme.type != LXB_URL_SCHEMEL_TYPE__UNKNOWN; +diff --git a/source/lexbor/url/url.h b/source/lexbor/url/url.h +index 4ed3f32a..6cc6f108 100644 +--- a/source/lexbor/url/url.h ++++ b/source/lexbor/url/url.h +@@ -763,6 +763,15 @@ LXB_API lxb_status_t + lxb_url_search_params_serialize(lxb_url_search_params_t *search_params, + lexbor_callback_f cb, void *ctx); + ++/** ++ * Returns whether the URL is special. ++ * ++ * @param[in] lxb_url_t *. Cannot be NULL. ++ * @return true if URL is special, false otherwise. ++ */ ++LXB_API bool ++lxb_url_is_special(const lxb_url_t *url); ++ + /* + * Inline functions. + */ \ No newline at end of file diff --git a/ext/libxml/tests/bug61367-read_2.phpt b/ext/libxml/tests/bug61367-read_2.phpt index f4b0f300293c..ec4ce827336b 100644 --- a/ext/libxml/tests/bug61367-read_2.phpt +++ b/ext/libxml/tests/bug61367-read_2.phpt @@ -5,6 +5,7 @@ dom --SKIPIF-- = 2.9.12 only'); +if (preg_match('/[^\x00-\x7F]/', __DIR__)) die('skip path contains non-ASCII characters that libxml URI parser rejects'); ?> --INI-- open_basedir=. @@ -58,6 +59,6 @@ bool(true) int(4) bool(true) -%s: DOMDocument::loadXML(): %Sfailed to load %s +%s: DOMDocument::loadXML(): %s Warning: Attempt to read property "nodeValue" on null in %s on line %d diff --git a/ext/libxml/tests/libxml_disable_entity_loader_2.phpt b/ext/libxml/tests/libxml_disable_entity_loader_2.phpt index fb71c0c8d756..be386154bb20 100644 --- a/ext/libxml/tests/libxml_disable_entity_loader_2.phpt +++ b/ext/libxml/tests/libxml_disable_entity_loader_2.phpt @@ -6,6 +6,7 @@ dom --SKIPIF-- = 2.9.12 only'); +if (preg_match('/[^\x00-\x7F]/', __DIR__)) die('skip path contains non-ASCII characters that libxml URI parser rejects'); ?> --FILE-- --FILE-- mime_name) { - if (strncasecmp((*encoding)->mime_name, name, name_len) == 0 && (*encoding)->mime_name[name_len] == '\0') { + size_t mime_len = strlen((*encoding)->mime_name); + if (mime_len == name_len && strncasecmp((*encoding)->mime_name, name, name_len) == 0) { return *encoding; } } @@ -352,7 +353,8 @@ const mbfl_encoding *mbfl_name2encoding_ex(const char *name, size_t name_len) for (encoding = mbfl_encoding_ptr_list; *encoding; encoding++) { if ((*encoding)->aliases) { for (const char **alias = (*encoding)->aliases; *alias; alias++) { - if (strncasecmp(name, *alias, name_len) == 0 && (*alias)[name_len] == '\0') { + size_t alias_len = strlen(*alias); + if (alias_len == name_len && strncasecmp(name, *alias, name_len) == 0) { return *encoding; } } diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 8780712aee96..faa21b11a0f7 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -54,6 +54,7 @@ #include "mb_gpc.h" #ifdef HAVE_MBREGEX +# include "zend_attributes.h" # include "php_mbregex.h" #endif @@ -623,7 +624,7 @@ static char *php_mb_rfc1867_getword(const zend_encoding *encoding, char **line, static char *php_mb_rfc1867_getword_conf(const zend_encoding *encoding, char *str) /* {{{ */ { - while (*str && isspace(*(unsigned char *)str)) { + while (*str && isspace((unsigned char)*str)) { ++str; } @@ -639,7 +640,7 @@ static char *php_mb_rfc1867_getword_conf(const zend_encoding *encoding, char *st } else { char *strend = str; - while (*strend && !isspace(*(unsigned char *)strend)) { + while (*strend && !isspace((unsigned char)*strend)) { ++strend; } return php_mb_rfc1867_substring_conf(encoding, str, strend - str, 0); diff --git a/ext/mbstring/mbstring.stub.php b/ext/mbstring/mbstring.stub.php index af9c5cbb93ea..109a3ad564d8 100644 --- a/ext/mbstring/mbstring.stub.php +++ b/ext/mbstring/mbstring.stub.php @@ -7,6 +7,7 @@ * @var string * @cvalue php_mb_oniguruma_version */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] const MB_ONIGURUMA_VERSION = UNKNOWN; #endif @@ -197,57 +198,73 @@ function mb_str_pad(string $string, int $length, string $pad_string = " ", int $ #ifdef HAVE_MBREGEX /** @refcount 1 */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_regex_encoding(?string $encoding = null): string|bool {} /** @param array $matches */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg(string $pattern, string $string, &$matches = null): bool {} /** @param array $matches */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_eregi(string $pattern, string $string, &$matches = null): bool {} /** @refcount 1 */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg_replace(string $pattern, string $replacement, string $string, ?string $options = null): string|false|null {} /** @refcount 1 */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_eregi_replace(string $pattern, string $replacement, string $string, ?string $options = null): string|false|null {} /** @refcount 1 */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg_replace_callback(string $pattern, callable $callback, string $string, ?string $options = null): string|false|null {} /** * @return array|false * @refcount 1 */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_split(string $pattern, string $string, int $limit = -1): array|false {} +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg_match(string $pattern, string $string, ?string $options = null): bool {} +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg_search(?string $pattern = null, ?string $options = null): bool {} /** * @return array|false * @refcount 1 */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg_search_pos(?string $pattern = null, ?string $options = null): array|false {} /** * @return array|false * @refcount 1 */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg_search_regs(?string $pattern = null, ?string $options = null): array|false {} +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg_search_init(string $string, ?string $pattern = null, ?string $options = null): bool {} /** * @return array|false * @refcount 1 */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg_search_getregs(): array|false {} +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg_search_getpos(): int {} +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_ereg_search_setpos(int $offset): bool {} /** @refcount 1 */ +#[\Deprecated(since: '8.6', message: "because the underlying library is no longer maintained")] function mb_regex_set_options(?string $options = null): string {} #endif diff --git a/ext/mbstring/mbstring_arginfo.h b/ext/mbstring/mbstring_arginfo.h index 8fb83425ee1d..bc9db3d415c1 100644 --- a/ext/mbstring/mbstring_arginfo.h +++ b/ext/mbstring/mbstring_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit mbstring.stub.php instead. - * Stub hash: 03c07f68bea7d7b96e6dc11f180f45663b859ed3 */ + * Stub hash: f02c317efd6814f902ea75c9d222893713546845 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_mb_language, 0, 0, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, language, IS_STRING, 1, "null") @@ -413,22 +413,22 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(mb_chr, arginfo_mb_chr) ZEND_FE(mb_str_pad, arginfo_mb_str_pad) #if defined(HAVE_MBREGEX) - ZEND_FE(mb_regex_encoding, arginfo_mb_regex_encoding) - ZEND_FE(mb_ereg, arginfo_mb_ereg) - ZEND_FE(mb_eregi, arginfo_mb_eregi) - ZEND_FE(mb_ereg_replace, arginfo_mb_ereg_replace) - ZEND_FE(mb_eregi_replace, arginfo_mb_eregi_replace) - ZEND_FE(mb_ereg_replace_callback, arginfo_mb_ereg_replace_callback) - ZEND_FE(mb_split, arginfo_mb_split) - ZEND_FE(mb_ereg_match, arginfo_mb_ereg_match) - ZEND_FE(mb_ereg_search, arginfo_mb_ereg_search) - ZEND_FE(mb_ereg_search_pos, arginfo_mb_ereg_search_pos) - ZEND_FE(mb_ereg_search_regs, arginfo_mb_ereg_search_regs) - ZEND_FE(mb_ereg_search_init, arginfo_mb_ereg_search_init) - ZEND_FE(mb_ereg_search_getregs, arginfo_mb_ereg_search_getregs) - ZEND_FE(mb_ereg_search_getpos, arginfo_mb_ereg_search_getpos) - ZEND_FE(mb_ereg_search_setpos, arginfo_mb_ereg_search_setpos) - ZEND_FE(mb_regex_set_options, arginfo_mb_regex_set_options) + ZEND_RAW_FENTRY("mb_regex_encoding", zif_mb_regex_encoding, arginfo_mb_regex_encoding, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg", zif_mb_ereg, arginfo_mb_ereg, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_eregi", zif_mb_eregi, arginfo_mb_eregi, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg_replace", zif_mb_ereg_replace, arginfo_mb_ereg_replace, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_eregi_replace", zif_mb_eregi_replace, arginfo_mb_eregi_replace, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg_replace_callback", zif_mb_ereg_replace_callback, arginfo_mb_ereg_replace_callback, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_split", zif_mb_split, arginfo_mb_split, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg_match", zif_mb_ereg_match, arginfo_mb_ereg_match, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg_search", zif_mb_ereg_search, arginfo_mb_ereg_search, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg_search_pos", zif_mb_ereg_search_pos, arginfo_mb_ereg_search_pos, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg_search_regs", zif_mb_ereg_search_regs, arginfo_mb_ereg_search_regs, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg_search_init", zif_mb_ereg_search_init, arginfo_mb_ereg_search_init, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg_search_getregs", zif_mb_ereg_search_getregs, arginfo_mb_ereg_search_getregs, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg_search_getpos", zif_mb_ereg_search_getpos, arginfo_mb_ereg_search_getpos, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_ereg_search_setpos", zif_mb_ereg_search_setpos, arginfo_mb_ereg_search_setpos, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("mb_regex_set_options", zif_mb_regex_set_options, arginfo_mb_regex_set_options, ZEND_ACC_DEPRECATED, NULL, NULL) #endif ZEND_FE_END }; @@ -436,7 +436,7 @@ static const zend_function_entry ext_functions[] = { static void register_mbstring_symbols(int module_number) { #if defined(HAVE_MBREGEX) - REGISTER_STRING_CONSTANT("MB_ONIGURUMA_VERSION", php_mb_oniguruma_version, CONST_PERSISTENT); + zend_constant *const_MB_ONIGURUMA_VERSION = REGISTER_STRING_CONSTANT("MB_ONIGURUMA_VERSION", php_mb_oniguruma_version, CONST_PERSISTENT | CONST_DEPRECATED); #endif REGISTER_LONG_CONSTANT("MB_CASE_UPPER", PHP_UNICODE_CASE_UPPER, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MB_CASE_LOWER", PHP_UNICODE_CASE_LOWER, CONST_PERSISTENT); @@ -446,4 +446,145 @@ static void register_mbstring_symbols(int module_number) REGISTER_LONG_CONSTANT("MB_CASE_LOWER_SIMPLE", PHP_UNICODE_CASE_LOWER_SIMPLE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MB_CASE_TITLE_SIMPLE", PHP_UNICODE_CASE_TITLE_SIMPLE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("MB_CASE_FOLD_SIMPLE", PHP_UNICODE_CASE_FOLD_SIMPLE, CONST_PERSISTENT); + +#if defined(HAVE_MBREGEX) + + zend_attribute *attribute_Deprecated_func_mb_regex_encoding_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_regex_encoding", sizeof("mb_regex_encoding") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_regex_encoding_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_regex_encoding_0->args[0].value, attribute_Deprecated_func_mb_regex_encoding_0_arg0_str); + attribute_Deprecated_func_mb_regex_encoding_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_regex_encoding_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_regex_encoding_0->args[1].value, attribute_Deprecated_func_mb_regex_encoding_0_arg1_str); + attribute_Deprecated_func_mb_regex_encoding_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg", sizeof("mb_ereg") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_0->args[0].value, attribute_Deprecated_func_mb_ereg_0_arg0_str); + attribute_Deprecated_func_mb_ereg_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_0->args[1].value, attribute_Deprecated_func_mb_ereg_0_arg1_str); + attribute_Deprecated_func_mb_ereg_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_eregi_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_eregi", sizeof("mb_eregi") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_eregi_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_eregi_0->args[0].value, attribute_Deprecated_func_mb_eregi_0_arg0_str); + attribute_Deprecated_func_mb_eregi_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_eregi_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_eregi_0->args[1].value, attribute_Deprecated_func_mb_eregi_0_arg1_str); + attribute_Deprecated_func_mb_eregi_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_replace_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg_replace", sizeof("mb_ereg_replace") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_replace_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_replace_0->args[0].value, attribute_Deprecated_func_mb_ereg_replace_0_arg0_str); + attribute_Deprecated_func_mb_ereg_replace_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_replace_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_replace_0->args[1].value, attribute_Deprecated_func_mb_ereg_replace_0_arg1_str); + attribute_Deprecated_func_mb_ereg_replace_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_eregi_replace_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_eregi_replace", sizeof("mb_eregi_replace") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_eregi_replace_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_eregi_replace_0->args[0].value, attribute_Deprecated_func_mb_eregi_replace_0_arg0_str); + attribute_Deprecated_func_mb_eregi_replace_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_eregi_replace_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_eregi_replace_0->args[1].value, attribute_Deprecated_func_mb_eregi_replace_0_arg1_str); + attribute_Deprecated_func_mb_eregi_replace_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_replace_callback_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg_replace_callback", sizeof("mb_ereg_replace_callback") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_replace_callback_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_replace_callback_0->args[0].value, attribute_Deprecated_func_mb_ereg_replace_callback_0_arg0_str); + attribute_Deprecated_func_mb_ereg_replace_callback_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_replace_callback_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_replace_callback_0->args[1].value, attribute_Deprecated_func_mb_ereg_replace_callback_0_arg1_str); + attribute_Deprecated_func_mb_ereg_replace_callback_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_split_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_split", sizeof("mb_split") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_split_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_split_0->args[0].value, attribute_Deprecated_func_mb_split_0_arg0_str); + attribute_Deprecated_func_mb_split_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_split_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_split_0->args[1].value, attribute_Deprecated_func_mb_split_0_arg1_str); + attribute_Deprecated_func_mb_split_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_match_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg_match", sizeof("mb_ereg_match") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_match_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_match_0->args[0].value, attribute_Deprecated_func_mb_ereg_match_0_arg0_str); + attribute_Deprecated_func_mb_ereg_match_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_match_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_match_0->args[1].value, attribute_Deprecated_func_mb_ereg_match_0_arg1_str); + attribute_Deprecated_func_mb_ereg_match_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_search_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg_search", sizeof("mb_ereg_search") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_search_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_0->args[0].value, attribute_Deprecated_func_mb_ereg_search_0_arg0_str); + attribute_Deprecated_func_mb_ereg_search_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_search_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_0->args[1].value, attribute_Deprecated_func_mb_ereg_search_0_arg1_str); + attribute_Deprecated_func_mb_ereg_search_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_search_pos_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg_search_pos", sizeof("mb_ereg_search_pos") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_search_pos_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_pos_0->args[0].value, attribute_Deprecated_func_mb_ereg_search_pos_0_arg0_str); + attribute_Deprecated_func_mb_ereg_search_pos_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_search_pos_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_pos_0->args[1].value, attribute_Deprecated_func_mb_ereg_search_pos_0_arg1_str); + attribute_Deprecated_func_mb_ereg_search_pos_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_search_regs_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg_search_regs", sizeof("mb_ereg_search_regs") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_search_regs_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_regs_0->args[0].value, attribute_Deprecated_func_mb_ereg_search_regs_0_arg0_str); + attribute_Deprecated_func_mb_ereg_search_regs_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_search_regs_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_regs_0->args[1].value, attribute_Deprecated_func_mb_ereg_search_regs_0_arg1_str); + attribute_Deprecated_func_mb_ereg_search_regs_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_search_init_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg_search_init", sizeof("mb_ereg_search_init") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_search_init_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_init_0->args[0].value, attribute_Deprecated_func_mb_ereg_search_init_0_arg0_str); + attribute_Deprecated_func_mb_ereg_search_init_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_search_init_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_init_0->args[1].value, attribute_Deprecated_func_mb_ereg_search_init_0_arg1_str); + attribute_Deprecated_func_mb_ereg_search_init_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_search_getregs_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg_search_getregs", sizeof("mb_ereg_search_getregs") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_search_getregs_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_getregs_0->args[0].value, attribute_Deprecated_func_mb_ereg_search_getregs_0_arg0_str); + attribute_Deprecated_func_mb_ereg_search_getregs_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_search_getregs_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_getregs_0->args[1].value, attribute_Deprecated_func_mb_ereg_search_getregs_0_arg1_str); + attribute_Deprecated_func_mb_ereg_search_getregs_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_search_getpos_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg_search_getpos", sizeof("mb_ereg_search_getpos") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_search_getpos_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_getpos_0->args[0].value, attribute_Deprecated_func_mb_ereg_search_getpos_0_arg0_str); + attribute_Deprecated_func_mb_ereg_search_getpos_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_search_getpos_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_getpos_0->args[1].value, attribute_Deprecated_func_mb_ereg_search_getpos_0_arg1_str); + attribute_Deprecated_func_mb_ereg_search_getpos_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_ereg_search_setpos_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_ereg_search_setpos", sizeof("mb_ereg_search_setpos") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_ereg_search_setpos_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_setpos_0->args[0].value, attribute_Deprecated_func_mb_ereg_search_setpos_0_arg0_str); + attribute_Deprecated_func_mb_ereg_search_setpos_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_ereg_search_setpos_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_ereg_search_setpos_0->args[1].value, attribute_Deprecated_func_mb_ereg_search_setpos_0_arg1_str); + attribute_Deprecated_func_mb_ereg_search_setpos_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + + zend_attribute *attribute_Deprecated_func_mb_regex_set_options_0 = zend_add_function_attribute(zend_hash_str_find_ptr(CG(function_table), "mb_regex_set_options", sizeof("mb_regex_set_options") - 1), ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_func_mb_regex_set_options_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_regex_set_options_0->args[0].value, attribute_Deprecated_func_mb_regex_set_options_0_arg0_str); + attribute_Deprecated_func_mb_regex_set_options_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_func_mb_regex_set_options_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_func_mb_regex_set_options_0->args[1].value, attribute_Deprecated_func_mb_regex_set_options_0_arg1_str); + attribute_Deprecated_func_mb_regex_set_options_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); +#endif +#if defined(HAVE_MBREGEX) + + zend_attribute *attribute_Deprecated_const_MB_ONIGURUMA_VERSION_0 = zend_add_global_constant_attribute(const_MB_ONIGURUMA_VERSION, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zend_string *attribute_Deprecated_const_MB_ONIGURUMA_VERSION_0_arg0_str = zend_string_init("8.6", strlen("8.6"), 1); + ZVAL_STR(&attribute_Deprecated_const_MB_ONIGURUMA_VERSION_0->args[0].value, attribute_Deprecated_const_MB_ONIGURUMA_VERSION_0_arg0_str); + attribute_Deprecated_const_MB_ONIGURUMA_VERSION_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zend_string *attribute_Deprecated_const_MB_ONIGURUMA_VERSION_0_arg1_str = zend_string_init("because the underlying library is no longer maintained", strlen("because the underlying library is no longer maintained"), 1); + ZVAL_STR(&attribute_Deprecated_const_MB_ONIGURUMA_VERSION_0->args[1].value, attribute_Deprecated_const_MB_ONIGURUMA_VERSION_0_arg1_str); + attribute_Deprecated_const_MB_ONIGURUMA_VERSION_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); +#endif } diff --git a/ext/mbstring/php_mbregex.c b/ext/mbstring/php_mbregex.c index baf9c57f41fb..1043c4a46eb2 100644 --- a/ext/mbstring/php_mbregex.c +++ b/ext/mbstring/php_mbregex.c @@ -407,8 +407,13 @@ int php_mb_regex_set_mbctype(const char *encname) if (mbctype == ONIG_ENCODING_UNDEF) { return FAILURE; } + const mbfl_encoding *mbfl_enc = mbfl_name2encoding(encname); + if (mbfl_enc == NULL) { + /* Encoding supported by Oniguruma but not by mbfl */ + return FAILURE; + } MBREX(current_mbctype) = mbctype; - MBREX(current_mbctype_mbfl_encoding) = mbfl_name2encoding(encname); + MBREX(current_mbctype_mbfl_encoding) = mbfl_enc; return SUCCESS; } /* }}} */ @@ -777,7 +782,7 @@ static inline void mb_regex_substitute( continue; } if (name_end[0] == delim) break; - if (maybe_num && !isdigit(name_end[0])) maybe_num = 0; + if (maybe_num && !isdigit((unsigned char)name_end[0])) maybe_num = 0; name_end++; } p = name_end + 1; diff --git a/ext/mbstring/tests/GHSA-74r9-qxhc-fx53.phpt b/ext/mbstring/tests/GHSA-74r9-qxhc-fx53.phpt new file mode 100644 index 000000000000..e58be7e50d8a --- /dev/null +++ b/ext/mbstring/tests/GHSA-74r9-qxhc-fx53.phpt @@ -0,0 +1,50 @@ +--TEST-- +GHSA-74r9-qxhc-fx53: Out-of-bounds access in mbfl_name2encoding_ex() +--CREDITS-- +Akshay Jain (AkshayJainG) +--EXTENSIONS-- +mbstring +--FILE-- +getMessage(), "\n"; + } +} + +ini_set('mbstring.detect_order', $encoding); +ini_set('mbstring.detect_order', $alias); +ini_set('mbstring.http_output', $encoding); +ini_set('mbstring.http_output', $alias); + +test(fn () => mb_convert_encoding('foo', $encoding, $encoding)); +test(fn () => mb_convert_encoding('foo', $alias, $alias)); +test(fn () => mb_detect_encoding('foo', $encoding)); +test(fn () => mb_detect_encoding('foo', $alias)); +test(fn () => mb_convert_variables($encoding, $alias, $var)); +test(fn () => mb_detect_order($encoding)); +test(fn () => mb_detect_order($alias)); + +?> +--EXPECTF-- +Warning: ini_set(): INI setting contains invalid encoding "UTF-8" in %s on line %d + +Warning: ini_set(): INI setting contains invalid encoding "binary" in %s on line %d + +Deprecated: ini_set(): Use of mbstring.http_output is deprecated in %s on line %d + +Deprecated: ini_set(): Use of mbstring.http_output is deprecated in %s on line %d +ValueError: mb_convert_encoding(): Argument #3 ($from_encoding) contains invalid encoding "UTF-8" +ValueError: mb_convert_encoding(): Argument #3 ($from_encoding) contains invalid encoding "binary" +ValueError: mb_detect_encoding(): Argument #2 ($encodings) contains invalid encoding "UTF-8" +ValueError: mb_detect_encoding(): Argument #2 ($encodings) contains invalid encoding "binary" +ValueError: mb_convert_variables(): Argument #2 ($from_encoding) contains invalid encoding "binary" +ValueError: mb_detect_order(): Argument #1 ($encoding) contains invalid encoding "UTF-8" +ValueError: mb_detect_order(): Argument #1 ($encoding) contains invalid encoding "binary" diff --git a/ext/mbstring/tests/bug43994.phpt b/ext/mbstring/tests/bug43994.phpt index b4ae29ff40e7..cc6953808cc6 100644 --- a/ext/mbstring/tests/bug43994.phpt +++ b/ext/mbstring/tests/bug43994.phpt @@ -31,9 +31,13 @@ try { var_dump($mb_regs); ?> ---EXPECT-- +--EXPECTF-- Without $regs arg: + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_ereg(): Argument #1 ($pattern) must not be empty With $regs arg: + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_ereg(): Argument #1 ($pattern) must not be empty NULL diff --git a/ext/mbstring/tests/bug69151.phpt b/ext/mbstring/tests/bug69151.phpt index c95fa05c49f8..9dd546d2eee6 100644 --- a/ext/mbstring/tests/bug69151.phpt +++ b/ext/mbstring/tests/bug69151.phpt @@ -18,9 +18,16 @@ var_dump(NULL === mb_ereg_replace('.', "\\0", $str)); var_dump(false === mb_ereg_search_init("\x80", '.')); var_dump(false === mb_ereg_search()); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_eregi() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) bool(true) + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) diff --git a/ext/mbstring/tests/bug72164.phpt b/ext/mbstring/tests/bug72164.phpt index e4ed04ba5eba..2b8f3950d92c 100644 --- a/ext/mbstring/tests/bug72164.phpt +++ b/ext/mbstring/tests/bug72164.phpt @@ -19,5 +19,6 @@ try { } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d Option "e" is not supported diff --git a/ext/mbstring/tests/bug72399.phpt b/ext/mbstring/tests/bug72399.phpt index 4d3ea6d28c0b..bf9880c0ab42 100644 --- a/ext/mbstring/tests/bug72399.phpt +++ b/ext/mbstring/tests/bug72399.phpt @@ -19,7 +19,12 @@ try { echo $e->getMessage() . \PHP_EOL; } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(0) "" + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d No pattern was provided diff --git a/ext/mbstring/tests/bug72402.phpt b/ext/mbstring/tests/bug72402.phpt index 0441887e33c7..511c18bfec2c 100644 --- a/ext/mbstring/tests/bug72402.phpt +++ b/ext/mbstring/tests/bug72402.phpt @@ -17,5 +17,6 @@ try { } catch(Exception $e) {} ?> DONE ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace_callback() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d DONE diff --git a/ext/mbstring/tests/bug72691.phpt b/ext/mbstring/tests/bug72691.phpt index db299e9d9dbc..9147d149fcb3 100644 --- a/ext/mbstring/tests/bug72691.phpt +++ b/ext/mbstring/tests/bug72691.phpt @@ -27,23 +27,48 @@ mb_ereg_search('\Z'); var_dump(mb_ereg_search_getpos()); var_dump(mb_ereg_search_getregs()); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(0) + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(1) { [0]=> string(0) "" } + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(0) + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(1) { [0]=> string(0) "" } + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(3) + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(1) { [0]=> string(3) "foo" } + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(3) + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(1) { [0]=> string(0) "" diff --git a/ext/mbstring/tests/bug72693.phpt b/ext/mbstring/tests/bug72693.phpt index 6596eadd5fea..7b120164afab 100644 --- a/ext/mbstring/tests/bug72693.phpt +++ b/ext/mbstring/tests/bug72693.phpt @@ -26,18 +26,41 @@ var_dump(mb_ereg_search_getpos()); var_dump(mb_ereg_search('\Z')); var_dump(mb_ereg_search_getpos()); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(0) + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(0) + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(3) + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(1) { [0]=> string(3) "foo" } + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(3) + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(3) diff --git a/ext/mbstring/tests/bug72694.phpt b/ext/mbstring/tests/bug72694.phpt index ed7d2e79250e..0789f3620817 100644 --- a/ext/mbstring/tests/bug72694.phpt +++ b/ext/mbstring/tests/bug72694.phpt @@ -16,8 +16,17 @@ var_dump(mb_ereg_search_getpos()); var_dump(mb_ereg_search('\Z')); var_dump(mb_ereg_search_getpos()); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(3) + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(3) diff --git a/ext/mbstring/tests/bug72710.phpt b/ext/mbstring/tests/bug72710.phpt index ca1558c2ff1f..614abcf542f8 100644 --- a/ext/mbstring/tests/bug72710.phpt +++ b/ext/mbstring/tests/bug72710.phpt @@ -11,4 +11,6 @@ if (!function_exists('mb_ereg')) die('skip mbregex support not available'); mb_ereg('(?<0>a)', 'a'); ?> --EXPECTF-- +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + Warning: mb_ereg(): mbregex compile err: invalid group name <0> in %s on line %d diff --git a/ext/mbstring/tests/bug73532.phpt b/ext/mbstring/tests/bug73532.phpt index 27269296894c..1af3e2422904 100644 --- a/ext/mbstring/tests/bug73532.phpt +++ b/ext/mbstring/tests/bug73532.phpt @@ -10,5 +10,6 @@ if (!function_exists('mb_ereg')) die('skip mbregex support not available'); ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_eregi() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) diff --git a/ext/mbstring/tests/bug73646.phpt b/ext/mbstring/tests/bug73646.phpt index 9897b866439e..7b3e98164989 100644 --- a/ext/mbstring/tests/bug73646.phpt +++ b/ext/mbstring/tests/bug73646.phpt @@ -11,5 +11,7 @@ if (!function_exists('mb_ereg')) die('skip mbregex support not available'); var_dump(mb_ereg_search_init(NULL)); ?> --EXPECTF-- +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + Deprecated: mb_ereg_search_init(): Passing null to parameter #1 ($string) of type string is deprecated in %s on line %d bool(true) diff --git a/ext/mbstring/tests/bug76999.phpt b/ext/mbstring/tests/bug76999.phpt index 57e286af3b8a..5c6dc442573f 100644 --- a/ext/mbstring/tests/bug76999.phpt +++ b/ext/mbstring/tests/bug76999.phpt @@ -19,9 +19,20 @@ try { } var_dump(mb_regex_set_options()); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(2) "pr" + +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(2) "mr" + +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(3) "imd" + +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d Option "a" is not supported + +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(2) "mr" diff --git a/ext/mbstring/tests/bug77367.phpt b/ext/mbstring/tests/bug77367.phpt index ad1dac2e07c8..f4a1a373a98f 100644 --- a/ext/mbstring/tests/bug77367.phpt +++ b/ext/mbstring/tests/bug77367.phpt @@ -11,5 +11,8 @@ if (!function_exists('mb_split')) die('skip mb_split() not available'); mb_regex_encoding('UTF-8'); var_dump(mb_split("\\w", "\xfc")); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) diff --git a/ext/mbstring/tests/bug77370.phpt b/ext/mbstring/tests/bug77370.phpt index a2e3759c120a..8ccb169f83e0 100644 --- a/ext/mbstring/tests/bug77370.phpt +++ b/ext/mbstring/tests/bug77370.phpt @@ -11,5 +11,7 @@ if (!function_exists('mb_split')) die('skip mb_split() not available'); var_dump(mb_split(" \xfd","")); ?> --EXPECTF-- +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line 2 + Warning: mb_split(): Pattern is not valid under UTF-8 encoding in %s on line %d bool(false) diff --git a/ext/mbstring/tests/bug77371.phpt b/ext/mbstring/tests/bug77371.phpt index 767533c66a4e..b4a88ffc5124 100644 --- a/ext/mbstring/tests/bug77371.phpt +++ b/ext/mbstring/tests/bug77371.phpt @@ -11,5 +11,7 @@ if (!function_exists('mb_ereg')) die('skip mb_ereg() not available'); var_dump(mb_ereg("()0\xfc00000\xfc00000\xfc00000\xfc","")); ?> --EXPECTF-- +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + Warning: mb_ereg(): Pattern is not valid under UTF-8 encoding in %s on line %d bool(false) diff --git a/ext/mbstring/tests/bug77381.phpt b/ext/mbstring/tests/bug77381.phpt index 76607bc9ceba..cde197c49a68 100644 --- a/ext/mbstring/tests/bug77381.phpt +++ b/ext/mbstring/tests/bug77381.phpt @@ -14,14 +14,22 @@ var_dump(mb_ereg("0000\\"."\xf5","0")); var_dump(mb_ereg("(?i)FFF00000000000000000\xfd","")); ?> --EXPECTF-- +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + Warning: mb_ereg(): Pattern is not valid under UTF-8 encoding in %s on line %d bool(false) +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + Warning: mb_ereg(): Pattern is not valid under UTF-8 encoding in %s on line %d bool(false) +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + Warning: mb_ereg(): Pattern is not valid under UTF-8 encoding in %s on line %d bool(false) +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + Warning: mb_ereg(): Pattern is not valid under UTF-8 encoding in %s on line %d bool(false) diff --git a/ext/mbstring/tests/bug77418.phpt b/ext/mbstring/tests/bug77418.phpt index 352e125d689e..2227eef44a1b 100644 --- a/ext/mbstring/tests/bug77418.phpt +++ b/ext/mbstring/tests/bug77418.phpt @@ -12,5 +12,8 @@ mb_regex_encoding("UTF-32"); var_dump(mb_split("\x00\x00\x00\x5c\x00\x00\x00B","000000000000000000000000000000")); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) diff --git a/ext/mbstring/tests/bug77428.phpt b/ext/mbstring/tests/bug77428.phpt index c65cd000042f..71f9e09b0b1e 100644 --- a/ext/mbstring/tests/bug77428.phpt +++ b/ext/mbstring/tests/bug77428.phpt @@ -15,6 +15,9 @@ var_dump(mb_ereg_replace('(%)', '\\\1', 'a%c')); var_dump(mb_ereg_replace('(%)', '\\\\1', 'a%c')); ?> ---EXPECT-- -string(4) "a\%c" -string(4) "a\%c" +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d +string(4) "a\%%c" + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d +string(4) "a\%%c" diff --git a/ext/mbstring/tests/bug77514.phpt b/ext/mbstring/tests/bug77514.phpt index 4b33cca8a309..f6b7ca5d1808 100644 --- a/ext/mbstring/tests/bug77514.phpt +++ b/ext/mbstring/tests/bug77514.phpt @@ -13,5 +13,6 @@ $a="abc123"; var_dump(mb_ereg_replace("123","def\\",$a)); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "abcdef\" diff --git a/ext/mbstring/tests/bug78559.phpt b/ext/mbstring/tests/bug78559.phpt index e107d1ba864a..18b1fe00f83a 100644 --- a/ext/mbstring/tests/bug78559.phpt +++ b/ext/mbstring/tests/bug78559.phpt @@ -12,5 +12,6 @@ $str = "5b5b5b5b5b5b5b492a5bce946b5c4b5d5c6b5c4b5d5c4b5d1cceb04b5d1cceb07a73717e $str = hex2bin($str); var_dump(mb_eregi($str, $str)); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_eregi() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) diff --git a/ext/mbstring/tests/bug78633.phpt b/ext/mbstring/tests/bug78633.phpt index a5dcd48b9bf9..42a1ccc4ed2d 100644 --- a/ext/mbstring/tests/bug78633.phpt +++ b/ext/mbstring/tests/bug78633.phpt @@ -15,5 +15,6 @@ if (is_bool($res)) { var_dump($res); } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_eregi() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d ok diff --git a/ext/mbstring/tests/empty_pattern.phpt b/ext/mbstring/tests/empty_pattern.phpt index 64e63147ee78..c7a2ca285c29 100644 --- a/ext/mbstring/tests/empty_pattern.phpt +++ b/ext/mbstring/tests/empty_pattern.phpt @@ -24,6 +24,11 @@ try { } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_ereg_search_init(): Argument #2 ($pattern) must not be empty + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_regs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d No pattern was provided diff --git a/ext/mbstring/tests/gh18901.phpt b/ext/mbstring/tests/gh18901.phpt index 8d862a537c3b..55e41d48377d 100644 --- a/ext/mbstring/tests/gh18901.phpt +++ b/ext/mbstring/tests/gh18901.phpt @@ -13,7 +13,8 @@ foreach ($vals as $val) { var_dump(mb_split('\d', '123', $val)); } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(4) { [0]=> string(0) "" @@ -24,6 +25,8 @@ array(4) { [3]=> string(0) "" } + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(4) { [0]=> string(0) "" @@ -34,6 +37,8 @@ array(4) { [3]=> string(0) "" } + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(4) { [0]=> string(0) "" @@ -44,10 +49,14 @@ array(4) { [3]=> string(0) "" } + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(1) { [0]=> string(3) "123" } + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(1) { [0]=> string(3) "123" diff --git a/ext/mbstring/tests/mb_ereg-compat-01.phpt b/ext/mbstring/tests/mb_ereg-compat-01.phpt index e9d407d1013b..c5752806d5c5 100644 --- a/ext/mbstring/tests/mb_ereg-compat-01.phpt +++ b/ext/mbstring/tests/mb_ereg-compat-01.phpt @@ -17,6 +17,9 @@ function_exists('mb_ereg') or die("skip mb_ereg() is not available in this build echo "ok\n"; } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d ok + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d ok diff --git a/ext/mbstring/tests/mb_ereg-compat-02.phpt b/ext/mbstring/tests/mb_ereg-compat-02.phpt index d30b9927cc53..a47f9c665bc8 100644 --- a/ext/mbstring/tests/mb_ereg-compat-02.phpt +++ b/ext/mbstring/tests/mb_ereg-compat-02.phpt @@ -19,7 +19,8 @@ function_exists('mb_ereg') or die("skip mb_ereg() is not available in this build echo $registers[2]; echo "\n"; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d 1 This is a nice and simple string is diff --git a/ext/mbstring/tests/mb_ereg.phpt b/ext/mbstring/tests/mb_ereg.phpt index 53bb58e15a86..5b3f9c125fcd 100644 --- a/ext/mbstring/tests/mb_ereg.phpt +++ b/ext/mbstring/tests/mb_ereg.phpt @@ -31,16 +31,65 @@ output_handler= do_tests( $enc ); } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)6162632064656620676869206a6b6c2064656620676869206a6b6c + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)a3e1a3e2a3e320a4a2a4aaa4a420a4aba4b3a4ca20a4efa4f1a4f320a3e1a3e2a3e320a4a2a4aaa4a420a4ab20a4b3a4ca20a4efa4f1a4f3 + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)a3eda3faa3f8a3e6a3f020a4a6a4aaa4ab20a4ada4ab20a4f2a4f020a3eda3faa3f8a3e6a3f020a4a6a4aaa4ab2020a4ada4ab20a4f2a4f0 + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)6162632064656620676869206a6b6c2064656620676869206a6b6c + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)a3e1a3e2a3e320a4a2a4aaa4a420a4aba4b3a4ca20a4efa4f1a4f320a3e1a3e2a3e320a4a2a4aaa4a420a4ab20a4b3a4ca20a4efa4f1a4f3 + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)a3eda3faa3f8a3e6a3f020a4a6a4aaa4ab20a4ada4ab20a4f2a4f020a3eda3faa3f8a3e6a3f020a4a6a4aaa4ab2020a4ada4ab20a4f2a4f0 + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)6162632064656620676869206a6b6c2064656620676869206a6b6c + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)a3e1a3e2a3e320a4a2a4aaa4a420a4aba4b3a4ca20a4efa4f1a4f320a3e1a3e2a3e320a4a2a4aaa4a420a4ab20a4b3a4ca20a4efa4f1a4f3 + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)a3eda3faa3f8a3e6a3f020a4a6a4aaa4ab20a4ada4ab20a4f2a4f020a3eda3faa3f8a3e6a3f020a4a6a4aaa4ab2020a4ada4ab20a4f2a4f0 + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)6162632064656620676869206a6b6c2064656620676869206a6b6c + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)a3e1a3e2a3e320a4a2a4aaa4a420a4aba4b3a4ca20a4efa4f1a4f320a3e1a3e2a3e320a4a2a4aaa4a420a4ab20a4b3a4ca20a4efa4f1a4f3 + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d (1)a3eda3faa3f8a3e6a3f020a4a6a4aaa4ab20a4ada4ab20a4f2a4f020a3eda3faa3f8a3e6a3f020a4a6a4aaa4ab2020a4ada4ab20a4f2a4f0 diff --git a/ext/mbstring/tests/mb_ereg1.phpt b/ext/mbstring/tests/mb_ereg1.phpt index 813fe5e41ef0..a92b796423de 100644 --- a/ext/mbstring/tests/mb_ereg1.phpt +++ b/ext/mbstring/tests/mb_ereg1.phpt @@ -24,7 +24,8 @@ foreach ($a as $args) { var_dump($args); } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) array(3) { [0]=> @@ -35,6 +36,8 @@ array(3) { array(0) { } } + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d ValueError: mb_ereg(): Argument #1 ($pattern) must not be empty array(3) { [0]=> @@ -44,6 +47,8 @@ array(3) { [2]=> string(0) "" } + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d TypeError: mb_ereg(): Argument #1 ($pattern) must be of type string, array given array(3) { [0]=> @@ -54,6 +59,8 @@ array(3) { [2]=> string(0) "" } + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d TypeError: mb_ereg(): Argument #2 ($string) must be of type string, array given array(3) { [0]=> @@ -64,6 +71,8 @@ array(3) { [2]=> string(0) "" } + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) array(3) { [0]=> diff --git a/ext/mbstring/tests/mb_ereg2.phpt b/ext/mbstring/tests/mb_ereg2.phpt index f87b5c5410a8..22cd29199dda 100644 --- a/ext/mbstring/tests/mb_ereg2.phpt +++ b/ext/mbstring/tests/mb_ereg2.phpt @@ -18,13 +18,16 @@ var_dump($a, $b, $c); echo "Done\n"; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(-1) int(-1) array(1) { [0]=> string(2) "-1" } + +Deprecated: Function mb_eregi() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(-1) int(-1) array(1) { diff --git a/ext/mbstring/tests/mb_ereg_basic.phpt b/ext/mbstring/tests/mb_ereg_basic.phpt index 094f2949ea43..d54f3bb3be80 100644 --- a/ext/mbstring/tests/mb_ereg_basic.phpt +++ b/ext/mbstring/tests/mb_ereg_basic.phpt @@ -71,15 +71,23 @@ function base64_encode_var_dump($regs) { } } ?> ---EXPECT-- +--EXPECTF-- *** Testing mb_ereg() : basic functionality *** + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d Regex encoding set to utf-8 **-- ASCII String --** -- Without $regs argument-- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) --With $regs argument -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(2) { [0]=> @@ -87,6 +95,8 @@ array(2) { [1]=> string(24) "VGhpcyBpcyBhbiBFbmdsaXM=" } + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -95,9 +105,15 @@ array(1) { **-- Multibyte String --** -- Without $regs argument -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) -- With $regs argument -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(3) { [0]=> @@ -107,6 +123,8 @@ array(3) { [2]=> string(8) "MTIzNA==" } + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) array(0) { } diff --git a/ext/mbstring/tests/mb_ereg_dupnames.phpt b/ext/mbstring/tests/mb_ereg_dupnames.phpt index cf4444682f57..104dba927189 100644 --- a/ext/mbstring/tests/mb_ereg_dupnames.phpt +++ b/ext/mbstring/tests/mb_ereg_dupnames.phpt @@ -15,7 +15,10 @@ function_exists('mb_ereg') or die("skip mb_ereg() is not available in this build mb_ereg($pattern, '中!', $m); var_dump($m); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(4) { [0]=> string(6) "中?" @@ -26,6 +29,8 @@ array(4) { ["punct"]=> string(3) "?" } + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(4) { [0]=> string(6) "中!" diff --git a/ext/mbstring/tests/mb_ereg_match_basic.phpt b/ext/mbstring/tests/mb_ereg_match_basic.phpt index 5b7a8c2a5402..671a72d27272 100644 --- a/ext/mbstring/tests/mb_ereg_match_basic.phpt +++ b/ext/mbstring/tests/mb_ereg_match_basic.phpt @@ -38,18 +38,27 @@ var_dump(mb_ereg_match($regex2, $string_mb)); echo "Done"; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d *** Testing mb_ereg_match() : basic functionality *** -- ASCII string 1 -- + +Deprecated: Function mb_ereg_match() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) -- ASCII string 2 -- + +Deprecated: Function mb_ereg_match() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) -- Multibyte string 1 -- + +Deprecated: Function mb_ereg_match() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) -- Multibyte string 2 -- + +Deprecated: Function mb_ereg_match() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) Done diff --git a/ext/mbstring/tests/mb_ereg_named_subpatterns.phpt b/ext/mbstring/tests/mb_ereg_named_subpatterns.phpt index 80c17b729d70..7756b49d2128 100644 --- a/ext/mbstring/tests/mb_ereg_named_subpatterns.phpt +++ b/ext/mbstring/tests/mb_ereg_named_subpatterns.phpt @@ -16,7 +16,10 @@ function_exists('mb_ereg') or die("skip mb_ereg() is not available in this build mb_ereg('(\s*)(?\w+)', ' 中国', $m); var_dump($m); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(5) { [0]=> string(8) " 中国" @@ -29,6 +32,8 @@ array(5) { ["word"]=> string(6) "中国" } + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(5) { [0]=> string(3) "国" @@ -41,6 +46,8 @@ array(5) { ["word"]=> string(3) "国" } + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(3) { [0]=> string(8) " 中国" diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-01.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-01.phpt index 9ce201725219..057460ff01e4 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-01.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-01.phpt @@ -12,5 +12,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab $a="abc123"; echo mb_ereg_replace("123","def",$a); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d abcdef diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-02.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-02.phpt index 8fec03b486af..b9bebcf8d71f 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-02.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-02.phpt @@ -12,5 +12,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab $a="abc123"; echo mb_ereg_replace("123","",$a); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d abc diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-03.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-03.phpt index 0e589a8665bb..167eb6986c87 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-03.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-03.phpt @@ -12,5 +12,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab $a="\\'test"; echo mb_ereg_replace("\\\\'","'",$a); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d 'test diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-04.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-04.phpt index a9a4fc953281..70d29c8d1805 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-04.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-04.phpt @@ -12,5 +12,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab $a="This is a nice and simple string"; echo mb_ereg_replace("^This","That",$a); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d That is a nice and simple string diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-05.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-05.phpt index 4e15d8bfb8eb..0eaa22615b10 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-05.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-05.phpt @@ -13,5 +13,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab $b=mb_ereg_replace("abcd","",$a); echo "strlen(\$b)=".strlen($b); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d strlen($b)=0 diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-06.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-06.phpt index 396c8e870a26..6c169388387f 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-06.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-06.phpt @@ -11,5 +11,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab /* (counterpart: ext/standard/tests/reg/008.phpt) */ echo mb_ereg_replace("([a-z]*)([-=+|]*)([0-9]+)","\\3 \\1 \\2\n","abc+-|=123"); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d 123 abc +-|= diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-07.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-07.phpt index f0cd2edfe273..bc5ff9a6ca30 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-07.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-07.phpt @@ -12,5 +12,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab $a="abc122222222223"; echo mb_ereg_replace("1(2*)3","\\1def\\1",$a); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d abc2222222222def2222222222 diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-08.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-08.phpt index 8e23aba9cff9..8368e21a2a88 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-08.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-08.phpt @@ -12,5 +12,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab $a="abc123"; echo mb_ereg_replace("123","def\\0ghi",$a); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d abcdef123ghi diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-09.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-09.phpt index cb8fce9135d8..efa1eb0e9dc4 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-09.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-09.phpt @@ -12,5 +12,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab $a="abc123"; echo mb_ereg_replace("123",'def\1ghi',$a); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d abcdef\1ghi diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-10.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-10.phpt index 96c762bc4d5f..d280e93574cb 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-10.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-10.phpt @@ -12,5 +12,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab $a="abc123"; echo mb_ereg_replace("123","def\\g\\\\hi\\",$a); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d abcdef\g\\hi\ diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-11.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-11.phpt index a88f358b4b55..8f4b4fe5b295 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-11.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-11.phpt @@ -12,5 +12,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab $a="a\\2bxc"; echo mb_ereg_replace("a(.*)b(.*)c","\\1",$a); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d \2 diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-12.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-12.phpt index 0f371053e508..1dbc9a0416de 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-12.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-12.phpt @@ -11,5 +11,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab /* (counterpart: ext/standard/tests/reg/015.phpt) */ echo mb_ereg_replace("^","z","abc123"); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d zabc123 diff --git a/ext/mbstring/tests/mb_ereg_replace-compat-13.phpt b/ext/mbstring/tests/mb_ereg_replace-compat-13.phpt index a271eeff405c..4e6eaf017bc4 100644 --- a/ext/mbstring/tests/mb_ereg_replace-compat-13.phpt +++ b/ext/mbstring/tests/mb_ereg_replace-compat-13.phpt @@ -11,5 +11,6 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab /* (counterpart: ext/standard/tests/reg/016.phpt) */ echo mb_ereg_replace('\?',"abc","?123?"); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d abc123abc diff --git a/ext/mbstring/tests/mb_ereg_replace.phpt b/ext/mbstring/tests/mb_ereg_replace.phpt index 455b13e23ce5..1221b6b366b0 100644 --- a/ext/mbstring/tests/mb_ereg_replace.phpt +++ b/ext/mbstring/tests/mb_ereg_replace.phpt @@ -12,6 +12,11 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab print mb_ereg_replace( ' ', '-', 'a b c d e' )."\n"; print mb_ereg_replace( '([a-z]+)','[\\1]', 'abc def ghi' ); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d a-b-c-d-e + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d [abc] [def] [ghi] diff --git a/ext/mbstring/tests/mb_ereg_replace_basic.phpt b/ext/mbstring/tests/mb_ereg_replace_basic.phpt index a3f18952d375..7faac7371abd 100644 --- a/ext/mbstring/tests/mb_ereg_replace_basic.phpt +++ b/ext/mbstring/tests/mb_ereg_replace_basic.phpt @@ -41,18 +41,28 @@ var_dump(bin2hex($result_4)); echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing mb_ereg_replace() : basic functionality *** +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + -- ASCII string 1 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(16) "6162632020313233" -- ASCII string 2 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(14) "61626320646566" -- Multibyte string 1 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(72) "e697a5e69cace8aa9e5f5f5f5f5f31323334efbc95efbc96efbc97efbc98efbc99e38082" -- Multibyte string 2 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(106) "e697a5e69cace8aa9ee38386e382ade382b9e38388e381a7e38199e380823031323334efbc95efbc96efbc97efbc98efbc99e38082" Done diff --git a/ext/mbstring/tests/mb_ereg_replace_callback.phpt b/ext/mbstring/tests/mb_ereg_replace_callback.phpt index 40120c03eab2..d4541d09cafa 100644 --- a/ext/mbstring/tests/mb_ereg_replace_callback.phpt +++ b/ext/mbstring/tests/mb_ereg_replace_callback.phpt @@ -18,6 +18,9 @@ echo mb_ereg_replace_callback('(?\w+) (?\d+).*', function ($m) { return sprintf("%s-%s", $m['digit'], $m['word']); }, $str), "\n"; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace_callback() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d abc(3) 123(3) #",;(4) $foo(4) + +Deprecated: Function mb_ereg_replace_callback() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d 123-abc diff --git a/ext/mbstring/tests/mb_ereg_replace_named_subpatterns.phpt b/ext/mbstring/tests/mb_ereg_replace_named_subpatterns.phpt index 7e4d8e2e4940..8a58e95dffdf 100644 --- a/ext/mbstring/tests/mb_ereg_replace_named_subpatterns.phpt +++ b/ext/mbstring/tests/mb_ereg_replace_named_subpatterns.phpt @@ -26,12 +26,29 @@ function_exists('mb_ereg_replace') or die("skip mb_ereg_replace() is not availab // An unclosed backref is ignored echo mb_ereg_replace('(?\w+)', '-\k ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d _a_ _b_ _c_ _d_ _e_ + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d 123456789aa-a- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d 123456789aa-a- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d a_\k<01> + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d -\1- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d -\k<>- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d -\k ---EXPECT-- +--EXPECTF-- *** Testing mb_ereg_replace() : usage variations *** -- Iteration 1 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "string_val" -- Iteration 2 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "string_val" -- Iteration 3 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "string_val" -- Iteration 4 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "string_val" -- Iteration 5 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "string_val" -- Iteration 6 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "string_val" -- Iteration 7 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "string_val" -- Iteration 8 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "string_val" -- Iteration 9 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "string_val" -- Iteration 10 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(120) "string_valsstring_valtstring_valrstring_valistring_valnstring_valgstring_val_string_valvstring_valastring_vallstring_val" -- Iteration 11 -- + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "string_val" Done diff --git a/ext/mbstring/tests/mb_ereg_search.phpt b/ext/mbstring/tests/mb_ereg_search.phpt index f167b9d7607d..25f581b7dd8d 100644 --- a/ext/mbstring/tests/mb_ereg_search.phpt +++ b/ext/mbstring/tests/mb_ereg_search.phpt @@ -32,9 +32,26 @@ function_exists('mb_ereg_search') or die("skip mb_ereg_search() is not available while($r); } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(9) "中国abc" + +Deprecated: Function mb_ereg_search_regs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(3) "abc" + +Deprecated: Function mb_ereg_search_regs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "字符" + +Deprecated: Function mb_ereg_search_regs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "china" + +Deprecated: Function mb_ereg_search_regs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "string" + +Deprecated: Function mb_ereg_search_regs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d diff --git a/ext/mbstring/tests/mb_ereg_search_invalid_pattern.phpt b/ext/mbstring/tests/mb_ereg_search_invalid_pattern.phpt index 6979816fe8e0..33ad520e5fe6 100644 --- a/ext/mbstring/tests/mb_ereg_search_invalid_pattern.phpt +++ b/ext/mbstring/tests/mb_ereg_search_invalid_pattern.phpt @@ -14,8 +14,15 @@ var_dump(mb_ereg_search_getregs()); ?> --EXPECTF-- +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + Warning: mb_ereg_search(): Pattern is not valid under UTF-8 encoding in %s on line %d bool(false) + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) diff --git a/ext/mbstring/tests/mb_ereg_search_named_subpatterns_1.phpt b/ext/mbstring/tests/mb_ereg_search_named_subpatterns_1.phpt index 03161cf27cec..bcf73b6b3f40 100644 --- a/ext/mbstring/tests/mb_ereg_search_named_subpatterns_1.phpt +++ b/ext/mbstring/tests/mb_ereg_search_named_subpatterns_1.phpt @@ -5,7 +5,7 @@ mbstring --SKIPIF-- --FILE-- =') or die("skip requires oniguruma >= 6.9.4"); +@version_compare(MB_ONIGURUMA_VERSION, '6.9.4', '>=') or die("skip requires oniguruma >= 6.9.4"); ?> --FILE-- =') or die("skip requires onigu mb_ereg_search('(?\s*)(?\w+)(?[?!])'); var_dump(mb_ereg_search_getregs()); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(7) { [0]=> string(11) " 中国?" diff --git a/ext/mbstring/tests/mb_ereg_search_pos.phpt b/ext/mbstring/tests/mb_ereg_search_pos.phpt index 7f8f99f0c5d2..2c88cff01e04 100644 --- a/ext/mbstring/tests/mb_ereg_search_pos.phpt +++ b/ext/mbstring/tests/mb_ereg_search_pos.phpt @@ -22,7 +22,12 @@ else{ var_dump("false"); } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(2) { [0]=> int(5) diff --git a/ext/mbstring/tests/mb_ereg_search_regs.phpt b/ext/mbstring/tests/mb_ereg_search_regs.phpt index eb883387e620..dd28df903729 100644 --- a/ext/mbstring/tests/mb_ereg_search_regs.phpt +++ b/ext/mbstring/tests/mb_ereg_search_regs.phpt @@ -22,7 +22,10 @@ function_exists('mb_ereg_search_regs') or die("skip mb_ereg_search_regs() not av ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_regs() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(1) { [0]=> string(4) "ов" diff --git a/ext/mbstring/tests/mb_ereg_search_setpos.phpt b/ext/mbstring/tests/mb_ereg_search_setpos.phpt index 6a953447174d..6153d86282df 100644 --- a/ext/mbstring/tests/mb_ereg_search_setpos.phpt +++ b/ext/mbstring/tests/mb_ereg_search_setpos.phpt @@ -35,38 +35,77 @@ foreach($positions as $pos) { } } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_ereg_search_setpos(): Argument #1 ($offset) is out of range +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + * Position: 5 : + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(5) * Position: 20 : + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(20) * Position: 21 : + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_ereg_search_setpos(): Argument #1 ($offset) is out of range + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(20) * Position: 25 : + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_ereg_search_setpos(): Argument #1 ($offset) is out of range + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(20) * Position: 0 : + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(0) * Position: -5 : + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(15) * Position: -20 : + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(0) * Position: -30 : + +Deprecated: Function mb_ereg_search_setpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_ereg_search_setpos(): Argument #1 ($offset) is out of range + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d int(0) diff --git a/ext/mbstring/tests/mb_ereg_search_syntax.phpt b/ext/mbstring/tests/mb_ereg_search_syntax.phpt index 5e16a91c3695..f6b427a3c7e6 100644 --- a/ext/mbstring/tests/mb_ereg_search_syntax.phpt +++ b/ext/mbstring/tests/mb_ereg_search_syntax.phpt @@ -13,5 +13,8 @@ mb_ereg_search_init("a"); var_dump(mb_ereg_search("a\\{1,2\\}", "b")); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_search() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) diff --git a/ext/mbstring/tests/mb_ereg_search_xxx.phpt b/ext/mbstring/tests/mb_ereg_search_xxx.phpt index 238dcc0e87a5..33d21052ecd3 100644 --- a/ext/mbstring/tests/mb_ereg_search_xxx.phpt +++ b/ext/mbstring/tests/mb_ereg_search_xxx.phpt @@ -35,48 +35,409 @@ output_handler= do_tests( $enc, 'x' ); } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (10) + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (5) abcde + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (14) abdeabcf + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (22) abc + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (31) abcd + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (5) ϡ + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (10) + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (5) abcde + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (14) abdeabcf + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (22) abc + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (EUC-JP) (31) abcd + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (10) + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (5) abcde + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (14) abdeabcf + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (22) abc + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (31) abcd + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (5) ϡ + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (10) + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (5) abcde + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (14) abdeabcf + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (22) abc + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (Shift_JIS) (31) abcd + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (10) + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (5) abcde + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (14) abdeabcf + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (22) abc + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (31) abcd + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (5) ϡ + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (10) + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (5) abcde + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (14) abdeabcf + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (22) abc + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (SJIS) (31) abcd + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (14) + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (5) abcde + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (14) abdeabcf + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (22) abc + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (31) abcd + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (7) ϡ + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (14) + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_init() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (5) abcde + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (14) abdeabcf + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (22) abc + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getregs() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_search_getpos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d (UTF-8) (31) abcd + +Deprecated: Function mb_ereg_search_pos() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d diff --git a/ext/mbstring/tests/mb_ereg_variation3.phpt b/ext/mbstring/tests/mb_ereg_variation3.phpt index 329c5597d440..5735e8248ef2 100644 --- a/ext/mbstring/tests/mb_ereg_variation3.phpt +++ b/ext/mbstring/tests/mb_ereg_variation3.phpt @@ -5,7 +5,7 @@ mbstring --SKIPIF-- =') or die("skip requires oniguruma >= 6.1.0"); +@version_compare(MB_ONIGURUMA_VERSION, '6.1.0', '>=') or die("skip requires oniguruma >= 6.1.0"); ?> --FILE-- ---EXPECT-- +--EXPECTF-- *** Testing mb_ereg() : variation *** +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + -- Iteration 1 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -77,6 +81,8 @@ array(1) { } -- Iteration 2 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -84,6 +90,8 @@ array(1) { } -- Iteration 3 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -91,6 +99,8 @@ array(1) { } -- Iteration 4 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -98,6 +108,8 @@ array(1) { } -- Iteration 5 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -105,6 +117,8 @@ array(1) { } -- Iteration 6 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -112,6 +126,8 @@ array(1) { } -- Iteration 7 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -119,6 +135,8 @@ array(1) { } -- Iteration 8 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -126,6 +144,8 @@ array(1) { } -- Iteration 9 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -133,6 +153,8 @@ array(1) { } -- Iteration 10 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -140,6 +162,8 @@ array(1) { } -- Iteration 11 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -147,6 +171,8 @@ array(1) { } -- Iteration 12 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> diff --git a/ext/mbstring/tests/mb_ereg_variation4.phpt b/ext/mbstring/tests/mb_ereg_variation4.phpt index 05b96a9bf40f..559c5fd467cd 100644 --- a/ext/mbstring/tests/mb_ereg_variation4.phpt +++ b/ext/mbstring/tests/mb_ereg_variation4.phpt @@ -69,10 +69,14 @@ function base64_encode_var_dump($regs) { echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing mb_ereg() : usage variations *** +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + -- Iteration 1 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(true) array(1) { [0]=> @@ -80,6 +84,8 @@ array(1) { } -- Iteration 2 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(true) array(1) { [0]=> @@ -87,6 +93,8 @@ array(1) { } -- Iteration 3 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(true) array(1) { [0]=> @@ -94,12 +102,18 @@ array(1) { } -- Iteration 4 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(false) -- Iteration 5 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(false) -- Iteration 6 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(true) array(1) { [0]=> @@ -107,6 +121,8 @@ array(1) { } -- Iteration 7 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(true) array(1) { [0]=> @@ -114,9 +130,13 @@ array(1) { } -- Iteration 8 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(false) -- Iteration 9 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(true) array(1) { [0]=> @@ -124,6 +144,8 @@ array(1) { } -- Iteration 10 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(true) array(1) { [0]=> @@ -131,12 +153,18 @@ array(1) { } -- Iteration 11 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(false) -- Iteration 12 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(false) -- Iteration 13 -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d bool(true) array(1) { [0]=> diff --git a/ext/mbstring/tests/mb_ereg_variation5.phpt b/ext/mbstring/tests/mb_ereg_variation5.phpt index 569162534dcd..37cb325bf84b 100644 --- a/ext/mbstring/tests/mb_ereg_variation5.phpt +++ b/ext/mbstring/tests/mb_ereg_variation5.phpt @@ -57,19 +57,29 @@ function base64_encode_var_dump($regs) { } } ?> ---EXPECT-- +--EXPECTF-- *** Testing mb_ereg() : usage variations *** -ASCII String without $regs arg: bool(true) +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +ASCII String without $regs arg: +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d +bool(true) ASCII String with $regs arg: + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> string(52) "VGhpcyBpcyBhbiBFbmdsaXNoIHN0cmluZy4gMDEyMzQ1Njc4OS4=" } -Multibyte String without $regs arg: bool(true) +Multibyte String without $regs arg: +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d +bool(true) Multubyte String with $regs arg: + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> diff --git a/ext/mbstring/tests/mb_ereg_variation6.phpt b/ext/mbstring/tests/mb_ereg_variation6.phpt index cea3fcf661b2..33df1eaf051a 100644 --- a/ext/mbstring/tests/mb_ereg_variation6.phpt +++ b/ext/mbstring/tests/mb_ereg_variation6.phpt @@ -73,18 +73,24 @@ function base64_encode_var_dump($regs) { echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing mb_ereg() : usage variations *** + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d Regex encoding set to utf-8 --** Pattern is: \w+ **-- -- ASCII String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> string(8) "VGhpcw==" } -- Multibyte String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -93,12 +99,16 @@ array(1) { --** Pattern is: \W+ **-- -- ASCII String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> string(4) "IA==" } -- Multibyte String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -107,23 +117,31 @@ array(1) { --** Pattern is: \s+ **-- -- ASCII String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> string(4) "IA==" } -- Multibyte String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) NULL --** Pattern is: \S+ **-- -- ASCII String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> string(8) "VGhpcw==" } -- Multibyte String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -132,12 +150,16 @@ array(1) { --** Pattern is: \d+ **-- -- ASCII String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> string(16) "MDEyMzQ1Njc4OQ==" } -- Multibyte String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -146,12 +168,16 @@ array(1) { --** Pattern is: \D+ **-- -- ASCII String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> string(36) "VGhpcyBpcyBhbiBFbmdsaXNoIHN0cmluZy4g" } -- Multibyte String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -160,12 +186,16 @@ array(1) { --** Pattern is: \b **-- -- ASCII String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> bool(false) } -- Multibyte String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> @@ -174,12 +204,16 @@ array(1) { --** Pattern is: \B **-- -- ASCII String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> bool(false) } -- Multibyte String: -- + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(1) { [0]=> diff --git a/ext/mbstring/tests/mb_ereg_variation7.phpt b/ext/mbstring/tests/mb_ereg_variation7.phpt index 832bbbfb6f3b..8e6ad836668a 100644 --- a/ext/mbstring/tests/mb_ereg_variation7.phpt +++ b/ext/mbstring/tests/mb_ereg_variation7.phpt @@ -58,9 +58,13 @@ function base64_encode_var_dump($regs) { echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing mb_ereg() : usage variations *** + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d Regex encoding set to utf-8 + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(5) { [0]=> @@ -74,6 +78,8 @@ array(5) { [4]=> string(4) "ODk=" } + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) array(5) { [0]=> diff --git a/ext/mbstring/tests/mb_eregi.phpt b/ext/mbstring/tests/mb_eregi.phpt index 2cdef03a6cb4..1660f154fbf1 100644 --- a/ext/mbstring/tests/mb_eregi.phpt +++ b/ext/mbstring/tests/mb_eregi.phpt @@ -15,7 +15,14 @@ var_dump(mb_eregi('z', 'XYZ')); var_dump(mb_eregi('xyzp', 'XYZ')); var_dump(mb_eregi('ö', 'Öäü')); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_eregi() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) + +Deprecated: Function mb_eregi() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) diff --git a/ext/mbstring/tests/mb_eregi_replace.phpt b/ext/mbstring/tests/mb_eregi_replace.phpt index b03874701f95..89963c922761 100644 --- a/ext/mbstring/tests/mb_eregi_replace.phpt +++ b/ext/mbstring/tests/mb_eregi_replace.phpt @@ -33,7 +33,140 @@ function do_translit($st) { mb_regex_encoding('ISO-8859-1'); echo do_translit("Пеар"); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_eregi_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d Pear --CREDITS-- Testfest Wuerzburg 2009-06-20 (modified by rui 2011-10-15) diff --git a/ext/mbstring/tests/mb_regex_encoding_basic.phpt b/ext/mbstring/tests/mb_regex_encoding_basic.phpt index 8492369aceff..745cb0cf021b 100644 --- a/ext/mbstring/tests/mb_regex_encoding_basic.phpt +++ b/ext/mbstring/tests/mb_regex_encoding_basic.phpt @@ -25,7 +25,13 @@ echo "Done"; ?> --EXPECTF-- *** Testing mb_regex_encoding() : basic functionality *** + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(%d) "%s" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UTF-8" Done diff --git a/ext/mbstring/tests/mb_regex_encoding_error2.phpt b/ext/mbstring/tests/mb_regex_encoding_error2.phpt index 6874f5379a6a..ee665e5aa8bd 100644 --- a/ext/mbstring/tests/mb_regex_encoding_error2.phpt +++ b/ext/mbstring/tests/mb_regex_encoding_error2.phpt @@ -21,6 +21,8 @@ try { } ?> ---EXPECT-- +--EXPECTF-- *** Testing mb_regex_encoding() : error conditions *** + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line 9 mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "unknown" given diff --git a/ext/mbstring/tests/mb_regex_encoding_variation2.phpt b/ext/mbstring/tests/mb_regex_encoding_variation2.phpt index 5d46b3ad7936..55d139eb3ad7 100644 --- a/ext/mbstring/tests/mb_regex_encoding_variation2.phpt +++ b/ext/mbstring/tests/mb_regex_encoding_variation2.phpt @@ -87,286 +87,622 @@ foreach($encoding as $enc) { } echo "Done"; ?> ---EXPECT-- +--EXPECTF-- *** Testing mb_regex_encoding() : usage variations *** -- Iteration 1 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UTF-8" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UCS-4" -- Iteration 2 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UCS-4" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "UCS-4BE" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UCS-4" -- Iteration 3 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UCS-4" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "UCS-4LE" -- Iteration 4 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "UCS-4LE" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "UCS-2" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "UCS-4LE" -- Iteration 5 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "UCS-4LE" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "UCS-2BE" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "UCS-4LE" -- Iteration 6 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "UCS-4LE" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "UCS-2LE" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "UCS-4LE" -- Iteration 7 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "UCS-4LE" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UCS-4" -- Iteration 8 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UCS-4" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UCS-4" -- Iteration 9 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UCS-4" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "UCS-4LE" -- Iteration 10 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(7) "UCS-4LE" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "UTF-16" -- Iteration 11 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "UTF-16" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "UTF-16" -- Iteration 12 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "UTF-16" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(8) "UTF-16LE" -- Iteration 13 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(8) "UTF-16LE" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "UTF-7" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(8) "UTF-16LE" -- Iteration 14 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(8) "UTF-16LE" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "UTF7-IMAP" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(8) "UTF-16LE" -- Iteration 15 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(8) "UTF-16LE" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UTF-8" -- Iteration 16 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "UTF-8" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "ASCII" -- Iteration 17 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "ASCII" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-JP" -- Iteration 18 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-JP" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "SJIS" -- Iteration 19 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "SJIS" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-JP" -- Iteration 20 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-JP" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "SJIS" -- Iteration 21 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "SJIS" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "ISO-2022-JP" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "SJIS" -- Iteration 22 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "SJIS" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "JIS" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "SJIS" -- Iteration 23 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "SJIS" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-1" -- Iteration 24 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-1" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-2" -- Iteration 25 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-2" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-3" -- Iteration 26 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-3" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-4" -- Iteration 27 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-4" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-5" -- Iteration 28 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-5" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-6" -- Iteration 29 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-6" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-7" -- Iteration 30 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-7" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-8" -- Iteration 31 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-8" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-9" -- Iteration 32 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(10) "ISO-8859-9" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-10" -- Iteration 33 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-10" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-13" -- Iteration 34 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-13" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-14" -- Iteration 35 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-14" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" -- Iteration 36 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "byte2be" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" -- Iteration 37 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "byte2le" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" -- Iteration 38 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "byte4be" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" -- Iteration 39 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "byte4le" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" -- Iteration 40 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "BASE64" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" -- Iteration 41 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "HTML-ENTITIES" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" -- Iteration 42 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "7bit" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" -- Iteration 43 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "8bit" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" -- Iteration 44 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(11) "ISO-8859-15" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-CN" -- Iteration 45 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-CN" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "CP936" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-CN" -- Iteration 46 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-CN" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "HZ" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-CN" -- Iteration 47 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-CN" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-TW" -- Iteration 48 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-TW" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "CP950" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-TW" -- Iteration 49 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-TW" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "BIG5" -- Iteration 50 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "BIG5" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" -- Iteration 51 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "UHC" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" -- Iteration 52 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "ISO-2022-KR" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" -- Iteration 53 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "Windows-1251" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" -- Iteration 54 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "Windows-1252" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" -- Iteration 55 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d mb_regex_encoding(): Argument #1 ($encoding) must be a valid encoding, "CP866" given + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" -- Iteration 56 -- + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(6) "EUC-KR" + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(5) "KOI8R" Done diff --git a/ext/mbstring/tests/mb_regex_set_options.phpt b/ext/mbstring/tests/mb_regex_set_options.phpt index a5fb611cacf3..f6994049f502 100644 --- a/ext/mbstring/tests/mb_regex_set_options.phpt +++ b/ext/mbstring/tests/mb_regex_set_options.phpt @@ -14,5 +14,12 @@ function_exists('mb_regex_set_options') or die("skip\n"); mb_regex_set_options( '' ); print mb_ereg_replace(' -', '+', '- - - - -' ); ?> ---EXPECT-- -+ + + + +-++++ +--EXPECTF-- +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s line %d + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d ++ + + + + +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d +-++++ diff --git a/ext/mbstring/tests/mb_split-compat-01.phpt b/ext/mbstring/tests/mb_split-compat-01.phpt index 69d398cb0de3..8c5dcc0b4f10 100644 --- a/ext/mbstring/tests/mb_split-compat-01.phpt +++ b/ext/mbstring/tests/mb_split-compat-01.phpt @@ -16,7 +16,8 @@ test"); echo $a[$i] . "\n"; } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d 4 this is diff --git a/ext/mbstring/tests/mb_split.phpt b/ext/mbstring/tests/mb_split.phpt index 208848ee2bf5..8b8df7411a81 100644 --- a/ext/mbstring/tests/mb_split.phpt +++ b/ext/mbstring/tests/mb_split.phpt @@ -33,13 +33,36 @@ function_exists('mb_split') or die("skip mb_split() is not available in this bui verify_split( "\xa1\xa1+", "\xa1\xa1\xa1\xa2\xa2\xa1\xa1\xa1\xa1\xa1\xa1\xa2\xa2\xa1\xa1\xa1", $i ); } ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_regex_encoding() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d ok + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d ok + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d ok + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d ok + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d ok + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d 2-2 + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d 3-3 + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d 4-4 diff --git a/ext/mbstring/tests/mb_split_empty_match.phpt b/ext/mbstring/tests/mb_split_empty_match.phpt index 56ee59c31c6d..14604adfa98b 100644 --- a/ext/mbstring/tests/mb_split_empty_match.phpt +++ b/ext/mbstring/tests/mb_split_empty_match.phpt @@ -11,7 +11,10 @@ function_exists('mb_split') or die("skip mb_split() is not available in this bui mb_regex_set_options('m'); var_dump(mb_split('^', "a\nb\nc")); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_regex_set_options() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + +Deprecated: Function mb_split() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d array(3) { [0]=> string(2) "a diff --git a/ext/mbstring/tests/mbregex_stack_limit.phpt b/ext/mbstring/tests/mbregex_stack_limit.phpt index a291c280b18d..5b1ac5b2f080 100644 --- a/ext/mbstring/tests/mbregex_stack_limit.phpt +++ b/ext/mbstring/tests/mbregex_stack_limit.phpt @@ -5,7 +5,7 @@ mbstring --SKIPIF-- @@ -24,8 +24,13 @@ var_dump(mb_ereg('\\s+$', $s)); echo 'OK'; ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) OK diff --git a/ext/mbstring/tests/mbregex_stack_limit2.phpt b/ext/mbstring/tests/mbregex_stack_limit2.phpt index 9c2efcc39ec2..e7860654f3c0 100644 --- a/ext/mbstring/tests/mbregex_stack_limit2.phpt +++ b/ext/mbstring/tests/mbregex_stack_limit2.phpt @@ -6,7 +6,7 @@ iconv --SKIPIF-- @@ -28,6 +28,10 @@ var_dump(mb_trim_regex(str_repeat(' ', 10000))); echo 'OK'; ?> --EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d + Warning: mb_ereg_replace(): mbregex search failure in php_mbereg_replace_exec(): match-stack limit over in %s on line %d + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(0) "" OK diff --git a/ext/mbstring/tests/php_gr_jp_10830.phpt b/ext/mbstring/tests/php_gr_jp_10830.phpt index 02b46c01d3db..4b7517fd6e0f 100644 --- a/ext/mbstring/tests/php_gr_jp_10830.phpt +++ b/ext/mbstring/tests/php_gr_jp_10830.phpt @@ -13,6 +13,7 @@ $a="aaa\n<>"; var_dump( mb_ereg("^[^><]+$",$a) ); var_dump( !!preg_match("/^[^><]+$/",$a) ); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) bool(false) diff --git a/ext/mbstring/tests/php_gr_jp_dev_884-1.phpt b/ext/mbstring/tests/php_gr_jp_dev_884-1.phpt index b9d82bf9af9f..4bf317499527 100644 --- a/ext/mbstring/tests/php_gr_jp_dev_884-1.phpt +++ b/ext/mbstring/tests/php_gr_jp_dev_884-1.phpt @@ -12,6 +12,8 @@ set_time_limit(2); var_dump(preg_replace("/.*/", "b", "a")); var_dump(mb_ereg_replace(".*", "b", "a")); ?> ---EXPECT-- +--EXPECTF-- string(2) "bb" + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(2) "bb" diff --git a/ext/mbstring/tests/php_gr_jp_dev_884-2.phpt b/ext/mbstring/tests/php_gr_jp_dev_884-2.phpt index 51086285ed61..405a9f6bf96f 100644 --- a/ext/mbstring/tests/php_gr_jp_dev_884-2.phpt +++ b/ext/mbstring/tests/php_gr_jp_dev_884-2.phpt @@ -13,8 +13,11 @@ var_dump(preg_replace("/C?$/", "Z", "ABC")); var_dump(mb_ereg_replace("C*$", "Z", "ABC")); var_dump(preg_replace("/C*$/", "Z", "ABC")); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "ABZZ" string(4) "ABZZ" + +Deprecated: Function mb_ereg_replace() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d string(4) "ABZZ" string(4) "ABZZ" diff --git a/ext/mbstring/tests/retry_limit.phpt b/ext/mbstring/tests/retry_limit.phpt index bcb1a1def5c0..1ada020e68c6 100644 --- a/ext/mbstring/tests/retry_limit.phpt +++ b/ext/mbstring/tests/retry_limit.phpt @@ -5,7 +5,7 @@ mbstring --SKIPIF-- = 6.9.3'); } ?> @@ -19,6 +19,9 @@ ini_set('mbstring.regex_retry_limit', '100000'); var_dump(mb_ereg($regex, $str)); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(true) + +Deprecated: Function mb_ereg() is deprecated since 8.6, because the underlying library is no longer maintained in %s on line %d bool(false) diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index c7d349706e45..b45e7416c773 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -479,7 +479,7 @@ PHP_MINIT_FUNCTION(mysqli) REGISTER_INI_ENTRIES(); memcpy(&mysqli_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - mysqli_object_handlers.offset = XtOffsetOf(mysqli_object, zo); + mysqli_object_handlers.offset = offsetof(mysqli_object, zo); mysqli_object_handlers.free_obj = mysqli_objects_free_storage; mysqli_object_handlers.clone_obj = NULL; mysqli_object_handlers.read_property = mysqli_read_property; diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index c667712f85fe..526fd10b2623 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -530,6 +530,10 @@ PHP_FUNCTION(mysqli_execute_query) MYSQLND_PARAM_BIND *params; if (!zend_array_is_list(input_params)) { + if (stmt->query) { + efree(stmt->query); + stmt->query = NULL; + } mysqli_stmt_close(stmt->stmt, false); stmt->stmt = NULL; efree(stmt); @@ -540,6 +544,10 @@ PHP_FUNCTION(mysqli_execute_query) hash_num_elements = zend_hash_num_elements(input_params); param_count = mysql_stmt_param_count(stmt->stmt); if (hash_num_elements != param_count) { + if (stmt->query) { + efree(stmt->query); + stmt->query = NULL; + } mysqli_stmt_close(stmt->stmt, false); stmt->stmt = NULL; efree(stmt); diff --git a/ext/mysqli/php_mysqli_structs.h b/ext/mysqli/php_mysqli_structs.h index 40d3f7fcdeef..8ccead1a7518 100644 --- a/ext/mysqli/php_mysqli_structs.h +++ b/ext/mysqli/php_mysqli_structs.h @@ -61,9 +61,7 @@ typedef struct _mysqli_object { zend_object zo; } mysqli_object; /* extends zend_object */ -static inline mysqli_object *php_mysqli_fetch_object(zend_object *obj) { - return (mysqli_object *)((char*)(obj) - XtOffsetOf(mysqli_object, zo)); -} +#define php_mysqli_fetch_object(obj) ZEND_CONTAINER_OF(obj, mysqli_object, zo) #define Z_MYSQLI_P(zv) php_mysqli_fetch_object(Z_OBJ_P((zv))) diff --git a/ext/mysqli/tests/mysqli_execute_query_leak.phpt b/ext/mysqli/tests/mysqli_execute_query_leak.phpt new file mode 100644 index 000000000000..11f56877aece --- /dev/null +++ b/ext/mysqli/tests/mysqli_execute_query_leak.phpt @@ -0,0 +1,37 @@ +--TEST-- +mysqli_execute_query() does not leak stmt->query on input_params validation errors with MYSQLI_REPORT_INDEX +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--FILE-- +execute_query('SELECT label, ? AS anon, ? AS num FROM test WHERE id=?', ['foo', 42]); +} catch (ValueError $e) { + echo '[001] '.$e->getMessage()."\n"; +} + +try { + $link->execute_query('SELECT label, ? AS anon, ? AS num FROM test WHERE id=?', ['foo' => 42]); +} catch (ValueError $e) { + echo '[002] '.$e->getMessage()."\n"; +} + +print "done!"; +?> +--CLEAN-- + +--EXPECT-- +[001] mysqli::execute_query(): Argument #2 ($params) must consist of exactly 3 elements, 2 present +[002] mysqli::execute_query(): Argument #2 ($params) must be a list array +done! diff --git a/ext/mysqli/tests/mysqli_real_connect_retry_attr.phpt b/ext/mysqli/tests/mysqli_real_connect_retry_attr.phpt new file mode 100644 index 000000000000..7f7731807428 --- /dev/null +++ b/ext/mysqli/tests/mysqli_real_connect_retry_attr.phpt @@ -0,0 +1,26 @@ +--TEST-- +mysqli_real_connect() retry on same handle does not corrupt mysqlnd connect_attr +--EXTENSIONS-- +mysqli +--SKIPIF-- + +--FILE-- + +--EXPECT-- +done! diff --git a/ext/mysqlnd/mysqlnd_alloc.c b/ext/mysqlnd/mysqlnd_alloc.c index 12fa5fb723ae..187298ca9969 100644 --- a/ext/mysqlnd/mysqlnd_alloc.c +++ b/ext/mysqlnd/mysqlnd_alloc.c @@ -199,7 +199,7 @@ static void _mysqlnd_efree(void *ptr MYSQLND_MEM_D) #if PHP_DEBUG { - char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); + const char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno); } #endif @@ -230,7 +230,7 @@ static void _mysqlnd_pefree(void *ptr, bool persistent MYSQLND_MEM_D) #if PHP_DEBUG { - char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); + const char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno); } #endif @@ -262,7 +262,7 @@ static char * _mysqlnd_pememdup(const char * const ptr, size_t length, bool pers #if PHP_DEBUG { - char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); + const char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno); } #endif @@ -293,7 +293,7 @@ static char * _mysqlnd_pestrndup(const char * const ptr, size_t length, bool per #if PHP_DEBUG { - char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); + const char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno); } #endif @@ -334,7 +334,7 @@ static char * _mysqlnd_pestrdup(const char * const ptr, bool persistent MYSQLND_ TRACE_ALLOC_ENTER(mysqlnd_pestrdup_name); #if PHP_DEBUG { - char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); + const char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno); } #endif diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index e3ca87bd7131..517b149c0b4d 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -550,7 +550,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_ /* IPv6 without square brackets so without port */ transport.l = mnd_sprintf(&transport.s, 0, "tcp://[%s]:%u", hostname.s, port); } else { - char *p; + const char *p; /* IPv6 addresses are in the format [address]:port */ if (hostname.s[0] == '[') { /* IPv6 */ @@ -1557,17 +1557,14 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * cons zval attrz; zend_string *str; + str = zend_string_init(key, strlen(key), conn->persistent); + ZVAL_NEW_STR(&attrz, zend_string_init(value, strlen(value), conn->persistent)); if (conn->persistent) { - str = zend_string_init(key, strlen(key), 1); GC_MAKE_PERSISTENT_LOCAL(str); - ZVAL_NEW_STR(&attrz, zend_string_init(value, strlen(value), 1)); GC_MAKE_PERSISTENT_LOCAL(Z_COUNTED(attrz)); - } else { - str = zend_string_init(key, strlen(key), 0); - ZVAL_NEW_STR(&attrz, zend_string_init(value, strlen(value), 0)); } zend_hash_update(conn->options->connect_attr, str, &attrz); - zend_string_release_ex(str, 1); + zend_string_release_ex(str, conn->persistent); } break; default: diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index 1d43946f41d3..b4d0bdbd8d2c 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -720,9 +720,6 @@ MYSQLND_METHOD(mysqlnd_res, store_result_fetch_data)(MYSQLND_CONN_DATA * const c if (ret == FAIL) { COPY_CLIENT_ERROR(&set->error_info, row_packet.error_info); - } else { - /* libmysql's documentation says it should be so for SELECT statements */ - UPSERT_STATUS_SET_AFFECTED_ROWS(conn->upsert_status, set->row_count); } DBG_INF_FMT("ret=%s row_count=%u warnings=%u server_status=%u", ret == PASS? "PASS":"FAIL", @@ -1002,18 +999,11 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const unsigned int } } if (flags & MYSQLND_FETCH_ASSOC) { - /* zend_hash_quick_update needs length + trailing zero */ - /* QQ: Error handling ? */ - /* - zend_hash_quick_update does not check, as add_assoc_zval_ex do, whether - the index is a numeric and convert it to it. This however means constant - hashing of the column name, which is not needed as it can be precomputed. - */ Z_TRY_ADDREF_P(data); - if (meta->fields[i].is_numeric == FALSE) { - zend_hash_update(row_ht, meta->fields[i].sname, data); + if (field->is_numeric == FALSE) { + zend_hash_update(row_ht, field->sname, data); } else { - zend_hash_index_update(row_ht, meta->fields[i].num_key, data); + zend_hash_index_update(row_ht, field->num_key, data); } } @@ -1036,10 +1026,9 @@ MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result) mysqlnd_result_free_prev_data(result); if (result->m.fetch_row(result, &row_data, 0, &fetched_anything) == PASS && fetched_anything) { unsigned field_count = result->field_count; - MYSQLND_FIELD *field = result->meta->fields; ret = mnd_emalloc(field_count * sizeof(char *)); - for (unsigned i = 0; i < field_count; i++, field++) { + for (unsigned i = 0; i < field_count; i++) { zval *data = &row_data[i]; if (Z_TYPE_P(data) != IS_NULL) { convert_to_string(data); diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index 633a4cca1623..b957240a4088 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -459,7 +459,7 @@ php_mysqlnd_greet_read(MYSQLND_CONN_DATA * conn, void * _packet) packet->auth_protocol = estrdup(""); } else { /* Check if NUL present */ - char *null_terminator = memchr(p, '\0', remaining_size); + const char *null_terminator = memchr(p, '\0', remaining_size); size_t auth_protocol_len; if (null_terminator) { /* If present, do basically estrdup */ diff --git a/ext/odbc/odbc_utils.c b/ext/odbc/odbc_utils.c index 742bf54bb292..8468fa9a06c1 100644 --- a/ext/odbc/odbc_utils.c +++ b/ext/odbc/odbc_utils.c @@ -57,7 +57,7 @@ PHP_FUNCTION(odbc_connection_string_quote) Z_PARAM_STR(str) ZEND_PARSE_PARAMETERS_END(); - size_t new_size = php_odbc_connstr_estimate_quote_length(ZSTR_VAL(str)); + size_t new_size = php_odbc_connstr_get_quoted_length(ZSTR_VAL(str)); zend_string *new_string = zend_string_alloc(new_size, 0); php_odbc_connstr_quote(ZSTR_VAL(new_string), ZSTR_VAL(str), new_size); /* reset length */ diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 95c872c293f1..a6218cab7276 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -60,7 +60,6 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); static void safe_odbc_disconnect(void *handle); static void close_results_with_connection(odbc_connection *conn); -static inline odbc_result *odbc_result_from_obj(zend_object *obj); static int le_pconn; @@ -69,6 +68,7 @@ static zend_object_handlers odbc_connection_object_handlers, odbc_result_object_ #define Z_ODBC_LINK_P(zv) odbc_link_from_obj(Z_OBJ_P(zv)) #define Z_ODBC_CONNECTION_P(zv) Z_ODBC_LINK_P(zv)->connection +#define odbc_result_from_obj(obj) ZEND_CONTAINER_OF(obj, odbc_result, std) #define Z_ODBC_RESULT_P(zv) odbc_result_from_obj(Z_OBJ_P(zv)) static void odbc_insert_new_result(odbc_connection *connection, zval *result) @@ -85,10 +85,7 @@ static void odbc_insert_new_result(odbc_connection *connection, zval *result) Z_ADDREF_P(result); } -static inline odbc_link *odbc_link_from_obj(zend_object *obj) -{ - return (odbc_link *)((char *)(obj) - XtOffsetOf(odbc_link, std)); -} +#define odbc_link_from_obj(obj) ZEND_CONTAINER_OF(obj, odbc_link, std) static int _close_pconn_with_res(zval *zv, void *p) { @@ -202,11 +199,6 @@ static void odbc_connection_free_obj(zend_object *obj) zend_object_std_dtor(&link->std); } -static inline odbc_result *odbc_result_from_obj(zend_object *obj) -{ - return (odbc_result *)((char *)(obj) - XtOffsetOf(odbc_result, std)); -} - static zend_object *odbc_result_create_object(zend_class_entry *class_type) { odbc_result *intern = zend_object_alloc(sizeof(odbc_result), class_type); @@ -507,7 +499,7 @@ PHP_MINIT_FUNCTION(odbc) odbc_connection_ce->default_object_handlers = &odbc_connection_object_handlers; memcpy(&odbc_connection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - odbc_connection_object_handlers.offset = XtOffsetOf(odbc_link, std); + odbc_connection_object_handlers.offset = offsetof(odbc_link, std); odbc_connection_object_handlers.free_obj = odbc_connection_free_obj; odbc_connection_object_handlers.get_constructor = odbc_connection_get_constructor; odbc_connection_object_handlers.clone_obj = NULL; @@ -519,7 +511,7 @@ PHP_MINIT_FUNCTION(odbc) odbc_result_ce->default_object_handlers = &odbc_result_object_handlers; memcpy(&odbc_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - odbc_result_object_handlers.offset = XtOffsetOf(odbc_result, std); + odbc_result_object_handlers.offset = offsetof(odbc_result, std); odbc_result_object_handlers.free_obj = odbc_result_free_obj; odbc_result_object_handlers.get_constructor = odbc_result_get_constructor; odbc_result_object_handlers.clone_obj = NULL; @@ -1951,9 +1943,9 @@ bool odbc_sqlconnect(zval *zv, char *db, char *uid, char *pwd, int cur_opt, bool if (use_uid_arg) { should_quote_uid = !php_odbc_connstr_is_quoted(uid) && php_odbc_connstr_should_quote(uid); if (should_quote_uid) { - size_t estimated_length = php_odbc_connstr_estimate_quote_length(uid); - uid_quoted = emalloc(estimated_length); - php_odbc_connstr_quote(uid_quoted, uid, estimated_length); + size_t quoted_length = php_odbc_connstr_get_quoted_length(uid); + uid_quoted = emalloc(quoted_length); + php_odbc_connstr_quote(uid_quoted, uid, quoted_length); } else { uid_quoted = uid; } @@ -1966,9 +1958,9 @@ bool odbc_sqlconnect(zval *zv, char *db, char *uid, char *pwd, int cur_opt, bool if (use_pwd_arg) { should_quote_pwd = !php_odbc_connstr_is_quoted(pwd) && php_odbc_connstr_should_quote(pwd); if (should_quote_pwd) { - size_t estimated_length = php_odbc_connstr_estimate_quote_length(pwd); - pwd_quoted = emalloc(estimated_length); - php_odbc_connstr_quote(pwd_quoted, pwd, estimated_length); + size_t quoted_length = php_odbc_connstr_get_quoted_length(pwd); + pwd_quoted = emalloc(quoted_length); + php_odbc_connstr_quote(pwd_quoted, pwd, quoted_length); } else { pwd_quoted = pwd; } diff --git a/ext/opcache/jit/ir/.gitignore b/ext/opcache/jit/ir/.gitignore index 7a37a4fd0593..367a68671bc0 100644 --- a/ext/opcache/jit/ir/.gitignore +++ b/ext/opcache/jit/ir/.gitignore @@ -20,3 +20,6 @@ tests/**/*.log win32/vcpkg win32/build_* + +fuzz/build/ +fuzz/corpus/ diff --git a/ext/opcache/jit/ir/ir.c b/ext/opcache/jit/ir/ir.c index a02332e0d39c..f6a0cb60af98 100644 --- a/ext/opcache/jit/ir/ir.c +++ b/ext/opcache/jit/ir/ir.c @@ -161,6 +161,8 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted case IR_CHAR: if (insn->val.c == '\\') { fprintf(f, "'\\\\'"); + } else if (insn->val.c == '\'') { + fprintf(f, "'\\\''"); } else if (insn->val.c >= ' ') { fprintf(f, "'%c'", insn->val.c); } else if (insn->val.c == '\t') { @@ -283,6 +285,7 @@ void ir_print_const(const ir_ctx *ctx, const ir_insn *insn, FILE *f, bool quoted #define ir_op_kind_src IR_OPND_CONTROL #define ir_op_kind_reg IR_OPND_CONTROL_DEP #define ir_op_kind_ret IR_OPND_CONTROL_REF +#define ir_op_kind_grd IR_OPND_CONTROL_GUARD #define ir_op_kind_str IR_OPND_STR #define ir_op_kind_num IR_OPND_NUM #define ir_op_kind_fld IR_OPND_STR @@ -1843,7 +1846,7 @@ int ir_mem_unprotect(void *ptr, size_t size) int ir_mem_flush(void *ptr, size_t size) { - return 1; + return FlushInstructionCache(GetCurrentProcess(), ptr, size) == TRUE ? 1 : 0; } #else @@ -2168,7 +2171,10 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_load_i(const ir_ctx *ctx, ir_ref ref, i if (!(proto->flags & (IR_CONST_FUNC|IR_PURE_FUNC))) { break; } - } else if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_VSTORE) { + } else if (insn->op == IR_MERGE + || insn->op == IR_LOOP_BEGIN + || insn->op == IR_VSTORE + || (insn->op == IR_BEGIN && insn->op2)) { return IR_UNUSED; } ref = insn->op1; @@ -2233,7 +2239,10 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_vload_i(const ir_ctx *ctx, ir_ref ref, if (!(proto->flags & (IR_CONST_FUNC|IR_PURE_FUNC))) { break; } - } else if (insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN || insn->op == IR_STORE) { + } else if (insn->op == IR_MERGE + || insn->op == IR_LOOP_BEGIN + || insn->op == IR_STORE + || (insn->op == IR_BEGIN && insn->op2)) { break; } ref = insn->op1; @@ -2326,7 +2335,15 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_store_i(ir_ctx *ctx, ir_ref ref, ir_ref } } else if (insn->op == IR_GUARD || insn->op == IR_GUARD_NOT) { guarded = 1; - } else if (insn->op >= IR_START || insn->op == IR_CALL) { + } else if (insn->op >= IR_START) { + if (insn->op == IR_BEGIN && insn->op1 && !insn->op2) { + /* skip END */ + ref = insn->op1; + insn = &ctx->ir_base[ref]; + } else { + break; + } + } else if (insn->op == IR_CALL) { break; } next = ref; @@ -2407,7 +2424,15 @@ IR_ALWAYS_INLINE ir_ref ir_find_aliasing_vstore_i(ir_ctx *ctx, ir_ref ref, ir_re } } else if (insn->op == IR_GUARD || insn->op == IR_GUARD_NOT) { guarded = 1; - } else if (insn->op >= IR_START || insn->op == IR_CALL || insn->op == IR_LOAD || insn->op == IR_STORE) { + } else if (insn->op >= IR_START) { + if (insn->op == IR_BEGIN && insn->op1 && !insn->op2) { + /* skip END */ + ref = insn->op1; + insn = &ctx->ir_base[ref]; + } else { + break; + } + } else if (insn->op == IR_CALL || insn->op == IR_LOAD || insn->op == IR_STORE) { break; } next = ref; @@ -2422,6 +2447,37 @@ ir_ref ir_find_aliasing_vstore(ir_ctx *ctx, ir_ref ref, ir_ref var, ir_ref val) } /* IR Construction API */ +static ir_ref ir_last_guard(ir_ctx *ctx) +{ + ir_ref ref; + ir_insn *insn; + + IR_ASSERT(ctx->control); + ref = ctx->control; + while (1) { + insn = &ctx->ir_base[ref]; + if (IR_IS_BB_START(insn->op) || insn->op == IR_GUARD || insn->op == IR_GUARD_NOT) { + if (insn->op == IR_START) ref = IR_UNUSED; + break; + } + ref = insn->op1; + } + return ref; +} + +ir_ref _ir_DIV(ir_ctx *ctx, ir_type type, ir_ref op1, ir_ref op2) +{ + ir_ref guard = (IR_IS_TYPE_FP(type) || (IR_IS_CONST_REF(op2) && ctx->ir_base[op2].val.u64 != 0)) ? + IR_UNUSED : ir_last_guard(ctx); + return ir_fold3(ctx, IR_OPT(IR_DIV, type), op1, op2, guard); +} + +ir_ref _ir_MOD(ir_ctx *ctx, ir_type type, ir_ref op1, ir_ref op2) +{ + ir_ref guard = (IR_IS_CONST_REF(op2) && ctx->ir_base[op2].val.u64 != 0) ? + IR_UNUSED : ir_last_guard(ctx); + return ir_fold3(ctx, IR_OPT(IR_MOD, type), op1, op2, guard); +} ir_ref _ir_PARAM(ir_ctx *ctx, ir_type type, const char* name, ir_ref num) { diff --git a/ext/opcache/jit/ir/ir.h b/ext/opcache/jit/ir/ir.h index b0a96b511bd0..01db4ecf6b15 100644 --- a/ext/opcache/jit/ir/ir.h +++ b/ext/opcache/jit/ir/ir.h @@ -210,6 +210,7 @@ typedef enum _ir_type { * arg - argument reference CALL/TAILCALL/CARG->CARG * src - reference to a previous control region (IF, IF_TRUE, IF_FALSE, MERGE, LOOP_BEGIN, LOOP_END, RETURN) * reg - data-control dependency on region (PHI, VAR, PARAM) + * grd - optional data-control dependency guard (DIV, MOD) * ret - reference to a previous RETURN instruction (RETURN) * str - string: variable/argument name (VAR, PARAM, CALL, TAILCALL) * num - number: argument number (PARAM) @@ -265,8 +266,8 @@ typedef enum _ir_type { _(ADD, d2C, def, def, ___) /* addition */ \ _(SUB, d2, def, def, ___) /* subtraction (must be ADD+1) */ \ _(MUL, d2C, def, def, ___) /* multiplication */ \ - _(DIV, d2, def, def, ___) /* division */ \ - _(MOD, d2, def, def, ___) /* modulo */ \ + _(DIV, d3, def, def, grd) /* division */ \ + _(MOD, d3, def, def, grd) /* modulo */ \ _(NEG, d1, def, ___, ___) /* change sign */ \ _(ABS, d1, def, ___, ___) /* absolute value */ \ /* (LDEXP, MIN, MAX, FPMATH) */ \ @@ -383,6 +384,14 @@ typedef enum _ir_type { _(RETURN, T2X1, src, def, ret) /* function return */ \ _(UNREACHABLE, T1X2, src, ___, ret) /* unreachable (tailcall, etc) */ \ \ + /* inline assembler */ \ + _(ASM, xN, src, def, def) /* GCC inline assembler */ \ + /* op2 - asm template string */ \ + /* op3 - asm constraint string */ \ + /* opN - asm input argument */ \ + _(ASM_OUT, x1, src, ___, ___) /* ASM data output projection */ \ + _(ASM_GOTO, E1, src, ___, ___) /* ASM goto (bb end after ASM) */ \ + \ /* deoptimization helper */ \ _(EXITCALL, x2, src, def, ___) /* save CPU regs and call op2 */ \ diff --git a/ext/opcache/jit/ir/ir_aarch64.dasc b/ext/opcache/jit/ir/ir_aarch64.dasc index bdf6b027b9fe..fc4bb84f1e05 100644 --- a/ext/opcache/jit/ir/ir_aarch64.dasc +++ b/ext/opcache/jit/ir/ir_aarch64.dasc @@ -402,6 +402,7 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co int flags = IR_USE_MUST_BE_IN_REG | IR_OP1_MUST_BE_IN_REG | IR_OP2_MUST_BE_IN_REG | IR_OP3_MUST_BE_IN_REG; const ir_proto_t *proto; const ir_call_conv_dsc *cc; + ir_ref next; constraints->def_reg = IR_REG_NONE; constraints->hints_count = 0; @@ -562,11 +563,13 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co constraints->tmp_regs[0] = IR_TMP_REG(1, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n = 1; } - if (IR_IS_CONST_REF(insn->op2) && insn->op1 != insn->op2) { - insn = &ctx->ir_base[insn->op2]; - if (IR_IS_SYM_CONST(insn->op) || !aarch64_may_encode_imm12(insn->val.u64)) { - constraints->tmp_regs[n] = IR_TMP_REG(2, insn->type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); - n++; + if (IR_IS_CONST_REF(insn->op2)) { + if (insn->op1 != insn->op2) { + insn = &ctx->ir_base[insn->op2]; + if (IR_IS_SYM_CONST(insn->op) || !aarch64_may_encode_imm12(insn->val.u64)) { + constraints->tmp_regs[n] = IR_TMP_REG(2, insn->type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); + n++; + } } } else if (ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { constraints->tmp_regs[n] = IR_TMP_REG(2, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); @@ -751,6 +754,10 @@ get_arg_hints: break; case IR_SNAPSHOT: flags = 0; + next = ir_next_control(ctx, ref); + if (ctx->ir_base[next].op == IR_GUARD || ctx->ir_base[next].op == IR_GUARD_NOT) { + flags = IR_EXTEND_INPUTS_TO_NEXT; + } break; case IR_VA_START: flags = IR_OP2_MUST_BE_IN_REG; @@ -1199,10 +1206,6 @@ binop_fp: if (!IR_IS_CONST_REF(insn->op2) && (ctx->use_lists[insn->op2].count == 1 || all_usages_are_fusable(ctx, insn->op2))) { op2_insn = &ctx->ir_base[insn->op2]; if (op2_insn->op >= IR_EQ && op2_insn->op <= IR_UNORDERED) { - // TODO: register allocator may clobber operands of CMP before they are used in the GUARD_CMP -//??? && (insn->op2 == ref - 1 || -//??? (insn->op2 == ctx->prev_ref[ref] - 1 -//??? && ctx->ir_base[ctx->prev_ref[ref]].op == IR_SNAPSHOT))) { if (IR_IS_TYPE_INT(ctx->ir_base[op2_insn->op1].type)) { ctx->rules[insn->op2] = IR_FUSED | IR_CMP_INT; return IR_GUARD_CMP_INT; @@ -1265,6 +1268,12 @@ binop_fp: return IR_FUSED | IR_ARGVAL; case IR_NOP: return IR_SKIPPED | IR_NOP; + case IR_ASM: + case IR_ASM_OUT: + case IR_ASM_GOTO: + fprintf(stderr, "ERROR: IR_ASM is not implemented yet\n"); + exit(1); + return IR_SKIPPED | IR_NOP; default: break; } @@ -4996,7 +5005,8 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) void *addr = ir_jmp_addr(ctx, insn, &ctx->ir_base[insn->op2]); | .addr &addr - if (ctx->ir_base[bb->start].op != IR_CASE_DEFAULT) { + if (ctx->ir_base[bb->start].op1 == def + && ctx->ir_base[bb->start].op != IR_CASE_DEFAULT) { bb->flags |= IR_BB_EMPTY; } continue; diff --git a/ext/opcache/jit/ir/ir_builder.h b/ext/opcache/jit/ir/ir_builder.h index 084216a06343..9492945b1362 100644 --- a/ext/opcache/jit/ir/ir_builder.h +++ b/ext/opcache/jit/ir/ir_builder.h @@ -118,31 +118,31 @@ extern "C" { #define ir_MUL_D(_op1, _op2) ir_BINARY_OP_D(IR_MUL, (_op1), (_op2)) #define ir_MUL_F(_op1, _op2) ir_BINARY_OP_F(IR_MUL, (_op1), (_op2)) -#define ir_DIV(_type, _op1, _op2) ir_BINARY_OP(IR_DIV, (_type), (_op1), (_op2)) -#define ir_DIV_U8(_op1, _op2) ir_BINARY_OP_U8(IR_DIV, (_op1), (_op2)) -#define ir_DIV_U16(_op1, _op2) ir_BINARY_OP_U16(IR_DIV, (_op1), (_op2)) -#define ir_DIV_U32(_op1, _op2) ir_BINARY_OP_U32(IR_DIV, (_op1), (_op2)) -#define ir_DIV_U64(_op1, _op2) ir_BINARY_OP_U64(IR_DIV, (_op1), (_op2)) -#define ir_DIV_A(_op1, _op2) ir_BINARY_OP_A(IR_DIV, (_op1), (_op2)) -#define ir_DIV_C(_op1, _op2) ir_BINARY_OP_C(IR_DIV, (_op1), (_op2)) -#define ir_DIV_I8(_op1, _op2) ir_BINARY_OP_I8(IR_DIV, (_op1), (_op2)) -#define ir_DIV_I16(_op1, _op2) ir_BINARY_OP_I16(IR_DIV, (_op1), (_op2)) -#define ir_DIV_I32(_op1, _op2) ir_BINARY_OP_I32(IR_DIV, (_op1), (_op2)) -#define ir_DIV_I64(_op1, _op2) ir_BINARY_OP_I64(IR_DIV, (_op1), (_op2)) +#define ir_DIV(_type, _op1, _op2) _ir_DIV(_ir_CTX, (_type), (_op1), (_op2)) +#define ir_DIV_U8(_op1, _op2) ir_DIV(IR_U8, (_op1), (_op2)) +#define ir_DIV_U16(_op1, _op2) ir_DIV(IR_U16, (_op1), (_op2)) +#define ir_DIV_U32(_op1, _op2) ir_DIV(IR_U32, (_op1), (_op2)) +#define ir_DIV_U64(_op1, _op2) ir_DIV(IR_U64, (_op1), (_op2)) +#define ir_DIV_A(_op1, _op2) ir_DIV(IR_ADDR, (_op1), (_op2)) +#define ir_DIV_C(_op1, _op2) ir_DIV(IR_CHAR, (_op1), (_op2)) +#define ir_DIV_I8(_op1, _op2) ir_DIV(IR_I8, (_op1), (_op2)) +#define ir_DIV_I16(_op1, _op2) ir_DIV(IR_I16, (_op1), (_op2)) +#define ir_DIV_I32(_op1, _op2) ir_DIV(IR_I32, (_op1), (_op2)) +#define ir_DIV_I64(_op1, _op2) ir_DIV(IR_I64, (_op1), (_op2)) #define ir_DIV_D(_op1, _op2) ir_BINARY_OP_D(IR_DIV, (_op1), (_op2)) #define ir_DIV_F(_op1, _op2) ir_BINARY_OP_F(IR_DIV, (_op1), (_op2)) -#define ir_MOD(_type, _op1, _op2) ir_BINARY_OP(IR_MOD, (_type), (_op1), (_op2)) -#define ir_MOD_U8(_op1, _op2) ir_BINARY_OP_U8(IR_MOD, (_op1), (_op2)) -#define ir_MOD_U16(_op1, _op2) ir_BINARY_OP_U16(IR_MOD, (_op1), (_op2)) -#define ir_MOD_U32(_op1, _op2) ir_BINARY_OP_U32(IR_MOD, (_op1), (_op2)) -#define ir_MOD_U64(_op1, _op2) ir_BINARY_OP_U64(IR_MOD, (_op1), (_op2)) -#define ir_MOD_A(_op1, _op2) ir_BINARY_OP_A(IR_MOD, (_op1), (_op2)) -#define ir_MOD_C(_op1, _op2) ir_BINARY_OP_C(IR_MOD, (_op1), (_op2)) -#define ir_MOD_I8(_op1, _op2) ir_BINARY_OP_I8(IR_MOD, (_op1), (_op2)) -#define ir_MOD_I16(_op1, _op2) ir_BINARY_OP_I16(IR_MOD, (_op1), (_op2)) -#define ir_MOD_I32(_op1, _op2) ir_BINARY_OP_I32(IR_MOD, (_op1), (_op2)) -#define ir_MOD_I64(_op1, _op2) ir_BINARY_OP_I64(IR_MOD, (_op1), (_op2)) +#define ir_MOD(_type, _op1, _op2) _ir_MOD(_ir_CTX, (_type), (_op1), (_op2)) +#define ir_MOD_U8(_op1, _op2) ir_MOD(IR_U8, (_op1), (_op2)) +#define ir_MOD_U16(_op1, _op2) ir_MOD(IR_U16, (_op1), (_op2)) +#define ir_MOD_U32(_op1, _op2) ir_MOD(IR_U32, (_op1), (_op2)) +#define ir_MOD_U64(_op1, _op2) ir_MOD(IR_U64, (_op1), (_op2)) +#define ir_MOD_A(_op1, _op2) ir_MOD(IR_ADDR, (_op1), (_op2)) +#define ir_MOD_C(_op1, _op2) ir_MOD(IR_CHAR, (_op1), (_op2)) +#define ir_MOD_I8(_op1, _op2) ir_MOD(IR_I8, (_op1), (_op2)) +#define ir_MOD_I16(_op1, _op2) ir_MOD(IR_I16, (_op1), (_op2)) +#define ir_MOD_I32(_op1, _op2) ir_MOD(IR_I32, (_op1), (_op2)) +#define ir_MOD_I64(_op1, _op2) ir_MOD(IR_I64, (_op1), (_op2)) #define ir_NEG(_type, _op1) ir_UNARY_OP(IR_NEG, (_type), (_op1)) #define ir_NEG_C(_op1) ir_UNARY_OP_C(IR_NEG, (_op1)) @@ -633,6 +633,8 @@ extern "C" { #define ir_MERGE_WITH_EMPTY_TRUE(_if) do {ir_ref end = ir_END(); ir_IF_TRUE(_if); ir_MERGE_2(end, ir_END());} while (0) #define ir_MERGE_WITH_EMPTY_FALSE(_if) do {ir_ref end = ir_END(); ir_IF_FALSE(_if); ir_MERGE_2(end, ir_END());} while (0) +ir_ref _ir_DIV(ir_ctx *ctx, ir_type type, ir_ref op1, ir_ref op2); +ir_ref _ir_MOD(ir_ctx *ctx, ir_type type, ir_ref op1, ir_ref op2); ir_ref _ir_ADD_OFFSET(ir_ctx *ctx, ir_ref addr, uintptr_t offset); ir_ref _ir_PHI_2(ir_ctx *ctx, ir_type type, ir_ref src1, ir_ref src2); ir_ref _ir_PHI_N(ir_ctx *ctx, ir_type type, ir_ref n, ir_ref *inputs); diff --git a/ext/opcache/jit/ir/ir_cfg.c b/ext/opcache/jit/ir/ir_cfg.c index 40041004c561..92042ea8cbb5 100644 --- a/ext/opcache/jit/ir/ir_cfg.c +++ b/ext/opcache/jit/ir/ir_cfg.c @@ -1502,6 +1502,23 @@ static bool ir_is_merged_loop_back_edge(ir_ctx *ctx, uint32_t hdr, uint32_t b) } #endif +static bool ir_should_align_loop(ir_ctx *ctx, ir_chain *chains, uint32_t b, ir_block *bb) +{ + uint32_t n = bb->predecessors_count; + uint32_t *p = ctx->cfg_edges + bb->predecessors; + + for (; n > 0; p++, n--) { + uint32_t pred = *p; + if (chains[pred].head) { + if (ir_chain_head(chains, pred) == b) return 1; + } else { + if (ir_should_align_loop(ctx, chains, b, &ctx->cfg_blocks[pred])) return 1; + } + } + + return 0; +} + static int ir_schedule_blocks_bottom_up(ir_ctx *ctx) { uint32_t max_edges_count = ctx->cfg_edges_count / 2; @@ -1862,7 +1879,7 @@ static int ir_schedule_blocks_bottom_up(ir_ctx *ctx) if (chains[b].head == b) { bb = &ctx->cfg_blocks[b]; if (bb->loop_depth) { - if ((bb->flags & IR_BB_LOOP_HEADER) || ir_chain_head(chains, bb->loop_header) == b) { + if (ir_should_align_loop(ctx, chains, b, bb)) { bb->flags |= IR_BB_ALIGN_LOOP; } } diff --git a/ext/opcache/jit/ir/ir_check.c b/ext/opcache/jit/ir/ir_check.c index ee951291b1b0..e1be7f6544df 100644 --- a/ext/opcache/jit/ir/ir_check.c +++ b/ext/opcache/jit/ir/ir_check.c @@ -148,6 +148,12 @@ bool ir_check(const ir_ctx *ctx) bool ok = 1; ir_check_ctx check_ctx; + if (ctx->insns_count < 1 || ctx->ir_base[1].op != IR_START) { + fprintf(stderr, "ir_base[1].op invalid opcode (%d)\n", + (ctx->insns_count < 1) ? IR_NOP : ctx->ir_base[0].op); + ok = 0; + } + check_ctx.arena = NULL; check_ctx.use_set = NULL; check_ctx.input_set = NULL; @@ -297,6 +303,14 @@ bool ir_check(const ir_ctx *ctx) ok = 0; } break; + case IR_OPND_CONTROL_GUARD: + if (!(ir_op_flags[use_insn->op] & IR_OP_FLAG_BB_START) + && use_insn->op != IR_GUARD + && use_insn->op != IR_GUARD_NOT) { + fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) must be BB_START or GUARD\n", i, j, use); + ok = 0; + } + break; default: fprintf(stderr, "ir_base[%d].ops[%d] reference (%d) of unsupported kind\n", i, j, use); ok = 0; @@ -306,6 +320,8 @@ bool ir_check(const ir_ctx *ctx) /* pass (function returns void) */ } else if (insn->op == IR_BEGIN && j == 1) { /* pass (start of unreachable basic block) */ + } else if (IR_OPND_KIND(flags, j) == IR_OPND_CONTROL_GUARD) { + /* reference to control guard is optional */ } else if (IR_OPND_KIND(flags, j) != IR_OPND_CONTROL_REF && (insn->op != IR_SNAPSHOT || j == 1)) { fprintf(stderr, "ir_base[%d].ops[%d] missing reference (%d)\n", i, j, use); @@ -413,6 +429,7 @@ bool ir_check(const ir_ctx *ctx) } break; case IR_IGOTO: + case IR_ASM_GOTO: break; default: /* skip data references */ @@ -464,6 +481,10 @@ bool ir_check(const ir_ctx *ctx) // if (!ok) { // ir_dump_codegen(ctx, stderr); // } + +#ifndef IR_CHECK_NO_ABORT IR_ASSERT(ok); +#endif + return ok; } diff --git a/ext/opcache/jit/ir/ir_dump.c b/ext/opcache/jit/ir/ir_dump.c index 037003f021a7..3b34294d1c76 100644 --- a/ext/opcache/jit/ir/ir_dump.c +++ b/ext/opcache/jit/ir/ir_dump.c @@ -142,6 +142,7 @@ void ir_dump_dot(const ir_ctx *ctx, const char *name, const char *comments, FILE break; case IR_OPND_CONTROL_DEP: case IR_OPND_CONTROL_REF: + case IR_OPND_CONTROL_GUARD: fprintf(f, "\tn%d -> n%d [style=dashed,dir=back,weight=%d];\n", ref, i, REF_WEIGHT); break; case IR_OPND_LABEL_REF: @@ -650,6 +651,7 @@ void ir_dump_codegen(const ir_ctx *ctx, FILE *f) case IR_OPND_CONTROL: case IR_OPND_CONTROL_DEP: case IR_OPND_CONTROL_REF: + case IR_OPND_CONTROL_GUARD: fprintf(f, "%sl_%d", first ? "(" : ", ", ref); first = 0; break; @@ -680,6 +682,8 @@ void ir_dump_codegen(const ir_ctx *ctx, FILE *f) } else if (opnd_kind == IR_OPND_NUM) { fprintf(f, "%s%d", first ? "(" : ", ", ref); first = 0; + } else if (opnd_kind == IR_OPND_CONTROL_GUARD) { + /* skip */ } else if (j != n && (IR_IS_REF_OPND_KIND(opnd_kind) || (opnd_kind == IR_OPND_UNUSED && p[n-j]))) { fprintf(f, "%snull", first ? "(" : ", "); diff --git a/ext/opcache/jit/ir/ir_fold.h b/ext/opcache/jit/ir/ir_fold.h index 136bbb0e08e1..cbe049be932c 100644 --- a/ext/opcache/jit/ir/ir_fold.h +++ b/ext/opcache/jit/ir/ir_fold.h @@ -1679,44 +1679,6 @@ IR_FOLD(EQ(SEXT, C_I16)) IR_FOLD(EQ(SEXT, C_I32)) IR_FOLD(EQ(SEXT, C_I64)) IR_FOLD(EQ(SEXT, C_ADDR)) -{ - if (ctx->use_lists && ctx->use_lists[op1_insn->op1].count != 1) { - /* pass */ - } else if (op2_insn->val.u64 == 0 && ctx->ir_base[op1_insn->op1].type == IR_BOOL) { - opt = IR_OPT(IR_NOT, IR_BOOL); - op1 = op1_insn->op1; - op2 = IR_UNUSED; - IR_FOLD_RESTART; - } else { - ir_type type = ctx->ir_base[op1_insn->op1].type; - - if (op1_insn->op == IR_ZEXT - && (op2_insn->val.u64 >> (ir_type_size[type] * 8)) != 0) { - IR_FOLD_NEXT; - } - if (IR_IS_TYPE_SIGNED(type)) { - switch (ir_type_size[type]) { - case 1: val.i64 = op2_insn->val.i8; break; - case 2: val.i64 = op2_insn->val.i16; break; - case 4: val.i64 = op2_insn->val.i32; break; - default: val.u64 = op2_insn->val.u64; break; - } - } else { - switch (ir_type_size[type]) { - case 1: val.u64 = op2_insn->val.u8; break; - case 2: val.u64 = op2_insn->val.u16; break; - case 4: val.u64 = op2_insn->val.u32; break; - default: val.u64 = op2_insn->val.u64; break; - } - } - op1 = op1_insn->op1; - op2 = ir_const(ctx, val, type); - IR_FOLD_RESTART; - } - - IR_FOLD_NEXT; -} - IR_FOLD(NE(ZEXT, C_U16)) IR_FOLD(NE(ZEXT, C_U32)) IR_FOLD(NE(ZEXT, C_U64)) @@ -1731,16 +1693,93 @@ IR_FOLD(NE(SEXT, C_I16)) IR_FOLD(NE(SEXT, C_I32)) IR_FOLD(NE(SEXT, C_I64)) IR_FOLD(NE(SEXT, C_ADDR)) -{ - if (ctx->use_lists && ctx->use_lists[op1_insn->op1].count != 1) { +IR_FOLD(ULT(ZEXT, C_U16)) +IR_FOLD(ULT(ZEXT, C_U32)) +IR_FOLD(ULT(ZEXT, C_U64)) +IR_FOLD(ULT(ZEXT, C_I16)) +IR_FOLD(ULT(ZEXT, C_I32)) +IR_FOLD(ULT(ZEXT, C_I64)) +IR_FOLD(ULT(ZEXT, C_ADDR)) +IR_FOLD(UGE(ZEXT, C_U16)) +IR_FOLD(UGE(ZEXT, C_U32)) +IR_FOLD(UGE(ZEXT, C_U64)) +IR_FOLD(UGE(ZEXT, C_I16)) +IR_FOLD(UGE(ZEXT, C_I32)) +IR_FOLD(UGE(ZEXT, C_I64)) +IR_FOLD(UGE(ZEXT, C_ADDR)) +IR_FOLD(ULE(ZEXT, C_U16)) +IR_FOLD(ULE(ZEXT, C_U32)) +IR_FOLD(ULE(ZEXT, C_U64)) +IR_FOLD(ULE(ZEXT, C_I16)) +IR_FOLD(ULE(ZEXT, C_I32)) +IR_FOLD(ULE(ZEXT, C_I64)) +IR_FOLD(ULE(ZEXT, C_ADDR)) +IR_FOLD(UGT(ZEXT, C_U16)) +IR_FOLD(UGT(ZEXT, C_U32)) +IR_FOLD(UGT(ZEXT, C_U64)) +IR_FOLD(UGT(ZEXT, C_I16)) +IR_FOLD(UGT(ZEXT, C_I32)) +IR_FOLD(UGT(ZEXT, C_I64)) +IR_FOLD(UGT(ZEXT, C_ADDR)) +IR_FOLD(LT(SEXT, C_U16)) +IR_FOLD(LT(SEXT, C_U32)) +IR_FOLD(LT(SEXT, C_U64)) +IR_FOLD(LT(SEXT, C_I16)) +IR_FOLD(LT(SEXT, C_I32)) +IR_FOLD(LT(SEXT, C_I64)) +IR_FOLD(LT(SEXT, C_ADDR)) +IR_FOLD(GE(SEXT, C_U16)) +IR_FOLD(GE(SEXT, C_U32)) +IR_FOLD(GE(SEXT, C_U64)) +IR_FOLD(GE(SEXT, C_I16)) +IR_FOLD(GE(SEXT, C_I32)) +IR_FOLD(GE(SEXT, C_I64)) +IR_FOLD(GE(SEXT, C_ADDR)) +IR_FOLD(LE(SEXT, C_U16)) +IR_FOLD(LE(SEXT, C_U32)) +IR_FOLD(LE(SEXT, C_U64)) +IR_FOLD(LE(SEXT, C_I16)) +IR_FOLD(LE(SEXT, C_I32)) +IR_FOLD(LE(SEXT, C_I64)) +IR_FOLD(LE(SEXT, C_ADDR)) +IR_FOLD(GT(SEXT, C_U16)) +IR_FOLD(GT(SEXT, C_U32)) +IR_FOLD(GT(SEXT, C_U64)) +IR_FOLD(GT(SEXT, C_I16)) +IR_FOLD(GT(SEXT, C_I32)) +IR_FOLD(GT(SEXT, C_I64)) +IR_FOLD(GT(SEXT, C_ADDR)) +{ + if (ctx->use_lists && ctx->use_lists[op1].count != 1) { /* pass */ - } else if (op2_insn->val.u64 == 0 && ctx->ir_base[op1_insn->op1].type == IR_BOOL) { - IR_FOLD_COPY(op1_insn->op1); } else { ir_type type = ctx->ir_base[op1_insn->op1].type; - if (op1_insn->op == IR_ZEXT - && (op2_insn->val.u64 >> (ir_type_size[type] * 8)) != 0) { + if (type == IR_BOOL && op2_insn->val.u64 == 0) { + if ((opt & IR_OPT_OP_MASK) == IR_EQ) { + opt = IR_OPT(IR_NOT, IR_BOOL); + op1 = op1_insn->op1; + op2 = IR_UNUSED; + IR_FOLD_RESTART; + } else if ((opt & IR_OPT_OP_MASK) == IR_NE) { + IR_FOLD_COPY(op1_insn->op1); + } + } + if ((op2_insn->val.u64 >> (ir_type_size[type] * 8)) != 0 + && (op1_insn->op != IR_SEXT || (op2_insn->val.i64 >> (ir_type_size[type] * 8)) != -1)) { + if ((opt & IR_OPT_OP_MASK) == IR_EQ + || (opt & IR_OPT_OP_MASK) == IR_UGT + || (opt & IR_OPT_OP_MASK) == IR_UGE) { + IR_FOLD_COPY(IR_FALSE); + } else if ((opt & IR_OPT_OP_MASK) == IR_NE + || (opt & IR_OPT_OP_MASK) == IR_ULT + || (opt & IR_OPT_OP_MASK) == IR_ULE) { + IR_FOLD_COPY(IR_TRUE); + } else if ((opt & IR_OPT_OP_MASK) == IR_GT || (opt & IR_OPT_OP_MASK) == IR_GE) { + IR_FOLD_COPY(op2_insn->val.i64 >= 0 ? IR_FALSE : IR_TRUE); + } else if ((opt & IR_OPT_OP_MASK) == IR_LT || (opt & IR_OPT_OP_MASK) == IR_LE) { + IR_FOLD_COPY(op2_insn->val.i64 >= 0 ? IR_TRUE : IR_FALSE); + } IR_FOLD_NEXT; } if (IR_IS_TYPE_SIGNED(type)) { @@ -1765,6 +1804,43 @@ IR_FOLD(NE(SEXT, C_ADDR)) IR_FOLD_NEXT; } +IR_FOLD(EQ(ZEXT, ZEXT)) +IR_FOLD(NE(ZEXT, ZEXT)) +IR_FOLD(ULT(ZEXT, ZEXT)) +IR_FOLD(UGE(ZEXT, ZEXT)) +IR_FOLD(ULE(ZEXT, ZEXT)) +IR_FOLD(UGT(ZEXT, ZEXT)) +IR_FOLD(EQ(SEXT, SEXT)) +IR_FOLD(NE(SEXT, SEXT)) +IR_FOLD(LT(SEXT, SEXT)) +IR_FOLD(GE(SEXT, SEXT)) +IR_FOLD(LE(SEXT, SEXT)) +IR_FOLD(GT(SEXT, SEXT)) +{ + if (ctx->ir_base[op1_insn->op1].type == ctx->ir_base[op2_insn->op1].type + && (!ctx->use_lists || (ctx->use_lists[op1].count == 1 && ctx->use_lists[op2].count == 1))) { + op1 = op1_insn->op1; + op2 = op2_insn->op1; + IR_FOLD_RESTART; + } + IR_FOLD_NEXT; +} + +IR_FOLD(LT(ZEXT, ZEXT)) +IR_FOLD(GE(ZEXT, ZEXT)) +IR_FOLD(LE(ZEXT, ZEXT)) +IR_FOLD(GT(ZEXT, ZEXT)) +{ + if (ctx->ir_base[op1_insn->op1].type == ctx->ir_base[op2_insn->op1].type + && (!ctx->use_lists || (ctx->use_lists[op1].count == 1 && ctx->use_lists[op2].count == 1))) { + op1 = op1_insn->op1; + op2 = op2_insn->op1; + opt += 4; /* LT -> ULT, ... */ + IR_FOLD_RESTART; + } + IR_FOLD_NEXT; +} + IR_FOLD(NOT(EQ)) IR_FOLD(NOT(NE)) IR_FOLD(NOT(LT)) diff --git a/ext/opcache/jit/ir/ir_gcm.c b/ext/opcache/jit/ir/ir_gcm.c index 1b45eb834ce6..b194eeb81770 100644 --- a/ext/opcache/jit/ir/ir_gcm.c +++ b/ext/opcache/jit/ir/ir_gcm.c @@ -148,7 +148,7 @@ static uint32_t ir_gcm_select_best_block(ir_ctx *ctx, ir_ref ref, uint32_t lca) } #if IR_GCM_SPLIT -/* Partially Dead Code Elimination through splitting the node and sunking the clones +/* Partially Dead Code Elimination through splitting the node and sinking the clones * * This code is based on the Benedikt Meurer's idea first implemented in V8. * See: https://codereview.chromium.org/899433005 @@ -309,6 +309,7 @@ static bool ir_split_partially_dead_node(ir_ctx *ctx, ir_ref ref, uint32_t b) struct { ir_ref ref; uint32_t block; + uint32_t lca; uint32_t use_count; uint32_t use; } *clones = ir_mem_malloc(sizeof(*clones) * use_list->count); @@ -344,8 +345,11 @@ static bool ir_split_partially_dead_node(ir_ctx *ctx, ir_ref ref, uint32_t b) clone = clones_count++; ir_hashtab_add(&hash, j, clone); clones[clone].block = j; + clones[clone].lca = i; clones[clone].use_count = 0; clones[clone].use = (uint32_t)-1; + } else { + clones[clone].lca = ir_gcm_find_lca(ctx, clones[clone].lca, i); } uses[uses_count].ref = use; uses[uses_count].block = i; @@ -367,8 +371,11 @@ static bool ir_split_partially_dead_node(ir_ctx *ctx, ir_ref ref, uint32_t b) clone = clones_count++; ir_hashtab_add(&hash, j, clone); clones[clone].block = j; + clones[clone].lca = i; clones[clone].use_count = 0; clones[clone].use = -1; + } else { + clones[clone].lca = ir_gcm_find_lca(ctx, clones[clone].lca, i); } uses[uses_count].ref = use; uses[uses_count].block = i; @@ -378,6 +385,42 @@ static bool ir_split_partially_dead_node(ir_ctx *ctx, ir_ref ref, uint32_t b) } } + /* Select best blocks to insert clones */ + for (i = 0; i < clones_count; i++) { + uint32_t b0 = clones[i].block; + uint32_t lca = clones[i].lca; + + if (b0 != lca) { + ir_block *bb = &ctx->cfg_blocks[lca]; + uint32_t loop_depth = bb->loop_depth; + + if (loop_depth) { + uint32_t b; + uint32_t best; + + best = b = lca; + do { + b = bb->dom_parent; + bb = &ctx->cfg_blocks[b]; + if (bb->loop_depth < loop_depth) { + if (!bb->loop_depth) { + best = b; + break; + } + loop_depth = bb->loop_depth; + best = b; + } + } while (b != b0); + lca = best; + } + clones[i].block = lca; + } + } + + // TODO: instead of inserting clone into the block where the expressin is partially available, + // we should insert PHI and the actual clones into the block sources where it's not available + // (similar to SSAPRE) + #ifdef IR_DEBUG if (ctx->flags & IR_DEBUG_GCM_SPLIT) { for (i = 0; i < clones_count; i++) { @@ -1170,11 +1213,11 @@ int ir_schedule(ir_ctx *ctx) ir_ref use = *p; ir_insn *use_insn = &ctx->ir_base[use]; if (!_xlat[use] && ctx->cfg_map[use]) { - IR_ASSERT(ctx->cfg_map[use] == b); if (use_insn->op == IR_PARAM || use_insn->op == IR_VAR || use_insn->op == IR_PI || use_insn->op == IR_PHI) { + IR_ASSERT(ctx->cfg_map[use] == b); if (_prev[use] != phis) { /* remove "use" */ _prev[_next[use]] = _prev[use]; diff --git a/ext/opcache/jit/ir/ir_private.h b/ext/opcache/jit/ir/ir_private.h index 9e3a3a171b46..3e1051ca3379 100644 --- a/ext/opcache/jit/ir/ir_private.h +++ b/ext/opcache/jit/ir/ir_private.h @@ -949,10 +949,11 @@ IR_ALWAYS_INLINE bool ir_ref_is_true(const ir_ctx *ctx, ir_ref ref) #define IR_OPND_LABEL_REF 0x3 #define IR_OPND_CONTROL_DEP 0x4 #define IR_OPND_CONTROL_REF 0x5 -#define IR_OPND_STR 0x6 -#define IR_OPND_NUM 0x7 -#define IR_OPND_PROB 0x8 -#define IR_OPND_PROTO 0x9 +#define IR_OPND_CONTROL_GUARD 0x6 +#define IR_OPND_STR 0x7 +#define IR_OPND_NUM 0x8 +#define IR_OPND_PROB 0x9 +#define IR_OPND_PROTO 0xa #define IR_OP_FLAGS(op_flags, op1_flags, op2_flags, op3_flags) \ ((op_flags) | ((op1_flags) << 20) | ((op2_flags) << 24) | ((op3_flags) << 28)) @@ -966,7 +967,7 @@ IR_ALWAYS_INLINE bool ir_ref_is_true(const ir_ctx *ctx, ir_ref ref) (((flags) >> (16 + (4 * (((i) > 3) ? 3 : (i))))) & 0xf) #define IR_IS_REF_OPND_KIND(kind) \ - ((kind) >= IR_OPND_DATA && (kind) <= IR_OPND_CONTROL_REF) + ((kind) >= IR_OPND_DATA && (kind) <= IR_OPND_CONTROL_GUARD) IR_ALWAYS_INLINE ir_ref ir_operands_count(const ir_ctx *ctx, const ir_insn *insn) { @@ -1223,6 +1224,7 @@ typedef struct _ir_use_pos ir_use_pos; #define IR_USE_SHOULD_BE_IN_REG (1<<1) #define IR_DEF_REUSES_OP1_REG (1<<2) #define IR_DEF_CONFLICTS_WITH_INPUT_REGS (1<<3) +#define IR_EXTEND_INPUTS_TO_NEXT (1<<4) /* used for SNAPSHOT followed by GUARD */ #define IR_FUSED_USE (1<<6) #define IR_PHI_USE (1<<7) diff --git a/ext/opcache/jit/ir/ir_ra.c b/ext/opcache/jit/ir/ir_ra.c index aff9aa7bab3f..f22e06083786 100644 --- a/ext/opcache/jit/ir/ir_ra.c +++ b/ext/opcache/jit/ir/ir_ra.c @@ -799,6 +799,34 @@ int ir_compute_live_ranges(ir_ctx *ctx) ir_add_use(ctx, ival, 0, IR_DEF_LIVE_POS_FROM_REF(ref), IR_REG_NONE, IR_USE_SHOULD_BE_IN_REG, 0); continue; } + } else if (def_flags & IR_EXTEND_INPUTS_TO_NEXT) { + ir_ref next = ir_next_control(ctx, ref); + ir_live_pos use_pos; + + IR_ASSERT(insn->op == IR_SNAPSHOT); + j = 2; + p = insn->ops + 2; + for (; j <= insn->inputs_count; j++, p++) { + ir_ref input = *p; + uint32_t v; + + if (input > 0) { + v = ctx->vregs[input]; + IR_ASSERT(v); + use_pos = IR_USE_LIVE_POS_FROM_REF(next); + if (!ir_bitset_in(live, v)) { + /* live.add(opd) */ + ir_bitset_incl(live, v); + /* intervals[opd].addRange(b.from, op.id) */ + ival = ir_add_live_range(ctx, v, IR_START_LIVE_POS_FROM_REF(bb->start), use_pos); + } else { + ival = ctx->live_intervals[v]; + } + use_pos = IR_USE_LIVE_POS_FROM_REF(ref); + ir_add_use(ctx, ival, j, use_pos, IR_REG_NONE, 0, IR_UNUSED); + } + } + continue; } IR_ASSERT(insn->op != IR_PHI && (!ctx->rules || !(ctx->rules[ref] & (IR_FUSED|IR_SKIPPED)))); @@ -1418,6 +1446,34 @@ int ir_compute_live_ranges(ir_ctx *ctx) ir_add_use(ctx, ival, 0, IR_DEF_LIVE_POS_FROM_REF(ref), IR_REG_NONE, IR_USE_SHOULD_BE_IN_REG, 0); continue; } + } else if (def_flags & IR_EXTEND_INPUTS_TO_NEXT) { + ir_ref next = ir_next_control(ctx, ref); + ir_live_pos use_pos; + + IR_ASSERT(insn->op == IR_SNAPSHOT); + j = 2; + p = insn->ops + 2; + for (; j <= insn->inputs_count; j++, p++) { + ir_ref input = *p; + uint32_t v; + + if (input > 0) { + v = ctx->vregs[input]; + IR_ASSERT(v); + use_pos = IR_USE_LIVE_POS_FROM_REF(next); + if (!IS_LIVE_IN_BLOCK(v, b)) { + /* live.add(opd) */ + SET_LIVE_IN_BLOCK(v, b); + /* intervals[opd].addRange(b.from, op.id) */ + ival = ir_add_live_range(ctx, v, IR_START_LIVE_POS_FROM_REF(bb->start), use_pos); + } else { + ival = ctx->live_intervals[v]; + } + use_pos = IR_USE_LIVE_POS_FROM_REF(ref); + ir_add_use(ctx, ival, j, use_pos, IR_REG_NONE, 0, IR_UNUSED); + } + } + continue; } IR_ASSERT(insn->op != IR_PHI && (!ctx->rules || !(ctx->rules[ref] & (IR_FUSED|IR_SKIPPED)))); @@ -3004,6 +3060,7 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li { ir_live_pos nextUsePos[IR_REG_NUM]; ir_live_pos blockPos[IR_REG_NUM]; + int score, best_score, scores[IR_REG_NUM]; int i, reg; ir_live_pos pos, next_use_pos; ir_live_interval *other, *prev; @@ -3032,6 +3089,7 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li for (i = IR_REG_FP_FIRST; i <= IR_REG_FP_LAST; i++) { nextUsePos[i] = 0x7fffffff; blockPos[i] = 0x7fffffff; + scores[i] = 0; } } else { available = IR_REGSET_GP; @@ -3050,6 +3108,7 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li for (i = IR_REG_GP_FIRST; i <= IR_REG_GP_LAST; i++) { nextUsePos[i] = 0x7fffffff; blockPos[i] = 0x7fffffff; + scores[i] = 0; } } @@ -3080,6 +3139,8 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li IR_USE_MUST_BE_IN_REG | IR_USE_SHOULD_BE_IN_REG); if (pos < nextUsePos[reg]) { nextUsePos[reg] = pos; + /* Prefer splitting interval that was already splitted before */ + scores[reg] = (other->flags & IR_LIVE_INTERVAL_SPLIT_CHILD) ? 1 : 0; } } } @@ -3100,6 +3161,7 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li IR_REGSET_FOREACH(regset, reg) { if (overlap < nextUsePos[reg]) { nextUsePos[reg] = overlap; + scores[reg] = 0; } if (overlap < blockPos[reg]) { blockPos[reg] = overlap; @@ -3113,6 +3175,7 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li if (other->flags & (IR_LIVE_INTERVAL_FIXED|IR_LIVE_INTERVAL_TEMP)) { if (overlap < nextUsePos[reg]) { nextUsePos[reg] = overlap; + scores[reg] = 0; } if (overlap < blockPos[reg]) { blockPos[reg] = overlap; @@ -3122,6 +3185,8 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li IR_USE_MUST_BE_IN_REG | IR_USE_SHOULD_BE_IN_REG); if (pos < nextUsePos[reg]) { nextUsePos[reg] = pos; + /* Prefer splitting interval that was already splitted before */ + scores[reg] = (other->flags & IR_LIVE_INTERVAL_SPLIT_CHILD) ? 1 : 0; } } } @@ -3141,12 +3206,17 @@ static ir_reg ir_allocate_blocked_reg(ir_ctx *ctx, ir_live_interval *ival, ir_li /* reg = register with highest nextUsePos */ pos = nextUsePos[reg]; + best_score = (scores[reg] << 28) + nextUsePos[reg]; tmp_regset = available; IR_REGSET_EXCL(tmp_regset, reg); IR_REGSET_FOREACH(tmp_regset, i) { if (nextUsePos[i] > pos) { pos = nextUsePos[i]; + } + score = (scores[i] << 28) + nextUsePos[i]; + if (score > best_score) { reg = i; + best_score = score; } } IR_REGSET_FOREACH_END(); diff --git a/ext/opcache/jit/ir/ir_save.c b/ext/opcache/jit/ir/ir_save.c index 3f1d943c6870..8b3f3b5c6b5d 100644 --- a/ext/opcache/jit/ir/ir_save.c +++ b/ext/opcache/jit/ir/ir_save.c @@ -283,7 +283,7 @@ void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f) n = ir_operands_count(ctx, insn); if ((insn->op == IR_MERGE || insn->op == IR_LOOP_BEGIN) && n != 2) { fprintf(f, "/%d", n); - } else if ((insn->op == IR_CALL || insn->op == IR_TAILCALL) && n != 2) { + } else if ((insn->op == IR_CALL || insn->op == IR_TAILCALL || insn->op == IR_ASM) && n != 2) { fprintf(f, "/%d", n - 2); } else if (insn->op == IR_PHI && n != 3) { fprintf(f, "/%d", n - 1); @@ -321,6 +321,7 @@ void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f) case IR_OPND_CONTROL: case IR_OPND_CONTROL_DEP: case IR_OPND_CONTROL_REF: + case IR_OPND_CONTROL_GUARD: fprintf(f, "%sl_%d", first ? "(" : ", ", ref); first = 0; break; @@ -352,6 +353,8 @@ void ir_save(const ir_ctx *ctx, uint32_t save_flags, FILE *f) } else if (opnd_kind == IR_OPND_NUM) { fprintf(f, "%s%d", first ? "(" : ", ", ref); first = 0; + } else if (opnd_kind == IR_OPND_CONTROL_GUARD) { + /* skip */ } else if (j != n && (IR_IS_REF_OPND_KIND(opnd_kind) || (opnd_kind == IR_OPND_UNUSED && p[n-j]))) { fprintf(f, "%snull", first ? "(" : ", "); diff --git a/ext/opcache/jit/ir/ir_sccp.c b/ext/opcache/jit/ir/ir_sccp.c index 921790fd92bd..f2b8616e2afa 100644 --- a/ext/opcache/jit/ir/ir_sccp.c +++ b/ext/opcache/jit/ir/ir_sccp.c @@ -609,6 +609,10 @@ static IR_NEVER_INLINE void ir_sccp_analyze(const ir_ctx *ctx, ir_sccp_val *_val IR_ASSERT(!IR_OP_HAS_VAR_INPUTS(flags)); n = IR_INPUT_EDGES_COUNT(flags); + if (insn->op == IR_DIV || insn->op == IR_MOD) { + /* skip data-control guard edge */ + n--; + } for (p = insn->ops + 1; n > 0; p++, n--) { ir_ref input = *p; if (input > 0) { @@ -1419,7 +1423,7 @@ static ir_ref ir_iter_find_cse(const ir_ctx *ctx, ir_ref ref, uint32_t opt, ir_r if (!IR_IS_CONST_REF(op2) && (!use_list || use_list->count > ctx->use_lists[op2].count)) { use_list = &ctx->use_lists[op2]; } - if (!IR_IS_CONST_REF(op3) && (!use_list || use_list->count > ctx->use_lists[op3].count)) { + if (op3 > 0 && (!use_list || use_list->count > ctx->use_lists[op3].count)) { use_list = &ctx->use_lists[op3]; } if (use_list) { @@ -1907,6 +1911,46 @@ static ir_ref ir_promote_i2i(ir_ctx *ctx, ir_type type, ir_ref ref, ir_ref use, insn->op3 = ir_promote_i2i(ctx, type, insn->op3, ref, worklist); } insn->type = type; + if (IR_IS_TYPE_SIGNED(type)) { + ir_insn *cond = &ctx->ir_base[insn->op1]; + if (cond->op == IR_LT || cond->op == IR_LE || cond->op == IR_GT || cond->op == IR_GE) { + if (cond->op1 == insn->op2 && cond->op2 == insn->op3) { + insn->op = (cond->op == IR_LT || cond->op == IR_LE) ? IR_MIN : IR_MAX; + ir_use_list_remove_one(ctx, insn->op1, ref); + ir_bitqueue_add(worklist, insn->op1); + insn->op1 = insn->op2; + insn->op2 = insn->op3; + insn->op3 = IR_UNUSED; + } else if (cond->op1 == insn->op3 && cond->op2 == insn->op1) { + insn->op = (cond->op == IR_LT || cond->op == IR_LE) ? IR_MAX : IR_MIN; + ir_use_list_remove_one(ctx, insn->op1, ref); + ir_bitqueue_add(worklist, insn->op1); + insn->op1 = insn->op2; + insn->op2 = insn->op3; + insn->op3 = IR_UNUSED; + } + } + } else { + IR_ASSERT(IR_IS_TYPE_UNSIGNED(type)); + ir_insn *cond = &ctx->ir_base[insn->op1]; + if (cond->op == IR_ULT || cond->op == IR_ULE || cond->op == IR_UGT || cond->op == IR_UGE) { + if (cond->op1 == insn->op2 && cond->op2 == insn->op3) { + insn->op = (cond->op == IR_ULT || cond->op == IR_ULE) ? IR_MIN : IR_MAX; + ir_use_list_remove_one(ctx, insn->op1, ref); + ir_bitqueue_add(worklist, insn->op1); + insn->op1 = insn->op2; + insn->op2 = insn->op3; + insn->op3 = IR_UNUSED; + } else if (cond->op1 == insn->op3 && cond->op2 == insn->op1) { + insn->op = (cond->op == IR_ULT || cond->op == IR_ULE) ? IR_MAX : IR_MIN; + ir_use_list_remove_one(ctx, insn->op1, ref); + ir_bitqueue_add(worklist, insn->op1); + insn->op1 = insn->op2; + insn->op2 = insn->op3; + insn->op3 = IR_UNUSED; + } + } + } return ref; case IR_PHI: for (p = insn->ops + 2, n = insn->inputs_count - 1; n > 0; p++, n--) { @@ -1995,7 +2039,7 @@ static uint32_t _ir_estimated_control(const ir_ctx *ctx, ir_ref val, ir_ref loop const ir_ref *p; ir_ref n, input, result, ctrl; - if (IR_IS_CONST_REF(val)) { + if (val <= 0) { /* constant or IR_UNUSED */ return 1; /* IR_START */ } @@ -2129,14 +2173,14 @@ static bool ir_try_promote_induction_var_ext(ir_ctx *ctx, ir_ref ext_ref, ir_ref const ir_insn *use_insn = &ctx->ir_base[use]; if (use_insn->op >= IR_EQ && use_insn->op <= IR_UGT) { - if (use_insn->op1 == phi_ref) { + if (use_insn->op1 == op_ref) { if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op2].type)) { return 0; } if (ir_is_cheaper_ext(ctx, use_insn->op2, ctx->ir_base[phi_ref].op1, ext_ref, op)) { continue; } - } else if (use_insn->op2 == phi_ref) { + } else if (use_insn->op2 == op_ref) { if (IR_IS_TYPE_SIGNED(type) != IR_IS_TYPE_SIGNED(ctx->ir_base[use_insn->op1].type)) { return 0; } @@ -2521,6 +2565,52 @@ static bool ir_is_zero(const ir_ctx *ctx, ir_ref ref) && ctx->ir_base[ref].val.u32 == 0; } +static bool ir_fix_min_max_const(ir_ctx *ctx, ir_insn *cond, ir_ref ref) +{ + if (cond->op == IR_ULE) { + /* (x <= 3 ? 4 : x) => (x < 4 ? 4 : x) => max(x, 4) */ + /* (x <= 3 ? x : 4) => (x < 4 ? x : 4) => min(x, 4) */ + if (!IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op) + && !IR_IS_SYM_CONST(ctx->ir_base[ref].op) + && ctx->ir_base[cond->op2].val.u64 == ctx->ir_base[ref].val.u64 - 1 + && ctx->ir_base[cond->op2].type == ctx->ir_base[ref].type) { + cond->op2 = ref; + return 1; + } + } else if (cond->op == IR_UGE) { + /* (x >= 3 ? 2 : x) => (x > 2 ? 2 : x) => min(x, 2) */ + /* (x >= 3 ? x : 2) => (x > 2 ? x : 2) => max(x, 2) */ + if (!IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op) + && !IR_IS_SYM_CONST(ctx->ir_base[ref].op) + && ctx->ir_base[cond->op2].val.u64 == ctx->ir_base[ref].val.u64 + 1 + && ctx->ir_base[cond->op2].type == ctx->ir_base[ref].type) { + cond->op2 = ref; + return 1; + } + } else if (cond->op == IR_LE) { + /* (x <= 3 ? 4 : x) => (x < 4 ? 4 : x) => max(x, 4) */ + /* (x <= 3 ? x : 4) => (x < 4 ? x : 4) => min(x, 4) */ + if (!IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op) + && !IR_IS_SYM_CONST(ctx->ir_base[ref].op) + && ctx->ir_base[cond->op2].val.u64 == ctx->ir_base[ref].val.u64 - 1 + && ctx->ir_base[cond->op2].type == ctx->ir_base[ref].type) { + cond->op2 = ref; + return 1; + } + } else if (cond->op == IR_GE) { + /* (x >= 3 ? 2 : x) => (x > 2 ? 2 : x) => min(x, 2) */ + /* (x >= 3 ? x : 2) => (x > 2 ? x : 2) => max(x, 2) */ + if (!IR_IS_SYM_CONST(ctx->ir_base[cond->op2].op) + && !IR_IS_SYM_CONST(ctx->ir_base[ref].op) + && ctx->ir_base[cond->op2].val.i64 == ctx->ir_base[ref].val.i64 + 1 + && ctx->ir_base[cond->op2].type == ctx->ir_base[ref].type) { + cond->op2 = ref; + return 1; + } + } + return 0; +} + static bool ir_optimize_phi(ir_ctx *ctx, ir_ref merge_ref, ir_insn *merge, ir_ref ref, ir_insn *insn, ir_bitqueue *worklist) { IR_ASSERT(insn->inputs_count == 3); @@ -2560,8 +2650,18 @@ static bool ir_optimize_phi(ir_ctx *ctx, ir_ref merge_ref, ir_insn *merge, ir_re } if (is_cmp - && ((insn->op2 == cond->op1 && insn->op3 == cond->op2) - || (insn->op2 == cond->op2 && insn->op3 == cond->op1))) { + && ((insn->op2 == cond->op1 + && (insn->op3 == cond->op2 + || (IR_IS_CONST_REF(cond->op2) + && (IR_IS_CONST_REF(insn->op3) + && IR_IS_TYPE_INT(insn->type) + && ir_fix_min_max_const(ctx, cond, insn->op3))))) + || (insn->op3 == cond->op1 + && (insn->op2 == cond->op2 + || (IR_IS_CONST_REF(cond->op2) + && (IR_IS_CONST_REF(insn->op2) + && IR_IS_TYPE_INT(insn->type) + && ir_fix_min_max_const(ctx, cond, insn->op2))))))) { /* MAX/MIN * * prev prev @@ -2612,14 +2712,14 @@ static bool ir_optimize_phi(ir_ctx *ctx, ir_ref merge_ref, ir_insn *merge, ir_re next->op1 = root->op1; ir_use_list_replace_one(ctx, root->op1, root_ref, next_ref); - if (!IR_IS_CONST_REF(insn->op1)) { - ir_use_list_remove_one(ctx, insn->op1, cond_ref); - } - if (!IR_IS_CONST_REF(insn->op2)) { - ir_use_list_remove_one(ctx, insn->op2, cond_ref); - } if (ctx->use_lists[cond_ref].count == 1) { + if (!IR_IS_CONST_REF(insn->op1)) { + ir_use_list_remove_one(ctx, insn->op1, cond_ref); + } + if (!IR_IS_CONST_REF(insn->op2)) { + ir_use_list_remove_one(ctx, insn->op2, cond_ref); + } MAKE_NOP(cond); CLEAR_USES(cond_ref); } else { ir_use_list_remove_one(ctx, cond_ref, root_ref); @@ -2705,11 +2805,11 @@ static bool ir_optimize_phi(ir_ctx *ctx, ir_ref merge_ref, ir_insn *merge, ir_re next->op1 = root->op1; ir_use_list_replace_one(ctx, root->op1, root_ref, next_ref); ir_use_list_remove_one(ctx, insn->op1, neg_ref); - if (!IR_IS_CONST_REF(insn->op1)) { - ir_use_list_remove_one(ctx, insn->op1, cond_ref); - } if (ctx->use_lists[cond_ref].count == 1) { + if (!IR_IS_CONST_REF(insn->op1)) { + ir_use_list_remove_one(ctx, insn->op1, cond_ref); + } MAKE_NOP(cond); CLEAR_USES(cond_ref); } else { ir_use_list_remove_one(ctx, cond_ref, root_ref); @@ -2727,7 +2827,7 @@ static bool ir_optimize_phi(ir_ctx *ctx, ir_ref merge_ref, ir_insn *merge, ir_re } return 1; - } else if (insn->op2 <= cond_ref && insn->op3 <= cond_ref + } else if (insn->op2 <= root_ref && insn->op3 <= root_ref && cond->op != IR_OVERFLOW // TODO: temporary disable IF-conversion for RLOAD. // We don't track anti-dependencies in GCM and Local Scheduling. @@ -3437,6 +3537,13 @@ static ir_ref ir_iter_optimize_condition(ir_ctx *ctx, ir_ref control, ir_ref con } } + if (condition_insn->op == IR_SHL && IR_IS_CONST_REF(condition_insn->op1)) { + ir_insn *val_insn = &ctx->ir_base[condition_insn->op1]; + if (!IR_IS_SYM_CONST(val_insn->op) && val_insn->val.u64 == 1) { + return IR_TRUE; + } + } + while ((condition_insn->op == IR_BITCAST || condition_insn->op == IR_ZEXT || condition_insn->op == IR_SEXT) diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index 9cd41c37ffef..ca42001a8816 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -1273,6 +1273,7 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co int flags = IR_USE_MUST_BE_IN_REG | IR_OP1_MUST_BE_IN_REG | IR_OP2_MUST_BE_IN_REG | IR_OP3_MUST_BE_IN_REG; const ir_proto_t *proto; const ir_call_conv_dsc *cc; + ir_ref next; constraints->def_reg = IR_REG_NONE; constraints->hints_count = 0; @@ -1345,9 +1346,11 @@ int ir_get_target_constraints(ir_ctx *ctx, ir_ref ref, ir_target_constraints *co flags = IR_DEF_REUSES_OP1_REG | IR_USE_MUST_BE_IN_REG | IR_OP1_SHOULD_BE_IN_REG | IR_OP2_MUST_BE_IN_REG; op2_const: insn = &ctx->ir_base[ref]; - if (IR_IS_CONST_REF(insn->op2) && insn->op1 != insn->op2) { - constraints->tmp_regs[n] = IR_TMP_REG(2, insn->type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); - n++; + if (IR_IS_CONST_REF(insn->op2)) { + if (insn->op1 != insn->op2) { + constraints->tmp_regs[n] = IR_TMP_REG(2, insn->type, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); + n++; + } } else if (ir_rule(ctx, insn->op2) == IR_STATIC_ALLOCA) { constraints->tmp_regs[n] = IR_TMP_REG(2, IR_ADDR, IR_LOAD_SUB_REF, IR_DEF_SUB_REF); n = 1; @@ -1712,6 +1715,10 @@ get_arg_hints: break; case IR_SNAPSHOT: flags = 0; + next = ir_next_control(ctx, ref); + if (ctx->ir_base[next].op == IR_GUARD || ctx->ir_base[next].op == IR_GUARD_NOT) { + flags = IR_EXTEND_INPUTS_TO_NEXT; + } break; case IR_VA_START: flags = IR_OP2_MUST_BE_IN_REG; @@ -3078,10 +3085,6 @@ store_int: if (!IR_IS_CONST_REF(insn->op2) && (ctx->use_lists[insn->op2].count == 1 || all_usages_are_fusable(ctx, insn->op2))) { op2_insn = &ctx->ir_base[insn->op2]; if (op2_insn->op >= IR_EQ && op2_insn->op <= IR_UNORDERED) { - // TODO: register allocator may clobber operands of CMP before they are used in the GUARD_CMP -//??? && (insn->op2 == ref - 1 || -//??? (insn->op2 == ctx->prev_ref[ref] - 1 -//??? && ctx->ir_base[ctx->prev_ref[ref]].op == IR_SNAPSHOT))) { if (IR_IS_TYPE_INT(ctx->ir_base[op2_insn->op1].type)) { if (IR_IS_CONST_REF(op2_insn->op2) && !IR_IS_SYM_CONST(ctx->ir_base[op2_insn->op2].op) @@ -3262,6 +3265,12 @@ store_int: return IR_FUSED | IR_ARGVAL; case IR_NOP: return IR_SKIPPED | IR_NOP; + case IR_ASM: + case IR_ASM_OUT: + case IR_ASM_GOTO: + fprintf(stderr, "ERROR: IR_ASM is not implemented yet\n"); + exit(1); + return IR_SKIPPED | IR_NOP; default: break; } @@ -9429,7 +9438,8 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) void *addr = ir_jmp_addr(ctx, insn, &ctx->ir_base[insn->op2]); | .aword &addr - if (ctx->ir_base[bb->start].op != IR_CASE_DEFAULT) { + if (ctx->ir_base[bb->start].op1 == def + && ctx->ir_base[bb->start].op != IR_CASE_DEFAULT) { bb->flags |= IR_BB_EMPTY; } continue; diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 738f204c5026..fbbfab6b243c 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -76,6 +76,7 @@ int zend_jit_profile_counter_rid = -1; int16_t zend_jit_hot_counters[ZEND_HOT_COUNTERS_COUNT]; const zend_op *zend_jit_halt_op = NULL; +const zend_op *zend_jit_interrupt_op = NULL; #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP static int zend_write_protect = 1; #endif @@ -3776,6 +3777,7 @@ int zend_jit_check_support(void) void zend_jit_startup(void *buf, size_t size, bool reattached) { zend_jit_halt_op = zend_get_halt_op(); + zend_jit_interrupt_op = zend_get_interrupt_op(); zend_jit_profile_counter_rid = zend_get_op_array_extension_handle(ACCELERATOR_PRODUCT_NAME); #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP diff --git a/ext/opcache/jit/zend_jit_internal.h b/ext/opcache/jit/zend_jit_internal.h index be27bcaf5341..7b5f93f70db7 100644 --- a/ext/opcache/jit/zend_jit_internal.h +++ b/ext/opcache/jit/zend_jit_internal.h @@ -175,6 +175,7 @@ typedef struct _zend_jit_op_array_hot_extension { zend_jit_hash((op_array)->opcodes) extern const zend_op *zend_jit_halt_op; +extern const zend_op *zend_jit_interrupt_op; #ifdef HAVE_GCC_GLOBAL_REGS # define EXECUTE_DATA_D void diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index e802d213aff1..cf43d3ad840f 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -8000,14 +8000,12 @@ static int zend_jit_escape_if_undef(zend_jit_ctx *jit, int var, uint32_t flags, zend_jit_op_array_trace_extension *jit_extension = (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array); size_t offset = jit_extension->offset; - ir_ref ref = ir_CONST_ADDR(ZEND_OP_TRACE_INFO((opline - 1), offset)->orig_handler); + ir_ref ref = ir_CONST_FC_FUNC(ZEND_OP_TRACE_INFO((opline - 1), offset)->orig_handler); if (GCC_GLOBAL_REGS || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL) { ir_TAILCALL(IR_OPCODE_HANDLER_RET, ref); } else { -#if defined(IR_TARGET_X86) - ref = ir_CAST_FC_FUNC(ref); -#endif - ir_TAILCALL_2(IR_ADDR, ref, jit_FP(jit), jit_IP(jit)); + ir_ref opline_ref = ir_CALL_2(IR_OPCODE_HANDLER_RET, ref, jit_FP(jit), jit_IP(jit)); + zend_jit_vm_enter(jit, opline_ref); } ir_IF_TRUE(if_def); @@ -10444,28 +10442,19 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen if (ZEND_OBSERVER_ENABLED && (!func || (func->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_GENERATOR)) == 0)) { ir_ref observer_handler; ir_ref rx = jit_FP(jit); + const zend_op *observer_opline = NULL; struct jit_observer_fcall_is_unobserved_data unobserved_data = jit_observer_fcall_is_unobserved_start(jit, func, &observer_handler, rx, func_ref); if (trace && (trace->op != ZEND_JIT_TRACE_END || trace->stop < ZEND_JIT_TRACE_STOP_INTERPRETER)) { ZEND_ASSERT(trace[1].op == ZEND_JIT_TRACE_VM || trace[1].op == ZEND_JIT_TRACE_END); - jit_SET_EX_OPLINE(jit, trace[1].opline); + observer_opline = trace[1].opline; + jit_SET_EX_OPLINE(jit, observer_opline); } else { // EX(opline) = opline ir_STORE(jit_EX(opline), jit_IP(jit)); } jit_observer_fcall_begin(jit, rx, observer_handler); - if (trace) { - int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); - - exit_addr = zend_jit_trace_get_exit_addr(exit_point); - if (!exit_addr) { - return 0; - } - } else { - exit_addr = NULL; - } - - zend_jit_check_timeout(jit, NULL /* we're inside the called function */, exit_addr); + zend_jit_check_timeout(jit, observer_opline, NULL); jit_observer_fcall_is_unobserved_end(jit, &unobserved_data); } diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index 4b3cb663686d..024a5d0e194d 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -2359,6 +2359,9 @@ static zend_ssa *zend_jit_trace_build_tssa(zend_jit_trace_rec *trace_buffer, uin if (ssa_ops[idx].op2_use >= 0 && ssa_ops[idx].op2_def >= 0) { ssa_var_info[ssa_ops[idx].op2_def] = ssa_var_info[ssa_ops[idx].op2_use]; } + if (ssa_ops[idx].result_use >= 0 && ssa_ops[idx].result_def >= 0) { + ssa_var_info[ssa_ops[idx].result_def] = ssa_var_info[ssa_ops[idx].result_use]; + } } else { if (zend_update_type_info(op_array, tssa, script, (zend_op*)opline, ssa_ops + idx, ssa_opcodes, optimization_level) == FAILURE) { // TODO: @@ -8568,7 +8571,7 @@ int ZEND_FASTCALL zend_jit_trace_hot_side(zend_execute_data *execute_data, uint3 do { ex = ex->prev_execute_data; n++; - } while (ex && zend_jit_traces[root].op_array != &ex->func->op_array); + } while (ex && (!ex->func || zend_jit_traces[root].op_array != &ex->func->op_array)); if (ex && n <= ZEND_JIT_TRACE_MAX_RET_DEPTH) { ret_depth = n; } diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 1c7fb4073569..271d923598d9 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -1070,6 +1070,11 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (UNEXPECTED(opline == zend_jit_halt_op)) { #else opline = handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); +# if ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL + while (UNEXPECTED(opline == zend_jit_interrupt_op)) { + opline = zend_vm_handle_interrupt(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } +# endif if (UNEXPECTED(((uintptr_t)opline & ~ZEND_VM_ENTER_BIT) == 0)) { #endif if (prev_opline->opcode == ZEND_YIELD || prev_opline->opcode == ZEND_YIELD_FROM) { @@ -1095,6 +1100,8 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, if (UNEXPECTED(!jit_extension) || UNEXPECTED(!(jit_extension->func_info.flags & ZEND_FUNC_JIT_ON_HOT_TRACE))) { if (execute_data->prev_execute_data != prev_execute_data) { + stop = ZEND_JIT_TRACE_STOP_RETURN; + } else { stop = ZEND_JIT_TRACE_STOP_INTERPRETER; } break; diff --git a/ext/opcache/tests/gh22071.phpt b/ext/opcache/tests/gh22071.phpt new file mode 100644 index 000000000000..148ca461be21 --- /dev/null +++ b/ext/opcache/tests/gh22071.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-22071: Assertion failure jit_CONST_FUNC_PROTO on abstract static method call +--CREDITS-- +YuanchengJiang +--EXTENSIONS-- +opcache +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=1205 +opcache.jit_buffer_size=16M +--FILE-- + +--EXPECT-- +ok diff --git a/ext/opcache/tests/jit/gh21368_call_vm.phpt b/ext/opcache/tests/jit/gh21368_call_vm.phpt new file mode 100644 index 000000000000..cc64e62a610e --- /dev/null +++ b/ext/opcache/tests/jit/gh21368_call_vm.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-21368 (JIT escape_if_undef SEGV on CALL VM with aggressive trace counters) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit_buffer_size=64M +opcache.jit=tracing +opcache.protect_memory=1 +opcache.jit_hot_loop=1 +opcache.jit_hot_func=1 +opcache.jit_hot_return=1 +opcache.jit_hot_side_exit=1 +--FILE-- +x; } +} + +$o1 = new C; +$o2 = new C; +$o2->x = false; +$o3 = new C; +unset($o3->x); +$a = [$o1, $o2, $o3]; + +for ($i = 0; $i < 8; $i++) { + $m = $a[$i % 3]; + $m->getX(); + $m->getX(); +} +?> +OK +--EXPECT-- +OK diff --git a/ext/opcache/tests/jit/gh21746.inc b/ext/opcache/tests/jit/gh21746.inc new file mode 100644 index 000000000000..b118625484d0 --- /dev/null +++ b/ext/opcache/tests/jit/gh21746.inc @@ -0,0 +1,16 @@ + ''; + $e = fn() => ''; + $f = fn() => ''; + $g = fn() => ''; + return '' + .LR::p(null, '', null, [], '') + .LR::hbbch([null], $d) + .LR::hbbch([null], $e) + .LR::hbbch([null], $f) + .LR::hbbch([null], $g); +}; diff --git a/ext/opcache/tests/jit/gh21746.phpt b/ext/opcache/tests/jit/gh21746.phpt new file mode 100644 index 000000000000..29b3be778be7 --- /dev/null +++ b/ext/opcache/tests/jit/gh21746.phpt @@ -0,0 +1,112 @@ +--TEST-- +GH-21746: Segfault with tracing JIT on 2nd call of eval'd closure +--CREDITS-- +theodorejb +--EXTENSIONS-- +opcache +--INI-- +opcache.jit=tracing +--FILE-- + $_) { if ($i === 0) break; } + return $result; + } + public static function hbbch(array $positional, ?\Closure $cb): string { + if ($cb && is_array($positional[0] ?? null)) foreach ($positional[0] as $v) $cb($v); + return ''; + } +} + +$renderFile = __DIR__ . '/gh21746.inc'; + +// prevent JITing $renderFile +ini_set('opcache.file_update_protection', 100); +touch($renderFile); + +$renderer = require $renderFile; + +for ($r = 0; $r < 70; $r++) { + echo "Render $r..."; + $renderer(); + echo "OK\n"; +} + +?> +==DONE== +--EXPECT-- +Render 0...OK +Render 1...OK +Render 2...OK +Render 3...OK +Render 4...OK +Render 5...OK +Render 6...OK +Render 7...OK +Render 8...OK +Render 9...OK +Render 10...OK +Render 11...OK +Render 12...OK +Render 13...OK +Render 14...OK +Render 15...OK +Render 16...OK +Render 17...OK +Render 18...OK +Render 19...OK +Render 20...OK +Render 21...OK +Render 22...OK +Render 23...OK +Render 24...OK +Render 25...OK +Render 26...OK +Render 27...OK +Render 28...OK +Render 29...OK +Render 30...OK +Render 31...OK +Render 32...OK +Render 33...OK +Render 34...OK +Render 35...OK +Render 36...OK +Render 37...OK +Render 38...OK +Render 39...OK +Render 40...OK +Render 41...OK +Render 42...OK +Render 43...OK +Render 44...OK +Render 45...OK +Render 46...OK +Render 47...OK +Render 48...OK +Render 49...OK +Render 50...OK +Render 51...OK +Render 52...OK +Render 53...OK +Render 54...OK +Render 55...OK +Render 56...OK +Render 57...OK +Render 58...OK +Render 59...OK +Render 60...OK +Render 61...OK +Render 62...OK +Render 63...OK +Render 64...OK +Render 65...OK +Render 66...OK +Render 67...OK +Render 68...OK +Render 69...OK +==DONE== diff --git a/ext/opcache/tests/jit/gh22004.phpt b/ext/opcache/tests/jit/gh22004.phpt new file mode 100644 index 000000000000..bb60868a269f --- /dev/null +++ b/ext/opcache/tests/jit/gh22004.phpt @@ -0,0 +1,63 @@ +--TEST-- +GH-22004: ZEND_FE_FETCH_R with key operand +--CREDITS-- +YuanchengJiang +--FILE-- + $line) { + } + } +} +$iter = 20; +doRandom($iter); + +?> +==DONE== +--EXPECTF-- +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d + +Warning: Undefined variable $nLines in %s on line %d +==DONE== diff --git a/ext/opcache/zend_accelerator_blacklist.c b/ext/opcache/zend_accelerator_blacklist.c index 56a4ceb4dc4d..ed03a82e9f3f 100644 --- a/ext/opcache/zend_accelerator_blacklist.c +++ b/ext/opcache/zend_accelerator_blacklist.c @@ -225,7 +225,7 @@ static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist) { if (blacklist->pos == blacklist->size) { blacklist->size += ZEND_BLACKLIST_BLOCK_SIZE; - blacklist->entries = (zend_blacklist_entry *) realloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size); + blacklist->entries = (zend_blacklist_entry *) perealloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size, true); } } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 973e441edc80..c06452e6acf2 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -542,9 +542,8 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc zend_op *new_opcodes = zend_shared_memdup_put(op_array->opcodes, sizeof(zend_op) * op_array->last); zend_op *opline = new_opcodes; zend_op *end = new_opcodes + op_array->last; - int offset = 0; - for (; opline < end ; opline++, offset++) { + for (; opline < end ; opline++) { #if ZEND_USE_ABS_CONST_ADDR if (opline->op1_type == IS_CONST) { opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals)); diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index de52191c441a..e7f29c6a354b 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -56,6 +56,10 @@ ZEND_DECLARE_MODULE_GLOBALS(openssl) #include "openssl_arginfo.h" +/* OpenSSLException class */ + +zend_class_entry *php_openssl_exception_ce; + /* OpenSSLCertificate class */ zend_class_entry *php_openssl_certificate_ce; @@ -165,6 +169,371 @@ static void php_openssl_pkey_free_obj(zend_object *object) zend_object_std_dtor(&key_object->std); } +/* Openssl\Psk class */ + +zend_class_entry *php_openssl_psk_ce; + +static zend_object_handlers php_openssl_psk_object_handlers; + +bool php_openssl_is_psk_ce(zval *val) +{ + return Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val) == php_openssl_psk_ce; +} + +zend_string *php_openssl_psk_get_psk(zval *psk_zv) +{ + zval rv; + zval *prop = zend_read_property(php_openssl_psk_ce, Z_OBJ_P(psk_zv), ZEND_STRL("psk"), 0, &rv); + if (UNEXPECTED(Z_TYPE_P(prop) != IS_STRING)) { + return NULL; + } + return Z_STR_P(prop); +} + +zend_string *php_openssl_psk_get_identity(zval *psk_zv) +{ + zval rv; + zval *prop = zend_read_property(php_openssl_psk_ce, Z_OBJ_P(psk_zv), + ZEND_STRL("identity"), 0, &rv); + if (Z_TYPE_P(prop) == IS_NULL) { + return NULL; + } + if (UNEXPECTED(Z_TYPE_P(prop) != IS_STRING)) { + return NULL; + } + return Z_STR_P(prop); +} + +PHP_METHOD(Openssl_Psk, __construct) +{ + zend_string *psk; + zend_string *identity = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(psk) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(identity) + ZEND_PARSE_PARAMETERS_END(); + + if (ZSTR_LEN(psk) == 0) { + zend_argument_must_not_be_empty_error(1); + RETURN_THROWS(); + } + if (ZSTR_LEN(psk) > PHP_OPENSSL_PSK_MAX_PSK_LEN) { + zend_argument_value_error(1, "must not exceed %d bytes", PHP_OPENSSL_PSK_MAX_PSK_LEN); + RETURN_THROWS(); + } + if (identity != NULL && ZSTR_LEN(identity) > PHP_OPENSSL_PSK_MAX_IDENTITY_LEN) { + zend_argument_value_error(2, "must not exceed %d bytes", PHP_OPENSSL_PSK_MAX_IDENTITY_LEN); + RETURN_THROWS(); + } + + zend_update_property_str(php_openssl_psk_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("psk"), psk); + + if (identity != NULL) { + zend_update_property_str(php_openssl_psk_ce, Z_OBJ_P(ZEND_THIS), + ZEND_STRL("identity"), identity); + } else { + zend_update_property_null(php_openssl_psk_ce, Z_OBJ_P(ZEND_THIS), ZEND_STRL("identity")); + } +} + +/* Openssl\Session class */ + +zend_class_entry *php_openssl_session_ce; + +static zend_object_handlers php_openssl_session_object_handlers; + +bool php_openssl_is_session_ce(zval *val) +{ + return Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val) == php_openssl_session_ce; +} + +SSL_SESSION *php_openssl_session_from_zval(zval *zv) +{ + if (!php_openssl_is_session_ce(zv)) { + return NULL; + } + return Z_OPENSSL_SESSION_P(zv)->session; +} + +void php_openssl_session_object_init(zval *zv, SSL_SESSION *session) +{ + object_init_ex(zv, php_openssl_session_ce); + php_openssl_session_object *obj = Z_OPENSSL_SESSION_P(zv); + obj->session = session; + + unsigned int id_len = 0; + const unsigned char *id = SSL_SESSION_get_id(session, &id_len); + zend_update_property_stringl(php_openssl_session_ce, Z_OBJ_P(zv), + ZEND_STRL("id"), (char *)id, id_len); +} + +static zend_object *php_openssl_session_create_object(zend_class_entry *class_type) +{ + php_openssl_session_object *intern = zend_object_alloc(sizeof(php_openssl_session_object), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + + return &intern->std; +} + +static zend_function *php_openssl_session_get_constructor(zend_object *object) +{ + zend_throw_error(NULL, + "Cannot directly construct OpenSSLSession, use OpenSSLSession::import() or TLS session callbacks"); + return NULL; +} + +static void php_openssl_session_free_obj(zend_object *object) +{ + php_openssl_session_object *session_object = php_openssl_session_from_obj(object); + + if (session_object->session) { + SSL_SESSION_free(session_object->session); + session_object->session = NULL; + } + zend_object_std_dtor(&session_object->std); +} + +#define PHP_OPENSSL_SESSION_CHECK() \ + php_openssl_session_object *obj = Z_OPENSSL_SESSION_P(ZEND_THIS); \ + if (!obj->session) { \ + zend_throw_exception(php_openssl_exception_ce, "Session is not valid", 0); \ + RETURN_THROWS(); \ + } + +PHP_METHOD(Openssl_Session, export) +{ + zend_long format = ENCODING_PEM; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(format) + ZEND_PARSE_PARAMETERS_END(); + + PHP_OPENSSL_SESSION_CHECK(); + + if (format == ENCODING_DER) { + int len = i2d_SSL_SESSION(obj->session, NULL); + if (len <= 0) { + zend_throw_exception(php_openssl_exception_ce, "Failed to export session", 0); + RETURN_THROWS(); + } + + zend_string *result = zend_string_alloc(len, 0); + unsigned char *p = (unsigned char *)ZSTR_VAL(result); + i2d_SSL_SESSION(obj->session, &p); + ZSTR_VAL(result)[len] = '\0'; + + RETURN_NEW_STR(result); + } + + if (format == ENCODING_PEM) { + BIO *bio = BIO_new(BIO_s_mem()); + if (!bio) { + zend_throw_exception(php_openssl_exception_ce, "Failed to create BIO", 0); + RETURN_THROWS(); + } + + if (!PEM_write_bio_SSL_SESSION(bio, obj->session)) { + BIO_free(bio); + zend_throw_exception(php_openssl_exception_ce, "Failed to export session as PEM", 0); + RETURN_THROWS(); + } + + char *data; + long len = BIO_get_mem_data(bio, &data); + zend_string *result = zend_string_init(data, len, 0); + BIO_free(bio); + + RETURN_NEW_STR(result); + } + + zend_argument_value_error(1, "must be OPENSSL_ENCODING_DER or OPENSSL_ENCODING_PEM"); + RETURN_THROWS(); +} + +PHP_METHOD(Openssl_Session, import) +{ + zend_string *data; + zend_long format = ENCODING_PEM; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(data) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(format) + ZEND_PARSE_PARAMETERS_END(); + + SSL_SESSION *session = NULL; + + if (format == ENCODING_DER) { + const unsigned char *p = (const unsigned char *)ZSTR_VAL(data); + session = d2i_SSL_SESSION(NULL, &p, ZSTR_LEN(data)); + } else if (format == ENCODING_PEM) { + BIO *bio = BIO_new_mem_buf(ZSTR_VAL(data), ZSTR_LEN(data)); + if (bio) { + session = PEM_read_bio_SSL_SESSION(bio, NULL, NULL, NULL); + BIO_free(bio); + } + } else { + zend_argument_value_error(2, "must be OPENSSL_ENCODING_DER or OPENSSL_ENCODING_PEM"); + RETURN_THROWS(); + } + + if (!session) { + zend_throw_exception(php_openssl_exception_ce, "Failed to import session data", 0); + RETURN_THROWS(); + } + + php_openssl_session_object_init(return_value, session); +} + +PHP_METHOD(Openssl_Session, isResumable) +{ + ZEND_PARSE_PARAMETERS_NONE(); + PHP_OPENSSL_SESSION_CHECK(); + + RETURN_BOOL(SSL_SESSION_is_resumable(obj->session)); +} + +PHP_METHOD(Openssl_Session, getTimeout) +{ + ZEND_PARSE_PARAMETERS_NONE(); + PHP_OPENSSL_SESSION_CHECK(); + RETURN_LONG((zend_long)SSL_SESSION_get_timeout(obj->session)); +} + +PHP_METHOD(Openssl_Session, getCreatedAt) +{ + ZEND_PARSE_PARAMETERS_NONE(); + PHP_OPENSSL_SESSION_CHECK(); +#if PHP_OPENSSL_API_VERSION >= 0x30300 + RETURN_LONG((zend_long)SSL_SESSION_get_time_ex(obj->session)); +#else + RETURN_LONG((zend_long)SSL_SESSION_get_time(obj->session)); +#endif +} + +PHP_METHOD(Openssl_Session, getProtocol) +{ + ZEND_PARSE_PARAMETERS_NONE(); + PHP_OPENSSL_SESSION_CHECK(); + + int version = SSL_SESSION_get_protocol_version(obj->session); + + switch (version) { + case TLS1_3_VERSION: + RETURN_STRING("TLSv1.3"); + case TLS1_2_VERSION: + RETURN_STRING("TLSv1.2"); + case TLS1_1_VERSION: + RETURN_STRING("TLSv1.1"); + case TLS1_VERSION: + RETURN_STRING("TLSv1.0"); + default: + RETURN_NULL(); + } +} + +PHP_METHOD(Openssl_Session, getCipher) +{ + ZEND_PARSE_PARAMETERS_NONE(); + PHP_OPENSSL_SESSION_CHECK(); + + const SSL_CIPHER *cipher = SSL_SESSION_get0_cipher(obj->session); + if (!cipher) { + RETURN_NULL(); + } + + RETURN_STRING(SSL_CIPHER_get_name(cipher)); +} + +PHP_METHOD(Openssl_Session, hasTicket) +{ + ZEND_PARSE_PARAMETERS_NONE(); + PHP_OPENSSL_SESSION_CHECK(); + + RETURN_BOOL(SSL_SESSION_has_ticket(obj->session)); +} + +PHP_METHOD(Openssl_Session, getTicketLifetimeHint) +{ + ZEND_PARSE_PARAMETERS_NONE(); + PHP_OPENSSL_SESSION_CHECK(); + + if (!SSL_SESSION_has_ticket(obj->session)) { + RETURN_NULL(); + } + + RETURN_LONG((zend_long)SSL_SESSION_get_ticket_lifetime_hint(obj->session)); +} +PHP_METHOD(Openssl_Session, __serialize) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + PHP_OPENSSL_SESSION_CHECK(); + + BIO *bio = BIO_new(BIO_s_mem()); + if (!bio) { + zend_throw_exception(php_openssl_exception_ce, "Failed to serialize session", 0); + RETURN_THROWS(); + } + + if (!PEM_write_bio_SSL_SESSION(bio, obj->session)) { + BIO_free(bio); + zend_throw_exception(php_openssl_exception_ce, "Failed to serialize session", 0); + RETURN_THROWS(); + } + + char *data; + long len = BIO_get_mem_data(bio, &data); + zend_string *pem = zend_string_init(data, len, 0); + BIO_free(bio); + + array_init(return_value); + add_assoc_str(return_value, "pem", pem); +} + +PHP_METHOD(Openssl_Session, __unserialize) +{ + HashTable *data; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(data) + ZEND_PARSE_PARAMETERS_END(); + + zval *pem_zv = zend_hash_str_find(data, ZEND_STRL("pem")); + if (!pem_zv || Z_TYPE_P(pem_zv) != IS_STRING) { + zend_throw_exception(php_openssl_exception_ce, "Invalid serialization data", 0); + RETURN_THROWS(); + } + + BIO *bio = BIO_new_mem_buf(Z_STRVAL_P(pem_zv), Z_STRLEN_P(pem_zv)); + if (!bio) { + zend_throw_exception(php_openssl_exception_ce, "Failed to unserialize session", 0); + RETURN_THROWS(); + } + + SSL_SESSION *session = PEM_read_bio_SSL_SESSION(bio, NULL, NULL, NULL); + BIO_free(bio); + + if (!session) { + zend_throw_exception(php_openssl_exception_ce, "Failed to unserialize session", 0); + RETURN_THROWS(); + } + + php_openssl_session_object *obj = Z_OPENSSL_SESSION_P(ZEND_THIS); + obj->session = session; + + /* Populate id property */ + unsigned int id_len = 0; + const unsigned char *id = SSL_SESSION_get_id(session, &id_len); + zend_update_property_stringl(php_openssl_session_ce, Z_OBJ_P(ZEND_THIS), + ZEND_STRL("id"), (char *)id, id_len); +} + #if defined(HAVE_OPENSSL_ARGON2) static const zend_module_dep openssl_deps[] = { ZEND_MOD_REQUIRED("standard") @@ -381,12 +750,14 @@ PHP_INI_END() /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(openssl) { + php_openssl_exception_ce = register_class_Openssl_OpensslException(zend_ce_exception); + php_openssl_certificate_ce = register_class_OpenSSLCertificate(); php_openssl_certificate_ce->create_object = php_openssl_certificate_create_object; php_openssl_certificate_ce->default_object_handlers = &php_openssl_certificate_object_handlers; memcpy(&php_openssl_certificate_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - php_openssl_certificate_object_handlers.offset = XtOffsetOf(php_openssl_certificate_object, std); + php_openssl_certificate_object_handlers.offset = offsetof(php_openssl_certificate_object, std); php_openssl_certificate_object_handlers.free_obj = php_openssl_certificate_free_obj; php_openssl_certificate_object_handlers.get_constructor = php_openssl_certificate_get_constructor; php_openssl_certificate_object_handlers.clone_obj = NULL; @@ -397,7 +768,7 @@ PHP_MINIT_FUNCTION(openssl) php_openssl_request_ce->default_object_handlers = &php_openssl_request_object_handlers; memcpy(&php_openssl_request_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - php_openssl_request_object_handlers.offset = XtOffsetOf(php_openssl_request_object, std); + php_openssl_request_object_handlers.offset = offsetof(php_openssl_request_object, std); php_openssl_request_object_handlers.free_obj = php_openssl_request_free_obj; php_openssl_request_object_handlers.get_constructor = php_openssl_request_get_constructor; php_openssl_request_object_handlers.clone_obj = NULL; @@ -408,12 +779,28 @@ PHP_MINIT_FUNCTION(openssl) php_openssl_pkey_ce->default_object_handlers = &php_openssl_pkey_object_handlers; memcpy(&php_openssl_pkey_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - php_openssl_pkey_object_handlers.offset = XtOffsetOf(php_openssl_pkey_object, std); + php_openssl_pkey_object_handlers.offset = offsetof(php_openssl_pkey_object, std); php_openssl_pkey_object_handlers.free_obj = php_openssl_pkey_free_obj; php_openssl_pkey_object_handlers.get_constructor = php_openssl_pkey_get_constructor; php_openssl_pkey_object_handlers.clone_obj = NULL; php_openssl_pkey_object_handlers.compare = zend_objects_not_comparable; + php_openssl_psk_ce = register_class_Openssl_Psk(); + php_openssl_psk_ce->default_object_handlers = &php_openssl_psk_object_handlers; + + memcpy(&php_openssl_psk_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + + php_openssl_session_ce = register_class_Openssl_Session(); + php_openssl_session_ce->create_object = php_openssl_session_create_object; + php_openssl_session_ce->default_object_handlers = &php_openssl_session_object_handlers; + + memcpy(&php_openssl_session_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + php_openssl_session_object_handlers.offset = offsetof(php_openssl_session_object, std); + php_openssl_session_object_handlers.free_obj = php_openssl_session_free_obj; + php_openssl_session_object_handlers.get_constructor = php_openssl_session_get_constructor; + php_openssl_session_object_handlers.clone_obj = NULL; + php_openssl_session_object_handlers.compare = zend_objects_not_comparable; + register_openssl_symbols(module_number); php_openssl_backend_init(); diff --git a/ext/openssl/openssl.stub.php b/ext/openssl/openssl.stub.php index 0111cc0cc7bc..6080ac323903 100644 --- a/ext/openssl/openssl.stub.php +++ b/ext/openssl/openssl.stub.php @@ -2,6 +2,67 @@ /** @generate-class-entries */ +namespace Openssl { + + class OpensslException extends Exception + { + } + + /** + * @strict-properties + */ + final class Psk + { + /** + * @cvalue PHP_OPENSSL_PSK_MAX_PSK_LEN + */ + public const int MAX_PSK_LEN = UNKNOWN; + + /** + * @cvalue PHP_OPENSSL_PSK_MAX_IDENTITY_LEN + */ + public const int MAX_IDENTITY_LEN = UNKNOWN; + + public readonly string $psk; + public readonly ?string $identity; + + public function __construct(string $psk, ?string $identity = null) {} + } + + /** + * @strict-properties + */ + final class Session + { + public readonly string $id; + + public function export(int $format = OPENSSL_ENCODING_PEM): string {} + + public static function import(string $data, int $format = OPENSSL_ENCODING_PEM): Session {} + + public function isResumable(): bool {} + + public function getTimeout(): int {} + + public function getCreatedAt(): int {} + + public function getProtocol(): ?string {} + + public function getCipher(): ?string {} + + public function hasTicket(): bool {} + + public function getTicketLifetimeHint(): ?int {} + + public function __serialize(): array {} + + public function __unserialize(array $data): void {} + } + +} + +namespace { + /** * @var string * @cvalue OPENSSL_VERSION_TEXT @@ -409,7 +470,6 @@ */ const OPENSSL_ENCODING_PEM = UNKNOWN; - /** * @strict-properties * @not-serializable @@ -699,3 +759,5 @@ function openssl_get_cert_locations(): array {} function openssl_password_hash(string $algo, #[\SensitiveParameter] string $password, array $options = []): string {} function openssl_password_verify(string $algo, #[\SensitiveParameter] string $password, string $hash): bool {} #endif + +} diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h index 32002cd81d5a..caf47a256e78 100644 --- a/ext/openssl/openssl_arginfo.h +++ b/ext/openssl/openssl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit openssl.stub.php instead. - * Stub hash: a571945d38a3460de017405454b61609811fe1b1 */ + * Stub hash: 4d38e81a2f73bb6dd4bbe7a3e0b8ba86600654e2 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_TYPE_MASK(0, certificate, OpenSSLCertificate, MAY_BE_STRING, NULL) @@ -406,6 +406,44 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_password_verify, 0, 3, _ ZEND_END_ARG_INFO() #endif +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Openssl_Psk___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, psk, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, identity, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Openssl_Session_export, 0, 0, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, format, IS_LONG, 0, "OPENSSL_ENCODING_PEM") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Openssl_Session_import, 0, 1, Openssl\\Session, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, format, IS_LONG, 0, "OPENSSL_ENCODING_PEM") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Openssl_Session_isResumable, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Openssl_Session_getTimeout, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_Openssl_Session_getCreatedAt arginfo_class_Openssl_Session_getTimeout + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Openssl_Session_getProtocol, 0, 0, IS_STRING, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Openssl_Session_getCipher arginfo_class_Openssl_Session_getProtocol + +#define arginfo_class_Openssl_Session_hasTicket arginfo_class_Openssl_Session_isResumable + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Openssl_Session_getTicketLifetimeHint, 0, 0, IS_LONG, 1) +ZEND_END_ARG_INFO() + +#define arginfo_class_Openssl_Session___serialize arginfo_openssl_get_cert_locations + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Openssl_Session___unserialize, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + ZEND_FUNCTION(openssl_x509_export_to_file); ZEND_FUNCTION(openssl_x509_export); ZEND_FUNCTION(openssl_x509_fingerprint); @@ -473,6 +511,18 @@ ZEND_FUNCTION(openssl_get_cert_locations); ZEND_FUNCTION(openssl_password_hash); ZEND_FUNCTION(openssl_password_verify); #endif +ZEND_METHOD(Openssl_Psk, __construct); +ZEND_METHOD(Openssl_Session, export); +ZEND_METHOD(Openssl_Session, import); +ZEND_METHOD(Openssl_Session, isResumable); +ZEND_METHOD(Openssl_Session, getTimeout); +ZEND_METHOD(Openssl_Session, getCreatedAt); +ZEND_METHOD(Openssl_Session, getProtocol); +ZEND_METHOD(Openssl_Session, getCipher); +ZEND_METHOD(Openssl_Session, hasTicket); +ZEND_METHOD(Openssl_Session, getTicketLifetimeHint); +ZEND_METHOD(Openssl_Session, __serialize); +ZEND_METHOD(Openssl_Session, __unserialize); static const zend_function_entry ext_functions[] = { ZEND_FE(openssl_x509_export_to_file, arginfo_openssl_x509_export_to_file) @@ -548,6 +598,26 @@ static const zend_function_entry ext_functions[] = { ZEND_FE_END }; +static const zend_function_entry class_Openssl_Psk_methods[] = { + ZEND_ME(Openssl_Psk, __construct, arginfo_class_Openssl_Psk___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static const zend_function_entry class_Openssl_Session_methods[] = { + ZEND_ME(Openssl_Session, export, arginfo_class_Openssl_Session_export, ZEND_ACC_PUBLIC) + ZEND_ME(Openssl_Session, import, arginfo_class_Openssl_Session_import, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + ZEND_ME(Openssl_Session, isResumable, arginfo_class_Openssl_Session_isResumable, ZEND_ACC_PUBLIC) + ZEND_ME(Openssl_Session, getTimeout, arginfo_class_Openssl_Session_getTimeout, ZEND_ACC_PUBLIC) + ZEND_ME(Openssl_Session, getCreatedAt, arginfo_class_Openssl_Session_getCreatedAt, ZEND_ACC_PUBLIC) + ZEND_ME(Openssl_Session, getProtocol, arginfo_class_Openssl_Session_getProtocol, ZEND_ACC_PUBLIC) + ZEND_ME(Openssl_Session, getCipher, arginfo_class_Openssl_Session_getCipher, ZEND_ACC_PUBLIC) + ZEND_ME(Openssl_Session, hasTicket, arginfo_class_Openssl_Session_hasTicket, ZEND_ACC_PUBLIC) + ZEND_ME(Openssl_Session, getTicketLifetimeHint, arginfo_class_Openssl_Session_getTicketLifetimeHint, ZEND_ACC_PUBLIC) + ZEND_ME(Openssl_Session, __serialize, arginfo_class_Openssl_Session___serialize, ZEND_ACC_PUBLIC) + ZEND_ME(Openssl_Session, __unserialize, arginfo_class_Openssl_Session___unserialize, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static void register_openssl_symbols(int module_number) { REGISTER_STRING_CONSTANT("OPENSSL_VERSION_TEXT", OPENSSL_VERSION_TEXT, CONST_PERSISTENT); @@ -751,6 +821,66 @@ static void register_openssl_symbols(int module_number) #endif } +static zend_class_entry *register_class_Openssl_OpensslException(zend_class_entry *class_entry_Openssl_Exception) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Openssl", "OpensslException", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Openssl_Exception, 0); + + return class_entry; +} + +static zend_class_entry *register_class_Openssl_Psk(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Openssl", "Psk", class_Openssl_Psk_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES); + + zval const_MAX_PSK_LEN_value; + ZVAL_LONG(&const_MAX_PSK_LEN_value, PHP_OPENSSL_PSK_MAX_PSK_LEN); + zend_string *const_MAX_PSK_LEN_name = zend_string_init_interned("MAX_PSK_LEN", sizeof("MAX_PSK_LEN") - 1, true); + zend_declare_typed_class_constant(class_entry, const_MAX_PSK_LEN_name, &const_MAX_PSK_LEN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release_ex(const_MAX_PSK_LEN_name, true); + + zval const_MAX_IDENTITY_LEN_value; + ZVAL_LONG(&const_MAX_IDENTITY_LEN_value, PHP_OPENSSL_PSK_MAX_IDENTITY_LEN); + zend_string *const_MAX_IDENTITY_LEN_name = zend_string_init_interned("MAX_IDENTITY_LEN", sizeof("MAX_IDENTITY_LEN") - 1, true); + zend_declare_typed_class_constant(class_entry, const_MAX_IDENTITY_LEN_name, &const_MAX_IDENTITY_LEN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release_ex(const_MAX_IDENTITY_LEN_name, true); + + zval property_psk_default_value; + ZVAL_UNDEF(&property_psk_default_value); + zend_string *property_psk_name = zend_string_init("psk", sizeof("psk") - 1, true); + zend_declare_typed_property(class_entry, property_psk_name, &property_psk_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release_ex(property_psk_name, true); + + zval property_identity_default_value; + ZVAL_UNDEF(&property_identity_default_value); + zend_string *property_identity_name = zend_string_init("identity", sizeof("identity") - 1, true); + zend_declare_typed_property(class_entry, property_identity_name, &property_identity_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release_ex(property_identity_name, true); + + return class_entry; +} + +static zend_class_entry *register_class_Openssl_Session(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Openssl", "Session", class_Openssl_Session_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES); + + zval property_id_default_value; + ZVAL_UNDEF(&property_id_default_value); + zend_string *property_id_name = zend_string_init("id", sizeof("id") - 1, true); + zend_declare_typed_property(class_entry, property_id_name, &property_id_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release_ex(property_id_name, true); + + return class_entry; +} + static zend_class_entry *register_class_OpenSSLCertificate(void) { zend_class_entry ce, *class_entry; diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index 222053984916..5aa8d246177e 100644 --- a/ext/openssl/openssl_backend_common.c +++ b/ext/openssl/openssl_backend_common.c @@ -108,7 +108,7 @@ void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, void php_openssl_add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str) { - add_assoc_stringl(val, key, (char *)str->data, str->length); + add_assoc_stringl(val, key, (const char *)ASN1_STRING_get0_data(str), ASN1_STRING_length(str)); } time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr) @@ -140,12 +140,12 @@ time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr) } if (timestr_len < 13) { - php_error_docref(NULL, E_WARNING, "Unable to parse time string %s correctly", timestr->data); + php_error_docref(NULL, E_WARNING, "Unable to parse time string %s correctly", ASN1_STRING_get0_data(timestr)); return (time_t)-1; } if (ASN1_STRING_type(timestr) == V_ASN1_GENERALIZEDTIME && timestr_len < 15) { - php_error_docref(NULL, E_WARNING, "Unable to parse time string %s correctly", timestr->data); + php_error_docref(NULL, E_WARNING, "Unable to parse time string %s correctly", ASN1_STRING_get0_data(timestr)); return (time_t)-1; } @@ -626,8 +626,8 @@ int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension) } extension_data = X509_EXTENSION_get_data(extension); - p = extension_data->data; - length = extension_data->length; + p = ASN1_STRING_get0_data(extension_data); + length = ASN1_STRING_length(extension_data); if (method->it) { names = (GENERAL_NAMES*) (ASN1_item_d2i(NULL, &p, length, ASN1_ITEM_ptr(method->it))); diff --git a/ext/openssl/php_openssl.h b/ext/openssl/php_openssl.h index 9801f5458a76..20a04bbcd1a0 100644 --- a/ext/openssl/php_openssl.h +++ b/ext/openssl/php_openssl.h @@ -30,8 +30,10 @@ extern zend_module_entry openssl_module_entry; #define PHP_OPENSSL_API_VERSION 0x10100 #elif OPENSSL_VERSION_NUMBER < 0x30200000L #define PHP_OPENSSL_API_VERSION 0x30000 -#else +#elif OPENSSL_VERSION_NUMBER < 0x30300000L #define PHP_OPENSSL_API_VERSION 0x30200 +#else +#define PHP_OPENSSL_API_VERSION 0x30300 #endif #define PHP_OPENSSL_ERR_BUFFER_SIZE 16 @@ -161,9 +163,7 @@ typedef struct _php_openssl_certificate_object { extern zend_class_entry *php_openssl_certificate_ce; -static inline php_openssl_certificate_object *php_openssl_certificate_from_obj(zend_object *obj) { - return (php_openssl_certificate_object *)((char *)(obj) - XtOffsetOf(php_openssl_certificate_object, std)); -} +#define php_openssl_certificate_from_obj(obj) ZEND_CONTAINER_OF(obj, php_openssl_certificate_object, std) #define Z_OPENSSL_CERTIFICATE_P(zv) php_openssl_certificate_from_obj(Z_OBJ_P(zv)) @@ -176,9 +176,7 @@ typedef struct _php_openssl_x509_request_object { zend_object std; } php_openssl_request_object; -static inline php_openssl_request_object *php_openssl_request_from_obj(zend_object *obj) { - return (php_openssl_request_object *)((char *)(obj) - XtOffsetOf(php_openssl_request_object, std)); -} +#define php_openssl_request_from_obj(obj) ZEND_CONTAINER_OF(obj, php_openssl_request_object, std) #define Z_OPENSSL_REQUEST_P(zv) php_openssl_request_from_obj(Z_OBJ_P(zv)) @@ -192,15 +190,47 @@ typedef struct _php_openssl_pkey_object { zend_object std; } php_openssl_pkey_object; -static inline php_openssl_pkey_object *php_openssl_pkey_from_obj(zend_object *obj) { - return (php_openssl_pkey_object *)((char *)(obj) - XtOffsetOf(php_openssl_pkey_object, std)); -} +#define php_openssl_pkey_from_obj(obj) ZEND_CONTAINER_OF(obj, php_openssl_pkey_object, std) #define Z_OPENSSL_PKEY_P(zv) php_openssl_pkey_from_obj(Z_OBJ_P(zv)) bool php_openssl_is_pkey_ce(zval *val); void php_openssl_pkey_object_init(zval *zv, EVP_PKEY *pkey, bool is_private); +/* Openssl\Psk class */ + +/* Matches OpenSSL's PSK_MAX_PSK_LEN and PSK_MAX_IDENTITY_LEN */ +#define PHP_OPENSSL_PSK_MAX_PSK_LEN 256 +#define PHP_OPENSSL_PSK_MAX_IDENTITY_LEN 128 + +extern zend_class_entry *php_openssl_psk_ce; + +bool php_openssl_is_psk_ce(zval *val); +zend_string *php_openssl_psk_get_psk(zval *psk_zv); +zend_string *php_openssl_psk_get_identity(zval *psk_zv); + +/* Openssl\Session class */ + +#include + +typedef struct _php_openssl_session_object { + SSL_SESSION *session; + zend_object std; +} php_openssl_session_object; + +static inline php_openssl_session_object *php_openssl_session_from_obj(zend_object *obj) { + return (php_openssl_session_object *)((char *)(obj) - offsetof(php_openssl_session_object, std)); +} + +#define Z_OPENSSL_SESSION_P(zv) php_openssl_session_from_obj(Z_OBJ_P(zv)) + +/* Extern declarations for xp_ssl.c */ +extern zend_class_entry *php_openssl_session_ce; + +void php_openssl_session_object_init(zval *zv, SSL_SESSION *session); +bool php_openssl_is_session_ce(zval *val); +SSL_SESSION *php_openssl_session_from_zval(zval *zv); + #if defined(HAVE_OPENSSL_ARGON2) /** diff --git a/ext/openssl/tests/ServerClientTestCase.inc b/ext/openssl/tests/ServerClientTestCase.inc index 8eedbfdebee8..c5db41d48417 100644 --- a/ext/openssl/tests/ServerClientTestCase.inc +++ b/ext/openssl/tests/ServerClientTestCase.inc @@ -100,7 +100,8 @@ class ServerClientTestCase $ini = php_ini_loaded_file(); $cmd = sprintf( '%s %s "%s" %s', - PHP_BINARY, $ini ? "-n -c $ini" : "", + // XXX: TEST_PHP_EXTRA_ARGS for run-test values won't work here? + PHP_BINARY, $ini ? "-n -c $ini -d error_include_args=0" : "", __FILE__, WORKER_ARGV_VALUE ); @@ -179,6 +180,9 @@ class ServerClientTestCase if (empty($addr)) { throw new \Exception("Failed server start"); } + if (strpos($addr, 'SERVER_EXCEPTION') !== false) { + echo $addr; + } if ($code === false) { $clientCode = preg_replace('/{{\s*ADDR\s*}}/', $addr, $clientCode); } else { diff --git a/ext/openssl/tests/gh22081.phpt b/ext/openssl/tests/gh22081.phpt new file mode 100644 index 000000000000..17f74be7584c --- /dev/null +++ b/ext/openssl/tests/gh22081.phpt @@ -0,0 +1,76 @@ +--TEST-- +GH-22081: server reneg limit not reallocated across non-blocking retries +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + ]]); + + /* Plain TCP listener so the TLS handshake is driven manually below. */ + $server = stream_socket_server('tcp://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Complete each handshake in non-blocking mode so that php_openssl_enable_crypto() is + * re-entered across multiple WANT_READ/WANT_WRITE rounds per connection. */ + for ($i = 0; $i < 5; $i++) { + $conn = @stream_socket_accept($server, 30); + if (!$conn) { + continue; + } + stream_set_blocking($conn, false); + do { + $r = stream_socket_enable_crypto($conn, true, STREAM_CRYPTO_METHOD_TLS_SERVER); + } while ($r === 0); + + if ($r === true) { + fwrite($conn, "ok $i\n"); + } + fclose($conn); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ]]); + + for ($i = 0; $i < 5; $i++) { + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client) { + echo trim(fgets($client)) . "\n"; + fclose($client); + } + } +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('gh22081-server-reneg-nonblocking', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +ok 0 +ok 1 +ok 2 +ok 3 +ok 4 + diff --git a/ext/openssl/tests/session_resumption_cache_disabled.phpt b/ext/openssl/tests/session_resumption_cache_disabled.phpt new file mode 100644 index 000000000000..9e0e8a82f399 --- /dev/null +++ b/ext/openssl/tests/session_resumption_cache_disabled.phpt @@ -0,0 +1,86 @@ +--TEST-- +TLS session resumption - server with cache disabled +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'session_cache' => false, /* Disable session caching */ + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Accept two connections */ + for ($i = 0; $i < 2; $i++) { + $client = @stream_socket_accept($server, 30); + if ($client) { + fwrite($client, "No cache connection " . ($i + 1) . "\n"); + fclose($client); + } + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $globalSession = null; + + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function($stream, $session) use (&$globalSession) { + $globalSession = $session; + } + ]]); + + /* First connection */ + $client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client1) { + echo trim(fgets($client1)) . "\n"; + $meta1 = stream_get_meta_data($client1); + echo "First connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n"; + fclose($client1); + } + + /* Second connection - server won't use cached session */ + $ctx2 = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_data' => $globalSession, + ]]); + + $client2 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx2); + if ($client2) { + echo trim(fgets($client2)) . "\n"; + $meta2 = stream_get_meta_data($client2); + echo "Second connection resumed: " . ($meta2['crypto']['session_reused'] ? "yes" : "no") . "\n"; + fclose($client2); + } +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_disabled_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +No cache connection 1 +First connection resumed: no +No cache connection 2 +Second connection resumed: no diff --git a/ext/openssl/tests/session_resumption_client_basic.phpt b/ext/openssl/tests/session_resumption_client_basic.phpt new file mode 100644 index 000000000000..ee1d126a6d9f --- /dev/null +++ b/ext/openssl/tests/session_resumption_client_basic.phpt @@ -0,0 +1,89 @@ +--TEST-- +TLS session resumption - client basic resumption +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'session_cache' => true, + 'session_id_context' => 'test-basic', + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Accept two connections */ + for ($i = 0; $i < 2; $i++) { + $client = @stream_socket_accept($server, 30); + if ($client) { + fwrite($client, "Hello from server\n"); + fclose($client); + } + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $sessionData = ''; + + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function($stream, $session) use (&$sessionData) { + $sessionData = $session; + } + ]]); + + /* First connection - full handshake */ + $client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client1) { + echo trim(fgets($client1)) . "\n"; + $meta1 = stream_get_meta_data($client1); + echo "First connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo "Session data received: " . (!empty($sessionData) ? "yes" : "no") . "\n"; + fclose($client1); + } + + /* Second connection - resumed session */ + $ctx2 = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_data' => $sessionData, + ]]); + + $client2 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx2); + if ($client2) { + echo trim(fgets($client2)) . "\n"; + $meta2 = stream_get_meta_data($client2); + echo "Second connection resumed: " . ($meta2['crypto']['session_reused'] ? "yes" : "no") . "\n"; + fclose($client2); + } +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_resumption_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +Hello from server +First connection resumed: no +Session data received: yes +Hello from server +Second connection resumed: yes diff --git a/ext/openssl/tests/session_resumption_get_cb_no_ticket.phpt b/ext/openssl/tests/session_resumption_get_cb_no_ticket.phpt new file mode 100644 index 000000000000..f87f831a7859 --- /dev/null +++ b/ext/openssl/tests/session_resumption_get_cb_no_ticket.phpt @@ -0,0 +1,79 @@ +--TEST-- +TLS session resumption - warning when trying to enable tickets with session_get_cb +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'session_id_context' => 'test-app', + 'no_ticket' => false, // Explicitly trying to enable tickets + 'session_new_cb' => function($stream, $sessionData) { + // Store session + }, + 'session_get_cb' => function($stream, $sessionId) { + return null; + } + ]]); + + try { + $server = @stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + $client = @stream_socket_accept($server, 30); + if ($client === false) { + phpt_notify(message: "SERVER_FAILED_UNEXPECTEDLY"); + } else { + phpt_notify(message: "SERVER_CREATED_UNEXPECTEDLY"); + fclose($server); + } + } catch (\Throwable $e) { + phpt_notify(message: "SERVER_EXCEPTION: " . $e->getMessage()); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT; + + /* Try to use corrupted session data */ + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_data' => 'this_is_invalid_session_data', + ]]); + + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + + if ($client === false) { + echo "Connection failed as expected\n"; + } + + $result = phpt_wait(); + echo trim($result) . "\n"; +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_no_ticket_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +SERVER_EXCEPTION: Session tickets cannot be enabled when session_get_cb is setConnection failed as expected + diff --git a/ext/openssl/tests/session_resumption_get_cb_num_tickets_positive.phpt b/ext/openssl/tests/session_resumption_get_cb_num_tickets_positive.phpt new file mode 100644 index 000000000000..5563b0c22cf8 --- /dev/null +++ b/ext/openssl/tests/session_resumption_get_cb_num_tickets_positive.phpt @@ -0,0 +1,82 @@ +--TEST-- +TLS session resumption - num_tickets controls ticket generation (TLS 1.3) +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_SERVER, + 'num_tickets' => 3, // Issue 3 tickets per connection + ]]); + + $server = stream_socket_server('tlsv1.3://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + // Accept one connection + $client = @stream_socket_accept($server, 30); + if ($client) { + fwrite($client, "Ticket test\n"); + // Keep connection open briefly to allow tickets to be sent + usleep(100000); // 100ms + fclose($client); + } + + phpt_notify(message: "SERVER_DONE"); +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $ticketCount = 0; + + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT, + 'session_new_cb' => function($stream, $session) use (&$ticketCount) { + $ticketCount++; + } + ]]); + + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client) { + $response = fgets($client); + echo trim($response) . "\n"; + + // Keep connection open briefly to receive all tickets + usleep(150000); // 150ms + fclose($client); + } + + echo "Tickets received: $ticketCount\n"; + + $result = phpt_wait(); + echo trim($result) . "\n"; +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_num_tickets_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- +Ticket test +Tickets received: 3 +SERVER_DONE diff --git a/ext/openssl/tests/session_resumption_get_cb_num_tickets_zero.phpt b/ext/openssl/tests/session_resumption_get_cb_num_tickets_zero.phpt new file mode 100644 index 000000000000..13654cd451e9 --- /dev/null +++ b/ext/openssl/tests/session_resumption_get_cb_num_tickets_zero.phpt @@ -0,0 +1,112 @@ +--TEST-- +TLS session resumption - num_tickets = 0 disables tickets, forces session IDs +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'session_id_context' => 'test-no-tickets', + 'num_tickets' => 0, // Disable ticket issuance + 'session_new_cb' => function($stream, $session) use (&$sessionStore, &$newCbCalled) { + $key = bin2hex($session->id); + $sessionStore[$key] = $session; + $newCbCalled++; + }, + 'session_get_cb' => function($stream, $sessionId) use (&$sessionStore) { + $key = bin2hex($sessionId); + return $sessionStore[$key] ?? null; + }, + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + // Accept two connections + for ($i = 0; $i < 2; $i++) { + $client = @stream_socket_accept($server, 30); + if ($client) { + fwrite($client, "Response " . ($i + 1) . "\n"); + usleep(50000); // Allow session storage + fclose($client); + } + } + + phpt_notify(message: "NEW_CB_CALLS:$newCbCalled"); +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $sessionData = null; + $clientTickets = 0; + + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function($stream, $session) use (&$sessionData, &$clientTickets) { + $sessionData = $session; + $clientTickets++; + } + ]]); + + // First connection + $client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client1) { + $meta1 = stream_get_meta_data($client1); + echo "Client first connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo trim(fgets($client1)) . "\n"; + usleep(100000); // Wait for session storage + fclose($client1); + } + + echo "Client received tickets on first connection: $clientTickets\n"; + + // Second connection with resumption + $ctx2 = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_data' => $sessionData, + ]]); + + $client2 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx2); + if ($client2) { + $meta2 = stream_get_meta_data($client2); + echo "Client second connection resumed: " . ($meta2['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo trim(fgets($client2)) . "\n"; + fclose($client2); + } + + $result = phpt_wait(); + echo "Server: " . trim($result) . "\n"; +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_no_tickets_zero_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +Client first connection resumed: no +Response 1 +Client received tickets on first connection: 0 +Client second connection resumed: no +Response 2 +Server: NEW_CB_CALLS:0 diff --git a/ext/openssl/tests/session_resumption_import_export_session.phpt b/ext/openssl/tests/session_resumption_import_export_session.phpt new file mode 100644 index 000000000000..0d9a3274b113 --- /dev/null +++ b/ext/openssl/tests/session_resumption_import_export_session.phpt @@ -0,0 +1,98 @@ +--TEST-- +TLS session resumption - import and export session +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'session_cache' => true, + 'session_id_context' => 'test-basic', + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Accept single connections */ + $client = @stream_socket_accept($server, 30); + if ($client) { + fwrite($client, "Hello from server\n"); + fclose($client); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $sessionData = ''; + + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function($stream, $session) use (&$sessionData) { + if (empty($sessionData)) { + // default should be pem + $pemSessionData = $session->export(); + var_dump($pemSessionData); + $session = Openssl\Session::import($pemSessionData); + $pemSessionData = $session->export(OPENSSL_ENCODING_PEM); + var_dump($pemSessionData); + $session = Openssl\Session::import($pemSessionData, OPENSSL_ENCODING_PEM); + $derSessionData = $session->export(OPENSSL_ENCODING_DER); + var_dump(strlen($derSessionData) > 0); + var_dump(strpos($derSessionData, 'BEGIN SSL SESSION PARAMETERS') === false); + $session = Openssl\Session::import($derSessionData, OPENSSL_ENCODING_DER); + var_dump($session); + } + $sessionData = $session; + } + ]]); + + /* First connection - full handshake */ + $client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client1) { + echo trim(fgets($client1)) . "\n"; + $meta1 = stream_get_meta_data($client1); + echo "First connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo "Session data received: " . (!empty($sessionData) ? "yes" : "no") . "\n"; + fclose($client1); + } +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_resumption_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- +string(%d) "-----BEGIN SSL SESSION PARAMETERS----- +%a +-----END SSL SESSION PARAMETERS----- +" +string(%d) "-----BEGIN SSL SESSION PARAMETERS----- +%a +-----END SSL SESSION PARAMETERS----- +" +bool(true) +bool(true) +object(Openssl\Session)#%d (1) { + ["id"]=> + string(32) "%a" +} +Hello from server +First connection resumed: no +Session data received: yes diff --git a/ext/openssl/tests/session_resumption_invalid_callback.phpt b/ext/openssl/tests/session_resumption_invalid_callback.phpt new file mode 100644 index 000000000000..b6cfc90a0554 --- /dev/null +++ b/ext/openssl/tests/session_resumption_invalid_callback.phpt @@ -0,0 +1,60 @@ +--TEST-- +TLS session resumption - invalid callback throws TypeError +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + $client = @stream_socket_accept($server, 30); + if ($client) { + fclose($client); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT; + + /* Try to use invalid callback */ + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => 'not_a_valid_function', + ]]); + + try { + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + echo "Should not reach here\n"; + } catch (TypeError $e) { + echo "TypeError caught: " . (strpos($e->getMessage(), 'session_new_cb must be a valid callback') !== false ? "YES" : "NO"); + echo "\n"; + } +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_invalid_cb_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- +TypeError caught: YES diff --git a/ext/openssl/tests/session_resumption_invalid_data.phpt b/ext/openssl/tests/session_resumption_invalid_data.phpt new file mode 100644 index 000000000000..7e50b9235a2b --- /dev/null +++ b/ext/openssl/tests/session_resumption_invalid_data.phpt @@ -0,0 +1,66 @@ +--TEST-- +TLS session resumption - invalid session data is fatal +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + $client = @stream_socket_accept($server, 30); + if ($client) { + fclose($client); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT; + + /* Try to use corrupted session data */ + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_data' => 'this_is_invalid_session_data', + ]]); + + try { + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + + if ($client === false) { + echo "Connection failed unexpectedlyd\n"; + } + } catch (\Throwable $e) { + echo "Type error thrown: " . $e->getMessage() . "\n"; + } +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_invalid_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- + +Warning: stream_socket_client(): Failed to enable crypto in %s on line %d + +Warning: stream_socket_client(): Unable to connect to %s in %s on line %d +Type error thrown: session_data must be an OpenSSLSession instance diff --git a/ext/openssl/tests/session_resumption_invalid_session_import.phpt b/ext/openssl/tests/session_resumption_invalid_session_import.phpt new file mode 100644 index 000000000000..a9c5b65f2051 --- /dev/null +++ b/ext/openssl/tests/session_resumption_invalid_session_import.phpt @@ -0,0 +1,23 @@ +--TEST-- +TLS session resumption - invalid session import +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- +getMessage() . "\n"; +} +?> +--CLEAN-- + +--EXPECT-- +Failed to import session data diff --git a/ext/openssl/tests/session_resumption_new_cb_no_context.phpt b/ext/openssl/tests/session_resumption_new_cb_no_context.phpt new file mode 100644 index 000000000000..ee2852c54ad7 --- /dev/null +++ b/ext/openssl/tests/session_resumption_new_cb_no_context.phpt @@ -0,0 +1,78 @@ +--TEST-- +TLS session resumption - warning when session_new_cb without session_id_context and verify_peer enabled +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'verify_peer' => true, + 'cafile' => '%s', + 'session_new_cb' => function($stream, $session) { + echo "not called new_cb\n"; + }, + 'session_get_cb' => function($stream, $sessionId) { + echo "not called new_cb\n"; + return null; + } + /* Missing: 'session_id_context' => 'myapp' */ + ]]); + + try { + $server = @stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + $client = @stream_socket_accept($server, 30); + if ($client === false) { + phpt_notify(message: "SERVER_FAILED_UNEXPECTEDLY"); + } else { + phpt_notify(message: "SERVER_CREATED_UNEXPECTEDLY"); + fclose($server); + } + } catch (\Throwable $e) { + phpt_notify(message: "SERVER_EXCEPTION: " . $e->getMessage()); + } +CODE; +$serverCode = sprintf($serverCode, $certFile, $caCertFile); + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT; + + /* Try to use corrupted session data */ + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false + ]]); + + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + + $result = phpt_wait(); + echo trim($result) . "\n"; +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveCaCert($caCertFile); +$certificateGenerator->saveNewCertAsFileWithKey('session_verify_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +SERVER_EXCEPTION: session_id_context must be set if session_new_cb is set diff --git a/ext/openssl/tests/session_resumption_persistent_reject.phpt b/ext/openssl/tests/session_resumption_persistent_reject.phpt new file mode 100644 index 000000000000..835e9bb51648 --- /dev/null +++ b/ext/openssl/tests/session_resumption_persistent_reject.phpt @@ -0,0 +1,66 @@ +--TEST-- +TLS session resumption - callbacks rejected on persistent streams +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + $client = @stream_socket_accept($server, 30); + if ($client) { + fclose($client); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT; + + /* Try to use callback with persistent stream */ + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function($stream, $session) { + echo "This should never be called\n"; + } + ]]); + + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + + if ($client === false) { + echo "Connection failed as expected with persistent stream\n"; + } +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_persistent_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- + +Warning: stream_socket_client(): session_new_cb is not supported for persistent streams in %s on line %d + +Warning: stream_socket_client(): Failed to enable crypto in %s on line %d + +Warning: stream_socket_client(): Unable to connect to %s in %s on line %d +Connection failed as expected with persistent stream diff --git a/ext/openssl/tests/session_resumption_require_new_cb.phpt b/ext/openssl/tests/session_resumption_require_new_cb.phpt new file mode 100644 index 000000000000..a08408e2d90b --- /dev/null +++ b/ext/openssl/tests/session_resumption_require_new_cb.phpt @@ -0,0 +1,73 @@ +--TEST-- +TLS session resumption - server requires session_new_cb with session_get_cb +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'session_get_cb' => function($stream, $sessionId) { + return null; + } + ]]); + + try { + $server = @stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + $client = @stream_socket_accept($server, 30); + if ($client === false) { + phpt_notify(message: "SERVER_FAILED_UNEXPECTEDLY"); + } else { + phpt_notify(message: "SERVER_CREATED_UNEXPECTEDLY"); + fclose($server); + } + } catch (\Throwable $e) { + phpt_notify(message: "SERVER_EXCEPTION: " . $e->getMessage()); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $flags = STREAM_CLIENT_CONNECT; + + /* Try to use corrupted session data */ + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_data' => 'this_is_invalid_session_data', + ]]); + + $client = @stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + + if ($client === false) { + echo "Connection failed as expected\n"; + } + + $result = phpt_wait(); + echo trim($result) . "\n"; +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_require_cb_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +SERVER_EXCEPTION: session_new_cb is required when session_get_cb is providedConnection failed as expected diff --git a/ext/openssl/tests/session_resumption_serialize_session.phpt b/ext/openssl/tests/session_resumption_serialize_session.phpt new file mode 100644 index 000000000000..f2f3c98e5e5b --- /dev/null +++ b/ext/openssl/tests/session_resumption_serialize_session.phpt @@ -0,0 +1,84 @@ +--TEST-- +TLS session resumption - serialize session +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'session_cache' => true, + 'session_id_context' => 'test-basic', + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Accept single connections */ + $client = @stream_socket_accept($server, 30); + if ($client) { + fwrite($client, "Hello from server\n"); + fclose($client); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $sessionData = ''; + + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function($stream, $session) use (&$sessionData) { + if (empty($sessionData)) { + $serializedSessionData = serialize($session); + var_dump($serializedSessionData); + $session = unserialize($serializedSessionData); + var_dump($session); + } + $sessionData = $session; + } + ]]); + + /* First connection - full handshake */ + $client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client1) { + echo trim(fgets($client1)) . "\n"; + $meta1 = stream_get_meta_data($client1); + echo "First connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo "Session data received: " . (!empty($sessionData) ? "yes" : "no") . "\n"; + fclose($client1); + } +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_resumption_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- +string(%d) "O:15:"Openssl\Session":1:{s:3:"pem";s:%d:"-----BEGIN SSL SESSION PARAMETERS----- +%a +-----END SSL SESSION PARAMETERS----- +";}" +object(Openssl\Session)#9 (1) { + ["id"]=> +%a +} +Hello from server +First connection resumed: no +Session data received: yes diff --git a/ext/openssl/tests/session_resumption_server_external_with_context_id.phpt b/ext/openssl/tests/session_resumption_server_external_with_context_id.phpt new file mode 100644 index 000000000000..02c6a7dcfdc7 --- /dev/null +++ b/ext/openssl/tests/session_resumption_server_external_with_context_id.phpt @@ -0,0 +1,116 @@ +--TEST-- +TLS session resumption - server external cache callbacks with context id +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'session_id_context' => 'test-server', + 'session_new_cb' => function($stream, $session) use (&$sessionStore, &$newCbCalled) { + $key = bin2hex($session->id); + $sessionStore[$key] = $session; + $newCbCalled = true; + }, + 'session_get_cb' => function($stream, $sessionId) use (&$sessionStore, &$getCbCalled) { + $key = bin2hex($sessionId); + $getCbCalled = true; + return $sessionStore[$key] ?? null; + }, + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Accept two connections */ + for ($i = 0; $i < 2; $i++) { + $client = @stream_socket_accept($server, 30); + if ($client) { + fwrite($client, "Response " . ($i + 1) . "\n"); + fclose($client); + } + } + + /* Report results */ + $result = []; + if ($newCbCalled) $result[] = "NEW_CB_CALLED"; + if ($getCbCalled) $result[] = "GET_CB_CALLED"; + $result[] = "SESSIONS:" . count($sessionStore); + + phpt_notify(message: implode(",", $result)); +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $sessionData = null; + + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function($stream, $session) use (&$sessionData) { + $sessionData = $session; + } + ]]); + + /* First connection */ + $client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client1) { + $meta1 = stream_get_meta_data($client1); + echo "Client first connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo trim(fgets($client1)) . "\n"; + fclose($client1); + } + + echo "Session captured: " . ($sessionData !== null ? "YES" : "NO") . "\n"; + + /* Second connection with session resumption */ + $ctx2 = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_data' => $sessionData, + ]]); + + $client2 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx2); + if ($client2) { + $meta2 = stream_get_meta_data($client2); + echo "Client second connection resumed: " . ($meta2['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo trim(fgets($client2)) . "\n"; + fclose($client2); + } + + /* Get server callback results */ + $result = phpt_wait(); + echo "Server: " . trim($result) . "\n"; +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_external_proper_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- +Client first connection resumed: no +Response 1 +Session captured: YES +Client second connection resumed: yes +Response 2 +Server: NEW_CB_CALLED,GET_CB_CALLED,SESSIONS:3 diff --git a/ext/openssl/tests/session_resumption_server_external_with_context_id_tls12.phpt b/ext/openssl/tests/session_resumption_server_external_with_context_id_tls12.phpt new file mode 100644 index 000000000000..bdc3d2ce1bf6 --- /dev/null +++ b/ext/openssl/tests/session_resumption_server_external_with_context_id_tls12.phpt @@ -0,0 +1,116 @@ +--TEST-- +TLS session resumption - server external cache callbacks with context id for TLS 1.2 +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'session_id_context' => 'test-server', // Proper configuration + 'session_new_cb' => function($stream, $session) use (&$sessionStore, &$newCbCalled) { + $key = bin2hex($session->id); + $sessionStore[$key] = $session; + $newCbCalled = true; + }, + 'session_get_cb' => function($stream, $sessionId) use (&$sessionStore, &$getCbCalled) { + $key = bin2hex($sessionId); + $getCbCalled = true; + return $sessionStore[$key] ?? null; + }, + ]]); + + $server = stream_socket_server('tlsv1.2://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Accept two connections */ + for ($i = 0; $i < 2; $i++) { + $client = @stream_socket_accept($server, 30); + if ($client) { + fwrite($client, "Response " . ($i + 1) . "\n"); + fclose($client); + } + } + + /* Report results */ + $result = []; + if ($newCbCalled) $result[] = "NEW_CB_CALLED"; + if ($getCbCalled) $result[] = "GET_CB_CALLED"; + $result[] = "SESSIONS:" . count($sessionStore); + + phpt_notify(message: implode(",", $result)); +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $sessionData = null; + + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function($stream, $session) use (&$sessionData) { + $sessionData = $session; + } + ]]); + + /* First connection */ + $client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client1) { + $meta1 = stream_get_meta_data($client1); + echo "Client first connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo trim(fgets($client1)) . "\n"; + fclose($client1); + } + + echo "Session captured: " . ($sessionData !== null ? "YES" : "NO") . "\n"; + + /* Second connection with session resumption */ + $ctx2 = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_data' => $sessionData, + ]]); + + $client2 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx2); + if ($client2) { + $meta2 = stream_get_meta_data($client2); + echo "Client second connection resumed: " . ($meta2['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo trim(fgets($client2)) . "\n"; + fclose($client2); + } + + /* Get server callback results */ + $result = phpt_wait(); + echo "Server: " . trim($result) . "\n"; +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_external_proper_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- +Client first connection resumed: no +Response 1 +Session captured: YES +Client second connection resumed: yes +Response 2 +Server: NEW_CB_CALLED,GET_CB_CALLED,SESSIONS:1 diff --git a/ext/openssl/tests/session_resumption_server_external_with_no_verify.phpt b/ext/openssl/tests/session_resumption_server_external_with_no_verify.phpt new file mode 100644 index 000000000000..28bb97faff28 --- /dev/null +++ b/ext/openssl/tests/session_resumption_server_external_with_no_verify.phpt @@ -0,0 +1,121 @@ +--TEST-- +TLS session resumption - server external cache callbacks with no verify +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'verify_peer' => false, + 'no_ticket' => true, + 'session_cache' => true, + 'session_new_cb' => function($stream, $session) use (&$sessionStore, &$newCbCalled) { + $key = bin2hex($session->id); + $sessionStore[$key] = $session; + $newCbCalled = true; + }, + 'session_get_cb' => function($stream, $sessionId) use (&$sessionStore, &$getCbCalled) { + $key = bin2hex($sessionId); + $getCbCalled = true; + return $sessionStore[$key] ?? null; + }, + 'session_remove_cb' => function($stream, $sessionId) use (&$sessionStore, &$removeCbCalled) { + $key = bin2hex($sessionId); + unset($sessionStore[$key]); + $removeCbCalled = true; + } + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Accept two connections */ + for ($i = 0; $i < 2; $i++) { + $client = @stream_socket_accept($server, 30); + if ($client) { + fwrite($client, "Response " . ($i + 1) . "\n"); + fclose($client); + } + } + + /* Notify client about callback invocations */ + $result = []; + if ($newCbCalled) $result[] = "NEW_CB_CALLED"; + if ($getCbCalled) $result[] = "GET_CB_CALLED"; + if ($removeCbCalled) $result[] = "REMOVE_CB_CALLED"; + + phpt_notify(message: implode(",", $result)); +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $sessionData = null; + + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function($stream, $session) use (&$sessionData) { + $sessionData = $session; + } + ]]); + + /* First connection */ + $client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client1) { + $meta1 = stream_get_meta_data($client1); + echo "Client first connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo trim(fgets($client1)) . "\n"; + fclose($client1); + } + + /* Second connection with session resumption */ + $ctx2 = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_data' => $sessionData, + ]]); + + $client2 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx2); + if ($client2) { + $meta2 = stream_get_meta_data($client2); + echo "Client second connection resumed: " . ($meta2['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo trim(fgets($client2)) . "\n"; + fclose($client2); + } + + /* Get server callback results */ + $result = phpt_wait(); + echo "Server callbacks: " . trim($result) . "\n"; +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_server_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- +Client first connection resumed: no +Response 1 +Client second connection resumed: yes +Response 2 +Server callbacks: NEW_CB_CALLED,GET_CB_CALLED diff --git a/ext/openssl/tests/session_resumption_server_internal.phpt b/ext/openssl/tests/session_resumption_server_internal.phpt new file mode 100644 index 000000000000..d7c2633601e8 --- /dev/null +++ b/ext/openssl/tests/session_resumption_server_internal.phpt @@ -0,0 +1,99 @@ +--TEST-- +TLS session resumption - server internal cache +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'local_cert' => '%s', + 'session_id_context' => 'test-server', + 'session_cache' => true, + 'session_cache_size' => 1024, + 'session_timeout' => 300, + ]]); + + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + /* Accept two connections */ + for ($i = 0; $i < 2; $i++) { + $client = @stream_socket_accept($server, 30); + if ($client) { + fwrite($client, "Connection " . ($i + 1) . "\n"); + fclose($client); + } + } + + phpt_notify(message: "SERVER_DONE"); +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $sessionData = null; + + $flags = STREAM_CLIENT_CONNECT; + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_new_cb' => function($stream, $session) use (&$sessionData) { + $sessionData = $session; + } + ]]); + + /* First connection */ + $client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx); + if ($client1) { + $meta1 = stream_get_meta_data($client1); + echo "Client first connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo trim(fgets($client1)) . "\n"; + fclose($client1); + } + + echo "Session data received: " . ($sessionData !== null ? "YES" : "NO") . "\n"; + + /* Second connection with session resumption */ + $ctx2 = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'session_data' => $sessionData, + ]]); + + $client2 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx2); + if ($client2) { + $meta2 = stream_get_meta_data($client2); + echo "Client second connection resumed: " . ($meta2['crypto']['session_reused'] ? "yes" : "no") . "\n"; + echo trim(fgets($client2)) . "\n"; + fclose($client2); + } + + /* Wait for server */ + $result = phpt_wait(); + echo trim($result) . "\n"; +CODE; + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey('session_internal_cache_test', $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECTF-- +Client first connection resumed: no +Connection 1 +Session data received: YES +Client second connection resumed: yes +Connection 2 +SERVER_DONE diff --git a/ext/openssl/tests/stream_socket_get_crypto_status_basic.phpt b/ext/openssl/tests/stream_socket_get_crypto_status_basic.phpt new file mode 100644 index 000000000000..b659eee79e78 --- /dev/null +++ b/ext/openssl/tests/stream_socket_get_crypto_status_basic.phpt @@ -0,0 +1,22 @@ +--TEST-- +stream_socket_get_crypto_status(): constants and behavior on a non-crypto stream +--EXTENSIONS-- +openssl +--FILE-- + +--EXPECT-- +int(0) +int(1) +int(2) +bool(true) diff --git a/ext/openssl/tests/stream_socket_get_crypto_status_handshake.phpt b/ext/openssl/tests/stream_socket_get_crypto_status_handshake.phpt new file mode 100644 index 000000000000..2a1c554a79d0 --- /dev/null +++ b/ext/openssl/tests/stream_socket_get_crypto_status_handshake.phpt @@ -0,0 +1,97 @@ +--TEST-- +stream_socket_get_crypto_status(): reports WANT_READ/WANT_WRITE during a non-blocking handshake +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + ['local_cert' => '%s']]); + $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN; + $server = stream_socket_server("tls://127.0.0.1:0", $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + $conn = stream_socket_accept($server, 30); + if ($conn) { + fwrite($conn, "ok\n"); + phpt_wait(); + fclose($conn); + } +CODE; +$serverCode = sprintf($serverCode, $certFile); + +/* Client connects over plain TCP, then completes the TLS handshake in non-blocking mode, using + * the reported crypto status to select the right direction to wait on. */ +$clientCode = <<<'CODE' + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'peer_name' => '%s', + ]]); + + $client = stream_socket_client("tcp://{{ ADDR }}", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $ctx); + stream_set_blocking($client, false); + + $sawWant = false; + $pendingAlwaysWant = true; + + do { + $r = stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); + if ($r === 0) { + $status = stream_socket_get_crypto_status($client); + if ($status === STREAM_CRYPTO_STATUS_WANT_READ + || $status === STREAM_CRYPTO_STATUS_WANT_WRITE) { + $sawWant = true; + } else { + /* must never be NONE while the handshake is still pending */ + $pendingAlwaysWant = false; + } + + /* Wait on the direction the engine actually asked for. */ + $read = $write = $except = null; + if ($status === STREAM_CRYPTO_STATUS_WANT_WRITE) { + $write = [$client]; + } else { + $read = [$client]; + } + stream_select($read, $write, $except, 1); + } + } while ($r === 0); + + var_dump($r); + var_dump($sawWant); + var_dump($pendingAlwaysWant); + /* After a completed handshake the status is reset to NONE. */ + var_dump(stream_socket_get_crypto_status($client) === STREAM_CRYPTO_STATUS_NONE); + + stream_set_blocking($client, true); + echo trim(fgets($client)), "\n"; + phpt_notify(); + fclose($client); +CODE; +$clientCode = sprintf($clientCode, $peerName); + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey($peerName, $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +ok diff --git a/ext/openssl/tests/stream_socket_get_crypto_status_read.phpt b/ext/openssl/tests/stream_socket_get_crypto_status_read.phpt new file mode 100644 index 000000000000..308950a8ef03 --- /dev/null +++ b/ext/openssl/tests/stream_socket_get_crypto_status_read.phpt @@ -0,0 +1,95 @@ +--TEST-- +stream_socket_get_crypto_status(): reports WANT_READ on a non-blocking read with no application data +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + ['local_cert' => '%s']]); + $flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN; + $server = stream_socket_server("tls://127.0.0.1:0", $errno, $errstr, $flags, $ctx); + phpt_notify_server_start($server); + + $conn = stream_socket_accept($server, 30); + + /* Do not send anything until the client has performed its first read, so that the read is + * guaranteed to find no application data. */ + phpt_wait(); + fwrite($conn, "hello\n"); + + phpt_wait(); + fclose($conn); +CODE; +$serverCode = sprintf($serverCode, $certFile); + +$clientCode = <<<'CODE' + $ctx = stream_context_create(['ssl' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'peer_name' => '%s', + ]]); + + $client = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $ctx); + stream_set_blocking($client, false); + + /* No application data has been sent yet - a non-blocking read returns nothing and the crypto + * status reflects that the OpenSSL wants to read. */ + $data = fread($client, 100); + var_dump($data === '' || $data === false); + var_dump(stream_socket_get_crypto_status($client) === STREAM_CRYPTO_STATUS_WANT_READ); + + /* Now let the server send. */ + phpt_notify(); + + $buf = ''; + $read = [$client]; + $write = $except = null; + while (stream_select($read, $write, $except, 5)) { + $chunk = fread($client, 100); + if ($chunk === '' || $chunk === false) { + /* A non-application record (e.g. a TLS 1.3 session ticket) may arrive first. */ + if (feof($client)) { + break; + } + } else { + $buf .= $chunk; + if (strlen($buf) >= 6) { + break; + } + } + $read = [$client]; + $write = $except = null; + } + + echo trim($buf), "\n"; + /* A successful read clears the pending status back to NONE. */ + var_dump(stream_socket_get_crypto_status($client) === STREAM_CRYPTO_STATUS_NONE); + + phpt_notify(); + fclose($client); +CODE; +$clientCode = sprintf($clientCode, $peerName); + +include 'CertificateGenerator.inc'; +$certificateGenerator = new CertificateGenerator(); +$certificateGenerator->saveNewCertAsFileWithKey($peerName, $certFile); + +include 'ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--CLEAN-- + +--EXPECT-- +bool(true) +bool(true) +hello +bool(true) diff --git a/ext/openssl/tests/tls_psk_callback_not_callable.phpt b/ext/openssl/tests/tls_psk_callback_not_callable.phpt new file mode 100644 index 000000000000..29382e2a13ab --- /dev/null +++ b/ext/openssl/tests/tls_psk_callback_not_callable.phpt @@ -0,0 +1,46 @@ +--TEST-- +TLS PSK callback option must be a valid callable +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk("k", "id"); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => 'php_openssl_no_such_function', + ]]); + try { + @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + echo "no exception\n"; + } catch (TypeError $e) { + echo "caught: ", $e->getMessage(), "\n"; + } +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECTF-- +caught: psk_client_cb must be a valid callback, %s + diff --git a/ext/openssl/tests/tls_psk_callback_wrong_type.phpt b/ext/openssl/tests/tls_psk_callback_wrong_type.phpt new file mode 100644 index 000000000000..78d950f19974 --- /dev/null +++ b/ext/openssl/tests/tls_psk_callback_wrong_type.phpt @@ -0,0 +1,48 @@ +--TEST-- +TLS PSK client callback returning wrong type raises TypeError +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk("k", "id"); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => function ($stream) { + /* Returning a string instead of Openssl\Psk|null. */ + return "not a Psk object"; + }, + ]]); + try { + @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + echo "no exception\n"; + } catch (TypeError $e) { + echo "caught: ", $e->getMessage(), "\n"; + } +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +caught: PSK callback must return Openssl\Psk or null diff --git a/ext/openssl/tests/tls_psk_client_callback_null.phpt b/ext/openssl/tests/tls_psk_client_callback_null.phpt new file mode 100644 index 000000000000..92d970908c8e --- /dev/null +++ b/ext/openssl/tests/tls_psk_client_callback_null.phpt @@ -0,0 +1,45 @@ +--TEST-- +TLS 1.2 PSK: client callback returning null aborts handshake (no shared cipher) +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk("doesnotmatter", null); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + /* Reject: no PSK to use, no other ciphers offered. */ + return null; + }, + ]]); + $client = @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + var_dump($client); +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(false) + diff --git a/ext/openssl/tests/tls_psk_client_callback_throws.phpt b/ext/openssl/tests/tls_psk_client_callback_throws.phpt new file mode 100644 index 000000000000..13b2d71476c2 --- /dev/null +++ b/ext/openssl/tests/tls_psk_client_callback_throws.phpt @@ -0,0 +1,48 @@ +--TEST-- +TLS PSK client callback throwing exception propagates to stream_socket_client caller +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk("k", "id"); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + throw new RuntimeException('callback boom'); + }, + ]]); + try { + @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + echo "no exception\n"; + } catch (RuntimeException $e) { + echo "caught: ", $e->getMessage(), "\n"; + } +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +caught: callback boom + diff --git a/ext/openssl/tests/tls_psk_client_no_identity.phpt b/ext/openssl/tests/tls_psk_client_no_identity.phpt new file mode 100644 index 000000000000..bfc8d078ceff --- /dev/null +++ b/ext/openssl/tests/tls_psk_client_no_identity.phpt @@ -0,0 +1,49 @@ +--TEST-- +TLS PSK client callback must return Openssl\Psk with a non-null identity +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk("k", "id"); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + /* No identity supplied. */ + return new Openssl\Psk(str_repeat("\x42", 16)); + }, + ]]); + try { + @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + echo "no exception\n"; + } catch (ValueError $e) { + echo "caught: ", $e->getMessage(), "\n"; + } +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +caught: Client PSK callback must return Openssl\Psk with a non-null identity + diff --git a/ext/openssl/tests/tls_psk_mismatch.phpt b/ext/openssl/tests/tls_psk_mismatch.phpt new file mode 100644 index 000000000000..8fa90d115c5a --- /dev/null +++ b/ext/openssl/tests/tls_psk_mismatch.phpt @@ -0,0 +1,44 @@ +--TEST-- +TLS 1.2 PSK: mismatching key material aborts handshake +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return new Openssl\Psk(str_repeat("\x11", 16), $identity); + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + return new Openssl\Psk(str_repeat("\x22", 16), 'id'); + }, + ]]); + $client = @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + var_dump($client); +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(false) + diff --git a/ext/openssl/tests/tls_psk_tls12_basic.phpt b/ext/openssl/tests/tls_psk_tls12_basic.phpt new file mode 100644 index 000000000000..2f2a558c4b40 --- /dev/null +++ b/ext/openssl/tests/tls_psk_tls12_basic.phpt @@ -0,0 +1,62 @@ +--TEST-- +TLS 1.2 PSK basic client/server round-trip +--EXTENSIONS-- +openssl +--SKIPIF-- += 1.1.1 required"); +?> +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, + 'ciphers' => 'PSK', + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + if ($identity === 'client_id') { + return new Openssl\Psk("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff"); + } + return null; + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + $client = stream_socket_accept($server, 30); + fwrite($client, "hello-from-server"); + fread($client, 1024); + fclose($client); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT, + 'ciphers' => 'PSK', + 'verify_peer' => false, + 'verify_peer_name' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + return new Openssl\Psk( + "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + 'client_id' + ); + }, + ]]); + $client = stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 30, STREAM_CLIENT_CONNECT, $clientCtx); + var_dump($client !== false); + var_dump(fread($client, 1024)); + fwrite($client, "hello-from-client"); + /* Verify PSK was used (no peer cert) */ + $params = stream_context_get_params($client); + var_dump(isset($params['options']['ssl']['peer_certificate'])); + fclose($client); +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(true) +string(17) "hello-from-server" +bool(false) diff --git a/ext/openssl/tests/tls_psk_tls13_basic.phpt b/ext/openssl/tests/tls_psk_tls13_basic.phpt new file mode 100644 index 000000000000..a7642624252a --- /dev/null +++ b/ext/openssl/tests/tls_psk_tls13_basic.phpt @@ -0,0 +1,55 @@ +--TEST-- +TLS 1.3 PSK basic client/server round-trip +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_SERVER, + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + if ($identity === 'tls13-client') { + return new Openssl\Psk(str_repeat("\x42", 32)); + } + return null; + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + $client = stream_socket_accept($server, 30); + fwrite($client, "tls13-psk-ok"); + fclose($client); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT, + 'verify_peer' => false, + 'verify_peer_name' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + return new Openssl\Psk(str_repeat("\x42", 32), 'tls13-client'); + }, + ]]); + $client = stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 30, STREAM_CLIENT_CONNECT, $clientCtx); + var_dump($client !== false); + var_dump(fread($client, 1024)); + /* TLS 1.3 PSK: no peer cert */ + $params = stream_context_get_params($client); + var_dump(isset($params['options']['ssl']['peer_certificate'])); + fclose($client); +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(true) +string(12) "tls13-psk-ok" +bool(false) diff --git a/ext/openssl/tests/tls_psk_tls13_unknown_identity.phpt b/ext/openssl/tests/tls_psk_tls13_unknown_identity.phpt new file mode 100644 index 000000000000..830c02449dc9 --- /dev/null +++ b/ext/openssl/tests/tls_psk_tls13_unknown_identity.phpt @@ -0,0 +1,44 @@ +--TEST-- +TLS 1.3 PSK: server rejects unknown identity (no cert fallback) +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_SERVER, + 'psk_server_cb' => function ($stream, string $identity): ?Openssl\Psk { + return null; + }, + ]]); + $server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, + STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $serverCtx); + phpt_notify_server_start($server); + @stream_socket_accept($server, 3); +CODE; + +$clientCode = <<<'CODE' + $clientCtx = stream_context_create(['ssl' => [ + 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT, + 'verify_peer' => false, + 'verify_peer_name' => false, + 'psk_client_cb' => function ($stream): ?Openssl\Psk { + return new Openssl\Psk(str_repeat("\x42", 32), 'unknown-id'); + }, + ]]); + $client = @stream_socket_client('tls://{{ ADDR }}', $errno, $errstr, + 5, STREAM_CLIENT_CONNECT, $clientCtx); + var_dump($client); +CODE; + +include __DIR__ . '/ServerClientTestCase.inc'; +ServerClientTestCase::getInstance()->run($clientCode, $serverCode); +?> +--EXPECT-- +bool(false) + diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c index eea758da4713..a154aa4572f7 100644 --- a/ext/openssl/xp_ssl.c +++ b/ext/openssl/xp_ssl.c @@ -176,6 +176,25 @@ typedef struct _php_openssl_alpn_ctx_t { } php_openssl_alpn_ctx; #endif +/* TLS 1.3 PSK ciphersuite IDs */ +static const unsigned char php_openssl_tls13_aes128gcmsha256_id[] = { 0x13, 0x01 }; +static const unsigned char php_openssl_tls13_aes256gcmsha384_id[] = { 0x13, 0x02 }; + +/* Holds PSK callbacks */ +typedef struct _php_openssl_psk_callbacks_t { + int refcount; + zend_fcall_info_cache client_cb; + zend_fcall_info_cache server_cb; +} php_openssl_psk_callbacks_t; + +/* Holds session callback */ +typedef struct _php_openssl_session_callbacks_t { + int refcount; + zend_fcall_info_cache new_cb; + zend_fcall_info_cache get_cb; + zend_fcall_info_cache remove_cb; +} php_openssl_session_callbacks_t; + /* This implementation is very closely tied to the that of the native * sockets implemented in the core. * Don't try this technique in other extensions! @@ -188,6 +207,7 @@ typedef struct _php_openssl_netstream_data_t { int enable_on_connect; int is_client; int ssl_active; + int last_status; php_stream_xport_crypt_method_t method; php_openssl_handshake_bucket_t *reneg; php_openssl_sni_cert_t *sni_certs; @@ -195,6 +215,12 @@ typedef struct _php_openssl_netstream_data_t { #ifdef HAVE_TLS_ALPN php_openssl_alpn_ctx alpn_ctx; #endif + php_openssl_session_callbacks_t *session_callbacks; + php_openssl_psk_callbacks_t *psk_callbacks; + /* Identity buffer for TLS 1.3 client PSK whose lifetime outlives the + * psk_use_session_cb call but OpenSSL doesn't free it, so we own it. */ + unsigned char *psk_identity_buf; + size_t psk_identity_len; char *url_name; unsigned state_set:1; unsigned _spare:31; @@ -246,6 +272,10 @@ static int php_openssl_handle_ssl_error(php_stream *stream, int nr_bytes, bool i * packets: retry in next iteration */ errno = EAGAIN; retry = is_init ? true : sslsock->s.is_blocked; + if (!retry) { + sslsock->last_status = err == SSL_ERROR_WANT_READ ? + STREAM_CRYPTO_STATUS_WANT_READ : STREAM_CRYPTO_STATUS_WANT_WRITE; + } break; case SSL_ERROR_SYSCALL: if (ERR_peek_error() == 0) { @@ -492,12 +522,12 @@ static bool php_openssl_matches_san_list(X509 *peer, const char *subject_name) / } OPENSSL_free(cert_name); } else if (san->type == GEN_IPADD) { - if (san->d.iPAddress->length == 4) { + if (ASN1_STRING_length(san->d.iPAddress) == 4) { snprintf(ipbuffer, sizeof(ipbuffer), "%d.%d.%d.%d", - san->d.iPAddress->data[0], - san->d.iPAddress->data[1], - san->d.iPAddress->data[2], - san->d.iPAddress->data[3] + ASN1_STRING_get0_data(san->d.iPAddress)[0], + ASN1_STRING_get0_data(san->d.iPAddress)[1], + ASN1_STRING_get0_data(san->d.iPAddress)[2], + ASN1_STRING_get0_data(san->d.iPAddress)[3] ); if (strcasecmp(subject_name, (const char*)ipbuffer) == 0) { sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); @@ -506,9 +536,9 @@ static bool php_openssl_matches_san_list(X509 *peer, const char *subject_name) / } } #ifdef HAVE_IPV6_SAN - else if (san->d.ip->length == 16 && subject_name_is_ipv6) { + else if (ASN1_STRING_length(san->d.ip) == 16 && subject_name_is_ipv6) { ipbuffer[0] = 0; - EXPAND_IPV6_ADDRESS(ipbuffer, san->d.iPAddress->data); + EXPAND_IPV6_ADDRESS(ipbuffer, ASN1_STRING_get0_data(san->d.iPAddress)); if (strcasecmp((const char*)subject_name_ipv6_expanded, (const char*)ipbuffer) == 0) { sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); @@ -1547,287 +1577,1165 @@ static int php_openssl_server_alpn_callback(SSL *ssl_handle, #endif -static zend_result php_openssl_setup_crypto(php_stream *stream, - php_openssl_netstream_data_t *sslsock, - php_stream_xport_crypto_param *cparam) /* {{{ */ +static int php_openssl_get_ctx_stream_data_index(void) { - const SSL_METHOD *method; - int ssl_ctx_options; - int method_flags; - zend_long min_version = 0; - zend_long max_version = 0; - char *cipherlist = NULL; - char *alpn_protocols = NULL; - zval *val; - bool verify_peer = false; + static int ctx_data_index = -1; + if (ctx_data_index < 0) { + ctx_data_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); + } + return ctx_data_index; +} - if (sslsock->ssl_handle) { - if (sslsock->s.is_blocked) { - php_error_docref(NULL, E_WARNING, "SSL/TLS already set-up for this stream"); - return FAILURE; - } else { - return SUCCESS; +/** + * Build a SSL_SESSION suitable for use as an external PSK in TLS 1.3. + */ +static SSL_SESSION *php_openssl_psk_build_session(SSL *ssl, + const unsigned char *psk, size_t psk_len, const EVP_MD *md) +{ + const SSL_CIPHER *cipher; + + cipher = SSL_CIPHER_find(ssl, php_openssl_tls13_aes128gcmsha256_id); + if (cipher == NULL) { + return NULL; + } + + if (md != NULL && SSL_CIPHER_get_handshake_digest(cipher) != md) { + /* Fallback to SHA384 if SHA256 doesn't match */ + cipher = SSL_CIPHER_find(ssl, php_openssl_tls13_aes256gcmsha384_id); + if (cipher == NULL || SSL_CIPHER_get_handshake_digest(cipher) != md) { + return NULL; } } - ERR_clear_error(); + SSL_SESSION *sess = SSL_SESSION_new(); + if (sess == NULL) { + return NULL; + } - /* We need to do slightly different things based on client/server method - * so let's remember which method was selected */ - sslsock->is_client = cparam->inputs.method & STREAM_CRYPTO_IS_CLIENT; - method_flags = cparam->inputs.method & ~STREAM_CRYPTO_IS_CLIENT; + if (!SSL_SESSION_set1_master_key(sess, psk, psk_len) + || !SSL_SESSION_set_cipher(sess, cipher) + || !SSL_SESSION_set_protocol_version(sess, TLS1_3_VERSION)) { + SSL_SESSION_free(sess); + return NULL; + } - method = sslsock->is_client ? SSLv23_client_method() : SSLv23_server_method(); - sslsock->ctx = SSL_CTX_new(method); + return sess; +} - if (sslsock->ctx == NULL) { - php_error_docref(NULL, E_WARNING, "SSL context creation failure"); - return FAILURE; - } - GET_VER_OPT_LONG("min_proto_version", min_version); - GET_VER_OPT_LONG("max_proto_version", max_version); - method_flags = php_openssl_get_proto_version_flags(method_flags, min_version, max_version); - ssl_ctx_options = SSL_OP_ALL; +/** + * Invoke a user PHP callback (psk_client_cb or psk_server_cb). + */ +static zend_result php_openssl_call_psk_cb(php_stream *stream, zend_fcall_info_cache *fcc, + const unsigned char *identity, size_t identity_len, + zval *result) +{ + zval args[2]; + zval retval; + int argc; - if (GET_VER_OPT("no_ticket") && zend_is_true(val)) { - ssl_ctx_options |= SSL_OP_NO_TICKET; + ZVAL_RES(&args[0], stream->res); + + if (identity != NULL) { + ZVAL_STRINGL(&args[1], (const char *)identity, identity_len); + argc = 2; + } else { + argc = 1; } - ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + ZVAL_UNDEF(&retval); -#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF - /* Only for OpenSSL 3+ to keep OpenSSL 1.1.1 behavior */ - ssl_ctx_options |= SSL_OP_IGNORE_UNEXPECTED_EOF; -#endif + zend_call_known_fcc(fcc, &retval, argc, args, NULL); - if (!GET_VER_OPT("disable_compression") || zend_is_true(val)) { - ssl_ctx_options |= SSL_OP_NO_COMPRESSION; + if (identity != NULL) { + zval_ptr_dtor(&args[1]); } - if (GET_VER_OPT("verify_peer") && !zend_is_true(val)) { - php_openssl_disable_peer_verification(sslsock->ctx, stream); - } else { - verify_peer = true; - if (FAILURE == php_openssl_enable_peer_verification(sslsock->ctx, stream)) { - return FAILURE; - } + if (Z_ISUNDEF(retval)) { + ZVAL_UNDEF(result); + return FAILURE; } - /* callback for the passphrase (for localcert) */ - if (GET_VER_OPT("passphrase")) { - SSL_CTX_set_default_passwd_cb_userdata(sslsock->ctx, stream); - SSL_CTX_set_default_passwd_cb(sslsock->ctx, php_openssl_passwd_callback); + if (Z_TYPE(retval) == IS_NULL) { + ZVAL_NULL(result); + return SUCCESS; } - GET_VER_OPT_STRING("ciphers", cipherlist); -#ifndef USE_OPENSSL_SYSTEM_CIPHERS - if (!cipherlist) { - cipherlist = OPENSSL_DEFAULT_STREAM_CIPHERS; + if (!php_openssl_is_psk_ce(&retval)) { + zval_ptr_dtor(&retval); + zend_type_error("PSK callback must return Openssl\\Psk or null"); + ZVAL_UNDEF(result); + return FAILURE; } -#endif - if (cipherlist) { - if (SSL_CTX_set_cipher_list(sslsock->ctx, cipherlist) != 1) { - return FAILURE; - } + + ZVAL_COPY_VALUE(result, &retval); + return SUCCESS; +} + +#ifndef OPENSSL_NO_PSK +/* TLS 1.2 (and below) PSK callbacks. */ + +static unsigned int php_openssl_psk_client_cb(SSL *ssl, const char *hint, + char *identity_out, unsigned int max_identity_len, + unsigned char *psk_out, unsigned int max_psk_len) +{ + (void)hint; /* identity hint deliberately not exposed as it is useless */ + (void)max_identity_len; + (void)max_psk_len; + + php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, + php_openssl_get_ssl_stream_data_index()); + if (stream == NULL) { + return 0; } - if (GET_VER_OPT("security_level")) { - zend_long lval = zval_get_long(val); - if (lval < 0 || lval > 5) { - php_error_docref(NULL, E_WARNING, "Security level must be between 0 and 5"); - } -#ifdef HAVE_SEC_LEVEL - SSL_CTX_set_security_level(sslsock->ctx, lval); -#endif + php_openssl_netstream_data_t *sslsock = + (php_openssl_netstream_data_t *)stream->abstract; + if (sslsock == NULL || sslsock->psk_callbacks == NULL + || !ZEND_FCC_INITIALIZED(sslsock->psk_callbacks->client_cb)) { + return 0; } - GET_VER_OPT_STRING("alpn_protocols", alpn_protocols); - if (alpn_protocols) { -#ifdef HAVE_TLS_ALPN - { - unsigned short alpn_len; - unsigned char *alpn = php_openssl_alpn_protos_parse(&alpn_len, alpn_protocols); + zval result; + if (php_openssl_call_psk_cb(stream, &sslsock->psk_callbacks->client_cb, + NULL, 0, &result) != SUCCESS) { + return 0; + } - if (alpn == NULL) { - php_error_docref(NULL, E_WARNING, "Failed parsing comma-separated TLS ALPN protocol string"); - SSL_CTX_free(sslsock->ctx); - sslsock->ctx = NULL; - return FAILURE; - } - if (sslsock->is_client) { - SSL_CTX_set_alpn_protos(sslsock->ctx, alpn, alpn_len); - } else { - sslsock->alpn_ctx.data = (unsigned char *) pestrndup((const char*)alpn, alpn_len, php_stream_is_persistent(stream)); - sslsock->alpn_ctx.len = alpn_len; - SSL_CTX_set_alpn_select_cb(sslsock->ctx, php_openssl_server_alpn_callback, sslsock); - } + if (Z_TYPE(result) == IS_NULL) { + return 0; + } - efree(alpn); - } -#else - php_error_docref(NULL, E_WARNING, - "alpn_protocols support is not compiled into the OpenSSL library against which PHP is linked"); -#endif + zend_string *psk_str = php_openssl_psk_get_psk(&result); + zend_string *identity_str = php_openssl_psk_get_identity(&result); + + if (psk_str == NULL) { + zval_ptr_dtor(&result); + return 0; } - if (FAILURE == php_openssl_set_local_cert(sslsock->ctx, stream)) { - return FAILURE; + if (identity_str == NULL) { + zval_ptr_dtor(&result); + zend_value_error("Client PSK callback must return Openssl\\Psk with a non-null identity"); + return 0; } - SSL_CTX_set_options(sslsock->ctx, ssl_ctx_options); + memcpy(identity_out, ZSTR_VAL(identity_str), ZSTR_LEN(identity_str)); + identity_out[ZSTR_LEN(identity_str)] = '\0'; + memcpy(psk_out, ZSTR_VAL(psk_str), ZSTR_LEN(psk_str)); - SSL_CTX_set_min_proto_version(sslsock->ctx, php_openssl_get_min_proto_version(method_flags)); - SSL_CTX_set_max_proto_version(sslsock->ctx, php_openssl_get_max_proto_version(method_flags)); + unsigned int psk_len = (unsigned int)ZSTR_LEN(psk_str); + zval_ptr_dtor(&result); + return psk_len; +} - if (sslsock->is_client == 0 && - PHP_STREAM_CONTEXT(stream) && - FAILURE == php_openssl_set_server_specific_opts(stream, sslsock->ctx) - ) { - return FAILURE; - } +static unsigned int php_openssl_psk_server_cb(SSL *ssl, const char *identity, + unsigned char *psk_out, unsigned int max_psk_len) +{ + (void)max_psk_len; - sslsock->ssl_handle = SSL_new(sslsock->ctx); + php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, + php_openssl_get_ssl_stream_data_index()); + if (stream == NULL) { + return 0; + } - if (sslsock->ssl_handle == NULL - || !SSL_set_ex_data(sslsock->ssl_handle, php_openssl_get_ssl_stream_data_index(), stream)) { - php_error_docref(NULL, E_WARNING, "SSL handle creation failure"); - SSL_CTX_free(sslsock->ctx); - sslsock->ctx = NULL; -#ifdef HAVE_TLS_ALPN - if (sslsock->alpn_ctx.data) { - pefree(sslsock->alpn_ctx.data, php_stream_is_persistent(stream)); - sslsock->alpn_ctx.data = NULL; - } -#endif - return FAILURE; + php_openssl_netstream_data_t *sslsock = + (php_openssl_netstream_data_t *)stream->abstract; + if (sslsock == NULL || sslsock->psk_callbacks == NULL + || !ZEND_FCC_INITIALIZED(sslsock->psk_callbacks->server_cb)) { + return 0; } - if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) { - php_openssl_handle_ssl_error(stream, 0, true); + if (SSL_version(ssl) >= TLS1_3_VERSION) { + return 0; } -#ifdef HAVE_TLS_SNI - /* Enable server-side SNI */ - if (!sslsock->is_client && php_openssl_enable_server_sni(stream, sslsock, verify_peer) == FAILURE) { - return FAILURE; + if (identity == NULL) { + return 0; } -#endif - /* Enable server-side handshake renegotiation rate-limiting */ - if (!sslsock->is_client) { - php_openssl_init_server_reneg_limit(stream, sslsock); + size_t identity_len = strlen(identity); + + zval result; + if (php_openssl_call_psk_cb(stream, &sslsock->psk_callbacks->server_cb, + (const unsigned char *)identity, identity_len, &result) != SUCCESS) { + return 0; } -#ifdef SSL_MODE_RELEASE_BUFFERS - SSL_set_mode(sslsock->ssl_handle, SSL_MODE_RELEASE_BUFFERS); -#endif + if (Z_TYPE(result) == IS_NULL) { + return 0; + } - if (cparam->inputs.session) { - if (cparam->inputs.session->ops != &php_openssl_socket_ops) { - php_error_docref(NULL, E_WARNING, "Supplied session stream must be an SSL enabled stream"); - } else if (((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle == NULL) { - php_error_docref(NULL, E_WARNING, "Supplied SSL session stream is not initialized"); - } else { - SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle); - } + zend_string *psk_str = php_openssl_psk_get_psk(&result); + if (psk_str == NULL) { + zval_ptr_dtor(&result); + return 0; } - return SUCCESS; + memcpy(psk_out, ZSTR_VAL(psk_str), ZSTR_LEN(psk_str)); + unsigned int psk_len = (unsigned int)ZSTR_LEN(psk_str); + + zval_ptr_dtor(&result); + return psk_len; } -/* }}} */ +#endif /* OPENSSL_NO_PSK */ -static int php_openssl_capture_peer_certs(php_stream *stream, - php_openssl_netstream_data_t *sslsock, X509 *peer_cert) /* {{{ */ +/* TLS 1.3 PSK callbacks */ + +static int php_openssl_psk_use_session_cb(SSL *ssl, const EVP_MD *md, + const unsigned char **id, size_t *idlen, SSL_SESSION **sess) { - zval *val, zcert; - php_openssl_certificate_object *cert_object; - int cert_captured = 0; + *id = NULL; + *idlen = 0; + *sess = NULL; - if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), - "ssl", "capture_peer_cert")) && - zend_is_true(val) - ) { - object_init_ex(&zcert, php_openssl_certificate_ce); - cert_object = Z_OPENSSL_CERTIFICATE_P(&zcert); - cert_object->x509 = peer_cert; + php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, + php_openssl_get_ssl_stream_data_index()); + if (stream == NULL) { + return 1; + } - php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate", &zcert); - zval_ptr_dtor(&zcert); - cert_captured = 1; + php_openssl_netstream_data_t *sslsock = + (php_openssl_netstream_data_t *)stream->abstract; + if (sslsock == NULL || sslsock->psk_callbacks == NULL + || !ZEND_FCC_INITIALIZED(sslsock->psk_callbacks->client_cb)) { + return 1; } - if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), - "ssl", "capture_peer_cert_chain")) && - zend_is_true(val) - ) { - zval arr; - STACK_OF(X509) *chain; + zval result; + if (php_openssl_call_psk_cb(stream, &sslsock->psk_callbacks->client_cb, + NULL, 0, &result) != SUCCESS) { + return 0; + } - chain = SSL_get_peer_cert_chain(sslsock->ssl_handle); + if (Z_TYPE(result) == IS_NULL) { + return 1; /* user rejected, continue without PSK */ + } - if (chain && sk_X509_num(chain) > 0) { - int i; - array_init(&arr); + zend_string *psk_str = php_openssl_psk_get_psk(&result); + zend_string *identity_str = php_openssl_psk_get_identity(&result); - for (i = 0; i < sk_X509_num(chain); i++) { - X509 *mycert = X509_dup(sk_X509_value(chain, i)); + if (psk_str == NULL) { + zval_ptr_dtor(&result); + return 0; + } - object_init_ex(&zcert, php_openssl_certificate_ce); - cert_object = Z_OPENSSL_CERTIFICATE_P(&zcert); - cert_object->x509 = mycert; - add_next_index_zval(&arr, &zcert); - } + if (identity_str == NULL) { + zval_ptr_dtor(&result); + zend_value_error("Client PSK callback must return Openssl\\Psk with a non-null identity"); + return 0; + } - } else { - ZVAL_NULL(&arr); + SSL_SESSION *session = php_openssl_psk_build_session(ssl, + (const unsigned char *)ZSTR_VAL(psk_str), ZSTR_LEN(psk_str), md); + if (session == NULL) { + zval_ptr_dtor(&result); + if (md != NULL) { + /* Could not satisfy the server's chosen digest */ + return 1; } + return 0; + } - php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate_chain", &arr); - zval_ptr_dtor(&arr); + /* Identity buffer must outlive this callback. */ + if (sslsock->psk_identity_buf == NULL) { + sslsock->psk_identity_buf = emalloc(PHP_OPENSSL_PSK_MAX_IDENTITY_LEN); } + memcpy(sslsock->psk_identity_buf, ZSTR_VAL(identity_str), ZSTR_LEN(identity_str)); + sslsock->psk_identity_len = ZSTR_LEN(identity_str); - return cert_captured; -} -/* }}} */ + *id = sslsock->psk_identity_buf; + *idlen = sslsock->psk_identity_len; + *sess = session; -static zend_result php_openssl_set_blocking(php_openssl_netstream_data_t *sslsock, int block) -{ - zend_result result = php_set_sock_blocking(sslsock->s.socket, block); - if (EXPECTED(SUCCESS == result)) { - sslsock->s.is_blocked = block; - } - return result; + zval_ptr_dtor(&result); + return 1; } -static int php_openssl_enable_crypto(php_stream *stream, - php_openssl_netstream_data_t *sslsock, - php_stream_xport_crypto_param *cparam) /* {{{ */ +static int php_openssl_psk_find_session_cb(SSL *ssl, const unsigned char *identity, + size_t identity_len, SSL_SESSION **sess) { - int n; - int retry = 1; - int cert_captured = 0; - X509 *peer_cert; + *sess = NULL; - if (cparam->inputs.activate && !sslsock->ssl_active) { - struct timeval start_time, *timeout; - bool blocked = sslsock->s.is_blocked, has_timeout = false; + php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, + php_openssl_get_ssl_stream_data_index()); + if (stream == NULL) { + return 1; + } -#ifdef HAVE_TLS_SNI - if (sslsock->is_client) { - php_openssl_enable_client_sni(stream, sslsock); + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t *)stream->abstract; + if (sslsock == NULL || sslsock->psk_callbacks == NULL + || !ZEND_FCC_INITIALIZED(sslsock->psk_callbacks->server_cb)) { + return 1; + } + + zval result; + if (php_openssl_call_psk_cb(stream, &sslsock->psk_callbacks->server_cb, + identity, identity_len, &result) != SUCCESS) { + return 0; + } + + if (Z_TYPE(result) == IS_NULL) { + return 1; /* identity unknown - let handshake fall through */ + } + + zend_string *psk_str = php_openssl_psk_get_psk(&result); + if (psk_str == NULL) { + zval_ptr_dtor(&result); + return 0; + } + + SSL_SESSION *session = php_openssl_psk_build_session(ssl, + (const unsigned char *)ZSTR_VAL(psk_str), ZSTR_LEN(psk_str), NULL); + + zval_ptr_dtor(&result); + + if (session == NULL) { + return 0; + } + + *sess = session; + return 1; +} + +/* PSK setup */ + +static zend_result php_openssl_validate_and_allocate_psk_callback( + php_openssl_netstream_data_t *sslsock, const zval *callable, + bool is_client, bool is_persistent) +{ + const char *callback_name = is_client ? "psk_client_cb" : "psk_server_cb"; + if (is_persistent) { + php_error_docref(NULL, E_WARNING, + "%s is not supported for persistent streams", callback_name); + return FAILURE; + } + + char *is_callable_error = NULL; + zend_fcall_info_cache fcc = {0}; + if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, &is_callable_error)) { + if (is_callable_error) { + zend_type_error("%s must be a valid callback, %s", + callback_name, is_callable_error); + efree(is_callable_error); + } else { + zend_type_error("%s must be a valid callback", callback_name); + } + return FAILURE; + } + + if (!sslsock->psk_callbacks) { + sslsock->psk_callbacks = (php_openssl_psk_callbacks_t *)pecalloc( + 1, sizeof(php_openssl_psk_callbacks_t), is_persistent); + sslsock->psk_callbacks->refcount = 1; + } + zend_fcc_addref(&fcc); + if (is_client) { + sslsock->psk_callbacks->client_cb = fcc; + } else { + sslsock->psk_callbacks->server_cb = fcc; + } + + return SUCCESS; +} + +static zend_result php_openssl_setup_client_psk(php_stream *stream, + php_openssl_netstream_data_t *sslsock) +{ + zval *val; + + if (!GET_VER_OPT("psk_client_cb")) { + return SUCCESS; + } + + if (FAILURE == php_openssl_validate_and_allocate_psk_callback( + sslsock, val, true, php_stream_is_persistent(stream))) { + return FAILURE; + } + +#ifndef OPENSSL_NO_PSK + SSL_CTX_set_psk_client_callback(sslsock->ctx, php_openssl_psk_client_cb); +#endif + SSL_CTX_set_psk_use_session_callback(sslsock->ctx, php_openssl_psk_use_session_cb); + + return SUCCESS; +} + +static zend_result php_openssl_setup_server_psk(php_stream *stream, + php_openssl_netstream_data_t *sslsock) +{ + zval *val; + + if (!GET_VER_OPT("psk_server_cb")) { + return SUCCESS; + } + + if (FAILURE == php_openssl_validate_and_allocate_psk_callback( + sslsock, val, false, php_stream_is_persistent(stream))) { + return FAILURE; + } + +#ifndef OPENSSL_NO_PSK + SSL_CTX_set_psk_server_callback(sslsock->ctx, php_openssl_psk_server_cb); +#endif + SSL_CTX_set_psk_find_session_callback(sslsock->ctx, php_openssl_psk_find_session_cb); + + if (!GET_VER_OPT("session_id_context")) { + static const unsigned char default_psk_sid_ctx[] = "PHP_PSK"; + SSL_CTX_set_session_id_context(sslsock->ctx, default_psk_sid_ctx, + sizeof(default_psk_sid_ctx) - 1); + } + + return SUCCESS; +} + +/** + * OpenSSL new session callback - called when a new session is established + */ +static int php_openssl_session_new_cb(SSL *ssl, SSL_SESSION *session) +{ + php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, php_openssl_get_ssl_stream_data_index()); + if (!stream) { + return 0; + } + + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t *)stream->abstract; + if (!sslsock || !sslsock->session_callbacks) { + return 0; + } + + /* Increment reference - we're giving ownership to the PHP object */ + SSL_SESSION_up_ref(session); + + zval args[2]; + + ZVAL_RES(&args[0], stream->res); + php_openssl_session_object_init(&args[1], session); + + zend_call_known_fcc(&sslsock->session_callbacks->new_cb, NULL, 2, args, NULL); + + zval_ptr_dtor(&args[1]); + + return 0; +} + +/** + * OpenSSL get session callback - called when server needs to retrieve a session + */ +static SSL_SESSION *php_openssl_session_get_cb(SSL *ssl, const unsigned char *session_id, + int session_id_len, int *copy) +{ + php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, php_openssl_get_ssl_stream_data_index()); + if (!stream) { + *copy = 0; + return NULL; + } + + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t *)stream->abstract; + if (!sslsock || !sslsock->session_callbacks) { + *copy = 0; + return NULL; + } + + zval args[2]; + zval retval; + + ZVAL_RES(&args[0], stream->res); + ZVAL_STRINGL(&args[1], (char *)session_id, session_id_len); + + SSL_SESSION *session = NULL; + + zend_call_known_fcc(&sslsock->session_callbacks->get_cb, &retval, 2, args, NULL); + zval_ptr_dtor(&args[1]); + + if (php_openssl_is_session_ce(&retval)) { + /* Get session from object and increment ref since OpenSSL will own it */ + php_openssl_session_object *obj = Z_OPENSSL_SESSION_P(&retval); + if (obj->session) { + SSL_SESSION_up_ref(obj->session); + session = obj->session; + } + } else if (Z_TYPE(retval) != IS_NULL) { + zend_type_error("session_get_cb return type must be null or OpenSSLSession"); + } + + zval_ptr_dtor(&retval); + + *copy = 0; + return session; +} + +/** + * OpenSSL remove session callback - called when a session is evicted from cache + */ +static void php_openssl_session_remove_cb(SSL_CTX *ctx, SSL_SESSION *session) +{ + php_stream *stream = (php_stream *)SSL_CTX_get_ex_data(ctx, php_openssl_get_ctx_stream_data_index()); + if (!stream) { + return; + } + + php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t *)stream->abstract; + if (!sslsock || !sslsock->session_callbacks) { + return; + } + + unsigned int session_id_len = 0; + const unsigned char *session_id = SSL_SESSION_get_id(session, &session_id_len); + + zval args[2]; + + ZVAL_RES(&args[0], stream->res); + ZVAL_STRINGL(&args[1], (char *)session_id, session_id_len); + + zend_call_known_fcc(&sslsock->session_callbacks->remove_cb, NULL, 2, args, NULL); + zval_ptr_dtor(&args[1]); +} + + +enum php_openssl_session_callback_type { + PHP_OPENSSL_NEW_CB, + PHP_OPENSSL_GET_CB, + PHP_OPENSSL_REMOVE_CB, +}; +/** + * Validate callable and allocate callback structure if needed. + */ +static zend_result php_openssl_validate_and_allocate_session_callback( + php_openssl_netstream_data_t *sslsock, const zval *callable, + enum php_openssl_session_callback_type cb_type, bool is_persistent) +{ + char *is_callable_error = NULL; + const char *callback_name; + + switch (cb_type) { + case PHP_OPENSSL_NEW_CB: + callback_name = "session_new_cb"; + break; + case PHP_OPENSSL_GET_CB: + callback_name = "session_get_cb"; + break; + case PHP_OPENSSL_REMOVE_CB: + callback_name = "session_remove_cb"; + break; + } + + /* Callbacks not supported for persistent streams */ + if (is_persistent) { + php_error_docref(NULL, E_WARNING, + "%s is not supported for persistent streams", callback_name); + return FAILURE; + } + + /* Validate callable */ + zend_fcall_info_cache fcc; + if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, &is_callable_error)) { + if (is_callable_error) { + zend_type_error("%s must be a valid callback, %s", callback_name, is_callable_error); + efree(is_callable_error); + } else { + zend_type_error("%s must be a valid callback", callback_name); + } + return FAILURE; + } + + /* Allocate callback structure if not already allocated */ + if (!sslsock->session_callbacks) { + sslsock->session_callbacks = (php_openssl_session_callbacks_t *)pecalloc( + 1, sizeof(php_openssl_session_callbacks_t), is_persistent); + sslsock->session_callbacks->refcount = 1; + } + + zend_fcc_addref(&fcc); + switch (cb_type) { + case PHP_OPENSSL_NEW_CB: + sslsock->session_callbacks->new_cb = fcc; + break; + case PHP_OPENSSL_GET_CB: + sslsock->session_callbacks->get_cb = fcc; + break; + case PHP_OPENSSL_REMOVE_CB: + sslsock->session_callbacks->remove_cb = fcc; + break; + } + + return SUCCESS; +} + +/** + * Configure session resumption options for client connections + */ +static zend_result php_openssl_setup_client_session(php_stream *stream, + php_openssl_netstream_data_t *sslsock) +{ + zval *val; + bool enable_client_cache = false; + bool is_persistent = php_stream_is_persistent(stream); + + if (GET_VER_OPT("session_data")) { + if (php_openssl_is_session_ce(val)) { + enable_client_cache = true; + } else if (Z_TYPE_P(val) != IS_NULL) { + zend_type_error("session_data must be an OpenSSLSession instance"); + return FAILURE; + } + } + + if (GET_VER_OPT("session_new_cb")) { + if (FAILURE == php_openssl_validate_and_allocate_session_callback( + sslsock, val, PHP_OPENSSL_NEW_CB, is_persistent)) { + return FAILURE; + } + + SSL_CTX_sess_set_new_cb(sslsock->ctx, php_openssl_session_new_cb); + enable_client_cache = true; + } + + if (enable_client_cache) { + SSL_CTX_set_session_cache_mode(sslsock->ctx, + SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL); + } + + return SUCCESS; +} + +static bool php_openssl_is_session_cache_enabled(php_stream *stream, bool internal_only) +{ + zval *val; + + if (GET_VER_OPT("session_cache")) { + return zend_is_true(val); + } + + if (internal_only) { + return false; + } + + return GET_VER_OPT("session_get_cb"); +} + +/** + * Configure session resumption options for server connections + */ +static zend_result php_openssl_setup_server_session(php_stream *stream, + php_openssl_netstream_data_t *sslsock) +{ + zval *val; + bool has_get_cb = false; + bool has_new_cb = false; + bool has_remove_cb = false; + bool has_session_id_context = false; + bool is_persistent = php_stream_is_persistent(stream); + + /* Check for session_get_cb first (determines cache mode) */ + if (GET_VER_OPT("session_get_cb")) { + if (FAILURE == php_openssl_validate_and_allocate_session_callback( + sslsock, val, PHP_OPENSSL_GET_CB, is_persistent)) { + return FAILURE; + } + has_get_cb = true; + } + + if (GET_VER_OPT("session_id_context")) { + if (Z_TYPE_P(val) != IS_STRING || Z_STRLEN_P(val) == 0) { + zend_type_error("session_id_context must be a non empty string"); + return FAILURE; + } + SSL_CTX_set_session_id_context(sslsock->ctx, (const unsigned char *) Z_STRVAL_P(val), + Z_STRLEN_P(val)); + has_session_id_context = true; + } + + /* Check for session_new_cb */ + if (GET_VER_OPT("session_new_cb")) { + if (FAILURE == php_openssl_validate_and_allocate_session_callback( + sslsock, val, PHP_OPENSSL_NEW_CB, is_persistent)) { + return FAILURE; + } + has_new_cb = true; + + if (!has_session_id_context && + (SSL_CTX_get_verify_mode(sslsock->ctx) & SSL_VERIFY_PEER) != 0) { + zend_value_error("session_id_context must be set if session_new_cb is set"); + return FAILURE; + } + } + + /* Validate: if session_get_cb is provided, session_new_cb is required */ + if (has_get_cb && !has_new_cb) { + zend_value_error("session_new_cb is required when session_get_cb is provided"); + return FAILURE; + } + + /* Check for session_remove_cb (optional) */ + if (GET_VER_OPT("session_remove_cb")) { + if (FAILURE == php_openssl_validate_and_allocate_session_callback( + sslsock, val, PHP_OPENSSL_REMOVE_CB, is_persistent)) { + return FAILURE; + } + + has_remove_cb = true; + } + + /* Configure cache mode based on whether external callbacks are provided */ + if (has_get_cb) { + /* External cache mode - disable internal cache */ + SSL_CTX_set_session_cache_mode(sslsock->ctx, + SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL); + + /* Set callbacks */ + SSL_CTX_sess_set_new_cb(sslsock->ctx, php_openssl_session_new_cb); + SSL_CTX_sess_set_get_cb(sslsock->ctx, php_openssl_session_get_cb); + + if (has_remove_cb) { + SSL_CTX_sess_set_remove_cb(sslsock->ctx, php_openssl_session_remove_cb); + } + + // Disable tickets (they won't work anyway) and warn if explicity enabled + SSL_CTX_set_options(sslsock->ctx, SSL_OP_NO_TICKET); + if (GET_VER_OPT("no_ticket") && !zend_is_true(val)) { + zend_value_error("Session tickets cannot be enabled when session_get_cb is set"); + } + } else if (php_openssl_is_session_cache_enabled(stream, true)) { + if (!has_session_id_context && + (SSL_CTX_get_verify_mode(sslsock->ctx) & SSL_VERIFY_PEER) != 0) { + zend_value_error("session_id_context must be set for internal session cache"); + } + + /* Internal cache mode */ + SSL_CTX_set_session_cache_mode(sslsock->ctx, SSL_SESS_CACHE_SERVER); + + /* Handle session_cache_size */ + if (GET_VER_OPT("session_cache_size")) { + zend_long cache_size = zval_get_long(val); + if (cache_size > 0) { + SSL_CTX_sess_set_cache_size(sslsock->ctx, cache_size); + } else { + zend_value_error("session_cache_size must be positive"); + } + } else { + /* Default cache size from RFC */ + SSL_CTX_sess_set_cache_size(sslsock->ctx, 20480); + } + + /* Handle session_timeout */ + if (GET_VER_OPT("session_timeout")) { + zend_long timeout = zval_get_long(val); + if (timeout > 0) { + SSL_CTX_set_timeout(sslsock->ctx, timeout); + } else { + zend_value_error("session_timeout must be positive"); + } + } else { + /* Default timeout from RFC */ + SSL_CTX_set_timeout(sslsock->ctx, 300); + } + + /* Optional notification callback for internal cache */ + if (has_new_cb) { + SSL_CTX_sess_set_new_cb(sslsock->ctx, php_openssl_session_new_cb); + } + } else { + /* Session caching disabled */ + SSL_CTX_set_session_cache_mode(sslsock->ctx, SSL_SESS_CACHE_OFF); + } + + return SUCCESS; +} + +static zend_result php_openssl_apply_client_session_data(php_stream *stream, + php_openssl_netstream_data_t *sslsock) +{ + zval *val; + + if (GET_VER_OPT("session_data")) { + SSL_SESSION *session = NULL; + bool needs_free = false; + + if (php_openssl_is_session_ce(val)) { + session = php_openssl_session_from_zval(val); + if (!session) { + php_error_docref(NULL, E_WARNING, + "Invalid OpenSSLSession object, falling back to full handshake"); + return FAILURE; + } + /* Object owns the session, we just borrow it */ + needs_free = false; + } else if (Z_TYPE_P(val) != IS_NULL) { + zend_type_error("session_data must be an OpenSSLSession instance"); + return FAILURE; + } + + if (session) { + if (SSL_set_session(sslsock->ssl_handle, session) != 1) { + php_error_docref(NULL, E_WARNING, + "Failed to set session for resumption, falling back to full handshake"); + if (needs_free) { + SSL_SESSION_free(session); + } + ERR_clear_error(); + return FAILURE; + } + + if (needs_free) { + SSL_SESSION_free(session); + } + } + } + + return SUCCESS; +} + +static zend_result php_openssl_create_server_ctx(php_stream *stream, + php_openssl_netstream_data_t *sslsock, int method_flags) +{ + zval *val; + + const SSL_METHOD *method = sslsock->is_client ? SSLv23_client_method() : SSLv23_server_method(); + sslsock->ctx = SSL_CTX_new(method); + + if (sslsock->ctx == NULL) { + php_error_docref(NULL, E_WARNING, "SSL context creation failure"); + return FAILURE; + } + + SSL_CTX_set_ex_data(sslsock->ctx, php_openssl_get_ctx_stream_data_index(), stream); + + zend_long min_version = 0; + zend_long max_version = 0; + GET_VER_OPT_LONG("min_proto_version", min_version); + GET_VER_OPT_LONG("max_proto_version", max_version); + method_flags = php_openssl_get_proto_version_flags(method_flags, min_version, max_version); + int ssl_ctx_options = SSL_OP_ALL; + + if (GET_VER_OPT("no_ticket") && zend_is_true(val)) { + ssl_ctx_options |= SSL_OP_NO_TICKET; + } + if (GET_VER_OPT("num_tickets")) { + zend_long num_tickets = zval_get_long(val); + if (num_tickets >= 0) { + SSL_CTX_set_num_tickets(sslsock->ctx, num_tickets); + } + } + + ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + +#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF + /* Only for OpenSSL 3+ to keep OpenSSL 1.1.1 behavior */ + ssl_ctx_options |= SSL_OP_IGNORE_UNEXPECTED_EOF; +#endif + + if (!GET_VER_OPT("disable_compression") || zend_is_true(val)) { + ssl_ctx_options |= SSL_OP_NO_COMPRESSION; + } + + bool verify_peer = false; + if (GET_VER_OPT("verify_peer") && !zend_is_true(val)) { + php_openssl_disable_peer_verification(sslsock->ctx, stream); + } else { + verify_peer = true; + if (FAILURE == php_openssl_enable_peer_verification(sslsock->ctx, stream)) { + return FAILURE; + } + } + + /* callback for the passphrase (for localcert) */ + if (GET_VER_OPT("passphrase")) { + SSL_CTX_set_default_passwd_cb_userdata(sslsock->ctx, stream); + SSL_CTX_set_default_passwd_cb(sslsock->ctx, php_openssl_passwd_callback); + } + + char *cipherlist = NULL; + GET_VER_OPT_STRING("ciphers", cipherlist); +#ifndef USE_OPENSSL_SYSTEM_CIPHERS + if (!cipherlist) { + cipherlist = OPENSSL_DEFAULT_STREAM_CIPHERS; + } +#endif + if (cipherlist) { + if (SSL_CTX_set_cipher_list(sslsock->ctx, cipherlist) != 1) { + return FAILURE; + } + } + + if (GET_VER_OPT("security_level")) { + zend_long lval = zval_get_long(val); + if (lval < 0 || lval > 5) { + php_error_docref(NULL, E_WARNING, "Security level must be between 0 and 5"); + } +#ifdef HAVE_SEC_LEVEL + SSL_CTX_set_security_level(sslsock->ctx, lval); +#endif + } + + char *alpn_protocols = NULL; + GET_VER_OPT_STRING("alpn_protocols", alpn_protocols); + if (alpn_protocols) { +#ifdef HAVE_TLS_ALPN + { + unsigned short alpn_len; + unsigned char *alpn = php_openssl_alpn_protos_parse(&alpn_len, alpn_protocols); + + if (alpn == NULL) { + php_error_docref(NULL, E_WARNING, "Failed parsing comma-separated TLS ALPN protocol string"); + SSL_CTX_free(sslsock->ctx); + sslsock->ctx = NULL; + return FAILURE; + } + if (sslsock->is_client) { + SSL_CTX_set_alpn_protos(sslsock->ctx, alpn, alpn_len); + } else { + sslsock->alpn_ctx.data = (unsigned char *) pestrndup((const char*)alpn, alpn_len, php_stream_is_persistent(stream)); + sslsock->alpn_ctx.len = alpn_len; + SSL_CTX_set_alpn_select_cb(sslsock->ctx, php_openssl_server_alpn_callback, sslsock); + } + + efree(alpn); + } +#else + php_error_docref(NULL, E_WARNING, + "alpn_protocols support is not compiled into the OpenSSL library against which PHP is linked"); +#endif + } + + if (FAILURE == php_openssl_set_local_cert(sslsock->ctx, stream)) { + return FAILURE; + } + + SSL_CTX_set_options(sslsock->ctx, ssl_ctx_options); + + SSL_CTX_set_min_proto_version(sslsock->ctx, php_openssl_get_min_proto_version(method_flags)); + SSL_CTX_set_max_proto_version(sslsock->ctx, php_openssl_get_max_proto_version(method_flags)); + + + if (sslsock->is_client) { + if (FAILURE == php_openssl_setup_client_session(stream, sslsock)) { + return FAILURE; + } + if (FAILURE == php_openssl_setup_client_psk(stream, sslsock)) { + return FAILURE; + } + } else if (PHP_STREAM_CONTEXT(stream)) { + if (FAILURE == php_openssl_setup_server_session(stream, sslsock)) { + return FAILURE; + } + if (FAILURE == php_openssl_setup_server_psk(stream, sslsock)) { + return FAILURE; + } + if (FAILURE == php_openssl_set_server_specific_opts(stream, sslsock->ctx)) { + return FAILURE; + } + } + +#ifdef HAVE_TLS_SNI + /* Enable server-side SNI */ + if (!sslsock->is_client && php_openssl_enable_server_sni(stream, sslsock, verify_peer) == FAILURE) { + return FAILURE; + } +#endif + + return SUCCESS; +} + +static zend_result php_openssl_setup_crypto(php_stream *stream, + php_openssl_netstream_data_t *sslsock, + php_stream_xport_crypto_param *cparam) /* {{{ */ +{ + if (sslsock->ssl_handle) { + if (sslsock->s.is_blocked) { + php_error_docref(NULL, E_WARNING, "SSL/TLS already set-up for this stream"); + return FAILURE; + } else { + return SUCCESS; + } + } + + ERR_clear_error(); + + /* We need to do slightly different things based on client/server method + * so let's remember which method was selected */ + sslsock->is_client = cparam->inputs.method & STREAM_CRYPTO_IS_CLIENT; + int method_flags = cparam->inputs.method & ~STREAM_CRYPTO_IS_CLIENT; + + /* Re-use SSL_CTX if session is set */ + if (cparam->inputs.session) { + php_openssl_netstream_data_t *parent_sslsock; + + if (cparam->inputs.session->ops != &php_openssl_socket_ops) { + php_error_docref(NULL, E_WARNING, "Supplied session stream must be an SSL enabled stream"); + } else if ((parent_sslsock = cparam->inputs.session->abstract)->ctx == NULL) { + php_error_docref(NULL, E_WARNING, "Supplied SSL session stream is not set up"); + } else if (sslsock->is_client && parent_sslsock->ssl_handle == NULL) { + php_error_docref(NULL, E_WARNING, "Supplied SSL session stream is not initialized"); + } else { + SSL_CTX_up_ref(parent_sslsock->ctx); + sslsock->ctx = parent_sslsock->ctx; + if (parent_sslsock->session_callbacks) { + parent_sslsock->session_callbacks->refcount++; + sslsock->session_callbacks = parent_sslsock->session_callbacks; + } + if (parent_sslsock->psk_callbacks) { + parent_sslsock->psk_callbacks->refcount++; + sslsock->psk_callbacks = parent_sslsock->psk_callbacks; + } + + sslsock->ssl_handle = SSL_new(sslsock->ctx); + if (!sslsock->ssl_handle) { + php_error_docref(NULL, E_WARNING, "SSL handle creation failure"); + SSL_CTX_free(sslsock->ctx); + sslsock->ctx = NULL; + return FAILURE; + } + + SSL_set_ex_data(sslsock->ssl_handle, php_openssl_get_ssl_stream_data_index(), stream); + + if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) { + php_openssl_handle_ssl_error(stream, 0, true); + } + + if (sslsock->is_client) { + if (SSL_copy_session_id(sslsock->ssl_handle, parent_sslsock->ssl_handle)) { + SSL_CTX_set_session_cache_mode(sslsock->ctx, SSL_SESS_CACHE_CLIENT); + } else { + php_error_docref(NULL, E_WARNING, "SSL session copying failed creation failure"); + } + } + + return SUCCESS; + } + } + + if (php_openssl_create_server_ctx(stream, sslsock, method_flags) == FAILURE) { + return FAILURE; + } + + sslsock->ssl_handle = SSL_new(sslsock->ctx); + + if (sslsock->ssl_handle == NULL + || !SSL_set_ex_data(sslsock->ssl_handle, php_openssl_get_ssl_stream_data_index(), stream)) { + php_error_docref(NULL, E_WARNING, "SSL handle creation failure"); + SSL_CTX_free(sslsock->ctx); + sslsock->ctx = NULL; +#ifdef HAVE_TLS_ALPN + if (sslsock->alpn_ctx.data) { + pefree(sslsock->alpn_ctx.data, php_stream_is_persistent(stream)); + sslsock->alpn_ctx.data = NULL; } #endif + return FAILURE; + } + + if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) { + php_openssl_handle_ssl_error(stream, 0, true); + } + + return SUCCESS; +} +/* }}} */ + +static int php_openssl_capture_peer_certs(php_stream *stream, + php_openssl_netstream_data_t *sslsock, X509 *peer_cert) /* {{{ */ +{ + zval *val, zcert; + php_openssl_certificate_object *cert_object; + int cert_captured = 0; + + if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), + "ssl", "capture_peer_cert")) && + zend_is_true(val) + ) { + object_init_ex(&zcert, php_openssl_certificate_ce); + cert_object = Z_OPENSSL_CERTIFICATE_P(&zcert); + cert_object->x509 = peer_cert; + + php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate", &zcert); + zval_ptr_dtor(&zcert); + cert_captured = 1; + } + + if (NULL != (val = php_stream_context_get_option(PHP_STREAM_CONTEXT(stream), + "ssl", "capture_peer_cert_chain")) && + zend_is_true(val) + ) { + zval arr; + STACK_OF(X509) *chain; + + chain = SSL_get_peer_cert_chain(sslsock->ssl_handle); + + if (chain && sk_X509_num(chain) > 0) { + int i; + array_init(&arr); + + for (i = 0; i < sk_X509_num(chain); i++) { + X509 *mycert = X509_dup(sk_X509_value(chain, i)); + + object_init_ex(&zcert, php_openssl_certificate_ce); + cert_object = Z_OPENSSL_CERTIFICATE_P(&zcert); + cert_object->x509 = mycert; + add_next_index_zval(&arr, &zcert); + } + + } else { + ZVAL_NULL(&arr); + } + + php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate_chain", &arr); + zval_ptr_dtor(&arr); + } + + return cert_captured; +} +/* }}} */ + +static zend_result php_openssl_set_blocking(php_openssl_netstream_data_t *sslsock, int block) +{ + zend_result result = php_set_sock_blocking(sslsock->s.socket, block); + if (EXPECTED(SUCCESS == result)) { + sslsock->s.is_blocked = block; + } + return result; +} + +static int php_openssl_enable_crypto(php_stream *stream, + php_openssl_netstream_data_t *sslsock, + php_stream_xport_crypto_param *cparam) /* {{{ */ +{ + int n; + int retry = 1; + int cert_captured = 0; + X509 *peer_cert; + + sslsock->last_status = STREAM_CRYPTO_STATUS_NONE; + + if (cparam->inputs.activate && !sslsock->ssl_active) { + struct timeval start_time, *timeout; + bool blocked = sslsock->s.is_blocked, has_timeout = false; if (!sslsock->state_set) { +#ifdef PHP_OPENSSL_TLS_DEBUG + BIO *b_out = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT); + SSL_set_msg_callback(sslsock->ssl_handle, SSL_trace); + SSL_set_msg_callback_arg(sslsock->ssl_handle, b_out); +#endif if (sslsock->is_client) { + /* Set session data for client */ + if (php_openssl_apply_client_session_data(stream, sslsock) == FAILURE) { + return -1; + } +#ifdef HAVE_TLS_SNI + php_openssl_enable_client_sni(stream, sslsock); +#endif SSL_set_connect_state(sslsock->ssl_handle); } else { + php_openssl_init_server_reneg_limit(stream, sslsock); SSL_set_accept_state(sslsock->ssl_handle); } sslsock->state_set = 1; } + SSL_set_mode(sslsock->ssl_handle, SSL_MODE_RELEASE_BUFFERS); + if (SUCCESS == php_openssl_set_blocking(sslsock, 0)) { /* The following mode are added only if we are able to change socket * to non blocking mode which is also used for read and write */ @@ -1999,6 +2907,7 @@ static ssize_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, si /* Now, do the IO operation. Don't block if we can't complete... */ ERR_clear_error(); + sslsock->last_status = STREAM_CRYPTO_STATUS_NONE; if (read) { nr_bytes = SSL_read(sslsock->ssl_handle, buf, (int)count); @@ -2073,6 +2982,10 @@ static ssize_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, si php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : (POLLOUT|POLLPRI), has_timeout ? &left_time : NULL); } + } else if (err == SSL_ERROR_WANT_READ) { + sslsock->last_status = STREAM_CRYPTO_STATUS_WANT_READ; + } else if (err == SSL_ERROR_WANT_WRITE) { + sslsock->last_status = STREAM_CRYPTO_STATUS_WANT_WRITE; } } @@ -2201,6 +3114,34 @@ static int php_openssl_sockop_close(php_stream *stream, int close_handle) /* {{{ pefree(sslsock->reneg, php_stream_is_persistent(stream)); } + if (sslsock->session_callbacks && --sslsock->session_callbacks->refcount == 0) { + if (ZEND_FCC_INITIALIZED(sslsock->session_callbacks->new_cb)) { + zend_fcc_dtor(&sslsock->session_callbacks->new_cb); + } + if (ZEND_FCC_INITIALIZED(sslsock->session_callbacks->get_cb)) { + zend_fcc_dtor(&sslsock->session_callbacks->get_cb); + } + if (ZEND_FCC_INITIALIZED(sslsock->session_callbacks->remove_cb)) { + zend_fcc_dtor(&sslsock->session_callbacks->remove_cb); + } + pefree(sslsock->session_callbacks, php_stream_is_persistent(stream)); + } + + if (sslsock->psk_callbacks && --sslsock->psk_callbacks->refcount == 0) { + if (ZEND_FCC_INITIALIZED(sslsock->psk_callbacks->client_cb)) { + zend_fcc_dtor(&sslsock->psk_callbacks->client_cb); + } + + if (ZEND_FCC_INITIALIZED(sslsock->psk_callbacks->server_cb)) { + zend_fcc_dtor(&sslsock->psk_callbacks->server_cb); + } + pefree(sslsock->psk_callbacks, php_stream_is_persistent(stream)); + } + + if (sslsock->psk_identity_buf) { + efree(sslsock->psk_identity_buf); + } + pefree(sslsock, php_stream_is_persistent(stream)); return 0; @@ -2277,7 +3218,7 @@ static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_ clisockdata->method = sock->method; if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method, - NULL) < 0 || php_stream_xport_crypto_enable( + sock->ctx ? stream : NULL) < 0 || php_stream_xport_crypto_enable( xparam->outputs.client, 1) < 0) { php_error_docref(NULL, E_WARNING, "Failed to enable crypto"); @@ -2336,6 +3277,7 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val add_assoc_string(&tmp, "cipher_name", (char *) SSL_CIPHER_get_name(cipher)); add_assoc_long(&tmp, "cipher_bits", SSL_CIPHER_get_bits(cipher, NULL)); add_assoc_string(&tmp, "cipher_version", SSL_CIPHER_get_version(cipher)); + add_assoc_bool(&tmp, "session_reused", SSL_session_reused(sslsock->ssl_handle)); #ifdef HAVE_TLS_ALPN { @@ -2515,6 +3457,9 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val case STREAM_XPORT_CRYPTO_OP_ENABLE: cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam); return PHP_STREAM_OPTION_RETURN_OK; + case STREAM_XPORT_CRYPTO_OP_GET_STATUS: + cparam->outputs.returncode = sslsock->last_status; + return PHP_STREAM_OPTION_RETURN_OK; default: /* fall through */ break; @@ -2536,7 +3481,14 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val (xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS))) { - if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL) < 0 || + zval *val; + php_stream *session_stream = NULL; + + if (GET_VER_OPT("session_stream")) { + php_stream_from_zval_no_verify(session_stream, val); + } + + if (php_stream_xport_crypto_setup(stream, sslsock->method, session_stream) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { php_error_docref(NULL, E_WARNING, "Failed to enable crypto"); xparam->outputs.returncode = -1; @@ -2544,6 +3496,21 @@ static int php_openssl_sockop_set_option(php_stream *stream, int option, int val } return PHP_STREAM_OPTION_RETURN_OK; + case STREAM_XPORT_OP_LISTEN: + /* Do normal listen first */ + xparam->outputs.returncode = php_stream_socket_ops.set_option( + stream, option, value, ptrparam); + + if (xparam->outputs.returncode == 0 && sslsock->enable_on_connect) { + /* Check if we should create SSL_CTX early for session resumption */ + if (php_openssl_is_session_cache_enabled(stream, false)) { + if (FAILURE == php_openssl_create_server_ctx(stream, sslsock, sslsock->method)) { + xparam->outputs.returncode = -1; + } + } + } + return PHP_STREAM_OPTION_RETURN_OK; + case STREAM_XPORT_OP_ACCEPT: /* we need to copy the additional fields that the underlying tcp transport * doesn't know about */ diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index e9453b21329e..2ba732c4540e 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -675,7 +675,11 @@ PHP_FUNCTION(pcntl_exec) ZEND_PARSE_PARAMETERS_END(); if (args != NULL) { - // TODO Check array is a list? + if (!zend_array_is_list(Z_ARRVAL_P(args))) { + zend_argument_value_error(2, "must be a list array"); + RETURN_THROWS(); + } + /* Build argument list */ SEPARATE_ARRAY(args); const HashTable *args_ht = Z_ARRVAL_P(args); diff --git a/ext/pcntl/tests/pcntl_exec_list_args.phpt b/ext/pcntl/tests/pcntl_exec_list_args.phpt new file mode 100644 index 000000000000..5cd8c0fbe228 --- /dev/null +++ b/ext/pcntl/tests/pcntl_exec_list_args.phpt @@ -0,0 +1,14 @@ +--TEST-- +pcntl_exec(): Argument array must be a list +--EXTENSIONS-- +pcntl +--FILE-- + '-n']); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; +} +?> +--EXPECT-- +ValueError: pcntl_exec(): Argument #2 ($args) must be a list array diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index 161cdf852c1e..6a62d9717e7b 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -620,7 +620,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo /* Parse through the leading whitespace, and display a warning if we get to the end without encountering a delimiter. */ - while (isspace((int)*(unsigned char *)p)) p++; + while (isspace((unsigned char)*p)) p++; if (p >= end_p) { if (key != regex) { zend_string_release_ex(key, 0); @@ -633,7 +633,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo /* Get the delimiter and display a warning if it is alphanumeric or a backslash. */ delimiter = *p++; - if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\' || delimiter == '\0') { + if (isalnum((unsigned char)delimiter) || delimiter == '\\' || delimiter == '\0') { if (key != regex) { zend_string_release_ex(key, 0); } @@ -748,21 +748,23 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo } if (key != regex) { - tables = (uint8_t *)zend_hash_find_ptr(&char_tables, BG(ctype_string)); - if (!tables) { - zend_string *_k; + zv = zend_hash_str_lookup(&char_tables, ZSTR_VAL(BG(ctype_string)), ZSTR_LEN(BG(ctype_string))); + if (Z_ISNULL_P(zv)) { tables = pcre2_maketables(gctx); if (UNEXPECTED(!tables)) { + /* Remove the placeholder entry created by zend_hash_str_lookup(), + * set ptr to NULL first so the destructor (pefree) is safe. */ + ZVAL_PTR(zv, NULL); + zend_hash_str_del(&char_tables, ZSTR_VAL(BG(ctype_string)), ZSTR_LEN(BG(ctype_string))); php_error_docref(NULL,E_WARNING, "Failed to generate locale character tables"); pcre_handle_exec_error(PCRE2_ERROR_NOMEMORY); zend_string_release_ex(key, 0); efree(pattern); return NULL; } - _k = zend_string_init(ZSTR_VAL(BG(ctype_string)), ZSTR_LEN(BG(ctype_string)), 1); - GC_MAKE_PERSISTENT_LOCAL(_k); - zend_hash_add_ptr(&char_tables, _k, (void *)tables); - zend_string_release(_k); + ZVAL_PTR(zv, (void *)tables); + } else { + tables = Z_PTR_P(zv); } } pcre2_set_character_tables(cctx, tables); @@ -824,19 +826,8 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache_ex(zend_string *regex, bo new_entry.refcount = 0; new_entry.subpats_table = NULL; - rc = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &new_entry.capture_count); - if (rc < 0) { - if (key != regex) { - zend_string_release_ex(key, 0); - } - pcre2_code_free(new_entry.re); - php_error_docref(NULL, E_WARNING, "Internal pcre2_pattern_info() error %d", rc); - pcre_handle_exec_error(PCRE2_ERROR_INTERNAL); - return NULL; - } - - rc = pcre2_pattern_info(re, PCRE2_INFO_NAMECOUNT, &new_entry.name_count); - if (rc < 0) { + if ((rc = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &new_entry.capture_count)) < 0 || + (rc = pcre2_pattern_info(re, PCRE2_INFO_NAMECOUNT, &new_entry.name_count)) < 0) { if (key != regex) { zend_string_release_ex(key, 0); } @@ -1088,7 +1079,7 @@ static void populate_subpat_array( /* Add MARK, if available */ if (mark) { ZVAL_STRING(&val, (char *)mark); - zend_hash_str_add_new(subpats_ht, ZEND_STRL("MARK"), &val); + zend_hash_str_update(subpats_ht, ZEND_STRL("MARK"), &val); } } @@ -1568,6 +1559,7 @@ static zend_string *preg_do_repl_func(zend_fcall_info *fci, zend_fcall_info_cach fci->retval = &retval; fci->param_count = 1; fci->params = &arg; + fci->consumed_args = zend_fci_consumed_arg(0); zend_call_function(fci, fcc); zval_ptr_dtor(&arg); if (EXPECTED(Z_TYPE(retval) == IS_STRING)) { diff --git a/ext/pcre/tests/mark_named_collision.phpt b/ext/pcre/tests/mark_named_collision.phpt new file mode 100644 index 000000000000..b644c4d44023 --- /dev/null +++ b/ext/pcre/tests/mark_named_collision.phpt @@ -0,0 +1,15 @@ +--TEST-- +preg_match: (*MARK:name) directive does not collide with named group called MARK +--FILE-- +[abc])(*MARK:value)/', "abc", $m); +echo "count: ", count($m), "\n"; +echo "MARK: ", $m['MARK'], "\n"; +echo "json: ", json_encode($m), "\n"; +echo "keys: ", implode(',', array_keys($m)), "\n"; +?> +--EXPECT-- +count: 3 +MARK: value +json: {"0":"a","MARK":"value","1":"a"} +keys: 0,MARK,1 diff --git a/ext/pcre/tests/preg_replace_callback_matches_refcount.phpt b/ext/pcre/tests/preg_replace_callback_matches_refcount.phpt new file mode 100644 index 000000000000..0ae802654c9a --- /dev/null +++ b/ext/pcre/tests/preg_replace_callback_matches_refcount.phpt @@ -0,0 +1,37 @@ +--TEST-- +preg_replace_callback(): capture match array refcount stays low during callback +--FILE-- + static function ($matches) { + debug_zval_dump($matches); + return ''; +}], 'abc')); +?> +--EXPECTF-- +array(4) packed refcount(2){ + [0]=> + string(3) "abc"%s + [1]=> + string(1) "a" interned + [2]=> + string(1) "b" interned + [3]=> + string(1) "c" interned +} +string(0) "" +array(4) packed refcount(2){ + [0]=> + string(3) "abc"%s + [1]=> + string(1) "a" interned + [2]=> + string(1) "b" interned + [3]=> + string(1) "c" interned +} +string(0) "" diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c index 9a5f814304fc..5b881b0f56da 100644 --- a/ext/pdo/pdo.c +++ b/ext/pdo/pdo.c @@ -239,7 +239,7 @@ PDO_API int php_pdo_parse_data_source(const char *data_source, zend_ulong data_s } } - while (i < data_source_len && isspace(data_source[i])) { + while (i < data_source_len && isspace((unsigned char)data_source[i])) { i++; } diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 21002ce3a93d..6a47ec30c862 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -1529,7 +1529,7 @@ void pdo_dbh_init(int module_number) pdo_dbh_ce->default_object_handlers = &pdo_dbh_object_handlers; memcpy(&pdo_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - pdo_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std); + pdo_dbh_object_handlers.offset = offsetof(pdo_dbh_object_t, std); pdo_dbh_object_handlers.free_obj = pdo_dbh_free_storage; pdo_dbh_object_handlers.clone_obj = NULL; pdo_dbh_object_handlers.get_method = dbh_method_get; diff --git a/ext/pdo/pdo_sql_parser.re b/ext/pdo/pdo_sql_parser.re index d154a85287f2..d77425531cc4 100644 --- a/ext/pdo/pdo_sql_parser.re +++ b/ext/pdo/pdo_sql_parser.re @@ -115,7 +115,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string if (t == PDO_PARSER_BIND) { ptrdiff_t len = s.cur - s.tok; - if ((ZSTR_VAL(inquery) < (s.cur - len)) && isalnum(*(s.cur - len - 1))) { + if ((ZSTR_VAL(inquery) < (s.cur - len)) && isalnum((unsigned char)s.cur[-len - 1])) { continue; } query_type |= PDO_PLACEHOLDER_NAMED; diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index a8564cd8d39c..2b4e5a8f8239 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2420,7 +2420,7 @@ void pdo_stmt_init(void) pdo_dbstmt_ce->default_object_handlers = &pdo_dbstmt_object_handlers; memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std); + pdo_dbstmt_object_handlers.offset = offsetof(pdo_stmt_t, std); pdo_dbstmt_object_handlers.free_obj = pdo_dbstmt_free_storage; pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write; pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete; @@ -2434,7 +2434,7 @@ void pdo_stmt_init(void) pdo_row_ce->default_object_handlers = &pdo_row_object_handlers; memcpy(&pdo_row_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - pdo_row_object_handlers.offset = XtOffsetOf(pdo_row_t, std); + pdo_row_object_handlers.offset = offsetof(pdo_row_t, std); pdo_row_object_handlers.free_obj = pdo_row_free_storage; pdo_row_object_handlers.clone_obj = NULL; pdo_row_object_handlers.get_property_ptr_ptr = pdo_row_get_property_ptr_ptr; diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index 9dc18f75bfe1..479a1b5436c9 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -518,12 +518,10 @@ struct _pdo_dbh_object_t { }; static inline pdo_dbh_t *php_pdo_dbh_fetch_inner(zend_object *obj) { - return (pdo_dbh_t *)(((pdo_dbh_object_t *)((char*)(obj) - XtOffsetOf(pdo_dbh_object_t, std)))->inner); + return (pdo_dbh_t *)((ZEND_CONTAINER_OF(obj, pdo_dbh_object_t, std))->inner); } -static inline pdo_dbh_object_t *php_pdo_dbh_fetch_object(zend_object *obj) { - return (pdo_dbh_object_t *)((char*)(obj) - XtOffsetOf(pdo_dbh_object_t, std)); -} +#define php_pdo_dbh_fetch_object(obj) ZEND_CONTAINER_OF(obj, pdo_dbh_object_t, std) #define Z_PDO_DBH_P(zv) php_pdo_dbh_fetch_inner(Z_OBJ_P((zv))) #define Z_PDO_OBJECT_P(zv) php_pdo_dbh_fetch_object(Z_OBJ_P((zv))) @@ -637,11 +635,7 @@ struct _pdo_stmt_t { zend_object std; }; - - -static inline pdo_stmt_t *php_pdo_stmt_fetch_object(zend_object *obj) { - return (pdo_stmt_t *)((char*)(obj) - XtOffsetOf(pdo_stmt_t, std)); -} +#define php_pdo_stmt_fetch_object(obj) ZEND_CONTAINER_OF(obj, pdo_stmt_t, std) #define Z_PDO_STMT_P(zv) php_pdo_stmt_fetch_object(Z_OBJ_P((zv))) @@ -650,9 +644,7 @@ struct _pdo_row_t { zend_object std; }; -static inline pdo_row_t *php_pdo_row_fetch_object(zend_object *obj) { - return (pdo_row_t *)((char*)(obj) - XtOffsetOf(pdo_row_t, std)); -} +#define php_pdo_row_fetch_object(obj) ZEND_CONTAINER_OF(obj, pdo_row_t, std) struct _pdo_scanner_t { const char *ptr, *cur, *tok, *end; diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c index 42ba72b40ede..9f590c9071b6 100644 --- a/ext/pdo_dblib/dblib_driver.c +++ b/ext/pdo_dblib/dblib_driver.c @@ -420,6 +420,17 @@ static int dblib_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_valu return 1; } +static zend_result dblib_handle_check_liveness(pdo_dbh_t *dbh) +{ + pdo_dblib_db_handle *H = (pdo_dblib_db_handle *)dbh->driver_data; + + if (dbdead(H->link)) { + return FAILURE; + } + + return SUCCESS; +} + static const struct pdo_dbh_methods dblib_methods = { dblib_handle_closer, dblib_handle_preparer, @@ -432,7 +443,7 @@ static const struct pdo_dbh_methods dblib_methods = { dblib_handle_last_id, /* last insert id */ dblib_fetch_error, /* fetch error */ dblib_get_attribute, /* get attr */ - NULL, /* check liveness */ + dblib_handle_check_liveness, /* check_liveness */ NULL, /* get driver methods */ NULL, /* request shutdown */ NULL, /* in transaction, use PDO's internal tracking mechanism */ diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 8193132beaf4..c20969aac2b0 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -293,7 +293,7 @@ static FbTokenType php_firebird_get_token(const char** begin, const char* end) return ret; } -static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) +static int php_firebird_preprocess(const zend_string* sql, char* sql_out, size_t* sql_out_len, HashTable* named_params) { bool passAsIs = true, execBlock = false; zend_long pindex = -1; @@ -324,7 +324,7 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa if (l > 252) { return 0; } - strncpy(ident, i, l); + memcpy(ident, i, l); ident[l] = '\0'; if (!strcasecmp(ident, "EXECUTE")) { @@ -349,7 +349,7 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa if (l > 252) { return 0; } - strncpy(ident2, i2, l); + memcpy(ident2, i2, l); ident2[l] = '\0'; execBlock = !strcasecmp(ident2, "BLOCK"); passAsIs = false; @@ -365,11 +365,15 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa if (passAsIs) { - strcpy(sql_out, ZSTR_VAL(sql)); + memcpy(sql_out, ZSTR_VAL(sql), ZSTR_LEN(sql)); + sql_out[ZSTR_LEN(sql)] = '\0'; + *sql_out_len = ZSTR_LEN(sql); return 1; } - strncat(sql_out, start, p - start); + char *sql_out_p = sql_out; + memcpy(sql_out_p, start, p - start); + sql_out_p += p - start; while (p < end) { @@ -377,10 +381,12 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa tok = php_firebird_get_token(&p, end); switch (tok) { - case ttParamMark: - tok = php_firebird_get_token(&p, end); + case ttParamMark: { + const char* p_peek = p; + tok = php_firebird_get_token(&p_peek, end); if (tok == ttIdent /*|| tok == ttString*/) { + p = p_peek; ++pindex; l = p - start; /* check the length of the identifier */ @@ -389,7 +395,7 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa if (l > 253) { return 0; } - strncpy(pname, start, l); + memcpy(pname, start, l); pname[l] = '\0'; if (named_params) { @@ -398,7 +404,7 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa zend_hash_str_update(named_params, pname, l, &tmp); } - strcat(sql_out, "?"); + *sql_out_p++ = '?'; } else { @@ -408,10 +414,11 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa return 0; } ++pindex; - strncat(sql_out, start, p - start); + memcpy(sql_out_p, start, p - start); + sql_out_p += p - start; } break; - + } case ttIdent: if (execBlock) { @@ -423,11 +430,14 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa if (l > 252) { return 0; } - strncpy(ident, start, l); + memcpy(ident, start, l); ident[l] = '\0'; if (!strcasecmp(ident, "AS")) { - strncat(sql_out, start, end - start); + memcpy(sql_out_p, start, end - start); + sql_out_p += end - start; + *sql_out_p = '\0'; + *sql_out_len = sql_out_p - sql_out; return 1; } } @@ -438,7 +448,8 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa case ttComment: case ttString: case ttOther: - strncat(sql_out, start, p - start); + memcpy(sql_out_p, start, p - start); + sql_out_p += p - start; break; case ttBrokenComment: @@ -455,6 +466,8 @@ static int php_firebird_preprocess(const zend_string* sql, char* sql_out, HashTa return 0; } } + *sql_out_p = '\0'; + *sql_out_len = sql_out_p - sql_out; return 1; } @@ -789,7 +802,7 @@ static zend_long firebird_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /* static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) { size_t qcount = 0; - char const *co, *l, *r; + char const *co, *l; char *c; size_t quotedlen; zend_string *quoted_str; @@ -798,9 +811,15 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un return ZSTR_INIT_LITERAL("''", 0); } + const char * const end = ZSTR_VAL(unquoted) + ZSTR_LEN(unquoted); + /* Firebird only requires single quotes to be doubled if string lengths are used */ /* count the number of ' characters */ - for (co = ZSTR_VAL(unquoted); (co = strchr(co,'\'')); qcount++, co++); + for (co = ZSTR_VAL(unquoted); co < end; co++) { + if (*co == '\'') { + qcount++; + } + } if (UNEXPECTED(ZSTR_LEN(unquoted) + 2 > ZSTR_MAX_LEN - qcount)) { return NULL; @@ -812,15 +831,14 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un *c++ = '\''; /* foreach (chunk that ends in a quote) */ - for (l = ZSTR_VAL(unquoted); (r = strchr(l,'\'')); l = r+1) { - strncpy(c, l, r-l+1); - c += (r-l+1); - /* add the second quote */ - *c++ = '\''; + for (l = ZSTR_VAL(unquoted); l < end; l++) { + *c++ = *l; + if (*l == '\'') { + /* add the second quote */ + *c++ = '\''; + } } - /* copy the remainder */ - strncpy(c, l, quotedlen-(c-ZSTR_VAL(quoted_str))-1); ZSTR_VAL(quoted_str)[quotedlen-1] = '\''; ZSTR_VAL(quoted_str)[quotedlen] = '\0'; @@ -998,6 +1016,7 @@ static int php_firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sq { pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data; char *new_sql; + size_t new_sql_len; /* allocate the statement */ if (isc_dsql_allocate_statement(H->isc_status, &H->db, s)) { @@ -1009,14 +1028,14 @@ static int php_firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sq we need to replace :foo by ?, and store the name we just replaced */ new_sql = emalloc(ZSTR_LEN(sql)+1); new_sql[0] = '\0'; - if (!php_firebird_preprocess(sql, new_sql, named_params)) { + if (!php_firebird_preprocess(sql, new_sql, &new_sql_len, named_params)) { php_firebird_error_with_info(dbh, "07000", strlen("07000"), NULL, 0); efree(new_sql); return 0; } /* prepare the statement */ - if (isc_dsql_prepare(H->isc_status, &H->tr, s, 0, new_sql, H->sql_dialect, out_sqlda)) { + if (isc_dsql_prepare(H->isc_status, &H->tr, s, new_sql_len, new_sql, H->sql_dialect, out_sqlda)) { php_firebird_error(dbh); efree(new_sql); return 0; diff --git a/ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt b/ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt new file mode 100644 index 000000000000..3046fbc011b7 --- /dev/null +++ b/ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt @@ -0,0 +1,46 @@ +--TEST-- +GHSA-w476-322c-wpvm: SQL injection in pdo_firebird via NUL bytes in quoted strings +--EXTENSIONS-- +pdo_firebird +--SKIPIF-- + +--XLEAK-- +A bug in firebird causes a memory leak when calling `isc_attach_database()`. +See https://github.com/FirebirdSQL/firebird/issues/7849 +--FILE-- +exec('CREATE TABLE ghsa_w476_322c_wpvm (name VARCHAR(255))'); + +$param = $dbh->quote("\0"); +$param2 = $dbh->quote('or 1=1--'); +var_export($param); +echo("\n"); + +echo "prepare: "; +$stmt = $dbh->prepare("SELECT * FROM ghsa_w476_322c_wpvm WHERE name = {$param} AND name = {$param2}"); +$stmt->execute(); +echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)) . "\n"; + +echo "query: "; +$stmt = $dbh->query("SELECT * FROM ghsa_w476_322c_wpvm WHERE name = {$param} AND name = {$param2}"); +echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)) . "\n"; + +echo "exec: "; +$affectedRows = $dbh->exec("UPDATE ghsa_w476_322c_wpvm SET name = 'updated' WHERE name = {$param} AND name = {$param2}"); +echo $affectedRows . "\n"; +?> +--CLEAN-- +exec("DROP TABLE ghsa_w476_322c_wpvm"); +?> +--EXPECT-- +'\'' . "\0" . '\'' +prepare: [] +query: [] +exec: 0 diff --git a/ext/pdo_odbc/odbc_driver.c b/ext/pdo_odbc/odbc_driver.c index 4c627419d18e..3c8afe2d4215 100644 --- a/ext/pdo_odbc/odbc_driver.c +++ b/ext/pdo_odbc/odbc_driver.c @@ -548,9 +548,9 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ if (use_uid_arg) { should_quote_uid = !php_odbc_connstr_is_quoted(dbh->username) && php_odbc_connstr_should_quote(dbh->username); if (should_quote_uid) { - size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->username); - uid = emalloc(estimated_length); - php_odbc_connstr_quote(uid, dbh->username, estimated_length); + size_t quoted_length = php_odbc_connstr_get_quoted_length(dbh->username); + uid = emalloc(quoted_length); + php_odbc_connstr_quote(uid, dbh->username, quoted_length); } else { uid = dbh->username; } @@ -565,9 +565,9 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ if (use_pwd_arg) { should_quote_pwd = !php_odbc_connstr_is_quoted(dbh->password) && php_odbc_connstr_should_quote(dbh->password); if (should_quote_pwd) { - size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->password); - pwd = emalloc(estimated_length); - php_odbc_connstr_quote(pwd, dbh->password, estimated_length); + size_t quoted_length = php_odbc_connstr_get_quoted_length(dbh->password); + pwd = emalloc(quoted_length); + php_odbc_connstr_quote(pwd, dbh->password, quoted_length); } else { pwd = dbh->password; } diff --git a/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt b/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt index 9f0c777e83d8..fafa4f6bfa21 100644 --- a/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt +++ b/ext/pdo_sqlite/tests/pdo_sqlite_createfunction_with_flags.phpt @@ -4,7 +4,7 @@ PDO_sqlite: Testing sqliteCreateFunction() with flags pdo_sqlite --SKIPIF-- = 3.8.3'); ?> --FILE-- = 3.8.3'); +?> --FILE-- = 3.8.3'); +?> --FILE-- = 3.8.3'); ?> --FILE-- = 3.8.3'); +?> --FILE-- = 3.8.3'); +?> --FILE-- std); } -static inline pgsql_result_handle *pgsql_result_from_obj(zend_object *obj) { - return (pgsql_result_handle *)((char *)(obj) - XtOffsetOf(pgsql_result_handle, std)); -} +#define pgsql_result_from_obj(obj) ZEND_CONTAINER_OF(obj, pgsql_result_handle, std) #define Z_PGSQL_RESULT_P(zv) pgsql_result_from_obj(Z_OBJ_P(zv)) @@ -244,9 +240,7 @@ static void pgsql_result_free_obj(zend_object *obj) zend_object_std_dtor(&pg_result->std); } -static inline pgLofp *pgsql_lob_from_obj(zend_object *obj) { - return (pgLofp *)((char *)(obj) - XtOffsetOf(pgLofp, std)); -} +#define pgsql_lob_from_obj(obj) ZEND_CONTAINER_OF(obj, pgLofp, std) #define Z_PGSQL_LOB_P(zv) pgsql_lob_from_obj(Z_OBJ_P(zv)) @@ -574,7 +568,7 @@ PHP_MINIT_FUNCTION(pgsql) pgsql_link_ce->default_object_handlers = &pgsql_link_object_handlers; memcpy(&pgsql_link_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - pgsql_link_object_handlers.offset = XtOffsetOf(pgsql_link_handle, std); + pgsql_link_object_handlers.offset = offsetof(pgsql_link_handle, std); pgsql_link_object_handlers.free_obj = pgsql_link_free_obj; pgsql_link_object_handlers.get_constructor = pgsql_link_get_constructor; pgsql_link_object_handlers.clone_obj = NULL; @@ -585,7 +579,7 @@ PHP_MINIT_FUNCTION(pgsql) pgsql_result_ce->default_object_handlers = &pgsql_result_object_handlers; memcpy(&pgsql_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - pgsql_result_object_handlers.offset = XtOffsetOf(pgsql_result_handle, std); + pgsql_result_object_handlers.offset = offsetof(pgsql_result_handle, std); pgsql_result_object_handlers.free_obj = pgsql_result_free_obj; pgsql_result_object_handlers.get_constructor = pgsql_result_get_constructor; pgsql_result_object_handlers.clone_obj = NULL; @@ -596,7 +590,7 @@ PHP_MINIT_FUNCTION(pgsql) pgsql_lob_ce->default_object_handlers = &pgsql_lob_object_handlers; memcpy(&pgsql_lob_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - pgsql_lob_object_handlers.offset = XtOffsetOf(pgLofp, std); + pgsql_lob_object_handlers.offset = offsetof(pgLofp, std); pgsql_lob_object_handlers.free_obj = pgsql_lob_free_obj; pgsql_lob_object_handlers.get_constructor = pgsql_lob_get_constructor; pgsql_lob_object_handlers.clone_obj = NULL; @@ -1228,7 +1222,7 @@ PHP_FUNCTION(pg_query) /* The char pointer MUST refer to the char* of a zend_string struct */ static void php_pgsql_zend_string_release_from_char_pointer(char *ptr) { - zend_string_release((zend_string*) (ptr - XtOffsetOf(zend_string, val))); + zend_string_release((zend_string*) (ptr - offsetof(zend_string, val))); } static void _php_pgsql_free_params(char **params, uint32_t num_params) @@ -2129,26 +2123,17 @@ PHP_FUNCTION(pg_fetch_object) ce = zend_standard_class_def; } - if (!ce->constructor && ctor_params && zend_hash_num_elements(ctor_params) > 0) { - zend_argument_value_error(3, - "must be empty when the specified class (%s) does not have a constructor", - ZSTR_VAL(ce->name) - ); + if (UNEXPECTED(object_init_ex(return_value, ce) == FAILURE)) { RETURN_THROWS(); } zval dataset; if (UNEXPECTED(!php_pgsql_fetch_hash(&dataset, result, row, row_is_null, PGSQL_ASSOC))) { /* Either an exception is thrown, or we return false */ + zval_ptr_dtor(return_value); RETURN_FALSE; } - // TODO: Check CE is an instantiable class earlier? - zend_result obj_initialized = object_init_ex(return_value, ce); - if (UNEXPECTED(obj_initialized == FAILURE)) { - zval_ptr_dtor(&dataset); - RETURN_THROWS(); - } if (!ce->default_properties_count && !ce->__set) { Z_OBJ_P(return_value)->properties = Z_ARR(dataset); } else { @@ -2156,10 +2141,32 @@ PHP_FUNCTION(pg_fetch_object) zval_ptr_dtor(&dataset); } - // TODO: Need to grab constructor via object handler as this allows instantiating internal objects with overridden get_constructor - if (ce->constructor) { - zend_call_known_function(ce->constructor, Z_OBJ_P(return_value), Z_OBJCE_P(return_value), + zend_object *obj = Z_OBJ_P(return_value); + const zend_class_entry *old = EG(fake_scope); + EG(fake_scope) = ce; + zend_function *constructor = obj->handlers->get_constructor(obj); + EG(fake_scope) = old; + + if (UNEXPECTED(EG(exception))) { + /* visibility error or override refused - VM dtors return_value */ + return; + } + + if (UNEXPECTED(!constructor && ctor_params && zend_hash_num_elements(ctor_params) > 0)) { + zend_argument_value_error(4, + "must be empty when the specified class (%s) does not have a constructor", + ZSTR_VAL(ce->name) + ); + RETURN_THROWS(); + } + + if (constructor) { + zend_call_known_function(constructor, obj, ce, /* retval */ NULL, /* argc */ 0, /* params */ NULL, ctor_params); + if (EG(exception)) { + zend_object_store_ctor_failed(obj); + RETURN_THROWS(); + } } } /* }}} */ diff --git a/ext/pgsql/tests/pg_fetch_object_ctor_paths.phpt b/ext/pgsql/tests/pg_fetch_object_ctor_paths.phpt new file mode 100644 index 000000000000..bd3df73acba4 --- /dev/null +++ b/ext/pgsql/tests/pg_fetch_object_ctor_paths.phpt @@ -0,0 +1,69 @@ +--TEST-- +pg_fetch_object() constructor handling: ctor_params validation, throwing constructor, property visibility +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- +num}, str={$this->str}\n"; + } + public function __destruct() { + echo "SeesProps::__destruct called\n"; + } +} + +$table_name = "pg_fetch_object_ctor_paths"; +$db = pg_connect($conn_str); +pg_query($db, "CREATE TABLE {$table_name} (num int, str text)"); +pg_query($db, "INSERT INTO {$table_name} VALUES(1, 'hello')"); + +$sql = "SELECT * FROM {$table_name} WHERE num = 1"; + +// 1) ctor_params on a class with no constructor must throw ValueError +try { + pg_fetch_object(pg_query($db, $sql), null, 'NoCtor', [1, 2]); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} + +// 2) Constructor that throws: __destruct must NOT run on the partially constructed object +try { + pg_fetch_object(pg_query($db, $sql), null, 'ThrowingCtor'); +} catch (RuntimeException $e) { + echo "caught: ", $e->getMessage(), "\n"; +} + +// 3) Constructor sees row properties already merged onto $this +$obj = pg_fetch_object(pg_query($db, $sql), null, 'SeesProps'); +unset($obj); + +echo "Ok\n"; +?> +--CLEAN-- + +--EXPECT-- +pg_fetch_object(): Argument #4 ($constructor_args) must be empty when the specified class (NoCtor) does not have a constructor +caught: boom +ctor sees: num=1, str=hello +SeesProps::__destruct called +Ok diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 4bef14dfe6ae..b1c8c4747cc6 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -19,8 +19,6 @@ #include "phar_internal.h" #include "dirstream.h" -void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, bool is_dir); - static const php_stream_ops phar_dir_ops = { phar_dir_write, /* write */ phar_dir_read, /* read */ @@ -248,40 +246,46 @@ static php_stream *phar_make_dirstream(const char *dir, size_t dirlen, const Has php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */ { char *error; - phar_archive_data *phar; - php_url *resource = phar_parse_url(wrapper, path, mode, options); - if (!resource) { - php_stream_wrapper_log_error(wrapper, options, "phar url \"%s\" is unknown", path); + php_url *resource = phar_parse_url(wrapper, context, path, mode, options); + if (resource == NULL) { + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar url \"%s\" is unknown", path); return NULL; } /* we must have at the very least phar://alias.phar/ */ if (!resource->scheme || !resource->host || !resource->path) { if (resource->host && !resource->path) { - php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidPath, + "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", + path, ZSTR_VAL(resource->host)); php_url_free(resource); return NULL; } php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url \"%s\", must have at least phar://%s/", path, path); return NULL; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: not a phar url \"%s\"", path); return NULL; } phar_request_initialize(); - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { + const phar_archive_data *phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error); + if (!phar) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar file \"%s\" is unknown", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "phar file \"%s\" is unknown", ZSTR_VAL(resource->host)); } php_url_free(resource); return NULL; @@ -300,7 +304,7 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, const char *internal_file = ZSTR_VAL(resource->path) + 1; /* strip leading "/" */ size_t internal_file_len = ZSTR_LEN(resource->path) - 1; - phar_entry_info *entry = zend_hash_str_find_ptr(&phar->manifest, internal_file, internal_file_len); + const phar_entry_info *entry = zend_hash_str_find_ptr(&phar->manifest, internal_file, internal_file_len); php_stream *ret; if (NULL != entry && !entry->is_dir) { @@ -340,47 +344,51 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mode, int options, php_stream_context *context) /* {{{ */ { phar_entry_info entry; - phar_archive_data *phar = NULL; char *error; php_url *resource = NULL; /* pre-readonly check, we need to know if this is a data phar */ zend_string *arch = phar_split_fname(url_from, strlen(url_from), NULL, 2, 2); if (!arch) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", no phar archive specified", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "phar error: cannot create directory \"%s\", no phar archive specified", url_from); return 0; } - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL)) { - phar = NULL; - } + phar_archive_data *phar = phar_get_archive(ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL); zend_string_release_ex(arch, false); if (PHAR_G(readonly) && (!phar || !phar->is_data)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", write operations disabled", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, Readonly, + "phar error: cannot create directory \"%s\", write operations disabled", url_from); return 0; } - if ((resource = phar_parse_url(wrapper, url_from, "w", options)) == NULL) { + if ((resource = phar_parse_url(wrapper, context, url_from, "w", options)) == NULL) { return 0; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url \"%s\"", url_from); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url_from); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: not a phar stream url \"%s\"", url_from); return 0; } - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error); + phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error); + if (!phar) { + php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed, + "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", + ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -393,13 +401,17 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo zend_string_efree(e->filename); efree(e); } - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, AlreadyExists, + "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); php_url_free(resource); return 0; } if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -407,13 +419,17 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo if (phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1, 0, &error, true)) { /* entry exists as a file */ - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, AlreadyExists, + "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); php_url_free(resource); return 0; } if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -441,9 +457,10 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo entry.flags = PHAR_ENT_PERM_DEF_DIR; entry.old_flags = PHAR_ENT_PERM_DEF_DIR; - void *had_been_added = zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info)); - if (!had_been_added) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname)); + if (NULL == zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info))) { + php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed, + "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", + ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname)); zend_string_efree(entry.filename); return 0; } @@ -451,7 +468,9 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo phar_flush(phar, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname), error); + php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed, + "phar error: cannot create directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname), error); zend_hash_del(&phar->manifest, entry.filename); efree(error); return 0; @@ -467,28 +486,27 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo */ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context) /* {{{ */ { - phar_archive_data *phar = NULL; char *error; /* pre-readonly check, we need to know if this is a data phar */ zend_string *arch = phar_split_fname(url, strlen(url), NULL, 2, 2); if (!arch) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); return 0; } - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL)) { - phar = NULL; - } + phar_archive_data *phar = phar_get_archive(ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL); zend_string_release_ex(arch, false); if (PHAR_G(readonly) && (!phar || !phar->is_data)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot rmdir directory \"%s\", write operations disabled", url); + php_stream_wrapper_log_warn(wrapper, context, options, Readonly, + "phar error: cannot rmdir directory \"%s\", write operations disabled", url); return 0; } - php_url *resource = phar_parse_url(wrapper, url, "w", options); + php_url *resource = phar_parse_url(wrapper, context, url, "w", options); if (!resource) { return 0; } @@ -496,18 +514,23 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url \"%s\"", url); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: not a phar stream url \"%s\"", url); return 0; } - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error); + if (!phar) { + php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed, + "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); php_url_free(resource); return 0; @@ -518,10 +541,14 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options phar_entry_info *entry = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, path_len, 2, &error, true); if (!entry) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); + php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed, + "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", + ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host)); } php_url_free(resource); return 0; @@ -535,7 +562,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len) && IS_SLASH(ZSTR_VAL(str_key)[path_len]) ) { - php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty"); + php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed, + "phar error: Directory not empty"); if (entry->is_temp_dir) { zend_string_efree(entry->filename); efree(entry); @@ -551,7 +579,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len) && IS_SLASH(ZSTR_VAL(str_key)[path_len]) ) { - php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty"); + php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed, + "phar error: Directory not empty"); if (entry->is_temp_dir) { zend_string_efree(entry->filename); efree(entry); @@ -572,7 +601,9 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options phar_flush(phar, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry->filename), ZSTR_VAL(phar->fname), error); + php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed, + "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", + ZSTR_VAL(entry->filename), ZSTR_VAL(phar->fname), error); php_url_free(resource); efree(error); return 0; diff --git a/ext/phar/dirstream.h b/ext/phar/dirstream.h index 41899f88a992..179150229e3e 100644 --- a/ext/phar/dirstream.h +++ b/ext/phar/dirstream.h @@ -22,7 +22,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options #ifdef PHAR_DIRSTREAM #include "ext/standard/url.h" -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options); +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, const char *filename, const char *mode, int options); /* directory handlers */ static ssize_t phar_dir_write(php_stream *stream, const char *buf, size_t count); diff --git a/ext/phar/func_interceptors.c b/ext/phar/func_interceptors.c index 61bb9050550f..f6e69f20035c 100644 --- a/ext/phar/func_interceptors.c +++ b/ext/phar/func_interceptors.c @@ -98,15 +98,15 @@ static zend_string* phar_get_name_for_relative_paths(zend_string *filename, bool /* fopen within phar, if :// is not in the url, then prepend phar:/// */ /* retrieving a file defaults to within the current directory, so use this if possible */ - phar_archive_data *phar; - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL)) { + phar_archive_data *phar = phar_get_archive(ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL); + if (!phar) { zend_string_release_ex(arch, false); return NULL; } zend_string *name = NULL; if (using_include_path) { - name = phar_find_in_include_path(filename, NULL); + name = phar_find_in_include_path(filename); if (!name) { /* this file is not in the phar, use the original path */ zend_string_release_ex(arch, false); @@ -323,10 +323,6 @@ static void phar_fancy_stat(zend_stat_t *stat_sb, int type, zval *return_value) zval stat_dev, stat_ino, stat_mode, stat_nlink, stat_uid, stat_gid, stat_rdev, stat_size, stat_atime, stat_mtime, stat_ctime, stat_blksize, stat_blocks; int rmask=S_IROTH, wmask=S_IWOTH, xmask=S_IXOTH; /* access rights defaults to other */ - char *stat_sb_names[13] = { - "dev", "ino", "mode", "nlink", "uid", "gid", "rdev", - "size", "atime", "mtime", "ctime", "blksize", "blocks" - }; if (type >= FS_IS_W && type <= FS_IS_X) { if(stat_sb->st_uid==getuid()) { @@ -443,19 +439,19 @@ static void phar_fancy_stat(zend_stat_t *stat_sb, int type, zval *return_value) zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blocks); /* Store string indexes referencing the same zval*/ - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[0], strlen(stat_sb_names[0]), &stat_dev); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[1], strlen(stat_sb_names[1]), &stat_ino); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[2], strlen(stat_sb_names[2]), &stat_mode); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[3], strlen(stat_sb_names[3]), &stat_nlink); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[4], strlen(stat_sb_names[4]), &stat_uid); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[5], strlen(stat_sb_names[5]), &stat_gid); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[6], strlen(stat_sb_names[6]), &stat_rdev); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[7], strlen(stat_sb_names[7]), &stat_size); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[8], strlen(stat_sb_names[8]), &stat_atime); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[9], strlen(stat_sb_names[9]), &stat_mtime); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[10], strlen(stat_sb_names[10]), &stat_ctime); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[11], strlen(stat_sb_names[11]), &stat_blksize); - zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[12], strlen(stat_sb_names[12]), &stat_blocks); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("dev"), &stat_dev); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("ino"), &stat_ino); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("mode"), &stat_mode); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("nlink"), &stat_nlink); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("uid"), &stat_uid); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("gid"), &stat_gid); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("rdev"), &stat_rdev); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("size"), &stat_size); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("atime"), &stat_atime); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("mtime"), &stat_mtime); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("ctime"), &stat_ctime); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("blksize"), &stat_blksize); + zend_hash_str_update(Z_ARRVAL_P(return_value), ZEND_STRL("blocks"), &stat_blocks); return; } @@ -492,9 +488,9 @@ static void phar_file_stat(const char *filename, size_t filename_length, int typ zend_string *arch = phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), NULL, 2, 0); if (arch) { /* fopen within phar, if :// is not in the url, then prepend phar:/// */ - zend_result has_archive = phar_get_archive(&phar, ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL); + phar = phar_get_archive(ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL); zend_string_release_ex(arch, false); - if (FAILURE == has_archive) { + if (!phar) { goto skip_phar; } splitted:; @@ -727,13 +723,13 @@ PHP_FUNCTION(phar_is_file) /* {{{ */ zend_string *arch = phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), NULL, 2, 0); if (arch) { - phar_archive_data *phar; + ; /* fopen within phar, if :// is not in the url, then prepend phar:/// */ /* retrieving a file within the current directory, so use this if possible */ - zend_result has_archive = phar_get_archive(&phar, ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL); + phar_archive_data *phar = phar_get_archive(ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL); zend_string_release_ex(arch, false); - if (has_archive == SUCCESS) { + if (phar) { phar_entry_info *etemp; zend_string *entry = phar_fix_filepath(filename, filename_len, true); @@ -784,13 +780,11 @@ PHP_FUNCTION(phar_is_link) /* {{{ */ zend_string *arch = phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), NULL, 2, 0); if (arch) { - phar_archive_data *phar; - /* fopen within phar, if :// is not in the url, then prepend phar:/// */ /* retrieving a file within the current directory, so use this if possible */ - zend_result has_archive = phar_get_archive(&phar, ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL); + phar_archive_data *phar = phar_get_archive(ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL); zend_string_release_ex(arch, false); - if (has_archive == SUCCESS) { + if (phar) { phar_entry_info *etemp; zend_string *entry = phar_fix_filepath(filename, filename_len, true); diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 2ab799ab8c83..ee12c5b91feb 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -131,7 +131,7 @@ static void phar_split_cache_list(void) /* {{{ */ len = strlen(key); } - if (SUCCESS == phar_open_from_filename(key, len, NULL, 0, 0, &phar, NULL)) { + if (SUCCESS == phar_open_from_filename(key, len, NULL, 0, &phar, NULL)) { phar->phar_pos = i++; php_stream_close(phar->fp); phar->fp = NULL; @@ -484,9 +484,15 @@ ZEND_ATTRIBUTE_NONNULL void phar_entry_remove(phar_entry_data *idata, char **err /** * Open an already loaded phar */ -static zend_result phar_open_parsed_phar(char *fname, size_t fname_len, char *alias, size_t alias_len, bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */ -{ - phar_archive_data *phar; +static zend_result phar_open_parsed_phar( + char *fname, + size_t fname_len, + /* copyable & hash update */ zend_string *alias, + bool is_data, + uint32_t options, + phar_archive_data** pphar, + char **error +) { #ifdef PHP_WIN32 char *save_fname; ALLOCA_FLAG(fname_use_heap) @@ -504,12 +510,14 @@ static zend_result phar_open_parsed_phar(char *fname, size_t fname_len, char *al phar_unixify_path_separators(fname, fname_len); } #endif - zend_result archive_retrieved = phar_get_archive(&phar, fname, fname_len, alias, alias_len, error); + const char *alias_cstr = alias ? ZSTR_VAL(alias) : NULL; + size_t alias_len = alias ? ZSTR_LEN(alias) : 0; + phar_archive_data *phar = phar_get_archive(fname, fname_len, alias_cstr, alias_len, error); /* logic is as follows: - If no alias was passed in, then it can match either and be valid - If an explicit alias was requested, ensure the filename passed in matches the phar's filename. */ - bool process_phar = SUCCESS == archive_retrieved && (!alias || zend_string_equals_cstr(phar->fname, fname, fname_len)); + bool process_phar = phar && (!alias || zend_string_equals_cstr(phar->fname, fname, fname_len)); #ifdef PHP_WIN32 if (fname != save_fname) { free_alloca(fname, fname_use_heap); @@ -553,7 +561,6 @@ static zend_result phar_open_parsed_phar(char *fname, size_t fname_len, char *al return FAILURE; } } -/* }}}*/ /** * Attempt to serialize the data. @@ -721,7 +728,7 @@ void phar_parse_metadata_lazy(const char *buffer, phar_metadata_tracker *tracker * This is used by phar_open_from_filename to process the manifest, but can be called * directly. */ -static zend_result phar_parse_pharfile(php_stream *fp, char *fname, size_t fname_len, char *alias, size_t alias_len, zend_long halt_offset, phar_archive_data** pphar, uint32_t compression, char **error) /* {{{ */ +static zend_result phar_parse_pharfile(php_stream *fp, const char *fname, size_t fname_len, const char *alias, size_t alias_len, zend_long halt_offset, phar_archive_data** pphar, uint32_t compression, char **error) /* {{{ */ { char b32[4], *buffer, *endbuffer, *savebuf; phar_archive_data *mydata = NULL; @@ -1303,8 +1310,14 @@ static zend_result phar_parse_pharfile(php_stream *fp, char *fname, size_t fname /** * Create or open a phar for writing */ -ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_open_or_create_filename(zend_string *fname, char *alias, size_t alias_len, bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */ -{ +ZEND_ATTRIBUTE_NONNULL_ARGS(1, 5, 6) zend_result phar_open_or_create_filename( + zend_string *fname, + /* copyable & hash update */ zend_string *alias, + bool is_data, + uint32_t options, + phar_archive_data** pphar, + char **error +) { const char *ext_str, *z; char *my_error; size_t ext_len; @@ -1329,7 +1342,7 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_open_or_create_filename(ze return FAILURE; } check_file: - if (phar_open_parsed_phar(ZSTR_VAL(fname), ZSTR_LEN(fname), alias, alias_len, is_data, options, test, &my_error) == SUCCESS) { + if (phar_open_parsed_phar(ZSTR_VAL(fname), ZSTR_LEN(fname), alias, is_data, options, test, &my_error) == SUCCESS) { *pphar = *test; if ((*test)->is_data && !(*test)->is_tar && !(*test)->is_zip) { @@ -1355,22 +1368,27 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_open_or_create_filename(ze if (ext_len > 3 && (z = memchr(ext_str, 'z', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ip", 2)) { /* assume zip-based phar */ - return phar_open_or_create_zip(fname, alias, alias_len, is_data, options, pphar, error); + return phar_open_or_create_zip(fname, alias, is_data, options, pphar, error); } if (ext_len > 3 && (z = memchr(ext_str, 't', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ar", 2)) { /* assume tar-based phar */ - return phar_open_or_create_tar(fname, alias, alias_len, is_data, options, pphar, error); + return phar_open_or_create_tar(fname, alias, is_data, options, pphar, error); } - return phar_create_or_parse_filename(fname, alias, alias_len, is_data, options, pphar, error); + return phar_create_or_parse_filename(fname, alias, is_data, options, pphar, error); } -/* }}} */ -static zend_result phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char *alias, size_t alias_len, uint32_t options, phar_archive_data** pphar, char **error); +static zend_result phar_open_from_fp(php_stream* fp, const char *fname, size_t fname_len, /* copyable & hash update */ zend_string *alias, uint32_t options, phar_archive_data** pphar, char **error); -ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_create_or_parse_filename(zend_string *fname, char *alias, size_t alias_len, bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */ -{ +ZEND_ATTRIBUTE_NONNULL_ARGS(1, 5, 6) zend_result phar_create_or_parse_filename( + zend_string *fname, + /* copyable & hash update */ zend_string *alias, + bool is_data, + uint32_t options, + phar_archive_data** pphar, + char **error +) { php_stream *fp; zend_string *actual = NULL; zend_string *save_fname = fname; @@ -1388,7 +1406,7 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_create_or_parse_filename(z } if (fp) { - if (phar_open_from_fp(fp, ZSTR_VAL(fname), ZSTR_LEN(fname), alias, alias_len, options, pphar, error) == SUCCESS) { + if (phar_open_from_fp(fp, ZSTR_VAL(fname), ZSTR_LEN(fname), alias, options, pphar, error) == SUCCESS) { if ((*pphar)->is_data || !PHAR_G(readonly)) { (*pphar)->is_writeable = 1; } @@ -1459,15 +1477,14 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_create_or_parse_filename(z if (is_data) { alias = NULL; - alias_len = 0; mydata->is_data = 1; /* assume tar format, PharData can specify other */ mydata->is_tar = 1; } else { if (alias) { - const phar_archive_data *fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len); + const phar_archive_data *fd_ptr = zend_hash_find_ptr(&(PHAR_G(phar_alias_map)), alias); if (fd_ptr && SUCCESS != phar_free_alias(fd_ptr)) { - spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", ZSTR_VAL(mydata->fname), alias); + spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", ZSTR_VAL(mydata->fname), ZSTR_VAL(alias)); zend_hash_del(&(PHAR_G(phar_fname_map)), mydata->fname); @@ -1478,14 +1495,14 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_create_or_parse_filename(z } ZEND_ASSERT(!mydata->is_persistent); - mydata->alias = alias ? estrndup(alias, alias_len) : estrndup(ZSTR_VAL(mydata->fname), ZSTR_LEN(mydata->fname)); - mydata->alias_len = alias ? alias_len : ZSTR_LEN(mydata->fname); + mydata->alias = alias ? estrndup(ZSTR_VAL(alias), ZSTR_LEN(alias)) : estrndup(ZSTR_VAL(mydata->fname), ZSTR_LEN(mydata->fname)); + mydata->alias_len = alias ? ZSTR_LEN(alias) : ZSTR_LEN(mydata->fname); } - if (alias_len && alias) { - if (NULL == zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, mydata)) { + if (alias) { + if (NULL == zend_hash_add_ptr(&(PHAR_G(phar_alias_map)), alias, mydata)) { if (options & REPORT_ERRORS) { - spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", ZSTR_VAL(fname), alias); + spprintf(error, 0, "archive \"%s\" cannot be associated with alias \"%s\", already in use", ZSTR_VAL(fname), ZSTR_VAL(alias)); } zend_hash_del(&(PHAR_G(phar_fname_map)), mydata->fname); @@ -1499,7 +1516,6 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_create_or_parse_filename(z *pphar = mydata; return SUCCESS; } -/* }}}*/ /** * Return an already opened filename. @@ -1508,8 +1524,14 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_create_or_parse_filename(z * that the manifest is proper, then pass it to phar_parse_pharfile(). SUCCESS * or FAILURE is returned and pphar is set to a pointer to the phar's manifest */ -zend_result phar_open_from_filename(char *fname, size_t fname_len, char *alias, size_t alias_len, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */ -{ +zend_result phar_open_from_filename( + char *fname, + size_t fname_len, + /* copyable & hash update */ zend_string *alias, + uint32_t options, + phar_archive_data** pphar, + char **error +) { php_stream *fp; zend_string *actual; bool is_data = false; @@ -1522,7 +1544,7 @@ zend_result phar_open_from_filename(char *fname, size_t fname_len, char *alias, is_data = true; } - if (phar_open_parsed_phar(fname, fname_len, alias, alias_len, is_data, options, pphar, error) == SUCCESS) { + if (phar_open_parsed_phar(fname, fname_len, alias, is_data, options, pphar, error) == SUCCESS) { return SUCCESS; } else if (error && *error) { return FAILURE; @@ -1550,7 +1572,7 @@ zend_result phar_open_from_filename(char *fname, size_t fname_len, char *alias, fname_len = ZSTR_LEN(actual); } - zend_result ret = phar_open_from_fp(fp, fname, fname_len, alias, alias_len, options, pphar, error); + zend_result ret = phar_open_from_fp(fp, fname, fname_len, alias, options, pphar, error); if (actual) { zend_string_release_ex(actual, 0); @@ -1558,15 +1580,21 @@ zend_result phar_open_from_filename(char *fname, size_t fname_len, char *alias, return ret; } -/* }}}*/ /** * Scan an open fp for the required __HALT_COMPILER(); ?> token and verify * that the manifest is proper, then pass it to phar_parse_pharfile(). SUCCESS * or FAILURE is returned and pphar is set to a pointer to the phar's manifest */ -static zend_result phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char *alias, size_t alias_len, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */ -{ +static zend_result phar_open_from_fp( + php_stream* fp, + const char *fname, + size_t fname_len, + /* copyable & hash update */ zend_string *alias, + uint32_t options, + phar_archive_data** pphar, + char **error +) { static const char token[] = "__HALT_COMPILER();"; static const char zip_magic[] = "PK\x03\x04"; static const char gz_magic[] = "\x1f\x8b\x08"; @@ -1717,20 +1745,22 @@ static zend_result phar_open_from_fp(php_stream* fp, char *fname, size_t fname_l if (!memcmp(pos, zip_magic, 4)) { php_stream_seek(fp, 0, SEEK_END); - return phar_parse_zipfile(fp, fname, fname_len, alias, alias_len, pphar, error); + return phar_parse_zipfile(fp, fname, fname_len, alias, pphar, error); } if (got >= 512) { if (phar_is_tar(pos, fname)) { php_stream_rewind(fp); - return phar_parse_tarfile(fp, fname, fname_len, alias, alias_len, pphar, compression, error); + return phar_parse_tarfile(fp, fname, fname_len, alias, pphar, compression, error); } } } if (got > 0 && (pos = php_memnistr(buffer, token, tokenlen, buffer + got + sizeof(token))) != NULL) { halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */ - return phar_parse_pharfile(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error); + const char *alias_cstr = alias ? ZSTR_VAL(alias) : NULL; + size_t alias_len = alias ? ZSTR_LEN(alias) : 0; + return phar_parse_pharfile(fp, fname, fname_len, alias_cstr, alias_len, halt_offset, pphar, compression, error); } halt_offset += got; @@ -1739,7 +1769,6 @@ static zend_result phar_open_from_fp(php_stream* fp, char *fname, size_t fname_l MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (__HALT_COMPILER(); not found)") } -/* }}} */ /* * given the location of the file extension and the start of the file path, @@ -2073,7 +2102,7 @@ zend_string* phar_fix_filepath(const char *path, size_t path_length, bool use_cw size_t ptr_length; if (use_cwd && PHAR_G(cwd_len) && path_length > 2 && path[0] == '.' && path[1] == '/') { - new_path = zend_string_alloc(path_length + path_length + 1, false); + new_path = zend_string_alloc(path_length + PHAR_G(cwd_len) + 1, false); new_path_len = PHAR_G(cwd_len); memcpy(ZSTR_VAL(new_path), PHAR_G(cwd), PHAR_G(cwd_len)); } else { @@ -2253,7 +2282,7 @@ zend_string* phar_split_fname(const char *filename, size_t filename_len, zend_st * Invoked when a user calls Phar::mapPhar() from within an executing .phar * to set up its manifest directly */ -ZEND_ATTRIBUTE_NONNULL_ARGS(3) zend_result phar_open_executed_filename(char *alias, size_t alias_len, char **error) /* {{{ */ +ZEND_ATTRIBUTE_NONNULL_ARGS(2) zend_result phar_open_executed_filename(/* copyable & hash update */ zend_string *alias, char **error) /* {{{ */ { *error = NULL; @@ -2264,7 +2293,7 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(3) zend_result phar_open_executed_filename(char *ali return FAILURE; } - if (phar_open_parsed_phar(ZSTR_VAL(fname), ZSTR_LEN(fname), alias, alias_len, false, REPORT_ERRORS, NULL, NULL) == SUCCESS) { + if (phar_open_parsed_phar(ZSTR_VAL(fname), ZSTR_LEN(fname), alias, false, REPORT_ERRORS, NULL, NULL) == SUCCESS) { return SUCCESS; } @@ -2293,7 +2322,7 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(3) zend_result phar_open_executed_filename(char *ali fname = actual; } - zend_result ret = phar_open_from_fp(fp, ZSTR_VAL(fname), ZSTR_LEN(fname), alias, alias_len, REPORT_ERRORS, NULL, error); + zend_result ret = phar_open_from_fp(fp, ZSTR_VAL(fname), ZSTR_LEN(fname), alias, REPORT_ERRORS, NULL, error); if (actual) { zend_string_release_ex(actual, 0); @@ -2995,17 +3024,12 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_flush_ex(phar_archive_data *phar, zen switch(phar->sig_flags) { default: { - char *digest = NULL; - size_t digest_len; - char *signature_error = NULL; - if (FAILURE == phar_create_signature(phar, newfile, &digest, &digest_len, &signature_error)) { + zend_string *signature = phar_create_signature(phar, newfile, &signature_error); + if (!signature) { spprintf(error, 0, "phar error: unable to write signature: %s", signature_error); efree(signature_error); - if (digest) { - efree(digest); - } if (must_close_old_file) { php_stream_close(oldfile); } @@ -3013,14 +3037,14 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_flush_ex(phar_archive_data *phar, zen return EOF; } - php_stream_write(newfile, digest, digest_len); - efree(digest); + php_stream_write(newfile, ZSTR_VAL(signature), ZSTR_LEN(signature)); if (phar->sig_flags == PHAR_SIG_OPENSSL || phar->sig_flags == PHAR_SIG_OPENSSL_SHA256 || phar->sig_flags == PHAR_SIG_OPENSSL_SHA512) { - phar_set_32(sig_buf, digest_len); + phar_set_32(sig_buf, ZSTR_LEN(signature)); php_stream_write(newfile, sig_buf, 4); } + zend_string_release_ex(signature, false); break; } } @@ -3144,7 +3168,7 @@ zend_op_array *(*phar_orig_compile_file)(zend_file_handle *file_handle, int type static zend_string *phar_resolve_path(zend_string *filename) { - zend_string *ret = phar_find_in_include_path(filename, NULL); + zend_string *ret = phar_find_in_include_path(filename); if (!ret) { ret = phar_save_resolve_path(filename); } @@ -3162,7 +3186,7 @@ static zend_op_array *phar_compile_file(zend_file_handle *file_handle, int type) return phar_orig_compile_file(file_handle, type); } if (strstr(ZSTR_VAL(file_handle->filename), ".phar") && !strstr(ZSTR_VAL(file_handle->filename), "://")) { - if (SUCCESS == phar_open_from_filename(ZSTR_VAL(file_handle->filename), ZSTR_LEN(file_handle->filename), NULL, 0, 0, &phar, NULL)) { + if (SUCCESS == phar_open_from_filename(ZSTR_VAL(file_handle->filename), ZSTR_LEN(file_handle->filename), NULL, 0, &phar, NULL)) { if (phar->is_zip || phar->is_tar) { zend_file_handle f; diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 36fa2b543501..d75498f9c95a 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -406,14 +406,14 @@ void phar_object_init(void); void phar_destroy_phar_data(phar_archive_data *phar); ZEND_ATTRIBUTE_NONNULL zend_result phar_postprocess_file(phar_entry_data *idata, uint32_t crc32, char **error, int process_zip); -zend_result phar_open_from_filename(char *fname, size_t fname_len, char *alias, size_t alias_len, uint32_t options, phar_archive_data** pphar, char **error); -ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_open_or_create_filename(zend_string *fname, char *alias, size_t alias_len, bool is_data, uint32_t options, phar_archive_data** pphar, char **error); -ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_create_or_parse_filename(zend_string *fname, char *alias, size_t alias_len, bool is_data, uint32_t options, phar_archive_data** pphar, char **error); -ZEND_ATTRIBUTE_NONNULL_ARGS(3) zend_result phar_open_executed_filename(char *alias, size_t alias_len, char **error); +zend_result phar_open_from_filename(char *fname, size_t fname_len, /* copyable & hash update */ zend_string *alias, uint32_t options, phar_archive_data** pphar, char **error); +ZEND_ATTRIBUTE_NONNULL_ARGS(1, 5, 6) zend_result phar_open_or_create_filename(zend_string *fname, /* copyable & hash update */ zend_string *alias, bool is_data, uint32_t options, phar_archive_data** pphar, char **error); +ZEND_ATTRIBUTE_NONNULL_ARGS(1, 5, 6) zend_result phar_create_or_parse_filename(zend_string *fname, /* copyable & hash update */ zend_string *alias, bool is_data, uint32_t options, phar_archive_data** pphar, char **error); +ZEND_ATTRIBUTE_NONNULL_ARGS(2) zend_result phar_open_executed_filename(/* copyable & hash update */ zend_string *alias, char **error); zend_result phar_free_alias(const phar_archive_data *phar); -zend_result phar_get_archive(phar_archive_data **archive, const char *fname, size_t fname_len, const char *alias, size_t alias_len, char **error); -zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, char *sig, size_t sig_len, const char *fname, char **signature, size_t *signature_len, char **error); -ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signature, size_t *signature_length, char **error); +phar_archive_data* phar_get_archive(const char *fname, size_t fname_len, const char *alias, size_t alias_len, char **error); +zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, const char *sig, size_t sig_len, const char *fname, char **signature, size_t *signature_len, char **error); +ZEND_ATTRIBUTE_NONNULL zend_string* phar_create_signature(phar_archive_data *phar, php_stream *fp, char **error); /* utility functions */ zend_string *phar_create_default_stub(const zend_string *php_index_str, const zend_string *web_index_str, char **error); @@ -423,7 +423,7 @@ const char *phar_compress_filter(const phar_entry_info *entry, bool return_unkno /* void phar_remove_virtual_dirs(phar_archive_data *phar, char *filename, size_t filename_len); */ void phar_add_virtual_dirs(phar_archive_data *phar, const char *filename, size_t filename_len); zend_result phar_mount_entry(phar_archive_data *phar, const char *filename, size_t filename_len, char *path, size_t path_len); -zend_string *phar_find_in_include_path(zend_string *file, phar_archive_data **pphar); + zend_string *phar_find_in_include_path(const zend_string *file); zend_string* phar_fix_filepath(const char *path, size_t path_length, bool use_cwd); ZEND_ATTRIBUTE_NONNULL phar_entry_info * phar_open_jit(const phar_archive_data *phar, phar_entry_info *entry, char **error); void phar_parse_metadata_lazy(const char *buffer, phar_metadata_tracker *tracker, uint32_t zip_metadata_len, bool persistent); @@ -445,13 +445,13 @@ zend_result phar_copy_on_write(phar_archive_data **pphar); /* tar functions in tar.c */ bool phar_is_tar(const char *buf, const char *fname); -zend_result phar_parse_tarfile(php_stream* fp, const char *fname, size_t fname_len, const char *alias, size_t alias_len, phar_archive_data** pphar, uint32_t compression, char **error); -ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_open_or_create_tar(zend_string *fname, char *alias, size_t alias_len, bool is_data, uint32_t options, phar_archive_data** pphar, char **error); +zend_result phar_parse_tarfile(php_stream* fp, const char *fname, size_t fname_len, /* copyable & hash update */ zend_string *alias, phar_archive_data** pphar, uint32_t compression, char **error); +ZEND_ATTRIBUTE_NONNULL_ARGS(1, 5, 6) zend_result phar_open_or_create_tar(zend_string *fname, /* copyable & hash update */ zend_string *alias, bool is_data, uint32_t options, phar_archive_data** pphar, char **error); ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error); /* zip functions in zip.c */ -zend_result phar_parse_zipfile(php_stream *fp, const char *fname, size_t fname_len, const char *alias, size_t alias_len, phar_archive_data** pphar, char **error); -ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_open_or_create_zip(zend_string *fname, char *alias, size_t alias_len, bool is_data, uint32_t options, phar_archive_data** pphar, char **error); +zend_result phar_parse_zipfile(php_stream *fp, const char *fname, size_t fname_len, /* copyable & hash update */ zend_string *alias, phar_archive_data** pphar, char **error); +ZEND_ATTRIBUTE_NONNULL_ARGS(1, 5, 6) zend_result phar_open_or_create_zip(zend_string *fname, /* copyable & hash update */ zend_string *alias, bool is_data, uint32_t options, phar_archive_data** pphar, char **error); ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_zip_flush(phar_archive_data *archive, zend_string *user_stub, bool is_default_stub, char **error); #ifdef PHAR_MAIN diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index bc55c175f7ba..46db925cfd50 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -351,7 +351,8 @@ static void phar_do_404(phar_archive_data *phar, char *fname, size_t fname_len, which calls "blah.phar" file "path/to/file.php" with PATH_INFO "/extra/stuff" */ static void phar_postprocess_ru_web(const char *fname, size_t fname_len, char *entry, size_t *entry_len, char **ru, size_t *ru_len) /* {{{ */ { - char *e = entry + 1, *u1 = NULL, *u = NULL, *saveu = NULL; + char *e = entry + 1; + char *u1 = NULL, *u = NULL, *saveu = NULL; size_t e_len = *entry_len - 1, u_len = 0; phar_archive_data *pphar; @@ -548,8 +549,8 @@ PHP_METHOD(Phar, webPhar) zval *mimeoverride = NULL; zend_fcall_info rewrite_fci = {0}; zend_fcall_info_cache rewrite_fcc; - char *alias = NULL, *error, *index_php = NULL, *ru = NULL; - size_t alias_len = 0, free_pathinfo = 0; + char *error, *index_php = NULL, *ru = NULL; + size_t free_pathinfo = 0; zend_string *f404 = NULL; size_t ru_len = 0; char *fname, *path_info, *mime_type = NULL, *entry, *pt; @@ -561,14 +562,15 @@ PHP_METHOD(Phar, webPhar) phar_entry_info *info = NULL; size_t sapi_mod_name_len = strlen(sapi_module.name); phar_action_status status = PHAR_ACT_DO_EXIT; + zend_string *alias = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!s!S!af!", &alias, &alias_len, &index_php, &index_php_len, &f404, &mimeoverride, &rewrite_fci, &rewrite_fcc) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S!s!S!af!", &alias, &index_php, &index_php_len, &f404, &mimeoverride, &rewrite_fci, &rewrite_fcc) == FAILURE) { RETURN_THROWS(); } phar_request_initialize(); - if (phar_open_executed_filename(alias, alias_len, &error) != SUCCESS) { + if (phar_open_executed_filename(alias, &error) != SUCCESS) { if (error) { zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error); efree(error); @@ -752,8 +754,8 @@ PHP_METHOD(Phar, webPhar) entry_len = sizeof("/index.php")-1; } - if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, NULL) || - (info = phar_get_entry_info(phar, entry, entry_len, NULL, false)) == NULL) { + phar = phar_get_archive(fname, fname_len, NULL, 0, NULL); + if (!phar || (info = phar_get_entry_info(phar, entry, entry_len, NULL, false)) == NULL) { phar_do_404(phar, fname, fname_len, f404); } else { char *tmp = NULL, sa = '\0'; @@ -793,8 +795,8 @@ PHP_METHOD(Phar, webPhar) goto cleanup_skip_entry; } - if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, NULL) || - (info = phar_get_entry_info(phar, entry, entry_len, NULL, false)) == NULL) { + phar = phar_get_archive(fname, fname_len, NULL, 0, NULL); + if (!phar || (info = phar_get_entry_info(phar, entry, entry_len, NULL, false)) == NULL) { phar_do_404(phar, fname, fname_len, f404); goto cleanup; } @@ -946,17 +948,17 @@ PHP_METHOD(Phar, createDefaultStub) /* {{{ Reads the currently executed file (a phar) and registers its manifest */ PHP_METHOD(Phar, mapPhar) { - char *alias = NULL, *error; - size_t alias_len = 0; + zend_string *alias = NULL; + char *error; zend_long dataoffset = 0; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!l", &alias, &alias_len, &dataoffset) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S!l", &alias, &dataoffset) == FAILURE) { RETURN_THROWS(); } phar_request_initialize(); - RETVAL_BOOL(phar_open_executed_filename(alias, alias_len, &error) == SUCCESS); + RETVAL_BOOL(phar_open_executed_filename(alias, &error) == SUCCESS); if (error) { zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error); @@ -968,16 +970,16 @@ PHP_METHOD(Phar, mapPhar) PHP_METHOD(Phar, loadPhar) { zend_string *fname; - char *alias = NULL, *error; - size_t alias_len = 0; + zend_string *alias = NULL; + char *error; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|s!", &fname, &alias, &alias_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|S!", &fname, &alias) == FAILURE) { RETURN_THROWS(); } phar_request_initialize(); - RETVAL_BOOL(phar_open_from_filename(ZSTR_VAL(fname), ZSTR_LEN(fname), alias, alias_len, REPORT_ERRORS, NULL, &error) == SUCCESS); + RETVAL_BOOL(phar_open_from_filename(ZSTR_VAL(fname), ZSTR_LEN(fname), alias, REPORT_ERRORS, NULL, &error) == SUCCESS); if (error) { zend_throw_exception_ex(phar_ce_PharException, 0, "%s", error); @@ -1083,8 +1085,8 @@ static const spl_other_handler phar_spl_foreign_handler = { PHP_METHOD(Phar, __construct) { zend_string *fname; - char *alias = NULL, *error; - size_t alias_len = 0; + zend_string *alias = NULL; + char *error; bool is_data; zend_long flags = SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS; zend_long format = 0; @@ -1097,11 +1099,11 @@ PHP_METHOD(Phar, __construct) is_data = instanceof_function(Z_OBJCE_P(ZEND_THIS), phar_ce_data); if (is_data) { - if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|ls!l", &fname, &flags, &alias, &alias_len, &format) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|lS!l", &fname, &flags, &alias, &format) == FAILURE) { RETURN_THROWS(); } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|ls!", &fname, &flags, &alias, &alias_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "P|lS!", &fname, &flags, &alias) == FAILURE) { RETURN_THROWS(); } } @@ -1120,7 +1122,7 @@ PHP_METHOD(Phar, __construct) fname = arch; } - zend_result phar_status = phar_open_or_create_filename(fname, alias, alias_len, is_data, REPORT_ERRORS, &phar_data, &error); + zend_result phar_status = phar_open_or_create_filename(fname, alias, is_data, REPORT_ERRORS, &phar_data, &error); if (arch) { zend_string_release_ex(arch, false); @@ -1264,7 +1266,7 @@ PHP_METHOD(Phar, unlinkArchive) RETURN_THROWS(); } - if (FAILURE == phar_open_from_filename(ZSTR_VAL(fname), ZSTR_LEN(fname), NULL, 0, REPORT_ERRORS, &phar, &error)) { + if (FAILURE == phar_open_from_filename(ZSTR_VAL(fname), ZSTR_LEN(fname), NULL, REPORT_ERRORS, &phar, &error)) { if (error) { zend_throw_exception_ex(phar_ce_PharException, 0, "Unknown phar archive \"%s\": %s", ZSTR_VAL(fname), error); efree(error); @@ -3787,9 +3789,16 @@ PHP_METHOD(Phar, addEmptyDir) PHAR_ARCHIVE_OBJECT(); - if (zend_string_starts_with_literal(dir_name, ".phar")) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory"); - RETURN_THROWS(); + if ( + zend_string_starts_with_literal(dir_name, ".phar") + || zend_string_starts_with_literal(dir_name, "/.phar") + ) { + size_t prefix_len = (ZSTR_VAL(dir_name)[0] == '/') + sizeof(".phar") - 1; + char next_char = ZSTR_VAL(dir_name)[prefix_len]; + if (next_char == '/' || next_char == '\\' || next_char == '\0') { + zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Cannot create a directory in magic \".phar\" directory"); + RETURN_THROWS(); + } } phar_mkdir(&phar_obj->archive, dir_name); @@ -4414,7 +4423,7 @@ PHP_METHOD(PharFileInfo, __construct) RETURN_THROWS(); } - if (phar_open_from_filename(ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, REPORT_ERRORS, &phar_data, &error) == FAILURE) { + if (phar_open_from_filename(ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, REPORT_ERRORS, &phar_data, &error) == FAILURE) { zend_string_release_ex(arch, false); efree(entry); if (error) { diff --git a/ext/phar/stream.c b/ext/phar/stream.c index f49bc5e2deba..9474498e3892 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -55,7 +55,8 @@ const php_stream_wrapper php_stream_phar_wrapper = { /** * Open a phar file for streams API */ -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options) /* {{{ */ +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, + const char *filename, const char *mode, int options) { php_url *resource; char *error; @@ -65,7 +66,8 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (mode[0] == 'a') { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: open mode append not supported"); + php_stream_wrapper_log_warn(wrapper, context, options, ModeNotSupported, + "phar error: open mode append not supported"); } return NULL; } @@ -76,9 +78,13 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (!arch) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { if (arch_error && !entry) { - php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch_error); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidPath, + "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", + filename, arch_error); + arch = NULL; } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url or non-existent phar \"%s\"", filename); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url or non-existent phar \"%s\"", filename); } } return NULL; @@ -109,16 +115,17 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_log_warn(wrapper, context, options, Readonly, + "phar error: write operations disabled by the php.ini setting phar.readonly"); } php_url_free(resource); return NULL; } - if (phar_open_or_create_filename(resource->host, NULL, 0, 0, options, &phar, &error) == FAILURE) + if (phar_open_or_create_filename(resource->host, NULL, 0, options, &phar, &error) == FAILURE) { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, NULL, options, OpenFailed, "%s", error); } efree(error); } @@ -129,7 +136,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const if (error) { spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", ZSTR_VAL(resource->host)); if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, OpenFailed, "%s", error); } efree(error); } @@ -137,11 +144,11 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const return NULL; } } else { - if (phar_open_from_filename(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, options, NULL, &error) == FAILURE) + if (phar_open_from_filename(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, options, NULL, &error) == FAILURE) { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, "%s", error); } efree(error); } @@ -151,7 +158,6 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const } return resource; } -/* }}} */ /** * used for fopen('phar://...') and company @@ -166,7 +172,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_stream *fpf; zval *pzoption, *metadata; - php_url *resource = phar_parse_url(wrapper, path, mode, options); + php_url *resource = phar_parse_url(wrapper, context, path, mode, options); if (!resource) { return NULL; } @@ -174,13 +180,15 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url \"%s\"", path); return NULL; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", path); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: not a phar stream url \"%s\"", path); return NULL; } @@ -191,10 +199,11 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { if (NULL == (idata = phar_get_or_create_entry_data(resource->host, internal_file, strlen(internal_file), mode, 0, &error, true, time(NULL)))) { if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, CreateFailed, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, CreateFailed, + "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); php_url_free(resource); @@ -204,8 +213,9 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha php_url_free(resource); efree(internal_file); - if (context && Z_TYPE(context->options) != IS_UNDEF && (pzoption = zend_hash_str_find_ind(HASH_OF(&context->options), "phar", sizeof("phar")-1)) != NULL) { - pharcontext = HASH_OF(pzoption); + if (context && Z_TYPE(context->options) != IS_UNDEF && (pzoption = zend_hash_str_find(Z_ARR(context->options), "phar", sizeof("phar")-1)) != NULL) { + ZEND_ASSERT(Z_TYPE_P(pzoption) == IS_ARRAY); + pharcontext = Z_ARR_P(pzoption); if (idata->internal_file->uncompressed_filesize == 0 && idata->internal_file->compressed_filesize == 0 && (pzoption = zend_hash_str_find_ind(pharcontext, "compress", sizeof("compress")-1)) != NULL @@ -230,8 +240,10 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha } else { if (!*internal_file && (options & STREAM_OPEN_FOR_INCLUDE)) { /* retrieve the stub */ - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, NULL)) { - php_stream_wrapper_log_error(wrapper, options, "file %s is not a valid phar archive", ZSTR_VAL(resource->host)); + phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, NULL); + if (!phar) { + php_stream_wrapper_log_warn(wrapper, context, options, InvalidFormat, + "file %s is not a valid phar archive", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; @@ -251,7 +263,8 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if (stream == NULL) { stream = phar_open_archive_fp(phar); if (UNEXPECTED(!stream)) { - php_stream_wrapper_log_error(wrapper, options, "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, OpenFailed, + "phar error: could not reopen phar \"%s\"", ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); return NULL; @@ -288,10 +301,11 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha if ((FAILURE == phar_get_entry_data(&idata, resource->host, internal_file, strlen(internal_file), "r", 0, &error, false)) || !idata) { idata_error: if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, "%s", error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, ZSTR_VAL(resource->host)); } efree(internal_file); php_url_free(resource); @@ -309,7 +323,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* check length, crc32 */ if (!idata->internal_file->is_crc_checked && phar_postprocess_file(idata, idata->internal_file->crc32, &error, 2) != SUCCESS) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, ArchivingFailed, "%s", error); efree(error); phar_entry_delref(idata); efree(internal_file); @@ -363,7 +377,7 @@ static ssize_t phar_stream_read(php_stream *stream, char *buf, size_t count) /* { phar_entry_data *data = (phar_entry_data *)stream->abstract; ssize_t got; - phar_entry_info *entry; + const phar_entry_info *entry; if (data->internal_file->symlink) { entry = phar_get_link_source(data->internal_file); @@ -393,7 +407,7 @@ static ssize_t phar_stream_read(php_stream *stream, char *buf, size_t count) /* static int phar_stream_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) /* {{{ */ { phar_entry_data *data = (phar_entry_data *)stream->abstract; - phar_entry_info *entry; + const phar_entry_info *entry; int res; zend_ulong temp; @@ -440,7 +454,9 @@ static ssize_t phar_stream_write(php_stream *stream, const char *buf, size_t cou php_stream_seek(data->fp, data->position + data->zero, SEEK_SET); if (count != php_stream_write(data->fp, buf, count)) { - php_stream_wrapper_log_error(stream->wrapper, stream->flags, "phar error: Could not write %zu characters to \"%s\" in phar \"%s\"", count, ZSTR_VAL(data->internal_file->filename), ZSTR_VAL(data->phar->fname)); + php_stream_warn(stream, WriteFailed, + "phar error: Could not write %zu characters to \"%s\" in phar \"%s\"", + count, ZSTR_VAL(data->internal_file->filename), ZSTR_VAL(data->phar->fname)); return -1; } data->position = php_stream_tell(data->fp) - data->zero; @@ -461,13 +477,13 @@ static int phar_stream_flush(php_stream *stream) /* {{{ */ { char *error; int ret; - phar_entry_data *data = (phar_entry_data *) stream->abstract; + const phar_entry_data *data = stream->abstract; if (data->internal_file->is_modified) { data->internal_file->timestamp = time(0); ret = phar_flush(data->phar, &error); if (error) { - php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS, "%s", error); + php_stream_warn(stream, FlushFailed, "%s", error); efree(error); } return ret; @@ -481,7 +497,7 @@ static int phar_stream_flush(php_stream *stream) /* {{{ */ /** * stat an opened phar file handle stream, used by phar_stat() */ -void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, bool is_temp_dir) +static void phar_dostat(const phar_archive_data *phar, const phar_entry_info *data, php_stream_statbuf *ssb, bool is_temp_dir) { memset(ssb, 0, sizeof(php_stream_statbuf)); @@ -535,7 +551,7 @@ void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_stat */ static int phar_stream_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ */ { - phar_entry_data *data = (phar_entry_data *)stream->abstract; + const phar_entry_data *data = stream->abstract; /* If ssb is NULL then someone is misbehaving */ if (!ssb) { @@ -554,10 +570,9 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, const char *url, int f php_stream_statbuf *ssb, php_stream_context *context) /* {{{ */ { char *internal_file; - phar_archive_data *phar; size_t internal_file_len; - php_url *resource = phar_parse_url(wrapper, url, "r", flags|PHP_STREAM_URL_STAT_QUIET); + php_url *resource = phar_parse_url(wrapper, context, url, "r", flags|PHP_STREAM_URL_STAT_QUIET); if (!resource) { return FAILURE; } @@ -577,7 +592,8 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, const char *url, int f internal_file = ZSTR_VAL(resource->path) + 1; /* strip leading "/" */ /* find the phar in our trusty global hash indexed by alias (host of phar://blah.phar/file.whatever) */ - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, NULL)) { + phar_archive_data *phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, NULL); + if (!phar) { php_url_free(resource); return FAILURE; } @@ -658,33 +674,36 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int char *internal_file, *error; size_t internal_file_len; phar_entry_data *idata; - phar_archive_data *pphar; - php_url *resource = phar_parse_url(wrapper, url, "rb", options); + php_url *resource = phar_parse_url(wrapper, context, url, "rb", options); if (!resource) { - php_stream_wrapper_log_error(wrapper, options, "phar error: unlink failed"); + php_stream_wrapper_log_warn(wrapper, context, options, UnlinkFailed, + "phar error: unlink failed"); return 0; } /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource->scheme || !resource->host || !resource->path) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "phar error: invalid url \"%s\"", url); return 0; } if (!zend_string_equals_literal_ci(resource->scheme, "phar")) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: not a phar stream url \"%s\"", url); return 0; } phar_request_initialize(); - pphar = zend_hash_find_ptr(&(PHAR_G(phar_fname_map)), resource->host); + const phar_archive_data *pphar = zend_hash_find_ptr(&(PHAR_G(phar_fname_map)), resource->host); if (PHAR_G(readonly) && (!pphar || !pphar->is_data)) { php_url_free(resource); - php_stream_wrapper_log_error(wrapper, options, "phar error: write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_log_warn(wrapper, context, options, Readonly, + "phar error: write operations disabled by the php.ini setting phar.readonly"); return 0; } @@ -694,10 +713,12 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int if (FAILURE == phar_get_entry_data(&idata, resource->host, internal_file, internal_file_len, "r", 0, &error, true)) { /* constraints of fp refcount were not met */ if (error) { - php_stream_wrapper_log_error(wrapper, options, "unlink of \"%s\" failed: %s", url, error); + php_stream_wrapper_log_warn(wrapper, context, options, UnlinkFailed, + "unlink of \"%s\" failed: %s", url, error); efree(error); } else { - php_stream_wrapper_log_error(wrapper, options, "unlink of \"%s\" failed, file does not exist", url); + php_stream_wrapper_log_warn(wrapper, context, options, NotFound, + "unlink of \"%s\" failed, file does not exist", url); } efree(internal_file); php_url_free(resource); @@ -705,7 +726,9 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int } if (idata->internal_file->fp_refcount > 1) { /* more than just our fp resource is open for this file */ - php_stream_wrapper_log_error(wrapper, options, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, ZSTR_VAL(resource->host)); + php_stream_wrapper_log_warn(wrapper, context, options, UnlinkFailed, + "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", + internal_file, ZSTR_VAL(resource->host)); efree(internal_file); php_url_free(resource); phar_entry_delref(idata); @@ -715,7 +738,7 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int efree(internal_file); phar_entry_remove(idata, &error); if (error) { - php_stream_wrapper_log_error(wrapper, options, "%s", error); + php_stream_wrapper_log_warn(wrapper, context, options, UnlinkFailed, "%s", error); efree(error); } return 1; @@ -725,46 +748,51 @@ static int phar_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context) /* {{{ */ { char *error; - phar_archive_data *phar, *pfrom, *pto; bool is_dir = false; bool is_modified = false; error = NULL; - php_url *resource_from = phar_parse_url(wrapper, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET); + php_url *resource_from = phar_parse_url(wrapper, context, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET); if (!resource_from) { - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, InvalidUrl, + "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", + url_from, url_to, url_from); return 0; } - if (SUCCESS != phar_get_archive(&pfrom, ZSTR_VAL(resource_from->host), ZSTR_LEN(resource_from->host), NULL, 0, NULL)) { - pfrom = NULL; - } + + phar_archive_data *pfrom = phar_get_archive(ZSTR_VAL(resource_from->host), ZSTR_LEN(resource_from->host), NULL, 0, NULL); if (PHAR_G(readonly) && (!pfrom || !pfrom->is_data)) { php_url_free(resource_from); - php_error_docref(NULL, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_warn(wrapper, context, options, Readonly, + "phar error: Write operations disabled by the php.ini setting phar.readonly"); return 0; } - php_url *resource_to = phar_parse_url(wrapper, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET); + php_url *resource_to = phar_parse_url(wrapper, context, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET); if (!resource_to) { php_url_free(resource_from); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, InvalidUrl, + "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", + url_from, url_to, url_to); return 0; } - if (SUCCESS != phar_get_archive(&pto, ZSTR_VAL(resource_to->host), ZSTR_LEN(resource_to->host), NULL, 0, NULL)) { - pto = NULL; - } + + phar_archive_data *pto = phar_get_archive(ZSTR_VAL(resource_to->host), ZSTR_LEN(resource_to->host), NULL, 0, NULL); if (PHAR_G(readonly) && (!pto || !pto->is_data)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: Write operations disabled by the php.ini setting phar.readonly"); + php_stream_wrapper_warn(wrapper, context, options, Readonly, + "phar error: Write operations disabled by the php.ini setting phar.readonly"); return 0; } if (!zend_string_equals(resource_from->host, resource_to->host)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", + url_from, url_to); return 0; } @@ -772,35 +800,45 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (!resource_from->scheme || !resource_from->host || !resource_from->path) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, InvalidUrl, + "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", + url_from, url_to, url_from); return 0; } if (!resource_to->scheme || !resource_to->host || !resource_to->path) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, InvalidUrl, + "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", + url_from, url_to, url_to); return 0; } if (!zend_string_equals_literal_ci(resource_from->scheme, "phar")) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_from); + php_stream_wrapper_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", + url_from, url_to, url_from); return 0; } if (!zend_string_equals_literal_ci(resource_to->scheme, "phar")) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_to); + php_stream_wrapper_warn(wrapper, context, options, ProtocolUnsupported, + "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", + url_from, url_to, url_to); return 0; } - if (SUCCESS != phar_get_archive(&phar, ZSTR_VAL(resource_from->host), ZSTR_LEN(resource_from->host), NULL, 0, &error)) { + phar_archive_data *phar = phar_get_archive(ZSTR_VAL(resource_from->host), ZSTR_LEN(resource_from->host), NULL, 0, &error); + if (!phar) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); return 0; } @@ -808,7 +846,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", + url_from, url_to); return 0; } @@ -820,7 +860,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (entry->is_deleted) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, NotFound, + "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", + url_from, url_to); return 0; } /* transfer all data over to the new entry */ @@ -841,7 +883,8 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (FAILURE == phar_copy_entry_fp(source, entry, &error)) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); zend_hash_del(&phar->manifest, entry->filename); return 0; @@ -855,7 +898,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from /* file does not exist */ php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", url_from, url_to); + php_stream_wrapper_warn(wrapper, context, options, NotFound, + "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", + url_from, url_to); return 0; } @@ -934,7 +979,8 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from if (error) { php_url_free(resource_from); php_url_free(resource_to); - php_error_docref(NULL, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error); efree(error); return 0; } diff --git a/ext/phar/stream.h b/ext/phar/stream.h index cf40e3129c69..ebac093402b1 100644 --- a/ext/phar/stream.h +++ b/ext/phar/stream.h @@ -18,7 +18,7 @@ BEGIN_EXTERN_C() #include "ext/standard/url.h" -php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options); +php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, const char *filename, const char *mode, int options); ZEND_ATTRIBUTE_NONNULL void phar_entry_remove(phar_entry_data *idata, char **error); static php_stream* phar_wrapper_open_url(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC); diff --git a/ext/phar/tar.c b/ext/phar/tar.c index a7c02e352731..f85241ccc4e5 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -126,10 +126,16 @@ bool phar_is_tar(const char *buf, const char *fname) /* {{{ */ } /* }}} */ -ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_open_or_create_tar(zend_string *fname, char *alias, size_t alias_len, bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */ -{ +ZEND_ATTRIBUTE_NONNULL_ARGS(1, 5, 6) zend_result phar_open_or_create_tar( + zend_string *fname, + /* copyable & hash update */ zend_string *alias, + bool is_data, + uint32_t options, + phar_archive_data** pphar, + char **error +) { phar_archive_data *phar; - zend_result ret = phar_create_or_parse_filename(fname, alias, alias_len, is_data, options, &phar, error); + zend_result ret = phar_create_or_parse_filename(fname, alias, is_data, options, &phar, error); if (FAILURE == ret) { return FAILURE; @@ -153,7 +159,6 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_open_or_create_tar(zend_st spprintf(error, 4096, "phar tar error: \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a tar-based phar", ZSTR_VAL(fname)); return FAILURE; } -/* }}} */ static zend_result phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp) /* {{{ */ { @@ -197,8 +202,15 @@ static zend_result phar_tar_process_metadata(phar_entry_info *entry, php_stream } /* }}} */ -zend_result phar_parse_tarfile(php_stream* fp, const char *fname, size_t fname_len, const char *alias, size_t alias_len, phar_archive_data** pphar, uint32_t compression, char **error) /* {{{ */ -{ +zend_result phar_parse_tarfile( + php_stream* fp, + const char *fname, + size_t fname_len, + /* copyable & hash update */ zend_string *alias, + phar_archive_data** pphar, + uint32_t compression, + char **error +) { char buf[512], *actual_alias = NULL, *p; phar_entry_info entry = {0}; size_t pos = 0, read, totalsize; @@ -674,8 +686,8 @@ zend_result phar_parse_tarfile(php_stream* fp, const char *fname, size_t fname_l zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), actual_alias, myphar->alias_len, myphar); } else { - if (alias_len) { - phar_archive_data *fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len); + if (alias) { + phar_archive_data *fd_ptr = zend_hash_find_ptr(&(PHAR_G(phar_alias_map)), alias); if (fd_ptr) { if (SUCCESS != phar_free_alias(fd_ptr)) { if (error) { @@ -685,9 +697,9 @@ zend_result phar_parse_tarfile(php_stream* fp, const char *fname, size_t fname_l return FAILURE; } } - zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, myphar); - myphar->alias = pestrndup(alias, alias_len, myphar->is_persistent); - myphar->alias_len = alias_len; + zend_hash_add_ptr(&(PHAR_G(phar_alias_map)), alias, myphar); + myphar->alias = pestrndup(ZSTR_VAL(alias), ZSTR_LEN(alias), myphar->is_persistent); + myphar->alias_len = ZSTR_LEN(alias); } else { myphar->alias = pestrndup(ZSTR_VAL(myphar->fname), ZSTR_LEN(myphar->fname), myphar->is_persistent); myphar->alias_len = ZSTR_LEN(myphar->fname); @@ -702,7 +714,6 @@ zend_result phar_parse_tarfile(php_stream* fp, const char *fname, size_t fname_l return SUCCESS; } -/* }}} */ struct _phar_pass_tar_info { php_stream *old; @@ -802,7 +813,7 @@ static int phar_tar_writeheaders_int(phar_entry_info *entry, void *argument) /* /* write header */ entry->header_offset = php_stream_tell(fp->new); - if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) { + if (sizeof(header) != php_stream_write(fp->new, (const char *) &header, sizeof(header))) { if (fp->error) { spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for file \"%s\" could not be written", ZSTR_VAL(entry->phar->fname), ZSTR_VAL(entry->filename)); } @@ -906,7 +917,7 @@ ZEND_ATTRIBUTE_NONNULL static int phar_tar_setmetadata(const phar_metadata_track ZEND_ATTRIBUTE_NONNULL static int phar_tar_setupmetadata(zval *zv, void *argument) /* {{{ */ { - struct _phar_pass_tar_info *i = (struct _phar_pass_tar_info *)argument; + const struct _phar_pass_tar_info *i = (struct _phar_pass_tar_info *)argument; char **error = i->error; phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv), newentry = {0}; @@ -968,9 +979,8 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_tar_flush(phar_archive_data *phar, ze phar_entry_info entry = {0}; php_stream *oldfile, *newfile; bool must_close_old_file = false; - size_t signature_length; struct _phar_pass_tar_info pass; - char *buf, *signature, sigbuf[8]; + char *buf, sigbuf[8]; entry.flags = PHAR_ENT_PERM_DEF_FILE; entry.timestamp = time(NULL); @@ -1168,7 +1178,8 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_tar_flush(phar_archive_data *phar, ze /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */ if (!phar->is_data || phar->sig_flags) { char *signature_error = NULL; - if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, &signature_error)) { + zend_string *signature = phar_create_signature(phar, newfile, &signature_error); + if (!signature) { spprintf(error, 0, "phar error: unable to write signature to tar-based phar: %s", signature_error); efree(signature_error); @@ -1184,7 +1195,7 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_tar_flush(phar_archive_data *phar, ze if (entry.fp == NULL) { *error = estrdup("phar error: unable to create temporary file"); - efree(signature); + zend_string_release_ex(signature, false); if (must_close_old_file) { php_stream_close(oldfile); } @@ -1203,10 +1214,10 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_tar_flush(phar_archive_data *phar, ze # define PHAR_SET_32(destination, source) memcpy(destination, &source, 4) #endif PHAR_SET_32(sigbuf, phar->sig_flags); - PHAR_SET_32(sigbuf + 4, signature_length); + PHAR_SET_32(sigbuf + 4, ZSTR_LEN(signature)); - if (8 != php_stream_write(entry.fp, sigbuf, 8) || signature_length != php_stream_write(entry.fp, signature, signature_length)) { - efree(signature); + if (8 != php_stream_write(entry.fp, sigbuf, 8) || ZSTR_LEN(signature) != php_stream_write(entry.fp, ZSTR_VAL(signature), ZSTR_LEN(signature))) { + zend_string_release_ex(signature, false); spprintf(error, 0, "phar error: unable to write signature to tar-based phar %s", ZSTR_VAL(phar->fname)); if (must_close_old_file) { @@ -1218,11 +1229,11 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_tar_flush(phar_archive_data *phar, ze ALLOCA_FLAG(use_heap); ZSTR_ALLOCA_INIT(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1, use_heap); - efree(signature); - entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8; + entry.uncompressed_filesize = entry.compressed_filesize = ZSTR_LEN(signature) + 8; /* throw out return value and write the signature */ phar_tar_writeheaders_int(&entry, &pass); ZSTR_ALLOCA_FREE(entry.filename, use_heap); + zend_string_release_ex(signature, false); if (*error) { if (must_close_old_file) { diff --git a/ext/phar/tests/mkdir.phpt b/ext/phar/tests/mkdir.phpt index 1ffdc7fe252d..2c1586b0de5c 100644 --- a/ext/phar/tests/mkdir.phpt +++ b/ext/phar/tests/mkdir.phpt @@ -24,6 +24,13 @@ $a->addEmptyDir('.phar'); } catch (Exception $e) { echo $e->getMessage(),"\n"; } +try { +$a->addEmptyDir('/.phar'); +} catch (Exception $e) { +echo $e->getMessage(),"\n"; +} +$a->addEmptyDir('/.pharx'); +var_dump(is_dir($pname . '/.pharx')); ?> --CLEAN-- getContent()); +?> +--CLEAN-- + +--EXPECT-- +string(0) "" diff --git a/ext/phar/util.c b/ext/phar/util.c index 491a07c4dd21..9906728a00f7 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -34,7 +34,8 @@ #include #include #else -static zend_result phar_call_openssl_signverify(bool is_sign, php_stream *fp, zend_off_t end, const char *key, size_t key_len, char **signature, size_t *signature_len, uint32_t sig_type); +ZEND_ATTRIBUTE_NONNULL static bool phar_call_openssl_verify(php_stream *fp, zend_off_t end, zend_string *public_key, const char *signature, size_t signature_len, uint32_t sig_type); +static zend_result phar_call_openssl_sign(php_stream *fp, zend_off_t end, const char *key, size_t key_len, char **signature, size_t *signature_len, uint32_t sig_type); #endif /* for links to relative location, prepend cwd of the entry */ @@ -65,23 +66,30 @@ static zend_string *phar_get_link_location(phar_entry_info *entry) /* {{{ */ phar_entry_info *phar_get_link_source(phar_entry_info *entry) /* {{{ */ { phar_entry_info *link_entry; + uint32_t depth = 0, max_depth; if (!entry->symlink) { return entry; } - link_entry = zend_hash_find_ptr(&(entry->phar->manifest), entry->symlink); - if (link_entry) { - return phar_get_link_source(link_entry); - } + max_depth = zend_hash_num_elements(&(entry->phar->manifest)); + + while (entry->symlink) { + if (UNEXPECTED(++depth > max_depth)) { + return NULL; + } + zend_string *link = phar_get_link_location(entry); - zend_string *link = phar_get_link_location(entry); - link_entry = zend_hash_find_ptr(&(entry->phar->manifest), link); - zend_string_release(link); - if (link_entry) { - return phar_get_link_source(link_entry); + if (NULL != (link_entry = zend_hash_find_ptr(&(entry->phar->manifest), entry->symlink)) || + NULL != (link_entry = zend_hash_find_ptr(&(entry->phar->manifest), link))) { + zend_string_release(link); + entry = link_entry; + } else { + zend_string_release(link); + return NULL; + } } - return NULL; + return entry; } /* }}} */ @@ -263,19 +271,13 @@ zend_result phar_mount_entry(phar_archive_data *phar, const char *filename, size } /* }}} */ -zend_string *phar_find_in_include_path(zend_string *filename, phar_archive_data **pphar) /* {{{ */ +zend_string *phar_find_in_include_path(const zend_string *filename) /* {{{ */ { zend_string *ret; char *path; zend_string *arch; phar_archive_data *phar; - if (pphar) { - *pphar = NULL; - } else { - pphar = &phar; - } - if (!zend_is_executing() || !PHAR_G(cwd)) { return NULL; } @@ -308,15 +310,12 @@ zend_string *phar_find_in_include_path(zend_string *filename, phar_archive_data } if (*ZSTR_VAL(filename) == '.') { - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL)) { + phar = phar_get_archive(ZSTR_VAL(arch), ZSTR_LEN(arch), NULL, 0, NULL); + if (!phar) { zend_string_release_ex(arch, false); return NULL; } -splitted: - if (pphar) { - *pphar = phar; - } - +splitted:; zend_string *test = phar_fix_filepath(ZSTR_VAL(filename), ZSTR_LEN(filename), true); if (ZSTR_VAL(test)[0] == '/') { if (zend_hash_str_exists(&(phar->manifest), ZSTR_VAL(test) + 1, ZSTR_LEN(test) - 1)) { @@ -345,22 +344,6 @@ zend_string *phar_find_in_include_path(zend_string *filename, phar_archive_data ret = php_resolve_path(ZSTR_VAL(filename), ZSTR_LEN(filename), path); efree(path); - if (ret && zend_string_starts_with_literal_ci(ret, "phar://")) { - /* found phar:// */ - arch = phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), NULL, 1, 0); - if (!arch) { - return ret; - } - - *pphar = zend_hash_find_ptr(&(PHAR_G(phar_fname_map)), arch); - - if (!*pphar && PHAR_G(manifest_cached)) { - *pphar = zend_hash_find_ptr(&cached_phars, arch); - } - - zend_string_release_ex(arch, false); - } - return ret; } /* }}} */ @@ -468,7 +451,6 @@ ZEND_ATTRIBUTE_NONNULL static zend_result phar_separate_entry_fp(phar_entry_info */ ZEND_ATTRIBUTE_NONNULL zend_result phar_get_entry_data(phar_entry_data **ret, const zend_string *fname, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security) /* {{{ */ { - phar_archive_data *phar; phar_entry_info *entry; bool for_write = mode[0] != 'r' || mode[1] == '+'; bool for_append = mode[0] == 'a'; @@ -478,7 +460,8 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_get_entry_data(phar_entry_data **ret, co *ret = NULL; *error = NULL; - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(fname), ZSTR_LEN(fname), NULL, 0, error)) { + phar_archive_data *phar = phar_get_archive(ZSTR_VAL(fname), ZSTR_LEN(fname), NULL, 0, error); + if (!phar) { return FAILURE; } @@ -613,7 +596,6 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_get_entry_data(phar_entry_data **ret, co */ ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(zend_string *fname, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security, uint32_t timestamp) /* {{{ */ { - phar_archive_data *phar; phar_entry_info etemp; phar_entry_data *ret; const char *pcr_error; @@ -625,7 +607,8 @@ ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(zend_strin is_dir = (path_len && path[path_len - 1] == '/') ? 1 : 0; - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(fname), ZSTR_LEN(fname), NULL, 0, error)) { + phar_archive_data *phar = phar_get_archive(ZSTR_VAL(fname), ZSTR_LEN(fname), NULL, 0, error); + if (!phar) { return NULL; } @@ -960,9 +943,9 @@ zend_result phar_free_alias(const phar_archive_data *phar) /* {{{ */ * Looks up a phar archive in the filename map, connecting it to the alias * (if any) or returns null */ -zend_result phar_get_archive(phar_archive_data **archive, const char *fname, size_t fname_len, const char *alias, size_t alias_len, char **error) /* {{{ */ +phar_archive_data* phar_get_archive(const char *fname, size_t fname_len, const char *alias, size_t alias_len, char **error) /* {{{ */ { - phar_archive_data *fd_ptr; + phar_archive_data *archive; phar_request_initialize(); @@ -970,142 +953,132 @@ zend_result phar_get_archive(phar_archive_data **archive, const char *fname, siz *error = NULL; } - *archive = NULL; - if (PHAR_G(last_phar) && zend_string_equals_cstr(PHAR_G(last_phar_name), fname, fname_len)) { - *archive = PHAR_G(last_phar); + archive = PHAR_G(last_phar); if (alias && alias_len) { if (!PHAR_G(last_phar)->is_temporary_alias && (alias_len != PHAR_G(last_phar)->alias_len || memcmp(PHAR_G(last_phar)->alias, alias, alias_len))) { if (error) { spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, ZSTR_VAL(PHAR_G(last_phar)->fname), fname); } - *archive = NULL; - return FAILURE; + return NULL; } if (PHAR_G(last_phar)->alias_len && zend_hash_str_exists(&(PHAR_G(phar_alias_map)), PHAR_G(last_phar)->alias, PHAR_G(last_phar)->alias_len)) { zend_hash_str_del(&(PHAR_G(phar_alias_map)), PHAR_G(last_phar)->alias, PHAR_G(last_phar)->alias_len); } - zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, *archive); + zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, archive); PHAR_G(last_alias) = alias; PHAR_G(last_alias_len) = alias_len; } - return SUCCESS; + return archive; } if (alias && alias_len) { /* If the alias stored in the last_phar cache matches, just use it directly */ if (PHAR_G(last_phar) && alias_len == PHAR_G(last_alias_len) && !memcmp(alias, PHAR_G(last_alias), alias_len)) { - fd_ptr = PHAR_G(last_phar); + archive = PHAR_G(last_phar); } else { - fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len); + archive = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len); } /* If we didn't find the alias, check in the cached manifest to see if we can find it */ - if (!fd_ptr && PHAR_G(manifest_cached)) { - fd_ptr = zend_hash_str_find_ptr(&cached_alias, alias, alias_len); + if (!archive && PHAR_G(manifest_cached)) { + archive = zend_hash_str_find_ptr(&cached_alias, alias, alias_len); } - if (fd_ptr) { - if (!zend_string_equals_cstr(fd_ptr->fname, fname, fname_len)) { + if (archive) { + if (!zend_string_equals_cstr(archive->fname, fname, fname_len)) { if (error) { - spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, ZSTR_VAL(fd_ptr->fname), fname); + spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, ZSTR_VAL(archive->fname), fname); } - if (SUCCESS == phar_free_alias(fd_ptr)) { + if (SUCCESS == phar_free_alias(archive)) { if (error) { efree(*error); *error = NULL; } } - return FAILURE; + return NULL; } - *archive = fd_ptr; - PHAR_G(last_phar) = fd_ptr; - PHAR_G(last_phar_name) = fd_ptr->fname; + PHAR_G(last_phar) = archive; + PHAR_G(last_phar_name) = archive->fname; PHAR_G(last_alias) = alias; PHAR_G(last_alias_len) = alias_len; - return SUCCESS; + return archive; } } if (fname && fname_len) { - fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), fname, fname_len); - if (fd_ptr) { - *archive = fd_ptr; - + archive = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), fname, fname_len); + if (archive) { if (alias && alias_len) { - if (!fd_ptr->is_temporary_alias && (alias_len != fd_ptr->alias_len || memcmp(fd_ptr->alias, alias, alias_len))) { + if (!archive->is_temporary_alias && (alias_len != archive->alias_len || memcmp(archive->alias, alias, alias_len))) { if (error) { - spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, ZSTR_VAL(fd_ptr->fname), fname); + spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, ZSTR_VAL(archive->fname), fname); } - return FAILURE; + return NULL; } - if (fd_ptr->alias_len && zend_hash_str_exists(&(PHAR_G(phar_alias_map)), fd_ptr->alias, fd_ptr->alias_len)) { - zend_hash_str_del(&(PHAR_G(phar_alias_map)), fd_ptr->alias, fd_ptr->alias_len); + if (archive->alias_len && zend_hash_str_exists(&(PHAR_G(phar_alias_map)), archive->alias, archive->alias_len)) { + zend_hash_str_del(&(PHAR_G(phar_alias_map)), archive->alias, archive->alias_len); } - zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, fd_ptr); + zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, archive); } - PHAR_G(last_phar) = fd_ptr; - PHAR_G(last_phar_name) = fd_ptr->fname; - PHAR_G(last_alias) = fd_ptr->alias; - PHAR_G(last_alias_len) = fd_ptr->alias_len; + PHAR_G(last_phar) = archive; + PHAR_G(last_phar_name) = archive->fname; + PHAR_G(last_alias) = archive->alias; + PHAR_G(last_alias_len) = archive->alias_len; - return SUCCESS; + return archive; } - if (PHAR_G(manifest_cached) && NULL != (fd_ptr = zend_hash_str_find_ptr(&cached_phars, fname, fname_len))) { - *archive = fd_ptr; - + if (PHAR_G(manifest_cached) && NULL != (archive = zend_hash_str_find_ptr(&cached_phars, fname, fname_len))) { /* this could be problematic - alias should never be different from manifest alias for cached phars */ - if (!fd_ptr->is_temporary_alias && alias && alias_len) { - if (alias_len != fd_ptr->alias_len || memcmp(fd_ptr->alias, alias, alias_len)) { + if (!archive->is_temporary_alias && alias && alias_len) { + if (alias_len != archive->alias_len || memcmp(archive->alias, alias, alias_len)) { if (error) { - spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, ZSTR_VAL(fd_ptr->fname), fname); + spprintf(error, 0, "alias \"%s\" is already used for archive \"%s\" cannot be overloaded with \"%s\"", alias, ZSTR_VAL(archive->fname), fname); } - return FAILURE; + return NULL; } } - PHAR_G(last_phar) = fd_ptr; - PHAR_G(last_phar_name) = fd_ptr->fname; - PHAR_G(last_alias) = fd_ptr->alias; - PHAR_G(last_alias_len) = fd_ptr->alias_len; + PHAR_G(last_phar) = archive; + PHAR_G(last_phar_name) = archive->fname; + PHAR_G(last_alias) = archive->alias; + PHAR_G(last_alias_len) = archive->alias_len; - return SUCCESS; + return archive; } - fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), fname, fname_len); + archive = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), fname, fname_len); /* If we didn't find the fname in the alias map, check in the cached manifest to see if we can find it */ - if (!fd_ptr && PHAR_G(manifest_cached)) { - fd_ptr = zend_hash_str_find_ptr(&cached_alias, fname, fname_len); + if (!archive && PHAR_G(manifest_cached)) { + archive = zend_hash_str_find_ptr(&cached_alias, fname, fname_len); } - if (fd_ptr) { - *archive = fd_ptr; - - PHAR_G(last_phar) = fd_ptr; - PHAR_G(last_phar_name) = fd_ptr->fname; - PHAR_G(last_alias) = fd_ptr->alias; - PHAR_G(last_alias_len) = fd_ptr->alias_len; + if (archive) { + PHAR_G(last_phar) = archive; + PHAR_G(last_phar_name) = archive->fname; + PHAR_G(last_alias) = archive->alias; + PHAR_G(last_alias_len) = archive->alias_len; - return SUCCESS; + return archive; } /* not found, try converting \ to / */ char *my_realpath = expand_filepath(fname, NULL); if (UNEXPECTED(!my_realpath)) { - return FAILURE; + return NULL; } size_t my_realpath_len = strlen(my_realpath); @@ -1113,31 +1086,29 @@ zend_result phar_get_archive(phar_archive_data **archive, const char *fname, siz phar_unixify_path_separators(my_realpath, my_realpath_len); #endif - fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), my_realpath, my_realpath_len); + archive = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), my_realpath, my_realpath_len); /* If we didn't find the path in the fname map, check in the cached manifest to see if we can find it */ - if (!fd_ptr && PHAR_G(manifest_cached)) { - fd_ptr = zend_hash_str_find_ptr(&cached_phars, my_realpath, my_realpath_len); + if (!archive && PHAR_G(manifest_cached)) { + archive = zend_hash_str_find_ptr(&cached_phars, my_realpath, my_realpath_len); } efree(my_realpath); - if (fd_ptr) { - *archive = fd_ptr; - + if (archive) { if (alias && alias_len) { - zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, fd_ptr); + zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, archive); } - PHAR_G(last_phar) = fd_ptr; - PHAR_G(last_phar_name) = fd_ptr->fname; - PHAR_G(last_alias) = fd_ptr->alias; - PHAR_G(last_alias_len) = fd_ptr->alias_len; + PHAR_G(last_phar) = archive; + PHAR_G(last_phar_name) = archive->fname; + PHAR_G(last_alias) = archive->alias; + PHAR_G(last_alias_len) = archive->alias_len; - return SUCCESS; + return archive; } } - return FAILURE; + return NULL; } /* }}} */ @@ -1355,53 +1326,61 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, si static const char hexChars[] = "0123456789ABCDEF"; -static int phar_hex_str(const char *digest, size_t digest_len, char **signature) /* {{{ */ +static size_t phar_hex_str(const char *digest, size_t digest_len, char **signature) /* {{{ */ { - int pos = -1; + size_t pos = 0; size_t len = 0; *signature = (char*)safe_pemalloc(digest_len, 2, 1, PHAR_G(persist)); for (; len < digest_len; ++len) { - (*signature)[++pos] = hexChars[((const unsigned char *)digest)[len] >> 4]; - (*signature)[++pos] = hexChars[((const unsigned char *)digest)[len] & 0x0F]; + (*signature)[pos++] = hexChars[((const unsigned char *)digest)[len] >> 4]; + (*signature)[pos++] = hexChars[((const unsigned char *)digest)[len] & 0x0F]; } - (*signature)[++pos] = '\0'; + (*signature)[pos] = '\0'; return pos; } /* }}} */ #ifndef PHAR_HAVE_OPENSSL -static zend_result phar_call_openssl_signverify(bool is_sign, php_stream *fp, zend_off_t end, const char *key, size_t key_len, char **signature, size_t *signature_len, uint32_t sig_type) /* {{{ */ -{ - zval retval, zp[4]; - zend_string *str; - - zend_function *fn = NULL; - if (is_sign) { - fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("openssl_sign")); - } else { - fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("openssl_verify")); - } +ZEND_ATTRIBUTE_NONNULL static bool phar_call_openssl_verify( + php_stream *fp, + zend_off_t end, + zend_string *public_key, + const char *signature, + size_t signature_len, + uint32_t sig_type +) { + ZEND_ASSERT(signature_len != 0); + zend_function *fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("openssl_verify")); /* OpenSSL is not available, even as a shared module */ - if (fn == NULL) { - return FAILURE; + if (UNEXPECTED(fn == NULL)) { + return false; } - if (*signature_len) { - ZVAL_STRINGL(&zp[1], *signature, *signature_len); - } else { - ZVAL_EMPTY_STRING(&zp[1]); - } - ZVAL_STRINGL(&zp[2], key, key_len); + /* Read and copy stream content */ php_stream_rewind(fp); - str = php_stream_copy_to_mem(fp, (size_t) end, 0); - if (str) { - ZVAL_STR(&zp[0], str); - } else { - ZVAL_EMPTY_STRING(&zp[0]); - } + zend_string *str = php_stream_copy_to_mem(fp, (size_t) end, false); + /* No content thus signing must fail */ + if (UNEXPECTED(str == NULL || (size_t)end != ZSTR_LEN(str))) { + return false; + } + + /* Set up parameters for call to openssl_verify() + * openssl_verify( + * string $data, + * string $signature, + * OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $public_key, + * string|int $algorithm = OPENSSL_ALGO_SHA1, + * int $padding = 0 + * ): int|false + */ + zval retval, zp[4]; + ZVAL_STR(&zp[0], str); + ZVAL_STRINGL(&zp[1], signature, signature_len); + /* Note we do not own the lifetime of the public key, but it is fine as calling the function will increase the refcount) */ + ZVAL_STR(&zp[2], public_key); if (sig_type == PHAR_SIG_OPENSSL_SHA512) { ZVAL_LONG(&zp[3], 9); /* value from openssl.c #define OPENSSL_ALGO_SHA512 9 */ } else if (sig_type == PHAR_SIG_OPENSSL_SHA256) { @@ -1410,52 +1389,84 @@ static zend_result phar_call_openssl_signverify(bool is_sign, php_stream *fp, ze /* don't rely on default value which may change in the future */ ZVAL_LONG(&zp[3], 1); /* value from openssl.c #define OPENSSL_ALGO_SHA1 1 */ } + zend_call_known_function(fn, NULL, NULL, &retval, /* param_count */ 4, zp, NULL); + /* Free string arguments that we own */ + zval_ptr_dtor_str(&zp[0]); + zval_ptr_dtor_str(&zp[1]); + + /* Returns 1 if the signature is correct, 0 if it is incorrect, and -1 or false on error. */ + switch (Z_TYPE(retval)) { + case IS_LONG: + if (1 == Z_LVAL(retval)) { + return true; + } + ZEND_FALLTHROUGH; + default: + /* Unlikely, but the openssl_verify() function may be disabled and redefined in userland and return bollocks */ + zval_ptr_dtor(&retval); + return false; + } +} + +static zend_result phar_call_openssl_sign(php_stream *fp, zend_off_t end, const char *key, size_t key_len, char **signature, size_t *signature_len, uint32_t sig_type) /* {{{ */ +{ + ZEND_ASSERT(end != 0); + ZEND_ASSERT(*signature_len == 0); + zval retval, zp[4]; + + zend_function *fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("openssl_sign")); + + /* OpenSSL is not available, even as a shared module */ + if (fn == NULL) { + return FAILURE; + } - if ((size_t)end != Z_STRLEN(zp[0])) { - zval_ptr_dtor_str(&zp[0]); - zval_ptr_dtor_str(&zp[1]); - zval_ptr_dtor_str(&zp[2]); + /* Read and copy stream content */ + php_stream_rewind(fp); + zend_string *str = php_stream_copy_to_mem(fp, (size_t) end, false); + /* No content thus signing must fail */ + if (!str || (size_t)end != ZSTR_LEN(str)) { return FAILURE; } - Z_ADDREF(zp[0]); - if (is_sign) { - ZVAL_NEW_REF(&zp[1], &zp[1]); + /* Set up parameters for call to openssl_sign() + * openssl_sign( + * string $data, + * string &$signature, + * #[\SensitiveParameter] OpenSSLAsymmetricKey|OpenSSLCertificate|array|string $private_key, + * string|int $algorithm = OPENSSL_ALGO_SHA1, + * int $padding = 0 + * ): bool + */ + ZVAL_STR(&zp[0], str); + ZVAL_EMPTY_STRING(&zp[1]); + ZVAL_NEW_REF(&zp[1], &zp[1]); + ZVAL_STRINGL(&zp[2], key, key_len); + if (sig_type == PHAR_SIG_OPENSSL_SHA512) { + ZVAL_LONG(&zp[3], 9); /* value from openssl.c #define OPENSSL_ALGO_SHA512 9 */ + } else if (sig_type == PHAR_SIG_OPENSSL_SHA256) { + ZVAL_LONG(&zp[3], 7); /* value from openssl.c #define OPENSSL_ALGO_SHA256 7 */ } else { - Z_ADDREF(zp[1]); + /* don't rely on default value which may change in the future */ + ZVAL_LONG(&zp[3], 1); /* value from openssl.c #define OPENSSL_ALGO_SHA1 1 */ } - Z_ADDREF(zp[2]); zend_call_known_function(fn, NULL, NULL, &retval, /* param_count */ 4, zp, NULL); - Z_DELREF(zp[0]); - - if (is_sign) { - ZVAL_UNREF(&zp[1]); - } else { - Z_DELREF(zp[1]); - } - Z_DELREF(zp[2]); + ZVAL_UNREF(&zp[1]); zval_ptr_dtor_str(&zp[0]); zval_ptr_dtor_str(&zp[2]); switch (Z_TYPE(retval)) { - case IS_LONG: - zval_ptr_dtor(&zp[1]); - if (1 == Z_LVAL(retval)) { - return SUCCESS; - } - return FAILURE; case IS_TRUE: *signature = estrndup(Z_STRVAL(zp[1]), Z_STRLEN(zp[1])); *signature_len = Z_STRLEN(zp[1]); zval_ptr_dtor(&zp[1]); return SUCCESS; default: + /* Unlikely, but the openssl_sign() function may be disabled and redefined in userland and return bollocks */ zval_ptr_dtor(&retval); - ZEND_FALLTHROUGH; - case IS_FALSE: zval_ptr_dtor(&zp[1]); return FAILURE; } @@ -1463,7 +1474,7 @@ static zend_result phar_call_openssl_signverify(bool is_sign, php_stream *fp, ze /* }}} */ #endif /* #ifndef PHAR_HAVE_OPENSSL */ -zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, char *sig, size_t sig_len, const char *fname, char **signature, size_t *signature_len, char **error) /* {{{ */ +zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t sig_type, const char *sig, size_t sig_len, const char *fname, char **signature, size_t *signature_len, char **error) /* {{{ */ { size_t read_size, len; zend_off_t read_len; @@ -1488,8 +1499,6 @@ zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t s } else { mdtype = EVP_sha1(); } -#else - size_t tempsig; #endif zend_string *pubkey = NULL; char *pfile; @@ -1524,9 +1533,7 @@ zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t s } #ifndef PHAR_HAVE_OPENSSL - tempsig = sig_len; - - if (FAILURE == phar_call_openssl_signverify(false, fp, end_of_phar, ZSTR_VAL(pubkey), ZSTR_LEN(pubkey), &sig, &tempsig, sig_type)) { + if (!phar_call_openssl_verify(fp, end_of_phar, pubkey, sig, sig_len, sig_type)) { zend_string_release_ex(pubkey, 0); if (error) { @@ -1536,9 +1543,7 @@ zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t s return FAILURE; } - zend_string_release_ex(pubkey, 0); - - sig_len = tempsig; + zend_string_release_ex(pubkey, false); #else in = BIO_new_mem_buf(ZSTR_VAL(pubkey), ZSTR_LEN(pubkey)); @@ -1610,7 +1615,7 @@ zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t s EVP_MD_CTX_destroy(md_ctx); #endif - *signature_len = phar_hex_str((const char*)sig, sig_len, signature); + *signature_len = phar_hex_str(sig, sig_len, signature); } break; case PHAR_SIG_SHA512: { @@ -1783,8 +1788,9 @@ zend_result phar_verify_signature(php_stream *fp, size_t end_of_phar, uint32_t s } /* }}} */ -ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signature, size_t *signature_length, char **error) /* {{{ */ +ZEND_ATTRIBUTE_NONNULL zend_string* phar_create_signature(phar_archive_data *phar, php_stream *fp, char **error) /* {{{ */ { + zend_string *signature = NULL; unsigned char buf[1024]; size_t sig_len; @@ -1807,8 +1813,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar } PHP_SHA512Final(digest, &context); - *signature = estrndup((char *) digest, 64); - *signature_length = 64; + signature = zend_string_init((const char*)digest, 64, false); break; } default: @@ -1825,8 +1830,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar } PHP_SHA256Final(digest, &context); - *signature = estrndup((char *) digest, 32); - *signature_length = 32; + signature = zend_string_init((const char*)digest, 32, false); break; } case PHAR_SIG_OPENSSL_SHA512: @@ -1852,7 +1856,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar if (in == NULL) { spprintf(error, 0, "unable to write to phar \"%s\" with requested openssl signature", ZSTR_VAL(phar->fname)); - return FAILURE; + return NULL; } key = PEM_read_bio_PrivateKey(in, NULL,NULL, ""); @@ -1860,14 +1864,14 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar if (!key) { *error = estrdup("unable to process private key"); - return FAILURE; + return NULL; } md_ctx = EVP_MD_CTX_create(); if (md_ctx == NULL) { EVP_PKEY_free(key); spprintf(error, 0, "unable to initialize openssl signature for phar \"%s\"", ZSTR_VAL(phar->fname)); - return FAILURE; + return NULL; } siglen = EVP_PKEY_size(key); @@ -1878,7 +1882,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar EVP_MD_CTX_destroy(md_ctx); efree(sigbuf); spprintf(error, 0, "unable to initialize openssl signature for phar \"%s\"", ZSTR_VAL(phar->fname)); - return FAILURE; + return NULL; } while ((sig_len = php_stream_read(fp, (char*)buf, sizeof(buf))) > 0) { @@ -1887,7 +1891,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar EVP_MD_CTX_destroy(md_ctx); efree(sigbuf); spprintf(error, 0, "unable to update the openssl signature for phar \"%s\"", ZSTR_VAL(phar->fname)); - return FAILURE; + return NULL; } } @@ -1896,25 +1900,27 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar EVP_MD_CTX_destroy(md_ctx); efree(sigbuf); spprintf(error, 0, "unable to write phar \"%s\" with requested openssl signature", ZSTR_VAL(phar->fname)); - return FAILURE; + return NULL; } sigbuf[siglen] = '\0'; EVP_PKEY_free(key); EVP_MD_CTX_destroy(md_ctx); + signature = zend_string_init((const char*)sigbuf, siglen, false); + efree(sigbuf); #else size_t siglen; sigbuf = NULL; siglen = 0; php_stream_seek(fp, 0, SEEK_END); - if (FAILURE == phar_call_openssl_signverify(true, fp, php_stream_tell(fp), PHAR_G(openssl_privatekey), PHAR_G(openssl_privatekey_len), (char **)&sigbuf, &siglen, phar->sig_flags)) { + if (FAILURE == phar_call_openssl_sign(fp, php_stream_tell(fp), PHAR_G(openssl_privatekey), PHAR_G(openssl_privatekey_len), (char **)&sigbuf, &siglen, phar->sig_flags)) { spprintf(error, 0, "unable to write phar \"%s\" with requested openssl signature", ZSTR_VAL(phar->fname)); - return FAILURE; + return NULL; } + signature = zend_string_init((const char*)sigbuf, siglen, false); + efree(sigbuf); #endif - *signature = (char *) sigbuf; - *signature_length = siglen; } break; case PHAR_SIG_SHA1: { @@ -1928,8 +1934,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar } PHP_SHA1Final(digest, &context); - *signature = estrndup((char *) digest, 20); - *signature_length = 20; + signature = zend_string_init((const char*)digest, 20, false); break; } case PHAR_SIG_MD5: { @@ -1943,14 +1948,13 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_create_signature(phar_archive_data *phar } PHP_MD5Final(digest, &context); - *signature = estrndup((char *) digest, 16); - *signature_length = 16; + signature = zend_string_init((const char*)digest, 16, false); break; } } - phar->sig_len = phar_hex_str((const char *)*signature, *signature_length, &phar->signature); - return SUCCESS; + phar->sig_len = phar_hex_str(ZSTR_VAL(signature), ZSTR_LEN(signature), &phar->signature); + return signature; } /* }}} */ @@ -2011,14 +2015,13 @@ static void phar_manifest_copy_ctor(zval *zv) /* {{{ */ } /* }}} */ -static void phar_copy_cached_phar(phar_archive_data **pphar) /* {{{ */ +static phar_archive_data* phar_copy_cached_phar(const phar_archive_data *persistent_phar) /* {{{ */ { - phar_archive_data *phar; HashTable newmanifest; phar_archive_object *objphar; - phar = (phar_archive_data *) emalloc(sizeof(phar_archive_data)); - *phar = **pphar; + phar_archive_data *phar = emalloc(sizeof(phar_archive_data)); + *phar = *persistent_phar; phar->is_persistent = 0; zend_string_addref(phar->fname); @@ -2032,17 +2035,13 @@ static void phar_copy_cached_phar(phar_archive_data **pphar) /* {{{ */ phar_metadata_tracker_clone(&phar->metadata_tracker); - zend_hash_init(&newmanifest, sizeof(phar_entry_info), - zend_get_hash_value, destroy_phar_manifest_entry, 0); - zend_hash_copy(&newmanifest, &(*pphar)->manifest, phar_manifest_copy_ctor); + zend_hash_init(&newmanifest, sizeof(phar_entry_info), NULL, destroy_phar_manifest_entry, 0); + zend_hash_copy(&newmanifest, &persistent_phar->manifest, phar_manifest_copy_ctor); zend_hash_apply_with_argument(&newmanifest, phar_update_cached_entry, (void *)phar); phar->manifest = newmanifest; - zend_hash_init(&phar->mounted_dirs, sizeof(char *), - zend_get_hash_value, NULL, 0); - zend_hash_init(&phar->virtual_dirs, sizeof(char *), - zend_get_hash_value, NULL, 0); - zend_hash_copy(&phar->virtual_dirs, &(*pphar)->virtual_dirs, NULL); - *pphar = phar; + zend_hash_init(&phar->mounted_dirs, sizeof(char *), NULL, NULL, 0); + zend_hash_init(&phar->virtual_dirs, sizeof(char *), NULL, NULL, 0); + zend_hash_copy(&phar->virtual_dirs, &persistent_phar->virtual_dirs, NULL); /* now, scan the list of persistent Phar objects referencing this phar and update the pointers */ ZEND_HASH_MAP_FOREACH_PTR(&PHAR_G(phar_persist_map), objphar) { @@ -2050,29 +2049,26 @@ static void phar_copy_cached_phar(phar_archive_data **pphar) /* {{{ */ objphar->archive = phar; } } ZEND_HASH_FOREACH_END(); + return phar; } /* }}} */ zend_result phar_copy_on_write(phar_archive_data **pphar) /* {{{ */ { - zval zv, *pzv; - phar_archive_data *newpphar; + phar_archive_data *newpphar = phar_copy_cached_phar(*pphar); - ZVAL_PTR(&zv, *pphar); - pzv = zend_hash_add(&(PHAR_G(phar_fname_map)), (*pphar)->fname, &zv); + zval *pzv = zend_hash_add_ptr(&(PHAR_G(phar_fname_map)), newpphar->fname, newpphar); if (!pzv) { return FAILURE; } - phar_copy_cached_phar((phar_archive_data **)&Z_PTR_P(pzv)); - newpphar = Z_PTR_P(pzv); /* invalidate phar cache */ PHAR_G(last_phar) = NULL; PHAR_G(last_alias) = NULL; PHAR_G(last_phar_name) = NULL; if (newpphar->alias_len && NULL == zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), newpphar->alias, newpphar->alias_len, newpphar)) { - zend_hash_del(&(PHAR_G(phar_fname_map)), (*pphar)->fname); + zend_hash_del(&(PHAR_G(phar_fname_map)), newpphar->fname); return FAILURE; } diff --git a/ext/phar/zip.c b/ext/phar/zip.c index 28e1a3d5641e..7811865e3e69 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -224,8 +224,14 @@ static char *phar_find_eocd(const char *s, size_t n) * This is used by phar_open_from_fp to process a zip-based phar, but can be called * directly. */ -zend_result phar_parse_zipfile(php_stream *fp, const char *fname, size_t fname_len, const char *alias, size_t alias_len, phar_archive_data** pphar, char **error) /* {{{ */ -{ +zend_result phar_parse_zipfile( + php_stream *fp, + const char *fname, + size_t fname_len, + /* copyable & hash update */ zend_string *alias, + phar_archive_data** pphar, + char **error +) { phar_zip_dir_end locator; char buf[sizeof(locator) + 65536]; zend_off_t size; @@ -760,8 +766,8 @@ zend_result phar_parse_zipfile(php_stream *fp, const char *fname, size_t fname_l zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), mydata->alias, mydata->alias_len, mydata); } else { - if (alias_len) { - phar_archive_data *fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len); + if (alias) { + phar_archive_data *fd_ptr = zend_hash_find_ptr(&(PHAR_G(phar_alias_map)), alias); if (fd_ptr) { if (SUCCESS != phar_free_alias(fd_ptr)) { if (error) { @@ -772,9 +778,9 @@ zend_result phar_parse_zipfile(php_stream *fp, const char *fname, size_t fname_l } } - zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, mydata); - mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent); - mydata->alias_len = alias_len; + zend_hash_add_ptr(&(PHAR_G(phar_alias_map)), alias, mydata); + mydata->alias = pestrndup(ZSTR_VAL(alias), ZSTR_LEN(alias), mydata->is_persistent); + mydata->alias_len = ZSTR_LEN(alias); } else { mydata->alias = pestrndup(ZSTR_VAL(mydata->fname), ZSTR_LEN(mydata->fname), mydata->is_persistent); mydata->alias_len = fname_len; @@ -794,10 +800,16 @@ zend_result phar_parse_zipfile(php_stream *fp, const char *fname, size_t fname_l /** * Create or open a zip-based phar for writing */ -ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_open_or_create_zip(zend_string *fname, char *alias, size_t alias_len, bool is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */ -{ +ZEND_ATTRIBUTE_NONNULL_ARGS(1, 5, 6) zend_result phar_open_or_create_zip( + zend_string *fname, + /* copyable & hash update */ zend_string *alias, + bool is_data, + uint32_t options, + phar_archive_data** pphar, + char **error +) { phar_archive_data *phar; - zend_result ret = phar_create_or_parse_filename(fname, alias, alias_len, is_data, options, &phar, error); + zend_result ret = phar_create_or_parse_filename(fname, alias, is_data, options, &phar, error); if (FAILURE == ret) { return FAILURE; @@ -822,7 +834,6 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 6, 7) zend_result phar_open_or_create_zip(zend_st return FAILURE; } -/* }}} */ struct _phar_zip_pass { php_stream *filefp; @@ -1148,8 +1159,7 @@ static zend_result phar_zip_applysignature(phar_archive_data *phar, struct _phar { /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */ if (!phar->is_data || phar->sig_flags) { - size_t signature_length; - char *signature, sigbuf[8]; + char sigbuf[8]; phar_entry_info entry = {0}; php_stream *newfile; zend_off_t tell; @@ -1171,11 +1181,10 @@ static zend_result phar_zip_applysignature(phar_archive_data *phar, struct _phar } char *signature_error = NULL; - if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, &signature_error)) { - if (signature_error) { - spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", signature_error); - efree(signature_error); - } + zend_string *signature = phar_create_signature(phar, newfile, &signature_error); + if (!signature) { + spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", signature_error); + efree(signature_error); php_stream_close(newfile); return FAILURE; @@ -1185,17 +1194,17 @@ static zend_result phar_zip_applysignature(phar_archive_data *phar, struct _phar entry.fp_type = PHAR_MOD; entry.is_modified = 1; if (entry.fp == NULL) { - efree(signature); + zend_string_release_ex(signature, false); spprintf(pass->error, 0, "phar error: unable to create temporary file for signature"); php_stream_close(newfile); return FAILURE; } PHAR_SET_32(sigbuf, phar->sig_flags); - PHAR_SET_32(sigbuf + 4, signature_length); + PHAR_SET_32(sigbuf + 4, ZSTR_LEN(signature)); - if (Z_UL(8) != php_stream_write(entry.fp, sigbuf, 8) || signature_length != php_stream_write(entry.fp, signature, signature_length)) { - efree(signature); + if (Z_UL(8) != php_stream_write(entry.fp, sigbuf, 8) || ZSTR_LEN(signature) != php_stream_write(entry.fp, ZSTR_VAL(signature), ZSTR_LEN(signature))) { + zend_string_release_ex(signature, false); if (pass->error) { spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", ZSTR_VAL(phar->fname)); } @@ -1206,13 +1215,13 @@ static zend_result phar_zip_applysignature(phar_archive_data *phar, struct _phar ALLOCA_FLAG(use_heap); ZSTR_ALLOCA_INIT(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1, use_heap); - efree(signature); - entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8; + entry.uncompressed_filesize = entry.compressed_filesize = ZSTR_LEN(signature) + 8; entry.phar = phar; /* throw out return value and write the signature */ phar_zip_changed_apply_int(&entry, (void *)pass); ZSTR_ALLOCA_FREE(entry.filename, use_heap); php_stream_close(newfile); + zend_string_release_ex(signature, false); if (pass->error && *(pass->error)) { /* error is set by writeheaders */ @@ -1273,7 +1282,8 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_zip_flush(phar_archive_data *phar, ze /* register alias */ if (phar->alias_len) { - if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(phar->fname), ZSTR_LEN(phar->fname), phar->alias, phar->alias_len, error)) { + phar = phar_get_archive(ZSTR_VAL(phar->fname), ZSTR_LEN(phar->fname), phar->alias, phar->alias_len, error); + if (!phar) { return EOF; } } diff --git a/ext/random/php_random.h b/ext/random/php_random.h index 7c17cd40790f..f5db3403ec18 100644 --- a/ext/random/php_random.h +++ b/ext/random/php_random.h @@ -125,13 +125,8 @@ extern PHPAPI zend_class_entry *random_ce_Random_Randomizer; extern PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary; -static inline php_random_engine *php_random_engine_from_obj(zend_object *object) { - return (php_random_engine *)((char *)(object) - XtOffsetOf(php_random_engine, std)); -} - -static inline php_random_randomizer *php_random_randomizer_from_obj(zend_object *object) { - return (php_random_randomizer *)((char *)(object) - XtOffsetOf(php_random_randomizer, std)); -} +#define php_random_engine_from_obj(object) ZEND_CONTAINER_OF(object, php_random_engine, std) +#define php_random_randomizer_from_obj(object) ZEND_CONTAINER_OF(object, php_random_randomizer, std) # define Z_RANDOM_ENGINE_P(zval) php_random_engine_from_obj(Z_OBJ_P(zval)) diff --git a/ext/random/random.c b/ext/random/random.c index dbc7ee50a080..bd6f9f8a9018 100644 --- a/ext/random/random.c +++ b/ext/random/random.c @@ -273,7 +273,7 @@ PHPAPI void php_random_engine_common_free_object(zend_object *object) PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object) { - php_random_engine *old_engine = php_random_engine_from_obj(object); + const php_random_engine *old_engine = php_random_engine_from_obj(object); php_random_engine *new_engine = php_random_engine_from_obj(old_engine->std.ce->create_object(old_engine->std.ce)); new_engine->engine.algo = old_engine->engine.algo; @@ -745,7 +745,7 @@ PHP_MINIT_FUNCTION(random) random_ce_Random_Engine_Mt19937->create_object = php_random_engine_mt19937_new; random_ce_Random_Engine_Mt19937->default_object_handlers = &random_engine_mt19937_object_handlers; memcpy(&random_engine_mt19937_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - random_engine_mt19937_object_handlers.offset = XtOffsetOf(php_random_engine, std); + random_engine_mt19937_object_handlers.offset = offsetof(php_random_engine, std); random_engine_mt19937_object_handlers.free_obj = php_random_engine_common_free_object; random_engine_mt19937_object_handlers.clone_obj = php_random_engine_common_clone_object; @@ -754,7 +754,7 @@ PHP_MINIT_FUNCTION(random) random_ce_Random_Engine_PcgOneseq128XslRr64->create_object = php_random_engine_pcgoneseq128xslrr64_new; random_ce_Random_Engine_PcgOneseq128XslRr64->default_object_handlers = &random_engine_pcgoneseq128xslrr64_object_handlers; memcpy(&random_engine_pcgoneseq128xslrr64_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - random_engine_pcgoneseq128xslrr64_object_handlers.offset = XtOffsetOf(php_random_engine, std); + random_engine_pcgoneseq128xslrr64_object_handlers.offset = offsetof(php_random_engine, std); random_engine_pcgoneseq128xslrr64_object_handlers.free_obj = php_random_engine_common_free_object; random_engine_pcgoneseq128xslrr64_object_handlers.clone_obj = php_random_engine_common_clone_object; @@ -763,7 +763,7 @@ PHP_MINIT_FUNCTION(random) random_ce_Random_Engine_Xoshiro256StarStar->create_object = php_random_engine_xoshiro256starstar_new; random_ce_Random_Engine_Xoshiro256StarStar->default_object_handlers = &random_engine_xoshiro256starstar_object_handlers; memcpy(&random_engine_xoshiro256starstar_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - random_engine_xoshiro256starstar_object_handlers.offset = XtOffsetOf(php_random_engine, std); + random_engine_xoshiro256starstar_object_handlers.offset = offsetof(php_random_engine, std); random_engine_xoshiro256starstar_object_handlers.free_obj = php_random_engine_common_free_object; random_engine_xoshiro256starstar_object_handlers.clone_obj = php_random_engine_common_clone_object; @@ -772,7 +772,7 @@ PHP_MINIT_FUNCTION(random) random_ce_Random_Engine_Secure->create_object = php_random_engine_secure_new; random_ce_Random_Engine_Secure->default_object_handlers = &random_engine_secure_object_handlers; memcpy(&random_engine_secure_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - random_engine_secure_object_handlers.offset = XtOffsetOf(php_random_engine, std); + random_engine_secure_object_handlers.offset = offsetof(php_random_engine, std); random_engine_secure_object_handlers.free_obj = php_random_engine_common_free_object; random_engine_secure_object_handlers.clone_obj = NULL; @@ -781,7 +781,7 @@ PHP_MINIT_FUNCTION(random) random_ce_Random_Randomizer->create_object = php_random_randomizer_new; random_ce_Random_Randomizer->default_object_handlers = &random_randomizer_object_handlers; memcpy(&random_randomizer_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - random_randomizer_object_handlers.offset = XtOffsetOf(php_random_randomizer, std); + random_randomizer_object_handlers.offset = offsetof(php_random_randomizer, std); random_randomizer_object_handlers.free_obj = randomizer_free_obj; random_randomizer_object_handlers.clone_obj = NULL; diff --git a/ext/readline/readline_cli.c b/ext/readline/readline_cli.c index 01c997a1ccaa..3b2a08a15843 100644 --- a/ext/readline/readline_cli.c +++ b/ext/readline/readline_cli.c @@ -527,7 +527,8 @@ static char *cli_completion_generator(const char *text, int index) /* {{{ */ } else if (text[0] == '#' && text[1] != '[') { retval = cli_completion_generator_ini(text, textlen, &cli_completion_state); } else { - char *lc_text, *class_name_end; + char *lc_text; + const char *class_name_end; zend_string *class_name = NULL; zend_class_entry *ce = NULL; @@ -659,6 +660,7 @@ static int readline_shell_run(void) /* {{{ */ zend_string_release_ex(prompt, 0); /* TODO: This might be wrong! */ prompt = cli_get_prompt("php", '>'); + free(line); continue; } } diff --git a/ext/readline/tests/readline_cli_ini_directive.phpt b/ext/readline/tests/readline_cli_ini_directive.phpt new file mode 100644 index 000000000000..ebf4ac10a281 --- /dev/null +++ b/ext/readline/tests/readline_cli_ini_directive.phpt @@ -0,0 +1,21 @@ +--TEST-- +Interactive shell: setting an INI directive via #name=value +--EXTENSIONS-- +readline +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +%AINI[5]%A diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index e74b7fcb27a7..af565ed53a32 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -174,9 +174,7 @@ typedef struct { zend_object zo; } reflection_object; -static inline reflection_object *reflection_object_from_obj(zend_object *obj) { - return (reflection_object*)((char*)(obj) - XtOffsetOf(reflection_object, zo)); -} +#define reflection_object_from_obj(obj) ZEND_CONTAINER_OF(obj, reflection_object, zo) #define Z_REFLECTION_P(zv) reflection_object_from_obj(Z_OBJ_P((zv))) /* }}} */ @@ -6731,6 +6729,11 @@ ZEND_METHOD(ReflectionProperty, isReadable) zval member; ZVAL_STR(&member, ref->unmangled_name); zend_call_known_instance_method_with_1_params(ce->__isset, obj, return_value, &member); + + if (Z_TYPE_P(return_value) == IS_REFERENCE) { + zend_unwrap_reference(return_value); + } + *guard &= ~ZEND_GUARD_PROPERTY_ISSET; OBJ_RELEASE(obj); return; @@ -8159,7 +8162,7 @@ ZEND_METHOD(ReflectionConstant, __toString) PHP_MINIT_FUNCTION(reflection) /* {{{ */ { memcpy(&reflection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - reflection_object_handlers.offset = XtOffsetOf(reflection_object, zo); + reflection_object_handlers.offset = offsetof(reflection_object, zo); reflection_object_handlers.free_obj = reflection_free_objects_storage; reflection_object_handlers.clone_obj = NULL; reflection_object_handlers.write_property = _reflection_write_property; diff --git a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt index 9d1210705096..9ef87f42b225 100644 --- a/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClassNames_basic.phpt @@ -17,5 +17,10 @@ Directory RoundingMode SortDirection StreamBucket +StreamError +StreamErrorCode +StreamErrorMode +StreamErrorStore +StreamException __PHP_Incomplete_Class php_user_filter diff --git a/ext/reflection/tests/gh22000.phpt b/ext/reflection/tests/gh22000.phpt new file mode 100644 index 000000000000..aee4863e9b7d --- /dev/null +++ b/ext/reflection/tests/gh22000.phpt @@ -0,0 +1,61 @@ +--TEST-- +GH-22000 - Ensure __isset returning a reference is supported by ReflectionProperty::isReadable() +--FILE-- +b); + unset($this->c); + } + + public function __isset($name) { + return $name === 'b'; + } + + public function __get($name) {} +} + +class TestClass2 { + public int $d; + public int $e; + public int $f; + + public function __construct() { + unset($this->e); + unset($this->f); + } + + public function &__isset($name) { + return $name === 'f'; + } + + public function __get($name) {} +} + + +function test($class) { + $rc = new ReflectionClass($class); + foreach ($rc->getProperties() as $rp) { + echo $rp->getName() . ' from global:'; + var_dump($rp->isReadable(null, new $class)); + } +} + +test('TestClass1'); +test('TestClass2'); +?> +--EXPECTF-- +a from global:bool(true) +b from global:bool(true) +c from global:bool(false) +d from global:bool(false) +e from global: +Notice: Only variable references should be returned by reference in %s on line %d +bool(false) +f from global: +Notice: Only variable references should be returned by reference in %s on line %d +bool(true) diff --git a/ext/session/session.c b/ext/session/session.c index 96e32ea7043f..1fdfc5d1073f 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -446,9 +446,7 @@ static zend_result php_session_initialize(void) } else if (PS(use_strict_mode) && PS(mod)->s_validate_sid && PS(mod)->s_validate_sid(&PS(mod_data), PS(id)) == FAILURE ) { - if (PS(id)) { - zend_string_release_ex(PS(id), false); - } + zend_string_release_ex(PS(id), false); PS(id) = PS(mod)->s_create_sid(&PS(mod_data)); if (!PS(id)) { PS(id) = php_session_create_id(NULL); @@ -736,6 +734,27 @@ static PHP_INI_MH(OnUpdateSessionStr) SESSION_CHECK_ACTIVE_STATE; SESSION_CHECK_OUTPUT_STATE; + if (new_value && zend_str_has_nul_byte(new_value)) { + if (stage != ZEND_INI_STAGE_DEACTIVATE) { + php_error_docref(NULL, E_WARNING, "\"%s\" must not contain null bytes", ZSTR_VAL(entry->name)); + } + return FAILURE; + } + + return OnUpdateStr(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); +} + +static PHP_INI_MH(OnUpdateSessionSameSite) +{ + SESSION_CHECK_ACTIVE_STATE; + SESSION_CHECK_OUTPUT_STATE; + + if (new_value && ZSTR_LEN(new_value) > 0 && !php_is_valid_samesite_value(new_value)) { + php_error_docref(NULL, E_WARNING, + "session.cookie_samesite must be \"Strict\", \"Lax\", \"None\", or \"\""); + return FAILURE; + } + return OnUpdateStr(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); } @@ -909,11 +928,11 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("session.cookie_domain", "", PHP_INI_ALL, OnUpdateSessionStr, cookie_domain, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.cookie_secure", "0", PHP_INI_ALL, OnUpdateSessionBool, cookie_secure, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.cookie_partitioned", "0", PHP_INI_ALL, OnUpdateSessionBool, cookie_partitioned, php_ps_globals, ps_globals) - STD_PHP_INI_BOOLEAN("session.cookie_httponly", "0", PHP_INI_ALL, OnUpdateSessionBool, cookie_httponly, php_ps_globals, ps_globals) - STD_PHP_INI_ENTRY("session.cookie_samesite", "", PHP_INI_ALL, OnUpdateSessionStr, cookie_samesite, php_ps_globals, ps_globals) + STD_PHP_INI_BOOLEAN("session.cookie_httponly", "1", PHP_INI_ALL, OnUpdateSessionBool, cookie_httponly, php_ps_globals, ps_globals) + STD_PHP_INI_ENTRY("session.cookie_samesite", "Lax", PHP_INI_ALL, OnUpdateSessionSameSite, cookie_samesite, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.use_cookies", "1", PHP_INI_ALL, OnUpdateSessionBool, use_cookies, php_ps_globals, ps_globals) STD_PHP_INI_BOOLEAN("session.use_only_cookies", "1", PHP_INI_ALL, OnUpdateUseOnlyCookies, use_only_cookies, php_ps_globals, ps_globals) - STD_PHP_INI_BOOLEAN("session.use_strict_mode", "0", PHP_INI_ALL, OnUpdateSessionBool, use_strict_mode, php_ps_globals, ps_globals) + STD_PHP_INI_BOOLEAN("session.use_strict_mode", "1", PHP_INI_ALL, OnUpdateSessionBool, use_strict_mode, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.referer_check", "", PHP_INI_ALL, OnUpdateRefererCheck, extern_referer_chk, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.cache_limiter", "nocache", PHP_INI_ALL, OnUpdateSessionStr, cache_limiter, php_ps_globals, ps_globals) STD_PHP_INI_ENTRY("session.cache_expire", "180", PHP_INI_ALL, OnUpdateSessionLong, cache_expire, php_ps_globals, ps_globals) diff --git a/ext/session/tests/bug74892.phpt b/ext/session/tests/bug74892.phpt index 916120cb4d44..7a2211210b45 100644 --- a/ext/session/tests/bug74892.phpt +++ b/ext/session/tests/bug74892.phpt @@ -4,6 +4,7 @@ Bug #74892 Url Rewriting (trans_sid) not working on urls that start with # session.use_cookies=0 session.use_only_cookies=0 session.use_trans_sid=1 +session.use_strict_mode=0 --EXTENSIONS-- session --SKIPIF-- diff --git a/ext/session/tests/bug80774.phpt b/ext/session/tests/bug80774.phpt index 7dd86add02b0..ba5d39e1953a 100644 --- a/ext/session/tests/bug80774.phpt +++ b/ext/session/tests/bug80774.phpt @@ -2,6 +2,8 @@ Bug #80774 (session_name() problem with backslash) --EXTENSIONS-- session +--INI-- +session.use_strict_mode=0 --FILE-- --EXPECTHEADERS-- -Set-Cookie: foo\bar=12345; path=/ +Set-Cookie: foo\bar=12345; path=/; HttpOnly; SameSite=Lax --EXPECT-- diff --git a/ext/session/tests/gh9200.phpt b/ext/session/tests/gh9200.phpt index fd6e28ab8b90..7e458176d10f 100644 --- a/ext/session/tests/gh9200.phpt +++ b/ext/session/tests/gh9200.phpt @@ -2,6 +2,7 @@ GH-9200: setcookie has an obsolete expires date format --INI-- session.cookie_lifetime=3600 +session.use_strict_mode=0 --EXTENSIONS-- session --CGI-- @@ -12,7 +13,7 @@ session_id('bar'); session_start(); foreach (headers_list() as $header) { - if (preg_match('/^Set-Cookie: foo=bar; expires=(Mon|Tue|Wed|Thu|Fri|Sat|Sun), [0-9][0-9] (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) 2[0-9][0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] GMT; Max-Age=3600; path=\\/$/', $header)) { + if (preg_match('/^Set-Cookie: foo=bar; expires=(Mon|Tue|Wed|Thu|Fri|Sat|Sun), [0-9][0-9] (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) 2[0-9][0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] GMT; Max-Age=3600; path=\/; HttpOnly; SameSite=Lax$/', $header)) { echo "Success", PHP_EOL; exit; } diff --git a/ext/session/tests/mod_files/gc_dirdepth2.phpt b/ext/session/tests/mod_files/gc_dirdepth2.phpt index a8724285125a..d858c1f95db8 100644 --- a/ext/session/tests/mod_files/gc_dirdepth2.phpt +++ b/ext/session/tests/mod_files/gc_dirdepth2.phpt @@ -7,6 +7,7 @@ session --INI-- session.gc_probability=0 session.gc_maxlifetime=10 +session.use_strict_mode=0 --FILE-- "baz", "secure" => FALSE, "httponly" => FALSE, - "samesite" => "please"])); + "samesite" => "Strict"])); var_dump(session_get_cookie_params()); var_dump(session_set_cookie_params([ "secure" => TRUE, @@ -107,7 +107,7 @@ array(7) { ["httponly"]=> bool(false) ["samesite"]=> - string(6) "please" + string(6) "Strict" } bool(true) array(7) { @@ -124,6 +124,6 @@ array(7) { ["httponly"]=> bool(false) ["samesite"]=> - string(6) "please" + string(6) "Strict" } Done diff --git a/ext/session/tests/session_get_cookie_params_variation1.phpt b/ext/session/tests/session_get_cookie_params_variation1.phpt index 7ce112c9b94d..0ab0f233530b 100644 --- a/ext/session/tests/session_get_cookie_params_variation1.phpt +++ b/ext/session/tests/session_get_cookie_params_variation1.phpt @@ -30,7 +30,7 @@ ini_set("session.cookie_secure", TRUE); var_dump(session_get_cookie_params()); ini_set("session.cookie_httponly", TRUE); var_dump(session_get_cookie_params()); -ini_set("session.cookie_samesite", "foo"); +ini_set("session.cookie_samesite", "Lax"); var_dump(session_get_cookie_params()); ini_set("session.cookie_partitioned", TRUE); var_dump(session_get_cookie_params()); @@ -150,7 +150,7 @@ array(7) { ["httponly"]=> bool(true) ["samesite"]=> - string(3) "foo" + string(3) "Lax" } array(7) { ["lifetime"]=> @@ -166,6 +166,6 @@ array(7) { ["httponly"]=> bool(true) ["samesite"]=> - string(3) "foo" + string(3) "Lax" } Done diff --git a/ext/session/tests/session_regenerate_id_cookie.phpt b/ext/session/tests/session_regenerate_id_cookie.phpt index 6516ad7061d1..32706047539a 100644 --- a/ext/session/tests/session_regenerate_id_cookie.phpt +++ b/ext/session/tests/session_regenerate_id_cookie.phpt @@ -67,14 +67,14 @@ string(%d) "X-Powered-By: PHP/%d.%d.%s Expires: %s Cache-Control: no-store, no-cache, must-revalidate Pragma: no-cache -Set-Cookie: PHPSESSID=%s; path=/ +Set-Cookie: PHPSESSID=%s; path=/; HttpOnly; SameSite=Lax Content-type: text/html; charset=UTF-8 bool(true) -Set-Cookie: PHPSESSID=%s; path=/ +Set-Cookie: PHPSESSID=%s; path=/; HttpOnly; SameSite=Lax bool(true) bool(true) -Set-Cookie: PHPSESSID=%s; path=/ +Set-Cookie: PHPSESSID=%s; path=/; HttpOnly; SameSite=Lax bool(true) bool(true) string(32) "%s" diff --git a/ext/session/tests/session_set_cookie_params_invalid_ini.phpt b/ext/session/tests/session_set_cookie_params_invalid_ini.phpt new file mode 100644 index 000000000000..8ebef4a5209d --- /dev/null +++ b/ext/session/tests/session_set_cookie_params_invalid_ini.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test session.cookie_samesite with invalid INI value +--INI-- +session.cookie_samesite=Invalid +--EXTENSIONS-- +session +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: PHP Startup: session.cookie_samesite must be "Strict", "Lax", "None", or "" in Unknown on line 0 +string(3) "Lax" +Done diff --git a/ext/session/tests/session_set_cookie_params_variation6.phpt b/ext/session/tests/session_set_cookie_params_variation6.phpt index 61243f82751e..fbf958a0be07 100644 --- a/ext/session/tests/session_set_cookie_params_variation6.phpt +++ b/ext/session/tests/session_set_cookie_params_variation6.phpt @@ -1,7 +1,7 @@ --TEST-- -Test session_set_cookie_params() function : variation +Test session_set_cookie_params() samesite validation --INI-- -session.cookie_samesite=test +session.cookie_samesite=Lax --EXTENSIONS-- session --SKIPIF-- @@ -11,36 +11,56 @@ session ob_start(); -echo "*** Testing session_set_cookie_params() : variation ***\n"; - +echo "-- Valid values --\n"; var_dump(ini_get("session.cookie_samesite")); -var_dump(session_set_cookie_params(["samesite" => "nothing"])); +var_dump(session_set_cookie_params(["samesite" => "Strict"])); var_dump(ini_get("session.cookie_samesite")); -var_dump(session_start()); +var_dump(session_set_cookie_params(["samesite" => "None"])); var_dump(ini_get("session.cookie_samesite")); -var_dump(session_set_cookie_params(["samesite" => "test"])); +var_dump(session_set_cookie_params(["samesite" => ""])); var_dump(ini_get("session.cookie_samesite")); -var_dump(session_destroy()); + +echo "-- Invalid value via session_set_cookie_params --\n"; +var_dump(session_set_cookie_params(["samesite" => "Invalid"])); var_dump(ini_get("session.cookie_samesite")); -var_dump(session_set_cookie_params(["samesite" => "other"])); + +echo "-- Invalid value via ini_set --\n"; +var_dump(ini_set("session.cookie_samesite", "Invalid")); var_dump(ini_get("session.cookie_samesite")); +echo "-- Cannot change while session is active --\n"; +var_dump(session_set_cookie_params(["samesite" => "Lax"])); +var_dump(session_start()); +var_dump(session_set_cookie_params(["samesite" => "Strict"])); +var_dump(session_destroy()); + echo "Done"; ob_end_flush(); ?> --EXPECTF-- -*** Testing session_set_cookie_params() : variation *** -string(4) "test" +-- Valid values -- +string(3) "Lax" bool(true) -string(7) "nothing" +string(6) "Strict" bool(true) -string(7) "nothing" +string(4) "None" +bool(true) +string(0) "" +-- Invalid value via session_set_cookie_params -- -Warning: session_set_cookie_params(): Session cookie parameters cannot be changed when a session is active (started from %s on line %d) in %s on line %d +Warning: session_set_cookie_params(): session.cookie_samesite must be "Strict", "Lax", "None", or "" in %s on line %d +bool(false) +string(0) "" +-- Invalid value via ini_set -- + +Warning: ini_set(): session.cookie_samesite must be "Strict", "Lax", "None", or "" in %s on line %d bool(false) -string(7) "nothing" +string(0) "" +-- Cannot change while session is active -- bool(true) -string(7) "nothing" bool(true) -string(5) "other" + +Warning: session_set_cookie_params(): Session cookie parameters cannot be changed when a session is active (started from %s on line %d) in %s on line %d +bool(false) +bool(true) Done diff --git a/ext/session/tests/session_set_cookie_params_variation7.phpt b/ext/session/tests/session_set_cookie_params_variation7.phpt index 430c6efc36e9..3780fc0222f1 100644 --- a/ext/session/tests/session_set_cookie_params_variation7.phpt +++ b/ext/session/tests/session_set_cookie_params_variation7.phpt @@ -33,7 +33,7 @@ try { var_dump(ini_get("session.cookie_secure")); var_dump(ini_get("session.cookie_samesite")); -var_dump(session_set_cookie_params(["secure" => true, "samesite" => "please"])); +var_dump(session_set_cookie_params(["secure" => true, "samesite" => "Strict"])); var_dump(ini_get("session.cookie_secure")); var_dump(ini_get("session.cookie_samesite")); @@ -66,7 +66,7 @@ string(1) "0" string(0) "" bool(true) string(1) "1" -string(6) "please" +string(6) "Strict" string(1) "0" bool(true) string(2) "42" diff --git a/ext/session/tests/session_start_partitioned_headers.phpt b/ext/session/tests/session_start_partitioned_headers.phpt index 6fa3815aa85d..4a2c7dcf878f 100644 --- a/ext/session/tests/session_start_partitioned_headers.phpt +++ b/ext/session/tests/session_start_partitioned_headers.phpt @@ -2,6 +2,8 @@ session_start() with partitioned cookies - header test --EXTENSIONS-- session +--INI-- +session.use_strict_mode=0 --FILE-- true, "partitioned" => true]); session_start(); ?> --EXPECTHEADERS-- -Set-Cookie: PHPSESSID=12345; path=/; secure; Partitioned +Set-Cookie: PHPSESSID=12345; path=/; secure; Partitioned; HttpOnly; SameSite=Lax --EXPECT-- diff --git a/ext/session/tests/session_str_settings_null_byte.phpt b/ext/session/tests/session_str_settings_null_byte.phpt new file mode 100644 index 000000000000..693ec6971601 --- /dev/null +++ b/ext/session/tests/session_str_settings_null_byte.phpt @@ -0,0 +1,37 @@ +--TEST-- +session.cookie_path, session.cookie_domain, and session.cache_limiter must not contain null bytes +--EXTENSIONS-- +session +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: ini_set(): "session.cookie_path" must not contain null bytes in %s on line %d +bool(false) + +Warning: ini_set(): "session.cookie_domain" must not contain null bytes in %s on line %d +bool(false) + +Warning: ini_set(): "session.cache_limiter" must not contain null bytes in %s on line %d +bool(false) + +Warning: session_set_cookie_params(): "session.cookie_path" must not contain null bytes in %s on line %d +bool(false) + +Warning: session_set_cookie_params(): "session.cookie_domain" must not contain null bytes in %s on line %d +bool(false) +Done diff --git a/ext/shmop/shmop.c b/ext/shmop/shmop.c index 18e6cac9a0c9..c3ed217443f4 100644 --- a/ext/shmop/shmop.c +++ b/ext/shmop/shmop.c @@ -68,10 +68,7 @@ typedef struct php_shmop zend_class_entry *shmop_ce; static zend_object_handlers shmop_object_handlers; -static inline php_shmop *shmop_from_obj(zend_object *obj) -{ - return (php_shmop *)((char *)(obj) - XtOffsetOf(php_shmop, std)); -} +#define shmop_from_obj(obj) ZEND_CONTAINER_OF(obj, php_shmop, std) #define Z_SHMOP_P(zv) shmop_from_obj(Z_OBJ_P(zv)) @@ -108,7 +105,7 @@ PHP_MINIT_FUNCTION(shmop) shmop_ce->default_object_handlers = &shmop_object_handlers; memcpy(&shmop_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - shmop_object_handlers.offset = XtOffsetOf(php_shmop, std); + shmop_object_handlers.offset = offsetof(php_shmop, std); shmop_object_handlers.free_obj = shmop_free_obj; shmop_object_handlers.get_constructor = shmop_get_constructor; shmop_object_handlers.clone_obj = NULL; diff --git a/ext/simplexml/php_simplexml_exports.h b/ext/simplexml/php_simplexml_exports.h index f1f519e85e12..fb940f2fbc77 100644 --- a/ext/simplexml/php_simplexml_exports.h +++ b/ext/simplexml/php_simplexml_exports.h @@ -35,10 +35,7 @@ PHP_SXE_API zend_object *sxe_object_new(zend_class_entry *ce); -static inline php_sxe_object *php_sxe_fetch_object(zend_object *obj) /* {{{ */ { - return (php_sxe_object *)((char*)(obj) - XtOffsetOf(php_sxe_object, zo)); -} -/* }}} */ +#define php_sxe_fetch_object(obj) ZEND_CONTAINER_OF(obj, php_sxe_object, zo) #define Z_SXEOBJ_P(zv) php_sxe_fetch_object(Z_OBJ_P((zv))) diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 5b749a462492..d86198c7639e 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -2631,7 +2631,7 @@ PHP_MINIT_FUNCTION(simplexml) ce_SimpleXMLElement->get_iterator = php_sxe_get_iterator; memcpy(&sxe_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - sxe_object_handlers.offset = XtOffsetOf(php_sxe_object, zo); + sxe_object_handlers.offset = offsetof(php_sxe_object, zo); sxe_object_handlers.free_obj = sxe_object_free_storage; sxe_object_handlers.clone_obj = sxe_object_clone; sxe_object_handlers.read_property = sxe_property_read; diff --git a/ext/simplexml/simplexml.stub.php b/ext/simplexml/simplexml.stub.php index 935af16621e9..6faf873c885f 100644 --- a/ext/simplexml/simplexml.stub.php +++ b/ext/simplexml/simplexml.stub.php @@ -51,7 +51,7 @@ public function getName(): string {} public function __toString(): string {} - public function __debugInfo(): ?array {} + public function __debugInfo(): array {} /** @tentative-return-type */ public function count(): int {} diff --git a/ext/simplexml/simplexml_arginfo.h b/ext/simplexml/simplexml_arginfo.h index 419c7874fbc5..5a62efbc7be5 100644 --- a/ext/simplexml/simplexml_arginfo.h +++ b/ext/simplexml/simplexml_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit simplexml.stub.php instead. - * Stub hash: cee51320f0f09f14962fb72125ef8ff6073a642a */ + * Stub hash: 83f1bd204b309a4558c1b09128fe29f972f54252 */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_simplexml_load_file, 0, 1, SimpleXMLElement, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -79,7 +79,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement___toString, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement___debugInfo, 0, 0, IS_ARRAY, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement___debugInfo, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SimpleXMLElement_count, 0, 0, IS_LONG, 0) diff --git a/ext/snmp/php_snmp.h b/ext/snmp/php_snmp.h index 21d544fa7b8b..d889eb37ab95 100644 --- a/ext/snmp/php_snmp.h +++ b/ext/snmp/php_snmp.h @@ -56,9 +56,7 @@ typedef struct _php_snmp_object { zend_object zo; } php_snmp_object; -static inline php_snmp_object *php_snmp_fetch_object(zend_object *obj) { - return (php_snmp_object *)((char*)(obj) - XtOffsetOf(php_snmp_object, zo)); -} +#define php_snmp_fetch_object(obj) ZEND_CONTAINER_OF(obj, php_snmp_object, zo) #define Z_SNMP_P(zv) php_snmp_fetch_object(Z_OBJ_P((zv))) diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index ecea48883d52..44f714bf0368 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -624,7 +624,7 @@ static void php_snmp_internal(INTERNAL_FUNCTION_PARAMETERS, int st, static void php_snmp_zend_string_release_from_char_pointer(char *ptr) { if (ptr) { - zend_string *pptr = (zend_string *)(ptr - XtOffsetOf(zend_string, val)); + zend_string *pptr = (zend_string *)(ptr - offsetof(zend_string, val)); zend_string_release(pptr); } } @@ -2144,7 +2144,7 @@ PHP_MINIT_FUNCTION(snmp) php_snmp_ce = register_class_SNMP(); php_snmp_ce->create_object = php_snmp_object_new; php_snmp_ce->default_object_handlers = &php_snmp_object_handlers; - php_snmp_object_handlers.offset = XtOffsetOf(php_snmp_object, zo); + php_snmp_object_handlers.offset = offsetof(php_snmp_object, zo); php_snmp_object_handlers.clone_obj = NULL; php_snmp_object_handlers.free_obj = php_snmp_object_free_storage; diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 1836ef3af7e8..151fa811b0fa 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ */ +#include #include #include "php_soap.h" @@ -349,6 +350,7 @@ static bool soap_check_xml_ref(zval *data, xmlNodePtr node) static void soap_add_xml_ref(zval *data, xmlNodePtr node) { if (SOAP_GLOBAL(ref_map)) { + Z_TRY_ADDREF_P(data); zend_hash_index_update(SOAP_GLOBAL(ref_map), (zend_ulong)(uintptr_t)node, data); } } @@ -2047,6 +2049,15 @@ static int calc_dimension_12(const char* str) return i; } +static void soap_array_position_add_digit(int *position, int digit) +{ + if (*position > (INT_MAX - digit) / 10) { + soap_error0(E_ERROR, "Encoding: array index out of range"); + } + + *position = (*position * 10) + digit; +} + static int* get_position_12(int dimension, const char* str) { int *pos; @@ -2067,7 +2078,7 @@ static int* get_position_12(int dimension, const char* str) i++; flag = 1; } - pos[i] = (pos[i]*10)+(*str-'0'); + soap_array_position_add_digit(&pos[i], *str - '0'); } else if (*str == '*') { soap_error0(E_ERROR, "Encoding: '*' may only be first arraySize value in list"); } else { @@ -2097,7 +2108,7 @@ static void get_position_ex(int dimension, const char* str, int** pos) memset(*pos,0,sizeof(int)*dimension); while (*str != ']' && *str != '\0' && i < dimension) { if (*str >= '0' && *str <= '9') { - (*pos)[i] = ((*pos)[i]*10)+(*str-'0'); + soap_array_position_add_digit(&(*pos)[i], *str - '0'); } else if (*str == ',') { i++; } @@ -2684,16 +2695,20 @@ static zval *to_zval_array(zval *ret, encodeTypePtr type, xmlNodePtr data) /* Increment position */ i = dimension; while (i > 0) { - i--; - pos[i]++; - if (pos[i] >= dims[i]) { - if (i > 0) { - pos[i] = 0; - } else { - /* TODO: Array index overflow */ - } - } else { - break; + i--; + if (pos[i] == INT_MAX) { + efree(dims); + efree(pos); + zval_ptr_dtor(ret); + ZVAL_UNDEF(ret); + soap_error0(E_ERROR, "Encoding: array index out of range"); + } + pos[i]++; + if (pos[i] < dims[i]) { + break; + } + if (i > 0) { + pos[i] = 0; } } } @@ -2783,7 +2798,7 @@ static zval *to_zval_map(zval *ret, encodeTypePtr type, xmlNodePtr data) } xmlValue = get_node(item->children, "value"); - if (!xmlKey) { + if (!xmlValue) { soap_error0(E_ERROR, "Encoding: Can't decode apache map, missing value"); } @@ -3520,7 +3535,7 @@ void encode_reset_ns(void) } else { SOAP_GLOBAL(ref_map) = emalloc(sizeof(HashTable)); } - zend_hash_init(SOAP_GLOBAL(ref_map), 0, NULL, NULL, 0); + zend_hash_init(SOAP_GLOBAL(ref_map), 0, NULL, ZVAL_PTR_DTOR, 0); } void encode_finish(void) diff --git a/ext/soap/php_packet_soap.c b/ext/soap/php_packet_soap.c index 6cf94b0a3b36..b3aceb413890 100644 --- a/ext/soap/php_packet_soap.c +++ b/ext/soap/php_packet_soap.c @@ -16,6 +16,28 @@ #include "php_soap.h" +static void master_to_zval_with_doc_cleanup(zval *ret, encodePtr encode, xmlNodePtr data, xmlDocPtr doc) +{ + bool bailout = false; + + ZVAL_UNDEF(ret); + + /* SoapClient can turn decode errors into a bailout before parse_packet_soap() frees the response doc. */ + zend_try { + master_to_zval(ret, encode, data); + } zend_catch { + bailout = true; + } zend_end_try(); + + if (bailout) { + if (!Z_ISUNDEF_P(ret)) { + zval_ptr_dtor(ret); + } + xmlFreeDoc(doc); + zend_bailout(); + } +} + /* SOAP client calls this function to parse response from SOAP server */ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctionPtr fn, char *fn_name, zval *return_value, zval *soap_headers) { @@ -190,7 +212,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio tmp = get_node(fault->children, "faultstring"); if (tmp != NULL && tmp->children != NULL) { zval zv; - master_to_zval(&zv, get_conversion(IS_STRING), tmp); + master_to_zval_with_doc_cleanup(&zv, get_conversion(IS_STRING), tmp, response); convert_to_string(&zv) faultstring = Z_STR(zv); } @@ -198,14 +220,14 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio tmp = get_node(fault->children, "faultactor"); if (tmp != NULL && tmp->children != NULL) { zval zv; - master_to_zval(&zv, get_conversion(IS_STRING), tmp); + master_to_zval_with_doc_cleanup(&zv, get_conversion(IS_STRING), tmp, response); convert_to_string(&zv) faultactor = Z_STR(zv); } tmp = get_node(fault->children, "detail"); if (tmp != NULL) { - master_to_zval(&details, NULL, tmp); + master_to_zval_with_doc_cleanup(&details, NULL, tmp, response); } } else { tmp = get_node(fault->children, "Code"); @@ -221,7 +243,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio tmp = get_node(tmp->children,"Text"); if (tmp != NULL && tmp->children != NULL) { zval zv; - master_to_zval(&zv, get_conversion(IS_STRING), tmp); + master_to_zval_with_doc_cleanup(&zv, get_conversion(IS_STRING), tmp, response); convert_to_string(&zv) faultstring = Z_STR(zv); @@ -236,7 +258,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio tmp = get_node(fault->children,"Detail"); if (tmp != NULL) { - master_to_zval(&details, NULL, tmp); + master_to_zval_with_doc_cleanup(&details, NULL, tmp, response); } } add_soap_fault(this_ptr, faultcode, faultstring ? ZSTR_VAL(faultstring) : NULL, faultactor ? ZSTR_VAL(faultactor) : NULL, &details, lang); @@ -330,9 +352,9 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio } else { /* Decoding value of parameter */ if (param != NULL) { - master_to_zval(&tmp, param->encode, val); + master_to_zval_with_doc_cleanup(&tmp, param->encode, val, response); } else { - master_to_zval(&tmp, NULL, val); + master_to_zval_with_doc_cleanup(&tmp, NULL, val, response); } } add_assoc_zval(return_value, param->paramName, &tmp); @@ -353,7 +375,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio zval tmp; zval *arr; - master_to_zval(&tmp, NULL, val); + master_to_zval_with_doc_cleanup(&tmp, NULL, val, response); if (val->name) { if ((arr = zend_hash_str_find(Z_ARRVAL_P(return_value), (char*)val->name, strlen((char*)val->name))) != NULL) { add_next_index_zval(arr, &tmp); @@ -418,7 +440,7 @@ bool parse_packet_soap(zval *this_ptr, char *buffer, int buffer_size, sdlFunctio } smart_str_free(&key); } - master_to_zval(&val, enc, trav); + master_to_zval_with_doc_cleanup(&val, enc, trav, response); add_assoc_zval(soap_headers, (char*)trav->name, &val); } trav = trav->next; diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c index 73d6691af262..d97a0eac05b5 100644 --- a/ext/soap/php_schema.c +++ b/ext/soap/php_schema.c @@ -53,6 +53,28 @@ static bool node_is_equal_xsd(xmlNodePtr node, const char *name) return node_is_equal_ex_one_of(node, name, ns); } +static int schema_parse_int(const xmlChar *value, const char *name, bool allow_negative) +{ + const char *str = (const char *) value; + zend_long lval = 0; + int oflow_info = 0; + uint8_t type = is_numeric_string_ex(str, strlen(str), &lval, NULL, true, &oflow_info, NULL); + + if (type != IS_LONG) { + errno = 0; + lval = ZEND_STRTOL(str, NULL, 10); + if (oflow_info || (errno == ERANGE && lval != 0)) { + soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name); + } + } + + if (ZEND_LONG_EXCEEDS_INT(lval) || (!allow_negative && lval < 0)) { + soap_error1(E_ERROR, "Parsing Schema: %s value is out of range", name); + } + + return (int) lval; +} + static encodePtr create_encoder(sdlPtr sdl, sdlTypePtr cur_type, const xmlChar *ns, const xmlChar *type) { smart_str nscat = {0}; @@ -854,7 +876,7 @@ static int schema_restriction_var_int(xmlNodePtr val, sdlRestrictionIntPtr *valp soap_error0(E_ERROR, "Parsing Schema: missing restriction value"); } - (*valptr)->value = atoi((char*)value->children->content); + (*valptr)->value = schema_parse_int(value->children->content, (const char *) val->name, true); return TRUE; } @@ -1016,7 +1038,7 @@ void schema_min_max(xmlNodePtr node, sdlContentModelPtr model) xmlAttrPtr attr = get_attribute(node->properties, "minOccurs"); if (attr) { - model->min_occurs = atoi((char*)attr->children->content); + model->min_occurs = schema_parse_int(attr->children->content, "minOccurs", false); } else { model->min_occurs = 1; } @@ -1026,7 +1048,7 @@ void schema_min_max(xmlNodePtr node, sdlContentModelPtr model) if (!strncmp((char*)attr->children->content, "unbounded", sizeof("unbounded"))) { model->max_occurs = -1; } else { - model->max_occurs = atoi((char*)attr->children->content); + model->max_occurs = schema_parse_int(attr->children->content, "maxOccurs", false); } } else { model->max_occurs = 1; diff --git a/ext/soap/php_soap.h b/ext/soap/php_soap.h index ebae8ca58a79..58dbdfed6c05 100644 --- a/ext/soap/php_soap.h +++ b/ext/soap/php_soap.h @@ -258,10 +258,7 @@ typedef struct soap_url_object { zend_object std; } soap_url_object; -static inline soap_url_object *soap_url_object_fetch(zend_object *obj) -{ - return (soap_url_object *) ((char *) obj - XtOffsetOf(soap_url_object, std)); -} +#define soap_url_object_fetch(obj) ZEND_CONTAINER_OF(obj, soap_url_object, std) #define Z_SOAP_URL_P(zv) soap_url_object_fetch(Z_OBJ_P(zv)) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 0c8e7070b359..8a103e09636d 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -205,13 +205,8 @@ typedef struct { zend_object std; } soap_client_object; -static inline soap_client_object *soap_client_object_fetch(zend_object *obj) { - return (soap_client_object *) ((char *) obj - XtOffsetOf(soap_client_object, std)); -} - -static inline soap_server_object *soap_server_object_fetch(zend_object *obj) { - return (soap_server_object *) ((char *) obj - XtOffsetOf(soap_server_object, std)); -} +#define soap_client_object_fetch(obj) ZEND_CONTAINER_OF(obj, soap_client_object, std) +#define soap_server_object_fetch(obj) ZEND_CONTAINER_OF(obj, soap_server_object, std) static zend_object *soap_client_object_create(zend_class_entry *ce) { @@ -286,10 +281,7 @@ static zend_result soap_url_cast_object(zend_object *obj, zval *result, int type return zend_std_cast_object_tostring(obj, result, type); } -static inline soap_sdl_object *soap_sdl_object_fetch(zend_object *obj) -{ - return (soap_sdl_object *) ((char *) obj - XtOffsetOf(soap_sdl_object, std)); -} +#define soap_sdl_object_fetch(obj) ZEND_CONTAINER_OF(obj, soap_sdl_object, std) #define Z_SOAP_SDL_P(zv) soap_sdl_object_fetch(Z_OBJ_P(zv)) @@ -532,7 +524,7 @@ PHP_MINIT_FUNCTION(soap) soap_class_entry->default_object_handlers = &soap_client_object_handlers; memcpy(&soap_client_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - soap_client_object_handlers.offset = XtOffsetOf(soap_client_object, std); + soap_client_object_handlers.offset = offsetof(soap_client_object, std); soap_client_object_handlers.free_obj = soap_client_object_free; soap_client_object_handlers.clone_obj = NULL; @@ -545,7 +537,7 @@ PHP_MINIT_FUNCTION(soap) soap_server_class_entry->default_object_handlers = &soap_server_object_handlers; memcpy(&soap_server_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - soap_server_object_handlers.offset = XtOffsetOf(soap_server_object, std); + soap_server_object_handlers.offset = offsetof(soap_server_object, std); soap_server_object_handlers.free_obj = soap_server_object_free; soap_server_object_handlers.clone_obj = NULL; @@ -562,7 +554,7 @@ PHP_MINIT_FUNCTION(soap) soap_url_class_entry->default_object_handlers = &soap_url_object_handlers; memcpy(&soap_url_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - soap_url_object_handlers.offset = XtOffsetOf(soap_url_object, std); + soap_url_object_handlers.offset = offsetof(soap_url_object, std); soap_url_object_handlers.free_obj = soap_url_object_free; soap_url_object_handlers.get_constructor = soap_url_object_get_constructor; soap_url_object_handlers.clone_obj = NULL; @@ -574,7 +566,7 @@ PHP_MINIT_FUNCTION(soap) soap_sdl_class_entry->default_object_handlers = &soap_sdl_object_handlers; memcpy(&soap_sdl_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - soap_sdl_object_handlers.offset = XtOffsetOf(soap_sdl_object, std); + soap_sdl_object_handlers.offset = offsetof(soap_sdl_object, std); soap_sdl_object_handlers.free_obj = soap_sdl_object_free; soap_sdl_object_handlers.get_constructor = soap_sdl_object_get_constructor; soap_sdl_object_handlers.clone_obj = NULL; @@ -1421,10 +1413,10 @@ PHP_METHOD(SoapServer, handle) return; } } - } - if ((soap_action_z = zend_hash_str_find(Z_ARRVAL_P(server_vars), ZEND_STRL("HTTP_SOAPACTION"))) != NULL && Z_TYPE_P(soap_action_z) == IS_STRING) { - soap_action = Z_STRVAL_P(soap_action_z); + if ((soap_action_z = zend_hash_str_find(Z_ARRVAL_P(server_vars), ZEND_STRL("HTTP_SOAPACTION"))) != NULL && Z_TYPE_P(soap_action_z) == IS_STRING) { + soap_action = Z_STRVAL_P(soap_action_z); + } } doc_request = soap_xmlParseFile("php://input"); @@ -1592,12 +1584,20 @@ PHP_METHOD(SoapServer, handle) instanceof_function(Z_OBJCE(h->retval), soap_fault_class_entry)) { php_output_discard(); soap_server_fault_ex(function, &h->retval, h); - if (service->type == SOAP_CLASS && soap_obj) {zval_ptr_dtor(soap_obj);} + if (service->type == SOAP_CLASS && soap_obj) { + if (service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) { + zval_ptr_dtor(soap_obj); + } + } goto fail; } else if (EG(exception)) { php_output_discard(); _soap_server_exception(service, function, ZEND_THIS); - if (service->type == SOAP_CLASS && soap_obj) {zval_ptr_dtor(soap_obj);} + if (service->type == SOAP_CLASS && soap_obj) { + if (service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) { + zval_ptr_dtor(soap_obj); + } + } goto fail; } } else if (h->mustUnderstand) { @@ -2441,9 +2441,19 @@ static void do_soap_call(zend_execute_data *execute_data, request = NULL; if (ret && Z_TYPE(response) == IS_STRING) { + bool parse_bailout = false; + encode_reset_ns(); - ret = parse_packet_soap(this_ptr, Z_STRVAL(response), Z_STRLEN(response), fn, NULL, return_value, output_headers); + zend_try { + ret = parse_packet_soap(this_ptr, Z_STRVAL(response), Z_STRLEN(response), fn, NULL, return_value, output_headers); + } zend_catch { + parse_bailout = true; + } zend_end_try(); encode_finish(); + if (parse_bailout) { + zval_ptr_dtor(&response); + zend_bailout(); + } } zval_ptr_dtor(&response); @@ -2485,9 +2495,19 @@ static void do_soap_call(zend_execute_data *execute_data, request = NULL; if (ret && Z_TYPE(response) == IS_STRING) { + bool parse_bailout = false; + encode_reset_ns(); - ret = parse_packet_soap(this_ptr, Z_STRVAL(response), Z_STRLEN(response), NULL, NULL, return_value, output_headers); + zend_try { + ret = parse_packet_soap(this_ptr, Z_STRVAL(response), Z_STRLEN(response), NULL, NULL, return_value, output_headers); + } zend_catch { + parse_bailout = true; + } zend_end_try(); encode_finish(); + if (parse_bailout) { + zval_ptr_dtor(&response); + zend_bailout(); + } } zval_ptr_dtor(&response); diff --git a/ext/soap/tests/GHSA-85c2-q967-79q5.phpt b/ext/soap/tests/GHSA-85c2-q967-79q5.phpt new file mode 100644 index 000000000000..8bcac26ad187 --- /dev/null +++ b/ext/soap/tests/GHSA-85c2-q967-79q5.phpt @@ -0,0 +1,61 @@ +--TEST-- +GHSA-85c2-q967-79q5: Stale SOAP_GLOBAL(ref_map) pointer with Apache Map +--CREDITS-- +brettgervasoni +--EXTENSIONS-- +soap +--FILE-- + + + + + + + + foo + bar + + + foo + baz + + + + + + +XML; + +$s = new SoapServer(null, ['uri' => 'urn:a']); +$s->setClass(Handler::class); +$s->handle($envelope); +var_dump($result); + +?> +--EXPECTF-- + + +array(2) { + [0]=> + array(1) { + ["foo"]=> + string(3) "baz" + } + [1]=> + object(stdClass)#%d (1) { + ["object"]=> + string(3) "bar" + } +} diff --git a/ext/soap/tests/GHSA-hmxp-6pc4-f3vv.phpt b/ext/soap/tests/GHSA-hmxp-6pc4-f3vv.phpt new file mode 100644 index 000000000000..e46ab2e4607d --- /dev/null +++ b/ext/soap/tests/GHSA-hmxp-6pc4-f3vv.phpt @@ -0,0 +1,39 @@ +--TEST-- +GHSA-hmxp-6pc4-f3vv: Null pointer dereference on missing Apache map value +--CREDITS-- +Ilia Alshanetsky (iliaal) +--EXTENSIONS-- +soap +--FILE-- + + + + + + + hello + + + + +XML; + +$server = new SoapServer(null, [ + 'uri' => 'urn:test', + 'typemap' => [['type_name' => 'anything']], +]); +$server->addFunction('test'); +function test($m) { return null; } +$server->handle($request); + +?> +--EXPECT-- + +SOAP-ENV:ServerSOAP-ERROR: Encoding: Can't decode apache map, missing value diff --git a/ext/soap/tests/GHSA-m33r-qmcv-p97q.phpt b/ext/soap/tests/GHSA-m33r-qmcv-p97q.phpt new file mode 100644 index 000000000000..bcf441ccd18a --- /dev/null +++ b/ext/soap/tests/GHSA-m33r-qmcv-p97q.phpt @@ -0,0 +1,58 @@ +--TEST-- +GHSA-m33r-qmcv-p97q: Use-after-free after header parsing failure with SOAP_PERSISTENCE_SESSION +--CREDITS-- +Ilia Alshanetsky (iliaal) +--EXTENSIONS-- +soap +session +--FILE-- + 'urn:a']); +$srv->setClass(Handler::class); +$srv->setPersistence(SOAP_PERSISTENCE_SESSION); + +$srv->handle(<< + + + + + + + + +XML); + +$srv->handle(<< + + + + + + + + +XML); + +?> +--EXPECT-- + +SOAP-ENV:Serverdenied + +SOAP-ENV:Serverdenied diff --git a/ext/soap/tests/bugs/gh22167.phpt b/ext/soap/tests/bugs/gh22167.phpt new file mode 100644 index 000000000000..f24bfb0eac32 --- /dev/null +++ b/ext/soap/tests/bugs/gh22167.phpt @@ -0,0 +1,128 @@ +--TEST-- +GH-22167 (Out-of-range XML Schema integer values in SOAP WSDL) +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=0 +--FILE-- + + + + + $schema + + + + + + + + + + + + + + + +XML; +} + +function occurrence_schema(string $attribute, string $value = "2147483648"): string { + return << + + + + +XML; +} + +function restriction_schema(string $facet, string $value = "2147483648"): string { + return << + + + + +XML; +} + +$cases = [ + "minOccurs" => occurrence_schema("minOccurs"), + "maxOccurs" => occurrence_schema("maxOccurs"), + "negative minOccurs" => occurrence_schema("minOccurs", "-1"), + "negative maxOccurs" => occurrence_schema("maxOccurs", "-1"), + "minExclusive" => restriction_schema("minExclusive"), + "minInclusive" => restriction_schema("minInclusive"), + "maxExclusive" => restriction_schema("maxExclusive"), + "maxInclusive" => restriction_schema("maxInclusive"), + "totalDigits" => restriction_schema("totalDigits"), + "fractionDigits" => restriction_schema("fractionDigits"), + "length" => restriction_schema("length"), + "minLength" => restriction_schema("minLength"), + "maxLength" => restriction_schema("maxLength"), +]; + +$numeric_string_cases = [ + "leading whitespace numeric-string" => " 2147483648", + "leading plus numeric-string" => "+2147483648", + "leading zero numeric-string" => "00000000002147483648", + "leading numeric-string with trailing data" => "2147483648abc", + "negative out-of-range numeric-string" => "-2147483649", + "decimal numeric-string" => "2147483648.0", + "exponent numeric-string" => "2147483648e0", +]; + +foreach ($numeric_string_cases as $name => $value) { + $cases[$name] = occurrence_schema("maxOccurs", $value); +} + +$cases["fractional numeric-string within int range"] = occurrence_schema("maxOccurs", "3.141"); + +foreach ($cases as $name => $schema) { + $file = tempnam(sys_get_temp_dir(), "wsdl"); + file_put_contents($file, wsdl_with_schema($schema)); + + try { + new SoapClient($file, ["cache_wsdl" => WSDL_CACHE_NONE]); + echo "$name: parsed\n"; + } catch (SoapFault $e) { + echo "$name: {$e->getMessage()}\n"; + } finally { + unlink($file); + } +} +?> +--EXPECT-- +minOccurs: SOAP-ERROR: Parsing Schema: minOccurs value is out of range +maxOccurs: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range +negative minOccurs: SOAP-ERROR: Parsing Schema: minOccurs value is out of range +negative maxOccurs: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range +minExclusive: SOAP-ERROR: Parsing Schema: minExclusive value is out of range +minInclusive: SOAP-ERROR: Parsing Schema: minInclusive value is out of range +maxExclusive: SOAP-ERROR: Parsing Schema: maxExclusive value is out of range +maxInclusive: SOAP-ERROR: Parsing Schema: maxInclusive value is out of range +totalDigits: SOAP-ERROR: Parsing Schema: totalDigits value is out of range +fractionDigits: SOAP-ERROR: Parsing Schema: fractionDigits value is out of range +length: SOAP-ERROR: Parsing Schema: length value is out of range +minLength: SOAP-ERROR: Parsing Schema: minLength value is out of range +maxLength: SOAP-ERROR: Parsing Schema: maxLength value is out of range +leading whitespace numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range +leading plus numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range +leading zero numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range +leading numeric-string with trailing data: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range +negative out-of-range numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range +decimal numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range +exponent numeric-string: SOAP-ERROR: Parsing Schema: maxOccurs value is out of range +fractional numeric-string within int range: parsed diff --git a/ext/soap/tests/gh22218.phpt b/ext/soap/tests/gh22218.phpt new file mode 100644 index 000000000000..5b0714037a0a --- /dev/null +++ b/ext/soap/tests/gh22218.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-22218 (SoapServer::handle() segfault on non-array/unset $_SERVER) +--EXTENSIONS-- +soap +--CREDITS-- +Rex-Reynolds +--SKIPIF-- + +--POST-- + + + + + +--FILE-- + 'http://test-uri']); +$server->handle(); +?> +--EXPECTF-- +%AFunction 'test' doesn't exist%A diff --git a/ext/soap/tests/soap_array_index_overflow.phpt b/ext/soap/tests/soap_array_index_overflow.phpt new file mode 100644 index 000000000000..ae481ee317ff --- /dev/null +++ b/ext/soap/tests/soap_array_index_overflow.phpt @@ -0,0 +1,89 @@ +--TEST-- +SOAP array index overflow is rejected +--EXTENSIONS-- +soap +--FILE-- +response; + } +} + +function soap_response(string $attributes, string $itemAttributes = ''): string { + return << + + + + + value + + + + +XML; +} + +function test_overflow(string $name, string $response): void { + $client = new TestSoapClient(NULL, [ + 'location' => 'test://', + 'uri' => 'http://example.org/', + 'exceptions' => true, + ]); + $client->response = $response; + + try { + $client->test(); + echo "$name: no fault\n"; + } catch (SoapFault $e) { + echo "$name: $e->faultstring\n"; + } +} + +function test_boundary_position(): void { + $client = new TestSoapClient(NULL, [ + 'location' => 'test://', + 'uri' => 'http://example.org/', + 'exceptions' => true, + ]); + $client->response = soap_response( + 'SOAP-ENC:arrayType="xsd:string[1]" xsi:type="SOAP-ENC:Array"', + 'SOAP-ENC:position="[2147483646]"' + ); + + var_dump($client->test()); +} + +test_overflow( + 'arrayType', + soap_response('SOAP-ENC:arrayType="xsd:string[2147483648]" xsi:type="SOAP-ENC:Array"') +); + +test_overflow( + 'offset', + soap_response('SOAP-ENC:arrayType="xsd:string[1]" SOAP-ENC:offset="[2147483648]" xsi:type="SOAP-ENC:Array"') +); + +test_overflow( + 'position', + soap_response('SOAP-ENC:arrayType="xsd:string[1]" xsi:type="SOAP-ENC:Array"', 'SOAP-ENC:position="[2147483647]"') +); + +test_boundary_position(); +?> +--EXPECT-- +arrayType: SOAP-ERROR: Encoding: array index out of range +offset: SOAP-ERROR: Encoding: array index out of range +position: SOAP-ERROR: Encoding: array index out of range +array(1) { + [2147483646]=> + string(5) "value" +} diff --git a/ext/sockets/php_sockets.h b/ext/sockets/php_sockets.h index 437d35b41032..4d047885dbaa 100644 --- a/ext/sockets/php_sockets.h +++ b/ext/sockets/php_sockets.h @@ -75,9 +75,7 @@ typedef struct { extern PHP_SOCKETS_API zend_class_entry *socket_ce; -static inline php_socket *socket_from_obj(zend_object *obj) { - return (php_socket *)((char *)(obj) - XtOffsetOf(php_socket, std)); -} +#define socket_from_obj(obj) ZEND_CONTAINER_OF(obj, php_socket, std) #define Z_SOCKET_P(zv) socket_from_obj(Z_OBJ_P(zv)) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index fa84a7877ad6..7b9b903585b8 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -180,9 +180,7 @@ typedef struct { zend_class_entry *address_info_ce; static zend_object_handlers address_info_object_handlers; -static inline php_addrinfo *address_info_from_obj(zend_object *obj) { - return (php_addrinfo *)((char *)(obj) - XtOffsetOf(php_addrinfo, std)); -} +#define address_info_from_obj(obj) ZEND_CONTAINER_OF(obj, php_addrinfo, std) #define Z_ADDRESS_INFO_P(zv) address_info_from_obj(Z_OBJ_P(zv)) @@ -484,7 +482,7 @@ static PHP_MINIT_FUNCTION(sockets) socket_ce->default_object_handlers = &socket_object_handlers; memcpy(&socket_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - socket_object_handlers.offset = XtOffsetOf(php_socket, std); + socket_object_handlers.offset = offsetof(php_socket, std); socket_object_handlers.free_obj = socket_free_obj; socket_object_handlers.get_constructor = socket_get_constructor; socket_object_handlers.clone_obj = NULL; @@ -496,7 +494,7 @@ static PHP_MINIT_FUNCTION(sockets) address_info_ce->default_object_handlers = &address_info_object_handlers; memcpy(&address_info_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - address_info_object_handlers.offset = XtOffsetOf(php_addrinfo, std); + address_info_object_handlers.offset = offsetof(php_addrinfo, std); address_info_object_handlers.free_obj = address_info_free_obj; address_info_object_handlers.get_constructor = address_info_get_constructor; address_info_object_handlers.clone_obj = NULL; @@ -852,18 +850,17 @@ PHP_FUNCTION(socket_listen) PHP_FUNCTION(socket_close) { zval *arg1; - php_socket *php_socket; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJECT_OF_CLASS(arg1, socket_ce) ZEND_PARSE_PARAMETERS_END(); - php_socket = Z_SOCKET_P(arg1); - ENSURE_SOCKET_VALID(php_socket); + php_socket *socket = Z_SOCKET_P(arg1); + ENSURE_SOCKET_VALID(socket); - if (!Z_ISUNDEF(php_socket->zstream)) { + if (!Z_ISUNDEF(socket->zstream)) { php_stream *stream = NULL; - php_stream_from_zval_no_verify(stream, &php_socket->zstream); + php_stream_from_zval_no_verify(stream, &socket->zstream); if (stream != NULL) { /* close & destroy stream, incl. removing it from the rsrc list; * resource stored in php_sock->zstream will become invalid */ @@ -872,13 +869,13 @@ PHP_FUNCTION(socket_close) (stream->is_persistent?PHP_STREAM_FREE_CLOSE_PERSISTENT:0)); } } else { - if (!IS_INVALID_SOCKET(php_socket)) { - close(php_socket->bsd_socket); + if (!IS_INVALID_SOCKET(socket)) { + close(socket->bsd_socket); } } - ZVAL_UNDEF(&php_socket->zstream); - php_socket->bsd_socket = -1; + ZVAL_UNDEF(&socket->zstream); + socket->bsd_socket = -1; } /* }}} */ @@ -1272,7 +1269,7 @@ PHP_FUNCTION(socket_connect) s_un.sun_family = AF_UNIX; memcpy(&s_un.sun_path, ZSTR_VAL(addr), ZSTR_LEN(addr)); retval = connect(php_sock->bsd_socket, (struct sockaddr *) &s_un, - (socklen_t)(XtOffsetOf(struct sockaddr_un, sun_path) + ZSTR_LEN(addr))); + (socklen_t)(offsetof(struct sockaddr_un, sun_path) + ZSTR_LEN(addr))); break; } @@ -1649,6 +1646,7 @@ PHP_FUNCTION(socket_recvfrom) */ #endif default: + zend_string_efree(recv_buf); zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); RETURN_THROWS(); } diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 737260ad6f0d..a3df888a6102 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -51,10 +51,7 @@ typedef struct _spl_array_object { zend_object std; } spl_array_object; -static inline spl_array_object *spl_array_from_obj(zend_object *obj) /* {{{ */ { - return (spl_array_object*)((char*)(obj) - XtOffsetOf(spl_array_object, std)); -} -/* }}} */ +#define spl_array_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_array_object, std) #define Z_SPLARRAY_P(zv) spl_array_from_obj(Z_OBJ_P((zv))) @@ -1482,9 +1479,9 @@ PHP_METHOD(ArrayObject, __unserialize) RETURN_THROWS(); } - if (!instanceof_function(ce, zend_ce_iterator)) { + if (!instanceof_function(ce, spl_ce_ArrayIterator)) { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, - "Cannot deserialize ArrayObject with iterator class '%s'; this class does not implement the Iterator interface", + "Cannot deserialize ArrayObject with iterator class '%s'; this class is not derived from ArrayIterator", ZSTR_VAL(Z_STR_P(iterator_class_zv))); RETURN_THROWS(); } @@ -1847,7 +1844,7 @@ PHP_MINIT_FUNCTION(spl_array) memcpy(&spl_handler_ArrayObject, &std_object_handlers, sizeof(zend_object_handlers)); - spl_handler_ArrayObject.offset = XtOffsetOf(spl_array_object, std); + spl_handler_ArrayObject.offset = offsetof(spl_array_object, std); spl_handler_ArrayObject.clone_obj = spl_array_object_clone; spl_handler_ArrayObject.read_dimension = spl_array_read_dimension; diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 89af25dd9d35..f662bb8a1dbd 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -48,11 +48,7 @@ PHPAPI zend_class_entry *spl_ce_GlobIterator; PHPAPI zend_class_entry *spl_ce_SplFileObject; PHPAPI zend_class_entry *spl_ce_SplTempFileObject; -/* Object helper */ -static inline spl_filesystem_object *spl_filesystem_from_obj(zend_object *obj) /* {{{ */ { - return (spl_filesystem_object*)((char*)(obj) - XtOffsetOf(spl_filesystem_object, std)); -} -/* }}} */ +#define spl_filesystem_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_filesystem_object, std) /* define an overloaded iterator structure */ typedef struct { @@ -188,8 +184,8 @@ static zend_object *spl_filesystem_object_new(zend_class_entry *class_type) intern = emalloc(sizeof(spl_filesystem_object) + zend_object_properties_size(class_type)); /* Avoid initializing the entirety of spl_filesystem_object.u.dir.entry. */ memset(intern, 0, - MAX(XtOffsetOf(spl_filesystem_object, u.dir.entry), - XtOffsetOf(spl_filesystem_object, u.file.escape) + sizeof(int))); + MAX(offsetof(spl_filesystem_object, u.dir.entry), + offsetof(spl_filesystem_object, u.file.escape) + sizeof(int))); /* intern->type = SPL_FS_INFO; done by set 0 */ intern->file_class = spl_ce_SplFileObject; intern->info_class = spl_ce_SplFileInfo; @@ -2682,7 +2678,7 @@ PHP_MINIT_FUNCTION(spl_directory) spl_ce_SplFileInfo->default_object_handlers = &spl_filesystem_object_handlers; memcpy(&spl_filesystem_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - spl_filesystem_object_handlers.offset = XtOffsetOf(spl_filesystem_object, std); + spl_filesystem_object_handlers.offset = offsetof(spl_filesystem_object, std); spl_filesystem_object_handlers.clone_obj = spl_filesystem_object_clone; spl_filesystem_object_handlers.dtor_obj = spl_filesystem_object_destroy_object; spl_filesystem_object_handlers.free_obj = spl_filesystem_object_free_storage; diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index a4d89dc87bcf..77e8b0d4e7da 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -85,10 +85,7 @@ struct _spl_dllist_it { int flags; }; -static inline spl_dllist_object *spl_dllist_from_obj(zend_object *obj) /* {{{ */ { - return (spl_dllist_object*)((char*)(obj) - XtOffsetOf(spl_dllist_object, std)); -} -/* }}} */ +#define spl_dllist_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_dllist_object, std) #define Z_SPLDLLIST_P(zv) spl_dllist_from_obj(Z_OBJ_P((zv))) @@ -798,6 +795,7 @@ static void spl_dllist_it_helper_move_forward(spl_ptr_llist_element **traverse_p if (flags & SPL_DLLIST_IT_LIFO) { *traverse_pointer_ptr = old->prev; + SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr); (*traverse_position_ptr)--; if (flags & SPL_DLLIST_IT_DELETE) { @@ -808,6 +806,7 @@ static void spl_dllist_it_helper_move_forward(spl_ptr_llist_element **traverse_p } } else { *traverse_pointer_ptr = old->next; + SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr); if (flags & SPL_DLLIST_IT_DELETE) { zval prev; @@ -820,7 +819,6 @@ static void spl_dllist_it_helper_move_forward(spl_ptr_llist_element **traverse_p } SPL_LLIST_DELREF(old); - SPL_LLIST_CHECK_ADDREF(*traverse_pointer_ptr); } } /* }}} */ @@ -1207,7 +1205,7 @@ PHP_MINIT_FUNCTION(spl_dllist) /* {{{ */ memcpy(&spl_handler_SplDoublyLinkedList, &std_object_handlers, sizeof(zend_object_handlers)); - spl_handler_SplDoublyLinkedList.offset = XtOffsetOf(spl_dllist_object, std); + spl_handler_SplDoublyLinkedList.offset = offsetof(spl_dllist_object, std); spl_handler_SplDoublyLinkedList.clone_obj = spl_dllist_object_clone; spl_handler_SplDoublyLinkedList.count_elements = spl_dllist_object_count_elements; spl_handler_SplDoublyLinkedList.get_gc = spl_dllist_object_get_gc; diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index 82e09702219a..d2eae52e3c0c 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -53,10 +53,7 @@ typedef struct _spl_fixedarray_it { zend_long current; } spl_fixedarray_it; -static spl_fixedarray_object *spl_fixed_array_from_obj(zend_object *obj) -{ - return (spl_fixedarray_object*)((char*)(obj) - XtOffsetOf(spl_fixedarray_object, std)); -} +#define spl_fixed_array_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_fixedarray_object, std) #define Z_SPLFIXEDARRAY_P(zv) spl_fixed_array_from_obj(Z_OBJ_P((zv))) @@ -170,18 +167,18 @@ static void spl_fixedarray_resize(spl_fixedarray *array, zend_long size) return; } - /* first initialization */ - if (array->size == 0) { - spl_fixedarray_init(array, size); - return; - } - if (UNEXPECTED(array->cached_resize >= 0)) { /* We're already resizing, so just remember the desired size. * The resize will happen later. */ array->cached_resize = size; return; } + /* first initialization */ + if (array->size == 0) { + spl_fixedarray_init(array, size); + return; + } + array->cached_resize = size; /* clearing the array */ @@ -952,7 +949,7 @@ PHP_MINIT_FUNCTION(spl_fixedarray) memcpy(&spl_handler_SplFixedArray, &std_object_handlers, sizeof(zend_object_handlers)); - spl_handler_SplFixedArray.offset = XtOffsetOf(spl_fixedarray_object, std); + spl_handler_SplFixedArray.offset = offsetof(spl_fixedarray_object, std); spl_handler_SplFixedArray.clone_obj = spl_fixedarray_object_clone; spl_handler_SplFixedArray.read_dimension = spl_fixedarray_object_read_dimension; spl_handler_SplFixedArray.write_dimension = spl_fixedarray_object_write_dimension; diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index 642fe4b95f05..1073836aa53b 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -70,10 +70,7 @@ typedef struct _spl_pqueue_elem { zval priority; } spl_pqueue_elem; -static inline spl_heap_object *spl_heap_from_obj(zend_object *obj) /* {{{ */ { - return (spl_heap_object*)((char*)(obj) - XtOffsetOf(spl_heap_object, std)); -} -/* }}} */ +#define spl_heap_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_heap_object, std) #define Z_SPLHEAP_P(zv) spl_heap_from_obj(Z_OBJ_P((zv))) @@ -1332,7 +1329,7 @@ PHP_MINIT_FUNCTION(spl_heap) /* {{{ */ memcpy(&spl_handler_SplHeap, &std_object_handlers, sizeof(zend_object_handlers)); - spl_handler_SplHeap.offset = XtOffsetOf(spl_heap_object, std); + spl_handler_SplHeap.offset = offsetof(spl_heap_object, std); spl_handler_SplHeap.clone_obj = spl_heap_object_clone; spl_handler_SplHeap.count_elements = spl_heap_object_count_elements; spl_handler_SplHeap.get_gc = spl_heap_object_get_gc; @@ -1353,7 +1350,7 @@ PHP_MINIT_FUNCTION(spl_heap) /* {{{ */ memcpy(&spl_handler_SplPriorityQueue, &std_object_handlers, sizeof(zend_object_handlers)); - spl_handler_SplPriorityQueue.offset = XtOffsetOf(spl_heap_object, std); + spl_handler_SplPriorityQueue.offset = offsetof(spl_heap_object, std); spl_handler_SplPriorityQueue.clone_obj = spl_heap_object_clone; spl_handler_SplPriorityQueue.count_elements = spl_heap_object_count_elements; spl_handler_SplPriorityQueue.get_gc = spl_pqueue_object_get_gc; diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index c77aa9c1cf4b..86652ae4f5cc 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -136,16 +136,11 @@ typedef struct _spl_dual_it_object { static zend_object_handlers spl_handlers_rec_it_it; static zend_object_handlers spl_handlers_dual_it; -static inline spl_recursive_it_object *spl_recursive_it_from_obj(zend_object *obj) /* {{{ */ { - return (spl_recursive_it_object*)((char*)(obj) - XtOffsetOf(spl_recursive_it_object, std)); -} -/* }}} */ +#define spl_recursive_it_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_recursive_it_object, std) #define Z_SPLRECURSIVE_IT_P(zv) spl_recursive_it_from_obj(Z_OBJ_P((zv))) -static inline spl_dual_it_object *spl_dual_it_from_obj(zend_object *obj) /* {{{ */ { - return (spl_dual_it_object*)((char*)(obj) - XtOffsetOf(spl_dual_it_object, std)); -} /* }}} */ +#define spl_dual_it_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_dual_it_object, std) #define Z_SPLDUAL_IT_P(zv) spl_dual_it_from_obj(Z_OBJ_P((zv))) @@ -3134,14 +3129,14 @@ PHP_MINIT_FUNCTION(spl_iterators) spl_ce_RecursiveIteratorIterator->get_iterator = spl_recursive_it_get_iterator; memcpy(&spl_handlers_rec_it_it, &std_object_handlers, sizeof(zend_object_handlers)); - spl_handlers_rec_it_it.offset = XtOffsetOf(spl_recursive_it_object, std); + spl_handlers_rec_it_it.offset = offsetof(spl_recursive_it_object, std); spl_handlers_rec_it_it.get_method = spl_recursive_it_get_method; spl_handlers_rec_it_it.clone_obj = NULL; spl_handlers_rec_it_it.free_obj = spl_RecursiveIteratorIterator_free_storage; spl_handlers_rec_it_it.get_gc = spl_RecursiveIteratorIterator_get_gc; memcpy(&spl_handlers_dual_it, &std_object_handlers, sizeof(zend_object_handlers)); - spl_handlers_dual_it.offset = XtOffsetOf(spl_dual_it_object, std); + spl_handlers_dual_it.offset = offsetof(spl_dual_it_object, std); spl_handlers_dual_it.get_method = spl_dual_it_get_method; spl_handlers_dual_it.clone_obj = NULL; spl_handlers_dual_it.free_obj = spl_dual_it_free_storage; diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index 6e9ea0e1ba99..f897ab1350cc 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -45,6 +45,8 @@ static zend_object_handlers spl_handler_MultipleIterator; #define SOS_OVERRIDDEN_WRITE_DIMENSION 2 #define SOS_OVERRIDDEN_UNSET_DIMENSION 4 +ZEND_TLS uint32_t spl_object_storage_get_hash_depth; + typedef struct _spl_SplObjectStorage { /* {{{ */ HashTable storage; zend_long index; @@ -62,13 +64,20 @@ typedef struct _spl_SplObjectStorageElement { zval inf; } spl_SplObjectStorageElement; /* }}} */ -static inline spl_SplObjectStorage *spl_object_storage_from_obj(zend_object *obj) /* {{{ */ { - return (spl_SplObjectStorage*)((char*)(obj) - XtOffsetOf(spl_SplObjectStorage, std)); -} -/* }}} */ +#define spl_object_storage_from_obj(obj) ZEND_CONTAINER_OF(obj, spl_SplObjectStorage, std) #define Z_SPLOBJSTORAGE_P(zv) spl_object_storage_from_obj(Z_OBJ_P((zv))) +static zend_always_inline bool spl_object_storage_is_mutating_within_get_hash_call(void) +{ + if (UNEXPECTED(spl_object_storage_get_hash_depth)) { + zend_throw_error(NULL, "Modification of SplObjectStorage during getHash() is prohibited"); + return true; + } + + return false; +} + static void spl_SplObjectStorage_free_storage(zend_object *object) /* {{{ */ { spl_SplObjectStorage *intern = spl_object_storage_from_obj(object); @@ -83,7 +92,10 @@ static zend_result spl_object_storage_get_hash(zend_hash_key *key, spl_SplObject zval param; zval rv; ZVAL_OBJ(¶m, obj); + ZVAL_UNDEF(&rv); + spl_object_storage_get_hash_depth++; zend_call_method_with_1_params(&intern->std, intern->std.ce, &intern->fptr_get_hash, "getHash", &rv, ¶m); + spl_object_storage_get_hash_depth--; if (UNEXPECTED(Z_ISUNDEF(rv))) { /* An exception has occurred */ return FAILURE; @@ -176,6 +188,10 @@ static spl_SplObjectStorageElement *spl_object_storage_attach_handle(spl_SplObje static spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *intern, zend_object *obj, zval *inf) /* {{{ */ { + if (UNEXPECTED(spl_object_storage_is_mutating_within_get_hash_call())) { + return NULL; + } + if (EXPECTED(!(intern->flags & SOS_OVERRIDDEN_WRITE_DIMENSION))) { return spl_object_storage_attach_handle(intern, obj, inf); } @@ -221,6 +237,10 @@ static spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStora static zend_result spl_object_storage_detach(spl_SplObjectStorage *intern, zend_object *obj) /* {{{ */ { + if (UNEXPECTED(spl_object_storage_is_mutating_within_get_hash_call())) { + return FAILURE; + } + if (EXPECTED(!(intern->flags & SOS_OVERRIDDEN_UNSET_DIMENSION))) { return zend_hash_index_del(&intern->storage, obj->handle); } @@ -247,7 +267,7 @@ static zend_result spl_object_storage_detach(spl_SplObjectStorage *intern, zend_ if (UNEXPECTED(Z_ISUNDEF_P(_z))) continue; \ _ptr = Z_PTR_P(_z); -static void spl_object_storage_addall(spl_SplObjectStorage *intern, spl_SplObjectStorage *other) { /* {{{ */ +static zend_result spl_object_storage_addall(spl_SplObjectStorage *intern, spl_SplObjectStorage *other) { /* {{{ */ spl_SplObjectStorageElement *element; SPL_SAFE_HASH_FOREACH_PTR(&other->storage, element) { @@ -255,12 +275,16 @@ static void spl_object_storage_addall(spl_SplObjectStorage *intern, spl_SplObjec zend_object *obj = element->obj; GC_ADDREF(obj); ZVAL_COPY(&zv, &element->inf); - spl_object_storage_attach(intern, obj, &zv); + spl_SplObjectStorageElement *attached = spl_object_storage_attach(intern, obj, &zv); zval_ptr_dtor(&zv); OBJ_RELEASE(obj); + if (UNEXPECTED(!attached)) { + return FAILURE; + } } ZEND_HASH_FOREACH_END(); intern->index = 0; + return SUCCESS; } /* }}} */ #define SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, zstr_method) \ @@ -446,6 +470,9 @@ PHP_METHOD(SplObjectStorage, attach) Z_PARAM_ZVAL(inf) ZEND_PARSE_PARAMETERS_END(); spl_object_storage_attach(intern, obj, inf); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } /* }}} */ // todo: make spl_object_storage_has_dimension return bool as well @@ -494,6 +521,10 @@ static zval *spl_object_storage_read_dimension(zend_object *object, zval *offset static void spl_object_storage_write_dimension(zend_object *object, zval *offset, zval *inf) { spl_SplObjectStorage *intern = spl_object_storage_from_obj(object); + if (UNEXPECTED(spl_object_storage_is_mutating_within_get_hash_call())) { + return; + } + if (UNEXPECTED(offset == NULL || Z_TYPE_P(offset) != IS_OBJECT || (intern->flags & SOS_OVERRIDDEN_WRITE_DIMENSION))) { zend_std_write_dimension(object, offset, inf); return; @@ -504,6 +535,10 @@ static void spl_object_storage_write_dimension(zend_object *object, zval *offset static void spl_multiple_iterator_write_dimension(zend_object *object, zval *offset, zval *inf) { spl_SplObjectStorage *intern = spl_object_storage_from_obj(object); + if (UNEXPECTED(spl_object_storage_is_mutating_within_get_hash_call())) { + return; + } + if (UNEXPECTED(offset == NULL || Z_TYPE_P(offset) != IS_OBJECT || (intern->flags & SOS_OVERRIDDEN_WRITE_DIMENSION))) { zend_std_write_dimension(object, offset, inf); return; @@ -518,6 +553,10 @@ static void spl_multiple_iterator_write_dimension(zend_object *object, zval *off static void spl_object_storage_unset_dimension(zend_object *object, zval *offset) { spl_SplObjectStorage *intern = spl_object_storage_from_obj(object); + if (UNEXPECTED(spl_object_storage_is_mutating_within_get_hash_call())) { + return; + } + if (UNEXPECTED(Z_TYPE_P(offset) != IS_OBJECT || (intern->flags & SOS_OVERRIDDEN_UNSET_DIMENSION))) { zend_std_unset_dimension(object, offset); return; @@ -535,6 +574,9 @@ PHP_METHOD(SplObjectStorage, detach) Z_PARAM_OBJ(obj) ZEND_PARSE_PARAMETERS_END(); spl_object_storage_detach(intern, obj); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); intern->index = 0; @@ -566,6 +608,7 @@ PHP_METHOD(SplObjectStorage, offsetGet) ZEND_PARSE_PARAMETERS_END(); if (spl_object_storage_get_hash(&key, intern, obj) == FAILURE) { + /* This may be the old NULL fallback, or an exception thrown by getHash(). */ RETURN_NULL(); } @@ -592,7 +635,9 @@ PHP_METHOD(SplObjectStorage, addAll) other = Z_SPLOBJSTORAGE_P(obj); - spl_object_storage_addall(intern, other); + if (UNEXPECTED(spl_object_storage_addall(intern, other) == FAILURE)) { + RETURN_THROWS(); + } RETURN_LONG(zend_hash_num_elements(&intern->storage)); } /* }}} */ @@ -614,6 +659,9 @@ PHP_METHOD(SplObjectStorage, removeAll) zend_hash_internal_pointer_reset(&other->storage); while ((element = zend_hash_get_current_data_ptr(&other->storage)) != NULL) { if (spl_object_storage_detach(intern, element->obj) == FAILURE) { + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } zend_hash_move_forward(&other->storage); } } @@ -641,8 +689,19 @@ PHP_METHOD(SplObjectStorage, removeAllExcept) SPL_SAFE_HASH_FOREACH_PTR(&intern->storage, element) { zend_object *elem_obj = element->obj; GC_ADDREF(elem_obj); - if (!spl_object_storage_contains(other, elem_obj)) { - spl_object_storage_detach(intern, elem_obj); + bool contains = spl_object_storage_contains(other, elem_obj); + if (UNEXPECTED(EG(exception))) { + OBJ_RELEASE(elem_obj); + RETURN_THROWS(); + } + if (!contains) { + if (spl_object_storage_detach(intern, elem_obj) == FAILURE) { + OBJ_RELEASE(elem_obj); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } + continue; + } } OBJ_RELEASE(elem_obj); } ZEND_HASH_FOREACH_END(); @@ -663,7 +722,13 @@ PHP_METHOD(SplObjectStorage, contains) ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_OBJ(obj) ZEND_PARSE_PARAMETERS_END(); - RETURN_BOOL(spl_object_storage_contains(intern, obj)); + + bool contains = spl_object_storage_contains(intern, obj); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } + + RETURN_BOOL(contains); } /* }}} */ /* {{{ Determine number of objects in storage */ @@ -753,6 +818,9 @@ PHP_METHOD(SplObjectStorage, setInfo) if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &inf) == FAILURE) { RETURN_THROWS(); } + if (UNEXPECTED(spl_object_storage_is_mutating_within_get_hash_call())) { + RETURN_THROWS(); + } if ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) == NULL) { RETURN_NULL(); @@ -947,6 +1015,10 @@ PHP_METHOD(SplObjectStorage, unserialize) if (spl_object_storage_get_hash(&key, intern, Z_OBJ_P(entry)) == FAILURE) { zval_ptr_dtor(&inf); + if (EG(exception)) { + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + RETURN_THROWS(); + } goto outexcept; } pelement = spl_object_storage_get(intern, &key); @@ -960,6 +1032,14 @@ PHP_METHOD(SplObjectStorage, unserialize) var_push_dtor(&var_hash, &obj); } element = spl_object_storage_attach(intern, Z_OBJ_P(entry), Z_ISUNDEF(inf)?NULL:&inf); + if (UNEXPECTED(!element)) { + zval_ptr_dtor(&inf); + if (EG(exception)) { + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + RETURN_THROWS(); + } + goto outexcept; + } var_replace(&var_hash, &inf, &element->inf); zval_ptr_dtor(&inf); } @@ -1055,7 +1135,9 @@ PHP_METHOD(SplObjectStorage, __unserialize) } ZVAL_DEREF(val); - spl_object_storage_attach(intern, Z_OBJ_P(key), val); + if (UNEXPECTED(!spl_object_storage_attach(intern, Z_OBJ_P(key), val))) { + RETURN_THROWS(); + } key = NULL; } else { key = val; @@ -1151,8 +1233,14 @@ PHP_METHOD(MultipleIterator, attachIterator) } spl_object_storage_attach(intern, iterator, &zinfo); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } else { spl_object_storage_attach(intern, iterator, NULL); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } } } /* }}} */ @@ -1167,6 +1255,9 @@ PHP_METHOD(MultipleIterator, detachIterator) RETURN_THROWS(); } spl_object_storage_detach(intern, Z_OBJ_P(iterator)); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); intern->index = 0; @@ -1206,7 +1297,9 @@ PHP_METHOD(MultipleIterator, rewind) zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; + GC_ADDREF(it); zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_rewind, it, NULL); + OBJ_RELEASE(it); zend_hash_move_forward_ex(&intern->storage, &intern->pos); } } @@ -1225,7 +1318,9 @@ PHP_METHOD(MultipleIterator, next) zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; + GC_ADDREF(it); zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_next, it, NULL); + OBJ_RELEASE(it); zend_hash_move_forward_ex(&intern->storage, &intern->pos); } } @@ -1252,7 +1347,9 @@ PHP_METHOD(MultipleIterator, valid) zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; + GC_ADDREF(it); zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_valid, it, &retval); + OBJ_RELEASE(it); if (!Z_ISUNDEF(retval)) { valid = (Z_TYPE(retval) == IS_TRUE); @@ -1290,6 +1387,9 @@ static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_ zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos); while ((element = zend_hash_get_current_data_ptr_ex(&intern->storage, &intern->pos)) != NULL && !EG(exception)) { zend_object *it = element->obj; + zval inf; + GC_ADDREF(it); + ZVAL_COPY(&inf, &element->inf); zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_valid, it, &retval); if (!Z_ISUNDEF(retval)) { @@ -1306,10 +1406,14 @@ static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_ zend_call_known_instance_method_with_0_params(it->ce->iterator_funcs_ptr->zf_key, it, &retval); } if (Z_ISUNDEF(retval)) { + OBJ_RELEASE(it); + zval_ptr_dtor(&inf); zend_throw_exception(spl_ce_RuntimeException, "Failed to call sub iterator method", 0); return; } } else if (intern->flags & MIT_NEED_ALL) { + OBJ_RELEASE(it); + zval_ptr_dtor(&inf); if (SPL_MULTIPLE_ITERATOR_GET_ALL_CURRENT == get_type) { zend_throw_exception(spl_ce_RuntimeException, "Called current() with non valid sub iterator", 0); } else { @@ -1321,15 +1425,17 @@ static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_ } if (intern->flags & MIT_KEYS_ASSOC) { - switch (Z_TYPE(element->inf)) { + switch (Z_TYPE(inf)) { case IS_LONG: - add_index_zval(return_value, Z_LVAL(element->inf), &retval); + add_index_zval(return_value, Z_LVAL(inf), &retval); break; case IS_STRING: - zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR(element->inf), &retval); + zend_symtable_update(Z_ARRVAL_P(return_value), Z_STR(inf), &retval); break; default: zval_ptr_dtor(&retval); + OBJ_RELEASE(it); + zval_ptr_dtor(&inf); zend_throw_exception(spl_ce_InvalidArgumentException, "Sub-Iterator is associated with NULL", 0); return; } @@ -1337,6 +1443,8 @@ static void spl_multiple_iterator_get_all(spl_SplObjectStorage *intern, int get_ add_next_index_zval(return_value, &retval); } + OBJ_RELEASE(it); + zval_ptr_dtor(&inf); zend_hash_move_forward_ex(&intern->storage, &intern->pos); } } @@ -1378,7 +1486,7 @@ PHP_MINIT_FUNCTION(spl_observer) memcpy(&spl_handler_SplObjectStorage, &std_object_handlers, sizeof(zend_object_handlers)); - spl_handler_SplObjectStorage.offset = XtOffsetOf(spl_SplObjectStorage, std); + spl_handler_SplObjectStorage.offset = offsetof(spl_SplObjectStorage, std); spl_handler_SplObjectStorage.compare = spl_object_storage_compare_objects; spl_handler_SplObjectStorage.clone_obj = spl_object_storage_clone; spl_handler_SplObjectStorage.get_gc = spl_object_storage_get_gc; diff --git a/ext/spl/tests/GH-22047.phpt b/ext/spl/tests/GH-22047.phpt new file mode 100644 index 000000000000..b01fbfd633c0 --- /dev/null +++ b/ext/spl/tests/GH-22047.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-22047: ArrayObject invalid iterator class in serialized payload +--CREDITS-- +Igor Sak-Sakovskiy (Positive Technologies) +--FILE-- + $v) { + echo "should not reach here\n"; + } +} catch (UnexpectedValueException $e) { + echo $e->getMessage(), "\n"; +} + +?> +--EXPECTF-- +Cannot deserialize ArrayObject with iterator class 'GlobIterator'; this class is not derived from ArrayIterator diff --git a/ext/spl/tests/SplFixedArray_setSize_destruct_grow_during_clear.phpt b/ext/spl/tests/SplFixedArray_setSize_destruct_grow_during_clear.phpt new file mode 100644 index 000000000000..f0982364afa8 --- /dev/null +++ b/ext/spl/tests/SplFixedArray_setSize_destruct_grow_during_clear.phpt @@ -0,0 +1,28 @@ +--TEST-- +SplFixedArray::setSize: grow re-entrantly during clear (setSize(0)) +--FILE-- +arr !== null) { + $this->arr->setSize(5); + } + } +} + +$arr = new SplFixedArray(2); +$r = new Reentrant(); +$r->arr = $arr; +$arr[0] = $r; +unset($r); +$arr[1] = "tail"; + +$arr->setSize(0); +echo "size: ", $arr->getSize(), "\n"; +$arr[0] = "ok"; +var_dump($arr[0]); +?> +--EXPECT-- +size: 5 +string(2) "ok" diff --git a/ext/spl/tests/SplObjectStorage/concurrent_deletion.phpt b/ext/spl/tests/SplObjectStorage/concurrent_deletion.phpt index ae063f1c9b03..9da6270b6d7d 100644 --- a/ext/spl/tests/SplObjectStorage/concurrent_deletion.phpt +++ b/ext/spl/tests/SplObjectStorage/concurrent_deletion.phpt @@ -1,46 +1,62 @@ --TEST-- -SplObjectStorage: Concurrent deletion during iteration +SplObjectStorage: Mutation during getHash is prohibited --CREDITS-- cnitlrt --FILE-- mutate) { + $victim[new stdClass()] = null; } return spl_object_hash($obj); } } +function populate(SplObjectStorage $victim, SplObjectStorage $other): void { + for ($i = 0; $i < 1024; $i++) { + $o = new stdClass(); + $victim[$o] = null; + $other[$o] = null; + } +} + $victim = new SplObjectStorage(); $other = new EvilStorage(); -for ($i = 0; $i < 1024; $i++) { - $o = new stdClass(); - $victim[$o] = null; - $other[$o] = null; +populate($victim, $other); +$other->mutate = true; + +try { + $victim->removeAllExcept($other); +} catch (Error $e) { + echo $e->getMessage(), "\n"; } -var_dump($victim->removeAllExcept($other)); +var_dump(count($victim), count($other)); unset($victim, $other); $victim = new SplObjectStorage(); $other = new EvilStorage(); -for ($i = 0; $i < 1024; $i++) { - $o = new stdClass(); - $victim[$o] = null; - $other[$o] = null; +populate($victim, $other); +$other->mutate = true; + +try { + $other->addAll($victim); +} catch (Error $e) { + echo $e->getMessage(), "\n"; } -var_dump($other->addAll($victim)); +var_dump(count($victim), count($other)); ?> ---EXPECTF-- -int(%d) +--EXPECT-- +Modification of SplObjectStorage during getHash() is prohibited +int(1024) +int(1024) +Modification of SplObjectStorage during getHash() is prohibited +int(1024) int(1024) diff --git a/ext/spl/tests/SplObjectStorage/concurrent_deletion_addall.phpt b/ext/spl/tests/SplObjectStorage/concurrent_deletion_addall.phpt index af3fc381b562..aadbe2acba27 100644 --- a/ext/spl/tests/SplObjectStorage/concurrent_deletion_addall.phpt +++ b/ext/spl/tests/SplObjectStorage/concurrent_deletion_addall.phpt @@ -1,5 +1,5 @@ --TEST-- -SplObjectStorage: Concurrent deletion during addAll +SplObjectStorage: Mutation during getHash is prohibited during addAll --CREDITS-- cnitlrt --FILE-- @@ -17,27 +17,16 @@ $storage = new SplObjectStorage(); $storage[new stdClass] = 'foo'; $evil = new EvilStorage(); -$evil->addAll($storage); +try { + $evil->addAll($storage); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} -var_dump($evil, $storage); +var_dump(count($evil), count($storage)); ?> ---EXPECTF-- -object(EvilStorage)#%d (1) { - ["storage":"SplObjectStorage":private]=> - array(1) { - [0]=> - array(2) { - ["obj"]=> - object(stdClass)#%d (0) { - } - ["inf"]=> - string(3) "foo" - } - } -} -object(SplObjectStorage)#%d (1) { - ["storage":"SplObjectStorage":private]=> - array(0) { - } -} +--EXPECT-- +Modification of SplObjectStorage during getHash() is prohibited +int(0) +int(1) diff --git a/ext/spl/tests/SplObjectStorage/concurrent_deletion_removeexcept.phpt b/ext/spl/tests/SplObjectStorage/concurrent_deletion_removeexcept.phpt index b2ed211b304a..2602bc9e1f03 100644 --- a/ext/spl/tests/SplObjectStorage/concurrent_deletion_removeexcept.phpt +++ b/ext/spl/tests/SplObjectStorage/concurrent_deletion_removeexcept.phpt @@ -1,5 +1,5 @@ --TEST-- -SplObjectStorage: Concurrent deletion during removeAllExcept +SplObjectStorage: Mutation during getHash is prohibited during removeAllExcept --CREDITS-- cnitlrt --FILE-- @@ -17,19 +17,16 @@ $storage = new SplObjectStorage(); $storage[new stdClass] = 'foo'; $evil = new EvilStorage(); -$storage->removeAllExcept($evil); +try { + $storage->removeAllExcept($evil); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} -var_dump($evil, $storage); +var_dump(count($evil), count($storage)); ?> ---EXPECTF-- -object(EvilStorage)#%d (1) { - ["storage":"SplObjectStorage":private]=> - array(0) { - } -} -object(SplObjectStorage)#%d (1) { - ["storage":"SplObjectStorage":private]=> - array(0) { - } -} +--EXPECT-- +Modification of SplObjectStorage during getHash() is prohibited +int(0) +int(1) diff --git a/ext/spl/tests/SplObjectStorage/gh21831.phpt b/ext/spl/tests/SplObjectStorage/gh21831.phpt new file mode 100644 index 000000000000..581012d86a4f --- /dev/null +++ b/ext/spl/tests/SplObjectStorage/gh21831.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-21831: SplObjectStorage::getHash() cannot mutate storage during removeAllExcept() +--FILE-- +other) { + $this->other->offsetUnset($obj); + $this->other = null; + } + + return 'x'; + } +} + +$storage = new SplObjectStorage(); +$storage[new stdClass()] = null; + +$filter = new FilterStorage(); +$filter->other = $storage; + +try { + $storage->removeAllExcept($filter); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} + +var_dump(count($storage)); + +?> +--EXPECT-- +Modification of SplObjectStorage during getHash() is prohibited +int(1) diff --git a/ext/spl/tests/gh21927.phpt b/ext/spl/tests/gh21927.phpt new file mode 100644 index 000000000000..a60ea5ca1025 --- /dev/null +++ b/ext/spl/tests/gh21927.phpt @@ -0,0 +1,151 @@ +--TEST-- +GH-21927: Use-after-free of self-freeing MultipleIterator children +--FILE-- +parent->detachIterator($this); + echo "rewind: still alive\n"; + } + public function next(): void {} + public function current(): mixed { return 0; } + public function key(): mixed { return 0; } + public function valid(): bool { return false; } +} + +class DetachOnNext implements Iterator { + public function __construct(private MultipleIterator $parent) {} + public function rewind(): void {} + public function next(): void { + $this->parent->detachIterator($this); + echo "next: still alive\n"; + } + public function current(): mixed { return 0; } + public function key(): mixed { return 0; } + public function valid(): bool { return true; } +} + +class DetachOnValid implements Iterator { + public function __construct(private MultipleIterator $parent) {} + public function rewind(): void {} + public function next(): void {} + public function current(): mixed { return 0; } + public function key(): mixed { return 0; } + public function valid(): bool { + $this->parent->detachIterator($this); + echo "valid: still alive\n"; + return true; + } +} + +class DetachOnCurrent implements Iterator { + public function __construct(private MultipleIterator $parent) {} + public function rewind(): void {} + public function next(): void {} + public function current(): mixed { + $this->parent->detachIterator($this); + echo "current: still alive\n"; + return 'C'; + } + public function key(): mixed { return 'k'; } + public function valid(): bool { return true; } +} + +class DetachOnKey implements Iterator { + public function __construct(private MultipleIterator $parent) {} + public function rewind(): void {} + public function next(): void {} + public function current(): mixed { return 'C'; } + public function key(): mixed { + $this->parent->detachIterator($this); + echo "key: still alive\n"; + return 'K'; + } + public function valid(): bool { return true; } +} + +echo "-- detach inside rewind --\n"; +$mi = new MultipleIterator(); +$mi->attachIterator(new DetachOnRewind($mi)); +$mi->rewind(); +var_dump($mi->countIterators()); + +echo "-- detach inside next --\n"; +$mi = new MultipleIterator(); +$mi->attachIterator(new DetachOnNext($mi)); +$mi->rewind(); +$mi->next(); +var_dump($mi->countIterators()); + +echo "-- detach inside valid --\n"; +$mi = new MultipleIterator(); +$mi->attachIterator(new DetachOnValid($mi)); +var_dump($mi->valid()); +var_dump($mi->countIterators()); + +echo "-- detach inside current (numeric keys) --\n"; +$mi = new MultipleIterator(); +$mi->attachIterator(new DetachOnCurrent($mi)); +var_dump($mi->current()); +var_dump($mi->countIterators()); + +echo "-- detach inside key (numeric keys) --\n"; +$mi = new MultipleIterator(); +$mi->attachIterator(new DetachOnKey($mi)); +var_dump($mi->key()); +var_dump($mi->countIterators()); + +echo "-- detach inside current (assoc keys, refcounted inf) --\n"; +$mi = new MultipleIterator(MultipleIterator::MIT_NEED_ALL | MultipleIterator::MIT_KEYS_ASSOC); +$mi->attachIterator(new DetachOnCurrent($mi), 'cur_info_string'); +var_dump($mi->current()); +var_dump($mi->countIterators()); + +echo "-- detach inside key (assoc keys, refcounted inf) --\n"; +$mi = new MultipleIterator(MultipleIterator::MIT_NEED_ALL | MultipleIterator::MIT_KEYS_ASSOC); +$mi->attachIterator(new DetachOnKey($mi), 'key_info_string'); +var_dump($mi->key()); +var_dump($mi->countIterators()); + +?> +--EXPECT-- +-- detach inside rewind -- +rewind: still alive +int(0) +-- detach inside next -- +next: still alive +int(0) +-- detach inside valid -- +valid: still alive +bool(true) +int(0) +-- detach inside current (numeric keys) -- +current: still alive +array(1) { + [0]=> + string(1) "C" +} +int(0) +-- detach inside key (numeric keys) -- +key: still alive +array(1) { + [0]=> + string(1) "K" +} +int(0) +-- detach inside current (assoc keys, refcounted inf) -- +current: still alive +array(1) { + ["cur_info_string"]=> + string(1) "C" +} +int(0) +-- detach inside key (assoc keys, refcounted inf) -- +key: still alive +array(1) { + ["key_info_string"]=> + string(1) "K" +} +int(0) diff --git a/ext/spl/tests/gh22062.phpt b/ext/spl/tests/gh22062.phpt new file mode 100644 index 000000000000..ea67a9983a3d --- /dev/null +++ b/ext/spl/tests/gh22062.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-22062 (SplDoublyLinkedList iterator UAF via destructor releasing next node) +--FILE-- +setIteratorMode( + SplDoublyLinkedList::IT_MODE_FIFO | + SplDoublyLinkedList::IT_MODE_DELETE +); + +$list->push(new class($list) { + public function __construct(private SplDoublyLinkedList $list) {} + public function __destruct() { + if ($this->list->count() > 0) { + $this->list->offsetUnset(0); + } + } +}); + +$list->push(new stdClass()); + +foreach ($list as $item) { + unset($item); +} + +var_dump($list->count()); +?> +--EXPECT-- +int(0) diff --git a/ext/spl/tests/unserialize_errors.phpt b/ext/spl/tests/unserialize_errors.phpt index 1138b5c8cd54..64356923ae29 100644 --- a/ext/spl/tests/unserialize_errors.phpt +++ b/ext/spl/tests/unserialize_errors.phpt @@ -144,7 +144,7 @@ Incomplete or ill-typed serialization data Passed variable is not an array or object Incomplete or ill-typed serialization data Cannot deserialize ArrayObject with iterator class 'NonExistent'; no such class exists -Cannot deserialize ArrayObject with iterator class 'Existent'; this class does not implement the Iterator interface +Cannot deserialize ArrayObject with iterator class 'Existent'; this class is not derived from ArrayIterator ArrayIterator: Incomplete or ill-typed serialization data Incomplete or ill-typed serialization data diff --git a/ext/sqlite3/php_sqlite3_structs.h b/ext/sqlite3/php_sqlite3_structs.h index a43b2f76ca32..e8488028240e 100644 --- a/ext/sqlite3/php_sqlite3_structs.h +++ b/ext/sqlite3/php_sqlite3_structs.h @@ -72,9 +72,7 @@ typedef struct _php_sqlite3_db_object { zend_object zo; } php_sqlite3_db_object; -static inline php_sqlite3_db_object *php_sqlite3_db_from_obj(zend_object *obj) { - return (php_sqlite3_db_object*)((char*)(obj) - XtOffsetOf(php_sqlite3_db_object, zo)); -} +#define php_sqlite3_db_from_obj(obj) ZEND_CONTAINER_OF(obj, php_sqlite3_db_object, zo) #define Z_SQLITE3_DB_P(zv) php_sqlite3_db_from_obj(Z_OBJ_P((zv))) @@ -101,9 +99,7 @@ struct _php_sqlite3_result_object { zend_object zo; }; -static inline php_sqlite3_result *php_sqlite3_result_from_obj(zend_object *obj) { - return (php_sqlite3_result*)((char*)(obj) - XtOffsetOf(php_sqlite3_result, zo)); -} +#define php_sqlite3_result_from_obj(obj) ZEND_CONTAINER_OF(obj, php_sqlite3_result, zo) #define Z_SQLITE3_RESULT_P(zv) php_sqlite3_result_from_obj(Z_OBJ_P((zv))) @@ -119,9 +115,7 @@ struct _php_sqlite3_stmt_object { zend_object zo; }; -static inline php_sqlite3_stmt *php_sqlite3_stmt_from_obj(zend_object *obj) { - return (php_sqlite3_stmt*)((char*)(obj) - XtOffsetOf(php_sqlite3_stmt, zo)); -} +#define php_sqlite3_stmt_from_obj(obj) ZEND_CONTAINER_OF(obj, php_sqlite3_stmt, zo) #define Z_SQLITE3_STMT_P(zv) php_sqlite3_stmt_from_obj(Z_OBJ_P((zv))) diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 7d73b9923695..d257703f17ac 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -478,6 +478,9 @@ PHP_METHOD(SQLite3, escapeString) if (ret) { RETVAL_STRING(ret); sqlite3_free(ret); + } else { + zend_throw_exception_ex(php_sqlite3_exception_ce, 0, "Unable to escape string"); + RETURN_THROWS(); } } else { RETURN_EMPTY_STRING(); @@ -2480,7 +2483,7 @@ PHP_MINIT_FUNCTION(sqlite3) memcpy(&sqlite3_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); /* Register SQLite 3 Class */ - sqlite3_object_handlers.offset = XtOffsetOf(php_sqlite3_db_object, zo); + sqlite3_object_handlers.offset = offsetof(php_sqlite3_db_object, zo); sqlite3_object_handlers.clone_obj = NULL; sqlite3_object_handlers.free_obj = php_sqlite3_object_free_storage; sqlite3_object_handlers.get_gc = php_sqlite3_get_gc; @@ -2489,7 +2492,7 @@ PHP_MINIT_FUNCTION(sqlite3) php_sqlite3_sc_entry->default_object_handlers = &sqlite3_object_handlers; /* Register SQLite 3 Prepared Statement Class */ - sqlite3_stmt_object_handlers.offset = XtOffsetOf(php_sqlite3_stmt, zo); + sqlite3_stmt_object_handlers.offset = offsetof(php_sqlite3_stmt, zo); sqlite3_stmt_object_handlers.clone_obj = NULL; sqlite3_stmt_object_handlers.free_obj = php_sqlite3_stmt_object_free_storage; php_sqlite3_stmt_entry = register_class_SQLite3Stmt(); @@ -2497,7 +2500,7 @@ PHP_MINIT_FUNCTION(sqlite3) php_sqlite3_stmt_entry->default_object_handlers = &sqlite3_stmt_object_handlers; /* Register SQLite 3 Result Class */ - sqlite3_result_object_handlers.offset = XtOffsetOf(php_sqlite3_result, zo); + sqlite3_result_object_handlers.offset = offsetof(php_sqlite3_result, zo); sqlite3_result_object_handlers.clone_obj = NULL; sqlite3_result_object_handlers.free_obj = php_sqlite3_result_object_free_storage; php_sqlite3_result_entry = register_class_SQLite3Result(); diff --git a/ext/sqlite3/tests/sqlite3_35_stmt_readonly.phpt b/ext/sqlite3/tests/sqlite3_35_stmt_readonly.phpt index 49f029d5854f..e07d5b73e999 100644 --- a/ext/sqlite3/tests/sqlite3_35_stmt_readonly.phpt +++ b/ext/sqlite3/tests/sqlite3_35_stmt_readonly.phpt @@ -2,13 +2,6 @@ SQLite3_stmt::readOnly check --EXTENSIONS-- sqlite3 ---SKIPIF-- - --FILE-- = 3.8.3'); ?> --FILE-- --FILE-- cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER); + + /* Do not type error for BC */ + if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) { + php_error_docref(NULL, E_WARNING, "%s is not supported on type %s", + op_name, zend_zval_type_name(entry)); + return; + } + op(return_value, return_value, &dst); + return; + } + + zend_result status = op(return_value, return_value, entry); + if (status == FAILURE) { + ZEND_ASSERT(EG(exception)); + zend_clear_exception(); + /* BC resources: previously resources were cast to int */ + if (Z_TYPE_P(entry) == IS_RESOURCE) { + zval tmp; + ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry)); + op(return_value, return_value, &tmp); + } + /* BC non numeric strings: previously were cast to 0 */ + else if (Z_TYPE_P(entry) == IS_STRING) { + zval tmp; + ZVAL_LONG(&tmp, 0); + op(return_value, return_value, &tmp); + } + php_error_docref(NULL, E_WARNING, "%s is not supported on type %s", + op_name, zend_zval_type_name(entry)); + } +} + /* Wrapper for array_sum and array_product */ -static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial) +static zend_always_inline void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, binary_op_type op, zend_long initial) { HashTable *input; - zval *entry; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(input) @@ -6329,42 +6368,39 @@ static void php_array_binop(INTERNAL_FUNCTION_PARAMETERS, const char *op_name, b } ZVAL_LONG(return_value, initial); - ZEND_HASH_FOREACH_VAL(input, entry) { - /* For objects we try to cast them to a numeric type */ - if (Z_TYPE_P(entry) == IS_OBJECT) { - zval dst; - zend_result status = Z_OBJ_HT_P(entry)->cast_object(Z_OBJ_P(entry), &dst, _IS_NUMBER); - - /* Do not type error for BC */ - if (status == FAILURE || (Z_TYPE(dst) != IS_LONG && Z_TYPE(dst) != IS_DOUBLE)) { - php_error_docref(NULL, E_WARNING, "%s is not supported on type %s", - op_name, zend_zval_type_name(entry)); - continue; - } - op(return_value, return_value, &dst); - continue; - } - zend_result status = op(return_value, return_value, entry); - if (status == FAILURE) { - ZEND_ASSERT(EG(exception)); - zend_clear_exception(); - /* BC resources: previously resources were cast to int */ - if (Z_TYPE_P(entry) == IS_RESOURCE) { - zval tmp; - ZVAL_LONG(&tmp, Z_RES_HANDLE_P(entry)); - op(return_value, return_value, &tmp); + if (op == add_function) { + zval *entry; + ZEND_HASH_FOREACH_VAL(input, entry) { + if (EXPECTED(Z_TYPE_P(entry) == IS_LONG) && EXPECTED(Z_TYPE_P(return_value) == IS_LONG)) { + fast_long_add_function(return_value, return_value, entry); + continue; } - /* BC non numeric strings: previously were cast to 0 */ - else if (Z_TYPE_P(entry) == IS_STRING) { - zval tmp; - ZVAL_LONG(&tmp, 0); - op(return_value, return_value, &tmp); + php_array_binop_apply(return_value, entry, op_name, op); + } ZEND_HASH_FOREACH_END(); + } else if (op == mul_function) { + zval *entry; + ZEND_HASH_FOREACH_VAL(input, entry) { + if (EXPECTED(Z_TYPE_P(entry) == IS_LONG) && EXPECTED(Z_TYPE_P(return_value) == IS_LONG)) { + zend_long lval; + double dval; + int overflow; + ZEND_SIGNED_MULTIPLY_LONG(Z_LVAL_P(return_value), Z_LVAL_P(entry), lval, dval, overflow); + if (UNEXPECTED(overflow)) { + ZVAL_DOUBLE(return_value, dval); + } else { + Z_LVAL_P(return_value) = lval; + } + continue; } - php_error_docref(NULL, E_WARNING, "%s is not supported on type %s", - op_name, zend_zval_type_name(entry)); - } - } ZEND_HASH_FOREACH_END(); + php_array_binop_apply(return_value, entry, op_name, op); + } ZEND_HASH_FOREACH_END(); + } else { + zval *entry; + ZEND_HASH_FOREACH_VAL(input, entry) { + php_array_binop_apply(return_value, entry, op_name, op); + } ZEND_HASH_FOREACH_END(); + } } /* {{{ Returns the sum of the array entries */ @@ -6417,6 +6453,7 @@ PHP_FUNCTION(array_reduce) fci.retval = return_value; fci.param_count = 2; fci.params = args; + fci.consumed_args = zend_fci_consumed_arg(0); ZEND_HASH_FOREACH_VAL(htbl, operand) { ZVAL_COPY_VALUE(&args[0], return_value); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 5c6b1ce1d1d1..0c1e2f8222a6 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -335,6 +335,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */ #endif BASIC_MINIT_SUBMODULE(exec) + BASIC_MINIT_SUBMODULE(stream_errors) BASIC_MINIT_SUBMODULE(user_streams) php_register_url_stream_wrapper("php", &php_stream_php_wrapper); @@ -598,6 +599,20 @@ PHP_FUNCTION(ip2long) if (addr_len == 0 || inet_pton(AF_INET, addr, &ip) != 1) { RETURN_FALSE; } +#ifdef _AIX + /* + AIX accepts IP strings with extraneous 0 (192.168.042.42 will be treated as + 192.168.42.42), while Linux doesn't. + For consistency, we convert back the IP to a string and check if it is equal to + the original string. If not, the IP should be considered invalid. + */ + char str[INET_ADDRSTRLEN]; + const char* result = inet_ntop(AF_INET, &ip, str, sizeof(str)); + ZEND_ASSERT(result != NULL); + if (strcmp(addr, result) != 0) { + RETURN_FALSE; + } +#endif RETURN_LONG(ntohl(ip.s_addr)); } /* }}} */ @@ -1941,6 +1956,12 @@ PHP_FUNCTION(ini_get_all) add_assoc_null(&option, "local_value"); } + if (ini_entry->def->value) { + add_assoc_stringl(&option, "builtin_default_value", ini_entry->def->value, ini_entry->def->value_length); + } else { + add_assoc_null(&option, "builtin_default_value"); + } + add_assoc_long(&option, "access", ini_entry->modifiable); zend_symtable_update(Z_ARRVAL_P(return_value), ini_entry->name, &option); diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 1999c9b92be1..7fc31e7d80ca 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -3385,7 +3385,10 @@ function soundex(string $string): string {} /* streamsfuncs.c */ -function stream_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, ?int $microseconds = null): int|false {} +/** + * @param resource|null $context + */ +function stream_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, ?int $microseconds = null, $context = null): int|false {} /** * @return resource @@ -3481,6 +3484,11 @@ function stream_socket_sendto($socket, string $data, int $flags = 0, string $add */ function stream_socket_enable_crypto($stream, bool $enable, ?int $crypto_method = null, $session_stream = null): int|bool {} +/** + * @param resource $stream + */ +function stream_socket_get_crypto_status($stream): int {} + #ifdef HAVE_SHUTDOWN /** @param resource $stream */ function stream_socket_shutdown($stream, int $mode): bool {} @@ -3488,17 +3496,19 @@ function stream_socket_shutdown($stream, int $mode): bool {} #ifdef HAVE_SOCKETPAIR /** + * @param resource|null $context * @return array|false * @refcount 1 */ -function stream_socket_pair(int $domain, int $type, int $protocol): array|false {} +function stream_socket_pair(int $domain, int $type, int $protocol, $context = null): array|false {} #endif /** * @param resource $from * @param resource $to + * @param resource|null $context */ -function stream_copy_to_stream($from, $to, ?int $length = null, int $offset = 0): int|false {} +function stream_copy_to_stream($from, $to, ?int $length = null, int $offset = 0, $context = null): int|false {} /** * @param resource $stream @@ -3558,14 +3568,24 @@ function stream_resolve_include_path(string $filename): string|false {} */ function stream_get_wrappers(): array {} +/** + * @return array + */ +function stream_last_errors(): array {} + +function stream_clear_errors(): void {} + /** * @return array * @refcount 1 */ function stream_get_transports(): array {} -/** @param resource|string $stream */ -function stream_is_local($stream): bool {} +/** + * @param resource|string $stream + * @param resource|null $context + */ +function stream_is_local($stream, $context = null): bool {} /** @param resource $stream */ function stream_isatty($stream): bool {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index e51a837ffa4d..c5266c5a877c 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: 36b71aa7bbfe478a5e4af400b2822a77067efa2f + * Stub hash: 3b1649a3abb3cfb5cb39d93f30a97765fe862d67 * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) @@ -1823,6 +1823,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_select, 0, 4, MAY_BE_LONG ZEND_ARG_TYPE_INFO(1, except, IS_ARRAY, 1) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, microseconds, IS_LONG, 1, "null") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_stream_context_create, 0, 0, 0) @@ -1925,6 +1926,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_socket_enable_crypto, 0, ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, session_stream, "null") ZEND_END_ARG_INFO() +#define arginfo_stream_socket_get_crypto_status arginfo_fpassthru + #if defined(HAVE_SHUTDOWN) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_socket_shutdown, 0, 2, _IS_BOOL, 0) ZEND_ARG_INFO(0, stream) @@ -1937,6 +1940,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_socket_pair, 0, 3, MAY_BE ZEND_ARG_TYPE_INFO(0, domain, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, protocol, IS_LONG, 0) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() #endif @@ -1945,6 +1949,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_copy_to_stream, 0, 2, MAY ZEND_ARG_INFO(0, to) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "0") + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stream_get_contents, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) @@ -1987,9 +1992,16 @@ ZEND_END_ARG_INFO() #define arginfo_stream_get_wrappers arginfo_ob_list_handlers +#define arginfo_stream_last_errors arginfo_ob_list_handlers + +#define arginfo_stream_clear_errors arginfo_flush + #define arginfo_stream_get_transports arginfo_ob_list_handlers -#define arginfo_stream_is_local arginfo_rewind +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stream_is_local, 0, 1, _IS_BOOL, 0) + ZEND_ARG_INFO(0, stream) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, context, "null") +ZEND_END_ARG_INFO() #define arginfo_stream_isatty arginfo_rewind @@ -2813,6 +2825,7 @@ ZEND_FUNCTION(stream_socket_get_name); ZEND_FUNCTION(stream_socket_recvfrom); ZEND_FUNCTION(stream_socket_sendto); ZEND_FUNCTION(stream_socket_enable_crypto); +ZEND_FUNCTION(stream_socket_get_crypto_status); #if defined(HAVE_SHUTDOWN) ZEND_FUNCTION(stream_socket_shutdown); #endif @@ -2829,6 +2842,8 @@ ZEND_FUNCTION(stream_get_meta_data); ZEND_FUNCTION(stream_get_line); ZEND_FUNCTION(stream_resolve_include_path); ZEND_FUNCTION(stream_get_wrappers); +ZEND_FUNCTION(stream_last_errors); +ZEND_FUNCTION(stream_clear_errors); ZEND_FUNCTION(stream_get_transports); ZEND_FUNCTION(stream_is_local); ZEND_FUNCTION(stream_isatty); @@ -3426,6 +3441,7 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(stream_socket_recvfrom, arginfo_stream_socket_recvfrom) ZEND_FE(stream_socket_sendto, arginfo_stream_socket_sendto) ZEND_FE(stream_socket_enable_crypto, arginfo_stream_socket_enable_crypto) + ZEND_FE(stream_socket_get_crypto_status, arginfo_stream_socket_get_crypto_status) #if defined(HAVE_SHUTDOWN) ZEND_FE(stream_socket_shutdown, arginfo_stream_socket_shutdown) #endif @@ -3445,6 +3461,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(stream_get_line, arginfo_stream_get_line) ZEND_FE(stream_resolve_include_path, arginfo_stream_resolve_include_path) ZEND_FE(stream_get_wrappers, arginfo_stream_get_wrappers) + ZEND_FE(stream_last_errors, arginfo_stream_last_errors) + ZEND_FE(stream_clear_errors, arginfo_stream_clear_errors) ZEND_FE(stream_get_transports, arginfo_stream_get_transports) ZEND_FE(stream_is_local, arginfo_stream_is_local) ZEND_FE(stream_isatty, arginfo_stream_isatty) diff --git a/ext/standard/basic_functions_decl.h b/ext/standard/basic_functions_decl.h index b3eb25c5d988..630a4b7e656b 100644 --- a/ext/standard/basic_functions_decl.h +++ b/ext/standard/basic_functions_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: 36b71aa7bbfe478a5e4af400b2822a77067efa2f */ + * Stub hash: 3b1649a3abb3cfb5cb39d93f30a97765fe862d67 */ -#ifndef ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H -#define ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H +#ifndef ZEND_BASIC_FUNCTIONS_DECL_3b1649a3abb3cfb5cb39d93f30a97765fe862d67_H +#define ZEND_BASIC_FUNCTIONS_DECL_3b1649a3abb3cfb5cb39d93f30a97765fe862d67_H typedef enum zend_enum_SortDirection { ZEND_ENUM_SortDirection_Ascending = 1, @@ -20,4 +20,4 @@ typedef enum zend_enum_RoundingMode { ZEND_ENUM_RoundingMode_PositiveInfinity = 8, } zend_enum_RoundingMode; -#endif /* ZEND_BASIC_FUNCTIONS_DECL_36b71aa7bbfe478a5e4af400b2822a77067efa2f_H */ +#endif /* ZEND_BASIC_FUNCTIONS_DECL_3b1649a3abb3cfb5cb39d93f30a97765fe862d67_H */ diff --git a/ext/standard/credits_ext.h b/ext/standard/credits_ext.h index 509135a27e3e..b22a3f090c43 100644 --- a/ext/standard/credits_ext.h +++ b/ext/standard/credits_ext.h @@ -19,7 +19,7 @@ CREDIT_LINE("cURL", "Sterling Hughes"); CREDIT_LINE("Date/Time Support", "Derick Rethans"); CREDIT_LINE("DB-LIB (MS SQL, Sybase)", "Wez Furlong, Frank M. Kromann, Adam Baratz"); CREDIT_LINE("DBA", "Sascha Schumann, Marcus Boerger"); -CREDIT_LINE("DOM", "Christian Stocker, Rob Richards, Marcus Boerger, Niels Dossche"); +CREDIT_LINE("DOM", "Christian Stocker, Rob Richards, Marcus Boerger, Nora Dossche"); CREDIT_LINE("enchant", "Pierre-Alain Joye, Ilia Alshanetsky"); CREDIT_LINE("EXIF", "Rasmus Lerdorf, Marcus Boerger"); CREDIT_LINE("FFI", "Dmitry Stogov"); @@ -69,7 +69,7 @@ CREDIT_LINE("System V Semaphores", "Tom May"); CREDIT_LINE("System V Shared Memory", "Christian Cartus"); CREDIT_LINE("tidy", "John Coggeshall, Ilia Alshanetsky"); CREDIT_LINE("tokenizer", "Andrei Zmievski, Johannes Schlueter"); -CREDIT_LINE("uri", "Máté Kocsis, Tim Düsterhus, Ignace Nyamagana Butera, Arnaud Le Blanc, Dennis Snell, Niels Dossche, Nicolas Grekas"); +CREDIT_LINE("uri", "Máté Kocsis, Tim Düsterhus, Ignace Nyamagana Butera, Arnaud Le Blanc, Dennis Snell, Nora Dossche, Nicolas Grekas"); CREDIT_LINE("XML", "Stig Bakken, Thies C. Arntzen, Sterling Hughes"); CREDIT_LINE("XMLReader", "Rob Richards"); CREDIT_LINE("XMLWriter", "Rob Richards, Pierre-Alain Joye"); diff --git a/ext/standard/dl.c b/ext/standard/dl.c index 63f627e51d88..a6d0ced6fa86 100644 --- a/ext/standard/dl.c +++ b/ext/standard/dl.c @@ -91,7 +91,7 @@ PHPAPI void *php_load_shlib(const char *path, char **errp) size_t i = strlen(err); (*errp)=estrdup(err); php_win32_error_msg_free(err); - while (i > 0 && isspace((*errp)[i-1])) { (*errp)[i-1] = '\0'; i--; } + while (i > 0 && isspace((unsigned char)(*errp)[i-1])) { (*errp)[i-1] = '\0'; i--; } } else { (*errp) = estrdup(""); } diff --git a/ext/standard/exec.c b/ext/standard/exec.c index 0e5903420a3e..5c9e5c6076a8 100644 --- a/ext/standard/exec.c +++ b/ext/standard/exec.c @@ -79,7 +79,7 @@ PHP_MINIT_FUNCTION(exec) static size_t strip_trailing_whitespace(char *buf, size_t bufl) { size_t l = bufl; - while (l-- > 0 && isspace(((unsigned char *)buf)[l])); + while (l-- > 0 && isspace((unsigned char)buf[l])); if (l != (bufl - 1)) { bufl = l + 1; buf[bufl] = '\0'; diff --git a/ext/standard/file.c b/ext/standard/file.c index 1841c242b870..db0fc45385dd 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -221,7 +221,9 @@ PHP_FUNCTION(flock) Z_PARAM_ZVAL(wouldblock) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); php_flock_common(stream, operation, 2, wouldblock, return_value); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -257,6 +259,8 @@ PHP_FUNCTION(get_meta_tags) RETURN_FALSE; } + php_stream_error_operation_begin(); + array_init(return_value); tok_last = TOK_EOF; @@ -368,6 +372,8 @@ PHP_FUNCTION(get_meta_tags) if (value) efree(value); if (name) efree(name); php_stream_close(md.stream); + + php_stream_error_operation_end_for_stream(md.stream); } /* }}} */ @@ -402,12 +408,13 @@ PHP_FUNCTION(file_get_contents) RETURN_THROWS(); } + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, 0); - stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (!stream) { + php_stream_error_operation_end(context); RETURN_FALSE; } @@ -418,8 +425,9 @@ PHP_FUNCTION(file_get_contents) } if (offset != 0 && php_stream_seek(stream, offset, ((offset > 0) ? SEEK_SET : SEEK_END)) < 0) { - php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset); php_stream_close(stream); + php_stream_error_operation_end(context); + php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset); RETURN_FALSE; } @@ -430,6 +438,7 @@ PHP_FUNCTION(file_get_contents) } php_stream_close(stream); + php_stream_error_operation_end(context); } /* }}} */ @@ -459,6 +468,7 @@ PHP_FUNCTION(file_put_contents) php_stream_from_zval(srcstream, data); } + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); if (flags & PHP_FILE_APPEND) { @@ -477,11 +487,13 @@ PHP_FUNCTION(file_put_contents) stream = php_stream_open_wrapper_ex(filename, mode, ((flags & PHP_FILE_USE_INCLUDE_PATH) ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (stream == NULL) { + php_stream_error_operation_end(context); RETURN_FALSE; } if ((flags & LOCK_EX) && (!php_stream_supports_lock(stream) || php_stream_lock(stream, LOCK_EX))) { php_stream_close(stream); + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Exclusive locks are not supported for this stream"); RETURN_FALSE; } @@ -564,6 +576,7 @@ PHP_FUNCTION(file_put_contents) break; } php_stream_close(stream); + php_stream_error_operation_end(context); if (numbytes < 0) { RETURN_FALSE; @@ -609,10 +622,12 @@ PHP_FUNCTION(file) include_new_line = !(flags & PHP_FILE_IGNORE_NEW_LINES); skip_blank_lines = flags & PHP_FILE_SKIP_EMPTY_LINES; + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (!stream) { + php_stream_error_operation_end(context); RETURN_FALSE; } @@ -666,6 +681,7 @@ PHP_FUNCTION(file) } php_stream_close(stream); + php_stream_error_operation_end(context); } /* }}} */ @@ -705,7 +721,9 @@ PHP_FUNCTION(tmpfile) ZEND_PARSE_PARAMETERS_NONE(); + php_stream_error_operation_begin(); stream = php_stream_fopen_tmpfile(); + php_stream_error_operation_end_for_stream(stream); if (stream) { php_stream_to_zval(stream, return_value); @@ -733,9 +751,11 @@ PHP_FUNCTION(fopen) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, 0); stream = php_stream_open_wrapper_ex(filename, mode, (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); + php_stream_error_operation_end(context); if (stream == NULL) { RETURN_FALSE; @@ -759,9 +779,11 @@ PHPAPI PHP_FUNCTION(fclose) RETURN_FALSE; } + php_stream_error_operation_begin(); php_stream_free(stream, PHP_STREAM_FREE_KEEP_RSRC | (stream->is_persistent ? PHP_STREAM_FREE_CLOSE_PERSISTENT : PHP_STREAM_FREE_CLOSE)); + php_stream_error_operation_end_for_stream(stream); RETURN_TRUE; } @@ -809,6 +831,7 @@ PHP_FUNCTION(popen) RETURN_FALSE; } + php_stream_error_operation_begin(); stream = php_stream_fopen_from_pipe(fp, mode); if (stream == NULL) { @@ -817,6 +840,7 @@ PHP_FUNCTION(popen) } else { php_stream_to_zval(stream, return_value); } + php_stream_error_operation_end_for_stream(stream); efree(posix_mode); } @@ -831,9 +855,11 @@ PHP_FUNCTION(pclose) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); FG(pclose_wait) = 1; zend_list_close(stream->res); FG(pclose_wait) = 0; + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(FG(pclose_ret)); } /* }}} */ @@ -847,11 +873,13 @@ PHPAPI PHP_FUNCTION(feof) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (php_stream_eof(stream)) { - RETURN_TRUE; + RETVAL_TRUE; } else { - RETURN_FALSE; + RETVAL_FALSE; } + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -871,9 +899,11 @@ PHPAPI PHP_FUNCTION(fgets) Z_PARAM_LONG_OR_NULL(len, len_is_null) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (len_is_null) { /* ask streams to give us a buffer of an appropriate size */ buf = php_stream_get_line(stream, NULL, 0, &line_len); + php_stream_error_operation_end_for_stream(stream); if (buf == NULL) { RETURN_FALSE; } @@ -887,7 +917,9 @@ PHPAPI PHP_FUNCTION(fgets) } str = zend_string_alloc(len, 0); - if (php_stream_get_line(stream, ZSTR_VAL(str), len, &line_len) == NULL) { + buf = php_stream_get_line(stream, ZSTR_VAL(str), len, &line_len); + php_stream_error_operation_end_for_stream(stream); + if (buf == NULL) { zend_string_efree(str); RETURN_FALSE; } @@ -912,7 +944,9 @@ PHPAPI PHP_FUNCTION(fgetc) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); int result = php_stream_getc(stream); + php_stream_error_operation_end_for_stream(stream); if (result == EOF) { RETVAL_FALSE; @@ -931,7 +965,6 @@ PHP_FUNCTION(fscanf) zval *file_handle; char *buf, *format; size_t len; - void *what; ZEND_PARSE_PARAMETERS_START(2, -1) Z_PARAM_RESOURCE(file_handle) @@ -939,16 +972,18 @@ PHP_FUNCTION(fscanf) Z_PARAM_VARIADIC('*', args, argc) ZEND_PARSE_PARAMETERS_END(); - what = zend_fetch_resource2(Z_RES_P(file_handle), "File-Handle", php_file_le_stream(), php_file_le_pstream()); + php_stream *stream = zend_fetch_resource2(Z_RES_P(file_handle), "File-Handle", php_file_le_stream(), php_file_le_pstream()); - /* we can't do a ZEND_VERIFY_RESOURCE(what), otherwise we end up + /* we can't do a ZEND_VERIFY_RESOURCE(stream), otherwise we end up * with a leak if we have an invalid filehandle. This needs changing * if the code behind ZEND_VERIFY_RESOURCE changed. - cc */ - if (!what) { + if (!stream) { RETURN_THROWS(); } - buf = php_stream_get_line((php_stream *) what, NULL, 0, &len); + php_stream_error_operation_begin(); + buf = php_stream_get_line(stream, NULL, 0, &len); + php_stream_error_operation_end_for_stream(stream); if (buf == NULL) { RETURN_FALSE; } @@ -994,7 +1029,9 @@ PHPAPI PHP_FUNCTION(fwrite) RETURN_LONG(0); } + php_stream_error_operation_begin(); ret = php_stream_write(stream, input, num_bytes); + php_stream_error_operation_end_for_stream(stream); if (ret < 0) { RETURN_FALSE; } @@ -1013,8 +1050,9 @@ PHPAPI PHP_FUNCTION(fflush) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); ret = php_stream_flush(stream); - + php_stream_error_operation_end_for_stream(stream); RETURN_BOOL(!ret); } /* }}} */ @@ -1022,13 +1060,17 @@ PHPAPI PHP_FUNCTION(fflush) /* {{{ Rewind the position of a file pointer */ PHPAPI PHP_FUNCTION(rewind) { + int ret; php_stream *stream; ZEND_PARSE_PARAMETERS_START(1, 1) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); - RETURN_BOOL(-1 != php_stream_rewind(stream)); + php_stream_error_operation_begin(); + ret = php_stream_rewind(stream); + php_stream_error_operation_end_for_stream(stream); + RETURN_BOOL(-1 != ret); } /* }}} */ @@ -1042,7 +1084,9 @@ PHPAPI PHP_FUNCTION(ftell) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); ret = php_stream_tell(stream); + php_stream_error_operation_end_for_stream(stream); if (ret == -1) { RETURN_FALSE; } @@ -1063,7 +1107,9 @@ PHPAPI PHP_FUNCTION(fseek) Z_PARAM_LONG(whence) ZEND_PARSE_PARAMETERS_END(); - RETURN_LONG(php_stream_seek(stream, offset, (int) whence)); + php_stream_error_operation_begin(); + RETVAL_LONG(php_stream_seek(stream, offset, (int) whence)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1087,7 +1133,9 @@ PHP_FUNCTION(mkdir) context = php_stream_context_from_zval(zcontext, 0); - RETURN_BOOL(php_stream_mkdir(dir, (int)mode, (recursive ? PHP_STREAM_MKDIR_RECURSIVE : 0) | REPORT_ERRORS, context)); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_mkdir(dir, (int)mode, (recursive ? PHP_STREAM_MKDIR_RECURSIVE : 0) | REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1107,7 +1155,9 @@ PHP_FUNCTION(rmdir) context = php_stream_context_from_zval(zcontext, 0); - RETURN_BOOL(php_stream_rmdir(dir, REPORT_ERRORS, context)); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_rmdir(dir, REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1131,14 +1181,17 @@ PHP_FUNCTION(readfile) context = php_stream_context_from_zval(zcontext, 0); + php_stream_error_operation_begin(); stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context); if (stream) { size = php_stream_passthru(stream); php_stream_close(stream); - RETURN_LONG(size); + RETVAL_LONG(size); + } else { + RETVAL_FALSE; } + php_stream_error_operation_end(context); - RETURN_FALSE; } /* }}} */ @@ -1180,7 +1233,9 @@ PHPAPI PHP_FUNCTION(fpassthru) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); size = php_stream_passthru(stream); + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(size); } /* }}} */ @@ -1201,26 +1256,31 @@ PHP_FUNCTION(rename) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + wrapper = php_stream_locate_url_wrapper(old_name, NULL, 0); if (!wrapper || !wrapper->wops) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Unable to locate stream wrapper"); RETURN_FALSE; } if (!wrapper->wops->rename) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "%s wrapper does not support renaming", wrapper->wops->label ? wrapper->wops->label : "Source"); RETURN_FALSE; } if (wrapper != php_stream_locate_url_wrapper(new_name, NULL, 0)) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Cannot rename a file across wrapper types"); RETURN_FALSE; } - context = php_stream_context_from_zval(zcontext, 0); - - RETURN_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, 0, context)); + RETVAL_BOOL(wrapper->wops->rename(wrapper, old_name, new_name, REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1239,20 +1299,24 @@ PHP_FUNCTION(unlink) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, 0); wrapper = php_stream_locate_url_wrapper(filename, NULL, 0); if (!wrapper || !wrapper->wops) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Unable to locate stream wrapper"); RETURN_FALSE; } if (!wrapper->wops->unlink) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "%s does not allow unlinking", wrapper->wops->label ? wrapper->wops->label : "Wrapper"); RETURN_FALSE; } - RETURN_BOOL(wrapper->wops->unlink(wrapper, filename, REPORT_ERRORS, context)); + RETVAL_BOOL(wrapper->wops->unlink(wrapper, filename, REPORT_ERRORS, context)); + php_stream_error_operation_end(context); } /* }}} */ @@ -1264,12 +1328,15 @@ PHP_FUNCTION(fsync) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (!php_stream_sync_supported(stream)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Can't fsync this stream!"); RETURN_FALSE; } - RETURN_BOOL(php_stream_sync(stream, /* data_only */ 0) == 0); + RETVAL_BOOL(php_stream_sync(stream, /* data_only */ 0) == 0); + php_stream_error_operation_end_for_stream(stream); } PHP_FUNCTION(fdatasync) @@ -1280,12 +1347,15 @@ PHP_FUNCTION(fdatasync) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (!php_stream_sync_supported(stream)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Can't fsync this stream!"); RETURN_FALSE; } - RETURN_BOOL(php_stream_sync(stream, /* data_only */ 1) == 0); + RETVAL_BOOL(php_stream_sync(stream, /* data_only */ 1) == 0); + php_stream_error_operation_end_for_stream(stream); } /* {{{ Truncate file to 'size' length */ @@ -1304,12 +1374,16 @@ PHP_FUNCTION(ftruncate) RETURN_THROWS(); } + php_stream_error_operation_begin(); + if (!php_stream_truncate_supported(stream)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Can't truncate this stream!"); RETURN_FALSE; } - RETURN_BOOL(0 == php_stream_truncate_set_size(stream, size)); + RETVAL_BOOL(0 == php_stream_truncate_set_size(stream, size)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ PHPAPI void php_fstat(php_stream *stream, zval *return_value) @@ -1393,7 +1467,9 @@ PHP_FUNCTION(fstat) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); php_fstat(stream, return_value); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1412,13 +1488,16 @@ PHP_FUNCTION(copy) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + if (php_stream_locate_url_wrapper(source, NULL, 0) == &php_plain_files_wrapper && php_check_open_basedir(source)) { + php_stream_error_operation_end(context); RETURN_FALSE; } - context = php_stream_context_from_zval(zcontext, 0); - - RETURN_BOOL(php_copy_file_ctx(source, target, 0, context) == SUCCESS); + RETVAL_BOOL(php_copy_file_ctx(source, target, 0, context) == SUCCESS); + php_stream_error_operation_end(context); } /* }}} */ @@ -1543,7 +1622,9 @@ PHPAPI PHP_FUNCTION(fread) RETURN_THROWS(); } + php_stream_error_operation_begin(); str = php_stream_read_to_str(stream, len); + php_stream_error_operation_end_for_stream(stream); if (!str) { RETURN_FALSE; } @@ -1662,7 +1743,9 @@ PHP_FUNCTION(fputcsv) RETURN_THROWS(); } + php_stream_error_operation_begin(); ret = php_fputcsv(stream, fields, delimiter, enclosure, escape_char, eol_str); + php_stream_error_operation_end_for_stream(stream); if (ret < 0) { RETURN_FALSE; } @@ -1795,19 +1878,23 @@ PHP_FUNCTION(fgetcsv) RETURN_THROWS(); } + php_stream_error_operation_begin(); if (len < 0) { if ((buf = php_stream_get_line(stream, NULL, 0, &buf_len)) == NULL) { + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } } else { buf = emalloc(len + 1); if (php_stream_get_line(stream, buf, len + 1, &buf_len) == NULL) { efree(buf); + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } } HashTable *values = php_fgetcsv(stream, delimiter, enclosure, escape_char, buf_len, buf); + php_stream_error_operation_end_for_stream(stream); if (values == NULL) { values = php_bc_fgetcsv_empty_line(); } @@ -1860,7 +1947,7 @@ PHPAPI HashTable *php_fgetcsv(php_stream *stream, char delimiter, char enclosure inc_len = (bptr < limit ? (*bptr == '\0' ? 1 : php_mblen(bptr, limit - bptr)): 0); if (inc_len == 1) { char *tmp = bptr; - while ((*tmp != delimiter) && isspace((int)*(unsigned char *)tmp)) { + while ((*tmp != delimiter) && isspace((unsigned char)*tmp)) { tmp++; } if (*tmp == enclosure && tmp < limit) { diff --git a/ext/standard/file.h b/ext/standard/file.h index 3c6160fd4bb1..ac218169599c 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -32,6 +32,7 @@ PHPAPI PHP_FUNCTION(ftell); PHPAPI PHP_FUNCTION(fseek); PHPAPI PHP_FUNCTION(fpassthru); +PHP_MINIT_FUNCTION(stream_errors); PHP_MINIT_FUNCTION(user_streams); PHPAPI zend_result php_copy_file(const char *src, const char *dest); @@ -98,7 +99,8 @@ typedef struct { php_stream_context *default_context; HashTable *stream_wrappers; /* per-request copy of url_stream_wrappers_hash */ HashTable *stream_filters; /* per-request copy of stream_filters_hash */ - HashTable *wrapper_errors; /* key: wrapper address; value: linked list of char* */ + HashTable *wrapper_logged_errors; /* key: wrapper address; value: linked list of error entries */ + php_stream_error_state stream_error_state; int pclose_wait; #ifdef HAVE_GETHOSTBYNAME_R struct hostent tmp_host_info; diff --git a/ext/standard/file.stub.php b/ext/standard/file.stub.php index 5e12c43f397c..d7b1fef17cdc 100644 --- a/ext/standard/file.stub.php +++ b/ext/standard/file.stub.php @@ -256,6 +256,23 @@ */ const STREAM_CRYPTO_PROTO_TLSv1_3 = UNKNOWN; +/** + * @var int + * @cvalue STREAM_CRYPTO_STATUS_NONE + */ +const STREAM_CRYPTO_STATUS_NONE = UNKNOWN; +/** + * @var int + * @cvalue STREAM_CRYPTO_STATUS_WANT_READ + */ +const STREAM_CRYPTO_STATUS_WANT_READ = UNKNOWN; +/** + * @var int + * @cvalue STREAM_CRYPTO_STATUS_WANT_WRITE + */ +const STREAM_CRYPTO_STATUS_WANT_WRITE = UNKNOWN; + + /** * @var int * @cvalue STREAM_SHUT_RD diff --git a/ext/standard/file_arginfo.h b/ext/standard/file_arginfo.h index b3888925ee9a..24e3722cd86e 100644 --- a/ext/standard/file_arginfo.h +++ b/ext/standard/file_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit file.stub.php instead. - * Stub hash: c394e14cd32587ce9ad0503e21c6c4cf5b301697 */ + * Stub hash: 0c62c6fb217a87010a9e2e63d4b104cde0138655 */ static void register_file_symbols(int module_number) { @@ -52,6 +52,9 @@ static void register_file_symbols(int module_number) REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_TLSv1_1", STREAM_CRYPTO_METHOD_TLSv1_1_SERVER, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_TLSv1_2", STREAM_CRYPTO_METHOD_TLSv1_2_SERVER, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_CRYPTO_PROTO_TLSv1_3", STREAM_CRYPTO_METHOD_TLSv1_3_SERVER, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_CRYPTO_STATUS_NONE", STREAM_CRYPTO_STATUS_NONE, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_CRYPTO_STATUS_WANT_READ", STREAM_CRYPTO_STATUS_WANT_READ, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("STREAM_CRYPTO_STATUS_WANT_WRITE", STREAM_CRYPTO_STATUS_WANT_WRITE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_SHUT_RD", STREAM_SHUT_RD, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_SHUT_WR", STREAM_SHUT_WR, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("STREAM_SHUT_RDWR", STREAM_SHUT_RDWR, CONST_PERSISTENT); diff --git a/ext/standard/filters.c b/ext/standard/filters.c index 90dd471cf848..d26429704e26 100644 --- a/ext/standard/filters.c +++ b/ext/standard/filters.c @@ -63,7 +63,8 @@ static const php_stream_filter_ops strfilter_rot13_ops = { static php_stream_filter *strfilter_rot13_create(const char *filtername, zval *filterparams, bool persistent) { - return php_stream_filter_alloc(&strfilter_rot13_ops, NULL, persistent, PSFS_SEEKABLE_ALWAYS); + return php_stream_filter_alloc(&strfilter_rot13_ops, NULL, persistent, + PSFS_SEEKABLE_ALWAYS, PSFS_SEEKABLE_ALWAYS); } static const php_stream_filter_factory strfilter_rot13_factory = { @@ -147,12 +148,14 @@ static const php_stream_filter_ops strfilter_tolower_ops = { static php_stream_filter *strfilter_toupper_create(const char *filtername, zval *filterparams, bool persistent) { - return php_stream_filter_alloc(&strfilter_toupper_ops, NULL, persistent, PSFS_SEEKABLE_ALWAYS); + return php_stream_filter_alloc(&strfilter_toupper_ops, NULL, persistent, + PSFS_SEEKABLE_ALWAYS, PSFS_SEEKABLE_ALWAYS); } static php_stream_filter *strfilter_tolower_create(const char *filtername, zval *filterparams, bool persistent) { - return php_stream_filter_alloc(&strfilter_tolower_ops, NULL, persistent, PSFS_SEEKABLE_ALWAYS); + return php_stream_filter_alloc(&strfilter_tolower_ops, NULL, persistent, + PSFS_SEEKABLE_ALWAYS, PSFS_SEEKABLE_ALWAYS); } static const php_stream_filter_factory strfilter_toupper_factory = { @@ -1000,7 +1003,7 @@ static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *ins goto out; } - if (!isxdigit((int) *ps)) { + if (!isxdigit(*ps)) { err = PHP_CONV_ERR_INVALID_SEQ; goto out; } @@ -1634,7 +1637,7 @@ static const php_stream_filter_ops strfilter_convert_ops = { static php_stream_filter *strfilter_convert_create(const char *filtername, zval *filterparams, bool persistent) { php_convert_filter *inst; - + php_stream_filter_seekable_t write_seekable; const char *dot; int conv_mode = 0; @@ -1648,6 +1651,10 @@ static php_stream_filter *strfilter_convert_create(const char *filtername, zval } ++dot; + if (php_stream_filter_parse_write_seek_mode(filterparams, &write_seekable) == FAILURE) { + return NULL; + } + inst = pemalloc(sizeof(php_convert_filter), persistent); if (strcasecmp(dot, "base64-encode") == 0) { @@ -1667,7 +1674,7 @@ static php_stream_filter *strfilter_convert_create(const char *filtername, zval return NULL; } - return php_stream_filter_alloc(&strfilter_convert_ops, inst, persistent, PSFS_SEEKABLE_START); + return php_stream_filter_alloc(&strfilter_convert_ops, inst, persistent, PSFS_SEEKABLE_START, write_seekable); } static const php_stream_filter_factory strfilter_convert_factory = { @@ -1761,7 +1768,7 @@ static php_stream_filter *consumed_filter_create(const char *filtername, zval *f data->offset = ~0; fops = &consumed_filter_ops; - return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START); + return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START, PSFS_SEEKABLE_ALWAYS); } static const php_stream_filter_factory consumed_filter_factory = { @@ -1992,7 +1999,7 @@ static php_stream_filter *chunked_filter_create(const char *filtername, zval *fi data->persistent = persistent; fops = &chunked_filter_ops; - return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START); + return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START, PSFS_SEEKABLE_ALWAYS); } static const php_stream_filter_factory chunked_filter_factory = { diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c index 4324e7c14dd7..f35d196fb971 100644 --- a/ext/standard/formatted_print.c +++ b/ext/standard/formatted_print.c @@ -374,7 +374,7 @@ php_sprintf_getnumber(char **buffer, size_t *len) int php_sprintf_get_argnum(char **format, size_t *format_len) { char *temppos = *format; - while (isdigit((int) *temppos)) temppos++; + while (isdigit((unsigned char)*temppos)) temppos++; if (*temppos != '$') { return ARG_NUM_NEXT; } @@ -466,7 +466,7 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n PRINTF_DEBUG(("sprintf: first looking at '%c', inpos=%zu\n", *format, format - format_orig)); - if (isalpha((int)*format)) { + if (isalpha((unsigned char)*format)) { width = precision = 0; argnum = ARG_NUM_NEXT; } else { @@ -535,7 +535,7 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n } width = Z_LVAL_P(tmp); adjusting |= ADJ_WIDTH; - } else if (isdigit((int)*format)) { + } else if (isdigit((unsigned char)*format)) { PRINTF_DEBUG(("sprintf: getting width\n")); if ((width = php_sprintf_getnumber(&format, &format_len)) < 0) { zend_value_error("Width must be between 0 and %d", INT_MAX); @@ -580,7 +580,7 @@ php_formatted_print(char *format, size_t format_len, zval *args, int argc, int n precision = Z_LVAL_P(tmp); adjusting |= ADJ_PRECISION; expprec = 1; - } else if (isdigit((int)*format)) { + } else if (isdigit((unsigned char)*format)) { if ((precision = php_sprintf_getnumber(&format, &format_len)) < 0) { zend_value_error("Precision must be between 0 and %d", INT_MAX); goto fail; diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index 73407aaa401c..f99ae5e4b4e1 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -77,8 +77,8 @@ static inline int get_ftp_result(php_stream *stream, char *buffer, size_t buffer { buffer[0] = '\0'; /* in case read fails to read anything */ while (php_stream_gets(stream, buffer, buffer_size-1) && - !(isdigit((int) buffer[0]) && isdigit((int) buffer[1]) && - isdigit((int) buffer[2]) && buffer[3] == ' ')); + !(isdigit((unsigned char)buffer[0]) && isdigit((unsigned char)buffer[1]) && + isdigit((unsigned char)buffer[2]) && buffer[3] == ' ')); return strtol(buffer, NULL, 10); } /* }}} */ @@ -106,7 +106,9 @@ static int php_stream_ftp_stream_close(php_stream_wrapper *wrapper, php_stream * /* For write modes close data stream first to signal EOF to server */ result = GET_FTP_RESULT(controlstream); if (result != 226 && result != 250) { - php_error_docref(NULL, E_WARNING, "FTP server error %d:%s", result, tmp_line); + php_stream_wrapper_warn(wrapper, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + ProtocolError, + "FTP server error %d:%s", result, tmp_line); ret = EOF; } } @@ -184,7 +186,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char /* get the response */ result = GET_FTP_RESULT(stream); if (result != 334) { - php_stream_wrapper_log_error(wrapper, options, "Server doesn't support FTPS."); + php_stream_wrapper_log_warn(wrapper, context, options, SslNotSupported, + "Server doesn't support FTPS."); goto connect_errexit; } else { /* we must reuse the old SSL session id */ @@ -203,7 +206,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, context, options, SslNotSupported, + "Unable to activate SSL mode"); php_stream_close(stream); stream = NULL; goto connect_errexit; @@ -233,8 +237,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char #define PHP_FTP_CNTRL_CHK(val, val_len, err_msg) { \ unsigned char *s = (unsigned char *) val, *e = (unsigned char *) s + val_len; \ while (s < e) { \ - if (iscntrl(*s)) { \ - php_stream_wrapper_log_error(wrapper, options, err_msg, val); \ + if (iscntrl((unsigned char)*s)) { \ + php_stream_wrapper_log_warn(wrapper, context, options, AuthFailed, err_msg, val); \ goto connect_errexit; \ } \ s++; \ @@ -342,14 +346,14 @@ static unsigned short php_fopen_do_pasv(php_stream *stream, char *ip, size_t ip_ /* parse pasv command (129, 80, 95, 25, 13, 221) */ tpath = tmp_line; /* skip over the "227 Some message " part */ - for (tpath += 4; *tpath && !isdigit((int) *tpath); tpath++); + for (tpath += 4; *tpath && !isdigit((unsigned char)*tpath); tpath++); if (!*tpath) { return 0; } /* skip over the host ip, to get the port */ hoststart = tpath; for (i = 0; i < 4; i++) { - for (; isdigit((int) *tpath); tpath++); + for (; isdigit((unsigned char)*tpath); tpath++); if (*tpath != ',') { return 0; } @@ -431,7 +435,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa } if (strpbrk(mode, "wa+")) { if (read_write) { - php_stream_wrapper_log_error(wrapper, options, "FTP does not support simultaneous read/write connections"); + php_stream_wrapper_log_warn(wrapper, context, options, ModeNotSupported, + "FTP does not support simultaneous read/write connections"); return NULL; } if (strchr(mode, 'a')) { @@ -442,7 +447,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa } if (!read_write) { /* No mode specified? */ - php_stream_wrapper_log_error(wrapper, options, "Unknown file open mode"); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidMode, + "Unknown file open mode"); return NULL; } @@ -453,7 +459,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa return php_stream_url_wrap_http(wrapper, path, mode, options, opened_path, context STREAMS_CC); } else { /* ftp proxy is read-only */ - php_stream_wrapper_log_error(wrapper, options, "FTP proxy may only be used in read mode"); + php_stream_wrapper_log_warn(wrapper, context, options, ModeNotSupported, + "FTP proxy may only be used in read mode"); return NULL; } } @@ -505,7 +512,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa goto errexit; } } else { - php_stream_wrapper_log_error(wrapper, options, "Remote file already exists and overwrite context option not specified"); + php_stream_wrapper_log_warn(wrapper, context, options, AlreadyExists, + "Remote file already exists and overwrite context option not specified"); errno = EEXIST; goto errexit; } @@ -529,7 +537,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_printf(stream, "REST " ZEND_LONG_FMT "\r\n", Z_LVAL_P(tmpzval)); result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { - php_stream_wrapper_log_error(wrapper, options, "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); + php_stream_wrapper_log_warn(wrapper, context, options, ResumptionFailed, + "Unable to resume from offset " ZEND_LONG_FMT, Z_LVAL_P(tmpzval)); goto errexit; } } @@ -574,7 +583,8 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(datastream, 1) < 0)) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, context, options, SslNotSupported, + "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; tmp_line[0]='\0'; @@ -596,10 +606,12 @@ php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *pa php_stream_close(stream); } if (tmp_line[0] != '\0') - php_stream_wrapper_log_error(wrapper, options, "FTP server reports %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "FTP server reports %s", tmp_line); if (error_message) { - php_stream_wrapper_log_error(wrapper, options, "Failed to set up data channel: %s", ZSTR_VAL(error_message)); + php_stream_wrapper_log_warn(wrapper, context, options, NetworkSendFailed, + "Failed to set up data channel: %s", ZSTR_VAL(error_message)); zend_string_release(error_message); } return NULL; @@ -744,7 +756,8 @@ static php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const ch STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(datastream, 1) < 0)) { - php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode"); + php_stream_wrapper_log_warn(wrapper, context, options, SslNotSupported, + "Unable to activate SSL mode"); php_stream_close(datastream); datastream = NULL; goto opendir_errexit; @@ -768,7 +781,8 @@ static php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const ch php_stream_close(stream); } if (tmp_line[0] != '\0') { - php_stream_wrapper_log_error(wrapper, options, "FTP server reports %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "FTP server reports %s", tmp_line); } return NULL; } @@ -830,7 +844,7 @@ static int php_stream_ftp_url_stat(php_stream_wrapper *wrapper, const char *url, struct tm tm, tmbuf, *gmt; time_t stamp; - while ((size_t)(p - tmp_line) < sizeof(tmp_line) && !isdigit(*p)) { + while ((size_t)(p - tmp_line) < sizeof(tmp_line) && !isdigit((unsigned char)*p)) { p++; } @@ -907,14 +921,16 @@ static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, i stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", url); } goto unlink_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, InvalidPath, + "Invalid path provided in %s", url); } goto unlink_errexit; } @@ -925,7 +941,8 @@ static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, i result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Deleting file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, UnlinkFailed, + "Error Deleting file: %s", tmp_line); } goto unlink_errexit; } @@ -989,7 +1006,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr stream = php_ftp_fopen_connect(wrapper, url_from, "r", 0, NULL, context, NULL, NULL, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", ZSTR_VAL(resource_from->host)); + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", ZSTR_VAL(resource_from->host)); } goto rename_errexit; } @@ -1000,7 +1018,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr result = GET_FTP_RESULT(stream); if (result < 300 || result > 399) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Renaming file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "Error Renaming file: %s", tmp_line); } goto rename_errexit; } @@ -1011,7 +1030,8 @@ static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_fr result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Error Renaming file: %s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, RenameFailed, + "Error Renaming file: %s", tmp_line); } goto rename_errexit; } @@ -1044,14 +1064,16 @@ static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, in stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", url); } goto mkdir_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, InvalidPath, + "Invalid path provided in %s", url); } goto mkdir_errexit; } @@ -1092,7 +1114,8 @@ static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, in result = GET_FTP_RESULT(stream); if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, MkdirFailed, + "%s", tmp_line); } break; } @@ -1136,14 +1159,16 @@ static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, in stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL); if (!stream) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url); + php_stream_wrapper_warn(wrapper, context, options, AuthFailed, + "Unable to connect to %s", url); } goto rmdir_errexit; } if (resource->path == NULL) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url); + php_stream_wrapper_warn(wrapper, context, options, InvalidPath, + "Invalid path provided in %s", url); } goto rmdir_errexit; } @@ -1153,7 +1178,8 @@ static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, in if (result < 200 || result > 299) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", tmp_line); + php_stream_wrapper_warn(wrapper, context, options, RmdirFailed, + "%s", tmp_line); } goto rmdir_errexit; } diff --git a/ext/standard/head.c b/ext/standard/head.c index 69e8d1f794ff..03f2e6189ee5 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -74,6 +74,13 @@ PHPAPI bool php_header(void) } } +PHPAPI bool php_is_valid_samesite_value(zend_string *value) +{ + return zend_string_equals_literal_ci(value, "Strict") + || zend_string_equals_literal_ci(value, "Lax") + || zend_string_equals_literal_ci(value, "None"); +} + #define ILLEGAL_COOKIE_CHARACTER "\",\", \";\", \" \", \"\\t\", \"\\r\", \"\\n\", \"\\013\", or \"\\014\"" PHPAPI zend_result php_setcookie(zend_string *name, zend_string *value, time_t expires, zend_string *path, zend_string *domain, bool secure, bool httponly, @@ -121,7 +128,11 @@ PHPAPI zend_result php_setcookie(zend_string *name, zend_string *value, time_t e return FAILURE; } - /* Should check value of SameSite? */ + if (samesite && ZSTR_LEN(samesite) > 0 && !php_is_valid_samesite_value(samesite)) { + zend_value_error("%s(): \"samesite\" option must be \"Strict\", \"Lax\", \"None\", or \"\"", + get_active_function_name()); + return FAILURE; + } if (value == NULL || ZSTR_LEN(value) == 0) { /* diff --git a/ext/standard/head.h b/ext/standard/head.h index 07c947f8ad0b..8b91371a46e2 100644 --- a/ext/standard/head.h +++ b/ext/standard/head.h @@ -24,6 +24,8 @@ #define COOKIE_SAMESITE "; SameSite=" #define COOKIE_PARTITIONED "; Partitioned" +PHPAPI bool php_is_valid_samesite_value(zend_string *value); + extern PHP_RINIT_FUNCTION(head); PHPAPI bool php_header(void); diff --git a/ext/standard/html.c b/ext/standard/html.c index c8920f497d75..1e761c971eda 100644 --- a/ext/standard/html.c +++ b/ext/standard/html.c @@ -676,8 +676,8 @@ static inline zend_result process_numeric_entity(const char **buf, unsigned *cod /* strtol allows whitespace and other stuff in the beginning * we're not interested */ - if ((hexadecimal && !isxdigit(**buf)) || - (!hexadecimal && !isdigit(**buf))) { + if ((hexadecimal && !isxdigit((unsigned char)**buf)) || + (!hexadecimal && !isdigit((unsigned char)**buf))) { return FAILURE; } diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c index f3172180bed9..9e3db6716048 100644 --- a/ext/standard/http_fopen_wrapper.c +++ b/ext/standard/http_fopen_wrapper.c @@ -243,7 +243,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w while (last_header_name < last_header_value) { if (*last_header_name == ' ' || *last_header_name == '\t') { header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, InvalidResponse, "HTTP invalid response format (space in header name)!"); zend_string_efree(last_header_line_str); return NULL; @@ -261,7 +261,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w } else { /* There is no colon which means invalid response so error. */ header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, InvalidResponse, "HTTP invalid response format (no colon in header line)!"); zend_string_efree(last_header_line_str); return NULL; @@ -285,7 +285,7 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w size_t last_header_value_len = strlen(last_header_value); if (last_header_value_len > HTTP_HEADER_MAX_LOCATION_SIZE) { header_info->error = true; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, InvalidResponse, "HTTP Location header size is over the limit of %d bytes", HTTP_HEADER_MAX_LOCATION_SIZE); zend_string_efree(last_header_line_str); @@ -386,7 +386,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, tmp_line[0] = '\0'; if (redirect_max < 1) { - php_stream_wrapper_log_error(wrapper, options, "Redirection limit reached, aborting"); + php_stream_wrapper_log_warn(wrapper, context, options, RedirectLimit, + "Redirection limit reached, aborting"); return NULL; } @@ -419,7 +420,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, /* Normal http request (possibly with proxy) */ if (strpbrk(mode, "awx+")) { - php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper does not support writeable connections"); + php_stream_wrapper_log_warn(wrapper, context, options, ModeNotSupported, + "HTTP wrapper does not support writeable connections"); php_uri_struct_free(resource); return NULL; } @@ -448,7 +450,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (request_fulluri && (strchr(path, '\n') != NULL || strchr(path, '\r') != NULL)) { - php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper full URI path does not allow CR or LF characters"); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "HTTP wrapper full URI path does not allow CR or LF characters"); php_uri_struct_free(resource); zend_string_release(transport_string); return NULL; @@ -463,7 +466,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, #endif if (d > timeoutmax) { - php_stream_wrapper_log_error(wrapper, options, "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidParam, + "timeout must be lower than " ZEND_ULONG_FMT, (zend_ulong)timeoutmax); zend_string_release(transport_string); php_uri_struct_free(resource); return NULL; @@ -493,7 +497,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (errstr) { - php_stream_wrapper_log_error(wrapper, options, "%s", ZSTR_VAL(errstr)); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "%s", ZSTR_VAL(errstr)); zend_string_release_ex(errstr, 0); errstr = NULL; } @@ -547,8 +552,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (reset_ssl_peer_name) { php_stream_context_unset_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name"); } - - php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy"); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } @@ -573,7 +578,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 || php_stream_xport_crypto_enable(stream, 1) < 0) { - php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy"); + php_stream_wrapper_log_warn(wrapper, context, options, SslNotSupported, + "Cannot connect to HTTPS server through proxy"); php_stream_close(stream); stream = NULL; } @@ -755,14 +761,14 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, smart_str scratch = {0}; /* decode the strings first */ - php_url_decode(ZSTR_VAL(resource->user), ZSTR_LEN(resource->user)); + ZSTR_LEN(resource->user) = php_url_decode(ZSTR_VAL(resource->user), ZSTR_LEN(resource->user)); smart_str_append(&scratch, resource->user); smart_str_appendc(&scratch, ':'); /* Note: password is optional! */ if (resource->password) { - php_url_decode(ZSTR_VAL(resource->password), ZSTR_LEN(resource->password)); + ZSTR_LEN(resource->password) = php_url_decode(ZSTR_VAL(resource->password), ZSTR_LEN(resource->password)); smart_str_append(&scratch, resource->password); } @@ -830,7 +836,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, ua[ua_len] = 0; smart_str_appendl(&req_buf, ua, ua_len); } else { - php_error_docref(NULL, E_WARNING, "Cannot construct User-agent header"); + php_stream_wrapper_warn_nt(wrapper, context, options, InvalidHeader, + "Cannot construct User-agent header"); } efree(ua); } @@ -869,7 +876,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } if (!(have_header & HTTP_HEADER_TYPE)) { smart_str_appends(&req_buf, "Content-Type: application/x-www-form-urlencoded\r\n"); - php_error_docref(NULL, E_NOTICE, "Content-type not specified assuming application/x-www-form-urlencoded"); + php_stream_wrapper_notice(wrapper, context, options, InvalidHeader, + "Content-type not specified assuming application/x-www-form-urlencoded"); } smart_str_appends(&req_buf, "\r\n"); smart_str_append(&req_buf, Z_STR_P(tmpzval)); @@ -956,7 +964,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, } else { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, "HTTP request failed!"); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "HTTP request failed!"); goto out; } } @@ -974,7 +983,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (http_header_line[1] != '\n') { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, InvalidResponse, "HTTP invalid header name (cannot start with CR character)!"); goto out; } @@ -1005,7 +1014,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, if (*http_header_line == ' ' || *http_header_line == '\t') { php_stream_close(stream); stream = NULL; - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, InvalidResponse, "HTTP invalid response format (folding header at the start)!"); goto out; } @@ -1101,7 +1110,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, php_uri_struct_free(resource); /* check for invalid redirection URLs */ if ((resource = php_uri_parse_to_struct(uri_parser, new_path, strlen(new_path), PHP_URI_COMPONENT_READ_MODE_RAW, true)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, + "Invalid redirect URL! %s", new_path); efree(new_path); goto out; } @@ -1113,7 +1123,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, s = (unsigned char*)ZSTR_VAL(val); e = s + ZSTR_LEN(val); \ while (s < e) { \ if (iscntrl(*s)) { \ - php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); \ + php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, \ + "Invalid redirect URL! %s", new_path); \ efree(new_path); \ goto out; \ } \ @@ -1139,7 +1150,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, --redirect_max, new_flags, response_header STREAMS_CC); efree(new_path); } else { - php_stream_wrapper_log_error(wrapper, options, "HTTP request failed! %s", tmp_line); + php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError, + "HTTP request failed! %s", tmp_line); } } out: diff --git a/ext/standard/math.c b/ext/standard/math.c index 66fbfeaa9428..1898d210ce65 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -859,9 +859,9 @@ PHPAPI void _php_math_basetozval(zend_string *str, int base, zval *ret) e = s + ZSTR_LEN(str); /* Skip leading whitespace */ - while (s < e && isspace(*s)) s++; + while (s < e && isspace((unsigned char)*s)) s++; /* Skip trailing whitespace */ - while (s < e && isspace(*(e-1))) e--; + while (s < e && isspace((unsigned char)e[-1])) e--; if (e - s >= 2) { if (base == 16 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) s += 2; @@ -1174,7 +1174,7 @@ PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *de tmpbuf = strpprintf(0, "%.*F", dec, d); if (tmpbuf == NULL) { return NULL; - } else if (!isdigit((int)ZSTR_VAL(tmpbuf)[0])) { + } else if (!isdigit((unsigned char)ZSTR_VAL(tmpbuf)[0])) { return tmpbuf; } diff --git a/ext/standard/metaphone.c b/ext/standard/metaphone.c index 816e5d28220e..8f33057de5ac 100644 --- a/ext/standard/metaphone.c +++ b/ext/standard/metaphone.c @@ -78,7 +78,7 @@ static const char _codes[26] = /* Note: these functions require an uppercase letter input! */ static zend_always_inline char encode(char c) { - if (isalpha(c)) { + if (isalpha((unsigned char)c)) { ZEND_ASSERT(c >= 'A' && c <= 'Z'); return _codes[(c - 'A')]; } else { @@ -107,7 +107,7 @@ static zend_always_inline char encode(char c) { /* I suppose I could have been using a character pointer instead of * accessing the array directly... */ -#define Convert_Raw(c) toupper(c) +#define Convert_Raw(c) toupper((unsigned char)c) /* Look at the next letter in the word */ #define Read_Raw_Next_Letter (word[w_idx+1]) #define Read_Next_Letter (Convert_Raw(Read_Raw_Next_Letter)) @@ -121,14 +121,14 @@ static zend_always_inline char encode(char c) { /* Look two letters down. It makes sure you don't walk off the string. */ #define Read_After_Next_Letter (Read_Raw_Next_Letter != '\0' ? Convert_Raw(word[w_idx+2]) \ : '\0') -#define Look_Ahead_Letter(n) (toupper(Lookahead((char *) word+w_idx, n))) +#define Look_Ahead_Letter(n) (toupper((unsigned char)Lookahead((char *) word+w_idx, n))) /* Allows us to safely look ahead an arbitrary # of letters */ /* I probably could have just used strlen... */ -static char Lookahead(char *word, int how_far) +static char Lookahead(char *word, size_t how_far) { - int idx; + size_t idx; for (idx = 0; word[idx] != '\0' && idx < how_far; idx++); /* Edge forward in the string... */ @@ -163,12 +163,12 @@ static char Lookahead(char *word, int how_far) #define Phone_Len (p_idx) /* Note is a letter is a 'break' in the word */ -#define Isbreak(c) (!isalpha(c)) +#define Isbreak(c) (!isalpha((unsigned char)(c))) /* {{{ metaphone */ static void metaphone(unsigned char *word, size_t word_len, zend_long max_phonemes, zend_string **phoned_word, int traditional) { - int w_idx = 0; /* point in the phonization we're at. */ + size_t w_idx = 0; /* point in the phonization we're at. */ size_t p_idx = 0; /* end of the phoned phrase */ size_t max_buffer_len = 0; /* maximum length of the destination buffer */ char curr_letter; @@ -187,7 +187,7 @@ static void metaphone(unsigned char *word, size_t word_len, zend_long max_phonem /*-- The first phoneme has to be processed specially. --*/ /* Find our first letter */ - for (; !isalpha(curr_letter = Read_Raw_Curr_Letter); w_idx++) { + for (; !isalpha((unsigned char)(curr_letter = Read_Raw_Curr_Letter)); w_idx++) { /* On the off chance we were given nothing but crap... */ if (curr_letter == '\0') { End_Phoned_Word(); @@ -275,7 +275,7 @@ static void metaphone(unsigned char *word, size_t word_len, zend_long max_phonem */ /* Ignore non-alphas */ - if (!isalpha(curr_letter)) + if (!isalpha((unsigned char)curr_letter)) continue; curr_letter = Convert_Raw(curr_letter); diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index 89c2d9c49b0c..3f062bf2ea1e 100644 --- a/ext/standard/php_fopen_wrapper.c +++ b/ext/standard/php_fopen_wrapper.c @@ -156,14 +156,18 @@ static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, i if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) { php_stream_filter_append(&stream->readfilters, temp_filter); } else { - php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p); + php_stream_wrapper_warn_nt(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + CreateFailed, + "Unable to create filter (%s)", p); } } if (write_chain) { if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) { php_stream_filter_append(&stream->writefilters, temp_filter); } else { - php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p); + php_stream_wrapper_warn_nt(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + CreateFailed, + "Unable to create filter (%s)", p); } } p = php_strtok_r(NULL, "|", &token); @@ -217,7 +221,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -236,7 +242,9 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (!strcasecmp(path, "stdin")) { if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -295,14 +303,18 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c if (strcmp(sapi_module.name, "cli")) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Direct access to file descriptors is only available from command-line PHP"); + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "Direct access to file descriptors is only available from command-line PHP"); } return NULL; } if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration"); + php_stream_wrapper_warn(wrapper, context, options, + Disabled, + "URL file-access is disabled in the server configuration"); } return NULL; } @@ -310,7 +322,8 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c start = &path[3]; fildes_ori = ZEND_STRTOL(start, &end, 10); if (end == start || *end != '\0') { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "php://fd/ stream must be specified in the form php://fd/"); return NULL; } @@ -322,14 +335,16 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c #endif if (fildes_ori < 0 || fildes_ori >= dtablesize) { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidParam, "The file descriptors must be non-negative numbers smaller than %d", dtablesize); return NULL; } fd = dup((int)fildes_ori); if (fd == -1) { - php_stream_wrapper_log_error(wrapper, options, + php_stream_wrapper_log_warn(wrapper, context, options, + DupFailed, "Error duping file descriptor " ZEND_LONG_FMT "; possibly it doesn't exist: " "[%d]: %s", fildes_ori, errno, strerror(errno)); return NULL; @@ -378,7 +393,8 @@ static php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const c return stream; } else { /* invalid php://thingy */ - php_error_docref(NULL, E_WARNING, "Invalid php:// URL specified"); + php_stream_wrapper_warn(wrapper, context, options, + InvalidUrl, "Invalid php:// URL specified"); return NULL; } diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index edccbeae5646..111111406799 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -1240,7 +1240,7 @@ PHP_FUNCTION(proc_open) Z_PARAM_ARRAY_HT(descriptorspec) Z_PARAM_ZVAL(pipes) Z_PARAM_OPTIONAL - Z_PARAM_STRING_OR_NULL(cwd, cwd_len) + Z_PARAM_PATH_OR_NULL(cwd, cwd_len) Z_PARAM_ARRAY_HT_OR_NULL(environment) Z_PARAM_ARRAY_OR_NULL(other_options) ZEND_PARSE_PARAMETERS_END(); diff --git a/ext/standard/quot_print.c b/ext/standard/quot_print.c index f4954a88f8fa..e9d8fafb9987 100644 --- a/ext/standard/quot_print.c +++ b/ext/standard/quot_print.c @@ -210,11 +210,11 @@ PHP_FUNCTION(quoted_printable_decode) switch (str_in[i]) { case '=': if (str_in[i + 1] && str_in[i + 2] && - isxdigit((int) str_in[i + 1]) && - isxdigit((int) str_in[i + 2])) + isxdigit((unsigned char)str_in[i + 1]) && + isxdigit((unsigned char)str_in[i + 2])) { - ZSTR_VAL(str_out)[j++] = (php_hex2int((int) str_in[i + 1]) << 4) - + php_hex2int((int) str_in[i + 2]); + ZSTR_VAL(str_out)[j++] = (php_hex2int((unsigned char)str_in[i + 1]) << 4) + + php_hex2int((unsigned char)str_in[i + 2]); i += 3; } else /* check for soft line break according to RFC 2045*/ { k = 1; diff --git a/ext/standard/scanf.c b/ext/standard/scanf.c index 980009c30640..5aeb585fea23 100644 --- a/ext/standard/scanf.c +++ b/ext/standard/scanf.c @@ -343,7 +343,7 @@ PHPAPI int ValidateFormat(char *format, int numVars, int *totalSubs) goto xpgCheckDone; } - if ( isdigit( (int)*ch ) ) { + if ( isdigit( (unsigned char)*ch ) ) { /* * Check for an XPG3-style %n$ specification. Note: there * must not be a mixture of XPG3 specs and non-XPG3 specs @@ -654,9 +654,9 @@ PHPAPI int php_sscanf_internal( char *string, char *format, /* * If we see whitespace in the format, skip whitespace in the string. */ - if ( isspace( (int)*ch ) ) { + if ( isspace( (unsigned char)*ch ) ) { sch = *string; - while ( isspace( (int)sch ) ) { + while ( isspace( (unsigned char)sch ) ) { if (*string == '\0') { goto done; } @@ -807,7 +807,7 @@ PHPAPI int php_sscanf_internal( char *string, char *format, if (!(flags & SCAN_NOSKIP)) { while (*string != '\0') { sch = *string; - if (! isspace((int)sch) ) { + if (! isspace((unsigned char)sch) ) { break; } string++; @@ -833,7 +833,7 @@ PHPAPI int php_sscanf_internal( char *string, char *format, end = string; while (*end != '\0') { sch = *end; - if ( isspace( (int)sch ) ) { + if ( isspace( (unsigned char)sch ) ) { break; } end++; diff --git a/ext/standard/soundex.c b/ext/standard/soundex.c index 527ef9537af3..f06c3ac98572 100644 --- a/ext/standard/soundex.c +++ b/ext/standard/soundex.c @@ -63,7 +63,7 @@ PHP_FUNCTION(soundex) /* BUG: should also map here accented letters used in non */ /* English words or names (also found in English text!): */ /* esstsett, thorn, n-tilde, c-cedilla, s-caron, ... */ - code = toupper((int)(unsigned char)str[i]); + code = toupper((unsigned char)str[i]); if (code >= 'A' && code <= 'Z') { if (_small == 0) { /* remember first valid char */ diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 68f79783c6f8..bad645f8668a 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -48,11 +48,15 @@ PHP_FUNCTION(stream_socket_pair) zend_long domain, type, protocol; php_stream *s1, *s2; php_socket_t pair[2]; + zval *zcontext = NULL; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(3, 3) + ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_LONG(domain) Z_PARAM_LONG(type) Z_PARAM_LONG(protocol) + Z_PARAM_OPTIONAL + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); if (0 != socketpair((int)domain, (int)type, (int)protocol, pair)) { @@ -62,10 +66,14 @@ PHP_FUNCTION(stream_socket_pair) RETURN_FALSE; } + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + s1 = php_stream_sock_open_from_socket(pair[0], 0); if (s1 == NULL) { close(pair[0]); close(pair[1]); + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Failed to open stream from socketpair"); RETURN_FALSE; } @@ -73,6 +81,7 @@ PHP_FUNCTION(stream_socket_pair) if (s2 == NULL) { php_stream_free(s1, PHP_STREAM_FREE_CLOSE); close(pair[1]); + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Failed to open stream from socketpair"); RETURN_FALSE; } @@ -86,6 +95,8 @@ PHP_FUNCTION(stream_socket_pair) add_next_index_resource(return_value, s1->res); add_next_index_resource(return_value, s2->res); + + php_stream_error_operation_end(context); } /* }}} */ #endif @@ -124,6 +135,7 @@ PHP_FUNCTION(stream_socket_client) RETURN_THROWS(); } + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); if (flags & PHP_STREAM_CLIENT_PERSISTENT) { @@ -158,6 +170,7 @@ PHP_FUNCTION(stream_socket_client) (flags & PHP_STREAM_CLIENT_ASYNC_CONNECT ? STREAM_XPORT_CONNECT_ASYNC : 0), hashkey, tv_pointer, context, &errstr, &err); + php_stream_error_operation_end(context); if (stream == NULL) { /* host might contain binary characters */ @@ -215,6 +228,7 @@ PHP_FUNCTION(stream_socket_server) Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT); if (zerrno) { @@ -228,6 +242,8 @@ PHP_FUNCTION(stream_socket_server) STREAM_XPORT_SERVER | (int)flags, NULL, NULL, context, &errstr, &err); + php_stream_error_operation_end(context); + if (stream == NULL) { php_error_docref(NULL, E_WARNING, "Unable to connect to %s (%s)", host, errstr == NULL ? "Unknown error" : ZSTR_VAL(errstr)); } @@ -293,6 +309,8 @@ PHP_FUNCTION(stream_socket_accept) tv_pointer = &tv; } + php_stream_error_operation_begin(); + if (0 == php_stream_xport_accept(stream, &clistream, zpeername ? &peername : NULL, NULL, NULL, @@ -311,6 +329,8 @@ PHP_FUNCTION(stream_socket_accept) RETVAL_FALSE; } + php_stream_error_operation_end_for_stream(stream); + if (errstr) { zend_string_release_ex(errstr, 0); } @@ -329,10 +349,11 @@ PHP_FUNCTION(stream_socket_get_name) Z_PARAM_BOOL(want_peer) ZEND_PARSE_PARAMETERS_END(); - if (0 != php_stream_xport_get_name(stream, want_peer, - &name, - NULL, NULL - ) || !name) { + php_stream_error_operation_begin(); + int ret = php_stream_xport_get_name(stream, want_peer, &name, NULL, NULL); + php_stream_error_operation_end_for_stream(stream); + + if (0 != ret || !name) { RETURN_FALSE; } @@ -363,15 +384,18 @@ PHP_FUNCTION(stream_socket_sendto) Z_PARAM_STRING(target_addr, target_addr_len) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); if (target_addr_len) { /* parse the address */ if (FAILURE == php_network_parse_network_address_with_port(target_addr, target_addr_len, (struct sockaddr*)&sa, &sl)) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Failed to parse `%s' into a valid network address", target_addr); RETURN_FALSE; } } - RETURN_LONG(php_stream_xport_sendto(stream, data, datalen, (int)flags, target_addr_len ? &sa : NULL, sl)); + RETVAL_LONG(php_stream_xport_sendto(stream, data, datalen, (int)flags, target_addr_len ? &sa : NULL, sl)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -405,9 +429,10 @@ PHP_FUNCTION(stream_socket_recvfrom) read_buf = zend_string_alloc(to_read, 0); + php_stream_error_operation_begin(); recvd = php_stream_xport_recvfrom(stream, ZSTR_VAL(read_buf), to_read, (int)flags, NULL, NULL, - zremote ? &remote_addr : NULL - ); + zremote ? &remote_addr : NULL); + php_stream_error_operation_end_for_stream(stream); if (recvd >= 0) { if (zremote && remote_addr) { @@ -445,6 +470,8 @@ PHP_FUNCTION(stream_get_contents) RETURN_THROWS(); } + php_stream_error_operation_begin(); + if (desiredpos >= 0) { int seek_res = 0; zend_off_t position; @@ -459,6 +486,7 @@ PHP_FUNCTION(stream_get_contents) } if (seek_res != 0) { + php_stream_error_operation_end_for_stream(stream); php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", desiredpos); RETURN_FALSE; @@ -466,10 +494,11 @@ PHP_FUNCTION(stream_get_contents) } if ((contents = php_stream_copy_to_mem(stream, maxlen, 0))) { - RETURN_STR(contents); + RETVAL_STR(contents); } else { - RETURN_EMPTY_STRING(); + RETVAL_EMPTY_STRING(); } + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -480,28 +509,37 @@ PHP_FUNCTION(stream_copy_to_stream) zend_long maxlen, pos = 0; bool maxlen_is_null = 1; size_t len; + zval *zcontext = NULL; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(2, 4) + ZEND_PARSE_PARAMETERS_START(2, 5) PHP_Z_PARAM_STREAM(src) PHP_Z_PARAM_STREAM(dest) Z_PARAM_OPTIONAL Z_PARAM_LONG_OR_NULL(maxlen, maxlen_is_null) Z_PARAM_LONG(pos) + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); if (maxlen_is_null) { maxlen = PHP_STREAM_COPY_ALL; } + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + if (pos > 0 && php_stream_seek(src, pos, SEEK_SET) < 0) { + php_stream_error_operation_end(context); php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", pos); RETURN_FALSE; } if (php_stream_copy_to_stream_ex(src, dest, maxlen, &len) != SUCCESS) { - RETURN_FALSE; + RETVAL_FALSE; + } else { + RETVAL_LONG(len); } - RETURN_LONG(len); + php_stream_error_operation_end(context); } /* }}} */ @@ -516,11 +554,13 @@ PHP_FUNCTION(stream_get_meta_data) array_init(return_value); + php_stream_error_operation_begin(); if (!php_stream_populate_meta_data(stream, return_value)) { add_assoc_bool(return_value, "timed_out", 0); add_assoc_bool(return_value, "blocked", 1); add_assoc_bool(return_value, "eof", php_stream_eof(stream)); } + php_stream_error_operation_end_for_stream(stream); if (!Z_ISUNDEF(stream->wrapperdata)) { Z_ADDREF_P(&stream->wrapperdata); @@ -592,6 +632,18 @@ PHP_FUNCTION(stream_get_wrappers) } /* }}} */ +PHP_FUNCTION(stream_last_errors) +{ + ZEND_PARSE_PARAMETERS_NONE(); + php_stream_error_get_last(return_value); +} + +PHP_FUNCTION(stream_clear_errors) +{ + ZEND_PARSE_PARAMETERS_NONE(); + php_stream_error_clear_stored(); +} + /* {{{ stream_select related functions */ static int stream_array_to_fd_set(const HashTable *stream_array, fd_set *fds, php_socket_t *max_fd) { @@ -724,7 +776,7 @@ static int stream_array_emulate_read_fd_set(zval *stream_array) /* {{{ Runs the select() system call on the sets of streams with a timeout specified by tv_sec and tv_usec */ PHP_FUNCTION(stream_select) { - zval *r_array, *w_array, *e_array; + zval *r_array, *w_array, *e_array, *zcontext = NULL; struct timeval tv, *tv_p = NULL; fd_set rfds, wfds, efds; php_socket_t max_fd = 0; @@ -733,20 +785,25 @@ PHP_FUNCTION(stream_select) bool secnull; bool usecnull = 1; int set_count, max_set_count = 0; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(4, 5) + ZEND_PARSE_PARAMETERS_START(4, 6) Z_PARAM_ARRAY_EX2(r_array, 1, 1, 0) Z_PARAM_ARRAY_EX2(w_array, 1, 1, 0) Z_PARAM_ARRAY_EX2(e_array, 1, 1, 0) Z_PARAM_LONG_OR_NULL(sec, secnull) Z_PARAM_OPTIONAL Z_PARAM_LONG_OR_NULL(usec, usecnull) + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); + if (r_array != NULL) { set_count = stream_array_to_fd_set(Z_ARR_P(r_array), &rfds, &max_fd); if (set_count > max_set_count) @@ -769,6 +826,7 @@ PHP_FUNCTION(stream_select) } if (!sets) { + php_stream_error_operation_end(context); zend_value_error("No stream arrays were passed"); RETURN_THROWS(); } @@ -779,6 +837,7 @@ PHP_FUNCTION(stream_select) if (secnull && !usecnull) { if (usec != 0) { + php_stream_error_operation_end(context); zend_argument_value_error(5, "must be null when argument #4 ($seconds) is null"); RETURN_THROWS(); } @@ -787,9 +846,11 @@ PHP_FUNCTION(stream_select) /* If seconds is not set to null, build the timeval, else we wait indefinitely */ if (!secnull) { if (sec < 0) { + php_stream_error_operation_end(context); zend_argument_value_error(4, "must be greater than or equal to 0"); RETURN_THROWS(); } else if (usec < 0) { + php_stream_error_operation_end(context); zend_argument_value_error(5, "must be greater than or equal to 0"); RETURN_THROWS(); } @@ -806,6 +867,7 @@ PHP_FUNCTION(stream_select) if (r_array != NULL) { retval = stream_array_emulate_read_fd_set(r_array); if (retval > 0) { + php_stream_error_operation_end(context); if (w_array != NULL) { zval_ptr_dtor(w_array); ZVAL_EMPTY_ARRAY(w_array); @@ -819,6 +881,7 @@ PHP_FUNCTION(stream_select) } retval = php_select(max_fd+1, &rfds, &wfds, &efds, tv_p); + php_stream_error_operation_end(context); if (retval == -1) { php_error_docref(NULL, E_WARNING, "Unable to select [%d]: %s (max_fd=" PHP_SOCKET_FMT ")", @@ -1145,6 +1208,24 @@ PHP_FUNCTION(stream_context_get_default) } /* }}} */ +/* Check if options contain stream error handling settings */ +static bool php_stream_context_options_has_error_settings(const HashTable *options) +{ + zval *stream_options = zend_hash_str_find(options, ZEND_STRL("stream")); + if (!stream_options) { + return false; + } + + ZVAL_DEREF(stream_options); + if (Z_TYPE_P(stream_options) != IS_ARRAY) { + return false; + } + + return zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_mode")) + || zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_store")) + || zend_hash_str_exists(Z_ARRVAL_P(stream_options), ZEND_STRL("error_handler")); +} + /* {{{ Set default file/stream context, returns the context as a resource */ PHP_FUNCTION(stream_context_set_default) { @@ -1160,6 +1241,11 @@ PHP_FUNCTION(stream_context_set_default) } context = FG(default_context); + if (php_stream_context_options_has_error_settings(options)) { + zend_value_error("Stream error handling options cannot be set on the default context"); + RETURN_THROWS(); + } + if (parse_context_options(context, options) == FAILURE) { RETURN_THROWS(); } @@ -1339,11 +1425,13 @@ PHP_FUNCTION(stream_get_line) max_length = PHP_SOCK_CHUNK_SIZE; } + php_stream_error_operation_begin(); if ((buf = php_stream_get_record(stream, max_length, str, str_len))) { - RETURN_STR(buf); + RETVAL_STR(buf); } else { - RETURN_FALSE; + RETVAL_FALSE; } + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1359,7 +1447,9 @@ PHP_FUNCTION(stream_set_blocking) Z_PARAM_BOOL(block) ZEND_PARSE_PARAMETERS_END(); - RETURN_BOOL(-1 != php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block, NULL)); + php_stream_error_operation_begin(); + RETVAL_BOOL(-1 != php_stream_set_option(stream, PHP_STREAM_OPTION_BLOCKING, block, NULL)); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ @@ -1400,7 +1490,9 @@ PHP_FUNCTION(stream_set_timeout) } #endif - RETURN_BOOL(PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)); + php_stream_error_operation_begin(); + RETVAL_BOOL(PHP_STREAM_OPTION_RETURN_OK == php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &t)); + php_stream_error_operation_end_for_stream(stream); } #endif /* HAVE_SYS_TIME_H || defined(PHP_WIN32) */ /* }}} */ @@ -1420,12 +1512,14 @@ PHP_FUNCTION(stream_set_write_buffer) buff = arg2; + php_stream_error_operation_begin(); /* if buff is 0 then set to non-buffered */ if (buff == 0) { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); } else { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_FULL, &buff); } + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(ret == 0 ? 0 : EOF); } @@ -1456,7 +1550,9 @@ PHP_FUNCTION(stream_set_chunk_size) RETURN_THROWS(); } + php_stream_error_operation_begin(); ret = php_stream_set_option(stream, PHP_STREAM_OPTION_SET_CHUNK_SIZE, (int)csize, NULL); + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(ret > 0 ? (zend_long)ret : (zend_long)EOF); } @@ -1477,12 +1573,14 @@ PHP_FUNCTION(stream_set_read_buffer) buff = arg2; + php_stream_error_operation_begin(); /* if buff is 0 then set to non-buffered */ if (buff == 0) { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); } else { ret = php_stream_set_option(stream, PHP_STREAM_OPTION_READ_BUFFER, PHP_STREAM_BUFFER_FULL, &buff); } + php_stream_error_operation_end_for_stream(stream); RETURN_LONG(ret == 0 ? 0 : EOF); } @@ -1504,11 +1602,14 @@ PHP_FUNCTION(stream_socket_enable_crypto) PHP_Z_PARAM_STREAM_OR_NULL(sessstream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + if (enable) { if (cryptokindnull) { zval *val; if (!GET_CTX_OPT(stream, "ssl", "crypto_method", val)) { + php_stream_error_operation_end_for_stream(stream); zend_argument_value_error(3, "must be specified when enabling encryption"); RETURN_THROWS(); } @@ -1517,11 +1618,13 @@ PHP_FUNCTION(stream_socket_enable_crypto) } if (php_stream_xport_crypto_setup(stream, cryptokind, sessstream) < 0) { + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } } ret = php_stream_xport_crypto_enable(stream, enable); + php_stream_error_operation_end_for_stream(stream); switch (ret) { case -1: RETURN_FALSE; @@ -1535,6 +1638,18 @@ PHP_FUNCTION(stream_socket_enable_crypto) } /* }}} */ +/* Get crypto status */ +PHP_FUNCTION(stream_socket_get_crypto_status) +{ + php_stream *stream; + + ZEND_PARSE_PARAMETERS_START(1, 1) + PHP_Z_PARAM_STREAM(stream) + ZEND_PARSE_PARAMETERS_END(); + + RETURN_LONG(php_stream_xport_crypto_get_status(stream)); +} + /* {{{ Determine what file will be opened by calls to fopen() with a relative path */ PHP_FUNCTION(stream_resolve_include_path) { @@ -1557,12 +1672,15 @@ PHP_FUNCTION(stream_resolve_include_path) /* {{{ */ PHP_FUNCTION(stream_is_local) { - zval *zstream; + zval *zstream, *zcontext = NULL; php_stream *stream = NULL; php_stream_wrapper *wrapper = NULL; + php_stream_context *context = NULL; - ZEND_PARSE_PARAMETERS_START(1, 1) + ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_ZVAL(zstream) + Z_PARAM_OPTIONAL + Z_PARAM_RESOURCE_OR_NULL(zcontext) ZEND_PARSE_PARAMETERS_END(); if (Z_TYPE_P(zstream) == IS_RESOURCE) { @@ -1573,7 +1691,10 @@ PHP_FUNCTION(stream_is_local) RETURN_THROWS(); } + php_stream_error_operation_begin(); + context = php_stream_context_from_zval(zcontext, 0); wrapper = php_stream_locate_url_wrapper(Z_STRVAL_P(zstream), NULL, 0); + php_stream_error_operation_end(context); } RETURN_BOOL(wrapper && wrapper->is_url == 0); @@ -1589,7 +1710,9 @@ PHP_FUNCTION(stream_supports_lock) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); - RETURN_BOOL(php_stream_supports_lock(stream)); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_supports_lock(stream)); + php_stream_error_operation_end_for_stream(stream); } /* {{{ Check if a stream is a TTY. */ @@ -1602,6 +1725,8 @@ PHP_FUNCTION(stream_isatty) PHP_Z_PARAM_STREAM(stream) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + /* get the fd. * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting. * It is only used here so that the buffered data warning is not displayed. @@ -1611,8 +1736,10 @@ PHP_FUNCTION(stream_isatty) } else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) { php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0); } else { + php_stream_error_operation_end_for_stream(stream); RETURN_FALSE; } + php_stream_error_operation_end_for_stream(stream); #ifdef PHP_WIN32 /* Check if the Windows standard handle is redirected to file */ @@ -1642,6 +1769,8 @@ PHP_FUNCTION(sapi_windows_vt100_support) Z_PARAM_BOOL_OR_NULL(enable, enable_is_null) ZEND_PARSE_PARAMETERS_END(); + php_stream_error_operation_begin(); + /* get the fd. * NB: Most other code will NOT use the PHP_STREAM_CAST_INTERNAL flag when casting. * It is only used here so that the buffered data warning is not displayed. @@ -1651,6 +1780,7 @@ PHP_FUNCTION(sapi_windows_vt100_support) } else if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL) == SUCCESS) { php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL, (void*)&fileno, 0); } else { + php_stream_error_operation_end_for_stream(stream); if (!enable_is_null) { php_error_docref( NULL, @@ -1660,6 +1790,7 @@ PHP_FUNCTION(sapi_windows_vt100_support) } RETURN_FALSE; } + php_stream_error_operation_end_for_stream(stream); /* Check if the file descriptor is a console */ if (!php_win32_console_fileno_is_console(fileno)) { @@ -1699,7 +1830,9 @@ PHP_FUNCTION(stream_socket_shutdown) RETURN_THROWS(); } - RETURN_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how) == 0); + php_stream_error_operation_begin(); + RETVAL_BOOL(php_stream_xport_shutdown(stream, (stream_shutdown_t)how) == 0); + php_stream_error_operation_end_for_stream(stream); } /* }}} */ #endif diff --git a/ext/standard/string.c b/ext/standard/string.c index ef9e66ab53f8..006adc4467d3 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -914,7 +914,7 @@ PHP_FUNCTION(explode) ZEND_PARSE_PARAMETERS_END(); if (ZSTR_LEN(delim) == 0) { - zend_argument_must_not_be_empty_error(1); + zend_argument_value_error(1, "must not be empty, use str_split() to split a string into characters"); RETURN_THROWS(); } @@ -3760,9 +3760,9 @@ PHPAPI void php_stripcslashes(zend_string *str) case 'f': *target++='\f'; nlen--; break; case '\\': *target++='\\'; nlen--; break; case 'x': - if (source+1 < end && isxdigit((int)(*(source+1)))) { + if (source+1 < end && isxdigit((unsigned char)source[1])) { numtmp[0] = *++source; - if (source+1 < end && isxdigit((int)(*(source+1)))) { + if (source+1 < end && isxdigit((unsigned char)source[1])) { numtmp[1] = *++source; numtmp[2] = '\0'; nlen-=3; @@ -4617,7 +4617,7 @@ PHP_FUNCTION(hebrev) do { if (block_type == _HEB_BLOCK_TYPE_HEB) { - while ((isheb((int)*(tmp+1)) || _isblank((int)*(tmp+1)) || ispunct((int)*(tmp+1)) || (int)*(tmp+1)=='\n' ) && block_end block_start) { + while ((_isblank((int)*tmp) || ispunct((unsigned char)*tmp)) && *tmp!='/' && *tmp!='-' && block_end > block_start) { tmp--; block_end--; } @@ -5012,7 +5012,7 @@ PHP_FUNCTION(parse_str) size_t arglen; ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_STRING(arg, arglen) + Z_PARAM_PATH(arg, arglen) Z_PARAM_ZVAL(arrayArg) ZEND_PARSE_PARAMETERS_END(); @@ -5067,7 +5067,7 @@ static bool php_tag_find(char *tag, size_t len, const char *set) { done = true; break; default: - if (!isspace((int)c)) { + if (!isspace((unsigned char)c)) { if (state == 0) { state=1; } @@ -5157,7 +5157,7 @@ PHPAPI size_t php_strip_tags_ex(char *rbuf, size_t len, const char *allow, size_ if (in_q) { break; } - if (isspace(*(p + 1)) && !allow_tag_spaces) { + if (isspace((unsigned char)p[1]) && !allow_tag_spaces) { *(rp++) = c; break; } @@ -5204,7 +5204,7 @@ PHPAPI size_t php_strip_tags_ex(char *rbuf, size_t len, const char *allow, size_ if (in_q) { break; } - if (isspace(*(p + 1)) && !allow_tag_spaces) { + if (isspace((unsigned char)p[1]) && !allow_tag_spaces) { goto reg_char_1; } depth++; diff --git a/ext/standard/strnatcmp.c b/ext/standard/strnatcmp.c index 3c3f5a99232f..583cbb5dc47a 100644 --- a/ext/standard/strnatcmp.c +++ b/ext/standard/strnatcmp.c @@ -40,12 +40,12 @@ compare_right(char const **a, char const *aend, char const **b, char const *bend both numbers to know that they have the same magnitude, so we remember it in BIAS. */ for(;; (*a)++, (*b)++) { - if ((*a == aend || !isdigit((int)(unsigned char)**a)) && - (*b == bend || !isdigit((int)(unsigned char)**b))) + if ((*a == aend || !isdigit((unsigned char)**a)) && + (*b == bend || !isdigit((unsigned char)**b))) return bias; - else if (*a == aend || !isdigit((int)(unsigned char)**a)) + else if (*a == aend || !isdigit((unsigned char)**a)) return -1; - else if (*b == bend || !isdigit((int)(unsigned char)**b)) + else if (*b == bend || !isdigit((unsigned char)**b)) return +1; else if (**a < **b) { if (!bias) @@ -67,12 +67,12 @@ compare_left(char const **a, char const *aend, char const **b, char const *bend) /* Compare two left-aligned numbers: the first to have a different value wins. */ for(;; (*a)++, (*b)++) { - if ((*a == aend || !isdigit((int)(unsigned char)**a)) && - (*b == bend || !isdigit((int)(unsigned char)**b))) + if ((*a == aend || !isdigit((unsigned char)**a)) && + (*b == bend || !isdigit((unsigned char)**b))) return 0; - else if (*a == aend || !isdigit((int)(unsigned char)**a)) + else if (*a == aend || !isdigit((unsigned char)**a)) return -1; - else if (*b == bend || !isdigit((int)(unsigned char)**b)) + else if (*b == bend || !isdigit((unsigned char)**b)) return +1; else if (**a < **b) return -1; @@ -103,27 +103,27 @@ PHPAPI int strnatcmp_ex(char const *a, size_t a_len, char const *b, size_t b_len ca = *ap; cb = *bp; /* skip over leading zeros */ - while (ca == '0' && (ap+1 < aend) && isdigit((int)(unsigned char)*(ap+1))) { + while (ca == '0' && (ap+1 < aend) && isdigit((unsigned char)ap[1])) { ca = *++ap; } - while (cb == '0' && (bp+1 < bend) && isdigit((int)(unsigned char)*(bp+1))) { + while (cb == '0' && (bp+1 < bend) && isdigit((unsigned char)bp[1])) { cb = *++bp; } while (1) { /* Skip consecutive whitespace */ - while (isspace((int)(unsigned char)ca)) { + while (isspace(ca)) { ca = *++ap; } - while (isspace((int)(unsigned char)cb)) { + while (isspace(cb)) { cb = *++bp; } /* process run of digits */ - if (isdigit((int)(unsigned char)ca) && isdigit((int)(unsigned char)cb)) { + if (isdigit(ca) && isdigit(cb)) { fractional = (ca == '0' || cb == '0'); if (fractional) @@ -147,8 +147,8 @@ PHPAPI int strnatcmp_ex(char const *a, size_t a_len, char const *b, size_t b_len } if (is_case_insensitive) { - ca = toupper((int)(unsigned char)ca); - cb = toupper((int)(unsigned char)cb); + ca = toupper(ca); + cb = toupper(cb); } if (ca < cb) diff --git a/ext/standard/tests/GHSA-96wq-48vp-hh57.phpt b/ext/standard/tests/GHSA-96wq-48vp-hh57.phpt new file mode 100644 index 000000000000..cf9a40062f84 --- /dev/null +++ b/ext/standard/tests/GHSA-96wq-48vp-hh57.phpt @@ -0,0 +1,22 @@ +--TEST-- +GHSA-96wq-48vp-hh57: signed integer overflow of char array offset +--CREDITS-- +Aleksey Solovev (Positive Technologies) +--INI-- +memory_limit=3G +--SKIPIF-- + +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/ext/standard/tests/array/array_product_packed_long_overflow.phpt b/ext/standard/tests/array/array_product_packed_long_overflow.phpt new file mode 100644 index 000000000000..7c1d8a0adebd --- /dev/null +++ b/ext/standard/tests/array/array_product_packed_long_overflow.phpt @@ -0,0 +1,22 @@ +--TEST-- +array_product() packed long overflow continues in double mode +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/standard/tests/array/array_reduce_accumulator_refcount.phpt b/ext/standard/tests/array/array_reduce_accumulator_refcount.phpt new file mode 100644 index 000000000000..f2a0639b3456 --- /dev/null +++ b/ext/standard/tests/array/array_reduce_accumulator_refcount.phpt @@ -0,0 +1,35 @@ +--TEST-- +array_reduce(): accumulator refcount stays low during callback +--FILE-- + +--EXPECT-- +array(0) interned { +} +array(1) packed refcount(2){ + [0]=> + int(1) +} +array(2) packed refcount(2){ + [0]=> + int(1) + [1]=> + int(2) +} +array(3) packed refcount(2){ + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} diff --git a/ext/standard/tests/array/array_sum_packed_long_overflow.phpt b/ext/standard/tests/array/array_sum_packed_long_overflow.phpt new file mode 100644 index 000000000000..22e4f3be52aa --- /dev/null +++ b/ext/standard/tests/array/array_sum_packed_long_overflow.phpt @@ -0,0 +1,22 @@ +--TEST-- +array_sum() packed long overflow continues in double mode +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/standard/tests/array/array_sum_product_integration.phpt b/ext/standard/tests/array/array_sum_product_integration.phpt new file mode 100644 index 000000000000..4b83042a3b45 --- /dev/null +++ b/ext/standard/tests/array/array_sum_product_integration.phpt @@ -0,0 +1,32 @@ +--TEST-- +array_sum()/array_product(): PHP 8.3 nested-array warning and array_column() integration +--FILE-- + 'Pen', 'price' => 3], + ['name' => 'Paper', 'price' => 5], +]; +$prices = array_column($products, 'price'); +var_dump($prices === [3, 5]); +var_dump(array_sum($prices) === 8); +var_dump(array_product($prices) === 15); + +echo "-- PHP 8.3: nested array emits warning and is skipped --\n"; +var_dump(array_sum([1, [2], 3])); +var_dump(array_product([2, [3], 4])); + +?> +--EXPECTF-- +-- array_column() + array_sum()/array_product() integration -- +bool(true) +bool(true) +bool(true) +-- PHP 8.3: nested array emits warning and is skipped -- + +Warning: array_sum(): Addition is not supported on type array in %s on line %d +int(4) + +Warning: array_product(): Multiplication is not supported on type array in %s on line %d +int(8) diff --git a/ext/standard/tests/file/php_fd_wrapper_03.phpt b/ext/standard/tests/file/php_fd_wrapper_03.phpt index 991c497f5e19..a19d1f5acd94 100644 --- a/ext/standard/tests/file/php_fd_wrapper_03.phpt +++ b/ext/standard/tests/file/php_fd_wrapper_03.phpt @@ -10,7 +10,6 @@ fopen("php://fd/1/", "w"); echo "\nDone.\n"; ?> --EXPECTF-- -Warning: fopen(): Invalid php:// URL specified in %s on line %d Warning: fopen(php://fd): Failed to open stream: operation failed in %s on line 2 diff --git a/ext/standard/tests/filters/chunked_002.phpt b/ext/standard/tests/filters/chunked_002.phpt new file mode 100644 index 000000000000..7114e278e3bd --- /dev/null +++ b/ext/standard/tests/filters/chunked_002.phpt @@ -0,0 +1,58 @@ +--TEST-- +Dechunk write filter state must survive stream seek +--FILE-- + +--EXPECT-- +string(5) "Hello" +string(12) "Hello, World" +string(12) "Hello, World" +string(5) "Hello" +int(5) diff --git a/ext/standard/tests/filters/convert_filter_write_seek_modes.phpt b/ext/standard/tests/filters/convert_filter_write_seek_modes.phpt new file mode 100644 index 000000000000..0a2d4114bf3c --- /dev/null +++ b/ext/standard/tests/filters/convert_filter_write_seek_modes.phpt @@ -0,0 +1,61 @@ +--TEST-- +convert.* write filter: write_seek_mode parameter +--FILE-- + 'preserve']); + fwrite($fp, 'Hello'); + var_dump(fseek($fp, 0, SEEK_SET) === 0); + var_dump(fseek($fp, 100, SEEK_SET) === 0); + fclose($fp); + + /* reset: seeks succeed, callback dispatched */ + $fp = fopen('php://memory', 'w+'); + stream_filter_append($fp, $name, STREAM_FILTER_WRITE, + ['write_seek_mode' => 'reset']); + fwrite($fp, 'Hello'); + var_dump(fseek($fp, 0, SEEK_SET) === 0); + fclose($fp); + + /* strict: seek fails */ + $fp = fopen('php://memory', 'w+'); + stream_filter_append($fp, $name, STREAM_FILTER_WRITE, + ['write_seek_mode' => 'strict']); + fwrite($fp, 'Hello'); + var_dump(@fseek($fp, 0, SEEK_SET) === -1); + fclose($fp); + + /* invalid: ValueError */ + $fp = fopen('php://memory', 'w+'); + stream_filter_append($fp, $name, STREAM_FILTER_WRITE, + ['write_seek_mode' => 42]); + if ($fp) { + fclose($fp); + } +} +?> +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) + +Warning: stream_filter_append(): "write_seek_mode" filter parameter must be one of "preserve", "reset", or "strict" in %s + +Warning: stream_filter_append(): Unable to create or locate filter "convert.base64-encode" in %s +bool(true) +bool(true) +bool(true) +bool(true) + +Warning: stream_filter_append(): "write_seek_mode" filter parameter must be one of "preserve", "reset", or "strict" in %s + +Warning: stream_filter_append(): Unable to create or locate filter "convert.quoted-printable-encode" in %s diff --git a/ext/standard/tests/filters/php_user_filter_04.phpt b/ext/standard/tests/filters/php_user_filter_04.phpt index 72f874f21ad4..6dc62779aa90 100644 --- a/ext/standard/tests/filters/php_user_filter_04.phpt +++ b/ext/standard/tests/filters/php_user_filter_04.phpt @@ -25,4 +25,4 @@ class InvalidSeekFilter extends php_user_filter ?> --EXPECTF-- -Fatal error: Declaration of InvalidSeekFilter::seek($offset): bool must be compatible with php_user_filter::seek(int $offset, int $whence): bool in %s on line %d +Fatal error: Declaration of InvalidSeekFilter::seek($offset): bool must be compatible with php_user_filter::seek(int $offset, int $whence, int $chain): bool in %s on line %d diff --git a/ext/standard/tests/filters/user_filter_seek_01.phpt b/ext/standard/tests/filters/user_filter_seek_01.phpt index 31ec95ca6aa6..6023ced1d0ee 100644 --- a/ext/standard/tests/filters/user_filter_seek_01.phpt +++ b/ext/standard/tests/filters/user_filter_seek_01.phpt @@ -39,7 +39,7 @@ class RotateFilter extends php_user_filter public function onClose(): void {} - public function seek(int $offset, int $whence): bool + public function seek(int $offset, int $whence, int $chain): bool { // Stateless filter - always seekable to any position return true; diff --git a/ext/standard/tests/filters/user_filter_seek_02.phpt b/ext/standard/tests/filters/user_filter_seek_02.phpt index 39f4c3c66243..f728e75cdca9 100644 --- a/ext/standard/tests/filters/user_filter_seek_02.phpt +++ b/ext/standard/tests/filters/user_filter_seek_02.phpt @@ -28,7 +28,7 @@ class CountingFilter extends php_user_filter public function onClose(): void {} - public function seek(int $offset, int $whence): bool + public function seek(int $offset, int $whence, int $chain): bool { if ($offset === 0 && $whence === SEEK_SET) { $this->count = 0; diff --git a/ext/standard/tests/filters/user_filter_seek_03.phpt b/ext/standard/tests/filters/user_filter_seek_03.phpt new file mode 100644 index 000000000000..bc814e8ed160 --- /dev/null +++ b/ext/standard/tests/filters/user_filter_seek_03.phpt @@ -0,0 +1,67 @@ +--TEST-- +php_user_filter::seek receives chain identifier (read vs write) +--FILE-- +datalen; + stream_bucket_append($out, $bucket); + } + return PSFS_PASS_ON; + } + + public function onCreate(): bool + { + return true; + } + + public function onClose(): void {} + + public function seek(int $offset, int $whence, int $chain): bool + { + $name = match ($chain) { + STREAM_FILTER_READ => 'read', + STREAM_FILTER_WRITE => 'write', + default => 'other', + }; + self::$log[] = "$name:$offset:$whence"; + return true; + } +} + +stream_filter_register('test.tracking', 'TrackingFilter'); + +$file = __DIR__ . '/user_filter_seek_03.txt'; +file_put_contents($file, "abcdefghij"); + +/* Read chain: seek-to-start should dispatch with STREAM_FILTER_READ */ +TrackingFilter::$log = []; +$fp = fopen($file, 'r'); +stream_filter_append($fp, 'test.tracking', STREAM_FILTER_READ); +fread($fp, 4); +fseek($fp, 0, SEEK_SET); +fclose($fp); +echo "Read chain log: " . implode(',', TrackingFilter::$log) . "\n"; + +/* Write chain: any seek should dispatch with STREAM_FILTER_WRITE */ +TrackingFilter::$log = []; +$fp = fopen($file, 'w+'); +stream_filter_append($fp, 'test.tracking', STREAM_FILTER_WRITE); +fwrite($fp, "xyz"); +fseek($fp, 0, SEEK_SET); +fseek($fp, 5, SEEK_SET); +fclose($fp); +echo "Write chain log: " . implode(',', TrackingFilter::$log) . "\n"; + +unlink($file); + +?> +--EXPECT-- +Read chain log: read:0:0 +Write chain log: write:0:0,write:5:0 diff --git a/ext/standard/tests/general_functions/ini_get_all.phpt b/ext/standard/tests/general_functions/ini_get_all.phpt index 2b71b474a013..c54b39ac3325 100644 --- a/ext/standard/tests/general_functions/ini_get_all.phpt +++ b/ext/standard/tests/general_functions/ini_get_all.phpt @@ -33,29 +33,35 @@ array(0) { } array(3) { ["pcre.backtrack_limit"]=> - array(3) { + array(4) { ["global_value"]=> string(7) "1000000" ["local_value"]=> string(7) "1000000" + ["builtin_default_value"]=> + string(7) "1000000" ["access"]=> int(7) } ["pcre.jit"]=> - array(3) { + array(4) { ["global_value"]=> string(1) "1" ["local_value"]=> string(1) "1" + ["builtin_default_value"]=> + string(1) "1" ["access"]=> int(7) } ["pcre.recursion_limit"]=> - array(3) { + array(4) { ["global_value"]=> string(6) "100000" ["local_value"]=> string(6) "100000" + ["builtin_default_value"]=> + string(6) "100000" ["access"]=> int(7) } diff --git a/ext/standard/tests/general_functions/ini_get_all_builtin_default_value.phpt b/ext/standard/tests/general_functions/ini_get_all_builtin_default_value.phpt new file mode 100644 index 000000000000..82de8781caa7 --- /dev/null +++ b/ext/standard/tests/general_functions/ini_get_all_builtin_default_value.phpt @@ -0,0 +1,33 @@ +--TEST-- +ini_get_all() exposes the built-in default value independent of configuration and runtime changes +--INI-- +precision=8 +--FILE-- + +--EXPECT-- +string(1) "8" +string(1) "8" +string(2) "14" +string(1) "8" +string(1) "3" +string(2) "14" +Done diff --git a/ext/standard/tests/general_functions/ini_get_all_builtin_default_value_null.phpt b/ext/standard/tests/general_functions/ini_get_all_builtin_default_value_null.phpt new file mode 100644 index 000000000000..efd86eb947e4 --- /dev/null +++ b/ext/standard/tests/general_functions/ini_get_all_builtin_default_value_null.phpt @@ -0,0 +1,33 @@ +--TEST-- +ini_get_all() reports a null built-in default value for a directive that has no compiled-in default +--INI-- +error_append_string=FOO +--FILE-- + +--EXPECT-- +string(3) "FOO" +string(3) "FOO" +NULL +string(3) "FOO" +string(3) "BAR" +NULL +Done diff --git a/ext/standard/tests/general_functions/proc_open_cwd_null_bytes.phpt b/ext/standard/tests/general_functions/proc_open_cwd_null_bytes.phpt new file mode 100644 index 000000000000..faa86c824177 --- /dev/null +++ b/ext/standard/tests/general_functions/proc_open_cwd_null_bytes.phpt @@ -0,0 +1,18 @@ +--TEST-- +proc_open() rejects null bytes in cwd +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +proc_open(): Argument #4 ($cwd) must not contain any null bytes diff --git a/ext/standard/tests/http/gh22171.phpt b/ext/standard/tests/http/gh22171.phpt new file mode 100644 index 000000000000..0c1aa150f60f --- /dev/null +++ b/ext/standard/tests/http/gh22171.phpt @@ -0,0 +1,47 @@ +--TEST-- +GH-22171 (http(s) stream wrapper sends a corrupted Authorization header for percent-encoded userinfo) +--SKIPIF-- + +--INI-- +allow_url_fopen=1 +--FILE-- + $pid, 'uri' => $uri] = http_server($responses, $output); + + $url = preg_replace('(^http://)', 'http://' . $userinfo . '@', $uri); + file_get_contents($url); + + fseek($output, 0, SEEK_SET); + $output = stream_get_contents($output); + + http_server_kill($pid); + + if (preg_match('(^Authorization:\s*Basic\s+(\S+))mi', $output, $m)) { + $decoded = base64_decode($m[1]); + } else { + $decoded = ''; + } + + echo "=== {$label} ===", PHP_EOL; + echo " decoded : ", addcslashes($decoded, "\0..\37"), PHP_EOL; + echo " result : ", ($decoded === $expected ? "OK" : "CORRUPT"), PHP_EOL; +} + +probe('user only', '%76%6f%72%74%66%75', 'vortfu:'); +probe('user + password', '%76%6f%72%74%66%75:%70%61%73%73%77%6f%72%64', 'vortfu:password'); +?> +--EXPECT-- +=== user only === + decoded : vortfu: + result : OK +=== user + password === + decoded : vortfu:password + result : OK diff --git a/ext/standard/tests/setcookie_samesite_validation.phpt b/ext/standard/tests/setcookie_samesite_validation.phpt new file mode 100644 index 000000000000..3827f04fe8d4 --- /dev/null +++ b/ext/standard/tests/setcookie_samesite_validation.phpt @@ -0,0 +1,52 @@ +--TEST-- +setcookie() and setrawcookie() validate samesite option +--FILE-- + 'Strict'])); +var_dump(setcookie('test', 'value', ['samesite' => 'Lax'])); +var_dump(setcookie('test', 'value', ['samesite' => 'None'])); +var_dump(setcookie('test', 'value', ['samesite' => ''])); + +// Case-insensitive +var_dump(setcookie('test', 'value', ['samesite' => 'strict'])); +var_dump(setcookie('test', 'value', ['samesite' => 'LAX'])); +var_dump(setcookie('test', 'value', ['samesite' => 'NONE'])); + +// setrawcookie uses the same validation +var_dump(setrawcookie('test', 'value', ['samesite' => 'Lax'])); + +// Invalid values +try { + setcookie('test', 'value', ['samesite' => 'Invalid']); +} catch (ValueError $e) { + echo $e->getMessage() . "\n"; +} + +try { + setcookie('test', 'value', ['samesite' => "Strict\r\nX-Injected: evil"]); +} catch (ValueError $e) { + echo $e->getMessage() . "\n"; +} + +try { + setrawcookie('test', 'value', ['samesite' => 'Invalid']); +} catch (ValueError $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +setcookie(): "samesite" option must be "Strict", "Lax", "None", or "" +setcookie(): "samesite" option must be "Strict", "Lax", "None", or "" +setrawcookie(): "samesite" option must be "Strict", "Lax", "None", or "" diff --git a/ext/standard/tests/streams/gh14506.phpt b/ext/standard/tests/streams/gh14506.phpt index f83eba4f1ff3..3d82350221d5 100644 --- a/ext/standard/tests/streams/gh14506.phpt +++ b/ext/standard/tests/streams/gh14506.phpt @@ -86,10 +86,10 @@ Warning: fclose(): cannot close the provided stream, as it must not be manually Warning: fclose(): cannot close the provided stream, as it must not be manually closed in %s on line %d -Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d - Warning: fclose(): cannot close the provided stream, as it must not be manually closed in %s on line %d +Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d + Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d No stream arrays were passed fclose(): Argument #1 ($stream) must be an open stream resource diff --git a/ext/standard/tests/streams/gh22200.phpt b/ext/standard/tests/streams/gh22200.phpt new file mode 100644 index 000000000000..01a58bbf8cf2 --- /dev/null +++ b/ext/standard/tests/streams/gh22200.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-22200 (Memory leaks in the stream errors code on reentrant error handler) +--FILE-- + +--EXPECT-- +done diff --git a/ext/standard/tests/streams/stream_errors_error_has_code.phpt b/ext/standard/tests/streams/stream_errors_error_has_code.phpt new file mode 100644 index 000000000000..6cd6b70411a6 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_error_has_code.phpt @@ -0,0 +1,30 @@ +--TEST-- +Stream errors - multiple operations stored +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::All, + ] +]); + +// First operation +$stream1 = fopen('php://nonexistent1', 'r', false, $context); +$error1 = stream_last_errors()[0]; + +// Second operation +$stream2 = fopen('php://nonexistent2', 'r', false, $context); +$error2 = stream_last_errors()[0]; + +// Should get the most recent error (second operation) +if ($error2) { + echo "Got most recent error\n"; + echo "Param contains 'nonexistent2': " . (strpos($error2->param ?? '', 'nonexistent2') !== false ? 'yes' : 'no') . "\n"; +} + +?> +--EXPECT-- +Got most recent error +Param contains 'nonexistent2': yes diff --git a/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt new file mode 100644 index 000000000000..37f5d39bfa75 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_exception_mode_terminal.phpt @@ -0,0 +1,31 @@ +--TEST-- +Stream errors - exception mode for terminal errors +--FILE-- + [ + 'error_mode' => StreamErrorMode::Exception, + ] +]); + +try { + $stream = fopen('php://nonexistent', 'r', false, $context); +} catch (StreamException $e) { + echo "Caught: " . $e->getMessage() . "\n"; + echo "Code: " . $e->getCode() . "\n"; + + $errors = $e->getErrors(); + if (!empty($errors)) { + $error = $errors[0]; + echo "Wrapper: " . $error->wrapperName . "\n"; + echo "Error code name: " . $error->code->name . "\n"; + } +} + +?> +--EXPECTF-- +Caught: Failed to open stream: operation failed +Code: 20 +Wrapper: PHP +Error code name: OpenFailed diff --git a/ext/standard/tests/streams/stream_errors_invalid_types.phpt b/ext/standard/tests/streams/stream_errors_invalid_types.phpt new file mode 100644 index 000000000000..1b3ab5e8931e --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_invalid_types.phpt @@ -0,0 +1,37 @@ +--TEST-- +Stream errors - invalid enum type throws TypeError +--FILE-- + [ + 'error_mode' => 'invalid', + ] + ]); + + fopen('php://nonexistent', 'r', false, $context); +} catch (TypeError $e) { + echo "Caught TypeError for error_mode\n"; +} + +try { + $context = stream_context_create([ + 'stream' => [ + 'error_store' => 123, + ] + ]); + + fopen('php://nonexistent', 'r', false, $context); +} catch (TypeError $e) { + echo "Caught TypeError for error_store\n"; +} + +?> +--EXPECTF-- + +Warning: fopen(php://nonexistent): Failed to open stream: operation failed in %s on line %d +Caught TypeError for error_mode + +Warning: fopen(php://nonexistent): Failed to open stream: operation failed in %s on line %d +Caught TypeError for error_store diff --git a/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt b/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt new file mode 100644 index 000000000000..49c18616dad3 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_mix_modes_storage.phpt @@ -0,0 +1,149 @@ +--TEST-- +Stream errors - silent mode with mixed error stores +--FILE-- +position += $count; + return str_repeat('x', $count + 10); + } + + public function stream_eof() { + return $this->position >= 100; + } + + public function stream_stat() { + return []; + } +} + +stream_wrapper_register('test', 'TestStream'); + +function stream_test_errors($title, $contextOptions) { + stream_clear_errors(); + $context = stream_context_create($contextOptions); + $stream = fopen('test://foo', 'r', false, $context); + try { + echo $title . "\n"; + $readin = fopen('php://stdin', 'r', false, $context); + $data = fread($stream, 10); + + $read = [$readin, $stream]; + $write = NULL; + $except = NULL; + stream_select($read, $write, $except, 0, 0, $context); + } catch (StreamException $e) { + echo 'EXCEPTION: ' . $e->getMessage() . "\n"; + } + + $errors = stream_last_errors(); + if ($errors) { + $first = $errors[0]; + echo "Error details:\n"; + echo "- Message: $first->message\n"; + echo "- Code: " . $first->code->name . "\n"; + echo "- Wrapper: $first->wrapperName\n"; + echo "- Terminating: " . ($first->terminating ? 'yes' : 'no') . "\n"; + echo "- Count: " . count($errors) . "\n"; + + foreach ($errors as $idx => $error) { + echo " [$idx] " . $error->code->name . ": " . $error->message . "\n"; + } + } else { + echo "No errors stored\n"; + } + echo "\n"; +} + +stream_test_errors('ALL', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::All, + ] +]); + +stream_test_errors('NON TERMINATING', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::NonTerminating, + ] +]); + +stream_test_errors('TERMINATING', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::Terminating, + ] +]); + +stream_test_errors('AUTO EXCEPTION', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Exception, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +stream_test_errors('AUTO ERROR', [ + 'stream' => [ + 'error_mode' => StreamErrorMode::Error, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +?> +--EXPECTF-- +ALL +Error details: +- Message: TestStream::stream_cast is not implemented! +- Code: NotImplemented +- Wrapper: user-space +- Terminating: yes +- Count: 2 + [0] NotImplemented: TestStream::stream_cast is not implemented! + [1] CastNotSupported: Cannot represent a stream of type user-space as a select()able descriptor + +NON TERMINATING +Error details: +- Message: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost +- Code: UserspaceInvalidReturn +- Wrapper: user-space +- Terminating: no +- Count: 1 + [0] UserspaceInvalidReturn: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost + +TERMINATING +Error details: +- Message: TestStream::stream_cast is not implemented! +- Code: NotImplemented +- Wrapper: user-space +- Terminating: yes +- Count: 2 + [0] NotImplemented: TestStream::stream_cast is not implemented! + [1] CastNotSupported: Cannot represent a stream of type user-space as a select()able descriptor + +AUTO EXCEPTION +EXCEPTION: TestStream::stream_cast is not implemented! +Error details: +- Message: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost +- Code: UserspaceInvalidReturn +- Wrapper: user-space +- Terminating: no +- Count: 1 + [0] UserspaceInvalidReturn: TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost + +AUTO ERROR + +Warning: fread(): TestStream::stream_read - read 10 bytes more data than requested (8202 read, 8192 max) - excess data will be lost in %s on line %d + +Warning: stream_select(): TestStream::stream_cast is not implemented! in %s on line %d + +Warning: stream_select(): Cannot represent a stream of type user-space as a select()able descriptor in %s on line %d +No errors stored diff --git a/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt b/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt new file mode 100644 index 000000000000..2575a8a795f9 --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_modes_with_auto_store.phpt @@ -0,0 +1,49 @@ +--TEST-- +Stream errors - error_store AUTO mode behavior +--FILE-- + [ + 'error_mode' => StreamErrorMode::Error, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +@fopen('php://nonexistent', 'r', false, $context1); +$errors1 = stream_last_errors(); +echo "ERROR mode AUTO: " . (!empty($errors1) ? "has error" : "no error") . "\n"; + +// AUTO with EXCEPTION mode should store NON_TERM +$context2 = stream_context_create([ + 'stream' => [ + 'error_mode' => StreamErrorMode::Exception, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +try { + fopen('php://nonexistent2', 'r', false, $context2); +} catch (StreamException $e) {} + +$errors2 = stream_last_errors(); +echo "EXCEPTION mode AUTO: " . (!empty($errors2) ? "has error" : "no error") . "\n"; + +// AUTO with SILENT mode should store ALL +$context3 = stream_context_create([ + 'stream' => [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::Auto, + ] +]); + +fopen('php://nonexistent3', 'r', false, $context3); +$errors3 = stream_last_errors(); +echo "SILENT mode AUTO: " . (!empty($errors3) ? "has error" : "no error") . "\n"; + +?> +--EXPECTF-- +ERROR mode AUTO: no error +EXCEPTION mode AUTO: %s +SILENT mode AUTO: has error diff --git a/ext/standard/tests/streams/stream_errors_set_default_context_error.phpt b/ext/standard/tests/streams/stream_errors_set_default_context_error.phpt new file mode 100644 index 000000000000..46fc0f7fd34b --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_set_default_context_error.phpt @@ -0,0 +1,18 @@ +--TEST-- +Stream errors - prohibit setting error mode in default context +--FILE-- + [ + 'error_mode' => StreamErrorMode::Exception, + ] + ]); +} catch (\ValueError $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +Stream error handling options cannot be set on the default context diff --git a/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt b/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt new file mode 100644 index 000000000000..456575affc3a --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_with_handler.phpt @@ -0,0 +1,37 @@ +--TEST-- +Stream errors - custom error handler +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + 'error_handler' => function(array $errors) use (&$handler_called) { + $handler_called = true; + echo "Handler called\n"; + foreach ($errors as $error) { + echo "Wrapper: " . $error->wrapperName . "\n"; + echo "Code: " . $error->code->name . "\n"; + echo "Message: " . $error->message . "\n"; + echo "Param: " . ($error->param ?? 'null') . "\n"; + echo "Terminating: " . ($error->terminating ? 'yes' : 'no') . "\n"; + } + } + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); + +var_dump($handler_called); + +?> +--EXPECT-- +Handler called +Wrapper: PHP +Code: OpenFailed +Message: Failed to open stream: operation failed +Param: php://nonexistent +Terminating: yes +bool(true) diff --git a/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt b/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt new file mode 100644 index 000000000000..9e356216b0dd --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_with_storage.phpt @@ -0,0 +1,29 @@ +--TEST-- +Stream errors - silent mode with error storage +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); +var_dump($stream); + +$errors = stream_last_errors(); +foreach ($errors as $error) { + echo "Has error: yes\n"; + echo "Error code: " . $error->code->name . "\n"; + echo "Error wrapper: " . $error->wrapperName . "\n"; + echo "Error message: " . $error->message . "\n"; +} + +?> +--EXPECT-- +bool(false) +Has error: yes +Error code: OpenFailed +Error wrapper: PHP +Error message: Failed to open stream: operation failed diff --git a/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt b/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt new file mode 100644 index 000000000000..8155ce60bfee --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_silent_without_storage.phpt @@ -0,0 +1,20 @@ +--TEST-- +Stream errors - error_store NONE option +--FILE-- + [ + 'error_mode' => StreamErrorMode::Silent, + 'error_store' => StreamErrorStore::None, + ] +]); + +$stream = fopen('php://nonexistent', 'r', false, $context); + +$errors = stream_last_errors(); +echo "Has error: " . (!empty($error) ? "yes" : "no") . "\n"; + +?> +--EXPECT-- +Has error: no diff --git a/ext/standard/tests/streams/stream_errors_standard_error.phpt b/ext/standard/tests/streams/stream_errors_standard_error.phpt new file mode 100644 index 000000000000..42de8cc3b17d --- /dev/null +++ b/ext/standard/tests/streams/stream_errors_standard_error.phpt @@ -0,0 +1,20 @@ +--TEST-- +Stream errors - error mode with standard error reporting +--FILE-- + [ + 'error_mode' => StreamErrorMode::Error, + ] +]); + +// This will trigger a warning +$stream = fopen('php://nonexistent', 'r', false, $context); + +var_dump($stream); + +?> +--EXPECTF-- +Warning: fopen(php://nonexistent): Failed to open stream: %s in %s on line %d +bool(false) diff --git a/ext/standard/tests/strings/explode.phpt b/ext/standard/tests/strings/explode.phpt index ea467a2d5504..248fade7605c 100644 --- a/ext/standard/tests/strings/explode.phpt +++ b/ext/standard/tests/strings/explode.phpt @@ -62,9 +62,9 @@ array ( 4 => 'd', ) d6bee42a771449205344c0938ad4f035 -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters array(1) { [0]=> string(0) "" @@ -79,7 +79,7 @@ array(1) { [0]=> string(0) "" } -explode(): Argument #1 ($separator) must not be empty +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters array(1) { [0]=> string(3) "acb" diff --git a/ext/standard/tests/strings/explode1.phpt b/ext/standard/tests/strings/explode1.phpt index 876b159821cd..022654a57643 100644 --- a/ext/standard/tests/strings/explode1.phpt +++ b/ext/standard/tests/strings/explode1.phpt @@ -91,15 +91,15 @@ var_dump( explode("b", $obj) ); --EXPECT-- *** Testing explode() for basic operations *** -- Iteration 1 -- -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters -- Iteration 2 -- -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters -- Iteration 3 -- array(1) { [0]=> @@ -201,10 +201,10 @@ array(2) { string(56) "234NULL23abcd00000TRUEFALSE-11.234444true-11.24%PHP%ZEND" } -- Iteration 7 -- -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty -explode(): Argument #1 ($separator) must not be empty +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters +explode(): Argument #1 ($separator) must not be empty, use str_split() to split a string into characters -- Iteration 8 -- array(2) { [0]=> diff --git a/ext/standard/tests/strings/parse_str_null_bytes.phpt b/ext/standard/tests/strings/parse_str_null_bytes.phpt new file mode 100644 index 000000000000..fd0d94bb0fc8 --- /dev/null +++ b/ext/standard/tests/strings/parse_str_null_bytes.phpt @@ -0,0 +1,14 @@ +--TEST-- +parse_str() rejects null bytes +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +parse_str(): Argument #1 ($string) must not contain any null bytes diff --git a/ext/standard/tests/versioning/version_compare.phpt b/ext/standard/tests/versioning/version_compare.phpt index 07550dd410f4..e22ddabfa56b 100644 --- a/ext/standard/tests/versioning/version_compare.phpt +++ b/ext/standard/tests/versioning/version_compare.phpt @@ -22,6 +22,9 @@ foreach ($special_forms as $f1) { test("1.0$f1", "1.0$f2"); } } +test("1.2.", "1.2."); +test("1.2..", "1.2.."); + print "TESTING OPERATORS\n"; foreach ($special_forms as $f1) { foreach ($special_forms as $f2) { @@ -106,6 +109,8 @@ TESTING COMPARE 1.0pl1 > 1.0rc1 1.0pl1 > 1.0 1.0pl1 = 1.0pl1 +1.2. = 1.2. +1.2.. = 1.2.. TESTING OPERATORS 1.0-dev lt 1.0-dev : false 1.0-dev < 1.0-dev : false diff --git a/ext/standard/type.c b/ext/standard/type.c index 7546216e5049..fc681a3c50a7 100644 --- a/ext/standard/type.c +++ b/ext/standard/type.c @@ -157,7 +157,7 @@ PHP_FUNCTION(intval) char *strval = Z_STRVAL_P(num); size_t strlen = Z_STRLEN_P(num); - while (isspace(*strval) && strlen) { + while (isspace((unsigned char)*strval) && strlen) { strval++; strlen--; } diff --git a/ext/standard/url.c b/ext/standard/url.c index 089dca315f43..ac303027a722 100644 --- a/ext/standard/url.c +++ b/ext/standard/url.c @@ -101,7 +101,7 @@ PHPAPI php_url *php_url_parse_ex2(char const *str, size_t length, bool *has_port p = s; while (p < e) { /* scheme = 1*[ lowalpha | digit | "+" | "-" | "." ] */ - if (!isalpha(*p) && !isdigit(*p) && *p != '+' && *p != '.' && *p != '-') { + if (!isalpha((unsigned char)*p) && !isdigit((unsigned char)*p) && *p != '+' && *p != '.' && *p != '-') { if (e + 1 < ue && e < binary_strcspn(s, ue, "?#")) { goto parse_port; } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */ @@ -130,7 +130,7 @@ PHPAPI php_url *php_url_parse_ex2(char const *str, size_t length, bool *has_port * correctly parse things like a.com:80 */ p = e + 1; - while (p < ue && isdigit(*p)) { + while (p < ue && isdigit((unsigned char)*p)) { p++; } @@ -170,7 +170,7 @@ PHPAPI php_url *php_url_parse_ex2(char const *str, size_t length, bool *has_port p = e + 1; pp = p; - while (pp < ue && pp - p < 6 && isdigit(*pp)) { + while (pp < ue && pp - p < 6 && isdigit((unsigned char)*pp)) { pp++; } @@ -589,8 +589,8 @@ PHPAPI size_t php_url_decode_ex(char *dest, const char *src, size_t src_len) if (*data == '+') { *dest = ' '; } - else if (*data == '%' && src_len >= 2 && isxdigit((int) *(data + 1)) - && isxdigit((int) *(data + 2))) { + else if (*data == '%' && src_len >= 2 && isxdigit((unsigned char)data[1]) + && isxdigit((unsigned char)data[2])) { *dest = (char) php_htoi(data + 1); data += 2; src_len -= 2; @@ -662,8 +662,8 @@ PHPAPI size_t php_raw_url_decode_ex(char *dest, const char *src, size_t src_len) const char *data = src; while (src_len--) { - if (*data == '%' && src_len >= 2 && isxdigit((int) *(data + 1)) - && isxdigit((int) *(data + 2))) { + if (*data == '%' && src_len >= 2 && isxdigit((unsigned char)data[1]) + && isxdigit((unsigned char)data[2])) { *dest = (char) php_htoi(data + 1); data += 2; src_len -= 2; @@ -730,7 +730,7 @@ PHP_FUNCTION(get_headers) c = *p; *p = '\0'; s = p + 1; - while (isspace((int)*(unsigned char *)s)) { + while (isspace((unsigned char)*s)) { s++; } diff --git a/ext/standard/url_scanner_ex.re b/ext/standard/url_scanner_ex.re index af605b8886cc..506f2d08378e 100644 --- a/ext/standard/url_scanner_ex.re +++ b/ext/standard/url_scanner_ex.re @@ -83,7 +83,7 @@ static zend_result php_ini_on_update_tags(zend_ini_entry *entry, zend_string *ne *val++ = '\0'; for (q = key; *q; q++) { - *q = tolower(*q); + *q = tolower((unsigned char)*q); } keylen = q - key; str = zend_string_init(key, keylen, 1); @@ -135,7 +135,7 @@ static zend_result php_ini_on_update_hosts(zend_ini_entry *entry, zend_string *n char *q; for (q = key; *q; q++) { - *q = tolower(*q); + *q = tolower((unsigned char)*q); } keylen = q - key; if (keylen > 0) { @@ -461,7 +461,7 @@ static inline void handle_tag(STD_PARA) } smart_str_appendl(&ctx->tag, start, YYCURSOR - start); for (i = 0; i < ZSTR_LEN(ctx->tag.s); i++) - ZSTR_VAL(ctx->tag.s)[i] = tolower((int)(unsigned char)ZSTR_VAL(ctx->tag.s)[i]); + ZSTR_VAL(ctx->tag.s)[i] = tolower((unsigned char)ZSTR_VAL(ctx->tag.s)[i]); /* intentionally using str_find here, in case the hash value is set, but the string val is changed later */ if ((ctx->lookup_data = zend_hash_str_find_ptr(ctx->tags, ZSTR_VAL(ctx->tag.s), ZSTR_LEN(ctx->tag.s))) != NULL) { ok = 1; @@ -661,7 +661,7 @@ static void php_url_scanner_ex_activate(bool is_session) ctx = &BG(url_adapt_output_ex); } - memset(ctx, 0, XtOffsetOf(url_adapt_state_ex_t, tags)); + memset(ctx, 0, offsetof(url_adapt_state_ex_t, tags)); } static void php_url_scanner_ex_deactivate(bool is_session) diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index 816a798c4b00..ae4132733bec 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -48,8 +48,9 @@ PHP_METHOD(php_user_filter, filter) PHP_METHOD(php_user_filter, seek) { - zend_long offset, whence; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &offset, &whence) == FAILURE) { + zend_long offset, whence, chain; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &offset, &whence, &chain) == FAILURE) { RETURN_THROWS(); } @@ -263,7 +264,7 @@ static zend_result userfilter_seek( { zval *obj = &thisfilter->abstract; zval retval; - zval args[2]; + zval args[3]; /* the userfilter object probably doesn't exist anymore */ if (CG(unclean_shutdown)) { @@ -289,8 +290,9 @@ static zend_result userfilter_seek( /* Setup calling arguments */ ZVAL_LONG(&args[0], offset); ZVAL_LONG(&args[1], whence); + ZVAL_LONG(&args[2], php_stream_filter_get_chain_type(stream, thisfilter)); - zend_call_known_function(seek_method, Z_OBJ_P(obj), Z_OBJCE_P(obj), &retval, 2, args, NULL); + zend_call_known_function(seek_method, Z_OBJ_P(obj), Z_OBJCE_P(obj), &retval, 3, args, NULL); zend_result ret = FAILURE; if (Z_TYPE(retval) != IS_UNDEF) { @@ -383,7 +385,8 @@ static php_stream_filter *user_filter_factory_create(const char *filtername, return NULL; } - filter = php_stream_filter_alloc(&userfilter_ops, NULL, false, PSFS_SEEKABLE_CHECK); + filter = php_stream_filter_alloc(&userfilter_ops, NULL, false, + PSFS_SEEKABLE_CHECK, PSFS_SEEKABLE_CHECK); /* filtername */ add_property_string(&obj, "filtername", filtername); diff --git a/ext/standard/user_filters.stub.php b/ext/standard/user_filters.stub.php index 475ec58e79e7..8696845b78c8 100644 --- a/ext/standard/user_filters.stub.php +++ b/ext/standard/user_filters.stub.php @@ -50,7 +50,7 @@ class php_user_filter public function filter($in, $out, &$consumed, bool $closing): int {} /** @tentative-return-type */ - public function seek(int $offset, int $whence): bool {} + public function seek(int $offset, int $whence, int $chain): bool {} /** @tentative-return-type */ public function onCreate(): bool {} diff --git a/ext/standard/user_filters_arginfo.h b/ext/standard/user_filters_arginfo.h index d530a5c00066..f5b8a7dfa472 100644 --- a/ext/standard/user_filters_arginfo.h +++ b/ext/standard/user_filters_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit user_filters.stub.php instead. - * Stub hash: 593afbcb51bb35207b93ac7556b92ac845043116 */ + * Stub hash: 01be6d52377ecd1940c14e3d508df28a70456c58 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_filter, 0, 4, IS_LONG, 0) ZEND_ARG_INFO(0, in) @@ -8,9 +8,10 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_ ZEND_ARG_TYPE_INFO(0, closing, _IS_BOOL, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_seek, 0, 2, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_seek, 0, 3, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, offset, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, whence, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, chain, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_php_user_filter_onCreate, 0, 0, _IS_BOOL, 0) diff --git a/ext/standard/var.c b/ext/standard/var.c index 7bf4e17a4cb5..3137a270f661 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -166,7 +166,7 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */ break; case IS_OBJECT: { zend_class_entry *ce = Z_OBJCE_P(struc); - if (ce->ce_flags & ZEND_ACC_ENUM) { + if ((ce->ce_flags & ZEND_ACC_ENUM) && ce->__debugInfo == NULL) { zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(struc)); php_printf("%senum(%s::%s)\n", COMMON, ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval)); return; @@ -180,11 +180,16 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */ ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj); myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG); - class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc)); - const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(struc)); + if (ce->ce_flags & ZEND_ACC_ENUM) { + zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(struc)); + php_printf("%senum(%s::%s) (%d) {\n", COMMON, ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval), myht ? zend_array_count(myht) : 0); + } else { + class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc)); + const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(struc)); - php_printf("%s%sobject(%s)#%d (%d) {\n", COMMON, prefix, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0); - zend_string_release_ex(class_name, 0); + php_printf("%s%sobject(%s)#%d (%d) {\n", COMMON, prefix, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0); + zend_string_release_ex(class_name, 0); + } if (myht) { zend_ulong num; diff --git a/ext/standard/versioning.c b/ext/standard/versioning.c index 75968ca577fd..dc7ca71af469 100644 --- a/ext/standard/versioning.c +++ b/ext/standard/versioning.c @@ -43,8 +43,8 @@ php_canonicalize_version(const char *version) * s/([^\d\.])([^\D\.])/$1.$2/g; * s/([^\D\.])([^\d\.])/$1.$2/g; */ -#define isdig(x) (isdigit(x)&&(x)!='.') -#define isndig(x) (!isdigit(x)&&(x)!='.') +#define isdig(x) (isdigit((unsigned char)(x))&&(x)!='.') +#define isndig(x) (!isdigit((unsigned char)(x))&&(x)!='.') #define isspecialver(x) ((x)=='-'||(x)=='_'||(x)=='+') lq = *(q - 1); @@ -57,7 +57,7 @@ php_canonicalize_version(const char *version) *q++ = '.'; } *q++ = *p; - } else if (!isalnum(*p)) { + } else if (!isalnum((unsigned char)*p)) { if (lq != '.') { *q++ = '.'; } @@ -66,7 +66,15 @@ php_canonicalize_version(const char *version) } lp = *p++; } - *q++ = '\0'; + + /* Check if the last component is empty (i.e. the last character is a dot) + * and remove it if it is. */ + if (*(q - 1) == '.') { + *(q - 1) = '\0'; + } else { + *q = '\0'; + } + return buf; } @@ -150,17 +158,17 @@ php_version_compare(const char *orig_ver1, const char *orig_ver2) if ((n2 = strchr(p2, '.')) != NULL) { *n2 = '\0'; } - if (isdigit(*p1) && isdigit(*p2)) { + if (isdigit((unsigned char)*p1) && isdigit((unsigned char)*p2)) { /* compare element numerically */ l1 = strtol(p1, NULL, 10); l2 = strtol(p2, NULL, 10); compare = ZEND_NORMALIZE_BOOL(l1 - l2); - } else if (!isdigit(*p1) && !isdigit(*p2)) { + } else if (!isdigit((unsigned char)*p1) && !isdigit((unsigned char)*p2)) { /* compare element names */ compare = compare_special_version_forms(p1, p2); } else { /* mix of names and numbers */ - if (isdigit(*p1)) { + if (isdigit((unsigned char)*p1)) { compare = compare_special_version_forms("#N#", p2); } else { compare = compare_special_version_forms(p1, "#N#"); @@ -178,13 +186,13 @@ php_version_compare(const char *orig_ver1, const char *orig_ver2) } if (compare == 0) { if (n1 != NULL) { - if (isdigit(*p1)) { + if (isdigit((unsigned char)*p1)) { compare = 1; } else { compare = php_version_compare(p1, "#N#"); } } else if (n2 != NULL) { - if (isdigit(*p2)) { + if (isdigit((unsigned char)*p2)) { compare = -1; } else { compare = php_version_compare("#N#", p2); diff --git a/ext/sysvmsg/sysvmsg.c b/ext/sysvmsg/sysvmsg.c index c86404fc5c45..0c2b3dcf183b 100644 --- a/ext/sysvmsg/sysvmsg.c +++ b/ext/sysvmsg/sysvmsg.c @@ -65,9 +65,7 @@ ZEND_GET_MODULE(sysvmsg) zend_class_entry *sysvmsg_queue_ce; static zend_object_handlers sysvmsg_queue_object_handlers; -static inline sysvmsg_queue_t *sysvmsg_queue_from_obj(zend_object *obj) { - return (sysvmsg_queue_t *)((char *)(obj) - XtOffsetOf(sysvmsg_queue_t, std)); -} +#define sysvmsg_queue_from_obj(obj) ZEND_CONTAINER_OF(obj, sysvmsg_queue_t, std) #define Z_SYSVMSG_QUEUE_P(zv) sysvmsg_queue_from_obj(Z_OBJ_P(zv)) @@ -101,7 +99,7 @@ PHP_MINIT_FUNCTION(sysvmsg) sysvmsg_queue_ce->default_object_handlers = &sysvmsg_queue_object_handlers; memcpy(&sysvmsg_queue_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - sysvmsg_queue_object_handlers.offset = XtOffsetOf(sysvmsg_queue_t, std); + sysvmsg_queue_object_handlers.offset = offsetof(sysvmsg_queue_t, std); sysvmsg_queue_object_handlers.free_obj = sysvmsg_queue_free_obj; sysvmsg_queue_object_handlers.get_constructor = sysvmsg_queue_get_constructor; sysvmsg_queue_object_handlers.clone_obj = NULL; diff --git a/ext/sysvsem/sysvsem.c b/ext/sysvsem/sysvsem.c index e29283ea3f6e..e506bd5bb37c 100644 --- a/ext/sysvsem/sysvsem.c +++ b/ext/sysvsem/sysvsem.c @@ -82,9 +82,7 @@ ZEND_GET_MODULE(sysvsem) zend_class_entry *sysvsem_ce; static zend_object_handlers sysvsem_object_handlers; -static inline sysvsem_sem *sysvsem_from_obj(zend_object *obj) { - return (sysvsem_sem *)((char *)(obj) - XtOffsetOf(sysvsem_sem, std)); -} +#define sysvsem_from_obj(obj) ZEND_CONTAINER_OF(obj, sysvsem_sem, std) #define Z_SYSVSEM_P(zv) sysvsem_from_obj(Z_OBJ_P(zv)) @@ -147,7 +145,7 @@ PHP_MINIT_FUNCTION(sysvsem) sysvsem_ce->default_object_handlers = &sysvsem_object_handlers; memcpy(&sysvsem_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - sysvsem_object_handlers.offset = XtOffsetOf(sysvsem_sem, std); + sysvsem_object_handlers.offset = offsetof(sysvsem_sem, std); sysvsem_object_handlers.free_obj = sysvsem_free_obj; sysvsem_object_handlers.get_constructor = sysvsem_get_constructor; sysvsem_object_handlers.clone_obj = NULL; diff --git a/ext/sysvshm/sysvshm.c b/ext/sysvshm/sysvshm.c index 29f153b19662..59ca49a974e6 100644 --- a/ext/sysvshm/sysvshm.c +++ b/ext/sysvshm/sysvshm.c @@ -33,9 +33,7 @@ zend_class_entry *sysvshm_ce; static zend_object_handlers sysvshm_object_handlers; -static inline sysvshm_shm *sysvshm_from_obj(zend_object *obj) { - return (sysvshm_shm *)((char *)(obj) - XtOffsetOf(sysvshm_shm, std)); -} +#define sysvshm_from_obj(obj) ZEND_CONTAINER_OF(obj, sysvshm_shm, std) #define Z_SYSVSHM_P(zv) sysvshm_from_obj(Z_OBJ_P(zv)) @@ -100,7 +98,7 @@ PHP_MINIT_FUNCTION(sysvshm) sysvshm_ce->default_object_handlers = &sysvshm_object_handlers; memcpy(&sysvshm_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - sysvshm_object_handlers.offset = XtOffsetOf(sysvshm_shm, std); + sysvshm_object_handlers.offset = offsetof(sysvshm_shm, std); sysvshm_object_handlers.free_obj = sysvshm_free_obj; sysvshm_object_handlers.get_constructor = sysvshm_get_constructor; sysvshm_object_handlers.clone_obj = NULL; diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index ba22c06ef494..98e1c05a720f 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -104,9 +104,7 @@ struct _PHPTidyObj { zend_object std; }; -static inline PHPTidyObj *php_tidy_fetch_object(zend_object *obj) { - return (PHPTidyObj *)((char*)(obj) - XtOffsetOf(PHPTidyObj, std)); -} +#define php_tidy_fetch_object(obj) ZEND_CONTAINER_OF(obj, PHPTidyObj, std) #define Z_TIDY_P(zv) php_tidy_fetch_object(Z_OBJ_P((zv))) /* }}} */ @@ -848,7 +846,7 @@ static PHP_MINIT_FUNCTION(tidy) tidy_object_handlers_doc.cast_object = tidy_doc_cast_handler; tidy_object_handlers_node.cast_object = tidy_node_cast_handler; - tidy_object_handlers_node.offset = tidy_object_handlers_doc.offset = XtOffsetOf(PHPTidyObj, std); + tidy_object_handlers_node.offset = tidy_object_handlers_doc.offset = offsetof(PHPTidyObj, std); tidy_object_handlers_node.free_obj = tidy_object_handlers_doc.free_obj = tidy_object_free_storage; register_tidy_symbols(module_number); diff --git a/ext/uri/CREDITS b/ext/uri/CREDITS index fc6ce83fec88..0bbea2af21d2 100644 --- a/ext/uri/CREDITS +++ b/ext/uri/CREDITS @@ -1,2 +1,2 @@ uri -Máté Kocsis, Tim Düsterhus, Ignace Nyamagana Butera, Arnaud Le Blanc, Dennis Snell, Niels Dossche, Nicolas Grekas +Máté Kocsis, Tim Düsterhus, Ignace Nyamagana Butera, Arnaud Le Blanc, Dennis Snell, Nora Dossche, Nicolas Grekas diff --git a/ext/uri/config.w32 b/ext/uri/config.w32 index b7c4bfbd2246..2bed937a7377 100644 --- a/ext/uri/config.w32 +++ b/ext/uri/config.w32 @@ -2,7 +2,7 @@ EXTENSION("uri", "php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_wha AC_DEFINE("URI_ENABLE_ANSI", 1, "Define to 1 for enabling ANSI support of uriparser.") AC_DEFINE("URI_NO_UNICODE", 1, "Define to 1 for disabling unicode support of uriparser.") -ADD_FLAG("CFLAGS_URI", "/D URI_STATIC_BUILD"); +ADD_FLAG("CFLAGS_URI", "/D URI_STATIC_BUILD /D LEXBOR_STATIC"); ADD_EXTENSION_DEP('uri', 'lexbor'); ADD_SOURCES("ext/uri/uriparser/src", "UriCommon.c UriCompare.c UriCopy.c UriEscape.c UriFile.c UriIp4.c UriIp4Base.c \ diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index a9e20d09996f..58f34a370151 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -31,11 +31,14 @@ #include "uriparser/Uri.h" zend_class_entry *php_uri_ce_rfc3986_uri; +zend_class_entry *php_uri_ce_rfc3986_uri_type; +zend_class_entry *php_uri_ce_rfc3986_uri_host_type; zend_class_entry *php_uri_ce_whatwg_url; zend_class_entry *php_uri_ce_comparison_mode; zend_class_entry *php_uri_ce_exception; zend_class_entry *php_uri_ce_error; zend_class_entry *php_uri_ce_invalid_uri_exception; +zend_class_entry *php_uri_ce_whatwg_url_host_type; zend_class_entry *php_uri_ce_whatwg_invalid_url_exception; zend_class_entry *php_uri_ce_whatwg_url_validation_error_type; zend_class_entry *php_uri_ce_whatwg_url_validation_error; @@ -508,6 +511,16 @@ PHP_METHOD(Uri_WhatWg_Url, __construct) create_whatwg_uri(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } +PHP_METHOD(Uri_Rfc3986_Uri, getUriType) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + php_uri_object *uri_object = Z_URI_OBJECT_P(ZEND_THIS); + ZEND_ASSERT(uri_object->uri != NULL); + + php_uri_parser_rfc3986_uri_type_read(uri_object->uri, return_value); +} + PHP_METHOD(Uri_Rfc3986_Uri, getScheme) { php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_SCHEME, PHP_URI_COMPONENT_READ_MODE_NORMALIZED_ASCII); @@ -611,6 +624,16 @@ PHP_METHOD(Uri_Rfc3986_Uri, getRawHost) php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_HOST, PHP_URI_COMPONENT_READ_MODE_RAW); } +PHP_METHOD(Uri_Rfc3986_Uri, getHostType) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + php_uri_object *uri_object = Z_URI_OBJECT_P(ZEND_THIS); + ZEND_ASSERT(uri_object->uri != NULL); + + php_uri_parser_rfc3986_host_type_read(uri_object->uri, return_value); +} + PHP_METHOD(Uri_Rfc3986_Uri, withHost) { php_uri_property_write_str_or_null_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_HOST); @@ -883,6 +906,16 @@ PHP_METHOD(Uri_WhatWg_Url, withScheme) php_uri_property_write_str_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_SCHEME); } +PHP_METHOD(Uri_WhatWg_Url, isSpecialScheme) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + php_uri_object *uri_object = Z_URI_OBJECT_P(ZEND_THIS); + ZEND_ASSERT(uri_object->uri != NULL); + + RETVAL_BOOL(php_uri_parser_whatwg_is_special(uri_object->uri)); +} + PHP_METHOD(Uri_WhatWg_Url, withUsername) { php_uri_property_write_str_or_null_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_USERNAME); @@ -903,6 +936,16 @@ PHP_METHOD(Uri_WhatWg_Url, getUnicodeHost) php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_HOST, PHP_URI_COMPONENT_READ_MODE_NORMALIZED_UNICODE); } +PHP_METHOD(Uri_WhatWg_Url, getHostType) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + php_uri_object *uri_object = Z_URI_OBJECT_P(ZEND_THIS); + ZEND_ASSERT(uri_object->uri != NULL); + + php_uri_parser_whatwg_host_type_read(uri_object->uri, return_value); +} + PHP_METHOD(Uri_WhatWg_Url, getFragment) { php_uri_property_read_helper(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_URI_PROPERTY_NAME_FRAGMENT, PHP_URI_COMPONENT_READ_MODE_NORMALIZED_UNICODE); @@ -1034,7 +1077,7 @@ PHPAPI void php_uri_object_handler_free(zend_object *object) PHPAPI zend_object *php_uri_object_handler_clone(zend_object *object) { - php_uri_object *uri_object = php_uri_object_from_obj(object); + const php_uri_object *uri_object = php_uri_object_from_obj(object); ZEND_ASSERT(uri_object->uri != NULL); @@ -1074,15 +1117,18 @@ static PHP_MINIT_FUNCTION(uri) php_uri_ce_rfc3986_uri->create_object = php_uri_object_create_rfc3986; php_uri_ce_rfc3986_uri->default_object_handlers = &object_handlers_rfc3986_uri; memcpy(&object_handlers_rfc3986_uri, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - object_handlers_rfc3986_uri.offset = XtOffsetOf(php_uri_object, std); + object_handlers_rfc3986_uri.offset = offsetof(php_uri_object, std); object_handlers_rfc3986_uri.free_obj = php_uri_object_handler_free; object_handlers_rfc3986_uri.clone_obj = php_uri_object_handler_clone; + php_uri_ce_rfc3986_uri_type = register_class_Uri_Rfc3986_UriType(); + php_uri_ce_rfc3986_uri_host_type = register_class_Uri_Rfc3986_UriHostType(); + php_uri_ce_whatwg_url = register_class_Uri_WhatWg_Url(); php_uri_ce_whatwg_url->create_object = php_uri_object_create_whatwg; php_uri_ce_whatwg_url->default_object_handlers = &object_handlers_whatwg_uri; memcpy(&object_handlers_whatwg_uri, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - object_handlers_whatwg_uri.offset = XtOffsetOf(php_uri_object, std); + object_handlers_whatwg_uri.offset = offsetof(php_uri_object, std); object_handlers_whatwg_uri.free_obj = php_uri_object_handler_free; object_handlers_whatwg_uri.clone_obj = php_uri_object_handler_clone; @@ -1091,6 +1137,7 @@ static PHP_MINIT_FUNCTION(uri) php_uri_ce_error = register_class_Uri_UriError(zend_ce_error); php_uri_ce_invalid_uri_exception = register_class_Uri_InvalidUriException(php_uri_ce_exception); php_uri_ce_whatwg_invalid_url_exception = register_class_Uri_WhatWg_InvalidUrlException(php_uri_ce_invalid_uri_exception); + php_uri_ce_whatwg_url_host_type = register_class_Uri_WhatWg_UrlHostType(); php_uri_ce_whatwg_url_validation_error = register_class_Uri_WhatWg_UrlValidationError(); php_uri_ce_whatwg_url_validation_error_type = register_class_Uri_WhatWg_UrlValidationErrorType(); diff --git a/ext/uri/php_uri.stub.php b/ext/uri/php_uri.stub.php index 6d4b2c3517a5..b0b83fcf83ec 100644 --- a/ext/uri/php_uri.stub.php +++ b/ext/uri/php_uri.stub.php @@ -29,6 +29,22 @@ enum UriComparisonMode } namespace Uri\Rfc3986 { + enum UriType + { + case AbsolutePathReference; + case RelativePathReference; + case NetworkPathReference; + case Uri; + } + + enum UriHostType + { + case IPv4; + case IPv6; + case IPvFuture; + case RegisteredName; + } + /** @strict-properties */ final readonly class Uri { @@ -36,6 +52,8 @@ public static function parse(string $uri, ?\Uri\Rfc3986\Uri $baseUrl = null): ?s public function __construct(string $uri, ?\Uri\Rfc3986\Uri $baseUrl = null) {} + public function getUriType(): ?\Uri\Rfc3986\UriType {} + public function getScheme(): ?string {} public function getRawScheme(): ?string {} @@ -60,6 +78,8 @@ public function getHost(): ?string {} public function getRawHost(): ?string {} + public function getHostType(): ?\Uri\Rfc3986\UriHostType {} + public function withHost(?string $host): static {} public function getPort(): ?int {} @@ -152,6 +172,15 @@ enum UrlValidationErrorType public function __construct(string $context, \Uri\WhatWg\UrlValidationErrorType $type, bool $failure) {} } + enum UrlHostType + { + case IPv4; + case IPv6; + case Domain; + case Opaque; + case Empty; + } + /** @strict-properties */ final readonly class Url { @@ -165,6 +194,8 @@ public function getScheme(): string {} public function withScheme(string $scheme): static {} + public function isSpecialScheme(): bool {} + /** @implementation-alias Uri\Rfc3986\Uri::getUsername */ public function getUsername(): ?string {} @@ -179,6 +210,8 @@ public function getAsciiHost(): ?string {} public function getUnicodeHost(): ?string {} + public function getHostType(): ?\Uri\WhatWg\UrlHostType {} + /** @implementation-alias Uri\Rfc3986\Uri::withHost */ public function withHost(?string $host): static {} diff --git a/ext/uri/php_uri_arginfo.h b/ext/uri/php_uri_arginfo.h index 18d7f4adf783..0fb464ee74aa 100644 --- a/ext/uri/php_uri_arginfo.h +++ b/ext/uri/php_uri_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_uri.stub.php instead. - * Stub hash: 3c228f4227e7543be5c12c99074789d92c27ab99 + * Stub hash: a3b4696ac001d537cc34b818715c7eb382c17c5b * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_parse, 0, 1, IS_STATIC, 1) @@ -12,6 +12,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Uri_Rfc3986_Uri___construct, 0, 0, 1) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, baseUrl, Uri\\Rfc3986\\\125ri, 1, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getUriType, 0, 0, Uri\\Rfc3986\\\125riType, 1) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getScheme, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -41,6 +44,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_Rfc3986_Uri_getRawHost arginfo_class_Uri_Rfc3986_Uri_getScheme +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getHostType, 0, 0, Uri\\Rfc3986\\\125riHostType, 1) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withHost, 0, 1, IS_STATIC, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -130,6 +136,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withScheme, ZEND_ARG_TYPE_INFO(0, scheme, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_isSpecialScheme, 0, 0, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_Uri_WhatWg_Url_getUsername arginfo_class_Uri_Rfc3986_Uri_getScheme ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withUsername, 0, 1, IS_STATIC, 0) @@ -146,6 +155,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_WhatWg_Url_getUnicodeHost arginfo_class_Uri_Rfc3986_Uri_getScheme +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Uri_WhatWg_Url_getHostType, 0, 0, Uri\\WhatWg\\\125rlHostType, 1) +ZEND_END_ARG_INFO() + #define arginfo_class_Uri_WhatWg_Url_withHost arginfo_class_Uri_Rfc3986_Uri_withHost #define arginfo_class_Uri_WhatWg_Url_getPort arginfo_class_Uri_Rfc3986_Uri_getPort @@ -186,6 +198,7 @@ ZEND_END_ARG_INFO() ZEND_METHOD(Uri_Rfc3986_Uri, parse); ZEND_METHOD(Uri_Rfc3986_Uri, __construct); +ZEND_METHOD(Uri_Rfc3986_Uri, getUriType); ZEND_METHOD(Uri_Rfc3986_Uri, getScheme); ZEND_METHOD(Uri_Rfc3986_Uri, getRawScheme); ZEND_METHOD(Uri_Rfc3986_Uri, withScheme); @@ -198,6 +211,7 @@ ZEND_METHOD(Uri_Rfc3986_Uri, getPassword); ZEND_METHOD(Uri_Rfc3986_Uri, getRawPassword); ZEND_METHOD(Uri_Rfc3986_Uri, getHost); ZEND_METHOD(Uri_Rfc3986_Uri, getRawHost); +ZEND_METHOD(Uri_Rfc3986_Uri, getHostType); ZEND_METHOD(Uri_Rfc3986_Uri, withHost); ZEND_METHOD(Uri_Rfc3986_Uri, getPort); ZEND_METHOD(Uri_Rfc3986_Uri, withPort); @@ -223,10 +237,12 @@ ZEND_METHOD(Uri_WhatWg_Url, parse); ZEND_METHOD(Uri_WhatWg_Url, __construct); ZEND_METHOD(Uri_WhatWg_Url, getScheme); ZEND_METHOD(Uri_WhatWg_Url, withScheme); +ZEND_METHOD(Uri_WhatWg_Url, isSpecialScheme); ZEND_METHOD(Uri_WhatWg_Url, withUsername); ZEND_METHOD(Uri_WhatWg_Url, withPassword); ZEND_METHOD(Uri_WhatWg_Url, getAsciiHost); ZEND_METHOD(Uri_WhatWg_Url, getUnicodeHost); +ZEND_METHOD(Uri_WhatWg_Url, getHostType); ZEND_METHOD(Uri_WhatWg_Url, equals); ZEND_METHOD(Uri_WhatWg_Url, toAsciiString); ZEND_METHOD(Uri_WhatWg_Url, toUnicodeString); @@ -238,6 +254,7 @@ ZEND_METHOD(Uri_WhatWg_Url, __debugInfo); static const zend_function_entry class_Uri_Rfc3986_Uri_methods[] = { ZEND_ME(Uri_Rfc3986_Uri, parse, arginfo_class_Uri_Rfc3986_Uri_parse, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(Uri_Rfc3986_Uri, __construct, arginfo_class_Uri_Rfc3986_Uri___construct, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getUriType, arginfo_class_Uri_Rfc3986_Uri_getUriType, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getScheme, arginfo_class_Uri_Rfc3986_Uri_getScheme, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawScheme, arginfo_class_Uri_Rfc3986_Uri_getRawScheme, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, withScheme, arginfo_class_Uri_Rfc3986_Uri_withScheme, ZEND_ACC_PUBLIC) @@ -250,6 +267,7 @@ static const zend_function_entry class_Uri_Rfc3986_Uri_methods[] = { ZEND_ME(Uri_Rfc3986_Uri, getRawPassword, arginfo_class_Uri_Rfc3986_Uri_getRawPassword, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getHost, arginfo_class_Uri_Rfc3986_Uri_getHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawHost, arginfo_class_Uri_Rfc3986_Uri_getRawHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, getHostType, arginfo_class_Uri_Rfc3986_Uri_getHostType, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, withHost, arginfo_class_Uri_Rfc3986_Uri_withHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getPort, arginfo_class_Uri_Rfc3986_Uri_getPort, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, withPort, arginfo_class_Uri_Rfc3986_Uri_withPort, ZEND_ACC_PUBLIC) @@ -287,12 +305,14 @@ static const zend_function_entry class_Uri_WhatWg_Url_methods[] = { ZEND_ME(Uri_WhatWg_Url, __construct, arginfo_class_Uri_WhatWg_Url___construct, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getScheme, arginfo_class_Uri_WhatWg_Url_getScheme, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, withScheme, arginfo_class_Uri_WhatWg_Url_withScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, isSpecialScheme, arginfo_class_Uri_WhatWg_Url_isSpecialScheme, ZEND_ACC_PUBLIC) ZEND_RAW_FENTRY("getUsername", zim_Uri_Rfc3986_Uri_getUsername, arginfo_class_Uri_WhatWg_Url_getUsername, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, withUsername, arginfo_class_Uri_WhatWg_Url_withUsername, ZEND_ACC_PUBLIC) ZEND_RAW_FENTRY("getPassword", zim_Uri_Rfc3986_Uri_getPassword, arginfo_class_Uri_WhatWg_Url_getPassword, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, withPassword, arginfo_class_Uri_WhatWg_Url_withPassword, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getAsciiHost, arginfo_class_Uri_WhatWg_Url_getAsciiHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getUnicodeHost, arginfo_class_Uri_WhatWg_Url_getUnicodeHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_WhatWg_Url, getHostType, arginfo_class_Uri_WhatWg_Url_getHostType, ZEND_ACC_PUBLIC) ZEND_RAW_FENTRY("withHost", zim_Uri_Rfc3986_Uri_withHost, arginfo_class_Uri_WhatWg_Url_withHost, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getPort", zim_Uri_Rfc3986_Uri_getPort, arginfo_class_Uri_WhatWg_Url_getPort, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("withPort", zim_Uri_Rfc3986_Uri_withPort, arginfo_class_Uri_WhatWg_Url_withPort, ZEND_ACC_PUBLIC, NULL, NULL) @@ -353,6 +373,36 @@ static zend_class_entry *register_class_Uri_UriComparisonMode(void) return class_entry; } +static zend_class_entry *register_class_Uri_Rfc3986_UriType(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\Rfc3986\\UriType", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "AbsolutePathReference", NULL); + + zend_enum_add_case_cstr(class_entry, "RelativePathReference", NULL); + + zend_enum_add_case_cstr(class_entry, "NetworkPathReference", NULL); + + zend_enum_add_case_cstr(class_entry, "Uri", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_Uri_Rfc3986_UriHostType(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\Rfc3986\\UriHostType", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "IPv4", NULL); + + zend_enum_add_case_cstr(class_entry, "IPv6", NULL); + + zend_enum_add_case_cstr(class_entry, "IPvFuture", NULL); + + zend_enum_add_case_cstr(class_entry, "RegisteredName", NULL); + + return class_entry; +} + static zend_class_entry *register_class_Uri_Rfc3986_Uri(void) { zend_class_entry ce, *class_entry; @@ -474,6 +524,23 @@ static zend_class_entry *register_class_Uri_WhatWg_UrlValidationError(void) return class_entry; } +static zend_class_entry *register_class_Uri_WhatWg_UrlHostType(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\WhatWg\\UrlHostType", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "IPv4", NULL); + + zend_enum_add_case_cstr(class_entry, "IPv6", NULL); + + zend_enum_add_case_cstr(class_entry, "Domain", NULL); + + zend_enum_add_case_cstr(class_entry, "Opaque", NULL); + + zend_enum_add_case_cstr(class_entry, "Empty", NULL); + + return class_entry; +} + static zend_class_entry *register_class_Uri_WhatWg_Url(void) { zend_class_entry ce, *class_entry; diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index f081fd6b6fd0..f83327690dd4 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -18,6 +18,8 @@ #include "php_uri_decl.h" extern zend_class_entry *php_uri_ce_rfc3986_uri; +extern zend_class_entry *php_uri_ce_rfc3986_uri_type; +extern zend_class_entry *php_uri_ce_rfc3986_uri_host_type; extern zend_class_entry *php_uri_ce_whatwg_url; extern zend_class_entry *php_uri_ce_comparison_mode; extern zend_class_entry *php_uri_ce_exception; @@ -26,6 +28,7 @@ extern zend_class_entry *php_uri_ce_invalid_uri_exception; extern zend_class_entry *php_uri_ce_whatwg_invalid_url_exception; extern zend_class_entry *php_uri_ce_whatwg_url_validation_error_type; extern zend_class_entry *php_uri_ce_whatwg_url_validation_error; +extern zend_class_entry *php_uri_ce_whatwg_url_host_type; typedef enum php_uri_recomposition_mode { PHP_URI_RECOMPOSITION_MODE_RAW_ASCII, @@ -146,9 +149,7 @@ typedef struct php_uri_object { zend_object std; } php_uri_object; -static inline php_uri_object *php_uri_object_from_obj(zend_object *object) { - return (php_uri_object*)((char*)(object) - XtOffsetOf(php_uri_object, std)); -} +#define php_uri_object_from_obj(object) ZEND_CONTAINER_OF(object, php_uri_object, std) #define Z_URI_OBJECT_P(zv) php_uri_object_from_obj(Z_OBJ_P((zv))) diff --git a/ext/uri/php_uri_decl.h b/ext/uri/php_uri_decl.h index 3c069f3abe6d..d1fd58d04b2c 100644 --- a/ext/uri/php_uri_decl.h +++ b/ext/uri/php_uri_decl.h @@ -1,14 +1,28 @@ /* This is a generated file, edit php_uri.stub.php instead. - * Stub hash: 3c228f4227e7543be5c12c99074789d92c27ab99 */ + * Stub hash: a3b4696ac001d537cc34b818715c7eb382c17c5b */ -#ifndef ZEND_PHP_URI_DECL_3c228f4227e7543be5c12c99074789d92c27ab99_H -#define ZEND_PHP_URI_DECL_3c228f4227e7543be5c12c99074789d92c27ab99_H +#ifndef ZEND_PHP_URI_DECL_a3b4696ac001d537cc34b818715c7eb382c17c5b_H +#define ZEND_PHP_URI_DECL_a3b4696ac001d537cc34b818715c7eb382c17c5b_H typedef enum zend_enum_Uri_UriComparisonMode { ZEND_ENUM_Uri_UriComparisonMode_IncludeFragment = 1, ZEND_ENUM_Uri_UriComparisonMode_ExcludeFragment = 2, } zend_enum_Uri_UriComparisonMode; +typedef enum zend_enum_Uri_Rfc3986_UriType { + ZEND_ENUM_Uri_Rfc3986_UriType_AbsolutePathReference = 1, + ZEND_ENUM_Uri_Rfc3986_UriType_RelativePathReference = 2, + ZEND_ENUM_Uri_Rfc3986_UriType_NetworkPathReference = 3, + ZEND_ENUM_Uri_Rfc3986_UriType_Uri = 4, +} zend_enum_Uri_Rfc3986_UriType; + +typedef enum zend_enum_Uri_Rfc3986_UriHostType { + ZEND_ENUM_Uri_Rfc3986_UriHostType_IPv4 = 1, + ZEND_ENUM_Uri_Rfc3986_UriHostType_IPv6 = 2, + ZEND_ENUM_Uri_Rfc3986_UriHostType_IPvFuture = 3, + ZEND_ENUM_Uri_Rfc3986_UriHostType_RegisteredName = 4, +} zend_enum_Uri_Rfc3986_UriHostType; + typedef enum zend_enum_Uri_WhatWg_UrlValidationErrorType { ZEND_ENUM_Uri_WhatWg_UrlValidationErrorType_DomainToAscii = 1, ZEND_ENUM_Uri_WhatWg_UrlValidationErrorType_DomainToUnicode = 2, @@ -41,4 +55,12 @@ typedef enum zend_enum_Uri_WhatWg_UrlValidationErrorType { ZEND_ENUM_Uri_WhatWg_UrlValidationErrorType_FileInvalidWindowsDriveLetterHost = 29, } zend_enum_Uri_WhatWg_UrlValidationErrorType; -#endif /* ZEND_PHP_URI_DECL_3c228f4227e7543be5c12c99074789d92c27ab99_H */ +typedef enum zend_enum_Uri_WhatWg_UrlHostType { + ZEND_ENUM_Uri_WhatWg_UrlHostType_IPv4 = 1, + ZEND_ENUM_Uri_WhatWg_UrlHostType_IPv6 = 2, + ZEND_ENUM_Uri_WhatWg_UrlHostType_Domain = 3, + ZEND_ENUM_Uri_WhatWg_UrlHostType_Opaque = 4, + ZEND_ENUM_Uri_WhatWg_UrlHostType_Empty = 5, +} zend_enum_Uri_WhatWg_UrlHostType; + +#endif /* ZEND_PHP_URI_DECL_a3b4696ac001d537cc34b818715c7eb382c17c5b_H */ diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_ip_future.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_ip_future.phpt new file mode 100644 index 000000000000..0ac7ea3f7efd --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/host_type_success_ip_future.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri component retrieval - host type - IP future +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriHostType::IPvFuture) diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_ipv4.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_ipv4.phpt new file mode 100644 index 000000000000..1c0c4448b953 --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/host_type_success_ipv4.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri component retrieval - host type - IPv4 +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriHostType::IPv4) diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_ipv6.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_ipv6.phpt new file mode 100644 index 000000000000..5f3d826b412a --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/host_type_success_ipv6.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri component retrieval - host type - IPv6 +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriHostType::IPv6) diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_none.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_none.phpt new file mode 100644 index 000000000000..3a0e0205befe --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/host_type_success_none.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri component retrieval - host type - none +--FILE-- +getHostType()); + +?> +--EXPECT-- +NULL diff --git a/ext/uri/tests/rfc3986/getters/host_type_success_registered_name.phpt b/ext/uri/tests/rfc3986/getters/host_type_success_registered_name.phpt new file mode 100644 index 000000000000..31d4aca36c82 --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/host_type_success_registered_name.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri component retrieval - host type - registered name +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriHostType::RegisteredName) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_absolute_path_reference.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_absolute_path_reference.phpt new file mode 100644 index 000000000000..1cddc15fa33c --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_absolute_path_reference.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - Absolute path reference +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::AbsolutePathReference) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_network_path_reference.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_network_path_reference.phpt new file mode 100644 index 000000000000..d7debc5ba66c --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_network_path_reference.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - Network path reference +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::NetworkPathReference) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_relative_path_reference.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_relative_path_reference.phpt new file mode 100644 index 000000000000..6cb51405f925 --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_relative_path_reference.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - Relative path reference +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::RelativePathReference) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_uri_basic.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_basic.phpt new file mode 100644 index 000000000000..262300103f9c --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_basic.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - URI +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::Uri) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_uri_empty_authority.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_empty_authority.phpt new file mode 100644 index 000000000000..ad8f2f27a8c6 --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_empty_authority.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - URI with empty authority +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::Uri) diff --git a/ext/uri/tests/rfc3986/getters/uri_type_success_uri_no_authority.phpt b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_no_authority.phpt new file mode 100644 index 000000000000..e266502ce90e --- /dev/null +++ b/ext/uri/tests/rfc3986/getters/uri_type_success_uri_no_authority.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\Rfc3986\Uri getter - uri type - URI without authority +--FILE-- +getUriType()); + +?> +--EXPECT-- +enum(Uri\Rfc3986\UriType::Uri) diff --git a/ext/uri/tests/whatwg/getters/host_type_success_domain.phpt b/ext/uri/tests/whatwg/getters/host_type_success_domain.phpt new file mode 100644 index 000000000000..21fc961e5541 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_domain.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - registered name +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\WhatWg\UrlHostType::Domain) diff --git a/ext/uri/tests/whatwg/getters/host_type_success_empty.phpt b/ext/uri/tests/whatwg/getters/host_type_success_empty.phpt new file mode 100644 index 000000000000..41351350fb61 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_empty.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - empty +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\WhatWg\UrlHostType::Empty) diff --git a/ext/uri/tests/whatwg/getters/host_type_success_ipv4.phpt b/ext/uri/tests/whatwg/getters/host_type_success_ipv4.phpt new file mode 100644 index 000000000000..f2f63caabb5e --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_ipv4.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - IPv4 +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\WhatWg\UrlHostType::IPv4) diff --git a/ext/uri/tests/whatwg/getters/host_type_success_ipv6.phpt b/ext/uri/tests/whatwg/getters/host_type_success_ipv6.phpt new file mode 100644 index 000000000000..ce5bb00b93d5 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_ipv6.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - IPv6 +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\WhatWg\UrlHostType::IPv6) diff --git a/ext/uri/tests/whatwg/getters/host_type_success_none.phpt b/ext/uri/tests/whatwg/getters/host_type_success_none.phpt new file mode 100644 index 000000000000..07d54412a23c --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_none.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - none +--FILE-- +getHostType()); + +?> +--EXPECT-- +NULL diff --git a/ext/uri/tests/whatwg/getters/host_type_success_opaque.phpt b/ext/uri/tests/whatwg/getters/host_type_success_opaque.phpt new file mode 100644 index 000000000000..844cba82d512 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/host_type_success_opaque.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url component retrieval - host type - opaque +--FILE-- +getHostType()); + +?> +--EXPECT-- +enum(Uri\WhatWg\UrlHostType::Opaque) diff --git a/ext/uri/tests/whatwg/getters/is_special_scheme_success_false.phpt b/ext/uri/tests/whatwg/getters/is_special_scheme_success_false.phpt new file mode 100644 index 000000000000..9bbb85755843 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/is_special_scheme_success_false.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test Uri\WhatWg\Url::isSpecialScheme() - success - not special +--FILE-- +isSpecialScheme()); + +?> +--EXPECT-- +bool(false) diff --git a/ext/uri/tests/whatwg/getters/is_special_scheme_success_true.phpt b/ext/uri/tests/whatwg/getters/is_special_scheme_success_true.phpt new file mode 100644 index 000000000000..1e19641b5600 --- /dev/null +++ b/ext/uri/tests/whatwg/getters/is_special_scheme_success_true.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test Uri\WhatWg\Url::isSpecialScheme() - success - special +--FILE-- +isSpecialScheme()); + +$url = Uri\WhatWg\Url::parse("https://example.com"); +var_dump($url->isSpecialScheme()); + +$url = Uri\WhatWg\Url::parse("ws://example.com"); +var_dump($url->isSpecialScheme()); + +$url = Uri\WhatWg\Url::parse("wss://example.com"); +var_dump($url->isSpecialScheme()); + +$url = Uri\WhatWg\Url::parse("ftp://example.com"); +var_dump($url->isSpecialScheme()); + +$url = Uri\WhatWg\Url::parse("file://example.com"); +var_dump($url->isSpecialScheme()); + +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/uri/uri_parser_rfc3986.c b/ext/uri/uri_parser_rfc3986.c index 172d7f08f144..4e2c5656aa77 100644 --- a/ext/uri/uri_parser_rfc3986.c +++ b/ext/uri/uri_parser_rfc3986.c @@ -15,6 +15,7 @@ #include "php.h" #include "uri_parser_rfc3986.h" #include "php_uri_common.h" +#include "Zend/zend_enum.h" #include "Zend/zend_smart_str.h" #include "Zend/zend_exceptions.h" @@ -23,6 +24,7 @@ struct php_uri_parser_rfc3986_uris { UriUriA uri; UriUriA normalized_uri; + unsigned int normalization_mask; bool normalized_uri_initialized; }; @@ -83,12 +85,21 @@ ZEND_ATTRIBUTE_NONNULL static void copy_uri(UriUriA *new_uriparser_uri, const Ur ZEND_ATTRIBUTE_NONNULL static UriUriA *get_normalized_uri(php_uri_parser_rfc3986_uris *uriparser_uris) { if (!uriparser_uris->normalized_uri_initialized) { - copy_uri(&uriparser_uris->normalized_uri, &uriparser_uris->uri); - int result = uriNormalizeSyntaxExMmA(&uriparser_uris->normalized_uri, (unsigned int)-1, mm); - ZEND_ASSERT(result == URI_SUCCESS); + int mask_result = uriNormalizeSyntaxMaskRequiredExA(&uriparser_uris->uri, &uriparser_uris->normalization_mask); + ZEND_ASSERT(mask_result == URI_SUCCESS); + + if (uriparser_uris->normalization_mask != URI_NORMALIZED) { + copy_uri(&uriparser_uris->normalized_uri, &uriparser_uris->uri); + int result = uriNormalizeSyntaxExMmA(&uriparser_uris->normalized_uri, uriparser_uris->normalization_mask, mm); + ZEND_ASSERT(result == URI_SUCCESS); + } uriparser_uris->normalized_uri_initialized = true; } + if (uriparser_uris->normalization_mask == URI_NORMALIZED) { + return &uriparser_uris->uri; + } + return &uriparser_uris->normalized_uri; } @@ -110,6 +121,25 @@ ZEND_ATTRIBUTE_NONNULL static UriUriA *get_uri_for_writing(php_uri_parser_rfc398 return &uriparser_uris->uri; } +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_uri_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval) +{ + const UriUriA *uriparser_uri = get_uri_for_reading(uri, PHP_URI_COMPONENT_READ_MODE_RAW); + + const char *type; + + if (has_text_range(&uriparser_uri->scheme)) { + type = "Uri"; + } else if (has_text_range(&uriparser_uri->hostText)) { + type = "NetworkPathReference"; + } else if (uriparser_uri->absolutePath) { + type = "AbsolutePathReference"; + } else { + type = "RelativePathReference"; + } + + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_rfc3986_uri_type, type)); +} + ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_scheme_read(void *uri, php_uri_component_read_mode read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(uri, read_mode); @@ -251,6 +281,30 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_host_read(void return SUCCESS; } +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_host_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval) +{ + const UriUriA *uriparser_uri = get_uri_for_reading(uri, PHP_URI_COMPONENT_READ_MODE_RAW); + + if (!has_text_range(&uriparser_uri->hostText)) { + ZVAL_NULL(retval); + return; + } + + const char *type; + + if (uriparser_uri->hostData.ip4 != NULL) { + type = "IPv4"; + } else if (uriparser_uri->hostData.ip6 != NULL) { + type = "IPv6"; + } else if (has_text_range(&uriparser_uri->hostData.ipFuture)) { + type = "IPvFuture"; + } else { + type = "RegisteredName"; + } + + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_rfc3986_uri_host_type, type)); +} + static zend_result php_uri_parser_rfc3986_host_write(void *uri, zval *value, zval *errors) { UriUriA *uriparser_uri = get_uri_for_writing(uri); diff --git a/ext/uri/uri_parser_rfc3986.h b/ext/uri/uri_parser_rfc3986.h index 21f938c370ad..633dd72062f2 100644 --- a/ext/uri/uri_parser_rfc3986.h +++ b/ext/uri/uri_parser_rfc3986.h @@ -21,6 +21,9 @@ PHPAPI extern const php_uri_parser php_uri_parser_rfc3986; typedef struct php_uri_parser_rfc3986_uris php_uri_parser_rfc3986_uris; +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_uri_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval); +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_rfc3986_host_type_read(php_uri_parser_rfc3986_uris *uri, zval *retval); + zend_result php_uri_parser_rfc3986_userinfo_read(php_uri_parser_rfc3986_uris *uri, php_uri_component_read_mode read_mode, zval *retval); zend_result php_uri_parser_rfc3986_userinfo_write(php_uri_parser_rfc3986_uris *uri, zval *value, zval *errors); diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c index d140357c18ed..f4e148704004 100644 --- a/ext/uri/uri_parser_whatwg.c +++ b/ext/uri/uri_parser_whatwg.c @@ -273,6 +273,11 @@ static zend_result php_uri_parser_whatwg_scheme_write(void *uri, zval *value, zv return SUCCESS; } +ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_whatwg_is_special(const lxb_url_t *lexbor_uri) +{ + return lxb_url_is_special(lexbor_uri); +} + /* 4.2. URL miscellaneous: A URL includes credentials if its username or password is not the empty string. */ static bool includes_credentials(const lxb_url_t *lexbor_uri) { @@ -381,6 +386,31 @@ static zend_result php_uri_parser_whatwg_host_read(void *uri, php_uri_component_ return SUCCESS; } +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_whatwg_host_type_read(const lxb_url_t *lexbor_uri, zval *retval) +{ + switch (lexbor_uri->host.type) { + case LXB_URL_HOST_TYPE_IPV4: + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "IPv4")); + return; + case LXB_URL_HOST_TYPE_IPV6: + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "IPv6")); + return; + case LXB_URL_HOST_TYPE_DOMAIN: + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "Domain")); + return; + case LXB_URL_HOST_TYPE_EMPTY: + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "Empty")); + return; + case LXB_URL_HOST_TYPE_OPAQUE: + ZVAL_OBJ_COPY(retval, zend_enum_get_case_cstr(php_uri_ce_whatwg_url_host_type, "Opaque")); + return; + case LXB_URL_HOST_TYPE__UNDEF: + ZVAL_NULL(retval); + return; + default: ZEND_UNREACHABLE(); + } +} + static zend_result php_uri_parser_whatwg_host_write(void *uri, zval *value, zval *errors) { lxb_url_t *lexbor_uri = uri; diff --git a/ext/uri/uri_parser_whatwg.h b/ext/uri/uri_parser_whatwg.h index 3e9e2824e42b..f714ee483680 100644 --- a/ext/uri/uri_parser_whatwg.h +++ b/ext/uri/uri_parser_whatwg.h @@ -20,6 +20,9 @@ PHPAPI extern const php_uri_parser php_uri_parser_whatwg; +ZEND_ATTRIBUTE_NONNULL bool php_uri_parser_whatwg_is_special(const lxb_url_t *lexbor_uri); +ZEND_ATTRIBUTE_NONNULL void php_uri_parser_whatwg_host_type_read(const lxb_url_t *lexbor_uri, zval *retval); + lxb_url_t *php_uri_parser_whatwg_parse_ex(const char *uri_str, size_t uri_str_len, const lxb_url_t *lexbor_base_url, zval *errors, bool silent); PHP_RINIT_FUNCTION(uri_parser_whatwg); diff --git a/ext/uri/uriparser/include/uriparser/Uri.h b/ext/uri/uriparser/include/uriparser/Uri.h index 88976a484627..ddbbd26f9e44 100644 --- a/ext/uri/uriparser/include/uriparser/Uri.h +++ b/ext/uri/uriparser/include/uriparser/Uri.h @@ -1,4 +1,4 @@ -/* 5abed1007be99942f49ffe603a894d277066b79b9cb824547af0f3b9481cb9ca (1.0.0+) +/* c9d94656d067288e474df19a062d487c736b0fa8517d2ef7bbeb8dcd5a70c05b (1.0.2+) * * uriparser - RFC 3986 URI parsing library * diff --git a/ext/uri/uriparser/include/uriparser/UriBase.h b/ext/uri/uriparser/include/uriparser/UriBase.h index 3a9a868e3bb1..1cc91ceab772 100644 --- a/ext/uri/uriparser/include/uriparser/UriBase.h +++ b/ext/uri/uriparser/include/uriparser/UriBase.h @@ -52,7 +52,7 @@ /* Version */ # define URI_VER_MAJOR 1 # define URI_VER_MINOR 0 -# define URI_VER_RELEASE 0 +# define URI_VER_RELEASE 2 # define URI_VER_SUFFIX_ANSI "" # define URI_VER_SUFFIX_UNICODE URI_ANSI_TO_UNICODE(URI_VER_SUFFIX_ANSI) diff --git a/ext/uri/uriparser/src/UriCommon.c b/ext/uri/uriparser/src/UriCommon.c index 3644e8828f35..e9a6992b01c2 100644 --- a/ext/uri/uriparser/src/UriCommon.c +++ b/ext/uri/uriparser/src/UriCommon.c @@ -66,6 +66,8 @@ # endif # include +# include +# include // SIZE_MAX /*extern*/ const URI_CHAR * const URI_FUNC(SafeToPointTo) = _UT("X"); /*extern*/ const URI_CHAR * const URI_FUNC(ConstPwd) = _UT("."); @@ -104,42 +106,35 @@ int URI_FUNC(FreeUriPath)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { } /* Compares two text ranges for equal text content */ -int URI_FUNC(CompareRange)(const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b) { - int diff; - +bool URI_FUNC(RangeEquals)(const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b) { /* NOTE: Both NULL means equal! */ if ((a == NULL) || (b == NULL)) { - return ((a == NULL) ? 0 : 1) - ((b == NULL) ? 0 : 1); + return a == b; } /* NOTE: Both NULL means equal! */ if ((a->first == NULL) || (b->first == NULL)) { - return ((a->first == NULL) ? 0 : 1) - ((b->first == NULL) ? 0 : 1); - } - - diff = ((int)(a->afterLast - a->first) - (int)(b->afterLast - b->first)); - if (diff > 0) { - return 1; - } else if (diff < 0) { - return -1; + return a->first == b->first; } - diff = URI_STRNCMP(a->first, b->first, (a->afterLast - a->first)); + const size_t lenA = a->afterLast - a->first; + const size_t lenB = b->afterLast - b->first; - if (diff > 0) { - return 1; - } else if (diff < 0) { - return -1; + if (lenA != lenB) { + return false; } - return diff; + return URI_STRNCMP(a->first, b->first, lenA) == 0; } UriBool URI_FUNC(CopyRange)(URI_TYPE(TextRange) * destRange, const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory) { - const int lenInChars = (int)(sourceRange->afterLast - sourceRange->first); - const int lenInBytes = lenInChars * sizeof(URI_CHAR); + const size_t lenInChars = sourceRange->afterLast - sourceRange->first; + if (lenInChars > SIZE_MAX / sizeof(URI_CHAR)) { // detect integer overflow + return URI_FALSE; + } + const size_t lenInBytes = lenInChars * sizeof(URI_CHAR); URI_CHAR * dup = memory->malloc(memory, lenInBytes); if (dup == NULL) { return URI_FALSE; @@ -178,7 +173,7 @@ UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri, UriBool relative, walker->reserved = NULL; /* Prev pointer */ do { UriBool removeSegment = URI_FALSE; - int len = (int)(walker->text.afterLast - walker->text.first); + const size_t len = walker->text.afterLast - walker->text.first; switch (len) { case 1: if ((walker->text.first)[0] == _UT('.')) { @@ -727,7 +722,7 @@ UriBool URI_FUNC(FixPathNoScheme)(URI_TYPE(Uri) * uri, UriMemoryManager * memory } /* When dropping a host from a URI without a scheme, an absolute path - * and and empty first path segment, a consecutive reparse would rightfully + * and empty first path segment, a consecutive reparse would rightfully * mis-classify the first path segment as a host marker due to the "//". * To protect against this case, we prepend an artificial "." segment * to the path in here; the function is called after the host has diff --git a/ext/uri/uriparser/src/UriCommon.h b/ext/uri/uriparser/src/UriCommon.h index d141935f19e5..12c691a73386 100644 --- a/ext/uri/uriparser/src/UriCommon.h +++ b/ext/uri/uriparser/src/UriCommon.h @@ -67,6 +67,8 @@ # include # endif +# include + /* Used to point to from empty path segments. * X.first and X.afterLast must be the same non-NULL value then. */ extern const URI_CHAR * const URI_FUNC(SafeToPointTo); @@ -77,7 +79,7 @@ void URI_FUNC(ResetUri)(URI_TYPE(Uri) * uri); int URI_FUNC(FreeUriPath)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); -int URI_FUNC(CompareRange)(const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b); +bool URI_FUNC(RangeEquals)(const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b); UriBool URI_FUNC(CopyRange)(URI_TYPE(TextRange) * destRange, const URI_TYPE(TextRange) * sourceRange, diff --git a/ext/uri/uriparser/src/UriCompare.c b/ext/uri/uriparser/src/UriCompare.c index 781100e7e6f4..f9def86f6669 100644 --- a/ext/uri/uriparser/src/UriCompare.c +++ b/ext/uri/uriparser/src/UriCompare.c @@ -72,17 +72,17 @@ UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b) { } /* scheme */ - if (URI_FUNC(CompareRange)(&(a->scheme), &(b->scheme))) { + if (!URI_FUNC(RangeEquals)(&(a->scheme), &(b->scheme))) { return URI_FALSE; } - /* absolutePath */ - if ((a->scheme.first == NULL) && (a->absolutePath != b->absolutePath)) { + /* absolutePath -- not meaningful for URIs with a host set! */ + if (!URI_FUNC(HasHost)(a) && (a->absolutePath != b->absolutePath)) { return URI_FALSE; } /* userInfo */ - if (URI_FUNC(CompareRange)(&(a->userInfo), &(b->userInfo))) { + if (!URI_FUNC(RangeEquals)(&(a->userInfo), &(b->userInfo))) { return URI_FALSE; } @@ -107,20 +107,20 @@ UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b) { } if (a->hostData.ipFuture.first != NULL) { - if (URI_FUNC(CompareRange)(&(a->hostData.ipFuture), &(b->hostData.ipFuture))) { + if (!URI_FUNC(RangeEquals)(&(a->hostData.ipFuture), &(b->hostData.ipFuture))) { return URI_FALSE; } } if ((a->hostData.ip4 == NULL) && (a->hostData.ip6 == NULL) && (a->hostData.ipFuture.first == NULL)) { - if (URI_FUNC(CompareRange)(&(a->hostText), &(b->hostText))) { + if (!URI_FUNC(RangeEquals)(&(a->hostText), &(b->hostText))) { return URI_FALSE; } } /* portText */ - if (URI_FUNC(CompareRange)(&(a->portText), &(b->portText))) { + if (!URI_FUNC(RangeEquals)(&(a->portText), &(b->portText))) { return URI_FALSE; } @@ -133,7 +133,7 @@ UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b) { URI_TYPE(PathSegment) * walkA = a->pathHead; URI_TYPE(PathSegment) * walkB = b->pathHead; do { - if (URI_FUNC(CompareRange)(&(walkA->text), &(walkB->text))) { + if (!URI_FUNC(RangeEquals)(&(walkA->text), &(walkB->text))) { return URI_FALSE; } if ((walkA->next == NULL) != (walkB->next == NULL)) { @@ -145,12 +145,12 @@ UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b) { } /* query */ - if (URI_FUNC(CompareRange)(&(a->query), &(b->query))) { + if (!URI_FUNC(RangeEquals)(&(a->query), &(b->query))) { return URI_FALSE; } /* fragment */ - if (URI_FUNC(CompareRange)(&(a->fragment), &(b->fragment))) { + if (!URI_FUNC(RangeEquals)(&(a->fragment), &(b->fragment))) { return URI_FALSE; } diff --git a/ext/uri/uriparser/src/UriCopy.c b/ext/uri/uriparser/src/UriCopy.c index 3bc6b0dbe3f4..fbe6340faa42 100644 --- a/ext/uri/uriparser/src/UriCopy.c +++ b/ext/uri/uriparser/src/UriCopy.c @@ -199,6 +199,12 @@ int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, const URI_TYPE(Uri) * sourceUri if (URI_FUNC(CopyRangeAsNeeded)(&destWalker->text, &sourceWalker->text, memory) == URI_FALSE) { + // Unless wired to `destUri` above, `destWalker` may be hanging + // in the air now + if (destUri->pathHead != destWalker) { + memory->free(memory, destWalker); + } + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } diff --git a/ext/uri/uriparser/src/UriFile.c b/ext/uri/uriparser/src/UriFile.c index 7510e701c9f5..0af88480b0ad 100644 --- a/ext/uri/uriparser/src/UriFile.c +++ b/ext/uri/uriparser/src/UriFile.c @@ -63,7 +63,8 @@ # include # endif -# include /* for size_t, avoiding stddef.h for older MSVCs */ +# include // size_t +# include // SIZE_MAX static URI_INLINE int URI_FUNC(FilenameToUriString)(const URI_CHAR * filename, URI_CHAR * uriString, @@ -90,6 +91,11 @@ static URI_INLINE int URI_FUNC(FilenameToUriString)(const URI_CHAR * filename, : _UT("file:///"); const size_t prefixLen = URI_STRLEN(prefix); + // Detect and avoid integer overflow + if (prefixLen > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_OUTPUT_TOO_LARGE; + } + /* Copy prefix */ memcpy(uriString, prefix, prefixLen * sizeof(URI_CHAR)); output += prefixLen; @@ -103,7 +109,13 @@ static URI_INLINE int URI_FUNC(FilenameToUriString)(const URI_CHAR * filename, if (lastSep + 1 < input) { if (!fromUnix && absolute && (firstSegment == URI_TRUE)) { /* Quick hack to not convert "C:" to "C%3A" */ - const int charsToCopy = (int)(input - (lastSep + 1)); + const size_t charsToCopy = input - (lastSep + 1); + + // Detect and avoid integer overflow + if (charsToCopy > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_OUTPUT_TOO_LARGE; + } + memcpy(output, lastSep + 1, charsToCopy * sizeof(URI_CHAR)); output += charsToCopy; } else { diff --git a/ext/uri/uriparser/src/UriMemory.c b/ext/uri/uriparser/src/UriMemory.c index 3caf8199dc45..669f48dd6827 100644 --- a/ext/uri/uriparser/src/UriMemory.c +++ b/ext/uri/uriparser/src/UriMemory.c @@ -45,12 +45,25 @@ #include "UriConfig.h" /* for HAVE_REALLOCARRAY */ #ifdef HAVE_REALLOCARRAY -# ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 +// For glibc >=2.29 of 2019-02-01 +# if !defined(_DEFAULT_SOURCE) +# define _DEFAULT_SOURCE 1 # endif -# ifdef __NetBSD__ + +// For NetBSD (stdlib.h revision 1.122 of 2020-05-26) +# if defined(__NetBSD__) && !defined(_OPENBSD_SOURCE) # define _OPENBSD_SOURCE 1 # endif + +// POSIX 2024 (XPG8) for e.g. Illumos/SmartOS +# if !defined(_XOPEN_SOURCE) || (_XOPEN_SOURCE - 0 < 800) +# undef _XOPEN_SOURCE +# define _XOPEN_SOURCE 800 +# endif +# if !defined(_POSIX_C_SOURCE) || (_POSIX_C_SOURCE - 0 < 202405L) +# undef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 202405L +# endif #endif #include diff --git a/ext/uri/uriparser/src/UriNormalize.c b/ext/uri/uriparser/src/UriNormalize.c index 8c812d37a0a7..c73e22b28879 100644 --- a/ext/uri/uriparser/src/UriNormalize.c +++ b/ext/uri/uriparser/src/UriNormalize.c @@ -73,6 +73,7 @@ # endif # include +# include // SIZE_MAX static int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask, unsigned int * outMask, @@ -254,20 +255,22 @@ URI_FUNC(LowercaseInplaceExceptPercentEncoding)(const URI_CHAR * first, static URI_INLINE UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first, const URI_CHAR ** afterLast, UriMemoryManager * memory) { - int lenInChars; const int lowerUpperDiff = (_UT('a') - _UT('A')); URI_CHAR * buffer; - int i = 0; + size_t i = 0; if ((first == NULL) || (afterLast == NULL) || (*first == NULL) || (*afterLast == NULL)) { return URI_FALSE; } - lenInChars = (int)(*afterLast - *first); + const size_t lenInChars = *afterLast - *first; if (lenInChars == 0) { return URI_TRUE; - } else if (lenInChars < 0) { + } + + // Detect and avoid integer overflow + if (lenInChars > SIZE_MAX / sizeof(URI_CHAR)) { return URI_FALSE; } @@ -295,8 +298,8 @@ URI_FUNC(FixPercentEncodingEngine)(const URI_CHAR * inFirst, const URI_CHAR * in const URI_CHAR * outFirst, const URI_CHAR ** outAfterLast) { URI_CHAR * write = (URI_CHAR *)outFirst; - const int lenInChars = (int)(inAfterLast - inFirst); - int i = 0; + const size_t lenInChars = inAfterLast - inFirst; + size_t i = 0; /* All but last two */ for (; i + 2 < lenInChars; i++) { @@ -350,7 +353,6 @@ static URI_INLINE void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * firs static URI_INLINE UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** first, const URI_CHAR ** afterLast, UriMemoryManager * memory) { - int lenInChars; URI_CHAR * buffer; /* Death checks */ @@ -360,10 +362,13 @@ static URI_INLINE UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** f } /* Old text length */ - lenInChars = (int)(*afterLast - *first); + const size_t lenInChars = *afterLast - *first; if (lenInChars == 0) { return URI_TRUE; - } else if (lenInChars < 0) { + } + + // Detect and avoid integer overflow + if (lenInChars > SIZE_MAX / sizeof(URI_CHAR)) { return URI_FALSE; } diff --git a/ext/uri/uriparser/src/UriQuery.c b/ext/uri/uriparser/src/UriQuery.c index 801a237a3a1b..de3a040de042 100644 --- a/ext/uri/uriparser/src/UriQuery.c +++ b/ext/uri/uriparser/src/UriQuery.c @@ -67,6 +67,7 @@ # include # include /* size_t */ +# include // SIZE_MAX static int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest, const URI_TYPE(QueryList) * queryList, @@ -254,7 +255,14 @@ int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest, const URI_TYPE(QueryList) * qu if (dest != NULL) { write[0] = _UT('\0'); if (charsWritten != NULL) { - *charsWritten = (int)(write - dest) + 1; /* .. for terminator */ + const size_t lenInChars = write - dest; + + // Detect and avoid integer overflow + if (lenInChars > INT_MAX - 1) { + return URI_ERROR_OUTPUT_TOO_LARGE; + } + + *charsWritten = (int)(lenInChars + 1); /* .. for terminator */ } } @@ -267,8 +275,8 @@ UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) * *prevNext, int * itemCou const URI_CHAR * valueAfter, UriBool plusToSpace, UriBreakConversion breakConversion, UriMemoryManager * memory) { - const int keyLen = (int)(keyAfter - keyFirst); - const int valueLen = (int)(valueAfter - valueFirst); + const size_t keyLen = keyAfter - keyFirst; + const size_t valueLen = valueAfter - valueFirst; URI_CHAR * key; URI_CHAR * value; @@ -285,6 +293,13 @@ UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) * *prevNext, int * itemCou } (*prevNext)->next = NULL; + // Detect integer overflow + if ((keyLen > SIZE_MAX - 1) || (keyLen + 1 > SIZE_MAX / sizeof(URI_CHAR))) { + memory->free(memory, *prevNext); + *prevNext = NULL; + return URI_FALSE; // Raises malloc error + } + /* Fill key */ key = memory->malloc(memory, (keyLen + 1) * sizeof(URI_CHAR)); if (key == NULL) { @@ -305,6 +320,14 @@ UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) * *prevNext, int * itemCou /* Fill value */ if (valueFirst != NULL) { + // Detect integer overflow + if ((valueLen > SIZE_MAX - 1) || (valueLen + 1 > SIZE_MAX / sizeof(URI_CHAR))) { + memory->free(memory, key); + memory->free(memory, *prevNext); + *prevNext = NULL; + return URI_FALSE; // Raises malloc error + } + value = memory->malloc(memory, (valueLen + 1) * sizeof(URI_CHAR)); if (value == NULL) { memory->free(memory, key); diff --git a/ext/uri/uriparser/src/UriRecompose.c b/ext/uri/uriparser/src/UriRecompose.c index 61ffee77248f..055e0483d3f2 100644 --- a/ext/uri/uriparser/src/UriRecompose.c +++ b/ext/uri/uriparser/src/UriRecompose.c @@ -64,6 +64,9 @@ # include "UriCommon.h" # endif +# include // INT_MAX +# include // SIZE_MAX + static int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, int maxChars, int * charsWritten, int * charsRequired); @@ -116,10 +119,19 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U /* clang-format off */ /* [03/19] append scheme to result; */ /* clang-format on */ - const int charsToWrite = - (int)(uri->scheme.afterLast - uri->scheme.first); + const size_t charsToWrite = uri->scheme.afterLast - uri->scheme.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->scheme.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -131,6 +143,11 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - *charsRequired) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite; } /* clang-format off */ @@ -180,10 +197,20 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U /* clang-format on */ /* UserInfo */ if (uri->userInfo.first != NULL) { - const int charsToWrite = - (int)(uri->userInfo.afterLast - uri->userInfo.first); + const size_t charsToWrite = + uri->userInfo.afterLast - uri->userInfo.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->userInfo.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -206,6 +233,13 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if ((charsToWrite > (size_t)INT_MAX - 1) + || (charsToWrite + 1 + > (size_t)INT_MAX - *charsRequired)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite + 1; } } @@ -334,8 +368,8 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U } } else if (uri->hostData.ipFuture.first != NULL) { /* IPvFuture */ - const int charsToWrite = (int)(uri->hostData.ipFuture.afterLast - - uri->hostData.ipFuture.first); + const size_t charsToWrite = uri->hostData.ipFuture.afterLast + - uri->hostData.ipFuture.first; if (dest != NULL) { if (written + 1 <= maxChars) { memcpy(dest + written, _UT("["), 1 * sizeof(URI_CHAR)); @@ -348,7 +382,17 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->hostData.ipFuture.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -371,14 +415,31 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if ((charsToWrite > (size_t)INT_MAX - 1 - 1) + || (1 + charsToWrite + 1 + > (size_t)INT_MAX - *charsRequired)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += 1 + charsToWrite + 1; } } else if (uri->hostText.first != NULL) { /* Regname */ - const int charsToWrite = - (int)(uri->hostText.afterLast - uri->hostText.first); + const size_t charsToWrite = + uri->hostText.afterLast - uri->hostText.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->hostText.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -390,14 +451,19 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - *charsRequired) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite; } } /* Port */ if (uri->portText.first != NULL) { - const int charsToWrite = - (int)(uri->portText.afterLast - uri->portText.first); + const size_t charsToWrite = + uri->portText.afterLast - uri->portText.first; if (dest != NULL) { /* Leading ':' */ if (written + 1 <= maxChars) { @@ -411,8 +477,18 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + /* Port number */ - if (written + charsToWrite <= maxChars) { + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->portText.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -424,6 +500,13 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if ((charsToWrite > (size_t)INT_MAX - 1) + || (1 + charsToWrite + > (size_t)INT_MAX - *charsRequired)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += 1 + charsToWrite; } } @@ -456,10 +539,20 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U if (uri->pathHead != NULL) { URI_TYPE(PathSegment) * walker = uri->pathHead; do { - const int charsToWrite = - (int)(walker->text.afterLast - walker->text.first); + const size_t charsToWrite = + walker->text.afterLast - walker->text.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, walker->text.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -471,6 +564,11 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - *charsRequired) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite; } @@ -520,10 +618,19 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U /* clang-format off */ /* [13/19] append query to result; */ /* clang-format on */ - const int charsToWrite = - (int)(uri->query.afterLast - uri->query.first); + const size_t charsToWrite = uri->query.afterLast - uri->query.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->query.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -535,6 +642,11 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - *charsRequired) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite; } /* clang-format off */ @@ -565,10 +677,20 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U /* clang-format off */ /* [17/19] append fragment to result; */ /* clang-format on */ - const int charsToWrite = - (int)(uri->fragment.afterLast - uri->fragment.first); + const size_t charsToWrite = + uri->fragment.afterLast - uri->fragment.first; if (dest != NULL) { - if (written + charsToWrite <= maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - written) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + + if (written + charsToWrite <= (size_t)maxChars) { + // Detect and avoid integer overflow + if (charsToWrite > SIZE_MAX / sizeof(URI_CHAR)) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + memcpy(dest + written, uri->fragment.first, charsToWrite * sizeof(URI_CHAR)); written += charsToWrite; @@ -580,6 +702,11 @@ static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(U return URI_ERROR_TOSTRING_TOO_LONG; } } else { + // Detect and avoid integer overflow + if (charsToWrite > (size_t)INT_MAX - *charsRequired) { + return URI_ERROR_TOSTRING_TOO_LONG; + } + (*charsRequired) += charsToWrite; } /* clang-format off */ diff --git a/ext/uri/uriparser/src/UriResolve.c b/ext/uri/uriparser/src/UriResolve.c index 302665d21cd6..90c8bfd778dc 100644 --- a/ext/uri/uriparser/src/UriResolve.c +++ b/ext/uri/uriparser/src/UriResolve.c @@ -183,9 +183,8 @@ static int URI_FUNC(AddBaseUriImpl)(URI_TYPE(Uri) * absDest, if ((options & URI_RESOLVE_IDENTICAL_SCHEME_COMPAT) && (absBase->scheme.first != NULL) && (relSource->scheme.first != NULL) - && (0 - == URI_FUNC(CompareRange)(&(absBase->scheme), - &(relSource->scheme)))) { + && (URI_FUNC(RangeEquals)(&(absBase->scheme), + &(relSource->scheme)))) { /* clang-format off */ /* [00/32] undefine(R.scheme); */ /* clang-format on */ diff --git a/ext/uri/uriparser/src/UriShorten.c b/ext/uri/uriparser/src/UriShorten.c index 548b0b4157dd..001bd7db9990 100644 --- a/ext/uri/uriparser/src/UriShorten.c +++ b/ext/uri/uriparser/src/UriShorten.c @@ -111,14 +111,14 @@ static URI_INLINE UriBool URI_FUNC(EqualsAuthority)(const URI_TYPE(Uri) * first, /* IPvFuture */ if (first->hostData.ipFuture.first != NULL) { return ((second->hostData.ipFuture.first != NULL) - && !URI_FUNC(CompareRange)(&first->hostData.ipFuture, - &second->hostData.ipFuture)) + && URI_FUNC(RangeEquals)(&first->hostData.ipFuture, + &second->hostData.ipFuture)) ? URI_TRUE : URI_FALSE; } - return !URI_FUNC(CompareRange)(&first->hostText, &second->hostText) ? URI_TRUE - : URI_FALSE; + return URI_FUNC(RangeEquals)(&first->hostText, &second->hostText) ? URI_TRUE + : URI_FALSE; } static int URI_FUNC(RemoveBaseUriImpl)(URI_TYPE(Uri) * dest, @@ -152,7 +152,7 @@ static int URI_FUNC(RemoveBaseUriImpl)(URI_TYPE(Uri) * dest, /* clang-format off */ /* [01/50] if (A.scheme != Base.scheme) then */ /* clang-format on */ - if (URI_FUNC(CompareRange)(&absSource->scheme, &absBase->scheme)) { + if (!URI_FUNC(RangeEquals)(&absSource->scheme, &absBase->scheme)) { /* clang-format off */ /* [02/50] T.scheme = A.scheme; */ /* clang-format on */ @@ -255,8 +255,7 @@ static int URI_FUNC(RemoveBaseUriImpl)(URI_TYPE(Uri) * dest, /* clang-format on */ while ( (sourceSeg != NULL) && (baseSeg != NULL) - && !URI_FUNC(CompareRange)(&sourceSeg->text, - &baseSeg->text) + && URI_FUNC(RangeEquals)(&sourceSeg->text, &baseSeg->text) && !((sourceSeg->text.first == sourceSeg->text.afterLast) && ((sourceSeg->next == NULL) != (baseSeg->next == NULL)))) { diff --git a/ext/xml/xml.c b/ext/xml/xml.c index d9df7920bb92..d1bb4bbda23f 100644 --- a/ext/xml/xml.c +++ b/ext/xml/xml.c @@ -222,7 +222,7 @@ PHP_MINIT_FUNCTION(xml) xml_parser_ce->default_object_handlers = &xml_parser_object_handlers; memcpy(&xml_parser_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - xml_parser_object_handlers.offset = XtOffsetOf(xml_parser, std); + xml_parser_object_handlers.offset = offsetof(xml_parser, std); xml_parser_object_handlers.free_obj = xml_parser_free_obj; xml_parser_object_handlers.get_gc = xml_parser_get_gc; xml_parser_object_handlers.get_constructor = xml_parser_get_constructor; @@ -295,9 +295,7 @@ static void xml_xmlchar_zval(const XML_Char *s, int len, const XML_Char *encodin } /* }}} */ -static inline xml_parser *xml_parser_from_obj(zend_object *obj) { - return (xml_parser *)((char *)(obj) - XtOffsetOf(xml_parser, std)); -} +#define xml_parser_from_obj(obj) ZEND_CONTAINER_OF(obj, xml_parser, std) #define Z_XMLPARSER_P(zv) xml_parser_from_obj(Z_OBJ_P(zv)) @@ -829,16 +827,15 @@ void xml_characterDataHandler(void *userData, const XML_Char *s, int len) zval *myval; /* check if the current tag already has a value - if yes append to that! */ if ((myval = zend_hash_find(Z_ARRVAL_P(ctag), ZSTR_KNOWN(ZEND_STR_VALUE))) && Z_TYPE_P(myval) == IS_STRING) { - size_t newlen = Z_STRLEN_P(myval) + ZSTR_LEN(decoded_value); - Z_STR_P(myval) = zend_string_extend(Z_STR_P(myval), newlen, 0); + Z_STR_P(myval) = zend_string_safe_realloc(Z_STR_P(myval), 1, Z_STRLEN_P(myval), ZSTR_LEN(decoded_value), false); strncpy(Z_STRVAL_P(myval) + Z_STRLEN_P(myval) - ZSTR_LEN(decoded_value), ZSTR_VAL(decoded_value), ZSTR_LEN(decoded_value) + 1); - zend_string_release_ex(decoded_value, 0); + zend_string_release_ex(decoded_value, false); } else { if (doprint || (! parser->skipwhite)) { add_assoc_str(ctag, "value", decoded_value); } else { - zend_string_release_ex(decoded_value, 0); + zend_string_release_ex(decoded_value, false); } } } else { @@ -856,11 +853,10 @@ void xml_characterDataHandler(void *userData, const XML_Char *s, int len) if (EXPECTED(Z_TYPE_P(mytype) == IS_STRING) && zend_string_equals_literal(Z_STR_P(mytype), "cdata")) { SEPARATE_ARRAY(curtag); if ((myval = zend_hash_find(Z_ARRVAL_P(curtag), ZSTR_KNOWN(ZEND_STR_VALUE)))) { - size_t newlen = Z_STRLEN_P(myval) + ZSTR_LEN(decoded_value); - Z_STR_P(myval) = zend_string_extend(Z_STR_P(myval), newlen, 0); + Z_STR_P(myval) = zend_string_safe_realloc(Z_STR_P(myval), 1, Z_STRLEN_P(myval), ZSTR_LEN(decoded_value), false); strncpy(Z_STRVAL_P(myval) + Z_STRLEN_P(myval) - ZSTR_LEN(decoded_value), ZSTR_VAL(decoded_value), ZSTR_LEN(decoded_value) + 1); - zend_string_release_ex(decoded_value, 0); + zend_string_release_ex(decoded_value, false); return; } } @@ -879,7 +875,7 @@ void xml_characterDataHandler(void *userData, const XML_Char *s, int len) } else if (parser->level == (XML_MAXLEVEL + 1)) { php_error_docref(NULL, E_WARNING, "Maximum depth exceeded - Results truncated"); } else { - zend_string_release_ex(decoded_value, 0); + zend_string_release_ex(decoded_value, false); } } } diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index 18a2612d9ede..6a7fb65e2af0 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -1343,7 +1343,7 @@ PHP_MINIT_FUNCTION(xmlreader) { memcpy(&xmlreader_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - xmlreader_object_handlers.offset = XtOffsetOf(xmlreader_object, std); + xmlreader_object_handlers.offset = offsetof(xmlreader_object, std); xmlreader_object_handlers.free_obj = xmlreader_objects_free_storage; xmlreader_object_handlers.has_property = xmlreader_has_property; xmlreader_object_handlers.read_property = xmlreader_read_property; diff --git a/ext/xmlreader/php_xmlreader.h b/ext/xmlreader/php_xmlreader.h index 74d11c6bdbea..d33938f40506 100644 --- a/ext/xmlreader/php_xmlreader.h +++ b/ext/xmlreader/php_xmlreader.h @@ -44,9 +44,7 @@ typedef struct _xmlreader_object { zend_object std; } xmlreader_object; -static inline xmlreader_object *php_xmlreader_fetch_object(zend_object *obj) { - return (xmlreader_object *)((char*)(obj) - XtOffsetOf(xmlreader_object, std)); -} +#define php_xmlreader_fetch_object(obj) ZEND_CONTAINER_OF(obj, xmlreader_object, std) #define Z_XMLREADER_P(zv) php_xmlreader_fetch_object(Z_OBJ_P((zv))) diff --git a/ext/xmlreader/tests/012.phpt b/ext/xmlreader/tests/012.phpt index b10421d2624a..e00f37b82667 100644 --- a/ext/xmlreader/tests/012.phpt +++ b/ext/xmlreader/tests/012.phpt @@ -2,6 +2,10 @@ XMLReader: accessing empty and non existing attributes --EXTENSIONS-- xmlreader +--SKIPIF-- + --FILE-- handlers = &vm_interrupt_comparable_object_handlers; + ZVAL_LONG(OBJ_PROP_NUM(obj, 0), l); + return obj; +} + +static zend_object *vm_interrupt_comparable_object_create(zend_class_entry *ce) +{ + return vm_interrupt_comparable_object_create_ex(ce, 0); +} + +static int vm_interrupt_comparable_compare(zval *op1, zval *op2) +{ + ZEND_COMPARE_OBJECTS_FALLBACK(op1, op2); + + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + + return ZEND_THREEWAY_COMPARE( + Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op1), 0)), + Z_LVAL_P(OBJ_PROP_NUM(Z_OBJ_P(op2), 0))); +} + +ZEND_METHOD(VmInterruptComparable, __construct) +{ + zend_long l; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_LONG(l) + ZEND_PARSE_PARAMETERS_END(); + + ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), l); +} + static zend_class_entry *dimension_handlers_no_ArrayAccess_ce; static zend_object_handlers dimension_handlers_no_ArrayAccess_object_handlers; @@ -300,6 +338,11 @@ void zend_test_object_handlers_init(void) memcpy(&numeric_castable_no_operation_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); numeric_castable_no_operation_object_handlers.cast_object = numeric_castable_no_operation_cast_object; + vm_interrupt_comparable_ce = register_class_VmInterruptComparable(); + vm_interrupt_comparable_ce->create_object = vm_interrupt_comparable_object_create; + memcpy(&vm_interrupt_comparable_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + vm_interrupt_comparable_object_handlers.compare = vm_interrupt_comparable_compare; + dimension_handlers_no_ArrayAccess_ce = register_class_DimensionHandlersNoArrayAccess(); dimension_handlers_no_ArrayAccess_ce->create_object = dimension_handlers_no_ArrayAccess_object_create; memcpy(&dimension_handlers_no_ArrayAccess_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/zend_test/object_handlers.stub.php b/ext/zend_test/object_handlers.stub.php index a474908b1095..8c8e7f9bfe9b 100644 --- a/ext/zend_test/object_handlers.stub.php +++ b/ext/zend_test/object_handlers.stub.php @@ -23,6 +23,11 @@ final class NumericCastableNoOperations { public function __construct(int|float $val) {} } +final class VmInterruptComparable { + private int $val; + public function __construct(int $val) {} +} + class DimensionHandlersNoArrayAccess { public bool $read = false; public bool $write = false; diff --git a/ext/zend_test/object_handlers_arginfo.h b/ext/zend_test/object_handlers_arginfo.h index 34008c33bc0c..995bd62dc478 100644 --- a/ext/zend_test/object_handlers_arginfo.h +++ b/ext/zend_test/object_handlers_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit object_handlers.stub.php instead. - * Stub hash: 81be60f2c465ffe5c036739d072ab80d9c388907 */ + * Stub hash: 1a70ed60c5af38539b1222a979f97fddf7d1826e */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DoOperationNoCast___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, val, IS_LONG, 0) @@ -15,10 +15,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_NumericCastableNoOperations___construct, 0, ZEND_ARG_TYPE_MASK(0, val, MAY_BE_LONG|MAY_BE_DOUBLE, NULL) ZEND_END_ARG_INFO() +#define arginfo_class_VmInterruptComparable___construct arginfo_class_DoOperationNoCast___construct + static ZEND_METHOD(DoOperationNoCast, __construct); static ZEND_METHOD(LongCastableNoOperations, __construct); static ZEND_METHOD(FloatCastableNoOperations, __construct); static ZEND_METHOD(NumericCastableNoOperations, __construct); +static ZEND_METHOD(VmInterruptComparable, __construct); static const zend_function_entry class_DoOperationNoCast_methods[] = { ZEND_ME(DoOperationNoCast, __construct, arginfo_class_DoOperationNoCast___construct, ZEND_ACC_PUBLIC) @@ -40,6 +43,11 @@ static const zend_function_entry class_NumericCastableNoOperations_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_VmInterruptComparable_methods[] = { + ZEND_ME(VmInterruptComparable, __construct, arginfo_class_VmInterruptComparable___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_DoOperationNoCast(void) { zend_class_entry ce, *class_entry; @@ -104,6 +112,22 @@ static zend_class_entry *register_class_NumericCastableNoOperations(void) return class_entry; } +static zend_class_entry *register_class_VmInterruptComparable(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "VmInterruptComparable", class_VmInterruptComparable_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL); + + zval property_val_default_value; + ZVAL_UNDEF(&property_val_default_value); + zend_string *property_val_name = zend_string_init("val", sizeof("val") - 1, true); + zend_declare_typed_property(class_entry, property_val_name, &property_val_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release_ex(property_val_name, true); + + return class_entry; +} + static zend_class_entry *register_class_DimensionHandlersNoArrayAccess(void) { zend_class_entry ce, *class_entry; diff --git a/ext/zend_test/observer.c b/ext/zend_test/observer.c index 3a8b07778d5e..8883a9d1dfb6 100644 --- a/ext/zend_test/observer.c +++ b/ext/zend_test/observer.c @@ -74,6 +74,10 @@ static void observer_begin(zend_execute_data *execute_data) { assert_observer_opline(execute_data); + if (ZT_G(observer_set_vm_interrupt_on_begin)) { + zend_atomic_bool_store_ex(&EG(vm_interrupt), true); + } + if (!ZT_G(observer_show_output)) { return; } @@ -142,6 +146,14 @@ static void observer_end(zend_execute_data *execute_data, zval *retval) } } +static void (*zend_test_prev_interrupt_function)(zend_execute_data *execute_data); +static void zend_test_interrupt_function(zend_execute_data *execute_data) +{ + if (zend_test_prev_interrupt_function) { + zend_test_prev_interrupt_function(execute_data); + } +} + static void observer_show_init(zend_function *fbc) { if (fbc->common.function_name) { @@ -381,6 +393,7 @@ PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("zend_test.observer.show_init_backtrace", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_init_backtrace, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.observer.show_opcode", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_opcode, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_ENTRY("zend_test.observer.show_opcode_in_user_handler", "", PHP_INI_SYSTEM, OnUpdateString, observer_show_opcode_in_user_handler, zend_zend_test_globals, zend_test_globals) + STD_PHP_INI_BOOLEAN("zend_test.observer.set_vm_interrupt_on_begin", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_set_vm_interrupt_on_begin, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_init", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_init, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_switch", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_switch, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_destroy", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_destroy, zend_zend_test_globals, zend_test_globals) @@ -418,10 +431,20 @@ void zend_test_observer_init(INIT_FUNC_ARGS) zend_test_prev_execute_internal = zend_execute_internal; zend_execute_internal = zend_test_execute_internal; } + + if (ZT_G(observer_set_vm_interrupt_on_begin)) { + zend_test_prev_interrupt_function = zend_interrupt_function; + zend_interrupt_function = zend_test_interrupt_function; + } } void zend_test_observer_shutdown(SHUTDOWN_FUNC_ARGS) { + if (zend_interrupt_function == zend_test_interrupt_function) { + zend_interrupt_function = zend_test_prev_interrupt_function; + zend_test_prev_interrupt_function = NULL; + } + if (type != MODULE_TEMPORARY) { UNREGISTER_INI_ENTRIES(); } diff --git a/ext/zend_test/php_test.h b/ext/zend_test/php_test.h index e3cf4b4284ba..65b04856eb31 100644 --- a/ext/zend_test/php_test.h +++ b/ext/zend_test/php_test.h @@ -41,6 +41,7 @@ ZEND_BEGIN_MODULE_GLOBALS(zend_test) int observer_show_init_backtrace; int observer_show_opcode; char *observer_show_opcode_in_user_handler; + int observer_set_vm_interrupt_on_begin; int observer_nesting_depth; int observer_fiber_init; int observer_fiber_switch; diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 96f8db83c116..8cb3fe6cba01 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -543,6 +543,79 @@ static ZEND_FUNCTION(zend_call_method_if_exists) } RETURN_NULL(); } + if (Z_TYPE_P(return_value) == IS_REFERENCE) { + zend_unwrap_reference(return_value); + } +} + +static ZEND_FUNCTION(zend_test_call_with_consumed_args) +{ + zend_fcall_info fci = empty_fcall_info; + zend_fcall_info_cache fcc = empty_fcall_info_cache; + zval *args; + zend_long consumed_args; + zval retval; + uint32_t actual_consumed_args = 0; + uint32_t i; + zend_result call_result; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_ARRAY(args) + Z_PARAM_LONG(consumed_args) + ZEND_PARSE_PARAMETERS_END(); + + if (UNEXPECTED(consumed_args < 0 || consumed_args > UINT32_MAX)) { + zend_argument_value_error(3, "must be between 0 and 4294967295"); + RETURN_THROWS(); + } + + zend_fcall_info_args(&fci, args); + + ZVAL_UNDEF(&retval); + fci.retval = &retval; + fci.consumed_args = (uint32_t) consumed_args; + + call_result = zend_call_function(&fci, &fcc); + + for (i = 0; i < fci.param_count && i < 32; i++) { + if (Z_ISUNDEF(fci.params[i])) { + actual_consumed_args |= (1u << i); + } + } + + zend_fcall_info_args_clear(&fci, true); + + if (call_result == FAILURE || EG(exception)) { + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + RETURN_THROWS(); + } + + array_init(return_value); + add_assoc_long(return_value, "consumed_args", actual_consumed_args); + + if (Z_ISUNDEF(retval)) { + add_assoc_null(return_value, "retval"); + } else { + add_assoc_zval(return_value, "retval", &retval); + } +} + +static ZEND_FUNCTION(zend_test_refcount) +{ + zval *value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(value) + ZEND_PARSE_PARAMETERS_END(); + + if (!Z_REFCOUNTED_P(value)) { + RETURN_LONG(-1); + } + + RETURN_LONG(Z_REFCOUNT_P(value)); } static ZEND_FUNCTION(zend_get_unit_enum) @@ -1044,7 +1117,7 @@ static zend_object *zend_test_class_new(zend_class_entry *class_type) static void zend_test_class_free_obj(zend_object *object) { - zend_test_object *intern = (zend_test_object*)((char*)object - XtOffsetOf(zend_test_object, std)); + zend_test_object *intern = ZEND_CONTAINER_OF(object, zend_test_object, std); if (intern->tmp_method) { zend_internal_function *func = intern->tmp_method; @@ -1059,7 +1132,7 @@ static void zend_test_class_free_obj(zend_object *object) static zend_function *zend_test_class_method_get(zend_object **object, zend_string *name, const zval *key) { - zend_test_object *intern = (zend_test_object*)((char*)(*object) - XtOffsetOf(zend_test_object, std)); + zend_test_object *intern = ZEND_CONTAINER_OF(*object, zend_test_object, std); if (zend_string_equals_literal_ci(name, "test")) { zend_internal_function *fptr; @@ -1526,7 +1599,7 @@ PHP_MINIT_FUNCTION(zend_test) zend_test_class_handlers.get_method = zend_test_class_method_get; zend_test_class_handlers.clone_obj = NULL; zend_test_class_handlers.free_obj = zend_test_class_free_obj; - zend_test_class_handlers.offset = XtOffsetOf(zend_test_object, std); + zend_test_class_handlers.offset = offsetof(zend_test_object, std); zend_test_gen_stub_flag_compatibility_test = register_class_ZendTestGenStubFlagCompatibilityTest(); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 653630ed73b7..489d7d0a260b 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -305,6 +305,10 @@ function zend_object_init_with_constructor(string $class, mixed ...$args): mixed function zend_call_method_if_exists(object $obj, string $method, mixed ...$args): mixed {} + function zend_test_call_with_consumed_args(callable $cb, array $args, int $consumed_args): array {} + + function zend_test_refcount(mixed $value): int {} + function zend_test_zend_ini_parse_quantity(string $str): int {} function zend_test_zend_ini_parse_uquantity(string $str): int {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index adcae16cdf61..94f75cdb3601 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: e1fb73f5a5f455a3a1eb871e670f26b671da0407 + * Stub hash: 4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9 * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, IS_NEVER, 0) @@ -123,6 +123,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_call_method_if_exists, 0, 2 ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_call_with_consumed_args, 0, 3, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, cb, IS_CALLABLE, 0) + ZEND_ARG_TYPE_INFO(0, args, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, consumed_args, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_refcount, 0, 1, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_zend_ini_parse_quantity, 0, 1, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -316,6 +326,8 @@ static ZEND_FUNCTION(zend_get_current_func_name); static ZEND_FUNCTION(zend_call_method); static ZEND_FUNCTION(zend_object_init_with_constructor); static ZEND_FUNCTION(zend_call_method_if_exists); +static ZEND_FUNCTION(zend_test_call_with_consumed_args); +static ZEND_FUNCTION(zend_test_refcount); static ZEND_FUNCTION(zend_test_zend_ini_parse_quantity); static ZEND_FUNCTION(zend_test_zend_ini_parse_uquantity); static ZEND_FUNCTION(zend_test_zend_ini_str); @@ -448,6 +460,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_call_method, arginfo_zend_call_method) ZEND_FE(zend_object_init_with_constructor, arginfo_zend_object_init_with_constructor) ZEND_FE(zend_call_method_if_exists, arginfo_zend_call_method_if_exists) + ZEND_FE(zend_test_call_with_consumed_args, arginfo_zend_test_call_with_consumed_args) + ZEND_FE(zend_test_refcount, arginfo_zend_test_refcount) ZEND_FE(zend_test_zend_ini_parse_quantity, arginfo_zend_test_zend_ini_parse_quantity) ZEND_FE(zend_test_zend_ini_parse_uquantity, arginfo_zend_test_zend_ini_parse_uquantity) ZEND_FE(zend_test_zend_ini_str, arginfo_zend_test_zend_ini_str) diff --git a/ext/zend_test/test_decl.h b/ext/zend_test/test_decl.h index ba6aab902280..4a6babbe12b9 100644 --- a/ext/zend_test/test_decl.h +++ b/ext/zend_test/test_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: e1fb73f5a5f455a3a1eb871e670f26b671da0407 */ + * Stub hash: 4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9 */ -#ifndef ZEND_TEST_DECL_e1fb73f5a5f455a3a1eb871e670f26b671da0407_H -#define ZEND_TEST_DECL_e1fb73f5a5f455a3a1eb871e670f26b671da0407_H +#ifndef ZEND_TEST_DECL_4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9_H +#define ZEND_TEST_DECL_4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9_H typedef enum zend_enum_ZendTestUnitEnum { ZEND_ENUM_ZendTestUnitEnum_Foo = 1, @@ -27,4 +27,4 @@ typedef enum zend_enum_ZendTestEnumWithInterface { ZEND_ENUM_ZendTestEnumWithInterface_Bar = 2, } zend_enum_ZendTestEnumWithInterface; -#endif /* ZEND_TEST_DECL_e1fb73f5a5f455a3a1eb871e670f26b671da0407_H */ +#endif /* ZEND_TEST_DECL_4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9_H */ diff --git a/ext/zend_test/test_legacy_arginfo.h b/ext/zend_test/test_legacy_arginfo.h index b42d524d7a8a..a4c1ae3f2c96 100644 --- a/ext/zend_test/test_legacy_arginfo.h +++ b/ext/zend_test/test_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: e1fb73f5a5f455a3a1eb871e670f26b671da0407 + * Stub hash: 4bb5b467b9d62c0e0c6a7c1e069e8755403a0af9 * Has decl header: yes */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, 0) @@ -107,6 +107,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_call_method_if_exists, 0, 0, 2) ZEND_ARG_VARIADIC_INFO(0, args) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_test_call_with_consumed_args, 0, 0, 3) + ZEND_ARG_INFO(0, cb) + ZEND_ARG_INFO(0, args) + ZEND_ARG_INFO(0, consumed_args) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_test_refcount, 0, 0, 1) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + #define arginfo_zend_test_zend_ini_parse_quantity arginfo_zend_create_unterminated_string #define arginfo_zend_test_zend_ini_parse_uquantity arginfo_zend_create_unterminated_string @@ -279,6 +289,8 @@ static ZEND_FUNCTION(zend_get_current_func_name); static ZEND_FUNCTION(zend_call_method); static ZEND_FUNCTION(zend_object_init_with_constructor); static ZEND_FUNCTION(zend_call_method_if_exists); +static ZEND_FUNCTION(zend_test_call_with_consumed_args); +static ZEND_FUNCTION(zend_test_refcount); static ZEND_FUNCTION(zend_test_zend_ini_parse_quantity); static ZEND_FUNCTION(zend_test_zend_ini_parse_uquantity); static ZEND_FUNCTION(zend_test_zend_ini_str); @@ -383,6 +395,8 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(zend_call_method, arginfo_zend_call_method) ZEND_FE(zend_object_init_with_constructor, arginfo_zend_object_init_with_constructor) ZEND_FE(zend_call_method_if_exists, arginfo_zend_call_method_if_exists) + ZEND_FE(zend_test_call_with_consumed_args, arginfo_zend_test_call_with_consumed_args) + ZEND_FE(zend_test_refcount, arginfo_zend_test_refcount) ZEND_FE(zend_test_zend_ini_parse_quantity, arginfo_zend_test_zend_ini_parse_quantity) ZEND_FE(zend_test_zend_ini_parse_uquantity, arginfo_zend_test_zend_ini_parse_uquantity) ZEND_FE(zend_test_zend_ini_str, arginfo_zend_test_zend_ini_str) diff --git a/ext/zend_test/tests/consumed_args_basic.phpt b/ext/zend_test/tests/consumed_args_basic.phpt new file mode 100644 index 000000000000..711ac8124579 --- /dev/null +++ b/ext/zend_test/tests/consumed_args_basic.phpt @@ -0,0 +1,43 @@ +--TEST-- +zend_test_call_with_consumed_args(): consume a non-reference arg +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTF-- +array(3) %srefcount(3){ + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +int(1) +array(2) { + [0]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } + [1]=> + string(1) "x" +} diff --git a/ext/zend_test/tests/consumed_args_ob_start_refcount.phpt b/ext/zend_test/tests/consumed_args_ob_start_refcount.phpt new file mode 100644 index 000000000000..3d5088d284d9 --- /dev/null +++ b/ext/zend_test/tests/consumed_args_ob_start_refcount.phpt @@ -0,0 +1,23 @@ +--TEST-- +ob_start(): consumed callback arg has low refcount +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + int(2) +} diff --git a/ext/zend_test/tests/consumed_args_ref_arg.phpt b/ext/zend_test/tests/consumed_args_ref_arg.phpt new file mode 100644 index 000000000000..a528860006b0 --- /dev/null +++ b/ext/zend_test/tests/consumed_args_ref_arg.phpt @@ -0,0 +1,35 @@ +--TEST-- +zend_test_call_with_consumed_args(): do not consume reference args +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECT-- +int(0) +array(2) { + [0]=> + int(10) + [1]=> + int(20) +} +array(1) { + [0]=> + int(10) +} diff --git a/ext/zend_test/tests/consumed_args_ref_param_and_ref_arg.phpt b/ext/zend_test/tests/consumed_args_ref_param_and_ref_arg.phpt new file mode 100644 index 000000000000..01d4025842a4 --- /dev/null +++ b/ext/zend_test/tests/consumed_args_ref_param_and_ref_arg.phpt @@ -0,0 +1,37 @@ +--TEST-- +zend_test_call_with_consumed_args(): ref-required params with reference args are not consumed +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECT-- +int(0) +array(2) { + [0]=> + int(10) + [1]=> + int(20) +} +array(2) { + [0]=> + int(10) + [1]=> + int(20) +} diff --git a/ext/zend_test/tests/consumed_args_ref_required.phpt b/ext/zend_test/tests/consumed_args_ref_required.phpt new file mode 100644 index 000000000000..f261131a686a --- /dev/null +++ b/ext/zend_test/tests/consumed_args_ref_required.phpt @@ -0,0 +1,24 @@ +--TEST-- +zend_test_call_with_consumed_args(): ref-required params are not consumed +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTF-- +Warning: {closure:%s:%d}(): Argument #1 ($a) must be passed by reference, value given in %s on line %d +int(0) +int(42) diff --git a/ext/zend_test/tests/gh22175.phpt b/ext/zend_test/tests/gh22175.phpt new file mode 100644 index 000000000000..c2db3c9c4e0c --- /dev/null +++ b/ext/zend_test/tests/gh22175.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-22175: zend_call_method_if_exists() must unref return value +--CREDITS-- +YuanchengJiang +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTF-- +Notice: Only variable references should be returned by reference in %s on line %d diff --git a/ext/zend_test/tests/observer_jit_vm_interrupt.inc b/ext/zend_test/tests/observer_jit_vm_interrupt.inc new file mode 100644 index 000000000000..426d9fdc2cb2 --- /dev/null +++ b/ext/zend_test/tests/observer_jit_vm_interrupt.inc @@ -0,0 +1,8 @@ + +--FILE-- + +--EXPECT-- +total=2438400 diff --git a/ext/zend_test/tests/observer_vm_interrupt_tailcall_helper.phpt b/ext/zend_test/tests/observer_vm_interrupt_tailcall_helper.phpt new file mode 100644 index 000000000000..d0178bdbf614 --- /dev/null +++ b/ext/zend_test/tests/observer_vm_interrupt_tailcall_helper.phpt @@ -0,0 +1,26 @@ +--TEST-- +Observer: VM interrupt during tailcall helper dispatch +--DESCRIPTION-- +This exercises a VM interrupt raised while an opcode handler dispatches to an +extra-argument helper. On the tailcall VM, the helper may return an opline +tagged with ZEND_VM_ENTER_BIT; treating that tagged value as a zend_op * before +tailcalling the next handler can crash. +--EXTENSIONS-- +zend_test +--INI-- +opcache.jit=0 +zend_test.observer.set_vm_interrupt_on_begin=1 +--FILE-- + +--EXPECT-- +stdClass diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 1a65a1e87220..12344450678b 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -309,11 +309,15 @@ static zend_result php_zip_add_file(ze_zip_object *obj, const char *filename, si } flags ^= ZIP_FL_OPEN_FILE_NOW; zs = zip_source_filep(obj->za, fd, offset_start, offset_len); + if (!zs) { + fclose(fd); + return FAILURE; + } } else { zs = zip_source_file(obj->za, resolved_path, offset_start, offset_len); - } - if (!zs) { - return FAILURE; + if (!zs) { + return FAILURE; + } } /* Replace */ if (replace >= 0) { @@ -573,8 +577,12 @@ static char * php_zipobj_get_zip_comment(ze_zip_object *obj, int *len) /* {{{ */ } /* }}} */ -/* Close and free the zip_t */ -static bool php_zipobj_close(ze_zip_object *obj) /* {{{ */ +/* Close and free the zip_t. If the archive was opened as a string, the + * final contents of the archive will be assigned to *out_str and that + * string will afterwards be owned by the caller. + * + * If out_str is NULL, the final string contents, if any, will be discarded. */ +static bool php_zipobj_close(ze_zip_object *obj, zend_string **out_str) /* {{{ */ { struct zip *intern = obj->za; bool success = false; @@ -606,7 +614,19 @@ static bool php_zipobj_close(ze_zip_object *obj) /* {{{ */ obj->filename_len = 0; } + if (obj->out_str) { + if (out_str) { + *out_str = obj->out_str; + } else { + zend_string_release(obj->out_str); + } + obj->out_str = NULL; + } else { + ZEND_ASSERT(!out_str); + } + obj->za = NULL; + obj->from_string = false; return success; } /* }}} */ @@ -781,7 +801,10 @@ int php_zip_pcre(zend_string *regexp, char *path, int path_len, zval *return_val if ((path_len + namelist_len + 1) >= MAXPATHLEN) { php_error_docref(NULL, E_WARNING, "add_path string too long (max: %u, %zu given)", MAXPATHLEN - 1, (path_len + namelist_len + 1)); - zend_string_release_ex(namelist[i], false); + /* The loop isn't continued, so all remaining file names must get freed. */ + for (; i < files_cnt; i++) { + zend_string_release_ex(namelist[i], false); + } break; } @@ -1060,7 +1083,7 @@ static void php_zip_object_free_storage(zend_object *object) /* {{{ */ { ze_zip_object * intern = php_zip_fetch_object(object); - php_zipobj_close(intern); + php_zipobj_close(intern, NULL); #ifdef HAVE_PROGRESS_CALLBACK /* if not properly called by libzip */ @@ -1467,7 +1490,7 @@ PHP_METHOD(ZipArchive, open) } /* If we already have an opened zip, free it */ - php_zipobj_close(ze_obj); + php_zipobj_close(ze_obj, NULL); /* open for write without option to empty the archive */ if ((flags & (ZIP_TRUNCATE | ZIP_RDONLY)) == 0) { @@ -1491,28 +1514,34 @@ PHP_METHOD(ZipArchive, open) ze_obj->filename = resolved_path; ze_obj->filename_len = strlen(resolved_path); ze_obj->za = intern; + ze_obj->from_string = false; RETURN_TRUE; } /* }}} */ -/* {{{ Create new read-only zip using given string */ +/* {{{ Create new zip from a string, or a create an empty zip to be saved to a string */ PHP_METHOD(ZipArchive, openString) { - zend_string *buffer; + zend_string *buffer = NULL; + zend_long flags = 0; zval *self = ZEND_THIS; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &buffer) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sl", &buffer, &flags) == FAILURE) { RETURN_THROWS(); } + if (!buffer) { + buffer = ZSTR_EMPTY_ALLOC(); + } + ze_zip_object *ze_obj = Z_ZIP_P(self); - php_zipobj_close(ze_obj); + php_zipobj_close(ze_obj, NULL); zip_error_t err; zip_error_init(&err); - zip_source_t * zip_source = php_zip_create_string_source(buffer, NULL, &err); + zip_source_t * zip_source = php_zip_create_string_source(buffer, &ze_obj->out_str, &err); if (!zip_source) { ze_obj->err_zip = zip_error_code_zip(&err); @@ -1521,7 +1550,7 @@ PHP_METHOD(ZipArchive, openString) RETURN_LONG(ze_obj->err_zip); } - struct zip *intern = zip_open_from_source(zip_source, ZIP_RDONLY, &err); + struct zip *intern = zip_open_from_source(zip_source, flags, &err); if (!intern) { ze_obj->err_zip = zip_error_code_zip(&err); ze_obj->err_sys = zip_error_code_system(&err); @@ -1530,6 +1559,7 @@ PHP_METHOD(ZipArchive, openString) RETURN_LONG(ze_obj->err_zip); } + ze_obj->from_string = true; ze_obj->za = intern; zip_error_fini(&err); RETURN_TRUE; @@ -1568,7 +1598,34 @@ PHP_METHOD(ZipArchive, close) ZIP_FROM_OBJECT(intern, self); - RETURN_BOOL(php_zipobj_close(Z_ZIP_P(self))); + RETURN_BOOL(php_zipobj_close(Z_ZIP_P(self), NULL)); +} +/* }}} */ + +/* {{{ close the zip archive and get the result as a string */ +PHP_METHOD(ZipArchive, closeString) +{ + struct zip *intern; + zval *self = ZEND_THIS; + + ZEND_PARSE_PARAMETERS_NONE(); + + ZIP_FROM_OBJECT(intern, self); + + if (!Z_ZIP_P(self)->from_string) { + zend_throw_error(NULL, "ZipArchive::closeString can only be called on " + "an archive opened with ZipArchive::openString"); + RETURN_THROWS(); + } + + zend_string *ret = NULL; + bool success = php_zipobj_close(Z_ZIP_P(self), &ret); + ZEND_ASSERT(ret); + if (success) { + RETURN_STR(ret); + } + zend_string_release(ret); + RETURN_FALSE; } /* }}} */ @@ -2811,6 +2868,7 @@ static void php_zip_get_from(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ */ buffer = zend_string_safe_alloc(1, len, 0, false); zip_int64_t n = zip_fread(zf, ZSTR_VAL(buffer), ZSTR_LEN(buffer)); if (n < 1) { + zip_fclose(zf); zend_string_efree(buffer); RETURN_EMPTY_STRING(); } @@ -3051,7 +3109,7 @@ static void php_zip_free_prop_handler(zval *el) /* {{{ */ { static PHP_MINIT_FUNCTION(zip) { memcpy(&zip_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - zip_object_handlers.offset = XtOffsetOf(ze_zip_object, zo); + zip_object_handlers.offset = offsetof(ze_zip_object, zo); zip_object_handlers.free_obj = php_zip_object_free_storage; zip_object_handlers.dtor_obj = php_zip_object_dtor; zip_object_handlers.clone_obj = NULL; diff --git a/ext/zip/php_zip.h b/ext/zip/php_zip.h index 4c7661256538..9e57a54de1cc 100644 --- a/ext/zip/php_zip.h +++ b/ext/zip/php_zip.h @@ -69,6 +69,8 @@ typedef struct _ze_zip_object { HashTable *prop_handler; char *filename; size_t filename_len; + zend_string *out_str; + bool from_string; zip_int64_t last_id; int err_zip; int err_sys; @@ -81,9 +83,7 @@ typedef struct _ze_zip_object { zend_object zo; } ze_zip_object; -static inline ze_zip_object *php_zip_fetch_object(zend_object *obj) { - return (ze_zip_object *)((char*)(obj) - XtOffsetOf(ze_zip_object, zo)); -} +#define php_zip_fetch_object(obj) ZEND_CONTAINER_OF(obj, ze_zip_object, zo) #define Z_ZIP_P(zv) php_zip_fetch_object(Z_OBJ_P((zv))) diff --git a/ext/zip/php_zip.stub.php b/ext/zip/php_zip.stub.php index 19ea67e07fba..49dd19e53553 100644 --- a/ext/zip/php_zip.stub.php +++ b/ext/zip/php_zip.stub.php @@ -646,7 +646,7 @@ class ZipArchive implements Countable /** @tentative-return-type */ public function open(string $filename, int $flags = 0): bool|int {} - public function openString(string $data): bool|int {} + public function openString(string $data = '', int $flags = 0): bool|int {} /** * @tentative-return-type @@ -656,6 +656,8 @@ public function setPassword(#[\SensitiveParameter] string $password): bool {} /** @tentative-return-type */ public function close(): bool {} + public function closeString(): string|false {} + /** @tentative-return-type */ public function count(): int {} diff --git a/ext/zip/php_zip_arginfo.h b/ext/zip/php_zip_arginfo.h index ae2569400efe..faa6feb1cb1e 100644 --- a/ext/zip/php_zip_arginfo.h +++ b/ext/zip/php_zip_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_zip.stub.php instead. - * Stub hash: bf6706496639628a3287d0026f68f57ecebc4a55 */ + * Stub hash: d623efdfe5ac46f07aebf8fb120050c818f3d793 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zip_open, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0) @@ -45,8 +45,9 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_open, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_openString, 0, 1, MAY_BE_BOOL|MAY_BE_LONG) - ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_openString, 0, 0, MAY_BE_BOOL|MAY_BE_LONG) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, data, IS_STRING, 0, "\'\'") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_setPassword, 0, 1, _IS_BOOL, 0) @@ -56,6 +57,9 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_close, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ZipArchive_closeString, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZipArchive_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -320,6 +324,7 @@ ZEND_METHOD(ZipArchive, open); ZEND_METHOD(ZipArchive, openString); ZEND_METHOD(ZipArchive, setPassword); ZEND_METHOD(ZipArchive, close); +ZEND_METHOD(ZipArchive, closeString); ZEND_METHOD(ZipArchive, count); ZEND_METHOD(ZipArchive, getStatusString); ZEND_METHOD(ZipArchive, clearError); @@ -399,6 +404,7 @@ static const zend_function_entry class_ZipArchive_methods[] = { ZEND_ME(ZipArchive, openString, arginfo_class_ZipArchive_openString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, setPassword, arginfo_class_ZipArchive_setPassword, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, close, arginfo_class_ZipArchive_close, ZEND_ACC_PUBLIC) + ZEND_ME(ZipArchive, closeString, arginfo_class_ZipArchive_closeString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, count, arginfo_class_ZipArchive_count, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, getStatusString, arginfo_class_ZipArchive_getStatusString, ZEND_ACC_PUBLIC) ZEND_ME(ZipArchive, clearError, arginfo_class_ZipArchive_clearError, ZEND_ACC_PUBLIC) diff --git a/ext/zip/tests/ZipArchive_closeString_basic.phpt b/ext/zip/tests/ZipArchive_closeString_basic.phpt new file mode 100644 index 000000000000..852a7ebf53f3 --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeString_basic.phpt @@ -0,0 +1,25 @@ +--TEST-- +ZipArchive::closeString() basic +--EXTENSIONS-- +zip +--FILE-- +openString(); +$zip->addFromString('test1', '1'); +$zip->addFromString('test2', '2'); +$contents = $zip->closeString(); +echo $contents ? "OK\n" : "FAILED\n"; + +$zip = new ZipArchive(); +$zip->openString($contents); +var_dump($zip->getFromName('test1')); +var_dump($zip->getFromName('test2')); +var_dump($zip->getFromName('nonexistent')); + +?> +--EXPECT-- +OK +string(1) "1" +string(1) "2" +bool(false) diff --git a/ext/zip/tests/ZipArchive_closeString_error.phpt b/ext/zip/tests/ZipArchive_closeString_error.phpt new file mode 100644 index 000000000000..d5dca14a97fc --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeString_error.phpt @@ -0,0 +1,47 @@ +--TEST-- +ZipArchive::closeString() error cases +--EXTENSIONS-- +zip +--FILE-- +openString(); +var_dump($zip->open(__DIR__ . '/test.zip')); +try { + $zip->closeString(); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +echo "2.\n"; +$zip = new ZipArchive(); +$zip->openString('...'); +echo $zip->getStatusString() . "\n"; +try { + $zip->closeString(); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +echo "3.\n"; +$zip = new ZipArchive(); +$zip->openString(file_get_contents(__DIR__ . '/test.zip')); +echo gettype($zip->closeString()) . "\n"; +try { + $zip->closeString(); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECT-- +1. +bool(true) +ZipArchive::closeString can only be called on an archive opened with ZipArchive::openString +2. +Not a zip archive +Invalid or uninitialized Zip object +3. +string +Invalid or uninitialized Zip object diff --git a/ext/zip/tests/ZipArchive_closeString_false.phpt b/ext/zip/tests/ZipArchive_closeString_false.phpt new file mode 100644 index 000000000000..8a7d942527f6 --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeString_false.phpt @@ -0,0 +1,22 @@ +--TEST-- +ZipArchive::closeString() false return +--EXTENSIONS-- +zip +--FILE-- +openString($input)); +$zip->setCompressionIndex(0, ZipArchive::CM_DEFLATE); +var_dump($zip->closeString()); +echo $zip->getStatusString() . "\n"; +?> +--EXPECTREGEX-- +bool\(true\) + +Warning: ZipArchive::closeString\(\): (Zip archive inconsistent|Unexpected length of data).* +bool\(false\) +(Zip archive inconsistent|Unexpected length of data) diff --git a/ext/zip/tests/ZipArchive_closeString_variation.phpt b/ext/zip/tests/ZipArchive_closeString_variation.phpt new file mode 100644 index 000000000000..a1922ebd8f1f --- /dev/null +++ b/ext/zip/tests/ZipArchive_closeString_variation.phpt @@ -0,0 +1,39 @@ +--TEST-- +ZipArchive::closeString() variations +--EXTENSIONS-- +zip +--FILE-- +openString(); +var_dump($zip->closeString()); +echo $zip->getStatusString() . "\n"; + +echo "2. Update existing archive\n"; +$input = file_get_contents(__DIR__ . '/test.zip'); +$zip = new ZipArchive(); +$zip->openString($input); +$zip->addFromString('entry1.txt', ''); +$result = $zip->closeString(); +echo gettype($result) . "\n"; +var_dump($input !== $result); + +echo "3. Unchanged existing archive\n"; +$zip = new ZipArchive(); +$zip->openString($input); +$result = $zip->closeString(); +echo gettype($result) . "\n"; +var_dump($input === $result); + +?> +--EXPECT-- +1. Empty archive creation +string(0) "" +No error +2. Update existing archive +string +bool(true) +3. Unchanged existing archive +string +bool(true) diff --git a/ext/zip/tests/ZipArchive_openString.phpt b/ext/zip/tests/ZipArchive_openString.phpt index f787b4a84933..2a11505fcf69 100644 --- a/ext/zip/tests/ZipArchive_openString.phpt +++ b/ext/zip/tests/ZipArchive_openString.phpt @@ -4,8 +4,10 @@ ZipArchive::openString() method zip --FILE-- openString(file_get_contents(__DIR__."/test_procedural.zip")); +$zip->openString($input, ZipArchive::RDONLY); for ($i = 0; $i < $zip->numFiles; $i++) { $stat = $zip->statIndex($i); @@ -17,8 +19,22 @@ var_dump($zip->addFromString("foobar/baz", "baz")); var_dump($zip->addEmptyDir("blub")); var_dump($zip->close()); + +echo "2. CREATE and EXCL flags\n"; +$zip = new ZipArchive(); +var_dump($zip->openString($input, ZipArchive::CREATE)); +var_dump($zip->openString($input, ZipArchive::EXCL)); +echo $zip->getStatusString() . "\n"; + +echo "3. CHECKCONS flag\n"; +$inconsistent = file_get_contents(__DIR__ . '/checkcons.zip'); +$zip = new ZipArchive(); +var_dump($zip->openString($inconsistent)); +var_dump($zip->openString($inconsistent, ZipArchive::CHECKCONS)); + ?> --EXPECTF-- +1. Open read-only and read directory foo bar foobar/ @@ -26,3 +42,10 @@ foobar/baz bool(false) bool(false) bool(true) +2. CREATE and EXCL flags +bool(true) +int(10) +File already exists +3. CHECKCONS flag +bool(true) +int(%d) diff --git a/ext/zip/tests/checkcons.zip b/ext/zip/tests/checkcons.zip new file mode 100644 index 000000000000..50bcea128a46 Binary files /dev/null and b/ext/zip/tests/checkcons.zip differ diff --git a/ext/zip/tests/wrong-file-size.zip b/ext/zip/tests/wrong-file-size.zip new file mode 100644 index 000000000000..fc9fa1a434c7 Binary files /dev/null and b/ext/zip/tests/wrong-file-size.zip differ diff --git a/ext/zip/zip_source.c b/ext/zip/zip_source.c index 48b35862b903..03fa50867d56 100644 --- a/ext/zip/zip_source.c +++ b/ext/zip/zip_source.c @@ -155,6 +155,7 @@ static zip_int64_t php_zip_string_cb(void *userdata, void *data, zip_uint64_t le ctx->in_str = ctx->out_str; ctx->out_str = ZSTR_EMPTY_ALLOC(); if (ctx->dest) { + zend_string_release(*(ctx->dest)); *(ctx->dest) = zend_string_copy(ctx->in_str); } return 0; @@ -200,5 +201,10 @@ zip_source_t * php_zip_create_string_source(zend_string *str, zend_string **dest ctx->out_str = ZSTR_EMPTY_ALLOC(); ctx->dest = dest; ctx->mtime = time(NULL); + + if (dest) { + *dest = zend_string_copy(str); + } + return zip_source_function_create(php_zip_string_cb, (void*)ctx, err); } diff --git a/ext/zlib/tests/bug55544-win.phpt b/ext/zlib/tests/bug55544-win.phpt index 11116da28b6d..02a1ac837000 100644 Binary files a/ext/zlib/tests/bug55544-win.phpt and b/ext/zlib/tests/bug55544-win.phpt differ diff --git a/ext/zlib/tests/data/func.inc b/ext/zlib/tests/data/func.inc deleted file mode 100644 index 73cfc4e06ffc..000000000000 --- a/ext/zlib/tests/data/func.inc +++ /dev/null @@ -1,18 +0,0 @@ - (\d+\.\d+\.\d+),s', $info, $match)) { - // $version = $match[1]; - if (preg_match(',zlib(?!.*libXML).*Compiled Version (=> | []]); + deflate_init(ZLIB_ENCODING_DEFLATE, ['level' => 'foo']); } catch (TypeError $e) { echo $e->getMessage(), PHP_EOL; } +try { + deflate_init(ZLIB_ENCODING_DEFLATE, ['memory' => []]); +} catch (TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + deflate_init(ZLIB_ENCODING_DEFLATE, ['window' => new A()]); +} catch (TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + deflate_init(ZLIB_ENCODING_DEFLATE, ['strategy' => $fp]); +} catch (TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +fclose($fp); + ?> --EXPECT-- -deflate_init(): Argument #2 ($options) the value for option "strategy" must be of type int, array given +deflate_init(): Argument #2 ($options) the value for option "level" must be of type int, string given +deflate_init(): Argument #2 ($options) the value for option "memory" must be of type int, array given +deflate_init(): Argument #2 ($options) the value for option "window" must be of type int, A given +deflate_init(): Argument #2 ($options) the value for option "strategy" must be of type int, resource given diff --git a/ext/zlib/tests/gh22142.phpt b/ext/zlib/tests/gh22142.phpt new file mode 100644 index 000000000000..84397a4e85a9 --- /dev/null +++ b/ext/zlib/tests/gh22142.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-22142 (Assertion failure in zendi_try_get_long() on IS_UNDEF) +--EXTENSIONS-- +zlib +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +deflate_init(): Argument #2 ($options) the value for option "level" must be of type int, null given diff --git a/ext/zlib/tests/gzencode_variation2-win32.phpt b/ext/zlib/tests/gzencode_variation2-win32.phpt index 2cc1f982089d..183d67f359b8 100644 --- a/ext/zlib/tests/gzencode_variation2-win32.phpt +++ b/ext/zlib/tests/gzencode_variation2-win32.phpt @@ -7,9 +7,8 @@ zlib if( substr(PHP_OS, 0, 3) != "WIN" ) { die("skip.. only for Windows"); } -include 'data/func.inc'; -if (version_compare(get_zlib_version(), "1.2.11") < 0) { - die("skip - at least zlib 1.2.11 required, got " . get_zlib_version()); +if (version_compare(ZLIB_VERSION, "1.2.11") < 0) { + die("skip - at least zlib 1.2.11 required, got " . ZLIB_VERSION); } ?> --FILE-- diff --git a/ext/zlib/tests/gzgetc_basic.phpt b/ext/zlib/tests/gzgetc_basic.phpt index ce366e4b6b31..3540332a740e 100644 --- a/ext/zlib/tests/gzgetc_basic.phpt +++ b/ext/zlib/tests/gzgetc_basic.phpt @@ -4,8 +4,7 @@ Test function gzgetc() by calling it with its expected arguments zlib 1.2.5 zlib --SKIPIF-- 0) { +if (version_compare(ZLIB_VERSION, '1.2.5') > 0) { die('skip - only for zlib <= 1.2.5'); } ?> diff --git a/ext/zlib/tests/gzgetc_basic_1.phpt b/ext/zlib/tests/gzgetc_basic_1.phpt index cf6da96e5612..82d7c1941327 100644 --- a/ext/zlib/tests/gzgetc_basic_1.phpt +++ b/ext/zlib/tests/gzgetc_basic_1.phpt @@ -4,9 +4,7 @@ Test function gzgetc() by calling it with its expected arguments zlib 1.2.7 zlib --SKIPIF-- = 1.2.7'); } ?> diff --git a/ext/zlib/tests/inflate_add_no_dict.phpt b/ext/zlib/tests/inflate_add_no_dict.phpt new file mode 100644 index 000000000000..da805e567022 --- /dev/null +++ b/ext/zlib/tests/inflate_add_no_dict.phpt @@ -0,0 +1,22 @@ +--TEST-- +inflate_add(): Z_NEED_DICT returned when no dictionary provided to inflate_init() +--EXTENSIONS-- +zlib +--FILE-- + $dict]); +$compressed = deflate_add($dc, $data, ZLIB_FINISH); + +// Inflate without supplying the dictionary +$ic = inflate_init(ZLIB_ENCODING_DEFLATE); +$result = inflate_add($ic, $compressed, ZLIB_SYNC_FLUSH); +var_dump($result); + +?> +--EXPECTF-- +Warning: inflate_add(): Inflating this data requires a preset dictionary, please specify it in the options array of inflate_init() in %s on line %d +bool(false) diff --git a/ext/zlib/tests/inflate_init_window_type_error.phpt b/ext/zlib/tests/inflate_init_window_type_error.phpt new file mode 100644 index 000000000000..fbca3129681e --- /dev/null +++ b/ext/zlib/tests/inflate_init_window_type_error.phpt @@ -0,0 +1,16 @@ +--TEST-- +inflate_init(): window option type validation +--EXTENSIONS-- +zlib +--FILE-- + []]); +} catch (TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +inflate_init(): Argument #2 ($options) the value for option "window" must be of type int, array given diff --git a/ext/zlib/tests/zlib_filter_seek_deflate.phpt b/ext/zlib/tests/zlib_filter_seek_deflate.phpt index 6acee8e4e8c8..743f1be566fd 100644 --- a/ext/zlib/tests/zlib_filter_seek_deflate.phpt +++ b/ext/zlib/tests/zlib_filter_seek_deflate.phpt @@ -1,55 +1,52 @@ --TEST-- -zlib.deflate filter with seek to start +zlib.deflate write filter is not reset on seek --EXTENSIONS-- zlib --FILE-- $size1 ? "YES" : "NO") . "\n"; - +/* Seek to middle also succeeds */ $result = fseek($fp, 50, SEEK_SET); echo "Seek to middle: " . ($result === 0 ? "SUCCESS" : "FAILURE") . "\n"; fclose($fp); +/* Verify the compressed output is still valid */ $fp = fopen($file, 'r'); stream_filter_append($fp, 'zlib.inflate', STREAM_FILTER_READ); $content = stream_get_contents($fp); fclose($fp); -echo "Decompressed content matches text2: " . ($content === $text2 ? "YES" : "NO") . "\n"; +echo "Decompressed content matches: " . ($content === $text ? "YES" : "NO") . "\n"; ?> --CLEAN-- --EXPECTF-- -Size after first write: %d +Size after write: %d Seek to start: SUCCESS -Size after second write: %d -Second write is larger: YES - -Warning: fseek(): Stream filter zlib.deflate is seekable only to start position in %s on line %d -Seek to middle: FAILURE -Decompressed content matches text2: YES +Seek to middle: SUCCESS +Decompressed content matches: YES diff --git a/ext/zlib/tests/zlib_filter_write_seek_modes.phpt b/ext/zlib/tests/zlib_filter_write_seek_modes.phpt new file mode 100644 index 000000000000..39824a7283cf --- /dev/null +++ b/ext/zlib/tests/zlib_filter_write_seek_modes.phpt @@ -0,0 +1,78 @@ +--TEST-- +zlib.deflate write filter: write_seek_mode parameter +--EXTENSIONS-- +zlib +--FILE-- + 'reset']); +fwrite($fp, $text1); +ftruncate($fp, 0); +var_dump(fseek($fp, 0, SEEK_SET) === 0); +fwrite($fp, $text2); +fclose($fp); + +$fp = fopen($file, 'r'); +stream_filter_append($fp, 'zlib.inflate', STREAM_FILTER_READ); +$decoded = stream_get_contents($fp); +fclose($fp); +var_dump($decoded === $text2); + +/* "reset": only seekable to start */ +$fp = fopen($file, 'w+'); +stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, + ['write_seek_mode' => 'reset']); +fwrite($fp, $text1); +var_dump(fseek($fp, 5, SEEK_SET) === 0); +fclose($fp); + +/* "preserve": same as default; seeks accepted, state preserved */ +$fp = fopen($file, 'w+'); +stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, + ['write_seek_mode' => 'preserve']); +fwrite($fp, $text1); +var_dump(fseek($fp, 0, SEEK_SET) === 0); +var_dump(fseek($fp, 50, SEEK_SET) === 0); +fclose($fp); + +/* "strict": every write-chain seek fails */ +$fp = fopen($file, 'w+'); +stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, + ['write_seek_mode' => 'strict']); +fwrite($fp, $text1); +var_dump(@fseek($fp, 0, SEEK_SET) === -1); +var_dump(@fseek($fp, 50, SEEK_SET) === -1); +fclose($fp); + +/* Invalid mode: ValueError */ +$fp = fopen($file, 'w+'); +stream_filter_append($fp, 'zlib.deflate', STREAM_FILTER_WRITE, + ['write_seek_mode' => 'rewind']); +fclose($fp); + +?> +--CLEAN-- + +--EXPECTF-- +bool(true) +bool(true) + +Warning: fseek(): Stream filter zlib.deflate is seekable only to start position in %s +bool(false) +bool(true) +bool(true) +bool(true) +bool(true) + +Warning: stream_filter_append(): "write_seek_mode" filter parameter must be one of "preserve", "reset", or "strict" in %s + +Warning: stream_filter_append(): Unable to create or locate filter "zlib.deflate" in %s diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 115eedbc894a..5b630d74c889 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -47,9 +47,7 @@ ZEND_DECLARE_MODULE_GLOBALS(zlib) zend_class_entry *inflate_context_ce; static zend_object_handlers inflate_context_object_handlers; -static inline php_zlib_context *inflate_context_from_obj(zend_object *obj) { - return (php_zlib_context *)((char *)(obj) - XtOffsetOf(php_zlib_context, std)); -} +#define inflate_context_from_obj(obj) ZEND_CONTAINER_OF(obj, php_zlib_context, std) #define Z_INFLATE_CONTEXT_P(zv) inflate_context_from_obj(Z_OBJ_P(zv)) @@ -85,9 +83,7 @@ static void inflate_context_free_obj(zend_object *object) zend_class_entry *deflate_context_ce; static zend_object_handlers deflate_context_object_handlers; -static inline php_zlib_context *deflate_context_from_obj(zend_object *obj) { - return (php_zlib_context *)((char *)(obj) - XtOffsetOf(php_zlib_context, std)); -} +#define deflate_context_from_obj(obj) ZEND_CONTAINER_OF(obj, php_zlib_context, std) #define Z_DEFLATE_CONTEXT_P(zv) deflate_context_from_obj(Z_OBJ_P(zv)) @@ -854,6 +850,30 @@ static bool zlib_create_dictionary_string(HashTable *options, char **dict, size_ return true; } +ZEND_ATTRIBUTE_NONNULL static bool zlib_get_long_option(HashTable *options, const char *option_name, size_t option_name_len, zend_long *value) +{ + bool failed = false; + zval *option_buffer = zend_hash_str_find(options, option_name, option_name_len); + + if (!option_buffer) { + return true; + } + + /* The |H ZPP specifier may leave HashTable entries wrapped in IS_INDIRECT. */ + ZVAL_DEINDIRECT(option_buffer); + *value = zval_try_get_long(option_buffer, &failed); + if (UNEXPECTED(failed)) { + zend_argument_type_error( + 2, + "the value for option \"%.*s\" must be of type int, %s given", + (int) option_name_len, option_name, zend_zval_value_name(option_buffer) + ); + return false; + } + + return true; +} + /* {{{ Initialize an incremental inflate context with the specified encoding */ PHP_FUNCTION(inflate_init) { @@ -861,16 +881,14 @@ PHP_FUNCTION(inflate_init) zend_long encoding, window = 15; char *dict = NULL; size_t dictlen = 0; - HashTable *options = NULL; - zval *option_buffer; + HashTable *options = (HashTable *) &zend_empty_array; if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l|H", &encoding, &options)) { RETURN_THROWS(); } - if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("window"))) != NULL) { - ZVAL_DEINDIRECT(option_buffer); - window = zval_get_long(option_buffer); + if (!zlib_get_long_option(options, ZEND_STRL("window"), &window)) { + RETURN_THROWS(); } if (window < 8 || window > 15) { zend_value_error("zlib window size (logarithm) (" ZEND_LONG_FMT ") must be within 8..15", window); @@ -907,6 +925,7 @@ PHP_FUNCTION(inflate_init) } if (inflateInit2(&ctx->Z, encoding) != Z_OK) { + /* dict is stored in the ctx in the object and will thus be freed by zval_ptr_dtor(). */ zval_ptr_dtor(return_value); php_error_docref(NULL, E_WARNING, "Failed allocating zlib.inflate context"); RETURN_FALSE; @@ -1024,6 +1043,7 @@ PHP_FUNCTION(inflate_add) } break; } else { + zend_string_release_ex(out, false); php_error_docref(NULL, E_WARNING, "Inflating this data requires a preset dictionary, please specify it in the options array of inflate_init()"); RETURN_FALSE; } @@ -1080,49 +1100,38 @@ PHP_FUNCTION(deflate_init) zend_long encoding, level = -1, memory = 8, window = 15, strategy = Z_DEFAULT_STRATEGY; char *dict = NULL; size_t dictlen = 0; - HashTable *options = NULL; - zval *option_buffer; + HashTable *options = (HashTable*)&zend_empty_array; if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "l|H", &encoding, &options)) { RETURN_THROWS(); } - if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("level"))) != NULL) { - ZVAL_DEINDIRECT(option_buffer); - level = zval_get_long(option_buffer); + if (!zlib_get_long_option(options, ZEND_STRL("level"), &level)) { + RETURN_THROWS(); } if (level < -1 || level > 9) { zend_value_error("deflate_init(): \"level\" option must be between -1 and 9"); RETURN_THROWS(); } - if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("memory"))) != NULL) { - ZVAL_DEINDIRECT(option_buffer); - memory = zval_get_long(option_buffer); + if (!zlib_get_long_option(options, ZEND_STRL("memory"), &memory)) { + RETURN_THROWS(); } if (memory < 1 || memory > 9) { zend_value_error("deflate_init(): \"memory\" option must be between 1 and 9"); RETURN_THROWS(); } - if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("window"))) != NULL) { - ZVAL_DEINDIRECT(option_buffer); - window = zval_get_long(option_buffer); + if (!zlib_get_long_option(options, ZEND_STRL("window"), &window)) { + RETURN_THROWS(); } if (window < 8 || window > 15) { zend_value_error("deflate_init(): \"window\" option must be between 8 and 15"); RETURN_THROWS(); } - if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("strategy"))) != NULL) { - bool failed = false; - - ZVAL_DEINDIRECT(option_buffer); - strategy = zval_try_get_long(option_buffer, &failed); - if (UNEXPECTED(failed)) { - zend_argument_type_error(2, "the value for option \"strategy\" must be of type int, %s given", zend_zval_value_name(option_buffer)); - RETURN_THROWS(); - } + if (!zlib_get_long_option(options, ZEND_STRL("strategy"), &strategy)) { + RETURN_THROWS(); } switch (strategy) { case Z_FILTERED: @@ -1163,6 +1172,7 @@ PHP_FUNCTION(deflate_init) } if (deflateInit2(&ctx->Z, level, Z_DEFLATED, encoding, memory, strategy) != Z_OK) { + efree(dict); zval_ptr_dtor(return_value); php_error_docref(NULL, E_WARNING, "Failed allocating zlib.deflate context"); RETURN_FALSE; @@ -1342,7 +1352,7 @@ static PHP_MINIT_FUNCTION(zlib) inflate_context_ce->default_object_handlers = &inflate_context_object_handlers; memcpy(&inflate_context_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - inflate_context_object_handlers.offset = XtOffsetOf(php_zlib_context, std); + inflate_context_object_handlers.offset = offsetof(php_zlib_context, std); inflate_context_object_handlers.free_obj = inflate_context_free_obj; inflate_context_object_handlers.get_constructor = inflate_context_get_constructor; inflate_context_object_handlers.clone_obj = NULL; @@ -1353,7 +1363,7 @@ static PHP_MINIT_FUNCTION(zlib) deflate_context_ce->default_object_handlers = &deflate_context_object_handlers; memcpy(&deflate_context_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); - deflate_context_object_handlers.offset = XtOffsetOf(php_zlib_context, std); + deflate_context_object_handlers.offset = offsetof(php_zlib_context, std); deflate_context_object_handlers.free_obj = deflate_context_free_obj; deflate_context_object_handlers.get_constructor = deflate_context_get_constructor; deflate_context_object_handlers.clone_obj = NULL; diff --git a/ext/zlib/zlib_filter.c b/ext/zlib/zlib_filter.c index b6393feb9083..2d0e4fbb7fa4 100644 --- a/ext/zlib/zlib_filter.c +++ b/ext/zlib/zlib_filter.c @@ -357,9 +357,14 @@ static const php_stream_filter_ops php_zlib_deflate_ops = { static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *filterparams, bool persistent) { const php_stream_filter_ops *fops = NULL; + php_stream_filter_seekable_t write_seekable; php_zlib_filter_data *data; int status; + if (php_stream_filter_parse_write_seek_mode(filterparams, &write_seekable) == FAILURE) { + return NULL; + } + /* Create this filter */ data = pecalloc(1, sizeof(php_zlib_filter_data), persistent); if (!data) { @@ -498,7 +503,7 @@ static php_stream_filter *php_zlib_filter_create(const char *filtername, zval *f return NULL; } - return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START); + return php_stream_filter_alloc(fops, data, persistent, PSFS_SEEKABLE_START, write_seekable); } const php_stream_filter_factory php_zlib_filter_factory = { diff --git a/main/SAPI.c b/main/SAPI.c index 144f727dd1fe..62e1c89e4bb9 100644 --- a/main/SAPI.c +++ b/main/SAPI.c @@ -189,7 +189,7 @@ SAPI_API void sapi_read_post_data(void) *p = 0; break; default: - *p = tolower(*p); + *p = tolower((unsigned char)*p); break; } } @@ -731,10 +731,10 @@ SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg) } /* cut off trailing spaces, linefeeds and carriage-returns */ - if (header_line_len && isspace(header_line[header_line_len-1])) { + if (header_line_len && isspace((unsigned char)header_line[header_line_len - 1])) { do { header_line_len--; - } while(header_line_len && isspace(header_line[header_line_len-1])); + } while(header_line_len && isspace((unsigned char)header_line[header_line_len - 1])); header_line[header_line_len]='\0'; } diff --git a/main/fastcgi.c b/main/fastcgi.c index a4853d8f0535..02ef614f4639 100644 --- a/main/fastcgi.c +++ b/main/fastcgi.c @@ -735,7 +735,7 @@ int fcgi_listen(const char *path, int backlog) memset(&sa.sa_unix, 0, sizeof(sa.sa_unix)); sa.sa_unix.sun_family = AF_UNIX; memcpy(sa.sa_unix.sun_path, path, path_len + 1); - sock_len = XtOffsetOf(struct sockaddr_un, sun_path) + path_len; + sock_len = offsetof(struct sockaddr_un, sun_path) + path_len; #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN sa.sa_unix.sun_len = sock_len; #endif diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c index a9e159efc8bf..c0f88ad3b5ba 100644 --- a/main/fopen_wrappers.c +++ b/main/fopen_wrappers.c @@ -508,7 +508,7 @@ PHPAPI zend_string *php_resolve_path(const char *filename, size_t filename_lengt } /* Don't resolve paths which contain protocol (except of file://) */ - for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++); + for (p = filename; isalnum((unsigned char)*p) || *p == '+' || *p == '-' || *p == '.'; p++); if ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/')) { wrapper = php_stream_locate_url_wrapper(filename, &actual_path, STREAM_OPEN_FOR_INCLUDE); if (wrapper == &php_plain_files_wrapper) { @@ -540,7 +540,7 @@ PHPAPI zend_string *php_resolve_path(const char *filename, size_t filename_lengt /* Check for stream wrapper */ int is_stream_wrapper = 0; - for (p = ptr; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++); + for (p = ptr; isalnum((unsigned char)*p) || *p == '+' || *p == '-' || *p == '.'; p++); if ((*p == ':') && (p - ptr > 1) && (p[1] == '/') && (p[2] == '/')) { /* .:// or ..:// is not a stream wrapper */ if (p[-1] != '.' || p[-2] != '.' || p - 2 != ptr) { @@ -615,7 +615,7 @@ PHPAPI zend_string *php_resolve_path(const char *filename, size_t filename_lengt actual_path = trypath; /* Check for stream wrapper */ - for (p = trypath; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++); + for (p = trypath; isalnum((unsigned char)*p) || *p == '+' || *p == '-' || *p == '.'; p++); if ((*p == ':') && (p - trypath > 1) && (p[1] == '/') && (p[2] == '/')) { wrapper = php_stream_locate_url_wrapper(trypath, &actual_path, STREAM_OPEN_FOR_INCLUDE); if (!wrapper) { diff --git a/main/fopen_wrappers.h b/main/fopen_wrappers.h index f92c7d6fd443..62bebca9b715 100644 --- a/main/fopen_wrappers.h +++ b/main/fopen_wrappers.h @@ -28,9 +28,6 @@ PHPAPI int php_check_open_basedir(const char *path); PHPAPI int php_check_open_basedir_ex(const char *path, int warn); PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path); -/* OPENBASEDIR_CHECKPATH(filename) to ease merge between 6.x and 5.x */ -#define OPENBASEDIR_CHECKPATH(filename) php_check_open_basedir(filename) - PHPAPI zend_string *php_resolve_path(const char *filename, size_t filename_len, const char *path); PHPAPI FILE *php_fopen_with_path(const char *filename, const char *mode, const char *path, zend_string **opened_path); diff --git a/main/getopt.c b/main/getopt.c index 516d1c41022b..210c32152415 100644 --- a/main/getopt.c +++ b/main/getopt.c @@ -18,15 +18,14 @@ #include #include "php_getopt.h" -#define OPTERRCOLON (1) -#define OPTERRNF (2) -#define OPTERRARG (3) +#define OPTERRCOLON 1 +#define OPTERRNF 2 +#define OPTERRARG 3 // Print error message to stderr and return -2 to distinguish it from '?' command line option. static int php_opt_error(int argc, char * const *argv, int optint, int optchr, int err, int show_err) /* {{{ */ { - if (show_err) - { + if (show_err) { fprintf(stderr, "Error in argument %d, char %d: ", optint, optchr+1); switch(err) { @@ -57,8 +56,11 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char static char **prev_optarg = NULL; php_optidx = -1; + if (optarg) { + *optarg = NULL; + } - if(prev_optarg && prev_optarg != optarg) { + if (prev_optarg && prev_optarg != optarg) { /* reset the state */ optchr = 0; dash = 0; @@ -66,20 +68,18 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char prev_optarg = optarg; if (*optind >= argc) { - return(EOF); + return EOF; } if (!dash) { - if ((argv[*optind][0] != '-')) { - return(EOF); - } else { - if (!argv[*optind][1]) - { - /* - * use to specify stdin. Need to let pgm process this and - * the following args - */ - return(EOF); - } + if (argv[*optind][0] != '-') { + return EOF; + } + if (!argv[*optind][1]) { + /* + * use to specify stdin. Need to let pgm process this and + * the following args + */ + return EOF; } } if ((argv[*optind][0] == '-') && (argv[*optind][1] == '-')) { @@ -89,7 +89,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char /* '--' indicates end of args if not followed by a known long option name */ if (argv[*optind][2] == '\0') { (*optind)++; - return(EOF); + return EOF; } arg_start = 2; @@ -106,7 +106,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char php_optidx++; if (opts[php_optidx].opt_char == '-') { (*optind)++; - return(php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err)); + return php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err); } else if (opts[php_optidx].opt_name && !strncmp(&argv[*optind][2], opts[php_optidx].opt_name, arg_end) && arg_end == strlen(opts[php_optidx].opt_name)) { break; } @@ -124,7 +124,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char if (argv[*optind][optchr] == ':') { dash = 0; (*optind)++; - return (php_opt_error(argc, argv, *optind-1, optchr, OPTERRCOLON, show_err)); + return php_opt_error(argc, argv, *optind-1, optchr, OPTERRCOLON, show_err); } arg_start = 1 + optchr; } @@ -142,7 +142,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char optchr++; arg_start++; } - return(php_opt_error(argc, argv, errind, errchr, OPTERRNF, show_err)); + return php_opt_error(argc, argv, errind, errchr, OPTERRNF, show_err); } else if (argv[*optind][optchr] == opts[php_optidx].opt_char) { break; } @@ -157,7 +157,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char if (*optind == argc) { /* Was the value required or is it optional? */ if (opts[php_optidx].need_param == 1) { - return(php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err)); + return php_opt_error(argc, argv, *optind-1, optchr, OPTERRARG, show_err); } /* Optional value is not supported with - style */ } else if (opts[php_optidx].need_param == 1) { @@ -175,8 +175,7 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char } else { /* multiple options specified as one (exclude long opts) */ if (arg_start >= 2 && !((argv[*optind][0] == '-') && (argv[*optind][1] == '-'))) { - if (!argv[*optind][optchr+1]) - { + if (!argv[*optind][optchr+1]) { dash = 0; (*optind)++; } else { @@ -188,6 +187,6 @@ PHPAPI int php_getopt(int argc, char* const *argv, const opt_struct opts[], char return opts[php_optidx].opt_char; } assert(0); - return(0); /* never reached */ + return 0; /* never reached */ } /* }}} */ diff --git a/main/main.c b/main/main.c index cc3f1cae2586..6bda55ac8746 100644 --- a/main/main.c +++ b/main/main.c @@ -62,6 +62,7 @@ #include "win32/php_registry.h" #include "ext/standard/flock_compat.h" #endif +#include "Zend/zend_builtin_functions.h" #include "Zend/zend_exceptions.h" #if PHP_SIGCHILD @@ -801,6 +802,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY_EX("display_errors", "1", PHP_INI_ALL, OnUpdateDisplayErrors, display_errors, php_core_globals, core_globals, display_errors_mode) STD_PHP_INI_BOOLEAN("display_startup_errors", "1", PHP_INI_ALL, OnUpdateBool, display_startup_errors, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("enable_dl", "1", PHP_INI_SYSTEM, OnUpdateBool, enable_dl, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("error_include_args", "0", PHP_INI_ALL, OnUpdateBool, error_include_args, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("expose_php", "1", PHP_INI_SYSTEM, OnUpdateBool, expose_php, php_core_globals, core_globals) STD_PHP_INI_ENTRY("docref_root", "", PHP_INI_ALL, OnUpdateString, docref_root, php_core_globals, core_globals) STD_PHP_INI_ENTRY("docref_ext", "", PHP_INI_ALL, OnUpdateString, docref_ext, php_core_globals, core_globals) @@ -1132,7 +1134,14 @@ PHPAPI ZEND_COLD void php_verror(const char *docref, const char *params, int typ /* if we still have memory then format the origin */ if (is_function) { - origin_len = spprintf(&origin, 0, "%s%s%s(%s)", class_name, space, function, params); + zend_string *dynamic_params = NULL; + if (PG(error_include_args)) { + dynamic_params = zend_trace_current_function_args_string(); + } + origin_len = spprintf(&origin, 0, "%s%s%s(%s)", class_name, space, function, dynamic_params ? ZSTR_VAL(dynamic_params) : params); + if (dynamic_params) { + zend_string_release(dynamic_params); + } } else { origin_len = strlen(function); origin = estrndup(function, origin_len); diff --git a/main/network.c b/main/network.c index f652cf555ffb..90d1716b5582 100644 --- a/main/network.c +++ b/main/network.c @@ -1459,7 +1459,7 @@ static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf if (*hstbuflen == 0) { *hstbuflen = 1024; - *tmphstbuf = (char *)malloc (*hstbuflen); + *tmphstbuf = (char *)pemalloc(*hstbuflen, true); } while (( res = @@ -1467,7 +1467,7 @@ static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf && (errno == ERANGE)) { /* Enlarge the buffer. */ *hstbuflen *= 2; - *tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen); + *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true); } if (res != 0) { @@ -1485,7 +1485,7 @@ static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf if (*hstbuflen == 0) { *hstbuflen = 1024; - *tmphstbuf = (char *)malloc (*hstbuflen); + *tmphstbuf = (char *)pemalloc(*hstbuflen, true); } while ((NULL == ( hp = @@ -1493,7 +1493,7 @@ static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf && (errno == ERANGE)) { /* Enlarge the buffer. */ *hstbuflen *= 2; - *tmphstbuf = (char *)realloc (*tmphstbuf,*hstbuflen); + *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true); } return hp; } @@ -1503,11 +1503,11 @@ static struct hostent * gethostname_re (const char *host,struct hostent *hostbuf { if (*hstbuflen == 0) { *hstbuflen = sizeof(struct hostent_data); - *tmphstbuf = (char *)malloc (*hstbuflen); + *tmphstbuf = (char *)pemalloc(*hstbuflen, true); } else { if (*hstbuflen < sizeof(struct hostent_data)) { *hstbuflen = sizeof(struct hostent_data); - *tmphstbuf = (char *)realloc(*tmphstbuf, *hstbuflen); + *tmphstbuf = (char *)perealloc(*tmphstbuf, *hstbuflen, true); } } memset((void *)(*tmphstbuf),0,*hstbuflen); diff --git a/main/output.c b/main/output.c index 72b42301a724..664adf7f1688 100644 --- a/main/output.c +++ b/main/output.c @@ -970,6 +970,7 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl handler->func.user->fci.param_count = 2; handler->func.user->fci.params = ob_args; handler->func.user->fci.retval = &retval; + handler->func.user->fci.consumed_args = zend_fci_consumed_arg(0); if (SUCCESS == zend_call_function(&handler->func.user->fci, &handler->func.user->fcc) && Z_TYPE(retval) != IS_UNDEF) { if (handler->flags & PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT) { diff --git a/main/php_globals.h b/main/php_globals.h index f6f57e0045c8..8a032e9edb13 100644 --- a/main/php_globals.h +++ b/main/php_globals.h @@ -59,6 +59,7 @@ struct _php_core_globals { uint8_t display_errors; bool display_startup_errors; + bool error_include_args; bool log_errors; bool ignore_repeated_errors; bool ignore_repeated_source; diff --git a/main/php_ini.c b/main/php_ini.c index 9925eafad1fd..a3b33da65bab 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -37,7 +37,7 @@ char *tmp = path; \ while (*tmp) { \ if (*tmp == '\\') *tmp = '/'; \ - else *tmp = tolower(*tmp); \ + else *tmp = tolower((unsigned char)*tmp); \ tmp++; \ } \ } @@ -694,7 +694,7 @@ void php_init_config(void) if (total_l) { size_t php_ini_scanned_files_len = (php_ini_scanned_files) ? strlen(php_ini_scanned_files) + 1 : 0; - php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1); + php_ini_scanned_files = (char *) perealloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1, true); if (!php_ini_scanned_files_len) { *php_ini_scanned_files = '\0'; } diff --git a/main/php_ini_builder.c b/main/php_ini_builder.c index 1063d014bc2a..cdc10f0b5e25 100644 --- a/main/php_ini_builder.c +++ b/main/php_ini_builder.c @@ -65,7 +65,7 @@ PHPAPI void php_ini_builder_define(struct php_ini_builder *b, const char *arg) if (val != NULL) { val++; - if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') { + if (!isalnum((unsigned char)*val) && *val != '"' && *val != '\'' && *val != '\0') { php_ini_builder_quoted(b, arg, val - arg - 1, val, arg + len - val); } else { php_ini_builder_realloc(b, len + strlen("\n")); diff --git a/main/php_ini_builder.h b/main/php_ini_builder.h index 0ad70b6f68a7..9f73cba13cb7 100644 --- a/main/php_ini_builder.h +++ b/main/php_ini_builder.h @@ -60,7 +60,7 @@ static inline char *php_ini_builder_finish(struct php_ini_builder *b) static inline void php_ini_builder_realloc(struct php_ini_builder *b, size_t delta) { /* reserve enough space for the null terminator */ - b->value = realloc(b->value, b->length + delta + 1); + b->value = perealloc(b->value, b->length + delta + 1, true); } /** diff --git a/main/php_odbc_utils.c b/main/php_odbc_utils.c index d3b18f2bd642..797a4ac04be1 100644 --- a/main/php_odbc_utils.c +++ b/main/php_odbc_utils.c @@ -71,12 +71,20 @@ PHPAPI bool php_odbc_connstr_should_quote(const char *str) } /** - * Estimates the worst-case scenario for a quoted version of a string's size. + * Gets the length of a string after it has been quoted. */ -PHPAPI size_t php_odbc_connstr_estimate_quote_length(const char *in_str) +PHPAPI size_t php_odbc_connstr_get_quoted_length(const char *in_str) { - /* Assume all '}'. Include '{,' '}', and the null terminator too */ - return (strlen(in_str) * 2) + 3; + /* Start with including the quotes ({}) and the null terminator */ + size_t size = 3; + /* Scan the string like strlen, doubling each } character. */ + while (*in_str) { + size++; + if (*in_str++ == '}') { + size++; + } + } + return size; } /** diff --git a/main/php_odbc_utils.h b/main/php_odbc_utils.h index 78353b49a814..f2bc46ebc5ba 100644 --- a/main/php_odbc_utils.h +++ b/main/php_odbc_utils.h @@ -16,5 +16,5 @@ PHPAPI bool php_odbc_connstr_is_quoted(const char *str); PHPAPI bool php_odbc_connstr_should_quote(const char *str); -PHPAPI size_t php_odbc_connstr_estimate_quote_length(const char *in_str); +PHPAPI size_t php_odbc_connstr_get_quoted_length(const char *in_str); PHPAPI size_t php_odbc_connstr_quote(char *out_str, const char *in_str, size_t out_str_size); diff --git a/main/php_streams.h b/main/php_streams.h index d248de7a8168..7622a7295af3 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -246,6 +246,8 @@ struct _php_stream { #endif struct _php_stream *enclosing_stream; /* this is a private stream owned by enclosing_stream */ + + zend_llist *error_list; }; /* php_stream */ #define PHP_STREAM_CONTEXT(stream) \ @@ -537,6 +539,7 @@ PHPAPI ssize_t _php_stream_passthru(php_stream * src STREAMS_DC); #define php_stream_passthru(stream) _php_stream_passthru((stream) STREAMS_CC) END_EXTERN_C() +#include "streams/php_stream_errors.h" #include "streams/php_stream_transport.h" #include "streams/php_stream_plain_wrapper.h" #include "streams/php_stream_glob_wrapper.h" @@ -640,10 +643,6 @@ PHPAPI const char *php_stream_locate_eol(php_stream *stream, zend_string *buf); #define php_stream_open_wrapper(path, mode, options, opened) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), NULL STREAMS_CC) #define php_stream_open_wrapper_ex(path, mode, options, opened, context) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), (context) STREAMS_CC) - -/* pushes an error message onto the stack for a wrapper instance */ -PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, const char *fmt, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4); - typedef enum { PHP_STREAM_UNCHANGED = 0, /* orig stream was seekable anyway */ PHP_STREAM_RELEASED = 1, /* newstream should be used; origstream is no longer valid */ diff --git a/main/php_variables.c b/main/php_variables.c index 7453014fa2cc..2121bfd8b35b 100644 --- a/main/php_variables.c +++ b/main/php_variables.c @@ -219,7 +219,7 @@ PHPAPI void php_register_variable_ex(const char *var_name, zval *val, zval *trac ip++; index_s = ip; - if (isspace(*ip)) { + if (isspace((unsigned char)*ip)) { ip++; } if (*ip==']') { @@ -542,7 +542,7 @@ SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data) if (arg == PARSE_COOKIE) { /* Remove leading spaces from cookie names, needed for multi-cookie header where ; can be followed by a space */ - while (isspace(*var)) { + while (isspace((unsigned char)*var)) { var++; } if (var == val || *var == '\0') { diff --git a/main/rfc1867.c b/main/rfc1867.c index 0f55a380a85e..e40a1a9e3008 100644 --- a/main/rfc1867.c +++ b/main/rfc1867.c @@ -390,7 +390,7 @@ static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header) } /* space in the beginning means same header */ - if (!isspace(line[0])) { + if (!isspace((unsigned char)line[0])) { value = strchr(line, ':'); } @@ -406,7 +406,7 @@ static int multipart_buffer_headers(multipart_buffer *self, zend_llist *header) } *value = '\0'; - do { value++; } while (isspace(*value)); + do { value++; } while (isspace((unsigned char)*value)); key = estrdup(line); smart_string_appends(&buf_value, value); @@ -503,7 +503,7 @@ static char *substring_conf(char *start, int len, char quote) static char *php_ap_getword_conf(const zend_encoding *encoding, char *str) { - while (*str && isspace(*str)) { + while (*str && isspace((unsigned char)*str)) { ++str; } @@ -519,7 +519,7 @@ static char *php_ap_getword_conf(const zend_encoding *encoding, char *str) } else { char *strend = str; - while (*strend && !isspace(*strend)) { + while (*strend && !isspace((unsigned char)*strend)) { ++strend; } return substring_conf(str, strend - str, 0); @@ -795,7 +795,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) goto fileupload_done; } - while (isspace(*cd)) { + while (isspace((unsigned char)*cd)) { ++cd; } @@ -803,7 +803,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) { char *key = NULL, *word = pair; - while (isspace(*cd)) { + while (isspace((unsigned char)*cd)) { ++cd; } diff --git a/main/snprintf.c b/main/snprintf.c index 44c70dd86479..73c981a1cee7 100644 --- a/main/snprintf.c +++ b/main/snprintf.c @@ -284,7 +284,7 @@ PHPAPI char * php_conv_fp(char format, double num, /* * Check for Infinity and NaN */ - if (isalpha((int)*p)) { + if (isalpha((unsigned char)*p)) { *len = strlen(p); memcpy(buf, p, *len + 1); *is_negative = false; @@ -431,11 +431,11 @@ typedef struct buf_area buffy; #define NUM( c ) ( c - '0' ) #define STR_TO_DEC( str, num ) \ - num = NUM( *str++ ) ; \ - while ( isdigit((int)*str ) ) \ + num = NUM( *(str)++ ) ; \ + while ( isdigit((unsigned char)*(str) ) ) \ { \ num *= 10 ; \ - num += NUM( *str++ ) ; \ + num += NUM( *(str)++ ) ; \ } /* @@ -529,7 +529,7 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ /* * Try to avoid checking for flags, width or precision */ - if (isascii((int)*fmt) && !islower((int)*fmt)) { + if (isascii((unsigned char)*fmt) && !islower((unsigned char)*fmt)) { /* * Recognize flags: -, #, BLANK, + */ @@ -551,7 +551,7 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ /* * Check if a width was specified */ - if (isdigit((int)*fmt)) { + if (isdigit((unsigned char)*fmt)) { STR_TO_DEC(fmt, min_width); adjust_width = true; } else if (*fmt == '*') { @@ -571,7 +571,7 @@ static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ if (*fmt == '.') { adjust_precision = true; fmt++; - if (isdigit((int)*fmt)) { + if (isdigit((unsigned char)*fmt)) { STR_TO_DEC(fmt, precision); } else if (*fmt == '*') { precision = va_arg(ap, int); diff --git a/main/spprintf.c b/main/spprintf.c index 7249c17cdda0..6553853d8104 100644 --- a/main/spprintf.c +++ b/main/spprintf.c @@ -146,12 +146,12 @@ #define NUM(c) (c - '0') #define STR_TO_DEC(str, num) do { \ - num = NUM(*str++); \ - while (isdigit((int)*str)) { \ + num = NUM(*(str)++); \ + while (isdigit((unsigned char)*(str))) {\ num *= 10; \ - num += NUM(*str++); \ + num += NUM(*(str)++); \ if (num >= INT_MAX / 10) { \ - while (isdigit((int)*str++)); \ + while (isdigit((unsigned char)*(str)++)); \ break; \ } \ } \ @@ -231,7 +231,7 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ /* * Try to avoid checking for flags, width or precision */ - if (isascii((int)*fmt) && !islower((int)*fmt)) { + if (isascii((unsigned char)*fmt) && !islower((unsigned char)*fmt)) { /* * Recognize flags: -, #, BLANK, + */ @@ -253,7 +253,7 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ /* * Check if a width was specified */ - if (isdigit((int)*fmt)) { + if (isdigit((unsigned char)*fmt)) { STR_TO_DEC(fmt, min_width); adjust_width = true; } else if (*fmt == '*') { @@ -273,7 +273,7 @@ static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_ if (*fmt == '.') { adjust_precision = true; fmt++; - if (isdigit((int)*fmt)) { + if (isdigit((unsigned char)*fmt)) { STR_TO_DEC(fmt, precision); } else if (*fmt == '*') { precision = va_arg(ap, int); diff --git a/main/streams/cast.c b/main/streams/cast.c index 10c93cbb3ffb..f480f3cd59a6 100644 --- a/main/streams/cast.c +++ b/main/streams/cast.c @@ -187,7 +187,6 @@ void php_stream_mode_sanitize_fdopen_fopencookie(php_stream *stream, char *resul result[res_curs] = '\0'; } /* }}} */ - /* {{{ php_stream_cast */ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, int show_err) { @@ -257,7 +256,7 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, b) no memory -> lets bail */ - php_error_docref(NULL, E_ERROR, "fopencookie failed"); + php_stream_fatal(stream, CastFailed, "fopencookie failed"); return FAILURE; #endif @@ -297,7 +296,8 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, if (php_stream_is_filtered(stream) && castas != PHP_STREAM_AS_FD_FOR_SELECT) { if (show_err) { - php_error_docref(NULL, E_WARNING, "Cannot cast a filtered stream on this system"); + php_stream_warn(stream, CastNotSupported, + "Cannot cast a filtered stream on this system"); } return FAILURE; } else if (stream->ops->cast && stream->ops->cast(stream, castas, ret) == SUCCESS) { @@ -313,7 +313,8 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, "select()able descriptor" }; - php_error_docref(NULL, E_WARNING, "Cannot represent a stream of type %s as a %s", stream->ops->label, cast_names[castas]); + php_stream_warn(stream, CastNotSupported, + "Cannot represent a stream of type %s as a %s", stream->ops->label, cast_names[castas]); } return FAILURE; @@ -328,7 +329,9 @@ PHPAPI zend_result _php_stream_cast(php_stream *stream, int castas, void **ret, * will be accessing the stream. Emit a warning so that the end-user will * know that they should try something else */ - php_error_docref(NULL, E_WARNING, ZEND_LONG_FMT " bytes of buffered data lost during stream conversion!", (zend_long)(stream->writepos - stream->readpos)); + php_stream_warn_nt(stream, BufferedDataLost, + ZEND_LONG_FMT " bytes of buffered data lost during stream conversion!", + (zend_long)(stream->writepos - stream->readpos)); } if (castas == PHP_STREAM_AS_STDIO && ret) { diff --git a/main/streams/filter.c b/main/streams/filter.c index 9ec144195693..35dbef455a30 100644 --- a/main/streams/filter.c +++ b/main/streams/filter.c @@ -259,7 +259,8 @@ PHPAPI php_stream_filter *php_stream_filter_create(const char *filtername, zval } PHPAPI php_stream_filter *_php_stream_filter_alloc(const php_stream_filter_ops *fops, - void *abstract, bool persistent, php_stream_filter_seekable_t seekable STREAMS_DC) + void *abstract, bool persistent, php_stream_filter_seekable_t read_seekable, + php_stream_filter_seekable_t write_seekable STREAMS_DC) { php_stream_filter *filter; @@ -267,13 +268,60 @@ PHPAPI php_stream_filter *_php_stream_filter_alloc(const php_stream_filter_ops * memset(filter, 0, sizeof(php_stream_filter)); filter->fops = fops; - filter->seekable = seekable; + filter->read_seekable = read_seekable; + filter->write_seekable = write_seekable; Z_PTR(filter->abstract) = abstract; filter->is_persistent = persistent; return filter; } +PHPAPI zend_result php_stream_filter_parse_write_seek_mode( + zval *filterparams, + php_stream_filter_seekable_t *write_seekable) +{ + *write_seekable = PSFS_SEEKABLE_ALWAYS; + + if (filterparams == NULL) { + return SUCCESS; + } + if (Z_TYPE_P(filterparams) != IS_ARRAY && Z_TYPE_P(filterparams) != IS_OBJECT) { + return SUCCESS; + } + + zval *tmp = zend_hash_str_find_ind(HASH_OF(filterparams), + "write_seek_mode", sizeof("write_seek_mode") - 1); + if (tmp == NULL) { + return SUCCESS; + } + + zend_string *tmp_str; + zend_string *str = zval_get_tmp_string(tmp, &tmp_str); + zend_result result = SUCCESS; + + if (zend_string_equals_literal(str, "preserve")) { + *write_seekable = PSFS_SEEKABLE_ALWAYS; + } else if (zend_string_equals_literal(str, "reset")) { + *write_seekable = PSFS_SEEKABLE_START; + } else if (zend_string_equals_literal(str, "strict")) { + *write_seekable = PSFS_SEEKABLE_NEVER; + } else { + php_error_docref(NULL, E_WARNING, + "\"write_seek_mode\" filter parameter must be one of " + "\"preserve\", \"reset\", or \"strict\""); + result = FAILURE; + } + + zend_tmp_string_release(tmp_str); + return result; +} + +PHPAPI int php_stream_filter_get_chain_type(php_stream *stream, php_stream_filter *filter) +{ + return filter->chain == &stream->readfilters ? + PHP_STREAM_FILTER_READ : PHP_STREAM_FILTER_WRITE; +} + PHPAPI void php_stream_filter_free(php_stream_filter *filter) { if (filter->fops->dtor) @@ -343,7 +391,8 @@ PHPAPI zend_result php_stream_filter_append_ex(php_stream_filter_chain *chain, p php_stream_bucket_unlink(bucket); php_stream_bucket_delref(bucket); } - php_error_docref(NULL, E_WARNING, "Filter failed to process pre-buffered data"); + php_stream_warn(stream, FilterFailed, + "Filter failed to process pre-buffered data"); return FAILURE; case PSFS_FEED_ME: /* We don't actually need data yet, diff --git a/main/streams/memory.c b/main/streams/memory.c index 44336f0e3e2b..af54c46dd9ad 100644 --- a/main/streams/memory.c +++ b/main/streams/memory.c @@ -362,7 +362,9 @@ static ssize_t php_stream_temp_write(php_stream *stream, const char *buf, size_t zend_string *membuf = php_stream_memory_get_buffer(ts->innerstream); php_stream *file = php_stream_fopen_temporary_file(ts->tmpdir, "php", NULL); if (file == NULL) { - php_error_docref(NULL, E_WARNING, "Unable to create temporary file, Check permissions in temporary files directory."); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + PermissionDenied, + "Unable to create temporary file, Check permissions in temporary files directory."); return 0; } php_stream_write(file, ZSTR_VAL(membuf), ZSTR_LEN(membuf)); @@ -489,7 +491,8 @@ static int php_stream_temp_cast(php_stream *stream, int castas, void **ret) file = php_stream_fopen_tmpfile(); if (file == NULL) { - php_error_docref(NULL, E_WARNING, "Unable to create temporary file."); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + PermissionDenied, "Unable to create temporary file."); return FAILURE; } @@ -639,7 +642,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con } if ((comma = (char *) memchr(path, ',', dlen)) == NULL) { - php_stream_wrapper_log_error(wrapper, options, "rfc2397: no comma in URL"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "rfc2397: no comma in URL"); return NULL; } @@ -651,7 +655,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con sep = memchr(path, '/', mlen); if (!semi && !sep) { - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal media type"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "rfc2397: illegal media type"); return NULL; } @@ -666,7 +671,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con path += plen; } else if (semi != path || mlen != sizeof(";base64")-1 || memcmp(path, ";base64", sizeof(";base64")-1)) { /* must be error since parameters are only allowed after mediatype */ zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal media type"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "rfc2397: illegal media type"); return NULL; } /* get parameters and potentially ';base64' */ @@ -679,7 +685,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con if (mlen != sizeof("base64")-1 || memcmp(path, "base64", sizeof("base64")-1)) { /* must be error since parameters are only allowed after mediatype and we have no '=' sign */ zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal parameter"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidParam, "rfc2397: illegal parameter"); return NULL; } base64 = 1; @@ -699,7 +706,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con } if (mlen) { zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: illegal URL"); + php_stream_wrapper_log_warn(wrapper, context, options, + InvalidUrl, "rfc2397: illegal URL"); return NULL; } } else { @@ -715,7 +723,8 @@ static php_stream * php_stream_url_wrap_rfc2397(php_stream_wrapper *wrapper, con base64_comma = php_base64_decode_ex((const unsigned char *)comma, dlen, 1); if (!base64_comma) { zval_ptr_dtor(&meta); - php_stream_wrapper_log_error(wrapper, options, "rfc2397: unable to decode"); + php_stream_wrapper_log_warn(wrapper, context, options, + DecodingFailed, "rfc2397: unable to decode"); return NULL; } comma = ZSTR_VAL(base64_comma); diff --git a/main/streams/php_stream_errors.h b/main/streams/php_stream_errors.h new file mode 100644 index 000000000000..c55c06e37f25 --- /dev/null +++ b/main/streams/php_stream_errors.h @@ -0,0 +1,239 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jakub Zelenka | + +----------------------------------------------------------------------+ + */ + +#ifndef PHP_STREAM_ERRORS_H +#define PHP_STREAM_ERRORS_H + +#include "php.h" +#include "php_streams.h" +#include "stream_errors_decl.h" + +BEGIN_EXTERN_C() + +/* Error mode context options (internal C constants) */ +#define PHP_STREAM_ERROR_MODE_ERROR 0 +#define PHP_STREAM_ERROR_MODE_EXCEPTION 1 +#define PHP_STREAM_ERROR_MODE_SILENT 2 + +/* Error store context options (internal C constants) */ +#define PHP_STREAM_ERROR_STORE_AUTO 0 +#define PHP_STREAM_ERROR_STORE_NONE 1 +#define PHP_STREAM_ERROR_STORE_NON_TERM 2 +#define PHP_STREAM_ERROR_STORE_TERMINAL 3 +#define PHP_STREAM_ERROR_STORE_ALL 4 + +/* Maximum operation nesting depth */ +#define PHP_STREAM_ERROR_MAX_DEPTH 1000 +/* Operations pool size to prevent extra allocations */ +#define PHP_STREAM_ERROR_OPERATION_POOL_SIZE 8 + +/* Shorthand for error code enum values */ +#define PHP_STREAM_EC(name) ZEND_ENUM_StreamErrorCode_##name + +/* Error code range boundaries (case_id based, ranges are [start, end)) */ +#define STREAM_EC_IO_START PHP_STREAM_EC(ReadFailed) +#define STREAM_EC_IO_END PHP_STREAM_EC(Disabled) +#define STREAM_EC_FS_START PHP_STREAM_EC(Disabled) +#define STREAM_EC_FS_END PHP_STREAM_EC(NotImplemented) +#define STREAM_EC_WRAPPER_START PHP_STREAM_EC(NotImplemented) +#define STREAM_EC_WRAPPER_END PHP_STREAM_EC(FilterNotFound) +#define STREAM_EC_FILTER_START PHP_STREAM_EC(FilterNotFound) +#define STREAM_EC_FILTER_END PHP_STREAM_EC(CastFailed) +#define STREAM_EC_CAST_START PHP_STREAM_EC(CastFailed) +#define STREAM_EC_CAST_END PHP_STREAM_EC(NetworkSendFailed) +#define STREAM_EC_NETWORK_START PHP_STREAM_EC(NetworkSendFailed) +#define STREAM_EC_NETWORK_END PHP_STREAM_EC(ArchivingFailed) +#define STREAM_EC_ENCODING_START PHP_STREAM_EC(ArchivingFailed) +#define STREAM_EC_ENCODING_END PHP_STREAM_EC(AllocationFailed) +#define STREAM_EC_RESOURCE_START PHP_STREAM_EC(AllocationFailed) +#define STREAM_EC_RESOURCE_END PHP_STREAM_EC(LockFailed) +#define STREAM_EC_LOCK_START PHP_STREAM_EC(LockFailed) +#define STREAM_EC_LOCK_END PHP_STREAM_EC(UserspaceNotImplemented) +#define STREAM_EC_USERSPACE_START PHP_STREAM_EC(UserspaceNotImplemented) +#define STREAM_EC_USERSPACE_END (PHP_STREAM_EC(UserspaceCallFailed) + 1) + +/* Wrapper name for PHP errors */ +#define PHP_STREAM_ERROR_WRAPPER_DEFAULT_NAME ":na" +#define PHP_STREAM_ERROR_WRAPPER_NAME(_wrapper) \ + (_wrapper ? _wrapper->wops->label : PHP_STREAM_ERROR_WRAPPER_DEFAULT_NAME) + +/* Error entry in chain (internal linked list) */ +typedef struct _php_stream_error_entry { + zend_string *message; + zend_enum_StreamErrorCode code; + char *wrapper_name; + char *param; + char *docref; + int severity; + bool terminating; + struct _php_stream_error_entry *next; +} php_stream_error_entry; + +/* Active error operation */ +typedef struct _php_stream_error_operation { + php_stream_error_entry *first_error; + php_stream_error_entry *last_error; + uint32_t error_count; +} php_stream_error_operation; + +/* Stored completed operation */ +typedef struct _php_stream_stored_error { + php_stream_error_entry *first_error; + uint32_t error_count; + struct _php_stream_stored_error *next; +} php_stream_stored_error; + +typedef struct { + php_stream_error_operation *current_operation; + uint32_t operation_depth; + php_stream_stored_error *stored_errors; + uint32_t stored_count; + php_stream_error_operation operation_pool[PHP_STREAM_ERROR_OPERATION_POOL_SIZE]; + php_stream_error_operation *overflow_operations; + uint32_t overflow_capacity; +} php_stream_error_state; + +/* Error operation management */ +PHPAPI php_stream_error_operation *php_stream_error_operation_begin(void); +PHPAPI void php_stream_error_operation_end(php_stream_context *context); +PHPAPI void php_stream_error_operation_end_for_stream(php_stream *stream); +PHPAPI void php_stream_error_operation_abort(void); + +/* State cleanup function */ +PHPAPI void php_stream_error_state_cleanup(void); + +/* Retrieve last stored errors as array of StreamError objects */ +PHPAPI void php_stream_error_get_last(zval *return_value); + +/* Clear all stored errors */ +PHPAPI void php_stream_error_clear_stored(void); + +/* Wrapper error reporting functions */ +PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 9, 10); + +PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *param1, const char *param2, + const char *fmt, ...) ZEND_ATTRIBUTE_FORMAT(printf, 10, 11); + +PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 6, 7); + +/* Legacy wrapper error log functions */ +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 7, 8); + +PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) + ZEND_ATTRIBUTE_FORMAT(printf, 8, 9); + +PHPAPI void php_stream_display_wrapper_name_errors(const char *wrapper_name, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption); + +PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption); + +PHPAPI void php_stream_tidy_wrapper_name_error_log(const char *wrapper_name); +PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper); + +/* Convenience macros - code argument is the bare case name (e.g. RenameFailed) */ +#define php_stream_wrapper_warn(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_warn_name(wrapper_name, context, options, code, ...) \ + php_stream_wrapper_error_with_name( \ + wrapper_name, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_warn_nt(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_WARNING, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_notice(wrapper, context, options, code, ...) \ + php_stream_wrapper_error(wrapper, context, NULL, options, E_NOTICE, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_warn_param(wrapper, context, options, code, param, ...) \ + php_stream_wrapper_error_param( \ + wrapper, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(code), param, __VA_ARGS__) + +#define php_stream_wrapper_warn_param_nt(wrapper, context, options, code, param, ...) \ + php_stream_wrapper_error_param( \ + wrapper, context, NULL, options, E_WARNING, false, \ + PHP_STREAM_EC(code), param, __VA_ARGS__) + +#define php_stream_wrapper_warn_param2(wrapper, context, options, code, param1, param2, ...) \ + php_stream_wrapper_error_param2( \ + wrapper, context, NULL, options, E_WARNING, true, \ + PHP_STREAM_EC(code), param1, param2, __VA_ARGS__) + +#define php_stream_wrapper_warn_param2_nt(wrapper, context, options, code, param1, param2, ...) \ + php_stream_wrapper_error_param2( \ + wrapper, context, NULL, options, E_WARNING, false, \ + PHP_STREAM_EC(code), param1, param2, __VA_ARGS__) + +#define php_stream_warn(stream, code, ...) \ + php_stream_error(stream, NULL, E_WARNING, true, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_warn_nt(stream, code, ...) \ + php_stream_error(stream, NULL, E_WARNING, false, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_warn_docref(stream, docref, code, ...) \ + php_stream_error(stream, docref, E_WARNING, true, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_notice(stream, code, ...) \ + php_stream_error(stream, NULL, E_NOTICE, false, PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_fatal(stream, code, ...) \ + php_stream_error(stream, NULL, E_ERROR, true, PHP_STREAM_EC(code), __VA_ARGS__) + +/* Legacy log variants */ +#define php_stream_wrapper_log_warn(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_WARNING, true, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_log_warn_nt(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_WARNING, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +#define php_stream_wrapper_log_notice(wrapper, context, options, code, ...) \ + php_stream_wrapper_log_error(wrapper, context, options, E_NOTICE, false, \ + PHP_STREAM_EC(code), __VA_ARGS__) + +END_EXTERN_C() + +#endif /* PHP_STREAM_ERRORS_H */ diff --git a/main/streams/php_stream_filter_api.h b/main/streams/php_stream_filter_api.h index a61bc48815f5..111127a8ad1a 100644 --- a/main/streams/php_stream_filter_api.h +++ b/main/streams/php_stream_filter_api.h @@ -118,7 +118,8 @@ struct _php_stream_filter { zval abstract; /* for use by filter implementation */ php_stream_filter *next; php_stream_filter *prev; - php_stream_filter_seekable_t seekable; + php_stream_filter_seekable_t read_seekable; + php_stream_filter_seekable_t write_seekable; bool is_persistent; /* link into stream and chain */ @@ -141,13 +142,17 @@ PHPAPI zend_result _php_stream_filter_flush(php_stream_filter *filter, bool fini PHPAPI php_stream_filter *php_stream_filter_remove(php_stream_filter *filter, bool call_dtor); PHPAPI void php_stream_filter_free(php_stream_filter *filter); PHPAPI php_stream_filter *_php_stream_filter_alloc(const php_stream_filter_ops *fops, - void *abstract, bool persistent, php_stream_filter_seekable_t seekable STREAMS_DC); + void *abstract, bool persistent, php_stream_filter_seekable_t read_seekable, + php_stream_filter_seekable_t write_seekable STREAMS_DC); +PHPAPI zend_result php_stream_filter_parse_write_seek_mode(zval *filterparams, + php_stream_filter_seekable_t *write_seekable); +PHPAPI int php_stream_filter_get_chain_type(php_stream *stream, php_stream_filter *filter); END_EXTERN_C() -#define php_stream_filter_alloc(fops, thisptr, persistent, seekable) \ - _php_stream_filter_alloc((fops), (thisptr), (persistent), (seekable) STREAMS_CC) -#define php_stream_filter_alloc_rel(fops, thisptr, persistent, seekable) \ - _php_stream_filter_alloc((fops), (thisptr), (persistent), (seekable) STREAMS_REL_CC) +#define php_stream_filter_alloc(fops, thisptr, persistent, rseek, wseek) \ + _php_stream_filter_alloc((fops), (thisptr), (persistent), (rseek), (wseek) STREAMS_CC) +#define php_stream_filter_alloc_rel(fops, thisptr, persistent, rseek, wseek) \ + _php_stream_filter_alloc((fops), (thisptr), (persistent), (rseek), (wseek) STREAMS_REL_CC) #define php_stream_filter_prepend(chain, filter) _php_stream_filter_prepend((chain), (filter)) #define php_stream_filter_append(chain, filter) _php_stream_filter_append((chain), (filter)) #define php_stream_filter_flush(filter, finish) _php_stream_filter_flush((filter), (finish)) diff --git a/main/streams/php_stream_transport.h b/main/streams/php_stream_transport.h index 0125035aaa69..60bea8e9e1fc 100644 --- a/main/streams/php_stream_transport.h +++ b/main/streams/php_stream_transport.h @@ -186,11 +186,17 @@ typedef enum { STREAM_CRYPTO_METHOD_ANY_SERVER = ((1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)) } php_stream_xport_crypt_method_t; +/* Flags for crypto status */ +#define STREAM_CRYPTO_STATUS_NONE 0 +#define STREAM_CRYPTO_STATUS_WANT_READ 1 +#define STREAM_CRYPTO_STATUS_WANT_WRITE 2 + /* These functions provide crypto support on the underlying transport */ BEGIN_EXTERN_C() PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_crypt_method_t crypto_method, php_stream *session_stream); PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate); +PHPAPI int php_stream_xport_crypto_get_status(php_stream *stream); END_EXTERN_C() typedef struct _php_stream_xport_crypto_param { @@ -204,7 +210,8 @@ typedef struct _php_stream_xport_crypto_param { } outputs; enum { STREAM_XPORT_CRYPTO_OP_SETUP, - STREAM_XPORT_CRYPTO_OP_ENABLE + STREAM_XPORT_CRYPTO_OP_ENABLE, + STREAM_XPORT_CRYPTO_OP_GET_STATUS } op; } php_stream_xport_crypto_param; diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 69258158fbe3..9335ab3fdb6c 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -392,7 +392,8 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun } if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { char errstr[256]; - php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", + php_stream_notice(stream, WriteFailed, + "Write of %zu bytes failed with errno=%d %s", count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } @@ -470,7 +471,8 @@ static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count) } else { if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { char errstr[256]; - php_error_docref(NULL, E_NOTICE, "Read of %zu bytes failed with errno=%d %s", + php_stream_notice(stream, ReadFailed, + "Read of %zu bytes failed with errno=%d %s", count, errno, php_socket_strerror_s(errno, errstr, sizeof(errstr))); } @@ -619,7 +621,8 @@ static int php_stdiop_seek(php_stream *stream, zend_off_t offset, int whence, ze assert(data != NULL); if (!data->is_seekable) { - php_error_docref(NULL, E_WARNING, "Cannot seek on this stream"); + php_stream_wrapper_warn(NULL, PHP_STREAM_CONTEXT(stream), REPORT_ERRORS, + SeekNotSupported, "Cannot seek on this stream"); return -1; } @@ -1156,7 +1159,8 @@ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zen char *persistent_id = NULL; if (FAILURE == php_stream_parse_fopen_modes(mode, &open_flags)) { - php_stream_wrapper_log_error(&php_plain_files_wrapper, options, "`%s' is not a valid mode for fopen", mode); + php_stream_wrapper_log_warn(&php_plain_files_wrapper, NULL, options, + InvalidMode, "`%s' is not a valid mode for fopen", mode); return NULL; } @@ -1309,8 +1313,9 @@ static int php_plain_files_unlink(php_stream_wrapper *wrapper, const char *url, if (ret == -1) { if (options & REPORT_ERRORS) { char errstr[256]; - php_error_docref1(NULL, url, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + UnlinkFailed, url, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1377,20 +1382,22 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f * access to the file in the meantime. */ if (VCWD_CHOWN(url_to, sb.st_uid, sb.st_gid)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, + !success, PHP_STREAM_EC(ChownFailed), url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } if (success) { if (VCWD_CHMOD(url_to, sb.st_mode)) { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); if (errno != EPERM) { success = 0; } + php_stream_wrapper_error_param2(wrapper, context, NULL, options, E_WARNING, + !success, PHP_STREAM_EC(ChownFailed), url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } # endif @@ -1398,12 +1405,14 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f VCWD_UNLINK(url_from); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2_nt(wrapper, context, options, StatFailed, + url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } } else { - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2_nt(wrapper, context, options, CopyFailed, + url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } # if !defined(ZTS) && !defined(TSRM_WIN32) umask(oldmask); @@ -1416,8 +1425,9 @@ static int php_plain_files_rename(php_stream_wrapper *wrapper, const char *url_f #ifdef PHP_WIN32 php_win32_docref2_from_error(GetLastError(), url_from, url_to); #else - php_error_docref2(NULL, url_from, url_to, E_WARNING, "%s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param2(wrapper, context, options, + RenameFailed, url_from, url_to, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); #endif return 0; } @@ -1441,7 +1451,8 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i int ret = VCWD_MKDIR(dir, (mode_t)mode); if (ret < 0 && (options & REPORT_ERRORS)) { - php_error_docref(NULL, E_WARNING, "%s", strerror(errno)); + php_stream_wrapper_warn(wrapper, context, options, + MkdirFailed, "%s", strerror(errno)); return 0; } @@ -1450,7 +1461,8 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i char buf[MAXPATHLEN]; if (!expand_filepath_with_mode(dir, buf, NULL, 0, CWD_EXPAND)) { - php_error_docref(NULL, E_WARNING, "Invalid path"); + php_stream_wrapper_warn(wrapper, context, options, + InvalidPath, "Invalid path"); return 0; } @@ -1502,7 +1514,9 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i int ret = VCWD_MKDIR(buf, (mode_t) mode); if (ret < 0 && errno != EEXIST) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn(wrapper, context, options, + MkdirFailed, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1522,7 +1536,9 @@ static int php_plain_files_mkdir(php_stream_wrapper *wrapper, const char *dir, i /* issue a warning to client when the last directory was created failed */ if (ret < 0) { if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn(wrapper, context, options, + MkdirFailed, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); } return 0; } @@ -1544,13 +1560,17 @@ static int php_plain_files_rmdir(php_stream_wrapper *wrapper, const char *url, i char errstr[256]; #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + NotFound, url, + "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif if (VCWD_RMDIR(url) < 0) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, options, + RmdirFailed, url, + "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } @@ -1573,7 +1593,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url #ifdef PHP_WIN32 if (!php_win32_check_trailing_space(url, strlen(url))) { - php_error_docref1(NULL, url, E_WARNING, "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + NotFound, url, + "%s", php_socket_strerror_s(ENOENT, errstr, sizeof(errstr))); return 0; } #endif @@ -1592,7 +1614,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url if (VCWD_ACCESS(url, F_OK) != 0) { FILE *file = VCWD_FOPEN(url, "w"); if (file == NULL) { - php_error_docref1(NULL, url, E_WARNING, "Unable to create file %s because %s", url, + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + PermissionDenied, url, + "Unable to create file %s because %s", url, php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } @@ -1606,7 +1630,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url case PHP_STREAM_META_OWNER: if(option == PHP_STREAM_META_OWNER_NAME) { if(php_get_uid_by_name((char *)value, &uid) != SUCCESS) { - php_error_docref1(NULL, url, E_WARNING, "Unable to find uid for %s", (char *)value); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + MetaFailed, url, + "Unable to find uid for %s", (char *)value); return 0; } } else { @@ -1618,7 +1644,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url case PHP_STREAM_META_GROUP_NAME: if(option == PHP_STREAM_META_GROUP_NAME) { if(php_get_gid_by_name((char *)value, &gid) != SUCCESS) { - php_error_docref1(NULL, url, E_WARNING, "Unable to find gid for %s", (char *)value); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + MetaFailed, url, + "Unable to find gid for %s", (char *)value); return 0; } } else { @@ -1636,8 +1664,9 @@ static int php_plain_files_metadata(php_stream_wrapper *wrapper, const char *url return 0; } if (ret == -1) { - php_error_docref1(NULL, url, E_WARNING, "Operation failed: %s", - php_socket_strerror_s(errno, errstr, sizeof(errstr))); + php_stream_wrapper_warn_param(wrapper, context, REPORT_ERRORS, + MetaFailed, url, + "Operation failed: %s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); return 0; } php_clear_stat_cache(0, NULL, 0); @@ -1730,7 +1759,9 @@ PHPAPI php_stream *_php_stream_fopen_with_path(const char *filename, const char *(cwd+3) = '\0'; if (snprintf(trypath, MAXPATHLEN, "%s%s", cwd, filename) >= MAXPATHLEN) { - php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN); + php_stream_wrapper_notice(NULL, NULL, REPORT_ERRORS, + PathTooLong, + "%s/%s path was truncated to %d", cwd, filename, MAXPATHLEN); } efree(cwd); @@ -1785,7 +1816,9 @@ PHPAPI php_stream *_php_stream_fopen_with_path(const char *filename, const char goto stream_skip; } if (snprintf(trypath, MAXPATHLEN, "%s/%s", ptr, filename) >= MAXPATHLEN) { - php_error_docref(NULL, E_NOTICE, "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN); + php_stream_wrapper_notice(NULL, NULL, REPORT_ERRORS, + PathTooLong, + "%s/%s path was truncated to %d", ptr, filename, MAXPATHLEN); } if (((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) && php_check_open_basedir_ex(trypath, 0)) { diff --git a/main/streams/stream_errors.c b/main/streams/stream_errors.c new file mode 100644 index 000000000000..3d7056c44d1e --- /dev/null +++ b/main/streams/stream_errors.c @@ -0,0 +1,918 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Jakub Zelenka | + +----------------------------------------------------------------------+ + */ + +#define ZEND_ENUM_StreamErrorCode_USE_NAME_TABLE +#include "php.h" +#include "php_globals.h" +#include "php_streams.h" +#include "php_stream_errors.h" +#include "zend_enum.h" +#include "zend_exceptions.h" +#include "ext/standard/file.h" +#include "stream_errors_arginfo.h" + +/* Class entries */ +static zend_class_entry *php_ce_stream_error_code; +static zend_class_entry *php_ce_stream_error_mode; +static zend_class_entry *php_ce_stream_error_store; +static zend_class_entry *php_ce_stream_error; +static zend_class_entry *php_ce_stream_exception; + +/* Forward declarations */ +static void php_stream_error_entry_free(php_stream_error_entry *entry); + +/* Helper to create a single StreamError object from an entry */ +static void php_stream_error_create_object(zval *zv, php_stream_error_entry *entry) +{ + object_init_ex(zv, php_ce_stream_error); + + const char *case_name = NULL; + if (entry->code > 0 && entry->code <= ZEND_ENUM_StreamErrorCode_CASE_COUNT) { + case_name = zend_enum_StreamErrorCode_case_names[entry->code]; + } + if (!case_name) { + case_name = "Generic"; + } + + zend_object *enum_obj = zend_enum_get_case_cstr(php_ce_stream_error_code, case_name); + ZEND_ASSERT(enum_obj != NULL); + + zval code_enum; + ZVAL_OBJ_COPY(&code_enum, enum_obj); + + zend_update_property(php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("code"), &code_enum); + zval_ptr_dtor(&code_enum); + + zend_update_property_str( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("message"), entry->message); + + zend_update_property_string(php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("wrapperName"), + entry->wrapper_name ? entry->wrapper_name : ""); + + zend_update_property_long( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("severity"), entry->severity); + + zend_update_property_bool( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("terminating"), entry->terminating); + + if (entry->param) { + zend_update_property_string( + php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("param"), entry->param); + } else { + zend_update_property_null(php_ce_stream_error, Z_OBJ_P(zv), ZEND_STRL("param")); + } +} + +/* Create array of StreamError objects from error chain */ +PHPAPI void php_stream_error_create_array(zval *zv, php_stream_error_entry *first) +{ + array_init(zv); + + php_stream_error_entry *entry = first; + while (entry) { + zval error_obj; + php_stream_error_create_object(&error_obj, entry); + zend_hash_next_index_insert_new(Z_ARRVAL_P(zv), &error_obj); + entry = entry->next; + } +} + +/* Context option helpers */ + +static int php_stream_auto_decide_error_store_mode(int error_mode) +{ + switch (error_mode) { + case PHP_STREAM_ERROR_MODE_ERROR: + return PHP_STREAM_ERROR_STORE_NONE; + case PHP_STREAM_ERROR_MODE_EXCEPTION: + return PHP_STREAM_ERROR_STORE_NON_TERM; + case PHP_STREAM_ERROR_MODE_SILENT: + return PHP_STREAM_ERROR_STORE_ALL; + default: + return PHP_STREAM_ERROR_STORE_NONE; + } +} + +static int php_stream_get_error_mode(php_stream_context *context) +{ + if (!context) { + return PHP_STREAM_ERROR_MODE_ERROR; + } + + zval *option = php_stream_context_get_option(context, "stream", "error_mode"); + if (!option) { + return PHP_STREAM_ERROR_MODE_ERROR; + } + + if (Z_TYPE_P(option) != IS_OBJECT + || !instanceof_function(Z_OBJCE_P(option), php_ce_stream_error_mode)) { + zend_type_error("stream context option 'error_mode' must be of type StreamErrorMode"); + return PHP_STREAM_ERROR_MODE_ERROR; + } + + switch ((zend_enum_StreamErrorMode) zend_enum_fetch_case_id(Z_OBJ_P(option))) { + case ZEND_ENUM_StreamErrorMode_Error: + return PHP_STREAM_ERROR_MODE_ERROR; + case ZEND_ENUM_StreamErrorMode_Exception: + return PHP_STREAM_ERROR_MODE_EXCEPTION; + case ZEND_ENUM_StreamErrorMode_Silent: + return PHP_STREAM_ERROR_MODE_SILENT; + } + + return PHP_STREAM_ERROR_MODE_ERROR; +} + +static int php_stream_get_error_store_mode(php_stream_context *context, int error_mode) +{ + if (!context) { + return php_stream_auto_decide_error_store_mode(error_mode); + } + + zval *option = php_stream_context_get_option(context, "stream", "error_store"); + if (!option) { + return php_stream_auto_decide_error_store_mode(error_mode); + } + + if (Z_TYPE_P(option) != IS_OBJECT + || !instanceof_function(Z_OBJCE_P(option), php_ce_stream_error_store)) { + zend_type_error("stream context option 'error_store' must be of type StreamErrorStore"); + return php_stream_auto_decide_error_store_mode(error_mode); + } + + switch ((zend_enum_StreamErrorStore) zend_enum_fetch_case_id(Z_OBJ_P(option))) { + case ZEND_ENUM_StreamErrorStore_Auto: + return php_stream_auto_decide_error_store_mode(error_mode); + case ZEND_ENUM_StreamErrorStore_None: + return PHP_STREAM_ERROR_STORE_NONE; + case ZEND_ENUM_StreamErrorStore_NonTerminating: + return PHP_STREAM_ERROR_STORE_NON_TERM; + case ZEND_ENUM_StreamErrorStore_Terminating: + return PHP_STREAM_ERROR_STORE_TERMINAL; + case ZEND_ENUM_StreamErrorStore_All: + return PHP_STREAM_ERROR_STORE_ALL; + } + + return php_stream_auto_decide_error_store_mode(error_mode); +} + +/* Helper functions */ + +static bool php_stream_has_terminating_error(php_stream_error_operation *op) +{ + php_stream_error_entry *entry = op->first_error; + while (entry) { + if (entry->terminating) { + return true; + } + entry = entry->next; + } + return false; +} + +static inline php_stream_error_operation *php_stream_get_operation_at_depth(uint32_t depth) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (depth < PHP_STREAM_ERROR_OPERATION_POOL_SIZE) { + return &state->operation_pool[depth]; + } else { + uint32_t overflow_index = depth - PHP_STREAM_ERROR_OPERATION_POOL_SIZE; + ZEND_ASSERT(overflow_index < state->overflow_capacity); + return &state->overflow_operations[overflow_index]; + } +} + +static inline php_stream_error_operation *php_stream_get_parent_operation(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (state->operation_depth <= 1) { + return NULL; + } + + return php_stream_get_operation_at_depth(state->operation_depth - 2); +} + +/* Clean up functions */ + +static void php_stream_error_entry_free(php_stream_error_entry *entry) +{ + while (entry) { + php_stream_error_entry *next = entry->next; + zend_string_release(entry->message); + efree(entry->wrapper_name); + efree(entry->param); + efree(entry->docref); + efree(entry); + entry = next; + } +} + +PHPAPI void php_stream_error_state_cleanup(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + while (state->current_operation) { + php_stream_error_operation *op = state->current_operation; + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + php_stream_error_entry_free(op->first_error); + + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; + } + + php_stream_stored_error *stored = state->stored_errors; + while (stored) { + php_stream_stored_error *next = stored->next; + php_stream_error_entry_free(stored->first_error); + efree(stored); + stored = next; + } + + state->stored_errors = NULL; + state->stored_count = 0; + state->operation_depth = 0; + + if (state->overflow_operations) { + efree(state->overflow_operations); + state->overflow_operations = NULL; + state->overflow_capacity = 0; + } +} + +PHPAPI void php_stream_error_get_last(zval *return_value) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (!state->stored_errors) { + ZVAL_EMPTY_ARRAY(return_value); + return; + } + + php_stream_error_create_array(return_value, state->stored_errors->first_error); +} + +PHPAPI void php_stream_error_clear_stored(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + php_stream_stored_error *stored = state->stored_errors; + while (stored) { + php_stream_stored_error *next = stored->next; + php_stream_error_entry_free(stored->first_error); + efree(stored); + stored = next; + } + + state->stored_errors = NULL; + state->stored_count = 0; +} + +/* Error operation stack management */ + +PHPAPI php_stream_error_operation *php_stream_error_operation_begin(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + + if (state->operation_depth >= PHP_STREAM_ERROR_MAX_DEPTH) { + php_error_docref(NULL, E_WARNING, + "Stream error operation depth exceeded (%u), possible infinite recursion", + state->operation_depth); + return NULL; + } + + php_stream_error_operation *op; + + if (state->operation_depth < PHP_STREAM_ERROR_OPERATION_POOL_SIZE) { + op = &state->operation_pool[state->operation_depth]; + } else { + uint32_t overflow_index = state->operation_depth - PHP_STREAM_ERROR_OPERATION_POOL_SIZE; + + if (overflow_index >= state->overflow_capacity) { + uint32_t new_capacity + = state->overflow_capacity == 0 ? 8 : state->overflow_capacity * 2; + php_stream_error_operation *new_overflow = erealloc( + state->overflow_operations, sizeof(php_stream_error_operation) * new_capacity); + state->overflow_operations = new_overflow; + state->overflow_capacity = new_capacity; + } + + op = &state->overflow_operations[overflow_index]; + } + + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; + + state->current_operation = op; + state->operation_depth++; + + return op; +} + +static void php_stream_error_add(zend_enum_StreamErrorCode code, const char *wrapper_name, + zend_string *message, const char *docref, char *param, int severity, bool terminating) +{ + php_stream_error_operation *op = FG(stream_error_state).current_operation; + ZEND_ASSERT(op != NULL); + + php_stream_error_entry *entry = emalloc(sizeof(php_stream_error_entry)); + entry->message = message; + entry->code = code; + entry->wrapper_name = wrapper_name ? estrdup(wrapper_name) : NULL; + entry->param = param; + entry->docref = docref ? estrdup(docref) : NULL; + entry->severity = severity; + entry->terminating = terminating; + entry->next = NULL; + + if (op->last_error) { + op->last_error->next = entry; + } else { + op->first_error = entry; + } + op->last_error = entry; + op->error_count++; +} + +/* Error reporting */ + +static void php_stream_call_error_handler(zval *handler, zval *errors_array) +{ + zend_fcall_info_cache fcc; + char *is_callable_error = NULL; + + if (!zend_is_callable_ex(handler, NULL, 0, NULL, &fcc, &is_callable_error)) { + if (is_callable_error) { + zend_type_error("stream error handler must be a valid callback, %s", is_callable_error); + efree(is_callable_error); + } + return; + } + + zend_call_known_fcc(&fcc, NULL, 1, errors_array, NULL); +} + +static void php_stream_throw_exception_with_errors(php_stream_error_operation *op) +{ + if (!op->first_error) { + return; + } + + zval ex; + object_init_ex(&ex, php_ce_stream_exception); + + /* Set message from first error */ + zend_update_property_string(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("message"), + ZSTR_VAL(op->first_error->message)); + + /* Set code from first error */ + zend_update_property_long(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("code"), + (zend_long) op->first_error->code); + + /* Build errors array and set it */ + zval errors_array; + php_stream_error_create_array(&errors_array, op->first_error); + zend_update_property(php_ce_stream_exception, Z_OBJ(ex), ZEND_STRL("errors"), &errors_array); + zval_ptr_dtor(&errors_array); + + zend_throw_exception_object(&ex); +} + +static void php_stream_report_errors(php_stream_context *context, php_stream_error_operation *op, + int error_mode, bool is_terminating) +{ + switch (error_mode) { + case PHP_STREAM_ERROR_MODE_ERROR: { + php_stream_error_entry *entry = op->first_error; + while (entry) { + if (entry->param) { + php_error_docref1(entry->docref, entry->param, entry->severity, "%s", + ZSTR_VAL(entry->message)); + } else { + php_error_docref( + entry->docref, entry->severity, "%s", ZSTR_VAL(entry->message)); + } + entry = entry->next; + } + break; + } + + case PHP_STREAM_ERROR_MODE_EXCEPTION: { + if (is_terminating) { + php_stream_throw_exception_with_errors(op); + } + break; + } + + case PHP_STREAM_ERROR_MODE_SILENT: + break; + } + + /* Call user error handler if set */ + zval *handler + = context ? php_stream_context_get_option(context, "stream", "error_handler") : NULL; + + if (handler) { + zval errors_array; + php_stream_error_create_array(&errors_array, op->first_error); + + php_stream_call_error_handler(handler, &errors_array); + + zval_ptr_dtor(&errors_array); + } +} + +/* Error storage */ + +PHPAPI void php_stream_error_operation_end(php_stream_context *context) +{ + php_stream_error_state *state = &FG(stream_error_state); + php_stream_error_operation *op = state->current_operation; + + if (!op) { + return; + } + + if (op->error_count > 0) { + if (context == NULL) { + context = FG(default_context); + } + + int error_mode = php_stream_get_error_mode(context); + int store_mode = php_stream_get_error_store_mode(context, error_mode); + + bool is_terminating = php_stream_has_terminating_error(op); + + php_stream_report_errors(context, op, error_mode, is_terminating); + + if (store_mode == PHP_STREAM_ERROR_STORE_NONE) { + php_stream_error_entry_free(op->first_error); + op->first_error = NULL; + } else { + php_stream_error_entry *entry = op->first_error; + php_stream_error_entry *prev = NULL; + php_stream_error_entry *to_store_first = NULL; + php_stream_error_entry *to_store_last = NULL; + uint32_t to_store_count = 0; + php_stream_error_entry *remaining_first = NULL; + + while (entry) { + php_stream_error_entry *next = entry->next; + bool should_store = false; + + if (store_mode == PHP_STREAM_ERROR_STORE_ALL) { + should_store = true; + } else if (store_mode == PHP_STREAM_ERROR_STORE_NON_TERM && !entry->terminating) { + should_store = true; + } else if (store_mode == PHP_STREAM_ERROR_STORE_TERMINAL && entry->terminating) { + should_store = true; + } + + if (should_store) { + entry->next = NULL; + if (to_store_last) { + to_store_last->next = entry; + } else { + to_store_first = entry; + } + to_store_last = entry; + to_store_count++; + } else { + entry->next = NULL; + if (prev) { + prev->next = entry; + } else { + remaining_first = entry; + } + prev = entry; + } + + entry = next; + } + + if (to_store_first) { + php_stream_stored_error *stored = emalloc(sizeof(php_stream_stored_error)); + stored->first_error = to_store_first; + stored->error_count = to_store_count; + stored->next = state->stored_errors; + + state->stored_errors = stored; + state->stored_count++; + } + + if (remaining_first) { + php_stream_error_entry_free(remaining_first); + } + + op->first_error = NULL; + } + } + + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; +} + +PHPAPI void php_stream_error_operation_end_for_stream(php_stream *stream) +{ + php_stream_error_state *state = &FG(stream_error_state); + php_stream_error_operation *op = state->current_operation; + + if (!op) { + return; + } + + if (op->error_count == 0) { + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + op->first_error = NULL; + op->last_error = NULL; + return; + } + + php_stream_context *context = PHP_STREAM_CONTEXT(stream); + php_stream_error_operation_end(context); +} + +PHPAPI void php_stream_error_operation_abort(void) +{ + php_stream_error_state *state = &FG(stream_error_state); + php_stream_error_operation *op = state->current_operation; + + if (!op) { + return; + } + + state->operation_depth--; + state->current_operation = php_stream_get_parent_operation(); + + php_stream_error_entry_free(op->first_error); + op->first_error = NULL; + op->last_error = NULL; + op->error_count = 0; +} + +/* Wrapper error reporting */ + +static void php_stream_wrapper_error_internal(const char *wrapper_name, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, char *param, zend_string *message) +{ + bool implicit_operation = (FG(stream_error_state).current_operation == NULL); + if (implicit_operation) { + php_stream_error_operation_begin(); + } + + php_stream_error_add(code, wrapper_name, message, docref, param, severity, terminating); + + if (implicit_operation) { + php_stream_error_operation_end(context); + } +} + +PHPAPI void php_stream_wrapper_error_with_name(const char *wrapper_name, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + php_stream_wrapper_error_internal( + wrapper_name, context, docref, options, severity, terminating, code, NULL, message); +} + +PHPAPI void php_stream_wrapper_error(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + + php_stream_wrapper_error_internal( + wrapper_name, context, docref, options, severity, terminating, code, NULL, message); +} + +PHPAPI void php_stream_wrapper_error_param(php_stream_wrapper *wrapper, php_stream_context *context, + const char *docref, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + char *param_copy = param ? estrdup(param) : NULL; + + php_stream_wrapper_error_internal(wrapper_name, context, docref, options, severity, terminating, + code, param_copy, message); +} + +PHPAPI void php_stream_wrapper_error_param2(php_stream_wrapper *wrapper, + php_stream_context *context, const char *docref, int options, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *param1, const char *param2, + const char *fmt, ...) +{ + if (!(options & REPORT_ERRORS)) { + return; + } + + char *combined_param; + spprintf(&combined_param, 0, "%s,%s", param1, param2); + + va_list args; + va_start(args, fmt); + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + + php_stream_wrapper_error_internal(wrapper_name, context, docref, options, severity, terminating, + code, combined_param, message); +} + +/* Stream error reporting */ + +PHPAPI void php_stream_error(php_stream *stream, const char *docref, int severity, + bool terminating, zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + zend_string *message = vstrpprintf(0, fmt, args); + va_end(args); + + const char *wrapper_name = stream->wrapper ? stream->wrapper->wops->label : "stream"; + + php_stream_context *context = PHP_STREAM_CONTEXT(stream); + + php_stream_wrapper_error_internal(wrapper_name, context, docref, REPORT_ERRORS, severity, + terminating, code, NULL, message); +} + +/* Legacy wrapper error logging */ + +static void php_stream_error_entry_dtor_legacy(void *error) +{ + php_stream_error_entry *entry = *(php_stream_error_entry **) error; + zend_string_release(entry->message); + efree(entry->wrapper_name); + efree(entry->param); + efree(entry->docref); + efree(entry); +} + +static void php_stream_error_list_dtor(zval *item) +{ + zend_llist *list = (zend_llist *) Z_PTR_P(item); + zend_llist_destroy(list); + efree(list); +} + +static void php_stream_wrapper_log_store_error(zend_string *message, zend_enum_StreamErrorCode code, + const char *wrapper_name, const char *param, int severity, bool terminating) +{ + char *param_copy = param ? estrdup(param) : NULL; + + php_stream_error_entry *entry = ecalloc(1, sizeof(php_stream_error_entry)); + entry->message = message; + entry->code = code; + entry->wrapper_name = wrapper_name ? estrdup(wrapper_name) : NULL; + entry->param = param_copy; + entry->severity = severity; + entry->terminating = terminating; + + if (!FG(wrapper_logged_errors)) { + ALLOC_HASHTABLE(FG(wrapper_logged_errors)); + zend_hash_init(FG(wrapper_logged_errors), 8, NULL, php_stream_error_list_dtor, 0); + } + + zend_llist *list + = zend_hash_str_find_ptr(FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); + + if (!list) { + zend_llist new_list; + zend_llist_init( + &new_list, sizeof(php_stream_error_entry *), php_stream_error_entry_dtor_legacy, 0); + list = zend_hash_str_update_mem(FG(wrapper_logged_errors), wrapper_name, + strlen(wrapper_name), &new_list, sizeof(new_list)); + } + + zend_llist_add_element(list, &entry); +} + +static void php_stream_wrapper_log_error_internal(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, char *param, const char *fmt, va_list args) +{ + zend_string *message = vstrpprintf(0, fmt, args); + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + + if (options & REPORT_ERRORS) { + php_stream_wrapper_error_internal( + wrapper_name, context, NULL, options, severity, terminating, code, param, message); + } else { + php_stream_wrapper_log_store_error( + message, code, wrapper_name, param, severity, terminating); + } +} + +PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + php_stream_wrapper_log_error_internal( + wrapper, context, options, severity, terminating, code, NULL, fmt, args); + va_end(args); +} + +PHPAPI void php_stream_wrapper_log_error_param(const php_stream_wrapper *wrapper, + php_stream_context *context, int options, int severity, bool terminating, + zend_enum_StreamErrorCode code, const char *param, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + char *param_copy = param ? estrdup(param) : NULL; + php_stream_wrapper_log_error_internal( + wrapper, context, options, severity, terminating, code, param_copy, fmt, args); + va_end(args); +} + +static zend_llist *php_stream_get_wrapper_errors_list(const char *wrapper_name) +{ + if (!FG(wrapper_logged_errors)) { + return NULL; + } + return (zend_llist *) zend_hash_str_find_ptr( + FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); +} + +PHPAPI void php_stream_display_wrapper_name_errors(const char *wrapper_name, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption) +{ + char *msg; + char errstr[256]; + int free_msg = 0; + + if (EG(exception)) { + return; + } + + char *tmp = estrdup(path); + if (strcmp(wrapper_name, PHP_STREAM_ERROR_WRAPPER_DEFAULT_NAME)) { + zend_llist *err_list = php_stream_get_wrapper_errors_list(wrapper_name); + if (err_list) { + size_t l = 0; + int brlen; + int i; + int count = (int) zend_llist_count(err_list); + const char *br; + php_stream_error_entry **err_entry_p; + zend_llist_position pos; + + if (PG(html_errors)) { + brlen = 7; + br = "
\n"; + } else { + brlen = 1; + br = "\n"; + } + + for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p; + err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) { + l += ZSTR_LEN((*err_entry_p)->message); + if (i < count - 1) { + l += brlen; + } + } + msg = emalloc(l + 1); + msg[0] = '\0'; + for (err_entry_p = zend_llist_get_first_ex(err_list, &pos), i = 0; err_entry_p; + err_entry_p = zend_llist_get_next_ex(err_list, &pos), i++) { + strcat(msg, ZSTR_VAL((*err_entry_p)->message)); + if (i < count - 1) { + strcat(msg, br); + } + } + + free_msg = 1; + } else { + if (!strcmp(wrapper_name, php_plain_files_wrapper.wops->label)) { + msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); + } else { + msg = "operation failed"; + } + } + } else { + msg = "no suitable wrapper could be found"; + } + + php_strip_url_passwd(tmp); + + zend_string *message = strpprintf(0, "%s: %s", caption, msg); + + php_stream_wrapper_error_internal(wrapper_name, context, NULL, REPORT_ERRORS, E_WARNING, true, + code, tmp, message); + + if (free_msg) { + efree(msg); + } +} + +PHPAPI void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, + php_stream_context *context, zend_enum_StreamErrorCode code, const char *path, + const char *caption) +{ + if (wrapper) { + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + php_stream_display_wrapper_name_errors(wrapper_name, context, code, path, caption); + } +} + +PHPAPI void php_stream_tidy_wrapper_name_error_log(const char *wrapper_name) +{ + if (FG(wrapper_logged_errors)) { + zend_hash_str_del(FG(wrapper_logged_errors), wrapper_name, strlen(wrapper_name)); + } +} + +PHPAPI void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) +{ + if (wrapper) { + const char *wrapper_name = PHP_STREAM_ERROR_WRAPPER_NAME(wrapper); + php_stream_tidy_wrapper_name_error_log(wrapper_name); + } +} + +/* StreamException methods */ + +PHP_METHOD(StreamException, getErrors) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zval *errors = zend_read_property( + php_ce_stream_exception, Z_OBJ_P(ZEND_THIS), ZEND_STRL("errors"), 1, NULL); + + RETURN_COPY(errors); +} + +/* Module init */ + +PHP_MINIT_FUNCTION(stream_errors) +{ + php_ce_stream_error_code = register_class_StreamErrorCode(); + php_ce_stream_error_mode = register_class_StreamErrorMode(); + php_ce_stream_error_store = register_class_StreamErrorStore(); + + php_ce_stream_error = register_class_StreamError(); + php_ce_stream_exception = register_class_StreamException(zend_ce_exception); + + return SUCCESS; +} + +PHP_MSHUTDOWN_FUNCTION(stream_errors) +{ + return SUCCESS; +} diff --git a/main/streams/stream_errors.stub.php b/main/streams/stream_errors.stub.php new file mode 100644 index 000000000000..2c56932c4850 --- /dev/null +++ b/main/streams/stream_errors.stub.php @@ -0,0 +1,143 @@ + */ + private array $errors = []; + + /** @return array */ + public function getErrors(): array {} +} diff --git a/main/streams/stream_errors_arginfo.h b/main/streams/stream_errors_arginfo.h new file mode 100644 index 000000000000..3a10e9bc5a40 --- /dev/null +++ b/main/streams/stream_errors_arginfo.h @@ -0,0 +1,259 @@ +/* This is a generated file, edit stream_errors.stub.php instead. + * Stub hash: 3e9ee6f0fdd8ecf3ded82728487a9e774137036a + * Has decl header: yes */ + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_StreamException_getErrors, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +static ZEND_METHOD(StreamException, getErrors); + +static const zend_function_entry class_StreamException_methods[] = { + ZEND_ME(StreamException, getErrors, arginfo_class_StreamException_getErrors, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static zend_class_entry *register_class_StreamErrorCode(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("StreamErrorCode", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "None", NULL); + + zend_enum_add_case_cstr(class_entry, "Generic", NULL); + + zend_enum_add_case_cstr(class_entry, "ReadFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "WriteFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SeekFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SeekNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "FlushFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "TruncateFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ConnectFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "BindFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ListenFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "NotWritable", NULL); + + zend_enum_add_case_cstr(class_entry, "NotReadable", NULL); + + zend_enum_add_case_cstr(class_entry, "Disabled", NULL); + + zend_enum_add_case_cstr(class_entry, "NotFound", NULL); + + zend_enum_add_case_cstr(class_entry, "PermissionDenied", NULL); + + zend_enum_add_case_cstr(class_entry, "AlreadyExists", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidPath", NULL); + + zend_enum_add_case_cstr(class_entry, "PathTooLong", NULL); + + zend_enum_add_case_cstr(class_entry, "OpenFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CreateFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "DupFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "UnlinkFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "RenameFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "MkdirFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "RmdirFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "StatFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "MetaFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ChmodFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ChownFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CopyFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "TouchFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidMode", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidMeta", NULL); + + zend_enum_add_case_cstr(class_entry, "ModeNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "Readonly", NULL); + + zend_enum_add_case_cstr(class_entry, "RecursionDetected", NULL); + + zend_enum_add_case_cstr(class_entry, "NotImplemented", NULL); + + zend_enum_add_case_cstr(class_entry, "NoOpener", NULL); + + zend_enum_add_case_cstr(class_entry, "PersistentNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperNotFound", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperDisabled", NULL); + + zend_enum_add_case_cstr(class_entry, "ProtocolUnsupported", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperRegistrationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperUnregistrationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "WrapperRestorationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "FilterNotFound", NULL); + + zend_enum_add_case_cstr(class_entry, "FilterFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CastFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "CastNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "MakeSeekableFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "BufferedDataLost", NULL); + + zend_enum_add_case_cstr(class_entry, "NetworkSendFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "NetworkRecvFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SslNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "ResumptionFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "SocketPathTooLong", NULL); + + zend_enum_add_case_cstr(class_entry, "OobNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "ProtocolError", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidUrl", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidResponse", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidHeader", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidParam", NULL); + + zend_enum_add_case_cstr(class_entry, "RedirectLimit", NULL); + + zend_enum_add_case_cstr(class_entry, "AuthFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "ArchivingFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "EncodingFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "DecodingFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "InvalidFormat", NULL); + + zend_enum_add_case_cstr(class_entry, "AllocationFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "TemporaryFileFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "LockFailed", NULL); + + zend_enum_add_case_cstr(class_entry, "LockNotSupported", NULL); + + zend_enum_add_case_cstr(class_entry, "UserspaceNotImplemented", NULL); + + zend_enum_add_case_cstr(class_entry, "UserspaceInvalidReturn", NULL); + + zend_enum_add_case_cstr(class_entry, "UserspaceCallFailed", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_StreamErrorMode(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("StreamErrorMode", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "Error", NULL); + + zend_enum_add_case_cstr(class_entry, "Exception", NULL); + + zend_enum_add_case_cstr(class_entry, "Silent", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_StreamErrorStore(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("StreamErrorStore", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "Auto", NULL); + + zend_enum_add_case_cstr(class_entry, "None", NULL); + + zend_enum_add_case_cstr(class_entry, "NonTerminating", NULL); + + zend_enum_add_case_cstr(class_entry, "Terminating", NULL); + + zend_enum_add_case_cstr(class_entry, "All", NULL); + + return class_entry; +} + +static zend_class_entry *register_class_StreamError(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "StreamError", NULL); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_READONLY_CLASS); + + zval property_code_default_value; + ZVAL_UNDEF(&property_code_default_value); + zend_string *property_code_class_StreamErrorCode = zend_string_init("StreamErrorCode", sizeof("StreamErrorCode")-1, 1); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_CODE), &property_code_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_code_class_StreamErrorCode, 0, 0)); + + zval property_message_default_value; + ZVAL_UNDEF(&property_message_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_MESSAGE), &property_message_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + + zval property_wrapperName_default_value; + ZVAL_UNDEF(&property_wrapperName_default_value); + zend_string *property_wrapperName_name = zend_string_init("wrapperName", sizeof("wrapperName") - 1, true); + zend_declare_typed_property(class_entry, property_wrapperName_name, &property_wrapperName_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release_ex(property_wrapperName_name, true); + + zval property_severity_default_value; + ZVAL_UNDEF(&property_severity_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_SEVERITY), &property_severity_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + + zval property_terminating_default_value; + ZVAL_UNDEF(&property_terminating_default_value); + zend_string *property_terminating_name = zend_string_init("terminating", sizeof("terminating") - 1, true); + zend_declare_typed_property(class_entry, property_terminating_name, &property_terminating_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release_ex(property_terminating_name, true); + + zval property_param_default_value; + ZVAL_UNDEF(&property_param_default_value); + zend_string *property_param_name = zend_string_init("param", sizeof("param") - 1, true); + zend_declare_typed_property(class_entry, property_param_name, &property_param_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); + zend_string_release_ex(property_param_name, true); + + return class_entry; +} + +static zend_class_entry *register_class_StreamException(zend_class_entry *class_entry_Exception) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "StreamException", class_StreamException_methods); + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_Exception, 0); + + zval property_errors_default_value; + ZVAL_EMPTY_ARRAY(&property_errors_default_value); + zend_string *property_errors_name = zend_string_init("errors", sizeof("errors") - 1, true); + zend_declare_typed_property(class_entry, property_errors_name, &property_errors_default_value, ZEND_ACC_PRIVATE, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_string_release_ex(property_errors_name, true); + + return class_entry; +} diff --git a/main/streams/stream_errors_decl.h b/main/streams/stream_errors_decl.h new file mode 100644 index 000000000000..4748768195f1 --- /dev/null +++ b/main/streams/stream_errors_decl.h @@ -0,0 +1,183 @@ +/* This is a generated file, edit stream_errors.stub.php instead. + * Stub hash: 3e9ee6f0fdd8ecf3ded82728487a9e774137036a */ + +#ifndef ZEND_STREAM_ERRORS_DECL_3e9ee6f0fdd8ecf3ded82728487a9e774137036a_H +#define ZEND_STREAM_ERRORS_DECL_3e9ee6f0fdd8ecf3ded82728487a9e774137036a_H + +typedef enum zend_enum_StreamErrorCode { + ZEND_ENUM_StreamErrorCode_None = 1, + ZEND_ENUM_StreamErrorCode_Generic = 2, + ZEND_ENUM_StreamErrorCode_ReadFailed = 3, + ZEND_ENUM_StreamErrorCode_WriteFailed = 4, + ZEND_ENUM_StreamErrorCode_SeekFailed = 5, + ZEND_ENUM_StreamErrorCode_SeekNotSupported = 6, + ZEND_ENUM_StreamErrorCode_FlushFailed = 7, + ZEND_ENUM_StreamErrorCode_TruncateFailed = 8, + ZEND_ENUM_StreamErrorCode_ConnectFailed = 9, + ZEND_ENUM_StreamErrorCode_BindFailed = 10, + ZEND_ENUM_StreamErrorCode_ListenFailed = 11, + ZEND_ENUM_StreamErrorCode_NotWritable = 12, + ZEND_ENUM_StreamErrorCode_NotReadable = 13, + ZEND_ENUM_StreamErrorCode_Disabled = 14, + ZEND_ENUM_StreamErrorCode_NotFound = 15, + ZEND_ENUM_StreamErrorCode_PermissionDenied = 16, + ZEND_ENUM_StreamErrorCode_AlreadyExists = 17, + ZEND_ENUM_StreamErrorCode_InvalidPath = 18, + ZEND_ENUM_StreamErrorCode_PathTooLong = 19, + ZEND_ENUM_StreamErrorCode_OpenFailed = 20, + ZEND_ENUM_StreamErrorCode_CreateFailed = 21, + ZEND_ENUM_StreamErrorCode_DupFailed = 22, + ZEND_ENUM_StreamErrorCode_UnlinkFailed = 23, + ZEND_ENUM_StreamErrorCode_RenameFailed = 24, + ZEND_ENUM_StreamErrorCode_MkdirFailed = 25, + ZEND_ENUM_StreamErrorCode_RmdirFailed = 26, + ZEND_ENUM_StreamErrorCode_StatFailed = 27, + ZEND_ENUM_StreamErrorCode_MetaFailed = 28, + ZEND_ENUM_StreamErrorCode_ChmodFailed = 29, + ZEND_ENUM_StreamErrorCode_ChownFailed = 30, + ZEND_ENUM_StreamErrorCode_CopyFailed = 31, + ZEND_ENUM_StreamErrorCode_TouchFailed = 32, + ZEND_ENUM_StreamErrorCode_InvalidMode = 33, + ZEND_ENUM_StreamErrorCode_InvalidMeta = 34, + ZEND_ENUM_StreamErrorCode_ModeNotSupported = 35, + ZEND_ENUM_StreamErrorCode_Readonly = 36, + ZEND_ENUM_StreamErrorCode_RecursionDetected = 37, + ZEND_ENUM_StreamErrorCode_NotImplemented = 38, + ZEND_ENUM_StreamErrorCode_NoOpener = 39, + ZEND_ENUM_StreamErrorCode_PersistentNotSupported = 40, + ZEND_ENUM_StreamErrorCode_WrapperNotFound = 41, + ZEND_ENUM_StreamErrorCode_WrapperDisabled = 42, + ZEND_ENUM_StreamErrorCode_ProtocolUnsupported = 43, + ZEND_ENUM_StreamErrorCode_WrapperRegistrationFailed = 44, + ZEND_ENUM_StreamErrorCode_WrapperUnregistrationFailed = 45, + ZEND_ENUM_StreamErrorCode_WrapperRestorationFailed = 46, + ZEND_ENUM_StreamErrorCode_FilterNotFound = 47, + ZEND_ENUM_StreamErrorCode_FilterFailed = 48, + ZEND_ENUM_StreamErrorCode_CastFailed = 49, + ZEND_ENUM_StreamErrorCode_CastNotSupported = 50, + ZEND_ENUM_StreamErrorCode_MakeSeekableFailed = 51, + ZEND_ENUM_StreamErrorCode_BufferedDataLost = 52, + ZEND_ENUM_StreamErrorCode_NetworkSendFailed = 53, + ZEND_ENUM_StreamErrorCode_NetworkRecvFailed = 54, + ZEND_ENUM_StreamErrorCode_SslNotSupported = 55, + ZEND_ENUM_StreamErrorCode_ResumptionFailed = 56, + ZEND_ENUM_StreamErrorCode_SocketPathTooLong = 57, + ZEND_ENUM_StreamErrorCode_OobNotSupported = 58, + ZEND_ENUM_StreamErrorCode_ProtocolError = 59, + ZEND_ENUM_StreamErrorCode_InvalidUrl = 60, + ZEND_ENUM_StreamErrorCode_InvalidResponse = 61, + ZEND_ENUM_StreamErrorCode_InvalidHeader = 62, + ZEND_ENUM_StreamErrorCode_InvalidParam = 63, + ZEND_ENUM_StreamErrorCode_RedirectLimit = 64, + ZEND_ENUM_StreamErrorCode_AuthFailed = 65, + ZEND_ENUM_StreamErrorCode_ArchivingFailed = 66, + ZEND_ENUM_StreamErrorCode_EncodingFailed = 67, + ZEND_ENUM_StreamErrorCode_DecodingFailed = 68, + ZEND_ENUM_StreamErrorCode_InvalidFormat = 69, + ZEND_ENUM_StreamErrorCode_AllocationFailed = 70, + ZEND_ENUM_StreamErrorCode_TemporaryFileFailed = 71, + ZEND_ENUM_StreamErrorCode_LockFailed = 72, + ZEND_ENUM_StreamErrorCode_LockNotSupported = 73, + ZEND_ENUM_StreamErrorCode_UserspaceNotImplemented = 74, + ZEND_ENUM_StreamErrorCode_UserspaceInvalidReturn = 75, + ZEND_ENUM_StreamErrorCode_UserspaceCallFailed = 76, +} zend_enum_StreamErrorCode; + +#define ZEND_ENUM_StreamErrorCode_CASE_COUNT 76 + +#ifdef ZEND_ENUM_StreamErrorCode_USE_NAME_TABLE +static const char *zend_enum_StreamErrorCode_case_names[ZEND_ENUM_StreamErrorCode_CASE_COUNT + 1] = { + [ZEND_ENUM_StreamErrorCode_None] = "None", + [ZEND_ENUM_StreamErrorCode_Generic] = "Generic", + [ZEND_ENUM_StreamErrorCode_ReadFailed] = "ReadFailed", + [ZEND_ENUM_StreamErrorCode_WriteFailed] = "WriteFailed", + [ZEND_ENUM_StreamErrorCode_SeekFailed] = "SeekFailed", + [ZEND_ENUM_StreamErrorCode_SeekNotSupported] = "SeekNotSupported", + [ZEND_ENUM_StreamErrorCode_FlushFailed] = "FlushFailed", + [ZEND_ENUM_StreamErrorCode_TruncateFailed] = "TruncateFailed", + [ZEND_ENUM_StreamErrorCode_ConnectFailed] = "ConnectFailed", + [ZEND_ENUM_StreamErrorCode_BindFailed] = "BindFailed", + [ZEND_ENUM_StreamErrorCode_ListenFailed] = "ListenFailed", + [ZEND_ENUM_StreamErrorCode_NotWritable] = "NotWritable", + [ZEND_ENUM_StreamErrorCode_NotReadable] = "NotReadable", + [ZEND_ENUM_StreamErrorCode_Disabled] = "Disabled", + [ZEND_ENUM_StreamErrorCode_NotFound] = "NotFound", + [ZEND_ENUM_StreamErrorCode_PermissionDenied] = "PermissionDenied", + [ZEND_ENUM_StreamErrorCode_AlreadyExists] = "AlreadyExists", + [ZEND_ENUM_StreamErrorCode_InvalidPath] = "InvalidPath", + [ZEND_ENUM_StreamErrorCode_PathTooLong] = "PathTooLong", + [ZEND_ENUM_StreamErrorCode_OpenFailed] = "OpenFailed", + [ZEND_ENUM_StreamErrorCode_CreateFailed] = "CreateFailed", + [ZEND_ENUM_StreamErrorCode_DupFailed] = "DupFailed", + [ZEND_ENUM_StreamErrorCode_UnlinkFailed] = "UnlinkFailed", + [ZEND_ENUM_StreamErrorCode_RenameFailed] = "RenameFailed", + [ZEND_ENUM_StreamErrorCode_MkdirFailed] = "MkdirFailed", + [ZEND_ENUM_StreamErrorCode_RmdirFailed] = "RmdirFailed", + [ZEND_ENUM_StreamErrorCode_StatFailed] = "StatFailed", + [ZEND_ENUM_StreamErrorCode_MetaFailed] = "MetaFailed", + [ZEND_ENUM_StreamErrorCode_ChmodFailed] = "ChmodFailed", + [ZEND_ENUM_StreamErrorCode_ChownFailed] = "ChownFailed", + [ZEND_ENUM_StreamErrorCode_CopyFailed] = "CopyFailed", + [ZEND_ENUM_StreamErrorCode_TouchFailed] = "TouchFailed", + [ZEND_ENUM_StreamErrorCode_InvalidMode] = "InvalidMode", + [ZEND_ENUM_StreamErrorCode_InvalidMeta] = "InvalidMeta", + [ZEND_ENUM_StreamErrorCode_ModeNotSupported] = "ModeNotSupported", + [ZEND_ENUM_StreamErrorCode_Readonly] = "Readonly", + [ZEND_ENUM_StreamErrorCode_RecursionDetected] = "RecursionDetected", + [ZEND_ENUM_StreamErrorCode_NotImplemented] = "NotImplemented", + [ZEND_ENUM_StreamErrorCode_NoOpener] = "NoOpener", + [ZEND_ENUM_StreamErrorCode_PersistentNotSupported] = "PersistentNotSupported", + [ZEND_ENUM_StreamErrorCode_WrapperNotFound] = "WrapperNotFound", + [ZEND_ENUM_StreamErrorCode_WrapperDisabled] = "WrapperDisabled", + [ZEND_ENUM_StreamErrorCode_ProtocolUnsupported] = "ProtocolUnsupported", + [ZEND_ENUM_StreamErrorCode_WrapperRegistrationFailed] = "WrapperRegistrationFailed", + [ZEND_ENUM_StreamErrorCode_WrapperUnregistrationFailed] = "WrapperUnregistrationFailed", + [ZEND_ENUM_StreamErrorCode_WrapperRestorationFailed] = "WrapperRestorationFailed", + [ZEND_ENUM_StreamErrorCode_FilterNotFound] = "FilterNotFound", + [ZEND_ENUM_StreamErrorCode_FilterFailed] = "FilterFailed", + [ZEND_ENUM_StreamErrorCode_CastFailed] = "CastFailed", + [ZEND_ENUM_StreamErrorCode_CastNotSupported] = "CastNotSupported", + [ZEND_ENUM_StreamErrorCode_MakeSeekableFailed] = "MakeSeekableFailed", + [ZEND_ENUM_StreamErrorCode_BufferedDataLost] = "BufferedDataLost", + [ZEND_ENUM_StreamErrorCode_NetworkSendFailed] = "NetworkSendFailed", + [ZEND_ENUM_StreamErrorCode_NetworkRecvFailed] = "NetworkRecvFailed", + [ZEND_ENUM_StreamErrorCode_SslNotSupported] = "SslNotSupported", + [ZEND_ENUM_StreamErrorCode_ResumptionFailed] = "ResumptionFailed", + [ZEND_ENUM_StreamErrorCode_SocketPathTooLong] = "SocketPathTooLong", + [ZEND_ENUM_StreamErrorCode_OobNotSupported] = "OobNotSupported", + [ZEND_ENUM_StreamErrorCode_ProtocolError] = "ProtocolError", + [ZEND_ENUM_StreamErrorCode_InvalidUrl] = "InvalidUrl", + [ZEND_ENUM_StreamErrorCode_InvalidResponse] = "InvalidResponse", + [ZEND_ENUM_StreamErrorCode_InvalidHeader] = "InvalidHeader", + [ZEND_ENUM_StreamErrorCode_InvalidParam] = "InvalidParam", + [ZEND_ENUM_StreamErrorCode_RedirectLimit] = "RedirectLimit", + [ZEND_ENUM_StreamErrorCode_AuthFailed] = "AuthFailed", + [ZEND_ENUM_StreamErrorCode_ArchivingFailed] = "ArchivingFailed", + [ZEND_ENUM_StreamErrorCode_EncodingFailed] = "EncodingFailed", + [ZEND_ENUM_StreamErrorCode_DecodingFailed] = "DecodingFailed", + [ZEND_ENUM_StreamErrorCode_InvalidFormat] = "InvalidFormat", + [ZEND_ENUM_StreamErrorCode_AllocationFailed] = "AllocationFailed", + [ZEND_ENUM_StreamErrorCode_TemporaryFileFailed] = "TemporaryFileFailed", + [ZEND_ENUM_StreamErrorCode_LockFailed] = "LockFailed", + [ZEND_ENUM_StreamErrorCode_LockNotSupported] = "LockNotSupported", + [ZEND_ENUM_StreamErrorCode_UserspaceNotImplemented] = "UserspaceNotImplemented", + [ZEND_ENUM_StreamErrorCode_UserspaceInvalidReturn] = "UserspaceInvalidReturn", + [ZEND_ENUM_StreamErrorCode_UserspaceCallFailed] = "UserspaceCallFailed", +}; +#endif + +typedef enum zend_enum_StreamErrorMode { + ZEND_ENUM_StreamErrorMode_Error = 1, + ZEND_ENUM_StreamErrorMode_Exception = 2, + ZEND_ENUM_StreamErrorMode_Silent = 3, +} zend_enum_StreamErrorMode; + +typedef enum zend_enum_StreamErrorStore { + ZEND_ENUM_StreamErrorStore_Auto = 1, + ZEND_ENUM_StreamErrorStore_None = 2, + ZEND_ENUM_StreamErrorStore_NonTerminating = 3, + ZEND_ENUM_StreamErrorStore_Terminating = 4, + ZEND_ENUM_StreamErrorStore_All = 5, +} zend_enum_StreamErrorStore; + +#endif /* ZEND_STREAM_ERRORS_DECL_3e9ee6f0fdd8ecf3ded82728487a9e774137036a_H */ diff --git a/main/streams/streams.c b/main/streams/streams.c index 31d1eda16790..715bbcfe0371 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -138,141 +138,6 @@ PHPAPI int php_stream_from_persistent_id(const char *persistent_id, php_stream * return PHP_STREAM_PERSISTENT_NOT_EXIST; } -/* }}} */ - -static zend_llist *php_get_wrapper_errors_list(php_stream_wrapper *wrapper) -{ - if (!FG(wrapper_errors)) { - return NULL; - } else { - return (zend_llist*) zend_hash_str_find_ptr(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper)); - } -} - -/* {{{ wrapper error reporting */ -static void php_stream_display_wrapper_errors(php_stream_wrapper *wrapper, const char *path, const char *caption) -{ - char *tmp; - char *msg; - char errstr[256]; - int free_msg = 0; - - if (EG(exception)) { - /* Don't emit additional warnings if an exception has already been thrown. */ - return; - } - - tmp = estrdup(path); - if (wrapper) { - zend_llist *err_list = php_get_wrapper_errors_list(wrapper); - if (err_list) { - size_t l = 0; - int brlen; - int i; - int count = (int)zend_llist_count(err_list); - const char *br; - const char **err_buf_p; - zend_llist_position pos; - - if (PG(html_errors)) { - brlen = 7; - br = "
\n"; - } else { - brlen = 1; - br = "\n"; - } - - for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0; - err_buf_p; - err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) { - l += strlen(*err_buf_p); - if (i < count - 1) { - l += brlen; - } - } - msg = emalloc(l + 1); - msg[0] = '\0'; - for (err_buf_p = zend_llist_get_first_ex(err_list, &pos), i = 0; - err_buf_p; - err_buf_p = zend_llist_get_next_ex(err_list, &pos), i++) { - strcat(msg, *err_buf_p); - if (i < count - 1) { - strcat(msg, br); - } - } - - free_msg = 1; - } else { - if (wrapper == &php_plain_files_wrapper) { - msg = php_socket_strerror_s(errno, errstr, sizeof(errstr)); - } else { - msg = "operation failed"; - } - } - } else { - msg = "no suitable wrapper could be found"; - } - - php_strip_url_passwd(tmp); - php_error_docref1(NULL, tmp, E_WARNING, "%s: %s", caption, msg); - efree(tmp); - if (free_msg) { - efree(msg); - } -} - -static void php_stream_tidy_wrapper_error_log(php_stream_wrapper *wrapper) -{ - if (wrapper && FG(wrapper_errors)) { - zend_hash_str_del(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper)); - } -} - -static void wrapper_error_dtor(void *error) -{ - efree(*(char**)error); -} - -static void wrapper_list_dtor(zval *item) { - zend_llist *list = (zend_llist*)Z_PTR_P(item); - zend_llist_destroy(list); - efree(list); -} - -PHPAPI void php_stream_wrapper_log_error(const php_stream_wrapper *wrapper, int options, const char *fmt, ...) -{ - va_list args; - char *buffer = NULL; - - va_start(args, fmt); - vspprintf(&buffer, 0, fmt, args); - va_end(args); - - if ((options & REPORT_ERRORS) || wrapper == NULL) { - php_error_docref(NULL, E_WARNING, "%s", buffer); - efree(buffer); - } else { - zend_llist *list = NULL; - if (!FG(wrapper_errors)) { - ALLOC_HASHTABLE(FG(wrapper_errors)); - zend_hash_init(FG(wrapper_errors), 8, NULL, wrapper_list_dtor, 0); - } else { - list = zend_hash_str_find_ptr(FG(wrapper_errors), (const char*)&wrapper, sizeof(wrapper)); - } - - if (!list) { - zend_llist new_list; - zend_llist_init(&new_list, sizeof(buffer), wrapper_error_dtor, 0); - list = zend_hash_str_update_mem(FG(wrapper_errors), (const char*)&wrapper, - sizeof(wrapper), &new_list, sizeof(new_list)); - } - - /* append to linked list */ - zend_llist_add_element(list, &buffer); - } -} - - /* }}} */ /* allocate a new stream for a particular ops */ @@ -511,6 +376,11 @@ fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remov ZVAL_UNDEF(&stream->wrapperdata); } + if (stream->error_list) { + zend_llist_destroy(stream->error_list); + pefree(stream->error_list, stream->is_persistent); + } + if (stream->readbuf) { pefree(stream->readbuf, stream->is_persistent); stream->readbuf = NULL; @@ -1318,7 +1188,7 @@ PHPAPI ssize_t _php_stream_write(php_stream *stream, const char *buf, size_t cou ZEND_ASSERT(buf != NULL); if (stream->ops->write == NULL) { - php_error_docref(NULL, E_NOTICE, "Stream is not writable"); + php_stream_notice(stream, NotWritable, "Stream is not writable"); return (ssize_t) -1; } @@ -1360,14 +1230,16 @@ PHPAPI zend_off_t _php_stream_tell(const php_stream *stream) return stream->position; } -static bool php_stream_are_filters_seekable(php_stream_filter *filter, bool is_start_seeking) +static bool php_stream_are_filters_seekable(php_stream_filter *filter, bool is_start_seeking, int chain_type) { while (filter) { - if (filter->seekable == PSFS_SEEKABLE_NEVER) { + php_stream_filter_seekable_t seekable = (chain_type == PHP_STREAM_FILTER_READ) ? + filter->read_seekable : filter->write_seekable; + if (seekable == PSFS_SEEKABLE_NEVER) { php_error_docref(NULL, E_WARNING, "Stream filter %s is never seekable", filter->fops->label); return false; } - if (!is_start_seeking && filter->seekable == PSFS_SEEKABLE_START) { + if (!is_start_seeking && seekable == PSFS_SEEKABLE_START) { php_error_docref(NULL, E_WARNING, "Stream filter %s is seekable only to start position", filter->fops->label); return false; } @@ -1377,11 +1249,13 @@ static bool php_stream_are_filters_seekable(php_stream_filter *filter, bool is_s } static zend_result php_stream_filters_seek(php_stream *stream, php_stream_filter *filter, - bool is_start_seeking, zend_off_t offset, int whence) + bool is_start_seeking, zend_off_t offset, int whence, int chain_type) { while (filter) { - if (((filter->seekable == PSFS_SEEKABLE_START && is_start_seeking) || - filter->seekable == PSFS_SEEKABLE_CHECK) && + php_stream_filter_seekable_t seekable = (chain_type == PHP_STREAM_FILTER_READ) ? + filter->read_seekable : filter->write_seekable; + if (((seekable == PSFS_SEEKABLE_START && is_start_seeking) || + seekable == PSFS_SEEKABLE_CHECK) && filter->fops->seek(stream, filter, offset, whence) == FAILURE) { php_error_docref(NULL, E_WARNING, "Stream filter seeking for %s failed", filter->fops->label); return FAILURE; @@ -1394,10 +1268,12 @@ static zend_result php_stream_filters_seek(php_stream *stream, php_stream_filter static zend_result php_stream_filters_seek_all(php_stream *stream, bool is_start_seeking, zend_off_t offset, int whence) { - if (php_stream_filters_seek(stream, stream->writefilters.head, is_start_seeking, offset, whence) == FAILURE) { + if (php_stream_filters_seek(stream, stream->writefilters.head, is_start_seeking, + offset, whence, PHP_STREAM_FILTER_WRITE) == FAILURE) { return FAILURE; } - if (php_stream_filters_seek(stream, stream->readfilters.head, is_start_seeking, offset, whence) == FAILURE) { + if (php_stream_filters_seek(stream, stream->readfilters.head, is_start_seeking, + offset, whence, PHP_STREAM_FILTER_READ) == FAILURE) { return FAILURE; } @@ -1422,11 +1298,13 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) if (stream->writefilters.head) { _php_stream_flush(stream, 0); - if (!php_stream_are_filters_seekable(stream->writefilters.head, is_start_seeking)) { + if (!php_stream_are_filters_seekable(stream->writefilters.head, is_start_seeking, + PHP_STREAM_FILTER_WRITE)) { return -1; } } - if (stream->readfilters.head && !php_stream_are_filters_seekable(stream->readfilters.head, is_start_seeking)) { + if (stream->readfilters.head && !php_stream_are_filters_seekable( + stream->readfilters.head, is_start_seeking, PHP_STREAM_FILTER_READ)) { return -1; } @@ -1505,7 +1383,8 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) return 0; } - php_error_docref(NULL, E_WARNING, "Stream does not support seeking"); + php_stream_warn(stream, SeekNotSupported, + "Stream does not support seeking"); return -1; } @@ -1929,11 +1808,13 @@ void php_shutdown_stream_hashes(void) FG(stream_filters) = NULL; } - if (FG(wrapper_errors)) { - zend_hash_destroy(FG(wrapper_errors)); - efree(FG(wrapper_errors)); - FG(wrapper_errors) = NULL; + if (FG(wrapper_logged_errors)) { + zend_hash_destroy(FG(wrapper_logged_errors)); + efree(FG(wrapper_logged_errors)); + FG(wrapper_logged_errors) = NULL; } + + php_stream_error_state_cleanup(); } zend_result php_init_stream_wrappers(int module_number) @@ -1973,7 +1854,7 @@ void php_shutdown_stream_wrappers(int module_number) static inline zend_result php_stream_wrapper_scheme_validate(const char *protocol, size_t protocol_len) { for (size_t i = 0; i < protocol_len; i++) { - if (!isalnum((int)protocol[i]) && + if (!isalnum((unsigned char)protocol[i]) && protocol[i] != '+' && protocol[i] != '-' && protocol[i] != '.') { @@ -2053,7 +1934,7 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const return (php_stream_wrapper*)((options & STREAM_LOCATE_WRAPPERS_ONLY) ? NULL : &php_plain_files_wrapper); } - for (p = path; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) { + for (p = path; isalnum((unsigned char)*p) || *p == '+' || *p == '-' || *p == '.'; p++) { n++; } @@ -2100,7 +1981,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const if (!localhost && path[n+3] != '\0' && path[n+3] != '/') { #endif if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "Remote host file access not supported, %s", path); + php_stream_wrapper_warn(plain_files_wrapper, NULL, options, + ProtocolUnsupported, + "Remote host file access not supported, %s", path); } return NULL; } @@ -2139,7 +2022,9 @@ PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const } if (options & REPORT_ERRORS) { - php_error_docref(NULL, E_WARNING, "file:// wrapper is disabled in the server configuration"); + php_stream_wrapper_warn(plain_files_wrapper, NULL, options, + Disabled, + "file:// wrapper is disabled in the server configuration"); } return NULL; } @@ -2237,10 +2122,12 @@ PHPAPI php_stream *_php_stream_opendir(const char *path, int options, stream->flags |= PHP_STREAM_FLAG_NO_BUFFER | PHP_STREAM_FLAG_IS_DIR; } } else if (wrapper) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, "not implemented"); + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + NoOpener, "not implemented"); } if (stream == NULL && (options & REPORT_ERRORS)) { - php_stream_display_wrapper_errors(wrapper, path, "Failed to open directory"); + php_stream_display_wrapper_errors(wrapper, context, PHP_STREAM_EC(OpenFailed), + path, "Failed to open directory"); } php_stream_tidy_wrapper_error_log(wrapper); @@ -2307,16 +2194,25 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options); if ((options & STREAM_USE_URL) && (!wrapper || !wrapper->is_url)) { - php_error_docref(NULL, E_WARNING, "This function may only be used against URLs"); + if (wrapper) { + php_stream_wrapper_warn(wrapper, context, options, + ProtocolUnsupported, + "This function may only be used against URLs"); + } else { + php_error_docref(NULL, E_WARNING, "This function may only be used against URLs"); + } if (resolved_path) { zend_string_release_ex(resolved_path, 0); } return NULL; } + /* wrapper name needs to be stored as wrapper can be removed in opener (user stream) */ + char *wrapper_name = pestrdup(PHP_STREAM_ERROR_WRAPPER_NAME(wrapper), persistent); if (wrapper) { if (!wrapper->wops->stream_opener) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + NoOpener, "wrapper does not support stream open"); } else { stream = wrapper->wops->stream_opener(wrapper, @@ -2327,7 +2223,8 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod /* if the caller asked for a persistent stream but the wrapper did not * return one, force an error here */ if (stream && persistent && !stream->is_persistent) { - php_stream_wrapper_log_error(wrapper, options & ~REPORT_ERRORS, + php_stream_wrapper_log_warn(wrapper, context, options & ~REPORT_ERRORS, + PersistentNotSupported, "wrapper does not support persistent streams"); php_stream_close(stream); stream = NULL; @@ -2351,6 +2248,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod stream->open_filename = __zend_orig_filename ? __zend_orig_filename : __zend_filename; stream->open_lineno = __zend_orig_lineno ? __zend_orig_lineno : __zend_lineno; #endif + if (stream->ctx == NULL && context != NULL && !persistent) { + php_stream_context_set(stream, context); + } } if (stream != NULL && (options & STREAM_MUST_SEEK)) { @@ -2363,6 +2263,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (resolved_path) { zend_string_release_ex(resolved_path, 0); } + pefree(wrapper_name, persistent); return stream; case PHP_STREAM_RELEASED: if (newstream->orig_path) { @@ -2372,6 +2273,7 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (resolved_path) { zend_string_release_ex(resolved_path, 0); } + pefree(wrapper_name, persistent); return newstream; default: php_stream_close(stream); @@ -2379,8 +2281,9 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod if (options & REPORT_ERRORS) { char *tmp = estrdup(path); php_strip_url_passwd(tmp); - php_error_docref1(NULL, tmp, E_WARNING, "could not make seekable - %s", - tmp); + php_stream_wrapper_warn_param(wrapper, context, options, + SeekNotSupported, tmp, + "could not make seekable - %s", tmp); efree(tmp); options &= ~REPORT_ERRORS; @@ -2398,13 +2301,15 @@ PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mod } if (stream == NULL && (options & REPORT_ERRORS)) { - php_stream_display_wrapper_errors(wrapper, path, "Failed to open stream"); + php_stream_display_wrapper_name_errors(wrapper_name, context, PHP_STREAM_EC(OpenFailed), + path, "Failed to open stream"); if (opened_path && *opened_path) { zend_string_release_ex(*opened_path, 0); *opened_path = NULL; } } - php_stream_tidy_wrapper_error_log(wrapper); + php_stream_tidy_wrapper_name_error_log(wrapper_name); + pefree(wrapper_name, persistent); if (resolved_path) { zend_string_release_ex(resolved_path, 0); } diff --git a/main/streams/transports.c b/main/streams/transports.c index 0a18d10f7433..e171b545b11b 100644 --- a/main/streams/transports.c +++ b/main/streams/transports.c @@ -37,13 +37,13 @@ PHPAPI int php_stream_xport_unregister(const char *protocol) return zend_hash_str_del(&xport_hash, protocol, strlen(protocol)); } -#define ERR_REPORT(out_err, fmt, arg) \ +#define ERR_REPORT(code, out_err, fmt, arg) \ if (out_err) { *out_err = strpprintf(0, fmt, arg); } \ - else { php_error_docref(NULL, E_WARNING, fmt, arg); } + else { php_stream_wrapper_warn(NULL, NULL, REPORT_ERRORS, code, fmt, arg); } -#define ERR_RETURN(out_err, local_err, fmt) \ +#define ERR_RETURN(code, out_err, local_err, fmt) \ if (out_err) { *out_err = local_err; } \ - else { php_error_docref(NULL, E_WARNING, fmt, local_err ? ZSTR_VAL(local_err) : "Unspecified error"); \ + else { php_stream_wrapper_warn(NULL, NULL, REPORT_ERRORS, code, fmt, local_err ? ZSTR_VAL(local_err) : "Unspecified error"); \ if (local_err) { zend_string_release_ex(local_err, 0); local_err = NULL; } \ } @@ -93,7 +93,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in } orig_path = name; - for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) { + for (p = name; isalnum((unsigned char)*p) || *p == '+' || *p == '-' || *p == '.'; p++) { n++; } @@ -114,7 +114,8 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in n = sizeof(wrapper_name) - 1; PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n); - ERR_REPORT(error_string, "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?", + ERR_REPORT(WrapperNotFound, error_string, + "Unable to find the socket transport \"%s\" - did you forget to enable it when you configured PHP?", wrapper_name); return NULL; @@ -123,7 +124,8 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in if (factory == NULL) { /* should never happen */ - php_error_docref(NULL, E_WARNING, "Could not find a factory !?"); + php_stream_wrapper_warn(NULL, context, REPORT_ERRORS, + WrapperNotFound, "Could not find a factory !?"); return NULL; } @@ -144,7 +146,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in flags & STREAM_XPORT_CONNECT_ASYNC ? 1 : 0, timeout, &error_text, error_code)) { - ERR_RETURN(error_string, error_text, "connect() failed: %s"); + ERR_RETURN(ConnectFailed, error_string, error_text, "connect() failed: %s"); failed = true; } @@ -154,7 +156,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in /* server */ if (flags & STREAM_XPORT_BIND) { if (0 != php_stream_xport_bind(stream, name, namelen, &error_text)) { - ERR_RETURN(error_string, error_text, "bind() failed: %s"); + ERR_RETURN(BindFailed, error_string, error_text, "bind() failed: %s"); failed = true; } else if (flags & STREAM_XPORT_LISTEN) { zval *zbacklog = NULL; @@ -165,7 +167,7 @@ PHPAPI php_stream *_php_stream_xport_create(const char *name, size_t namelen, in } if (0 != php_stream_xport_listen(stream, backlog, &error_text)) { - ERR_RETURN(error_string, error_text, "listen() failed: %s"); + ERR_RETURN(ListenFailed, error_string, error_text, "listen() failed: %s"); failed = true; } } @@ -368,7 +370,8 @@ PHPAPI int php_stream_xport_crypto_setup(php_stream *stream, php_stream_xport_cr return param.outputs.returncode; } - php_error_docref("streams.crypto", E_WARNING, "This stream does not support SSL/crypto"); + php_stream_warn_docref(stream, "streams.crypto", SslNotSupported, + "This stream does not support SSL/crypto"); return ret; } @@ -388,11 +391,32 @@ PHPAPI int php_stream_xport_crypto_enable(php_stream *stream, int activate) return param.outputs.returncode; } - php_error_docref("streams.crypto", E_WARNING, "This stream does not support SSL/crypto"); + php_stream_warn_docref(stream, "streams.crypto", SslNotSupported, + "This stream does not support SSL/crypto"); return ret; } +PHPAPI int php_stream_xport_crypto_get_status(php_stream *stream) +{ + php_stream_xport_crypto_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + param.op = STREAM_XPORT_CRYPTO_OP_GET_STATUS; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + return param.outputs.returncode; + } + + php_stream_warn_docref(stream, "streams.crypto", SslNotSupported, + "This stream does not support SSL/crypto"); + + return STREAM_CRYPTO_STATUS_NONE; +} + /* Similar to recv() system call; read data from the stream, optionally * peeking, optionally retrieving OOB data */ PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t buflen, @@ -410,7 +434,8 @@ PHPAPI int php_stream_xport_recvfrom(php_stream *stream, char *buf, size_t bufle } if (stream->readfilters.head) { - php_error_docref(NULL, E_WARNING, "Cannot peek or fetch OOB data from a filtered stream"); + php_stream_warn(stream, FilterFailed, + "Cannot peek or fetch OOB data from a filtered stream"); return -1; } @@ -480,7 +505,8 @@ PHPAPI int php_stream_xport_sendto(php_stream *stream, const char *buf, size_t b oob = (flags & STREAM_OOB) == STREAM_OOB; if ((oob || addr) && stream->writefilters.head) { - php_error_docref(NULL, E_WARNING, "Cannot write OOB data, or data to a targeted address on a filtered stream"); + php_stream_warn(stream, FilterFailed, + "Cannot write OOB data, or data to a targeted address on a filtered stream"); return -1; } diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 335ef3aa4f27..bf6ffa500963 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -291,7 +291,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * /* Try to catch bad usage without preventing flexibility */ if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { - php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented"); + php_stream_wrapper_log_warn(wrapper, context, options, + RecursionDetected, "infinite recursion prevented"); return NULL; } FG(user_stream_current_filename) = filename; @@ -332,8 +333,8 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" is not implemented", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options,NotImplemented, + "\"%s::" USERSTREAM_OPEN "\" is not implemented", ZSTR_VAL(us->wrapper->ce->name)); zval_ptr_dtor(&args[3]); goto end; } @@ -355,8 +356,9 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char * /* set wrapper data to be a reference to our object */ ZVAL_COPY(&stream->wrapperdata, &us->object); } else { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_OPEN "\" call failed", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, + UserspaceCallFailed, + "\"%s::" USERSTREAM_OPEN "\" call failed", ZSTR_VAL(us->wrapper->ce->name)); } zval_ptr_dtor(&zretval); @@ -392,7 +394,8 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char /* Try to catch bad usage without preventing flexibility */ if (FG(user_stream_current_filename) != NULL && strcmp(filename, FG(user_stream_current_filename)) == 0) { - php_stream_wrapper_log_error(wrapper, options, "infinite recursion prevented"); + php_stream_wrapper_log_warn(wrapper, context, options, + RecursionDetected, "infinite recursion prevented"); return NULL; } FG(user_stream_current_filename) = filename; @@ -417,8 +420,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" is not implemented", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, NotImplemented, + "\"%s::" USERSTREAM_DIR_OPEN "\" is not implemented", + ZSTR_VAL(us->wrapper->ce->name)); goto end; } /* Exception occurred in call */ @@ -433,8 +437,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char /* set wrapper data to be a reference to our object */ ZVAL_COPY(&stream->wrapperdata, &us->object); } else { - php_stream_wrapper_log_error(wrapper, options, "\"%s::" USERSTREAM_DIR_OPEN "\" call failed", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_wrapper_log_warn(wrapper, context, options, + UserspaceCallFailed, + "\"%s::" USERSTREAM_DIR_OPEN "\" call failed", ZSTR_VAL(us->wrapper->ce->name)); } zval_ptr_dtor(&zretval); @@ -477,10 +482,15 @@ PHP_FUNCTION(stream_wrapper_register) /* We failed. But why? */ if (zend_hash_exists(php_stream_get_url_stream_wrappers_hash(), protocol)) { - php_error_docref(NULL, E_WARNING, "Protocol %s:// is already defined.", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(&uwrap->wrapper, NULL, REPORT_ERRORS, + WrapperRegistrationFailed, + "Protocol %s:// is already defined.", ZSTR_VAL(protocol)); } else { /* Hash doesn't exist so it must have been an invalid protocol scheme */ - php_error_docref(NULL, E_WARNING, "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", ZSTR_VAL(uwrap->ce->name), ZSTR_VAL(protocol)); + php_stream_wrapper_warn(&uwrap->wrapper, NULL, REPORT_ERRORS, + WrapperRegistrationFailed, + "Invalid protocol scheme specified. Unable to register wrapper class %s to %s://", + ZSTR_VAL(uwrap->ce->name), ZSTR_VAL(protocol)); } zend_list_delete(rsrc); @@ -500,7 +510,9 @@ PHP_FUNCTION(stream_wrapper_unregister) php_stream_wrapper *wrapper = zend_hash_find_ptr(php_stream_get_url_stream_wrappers_hash(), protocol); if (php_unregister_url_stream_wrapper_volatile(protocol) == FAILURE) { /* We failed */ - php_error_docref(NULL, E_WARNING, "Unable to unregister protocol %s://", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(wrapper, NULL, REPORT_ERRORS, + WrapperUnregistrationFailed, + "Unable to unregister protocol %s://", ZSTR_VAL(protocol)); RETURN_FALSE; } @@ -528,13 +540,17 @@ PHP_FUNCTION(stream_wrapper_restore) global_wrapper_hash = php_stream_get_url_stream_wrappers_hash_global(); if ((wrapper = zend_hash_find_ptr(global_wrapper_hash, protocol)) == NULL) { - php_error_docref(NULL, E_WARNING, "%s:// never existed, nothing to restore", ZSTR_VAL(protocol)); + php_stream_wrapper_warn_name(user_stream_wops.label, NULL, REPORT_ERRORS, + WrapperNotFound, + "%s:// never existed, nothing to restore", ZSTR_VAL(protocol)); RETURN_FALSE; } wrapper_hash = php_stream_get_url_stream_wrappers_hash(); if (wrapper_hash == global_wrapper_hash || zend_hash_find_ptr(wrapper_hash, protocol) == wrapper) { - php_error_docref(NULL, E_NOTICE, "%s:// was never changed, nothing to restore", ZSTR_VAL(protocol)); + php_stream_wrapper_notice(wrapper, NULL, REPORT_ERRORS, + WrapperRestorationFailed, + "%s:// was never changed, nothing to restore", ZSTR_VAL(protocol)); RETURN_TRUE; } @@ -542,7 +558,9 @@ PHP_FUNCTION(stream_wrapper_restore) php_unregister_url_stream_wrapper_volatile(protocol); if (php_register_url_stream_wrapper_volatile(protocol, wrapper) == FAILURE) { - php_error_docref(NULL, E_WARNING, "Unable to restore original %s:// wrapper", ZSTR_VAL(protocol)); + php_stream_wrapper_warn(wrapper, NULL, REPORT_ERRORS, + WrapperRestorationFailed, + "Unable to restore original %s:// wrapper", ZSTR_VAL(protocol)); RETURN_FALSE; } @@ -570,8 +588,8 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ zval_ptr_dtor(&args[0]); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_WRITE " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } stream->flags &= ~PHP_STREAM_FLAG_NO_FCLOSE; @@ -591,7 +609,9 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_ /* don't allow strange buffer overruns due to bogus return */ if (didwrite > 0 && didwrite > count) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)", + php_stream_warn_nt(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_WRITE " wrote " ZEND_LONG_FMT " bytes more data than requested (" + ZEND_LONG_FMT " written, " ZEND_LONG_FMT " max)", ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didwrite - count), (zend_long)didwrite, (zend_long)count); didwrite = count; @@ -622,8 +642,8 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count } if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_READ " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); goto err; } @@ -639,8 +659,12 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count didread = Z_STRLEN(retval); if (didread > 0) { if (didread > count) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT " bytes more data than requested (" ZEND_LONG_FMT " read, " ZEND_LONG_FMT " max) - excess data will be lost", - ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didread - count), (zend_long)didread, (zend_long)count); + php_stream_warn_nt(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_READ " - read " ZEND_LONG_FMT + " bytes more data than requested (" ZEND_LONG_FMT " read, " + ZEND_LONG_FMT " max) - excess data will be lost", + ZSTR_VAL(us->wrapper->ce->name), (zend_long)(didread - count), + (zend_long)didread, (zend_long)count); didread = count; } memcpy(buf, Z_STRVAL(retval), didread); @@ -656,7 +680,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, NotImplemented, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); stream->eof = 1; @@ -772,7 +796,8 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when *newoffs = Z_LVAL(retval); ret = 0; } else if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_TELL " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_TELL " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); ret = -1; } else { ret = -1; @@ -836,8 +861,8 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_STAT " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -855,7 +880,7 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb) return ret; } -static int user_stream_set_check_liveliness(const php_userstream_data_t *us) +static int user_stream_set_check_liveliness(php_stream *stream, const php_userstream_data_t *us) { zval retval; @@ -864,7 +889,7 @@ static int user_stream_set_check_liveliness(const php_userstream_data_t *us) zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, NotImplemented, "%s::" USERSTREAM_EOF " is not implemented! Assuming EOF", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -875,15 +900,15 @@ static int user_stream_set_check_liveliness(const php_userstream_data_t *us) if (EXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) { return Z_TYPE(retval) == IS_TRUE ? PHP_STREAM_OPTION_RETURN_ERR : PHP_STREAM_OPTION_RETURN_OK; } else { - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_EOF " value must be of type bool, %s given", + php_stream_warn(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_EOF " value must be of type bool, %s given", ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval)); zval_ptr_dtor(&retval); return PHP_STREAM_OPTION_RETURN_ERR; } } -static int user_stream_set_locking(const php_userstream_data_t *us, int value) +static int user_stream_set_locking(php_stream *stream, const php_userstream_data_t *us, int value) { zval retval; zval zlock; @@ -918,9 +943,8 @@ static int user_stream_set_locking(const php_userstream_data_t *us, int value) /* lock support test (TODO: more check) */ return PHP_STREAM_OPTION_RETURN_OK; } - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_LOCK " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_LOCK " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -932,14 +956,15 @@ static int user_stream_set_locking(const php_userstream_data_t *us, int value) } // TODO: ext/standard/tests/file/userstreams_004.phpt returns null implicitly for function // Should this warn or not? And should this be considered an error? - //php_error_docref(NULL, E_WARNING, - // "%s::" USERSTREAM_LOCK " value must be of type bool, %s given", + //php_stream_warn(stream, UserspaceInvalidReturn, + // "%s::" USERSTREAM_LOCK " value must be of type bool, %s given", // ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval)); zval_ptr_dtor(&retval); return PHP_STREAM_OPTION_RETURN_NOTIMPL; } -static int user_stream_set_truncation(const php_userstream_data_t *us, int value, void *ptrparam) { +static int user_stream_set_truncation(php_stream *stream, const php_userstream_data_t *us, + int value, void *ptrparam) { zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_TRUNCATE, false); if (value == PHP_STREAM_TRUNCATE_SUPPORTED) { @@ -967,9 +992,8 @@ static int user_stream_set_truncation(const php_userstream_data_t *us, int value zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_TRUNCATE " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_TRUNCATE " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -978,15 +1002,16 @@ static int user_stream_set_truncation(const php_userstream_data_t *us, int value if (EXPECTED(Z_TYPE(retval) == IS_FALSE || Z_TYPE(retval) == IS_TRUE)) { return Z_TYPE(retval) == IS_TRUE ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; } else { - php_error_docref(NULL, E_WARNING, - "%s::" USERSTREAM_TRUNCATE " value must be of type bool, %s given", + php_stream_warn(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_TRUNCATE " value must be of type bool, %s given", ZSTR_VAL(us->wrapper->ce->name), zend_zval_value_name(&retval)); zval_ptr_dtor(&retval); return PHP_STREAM_OPTION_RETURN_ERR; } } -static int user_stream_set_option(const php_userstream_data_t *us, int option, int value, void *ptrparam) +static int user_stream_set_option(php_stream *stream, const php_userstream_data_t *us, int option, + int value, void *ptrparam) { zval args[3]; ZVAL_LONG(&args[0], option); @@ -1011,7 +1036,7 @@ static int user_stream_set_option(const php_userstream_data_t *us, int option, i zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, + php_stream_warn(stream, NotImplemented, "%s::" USERSTREAM_SET_OPTION " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); return PHP_STREAM_OPTION_RETURN_ERR; @@ -1036,19 +1061,19 @@ static int php_userstreamop_set_option(php_stream *stream, int option, int value switch (option) { case PHP_STREAM_OPTION_CHECK_LIVENESS: - return user_stream_set_check_liveliness(us); + return user_stream_set_check_liveliness(stream, us); case PHP_STREAM_OPTION_LOCKING: - return user_stream_set_locking(us, value); + return user_stream_set_locking(stream, us, value); case PHP_STREAM_OPTION_TRUNCATE_API: - return user_stream_set_truncation(us, value, ptrparam); + return user_stream_set_truncation(stream, us, value, ptrparam); case PHP_STREAM_OPTION_READ_BUFFER: case PHP_STREAM_OPTION_WRITE_BUFFER: case PHP_STREAM_OPTION_READ_TIMEOUT: case PHP_STREAM_OPTION_BLOCKING: - return user_stream_set_option(us, option, value, ptrparam); + return user_stream_set_option(stream, us, option, value, ptrparam); default: return PHP_STREAM_OPTION_RETURN_NOTIMPL; @@ -1081,7 +1106,8 @@ static int user_wrapper_unlink(php_stream_wrapper *wrapper, const char *url, int zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_UNLINK " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_UNLINK " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1119,7 +1145,8 @@ static int user_wrapper_rename(php_stream_wrapper *wrapper, const char *url_from zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RENAME " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_RENAME " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1157,7 +1184,8 @@ static int user_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url, int zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_MKDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_MKDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1194,7 +1222,8 @@ static int user_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_RMDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_RMDIR " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1233,7 +1262,9 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i ZVAL_STRING(&args[2], value); break; default: - php_error_docref(NULL, E_WARNING, "Unknown option %d for " USERSTREAM_METADATA, option); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, + InvalidMeta, + "Unknown option %d for " USERSTREAM_METADATA, option); return ret; } @@ -1256,7 +1287,8 @@ static int user_wrapper_metadata(php_stream_wrapper *wrapper, const char *url, i zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_METADATA " is not implemented!", ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_METADATA " is not implemented!", ZSTR_VAL(uwrap->ce->name)); } else if (Z_TYPE(zretval) == IS_FALSE || Z_TYPE(zretval) == IS_TRUE) { ret = Z_TYPE(zretval) == IS_TRUE; } @@ -1294,8 +1326,8 @@ static int user_wrapper_stat_url(php_stream_wrapper *wrapper, const char *url, i zval_ptr_dtor(&object); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STATURL " is not implemented!", - ZSTR_VAL(uwrap->ce->name)); + php_stream_wrapper_warn(wrapper, context, REPORT_ERRORS, NotImplemented, + "%s::" USERSTREAM_STATURL " is not implemented!", ZSTR_VAL(uwrap->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(zretval))) { @@ -1330,8 +1362,9 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co zend_string_release_ex(func_name, false); if (UNEXPECTED(call_result == FAILURE)) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!", - ZSTR_VAL(us->wrapper->ce->name)); + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_DIR_READ " is not implemented!", + ZSTR_VAL(us->wrapper->ce->name)); return -1; } if (UNEXPECTED(Z_ISUNDEF(retval))) { @@ -1416,7 +1449,8 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) if (UNEXPECTED(call_result == FAILURE)) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " is not implemented!", + php_stream_warn(stream, NotImplemented, + "%s::" USERSTREAM_CAST " is not implemented!", ZSTR_VAL(us->wrapper->ce->name)); } goto out; @@ -1430,14 +1464,16 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr) php_stream_from_zval_no_verify(intstream, &retval); if (!intstream) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must return a stream resource", + php_stream_warn(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_CAST " must return a stream resource", ZSTR_VAL(us->wrapper->ce->name)); } break; } if (intstream == stream) { if (report_errors) { - php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_CAST " must not return itself", + php_stream_warn(stream, UserspaceInvalidReturn, + "%s::" USERSTREAM_CAST " must not return itself", ZSTR_VAL(us->wrapper->ce->name)); } intstream = NULL; diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c index 77977a557fc6..d18b0e901957 100644 --- a/main/streams/xp_socket.c +++ b/main/streams/xp_socket.c @@ -114,9 +114,8 @@ static ssize_t php_sockop_write(php_stream *stream, const char *buf, size_t coun if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) { estr = php_socket_strerror(err, NULL, 0); - php_error_docref(NULL, E_NOTICE, - "Send of %zu bytes failed with errno=%d %s", - count, err, estr); + php_stream_warn(stream, NetworkSendFailed, + "Send of %zu bytes failed with errno=%d %s", count, err, estr); efree(estr); } } @@ -452,8 +451,7 @@ static int php_sockop_set_option(php_stream *stream, int option, int value, void xparam->inputs.addrlen); if (xparam->outputs.returncode == -1) { char *err = php_socket_strerror(php_socket_errno(), NULL, 0); - php_error_docref(NULL, E_WARNING, - "%s\n", err); + php_stream_warn(stream, NetworkSendFailed, "%s", err); efree(err); } return PHP_STREAM_OPTION_RETURN_OK; @@ -593,7 +591,8 @@ static const php_stream_ops php_stream_unixdg_socket_ops = { /* network socket operations */ #ifdef AF_UNIX -static inline int parse_unix_address(php_stream_xport_param *xparam, struct sockaddr_un *unix_addr) +static inline int parse_unix_address(php_stream *stream, php_stream_xport_param *xparam, + struct sockaddr_un *unix_addr) { memset(unix_addr, 0, sizeof(*unix_addr)); unix_addr->sun_family = AF_UNIX; @@ -612,9 +611,9 @@ static inline int parse_unix_address(php_stream_xport_param *xparam, struct sock * BUT, to get into this branch of code, the name is too long, * so we don't care. */ xparam->inputs.namelen = max_length; - php_error_docref(NULL, E_NOTICE, - "socket path exceeded the maximum allowed length of %lu bytes " - "and was truncated", max_length); + php_stream_notice(stream, InvalidPath, + "socket path exceeded the maximum allowed length of %lu bytes and was truncated", + max_length); } memcpy(unix_addr->sun_path, xparam->inputs.name, xparam->inputs.namelen); @@ -695,10 +694,10 @@ static inline int php_tcp_sockop_bind(php_stream *stream, php_netstream_data_t * return -1; } - parse_unix_address(xparam, &unix_addr); + parse_unix_address(stream, xparam, &unix_addr); int result = bind(sock->socket, (const struct sockaddr *)&unix_addr, - (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen); + (socklen_t) offsetof(struct sockaddr_un, sun_path) + xparam->inputs.namelen); if (result == -1 && xparam->want_errortext) { char errstr[256]; xparam->outputs.error_text = strpprintf(0, "%s", php_socket_strerror_s(errno, errstr, sizeof(errstr))); @@ -831,10 +830,10 @@ static inline int php_tcp_sockop_connect(php_stream *stream, php_netstream_data_ return -1; } - parse_unix_address(xparam, &unix_addr); + parse_unix_address(stream, xparam, &unix_addr); ret = php_network_connect_socket(sock->socket, - (const struct sockaddr *)&unix_addr, (socklen_t) XtOffsetOf(struct sockaddr_un, sun_path) + xparam->inputs.namelen, + (const struct sockaddr *)&unix_addr, (socklen_t) offsetof(struct sockaddr_un, sun_path) + xparam->inputs.namelen, xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC, xparam->inputs.timeout, xparam->want_errortext ? &xparam->outputs.error_text : NULL, &err); diff --git a/php.ini-development b/php.ini-development index ee75459ea56c..afabe74ba0e4 100644 --- a/php.ini-development +++ b/php.ini-development @@ -611,6 +611,12 @@ ignore_repeated_source = Off ; Production Value: On ;fatal_error_backtraces = On +; This directive controls whether PHP will print the actual arguments of a +; function upon an error. If this is off (or there was an error fetching the +; arguments), the function providing the error may optionally provide some +; additional information after the problem function's name. +;error_include_args = Off + ;;;;;;;;;;;;;;;;; ; Data Handling ; ;;;;;;;;;;;;;;;;; @@ -1305,10 +1311,9 @@ session.save_handler = files ; Strict session mode does not accept an uninitialized session ID, and ; regenerates the session ID if the browser sends an uninitialized session ID. ; Strict mode protects applications from session fixation via a session adoption -; vulnerability. It is disabled by default for maximum compatibility, but -; enabling it is encouraged. +; vulnerability. ; https://wiki.php.net/rfc/strict_sessions -session.use_strict_mode = 0 +session.use_strict_mode = 1 ; Whether to use cookies. ; https://php.net/session.use-cookies @@ -1350,13 +1355,13 @@ session.cookie_domain = ; Whether or not to add the httpOnly flag to the cookie, which makes it ; inaccessible to browser scripting languages such as JavaScript. ; https://php.net/session.cookie-httponly -session.cookie_httponly = +session.cookie_httponly = 1 ; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF) ; Current valid values are "Strict", "Lax" or "None". When using "None", ; make sure to include the quotes, as `none` is interpreted like `false` in ini files. ; https://tools.ietf.org/html/draft-west-first-party-cookies-07 -session.cookie_samesite = +session.cookie_samesite = "Lax" ; Handler used to serialize data. php is the standard serializer of PHP. ; https://php.net/session.serialize-handler diff --git a/php.ini-production b/php.ini-production index b10e2ba9944a..04a7b699dadd 100644 --- a/php.ini-production +++ b/php.ini-production @@ -613,6 +613,12 @@ ignore_repeated_source = Off ; Production Value: On ;fatal_error_backtraces = On +; This directive controls whether PHP will print the actual arguments of a +; function upon an error. If this is off (or there was an error fetching the +; arguments), the function providing the error may optionally provide some +; additional information after the problem function's name. +;error_include_args = Off + ;;;;;;;;;;;;;;;;; ; Data Handling ; ;;;;;;;;;;;;;;;;; @@ -1307,10 +1313,9 @@ session.save_handler = files ; Strict session mode does not accept an uninitialized session ID, and ; regenerates the session ID if the browser sends an uninitialized session ID. ; Strict mode protects applications from session fixation via a session adoption -; vulnerability. It is disabled by default for maximum compatibility, but -; enabling it is encouraged. +; vulnerability. ; https://wiki.php.net/rfc/strict_sessions -session.use_strict_mode = 0 +session.use_strict_mode = 1 ; Whether to use cookies. ; https://php.net/session.use-cookies @@ -1352,13 +1357,13 @@ session.cookie_domain = ; Whether or not to add the httpOnly flag to the cookie, which makes it ; inaccessible to browser scripting languages such as JavaScript. ; https://php.net/session.cookie-httponly -session.cookie_httponly = +session.cookie_httponly = 1 ; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF) ; Current valid values are "Strict", "Lax" or "None". When using "None", ; make sure to include the quotes, as `none` is interpreted like `false` in ini files. ; https://tools.ietf.org/html/draft-west-first-party-cookies-07 -session.cookie_samesite = +session.cookie_samesite = "Lax" ; Handler used to serialize data. php is the standard serializer of PHP. ; https://php.net/session.serialize-handler diff --git a/run-tests.php b/run-tests.php index c08d07cdd7c1..f5c7be8b4f45 100755 --- a/run-tests.php +++ b/run-tests.php @@ -273,6 +273,7 @@ function main(): void 'fatal_error_backtraces=Off', 'display_errors=1', 'display_startup_errors=1', + 'error_include_args=0', 'log_errors=0', 'html_errors=0', 'track_errors=0', diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 0dfbe2d3db2a..797979b67305 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -651,7 +651,7 @@ static int sapi_cli_server_register_entry_cb(zval *entry, int num_args, va_list if (key[i] == '-') { key[i] = '_'; } else { - key[i] = toupper(key[i]); + key[i] = toupper((unsigned char)key[i]); } } spprintf(&real_key, 0, "%s_%s", "HTTP", key); diff --git a/sapi/cli/tests/gh21901.phpt b/sapi/cli/tests/gh21901.phpt new file mode 100644 index 000000000000..39c76913d1f8 --- /dev/null +++ b/sapi/cli/tests/gh21901.phpt @@ -0,0 +1,13 @@ +--TEST-- +Stale getopt() optional value in CLI +--FILE-- + +--EXPECTF-- +Configuration File (php.ini) Path: %s +Loaded Configuration File: %s +Scan for additional .ini files in: %s +Additional .ini files parsed: %s diff --git a/sapi/cli/tests/php_cli_server.inc b/sapi/cli/tests/php_cli_server.inc index 3022022f894e..3ad6ced5cb44 100644 --- a/sapi/cli/tests/php_cli_server.inc +++ b/sapi/cli/tests/php_cli_server.inc @@ -24,7 +24,8 @@ function php_cli_server_start( file_put_contents($doc_root . '/' . ($router ?: 'index.php'), ''); } - $cmd = [$php_executable, '-t', $doc_root, '-n', ...$cmd_args, '-S', 'localhost:0']; + // XXX: This should ideally use the same INI overrides as run-tests + $cmd = [$php_executable, '-d', 'error_include_args=0', '-t', $doc_root, '-n', ...$cmd_args, '-S', 'localhost:0']; if (!is_null($router)) { $cmd[] = $router; } diff --git a/sapi/cli/tests/sapi_windows_set_ctrl_trampoline.phpt b/sapi/cli/tests/sapi_windows_set_ctrl_trampoline.phpt new file mode 100644 index 000000000000..4699387b2c39 --- /dev/null +++ b/sapi/cli/tests/sapi_windows_set_ctrl_trampoline.phpt @@ -0,0 +1,31 @@ +--TEST-- +sapi_windows_set_ctrl_handler() trampoline test +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c index 3979d875a18e..a77f7776bdbe 100644 --- a/sapi/fpm/fpm/fpm_conf.c +++ b/sapi/fpm/fpm/fpm_conf.c @@ -973,7 +973,7 @@ static int fpm_conf_process_all_pools(void) } for (i = 0; i < strlen(status); i++) { - if (!isalnum(status[i]) && status[i] != '/' && status[i] != '-' && status[i] != '_' && status[i] != '.' && status[i] != '~') { + if (!isalnum((unsigned char)status[i]) && status[i] != '/' && status[i] != '-' && status[i] != '_' && status[i] != '.' && status[i] != '~') { zlog(ZLOG_ERROR, "[pool %s] the status path '%s' must contain only the following characters '[alphanum]/_-.~'", wp->config->name, status); return -1; } @@ -996,7 +996,7 @@ static int fpm_conf_process_all_pools(void) } for (i = 0; i < strlen(ping); i++) { - if (!isalnum(ping[i]) && ping[i] != '/' && ping[i] != '-' && ping[i] != '_' && ping[i] != '.' && ping[i] != '~') { + if (!isalnum((unsigned char)ping[i]) && ping[i] != '/' && ping[i] != '-' && ping[i] != '_' && ping[i] != '.' && ping[i] != '~') { zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' must contain only the following characters '[alphanum]/_-.~'", wp->config->name, ping); return -1; } diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c index 9cb8731363fa..ff6ef68f3949 100644 --- a/sapi/fpm/fpm/fpm_status.c +++ b/sapi/fpm/fpm/fpm_status.c @@ -522,8 +522,8 @@ int fpm_status_handle_request(void) /* {{{ */ if (full_syntax) { unsigned int i; int first; - zend_string *tmp_query_string; - char *query_string; + zend_string *tmp_query_string, *tmp_request_uri_string; + char *query_string, *request_uri_string; struct timeval duration, now; float cpu; @@ -548,13 +548,36 @@ int fpm_status_handle_request(void) /* {{{ */ } } + request_uri_string = NULL; + tmp_request_uri_string = NULL; + if (proc->request_uri[0] != '\0') { + if (encode_html) { + tmp_request_uri_string = php_escape_html_entities_ex( + (const unsigned char *) proc->request_uri, + strlen(proc->request_uri), 1, ENT_DISALLOWED | ENT_HTML_DOC_XML1 | ENT_COMPAT, + NULL, /* double_encode */ 1, /* quiet */ 0); + request_uri_string = ZSTR_VAL(tmp_request_uri_string); + } else if (encode_json) { + tmp_request_uri_string = php_json_encode_string(proc->request_uri, + strlen(proc->request_uri), PHP_JSON_INVALID_UTF8_IGNORE); + request_uri_string = ZSTR_VAL(tmp_request_uri_string); + /* remove quotes around the string */ + if (ZSTR_LEN(tmp_request_uri_string) >= 2) { + request_uri_string[ZSTR_LEN(tmp_request_uri_string) - 1] = '\0'; + ++request_uri_string; + } + } else { + request_uri_string = proc->request_uri; + } + } + query_string = NULL; tmp_query_string = NULL; if (proc->query_string[0] != '\0') { if (encode_html) { tmp_query_string = php_escape_html_entities_ex( (const unsigned char *) proc->query_string, - strlen(proc->query_string), 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT, + strlen(proc->query_string), 1, ENT_DISALLOWED | ENT_HTML_DOC_XML1 | ENT_COMPAT, NULL, /* double_encode */ 1, /* quiet */ 0); } else if (encode_json) { tmp_query_string = php_json_encode_string(proc->query_string, @@ -593,7 +616,7 @@ int fpm_status_handle_request(void) /* {{{ */ proc->requests, (unsigned long) (duration.tv_sec * 1000000UL + duration.tv_usec), proc->request_method[0] != '\0' ? proc->request_method : "-", - proc->request_uri[0] != '\0' ? proc->request_uri : "-", + request_uri_string ? request_uri_string : "-", query_string ? "?" : "", query_string ? query_string : "", proc->content_length, @@ -604,6 +627,9 @@ int fpm_status_handle_request(void) /* {{{ */ PUTS(buffer); efree(buffer); + if (tmp_request_uri_string) { + zend_string_free(tmp_request_uri_string); + } if (tmp_query_string) { zend_string_free(tmp_query_string); } diff --git a/sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt b/sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt new file mode 100644 index 000000000000..475bc130a42b --- /dev/null +++ b/sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt @@ -0,0 +1,48 @@ +--TEST-- +FPM: GHSA-7qg2-v9fj-4mwv - status xss +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$responses = $tester + ->multiRequest([ + ['uri' => '/', 'query' => ''], + ['uri' => '/status', 'query' => 'full&html', 'delay' => 100000], + ]); +var_dump(strpos($responses[1]->getBody(), '