Add automated overflow/underflow checks for bitvector arithmetic#592
Add automated overflow/underflow checks for bitvector arithmetic#592tautschnig merged 30 commits intomainfrom
Conversation
Add PropertyType.arithmeticOverflow alongside the existing divisionByZero property type. This is the foundation for bitvector and floating-point overflow/underflow checks. Changes: - EvalContext.lean: new PropertyType.arithmeticOverflow variant - MetaData.lean: new MetaData.arithmeticOverflow constant - StatementEval.lean: recognize "arithmeticOverflow" in metadata - Verifier.lean: display labels/emoji, check mode handling - SarifOutput.lean: SARIF classification "arithmetic-overflow" - PrecondElim.lean: classify BvN.Safe* operators as overflow checks Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Add safe BV operator variants (SafeAdd, SafeSub, SafeMul, SafeNeg) with built-in overflow preconditions for all BV sizes (1, 2, 8, 16, 32, 64). Each safe operator carries a precondition asserting that the corresponding signed overflow predicate is false. New overflow predicate functions (SAddOverflow, SSubOverflow, SMulOverflow, SNegOverflow) are added as factory functions that map to SMT-LIB's bvsaddo, bvssubo, bvsmulo, and bvnego operations. The PrecondElim transform automatically generates verification conditions at each use site of a safe operator, classified as 'arithmeticOverflow' property type. Changes: - IntBoolFactory.lean: add optional preconditions parameter to unaryOp - Factory.lean: new BVEvalKind variants (overflowBinary/overflowUnary), BVSafeOpSpecs, ExpandBVSafeOpFuncDefs/Names/Exprs macros - SMTEncoder.lean: map safe ops to normal SMT ops, overflow predicates to bvsaddo/bvssubo/bvsmulo/bvnego - ASTtoCST.lean: handle safe ops and overflow predicates in CST printing - New OverflowCheckTest.lean: unit tests verifying factory presence and WF obligation generation - Updated ProgramTypeTests.lean, ProcedureEvalTests.lean: new factory function listings Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Add SafeSDiv and SafeSMod operators that check both division-by-zero (y ≠ 0) and signed division overflow (x ≠ INT_MIN ∨ y ≠ -1) for all BV sizes. These use the new SDivOverflow predicate which leverages Lean4's BitVec.sdivOverflow from the standard library. Each SafeSDiv/SafeSMod carries two preconditions: 1. ¬(y == 0) — division by zero (classified as divisionByZero) 2. ¬SDivOverflow(x, y) — INT_MIN / -1 overflow (classified as arithmeticOverflow) Changes: - Factory.lean: SDivOverflow predicate in BVOpSpecs, SafeSDiv/SafeSMod via ExpandBVSafeDivOpFuncDefs macro with dual preconditions - SMTEncoder.lean: SDivOverflow encoded as (x == INT_MIN) ∧ (y == -1), SafeSDiv/SafeSMod map to bvsdiv/bvsrem - ASTtoCST.lean: SafeSDiv/SafeSMod CST printing - Updated test expected outputs Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Add unsigned overflow predicates (UAddOverflow, USubOverflow, UMulOverflow, UNegOverflow) and safe unsigned operators (SafeUAdd, SafeUSub, SafeUMul, SafeUNeg) for all BV sizes. Unsigned overflow is controlled by the new unsignedOverflowCheck option in VerifyOptions (default: false). Front-end translators use the safe unsigned variants when this option is enabled. SMT encoding for unsigned overflow predicates: - UAddOverflow: bvult(bvadd(x, y), x) — wrapping add < operand - USubOverflow: bvult(x, y) — unsigned subtraction underflow - UMulOverflow: bvugt(zero_extend(N, x) * zero_extend(N, y), zero_extend(N, MAX)) - UNegOverflow: x != 0 Note: ProgramTypeTests and ProcedureEvalTests expected output needs updating for the new factory functions (the OverflowCheckTest validates the core functionality). Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Teach the GOTO backend to translate overflow predicate expressions to CBMC's native overflow expression types: - SAddOverflow/UAddOverflow → overflow-+ (PlusOverflow) - SSubOverflow/USubOverflow → overflow-- (MinusOverflow) - SMulOverflow/UMulOverflow → overflow-* (MultOverflow) - SNegOverflow/UNegOverflow → overflow-unary- (UnaryMinusOverflow) Safe operators (SafeAdd, SafeSub, etc.) translate to the same GOTO operations as their unsafe counterparts (Plus, Minus, etc.), since the overflow checks are handled by PrecondElim-generated assertions. Changes: - Expr.lean: add MinusOverflow, MultOverflow (Binary), UnaryMinusOverflow (Unary) with format strings and helper constructors - LambdaToCProverGOTO.lean: map overflow predicates and safe ops in fnToGotoID; add signed ops to isSignedBvOp - Tests: verify overflow predicate → GOTO expression mapping Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Add IEEE 754 double-precision floating-point support to the SMT layer and Core language, with safe arithmetic operators for overflow detection. SMT layer: - TermPrimType.float64: maps to (_ FloatingPoint 11 53) in SMT-LIB - TermPrim.rne: RoundNearestTiesToEven rounding mode constant - Op.FP: fp.add, fp.sub, fp.mul, fp.div, fp.neg, fp.isInfinite, fp.isNaN, fp.isZero operations - DDM Translate: float64 sort and rne constant encoding Core Factory: - Float64.Add/Sub/Mul/Div/Neg: basic arithmetic (unevaluated) - Float64.IsInfinite/IsNaN: predicates - Float64.SafeAdd/SafeSub/SafeMul/SafeDiv: overflow-checked variants - SMT encoding: FP ops use RNE rounding mode Laurel integration: - TFloat64 now maps to LMonoTy.float64 (was "Float64IsNotSupportedYet") - Arithmetic on Float64 uses safe variants (SafeAdd/Sub/Mul/Div) - PrecondElim classifies Float64.Safe* as arithmeticOverflow Note: Float64 safe ops are currently unevaluated (no preconditions via binaryOp). Full overflow checking (result becomes ±infinity from finite inputs) requires a dedicated transform that inserts assertions using Float64.IsInfinite, similar to CBMC's float_overflow_check approach. Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
…encoding
Address all remaining work items:
1. Test file updates:
- ProgramTypeTests.lean: replaced docstring with actual output including
all new factory functions (Float64, unsigned overflow, safe ops)
- ProcedureEvalTests.lean: same
- TypeDecl.lean: added float64 to known types list
2. Float64 overflow predicates:
- Float64.AddOverflow/SubOverflow/MulOverflow/DivOverflow: predicates
that return true when finite inputs produce infinite result
- SMT encoding: ¬isInfinite(x) ∧ ¬isInfinite(y) ∧ isInfinite(fp.op(RNE, x, y))
- Following CBMC's float_overflow_check approach
3. Bug fixes:
- UAddOverflow SMT encoding: use operand type (not return type) for bvadd
- SMT Denote: add float64 case (returns none)
- SMT Translate: add float64 case (throws unsupported)
- Remove Float64IsNotSupportedYet datatype from HeapParameterizationConstants
- Add float64 to KnownLTys so Core type checker recognizes it
Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Replace #check with example := for silent compilation checks. Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR extends Strata’s “safe operator + PrecondElim” verification pipeline (previously used for division-by-zero) to also generate verification conditions for arithmetic overflow/underflow across bitvector operations and (newly supported) Float64, with a new arithmeticOverflow property type surfaced through reporting/SARIF and supported in the CBMC GOTO backend.
Changes:
- Introduces new Core bitvector overflow predicate functions and corresponding
Safe*operators intended to carry overflow preconditions (plus special handling for signed div overflow). - Adds an SMT-layer
float64sort and Float64 operators/predicates, and updates Laurel’sTFloat64translation to use real Float64 operators. - Adds reporting/plumbing for the new
arithmeticOverflowproperty type (labels/emojis/SARIF classification) and adds/updates tests for factory presence and backend translation.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| StrataTest/Languages/Core/Tests/ProgramTypeTests.lean | Updates expected environment dump to include new Float64/BV overflow/safe functions. |
| StrataTest/Languages/Core/Tests/ProcedureEvalTests.lean | Updates expected prelude with new Float64/BV overflow/safe functions. |
| StrataTest/Languages/Core/Tests/OverflowCheckTest.lean | Adds unit tests for presence of safe ops/predicates and WF obligation generation. |
| StrataTest/Languages/Core/Examples/TypeDecl.lean | Updates expected reserved-type list to include float64. |
| StrataTest/Backends/CBMC/GOTO/LambdaToCProverGOTO.lean | Adds tests for mapping overflow predicates and safe ops to CBMC identifiers. |
| Strata/Transform/PrecondElim.lean | Classifies safe BV/Float64 preconditions as arithmeticOverflow for SARIF/reporting. |
| Strata/Languages/Laurel/LaurelToCoreTranslator.lean | Translates Laurel Float64 types/ops to Core Float64 (and uses Float64 safe ops for arithmetic). |
| Strata/Languages/Laurel/HeapParameterizationConstants.lean | Removes the old placeholder Float64IsNotSupportedYet datatype. |
| Strata/Languages/Core/Verifier.lean | Extends minimal/full label+emoji logic to include arithmeticOverflow. |
| Strata/Languages/Core/StatementEval.lean | Maps MetaData.propertyType strings to the new .arithmeticOverflow PropertyType. |
| Strata/Languages/Core/SarifOutput.lean | Adds SARIF classification string for arithmeticOverflow and updates unreachable handling comment. |
| Strata/Languages/Core/SMTEncoder.lean | Adds SMT encoding for float64 sort/ops, float overflow predicates, signed overflow builtins, and unsigned overflow encodings. |
| Strata/Languages/Core/Options.lean | Adds unsignedOverflowCheck option field (default false). |
| Strata/Languages/Core/Factory.lean | Adds float64 to known types; generates BV overflow predicates and BV safe ops; adds Float64 functions to the factory. |
| Strata/Languages/Core/DDMTransform/ASTtoCST.lean | Extends CST translation mappings for safe BV ops (and attempts overflow predicate handling). |
| Strata/DL/Util/BitVec.lean | Adds BitVec.unegOverflow. |
| Strata/DL/SMT/Translate.lean | Rejects float64 sort in Lean-denotation translation. |
| Strata/DL/SMT/TermType.lean | Adds float64 to SMT term primitive types. |
| Strata/DL/SMT/Term.lean | Adds rounding-mode primitive rne and its typing. |
| Strata/DL/SMT/Op.lean | Adds SMT-LIB floating-point ops (fp.add/sub/mul/div, etc.) and abbrev generation. |
| Strata/DL/SMT/Denote.lean | Marks float64 as having no Lean denotation. |
| Strata/DL/SMT/DDMTransform/Translate.lean | Emits SMT-LIB (_ FloatingPoint 11 53) for float64 and emits RNE constant. |
| Strata/DL/Lambda/LTy.lean | Adds LMonoTy.float64. |
| Strata/DL/Lambda/IntBoolFactory.lean | Extends unaryOp helper to accept preconditions (for safe operator construction). |
| Strata/DL/Imperative/MetaData.lean | Adds MetaData.arithmeticOverflow classification string constant. |
| Strata/DL/Imperative/EvalContext.lean | Adds PropertyType.arithmeticOverflow and integrates it into formatting/unreachable semantics. |
| Strata/Backends/CBMC/GOTO/LambdaToCProverGOTO.lean | Maps safe ops and overflow predicates to CBMC GOTO overflow IDs and marks them as signed BV ops where appropriate. |
| Strata/Backends/CBMC/GOTO/Expr.lean | Adds CBMC expression identifiers for minus/mult/unary-minus overflow and helper constructors. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…T handling
Address all 5 Copilot review comments:
1. Float64 safe ops now carry overflow preconditions (Comment 5):
Float64.SafeAdd/Sub/Mul/Div each require ¬Float64.*Overflow(x, y).
PrecondElim now generates overflow VCs at each Float64 use site.
2. SafeSDiv/SafeSMod div-by-zero classification (Comment 3):
classifyPrecondition now takes a per-function precondition index.
For SafeSDiv/SafeSMod, precondition 0 (y≠0) is classified as
divisionByZero, precondition 1 (INT_MIN/-1) as arithmeticOverflow.
3. ASTtoCST overflow predicate handling (Comments 1 & 2):
- SNegOverflow/UNegOverflow: changed from neg_expr to Bool.Not
- Binary overflow predicates (SAddOverflow etc.): added to
bvBinaryOpMap with boolean approximation for CST printing
- These only appear in generated assertions, not user code
4. unsignedOverflowCheck documentation (Comment 4):
Added doc comment explaining the option is forward-looking
infrastructure, to be wired when front-end languages gain BV types.
Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Replace the unused unsignedOverflowCheck bool with a structured OverflowChecks configuration covering signed BV, unsigned BV, and float64 overflow checks. Defaults match C/IEEE 754 semantics: signed BV checked (UB in C), unsigned BV unchecked (wraps), float64 unchecked (overflows to ±∞ per IEEE 754). - Add OverflowChecks structure to Core.Options - Wire --overflow-checks CLI flag to StrataVerify - Thread config through LaurelTranslateOptions and TranslateState - Laurel translator now consults overflowChecks.float64 to choose between safe and non-safe float ops (non-safe by default) Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
…hecks # Conflicts: # Strata/Languages/Core/Options.lean # Strata/Languages/Laurel/HeapParameterizationConstants.lean # Strata/Languages/Laurel/LaurelToCoreTranslator.lean # StrataVerify.lean
|
I haven't looked at this PR deeply, but I'd recommend removing FP support in Strata's SMT dialect from this PR and keeping it focused only on integers for now. Let's go back to the design board before we use SMTLib's FP theory. |
…hecks Resolve conflicts: - Options.lean: keep both overflowChecks and outputSarif fields - LaurelToCoreTranslator.lean: keep Float64 support, adopt throwExprDiagnostic pattern, keep both overflowChecks and coreProgramHasSuperfluousErrors in TranslateState - TypeDecl.lean: include both float64 and Sequence in known types list - StrataVerify.lean: accept deletion (migrated to strata verify) - StrataMain.lean: add --overflow-checks flag to verify command Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
…cks) Per reviewer feedback, remove floating-point support from this PR to keep it focused on integer overflow checks. FP support in the SMT dialect needs further design discussion before using SMT-LIB's FP theory. Removed: - SMT dialect: float64 primitive type, Op.FP operations, rne rounding mode - Lambda types: LMonoTy.float64 - Core Factory: all Float64 functions (Add/Sub/Mul/Div/Neg, predicates, safe ops, overflow predicates) - Core SMT Encoder: Float64 sort mapping and FP operation encoding - Laurel translator: TFloat64 type translation and Float64 operator selection - PrecondElim: Float64.Safe* classification - MetaVerifier: Op.FP ToExpr instance Retained: - OverflowChecks.float64 config field (harmless, ready for future use) - All integer overflow infrastructure (signed/unsigned BV, division overflow) - arithmeticOverflow property type and SARIF classification - GOTO backend overflow expression support Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
I have now removed the floating-point aspects from this PR. |
|
@keyboardDrummer-bot Please resolve git conflicts. |
Conflicts resolved: - Strata/Languages/Laurel/LaurelToCoreTranslator.lean: Took main's refactored version (pipeline code moved to LaurelCompilationPipeline.lean) and re-applied the PR's overflowChecks additions to TranslateState and LaurelTranslateOptions. - Strata/Languages/Laurel/LaurelCompilationPipeline.lean: Added overflowChecks := options.overflowChecks to initState, matching the PR's intent now that the pipeline lives in this new file. - 4 test expected files: Took main's updated expected outputs.
|
I've resolved the git conflicts by merging Conflicts resolved (5 files):
Verification: Both |
The previous merge (8d208f9) took main's expected outputs for 4 Python test files, but those didn't account for this PR's PrecondElim.lean change that strips propertySummary metadata from generated precondition checks. Regenerated the expected files to match actual output. Affected files: - test_class_methods.expected - test_class_mixed_init.expected - test_class_with_methods.expected - test_deep_inline.expected
|
Fixed the CI failure in Root cause: The previous merge conflict resolution (8d208f9) took Specifically, auto-generated precondition checks now produce an additional line with a technical label like Fix: Regenerated the 4 affected expected files using
Verified locally:
|
Replace verbose match arms that enumerate assert/divisionByZero/ arithmeticOverflow separately with property.passWhenUnreachable checks. The key insight: all assert-like properties (assert, divisionByZero, arithmeticOverflow) behave identically, while cover behaves differently. Affected functions: label, emoji, verifySingleEnv check selection. This makes the code resilient to adding new assert-like property types. Addresses shigoel's feedback on PR #592. Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Safe BV operators (SafeAdd, SafeSub, SafeMul, SafeNeg, SafeSDiv, SafeSMod and their unsigned variants) previously mapped to the same CST constructors as their unsafe counterparts, losing the safe annotation through AST/CST roundtrip. Add new grammar productions: safe+, safe-, safe*, safe_neg, safesdiv, safesmod Update ASTtoCST to emit the new constructors for safe operators. Update Translate to map them back to the safe Factory operators. Signed and unsigned variants share the same CST syntax (the type system distinguishes them); CST→AST maps to the signed variant. Includes SafeBvOps.core.st example and roundtrip test via run_examples.sh. Addresses shigoel's feedback on PR #592 about safe operators being lost through AST/CST roundtrip. Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
… call SDivOverflow(x, y) was mapped to .functionApplication which CBMC cannot solve. Now expanded inline to (x == INT_MIN) && (y == -1), matching the SMT backend's sdivOverflowEnc. Addresses shigoel's feedback on PR #592. Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
Motivation
Arithmetic overflow is a major source of bugs in systems code. Signed integer overflow is undefined behavior in C/C++, unsigned overflow silently wraps, and floating-point overflow silently produces infinity. Unlike division-by-zero (added in #510), these errors are easy to miss in code review and hard to trigger in testing.
This PR extends Strata's automated verification to catch arithmetic overflow at every use site — the same way #510 catches every division by zero — so that front-end languages like Laurel (and future C/Java front-ends) get overflow checking for free, without any source-level instrumentation.
Approach
Following the pattern established by #510 for division-by-zero:
Bv32.SafeAdd) carry built-in preconditions asserting that overflow does not occur.PrecondElimtransform automatically generates verification conditions at each use site.This keeps the architecture uniform: the same mechanism handles division-by-zero, signed overflow, unsigned overflow, and signed division overflow (
INT_MIN / -1).What's covered
Signed bitvector overflow (all sizes: bv1, bv2, bv8, bv16, bv32, bv64):
SafeAdd/SafeSub/SafeMul/SafeNegwith preconditions using SMT-LIB'sbvsaddo/bvssubo/bvsmulo/bvnegoSigned division overflow (
INT_MIN / -1):SafeSDiv/SafeSModwith dual preconditions: divisor ≠ 0 AND ¬(dividend = INT_MIN ∧ divisor = −1)Unsigned bitvector overflow (configurable via
unsignedOverflowCheckoption):SafeUAdd/SafeUSub/SafeUMul/SafeUNegwith unsigned overflow predicatesGOTO backend:
overflow-+,overflow--,overflow-*,overflow-unary-expressionsKey design decisions
Bv32.Add(wrapping) andBv32.SafeAdd(checked) coexist. Front-end translators choose which to emit.unsignedOverflowCheck := true.Description of changes:
New
arithmeticOverflowproperty type alongside the existingdivisionByZero, with SARIF classification, display labels, and check mode handling. Overflow predicates and safe operators for all BV sizes generated via Lean4 macros. SMT encoding for signed overflow uses SMT-LIB builtins; unsigned uses custom encodings. Comprehensive unit tests verify factory presence, WF obligation generation, and GOTO translation.Co-authored-by: Kiro kiro-agent@users.noreply.github.com
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.