Repository navigation
Conversation
A pointer compared with a scalar register reached numeric narrowing (#1037), so a branch could be pruned from the pointer's modeled address range. The checker accepted these comparisons because Comparable ignored or_r2_is_number. Mixed-type registers had a second, independent hole: a constraint applied to the unsplit state of a {number, pointer} register kills the pointer path through the number side's svalue, so `if r7 == 0` on a {0, packet} register hid every check on the packet path. Checker: comparing values whose types may differ requires the scalar to be exactly zero; a signed or 32-bit register comparison requires numbers, as the immediate-zero case already did. Transformer: Assume splits both operands by type and narrows only what holds wherever regions lie: numbers as before, same-region pointer offsets on 64-bit unsigned/equality comparisons (map descriptors on ==/!=, other regions on == only), and pointer-vs-exact-zero as a null check. Every other pair keeps both branches, so privileged programs, which the checker does not restrict, stay sound. correlated_branch.o passed only through the second hole and is now an expected failure. Fixes #1247 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Elazar Gershuni <elazarg@gmail.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Change: Bug fix · Severity of issue fixed: Medium Merge Risk: ⚪ Minimal · up to
|
Fixes #1247. Alternative to #1248 (same checker rule; covers the cases below that #1248 leaves open).
Issues
Comparableignoresor_r2_is_number, so the checker accepts a comparison between a pointer and any number. Before Fix assertion crash in Assume when operands have different types #1037 this hit an assert inAssume; since Fix assertion crash in Assume when operands have different types #1037 it narrows the pointer'ssvalue/uvalueagainst the scalar, pruning branches from the modeled address range ([1, ptr_max]), which is not where real pointers live.{number, pointer}register.svalueis a number-only kind, so after a join the number side's value (e.g.svalue = 0) remains in the shared state. Applyingr7 != 0to that state without splitting by type kills the pointer path too. On HEAD,r7 = {0, packet}followed byif r7 == 0hides an out-of-bounds packet read atr7 + 60000. Affects the immediate path and the mixed-type register path.Assumes: pointer vs. nonzero immediate; signed or 32-bit comparisons of pointers (with zero or with each other);!=/ordering of map-value pointers with equal offsets (different regions); pointers of different types (sent to bottom).Fix
Checker (
Comparable): when the types may differ, a comparison requires the scalar to be exactly zero ([0, 1]is rejected). Subtraction is unchanged.Assertions: a signed or 32-bit register comparison requires both operands to be numbers, via the new
admits_pointer_operands(Condition), which the immediate-zero case now also uses.Transformer (
Assume): split both operands by type and narrow only what is independent of region placement:==/!=; other regions==onlyuvalueSoundness no longer depends on the checker, so privileged programs are covered.
Behavior changes
correlated_branch.o(xdp) passed only through issue 2: the packet path was pruned, so its bounds check never ran. Now that path is analysed, and the read fails because the join losespacket_size >= 14, which holds only on the packet path. Markedexpected_failure(VerifierTypeTracking).jump.yaml"assume reg-vs-reg with different types (ctx == number)": the expected post-state now readsr2.svalue=0instead ofr2.svalue=r1.svalue. Given the pre-state, these mean the same.Not addressed
uvaluestill models the address, capped atptr_max. It now affects verdicts only through null checks, which use its lower bound. A null type, with nouvaluetracking for pointers, is the intended long-term design.Tests
src/test/test_assume.cpp: applies implicit (unchecked)Assumes directly; 27 checks. HEAD fails 11; Reject nonzero scalar comparisons with pointers #1248 fails 9.jump.yaml: the Prevent pointer addresses from narrowing mixed pointer/scalar comparisons #1247 case; a possibly-pointer register vs. nonzero scalar; a[0, 1]scalar; exact-zero==/!=/>; signed and 32-bit vs. zero register (rejected); packet-or-zero null check with an out-of-bounds read (rejected). Each rejection test fails on HEAD.🤖 Generated with Claude Code