- 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>
When invoking the PHP in a subprocess, TEST_PHP_EXECUTABLE_ESCAPED is
preferable to PHP_BINARY, because the former can be set to ensure that
the just-built (often development) php gets used rather than a system
install. In addition, TEST_PHP_EXTRA_ARGS should be passed to this
interpreter.
Closes GH-22414
php_cli_server_content_sender_send() moved on to the next queued chunk
after a short send() of the current one. When that send() succeeded, its
bytes went out ahead of the current chunk's remainder, and the full-send
branch pointed buffer.first past both, unlinking the partially sent chunk.
The client received later chunks spliced into the body, the connection was
closed short of Content-Length, and the unsent remainder leaked. Stop at
the first partial write and resume from that chunk on the next POLLOUT.
Closes GH-23969
* PHP-8.4:
sapi/cli: Fix crash in the CLI server when a client is reset before being accepted
ext/curl: Preserve callback lifetime without rejecting callback changes (#23863)
accept() can return a socket without filling in the peer address when the
client has already reset the connection. The address buffer was left
uninitialized and php_network_populate_name_from_sockaddr() does not set the
output string for an unknown family, so zend_string_dup() was handed a NULL.
Zero the buffer and fall back to "-" when the address is unknown.
Close GH-23840
php_cli_server_begin_send_static() opens the file, but for HEAD requests
never stores the descriptor in client->file_fd, so nothing closes it.
Close it right away; Content-Length comes from the stat and the body is
never sent, so the descriptor is not needed.
Closes GH-23765.
sapi_cli_server_send_headers() sent the header buffer through
php_cli_server_client_send_through() but dropped the return value, so it
always reported SAPI_HEADER_SENT_SUCCESSFULLY, even when the send failed
and php_handle_aborted_connection() did not bail out (ignore_user_abort=1).
It now compares the send result against the buffer length and returns
SAPI_HEADER_SEND_FAILED when it comes up short.
php_cli_server_client_send_through() itself returned the number of bytes
left on failure, which equals str_len when nothing was sent: the same
value it returns on success. It now returns the number of bytes actually
sent on both paths.
Since sapi_send_headers() resets headers_sent on SAPI_HEADER_SEND_FAILED,
a headers_written flag on the client guards against rebuilding and
resending the whole header block on every subsequent output write.
Close GH-23428
Technically this applies to other SAPIs, but CLI is currently the only one with
a flag with an optional value. The PHP getopt() implementation already clears
php_optarg by-hand.
Fixes GH-21901
Closes GH-21902
Co-authored-by: Ilija Tovilo <ilija.tovilo@me.com>
When installing executables that were built using libtool, we are
supposed to use
$ libtool --mode-install <install-command>
rather than the bare <install-command>. This is discussed ever so
briefly in the "Installing executables" section of the GNU libtool
documentation:
https://www.gnu.org/software/libtool/manual/libtool.html
So far this has not caused a problem with GNU libtool on the platforms
that PHP supports, but there is an alternate libtool implementation
called slibtool that stores wrappers at the locations where PHP is
expecting the true executables to live. As a result, the wrappers (and
not the executables) are installed when slibtool is used to build PHP.
This is fixed by replacing,
$(INSTALL)
with
$(LIBTOOL) --mode=install $(INSTALL)
in the install-foo rules for the executables that are built with
libtool.
Closes GH-13674
This ensures that no useless "Failed to poll event" error messages are
logged during normal server operation, as the SOCK_EAGAIN error simply
indicates another worker is already serving the request.
Closes GH-19964
* main: Ignore `register_argc_argv` when `SG(request_info).argc` is available
* sapi: Remove hardcoded `register_argc_argv` for CLI SAPIs
This INI is ignored since the previous commit, which makes the hardcoded
setting obsolete.
* main: Deprecate deriving $_SERVER['argc'] and $_SERVER['argv'] from the query string
RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_register_argc_argv_ini_directive
* main: Adjust deprecation message for `register_argc_argv`
* NEWS/UPGRADING
The shadow key is refreshed when resetting the memory manager between two
requests. But in forking SAPIs the first request of a child process inherits the
shadow key of the parent. As a result, a leak of the shadow key during the first
request of one process gives away the shadow key used during the first request
of other processes. This makes the key refresh mechanism less useful.
Here I ensure that we refresh the shadow key after a fork. We can not reset the
manager as there may be active allocations. Instead, we have to recompute shadow
pointers with the new key.
Closes GH-16765
This removes the --enable-opcache/--disable-opcache configure switch. OPcache
is now always builtin. The default value of opcache.enable and
opcache.enable_cli is unchanged.
RFC: https://wiki.php.net/rfc/make_opcache_required
Closes GH-18961.
Co-authored-by: Tim Düsterhus <tim@tideways-gmbh.com>
In #18527, I accidentally swapped the values. This is before my modification:
```
zend_printf("Configuration File (php.ini) Path: %s\n", PHP_CONFIG_FILE_PATH);
zend_printf("Loaded Configuration File: %s\n", php_ini_opened_path ? php_ini_opened_path : "(none)");
zend_printf("Scan for additional .ini files in: %s\n", php_ini_scanned_path ? php_ini_scanned_path : "(none)");
```
- "Loaded Configuration File" should be `php_ini_opened_path`
- "Scan for additional .ini files in" shoudl be `php_ini_scanned_path`
When a config var has whitespace (especially trailing whitespace) it is hard to see. This commit wraps the values (if they exist) in double quotes, so the difference is visually observable:
Before:
```
$ export PHP_INI_SCAN_DIR="/opt/homebrew/etc/php/8.4/conf.d "
$ ./sapi/cli/php --ini
Configuration File (php.ini) Path: /usr/local/lib
Loaded Configuration File: /opt/homebrew/etc/php/8.4/conf.d
Scan for additional .ini files in: (none)
Additional .ini files parsed: (none)
```
> Note
> The above output has trailing whitespace that is not visible, you can see it if you copy it into an editor:
After:
```
$ ./sapi/cli/php --ini
Configuration File (php.ini) Path: "/usr/local/lib"
Loaded Configuration File: "/opt/homebrew/etc/php/8.4/conf.d "
Scan for additional .ini files in: (none)
Additional .ini files parsed: (none)
```
Above the whitespace is now visible `/opt/homebrew/etc/php/8.4/conf.d `.
Close#18390