Repository navigation
Loops that write local or dynamic storage: frame them by the objects they write - #75
Merged
Merged
Conversation
…r does not represent.
…the release notes.
…the address passed to a callee.
…reseated, and members named whole in modifies as their bytes.
…n the architecture.
… to report, so no header's is mangled.
… loop that stores to local storage keeps what was initialized.
…reads bound the head.
…dition reads what the loop changes.
…ion across loops, with a checked example.
…s of the objects it writes.
This was referenced Oct 24, 2026
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.
A loop that wrote local or dynamic storage forgot the value of every object. On the base (#74):
Now:
Stacked on #74, which found it: a loop that fills a local array is now verified, but it still forgot every other local.
Cause
A loop writes only the objects its stores and calls reach (
loopWriteRegions): a parameter's cell, the entry objects of a pointer's origins, or a parameter's or global's object (rootRegion). Local and dynamic storage has no origin (it carries its lifetime companion instead) and is no root there, so a loop that stored to it was not framed and the whole value heap was havocked. The other bound, the function's own frame, rightly declines when the function owns storage: a store to its own storage is never checked against itsmodifies, so assuming that frame would freeze the loop's writes to its locals.Design
The object model already bounds where such a store lands; the frame now says so, as it does for parameters.
representedCell): when its address is that pointer, a reference to a local say, and the loop assigns neither the pointer nor its lifetime companion, it writes that one cell. So does a call whose footprint is the one cell its reference argument binds.representedRegions,RepresentedObjects: the allocated type and size, recorded at eachVAllocateStmtand fresh-owned call), under the same condition, writes that object: its fields lie in the record's layout and its fixed-array indices are checked (kindbounds), as a store through a parameter lies in its entry object by the object model's checks; a dynamic object is one scalar. So does a call whose footprint's argument is rooted there.VFunction::Layouts, array repeats expanded), each one cell, so the loop's frame stays the chain of stores of fresh values (ARCHITECTURE, loop frames); without a layout it is its bytes. As a byte range (aHeapFrame, an unbounded quantifier), Z3 found no counterexample tooverwrittenbelow in 90 s; with the cells it fails in 0.2 s.Tests
suite/loop_local_frames.cpp(Z3, with a check that no proof is vacuous, and BMC):kept_value: a scalar behind a reference keeps its value across a loop that writes an array;other_array: so does another array;record_read: a local record the loop reads keeps the field it does not write;with_parameter: a loop writing a parameter's object and a local array keeps a third object;through_call: a call in the loop writing an element of a local array (set(a[i], 0));dynamic_kept: anewobject keeps its value;reference_written,reference_call: a store or a call through a reference to a local writes only that local;overwritten: a cell the loop writes is not kept (postcondition), which a frame too small would prove;through_reference: neither is a local written through a reference to it (postcondition).On the base, all eight that verify fail their postconditions (
other_arrayitsoverflowcheck first).Mutants, each built on an assertions build and run on
loop_local_frames.cpp,loop_initialization.cpp,loop_frames.cpp,out_parameters.cpp, andlocal_record_arguments.cpp: 6 of 6 killed.loop_local_frames(the eight that verify)loop_local_frames(overwrittenverifies: the false proof a frame too small gives)loop_local_frames(overwritten,through_reference:solver.timeout)loop_local_frames(reference_written)loop_local_frames(through_call)loop_local_frames(reference_call)Docs
other_array,overwritten).Checked
docs_examples.testanddocs_coverage.testpass.