fix(klee): name symbolic objects by their type, not by unexpanded macro text - #63
Open
GuilhermeBn198 wants to merge 1 commit into
Open
fix(klee): name symbolic objects by their type, not by unexpanded macro text#63GuilhermeBn198 wants to merge 1 commit into
GuilhermeBn198 wants to merge 1 commit into
Conversation
…ro text
klee_make_symbolic(&non_det, sizeof(non_det), "non_det_#type");
`#type` inside a string literal is not the stringify operator -- it is the two
characters # and t. Every symbolic object in every .ktest was called the
literal "non_det_#type": the same name for an int, a double and a char.
Nothing read that name, which is how it survived every baseline this tool has
run. The nondet log carries its own type column, so suites and verdicts were
correct and there was no symptom to notice.
It matters now. KLEE writes one .ktest per explored path, each holding that
path's input vector in consumption order -- exactly the material a
Cover-Branches suite is made of, produced by the engine for free and without
perturbing the search. A .ktest records each object's name, size and bytes but
NOT its type, so the name is the only place a type can travel. Without it four
bytes could be an int, a float or half of a long and nothing tells them apart.
before: object 0: name: 'non_det_#type' size: 4
object 1: name: 'non_det_#type' size: 1
after: object 0: name: 'non_det_int' size: 4 int: 42
object 1: name: 'non_det_char' size: 1 int: 122
The test asserts the pairing of name to size, not just the names: a one-byte
object called non_det_int would be worse than no name at all. Writing it also
caught a trap worth recording -- klee-last is a symlink, and `find` does not
descend into a symlinked start point without -L, so the first version reported
"no ktest files" on a run that had produced three.
Verified against a full build: new test 5/5, emission suite still 14/14.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Found while investigating H1.3 (per-input test vectors for Cover-Branches).
The defect
#typeinside a string literal is not the stringify operator — it is the two characters#andt. Every symbolic object in every.ktestwas named the literal textnon_det_#type: the same name for anint, adoubleand achar.Why it survived every baseline
Nothing read that name. The nondet log carries its own type column, so suites were correct, verdicts were correct, and there was no symptom to notice — the classic shape of a bug that only becomes real when someone finally consumes the thing.
Why it matters now
KLEE writes one
.ktestper explored path, each holding that path's input vector in consumption order:That is exactly the material a Cover-Branches suite is made of, produced by the engine for free and without perturbing the search. But a
.ktestrecords each object's name, size and bytes — not its type, so the name is the only place a type can travel. Without it, four bytes could be anint, afloator half of along, and nothing tells them apart.name: 'non_det_#type' size: 4name: 'non_det_int' size: 4 int: 42name: 'non_det_#type' size: 1name: 'non_det_char' size: 1 int: 122Test
tests/integration/test_ktest_object_names.sh, wired into the regression job. It asserts the pairing of name to size, not just the names — a one-byte object callednon_det_intwould be worse than no name at all.Writing it caught a trap worth recording in the test itself:
klee-lastis a symlink, andfinddoes not descend into a symlinked start point without-L. The first version reported "no ktest files" on a run that had produced three.Verification
Full build: new test 5/5, emission suite still 14/14 (the Cover-Error path is untouched).
Context: this replaces the H1.3 design I had started
I first implemented per-input vectors by having the C runtime write one log per terminating execution. The logic was right — 7/7 in an isolated native unit test — but the approach is not viable, and the measurement is unambiguous:
FAILED✅ correctSUCCEEDED❌ wrongOne extra
fopenper state under KLEE turns a one-second correct run into a timed-out wrong answer. That is corruption, not slowness, so none of it is in this PR. KLEE's own.ktestoutput gives the same vectors at no cost — which is what this fix unblocks.