Skip to content

Z3JavaTranslator: widen IntNum/ArithExpr coercion across binary BV ops#1

Open
jon-bell wants to merge 2 commits into
masterfrom
fix/z3-translator-coercion
Open

Z3JavaTranslator: widen IntNum/ArithExpr coercion across binary BV ops#1
jon-bell wants to merge 2 commits into
masterfrom
fix/z3-translator-coercion

Conversation

@jon-bell
Copy link
Copy Markdown

Summary

Z3JavaTranslator.postVisit(Operation) had asymmetric and incomplete coercion of non-BitVec operands to BitVec for several binary bit-vector operators. Two failure modes surfaced in downstream use (Knarr concolic execution on Apache Commons Compress):

  • BIT_AND only coerced IntNum on the rhs when the lhs was a BitVecExpr. The mirror shape (IntNum lhs, BitVecExpr rhs) fell through to the casts and crashed with ClassCastException: IntNum cannot be cast to BitVecExpr.
  • Most other binary BV operators (BIT_OR, BIT_XOR, ADD, SUB, MUL, DIV, MOD, LT, LE, GT, GE, SHIFTL, SHIFTR, SHIFTUR) had related holes — either handling IntNum on only one side, or missing the IntExpr (non-literal ArithExpr) case that arises when a sub-expression came through I2R/BV2I.

This PR mirrors the existing BIT_OR pattern uniformly across every binary BV op: for each operator, coerce IntNum on either side via mkBV(...getInt64(), sortSize) (bumped from getInt() so 32-bit masks like 0xFFFFFFFF don't lose bits), and convert IntExpr via mkInt2BV(sortSize, intExpr) on either side.

Changes

  • Commit 7f2b048: fix BIT_AND — symmetric IntNum coercion + IntExpr → BV widening (the bug that surfaced the issue).
  • Commit 4bf8a46: sweep the same pattern across BIT_OR, BIT_XOR, ADD, SUB, MUL, DIV, MOD, LT, LE, GT, GE, SHIFTL, SHIFTR, SHIFTUR. Left BIT_NOT (unary), BIT_CONCAT (special-cased for strings), SELECT / STORE (sort-dependent on the array type) alone.

Ops NOT widened — flagged for follow-up if they ever matter:

  • RealExpr on a BV op path would still ClassCastException. Semantics for mixing real and BV are dubious.
  • SeqExpr/ArrayExpr on BV op paths — ill-typed input rather than missing coercion.
  • Comparison ops keep their special SeqExpr-with-IntNum (character-range) branches; SeqExpr + non-literal IntExpr isn't covered.

Test plan

  • mvn test in green/ is a no-op (no test sources present); mvn -DskipTests install succeeds and the jar is consumable by downstream.
  • End-to-end verified in gmu-swe/knarr: a 1364-constraint byte-array path condition from a commons-compress tar-header parse now round-trips through Z3 cleanly where it previously threw ClassCastException or returned UNSAT due to dropped-bit IntNum masks.

🤖 Generated with Claude Code

jon-bell and others added 2 commits April 19, 2026 23:03
… BIT_AND

BIT_AND only coerced IntNum rhs when lhs was BitVecExpr. A symbolic
byte-array tagged with BVVariable(..., 32) flowing through Knarr's path
constraints hit the reverse shape (BitVecExpr rhs, IntNum lhs) and
Z3JavaTranslator crashed with "ClassCastException: IntNum cannot be cast
to BitVecExpr" inside the mkBVAND call.

A second shape — BIT_AND with an ArithExpr (IntExpr) side that came from
an I2R or related conversion — also reached the casts. Add mkInt2BV
widening for that case so the BVAND always sees two BitVecExprs of the
same sort.

Together with Knarr's Symbolicator fix that now tags byte[] elements as
BitVec(32) (matching Z3JavaTranslator's byte-array range sort), this
lets byte-array concolic execution complete the solver round-trip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Several binary BV cases in postVisit had incomplete sort coercion: some
handled IntNum on only one side, and none handled non-literal IntExpr
(ArithExpr) operands via mkInt2BV. This mirrors the fix applied to
BIT_AND in 7f2b048 across all remaining binary BV operators so that
either operand may be an IntNum or IntExpr and the pair is normalized
to matching BV sort before invoking the Z3 BV API.

Ops updated: BIT_OR, BIT_XOR, ADD, SUB, MUL, DIV, MOD, LT, LE, GT, GE,
SHIFTL, SHIFTR, SHIFTUR. BIT_AND already done upstream.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant