Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
56d11d1
docs(tacasv3a): design spec -- smart seeds plumbing (AFL++ <-> KLEE)
GuilhermeBn198 Sep 29, 2026
1f68143
docs(tacasv3a): implementation plan
GuilhermeBn198 Sep 29, 2026
fb8248a
feat(tacasv3a): pure seed-store helpers
GuilhermeBn198 Sep 29, 2026
a560d1e
feat(tacasv3a): a seed store that survives the hybrid's phases
GuilhermeBn198 Sep 29, 2026
b662546
feat(tacasv3a): the fuzzer corpus reaches KLEE as typed seeds
GuilhermeBn198 Sep 29, 2026
3a89b46
fix(tacasv3a): bound and phase-gate the replays, clean store, keep pr…
GuilhermeBn198 Sep 29, 2026
3096644
docs(tacas): R15 -- Cover-Error sample, tacasv2 x v15
GuilhermeBn198 Sep 29, 2026
14c8506
docs(tacas): R15 -- Cover-Branches sample
GuilhermeBn198 Sep 29, 2026
1eb8a30
fix(verdict): the program's abort() prunes the path, it does not end …
GuilhermeBn198 Sep 29, 2026
3b156aa
docs(tacas): R18 partial and the 2026-09-29 handoff
GuilhermeBn198 Sep 29, 2026
4206581
docs(tacas): R18 -- the abort fix clears 6 of 8 wrong TRUEs
GuilhermeBn198 Sep 29, 2026
6fa1b24
docs(tacas): drop the stale R18 partial from the handoff
GuilhermeBn198 Sep 29, 2026
4c16013
fix(verdict): KLEE finishing after dropping paths is not a proof
GuilhermeBn198 Sep 29, 2026
0e6e015
docs(tacas): R16 -- the seed exchange covers 148 vs 129
GuilhermeBn198 Sep 29, 2026
7c6f892
fix(verdict): a path an assumption pruned is not a dropped path
GuilhermeBn198 Sep 29, 2026
09d8066
fix(verdict): a KLEE that died, or hit its memory cap, proved nothing
GuilhermeBn198 Sep 29, 2026
93e8de3
feat(slice): slice once per run, and experiment knobs for the slice
GuilhermeBn198 Sep 29, 2026
be6754d
fix(slice): bound the post-slice cleanup like the slicer
GuilhermeBn198 Sep 29, 2026
8cdcb07
perf(slice): scan the IR for names without std::regex
GuilhermeBn198 Sep 30, 2026
c3d2bbe
fix(afl): the fuzzer's input runs out as zeros, not as a replay of it…
GuilhermeBn198 Sep 29, 2026
4d1e91e
feat(hybrid): --alternate-engines, turns that end when an engine stag…
GuilhermeBn198 Sep 29, 2026
2e931ba
docs(schedule): phases 2 and 3 as the TACAS line delivered them
GuilhermeBn198 Sep 29, 2026
902eaf5
fix(hybrid): the review of --alternate-engines
GuilhermeBn198 Sep 29, 2026
e247b64
fix(hybrid): the fuzzer gets all of KLEE's vectors, built once, with …
GuilhermeBn198 Sep 29, 2026
ca996ba
fix(afl): one build budget for the three AFL++ binaries
GuilhermeBn198 Sep 30, 2026
956df7b
fix(memtrack): check memset/memcpy/memmove under LLVM 16's opaque poi…
GuilhermeBn198 Sep 29, 2026
8111145
docs(tacas): handoff after 2d, 3b and the review fixes
GuilhermeBn198 Sep 29, 2026
56e1cb9
fix(afl): a fuzzer binary that fails to link says so, and why
GuilhermeBn198 Sep 29, 2026
b664ae6
feat(seeds): the fuzzer entries that reached new edges go to KLEE first
GuilhermeBn198 Sep 29, 2026
9853308
docs(tacas): handoff -- 3c v1 and R20
GuilhermeBn198 Sep 29, 2026
6e7bfdd
docs(tacas): R16 -- Cover-Branches, the seed exchange 49.7% vs 47.7%
GuilhermeBn198 Sep 29, 2026
caf30b6
docs(tacas): R17 and a partial R19
GuilhermeBn198 Sep 29, 2026
e482c3a
fix(eval): a crash replayed from the fuzzer is not a tool failure
GuilhermeBn198 Sep 29, 2026
e3a2500
fix(memtrack): check the strings a %s or puts will read
GuilhermeBn198 Sep 29, 2026
f18be77
fix(memtrack): the %s checks behind MAP2CHECK_CHECK_CSTRINGS=1
GuilhermeBn198 Sep 30, 2026
d6da7c5
feat(suite): the fuzzer's corpus in the Cover-Branches suite (knob)
GuilhermeBn198 Sep 29, 2026
d89ef22
docs(tacas): handoff -- strings, fuzzer suite, R21/R22
GuilhermeBn198 Sep 29, 2026
fd83c46
docs(tacas): R17 complete, and why the alternation lost eca-* tasks
GuilhermeBn198 Sep 29, 2026
f1f5237
docs(tacas): handoff -- R23 and the alternation fix
GuilhermeBn198 Sep 29, 2026
7636ec5
feat(hybrid): run KLEE's vectors on natively, completed with zeros
GuilhermeBn198 Sep 29, 2026
54c5d58
docs(tacas): handoff -- vector replay and R24
GuilhermeBn198 Sep 29, 2026
65a9286
fix(eval): the programs under test must not inherit the manifest
GuilhermeBn198 Sep 30, 2026
e972dc4
docs(tacas): R19 and R21 results
GuilhermeBn198 Sep 30, 2026
4f02611
docs(tacas): handoff -- night state
GuilhermeBn198 Sep 30, 2026
49efa33
fix(frontend): an unreadable input program is an error that says so
GuilhermeBn198 Sep 30, 2026
c312c7a
docs(tacas): handoff -- R22 rerun
GuilhermeBn198 Sep 30, 2026
e265f5e
docs(tacas): handoff -- the Crab-LLVM to Clam gap is the next front
GuilhermeBn198 Sep 30, 2026
4fabab4
docs(tacas): handoff -- first probe of the old crab-llvm engine vs Clam
GuilhermeBn198 Sep 30, 2026
dff7246
docs(tacas): INV-1 -- the old crab-llvm vs Clam, and where the gain r…
GuilhermeBn198 Sep 30, 2026
35f32c9
feat(invariants): Clam profiles, an invariant count, and SSA pre-opti…
GuilhermeBn198 Sep 30, 2026
51dc7f0
docs(tacas): handoff -- INV-1 and R25
GuilhermeBn198 Sep 30, 2026
811fc13
docs(tacas): first results of R20, R22, R23 and R24
GuilhermeBn198 Sep 30, 2026
fdd6bd9
chore(release): Map2Check 9.0.0
GuilhermeBn198 Sep 30, 2026
a17aca5
docs(tacas): R21 CASTLE, and the scheduler's collapsed tab fields
GuilhermeBn198 Sep 30, 2026
c3ebf40
docs(tacas): R20 and R23 seeds complete
GuilhermeBn198 Sep 30, 2026
a1506bc
fix(invariants): bound Clam like the slicer
GuilhermeBn198 Sep 30, 2026
e80a478
docs(tacas): overnight results -- R22, R23, R24, R25
GuilhermeBn198 Sep 30, 2026
e8fe0da
feat(hybrid): the alternation and the fuzzer suite become the 9.0 def…
GuilhermeBn198 Sep 30, 2026
3476ff7
docs: campaign plan on the 2765-task Cover-Branches set, witness stud…
GuilhermeBn198 Sep 30, 2026
529d43a
docs(tacas): handoff -- the 9.0 campaign is chained after R26
GuilhermeBn198 Sep 30, 2026
b1f1870
docs(tacas): R26 complete and the first batches of the 9.0 campaign
GuilhermeBn198 Oct 1, 2026
a65377e
docs(tacas): handoff -- resuming the 9.0 campaign after a power loss
GuilhermeBn198 Oct 2, 2026
76eedfc
fix(hybrid): link the fuzzer with libm, and kill a KLEE an interrupt …
GuilhermeBn198 Oct 3, 2026
43ee04e
docs(tacas): the two defects of the 9.0 campaign and the build swap
GuilhermeBn198 Oct 3, 2026
c8a4f6a
docs(tacas): 9.0 campaign -- Cover-Error, CASTLE and Juliet a/b/d com…
GuilhermeBn198 Oct 3, 2026
60597d7
docs(tacas): handoff -- Cover-Branches shard 5 split in four
GuilhermeBn198 Oct 4, 2026
b51ee3d
docs(backlog): campaign sample vs the official Test-Comp 2026 set; as…
GuilhermeBn198 Oct 4, 2026
7c08270
docs(tacas): 9.0 campaign -- Cover-Branches complete
GuilhermeBn198 Oct 5, 2026
e4e0b74
fix(klee): end an assumption-pruned path with _exit, so KLEE writes i…
GuilhermeBn198 Oct 5, 2026
6848d23
fix(suite): assumption-pruned paths give way to deeper ones in a capp…
GuilhermeBn198 Oct 5, 2026
53a43e8
docs(tacas): assume-pruned paths fix and its rerun
GuilhermeBn198 Oct 6, 2026
5d57fff
chore: untrack local documents committed by mistake in 53a43e821
GuilhermeBn198 Oct 6, 2026
e5a86a7
test(testcomp): validate suites with the five Test-Comp 2026 validato…
GuilhermeBn198 Oct 7, 2026
cb99fe7
test(testcomp): replace the validator scripts with a generate/validat…
GuilhermeBn198 Oct 7, 2026
866d350
test(testcomp): bundle the AFL++ toolchain in a self-contained archive
GuilhermeBn198 Oct 7, 2026
44d2160
fix(testcomp): stage each task's own property files
GuilhermeBn198 Oct 7, 2026
4a2dcca
fix(testcomp): drop each validator's scratch so a full corpus fits on…
GuilhermeBn198 Oct 12, 2026
14766fd
test(testcomp): let TESTCOV_S=0 skip the internal TestCov
GuilhermeBn198 Oct 12, 2026
0149e22
test(testcomp): official corpora with proportional stratification
GuilhermeBn198 Oct 12, 2026
3e0dcef
docs(tacas): official Test-Comp environment and the V99 campaign handoff
GuilhermeBn198 Oct 12, 2026
05a1573
fix(nondet): model float, longlong, ulonglong, u8/u16/u32 and nondet_…
GuilhermeBn198 Oct 12, 2026
5146272
docs(tacas): issue #74 fix, probe and the V99 re-run of the affected …
GuilhermeBn198 Oct 12, 2026
a2a164a
docs(tacas): issue #74 re-run report and the KLEE log explosion (#75)
GuilhermeBn198 Oct 12, 2026
4855cbb
docs(changelog): unmodeled nondet types (#74) and the official Test-C…
GuilhermeBn198 Oct 13, 2026
8da5794
fix(runtime): stop the KLEE nondet log from exploding the error path
GuilhermeBn198 Oct 13, 2026
b4bda90
feat(klee): MAP2CHECK_KLEE_MAX_MEMORY knob; changelog for #75
GuilhermeBn198 Oct 13, 2026
a2fbc56
docs(tacas): issue #75 probes and the 40-task CE sample on install_v99c
GuilhermeBn198 Oct 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,96 @@ The format loosely follows [Keep a Changelog](https://keepachangelog.com/en/1.0.

## [Unreleased]

## [9.0.0] - Unreleased

A major version: the fuzzing engine changed (LibFuzzer → AFL++ 4.40c), the
`--nondet-generator` values changed (`fuzzer` → `afl`), and several verdicts
change meaning -- a KLEE run that did not explore every path is no longer
TRUE, and the program's own `abort()` prunes a path instead of ending the
search. Measured against the v15 baseline in `docs/reports/tacas-experiment-log.md`.

### Changed (breaking)

- **The default hybrid is the alternation** (`--alternate-engines`, with seed
exchange) whenever `--timeout` is given. `--fixed-hybrid` keeps the 8.x
schedule. Measured on the 213-task Cover-Error sample: 157 covered, against
128 for the 8.x hybrid. On Cover-Branches: 53.5% against 46.5% (R26).
- The fuzzer's corpus is part of Cover-Branches suites by default
(`MAP2CHECK_FUZZER_SUITE=0` turns it off).

- Fuzzing engine: LibFuzzer replaced by AFL++ 4.40c (persistent mode, PCGUARD,
CmpLog). `--nondet-generator fuzzer` is now `--nondet-generator afl`.
- Verdicts: TRUE only from an exhaustive KLEE exploration. KLEE exiting 0 after
its timer, after concretizing a symbolic input (floats), after killing
states (`*.err`, `*.early`), near its memory cap, or after crashing is now
UNKNOWN, never TRUE.
- The program's own `abort()` (inline or through `assume_abort_if_not`) prunes
the path (`map2check_assume(0)`, `klee_silent_exit` under KLEE) instead of
stopping KLEE's search.

### Added

- `--slice` for every property (reachability, assert, memtrack, memcleanup,
overflow) through sbt-slicer, preserving the nondet read order; the slice is
computed once per run (`<hash>.slice/`), a slicer failure is remembered, and
the criteria are collected without regex. Experiment knobs:
`MAP2CHECK_SLICE_CLEANUP=light|o2`, `MAP2CHECK_SLICER_FLAGS`.
- `--seed-exchange`: the engines hand each other input vectors through a
persistent store (`<hash>.seeds/`) -- the fuzzer queue, replayed through the
witness binary into typed `.ktest` seeds for KLEE (ranked: new-edge entries
first), and KLEE's vectors back to the fuzzer.
- `--alternate-engines`: AFL++ and KLEE take turns, each ending when its engine
stagnates (`AFL_EXIT_ON_TIME`; KLEE's covered instructions from `run.stats`,
SQLite optional), with windows and patience doubling every round.
- KLEE's vectors, completed with zeros past their end, are run natively
through the witness after every KLEE phase that found nothing.
- AFL++ binaries built once per run (`<hash>.build/`), within one build budget.
- The fuzzer's corpus contributes Cover-Branches test cases (up to half the
suite, deduplicated).
- `--add-invariants` profiles (`MAP2CHECK_CLAM_PROFILE=default|memory|none`),
the number of invariants inserted in the log, and a fallback when Clam
fails. `MAP2CHECK_PREOPT=ssa` (reachability and assert): the module in SSA
form before instrumentation.
- `MAP2CHECK_CHECK_CSTRINGS=1`: the strings a `%s` or `puts` reads are checked.
- `MAP2CHECK_KLEE_MAX_MEMORY` (MB): KLEE's memory cap, default unchanged.
- Official Test-Comp 2026 validation (`tests/testcomp/official/run.sh`):
suites are generated and validated under BenchExec with the competition's
validators (TestCov gcc/clang unformatted, TestCoCa clang formatted), each
task with its own property files; the corpora are drawn from the official
task set with proportional stratification (`build_corpus_official.py`).

### Fixed

- Nondet types the instrumentation did not model (#74): `float`, `longlong`,
`ulonglong`, `u8`/`u16`/`u32` and `__VERIFIER_nondet_memory` were external
calls, so KLEE killed every state and the task was UNKNOWN by construction.
Each now has a KLEE and an AFL++ generator of the right width, a full-width
log entry (64-bit and float values no longer go through the `int` logger;
floats print as `%.9g`) and a ktest decoding; `nondet_memory` yields one
`uchar` input per byte, as the validators' harness reads it. Floats in the
official Cover-Error corpus: 11/33 → 33/33, every suite confirmed by all
three validators.
- The KLEE runtime wrote its nondet log with the values still symbolic, and
uClibc's `vfprintf` forked on every digit (#75): a straight-line program
with ten nondet reads reached its target and never finished. The values are
now pinned to one model before printing, the rows are formatted without
`printf` (byte-identical output), a log past 1,024 rows is left to the
violating `.ktest`, and the log container grows geometrically. Ten reads:
UNKNOWN → FAILED in 1 s; 10,000 reads: UNKNOWN → FAILED in 31 s.
- The AFL++ generator replayed its input from the start past its end: a
`while (__VERIFIER_nondet_int())` loop never ended and afl-fuzz aborted in
its dry run. Reads past the end are now zero.
- MemoryTrackPass matched memory intrinsics by their LLVM 6 names:
`memset/memcpy/memmove` were never checked under LLVM 16.
- A fuzzer binary that fails to link is reported with its cause instead of as
a build timeout; an unreadable input program is reported as such.
- The Cover-Branches suite: the 50-case cap and duplicates count across
phases, and a run starts from an empty suite.
- Evaluation harnesses: children no longer inherit the manifest's descriptor
(the program under test could move the loop's offset); a crash replayed from
the fuzzer is not a tool failure.


### Changed

- Replaced LibFuzzer with AFL++ 4.40c (persistent, PCGUARD) as the fuzzing engine.
Expand Down
20 changes: 20 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ the driver is present at `$CLAM_DIR/bin/clam.py`. It is opt-in because an unsoun
invariant produces a wrong TRUE rather than an error — see
[the dependency review](docs/reports/2026-08-16-crabllvm-review.md).

Status of `--add-invariants` (2026-09-30): **optional and under study**.
- The crab-llvm invariants of the SV-COMP 2019/2020 builds were emitted as `llvm.assume`,
which neither `NonDetPass` nor KLEE consumes.
- On the current hybrid, Clam's profiles (`MAP2CHECK_CLAM_PROFILE=default|memory|none`)
have shown no gain so far.
- A dedicated study round is pending; see `docs/reports/tacas-experiment-log.md` (INV-1,
R25) and `docs/backlog.md`.

Enabling sanitizers switches from static to shared linking and enables `-fsanitize=address,undefined -fno-omit-frame-pointer -g`.

### Run unit tests
Expand Down Expand Up @@ -107,6 +115,18 @@ Entry point: `map2check.cpp` → `main()`. Parses CLI options (via Boost.Program
5. `executeAnalysis()` — run the instrumented binary; collect results
6. Witness/counterexample generation in [counter_example/](modules/frontend/counter_example/) and [witness/](modules/frontend/witness/)

With no `--nondet-generator`, `main()` runs the hybrid, and each phase builds a new `Caller`:
- **Alternating (the default since 9.0, needs `--timeout`):** AFL++ and KLEE take turns
(`alternateEngines()`), each stopped on stagnation.
- **Fixed (`--fixed-hybrid`):** the 8.x schedule.

State that must survive the phases lives beside the scratch directory, not inside it:
`<hash>.seeds/` (seed store), `<hash>.slice/` (slice cache) and `<hash>.build/` (AFL++
binaries).

A TRUE verdict requires an exhaustive KLEE run: see `kleeDroppedPaths()` in
`modules/frontend/test_suite/ktest_reader.cpp`.

**Verification modes** (enum `Map2CheckMode`): `MEMTRACK_MODE`, `REACHABILITY_MODE`, `OVERFLOW_MODE`, `ASSERT_MODE`, `MEMCLEANUP_MODE`.

To add a new analysis: extend `callPass()` in [caller.cpp](modules/frontend/caller.cpp) and add a CLI option in [map2check.cpp](modules/frontend/map2check.cpp).
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.20)
project(Map2Check VERSION 8.0.0 LANGUAGES C CXX)
project(Map2Check VERSION 9.0.0 LANGUAGES C CXX)

option(BUILD_DOC "Build documentation" OFF)
option(SKIP_AFL_PLUS_PLUS "Don't use AFL++" OFF)
Expand Down
8 changes: 6 additions & 2 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,12 @@ RUN printf 'int main(void){return 0;}\n' > /tmp/aflcheck.c && \
# libzstd-dev is required, not optional: LLVM 16 from apt.llvm.org exports
# zstd::libzstd_shared, so Clam's find_package(LLVM) fails outright without it.
# libflint-dev is NOT needed -- that belongs to the PPLite domain, and no
# optional domain is enabled here: --crab-track=num uses the default interval
# domain, so LDD, Apron, Elina and PPLite would all be dead weight.
# optional domain is enabled here. Clam's default domain is zones (a DBM,
# built into Crab), the same default the crab-llvm of the SV-COMP builds had;
# what LDD, Apron, Elina and PPLite would add is boxes, oct and pk, which the
# old engine shipped (Apron) and --add-invariants never selected. Whether they
# are worth building is part of the pending --add-invariants study
# (docs/backlog.md).
RUN apt-get update && apt-get install -y \
libmpfr-dev \
libzstd-dev \
Expand Down
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,78 @@ $ ./map2check --help

When you use a LLVM bytecode as input for the tool, be sure to add `-g` flag when generating the file, it is not required, but map2check will provide better info (like line numbers).

#### Engines, test suites and analysis options (9.0)

<p align="justify">
Map2Check generates inputs with two engines: the <b>AFL++ 4.40c</b> fuzzer (persistent mode, PCGUARD,
CmpLog) and the <b>KLEE 3.1</b> symbolic executor. By default, and whenever <code>--timeout</code> is
given, the two run as an <b>alternating hybrid</b>. They take turns, each turn ends once its engine stops
finding new coverage, and every round doubles the turn's window. The engines also hand each other
input vectors.
</p>

| Option | Effect |
|---|---|
| *(default, with `--timeout`)* | Alternating hybrid with seed exchange. Same as `--alternate-engines`. |
| `--fixed-hybrid` | The 8.x schedule: the fuzzer for 0.2 of the budget, then KLEE. |
| `--fixed-hybrid --seed-exchange` | The 8.x schedule with seed exchange (0.2 / 0.6 / 0.2, with a last fuzzer phase). |
| `--nondet-generator afl` / `symex` | One engine only: AFL++ or KLEE. In 8.x the fuzzer value was `fuzzer`. |
| `--slice` | Slice the program before the analysis (sbt-slicer), for reachability, `--check-asserts`, `--memtrack`, `--memcleanup-property` and `--check-overflow`. |
| `--generate-test-suite` | Emit a Test-Comp test suite (`test-suite/`). |
| `--generate-test-suite --cover-branches` | Emit a Cover-Branches suite, up to 50 cases, from KLEE's paths and the fuzzer's corpus. |
| `--add-invariants` | Optional, and off in the default build. See below. |

<p align="justify">
<b>Verdicts.</b> A TRUE verdict needs KLEE to have explored every path. Map2Check reports
<code>UNKNOWN</code> instead when KLEE stopped early for any reason:
</p>

- its timer ran out;
- it concretized a symbolic input (for example a floating-point value);
- it killed states;
- it hit its memory cap;
- it crashed.

<p align="justify">
A call to <code>abort()</code> in the program under analysis is treated the way SV-COMP treats it: as
an assumption. It prunes that path and does not end the search.
</p>

<p align="justify">
<b>Environment knobs.</b> These exist for experiments; the defaults are the measured choices.
</p>

| Variable | Default | Meaning |
|---|---|---|
| `MAP2CHECK_FUZZER_SUITE` | on | `0` leaves the fuzzer's corpus out of Cover-Branches suites. |
| `MAP2CHECK_SLICE_CLEANUP` | none | `light` or `o2`: an opt pipeline over the fresh slice. Measured neutral. |
| `MAP2CHECK_SLICER_FLAGS` | — | Extra sbt-slicer flags, e.g. `--cda=ntscd`. Measured neutral. |
| `MAP2CHECK_PREOPT` | off | `ssa`: mem2reg + simplifycfg before instrumentation, for reachability and assert only. Measured neutral in the hybrid. |
| `MAP2CHECK_CHECK_CSTRINGS` | off | `1`: check the strings a `%s` or `puts` reads. It fixes one wrong TRUE on Juliet, but produces false positives on memory the runtime does not track (CASTLE: FP from 1 to 4). |
| `MAP2CHECK_CLAM_PROFILE` | default | `memory` or `none`: see `--add-invariants`. |
| `MAP2CHECK_KLEE_MAX_MEMORY` | KLEE's 2000 | KLEE's `--max-memory`, in MB. Raising it to 6000 changed nothing on Intel-TDX (0/8 either way). |

<p align="justify">
<b><code>--add-invariants</code></b> inserts abstract-interpretation invariants computed by
<a href="https://github.com/seahorn/clam">Clam</a> (formerly crab-llvm). The build must be configured
with <code>-DENABLE_CLAM=ON</code> and Clam installed at <code>$CLAM_DIR</code>; otherwise the option is
refused with exit code 3.
</p>

<p align="justify">
The option stays <b>optional</b> while it is under study. So far:
</p>

- In the SV-COMP 2019/2020 builds, the invariants reached neither the instrumentation nor KLEE: they were emitted as `llvm.assume`.
- On the current hybrid, the first measurement found no gain.
- Clam is bounded to 0.2 of the budget and falls back to the plain compile when it fails.
- A dedicated study round is planned. See `docs/reports/tacas-experiment-log.md` (INV-1, R25).

<p align="justify">
Measurements of every option above are recorded in
<a href="docs/reports/tacas-experiment-log.md">docs/reports/tacas-experiment-log.md</a>.
</p>

___

#### Verifying WebAssembly (WASM) binaries
Expand Down
106 changes: 106 additions & 0 deletions docs/backlog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Backlog

Itens decididos como "não agora", cada um com o motivo e onde está a evidência.

## `--add-invariants`: rodada dedicada de estudo

- **Estado:** a opção é opcional e fica desligada no build padrão (`-DENABLE_CLAM=OFF`).
O usuário ainda desconfia da conclusão "não ajuda".
- **O que já se sabe** (log de experimentos, INV-1 e R25):
- Na v7.3 os invariantes saíam como `llvm.assume` e ficavam inertes. Até out/2018 eles
chegavam ao KLEE (`--crab-track=arr --crab-add-invariants=after-load`).
- Numa sonda só com o KLEE, o ganho veio do pré-processamento em SSA, não dos
invariantes.
- No híbrido (71 tarefas de Cover-Error e 50 de MemSafety), os perfis do Clam não
ganharam e o MemSafety teve mais FALSE errados (4 contra 2). Parte dos ERROR vinha do
Clam sem limite de tempo, já corrigido.
- **O que a rodada precisa responder:**
- efeito só com o KLEE (`--nondet-generator symex`), onde os invariantes têm mais
chance de pesar, separado do efeito no híbrido;
- por que os FALSE errados de MemSafety aumentam: um invariante incorreto ou o
caminho de compilação do Clam (`-m 64`, pré-processamento próprio);
- os domínios que o motor antigo tinha e o nosso Clam não compila: Apron (`oct`,
`pk`) e LDD (`boxes`);
- o gate: nenhuma detecção perdida no CASTLE e numa amostra do Juliet.

## Limite de memória do KLEE nas rodadas paralelas

- **Sintoma:** com 8 contêineres, e às vezes com 4, a memória da máquina (23 GB) chega a
ficar crítica. O KLEE usa até `--max-memory` = 2000 MB por processo. Um KLEE morto
pelo OOM killer sai como "did not finish its run", ou seja UNKNOWN, e contamina a
medição. Os vigias do Claude Code também foram encerrados por isso.
- **Opções:**
- passar `--max-memory` por execução, proporcional às vagas;
- limitar a memória por contêiner (`docker run --memory`);
- registrar no resultado quando o KLEE morreu por falta de memória, para descartar e
repetir essas tarefas.
- **Por que não agora:** não afeta o produto (uma execução de competição tem uma tarefa
por máquina), só a infraestrutura de medição.

## Harness de campanha fora do repositório

- Os escalonadores das rodadas R19 a R26 vivem em `../tacas-results/` e não são
versionados, por decisão: campanhas não pertencem ao repositório do produto.
- Dois defeitos deles contaminaram rodadas, e quem escrever o próximo precisa evitá-los:
- `IFS=$'\t' read` junta tabs consecutivos, e uma coluna vazia desloca as seguintes;
usar `-` nos campos vazios;
- os filhos herdavam o manifest pelo descritor 3. Corrigido no harness do repositório
(`3<&-`).

## Validação de witness (fase D, item 4.2): estudo, a discutir com o orientador

- **Estado atual:**
- O Map2Check gera só witness **GraphML** (`--generate-witness`, formato 1.0), gravado
em `../witness.graphml` a partir do scratch.
- Não gera o formato **2.0 (YAML)**, adotado pelo SV-COMP. É preciso confirmar no
call do ano quais formatos ainda são aceitos.
- Nenhum teste valida os witnesses gerados.
- A Test-Comp não usa witness; a suíte é validada pelo TestCov. O item só pesa para o
SV-COMP.
- **Caminho barato já esboçado (validação por execução):** compilar o programa original
(com ASan, para as propriedades de memória), alimentá-lo com o vetor que viola a
propriedade (que o Map2Check já emite na suíte de Cover-Error) e confirmar o
`reach_error` ou o crash. É parecido com o que o TestCov faz e com os validadores por
execução do SV-COMP.
- **Não começar a implementação** antes da conversa com o orientador: definir se o
objetivo é o formato 2.0, a validação por execução, os dois ou nenhum.

## `--debug` com a alternância dá segfault

- Achado em 2026-10-02: `map2check --debug --check-overflow --timeout 60` sobre um caso do
Juliet (CWE190 `char_fscanf_square_01`, good) termina com segfault depois das fases. O
scratch aparece aninhado (`<hash>.map2check/<hash>.map2check-…`). Sem `--debug` a
execução termina normalmente. Só afeta a depuração.

## Recorte da campanha × conjunto oficial da Test-Comp 2026

- **Contexto:** a campanha da 9.0 usa o recorte da v15 (`cover-error-q400.tsv`, 1 087
tarefas; `cover-branches-q400.tsv`, 2 765). A Test-Comp 2026 tem 1 895 tarefas em
C.Cover-Error e 14 322 em C.Cover-Branches
([resultados](https://test-comp.sosy-lab.org/2026/results/results-verified/)).
- **Diferença de composição (Cover-Error):**
- faltam no recorte as subcategorias novas: SoftwareSystems-Intel-TDX-Module (658),
Hardware (497), coreutils (29) e BusyBox, OpenBSD e Linux64 (7);
- o ECA pesa 400 tarefas no recorte, contra 18 no conjunto oficial;
- as subcategorias clássicas (ProductLines, Loops, Sequentialized, Arrays, XCSP,
Floats, BitVectors, ControlFlow, Recursive) praticamente coincidem.
- **Consequência:** os 722 pontos de Cover-Error da 9.0 valem só para a comparação
pareada com a v15. Não estimam a pontuação oficial, que é normalizada por subcategoria.
- **TODO:** montar um manifest com as subcategorias ausentes (Hardware, Intel-TDX,
coreutils) e rodar uma amostra delas depois da campanha de Cover-Branches.

## ~~Caminhos podados pelo assume no KLEE não geram teste~~ (resolvido)

- **Sintoma:** na campanha da 9.0, 29 tarefas de Cover-Branches (25 XCSP, 4 Floats)
terminam com zero testes. O XCSP cai de 76,2% para 58,1% de cobertura média, o que
custa cerca de 1,7 p.p. na média geral.
- **Causa:** o `nondet_assume` sob o KLEE poda com `klee_silent_exit`, que não grava
`.ktest`. Quando todos os caminhos são podados, a suíte sai vazia.
- **Correção proposta:** podar com `_exit(0)`. O caminho termina normalmente e grava o
teste, sem rodar os handlers de saída.
- **Validação necessária:**
- os testes de integração;
- o §29 (`safe.c`) continua dando TRUE;
- o memtrack com `sleep-3` não ganha falso positivo.
- Depois disso, repetir só as tarefas que ficaram com zero testes.
- **Resolvido** em `e4e0b74c7` e `6848d2395` (ver o log de experimentos, 2026-10-05/06).
Loading
Loading