This issue is properly fixed by GH-7121 on master. For older
branches, disable the use of range information in SCCP, to
reduce impact of potentially incorrect ranges.
Literal compaction was incorrectly assuming that literals with
the same base literal and the same number of related literals
would be equal. Maybe that was the case historically, but at
least it isn't true in PHP 8, where FETCH_CONSTANT and INIT_METHOD
have distinct literals at the second position.
Fix this by making the cache key a concatenation of all literals,
rather than just the base literal. We still distinguish the number
of related literals based on a bias added to the string hash.
Make sure that the previous opline is part of the same block,
otherwise it may be non-dominating.
The test case does not fail on PHP-7.4, but I think the general
problem can appear on 7.4 as well, so I'm applying the patch to
that branch.
The ASSERT_CALLBACK value is not validated at all -- it's possible
to set it to an arbitrary value. As such, the function can also
return any value or type (even without outright abuse, the opcache
func info was wrong in that the return can be rcn, and the array
can be array_of_ref).
Function info for curl_exec() incorrect specified that the
function cannot return true. This is already fixed in PHP 8,
as the func info entry was removed there.
When encountering the following SSA graph:
BB1:
#2.T1 [string] = COALESCE #1.CV0($str) [null, string] BB2
BB2:
#5.T1 [string] = QM_ASSIGN string("")
BB3:
#7.X1 [string] = Phi(#2.X1 [string], #5.X1 [string])
FREE #7.T1 [string]
We would currently determine that #7, #5 are dead, and eliminate
the FREE and QM_ASSIGN. However, we cannot eliminate #2, as
COALESCE is also responsible for control flow.
Fix this my marking all non-CV phis as live to start with. This
can be relaxed to check the kind of the source instruction, but
I couldn't immediately come up with a case where it would be
useful.
* mysqli_commit $flags default value is 0, not -1.
* A number of functions cannot actually return null.
* mysqli_poll parameter names were incorrect, as this function
has a different signature from select.
* fetch functions apart from fetch_all can return false on failure.
Replacing the result type in the general case is dangerous,
because not all opcodes support both VAR and TMP. One common case
is the in_array() result being passed to SEND_VAR, which would
have to be changed to SEND_VAL.
Rather than complicating this logic, reduce the scope to only
doing the type replacement for JMPZ and JMPNZ. The only reason
we're doing this in the first place is to enable the smart branch
optimization, so we can limit it to the relevant opcodes. Replacing
the result type may be marginally useful in other cases as well
(as it may avoid reference checks), but not worth the bother.
We only need to reject functions that could warn (or have runtime
dependent behavior). If a function can throw in some cases, just
let it and discard the result.
We should clear the exception *before* we destroy the execute_data.
Add a variation of the test that indirects through another file,
and would crash otherwise.
For a division like [1..1]/[2..2] produce [0..1] as a result, which
would be the integer envelope of the floating-point result.
The implementation is pretty ugly (we're now taking min/max across
eight values...) but I couldn't come up with a more elegant way
to handle this that doesn't make things a lot more complex (the
division sign handling is the annoying issue here).
It's very common that one of the bounds is LONG_MIN or LONG_MAX.
Dump them as MIN/MAX instead of the int representation in that
case, as it makes the dump less noisy.
For x ? y : z style structures, the live range starts at z, but
may also hold the value of y. Make sure that the refcounting check
takes this into account, by checking the type of a potential phi
user.
If there is a previous use of the new variable in the phi, we need
to NULL out the use chain of the new source we're adding.
Test case is reduced from an assertion failure in the Symfony Demo.