* Fix GH-24533: Accept z.lib from zlib 1.3.2 CMake builds on Windows
* Prefer static zs.lib and add NEWS entry
* Accept zlib 1.3.2 lib names in the other zlib lookups
- Replace bailout on OOM with UnwindExit
- Properly reset various globals that may be accessed during startup
Fix GHSA-w8hw-w9fx-j79w (CVE-2026-109618)
Fix GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
Co-authored-by: Ilija Tovilo <ilija.tovilo@me.com>
Co-authored-by: Jakub Zelenka <bukka@php.net>
Co-authored-by: Nora Dossche <7771979+ndossche@users.noreply.github.com>
Co-authored-by: Nicolas Grekas <nicolas.grekas@gmail.com>
Corrupt input makes zlib.inflate raise an E_NOTICE. An error handler can
remove the filter and free its state, so resetting the input pointers after
the notice writes to freed memory.
Release the input bucket and reset the filter state before raising the
notice, then return without accessing the filter again. Add regression
coverage for error handlers that remove both write and read filters.
Closing the stream itself from an error handler is outside this fix and
requires protection around the filter chain walk.
Closes#24176
inflate_init() applies the preset dictionary eagerly for raw streams via
inflateSetDictionary(), gated on encoding == PHP_ZLIB_ENCODING_RAW. But
encoding is first adjusted by the window size (encoding += 15 - window),
so a raw stream with a non-default window no longer equals
PHP_ZLIB_ENCODING_RAW and the dictionary is silently dropped; raw streams
carry no header and never emit Z_NEED_DICT, so inflate_add()'s deferred
path never applies it either. Gate on the pre-adjustment encoding. The
deflate side already applies the dictionary unconditionally, so the
roundtrip was broken for this case.
Closes GH-22381
Headers required by the generated code (zend_attributes.h, zend_constants.h, zend_enum.h) are collected during generation and emitted as includes at the top of the arginfo file, with the same preprocessor conditions and PHP version guards as the code that needs them.
Removes the manual zend_attributes.h and zend_enum.h includes from sources that only needed them for the arginfo file they include.
Fix GH-22142
An object passed as options exposes uninitialised typed properties as
IS_UNDEF slots, which zval_try_get_long() rejected through an unreachable
branch. Those slots are now skipped, matching get_object_vars().
Close GH-23409
Runs the test suite in parallel by default; automatically selects up to 10 workers. Explicit -j1 remains the opt-out to run tests serial. Smaller batches improve balancing.
* ext/bz2: applied fixers to improve test robustness
* ext/calendar: applied fixers to improve test robustness
* ext/exif: applied fixers to improve test robustness
* ext/fileinfo: applied fixers to improve test robustness
* ext/filter: applied fixers to improve test robustness
* ext/ftp: applied fixers to improve test robustness
* ext/hash: applied fixers to improve test robustness
* ext/iconv: applied fixers to improve test robustness
* ext/ldap: applied fixers to improve test robustness
* ext/pcre: applied fixers to improve test robustness
* ext/posix: applied fixers to improve test robustness
* ext/readline: applied fixers to improve test robustness
* ext/shmop: applied fixers to improve test robustness
* ext/snmp: applied fixers to improve test robustness
* ext/sodium: applied fixers to improve test robustness
* ext/sqlite3: applied fixers to improve test robustness
* ext/sysvmsg: applied fixers to improve test robustness
* ext/sysvsem: applied fixers to improve test robustness
* ext/sysvshm: applied fixers to improve test robustness
* ext/tidy: applied fixers to improve test robustness
* ext/tokenizer: applied fixers to improve test robustness
* ext/zend_test: applied fixers to improve test robustness
* ext/zlib: applied fixers to improve test robustness
This was introduced in b0ef5fcaa1 because
it added `efree(dict)` to both inflate and deflate error handling.
But this is only necessary for the deflate case, not the inflate case
(as also shown in the original leak report).
* ext/*: Make `X_from_obj(zend_object *obj)` functions macros
With the introduction of the `ZEND_CONTAINER_OF()` macro we can now preserve
whether or not the `zend_object *` is `const` when extracting the
class-specific struct. This change will allow to use `const zend_object *` in
more locations.
Changes performed with Coccinelle followed by some manual cleanup for comment
and whitespace formatting:
@a@
attribute name zend_always_inline;
type T;
identifier obj;
identifier f;
typedef zend_object;
@@
T* f(zend_object *obj) { return ZEND_CONTAINER_OF(obj, T, std); }
+ #define f(obj) ZEND_CONTAINER_OF(obj, T, std)
@@
identifier a.f;
@@
- f(...) { ... }
@b@
attribute name zend_always_inline;
type T;
identifier obj;
identifier f;
typedef zend_object;
@@
T* f(zend_object *obj) { return ZEND_CONTAINER_OF(obj, T, zo); }
+ #define f(obj) ZEND_CONTAINER_OF(obj, T, zo)
@@
identifier b.f;
@@
- f(...) { ... }
* sockets: Fix build
* odbc: Fix build
Depending on the specific PHP configuration, the regex matching for the
version number in get_zlib_version() can fail.
On master, this results in deprecation warnings and subsequent test
borks:
```
Deprecated: version_compare(): Passing null to parameter #1 ($version1) of type string is deprecated in /work/php-src/ext/zlib/tests/gzgetc_basic.skip.php on line 3 [/work/php-src/ext/zlib/tests/gzgetc_basic.phpt]
Deprecated: version_compare(): Passing null to parameter #1 ($version1) of type string is deprecated in /work/php-src/ext/zlib/tests/gzgetc_basic_1.skip.php on line 4
```
Since ZLIB_VERSION is a constant containing the version as a string,
remove all this nonsense and just use the constant directly.
* zend_portability: Add `ZEND_CONTAINER_OF()`
* tree-wide: Use `ZEND_CONTAINER_OF()`
Changes made with Coccinelle:
@@
type T_container;
identifier member;
expression e;
@@
- (T_container *)(((char *)(e)) - offsetof(T_container, member))
+ ZEND_CONTAINER_OF(e, T_container, member)
@@
type T_container;
identifier member;
expression e;
typedef uintptr_t;
@@
- (T_container *)(((uintptr_t)(e)) - offsetof(T_container, member))
+ ZEND_CONTAINER_OF(e, T_container, member)
@@
type T_container;
identifier member;
expression e;
@@
- (const T_container *)(((char *)(e)) - offsetof(T_container, member))
+ ZEND_CONTAINER_OF(e, T_container, member)
@@
type T_container;
identifier member;
expression e;
typedef uintptr_t;
@@
- (const T_container *)(((uintptr_t)(e)) - offsetof(T_container, member))
+ ZEND_CONTAINER_OF(e, T_container, member)
* bcmath: Fix `get_bcmath_number_from_obj()` const-correctness
A follow-up change to the new `ZEND_CONTAINER_OF()` macro will preserve the
`const`-ness of the input pointer. By wrapping this macro inside a macro rather
an an inline function we can preserve the `const`-ness across all layers.
* zend_portability: Keep `const`-ness in `ZEND_CONTAINER_OF()`
* zend_portability: Add C++ version to `ZEND_CONTAINER_OF()`
The single seekable field caused write-chain seeks to reset filter
state after the bug #49874 fix, breaking dechunk on php://temp (used
by Symfony HttpClient).
Split into read_seekable and write_seekable. Write defaults to ALWAYS
for stateless and non-buffering filters. Buffer-holding filters
(zlib, bz2, convert.*) accept a write_seek_mode parameter:
"preserve" (default), "reset", or "strict". Invalid values throw
ValueError.
php_user_filter::seek gains a third int $chain argument; the ops
seek signature is unchanged.
`zval_get_long()` does a forceful cast and will even cast values like arrays or objects into integers, something that points to a bug. Use the more modern `zval_try_get_long()` API instead.
Create a helper function to handle the options consistently.
Co-authored-by: Gina Peter Banyard <girgias@php.net>
There are several issues with this macro:
- It is incorrectly named: It's not an empty default case. It's an unreachable
default case.
- It is hiding control flow in its definition, which can be confusing for
humans and tools (such as Coccinelle) alike, because it looks like it would
be a statement that belongs to the "current" case rather than opening a new
one.
Since this macro is exactly as long as explicitly spelling out its definition
(excluding the useless `break;`), it is not even making the code any more
succinct.
Audit zend_ini_string(), INI_STR(), and related functions and macros, and all other INI_* macros.
The primary motivation is that these APIs should return a `const char*` because the `char*` is owned by a zend_string, and thus should never be released.
Moreover, the INI_STR() API doesn't follow our usual naming scheme as it returns a char* rather than a zend_string*.
In this PR new zend_ini_{bool|long|double|str|string}_literal() macros are introduced replacing the INI_{BOOL|INT|FLT|STR} macros which follow our more modern naming convention.
Moreover, the INI_BOOL() macro didn't produce correct values if the INI string is a word like "true" or "on".
The INI_ORIG_* APIs are removed because a Sourcegraph search shows 0 results, other than the one case used in ext/tidy that we fix using the typical API.
Add some additional checks for the value of an INI string to ensure it doesn't contain a nul byte when describing a path.
Currently filter seeking does not work correctly for most streams. The
idea is to extend API to allow seeking for some streams. There are
couple of cases:
- filter is always seekable - e.g. string.rot13, string.toupper,
string.tolower
- filter is seekable only when sought to start or when it re-run the
data from the start (it means when there is some data buffered) -
this is for pretty much all other filters that keep some state.
- user filters are seekable by default for BC reason but if new
seek method is implemented, then it is called and based on the
bool result the seeking either fails or succeed.