When declaring the same static property with a doc block in a class and in a trait,
the doc block of the property in the class is leaked. While at it, possibly fix doc
comment for internal classes.
Close GH-12238
These tests intermittently crash asan. It might be due to some function invoking
dl(), which is known to crash lsan. It might also be something else, the version
of asan shipped with ubuntu 22.04 is flaky.
Despite being OpenBSD's predecessor, the approach is in fact
a lot closer to Linux, at least in principle. We purposely
avoid reading /proc/N/maps to be more future-proof.
Close GH-11637
Reorder when we assign the property value to NULL which is identical to
a3a3964497
Just for the declared property case instead of dynamic.
Closes GH-12114
Addref to relevant fields before allocating any memory. Also only set/remove the
ZEND_ACC_HEAP_RT_CACHE flag after allocating memory.
Fixes GH-12073
Closes GH-12074
With the fix in https://github.com/php/php-src/pull/12114, the behaviour
would change for non-dynamic properties. Align the behaviour for dynamic
properties to be the same.
Closes GH-12117.
Because the error handler is invoked after the property is updated,
the error handler has the opportunity to remove it before the property
is returned.
Switching the order around fixes this issue. The comments mention that
the current ordering prevents overwriting the EG(std_property_info)
field in the error handler. EG(std_property_info) no longer exists as it
was removed in 7471c217. Back then a global was used to store the
returned property info, but as this is no longer the case there is no
longer a need to protect against overwriting a global.
Closes GH-12062.
When executing a foreach ($ht as &$ref), foreach calls zend_hash_iterator_pos_ex() on every iteration. If the HashTable contained in the $ht variable is not the tracked HashTable, it will reset the position to the internal array pointer of the array currently in $ht.
This behaviour is generally fine, but undesirable for copy-on-write copies of the iterated HashTable. This may trivially occur when the iterated over HashTable is assigned to some variable, then the iterated over variable modified, leading to array separation, changing the HashTable pointer in the variable. Thus foreach happily restarting iteration.
This behaviour (despite existing since PHP 7.0) is considered a bug, if not only for the behaviour being unexpected to the user, also copy-on-write should not have trivially observable side-effects by mere assignment.
The bugfix consists of duplicating HashTableIterators whenever zend_array_dup() is called (the primitive used on array separation).
When a further access to the HashPosition through the HashTableIterators API happens and the HashTable does not match the tracked one, all the duplicates (which are tracked by single linked list) are searched for the wanted HashTable. If found, the HashTableIterator is replaced by the found copy and all other copies are removed.
This ensures that we always end up tracking the correct HashTable.
Fixes GH-11244.
Signed-off-by: Bob Weinand <bobwei9@hotmail.com>
This PR introduces a new way of recursion protection in JSON, var_dump
and friends. It fixes issue in master for __debugInfo and also improves
perf for jsonSerializable in some cases. More info can be found in
GH-10020.
Closes GH-11812
* Zend: Make zend_strnlen available for use outside zend_compile
* exif: remove local php_strnlen, use zend_strnlen instead
* main: remove local strnlen, use zend_strnlen instead
* phar: remove local strnlen, use zend_strnlen
Evaluating constants at comptime can result in arrays that contain objects. This
is problematic for printing the default value of constant ASTs containing
objects, because we don't actually know what the constructor arguments were.
Avoid this by not propagating array constants.
Fixes GH-11937
Closes GH-11947
- GH-11958: DNF types in trait properties do not get bound properly
- GH-11883: Memory leak in zend_type_release() for non-arena allocated DNF types
- Internal trait bound to userland class would not be arena allocated
- Property DNF types were not properly deep copied during lazy loading
Co-authored-by: Ilija Tovilo <ilija.tovilo@me.com>
Co-authored-by: ju1ius <jules.bernable@gmail.com>
We may OOM during object initialization. In this case, free_obj needs to guard
against NULL values. There may be more cases where this is an issue, these were
the ones I was able to discover via script.
Fixes GH-11734
* opcache: use zend_ast_size helper in zend_persist_ast
* opcache: use zend_ast_size helper in zend_persist_ast_calc
* Zend: fix zend_ast_size definition
It is better not to use sizeof(struct_with_flexible_array)
and instead rely on offsetof(type, member) like most
other similar wrappers do.
Cirrus will no longer offer unlimited free builds starting next month. We don't
have an alternative for FreeBSD and ARM, so move what we can for now.
Closes GH-11898
opnum_start denotes the start of the ZEND_FREE block of skipped consuming
opcodes. Storing the number before zend_compile_expr(..., label_ast) makes it
seem like it denotes the start of the label block. However, label_ast must only
be a zval string AST, and as such never results in an actual opcode.
We require valid code for compilation to succeed, but these paths should always
be guarded by OPx_TYPE checks and never execute. Add an assertion to verify.
This does still deviate from USE_ZEND_ALLOC=0 in that we're not rounding up the
size of the allocation to fixed sizes. Doing so would suppress some
out-of-bounds errors checked by ASAN. Rounding up the size in
_zend_mm_block_size would not be good either as it would break code like
memset(ptr, 0 _zend_mm_block_size(ptr)).
The typo in HAVE_PTHREAD_ATTR_GET_STACK (might be due to pthread_attr_get_np being different from Linux's pthread_getattr_np) led to this code path never get called on FreeBSD.
We don't want to invoke calls twice, even if they are considered "variables",
i.e. might be writable if returning a reference. Function calls behave the same
in all BP contexts so they don't need to be invoked twice. The singular
exception to this is nullsafe coalesce in isset/empty, because it needs to
return false/true respectively when short-circuited. However, since nullsafe
calls are not allwed in write context we may ignore this problem.
Closes GH-11592
* Add behavioural tests for incdec operators
* Add support to ++/-- for objects castable to _IS_NUMBER
* Add str_increment() function
* Add str_decrement() function
RFC: https://wiki.php.net/rfc/saner-inc-dec-operators
Co-authored-by: Ilija Tovilo <ilija.tovilo@me.com>
Co-authored-by: Arnaud Le Blanc <arnaud.lb@gmail.com>
We transform the arrow function by nesting the expression into a return
statement. If we compile the arrow function twice this would be done twice,
leading to a compile assertion.
Fix oss-fuzz #60411
Closes GH-11632
Normally, PHP evaluates all expressions in offsets (property or array), as well
as the right hand side of assignments before actually fetching the offsets. This
is well explained in this blog post.
https://www.npopov.com/2017/04/14/PHP-7-Virtual-machine.html#writes-and-memory-safety
For ??= we have a bit of a problem in that the rhs must only be evaluated if the
lhs is null or undefined. Thus, we have to first compile the lhs with BP_VAR_IS,
conditionally run the rhs and then re-fetch the lhs with BP_VAR_W to to make
sure the offsets are valid if they have been invalidated.
However, we don't want to just re-evaluate the entire lhs because it may contain
side-effects, as in $array[$x++] ??= 42;. In this case, we don't want to
re-evaluate $x++ because it would result in writing to a different offset than
was previously tested. The same goes for function calls, like
$array[foo()] ??= 42;, where the second call to foo() might result in a
different value. PHP behaves correctly in these cases. This is implemented by
memoizing sub-expressions in the lhs of ??= and reusing them when compiling the
lhs for the second time. This is done for any expression that isn't a variable,
i.e. anything that can (potentially) be written to.
Unfortunately, this also means that function calls are considered writable due
to their return-by-reference semantics, and will thus not be memoized. The
expression foo()['bar'] ??= 42; will invoke foo() twice. Even worse,
foo(bar()) ??= 42; will call both foo() and bar() twice, but
foo(bar() + 1) ??= 42; will only call foo() twice. This is likely not by design,
and was just overlooked in the implementation. The RFC does not specify how
function calls in the lhs of the coalesce assignment behaves. This should
probably be improved in the future.
Now, the problem this commit actually fixes is that ??= may memoize expressions
inside assert() function calls that may not actually execute. This is not only
an issue when using the VAR in the second expression (which would usually also
be skipped) but also when freeing the VAR. For this reason, it is not safe to
memoize assert() sub-expressions.
There are two possible solutions:
1. Don't memoize any sub-expressions of assert(), meaning they will execute
twice.
2. Throw a compile error.
Option 2 is not quite simple, because we can't disallow all memoization inside
assert(), as that would break assertions like assert($array[foo()] ??= 'bar');.
Code like this is highly unlikely (and dubious) but possible. In this case, we
would need to make sure that a memoized value could not be used across the
assert boundary it was created in. The complexity for this is not worthwhile. So
we opt for option 1 and disable memoization immediately inside assert().
Fixes GH-11580
Closes GH-11581
Having this lineno on the same last compiled element can lead to an incorrectly
covered line number.
if (true) {
if (false) {
echo 'Never executed';
}
} else {
}
The echo will be reported as covered because the JMP from the if (true) branch
to the end of the else branch has the same lineno as the echo.
This is lacking a test because zend_dump.c does not have access to
ctx->debug_level and I don't think it's worth adjusting all the cases.
Closes GH-11598
zend_can_early_bind() might have already detected that the methods are
incompatible. In that case the class is still early bound, but must compile
error when inheritance is performed. Thus it is only safe to skip compatibility
checks when zend_can_early_bind() has succeeded.
When the code was moved to solve the uaf for memory overflow, this
caused the refcount to be higher than one in some self-concatenation
scenarios. This in turn causes quadratic time performance problems when
these concatenations happen in a loop.
Closes GH-11508.
The magic method trampoline closure may be variadic. However, the
arg_info for the variadic argument was not set, resulting in a crash
both in reflection and in the VM.
Fix it by creating an arg_info containing a single element in case of
the variadic case. The variadic argument is the last one (and in this
case only one) in the arg_info array.
We make sure the argument info is equivalent to the argument info of
`$closure` of the following code snippet:
```
function foo(...$arguments) {}
$closure = foo(...);
```
Closes GH-11417.
Depending on the order in which observers were installed, some observers might have been executed twice after removal of another observer. Also, adding an observer could produce a bogus pointer.
Fixes GH-11388.
Following https://wiki.php.net/rfc/horizontalreuse which introduced traits,
this should be allowed.
The implementation was refactored in 3f8c729. That commit is the first time
the "final" check appears AFAICT, but no reason was given for why. That
commit seems to have landed in 5.4.11 and the NEWS for that version doesn't
seem to mention something relevant to the behaviour change.
This patch removes the restriction of the final modifier.
Closes GH-11394.
This merges all usages of emitting an offset TypeError into a new ZEND_API function
zend_illegal_container_offset(const zend_string* container, const zval *offset, int type);
Where the container should represent the type on which the access is attempted (e.g. string, array)
The offset zval that is used, where the error message will display its type
The type of access, which should be a BP_VAR_* constant, to get special message for isset/empty/unset
* Add string output escaping into zend dump (phpdbg + opcache debug)
* Use ZSTR_VAL macro instead direct string access
* Move "escaped_string" into local switch/case scope
* Add zend_string_release
* Add Z_STR_P macro instead direct string access
* Merge zend_string declaration and its assigment in one stmt
Array literals will constant evaluate their elements. These can include
assignments, even though these are not valid constant expressions. The lhs of
assignments can be a list() element (or []) which is parsed as an array with a
special flag.
Supporting new constant expressions requires remembering to add them to
zend_eval_const_expr, even if it only evalutes its children. This is routinely
forgotten, at least by me. Use zend_ast_apply to solve this generically.
The block optimizer pass allows the use of sources of the preceding
block if the block is a follower and not a target. This causes issues
when trying to remove FREE instructions: if the source is not in the
block of the FREE, then the FREE and source are still removed. Therefore
the other successor blocks, which must consume or FREE the temporary,
will still contain the FREE opline. This opline will now refer to a
temporary that doesn't exist anymore, which most of the time results in
a crash. For these kind of non-local scenarios, we'll let the SSA
based optimizations handle those cases.
Closes GH-11251.
free_op2_string may be set to false when the operands are not strings, and
`result == op1 == op2`, by re-using the same string for both operands. In that
case, the string should still be copied to result because result is not actually
a string. Also change the op1 branch to stay consistent.
Introduced by GH-10049
The following sequence of actions was happening which caused a null
pointer dereference:
1. debug_backtrace() returns an array
2. The concatenation to $c will transform the array to a string via
`zval_get_string_func` for op2 and output a warning.
Note that zval op1 is of type string due to the first do-while
sequence.
3. The warning of an implicit "array to string conversion" triggers
the ob_start callback to run. This code transform $c (==op1) to a long.
4. The code below the 2 do-while sequences assume that both op1 and op2
are strings, but this is no longer the case. A dereference of the
string will therefore result in a null pointer dereference.
The solution used here is to work with the zend_string directly instead
of with the ops.
For the tests:
Co-authored-by: changochen1@gmail.com
Co-authored-by: cmbecker69@gmx.de
Co-authored-by: yukik@risec.co.jp
Closes GH-10049.
This will allow us to easily check in other mbstring functions if the
list of all supported encodings, returned by mb_list_encodings, is
passed in as input to another function.
Co-authored-by: Ilija Tovilo <ilija.tovilo@me.com>
Normally, we add classes without parents (and no interfaces or traits) directly
to the class map, early binding the class. However, if the same class has
already been registered, we would instead just add a ZEND_DECLARE_CLASS
instruction and let the handler throw a duplicate class declaration exception.
However, with opcache, if on the next request the files are included in the
opposite order, we won't perform early binding. To fix this, create a
ZEND_DECLARE_CLASS_DELAYED instruction instead and handle classes without
parents accordingly, skipping any linking for classes that are already linked in
delayed early binding.
Fixes GH-8846
Once code is emitted to JIT buffer, hint the hardware to
demote the corresponding cache lines to more distant level
so other CPUs can access them more quickly.
This gets nearly 1% performance gain on our workload.
Signed-off-by: Xue,Wang <xue1.wang@intel.com>
Signed-off-by: Tao,Su <tao.su@intel.com>
Signed-off-by: Hu,chen <hu1.chen@intel.com>
If we bind the class to the runtime slot even if we're not the ones who have
performed early binding we'll miss the redeclaration error in the
ZEND_DECLARE_CLASS_DELAYED handler.
Closes GH-11226
In older versions of GCC (<=4.5) designated initializers would not accept member
names nested inside anonymous structures. Instead, we need to use a positional
member wrapped in {}.
Fixes GH-11063
Closes GH-11212
ex->call is only set for user calls, we shouldn't access it here.
zend_unfinished_execution_gc_ex wouldn't actually use it for internal calls, so
it didn't cause any serious issues.
Closes GH-11208
The comment was incorrect. Zval ASTs store their lineno in u2, but u2 does not
get copied in ZVAL_COPY. This triggers use-of-uninitialized errors with MSAN.
Unfortunately, I don't have a simple reproducer.
There are more places in zend_hash.c where the resize happened after some values on the HashTable struct were set.
I reordered them all, but writing a test for these would rely on the particular amount of bytes allocated at given points in time.
Since lowercasing and uppercasing is a common operation for both
internal purposes and userland purposes, it makes sense to implement a
NEON accelerated version for this.
This patch preserves the scratch registers of the SysV x86-64 ABI by storing
them to the stack and restoring them later. We need to do this to prevent the
registers of the caller from being corrupted. The reason these get corrupted
is because the compiler is unaware of the Valgrind replacement function and
thus makes assumptions about the original function regarding registers which
are not true for the replacement function.
For implementation I used a GCC and Clang attribute. A more general
approach would be to use inline assembly but that's also less portable
and quite hacky. This attributes is supported since GCC 7.x, but the
target option is only supported since 11.x. For Clang the target option
does not matter.
Closes GH-10221.
There is a typo which causes the AND and OR range inference to infer a
wider range than necessary. Fix this typo. There are many ranges for
which the inference is too wide, I just picked one for AND and one for
OR that I found through symbolic execution.
In this example test, the previous range inferred for test_or was [-27..-1]
instead of [-20..-1].
And the previous range inferred for test_and was [-32..-25]
instead of [-28..-25].
Closes GH-11170.