fix(codegen): auto-vivify appends onto a null-container sentinel mixed cell#600
Open
mirchaemanuel wants to merge 4 commits into
Open
fix(codegen): auto-vivify appends onto a null-container sentinel mixed cell#600mirchaemanuel wants to merge 4 commits into
mirchaemanuel wants to merge 4 commits into
Conversation
…d cell A missed indexed read forwarded through a ternary/branch merge boxes the null-container sentinel (0x7ffffffffffffffe) into a `mixed` cell tagged as an indexed array. `$r[] = v` on that cell (`Op::MixedArrayAppend`) unboxed the receiver, accepted the tag-4 payload, and read the array header inline (`ldur x1, [x0, #-8]`) before `__rt_array_to_mixed`. The only pre-deref guard was a plain-zero check, so the sentinel passed through and the header read faulted at `sentinel - 8` (issue illegalstudio#592, exit 139). PHP instead auto-vivifies the null into a fresh `["z"]` array and continues. `lower_mixed_array_append` (aarch64 + x86_64) now guards the unboxed payload for both the null pointer and the in-band null-container sentinel before the header dereference (issue illegalstudio#533 convention). For a container-shaped tag with such a payload — indexed (4), associative (5), or null (8) — it auto-vivifies: it allocates a fresh empty indexed array (`__rt_array_new(0, 8)`, as `__rt_mixed_new_empty_array_cell` does), transfers that reference into the Mixed cell, retags the cell as an indexed array, and appends at index 0 through the shared `__rt_mixed_array_set` tail. Scalars whose payload happens to be zero or the sentinel are excluded by the tag check and keep the existing drop behavior. Ownership is balanced (the cell owns the fresh array; the array owns the appended value), so the autovivify path stays heap-clean. Adds regression coverage in tests/codegen/arrays/mixed_append_autovivify.rs: the exact issue repro, the `["z"]` read-back, a heap-clean sentinel variant, an associative missed-read sibling, a multi-append grow case, and a real-array Mixed-cell control that guards against a double-conversion regression. Fixtures make the taken arm `$argc`-dependent to defeat constant folding. Claude-Session: https://claude.ai/code/session_01HRvbcjPHa3kAojdCjNZL5L
Contributor
Author
|
While validating this fix I found two adjacent pre-existing bugs (both reverified on current main + this branch): #601 ( |
…el-autovivify # Conflicts: # CHANGELOG.md
Member
|
I updated this PR after merging the latest Changes now included:
Focused validation completed:
Published head: GitHub Actions for that exact head is currently queued. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #592.
Root cause
A missed indexed read forwarded through a ternary/branch merge boxes the null-container sentinel (
0x7ffffffffffffffe) into amixedcell tagged as an indexed array. Appending to that cell ($r[] = v, lowered toOp::MixedArrayAppend) unboxes the receiver, accepts the tag-4 payload, and reads the array header inline (ldur x1, [x0, #-8]on aarch64,mov rsi, [rdi - 8]on x86_64) before calling__rt_array_to_mixed. The only pre-dereference guard was a plain-zero check (cbz x1/test rdi, rdi), so the sentinel passed through and the header read faulted atsentinel - 8(exit 139). This append path calls__rt_array_to_mixeddirectly rather than throughlower_array_to_mixed, so the call-site sentinel guards added in #583 did not cover it.PHP 8.4 instead auto-vivifies the null into a fresh single-element array and continues: for the repro it warns
Undefined array key 5on the missed read, then printsdonewith$r === ["z"](no warning on the append).Run with no arguments, this segfaulted (139); it now prints
done.Fix
lower_mixed_array_append(aarch64 + x86_64) now guards the unboxed payload for both the null pointer and the in-band null-container sentinel before the header dereference, per the issue #533 convention. For a container-shaped tag whose payload is null or the sentinel — indexed (4), associative (5), or null (8) — it auto-vivifies: it allocates a fresh empty indexed array (__rt_array_new(0, 8), matching__rt_mixed_new_empty_array_cell), transfers that reference into the Mixed cell, retags the cell as an indexed array, and appends at index 0 through the shared__rt_mixed_array_settail. Scalars whose payload happens to be zero or equal to the sentinel value (PHP_INT_MAX - 1) are excluded by the tag check and keep the existing drop behavior.Ownership is balanced (the cell owns the fresh array; the array owns the appended value; the replaced sentinel/null payload holds nothing to release), so the auto-vivify path stays heap-clean. Both supported architectures are handled in the same change.
PHP semantics
Matches PHP's
$x[] = vonnull: the null container auto-vivifies to a fresh[v]array, the missed read still warns, and the append itself does not warn. A real-null Mixed cell ($r = cond ? null : [1]; $r[] = "z";) also auto-vivifies correctly through the same tag-8 route.Test plan
New
tests/codegen/arrays/mixed_append_autovivify.rs:done);["z"]read-back (length 1, index 0);Fixtures make the taken ternary arm
$argc-dependent to defeat constant folding. Focused slices run green:arrays::mixed_append_autovivify,nested_autovivify,array_to_mixed,append,push,sentinel,null_container,mixed_array,regressions::arrays,ternary(~292 tests, 0 failures). x86_64 coverage relies on CI (no local cross-assembler).https://claude.ai/code/session_01HRvbcjPHa3kAojdCjNZL5L