* Fix FD getting code on big endian (PHP 8.3)
stream casting as FD returns a php_socket_t, which is an int, but
zend_long is 64-bit (on those platforms). This works on LE by
accidental (unless it forgets to clear the high word), but is fatal
on big endian.
* change cast to match sig
A bunch of different issues:
1) The referenced value is copied without incrementing the refcount.
The reason the refcount isn't incremented is because otherwise
the array modifications would violate the RC1 constraints.
Solve this by copying the reference itself instead and always
read the referenced value.
2) No type checks on the array data, so malicious scripts could
cause type confusion bugs.
3) Potential overflow when the arrays resize and we access ctag.
Closes GH-17205.
This was a bug in both libxml and PHP.
We follow up with the same change as done in GNOME/libxml@b3871dd138.
Changing away from `xmlOutputBufferCreateFilenameDefault` is not
possible yet because this is a stable branch and would break BC.
Closes GH-17254.
The FFI call return values follow widening rules.
We must widen to `ffi_arg` in the case we're handling a return value for types shorter than the machine width.
From http://www.chiark.greenend.org.uk/doc/libffi-dev/html/The-Closure-API.html:
> In most cases, ret points to an object of exactly the size of the type specified when cif was constructed.
> However, integral types narrower than the system register size are widened.
> In these cases your program may assume that ret points to an ffi_arg object.
If we don't do this, we get wrong values when reading the return values.
Closes GH-17255.
Co-authored-by: Dmitry Stogov <dmitry@zend.com>
The issue that BMP RLE occasionally swallowed some pixels[1] had been
fixed long ago in libgd, but apparently it has been overlooked to port
it to our bundled libgd.
We also introduce the test helper `test_image_equals_image()` which
compares in-memory images for equality.
[1] <https://github.com/libgd/libgd/issues/276>
Closes GH-17250.
This bug happens because of a nested `SHM_UNPROTECT()` sequence.
In particular:
```
unprotect memory at ext/opcache/ZendAccelerator.c:2127
protect memory at ext/opcache/ZendAccelerator.c:2160
unprotect memory at ext/opcache/ZendAccelerator.c:2164
unprotect memory at ext/opcache/jit/zend_jit_trace.c:7464
^^^ Nested
protect memory at ext/opcache/jit/zend_jit_trace.c:7591
^^^ Problem is here: it should not protect again due to the nested unprotect
protect memory at ext/opcache/ZendAccelerator.c:2191
^^^ This one should actually protect, not the previous one
```
The reason this nesting happen is because:
1. We try to include the script, this eventually calls `cache_script_in_shared_memory`
2. `zend_optimize_script` will eventually run SCCP as part of the DFA pass.
3. SCCP will try to replace constants, but can also run destructors when a partial array is destructed here:
4e9cde758e/Zend/Optimizer/sccp.c (L2387-L2389)
In this case, this destruction invokes the GC which invokes the tracing JIT,
leading to the nested unprotects.
This patch disables the GC to prevent invoking user code, as user code
is not supposed to run during the optimizer pipeline.
Closes GH-17249.
Co-authored-by: Dmitry Stogov <dmitry@zend.com>
NULL checks for the glob stream are inconsistently applied. To solve
this generally, factor it out to a helper function so it's less likely
to be forgotten in the future.
Closes GH-17231.
When observer is enabled, we normally add an extra temporary to all
functions, to store the previously observed frame. However, this is done in
zend_observer_post_startup() so it doesn't happen to dl'ed() functions.
One possible fix would be to move that from zend_observer_post_startup()
to zend_register_functions(), but this would be too early: Observer may
not be enabled when zend_register_functions() is called, and may still be
enabled later.
However, when zend_register_functions() is called at run-time (during dl()),
we know definitively whether observer is enabled.
Here I update zend_register_functions() to add a temporary to dl'ed()
functions when observer is enabled.
Fixes: GH-17211
Closes: GH-17220
The gettext() family of functions under musl does not support codeset
suffixes like ".UTF-8", because the only codeset it understands is
UTF-8. (Yes, it is annoying that it doesn't support the suffix for the
codeset that it does understand; no, I am not in charge.) Thanks to
this, we have six failing tests on musl,
* FAIL Gettext basic test with en_US locale that should be on nearly
every system
[ext/gettext/tests/gettext_basic-enus.phpt]
* FAIL Test if bindtextdomain() returns string id if no directory path
is set( if directory path is 'null')
[ext/gettext/tests/gettext_bindtextdomain-cwd.phpt]
* FAIL Test dcgettext() functionality
[ext/gettext/tests/gettext_dcgettext.phpt]
* FAIL Test dgettext() functionality
[ext/gettext/tests/gettext_dgettext.phpt]
* FAIL Test if dngettext() returns the correct translations
(optionally plural).
[ext/gettext/tests/gettext_dngettext-plural.phpt]
* FAIL Test ngettext() functionality
[ext/gettext/tests/gettext_ngettext.phpt]
These are all fixed by symlinking the en_US.UTF-8 message data to en_US,
where musl is able to find it.
This does not make the situation any better for developers (who don't
know what libc their users will be running), but that problem is
inhereted from C and is not the fault of the gettext extension.
This partially addresses GH #13696
Musl has two quirks that are leading to failed internationalization
tests. First is that the return value of bindtextdomain(..., NULL)
will always be false, rather than an "implementation-defined default
directory," because musl does not have an implementation-defined
default directory. One test needs a special case for this.
Second is that the musl implementation of bind_textdomain_codeset()
always returns NULL. The POSIX-correctness of this is debatable, but
it is roughly equivalent to correct, because musl only support UTF-8,
so the NULL value indicating that the codeset is unchanged from the
locale's codeset (UTF-8) is accurate.
PHP's bind_textdomain_codeset() function however treats NULL as
failure, unconditionally:
* https://github.com/php/doc-en/issues/4311
* https://github.com/php/php-src/issues/17163
This unfortunately causes false to be returned consistently on musl --
even when nothing unexpected has happened -- and naturally this is
affecting several tests. For now we change two tests to accept "false"
in addition to "UTF-8" so that they may pass on musl. If PHP's
bind_textdomain_codeset() is updated to differentiate between NULL and
NULL-with-errno-set, these tests can also be updated once again to
reject the NULL-with-errno result.
This partially addresses GH #13696
According to POSIX, bindtextdomain() returns "the implementation-
defined default directory pathname used by the gettext family of
functions" when its second parameter is NULL (i.e. when you are
querying the directory corresponding to some text domain and that
directory has not yet been set). Its PHP counterpart is feeding
that result direclty to RETURN_STRING, but this can go wrong in
two ways:
1. If an error occurs, even POSIX-compliant implementations
may return NULL.
2. At least one non-compliant implementation (musl) lacks
a default directory and returns NULL whenever the domain
has not yet been bound.
In either of those cases, PHP segfaults on the NULL string. In this
commit we check for the NULL, and RETURN_FALSE when it happens rather
than crashing.
This partially addresses GH #13696
`glob(3)` doesn't know the virtual CWD of PHP, so we need to pass an
absolute path for ZTS builds. In lack of a reusable routine, we copy
the code from `glob()` and adapt as needed.
Closes GH-17074.
Commit edae2431 attempted to fix a leak and double free, but didn't
properly understand what was going on, causing a reference count mistake
and subsequent segfault in this case.
The first mistake of that commit is that the reference count should've
been increased because we're reusing a phar object. The error handling
path should've gotten changed instead to undo this refcount increase
instead of not refcounting at all (root cause of this bug).
The second mistake is that the alias isn't supposed to be transferred or
whatever, that just doesn't make sense. The reason the test
bug69958.phpt originally leaked is because in the non-reuse case we
borrowed the alias and otherwise we own the alias. If we own the alias
the alias information shouldn't get deleted anyway as that would desync
the alias map.
Fixing these will reveal a third issue in which the alias memory is not
always properly in sync with the persistence-ness of the phar, fix this
as well.
Closes GH-17150.
This test puts a fake cmd.exe in the CWD and removes it only after the
test has finished. We need to avoid that other tests are running while
that fake cmd.exe is there, because they may use it instead of the
proper cmd.exe.
We also unlink the fake cmd.exe as soon as possible, regardless of the
test result.
Fixes GH-17098.
Closes GH-17090.
op1 of ZEND_MATCH_ERROR, which refers to the match expression, is not freed by
MATCH_ERROR itself. Instead, it is freed by ZEND_HANDLE_EXCEPTION. For normal
control flow, a FREE is placed at the end of the match expression.
Since FREE may appear after MATCH_ERROR in the opcode sequence, we need to
correctly handle op1 of MATCH_ERROR as alive.
Fixes GH-17106
Closes GH-17108
The directives for FFI should be first in the file, which is fine,
however sometimes there can be comments or whitespace before or between
these defines. One practical example is for license information or when
a user adds newlines "by accident". In these cases, it's quite confusing
that the directives do not work properly.
To solve this, make the zend_ffi_parse_directives() aware of comments.
Closes GH-17082.
The `jit_prof_threshold` is a float, supposed to be in range [0, 1],
and usually very small (the default is 0.005). Reporting it as int
is meaningless.
Closes GH-17077.
As is, whenever `proc_open()` needs to invoke the shell, cmd.exe is
looked up in the usual executable search path. That implies that any
cmd.exe which is placed in the current working directory (which is not
necessarily what is reported by `getcwd()` for ZTS builds), will be
used. This is a known attack vector, and Microsoft recommends to
always use the fully qualified path to cmd.exe.
To prevent any cmd.exe in the current working directory to be used, but
to still allow users to use a drop in replacement for cmd.exe, we
search only the `PATH` for cmd.exe (and pass the fully qualified path
to `CreateProcessW`), instead of relying on automatic executable search
by passing the base name only.
To be able to easily test this, we provide a minimalist C file which
will be build as test_helper, and used by the new test case.
[1] <https://msrc.microsoft.com/blog/2014/04/ms14-019-fixing-a-binary-hijacking-via-cmd-or-bat-file/>
Closes GH-17043.
Besides that just checking for icuuc.lib does not necessarily imply
that the other libraries are available, doing it this way will not copy
the PDBs to the build folder, so these are not available in the debug
packages. Furthermore, `CHECK_LIB` already adds the library to the
flags, so there is no need to do this manually.
Closes GH-17010.