Repository navigation
Simplify casts of integer-address pointer constants to integers - #9238
Merged
feliperodri merged 1 commit intoOct 26, 2026
Merged
Conversation
simplify_typecast left (uintN)c alone when c is a non-NULL pointer constant of width N. Both back ends encode such a constant by the bits of its value, and a cast from a pointer to a bit vector of the same width as the identity, so the cast is the bit-vector constant with the same bits. Shadow memory produces these casts. The shadow of a pointer-typed object holds the field's value replicated over the pointer's bytes, as a pointer constant, and get_field reads it back through a cast to a bit vector. Because the cast was not folded, a value read from shadow memory was a term rather than a constant during symbolic execution, and branches on it were explored even when they could not be taken. NULL, addresses of objects, non-constant pointers and casts that change the width are unaffected.
feliperodri
requested review from
kroening,
peterschrammel and
tautschnig
as code owners
October 26, 2026 05:10
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #9238 +/- ##
========================================
Coverage 80.91% 80.91%
========================================
Files 1718 1718
Lines 190292 190320 +28
Branches 73 73
========================================
+ Hits 153974 153999 +25
- Misses 36318 36321 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Member
IMO, this would complicate things unnecessarily. For example, you would need to redeclare pointer-free versions of structs containing pointer-typed fields. |
peterschrammel
approved these changes
Oct 26, 2026
kroening
reviewed
Oct 26, 2026
| // back-ends encode both (bv_pointerst::convert_pointer_type and | ||
| // smt2_convt::convert_constant); shadow memory of pointer-typed objects | ||
| // yields such casts | ||
| if( |
Collaborator
There was a problem hiding this comment.
We should try to find out whether this is at all guaranteed by the standard.
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.
Why
Values read from shadow memory of a pointer-typed object never become constants during symbolic execution, so goto-symex explores branches on them that can never be taken. A
get_fieldon such an object yields(unsignedbv)c, wherecis a pointer constant holding the field value repeated over the pointer's bytes.simplify_typecastfolds this cast forNULLbut not for other integer-address constants.The cost is real. We hit this in Kani, which is adding Tree Borrows aliasing checks that keep the tags of pointers stored in memory in shadow memory. Every loaded tag stayed symbolic, so the monitor's branches were all explored. On our tests, harnesses whose stack objects hold pointers got up to 12x slower with this missing, and some ran out of 12 GB. Heap objects were unaffected, because their shadow is a byte array. The new regression test is a minimal version: 955 symex steps and 100 VCCs remaining without the fold, 48 steps and 0 VCCs with it.
Change
(τ)cis rewritten to the bit-vector constant withc's bits when:cis a non-NULL pointerconstant_exprt;τisunsignedbv,signedbvorbv;τhas the same width asc.This matches the encoding in both back ends:
bv_pointerst::convert_pointer_typeencodes a non-NULL pointer constant asbuild_constant(bvrep2integer(value)), and a pointer-to-integer cast aszero_extension, which is the identity at equal width.smt2_convtprints the constant as(_ bvK w)and the cast as((_ extract w-1 0) ·).So the rewrite replaces a closed term by one with the same value. Symbolic execution can only prune branches that are infeasible either way.
Unaffected:
NULL,address_ofand other non-constant pointers;Where I'd like input
simplify_typecastthe right place? The alternative is to make shadow memory avoid pointer-typed shadow cells, which I haven't explored.Testing
unit/util/simplify_expr.cppfails without the change and passes with it. All[core][util]unit tests pass.regression/cbmc-shadow-memory/pointer-object-constant1fails without the change and passes with it. It checks that the infeasible branch leaves nothing in--program-only.regression/cbmc-shadow-memoryandregression/cbmc-primitivespass.regression/cbmc: 1143 pass. 4 fail identically with and without the change, because the host's gcc 7 lacks-std=gnu2x(_BitInt1,predefined-constants1,static_assert1,typeof2).