This testing code was never meant to be used this way, but fixing this
will at least stop fuzzers from complaining about this, so might still
be worthwhile.
Closes GH-16919.
zlog_buf_prefix() can return a larger length than what actually was
written due to its use of snprintf(). The code in
zlog_stream_prefix_ex() does not take this into account, other callers
do. What ends up happening then is that stream->length is set to the
length as if snprintf() was able to write all bytes, causing
stream->length to become larger than stream->buf.size, causing a
segfault.
In case the buffer was too small we try with a larger buffer up to a
limit of zlog_limit. This makes sure that the stream length will remain
bounded by the buffer size.
This also adds assertions to make the programmer intent clear and catch
this more easily in debug builds.
Closes GH-16680.
* Don't fiddle with NDEBUG in C code
It is way to late to do this in php.h, since assert.h has already been
included. Even pushing that down to zend_portability.h may not have
the desired effect. Instead we define or undefine NDEBUG as CFLAG, so
that it works in all circumstances.
As a last resort we fail at build time, if `NDEBUG` is defined when
`ZEND_DEBUG` is enabled.
We also remove the useless workaround in zend_test to include assert.h
again, since that usually won't have any effect anyway.
Co-authored-by: Arnaud Le Blanc <arnaud.lb@gmail.com>
Unless `zend_test.observer.enabled` is on, we must not add observer
handlers, so we let the INI modify handler fail early.
We also need to ensure that the functions to observe have already been
called, so that their begin and end handlers are properly initialized.
Otherwise we will not observe the function execution, but a segfault.
Co-authored-by: Bob Weinand <bobwei9@hotmail.com>
Closes GH-16438.
These are equivalent to `zend_hash_clean()` and `zend_hash_destroy()`
respectively, but take care of correctly unregistering the weak references to
the keys.
This addition rounds off the weakmap functionality added in
471102edcd by taking one possible footgun away
from the user.
zend_pass_function also has no name, so we might also be referring to an
internal function here. In this case, ZEND_NEW uses the zend_pass_function when
there is no constructor.
Fixes GH-16294
Closes GH-16301
In the test, I have an internal `__call` function for `_ZendTestMagicCallForward` that calls the global function with name `$name` via `call_user_function`.
Note that observer writes the pointer to the previously observed frame in the last temporary of the new call frame (`*prev_observed_frame`).
The following happens:
First, we call `$test->callee`, this will be handled via a trampoline with T=2 for the two arguments. The call frame is allocated at this point. This call frame is not observed because it has `ZEND_ACC_CALL_VIA_TRAMPOLINE` set. Next we use `ZEND_CALL_TRAMPOLINE` to call the trampoline, this reuses the stack frame allocated earlier with T=2, but this time it is observed. The pointer to the previous frame is written outside of the call frame because `T` is too small (should be 3). We are now in the internal function `_ZendTestMagicCallForward::__call` where we call the global function `callee`. This will push a new call frame which will overlap `*prev_observed_frame`. This value gets overwritten by `zend_init_func_execute_data` when `EX(opline)` is set because `*prev_observed_frame` overlaps with `EX(opline)`. From now on, `*prev_observed_frame` is corrupted. When `zend_observer_fcall_end` is called this will result in reading wrong value `*prev_observed_frame` into `current_observed_frame`. This causes issues in `zend_observer_fcall_end_all` leading to the segfault we observe.
Despite function with `ZEND_ACC_CALL_VIA_TRAMPOLINE` not being observed, the reuse of call frames makes problems when `T` is not large enough.
To fix this, we make sure to add 1 to `T` if `ZEND_OBSERVER_ENABLED` is true.
Closes GH-16252.
When functions' or class methods' availability is based on some preprocessor
condition, the generated arginfo header files wrap the declarations in the
preprocessor `#if` conditional blocks, one per declaration, even if they are in
the same conditional block based on comments in the stub file. Instead of
having multiple conditional blocks one after the other with the same condition,
combine them into a single conditional block.
When a class (or enum) has no methods, rather than using an array that only
contains `ZEND_FE_END`, use `NULL` for the functions. The implementation of
class registration for internal classes, `do_register_internal_class()` in
zend_API.c, already skips classes where the functions are `NULL`. By removing
these unneeded arrays, we can reduce the size of the header files, while also
removing an unneeded call to zend_register_functions() for each internal class
with no extra methods.