cprover: establish liveness for the __CPROVER_allocate side effect#9079
cprover: establish liveness for the __CPROVER_allocate side effect#9079tautschnig wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a soundness/precision issue in the cprover state encoder where the generic __CPROVER_allocate side effect did not establish dynamic-object liveness, causing spurious failures of “pointer safe” properties on freshly allocated objects. It does so by rewriting ID_allocate side effects during expression evaluation into a state-tied allocate_exprt, matching the existing malloc-family special casing.
Changes:
- Rewrite
ID_side_effect/ID_allocateexpressions instate_encodingt::evaluate_expr_recintoallocate_exprt(state, size, ptr_type)so allocations are tied to the current state and become live dynamic objects. - Add a regression test where
__CPROVER_allocateshould yield successful pointer-safety checks on in-bounds accesses. - Add a companion regression test ensuring an out-of-bounds access on an allocated object is still refuted (i.e., the fix doesn’t mask safety violations).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/cprover/state_encoding.cpp | Rewrite ID_allocate side effects into state-tied allocate_exprt to establish liveness uniformly. |
| regression/cprover/pointers/allocate1.c | New test: in-bounds access after __CPROVER_allocate should be pointer-safe and assertion should succeed. |
| regression/cprover/pointers/allocate1.desc | Expected output for the new “in-bounds allocation is safe” regression. |
| regression/cprover/pointers/allocate2.c | New test: out-of-bounds access after __CPROVER_allocate must still be refuted by pointer-safety checks. |
| regression/cprover/pointers/allocate2.desc | Expected output for the new “OOB still refuted” regression. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #9079 +/- ##
========================================
Coverage 80.68% 80.69%
========================================
Files 1714 1714
Lines 189593 189602 +9
Branches 73 73
========================================
+ Hits 152979 152990 +11
+ Misses 36614 36612 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The state encoder special-cases calls to `malloc`/`posix_memalign`/`realloc` by tying the allocation to the current state (`update_state(state, lhs, allocate_exprt(state, size, type))`), which establishes that the result is a live dynamic object. The generic `__CPROVER_allocate` side effect, however, was left as-is during expression evaluation, so an assignment `p = __CPROVER_allocate(...)` never established the object's liveness. As a result, "pointer safe" properties over a freshly allocated object were spuriously refuted, even though cprover has axioms for `ID_allocate`. A function merely named `malloc` (even bodyless) was handled correctly while the same `__CPROVER_allocate` under any other name was not -- the behavior keyed on the C library name rather than the GOTO primitive. Handle the `__CPROVER_allocate` side effect uniformly in `evaluate_expr_rec` by rewriting it to an `allocate_exprt` tied to the current state, mirroring the malloc special case. This makes cprover treat the GOTO allocation primitive directly, independent of any C-level allocator name. The guard requires exactly two operands (matching c_typecheck_expr, which rejects any other ID_allocate) and the size is taken from the first operand. The second operand -- the zero-initialisation flag, with which calloc lowers to `__CPROVER_allocate(size, 1)` -- is not modelled: like the malloc/posix_memalign/realloc cases the allocated contents remain nondeterministic. This is a sound over-approximation. Soundness is preserved: safe allocations verify (pointer safe), while out-of-bounds accesses and functional bugs over allocated objects are still refuted. Regression tests in regression/cprover/pointers: allocate1 proves the in-bounds case safe and pins the `allocate(state, ...)` rewrite of the side effect; allocate2 confirms an out-of-bounds access is still refuted; allocate3 documents the unmodelled zero-initialisation as a KNOWNBUG (a read of calloc-style memory is refuted to be 0 rather than proven). Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
a85f15f to
e623675
Compare
The state encoder special-cases calls to
malloc/posix_memalign/reallocby tying the allocation to the current state (update_state(state, lhs, allocate_exprt(state, size, type))), which establishes that the result is a live dynamic object. The generic__CPROVER_allocateside effect, however, was left as-is during expression evaluation, so an assignmentp = __CPROVER_allocate(...)never established the object's liveness. As a result, "pointer safe" properties over a freshly allocated object were spuriously refuted, even though cprover has axioms forID_allocate. A function merely namedmalloc(even bodyless) was handled correctly while the same__CPROVER_allocateunder any other name was not -- the behavior keyed on the C library name rather than the GOTO primitive.Handle the
__CPROVER_allocateside effect uniformly inevaluate_expr_recby rewriting it to anallocate_exprttied to the current state, mirroring the malloc special case. This makes cprover treat the GOTO allocation primitive directly, independent of any C-level allocator name.Soundness is preserved: safe allocations verify (pointer safe), while out-of-bounds accesses and functional bugs over allocated objects are still refuted.