diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index bc0d98e..9545d5d 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -38,8 +38,10 @@ condition checkers behind the `VccChecker` trait; a checker returns `Safe` or a violated property, and the FFI layer records violations in the shared state. `output` serializes that state as the JSON report written to stdout. -The optional `libfuzzer` feature provides the LibFuzzer callback and -nondeterministic input generators in `nondet/libfuzzer.rs`. Cargo features and +Since 2.0.0 (D6) the C ABI is the Map2Check runtime ABI only +(`scripts/check-export-set.sh`); the generated IR modules of +`scripts/runtime-ir/gen_ll.py` define the other C names (modes, generators, +wasm stubs) and forward to the `__m2c_rt_*` entry points. Cargo features and the native/bitcode build setup are declared in `Cargo.toml` and `Dockerfile`; consult those files before changing how analysis modes are linked. @@ -62,3 +64,31 @@ consult those files before changing how analysis modes are linked. - `CLAUDE.md` describes the former C/CMake implementation. For this Rust checkout, use `README.md`, `Cargo.toml`, `.github/workflows/CI.yml`, and the current Rust sources for build commands and behavior. + +## Regras obrigatórias + +1. **GitHub CLI e ações remotas somente sob solicitação explícita.** + Não execute, sem um pedido explícito do usuário na mensagem atual, comandos `gh` que + criem, alterem ou encerrem algo no GitHub: `gh pr create/merge/close/comment/review`, + `gh issue create/close/comment/edit`, `gh release`, `gh repo` e similares. O mesmo vale + para `git push`, criação de tags remotas e alteração de branches remotos. Comandos apenas + de leitura (`gh pr view`, `gh issue list`) são permitidos. Na dúvida, pergunte antes. + +2. **Escopo restrito a este repositório.** + Todas as modificações de código, testes, configurações e documentação devem ser feitas + somente neste repositório. Nunca altere, faça commit ou abra PR/issue no repositório do + Map2Check, nem em qualquer outro repositório. + +3. **Map2Check para testes de integração: cópia local e sem versionamento.** + - Baixe o Map2Check sempre do branch `develop`, para o diretório `vendor/map2check/` (ou + outro que o usuário indicar), por meio de + `git clone --depth 1 --branch develop vendor/map2check`. Não use tags nem tarballs + de release. + - Logo após o download, remova o versionamento com `rm -rf vendor/map2check/.git`, para + que nenhum comando git atue sobre o repositório original. + - Garanta que `vendor/map2check/` esteja no `.gitignore` deste repositório, para que a + cópia não seja commitada aqui. + - Registre o commit do `develop` baixado (por exemplo, em `vendor/MAP2CHECK_VERSION`) para + tornar os testes reproduzíveis. + - Não edite os fontes baixados. Se um teste exigir mudanças no Map2Check, pare e informe + o usuário. diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 664f23e..1e6c819 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -5,6 +5,7 @@ on: branches: [ "main", "develop", "release/**" ] pull_request: branches: [ "main", "develop", "release/**" ] + workflow_dispatch: jobs: build_and_test: @@ -46,12 +47,146 @@ jobs: - name: Testar script de proteção de branches run: scripts/test-branch-protection.sh + - name: Testar download do Map2Check + run: scripts/test-fetch-map2check.sh + - name: Testar guard de artefatos da era C run: scripts/test-check-no-c-era-artifacts.sh - name: Verificar ausência de artefatos da era C run: scripts/check-no-c-era-artifacts.sh + - name: Testar guarda de cobertura da ABI do runtime C + run: scripts/test-runtime-abi-coverage.sh + + - name: Conferir assinaturas contra os headers do runtime C + run: | + test -d vendor/map2check/modules/backend/library/header || scripts/fetch-map2check.sh + tests/abi/check-signatures.sh vendor/map2check/modules/backend/library/header + + # Gerador dos módulos IR do layout A-fwd (D7). O teste de paridade de + # ABI compila cada assinatura com o clang instalado acima. + - name: Testar o gerador de IR do runtime (scripts/runtime-ir/gen_ll.py) + run: python3 -I -m unittest discover -s scripts/runtime-ir + + # Gera target/runtime-ir/AnalysisModeOverflow.ll (não versionado), + # confere a sintaxe com clang -c -x ir e liga um driver C ao .ll e à + # staticlib: cada encaminhador precisa chegar ao __m2c_rt_overflow_* + # do núcleo e escrever os arquivos de propriedade do C. + - name: Gerar, conferir e ligar o módulo IR AnalysisModeOverflow + run: | + cargo build --release --all-features + scripts/runtime-ir/test-overflow-link.sh + + # Idem para AnalysisModeAssert.ll e AnalysisModeNone.ll (Task 2.4): + # map2check_assert, analysis_* e map2check_target_function chegam ao + # núcleo; __VERIFIER_assert é vazio no IR, como no C. + - name: Gerar, conferir e ligar os módulos IR AnalysisModeAssert e AnalysisModeNone + run: | + cargo build --release --all-features + scripts/runtime-ir/test-assert-none-link.sh + + # Idem para AnalysisModeMemtrack.ll (Task 2.5), ligado com os módulos + # vazios de container e de log nos dois conjuntos (Array e --btree, D3): + # os cenários golden escrevem os arquivos de tests/legacy_golden/ byte a + # byte. + - name: Gerar, conferir e ligar o módulo IR AnalysisModeMemtrack + run: | + cargo build --release --all-features + scripts/runtime-ir/test-memtrack-link.sh + + # Idem para AnalysisModeMemcleanup.ll (Task 2.6), nos dois conjuntos de + # container; sem map2check_wasm_*. Cenários: vazamento no exit(0), os + # caminhos vazios do .c e o SD-23. + - name: Gerar, conferir e ligar o módulo IR AnalysisModeMemcleanup + run: | + cargo build --release --all-features + scripts/runtime-ir/test-memcleanup-link.sh + + # Gerador KLEE (Task 2.7): gera NonDetGeneratorKlee.ll, confere a + # sintaxe com clang -c -x ir, que não há stub nem intrínseco além dos do + # NonDetGeneratorKlee.c, e que a staticlib não define nenhum dos seus + # nomes C. O módulo chama intrínsecos do KLEE e não liga nativamente: + # roda sob o KLEE 3.1 em scripts/runtime-ir/test-klee-generator.sh, que + # precisa da imagem do Map2Check e não roda aqui. + - name: Gerar e conferir o módulo IR NonDetGeneratorKlee + run: | + cargo build --release --all-features + scripts/runtime-ir/test-klee-ir.sh + + # Gerador AFL++ (Task 2.8): gera NonDetGeneratorAFL.ll, confere a + # sintaxe com clang -c -x ir, que não há stub nem nome do AFL++ além das + # expansões de __AFL_FUZZ_INIT/__AFL_LOOP do NonDetGeneratorAFL.c, e que + # a staticlib não define nenhum dos seus nomes C. O módulo precisa do + # afl-compiler-rt e não liga aqui: roda sob o AFL++ 4.40c em + # scripts/runtime-ir/test-afl-generator.sh, que precisa da imagem do + # Map2Check e não roda aqui. + - name: Gerar e conferir o módulo IR NonDetGeneratorAFL + run: | + cargo build --release --all-features + scripts/runtime-ir/test-afl-ir.sh + + # Stubs wasm (Task 2.9): gera WasmRuntimeStubs.ll, confere a sintaxe e + # liga um driver C que chama os wasm_rt_* como a saída do wasm2c; cada + # nome C chega ao seu __m2c_rt_wasm_*. A staticlib não define nenhum + # deles (sem exceção desde a Task 2.10). + - name: Gerar, conferir e ligar o módulo IR WasmRuntimeStubs + run: | + cargo build --release --all-features + scripts/runtime-ir/test-wasm-link.sh + + # Sobreposição de instalação (Task 3.1): uma cópia de uma instalação do + # Map2Check em que só os 21 lib/*.bc mudam; bin/, plugins e symlinks + # ficam iguais. Aqui só as fixtures (a instalação real do C não existe + # no runner). + - name: Teste da sobreposição de instalação (fixtures) + run: scripts/it/test-overlay-install.sh + + # Nenhuma relocação da staticlib pode ter como alvo um nome que um + # módulo IR define (uma chamada interna a um export #[no_mangle] seria + # ligada à definição do IR, que encaminha de volta: recursão infinita). + - name: Guarda de chamadas a nomes definidos pelos módulos IR + run: | + cargo build --release --all-features + scripts/runtime-ir/test-check-no-ir-name-calls.sh + scripts/runtime-ir/check-no-ir-name-calls.sh + + # map2check_assume* (Task 2.10): a cola IR do Map2CheckFunctions.bc + # (Map2CheckFunctions-glue.ll) chama o nondet_assume do gerador. + - name: Gerar, conferir e ligar a cola IR (map2check_assume*) + run: | + cargo build --release --all-features + scripts/runtime-ir/test-assume-link.sh + + # D6 (Task 2.10): o conjunto exportado da staticlib é exatamente o ABI C + # do runtime que os módulos núcleo têm, mais os __m2c_rt_*. Nas duas + # bibliotecas: sem features e com --all-features. + - name: Guarda do conjunto exportado (ABI C do runtime) + run: | + scripts/test-check-export-set.sh + cargo build --release + scripts/check-export-set.sh --without-wasm + cargo build --release --all-features + scripts/check-export-set.sh + + # Os símbolos C dos modos, dos geradores KLEE e AFL++, dos stubs wasm e + # da cola (map2check_assume*) são definidos só pelos módulos IR: o .ll + # entra como artefato extra da cobertura. Usa os .ll dos passos + # anteriores. pending.txt fica vazio (Task 2.10). + - name: Conferir cobertura da ABI do runtime C (pending.txt) + run: | + CARGO_TARGET_DIR=target/coverage-lib cargo build --release --features wasm + scripts/runtime-abi-coverage.sh --lib target/coverage-lib/release/libmap2check.a \ + target/runtime-ir/AnalysisModeOverflow.ll \ + target/runtime-ir/AnalysisModeAssert.ll target/runtime-ir/AnalysisModeNone.ll \ + target/runtime-ir/AnalysisModeMemtrack.ll target/runtime-ir/AnalysisModeMemcleanup.ll \ + target/runtime-ir/NonDetGeneratorKlee.ll target/runtime-ir/NonDetGeneratorAFL.ll \ + target/runtime-ir/WasmRuntimeStubs.ll target/runtime-ir/Map2CheckFunctions-glue.ll + + # Os testes da lib só com a feature wasm, o build do núcleo do drop-in. + - name: Rodar os testes da lib com a feature wasm + run: cargo test --lib --features wasm + - name: Instalar cargo-tarpaulin uses: taiki-e/install-action@v2 with: @@ -82,30 +217,33 @@ jobs: # stopgap so this pre-existing flake doesn't make CI red on unrelated # PRs — see the retry: a *new* memory-safety bug would fail every # attempt, not just some. + # O harness é ligado como o Map2Check liga um programa: a staticlib, a + # cola IR (map2check_assume*) e o gerador AFL++ (NonDetGeneratorAFL.ll), + # cujo main roda uma entrada por iteração do __AFL_LOOP (Task 2.10). - name: Compilar e executar harness FFI com ASan e UBSan run: | cargo build --release --all-features - clang -fsanitize=address,undefined -fno-omit-frame-pointer \ - tests/ffi_harness.c target/release/libmap2check.a \ - -ldl -lpthread -lm -o target/ffi_harness + scripts/build-c-abi-program.sh -o target/ffi_harness \ + -m Map2CheckFunctions-glue -m NonDetGeneratorAFL tests/ffi_harness.c \ + -- -fsanitize=address,undefined -fno-omit-frame-pointer ok=0 for i in 1 2 3 4 5; do if target/ffi_harness; then ok=1; break; fi done [ "$ok" = 1 ] - # The harness rejects inputs with __VERIFIER_assume(0), which unwinds - # through the C frame of its __map2check_main__ (D1 condition 11). - # Optimised builds are where a missing unwind path would show: once - # with -fexceptions, the flag the pipeline must use, and once with - # clang's defaults. + # The harness rejects inputs with map2check_assume(0), whose + # nondet_assume longjmps through the C frame of its __map2check_main__ + # back to the __AFL_LOOP (D7 item 5). Optimised builds are where a + # broken jump would show: once with -fexceptions and once with clang's + # defaults. - name: Compilar e executar harness FFI em -O2 (com e sem -fexceptions) run: | cargo build --release --all-features for flags in "-fexceptions" ""; do - clang -O2 $flags -fsanitize=address,undefined -fno-omit-frame-pointer \ - tests/ffi_harness.c target/release/libmap2check.a \ - -ldl -lpthread -lm -o target/ffi_harness_o2 + scripts/build-c-abi-program.sh -o target/ffi_harness_o2 \ + -m Map2CheckFunctions-glue -m NonDetGeneratorAFL tests/ffi_harness.c \ + -- -O2 $flags -fsanitize=address,undefined -fno-omit-frame-pointer ok=0 for i in 1 2 3 4 5; do if target/ffi_harness_o2; then ok=1; break; fi @@ -113,44 +251,46 @@ jobs: [ "$ok" = 1 ] || { echo "ffi_harness -O2 $flags falhou"; exit 1; } done - - name: Compilar e executar test/codeAPI_by_caller_test.c (API núcleo) + - name: Compilar e executar test/codeAPI_by_caller_test.c (memtrack pelo ABI C) run: | cargo build --release - clang -fsanitize=address,undefined -fno-omit-frame-pointer \ - test/codeAPI_by_caller_test.c target/release/libmap2check.a \ - -ldl -lpthread -lm -o target/codeAPI_test + scripts/build-c-abi-program.sh -o target/codeAPI_test -m AnalysisModeMemtrack \ + test/codeAPI_by_caller_test.c -- -fsanitize=address,undefined -fno-omit-frame-pointer ok=0 for i in 1 2 3 4 5; do if target/codeAPI_test; then ok=1; break; fi done [ "$ok" = 1 ] - - name: Compilar e executar test/codefuzz_by_caller_test.c (feature libfuzzer) + - name: Compilar e executar test/codefuzz_by_caller_test.c (gerador AFL++ pelo ABI C) run: | - cargo build --release --features libfuzzer - clang -fsanitize=address,undefined -fno-omit-frame-pointer \ - test/codefuzz_by_caller_test.c target/release/libmap2check.a \ - -ldl -lpthread -lm -o target/codefuzz_test + cargo build --release + scripts/build-c-abi-program.sh -o target/codefuzz_test \ + -m Map2CheckFunctions-glue -m NonDetGeneratorAFL \ + test/codefuzz_by_caller_test.c -- -fsanitize=address,undefined -fno-omit-frame-pointer ok=0 for i in 1 2 3 4 5; do if target/codefuzz_test; then ok=1; break; fi done [ "$ok" = 1 ] - - name: Compilar e executar test/codeklee_by_caller_test.c (smoke test, sem KLEE real) + - name: Compilar e executar test/codeklee_by_caller_test.c (gerador KLEE pelo ABI C, sem KLEE real) run: | cargo build --release - clang -fsanitize=address,undefined -fno-omit-frame-pointer \ - test/codeklee_by_caller_test.c target/release/libmap2check.a \ - -ldl -lpthread -lm -o target/codeklee_test + scripts/build-c-abi-program.sh -o target/codeklee_test \ + -m Map2CheckFunctions-glue -m NonDetGeneratorKlee -m AnalysisModeAssert \ + test/codeklee_by_caller_test.c -- -fsanitize=address,undefined -fno-omit-frame-pointer ok=0 for i in 1 2 3 4 5; do if target/codeklee_test; then ok=1; break; fi done [ "$ok" = 1 ] - - name: Testar empacotamento de release (header, bibliotecas e docs) - run: scripts/test-package-release.sh + # Modo fixture: 21 arquivos de mentira no lugar dos .bc, sem Docker. O + # conteúdo do pacote, o SHA256SUMS interno e a reprodutibilidade são + # checados aqui; os .bc de verdade, em BC_DIR, no job runtime_matrix. + - name: Testar empacotamento de release (header, bibliotecas, 21 .bc de fixture e docs) + run: FIXTURE_BC=1 scripts/test-package-release.sh - name: Checar segurança de dependências (cargo-deny) uses: EmbarkStudios/cargo-deny-action@v2 @@ -167,14 +307,13 @@ jobs: - name: Build do Dockerfile run: docker build . - # Runtime do gerador `symex` (D1, rota A′; docs/klee-feasibility.md): as - # mesmas fontes compiladas por rustc 1.72.1 (LLVM 16) a partir de - # klee/Cargo.toml e do seu Cargo.lock v3, em bitcode que o llvm-link-16 e o - # KLEE 3.1 leem. O clippy em 1.82 do job acima não vê o que o 1.72 não - # compila (`LazyLock`, `unsafe extern`, API de std posterior a 1.72): este - # job é a guarda da condição 5. O KLEE em si não roda aqui (a imagem com o - # KLEE 3.1 é a do Map2Check); tests/klee_paths.sh roda no CI do Map2Check - # (Task 3.4) e localmente com Docker. + # Núcleo Rust do runtime drop-in em bitcode (D1, rota A′; D7): as mesmas + # fontes compiladas por rustc 1.72.1 (LLVM 16) a partir de klee/Cargo.toml + # e do seu Cargo.lock v3, em bitcode que o llvm-link-16 e o KLEE 3.1 leem. + # O clippy em 1.82 do job acima não vê o que o 1.72 não compila + # (`LazyLock`, `unsafe extern`, API de std posterior a 1.72): este job é a + # guarda da condição 5. O KLEE em si não roda aqui (a imagem com o KLEE 3.1 + # é a do Map2Check). klee_runtime: runs-on: ubuntu-24.04 steps: @@ -187,34 +326,107 @@ jobs: - name: Instalar o rustc 1.72.1 (LLVM 16) com clippy run: rustup toolchain install 1.72.1 --profile minimal --component clippy - # O clippy em 1.82 do job acima não compila nondet/klee.rs (feature - # `klee` só existe em klee/Cargo.toml); este é o lint dele. - - name: Clippy do build KLEE (rustc 1.72.1, feature klee) - run: RUSTUP_TOOLCHAIN=1.72.1 cargo clippy --locked --manifest-path klee/Cargo.toml --features klee,wasm -- -D warnings - - # O script roda os autotestes da troca de intrínsecos antes de usá-la: - # equivalência por lli-16 em tests/klee/lower-intrinsics.ll e recusa - # de tests/klee/unknown-intrinsic.ll; qualquer intrínseco que o KLEE - # 3.1 não execute faz o build falhar. Antes da troca, o simbolizador - # de backtrace do std vira stubs de abort (scripts/klee-stub-symbolizer.py, - # com autoteste em tests/klee/stub-symbolizer.ll), e a guarda final - # falha se algum corpo do simbolizador sobrar. - - name: Compilar o runtime KLEE em bitcode (klee/Cargo.toml, --locked) - run: scripts/build-klee-runtime.sh target/klee/map2check-klee.bc - - - name: Verificar que o bitcode existe e é legível pelo llvm-dis-16 + # O núcleo do runtime drop-in (feature `dropin`, Task 2.7): o + # map2check_init chama o analysis_init e o nondet_init externos dos + # módulos IR; só existe neste manifesto. + - name: Clippy do núcleo drop-in (rustc 1.72.1, feature dropin) + run: RUSTUP_TOOLCHAIN=1.72.1 cargo clippy --locked --manifest-path klee/Cargo.toml --features dropin,wasm -- -D warnings + + # Autotestes do pós-processamento do bitcode do núcleo (antes dentro de + # scripts/build-klee-runtime.sh, que saiu com a feature klee na Task + # 2.10): equivalência por lli-16 em tests/klee/lower-intrinsics.ll, + # recusa de tests/klee/unknown-intrinsic.ll e os stubs do simbolizador + # de backtrace (tests/klee/stub-symbolizer*.ll). + - name: Autotestes do pós-processamento do bitcode (scripts/klee-*.py) + run: scripts/test-klee-postprocess.sh + + # Os 21 módulos .bc do drop-in (Task 3.1, scripts/build-runtime-bc.sh): + # núcleo Rust com o pós-processamento da D7 e os 20 módulos IR montados + # com llvm-as-16. Usa o LLVM 16 instalado acima, sem a imagem; o build + # confere que o llvm-dis-16 lê os 21, que o núcleo não define nome de + # módulo IR, que não sobra nenhum `freeze` (o KLEE 3.1 não o executa), + # que o núcleo tem menos de 9 500 linhas de instrução e 13 600 linhas + # de IR (Task 3.3) e nenhum llvm.bitreverse (test-core-checks.sh é o + # teste dessas guardas). + # test-build-runtime-bc.sh constrói duas vezes, do zero, e exige a lista + # exata dos 21 arquivos e os mesmos sha256 (determinismo); a matriz de + # link precisa da imagem e roda no job runtime_matrix. + - name: Os 21 módulos .bc do runtime drop-in (lista, leitura, determinismo) run: | - test -s target/klee/map2check-klee.bc - llvm-dis-16 target/klee/map2check-klee.bc -o /dev/null + scripts/runtime-ir/test-core-checks.sh + MATRIX=0 scripts/test-build-runtime-bc.sh - name: Checar dependências do lockfile KLEE (cargo-deny) uses: EmbarkStudios/cargo-deny-action@v2 with: manifest-path: ./klee/Cargo.toml - - name: Publicar o bitcode do runtime KLEE - uses: actions/upload-artifact@v4 + # Matriz de link dos 21 .bc (Task 3.1) na imagem do Map2Check. A imagem + # map2check-dev:develop- não está num registro: o job baixa o + # develop (scripts/fetch-map2check.sh, que reescreve vendor/MAP2CHECK_VERSION + # no runner: o commit testado é o HEAD do develop do dia, não + # necessariamente o fixado no repositório), constrói a imagem a partir do + # Dockerfile.dev dele (camadas no cache do GitHub Actions) e a instalação do + # C (scripts/it/build-map2check.sh), gera os .bc e roda a matriz sem e com + # --ref. Longo (a imagem compila KLEE, AFL++, crab/clam, dg); só em push e + # sob demanda, não em pull_request. + # ATENÇÃO: este job ainda não rodou em nenhum runner do GitHub (escrito na + # Task 3.1 sem push); tempo de build da imagem e espaço em disco são + # estimativas (~6 min numa máquina local; 20-60 min previstos no runner). + runtime_matrix: + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-24.04 + timeout-minutes: 240 + steps: + - name: Baixar o código do repositório + uses: actions/checkout@v4 + + - name: Instalar o rustc 1.72.1 (LLVM 16) + run: rustup toolchain install 1.72.1 --profile minimal + + # O fetch reescreve vendor/MAP2CHECK_VERSION com o HEAD do develop do + # dia; o commit fixado é o do arquivo como está no repositório. + - name: Baixar o Map2Check (branch develop, sem .git) + run: | + pinned="$(sed -n 's/^commit=//p' vendor/MAP2CHECK_VERSION)" + scripts/fetch-map2check.sh + fetched="$(sed -n 's/^commit=//p' vendor/MAP2CHECK_VERSION)" + echo "develop baixado: $fetched" + echo "develop fixado: $pinned (vendor/MAP2CHECK_VERSION do repositório)" + if [ "$fetched" != "$pinned" ]; then + echo "::warning title=Map2Check develop mudou::o job testa $fetched, não o commit fixado $pinned (vendor/MAP2CHECK_VERSION)" + fi + echo "MAP2CHECK_COMMIT12=${fetched:0:12}" >> "$GITHUB_ENV" + + # A imagem tem ~4,2 GB, mais o cache do buildx e a instalação do C: o + # runner padrão tem pouco espaço livre. + - name: Liberar espaço em disco + run: | + df -h / + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force + df -h / + + - uses: docker/setup-buildx-action@v3 + + - name: Construir a imagem do Map2Check (Dockerfile.dev do develop) + uses: docker/build-push-action@v6 with: - name: map2check-klee-bc - path: target/klee/map2check-klee.bc - if-no-files-found: error + context: vendor/map2check + file: vendor/map2check/Dockerfile.dev + tags: map2check-dev:develop-${{ env.MAP2CHECK_COMMIT12 }} + load: true + cache-from: type=gha,scope=map2check-dev + cache-to: type=gha,scope=map2check-dev,mode=max + + - name: Construir e instalar o Map2Check (C) na imagem + run: scripts/it/build-map2check.sh + + - name: Os 21 .bc, determinismo e a matriz de link (sem e com --ref) + run: scripts/test-build-runtime-bc.sh + + - name: Sobreposição sobre a instalação do C + run: BC_DIR=target/runtime-bc-test/a/out scripts/it/test-overlay-install.sh + + - name: Pacote de release com os 21 .bc de verdade + run: BC_DIR=target/runtime-bc-test/a/out scripts/test-package-release.sh diff --git a/.gitignore b/.gitignore index 0ab168c..4efc56a 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,9 @@ map2check.h # Arquivos de release (scripts/package-release.sh) dist/ + +# Cópia local do Map2Check para testes de integração (sem versionamento) +vendor/map2check/ + +# Cache de bytecode do Python +__pycache__/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 4524757..65f0f4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,195 @@ the C ABI or the JSON report must update them. ## [Unreleased] +## [2.0.0-rc.1] - 2026-10-28 + +This release turns the library into the drop-in runtime of Map2Check +`develop`: the C ABI is the C runtime's, the package carries the 21 bitcode +modules that replace the C ones, and two differential gates against the C +runtime passed. + +- **Validated against** Map2Check `develop` + `0bca42eb89d116178e6b52f52acaf73f7c305088` (`MAP2CHECK_VERSION` in the + archive); its C runtime modules are byte for byte those of `3e914528ec57`, + the commit of the first gate. +- **Packaging.** The archive has the 21 `lib/*.bc` (`scripts/build-runtime-bc.sh`), + `MAP2CHECK_VERSION` and an inner `SHA256SUMS`, besides the header and the + static libraries (`scripts/package-release.sh`). `scripts/it/overlay-install.sh` + installs the modules over a C install, or copy `lib/*.bc` into + `${MAP2CHECK_PATH}/lib`. See `docs/integration-guide.md` and + `docs/release.md`; `docs/cutover/handoff-map2check.md` suggests what the + Map2Check maintainer would change to adopt the package. +- **Gate 1** (2026-10-27, `docs/cutover/gate-2026-10-27.md`): `gate: PASS` + after the approval of SD-24, SD-25, SD-26 and the extension of SD-20, with + the 2.0 s compile allowance for `afl` and `hybrid` wall time + (`docs/cutover/decisoes.md`; not an SD). +- **Gate 2** (2026-10-28, `docs/cutover/gate-2026-10-28.md`): `gate: PASS` on + the first comparison against the C baseline of `0bca42eb89d1`, with no new + deviation: median wall ratio 0.905 (limit 1.10), median RSS ratio 1.022 + (limit 1.36), no timeouts. + +### Breaking changes + +- **The C ABI is the Map2Check runtime ABI only (D6, Task 2.10).** + `libmap2check.a` exports exactly the names of the C runtime of Map2Check + `develop` that its core modules own (`Map2CheckFunctions`, `TrackBBLog`, + `NonDetLog`, `PropertyGenerator`; `map2check_init`, `map2check_success`, + `map2check_track_bb`, `map2check_target_function`, `map2check_error`, + `map2check_exit`, `map2check_destroy`, `map2check_get_current_step`, + `map2check_next_current_step`, `__VERIFIER_error`, `write_property`, + `nondet_log_init`, `nondet_log_destroy` and the 23 `map2check_nondet_`) + and the `__m2c_rt_*` core entry points the generated IR modules forward to; + `scripts/check-export-set.sh` fails on anything else. The rest of the C + runtime ABI is defined by the IR modules of `scripts/runtime-ir/gen_ll.py` + (D7). Left the C ABI and the header: + - the 1.x core API: `map2check_reset`, `set_false_result`, + `get_current_property`, `get_current_step`, `get_next_step`, + `print_all_containers_as_json`, `vcc_reset_meta_data`, + `map2check_set_memcleanup`, `map2check_set_null_is_valid`, + `map2check_enable_witness` (its C ABI is now `__m2c_rt_witness_enable`), + `map2check_set_mode` (and the `AnalysisMode` enum of the header), + `map2check_reach_target`, `map2check_save_basic_block_log`, + `map2check_is_in_trackbb_container`, `map2check_map_{alloca, + non_static_alloca,funct_address,malloc,calloc,posix_memalign,free, + store_pointer}`, `map2check_check_{load,free,free_resolved_address, + deref,mem_endprog}`, `map2check_is_valid_assert`, the 17 + `map2check_save_nondet_log_` (and `non_det_log_t`, `bbtrack_log_t`, + `enum var_type_t` of the header), `map2check_binop_{add,sub,mul,div}_int`, + `map2check_binop_{add,sub,mul,div}_unsigned`, `map2check_binop_neg_int`, + `map2check_binop_{shl,shr}_int`, `map2check_binop_shr_unsigned` and + `map2check_binop_{add,sub,mul,div,rem}_signed`. They stay Rust functions + of `map2check::ffi` that the Rust tests drive; + - the 1.x exports whose C names an IR module now defines with the C + signature: `map2check_check_deref`, `map2check_wasm_{malloc,free, + check_access}` (`AnalysisModeMemtrack.bc`), `wasm_rt_{allocate_memory, + grow_memory,free_memory,allocate_funcref_table,free_funcref_table, + is_initialized,init,free,trap}` and + `w2c_wasi__snapshot__preview1_proc_exit` (`WasmRuntimeStubs.bc`); + - the `libfuzzer` feature (`LLVMFuzzerTestOneInput`, + `__VERIFIER_nondet_`, `__VERIFIER_assume`, `__VERIFIER_assert`, + `map2check_fuzzer_assume`, `lib/libmap2check_libfuzzer.a` of the release + archive) and the `klee` feature of `klee/Cargo.toml` with + `scripts/build-klee-runtime.sh`, `map2check-klee.bc` and + `tests/klee_paths.sh`: the generators are the IR modules + `NonDetGenerator{None,Klee,AFL}.bc` (SD-07, SD-08, SD-11 and SD-18 say + what changed). The self-tests of the bitcode post-processing moved to + `scripts/test-klee-postprocess.sh`. + `map2check_assume`, `map2check_assume_loop` and `map2check_crab_assume` are + delivered by the glue IR of `Map2CheckFunctions.bc` + (`scripts/runtime-ir/test-assume-link.sh`); `docs/runtime-abi/pending.txt` + is empty. `tests/ffi_harness.c` and the programs of `test/` link the + staticlib with the IR modules they need (`scripts/build-c-abi-program.sh`). + The D5 mapping (`docs/cutover/abi-mapping.tsv`, `scripts/symbol-matrix.sh`, + `tests/core_api_coverage.rs`) is gone with D5. +- `map2check_init` takes the C runtime's `int isSvComp` (ignored), and + `ViolatedProperty` has the values of the C runtime's `enum ViolatedProperty` + (`NONE` 0 … `FALSE_DIVBYZERO` 9, `Concurrency` 10), which + `set_false_result` and `get_current_property` now use. `map2check_init` no + longer removes property files of an earlier run, as the C runtime. + +### Added + +- **The runtime as the 21 bitcode modules Map2Check links (Task 3.1).** + `scripts/build-runtime-bc.sh ` (was the test build + `scripts/runtime-ir/build-test-runtime-bc.sh`) writes exactly + `Map2CheckFunctions.bc` ... `WitnessGenerationNone.bc`, the same bytes on + every build; `scripts/it/overlay-install.sh` copies a C install of + Map2Check and replaces only its `lib/*.bc`. + +- The memcleanup analysis mode of the Map2Check C runtime ABI (Task 2.6 of + the drop-in plan): core entry points `__m2c_rt_memcleanup_analysis_{init, + destroy,is_program_correct,generate_aux_witness_files}` and + `__m2c_rt_memcleanup_map2check_{load,check_cstring,check_deref,malloc, + calloc,posix,free,free_resolved_address,add_store_pointer,alloca, + non_static_alloca,function}`. They share the memory rules of the memtrack + entry points (`MemMode`); what differs is `AnalysisModeMemcleanup.c`'s: the + leak is FALSE-MEMCLEANUP only while a pointer variable still refers to it, + `map2check_load`, `map2check_check_cstring` and `map2check_check_deref` are + empty, and no free is a FALSE-FREE. A release with no allocation row goes on + where develop crashes (SD-23). The C names are defined only by the generated + IR module `AnalysisModeMemcleanup.ll` (no `map2check_wasm_*`). +- The memtrack analysis mode of the Map2Check C runtime ABI (Task 2.5 of the + drop-in plan): core entry points `__m2c_rt_memtrack_analysis_{init,destroy, + is_program_correct,generate_aux_witness_files}` and + `__m2c_rt_memtrack_map2check_{load,check_cstring,check_deref,malloc,calloc, + posix,free,free_resolved_address,add_store_pointer,alloca, + non_static_alloca,function,wasm_malloc,wasm_free,wasm_check_access}`, with + the C signatures and semantics of `AnalysisModeMemtrack.c`, `AllocationLog.c`, + `ListLog.c` and `HeapLog.c` over develop's heap, allocation and list logs + (`memtrack::c_mode`, one container for the Array and `--btree` builds, D3). + The property files and `heap_log.csv`, `allocation_log.csv` and + `list_log.csv` are the C runtime's byte for byte. The C names are defined + only by the generated IR module `AnalysisModeMemtrack.ll`; the container and + log modules (`ContainerRealloc`, `ContainerBTree`, `BTree`, `AllocationLog`, + `ListLog`, `HeapLog`) are empty IR. The legacy `map2check_check_deref` and + `map2check_wasm_*` exports stay until Task 2.10. +- The assert and none analysis modes of the Map2Check C runtime ABI (Task 2.4 + of the drop-in plan): core entry points `__m2c_rt_assert_analysis_{init, + destroy,is_program_correct,generate_aux_witness_files}`, + `__m2c_rt_assert_map2check_assert` and `__m2c_rt_none_analysis_{init,destroy, + is_program_correct,generate_aux_witness_files}`, with the C semantics and + property files of `AnalysisModeAssert.c` and `AnalysisModeNone.c`. The C + names are defined only by the generated IR modules `AnalysisModeAssert.ll` + (where `__VERIFIER_assert` is an empty function, as in C) and + `AnalysisModeNone.ll`, never by `libmap2check.a`; each module's + `analysis_init` selects its mode. +- The overflow analysis mode of the Map2Check C runtime ABI (Task 2.3 of the + drop-in plan): core entry points `__m2c_rt_overflow_analysis_{init,destroy, + is_program_correct,generate_aux_witness_files}`, + `__m2c_rt_overflow_map2check_binop_{add,sub,mul,sdiv,srem}`, + `__m2c_rt_overflow_map2check_binop_{add,sub,mul}_uint` (no-ops, as in C) and + `__m2c_rt_overflow_overflowError`, with the C semantics and property files of + `AnalysisModeOverflow.c`. The C names are defined only by the generated IR + module `AnalysisModeOverflow.ll` (`scripts/runtime-ir/gen_ll.py --module + AnalysisModeOverflow`), never by `libmap2check.a`. The IR generator moved + from the D7 spike to `scripts/runtime-ir/` and now emits the + `signext`/`zeroext` attributes clang-16 gives `char`, `short` and `_Bool`. +- The nondet log of the Map2Check C runtime ABI (Task 2.2 of the drop-in + plan): the 23 `map2check_nondet_` exports of `NonDetLog.h` (`u8`, `u16`, + `u32`, `memory_byte`, `longlong` and `ulonglong` included), `nondet_log_init`, + `nondet_log_destroy` and `__m2c_rt_nondet_log_to_file`, which writes + `klee_log.csv` byte for byte as `NonDetLog.c` does. The log is separate from + the one behind `map2check_save_nondet_log_` and the JSON report, which + are unchanged, but `klee_log.csv` of the witness path is now formatted by the same code (`nondet::c_log`), so a function name longer than 35 bytes is cut there as `NonDetLog.c` does. `__m2c_rt_nondet_log_len`, `_get_i64` and `_set_i64` let the KLEE IR do the row-pinning of `NonDetGeneratorKlee.c`. The `none` generator needs no Rust entry point: it is pure IR. +- The core of the Map2Check C runtime ABI (Task 2.1 of the drop-in plan): + `map2check_track_bb`, `map2check_target_function`, `map2check_error`, + `map2check_exit`, `map2check_destroy`, `map2check_get_current_step`, + `map2check_next_current_step` and `write_property`, with the C signatures, + and the witness entry points `__m2c_rt_witness_generate_aux_files` and + `__m2c_rt_witness_generate_confirmation_file`. + +- `__m2c_rt_output_set_atomic(int)` (Task 2.8) selects how the result files + are written: through a temporary file and a rename (SD-20) or in place, as + develop's `fopen("w")`. The drop-in core starts in place and the KLEE + generator turns atomic writes on, so the AFL++ and `none` generators write + as the C runtime (user decision of 2026-10-27). Other builds stay atomic. + +### Changed + +- **A drop-in core four times smaller (Task 3.3).** The frontend compiles + the program linked with `Map2CheckFunctions.bc` three times with + `afl-clang-fast`, and that time grows with the core's IR. The result files + are now written by `rust/src/cfmt.rs` (the `%u`, `%d`, `%p`, `%lf` and + `%.9g` of glibc, exact, without `core::fmt`) and `rust/src/sys.rs` (the + same libc calls `std::fs`, `std::env` and `std::process` made). The + drop-in build has no serde, serde_json or thiserror (`klee/Cargo.toml` has + no dependencies), no JSON channel, no old-API leak checks, and is built + at `opt-level = "z"`. The core went from 34 582 to 8 865 instruction lines + (762 580 to 327 164 bytes), one `afl-clang-fast -g -O2` of the smoke + program from 1.74 s to 0.60 s; `scripts/build-runtime-bc.sh` now fails + above 9 500 instruction lines or 13 600 IR lines. The file contents, the + stderr diagnostics and the C ABI do not change. `thiserror` left the main + build too. +- **The drop-in core ends a run through the linked witness module (Task + 3.1).** In `Map2CheckFunctions.bc`, `map2check_exit` calls the + `generate_aux_files`/`generate_confirmation_file` of `WitnessGeneration.bc` + or `WitnessGenerationNone.bc`, as `Map2CheckFunctions.c:89-102` does, and + `generate_aux_files` calls the `analysis_generate_aux_witness_files` of the + mode module and the `nondet_generate_aux_witness_files` of the generator + module, which alone writes `klee_log.csv` (`WitnessGeneration.c:15-29`). + Before, the drop-in wrote no witness file at all: nothing turned the + witness option on. The staticlib keeps `__m2c_rt_witness_enable`. + ## [1.1.0-rc1] - 2026-10-17 Release candidate of the cutover work: the library becomes the runtime of diff --git a/CLAUDE.md b/CLAUDE.md index f2dd884..0ae6ce6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -Map2Check-Library is a Rust library with a C ABI for the Map2Check program verification tool. It tracks memory, basic blocks, and nondeterministic values, and checks safety properties (overflow, memory safety, assert reachability) in programs analyzed by LibFuzzer. The build produces the static library (`libmap2check.a`) and generates `include/map2check.h`. Remaining parity work with the legacy C implementation is tracked in [TODO.md](TODO.md). +Map2Check-Library is a Rust library with a C ABI for the Map2Check program verification tool. It tracks memory, basic blocks, and nondeterministic values, and checks safety properties (overflow, memory safety, assert reachability) in programs instrumented by Map2Check. Since 2.0.0 (D6) it is a drop-in runtime: the C ABI is the C runtime ABI of Map2Check `develop`, and the interface with Map2Check is the 21 bitcode modules (`lib/*.bc`, `scripts/build-runtime-bc.sh`) that replace the C runtime's. The build also produces the static library (`libmap2check.a`) and generates `include/map2check.h`. Version 2.0.0-rc.1 was validated against Map2Check `develop` `0bca42eb89d116178e6b52f52acaf73f7c305088` (`docs/cutover/gate-2026-10-27.md`, `docs/cutover/gate-2026-10-28.md`; install and handoff in `docs/integration-guide.md` and `docs/cutover/handoff-map2check.md`). Remaining parity work with the legacy C implementation is tracked in [TODO.md](TODO.md). ## Build Commands @@ -32,12 +32,16 @@ cargo fmt --check cargo clippy --all-targets --all-features -- -D warnings cargo deny check -# Exercise the C ABI with ASan and UBSan (requires clang) +# Exercise the C ABI with ASan and UBSan (requires clang): the harness links +# the staticlib with the IR modules it needs, as Map2Check links a program cargo build --release --all-features -clang -fsanitize=address,undefined -fno-omit-frame-pointer \ - tests/ffi_harness.c target/release/libmap2check.a \ - -ldl -lpthread -lm -o target/ffi_harness +scripts/build-c-abi-program.sh -o target/ffi_harness \ + -m Map2CheckFunctions-glue -m NonDetGeneratorAFL tests/ffi_harness.c \ + -- -fsanitize=address,undefined -fno-omit-frame-pointer target/ffi_harness + +# The exported set is exactly the C runtime ABI (D6) +scripts/check-export-set.sh ``` ## Architecture @@ -54,18 +58,20 @@ Each package lives in a subdirectory of `rust/src/`: | Package | Purpose | |---|---| | `caller/` | Result and violated-property types, global step counter. | -| `nondet/` | Typed nondeterministic values and LibFuzzer generators. | +| `nondet/` | Typed nondeterministic values and the C runtime's nondet log (`klee_log.csv`). | | `memtrack/` | Memory allocation tracking and memory-safety checks. | | `bbtrack/` | Basic-block entries and lookup. | | `analysismode/` | Verification condition checkers: `analysis_assert` (assert reachability), `analysis_overflow` (arithmetic overflow), `analysis_memory` (memory safety). | ### Container Pattern -Analysis records are stored in vectors in the process-wide `AnalysisState`, protected by a mutex and reset by `map2check_reset()`. +The 1.x core API below (`ffi.rs` functions such as `map2check_set_mode` and `map2check_map_*`) is no longer the interface with Map2Check; it remains as Rust functions that the Rust tests drive. + +Analysis records are stored in vectors in the process-wide `AnalysisState`, protected by a mutex and reset by `map2check_init()`. ### Output Format -At analysis end, `print_all_containers_as_json()` serializes the state to RFC 8259 JSON on stdout using `serde_json`. +With an analysis mode selected (a Map2Check run), results go to the property files of the C runtime (`map2check_property`...). With no mode, `map2check_success()` serializes the state to RFC 8259 JSON on stdout using `serde_json`. ### Generated Header @@ -77,9 +83,40 @@ At analysis end, `print_all_containers_as_json()` serializes the state to RFC 82 - Never add AI attributions (e.g. "Co-authored-by", "Generated with ...") to commits or PR descriptions. - See [.github/copilot-instructions.md](.github/copilot-instructions.md) for the same conventions in more detail. +## Regras obrigatórias + +1. **GitHub CLI e ações remotas somente sob solicitação explícita.** + Não execute, sem um pedido explícito do usuário na mensagem atual, comandos `gh` que + criem, alterem ou encerrem algo no GitHub: `gh pr create/merge/close/comment/review`, + `gh issue create/close/comment/edit`, `gh release`, `gh repo` e similares. O mesmo vale + para `git push`, criação de tags remotas e alteração de branches remotos. Comandos apenas + de leitura (`gh pr view`, `gh issue list`) são permitidos. Na dúvida, pergunte antes. + +2. **Escopo restrito a este repositório.** + Todas as modificações de código, testes, configurações e documentação devem ser feitas + somente neste repositório. Nunca altere, faça commit ou abra PR/issue no repositório do + Map2Check, nem em qualquer outro repositório. + +3. **Map2Check para testes de integração: cópia local e sem versionamento.** + - Baixe o Map2Check sempre do branch `develop`, para o diretório `vendor/map2check/` (ou + outro que o usuário indicar), por meio de + `git clone --depth 1 --branch develop vendor/map2check`. Não use tags nem tarballs + de release. + - Logo após o download, remova o versionamento com `rm -rf vendor/map2check/.git`, para + que nenhum comando git atue sobre o repositório original. + - Garanta que `vendor/map2check/` esteja no `.gitignore` deste repositório, para que a + cópia não seja commitada aqui. + - Registre o commit do `develop` baixado (por exemplo, em `vendor/MAP2CHECK_VERSION`) para + tornar os testes reproduzíveis. + - Não edite os fontes baixados. Se um teste exigir mudanças no Map2Check, pare e informe + o usuário. + ## Key Entry Points -- `map2check_init()` — initializes the global analysis state -- `map2check_success()` / `set_false_result(prp, line, func)` — set final verification result -- `print_all_containers_as_json()` — emits the JSON result report -- `map2check_is_valid_assert()`, `map2check_map_malloc()`, `map2check_map_free()` etc. — called by LLVM instrumentation passes in the Map2Check tool +The C ABI is the C runtime ABI of Map2Check `develop` (D6, 2.0.0); `scripts/check-export-set.sh` checks it. + +- `map2check_init(int isSvComp)` — initializes the global analysis state (C runtime signature) +- `map2check_success()`, `map2check_error()`, `map2check_target_function()`, `write_property()` — end of run and result files +- `map2check_track_bb()`, `map2check_nondet_()` — basic-block and nondet logs, called by the Map2Check passes +- `__m2c_rt_*` — core entry points; the generated IR modules (`scripts/runtime-ir/gen_ll.py`) define the other C names (`map2check_malloc`, `map2check_assert`, `analysis_init`...) and forward to them +- `lib/*.bc` (21 modules, `scripts/runtime-modules.txt`) — what Map2Check links; install with `scripts/it/overlay-install.sh` or copy to `${MAP2CHECK_PATH}/lib` diff --git a/Cargo.lock b/Cargo.lock index 3b83043..212159a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,14 +129,13 @@ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "map2check-library" -version = "1.1.0-rc1" +version = "2.0.0-rc.1" dependencies = [ "cbindgen", "mockall", "serde", "serde_json", "tempfile", - "thiserror", ] [[package]] @@ -309,26 +308,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "toml" version = "0.8.23" diff --git a/Cargo.toml b/Cargo.toml index ac81453..52215b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-3.0-only [package] name = "map2check-library" -version = "1.1.0-rc1" +version = "2.0.0-rc.1" edition = "2021" description = "Rust library to support Map2Check tool: tracks memory, basic blocks, and nondeterministic values, and checks safety properties" license = "GPL-3.0-only" @@ -15,24 +15,22 @@ path = "rust/src/lib.rs" [dependencies] serde = { version = "1", features = ["derive"] } serde_json = "1" -thiserror = "2" [dev-dependencies] mockall = "0.13" tempfile = "3" [features] -libfuzzer = [] -# The wasm2c runtime of Map2Check's `--wasm` mode (D2): the `wasm_rt_*` stubs, -# `w2c_wasi__snapshot__preview1_proc_exit` and the `map2check_wasm_*` checks. +# The wasm2c runtime of Map2Check's `--wasm` mode (D2): the `__m2c_rt_wasm_*` +# core entry points that `WasmRuntimeStubs.bc` forwards to. wasm = [] -# The `klee` feature (the KLEE runtime) lives in klee/Cargo.toml, the -# manifest of the rustc 1.72.1 bitcode build (D1, route A'). It is not a -# feature here: `--all-features` would link it with `libfuzzer` (both define -# `__VERIFIER_nondet_*`) and against KLEE intrinsics no host binary has. +# The `dropin` feature (the drop-in core) lives in klee/Cargo.toml, the +# manifest of the rustc 1.72.1 bitcode build (D1, route A'): its +# map2check_init calls the analysis_init and nondet_init of the IR modules, +# which no host build links. [lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(feature, values("klee"))'] } +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(feature, values("dropin"))'] } [profile.release] opt-level = 3 diff --git a/Dockerfile b/Dockerfile index 5d27311..1332904 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,6 +27,10 @@ RUN mkdir -p include && cargo build --release # ── Stage 2: Test + Coverage ────────────────────────────────────────────────── FROM builder AS test +# tests/gate_deltas.rs runs scripts/runtime-ir/gen_ll.py (SD-24). +RUN apt-get update && apt-get install -y --no-install-recommends python3 \ + && rm -rf /var/lib/apt/lists/* + COPY test/ ./test/ # Os testes leem os goldens do runtime C, os docs de compatibilidade, o # CHANGELOG, o mapa de ABI e o próprio Dockerfile (label de versão). @@ -34,6 +38,7 @@ COPY tests/ ./tests/ COPY docs/ ./docs/ COPY CHANGELOG.md Dockerfile ./ COPY scripts/coverage.sh ./scripts/coverage.sh +COPY scripts/runtime-ir/gen_ll.py ./scripts/runtime-ir/gen_ll.py RUN cargo test --all-features 2>&1 | tee /tmp/test-results.txt @@ -55,4 +60,4 @@ LABEL maintainer="herberthb12@gmail.com" LABEL org.opencontainers.image.title="map2check-library" LABEL org.opencontainers.image.description="Rust library to support Map2Check program verification tool" LABEL org.opencontainers.image.licenses="GPL-3.0-only" -LABEL org.opencontainers.image.version="1.1.0-rc1" +LABEL org.opencontainers.image.version="2.0.0-rc.1" diff --git a/README.md b/README.md index 194c889..3185230 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,15 @@ # Map2Check-Library -Biblioteca de suporte à ferramenta [Map2Check](https://github.com/hbgit/map2check), reescrita em **Rust** (rewrite completo do C original). Provê rastreamento dinâmico e verificação de propriedades de segurança para programas analisados por LibFuzzer: +Biblioteca de suporte à ferramenta [Map2Check](https://github.com/hbgit/map2check), reescrita em **Rust** (rewrite completo do C original). Provê rastreamento dinâmico e verificação de propriedades de segurança para os programas que o Map2Check instrumenta: - Rastreamento de blocos básicos (`bbtrack`) - Rastreamento de endereços de memória e operações de gerenciamento de memória (`memtrack`) -- Rastreamento de valores não-determinísticos gerados por LibFuzzer (`nondet`) +- Log dos valores não-determinísticos (`nondet`), gerados pelos módulos de gerador do Map2Check (AFL++, KLEE) - Verificação de propriedades de segurança: overflow aritmético, memória inválida, assert definido pelo usuário (`analysismode`) -- API C compatível via FFI (`ffi.rs`) para integração direta com a LLVM Pass do Map2Check +- ABI C via FFI (`ffi.rs`): desde a 2.0.0 (D6), exatamente o ABI C do runtime do Map2Check (`develop`) que os módulos núcleo definem, mais os pontos de entrada `__m2c_rt_*` para os quais os módulos IR gerados (`scripts/runtime-ir/gen_ll.py`) encaminham o resto do ABI (`scripts/check-export-set.sh`) + +**A interface com o Map2Check são os 21 módulos `.bc` do runtime drop-in** (`scripts/build-runtime-bc.sh`), que substituem os do runtime C em `${MAP2CHECK_PATH}/lib` sem mudança no frontend nem nos passes. A versão 2.0.0-rc.1 foi validada contra o `develop` `0bca42eb89d116178e6b52f52acaf73f7c305088` por dois gates diferenciais (`docs/cutover/gate-2026-10-27.md` e `docs/cutover/gate-2026-10-28.md`). A API núcleo 1.x (`map2check_set_mode`, `map2check_map_*`, `print_all_containers_as_json`...) deixou de ser ABI C e não é a interface com o Map2Check: ela segue como funções Rust de `map2check::ffi`, usadas pelos testes. Instalação: [`docs/integration-guide.md`](docs/integration-guide.md); sugestões para o mantenedor do Map2Check: [`docs/cutover/handoff-map2check.md`](docs/cutover/handoff-map2check.md). ## Requisitos @@ -32,7 +34,9 @@ cargo install cargo-tarpaulin cargo-audit cargo-deny cbindgen As features configuradas no `Cargo.toml` são: -- `libfuzzer`: habilita a integração com o LibFuzzer. +- `wasm`: os pontos de entrada `__m2c_rt_wasm_*` do runtime wasm2c (modo `--wasm` do Map2Check). + +A feature `libfuzzer` saiu na 2.0.0, com a API núcleo 1.x (veja o `CHANGELOG.md`). ## Build e testes @@ -110,7 +114,7 @@ rust/src/ ├── caller/mod.rs # ViolatedProperty, AnalysisResult, step counter ├── nondet/ │ ├── mod.rs # NonDetValue (enum), NonDetEntry -│ └── libfuzzer.rs # LLVMFuzzerTestOneInput (feature = "libfuzzer") +│ └── c_log.rs # log nondet do runtime C (NonDetLog.c, klee_log.csv) ├── memtrack/mod.rs # MemTrackEntry, VCC checks de memória ├── bbtrack/mod.rs # BasicBlockEntry, busca por linha ├── analysismode/ @@ -152,17 +156,17 @@ Map2Check Tool (LLVM Pass / Instrumentação) | Aritmética `INT_MAX/INT_MIN` manual | `i32::checked_add()`, `checked_sub()` etc. | | `lib/json-maker.c` + `ftoa.c` | `serde_json` + `#[derive(Serialize)]` | -**Política de `unsafe`**: somente `ffi.rs` contém blocos `unsafe`. Todos os outros módulos têm `#![deny(unsafe_code)]`. Cada bloco unsafe em `ffi.rs` possui um comentário `// SAFETY:`. +**Política de `unsafe`**: somente `ffi.rs` (com `wasm.rs` e `sys.rs`, as chamadas da libc dos arquivos de resultado, do ambiente e do stderr) contém blocos `unsafe`. Todos os outros módulos têm `#![deny(unsafe_code)]`. Cada bloco unsafe possui um comentário `// SAFETY:`. ## Output JSON -Ao final da análise, `print_all_containers_as_json()` emite no stdout um objeto `map2check_log` com o veredito (`result`: `"TRUE"`/`"FALSE"`), a propriedade violada e os containers `Container_TrackBBLog`, `Container_NonDetLog` e, só em violações de memória, `Container_AllocationLog`. O formato exato, com exemplos verificados contra o serializador, está em [`docs/integration-guide.md`](docs/integration-guide.md#json-report). +Sem modo de análise selecionado, `map2check_success()` emite no stdout um objeto `map2check_log` (numa execução do Map2Check, com modo, o resultado vai para os arquivos de propriedade, como no runtime C) com o veredito (`result`: `"TRUE"`/`"FALSE"`), a propriedade violada e os containers `Container_TrackBBLog`, `Container_NonDetLog` e, só em violações de memória, `Container_AllocationLog`. O formato exato, com exemplos verificados contra o serializador, está em [`docs/integration-guide.md`](docs/integration-guide.md#json-report). ## Compatibilidade e releases - [`docs/api-deltas.md`](docs/api-deltas.md): matriz de símbolos e assinaturas da API C (núcleo e passes do Map2Check) em relação ao C legado. - [`docs/semantic-deltas.md`](docs/semantic-deltas.md): diferenças de comportamento, cada uma com caso reproduzível e teste. -- [`docs/integration-guide.md`](docs/integration-guide.md): como linkar `libmap2check.a`, ordem de chamadas e formato do relatório JSON. +- [`docs/integration-guide.md`](docs/integration-guide.md): como instalar os 21 `.bc` no lugar dos do runtime C, o commit validado do Map2Check, como reproduzir o gate, como linkar `libmap2check.a` e o formato do relatório JSON. - [`docs/release.md`](docs/release.md) e [`CHANGELOG.md`](CHANGELOG.md): processo de release e histórico de versões. `tests/compat_docs.rs` falha em três casos: quando um export do header não está na matriz (ou está com a declaração errada), quando um `SD-NN` aponta para um teste que não existe e quando os exemplos JSON do guia divergem da saída real. Toda mudança na ABI C ou no relatório JSON atualiza esses documentos no mesmo PR. @@ -170,7 +174,7 @@ Ao final da análise, `print_all_containers_as_json()` emite no stdout um objeto Para montar o pacote de release localmente: ```sh -scripts/package-release.sh # dist/map2check-library--.tar.gz + SHA256SUMS +scripts/package-release.sh # dist/map2check-library--.tar.gz + SHA256SUMS (inclui os 21 .bc) ``` ## Map2Check flow com a biblioteca diff --git a/TODO.md b/TODO.md index b9b2510..522fbbd 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,25 @@ +# TODO + +**Estado em 2026-10-28 (2.0.0-rc.1).** O plano do runtime drop-in +([docs/superpowers/plans/2026-10-26-runtime-drop-in-map2check.md](docs/superpowers/plans/2026-10-26-runtime-drop-in-map2check.md)) +está concluído: o ABI C é o do runtime C do Map2Check `develop`, o pacote +leva os 21 `.bc` e dois gates diferenciais passaram contra o `develop` +`0bca42eb89d116178e6b52f52acaf73f7c305088` +([gate de 2026-10-27](docs/cutover/gate-2026-10-27.md) e +[gate de 2026-10-28](docs/cutover/gate-2026-10-28.md)). Falta só a release +(tag `v2.0.0-rc.1`, PR e GitHub release), que o mantenedor faz sob pedido +explícito (veja [docs/release.md](docs/release.md)). Sugestões de adoção pelo +Map2Check: [docs/cutover/handoff-map2check.md](docs/cutover/handoff-map2check.md). + +Tudo abaixo é o relatório de paridade de antes do plano, mantido como +histórico: as tarefas que descrevem a API núcleo 1.x (`map2check_set_mode`, +`map2check_map_*`, JSON, libfuzzer) deixaram de ser ABI C na 2.0.0 (veja o +`CHANGELOG.md`) e só valem para as funções Rust de `map2check::ffi`. O que +segue aberto de fato: desempenho como benchmark periódico no CI, e a +promoção do runtime Rust a padrão no Map2Check, decisão do mantenedor de lá. + +--- + # TODO — paridade da implementação Rust Comparação feita entre `master` (`05b232a02a69d2a14114df9b34172e56f6365a7c`) @@ -136,10 +158,13 @@ e não repetem as tarefas já listadas. Falhas na geração ou no link de bitcode não devem ser mascaradas por `|| true`. - [x] **Executar o gate de viabilidade de KLEE e registrar a decisão.** - Feito na Task 2.7 (#20): feature `klee` em `klee/Cargo.toml` (rustc 1.72.1, - bitcode LLVM 16, `scripts/build-klee-runtime.sh`), teste - `tests/klee_paths.sh` com KLEE 3.1, decisão **go** em - [docs/klee-feasibility.md](docs/klee-feasibility.md). + Feito na Task 2.7 (#20), decisão **go** em + [docs/klee-feasibility.md](docs/klee-feasibility.md). Desde a 2.0.0 (Task + 2.10) a rota legada (feature `klee`, `scripts/build-klee-runtime.sh`, + `tests/klee_paths.sh`) saiu: o núcleo é a feature `dropin` de + `klee/Cargo.toml` (rustc 1.72.1, bitcode LLVM 16, + `scripts/build-runtime-bc.sh`), testado sob KLEE 3.1 por + `scripts/runtime-ir/test-klee-generator.sh`. - [ ] **Medir e controlar a regressão de desempenho.** Criar benchmarks reproduzíveis contra a biblioteca C, usando programas de referência e corpus @@ -155,6 +180,12 @@ e não repetem as tarefas já listadas. ### Dependente do repositório externo `hbgit/Map2Check` +> Recomeçado em 2026-10-26: o trabalho do PR #76 saiu do `develop` do Map2Check +> (force push `a0f8b9cd → fbbcb929`). O novo plano é +> [docs/superpowers/plans/2026-10-26-runtime-drop-in-map2check.md](docs/superpowers/plans/2026-10-26-runtime-drop-in-map2check.md): +> a biblioteca exporta o ABI do runtime C do `develop`. Os itens abaixo são do +> plano anterior e valem só como histórico. + - [ ] **Adicionar uma camada de compatibilidade para os passes atuais.** Em `map2check-library`, implementar uma feature isolada que exporte os nomes e assinaturas usados pelos passes de `Map2Check@develop`, delegando à API diff --git a/build.rs b/build.rs index 753b92b..14eca51 100644 --- a/build.rs +++ b/build.rs @@ -25,94 +25,13 @@ fn main() { .expect("Falha ao gerar o header C") .write_to_file(&header_path); - let compatibility_declarations = r#" -#ifndef __NONDETLOG_H_INCLUDED__ -#define __NONDETLOG_H_INCLUDED__ -enum var_type_t { INT_ID, UNIT_ID, LONG_ID, CHAR_ID, DOUBLE_ID, FLOAT_ID }; - -typedef struct _non_det_log { - struct _non_det_log *tqe_next; - struct _non_det_log **tqe_prev; - long step; - int line; - int scope; - int type_var; - union { - int *i; - unsigned int *u; - long *l; - char *c; - char str[20]; - float *f; - double *d; - void *p; - } value; - const char *function_name; -} non_det_log_t; -#endif - -non_det_log_t *map2check_save_nondet_log_int(int line, int scope, enum var_type_t type_var, int *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_uint(int line, int scope, enum var_type_t type_var, unsigned int *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_long(int line, int scope, enum var_type_t type_var, long *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_char(int line, int scope, enum var_type_t type_var, char *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_float(int line, int scope, enum var_type_t type_var, float *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_double(int line, int scope, enum var_type_t type_var, double *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_unsigned(int line, int scope, int type_var, unsigned int *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_ulong(int line, int scope, int type_var, unsigned long *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_uchar(int line, int scope, int type_var, unsigned char *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_short(int line, int scope, int type_var, short *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_ushort(int line, int scope, int type_var, unsigned short *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_bool(int line, int scope, int type_var, int *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_size_t(int line, int scope, int type_var, size_t *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_loff_t(int line, int scope, int type_var, long long *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_sector_t(int line, int scope, int type_var, unsigned long *value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_pointer(int line, int scope, int type_var, void **value, const char *function_name); -non_det_log_t *map2check_save_nondet_log_pchar(int line, int scope, int type_var, char **value, const char *function_name); - -void map2check_binop_add_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name); -void map2check_binop_sub_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name); -void map2check_binop_mul_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name); -void map2check_binop_div_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name); -void map2check_binop_add_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name); -void map2check_binop_sub_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name); -void map2check_binop_mul_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name); -void map2check_binop_div_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name); -void map2check_binop_shl_int(int32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name); -void map2check_binop_shr_int(int32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name); -void map2check_binop_shr_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name); -"#; - let mut header = std::fs::read_to_string(&header_path).expect("Falha ao ler o header C gerado"); - let bbtrack_declaration = "typedef struct bbtrack_log_t {"; - assert!( - header.contains(bbtrack_declaration), - "O header gerado não contém a declaração bbtrack_log_t" - ); - header = header.replace( - bbtrack_declaration, - "#ifndef __TRACKBBLOG_H_INCLUDED__\n#define __TRACKBBLOG_H_INCLUDED__\ntypedef struct bbtrack_log_t {", - ); - let bbtrack_type_end = "} bbtrack_log_t;"; - assert!( - header.contains(bbtrack_type_end), - "O header gerado não contém o fim da declaração bbtrack_log_t" - ); - header = header.replace(bbtrack_type_end, "} bbtrack_log_t;\n#endif"); - header = header.replace( - "struct bbtrack_log_t *map2check_save_basic_block_log", - "bbtrack_log_t *map2check_save_basic_block_log", - ); // cbindgen 0.28 writes two spaces after the closing `#endif`; 0.26 wrote // one. Keep the header byte-identical across the bump. header = header.replace( "#endif /* __MAP2CHECK_H_INCLUDED__ */", "#endif /* __MAP2CHECK_H_INCLUDED__ */", ); - let include_guard_end = "#endif /* __MAP2CHECK_H_INCLUDED__ */"; - let Some(guard_index) = header.find(include_guard_end) else { - panic!("O header gerado não contém o include guard esperado"); - }; - header.insert_str(guard_index, compatibility_declarations); std::fs::write(header_path, header).expect("Falha ao adicionar declarações ABI ao header C"); // Roda este script novamente só se o código Rust, o cbindgen.toml ou o diff --git a/cbindgen.toml b/cbindgen.toml index be76d31..f6a1273 100644 --- a/cbindgen.toml +++ b/cbindgen.toml @@ -8,10 +8,14 @@ documentation_style = "doxy" [export] prefix = "" -# `map2check_set_mode` takes an `int32_t`; export the enum so the header -# still names the mode values. -include = ["AnalysisMode"] -exclude = [] +include = ["ViolatedProperty"] +exclude = ["ViolatedPropertyRaw"] [parse] parse_deps = false + +# `write_property` takes the C enum as its `int` (ViolatedPropertyRaw, so an +# out-of-range value is defined behaviour); the header keeps the C runtime's +# parameter type `enum ViolatedProperty`. +[export.rename] +"ViolatedPropertyRaw" = "enum ViolatedProperty" diff --git a/docs/api-deltas.md b/docs/api-deltas.md index 62fdfd1..8630393 100644 --- a/docs/api-deltas.md +++ b/docs/api-deltas.md @@ -6,6 +6,20 @@ exported by `libmap2check.a` (declared in the generated example live in [`semantic-deltas.md`](semantic-deltas.md) and are cited here by ID (`SD-NN`). +**2.0.0 (D6, Task 2.10).** The C ABI of `libmap2check.a` is the C runtime +ABI of Map2Check `develop` and nothing else: the names of +`docs/runtime-abi/inventory.tsv` that the core modules own +(`Map2CheckFunctions`, `TrackBBLog`, `NonDetLog`, `PropertyGenerator`), +except the `c-only` ones (SD-22) and those a generated IR module defines, +plus the `__m2c_rt_*` core entry points the IR modules forward to +(`scripts/check-export-set.sh` checks the archive). The rest of the C runtime +ABI is defined by the IR modules of `scripts/runtime-ir/gen_ll.py` (D7). The +1.x core API (`map2check_set_mode`, `map2check_map_*`, +`print_all_containers_as_json`...), the `libfuzzer` and `klee` features and +the old `wasm_rt_*`/`map2check_wasm_*`/`map2check_check_deref` exports left +the C ABI; their rows below are `discontinued` with a 2.0.0 note, and the +sections that describe them are kept as the record of 1.x. + Two baselines are compared: - **Legacy C library** — the C `map2check-library` that this repository @@ -30,107 +44,214 @@ removes or changes an export. | `same` | Same name and contract as the baseline. | yes | | `changed` | Exported, but the signature or behaviour differs from the baseline; the Notes say how. | yes | | `new` | No baseline counterpart. | yes | -| `discontinued` | Baseline symbol deliberately not ported. | no | +| `discontinued` | Baseline symbol deliberately not ported, or a 1.x export that left the C ABI in 2.0.0 (D6). | no | | `pending` | Required by the baseline; decision tracked in the cited issue. | no | | `out-of-scope` | Required by the baseline, excluded on purpose. | no | +| `ir` | Pass matrix only: defined by a generated IR module (D7 layout A-fwd), which forwards to a `__m2c_rt_*` core entry point; never in the header. | no (IR) | ## Core C API matrix The declaration column is copied from `include/map2check.h` with -whitespace collapsed. The header declares the `libfuzzer` symbols even -when the library is built without that feature; link them only from a -build with `--features libfuzzer`. +whitespace collapsed. For a `discontinued` row of a 1.x export it is the 1.x +declaration. | Symbol | Rust declaration (`include/map2check.h`) | Legacy C library | Status | Notes | |---|---|---|---|---| -| `map2check_init` | `void map2check_init(void)` | `map2check_init` | same | Starts a fresh analysis through the `analysis_init` of the selected mode and keeps the options and the mode (#10, SD-14). With a mode set, also removes the `map2check_property*` files left in the current directory (see “Result files”). Map2Check passes call `map2check_init(int)`; see the pass matrix. | -| `map2check_reset` | `void map2check_reset(void)` | — | new | Clears containers, result and step counter (#9). Keeps the options set by `map2check_set_memcleanup()` and `map2check_set_null_is_valid()` and the mode set by `map2check_set_mode()`: they are configuration, not analysis state (#10, SD-14). | +| `map2check_init` | `void map2check_init(int is_sv_comp)` | `map2check_init` | same | The C runtime's `map2check_init(int isSvComp)` (`Map2CheckFunctions.c:29-40`, Task 2.1): starts a fresh analysis through the `analysis_init` of the selected mode and keeps the options and the mode (#10, SD-14). `is_sv_comp` only sets develop's unread `sv_comp` and is ignored. Like develop, it leaves property files of an earlier run alone (see “Result files”). | +| `map2check_reset` | `void map2check_reset(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Clears containers, result and step counter (#9). Keeps the options set by `map2check_set_memcleanup()` and `map2check_set_null_is_valid()` and the mode set by `map2check_set_mode()`: they are configuration, not analysis state (#10, SD-14). | | `map2check_success` | `void map2check_success(void)` | `map2check_success` | same | Prints the JSON report when the mode is unset (SD-01). With a mode set it prints nothing and is develop's `map2check_success`: writes `NONE`, runs the end-of-program verdict, writes the witness files when enabled, and returns; a violating verdict writes its property and aborts (see “Result files”). | -| `set_false_result` | `void set_false_result(int32_t prp, int32_t line_number, const char *function_name)` | `set_false_result` | same | `ViolatedProperty` keeps the C variant order (`caller_lib_result.h:22-31`); the appended `Assert` (8), `Unknown` (9) and `DivByZero` (10) are accepted; unknown values are ignored (SD-09). Ignored when a violation is already recorded (SD-03). | -| `get_current_property` | `enum ViolatedProperty get_current_property(void)` | `get_current_property` | same | | -| `get_current_step` | `uint64_t get_current_step(void)` | `long get_current_step(void)` | changed | Returns `uint64_t` instead of `long`. | -| `get_next_step` | `uint64_t get_next_step(void)` | `get_next_step()` / `next_current_step()` | same | Advances and returns the step counter. `next_current_step` is not exported; see its row. | -| `print_all_containers_as_json` | `void print_all_containers_as_json(void)` | `print_all_containers_as_json` | changed | Report shape and whitespace differ (SD-01, SD-02). An additional channel with its own schema, pinned by a snapshot test; Map2Check reads the result files instead (#14, see “Result files”). | -| `vcc_reset_meta_data` | `void vcc_reset_meta_data(void)` | `vcc_reset_meta_data` | same | Host/test-only: the Map2Check passes never call it. It clears a recorded violation by design, bypassing the first-violation rule (SD-03). | -| `map2check_set_memcleanup` | `void map2check_set_memcleanup(void)` | `map2check_set_memcleanup` | same | Selects `MEMSAFETY_MEMCLEANUP` instead of `MEMSAFETY_MEMTRACK` for the end-of-program leak (SD-04): an alias of the memcleanup mode for the leak tag, in every mode that checks leaks (SD-14). Kept by `map2check_init()` and `map2check_reset()`. | -| `map2check_set_null_is_valid` | `void map2check_set_null_is_valid(void)` | `map2check_set_null_is_valid` | same | Makes a NULL load safe (SD-05). Kept by `map2check_init()` and `map2check_reset()`. | -| `map2check_enable_witness` | `void map2check_enable_witness(void)` | — | new | Turns on witness generation, the run-time counterpart of develop linking `WitnessGeneration.c` instead of `WitnessGenerationNone.c` (`caller.cpp:624-640`). A Map2Check run (mode set) then also writes `track_bb_log.st`, the memory logs and, on a violation, `klee_log.csv` and `map2check_checked_error`. Kept by `map2check_init()` and `map2check_reset()`. The frontend links a constructor that calls it into witness binaries. See “Result files”. | -| `map2check_set_mode` | `void map2check_set_mode(int32_t mode)` | — | new | Selects the analysis mode at run time; `mode` is one of the `enum AnalysisMode` values the header declares (`AnalysisMode_None` 0, `_Memtrack` 1, `_Memcleanup` 2, `_Overflow` 3, `_Assert` 4, `_Target` 5). Develop selects it at link time. Called before `map2check_init()`; kept by `map2check_init()` and `map2check_reset()`. Never called: mode unset, every check active. Any other value is ignored with a diagnostic on stderr and leaves the mode unchanged. See “Analysis modes” (SD-14). | -| `map2check_reach_target` | `void map2check_reach_target(const char *func, int32_t scope, int32_t line)` | — | new | Replaces the pass symbol `map2check_target_function`. Records `REACHABILITY` (develop `TARGET-REACHED`) at `line` in `func`, in every mode; `scope` is ignored. With a mode set, ends the run like any first violation; unset, it returns (SD-15). | -| `map2check_save_nondet_log_int` | `non_det_log_t *map2check_save_nondet_log_int(int line, int scope, enum var_type_t type_var, int *value, const char *function_name)` | returns `non_det_log_t *` | same | Returned record is not linked into a list (SD-10). | -| `map2check_save_nondet_log_uint` | `non_det_log_t *map2check_save_nondet_log_uint(int line, int scope, enum var_type_t type_var, unsigned int *value, const char *function_name)` | returns `non_det_log_t *` | same | Returned record is not linked into a list (SD-10). | -| `map2check_save_nondet_log_long` | `non_det_log_t *map2check_save_nondet_log_long(int line, int scope, enum var_type_t type_var, long *value, const char *function_name)` | returns `non_det_log_t *` | same | Returned record is not linked into a list (SD-10). | -| `map2check_save_nondet_log_float` | `non_det_log_t *map2check_save_nondet_log_float(int line, int scope, enum var_type_t type_var, float *value, const char *function_name)` | returns `non_det_log_t *` | same | Returned record is not linked into a list (SD-10). | -| `map2check_save_nondet_log_double` | `non_det_log_t *map2check_save_nondet_log_double(int line, int scope, enum var_type_t type_var, double *value, const char *function_name)` | returns `non_det_log_t *` | same | Returned record is not linked into a list (SD-10). | -| `map2check_save_nondet_log_char` | `non_det_log_t *map2check_save_nondet_log_char(int line, int scope, enum var_type_t type_var, char *value, const char *function_name)` | returns `non_det_log_t *`; `char` value | same | `char` is `i8` on the Rust side; see “`char` ABI”. Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_unsigned` | `non_det_log_t *map2check_save_nondet_log_unsigned(int line, int scope, int type_var, unsigned int *value, const char *function_name)` | — | new | Develop `map2check_nondet_unsigned` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type UNSIGNED (5), value printed with `%u`; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_ulong` | `non_det_log_t *map2check_save_nondet_log_ulong(int line, int scope, int type_var, unsigned long *value, const char *function_name)` | — | new | Develop `map2check_nondet_ulong` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type ULONG (6), low 32 bits with `%d`; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_uchar` | `non_det_log_t *map2check_save_nondet_log_uchar(int line, int scope, int type_var, unsigned char *value, const char *function_name)` | — | new | Develop `map2check_nondet_uchar` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type UCHAR (8), value zero-extended; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_short` | `non_det_log_t *map2check_save_nondet_log_short(int line, int scope, int type_var, short *value, const char *function_name)` | — | new | Develop `map2check_nondet_short` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type SHORT (14), value zero-extended as the pass does (`-2` is logged as `65534`); see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_ushort` | `non_det_log_t *map2check_save_nondet_log_ushort(int line, int scope, int type_var, unsigned short *value, const char *function_name)` | — | new | Develop `map2check_nondet_ushort` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type USHORT (3), value zero-extended; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_bool` | `non_det_log_t *map2check_save_nondet_log_bool(int line, int scope, int type_var, int *value, const char *function_name)` | — | new | Develop `map2check_nondet_bool` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type BOOL (7); develop's `bool` is `typedef int bool` (`NonDetGenerator.h:74`), so the pass widens the `_Bool` to `int` before storing it; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_size_t` | `non_det_log_t *map2check_save_nondet_log_size_t(int line, int scope, int type_var, size_t *value, const char *function_name)` | — | new | Develop `map2check_nondet_size_t` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type SIZE_T (10), low 32 bits with `%d`; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_loff_t` | `non_det_log_t *map2check_save_nondet_log_loff_t(int line, int scope, int type_var, long long *value, const char *function_name)` | — | new | Develop `map2check_nondet_loff_t` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type LOFF_T (11); `loff_t` is glibc's `long long`; low 32 bits with `%d`; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_sector_t` | `non_det_log_t *map2check_save_nondet_log_sector_t(int line, int scope, int type_var, unsigned long *value, const char *function_name)` | — | new | Develop `map2check_nondet_sector_t` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type SECTOR_T (12); `sector_t` is `unsigned long` (`NonDetGenerator.h:112`); low 32 bits with `%d`; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_pointer` | `non_det_log_t *map2check_save_nondet_log_pointer(int line, int scope, int type_var, void **value, const char *function_name)` | — | new | Develop `map2check_nondet_pointer` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type POINTER (2); the address, logged as its low 32 bits (the pass's `ptrtoint` to `i32`); see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_nondet_log_pchar` | `non_det_log_t *map2check_save_nondet_log_pchar(int line, int scope, int type_var, char **value, const char *function_name)` | — | new | Develop `map2check_nondet_pchar` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type PCHAR (9); the string's address, logged as its low 32 bits; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | -| `map2check_save_basic_block_log` | `bbtrack_log_t *map2check_save_basic_block_log(int32_t line, const char *function_name)` | returns `bbtrack_log_t *` | same | Returned record is not linked into a list (SD-10). | -| `map2check_is_in_trackbb_container` | `int32_t map2check_is_in_trackbb_container(int32_t line)` | `map2check_is_in_trackbb_container` | same | | -| `map2check_map_alloca` | `void map2check_map_alloca(const char *var_name, const void *ptr_address, int32_t size, int32_t size_primitive, int32_t line_number, int32_t scope)` | `container_memtracklog.h:41` | same | | -| `map2check_map_non_static_alloca` | `void map2check_map_non_static_alloca(const char *var_name, const void *ptr_address, int32_t size, int32_t size_primitive, int32_t line_number, int32_t scope)` | `container_memtracklog.h:44` | same | `size` is an element count; static slot of `size * size_primitive` bytes, never a leak (SD-12, #38). | -| `map2check_map_funct_address` | `void map2check_map_funct_address(const char *var_name, const void *ptr_address)` | `container_memtracklog.h:48` | same | Signature and semantics reconstructed; see its section. | -| `map2check_map_malloc` | `void map2check_map_malloc(const void *ptr_address, int32_t size)` | `map2check_map_malloc` | same | Negative sizes are ignored (SD-09). | -| `map2check_map_calloc` | `void map2check_map_calloc(const void *ptr_address, int32_t quantity, int32_t size)` | `map2check_map_calloc` | same | Negative sizes are ignored (SD-09). | -| `map2check_map_posix_memalign` | `void map2check_map_posix_memalign(const void *ptr_address, int32_t size)` | `map2check_posix` (develop) | new | `ptr_address` is posix_memalign's `memptr` (`void **`): reads the block from it, records it as `map2check_map_malloc`, then the store of the block into `*ptr_address` with scope 1, line 1, name `pointer`, function `cocoiso`, as develop's `map2check_posix` (`AnalysisModeMemtrack.c:327-333`); golden `POSIX-MEMALIGN`. NULL `ptr_address` ignored (SD-09); NULL block: as `map2check_map_malloc`, and no store. | -| `map2check_map_free` | `void map2check_map_free(const char *var_name, const void *var_address, const void *ptr_address, uint32_t scope, uint32_t line_number, const char *function_name)` | `map2check_map_free` | changed | Task 2.5: gains `var_address`, the address of the pointer variable freed (the slot develop's `map2check_free` receives); `ptr_address` stays the block, which the release is keyed on. The slot only names the variable in the FREE row of `list_log.csv` (`AnalysisModeMemtrack.c:339-347`). | -| `map2check_map_store_pointer` | `void map2check_map_store_pointer(const void *var_address, const void *value, uint32_t scope, const char *var_name, int32_t line_number, const char *funct_name)` | `map2check_map_store_pointer` | same | | -| `map2check_is_valid_assert` | `void map2check_is_valid_assert(int32_t line_number, const char *function_name, int32_t expression)` | `map2check_is_valid_assert` | changed | A failed assertion reports `ASSERT` (before Task 2.3: `REACHABILITY`) and, with the mode unset, prints the JSON report; with a mode set it ends the run (“Result files”) and prints nothing. Active in unset and assert modes only (SD-14). | -| `map2check_binop_add_int` | `void map2check_binop_add_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_add_int` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_sub_int` | `void map2check_binop_sub_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_sub_int` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_mul_int` | `void map2check_binop_mul_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_mul_int` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_div_int` | `void map2check_binop_div_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_div_int` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_neg_int` | `void map2check_binop_neg_int(int32_t param1, int32_t _param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_neg_int` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_shl_int` | `void map2check_binop_shl_int(int32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_shl_int` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_shr_int` | `void map2check_binop_shr_int(int32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_shr_int` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_add_unsigned` | `void map2check_binop_add_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_add_unsigned` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_sub_unsigned` | `void map2check_binop_sub_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_sub_unsigned` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_mul_unsigned` | `void map2check_binop_mul_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_mul_unsigned` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_div_unsigned` | `void map2check_binop_div_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_div_unsigned` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_shr_unsigned` | `void map2check_binop_shr_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_shr_unsigned` | same | `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | -| `map2check_binop_add_signed` | `void map2check_binop_add_signed(int64_t a, int64_t b, uint32_t bits, uint32_t line, const char *function_name)` | `map2check_binop_add` (develop) | changed | Operands sign-extended to 64 bits, `bits` the width of the result type (0 or above 64 means 64), as the pass sends them; reports `OVERFLOW` when the exact sum leaves the range of `bits` (`AnalysisModeOverflow.c:110-276`). No `scope` argument. Records and continues with the mode unset; ends the run with a mode set (SD-03). Active in unset and overflow modes only (SD-14). | -| `map2check_binop_sub_signed` | `void map2check_binop_sub_signed(int64_t a, int64_t b, uint32_t bits, uint32_t line, const char *function_name)` | `map2check_binop_sub` (develop) | changed | Operands sign-extended to 64 bits, `bits` the width of the result type (0 or above 64 means 64), as the pass sends them; reports `OVERFLOW` when the exact difference leaves the range of `bits` (`AnalysisModeOverflow.c:110-276`). No `scope` argument. Records and continues with the mode unset; ends the run with a mode set (SD-03). Active in unset and overflow modes only (SD-14). | -| `map2check_binop_mul_signed` | `void map2check_binop_mul_signed(int64_t a, int64_t b, uint32_t bits, uint32_t line, const char *function_name)` | `map2check_binop_mul` (develop) | changed | Operands sign-extended to 64 bits, `bits` the width of the result type (0 or above 64 means 64), as the pass sends them; reports `OVERFLOW` when the exact product leaves the range of `bits` (`AnalysisModeOverflow.c:110-276`). No `scope` argument. Records and continues with the mode unset; ends the run with a mode set (SD-03). Active in unset and overflow modes only (SD-14). | -| `map2check_binop_div_signed` | `void map2check_binop_div_signed(int64_t a, int64_t b, uint32_t bits, uint32_t line, const char *function_name)` | `map2check_binop_sdiv` (develop) | changed | Operands sign-extended to 64 bits, `bits` the width of the result type (0 or above 64 means 64), as the pass sends them; reports `DIVBYZERO` (`ViolatedProperty` 10, develop `FALSE-DIVBYZERO`) when `b` is 0, `OVERFLOW` for MIN / -1 of `bits` (`AnalysisModeOverflow.c:110-276`). No `scope` argument. Records and continues with the mode unset; ends the run with a mode set (SD-03). Active in unset and overflow modes only (SD-14). | -| `map2check_binop_rem_signed` | `void map2check_binop_rem_signed(int64_t a, int64_t b, uint32_t bits, uint32_t line, const char *function_name)` | `map2check_binop_srem` (develop) | changed | Operands sign-extended to 64 bits, `bits` the width of the result type (0 or above 64 means 64), as the pass sends them; reports `DIVBYZERO` when `b` is 0, `OVERFLOW` for MIN % -1 of `bits` (`AnalysisModeOverflow.c:110-276`). No `scope` argument. Records and continues with the mode unset; ends the run with a mode set (SD-03). Active in unset and overflow modes only (SD-14). | -| `map2check_check_load` | `void map2check_check_load(const void *ptr, int32_t line, uint32_t scope, int32_t size, const char *function_name)` | `map2check_check_load` | changed | Address-validity rule rewritten (plan R1; differential check in #15); since #11 a heap block is valid only while its latest alloc/free event is an allocation, as in `develop` (`AllocationLog.c:127-148`). The first violation is final; a later check never clears or replaces it (SD-03); NULL handling in SD-05. Active in unset and memtrack modes only (SD-14). | -| `map2check_check_free` | `void map2check_check_free(const char *name, const void *var_address, const void *ptr, uint32_t scope, uint32_t line, const char *function_name)` | `map2check_check_free` | changed | Task 2.5: same arguments as `map2check_map_free` (gains `var_address`; `name` and `scope` are now used): on a first violation it keeps the FREE row develop's `map2check_free` appends to `list_log.csv` before it fails (`AnalysisModeMemtrack.c:346-358`). The check itself is unchanged and keyed on `ptr`. Untracked address is a violation (SD-06). Active in unset and memtrack modes only (SD-14). | -| `map2check_check_free_resolved_address` | `void map2check_check_free_resolved_address(const void *ptr, uint32_t line, const char *function_name, int16_t is_null_valid)` | `analysis_memory.h:46` | same | Semantics in its section; records the release when the check passes, unlike `map2check_check_free`; untracked address is a violation (SD-06); see also SD-13. The check is active in unset and memtrack modes only; when inactive the release is still recorded (SD-14). | -| `map2check_check_deref` | `void map2check_check_deref(const void *ptr, uint32_t scope, uint32_t line, const char *function_name)` | `map2check_check_deref` | same | Name collides with the pass ABI `map2check_check_deref(i64, ptr)`; see the pass matrix (#21). Active in unset and memtrack modes only (SD-14). | -| `map2check_check_mem_endprog` | `void map2check_check_mem_endprog(void)` | `map2check_check_mem_endprog` | changed | Leak algorithm rewritten. Runs `analysis_is_program_correct` of the mode: unset and memtrack report memtrack or memcleanup by option, memcleanup mode reports memcleanup only for a still-referenced last leak, the other modes nothing (SD-04, SD-14). | -| `LLVMFuzzerTestOneInput` | `int32_t LLVMFuzzerTestOneInput(const uint8_t *data, uintptr_t size)` | `LLVMFuzzerTestOneInput` | same | Feature `libfuzzer`. Resets the analysis state on every iteration (SD-11). | -| `__VERIFIER_nondet_bool` | `bool __VERIFIER_nondet_bool(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads four bytes, develop's `typedef int bool`, and returns that `int != 0` (SD-18). | -| `__VERIFIER_nondet_char` | `int8_t __VERIFIER_nondet_char(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_uchar` | `uint8_t __VERIFIER_nondet_uchar(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_short` | `int16_t __VERIFIER_nondet_short(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_ushort` | `uint16_t __VERIFIER_nondet_ushort(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_int` | `int32_t __VERIFIER_nondet_int(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_uint` | `uint32_t __VERIFIER_nondet_uint(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_long` | `int64_t __VERIFIER_nondet_long(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_ulong` | `uint64_t __VERIFIER_nondet_ulong(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_float` | `float __VERIFIER_nondet_float(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads four bytes; develop has no `float` generator (SD-18). | -| `__VERIFIER_nondet_double` | `double __VERIFIER_nondet_double(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_size_t` | `uintptr_t __VERIFIER_nondet_size_t(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_pointer` | `void *__VERIFIER_nondet_pointer(void)` | — | new | Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | -| `__VERIFIER_nondet_loff_t` | `int64_t __VERIFIER_nondet_loff_t(void)` | `map2check_gen_data_fuzzer_loff_t` | new | Feature `libfuzzer`. Core target of the pass's `map2check_non_det_loff_t` (#6); `loff_t` is glibc's `long long`, eight bytes (SD-18). | -| `__VERIFIER_nondet_sector_t` | `unsigned long __VERIFIER_nondet_sector_t(void)` | `map2check_gen_data_fuzzer_sector_t` | new | Feature `libfuzzer`. Core target of `map2check_non_det_sector_t` (#6); develop's `sector_t` is `unsigned long` (`NonDetGenerator.h:112`), eight bytes (SD-18). | -| `__VERIFIER_nondet_pchar` | `char *__VERIFIER_nondet_pchar(void)` | `map2check_gen_data_fuzzer_pchar` | new | Feature `libfuzzer`. Core target of `map2check_non_det_pchar` (#6). An `unsigned` length, NULL for 0, capped at 4096, then `length - 1` chars and a NUL, as `NonDetGeneratorAFL.c:101-118`; the string comes from the C `malloc` and the program may `free` it (SD-18). | -| `__VERIFIER_assume` | `void __VERIFIER_assume(int32_t condition)` | — | new | Feature `libfuzzer`. `__VERIFIER_assume(0)` abandons the current fuzzer input and `LLVMFuzzerTestOneInput` returns 0, as develop's `nondet_cancel`; with no iteration running it calls `exit(0)` (SD-07). Defined `extern "C-unwind"`. The analysed program must be compiled with `-fexceptions` (D1 condition 11); see “`__VERIFIER_assume` under a fuzzer”. | -| `map2check_fuzzer_assume` | `void map2check_fuzzer_assume(int32_t condition)` | `map2check_fuzzer_assume(int)` (`nondet_gen_libfuzzer.h:41`) | changed | Feature `libfuzzer`. Same behaviour as `__VERIFIER_assume` (SD-07). | +| `set_false_result` | `void set_false_result(int32_t prp, int32_t line_number, const char *function_name)` | `set_false_result` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): `prp` is the C value of `enum ViolatedProperty` (`Map2CheckTypes.h:80-91`: `NONE` 0 … `FALSE_DIVBYZERO` 9) and 10 is `Concurrency` (Task 2.1; before, the order of `caller_lib_result.h:22-31`). Unknown values are ignored (SD-09). Ignored when a violation is already recorded (SD-03). | +| `get_current_property` | `enum ViolatedProperty get_current_property(void)` | `get_current_property` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Returns the C values of `enum ViolatedProperty` (see `set_false_result`). | +| `get_current_step` | `uint64_t get_current_step(void)` | `long get_current_step(void)` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Returns `uint64_t` instead of `long`. | +| `get_next_step` | `uint64_t get_next_step(void)` | `get_next_step()` / `next_current_step()` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Advances and returns the step counter. `next_current_step` is not exported; see its row. | +| `print_all_containers_as_json` | `void print_all_containers_as_json(void)` | `print_all_containers_as_json` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Report shape and whitespace differ (SD-01, SD-02). An additional channel with its own schema, pinned by a snapshot test; Map2Check reads the result files instead (#14, see “Result files”). | +| `vcc_reset_meta_data` | `void vcc_reset_meta_data(void)` | `vcc_reset_meta_data` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Host/test-only: the Map2Check passes never call it. It clears a recorded violation by design, bypassing the first-violation rule (SD-03). | +| `map2check_set_memcleanup` | `void map2check_set_memcleanup(void)` | `map2check_set_memcleanup` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Selects `MEMSAFETY_MEMCLEANUP` instead of `MEMSAFETY_MEMTRACK` for the end-of-program leak (SD-04): an alias of the memcleanup mode for the leak tag, in every mode that checks leaks (SD-14). Kept by `map2check_init()` and `map2check_reset()`. | +| `map2check_set_null_is_valid` | `void map2check_set_null_is_valid(void)` | `map2check_set_null_is_valid` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Makes a NULL load safe (SD-05). Kept by `map2check_init()` and `map2check_reset()`. | +| `map2check_enable_witness` | `void map2check_enable_witness(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Turns on witness generation, the run-time counterpart of develop linking `WitnessGeneration.c` instead of `WitnessGenerationNone.c` (`caller.cpp:624-640`). A Map2Check run (mode set) then also writes `track_bb_log.st`, the memory logs and, on a violation, `klee_log.csv` and `map2check_checked_error`. Kept by `map2check_init()` and `map2check_reset()`. The frontend links a constructor that calls it into witness binaries. See “Result files”. | +| `map2check_set_mode` | `void map2check_set_mode(int32_t mode)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Selects the analysis mode at run time; `mode` is one of the `enum AnalysisMode` values the header declares (`AnalysisMode_None` 0, `_Memtrack` 1, `_Memcleanup` 2, `_Overflow` 3, `_Assert` 4, `_Target` 5). Develop selects it at link time. Called before `map2check_init()`; kept by `map2check_init()` and `map2check_reset()`. Never called: mode unset, every check active. Any other value is ignored with a diagnostic on stderr and leaves the mode unchanged. See “Analysis modes” (SD-14). | +| `map2check_reach_target` | `void map2check_reach_target(const char *func, int32_t scope, int32_t line)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Replaces the pass symbol `map2check_target_function`. Records `REACHABILITY` (develop `TARGET-REACHED`) at `line` in `func`, in every mode; `scope` is ignored. With a mode set, ends the run like any first violation; unset, it returns (SD-15). | +| `map2check_track_bb` | `void map2check_track_bb(unsigned int line, const char *function_name)` | — | new | The C runtime export of the same name (`Map2CheckFunctions.c:49-55`, Task 2.1): records the basic block at `line` unless a block of that line is already recorded, so `track_bb_log.st` has one row per line with its first function name. | +| `map2check_target_function` | `void map2check_target_function(const char *func_name, int scope, int line)` | — | new | The C runtime export of the same name (`Map2CheckFunctions.c:61-65`, Task 2.1): `map2check_reach_target` under the C name. | +| `map2check_get_current_step` | `unsigned int map2check_get_current_step(void)` | — | new | develop's `Map2CheckCurrentStep` (`Map2CheckFunctions.c:42`, Task 2.1): 0 after `map2check_init`, advanced only by `map2check_next_current_step`; not the step of `get_current_step`. | +| `map2check_next_current_step` | `void map2check_next_current_step(void)` | — | new | `Map2CheckFunctions.c:43` (Task 2.1): adds 1, wrapping as a C `unsigned`. | +| `map2check_error` | `void map2check_error(void)` | — | new | `Map2CheckFunctions.c:72-75` (Task 2.1): marks the run failed and runs `map2check_exit`, which skips the verdict; with a mode set it writes the witness files (when enabled) and aborts, writing no property file. Without a mode it returns (SD-03, SD-14). | +| `map2check_exit` | `void map2check_exit(void)` | — | new | `Map2CheckFunctions.c:89-102` (Task 2.1): `map2check_success` without the `NONE`: the verdict of the mode when no error is recorded, the witness files when enabled, `map2check_destroy`, and on an error the confirmation file and the abort. Without a mode nothing is written and it returns. | +| `map2check_destroy` | `void map2check_destroy(void)` | — | new | `Map2CheckFunctions.c:77-87` (Task 2.1): releases the basic-block log and the containers of the mode once per run (develop: once per process). | +| `write_property` | `void write_property(enum ViolatedProperty violated, int line, const char *function_name)` | — | new | `PropertyGenerator.c:50-106` (Task 2.1): writes `map2check_property` and, when the property has one, `map2check_property_`, byte for byte as develop, in every mode; records nothing. `violated` takes the C values; `Concurrency` leaves an empty `map2check_property`. The `write_property_` helpers stay private (SD-22). | +| `__m2c_rt_witness_generate_aux_files` | `void __m2c_rt_witness_generate_aux_files(void *trackbb_log, char violation)` | — | new | Core entry point that the IR module `WitnessGeneration.bc` forwards `generate_aux_files` to (D7, Task 2.1): `track_bb_log.st`, the memory logs in memtrack/memcleanup mode, and `klee_log.csv` when `violation` is nonzero or `MAP2CHECK_SEED_REPLAY` is set (`WitnessGeneration.c:15-29`). `trackbb_log` is ignored and may be NULL. | +| `__m2c_rt_witness_generate_confirmation_file` | `void __m2c_rt_witness_generate_confirmation_file(void)` | — | new | Core entry point of `generate_confirmation_file` (`WitnessGeneration.c:32-36`, D7, Task 2.1): writes `END OF TIME` to `map2check_checked_error`. | +| `__m2c_rt_witness_enable` | `void __m2c_rt_witness_enable(void)` | — | new | 2.0.0 (Task 2.10): the C ABI of the 1.x `map2check_enable_witness`, same behaviour: a run with a mode then also writes `track_bb_log.st`, the memory logs and, on a violation, `klee_log.csv` and `map2check_checked_error`; kept by `map2check_init()`. For host programs that link the staticlib (`tests/ffi_harness.c`, the link tests); develop selects the witness by linking `WitnessGeneration.bc` (`caller.cpp:624-640`), and no IR module calls it. Task 3.1: the drop-in core (`Map2CheckFunctions.bc`) ignores it and calls the witness module it is linked with, as the C does; kept because the staticlib cannot reference the module functions (undefined in `cargo test` and in programs that link the staticlib alone). | +| `__m2c_rt_nondet_log_to_file` | `char __m2c_rt_nondet_log_to_file(void)` | — | new | Core entry point of `nondet_log_to_file` (`NonDetLog.c:50-125`, D7, Task 2.2), last step of the KLEE IR's `nondet_generate_aux_witness_files`: writes `klee_log.csv` in the current directory from the C nondet log, byte for byte as the C runtime; returns 1, or 0 when the file cannot be written. **Prerequisites the IR must do first** (`NonDetGeneratorKlee.c:33-62`): skip the call when `__m2c_rt_nondet_log_len() > 1024`, and pin each row with `__m2c_rt_nondet_log_get_i64` / `klee_get_value_i64` / `klee_assume` / `__m2c_rt_nondet_log_set_i64`. | +| `__m2c_rt_output_set_atomic` | `void __m2c_rt_output_set_atomic(int on)` | — | new | Task 2.8, for the KLEE IR: `NonDetGeneratorKlee.ll`'s `nondet_init` calls it with 1, so the result files go through a temporary file and a rename (SD-20); 0 writes them in place as develop's `fopen("w")`. The drop-in core starts plain (AFL++ and `none` write as the C runtime); every other build starts atomic. Approved by the user on 2026-10-27. | +| `__m2c_rt_nondet_log_len` | `unsigned int __m2c_rt_nondet_log_len(void)` | — | new | Task 2.2, for the KLEE IR: number of rows of the C nondet log (`NonDetGeneratorKlee.c:57-58`). | +| `__m2c_rt_nondet_log_get_i64` | `int64_t __m2c_rt_nondet_log_get_i64(unsigned int index)` | — | new | Task 2.2, for the KLEE IR: the 8 bytes of the value union of row `index` as `long long` (`memcpy`, `NonDetGeneratorKlee.c:39`): `int` types zero-extended, 64-bit types and doubles by bit pattern; 0 when out of range. | +| `__m2c_rt_nondet_log_set_i64` | `void __m2c_rt_nondet_log_set_i64(unsigned int index, int64_t value)` | — | new | Task 2.2, for the KLEE IR: overwrites the 8 bytes of the value union of row `index` (`memcpy`, `NonDetGeneratorKlee.c:42`); no-op when out of range. | +| `__m2c_rt_overflow_analysis_init` | `void __m2c_rt_overflow_analysis_init(void)` | — | new | Core entry point that `AnalysisModeOverflow.bc` forwards `analysis_init` to (D7, Task 2.3). The C body is empty (`AnalysisModeOverflow.c:279`); linking the module is what picks the mode, so this selects `AnalysisMode_Overflow` (result files and abort on the first violation, SD-14) and changes nothing else. | +| `__m2c_rt_overflow_analysis_destroy` | `void __m2c_rt_overflow_analysis_destroy(void)` | — | new | Task 2.3, forwarded from `analysis_destroy`: empty, as `AnalysisModeOverflow.c:281`. | +| `__m2c_rt_overflow_analysis_is_program_correct` | `char __m2c_rt_overflow_analysis_is_program_correct(void)` | — | new | Task 2.3, forwarded from `analysis_is_program_correct`: always 1 (TRUE), as `AnalysisModeOverflow.c:283`. | +| `__m2c_rt_overflow_analysis_generate_aux_witness_files` | `void __m2c_rt_overflow_analysis_generate_aux_witness_files(void)` | — | new | Task 2.3, forwarded from `analysis_generate_aux_witness_files`: empty, as `AnalysisModeOverflow.c:285`. | +| `__m2c_rt_overflow_overflowError` | `void __m2c_rt_overflow_overflowError(unsigned int line, const char *function_name)` | — | new | Task 2.3, forwarded from `overflowError` (`AnalysisModeOverflow.c:185-188`): records `OVERFLOW` at `line` in `function_name` (first violation final, SD-03); with a mode set, `map2check_property` and `map2check_property_overflow` get `OVERFLOW` / `Line: %d` / `Function: %s` and the process aborts. NULL name reads as "". | +| `__m2c_rt_overflow_map2check_binop_add` | `void __m2c_rt_overflow_map2check_binop_add(int64_t param1, int64_t param2, unsigned int width, unsigned int line, unsigned int scope, const char *function_name)` | — | new | Task 2.3, forwarded from `map2check_binop_add` (`AnalysisModeOverflow.c:197-276`): operands sign-extended to 64 bits, `width` the bit width of the result type (0 or 64 and above mean 64); `OVERFLOW` when the sum leaves the range of `width`. No mode gate (the module is linked only in overflow mode); `scope` unused, as in C. The legacy `map2check_binop_*_signed` export stays until Task 2.10. | +| `__m2c_rt_overflow_map2check_binop_sub` | `void __m2c_rt_overflow_map2check_binop_sub(int64_t param1, int64_t param2, unsigned int width, unsigned int line, unsigned int scope, const char *function_name)` | — | new | Task 2.3, forwarded from `map2check_binop_sub` (`AnalysisModeOverflow.c:197-276`): operands sign-extended to 64 bits, `width` the bit width of the result type (0 or 64 and above mean 64); `OVERFLOW` when the difference leaves the range of `width`. No mode gate (the module is linked only in overflow mode); `scope` unused, as in C. The legacy `map2check_binop_*_signed` export stays until Task 2.10. | +| `__m2c_rt_overflow_map2check_binop_mul` | `void __m2c_rt_overflow_map2check_binop_mul(int64_t param1, int64_t param2, unsigned int width, unsigned int line, unsigned int scope, const char *function_name)` | — | new | Task 2.3, forwarded from `map2check_binop_mul` (`AnalysisModeOverflow.c:197-276`): operands sign-extended to 64 bits, `width` the bit width of the result type (0 or 64 and above mean 64); `OVERFLOW` when the product leaves the range of `width`. No mode gate (the module is linked only in overflow mode); `scope` unused, as in C. The legacy `map2check_binop_*_signed` export stays until Task 2.10. | +| `__m2c_rt_overflow_map2check_binop_sdiv` | `void __m2c_rt_overflow_map2check_binop_sdiv(int64_t param1, int64_t param2, unsigned int width, unsigned int line, unsigned int scope, const char *function_name)` | — | new | Task 2.3, forwarded from `map2check_binop_sdiv` (`AnalysisModeOverflow.c:197-276`): operands sign-extended to 64 bits, `width` the bit width of the result type (0 or 64 and above mean 64); `FALSE-DIVBYZERO` for a zero divisor (only `map2check_property` is written, `PropertyGenerator.c:98-102`), `OVERFLOW` for MIN / -1 of `width`. No mode gate (the module is linked only in overflow mode); `scope` unused, as in C. The legacy `map2check_binop_*_signed` export stays until Task 2.10. | +| `__m2c_rt_overflow_map2check_binop_srem` | `void __m2c_rt_overflow_map2check_binop_srem(int64_t param1, int64_t param2, unsigned int width, unsigned int line, unsigned int scope, const char *function_name)` | — | new | Task 2.3, forwarded from `map2check_binop_srem` (`AnalysisModeOverflow.c:197-276`): operands sign-extended to 64 bits, `width` the bit width of the result type (0 or 64 and above mean 64); `FALSE-DIVBYZERO` for a zero divisor, `OVERFLOW` for MIN % -1 of `width`. No mode gate (the module is linked only in overflow mode); `scope` unused, as in C. The legacy `map2check_binop_*_signed` export stays until Task 2.10. | +| `__m2c_rt_overflow_map2check_binop_add_uint` | `void __m2c_rt_overflow_map2check_binop_add_uint(uint64_t param1, uint64_t param2, unsigned int width, unsigned int line, unsigned int scope, const char *function_name)` | — | new | Task 2.3, forwarded from `map2check_binop_add_uint`: a deliberate no-op, as in C (`AnalysisModeOverflow.c:219-248`: unsigned wraparound is not an overflow). | +| `__m2c_rt_overflow_map2check_binop_sub_uint` | `void __m2c_rt_overflow_map2check_binop_sub_uint(uint64_t param1, uint64_t param2, unsigned int width, unsigned int line, unsigned int scope, const char *function_name)` | — | new | Task 2.3, forwarded from `map2check_binop_sub_uint`: a deliberate no-op, as in C (`AnalysisModeOverflow.c:219-248`: unsigned wraparound is not an overflow). | +| `__m2c_rt_overflow_map2check_binop_mul_uint` | `void __m2c_rt_overflow_map2check_binop_mul_uint(uint64_t param1, uint64_t param2, unsigned int width, unsigned int line, unsigned int scope, const char *function_name)` | — | new | Task 2.3, forwarded from `map2check_binop_mul_uint`: a deliberate no-op, as in C (`AnalysisModeOverflow.c:219-248`: unsigned wraparound is not an overflow). | +| `__m2c_rt_assert_analysis_init` | `void __m2c_rt_assert_analysis_init(void)` | — | new | Core entry point that `AnalysisModeAssert.bc` forwards `analysis_init` to (D7, Task 2.4). The C body is empty (`AnalysisModeAssert.c:29-31`); linking the module is what picks the mode, so this selects `AnalysisMode_Assert` (result files and abort on the first violation, SD-14) and changes nothing else. | +| `__m2c_rt_assert_analysis_destroy` | `void __m2c_rt_assert_analysis_destroy(void)` | — | new | Task 2.4, forwarded from `analysis_destroy`: empty, as `AnalysisModeAssert.c:33-35`. | +| `__m2c_rt_assert_analysis_is_program_correct` | `char __m2c_rt_assert_analysis_is_program_correct(void)` | — | new | Task 2.4, forwarded from `analysis_is_program_correct`: always 1 (TRUE), as `AnalysisModeAssert.c:37`. | +| `__m2c_rt_assert_analysis_generate_aux_witness_files` | `void __m2c_rt_assert_analysis_generate_aux_witness_files(void)` | — | new | Task 2.4, forwarded from `analysis_generate_aux_witness_files`: empty, as `AnalysisModeAssert.c:39-41`. | +| `__m2c_rt_assert_map2check_assert` | `void __m2c_rt_assert_map2check_assert(int condition, int line, const char *func_name)` | — | new | Task 2.4, forwarded from `map2check_assert` (`AnalysisModeAssert.c:18-23`): when `condition` is 0, records `ASSERT` at `line` in `func_name` (first violation final, SD-03); with a mode set, `map2check_property` and `map2check_property_assert` get `ASSERT` / `Line: %d` / `Function: %s` and the process aborts. Any other `condition` does nothing. NULL name reads as "". `__VERIFIER_assert` is an empty function in `AnalysisModeAssert.bc` (`:25-27`) and has no core entry point; the legacy `__VERIFIER_assert` export stays until Task 2.10. | +| `__m2c_rt_none_analysis_init` | `void __m2c_rt_none_analysis_init(void)` | — | new | Core entry point that `AnalysisModeNone.bc` forwards `analysis_init` to (D7, Task 2.4). The C body is empty (`AnalysisModeNone.c:11-13`); linking the module is what picks the mode, so this selects `AnalysisMode_None` (result files and abort on the first violation, SD-14) and changes nothing else. `REACHABILITY_MODE` and `COVER_BRANCHES_MODE` both link this module, so it selects `AnalysisMode_None`, whose checks are all inactive (`map2check_target_function` records the target regardless of mode). | +| `__m2c_rt_none_analysis_destroy` | `void __m2c_rt_none_analysis_destroy(void)` | — | new | Task 2.4, forwarded from `analysis_destroy`: empty, as `AnalysisModeNone.c:15-17`. | +| `__m2c_rt_none_analysis_is_program_correct` | `char __m2c_rt_none_analysis_is_program_correct(void)` | — | new | Task 2.4, forwarded from `analysis_is_program_correct`: always 1 (TRUE), as `AnalysisModeNone.c:19`. | +| `__m2c_rt_none_analysis_generate_aux_witness_files` | `void __m2c_rt_none_analysis_generate_aux_witness_files(void)` | — | new | Task 2.4, forwarded from `analysis_generate_aux_witness_files`: empty, as `AnalysisModeNone.c:21-23`. | +| `__m2c_rt_memtrack_analysis_init` | `void __m2c_rt_memtrack_analysis_init(void)` | — | new | Core entry point that `AnalysisModeMemtrack.bc` forwards `analysis_init` to (D7, Task 2.5): selects `AnalysisMode_Memtrack` (linking the module is what picks the mode in C) and starts new heap, allocation and list logs with `ERROR_DEREF` clear, as `AnalysisModeMemtrack.c:125-130`. | +| `__m2c_rt_memtrack_analysis_destroy` | `void __m2c_rt_memtrack_analysis_destroy(void)` | — | new | Task 2.5, forwarded from `analysis_destroy`: frees the three logs, as `AnalysisModeMemtrack.c:132-136`. | +| `__m2c_rt_memtrack_analysis_is_program_correct` | `char __m2c_rt_memtrack_analysis_is_program_correct(void)` | — | new | Task 2.5, forwarded from `analysis_is_program_correct` (`AnalysisModeMemtrack.c:138-146`): `valid_allocation_log` (`AllocationLog.c:69-101`) appends a freed row for every leaked block; on a leak `map2check_property` and `map2check_property_memtrack` get `FALSE-MEMTRACK` and it returns 0, else 1. It records nothing and does not end the run. develop also calls `free()` on the leaked blocks (`:91`); the core does not (the process ends right after). | +| `__m2c_rt_memtrack_analysis_generate_aux_witness_files` | `void __m2c_rt_memtrack_analysis_generate_aux_witness_files(void)` | — | new | Task 2.5, forwarded from `analysis_generate_aux_witness_files` (`AnalysisModeMemtrack.c:148-152`): writes `heap_log.csv`, `allocation_log.csv` and `list_log.csv` from the drop-in logs, in the C formats. | +| `__m2c_rt_memtrack_map2check_check_cstring` | `void __m2c_rt_memtrack_map2check_check_cstring(const char *string)` | — | new | Task 2.5, forwarded from `map2check_check_cstring` (`AnalysisModeMemtrack.c:159-166`): each byte up to the terminator must lie in a tracked block (`map2check_load` of 1 byte); stops before the first invalid byte with `ERROR_DEREF` set. NULL does nothing. | +| `__m2c_rt_memtrack_map2check_load` | `void __m2c_rt_memtrack_map2check_load(const void *ptr, int size)` | — | new | Task 2.5, forwarded from `map2check_load` (`AnalysisModeMemtrack.c:168-174`): sets `ERROR_DEREF` when no heap-log row (`HeapLog.c:37-52`) and no live allocation (`AllocationLog.c:127-148`) covers the access. Takes `int size` as the C definition; the pass declares `i64` (MemoryTrackPass.cpp:885). | +| `__m2c_rt_memtrack_map2check_free_resolved_address` | `void __m2c_rt_memtrack_map2check_free_resolved_address(const void *ptr, unsigned int line, const char *function_name, short is_null_valid)` | — | new | Task 2.5, forwarded from `map2check_free_resolved_address` (`AnalysisModeMemtrack.c:176-195`): `is_invalid_free` (`ListLog.c:95-118`) records `FALSE-FREE`; otherwise the row is freed and the references updated. A block never stored through a pointer is `FALSE-FREE`, as in develop (SD-13 stays for the legacy export). A release with no allocation row (literal `free(NULL)`) goes on where develop crashes (SD-23). `short isNullValid` as the C definition; the pass declares `i1`. | +| `__m2c_rt_memtrack_map2check_add_store_pointer` | `void __m2c_rt_memtrack_map2check_add_store_pointer(const void *var, const void *value, unsigned int scope, const char *name, int line, const char *function_name)` | — | new | Task 2.5, forwarded from `map2check_add_store_pointer` (`AnalysisModeMemtrack.c:258-307`): a list row (C step) unless redundant, then `update_reference_list_log`. | +| `__m2c_rt_memtrack_map2check_check_deref` | `void __m2c_rt_memtrack_map2check_check_deref(int line, const char *function_name)` | — | new | Task 2.5, forwarded from `map2check_check_deref` (`AnalysisModeMemtrack.c:309-315`): with `ERROR_DEREF` set records `FALSE-DEREF` at `line`. The C signature `(int, const char *)`; the legacy export `map2check_check_deref(const void *, uint32_t, uint32_t, const char *)` stays until Task 2.10, so the IR forwarder and the staticlib still clash by name. | +| `__m2c_rt_memtrack_map2check_malloc` | `void __m2c_rt_memtrack_map2check_malloc(const void *ptr, int size)` | — | new | Task 2.5, forwarded from `map2check_malloc` (`AnalysisModeMemtrack.c:317-335`): revives the row of a known address or appends one; a block whose low 32 bits are zero (`int addr = (long)ptr`) records `UNKNOWN` (`map2check_property__unknown`). | +| `__m2c_rt_memtrack_map2check_posix` | `void __m2c_rt_memtrack_map2check_posix(const void *ptr, int size)` | — | new | Task 2.5, forwarded from `map2check_posix` (`AnalysisModeMemtrack.c:340-345`): `map2check_malloc(*ptr)` and the store of the block (scope 1, `pointer`, line 1, `cocoiso`). NULL `ptr` is ignored (develop dereferences it). | +| `__m2c_rt_memtrack_map2check_calloc` | `void __m2c_rt_memtrack_map2check_calloc(const void *ptr, int quantity, int size)` | — | new | Task 2.5, forwarded from `map2check_calloc` (`AnalysisModeMemtrack.c:347-349`): `map2check_malloc(ptr, quantity * size)`, an `int` product that wraps. | +| `__m2c_rt_memtrack_map2check_free` | `void __m2c_rt_memtrack_map2check_free(const char *name, const void *ptr, unsigned int scope, unsigned int line, const char *function_name)` | — | new | Task 2.5, forwarded from `map2check_free` (`AnalysisModeMemtrack.c:351-378`): reads the block from the slot `ptr`; NULL block does nothing; appends the FREE row of the variable, then `is_invalid_free` records `FALSE-FREE` or the block is released. NULL `ptr` is ignored (develop dereferences it). | +| `__m2c_rt_memtrack_map2check_non_static_alloca` | `void __m2c_rt_memtrack_map2check_non_static_alloca(const char *name, const void *ptr, int size, int size_of_primitive, int line, int scope)` | — | new | Task 2.5, forwarded from `map2check_non_static_alloca` (`AnalysisModeMemtrack.c:380-384`): `map2check_alloca` of `size * size_of_primitive` bytes (`int`, wraps). | +| `__m2c_rt_memtrack_map2check_alloca` | `void __m2c_rt_memtrack_map2check_alloca(const char *name, const void *ptr, int size, int size_of_primitive, int line, int scope)` | — | new | Task 2.5, forwarded from `map2check_alloca` (`AnalysisModeMemtrack.c:386-391`): a heap-log row; the name is cut at 35 bytes (`strncpy`, `HeapLog.c:25`). | +| `__m2c_rt_memtrack_map2check_function` | `void __m2c_rt_memtrack_map2check_function(const char *name, const void *ptr)` | — | new | Task 2.5, forwarded from `map2check_function` (`AnalysisModeMemtrack.c:393-397`): a heap-log row of size 1. | +| `__m2c_rt_memtrack_map2check_wasm_malloc` | `void __m2c_rt_memtrack_map2check_wasm_malloc(uint64_t offset, uint64_t size)` | — | new | Task 2.5, forwarded from `map2check_wasm_malloc` (`AnalysisModeMemtrack.c:403-416`) into the same allocation log as `map2check_malloc`; offsets 0 and `(uint64_t)-1` ignored. The legacy `map2check_wasm_malloc` (feature `wasm`, separate table) stays until Task 2.10. | +| `__m2c_rt_memtrack_map2check_wasm_free` | `void __m2c_rt_memtrack_map2check_wasm_free(uint64_t offset, int line)` | — | new | Task 2.5, forwarded from `map2check_wasm_free` (`AnalysisModeMemtrack.c:418-428`): `int line` as the C definition (the pass declares `i64`); an unknown offset records `FALSE-FREE` at `line`, function "". The legacy export with `int64_t line` stays until Task 2.10. | +| `__m2c_rt_memtrack_map2check_wasm_check_access` | `void __m2c_rt_memtrack_map2check_wasm_check_access(uint64_t offset, uint64_t access_size)` | — | new | Task 2.5, forwarded from `map2check_wasm_check_access` (`AnalysisModeMemtrack.c:430-436`): an access outside every live block records `OVERFLOW` at line 0 (SD-19). | +| `__m2c_rt_wasm_allocate_memory` | `void __m2c_rt_wasm_allocate_memory(void *mem, uint64_t initial_pages, uint64_t max_pages, bool is64, uint32_t page_size)` | — | new | Task 2.9, forwarded from `wasm_rt_allocate_memory` (`WasmRuntimeStubs.c:22-35`): `calloc`ed linear memory; `is64` is not stored. The memory and table descriptors (`wasm_rt_memory_t`, `wasm_rt_funcref_table_t`) cross the ABI as `void *` in the five descriptor entry points. The legacy `wasm_rt_*` exports (feature `wasm`) stay until Task 2.10. | +| `__m2c_rt_wasm_grow_memory` | `uint64_t __m2c_rt_wasm_grow_memory(void *mem, uint64_t pages)` | — | new | Task 2.9, forwarded from `wasm_rt_grow_memory` (`WasmRuntimeStubs.c:41-63`): previous page count, or `UINT32_MAX` on failure; SD-19 (no clearing when the size does not grow). | +| `__m2c_rt_wasm_free_memory` | `void __m2c_rt_wasm_free_memory(void *mem)` | — | new | Task 2.9, forwarded from `wasm_rt_free_memory` (`WasmRuntimeStubs.c:66-70`). | +| `__m2c_rt_wasm_allocate_funcref_table` | `void __m2c_rt_wasm_allocate_funcref_table(void *table, uint32_t elements, uint32_t max_elements)` | — | new | Task 2.9, forwarded from `wasm_rt_allocate_funcref_table` (`WasmRuntimeStubs.c:73-78`); the element size is SD-19. | +| `__m2c_rt_wasm_free_funcref_table` | `void __m2c_rt_wasm_free_funcref_table(void *table)` | — | new | Task 2.9, forwarded from `wasm_rt_free_funcref_table` (`WasmRuntimeStubs.c:81-84`). | +| `__m2c_rt_wasm_init` | `void __m2c_rt_wasm_init(void)` | — | new | Task 2.9, forwarded from `wasm_rt_init` (`WasmRuntimeStubs.c:102-104`). | +| `__m2c_rt_wasm_free` | `void __m2c_rt_wasm_free(void)` | — | new | Task 2.9, forwarded from `wasm_rt_free` (`WasmRuntimeStubs.c:106-108`). | +| `__m2c_rt_wasm_is_initialized` | `bool __m2c_rt_wasm_is_initialized(void)` | — | new | Task 2.9, forwarded from `wasm_rt_is_initialized` (`WasmRuntimeStubs.c:98-100`): true from the start. | +| `__m2c_rt_wasm_trap` | `void __m2c_rt_wasm_trap(uint32_t code)` | — | new | Task 2.9, forwarded from `wasm_rt_trap` (`WasmRuntimeStubs.c:110-116`): never returns; records an UNKNOWN verdict and aborts (SD-19). | +| `__m2c_rt_memcleanup_analysis_init` | `void __m2c_rt_memcleanup_analysis_init(void)` | — | new | Core entry point that `AnalysisModeMemcleanup.bc` forwards `analysis_init` to (D7, Task 2.6): selects `AnalysisMode_Memcleanup` and starts new heap, allocation and list logs with `ERROR_DEREF` clear, as `AnalysisModeMemcleanup.c:125-130`. | +| `__m2c_rt_memcleanup_analysis_destroy` | `void __m2c_rt_memcleanup_analysis_destroy(void)` | — | new | Task 2.6, forwarded from `analysis_destroy`: frees the three logs, as `AnalysisModeMemcleanup.c:132-136`. | +| `__m2c_rt_memcleanup_analysis_is_program_correct` | `char __m2c_rt_memcleanup_analysis_is_program_correct(void)` | — | new | Task 2.6, forwarded from `analysis_is_program_correct` (`AnalysisModeMemcleanup.c:138-148`): `valid_allocation_log` appends a freed row for every leaked block; only if the last leaked address is still referenced by a pointer variable (`is_memcleanup_error`, `ListLog.c:40-65`) do `map2check_property` and `map2check_property_memcleanup` get `FALSE-MEMCLEANUP` and it returns 0; else 1. It records nothing and does not end the run. | +| `__m2c_rt_memcleanup_analysis_generate_aux_witness_files` | `void __m2c_rt_memcleanup_analysis_generate_aux_witness_files(void)` | — | new | Task 2.6, forwarded from `analysis_generate_aux_witness_files` (`AnalysisModeMemcleanup.c:150-154`): writes `heap_log.csv`, `allocation_log.csv` and `list_log.csv`, as the memtrack entry point. | +| `__m2c_rt_memcleanup_map2check_check_cstring` | `void __m2c_rt_memcleanup_map2check_check_cstring(const char *string)` | — | new | Task 2.6, forwarded from `map2check_check_cstring`: empty (`AnalysisModeMemcleanup.c:160`). | +| `__m2c_rt_memcleanup_map2check_load` | `void __m2c_rt_memcleanup_map2check_load(const void *ptr, int size)` | — | new | Task 2.6, forwarded from `map2check_load`: empty (`AnalysisModeMemcleanup.c:158`); `ERROR_DEREF` is never set. | +| `__m2c_rt_memcleanup_map2check_free_resolved_address` | `void __m2c_rt_memcleanup_map2check_free_resolved_address(const void *ptr, unsigned int line, const char *function_name, short is_null_valid)` | — | new | Task 2.6, forwarded from `map2check_free_resolved_address` (`AnalysisModeMemcleanup.c:161-180`): `Bool error = FALSE` (`:168`), so no free is invalid; the block's row is freed and every pointer to it gets a FREE row. A block with no row goes on (SD-23). | +| `__m2c_rt_memcleanup_map2check_add_store_pointer` | `void __m2c_rt_memcleanup_map2check_add_store_pointer(const void *var, const void *value, unsigned int scope, const char *name, int line, const char *function_name)` | — | new | Task 2.6, forwarded from `map2check_add_store_pointer` (`AnalysisModeMemcleanup.c:243-292`): the same rules as the memtrack entry point. | +| `__m2c_rt_memcleanup_map2check_check_deref` | `void __m2c_rt_memcleanup_map2check_check_deref(int line, const char *function_name)` | — | new | Task 2.6, forwarded from `map2check_check_deref`: empty (`AnalysisModeMemcleanup.c:294`). | +| `__m2c_rt_memcleanup_map2check_malloc` | `void __m2c_rt_memcleanup_map2check_malloc(const void *ptr, int size)` | — | new | Task 2.6, forwarded from `map2check_malloc` (`AnalysisModeMemcleanup.c:296-314`): the same rules as the memtrack entry point. | +| `__m2c_rt_memcleanup_map2check_posix` | `void __m2c_rt_memcleanup_map2check_posix(const void *ptr, int size)` | — | new | Task 2.6, forwarded from `map2check_posix` (`AnalysisModeMemcleanup.c:319-324`): the same rules as the memtrack entry point. | +| `__m2c_rt_memcleanup_map2check_calloc` | `void __m2c_rt_memcleanup_map2check_calloc(const void *ptr, int quantity, int size)` | — | new | Task 2.6, forwarded from `map2check_calloc` (`AnalysisModeMemcleanup.c:326-328`): the same rules as the memtrack entry point. | +| `__m2c_rt_memcleanup_map2check_free` | `void __m2c_rt_memcleanup_map2check_free(const char *name, const void *ptr, unsigned int scope, unsigned int line, const char *function_name)` | — | new | Task 2.6, forwarded from `map2check_free` (`AnalysisModeMemcleanup.c:330-357`): as memtrack's except `Bool error = FALSE` (`:342`), so a double free or a free of a non-heap address is no FALSE-FREE. A block with no row goes on (SD-23). | +| `__m2c_rt_memcleanup_map2check_non_static_alloca` | `void __m2c_rt_memcleanup_map2check_non_static_alloca(const char *name, const void *ptr, int size, int size_of_primitive, int line, int scope)` | — | new | Task 2.6, forwarded from `map2check_non_static_alloca` (`AnalysisModeMemcleanup.c:359-363`): the same rules as the memtrack entry point. | +| `__m2c_rt_memcleanup_map2check_alloca` | `void __m2c_rt_memcleanup_map2check_alloca(const char *name, const void *ptr, int size, int size_of_primitive, int line, int scope)` | — | new | Task 2.6, forwarded from `map2check_alloca` (`AnalysisModeMemcleanup.c:365-370`): the same rules as the memtrack entry point. | +| `__m2c_rt_memcleanup_map2check_function` | `void __m2c_rt_memcleanup_map2check_function(const char *name, const void *ptr)` | — | new | Task 2.6, forwarded from `map2check_function` (`AnalysisModeMemcleanup.c:372-376`): the same rules as the memtrack entry point. | +| `map2check_nondet_bool` | `void map2check_nondet_bool(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_bool` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_char` | `void map2check_nondet_char(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_char` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_double` | `void map2check_nondet_double(unsigned int line, unsigned int scope, double value, const char *function_name)` | — | new | Develop `map2check_nondet_double` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_float` | `void map2check_nondet_float(unsigned int line, unsigned int scope, double value, const char *function_name)` | — | new | Develop `map2check_nondet_float` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_int` | `void map2check_nondet_int(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_int` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_loff_t` | `void map2check_nondet_loff_t(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_loff_t` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_long` | `void map2check_nondet_long(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_long` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_longlong` | `void map2check_nondet_longlong(unsigned int line, unsigned int scope, long long value, const char *function_name)` | — | new | Develop `map2check_nondet_longlong` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_memory_byte` | `void map2check_nondet_memory_byte(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_memory_byte` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_pchar` | `void map2check_nondet_pchar(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_pchar` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_pointer` | `void map2check_nondet_pointer(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_pointer` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_sector_t` | `void map2check_nondet_sector_t(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_sector_t` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_short` | `void map2check_nondet_short(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_short` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_size_t` | `void map2check_nondet_size_t(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_size_t` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_u16` | `void map2check_nondet_u16(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_u16` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_u32` | `void map2check_nondet_u32(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_u32` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_u8` | `void map2check_nondet_u8(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_u8` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_uchar` | `void map2check_nondet_uchar(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_uchar` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_uint` | `void map2check_nondet_uint(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_uint` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_ulong` | `void map2check_nondet_ulong(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_ulong` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_ulonglong` | `void map2check_nondet_ulonglong(unsigned int line, unsigned int scope, unsigned long long value, const char *function_name)` | — | new | Develop `map2check_nondet_ulonglong` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_unsigned` | `void map2check_nondet_unsigned(unsigned int line, unsigned int scope, unsigned int value, const char *function_name)` | — | new | Develop `map2check_nondet_unsigned` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `map2check_nondet_ushort` | `void map2check_nondet_ushort(unsigned int line, unsigned int scope, int value, const char *function_name)` | — | new | Develop `map2check_nondet_ushort` (`NonDetLog.c`, Task 2.2): appends a row of the C nondet log with the current `map2check_get_current_step()` and advances it; `klee_log.csv` is written from these rows. Row type and value printing as in `NonDetLog.c`; see “Nondet generators and the nondet log”. Does not touch the JSON-report log. | +| `nondet_log_destroy` | `void nondet_log_destroy(void)` | — | new | `NonDetLog.c:148` (Task 2.2): empties the C nondet log. | +| `nondet_log_init` | `void nondet_log_init(void)` | — | new | `NonDetLog.c:146` (Task 2.2): empties the C nondet log; the step counter is kept. Called by `nondet_init` of the generator IR (Task 3.1). | +| `map2check_save_nondet_log_int` | `non_det_log_t *map2check_save_nondet_log_int(int line, int scope, enum var_type_t type_var, int *value, const char *function_name)` | returns `non_det_log_t *` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Returned record is not linked into a list (SD-10). | +| `map2check_save_nondet_log_uint` | `non_det_log_t *map2check_save_nondet_log_uint(int line, int scope, enum var_type_t type_var, unsigned int *value, const char *function_name)` | returns `non_det_log_t *` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Returned record is not linked into a list (SD-10). | +| `map2check_save_nondet_log_long` | `non_det_log_t *map2check_save_nondet_log_long(int line, int scope, enum var_type_t type_var, long *value, const char *function_name)` | returns `non_det_log_t *` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Returned record is not linked into a list (SD-10). | +| `map2check_save_nondet_log_float` | `non_det_log_t *map2check_save_nondet_log_float(int line, int scope, enum var_type_t type_var, float *value, const char *function_name)` | returns `non_det_log_t *` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Returned record is not linked into a list (SD-10). | +| `map2check_save_nondet_log_double` | `non_det_log_t *map2check_save_nondet_log_double(int line, int scope, enum var_type_t type_var, double *value, const char *function_name)` | returns `non_det_log_t *` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Returned record is not linked into a list (SD-10). | +| `map2check_save_nondet_log_char` | `non_det_log_t *map2check_save_nondet_log_char(int line, int scope, enum var_type_t type_var, char *value, const char *function_name)` | returns `non_det_log_t *`; `char` value | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `char` is `i8` on the Rust side; see “`char` ABI”. Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_unsigned` | `non_det_log_t *map2check_save_nondet_log_unsigned(int line, int scope, int type_var, unsigned int *value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_unsigned` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type UNSIGNED (5), value printed with `%u`; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_ulong` | `non_det_log_t *map2check_save_nondet_log_ulong(int line, int scope, int type_var, unsigned long *value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_ulong` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type ULONG (6), low 32 bits with `%d`; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_uchar` | `non_det_log_t *map2check_save_nondet_log_uchar(int line, int scope, int type_var, unsigned char *value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_uchar` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type UCHAR (8), value zero-extended; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_short` | `non_det_log_t *map2check_save_nondet_log_short(int line, int scope, int type_var, short *value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_short` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type SHORT (14), value zero-extended as the pass does (`-2` is logged as `65534`); see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_ushort` | `non_det_log_t *map2check_save_nondet_log_ushort(int line, int scope, int type_var, unsigned short *value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_ushort` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type USHORT (3), value zero-extended; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_bool` | `non_det_log_t *map2check_save_nondet_log_bool(int line, int scope, int type_var, int *value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_bool` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type BOOL (7); develop's `bool` is `typedef int bool` (`NonDetGenerator.h:74`), so the pass widens the `_Bool` to `int` before storing it; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_size_t` | `non_det_log_t *map2check_save_nondet_log_size_t(int line, int scope, int type_var, size_t *value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_size_t` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type SIZE_T (10), low 32 bits with `%d`; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_loff_t` | `non_det_log_t *map2check_save_nondet_log_loff_t(int line, int scope, int type_var, long long *value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_loff_t` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type LOFF_T (11); `loff_t` is glibc's `long long`; low 32 bits with `%d`; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_sector_t` | `non_det_log_t *map2check_save_nondet_log_sector_t(int line, int scope, int type_var, unsigned long *value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_sector_t` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type SECTOR_T (12); `sector_t` is `unsigned long` (`NonDetGenerator.h:112`); low 32 bits with `%d`; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_pointer` | `non_det_log_t *map2check_save_nondet_log_pointer(int line, int scope, int type_var, void **value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_pointer` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type POINTER (2); the address, logged as its low 32 bits (the pass's `ptrtoint` to `i32`); see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_nondet_log_pchar` | `non_det_log_t *map2check_save_nondet_log_pchar(int line, int scope, int type_var, char **value, const char *function_name)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): Develop `map2check_nondet_pchar` (`NonDetLog.c:105-150`), called by the pass with the value stored in an `alloca` of its C type (#6, #13). Row type PCHAR (9); the string's address, logged as its low 32 bits; see “Nondet generators and the nondet log”. `type_var` is ignored for the row type (see “Nondet generators and the nondet log”). Returned record is not linked (SD-10). | +| `map2check_save_basic_block_log` | `bbtrack_log_t *map2check_save_basic_block_log(int32_t line, const char *function_name)` | returns `bbtrack_log_t *` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Returned record is not linked into a list (SD-10). | +| `map2check_is_in_trackbb_container` | `int32_t map2check_is_in_trackbb_container(int32_t line)` | `map2check_is_in_trackbb_container` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): | +| `map2check_map_alloca` | `void map2check_map_alloca(const char *var_name, const void *ptr_address, int32_t size, int32_t size_primitive, int32_t line_number, int32_t scope)` | `container_memtracklog.h:41` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): | +| `map2check_map_non_static_alloca` | `void map2check_map_non_static_alloca(const char *var_name, const void *ptr_address, int32_t size, int32_t size_primitive, int32_t line_number, int32_t scope)` | `container_memtracklog.h:44` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `size` is an element count; static slot of `size * size_primitive` bytes, never a leak (SD-12, #38). | +| `map2check_map_funct_address` | `void map2check_map_funct_address(const char *var_name, const void *ptr_address)` | `container_memtracklog.h:48` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Signature and semantics reconstructed; see its section. | +| `map2check_map_malloc` | `void map2check_map_malloc(const void *ptr_address, int32_t size)` | `map2check_map_malloc` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Negative sizes are ignored (SD-09). | +| `map2check_map_calloc` | `void map2check_map_calloc(const void *ptr_address, int32_t quantity, int32_t size)` | `map2check_map_calloc` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Negative sizes are ignored (SD-09). | +| `map2check_map_posix_memalign` | `void map2check_map_posix_memalign(const void *ptr_address, int32_t size)` | `map2check_posix` (develop) | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (new): `ptr_address` is posix_memalign's `memptr` (`void **`): reads the block from it, records it as `map2check_map_malloc`, then the store of the block into `*ptr_address` with scope 1, line 1, name `pointer`, function `cocoiso`, as develop's `map2check_posix` (`AnalysisModeMemtrack.c:327-333`); golden `POSIX-MEMALIGN`. NULL `ptr_address` ignored (SD-09); NULL block: as `map2check_map_malloc`, and no store. | +| `map2check_map_free` | `void map2check_map_free(const char *var_name, const void *var_address, const void *ptr_address, uint32_t scope, uint32_t line_number, const char *function_name)` | `map2check_map_free` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Task 2.5: gains `var_address`, the address of the pointer variable freed (the slot develop's `map2check_free` receives); `ptr_address` stays the block, which the release is keyed on. The slot only names the variable in the FREE row of `list_log.csv` (`AnalysisModeMemtrack.c:339-347`). | +| `map2check_map_store_pointer` | `void map2check_map_store_pointer(const void *var_address, const void *value, uint32_t scope, const char *var_name, int32_t line_number, const char *funct_name)` | `map2check_map_store_pointer` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): | +| `map2check_is_valid_assert` | `void map2check_is_valid_assert(int32_t line_number, const char *function_name, int32_t expression)` | `map2check_is_valid_assert` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): A failed assertion reports `ASSERT` (before Task 2.3: `REACHABILITY`) and, with the mode unset, prints the JSON report; with a mode set it ends the run (“Result files”) and prints nothing. Active in unset and assert modes only (SD-14). | +| `map2check_binop_add_int` | `void map2check_binop_add_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_add_int` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_sub_int` | `void map2check_binop_sub_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_sub_int` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_mul_int` | `void map2check_binop_mul_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_mul_int` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_div_int` | `void map2check_binop_div_int(int32_t param1, int32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_div_int` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_neg_int` | `void map2check_binop_neg_int(int32_t param1, int32_t _param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_neg_int` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_shl_int` | `void map2check_binop_shl_int(int32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_shl_int` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_shr_int` | `void map2check_binop_shr_int(int32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_shr_int` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_add_unsigned` | `void map2check_binop_add_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_add_unsigned` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_sub_unsigned` | `void map2check_binop_sub_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_sub_unsigned` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_mul_unsigned` | `void map2check_binop_mul_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_mul_unsigned` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_div_unsigned` | `void map2check_binop_div_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_div_unsigned` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_shr_unsigned` | `void map2check_binop_shr_unsigned(uint32_t param1, uint32_t param2, uint32_t line, uint32_t scope, const char *function_name)` | `map2check_binop_shr_unsigned` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): `int`/`unsigned` (32-bit) only, as in C. Active in unset and overflow modes only (SD-14). | +| `map2check_binop_add_signed` | `void map2check_binop_add_signed(int64_t a, int64_t b, uint32_t bits, uint32_t line, const char *function_name)` | `map2check_binop_add` (develop) | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Operands sign-extended to 64 bits, `bits` the width of the result type (0 or above 64 means 64), as the pass sends them; reports `OVERFLOW` when the exact sum leaves the range of `bits` (`AnalysisModeOverflow.c:110-276`). No `scope` argument. Records and continues with the mode unset; ends the run with a mode set (SD-03). Active in unset and overflow modes only (SD-14). | +| `map2check_binop_sub_signed` | `void map2check_binop_sub_signed(int64_t a, int64_t b, uint32_t bits, uint32_t line, const char *function_name)` | `map2check_binop_sub` (develop) | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Operands sign-extended to 64 bits, `bits` the width of the result type (0 or above 64 means 64), as the pass sends them; reports `OVERFLOW` when the exact difference leaves the range of `bits` (`AnalysisModeOverflow.c:110-276`). No `scope` argument. Records and continues with the mode unset; ends the run with a mode set (SD-03). Active in unset and overflow modes only (SD-14). | +| `map2check_binop_mul_signed` | `void map2check_binop_mul_signed(int64_t a, int64_t b, uint32_t bits, uint32_t line, const char *function_name)` | `map2check_binop_mul` (develop) | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Operands sign-extended to 64 bits, `bits` the width of the result type (0 or above 64 means 64), as the pass sends them; reports `OVERFLOW` when the exact product leaves the range of `bits` (`AnalysisModeOverflow.c:110-276`). No `scope` argument. Records and continues with the mode unset; ends the run with a mode set (SD-03). Active in unset and overflow modes only (SD-14). | +| `map2check_binop_div_signed` | `void map2check_binop_div_signed(int64_t a, int64_t b, uint32_t bits, uint32_t line, const char *function_name)` | `map2check_binop_sdiv` (develop) | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Operands sign-extended to 64 bits, `bits` the width of the result type (0 or above 64 means 64), as the pass sends them; reports `DIVBYZERO` (`ViolatedProperty` 10, develop `FALSE-DIVBYZERO`) when `b` is 0, `OVERFLOW` for MIN / -1 of `bits` (`AnalysisModeOverflow.c:110-276`). No `scope` argument. Records and continues with the mode unset; ends the run with a mode set (SD-03). Active in unset and overflow modes only (SD-14). | +| `map2check_binop_rem_signed` | `void map2check_binop_rem_signed(int64_t a, int64_t b, uint32_t bits, uint32_t line, const char *function_name)` | `map2check_binop_srem` (develop) | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Operands sign-extended to 64 bits, `bits` the width of the result type (0 or above 64 means 64), as the pass sends them; reports `DIVBYZERO` when `b` is 0, `OVERFLOW` for MIN % -1 of `bits` (`AnalysisModeOverflow.c:110-276`). No `scope` argument. Records and continues with the mode unset; ends the run with a mode set (SD-03). Active in unset and overflow modes only (SD-14). | +| `map2check_check_load` | `void map2check_check_load(const void *ptr, int32_t line, uint32_t scope, int32_t size, const char *function_name)` | `map2check_check_load` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Address-validity rule rewritten (plan R1; differential check in #15); since #11 a heap block is valid only while its latest alloc/free event is an allocation, as in `develop` (`AllocationLog.c:127-148`). The first violation is final; a later check never clears or replaces it (SD-03); NULL handling in SD-05. Active in unset and memtrack modes only (SD-14). | +| `map2check_check_free` | `void map2check_check_free(const char *name, const void *var_address, const void *ptr, uint32_t scope, uint32_t line, const char *function_name)` | `map2check_check_free` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Task 2.5: same arguments as `map2check_map_free` (gains `var_address`; `name` and `scope` are now used): on a first violation it keeps the FREE row develop's `map2check_free` appends to `list_log.csv` before it fails (`AnalysisModeMemtrack.c:346-358`). The check itself is unchanged and keyed on `ptr`. Untracked address is a violation (SD-06). Active in unset and memtrack modes only (SD-14). | +| `map2check_check_free_resolved_address` | `void map2check_check_free_resolved_address(const void *ptr, uint32_t line, const char *function_name, int16_t is_null_valid)` | `analysis_memory.h:46` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (same): Semantics in its section; records the release when the check passes, unlike `map2check_check_free`; untracked address is a violation (SD-06); see also SD-13. The check is active in unset and memtrack modes only; when inactive the release is still recorded (SD-14). | +| `map2check_check_deref` | `void map2check_check_deref(const void *ptr, uint32_t scope, uint32_t line, const char *function_name)` | `map2check_check_deref` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): the C name, with the C signature, is defined by the IR modules `AnalysisModeMemtrack.bc` and `AnalysisModeMemcleanup.bc` (`__m2c_rt__map2check_check_deref`). 1.x (same): Name collides with the pass ABI `map2check_check_deref(i64, ptr)`; see the pass matrix (#21). Active in unset and memtrack modes only (SD-14). | +| `map2check_check_mem_endprog` | `void map2check_check_mem_endprog(void)` | `map2check_check_mem_endprog` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): not in the C runtime ABI; still a Rust function of `map2check::ffi` that the Rust tests drive. 1.x (changed): Leak algorithm rewritten. Runs `analysis_is_program_correct` of the mode: unset and memtrack report memtrack or memcleanup by option, memcleanup mode reports memcleanup only for a still-referenced last leak, the other modes nothing (SD-04, SD-14). | +| `LLVMFuzzerTestOneInput` | `int32_t LLVMFuzzerTestOneInput(const uint8_t *data, uintptr_t size)` | `LLVMFuzzerTestOneInput` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (same): Feature `libfuzzer`. Resets the analysis state on every iteration (SD-11). | +| `__VERIFIER_nondet_bool` | `bool __VERIFIER_nondet_bool(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads four bytes, develop's `typedef int bool`, and returns that `int != 0` (SD-18). | +| `__VERIFIER_nondet_char` | `int8_t __VERIFIER_nondet_char(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_uchar` | `uint8_t __VERIFIER_nondet_uchar(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_short` | `int16_t __VERIFIER_nondet_short(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_ushort` | `uint16_t __VERIFIER_nondet_ushort(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_int` | `int32_t __VERIFIER_nondet_int(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_uint` | `uint32_t __VERIFIER_nondet_uint(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_long` | `int64_t __VERIFIER_nondet_long(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_ulong` | `uint64_t __VERIFIER_nondet_ulong(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_float` | `float __VERIFIER_nondet_float(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads four bytes; develop has no `float` generator (SD-18). | +| `__VERIFIER_nondet_double` | `double __VERIFIER_nondet_double(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_size_t` | `uintptr_t __VERIFIER_nondet_size_t(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_pointer` | `void *__VERIFIER_nondet_pointer(void)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`; replaces `map2check_gen_data_fuzzer_*`. Reads `sizeof` bytes like develop's AFL++ generator, rewinding at the end of the input (SD-18). | +| `__VERIFIER_nondet_loff_t` | `int64_t __VERIFIER_nondet_loff_t(void)` | `map2check_gen_data_fuzzer_loff_t` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`. Core target of the pass's `map2check_non_det_loff_t` (#6); `loff_t` is glibc's `long long`, eight bytes (SD-18). | +| `__VERIFIER_nondet_sector_t` | `unsigned long __VERIFIER_nondet_sector_t(void)` | `map2check_gen_data_fuzzer_sector_t` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`. Core target of `map2check_non_det_sector_t` (#6); develop's `sector_t` is `unsigned long` (`NonDetGenerator.h:112`), eight bytes (SD-18). | +| `__VERIFIER_nondet_pchar` | `char *__VERIFIER_nondet_pchar(void)` | `map2check_gen_data_fuzzer_pchar` | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`. Core target of `map2check_non_det_pchar` (#6). An `unsigned` length, NULL for 0, capped at 4096, then `length - 1` chars and a NUL, as `NonDetGeneratorAFL.c:101-118`; the string comes from the C `malloc` and the program may `free` it (SD-18). | +| `__VERIFIER_assume` | `void __VERIFIER_assume(int32_t condition)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (new): Feature `libfuzzer`. `__VERIFIER_assume(0)` abandons the current fuzzer input and `LLVMFuzzerTestOneInput` returns 0, as develop's `nondet_cancel`; with no iteration running it calls `exit(0)` (SD-07). Defined `extern "C-unwind"`. The analysed program must be compiled with `-fexceptions` (D1 condition 11); see “`__VERIFIER_assume` under a fuzzer”. | +| `map2check_fuzzer_assume` | `void map2check_fuzzer_assume(int32_t condition)` | `map2check_fuzzer_assume(int)` (`nondet_gen_libfuzzer.h:41`) | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` feature. 1.x (changed): Feature `libfuzzer`. Same behaviour as `__VERIFIER_assume` (SD-07). | | `__VERIFIER_error` | `void __VERIFIER_error(void)` | — | new | Features `libfuzzer` and `klee`. With an analysis mode set it does nothing, as develop's; with the mode unset it records REACHABILITY, prints the JSON report and aborts (SD-08). | -| `__VERIFIER_assert` | `void __VERIFIER_assert(int32_t condition)` | — | new | Features `libfuzzer` and `klee`. `__VERIFIER_assert(0)` calls `__VERIFIER_error()`: nothing with a mode set, an abort without (SD-08). | -| `map2check_wasm_malloc` | `void map2check_wasm_malloc(uint64_t offset, uint64_t size)` | `map2check_wasm_malloc(uint64_t, uint64_t)` (`AnalysisModeMemtrack.c:385`) | same | Feature `wasm` (the header declares it without). Registers the block the lifted `malloc` returned at linear-memory `offset`; 0 and `UINT64_MAX` are ignored, a known offset is resized and revived. Records in every mode (SD-19). | -| `map2check_wasm_free` | `void map2check_wasm_free(uint64_t offset, int64_t line)` | `map2check_wasm_free(uint64_t, int)` (`AnalysisModeMemtrack.c:399`) | same | Feature `wasm`. `line` is `int64_t`, the type the pass declares (`MemoryTrackPass.cpp:829`); develop's C reads the low 32 bits. An offset never registered is `MEMSAFETY_FREE` at `line`; a double free is not an error. Reported where the memory checks are active (SD-19). | -| `map2check_wasm_check_access` | `void map2check_wasm_check_access(uint64_t offset, uint64_t access_size)` | `map2check_wasm_check_access(uint64_t, uint64_t)` (`AnalysisModeMemtrack.c:416`) | same | Feature `wasm`. An access outside every live block is reported as `OVERFLOW`, line 0, no function, exactly as develop does (SD-19). Reported where the memory checks are active. | +| `__VERIFIER_assert` | `void __VERIFIER_assert(int32_t condition)` | — | discontinued | Not exported since 2.0.0 (D6, Task 2.10): removed with the `libfuzzer` and `klee` features; the C name is defined by the IR module `AnalysisModeAssert.bc`, empty as in C. 1.x (new): Features `libfuzzer` and `klee`. `__VERIFIER_assert(0)` calls `__VERIFIER_error()`: nothing with a mode set, an abort without (SD-08). | +| `map2check_wasm_malloc` | `void map2check_wasm_malloc(uint64_t offset, uint64_t size)` | `map2check_wasm_malloc(uint64_t, uint64_t)` (`AnalysisModeMemtrack.c:385`) | discontinued | Not exported since 2.0.0 (D6, Task 2.10): the C name is defined by the IR module `AnalysisModeMemtrack.bc` (`__m2c_rt_memtrack_map2check_wasm_malloc`). 1.x (same): Feature `wasm` (the header declares it without). Registers the block the lifted `malloc` returned at linear-memory `offset`; 0 and `UINT64_MAX` are ignored, a known offset is resized and revived. Records in every mode (SD-19). | +| `map2check_wasm_free` | `void map2check_wasm_free(uint64_t offset, int64_t line)` | `map2check_wasm_free(uint64_t, int)` (`AnalysisModeMemtrack.c:399`) | discontinued | Not exported since 2.0.0 (D6, Task 2.10): the C name, with the C signature `(uint64_t, int)`, is defined by the IR module `AnalysisModeMemtrack.bc` (`__m2c_rt_memtrack_map2check_wasm_free`). 1.x (same): Feature `wasm`. `line` is `int64_t`, the type the pass declares (`MemoryTrackPass.cpp:829`); develop's C reads the low 32 bits. An offset never registered is `MEMSAFETY_FREE` at `line`; a double free is not an error. Reported where the memory checks are active (SD-19). | +| `map2check_wasm_check_access` | `void map2check_wasm_check_access(uint64_t offset, uint64_t access_size)` | `map2check_wasm_check_access(uint64_t, uint64_t)` (`AnalysisModeMemtrack.c:416`) | discontinued | Not exported since 2.0.0 (D6, Task 2.10): the C name is defined by the IR module `AnalysisModeMemtrack.bc` (`__m2c_rt_memtrack_map2check_wasm_check_access`). 1.x (same): Feature `wasm`. An access outside every live block is reported as `OVERFLOW`, line 0, no function, exactly as develop does (SD-19). Reported where the memory checks are active. | | `next_current_step` | — | `next_current_step()` | discontinued | Use `get_next_step()`; same effect (#13). | | `map2check_save_memtrack_log` | — | `memtracklog.h:44` | discontinued | Covered by the `map2check_map_*` exports; no external caller. | | `map2check_gen_data_fuzzer_*` | — | bool, char, uchar, short, ushort, int32_t, int64_t, uint, unsigned, long, ulong, float, double, size_t | discontinued | Replaced by `__VERIFIER_nondet_*` (#12). | @@ -142,41 +263,46 @@ build with `--features libfuzzer`. ## Map2Check pass matrix Symbols inserted by the passes of `hbgit/Map2Check@develop` (plan -§1.2(b)). The compatibility layer that would export them is #21. Until it -lands, `libmap2check.a` cannot replace the C library embedded in -Map2Check. +§1.2(b)), and where the drop-in runtime defines them (D7 layout A-fwd, +2.0.0): `same` rows are exports of `libmap2check.a` with the C signature +(the core modules); `ir` rows are defined by a generated IR module +(`scripts/runtime-ir/gen_ll.py`) that forwards to a `__m2c_rt_*` core entry +point; `out-of-scope` rows are not in the C runtime ABI of develop +(`docs/runtime-abi/inventory.tsv`). | Symbol | Signature expected by the passes | Status | Notes | |---|---|---|---| -| `map2check_init` | `map2check_init(int)` | changed | Core export takes `void`; the mode the frontend chose at link time comes from `map2check_set_mode()` (Task 2.3, SD-14). | -| `map2check_check_deref` | `map2check_check_deref(i64, ptr)` | changed | Same name, incompatible core signature `(ptr, u32, u32, ptr)`. The plan (§2.1, D1) proposes renaming the core export to `map2check_check_deref_v2` inside the compat layer; decision in #21. | -| `map2check_track_bb` | `(u32, ptr)` | pending | Core equivalent: `map2check_save_basic_block_log` (#21). | -| `map2check_alloca` | `(ptr, ptr, i64, i64, i64, i64)` | pending | Core equivalent: `map2check_map_alloca` with `int32_t` sizes (#21). | -| `map2check_non_static_alloca` | `(ptr, ptr, i64, i64, i64, i64)` | pending | Core equivalent: `map2check_map_non_static_alloca` (#21). | -| `map2check_malloc` | `(ptr, i64)` | pending | Core equivalent: `map2check_map_malloc` (#21). | -| `map2check_calloc` | `(ptr, i64, i64)` | pending | Core equivalent: `map2check_map_calloc` (#21). | -| `map2check_posix` | `(ptr, i64)` | pending | Core equivalent: `map2check_map_posix_memalign(ptr, int32_t)`; the pass narrows the size (`coerceToParamType`) and switches to it in Task 3.2. | -| `map2check_free` | `(ptr, ptr, i64, i64, ptr)` | pending | Core equivalent: `map2check_check_free` then `map2check_map_free`, both `(name, var_slot, *var_slot, scope, line, fn)`; the pass loads the block and skips both calls for NULL (Task 3.2). | -| `map2check_free_resolved_address` | `(ptr, i64, ptr, i1)` | pending | Core equivalent: `map2check_check_free_resolved_address` (#21). | -| `map2check_load` | `(ptr, i64)` | pending | Core `map2check_check_load` takes 5 arguments (#21). | -| `map2check_add_store_pointer` | 6 arguments | pending | Core `map2check_map_store_pointer` differs in order and types (#21). | -| `map2check_function` | `(ptr, ptr)` | pending | Core equivalent: `map2check_map_funct_address` (#21). | -| `map2check_target_function` | `(ptr, i32, i32)` | pending | Core equivalent: `map2check_reach_target`, same arguments; with a mode set it ends the run as develop does, unset it records and returns (SD-15). The pass switches to it later in the cutover. | -| `map2check_assume` | — | pending | #21. | -| `map2check_assume_loop` | — | pending | #21. | -| `map2check_crab_assume` | — | pending | #21. | -| `map2check_assert` | — | pending | #21. | -| `map2check_non_det_*` | 16 typed generators: bool, char, double, int, loff_t, long, pchar, pointer, sector_t, short, size_t, uchar, uint, ulong, unsigned, ushort | pending | Core equivalents: `__VERIFIER_nondet_` for every type (`unsigned` → `__VERIFIER_nondet_uint`), reading the input as develop's AFL++ generator (#6, SD-18). The pass stops renaming the call in Task 3.2. | -| `map2check_nondet_*` | `(u32 line, u32 scope, i32 value, ptr fn)`, `double` value for `double` | pending | Core equivalents: `map2check_save_nondet_log_(line, scope, type_var, T *value, fn)` for every type; the pass stores the value in an `alloca` and passes its address (Task 3.2). See “Nondet generators and the nondet log”. | -| `map2check_nondet_cast_*` | — | pending | #21. | -| `map2check_pointer_cast` | — | pending | #21. | -| `map2check_wasm_malloc` | `(i64, i64)` | same | Core export of the same name and signature, feature `wasm` (Task 2.8, D2). | -| `map2check_wasm_free` | `(i64, i64)` | same | As above. | -| `map2check_wasm_check_access` | `(i64, i64)` | same | As above. | -| `__VERIFIER_nondet_*` | `int`, `uint`, `char`, `long`, `ushort`, `pointer` | same | Feature `libfuzzer`. | -| `__VERIFIER_assume` | `(int)` | same | Feature `libfuzzer` (SD-07). | -| `__VERIFIER_assert` | `(int)` | same | Features `libfuzzer` and `klee` (SD-08). | -| `__VERIFIER_error` | `(void)` | same | Features `libfuzzer` and `klee` (SD-08). | +| `map2check_init` | `map2check_init(int)` | same | Core export with the C signature since Task 2.1. In the drop-in `.bc` it calls the `analysis_init` of the linked mode module and the `nondet_init` of the generator (`Map2CheckFunctions.c:37-38`, Task 2.7). | +| `map2check_track_bb` | `(u32, ptr)` | same | Task 2.1. | +| `map2check_target_function` | `(ptr, i32, i32)` | same | Task 2.1: records `TARGET-REACHED`; with a mode set it ends the run as develop does, unset it records and returns (SD-15). | +| `map2check_nondet_*` | `(u32 line, u32 scope, i32 value, ptr fn)`, `double` value for `double`, `i64`/`u64` for `longlong`/`ulonglong`, `u32` for `unsigned` | same | Task 2.2: develop's names and C signatures (`NonDetLog.h`), including `u8`, `u16`, `u32`, `memory_byte`, `longlong` and `ulonglong`; they append to the C nondet log (`klee_log.csv`). | +| `__VERIFIER_error` | `(void)` | same | Does nothing, with a mode set and without, as `Map2CheckFunctions.c:57-59` (SD-08). | +| `map2check_check_deref` | `map2check_check_deref(i64, ptr)` | ir | `AnalysisModeMemtrack.bc` and `AnalysisModeMemcleanup.bc` → `__m2c_rt__map2check_check_deref` (Tasks 2.5, 2.6). | +| `map2check_alloca` | `(ptr, ptr, i64, i64, i64, i64)` | ir | `AnalysisModeMemtrack.bc` / `AnalysisModeMemcleanup.bc`, as every memory name below. | +| `map2check_non_static_alloca` | `(ptr, ptr, i64, i64, i64, i64)` | ir | As above. | +| `map2check_malloc` | `(ptr, i64)` | ir | As above. | +| `map2check_calloc` | `(ptr, i64, i64)` | ir | As above. | +| `map2check_posix` | `(ptr, i64)` | ir | As above. | +| `map2check_free` | `(ptr, ptr, i64, i64, ptr)` | ir | As above. | +| `map2check_free_resolved_address` | `(ptr, i64, ptr, i1)` | ir | As above. | +| `map2check_load` | `(ptr, i64)` | ir | As above. | +| `map2check_add_store_pointer` | 6 arguments | ir | As above. | +| `map2check_function` | `(ptr, ptr)` | ir | As above. | +| `map2check_check_cstring` | `(ptr)` | ir | As above. | +| `map2check_wasm_malloc` | `(i64, i64)` | ir | `AnalysisModeMemtrack.bc` only (Task 2.5). | +| `map2check_wasm_free` | `(i64, i32)` | ir | As above; the C signature `(uint64_t, int)`. | +| `map2check_wasm_check_access` | `(i64, i64)` | ir | As above. | +| `map2check_binop_*` | `add`, `sub`, `mul`, `sdiv`, `srem`, `add_uint`, `sub_uint`, `mul_uint` | ir | `AnalysisModeOverflow.bc` → `__m2c_rt_overflow_*` (Task 2.3). | +| `map2check_assert` | `(i32, i32, ptr)` | ir | `AnalysisModeAssert.bc` → `__m2c_rt_assert_map2check_assert` (Task 2.4). | +| `__VERIFIER_assert` | `(int)` | ir | `AnalysisModeAssert.bc`, empty as in C (`AnalysisModeAssert.c:25-27`). | +| `map2check_assume` | `(i32)` | ir | The glue IR of `Map2CheckFunctions.bc` (`Map2CheckFunctions-glue.ll`), calling the generator's `nondet_assume` from the program's frame (Task 2.10; `scripts/runtime-ir/test-assume-link.sh`). | +| `map2check_assume_loop` | `(i8)` | ir | As above, `(int)expr`. | +| `map2check_crab_assume` | `(i8)` | ir | As above, `(int)expr`. | +| `map2check_non_det_*` | the typed generators of `NonDetGenerator.h` | ir | The generator modules `NonDetGeneratorNone.bc`, `NonDetGeneratorKlee.bc` and `NonDetGeneratorAFL.bc` (Tasks 2.7, 2.8). | +| `map2check_nondet_cast_*` | — | out-of-scope | Not in the C runtime ABI of develop. | +| `map2check_pointer_cast` | — | out-of-scope | Not in the C runtime ABI of develop. | +| `__VERIFIER_nondet_*` | `int`, `uint`, `char`, `long`, `ushort`, `pointer` | out-of-scope | The pass replaces them with `map2check_non_det_*`; the C runtime does not define them. The 1.x `libfuzzer` exports left in 2.0.0. | +| `__VERIFIER_assume` | `(int)` | out-of-scope | Not in the C runtime ABI of develop; the 1.x `libfuzzer` export left in 2.0.0 (SD-07). | The passes also expect the result channel of the embedded library: the files that the Map2Check frontend parses. This library writes them when a @@ -187,18 +313,16 @@ mode is set (#19); see “Result files”. Map2Check's `--wasm` mode lifts a WebAssembly binary to C with wabt's wasm2c (1.0.41), instruments it with `-wasm-mode` and links the runtime support (decision D2, ported to Rust instead of dropped). `--features wasm` builds -that support into `libmap2check.a`: - -- the three `map2check_wasm_*` exports of the core matrix, declared in the - header; -- the `wasm_rt_*` functions of `WasmRuntimeStubs.c`, which wasm-rt.h - declares and the lifted code calls, so they are not in `map2check.h` - (`cbindgen:ignore`): `wasm_rt_allocate_memory`, `wasm_rt_grow_memory`, - `wasm_rt_free_memory`, `wasm_rt_allocate_funcref_table`, - `wasm_rt_free_funcref_table`, `wasm_rt_is_initialized`, `wasm_rt_init`, - `wasm_rt_free`, `wasm_rt_trap` and - `w2c_wasi__snapshot__preview1_proc_exit`. Memory and table are calloc/realloc - based (KLEE-friendly), as in develop. +that support into `libmap2check.a`. Since 2.0.0 (D6, Task 2.10) the C names +are not exports of the library: the IR module `WasmRuntimeStubs.bc` defines +the `wasm_rt_*` functions of `WasmRuntimeStubs.c` that wasm-rt.h declares +and the lifted code calls (and an empty +`w2c_wasi__snapshot__preview1_proc_exit`), and `AnalysisModeMemtrack.bc` +defines the three `map2check_wasm_*` the pass emits; both forward to the +`__m2c_rt_wasm_*` and `__m2c_rt_memtrack_map2check_wasm_*` core entry points +of the core matrix (Tasks 2.5, 2.9). Memory and table are calloc/realloc +based (KLEE-friendly), as in develop. The 1.x exports of the same names left +the C ABI. `wasm_rt_memory_t` and `wasm_rt_funcref_table_t` are mirrored with `#[repr(C)]` from wabt 1.0.41. `cargo test --features wasm --lib @@ -206,10 +330,16 @@ layout_matches_wasm_rt_h` compares size and field offsets with the real `wasm-rt.h` when `WASM_RT_INCLUDE` names its directory (CI sets it); without the variable the test says it is skipped. Link the lifted code against the wasm-rt.h of wabt 1.0.41; another wabt version may change the layout. -Differences from develop: SD-19. The KLEE bitcode (`klee/Cargo.toml`) is built with `--features klee,wasm`, so the stubs and the three exports are in it too (`tests/klee/wasm.c`). +Differences from develop: SD-19. The drop-in core (`klee/Cargo.toml`) is built with `--features dropin,wasm`. ## Analysis modes +**2.0.0.** `map2check_set_mode()` and the other 1.x checks of this table +(`map2check_map_*`, `map2check_check_*`, `map2check_is_valid_assert`, the +1.x binops) left the C ABI (D6); they stay Rust functions the tests drive. A +C program selects the mode by linking one `AnalysisMode*.bc` (see “Drop-in +modules” below). + `map2check_set_mode()` replaces the link-time choice of `AnalysisMode*.c` in the Map2Check frontend (`caller.cpp:575-612`). The per-mode `match` is in `rust/src/analysismode/mode.rs`. Tracking calls @@ -225,11 +355,43 @@ per-mode `match` is in `rust/src/analysismode/mode.rs`. Tracking calls | `AnalysisMode_Assert` (4) | `ASSERT_MODE` | `map2check_is_valid_assert` (`ASSERT`) | nothing | | `AnalysisMode_Target` (5) | `REACHABILITY_MODE` | none | nothing | +**Drop-in modules (D7, Task 2.3 on; assert and none in Task 2.4; memtrack in Task 2.5).** In the drop-in runtime the mode is +chosen as in develop, by the `AnalysisMode*.bc` the frontend links. That +module is generated IR (`scripts/runtime-ir/gen_ll.py`) and defines the C +names (`analysis_*`, and for overflow `map2check_binop_*` and +`overflowError`); each forwards to a core entry point +`__m2c_rt__`. `libmap2check.a` never defines the C names of a +mode: all five modes define `analysis_*`. `__m2c_rt_overflow_analysis_init` +selects `AnalysisMode_Overflow` (what `map2check_set_mode(3)` does); `__m2c_rt_assert_analysis_init` and `__m2c_rt_none_analysis_init` select `AnalysisMode_Assert` and `AnalysisMode_None` (the none module serves both reachability and cover-branches); `__m2c_rt_memtrack_analysis_init` selects `AnalysisMode_Memtrack` and starts the drop-in memory logs (`memtrack::c_mode`, develop's heap, allocation and list logs with their C rules), and the +`__m2c_rt_overflow_map2check_binop_*` checks run whatever the mode, as the C +module has no mode test. + +**Memtrack drop-in where develop crashes (Task 2.5).** The +`__m2c_rt_memtrack_*` entry points follow `AnalysisModeMemtrack.c` except +where the C dereferences an invalid pointer: a NULL slot in `map2check_free` +or `map2check_posix` is ignored (develop reads `*ptr`); a release whose block +has no allocation row (a NULL block with `isNullValid` 0 that `is_invalid_free` +lets through, or any untracked block in memcleanup mode) stops there, with +no row or reference update (develop writes through the NULL that +`find_row_with_address` returns, `:190-192`, `:372-375`; SD-23); a function or +variable name of 35 bytes or more is cut at 35 bytes in the logs (develop's +`strncpy` leaves no terminator and `%s` reads past the array; SD-17). At the end of +the run `valid_allocation_log` does not `free()` the leaked blocks +(`AllocationLog.c:91`); the process ends right after (for a wasm offset +develop's `free()` aborts before the verdict: SD-19). `klee_log.csv` of a +drop-in witness run is still the legacy log until Task 3.1. + +**NULL function name.** The core entry points (overflow included) write a +NULL `function_name` as an empty string (`Function: ` in the property +files), where the C runtime passes it to `fprintf("%s")` and glibc prints +`(null)`. The Map2Check passes always pass a name. + The memcleanup option only changes the tag of the unset/memtrack leak verdict; it does not apply the memcleanup-mode reachability rule. `map2check_reach_target` and `set_false_result` record in every mode. -`ViolatedProperty` gained `Assert` (8), `Unknown` (9) and `DivByZero` (10), -appended after `None`; `Reachability` is the target-reached property. +`ViolatedProperty` has the values of develop's `enum ViolatedProperty` +(`Map2CheckTypes.h:80-91`, Task 2.1) plus `Concurrency` (10); +`Reachability` is the target-reached property. ## Result files @@ -250,7 +412,9 @@ never change a verdict. The channel is tied to the mode. When `map2check_set_mode()` was called (a Map2Check-instrumented program): -- `map2check_init()` removes stale `map2check_property*` files; +- `map2check_init(int)` leaves `map2check_property*` files of an earlier + run alone, as develop does: `write_property` replaces only + `map2check_property` and the file of the property it writes; - the first violation writes the property files, then, with witness on, the witness files, runs the mode's `analysis_destroy`, writes `map2check_checked_error` (witness on) and calls `abort()`, as develop's @@ -276,8 +440,13 @@ In memtrack and memcleanup modes `map2check_map_malloc` and | `map2check_property_{free,deref,overflow,assert,memtrack,memcleanup}` | `PropertyGenerator.c:24-48`, `:108-130` | `legacy_files::write_property_files` | mode set: with `map2check_property` for that property | | `map2check_property__unknown` | `PropertyGenerator.c:16`, `:24-28` | `legacy_files::write_property_files` | mode set: `UNKNOWN` (double underscore kept, SD-16) | | `track_bb_log.st` | `TrackBBLog.c:35-44` | `legacy_files::write_trackbb_log` | mode set, witness on: end of run | -| `heap_log.csv`, `allocation_log.csv`, `list_log.csv` | `HeapLog.c:54-66`, `AllocationLog.c:161-174`, `ListLog.c:138-157` | `legacy_files::LegacyLogs` | memtrack or memcleanup mode, witness on: end of run (SD-17) | +| `heap_log.csv`, `allocation_log.csv`, `list_log.csv` | `HeapLog.c:54-66`, `AllocationLog.c:161-174`, `ListLog.c:138-157` | rows formatted by `memtrack::c_mode::{heap,allocation,list}_log_row`; from the drop-in logs (`CMemory`, Task 2.5) when the `__m2c_rt_memtrack_*` entry points logged rows, else replayed by `legacy_files::LegacyLogs` | memtrack or memcleanup mode, witness on: end of run (SD-17); `__m2c_rt_memtrack_analysis_generate_aux_witness_files` writes the drop-in ones whatever the mode | | `klee_log.csv` | `NonDetLog.c:17-60` (via `WitnessGeneration.c:22-24`) | `legacy_files::LegacyLogs::write_nondet_log` | mode set, witness on: end of a violating run | +| `klee_log.csv` (C ABI log) | `NonDetLog.c:50-125` | `nondet::c_log::nondet_log_to_file`, exported as `__m2c_rt_nondet_log_to_file` | Task 2.2: the rows of the `map2check_nondet_` exports, written when the KLEE generator IR calls it | + +**One formatter, two sources (Task 2.2).** `nondet::c_log` is the only code that formats `klee_log.csv`. The witness path (`__m2c_rt_witness_generate_aux_files`) feeds it the rows of the legacy log (`map2check_save_nondet_log_`, `__VERIFIER_nondet_`) through `LegacyLogs::nondet_calls`; `__m2c_rt_nondet_log_to_file` feeds it the rows of the `map2check_nondet_` exports. Current difference: the witness path does not read the `map2check_nondet_` rows, so a drop-in run that logs only through those exports writes an empty `klee_log.csv` there. C writes `klee_log.csv` only through the generator's `nondet_generate_aux_witness_files` (`WitnessGeneration.c:26-27`) and the `none` generator writes nothing; that delegation is wired in Task 3.1. Legacy output is byte-identical except that function names are now cut at 35 bytes, as `strncpy(..., FUNCTION_MAX_LENGTH_NAME)` in `NonDetLog.c`. + +**NULL `function_name`.** The `map2check_nondet_` exports record a NULL name as an empty name; C's `strncpy` would crash. | `map2check_checked_error` | `WitnessGeneration.c:28-32` | `legacy_files::write_checked_error` | mode set, witness on: before the abort | `tests/legacy_golden/` holds the files the develop C runtime writes for one @@ -286,11 +455,17 @@ library writes the same bytes for the same calls. ## Nondet generators and the nondet log +**2.0.0.** The `__VERIFIER_nondet_` generators (feature `libfuzzer`) and +the `map2check_save_nondet_log_` loggers left the C ABI (D6). The +generators are the IR modules `NonDetGenerator{None,Klee,AFL}.bc` and the +log is `map2check_nondet_` (core matrix). The rest of this section is the +record of 1.x. + Task 2.5 (#6) completed the nondet family for the Map2Check passes. Every `map2check_non_det_` the pass emits has a core `__VERIFIER_nondet_` target, `pchar`, `loff_t` and `sector_t` included, and every `map2check_nondet_` logger has a `map2check_save_nondet_log_` -(`docs/cutover/abi-mapping.tsv`). The earlier decision not to port the +(the D5 mapping table `docs/cutover/abi-mapping.tsv`, removed in 2.0.0). The earlier decision not to port the `pchar`/`loff_t`/`sector_t` generators is reversed: the pass does emit them (`NonDetPass.cpp:155-171`). @@ -330,10 +505,14 @@ nondet_type)`, `NonDetLog.c:105-128`) and take no type argument. Trusting pointer and `type_var` for the row; deriving it from the export keeps them the same. The cost: a pass that calls the wrong saver logs the wrong type silently, so Task 3.2 must map each `map2check_nondet_` to its -`map2check_save_nondet_log_` exactly as `docs/cutover/abi-mapping.tsv` +`map2check_save_nondet_log_` exactly as the D5 mapping table says. -## KLEE generator (feature `klee`) +## KLEE generator (feature `klee`; 1.x, removed in 2.0.0) + +The `klee` feature and `scripts/build-klee-runtime.sh` left in 2.0.0 (D6, +Task 2.10); the `symex` generator of the drop-in runtime is the IR module +`NonDetGeneratorKlee.bc` (Task 2.7). This section is the record of 1.x. The `symex` runtime (`rust/src/nondet/klee.rs`) is built only from `klee/Cargo.toml` with rustc 1.72.1 as LLVM 16 bitcode @@ -358,7 +537,10 @@ Differences from develop's KLEE generator (`docs/klee-feasibility.md`): - values are concretised (`klee_get_value_*` + `klee_assume`) before the witness logs or the JSON report format them (D1 condition 7). -## `__VERIFIER_assume` under a fuzzer +## `__VERIFIER_assume` under a fuzzer (1.x, removed in 2.0.0) + +The `libfuzzer` feature left in 2.0.0 (D6, Task 2.10); see SD-07 for the +drop-in runtime. This section is the record of 1.x. `__VERIFIER_assume(0)` and `map2check_fuzzer_assume(0)` abandon the current input, as develop's `nondet_cancel` does with `longjmp` diff --git a/docs/cutover/abi-mapping.tsv b/docs/cutover/abi-mapping.tsv deleted file mode 100644 index 7e2f5dd..0000000 --- a/docs/cutover/abi-mapping.tsv +++ /dev/null @@ -1,74 +0,0 @@ -c_symbol core_symbol owner_task notes -map2check_init map2check_set_mode+map2check_init 2.3 C: (i32 isSvComp) → núcleo: set_mode(int32_t, valores de enum AnalysisMode 0-5; outro valor é ignorado)+init(void) (Task 2.3; o modo vinha do .bc ligado, caller.cpp:575-612); o arg int isSvComp é morto: Map2CheckFunctions.c:35 grava sv_comp e nenhum arquivo o lê -map2check_success map2check_success - C: () → núcleo: () (LibraryFunctions.hpp:48) -map2check_track_bb map2check_save_basic_block_log - C: (i32 line,ptr fn) → núcleo: (line:i32,fn); mesma ordem (LibraryFunctions.hpp:54, ffi.rs:111); retorno do núcleo é bbtrack_log_t*, ignorado -map2check_target_function map2check_reach_target 2.3 C: (ptr fn,i32 scope,i32 line) (TargetPass.cpp:89); C runtime grava TARGET_REACHED e aborta (Map2CheckFunctions.c:61-65); núcleo: map2check_reach_target(const char*,i32,i32) (Task 2.3) registra Reachability (=TARGET-REACHED) em qualquer modo, ignora scope e retorna; C encerra na primeira violação; núcleo registra e continua (SD-03/#36, SD-15); término do processo é a Task 2.4 -map2check_assert map2check_is_valid_assert - C: (i32 cond,i32 line,ptr fn) (AssertPass.cpp:23) → núcleo: (line:i32,fn,expression:i32); ORDEM DIFERENTE: cond passa de primeiro a último; C encerra na primeira violação; núcleo registra e continua; SD-03/#36 (Task 2.1) (map2check_error em AnalysisModeAssert.c:20); is_valid_assert imprime o JSON na hora sem encerrar, então um map2check_success posterior imprime um segundo relatório -map2check_assume __VERIFIER_assume - C: (i32) (NonDetPass.cpp:94) → núcleo: (i32 condition); núcleo faz exit(0) se 0 -map2check_assume_loop __VERIFIER_assume - C: (i1) (LoopPredAssumePass.cpp:28; o C runtime recebe char e faz cast para int) → núcleo: (i32); o passe deve emitir zext i1→i32 -map2check_crab_assume __VERIFIER_assume - idem; sem getOrInsertFunction: nasce do rename de verifier.assume (NonDetPass.cpp:216), o tipo do arg segue o chamador; C runtime recebe char (Map2CheckFunctions.c:46) -map2check_binop_add map2check_binop_add_signed 2.5 C: (i64 a,i64 b,i32 width,i32 line,i32 scope,ptr fn) (OperationsFunctions.hpp:74) → núcleo novo: (a:int64,b:int64,bits:uint32,line:uint32,fn); o núcleo não recebe scope e passa a honrar a largura que o passe já envia; overflow reporta OVERFLOW (AnalysisModeOverflow.c:197-206); C encerra na primeira violação (overflowError, AnalysisModeOverflow.c:185) (map2check_error, Map2CheckFunctions.c:72); núcleo registra e continua; SD-03/#36 (Task 2.1) -map2check_binop_add_uint - - C é no-op deliberado (AnalysisModeOverflow.c:219: unsigned wraparound is not overflow); o passe deixa de emitir (OperationsFunctions.hpp:77) -map2check_binop_sub map2check_binop_sub_signed 2.5 C: (i64 a,i64 b,i32 width,i32 line,i32 scope,ptr fn) (OperationsFunctions.hpp:80) → núcleo novo: (a:int64,b:int64,bits:uint32,line:uint32,fn); o núcleo não recebe scope e passa a honrar a largura que o passe já envia; overflow reporta OVERFLOW (AnalysisModeOverflow.c:197-206); C encerra na primeira violação (overflowError, AnalysisModeOverflow.c:185) (map2check_error, Map2CheckFunctions.c:72); núcleo registra e continua; SD-03/#36 (Task 2.1) -map2check_binop_sub_uint - - C é no-op deliberado (AnalysisModeOverflow.c:219: unsigned wraparound is not overflow); o passe deixa de emitir (OperationsFunctions.hpp:83) -map2check_binop_mul map2check_binop_mul_signed 2.5 C: (i64 a,i64 b,i32 width,i32 line,i32 scope,ptr fn) (OperationsFunctions.hpp:86) → núcleo novo: (a:int64,b:int64,bits:uint32,line:uint32,fn); o núcleo não recebe scope e passa a honrar a largura que o passe já envia; overflow reporta OVERFLOW (AnalysisModeOverflow.c:197-206); C encerra na primeira violação (overflowError, AnalysisModeOverflow.c:185) (map2check_error, Map2CheckFunctions.c:72); núcleo registra e continua; SD-03/#36 (Task 2.1) -map2check_binop_mul_uint - - C é no-op deliberado (AnalysisModeOverflow.c:219: unsigned wraparound is not overflow); o passe deixa de emitir (OperationsFunctions.hpp:89) -map2check_binop_sdiv map2check_binop_div_signed 2.5 C: (i64 a,i64 b,i32 width,i32 line,i32 scope,ptr fn) (OperationsFunctions.hpp:92) → núcleo novo: (a:int64,b:int64,bits:uint32,line:uint32,fn); o núcleo não recebe scope e passa a honrar a largura que o passe já envia; divisão por zero reporta FALSE-DIVBYZERO (novo ViolatedProperty, owner 2.5), não OVERFLOW, como no C (AnalysisModeOverflow.c:252 e :266, divisionByZeroError :191); MIN/-1 reporta OVERFLOW; C encerra na primeira violação (overflowError, AnalysisModeOverflow.c:185) (map2check_error, Map2CheckFunctions.c:72); núcleo registra e continua; SD-03/#36 (Task 2.1) -map2check_binop_srem map2check_binop_rem_signed 2.5 C: (i64 a,i64 b,i32 width,i32 line,i32 scope,ptr fn) (OperationsFunctions.hpp:95) → núcleo novo: (a:int64,b:int64,bits:uint32,line:uint32,fn); o núcleo não recebe scope e passa a honrar a largura que o passe já envia; divisão por zero reporta FALSE-DIVBYZERO (novo ViolatedProperty, owner 2.5), não OVERFLOW, como no C (AnalysisModeOverflow.c:252 e :266, divisionByZeroError :191); MIN/-1 reporta OVERFLOW; C encerra na primeira violação (overflowError, AnalysisModeOverflow.c:185) (map2check_error, Map2CheckFunctions.c:72); núcleo registra e continua; SD-03/#36 (Task 2.1) -map2check_malloc map2check_map_malloc - C: (ptr,i64 size) (MemoryTrackPass.cpp:789) → núcleo: (ptr,size:i32); size estreita de i64 para i32 -map2check_calloc map2check_map_calloc - C: (ptr,i64 qty,i64 size) → núcleo: (ptr,qty:i32,size:i32); mesma ordem, estreita para i32 -map2check_alloca map2check_map_alloca - C: (ptr name,ptr addr,i64 size,i64 size_primitive,i64 line,i64 scope) (MemoryTrackPass.cpp:802) → núcleo: (name,addr,size:i32,size_primitive:i32,line:i32,scope:i32); mesma ordem, estreita para i32 -map2check_non_static_alloca map2check_map_non_static_alloca - SD-12 (#38, corrigido): o núcleo agora toma size como contagem de elementos e registra slot estático de size*size_primitive bytes (nunca vazamento), como o C; assinatura do núcleo mantida, igual a map2check_alloca (MemoryTrackPass.cpp:808); C multiplica size*size_primitive (AnalysisModeMemtrack.c:370) -map2check_function map2check_map_funct_address - C: (ptr name,ptr addr) (MemoryTrackPass.cpp:814) → núcleo: (var_name,ptr_address); igual -map2check_add_store_pointer map2check_map_store_pointer - C: (ptr var,ptr value,i64 scope,ptr name,i64 line,ptr fn) (MemoryTrackPass.cpp:779) → núcleo: (var_address,value,scope:u32,var_name,line:i32,fn); mesma ordem -map2check_load map2check_check_load - C: (ptr,i64 size) (MemoryTrackPass.cpp:770) → núcleo: (ptr,line:i32,scope:u32,size:i32,fn); o passe passa a emitir linha, escopo e função (DebugInfo); C encerra na primeira violação; núcleo registra e continua; SD-03/#36 (Task 2.1) (a flag ERROR_DEREF de AnalysisModeMemtrack.c:158 só encerra em check_deref); check_load grava a violação direto -map2check_check_deref - - check_load já registra a violação (ffi.rs:894, LoadChecker); C: (i64 line,ptr fn) (MemoryTrackPass.cpp:785) só lê a flag ERROR_DEREF e encerra (AnalysisModeMemtrack.c:297); o passe deixa de emitir; C encerra na primeira violação; núcleo registra e continua; SD-03/#36 (Task 2.1) -map2check_free map2check_check_free+map2check_map_free - C: (ptr name,ptr var_slot,i64 scope,i64 line,ptr fn) (MemoryTrackPass.cpp:818) → núcleo (ambos, Task 2.5): (name,var_slot,ptr,scope:u32,line:u32,fn), var_slot no lugar em que o passe já o envia e ptr = *var_slot; o núcleo é chaveado no endereço de heap carregado, então o passe carrega *var_slot antes da chamada e a ordem é check_free depois map_free; var_slot só nomeia a variável na linha FREE de list_log.csv (AnalysisModeMemtrack.c:339-347), e check_free guarda essa linha quando falha, como o C, que a acrescenta antes de checar (:346-358); golden FALSE-FREE; o passe não chama nenhum dos dois se o valor carregado for NULL, como o C (AnalysisModeMemtrack.c:342); C encerra na primeira violação; núcleo registra e continua; SD-03/#36 (Task 2.1) (AnalysisModeMemtrack.c:352) -map2check_free_resolved_address map2check_check_free_resolved_address - chamada única; registra a liberação quando a checagem passa (como o C, :178-182); check_free não registra — o passe chama map_free depois; vale também para a instrumentação de realloc (MemoryTrackPass.cpp:169-182); SD-06 (#37, corrigido): endereço sem registro reporta MEMSAFETY_FREE, como is_invalid_free (ListLog.c:95-118); SD-13: free de ponteiro nunca armazenado é válido no núcleo e FALSE_FREE no C; C: (ptr,i64 line,ptr fn,i1 nullValid) (MemoryTrackPass.cpp:774) → núcleo: (ptr,line:u32,fn,is_null_valid:i16); i1 passa a i16 (zext); C encerra na primeira violação; núcleo registra e continua; SD-03/#36 (Task 2.1) (AnalysisModeMemtrack.c:170) -map2check_posix map2check_map_posix_memalign 2.5 chamado em MemoryTrackPass.cpp:120 para posix_memalign; não é obsoleto; C: (ptr,i64 size) (MemoryTrackPass.cpp:793); o C runtime desreferencia ptr (void**) e registra malloc+store_pointer (AnalysisModeMemtrack.c:327-333); núcleo: (ptr_address,size:i32), lê o bloco de *ptr_address e grava malloc e store_pointer(ptr_address,bloco,1,"pointer",1,"cocoiso") como o C; o passe trunca o tamanho para i32 (coerceToParamType); golden POSIX-MEMALIGN -map2check_wasm_malloc map2check_wasm_malloc 2.8 C: (i64,i64) (MemoryTrackPass.cpp:825); núcleo igual (u64 offset,u64 size), feature wasm (Task 2.8): AnalysisModeMemtrack.c:385-397 -map2check_wasm_free map2check_wasm_free 2.8 C: (i64,i64) (MemoryTrackPass.cpp:829); núcleo (u64 offset,i64 line), feature wasm: AnalysisModeMemtrack.c:399-407; offset desconhecido reporta MEMSAFETY_FREE na linha (FALSE_FREE) -map2check_wasm_check_access map2check_wasm_check_access 2.8 C: (i64,i64) (MemoryTrackPass.cpp:833); núcleo igual (u64 offset,u64 access_size), feature wasm: AnalysisModeMemtrack.c:416-423; acesso fora de bloco vivo reporta OVERFLOW, linha 0, como o C (FALSE_OVERFLOW; SD-19); só ativo nos modos que checam memória -__map2check_main__ __map2check_main__ - definido pelo programa instrumentado, não pela biblioteca -overflowError - - morto: errorLines sempre vazio (OverflowPass.cpp:226); C: (i32 line,ptr fn) (OperationsFunctions.hpp:98) reporta FALSE_OVERFLOW e encerra (AnalysisModeOverflow.c:185) -__VERIFIER_assert __VERIFIER_assert - -__VERIFIER_assume __VERIFIER_assume - -__VERIFIER_error __VERIFIER_error - -__VERIFIER_nondet_char __VERIFIER_nondet_char - só comparado pelo nome (exclusões em TrackBasicBlockPass.cpp:113, OverflowPass.cpp:59); não emitido como chamada -__VERIFIER_nondet_int __VERIFIER_nondet_int - só comparado pelo nome (exclusões em TrackBasicBlockPass.cpp:113, OverflowPass.cpp:59); não emitido como chamada -__VERIFIER_nondet_long __VERIFIER_nondet_long - só comparado pelo nome (exclusões em TrackBasicBlockPass.cpp:113, OverflowPass.cpp:59); não emitido como chamada -__VERIFIER_nondet_pointer __VERIFIER_nondet_pointer - só comparado pelo nome (exclusões em TrackBasicBlockPass.cpp:113, OverflowPass.cpp:59); não emitido como chamada -__VERIFIER_nondet_uint __VERIFIER_nondet_uint - só comparado pelo nome (exclusões em TrackBasicBlockPass.cpp:113, OverflowPass.cpp:59); não emitido como chamada -__VERIFIER_nondet_ushort __VERIFIER_nondet_ushort - só comparado pelo nome (exclusões em TrackBasicBlockPass.cpp:113, OverflowPass.cpp:59); não emitido como chamada -map2check_non_det_int __VERIFIER_nondet_int - tipos de NonDetPass.cpp:155-171 (CALL_IF_HELPER) e NonDetPass.hpp:82-97; C: renomeia a chamada __VERIFIER_nondet_ para map2check_non_det_ (NonDetPass.cpp:186) → núcleo: __VERIFIER_nondet_() sem args -map2check_non_det_unsigned __VERIFIER_nondet_uint - idem primeira linha da família -map2check_non_det_char __VERIFIER_nondet_char - idem primeira linha da família -map2check_non_det_pointer __VERIFIER_nondet_pointer - idem primeira linha da família -map2check_non_det_long __VERIFIER_nondet_long - idem primeira linha da família -map2check_non_det_ushort __VERIFIER_nondet_ushort - idem primeira linha da família -map2check_non_det_short __VERIFIER_nondet_short - idem primeira linha da família -map2check_non_det_ulong __VERIFIER_nondet_ulong - idem primeira linha da família -map2check_non_det_bool __VERIFIER_nondet_bool - idem primeira linha da família -map2check_non_det_uchar __VERIFIER_nondet_uchar - idem primeira linha da família -map2check_non_det_pchar __VERIFIER_nondet_pchar 2.5 idem primeira linha da família; sem equivalente núcleo; export novo -map2check_non_det_size_t __VERIFIER_nondet_size_t - idem primeira linha da família -map2check_non_det_loff_t __VERIFIER_nondet_loff_t 2.5 idem primeira linha da família; sem equivalente núcleo; export novo -map2check_non_det_sector_t __VERIFIER_nondet_sector_t 2.5 idem primeira linha da família; sem equivalente núcleo; export novo -map2check_non_det_uint __VERIFIER_nondet_uint - idem primeira linha da família -map2check_non_det_double __VERIFIER_nondet_double - idem primeira linha da família -map2check_nondet_int map2check_save_nondet_log_int - tipos de NonDetFunctions.hpp:77-92 (NON_DET_FUNCTIONS_HELPER); C: (i32 line,i32 scope,i32 value,ptr fn) (double: valor double; pointer: ptrtoint; char/short/long/etc.: cast para i32) → núcleo (ffi.rs:305): (line:i32,scope:i32,type_var:i32,value:T*,function_name) → non_det_log_t*; o passe grava o valor num alloca e passa o ponteiro + type_var -map2check_nondet_unsigned map2check_save_nondet_log_unsigned 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_char map2check_save_nondet_log_char - idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var -map2check_nondet_pointer map2check_save_nondet_log_pointer 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_long map2check_save_nondet_log_long - idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var -map2check_nondet_ushort map2check_save_nondet_log_ushort 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_short map2check_save_nondet_log_short 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_ulong map2check_save_nondet_log_ulong 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_bool map2check_save_nondet_log_bool 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_uchar map2check_save_nondet_log_uchar 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_pchar map2check_save_nondet_log_pchar 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_size_t map2check_save_nondet_log_size_t 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_loff_t map2check_save_nondet_log_loff_t 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_sector_t map2check_save_nondet_log_sector_t 2.5 idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var; sem equivalente núcleo; export novo -map2check_nondet_uint map2check_save_nondet_log_uint - idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var -map2check_nondet_double map2check_save_nondet_log_double - idem primeira linha da família; o passe grava o valor num alloca e passa o ponteiro + type_var diff --git a/docs/cutover/baselines/c-0bca42eb89d1.tsv b/docs/cutover/baselines/c-0bca42eb89d1.tsv new file mode 100644 index 0000000..da5697a --- /dev/null +++ b/docs/cutover/baselines/c-0bca42eb89d1.tsv @@ -0,0 +1,616 @@ +task prp gen rep verdict tag line function witness wall_s maxrss_kb +array-examples/relax-1 valid-memsafety afl 1 ERROR 0 0.85 94296 +array-examples/relax-1 valid-memsafety symex 1 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.65 144664 +array-examples/relax-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.90 145964 +array-examples/standard_sentinel-2 unreach-call afl 1 UNKNOWN 0 9.41 8400504 +array-examples/standard_sentinel-2 unreach-call symex 1 UNKNOWN 0 31.36 173796 +array-examples/standard_sentinel-2 unreach-call hybrid 1 UNKNOWN 0 39.37 10300332 +array-examples/standard_two_index_04 unreach-call afl 1 UNKNOWN 0 9.42 10356696 +array-examples/standard_two_index_04 unreach-call symex 1 UNKNOWN 0 31.46 173812 +array-examples/standard_two_index_04 unreach-call hybrid 1 UNKNOWN 0 38.91 4840996 +array-examples/standard_two_index_05 unreach-call afl 1 UNKNOWN 0 9.84 10981788 +array-examples/standard_two_index_05 unreach-call symex 1 UNKNOWN 0 31.46 173148 +array-examples/standard_two_index_05 unreach-call hybrid 1 UNKNOWN 0 38.65 10700672 +array-memsafety/add_last_unsafe valid-memsafety afl 1 FAILED FALSE-DEREF 522 main 1 0.68 93776 +array-memsafety/add_last_unsafe valid-memsafety symex 1 FAILED FALSE-DEREF 522 main 1 0.63 144024 +array-memsafety/add_last_unsafe valid-memsafety hybrid 1 FAILED FALSE-DEREF 522 main 1 0.63 93044 +array-memsafety/array01-alloca-2 valid-memsafety afl 1 UNKNOWN 0 1.93 93772 +array-memsafety/array01-alloca-2 valid-memsafety symex 1 UNKNOWN 0 31.47 170252 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 1 UNKNOWN 0 42.18 269964 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 1 UNKNOWN 0 0.99 93896 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 1 UNKNOWN 0 1.49 144764 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 1 UNKNOWN 0 40.47 341068 +array-memsafety/lis_unsafe valid-memsafety afl 1 FAILED FALSE-DEREF 521 lis 1 0.67 93256 +array-memsafety/lis_unsafe valid-memsafety symex 1 UNKNOWN 0 1.63 145560 +array-memsafety/lis_unsafe valid-memsafety hybrid 1 FAILED FALSE-DEREF 521 lis 1 0.64 93976 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 1 UNKNOWN 0 9.47 93480 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 1 SUCCEEDED 1 0.46 128060 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 1 SUCCEEDED 1 5.10 128044 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 1 FAILED FALSE-FREE 46 main 1 0.64 93576 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 1 FAILED FALSE-FREE 46 main 1 0.48 128596 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 1 FAILED FALSE-FREE 46 main 1 0.67 93456 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 1 FAILED FALSE-DEREF 42 main 1 0.67 93336 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 1 FAILED FALSE-DEREF 42 main 1 0.47 128212 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 1 FAILED FALSE-DEREF 42 main 1 0.65 93704 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 1 UNKNOWN 0 9.49 93932 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 1 UNKNOWN 0 31.37 128312 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 1 UNKNOWN 0 38.82 128360 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 1 UNKNOWN 0 1.51 94120 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 1 UNKNOWN 0 31.36 128612 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 1 UNKNOWN 0 39.79 128564 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 1 FAILED FALSE-MEMTRACK 1 0.73 95288 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 1 FAILED FALSE-MEMTRACK 1 4.18 139312 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 1 FAILED FALSE-MEMTRACK 1 0.72 95360 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 1 UNKNOWN 0 9.66 610632 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 1 UNKNOWN 0 14.08 140144 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 1 UNKNOWN 0 39.08 606856 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 1 FAILED FALSE-FREE 779 free_17 1 0.94 95748 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 1 FAILED FALSE-FREE 779 free_17 1 20.18 142424 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 1 FAILED FALSE-FREE 779 free_17 1 0.91 95904 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 1 UNKNOWN 0 9.63 506628 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 1 UNKNOWN 0 4.48 138744 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 1 UNKNOWN 0 40.62 506884 +ldv-memsafety/memsetNonZero valid-memsafety afl 1 UNKNOWN 0 9.48 340824 +ldv-memsafety/memsetNonZero valid-memsafety symex 1 SUCCEEDED 1 0.39 126956 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 1 SUCCEEDED 1 5.07 341148 +list-ext-properties/960521-1_1-1 valid-memsafety afl 1 FAILED FALSE-DEREF 533 main 1 0.61 93620 +list-ext-properties/960521-1_1-1 valid-memsafety symex 1 FAILED FALSE-DEREF 0 1 1.62 136612 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 533 main 1 0.64 93780 +list-ext-properties/960521-1_1-2 valid-memsafety afl 1 UNKNOWN 0 9.51 341288 +list-ext-properties/960521-1_1-2 valid-memsafety symex 1 SUCCEEDED 1 15.14 137068 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 1 UNKNOWN 0 38.97 341120 +list-ext-properties/960521-1_1-3 valid-memsafety afl 1 FAILED FALSE-FREE 536 main 1 0.62 93668 +list-ext-properties/960521-1_1-3 valid-memsafety symex 1 FAILED FALSE-FREE 536 main 1 1.62 136180 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 1 FAILED FALSE-FREE 536 main 1 0.62 93896 +list-ext-properties/list-ext unreach-call afl 1 FAILED FALSE 556 main 1 0.55 92632 +list-ext-properties/list-ext unreach-call symex 1 UNKNOWN 0 0.44 134104 +list-ext-properties/list-ext unreach-call hybrid 1 FAILED FALSE 556 main 1 0.55 91732 +list-ext-properties/list-ext valid-memsafety afl 1 UNKNOWN 0 0.55 94480 +list-ext-properties/list-ext valid-memsafety symex 1 FAILED FALSE-DEREF 558 main 1 12.04 139124 +list-ext-properties/list-ext valid-memsafety hybrid 1 FAILED FALSE-DEREF 558 main 1 12.23 139092 +list-ext-properties/simple-ext unreach-call afl 1 FAILED FALSE 540 main 1 0.54 92672 +list-ext-properties/simple-ext unreach-call symex 1 UNKNOWN 0 0.41 133968 +list-ext-properties/simple-ext unreach-call hybrid 1 FAILED FALSE 540 main 1 0.53 92140 +list-ext-properties/simple-ext valid-memcleanup afl 1 UNKNOWN 0 9.50 341116 +list-ext-properties/simple-ext valid-memcleanup symex 1 UNKNOWN 0 0.97 135092 +list-ext-properties/simple-ext valid-memcleanup hybrid 1 UNKNOWN 0 35.04 341304 +list-ext-properties/test-0019_1-1 valid-memsafety afl 1 FAILED FALSE-MEMTRACK 1 0.63 94500 +list-ext-properties/test-0019_1-1 valid-memsafety symex 1 FAILED FALSE-MEMTRACK 1 0.60 128828 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 1 FAILED FALSE-MEMTRACK 1 0.65 94568 +list-ext-properties/test-0158_1-1 valid-memsafety afl 1 UNKNOWN 0 9.49 340820 +list-ext-properties/test-0158_1-1 valid-memsafety symex 1 SUCCEEDED 1 0.51 134948 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 1 SUCCEEDED 1 5.08 341072 +map2check-assert/array_bug unreach-call afl 1 FAILED FALSE 7 __VERIFIER_assert 1 0.54 91864 +map2check-assert/array_bug unreach-call symex 1 FAILED FALSE 7 __VERIFIER_assert 1 0.41 133428 +map2check-assert/array_bug unreach-call hybrid 1 FAILED FALSE 7 __VERIFIER_assert 1 0.52 91844 +map2check-assert/byte_add_bug unreach-call afl 1 UNKNOWN 0 9.41 92216 +map2check-assert/byte_add_bug unreach-call symex 1 SUCCEEDED 1 0.35 127260 +map2check-assert/byte_add_bug unreach-call hybrid 1 SUCCEEDED 1 5.00 340880 +map2check-assert/check_if_bug unreach-call afl 1 UNKNOWN 0 0.37 91616 +map2check-assert/check_if_bug unreach-call symex 1 UNKNOWN 0 31.78 184220 +map2check-assert/check_if_bug unreach-call hybrid 1 UNKNOWN 0 35.29 167940 +map2check-assert/ex01_false unreach-call afl 1 FAILED FALSE 5 __VERIFIER_assert 1 0.58 91988 +map2check-assert/ex01_false unreach-call symex 1 UNKNOWN 0 1.61 142656 +map2check-assert/ex01_false unreach-call hybrid 1 FAILED FALSE 5 __VERIFIER_assert 1 0.53 91696 +map2check-assert/ex02_false unreach-call afl 1 UNKNOWN 0 0.37 92556 +map2check-assert/ex02_false unreach-call symex 1 UNKNOWN 0 31.40 224752 +map2check-assert/ex02_false unreach-call hybrid 1 UNKNOWN 0 35.29 210056 +memory-alloca/c.03-alloca-2 valid-memsafety afl 1 UNKNOWN 0 9.48 94036 +memory-alloca/c.03-alloca-2 valid-memsafety symex 1 UNKNOWN 0 31.86 168836 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 1 UNKNOWN 0 41.96 173616 +memsafety-ext/dll_extends_pointer valid-memsafety afl 1 UNKNOWN 0 9.53 420204 +memsafety-ext/dll_extends_pointer valid-memsafety symex 1 UNKNOWN 0 31.44 158968 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 1 UNKNOWN 0 39.19 497256 +memsafety-ext/tree_stack valid-memsafety afl 1 UNKNOWN 0 9.53 592064 +memsafety-ext/tree_stack valid-memsafety symex 1 UNKNOWN 0 31.61 188904 +memsafety-ext/tree_stack valid-memsafety hybrid 1 UNKNOWN 0 40.51 392336 +memsafety-ext2/length_test03-1 valid-memsafety afl 1 UNKNOWN 0 9.52 469356 +memsafety-ext2/length_test03-1 valid-memsafety symex 1 UNKNOWN 0 31.36 141828 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 1 UNKNOWN 0 39.13 716824 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 1 FAILED FALSE-DEREF 540 containsHead 1 0.97 95000 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 1 UNKNOWN 0 31.94 204664 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 540 containsHead 1 0.87 94308 +memsafety/960521-1-1 valid-memsafety afl 1 FAILED FALSE-FREE 532 main 1 0.97 93564 +memsafety/960521-1-1 valid-memsafety symex 1 FAILED FALSE-FREE 532 main 1 1.19 128176 +memsafety/960521-1-1 valid-memsafety hybrid 1 FAILED FALSE-FREE 532 main 1 0.88 93048 +memsafety/960521-1-2 valid-memsafety afl 1 UNKNOWN 0 9.62 94304 +memsafety/960521-1-2 valid-memsafety symex 1 UNKNOWN 0 31.44 127828 +memsafety/960521-1-2 valid-memsafety hybrid 1 UNKNOWN 0 38.93 127896 +memsafety/960521-1-3 valid-memsafety afl 1 FAILED FALSE-DEREF 529 main 1 0.72 93620 +memsafety/960521-1-3 valid-memsafety symex 1 FAILED FALSE-DEREF 529 main 1 1.09 128876 +memsafety/960521-1-3 valid-memsafety hybrid 1 FAILED FALSE-DEREF 529 main 1 0.68 93720 +memsafety/test-0102-1 valid-memsafety afl 1 UNKNOWN 0 9.64 94180 +memsafety/test-0102-1 valid-memsafety symex 1 UNKNOWN 0 31.46 148252 +memsafety/test-0102-1 valid-memsafety hybrid 1 UNKNOWN 0 39.03 620188 +array-examples/relax-1 valid-memsafety afl 2 ERROR 0 0.88 94080 +array-examples/relax-1 valid-memsafety symex 2 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.71 152952 +array-examples/relax-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.95 153184 +array-examples/standard_sentinel-2 unreach-call afl 2 UNKNOWN 0 9.69 7249896 +array-examples/standard_sentinel-2 unreach-call symex 2 UNKNOWN 0 31.41 173864 +array-examples/standard_sentinel-2 unreach-call hybrid 2 UNKNOWN 0 39.47 9168064 +array-examples/standard_two_index_04 unreach-call afl 2 UNKNOWN 0 9.53 9807184 +array-examples/standard_two_index_04 unreach-call symex 2 UNKNOWN 0 31.46 173364 +array-examples/standard_two_index_04 unreach-call hybrid 2 UNKNOWN 0 38.81 9490636 +array-examples/standard_two_index_05 unreach-call afl 2 UNKNOWN 0 10.66 9525560 +array-examples/standard_two_index_05 unreach-call symex 2 UNKNOWN 0 31.46 173388 +array-examples/standard_two_index_05 unreach-call hybrid 2 UNKNOWN 0 39.13 9216892 +array-memsafety/add_last_unsafe valid-memsafety afl 2 FAILED FALSE-DEREF 522 main 1 0.68 93728 +array-memsafety/add_last_unsafe valid-memsafety symex 2 FAILED FALSE-DEREF 522 main 1 0.64 142836 +array-memsafety/add_last_unsafe valid-memsafety hybrid 2 FAILED FALSE-DEREF 522 main 1 0.60 93724 +array-memsafety/array01-alloca-2 valid-memsafety afl 2 UNKNOWN 0 0.90 94508 +array-memsafety/array01-alloca-2 valid-memsafety symex 2 UNKNOWN 0 31.44 169804 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 2 UNKNOWN 0 41.50 275612 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 2 UNKNOWN 0 1.08 93784 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 2 UNKNOWN 0 1.44 144256 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 2 UNKNOWN 0 39.13 228428 +array-memsafety/lis_unsafe valid-memsafety afl 2 FAILED FALSE-DEREF 521 lis 1 0.64 93988 +array-memsafety/lis_unsafe valid-memsafety symex 2 UNKNOWN 0 1.65 144828 +array-memsafety/lis_unsafe valid-memsafety hybrid 2 FAILED FALSE-DEREF 521 lis 1 0.64 94092 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 2 UNKNOWN 0 9.47 93576 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 2 SUCCEEDED 1 0.42 128424 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 2 SUCCEEDED 1 5.06 128748 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 2 FAILED FALSE-FREE 46 main 1 0.61 93488 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 2 FAILED FALSE-FREE 46 main 1 0.47 127936 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 2 FAILED FALSE-FREE 46 main 1 0.63 93620 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 2 FAILED FALSE-DEREF 42 main 1 0.63 93636 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 2 FAILED FALSE-DEREF 42 main 1 0.43 128540 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 2 FAILED FALSE-DEREF 42 main 1 0.65 93156 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 2 UNKNOWN 0 9.48 94148 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 2 UNKNOWN 0 31.36 128584 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 2 UNKNOWN 0 38.77 129608 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 2 UNKNOWN 0 1.50 94728 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 2 UNKNOWN 0 31.36 127964 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 2 UNKNOWN 0 39.78 128916 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 2 FAILED FALSE-MEMTRACK 1 0.74 96000 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 2 FAILED FALSE-MEMTRACK 1 4.43 138932 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 2 FAILED FALSE-MEMTRACK 1 0.74 95500 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 2 UNKNOWN 0 9.61 340900 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 2 UNKNOWN 0 14.15 140688 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 2 UNKNOWN 0 38.99 611464 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 2 FAILED FALSE-FREE 779 free_17 1 0.92 96084 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 2 FAILED FALSE-FREE 779 free_17 1 21.03 143264 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 2 FAILED FALSE-FREE 779 free_17 1 0.98 95772 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 2 UNKNOWN 0 9.65 506708 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 2 UNKNOWN 0 4.58 138644 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 2 UNKNOWN 0 40.08 507008 +ldv-memsafety/memsetNonZero valid-memsafety afl 2 UNKNOWN 0 9.46 93612 +ldv-memsafety/memsetNonZero valid-memsafety symex 2 SUCCEEDED 1 0.37 127468 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 2 SUCCEEDED 1 5.09 341028 +list-ext-properties/960521-1_1-1 valid-memsafety afl 2 FAILED FALSE-DEREF 533 main 1 0.63 93356 +list-ext-properties/960521-1_1-1 valid-memsafety symex 2 FAILED FALSE-DEREF 0 1 1.50 136360 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 533 main 1 0.65 93536 +list-ext-properties/960521-1_1-2 valid-memsafety afl 2 UNKNOWN 0 9.49 341556 +list-ext-properties/960521-1_1-2 valid-memsafety symex 2 SUCCEEDED 1 14.52 137400 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 2 UNKNOWN 0 39.12 341460 +list-ext-properties/960521-1_1-3 valid-memsafety afl 2 FAILED FALSE-FREE 536 main 1 0.64 94336 +list-ext-properties/960521-1_1-3 valid-memsafety symex 2 FAILED FALSE-FREE 536 main 1 1.70 136580 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 2 FAILED FALSE-FREE 536 main 1 0.64 93444 +list-ext-properties/list-ext unreach-call afl 2 FAILED FALSE 556 main 1 0.56 92900 +list-ext-properties/list-ext unreach-call symex 2 UNKNOWN 0 0.44 133880 +list-ext-properties/list-ext unreach-call hybrid 2 FAILED FALSE 556 main 1 0.57 92180 +list-ext-properties/list-ext valid-memsafety afl 2 UNKNOWN 0 0.57 94760 +list-ext-properties/list-ext valid-memsafety symex 2 FAILED FALSE-DEREF 558 main 1 12.65 138664 +list-ext-properties/list-ext valid-memsafety hybrid 2 FAILED FALSE-DEREF 558 main 1 12.22 138428 +list-ext-properties/simple-ext unreach-call afl 2 FAILED FALSE 540 main 1 0.56 92040 +list-ext-properties/simple-ext unreach-call symex 2 UNKNOWN 0 0.41 133956 +list-ext-properties/simple-ext unreach-call hybrid 2 FAILED FALSE 540 main 1 0.55 91540 +list-ext-properties/simple-ext valid-memcleanup afl 2 UNKNOWN 0 9.49 341136 +list-ext-properties/simple-ext valid-memcleanup symex 2 UNKNOWN 0 0.93 135384 +list-ext-properties/simple-ext valid-memcleanup hybrid 2 UNKNOWN 0 35.06 381288 +list-ext-properties/test-0019_1-1 valid-memsafety afl 2 FAILED FALSE-MEMTRACK 1 0.68 92980 +list-ext-properties/test-0019_1-1 valid-memsafety symex 2 FAILED FALSE-MEMTRACK 1 0.61 127968 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 2 FAILED FALSE-MEMTRACK 1 0.62 93592 +list-ext-properties/test-0158_1-1 valid-memsafety afl 2 UNKNOWN 0 9.52 341160 +list-ext-properties/test-0158_1-1 valid-memsafety symex 2 SUCCEEDED 1 0.52 135240 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 2 SUCCEEDED 1 5.13 340808 +map2check-assert/array_bug unreach-call afl 2 FAILED FALSE 7 __VERIFIER_assert 1 0.55 92168 +map2check-assert/array_bug unreach-call symex 2 FAILED FALSE 7 __VERIFIER_assert 1 0.43 133944 +map2check-assert/array_bug unreach-call hybrid 2 FAILED FALSE 7 __VERIFIER_assert 1 0.56 92680 +map2check-assert/byte_add_bug unreach-call afl 2 UNKNOWN 0 9.44 341092 +map2check-assert/byte_add_bug unreach-call symex 2 SUCCEEDED 1 0.40 127200 +map2check-assert/byte_add_bug unreach-call hybrid 2 SUCCEEDED 1 5.00 127136 +map2check-assert/check_if_bug unreach-call afl 2 UNKNOWN 0 0.39 92024 +map2check-assert/check_if_bug unreach-call symex 2 UNKNOWN 0 31.82 183288 +map2check-assert/check_if_bug unreach-call hybrid 2 UNKNOWN 0 35.35 167956 +map2check-assert/ex01_false unreach-call afl 2 UNKNOWN 0 1.42 91260 +map2check-assert/ex01_false unreach-call symex 2 UNKNOWN 0 1.87 142840 +map2check-assert/ex01_false unreach-call hybrid 2 FAILED FALSE 5 __VERIFIER_assert 1 0.54 92388 +map2check-assert/ex02_false unreach-call afl 2 UNKNOWN 0 0.36 92608 +map2check-assert/ex02_false unreach-call symex 2 UNKNOWN 0 31.46 224468 +map2check-assert/ex02_false unreach-call hybrid 2 UNKNOWN 0 35.32 205496 +memory-alloca/c.03-alloca-2 valid-memsafety afl 2 UNKNOWN 0 9.48 93788 +memory-alloca/c.03-alloca-2 valid-memsafety symex 2 UNKNOWN 0 31.87 169156 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 2 UNKNOWN 0 42.14 177836 +memsafety-ext/dll_extends_pointer valid-memsafety afl 2 UNKNOWN 0 9.54 341352 +memsafety-ext/dll_extends_pointer valid-memsafety symex 2 UNKNOWN 0 31.47 154212 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 2 UNKNOWN 0 39.18 463804 +memsafety-ext/tree_stack valid-memsafety afl 2 UNKNOWN 0 9.58 783568 +memsafety-ext/tree_stack valid-memsafety symex 2 UNKNOWN 0 31.61 181288 +memsafety-ext/tree_stack valid-memsafety hybrid 2 UNKNOWN 0 40.33 357636 +memsafety-ext2/length_test03-1 valid-memsafety afl 2 UNKNOWN 0 9.53 613656 +memsafety-ext2/length_test03-1 valid-memsafety symex 2 UNKNOWN 0 31.38 144508 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 2 UNKNOWN 0 39.14 735256 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 2 FAILED FALSE-DEREF 540 containsHead 1 0.67 94344 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 2 UNKNOWN 0 31.96 260004 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 540 containsHead 1 0.70 94664 +memsafety/960521-1-1 valid-memsafety afl 2 FAILED FALSE-FREE 532 main 1 0.74 93024 +memsafety/960521-1-1 valid-memsafety symex 2 FAILED FALSE-FREE 532 main 1 1.07 128172 +memsafety/960521-1-1 valid-memsafety hybrid 2 FAILED FALSE-FREE 532 main 1 0.67 93280 +memsafety/960521-1-2 valid-memsafety afl 2 UNKNOWN 0 9.58 94464 +memsafety/960521-1-2 valid-memsafety symex 2 UNKNOWN 0 31.39 128016 +memsafety/960521-1-2 valid-memsafety hybrid 2 UNKNOWN 0 38.81 128472 +memsafety/960521-1-3 valid-memsafety afl 2 FAILED FALSE-DEREF 529 main 1 0.67 93304 +memsafety/960521-1-3 valid-memsafety symex 2 FAILED FALSE-DEREF 529 main 1 1.07 128556 +memsafety/960521-1-3 valid-memsafety hybrid 2 FAILED FALSE-DEREF 529 main 1 0.67 93336 +memsafety/test-0102-1 valid-memsafety afl 2 UNKNOWN 0 9.52 341320 +memsafety/test-0102-1 valid-memsafety symex 2 UNKNOWN 0 31.49 149116 +memsafety/test-0102-1 valid-memsafety hybrid 2 UNKNOWN 0 39.79 1018128 +array-examples/relax-1 valid-memsafety afl 3 ERROR 0 0.81 94828 +array-examples/relax-1 valid-memsafety symex 3 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.50 145112 +array-examples/relax-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.95 145168 +array-examples/standard_sentinel-2 unreach-call afl 3 UNKNOWN 0 9.51 8020464 +array-examples/standard_sentinel-2 unreach-call symex 3 UNKNOWN 0 31.35 175284 +array-examples/standard_sentinel-2 unreach-call hybrid 3 UNKNOWN 0 38.91 8632204 +array-examples/standard_two_index_04 unreach-call afl 3 UNKNOWN 0 9.47 9203228 +array-examples/standard_two_index_04 unreach-call symex 3 UNKNOWN 0 31.37 172876 +array-examples/standard_two_index_04 unreach-call hybrid 3 UNKNOWN 0 38.67 5049948 +array-examples/standard_two_index_05 unreach-call afl 3 UNKNOWN 0 9.40 10001628 +array-examples/standard_two_index_05 unreach-call symex 3 UNKNOWN 0 31.38 172964 +array-examples/standard_two_index_05 unreach-call hybrid 3 UNKNOWN 0 38.98 10506928 +array-memsafety/add_last_unsafe valid-memsafety afl 3 FAILED FALSE-DEREF 522 main 1 0.63 93744 +array-memsafety/add_last_unsafe valid-memsafety symex 3 FAILED FALSE-DEREF 522 main 1 0.63 143408 +array-memsafety/add_last_unsafe valid-memsafety hybrid 3 FAILED FALSE-DEREF 522 main 1 0.61 93848 +array-memsafety/array01-alloca-2 valid-memsafety afl 3 UNKNOWN 0 2.64 93548 +array-memsafety/array01-alloca-2 valid-memsafety symex 3 UNKNOWN 0 31.50 169392 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 3 UNKNOWN 0 40.79 233332 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 3 UNKNOWN 0 0.85 93556 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 3 UNKNOWN 0 1.43 144944 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 3 UNKNOWN 0 40.27 189924 +array-memsafety/lis_unsafe valid-memsafety afl 3 FAILED FALSE-DEREF 521 lis 1 0.65 93492 +array-memsafety/lis_unsafe valid-memsafety symex 3 UNKNOWN 0 1.57 144620 +array-memsafety/lis_unsafe valid-memsafety hybrid 3 FAILED FALSE-DEREF 521 lis 1 0.64 93828 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 3 UNKNOWN 0 9.49 341324 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 3 SUCCEEDED 1 0.42 127520 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 3 SUCCEEDED 1 5.07 128684 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 3 FAILED FALSE-FREE 46 main 1 0.60 93588 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 3 FAILED FALSE-FREE 46 main 1 0.44 128552 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 3 FAILED FALSE-FREE 46 main 1 0.62 93928 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 3 FAILED FALSE-DEREF 42 main 1 0.61 93664 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 3 FAILED FALSE-DEREF 42 main 1 0.43 127948 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 3 FAILED FALSE-DEREF 42 main 1 0.62 93508 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 3 UNKNOWN 0 9.48 94044 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 3 UNKNOWN 0 31.36 127500 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 3 UNKNOWN 0 38.80 128108 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 3 UNKNOWN 0 1.50 93800 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 3 UNKNOWN 0 31.36 128468 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 3 UNKNOWN 0 39.81 128556 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 3 FAILED FALSE-MEMTRACK 1 0.72 96068 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 3 FAILED FALSE-MEMTRACK 1 4.46 139524 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 3 FAILED FALSE-MEMTRACK 1 0.74 95300 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 3 UNKNOWN 0 9.62 614616 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 3 UNKNOWN 0 14.00 140792 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 3 UNKNOWN 0 38.54 609368 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 3 FAILED FALSE-FREE 779 free_17 1 1.50 96440 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 3 FAILED FALSE-FREE 779 free_17 1 19.40 142880 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 3 FAILED FALSE-FREE 779 free_17 1 0.97 95612 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 3 UNKNOWN 0 9.60 507096 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 3 UNKNOWN 0 4.54 139128 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 3 UNKNOWN 0 40.11 507220 +ldv-memsafety/memsetNonZero valid-memsafety afl 3 UNKNOWN 0 9.46 93704 +ldv-memsafety/memsetNonZero valid-memsafety symex 3 SUCCEEDED 1 0.39 126968 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 3 SUCCEEDED 1 5.05 128288 +list-ext-properties/960521-1_1-1 valid-memsafety afl 3 FAILED FALSE-DEREF 533 main 1 0.63 93744 +list-ext-properties/960521-1_1-1 valid-memsafety symex 3 FAILED FALSE-DEREF 0 1 1.46 136536 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 533 main 1 0.63 93468 +list-ext-properties/960521-1_1-2 valid-memsafety afl 3 UNKNOWN 0 9.49 341224 +list-ext-properties/960521-1_1-2 valid-memsafety symex 3 SUCCEEDED 1 14.47 136804 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 3 UNKNOWN 0 38.97 341520 +list-ext-properties/960521-1_1-3 valid-memsafety afl 3 FAILED FALSE-FREE 536 main 1 0.66 93348 +list-ext-properties/960521-1_1-3 valid-memsafety symex 3 FAILED FALSE-FREE 536 main 1 1.62 136720 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 3 FAILED FALSE-FREE 536 main 1 0.64 94648 +list-ext-properties/list-ext unreach-call afl 3 FAILED FALSE 556 main 1 0.53 92028 +list-ext-properties/list-ext unreach-call symex 3 UNKNOWN 0 0.44 134312 +list-ext-properties/list-ext unreach-call hybrid 3 FAILED FALSE 556 main 1 0.55 92076 +list-ext-properties/list-ext valid-memsafety afl 3 UNKNOWN 0 0.56 94000 +list-ext-properties/list-ext valid-memsafety symex 3 FAILED FALSE-DEREF 558 main 1 11.99 138848 +list-ext-properties/list-ext valid-memsafety hybrid 3 FAILED FALSE-DEREF 558 main 1 12.23 139276 +list-ext-properties/simple-ext unreach-call afl 3 FAILED FALSE 540 main 1 0.55 92712 +list-ext-properties/simple-ext unreach-call symex 3 UNKNOWN 0 0.39 133556 +list-ext-properties/simple-ext unreach-call hybrid 3 FAILED FALSE 540 main 1 0.53 92340 +list-ext-properties/simple-ext valid-memcleanup afl 3 UNKNOWN 0 9.50 341448 +list-ext-properties/simple-ext valid-memcleanup symex 3 UNKNOWN 0 0.92 135192 +list-ext-properties/simple-ext valid-memcleanup hybrid 3 UNKNOWN 0 35.04 350184 +list-ext-properties/test-0019_1-1 valid-memsafety afl 3 FAILED FALSE-MEMTRACK 1 0.61 94236 +list-ext-properties/test-0019_1-1 valid-memsafety symex 3 FAILED FALSE-MEMTRACK 1 0.59 128120 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 3 FAILED FALSE-MEMTRACK 1 0.62 93664 +list-ext-properties/test-0158_1-1 valid-memsafety afl 3 UNKNOWN 0 9.49 341160 +list-ext-properties/test-0158_1-1 valid-memsafety symex 3 SUCCEEDED 1 0.52 135112 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 3 SUCCEEDED 1 5.09 340980 +map2check-assert/array_bug unreach-call afl 3 FAILED FALSE 7 __VERIFIER_assert 1 0.52 91844 +map2check-assert/array_bug unreach-call symex 3 FAILED FALSE 7 __VERIFIER_assert 1 0.37 133692 +map2check-assert/array_bug unreach-call hybrid 3 FAILED FALSE 7 __VERIFIER_assert 1 0.54 91764 +map2check-assert/byte_add_bug unreach-call afl 3 UNKNOWN 0 9.43 340940 +map2check-assert/byte_add_bug unreach-call symex 3 SUCCEEDED 1 0.36 127220 +map2check-assert/byte_add_bug unreach-call hybrid 3 SUCCEEDED 1 4.99 127472 +map2check-assert/check_if_bug unreach-call afl 3 UNKNOWN 0 0.37 91572 +map2check-assert/check_if_bug unreach-call symex 3 UNKNOWN 0 31.81 183340 +map2check-assert/check_if_bug unreach-call hybrid 3 UNKNOWN 0 35.30 167772 +map2check-assert/ex01_false unreach-call afl 3 FAILED FALSE 5 __VERIFIER_assert 1 0.54 92608 +map2check-assert/ex01_false unreach-call symex 3 UNKNOWN 0 1.67 142212 +map2check-assert/ex01_false unreach-call hybrid 3 FAILED FALSE 5 __VERIFIER_assert 1 0.53 91808 +map2check-assert/ex02_false unreach-call afl 3 UNKNOWN 0 0.37 91924 +map2check-assert/ex02_false unreach-call symex 3 UNKNOWN 0 31.40 225816 +map2check-assert/ex02_false unreach-call hybrid 3 UNKNOWN 0 35.28 212204 +memory-alloca/c.03-alloca-2 valid-memsafety afl 3 UNKNOWN 0 9.47 93644 +memory-alloca/c.03-alloca-2 valid-memsafety symex 3 UNKNOWN 0 31.73 169616 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 3 UNKNOWN 0 41.85 178676 +memsafety-ext/dll_extends_pointer valid-memsafety afl 3 UNKNOWN 0 9.50 479508 +memsafety-ext/dll_extends_pointer valid-memsafety symex 3 UNKNOWN 0 31.43 158976 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 3 UNKNOWN 0 39.21 436796 +memsafety-ext/tree_stack valid-memsafety afl 3 UNKNOWN 0 9.51 514328 +memsafety-ext/tree_stack valid-memsafety symex 3 UNKNOWN 0 31.61 191820 +memsafety-ext/tree_stack valid-memsafety hybrid 3 UNKNOWN 0 40.41 531648 +memsafety-ext2/length_test03-1 valid-memsafety afl 3 UNKNOWN 0 9.52 662936 +memsafety-ext2/length_test03-1 valid-memsafety symex 3 UNKNOWN 0 31.35 142552 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 3 UNKNOWN 0 39.09 763596 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 3 FAILED FALSE-DEREF 540 containsHead 1 0.65 94288 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 3 UNKNOWN 0 31.94 278264 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 540 containsHead 1 0.64 94192 +memsafety/960521-1-1 valid-memsafety afl 3 FAILED FALSE-FREE 532 main 1 0.62 93576 +memsafety/960521-1-1 valid-memsafety symex 3 FAILED FALSE-FREE 532 main 1 0.97 128492 +memsafety/960521-1-1 valid-memsafety hybrid 3 FAILED FALSE-FREE 532 main 1 0.62 93700 +memsafety/960521-1-2 valid-memsafety afl 3 UNKNOWN 0 9.48 93560 +memsafety/960521-1-2 valid-memsafety symex 3 UNKNOWN 0 31.36 127428 +memsafety/960521-1-2 valid-memsafety hybrid 3 UNKNOWN 0 38.80 128484 +memsafety/960521-1-3 valid-memsafety afl 3 FAILED FALSE-DEREF 529 main 1 0.61 93596 +memsafety/960521-1-3 valid-memsafety symex 3 FAILED FALSE-DEREF 529 main 1 0.90 128396 +memsafety/960521-1-3 valid-memsafety hybrid 3 FAILED FALSE-DEREF 529 main 1 0.63 93664 +memsafety/test-0102-1 valid-memsafety afl 3 UNKNOWN 0 9.54 611016 +memsafety/test-0102-1 valid-memsafety symex 3 UNKNOWN 0 31.39 149876 +memsafety/test-0102-1 valid-memsafety hybrid 3 UNKNOWN 0 39.48 740144 +array-examples/relax-1 valid-memsafety afl 4 ERROR 0 0.81 94016 +array-examples/relax-1 valid-memsafety symex 4 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.55 145588 +array-examples/relax-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.89 145116 +array-examples/standard_sentinel-2 unreach-call afl 4 UNKNOWN 0 9.44 9372364 +array-examples/standard_sentinel-2 unreach-call symex 4 UNKNOWN 0 31.41 173724 +array-examples/standard_sentinel-2 unreach-call hybrid 4 UNKNOWN 0 39.06 10044764 +array-examples/standard_two_index_04 unreach-call afl 4 UNKNOWN 0 9.71 11315064 +array-examples/standard_two_index_04 unreach-call symex 4 UNKNOWN 0 31.36 172800 +array-examples/standard_two_index_04 unreach-call hybrid 4 UNKNOWN 0 38.68 4951976 +array-examples/standard_two_index_05 unreach-call afl 4 UNKNOWN 0 9.71 11416720 +array-examples/standard_two_index_05 unreach-call symex 4 UNKNOWN 0 31.37 173684 +array-examples/standard_two_index_05 unreach-call hybrid 4 UNKNOWN 0 38.96 10933560 +array-memsafety/add_last_unsafe valid-memsafety afl 4 FAILED FALSE-DEREF 522 main 1 0.63 93472 +array-memsafety/add_last_unsafe valid-memsafety symex 4 FAILED FALSE-DEREF 522 main 1 0.61 143672 +array-memsafety/add_last_unsafe valid-memsafety hybrid 4 FAILED FALSE-DEREF 522 main 1 0.61 93224 +array-memsafety/array01-alloca-2 valid-memsafety afl 4 UNKNOWN 0 2.83 94096 +array-memsafety/array01-alloca-2 valid-memsafety symex 4 UNKNOWN 0 31.52 169628 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 4 UNKNOWN 0 42.20 242164 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 4 UNKNOWN 0 2.30 94160 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 4 UNKNOWN 0 1.42 146660 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 4 UNKNOWN 0 40.63 207180 +array-memsafety/lis_unsafe valid-memsafety afl 4 FAILED FALSE-DEREF 521 lis 1 0.63 93368 +array-memsafety/lis_unsafe valid-memsafety symex 4 UNKNOWN 0 1.58 145720 +array-memsafety/lis_unsafe valid-memsafety hybrid 4 FAILED FALSE-DEREF 521 lis 1 0.63 93344 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 4 UNKNOWN 0 9.47 93440 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 4 SUCCEEDED 1 0.43 128628 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 4 SUCCEEDED 1 5.08 341148 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 4 FAILED FALSE-FREE 46 main 1 0.61 93396 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 4 FAILED FALSE-FREE 46 main 1 0.45 128180 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 4 FAILED FALSE-FREE 46 main 1 0.61 94168 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 4 FAILED FALSE-DEREF 42 main 1 0.61 93832 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 4 FAILED FALSE-DEREF 42 main 1 0.43 128332 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 4 FAILED FALSE-DEREF 42 main 1 0.60 93536 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 4 UNKNOWN 0 9.47 93448 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 4 UNKNOWN 0 31.34 127880 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 4 UNKNOWN 0 38.79 128112 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 4 UNKNOWN 0 1.49 94432 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 4 UNKNOWN 0 31.36 128276 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 4 UNKNOWN 0 39.78 128124 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 4 FAILED FALSE-MEMTRACK 1 0.73 95304 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 4 FAILED FALSE-MEMTRACK 1 4.22 139092 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 4 FAILED FALSE-MEMTRACK 1 0.74 95072 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 4 UNKNOWN 0 9.61 616008 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 4 UNKNOWN 0 13.42 140592 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 4 UNKNOWN 0 38.98 609416 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 4 FAILED FALSE-FREE 779 free_17 1 1.79 95652 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 4 FAILED FALSE-FREE 779 free_17 1 19.28 143124 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 4 FAILED FALSE-FREE 779 free_17 1 1.02 96496 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 4 UNKNOWN 0 9.60 507056 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 4 UNKNOWN 0 4.17 138908 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 4 UNKNOWN 0 40.08 507260 +ldv-memsafety/memsetNonZero valid-memsafety afl 4 UNKNOWN 0 9.45 92868 +ldv-memsafety/memsetNonZero valid-memsafety symex 4 SUCCEEDED 1 0.36 128616 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 4 SUCCEEDED 1 5.07 341012 +list-ext-properties/960521-1_1-1 valid-memsafety afl 4 FAILED FALSE-DEREF 533 main 1 0.62 92856 +list-ext-properties/960521-1_1-1 valid-memsafety symex 4 FAILED FALSE-DEREF 0 1 1.46 136044 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 533 main 1 0.63 93524 +list-ext-properties/960521-1_1-2 valid-memsafety afl 4 UNKNOWN 0 9.50 341312 +list-ext-properties/960521-1_1-2 valid-memsafety symex 4 SUCCEEDED 1 14.28 137284 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 4 UNKNOWN 0 38.96 341456 +list-ext-properties/960521-1_1-3 valid-memsafety afl 4 FAILED FALSE-FREE 536 main 1 0.62 93852 +list-ext-properties/960521-1_1-3 valid-memsafety symex 4 FAILED FALSE-FREE 536 main 1 1.57 136364 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 4 FAILED FALSE-FREE 536 main 1 0.61 93628 +list-ext-properties/list-ext unreach-call afl 4 FAILED FALSE 556 main 1 0.52 91812 +list-ext-properties/list-ext unreach-call symex 4 UNKNOWN 0 0.45 134248 +list-ext-properties/list-ext unreach-call hybrid 4 FAILED FALSE 556 main 1 0.54 92092 +list-ext-properties/list-ext valid-memsafety afl 4 UNKNOWN 0 0.56 94052 +list-ext-properties/list-ext valid-memsafety symex 4 FAILED FALSE-DEREF 558 main 1 12.29 138820 +list-ext-properties/list-ext valid-memsafety hybrid 4 FAILED FALSE-DEREF 558 main 1 12.23 138632 +list-ext-properties/simple-ext unreach-call afl 4 FAILED FALSE 540 main 1 0.55 91584 +list-ext-properties/simple-ext unreach-call symex 4 UNKNOWN 0 0.41 133760 +list-ext-properties/simple-ext unreach-call hybrid 4 FAILED FALSE 540 main 1 0.55 92056 +list-ext-properties/simple-ext valid-memcleanup afl 4 UNKNOWN 0 9.49 341424 +list-ext-properties/simple-ext valid-memcleanup symex 4 UNKNOWN 0 0.97 135208 +list-ext-properties/simple-ext valid-memcleanup hybrid 4 UNKNOWN 0 35.03 387516 +list-ext-properties/test-0019_1-1 valid-memsafety afl 4 FAILED FALSE-MEMTRACK 1 0.62 93408 +list-ext-properties/test-0019_1-1 valid-memsafety symex 4 FAILED FALSE-MEMTRACK 1 0.58 127672 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 4 FAILED FALSE-MEMTRACK 1 0.62 93804 +list-ext-properties/test-0158_1-1 valid-memsafety afl 4 UNKNOWN 0 9.49 340784 +list-ext-properties/test-0158_1-1 valid-memsafety symex 4 SUCCEEDED 1 0.52 134252 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 4 SUCCEEDED 1 5.08 341412 +map2check-assert/array_bug unreach-call afl 4 FAILED FALSE 7 __VERIFIER_assert 1 0.53 92516 +map2check-assert/array_bug unreach-call symex 4 FAILED FALSE 7 __VERIFIER_assert 1 0.38 133404 +map2check-assert/array_bug unreach-call hybrid 4 FAILED FALSE 7 __VERIFIER_assert 1 0.53 91884 +map2check-assert/byte_add_bug unreach-call afl 4 UNKNOWN 0 9.42 341116 +map2check-assert/byte_add_bug unreach-call symex 4 SUCCEEDED 1 0.34 127404 +map2check-assert/byte_add_bug unreach-call hybrid 4 SUCCEEDED 1 4.98 127004 +map2check-assert/check_if_bug unreach-call afl 4 UNKNOWN 0 0.37 91644 +map2check-assert/check_if_bug unreach-call symex 4 UNKNOWN 0 31.87 183660 +map2check-assert/check_if_bug unreach-call hybrid 4 UNKNOWN 0 35.30 176388 +map2check-assert/ex01_false unreach-call afl 4 FAILED FALSE 5 __VERIFIER_assert 1 0.53 91604 +map2check-assert/ex01_false unreach-call symex 4 UNKNOWN 0 1.66 142432 +map2check-assert/ex01_false unreach-call hybrid 4 FAILED FALSE 5 __VERIFIER_assert 1 0.53 91068 +map2check-assert/ex02_false unreach-call afl 4 UNKNOWN 0 0.38 91660 +map2check-assert/ex02_false unreach-call symex 4 UNKNOWN 0 31.43 226528 +map2check-assert/ex02_false unreach-call hybrid 4 UNKNOWN 0 35.32 211416 +memory-alloca/c.03-alloca-2 valid-memsafety afl 4 UNKNOWN 0 9.48 93080 +memory-alloca/c.03-alloca-2 valid-memsafety symex 4 UNKNOWN 0 31.85 169860 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 4 UNKNOWN 0 42.15 178104 +memsafety-ext/dll_extends_pointer valid-memsafety afl 4 UNKNOWN 0 9.50 564460 +memsafety-ext/dll_extends_pointer valid-memsafety symex 4 UNKNOWN 0 31.43 158984 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 4 UNKNOWN 0 39.26 341808 +memsafety-ext/tree_stack valid-memsafety afl 4 UNKNOWN 0 9.53 511236 +memsafety-ext/tree_stack valid-memsafety symex 4 UNKNOWN 0 31.61 191496 +memsafety-ext/tree_stack valid-memsafety hybrid 4 UNKNOWN 0 40.51 546500 +memsafety-ext2/length_test03-1 valid-memsafety afl 4 UNKNOWN 0 9.49 565736 +memsafety-ext2/length_test03-1 valid-memsafety symex 4 UNKNOWN 0 31.36 141676 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 4 UNKNOWN 0 39.11 676884 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 4 FAILED FALSE-DEREF 540 containsHead 1 0.66 94144 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 4 UNKNOWN 0 31.95 278140 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 540 containsHead 1 0.65 94044 +memsafety/960521-1-1 valid-memsafety afl 4 FAILED FALSE-FREE 532 main 1 0.61 93472 +memsafety/960521-1-1 valid-memsafety symex 4 FAILED FALSE-FREE 532 main 1 0.95 128584 +memsafety/960521-1-1 valid-memsafety hybrid 4 FAILED FALSE-FREE 532 main 1 0.61 94560 +memsafety/960521-1-2 valid-memsafety afl 4 UNKNOWN 0 9.47 92712 +memsafety/960521-1-2 valid-memsafety symex 4 UNKNOWN 0 31.35 128036 +memsafety/960521-1-2 valid-memsafety hybrid 4 UNKNOWN 0 38.82 129672 +memsafety/960521-1-3 valid-memsafety afl 4 FAILED FALSE-DEREF 529 main 1 0.62 93336 +memsafety/960521-1-3 valid-memsafety symex 4 FAILED FALSE-DEREF 529 main 1 0.93 128400 +memsafety/960521-1-3 valid-memsafety hybrid 4 FAILED FALSE-DEREF 529 main 1 0.63 93032 +memsafety/test-0102-1 valid-memsafety afl 4 UNKNOWN 0 9.54 592368 +memsafety/test-0102-1 valid-memsafety symex 4 UNKNOWN 0 31.38 150160 +memsafety/test-0102-1 valid-memsafety hybrid 4 UNKNOWN 0 39.53 798916 +array-examples/relax-1 valid-memsafety afl 5 ERROR 0 0.80 93504 +array-examples/relax-1 valid-memsafety symex 5 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.50 145588 +array-examples/relax-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.38 145188 +array-examples/standard_sentinel-2 unreach-call afl 5 UNKNOWN 0 9.71 11660176 +array-examples/standard_sentinel-2 unreach-call symex 5 UNKNOWN 0 31.36 173288 +array-examples/standard_sentinel-2 unreach-call hybrid 5 UNKNOWN 0 39.23 11146968 +array-examples/standard_two_index_04 unreach-call afl 5 UNKNOWN 0 9.69 11399268 +array-examples/standard_two_index_04 unreach-call symex 5 UNKNOWN 0 31.35 174012 +array-examples/standard_two_index_04 unreach-call hybrid 5 UNKNOWN 0 38.95 10989592 +array-examples/standard_two_index_05 unreach-call afl 5 UNKNOWN 0 9.70 11346780 +array-examples/standard_two_index_05 unreach-call symex 5 UNKNOWN 0 31.35 173712 +array-examples/standard_two_index_05 unreach-call hybrid 5 UNKNOWN 0 38.97 10961756 +array-memsafety/add_last_unsafe valid-memsafety afl 5 FAILED FALSE-DEREF 522 main 1 0.63 93388 +array-memsafety/add_last_unsafe valid-memsafety symex 5 FAILED FALSE-DEREF 522 main 1 0.60 144052 +array-memsafety/add_last_unsafe valid-memsafety hybrid 5 FAILED FALSE-DEREF 522 main 1 0.62 93500 +array-memsafety/array01-alloca-2 valid-memsafety afl 5 UNKNOWN 0 0.79 93692 +array-memsafety/array01-alloca-2 valid-memsafety symex 5 UNKNOWN 0 31.47 169996 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 5 UNKNOWN 0 42.14 214360 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 5 UNKNOWN 0 0.98 94504 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 5 UNKNOWN 0 1.40 144592 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 5 UNKNOWN 0 40.41 221132 +array-memsafety/lis_unsafe valid-memsafety afl 5 FAILED FALSE-DEREF 521 lis 1 0.63 94400 +array-memsafety/lis_unsafe valid-memsafety symex 5 UNKNOWN 0 1.56 144640 +array-memsafety/lis_unsafe valid-memsafety hybrid 5 FAILED FALSE-DEREF 521 lis 1 0.63 93692 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 5 UNKNOWN 0 9.47 93120 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 5 SUCCEEDED 1 0.43 128224 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 5 SUCCEEDED 1 5.05 127856 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 5 FAILED FALSE-FREE 46 main 1 0.61 93536 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 5 FAILED FALSE-FREE 46 main 1 0.44 128132 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 5 FAILED FALSE-FREE 46 main 1 0.60 94144 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 5 FAILED FALSE-DEREF 42 main 1 0.62 93652 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 5 FAILED FALSE-DEREF 42 main 1 0.43 128100 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 5 FAILED FALSE-DEREF 42 main 1 0.63 93388 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 5 UNKNOWN 0 9.49 94016 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 5 UNKNOWN 0 31.35 127772 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 5 UNKNOWN 0 38.76 127704 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 5 UNKNOWN 0 1.50 93952 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 5 UNKNOWN 0 31.36 128488 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 5 UNKNOWN 0 39.78 128620 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 5 FAILED FALSE-MEMTRACK 1 0.73 96184 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 5 FAILED FALSE-MEMTRACK 1 4.14 139360 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 5 FAILED FALSE-MEMTRACK 1 0.72 95436 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 5 UNKNOWN 0 9.64 610952 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 5 UNKNOWN 0 13.13 140660 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 5 UNKNOWN 0 39.04 609624 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 5 FAILED FALSE-FREE 779 free_17 1 1.12 95336 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 5 FAILED FALSE-FREE 779 free_17 1 19.95 142572 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 5 FAILED FALSE-FREE 779 free_17 1 0.91 95840 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 5 UNKNOWN 0 9.61 507092 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 5 UNKNOWN 0 4.19 139328 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 5 UNKNOWN 0 40.61 507092 +ldv-memsafety/memsetNonZero valid-memsafety afl 5 UNKNOWN 0 9.46 93132 +ldv-memsafety/memsetNonZero valid-memsafety symex 5 SUCCEEDED 1 0.37 128836 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 5 SUCCEEDED 1 5.06 341224 +list-ext-properties/960521-1_1-1 valid-memsafety afl 5 FAILED FALSE-DEREF 533 main 1 0.62 94320 +list-ext-properties/960521-1_1-1 valid-memsafety symex 5 FAILED FALSE-DEREF 0 1 1.55 136528 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 533 main 1 0.61 93504 +list-ext-properties/960521-1_1-2 valid-memsafety afl 5 UNKNOWN 0 9.49 341204 +list-ext-properties/960521-1_1-2 valid-memsafety symex 5 SUCCEEDED 1 13.89 136240 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 5 UNKNOWN 0 38.97 341276 +list-ext-properties/960521-1_1-3 valid-memsafety afl 5 FAILED FALSE-FREE 536 main 1 0.65 93448 +list-ext-properties/960521-1_1-3 valid-memsafety symex 5 FAILED FALSE-FREE 536 main 1 1.56 136420 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 5 FAILED FALSE-FREE 536 main 1 0.67 94396 +list-ext-properties/list-ext unreach-call afl 5 FAILED FALSE 556 main 1 0.52 92348 +list-ext-properties/list-ext unreach-call symex 5 UNKNOWN 0 0.42 134296 +list-ext-properties/list-ext unreach-call hybrid 5 FAILED FALSE 556 main 1 0.56 91928 +list-ext-properties/list-ext valid-memsafety afl 5 UNKNOWN 0 0.60 93624 +list-ext-properties/list-ext valid-memsafety symex 5 FAILED FALSE-DEREF 558 main 1 12.58 138180 +list-ext-properties/list-ext valid-memsafety hybrid 5 FAILED FALSE-DEREF 558 main 1 12.23 139392 +list-ext-properties/simple-ext unreach-call afl 5 FAILED FALSE 540 main 1 0.57 92496 +list-ext-properties/simple-ext unreach-call symex 5 UNKNOWN 0 0.40 133820 +list-ext-properties/simple-ext unreach-call hybrid 5 FAILED FALSE 540 main 1 0.54 92496 +list-ext-properties/simple-ext valid-memcleanup afl 5 UNKNOWN 0 9.49 341216 +list-ext-properties/simple-ext valid-memcleanup symex 5 UNKNOWN 0 0.96 134856 +list-ext-properties/simple-ext valid-memcleanup hybrid 5 UNKNOWN 0 35.04 341424 +list-ext-properties/test-0019_1-1 valid-memsafety afl 5 FAILED FALSE-MEMTRACK 1 0.60 93372 +list-ext-properties/test-0019_1-1 valid-memsafety symex 5 FAILED FALSE-MEMTRACK 1 0.56 127988 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 5 FAILED FALSE-MEMTRACK 1 0.61 93248 +list-ext-properties/test-0158_1-1 valid-memsafety afl 5 UNKNOWN 0 9.49 341244 +list-ext-properties/test-0158_1-1 valid-memsafety symex 5 SUCCEEDED 1 0.48 135252 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 5 SUCCEEDED 1 5.09 340912 +map2check-assert/array_bug unreach-call afl 5 FAILED FALSE 7 __VERIFIER_assert 1 0.53 91236 +map2check-assert/array_bug unreach-call symex 5 FAILED FALSE 7 __VERIFIER_assert 1 0.37 133968 +map2check-assert/array_bug unreach-call hybrid 5 FAILED FALSE 7 __VERIFIER_assert 1 0.53 91660 +map2check-assert/byte_add_bug unreach-call afl 5 UNKNOWN 0 9.42 341312 +map2check-assert/byte_add_bug unreach-call symex 5 SUCCEEDED 1 0.34 127208 +map2check-assert/byte_add_bug unreach-call hybrid 5 SUCCEEDED 1 5.01 340832 +map2check-assert/check_if_bug unreach-call afl 5 UNKNOWN 0 0.37 92616 +map2check-assert/check_if_bug unreach-call symex 5 UNKNOWN 0 31.72 184364 +map2check-assert/check_if_bug unreach-call hybrid 5 UNKNOWN 0 35.30 167852 +map2check-assert/ex01_false unreach-call afl 5 FAILED FALSE 5 __VERIFIER_assert 1 0.53 91920 +map2check-assert/ex01_false unreach-call symex 5 UNKNOWN 0 1.61 142656 +map2check-assert/ex01_false unreach-call hybrid 5 FAILED FALSE 5 __VERIFIER_assert 1 0.54 92580 +map2check-assert/ex02_false unreach-call afl 5 UNKNOWN 0 0.36 91724 +map2check-assert/ex02_false unreach-call symex 5 UNKNOWN 0 31.42 225444 +map2check-assert/ex02_false unreach-call hybrid 5 UNKNOWN 0 35.29 210800 +memory-alloca/c.03-alloca-2 valid-memsafety afl 5 UNKNOWN 0 9.48 94452 +memory-alloca/c.03-alloca-2 valid-memsafety symex 5 UNKNOWN 0 31.72 169724 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 5 UNKNOWN 0 41.97 178748 +memsafety-ext/dll_extends_pointer valid-memsafety afl 5 UNKNOWN 0 9.50 667564 +memsafety-ext/dll_extends_pointer valid-memsafety symex 5 UNKNOWN 0 31.44 159380 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 5 UNKNOWN 0 39.31 455756 +memsafety-ext/tree_stack valid-memsafety afl 5 UNKNOWN 0 9.53 658248 +memsafety-ext/tree_stack valid-memsafety symex 5 UNKNOWN 0 31.60 192116 +memsafety-ext/tree_stack valid-memsafety hybrid 5 UNKNOWN 0 40.52 342428 +memsafety-ext2/length_test03-1 valid-memsafety afl 5 UNKNOWN 0 9.49 478448 +memsafety-ext2/length_test03-1 valid-memsafety symex 5 UNKNOWN 0 31.36 142068 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 5 UNKNOWN 0 39.02 778176 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 5 FAILED FALSE-DEREF 540 containsHead 1 0.67 94912 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 5 UNKNOWN 0 31.96 278440 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 540 containsHead 1 0.66 94052 +memsafety/960521-1-1 valid-memsafety afl 5 FAILED FALSE-FREE 532 main 1 0.60 93212 +memsafety/960521-1-1 valid-memsafety symex 5 FAILED FALSE-FREE 532 main 1 0.98 128468 +memsafety/960521-1-1 valid-memsafety hybrid 5 FAILED FALSE-FREE 532 main 1 0.62 93232 +memsafety/960521-1-2 valid-memsafety afl 5 UNKNOWN 0 9.48 94340 +memsafety/960521-1-2 valid-memsafety symex 5 UNKNOWN 0 31.35 127432 +memsafety/960521-1-2 valid-memsafety hybrid 5 UNKNOWN 0 38.79 127544 +memsafety/960521-1-3 valid-memsafety afl 5 FAILED FALSE-DEREF 529 main 1 0.62 94456 +memsafety/960521-1-3 valid-memsafety symex 5 FAILED FALSE-DEREF 529 main 1 0.95 128368 +memsafety/960521-1-3 valid-memsafety hybrid 5 FAILED FALSE-DEREF 529 main 1 0.63 93160 +memsafety/test-0102-1 valid-memsafety afl 5 UNKNOWN 0 9.53 515828 +memsafety/test-0102-1 valid-memsafety symex 5 UNKNOWN 0 31.39 150380 +memsafety/test-0102-1 valid-memsafety hybrid 5 UNKNOWN 0 39.55 675652 diff --git a/docs/cutover/baselines/c-3e914528ec57-20261027.tsv b/docs/cutover/baselines/c-3e914528ec57-20261027.tsv new file mode 100644 index 0000000..64f2c06 --- /dev/null +++ b/docs/cutover/baselines/c-3e914528ec57-20261027.tsv @@ -0,0 +1,616 @@ +task prp gen rep verdict tag line function witness wall_s maxrss_kb +array-examples/relax-1 valid-memsafety afl 1 ERROR 0 0.85 94356 +array-examples/relax-1 valid-memsafety symex 1 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.54 145604 +array-examples/relax-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.38 145140 +array-examples/standard_sentinel-2 unreach-call afl 1 UNKNOWN 0 9.71 9208444 +array-examples/standard_sentinel-2 unreach-call symex 1 UNKNOWN 0 31.40 173924 +array-examples/standard_sentinel-2 unreach-call hybrid 1 UNKNOWN 0 39.34 10629424 +array-examples/standard_two_index_04 unreach-call afl 1 UNKNOWN 0 9.77 11180448 +array-examples/standard_two_index_04 unreach-call symex 1 UNKNOWN 0 31.40 173048 +array-examples/standard_two_index_04 unreach-call hybrid 1 UNKNOWN 0 39.05 10364328 +array-examples/standard_two_index_05 unreach-call afl 1 UNKNOWN 0 9.80 10801728 +array-examples/standard_two_index_05 unreach-call symex 1 UNKNOWN 0 31.38 173052 +array-examples/standard_two_index_05 unreach-call hybrid 1 UNKNOWN 0 38.93 9913644 +array-memsafety/add_last_unsafe valid-memsafety afl 1 FAILED FALSE-DEREF 522 main 1 0.69 92888 +array-memsafety/add_last_unsafe valid-memsafety symex 1 FAILED FALSE-DEREF 522 main 1 0.70 142848 +array-memsafety/add_last_unsafe valid-memsafety hybrid 1 FAILED FALSE-DEREF 522 main 1 0.76 92860 +array-memsafety/array01-alloca-2 valid-memsafety afl 1 UNKNOWN 0 0.89 92620 +array-memsafety/array01-alloca-2 valid-memsafety symex 1 UNKNOWN 0 31.52 169252 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 1 UNKNOWN 0 40.75 218592 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 1 UNKNOWN 0 1.12 93628 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 1 UNKNOWN 0 1.43 144072 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 1 UNKNOWN 0 40.10 178484 +array-memsafety/lis_unsafe valid-memsafety afl 1 FAILED FALSE-DEREF 521 lis 1 0.70 93172 +array-memsafety/lis_unsafe valid-memsafety symex 1 UNKNOWN 0 1.72 145904 +array-memsafety/lis_unsafe valid-memsafety hybrid 1 FAILED FALSE-DEREF 521 lis 1 0.68 93136 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 1 UNKNOWN 0 9.50 93224 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 1 SUCCEEDED 1 0.45 127276 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 1 SUCCEEDED 1 5.12 341100 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 1 FAILED FALSE-FREE 46 main 1 0.65 92672 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 1 FAILED FALSE-FREE 46 main 1 0.50 126944 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 1 FAILED FALSE-FREE 46 main 1 0.66 93588 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 1 FAILED FALSE-DEREF 42 main 1 0.66 93136 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 1 FAILED FALSE-DEREF 42 main 1 0.45 128168 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 1 FAILED FALSE-DEREF 42 main 1 0.69 93628 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 1 UNKNOWN 0 9.52 93676 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 1 UNKNOWN 0 31.38 127252 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 1 UNKNOWN 0 38.77 128300 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 1 UNKNOWN 0 1.48 93404 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 1 UNKNOWN 0 31.35 128296 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 1 UNKNOWN 0 39.85 128152 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 1 FAILED FALSE-MEMTRACK 1 0.71 94796 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 1 FAILED FALSE-MEMTRACK 1 4.21 138172 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 1 FAILED FALSE-MEMTRACK 1 0.73 95184 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 1 UNKNOWN 0 9.62 608476 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 1 UNKNOWN 0 13.32 140352 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 1 UNKNOWN 0 39.05 612312 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 1 FAILED FALSE-FREE 779 free_17 1 0.90 95708 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 1 FAILED FALSE-FREE 779 free_17 1 19.56 142280 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 1 FAILED FALSE-FREE 779 free_17 1 1.05 95252 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 1 UNKNOWN 0 9.63 506964 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 1 UNKNOWN 0 4.30 138552 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 1 UNKNOWN 0 40.14 506884 +ldv-memsafety/memsetNonZero valid-memsafety afl 1 UNKNOWN 0 9.46 92400 +ldv-memsafety/memsetNonZero valid-memsafety symex 1 SUCCEEDED 1 0.37 128252 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 1 SUCCEEDED 1 5.06 128104 +list-ext-properties/960521-1_1-1 valid-memsafety afl 1 FAILED FALSE-DEREF 533 main 1 0.62 93816 +list-ext-properties/960521-1_1-1 valid-memsafety symex 1 FAILED FALSE-DEREF 0 1 1.46 136500 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 533 main 1 0.65 93176 +list-ext-properties/960521-1_1-2 valid-memsafety afl 1 UNKNOWN 0 9.54 341012 +list-ext-properties/960521-1_1-2 valid-memsafety symex 1 SUCCEEDED 1 16.35 136664 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 1 UNKNOWN 0 39.00 341312 +list-ext-properties/960521-1_1-3 valid-memsafety afl 1 FAILED FALSE-FREE 536 main 1 0.62 93228 +list-ext-properties/960521-1_1-3 valid-memsafety symex 1 FAILED FALSE-FREE 536 main 1 1.63 136136 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 1 FAILED FALSE-FREE 536 main 1 0.62 93812 +list-ext-properties/list-ext unreach-call afl 1 FAILED FALSE 556 main 1 0.54 91384 +list-ext-properties/list-ext unreach-call symex 1 UNKNOWN 0 0.44 133908 +list-ext-properties/list-ext unreach-call hybrid 1 FAILED FALSE 556 main 1 0.55 91680 +list-ext-properties/list-ext valid-memsafety afl 1 UNKNOWN 0 0.55 93208 +list-ext-properties/list-ext valid-memsafety symex 1 FAILED FALSE-DEREF 558 main 1 11.92 138616 +list-ext-properties/list-ext valid-memsafety hybrid 1 FAILED FALSE-DEREF 558 main 1 12.23 138032 +list-ext-properties/simple-ext unreach-call afl 1 FAILED FALSE 540 main 1 0.57 91860 +list-ext-properties/simple-ext unreach-call symex 1 UNKNOWN 0 0.41 133044 +list-ext-properties/simple-ext unreach-call hybrid 1 FAILED FALSE 540 main 1 0.55 91648 +list-ext-properties/simple-ext valid-memcleanup afl 1 UNKNOWN 0 9.50 341204 +list-ext-properties/simple-ext valid-memcleanup symex 1 UNKNOWN 0 0.92 135008 +list-ext-properties/simple-ext valid-memcleanup hybrid 1 UNKNOWN 0 35.05 361960 +list-ext-properties/test-0019_1-1 valid-memsafety afl 1 FAILED FALSE-MEMTRACK 1 0.65 93848 +list-ext-properties/test-0019_1-1 valid-memsafety symex 1 FAILED FALSE-MEMTRACK 1 0.67 127916 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 1 FAILED FALSE-MEMTRACK 1 0.66 93620 +list-ext-properties/test-0158_1-1 valid-memsafety afl 1 UNKNOWN 0 9.51 341016 +list-ext-properties/test-0158_1-1 valid-memsafety symex 1 SUCCEEDED 1 0.53 135128 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 1 SUCCEEDED 1 5.10 340700 +map2check-assert/array_bug unreach-call afl 1 FAILED FALSE 7 __VERIFIER_assert 1 0.55 91880 +map2check-assert/array_bug unreach-call symex 1 FAILED FALSE 7 __VERIFIER_assert 1 0.39 133744 +map2check-assert/array_bug unreach-call hybrid 1 FAILED FALSE 7 __VERIFIER_assert 1 0.53 91888 +map2check-assert/byte_add_bug unreach-call afl 1 UNKNOWN 0 9.44 340844 +map2check-assert/byte_add_bug unreach-call symex 1 SUCCEEDED 1 0.35 127112 +map2check-assert/byte_add_bug unreach-call hybrid 1 SUCCEEDED 1 4.99 127292 +map2check-assert/check_if_bug unreach-call afl 1 UNKNOWN 0 0.38 91344 +map2check-assert/check_if_bug unreach-call symex 1 UNKNOWN 0 31.73 183968 +map2check-assert/check_if_bug unreach-call hybrid 1 UNKNOWN 0 35.37 166928 +map2check-assert/ex01_false unreach-call afl 1 FAILED FALSE 5 __VERIFIER_assert 1 0.57 91704 +map2check-assert/ex01_false unreach-call symex 1 UNKNOWN 0 1.68 142280 +map2check-assert/ex01_false unreach-call hybrid 1 FAILED FALSE 5 __VERIFIER_assert 1 0.54 90860 +map2check-assert/ex02_false unreach-call afl 1 UNKNOWN 0 0.38 91216 +map2check-assert/ex02_false unreach-call symex 1 UNKNOWN 0 31.39 223332 +map2check-assert/ex02_false unreach-call hybrid 1 UNKNOWN 0 35.29 209268 +memory-alloca/c.03-alloca-2 valid-memsafety afl 1 UNKNOWN 0 9.48 93820 +memory-alloca/c.03-alloca-2 valid-memsafety symex 1 UNKNOWN 0 31.86 169552 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 1 UNKNOWN 0 41.46 178764 +memsafety-ext/dll_extends_pointer valid-memsafety afl 1 UNKNOWN 0 9.51 550592 +memsafety-ext/dll_extends_pointer valid-memsafety symex 1 UNKNOWN 0 31.44 157804 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 1 UNKNOWN 0 39.28 375852 +memsafety-ext/tree_stack valid-memsafety afl 1 UNKNOWN 0 9.53 615744 +memsafety-ext/tree_stack valid-memsafety symex 1 UNKNOWN 0 31.59 188292 +memsafety-ext/tree_stack valid-memsafety hybrid 1 UNKNOWN 0 40.51 1049032 +memsafety-ext2/length_test03-1 valid-memsafety afl 1 UNKNOWN 0 9.53 689088 +memsafety-ext2/length_test03-1 valid-memsafety symex 1 UNKNOWN 0 31.39 141108 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 1 UNKNOWN 0 39.23 692248 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 1 FAILED FALSE-DEREF 540 containsHead 1 0.72 93468 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 1 UNKNOWN 0 31.95 265260 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 540 containsHead 1 0.66 94032 +memsafety/960521-1-1 valid-memsafety afl 1 FAILED FALSE-FREE 532 main 1 0.62 93160 +memsafety/960521-1-1 valid-memsafety symex 1 FAILED FALSE-FREE 532 main 1 1.00 127740 +memsafety/960521-1-1 valid-memsafety hybrid 1 FAILED FALSE-FREE 532 main 1 0.63 93688 +memsafety/960521-1-2 valid-memsafety afl 1 UNKNOWN 0 9.47 93004 +memsafety/960521-1-2 valid-memsafety symex 1 UNKNOWN 0 31.35 127284 +memsafety/960521-1-2 valid-memsafety hybrid 1 UNKNOWN 0 38.83 127724 +memsafety/960521-1-3 valid-memsafety afl 1 FAILED FALSE-DEREF 529 main 1 0.65 92916 +memsafety/960521-1-3 valid-memsafety symex 1 FAILED FALSE-DEREF 529 main 1 0.95 127948 +memsafety/960521-1-3 valid-memsafety hybrid 1 FAILED FALSE-DEREF 529 main 1 0.65 92776 +memsafety/test-0102-1 valid-memsafety afl 1 UNKNOWN 0 9.55 684828 +memsafety/test-0102-1 valid-memsafety symex 1 UNKNOWN 0 31.40 149524 +memsafety/test-0102-1 valid-memsafety hybrid 1 UNKNOWN 0 39.54 918708 +array-examples/relax-1 valid-memsafety afl 2 ERROR 0 0.84 93732 +array-examples/relax-1 valid-memsafety symex 2 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.52 144684 +array-examples/relax-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.42 153108 +array-examples/standard_sentinel-2 unreach-call afl 2 UNKNOWN 0 9.71 9074352 +array-examples/standard_sentinel-2 unreach-call symex 2 UNKNOWN 0 31.46 172492 +array-examples/standard_sentinel-2 unreach-call hybrid 2 UNKNOWN 0 38.84 9336416 +array-examples/standard_two_index_04 unreach-call afl 2 UNKNOWN 0 9.40 10262584 +array-examples/standard_two_index_04 unreach-call symex 2 UNKNOWN 0 31.38 173460 +array-examples/standard_two_index_04 unreach-call hybrid 2 UNKNOWN 0 38.67 9368948 +array-examples/standard_two_index_05 unreach-call afl 2 UNKNOWN 0 9.42 9610544 +array-examples/standard_two_index_05 unreach-call symex 2 UNKNOWN 0 31.36 174084 +array-examples/standard_two_index_05 unreach-call hybrid 2 UNKNOWN 0 38.99 9398612 +array-memsafety/add_last_unsafe valid-memsafety afl 2 FAILED FALSE-DEREF 522 main 1 0.61 93356 +array-memsafety/add_last_unsafe valid-memsafety symex 2 FAILED FALSE-DEREF 522 main 1 0.62 143220 +array-memsafety/add_last_unsafe valid-memsafety hybrid 2 FAILED FALSE-DEREF 522 main 1 0.61 94656 +array-memsafety/array01-alloca-2 valid-memsafety afl 2 UNKNOWN 0 2.25 93620 +array-memsafety/array01-alloca-2 valid-memsafety symex 2 UNKNOWN 0 31.43 165624 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 2 UNKNOWN 0 40.73 246700 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 2 UNKNOWN 0 0.88 94064 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 2 UNKNOWN 0 1.56 145044 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 2 UNKNOWN 0 40.67 178268 +array-memsafety/lis_unsafe valid-memsafety afl 2 FAILED FALSE-DEREF 521 lis 1 0.67 93716 +array-memsafety/lis_unsafe valid-memsafety symex 2 UNKNOWN 0 1.72 144496 +array-memsafety/lis_unsafe valid-memsafety hybrid 2 FAILED FALSE-DEREF 521 lis 1 0.68 94560 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 2 UNKNOWN 0 9.52 340528 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 2 SUCCEEDED 1 0.46 128012 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 2 SUCCEEDED 1 5.09 128844 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 2 FAILED FALSE-FREE 46 main 1 0.65 93628 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 2 FAILED FALSE-FREE 46 main 1 0.49 128388 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 2 FAILED FALSE-FREE 46 main 1 0.62 93708 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 2 FAILED FALSE-DEREF 42 main 1 0.66 93888 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 2 FAILED FALSE-DEREF 42 main 1 0.46 128524 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 2 FAILED FALSE-DEREF 42 main 1 0.66 93636 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 2 UNKNOWN 0 9.50 93608 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 2 UNKNOWN 0 31.39 127656 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 2 UNKNOWN 0 38.79 128016 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 2 UNKNOWN 0 1.51 93748 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 2 UNKNOWN 0 31.36 129064 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 2 UNKNOWN 0 39.78 128824 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 2 FAILED FALSE-MEMTRACK 1 0.73 95388 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 2 FAILED FALSE-MEMTRACK 1 4.18 139204 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 2 FAILED FALSE-MEMTRACK 1 0.72 96192 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 2 UNKNOWN 0 9.61 341092 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 2 UNKNOWN 0 13.32 141220 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 2 UNKNOWN 0 38.54 612152 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 2 FAILED FALSE-FREE 779 free_17 1 0.95 95724 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 2 FAILED FALSE-FREE 779 free_17 1 19.24 142772 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 2 FAILED FALSE-FREE 779 free_17 1 1.76 96676 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 2 UNKNOWN 0 9.61 506836 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 2 UNKNOWN 0 4.26 138800 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 2 UNKNOWN 0 40.09 506836 +ldv-memsafety/memsetNonZero valid-memsafety afl 2 UNKNOWN 0 9.49 340924 +ldv-memsafety/memsetNonZero valid-memsafety symex 2 SUCCEEDED 1 0.37 128644 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 2 SUCCEEDED 1 5.05 127600 +list-ext-properties/960521-1_1-1 valid-memsafety afl 2 FAILED FALSE-DEREF 533 main 1 0.62 93192 +list-ext-properties/960521-1_1-1 valid-memsafety symex 2 FAILED FALSE-DEREF 0 1 1.53 136604 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 533 main 1 0.63 93868 +list-ext-properties/960521-1_1-2 valid-memsafety afl 2 UNKNOWN 0 9.49 341040 +list-ext-properties/960521-1_1-2 valid-memsafety symex 2 SUCCEEDED 1 14.13 136280 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 2 UNKNOWN 0 38.98 341276 +list-ext-properties/960521-1_1-3 valid-memsafety afl 2 FAILED FALSE-FREE 536 main 1 0.62 93624 +list-ext-properties/960521-1_1-3 valid-memsafety symex 2 FAILED FALSE-FREE 536 main 1 1.60 136492 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 2 FAILED FALSE-FREE 536 main 1 0.61 93748 +list-ext-properties/list-ext unreach-call afl 2 FAILED FALSE 556 main 1 0.54 91748 +list-ext-properties/list-ext unreach-call symex 2 UNKNOWN 0 0.43 134332 +list-ext-properties/list-ext unreach-call hybrid 2 FAILED FALSE 556 main 1 0.55 92364 +list-ext-properties/list-ext valid-memsafety afl 2 UNKNOWN 0 0.55 94188 +list-ext-properties/list-ext valid-memsafety symex 2 FAILED FALSE-DEREF 558 main 1 11.73 139772 +list-ext-properties/list-ext valid-memsafety hybrid 2 FAILED FALSE-DEREF 558 main 1 12.23 138880 +list-ext-properties/simple-ext unreach-call afl 2 FAILED FALSE 540 main 1 0.53 92136 +list-ext-properties/simple-ext unreach-call symex 2 UNKNOWN 0 0.40 133552 +list-ext-properties/simple-ext unreach-call hybrid 2 FAILED FALSE 540 main 1 0.55 92012 +list-ext-properties/simple-ext valid-memcleanup afl 2 UNKNOWN 0 9.49 341084 +list-ext-properties/simple-ext valid-memcleanup symex 2 UNKNOWN 0 0.93 134916 +list-ext-properties/simple-ext valid-memcleanup hybrid 2 UNKNOWN 0 35.06 364208 +list-ext-properties/test-0019_1-1 valid-memsafety afl 2 FAILED FALSE-MEMTRACK 1 0.61 93312 +list-ext-properties/test-0019_1-1 valid-memsafety symex 2 FAILED FALSE-MEMTRACK 1 0.57 128208 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 2 FAILED FALSE-MEMTRACK 1 0.61 94536 +list-ext-properties/test-0158_1-1 valid-memsafety afl 2 UNKNOWN 0 9.49 341280 +list-ext-properties/test-0158_1-1 valid-memsafety symex 2 SUCCEEDED 1 0.49 135632 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 2 SUCCEEDED 1 5.09 341040 +map2check-assert/array_bug unreach-call afl 2 FAILED FALSE 7 __VERIFIER_assert 1 0.55 91800 +map2check-assert/array_bug unreach-call symex 2 FAILED FALSE 7 __VERIFIER_assert 1 0.37 133720 +map2check-assert/array_bug unreach-call hybrid 2 FAILED FALSE 7 __VERIFIER_assert 1 0.56 92812 +map2check-assert/byte_add_bug unreach-call afl 2 UNKNOWN 0 9.40 92256 +map2check-assert/byte_add_bug unreach-call symex 2 SUCCEEDED 1 0.37 126944 +map2check-assert/byte_add_bug unreach-call hybrid 2 SUCCEEDED 1 4.99 127544 +map2check-assert/check_if_bug unreach-call afl 2 UNKNOWN 0 0.37 92676 +map2check-assert/check_if_bug unreach-call symex 2 UNKNOWN 0 31.83 185704 +map2check-assert/check_if_bug unreach-call hybrid 2 UNKNOWN 0 35.30 167688 +map2check-assert/ex01_false unreach-call afl 2 FAILED FALSE 5 __VERIFIER_assert 1 0.53 92836 +map2check-assert/ex01_false unreach-call symex 2 UNKNOWN 0 1.64 142220 +map2check-assert/ex01_false unreach-call hybrid 2 FAILED FALSE 5 __VERIFIER_assert 1 0.54 91824 +map2check-assert/ex02_false unreach-call afl 2 UNKNOWN 0 0.38 91860 +map2check-assert/ex02_false unreach-call symex 2 UNKNOWN 0 31.42 223520 +map2check-assert/ex02_false unreach-call hybrid 2 UNKNOWN 0 35.34 210892 +memory-alloca/c.03-alloca-2 valid-memsafety afl 2 UNKNOWN 0 9.49 93444 +memory-alloca/c.03-alloca-2 valid-memsafety symex 2 UNKNOWN 0 31.74 170404 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 2 UNKNOWN 0 40.90 170508 +memsafety-ext/dll_extends_pointer valid-memsafety afl 2 UNKNOWN 0 9.50 419388 +memsafety-ext/dll_extends_pointer valid-memsafety symex 2 UNKNOWN 0 31.44 159268 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 2 UNKNOWN 0 39.40 634668 +memsafety-ext/tree_stack valid-memsafety afl 2 UNKNOWN 0 9.54 874888 +memsafety-ext/tree_stack valid-memsafety symex 2 UNKNOWN 0 31.59 189716 +memsafety-ext/tree_stack valid-memsafety hybrid 2 UNKNOWN 0 40.39 357652 +memsafety-ext2/length_test03-1 valid-memsafety afl 2 UNKNOWN 0 9.48 93588 +memsafety-ext2/length_test03-1 valid-memsafety symex 2 UNKNOWN 0 31.36 141656 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 2 UNKNOWN 0 39.08 750664 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 2 FAILED FALSE-DEREF 540 containsHead 1 0.66 94040 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 2 UNKNOWN 0 31.94 270604 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 540 containsHead 1 0.66 93752 +memsafety/960521-1-1 valid-memsafety afl 2 FAILED FALSE-FREE 532 main 1 0.62 94228 +memsafety/960521-1-1 valid-memsafety symex 2 FAILED FALSE-FREE 532 main 1 0.98 128124 +memsafety/960521-1-1 valid-memsafety hybrid 2 FAILED FALSE-FREE 532 main 1 0.62 94396 +memsafety/960521-1-2 valid-memsafety afl 2 UNKNOWN 0 9.48 94180 +memsafety/960521-1-2 valid-memsafety symex 2 UNKNOWN 0 31.36 128748 +memsafety/960521-1-2 valid-memsafety hybrid 2 UNKNOWN 0 38.81 128044 +memsafety/960521-1-3 valid-memsafety afl 2 FAILED FALSE-DEREF 529 main 1 0.62 93692 +memsafety/960521-1-3 valid-memsafety symex 2 FAILED FALSE-DEREF 529 main 1 0.92 128472 +memsafety/960521-1-3 valid-memsafety hybrid 2 FAILED FALSE-DEREF 529 main 1 0.63 93652 +memsafety/test-0102-1 valid-memsafety afl 2 UNKNOWN 0 9.54 588144 +memsafety/test-0102-1 valid-memsafety symex 2 UNKNOWN 0 31.40 150916 +memsafety/test-0102-1 valid-memsafety hybrid 2 UNKNOWN 0 39.54 758212 +array-examples/relax-1 valid-memsafety afl 3 ERROR 0 1.06 94956 +array-examples/relax-1 valid-memsafety symex 3 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.56 145672 +array-examples/relax-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.94 145796 +array-examples/standard_sentinel-2 unreach-call afl 3 UNKNOWN 0 9.50 7959788 +array-examples/standard_sentinel-2 unreach-call symex 3 UNKNOWN 0 31.38 173584 +array-examples/standard_sentinel-2 unreach-call hybrid 3 UNKNOWN 0 38.74 7844464 +array-examples/standard_two_index_04 unreach-call afl 3 UNKNOWN 0 9.47 9087668 +array-examples/standard_two_index_04 unreach-call symex 3 UNKNOWN 0 31.36 173568 +array-examples/standard_two_index_04 unreach-call hybrid 3 UNKNOWN 0 38.73 7823680 +array-examples/standard_two_index_05 unreach-call afl 3 UNKNOWN 0 9.52 7987816 +array-examples/standard_two_index_05 unreach-call symex 3 UNKNOWN 0 31.38 173352 +array-examples/standard_two_index_05 unreach-call hybrid 3 UNKNOWN 0 38.76 7602668 +array-memsafety/add_last_unsafe valid-memsafety afl 3 FAILED FALSE-DEREF 522 main 1 0.62 93400 +array-memsafety/add_last_unsafe valid-memsafety symex 3 FAILED FALSE-DEREF 522 main 1 0.62 144040 +array-memsafety/add_last_unsafe valid-memsafety hybrid 3 FAILED FALSE-DEREF 522 main 1 0.61 93980 +array-memsafety/array01-alloca-2 valid-memsafety afl 3 UNKNOWN 0 3.12 93708 +array-memsafety/array01-alloca-2 valid-memsafety symex 3 UNKNOWN 0 31.50 170212 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 3 UNKNOWN 0 40.69 269168 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 3 UNKNOWN 0 1.00 93424 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 3 UNKNOWN 0 1.43 144196 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 3 UNKNOWN 0 36.89 146752 +array-memsafety/lis_unsafe valid-memsafety afl 3 FAILED FALSE-DEREF 521 lis 1 0.63 93808 +array-memsafety/lis_unsafe valid-memsafety symex 3 UNKNOWN 0 1.62 144404 +array-memsafety/lis_unsafe valid-memsafety hybrid 3 FAILED FALSE-DEREF 521 lis 1 0.64 93624 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 3 UNKNOWN 0 9.47 93684 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 3 SUCCEEDED 1 0.42 128220 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 3 SUCCEEDED 1 5.07 340864 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 3 FAILED FALSE-FREE 46 main 1 0.60 93464 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 3 FAILED FALSE-FREE 46 main 1 0.45 127792 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 3 FAILED FALSE-FREE 46 main 1 0.61 93720 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 3 FAILED FALSE-DEREF 42 main 1 0.61 93632 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 3 FAILED FALSE-DEREF 42 main 1 0.41 127808 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 3 FAILED FALSE-DEREF 42 main 1 0.63 93664 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 3 UNKNOWN 0 9.46 94276 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 3 UNKNOWN 0 31.36 130564 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 3 UNKNOWN 0 38.79 130436 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 3 UNKNOWN 0 1.50 93704 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 3 UNKNOWN 0 31.35 128360 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 3 UNKNOWN 0 39.80 128892 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 3 FAILED FALSE-MEMTRACK 1 0.75 95420 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 3 FAILED FALSE-MEMTRACK 1 4.13 141172 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 3 FAILED FALSE-MEMTRACK 1 0.74 95304 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 3 UNKNOWN 0 9.65 607176 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 3 UNKNOWN 0 13.08 140868 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 3 UNKNOWN 0 39.00 610268 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 3 FAILED FALSE-FREE 779 free_17 1 0.98 95420 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 3 FAILED FALSE-FREE 779 free_17 1 20.10 143504 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 3 FAILED FALSE-FREE 779 free_17 1 0.91 95460 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 3 UNKNOWN 0 9.63 341200 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 3 UNKNOWN 0 4.36 139368 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 3 UNKNOWN 0 40.13 506992 +ldv-memsafety/memsetNonZero valid-memsafety afl 3 UNKNOWN 0 9.48 340720 +ldv-memsafety/memsetNonZero valid-memsafety symex 3 SUCCEEDED 1 0.37 127728 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 3 SUCCEEDED 1 5.05 128292 +list-ext-properties/960521-1_1-1 valid-memsafety afl 3 FAILED FALSE-DEREF 533 main 1 0.62 94408 +list-ext-properties/960521-1_1-1 valid-memsafety symex 3 FAILED FALSE-DEREF 0 1 1.52 136464 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 533 main 1 0.63 93916 +list-ext-properties/960521-1_1-2 valid-memsafety afl 3 UNKNOWN 0 9.51 341064 +list-ext-properties/960521-1_1-2 valid-memsafety symex 3 SUCCEEDED 1 14.37 137328 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 3 UNKNOWN 0 38.97 341448 +list-ext-properties/960521-1_1-3 valid-memsafety afl 3 FAILED FALSE-FREE 536 main 1 0.61 93412 +list-ext-properties/960521-1_1-3 valid-memsafety symex 3 FAILED FALSE-FREE 536 main 1 1.61 136368 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 3 FAILED FALSE-FREE 536 main 1 0.62 94284 +list-ext-properties/list-ext unreach-call afl 3 FAILED FALSE 556 main 1 0.53 92052 +list-ext-properties/list-ext unreach-call symex 3 UNKNOWN 0 0.44 134368 +list-ext-properties/list-ext unreach-call hybrid 3 FAILED FALSE 556 main 1 0.55 92120 +list-ext-properties/list-ext valid-memsafety afl 3 UNKNOWN 0 0.56 93956 +list-ext-properties/list-ext valid-memsafety symex 3 FAILED FALSE-DEREF 558 main 1 11.84 139020 +list-ext-properties/list-ext valid-memsafety hybrid 3 FAILED FALSE-DEREF 558 main 1 12.29 138300 +list-ext-properties/simple-ext unreach-call afl 3 FAILED FALSE 540 main 1 0.55 92196 +list-ext-properties/simple-ext unreach-call symex 3 UNKNOWN 0 0.39 133688 +list-ext-properties/simple-ext unreach-call hybrid 3 FAILED FALSE 540 main 1 0.56 92124 +list-ext-properties/simple-ext valid-memcleanup afl 3 UNKNOWN 0 9.48 363836 +list-ext-properties/simple-ext valid-memcleanup symex 3 UNKNOWN 0 0.92 135012 +list-ext-properties/simple-ext valid-memcleanup hybrid 3 UNKNOWN 0 35.05 349244 +list-ext-properties/test-0019_1-1 valid-memsafety afl 3 FAILED FALSE-MEMTRACK 1 0.64 93592 +list-ext-properties/test-0019_1-1 valid-memsafety symex 3 FAILED FALSE-MEMTRACK 1 0.57 130808 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 3 FAILED FALSE-MEMTRACK 1 0.62 93396 +list-ext-properties/test-0158_1-1 valid-memsafety afl 3 UNKNOWN 0 9.50 340720 +list-ext-properties/test-0158_1-1 valid-memsafety symex 3 SUCCEEDED 1 0.51 134804 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 3 SUCCEEDED 1 5.07 340888 +map2check-assert/array_bug unreach-call afl 3 FAILED FALSE 7 __VERIFIER_assert 1 0.53 91992 +map2check-assert/array_bug unreach-call symex 3 FAILED FALSE 7 __VERIFIER_assert 1 0.38 135892 +map2check-assert/array_bug unreach-call hybrid 3 FAILED FALSE 7 __VERIFIER_assert 1 0.51 91868 +map2check-assert/byte_add_bug unreach-call afl 3 UNKNOWN 0 9.42 340856 +map2check-assert/byte_add_bug unreach-call symex 3 SUCCEEDED 1 0.35 127704 +map2check-assert/byte_add_bug unreach-call hybrid 3 SUCCEEDED 1 4.97 127672 +map2check-assert/check_if_bug unreach-call afl 3 UNKNOWN 0 0.38 92152 +map2check-assert/check_if_bug unreach-call symex 3 UNKNOWN 0 31.69 184752 +map2check-assert/check_if_bug unreach-call hybrid 3 UNKNOWN 0 35.32 167864 +map2check-assert/ex01_false unreach-call afl 3 FAILED FALSE 5 __VERIFIER_assert 1 0.53 92548 +map2check-assert/ex01_false unreach-call symex 3 UNKNOWN 0 1.60 142620 +map2check-assert/ex01_false unreach-call hybrid 3 FAILED FALSE 5 __VERIFIER_assert 1 0.52 91844 +map2check-assert/ex02_false unreach-call afl 3 UNKNOWN 0 0.37 91728 +map2check-assert/ex02_false unreach-call symex 3 UNKNOWN 0 31.39 226580 +map2check-assert/ex02_false unreach-call hybrid 3 UNKNOWN 0 35.34 210312 +memory-alloca/c.03-alloca-2 valid-memsafety afl 3 UNKNOWN 0 9.49 94008 +memory-alloca/c.03-alloca-2 valid-memsafety symex 3 UNKNOWN 0 31.77 168548 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 3 UNKNOWN 0 41.57 186912 +memsafety-ext/dll_extends_pointer valid-memsafety afl 3 UNKNOWN 0 9.49 512428 +memsafety-ext/dll_extends_pointer valid-memsafety symex 3 UNKNOWN 0 31.45 156496 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 3 UNKNOWN 0 39.26 341568 +memsafety-ext/tree_stack valid-memsafety afl 3 UNKNOWN 0 9.50 755436 +memsafety-ext/tree_stack valid-memsafety symex 3 UNKNOWN 0 31.59 188276 +memsafety-ext/tree_stack valid-memsafety hybrid 3 UNKNOWN 0 40.43 414668 +memsafety-ext2/length_test03-1 valid-memsafety afl 3 UNKNOWN 0 9.49 93536 +memsafety-ext2/length_test03-1 valid-memsafety symex 3 UNKNOWN 0 31.37 141552 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 3 UNKNOWN 0 39.12 641940 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 3 FAILED FALSE-DEREF 540 containsHead 1 0.65 94636 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 3 UNKNOWN 0 31.93 271520 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 540 containsHead 1 0.68 93980 +memsafety/960521-1-1 valid-memsafety afl 3 FAILED FALSE-FREE 532 main 1 0.61 93516 +memsafety/960521-1-1 valid-memsafety symex 3 FAILED FALSE-FREE 532 main 1 0.98 128340 +memsafety/960521-1-1 valid-memsafety hybrid 3 FAILED FALSE-FREE 532 main 1 0.62 93864 +memsafety/960521-1-2 valid-memsafety afl 3 UNKNOWN 0 9.47 93652 +memsafety/960521-1-2 valid-memsafety symex 3 UNKNOWN 0 31.35 128696 +memsafety/960521-1-2 valid-memsafety hybrid 3 UNKNOWN 0 38.83 127928 +memsafety/960521-1-3 valid-memsafety afl 3 FAILED FALSE-DEREF 529 main 1 0.63 94444 +memsafety/960521-1-3 valid-memsafety symex 3 FAILED FALSE-DEREF 529 main 1 0.92 128568 +memsafety/960521-1-3 valid-memsafety hybrid 3 FAILED FALSE-DEREF 529 main 1 0.62 93516 +memsafety/test-0102-1 valid-memsafety afl 3 UNKNOWN 0 9.54 658708 +memsafety/test-0102-1 valid-memsafety symex 3 UNKNOWN 0 31.38 150928 +memsafety/test-0102-1 valid-memsafety hybrid 3 UNKNOWN 0 39.51 731444 +array-examples/relax-1 valid-memsafety afl 4 ERROR 0 0.81 94184 +array-examples/relax-1 valid-memsafety symex 4 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.51 145264 +array-examples/relax-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.39 145424 +array-examples/standard_sentinel-2 unreach-call afl 4 UNKNOWN 0 9.43 5450904 +array-examples/standard_sentinel-2 unreach-call symex 4 UNKNOWN 0 31.37 173700 +array-examples/standard_sentinel-2 unreach-call hybrid 4 UNKNOWN 0 38.85 5292304 +array-examples/standard_two_index_04 unreach-call afl 4 UNKNOWN 0 9.56 5850876 +array-examples/standard_two_index_04 unreach-call symex 4 UNKNOWN 0 31.35 173712 +array-examples/standard_two_index_04 unreach-call hybrid 4 UNKNOWN 0 39.42 5896404 +array-examples/standard_two_index_05 unreach-call afl 4 UNKNOWN 0 9.54 6056452 +array-examples/standard_two_index_05 unreach-call symex 4 UNKNOWN 0 31.38 173556 +array-examples/standard_two_index_05 unreach-call hybrid 4 UNKNOWN 0 38.95 5878024 +array-memsafety/add_last_unsafe valid-memsafety afl 4 FAILED FALSE-DEREF 522 main 1 0.62 93832 +array-memsafety/add_last_unsafe valid-memsafety symex 4 FAILED FALSE-DEREF 522 main 1 0.61 143560 +array-memsafety/add_last_unsafe valid-memsafety hybrid 4 FAILED FALSE-DEREF 522 main 1 0.62 93656 +array-memsafety/array01-alloca-2 valid-memsafety afl 4 UNKNOWN 0 0.81 93860 +array-memsafety/array01-alloca-2 valid-memsafety symex 4 UNKNOWN 0 31.46 166104 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 4 UNKNOWN 0 41.24 232824 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 4 UNKNOWN 0 1.05 93936 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 4 UNKNOWN 0 1.53 144420 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 4 UNKNOWN 0 40.60 340828 +array-memsafety/lis_unsafe valid-memsafety afl 4 FAILED FALSE-DEREF 521 lis 1 0.64 94128 +array-memsafety/lis_unsafe valid-memsafety symex 4 UNKNOWN 0 1.65 145052 +array-memsafety/lis_unsafe valid-memsafety hybrid 4 FAILED FALSE-DEREF 521 lis 1 0.64 93732 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 4 UNKNOWN 0 9.48 93704 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 4 SUCCEEDED 1 0.42 127740 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 4 SUCCEEDED 1 5.07 340884 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 4 FAILED FALSE-FREE 46 main 1 0.61 93672 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 4 FAILED FALSE-FREE 46 main 1 0.46 128012 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 4 FAILED FALSE-FREE 46 main 1 0.61 93380 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 4 FAILED FALSE-DEREF 42 main 1 0.63 93804 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 4 FAILED FALSE-DEREF 42 main 1 0.42 128536 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 4 FAILED FALSE-DEREF 42 main 1 0.63 93744 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 4 UNKNOWN 0 9.47 94112 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 4 UNKNOWN 0 31.35 127992 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 4 UNKNOWN 0 38.80 128508 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 4 UNKNOWN 0 1.52 94100 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 4 UNKNOWN 0 31.38 128340 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 4 UNKNOWN 0 39.82 128580 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 4 FAILED FALSE-MEMTRACK 1 0.73 95520 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 4 FAILED FALSE-MEMTRACK 1 4.25 139160 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 4 FAILED FALSE-MEMTRACK 1 0.77 95192 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 4 UNKNOWN 0 9.63 610136 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 4 UNKNOWN 0 13.27 140424 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 4 UNKNOWN 0 39.01 606984 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 4 FAILED FALSE-FREE 779 free_17 1 1.07 95272 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 4 FAILED FALSE-FREE 779 free_17 1 19.88 142628 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 4 FAILED FALSE-FREE 779 free_17 1 1.25 95960 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 4 UNKNOWN 0 9.61 341328 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 4 UNKNOWN 0 4.55 139032 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 4 UNKNOWN 0 40.15 506452 +ldv-memsafety/memsetNonZero valid-memsafety afl 4 UNKNOWN 0 9.46 93236 +ldv-memsafety/memsetNonZero valid-memsafety symex 4 SUCCEEDED 1 0.39 128024 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 4 SUCCEEDED 1 5.05 128132 +list-ext-properties/960521-1_1-1 valid-memsafety afl 4 FAILED FALSE-DEREF 533 main 1 0.61 93572 +list-ext-properties/960521-1_1-1 valid-memsafety symex 4 FAILED FALSE-DEREF 0 1 1.52 136800 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 533 main 1 0.64 93540 +list-ext-properties/960521-1_1-2 valid-memsafety afl 4 UNKNOWN 0 9.50 341380 +list-ext-properties/960521-1_1-2 valid-memsafety symex 4 SUCCEEDED 1 14.06 137316 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 4 UNKNOWN 0 38.98 341476 +list-ext-properties/960521-1_1-3 valid-memsafety afl 4 FAILED FALSE-FREE 536 main 1 0.63 93864 +list-ext-properties/960521-1_1-3 valid-memsafety symex 4 FAILED FALSE-FREE 536 main 1 1.61 136608 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 4 FAILED FALSE-FREE 536 main 1 0.62 93608 +list-ext-properties/list-ext unreach-call afl 4 FAILED FALSE 556 main 1 0.55 92916 +list-ext-properties/list-ext unreach-call symex 4 UNKNOWN 0 0.43 134896 +list-ext-properties/list-ext unreach-call hybrid 4 FAILED FALSE 556 main 1 0.54 92296 +list-ext-properties/list-ext valid-memsafety afl 4 UNKNOWN 0 0.56 93880 +list-ext-properties/list-ext valid-memsafety symex 4 FAILED FALSE-DEREF 558 main 1 12.12 138584 +list-ext-properties/list-ext valid-memsafety hybrid 4 FAILED FALSE-DEREF 558 main 1 12.22 138848 +list-ext-properties/simple-ext unreach-call afl 4 FAILED FALSE 540 main 1 0.54 92280 +list-ext-properties/simple-ext unreach-call symex 4 UNKNOWN 0 0.40 134068 +list-ext-properties/simple-ext unreach-call hybrid 4 FAILED FALSE 540 main 1 0.54 91780 +list-ext-properties/simple-ext valid-memcleanup afl 4 UNKNOWN 0 9.51 341124 +list-ext-properties/simple-ext valid-memcleanup symex 4 UNKNOWN 0 0.97 134620 +list-ext-properties/simple-ext valid-memcleanup hybrid 4 UNKNOWN 0 35.09 341220 +list-ext-properties/test-0019_1-1 valid-memsafety afl 4 FAILED FALSE-MEMTRACK 1 0.65 94468 +list-ext-properties/test-0019_1-1 valid-memsafety symex 4 FAILED FALSE-MEMTRACK 1 0.58 128872 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 4 FAILED FALSE-MEMTRACK 1 0.63 93472 +list-ext-properties/test-0158_1-1 valid-memsafety afl 4 UNKNOWN 0 9.50 341408 +list-ext-properties/test-0158_1-1 valid-memsafety symex 4 SUCCEEDED 1 0.53 135496 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 4 SUCCEEDED 1 5.13 340776 +map2check-assert/array_bug unreach-call afl 4 FAILED FALSE 7 __VERIFIER_assert 1 0.53 92200 +map2check-assert/array_bug unreach-call symex 4 FAILED FALSE 7 __VERIFIER_assert 1 0.37 133496 +map2check-assert/array_bug unreach-call hybrid 4 FAILED FALSE 7 __VERIFIER_assert 1 0.54 92796 +map2check-assert/byte_add_bug unreach-call afl 4 UNKNOWN 0 9.39 93000 +map2check-assert/byte_add_bug unreach-call symex 4 SUCCEEDED 1 0.40 127188 +map2check-assert/byte_add_bug unreach-call hybrid 4 SUCCEEDED 1 4.99 127380 +map2check-assert/check_if_bug unreach-call afl 4 UNKNOWN 0 0.38 92028 +map2check-assert/check_if_bug unreach-call symex 4 UNKNOWN 0 31.68 185536 +map2check-assert/check_if_bug unreach-call hybrid 4 UNKNOWN 0 35.33 167636 +map2check-assert/ex01_false unreach-call afl 4 FAILED FALSE 5 __VERIFIER_assert 1 0.57 92112 +map2check-assert/ex01_false unreach-call symex 4 UNKNOWN 0 1.67 142972 +map2check-assert/ex01_false unreach-call hybrid 4 FAILED FALSE 5 __VERIFIER_assert 1 0.52 91552 +map2check-assert/ex02_false unreach-call afl 4 UNKNOWN 0 0.38 92584 +map2check-assert/ex02_false unreach-call symex 4 UNKNOWN 0 31.43 220872 +map2check-assert/ex02_false unreach-call hybrid 4 UNKNOWN 0 35.33 208996 +memory-alloca/c.03-alloca-2 valid-memsafety afl 4 UNKNOWN 0 9.49 94480 +memory-alloca/c.03-alloca-2 valid-memsafety symex 4 UNKNOWN 0 31.85 169468 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 4 UNKNOWN 0 41.34 178236 +memsafety-ext/dll_extends_pointer valid-memsafety afl 4 UNKNOWN 0 9.51 628284 +memsafety-ext/dll_extends_pointer valid-memsafety symex 4 UNKNOWN 0 31.43 158928 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 4 UNKNOWN 0 39.36 341512 +memsafety-ext/tree_stack valid-memsafety afl 4 UNKNOWN 0 9.53 878600 +memsafety-ext/tree_stack valid-memsafety symex 4 UNKNOWN 0 31.60 190176 +memsafety-ext/tree_stack valid-memsafety hybrid 4 UNKNOWN 0 40.48 342556 +memsafety-ext2/length_test03-1 valid-memsafety afl 4 UNKNOWN 0 9.51 654016 +memsafety-ext2/length_test03-1 valid-memsafety symex 4 UNKNOWN 0 31.38 142072 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 4 UNKNOWN 0 39.13 703376 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 4 FAILED FALSE-DEREF 540 containsHead 1 0.67 95068 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 4 UNKNOWN 0 31.92 260596 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 540 containsHead 1 0.67 94216 +memsafety/960521-1-1 valid-memsafety afl 4 FAILED FALSE-FREE 532 main 1 0.65 93556 +memsafety/960521-1-1 valid-memsafety symex 4 FAILED FALSE-FREE 532 main 1 1.00 129140 +memsafety/960521-1-1 valid-memsafety hybrid 4 FAILED FALSE-FREE 532 main 1 0.64 93696 +memsafety/960521-1-2 valid-memsafety afl 4 UNKNOWN 0 9.50 94312 +memsafety/960521-1-2 valid-memsafety symex 4 UNKNOWN 0 31.37 128212 +memsafety/960521-1-2 valid-memsafety hybrid 4 UNKNOWN 0 38.80 127836 +memsafety/960521-1-3 valid-memsafety afl 4 FAILED FALSE-DEREF 529 main 1 0.64 94292 +memsafety/960521-1-3 valid-memsafety symex 4 FAILED FALSE-DEREF 529 main 1 0.94 130448 +memsafety/960521-1-3 valid-memsafety hybrid 4 FAILED FALSE-DEREF 529 main 1 0.62 93568 +memsafety/test-0102-1 valid-memsafety afl 4 UNKNOWN 0 9.54 604828 +memsafety/test-0102-1 valid-memsafety symex 4 UNKNOWN 0 31.38 150648 +memsafety/test-0102-1 valid-memsafety hybrid 4 UNKNOWN 0 39.56 773276 +array-examples/relax-1 valid-memsafety afl 5 ERROR 0 0.82 93904 +array-examples/relax-1 valid-memsafety symex 5 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.54 146168 +array-examples/relax-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.37 145532 +array-examples/standard_sentinel-2 unreach-call afl 5 UNKNOWN 0 9.44 5550716 +array-examples/standard_sentinel-2 unreach-call symex 5 UNKNOWN 0 31.36 175424 +array-examples/standard_sentinel-2 unreach-call hybrid 5 UNKNOWN 0 38.78 5349152 +array-examples/standard_two_index_04 unreach-call afl 5 UNKNOWN 0 9.55 5752984 +array-examples/standard_two_index_04 unreach-call symex 5 UNKNOWN 0 31.36 173640 +array-examples/standard_two_index_04 unreach-call hybrid 5 UNKNOWN 0 39.28 5881100 +array-examples/standard_two_index_05 unreach-call afl 5 UNKNOWN 0 9.82 11040276 +array-examples/standard_two_index_05 unreach-call symex 5 UNKNOWN 0 31.46 174028 +array-examples/standard_two_index_05 unreach-call hybrid 5 UNKNOWN 0 39.21 10820124 +array-memsafety/add_last_unsafe valid-memsafety afl 5 FAILED FALSE-DEREF 522 main 1 0.66 93416 +array-memsafety/add_last_unsafe valid-memsafety symex 5 FAILED FALSE-DEREF 522 main 1 0.63 144060 +array-memsafety/add_last_unsafe valid-memsafety hybrid 5 FAILED FALSE-DEREF 522 main 1 0.63 94428 +array-memsafety/array01-alloca-2 valid-memsafety afl 5 UNKNOWN 0 1.77 93632 +array-memsafety/array01-alloca-2 valid-memsafety symex 5 UNKNOWN 0 31.48 165972 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 5 UNKNOWN 0 40.84 254900 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 5 UNKNOWN 0 1.00 93648 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 5 UNKNOWN 0 1.42 144840 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 5 UNKNOWN 0 37.41 341220 +array-memsafety/lis_unsafe valid-memsafety afl 5 FAILED FALSE-DEREF 521 lis 1 0.64 93464 +array-memsafety/lis_unsafe valid-memsafety symex 5 UNKNOWN 0 1.57 144784 +array-memsafety/lis_unsafe valid-memsafety hybrid 5 FAILED FALSE-DEREF 521 lis 1 0.63 93628 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 5 UNKNOWN 0 9.47 93092 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 5 SUCCEEDED 1 0.42 127740 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 5 SUCCEEDED 1 5.07 340964 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 5 FAILED FALSE-FREE 46 main 1 0.62 93512 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 5 FAILED FALSE-FREE 46 main 1 0.45 127956 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 5 FAILED FALSE-FREE 46 main 1 0.61 93508 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 5 FAILED FALSE-DEREF 42 main 1 0.64 93556 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 5 FAILED FALSE-DEREF 42 main 1 0.43 128656 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 5 FAILED FALSE-DEREF 42 main 1 0.64 93844 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 5 UNKNOWN 0 9.48 93724 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 5 UNKNOWN 0 31.35 128188 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 5 UNKNOWN 0 38.77 128168 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 5 UNKNOWN 0 1.50 94380 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 5 UNKNOWN 0 31.36 128176 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 5 UNKNOWN 0 39.77 128648 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 5 FAILED FALSE-MEMTRACK 1 0.75 95060 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 5 FAILED FALSE-MEMTRACK 1 4.26 138760 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 5 FAILED FALSE-MEMTRACK 1 0.76 95364 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 5 UNKNOWN 0 9.64 614656 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 5 UNKNOWN 0 13.78 141228 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 5 UNKNOWN 0 39.05 610312 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 5 FAILED FALSE-FREE 779 free_17 1 1.09 95404 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 5 FAILED FALSE-FREE 779 free_17 1 19.54 142956 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 5 FAILED FALSE-FREE 779 free_17 1 0.90 95520 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 5 UNKNOWN 0 9.63 506836 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 5 UNKNOWN 0 4.33 138324 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 5 UNKNOWN 0 40.62 506756 +ldv-memsafety/memsetNonZero valid-memsafety afl 5 UNKNOWN 0 9.46 93452 +ldv-memsafety/memsetNonZero valid-memsafety symex 5 SUCCEEDED 1 0.37 128308 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 5 SUCCEEDED 1 5.06 127600 +list-ext-properties/960521-1_1-1 valid-memsafety afl 5 FAILED FALSE-DEREF 533 main 1 0.65 93180 +list-ext-properties/960521-1_1-1 valid-memsafety symex 5 FAILED FALSE-DEREF 0 1 1.55 136644 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 533 main 1 0.65 93212 +list-ext-properties/960521-1_1-2 valid-memsafety afl 5 UNKNOWN 0 9.50 341232 +list-ext-properties/960521-1_1-2 valid-memsafety symex 5 SUCCEEDED 1 14.33 137084 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 5 UNKNOWN 0 38.98 341308 +list-ext-properties/960521-1_1-3 valid-memsafety afl 5 FAILED FALSE-FREE 536 main 1 0.64 93344 +list-ext-properties/960521-1_1-3 valid-memsafety symex 5 FAILED FALSE-FREE 536 main 1 1.62 136636 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 5 FAILED FALSE-FREE 536 main 1 0.64 93556 +list-ext-properties/list-ext unreach-call afl 5 FAILED FALSE 556 main 1 0.55 92008 +list-ext-properties/list-ext unreach-call symex 5 UNKNOWN 0 0.44 134344 +list-ext-properties/list-ext unreach-call hybrid 5 FAILED FALSE 556 main 1 0.56 92236 +list-ext-properties/list-ext valid-memsafety afl 5 UNKNOWN 0 0.57 94340 +list-ext-properties/list-ext valid-memsafety symex 5 FAILED FALSE-DEREF 558 main 1 11.93 138896 +list-ext-properties/list-ext valid-memsafety hybrid 5 FAILED FALSE-DEREF 558 main 1 12.24 138304 +list-ext-properties/simple-ext unreach-call afl 5 FAILED FALSE 540 main 1 0.53 91840 +list-ext-properties/simple-ext unreach-call symex 5 UNKNOWN 0 0.40 135632 +list-ext-properties/simple-ext unreach-call hybrid 5 FAILED FALSE 540 main 1 0.55 91948 +list-ext-properties/simple-ext valid-memcleanup afl 5 UNKNOWN 0 9.50 348644 +list-ext-properties/simple-ext valid-memcleanup symex 5 UNKNOWN 0 0.94 135076 +list-ext-properties/simple-ext valid-memcleanup hybrid 5 UNKNOWN 0 35.06 365356 +list-ext-properties/test-0019_1-1 valid-memsafety afl 5 FAILED FALSE-MEMTRACK 1 0.62 93484 +list-ext-properties/test-0019_1-1 valid-memsafety symex 5 FAILED FALSE-MEMTRACK 1 0.59 128120 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 5 FAILED FALSE-MEMTRACK 1 0.64 93468 +list-ext-properties/test-0158_1-1 valid-memsafety afl 5 UNKNOWN 0 1597.15 340828 +list-ext-properties/test-0158_1-1 valid-memsafety symex 5 SUCCEEDED 1 0.51 134808 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 5 SUCCEEDED 1 5.08 341232 +map2check-assert/array_bug unreach-call afl 5 FAILED FALSE 7 __VERIFIER_assert 1 0.52 92324 +map2check-assert/array_bug unreach-call symex 5 FAILED FALSE 7 __VERIFIER_assert 1 0.38 133904 +map2check-assert/array_bug unreach-call hybrid 5 FAILED FALSE 7 __VERIFIER_assert 1 0.57 92216 +map2check-assert/byte_add_bug unreach-call afl 5 UNKNOWN 0 9.41 91508 +map2check-assert/byte_add_bug unreach-call symex 5 SUCCEEDED 1 0.35 129588 +map2check-assert/byte_add_bug unreach-call hybrid 5 SUCCEEDED 1 4.98 126860 +map2check-assert/check_if_bug unreach-call afl 5 UNKNOWN 0 0.37 92388 +map2check-assert/check_if_bug unreach-call symex 5 UNKNOWN 0 31.85 183884 +map2check-assert/check_if_bug unreach-call hybrid 5 UNKNOWN 0 37.15 169488 +map2check-assert/ex01_false unreach-call afl 5 FAILED FALSE 5 __VERIFIER_assert 1 0.55 91628 +map2check-assert/ex01_false unreach-call symex 5 UNKNOWN 0 1.65 142640 +map2check-assert/ex01_false unreach-call hybrid 5 FAILED FALSE 5 __VERIFIER_assert 1 0.55 92148 +map2check-assert/ex02_false unreach-call afl 5 UNKNOWN 0 0.38 91608 +map2check-assert/ex02_false unreach-call symex 5 UNKNOWN 0 31.42 223824 +map2check-assert/ex02_false unreach-call hybrid 5 UNKNOWN 0 35.32 207060 +memory-alloca/c.03-alloca-2 valid-memsafety afl 5 UNKNOWN 0 9.48 94068 +memory-alloca/c.03-alloca-2 valid-memsafety symex 5 UNKNOWN 0 31.86 170256 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 5 UNKNOWN 0 41.84 178520 +memsafety-ext/dll_extends_pointer valid-memsafety afl 5 UNKNOWN 0 9.64 484284 +memsafety-ext/dll_extends_pointer valid-memsafety symex 5 UNKNOWN 0 31.51 154056 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 5 UNKNOWN 0 39.21 444908 +memsafety-ext/tree_stack valid-memsafety afl 5 UNKNOWN 0 9.52 457152 +memsafety-ext/tree_stack valid-memsafety symex 5 UNKNOWN 0 31.60 185380 +memsafety-ext/tree_stack valid-memsafety hybrid 5 UNKNOWN 0 40.32 342216 +memsafety-ext2/length_test03-1 valid-memsafety afl 5 UNKNOWN 0 9.54 521064 +memsafety-ext2/length_test03-1 valid-memsafety symex 5 UNKNOWN 0 31.36 141204 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 5 UNKNOWN 0 39.08 727236 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 5 FAILED FALSE-DEREF 540 containsHead 1 0.66 94364 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 5 UNKNOWN 0 31.95 265120 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 540 containsHead 1 0.66 94796 +memsafety/960521-1-1 valid-memsafety afl 5 FAILED FALSE-FREE 532 main 1 0.63 94116 +memsafety/960521-1-1 valid-memsafety symex 5 FAILED FALSE-FREE 532 main 1 1.01 127984 +memsafety/960521-1-1 valid-memsafety hybrid 5 FAILED FALSE-FREE 532 main 1 0.63 94304 +memsafety/960521-1-2 valid-memsafety afl 5 UNKNOWN 0 9.47 93648 +memsafety/960521-1-2 valid-memsafety symex 5 UNKNOWN 0 31.36 128152 +memsafety/960521-1-2 valid-memsafety hybrid 5 UNKNOWN 0 38.79 128464 +memsafety/960521-1-3 valid-memsafety afl 5 FAILED FALSE-DEREF 529 main 1 0.62 93276 +memsafety/960521-1-3 valid-memsafety symex 5 FAILED FALSE-DEREF 529 main 1 0.98 128556 +memsafety/960521-1-3 valid-memsafety hybrid 5 FAILED FALSE-DEREF 529 main 1 0.64 93352 +memsafety/test-0102-1 valid-memsafety afl 5 UNKNOWN 0 9.56 604316 +memsafety/test-0102-1 valid-memsafety symex 5 UNKNOWN 0 31.41 150080 +memsafety/test-0102-1 valid-memsafety hybrid 5 UNKNOWN 0 39.47 825796 diff --git a/docs/cutover/baselines/c-3e914528ec57.tsv b/docs/cutover/baselines/c-3e914528ec57.tsv new file mode 100644 index 0000000..30e9790 --- /dev/null +++ b/docs/cutover/baselines/c-3e914528ec57.tsv @@ -0,0 +1,616 @@ +task prp gen rep verdict tag line function witness wall_s maxrss_kb +array-examples/relax-1 valid-memsafety afl 1 ERROR 0 0.79 94552 +array-examples/relax-1 valid-memsafety symex 1 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.53 145448 +array-examples/relax-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.87 145656 +array-examples/standard_sentinel-2 unreach-call afl 1 UNKNOWN 0 9.54 6077580 +array-examples/standard_sentinel-2 unreach-call symex 1 UNKNOWN 0 31.36 174112 +array-examples/standard_sentinel-2 unreach-call hybrid 1 UNKNOWN 0 39.00 8597388 +array-examples/standard_two_index_04 unreach-call afl 1 UNKNOWN 0 9.77 10326800 +array-examples/standard_two_index_04 unreach-call symex 1 UNKNOWN 0 31.39 172620 +array-examples/standard_two_index_04 unreach-call hybrid 1 UNKNOWN 0 39.10 10045704 +array-examples/standard_two_index_05 unreach-call afl 1 UNKNOWN 0 9.38 10537744 +array-examples/standard_two_index_05 unreach-call symex 1 UNKNOWN 0 31.36 173024 +array-examples/standard_two_index_05 unreach-call hybrid 1 UNKNOWN 0 39.14 10518244 +array-memsafety/add_last_unsafe valid-memsafety afl 1 FAILED FALSE-DEREF 522 main 1 0.62 93632 +array-memsafety/add_last_unsafe valid-memsafety symex 1 FAILED FALSE-DEREF 522 main 1 0.62 143968 +array-memsafety/add_last_unsafe valid-memsafety hybrid 1 FAILED FALSE-DEREF 522 main 1 0.62 93664 +array-memsafety/array01-alloca-2 valid-memsafety afl 1 UNKNOWN 0 2.98 93920 +array-memsafety/array01-alloca-2 valid-memsafety symex 1 UNKNOWN 0 31.47 170080 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 1 UNKNOWN 0 40.85 272356 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 1 UNKNOWN 0 0.87 94176 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 1 UNKNOWN 0 1.46 144572 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 1 UNKNOWN 0 36.72 146416 +array-memsafety/lis_unsafe valid-memsafety afl 1 FAILED FALSE-DEREF 521 lis 1 0.63 93828 +array-memsafety/lis_unsafe valid-memsafety symex 1 UNKNOWN 0 1.60 144580 +array-memsafety/lis_unsafe valid-memsafety hybrid 1 FAILED FALSE-DEREF 521 lis 1 0.63 93460 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 1 UNKNOWN 0 9.46 93404 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 1 SUCCEEDED 1 0.44 128136 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 1 SUCCEEDED 1 5.06 128176 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 1 FAILED FALSE-FREE 46 main 1 0.61 93820 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 1 FAILED FALSE-FREE 46 main 1 0.46 127508 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 1 FAILED FALSE-FREE 46 main 1 0.61 93924 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 1 FAILED FALSE-DEREF 42 main 1 0.62 93764 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 1 FAILED FALSE-DEREF 42 main 1 0.43 127928 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 1 FAILED FALSE-DEREF 42 main 1 0.61 93532 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 1 UNKNOWN 0 9.50 94288 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 1 UNKNOWN 0 31.35 128180 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 1 UNKNOWN 0 38.79 128352 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 1 UNKNOWN 0 1.49 93576 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 1 UNKNOWN 0 31.37 129852 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 1 UNKNOWN 0 39.80 128372 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 1 FAILED FALSE-MEMTRACK 1 0.73 95716 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 1 FAILED FALSE-MEMTRACK 1 4.20 138704 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 1 FAILED FALSE-MEMTRACK 1 0.75 95096 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 1 UNKNOWN 0 9.62 606936 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 1 UNKNOWN 0 14.04 140820 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 1 UNKNOWN 0 39.02 610308 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 1 FAILED FALSE-FREE 779 free_17 1 0.89 95808 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 1 FAILED FALSE-FREE 779 free_17 1 20.25 142968 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 1 FAILED FALSE-FREE 779 free_17 1 1.00 95560 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 1 UNKNOWN 0 9.60 341212 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 1 UNKNOWN 0 4.32 139140 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 1 UNKNOWN 0 40.11 507076 +ldv-memsafety/memsetNonZero valid-memsafety afl 1 UNKNOWN 0 9.46 93216 +ldv-memsafety/memsetNonZero valid-memsafety symex 1 SUCCEEDED 1 0.37 127756 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 1 SUCCEEDED 1 5.08 341424 +list-ext-properties/960521-1_1-1 valid-memsafety afl 1 FAILED FALSE-DEREF 533 main 1 0.63 93436 +list-ext-properties/960521-1_1-1 valid-memsafety symex 1 FAILED FALSE-DEREF 0 1 1.51 136636 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 533 main 1 0.62 93504 +list-ext-properties/960521-1_1-2 valid-memsafety afl 1 UNKNOWN 0 9.49 341028 +list-ext-properties/960521-1_1-2 valid-memsafety symex 1 SUCCEEDED 1 14.95 135956 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 1 UNKNOWN 0 38.98 341536 +list-ext-properties/960521-1_1-3 valid-memsafety afl 1 FAILED FALSE-FREE 536 main 1 0.62 94124 +list-ext-properties/960521-1_1-3 valid-memsafety symex 1 FAILED FALSE-FREE 536 main 1 1.70 136568 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 1 FAILED FALSE-FREE 536 main 1 0.63 93384 +list-ext-properties/list-ext unreach-call afl 1 FAILED FALSE 556 main 1 0.53 91592 +list-ext-properties/list-ext unreach-call symex 1 UNKNOWN 0 0.45 134428 +list-ext-properties/list-ext unreach-call hybrid 1 FAILED FALSE 556 main 1 0.54 91812 +list-ext-properties/list-ext valid-memsafety afl 1 UNKNOWN 0 0.57 94064 +list-ext-properties/list-ext valid-memsafety symex 1 FAILED FALSE-DEREF 558 main 1 12.65 138596 +list-ext-properties/list-ext valid-memsafety hybrid 1 FAILED FALSE-DEREF 558 main 1 12.23 139344 +list-ext-properties/simple-ext unreach-call afl 1 FAILED FALSE 540 main 1 0.52 92000 +list-ext-properties/simple-ext unreach-call symex 1 UNKNOWN 0 0.39 133904 +list-ext-properties/simple-ext unreach-call hybrid 1 FAILED FALSE 540 main 1 0.55 92352 +list-ext-properties/simple-ext valid-memcleanup afl 1 UNKNOWN 0 9.50 341140 +list-ext-properties/simple-ext valid-memcleanup symex 1 UNKNOWN 0 0.96 134344 +list-ext-properties/simple-ext valid-memcleanup hybrid 1 UNKNOWN 0 35.07 341568 +list-ext-properties/test-0019_1-1 valid-memsafety afl 1 FAILED FALSE-MEMTRACK 1 0.62 93892 +list-ext-properties/test-0019_1-1 valid-memsafety symex 1 FAILED FALSE-MEMTRACK 1 0.60 128596 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 1 FAILED FALSE-MEMTRACK 1 0.62 93572 +list-ext-properties/test-0158_1-1 valid-memsafety afl 1 UNKNOWN 0 9.48 340884 +list-ext-properties/test-0158_1-1 valid-memsafety symex 1 SUCCEEDED 1 0.49 135164 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 1 SUCCEEDED 1 5.08 341152 +map2check-assert/array_bug unreach-call afl 1 FAILED FALSE 7 __VERIFIER_assert 1 0.53 92400 +map2check-assert/array_bug unreach-call symex 1 FAILED FALSE 7 __VERIFIER_assert 1 0.38 133980 +map2check-assert/array_bug unreach-call hybrid 1 FAILED FALSE 7 __VERIFIER_assert 1 0.51 92544 +map2check-assert/byte_add_bug unreach-call afl 1 UNKNOWN 0 9.43 341268 +map2check-assert/byte_add_bug unreach-call symex 1 SUCCEEDED 1 0.36 127368 +map2check-assert/byte_add_bug unreach-call hybrid 1 SUCCEEDED 1 4.99 126476 +map2check-assert/check_if_bug unreach-call afl 1 UNKNOWN 0 0.38 91796 +map2check-assert/check_if_bug unreach-call symex 1 UNKNOWN 0 31.76 184656 +map2check-assert/check_if_bug unreach-call hybrid 1 UNKNOWN 0 35.31 167772 +map2check-assert/ex01_false unreach-call afl 1 FAILED FALSE 5 __VERIFIER_assert 1 0.53 92140 +map2check-assert/ex01_false unreach-call symex 1 UNKNOWN 0 1.70 142892 +map2check-assert/ex01_false unreach-call hybrid 1 FAILED FALSE 5 __VERIFIER_assert 1 0.52 91608 +map2check-assert/ex02_false unreach-call afl 1 UNKNOWN 0 0.39 92004 +map2check-assert/ex02_false unreach-call symex 1 UNKNOWN 0 31.43 225480 +map2check-assert/ex02_false unreach-call hybrid 1 UNKNOWN 0 35.30 210964 +memory-alloca/c.03-alloca-2 valid-memsafety afl 1 UNKNOWN 0 9.48 93680 +memory-alloca/c.03-alloca-2 valid-memsafety symex 1 UNKNOWN 0 31.81 169884 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 1 UNKNOWN 0 41.32 179664 +memsafety-ext/dll_extends_pointer valid-memsafety afl 1 UNKNOWN 0 9.51 641684 +memsafety-ext/dll_extends_pointer valid-memsafety symex 1 UNKNOWN 0 31.45 157400 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 1 UNKNOWN 0 39.23 442004 +memsafety-ext/tree_stack valid-memsafety afl 1 UNKNOWN 0 9.51 340912 +memsafety-ext/tree_stack valid-memsafety symex 1 UNKNOWN 0 31.59 191672 +memsafety-ext/tree_stack valid-memsafety hybrid 1 UNKNOWN 0 40.34 357652 +memsafety-ext2/length_test03-1 valid-memsafety afl 1 UNKNOWN 0 9.49 735812 +memsafety-ext2/length_test03-1 valid-memsafety symex 1 UNKNOWN 0 31.38 142064 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 1 UNKNOWN 0 39.03 748008 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 1 FAILED FALSE-DEREF 540 containsHead 1 0.66 94560 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 1 UNKNOWN 0 31.97 275692 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 540 containsHead 1 0.66 94948 +memsafety/960521-1-1 valid-memsafety afl 1 FAILED FALSE-FREE 532 main 1 0.62 93372 +memsafety/960521-1-1 valid-memsafety symex 1 FAILED FALSE-FREE 532 main 1 1.01 128552 +memsafety/960521-1-1 valid-memsafety hybrid 1 FAILED FALSE-FREE 532 main 1 0.62 93604 +memsafety/960521-1-2 valid-memsafety afl 1 UNKNOWN 0 9.48 93928 +memsafety/960521-1-2 valid-memsafety symex 1 UNKNOWN 0 31.35 127724 +memsafety/960521-1-2 valid-memsafety hybrid 1 UNKNOWN 0 38.79 128520 +memsafety/960521-1-3 valid-memsafety afl 1 FAILED FALSE-DEREF 529 main 1 0.63 93512 +memsafety/960521-1-3 valid-memsafety symex 1 FAILED FALSE-DEREF 529 main 1 0.92 127884 +memsafety/960521-1-3 valid-memsafety hybrid 1 FAILED FALSE-DEREF 529 main 1 0.62 93868 +memsafety/test-0102-1 valid-memsafety afl 1 UNKNOWN 0 9.56 625568 +memsafety/test-0102-1 valid-memsafety symex 1 UNKNOWN 0 31.38 150672 +memsafety/test-0102-1 valid-memsafety hybrid 1 UNKNOWN 0 39.58 750020 +array-examples/relax-1 valid-memsafety afl 2 ERROR 0 0.79 93820 +array-examples/relax-1 valid-memsafety symex 2 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.53 152348 +array-examples/relax-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.91 144952 +array-examples/standard_sentinel-2 unreach-call afl 2 UNKNOWN 0 9.78 11129136 +array-examples/standard_sentinel-2 unreach-call symex 2 UNKNOWN 0 31.39 173616 +array-examples/standard_sentinel-2 unreach-call hybrid 2 UNKNOWN 0 39.23 10774288 +array-examples/standard_two_index_04 unreach-call afl 2 UNKNOWN 0 9.77 11031928 +array-examples/standard_two_index_04 unreach-call symex 2 UNKNOWN 0 31.36 174060 +array-examples/standard_two_index_04 unreach-call hybrid 2 UNKNOWN 0 39.06 10772084 +array-examples/standard_two_index_05 unreach-call afl 2 UNKNOWN 0 9.50 11155912 +array-examples/standard_two_index_05 unreach-call symex 2 UNKNOWN 0 31.36 173996 +array-examples/standard_two_index_05 unreach-call hybrid 2 UNKNOWN 0 39.07 10785036 +array-memsafety/add_last_unsafe valid-memsafety afl 2 FAILED FALSE-DEREF 522 main 1 0.61 93540 +array-memsafety/add_last_unsafe valid-memsafety symex 2 FAILED FALSE-DEREF 522 main 1 0.65 143860 +array-memsafety/add_last_unsafe valid-memsafety hybrid 2 FAILED FALSE-DEREF 522 main 1 0.61 94340 +array-memsafety/array01-alloca-2 valid-memsafety afl 2 UNKNOWN 0 2.37 94404 +array-memsafety/array01-alloca-2 valid-memsafety symex 2 UNKNOWN 0 31.45 165768 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 2 UNKNOWN 0 40.62 212840 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 2 UNKNOWN 0 1.36 94356 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 2 UNKNOWN 0 1.48 144504 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 2 UNKNOWN 0 39.94 220400 +array-memsafety/lis_unsafe valid-memsafety afl 2 FAILED FALSE-DEREF 521 lis 1 0.62 93864 +array-memsafety/lis_unsafe valid-memsafety symex 2 UNKNOWN 0 1.68 144816 +array-memsafety/lis_unsafe valid-memsafety hybrid 2 FAILED FALSE-DEREF 521 lis 1 0.63 93548 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 2 UNKNOWN 0 9.47 93680 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 2 SUCCEEDED 1 0.43 128580 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 2 SUCCEEDED 1 5.07 341048 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 2 FAILED FALSE-FREE 46 main 1 0.63 93416 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 2 FAILED FALSE-FREE 46 main 1 0.47 128276 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 2 FAILED FALSE-FREE 46 main 1 0.61 93528 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 2 FAILED FALSE-DEREF 42 main 1 0.62 93464 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 2 FAILED FALSE-DEREF 42 main 1 0.44 128420 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 2 FAILED FALSE-DEREF 42 main 1 0.62 93736 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 2 UNKNOWN 0 9.48 93624 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 2 UNKNOWN 0 31.36 127900 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 2 UNKNOWN 0 38.81 127908 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 2 UNKNOWN 0 1.51 93736 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 2 UNKNOWN 0 31.37 128332 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 2 UNKNOWN 0 39.80 128616 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 2 FAILED FALSE-MEMTRACK 1 0.72 95352 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 2 FAILED FALSE-MEMTRACK 1 4.39 138776 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 2 FAILED FALSE-MEMTRACK 1 0.74 96016 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 2 UNKNOWN 0 9.64 608092 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 2 UNKNOWN 0 13.91 140640 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 2 UNKNOWN 0 39.03 610248 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 2 FAILED FALSE-FREE 779 free_17 1 1.34 95788 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 2 FAILED FALSE-FREE 779 free_17 1 20.48 142904 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 2 FAILED FALSE-FREE 779 free_17 1 0.98 95780 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 2 UNKNOWN 0 9.62 506948 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 2 UNKNOWN 0 4.52 139148 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 2 UNKNOWN 0 40.13 507140 +ldv-memsafety/memsetNonZero valid-memsafety afl 2 UNKNOWN 0 9.47 93508 +ldv-memsafety/memsetNonZero valid-memsafety symex 2 SUCCEEDED 1 0.37 128472 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 2 SUCCEEDED 1 5.05 127912 +list-ext-properties/960521-1_1-1 valid-memsafety afl 2 FAILED FALSE-DEREF 533 main 1 0.63 94300 +list-ext-properties/960521-1_1-1 valid-memsafety symex 2 FAILED FALSE-DEREF 0 1 1.61 138656 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 533 main 1 0.63 93616 +list-ext-properties/960521-1_1-2 valid-memsafety afl 2 UNKNOWN 0 9.51 341144 +list-ext-properties/960521-1_1-2 valid-memsafety symex 2 SUCCEEDED 1 14.96 137476 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 2 UNKNOWN 0 38.99 341368 +list-ext-properties/960521-1_1-3 valid-memsafety afl 2 FAILED FALSE-FREE 536 main 1 0.63 94540 +list-ext-properties/960521-1_1-3 valid-memsafety symex 2 FAILED FALSE-FREE 536 main 1 1.63 136280 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 2 FAILED FALSE-FREE 536 main 1 0.62 94296 +list-ext-properties/list-ext unreach-call afl 2 FAILED FALSE 556 main 1 0.54 92740 +list-ext-properties/list-ext unreach-call symex 2 UNKNOWN 0 0.46 134256 +list-ext-properties/list-ext unreach-call hybrid 2 FAILED FALSE 556 main 1 0.56 91892 +list-ext-properties/list-ext valid-memsafety afl 2 UNKNOWN 0 0.57 93880 +list-ext-properties/list-ext valid-memsafety symex 2 FAILED FALSE-DEREF 558 main 1 12.86 138260 +list-ext-properties/list-ext valid-memsafety hybrid 2 FAILED FALSE-DEREF 558 main 1 12.26 139236 +list-ext-properties/simple-ext unreach-call afl 2 FAILED FALSE 540 main 1 0.54 92580 +list-ext-properties/simple-ext unreach-call symex 2 UNKNOWN 0 0.41 133900 +list-ext-properties/simple-ext unreach-call hybrid 2 FAILED FALSE 540 main 1 0.55 92652 +list-ext-properties/simple-ext valid-memcleanup afl 2 UNKNOWN 0 9.49 341032 +list-ext-properties/simple-ext valid-memcleanup symex 2 UNKNOWN 0 0.95 135068 +list-ext-properties/simple-ext valid-memcleanup hybrid 2 UNKNOWN 0 35.05 353768 +list-ext-properties/test-0019_1-1 valid-memsafety afl 2 FAILED FALSE-MEMTRACK 1 0.63 93684 +list-ext-properties/test-0019_1-1 valid-memsafety symex 2 FAILED FALSE-MEMTRACK 1 0.57 128028 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 2 FAILED FALSE-MEMTRACK 1 0.62 94108 +list-ext-properties/test-0158_1-1 valid-memsafety afl 2 UNKNOWN 0 9.49 341032 +list-ext-properties/test-0158_1-1 valid-memsafety symex 2 SUCCEEDED 1 0.52 135348 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 2 SUCCEEDED 1 5.09 341392 +map2check-assert/array_bug unreach-call afl 2 FAILED FALSE 7 __VERIFIER_assert 1 0.53 92108 +map2check-assert/array_bug unreach-call symex 2 FAILED FALSE 7 __VERIFIER_assert 1 0.38 133576 +map2check-assert/array_bug unreach-call hybrid 2 FAILED FALSE 7 __VERIFIER_assert 1 0.59 92192 +map2check-assert/byte_add_bug unreach-call afl 2 UNKNOWN 0 9.40 91836 +map2check-assert/byte_add_bug unreach-call symex 2 SUCCEEDED 1 0.36 127108 +map2check-assert/byte_add_bug unreach-call hybrid 2 SUCCEEDED 1 4.98 127624 +map2check-assert/check_if_bug unreach-call afl 2 UNKNOWN 0 0.40 91880 +map2check-assert/check_if_bug unreach-call symex 2 UNKNOWN 0 31.83 184220 +map2check-assert/check_if_bug unreach-call hybrid 2 UNKNOWN 0 35.34 167612 +map2check-assert/ex01_false unreach-call afl 2 FAILED FALSE 5 __VERIFIER_assert 1 0.53 92008 +map2check-assert/ex01_false unreach-call symex 2 UNKNOWN 0 1.62 142556 +map2check-assert/ex01_false unreach-call hybrid 2 FAILED FALSE 5 __VERIFIER_assert 1 0.52 91712 +map2check-assert/ex02_false unreach-call afl 2 UNKNOWN 0 0.39 92028 +map2check-assert/ex02_false unreach-call symex 2 UNKNOWN 0 31.42 224472 +map2check-assert/ex02_false unreach-call hybrid 2 UNKNOWN 0 35.31 210316 +memory-alloca/c.03-alloca-2 valid-memsafety afl 2 UNKNOWN 0 9.48 93688 +memory-alloca/c.03-alloca-2 valid-memsafety symex 2 UNKNOWN 0 31.84 170176 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 2 UNKNOWN 0 42.06 178104 +memsafety-ext/dll_extends_pointer valid-memsafety afl 2 UNKNOWN 0 9.50 575420 +memsafety-ext/dll_extends_pointer valid-memsafety symex 2 UNKNOWN 0 31.44 159280 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 2 UNKNOWN 0 39.14 344084 +memsafety-ext/tree_stack valid-memsafety afl 2 UNKNOWN 0 9.50 341128 +memsafety-ext/tree_stack valid-memsafety symex 2 UNKNOWN 0 31.60 192008 +memsafety-ext/tree_stack valid-memsafety hybrid 2 UNKNOWN 0 40.32 599568 +memsafety-ext2/length_test03-1 valid-memsafety afl 2 UNKNOWN 0 9.50 732700 +memsafety-ext2/length_test03-1 valid-memsafety symex 2 UNKNOWN 0 31.37 143652 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 2 UNKNOWN 0 39.10 819352 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 2 FAILED FALSE-DEREF 540 containsHead 1 0.64 94440 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 2 UNKNOWN 0 31.94 279360 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 540 containsHead 1 0.66 94436 +memsafety/960521-1-1 valid-memsafety afl 2 FAILED FALSE-FREE 532 main 1 0.61 94068 +memsafety/960521-1-1 valid-memsafety symex 2 FAILED FALSE-FREE 532 main 1 0.96 128248 +memsafety/960521-1-1 valid-memsafety hybrid 2 FAILED FALSE-FREE 532 main 1 0.61 93544 +memsafety/960521-1-2 valid-memsafety afl 2 UNKNOWN 0 9.48 93440 +memsafety/960521-1-2 valid-memsafety symex 2 UNKNOWN 0 31.35 128152 +memsafety/960521-1-2 valid-memsafety hybrid 2 UNKNOWN 0 38.80 127980 +memsafety/960521-1-3 valid-memsafety afl 2 FAILED FALSE-DEREF 529 main 1 0.61 93848 +memsafety/960521-1-3 valid-memsafety symex 2 FAILED FALSE-DEREF 529 main 1 0.95 128608 +memsafety/960521-1-3 valid-memsafety hybrid 2 FAILED FALSE-DEREF 529 main 1 0.62 93620 +memsafety/test-0102-1 valid-memsafety afl 2 UNKNOWN 0 9.52 690500 +memsafety/test-0102-1 valid-memsafety symex 2 UNKNOWN 0 31.40 150912 +memsafety/test-0102-1 valid-memsafety hybrid 2 UNKNOWN 0 39.57 739992 +array-examples/relax-1 valid-memsafety afl 3 ERROR 0 1.11 94184 +array-examples/relax-1 valid-memsafety symex 3 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.52 144556 +array-examples/relax-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.90 152708 +array-examples/standard_sentinel-2 unreach-call afl 3 UNKNOWN 0 9.72 11664316 +array-examples/standard_sentinel-2 unreach-call symex 3 UNKNOWN 0 31.37 173388 +array-examples/standard_sentinel-2 unreach-call hybrid 3 UNKNOWN 0 38.90 11044624 +array-examples/standard_two_index_04 unreach-call afl 3 UNKNOWN 0 9.73 11397740 +array-examples/standard_two_index_04 unreach-call symex 3 UNKNOWN 0 31.36 173804 +array-examples/standard_two_index_04 unreach-call hybrid 3 UNKNOWN 0 38.66 4905204 +array-examples/standard_two_index_05 unreach-call afl 3 UNKNOWN 0 9.71 11362220 +array-examples/standard_two_index_05 unreach-call symex 3 UNKNOWN 0 31.36 173840 +array-examples/standard_two_index_05 unreach-call hybrid 3 UNKNOWN 0 39.43 5024476 +array-memsafety/add_last_unsafe valid-memsafety afl 3 FAILED FALSE-DEREF 522 main 1 0.60 93456 +array-memsafety/add_last_unsafe valid-memsafety symex 3 FAILED FALSE-DEREF 522 main 1 0.60 143884 +array-memsafety/add_last_unsafe valid-memsafety hybrid 3 FAILED FALSE-DEREF 522 main 1 0.60 93728 +array-memsafety/array01-alloca-2 valid-memsafety afl 3 UNKNOWN 0 0.78 93656 +array-memsafety/array01-alloca-2 valid-memsafety symex 3 UNKNOWN 0 31.45 170128 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 3 UNKNOWN 0 41.59 186744 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 3 UNKNOWN 0 1.09 93704 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 3 UNKNOWN 0 1.50 145132 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 3 UNKNOWN 0 37.98 341224 +array-memsafety/lis_unsafe valid-memsafety afl 3 FAILED FALSE-DEREF 521 lis 1 0.62 94496 +array-memsafety/lis_unsafe valid-memsafety symex 3 UNKNOWN 0 1.67 144588 +array-memsafety/lis_unsafe valid-memsafety hybrid 3 FAILED FALSE-DEREF 521 lis 1 0.64 94508 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 3 UNKNOWN 0 9.49 341308 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 3 SUCCEEDED 1 0.44 127480 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 3 SUCCEEDED 1 5.08 341116 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 3 FAILED FALSE-FREE 46 main 1 0.61 93880 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 3 FAILED FALSE-FREE 46 main 1 0.47 128416 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 3 FAILED FALSE-FREE 46 main 1 0.61 94172 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 3 FAILED FALSE-DEREF 42 main 1 0.61 93712 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 3 FAILED FALSE-DEREF 42 main 1 0.41 127960 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 3 FAILED FALSE-DEREF 42 main 1 0.61 94236 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 3 UNKNOWN 0 9.48 93864 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 3 UNKNOWN 0 31.36 128572 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 3 UNKNOWN 0 38.77 128108 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 3 UNKNOWN 0 1.50 94500 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 3 UNKNOWN 0 31.36 128636 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 3 UNKNOWN 0 39.79 128504 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 3 FAILED FALSE-MEMTRACK 1 0.75 95556 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 3 FAILED FALSE-MEMTRACK 1 4.46 138660 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 3 FAILED FALSE-MEMTRACK 1 0.72 95448 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 3 UNKNOWN 0 9.63 613496 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 3 UNKNOWN 0 13.52 141196 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 3 UNKNOWN 0 39.01 608772 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 3 FAILED FALSE-FREE 779 free_17 1 0.96 96444 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 3 FAILED FALSE-FREE 779 free_17 1 19.34 143116 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 3 FAILED FALSE-FREE 779 free_17 1 1.03 96020 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 3 UNKNOWN 0 9.63 506964 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 3 UNKNOWN 0 4.48 139176 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 3 UNKNOWN 0 40.61 507220 +ldv-memsafety/memsetNonZero valid-memsafety afl 3 UNKNOWN 0 9.47 93636 +ldv-memsafety/memsetNonZero valid-memsafety symex 3 SUCCEEDED 1 0.38 127192 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 3 SUCCEEDED 1 5.07 341300 +list-ext-properties/960521-1_1-1 valid-memsafety afl 3 FAILED FALSE-DEREF 533 main 1 0.62 93600 +list-ext-properties/960521-1_1-1 valid-memsafety symex 3 FAILED FALSE-DEREF 0 1 1.51 135996 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 533 main 1 0.62 93768 +list-ext-properties/960521-1_1-2 valid-memsafety afl 3 UNKNOWN 0 9.50 340912 +list-ext-properties/960521-1_1-2 valid-memsafety symex 3 SUCCEEDED 1 14.16 137180 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 3 UNKNOWN 0 38.97 341144 +list-ext-properties/960521-1_1-3 valid-memsafety afl 3 FAILED FALSE-FREE 536 main 1 0.62 93728 +list-ext-properties/960521-1_1-3 valid-memsafety symex 3 FAILED FALSE-FREE 536 main 1 1.68 136696 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 3 FAILED FALSE-FREE 536 main 1 0.62 93788 +list-ext-properties/list-ext unreach-call afl 3 FAILED FALSE 556 main 1 0.52 92564 +list-ext-properties/list-ext unreach-call symex 3 UNKNOWN 0 0.42 134588 +list-ext-properties/list-ext unreach-call hybrid 3 FAILED FALSE 556 main 1 0.54 92388 +list-ext-properties/list-ext valid-memsafety afl 3 UNKNOWN 0 0.56 93636 +list-ext-properties/list-ext valid-memsafety symex 3 FAILED FALSE-DEREF 558 main 1 12.05 138784 +list-ext-properties/list-ext valid-memsafety hybrid 3 FAILED FALSE-DEREF 558 main 1 12.22 138544 +list-ext-properties/simple-ext unreach-call afl 3 FAILED FALSE 540 main 1 0.52 91964 +list-ext-properties/simple-ext unreach-call symex 3 UNKNOWN 0 0.39 133980 +list-ext-properties/simple-ext unreach-call hybrid 3 FAILED FALSE 540 main 1 0.55 92092 +list-ext-properties/simple-ext valid-memcleanup afl 3 UNKNOWN 0 9.49 341196 +list-ext-properties/simple-ext valid-memcleanup symex 3 UNKNOWN 0 0.97 135280 +list-ext-properties/simple-ext valid-memcleanup hybrid 3 UNKNOWN 0 35.05 364988 +list-ext-properties/test-0019_1-1 valid-memsafety afl 3 FAILED FALSE-MEMTRACK 1 0.63 94344 +list-ext-properties/test-0019_1-1 valid-memsafety symex 3 FAILED FALSE-MEMTRACK 1 0.59 128592 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 3 FAILED FALSE-MEMTRACK 1 0.61 93876 +list-ext-properties/test-0158_1-1 valid-memsafety afl 3 UNKNOWN 0 9.50 341196 +list-ext-properties/test-0158_1-1 valid-memsafety symex 3 SUCCEEDED 1 0.51 135000 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 3 SUCCEEDED 1 5.08 341164 +map2check-assert/array_bug unreach-call afl 3 FAILED FALSE 7 __VERIFIER_assert 1 0.53 92648 +map2check-assert/array_bug unreach-call symex 3 FAILED FALSE 7 __VERIFIER_assert 1 0.37 134060 +map2check-assert/array_bug unreach-call hybrid 3 FAILED FALSE 7 __VERIFIER_assert 1 0.53 91460 +map2check-assert/byte_add_bug unreach-call afl 3 UNKNOWN 0 9.42 92844 +map2check-assert/byte_add_bug unreach-call symex 3 SUCCEEDED 1 0.35 126524 +map2check-assert/byte_add_bug unreach-call hybrid 3 SUCCEEDED 1 4.98 127372 +map2check-assert/check_if_bug unreach-call afl 3 UNKNOWN 0 0.37 91796 +map2check-assert/check_if_bug unreach-call symex 3 UNKNOWN 0 31.78 183952 +map2check-assert/check_if_bug unreach-call hybrid 3 UNKNOWN 0 35.33 167576 +map2check-assert/ex01_false unreach-call afl 3 FAILED FALSE 5 __VERIFIER_assert 1 0.52 91960 +map2check-assert/ex01_false unreach-call symex 3 UNKNOWN 0 1.69 142092 +map2check-assert/ex01_false unreach-call hybrid 3 FAILED FALSE 5 __VERIFIER_assert 1 0.54 91800 +map2check-assert/ex02_false unreach-call afl 3 UNKNOWN 0 0.38 92352 +map2check-assert/ex02_false unreach-call symex 3 UNKNOWN 0 31.44 224824 +map2check-assert/ex02_false unreach-call hybrid 3 UNKNOWN 0 35.28 211568 +memory-alloca/c.03-alloca-2 valid-memsafety afl 3 UNKNOWN 0 9.48 93712 +memory-alloca/c.03-alloca-2 valid-memsafety symex 3 UNKNOWN 0 31.93 168624 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 3 UNKNOWN 0 40.81 178748 +memsafety-ext/dll_extends_pointer valid-memsafety afl 3 UNKNOWN 0 9.52 557584 +memsafety-ext/dll_extends_pointer valid-memsafety symex 3 UNKNOWN 0 31.44 158424 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 3 UNKNOWN 0 39.15 711040 +memsafety-ext/tree_stack valid-memsafety afl 3 UNKNOWN 0 9.54 817816 +memsafety-ext/tree_stack valid-memsafety symex 3 UNKNOWN 0 31.61 191616 +memsafety-ext/tree_stack valid-memsafety hybrid 3 UNKNOWN 0 40.40 349112 +memsafety-ext2/length_test03-1 valid-memsafety afl 3 UNKNOWN 0 9.52 728984 +memsafety-ext2/length_test03-1 valid-memsafety symex 3 UNKNOWN 0 31.36 141772 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 3 UNKNOWN 0 39.08 777152 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 3 FAILED FALSE-DEREF 540 containsHead 1 0.67 94484 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 3 UNKNOWN 0 31.94 279908 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 540 containsHead 1 0.65 94776 +memsafety/960521-1-1 valid-memsafety afl 3 FAILED FALSE-FREE 532 main 1 0.61 93776 +memsafety/960521-1-1 valid-memsafety symex 3 FAILED FALSE-FREE 532 main 1 1.00 128488 +memsafety/960521-1-1 valid-memsafety hybrid 3 FAILED FALSE-FREE 532 main 1 0.62 93672 +memsafety/960521-1-2 valid-memsafety afl 3 UNKNOWN 0 9.48 93404 +memsafety/960521-1-2 valid-memsafety symex 3 UNKNOWN 0 31.34 127464 +memsafety/960521-1-2 valid-memsafety hybrid 3 UNKNOWN 0 38.82 130096 +memsafety/960521-1-3 valid-memsafety afl 3 FAILED FALSE-DEREF 529 main 1 0.61 93724 +memsafety/960521-1-3 valid-memsafety symex 3 FAILED FALSE-DEREF 529 main 1 0.91 128312 +memsafety/960521-1-3 valid-memsafety hybrid 3 FAILED FALSE-DEREF 529 main 1 0.62 93780 +memsafety/test-0102-1 valid-memsafety afl 3 UNKNOWN 0 9.54 700784 +memsafety/test-0102-1 valid-memsafety symex 3 UNKNOWN 0 31.39 149848 +memsafety/test-0102-1 valid-memsafety hybrid 3 UNKNOWN 0 39.53 738380 +array-examples/relax-1 valid-memsafety afl 4 ERROR 0 0.81 94204 +array-examples/relax-1 valid-memsafety symex 4 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.53 147088 +array-examples/relax-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.91 145440 +array-examples/standard_sentinel-2 unreach-call afl 4 UNKNOWN 0 9.72 11810320 +array-examples/standard_sentinel-2 unreach-call symex 4 UNKNOWN 0 31.37 173316 +array-examples/standard_sentinel-2 unreach-call hybrid 4 UNKNOWN 0 38.85 11213508 +array-examples/standard_two_index_04 unreach-call afl 4 UNKNOWN 0 9.72 11317588 +array-examples/standard_two_index_04 unreach-call symex 4 UNKNOWN 0 31.36 173940 +array-examples/standard_two_index_04 unreach-call hybrid 4 UNKNOWN 0 38.95 10527308 +array-examples/standard_two_index_05 unreach-call afl 4 UNKNOWN 0 9.71 11158120 +array-examples/standard_two_index_05 unreach-call symex 4 UNKNOWN 0 31.36 173476 +array-examples/standard_two_index_05 unreach-call hybrid 4 UNKNOWN 0 38.76 11079772 +array-memsafety/add_last_unsafe valid-memsafety afl 4 FAILED FALSE-DEREF 522 main 1 0.62 93856 +array-memsafety/add_last_unsafe valid-memsafety symex 4 FAILED FALSE-DEREF 522 main 1 0.61 144152 +array-memsafety/add_last_unsafe valid-memsafety hybrid 4 FAILED FALSE-DEREF 522 main 1 0.62 93440 +array-memsafety/array01-alloca-2 valid-memsafety afl 4 UNKNOWN 0 1.92 93704 +array-memsafety/array01-alloca-2 valid-memsafety symex 4 UNKNOWN 0 31.46 170288 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 4 UNKNOWN 0 40.94 269000 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 4 UNKNOWN 0 0.82 93784 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 4 UNKNOWN 0 1.44 144720 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 4 UNKNOWN 0 36.09 341592 +array-memsafety/lis_unsafe valid-memsafety afl 4 FAILED FALSE-DEREF 521 lis 1 0.63 93728 +array-memsafety/lis_unsafe valid-memsafety symex 4 UNKNOWN 0 1.68 145140 +array-memsafety/lis_unsafe valid-memsafety hybrid 4 FAILED FALSE-DEREF 521 lis 1 0.62 93852 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 4 UNKNOWN 0 9.47 93620 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 4 SUCCEEDED 1 0.43 128732 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 4 SUCCEEDED 1 5.06 128720 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 4 FAILED FALSE-FREE 46 main 1 0.61 94192 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 4 FAILED FALSE-FREE 46 main 1 0.44 128724 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 4 FAILED FALSE-FREE 46 main 1 0.60 94240 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 4 FAILED FALSE-DEREF 42 main 1 0.63 94284 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 4 FAILED FALSE-DEREF 42 main 1 0.43 128320 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 4 FAILED FALSE-DEREF 42 main 1 0.63 93884 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 4 UNKNOWN 0 9.47 93848 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 4 UNKNOWN 0 31.36 128440 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 4 UNKNOWN 0 38.79 128028 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 4 UNKNOWN 0 1.49 93764 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 4 UNKNOWN 0 31.36 128872 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 4 UNKNOWN 0 39.82 128568 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 4 FAILED FALSE-MEMTRACK 1 0.74 95112 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 4 FAILED FALSE-MEMTRACK 1 4.45 139548 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 4 FAILED FALSE-MEMTRACK 1 0.72 95292 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 4 UNKNOWN 0 9.62 613448 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 4 UNKNOWN 0 13.92 140528 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 4 UNKNOWN 0 39.02 608644 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 4 FAILED FALSE-FREE 779 free_17 1 0.91 95632 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 4 FAILED FALSE-FREE 779 free_17 1 19.84 142796 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 4 FAILED FALSE-FREE 779 free_17 1 0.99 95740 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 4 UNKNOWN 0 9.61 507124 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 4 UNKNOWN 0 4.52 138916 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 4 UNKNOWN 0 40.60 507140 +ldv-memsafety/memsetNonZero valid-memsafety afl 4 UNKNOWN 0 9.46 93528 +ldv-memsafety/memsetNonZero valid-memsafety symex 4 SUCCEEDED 1 0.38 128788 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 4 SUCCEEDED 1 5.05 126964 +list-ext-properties/960521-1_1-1 valid-memsafety afl 4 FAILED FALSE-DEREF 533 main 1 0.63 93672 +list-ext-properties/960521-1_1-1 valid-memsafety symex 4 FAILED FALSE-DEREF 0 1 1.58 136224 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 533 main 1 0.63 94264 +list-ext-properties/960521-1_1-2 valid-memsafety afl 4 UNKNOWN 0 9.50 341432 +list-ext-properties/960521-1_1-2 valid-memsafety symex 4 SUCCEEDED 1 14.50 136568 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 4 UNKNOWN 0 39.00 341572 +list-ext-properties/960521-1_1-3 valid-memsafety afl 4 FAILED FALSE-FREE 536 main 1 0.62 93536 +list-ext-properties/960521-1_1-3 valid-memsafety symex 4 FAILED FALSE-FREE 536 main 1 1.62 136204 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 4 FAILED FALSE-FREE 536 main 1 0.61 93760 +list-ext-properties/list-ext unreach-call afl 4 FAILED FALSE 556 main 1 0.52 92492 +list-ext-properties/list-ext unreach-call symex 4 UNKNOWN 0 0.45 134356 +list-ext-properties/list-ext unreach-call hybrid 4 FAILED FALSE 556 main 1 0.53 92580 +list-ext-properties/list-ext valid-memsafety afl 4 UNKNOWN 0 0.56 94584 +list-ext-properties/list-ext valid-memsafety symex 4 FAILED FALSE-DEREF 558 main 1 13.16 138556 +list-ext-properties/list-ext valid-memsafety hybrid 4 FAILED FALSE-DEREF 558 main 1 12.23 139260 +list-ext-properties/simple-ext unreach-call afl 4 FAILED FALSE 540 main 1 0.54 92188 +list-ext-properties/simple-ext unreach-call symex 4 UNKNOWN 0 0.40 133888 +list-ext-properties/simple-ext unreach-call hybrid 4 FAILED FALSE 540 main 1 0.53 92028 +list-ext-properties/simple-ext valid-memcleanup afl 4 UNKNOWN 0 9.48 374244 +list-ext-properties/simple-ext valid-memcleanup symex 4 UNKNOWN 0 0.93 135320 +list-ext-properties/simple-ext valid-memcleanup hybrid 4 UNKNOWN 0 35.07 354196 +list-ext-properties/test-0019_1-1 valid-memsafety afl 4 FAILED FALSE-MEMTRACK 1 0.62 93700 +list-ext-properties/test-0019_1-1 valid-memsafety symex 4 FAILED FALSE-MEMTRACK 1 0.60 127964 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 4 FAILED FALSE-MEMTRACK 1 0.61 94348 +list-ext-properties/test-0158_1-1 valid-memsafety afl 4 UNKNOWN 0 9.49 341316 +list-ext-properties/test-0158_1-1 valid-memsafety symex 4 SUCCEEDED 1 0.49 134872 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 4 SUCCEEDED 1 5.09 341272 +map2check-assert/array_bug unreach-call afl 4 FAILED FALSE 7 __VERIFIER_assert 1 0.52 91792 +map2check-assert/array_bug unreach-call symex 4 FAILED FALSE 7 __VERIFIER_assert 1 0.42 133036 +map2check-assert/array_bug unreach-call hybrid 4 FAILED FALSE 7 __VERIFIER_assert 1 0.53 91832 +map2check-assert/byte_add_bug unreach-call afl 4 UNKNOWN 0 9.42 341076 +map2check-assert/byte_add_bug unreach-call symex 4 SUCCEEDED 1 0.36 127144 +map2check-assert/byte_add_bug unreach-call hybrid 4 SUCCEEDED 1 4.99 126980 +map2check-assert/check_if_bug unreach-call afl 4 UNKNOWN 0 0.38 91944 +map2check-assert/check_if_bug unreach-call symex 4 UNKNOWN 0 31.84 185832 +map2check-assert/check_if_bug unreach-call hybrid 4 UNKNOWN 0 35.32 168124 +map2check-assert/ex01_false unreach-call afl 4 FAILED FALSE 5 __VERIFIER_assert 1 0.52 91988 +map2check-assert/ex01_false unreach-call symex 4 UNKNOWN 0 1.58 142864 +map2check-assert/ex01_false unreach-call hybrid 4 FAILED FALSE 5 __VERIFIER_assert 1 0.52 91884 +map2check-assert/ex02_false unreach-call afl 4 UNKNOWN 0 0.38 91924 +map2check-assert/ex02_false unreach-call symex 4 UNKNOWN 0 31.42 226068 +map2check-assert/ex02_false unreach-call hybrid 4 UNKNOWN 0 35.29 209184 +memory-alloca/c.03-alloca-2 valid-memsafety afl 4 UNKNOWN 0 9.47 94456 +memory-alloca/c.03-alloca-2 valid-memsafety symex 4 UNKNOWN 0 31.77 170524 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 4 UNKNOWN 0 40.50 178812 +memsafety-ext/dll_extends_pointer valid-memsafety afl 4 UNKNOWN 0 9.51 497852 +memsafety-ext/dll_extends_pointer valid-memsafety symex 4 UNKNOWN 0 31.42 159284 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 4 UNKNOWN 0 39.16 341468 +memsafety-ext/tree_stack valid-memsafety afl 4 UNKNOWN 0 9.52 788844 +memsafety-ext/tree_stack valid-memsafety symex 4 UNKNOWN 0 31.59 193284 +memsafety-ext/tree_stack valid-memsafety hybrid 4 UNKNOWN 0 40.50 479248 +memsafety-ext2/length_test03-1 valid-memsafety afl 4 UNKNOWN 0 9.50 626752 +memsafety-ext2/length_test03-1 valid-memsafety symex 4 UNKNOWN 0 31.36 141568 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 4 UNKNOWN 0 39.08 814912 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 4 FAILED FALSE-DEREF 540 containsHead 1 0.65 94256 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 4 UNKNOWN 0 31.96 279132 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 540 containsHead 1 0.66 94380 +memsafety/960521-1-1 valid-memsafety afl 4 FAILED FALSE-FREE 532 main 1 0.61 93604 +memsafety/960521-1-1 valid-memsafety symex 4 FAILED FALSE-FREE 532 main 1 1.00 128816 +memsafety/960521-1-1 valid-memsafety hybrid 4 FAILED FALSE-FREE 532 main 1 0.61 93604 +memsafety/960521-1-2 valid-memsafety afl 4 UNKNOWN 0 9.49 93296 +memsafety/960521-1-2 valid-memsafety symex 4 UNKNOWN 0 31.36 128568 +memsafety/960521-1-2 valid-memsafety hybrid 4 UNKNOWN 0 38.81 128256 +memsafety/960521-1-3 valid-memsafety afl 4 FAILED FALSE-DEREF 529 main 1 0.62 93572 +memsafety/960521-1-3 valid-memsafety symex 4 FAILED FALSE-DEREF 529 main 1 0.96 127652 +memsafety/960521-1-3 valid-memsafety hybrid 4 FAILED FALSE-DEREF 529 main 1 0.62 93696 +memsafety/test-0102-1 valid-memsafety afl 4 UNKNOWN 0 9.54 611524 +memsafety/test-0102-1 valid-memsafety symex 4 UNKNOWN 0 31.40 150148 +memsafety/test-0102-1 valid-memsafety hybrid 4 UNKNOWN 0 39.42 905332 +array-examples/relax-1 valid-memsafety afl 5 ERROR 0 0.87 94036 +array-examples/relax-1 valid-memsafety symex 5 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.53 145548 +array-examples/relax-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 2.41 145256 +array-examples/standard_sentinel-2 unreach-call afl 5 UNKNOWN 0 9.76 11649680 +array-examples/standard_sentinel-2 unreach-call symex 5 UNKNOWN 0 31.37 173332 +array-examples/standard_sentinel-2 unreach-call hybrid 5 UNKNOWN 0 39.07 10822520 +array-examples/standard_two_index_04 unreach-call afl 5 UNKNOWN 0 9.74 11376872 +array-examples/standard_two_index_04 unreach-call symex 5 UNKNOWN 0 31.35 173716 +array-examples/standard_two_index_04 unreach-call hybrid 5 UNKNOWN 0 39.01 10762436 +array-examples/standard_two_index_05 unreach-call afl 5 UNKNOWN 0 9.75 11539816 +array-examples/standard_two_index_05 unreach-call symex 5 UNKNOWN 0 31.36 173536 +array-examples/standard_two_index_05 unreach-call hybrid 5 UNKNOWN 0 39.02 11191048 +array-memsafety/add_last_unsafe valid-memsafety afl 5 FAILED FALSE-DEREF 522 main 1 0.61 94360 +array-memsafety/add_last_unsafe valid-memsafety symex 5 FAILED FALSE-DEREF 522 main 1 0.62 143944 +array-memsafety/add_last_unsafe valid-memsafety hybrid 5 FAILED FALSE-DEREF 522 main 1 0.62 93580 +array-memsafety/array01-alloca-2 valid-memsafety afl 5 UNKNOWN 0 2.05 93524 +array-memsafety/array01-alloca-2 valid-memsafety symex 5 UNKNOWN 0 31.43 170052 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 5 UNKNOWN 0 41.07 257148 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 5 UNKNOWN 0 0.79 93784 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 5 UNKNOWN 0 1.52 144532 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 5 UNKNOWN 0 40.06 220264 +array-memsafety/lis_unsafe valid-memsafety afl 5 FAILED FALSE-DEREF 521 lis 1 0.63 94536 +array-memsafety/lis_unsafe valid-memsafety symex 5 UNKNOWN 0 1.67 144928 +array-memsafety/lis_unsafe valid-memsafety hybrid 5 FAILED FALSE-DEREF 521 lis 1 0.64 93956 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 5 UNKNOWN 0 9.45 93556 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 5 SUCCEEDED 1 0.43 128172 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 5 SUCCEEDED 1 5.08 341232 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 5 FAILED FALSE-FREE 46 main 1 0.61 93656 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 5 FAILED FALSE-FREE 46 main 1 0.46 128332 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 5 FAILED FALSE-FREE 46 main 1 0.60 93344 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 5 FAILED FALSE-DEREF 42 main 1 0.63 93376 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 5 FAILED FALSE-DEREF 42 main 1 0.42 128532 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 5 FAILED FALSE-DEREF 42 main 1 0.60 93432 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 5 UNKNOWN 0 9.48 93804 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 5 UNKNOWN 0 31.34 128092 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 5 UNKNOWN 0 38.78 128016 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 5 UNKNOWN 0 1.49 94104 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 5 UNKNOWN 0 31.35 128160 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 5 UNKNOWN 0 39.80 128800 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 5 FAILED FALSE-MEMTRACK 1 0.73 95440 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 5 FAILED FALSE-MEMTRACK 1 4.47 138992 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 5 FAILED FALSE-MEMTRACK 1 0.72 96148 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 5 UNKNOWN 0 9.62 613592 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 5 UNKNOWN 0 13.79 140380 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 5 UNKNOWN 0 39.03 612056 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 5 FAILED FALSE-FREE 779 free_17 1 1.03 96484 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 5 FAILED FALSE-FREE 779 free_17 1 20.05 143104 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 5 FAILED FALSE-FREE 779 free_17 1 1.10 96476 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 5 UNKNOWN 0 9.60 507260 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 5 UNKNOWN 0 4.50 139472 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 5 UNKNOWN 0 40.59 507092 +ldv-memsafety/memsetNonZero valid-memsafety afl 5 UNKNOWN 0 9.47 341556 +ldv-memsafety/memsetNonZero valid-memsafety symex 5 SUCCEEDED 1 0.39 127816 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 5 SUCCEEDED 1 5.07 341280 +list-ext-properties/960521-1_1-1 valid-memsafety afl 5 FAILED FALSE-DEREF 533 main 1 0.61 93836 +list-ext-properties/960521-1_1-1 valid-memsafety symex 5 FAILED FALSE-DEREF 0 1 1.55 136844 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 533 main 1 0.61 94284 +list-ext-properties/960521-1_1-2 valid-memsafety afl 5 UNKNOWN 0 9.50 341280 +list-ext-properties/960521-1_1-2 valid-memsafety symex 5 SUCCEEDED 1 14.48 137116 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 5 UNKNOWN 0 38.98 341472 +list-ext-properties/960521-1_1-3 valid-memsafety afl 5 FAILED FALSE-FREE 536 main 1 0.61 93696 +list-ext-properties/960521-1_1-3 valid-memsafety symex 5 FAILED FALSE-FREE 536 main 1 1.61 136676 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 5 FAILED FALSE-FREE 536 main 1 0.62 93356 +list-ext-properties/list-ext unreach-call afl 5 FAILED FALSE 556 main 1 0.55 92252 +list-ext-properties/list-ext unreach-call symex 5 UNKNOWN 0 0.42 134208 +list-ext-properties/list-ext unreach-call hybrid 5 FAILED FALSE 556 main 1 0.55 92456 +list-ext-properties/list-ext valid-memsafety afl 5 UNKNOWN 0 0.57 93284 +list-ext-properties/list-ext valid-memsafety symex 5 FAILED FALSE-DEREF 558 main 1 12.37 138516 +list-ext-properties/list-ext valid-memsafety hybrid 5 FAILED FALSE-DEREF 558 main 1 12.22 139600 +list-ext-properties/simple-ext unreach-call afl 5 FAILED FALSE 540 main 1 0.54 92100 +list-ext-properties/simple-ext unreach-call symex 5 UNKNOWN 0 0.40 133752 +list-ext-properties/simple-ext unreach-call hybrid 5 FAILED FALSE 540 main 1 0.54 91964 +list-ext-properties/simple-ext valid-memcleanup afl 5 UNKNOWN 0 9.49 341212 +list-ext-properties/simple-ext valid-memcleanup symex 5 UNKNOWN 0 0.92 134600 +list-ext-properties/simple-ext valid-memcleanup hybrid 5 UNKNOWN 0 35.06 341392 +list-ext-properties/test-0019_1-1 valid-memsafety afl 5 FAILED FALSE-MEMTRACK 1 0.62 93708 +list-ext-properties/test-0019_1-1 valid-memsafety symex 5 FAILED FALSE-MEMTRACK 1 0.57 128404 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 5 FAILED FALSE-MEMTRACK 1 0.64 94304 +list-ext-properties/test-0158_1-1 valid-memsafety afl 5 UNKNOWN 0 9.49 341176 +list-ext-properties/test-0158_1-1 valid-memsafety symex 5 SUCCEEDED 1 0.51 135036 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 5 SUCCEEDED 1 5.08 341236 +map2check-assert/array_bug unreach-call afl 5 FAILED FALSE 7 __VERIFIER_assert 1 0.53 91564 +map2check-assert/array_bug unreach-call symex 5 FAILED FALSE 7 __VERIFIER_assert 1 0.38 133984 +map2check-assert/array_bug unreach-call hybrid 5 FAILED FALSE 7 __VERIFIER_assert 1 0.52 92100 +map2check-assert/byte_add_bug unreach-call afl 5 UNKNOWN 0 9.40 92620 +map2check-assert/byte_add_bug unreach-call symex 5 SUCCEEDED 1 0.35 127432 +map2check-assert/byte_add_bug unreach-call hybrid 5 SUCCEEDED 1 4.99 126800 +map2check-assert/check_if_bug unreach-call afl 5 UNKNOWN 0 0.37 91936 +map2check-assert/check_if_bug unreach-call symex 5 UNKNOWN 0 31.77 184304 +map2check-assert/check_if_bug unreach-call hybrid 5 UNKNOWN 0 35.31 176356 +map2check-assert/ex01_false unreach-call afl 5 FAILED FALSE 5 __VERIFIER_assert 1 0.53 91804 +map2check-assert/ex01_false unreach-call symex 5 UNKNOWN 0 1.61 142016 +map2check-assert/ex01_false unreach-call hybrid 5 FAILED FALSE 5 __VERIFIER_assert 1 0.53 91692 +map2check-assert/ex02_false unreach-call afl 5 UNKNOWN 0 0.38 92008 +map2check-assert/ex02_false unreach-call symex 5 UNKNOWN 0 31.42 226176 +map2check-assert/ex02_false unreach-call hybrid 5 UNKNOWN 0 35.30 211504 +memory-alloca/c.03-alloca-2 valid-memsafety afl 5 UNKNOWN 0 9.48 93924 +memory-alloca/c.03-alloca-2 valid-memsafety symex 5 UNKNOWN 0 31.92 169800 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 5 UNKNOWN 0 42.32 186884 +memsafety-ext/dll_extends_pointer valid-memsafety afl 5 UNKNOWN 0 9.51 542140 +memsafety-ext/dll_extends_pointer valid-memsafety symex 5 UNKNOWN 0 31.45 157800 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 5 UNKNOWN 0 39.15 422460 +memsafety-ext/tree_stack valid-memsafety afl 5 UNKNOWN 0 9.50 466108 +memsafety-ext/tree_stack valid-memsafety symex 5 UNKNOWN 0 31.59 191456 +memsafety-ext/tree_stack valid-memsafety hybrid 5 UNKNOWN 0 40.34 1106624 +memsafety-ext2/length_test03-1 valid-memsafety afl 5 UNKNOWN 0 9.51 729048 +memsafety-ext2/length_test03-1 valid-memsafety symex 5 UNKNOWN 0 31.36 141928 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 5 UNKNOWN 0 39.09 880792 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 5 FAILED FALSE-DEREF 540 containsHead 1 0.65 94156 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 5 UNKNOWN 0 31.98 277716 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 540 containsHead 1 0.65 95136 +memsafety/960521-1-1 valid-memsafety afl 5 FAILED FALSE-FREE 532 main 1 0.62 94516 +memsafety/960521-1-1 valid-memsafety symex 5 FAILED FALSE-FREE 532 main 1 1.03 128456 +memsafety/960521-1-1 valid-memsafety hybrid 5 FAILED FALSE-FREE 532 main 1 0.62 93876 +memsafety/960521-1-2 valid-memsafety afl 5 UNKNOWN 0 9.47 93556 +memsafety/960521-1-2 valid-memsafety symex 5 UNKNOWN 0 31.35 127972 +memsafety/960521-1-2 valid-memsafety hybrid 5 UNKNOWN 0 38.80 128752 +memsafety/960521-1-3 valid-memsafety afl 5 FAILED FALSE-DEREF 529 main 1 0.62 93736 +memsafety/960521-1-3 valid-memsafety symex 5 FAILED FALSE-DEREF 529 main 1 0.97 128268 +memsafety/960521-1-3 valid-memsafety hybrid 5 FAILED FALSE-DEREF 529 main 1 0.62 93576 +memsafety/test-0102-1 valid-memsafety afl 5 UNKNOWN 0 9.51 94272 +memsafety/test-0102-1 valid-memsafety symex 5 UNKNOWN 0 31.40 152768 +memsafety/test-0102-1 valid-memsafety hybrid 5 UNKNOWN 0 39.56 896080 diff --git a/docs/cutover/baselines/rust-bcbdab3eb694.tsv b/docs/cutover/baselines/rust-bcbdab3eb694.tsv new file mode 100644 index 0000000..5f4dfee --- /dev/null +++ b/docs/cutover/baselines/rust-bcbdab3eb694.tsv @@ -0,0 +1,616 @@ +task prp gen rep verdict tag line function witness wall_s maxrss_kb +array-examples/relax-1 valid-memsafety afl 1 ERROR 0 2.81 107632 +array-examples/relax-1 valid-memsafety symex 1 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 0.79 155360 +array-examples/relax-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 4.33 155096 +array-examples/standard_sentinel-2 unreach-call afl 1 UNKNOWN 0 11.14 105552 +array-examples/standard_sentinel-2 unreach-call symex 1 UNKNOWN 0 31.52 180096 +array-examples/standard_sentinel-2 unreach-call hybrid 1 UNKNOWN 0 38.85 341384 +array-examples/standard_two_index_04 unreach-call afl 1 UNKNOWN 0 11.18 106452 +array-examples/standard_two_index_04 unreach-call symex 1 UNKNOWN 0 31.53 180048 +array-examples/standard_two_index_04 unreach-call hybrid 1 UNKNOWN 0 38.55 341376 +array-examples/standard_two_index_05 unreach-call afl 1 UNKNOWN 0 11.19 106196 +array-examples/standard_two_index_05 unreach-call symex 1 UNKNOWN 0 31.51 179800 +array-examples/standard_two_index_05 unreach-call hybrid 1 UNKNOWN 0 38.90 341244 +array-memsafety/add_last_unsafe valid-memsafety afl 1 FAILED FALSE-DEREF 522 main 1 2.50 108056 +array-memsafety/add_last_unsafe valid-memsafety symex 1 FAILED FALSE-DEREF 522 main 1 0.57 145112 +array-memsafety/add_last_unsafe valid-memsafety hybrid 1 FAILED FALSE-DEREF 522 main 1 2.52 107368 +array-memsafety/array01-alloca-2 valid-memsafety afl 1 UNKNOWN 0 2.76 107416 +array-memsafety/array01-alloca-2 valid-memsafety symex 1 UNKNOWN 0 31.77 171596 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 1 UNKNOWN 0 41.58 344988 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 1 UNKNOWN 0 2.84 107868 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 1 UNKNOWN 0 0.66 148444 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 1 UNKNOWN 0 40.45 180656 +array-memsafety/lis_unsafe valid-memsafety afl 1 FAILED FALSE-DEREF 521 lis 1 2.80 107180 +array-memsafety/lis_unsafe valid-memsafety symex 1 UNKNOWN 0 0.73 146000 +array-memsafety/lis_unsafe valid-memsafety hybrid 1 FAILED FALSE-DEREF 521 lis 1 2.54 108276 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 1 UNKNOWN 0 11.52 107472 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 1 SUCCEEDED 1 0.51 130008 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 1 SUCCEEDED 1 7.17 130068 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 1 FAILED FALSE-FREE 46 main 1 2.58 107492 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 1 FAILED FALSE-FREE 46 main 1 0.52 130104 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 1 FAILED FALSE-FREE 46 main 1 2.56 107568 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 1 FAILED FALSE-DEREF 42 main 1 2.56 107012 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 1 FAILED FALSE-DEREF 42 main 1 0.52 130180 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 1 FAILED FALSE-DEREF 42 main 1 2.55 107384 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 1 UNKNOWN 0 11.37 107800 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 1 SUCCEEDED 1 7.31 131028 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 1 SUCCEEDED 1 13.98 130728 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 1 UNKNOWN 0 3.39 107720 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 1 UNKNOWN 0 31.50 130588 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 1 UNKNOWN 0 39.76 131172 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 1 FAILED FALSE-MEMTRACK 1 2.94 109488 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 1 FAILED FALSE-MEMTRACK 1 0.90 141416 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 1 FAILED FALSE-MEMTRACK 1 2.65 109268 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 1 UNKNOWN 0 11.50 341076 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 1 SUCCEEDED 1 1.52 143172 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 1 SUCCEEDED 1 8.27 340756 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 1 FAILED FALSE-FREE 779 free_17 1 2.83 109288 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 1 FAILED FALSE-FREE 779 free_17 1 1.77 145208 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 1 FAILED FALSE-FREE 779 free_17 1 2.84 110292 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 1 UNKNOWN 0 11.51 341304 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 1 SUCCEEDED 1 0.88 141432 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 1 SUCCEEDED 1 7.64 341144 +ldv-memsafety/memsetNonZero valid-memsafety afl 1 UNKNOWN 0 11.34 107988 +ldv-memsafety/memsetNonZero valid-memsafety symex 1 SUCCEEDED 1 0.49 130524 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 1 SUCCEEDED 1 7.03 130556 +list-ext-properties/960521-1_1-1 valid-memsafety afl 1 FAILED FALSE-DEREF 533 main 1 2.60 107952 +list-ext-properties/960521-1_1-1 valid-memsafety symex 1 FAILED FALSE-DEREF 533 main 1 0.66 137984 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 533 main 1 2.58 107684 +list-ext-properties/960521-1_1-2 valid-memsafety afl 1 UNKNOWN 0 11.40 340900 +list-ext-properties/960521-1_1-2 valid-memsafety symex 1 SUCCEEDED 1 2.27 137940 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 1 SUCCEEDED 1 9.09 341368 +list-ext-properties/960521-1_1-3 valid-memsafety afl 1 FAILED FALSE-FREE 536 main 1 2.49 107396 +list-ext-properties/960521-1_1-3 valid-memsafety symex 1 FAILED FALSE-FREE 536 main 1 0.65 139812 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 1 FAILED FALSE-FREE 536 main 1 2.53 106944 +list-ext-properties/list-ext unreach-call afl 1 FAILED FALSE 556 main 1 2.18 106356 +list-ext-properties/list-ext unreach-call symex 1 FAILED FALSE 556 main 1 0.58 136164 +list-ext-properties/list-ext unreach-call hybrid 1 FAILED FALSE 556 main 1 2.17 105836 +list-ext-properties/list-ext valid-memsafety afl 1 FAILED FALSE-DEREF 558 main 1 2.66 107684 +list-ext-properties/list-ext valid-memsafety symex 1 FAILED FALSE-DEREF 558 main 1 3.96 139996 +list-ext-properties/list-ext valid-memsafety hybrid 1 FAILED FALSE-DEREF 558 main 1 2.64 107704 +list-ext-properties/simple-ext unreach-call afl 1 FAILED FALSE 540 main 1 2.16 106472 +list-ext-properties/simple-ext unreach-call symex 1 FAILED FALSE 540 main 1 0.53 136116 +list-ext-properties/simple-ext unreach-call hybrid 1 FAILED FALSE 540 main 1 2.15 105780 +list-ext-properties/simple-ext valid-memcleanup afl 1 UNKNOWN 0 11.46 341540 +list-ext-properties/simple-ext valid-memcleanup symex 1 UNKNOWN 0 0.82 136716 +list-ext-properties/simple-ext valid-memcleanup hybrid 1 UNKNOWN 0 36.03 341560 +list-ext-properties/test-0019_1-1 valid-memsafety afl 1 FAILED FALSE-MEMTRACK 1 2.50 107356 +list-ext-properties/test-0019_1-1 valid-memsafety symex 1 FAILED FALSE-MEMTRACK 1 0.52 130924 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 1 FAILED FALSE-MEMTRACK 1 2.53 107884 +list-ext-properties/test-0158_1-1 valid-memsafety afl 1 UNKNOWN 0 11.41 341572 +list-ext-properties/test-0158_1-1 valid-memsafety symex 1 SUCCEEDED 1 0.52 136624 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 1 SUCCEEDED 1 7.01 340868 +map2check-assert/array_bug unreach-call afl 1 FAILED FALSE 7 __VERIFIER_assert 1 2.17 105260 +map2check-assert/array_bug unreach-call symex 1 FAILED FALSE 7 __VERIFIER_assert 1 0.51 136176 +map2check-assert/array_bug unreach-call hybrid 1 FAILED FALSE 7 __VERIFIER_assert 1 2.18 105604 +map2check-assert/byte_add_bug unreach-call afl 1 UNKNOWN 0 11.07 106788 +map2check-assert/byte_add_bug unreach-call symex 1 SUCCEEDED 1 0.55 129804 +map2check-assert/byte_add_bug unreach-call hybrid 1 SUCCEEDED 1 6.89 341040 +map2check-assert/check_if_bug unreach-call afl 1 UNKNOWN 0 2.01 105776 +map2check-assert/check_if_bug unreach-call symex 1 UNKNOWN 0 31.83 188332 +map2check-assert/check_if_bug unreach-call hybrid 1 UNKNOWN 0 37.86 171372 +map2check-assert/ex01_false unreach-call afl 1 FAILED FALSE 5 __VERIFIER_assert 1 2.14 105248 +map2check-assert/ex01_false unreach-call symex 1 FAILED FALSE 5 __VERIFIER_assert 1 1.70 146156 +map2check-assert/ex01_false unreach-call hybrid 1 FAILED FALSE 5 __VERIFIER_assert 1 2.18 105144 +map2check-assert/ex02_false unreach-call afl 1 UNKNOWN 0 2.00 105448 +map2check-assert/ex02_false unreach-call symex 1 UNKNOWN 0 31.56 226748 +map2check-assert/ex02_false unreach-call hybrid 1 UNKNOWN 0 37.22 210276 +memory-alloca/c.03-alloca-2 valid-memsafety afl 1 UNKNOWN 0 11.36 107764 +memory-alloca/c.03-alloca-2 valid-memsafety symex 1 UNKNOWN 0 31.98 172552 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 1 UNKNOWN 0 41.44 189056 +memsafety-ext/dll_extends_pointer valid-memsafety afl 1 UNKNOWN 0 11.40 341516 +memsafety-ext/dll_extends_pointer valid-memsafety symex 1 UNKNOWN 0 31.66 176748 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 1 UNKNOWN 0 40.25 341820 +memsafety-ext/tree_stack valid-memsafety afl 1 UNKNOWN 0 11.38 357352 +memsafety-ext/tree_stack valid-memsafety symex 1 UNKNOWN 0 31.94 227804 +memsafety-ext/tree_stack valid-memsafety hybrid 1 UNKNOWN 0 40.43 342184 +memsafety-ext2/length_test03-1 valid-memsafety afl 1 UNKNOWN 0 11.51 341228 +memsafety-ext2/length_test03-1 valid-memsafety symex 1 UNKNOWN 0 31.51 145820 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 1 UNKNOWN 0 39.08 341156 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 1 FAILED FALSE-DEREF 540 containsHead 1 2.54 108948 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 1 UNKNOWN 0 32.65 395284 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 540 containsHead 1 2.51 108812 +memsafety/960521-1-1 valid-memsafety afl 1 FAILED FALSE-FREE 532 main 1 2.50 107916 +memsafety/960521-1-1 valid-memsafety symex 1 FAILED FALSE-FREE 532 main 1 0.59 130724 +memsafety/960521-1-1 valid-memsafety hybrid 1 FAILED FALSE-FREE 532 main 1 2.52 107736 +memsafety/960521-1-2 valid-memsafety afl 1 UNKNOWN 0 11.37 107408 +memsafety/960521-1-2 valid-memsafety symex 1 SUCCEEDED 1 15.64 129912 +memsafety/960521-1-2 valid-memsafety hybrid 1 UNKNOWN 0 38.77 130200 +memsafety/960521-1-3 valid-memsafety afl 1 FAILED FALSE-DEREF 529 main 1 2.55 107600 +memsafety/960521-1-3 valid-memsafety symex 1 FAILED FALSE-DEREF 529 main 1 0.58 130720 +memsafety/960521-1-3 valid-memsafety hybrid 1 FAILED FALSE-DEREF 529 main 1 2.53 107752 +memsafety/test-0102-1 valid-memsafety afl 1 UNKNOWN 0 11.46 340912 +memsafety/test-0102-1 valid-memsafety symex 1 UNKNOWN 0 31.54 166608 +memsafety/test-0102-1 valid-memsafety hybrid 1 UNKNOWN 0 38.98 341388 +array-examples/relax-1 valid-memsafety afl 2 ERROR 0 2.69 109164 +array-examples/relax-1 valid-memsafety symex 2 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 0.71 155464 +array-examples/relax-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 3.94 155556 +array-examples/standard_sentinel-2 unreach-call afl 2 UNKNOWN 0 11.05 106084 +array-examples/standard_sentinel-2 unreach-call symex 2 UNKNOWN 0 31.50 181092 +array-examples/standard_sentinel-2 unreach-call hybrid 2 UNKNOWN 0 39.43 341300 +array-examples/standard_two_index_04 unreach-call afl 2 UNKNOWN 0 11.02 105996 +array-examples/standard_two_index_04 unreach-call symex 2 UNKNOWN 0 31.50 180616 +array-examples/standard_two_index_04 unreach-call hybrid 2 UNKNOWN 0 38.53 341704 +array-examples/standard_two_index_05 unreach-call afl 2 UNKNOWN 0 11.04 106712 +array-examples/standard_two_index_05 unreach-call symex 2 UNKNOWN 0 31.49 180176 +array-examples/standard_two_index_05 unreach-call hybrid 2 UNKNOWN 0 38.70 341372 +array-memsafety/add_last_unsafe valid-memsafety afl 2 FAILED FALSE-DEREF 522 main 1 2.75 108032 +array-memsafety/add_last_unsafe valid-memsafety symex 2 FAILED FALSE-DEREF 522 main 1 0.61 146052 +array-memsafety/add_last_unsafe valid-memsafety hybrid 2 FAILED FALSE-DEREF 522 main 1 2.76 107188 +array-memsafety/array01-alloca-2 valid-memsafety afl 2 UNKNOWN 0 6.39 108012 +array-memsafety/array01-alloca-2 valid-memsafety symex 2 UNKNOWN 0 31.69 171384 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 2 UNKNOWN 0 41.01 330416 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 2 UNKNOWN 0 2.69 107760 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 2 UNKNOWN 0 0.65 145908 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 2 UNKNOWN 0 40.42 235196 +array-memsafety/lis_unsafe valid-memsafety afl 2 FAILED FALSE-DEREF 521 lis 1 2.53 108272 +array-memsafety/lis_unsafe valid-memsafety symex 2 UNKNOWN 0 0.67 147036 +array-memsafety/lis_unsafe valid-memsafety hybrid 2 FAILED FALSE-DEREF 521 lis 1 2.54 108736 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 2 UNKNOWN 0 11.36 108716 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 2 SUCCEEDED 1 0.50 130316 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 2 SUCCEEDED 1 6.97 341348 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 2 FAILED FALSE-FREE 46 main 1 2.49 107628 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 2 FAILED FALSE-FREE 46 main 1 0.50 130140 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 2 FAILED FALSE-FREE 46 main 1 2.51 108156 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 2 FAILED FALSE-DEREF 42 main 1 2.52 108016 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 2 FAILED FALSE-DEREF 42 main 1 0.50 130508 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 2 FAILED FALSE-DEREF 42 main 1 2.55 107884 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 2 UNKNOWN 0 11.39 108284 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 2 SUCCEEDED 1 7.62 130536 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 2 SUCCEEDED 1 14.00 130228 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 2 UNKNOWN 0 3.42 108120 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 2 UNKNOWN 0 31.49 130808 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 2 UNKNOWN 0 39.72 131068 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 2 FAILED FALSE-MEMTRACK 1 2.57 109732 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 2 FAILED FALSE-MEMTRACK 1 0.89 141512 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 2 FAILED FALSE-MEMTRACK 1 2.63 109308 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 2 UNKNOWN 0 11.58 341168 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 2 SUCCEEDED 1 1.51 142804 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 2 SUCCEEDED 1 8.65 341164 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 2 FAILED FALSE-FREE 779 free_17 1 2.76 109936 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 2 FAILED FALSE-FREE 779 free_17 1 1.87 144844 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 2 FAILED FALSE-FREE 779 free_17 1 3.27 108992 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 2 UNKNOWN 0 11.72 341196 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 2 SUCCEEDED 1 0.91 141288 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 2 SUCCEEDED 1 7.70 340892 +ldv-memsafety/memsetNonZero valid-memsafety afl 2 UNKNOWN 0 11.35 107768 +ldv-memsafety/memsetNonZero valid-memsafety symex 2 SUCCEEDED 1 0.50 130556 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 2 SUCCEEDED 1 6.98 341156 +list-ext-properties/960521-1_1-1 valid-memsafety afl 2 FAILED FALSE-DEREF 533 main 1 2.49 108040 +list-ext-properties/960521-1_1-1 valid-memsafety symex 2 FAILED FALSE-DEREF 533 main 1 0.64 138020 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 533 main 1 2.54 108244 +list-ext-properties/960521-1_1-2 valid-memsafety afl 2 UNKNOWN 0 11.39 341244 +list-ext-properties/960521-1_1-2 valid-memsafety symex 2 SUCCEEDED 1 2.18 137868 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 2 SUCCEEDED 1 9.07 341756 +list-ext-properties/960521-1_1-3 valid-memsafety afl 2 FAILED FALSE-FREE 536 main 1 2.46 108612 +list-ext-properties/960521-1_1-3 valid-memsafety symex 2 FAILED FALSE-FREE 536 main 1 0.65 138024 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 2 FAILED FALSE-FREE 536 main 1 2.49 108024 +list-ext-properties/list-ext unreach-call afl 2 FAILED FALSE 556 main 1 2.16 106264 +list-ext-properties/list-ext unreach-call symex 2 FAILED FALSE 556 main 1 0.58 136596 +list-ext-properties/list-ext unreach-call hybrid 2 FAILED FALSE 556 main 1 2.18 106180 +list-ext-properties/list-ext valid-memsafety afl 2 FAILED FALSE-DEREF 558 main 1 2.69 108124 +list-ext-properties/list-ext valid-memsafety symex 2 FAILED FALSE-DEREF 558 main 1 4.04 138736 +list-ext-properties/list-ext valid-memsafety hybrid 2 FAILED FALSE-DEREF 558 main 1 2.64 108140 +list-ext-properties/simple-ext unreach-call afl 2 FAILED FALSE 540 main 1 2.15 105916 +list-ext-properties/simple-ext unreach-call symex 2 FAILED FALSE 540 main 1 0.53 136432 +list-ext-properties/simple-ext unreach-call hybrid 2 FAILED FALSE 540 main 1 2.16 106000 +list-ext-properties/simple-ext valid-memcleanup afl 2 UNKNOWN 0 11.47 341260 +list-ext-properties/simple-ext valid-memcleanup symex 2 UNKNOWN 0 0.80 137028 +list-ext-properties/simple-ext valid-memcleanup hybrid 2 UNKNOWN 0 36.01 341212 +list-ext-properties/test-0019_1-1 valid-memsafety afl 2 FAILED FALSE-MEMTRACK 1 2.74 107740 +list-ext-properties/test-0019_1-1 valid-memsafety symex 2 FAILED FALSE-MEMTRACK 1 0.56 131016 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 2 FAILED FALSE-MEMTRACK 1 2.70 107600 +list-ext-properties/test-0158_1-1 valid-memsafety afl 2 UNKNOWN 0 11.52 341328 +list-ext-properties/test-0158_1-1 valid-memsafety symex 2 SUCCEEDED 1 0.54 135908 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 2 SUCCEEDED 1 7.08 341116 +map2check-assert/array_bug unreach-call afl 2 FAILED FALSE 7 __VERIFIER_assert 1 2.30 106264 +map2check-assert/array_bug unreach-call symex 2 FAILED FALSE 7 __VERIFIER_assert 1 0.56 136020 +map2check-assert/array_bug unreach-call hybrid 2 FAILED FALSE 7 __VERIFIER_assert 1 2.40 106668 +map2check-assert/byte_add_bug unreach-call afl 2 UNKNOWN 0 11.23 340760 +map2check-assert/byte_add_bug unreach-call symex 2 SUCCEEDED 1 0.50 130456 +map2check-assert/byte_add_bug unreach-call hybrid 2 SUCCEEDED 1 6.68 129976 +map2check-assert/check_if_bug unreach-call afl 2 UNKNOWN 0 2.05 105400 +map2check-assert/check_if_bug unreach-call symex 2 UNKNOWN 0 31.90 188196 +map2check-assert/check_if_bug unreach-call hybrid 2 UNKNOWN 0 37.79 171284 +map2check-assert/ex01_false unreach-call afl 2 FAILED FALSE 5 __VERIFIER_assert 1 2.30 105964 +map2check-assert/ex01_false unreach-call symex 2 FAILED FALSE 5 __VERIFIER_assert 1 1.71 146244 +map2check-assert/ex01_false unreach-call hybrid 2 FAILED FALSE 5 __VERIFIER_assert 1 2.31 105964 +map2check-assert/ex02_false unreach-call afl 2 UNKNOWN 0 2.11 106324 +map2check-assert/ex02_false unreach-call symex 2 UNKNOWN 0 31.55 224328 +map2check-assert/ex02_false unreach-call hybrid 2 UNKNOWN 0 37.24 211164 +memory-alloca/c.03-alloca-2 valid-memsafety afl 2 UNKNOWN 0 11.35 107784 +memory-alloca/c.03-alloca-2 valid-memsafety symex 2 UNKNOWN 0 31.97 173080 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 2 UNKNOWN 0 40.25 181296 +memsafety-ext/dll_extends_pointer valid-memsafety afl 2 UNKNOWN 0 11.33 341248 +memsafety-ext/dll_extends_pointer valid-memsafety symex 2 UNKNOWN 0 31.66 178000 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 2 UNKNOWN 0 40.24 341680 +memsafety-ext/tree_stack valid-memsafety afl 2 UNKNOWN 0 11.43 340960 +memsafety-ext/tree_stack valid-memsafety symex 2 UNKNOWN 0 32.01 232548 +memsafety-ext/tree_stack valid-memsafety hybrid 2 UNKNOWN 0 40.92 357632 +memsafety-ext2/length_test03-1 valid-memsafety afl 2 UNKNOWN 0 11.57 340740 +memsafety-ext2/length_test03-1 valid-memsafety symex 2 UNKNOWN 0 31.52 145904 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 2 UNKNOWN 0 39.22 341312 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 2 FAILED FALSE-DEREF 540 containsHead 1 2.61 108844 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 2 UNKNOWN 0 32.68 400300 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 540 containsHead 1 2.55 109084 +memsafety/960521-1-1 valid-memsafety afl 2 FAILED FALSE-FREE 532 main 1 2.56 108412 +memsafety/960521-1-1 valid-memsafety symex 2 FAILED FALSE-FREE 532 main 1 0.60 131008 +memsafety/960521-1-1 valid-memsafety hybrid 2 FAILED FALSE-FREE 532 main 1 2.69 108400 +memsafety/960521-1-2 valid-memsafety afl 2 UNKNOWN 0 11.46 107292 +memsafety/960521-1-2 valid-memsafety symex 2 SUCCEEDED 1 15.45 129472 +memsafety/960521-1-2 valid-memsafety hybrid 2 UNKNOWN 0 38.74 130384 +memsafety/960521-1-3 valid-memsafety afl 2 FAILED FALSE-DEREF 529 main 1 2.49 108284 +memsafety/960521-1-3 valid-memsafety symex 2 FAILED FALSE-DEREF 529 main 1 0.60 130760 +memsafety/960521-1-3 valid-memsafety hybrid 2 FAILED FALSE-DEREF 529 main 1 2.53 107552 +memsafety/test-0102-1 valid-memsafety afl 2 UNKNOWN 0 11.42 108964 +memsafety/test-0102-1 valid-memsafety symex 2 UNKNOWN 0 31.56 166692 +memsafety/test-0102-1 valid-memsafety hybrid 2 UNKNOWN 0 39.02 341372 +array-examples/relax-1 valid-memsafety afl 3 ERROR 0 2.74 108372 +array-examples/relax-1 valid-memsafety symex 3 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 0.71 155220 +array-examples/relax-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 3.79 155848 +array-examples/standard_sentinel-2 unreach-call afl 3 UNKNOWN 0 11.01 106436 +array-examples/standard_sentinel-2 unreach-call symex 3 UNKNOWN 0 31.53 180356 +array-examples/standard_sentinel-2 unreach-call hybrid 3 UNKNOWN 0 38.61 341480 +array-examples/standard_two_index_04 unreach-call afl 3 UNKNOWN 0 11.00 106736 +array-examples/standard_two_index_04 unreach-call symex 3 UNKNOWN 0 31.50 180440 +array-examples/standard_two_index_04 unreach-call hybrid 3 UNKNOWN 0 38.60 341320 +array-examples/standard_two_index_05 unreach-call afl 3 UNKNOWN 0 11.00 106380 +array-examples/standard_two_index_05 unreach-call symex 3 UNKNOWN 0 31.50 180532 +array-examples/standard_two_index_05 unreach-call hybrid 3 UNKNOWN 0 39.13 341404 +array-memsafety/add_last_unsafe valid-memsafety afl 3 FAILED FALSE-DEREF 522 main 1 2.47 108244 +array-memsafety/add_last_unsafe valid-memsafety symex 3 FAILED FALSE-DEREF 522 main 1 0.55 145052 +array-memsafety/add_last_unsafe valid-memsafety hybrid 3 FAILED FALSE-DEREF 522 main 1 2.49 107884 +array-memsafety/array01-alloca-2 valid-memsafety afl 3 UNKNOWN 0 5.05 107372 +array-memsafety/array01-alloca-2 valid-memsafety symex 3 UNKNOWN 0 31.67 171480 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 3 UNKNOWN 0 41.35 316332 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 3 UNKNOWN 0 3.10 107936 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 3 UNKNOWN 0 0.67 146608 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 3 UNKNOWN 0 38.44 190896 +array-memsafety/lis_unsafe valid-memsafety afl 3 FAILED FALSE-DEREF 521 lis 1 2.52 108120 +array-memsafety/lis_unsafe valid-memsafety symex 3 UNKNOWN 0 0.66 147324 +array-memsafety/lis_unsafe valid-memsafety hybrid 3 FAILED FALSE-DEREF 521 lis 1 2.56 108388 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 3 UNKNOWN 0 11.39 107780 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 3 SUCCEEDED 1 0.51 130756 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 3 SUCCEEDED 1 7.04 130916 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 3 FAILED FALSE-FREE 46 main 1 2.56 107560 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 3 FAILED FALSE-FREE 46 main 1 0.51 130140 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 3 FAILED FALSE-FREE 46 main 1 2.60 108540 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 3 FAILED FALSE-DEREF 42 main 1 2.58 108164 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 3 FAILED FALSE-DEREF 42 main 1 0.52 129772 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 3 FAILED FALSE-DEREF 42 main 1 2.57 107908 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 3 UNKNOWN 0 11.41 108424 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 3 SUCCEEDED 1 7.67 130964 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 3 SUCCEEDED 1 13.99 130636 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 3 UNKNOWN 0 3.53 108712 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 3 UNKNOWN 0 31.49 130596 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 3 UNKNOWN 0 39.75 131056 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 3 FAILED FALSE-MEMTRACK 1 2.64 110172 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 3 FAILED FALSE-MEMTRACK 1 0.91 141256 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 3 FAILED FALSE-MEMTRACK 1 2.62 109512 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 3 UNKNOWN 0 11.57 341312 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 3 SUCCEEDED 1 1.51 143560 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 3 SUCCEEDED 1 8.17 340988 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 3 FAILED FALSE-FREE 779 free_17 1 3.10 110304 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 3 FAILED FALSE-FREE 779 free_17 1 1.79 145228 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 3 FAILED FALSE-FREE 779 free_17 1 4.31 110192 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 3 UNKNOWN 0 11.50 340888 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 3 SUCCEEDED 1 0.90 141604 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 3 SUCCEEDED 1 7.80 341032 +ldv-memsafety/memsetNonZero valid-memsafety afl 3 UNKNOWN 0 11.32 107924 +ldv-memsafety/memsetNonZero valid-memsafety symex 3 SUCCEEDED 1 0.49 130464 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 3 SUCCEEDED 1 6.96 130388 +list-ext-properties/960521-1_1-1 valid-memsafety afl 3 FAILED FALSE-DEREF 533 main 1 2.48 108660 +list-ext-properties/960521-1_1-1 valid-memsafety symex 3 FAILED FALSE-DEREF 533 main 1 0.65 137636 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 533 main 1 2.54 108524 +list-ext-properties/960521-1_1-2 valid-memsafety afl 3 UNKNOWN 0 11.41 341292 +list-ext-properties/960521-1_1-2 valid-memsafety symex 3 SUCCEEDED 1 2.29 138236 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 3 SUCCEEDED 1 9.15 341176 +list-ext-properties/960521-1_1-3 valid-memsafety afl 3 FAILED FALSE-FREE 536 main 1 2.52 107904 +list-ext-properties/960521-1_1-3 valid-memsafety symex 3 FAILED FALSE-FREE 536 main 1 0.65 138124 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 3 FAILED FALSE-FREE 536 main 1 2.57 108056 +list-ext-properties/list-ext unreach-call afl 3 FAILED FALSE 556 main 1 2.19 106400 +list-ext-properties/list-ext unreach-call symex 3 FAILED FALSE 556 main 1 0.59 136428 +list-ext-properties/list-ext unreach-call hybrid 3 FAILED FALSE 556 main 1 2.19 106196 +list-ext-properties/list-ext valid-memsafety afl 3 FAILED FALSE-DEREF 558 main 1 2.68 108184 +list-ext-properties/list-ext valid-memsafety symex 3 FAILED FALSE-DEREF 558 main 1 4.12 139880 +list-ext-properties/list-ext valid-memsafety hybrid 3 FAILED FALSE-DEREF 558 main 1 2.66 108696 +list-ext-properties/simple-ext unreach-call afl 3 FAILED FALSE 540 main 1 2.18 106260 +list-ext-properties/simple-ext unreach-call symex 3 FAILED FALSE 540 main 1 0.53 136460 +list-ext-properties/simple-ext unreach-call hybrid 3 FAILED FALSE 540 main 1 2.17 106572 +list-ext-properties/simple-ext valid-memcleanup afl 3 UNKNOWN 0 11.49 341128 +list-ext-properties/simple-ext valid-memcleanup symex 3 UNKNOWN 0 0.82 136876 +list-ext-properties/simple-ext valid-memcleanup hybrid 3 UNKNOWN 0 36.05 341576 +list-ext-properties/test-0019_1-1 valid-memsafety afl 3 FAILED FALSE-MEMTRACK 1 2.52 108480 +list-ext-properties/test-0019_1-1 valid-memsafety symex 3 FAILED FALSE-MEMTRACK 1 0.53 130636 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 3 FAILED FALSE-MEMTRACK 1 2.60 107608 +list-ext-properties/test-0158_1-1 valid-memsafety afl 3 UNKNOWN 0 11.42 341268 +list-ext-properties/test-0158_1-1 valid-memsafety symex 3 SUCCEEDED 1 0.56 136780 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 3 SUCCEEDED 1 6.99 340924 +map2check-assert/array_bug unreach-call afl 3 FAILED FALSE 7 __VERIFIER_assert 1 2.17 105636 +map2check-assert/array_bug unreach-call symex 3 FAILED FALSE 7 __VERIFIER_assert 1 0.52 135944 +map2check-assert/array_bug unreach-call hybrid 3 FAILED FALSE 7 __VERIFIER_assert 1 2.17 105692 +map2check-assert/byte_add_bug unreach-call afl 3 UNKNOWN 0 11.06 106472 +map2check-assert/byte_add_bug unreach-call symex 3 SUCCEEDED 1 0.50 130368 +map2check-assert/byte_add_bug unreach-call hybrid 3 SUCCEEDED 1 6.71 341096 +map2check-assert/check_if_bug unreach-call afl 3 UNKNOWN 0 2.02 105940 +map2check-assert/check_if_bug unreach-call symex 3 UNKNOWN 0 31.86 187952 +map2check-assert/check_if_bug unreach-call hybrid 3 UNKNOWN 0 37.73 170368 +map2check-assert/ex01_false unreach-call afl 3 FAILED FALSE 5 __VERIFIER_assert 1 2.20 106304 +map2check-assert/ex01_false unreach-call symex 3 FAILED FALSE 5 __VERIFIER_assert 1 1.79 146276 +map2check-assert/ex01_false unreach-call hybrid 3 FAILED FALSE 5 __VERIFIER_assert 1 2.18 105628 +map2check-assert/ex02_false unreach-call afl 3 UNKNOWN 0 2.05 106332 +map2check-assert/ex02_false unreach-call symex 3 UNKNOWN 0 31.55 226172 +map2check-assert/ex02_false unreach-call hybrid 3 UNKNOWN 0 37.23 211584 +memory-alloca/c.03-alloca-2 valid-memsafety afl 3 UNKNOWN 0 11.35 108020 +memory-alloca/c.03-alloca-2 valid-memsafety symex 3 UNKNOWN 0 31.91 172988 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 3 UNKNOWN 0 40.81 189612 +memsafety-ext/dll_extends_pointer valid-memsafety afl 3 UNKNOWN 0 11.47 341272 +memsafety-ext/dll_extends_pointer valid-memsafety symex 3 UNKNOWN 0 31.66 178472 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 3 UNKNOWN 0 40.18 341848 +memsafety-ext/tree_stack valid-memsafety afl 3 UNKNOWN 0 11.36 357296 +memsafety-ext/tree_stack valid-memsafety symex 3 UNKNOWN 0 31.97 238080 +memsafety-ext/tree_stack valid-memsafety hybrid 3 UNKNOWN 0 40.13 357664 +memsafety-ext2/length_test03-1 valid-memsafety afl 3 UNKNOWN 0 11.39 341508 +memsafety-ext2/length_test03-1 valid-memsafety symex 3 UNKNOWN 0 31.51 145848 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 3 UNKNOWN 0 39.13 341308 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 3 FAILED FALSE-DEREF 540 containsHead 1 2.61 109328 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 3 UNKNOWN 0 32.60 377048 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 540 containsHead 1 2.53 109016 +memsafety/960521-1-1 valid-memsafety afl 3 FAILED FALSE-FREE 532 main 1 2.49 107984 +memsafety/960521-1-1 valid-memsafety symex 3 FAILED FALSE-FREE 532 main 1 0.57 130276 +memsafety/960521-1-1 valid-memsafety hybrid 3 FAILED FALSE-FREE 532 main 1 2.50 108088 +memsafety/960521-1-2 valid-memsafety afl 3 UNKNOWN 0 11.35 107668 +memsafety/960521-1-2 valid-memsafety symex 3 SUCCEEDED 1 14.90 130388 +memsafety/960521-1-2 valid-memsafety hybrid 3 UNKNOWN 0 38.82 130100 +memsafety/960521-1-3 valid-memsafety afl 3 FAILED FALSE-DEREF 529 main 1 2.48 107916 +memsafety/960521-1-3 valid-memsafety symex 3 FAILED FALSE-DEREF 529 main 1 0.58 130260 +memsafety/960521-1-3 valid-memsafety hybrid 3 FAILED FALSE-DEREF 529 main 1 2.51 107780 +memsafety/test-0102-1 valid-memsafety afl 3 UNKNOWN 0 11.39 341288 +memsafety/test-0102-1 valid-memsafety symex 3 UNKNOWN 0 31.54 166748 +memsafety/test-0102-1 valid-memsafety hybrid 3 UNKNOWN 0 39.02 341160 +array-examples/relax-1 valid-memsafety afl 4 ERROR 0 2.78 108572 +array-examples/relax-1 valid-memsafety symex 4 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 0.73 155756 +array-examples/relax-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 3.98 155960 +array-examples/standard_sentinel-2 unreach-call afl 4 UNKNOWN 0 11.16 105872 +array-examples/standard_sentinel-2 unreach-call symex 4 UNKNOWN 0 31.54 180052 +array-examples/standard_sentinel-2 unreach-call hybrid 4 UNKNOWN 0 39.10 341480 +array-examples/standard_two_index_04 unreach-call afl 4 UNKNOWN 0 11.12 106624 +array-examples/standard_two_index_04 unreach-call symex 4 UNKNOWN 0 31.52 180172 +array-examples/standard_two_index_04 unreach-call hybrid 4 UNKNOWN 0 39.04 341204 +array-examples/standard_two_index_05 unreach-call afl 4 UNKNOWN 0 11.09 105788 +array-examples/standard_two_index_05 unreach-call symex 4 UNKNOWN 0 31.51 179808 +array-examples/standard_two_index_05 unreach-call hybrid 4 UNKNOWN 0 39.38 341852 +array-memsafety/add_last_unsafe valid-memsafety afl 4 FAILED FALSE-DEREF 522 main 1 2.52 108104 +array-memsafety/add_last_unsafe valid-memsafety symex 4 FAILED FALSE-DEREF 522 main 1 0.57 145992 +array-memsafety/add_last_unsafe valid-memsafety hybrid 4 FAILED FALSE-DEREF 522 main 1 2.52 108028 +array-memsafety/array01-alloca-2 valid-memsafety afl 4 UNKNOWN 0 3.72 107812 +array-memsafety/array01-alloca-2 valid-memsafety symex 4 UNKNOWN 0 31.64 171020 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 4 UNKNOWN 0 39.82 171116 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 4 UNKNOWN 0 2.71 108152 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 4 UNKNOWN 0 0.65 146100 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 4 UNKNOWN 0 35.39 341608 +array-memsafety/lis_unsafe valid-memsafety afl 4 FAILED FALSE-DEREF 521 lis 1 2.55 108128 +array-memsafety/lis_unsafe valid-memsafety symex 4 UNKNOWN 0 0.65 147332 +array-memsafety/lis_unsafe valid-memsafety hybrid 4 FAILED FALSE-DEREF 521 lis 1 2.55 108480 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 4 UNKNOWN 0 11.59 341352 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 4 SUCCEEDED 1 0.51 130212 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 4 SUCCEEDED 1 6.97 130224 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 4 FAILED FALSE-FREE 46 main 1 2.48 108188 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 4 FAILED FALSE-FREE 46 main 1 0.51 130116 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 4 FAILED FALSE-FREE 46 main 1 2.52 107708 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 4 FAILED FALSE-DEREF 42 main 1 2.51 108400 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 4 FAILED FALSE-DEREF 42 main 1 0.50 130820 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 4 FAILED FALSE-DEREF 42 main 1 2.51 108276 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 4 UNKNOWN 0 11.36 108824 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 4 SUCCEEDED 1 7.64 130748 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 4 SUCCEEDED 1 13.97 131056 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 4 UNKNOWN 0 3.40 108384 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 4 UNKNOWN 0 31.48 131032 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 4 UNKNOWN 0 39.74 130816 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 4 FAILED FALSE-MEMTRACK 1 2.60 109948 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 4 FAILED FALSE-MEMTRACK 1 0.91 141856 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 4 FAILED FALSE-MEMTRACK 1 2.65 110304 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 4 UNKNOWN 0 11.50 341184 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 4 SUCCEEDED 1 1.54 143272 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 4 SUCCEEDED 1 8.15 341224 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 4 FAILED FALSE-FREE 779 free_17 1 2.79 110212 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 4 FAILED FALSE-FREE 779 free_17 1 1.78 144000 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 4 FAILED FALSE-FREE 779 free_17 1 2.87 110208 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 4 UNKNOWN 0 11.49 341376 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 4 SUCCEEDED 1 0.90 140964 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 4 SUCCEEDED 1 7.63 341316 +ldv-memsafety/memsetNonZero valid-memsafety afl 4 UNKNOWN 0 11.33 107740 +ldv-memsafety/memsetNonZero valid-memsafety symex 4 SUCCEEDED 1 0.49 130260 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 4 SUCCEEDED 1 6.94 130848 +list-ext-properties/960521-1_1-1 valid-memsafety afl 4 FAILED FALSE-DEREF 533 main 1 2.53 108528 +list-ext-properties/960521-1_1-1 valid-memsafety symex 4 FAILED FALSE-DEREF 533 main 1 0.66 137344 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 533 main 1 2.56 108220 +list-ext-properties/960521-1_1-2 valid-memsafety afl 4 UNKNOWN 0 11.40 341308 +list-ext-properties/960521-1_1-2 valid-memsafety symex 4 SUCCEEDED 1 2.24 137796 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 4 SUCCEEDED 1 9.11 341224 +list-ext-properties/960521-1_1-3 valid-memsafety afl 4 FAILED FALSE-FREE 536 main 1 2.52 108000 +list-ext-properties/960521-1_1-3 valid-memsafety symex 4 FAILED FALSE-FREE 536 main 1 0.67 137936 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 4 FAILED FALSE-FREE 536 main 1 2.51 108420 +list-ext-properties/list-ext unreach-call afl 4 FAILED FALSE 556 main 1 2.18 106012 +list-ext-properties/list-ext unreach-call symex 4 FAILED FALSE 556 main 1 0.57 136248 +list-ext-properties/list-ext unreach-call hybrid 4 FAILED FALSE 556 main 1 2.16 106340 +list-ext-properties/list-ext valid-memsafety afl 4 FAILED FALSE-DEREF 558 main 1 2.75 108648 +list-ext-properties/list-ext valid-memsafety symex 4 FAILED FALSE-DEREF 558 main 1 3.97 139584 +list-ext-properties/list-ext valid-memsafety hybrid 4 FAILED FALSE-DEREF 558 main 1 2.63 108212 +list-ext-properties/simple-ext unreach-call afl 4 FAILED FALSE 540 main 1 2.14 106376 +list-ext-properties/simple-ext unreach-call symex 4 FAILED FALSE 540 main 1 0.54 136268 +list-ext-properties/simple-ext unreach-call hybrid 4 FAILED FALSE 540 main 1 2.16 106564 +list-ext-properties/simple-ext valid-memcleanup afl 4 UNKNOWN 0 11.50 341208 +list-ext-properties/simple-ext valid-memcleanup symex 4 UNKNOWN 0 0.81 136172 +list-ext-properties/simple-ext valid-memcleanup hybrid 4 UNKNOWN 0 36.05 357628 +list-ext-properties/test-0019_1-1 valid-memsafety afl 4 FAILED FALSE-MEMTRACK 1 2.47 107992 +list-ext-properties/test-0019_1-1 valid-memsafety symex 4 FAILED FALSE-MEMTRACK 1 0.52 130160 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 4 FAILED FALSE-MEMTRACK 1 2.47 108088 +list-ext-properties/test-0158_1-1 valid-memsafety afl 4 UNKNOWN 0 11.38 341116 +list-ext-properties/test-0158_1-1 valid-memsafety symex 4 SUCCEEDED 1 0.52 136812 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 4 SUCCEEDED 1 6.96 341432 +map2check-assert/array_bug unreach-call afl 4 FAILED FALSE 7 __VERIFIER_assert 1 2.17 106276 +map2check-assert/array_bug unreach-call symex 4 FAILED FALSE 7 __VERIFIER_assert 1 0.51 136532 +map2check-assert/array_bug unreach-call hybrid 4 FAILED FALSE 7 __VERIFIER_assert 1 2.19 106524 +map2check-assert/byte_add_bug unreach-call afl 4 UNKNOWN 0 11.04 107324 +map2check-assert/byte_add_bug unreach-call symex 4 SUCCEEDED 1 0.48 130396 +map2check-assert/byte_add_bug unreach-call hybrid 4 SUCCEEDED 1 6.64 341136 +map2check-assert/check_if_bug unreach-call afl 4 UNKNOWN 0 1.98 106180 +map2check-assert/check_if_bug unreach-call symex 4 UNKNOWN 0 32.07 187732 +map2check-assert/check_if_bug unreach-call hybrid 4 UNKNOWN 0 37.73 171068 +map2check-assert/ex01_false unreach-call afl 4 FAILED FALSE 5 __VERIFIER_assert 1 2.12 106084 +map2check-assert/ex01_false unreach-call symex 4 FAILED FALSE 5 __VERIFIER_assert 1 1.69 146372 +map2check-assert/ex01_false unreach-call hybrid 4 FAILED FALSE 5 __VERIFIER_assert 1 2.15 105796 +map2check-assert/ex02_false unreach-call afl 4 UNKNOWN 0 2.02 106216 +map2check-assert/ex02_false unreach-call symex 4 UNKNOWN 0 31.56 226928 +map2check-assert/ex02_false unreach-call hybrid 4 UNKNOWN 0 37.21 210952 +memory-alloca/c.03-alloca-2 valid-memsafety afl 4 UNKNOWN 0 11.33 108852 +memory-alloca/c.03-alloca-2 valid-memsafety symex 4 UNKNOWN 0 32.18 172816 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 4 UNKNOWN 0 41.43 191340 +memsafety-ext/dll_extends_pointer valid-memsafety afl 4 UNKNOWN 0 11.40 341204 +memsafety-ext/dll_extends_pointer valid-memsafety symex 4 UNKNOWN 0 31.66 178708 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 4 UNKNOWN 0 40.21 357512 +memsafety-ext/tree_stack valid-memsafety afl 4 UNKNOWN 0 11.37 341232 +memsafety-ext/tree_stack valid-memsafety symex 4 UNKNOWN 0 31.97 233668 +memsafety-ext/tree_stack valid-memsafety hybrid 4 UNKNOWN 0 40.17 357500 +memsafety-ext2/length_test03-1 valid-memsafety afl 4 UNKNOWN 0 11.38 341244 +memsafety-ext2/length_test03-1 valid-memsafety symex 4 UNKNOWN 0 31.50 145708 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 4 UNKNOWN 0 39.06 341220 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 4 FAILED FALSE-DEREF 540 containsHead 1 2.53 108224 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 4 UNKNOWN 0 32.62 391736 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 540 containsHead 1 2.52 109112 +memsafety/960521-1-1 valid-memsafety afl 4 FAILED FALSE-FREE 532 main 1 2.50 107936 +memsafety/960521-1-1 valid-memsafety symex 4 FAILED FALSE-FREE 532 main 1 0.59 130312 +memsafety/960521-1-1 valid-memsafety hybrid 4 FAILED FALSE-FREE 532 main 1 2.53 107992 +memsafety/960521-1-2 valid-memsafety afl 4 UNKNOWN 0 11.41 108732 +memsafety/960521-1-2 valid-memsafety symex 4 SUCCEEDED 1 15.42 130044 +memsafety/960521-1-2 valid-memsafety hybrid 4 UNKNOWN 0 38.79 130504 +memsafety/960521-1-3 valid-memsafety afl 4 FAILED FALSE-DEREF 529 main 1 2.56 108664 +memsafety/960521-1-3 valid-memsafety symex 4 FAILED FALSE-DEREF 529 main 1 0.59 130696 +memsafety/960521-1-3 valid-memsafety hybrid 4 FAILED FALSE-DEREF 529 main 1 2.59 108032 +memsafety/test-0102-1 valid-memsafety afl 4 UNKNOWN 0 11.48 341092 +memsafety/test-0102-1 valid-memsafety symex 4 UNKNOWN 0 31.55 166936 +memsafety/test-0102-1 valid-memsafety hybrid 4 UNKNOWN 0 39.03 341140 +array-examples/relax-1 valid-memsafety afl 5 ERROR 0 2.80 108052 +array-examples/relax-1 valid-memsafety symex 5 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 0.73 154872 +array-examples/relax-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 3.91 155380 +array-examples/standard_sentinel-2 unreach-call afl 5 UNKNOWN 0 11.05 105764 +array-examples/standard_sentinel-2 unreach-call symex 5 UNKNOWN 0 31.52 180492 +array-examples/standard_sentinel-2 unreach-call hybrid 5 UNKNOWN 0 39.20 341004 +array-examples/standard_two_index_04 unreach-call afl 5 UNKNOWN 0 11.02 105784 +array-examples/standard_two_index_04 unreach-call symex 5 UNKNOWN 0 31.51 180228 +array-examples/standard_two_index_04 unreach-call hybrid 5 UNKNOWN 0 38.57 341528 +array-examples/standard_two_index_05 unreach-call afl 5 UNKNOWN 0 11.04 106232 +array-examples/standard_two_index_05 unreach-call symex 5 UNKNOWN 0 31.52 180120 +array-examples/standard_two_index_05 unreach-call hybrid 5 UNKNOWN 0 38.82 341552 +array-memsafety/add_last_unsafe valid-memsafety afl 5 FAILED FALSE-DEREF 522 main 1 2.58 107576 +array-memsafety/add_last_unsafe valid-memsafety symex 5 FAILED FALSE-DEREF 522 main 1 0.58 147896 +array-memsafety/add_last_unsafe valid-memsafety hybrid 5 FAILED FALSE-DEREF 522 main 1 2.53 107980 +array-memsafety/array01-alloca-2 valid-memsafety afl 5 UNKNOWN 0 7.05 108060 +array-memsafety/array01-alloca-2 valid-memsafety symex 5 UNKNOWN 0 31.61 171964 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 5 UNKNOWN 0 40.77 181600 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 5 UNKNOWN 0 2.83 108192 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 5 UNKNOWN 0 0.66 146620 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 5 UNKNOWN 0 40.64 222776 +array-memsafety/lis_unsafe valid-memsafety afl 5 FAILED FALSE-DEREF 521 lis 1 2.55 108076 +array-memsafety/lis_unsafe valid-memsafety symex 5 UNKNOWN 0 0.66 147140 +array-memsafety/lis_unsafe valid-memsafety hybrid 5 FAILED FALSE-DEREF 521 lis 1 2.55 107748 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 5 UNKNOWN 0 11.41 341244 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 5 SUCCEEDED 1 0.51 130376 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 5 SUCCEEDED 1 7.00 341136 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 5 FAILED FALSE-FREE 46 main 1 2.52 108452 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 5 FAILED FALSE-FREE 46 main 1 0.52 130256 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 5 FAILED FALSE-FREE 46 main 1 2.54 107824 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 5 FAILED FALSE-DEREF 42 main 1 2.50 107632 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 5 FAILED FALSE-DEREF 42 main 1 0.50 130920 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 5 FAILED FALSE-DEREF 42 main 1 2.52 107644 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 5 UNKNOWN 0 11.40 107268 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 5 SUCCEEDED 1 7.90 130776 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 5 SUCCEEDED 1 14.54 130672 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 5 UNKNOWN 0 3.40 108684 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 5 UNKNOWN 0 31.49 131068 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 5 UNKNOWN 0 39.97 131060 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 5 FAILED FALSE-MEMTRACK 1 2.72 109740 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 5 FAILED FALSE-MEMTRACK 1 0.94 141556 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 5 FAILED FALSE-MEMTRACK 1 2.82 109452 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 5 UNKNOWN 0 11.57 341004 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 5 SUCCEEDED 1 1.56 143400 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 5 SUCCEEDED 1 8.21 341260 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 5 FAILED FALSE-FREE 779 free_17 1 3.00 110076 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 5 FAILED FALSE-FREE 779 free_17 1 1.93 145172 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 5 FAILED FALSE-FREE 779 free_17 1 3.44 110680 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 5 UNKNOWN 0 11.74 340924 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 5 SUCCEEDED 1 0.89 141336 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 5 SUCCEEDED 1 7.60 341148 +ldv-memsafety/memsetNonZero valid-memsafety afl 5 UNKNOWN 0 11.38 107520 +ldv-memsafety/memsetNonZero valid-memsafety symex 5 SUCCEEDED 1 0.49 131020 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 5 SUCCEEDED 1 6.99 130316 +list-ext-properties/960521-1_1-1 valid-memsafety afl 5 FAILED FALSE-DEREF 533 main 1 2.56 108516 +list-ext-properties/960521-1_1-1 valid-memsafety symex 5 FAILED FALSE-DEREF 533 main 1 0.69 137836 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 533 main 1 2.61 108076 +list-ext-properties/960521-1_1-2 valid-memsafety afl 5 UNKNOWN 0 11.44 341092 +list-ext-properties/960521-1_1-2 valid-memsafety symex 5 SUCCEEDED 1 2.31 138268 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 5 SUCCEEDED 1 9.12 341124 +list-ext-properties/960521-1_1-3 valid-memsafety afl 5 FAILED FALSE-FREE 536 main 1 2.59 107768 +list-ext-properties/960521-1_1-3 valid-memsafety symex 5 FAILED FALSE-FREE 536 main 1 0.69 137572 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 5 FAILED FALSE-FREE 536 main 1 2.52 108020 +list-ext-properties/list-ext unreach-call afl 5 FAILED FALSE 556 main 1 2.26 106140 +list-ext-properties/list-ext unreach-call symex 5 FAILED FALSE 556 main 1 0.60 136608 +list-ext-properties/list-ext unreach-call hybrid 5 FAILED FALSE 556 main 1 2.19 105556 +list-ext-properties/list-ext valid-memsafety afl 5 FAILED FALSE-DEREF 558 main 1 2.72 108076 +list-ext-properties/list-ext valid-memsafety symex 5 FAILED FALSE-DEREF 558 main 1 4.29 139808 +list-ext-properties/list-ext valid-memsafety hybrid 5 FAILED FALSE-DEREF 558 main 1 2.80 108164 +list-ext-properties/simple-ext unreach-call afl 5 FAILED FALSE 540 main 1 2.22 106872 +list-ext-properties/simple-ext unreach-call symex 5 FAILED FALSE 540 main 1 0.55 136196 +list-ext-properties/simple-ext unreach-call hybrid 5 FAILED FALSE 540 main 1 2.23 106140 +list-ext-properties/simple-ext valid-memcleanup afl 5 UNKNOWN 0 11.57 341156 +list-ext-properties/simple-ext valid-memcleanup symex 5 UNKNOWN 0 0.84 136240 +list-ext-properties/simple-ext valid-memcleanup hybrid 5 UNKNOWN 0 36.09 341432 +list-ext-properties/test-0019_1-1 valid-memsafety afl 5 FAILED FALSE-MEMTRACK 1 2.51 107880 +list-ext-properties/test-0019_1-1 valid-memsafety symex 5 FAILED FALSE-MEMTRACK 1 0.55 130952 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 5 FAILED FALSE-MEMTRACK 1 2.60 108092 +list-ext-properties/test-0158_1-1 valid-memsafety afl 5 UNKNOWN 0 11.45 341180 +list-ext-properties/test-0158_1-1 valid-memsafety symex 5 SUCCEEDED 1 0.53 136516 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 5 SUCCEEDED 1 7.03 340944 +map2check-assert/array_bug unreach-call afl 5 FAILED FALSE 7 __VERIFIER_assert 1 2.26 106508 +map2check-assert/array_bug unreach-call symex 5 FAILED FALSE 7 __VERIFIER_assert 1 0.54 136388 +map2check-assert/array_bug unreach-call hybrid 5 FAILED FALSE 7 __VERIFIER_assert 1 2.23 106760 +map2check-assert/byte_add_bug unreach-call afl 5 UNKNOWN 0 11.13 107228 +map2check-assert/byte_add_bug unreach-call symex 5 SUCCEEDED 1 0.50 130836 +map2check-assert/byte_add_bug unreach-call hybrid 5 SUCCEEDED 1 6.74 130420 +map2check-assert/check_if_bug unreach-call afl 5 UNKNOWN 0 2.04 106180 +map2check-assert/check_if_bug unreach-call symex 5 UNKNOWN 0 31.90 188076 +map2check-assert/check_if_bug unreach-call hybrid 5 UNKNOWN 0 37.92 171172 +map2check-assert/ex01_false unreach-call afl 5 FAILED FALSE 5 __VERIFIER_assert 1 2.26 105876 +map2check-assert/ex01_false unreach-call symex 5 FAILED FALSE 5 __VERIFIER_assert 1 1.71 146612 +map2check-assert/ex01_false unreach-call hybrid 5 FAILED FALSE 5 __VERIFIER_assert 1 2.23 105628 +map2check-assert/ex02_false unreach-call afl 5 UNKNOWN 0 2.07 106028 +map2check-assert/ex02_false unreach-call symex 5 UNKNOWN 0 31.54 223248 +map2check-assert/ex02_false unreach-call hybrid 5 UNKNOWN 0 37.31 212300 +memory-alloca/c.03-alloca-2 valid-memsafety afl 5 UNKNOWN 0 11.45 108220 +memory-alloca/c.03-alloca-2 valid-memsafety symex 5 UNKNOWN 0 32.04 172564 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 5 UNKNOWN 0 41.00 181496 +memsafety-ext/dll_extends_pointer valid-memsafety afl 5 UNKNOWN 0 11.38 357384 +memsafety-ext/dll_extends_pointer valid-memsafety symex 5 UNKNOWN 0 31.67 178712 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 5 UNKNOWN 0 40.26 341548 +memsafety-ext/tree_stack valid-memsafety afl 5 UNKNOWN 0 11.38 357756 +memsafety-ext/tree_stack valid-memsafety symex 5 UNKNOWN 0 31.97 233872 +memsafety-ext/tree_stack valid-memsafety hybrid 5 UNKNOWN 0 40.73 342112 +memsafety-ext2/length_test03-1 valid-memsafety afl 5 UNKNOWN 0 11.40 341076 +memsafety-ext2/length_test03-1 valid-memsafety symex 5 UNKNOWN 0 31.51 145692 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 5 UNKNOWN 0 39.14 341376 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 5 FAILED FALSE-DEREF 540 containsHead 1 2.56 109160 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 5 UNKNOWN 0 32.66 388380 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 540 containsHead 1 2.62 108892 +memsafety/960521-1-1 valid-memsafety afl 5 FAILED FALSE-FREE 532 main 1 2.60 107892 +memsafety/960521-1-1 valid-memsafety symex 5 FAILED FALSE-FREE 532 main 1 0.59 130460 +memsafety/960521-1-1 valid-memsafety hybrid 5 FAILED FALSE-FREE 532 main 1 2.54 107920 +memsafety/960521-1-2 valid-memsafety afl 5 UNKNOWN 0 11.40 108284 +memsafety/960521-1-2 valid-memsafety symex 5 SUCCEEDED 1 16.03 130616 +memsafety/960521-1-2 valid-memsafety hybrid 5 UNKNOWN 0 38.76 130228 +memsafety/960521-1-3 valid-memsafety afl 5 FAILED FALSE-DEREF 529 main 1 2.52 107668 +memsafety/960521-1-3 valid-memsafety symex 5 FAILED FALSE-DEREF 529 main 1 0.59 130864 +memsafety/960521-1-3 valid-memsafety hybrid 5 FAILED FALSE-DEREF 529 main 1 2.55 107612 +memsafety/test-0102-1 valid-memsafety afl 5 UNKNOWN 0 11.51 341384 +memsafety/test-0102-1 valid-memsafety symex 5 UNKNOWN 0 31.56 167304 +memsafety/test-0102-1 valid-memsafety hybrid 5 UNKNOWN 0 39.01 341640 diff --git a/docs/cutover/baselines/rust-fa279fe2af8c.tsv b/docs/cutover/baselines/rust-fa279fe2af8c.tsv new file mode 100644 index 0000000..d2ec9f5 --- /dev/null +++ b/docs/cutover/baselines/rust-fa279fe2af8c.tsv @@ -0,0 +1,616 @@ +task prp gen rep verdict tag line function witness wall_s maxrss_kb +array-examples/relax-1 valid-memsafety afl 1 ERROR 0 3.99 107920 +array-examples/relax-1 valid-memsafety symex 1 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 1.04 154932 +array-examples/relax-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 5.08 156348 +array-examples/standard_sentinel-2 unreach-call afl 1 UNKNOWN 0 11.76 106368 +array-examples/standard_sentinel-2 unreach-call symex 1 UNKNOWN 0 31.63 178592 +array-examples/standard_sentinel-2 unreach-call hybrid 1 UNKNOWN 0 39.59 341384 +array-examples/standard_two_index_04 unreach-call afl 1 UNKNOWN 0 11.27 106572 +array-examples/standard_two_index_04 unreach-call symex 1 UNKNOWN 0 31.54 180468 +array-examples/standard_two_index_04 unreach-call hybrid 1 UNKNOWN 0 38.93 341232 +array-examples/standard_two_index_05 unreach-call afl 1 UNKNOWN 0 11.17 106112 +array-examples/standard_two_index_05 unreach-call symex 1 UNKNOWN 0 31.51 180836 +array-examples/standard_two_index_05 unreach-call hybrid 1 UNKNOWN 0 38.60 341624 +array-memsafety/add_last_unsafe valid-memsafety afl 1 FAILED FALSE-DEREF 522 main 1 2.67 108596 +array-memsafety/add_last_unsafe valid-memsafety symex 1 FAILED FALSE-DEREF 522 main 1 0.59 146092 +array-memsafety/add_last_unsafe valid-memsafety hybrid 1 FAILED FALSE-DEREF 522 main 1 2.60 108004 +array-memsafety/array01-alloca-2 valid-memsafety afl 1 UNKNOWN 0 2.70 108076 +array-memsafety/array01-alloca-2 valid-memsafety symex 1 UNKNOWN 0 31.71 171656 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 1 UNKNOWN 0 41.52 308352 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 1 UNKNOWN 0 3.95 108092 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 1 UNKNOWN 0 0.68 146768 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 1 UNKNOWN 0 35.08 341088 +array-memsafety/lis_unsafe valid-memsafety afl 1 FAILED FALSE-DEREF 521 lis 1 2.61 108016 +array-memsafety/lis_unsafe valid-memsafety symex 1 UNKNOWN 0 0.69 147632 +array-memsafety/lis_unsafe valid-memsafety hybrid 1 FAILED FALSE-DEREF 521 lis 1 2.58 108420 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 1 UNKNOWN 0 11.41 108256 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 1 SUCCEEDED 1 0.52 130204 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 1 SUCCEEDED 1 6.99 130212 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 1 FAILED FALSE-FREE 46 main 1 2.55 108588 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 1 FAILED FALSE-FREE 46 main 1 0.51 130288 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 1 FAILED FALSE-FREE 46 main 1 2.59 107940 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 1 FAILED FALSE-DEREF 42 main 1 2.59 108152 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 1 FAILED FALSE-DEREF 42 main 1 0.52 130964 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 1 FAILED FALSE-DEREF 42 main 1 2.55 108176 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 1 UNKNOWN 0 11.42 108488 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 1 SUCCEEDED 1 7.72 130712 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 1 SUCCEEDED 1 14.53 130780 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 1 UNKNOWN 0 3.42 108268 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 1 UNKNOWN 0 31.48 130644 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 1 UNKNOWN 0 40.03 131196 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 1 FAILED FALSE-MEMTRACK 1 2.62 109936 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 1 FAILED FALSE-MEMTRACK 1 0.92 142280 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 1 FAILED FALSE-MEMTRACK 1 2.64 110416 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 1 UNKNOWN 0 11.55 340704 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 1 SUCCEEDED 1 1.60 143060 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 1 SUCCEEDED 1 8.32 340864 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 1 FAILED FALSE-FREE 779 free_17 1 2.82 110028 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 1 FAILED FALSE-FREE 779 free_17 1 1.78 145496 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 1 FAILED FALSE-FREE 779 free_17 1 2.99 110632 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 1 UNKNOWN 0 11.56 340688 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 1 SUCCEEDED 1 0.90 141064 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 1 SUCCEEDED 1 7.67 341236 +ldv-memsafety/memsetNonZero valid-memsafety afl 1 UNKNOWN 0 11.41 108284 +ldv-memsafety/memsetNonZero valid-memsafety symex 1 SUCCEEDED 1 0.51 130264 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 1 SUCCEEDED 1 7.00 341056 +list-ext-properties/960521-1_1-1 valid-memsafety afl 1 FAILED FALSE-DEREF 533 main 1 2.55 107964 +list-ext-properties/960521-1_1-1 valid-memsafety symex 1 FAILED FALSE-DEREF 533 main 1 0.65 137876 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 533 main 1 2.61 107572 +list-ext-properties/960521-1_1-2 valid-memsafety afl 1 UNKNOWN 0 11.45 341252 +list-ext-properties/960521-1_1-2 valid-memsafety symex 1 SUCCEEDED 1 2.33 138104 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 1 SUCCEEDED 1 9.18 341128 +list-ext-properties/960521-1_1-3 valid-memsafety afl 1 FAILED FALSE-FREE 536 main 1 2.53 107796 +list-ext-properties/960521-1_1-3 valid-memsafety symex 1 FAILED FALSE-FREE 536 main 1 0.68 137960 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 1 FAILED FALSE-FREE 536 main 1 2.52 108128 +list-ext-properties/list-ext unreach-call afl 1 FAILED FALSE 556 main 1 2.24 106276 +list-ext-properties/list-ext unreach-call symex 1 FAILED FALSE 556 main 1 0.58 136676 +list-ext-properties/list-ext unreach-call hybrid 1 FAILED FALSE 556 main 1 2.17 105780 +list-ext-properties/list-ext valid-memsafety afl 1 FAILED FALSE-DEREF 558 main 1 2.84 108260 +list-ext-properties/list-ext valid-memsafety symex 1 FAILED FALSE-DEREF 558 main 1 4.10 139992 +list-ext-properties/list-ext valid-memsafety hybrid 1 FAILED FALSE-DEREF 558 main 1 2.91 108592 +list-ext-properties/simple-ext unreach-call afl 1 FAILED FALSE 540 main 1 2.20 106700 +list-ext-properties/simple-ext unreach-call symex 1 FAILED FALSE 540 main 1 0.55 135776 +list-ext-properties/simple-ext unreach-call hybrid 1 FAILED FALSE 540 main 1 2.17 106680 +list-ext-properties/simple-ext valid-memcleanup afl 1 UNKNOWN 0 11.49 341352 +list-ext-properties/simple-ext valid-memcleanup symex 1 UNKNOWN 0 0.84 136160 +list-ext-properties/simple-ext valid-memcleanup hybrid 1 UNKNOWN 0 36.03 341548 +list-ext-properties/test-0019_1-1 valid-memsafety afl 1 FAILED FALSE-MEMTRACK 1 2.54 108384 +list-ext-properties/test-0019_1-1 valid-memsafety symex 1 FAILED FALSE-MEMTRACK 1 0.52 130152 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 1 FAILED FALSE-MEMTRACK 1 2.51 107496 +list-ext-properties/test-0158_1-1 valid-memsafety afl 1 UNKNOWN 0 11.38 341224 +list-ext-properties/test-0158_1-1 valid-memsafety symex 1 SUCCEEDED 1 0.54 137396 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 1 SUCCEEDED 1 7.00 341200 +map2check-assert/array_bug unreach-call afl 1 FAILED FALSE 7 __VERIFIER_assert 1 2.13 105576 +map2check-assert/array_bug unreach-call symex 1 FAILED FALSE 7 __VERIFIER_assert 1 0.51 136328 +map2check-assert/array_bug unreach-call hybrid 1 FAILED FALSE 7 __VERIFIER_assert 1 2.30 106008 +map2check-assert/byte_add_bug unreach-call afl 1 UNKNOWN 0 11.08 106368 +map2check-assert/byte_add_bug unreach-call symex 1 SUCCEEDED 1 0.49 130332 +map2check-assert/byte_add_bug unreach-call hybrid 1 SUCCEEDED 1 6.69 130272 +map2check-assert/check_if_bug unreach-call afl 1 UNKNOWN 0 2.04 106472 +map2check-assert/check_if_bug unreach-call symex 1 UNKNOWN 0 32.00 179972 +map2check-assert/check_if_bug unreach-call hybrid 1 UNKNOWN 0 38.08 171320 +map2check-assert/ex01_false unreach-call afl 1 FAILED FALSE 5 __VERIFIER_assert 1 2.20 106616 +map2check-assert/ex01_false unreach-call symex 1 FAILED FALSE 5 __VERIFIER_assert 1 1.73 146548 +map2check-assert/ex01_false unreach-call hybrid 1 FAILED FALSE 5 __VERIFIER_assert 1 2.22 105860 +map2check-assert/ex02_false unreach-call afl 1 UNKNOWN 0 2.05 105912 +map2check-assert/ex02_false unreach-call symex 1 UNKNOWN 0 31.58 226972 +map2check-assert/ex02_false unreach-call hybrid 1 UNKNOWN 0 37.23 208492 +memory-alloca/c.03-alloca-2 valid-memsafety afl 1 UNKNOWN 0 11.35 107636 +memory-alloca/c.03-alloca-2 valid-memsafety symex 1 UNKNOWN 0 31.99 170680 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 1 UNKNOWN 0 40.35 189304 +memsafety-ext/dll_extends_pointer valid-memsafety afl 1 UNKNOWN 0 11.46 341328 +memsafety-ext/dll_extends_pointer valid-memsafety symex 1 UNKNOWN 0 31.66 178436 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 1 UNKNOWN 0 40.16 341584 +memsafety-ext/tree_stack valid-memsafety afl 1 UNKNOWN 0 11.42 357476 +memsafety-ext/tree_stack valid-memsafety symex 1 UNKNOWN 0 31.98 233212 +memsafety-ext/tree_stack valid-memsafety hybrid 1 UNKNOWN 0 40.45 342600 +memsafety-ext2/length_test03-1 valid-memsafety afl 1 UNKNOWN 0 11.41 340860 +memsafety-ext2/length_test03-1 valid-memsafety symex 1 UNKNOWN 0 31.52 145628 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 1 UNKNOWN 0 39.10 341328 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 1 FAILED FALSE-DEREF 540 containsHead 1 2.52 108668 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 1 UNKNOWN 0 32.73 398900 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 1 FAILED FALSE-DEREF 540 containsHead 1 2.58 108728 +memsafety/960521-1-1 valid-memsafety afl 1 FAILED FALSE-FREE 532 main 1 2.56 108420 +memsafety/960521-1-1 valid-memsafety symex 1 FAILED FALSE-FREE 532 main 1 0.59 130416 +memsafety/960521-1-1 valid-memsafety hybrid 1 FAILED FALSE-FREE 532 main 1 2.52 108484 +memsafety/960521-1-2 valid-memsafety afl 1 UNKNOWN 0 11.36 108100 +memsafety/960521-1-2 valid-memsafety symex 1 SUCCEEDED 1 19.37 129524 +memsafety/960521-1-2 valid-memsafety hybrid 1 UNKNOWN 0 39.34 129828 +memsafety/960521-1-3 valid-memsafety afl 1 FAILED FALSE-DEREF 529 main 1 3.11 107060 +memsafety/960521-1-3 valid-memsafety symex 1 FAILED FALSE-DEREF 529 main 1 0.72 130516 +memsafety/960521-1-3 valid-memsafety hybrid 1 FAILED FALSE-DEREF 529 main 1 3.12 108552 +memsafety/test-0102-1 valid-memsafety afl 1 UNKNOWN 0 12.00 341192 +memsafety/test-0102-1 valid-memsafety symex 1 UNKNOWN 0 31.64 163588 +memsafety/test-0102-1 valid-memsafety hybrid 1 UNKNOWN 0 39.33 151624 +array-examples/relax-1 valid-memsafety afl 2 ERROR 0 2.92 108500 +array-examples/relax-1 valid-memsafety symex 2 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 0.76 155496 +array-examples/relax-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 4.04 154140 +array-examples/standard_sentinel-2 unreach-call afl 2 UNKNOWN 0 11.12 106560 +array-examples/standard_sentinel-2 unreach-call symex 2 UNKNOWN 0 31.50 181364 +array-examples/standard_sentinel-2 unreach-call hybrid 2 UNKNOWN 0 38.77 341244 +array-examples/standard_two_index_04 unreach-call afl 2 UNKNOWN 0 10.99 105204 +array-examples/standard_two_index_04 unreach-call symex 2 UNKNOWN 0 31.51 179228 +array-examples/standard_two_index_04 unreach-call hybrid 2 UNKNOWN 0 38.75 341696 +array-examples/standard_two_index_05 unreach-call afl 2 UNKNOWN 0 11.01 105696 +array-examples/standard_two_index_05 unreach-call symex 2 UNKNOWN 0 31.49 181088 +array-examples/standard_two_index_05 unreach-call hybrid 2 UNKNOWN 0 39.39 341136 +array-memsafety/add_last_unsafe valid-memsafety afl 2 FAILED FALSE-DEREF 522 main 1 2.54 107392 +array-memsafety/add_last_unsafe valid-memsafety symex 2 FAILED FALSE-DEREF 522 main 1 0.57 144444 +array-memsafety/add_last_unsafe valid-memsafety hybrid 2 FAILED FALSE-DEREF 522 main 1 2.51 107680 +array-memsafety/array01-alloca-2 valid-memsafety afl 2 UNKNOWN 0 6.10 106884 +array-memsafety/array01-alloca-2 valid-memsafety symex 2 UNKNOWN 0 31.69 170392 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 2 UNKNOWN 0 40.36 232524 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 2 UNKNOWN 0 2.93 107564 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 2 UNKNOWN 0 0.63 146088 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 2 UNKNOWN 0 35.27 195536 +array-memsafety/lis_unsafe valid-memsafety afl 2 FAILED FALSE-DEREF 521 lis 1 2.51 108144 +array-memsafety/lis_unsafe valid-memsafety symex 2 UNKNOWN 0 0.65 147020 +array-memsafety/lis_unsafe valid-memsafety hybrid 2 FAILED FALSE-DEREF 521 lis 1 2.52 107400 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 2 UNKNOWN 0 11.35 107288 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 2 SUCCEEDED 1 0.50 130044 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 2 SUCCEEDED 1 6.98 129784 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 2 FAILED FALSE-FREE 46 main 1 2.45 107100 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 2 FAILED FALSE-FREE 46 main 1 0.52 129652 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 2 FAILED FALSE-FREE 46 main 1 2.46 108612 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 2 FAILED FALSE-DEREF 42 main 1 2.50 108384 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 2 FAILED FALSE-DEREF 42 main 1 0.50 129388 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 2 FAILED FALSE-DEREF 42 main 1 2.51 108228 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 2 UNKNOWN 0 11.40 108336 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 2 SUCCEEDED 1 7.43 130636 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 2 SUCCEEDED 1 14.47 130284 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 2 UNKNOWN 0 3.36 108756 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 2 UNKNOWN 0 31.47 130576 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 2 UNKNOWN 0 39.70 130884 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 2 FAILED FALSE-MEMTRACK 1 2.56 108688 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 2 FAILED FALSE-MEMTRACK 1 0.87 142236 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 2 FAILED FALSE-MEMTRACK 1 2.57 110272 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 2 UNKNOWN 0 11.51 341244 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 2 SUCCEEDED 1 1.48 143396 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 2 SUCCEEDED 1 8.10 341324 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 2 FAILED FALSE-FREE 779 free_17 1 2.86 110392 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 2 FAILED FALSE-FREE 779 free_17 1 1.88 144872 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 2 FAILED FALSE-FREE 779 free_17 1 3.22 109608 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 2 UNKNOWN 0 11.47 341184 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 2 SUCCEEDED 1 0.88 140940 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 2 SUCCEEDED 1 7.59 341368 +ldv-memsafety/memsetNonZero valid-memsafety afl 2 UNKNOWN 0 11.31 107032 +ldv-memsafety/memsetNonZero valid-memsafety symex 2 SUCCEEDED 1 0.49 130060 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 2 SUCCEEDED 1 6.93 341064 +list-ext-properties/960521-1_1-1 valid-memsafety afl 2 FAILED FALSE-DEREF 533 main 1 2.49 107428 +list-ext-properties/960521-1_1-1 valid-memsafety symex 2 FAILED FALSE-DEREF 533 main 1 0.66 137244 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 533 main 1 2.52 108380 +list-ext-properties/960521-1_1-2 valid-memsafety afl 2 UNKNOWN 0 11.39 341140 +list-ext-properties/960521-1_1-2 valid-memsafety symex 2 SUCCEEDED 1 2.33 137540 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 2 SUCCEEDED 1 9.11 341308 +list-ext-properties/960521-1_1-3 valid-memsafety afl 2 FAILED FALSE-FREE 536 main 1 2.47 108160 +list-ext-properties/960521-1_1-3 valid-memsafety symex 2 FAILED FALSE-FREE 536 main 1 0.64 136924 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 2 FAILED FALSE-FREE 536 main 1 2.50 108200 +list-ext-properties/list-ext unreach-call afl 2 FAILED FALSE 556 main 1 2.18 105308 +list-ext-properties/list-ext unreach-call symex 2 FAILED FALSE 556 main 1 0.59 135712 +list-ext-properties/list-ext unreach-call hybrid 2 FAILED FALSE 556 main 1 2.17 105256 +list-ext-properties/list-ext valid-memsafety afl 2 FAILED FALSE-DEREF 558 main 1 2.65 107864 +list-ext-properties/list-ext valid-memsafety symex 2 FAILED FALSE-DEREF 558 main 1 3.98 139248 +list-ext-properties/list-ext valid-memsafety hybrid 2 FAILED FALSE-DEREF 558 main 1 2.67 108452 +list-ext-properties/simple-ext unreach-call afl 2 FAILED FALSE 540 main 1 2.21 106024 +list-ext-properties/simple-ext unreach-call symex 2 FAILED FALSE 540 main 1 0.56 135468 +list-ext-properties/simple-ext unreach-call hybrid 2 FAILED FALSE 540 main 1 2.15 105568 +list-ext-properties/simple-ext valid-memcleanup afl 2 UNKNOWN 0 11.49 341604 +list-ext-properties/simple-ext valid-memcleanup symex 2 UNKNOWN 0 0.85 136320 +list-ext-properties/simple-ext valid-memcleanup hybrid 2 UNKNOWN 0 36.04 341428 +list-ext-properties/test-0019_1-1 valid-memsafety afl 2 FAILED FALSE-MEMTRACK 1 2.52 107512 +list-ext-properties/test-0019_1-1 valid-memsafety symex 2 FAILED FALSE-MEMTRACK 1 0.51 129968 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 2 FAILED FALSE-MEMTRACK 1 2.49 107984 +list-ext-properties/test-0158_1-1 valid-memsafety afl 2 UNKNOWN 0 11.38 341384 +list-ext-properties/test-0158_1-1 valid-memsafety symex 2 SUCCEEDED 1 0.54 136132 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 2 SUCCEEDED 1 6.97 341344 +map2check-assert/array_bug unreach-call afl 2 FAILED FALSE 7 __VERIFIER_assert 1 2.13 106568 +map2check-assert/array_bug unreach-call symex 2 FAILED FALSE 7 __VERIFIER_assert 1 0.49 136020 +map2check-assert/array_bug unreach-call hybrid 2 FAILED FALSE 7 __VERIFIER_assert 1 2.16 106612 +map2check-assert/byte_add_bug unreach-call afl 2 UNKNOWN 0 11.06 106008 +map2check-assert/byte_add_bug unreach-call symex 2 SUCCEEDED 1 0.47 129588 +map2check-assert/byte_add_bug unreach-call hybrid 2 SUCCEEDED 1 6.67 341064 +map2check-assert/check_if_bug unreach-call afl 2 UNKNOWN 0 2.01 106804 +map2check-assert/check_if_bug unreach-call symex 2 UNKNOWN 0 31.87 187936 +map2check-assert/check_if_bug unreach-call hybrid 2 UNKNOWN 0 37.70 173076 +map2check-assert/ex01_false unreach-call afl 2 FAILED FALSE 5 __VERIFIER_assert 1 2.12 105916 +map2check-assert/ex01_false unreach-call symex 2 FAILED FALSE 5 __VERIFIER_assert 1 1.76 145836 +map2check-assert/ex01_false unreach-call hybrid 2 FAILED FALSE 5 __VERIFIER_assert 1 2.15 105296 +map2check-assert/ex02_false unreach-call afl 2 UNKNOWN 0 2.00 105704 +map2check-assert/ex02_false unreach-call symex 2 UNKNOWN 0 31.56 226732 +map2check-assert/ex02_false unreach-call hybrid 2 UNKNOWN 0 37.18 211188 +memory-alloca/c.03-alloca-2 valid-memsafety afl 2 UNKNOWN 0 11.32 107468 +memory-alloca/c.03-alloca-2 valid-memsafety symex 2 UNKNOWN 0 32.04 172376 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 2 UNKNOWN 0 40.78 164240 +memsafety-ext/dll_extends_pointer valid-memsafety afl 2 UNKNOWN 0 11.31 341080 +memsafety-ext/dll_extends_pointer valid-memsafety symex 2 UNKNOWN 0 31.65 179012 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 2 UNKNOWN 0 40.11 341680 +memsafety-ext/tree_stack valid-memsafety afl 2 UNKNOWN 0 11.36 357648 +memsafety-ext/tree_stack valid-memsafety symex 2 UNKNOWN 0 32.02 236884 +memsafety-ext/tree_stack valid-memsafety hybrid 2 UNKNOWN 0 40.09 343056 +memsafety-ext2/length_test03-1 valid-memsafety afl 2 UNKNOWN 0 11.34 341480 +memsafety-ext2/length_test03-1 valid-memsafety symex 2 UNKNOWN 0 31.49 145392 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 2 UNKNOWN 0 39.07 341628 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 2 FAILED FALSE-DEREF 540 containsHead 1 2.51 109308 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 2 UNKNOWN 0 32.74 411844 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 2 FAILED FALSE-DEREF 540 containsHead 1 2.52 108004 +memsafety/960521-1-1 valid-memsafety afl 2 FAILED FALSE-FREE 532 main 1 2.51 107676 +memsafety/960521-1-1 valid-memsafety symex 2 FAILED FALSE-FREE 532 main 1 0.59 130692 +memsafety/960521-1-1 valid-memsafety hybrid 2 FAILED FALSE-FREE 532 main 1 2.48 107312 +memsafety/960521-1-2 valid-memsafety afl 2 UNKNOWN 0 11.34 106820 +memsafety/960521-1-2 valid-memsafety symex 2 SUCCEEDED 1 15.77 129604 +memsafety/960521-1-2 valid-memsafety hybrid 2 UNKNOWN 0 38.71 129624 +memsafety/960521-1-3 valid-memsafety afl 2 FAILED FALSE-DEREF 529 main 1 2.49 107416 +memsafety/960521-1-3 valid-memsafety symex 2 FAILED FALSE-DEREF 529 main 1 0.57 130416 +memsafety/960521-1-3 valid-memsafety hybrid 2 FAILED FALSE-DEREF 529 main 1 2.51 107952 +memsafety/test-0102-1 valid-memsafety afl 2 UNKNOWN 0 11.38 108100 +memsafety/test-0102-1 valid-memsafety symex 2 UNKNOWN 0 31.55 166684 +memsafety/test-0102-1 valid-memsafety hybrid 2 UNKNOWN 0 40.00 341624 +array-examples/relax-1 valid-memsafety afl 3 ERROR 0 2.66 108696 +array-examples/relax-1 valid-memsafety symex 3 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 0.71 154800 +array-examples/relax-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 4.31 155320 +array-examples/standard_sentinel-2 unreach-call afl 3 UNKNOWN 0 11.00 106008 +array-examples/standard_sentinel-2 unreach-call symex 3 UNKNOWN 0 31.50 181104 +array-examples/standard_sentinel-2 unreach-call hybrid 3 UNKNOWN 0 39.46 341076 +array-examples/standard_two_index_04 unreach-call afl 3 UNKNOWN 0 10.98 106300 +array-examples/standard_two_index_04 unreach-call symex 3 UNKNOWN 0 31.49 180828 +array-examples/standard_two_index_04 unreach-call hybrid 3 UNKNOWN 0 38.88 341544 +array-examples/standard_two_index_05 unreach-call afl 3 UNKNOWN 0 10.98 106264 +array-examples/standard_two_index_05 unreach-call symex 3 UNKNOWN 0 31.49 180152 +array-examples/standard_two_index_05 unreach-call hybrid 3 UNKNOWN 0 39.44 341588 +array-memsafety/add_last_unsafe valid-memsafety afl 3 FAILED FALSE-DEREF 522 main 1 2.45 107700 +array-memsafety/add_last_unsafe valid-memsafety symex 3 FAILED FALSE-DEREF 522 main 1 0.57 146188 +array-memsafety/add_last_unsafe valid-memsafety hybrid 3 FAILED FALSE-DEREF 522 main 1 2.48 107672 +array-memsafety/array01-alloca-2 valid-memsafety afl 3 UNKNOWN 0 2.68 108208 +array-memsafety/array01-alloca-2 valid-memsafety symex 3 UNKNOWN 0 31.64 171624 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 3 UNKNOWN 0 41.64 183780 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 3 UNKNOWN 0 2.88 108064 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 3 UNKNOWN 0 0.64 145980 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 3 UNKNOWN 0 37.43 151840 +array-memsafety/lis_unsafe valid-memsafety afl 3 FAILED FALSE-DEREF 521 lis 1 2.46 108216 +array-memsafety/lis_unsafe valid-memsafety symex 3 UNKNOWN 0 0.67 147068 +array-memsafety/lis_unsafe valid-memsafety hybrid 3 FAILED FALSE-DEREF 521 lis 1 2.50 108416 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 3 UNKNOWN 0 11.33 108488 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 3 SUCCEEDED 1 0.49 130556 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 3 SUCCEEDED 1 6.93 341312 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 3 FAILED FALSE-FREE 46 main 1 2.47 107652 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 3 FAILED FALSE-FREE 46 main 1 0.51 130252 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 3 FAILED FALSE-FREE 46 main 1 2.50 107380 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 3 FAILED FALSE-DEREF 42 main 1 2.47 107780 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 3 FAILED FALSE-DEREF 42 main 1 0.50 130440 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 3 FAILED FALSE-DEREF 42 main 1 2.48 108432 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 3 UNKNOWN 0 11.33 108172 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 3 SUCCEEDED 1 7.22 130836 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 3 SUCCEEDED 1 13.93 130428 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 3 UNKNOWN 0 3.35 108172 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 3 UNKNOWN 0 31.48 130016 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 3 UNKNOWN 0 39.72 131264 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 3 FAILED FALSE-MEMTRACK 1 2.57 109420 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 3 FAILED FALSE-MEMTRACK 1 0.91 141804 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 3 FAILED FALSE-MEMTRACK 1 2.58 110156 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 3 UNKNOWN 0 11.48 341344 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 3 SUCCEEDED 1 1.47 142920 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 3 SUCCEEDED 1 8.13 341028 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 3 FAILED FALSE-FREE 779 free_17 1 2.75 110956 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 3 FAILED FALSE-FREE 779 free_17 1 1.80 145108 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 3 FAILED FALSE-FREE 779 free_17 1 2.75 110088 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 3 UNKNOWN 0 11.50 341080 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 3 SUCCEEDED 1 0.94 141464 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 3 SUCCEEDED 1 7.59 341144 +ldv-memsafety/memsetNonZero valid-memsafety afl 3 UNKNOWN 0 11.31 341072 +ldv-memsafety/memsetNonZero valid-memsafety symex 3 SUCCEEDED 1 0.48 130736 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 3 SUCCEEDED 1 6.94 341564 +list-ext-properties/960521-1_1-1 valid-memsafety afl 3 FAILED FALSE-DEREF 533 main 1 2.51 107912 +list-ext-properties/960521-1_1-1 valid-memsafety symex 3 FAILED FALSE-DEREF 533 main 1 0.63 138088 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 533 main 1 2.50 108712 +list-ext-properties/960521-1_1-2 valid-memsafety afl 3 UNKNOWN 0 11.38 341272 +list-ext-properties/960521-1_1-2 valid-memsafety symex 3 SUCCEEDED 1 2.34 138308 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 3 SUCCEEDED 1 9.10 341048 +list-ext-properties/960521-1_1-3 valid-memsafety afl 3 FAILED FALSE-FREE 536 main 1 2.44 107928 +list-ext-properties/960521-1_1-3 valid-memsafety symex 3 FAILED FALSE-FREE 536 main 1 0.66 138032 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 3 FAILED FALSE-FREE 536 main 1 2.52 107852 +list-ext-properties/list-ext unreach-call afl 3 FAILED FALSE 556 main 1 2.17 105964 +list-ext-properties/list-ext unreach-call symex 3 FAILED FALSE 556 main 1 0.56 136524 +list-ext-properties/list-ext unreach-call hybrid 3 FAILED FALSE 556 main 1 2.20 106428 +list-ext-properties/list-ext valid-memsafety afl 3 FAILED FALSE-DEREF 558 main 1 2.63 108916 +list-ext-properties/list-ext valid-memsafety symex 3 FAILED FALSE-DEREF 558 main 1 3.92 139572 +list-ext-properties/list-ext valid-memsafety hybrid 3 FAILED FALSE-DEREF 558 main 1 2.64 108380 +list-ext-properties/simple-ext unreach-call afl 3 FAILED FALSE 540 main 1 2.17 106940 +list-ext-properties/simple-ext unreach-call symex 3 FAILED FALSE 540 main 1 0.55 135536 +list-ext-properties/simple-ext unreach-call hybrid 3 FAILED FALSE 540 main 1 2.18 105796 +list-ext-properties/simple-ext valid-memcleanup afl 3 UNKNOWN 0 11.50 340760 +list-ext-properties/simple-ext valid-memcleanup symex 3 UNKNOWN 0 0.81 136348 +list-ext-properties/simple-ext valid-memcleanup hybrid 3 UNKNOWN 0 36.03 341312 +list-ext-properties/test-0019_1-1 valid-memsafety afl 3 FAILED FALSE-MEMTRACK 1 2.47 108592 +list-ext-properties/test-0019_1-1 valid-memsafety symex 3 FAILED FALSE-MEMTRACK 1 0.51 130792 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 3 FAILED FALSE-MEMTRACK 1 2.48 108716 +list-ext-properties/test-0158_1-1 valid-memsafety afl 3 UNKNOWN 0 11.36 341260 +list-ext-properties/test-0158_1-1 valid-memsafety symex 3 SUCCEEDED 1 0.53 136196 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 3 SUCCEEDED 1 6.96 341264 +map2check-assert/array_bug unreach-call afl 3 FAILED FALSE 7 __VERIFIER_assert 1 2.13 106252 +map2check-assert/array_bug unreach-call symex 3 FAILED FALSE 7 __VERIFIER_assert 1 0.49 136308 +map2check-assert/array_bug unreach-call hybrid 3 FAILED FALSE 7 __VERIFIER_assert 1 2.17 106364 +map2check-assert/byte_add_bug unreach-call afl 3 UNKNOWN 0 11.06 340932 +map2check-assert/byte_add_bug unreach-call symex 3 SUCCEEDED 1 0.47 130172 +map2check-assert/byte_add_bug unreach-call hybrid 3 SUCCEEDED 1 6.64 130656 +map2check-assert/check_if_bug unreach-call afl 3 UNKNOWN 0 2.01 106808 +map2check-assert/check_if_bug unreach-call symex 3 UNKNOWN 0 31.76 187992 +map2check-assert/check_if_bug unreach-call hybrid 3 UNKNOWN 0 37.70 171156 +map2check-assert/ex01_false unreach-call afl 3 FAILED FALSE 5 __VERIFIER_assert 1 2.14 106824 +map2check-assert/ex01_false unreach-call symex 3 FAILED FALSE 5 __VERIFIER_assert 1 1.77 146708 +map2check-assert/ex01_false unreach-call hybrid 3 FAILED FALSE 5 __VERIFIER_assert 1 2.15 106444 +map2check-assert/ex02_false unreach-call afl 3 UNKNOWN 0 1.99 106852 +map2check-assert/ex02_false unreach-call symex 3 UNKNOWN 0 31.52 227732 +map2check-assert/ex02_false unreach-call hybrid 3 UNKNOWN 0 37.17 211544 +memory-alloca/c.03-alloca-2 valid-memsafety afl 3 UNKNOWN 0 11.32 108872 +memory-alloca/c.03-alloca-2 valid-memsafety symex 3 UNKNOWN 0 31.97 172944 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 3 UNKNOWN 0 41.35 184232 +memsafety-ext/dll_extends_pointer valid-memsafety afl 3 UNKNOWN 0 11.33 341436 +memsafety-ext/dll_extends_pointer valid-memsafety symex 3 UNKNOWN 0 31.64 178492 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 3 UNKNOWN 0 40.13 357728 +memsafety-ext/tree_stack valid-memsafety afl 3 UNKNOWN 0 11.38 341348 +memsafety-ext/tree_stack valid-memsafety symex 3 UNKNOWN 0 31.98 238312 +memsafety-ext/tree_stack valid-memsafety hybrid 3 UNKNOWN 0 40.20 342088 +memsafety-ext2/length_test03-1 valid-memsafety afl 3 UNKNOWN 0 11.35 341376 +memsafety-ext2/length_test03-1 valid-memsafety symex 3 UNKNOWN 0 31.50 145764 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 3 UNKNOWN 0 39.06 341324 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 3 FAILED FALSE-DEREF 540 containsHead 1 2.50 109092 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 3 UNKNOWN 0 32.74 409352 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 3 FAILED FALSE-DEREF 540 containsHead 1 2.52 108484 +memsafety/960521-1-1 valid-memsafety afl 3 FAILED FALSE-FREE 532 main 1 2.49 107900 +memsafety/960521-1-1 valid-memsafety symex 3 FAILED FALSE-FREE 532 main 1 0.59 129884 +memsafety/960521-1-1 valid-memsafety hybrid 3 FAILED FALSE-FREE 532 main 1 2.49 108608 +memsafety/960521-1-2 valid-memsafety afl 3 UNKNOWN 0 11.34 108280 +memsafety/960521-1-2 valid-memsafety symex 3 SUCCEEDED 1 15.62 129256 +memsafety/960521-1-2 valid-memsafety hybrid 3 UNKNOWN 0 38.71 130644 +memsafety/960521-1-3 valid-memsafety afl 3 FAILED FALSE-DEREF 529 main 1 2.45 108256 +memsafety/960521-1-3 valid-memsafety symex 3 FAILED FALSE-DEREF 529 main 1 0.59 130764 +memsafety/960521-1-3 valid-memsafety hybrid 3 FAILED FALSE-DEREF 529 main 1 2.46 107884 +memsafety/test-0102-1 valid-memsafety afl 3 UNKNOWN 0 11.38 341344 +memsafety/test-0102-1 valid-memsafety symex 3 UNKNOWN 0 31.55 166588 +memsafety/test-0102-1 valid-memsafety hybrid 3 UNKNOWN 0 38.97 341844 +array-examples/relax-1 valid-memsafety afl 4 ERROR 0 2.70 108856 +array-examples/relax-1 valid-memsafety symex 4 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 0.70 155464 +array-examples/relax-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 3.83 155668 +array-examples/standard_sentinel-2 unreach-call afl 4 UNKNOWN 0 10.99 105540 +array-examples/standard_sentinel-2 unreach-call symex 4 UNKNOWN 0 31.48 181216 +array-examples/standard_sentinel-2 unreach-call hybrid 4 UNKNOWN 0 38.86 341440 +array-examples/standard_two_index_04 unreach-call afl 4 UNKNOWN 0 10.98 105864 +array-examples/standard_two_index_04 unreach-call symex 4 UNKNOWN 0 31.49 180620 +array-examples/standard_two_index_04 unreach-call hybrid 4 UNKNOWN 0 38.62 341412 +array-examples/standard_two_index_05 unreach-call afl 4 UNKNOWN 0 10.97 105520 +array-examples/standard_two_index_05 unreach-call symex 4 UNKNOWN 0 31.51 180360 +array-examples/standard_two_index_05 unreach-call hybrid 4 UNKNOWN 0 38.91 341412 +array-memsafety/add_last_unsafe valid-memsafety afl 4 FAILED FALSE-DEREF 522 main 1 2.46 108084 +array-memsafety/add_last_unsafe valid-memsafety symex 4 FAILED FALSE-DEREF 522 main 1 0.56 145260 +array-memsafety/add_last_unsafe valid-memsafety hybrid 4 FAILED FALSE-DEREF 522 main 1 2.49 107628 +array-memsafety/array01-alloca-2 valid-memsafety afl 4 UNKNOWN 0 3.77 107820 +array-memsafety/array01-alloca-2 valid-memsafety symex 4 UNKNOWN 0 31.71 171268 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 4 UNKNOWN 0 29.95 264612 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 4 UNKNOWN 0 3.16 108388 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 4 UNKNOWN 0 0.66 146444 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 4 UNKNOWN 0 40.32 222720 +array-memsafety/lis_unsafe valid-memsafety afl 4 FAILED FALSE-DEREF 521 lis 1 2.48 107712 +array-memsafety/lis_unsafe valid-memsafety symex 4 UNKNOWN 0 0.65 147044 +array-memsafety/lis_unsafe valid-memsafety hybrid 4 FAILED FALSE-DEREF 521 lis 1 2.48 108168 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 4 UNKNOWN 0 11.35 341296 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 4 SUCCEEDED 1 0.49 129924 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 4 SUCCEEDED 1 6.92 129920 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 4 FAILED FALSE-FREE 46 main 1 2.46 108288 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 4 FAILED FALSE-FREE 46 main 1 0.49 130424 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 4 FAILED FALSE-FREE 46 main 1 2.47 108316 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 4 FAILED FALSE-DEREF 42 main 1 2.50 107564 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 4 FAILED FALSE-DEREF 42 main 1 0.50 129644 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 4 FAILED FALSE-DEREF 42 main 1 2.47 108064 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 4 UNKNOWN 0 11.35 107792 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 4 SUCCEEDED 1 7.26 129732 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 4 SUCCEEDED 1 13.95 130568 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 4 UNKNOWN 0 3.35 107896 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 4 UNKNOWN 0 31.48 130832 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 4 UNKNOWN 0 39.72 132780 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 4 FAILED FALSE-MEMTRACK 1 2.59 109460 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 4 FAILED FALSE-MEMTRACK 1 0.92 141760 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 4 FAILED FALSE-MEMTRACK 1 2.59 110144 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 4 UNKNOWN 0 11.49 341016 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 4 SUCCEEDED 1 1.55 142784 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 4 SUCCEEDED 1 8.12 341256 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 4 FAILED FALSE-FREE 779 free_17 1 2.78 109892 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 4 FAILED FALSE-FREE 779 free_17 1 1.85 144336 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 4 FAILED FALSE-FREE 779 free_17 1 2.75 109544 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 4 UNKNOWN 0 11.48 340828 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 4 SUCCEEDED 1 0.93 141164 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 4 SUCCEEDED 1 7.59 341028 +ldv-memsafety/memsetNonZero valid-memsafety afl 4 UNKNOWN 0 11.33 341184 +ldv-memsafety/memsetNonZero valid-memsafety symex 4 SUCCEEDED 1 0.50 130424 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 4 SUCCEEDED 1 6.92 130164 +list-ext-properties/960521-1_1-1 valid-memsafety afl 4 FAILED FALSE-DEREF 533 main 1 2.46 108136 +list-ext-properties/960521-1_1-1 valid-memsafety symex 4 FAILED FALSE-DEREF 533 main 1 0.63 137292 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 533 main 1 2.48 107724 +list-ext-properties/960521-1_1-2 valid-memsafety afl 4 UNKNOWN 0 11.39 341340 +list-ext-properties/960521-1_1-2 valid-memsafety symex 4 SUCCEEDED 1 2.34 138612 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 4 SUCCEEDED 1 9.08 341400 +list-ext-properties/960521-1_1-3 valid-memsafety afl 4 FAILED FALSE-FREE 536 main 1 2.47 108564 +list-ext-properties/960521-1_1-3 valid-memsafety symex 4 FAILED FALSE-FREE 536 main 1 0.65 137536 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 4 FAILED FALSE-FREE 536 main 1 2.49 108308 +list-ext-properties/list-ext unreach-call afl 4 FAILED FALSE 556 main 1 2.17 106428 +list-ext-properties/list-ext unreach-call symex 4 FAILED FALSE 556 main 1 0.56 136404 +list-ext-properties/list-ext unreach-call hybrid 4 FAILED FALSE 556 main 1 2.17 106532 +list-ext-properties/list-ext valid-memsafety afl 4 FAILED FALSE-DEREF 558 main 1 2.64 107704 +list-ext-properties/list-ext valid-memsafety symex 4 FAILED FALSE-DEREF 558 main 1 4.21 139580 +list-ext-properties/list-ext valid-memsafety hybrid 4 FAILED FALSE-DEREF 558 main 1 2.64 108460 +list-ext-properties/simple-ext unreach-call afl 4 FAILED FALSE 540 main 1 2.17 106044 +list-ext-properties/simple-ext unreach-call symex 4 FAILED FALSE 540 main 1 0.53 135844 +list-ext-properties/simple-ext unreach-call hybrid 4 FAILED FALSE 540 main 1 2.15 106144 +list-ext-properties/simple-ext valid-memcleanup afl 4 UNKNOWN 0 11.48 341144 +list-ext-properties/simple-ext valid-memcleanup symex 4 UNKNOWN 0 0.84 136536 +list-ext-properties/simple-ext valid-memcleanup hybrid 4 UNKNOWN 0 36.01 341564 +list-ext-properties/test-0019_1-1 valid-memsafety afl 4 FAILED FALSE-MEMTRACK 1 2.47 107456 +list-ext-properties/test-0019_1-1 valid-memsafety symex 4 FAILED FALSE-MEMTRACK 1 0.51 130516 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 4 FAILED FALSE-MEMTRACK 1 2.51 107540 +list-ext-properties/test-0158_1-1 valid-memsafety afl 4 UNKNOWN 0 11.33 341264 +list-ext-properties/test-0158_1-1 valid-memsafety symex 4 SUCCEEDED 1 0.53 136832 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 4 SUCCEEDED 1 6.97 341020 +map2check-assert/array_bug unreach-call afl 4 FAILED FALSE 7 __VERIFIER_assert 1 2.16 105896 +map2check-assert/array_bug unreach-call symex 4 FAILED FALSE 7 __VERIFIER_assert 1 0.51 135996 +map2check-assert/array_bug unreach-call hybrid 4 FAILED FALSE 7 __VERIFIER_assert 1 2.15 105828 +map2check-assert/byte_add_bug unreach-call afl 4 UNKNOWN 0 11.07 106900 +map2check-assert/byte_add_bug unreach-call symex 4 SUCCEEDED 1 0.48 130260 +map2check-assert/byte_add_bug unreach-call hybrid 4 SUCCEEDED 1 6.63 130496 +map2check-assert/check_if_bug unreach-call afl 4 UNKNOWN 0 2.01 106280 +map2check-assert/check_if_bug unreach-call symex 4 UNKNOWN 0 31.89 187400 +map2check-assert/check_if_bug unreach-call hybrid 4 UNKNOWN 0 37.73 171176 +map2check-assert/ex01_false unreach-call afl 4 FAILED FALSE 5 __VERIFIER_assert 1 2.14 105792 +map2check-assert/ex01_false unreach-call symex 4 FAILED FALSE 5 __VERIFIER_assert 1 1.77 146260 +map2check-assert/ex01_false unreach-call hybrid 4 FAILED FALSE 5 __VERIFIER_assert 1 2.13 105348 +map2check-assert/ex02_false unreach-call afl 4 UNKNOWN 0 1.98 106120 +map2check-assert/ex02_false unreach-call symex 4 UNKNOWN 0 31.52 227324 +map2check-assert/ex02_false unreach-call hybrid 4 UNKNOWN 0 37.18 210084 +memory-alloca/c.03-alloca-2 valid-memsafety afl 4 UNKNOWN 0 11.31 107532 +memory-alloca/c.03-alloca-2 valid-memsafety symex 4 UNKNOWN 0 31.94 172728 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 4 UNKNOWN 0 40.92 180828 +memsafety-ext/dll_extends_pointer valid-memsafety afl 4 UNKNOWN 0 11.34 341240 +memsafety-ext/dll_extends_pointer valid-memsafety symex 4 UNKNOWN 0 31.65 178548 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 4 UNKNOWN 0 40.13 357548 +memsafety-ext/tree_stack valid-memsafety afl 4 UNKNOWN 0 11.36 341400 +memsafety-ext/tree_stack valid-memsafety symex 4 UNKNOWN 0 32.01 238560 +memsafety-ext/tree_stack valid-memsafety hybrid 4 UNKNOWN 0 40.27 357944 +memsafety-ext2/length_test03-1 valid-memsafety afl 4 UNKNOWN 0 11.36 341012 +memsafety-ext2/length_test03-1 valid-memsafety symex 4 UNKNOWN 0 31.50 145616 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 4 UNKNOWN 0 39.10 341636 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 4 FAILED FALSE-DEREF 540 containsHead 1 2.51 108680 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 4 UNKNOWN 0 32.68 409592 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 4 FAILED FALSE-DEREF 540 containsHead 1 2.52 108544 +memsafety/960521-1-1 valid-memsafety afl 4 FAILED FALSE-FREE 532 main 1 2.48 107612 +memsafety/960521-1-1 valid-memsafety symex 4 FAILED FALSE-FREE 532 main 1 0.58 129680 +memsafety/960521-1-1 valid-memsafety hybrid 4 FAILED FALSE-FREE 532 main 1 2.50 107864 +memsafety/960521-1-2 valid-memsafety afl 4 UNKNOWN 0 11.36 107692 +memsafety/960521-1-2 valid-memsafety symex 4 SUCCEEDED 1 15.75 129964 +memsafety/960521-1-2 valid-memsafety hybrid 4 UNKNOWN 0 38.69 130492 +memsafety/960521-1-3 valid-memsafety afl 4 FAILED FALSE-DEREF 529 main 1 2.47 108572 +memsafety/960521-1-3 valid-memsafety symex 4 FAILED FALSE-DEREF 529 main 1 0.57 130600 +memsafety/960521-1-3 valid-memsafety hybrid 4 FAILED FALSE-DEREF 529 main 1 2.48 108768 +memsafety/test-0102-1 valid-memsafety afl 4 UNKNOWN 0 11.38 341400 +memsafety/test-0102-1 valid-memsafety symex 4 UNKNOWN 0 31.55 167548 +memsafety/test-0102-1 valid-memsafety hybrid 4 UNKNOWN 0 39.00 341156 +array-examples/relax-1 valid-memsafety afl 5 ERROR 0 2.66 108200 +array-examples/relax-1 valid-memsafety symex 5 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 0.70 155352 +array-examples/relax-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 570 is_relaxed_prefix 1 3.77 155708 +array-examples/standard_sentinel-2 unreach-call afl 5 UNKNOWN 0 11.01 105800 +array-examples/standard_sentinel-2 unreach-call symex 5 UNKNOWN 0 31.50 183248 +array-examples/standard_sentinel-2 unreach-call hybrid 5 UNKNOWN 0 38.85 341700 +array-examples/standard_two_index_04 unreach-call afl 5 UNKNOWN 0 10.97 105848 +array-examples/standard_two_index_04 unreach-call symex 5 UNKNOWN 0 31.50 180492 +array-examples/standard_two_index_04 unreach-call hybrid 5 UNKNOWN 0 38.88 341468 +array-examples/standard_two_index_05 unreach-call afl 5 UNKNOWN 0 10.97 106032 +array-examples/standard_two_index_05 unreach-call symex 5 UNKNOWN 0 31.51 180236 +array-examples/standard_two_index_05 unreach-call hybrid 5 UNKNOWN 0 38.89 341272 +array-memsafety/add_last_unsafe valid-memsafety afl 5 FAILED FALSE-DEREF 522 main 1 2.46 108352 +array-memsafety/add_last_unsafe valid-memsafety symex 5 FAILED FALSE-DEREF 522 main 1 0.54 146120 +array-memsafety/add_last_unsafe valid-memsafety hybrid 5 FAILED FALSE-DEREF 522 main 1 2.46 107824 +array-memsafety/array01-alloca-2 valid-memsafety afl 5 UNKNOWN 0 2.66 108272 +array-memsafety/array01-alloca-2 valid-memsafety symex 5 UNKNOWN 0 31.65 173708 +array-memsafety/array01-alloca-2 valid-memsafety hybrid 5 UNKNOWN 0 40.21 320840 +array-memsafety/insertionsort-alloca-1 valid-memsafety afl 5 UNKNOWN 0 2.74 107512 +array-memsafety/insertionsort-alloca-1 valid-memsafety symex 5 UNKNOWN 0 0.65 146400 +array-memsafety/insertionsort-alloca-1 valid-memsafety hybrid 5 UNKNOWN 0 37.80 340948 +array-memsafety/lis_unsafe valid-memsafety afl 5 FAILED FALSE-DEREF 521 lis 1 2.47 108776 +array-memsafety/lis_unsafe valid-memsafety symex 5 UNKNOWN 0 0.66 146912 +array-memsafety/lis_unsafe valid-memsafety hybrid 5 FAILED FALSE-DEREF 521 lis 1 2.51 108800 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety afl 5 UNKNOWN 0 11.35 341244 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety symex 5 SUCCEEDED 1 0.50 130736 +ldv-memsafety-bitfields/test-bitfields-1-1 valid-memsafety hybrid 5 SUCCEEDED 1 6.91 129944 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety afl 5 FAILED FALSE-FREE 46 main 1 2.45 107948 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety symex 5 FAILED FALSE-FREE 46 main 1 0.51 130368 +ldv-memsafety-bitfields/test-bitfields-2.1-2 valid-memsafety hybrid 5 FAILED FALSE-FREE 46 main 1 2.49 107752 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety afl 5 FAILED FALSE-DEREF 42 main 1 2.49 108564 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety symex 5 FAILED FALSE-DEREF 42 main 1 0.51 130100 +ldv-memsafety-bitfields/test-bitfields-3.1-2 valid-memsafety hybrid 5 FAILED FALSE-DEREF 42 main 1 2.52 108384 +ldv-memsafety/ArraysOfVariableLength valid-memsafety afl 5 UNKNOWN 0 11.35 108764 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex 5 SUCCEEDED 1 7.51 130128 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid 5 SUCCEEDED 1 13.95 130732 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety afl 5 UNKNOWN 0 3.34 107920 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety symex 5 UNKNOWN 0 31.49 130516 +ldv-memsafety/ArraysOfVariableLength2 valid-memsafety hybrid 5 UNKNOWN 0 39.73 130940 +ldv-memsafety/memleaks_test1-1 valid-memsafety afl 5 FAILED FALSE-MEMTRACK 1 2.58 109460 +ldv-memsafety/memleaks_test1-1 valid-memsafety symex 5 FAILED FALSE-MEMTRACK 1 0.88 141632 +ldv-memsafety/memleaks_test1-1 valid-memsafety hybrid 5 FAILED FALSE-MEMTRACK 1 2.59 109976 +ldv-memsafety/memleaks_test10-2 valid-memsafety afl 5 UNKNOWN 0 11.50 341352 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex 5 SUCCEEDED 1 1.47 143592 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid 5 SUCCEEDED 1 8.12 341168 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety afl 5 FAILED FALSE-FREE 779 free_17 1 3.81 109848 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety symex 5 FAILED FALSE-FREE 779 free_17 1 1.85 144824 +ldv-memsafety/memleaks_test17_2-1 valid-memsafety hybrid 5 FAILED FALSE-FREE 779 free_17 1 2.78 110012 +ldv-memsafety/memleaks_test3-2 valid-memsafety afl 5 UNKNOWN 0 11.46 341168 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex 5 SUCCEEDED 1 0.91 141932 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid 5 SUCCEEDED 1 7.59 341448 +ldv-memsafety/memsetNonZero valid-memsafety afl 5 UNKNOWN 0 11.30 107636 +ldv-memsafety/memsetNonZero valid-memsafety symex 5 SUCCEEDED 1 0.48 130048 +ldv-memsafety/memsetNonZero valid-memsafety hybrid 5 SUCCEEDED 1 6.92 129932 +list-ext-properties/960521-1_1-1 valid-memsafety afl 5 FAILED FALSE-DEREF 533 main 1 2.49 107584 +list-ext-properties/960521-1_1-1 valid-memsafety symex 5 FAILED FALSE-DEREF 533 main 1 0.65 137712 +list-ext-properties/960521-1_1-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 533 main 1 2.48 107572 +list-ext-properties/960521-1_1-2 valid-memsafety afl 5 UNKNOWN 0 11.35 341156 +list-ext-properties/960521-1_1-2 valid-memsafety symex 5 SUCCEEDED 1 2.32 137884 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid 5 SUCCEEDED 1 9.07 341112 +list-ext-properties/960521-1_1-3 valid-memsafety afl 5 FAILED FALSE-FREE 536 main 1 2.48 107212 +list-ext-properties/960521-1_1-3 valid-memsafety symex 5 FAILED FALSE-FREE 536 main 1 0.65 137860 +list-ext-properties/960521-1_1-3 valid-memsafety hybrid 5 FAILED FALSE-FREE 536 main 1 2.47 108536 +list-ext-properties/list-ext unreach-call afl 5 FAILED FALSE 556 main 1 2.15 106688 +list-ext-properties/list-ext unreach-call symex 5 FAILED FALSE 556 main 1 0.57 136360 +list-ext-properties/list-ext unreach-call hybrid 5 FAILED FALSE 556 main 1 2.17 105788 +list-ext-properties/list-ext valid-memsafety afl 5 FAILED FALSE-DEREF 558 main 1 2.66 107436 +list-ext-properties/list-ext valid-memsafety symex 5 FAILED FALSE-DEREF 558 main 1 4.17 139764 +list-ext-properties/list-ext valid-memsafety hybrid 5 FAILED FALSE-DEREF 558 main 1 2.63 107864 +list-ext-properties/simple-ext unreach-call afl 5 FAILED FALSE 540 main 1 2.16 106412 +list-ext-properties/simple-ext unreach-call symex 5 FAILED FALSE 540 main 1 0.52 135468 +list-ext-properties/simple-ext unreach-call hybrid 5 FAILED FALSE 540 main 1 2.15 105804 +list-ext-properties/simple-ext valid-memcleanup afl 5 UNKNOWN 0 11.47 341112 +list-ext-properties/simple-ext valid-memcleanup symex 5 UNKNOWN 0 0.82 136240 +list-ext-properties/simple-ext valid-memcleanup hybrid 5 UNKNOWN 0 36.02 341416 +list-ext-properties/test-0019_1-1 valid-memsafety afl 5 FAILED FALSE-MEMTRACK 1 2.46 107664 +list-ext-properties/test-0019_1-1 valid-memsafety symex 5 FAILED FALSE-MEMTRACK 1 0.52 130156 +list-ext-properties/test-0019_1-1 valid-memsafety hybrid 5 FAILED FALSE-MEMTRACK 1 2.49 107888 +list-ext-properties/test-0158_1-1 valid-memsafety afl 5 UNKNOWN 0 11.34 341132 +list-ext-properties/test-0158_1-1 valid-memsafety symex 5 SUCCEEDED 1 0.53 136632 +list-ext-properties/test-0158_1-1 valid-memsafety hybrid 5 SUCCEEDED 1 6.96 341136 +map2check-assert/array_bug unreach-call afl 5 FAILED FALSE 7 __VERIFIER_assert 1 2.16 105528 +map2check-assert/array_bug unreach-call symex 5 FAILED FALSE 7 __VERIFIER_assert 1 0.51 136496 +map2check-assert/array_bug unreach-call hybrid 5 FAILED FALSE 7 __VERIFIER_assert 1 2.17 105556 +map2check-assert/byte_add_bug unreach-call afl 5 UNKNOWN 0 11.05 341040 +map2check-assert/byte_add_bug unreach-call symex 5 SUCCEEDED 1 0.47 129628 +map2check-assert/byte_add_bug unreach-call hybrid 5 SUCCEEDED 1 6.64 341312 +map2check-assert/check_if_bug unreach-call afl 5 UNKNOWN 0 1.98 105308 +map2check-assert/check_if_bug unreach-call symex 5 UNKNOWN 0 31.99 187904 +map2check-assert/check_if_bug unreach-call hybrid 5 UNKNOWN 0 37.67 171268 +map2check-assert/ex01_false unreach-call afl 5 FAILED FALSE 5 __VERIFIER_assert 1 2.13 105828 +map2check-assert/ex01_false unreach-call symex 5 FAILED FALSE 5 __VERIFIER_assert 1 1.75 146332 +map2check-assert/ex01_false unreach-call hybrid 5 FAILED FALSE 5 __VERIFIER_assert 1 2.12 105880 +map2check-assert/ex02_false unreach-call afl 5 UNKNOWN 0 1.99 105784 +map2check-assert/ex02_false unreach-call symex 5 UNKNOWN 0 31.56 226364 +map2check-assert/ex02_false unreach-call hybrid 5 UNKNOWN 0 37.17 214116 +memory-alloca/c.03-alloca-2 valid-memsafety afl 5 UNKNOWN 0 11.32 107840 +memory-alloca/c.03-alloca-2 valid-memsafety symex 5 UNKNOWN 0 31.88 172000 +memory-alloca/c.03-alloca-2 valid-memsafety hybrid 5 UNKNOWN 0 40.96 189452 +memsafety-ext/dll_extends_pointer valid-memsafety afl 5 UNKNOWN 0 11.34 341312 +memsafety-ext/dll_extends_pointer valid-memsafety symex 5 UNKNOWN 0 31.65 178568 +memsafety-ext/dll_extends_pointer valid-memsafety hybrid 5 UNKNOWN 0 40.20 341640 +memsafety-ext/tree_stack valid-memsafety afl 5 UNKNOWN 0 11.35 357808 +memsafety-ext/tree_stack valid-memsafety symex 5 UNKNOWN 0 31.99 238772 +memsafety-ext/tree_stack valid-memsafety hybrid 5 UNKNOWN 0 40.76 357712 +memsafety-ext2/length_test03-1 valid-memsafety afl 5 UNKNOWN 0 11.35 341288 +memsafety-ext2/length_test03-1 valid-memsafety symex 5 UNKNOWN 0 31.48 145588 +memsafety-ext2/length_test03-1 valid-memsafety hybrid 5 UNKNOWN 0 39.13 341520 +memsafety-ext2/split_list_test05-1 valid-memsafety afl 5 FAILED FALSE-DEREF 540 containsHead 1 2.50 109120 +memsafety-ext2/split_list_test05-1 valid-memsafety symex 5 UNKNOWN 0 32.69 411436 +memsafety-ext2/split_list_test05-1 valid-memsafety hybrid 5 FAILED FALSE-DEREF 540 containsHead 1 2.51 108176 +memsafety/960521-1-1 valid-memsafety afl 5 FAILED FALSE-FREE 532 main 1 2.48 107784 +memsafety/960521-1-1 valid-memsafety symex 5 FAILED FALSE-FREE 532 main 1 0.59 130084 +memsafety/960521-1-1 valid-memsafety hybrid 5 FAILED FALSE-FREE 532 main 1 2.47 107240 +memsafety/960521-1-2 valid-memsafety afl 5 UNKNOWN 0 11.35 107584 +memsafety/960521-1-2 valid-memsafety symex 5 SUCCEEDED 1 16.09 130192 +memsafety/960521-1-2 valid-memsafety hybrid 5 UNKNOWN 0 38.73 130720 +memsafety/960521-1-3 valid-memsafety afl 5 FAILED FALSE-DEREF 529 main 1 2.45 108516 +memsafety/960521-1-3 valid-memsafety symex 5 FAILED FALSE-DEREF 529 main 1 0.57 130172 +memsafety/960521-1-3 valid-memsafety hybrid 5 FAILED FALSE-DEREF 529 main 1 2.46 108584 +memsafety/test-0102-1 valid-memsafety afl 5 UNKNOWN 0 11.38 341416 +memsafety/test-0102-1 valid-memsafety symex 5 UNKNOWN 0 31.54 166604 +memsafety/test-0102-1 valid-memsafety hybrid 5 UNKNOWN 0 39.97 341428 diff --git a/docs/cutover/d7-layout.md b/docs/cutover/d7-layout.md new file mode 100644 index 0000000..283bde8 --- /dev/null +++ b/docs/cutover/d7-layout.md @@ -0,0 +1,225 @@ +# D7: layout dos 21 módulos `.bc` do runtime drop-in (spike da Task 1.1) + +Data: 2026-10-26. Alvo: Map2Check `develop` `3e914528ec57` (cópia em `vendor/map2check/`), imagem `map2check-dev:develop-3e914528ec57` (clang/LLVM 16.0.6, KLEE 3.1, AFL++ 4.40c). Rust: rustc 1.72.1 (LLVM 16.0.5), rota A′ da D1 (fat LTO, `panic = "abort"`, `--emit=llvm-bc`, `opt-16 --strip-debug`). + +Protótipo: `spikes/d7-layout/` (fora do build da crate). Matriz: `scripts/it/check-link-matrix.sh`. + +**Status: aprovada pelo usuário em 2026-10-26: layout A-fwd com exceção à restrição de IR (item 1a), símbolos internos do C não exportados com delta `SD-22` (item 2) e itens 3–6 fixos.** + +**Revalidação:** o layout foi revalidado em `0bca42eb89d1` pelo gate da Task 4.2 (`gate-2026-10-28.md`): os 21 `.bc` do C são byte a byte iguais aos de `3e914528ec57`, o alvo deste spike, e o drop-in passou o gate. Data e histórico acima ficam como foram. + +## Pergunta + +O frontend (`Caller::linkLLVM`) liga, com `llvm-link`, o `-output.bc` do programa a um conjunto fixo de módulos por combinação: os 4 sempre presentes (`Map2CheckFunctions`, `TrackBBLog`, `NonDetLog`, `PropertyGenerator`), 1 ou 2 de container, 1 a 4 de modo, 1 de gerador, opcionalmente `WasmRuntimeStubs`, e `WitnessGeneration` (o `-witness-result.bc`) ou `WitnessGenerationNone` (o `-result.bc`). São 2 × 5 × 3 × 2 × 2 = 120 combinações. O runtime Rust precisa caber nesses 21 nomes de arquivo sem mudar o frontend. Em particular: + +- o Rust compila uma crate para **um** módulo (com `std` dentro, por LTO fat). Dois módulos com `std` cada um dariam símbolos duplicados; +- `main` é definido pelo gerador. O do AFL++ precisa de `setjmp`/`longjmp`, que o Rust não expressa; +- os intrínsecos `klee_*` e `__afl_*` só existem sob KLEE e no link do `afl-clang-fast`. Um módulo que os referencia em toda combinação deixa símbolos indefinidos nas outras. + +## Estratégias medidas + +| Id | Descrição | Protótipo | +|---|---|---| +| **A-lit** | A do plano ao pé da letra: `Map2CheckFunctions.bc` = runtime Rust inteiro, inclusive a lógica dos geradores; os módulos de gerador são `.ll` que definem `main` e um global forte `__m2c_sel_generator` sobre o padrão fraco do núcleo; `klee_make_symbolic` e o gancho de rejeição do AFL++ viram `extern_weak` no pós-processamento. `main` + `setjmp` do AFL++ em `.ll` (obrigatório: o Rust não tem `setjmp`). | `build-layout.sh alit`, feature `a_literal` de `core/` | +| **A-fwd** | A refinada: `Map2CheckFunctions.bc` = núcleo Rust (estado e lógica) + um trecho `.ll` com `map2check_assume`/`crab_assume`/`assume_loop`. Cada um dos outros 20 módulos é `.ll` que define **exatamente as funções externas que o módulo C homônimo define**, como encaminhadores para pontos de entrada `__m2c_rt_*` do núcleo. Os intrínsecos ficam só nos `.ll` de gerador (`klee_*` no Klee, `__afl_*` e `setjmp`/`longjmp` no AFL). Sem globais de seleção, sem `extern_weak`. | `build-layout.sh fwd` | +| **B** | Geradores como crates Rust `no_std` próprias (`staticlib`, fat LTO, `--emit=llvm-bc`), ligadas ao núcleo. O AFL++ ainda precisa de `main` + `setjmp`/`longjmp` + `nondet_assume` em `.ll`, ligado dentro do `NonDetGeneratorAFL.bc`. Demais módulos como em A-fwd. | `build-layout.sh b`, `gen-b/{none,klee,afl}` | +| **B-loop** | B com o laço persistente do AFL++ em Rust e **sem `longjmp`**: um `assume` falso encerra o processo (`_exit(0)`), e o afl-fuzz recomeça pelo fork server. | `build-layout.sh b-loop`, `gen-b/afl-loop` | + +Escopo do protótipo (ruling da Task 1.1): cobre o que o programa de fumaça alcança (núcleo, rastreio de blocos, log de nondet, arquivo de propriedade, assert, overflow, arquivos do witness, os três geradores completos). As entradas de memtrack/memcleanup/wasm são stubs gerados de `inventory.tsv`. + +### Pós-processamento do núcleo (todas as estratégias) + +`spikes/d7-layout/build-layout.sh`, dentro da imagem: + +1. `opt-16 --strip-debug` (condição 6 da D1); +2. `scripts/klee-stub-symbolizer.py` e `opt-16 -passes=globaldce` (hoje em `scripts/build-runtime-bc.sh`; o antigo `scripts/build-klee-runtime.sh` saiu na Task 2.10); +3. **novo:** `spikes/d7-layout/scripts/stub_personality.py`: o corpo de `rust_eh_personality` vira `abort()`. O `std` pré-compilado é `panic = unwind`; sem isso o personality fica e puxa o unwinder (`_Unwind_*`, de `libgcc_s`) para **todas** as 120 combinações. Com `panic = "abort"` nada desenrola, então o corpo é morto; **Ampliado (aprovado pelo usuário em 2026-10-27):** o mesmo passo (`scripts/runtime-ir/stub_personality.py`) também troca por `abort()` o corpo de `std::panicking::rust_panic_with_hook` (qualquer hash): é o caminho de pânico do `std`, que todo `core::panicking` alcança e que o `globaldce` não remove, e o `get_backtrace_style` inlinado nele tem um `freeze` que o KLEE 3.1 não executa. Com `panic = "abort"` o pânico já termina em `abort`; só a mensagem do pânico deixa de ser impressa. O script falha se qualquer das duas funções não for definida exatamente uma vez. +4. `scripts/klee-lower-intrinsics.py` (intrínsecos vetoriais que o KLEE 3.1 não executa); +5. **novo:** `opt-16 -passes=internalize,globaldce` com a lista pública = os 192 símbolos de `docs/runtime-abi/inventory.tsv` + `__m2c_*`. Sem isso o núcleo exportava ~180 símbolos do `std`/`core` com nome mangled (`_ZN4core3fmt5write...`, `rust_panic`, `rust_begin_unwind`), que podem colidir com o programa e, em B, com outra crate Rust do mesmo toolchain (mesmo hash de mangling). Depois do passo, o núcleo de A-fwd exporta só `map2check_*`, `__VERIFIER_error`, `nondet_log_{init,destroy}`, `write_property` e `__m2c_rt_*`. +6. **novo (fix round 1):** a triple do núcleo (e das crates de B) passa de `x86_64-unknown-linux-gnu` para `x86_64-pc-linux-gnu`, a do clang-16 que compila o programa e o runtime C, e o module flag `"Dwarf Version"` (4 no rustc 1.72, 5 no clang-16) é removido (`scripts/drop_dwarf_flag.py`). O frontend não redireciona o stderr do `llvm-link`, então sem isso cada execução imprimia avisos de triple e de flag. Medido: `llvm-link-16` de uma combinação memtrack+btree+wasm+witness com os três geradores, em A-fwd, B e B-loop, tem stderr vazio. + +## Matriz de link (`scripts/it/check-link-matrix.sh`) + +Programa de fumaça: `scripts/it/smoke/link-smoke.c` (seguro: `malloc` de tamanho fixo, soma num laço limitado por um nondet, verificação com `reach_error`), instrumentado pelo próprio Map2Check instalado (`target/map2check-it/c`), uma vez por modo, guardando o `-output.bc` (ruling R2). Modos: `--memtrack`, `--memcleanup-property`, `--check-overflow`, `--check-asserts`, `--target-function --target-function-name reach_error` (o modo `none`, que liga `AnalysisModeNone.bc`). + +Critérios por combinação: `llvm-link-16` sem erro; `llvm-nm-16 --undefined-only` (fortes e weak) contido em libc/libm da imagem, mais `klee_*` (symex), `__afl_*` (afl), `wasm_rt_*` (wasm); link nativo `clang-16` (gerador none) e `afl-clang-fast -g -O2 ... -lm` (afl). Opcional, com `--ref `: paridade dos símbolos externos definidos (seção abaixo). + +Exceção: `__dso_handle` é aceito **só como referência weak** (`w` no `llvm-nm`). O `std` o referencia assim (destrutores de `thread_local`), e o `crtbegin.o` de todo executável o define. Uma referência forte a ele reprova a combinação (fixture `dso` do teste). É um desvio do texto "libc/libm" do plano. + +| Biblioteca | Resultado | Falhas | +|---|---|---| +| C (`target/map2check-it/c/lib`) | **120/120 PASS** | — | +| A-lit | **0/120 PASS, matrix: FAIL** | gen none: `__m2c_afl_reject klee_make_symbolic` indefinidos (weak); gen symex: `__m2c_afl_reject`; gen afl: `klee_make_symbolic` (40 cada) | +| A-fwd | **120/120 PASS** | — | +| B | **120/120 PASS** | — | +| B-loop | **120/120 PASS** | — | + +O link nativo de A-lit com gerador none funciona (weak indefinido vira nulo; `./a.out` termina com `NONE` no arquivo de propriedade), mas a regra de símbolos permitidos da matriz reprova a estratégia, como previsto: referências `extern_weak` a intrínsecos aparecem em combinações onde o gerador não é aquele. + +## Paridade de símbolos definidos (`check-link-matrix.sh --ref`, fix round 1) + +Para cada combinação, `--ref target/map2check-it/c/lib` liga também a combinação do C sobre o mesmo programa de fumaça e compara os símbolos externos definidos (`llvm-nm-16 --defined-only --extern-only`). Toda diferença precisa estar em `docs/runtime-abi/export-policy.tsv`. A política lista 76 nomes `c-only` e o glob `__m2c_rt_*` `rust-only`: + +- os 76 `c-only` são as funções e globais do C que **só o próprio runtime C usa** (`referenced_by` = `-` ou `runtime` no inventário; nenhum tem consumidor `pass`, `frontend` ou `external`); +- o `rust-only` são os pontos de entrada que o IR chama no núcleo. + +| Biblioteca | `--ref` com a política | Diferenças fora da política | +|---|---|---| +| A-fwd | **120/120 PASS** | nenhuma | +| B | **80/120, matrix: FAIL** | as 40 combinações afl: `extra map2check_afl_index` (estático no C; em B é um `static` Rust exportado porque o `main` em IR o escreve) | + +Sem política, A-fwd difere do C exatamente nos 76 nomes. Todo símbolo de `pass`, `frontend` ou `external` que o C define numa combinação, A-fwd também define nela. +Depois da correção do `gen_ll.py` isso inclui `wasm_rt_trap(enum wasm_rt_trap_t)`: o `WasmRuntimeStubs.bc` de A-fwd define as 10 funções do C, contra 9 antes. A primeira versão descartava em silêncio as assinaturas com `enum`. Agora `enum` vira `i32`, e uma assinatura que o script não sabe traduzir é erro, salvo as 9 auxiliares `runtime` com struct por valor da lista `STRUCT_BY_VALUE_EXEMPT`. + +## KLEE (`spikes/d7-layout/measure-klee.sh`) + +`map2check --debug --nondet-generator symex --timeout 30 link-smoke.c` com a instalação do C e com cópias dela em que só `lib/*.bc` foi trocado (`cp -a` da instalação + os 21 `.bc` da estratégia). `SUCCEEDED` significa que `kleeDroppedPaths()` do frontend devolveu vazio (TRUE exige exploração exaustiva). + +| Instalação | memtrack | memcleanup | overflow | assert | none | +|---|---|---|---|---|---| +| C | SUCCEEDED | SUCCEEDED | SUCCEEDED | SUCCEEDED | SUCCEEDED | +| A-fwd¹ | SUCCEEDED | SUCCEEDED | SUCCEEDED | SUCCEEDED | SUCCEEDED | +| A-lit | SUCCEEDED | SUCCEEDED | SUCCEEDED | SUCCEEDED | SUCCEEDED | +| B | SUCCEEDED | SUCCEEDED | SUCCEEDED | SUCCEEDED | SUCCEEDED | + +¹ No protótipo, memtrack e memcleanup de A-fwd, A-lit e B são stubs: `analysis_is_program_correct` devolve 1 e as entradas de memória não fazem nada. Nesses dois modos a tabela mostra só que o núcleo, o rastreio e o gerador KLEE não descartam caminhos; ela não mostra que a análise de memória é exaustiva sob KLEE. Depois do fix round 1 (triple, flag DWARF, `wasm_rt_trap`), A-fwd e B foram remedidos com o mesmo resultado nos 5 modos. + +Em todas as 20 execuções: 11 caminhos completos, 0 parciais, 11 testes, nenhum `.err`/`.early`, nenhum "silently concretizing", "skipping fork" ou "over memory cap". Instruções executadas no modo none: C 104 383, A-fwd 62 072. + +Sanidade (modo none, symex, sem `--debug`): `progs/magic.c` (`x == 0x13572468 && y > 7`) dá `VERIFICATION FAILED`, "FALSE: Target Reached", linha 11, em C, A-fwd, A-lit e B; `progs/assume.c` (`__VERIFIER_assume(0 <= n <= 8)`) dá `VERIFICATION SUCCEEDED` nas quatro. + +## AFL++ (`spikes/d7-layout/measure-afl.sh`) + +O frontend compila os binários (`map2check --debug --nondet-generator afl`, ou seja `afl-clang-fast -g -O2` e o companheiro `AFL_LLVM_CMPLOG=1`). Depois o afl-fuzz roda 5 vezes **em sequência**, com o ambiente e os flags de `Caller::executeAnalysis`: `AFL_NO_UI=1 AFL_NO_AFFINITY=1 AFL_SKIP_CPUFREQ=1 AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 AFL_CRASHING_SEEDS_AS_NEW_CRASH=1 AFL_BENCH_UNTIL_CRASH=1 timeout afl-fuzz -i -o afl-out -c -- `. Modo none (`--target-function --target-function-name reach_error`), em que o trabalho por execução é o mesmo nos dois runtimes (init, rastreio de blocos, log de nondet, gravação de `map2check_property`). Seguros: 20 s; `magic`: até o crash, limite 30 s. Medianas de 5: + +| Programa | C | A-fwd | B | B-loop | +|---|---|---|---|---| +| `link-smoke` exec/s | 49 296 | 50 352 (**102%**) | 49 722 (101%) | 50 238 (102%) | +| `assume` exec/s (rejeita ~todas as entradas) | 110 395 | 104 981 (**95,1%**) | 105 272 (95,4%) | 7 247 (**6,6%**) | +| `magic` (CmpLog): ms até o 1º crash | 434 | **233** | 219 | 223 | +| `magic`: execs até o 1º crash | 12 986 | 1 855 | 1 226 | 946 | +| `magic`: crashes em 5 campanhas | 5/5 | 5/5 | 5/5 | 5/5 | + +As medianas são da build anterior ao fix round 1. A build nova muda a triple, o flag DWARF e o stub `wasm_rt_trap`, nada que o AFL++ execute. Conferência de 1 rodada na build nova: `assume` 106 575 exec/s (10 s), `magic` com crash em 235 ms. + +Faixas: `link-smoke` C 38 134–52 962, A-fwd 39 991–50 772. `assume` C 108 930–116 354, A-fwd 100 567–111 795. Operador do crash (`op:`): C its×3, havoc×2; A-fwd its×3, havoc×2; B its×4; B-loop its×5. + +**Decisão do AFL++:** `main` + `setjmp` em IR, com `nondet_assume` → `longjmp` também em IR (A-fwd e B). O laço sem `longjmp` (B-loop) perde 93% do throughput quando o programa rejeita entradas, porque cada rejeição encerra o processo e custa um fork. No A-fwd o `longjmp` nunca atravessa um frame Rust: `map2check_assume` é IR (trecho `.ll` do `Map2CheckFunctions.bc`) e chama o `nondet_assume` do gerador direto do frame do programa; o núcleo não segura nenhum lock nesse caminho. + +## Recomendação: A-fwd + +A-fwd atende os três critérios do Step 4: matriz 120/120 PASS; KLEE sem caminho descartado nos 5 modos; AFL++ com 102% e 95% do throughput do C (critério ≥ 90%) e crash do valor mágico em 233 ms contra 434 ms do C. + +Por que A-fwd e não B, que também passa: + +- **Um só módulo Rust.** B põe duas crates Rust na mesma ligação. Isso só funciona porque o `internalize` esconde os símbolos do `core`. Sem ele, os dois módulos exportariam as mesmas funções mangled do `core` (mesmo toolchain, mesmo hash) e o `llvm-link` falharia ao usar `core::fmt` nos dois. B também quadruplica as crates presas ao rustc 1.72 (núcleo + 3 geradores, com manifestos e lockfiles v3 próprios; custo (a)–(b) da D1). +- **B não elimina o IR.** O gerador AFL++ de B ainda precisa de `main`, `setjmp`/`longjmp`, `nondet_assume` e dos globais `__afl_*` em `.ll`. A diferença para A-fwd é onde fica a lógica dos geradores (próximo item). +- **O grafo de módulos do C é preservado.** Em A-fwd cada `.bc` define as funções externas com consumidor fora do módulo que o `.c` homônimo define, e o núcleo chama os outros módulos pelos mesmos símbolos que o C cruza (`analysis_*`, `nondet_*`, `generate_aux_files`, `generate_confirmation_file`). Medido com `--ref` (seção de paridade): cada combinação de A-fwd define os mesmos símbolos externos que a do C, **menos as 76 funções e globais internos do C** da política e **mais os `__m2c_rt_*`**. Não é o mesmo conjunto de definições externas. A paridade de nomes vale para tudo que passes, frontend, AFL++/KLEE ou wasm2c consomem. Isso importa porque um programa que define `__VERIFIER_assert`, como muitos do SV-COMP, falha o link com `AnalysisModeAssert.bc` no C e liga nos outros modos. Um núcleo que exportasse `__VERIFIER_assert` sempre (A-lit) quebraria os outros modos. + +### A-fwd põe lógica de gerador em IR: conflito com a restrição global + +As restrições globais do plano dizem: "Sem código C novo na biblioteca. Módulos de seleção ou de cola, se a D7 os exigir, são LLVM IR textual". **A-fwd não respeita essa restrição como está escrita.** O IR dos geradores não é só seleção ou cola. Ele contém lógica real de gerador, gerada por `gen_ll.py`: + +- o leitor de bytes do AFL++: `get_next_input_from_afl` e o laço `m2c_afl_get_bytes`; +- `map2check_non_det_pchar`: tamanho limitado a 4096, `malloc` e o laço de caracteres; +- `map2check_non_det_memory`, com o laço que registra byte a byte; +- o laço de concretização do KLEE em `nondet_generate_aux_witness_files`: `klee_get_value_i64`, `klee_assume` e o limite de 1024 linhas. + +Os três `.ll` de gerador somam **756 linhas** (AFL 348, Klee 318, None 90). A maior parte são as 21 funções por tipo, de 5 linhas cada. O `main` com `setjmp` do AFL++ e as chamadas a intrínsecos são cola de verdade, e B também precisa delas. + +B cumpre a restrição: a lógica dos geradores fica em Rust `no_std`, e o IR de B é só o `main`/`setjmp`/`longjmp`/`nondet_assume` e os globais do AFL++. A escolha é do usuário (item 1 de "O que a aprovação decide"): + +- **(a) A-fwd com exceção à restrição:** aceitar IR gerado com lógica de gerador (leitor, `pchar`, `memory`, concretização), testado como código (Task 2.7/2.8), em troca de um só módulo Rust; +- **(b) B, que cumpre a restrição:** 3 crates `no_std` a mais presas ao rustc 1.72 e a paridade de `map2check_afl_index` a corrigir (renomear para `__m2c_*`, ou listar na política). + +Uma variante de A-fwd que cumpra a restrição também é possível. Nela a lógica iria para `__m2c_rt_*` no núcleo, e o IR passaria a esse núcleo um ponteiro para a primitiva do gerador (`klee_make_symbolic`, leitor). Não foi medida. + +## Mapa módulo `.bc` → conteúdo (A-fwd) + +O mapa descreve o layout proposto. No protótipo, os encaminhadores de memtrack, memcleanup e wasm ainda são stubs; os de assert, overflow e witness já chamam o núcleo (`IMPLEMENTED` em `gen_ll.py`). + +| Módulo | Conteúdo | +|---|---| +| `Map2CheckFunctions.bc` | Núcleo Rust (rustc 1.72.1, fat LTO, pós-processado como acima): estado, rastreio de blocos, log de nondet, arquivos de propriedade, lógica dos modos (assert, overflow, memtrack, memcleanup), container (D3), witness e wasm. Exporta os símbolos C de `Map2CheckFunctions`, `TrackBBLog`, `NonDetLog` e `PropertyGenerator` que têm consumidor fora do módulo, mais os pontos de entrada `__m2c_rt_*`. Mais o trecho IR `map2check_assume`/`map2check_crab_assume`/`map2check_assume_loop` → `nondet_assume`. | +| `TrackBBLog.bc`, `NonDetLog.bc`, `PropertyGenerator.bc` | IR vazio: os quatro módulos do núcleo são sempre ligados juntos, e o núcleo já define os símbolos deles. | +| `ContainerRealloc.bc`, `ContainerBTree.bc`, `BTree.bc` | IR vazio: um só container Rust (D3); as funções C desses módulos são auxiliares internas do runtime (ver ponto de decisão 2). | +| `AllocationLog.bc`, `ListLog.bc`, `HeapLog.bc` | IR vazio: a lógica fica no memtrack do núcleo. | +| `AnalysisModeMemtrack.bc` | IR: `analysis_{init,destroy,is_program_correct,generate_aux_witness_files}`, os 12 `map2check_*` de memória dos passes e `map2check_wasm_{malloc,free,check_access}` → `__m2c_rt_memtrack_*`. | +| `AnalysisModeMemcleanup.bc` | IR: `analysis_{init,destroy,is_program_correct,generate_aux_witness_files}` e os 12 `map2check_*` de memória dos passes → `__m2c_rt_memcleanup_*` (sem `map2check_wasm_*`; regras de memtrack com `MemMode::Memcleanup`). | +| `AnalysisModeOverflow.bc` | IR: `analysis_*`, `map2check_binop_{add,sub,mul}[_uint]`, `map2check_binop_{sdiv,srem}`, `overflowError` → `__m2c_rt_overflow_*`. | +| `AnalysisModeAssert.bc` | IR: `analysis_*`, `map2check_assert` → `__m2c_rt_assert_*`, `__VERIFIER_assert` vazio (como no C). | +| `AnalysisModeNone.bc` | IR: `analysis_*` (constantes, `is_program_correct` = 1). | +| `NonDetGeneratorNone.bc` | IR: `main` → `__map2check_main__`; os 14 `map2check_non_det_` do C devolvem 0; `map2check_non_det_memory` = memset 0; `nondet_*` vazios. | +| `NonDetGeneratorKlee.bc` | IR: `main`; os 21 `map2check_non_det_` com `klee_make_symbolic` (nome `non_det_`); `pchar`, `memory`; `nondet_assume` (objeto `map2check_pruned` + `_exit(0)`); `nondet_init/destroy` → log do núcleo; `nondet_generate_aux_witness_files`: concretiza as linhas do log com `klee_get_value_i64`/`klee_assume` (até 1024) e chama `__m2c_rt_nondet_log_to_file`. | +| `NonDetGeneratorAFL.bc` | IR: expansões de `__AFL_FUZZ_INIT`/`__AFL_LOOP` (globais `__afl_sharedmem_fuzzing`, `__afl_fuzz_alt[_ptr]`, marcador `##SIG_AFL_PERSISTENT##`), `main` com `__afl_persistent_loop` + `_setjmp`, leitor `get_next_input_from_afl` (zero após o fim), os 21 `map2check_non_det_`, `pchar`, `memory`, `nondet_assume`/`nondet_cancel` → `longjmp`, `nondet_*`. | +| `WasmRuntimeStubs.bc` | IR: os `wasm_rt_*` e `w2c_wasi__snapshot__preview1_proc_exit` → `__m2c_rt_wasm_*` (stubs no protótipo; D2). | +| `WitnessGeneration.bc` | IR: `generate_aux_files`, `generate_confirmation_file` → `__m2c_rt_witness_*`. | +| `WitnessGenerationNone.bc` | IR: os dois vazios. | + +Gerador do IR: `spikes/d7-layout/scripts/gen_ll.py`, que lê os símbolos e assinaturas de `docs/runtime-abi/inventory.tsv`. + +## Achados que a Fase 2/3 herda + +1. **Uma só `Map2CheckFunctions.bc` serve KLEE e AFL++.** O pós-processamento feito para o KLEE (strip-debug, symbolizer, intrínsecos rebaixados para escalar) vale também para o binário do AFL++, que o `afl-clang-fast` recompila e instrumenta inteiro, inclusive o `std`. As medidas acima já incluem esse custo. +2. **`rust_eh_personality` precisa virar `abort()`** (ou o `std` ser compilado com `panic=abort`, o que exige `-Zbuild-std`). Sem isso, toda combinação depende de `libgcc_s`. +3. **`internalize` com a lista do inventário** é obrigatório para não vazar símbolos do `std`. +4. **Atributos de ABI nos encaminhadores.** O IR gerado não põe `signext`/`zeroext` nos parâmetros e retornos `char`/`short` (`analysis_is_program_correct`, `map2check_free_resolved_address`, `map2check_crab_assume`, `generate_aux_files`). Na Fase 2 o `gen_ll.py` precisa copiar os atributos que o clang-16 emite para a assinatura C, e `tests/abi/check-signatures.sh` (Task 2.0) precisa cobrir os encaminhadores. +5. **Uma só triple, obrigatória:** todo `.bc` entregue usa `x86_64-pc-linux-gnu` (a do clang-16), e o núcleo Rust é reescrito para ela no pós-processamento, sem o module flag `"Dwarf Version"`. O frontend não redireciona o stderr do `llvm-link`, então qualquer diferença aparece como aviso em toda execução. Feito no protótipo (passo 6 do pós-processamento). A Fase 3 deve ter um teste de que o `llvm-link` de toda combinação tem stderr vazio. +6. **Variáveis C sem consumidor externo** não são exportadas (lista completa no item 2 da aprovação). O `generate_aux_files` do witness recebe um ponteiro nulo no lugar de `&trackbb_log` e lê o estado do núcleo. +7. **Guarda de `freeze` (Task 2.7; não é passo de pós-processamento, a lista acima não muda).** O KLEE 3.1 não executa a instrução `freeze` do LLVM: o caminho termina com "illegal instruction" (`.exec.err`), que o frontend lê como caminho descartado, e o otimizador do rustc as escreve. `scripts/build-runtime-bc.sh` falha se sobrar alguma no núcleo depois do `internalize,globaldce` (`scripts/runtime-ir/check_no_freeze.py`, que nomeia as funções), sem exceções; a correção é no fonte Rust, nunca uma reescrita do IR. O único `freeze` que o fonte não remove, o do `std` pré-compilado em `rust_panic_with_hook`, sai com o stub do item 3 (decisão do usuário de 2026-10-27). +8. **`map2check_init` → `analysis_init`, `nondet_init` (Task 2.7, antecipado da 3.1).** O núcleo do drop-in é a crate de `klee/Cargo.toml` com a feature `dropin` (só nesse manifesto), e com ela o `map2check_init` chama os `analysis_init`/`nondet_init` externos dos módulos ligados (`Map2CheckFunctions.c:37-38`). Sem a feature (staticlib do `cargo test`, `tests/ffi_harness.c`, goldens, testes de link no host) nada externo é chamado e esses testes chamam `analysis_init` à mão. Build de teste dos 21 módulos: `scripts/build-runtime-bc.sh`; testes na imagem: `scripts/runtime-ir/test-init-wiring.sh` e `scripts/runtime-ir/test-klee-generator.sh`. +9. **Escrita atômica dos arquivos de resultado (SD-20) só sob KLEE (Task 2.8; decisão do usuário de 2026-10-27).** Não é passo de pós-processamento: o núcleo é um só e o modo é escolhido em tempo de execução. No build `dropin` o núcleo começa escrevendo como o C (`fopen("w")`: trunca e escreve no lugar), e o `nondet_init` do `NonDetGeneratorKlee.ll` chama `__m2c_rt_output_set_atomic(1)` antes de qualquer escrita, o que liga o arquivo temporário + `rename`. AFL++ e `none` ficam com a escrita do C. Fora do `dropin` (staticlib, `cargo test`, goldens, runtime KLEE legado) o padrão continua atômico. Motivo medido: com a escrita atômica em toda execução o AFL++ fazia 70% das execuções/s do C no `link-smoke`; com a escrita do C, 101%. Testes: `scripts/runtime-ir/test-klee-generator.sh` (`klee_symex_writes_result_files_atomically`) e `scripts/runtime-ir/test-afl-generator.sh` (`afl_writes_result_files_in_place_like_c`). +10. **O `longjmp` do AFL++ nunca atravessa um frame Rust (item 5 da aprovação, Task 2.8).** `scripts/build-runtime-bc.sh` falha se a parte Rust do núcleo referenciar `longjmp`/`setjmp`, `nondet_assume`/`nondet_cancel`, `map2check_assume*`, `main` ou `__map2check_main__`; `test_gen_ll.py` (`test_only_the_assume_entry_points_reach_longjmp`) confere que, nos módulos IR, só `nondet_cancel`/`nondet_assume` e os `map2check_assume*` do trecho de cola alcançam o `longjmp`. +11. **`map2check_exit` → `generate_aux_files`, `generate_confirmation_file` (Task 3.1).** No núcleo `dropin`, o fim da execução chama os `generate_aux_files(NULL, gotError)` e `generate_confirmation_file` do módulo de witness ligado (`Map2CheckFunctions.c:94-101`), e o `__m2c_rt_witness_generate_aux_files` chama o `analysis_generate_aux_witness_files` do módulo de modo e, com violação ou `MAP2CHECK_SEED_REPLAY`, o `nondet_generate_aux_witness_files` do gerador, único que escreve o `klee_log.csv` (`WitnessGeneration.c:15-29`). O `config.witness` não tem papel no drop-in. Teste na imagem: `scripts/runtime-ir/test-witness-wiring.sh`. Build definitivo: `scripts/build-runtime-bc.sh` (determinístico, `scripts/test-build-runtime-bc.sh`); instalação sobreposta: `scripts/it/overlay-install.sh`. + +## Alternativas + +- **B** (geradores em crates Rust `no_std`): passa nos mesmos critérios (matriz 120/120, KLEE 5/5, AFL++ 101% e 95%) e **cumpre a restrição global de IR só para seleção/cola**. Na paridade `--ref` dá 80/120 (`map2check_afl_index` exportado nas combinações afl, corrigível). Custa 3 crates a mais presas ao rustc 1.72, depende do `internalize` para não colidir no `llvm-link` e ainda precisa de IR para o `main`/`setjmp` do AFL++. Vale se o usuário preferir lógica de gerador em Rust a IR gerado. +- **A-lit** (A literal do plano): reprovada pela matriz (0/120). As referências `extern_weak` a `klee_*` e ao gancho do AFL++ ficam indefinidas nas combinações dos outros geradores. Além disso, o `nondet_assume` do AFL++ faz `longjmp` de dentro de um frame Rust, e um núcleo que define os símbolos de modo sempre quebra a paridade do conjunto de símbolos por combinação. +- **B-loop** (AFL++ sem `longjmp`): reprovado pelo critério de throughput no programa com `assume` (6,6% do C). + +## O que a aprovação decide + +Aprovar a D7 como proposta fixa, para as Fases 2 e 3: + +1. **Layout e a restrição de IR (decisão explícita):** escolher entre + - **(a) A-fwd com exceção à restrição global** "IR só para módulos de seleção ou de cola": o núcleo Rust é o `Map2CheckFunctions.bc`, os outros 20 módulos são LLVM IR textual gerado (`gen_ll.py` promovido a `scripts/`), com o mapa acima, e o IR dos geradores contém lógica de gerador (leitor do AFL++, `pchar`, `non_det_memory`, concretização do KLEE; 756 linhas geradas). Essa é a recomendação; + - **(b) B**, que cumpre a restrição, com geradores em crates Rust `no_std` e IR só para o `main`/`setjmp`/`longjmp` do AFL++. +2. **Símbolos exportados por combinação** (`docs/runtime-abi/export-policy.tsv`, conferido por `check-link-matrix.sh --ref`): cada `.bc` define as funções externas que o `.c` homônimo define **e que têm consumidor fora do runtime C** (`referenced_by` contém `pass`, `frontend` ou `external`). **Não** são exportados, por serem usados só pelo próprio runtime C: + - **globais:** `sv_comp`, `gotError`, `Map2CheckCurrentStep`, `trackbb_log`, `nondet_log`, `allocation_log`, `heap_log`, `list_log`, `ERROR_DEREF`, `klee_log_file`, `trackbb_log_file`, `heap_log_file`, `list_log_file`, `violated_property_file`, `violated_property_file_{unknown,memtrack,memcleanup,deref,free,overflow,assert}`; + - **funções:** + - núcleo e logs: `is_in_tracked`, `trackbb_new_row`, `trackbb_log_to_file`, `helper_map2check_nondet_append_element`, `new_nondet_call`, `map2check_nondet_get_log`, `nondet_log_to_file`, `write_property_{unknown,memtrack,memcleanup,deref,free,overflow,assert}`; + - container: `new_container`, `append_element`, `get_element_at`, `free_container`; + - BTree: `B_TREE_{CREATE,FREE,INSERT,INSERT_NONFULL,PAGE_CREATE,SEARCH,SPLIT_CHILD}`, `CHECK_AND_RELEASE_PAGES`, `DISK_{READ,WRITE}`, `DumpTree*`; + - logs de memória: os auxiliares de `AllocationLog`/`ListLog`/`HeapLog`; + - overflow: `divisionByZeroError`, `unknown_not_supported`. + + São 76 nomes ao todo, um por linha na política. + - **novos, só no Rust:** os pontos de entrada `__m2c_rt_*` do núcleo, que o IR chama. + + Consequência observável: um programa que define, por exemplo, `append_element` ou `gotError` liga com o runtime Rust e falha com o C. Se o usuário quiser paridade total de nomes, a alternativa é exportá-las também como encaminhadores, ao custo de reproduzir em IR as structs C passadas por valor (`MAP2CHECK_CONTAINER`). A recomendação é não exportar e registrar a diferença como `SD-xx`. +3. **Pós-processamento obrigatório do núcleo**: strip-debug, symbolizer, `rust_eh_personality` → `abort`, `std::panicking::rust_panic_with_hook` → `abort` (acrescentado com aprovação do usuário em 2026-10-27, Task 2.7), intrínsecos rebaixados, `internalize` com a lista do inventário. +4. **`__dso_handle` aceito na matriz só como referência weak**, além de libc/libm. +5. **O AFL++ continua com `setjmp`/`longjmp` em IR**, como o C; não há laço Rust sem `longjmp`. +6. **Uma só triple para todos os `.bc` entregues** (`x86_64-pc-linux-gnu`), sem o module flag `"Dwarf Version"` no código Rust, para que o `llvm-link` do frontend não imprima avisos. + +Fica para a Fase 2 o que o protótipo deixou como stub: memtrack, memcleanup e wasm reais, e os atributos `signext`/`zeroext` dos encaminhadores. + +## Reproduzir + +```sh +scripts/it/test-check-link-matrix.sh # teste do script da matriz +scripts/it/check-link-matrix.sh target/map2check-it/c/lib # C: matrix: PASS +for s in fwd alit b b-loop; do + spikes/d7-layout/build-layout.sh $s target/map2check-it/d7/$s + scripts/it/check-link-matrix.sh target/map2check-it/d7/$s + scripts/it/check-link-matrix.sh --ref target/map2check-it/c/lib target/map2check-it/d7/$s + rm -rf target/map2check-it/d7/inst-$s + cp -a target/map2check-it/c target/map2check-it/d7/inst-$s + cp target/map2check-it/d7/$s/*.bc target/map2check-it/d7/inst-$s/lib/ +done +spikes/d7-layout/measure-klee.sh target/map2check-it/d7/inst-fwd fwd +spikes/d7-layout/measure-afl.sh target/map2check-it/d7/inst-fwd fwd scripts/it/smoke/link-smoke.c 20 5 +spikes/d7-layout/measure-afl.sh target/map2check-it/d7/inst-fwd fwd spikes/d7-layout/progs/assume.c 20 5 +spikes/d7-layout/measure-afl.sh target/map2check-it/d7/inst-fwd fwd spikes/d7-layout/progs/magic.c 30 5 +``` diff --git a/docs/cutover/decisoes.md b/docs/cutover/decisoes.md index c9d9025..8c007da 100644 --- a/docs/cutover/decisoes.md +++ b/docs/cutover/decisoes.md @@ -180,6 +180,8 @@ A aprovação aceita a proposta junto com estas condições. - O `--btree` do C não é referência válida. Sob `symex`, dá SUCCEEDED errado em 3 tarefas cujo veredito esperado é falso: `lis_unsafe`, `memleaks_test1-1` e `memleaks_test17_2-1`. Também perde `list-ext` (FAILED → UNKNOWN, nos dois geradores). - Números de pico de RSS (mediana, 30 tarefas `valid-memsafety`): symex C-btree 136,3 MB contra Rust 221,2 MB; afl C-btree 94,3 MB contra Rust 92,3 MB. O maior pico do Rust é 472 MB. +**Ressalva do `--btree` do C (Task 2.5, drop-in):** no C, `find_row_with_address` e as buscas de `get_element_at` sobre `ContainerBTree.c`/`BTree.c` devolvem a linha da página carregada, e o runtime a altera no lugar (`row->size = 0; row->is_free = TRUE`, `AnalysisModeMemtrack.c:190-192`, `:372-375`) sem regravá-la. Se `CHECK_AND_RELEASE_PAGES` (`BTree.c:32`, chamado em `:125` e `:166`) liberar a página antes de ela ir para o disco (limite `B_TREE_MAX_OPEN_PAGES` = 133682 páginas, `BTree.h:39-40`), a liberação se perde e um free some do log. O container único do Rust (`memtrack::c_mode::CMemory`, servido aos dois conjuntos de módulos, que são IR vazios) não tem esse caso. Na prática ele é inalcançável: exige mais de 133682 páginas abertas. + **Desvio da condição literal da Task 2.6:** a condição (RSS do Rust ≤ o do C com `--btree` em todas as tarefas) **não** é atendida: sob `symex` o Rust fica acima em 30 de 30 tarefas, por um excesso fixo de cerca de 80 MB. O mantenedor aprovou explicitamente se afastar dela, pelo motivo acima. ## D4 @@ -248,3 +250,50 @@ The C tests drove internal containers with no Rust export, so each assertion goe | BTreeTest.AddItem | not ported | D3 | | BTreeTest.AddItemShouldSetValue | not ported | D3 | | BTreeTest.AddMultipleItems | not ported | D3 | + +## Merge da migração no Map2Check (Task 5.1) + +**2026-10-17.** O PR hbgit/Map2Check#76 (`feat/rust-runtime` → `develop`) foi mergeado pelo mantenedor com merge commit **a0f8b9cd4fd97ed71a2f3bc70fa34525d909cfc0** (pais f01e8c3e e 1f91659e). O rollback é `git revert -m 1 a0f8b9cd` em `develop`; o ensaio em clone descartável restaurou o runtime C e passou no `ctest` (10/10). O gate 4.2 passou duas vezes seguidas antes do merge (wall 1,011/1,026; RSS 1,081/1,017). Começa o período de observação de um ciclo de release (Task 5.1, Step 6); a Fase 6 só começa ao fim dele. + +## Reinício de 2026-10-26 + +**Contexto.** O PR hbgit/Map2Check#76 foi mergeado em `develop` como `a0f8b9cd` em 2026-10-17. Em 2026-10-22 um force push do mantenedor moveu `develop` de `a0f8b9cd` para `fbbcb929`, removendo esse merge. Depois disso o `develop` recebeu os PRs #73 (Map2Check 9.0), #85, #86 e #87; o `develop` atual é `3e914528`. A base comum entre `a0f8b9cd` e o `develop` atual é `f01e8c3e`. Portanto as mudanças no Map2Check da migração anterior não existem mais no `develop`, e o merge descrito em "Merge da migração no Map2Check (Task 5.1)" acima deixou de valer. + +**Decisões do usuário em 2026-10-26.** + +- Recomeçar do zero, sem as mudanças antigas no Map2Check. +- O agente altera somente este repositório; o Map2Check é lido apenas como cópia local do `develop` (`scripts/fetch-map2check.sh`, commit registrado em `vendor/MAP2CHECK_VERSION`). +- **D6:** a biblioteca exporta o ABI do runtime C do `develop` (drop-in). D6 **revoga D5**. +- O código Rust existente é mantido e revalidado contra o novo alvo. +- **D2** (wasm portado) e **D3** (`--btree` usa o mesmo container Rust, sem mudança observável) continuam valendo. + +Plano: [docs/superpowers/plans/2026-10-26-runtime-drop-in-map2check.md](../superpowers/plans/2026-10-26-runtime-drop-in-map2check.md). + +## Linha de base do C: `array-examples/relax-1` com afl é ERROR (Task 0.4) + +**2026-10-26.** Na linha de base `docs/cutover/baselines/c-3e914528ec57.tsv`, a chave `array-examples/relax-1 valid-memsafety afl` dá `ERROR` em 5 de 5 repetições: o frontend C do Map2Check develop aborta logo após o AFL++ ("AFL++ ran: 42 executions", depois "timeout: the monitored command dumped core") sem imprimir linha `VERIFICATION`. É comportamento do C, determinístico, e o gate o trata como um veredito comum. Se o runtime Rust divergir nessa chave (p.ex. dar UNKNOWN ou FAILED), a divergência só passa com um `SD-xx` em `docs/semantic-deltas.md` e uma linha em `docs/cutover/deltas-aceitos.tsv`. Reproduzir o `ERROR` fazendo o runtime Rust abortar de propósito é proibido. + +## D7 — layout dos módulos do runtime (aprovada em 2026-10-26) + +Medições e mapa completo em [d7-layout.md](d7-layout.md). O usuário aprovou: + +1. **Layout A-fwd, com exceção à restrição de IR.** O núcleo Rust é o `Map2CheckFunctions.bc`. Os outros 20 módulos são LLVM IR textual gerado por `gen_ll.py`, que será promovido a `scripts/`. O IR dos geradores contém lógica de gerador, que passa a ser testada como código (Tasks 2.7/2.8). +2. **Símbolos internos do C não exportados.** São 76 nomes, listados em `docs/runtime-abi/export-policy.tsv`. O Rust acrescenta os `__m2c_rt_*`. A diferença observável fica registrada como `SD-22` em `docs/semantic-deltas.md`, e `check-link-matrix.sh --ref` confere a política. +3. **Pós-processamento obrigatório do núcleo:** strip-debug, symbolizer, `rust_eh_personality` → `abort`, intrínsecos rebaixados e `internalize` com a lista do inventário. +4. **`__dso_handle`** é aceito na matriz só como referência weak. +5. **O AFL++ mantém `setjmp`/`longjmp` em IR**, como no C. +6. **Uma só triple** (`x86_64-pc-linux-gnu`) para todos os `.bc`, sem avisos do `llvm-link`. + +## Critério de tempo do gate para afl e híbrido (aprovado em 2026-10-27) + +A checagem prévia da Task 4.1 (relatório da Task 4.1a) mostrou que a mediana de `wall_rust / wall_c` não chega a 1,10 nos geradores `afl` e `hybrid`. A Task 3.3 reduziu o núcleo em 74%: de 43 340 para 12 741 linhas de IR. No subconjunto, a mediana caiu de 2,78 para 1,35 (afl 3,92, symex 0,95, híbrido 3,21). + +O que sobra é custo fixo. O frontend compila o programa ligado ao núcleo três vezes com `afl-clang-fast`: fuzzed, witness e cmplog. Cada compilação leva ~0,60 s, contra 0,10 s no C. Em tarefas de menos de 1 s, só um núcleo do tamanho do C chegaria a 1,10. Uma mudança no frontend, como pré-compilar o núcleo, é vedada pelas regras 2 e 3. + +O usuário decidiu: + +- Nos geradores `afl` e `hybrid`, cada `wall_rust` desconta até 2,0 s de custo fixo de compilação antes de se calcular a razão. O critério da mediana continua ≤ 1,10. +- No `symex`, o critério de 1,10 vale sem desconto. +- RSS, TIMEOUT e vereditos continuam como estavam. + +`scripts/it/compare_baselines.py` aplica o desconto. diff --git a/docs/cutover/deltas-aceitos.tsv b/docs/cutover/deltas-aceitos.tsv new file mode 100644 index 0000000..7c8c70c --- /dev/null +++ b/docs/cutover/deltas-aceitos.tsv @@ -0,0 +1,15 @@ +task prp gen field c_value rust_value sd_ref +list-ext-properties/simple-ext unreach-call symex verdict UNKNOWN FAILED SD-20 +map2check-assert/ex01_false unreach-call symex verdict UNKNOWN FAILED SD-20 +list-ext-properties/960521-1_1-1 valid-memsafety symex line 0 533 SD-20 +list-ext-properties/960521-1_1-1 valid-memsafety symex function main SD-20 +list-ext-properties/list-ext unreach-call symex verdict UNKNOWN FAILED SD-20 +ldv-memsafety/memleaks_test10-2 valid-memsafety hybrid verdict UNKNOWN SUCCEEDED SD-24 +ldv-memsafety/memleaks_test10-2 valid-memsafety symex verdict UNKNOWN SUCCEEDED SD-24 +ldv-memsafety/memleaks_test3-2 valid-memsafety hybrid verdict UNKNOWN SUCCEEDED SD-24 +ldv-memsafety/memleaks_test3-2 valid-memsafety symex verdict UNKNOWN SUCCEEDED SD-24 +ldv-memsafety/ArraysOfVariableLength valid-memsafety hybrid verdict UNKNOWN SUCCEEDED SD-25 +ldv-memsafety/ArraysOfVariableLength valid-memsafety symex verdict UNKNOWN SUCCEEDED SD-25 +memsafety/960521-1-2 valid-memsafety symex verdict UNKNOWN SUCCEEDED SD-25 +list-ext-properties/960521-1_1-2 valid-memsafety hybrid verdict UNKNOWN SUCCEEDED SD-25 +list-ext-properties/list-ext valid-memsafety afl verdict UNKNOWN FAILED SD-26 diff --git a/docs/cutover/gate-2026-10-27.md b/docs/cutover/gate-2026-10-27.md new file mode 100644 index 0000000..6f9bbd9 --- /dev/null +++ b/docs/cutover/gate-2026-10-27.md @@ -0,0 +1,191 @@ +# Primeiro gate diferencial do runtime drop-in (Task 4.1) + +Data: 2026-10-27 (execução de 18:08 a 22:45, horário local). + +**Resultado: `gate: PASS`** (2026-10-28, depois das decisões do usuário sobre a classe (iii); ver "Decisões de 2026-10-28"). + +Na primeira comparação (2026-10-27), o resultado foi `gate: FAIL`. Desempenho, RSS, TIMEOUT, witness e acertos já passavam. O critério `verdict` falhava com 10 divergências sem SD aprovado, todas com o C em UNKNOWN e o drop-in no veredito esperado pelo corpus. Não houve troca FAILED↔SUCCEEDED. + +## Versões + +| | | +|---|---| +| Map2Check `develop` | `3e914528ec5742d8ae4bc191589ca4669827c7ad` (`vendor/MAP2CHECK_VERSION`) | +| Biblioteca | `bcbdab3` (branch `feat/drop-in-runtime`). Os fontes do runtime são os de `ee77c56` (Task 3.3); o `Map2CheckFunctions.bc` é byte a byte igual ao medido na Task 3.3. | +| Imagem | `map2check-dev:develop-3e914528ec57-time` | +| Instalação C | `target/map2check-it/c` (`scripts/it/build-map2check.sh`) | +| Instalação drop-in | `scripts/build-runtime-bc.sh target/map2check-it/g41-bc` (`core checks: 8865 instruction lines (< 9500), 12741 IR lines (< 13600)`), depois `scripts/it/overlay-install.sh target/map2check-it/c target/map2check-it/g41-bc target/map2check-it/g41-ov` | +| Comparador | `scripts/it/compare_baselines.py` com o desconto de compilação aprovado (`COMPILE_ALLOWANCE_S = 2.0`, só em `afl` e `hybrid`; `decisoes.md`, "Critério de tempo do gate para afl e híbrido") | + +## Método + +`scripts/it/run-corpus.sh` com os parâmetros da linha de base do C (`c-3e914528ec57.tsv`, Task 0.4): 41 pares (task, prp), `GENERATORS="afl symex hybrid"`, `TIMEOUT_S=45`, `KILL_MARGIN_S=30`, 5 repetições, 615 execuções por lado. + +O host mudou desde a linha de base. Por isso o C rodou de novo no mesmo dia, intercalado com o drop-in: para r = 1..5, primeiro uma repetição completa do C (`REPS=1`), depois uma do drop-in. O número da repetição foi regravado na coluna `rep` ao juntar os arquivos. A ordem interna do `run-corpus.sh` não muda: repetição, par, gerador. Cada repetição levou cerca de 26 min no C e 24 min no drop-in. + +Arquivos: +- `docs/cutover/baselines/rust-bcbdab3eb694.tsv`: drop-in, 615 linhas. +- `docs/cutover/baselines/c-3e914528ec57-20261027.tsv`: o C do mesmo dia, 615 linhas. +- `docs/cutover/baselines/c-3e914528ec57.tsv`: a linha de base da Task 0.4, inalterada. + +**Deriva do host:** nenhuma. O C de hoje contra o C da Task 0.4 dá `gate: PASS`, com vereditos idênticos, wall bruto mediano de 1,000 e RSS de 0,999. O gate abaixo usa a linha de base commitada. A comparação com o C do mesmo dia leva ao mesmo resultado em todos os critérios. + +Nas 123 chaves, nenhum dos três TSVs muda veredito, tag, linha, função ou witness entre repetições. + +Comandos do comparador, na raiz do repositório (o gate é o primeiro; o segundo é a comparação com o C do mesmo dia): + +```sh +python3 scripts/it/compare_baselines.py docs/cutover/baselines/c-3e914528ec57.tsv docs/cutover/baselines/rust-bcbdab3eb694.tsv --deltas docs/cutover/deltas-aceitos.tsv +python3 scripts/it/compare_baselines.py docs/cutover/baselines/c-3e914528ec57-20261027.tsv docs/cutover/baselines/rust-bcbdab3eb694.tsv --deltas docs/cutover/deltas-aceitos.tsv +``` + +Em 2026-10-27 o `deltas-aceitos.tsv` tinha as 4 linhas da SD-20; depois das decisões de 2026-10-28, as 14 linhas. Com o arquivo atual, o primeiro comando reproduz a saída de "Decisões de 2026-10-28". + +## Saída do gate em 2026-10-27 (C commitado contra drop-in, com as 4 linhas da SD-20) + +``` +deltas: PASS (4 linhas, sd_ref inexistente: nenhum) +keys: PASS (123 C, 123 Rust, só C: 0, só Rust: 0) +reps: PASS (0 chaves com contagem diferente) +verdict: FAIL (12 divergências, 2 aceitas) ... +failed-details: PASS (2 divergências, 2 aceitas) +witness: PASS (0 divergências, 0 aceitas) +expected: PASS (afl 16->17, hybrid 21->25, symex 17->24) +wall: PASS (mediana 0.940, limite 1.10, 123 chaves; desconto de compilação 2.0 s por execução em afl, hybrid, symex sem desconto; mediana bruta 1.197; por gerador: afl 0.931 (bruta 2.948, desconto 2.0 s); hybrid 0.936 (bruta 1.345, desconto 2.0 s); symex 1.004 (bruta 1.004, desconto 0.0 s)) +rss: PASS (mediana 1.021, limite 1.36, 123 chaves) +warning: map2check-assert/byte_add_bug unreach-call hybrid razão rss 2.69 > 1.5 +timeouts: PASS (afl 0->0, hybrid 0->0, symex 0->0) +gate: FAIL +``` + +## Tempo de parede e RSS + +Razão por chave = mediana(rust) / mediana(C) sobre as 5 repetições. A tabela dá a mediana dessas razões. "Ajustada" aplica `max(wall_rust − 2,0 s, 0)` por execução em afl e hybrid. + +| gerador | contra o C commitado: bruta | ajustada | RSS | contra o C do mesmo dia: bruta | ajustada | RSS | +|---|---|---|---|---|---|---| +| afl | 2,948 | **0,931** | 1,151 | 2,853 | **0,933** | 1,151 | +| symex | 1,004 | **1,004** | 1,017 | 1,004 | **1,004** | 1,018 | +| hybrid | 1,345 | **0,936** | 1,021 | 1,345 | **0,936** | 1,059 | +| todas | 1,197 | **0,940** | 1,021 | 1,196 | **0,941** | 1,025 | + +Limites: wall ≤ 1,10 e RSS ≤ 1,36. Soma do wall de todas as chaves: C 1531 s, drop-in 1425 s (0,93). Por gerador: afl 188 → 264 s, symex 560 → 460 s, hybrid 783 → 701 s. + +### Tarefas acima de 1,5 + +- **Razão bruta:** 39 chaves contra o C commitado (afl 23, hybrid 16, symex 0). São as tarefas afl/hybrid em que o C termina em menos de 1–2 s. O excedente é o custo fixo das três compilações do núcleo pelo `afl-clang-fast`, que o desconto aprovado cobre. Com o desconto, nenhuma chave passa de 1,5 contra o C commitado. A maior razão bruta de symex é 1,43 (`byte_add_bug`, 0,35 → 0,50 s). +- **Razão ajustada, contra o C do mesmo dia:** uma chave, `array-memsafety/array01-alloca-2 valid-memsafety afl`, com 1,72 (C 1,77 s, drop-in 5,05 s, medianas). Não é custo do runtime. O AFL++ para no primeiro crash (`AFL_BENCH_UNTIL_CRASH`), e o crash não se reproduz no witness ("Could not replicate error", UNKNOWN nos dois lados), então o wall depende de quando o fuzzer acha esse crash. Na mesma chave, o C variou de 0,81 a 3,12 s entre repetições e o drop-in de 2,76 a 7,05 s. Perfil por amostragem do `ps` a cada 50 ms, 3 execuções de cada lado: + +| fase | C | drop-in | +|---|---|---| +| llvm-link e passes | 0,11 s | 0,11–0,16 s | +| 3 × `afl-clang-fast` (fuzzed, witness, cmplog) | 0,38 s | 2,25–2,30 s | +| `afl-fuzz` até o primeiro crash | 1,4–1,5 s | 1,1–1,5 s | +| replay do crash no witness | < 0,1 s | < 0,1 s | + + O tempo de fuzzing por execução é o mesmo nos dois lados. O que pesa são a compilação, já descontada, e a variância do AFL++. Contra o C commitado essa chave fica em 1,40. +- **RSS acima de 1,5:** `map2check-assert/byte_add_bug unreach-call hybrid`, com 2,69. Em 3 das 5 repetições do drop-in, o `maxrss` é o do processo `afl-fuzz` (cerca de 341 MB, o mesmo patamar da SD-21). Nas outras 2 é o do KLEE (cerca de 130 MB). O GNU time reporta o maior processo da árvore, e o C mostra a mesma alternância em `byte_add_bug afl` (340 844 KB na rep 1, 92 256 KB na rep 2). É efeito de medição, não memória do runtime. + +## Vereditos + +Vereditos por chave (moda das 5 repetições; nenhuma chave variou entre repetições): + +| gerador | C: FAILED / SUCCEEDED / UNKNOWN / ERROR | drop-in: FAILED / SUCCEEDED / UNKNOWN / ERROR | +|---|---|---| +| afl | 16 / 0 / 24 / 1 | 17 / 0 / 23 / 1 | +| symex | 13 / 5 / 23 / 0 | 16 / 9 / 16 / 0 | +| hybrid | 18 / 4 / 19 / 0 | 18 / 8 / 15 / 0 | + +TIMEOUT: 0 nos dois lados. `array-examples/relax-1 afl` é ERROR nos dois lados (5/5), como registrado em `decisoes.md`. Acertos contra `expected_verdict`: afl 16 → 17, symex 17 → 24, hybrid 21 → 25. + +## Divergências + +Há 14 divergências: 12 de `verdict` e 2 de `failed-details`. Todas aparecem nas 5 repetições, no C commitado e no C do mesmo dia. + +### (i) Cobertas por SD aprovado: 4 linhas novas em `deltas-aceitos.tsv` + +| chave | campo | C → drop-in | SD | +|---|---|---|---| +| `list-ext-properties/simple-ext unreach-call symex` | verdict | UNKNOWN → FAILED | SD-20 | +| `map2check-assert/ex01_false unreach-call symex` | verdict | UNKNOWN → FAILED | SD-20 | +| `list-ext-properties/960521-1_1-1 valid-memsafety symex` | line | 0 → 533 | SD-20 | +| `list-ext-properties/960521-1_1-1 valid-memsafety symex` | function | '' → main | SD-20 | + +A SD-20 cita estes casos pelo nome: "`simple-ext` and `ex01_false`: KLEE reaches `__VERIFIER_error` ..., but the frontend reports UNKNOWN. The expected verdict is false; Rust reports FAILED `TARGET-REACHED`." e "`960521-1_1-1`: `map2check_property` and `map2check_property_deref` are both 0 bytes. The frontend still says FALSE-DEREF ... but it parses line 0 and an empty function. C under `afl` reports `533 main` for the same task, and so does Rust under `symex`." + +### (ii) Não determinismo do C: nenhuma + +Nenhuma chave divergente varia entre repetições no C, nem na linha de base commitada nem no C do mesmo dia. A Task 4.1a atribuiu três destas divergências a oscilação do C. Isso não se confirma: o C dá UUUUU nas três. + +### (iii) Candidatos a bug ou delta não aprovado: 10 divergências em 4 grupos + +Todas foram aceitas em 2026-10-28; ver "Decisões de 2026-10-28". + +**A. `void main` sob KLEE (4 chaves).** `ldv-memsafety/memleaks_test10-2` e `ldv-memsafety/memleaks_test3-2`, `valid-memsafety`, em `symex` e `hybrid`: UNKNOWN → SUCCEEDED (esperado: true). +- No C, o KLEE para o único caminho com `KLEE: ERROR: ...preprocessed.c:790: return void when caller expected a result`, e o frontend diz "KLEE terminated states early ... Forcing timeout". +- Causa: o `main` de `NonDetGeneratorKlee.c` chama `int __map2check_main__(int, char**)`, mas o programa declara `void main(void)`. O C é compilado em `-O0` (`optnone`), e no `assembly.ll` do KLEE a chamada fica `%8 = call i32 @__map2check_main__(...)`, `ret i32 %8`. No drop-in, o `main` do `NonDetGeneratorKlee.bc` gerado não tem `optnone`, e o `--optimize` do KLEE o reduz a `tail call fastcc void @__map2check_main__()` / `ret i32 poison`, então o erro some. +- O mesmo vale para `memleaks_test1-1` e `memleaks_test17_2-1` (também `void main`): o veredito é igual (FAILED), mas o C sob symex leva 4,2 s e 19,6 s, contra 0,9 s e 1,8 s no drop-in. +- Reprodução mínima, com a mesma linha de comando nos dois lados (`--nondet-generator symex --timeout 45 --memtrack --generate-witness`): + ```c + #include + void main(void) { + int *p = malloc(sizeof(int)); + free(p); + } + ``` + C: `KLEE: ERROR: ...:5: return void when caller expected a result`, `VERIFICATION UNKNOWN`. Drop-in: `VERIFICATION SUCCEEDED`. + +**B. KLEE termina onde o C esgota o tempo (4 chaves).** Todas UNKNOWN → SUCCEEDED, com esperado true: +- `ldv-memsafety/ArraysOfVariableLength valid-memsafety`, symex e hybrid; +- `memsafety/960521-1-2 valid-memsafety symex`; +- `list-ext-properties/960521-1_1-2 valid-memsafety hybrid`. + +O C para no timer do KLEE ("KLEE halted on its timer -- not a complete exploration"). O drop-in completa a exploração com menos instruções interpretadas: + +| chave | C | drop-in | +|---|---|---| +| ArraysOfVariableLength symex | 112 M instruções, 0 caminhos completos, 31 s | 50 M, 1 caminho completo, 7 s | +| 960521-1-2 symex | 109 M, 0 completos, 31 s | 95 M, 1 completo, 16 s | +| 960521-1_1-2 hybrid, fase KLEE | 30 M, 18 completos e 13 parciais | 5,5 M, 31 completos | + +O veredito muda porque o runtime custa menos sob o KLEE, não por uma semântica de checagem diferente. Nenhuma SD cobre isso. + +**C. `list-ext-properties/list-ext valid-memsafety afl`: UNKNOWN → FAILED `FALSE-DEREF 558 main`** (esperado: false, valid-deref). +- No C, a semente `A` trava já no dry run do `afl-fuzz` (sig 06, "We need at least one valid input seed that does not crash!"), e o AFL++ aborta. No replay do witness num processo novo, a mesma entrada dá `NONE`, sem `map2check_checked_error`, e o frontend diz "Could not replicate error" → UNKNOWN. +- No drop-in, a semente não trava. O AFL++ acha um crash por havoc em cerca de 1600 execuções, e esse crash se reproduz no witness. +- O programa lê `p->n` de um nó cujo `n` nunca foi inicializado (laço de `free`: `while (p->n != 0)`, linha 558 do `list-ext.i`, linha 74 do `list-ext.c`). O resultado depende do conteúdo do heap reaproveitado no modo persistente, e o layout de heap difere entre os runtimes. +- Evidência em `target/map2check-it/g41/dbg/listext-afl-{c,ov}`, rodado com `--debug` sobre o `list-ext.c` (o `.i` do corpus dá o mesmo veredito, na linha 558). + +**D. `list-ext-properties/list-ext unreach-call symex`: UNKNOWN → FAILED.** O mecanismo é o da SD-20. Com `--debug`, o C deixa `test000001.abort.err` em `klee-out-0`, um `map2check_checked_error` de 12 bytes e um `map2check_property` de 0 byte. A SD-20, porém, não cita esta tarefa entre os seus casos (só `simple-ext`, `ex01_false` e `960521-1_1-1`), então a linha ficou fora de `deltas-aceitos.tsv` até o usuário confirmar. + +## Decisões de 2026-10-28 + +O usuário decidiu os quatro grupos da classe (iii). Em todos, o comportamento do drop-in fica como está. + +| grupo | decisão | chaves | linhas em `deltas-aceitos.tsv` | +|---|---|---|---| +| A | nova SD-24 (`void main` sob KLEE). O `main` gerado do `NonDetGeneratorKlee.bc` continua sem `optnone`. | `memleaks_test10-2` e `memleaks_test3-2`, `valid-memsafety`, symex e hybrid | 4 | +| B | nova SD-25 (o KLEE completa a exploração onde o C esgota o timer) | `ArraysOfVariableLength` symex e hybrid, `960521-1-2` symex, `960521-1_1-2` hybrid | 4 | +| C | nova SD-26 (leitura de heap não inicializado; o AFL++ do drop-in acha um crash que se reproduz) | `list-ext valid-memsafety afl` | 1 | +| D | a SD-20 passa a citar a tarefa | `list-ext unreach-call symex` | 1 | + +Cada SD nomeia as chaves exatas. `tests/gate_deltas.rs` exige uma linha em `deltas-aceitos.tsv` para cada chave citada, com os valores medidos. O mesmo arquivo verifica que o `main` do `NonDetGeneratorKlee` gerado não tem `optnone` (SD-24). + +Gate refeito com os mesmos TSVs (C commitado contra drop-in, 14 linhas em `deltas-aceitos.tsv`): + +``` +deltas: PASS (14 linhas, sd_ref inexistente: nenhum) +keys: PASS (123 C, 123 Rust, só C: 0, só Rust: 0) +reps: PASS (0 chaves com contagem diferente) +verdict: PASS (12 divergências, 12 aceitas) +failed-details: PASS (2 divergências, 2 aceitas) +witness: PASS (0 divergências, 0 aceitas) +expected: PASS (afl 16->17, hybrid 21->25, symex 17->24) +wall: PASS (mediana 0.940, limite 1.10, 123 chaves; desconto de compilação 2.0 s por execução em afl, hybrid, symex sem desconto; mediana bruta 1.197; por gerador: afl 0.931 (bruta 2.948, desconto 2.0 s); hybrid 0.936 (bruta 1.345, desconto 2.0 s); symex 1.004 (bruta 1.004, desconto 0.0 s)) +rss: PASS (mediana 1.021, limite 1.36, 123 chaves) +warning: map2check-assert/byte_add_bug unreach-call hybrid razão rss 2.69 > 1.5 +timeouts: PASS (afl 0->0, hybrid 0->0, symex 0->0) +gate: PASS +``` + +Contra o C do mesmo dia, o resultado também é `gate: PASS`. Nenhuma divergência fica sem aceitação. diff --git a/docs/cutover/gate-2026-10-28.md b/docs/cutover/gate-2026-10-28.md new file mode 100644 index 0000000..ae470d4 --- /dev/null +++ b/docs/cutover/gate-2026-10-28.md @@ -0,0 +1,139 @@ +# Segundo gate diferencial do runtime drop-in (Task 4.2) + +Data: 2026-10-28 (execução de 01:00 a 05:11, horário local). + +**Resultado: `gate: PASS`**, na primeira comparação, sem nenhuma linha nova em `deltas-aceitos.tsv` e sem SD novo. É o segundo gate consecutivo aprovado, depois do de 2026-10-27 (`gate-2026-10-27.md`). + +## Versões + +| | | +|---|---| +| Map2Check `develop` | `0bca42eb89d116178e6b52f52acaf73f7c305088` (`vendor/MAP2CHECK_VERSION`), "Merge pull request #89 from hbgit/feat/testcomp-unified-wrapper" | +| Biblioteca | `fa279fe` (branch `feat/drop-in-runtime`). Os 21 `.bc` do drop-in são byte a byte iguais aos do gate de 2026-10-27. | +| Imagem | `map2check-dev:develop-0bca42eb89d1-time`, o mesmo ID (`f8e1a4c757c8`) de `map2check-dev:develop-3e914528ec57-time`. O `Dockerfile.dev` não mudou, então a imagem foi reaproveitada com uma tag nova, sem rebuild do LLVM/KLEE. | +| Instalação C | `target/map2check-it/c`, reconstruída em `0bca42eb` com `scripts/it/build-map2check.sh` (a de `3e914528` foi guardada em `target/map2check-it/c-3e914528ec57`) | +| Instalação drop-in | `scripts/build-runtime-bc.sh target/map2check-it/g42-bc` (`core checks: 8865 instruction lines (< 9500), 12741 IR lines (< 13600)`), depois `scripts/it/overlay-install.sh target/map2check-it/c target/map2check-it/g42-bc target/map2check-it/g42-ov` | +| Comparador | `scripts/it/compare_baselines.py`, inalterado desde o gate de 2026-10-27 (desconto de compilação de 2,0 s em `afl` e `hybrid`) | + +## Mudança do `develop` + +O `develop` saiu de `3e914528` para `0bca42eb`. O novo clone foi feito conforme a regra 3 do CLAUDE.md (`git clone --depth 1 --branch develop`, depois `rm -rf vendor/map2check/.git`). Ele substituiu a cópia em `vendor/map2check/`. + +O que mudou e o que não mudou: +- **Sem mudança:** `modules/` (incluindo `modules/backend/library` e `modules/frontend`), `CMakeLists.txt`, `cmake/` e `Dockerfile.dev`. +- **Mudou só o empacotamento para BenchExec e Test-Comp.** O wrapper e o tool-info do Test-Comp foram unidos aos do SV-COMP (`utils/map2check-wrapper.py`, `utils/moduleBenchExec/map2check.py`), e os arquivos `*testcomp*` separados foram removidos. Mudaram também `scripts/package-release.sh`, `utils/testcomp-archive/*`, testes de integração e o log de experimentos. +- O `run-corpus.sh` chama o binário `map2check` direto, então esses arquivos não entram nas execuções. + +Instalação C, `0bca42eb` contra `3e914528` (sha256 de todos os 37 arquivos regulares): +- **Byte a byte iguais (36 arquivos):** os 21 `.bc` do runtime e todas as bibliotecas de passes (`lib*.so`). +- **Diferente:** só o binário `map2check`, em 21 bytes. A única diferença em `strings` é a data de build embutida na versão (`v9.0.0-Songbirds:2026-10-26` → `v9.0.0-Songbirds:2026-10-28`). + +## Método + +Igual ao do gate de 2026-10-27: +- `scripts/it/run-corpus.sh` com 41 pares (task, prp), `GENERATORS="afl symex hybrid"`, `TIMEOUT_S=45`, `KILL_MARGIN_S=30`, 5 repetições, 615 execuções por lado. +- C e drop-in intercalados no mesmo dia: para r = 1..5, uma repetição completa do C (`REPS=1`), depois uma do drop-in. A coluna `rep` foi regravada ao juntar os arquivos. +- Cada repetição levou cerca de 26 min no C e 24 min no drop-in. +- Runner: `target/map2check-it/g42/run.sh`. + +O C do mesmo dia, em `0bca42eb`, é a nova linha de base. O gate a usa. + +Arquivos: +- `docs/cutover/baselines/c-0bca42eb89d1.tsv`: linha de base do C em `0bca42eb`, 615 linhas. +- `docs/cutover/baselines/rust-fa279fe2af8c.tsv`: drop-in, 615 linhas. +- `docs/cutover/baselines/c-3e914528ec57.tsv`: a linha de base da Task 0.4, inalterada, usada só para comparação. + +Comandos do comparador, na raiz do repositório: + +```sh +# gate +python3 scripts/it/compare_baselines.py docs/cutover/baselines/c-0bca42eb89d1.tsv docs/cutover/baselines/rust-fa279fe2af8c.tsv --deltas docs/cutover/deltas-aceitos.tsv +# contra a linha de base commitada de 3e914528 +python3 scripts/it/compare_baselines.py docs/cutover/baselines/c-3e914528ec57.tsv docs/cutover/baselines/rust-fa279fe2af8c.tsv --deltas docs/cutover/deltas-aceitos.tsv +# deriva do C entre as duas linhas de base +python3 scripts/it/compare_baselines.py docs/cutover/baselines/c-3e914528ec57.tsv docs/cutover/baselines/c-0bca42eb89d1.tsv --deltas docs/cutover/deltas-aceitos.tsv +``` + +`deltas-aceitos.tsv` é o de `fa279fe`, com 14 linhas, sem mudança. + +## Saída do gate (C `0bca42eb` contra drop-in) + +``` +deltas: PASS (14 linhas, sd_ref inexistente: nenhum) +keys: PASS (123 C, 123 Rust, só C: 0, só Rust: 0) +reps: PASS (0 chaves com contagem diferente) +verdict: PASS (12 divergências, 12 aceitas) +failed-details: PASS (2 divergências, 2 aceitas) +witness: PASS (0 divergências, 0 aceitas) +expected: PASS (afl 16->17, hybrid 21->25, symex 17->24) +wall: PASS (mediana 0.905, limite 1.10, 123 chaves; desconto de compilação 2.0 s por execução em afl, hybrid, symex sem desconto; mediana bruta 1.192; por gerador: afl 0.900 (bruta 2.518, desconto 2.0 s); hybrid 0.804 (bruta 1.328, desconto 2.0 s); symex 1.004 (bruta 1.004, desconto 0.0 s)) +rss: PASS (mediana 1.022, limite 1.36, 123 chaves) +timeouts: PASS (afl 0->0, hybrid 0->0, symex 0->0) +gate: PASS +``` + +Contra a linha de base commitada de `3e914528`, o resultado também é `gate: PASS`: +- veredito, `failed-details`, `witness` e `expected` dão os mesmos números; +- wall: mediana 0,922 (bruta 1,193); +- rss: 1,021; +- timeouts 0→0. + +## Deriva do C entre `3e914528` e `0bca42eb` + +O C de hoje contra a linha de base da Task 0.4 dá `gate: PASS`: +- vereditos por chave idênticos (0 divergências); +- wall bruto mediano 1,000; +- RSS 0,999. + +O comparador emite um aviso de RSS em `map2check-assert/byte_add_bug unreach-call afl` (3,67). É a alternância já descrita no gate de 2026-10-27: o GNU time mede o maior processo da árvore, que às vezes é o `afl-fuzz`. + +Na comparação C contra C, as medianas ajustadas de afl (0,000) e hybrid (0,608) não querem dizer nada. O desconto de 2,0 s é aplicado ao lado "Rust", que aqui também é C. + +## Tempo de parede e RSS + +Razão por chave = mediana(drop-in) / mediana(C) sobre as 5 repetições. A tabela dá a mediana dessas razões. "Ajustada" aplica `max(wall_rust − 2,0 s, 0)` por execução em afl e hybrid. + +| gerador | contra C `0bca42eb`: bruta | ajustada | RSS | contra C `3e914528`: bruta | ajustada | RSS | +|---|---|---|---|---|---|---| +| afl | 2,518 | **0,900** | 1,151 | 2,938 | **0,906** | 1,151 | +| symex | 1,004 | **1,004** | 1,016 | 1,004 | **1,004** | 1,015 | +| hybrid | 1,328 | **0,804** | 1,020 | 1,331 | **0,819** | 1,021 | +| todas | 1,192 | **0,905** | 1,022 | 1,193 | **0,922** | 1,021 | + +Limites: wall ≤ 1,10 e RSS ≤ 1,36. Soma do wall de todas as chaves: C 1534 s, drop-in 1416 s (0,92). Por gerador: afl 188 → 260 s, symex 559 → 460 s, hybrid 786 → 695 s. + +Acima de 1,5: +- **Razão bruta:** 38 chaves, todas afl ou hybrid, nas duas comparações. É o custo fixo de compilação que o desconto cobre. +- **Razão ajustada e RSS:** nenhuma chave nas duas comparações. Por isso a saída do gate não tem avisos. + +## Vereditos + +Vereditos por chave (moda das 5 repetições): + +| gerador | C: FAILED / SUCCEEDED / UNKNOWN / ERROR | drop-in: FAILED / SUCCEEDED / UNKNOWN / ERROR | +|---|---|---| +| afl | 16 / 0 / 24 / 1 | 17 / 0 / 23 / 1 | +| symex | 13 / 5 / 23 / 0 | 16 / 9 / 16 / 0 | +| hybrid | 18 / 4 / 19 / 0 | 18 / 8 / 15 / 0 | + +TIMEOUT: 0 nos dois lados. As contagens são as mesmas do gate de 2026-10-27. + +Estabilidade entre repetições: +- **Drop-in:** nenhuma chave muda veredito, tag, linha, função ou witness. +- **C:** uma execução isolada muda. Em `map2check-assert/ex01_false unreach-call afl`, rep 2, o veredito foi UNKNOWN; as outras 4 deram FAILED `FALSE 5 __VERIFIER_assert`. O AFL++ abortou no dry run ("Test case 'id:000000,time:0,execs:0,orig:seed' results in a timeout", `perform_dry_run()`), e o frontend disse "Could not replicate error". É não determinismo do C ligado à carga do host. A moda continua FAILED, igual à do drop-in, então o comparador não a conta como divergência. + +## Divergências + +São 14: 12 de `verdict` e 2 de `failed-details`. As chaves e os valores são os mesmos de 2026-10-27, e todas aparecem nas 5 repetições. Cada uma bate com uma linha de `deltas-aceitos.tsv`. + +- **(i) Cobertas por SD aprovado, 14:** + - SD-20: `simple-ext`, `ex01_false` e `list-ext`, unreach-call/symex; `960521-1_1-1` valid-memsafety/symex, linha e função. + - SD-24: `memleaks_test10-2` e `memleaks_test3-2`, symex e hybrid. + - SD-25: `ArraysOfVariableLength` symex e hybrid, `960521-1-2` symex, `960521-1_1-2` hybrid. + - SD-26: `list-ext` valid-memsafety/afl. +- **(ii) Não determinismo do C:** nenhuma divergência de chave. Só a execução isolada de `ex01_false afl` descrita acima, que não muda a moda. +- **(iii) Candidatos a bug ou delta não aprovado:** nenhuma. + +## Commit validado + +Com o gate aprovado, `docs/release.md` ("Validated against") passa a citar `0bca42eb89d116178e6b52f52acaf73f7c305088`, o mesmo commit de `vendor/MAP2CHECK_VERSION`. É o que `scripts/test-package-release.sh` exige. diff --git a/docs/cutover/handoff-map2check.md b/docs/cutover/handoff-map2check.md new file mode 100644 index 0000000..07d5579 --- /dev/null +++ b/docs/cutover/handoff-map2check.md @@ -0,0 +1,85 @@ +# Suggestion for the Map2Check maintainer: adopting the Rust runtime + +This is a suggestion document. It describes what the Map2Check project could +change to adopt the `map2check-library` 2.0.0-rc.1 package in place of its C +runtime. The agents and the authors of `map2check-library` take no action in +the Map2Check repository: nothing here has been applied, opened as a pull +request or tested there, and every step is the maintainer's choice. + +## What the package is + +The 21 bitcode modules `lib/*.bc` that `Caller::linkLLVM` links into an +instrumented program, with the same file names, the same C ABI and the same +result files as the C runtime. The frontend, the passes and the generators do +not change. How to install it is in +[`../integration-guide.md`](../integration-guide.md), and the release layout +in [`../release.md`](../release.md). + +## What was validated + +Against Map2Check `develop` `0bca42eb89d116178e6b52f52acaf73f7c305088` (the C +runtime modules are byte for byte the same as at `3e914528ec57`), in two +differential gates on a 41-pair corpus with the `afl`, `symex` and `hybrid` +generators, both `gate: PASS`: + +- [`gate-2026-10-27.md`](gate-2026-10-27.md) (first gate); +- [`gate-2026-10-28.md`](gate-2026-10-28.md) (second gate, with the comparator + command to reproduce it). + +The verdict differences that remain are approved deviations (SD-20, SD-24, +SD-25, SD-26 in [`../semantic-deltas.md`](../semantic-deltas.md)); the +approved compile-time allowance for `afl` and `hybrid` is in +[`decisoes.md`](decisoes.md). Please read those before deciding: on some +corpus tasks the drop-in answers differently from the C runtime. + +**What the gates did not exercise.** The gate corpus has no `no-overflow` +task, `scripts/it/run-corpus.sh` skips the `coverage-*` properties, and no +gate run used `--btree` or `--wasm`. These modes were never compared end to +end through the frontend. They are covered by less direct evidence: + +- `--check-overflow`, `--cover-branches` and wasm: + `scripts/runtime-ir/test-klee-generator.sh` runs `unsafe-overflow.c`, the + cover-branches mode and a wasm program under KLEE in the Map2Check image, + against the C runtime; +- `--btree` and `--wasm`: the link matrix + (`scripts/it/check-link-matrix.sh`, both containers, with and without + `WasmRuntimeStubs`) links and runs every combination; +- the OVERFLOW result files: the legacy goldens + (`tests/legacy_golden/OVERFLOW/`). + +Users of `--check-overflow` rely on this less direct evidence. A later gate +could add an overflow task and a `--btree` pass. + +## Possible changes in Map2Check + +1. **Build.** The C sources under `modules/backend/library/lib/` are compiled + to the 21 `lib/*.bc` by the CMake of that directory. A conservative way to + adopt the package is an opt-in CMake option (off by default, for example + `MAP2CHECK_RUST_RUNTIME`) that, when on, copies the 21 `.bc` of a release + archive into the install's `lib/` instead of building them, after checking + `SHA256SUMS`. Keeping the option off keeps today's build. +2. **Version pin.** Record the `map2check-library` release (tag and the + archive's `SHA256SUMS`) the install uses, the way `MAP2CHECK_VERSION` pins + the Map2Check commit in `map2check-library`. The package's `MAP2CHECK_VERSION` file + says which `develop` commit it was validated on. +3. **CI.** A job that installs the package over a normal build and runs the + regression corpus would catch a future runtime change early. The corpus + and the comparator we used are `scripts/it/run-corpus.sh` and + `scripts/it/compare_baselines.py` in `map2check-library`, as an example. +4. **Removing the C runtime.** Only after the option has run for a release + cycle without unexplained differences, the C sources of the 21 modules + could be dropped and the Rust package become the only runtime. Until then, + keeping both allows going back by copying the C `.bc` files back. +5. **Docker images.** The images that build Map2Check need no Rust toolchain + to use the package, since it ships the `.bc` files. A Rust toolchain + (rustc 1.72.1 with LLVM 16 tools, the bitcode toolchain of `build-runtime-bc.sh`, distinct from the toolchain pinned in `rust-toolchain.toml`) is needed only to rebuild them with + `scripts/build-runtime-bc.sh`. + +## What could change for users + +- Verdicts on the tasks listed by the approved deviations. +- Wall time of `afl` and `hybrid` runs within the gate's limits (median + 0.905 of the C's with the 2.0 s allowance, second gate). +- The result files, the witness files and the stderr diagnostics are meant to + match the C runtime's; a difference there would be a bug to report in + `map2check-library`. diff --git a/docs/cutover/symbol-matrix.tsv b/docs/cutover/symbol-matrix.tsv deleted file mode 100644 index b279924..0000000 --- a/docs/cutover/symbol-matrix.tsv +++ /dev/null @@ -1,73 +0,0 @@ -symbol core_symbol in_rust_header -map2check_add_store_pointer map2check_map_store_pointer 1 -map2check_alloca map2check_map_alloca 1 -map2check_assert map2check_is_valid_assert 1 -map2check_assume __VERIFIER_assume 1 -map2check_assume_loop __VERIFIER_assume 1 -map2check_binop_add map2check_binop_add_signed 1 -map2check_binop_add_uint - 1 -map2check_binop_mul map2check_binop_mul_signed 1 -map2check_binop_mul_uint - 1 -map2check_binop_sdiv map2check_binop_div_signed 1 -map2check_binop_srem map2check_binop_rem_signed 1 -map2check_binop_sub map2check_binop_sub_signed 1 -map2check_binop_sub_uint - 1 -map2check_calloc map2check_map_calloc 1 -map2check_check_deref - 1 -map2check_crab_assume __VERIFIER_assume 1 -map2check_free map2check_check_free+map2check_map_free 1 -map2check_free_resolved_address map2check_check_free_resolved_address 1 -map2check_function map2check_map_funct_address 1 -map2check_init map2check_set_mode+map2check_init 1 -map2check_load map2check_check_load 1 -map2check_malloc map2check_map_malloc 1 -map2check_non_det_bool __VERIFIER_nondet_bool 1 -map2check_nondet_bool map2check_save_nondet_log_bool 1 -map2check_non_det_char __VERIFIER_nondet_char 1 -map2check_nondet_char map2check_save_nondet_log_char 1 -map2check_non_det_double __VERIFIER_nondet_double 1 -map2check_nondet_double map2check_save_nondet_log_double 1 -map2check_non_det_int __VERIFIER_nondet_int 1 -map2check_nondet_int map2check_save_nondet_log_int 1 -map2check_non_det_loff_t __VERIFIER_nondet_loff_t 1 -map2check_nondet_loff_t map2check_save_nondet_log_loff_t 1 -map2check_non_det_long __VERIFIER_nondet_long 1 -map2check_nondet_long map2check_save_nondet_log_long 1 -map2check_non_det_pchar __VERIFIER_nondet_pchar 1 -map2check_nondet_pchar map2check_save_nondet_log_pchar 1 -map2check_non_det_pointer __VERIFIER_nondet_pointer 1 -map2check_nondet_pointer map2check_save_nondet_log_pointer 1 -map2check_non_det_sector_t __VERIFIER_nondet_sector_t 1 -map2check_nondet_sector_t map2check_save_nondet_log_sector_t 1 -map2check_non_det_short __VERIFIER_nondet_short 1 -map2check_nondet_short map2check_save_nondet_log_short 1 -map2check_non_det_size_t __VERIFIER_nondet_size_t 1 -map2check_nondet_size_t map2check_save_nondet_log_size_t 1 -map2check_non_det_uchar __VERIFIER_nondet_uchar 1 -map2check_nondet_uchar map2check_save_nondet_log_uchar 1 -map2check_non_det_uint __VERIFIER_nondet_uint 1 -map2check_nondet_uint map2check_save_nondet_log_uint 1 -map2check_non_det_ulong __VERIFIER_nondet_ulong 1 -map2check_nondet_ulong map2check_save_nondet_log_ulong 1 -map2check_non_det_unsigned __VERIFIER_nondet_uint 1 -map2check_nondet_unsigned map2check_save_nondet_log_unsigned 1 -map2check_non_det_ushort __VERIFIER_nondet_ushort 1 -map2check_nondet_ushort map2check_save_nondet_log_ushort 1 -map2check_non_static_alloca map2check_map_non_static_alloca 1 -map2check_posix map2check_map_posix_memalign 1 -map2check_success map2check_success 1 -map2check_target_function map2check_reach_target 1 -map2check_track_bb map2check_save_basic_block_log 1 -map2check_wasm_check_access map2check_wasm_check_access 0 -map2check_wasm_free map2check_wasm_free 0 -map2check_wasm_malloc map2check_wasm_malloc 0 -overflowError - 1 -__VERIFIER_assert __VERIFIER_assert 1 -__VERIFIER_assume __VERIFIER_assume 1 -__VERIFIER_error __VERIFIER_error 1 -__VERIFIER_nondet_char __VERIFIER_nondet_char 1 -__VERIFIER_nondet_int __VERIFIER_nondet_int 1 -__VERIFIER_nondet_long __VERIFIER_nondet_long 1 -__VERIFIER_nondet_pointer __VERIFIER_nondet_pointer 1 -__VERIFIER_nondet_uint __VERIFIER_nondet_uint 1 -__VERIFIER_nondet_ushort __VERIFIER_nondet_ushort 1 diff --git a/docs/integration-guide.md b/docs/integration-guide.md index ce3cea1..f7b553a 100644 --- a/docs/integration-guide.md +++ b/docs/integration-guide.md @@ -1,108 +1,242 @@ -# Integration guide — linking `libmap2check.a` from C - -This guide covers what a C program or an instrumentation pass needs to use -the Rust `map2check-library`: the release contents, how to link, call -order, the JSON report and the known gaps. Compatibility details are in +# Integration guide — the drop-in runtime for Map2Check + +This guide covers what a Map2Check maintainer or a C program needs to use +the Rust `map2check-library` **2.0.0-rc.1**: what the release ships, how to +install the 21 bitcode modules in place of the C runtime's, the Map2Check +commit they were validated against, how to reproduce the validation, and how +to link the C ABI by hand. Paths of the form `map2check-library: ` and the `scripts/` and +`docs/cutover/` files named below are in the source repository, not in the +release archive, which ships only this guide, `api-deltas.md` and +`semantic-deltas.md` under `docs/`. Compatibility details are in [`api-deltas.md`](api-deltas.md) (symbols and signatures) and -[`semantic-deltas.md`](semantic-deltas.md) (behaviour). +[`semantic-deltas.md`](semantic-deltas.md) (behaviour). The steps a +maintainer would take to adopt the package are suggested in +`map2check-library: docs/cutover/handoff-map2check.md`. + +**What the library is (D6, 2.0.0).** The library exports the C runtime ABI of +Map2Check `develop`: the names its core modules own and the `__m2c_rt_*` +entry points that the generated IR modules (`scripts/runtime-ir/gen_ll.py`) +forward to. Together with those IR modules it is the 21 `.bc` files that +`Caller::linkLLVM` links into an instrumented program, so Map2Check runs +unchanged with them. It is not a library with its own API next to the +runtime: the 1.x core API (`map2check_set_mode`, `map2check_map_*`, +`print_all_containers_as_json`...) and the `libfuzzer` and `klee` features +left the C ABI (`CHANGELOG.md`). Those functions remain Rust functions of +`map2check::ffi` for the Rust tests, and the JSON report below belongs to +that Rust API. ## Release contents A release archive `map2check-library--.tar.gz` (see -[`release.md`](release.md)) unpacks to: +`map2check-library: docs/release.md`) unpacks to: ``` map2check-library--/ -├── include/map2check.h # C header; MAP2CHECK_VERSION == -├── lib/libmap2check.a # plain C programs (no features) -├── lib/libmap2check_libfuzzer.a # LibFuzzer targets (feature libfuzzer) -├── lib/libmap2check_runtime.a # runtime profile, features libfuzzer,wasm +├── include/map2check.h # C header; MAP2CHECK_VERSION == +├── lib/*.bc # the 21 runtime modules (below) +├── lib/libmap2check.a # no features +├── lib/libmap2check_runtime.a # runtime profile, feature wasm +├── MAP2CHECK_VERSION # Map2Check develop commit validated +├── SHA256SUMS # sha256 of every other file ├── docs/integration-guide.md ├── docs/api-deltas.md ├── docs/semantic-deltas.md ├── CHANGELOG.md -└── LICENSE # GPL-3.0-only +└── LICENSE # GPL-3.0-only ``` -A `SHA256SUMS` file next to the archive covers it. +The 21 modules are listed in `scripts/runtime-modules.txt` (`Map2CheckFunctions`, +`TrackBBLog`, `NonDetLog`, `PropertyGenerator`, `ContainerRealloc`, +`ContainerBTree`, `BTree`, `AnalysisModeMemtrack`, `AnalysisModeMemcleanup`, +`AnalysisModeOverflow`, `AnalysisModeAssert`, `AnalysisModeNone`, +`AllocationLog`, `ListLog`, `HeapLog`, `NonDetGeneratorNone`, +`NonDetGeneratorKlee`, `NonDetGeneratorAFL`, `WasmRuntimeStubs`, +`WitnessGeneration`, `WitnessGenerationNone`), each as `.bc`. The +layout and the reasons for it are in +`map2check-library: docs/cutover/d7-layout.md`. -## Building from source +## Validated Map2Check commit -Without a release archive, run this from the repository root: +The package was validated against Map2Check `develop` commit +`0bca42eb89d116178e6b52f52acaf73f7c305088`, recorded in +`vendor/MAP2CHECK_VERSION` and shipped as `MAP2CHECK_VERSION`. The 21 C +runtime modules are byte for byte the same at `0bca42eb` and at the commit of +the first gate (`3e914528ec57`). Use a Map2Check install built from that +commit, or from one whose `lib/*.bc` are identical to it. Other commits were +not tested. + +## Installing the 21 modules in place of the C ones + +Verify the archive, then either overlay a copy or patch in place: ```sh -cargo build --release # plain C programs -cargo build --release --features libfuzzer # LibFuzzer targets (overwrites the same .a) +tar -xzf map2check-library--.tar.gz +cd map2check-library-- +sha256sum -c SHA256SUMS # every file must say OK +cd .. + +# a copy of an existing install with only lib/*.bc replaced (recommended) +scripts/it/overlay-install.sh map2check-library--/lib + +# or in place: copy the modules over the C ones +cp map2check-library--/lib/*.bc "${MAP2CHECK_PATH}/lib/" ``` -The build Map2Check links is the `runtime` profile (release optimisations -with `panic = "unwind"`, so the FFI guard can recover panics): +`` is a Map2Check install whose `lib/` holds the 21 C modules +(for example the output of `scripts/it/build-map2check.sh`). The overlay +script is in this repository, not in the archive. It leaves `` +untouched, replaces only `lib/*.bc` (`bin/`, the pass plugins and every +symlink stay as they are), refuses a `` that is not exactly the 21 +module names (the `.a` files of the archive's `lib/` are ignored), and writes +the marker `.map2check-overlay` with the sha256 of the 21 modules. The +in-place copy has no such checks: do not mix modules of two builds, and +back up the C ones first if you want to go back. + +The frontend then links the program with the Rust runtime and nothing else +changes. Whether a run writes the witness files depends only on the witness +module it links (`WitnessGeneration.bc` or `WitnessGenerationNone.bc`), as in +the C runtime. + +## Reproducing the validation + +Two differential gates compared the C runtime and the drop-in on a +41-pair corpus (`afl`, `symex` and `hybrid` generators, 5 repetitions, 615 +runs per side), with `scripts/it/compare_baselines.py`. Both finished with +`gate: PASS`: + +| Gate | Map2Check `develop` | Report | Baseline | +|---|---|---|---| +| 1 (2026-10-27, accepted 2026-10-28) | `3e914528ec57` | `map2check-library: docs/cutover/gate-2026-10-27.md` | `docs/cutover/baselines/c-3e914528ec57.tsv` | +| 2 (2026-10-28) | `0bca42eb89d1` | `map2check-library: docs/cutover/gate-2026-10-28.md` | `docs/cutover/baselines/c-0bca42eb89d1.tsv` | + +The reports hold the exact comparator commands and outputs; this is the +second gate's: ```sh -cargo build --profile runtime --features libfuzzer,wasm # target/runtime/libmap2check.a +python3 scripts/it/compare_baselines.py docs/cutover/baselines/c-0bca42eb89d1.tsv docs/cutover/baselines/rust-fa279fe2af8c.tsv --deltas docs/cutover/deltas-aceitos.tsv ``` -The runtime of Map2Check's KLEE (`symex`) generator is one LLVM 16 bitcode -module built from `klee/Cargo.toml` with rustc 1.72.1 and the LLVM 16 tools -(`opt-16`, `llvm-dis-16`, ...). It is not part of the release archive: +To produce new measurements, from the repository root (Docker needed, and +a clone of Map2Check `develop` in `vendor/map2check/`, which +`scripts/fetch-map2check.sh` makes and `.gitignore` keeps out of git): ```sh -scripts/build-klee-runtime.sh out/map2check_klee.bc +scripts/it/build-map2check.sh # image and the C install: target/map2check-it/c +scripts/build-runtime-bc.sh target/map2check-it/bc # exactly the 21 drop-in modules +scripts/it/overlay-install.sh target/map2check-it/c target/map2check-it/bc target/map2check-it/ov +GENERATORS="afl symex hybrid" TIMEOUT_S=45 KILL_MARGIN_S=30 REPS=5 \ + scripts/it/run-corpus.sh target/map2check-it/c c.tsv +GENERATORS="afl symex hybrid" TIMEOUT_S=45 KILL_MARGIN_S=30 REPS=5 \ + scripts/it/run-corpus.sh target/map2check-it/ov rust.tsv +python3 scripts/it/compare_baselines.py c.tsv rust.tsv --deltas docs/cutover/deltas-aceitos.tsv +``` + +The gate reports interleave C and drop-in repetitions on the same day; do the +same, because wall time depends on the host load. + +What passing means: no verdict, failed-detail or witness difference that is +not an approved deviation (`docs/cutover/deltas-aceitos.tsv`, each row tied to +an `SD-NN` of [`semantic-deltas.md`](semantic-deltas.md)), the median wall +time of the drop-in at most 1.10 times the C's, the median peak memory +at most 1.36 times, and no new timeouts. For `afl` and `hybrid` the wall time +discounts up to 2.0 s of fixed compile time per run (the approved allowance +in `map2check-library: docs/cutover/decisoes.md`; it is not an SD): the drop-in +core is compiled with the program, and the median stays below the limit with +it (0.905 on the second gate; 1.192 without it). The deviations that the +gates accepted are SD-20, SD-24, SD-25 and SD-26. + +## Building from source + +Without a release archive, run this from the repository root: + +```sh +cargo build --release # no features +scripts/build-runtime-bc.sh # the 21 lib/*.bc, deterministic +``` + +`build-runtime-bc.sh` builds the Rust core from `klee/Cargo.toml` with +rustc 1.72.1 and the LLVM 16 tools, plus the IR modules, and fails when the +core exceeds 9 500 instruction lines or 13 600 IR lines (the frontend compiles +the program with the core three times with `afl-clang-fast`, so its size is +compile time). `scripts/package-release.sh` makes the archive from them. + +The `runtime` profile (release optimisations with `panic = "unwind"`, so the +FFI guard can recover panics) is what `libmap2check_runtime.a` is: + +```sh +cargo build --profile runtime --features wasm # target/runtime/libmap2check.a ``` `build.rs` regenerates `include/map2check.h` on every build. The header is not committed (`.gitignore`), so always take it from the same build as the library. -## Linking +## Linking a C program by hand ```sh -cc -I include my_program.c lib/libmap2check.a -ldl -lpthread -lm -o my_program +scripts/build-c-abi-program.sh -o my_program -m Map2CheckFunctions-glue -m NonDetGeneratorAFL my_program.c +``` + +This is what `tests/ffi_harness.c` uses, and what Map2Check does with its +passes. Without the script, the pieces are: + +```sh +cc -I include my_program.c lib/libmap2check.a -ldl -lpthread -lm -o my_program ``` - `-ldl -lpthread -lm` are the system libraries that the Rust standard library needs. -- Use `lib/libmap2check.a` for ordinary programs. It does not define the - `__VERIFIER_*` functions or `LLVMFuzzerTestOneInput`, even though the - header declares them. -- Use `lib/libmap2check_libfuzzer.a` for LibFuzzer targets. It defines - `LLVMFuzzerTestOneInput`, which calls `int __map2check_main__(void)`. The - program must define that function and be built with - `clang -fsanitize=fuzzer`. Without that function, the link fails with - `undefined reference to '__map2check_main__'`. +- The C names that an IR module defines (`map2check_malloc`, + `map2check_assert`, `analysis_init`, `map2check_non_det_`, `main` of a + generator...) are not in `libmap2check.a`: link the module's `.ll` (or + `.bc`) too, as `scripts/build-c-abi-program.sh -m ` does. - The header and the library must come from the same build. The header carries the version as a string, `MAP2CHECK_VERSION` (for example - `"1.1.0-rc1"`), so you can log it or check it in your build: + `"2.0.0-rc.1"`), so you can log it or check it in your build: ```c #include "map2check.h" #ifndef MAP2CHECK_VERSION - #error "map2check.h is older than 1.1.0-rc1" + #error "map2check.h is older than 2.0.0-rc.1" #endif ``` ## Call order -1. `map2check_init()` once, before any other call. -2. Tracking calls as the program runs: `map2check_save_basic_block_log`, - `map2check_save_nondet_log_*`, `map2check_map_*`. -3. Check calls next to the operation they guard: `map2check_check_load`, - `map2check_check_free`, `map2check_check_deref`, `map2check_binop_*`, - `map2check_is_valid_assert`. -4. Optionally, `map2check_set_memcleanup()` and then - `map2check_check_mem_endprog()` at exit. -5. `map2check_success()` or `print_all_containers_as_json()` to emit the - report; `get_current_property()` reads the verdict without printing. -6. `map2check_reset()` to analyse another input in the same process. - LibFuzzer builds do this on every iteration. - -A violation does not stop the program, but the first violation is final: -later checks, safe or not, and later `set_false_result` calls leave it in -place (SD-03). Only `map2check_init()`, `map2check_reset()` and -`vcc_reset_meta_data()` clear it. +The call order is the C runtime's, and the Map2Check passes and the IR +modules make the calls; a program that links the runtime by hand follows it: + +1. `map2check_init(0)` once, before any other call (the argument is the C + runtime's `isSvComp`, ignored). + + 1b. Then `analysis_init()` of the linked analysis mode module and + `nondet_init()` of the linked generator module, in that order. The + `Map2CheckFunctions.bc` core of a Map2Check install makes these two + calls inside `map2check_init`, as the C runtime does; the staticlib + `libmap2check.a` does not, because it cannot reference names that only + the IR modules define. Without them the staticlib runs with the mode + unset: it writes no `map2check_property*` file, never aborts on a + violation, and prints the JSON report at exit. `tests/ffi_harness.c` + calls the mode's init for this reason. +2. `map2check_track_bb` and the `map2check_nondet_` calls as the program + runs, and the check calls of the analysis mode module linked (the names + of the C runtime, defined by the IR modules; `api-deltas.md` lists + them). +3. `map2check_success()` at the end of a program that held its property, + `map2check_error()` or `map2check_exit()` when it did not. With an + analysis mode selected the verdict goes to the `map2check_property*` + files of the C runtime; with the none mode `map2check_success()` prints + the JSON report below. + +The first violation is final. `semantic-deltas.md` says where the behaviour +differs from the C runtime and from 1.x. ## JSON report +This is the report of the Rust API (`map2check::output`), kept for the Rust tests +and for `map2check_success()` with no analysis mode. `print_all_containers_as_json()` +and the calls below are no longer C exports (D6). + `print_all_containers_as_json()` prints one pretty-printed JSON object on stdout when no analysis mode is set (with a mode set, a Map2Check run, it prints nothing; see below). Whitespace is not part of the contract; parse it. The examples @@ -207,24 +341,27 @@ own schema, pinned by a snapshot test (#14, SD-01). ## Result files for Map2Check -A Map2Check-instrumented program calls `map2check_set_mode()` before -`map2check_init()`, and the witness binary also calls -`map2check_enable_witness()`. With a mode set, the library writes into the -current directory the files the Map2Check frontend reads, byte for byte as -the C runtime of Map2Check `develop` does: `map2check_property*`, and with -witness on `track_bb_log.st`, `heap_log.csv`, `allocation_log.csv`, -`list_log.csv`, `klee_log.csv` and `map2check_checked_error`. The first -violation ends the process with `abort()`; `map2check_success()` returns -when the end-of-program verdict holds. Without a mode nothing is written -and nothing aborts. See “Result files” in `api-deltas.md`. +The analysis mode and generator modules linked into the program select what +the runtime writes, as in the C runtime of Map2Check `develop`. In a +Map2Check run the library writes into the current directory the files the +frontend reads, byte for byte as the C runtime does (`map2check_property*`, +and with the witness module `WitnessGeneration.bc` linked `track_bb_log.st`, +`heap_log.csv`, `allocation_log.csv`, `list_log.csv`, `klee_log.csv` and +`map2check_checked_error`). The first violation ends the process, and +`map2check_success()` returns when the end-of-program verdict holds. With the +none mode nothing is written. The `map2check::ffi` functions that the +paragraph on the JSON report names set a mode directly, for the Rust tests. +See “Result files” in `api-deltas.md` and SD-20 for how the files are written. ## Known gaps -- **Map2Check passes:** the passes of `hbgit/Map2Check@develop` insert a - different symbol family (`map2check_track_bb`, `map2check_alloca`, …) and - cannot link against this library yet. See the pass matrix in - `api-deltas.md` and #21. -- **KLEE:** no KLEE build exists yet; the decision is #20. +- **Validated commit only:** the gates ran on Map2Check `develop` + `0bca42eb` (and `3e914528ec57`, same C runtime modules). A later + `develop` that changes the runtime modules or the frontend needs a new + gate. +- **KLEE and AFL++ differences:** the accepted deviations SD-20, SD-24, SD-25 + and SD-26 change verdicts on a few corpus tasks (the gate reports list the + exact keys). - **Thread safety:** the state sits behind one global mutex. The calls are safe from any thread but serialise, and a callback must not re-enter the library while a call is running. diff --git a/docs/klee-feasibility.md b/docs/klee-feasibility.md index 86ed70d..a5daa28 100644 --- a/docs/klee-feasibility.md +++ b/docs/klee-feasibility.md @@ -2,6 +2,8 @@ Data: 2026-10-16. Decisão de origem: D1, rota A′ (`docs/cutover/decisoes.md`), com a evidência do spike em `docs/cutover/spike-llvm16.md`. +**2.0.0 (Task 2.10, D6):** registro histórico. A feature `klee`, `scripts/build-klee-runtime.sh`, `tests/klee_paths.sh` e os programas de `tests/klee/` saíram com a API núcleo 1.x; ficaram as fixtures dos autotestes do pós-processamento (`scripts/test-klee-postprocess.sh`). O gerador `symex` do runtime drop-in é o módulo IR `NonDetGeneratorKlee.bc` com o núcleo Rust da feature `dropin` (`scripts/build-runtime-bc.sh`, `scripts/runtime-ir/test-klee-generator.sh`). + **Decisão: GO.** O gerador `symex` do Map2Check usa o runtime Rust: as fontes de `rust/src`, compiladas por rustc 1.72.1 (LLVM 16.0.5) como um módulo de bitcode, ligado ao programa analisado com `llvm-link-16` e executado no KLEE 3.1. Nos seis programas de `tests/klee/`, o runtime Rust explora o mesmo número de caminhos que o runtime C de `develop`, ou menos, e chega aos mesmos veredictos. O vetor do KLEE, convertido em semente como o frontend faz, leva o leitor AFL++/libFuzzer do Rust ao mesmo ramo. ## O produto diff --git a/docs/release.md b/docs/release.md index d0bed3f..d774cbe 100644 --- a/docs/release.md +++ b/docs/release.md @@ -1,9 +1,11 @@ # Release process A release publishes one archive per target with the generated C header, -three static libraries, the integration guide and the compatibility docs. -`scripts/package-release.sh` builds it, and CI runs the same script on -every push through `scripts/test-package-release.sh`. +two static libraries, the 21 bitcode modules of the drop-in runtime, the +validated Map2Check commit, an inner checksum file, the integration guide +and the compatibility docs. `scripts/package-release.sh` builds it, and CI +runs the same script on every push through +`scripts/test-package-release.sh`. ## Archive layout @@ -14,20 +16,48 @@ from `Cargo.toml` and `` is the `rustc` host triple (for example | Path in the archive | Source | |---|---| | `include/map2check.h` | Generated by `build.rs`; `MAP2CHECK_VERSION` must equal ``. | -| `lib/libmap2check.a` | `cargo build --release` (no features), for plain C programs. | -| `lib/libmap2check_libfuzzer.a` | `cargo build --release --features libfuzzer`, for LibFuzzer targets; the program must define `int __map2check_main__(void)`. | -| `lib/libmap2check_runtime.a` | `cargo build --profile runtime --features libfuzzer,wasm`: the build Map2Check links (unwinding panics, LibFuzzer and wasm2c runtime). | +| `lib/libmap2check.a` | `cargo build --release` (no features). | +| `lib/.bc` (21 files) | `scripts/build-runtime-bc.sh`: exactly the modules `Caller::linkLLVM` links, no others (list in `docs/cutover/d7-layout.md`). | +| `MAP2CHECK_VERSION` | Copy of `vendor/MAP2CHECK_VERSION`: the Map2Check `develop` commit the runtime was validated against. | +| `SHA256SUMS` | Inside the archive: `sha256sum` of every other file, relative paths. | +| `lib/libmap2check_runtime.a` | `cargo build --profile runtime --features wasm` (unwinding panics and the wasm2c entry points). | | `docs/integration-guide.md`, `docs/api-deltas.md`, `docs/semantic-deltas.md` | Repository. | | `CHANGELOG.md`, `LICENSE` | Repository. | -The KLEE runtime bitcode is not in the archive: it needs rustc 1.72.1 and -LLVM 16 tools, so it is built separately with -`scripts/build-klee-runtime.sh ` (see the integration guide). +Since 2.0.0 (D6) both libraries export the C runtime ABI of Map2Check +`develop` only (`scripts/check-export-set.sh`); the `libfuzzer` library and +the KLEE runtime bitcode (`scripts/build-klee-runtime.sh`) are gone. The +bitcode modules of the drop-in runtime are in the archive (`lib/*.bc`). -`dist/SHA256SUMS` sits next to the archive. There are two libraries -because the `libfuzzer` build references `__map2check_main__`. Without -that symbol in the program, linking the `libfuzzer` library fails with -`undefined reference to '__map2check_main__'`. +`dist/SHA256SUMS` sits next to the archive. + +## Validated against + +Map2Check `develop` commit `0bca42eb89d116178e6b52f52acaf73f7c305088`, the +`commit=` line of `vendor/MAP2CHECK_VERSION`, shipped in the archive as +`MAP2CHECK_VERSION`. `scripts/test-package-release.sh` fails when this line +and `vendor/MAP2CHECK_VERSION` differ, so change them together. + +## Install over a Map2Check develop install + +The `.bc` files replace the 21 modules of a Map2Check install built from the +commit above. `scripts/it/overlay-install.sh` looks only at the `*.bc` files +of the directory it is given (the `.a` files in the archive's `lib/` are +ignored) and still demands exactly the 21 module names among them, so the +extracted `lib/` can be passed as it is: + +```sh +tar -xzf map2check-library--.tar.gz +cd map2check-library-- +sha256sum -c SHA256SUMS # every file must say OK +cd .. +scripts/it/overlay-install.sh map2check-library--/lib +``` + +`` is left untouched; `` is a copy of it with only +`lib/*.bc` replaced, plus a `.map2check-overlay` marker listing the sha256 of +the 21 modules. To patch in place instead, copy `lib/*.bc` of the +archive over `/lib/`. ## Steps @@ -47,7 +77,7 @@ that symbol in the program, linking the `libfuzzer` library fails with 5. **Package.** On the merged commit, run: ```sh - scripts/package-release.sh # writes dist/ + BC_DIR= scripts/package-release.sh # writes dist/ (cd dist && sha256sum -c SHA256SUMS) ``` @@ -75,3 +105,5 @@ that symbol in the program, linking the `libfuzzer` library fails with `SHA256SUMS` only covers the newest one. Steps 6 and 7 publish outside the repository; the maintainer runs them. +Process note: push, tag, pull request and release happen only on the +maintainer's explicit request; building the package never does any of them. diff --git a/docs/runtime-abi/export-policy.tsv b/docs/runtime-abi/export-policy.tsv new file mode 100644 index 0000000..f134142 --- /dev/null +++ b/docs/runtime-abi/export-policy.tsv @@ -0,0 +1,78 @@ +symbol side reason +allocation_log c-only C global of AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +allocation_log_to_file c-only C function of AllocationLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +append_element c-only C function of ContainerBTree.bc+ContainerRealloc.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +B_TREE_CREATE c-only C function of BTree.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +B_TREE_FREE c-only C function of BTree.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +B_TREE_INSERT c-only C function of BTree.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +B_TREE_INSERT_NONFULL c-only C function of BTree.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +B_TREE_PAGE_CREATE c-only C function of BTree.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +B_TREE_SEARCH c-only C function of BTree.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +B_TREE_SPLIT_CHILD c-only C function of BTree.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +check_address_allocation_log c-only C function of AllocationLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +CHECK_AND_RELEASE_PAGES c-only C function of BTree.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +DISK_READ c-only C function of BTree.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +DISK_WRITE c-only C function of BTree.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +divisionByZeroError c-only C function of AnalysisModeOverflow.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +DumpTree c-only C function of BTree.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +DumpTreeHelper c-only C function of BTree.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +DumpTreePage c-only C function of BTree.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +DumpTreePageChildren c-only C function of BTree.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +ERROR_DEREF c-only C global of AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +find_row_with_address c-only C function of AllocationLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +free_container c-only C function of ContainerBTree.bc+ContainerRealloc.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +get_element_at c-only C function of ContainerBTree.bc+ContainerRealloc.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +get_old_reference c-only C function of ListLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +get_type_from_list_log_row c-only C function of ListLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +gotError c-only C global of Map2CheckFunctions.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +heap_log c-only C global of AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +heap_log_file c-only C global of HeapLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +heap_log_to_file c-only C function of HeapLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +helper_map2check_nondet_append_element c-only C function of NonDetLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +is_deref_error c-only C function of ListLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +is_in_tracked c-only C function of TrackBBLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +is_invalid_free c-only C function of ListLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +is_memcleanup_error c-only C function of ListLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +is_valid_allocation_address c-only C function of AllocationLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +is_valid_heap_address c-only C function of HeapLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +klee_log_file c-only C global of NonDetLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +list_log c-only C global of AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +list_log_file c-only C global of ListLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +list_log_to_file c-only C function of ListLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +Map2CheckCurrentStep c-only C global of Map2CheckFunctions.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +map2check_nondet_get_log c-only C function of NonDetLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +mark_allocation_log c-only C function of AllocationLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +mark_deallocation_log c-only C function of AllocationLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +mark_heap_log c-only C function of HeapLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +new_container c-only C function of ContainerBTree.bc+ContainerRealloc.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +new_heap_row c-only C function of HeapLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +new_list_row c-only C function of ListLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +new_memory_row c-only C function of AllocationLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +new_nondet_call c-only C function of NonDetLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +nondet_log c-only C global of NonDetLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +nondet_log_to_file c-only C function of NonDetLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +release_unreleased_addresses c-only C function of AllocationLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +sv_comp c-only C global of Map2CheckFunctions.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +trackbb_log c-only C global of Map2CheckFunctions.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +trackbb_log_file c-only C global of TrackBBLog.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +trackbb_log_to_file c-only C function of TrackBBLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +trackbb_new_row c-only C function of TrackBBLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +unknown_not_supported c-only C function of AnalysisModeOverflow.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +update_reference_list_log c-only C function of AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +valid_allocation_log c-only C function of AllocationLog.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +violated_property_file c-only C global of PropertyGenerator.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +violated_property_file_assert c-only C global of PropertyGenerator.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +violated_property_file_deref c-only C global of PropertyGenerator.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +violated_property_file_free c-only C global of PropertyGenerator.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +violated_property_file_memcleanup c-only C global of PropertyGenerator.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +violated_property_file_memtrack c-only C global of PropertyGenerator.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +violated_property_file_overflow c-only C global of PropertyGenerator.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +violated_property_file_unknown c-only C global of PropertyGenerator.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +write_property_assert c-only C function of PropertyGenerator.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +write_property_deref c-only C function of PropertyGenerator.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +write_property_free c-only C function of PropertyGenerator.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +write_property_memcleanup c-only C function of PropertyGenerator.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +write_property_memtrack c-only C function of PropertyGenerator.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +write_property_overflow c-only C function of PropertyGenerator.bc used by nobody outside its .c; the Rust core keeps it private (D7 approval item 2) +write_property_unknown c-only C function of PropertyGenerator.bc used by only the C runtime itself; the Rust core keeps it private (D7 approval item 2) +__m2c_rt_* rust-only Rust core entry points the IR forwarders call (D7 layout A-fwd), and __m2c_rt_witness_enable for host programs (Task 2.10); with the core names of inventory.tsv, the whole exported set (scripts/check-export-set.sh) diff --git a/docs/runtime-abi/inventory.tsv b/docs/runtime-abi/inventory.tsv new file mode 100644 index 0000000..724e2c9 --- /dev/null +++ b/docs/runtime-abi/inventory.tsv @@ -0,0 +1,193 @@ +symbol kind signature canonical_signature module referenced_by +B_TREE_CREATE func B_TREE (const char *) struct B_TREE (const char *) BTree.bc runtime +B_TREE_FREE func void (B_TREE *) void (struct B_TREE *) BTree.bc runtime +B_TREE_INSERT func char (B_TREE *, B_TREE_ROW *) char (struct B_TREE *, struct B_TREE_ROW *) BTree.bc runtime +B_TREE_INSERT_NONFULL func char (B_TREE *, B_TREE_ROW *, B_TREE_PAGE *) char (struct B_TREE *, struct B_TREE_ROW *, struct B_TREE_PAGE *) BTree.bc - +B_TREE_PAGE_CREATE func B_TREE_PAGE *(B_TREE *) struct B_TREE_PAGE *(struct B_TREE *) BTree.bc - +B_TREE_SEARCH func B_TREE_ROW *(B_TREE *, unsigned int) struct B_TREE_ROW *(struct B_TREE *, unsigned int) BTree.bc runtime +B_TREE_SPLIT_CHILD func char (B_TREE *, B_TREE_PAGE *, int, B_TREE_PAGE *) char (struct B_TREE *, struct B_TREE_PAGE *, int, struct B_TREE_PAGE *) BTree.bc - +CHECK_AND_RELEASE_PAGES func void (B_TREE *) void (struct B_TREE *) BTree.bc - +DISK_READ func char (B_TREE *, fpos_t *, B_TREE_PAGE *) char (struct B_TREE *, struct _G_fpos_t *, struct B_TREE_PAGE *) BTree.bc - +DISK_WRITE func char (B_TREE *, B_TREE_PAGE *) char (struct B_TREE *, struct B_TREE_PAGE *) BTree.bc - +DumpTree func void (B_TREE *) void (struct B_TREE *) BTree.bc - +DumpTreeHelper func void (B_TREE *, unsigned int, B_TREE_PAGE *) void (struct B_TREE *, unsigned int, struct B_TREE_PAGE *) BTree.bc - +DumpTreePage func void (B_TREE_PAGE *) void (struct B_TREE_PAGE *) BTree.bc - +DumpTreePageChildren func void (B_TREE_PAGE *) void (struct B_TREE_PAGE *) BTree.bc - +ERROR_DEREF data char char AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - +Map2CheckCurrentStep data unsigned int unsigned int Map2CheckFunctions.bc - +__VERIFIER_assert func void (int) void (int) AnalysisModeAssert.bc pass+frontend +__VERIFIER_error func void () void (void) Map2CheckFunctions.bc pass+frontend +__afl_fuzz_alt data unsigned char[1048576] unsigned char[1048576] NonDetGeneratorAFL.bc external +__afl_fuzz_alt_ptr data unsigned char * unsigned char * NonDetGeneratorAFL.bc external +__afl_sharedmem_fuzzing data int int NonDetGeneratorAFL.bc external +allocation_log data MAP2CHECK_CONTAINER struct obj4 AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - +allocation_log_to_file func void (MAP2CHECK_CONTAINER *) void (struct obj4 *) AllocationLog.bc runtime +analysis_destroy func void () void (void) AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime +analysis_generate_aux_witness_files func void () void (void) AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime +analysis_init func void () void (void) AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime +analysis_is_program_correct func char () char (void) AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime +append_element func char (MAP2CHECK_CONTAINER *, void *) char (struct obj4 *, void *) ContainerBTree.bc+ContainerRealloc.bc runtime +check_address_allocation_log func enum MemoryAddressStatus (MAP2CHECK_CONTAINER *, long) enum MemoryAddressStatus (struct obj4 *, long) AllocationLog.bc runtime +divisionByZeroError func void (unsigned int, const char *) void (unsigned int, const char *) AnalysisModeOverflow.bc - +find_row_with_address func MEMORY_ALLOCATIONS_ROW *(MAP2CHECK_CONTAINER *, void *) struct obj2 *(struct obj4 *, void *) AllocationLog.bc runtime +free_container func char (MAP2CHECK_CONTAINER *) char (struct obj4 *) ContainerBTree.bc+ContainerRealloc.bc runtime +generate_aux_files func void (MAP2CHECK_CONTAINER *, char) void (struct obj4 *, char) WitnessGeneration.bc+WitnessGenerationNone.bc runtime +generate_confirmation_file func void () void (void) WitnessGeneration.bc+WitnessGenerationNone.bc runtime +get_element_at func void *(unsigned int, MAP2CHECK_CONTAINER) void *(unsigned int, struct obj4) ContainerBTree.bc+ContainerRealloc.bc runtime +get_next_input_from_afl func uint8_t () unsigned char (void) NonDetGeneratorAFL.bc - +get_old_reference func long (long, MAP2CHECK_CONTAINER *) long (long, struct obj4 *) ListLog.bc - +get_type_from_list_log_row func enum MemoryAddressStatus (LIST_LOG_ROW *) enum MemoryAddressStatus (struct obj *) ListLog.bc runtime +gotError data char char Map2CheckFunctions.bc - +heap_log data MAP2CHECK_CONTAINER struct obj4 AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - +heap_log_file data const char * const char * HeapLog.bc - +heap_log_to_file func void (MAP2CHECK_CONTAINER *) void (struct obj4 *) HeapLog.bc runtime +helper_map2check_nondet_append_element func void (NONDET_CALL) void (struct obj3) NonDetLog.bc - +is_deref_error func char (long, MAP2CHECK_CONTAINER *) char (long, struct obj4 *) ListLog.bc - +is_in_tracked func char (unsigned int, MAP2CHECK_CONTAINER *) char (unsigned int, struct obj4 *) TrackBBLog.bc runtime +is_invalid_free func char (long, MAP2CHECK_CONTAINER *) char (long, struct obj4 *) ListLog.bc runtime +is_memcleanup_error func char (long, MAP2CHECK_CONTAINER *) char (long, struct obj4 *) ListLog.bc runtime +is_valid_allocation_address func char (MAP2CHECK_CONTAINER *, void *, int) char (struct obj4 *, void *, int) AllocationLog.bc runtime +is_valid_heap_address func char (MAP2CHECK_CONTAINER *, void *, int) char (struct obj4 *, void *, int) HeapLog.bc runtime +klee_log_file data const char * const char * NonDetLog.bc - +list_log data MAP2CHECK_CONTAINER struct obj4 AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - +list_log_file data const char * const char * ListLog.bc - +list_log_to_file func void (MAP2CHECK_CONTAINER *) void (struct obj4 *) ListLog.bc runtime +main func int (int, char **) | int () int (int, char **) | int (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass+frontend+external +map2check_add_store_pointer func void (void *, void *, unsigned int, const char *, int, const char *) void (void *, void *, unsigned int, const char *, int, const char *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_afl_data data const uint8_t * const unsigned char * NonDetGeneratorAFL.bc - +map2check_afl_size data size_t unsigned long NonDetGeneratorAFL.bc - +map2check_alloca func void (const char *, void *, int, int, int, int) void (const char *, void *, int, int, int, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_assert func void (int, int, const char *) void (int, int, const char *) AnalysisModeAssert.bc pass +map2check_assume func void (int) void (int) Map2CheckFunctions.bc pass +map2check_assume_loop func void (char) void (char) Map2CheckFunctions.bc pass +map2check_binop_add func void (long long, long long, unsigned int, unsigned int, unsigned int, char *) void (long long, long long, unsigned int, unsigned int, unsigned int, char *) AnalysisModeOverflow.bc pass +map2check_binop_add_uint func void (unsigned long long, unsigned long long, unsigned int, unsigned int, unsigned int, char *) void (unsigned long long, unsigned long long, unsigned int, unsigned int, unsigned int, char *) AnalysisModeOverflow.bc pass +map2check_binop_mul func void (long long, long long, unsigned int, unsigned int, unsigned int, char *) void (long long, long long, unsigned int, unsigned int, unsigned int, char *) AnalysisModeOverflow.bc pass +map2check_binop_mul_uint func void (unsigned long long, unsigned long long, unsigned int, unsigned int, unsigned int, char *) void (unsigned long long, unsigned long long, unsigned int, unsigned int, unsigned int, char *) AnalysisModeOverflow.bc pass +map2check_binop_sdiv func void (long long, long long, unsigned int, unsigned int, unsigned int, char *) void (long long, long long, unsigned int, unsigned int, unsigned int, char *) AnalysisModeOverflow.bc pass +map2check_binop_srem func void (long long, long long, unsigned int, unsigned int, unsigned int, char *) void (long long, long long, unsigned int, unsigned int, unsigned int, char *) AnalysisModeOverflow.bc pass +map2check_binop_sub func void (long long, long long, unsigned int, unsigned int, unsigned int, char *) void (long long, long long, unsigned int, unsigned int, unsigned int, char *) AnalysisModeOverflow.bc pass +map2check_binop_sub_uint func void (unsigned long long, unsigned long long, unsigned int, unsigned int, unsigned int, char *) void (unsigned long long, unsigned long long, unsigned int, unsigned int, unsigned int, char *) AnalysisModeOverflow.bc pass +map2check_calloc func void (void *, int, int) void (void *, int, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_check_cstring func void (const char *) void (const char *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_check_deref func void (int, const char *) void (int, const char *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_crab_assume func void (char) void (char) Map2CheckFunctions.bc pass +map2check_destroy func void () void (void) Map2CheckFunctions.bc - +map2check_error func void () void (void) Map2CheckFunctions.bc runtime +map2check_exit func void () void (void) Map2CheckFunctions.bc - +map2check_free func void (const char *, void *, unsigned int, unsigned int, const char *) void (const char *, void *, unsigned int, unsigned int, const char *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_free_resolved_address func void (void *, unsigned int, const char *, short) void (void *, unsigned int, const char *, short) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_function func void (const char *, void *) void (const char *, void *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_get_current_step func unsigned int () unsigned int (void) Map2CheckFunctions.bc runtime +map2check_init func void (int) void (int) Map2CheckFunctions.bc pass +map2check_load func void (void *, int) void (void *, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_malloc func void (void *, int) void (void *, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_next_current_step func void () void (void) Map2CheckFunctions.bc runtime +map2check_non_det_bool func bool () int (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_char func char () char (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_double func double () double (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_float func float () float (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_int func int () int (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_loff_t func loff_t () long (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_long func long () long (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_longlong func longlong () long long (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_memory func void (void *, size_t) void (void *, unsigned long) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_pchar func char *() char *(void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_pointer func pointer () void *(void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_sector_t func unsigned long () unsigned long (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_short func short () short (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_size_t func size_t () unsigned long (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_u16 func u16 () unsigned short (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_u32 func u32 () unsigned int (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_u8 func u8 () unsigned char (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_uchar func unsigned char () unsigned char (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_uint func unsigned int () unsigned int (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_ulong func unsigned long () unsigned long (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_ulonglong func ulonglong () unsigned long long (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_unsigned func unsigned int () unsigned int (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_ushort func ushort () unsigned short (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_static_alloca func void (const char *, void *, int, int, int, int) void (const char *, void *, int, int, int, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_nondet_bool func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_char func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_double func void (unsigned int, unsigned int, double, const char *) void (unsigned int, unsigned int, double, const char *) NonDetLog.bc pass +map2check_nondet_float func void (unsigned int, unsigned int, double, const char *) void (unsigned int, unsigned int, double, const char *) NonDetLog.bc pass +map2check_nondet_get_log func MAP2CHECK_CONTAINER () struct obj4 (void) NonDetLog.bc runtime +map2check_nondet_int func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_loff_t func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_long func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_longlong func void (unsigned int, unsigned int, long long, const char *) void (unsigned int, unsigned int, long long, const char *) NonDetLog.bc pass +map2check_nondet_memory_byte func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc runtime +map2check_nondet_pchar func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_pointer func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_sector_t func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_short func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_size_t func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_u16 func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_u32 func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_u8 func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_uchar func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_uint func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_ulong func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_ulonglong func void (unsigned int, unsigned int, unsigned long long, const char *) void (unsigned int, unsigned int, unsigned long long, const char *) NonDetLog.bc pass +map2check_nondet_unsigned func void (unsigned int, unsigned int, unsigned int, const char *) void (unsigned int, unsigned int, unsigned int, const char *) NonDetLog.bc pass +map2check_nondet_ushort func void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_posix func void (void *, int) void (void *, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_success func void () void (void) Map2CheckFunctions.bc pass +map2check_target_function func void (const char *, int, int) void (const char *, int, int) Map2CheckFunctions.bc pass +map2check_track_bb func void (unsigned int, const char *) void (unsigned int, const char *) Map2CheckFunctions.bc pass +map2check_wasm_check_access func void (uint64_t, uint64_t) void (unsigned long, unsigned long) AnalysisModeMemtrack.bc pass +map2check_wasm_free func void (uint64_t, int) void (unsigned long, int) AnalysisModeMemtrack.bc pass +map2check_wasm_malloc func void (uint64_t, uint64_t) void (unsigned long, unsigned long) AnalysisModeMemtrack.bc pass +mark_allocation_log func char (MAP2CHECK_CONTAINER *, long) char (struct obj4 *, long) AllocationLog.bc - +mark_deallocation_log func char (MAP2CHECK_CONTAINER *, long) char (struct obj4 *, long) AllocationLog.bc - +mark_heap_log func char (MAP2CHECK_CONTAINER *, MEMORY_HEAP_ROW) char (struct obj4 *, struct obj5) HeapLog.bc - +new_container func MAP2CHECK_CONTAINER (enum Container_Type) struct obj4 (enum Container_Type) ContainerBTree.bc+ContainerRealloc.bc runtime +new_heap_row func MEMORY_HEAP_ROW (int, int, void *, int, int, const char *) struct obj5 (int, int, void *, int, int, const char *) HeapLog.bc runtime +new_list_row func LIST_LOG_ROW (long, long, unsigned int, char, char, unsigned int, const char *, const char *, unsigned int) struct obj (long, long, unsigned int, char, char, unsigned int, const char *, const char *, unsigned int) ListLog.bc runtime +new_memory_row func MEMORY_ALLOCATIONS_ROW (long, char) struct obj2 (long, char) AllocationLog.bc runtime +new_nondet_call func NONDET_CALL (enum NONDET_TYPE, unsigned int, unsigned int, void *, const char *, unsigned int) struct obj3 (enum NONDET_TYPE, unsigned int, unsigned int, void *, const char *, unsigned int) NonDetLog.bc - +nondet_assume func void (int) void (int) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime +nondet_cancel func void () void (void) NonDetGeneratorAFL.bc - +nondet_destroy func void () void (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime +nondet_generate_aux_witness_files func void () void (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime +nondet_init func void () void (void) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime +nondet_log data MAP2CHECK_CONTAINER struct obj4 NonDetLog.bc - +nondet_log_destroy func void () void (void) NonDetLog.bc runtime +nondet_log_init func void () void (void) NonDetLog.bc runtime +nondet_log_to_file func char (MAP2CHECK_CONTAINER) char (struct obj4) NonDetLog.bc runtime +overflowError func void (unsigned int, const char *) void (unsigned int, const char *) AnalysisModeOverflow.bc pass +release_unreleased_addresses func void (MAP2CHECK_CONTAINER *) void (struct obj4 *) AllocationLog.bc - +sv_comp data char char Map2CheckFunctions.bc - +trackbb_log data MAP2CHECK_CONTAINER struct obj4 Map2CheckFunctions.bc - +trackbb_log_file data const char * const char * TrackBBLog.bc - +trackbb_log_to_file func void (MAP2CHECK_CONTAINER *) void (struct obj4 *) TrackBBLog.bc runtime +trackbb_new_row func TRACK_BB_ROW (unsigned int, const char *) struct obj6 (unsigned int, const char *) TrackBBLog.bc runtime +unknown_not_supported func void () void (void) AnalysisModeOverflow.bc - +update_reference_list_log func void (long, enum MemoryAddressStatus, unsigned int) void (long, enum MemoryAddressStatus, unsigned int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - +valid_allocation_log func long (MAP2CHECK_CONTAINER *) long (struct obj4 *) AllocationLog.bc runtime +violated_property_file data const char * const char * PropertyGenerator.bc - +violated_property_file_assert data const char * const char * PropertyGenerator.bc - +violated_property_file_deref data const char * const char * PropertyGenerator.bc - +violated_property_file_free data const char * const char * PropertyGenerator.bc - +violated_property_file_memcleanup data const char * const char * PropertyGenerator.bc - +violated_property_file_memtrack data const char * const char * PropertyGenerator.bc - +violated_property_file_overflow data const char * const char * PropertyGenerator.bc - +violated_property_file_unknown data const char * const char * PropertyGenerator.bc - +w2c_wasi__snapshot__preview1_proc_exit func void (struct w2c_wasi__snapshot__preview1 *, uint32_t) void (struct w2c_wasi__snapshot__preview1 *, unsigned int) WasmRuntimeStubs.bc external +wasm_rt_allocate_funcref_table func void (wasm_rt_funcref_table_t *, uint32_t, uint32_t) void (struct wasm_rt_funcref_table_t *, unsigned int, unsigned int) WasmRuntimeStubs.bc external +wasm_rt_allocate_memory func void (wasm_rt_memory_t *, uint64_t, uint64_t, bool, uint32_t) void (struct wasm_rt_memory_t *, unsigned long, unsigned long, _Bool, unsigned int) WasmRuntimeStubs.bc external +wasm_rt_free func void (void) void (void) WasmRuntimeStubs.bc external +wasm_rt_free_funcref_table func void (wasm_rt_funcref_table_t *) void (struct wasm_rt_funcref_table_t *) WasmRuntimeStubs.bc external +wasm_rt_free_memory func void (wasm_rt_memory_t *) void (struct wasm_rt_memory_t *) WasmRuntimeStubs.bc external +wasm_rt_grow_memory func uint64_t (wasm_rt_memory_t *, uint64_t) unsigned long (struct wasm_rt_memory_t *, unsigned long) WasmRuntimeStubs.bc external +wasm_rt_init func void (void) void (void) WasmRuntimeStubs.bc external +wasm_rt_is_initialized func bool (void) _Bool (void) WasmRuntimeStubs.bc external +wasm_rt_trap func void (wasm_rt_trap_t) void (enum wasm_rt_trap_t) WasmRuntimeStubs.bc external +write_property func void (enum ViolatedProperty, int, const char *) void (enum ViolatedProperty, int, const char *) PropertyGenerator.bc runtime +write_property_assert func void (int, const char *) void (int, const char *) PropertyGenerator.bc - +write_property_deref func void (int, const char *) void (int, const char *) PropertyGenerator.bc runtime +write_property_free func void (int, const char *) void (int, const char *) PropertyGenerator.bc runtime +write_property_memcleanup func void () void (void) PropertyGenerator.bc runtime +write_property_memtrack func void () void (void) PropertyGenerator.bc runtime +write_property_overflow func void (int, const char *) void (int, const char *) PropertyGenerator.bc - +write_property_unknown func void () void (void) PropertyGenerator.bc runtime diff --git a/docs/runtime-abi/pending-enums.txt b/docs/runtime-abi/pending-enums.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/runtime-abi/pending.txt b/docs/runtime-abi/pending.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/runtime-abi/rust-gap.tsv b/docs/runtime-abi/rust-gap.tsv new file mode 100644 index 0000000..886fa32 --- /dev/null +++ b/docs/runtime-abi/rust-gap.tsv @@ -0,0 +1,193 @@ +symbol status rust_signature rust_canonical_signature +B_TREE_CREATE missing - - +B_TREE_FREE missing - - +B_TREE_INSERT missing - - +B_TREE_INSERT_NONFULL missing - - +B_TREE_PAGE_CREATE missing - - +B_TREE_SEARCH missing - - +B_TREE_SPLIT_CHILD missing - - +CHECK_AND_RELEASE_PAGES missing - - +DISK_READ missing - - +DISK_WRITE missing - - +DumpTree missing - - +DumpTreeHelper missing - - +DumpTreePage missing - - +DumpTreePageChildren missing - - +ERROR_DEREF missing - - +Map2CheckCurrentStep missing - - +__VERIFIER_assert missing - - +__VERIFIER_error same void (void) void (void) +__afl_fuzz_alt missing - - +__afl_fuzz_alt_ptr missing - - +__afl_sharedmem_fuzzing missing - - +allocation_log missing - - +allocation_log_to_file missing - - +analysis_destroy missing - - +analysis_generate_aux_witness_files missing - - +analysis_init missing - - +analysis_is_program_correct missing - - +append_element missing - - +check_address_allocation_log missing - - +divisionByZeroError missing - - +find_row_with_address missing - - +free_container missing - - +generate_aux_files missing - - +generate_confirmation_file missing - - +get_element_at missing - - +get_next_input_from_afl missing - - +get_old_reference missing - - +get_type_from_list_log_row missing - - +gotError missing - - +heap_log missing - - +heap_log_file missing - - +heap_log_to_file missing - - +helper_map2check_nondet_append_element missing - - +is_deref_error missing - - +is_in_tracked missing - - +is_invalid_free missing - - +is_memcleanup_error missing - - +is_valid_allocation_address missing - - +is_valid_heap_address missing - - +klee_log_file missing - - +list_log missing - - +list_log_file missing - - +list_log_to_file missing - - +main missing - - +map2check_add_store_pointer missing - - +map2check_afl_data missing - - +map2check_afl_size missing - - +map2check_alloca missing - - +map2check_assert missing - - +map2check_assume missing - - +map2check_assume_loop missing - - +map2check_binop_add missing - - +map2check_binop_add_uint missing - - +map2check_binop_mul missing - - +map2check_binop_mul_uint missing - - +map2check_binop_sdiv missing - - +map2check_binop_srem missing - - +map2check_binop_sub missing - - +map2check_binop_sub_uint missing - - +map2check_calloc missing - - +map2check_check_cstring missing - - +map2check_check_deref missing - - +map2check_crab_assume missing - - +map2check_destroy same void (void) void (void) +map2check_error same void (void) void (void) +map2check_exit same void (void) void (void) +map2check_free missing - - +map2check_free_resolved_address missing - - +map2check_function missing - - +map2check_get_current_step same unsigned int (void) unsigned int (void) +map2check_init same void (int) void (int) +map2check_load missing - - +map2check_malloc missing - - +map2check_next_current_step same void (void) void (void) +map2check_non_det_bool missing - - +map2check_non_det_char missing - - +map2check_non_det_double missing - - +map2check_non_det_float missing - - +map2check_non_det_int missing - - +map2check_non_det_loff_t missing - - +map2check_non_det_long missing - - +map2check_non_det_longlong missing - - +map2check_non_det_memory missing - - +map2check_non_det_pchar missing - - +map2check_non_det_pointer missing - - +map2check_non_det_sector_t missing - - +map2check_non_det_short missing - - +map2check_non_det_size_t missing - - +map2check_non_det_u16 missing - - +map2check_non_det_u32 missing - - +map2check_non_det_u8 missing - - +map2check_non_det_uchar missing - - +map2check_non_det_uint missing - - +map2check_non_det_ulong missing - - +map2check_non_det_ulonglong missing - - +map2check_non_det_unsigned missing - - +map2check_non_det_ushort missing - - +map2check_non_static_alloca missing - - +map2check_nondet_bool same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_char same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_double same void (unsigned int, unsigned int, double, const char *) void (unsigned int, unsigned int, double, const char *) +map2check_nondet_float same void (unsigned int, unsigned int, double, const char *) void (unsigned int, unsigned int, double, const char *) +map2check_nondet_get_log missing - - +map2check_nondet_int same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_loff_t same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_long same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_longlong same void (unsigned int, unsigned int, long long, const char *) void (unsigned int, unsigned int, long long, const char *) +map2check_nondet_memory_byte same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_pchar same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_pointer same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_sector_t same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_short same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_size_t same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_u16 same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_u32 same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_u8 same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_uchar same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_uint same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_ulong same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_nondet_ulonglong same void (unsigned int, unsigned int, unsigned long long, const char *) void (unsigned int, unsigned int, unsigned long long, const char *) +map2check_nondet_unsigned same void (unsigned int, unsigned int, unsigned int, const char *) void (unsigned int, unsigned int, unsigned int, const char *) +map2check_nondet_ushort same void (unsigned int, unsigned int, int, const char *) void (unsigned int, unsigned int, int, const char *) +map2check_posix missing - - +map2check_success same void (void) void (void) +map2check_target_function same void (const char *, int, int) void (const char *, int, int) +map2check_track_bb same void (unsigned int, const char *) void (unsigned int, const char *) +map2check_wasm_check_access missing - - +map2check_wasm_free missing - - +map2check_wasm_malloc missing - - +mark_allocation_log missing - - +mark_deallocation_log missing - - +mark_heap_log missing - - +new_container missing - - +new_heap_row missing - - +new_list_row missing - - +new_memory_row missing - - +new_nondet_call missing - - +nondet_assume missing - - +nondet_cancel missing - - +nondet_destroy missing - - +nondet_generate_aux_witness_files missing - - +nondet_init missing - - +nondet_log missing - - +nondet_log_destroy same void (void) void (void) +nondet_log_init same void (void) void (void) +nondet_log_to_file missing - - +overflowError missing - - +release_unreleased_addresses missing - - +sv_comp missing - - +trackbb_log missing - - +trackbb_log_file missing - - +trackbb_log_to_file missing - - +trackbb_new_row missing - - +unknown_not_supported missing - - +update_reference_list_log missing - - +valid_allocation_log missing - - +violated_property_file missing - - +violated_property_file_assert missing - - +violated_property_file_deref missing - - +violated_property_file_free missing - - +violated_property_file_memcleanup missing - - +violated_property_file_memtrack missing - - +violated_property_file_overflow missing - - +violated_property_file_unknown missing - - +w2c_wasi__snapshot__preview1_proc_exit missing - - +wasm_rt_allocate_funcref_table missing - - +wasm_rt_allocate_memory missing - - +wasm_rt_free missing - - +wasm_rt_free_funcref_table missing - - +wasm_rt_free_memory missing - - +wasm_rt_grow_memory missing - - +wasm_rt_init missing - - +wasm_rt_is_initialized missing - - +wasm_rt_trap missing - - +write_property same void (enum ViolatedProperty, int, const char *) void (enum ViolatedProperty, int, const char *) +write_property_assert missing - - +write_property_deref missing - - +write_property_free missing - - +write_property_memcleanup missing - - +write_property_memtrack missing - - +write_property_overflow missing - - +write_property_unknown missing - - diff --git a/docs/semantic-deltas.md b/docs/semantic-deltas.md index e36f142..08b2286 100644 --- a/docs/semantic-deltas.md +++ b/docs/semantic-deltas.md @@ -31,6 +31,7 @@ entry and its tests in the same change. | `pending decision` | Waits on the cited issue. | | `fixed` | Was a regression; the Rust library now matches the C runtime of Map2Check `develop`. The entry cites the fixing issue and the `develop` source. | | `C behaviour preserved on purpose` | The C runtime of Map2Check `develop` does something odd and the Rust library does the same, because a consumer depends on the exact output. | +| `removed` | The export the entry describes left the C ABI in 2.0.0 (D6, Task 2.10), and its tests with it. The entry stays so that its ID keeps its meaning; **Tests:** is `—`. | ### SD-01 — JSON report shape @@ -62,7 +63,7 @@ Map2Check run (mode set) prints nothing on stdout, as develop. **Reproduce:** ```c -map2check_init(); +map2check_init(0); map2check_save_basic_block_log(10, "main"); print_all_containers_as_json(); ``` @@ -81,13 +82,13 @@ property is `MEMSAFETY_FREE`, `MEMSAFETY_DEREF`, `MEMSAFETY_MEMTRACK` or **Reproduce:** ```c -map2check_init(); +map2check_init(0); map2check_map_malloc((void *)0x1000, 16); -set_false_result(0 /* Overflow */, 3, "main"); +set_false_result(6 /* Overflow (C FALSE_OVERFLOW) */, 3, "main"); print_all_containers_as_json(); /* no Container_AllocationLog */ map2check_reset(); /* the first violation is final (SD-03) */ map2check_map_malloc((void *)0x1000, 16); -set_false_result(1 /* MemsafetyFree */, 4, "main"); +set_false_result(2 /* MemsafetyFree (C FALSE_FREE) */, 4, "main"); print_all_containers_as_json(); /* Container_AllocationLog present */ ``` @@ -123,7 +124,7 @@ violation is therefore final. `develop` matches `lib_old` here. **Reproduce:** ```c -map2check_init(); +map2check_init(0); map2check_map_malloc((void *)0x1000, 16); set_false_result(5 /* Reachability */, 1, "main"); map2check_check_load((void *)0x1000, 2, 0, 4, "main"); @@ -160,7 +161,7 @@ an empty function name. **Reproduce:** ```c -map2check_init(); +map2check_init(0); map2check_map_malloc((void *)0x1000, 16); map2check_check_mem_endprog(); /* MemsafetyMemtrack */ /* with map2check_set_memcleanup() first: MemsafetyMemcleanup */ @@ -186,7 +187,7 @@ allocation log and sets `ERROR_DEREF` (`AnalysisModeMemtrack.c:156-162`); **Reproduce:** ```c -map2check_init(); +map2check_init(0); map2check_check_load(NULL, 12, 0, 4, "main"); /* MemsafetyDeref */ map2check_set_null_is_valid(); map2check_check_load(NULL, 12, 0, 4, "main"); /* None */ @@ -228,7 +229,7 @@ library does not have that mode split.) **Reproduce:** ```c -map2check_init(); +map2check_init(0); map2check_check_free_resolved_address((void *)0x5000, 3, "main", 0); /* MemsafetyFree (before #37: None) */ map2check_reset(); map2check_check_free("p", (void *)0x5000, 0, 4, "main"); /* MemsafetyFree */ @@ -236,8 +237,16 @@ map2check_check_free("p", (void *)0x5000, 0, 4, "main"); /* Memsafet ### SD-07 — `__VERIFIER_assume(0)` abandons the fuzzer input; outside one it exits with 0 -**Classification:** intentional -**Tests:** `rust/src/nondet/libfuzzer.rs::failed_assume_abandons_only_the_current_iteration`, `rust/src/nondet/libfuzzer.rs::failed_fuzzer_assume_abandons_the_iteration_too`, `rust/src/nondet/libfuzzer.rs::verifier_assume_false_exits_with_zero`, `rust/src/nondet/libfuzzer.rs::map2check_fuzzer_assume_false_exits_with_zero` +**Classification:** removed (2.0.0) +**Tests:** — + +**2.0.0 (Task 2.10, D6):** `__VERIFIER_assume`, `map2check_fuzzer_assume` +and `LLVMFuzzerTestOneInput` left the C ABI with the `libfuzzer` feature. +The drop-in runtime has develop's mechanism: the program's +`map2check_assume*` (glue IR of `Map2CheckFunctions.bc`) call the +generator's `nondet_assume`, which in `NonDetGeneratorAFL.bc` `longjmp`s back +to the `__AFL_LOOP` of its `main` (D7 item 5; `scripts/runtime-ir/test-assume-link.sh`, +`tests/ffi_harness.c`). What follows describes 1.x. **Rust:** inside `LLVMFuzzerTestOneInput`, `__VERIFIER_assume(0)` and `map2check_fuzzer_assume(0)` abandon the current input: they unwind (a @@ -276,7 +285,13 @@ int __map2check_main__(void) { ### SD-08 — `__VERIFIER_error()` / `__VERIFIER_assert(0)` without an analysis mode **Classification:** fixed (#20) -**Tests:** `rust/src/nondet/libfuzzer.rs::verifier_error_is_a_no_op_with_a_mode_and_the_deref_is_reported`, `rust/src/nondet/libfuzzer.rs::target_mode_reaches_only_through_map2check_reach_target`, `rust/src/nondet/libfuzzer.rs::verifier_error_aborts_and_reports_reachability`, `rust/src/nondet/libfuzzer.rs::verifier_assert_false_aborts_and_reports_reachability` +**Tests:** `rust/src/ffi_tests.rs::core_verifier_error_does_nothing_as_in_c` + +**2.0.0 (Task 2.10, D6):** the `libfuzzer` and `klee` exports left the C +ABI, and with them the mode-unset path below. The `__VERIFIER_error` of the +library is the C core's: it does nothing, with a mode set and without +(`Map2CheckFunctions.c:57-59`), and `__VERIFIER_assert` is defined by the IR +module `AnalysisModeAssert.bc`, empty as in C. What follows describes 1.x. **Legacy (develop):** both are empty. `__VERIFIER_error()` is `// DO NOTHING` (`Map2CheckFunctions.c:57-59`) and `__VERIFIER_assert` is @@ -295,8 +310,10 @@ and aborted with no property file. Under KLEE's `--exit-on-error-type=Abort` that stopped the exploration before the real violation of a memory or overflow task. -Under KLEE the same is checked by `tests/klee_paths.sh` with -`tests/klee/vererr_deref.c` and `tests/klee/vererr_target.c`. Fixed for the +Under KLEE the same was checked by the legacy KLEE route (removed in +2.0.0); the drop-in's `__VERIFIER_error` is pinned by the test above and +the target path by `scripts/runtime-ir/test-klee-generator.sh` +(`klee_unsafe_program_reports_same_line`, unsafe-reach.c). Fixed for the mode-set path in the Task 2.7 review; the mode-unset path below stays as an intentional delta. @@ -307,11 +324,11 @@ an intentional delta. **Reproduce (link with `--features libfuzzer`):** ```c -map2check_init(); +map2check_init(0); __VERIFIER_error(); /* no mode: prints "REACHABILITY", then SIGABRT */ map2check_set_mode(1); /* memtrack */ -map2check_init(); +map2check_init(0); __VERIFIER_error(); /* returns; nothing recorded */ ``` @@ -337,7 +354,7 @@ at the boundary. **Reproduce:** ```c -map2check_init(); +map2check_init(0); map2check_map_malloc((void *)0x1000, -1); /* no record */ set_false_result(99, 1, NULL); /* ignored; result stays OK */ ``` @@ -359,7 +376,7 @@ into global lists. **Reproduce:** ```c -map2check_init(); +map2check_init(0); bbtrack_log_t *a = map2check_save_basic_block_log(1, "main"); bbtrack_log_t *b = map2check_save_basic_block_log(2, "main"); /* a->tqe_next == NULL && b->tqe_prev == NULL */ @@ -367,8 +384,13 @@ bbtrack_log_t *b = map2check_save_basic_block_log(2, "main"); ### SD-11 — Each LibFuzzer iteration starts from an empty state -**Classification:** intentional -**Tests:** `rust/src/nondet/libfuzzer.rs::fuzzer_iteration_resets_analysis_state_and_input` +**Classification:** removed (2.0.0) +**Tests:** — + +**2.0.0 (Task 2.10, D6):** `LLVMFuzzerTestOneInput` left the C ABI with the +`libfuzzer` feature. In the drop-in runtime each AFL++ iteration runs the +program's `main`, whose `map2check_init` starts a fresh state, as in develop +(SD-21 keeps the one difference). What follows describes 1.x. **Rust:** `LLVMFuzzerTestOneInput` resets the analysis state before it calls `__map2check_main__`. Records and results from one input never leak into @@ -407,7 +429,7 @@ are stack/static slots: `map2check_load` accepts them **Reproduce:** ```c -map2check_init(); +map2check_init(0); int n = 4; int vla[n]; map2check_map_non_static_alloca("vla", vla, n, sizeof(int), 3, 0); /* element count */ map2check_set_memcleanup(); @@ -440,7 +462,7 @@ This is a false positive in `develop`. **Reproduce:** ```c -map2check_init(); +map2check_init(0); map2check_map_malloc((void *)0x1000, 8); map2check_check_free_resolved_address((void *)0x1000, 3, "main", 0); /* None; develop: FALSE_FREE */ ``` @@ -496,10 +518,10 @@ The differences that remain are these: ```c map2check_set_mode(AnalysisMode_Memcleanup); -map2check_init(); +map2check_init(0); map2check_map_malloc((void *)0x3000, 8); map2check_check_mem_endprog(); /* None: nothing points to it */ -map2check_init(); +map2check_init(0); map2check_map_malloc((void *)0x3000, 8); map2check_map_store_pointer((void *)0x10, (void *)0x3000, 0, "p", 1, "main"); map2check_check_mem_endprog(); /* MemsafetyMemcleanup */ @@ -528,7 +550,7 @@ after the target. ```c map2check_set_mode(AnalysisMode_Target); -map2check_init(); +map2check_init(0); map2check_reach_target("reach_error", 0, 12); /* Reachability, line 12; returns */ ``` @@ -537,7 +559,7 @@ map2check_reach_target("reach_error", 0, 12); /* Reachability, line 12; return **Classification:** C behaviour preserved on purpose **Tests:** `rust/src/output/legacy_files.rs::unknown_uses_legacy_double_underscore_name`, `rust/src/ffi_golden_tests.rs::unknown_matches_c_runtime` -**Rust:** an `UNKNOWN` verdict (`set_false_result(9, ...)`, or a NULL block +**Rust:** an `UNKNOWN` verdict (`set_false_result(1, ...)`, or a NULL block from `map2check_map_malloc`/`_calloc` in the memtrack and memcleanup modes) writes `UNKNOWN\n` to `map2check_property__unknown`, with two underscores, and writes no `map2check_property`. @@ -555,14 +577,14 @@ develop. ```c map2check_set_mode(AnalysisMode_Memtrack); -map2check_init(); +map2check_init(0); map2check_map_malloc(NULL, 4); /* writes map2check_property__unknown, aborts */ ``` ### SD-17 — The memory and nondet logs are rebuilt from this library's records **Classification:** accepted limitation -**Tests:** `rust/src/ffi_golden_tests.rs::false_free_matches_c_runtime`, `rust/src/ffi_golden_tests.rs::false_free_resolved_matches_c_runtime`, `rust/src/ffi_golden_tests.rs::none_matches_c_runtime`, `rust/src/ffi_golden_tests.rs::false_memtrack_matches_c_runtime`, `rust/src/ffi_tests.rs::list_log_free_row_names_the_variable_passed_to_free`, `rust/src/ffi_tests.rs::failing_free_keeps_its_list_log_row_like_develop`, `rust/src/output/legacy_files.rs::nondet_rows_use_develop_types_and_value_formats`, `rust/src/ffi_golden_tests.rs::nondet_types_match_c_runtime` +**Tests:** `rust/src/ffi_golden_tests.rs::false_free_matches_c_runtime`, `rust/src/ffi_golden_tests.rs::false_free_resolved_matches_c_runtime`, `rust/src/ffi_golden_tests.rs::none_matches_c_runtime`, `rust/src/ffi_golden_tests.rs::false_memtrack_matches_c_runtime`, `rust/src/ffi_tests.rs::memtrack_rt_names_are_cut_at_35_bytes_except_the_variable_name`, `rust/src/memtrack/c_mode.rs::cname_cuts_at_35_bytes_and_at_the_first_nul`, `rust/src/ffi_tests.rs::nondet_function_name_is_cut_at_35_bytes_and_null_is_empty`, `rust/src/ffi_tests.rs::list_log_free_row_names_the_variable_passed_to_free`, `rust/src/ffi_tests.rs::failing_free_keeps_its_list_log_row_like_develop`, `rust/src/output/legacy_files.rs::nondet_rows_use_develop_types_and_value_formats`, `rust/src/ffi_golden_tests.rs::nondet_types_match_c_runtime` **Rust:** `heap_log.csv`, `allocation_log.csv`, `list_log.csv` and `klee_log.csv` are rebuilt at the end of the run by replaying the memory @@ -582,8 +604,16 @@ appends before it checks (`AnalysisModeMemtrack.c:346-358`); the golden byte, value format and type number included (golden `NONDET-TYPES`, SD-18). - **Long function names.** Develop copies names into `char[35]` with - `strncpy` (`FUNCTION_MAX_LENGTH_NAME`), which leaves names of 35 or more - characters unterminated; this library writes the whole name. + `strncpy` (`FUNCTION_MAX_LENGTH_NAME`; `ListLog.c:133`, `HeapLog.c:25`, + `NonDetLog.c`), which leaves a name of 35 or more bytes unterminated, so + its `%s` prints the 35 bytes and then whatever follows the array + (undefined behaviour). This library cuts the name at 35 bytes: the + function name of `list_log.csv` and the variable name of `heap_log.csv` + on the drop-in path (`__m2c_rt_memtrack_*`, Task 2.5), and the function + name of `klee_log.csv` on both paths (Task 2.2). The legacy replay of + `list_log.csv` and `heap_log.csv` still writes the whole name. The + variable name of `list_log.csv` is a pointer in C and is written whole + on both paths. The user approved the cut on 2026-10-26. - **NULL names.** Develop prints `(null)` for a NULL name; this library prints an empty string, as everywhere else (SD-09). @@ -592,15 +622,26 @@ appends before it checks (`AnalysisModeMemtrack.c:346-358`); the golden ```c map2check_set_mode(AnalysisMode_Overflow); map2check_enable_witness(); -map2check_init(); +map2check_init(0); map2check_binop_add_signed(INT64_MAX, 1, 64, 1, "main"); /* klee_log.csv is written; develop's NonDetGeneratorNone.c writes none */ ``` ### SD-18 — Nondet values: develop's AFL++ input layout and log format, with three differences -**Classification:** intentional -**Tests:** `rust/src/nondet/libfuzzer.rs::every_generator_reads_sizeof_type_bytes_and_wraps_like_develop`, `rust/src/nondet/libfuzzer.rs::seed_a_reads_0x41414141_like_develop`, `rust/src/nondet/libfuzzer.rs::bool_consumes_four_bytes_and_is_the_int_converted_to_bool`, `rust/src/nondet/libfuzzer.rs::empty_input_yields_zero`, `rust/src/nondet/libfuzzer.rs::without_an_iteration_every_generator_yields_zero`, `rust/src/nondet/libfuzzer.rs::pchar_reads_an_unsigned_length_then_length_minus_one_chars`, `rust/src/ffi_golden_tests.rs::nondet_types_match_c_runtime` +**Classification:** removed (2.0.0) +**Tests:** `rust/src/ffi_golden_tests.rs::nondet_types_match_c_runtime` + +**2.0.0 (Task 2.10, D6):** no difference in the drop-in runtime. The three +differences below belonged to the `__VERIFIER_nondet_` reader of the 1.x +`libfuzzer` feature, which left the C ABI with its tests. The drop-in +generators are the IR module `NonDetGeneratorAFL.bc` (Task 2.8), which does +what the vendored develop `NonDetGeneratorAFL.c` does: zero past the end of +the input, no rewind (`:55-66`); `bool` returns the raw value of its four +bytes, as develop's generator macro does (`:93`); `float` reads four bytes, +develop's own `float` generator (`:104`). The log is +`map2check_nondet_` (Task 2.2), whose format the golden test above still +pins. What follows is the record of the 1.x reader. **Rust:** the `__VERIFIER_nondet_` generators read the fuzzer input as develop's `NonDetGeneratorAFL.c` does: `sizeof(type)` bytes per value, in @@ -642,7 +683,7 @@ int __map2check_main__(void) { ### SD-19 — The wasm2c runtime: a bad access is OVERFLOW, and four small differences **Classification:** C behaviour preserved on purpose -**Tests:** `rust/src/ffi_tests.rs::wasm_access_past_the_block_is_reported_as_develop_does`, `rust/src/ffi_tests.rs::wasm_free_of_an_untracked_offset_is_a_false_free_at_the_line`, `rust/src/ffi_tests.rs::wasm_double_free_is_not_reported_as_develop`, `rust/src/ffi_tests.rs::wasm_access_inside_a_freed_inner_block_is_covered_by_the_older_block`, `rust/src/ffi_tests.rs::wasm_checks_are_inactive_in_modes_without_memory_checks`, `rust/src/ffi_tests.rs::wasm_first_violation_is_final`, `rust/src/ffi_tests.rs::rt_funcref_table_allocates_zeroed_elements` +**Tests:** `rust/src/ffi_tests.rs::wasm_access_past_the_block_is_reported_as_develop_does`, `rust/src/ffi_tests.rs::wasm_free_of_an_untracked_offset_is_a_false_free_at_the_line`, `rust/src/ffi_tests.rs::wasm_double_free_is_not_reported_as_develop`, `rust/src/ffi_tests.rs::wasm_access_inside_a_freed_inner_block_is_covered_by_the_older_block`, `rust/src/ffi_tests.rs::wasm_checks_are_inactive_in_modes_without_memory_checks`, `rust/src/ffi_tests.rs::wasm_first_violation_is_final`, `rust/src/ffi_tests.rs::rt_funcref_table_allocates_zeroed_elements`, `rust/src/ffi_golden_tests.rs::sd19_wasm_leak_is_false_memtrack_without_freeing_the_offset` **Rust (feature `wasm`):** `map2check_wasm_check_access(offset, size)` reports an access that does not lie inside a live registered block as @@ -671,7 +712,11 @@ Differences: - **Trap.** `wasm_rt_trap` records an `UNKNOWN` verdict and aborts (develop calls `map2check_error()`, which writes no property, so the frontend reads UNKNOWN). The function never returns, with or without a mode, as wasm-rt.h - declares. + declares. develop's `map2check_exit` runs `analysis_is_program_correct` + before it aborts, so a leak pending at the trap would be `FALSE_MEMTRACK` + there; the library reports `UNKNOWN`, because the trap's verdict is final + (first violation wins). This is a consequence of the trap recording + `UNKNOWN`, not a new delta. - **Funcref table element size.** develop's `wasm_rt_allocate_funcref_table` allocates `elements * sizeof(void*)` bytes for elements of `sizeof(wasm_rt_funcref_t)` (32 bytes), a heap overflow once the module's @@ -679,12 +724,22 @@ Differences: `max_size` is left untouched in both. - **Grow.** `wasm_rt_grow_memory` does not clear bytes when the new size is not larger than the old one (develop would `memset` a wrapped length). +- **Leaked block (Task 2.5, drop-in path).** At the end of a wasm run that + leaks a lifted `malloc` block, develop's `valid_allocation_log` calls + `free((void *)addr)` on the linear-memory offset (`AllocationLog.c:91`), + which is not a host heap pointer, so glibc aborts (`free(): invalid + pointer`) before `analysis_is_program_correct` writes `FALSE_MEMTRACK` + (`AnalysisModeMemtrack.c:139-142`). The frontend sees an abort with no + property file. `__m2c_rt_memtrack_*` never calls `free()` on a logged + address, so the run reports `FALSE-MEMTRACK` (`map2check_property` and + `map2check_property_memtrack`) and aborts. The user approved this on + 2026-10-26. **Reproduce (link with `--features wasm`, analysis mode memtrack):** ```c map2check_set_mode(AnalysisMode_Memtrack); -map2check_init(); +map2check_init(0); map2check_wasm_malloc(0x10000, 16); map2check_wasm_check_access(0x10010, 4); /* OVERFLOW, line 0, aborts */ ``` @@ -692,7 +747,7 @@ map2check_wasm_check_access(0x10010, 4); /* OVERFLOW, line 0, aborts */ ### SD-20 — Result files survive other KLEE states (gate ref `C-BUG-property-race`) **Classification:** intentional -**Tests:** `rust/src/output/legacy_files.rs::concurrent_reader_never_sees_empty_or_partial_file`, `rust/src/output/legacy_files.rs::write_goes_through_a_temp_file_and_rename`, `rust/src/output/legacy_files.rs::temp_name_collisions_are_skipped` +**Tests:** `rust/src/output/legacy_files.rs::concurrent_reader_never_sees_empty_or_partial_file`, `rust/src/output/legacy_files.rs::write_goes_through_a_temp_file_and_rename`, `rust/src/output/legacy_files.rs::temp_name_collisions_are_skipped`, `rust/src/output/legacy_files.rs::result_files_are_atomic_by_default_outside_dropin`, `rust/src/output/legacy_files.rs::plain_mode_truncates_the_target_in_place_like_fopen_w`, `rust/src/output/legacy_files.rs::atomic_mode_replaces_the_target_by_a_rename`, `rust/src/output/legacy_files.rs::set_atomic_writes_switches_the_property_writer`, `rust/src/ffi_tests.rs::output_set_atomic_selects_the_result_file_write_mode`, `rust/src/output/legacy_files.rs::never_downgrade_keeps_a_violation_against_none`, `rust/src/output/legacy_files.rs::never_downgrade_writes_none_when_there_is_no_property_file`, `rust/src/output/legacy_files.rs::never_downgrade_lets_a_violation_replace_none_and_violations`, `rust/src/output/legacy_files.rs::without_never_downgrade_none_replaces_a_violation`, `rust/src/output/legacy_files.rs::write_if_absent_never_replaces_and_cleans_up` This entry covers a bug of the C runtime of Map2Check `develop` that the Rust library fixes. The cutover gate cites it as `C-BUG-property-race` in @@ -726,15 +781,81 @@ Effects in gate run 1 (task 4.2), deterministic in 5 of 5 C reps: file exists, but it parses line 0 and an empty function. C under `afl` reports `533 main` for the same task, and so does Rust under `symex`. +**Drop-in gate of 2026-10-27 (Task 4.1, `docs/cutover/gate-2026-10-27.md`; +approved by the user on 2026-10-28):** the race shows on four keys, each +deterministic in 5 of 5 C reps, and each has a row in +`docs/cutover/deltas-aceitos.tsv`: +- `list-ext-properties/simple-ext` and `map2check-assert/ex01_false`, + `unreach-call`/`symex`: verdict UNKNOWN in C, FAILED in the drop-in; +- `list-ext-properties/list-ext`, `unreach-call`/`symex`: verdict UNKNOWN + in C, FAILED in the drop-in. With `--debug` the C run leaves + `test000001.abort.err` in `klee-out-0`, a 12-byte + `map2check_checked_error` and a 0-byte `map2check_property`; +- `list-ext-properties/960521-1_1-1`, `valid-memsafety`/`symex`: FALSE-DEREF + on both sides, line `0` and an empty function in C, `533 main` in the + drop-in. + The Rust library had the same race before `6b49b82` (task 3.3, "Cause B"). +**Scope in the drop-in runtime (approved by the user on 2026-10-27, Task +2.8):** the drop-in `Map2CheckFunctions.bc` writes atomically under the +`symex` generator only. It starts with develop's write (`fopen("w")`: +truncate and write in place), and `nondet_init` of +`NonDetGeneratorKlee.bc` turns the temporary file + rename on through +`__m2c_rt_output_set_atomic(1)` before anything is written. Under `afl` and +`none` the files are written as the C runtime writes them, so this entry +does not apply there: nothing races (one process per input, no forked +states sharing files), and the rename on every execution cost the AFL++ +generator 30% of its executions per second. Every build outside the drop-in +core (the staticlib) stays atomic. Image tests: +`scripts/runtime-ir/test-klee-generator.sh` +(`klee_symex_writes_result_files_atomically`) and +`scripts/runtime-ir/test-afl-generator.sh` +(`afl_writes_result_files_in_place_like_c`). + +**Never downgrade under KLEE (approved by the user on 2026-10-27, Task +3.1):** the temporary file + rename alone does not decide which state's +content survives. Once the drop-in ended every run through the witness +module (more writes per state), a KLEE state that finished without a +violation renamed `NONE` over the `TARGET-REACHED` the violating state had +just written, before that state reached its abort +(`scripts/runtime-ir/test-klee-generator.sh`, `unsafe-reach-none`; the C +kept the violation). So, under the `symex` generator only (the same +`__m2c_rt_output_set_atomic(1)` of `NonDetGeneratorKlee`'s `nondet_init` +turns it on), a `map2check_property` whose content is not a violation +(`NONE`, `UNKNOWN`, the empty `Concurrency` text) never replaces an +existing `map2check_property`: it is written to a temporary file and +hard-linked to the target (`link(2)` fails with `EEXIST` when the target +exists, in one system call, so there is no check-then-write window), and +the temporary name is removed. A violation is still renamed over the +target, whatever it held: between two violations the last writer wins, as +with develop's `fopen("w")` (within one process the first violation is +final and aborts, SD-03, so two violations only meet across KLEE states). +The `map2check_property_` files, `map2check_checked_error` and +`klee_log.csv` are written only on a violation and are unaffected; +`track_bb_log.st` carries no verdict and keeps the last writer. A KLEE +state killed (timer, memory cap) between creating its temporary file and +the rename or unlink can leave a `....tmp` file in the +scratch directory; the frontend reads only the fixed names, so the verdict +is unaffected. Plain mode +(AFL++, `none`) and the staticlib are unchanged: a `NONE` replaces whatever +was there. Observable difference from the C under `symex`: a program that +writes a violation and later `NONE` in the same path (only through direct +`write_property` calls; instrumented programs abort at the violation) +keeps the violation (`klee_symex_writes_result_files_atomically`). A +stale violation that an earlier KLEE run left in the run directory is also +kept against a later `NONE`, where the C overwrites it; the frontend makes +this unreachable, because every phase starts in a fresh scratch directory +(`rm -rf && mkdir `, `caller.cpp:94`). + **Reproduce:** the program is any one where KLEE forks a state that reaches the target and states that do not. With the C runtime: `map2check --debug --nondet-generator symex --target-function ex01_false.c` (sv-benchmarks `map2check-assert`). The kept directory has `abort.err` in `klee-out-0`, a 12-byte `map2check_checked_error`, and a 0-byte -`map2check_property`. Under the Rust runtime, `tests/klee/multistate.c` -(`tests/klee_paths.sh`) checks the opposite. +`map2check_property`. Under the drop-in runtime, +`klee_symex_writes_result_files_atomically` and the unsafe cases of +`scripts/runtime-ir/test-klee-generator.sh` check the opposite. ```c /* Under KLEE: one path reaches the target, the other finishes later. */ @@ -749,7 +870,14 @@ int main(void) { ### SD-21 — AFL++ iterations do not accumulate nondet records (gate ref `C-BUG-oom-afl-nondet`) **Classification:** intentional -**Tests:** `rust/src/nondet/libfuzzer.rs::two_fuzzer_iterations_yield_independent_results`, `rust/src/nondet/libfuzzer.rs::fuzzer_iteration_resets_analysis_state_and_input` +**Tests:** `rust/src/ffi_tests.rs::nondet_log_init_and_destroy_empty_the_log_and_keep_the_step`, `rust/src/ffi_tests.rs::map2check_init_takes_the_c_sv_comp_argument_and_starts_clean` + +**2.0.0 (Task 2.10, D6):** `LLVMFuzzerTestOneInput` left the C ABI. In the +drop-in runtime the `map2check_init` the pass puts at the start of `main` +runs once per `__AFL_LOOP` iteration and starts a fresh state, and its +`nondet_init` → `nondet_log_init` replaces the nondet log, releasing the old +one, where develop leaks it. The tests above pin both. The text below +describes the 1.x mechanism. This entry covers a bug of the C runtime of Map2Check `develop` that the Rust library does not have. The cutover gate cites it as @@ -798,3 +926,185 @@ int main(void) { return 0; } ``` + +### SD-22 — Runtime-internal C names are not exported (D7) + +**Classification:** intentional +**Tests:** `rust/src/output/legacy_files.rs::write_property_helpers_match_c_format`, `rust/src/bbtrack/mod.rs::is_in_tracked_matches_any_row_of_the_line`, `rust/src/output/legacy_files.rs::trackbb_log_to_file_matches_c_format` + +The symbol sets are checked by `scripts/it/check-link-matrix.sh --ref +--policy docs/runtime-abi/export-policy.tsv` and its test +`scripts/it/test-check-link-matrix.sh`; the tests above pin the C-only +helpers that the Rust core keeps private (Task 2.1). + +The user approved this on 2026-10-26 as item 2 of D7 ([d7-layout.md](cutover/d7-layout.md)). + +**Rust:** each runtime `.bc` defines only the external symbols that its C +counterpart defines *and* that have a consumer outside the C runtime +(`referenced_by` in `docs/runtime-abi/inventory.tsv` contains `pass`, +`frontend` or `external`). The Rust core also defines new entry points named +`__m2c_rt_*`, which the generated IR modules call. + +**C runtime of Map2Check `develop`:** the C runtime also exports 76 names that +only the runtime itself uses. They are listed as `c-only` in +`docs/runtime-abi/export-policy.tsv` and include: +- globals such as `sv_comp`, `gotError`, `Map2CheckCurrentStep`, + `trackbb_log`, `nondet_log` and `violated_property_file*`; +- functions such as `append_element`, `write_property_*`, `B_TREE_*` and + `is_in_tracked`. + +**Observable difference:** a program under analysis that defines one of those +76 names fails to link with the C runtime and links with the Rust one. A +program that defines a `__m2c_rt_*` name fails to link with the Rust runtime +only. + +### SD-23 — A release with no allocation row goes on where develop crashes + +**Classification:** intentional +**Tests:** `rust/src/ffi_golden_tests.rs::sd23_free_of_a_literal_null_goes_on_where_develop_crashes`, `rust/src/ffi_tests.rs::sd23_memtrack_rt_release_without_allocation_row_goes_on`, `rust/src/memtrack/c_mode.rs::a_release_without_allocation_row_stops_where_develop_crashes`, `rust/src/ffi_tests.rs::sd23_memcleanup_rt_release_without_allocation_row_goes_on`, `rust/src/ffi_golden_tests.rs::sd23_memcleanup_release_without_allocation_row_goes_on_where_develop_crashes` + +The user approved this on 2026-10-26. + +**Rust:** on the drop-in path (`__m2c_rt_memtrack_*`, Task 2.5), when a +release that the free check let through finds no allocation row for the +block, the core stops there. It updates no row and no reference, records +nothing, and the run goes on to its normal end. With a correct program that +end is `NONE`. + +**C runtime of Map2Check `develop`:** `map2check_free_resolved_address` +(`AnalysisModeMemtrack.c:176-195`) and `map2check_free` (`:351-378`) write +through the row that `find_row_with_address` (`AllocationLog.c:103-118`) +returns, with no NULL test (`row->size = 0`, `:190-192` and `:372-375`). For +an address with no row that is a NULL dereference: the process gets +SIGSEGV and writes no property file. + +**Cause:** the pass emits `map2check_free_resolved_address(ptr, line, fn, 0)` +for a free whose argument is not a `LoadInst` (`MemoryTrackPass.cpp:428-436`). +- In memtrack mode a literal `free(NULL)` or `free(0)` reaches the release: + `is_invalid_free` is FALSE for NULL (`ListLog.c:97-99`), and NULL has no + row. +- In memcleanup mode, `error = FALSE` (`AnalysisModeMemcleanup.c:168`, `:342`) + lets every free through, so a free of an untracked or stack address has no + row either. `scripts/runtime-ir/test-memcleanup-link.sh` (scenarios + `stack-free` and `untracked-free`) pins this through the linked IR module. + +**Reproduce:** + +```c +/* the program: */ +int main(void) { free(0); return 0; } +/* the calls the pass emits, with AnalysisModeMemtrack.bc: */ +map2check_init(0); +analysis_init(); +map2check_free_resolved_address(NULL, 1, "main", 0); /* Rust: nothing; develop: SIGSEGV */ +map2check_success(); /* Rust: NONE */ +``` + +### SD-24 — A `void main` runs to its end under KLEE + +**Classification:** intentional +**Tests:** `tests/gate_deltas.rs::sd24_klee_generator_main_is_not_optnone`, `tests/gate_deltas.rs::sd24_gate_rows_name_the_void_main_keys` + +The user approved this on 2026-10-28 (Task 4.1, first drop-in gate, +`docs/cutover/gate-2026-10-27.md`). + +**Rust:** the `main` of the generated `NonDetGeneratorKlee.bc` +(`scripts/runtime-ir/gen_ll.py`) calls `i32 @__map2check_main__(i32, ptr)`, +as `NonDetGeneratorKlee.c:18` does, and carries no attributes. When the +program declares `void main(void)`, the return types do not match, and +KLEE's `--optimize` folds the call to `tail call void +@__map2check_main__()` / `ret i32 poison`. The path runs to its end, and the +run gives the verdict of the analysis. + +**C runtime of Map2Check `develop`:** `NonDetGeneratorKlee.c` is compiled at +`-O0`, so its `main` is `optnone`. KLEE's `assembly.ll` keeps +`%8 = call i32 @__map2check_main__(...)` / `ret i32 %8`. KLEE stops the path +with `KLEE: ERROR: ...: return void when caller expected a result` and +writes a `.exec.err`. The frontend reports "KLEE terminated states early" +and then UNKNOWN. + +**Effect in the gate:** `ldv-memsafety/memleaks_test10-2` and +`ldv-memsafety/memleaks_test3-2`, `valid-memsafety`, under `symex` and +`hybrid`: UNKNOWN in C, SUCCEEDED in the drop-in (expected verdict true). +This is 4 rows in `docs/cutover/deltas-aceitos.tsv`. The verdicts of +`memleaks_test1-1` and `memleaks_test17_2-1` (also `void main`) are equal, +but their `symex` runs are shorter: 4.2 → 0.9 s and 19.6 → 1.8 s. + +**Reproduce** (`--nondet-generator symex --memtrack`): + +```c +#include +void main(void) { /* C: KLEE ERROR, UNKNOWN */ + int *p = malloc(sizeof(int)); + free(p); /* drop-in: SUCCEEDED */ +} +``` + +### SD-25 — KLEE completes explorations that the C runtime leaves to the timer + +**Classification:** intentional +**Tests:** `tests/gate_deltas.rs::sd25_gate_rows_name_the_keys_klee_completes` + +The user approved this on 2026-10-28 (Task 4.1, first drop-in gate, +`docs/cutover/gate-2026-10-27.md`). + +**Rust:** under KLEE the drop-in runtime costs fewer interpreted +instructions per check than the C runtime. On some programs KLEE then +finishes every path within the budget, and the run reports the verdict the +analysis proves. The checks and their results are the same; only the time +differs. + +**C runtime of Map2Check `develop`:** on the same programs KLEE halts on +its timer ("KLEE halted on its timer -- not a complete exploration"). The +frontend reports UNKNOWN. + +**Effect in the gate:** each change is from UNKNOWN in C to SUCCEEDED in +the drop-in, with expected verdict true. The keys, with one representative +run each: + +| task | prp | gen | C | drop-in | +|---|---|---|---|---| +| `ldv-memsafety/ArraysOfVariableLength` | `valid-memsafety` | `symex` | 112 M instructions, 0 complete paths, 31 s | 50 M instructions, 1 complete path, 7 s | +| `ldv-memsafety/ArraysOfVariableLength` | `valid-memsafety` | `hybrid` | KLEE phase on its timer | KLEE phase complete | +| `memsafety/960521-1-2` | `valid-memsafety` | `symex` | 109 M instructions, 0 complete paths | 95 M instructions, 1 complete path, 16 s | +| `list-ext-properties/960521-1_1-2` | `valid-memsafety` | `hybrid` | KLEE phase 30 M instructions, 13 partial paths | 5.5 M instructions, 31 complete paths | + +These are 4 rows in `docs/cutover/deltas-aceitos.tsv`. + +**Reproduce:** `map2check --nondet-generator symex --timeout 45 --memtrack +ArraysOfVariableLength.c` (sv-benchmarks `ldv-memsafety`) with each +runtime. Compare `KLEE: done: total instructions` and `completed paths` in +KLEE's output. + +### SD-26 — An uninitialised heap read can fuzz differently under AFL++ + +**Classification:** intentional +**Tests:** `tests/gate_deltas.rs::sd26_gate_row_names_the_uninitialised_read_key` + +The user approved this on 2026-10-28 (Task 4.1, first drop-in gate, +`docs/cutover/gate-2026-10-27.md`). + +**Rust:** the drop-in runtime allocates through the same `malloc`, but at +other sizes and times than the C runtime, so the heap of the program under +analysis has another layout. A program whose result depends on +uninitialised heap memory can then crash on other inputs. In +`list-ext-properties/list-ext` (`valid-memsafety`/`afl`), the free loop +reads `p->n` of a node whose `n` was never written (`while (p->n != 0)`, +line 558 of `list-ext.i`). Under the drop-in, the seed `A` does not crash. +AFL++ finds a crashing input by havoc in about 1,600 executions, the +witness replays it, and the run reports FAILED `FALSE-DEREF` at line 558, +function `main` (expected verdict false, subproperty `valid-deref`). + +**C runtime of Map2Check `develop`:** the seed `A` crashes in AFL++'s dry +run in persistent mode (sig 06, "We need at least one valid input seed that +does not crash!"), so the fuzzer aborts. Replayed in a fresh process, the +same input writes `NONE` and no `map2check_checked_error`. The frontend +reports "Could not replicate error", then UNKNOWN. + +**Effect in the gate:** this one key, UNKNOWN in C and FAILED in the +drop-in. One row in `docs/cutover/deltas-aceitos.tsv`. + +**Reproduce:** `map2check --debug --nondet-generator afl --timeout 45 +--memtrack list-ext.i` (sv-benchmarks `list-ext-properties`) with each +runtime. Compare `afl-out/default/crashes/`, `map2check_property` and +`map2check_checked_error` in the kept directory. diff --git a/docs/superpowers/plans/2026-10-15-cutover-runtime-c-map2check.md b/docs/superpowers/plans/2026-10-15-cutover-runtime-c-map2check.md index 295123e..751993f 100644 --- a/docs/superpowers/plans/2026-10-15-cutover-runtime-c-map2check.md +++ b/docs/superpowers/plans/2026-10-15-cutover-runtime-c-map2check.md @@ -10,8 +10,15 @@ **Spec:** a mensagem do usuário que pediu este plano (cutover para Rust apenas), mais as respostas da sessão de 2026-10-15: **escopo = runtime C do Map2Check**; **programas C de teste da ABI deste repositório ficam como testes**; **D5: sem camada de compatibilidade** e **migração direta dos passes, sem tabela de ABI dupla** (decisões do usuário). Requisitos de paridade de referência: [TODO.md](../../../TODO.md), issues #6–#24 e #36–#38, [docs/api-deltas.md](../../api-deltas.md) e [docs/semantic-deltas.md](../../semantic-deltas.md). +> **Substituído em 2026-10-26** por [2026-10-26-runtime-drop-in-map2check.md](2026-10-26-runtime-drop-in-map2check.md). O merge `a0f8b9cd` (PR #76) saiu do `develop` do Map2Check por force push em 2026-10-22, e o cutover recomeçou do zero com integração por drop-in da ABI C (D6, que revoga a D5). Este documento fica só como registro histórico; não execute tarefas dele. + +> **Revisão de escopo (2026-10-26):** a partir desta data, o agente que executa este plano só modifica **este repositório** (`map2check-library`), conforme a seção "Regras obrigatórias" do [CLAUDE.md](../../../CLAUDE.md). Tudo o que altera o repositório do Map2Check (commits, branches, tags, PRs, issues e releases) passa a ser **responsabilidade do mantenedor** e aparece neste plano marcado como **[Mantenedor]**, apenas como referência e pré-condição. O agente usa o Map2Check só como cópia local, sem versionamento e somente leitura, em `vendor/map2check/` (Task 5.3). As Fases 0–4 e a Task 5.2 foram concluídas antes desta revisão, e o seu texto fica como registro histórico. + ## Global Constraints +- **Escopo do agente (desde 2026-10-26):** código, testes, configuração e documentação são alterados somente neste repositório. O agente não altera, não faz commit e não abre PR/issue no Map2Check nem em outro repositório. `gh` com efeito remoto e `git push` só com pedido explícito do usuário na mensagem atual. +- **Map2Check nos testes de integração:** cópia do branch **`develop`** do Map2Check em `vendor/map2check/` (via `git clone --depth 1 --branch develop`; tags e tarballs de release não são usados), com `vendor/map2check/.git` removido logo após o download, o diretório no `.gitignore`, a versão ou o commit registrado em `vendor/MAP2CHECK_VERSION` e os fontes nunca editados. Builds e saídas do corpus vão para fora de `vendor/map2check/` (por exemplo, `target/map2check-it/`). Se um teste exigir mudança no Map2Check, o agente para e informa o usuário. + - Nada em C é removido sem equivalente Rust validado pelo gate diferencial (Task 4.2) no fluxo correspondente. - Nenhuma funcionalidade é declarada obsoleta sem uma justificativa registrada em `docs/cutover/decisoes.md`, com evidência e aprovação explícita do mantenedor. - Passos pequenos e reversíveis: até a Fase 3, `develop` do Map2Check não muda de runtime; da Task 5.1 até o ponto de não-retorno (Fase 6), voltar ao C é um `git revert -m 1` do merge. @@ -1360,10 +1367,11 @@ if __name__ == "__main__": - [ ] **Step 2:** Ensaiar o rollback antes do merge. Em um clone descartável, fazer o merge local, depois `git revert -m 1 HEAD`, build e `ctest`. Registrar no PR que o revert restaura o runtime C e passa. - [ ] **Step 3:** README do Map2Check: na seção de build, documentar `MAP2CHECK_RUST_LIB_DIR`/`MAP2CHECK_RUST_SRC` e a versão mínima da biblioteca. CHANGELOG: entrada "Runtime replaced by map2check-library (Rust)". - [ ] **Step 4:** Tirar o PR de draft e mergear com **merge commit** (não squash, não rebase), para que um único `git revert -m 1` desfaça tudo. Anotar o hash do merge no PR e em `docs/cutover/decisoes.md`. -- [ ] **Step 5:** Publicar uma release do Map2Check. -- [ ] **Step 6:** Período de observação de **um ciclo de release**. Toda regressão reportada é triada; se for de runtime e não tiver correção rápida, o rollback é `git revert -m 1 ` em `develop` + issue + nova release. +- [ ] **Step 5 [Mantenedor]:** Publicar uma release do Map2Check. +- [ ] **Step 6 [Mantenedor]:** Período de observação de **um ciclo de release**. Toda regressão reportada é triada; se for de runtime e não tiver correção rápida, o rollback é `git revert -m 1 ` em `develop` + issue + nova release. +- [ ] **Step 7 (agente, neste repositório):** Quando o mantenedor informar a release do Step 5, registrar a versão, a data e o merge `a0f8b9cd` em `docs/cutover/decisoes.md`. Durante a observação, toda regressão de runtime atribuída à biblioteca é reproduzida pela Task 5.3, sobre o `develop` atual do Map2Check, corrigida aqui com teste RED/GREEN e entregue numa release da biblioteca; o que exigir mudança no Map2Check é reportado ao usuário, sem ação do agente lá. -**Critério de conclusão:** uma release publicada com o runtime Rust, nenhum revert acionado e o gate 4.2 verde de novo ao fim do ciclo. +**Critério de conclusão:** uma release publicada com o runtime Rust, nenhum revert acionado e o gate 4.2 verde de novo ao fim do ciclo (reexecutado pela Task 5.3 sobre uma cópia nova do `develop` do Map2Check, que já contém a release). ### Task 5.2: Portar as asserções dos testes unitários C @@ -1375,26 +1383,49 @@ if __name__ == "__main__": **Critério de conclusão:** a tabela de correspondência `TEST C → #[test] Rust` está em `docs/cutover/decisoes.md` e todos passam. +### Task 5.3: Ambiente local de integração com o Map2Check (somente leitura) + +**Files:** `.gitignore` (já contém `vendor/map2check/`), criar `vendor/MAP2CHECK_VERSION` e, se útil, `scripts/fetch-map2check.sh` com o seu teste `scripts/test-fetch-map2check.sh`. + +- [ ] **Step 1:** Baixar o Map2Check sempre do branch `develop`: `git clone --depth 1 --branch develop https://github.com/hbgit/Map2Check vendor/map2check` e, em seguida, `rm -rf vendor/map2check/.git`. +- [ ] **Step 2:** Gravar em `vendor/MAP2CHECK_VERSION` a URL, o branch (`develop`), o commit resolvido (obtido antes de remover o `.git`, por exemplo com `git -C vendor/map2check rev-parse HEAD`) e a data. Conferir com `git status --short` que nada de `vendor/map2check/` aparece. +- [ ] **Step 3:** Construir o Map2Check da cópia com a biblioteca deste checkout (`MAP2CHECK_RUST_LIB_DIR` apontando para `target/release`), com o diretório de build e as saídas do corpus fora de `vendor/map2check/` (`target/map2check-it/`). +- [ ] **Step 4:** Rodar o corpus e o comparador do gate 4.2 a partir da cópia, sem editar os scripts dela. Se algum passo só funcionar com mudança nos fontes baixados, parar e informar o usuário. +- [ ] **Step 5:** Registrar o resultado (commit da cópia, `gate: PASS/FAIL`, razões de tempo e RSS) em `docs/cutover/decisoes.md`. + +**Critério de conclusão:** `vendor/MAP2CHECK_VERSION` identifica a cópia, `git status` não mostra arquivos de `vendor/map2check/`, e o gate roda contra a cópia sem nenhuma alteração nos fontes do Map2Check. + --- ## Fase 6 — Remoção do C (contém o PONTO DE NÃO-RETORNO) **Ponto de não-retorno:** o merge, em `develop` do Map2Check, do PR da Task 6.2. Antes dele, o runtime C ainda está no repositório (fora do build) e o merge da Task 5.1 pode ser revertido com `git revert -m 1`. Depois dele, voltar ao C exige reverter também a remoção ou partir de `legacy/c-runtime-final`; o plano trata isso como fora do caminho normal. +**Divisão de responsabilidade (revisão de 2026-10-26):** as Tasks 6.1, 6.2 e 6.3 e os itens da 6.4 que alteram o Map2Check são **[Mantenedor]**. O agente não os executa. O texto delas fica como roteiro para o mantenedor e como referência das pré-condições. Cabem ao agente a Task 6.0 (verificação local) e a Task 6.4a (documentação deste repositório). + **Pré-condições (todas obrigatórias, conferidas no PR):** - [ ] Task 5.1 concluída (um ciclo de release sem revert). - [ ] Gate 4.2 com `gate: PASS` sobre o commit-base do PR. - [ ] D1–D4 registradas e aprovadas. - [ ] Nenhuma issue aberta com o label `area:memtrack`/`kind:bug` sem triagem. -### Task 6.1: Atualizar a tag de preservação +### Task 6.0: Verificação local das pré-condições (agente, neste repositório) + +- [ ] **Step 1:** Atualizar a cópia de `vendor/map2check/` para o `develop` atual do Map2Check (Task 5.3, Steps 1–2) e rodar o gate 4.2 contra ela (Task 5.3, Steps 3–5). Esta é a segunda execução consecutiva exigida pelo critério de prontidão antes da Fase 6. +- [ ] **Step 2:** Na cópia, sem editá-la, confirmar que o runtime C está fora do build: `grep -rn 'backend/library' vendor/map2check/CMakeLists.txt vendor/map2check/modules/frontend` só deve citar código morto ou notas. +- [ ] **Step 3:** Conferir em `docs/cutover/decisoes.md` que D1–D4 estão registradas e aprovadas. Para as issues, usar só leitura (`gh issue list --repo hbgit/Map2Check --label area:memtrack --label kind:bug --state open`). +- [ ] **Step 4:** Registrar o resultado em `docs/cutover/decisoes.md` e informar ao usuário se as pré-condições da Fase 6 estão satisfeitas, para que o mantenedor decida sobre as Tasks 6.1–6.3. + +**Critério de conclusão:** o relatório de pré-condições está em `docs/cutover/decisoes.md`, com o commit da cópia usada. + +### Task 6.1 [Mantenedor]: Atualizar a tag de preservação - [ ] **Step 1:** `git tag -s -f legacy/c-runtime-final origin/develop -m "Último commit com o runtime C (antes da remoção)" && git push -f origin legacy/c-runtime-final`, e anotar o hash no PR da Task 6.2. - [ ] **Step 2:** Ensaiar a recuperação: em um clone descartável, `git checkout legacy/c-runtime-final`, build e `ctest`. Como a tag aponta para o commit antes da remoção (e depois do merge da 5.1), o ensaio também roda `git revert -m 1 ` e confirma que o runtime C volta a compilar e passar no `ctest`. **Critério de conclusão:** o rollback foi ensaiado com sucesso e registrado no PR. -### Task 6.2: Remover o runtime C, do menos ao mais acoplado +### Task 6.2 [Mantenedor]: Remover o runtime C, do menos ao mais acoplado Um PR, um commit por grupo, e **cada commit** mantém o build + `ctest` + `run-corpus.sh` (com `REPS=1`, como verificação rápida) verdes. Desde a Task 3.1 o runtime C está fora do build, então os commits só apagam código morto; a ordem, das folhas que nenhum outro `.c` usa até o núcleo, mantém cada commit revisável e revertível isoladamente. @@ -1414,24 +1445,33 @@ Um PR, um commit por grupo, e **cada commit** mantém o build + `ctest` + `run-c **Critério de conclusão:** o PR foi mergeado; o CI do Map2Check passa sem a coluna `c`; a busca acima retorna vazio. -### Task 6.3: CI e ferramentas de análise +### Task 6.3 [Mantenedor]: CI e ferramentas de análise - [ ] **Step 1:** Conferir que o `ci.yml` já não tem passos do runtime C (removidos na Task 3.4) e atualizar `.cppcheck-suppressions.txt` e `.clang-tidy` se ainda citarem `modules/backend/library`. - [ ] **Step 2:** Adicionar ao Map2Check uma versão de `scripts/check-no-c-era-artifacts.sh` (copiada deste repo) com o padrão `^modules/backend/library/` e o seu teste. **Critério de conclusão:** a guarda falha se alguém reintroduzir o diretório; o CI passa. -### Task 6.4: Documentação +### Task 6.4 [Mantenedor]: Documentação no Map2Check - [ ] **Map2Check `README.md`:** a seção de arquitetura passa a dizer que o runtime é `map2check-library` (Rust), com a versão mínima, o link e como fixar uma versão. -- [ ] **Map2Check `CLAUDE.md`, `docs/`, `modules/backend/pass/NOTES.txt`:** trocar referências a `modules/backend/library` pelos fontes Rust. As instruções de "como adicionar um tipo nondet" (`modules/backend/library/NOTES.txt`) migram para `map2check-library/docs/integration-guide.md`. -- [ ] **Este repo `README.md` e `CLAUDE.md`:** declarar que esta biblioteca é **o** runtime do Map2Check e citar `legacy/c-library-final` e `legacy/c-runtime-final` como referências históricas. -- [ ] **Este repo `TODO.md`:** marcar os itens concluídos e mover a seção "Dependente do repositório externo" para "Concluído", com os links dos PRs. -- [ ] **Este repo `docs/plano_map2check_library_rust.md` e `audit/`:** acrescentar uma nota no topo dizendo que o documento é histórico e apontando para este plano. Não reescrever evidência. -- [ ] **CHANGELOGs dos dois repositórios:** uma entrada de remoção, com o hash da tag de preservação. +- [ ] **Map2Check `CLAUDE.md`, `docs/`, `modules/backend/pass/NOTES.txt`:** trocar referências a `modules/backend/library` pelos fontes Rust e apontar para `map2check-library/docs/integration-guide.md` (Task 6.4a). +- [ ] **CHANGELOG do Map2Check:** uma entrada de remoção, com o hash da tag de preservação. **Critério de conclusão:** `grep -rn 'backend/library' README.md CLAUDE.md docs/` no Map2Check só aponta para notas históricas. +### Task 6.4a: Documentação deste repositório (agente) + +Os itens que dependem de um fato do Map2Check (hash da tag `legacy/c-runtime-final`, merge da Task 6.2) esperam o mantenedor informá-lo; os demais podem ser feitos já. + +- [ ] **`docs/integration-guide.md`:** incorporar as instruções de "como adicionar um tipo nondet", lidas de `vendor/map2check/modules/backend/library/NOTES.txt` (Task 5.3) e reescritas para a API núcleo Rust. +- [ ] **`README.md` e `CLAUDE.md`:** declarar que esta biblioteca é **o** runtime do Map2Check e citar `legacy/c-library-final` e `legacy/c-runtime-final` como referências históricas. +- [ ] **`TODO.md`:** marcar os itens concluídos e mover a seção "Dependente do repositório externo" para "Concluído", com os links dos PRs. +- [ ] **`docs/plano_map2check_library_rust.md` e `audit/`:** acrescentar uma nota no topo dizendo que o documento é histórico e apontando para este plano. Não reescrever evidência. +- [ ] **`CHANGELOG.md`:** uma entrada registrando a remoção do runtime C no Map2Check, com o hash da tag de preservação informado pelo mantenedor. + +**Critério de conclusão:** `cargo test --all-features` e `scripts/test-check-no-c-era-artifacts.sh` passam; os documentos deste repositório não descrevem mais o runtime C como ativo. + --- ## Fase 7 — (removida) @@ -1443,3 +1483,5 @@ A migração dos passes para a API núcleo, antes prevista aqui, virou a Task 3. ## Execution order (resumo) 0.1 → 0.2 → 0.3 → **1.1 (D1, aprovação)** → 1.2 → 2.1 → 2.2 → 2.3 → 2.4 → 2.5 → (2.6 depois de 4.3) → 2.7 → 2.8 → 2.9 → 3.1 → 3.2 → 3.3 → 3.4 → 4.1 → 4.3 → 4.2 → 5.1 → 5.2 → 6.1 → **6.2 (não-retorno)** → 6.3 → 6.4. + +**Ordem a partir da revisão de escopo (2026-10-26):** 5.3 → 5.1 Step 7 (acompanha 5.1 Steps 5–6 [Mantenedor]) → 6.4a (itens independentes do Map2Check) → 6.0 → [Mantenedor: 6.1 → **6.2 (não-retorno)** → 6.3 → 6.4] → 6.4a (itens restantes, com o hash da tag). diff --git a/docs/superpowers/plans/2026-10-26-runtime-drop-in-map2check.md b/docs/superpowers/plans/2026-10-26-runtime-drop-in-map2check.md new file mode 100644 index 0000000..6962e1c --- /dev/null +++ b/docs/superpowers/plans/2026-10-26-runtime-drop-in-map2check.md @@ -0,0 +1,321 @@ +# Runtime Rust drop-in para o Map2Check (`develop`) — Plano de implementação + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Fazer esta biblioteca produzir, a partir de Rust, um substituto direto (drop-in) dos 21 arquivos `.bc` do runtime C que o `develop` atual do Map2Check liga, com os mesmos nomes de arquivo, símbolos, assinaturas e arquivos de resultado, e provar a equivalência por um gate diferencial contra o runtime C, sem alterar nada no Map2Check. + +**Architecture:** O frontend do Map2Check (`modules/frontend/caller.cpp`, `Caller::linkLLVM`) faz `llvm-link` de `${MAP2CHECK_PATH}/lib/.bc`, compondo o runtime por estrutura de dados, modo, gerador, wasm e witness. A biblioteca passa a exportar a ABI do runtime C (decisão D6, que revoga a D5) e um script empacota a biblioteca como esses 22 módulos de bitcode LLVM 16, no layout decidido pelo spike da Task 1.1 (D7). O teste de integração constrói o Map2Check a partir de uma cópia somente leitura do `develop` em `vendor/map2check/`, instala-o fora da cópia e roda o mesmo binário do frontend duas vezes: com os `.bc` do C e com os `.bc` do Rust sobrepostos na instalação. A troca definitiva no Map2Check é trabalho do mantenedor. + +**Tech Stack:** Rust (crate em `rust-toolchain.toml`; bitcode do runtime com rustc 1.72.1/LLVM 16, rota A' da D1), cbindgen, serde_json; Map2Check `develop` em Docker (`vendor/map2check/Dockerfile.dev`: Ubuntu 22.04, LLVM/Clang 16, KLEE 3.1, AFL++ 4.40c); Python 3 para o comparador do gate. + +**Spec:** decisões do usuário na sessão de 2026-10-26: (1) recomeçar o cutover do zero, sem usar as alterações antigas no repositório do Map2Check (o merge `a0f8b9cd` do PR #76 saiu do `develop` por force push em 2026-10-22); (2) o agente só modifica este repositório e obtém o Map2Check apenas do branch `develop` ([CLAUDE.md](../../../CLAUDE.md), "Regras obrigatórias"); (3) integração por **drop-in da ABI C**; (4) o código Rust existente é **mantido e revalidado** contra o runtime C do `develop` atual. Este plano substitui [2026-10-15-cutover-runtime-c-map2check.md](2026-10-15-cutover-runtime-c-map2check.md), que fica como registro histórico. + +## Global Constraints + +- O agente altera somente este repositório. `gh` com efeito remoto, `git push`, tags e releases só com pedido explícito do usuário na mensagem atual. +- O Map2Check vem sempre do branch `develop`, por `git clone --depth 1 --branch develop`, para `vendor/map2check/`; todo `.git` da cópia é removido logo após o download; o commit fica em `vendor/MAP2CHECK_VERSION`; os fontes da cópia nunca são editados. Builds, instalações e saídas ficam em `target/map2check-it/`. +- Se um teste exigir mudança no Map2Check, o agente para e informa o usuário. +- Referência de comportamento: o runtime C em `vendor/map2check/modules/backend/library/` no commit de `vendor/MAP2CHECK_VERSION`. Qualquer diferença observável (veredito, arquivo de resultado, saída) é bug, salvo se registrada como `SD-xx` em `docs/semantic-deltas.md` e aprovada pelo usuário. +- Nomes dos módulos entregues (exatamente os que `caller.cpp` liga): `Map2CheckFunctions TrackBBLog NonDetLog PropertyGenerator ContainerRealloc ContainerBTree BTree AnalysisModeMemtrack AnalysisModeMemcleanup AnalysisModeOverflow AnalysisModeAssert AnalysisModeNone AllocationLog ListLog HeapLog NonDetGeneratorNone NonDetGeneratorKlee NonDetGeneratorAFL WasmRuntimeStubs WitnessGeneration WitnessGenerationNone` + `.bc` (21 arquivos). +- Todo `.bc` entregue é legível por `llvm-dis-16`. O código da biblioteca compila com rustc 1.72.1 (`rust-version = "1.72"`), além da toolchain fixada. +- Sem código C novo na biblioteca. Módulos de seleção ou de cola, se a D7 os exigir, são LLVM IR textual (`.ll`) montado com `llvm-as-16`. Exceção aprovada na D7 (2026-10-26): os módulos de gerador do layout A-fwd contêm lógica de gerador em IR gerado por `scripts/gen_ll.py`, testada como código. +- Desempenho (critérios mantidos do plano anterior, aprovados pelo mantenedor): mediana de `wall_rust / wall_c` ≤ 1,10; nos geradores `afl` e `hybrid`, cada `wall_rust` desconta antes um custo fixo de compilação de até 2,0 s por execução (as três compilações do núcleo pelo `afl-clang-fast` no frontend), por decisão do usuário em 2026-10-27 após a Task 3.3; no `symex` vale o 1,10 sem desconto; mediana da razão de pico de RSS ≤ 1,36; contagem de TIMEOUT por gerador não aumenta. +- Qualidade da biblioteca (CI atual): `cargo fmt --check`, `cargo clippy --all-targets --all-features -- -D warnings`, `cargo test --all-features`, `cargo deny check`, gate de cobertura e `tests/ffi_harness.c` sob ASan/UBSan. +- Commits assinados, sem atribuição de IA. `include/map2check.h` só muda via `build.rs`/`cbindgen.toml`. + +## Review Focus + +1. **Combinação de link que nenhum teste unitário monta** (por exemplo `--btree` + memcleanup + `afl` + `--wasm`, com e sem `WitnessGeneration.bc`): esperado `llvm-link` sem símbolo duplicado e link nativo sem símbolo indefinido fora da lista permitida do gerador. Teste: `scripts/it/check-link-matrix.sh` sobre as 120 combinações (Task 1.1, reaplicado nas Tasks 3.1 e 4.1). +2. **TRUE sob KLEE:** um runtime que faz chamadas externas ou bifurca caminhos a mais faz o KLEE descartar caminhos, e o frontend (`kleeDroppedPaths()`) rebaixa TRUE para UNKNOWN. Esperado: mesma contagem de `VERIFICATION SUCCEEDED` que o C com `symex`. Teste: `klee_safe_program_drops_no_paths` (Task 2.7) e o gate por gerador (Task 4.1). +3. **Iterações do AFL++ em persistent mode no mesmo processo:** o estado de uma entrada não pode vazar para a seguinte, e a rejeição por `nondet_assume` precisa cancelar só a entrada atual. Teste: `afl_persistent_iterations_start_from_clean_state` (Task 2.8). +4. **Programa sem violação que chama `exit()` no meio, com `--memcleanup-property`:** esperado FALSE-MEMCLEANUP quando há bloco alcançável não liberado, como o C faz no `atexit`. Teste: `memcleanup_reports_leak_at_exit` (Task 2.6). +5. **Largura e sinal dos tipos na ABI** (`long`, `unsigned`, `loff_t`, `sector_t`, ponteiros, `_Bool`): uma assinatura com o nome certo e o tipo errado não dá erro de link, só valor errado. Teste: `tests/abi/check-signatures.sh` (Task 2.0) e o ida-e-volta por tipo `nondet_roundtrip_` (Task 2.2). + +--- + +## Ponto de partida (fatos verificados em 2026-10-26) + +- `develop` do Map2Check: `3e914528` (9.0, alternância AFL++/KLEE como padrão). O runtime C são 21 `.c` (3 301 linhas) e 13 headers em `modules/backend/library/`, compilados com `clang -c -emit-llvm` e instalados em `lib/`. +- O C exporta 182 símbolos; 8 coincidem em nome com exports atuais da biblioteca (`map2check_init`, `map2check_success`, `map2check_check_deref`, `__VERIFIER_assert`, `__VERIFIER_error`, `map2check_wasm_{check_access,free,malloc}`), nem todos com a mesma assinatura. Os passes citam 47 nomes de função do runtime. +- Cada `AnalysisMode*.c` e cada `NonDetGenerator*.c` define os mesmos símbolos (`analysis_init`, `main`, `map2check_non_det_*`, ...): a escolha é feita por qual `.bc` é ligado. +- `NonDetGeneratorAFL.c` define `main`, lê `__AFL_FUZZ_TESTCASE_BUF` e cancela entradas com `longjmp`; `NonDetGeneratorKlee.c` usa `klee_make_symbolic`/`klee_assume`. +- A biblioteca já tem: rota de bitcode LLVM 16 (`klee/Cargo.toml`, pós-processamento `scripts/klee-*.py`; o `scripts/build-klee-runtime.sh` da rota antiga saiu na Task 2.10, e o build do núcleo drop-in é `scripts/build-runtime-bc.sh`), canal de arquivos legado (`rust/src/output/legacy_files.rs`), modos e memtrack (`rust/src/analysismode/`, `rust/src/memtrack/`), nondet (`rust/src/nondet/`) e wasm (`rust/src/wasm.rs`), escritos para a API núcleo. +- O repositório do Map2Check tem submódulos (`tests/castle/CASTLE-Benchmark`, `tests/juliet/juliet-test-suite-c`); o corpus do gate (`tests/regression_test/test_cases/sv-benchmarks`, 39 `.yml`) não depende deles. + +## Decisões + +| Id | Decisão | Estado | +|---|---|---| +| D6 | A biblioteca exporta a ABI do runtime C do `develop` (nomes, assinaturas, arquivos de resultado). Revoga a D5. Os exports da API núcleo que colidem ou que nenhum consumidor usa saem do C ABI (versão 2.0.0). | Aprovada pelo usuário em 2026-10-26 | +| D7 | Layout dos 21 módulos `.bc` e tratamento de `main`/`longjmp` do AFL++ e dos intrínsecos KLEE. | Pendente: spike da Task 1.1, **para e pede aprovação** | +| D1 | Bitcode LLVM 16 via rustc 1.72.1 (rota A'), agora para todos os geradores, já que tudo passa por `llvm-link-16`. | Herdada; reconfirmada na Task 1.1 | +| D2, D3 | Wasm portado; `--btree` com o mesmo container do Rust (`ContainerBTree.bc`/`BTree.bc` não mudam o comportamento observável). | Herdadas; registrar no reinício (Task 0.1) | + +--- + +## Fase 0 — Reinício e ambiente de integração + +### Task 0.1: Registro do reinício e download reprodutível do Map2Check + +**Files:** +- Create: `scripts/fetch-map2check.sh`, `scripts/test-fetch-map2check.sh` +- Modify: `vendor/MAP2CHECK_VERSION` (gerado pelo script), `docs/cutover/decisoes.md` (seção "Reinício de 2026-10-26": force push `a0f8b9cd → fbbcb929`, D5 revogada por D6, D2/D3 mantidas), `TODO.md` (a seção do repositório externo aponta para este plano), `.github/workflows/CI.yml` (roda o teste do script) + +**Interfaces:** +- Produces: `scripts/fetch-map2check.sh [--with-submodules] []` (padrão `vendor/map2check`); escreve `vendor/MAP2CHECK_VERSION` com as chaves `url branch commit commit_date commit_subject fetched`, uma por linha, `chave=valor`. URL vinda de `MAP2CHECK_URL` (padrão `https://github.com/hbgit/Map2Check`). + +- [ ] **Step 1:** Escrever `scripts/test-fetch-map2check.sh`, no estilo de `scripts/test-check-no-c-era-artifacts.sh`, usando um repositório local de fixture (`file://`) com branches `develop` e `other` e um submódulo. Casos: clona `develop` mesmo com `other` como HEAD do remoto; nenhum `.git` (diretório ou arquivo) sobra em ``; `MAP2CHECK_VERSION` tem as 6 chaves e `commit` igual ao `develop` da fixture; um segundo download substitui o destino; sem `--with-submodules` os submódulos ficam vazios; `` fora do repositório é recusado. +- [ ] **Step 2:** Rodar o teste e ver falhar (script inexistente). +- [ ] **Step 3:** Implementar o script: `git clone --depth 1 --branch develop [--recurse-submodules --shallow-submodules]`, `rev-parse HEAD` antes de `find -name .git -prune -exec rm -rf {} +`, escrita atômica do arquivo de versão. +- [ ] **Step 4:** Rodar o teste e ver passar; rodar o script de verdade e conferir `git status --short` (só `vendor/MAP2CHECK_VERSION`). +- [ ] **Step 5:** Escrever a seção do reinício em `decisoes.md` e o ajuste do `TODO.md`; ligar o teste no CI. +- [ ] **Step 6:** Commit `chore: fetch Map2Check develop as an unversioned local copy and record the restart`. + +### Task 0.2: Inventário da ABI do runtime C + +**Files:** +- Create: `scripts/runtime-abi-inventory.py`, `scripts/test_runtime_abi_inventory.py`, `docs/runtime-abi/inventory.tsv`, `docs/runtime-abi/rust-gap.tsv` + +**Interfaces:** +- Produces: `inventory.tsv` com cabeçalho `symbol kind signature module referenced_by`, onde `kind ∈ {func,data}`, `signature` é o protótipo C normalizado pelo clang (`int (int, const char *)`), `module` é o `.bc` que o define (vários separados por `+` quando o C o define em mais de um), `referenced_by ⊆ {pass,frontend,runtime}` separado por `+`. `rust-gap.tsv`: `symbol status rust_signature`, `status ∈ {same,different,missing}`. +- Consumes: `vendor/map2check/` (Task 0.1). + +- [ ] **Step 1:** Escrever `test_runtime_abi_inventory.py` (unittest) sobre uma árvore de fixture mínima (dois `.c`, um header, um passe com `getOrInsertFunction("map2check_x", ...)` e uma macro `CALL_IF_HELPER` como em `NonDetPass.cpp:186`): espera as linhas exatas do TSV, inclusive a expansão `map2check_non_det_` para cada `c_type` da macro. +- [ ] **Step 2:** Rodar `python3 -I -m unittest scripts/test_runtime_abi_inventory.py` e ver falhar. +- [ ] **Step 3:** Implementar o script: assinaturas por `clang -fsyntax-only -Xclang -ast-dump=json` em cada `.c`; símbolos definidos por `nm` do objeto; referências por literais e macros em `modules/backend/pass/*.cpp` e `modules/frontend/*.cpp`; o gap pelo `nm` de `target/release/libmap2check.a` e pelo `include/map2check.h`. +- [ ] **Step 4:** Rodar o teste e ver passar; gerar os dois TSVs a partir de `vendor/map2check/` e conferir 182 símbolos definidos. +- [ ] **Step 5:** Commit `docs: inventory the C runtime ABI of Map2Check develop`. + +### Task 0.3: Build do Map2Check a partir da cópia, sem tocá-la + +**Files:** +- Create: `scripts/it/build-map2check.sh`, `scripts/it/Dockerfile.time`, `scripts/it/test-build-map2check.sh` + +**Interfaces:** +- Produces: imagem `map2check-dev:develop-` (de `vendor/map2check/Dockerfile.dev`) e `map2check-dev:develop--time` (acrescenta `/usr/bin/time`); instalação em `target/map2check-it/c/` (com `bin/map2check` e `lib/*.bc`); `scripts/it/build-map2check.sh [--image-only]`. + +- [ ] **Step 1:** Escrever o teste: depois do build, (a) o manifesto `sha256sum` de todos os arquivos de `vendor/map2check/` é igual ao de antes; (b) `target/map2check-it/c/lib/` contém os 21 `.bc` do Global Constraints (o `WasmRuntimeStubs.bc` exige wabt na imagem); (c) `llvm-dis-16` lê cada um. +- [ ] **Step 2:** Rodar e ver falhar. +- [ ] **Step 3:** Implementar: a cópia é montada **somente leitura** (`-v vendor/map2check:/src:ro`), o build vai para `target/map2check-it/build` e o `install` para `target/map2check-it/c`, com os flags do `CLAUDE.md` do Map2Check (`cmake -G Ninja -DLLVM_DIR=/usr/lib/llvm-16/lib/cmake/llvm`). +- [ ] **Step 4:** Rodar e ver passar. +- [ ] **Step 5:** Commit `test: build Map2Check develop from the read-only vendor copy`. + +### Task 0.4: Runner do corpus, comparador e linha de base do C + +**Files:** +- Create: `scripts/it/run-corpus.sh`, `scripts/it/compare_baselines.py`, `scripts/it/test_compare_baselines.py`, `docs/cutover/deltas-aceitos.tsv` (só o cabeçalho), `docs/cutover/baselines/c-.tsv` + +**Interfaces:** +- Produces: `run-corpus.sh ` com `REPS` (padrão 5), `GENERATORS` (padrão `afl symex hybrid`; `hybrid` = sem `--nondet-generator`, com `--timeout`), `TIMEOUT_S`; TSV de 11 colunas `task prp gen rep verdict tag line function witness wall_s maxrss_kb`. `compare_baselines.py --deltas docs/cutover/deltas-aceitos.tsv` imprime uma linha por critério e termina com `gate: PASS` ou `gate: FAIL` (exit 0/1). `deltas-aceitos.tsv`: `task prp gen field c_value rust_value sd_ref`. + +- [ ] **Step 1:** Escrever `test_compare_baselines.py` com um caso por critério do gate: veredito por moda de 5 repetições; tag/`Line:`/`Function:` iguais em FAILED; witness presente onde o C gerou; contagem de acertos não diminui; divergência aceita só com `sd_ref` existente em `docs/semantic-deltas.md`; mediana `wall` 1,10 passa e 1,11 falha; TIMEOUT a mais falha; mediana RSS 1,36 passa e 1,37 falha; tarefa isolada > 1,5 só gera aviso. +- [ ] **Step 2:** Rodar e ver falhar. +- [ ] **Step 3:** Implementar o comparador e o runner (o runner roda cada tarefa num diretório novo sob `target/map2check-it/runs/`, dentro da imagem `-time`, com a instalação montada somente leitura). +- [ ] **Step 4:** Rodar o teste e ver passar. +- [ ] **Step 5:** Gerar a linha de base do C (`run-corpus.sh target/map2check-it/c ...`) e confirmar que `compare_baselines.py` do C contra ele mesmo dá `gate: PASS`. +- [ ] **Step 6:** Commit `test: corpus runner, gate comparator and C baseline for Map2Check develop`. + +--- + +## Fase 1 — Layout dos módulos (D7) — ponto de parada + +### Task 1.1: Spike do layout e matriz de link + +**Files:** +- Create: `scripts/it/check-link-matrix.sh`, `scripts/it/test-check-link-matrix.sh`, `docs/cutover/d7-layout.md`; protótipo em `spikes/d7-layout/` (fora do build da crate) + +**Interfaces:** +- Produces: `check-link-matrix.sh `: monta as 120 combinações de `Caller::linkLLVM` (container 2 × modo 5 × gerador 3 × wasm 2 × witness 2) sobre um programa de fumaça, exige `llvm-link-16` sem erro, `llvm-nm-16 --undefined-only` contido na lista permitida do gerador (libc/libm; `klee_*` para `symex`; `__afl_*` para `afl`; `wasm_rt_*` com wasm) e, para `none`/`afl`, link nativo com `clang-16`/`afl-clang-fast`. Saída: uma linha por combinação, `matrix: PASS|FAIL`. + +- [ ] **Step 1:** Escrever o teste do script com dois diretórios de fixture: um válido (os `.bc` do C de `target/map2check-it/c/lib`) que dá `matrix: PASS`, e um com um símbolo duplicado entre módulos que dá `FAIL` apontando a combinação. +- [ ] **Step 2:** Implementar `check-link-matrix.sh` e ver o teste passar. +- [ ] **Step 3:** Prototipar as estratégias e medir cada uma com a matriz: **(A)** `Map2CheckFunctions.bc` = runtime Rust inteiro; os módulos de modo/gerador/container/witness são `.ll` que definem globais fortes de seleção sobre padrões fracos do núcleo; referências a `klee_*`/`__afl_*` marcadas `extern_weak` no pós-processamento. **(B)** módulos de gerador como crates Rust `no_std` emitidos como `--crate-type=lib --emit=llvm-bc`, ligados ao núcleo. Para o AFL++, decidir entre `main` + `setjmp`/`longjmp` em `.ll` (o `longjmp` nunca atravessa frames Rust) e um loop sem `longjmp`; medir execuções/s contra o C. +- [ ] **Step 4:** Para a estratégia escolhida: matriz `PASS`; KLEE roda o programa de fumaça seguro com `kleeDroppedPaths() == 0`; o AFL++ executa ≥ 90% das execuções/s do C no mesmo programa. +- [ ] **Step 5:** Registrar em `docs/cutover/d7-layout.md` as medições, a escolha e o mapa `módulo .bc → conteúdo`; commit `docs: D7 module layout spike for the drop-in runtime`. +- [ ] **Step 6:** **Parar e pedir aprovação do usuário para a D7.** As Fases 2 e 3 dependem dela. + +--- + +## Fase 2 — Paridade da ABI (código existente revalidado) + +Em todas as tasks desta fase: a semântica de referência é o `.c` citado na cópia `vendor/`; cada função exportada ganha teste Rust que fixa o comportamento lido no C (valores, arquivos escritos, mensagens); o código existente indicado é reaproveitado e adaptado, não reescrito sem motivo; símbolos exportados seguem o mapa da D7; `docs/runtime-abi/pending.txt` perde os símbolos que a task entrega. + +### Task 2.0: Guardas de conformidade da ABI + +**Files:** +- Create: `tests/abi/check-signatures.sh`, `scripts/runtime-abi-coverage.sh`, `scripts/test-runtime-abi-coverage.sh`, `docs/runtime-abi/pending.txt` +- Modify: `.github/workflows/CI.yml`, `cbindgen.toml` (se precisar de tipos `long`/`unsigned long` nativos, `usize_is_size_t`) + +**Interfaces:** +- Produces: `check-signatures.sh `: para cada header do runtime C, compila com `clang -fsyntax-only -Werror` uma unidade que inclui o header e `include/map2check.h`; tipos conflitantes falham. `runtime-abi-coverage.sh`: falha se o conjunto de símbolos de `inventory.tsv` com `referenced_by` contendo `pass` ou `frontend`, menos os exports da biblioteca, for diferente de `pending.txt` (nem falta nem sobra). + +- [ ] **Step 1:** Escrever `test-runtime-abi-coverage.sh` (pendência esquecida falha; símbolo entregue ainda listado falha; igualdade passa). +- [ ] **Step 2:** Implementar os dois scripts; gerar `pending.txt` inicial; ver o teste passar e `check-signatures.sh` passar para os headers cujos símbolos já existem com a mesma assinatura. +- [ ] **Step 3:** Ligar ambos no CI; commit `test: guard the C runtime ABI signatures and coverage`. + +### Task 2.1: Núcleo, rastreio de blocos, propriedade e witness + +**Files:** referência `Map2CheckFunctions.c`, `TrackBBLog.c`, `PropertyGenerator.c`, `WitnessGeneration.c`, `WitnessGenerationNone.c`; modificar `rust/src/ffi.rs`, `rust/src/state.rs`, `rust/src/bbtrack/mod.rs`, `rust/src/output/legacy_files.rs`; testes em `rust/src/ffi_tests.rs`. + +**Interfaces:** +- Produces: os 17 símbolos que `inventory.tsv` atribui a `Map2CheckFunctions.bc` (entre eles `map2check_init`, `map2check_track_bb`, `map2check_assume`, `map2check_target_function`, `map2check_success`, `__VERIFIER_error`), os de `TrackBBLog.bc`, `PropertyGenerator.bc` (`write_property*`) e dos dois módulos de witness (`generate_aux_files`, `generate_confirmation_file`), com as assinaturas do inventário. + +- [ ] **Step 1:** Testes: um por função exportada, mais `property_file_matches_c_format` (conteúdo byte a byte de `map2check_property` e `map2check_property_` igual ao que o C escreve para a mesma chamada, com o texto copiado do C), `track_bb_log_matches_c_format`, e o comportamento do C para arquivos de propriedade de uma execução anterior no mesmo diretório. +- [ ] **Step 2:** Rodar `cargo test --all-features` e ver falhar. +- [ ] **Step 3:** Implementar; nomes da API núcleo que colidem com a ABI C (`map2check_init`, `map2check_success`) passam a ter a assinatura C. +- [ ] **Step 4:** Testes, `check-signatures.sh` e `runtime-abi-coverage.sh` passam. +- [ ] **Step 5:** Commit `feat: export the core functions of the Map2Check C runtime ABI`. + +### Task 2.2: Log de nondet e gerador `none` + +**Files:** referência `NonDetLog.c`, `NonDetGeneratorNone.c`; modificar `rust/src/nondet/mod.rs`, `rust/src/ffi.rs`. + +**Interfaces:** +- Produces: `map2check_nondet_` (log), `map2check_non_det_` e `main` do gerador `none` (segundo a D7), `klee_log.csv` no formato do C, para todos os `` do inventário (incluindo `u8 u16 u32 loff_t sector_t pchar pointer memory`). + +- [ ] **Step 1:** Testes `nondet_roundtrip_` (valor extremo de cada largura/sinal registrado e relido igual) e `klee_log_csv_matches_c_format`. +- [ ] **Step 2–4:** ver falhar, implementar, ver passar (com os dois scripts de ABI). +- [ ] **Step 5:** Commit `feat: export the nondet log and the none generator`. + +### Task 2.3: Modo overflow + +**Files:** referência `AnalysisModeOverflow.c`; modificar `rust/src/analysismode/overflow.rs`, `rust/src/ffi.rs`. + +**Interfaces:** Produces: `map2check_binop_{add,sub,mul}[_uint]`, `map2check_binop_sdiv`, `map2check_binop_srem`, `analysis_*` do modo e os demais símbolos que `inventory.tsv` atribui a `AnalysisModeOverflow.bc`. + +- [ ] **Step 1:** Testes por operação nos limites (`INT_MAX + 1`, `INT_MIN - 1`, `INT_MIN / -1`, `x % 0`, `UINT_MAX + 1`), com a tag e o texto de propriedade do C. +- [ ] **Step 2–4:** ver falhar, implementar, ver passar. +- [ ] **Step 5:** Commit `feat: export the overflow analysis mode`. + +### Task 2.4: Modos assert e none (alcance e cover-branches) + +**Files:** referência `AnalysisModeAssert.c`, `AnalysisModeNone.c`; modificar `rust/src/analysismode/assert.rs`, `rust/src/analysismode/mode.rs`, `rust/src/ffi.rs`. + +- [ ] **Step 1:** Testes: `map2check_assert`/`__VERIFIER_assert` com 0 e 1; `map2check_target_function` em modo none produz a propriedade de alcance do C; `analysis_is_program_correct` por modo. +- [ ] **Step 2–4:** ver falhar, implementar, ver passar. +- [ ] **Step 5:** Commit `feat: export the assert and none analysis modes`. + +### Task 2.5: Modo memtrack, logs de memória e containers + +**Files:** referência `AnalysisModeMemtrack.c`, `AllocationLog.c`, `ListLog.c`, `HeapLog.c`, `ContainerRealloc.c`, `ContainerBTree.c`, `BTree.c`; modificar `rust/src/memtrack/mod.rs`, `rust/src/analysismode/memory.rs`, `rust/src/ffi.rs`. + +**Interfaces:** Produces: `map2check_{malloc,calloc,realloc,free,free_resolved_address,alloca,add_store_pointer,check_deref,check_cstring,...}` com as assinaturas do inventário; `list_log.csv` no formato do C; `analysis_*` do modo. + +- [ ] **Step 1:** Testes portados de cada caminho de veredito do C: deref inválido, free inválido, double free, free de endereço não-heap, `check_cstring` além do bloco, e os formatos de `list_log.csv`; `--btree` e Array dão o mesmo resultado (D3). +- [ ] **Step 2–4:** ver falhar, implementar, ver passar. +- [ ] **Step 5:** Commit `feat: export the memtrack analysis mode`. + +### Task 2.6: Modo memcleanup + +**Files:** referência `AnalysisModeMemcleanup.c`; modificar `rust/src/analysismode/memory.rs`, `rust/src/ffi.rs`. + +- [ ] **Step 1:** Testes: `memcleanup_reports_leak_at_exit` (processo filho chama `exit(0)` com bloco alcançável não liberado; espera FALSE-MEMCLEANUP no arquivo de propriedade) e os demais caminhos do `.c`. +- [ ] **Step 2–4:** ver falhar, implementar, ver passar. +- [ ] **Step 5:** Commit `feat: export the memcleanup analysis mode`. + +### Task 2.7: Gerador KLEE + +**Files:** referência `NonDetGeneratorKlee.c`; modificar `rust/src/nondet/klee.rs`, `klee/Cargo.toml`, `scripts/build-klee-runtime.sh` (só o que a D7 pedir). (Situação após a Task 2.10: `rust/src/nondet/klee.rs` e `scripts/build-klee-runtime.sh` foram removidos; o gerador KLEE é IR de `scripts/runtime-ir/gen_ll.py`.) + +- [ ] **Step 1:** Teste de integração `klee_safe_program_drops_no_paths` (programa seguro com 3 nondet e um laço limitado, ligado pela combinação `symex` da matriz, roda no KLEE da imagem; espera zero caminhos descartados e o mesmo número de testes `.ktest` que o C) e `klee_unsafe_program_reports_same_line`. +- [ ] **Step 2–4:** ver falhar, implementar, ver passar. +- [ ] **Step 5:** Commit `feat: export the KLEE nondet generator`. + +### Task 2.8: Gerador AFL++ + +**Files:** referência `NonDetGeneratorAFL.c`; criar ou modificar o módulo indicado pela D7 (por exemplo `rust/src/nondet/afl.rs` e `rt/afl_main.ll`). + +- [ ] **Step 1:** Testes: `afl_persistent_iterations_start_from_clean_state` (duas entradas no mesmo processo; a primeira viola, a segunda não; a segunda não herda a violação); `afl_assume_rejection_cancels_only_current_input`; entrada curta completada como o C completa (zeros). +- [ ] **Step 2–4:** ver falhar, implementar, ver passar; execuções/s ≥ 90% do C no programa de fumaça. +- [ ] **Step 5:** Commit `feat: export the AFL++ nondet generator`. + +### Task 2.9: Stubs wasm + +**Files:** referência `WasmRuntimeStubs.c`; modificar `rust/src/wasm.rs`. + +- [ ] **Step 1:** Testes por stub `wasm_rt_*` e por `map2check_wasm_*` com a assinatura do C. +- [ ] **Step 2–4:** ver falhar, implementar, ver passar. +- [ ] **Step 5:** Commit `feat: export the wasm runtime stubs`. + +### Task 2.10: Remover a API núcleo antiga do C ABI + +**Files:** `rust/src/ffi.rs`, `tests/ffi_harness.c`, `test/code*_by_caller_test.c`, `docs/api-deltas.md`, `Cargo.toml` (`version = "2.0.0-rc.1"`). + +- O núcleo `Map2CheckFunctions.bc` não pode definir `__VERIFIER_assert` nem outro nome que só um módulo de modo define (D7: um programa que define `__VERIFIER_assert` liga nos modos sem assert). A exportação legada em `rust/src/nondet/{libfuzzer,klee}.rs` sai nesta task ou fica fora do build `.bc`; `check-link-matrix.sh --ref` sobre a saída da Task 3.1 é o teste. Com isso, `scripts/runtime-ir/test-assert-none-link.sh` perde o `-Wl,--allow-multiple-definition` e a nota do export legado passa a falhar se o símbolo reaparecer. + +- [ ] **Step 1:** Remover os exports da API núcleo que não estão no inventário; reescrever `ffi_harness.c` e os três programas de `test/` contra a ABI C (mesmos cenários de estresse). +- [ ] **Step 2:** `pending.txt` vazio; rodar a suíte completa de qualidade (Global Constraints) e o harness com ASan/UBSan. +- [ ] **Step 3:** Commit `refactor!: the C ABI is the Map2Check runtime ABI only`. + +--- + +## Fase 3 — Empacotamento + +### Task 3.1: Os 21 módulos `.bc` e a sobreposição de instalação + +**Files:** Create `scripts/build-runtime-bc.sh`, `scripts/it/overlay-install.sh`, `scripts/it/test-overlay-install.sh`; modify `.github/workflows/CI.yml` (job que gera os `.bc` e roda a matriz na imagem `map2check-dev:develop-`). + +**Interfaces:** +- Produces: `build-runtime-bc.sh ` gera exatamente os 21 arquivos do Global Constraints, segundo a D7, de forma determinística (dois builds dão `sha256` iguais). `overlay-install.sh ` copia a instalação do C e troca só `lib/*.bc`. + +- [ ] **Step 1:** Testes: lista de arquivos exata; `llvm-dis-16` lê todos; determinismo; o overlay não altera `bin/`; `check-link-matrix.sh` dá `matrix: PASS` sobre a saída. + Também: no build `.bc`, o núcleo chama os `generate_aux_files`/`generate_confirmation_file` externos (definidos por `WitnessGeneration.bc` → `__m2c_rt_witness_*`, ou vazios em `WitnessGenerationNone.bc`), em vez de decidir por `config.witness`; teste de que uma execução com witness escreve os arquivos auxiliares exatamente uma vez e sem witness nenhuma vez. Os `map2check_assume*` (adiados da Task 2.1) foram entregues na Task 2.10 pelo IR de cola do núcleo; aqui eles entram no `Map2CheckFunctions.bc` final. O `klee_log.csv` do witness sai só pelo `nondet_generate_aux_witness_files` do gerador ligado, como em `WitnessGeneration.c:26-27`: o `none` não escreve nada, e o KLEE usa os ganchos `__m2c_rt_nondet_log_{len,get_i64,set_i64}` com o limite de 1024 linhas de `NonDetGeneratorKlee.c:33-62`. Teste por gerador. O `map2check_init` do núcleo chama o `analysis_init` externo do módulo de modo ligado e depois o `nondet_init` do gerador, nessa ordem (`Map2CheckFunctions.c:37-38`); teste de que um programa ligado aos `.bc`, sem chamar `analysis_init` à mão, escreve os arquivos de propriedade e aborta em cada modo. Na reexecução de um crash do AFL++ pelo binário de witness, o drop-in escreve `map2check_checked_error` como o C; o caso `afl_crash_replay` de `scripts/runtime-ir/test-afl-generator.sh` passa de informativo a asserção. Também: depois de um `wasm_rt_trap`, o drop-in escreve `map2check_checked_error` como o C (Task 2.9 só verificou `map2check_property__unknown`). Quando os programas de host puderem ligar o `WitnessGeneration.ll`, reavaliar e, se não houver mais uso, remover o export `__m2c_rt_witness_enable` (Task 2.10). +- [ ] **Step 2–4:** ver falhar, implementar, ver passar. +- [ ] **Step 5:** Commit `build: package the runtime as the 21 bitcode modules Map2Check links`. + +### Task 3.2: Pacote de release + +**Files:** `scripts/package-release.sh`, `scripts/test-package-release.sh`, `docs/release.md`. + +- [ ] **Step 1:** Teste: o tarball contém `lib/*.bc` (os 21), `include/map2check.h`, `MAP2CHECK_VERSION` com o commit do `develop` validado e `SHA256SUMS`. +- [ ] **Step 2–4:** ver falhar, implementar, ver passar. +- [ ] **Step 5:** Commit `build: ship the bitcode runtime in the release package`. + +--- + +### Task 3.3: Encolher o núcleo drop-in (decisão do usuário, 2026-10-27) + +A checagem prévia da Task 4.1 deu mediana de tempo de parede rust/c 2,78 (afl 9,31; híbrido 7,25; symex 1,21), contra o limite 1,10. A causa medida é o tamanho do `Map2CheckFunctions.bc`: 762 580 bytes e 43 340 linhas de IR, contra 6 272 bytes e 257 linhas no C. O frontend compila o núcleo ligado três vezes com `afl-clang-fast`, a ~2 s cada. Mudar o frontend é proibido (regras 2 e 3). + +**Files:** `rust/src/` (código compilado no build `dropin`), `klee/Cargo.toml`, `scripts/build-runtime-bc.sh`. + +- [ ] **Step 1:** Medir o ponto de partida: linhas de IR e bytes do núcleo, e o tempo de uma compilação `afl-clang-fast` do programa de fumaça ligado ao núcleo, no C e no Rust. +- [ ] **Step 2:** Reduzir o núcleo `dropin`: sem `serde_json`, `std::fmt`/`io` pesados nem código só da API antiga; `no_std` + `alloc` onde couber; `opt-level` `s`/`z`; pânico direto para `abort`. Comportamento da ABI C inalterado: todos os testes de host, de link, de imagem e a matriz `--ref` continuam passando. A lista de pós-processamento da D7 não muda sem aprovação do usuário. +- [ ] **Step 3:** Novo guarda no `build-runtime-bc.sh`: teto de linhas de IR do núcleo no valor alcançado mais uma folga pequena. +- [ ] **Step 4:** Repetir a checagem prévia da Task 4.1 (mesmo subconjunto, mesmo método). Se a mediana não ficar ≤ 1,10, parar e levar o número ao usuário. +- [ ] **Step 5:** Commit `perf: shrink the drop-in core the frontend compiles`. + +## Fase 4 — Gate diferencial + +### Task 4.1: Primeira execução do gate + +- Diferença esperada, fora da paridade: em execuções com violação sob `symex`, a divisão entre caminhos completos e parciais do KLEE pode diferir do C (ordem do searcher com `--exit-on-error-type=Abort`); o total de `.ktest`, o veredito, a linha e a função precisam bater (Task 2.7, `scripts/runtime-ir/test-klee-generator.sh`). Ponto de atenção para o tempo de parede: no programa `assume` da Task 2.8 o AFL++ do drop-in fez 0,899 das exec/s do C (mediana de 5), contra 1,008 no programa de fumaça. Risco maior, medido no smoke da Task 3.1: execuções afl/híbridas que terminam cedo levam ~5,8 s no drop-in contra ~0,6 s no C, e as `symex` ~2,8× (o frontend compila o núcleo Rust, ~34 mil linhas de IR com std, três vezes com `afl-clang-fast` e o `llvm-link`/`--optimize` do KLEE). Antes de rodar o gate completo, medir a razão mediana de tempo de parede num subconjunto do corpus; se passar de 1,10, parar e levar ao usuário as opções (encolher o núcleo, por exemplo) em vez de rodar as horas do gate. + +- [ ] **Step 1:** Rodar `run-corpus.sh` na instalação sobreposta (Task 3.1) com os mesmos `REPS`, geradores e imagem da linha de base do C. +- [ ] **Step 2:** `compare_baselines.py` → `gate: PASS`. Cada divergência vira uma task de correção na fase 2 com teste RED/GREEN, ou um `SD-xx` aprovado pelo usuário e listado em `deltas-aceitos.tsv`. +- [ ] **Step 3:** Registrar em `docs/cutover/gate-.md` (commit do `develop`, commit da biblioteca, razões wall/RSS, tarefas > 1,5 com perfil); commit `docs: first differential gate of the drop-in runtime`. + +### Task 4.2: Segunda execução consecutiva + +- [ ] **Step 1:** Baixar de novo o `develop` (Task 0.1); se o commit mudou, regenerar a linha de base do C nele (Task 0.4, Step 5). +- [ ] **Step 2:** Repetir a Task 4.1 sobre esse commit; exige `gate: PASS`. +- [ ] **Step 3:** Commit `docs: second differential gate of the drop-in runtime`. + +--- + +## Fase 5 — Entrega ao mantenedor + +### Task 5.1: Documentação, versão e guia de troca + +**Files:** `README.md`, `CLAUDE.md` (seção de arquitetura), `docs/integration-guide.md`, `CHANGELOG.md`, `TODO.md`, `docs/cutover/handoff-map2check.md`. + +- [ ] **Step 1:** `integration-guide.md`: como instalar os 21 `.bc` no lugar dos do C (`overlay-install.sh` ou cópia para `${MAP2CHECK_PATH}/lib`), o commit do `develop` validado e como reproduzir o gate. `handoff-map2check.md`: o que o mantenedor precisaria mudar no Map2Check para adotar o pacote (CMake, CI, remoção do C), como sugestão, sem nenhuma ação do agente lá. +- [ ] **Step 2:** `CHANGELOG.md` com a entrada 2.0.0-rc.1 (D6, ABI C, empacotamento, resultado dos dois gates); `README.md`/`CLAUDE.md` deixam de descrever a API núcleo como a interface com o Map2Check; `TODO.md` atualizado. +- [ ] **Step 3:** Commit `docs: hand the drop-in runtime over to the Map2Check maintainer`. +- [ ] **Step 4:** Informar o usuário de que a release (tag `v2.0.0-rc.1`, PR para `develop`/`main`) está pronta; push, PR, tag e release só com pedido explícito. + +--- + +## Execution order + +0.1 → 0.2 → 0.3 → 0.4 → **1.1 (D7, aprovação)** → 2.0 → 2.1 → 2.2 → 2.3 → 2.4 → 2.5 → 2.6 → 2.7 → 2.8 → 2.9 → 2.10 → 3.1 → 3.2 → 3.3 → 4.1 → 4.2 → 5.1. diff --git a/klee/Cargo.lock b/klee/Cargo.lock index a01fc7d..772332a 100644 --- a/klee/Cargo.lock +++ b/klee/Cargo.lock @@ -2,127 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - [[package]] name = "map2check-klee-runtime" -version = "1.1.0-rc1" -dependencies = [ - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "serde" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.154" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "syn" -version = "3.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "2.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "unicode-ident" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" +version = "2.0.0-rc.1" diff --git a/klee/Cargo.toml b/klee/Cargo.toml index 07e4076..256c6af 100644 --- a/klee/Cargo.toml +++ b/klee/Cargo.toml @@ -1,9 +1,10 @@ # SPDX-License-Identifier: GPL-3.0-only # -# Second manifest: the runtime of the Map2Check `symex` generator (D1, route -# A', docs/cutover/decisoes.md). It compiles the same sources as the main -# build (../rust/src) with rustc 1.72.1, whose LLVM 16 writes bitcode that -# llvm-link-16 and KLEE 3.1 read. Build it with scripts/build-klee-runtime.sh. +# Second manifest: the Rust core of the drop-in runtime as LLVM 16 bitcode +# (D1, route A', docs/cutover/decisoes.md; D7). It compiles the same sources +# as the main build (../rust/src) with rustc 1.72.1, whose LLVM 16 writes +# bitcode that llvm-link-16 and KLEE 3.1 read. Built by +# scripts/build-runtime-bc.sh (feature `dropin`). # # Why a directory of its own and not a workspace member: a workspace shares # one Cargo.lock, and the main one is v4, which cargo 1.72 cannot parse; the @@ -14,7 +15,8 @@ # Rules for this manifest (D1 condition 5, costs (a)-(c)): # - no build script (`build = false`): the C header comes from the main build; # - no dev-dependencies; -# - dependency caps that compile on 1.72 (thiserror 2.0.21 needs 1.77); +# - no dependencies since Task 3.3; any added later must compile on 1.72 +# (thiserror 2.0.21, for one, needs 1.77); # - Cargo.lock regenerated only with the MSRV-aware resolver: # rustup toolchain install 1.86.0 --profile minimal # CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS=fallback RUSTUP_TOOLCHAIN=1.86.0 \ @@ -22,12 +24,12 @@ # sed -i 's/^version = 4/version = 3/' klee/Cargo.lock [package] name = "map2check-klee-runtime" -version = "1.1.0-rc1" +version = "2.0.0-rc.1" edition = "2021" rust-version = "1.72" build = false publish = false -description = "Map2Check runtime for the KLEE (symex) generator: the map2check-library sources built as LLVM 16 bitcode" +description = "Rust core of the Map2Check drop-in runtime (Map2CheckFunctions.bc): the map2check-library sources built as LLVM 16 bitcode" license = "GPL-3.0-only" authors = ["Herbert Rocha "] @@ -36,21 +38,35 @@ name = "map2check" crate-type = ["staticlib"] path = "../rust/src/lib.rs" +# No dependencies: the drop-in core (feature `dropin`) has no JSON channel +# (serde, serde_json) and formats its messages itself (rust/src/cfmt.rs), +# Task 3.3. [dependencies] -serde = { version = ">=1.0, <=1.0.229", features = ["derive"] } -serde_json = ">=1.0, <=1.0.154" -thiserror = ">=2.0, <=2.0.20" [features] -klee = [] wasm = [] +# The Rust core of the drop-in Map2CheckFunctions.bc (D7, layout A-fwd; +# build: scripts/build-runtime-bc.sh). Its map2check_init +# calls the external analysis_init and nondet_init, which only the linked +# mode and generator modules define, in that order (Map2CheckFunctions.c: +# 37-38). Not a feature of the main manifest: cargo test, the ffi harness, +# the golden tests and the host link tests link the staticlib without those +# modules and keep calling analysis_init by hand. +dropin = [] +# The manifest exists only for the drop-in core: without `dropin` it would +# need the JSON channel and serde (Task 3.3). +default = ["dropin"] # panic = "abort": nothing catches a panic under KLEE, and the unwind # machinery is code KLEE would have to execute. Fat LTO and one codegen unit # put std into the one module KLEE loads (without LTO KLEE cannot resolve # `__rust_no_alloc_shim_is_unstable`); D1 condition 6. +# opt-level "z": the frontend runs afl-clang-fast three times on the program +# linked with this core, and that time grows with the core's IR (Task 3.3). +# Measured on the final sources: 14 674 instruction lines at opt-level 3, +# 9 667 at "s", 8 865 at "z". [profile.release] -opt-level = 3 +opt-level = "z" lto = "fat" codegen-units = 1 panic = "abort" diff --git a/rust/src/analysismode/mode.rs b/rust/src/analysismode/mode.rs index 8f22407..453025c 100644 --- a/rust/src/analysismode/mode.rs +++ b/rust/src/analysismode/mode.rs @@ -17,7 +17,7 @@ use crate::{ caller::ViolatedProperty, - memtrack, + memtrack::{self, c_mode::MemMode}, state::{AnalysisState, Config}, }; @@ -100,11 +100,21 @@ pub fn analysis_init(mode: Option, state: &mut AnalysisState) { pub fn analysis_destroy(mode: Option, state: &mut AnalysisState) { match mode { // Unset: every check is active, so the memory log is in use. - None => state.memtrack.clear(), - // AnalysisModeMemtrack.c:132-136 — frees heap, list and allocation logs. - Some(AnalysisMode::Memtrack) => state.memtrack.clear(), + None => { + state.memtrack.clear(); + state.c_memory.clear(); + } + // AnalysisModeMemtrack.c:132-136 — frees heap, list and allocation + // logs (the drop-in ones too). + Some(AnalysisMode::Memtrack) => { + state.memtrack.clear(); + state.c_memory.clear(); + } // AnalysisModeMemcleanup.c:132-136 — same as memtrack. - Some(AnalysisMode::Memcleanup) => state.memtrack.clear(), + Some(AnalysisMode::Memcleanup) => { + state.memtrack.clear(); + state.c_memory.clear(); + } // AnalysisModeNone.c:15-17 — "DO NOTHING". Some(AnalysisMode::None) => {} // AnalysisModeOverflow.c:281 — empty. @@ -123,11 +133,16 @@ pub fn analysis_destroy(mode: Option, state: &mut AnalysisState) { /// /// The memcleanup option is an alias of the memcleanup mode for the leak tag: /// in every mode that checks leaks it selects `MemsafetyMemcleanup`. +/// +/// In memtrack mode the drop-in logs of the `__m2c_rt_memtrack_*` entry +/// points (`AnalysisState::c_memory`) are checked first, with develop's rule +/// (`valid_allocation_log`, which appends the freed rows the witness logs +/// show); that is why the state is mutable. pub fn analysis_is_program_correct( config: Config, - state: &AnalysisState, + state: &mut AnalysisState, ) -> Option { - let leak = |property| memtrack::has_memcleanup_error(&state.memtrack).then_some(property); + let leak = |property| old_api_leak(&state.memtrack).then_some(property); let memtrack_tag = if config.memcleanup { ViolatedProperty::MemsafetyMemcleanup } else { @@ -139,13 +154,22 @@ pub fn analysis_is_program_correct( // AnalysisModeMemtrack.c:138-146 — FALSE_MEMTRACK when // valid_allocation_log returns non-zero, i.e. any heap block is still // live; no reachability rule. `has_memcleanup_error` is that test. - Some(AnalysisMode::Memtrack) => leak(memtrack_tag), + Some(AnalysisMode::Memtrack) => state + .c_memory + .is_program_correct(MemMode::Memtrack) + .or_else(|| leak(memtrack_tag)), // AnalysisModeMemcleanup.c:138-148 — FALSE_MEMCLEANUP only when the // last leaked address (valid_allocation_log, AllocationLog.c:69-101) // is still referenced by a pointer variable (is_memcleanup_error, - // ListLog.c:40-65). An unreachable leak gives no verdict. - Some(AnalysisMode::Memcleanup) => memtrack::has_develop_memcleanup_error(&state.memtrack) - .then_some(ViolatedProperty::MemsafetyMemcleanup), + // ListLog.c:40-65). An unreachable leak gives no verdict. The drop-in + // logs are checked first with that rule, as in memtrack. + Some(AnalysisMode::Memcleanup) => state + .c_memory + .is_program_correct(MemMode::Memcleanup) + .or_else(|| { + old_api_develop_memcleanup_error(&state.memtrack) + .then_some(ViolatedProperty::MemsafetyMemcleanup) + }), // AnalysisModeNone.c:19 — TRUE. Some(AnalysisMode::None) => None, // AnalysisModeOverflow.c:283 — TRUE. @@ -157,6 +181,36 @@ pub fn analysis_is_program_correct( } } +/// The leak test of the records of the old API (`map2check_map_*`, +/// `AnalysisState::memtrack`). The drop-in core has no such records (only +/// the `__m2c_rt_*` entry points record, into `c_memory`), so there it is +/// always false and the code is not compiled (Task 3.3). +fn old_api_leak(records: &[memtrack::MemTrackEntry]) -> bool { + #[cfg(not(feature = "dropin"))] + { + memtrack::has_memcleanup_error(records) + } + #[cfg(feature = "dropin")] + { + debug_assert!(records.is_empty()); + false + } +} + +/// develop's memcleanup verdict on the records of the old API +/// ([`old_api_leak`]). +fn old_api_develop_memcleanup_error(records: &[memtrack::MemTrackEntry]) -> bool { + #[cfg(not(feature = "dropin"))] + { + memtrack::has_develop_memcleanup_error(records) + } + #[cfg(feature = "dropin")] + { + debug_assert!(records.is_empty()); + false + } +} + /// Whether `check` reports violations in `mode`. An inactive check records /// nothing, as when develop links a mode whose file has no such check (the /// pass of another mode never emits the call) or whose check never fails. @@ -367,40 +421,40 @@ mod tests { #[test] fn program_correct_unset_reports_leak_by_option() { - let s = leaked_state(); + let mut s = leaked_state(); assert_eq!( - analysis_is_program_correct(config(None, false), &s), + analysis_is_program_correct(config(None, false), &mut s), Some(ViolatedProperty::MemsafetyMemtrack) ); assert_eq!( - analysis_is_program_correct(config(None, true), &s), + analysis_is_program_correct(config(None, true), &mut s), Some(ViolatedProperty::MemsafetyMemcleanup) ); assert_eq!( - analysis_is_program_correct(config(None, false), &AnalysisState::default()), + analysis_is_program_correct(config(None, false), &mut AnalysisState::default()), None ); } #[test] fn program_correct_none_ignores_leak() { - let s = leaked_state(); + let mut s = leaked_state(); for memcleanup in [false, true] { let c = config(Some(AnalysisMode::None), memcleanup); - assert_eq!(analysis_is_program_correct(c, &s), None); + assert_eq!(analysis_is_program_correct(c, &mut s), None); } } #[test] fn program_correct_memtrack_reports_leak_as_memtrack() { - let s = leaked_state(); + let mut s = leaked_state(); let c = config(Some(AnalysisMode::Memtrack), false); assert_eq!( - analysis_is_program_correct(c, &s), + analysis_is_program_correct(c, &mut s), Some(ViolatedProperty::MemsafetyMemtrack) ); assert_eq!( - analysis_is_program_correct(c, &AnalysisState::default()), + analysis_is_program_correct(c, &mut AnalysisState::default()), None ); } @@ -410,7 +464,7 @@ mod tests { // The option is an alias of the memcleanup mode for the leak tag. let c = config(Some(AnalysisMode::Memtrack), true); assert_eq!( - analysis_is_program_correct(c, &leaked_state()), + analysis_is_program_correct(c, &mut leaked_state()), Some(ViolatedProperty::MemsafetyMemcleanup) ); } @@ -422,7 +476,7 @@ mod tests { assert_eq!( analysis_is_program_correct( config(Some(AnalysisMode::Memcleanup), false), - &leaked_state() + &mut leaked_state() ), None, "an unreferenced leak is no memcleanup error" @@ -433,11 +487,11 @@ mod tests { )); let c = config(Some(AnalysisMode::Memcleanup), false); assert_eq!( - analysis_is_program_correct(c, &s), + analysis_is_program_correct(c, &mut s), Some(ViolatedProperty::MemsafetyMemcleanup) ); assert_eq!( - analysis_is_program_correct(c, &AnalysisState::default()), + analysis_is_program_correct(c, &mut AnalysisState::default()), None ); } @@ -445,19 +499,76 @@ mod tests { #[test] fn program_correct_overflow_ignores_leak() { let c = config(Some(AnalysisMode::Overflow), true); - assert_eq!(analysis_is_program_correct(c, &leaked_state()), None); + assert_eq!(analysis_is_program_correct(c, &mut leaked_state()), None); } #[test] fn program_correct_assert_ignores_leak() { let c = config(Some(AnalysisMode::Assert), true); - assert_eq!(analysis_is_program_correct(c, &leaked_state()), None); + assert_eq!(analysis_is_program_correct(c, &mut leaked_state()), None); } #[test] fn program_correct_target_ignores_leak() { let c = config(Some(AnalysisMode::Target), true); - assert_eq!(analysis_is_program_correct(c, &leaked_state()), None); + assert_eq!(analysis_is_program_correct(c, &mut leaked_state()), None); + } + + #[test] + fn program_correct_memcleanup_checks_the_drop_in_logs_with_the_c_rule() { + // AnalysisModeMemcleanup.c:138-148 over the drop-in logs: only a + // leak still referenced by a pointer is FALSE_MEMCLEANUP. + let c = config(Some(AnalysisMode::Memcleanup), false); + let mut s = AnalysisState::default(); + assert!(s.c_memory.malloc(0x1000, 8)); + assert_eq!(analysis_is_program_correct(c, &mut s), None); + let mut s = AnalysisState::default(); + let mut step = 0; + assert!(s.c_memory.malloc(0x1000, 8)); + s.c_memory + .add_store_pointer(&mut step, 0x10, 0x1000, 1, b"p", 1, b"main"); + assert_eq!( + analysis_is_program_correct(c, &mut s), + Some(ViolatedProperty::MemsafetyMemcleanup) + ); + } + + #[test] + fn program_correct_memtrack_checks_the_drop_in_logs_with_the_c_rule() { + // AnalysisModeMemtrack.c:138-146 over the logs of the + // __m2c_rt_memtrack_* entry points: valid_allocation_log appends a + // freed row for the leak, so the next verdict is TRUE. + let mut s = AnalysisState::default(); + assert!(s.c_memory.malloc(0x1000, 8)); + let c = config(Some(AnalysisMode::Memtrack), false); + assert_eq!( + analysis_is_program_correct(c, &mut s), + Some(ViolatedProperty::MemsafetyMemtrack) + ); + assert_eq!( + s.c_memory.allocation_log_bytes(), + b"0x1000;8;0\n0x1000;0;1\n" + ); + assert_eq!(analysis_is_program_correct(c, &mut s), None); + // Other modes do not read them. + let mut s = AnalysisState::default(); + assert!(s.c_memory.malloc(0x1000, 8)); + let c = config(Some(AnalysisMode::Overflow), false); + assert_eq!(analysis_is_program_correct(c, &mut s), None); + } + + #[test] + fn destroy_of_the_memory_modes_frees_the_drop_in_logs() { + for mode in [ + None, + Some(AnalysisMode::Memtrack), + Some(AnalysisMode::Memcleanup), + ] { + let mut s = AnalysisState::default(); + assert!(s.c_memory.malloc(0x1000, 8)); + analysis_destroy(mode, &mut s); + assert!(!s.c_memory.has_rows(), "{mode:?}"); + } } // ── check_enabled: one test per arm ───────────────────────────────────── diff --git a/rust/src/analysismode/overflow.rs b/rust/src/analysismode/overflow.rs index 6d71cfc..3c217ba 100644 --- a/rust/src/analysismode/overflow.rs +++ b/rust/src/analysismode/overflow.rs @@ -210,14 +210,31 @@ pub struct SignedBinop { pub bits: u32, } +/// `signed_max_for_width` (`:111-124`) by width: index 0 stands for a width +/// of 0 or 64 and more (64 bits), index 1..=63 for that many bits. +const SIGNED_MAX: [i64; 64] = { + let mut t = [i64::MAX; 64]; + let mut bits = 1; + while bits < 64 { + t[bits] = (1i64 << (bits - 1)) - 1; + bits += 1; + } + t +}; + /// `signed_min_for_width` / `signed_max_for_width` (`:111-124`): a width of /// 0 or 64 and more means 64 bits. +/// +/// A table, not a shift, so that rustc's optimiser emits no `freeze`, which +/// KLEE 3.1 cannot execute (scripts/runtime-ir/check_no_freeze.py): a shift +/// by a width-dependent amount, speculated out of its branch or into the +/// branch-free `and` of the range test, needed one (`shl nuw`, `ashr`, +/// `lshr`). A load from a constant table at an in-bounds index cannot be +/// poison. `min` is the complement of `max`. fn signed_bounds(bits: u32) -> (i64, i64) { - if bits == 0 || bits >= 64 { - (i64::MIN, i64::MAX) - } else { - (-(1i64 << (bits - 1)), (1i64 << (bits - 1)) - 1) - } + let index = if bits >= 64 { 0 } else { bits as usize }; + let max = SIGNED_MAX[index]; + (!max, max) } impl SignedBinop { @@ -263,6 +280,21 @@ mod tests { VccContext::new(1, 0, "test") } + /// The C bounds (`:111-124`) for every width: 1..=63 bits give + /// -2^(bits-1) .. 2^(bits-1)-1; 0 and 64 or more give the i64 range. + #[test] + fn signed_bounds_match_c_for_every_width() { + for bits in (0..=200u32).chain([u32::MAX - 1, u32::MAX]) { + let want = if bits == 0 || bits >= 64 { + (i64::MIN, i64::MAX) + } else { + let half = 1i128 << (bits - 1); + ((-half) as i64, (half - 1) as i64) + }; + assert_eq!(signed_bounds(bits), want, "bits = {bits}"); + } + } + #[test] fn add_i32_overflow_detected() { assert_eq!( diff --git a/rust/src/bbtrack/mod.rs b/rust/src/bbtrack/mod.rs index a77ecd9..60f4a03 100644 --- a/rust/src/bbtrack/mod.rs +++ b/rust/src/bbtrack/mod.rs @@ -1,10 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only #![deny(unsafe_code)] -use serde::Serialize; - /// Single basic-block execution record — analogous to `bbtrack_log_t` in C. -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone)] +#[cfg_attr(not(feature = "dropin"), derive(serde::Serialize))] pub struct BasicBlockEntry { pub step: u64, pub line: u32, @@ -21,6 +20,19 @@ impl BasicBlockEntry { } } +/// `trackbb_new_row` (`TrackBBLog.c:15-21`), private to the Rust core +/// (SD-22): a row with `line` and a copy of `function_name`. develop copies +/// the name into `char[35]`; this library keeps the whole name (SD-17). +pub(crate) fn trackbb_new_row(step: u64, line: u32, function_name: &str) -> BasicBlockEntry { + BasicBlockEntry::new(step, line, function_name) +} + +/// `is_in_tracked` (`TrackBBLog.c:23-33`), private to the Rust core +/// (SD-22): whether a row of `line` exists, searched from the last row. +pub(crate) fn is_in_tracked(line: u32, log: &[BasicBlockEntry]) -> bool { + log.iter().rev().any(|e| e.line == line) +} + /// Returns `true` if any tracked basic block is at `line`. /// Replaces `map2check_is_in_trackbb_container` (0/1 int) with a proper bool. pub fn contains_line(entries: &[BasicBlockEntry], line: u32) -> bool { @@ -63,4 +75,31 @@ mod tests { assert!(json.contains("\"line\":15")); assert!(json.contains("\"check\"")); } + + /// `is_in_tracked` (`TrackBBLog.c:23-33`), private in the Rust core + /// (SD-22): whether any row has `line`, whatever its function name. + #[test] + fn is_in_tracked_matches_any_row_of_the_line() { + let rows = vec![ + BasicBlockEntry::new(1, 10, "foo"), + BasicBlockEntry::new(2, 20, "bar"), + ]; + assert!(is_in_tracked(10, &rows)); + assert!(is_in_tracked(20, &rows)); + assert!(!is_in_tracked(30, &rows)); + assert!(!is_in_tracked(0, &[])); + } + + /// `trackbb_new_row` (`TrackBBLog.c:15-21`), private in the Rust core + /// (SD-22): a row with the line and a copy of the name. + #[test] + fn trackbb_new_row_copies_line_and_name() { + let name = String::from("main"); + let row = trackbb_new_row(5, 7, &name); + drop(name); + assert_eq!( + (row.step, row.line, row.function_name.as_str()), + (5, 7, "main") + ); + } } diff --git a/rust/src/caller/mod.rs b/rust/src/caller/mod.rs index 65d5c4b..4f9e616 100644 --- a/rust/src/caller/mod.rs +++ b/rust/src/caller/mod.rs @@ -1,31 +1,43 @@ // SPDX-License-Identifier: GPL-3.0-only #![deny(unsafe_code)] -use serde::Serialize; - /// Properties that can be violated during program analysis. /// `#[repr(C)]` allows FFI callers to pass this enum across the ABI boundary. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Default)] +/// +/// The discriminants are the values of `enum ViolatedProperty` of the C +/// runtime of Map2Check `develop` (`Map2CheckTypes.h:80-91`), so the C ABI +/// (`write_property`, `set_false_result`, `get_current_property`) uses the C +/// numbers; `tests/abi/enum-map.tsv` pairs the enumerators and +/// `tests/abi/check-signatures.sh` checks the values. `Concurrency` has no C +/// counterpart and comes after them. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[cfg_attr(not(feature = "dropin"), derive(serde::Serialize))] #[repr(C)] pub enum ViolatedProperty { - Overflow, - MemsafetyFree, - MemsafetyDeref, - MemsafetyMemtrack, - MemsafetyMemcleanup, - Reachability, - Concurrency, + /// `NONE`: no violation. #[default] - None, - /// A failed `map2check_is_valid_assert` (`ASSERT` in `PropertyGenerator.c`). - /// `Reachability` is the target-reached property (`TARGET_REACHED`). - Assert, + None = 0, /// The analysis could not decide (`UNKNOWN` in `PropertyGenerator.c`). - Unknown, - /// Signed division or remainder by zero (`FALSE_DIVBYZERO`, - /// `Map2CheckTypes.h:78`), reported by `map2check_binop_div_signed` and - /// `map2check_binop_rem_signed`. - DivByZero, + Unknown = 1, + /// `FALSE_FREE`. + MemsafetyFree = 2, + /// `FALSE_DEREF`. + MemsafetyDeref = 3, + /// `FALSE_MEMTRACK`. + MemsafetyMemtrack = 4, + /// The target function was reached (`TARGET_REACHED`). + Reachability = 5, + /// `FALSE_OVERFLOW`. + Overflow = 6, + /// A failed `map2check_assert` (`ASSERT` in `PropertyGenerator.c`). + Assert = 7, + /// `FALSE_MEMCLEANUP`. + MemsafetyMemcleanup = 8, + /// Signed division or remainder by zero (`FALSE_DIVBYZERO`), reported by + /// `map2check_binop_sdiv` and `map2check_binop_srem`. + DivByZero = 9, + /// No counterpart in the C runtime. + Concurrency = 10, } impl ViolatedProperty { @@ -47,7 +59,8 @@ impl ViolatedProperty { } /// Central analysis result, analogous to `caller_result_t` in C. -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone)] +#[cfg_attr(not(feature = "dropin"), derive(serde::Serialize))] pub struct AnalysisResult { pub step: u64, pub line: u32, @@ -56,8 +69,9 @@ pub struct AnalysisResult { pub property: ViolatedProperty, } -impl Default for AnalysisResult { - fn default() -> Self { +impl AnalysisResult { + /// The result before any record: no violation, step 0. + pub const fn new() -> Self { Self { step: 0, line: 0, @@ -68,6 +82,12 @@ impl Default for AnalysisResult { } } +impl Default for AnalysisResult { + fn default() -> Self { + Self::new() + } +} + #[cfg(test)] mod tests { use super::*; @@ -87,12 +107,27 @@ mod tests { assert_eq!(ViolatedProperty::None.as_str(), "NONE"); } + /// The discriminants are the values of develop's `enum ViolatedProperty` + /// (`Map2CheckTypes.h:80-91`), so `write_property` takes the C values; + /// `Concurrency` has no C counterpart and comes after them. #[test] - fn new_variants_are_appended_after_the_original_ones() { - assert_eq!(ViolatedProperty::None as i32, 7); - assert_eq!(ViolatedProperty::Assert as i32, 8); - assert_eq!(ViolatedProperty::Unknown as i32, 9); - assert_eq!(ViolatedProperty::DivByZero as i32, 10); + fn discriminants_are_the_values_of_the_c_enum() { + let cases = [ + (ViolatedProperty::None, 0), + (ViolatedProperty::Unknown, 1), + (ViolatedProperty::MemsafetyFree, 2), + (ViolatedProperty::MemsafetyDeref, 3), + (ViolatedProperty::MemsafetyMemtrack, 4), + (ViolatedProperty::Reachability, 5), + (ViolatedProperty::Overflow, 6), + (ViolatedProperty::Assert, 7), + (ViolatedProperty::MemsafetyMemcleanup, 8), + (ViolatedProperty::DivByZero, 9), + (ViolatedProperty::Concurrency, 10), + ]; + for (property, value) in cases { + assert_eq!(property as i32, value, "{property:?}"); + } } #[test] diff --git a/rust/src/cfmt.rs b/rust/src/cfmt.rs new file mode 100644 index 0000000..1f966e2 --- /dev/null +++ b/rust/src/cfmt.rs @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-3.0-only +// +// The C conversions of the result files, written into a byte buffer: the +// `%u`/`%d`/`%lld`, `%p`, `%lf` and `%.9g` that the C runtime of Map2Check +// `develop` prints with `fprintf`/`snprintf` (`PropertyGenerator.c`, +// `TrackBBLog.c`, `ListLog.c`, `AllocationLog.c`, `HeapLog.c`, +// `NonDetLog.c`), as glibc prints them. +// +// Every build uses these, and they never go through `core::fmt`: the +// drop-in core (`Map2CheckFunctions.bc`) is compiled three times by +// `afl-clang-fast` on every Map2Check run, and `core::fmt` with its float +// formatting (Grisu and Dragon) made up most of its IR (Task 3.3). +// +// The floating-point conversions are exact: the value `m * 2^e` is written +// out in full as a decimal integer (`m * 5^k` with `k = -e` decimals, or +// `m * 2^e`), then rounded at the printed digit, a tie to even, as glibc +// does in the default rounding mode. +#![deny(unsafe_code)] + +/// Appends `bytes`. Not inlined: the drop-in core appends at every field +/// of every row, and an inlined `Vec` growth check at each one was a large +/// part of its IR (Task 3.3). +#[inline(never)] +pub fn put(out: &mut Vec, bytes: &[u8]) { + out.extend_from_slice(bytes); +} + +/// Appends one byte ([`put`]). +#[inline(never)] +pub fn put_byte(out: &mut Vec, byte: u8) { + out.push(byte); +} + +/// `%u`, `%lu`, `%llu`. +pub fn put_u64(out: &mut Vec, mut value: u64) { + let mut digits = [0u8; 20]; + let mut at = digits.len(); + loop { + at -= 1; + digits[at] = b'0' + (value % 10) as u8; + value /= 10; + if value == 0 { + break; + } + } + out.extend_from_slice(&digits[at..]); +} + +/// `%d`, `%ld`, `%lld`. +pub fn put_i64(out: &mut Vec, value: i64) { + if value < 0 { + out.push(b'-'); + } + put_u64(out, value.unsigned_abs()); +} + +/// `%p` of glibc: `(nil)` for NULL, otherwise `0x` and lowercase hex. +pub fn put_ptr(out: &mut Vec, address: u64) { + if address == 0 { + out.extend_from_slice(b"(nil)"); + return; + } + out.extend_from_slice(b"0x"); + put_hex(out, address); +} + +/// `%lx`: lowercase hex, `0` for zero. +pub fn put_hex(out: &mut Vec, mut value: u64) { + let mut digits = [0u8; 16]; + let mut at = digits.len(); + loop { + at -= 1; + digits[at] = b"0123456789abcdef"[(value & 0xf) as usize]; + value >>= 4; + if value == 0 { + break; + } + } + out.extend_from_slice(&digits[at..]); +} + +/// `inf`/`nan` with the sign glibc prints; true when `bits` is one of them. +fn put_special(out: &mut Vec, negative: bool, exponent_all_ones: bool, mantissa: u64) -> bool { + if !exponent_all_ones { + return false; + } + if negative { + out.push(b'-'); + } + out.extend_from_slice(if mantissa == 0 { b"inf" } else { b"nan" }); + true +} + +/// The exact decimal digits of a finite, non-negative `m * 2^e`: the +/// digits of an integer and the number of them that are decimals. No +/// leading zeros; `"0"` with 0 decimals for zero. +fn exact_digits(mut m: u64, mut e: i32) -> (Vec, usize) { + if m == 0 { + return (vec![b'0'], 0); + } + while m & 1 == 0 { + m >>= 1; + e += 1; + } + // Little-endian base-10^9 limbs of m * 2^e (e >= 0) or m * 5^-e. + let mut limbs: Vec = vec![(m % 1_000_000_000) as u32]; + let mut high = m / 1_000_000_000; + while high != 0 { + limbs.push((high % 1_000_000_000) as u32); + high /= 1_000_000_000; + } + let (factor, mut count, decimals) = if e >= 0 { + (2u64, e as u32, 0usize) + } else { + (5u64, e.unsigned_abs(), e.unsigned_abs() as usize) + }; + while count > 0 { + // Up to 2^29 or 5^12 at a time: the product fits in a u64. + let (step, mul) = if factor == 2 { + let s = count.min(29); + (s, 1u64 << s) + } else { + let s = count.min(12); + (s, 5u64.pow(s)) + }; + let mut carry = 0u64; + for limb in limbs.iter_mut() { + let v = u64::from(*limb) * mul + carry; + *limb = (v % 1_000_000_000) as u32; + carry = v / 1_000_000_000; + } + while carry != 0 { + limbs.push((carry % 1_000_000_000) as u32); + carry /= 1_000_000_000; + } + count -= step; + } + let mut digits = Vec::with_capacity(limbs.len() * 9); + let top = limbs.len() - 1; + put_u64(&mut digits, u64::from(limbs[top])); + for &limb in limbs[..top].iter().rev() { + let mut chunk = [b'0'; 9]; + let mut v = limb; + for slot in chunk.iter_mut().rev() { + *slot = b'0' + (v % 10) as u8; + v /= 10; + } + digits.extend_from_slice(&chunk); + } + (digits, decimals) +} + +/// Keeps the first `keep` digits of `digits`, rounded by the ones dropped: +/// up above one half, and a tie to even. Returns true when the rounding +/// carried out of the first digit (all nines): the kept digits are then +/// `1` followed by zeros, one more than `keep` long. +fn round_digits(digits: &mut Vec, keep: usize) -> bool { + if keep >= digits.len() { + return false; + } + let first = digits[keep]; + let rest_zero = digits[keep + 1..].iter().all(|&d| d == b'0'); + let odd = keep > 0 && (digits[keep - 1] - b'0') % 2 == 1; + let up = first > b'5' || (first == b'5' && (!rest_zero || odd)); + digits.truncate(keep); + if !up { + return false; + } + for d in digits.iter_mut().rev() { + if *d == b'9' { + *d = b'0'; + } else { + *d += 1; + return false; + } + } + digits.insert(0, b'1'); + true +} + +/// The sign, the biased exponent field and the mantissa field of a double. +fn split_f64(value: f64) -> (bool, i32, u64) { + let bits = value.to_bits(); + ( + bits >> 63 != 0, + ((bits >> 52) & 0x7ff) as i32, + bits & ((1u64 << 52) - 1), + ) +} + +/// `m` and `e` of a finite double with this exponent field and mantissa. +fn mantissa_exponent(field: i32, mantissa: u64) -> (u64, i32) { + if field == 0 { + (mantissa, -1074) + } else { + (mantissa | (1u64 << 52), field - 1075) + } +} + +/// `%lf` of glibc: six decimals, `inf`/`nan` with their sign, `-` on a +/// negative zero. +pub fn put_lf(out: &mut Vec, value: f64) { + let (negative, field, mantissa) = split_f64(value); + if put_special(out, negative, field == 0x7ff, mantissa) { + return; + } + let (m, e) = mantissa_exponent(field, mantissa); + let (mut digits, decimals) = exact_digits(m, e); + if decimals > 6 { + let dropped = decimals - 6; + match dropped.cmp(&digits.len()) { + // Below a tenth of the sixth decimal: rounds to zero. + std::cmp::Ordering::Greater => digits = vec![b'0'], + // The first dropped digit is the seventh decimal. + std::cmp::Ordering::Equal => { + digits.insert(0, b'0'); + round_digits(&mut digits, 1); + } + std::cmp::Ordering::Less => { + let keep = digits.len() - dropped; + round_digits(&mut digits, keep); + } + } + } else { + digits.extend(std::iter::repeat(b'0').take(6 - decimals)); + } + // `digits` now holds the value times 10^6. + if digits.len() < 7 { + let mut padded = vec![b'0'; 7 - digits.len()]; + padded.extend_from_slice(&digits); + digits = padded; + } + if negative { + out.push(b'-'); + } + let point = digits.len() - 6; + out.extend_from_slice(&digits[..point]); + out.push(b'.'); + out.extend_from_slice(&digits[point..]); +} + +/// `%.9g` of glibc on a `float` (`NonDetLog.c:99`): nine significant digits, +/// `%f` style for decimal exponents -4 to 8, `%e` style otherwise (at least +/// two exponent digits), trailing zeros and a trailing point removed. +pub fn put_g9(out: &mut Vec, value: f32) { + const P: usize = 9; + let (negative, field, mantissa) = split_f64(f64::from(value)); + if put_special(out, negative, field == 0x7ff, mantissa) { + return; + } + if negative { + out.push(b'-'); + } + let (m, e) = mantissa_exponent(field, mantissa); + if m == 0 { + out.push(b'0'); + return; + } + let (mut digits, decimals) = exact_digits(m, e); + // The decimal exponent of the leading digit, before and after rounding. + let mut x = digits.len() as i32 - 1 - decimals as i32; + if round_digits(&mut digits, P) { + x += 1; + digits.truncate(P); + } + digits.resize(P, b'0'); + while digits.len() > 1 && digits[digits.len() - 1] == b'0' { + digits.pop(); + } + if (-4..P as i32).contains(&x) { + if x < 0 { + out.extend_from_slice(b"0."); + out.extend(std::iter::repeat(b'0').take((-x - 1) as usize)); + out.extend_from_slice(&digits); + } else { + let int_len = x as usize + 1; + if digits.len() <= int_len { + out.extend_from_slice(&digits); + out.extend(std::iter::repeat(b'0').take(int_len - digits.len())); + } else { + out.extend_from_slice(&digits[..int_len]); + out.push(b'.'); + out.extend_from_slice(&digits[int_len..]); + } + } + } else { + out.push(digits[0]); + if digits.len() > 1 { + out.push(b'.'); + out.extend_from_slice(&digits[1..]); + } + out.push(b'e'); + out.push(if x < 0 { b'-' } else { b'+' }); + if x.unsigned_abs() < 10 { + out.push(b'0'); + } + put_u64(out, u64::from(x.unsigned_abs())); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn s(f: impl FnOnce(&mut Vec)) -> String { + let mut out = Vec::new(); + f(&mut out); + String::from_utf8(out).unwrap() + } + + #[allow(unsafe_code)] + fn glibc(format: &[u8], value: f64) -> String { + // A byte string, not a `c""` literal: rustc 1.72 (the drop-in build) + // cannot lex those. + assert_eq!(format.last(), Some(&0)); + extern "C" { + fn snprintf( + buf: *mut std::os::raw::c_char, + n: usize, + format: *const std::os::raw::c_char, + ... + ) -> std::os::raw::c_int; + } + let mut buf = vec![0u8; 2048]; + // SAFETY: a buffer of `buf.len()` bytes, a format with one double. + let n = unsafe { + snprintf( + buf.as_mut_ptr().cast(), + buf.len(), + format.as_ptr().cast(), + value, + ) + }; + buf.truncate(n as usize); + String::from_utf8(buf).unwrap() + } + + fn values() -> Vec { + let mut v = vec![ + 0.0, + -0.0, + 0.5, + 2.5, + 1.0e-7, + 5.0e-7, + 4.999_999_9e-7, + 1.5e-6, + 2.5e-6, + 0.000_000_5, + 0.000_001_5, + 123_456.789, + 1e300, + -1e300, + f64::MAX, + f64::MIN_POSITIVE, + 5e-324, + f64::INFINITY, + f64::NEG_INFINITY, + f64::NAN, + -f64::NAN, + 1e-30, + 9.999_999_5, + 999_999_999.5, + 0.000_099_999_999_5, + f64::from(f32::MAX), + f64::from(f32::MIN_POSITIVE), + f64::from(f32::from_bits(1)), + std::f64::consts::PI, + 16_777_216.0, + 1e9, + 1e8, + 123_456_789.0, + 0.0001, + 0.000_012_345_678_9, + ]; + // A deterministic spread of bit patterns (xorshift). + let mut x = 0x2545_f491_4f6c_dd1du64; + for _ in 0..4000 { + x ^= x << 13; + x ^= x >> 7; + x ^= x << 17; + v.push(f64::from_bits(x)); + v.push(f64::from(f32::from_bits(x as u32))); + v.push((x % 2_000_000) as f64 / 1024.0 - 900.0); + } + v + } + + #[test] + fn integers_and_pointers_match_printf() { + assert_eq!(s(|o| put_u64(o, 0)), "0"); + assert_eq!(s(|o| put_u64(o, u64::MAX)), "18446744073709551615"); + assert_eq!(s(|o| put_i64(o, i64::MIN)), "-9223372036854775808"); + assert_eq!(s(|o| put_i64(o, -1)), "-1"); + assert_eq!(s(|o| put_ptr(o, 0)), "(nil)"); + assert_eq!(s(|o| put_ptr(o, 0x1000_0100)), "0x10000100"); + assert_eq!(s(|o| put_ptr(o, u64::MAX)), "0xffffffffffffffff"); + } + + #[test] + fn lf_matches_glibc() { + for v in values() { + assert_eq!(s(|o| put_lf(o, v)), glibc(b"%lf\0", v), "{v:e}"); + } + } + + #[test] + fn g9_of_a_float_matches_glibc() { + for v in values() { + let f = v as f32; + assert_eq!(s(|o| put_g9(o, f)), glibc(b"%.9g\0", f64::from(f)), "{f:e}"); + } + } +} diff --git a/rust/src/error.rs b/rust/src/error.rs index b224e79..f6e5503 100644 --- a/rust/src/error.rs +++ b/rust/src/error.rs @@ -1,32 +1,109 @@ // SPDX-License-Identifier: GPL-3.0-only #![deny(unsafe_code)] -use thiserror::Error; - -#[derive(Debug, Error)] +/// The errors of the library. The messages are written by +/// [`Map2CheckError::describe`] into a byte buffer, without `core::fmt`, so +/// that the drop-in core can print them (Task 3.3); `Display` (host builds) +/// prints the same text. +#[derive(Debug)] pub enum Map2CheckError { - #[error("state not initialized; call map2check_init() first")] + /// `state not initialized; call map2check_init() first` NotInitialized, - #[error("lock poisoned: {0}")] + /// `lock poisoned: ` LockPoisoned(String), - #[error("address {0:#x} not found in memory tracker")] + /// `address <0x..> not found in memory tracker` AddressNotFound(usize), - #[error("JSON serialization failed: {0}")] - Serialization(#[from] serde_json::Error), + /// `JSON serialization failed: ` (the JSON channel, which the + /// drop-in core does not have). + #[cfg(not(feature = "dropin"))] + Serialization(serde_json::Error), - #[error("integer overflow in {operation} at line {line}")] + /// `integer overflow in at line ` OverflowDetected { operation: &'static str, line: u32 }, - #[error("null pointer dereference at line {line} in {function}")] + /// `null pointer dereference at line in ` NullDereference { line: u32, function: String }, - #[error("invalid free of address {address:#x} at line {line}")] + /// `invalid free of address <0x..> at line ` InvalidFree { address: usize, line: u32 }, } +impl Map2CheckError { + /// Appends the message of the error to `out`. + pub fn describe(&self, out: &mut Vec) { + use crate::cfmt::{put_hex, put_u64}; + match self { + Self::NotInitialized => { + out.extend_from_slice(b"state not initialized; call map2check_init() first"); + } + Self::LockPoisoned(cause) => { + out.extend_from_slice(b"lock poisoned: "); + out.extend_from_slice(cause.as_bytes()); + } + Self::AddressNotFound(address) => { + out.extend_from_slice(b"address 0x"); + put_hex(out, *address as u64); + out.extend_from_slice(b" not found in memory tracker"); + } + #[cfg(not(feature = "dropin"))] + Self::Serialization(cause) => { + out.extend_from_slice(b"JSON serialization failed: "); + out.extend_from_slice(cause.to_string().as_bytes()); + } + Self::OverflowDetected { operation, line } => { + out.extend_from_slice(b"integer overflow in "); + out.extend_from_slice(operation.as_bytes()); + out.extend_from_slice(b" at line "); + put_u64(out, u64::from(*line)); + } + Self::NullDereference { line, function } => { + out.extend_from_slice(b"null pointer dereference at line "); + put_u64(out, u64::from(*line)); + out.extend_from_slice(b" in "); + out.extend_from_slice(function.as_bytes()); + } + Self::InvalidFree { address, line } => { + out.extend_from_slice(b"invalid free of address 0x"); + put_hex(out, *address as u64); + out.extend_from_slice(b" at line "); + put_u64(out, u64::from(*line)); + } + } + } +} + +#[cfg(not(feature = "dropin"))] +impl std::fmt::Display for Map2CheckError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut text = Vec::new(); + self.describe(&mut text); + f.write_str(&String::from_utf8_lossy(&text)) + } +} + +#[cfg(not(feature = "dropin"))] +impl std::error::Error for Map2CheckError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Serialization(cause) => Some(cause), + _ => None, + } + } +} + +#[cfg(not(feature = "dropin"))] +impl From for Map2CheckError { + fn from(e: serde_json::Error) -> Self { + Self::Serialization(e) + } +} + +/// Host builds only: the drop-in core is built with `panic = "abort"`, so +/// no panic can poison its lock (`state::with_state`). +#[cfg(not(feature = "dropin"))] impl From> for Map2CheckError { fn from(e: std::sync::PoisonError) -> Self { Self::LockPoisoned(e.to_string()) @@ -37,6 +114,46 @@ impl From> for Map2CheckError { mod tests { use super::*; + /// The texts the `thiserror` attributes printed before Task 3.3. + #[test] + fn messages_are_unchanged() { + let cases: [(Map2CheckError, &str); 6] = [ + ( + Map2CheckError::NotInitialized, + "state not initialized; call map2check_init() first", + ), + (Map2CheckError::LockPoisoned("x".into()), "lock poisoned: x"), + ( + Map2CheckError::AddressNotFound(0x1f), + "address 0x1f not found in memory tracker", + ), + ( + Map2CheckError::OverflowDetected { + operation: "add", + line: 7, + }, + "integer overflow in add at line 7", + ), + ( + Map2CheckError::NullDereference { + line: 3, + function: "main".into(), + }, + "null pointer dereference at line 3 in main", + ), + ( + Map2CheckError::InvalidFree { + address: 0, + line: 4294967295, + }, + "invalid free of address 0x0 at line 4294967295", + ), + ]; + for (e, text) in cases { + assert_eq!(e.to_string(), text); + } + } + #[test] fn poison_error_converts_to_lock_poisoned() { let mutex = std::sync::Mutex::new(0_u8); diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs index 1481894..e0ba19b 100644 --- a/rust/src/ffi.rs +++ b/rust/src/ffi.rs @@ -1,9 +1,21 @@ // SPDX-License-Identifier: GPL-3.0-only // // FFI boundary: #[unsafe(no_mangle)] extern "C" functions that Map2Check's LLVM Pass calls. -// This is the ONLY module that contains unsafe code. +// With `sys.rs` (the C library calls of the result channel) and `wasm.rs`, +// one of the only modules that contain unsafe code (test modules aside). // Every unsafe block is annotated with a SAFETY comment. - +// +// The C ABI (D6, 2.0.0): the `#[no_mangle]` functions here are exactly the +// names of the C runtime of Map2Check `develop` that the core modules own +// (`Map2CheckFunctions`, `TrackBBLog`, `NonDetLog`, `PropertyGenerator`) +// and the `__m2c_rt_*` entry points the generated IR modules forward to +// (scripts/check-export-set.sh). The `extern "C"` functions without +// `#[no_mangle]` are the old core API (`map2check_set_mode`, +// `map2check_map_malloc`, `print_all_containers_as_json`...): no longer +// exported, kept as Rust functions because the Rust tests drive the core +// through them. + +use std::borrow::Cow; use std::ffi::CStr; // Importações desmembradas para evitar bugs do cbindgen @@ -34,6 +46,8 @@ use crate::analysismode::VccContext; use crate::bbtrack::BasicBlockEntry; use crate::caller::ViolatedProperty; use crate::error::Map2CheckError; +use crate::memtrack::c_mode::FreeOutcome; +use crate::memtrack::c_mode::MemMode; use crate::memtrack::MemOrigin; use crate::memtrack::MemTrackEntry; use crate::nondet::NonDetEntry; @@ -44,22 +58,25 @@ use crate::state; // — Helpers ————————————————————————————————————————————————————————————————— -/// Convert a raw C string to &str, returning "" on null or invalid UTF-8. +/// Convert a raw C string to text, `""` on null. Valid UTF-8 is borrowed +/// unchanged; invalid UTF-8 keeps its valid parts and has each invalid +/// sequence replaced by U+FFFD, so a name develop prints with `%s` is never +/// dropped (the memory paths keep the raw bytes with [`c_bytes`]). /// /// # Safety /// `ptr` must be null or point to a valid, null-terminated C string that /// remains valid for the duration of the call. -unsafe fn c_str<'a>(ptr: *const std::os::raw::c_char) -> &'a str { +unsafe fn c_str<'a>(ptr: *const std::os::raw::c_char) -> Cow<'a, str> { if ptr.is_null() { - return ""; + return Cow::Borrowed(""); } // SAFETY: caller guarantees ptr is a valid null-terminated C string. - unsafe { CStr::from_ptr(ptr) }.to_str().unwrap_or("") + unsafe { CStr::from_ptr(ptr) }.to_string_lossy() } // A assinatura foi encurtada porque importamos o Map2CheckError ali em cima fn log_error(e: &Map2CheckError) { - eprintln!("[map2check] error: {e}"); + termination::log_error(e); } /// Records a violation (the first one is final, SD-03). When it is the @@ -70,10 +87,13 @@ fn log_error(e: &Map2CheckError) { fn record_violation(property: ViolatedProperty, line: u32, fname: &str, end_of_program: bool) { match state::with_state(|s| { let first = s.result.ok; + if first && end_of_program { + s.end_of_program = true; + } s.set_false(property, line, fname); Ok(first) }) { - Ok(true) => termination::on_violation(end_of_program), + Ok(true) => termination::on_violation(), Ok(false) => {} Err(e) => log_error(&e), } @@ -100,21 +120,30 @@ fn check_active(check: Check) -> bool { mode::check_enabled(state::config().mode, check) } +/// A C `enum ViolatedProperty` received by value as its `int`, so any value +/// a C caller passes is defined behaviour. `cbindgen.toml` excludes this +/// alias and renames it, so the header keeps the parameter type `enum +/// ViolatedProperty` of the C runtime. +pub type ViolatedPropertyRaw = std::os::raw::c_int; + +/// The C value of `enum ViolatedProperty` (`Map2CheckTypes.h:80-91`), the +/// discriminant of [`ViolatedProperty`]; 10 is `Concurrency`. fn violated_property_from_raw(raw: i32) -> Option { - match raw { - 0 => Some(ViolatedProperty::Overflow), - 1 => Some(ViolatedProperty::MemsafetyFree), - 2 => Some(ViolatedProperty::MemsafetyDeref), - 3 => Some(ViolatedProperty::MemsafetyMemtrack), - 4 => Some(ViolatedProperty::MemsafetyMemcleanup), - 5 => Some(ViolatedProperty::Reachability), - 6 => Some(ViolatedProperty::Concurrency), - 7 => Some(ViolatedProperty::None), - 8 => Some(ViolatedProperty::Assert), - 9 => Some(ViolatedProperty::Unknown), - 10 => Some(ViolatedProperty::DivByZero), - _ => None, - } + [ + ViolatedProperty::None, + ViolatedProperty::Unknown, + ViolatedProperty::MemsafetyFree, + ViolatedProperty::MemsafetyDeref, + ViolatedProperty::MemsafetyMemtrack, + ViolatedProperty::Reachability, + ViolatedProperty::Overflow, + ViolatedProperty::Assert, + ViolatedProperty::MemsafetyMemcleanup, + ViolatedProperty::DivByZero, + ViolatedProperty::Concurrency, + ] + .into_iter() + .find(|p| *p as i32 == raw) } /// Failure policy at the `extern "C"` boundary (issue #8). @@ -132,10 +161,18 @@ fn violated_property_from_raw(raw: i32) -> Option { /// Deliberate process termination (`__VERIFIER_error`, `__VERIFIER_assume(0)`) /// is not a failure and bypasses recovery by design. pub(crate) fn ffi_guard(f: impl FnOnce() -> T, default: T) -> T { + // The drop-in core is built with `panic = "abort"` (klee/Cargo.toml): + // there is nothing to catch, and the panic goes straight to the abort. + #[cfg(feature = "dropin")] + { + let _ = default; + f() + } + #[cfg(not(feature = "dropin"))] match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) { Ok(value) => value, Err(_) => { - eprintln!("[map2check] panic in FFI boundary; recovering safely"); + crate::sys::diagnostic(b"panic in FFI boundary; recovering safely"); default } } @@ -143,244 +180,2426 @@ pub(crate) fn ffi_guard(f: impl FnOnce() -> T, default: T) -> T { // ── Initialisation ──────────────────────────────────────────────────────────── -/// Initialises all containers. Must be called before any tracking function. -/// With an analysis mode set, also removes the property files -/// (`map2check_property*`) that an earlier run left in the current -/// directory, so they cannot be read as this run's verdict. +/// `map2check_init` of the C runtime (`Map2CheckFunctions.c:29-40`), which +/// the Map2Check passes call first: starts a fresh analysis through the +/// `analysis_init` of the selected mode (every container, counter, result and +/// flag is cleared) and keeps the configuration, mode included. Must be +/// called before any tracking function. `is_sv_comp` only sets develop's +/// `sv_comp`, which nothing reads, so it is accepted and ignored. Like +/// develop, it leaves the property files of an earlier run alone. +/// +/// In the drop-in core (feature `dropin`, klee/Cargo.toml) it then calls the +/// external `analysis_init` and `nondet_init`, in that order, as +/// `Map2CheckFunctions.c:37-38` does: the linked mode module selects the +/// mode, the linked generator module starts the nondet log. Both are defined +/// only by those IR modules (D7), so builds that link the staticlib alone +/// (cargo test, `tests/ffi_harness.c`, the host link tests) do not have the +/// feature and call `analysis_init` themselves. The calls run outside the +/// state lock: both come back into the core. +#[no_mangle] +pub extern "C" fn map2check_init(is_sv_comp: std::os::raw::c_int) { + ffi_guard( + || { + let _ = is_sv_comp; + state::init(); + #[cfg(feature = "dropin")] + dropin::init_linked_modules(); + }, + (), + ); +} + +/// The calls of the drop-in core into the mode, generator and witness +/// modules it is linked with (D7 layout A-fwd). Not in +/// `include/map2check.h`: the IR modules define these names, the library +/// does not. Every call is made with the state lock released: the modules +/// call back into the core. +/// +/// cbindgen:ignore +#[cfg(feature = "dropin")] +pub(crate) mod dropin { + extern "C" { + /// `analysis_init` of the linked `AnalysisMode*.bc`. + fn analysis_init(); + /// `nondet_init` of the linked `NonDetGenerator*.bc`. + fn nondet_init(); + /// `analysis_generate_aux_witness_files` of the linked + /// `AnalysisMode*.bc`. + fn analysis_generate_aux_witness_files(); + /// `nondet_generate_aux_witness_files` of the linked + /// `NonDetGenerator*.bc`: `klee_log.csv` (AFL++, KLEE) or nothing + /// (none). + fn nondet_generate_aux_witness_files(); + /// `generate_aux_files(MAP2CHECK_CONTAINER *, Bool)` of the linked + /// `WitnessGeneration.bc` (forwards to + /// `__m2c_rt_witness_generate_aux_files`) or `WitnessGenerationNone.bc` + /// (empty). + fn generate_aux_files( + trackbb_log: *mut std::os::raw::c_void, + violation: std::os::raw::c_char, + ); + /// `generate_confirmation_file` of the linked witness module. + fn generate_confirmation_file(); + } + + /// `Map2CheckFunctions.c:37-38`. + pub(super) fn init_linked_modules() { + // SAFETY: both are `void (void)` C functions of the linked modules, + // which only call back into the core's exports. + unsafe { + analysis_init(); + nondet_init(); + } + } + + /// `generate_aux_files(&trackbb_log, gotError)` of `map2check_exit` + /// (`Map2CheckFunctions.c:95`). The core keeps the basic-block log + /// itself, so the container pointer is null (D7, finding 6). + pub(crate) fn witness_generate_aux_files(violation: bool) { + // SAFETY: a C function of the linked witness module; the pointer is + // never dereferenced (`__m2c_rt_witness_generate_aux_files`). + unsafe { generate_aux_files(std::ptr::null_mut(), std::os::raw::c_char::from(violation)) } + } + + /// `generate_confirmation_file()` of `map2check_exit` + /// (`Map2CheckFunctions.c:98`). + pub(crate) fn witness_generate_confirmation_file() { + // SAFETY: a `void (void)` C function of the linked witness module. + unsafe { generate_confirmation_file() } + } + + /// `analysis_generate_aux_witness_files()` of `generate_aux_files` + /// (`WitnessGeneration.c:17`). + pub(crate) fn mode_generate_aux_witness_files() { + // SAFETY: a `void (void)` C function of the linked mode module. + unsafe { analysis_generate_aux_witness_files() } + } + + /// `nondet_generate_aux_witness_files()` of `generate_aux_files` + /// (`WitnessGeneration.c:27`). + pub(crate) fn generator_generate_aux_witness_files() { + // SAFETY: a `void (void)` C function of the linked generator module. + unsafe { nondet_generate_aux_witness_files() } + } +} + +/// Resets the global analysis state without aborting the caller. +pub extern "C" fn map2check_reset() { + ffi_guard(state::reset, ()); +} + +// ── Result management ───────────────────────────────────────────────────────── + +/// End of the analysed program. With the mode unset, prints the JSON +/// report. With an analysis mode set it prints nothing and is develop's +/// `map2check_success()` +/// (`Map2CheckFunctions.c:67-70`, `:89-102`): writes `NONE` to +/// `map2check_property`, runs the end-of-program verdict of the mode, writes +/// the witness files when the witness is on (`__m2c_rt_witness_enable`), and +/// returns; when the verdict is a violation it writes that property instead +/// and aborts. +#[no_mangle] +pub extern "C" fn map2check_success() { + ffi_guard( + || { + if !termination::on_success() { + output::print_report_unless_mode_set(); + } + }, + (), + ); +} + +/// Records a verification failure. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +pub unsafe extern "C" fn set_false_result( + prp: i32, + line_number: i32, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + let Some(property) = violated_property_from_raw(prp) else { + eprintln!("[map2check] invalid violated-property value: {prp}"); + return; + }; + // SAFETY: caller guarantees function_name is a valid C string or null. + let fname: &str = &unsafe { c_str(function_name) }; + record_violation(property, line_number as u32, fname, false); + }, + (), + ); +} + +/// Returns the currently violated property. +pub extern "C" fn get_current_property() -> ViolatedProperty { + ffi_guard( + || state::with_state(|s| Ok(s.result.property)).unwrap_or(ViolatedProperty::None), + ViolatedProperty::None, + ) +} + +/// Returns the current step counter value. +pub extern "C" fn get_current_step() -> u64 { + ffi_guard( + || state::with_state(|s| Ok(s.current_step())).unwrap_or(0), + 0, + ) +} + +/// Advances and returns the step counter. Unlike `get_current_step`, this +/// mutates the counter — analogous to `get_next_step`/`next_current_step` in +/// the C library. +pub extern "C" fn get_next_step() -> u64 { + ffi_guard(|| state::with_state(|s| Ok(s.next_step())).unwrap_or(0), 0) +} + +/// Prints the full JSON report to stdout when no analysis mode is set. With +/// a mode set (a Map2Check run) it prints nothing: the result goes to the +/// files, and the analysed program's stdout stays clean. +pub extern "C" fn print_all_containers_as_json() { + ffi_guard(output::print_report_unless_mode_set, ()); +} + +/// Resets violation metadata without clearing tracking containers. +/// Host/test-only: the Map2Check passes never call it. It clears a recorded +/// violation by design, bypassing the first-violation rule (#36, SD-03). +pub extern "C" fn vcc_reset_meta_data() { + ffi_guard( + || { + if let Err(e) = state::with_state(|s| { + s.result.property = ViolatedProperty::None; + s.result.ok = true; + s.result.line = 0; + s.result.function_name.clear(); + s.failed_free = None; + s.got_error = false; + Ok(()) + }) { + log_error(&e); + } + }, + (), + ); +} + +/// Selects the memcleanup property for the end-of-program leak check. +/// Effect: `map2check_check_mem_endprog` reports a leak as +/// `MemsafetyMemcleanup` instead of `MemsafetyMemtrack`. The option is +/// configuration: `map2check_init()` and `map2check_reset()` keep it. +pub extern "C" fn map2check_set_memcleanup() { + ffi_guard( + || { + state::update_config(|c| c.memcleanup = true); + }, + (), + ); +} + +/// Marks NULL as valid for load checks. +/// Effect: `map2check_check_load` treats a NULL `ptr` as safe once this has +/// been called, instead of flagging it as `MemsafetyDeref`. Kept across +/// `map2check_init()` and `map2check_reset()`. +pub extern "C" fn map2check_set_null_is_valid() { + ffi_guard( + || { + state::update_config(|c| c.null_is_valid = true); + }, + (), + ); +} + +/// Turns on witness generation, as linking `WitnessGeneration.c` instead of +/// `WitnessGenerationNone.c` does in develop (`caller.cpp:624-640`). Effect: +/// when a Map2Check run ends (an analysis mode is set), the library also +/// writes `track_bb_log.st`, `heap_log.csv`, `allocation_log.csv` and +/// `list_log.csv` (memory modes), and on a violation `klee_log.csv` and +/// `map2check_checked_error`. Configuration: kept by `map2check_init()` and +/// `map2check_reset()`. Its C ABI is `__m2c_rt_witness_enable`. +pub extern "C" fn map2check_enable_witness() { + ffi_guard( + || { + state::update_config(|c| c.witness = true); + }, + (), + ); +} + +/// Selects the analysis mode, as the frontend of Map2Check `develop` does by +/// linking one `AnalysisMode*.c` per run (`caller.cpp:575-612`). The +/// instrumented program calls it before `map2check_init()` with one of the +/// `AnalysisMode` values (`AnalysisMode_None` 0 to `AnalysisMode_Target` 5). +/// Any other value is ignored with a diagnostic on stderr and leaves the mode +/// as it was. The mode is configuration: `map2check_init()` and +/// `map2check_reset()` keep it. Until a valid value is set the mode is unset +/// and every check is active. Per-mode effects are in +/// `rust/src/analysismode/mode.rs`. +pub extern "C" fn map2check_set_mode(mode: i32) { + ffi_guard( + || { + let Some(mode) = analysis_mode_from_raw(mode) else { + eprintln!("[map2check] invalid analysis mode: {mode}"); + return; + }; + state::update_config(|c| c.mode = Some(mode)); + }, + (), + ); +} + +fn analysis_mode_from_raw(raw: i32) -> Option { + match raw { + 0 => Some(AnalysisMode::None), + 1 => Some(AnalysisMode::Memtrack), + 2 => Some(AnalysisMode::Memcleanup), + 3 => Some(AnalysisMode::Overflow), + 4 => Some(AnalysisMode::Assert), + 5 => Some(AnalysisMode::Target), + _ => None, + } +} + +/// Records that the target function was reached, without ending the process. +/// Same property as `map2check_target_function` of the `develop` C runtime +/// (`TARGET_REACHED`, `Map2CheckFunctions.c:61-65`), which then ends the run; +/// here the first violation stays final (SD-03) and the caller keeps running. +/// +/// # Safety +/// `func` must be null or a valid null-terminated C string. +pub(crate) unsafe fn mark_target_reached( + func: *const std::os::raw::c_char, + _scope: i32, + line: i32, +) { + // SAFETY: caller guarantees func is a valid C string or null. + let fname: &str = &unsafe { c_str(func) }; + record_violation(ViolatedProperty::Reachability, line as u32, fname, false); +} + +/// Called by the target pass when the target function (e.g. `reach_error`) +/// is reached. Records `Reachability` (develop's `TARGET-REACHED`) at `line` +/// in `func`, in every mode, and returns: ending the run is Task 2.4 (SD-15). +/// `scope` is accepted for ABI compatibility and ignored, as in develop. +/// +/// # Safety +/// `func` must be null or a valid null-terminated C string. +pub unsafe extern "C" fn map2check_reach_target( + func: *const std::os::raw::c_char, + scope: i32, + line: i32, +) { + ffi_guard( + || { + // SAFETY: caller guarantees func is a valid C string or null. + unsafe { mark_target_reached(func, scope, line) } + }, + (), + ); +} + +// ── C runtime ABI: Map2CheckFunctions, TrackBBLog, PropertyGenerator ──────── +// +// `Map2CheckFunctions.c`, `TrackBBLog.c` and `PropertyGenerator.c` of +// Map2Check `develop`, with their C signatures. The C-only helpers of those +// files (`write_property_`, `trackbb_new_row`, `is_in_tracked`, +// `trackbb_log_to_file`) are private to the Rust core (SD-22). + +/// `map2check_get_current_step` (`Map2CheckFunctions.c:42`): develop's +/// `Map2CheckCurrentStep`, 0 after `map2check_init`. +#[no_mangle] +pub extern "C" fn map2check_get_current_step() -> std::os::raw::c_uint { + ffi_guard(|| state::with_state(|s| Ok(s.c_step)).unwrap_or(0), 0) +} + +/// `map2check_next_current_step` (`Map2CheckFunctions.c:43`): adds 1 to the +/// counter `map2check_get_current_step` reads, wrapping as a C `unsigned`. +#[no_mangle] +pub extern "C" fn map2check_next_current_step() { + ffi_guard( + || { + if let Err(e) = state::with_state(|s| { + s.c_step = s.c_step.wrapping_add(1); + Ok(()) + }) { + log_error(&e); + } + }, + (), + ); +} + +/// `map2check_track_bb` (`Map2CheckFunctions.c:49-55`): appends a row for +/// the basic block at `line` of `function_name` unless a row of that line +/// exists (`is_in_tracked`), so the first function name of a line stays. +/// The rows are `track_bb_log.st` of the witness. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_track_bb( + line: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: caller guarantees function_name is a valid C string or null. + let fname: &str = &unsafe { c_str(function_name) }; + if let Err(e) = state::with_state(|s| { + if !crate::bbtrack::is_in_tracked(line, &s.bbtrack) { + let step = s.next_step(); + s.bbtrack + .push(crate::bbtrack::trackbb_new_row(step, line, fname)); + } + Ok(()) + }) { + log_error(&e); + } + }, + (), + ); +} + +/// `__VERIFIER_error` of the C core (`Map2CheckFunctions.c:57-59`): "DO +/// NOTHING". The passes carry the checks (the target through +/// `map2check_target_function`, asserts through `map2check_assert`), so a +/// program's own call has no effect and the run goes on, with a mode set +/// and without (SD-08). +#[no_mangle] +pub extern "C" fn __VERIFIER_error() { + ffi_guard(|| (), ()); +} + +/// `map2check_target_function` (`Map2CheckFunctions.c:61-65`), which the +/// target pass calls when the target function is reached: records +/// `TARGET_REACHED` at `line` in `func_name`; `scope` is not used. With a +/// mode set the run ends as develop's does (`map2check_property` has +/// `TARGET-REACHED`, abort); unset, it returns (SD-15). +/// +/// # Safety +/// `func_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_target_function( + func_name: *const std::os::raw::c_char, + scope: std::os::raw::c_int, + line: std::os::raw::c_int, +) { + ffi_guard( + || { + // SAFETY: caller guarantees func_name is a valid C string or null. + unsafe { mark_target_reached(func_name, scope, line) } + }, + (), + ); +} + +/// `map2check_error` (`Map2CheckFunctions.c:72-75`): marks the run as +/// failed (`gotError`) and calls `map2check_exit`, which skips the +/// end-of-program verdict and, with a mode set, writes the witness files +/// (when enabled) and aborts. It writes no property file; develop's callers +/// write theirs first. Without a mode it returns (SD-03, SD-14). +#[no_mangle] +pub extern "C" fn map2check_error() { + ffi_guard(termination::map2check_error, ()); +} + +/// `map2check_exit` (`Map2CheckFunctions.c:89-102`): with no error yet, runs +/// the end-of-program verdict of the mode (a leak writes its property files); +/// then writes the witness files when enabled, runs `map2check_destroy`, and +/// on an error writes `map2check_checked_error` (witness on) and aborts. +/// Unlike `map2check_success` it writes no `NONE`. Without a mode nothing is +/// written and it returns (SD-03, SD-14). +#[no_mangle] +pub extern "C" fn map2check_exit() { + ffi_guard(termination::map2check_exit, ()); +} + +/// `map2check_destroy` (`Map2CheckFunctions.c:77-87`): releases the +/// basic-block log and the containers of the mode (`analysis_destroy`), +/// once per run; a second call does nothing until `map2check_init`. +#[no_mangle] +pub extern "C" fn map2check_destroy() { + ffi_guard( + || { + let config = state::config(); + if let Err(e) = state::with_state(|s| { + termination::destroy(config, s); + Ok(()) + }) { + log_error(&e); + } + }, + (), + ); +} + +/// `write_property` (`PropertyGenerator.c:50-106`): writes +/// `map2check_property` with the text of `violated` and, for FALSE-FREE, +/// FALSE-DEREF, FALSE-MEMTRACK, FALSE-MEMCLEANUP, OVERFLOW and ASSERT, its +/// `map2check_property_` file, into the current directory, whatever +/// the mode; `Line: %d` and `Function: %s` as develop prints them. It does +/// not record a violation or end the run. `Concurrency`, outside the C +/// switch, leaves `map2check_property` empty. +/// +/// `violated` is taken as a C `int` ([`ViolatedPropertyRaw`], `enum +/// ViolatedProperty` in the header), so a value outside the enum is not +/// undefined behaviour: like a value with no case in the C switch, it leaves +/// `map2check_property` empty. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn write_property( + violated: ViolatedPropertyRaw, + line: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // No case in the C switch: empty text, as for `Concurrency`. + let violated = + violated_property_from_raw(violated).unwrap_or(ViolatedProperty::Concurrency); + // SAFETY: caller guarantees function_name is a valid C string or null. + let fname: &str = &unsafe { c_str(function_name) }; + termination::write_property(violated, line, fname); + }, + (), + ); +} + +/// Core entry point of `generate_aux_files` (`WitnessGeneration.c:15-29`), +/// which the IR module `WitnessGeneration.bc` forwards to (D7): writes +/// `track_bb_log.st`, the logs of the memory modes and, when `violation` is +/// nonzero or `MAP2CHECK_SEED_REPLAY` is set, `klee_log.csv`, into the +/// current directory. `trackbb_log` is develop's container pointer; the core +/// reads its own state, so it is ignored and may be null. +/// +/// # Safety +/// `trackbb_log` is never dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_witness_generate_aux_files( + trackbb_log: *mut std::os::raw::c_void, + violation: std::os::raw::c_char, +) { + ffi_guard( + || { + let _ = trackbb_log; + termination::rt_generate_aux_files(violation != 0); + }, + (), + ); +} + +/// Core entry point of `generate_confirmation_file` +/// (`WitnessGeneration.c:32-36`), which `WitnessGeneration.bc` forwards to +/// (D7): writes `END OF TIME` to `map2check_checked_error`. +#[no_mangle] +pub extern "C" fn __m2c_rt_witness_generate_confirmation_file() { + ffi_guard(termination::rt_generate_confirmation_file, ()); +} + +/// Turns on the witness files of a Map2Check run, the C ABI of the old +/// `map2check_enable_witness` (not exported since 2.0.0, Task 2.10), with +/// its behaviour: when a run with a mode ends, `map2check_exit` also writes +/// `track_bb_log.st`, the logs of the memory modes and, on a violation, +/// `klee_log.csv` and `map2check_checked_error`. Configuration, kept by +/// `map2check_init`. In develop the frontend selects the witness by linking +/// `WitnessGeneration.bc` instead of `WitnessGenerationNone.bc` +/// (`caller.cpp:624-640`); the host programs that link the staticlib +/// (`tests/ffi_harness.c`, the link tests of `scripts/runtime-ir/`) call +/// this instead. No IR module calls it, and the drop-in core (feature +/// `dropin`) ignores the option: it ends a run through the +/// `generate_aux_files`/`generate_confirmation_file` of the witness module it +/// is linked with, as the C does (Task 3.1). It stays for the staticlib, +/// which cannot reference those module functions (they would be undefined in +/// `cargo test` and in every program that links the staticlib alone). +#[no_mangle] +pub extern "C" fn __m2c_rt_witness_enable() { + ffi_guard(|| state::update_config(|c| c.witness = true), ()); +} + +// ── C runtime ABI: AnalysisModeOverflow (core entry points, D7) ───────────── +// +// `AnalysisModeOverflow.c` of Map2Check `develop`. The C names +// (`analysis_*`, `map2check_binop_*`, `overflowError`) are defined only by +// the generated IR module `AnalysisModeOverflow.bc` +// (`scripts/runtime-ir/gen_ll.py`), which forwards each one to the +// `__m2c_rt_overflow_` below: every analysis mode defines +// `analysis_*`, so the core must not. `divisionByZeroError` and +// `unknown_not_supported` have no caller outside the C module and stay +// private (SD-22). The checks are `SignedBinop` (`analysismode::overflow`). + +/// The signed binops of `AnalysisModeOverflow.c:197-276`: on a violation, +/// `overflowError` (FALSE_OVERFLOW, `:185-188`) or `divisionByZeroError` +/// (FALSE_DIVBYZERO, `:192-195`), which write the property files and call +/// `map2check_error`. No mode gate: linking the module is the mode. `scope` +/// is not used, as in C. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +unsafe fn rt_overflow_signed( + op: SignedOp, + param1: i64, + param2: i64, + width: std::os::raw::c_uint, + line: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + let checker = SignedBinop { + op, + lhs: param1, + rhs: param2, + bits: width, + }; + if let Some(property) = checker.violation() { + // SAFETY: caller guarantees function_name is valid or null. + let fname: &str = &unsafe { c_str(function_name) }; + record_violation(property, line, fname, false); + } +} + +/// `analysis_init` of the overflow mode (`AnalysisModeOverflow.c:279`). The C +/// body is empty: linking this module is what selects the overflow mode +/// (`caller.cpp:575-612`), so the core entry point selects +/// `AnalysisMode_Overflow`, which turns on the result files and the abort +/// of the first violation (SD-14). Nothing else changes. +#[no_mangle] +pub extern "C" fn __m2c_rt_overflow_analysis_init() { + ffi_guard( + || state::update_config(|c| c.mode = Some(AnalysisMode::Overflow)), + (), + ); +} + +/// `analysis_destroy` of the overflow mode (`AnalysisModeOverflow.c:281`): +/// empty, the mode keeps no data. +#[no_mangle] +pub extern "C" fn __m2c_rt_overflow_analysis_destroy() { + ffi_guard(|| (), ()); +} + +/// `analysis_is_program_correct` of the overflow mode +/// (`AnalysisModeOverflow.c:283`): always TRUE (1). +#[no_mangle] +pub extern "C" fn __m2c_rt_overflow_analysis_is_program_correct() -> std::os::raw::c_char { + ffi_guard(|| 1, 1) +} + +/// `analysis_generate_aux_witness_files` of the overflow mode +/// (`AnalysisModeOverflow.c:285`): empty. +#[no_mangle] +pub extern "C" fn __m2c_rt_overflow_analysis_generate_aux_witness_files() { + ffi_guard(|| (), ()); +} + +/// `overflowError` (`AnalysisModeOverflow.c:185-188`): +/// `write_property(FALSE_OVERFLOW, line, function_name)` and +/// `map2check_error()`. Here: records `OVERFLOW` at `line` in +/// `function_name` (the first violation stays final, SD-03); with a mode set +/// the property files are written and the process aborts. NULL reads as "". +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[allow(non_snake_case)] +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_overflow_overflowError( + line: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: caller guarantees function_name is valid or null. + let fname: &str = &unsafe { c_str(function_name) }; + record_violation(ViolatedProperty::Overflow, line, fname, false); + }, + (), + ); +} + +/// `map2check_binop_add` (`AnalysisModeOverflow.c:197-206`): `OVERFLOW` when `param1 + param2` leaves the range of `width`. +/// Operands sign-extended to 64 bits, `width` the bit width of the result +/// type (0 or 64 and above mean 64). See `rt_overflow_signed`. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_overflow_map2check_binop_add( + param1: i64, + param2: i64, + width: std::os::raw::c_uint, + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + let _ = scope; + // SAFETY: forwarded caller contract. + unsafe { rt_overflow_signed(SignedOp::Add, param1, param2, width, line, function_name) } + }, + (), + ); +} + +/// `map2check_binop_sub` (`AnalysisModeOverflow.c:208-217`): `OVERFLOW` when `param1 - param2` leaves the range of `width`. +/// Operands sign-extended to 64 bits, `width` the bit width of the result +/// type (0 or 64 and above mean 64). See `rt_overflow_signed`. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_overflow_map2check_binop_sub( + param1: i64, + param2: i64, + width: std::os::raw::c_uint, + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + let _ = scope; + // SAFETY: forwarded caller contract. + unsafe { rt_overflow_signed(SignedOp::Sub, param1, param2, width, line, function_name) } + }, + (), + ); +} + +/// `map2check_binop_mul` (`AnalysisModeOverflow.c:234-243`): `OVERFLOW` when `param1 * param2` leaves the range of `width`. +/// Operands sign-extended to 64 bits, `width` the bit width of the result +/// type (0 or 64 and above mean 64). See `rt_overflow_signed`. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_overflow_map2check_binop_mul( + param1: i64, + param2: i64, + width: std::os::raw::c_uint, + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + let _ = scope; + // SAFETY: forwarded caller contract. + unsafe { rt_overflow_signed(SignedOp::Mul, param1, param2, width, line, function_name) } + }, + (), + ); +} + +/// `map2check_binop_sdiv` (`AnalysisModeOverflow.c:250-262`): `FALSE-DIVBYZERO` when `param2` is 0, `OVERFLOW` for MIN / -1 of `width`. +/// Operands sign-extended to 64 bits, `width` the bit width of the result +/// type (0 or 64 and above mean 64). See `rt_overflow_signed`. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_overflow_map2check_binop_sdiv( + param1: i64, + param2: i64, + width: std::os::raw::c_uint, + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + let _ = scope; + // SAFETY: forwarded caller contract. + unsafe { rt_overflow_signed(SignedOp::Div, param1, param2, width, line, function_name) } + }, + (), + ); +} + +/// `map2check_binop_srem` (`AnalysisModeOverflow.c:264-276`): `FALSE-DIVBYZERO` when `param2` is 0, `OVERFLOW` for MIN % -1 of `width` (undefined in C). +/// Operands sign-extended to 64 bits, `width` the bit width of the result +/// type (0 or 64 and above mean 64). See `rt_overflow_signed`. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_overflow_map2check_binop_srem( + param1: i64, + param2: i64, + width: std::os::raw::c_uint, + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + let _ = scope; + // SAFETY: forwarded caller contract. + unsafe { rt_overflow_signed(SignedOp::Rem, param1, param2, width, line, function_name) } + }, + (), + ); +} + +/// `map2check_binop_add_uint` (`AnalysisModeOverflow.c:224-227`): a deliberate +/// no-op, as in C (`:219-223`): unsigned wraparound is well-defined and is +/// not an overflow under SV-COMP's no-overflow property. +#[no_mangle] +pub extern "C" fn __m2c_rt_overflow_map2check_binop_add_uint( + param1: u64, + param2: u64, + width: std::os::raw::c_uint, + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + let _ = (param1, param2, width, line, scope, function_name); + }, + (), + ); +} + +/// `map2check_binop_sub_uint` (`AnalysisModeOverflow.c:229-232`): a deliberate +/// no-op, as in C (`:219-223`): unsigned wraparound is well-defined and is +/// not an overflow under SV-COMP's no-overflow property. +#[no_mangle] +pub extern "C" fn __m2c_rt_overflow_map2check_binop_sub_uint( + param1: u64, + param2: u64, + width: std::os::raw::c_uint, + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + let _ = (param1, param2, width, line, scope, function_name); + }, + (), + ); +} + +/// `map2check_binop_mul_uint` (`AnalysisModeOverflow.c:245-248`): a deliberate +/// no-op, as in C (`:219-223`): unsigned wraparound is well-defined and is +/// not an overflow under SV-COMP's no-overflow property. +#[no_mangle] +pub extern "C" fn __m2c_rt_overflow_map2check_binop_mul_uint( + param1: u64, + param2: u64, + width: std::os::raw::c_uint, + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + let _ = (param1, param2, width, line, scope, function_name); + }, + (), + ); +} + +// ── C runtime ABI: AnalysisModeAssert and AnalysisModeNone (core entry points, D7) ── +// +// `AnalysisModeAssert.c` and `AnalysisModeNone.c` of Map2Check `develop`. As +// for the overflow mode, the C names (`analysis_*`, `map2check_assert`, +// `__VERIFIER_assert`) are defined only by the generated IR modules +// `AnalysisModeAssert.bc` and `AnalysisModeNone.bc` +// (`scripts/runtime-ir/gen_ll.py`), never by this library. `__VERIFIER_assert` +// is an empty function in the IR (`AnalysisModeAssert.c:25-27`), so it has no +// core entry point. `analysis_init` of both modules forwards to the entry +// point that selects the mode: linking the module is what picks it in C +// (`caller.cpp:575-612`). `REACHABILITY_MODE` and `COVER_BRANCHES_MODE` both +// link `AnalysisModeNone.c`, so they cannot be told apart at link time; the +// none module selects `AnalysisMode_None`, whose checks are all inactive, +// exactly as the C module defines no check. + +/// `analysis_init` of the assert mode (`AnalysisModeAssert.c:29-31`). The C +/// body is empty; the core entry point selects `AnalysisMode_Assert` +/// (result files and abort on the first violation, SD-14). Nothing else +/// changes. +#[no_mangle] +pub extern "C" fn __m2c_rt_assert_analysis_init() { + ffi_guard( + || state::update_config(|c| c.mode = Some(AnalysisMode::Assert)), + (), + ); +} + +/// `analysis_destroy` of the assert mode (`AnalysisModeAssert.c:33-35`): +/// empty. +#[no_mangle] +pub extern "C" fn __m2c_rt_assert_analysis_destroy() { + ffi_guard(|| (), ()); +} + +/// `analysis_is_program_correct` of the assert mode +/// (`AnalysisModeAssert.c:37`): always TRUE (1). +#[no_mangle] +pub extern "C" fn __m2c_rt_assert_analysis_is_program_correct() -> std::os::raw::c_char { + ffi_guard(|| 1, 1) +} + +/// `analysis_generate_aux_witness_files` of the assert mode +/// (`AnalysisModeAssert.c:39-41`): empty. +#[no_mangle] +pub extern "C" fn __m2c_rt_assert_analysis_generate_aux_witness_files() { + ffi_guard(|| (), ()); +} + +/// `map2check_assert` (`AnalysisModeAssert.c:18-23`): when `condition` is 0, +/// `write_property(ASSERT, line, func_name)` and `map2check_error()`. Here: +/// records `ASSERT` at `line` in `func_name` (the first violation stays +/// final, SD-03); with a mode set the property files are written and the +/// process aborts. NULL reads as "". No mode gate: linking the module is the +/// mode. +/// +/// # Safety +/// `func_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_assert_map2check_assert( + condition: std::os::raw::c_int, + line: std::os::raw::c_int, + func_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: caller guarantees func_name is a valid C string or null. + let fname: &str = &unsafe { c_str(func_name) }; + let ctx = VccContext::new(line as u32, 0, fname); + match (AssertChecker { + expression: condition, + }) + .check(&ctx) + { + Ok(crate::analysismode::VccOutcome::Violated { property }) => { + record_violation(property, line as u32, fname, false); + } + Ok(crate::analysismode::VccOutcome::Safe) => {} + Err(e) => log_error(&e), + } + }, + (), + ); +} + +/// `analysis_init` of the none mode (`AnalysisModeNone.c:11-13`). The C body +/// is empty; the core entry point selects `AnalysisMode_None`, which turns on +/// the result files and the abort of the first violation (SD-14), so that +/// `map2check_target_function` ends the run as in C. +#[no_mangle] +pub extern "C" fn __m2c_rt_none_analysis_init() { + ffi_guard( + || state::update_config(|c| c.mode = Some(AnalysisMode::None)), + (), + ); +} + +/// `analysis_destroy` of the none mode (`AnalysisModeNone.c:15-17`): empty. +#[no_mangle] +pub extern "C" fn __m2c_rt_none_analysis_destroy() { + ffi_guard(|| (), ()); +} + +/// `analysis_is_program_correct` of the none mode (`AnalysisModeNone.c:19`): +/// always TRUE (1). +#[no_mangle] +pub extern "C" fn __m2c_rt_none_analysis_is_program_correct() -> std::os::raw::c_char { + ffi_guard(|| 1, 1) +} + +/// `analysis_generate_aux_witness_files` of the none mode +/// (`AnalysisModeNone.c:21-23`): empty. +#[no_mangle] +pub extern "C" fn __m2c_rt_none_analysis_generate_aux_witness_files() { + ffi_guard(|| (), ()); +} + +// ── C runtime ABI: AnalysisModeMemtrack (core entry points, D7) ───────────── +// +// `AnalysisModeMemtrack.c` of Map2Check `develop`. The C names (`analysis_*`, +// the 12 memory `map2check_*` the pass calls and `map2check_wasm_*`) are +// defined only by the generated IR module `AnalysisModeMemtrack.bc` +// (`scripts/runtime-ir/gen_ll.py`), each forwarding to the +// `__m2c_rt_memtrack_` below with the signature of the C definition. +// The logs (`heap_log`, `allocation_log`, `list_log`), `ERROR_DEREF` and +// `update_reference_list_log` stay private (SD-22): `AnalysisState::c_memory` +// (`memtrack::c_mode`). `ContainerRealloc.bc`, `ContainerBTree.bc`, +// `BTree.bc`, `AllocationLog.bc`, `ListLog.bc` and `HeapLog.bc` are empty IR: +// one Rust container serves the Array and `--btree` builds (D3). +// +// The rules take a `MemMode`, and so do the `rt_mem_*` helpers below: the +// memcleanup module (Task 2.6, below) shares them. No mode gate: linking the module +// is the mode. On a violation the C writes the property files and calls +// `map2check_error()`; here the violation is recorded and, with a mode set, +// the files are written and the run aborts (`record_violation`). The rows +// the C appends before it reports are appended first, so the witness logs +// match. NULL strings read as "". + +/// The bytes of a C string, `b""` for NULL. +/// +/// # Safety +/// `ptr` must be null or a valid null-terminated C string. +unsafe fn c_bytes<'a>(ptr: *const std::os::raw::c_char) -> &'a [u8] { + if ptr.is_null() { + return b""; + } + // SAFETY: caller guarantees a valid null-terminated C string. + unsafe { CStr::from_ptr(ptr) }.to_bytes() +} + +/// Runs `f` on the drop-in memory logs and the C step counter. +fn with_c_memory( + default: R, + f: impl FnOnce(&mut crate::memtrack::c_mode::CMemory, &mut u32) -> R, +) -> R { + match state::with_state(|s| Ok(f(&mut s.c_memory, &mut s.c_step))) { + Ok(r) => r, + Err(e) => { + log_error(&e); + default + } + } +} + +/// `analysis_init` of the memory modes (`AnalysisModeMemtrack.c:125-130`): +/// selects `mode` (linking the module is what picks it in C) and starts new +/// logs with `ERROR_DEREF` clear. +fn rt_mem_analysis_init(mode: AnalysisMode) { + state::update_config(|c| c.mode = Some(mode)); + with_c_memory((), |m, _| m.clear()); +} + +/// `analysis_is_program_correct` of the memory modes +/// (`AnalysisModeMemtrack.c:138-146`): on a leak, `write_property` of its +/// property (which writes `map2check_property` and its own file) and FALSE. +/// It records nothing and does not end the run; `map2check_exit` does. +fn rt_mem_is_program_correct(mode: MemMode) -> std::os::raw::c_char { + match with_c_memory(None, |m, _| m.is_program_correct(mode)) { + Some(property) => { + termination::write_property(property, 0, ""); + 0 + } + None => 1, + } +} + +/// `analysis_generate_aux_witness_files` of the memory modes +/// (`AnalysisModeMemtrack.c:148-152`). +fn rt_mem_generate_aux_witness_files() { + if let Some(dir) = termination::output_dir() { + if let Err(e) = state::with_state(|s| { + termination::write_c_memory_logs(&dir, &s.c_memory); + Ok(()) + }) { + log_error(&e); + } + } +} + +/// `map2check_check_cstring` (`AnalysisModeMemtrack.c:159-166`). +/// +/// # Safety +/// `string` must be null, or every byte up to its terminator that lies in a +/// tracked block must be readable. +unsafe fn rt_mem_check_cstring(mode: MemMode, string: *const std::os::raw::c_char) { + with_c_memory((), |m, _| { + m.check_cstring(mode, string as u64, |at| { + // SAFETY: `at` is read only after `map2check_load` found it inside + // a tracked block, as the C reads `string[i]`; caller contract. + unsafe { *(at as usize as *const u8) } + }) + }); +} + +/// `map2check_check_deref` (`AnalysisModeMemtrack.c:309-315`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +unsafe fn rt_mem_check_deref( + mode: MemMode, + line: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + if with_c_memory(false, |m, _| m.check_deref(mode)) { + // SAFETY: caller contract. + let fname: &str = &unsafe { c_str(function_name) }; + // The result keeps lines as u32; the C `int` is stored as its bit + // pattern and the property files print it back with `%d` + // (`write_property_files`: `line as i32`), so a negative line reads + // as in C. + let line = u32::from_ne_bytes(line.to_ne_bytes()); + record_violation(ViolatedProperty::MemsafetyDeref, line, fname, false); + } +} + +/// `map2check_free_resolved_address` (`AnalysisModeMemtrack.c:176-195`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +unsafe fn rt_mem_free_resolved_address( + mode: MemMode, + ptr: *const std::os::raw::c_void, + line: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, + is_null_valid: std::os::raw::c_short, +) { + let outcome = with_c_memory(FreeOutcome::Released, |m, step| { + m.free_resolved_address(mode, step, ptr as u64, line, is_null_valid) + }); + if outcome == FreeOutcome::InvalidFree { + // SAFETY: caller contract. + let fname: &str = &unsafe { c_str(function_name) }; + record_violation(ViolatedProperty::MemsafetyFree, line, fname, false); + } +} + +/// `map2check_add_store_pointer` (`AnalysisModeMemtrack.c:258-307`). +/// +/// # Safety +/// `name` and `function_name` must be null or valid null-terminated C strings. +unsafe fn rt_mem_add_store_pointer( + var: *const std::os::raw::c_void, + value: *const std::os::raw::c_void, + scope: std::os::raw::c_uint, + name: *const std::os::raw::c_char, + line: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + // SAFETY: caller contract. + let (name, fname) = unsafe { (c_bytes(name), c_bytes(function_name)) }; + with_c_memory((), |m, step| { + m.add_store_pointer(step, var as u64, value as u64, scope, name, line, fname) + }); +} + +/// `map2check_malloc` (`AnalysisModeMemtrack.c:317-335`): a NULL block +/// (low 32 bits zero) is `write_property_unknown` + `map2check_error`. +fn rt_mem_malloc(ptr: *const std::os::raw::c_void, size: std::os::raw::c_int) { + if !with_c_memory(true, |m, _| m.malloc(ptr as u64, size)) { + record_violation(ViolatedProperty::Unknown, 0, "", false); + } +} + +/// `map2check_calloc` (`AnalysisModeMemtrack.c:347-349`). +fn rt_mem_calloc( + ptr: *const std::os::raw::c_void, + quantity: std::os::raw::c_int, + size: std::os::raw::c_int, +) { + if !with_c_memory(true, |m, _| m.calloc(ptr as u64, quantity, size)) { + record_violation(ViolatedProperty::Unknown, 0, "", false); + } +} + +/// `map2check_posix` (`AnalysisModeMemtrack.c:340-345`): reads the block +/// from `*(void **)ptr`, `map2check_malloc` of it, then the store of it into +/// `ptr` with scope 1, name "pointer", line 1 and function "cocoiso". A +/// NULL `ptr` is ignored (develop dereferences it). +/// +/// # Safety +/// `ptr` must be null or point to a readable `void *`. +unsafe fn rt_mem_posix(ptr: *const std::os::raw::c_void, size: std::os::raw::c_int) { + if ptr.is_null() { + return; + } + // SAFETY: caller guarantees a readable `void *`; unaligned-safe read. + let block = unsafe { ptr.cast::<*const std::os::raw::c_void>().read_unaligned() }; + if !with_c_memory(true, |m, _| m.malloc(block as u64, size)) { + record_violation(ViolatedProperty::Unknown, 0, "", false); + return; + } + with_c_memory((), |m, step| { + m.add_store_pointer(step, ptr as u64, block as u64, 1, b"pointer", 1, b"cocoiso") + }); +} + +/// `map2check_free` (`AnalysisModeMemtrack.c:351-378`): `ptr` is the slot of +/// the pointer variable; the block is read from `*(long *)ptr`. A NULL `ptr` +/// is ignored (develop dereferences it). +/// +/// # Safety +/// `name` and `function_name` must be null or valid null-terminated C +/// strings; `ptr` must be null or point to a readable `long`. +unsafe fn rt_mem_free( + mode: MemMode, + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, + scope: std::os::raw::c_uint, + line: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + if ptr.is_null() { + return; + } + // SAFETY: caller guarantees a readable `long`; unaligned-safe read. + let points_to = unsafe { ptr.cast::().read_unaligned() }; + // SAFETY: caller contract. + let (name, fname) = unsafe { (c_bytes(name), c_bytes(function_name)) }; + let outcome = with_c_memory(FreeOutcome::Released, |m, step| { + m.free(mode, step, name, ptr as u64, points_to, scope, line, fname) + }); + if outcome == FreeOutcome::InvalidFree { + // SAFETY: caller contract. + let fname: &str = &unsafe { c_str(function_name) }; + record_violation(ViolatedProperty::MemsafetyFree, line, fname, false); + } +} + +/// `map2check_alloca` (`AnalysisModeMemtrack.c:386-391`). +/// +/// # Safety +/// `name` must be null or a valid null-terminated C string. +unsafe fn rt_mem_alloca( + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, + size_of_primitive: std::os::raw::c_int, + line: std::os::raw::c_int, + scope: std::os::raw::c_int, +) { + // SAFETY: caller contract. + let name = unsafe { c_bytes(name) }; + with_c_memory((), |m, _| { + m.alloca(name, ptr as u64, size, size_of_primitive, line, scope) + }); +} + +/// `map2check_non_static_alloca` (`AnalysisModeMemtrack.c:380-384`). +/// +/// # Safety +/// `name` must be null or a valid null-terminated C string. +unsafe fn rt_mem_non_static_alloca( + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, + size_of_primitive: std::os::raw::c_int, + line: std::os::raw::c_int, + scope: std::os::raw::c_int, +) { + // SAFETY: caller contract. + let name = unsafe { c_bytes(name) }; + with_c_memory((), |m, _| { + m.non_static_alloca(name, ptr as u64, size, size_of_primitive, line, scope) + }); +} + +/// `map2check_function` (`AnalysisModeMemtrack.c:393-397`). +/// +/// # Safety +/// `name` must be null or a valid null-terminated C string. +unsafe fn rt_mem_function(name: *const std::os::raw::c_char, ptr: *const std::os::raw::c_void) { + // SAFETY: caller contract. + let name = unsafe { c_bytes(name) }; + with_c_memory((), |m, _| m.function(name, ptr as u64)); +} + +/// `analysis_init` of the memtrack mode (`AnalysisModeMemtrack.c:125-130`): +/// selects `AnalysisMode_Memtrack` and starts new logs, `ERROR_DEREF` clear. +#[no_mangle] +pub extern "C" fn __m2c_rt_memtrack_analysis_init() { + ffi_guard(|| rt_mem_analysis_init(AnalysisMode::Memtrack), ()); +} + +/// `analysis_destroy` of the memtrack mode (`AnalysisModeMemtrack.c:132-136`): +/// frees the three logs. +#[no_mangle] +pub extern "C" fn __m2c_rt_memtrack_analysis_destroy() { + ffi_guard(|| with_c_memory((), |m, _| m.clear()), ()); +} + +/// `analysis_is_program_correct` of the memtrack mode +/// (`AnalysisModeMemtrack.c:138-146`): when `valid_allocation_log` finds a +/// leaked block (it appends a freed row for each), writes FALSE-MEMTRACK to +/// `map2check_property` and `map2check_property_memtrack` and returns 0; +/// otherwise 1. +#[no_mangle] +pub extern "C" fn __m2c_rt_memtrack_analysis_is_program_correct() -> std::os::raw::c_char { + ffi_guard(|| rt_mem_is_program_correct(MemMode::Memtrack), 1) +} + +/// `analysis_generate_aux_witness_files` of the memtrack mode +/// (`AnalysisModeMemtrack.c:148-152`): `heap_log.csv`, `allocation_log.csv` +/// and `list_log.csv` in the current directory, in the C formats. +#[no_mangle] +pub extern "C" fn __m2c_rt_memtrack_analysis_generate_aux_witness_files() { + ffi_guard(rt_mem_generate_aux_witness_files, ()); +} + +/// `map2check_check_cstring` (`AnalysisModeMemtrack.c:159-166`): every byte +/// up to the terminator must lie in a tracked block (`map2check_load` of 1 +/// byte); stops before the first invalid byte with `ERROR_DEREF` set, which +/// the caller's `map2check_check_deref` reports. NULL does nothing. +/// +/// # Safety +/// `string` must be null, or each byte of it that lies in a tracked block, +/// up to its terminator, must be readable. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memtrack_map2check_check_cstring( + string: *const std::os::raw::c_char, +) { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { rt_mem_check_cstring(MemMode::Memtrack, string) }, + (), + ); +} + +/// `map2check_load` (`AnalysisModeMemtrack.c:168-174`): sets `ERROR_DEREF` +/// when neither a heap-log row (stack slot, function) nor a live allocation +/// covers the `size` bytes at `ptr`. Reports nothing by itself. +#[no_mangle] +pub extern "C" fn __m2c_rt_memtrack_map2check_load( + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, +) { + ffi_guard( + || with_c_memory((), |m, _| m.load(MemMode::Memtrack, ptr as u64, size)), + (), + ); +} + +/// `map2check_free_resolved_address` (`AnalysisModeMemtrack.c:176-195`): +/// NULL with `is_null_valid` returns; `is_invalid_free` (`ListLog.c:95-118`) +/// records FALSE-FREE at `line`; otherwise the block's row is freed and every +/// pointer to it gets a FREE row (`update_reference_list_log`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string; `ptr` +/// is never dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memtrack_map2check_free_resolved_address( + ptr: *const std::os::raw::c_void, + line: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, + is_null_valid: std::os::raw::c_short, +) { + ffi_guard( + || { + // SAFETY: forwarded caller contract. + unsafe { + rt_mem_free_resolved_address( + MemMode::Memtrack, + ptr, + line, + function_name, + is_null_valid, + ) + } + }, + (), + ); +} + +/// `map2check_add_store_pointer` (`AnalysisModeMemtrack.c:258-307`): appends +/// a list row for `var = value` with the status of `value` in the allocation +/// log, unless the variable's newest row is the same, then updates the +/// other pointers to `value`. +/// +/// # Safety +/// `name` and `function_name` must be null or valid null-terminated C +/// strings; `var` and `value` are never dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memtrack_map2check_add_store_pointer( + var: *const std::os::raw::c_void, + value: *const std::os::raw::c_void, + scope: std::os::raw::c_uint, + name: *const std::os::raw::c_char, + line: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded caller contract. + unsafe { rt_mem_add_store_pointer(var, value, scope, name, line, function_name) } + }, + (), + ); +} + +/// `map2check_check_deref` (`AnalysisModeMemtrack.c:309-315`): when +/// `ERROR_DEREF` is set, records FALSE-DEREF at `line` in `function_name`. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memtrack_map2check_check_deref( + line: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded caller contract. + unsafe { rt_mem_check_deref(MemMode::Memtrack, line, function_name) } + }, + (), + ); +} + +/// `map2check_malloc` (`AnalysisModeMemtrack.c:317-335`): records the block, +/// reviving the row of a known address with the new size. A NULL block (its +/// low 32 bits zero, `int addr = (long)ptr`) records UNKNOWN +/// (`map2check_property__unknown`) and ends the run. +#[no_mangle] +pub extern "C" fn __m2c_rt_memtrack_map2check_malloc( + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, +) { + ffi_guard(|| rt_mem_malloc(ptr, size), ()); +} + +/// `map2check_posix` (`AnalysisModeMemtrack.c:340-345`), for +/// `posix_memalign(ptr, ...)`: `map2check_malloc(*ptr, size)` and the store +/// of the block into `ptr` (scope 1, "pointer", line 1, "cocoiso"). +/// +/// # Safety +/// `ptr` must be null or point to a readable `void *`. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memtrack_map2check_posix( + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, +) { + // SAFETY: forwarded caller contract. + ffi_guard(|| unsafe { rt_mem_posix(ptr, size) }, ()); +} + +/// `map2check_calloc` (`AnalysisModeMemtrack.c:347-349`): +/// `map2check_malloc(ptr, quantity * size)`, an `int` product. +#[no_mangle] +pub extern "C" fn __m2c_rt_memtrack_map2check_calloc( + ptr: *const std::os::raw::c_void, + quantity: std::os::raw::c_int, + size: std::os::raw::c_int, +) { + ffi_guard(|| rt_mem_calloc(ptr, quantity, size), ()); +} + +/// `map2check_free` (`AnalysisModeMemtrack.c:351-378`): `ptr` is the slot of +/// the pointer variable `name`. A NULL block does nothing; otherwise a FREE +/// row for the variable is appended, then `is_invalid_free` records +/// FALSE-FREE at `line`, or the block is released and every pointer to it +/// gets a FREE row. +/// +/// # Safety +/// `name` and `function_name` must be null or valid null-terminated C +/// strings; `ptr` must be null or point to a readable `long`. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memtrack_map2check_free( + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, + scope: std::os::raw::c_uint, + line: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded caller contract. + unsafe { rt_mem_free(MemMode::Memtrack, name, ptr, scope, line, function_name) } + }, + (), + ); +} + +/// `map2check_non_static_alloca` (`AnalysisModeMemtrack.c:380-384`): +/// `map2check_alloca` of `size * size_of_primitive` bytes (an `int` product). +/// +/// # Safety +/// `name` must be null or a valid null-terminated C string; `ptr` is never +/// dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memtrack_map2check_non_static_alloca( + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, + size_of_primitive: std::os::raw::c_int, + line: std::os::raw::c_int, + scope: std::os::raw::c_int, +) { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { rt_mem_non_static_alloca(name, ptr, size, size_of_primitive, line, scope) }, + (), + ); +} + +/// `map2check_alloca` (`AnalysisModeMemtrack.c:386-391`): a heap-log row +/// (`HeapLog.c:22-32`) for the stack slot `name` of `size` bytes at `ptr`. +/// +/// # Safety +/// `name` must be null or a valid null-terminated C string; `ptr` is never +/// dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memtrack_map2check_alloca( + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, + size_of_primitive: std::os::raw::c_int, + line: std::os::raw::c_int, + scope: std::os::raw::c_int, +) { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { rt_mem_alloca(name, ptr, size, size_of_primitive, line, scope) }, + (), + ); +} + +/// `map2check_function` (`AnalysisModeMemtrack.c:393-397`): a heap-log row +/// of size 1 for the function `name` at `ptr`. +/// +/// # Safety +/// `name` must be null or a valid null-terminated C string; `ptr` is never +/// dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memtrack_map2check_function( + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, +) { + // SAFETY: forwarded caller contract. + ffi_guard(|| unsafe { rt_mem_function(name, ptr) }, ()); +} + +/// `map2check_wasm_malloc` (`AnalysisModeMemtrack.c:403-416`): the block of +/// the lifted `malloc` at linear-memory `offset`, in the same allocation log +/// as `map2check_malloc`; offsets 0 and `(uint64_t)-1` are ignored. +#[no_mangle] +pub extern "C" fn __m2c_rt_memtrack_map2check_wasm_malloc(offset: u64, size: u64) { + ffi_guard(|| with_c_memory((), |m, _| m.wasm_malloc(offset, size)), ()); +} + +/// `map2check_wasm_free` (`AnalysisModeMemtrack.c:418-428`): frees the row of +/// `offset`; an offset with no row records FALSE-FREE at `line`, function "". +#[no_mangle] +pub extern "C" fn __m2c_rt_memtrack_map2check_wasm_free(offset: u64, line: std::os::raw::c_int) { + ffi_guard( + || { + if !with_c_memory(true, |m, _| m.wasm_free(offset)) { + // The C `int` line as its bit pattern (see rt_mem_check_deref). + let line = u32::from_ne_bytes(line.to_ne_bytes()); + record_violation(ViolatedProperty::MemsafetyFree, line, "", false); + } + }, + (), + ); +} + +/// `map2check_wasm_check_access` (`AnalysisModeMemtrack.c:430-436`): an +/// access outside every live block records OVERFLOW at line 0, function "" +/// (`FALSE_OVERFLOW`, SD-19). +#[no_mangle] +pub extern "C" fn __m2c_rt_memtrack_map2check_wasm_check_access(offset: u64, access_size: u64) { + ffi_guard( + || { + if !with_c_memory(true, |m, _| m.wasm_check_access(offset, access_size)) { + record_violation(ViolatedProperty::Overflow, 0, "", false); + } + }, + (), + ); +} + +// ── C runtime ABI: AnalysisModeMemcleanup (core entry points, D7) ─────────── +// +// `AnalysisModeMemcleanup.c` of Map2Check `develop`. The module +// `AnalysisModeMemcleanup.bc` (`scripts/runtime-ir/gen_ll.py`) defines the +// four `analysis_*` functions and the 12 memory `map2check_*` the pass calls +// and forwards each one to `__m2c_rt_memcleanup_` below. It has no +// `map2check_wasm_*`. Linking the module picks the mode (`MemMode`); the +// rules and the logs are the memtrack ones (`rt_mem_*`, `memtrack::c_mode`). +// What differs in C is in `MemMode::Memcleanup`: the verdict needs a pointer +// variable to the last leak (`:138-148`), `map2check_load`, +// `map2check_check_cstring` and `map2check_check_deref` are empty (`:158`, +// `:159`, `:294`), and `error = FALSE` in both release functions (`:168`, +// `:342`). A release with no allocation row is SD-23. + +/// `analysis_init` of the memcleanup mode (`AnalysisModeMemcleanup.c:125-130`): +/// selects `AnalysisMode_Memcleanup` and starts new logs. +#[no_mangle] +pub extern "C" fn __m2c_rt_memcleanup_analysis_init() { + ffi_guard(|| rt_mem_analysis_init(AnalysisMode::Memcleanup), ()); +} + +/// `analysis_destroy` of the memcleanup mode (`AnalysisModeMemcleanup.c:132-136`): +/// frees the three logs. +#[no_mangle] +pub extern "C" fn __m2c_rt_memcleanup_analysis_destroy() { + ffi_guard(|| with_c_memory((), |m, _| m.clear()), ()); +} + +/// `analysis_is_program_correct` of the memcleanup mode +/// (`AnalysisModeMemcleanup.c:138-148`): when `valid_allocation_log` finds a +/// leaked block and a pointer variable still refers to it +/// (`is_memcleanup_error`, `ListLog.c:40-65`), writes FALSE-MEMCLEANUP to +/// `map2check_property` and `map2check_property_memcleanup` and returns 0; +/// otherwise 1. +#[no_mangle] +pub extern "C" fn __m2c_rt_memcleanup_analysis_is_program_correct() -> std::os::raw::c_char { + ffi_guard(|| rt_mem_is_program_correct(MemMode::Memcleanup), 1) +} + +/// `analysis_generate_aux_witness_files` of the memcleanup mode +/// (`AnalysisModeMemcleanup.c:150-154`): the same three logs as memtrack. +#[no_mangle] +pub extern "C" fn __m2c_rt_memcleanup_analysis_generate_aux_witness_files() { + ffi_guard(rt_mem_generate_aux_witness_files, ()); +} + +/// `map2check_check_cstring` (`AnalysisModeMemcleanup.c:160`): empty. +/// +/// # Safety +/// `string` is never read. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memcleanup_map2check_check_cstring( + string: *const std::os::raw::c_char, +) { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { rt_mem_check_cstring(MemMode::Memcleanup, string) }, + (), + ); +} + +/// `map2check_load` (`AnalysisModeMemcleanup.c:158`): empty. +#[no_mangle] +pub extern "C" fn __m2c_rt_memcleanup_map2check_load( + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, +) { + ffi_guard( + || with_c_memory((), |m, _| m.load(MemMode::Memcleanup, ptr as u64, size)), + (), + ); +} + +/// `map2check_free_resolved_address` (`AnalysisModeMemcleanup.c:161-180`): +/// NULL with `is_null_valid` returns; no free is invalid (`error = FALSE`, +/// `:168`); the block's row is freed and every pointer to it gets a FREE row. +/// A block with no row is SD-23. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string; `ptr` +/// is never dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memcleanup_map2check_free_resolved_address( + ptr: *const std::os::raw::c_void, + line: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, + is_null_valid: std::os::raw::c_short, +) { + ffi_guard( + || { + // SAFETY: forwarded caller contract. + unsafe { + rt_mem_free_resolved_address( + MemMode::Memcleanup, + ptr, + line, + function_name, + is_null_valid, + ) + } + }, + (), + ); +} + +/// `map2check_add_store_pointer` (`AnalysisModeMemcleanup.c:243-292`): as +/// memtrack's. +/// +/// # Safety +/// `name` and `function_name` must be null or valid null-terminated C +/// strings; `var` and `value` are never dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memcleanup_map2check_add_store_pointer( + var: *const std::os::raw::c_void, + value: *const std::os::raw::c_void, + scope: std::os::raw::c_uint, + name: *const std::os::raw::c_char, + line: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded caller contract. + unsafe { rt_mem_add_store_pointer(var, value, scope, name, line, function_name) } + }, + (), + ); +} + +/// `map2check_check_deref` (`AnalysisModeMemcleanup.c:294`): empty. +/// +/// # Safety +/// `function_name` is never read. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memcleanup_map2check_check_deref( + line: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded caller contract. + unsafe { rt_mem_check_deref(MemMode::Memcleanup, line, function_name) } + }, + (), + ); +} + +/// `map2check_malloc` (`AnalysisModeMemcleanup.c:296-314`): as memtrack's. +#[no_mangle] +pub extern "C" fn __m2c_rt_memcleanup_map2check_malloc( + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, +) { + ffi_guard(|| rt_mem_malloc(ptr, size), ()); +} + +/// `map2check_posix` (`AnalysisModeMemcleanup.c:319-324`): as memtrack's. +/// +/// # Safety +/// `ptr` must be null or point to a readable `void *`. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memcleanup_map2check_posix( + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, +) { + // SAFETY: forwarded caller contract. + ffi_guard(|| unsafe { rt_mem_posix(ptr, size) }, ()); +} + +/// `map2check_calloc` (`AnalysisModeMemcleanup.c:326-328`): as memtrack's. +#[no_mangle] +pub extern "C" fn __m2c_rt_memcleanup_map2check_calloc( + ptr: *const std::os::raw::c_void, + quantity: std::os::raw::c_int, + size: std::os::raw::c_int, +) { + ffi_guard(|| rt_mem_calloc(ptr, quantity, size), ()); +} + +/// `map2check_free` (`AnalysisModeMemcleanup.c:330-357`): `ptr` is the slot of +/// the pointer variable `name`. A NULL block does nothing; otherwise a FREE +/// row for the variable is appended and the block is released (never +/// FALSE-FREE, `error = FALSE`, `:342`). A block with no row is SD-23. +/// +/// # Safety +/// `name` and `function_name` must be null or valid null-terminated C +/// strings; `ptr` must be null or point to a readable `long`. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memcleanup_map2check_free( + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, + scope: std::os::raw::c_uint, + line: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded caller contract. + unsafe { rt_mem_free(MemMode::Memcleanup, name, ptr, scope, line, function_name) } + }, + (), + ); +} + +/// `map2check_non_static_alloca` (`AnalysisModeMemcleanup.c:359-363`): as +/// memtrack's. +/// +/// # Safety +/// `name` must be null or a valid null-terminated C string; `ptr` is never +/// dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memcleanup_map2check_non_static_alloca( + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, + size_of_primitive: std::os::raw::c_int, + line: std::os::raw::c_int, + scope: std::os::raw::c_int, +) { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { rt_mem_non_static_alloca(name, ptr, size, size_of_primitive, line, scope) }, + (), + ); +} + +/// `map2check_alloca` (`AnalysisModeMemcleanup.c:365-370`): as memtrack's. +/// +/// # Safety +/// `name` must be null or a valid null-terminated C string; `ptr` is never +/// dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memcleanup_map2check_alloca( + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, + size: std::os::raw::c_int, + size_of_primitive: std::os::raw::c_int, + line: std::os::raw::c_int, + scope: std::os::raw::c_int, +) { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { rt_mem_alloca(name, ptr, size, size_of_primitive, line, scope) }, + (), + ); +} + +/// `map2check_function` (`AnalysisModeMemcleanup.c:372-376`): as memtrack's. +/// +/// # Safety +/// `name` must be null or a valid null-terminated C string; `ptr` is never +/// dereferenced. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_memcleanup_map2check_function( + name: *const std::os::raw::c_char, + ptr: *const std::os::raw::c_void, +) { + // SAFETY: forwarded caller contract. + ffi_guard(|| unsafe { rt_mem_function(name, ptr) }, ()); +} + +// ── C runtime ABI: NonDetLog ──────────────────────────────────────────────── +// +// `NonDetLog.c` of Map2Check `develop`. `nondet_log_to_file`, +// `new_nondet_call`, `helper_map2check_nondet_append_element`, +// `map2check_nondet_get_log` and the `nondet_log` global are private to the +// Rust core (SD-22): `crate::nondet::c_log` and `AnalysisState::c_nondet`. +// The generator modules (`map2check_non_det_`, `main`, `nondet_init`...) +// are IR (`scripts/runtime-ir/gen_ll.py`; KLEE: Task 2.7); `nondet_init` there +// calls `nondet_log_init`. + +/// Appends one row to the nondet log: `new_nondet_call` with the current +/// step, then `helper_map2check_nondet_append_element`, which advances the +/// step (`NonDetLog.c:117-155`). The exports wrap the call in `ffi_guard`. +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +unsafe fn log_c_nondet( + kind: i32, + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + bits: u64, + function_name: *const std::os::raw::c_char, +) { + let name: &[u8] = if function_name.is_null() { + b"" + } else { + // SAFETY: caller guarantees a valid null-terminated C string. + unsafe { CStr::from_ptr(function_name) }.to_bytes() + }; + if let Err(e) = state::with_state(|s| { + let call = + crate::nondet::c_log::CNondetCall::from_parts(kind, line, scope, s.c_step, name, bits); + s.c_step = s.c_step.wrapping_add(1); + s.c_nondet.push(call); + Ok(()) + }) { + log_error(&e); + } +} + +use crate::nondet::c_log::kind as nd; + +/// `as_int = *(int *)value` over a zeroed union: the int, zero-extended. +fn int_bits(v: std::os::raw::c_int) -> u64 { + u64::from(v as u32) +} + +// One export per `map2check_nondet_` of `NonDetLog.c:157-228`, with the C +// signature `(unsigned line, unsigned scope, value, const char +// *function_name)` of `NonDetLog.h`. Written out, not generated by a macro, +// so that cbindgen emits them into `include/map2check.h` and +// `tests/abi/check-signatures.sh` checks them against the C header. The bits +// are the union member `new_nondet_call` stores (`:128-141`): the int for +// every integer type but the 64-bit ones, the double for DOUBLE and FLOAT +// (a float arrives widened, `:219-221`). + +/// `map2check_nondet_int` (row type `INTEGER`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_int( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::INTEGER, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_char` (row type `CHAR`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_char( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::CHAR, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_pointer` (row type `POINTER`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_pointer( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::POINTER, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_ushort` (row type `USHORT`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_ushort( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::USHORT, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_short` (row type `SHORT`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_short( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::SHORT, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_long` (row type `LONG`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_long( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::LONG, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_ulong` (row type `ULONG`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_ulong( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::ULONG, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_bool` (row type `BOOL`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_bool( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::BOOL, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_uchar` (row type `UCHAR`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_uchar( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::UCHAR, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_pchar` (row type `PCHAR`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_pchar( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::PCHAR, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_size_t` (row type `SIZE_T`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_size_t( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::SIZE_T, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_loff_t` (row type `LOFF_T`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_loff_t( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::LOFF_T, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_sector_t` (row type `SECTOR_T`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_sector_t( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::SECTOR_T, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_uint` (row type `UINT`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. +#[no_mangle] +pub unsafe extern "C" fn map2check_nondet_uint( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { + ffi_guard( + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::UINT, line, scope, int_bits(value), function_name) } + }, + (), + ); +} + +/// `map2check_nondet_u8` (row type `U8`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. #[no_mangle] -pub extern "C" fn map2check_init() { +pub unsafe extern "C" fn map2check_nondet_u8( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { ffi_guard( || { - state::init(); - termination::on_init(); + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::U8, line, scope, int_bits(value), function_name) } }, (), ); } -/// Resets the global analysis state without aborting the caller. -#[no_mangle] -pub extern "C" fn map2check_reset() { - ffi_guard(state::reset, ()); -} - -// ── Result management ───────────────────────────────────────────────────────── - -/// End of the analysed program. With the mode unset, prints the JSON -/// report. With an analysis mode set it prints nothing and is develop's -/// `map2check_success()` -/// (`Map2CheckFunctions.c:67-70`, `:89-102`): writes `NONE` to -/// `map2check_property`, runs the end-of-program verdict of the mode, writes -/// the witness files when `map2check_enable_witness()` was called, and -/// returns; when the verdict is a violation it writes that property instead -/// and aborts. +/// `map2check_nondet_u16` (row type `U16`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. #[no_mangle] -pub extern "C" fn map2check_success() { +pub unsafe extern "C" fn map2check_nondet_u16( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { ffi_guard( || { - if !termination::on_success() { - output::print_report_unless_mode_set(); - } + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::U16, line, scope, int_bits(value), function_name) } }, (), ); } -/// Records a verification failure. +/// `map2check_nondet_u32` (row type `U32`). /// /// # Safety /// `function_name` must be null or a valid null-terminated C string. #[no_mangle] -pub unsafe extern "C" fn set_false_result( - prp: i32, - line_number: i32, +pub unsafe extern "C" fn map2check_nondet_u32( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, function_name: *const std::os::raw::c_char, ) { ffi_guard( || { - let Some(property) = violated_property_from_raw(prp) else { - eprintln!("[map2check] invalid violated-property value: {prp}"); - return; - }; - // SAFETY: caller guarantees function_name is a valid C string or null. - let fname = unsafe { c_str(function_name) }; - record_violation(property, line_number as u32, fname, false); + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::U32, line, scope, int_bits(value), function_name) } }, (), ); } -/// Returns the currently violated property. +/// `map2check_nondet_memory_byte` (row type `MEMORY_BYTE`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. #[no_mangle] -pub extern "C" fn get_current_property() -> ViolatedProperty { +pub unsafe extern "C" fn map2check_nondet_memory_byte( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_int, + function_name: *const std::os::raw::c_char, +) { ffi_guard( - || state::with_state(|s| Ok(s.result.property)).unwrap_or(ViolatedProperty::None), - ViolatedProperty::None, - ) + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::MEMORY_BYTE, line, scope, int_bits(value), function_name) } + }, + (), + ); } -/// Returns the current step counter value. +/// `map2check_nondet_unsigned` (row type `UNSIGNED`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. #[no_mangle] -pub extern "C" fn get_current_step() -> u64 { +pub unsafe extern "C" fn map2check_nondet_unsigned( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_uint, + function_name: *const std::os::raw::c_char, +) { ffi_guard( - || state::with_state(|s| Ok(s.current_step())).unwrap_or(0), - 0, - ) -} - -/// Advances and returns the step counter. Unlike `get_current_step`, this -/// mutates the counter — analogous to `get_next_step`/`next_current_step` in -/// the C library. -#[no_mangle] -pub extern "C" fn get_next_step() -> u64 { - ffi_guard(|| state::with_state(|s| Ok(s.next_step())).unwrap_or(0), 0) -} - -/// Prints the full JSON report to stdout when no analysis mode is set. With -/// a mode set (a Map2Check run) it prints nothing: the result goes to the -/// files, and the analysed program's stdout stays clean. -#[no_mangle] -pub extern "C" fn print_all_containers_as_json() { - ffi_guard(output::print_report_unless_mode_set, ()); + || { + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::UNSIGNED, line, scope, u64::from(value), function_name) } + }, + (), + ); } -/// Resets violation metadata without clearing tracking containers. -/// Host/test-only: the Map2Check passes never call it. It clears a recorded -/// violation by design, bypassing the first-violation rule (#36, SD-03). +/// `map2check_nondet_double` (row type `DOUBLE`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. #[no_mangle] -pub extern "C" fn vcc_reset_meta_data() { +pub unsafe extern "C" fn map2check_nondet_double( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: f64, + function_name: *const std::os::raw::c_char, +) { ffi_guard( || { - if let Err(e) = state::with_state(|s| { - s.result.property = ViolatedProperty::None; - s.result.ok = true; - s.result.line = 0; - s.result.function_name.clear(); - s.failed_free = None; - Ok(()) - }) { - log_error(&e); - } + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::DOUBLE, line, scope, value.to_bits(), function_name) } }, (), ); } -/// Selects the memcleanup property for the end-of-program leak check. -/// Effect: `map2check_check_mem_endprog` reports a leak as -/// `MemsafetyMemcleanup` instead of `MemsafetyMemtrack`. The option is -/// configuration: `map2check_init()` and `map2check_reset()` keep it. +/// `map2check_nondet_float` (row type `FLOAT`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. #[no_mangle] -pub extern "C" fn map2check_set_memcleanup() { +pub unsafe extern "C" fn map2check_nondet_float( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: f64, + function_name: *const std::os::raw::c_char, +) { ffi_guard( || { - state::update_config(|c| c.memcleanup = true); + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::FLOAT, line, scope, value.to_bits(), function_name) } }, (), ); } -/// Marks NULL as valid for load checks. -/// Effect: `map2check_check_load` treats a NULL `ptr` as safe once this has -/// been called, instead of flagging it as `MemsafetyDeref`. Kept across -/// `map2check_init()` and `map2check_reset()`. +/// `map2check_nondet_longlong` (row type `LONGLONG`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. #[no_mangle] -pub extern "C" fn map2check_set_null_is_valid() { +pub unsafe extern "C" fn map2check_nondet_longlong( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_longlong, + function_name: *const std::os::raw::c_char, +) { ffi_guard( || { - state::update_config(|c| c.null_is_valid = true); + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::LONGLONG, line, scope, value as u64, function_name) } }, (), ); } -/// Turns on witness generation, as linking `WitnessGeneration.c` instead of -/// `WitnessGenerationNone.c` does in develop (`caller.cpp:624-640`). Effect: -/// when a Map2Check run ends (an analysis mode is set), the library also -/// writes `track_bb_log.st`, `heap_log.csv`, `allocation_log.csv` and -/// `list_log.csv` (memory modes), and on a violation `klee_log.csv` and -/// `map2check_checked_error`. Configuration: kept by `map2check_init()` and -/// `map2check_reset()`. The Map2Check frontend links a constructor that -/// calls it into the witness binary. +/// `map2check_nondet_ulonglong` (row type `ULONGLONG`). +/// +/// # Safety +/// `function_name` must be null or a valid null-terminated C string. #[no_mangle] -pub extern "C" fn map2check_enable_witness() { +pub unsafe extern "C" fn map2check_nondet_ulonglong( + line: std::os::raw::c_uint, + scope: std::os::raw::c_uint, + value: std::os::raw::c_ulonglong, + function_name: *const std::os::raw::c_char, +) { ffi_guard( || { - state::update_config(|c| c.witness = true); + // SAFETY: forwarded from this function's contract. + unsafe { log_c_nondet(nd::ULONGLONG, line, scope, value, function_name) } }, (), ); } -/// Selects the analysis mode, as the frontend of Map2Check `develop` does by -/// linking one `AnalysisMode*.c` per run (`caller.cpp:575-612`). The -/// instrumented program calls it before `map2check_init()` with one of the -/// `AnalysisMode` values (`AnalysisMode_None` 0 to `AnalysisMode_Target` 5). -/// Any other value is ignored with a diagnostic on stderr and leaves the mode -/// as it was. The mode is configuration: `map2check_init()` and -/// `map2check_reset()` keep it. Until a valid value is set the mode is unset -/// and every check is active. Per-mode effects are in -/// `rust/src/analysismode/mode.rs`. +/// `nondet_log_init` (`NonDetLog.c:146`): a new, empty log. The step counter +/// is not touched. #[no_mangle] -pub extern "C" fn map2check_set_mode(mode: i32) { +pub extern "C" fn nondet_log_init() { + ffi_guard(clear_c_nondet_log, ()); +} + +/// `nondet_log_destroy` (`NonDetLog.c:148`): frees the log. +#[no_mangle] +pub extern "C" fn nondet_log_destroy() { + ffi_guard(clear_c_nondet_log, ()); +} + +fn clear_c_nondet_log() { + if let Err(e) = state::with_state(|s| { + s.c_nondet = Vec::new(); + Ok(()) + }) { + log_error(&e); + } +} + +/// Core entry point of `nondet_log_to_file` (`NonDetLog.c:50-125`) for the +/// IR of the KLEE generator (D7): writes the log of the run to `klee_log.csv` +/// in the current directory. Returns `TRUE` (1) or, when the file cannot be +/// written, `FALSE` (0). +/// +/// It is only the last step of `nondet_generate_aux_witness_files` +/// (`NonDetGeneratorKlee.c:57-62`). The IR must first skip the call when +/// `__m2c_rt_nondet_log_len()` is above 1024 (`MAP2CHECK_KLEE_LOG_MAX_ROWS`) +/// and pin every row with `__m2c_rt_nondet_log_get_i64` / +/// `klee_get_value_i64` / `klee_assume` / `__m2c_rt_nondet_log_set_i64` +/// (`:33-47`); the core has no KLEE intrinsics. +#[no_mangle] +pub extern "C" fn __m2c_rt_nondet_log_to_file() -> std::os::raw::c_char { ffi_guard( || { - let Some(mode) = analysis_mode_from_raw(mode) else { - eprintln!("[map2check] invalid analysis mode: {mode}"); - return; + let Some(dir) = termination::output_dir() else { + return 0; }; - state::update_config(|c| c.mode = Some(mode)); + match state::with_state(|s| { + Ok(crate::nondet::c_log::nondet_log_to_file(&s.c_nondet, &dir)) + }) { + Ok(written) => std::os::raw::c_char::from(written), + Err(e) => { + log_error(&e); + 0 + } + } + }, + 0, + ) +} + +/// Selects how the result files (`map2check_property*`, the witness logs, +/// `klee_log.csv`, `map2check_checked_error`) are written: `on` non-zero +/// through a temporary file and a rename (SD-20), zero truncated and written +/// in place as develop's `fopen("w")`. The KLEE generator module calls it +/// with 1 from its `nondet_init`, before anything is written: its forked +/// states share the host files. The drop-in core starts in plain mode, so +/// the AFL++ and `none` generators write as the C runtime; every other build +/// starts atomic. The user approved this scope of SD-20 on 2026-10-27. +/// The same switch turns on the never-downgrade rule (SD-20, user decision +/// of 2026-10-27): a `NONE` (or other non-violation) `map2check_property` +/// never replaces an existing one, so a KLEE state that ends without a +/// violation cannot erase the one another state wrote. +#[no_mangle] +pub extern "C" fn __m2c_rt_output_set_atomic(on: std::os::raw::c_int) { + ffi_guard( + || { + crate::output::legacy_files::set_atomic_writes(on != 0); + crate::output::legacy_files::set_never_downgrade(on != 0); }, (), ); } -fn analysis_mode_from_raw(raw: i32) -> Option { - match raw { - 0 => Some(AnalysisMode::None), - 1 => Some(AnalysisMode::Memtrack), - 2 => Some(AnalysisMode::Memcleanup), - 3 => Some(AnalysisMode::Overflow), - 4 => Some(AnalysisMode::Assert), - 5 => Some(AnalysisMode::Target), - _ => None, - } +/// Number of rows of the nondet log (`map2check_nondet_get_log().size`, +/// `NonDetGeneratorKlee.c:57-58`). +#[no_mangle] +pub extern "C" fn __m2c_rt_nondet_log_len() -> std::os::raw::c_uint { + ffi_guard( + || state::with_state(|s| Ok(s.c_nondet.len() as std::os::raw::c_uint)).unwrap_or(0), + 0, + ) } -/// Records that the target function was reached, without ending the process. -/// Same property as `map2check_target_function` of the `develop` C runtime -/// (`TARGET_REACHED`, `Map2CheckFunctions.c:61-65`), which then ends the run; -/// here the first violation stays final (SD-03) and the caller keeps running. -/// -/// # Safety -/// `func` must be null or a valid null-terminated C string. -pub(crate) unsafe fn mark_target_reached( - func: *const std::os::raw::c_char, - _scope: i32, - line: i32, -) { - // SAFETY: caller guarantees func is a valid C string or null. - let fname = unsafe { c_str(func) }; - record_violation(ViolatedProperty::Reachability, line as u32, fname, false); +/// The 8 bytes of the value union of row `index` as a `long long`, as +/// `memcpy(&symbolic, &call->value, sizeof(symbolic))` reads them +/// (`NonDetGeneratorKlee.c:39`): the `int` types zero-extended, the 64-bit +/// types and the doubles by bit pattern. 0 when `index` is out of range +/// (C stops at a NULL row, `:37-38`). +#[no_mangle] +pub extern "C" fn __m2c_rt_nondet_log_get_i64(index: std::os::raw::c_uint) -> i64 { + ffi_guard( + || { + state::with_state(|s| { + Ok(s.c_nondet + .get(index as usize) + .map_or(0, |c| c.value_bits() as i64)) + }) + .unwrap_or(0) + }, + 0, + ) } -/// Called by the target pass when the target function (e.g. `reach_error`) -/// is reached. Records `Reachability` (develop's `TARGET-REACHED`) at `line` -/// in `func`, in every mode, and returns: ending the run is Task 2.4 (SD-15). -/// `scope` is accepted for ABI compatibility and ignored, as in develop. -/// -/// # Safety -/// `func` must be null or a valid null-terminated C string. +/// Writes `value` over the 8 bytes of the value union of row `index`, as +/// `memcpy(&call->value, &concrete, sizeof(concrete))` does +/// (`NonDetGeneratorKlee.c:42`). Does nothing when `index` is out of range. #[no_mangle] -pub unsafe extern "C" fn map2check_reach_target( - func: *const std::os::raw::c_char, - scope: i32, - line: i32, -) { +pub extern "C" fn __m2c_rt_nondet_log_set_i64(index: std::os::raw::c_uint, value: i64) { ffi_guard( || { - // SAFETY: caller guarantees func is a valid C string or null. - unsafe { mark_target_reached(func, scope, line) } + if let Err(e) = state::with_state(|s| { + if let Some(c) = s.c_nondet.get_mut(index as usize) { + c.set_value_bits(value as u64); + } + Ok(()) + }) { + log_error(&e); + } }, (), ); @@ -433,7 +2652,6 @@ macro_rules! nondet_ffi { // `c_long` and friends are `i64`/`u64` on LP64 only: the widening // conversions in `$to_value` are no-ops there and real elsewhere. #[allow(clippy::useless_conversion)] - #[no_mangle] pub unsafe extern "C" fn $fn_name( line: i32, scope: i32, @@ -444,7 +2662,7 @@ macro_rules! nondet_ffi { ffi_guard( || { // SAFETY: caller guarantees function_name is a valid C string or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; let raw_value: $c_ty = if value.is_null() { $zero } else { @@ -621,7 +2839,6 @@ unsafe impl Send for bbtrack_log_t {} /// /// # Safety /// `function_name` must be null or a valid null-terminated C string. -#[no_mangle] pub unsafe extern "C" fn map2check_save_basic_block_log( line: i32, function_name: *const std::os::raw::c_char, @@ -629,7 +2846,7 @@ pub unsafe extern "C" fn map2check_save_basic_block_log( ffi_guard( || { // SAFETY: caller guarantees function_name is a valid C string or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; match state::with_state(|s| { let step = s.next_step(); s.bbtrack @@ -657,7 +2874,6 @@ pub unsafe extern "C" fn map2check_save_basic_block_log( } /// Returns 1 if `line` was recorded as executed, 0 otherwise. -#[no_mangle] pub extern "C" fn map2check_is_in_trackbb_container(line: i32) -> i32 { ffi_guard( || { @@ -678,7 +2894,6 @@ pub extern "C" fn map2check_is_in_trackbb_container(line: i32) -> i32 { /// # Safety /// `var_name` must be null or a valid null-terminated C string; `ptr_address` may be null. -#[no_mangle] pub unsafe extern "C" fn map2check_map_alloca( var_name: *const std::os::raw::c_char, ptr_address: *const std::os::raw::c_void, @@ -693,7 +2908,7 @@ pub unsafe extern "C" fn map2check_map_alloca( return; } // SAFETY: caller guarantees var_name is a valid C string or null. - let name = unsafe { c_str(var_name) }; + let name: &str = &unsafe { c_str(var_name) }; let addr = ptr_address as usize; if let Err(e) = state::with_state(|s| { let step = s.next_step(); @@ -733,7 +2948,6 @@ pub unsafe extern "C" fn map2check_map_alloca( /// /// # Safety /// `var_name` must be null or a valid null-terminated C string; `ptr_address` may be null. -#[no_mangle] pub unsafe extern "C" fn map2check_map_non_static_alloca( var_name: *const std::os::raw::c_char, ptr_address: *const std::os::raw::c_void, @@ -751,7 +2965,7 @@ pub unsafe extern "C" fn map2check_map_non_static_alloca( return; }; // SAFETY: caller guarantees var_name is a valid C string or null. - let name = unsafe { c_str(var_name) }; + let name: &str = &unsafe { c_str(var_name) }; let addr = ptr_address as usize; if let Err(e) = state::with_state(|s| { let step = s.next_step(); @@ -788,7 +3002,6 @@ pub unsafe extern "C" fn map2check_map_non_static_alloca( /// /// # Safety /// `var_name` must be null or a valid null-terminated C string; `ptr_address` may be null. -#[no_mangle] pub unsafe extern "C" fn map2check_map_funct_address( var_name: *const std::os::raw::c_char, ptr_address: *const std::os::raw::c_void, @@ -796,7 +3009,7 @@ pub unsafe extern "C" fn map2check_map_funct_address( ffi_guard( || { // SAFETY: caller guarantees var_name is a valid C string or null. - let name = unsafe { c_str(var_name) }; + let name: &str = &unsafe { c_str(var_name) }; let addr = ptr_address as usize; let pointer_width = std::mem::size_of::<*const std::os::raw::c_void>(); if let Err(e) = state::with_state(|s| { @@ -850,7 +3063,6 @@ fn malloc_returned_null(ptr_address: *const std::os::raw::c_void) -> bool { /// /// # Safety /// `ptr_address` may be null (null malloc result is valid C). -#[no_mangle] pub unsafe extern "C" fn map2check_map_malloc(ptr_address: *const std::os::raw::c_void, size: i32) { ffi_guard( || { @@ -894,7 +3106,6 @@ pub unsafe extern "C" fn map2check_map_malloc(ptr_address: *const std::os::raw:: /// /// # Safety /// `ptr_address` may be null. -#[no_mangle] pub unsafe extern "C" fn map2check_map_calloc( ptr_address: *const std::os::raw::c_void, quantity: i32, @@ -942,7 +3153,6 @@ pub unsafe extern "C" fn map2check_map_calloc( /// /// # Safety /// `ptr_address` must be null or point to a readable `void *`. -#[no_mangle] pub unsafe extern "C" fn map2check_map_posix_memalign( ptr_address: *const std::os::raw::c_void, size: i32, @@ -990,7 +3200,6 @@ pub unsafe extern "C" fn map2check_map_posix_memalign( /// # Safety /// `var_name` and `function_name` must be null or valid null-terminated C strings. /// `var_address` is only stored, never dereferenced. -#[no_mangle] pub unsafe extern "C" fn map2check_map_free( var_name: *const std::os::raw::c_char, var_address: *const std::os::raw::c_void, @@ -1002,8 +3211,8 @@ pub unsafe extern "C" fn map2check_map_free( ffi_guard( || { // SAFETY: caller guarantees string pointers are valid or null. - let name = unsafe { c_str(var_name) }; - let fname = unsafe { c_str(function_name) }; + let name: &str = &unsafe { c_str(var_name) }; + let fname: &str = &unsafe { c_str(function_name) }; let addr = ptr_address as usize; if let Err(e) = state::with_state(|s| { let step = s.next_step(); @@ -1036,7 +3245,6 @@ pub unsafe extern "C" fn map2check_map_free( /// # Safety /// `var_name` and `funct_name` must be null or valid null-terminated C strings. -#[no_mangle] pub unsafe extern "C" fn map2check_map_store_pointer( var_address: *const std::os::raw::c_void, value: *const std::os::raw::c_void, @@ -1048,8 +3256,8 @@ pub unsafe extern "C" fn map2check_map_store_pointer( ffi_guard( || { // SAFETY: caller guarantees string pointers are valid or null. - let name = unsafe { c_str(var_name) }; - let fname = unsafe { c_str(funct_name) }; + let name: &str = &unsafe { c_str(var_name) }; + let fname: &str = &unsafe { c_str(funct_name) }; let var_addr = var_address as usize; let points_to = value as usize; if let Err(e) = state::with_state(|s| { @@ -1086,7 +3294,6 @@ pub unsafe extern "C" fn map2check_map_store_pointer( /// /// # Safety /// `function_name` must be null or a valid null-terminated C string. -#[no_mangle] pub unsafe extern "C" fn map2check_is_valid_assert( line_number: i32, function_name: *const std::os::raw::c_char, @@ -1098,7 +3305,7 @@ pub unsafe extern "C" fn map2check_is_valid_assert( return; } // SAFETY: caller guarantees function_name is a valid C string or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; let ctx = VccContext::new(line_number as u32, 0, fname); let checker = AssertChecker { expression }; match checker.check(&ctx) { @@ -1122,7 +3329,6 @@ macro_rules! overflow_ffi_binop { ($fn_name:ident, $checker:ident, $ty:ty) => { /// # Safety /// `function_name` must be null or a valid null-terminated C string. - #[no_mangle] pub unsafe extern "C" fn $fn_name( param1: $ty, param2: $ty, @@ -1136,7 +3342,7 @@ macro_rules! overflow_ffi_binop { return; } // SAFETY: caller guarantees function_name is valid or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; let ctx = VccContext::new(line, scope, fname); run_vcc_check( $checker { @@ -1158,7 +3364,6 @@ macro_rules! overflow_ffi_shift { ($fn_name:ident, $checker:ident, $ty:ty) => { /// # Safety /// `function_name` must be null or a valid null-terminated C string. - #[no_mangle] pub unsafe extern "C" fn $fn_name( param1: $ty, param2: u32, @@ -1172,7 +3377,7 @@ macro_rules! overflow_ffi_shift { return; } // SAFETY: caller guarantees function_name is valid or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; let ctx = VccContext::new(line, scope, fname); run_vcc_check( $checker { @@ -1192,7 +3397,6 @@ macro_rules! overflow_ffi_shift { /// # Safety /// `function_name` must be null or a valid null-terminated C string. -#[no_mangle] pub unsafe extern "C" fn map2check_binop_neg_int( param1: i32, _param2: i32, @@ -1206,7 +3410,7 @@ pub unsafe extern "C" fn map2check_binop_neg_int( return; } // SAFETY: caller guarantees function_name is valid or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; let ctx = VccContext::new(line, scope, fname); run_vcc_check(NegI32 { val: param1 }, &ctx, line, fname); }, @@ -1247,7 +3451,7 @@ unsafe fn signed_binop( return; } // SAFETY: caller guarantees function_name is valid or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; let ctx = VccContext::new(line, 0, fname); let checker = SignedBinop { op, @@ -1264,7 +3468,6 @@ unsafe fn signed_binop( /// /// # Safety /// `function_name` must be null or a valid null-terminated C string. -#[no_mangle] pub unsafe extern "C" fn map2check_binop_add_signed( a: i64, b: i64, @@ -1285,7 +3488,6 @@ pub unsafe extern "C" fn map2check_binop_add_signed( /// /// # Safety /// `function_name` must be null or a valid null-terminated C string. -#[no_mangle] pub unsafe extern "C" fn map2check_binop_sub_signed( a: i64, b: i64, @@ -1306,7 +3508,6 @@ pub unsafe extern "C" fn map2check_binop_sub_signed( /// /// # Safety /// `function_name` must be null or a valid null-terminated C string. -#[no_mangle] pub unsafe extern "C" fn map2check_binop_mul_signed( a: i64, b: i64, @@ -1327,7 +3528,6 @@ pub unsafe extern "C" fn map2check_binop_mul_signed( /// /// # Safety /// `function_name` must be null or a valid null-terminated C string. -#[no_mangle] pub unsafe extern "C" fn map2check_binop_div_signed( a: i64, b: i64, @@ -1348,7 +3548,6 @@ pub unsafe extern "C" fn map2check_binop_div_signed( /// /// # Safety /// `function_name` must be null or a valid null-terminated C string. -#[no_mangle] pub unsafe extern "C" fn map2check_binop_rem_signed( a: i64, b: i64, @@ -1369,7 +3568,6 @@ pub unsafe extern "C" fn map2check_binop_rem_signed( /// /// # Safety /// `function_name` must be null or a valid null-terminated C string; `ptr` may be null. -#[no_mangle] pub unsafe extern "C" fn map2check_check_load( ptr: *const std::os::raw::c_void, line: i32, @@ -1383,7 +3581,7 @@ pub unsafe extern "C" fn map2check_check_load( return; } // SAFETY: caller guarantees function_name is valid or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; if size < 0 { record_violation(ViolatedProperty::MemsafetyDeref, line as u32, fname, false); return; @@ -1416,7 +3614,6 @@ pub unsafe extern "C" fn map2check_check_load( /// # Safety /// `name` and `function_name` must be null or valid null-terminated C strings; /// `ptr` and `var_address` may be null and are never dereferenced. -#[no_mangle] pub unsafe extern "C" fn map2check_check_free( name: *const std::os::raw::c_char, var_address: *const std::os::raw::c_void, @@ -1431,7 +3628,7 @@ pub unsafe extern "C" fn map2check_check_free( return; } // SAFETY: caller guarantees string pointers are valid or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; let ctx = VccContext::new(line, 0, fname); let checker = FreeChecker { address: ptr as usize, @@ -1439,7 +3636,7 @@ pub unsafe extern "C" fn map2check_check_free( match checker.check(&ctx) { Ok(crate::analysismode::VccOutcome::Violated { property }) => { // SAFETY: as above. - let var_name = unsafe { c_str(name) }; + let var_name: &str = &unsafe { c_str(name) }; if let Err(e) = state::with_state(|s| { if s.result.ok { let addr = ptr as usize; @@ -1480,7 +3677,6 @@ pub unsafe extern "C" fn map2check_check_free( /// /// # Safety /// `function_name` must be null or a valid null-terminated C string; `ptr` may be null. -#[no_mangle] pub unsafe extern "C" fn map2check_check_free_resolved_address( ptr: *const std::os::raw::c_void, line: u32, @@ -1490,7 +3686,7 @@ pub unsafe extern "C" fn map2check_check_free_resolved_address( ffi_guard( || { // SAFETY: caller guarantees function_name is valid or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; if ptr.is_null() && is_null_valid != 0 { return; } @@ -1530,7 +3726,6 @@ pub unsafe extern "C" fn map2check_check_free_resolved_address( /// /// # Safety /// `function_name` must be null or a valid null-terminated C string; `ptr` may be null. -#[no_mangle] pub unsafe extern "C" fn map2check_check_deref( ptr: *const std::os::raw::c_void, scope: u32, @@ -1543,7 +3738,7 @@ pub unsafe extern "C" fn map2check_check_deref( return; } // SAFETY: caller guarantees function_name is valid or null. - let fname = unsafe { c_str(function_name) }; + let fname: &str = &unsafe { c_str(function_name) }; let ctx = VccContext::new(line, scope, fname); run_vcc_check( DerefChecker { @@ -1564,7 +3759,6 @@ pub unsafe extern "C" fn map2check_check_deref( /// `map2check_set_memcleanup()`; memcleanup mode reports it as /// `MemsafetyMemcleanup`; the other modes report nothing (SD-04). A violation /// recorded earlier stays final (SD-03). -#[no_mangle] pub extern "C" fn map2check_check_mem_endprog() { ffi_guard( || { diff --git a/rust/src/ffi_golden_tests.rs b/rust/src/ffi_golden_tests.rs index 344b015..faf1b0b 100644 --- a/rust/src/ffi_golden_tests.rs +++ b/rust/src/ffi_golden_tests.rs @@ -13,7 +13,7 @@ // variable a test build writes no files and never aborts // (`output::termination::output_dir`). -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; use std::os::raw::{c_char, c_long, c_void}; use std::os::unix::process::ExitStatusExt; use std::path::Path; @@ -72,12 +72,18 @@ fn is_child() -> bool { /// Runs `ffi::golden_tests::` in a child process whose output /// directory is `dir`. fn run_child(name: &str, dir: &Path) -> std::process::Output { + run_child_with(name, dir, &[]) +} + +/// [`run_child`] with extra environment variables for the child. +fn run_child_with(name: &str, dir: &Path, envs: &[(&str, &str)]) -> std::process::Output { let exe = std::env::current_exe().unwrap(); let output = std::process::Command::new(exe) .args(["--exact", &format!("ffi::golden_tests::{name}")]) .args(["--nocapture", "--test-threads=1"]) .env(CHILD_ENV, "1") .env(crate::output::termination::OUTPUT_DIR_ENV, dir) + .envs(envs.iter().copied()) .output() .unwrap(); let stdout = String::from_utf8_lossy(&output.stdout); @@ -123,6 +129,12 @@ fn assert_quiet_stdout(stdout: &str, aborted: bool) { /// Runs the `` scenario and compares its files and its end with /// `tests/legacy_golden//`. fn assert_matches_golden(tag: &str, child: &str) { + assert_matches_golden_except(tag, child, &[]); +} + +/// [`assert_matches_golden`], except that the content of the files named in +/// `skip_content` is not compared (they must still exist). +fn assert_matches_golden_except(tag: &str, child: &str, skip_content: &[&str]) { let golden = Path::new(env!("CARGO_MANIFEST_DIR")) .join("tests/legacy_golden") .join(tag); @@ -154,6 +166,9 @@ fn assert_matches_golden(tag: &str, child: &str) { "{tag}: conjunto de arquivos" ); for name in files_in(&golden) { + if skip_content.contains(&name.as_str()) { + continue; + } let expected = std::fs::read(golden.join(&name)).unwrap(); let actual = std::fs::read(dir.path().join(&name)).unwrap(); assert_eq!( @@ -175,9 +190,9 @@ fn tracking() { let mut double_value: f64 = 2.5; // SAFETY: the values outlive the calls; the names are C-string literals. unsafe { - map2check_save_basic_block_log(3, c"main".as_ptr()); - map2check_save_basic_block_log(4, c"main".as_ptr()); - map2check_save_basic_block_log(3, c"main".as_ptr()); + map2check_track_bb(3, c"main".as_ptr()); + map2check_track_bb(4, c"main".as_ptr()); + map2check_track_bb(3, c"main".as_ptr()); map2check_save_nondet_log_int(5, 0, 0, &mut int_value, c"main".as_ptr()); map2check_save_nondet_log_uint(6, 0, 13, &mut uint_value, c"main".as_ptr()); map2check_save_nondet_log_char(7, 0, 1, &mut char_value, c"main".as_ptr()); @@ -237,7 +252,7 @@ fn start(mode: AnalysisMode) { println!("{STARTED}"); map2check_set_mode(mode as i32); map2check_enable_witness(); - map2check_init(); + map2check_init(0); tracking(); } @@ -494,7 +509,7 @@ fn overflow_without_witness_child() { if is_child() { println!("{STARTED}"); map2check_set_mode(AnalysisMode::Overflow as i32); - map2check_init(); + map2check_init(0); tracking(); // SAFETY: C-string literal is valid. unsafe { map2check_binop_add_int(i32::MAX, 1, 20, 0, c"main".as_ptr()) }; @@ -522,7 +537,7 @@ fn success_without_witness_child() { if is_child() { println!("{STARTED}"); map2check_set_mode(AnalysisMode::Memtrack as i32); - map2check_init(); + map2check_init(0); tracking(); memory(); free_p(); @@ -549,28 +564,44 @@ fn without_witness_success_writes_none_and_returns() { // ── Stale property files and the unset mode ───────────────────────────────── +/// develop's `map2check_init` touches no file (`Map2CheckFunctions.c:29-40`) +/// and `write_property` truncates only the files of the property it writes +/// (`PropertyGenerator.c:50-106`): property files of an earlier run in the +/// same directory stay, and `map2check_property` is replaced. #[test] -fn init_with_mode_removes_stale_property_files_child() { +fn property_files_of_an_earlier_run_are_kept_as_c_does_child() { if is_child() { println!("{STARTED}"); map2check_set_mode(AnalysisMode::Assert as i32); - map2check_init(); + map2check_init(0); + map2check_success(); + println!("{RETURNED}"); } } #[test] -fn init_with_mode_removes_stale_property_files() { +fn property_files_of_an_earlier_run_are_kept_as_c_does() { let dir = tempfile::tempdir().unwrap(); for name in crate::output::legacy_files::PROPERTY_FILES { std::fs::write(dir.path().join(name), "stale\n").unwrap(); } - std::fs::write(dir.path().join("other"), "kept\n").unwrap(); let output = run_child( - "init_with_mode_removes_stale_property_files_child", + "property_files_of_an_earlier_run_are_kept_as_c_does_child", dir.path(), ); assert_eq!(output.status.code(), Some(0)); - assert_eq!(files_in(dir.path()), BTreeSet::from(["other".to_owned()])); + for name in crate::output::legacy_files::PROPERTY_FILES { + let expected: &[u8] = if name == "map2check_property" { + b"NONE\n" + } else { + b"stale\n" + }; + assert_eq!( + std::fs::read(dir.path().join(name)).unwrap(), + expected, + "{name}" + ); + } } #[test] @@ -578,7 +609,7 @@ fn unset_mode_records_without_files_or_abort_child() { if is_child() { println!("{STARTED}"); map2check_enable_witness(); - map2check_init(); + map2check_init(0); // SAFETY: C-string literal is valid. unsafe { map2check_binop_add_int(i32::MAX, 1, 20, 0, c"main".as_ptr()) }; assert_eq!( @@ -613,7 +644,7 @@ fn assert_violation_without_witness_child() { if is_child() { println!("{STARTED}"); map2check_set_mode(AnalysisMode::Assert as i32); - map2check_init(); + map2check_init(0); print_all_containers_as_json(); // SAFETY: C-string literal is valid. unsafe { map2check_is_valid_assert(21, c"main".as_ptr(), 0) }; @@ -647,7 +678,7 @@ fn poisoned_state_at_success_child() { println!("{STARTED}"); map2check_set_mode(AnalysisMode::Memtrack as i32); map2check_enable_witness(); - map2check_init(); + map2check_init(0); let _ = std::thread::spawn(|| { let _held = crate::state::get().unwrap().lock().unwrap(); panic!("poison the analysis state on purpose"); @@ -674,3 +705,1459 @@ fn poisoned_state_at_success_reports_unknown_and_aborts() { ]) ); } + +// ── C ABI of Map2CheckFunctions, TrackBBLog, PropertyGenerator and the +// witness (Task 2.1) ───────────────────────────────────────────────────── + +/// `drivers/TARGET-REACHED.c` with the C entry point the target pass emits, +/// `map2check_target_function` (`Map2CheckFunctions.c:61-65`). +#[test] +fn target_function_child() { + if is_child() { + start(AnalysisMode::Target); + // SAFETY: C-string literal is valid. + unsafe { map2check_target_function(c"reach_error".as_ptr(), 0, 22) }; + map2check_success(); + } +} + +#[test] +fn target_function_matches_c_runtime() { + assert_matches_golden("TARGET-REACHED", "target_function_child"); +} + +/// `drivers/FALSE-MEMTRACK.c` ending with `map2check_exit` instead of +/// `map2check_success`: with no error the verdict runs (`:89-92`), and +/// memtrack's `analysis_is_program_correct` writes FALSE-MEMTRACK +/// (`AnalysisModeMemtrack.c:138-146`). `map2check_success` only writes +/// `NONE` first, which that verdict replaces, so the files are the golden's. +#[test] +fn map2check_exit_child() { + if is_child() { + start(AnalysisMode::Memtrack); + memory(); + map2check_exit(); + } +} + +#[test] +fn map2check_exit_matches_c_runtime() { + assert_matches_golden("FALSE-MEMTRACK", "map2check_exit_child"); +} + +/// `map2check_exit` of a correct run writes no property file (only +/// `map2check_success` writes `NONE`), writes the aux files and returns. +#[test] +fn map2check_exit_without_error_returns_child() { + if is_child() { + start(AnalysisMode::Assert); + map2check_exit(); + println!("{RETURNED}"); + } +} + +#[test] +fn map2check_exit_without_error_returns() { + let dir = tempfile::tempdir().unwrap(); + let output = run_child("map2check_exit_without_error_returns_child", dir.path()); + assert_eq!(output.status.code(), Some(0)); + assert_quiet_stdout(&String::from_utf8_lossy(&output.stdout), false); + assert_eq!( + files_in(dir.path()), + BTreeSet::from(["track_bb_log.st".to_owned()]) + ); +} + +/// `map2check_error` (`:72-75`): `gotError`, then `map2check_exit` skips the +/// verdict, writes the aux files with the nondet log (a violation), the +/// confirmation file, and aborts. No property file: the caller writes it. +#[test] +fn map2check_error_child() { + if is_child() { + start(AnalysisMode::Assert); + map2check_error(); + println!("map2check-golden-child-survived"); + } +} + +#[test] +fn map2check_error_writes_the_witness_files_and_aborts() { + let golden = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/legacy_golden/ASSERT"); + let dir = tempfile::tempdir().unwrap(); + let output = run_child("map2check_error_child", dir.path()); + assert_eq!(output.status.signal(), Some(6), "esperava SIGABRT"); + assert_quiet_stdout(&String::from_utf8_lossy(&output.stdout), true); + assert_eq!( + files_in(dir.path()), + BTreeSet::from([ + "klee_log.csv".to_owned(), + "map2check_checked_error".to_owned(), + "track_bb_log.st".to_owned(), + ]) + ); + for name in files_in(dir.path()) { + assert_eq!( + std::fs::read(dir.path().join(&name)).unwrap(), + std::fs::read(golden.join(&name)).unwrap(), + "{name}" + ); + } +} + +#[test] +fn property_file_child() { + if is_child() { + println!("{STARTED}"); + let value: i32 = std::env::var("M2C_PROPERTY").unwrap().parse().unwrap(); + let line: i32 = std::env::var("M2C_LINE").unwrap().parse().unwrap(); + // No mode, no init: `write_property` writes whatever the state. The + // value is passed as the C `int` of `enum ViolatedProperty`, in or + // out of range. + // SAFETY: C-string literal is valid. + unsafe { write_property(value, line, c"main".as_ptr()) }; + println!("{RETURNED}"); + } +} + +/// The files a scenario must leave, by name, with their contents. +type ExpectedFiles = Vec<(&'static str, String)>; + +/// `write_property(prp, line, "main")` writes exactly what +/// `PropertyGenerator.c` writes for the same call, byte for byte: the +/// `fprintf` format strings of `:50-106` for `map2check_property` and of the +/// `write_property_` helper each case calls (`:24-48`, `:108-130`) for +/// `map2check_property_`, with `Line: %d` and `Function: %s`. A value +/// outside the C switch (`Concurrency`, 10, or any `int` that is no +/// enumerator, such as 11, -1 or `INT_MAX`) leaves `map2check_property` +/// opened with `"w"` and closed: an empty file. +#[test] +fn property_file_matches_c_format() { + let located = |tag: &str, line: i32| format!("{tag}\nLine: {line}\nFunction: main\n"); + let cases: Vec<(i32, i32, ExpectedFiles)> = vec![ + // :55-57 + (0, 42, vec![("map2check_property", "NONE\n".into())]), + // :58-60 + (1, 42, vec![("map2check_property", "UNKNOWN\n".into())]), + // :61-66 and write_property_free :108-114 + ( + 2, + 42, + vec![ + ("map2check_property", located("FALSE-FREE", 42)), + ("map2check_property_free", located("FALSE-FREE", 42)), + ], + ), + // :67-72 and write_property_deref :42-48 + ( + 3, + -1, + vec![ + ("map2check_property", located("FALSE-DEREF", -1)), + ("map2check_property_deref", located("FALSE-DEREF", -1)), + ], + ), + // :73-76 and write_property_memtrack :30-34 + ( + 4, + 42, + vec![ + ("map2check_property", "FALSE-MEMTRACK\n".into()), + ("map2check_property_memtrack", "FALSE-MEMTRACK\n".into()), + ], + ), + // :81-85 + ( + 5, + 42, + vec![("map2check_property", located("TARGET-REACHED", 42))], + ), + // :86-91 and write_property_overflow :116-122 + ( + 6, + 42, + vec![ + ("map2check_property", located("OVERFLOW", 42)), + ("map2check_property_overflow", located("OVERFLOW", 42)), + ], + ), + // :92-97 and write_property_assert :124-130 + ( + 7, + 42, + vec![ + ("map2check_property", located("ASSERT", 42)), + ("map2check_property_assert", located("ASSERT", 42)), + ], + ), + // :77-80 and write_property_memcleanup :36-40 + ( + 8, + 42, + vec![ + ("map2check_property", "FALSE-MEMCLEANUP\n".into()), + ("map2check_property_memcleanup", "FALSE-MEMCLEANUP\n".into()), + ], + ), + // :98-102 + ( + 9, + 42, + vec![("map2check_property", located("FALSE-DIVBYZERO", 42))], + ), + // no case of the switch + (10, 42, vec![("map2check_property", String::new())]), + // out of the enum: no case of the switch either, and defined + // behaviour (the export takes the `int`) + (11, 42, vec![("map2check_property", String::new())]), + (-1, 42, vec![("map2check_property", String::new())]), + (i32::MAX, 42, vec![("map2check_property", String::new())]), + ]; + for (value, line, expected) in cases { + let dir = tempfile::tempdir().unwrap(); + let (value_s, line_s) = (value.to_string(), line.to_string()); + let output = run_child_with( + "property_file_child", + dir.path(), + &[("M2C_PROPERTY", &value_s), ("M2C_LINE", &line_s)], + ); + assert_eq!(output.status.code(), Some(0), "valor {value}"); + assert_eq!( + files_in(dir.path()), + expected + .iter() + .map(|(n, _)| (*n).to_owned()) + .collect::>(), + "valor {value}" + ); + for (name, text) in expected { + assert_eq!( + std::fs::read(dir.path().join(name)).unwrap(), + text.as_bytes(), + "valor {value}: {name}" + ); + } + } +} + +#[test] +fn property_file_non_utf8_child() { + if is_child() { + println!("{STARTED}"); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { write_property(7, 3, c"f\xffo".as_ptr()) }; + println!("{RETURNED}"); + } +} + +/// A function name that is not valid UTF-8 still reaches the property +/// files (develop prints it with `%s`); the invalid byte becomes U+FFFD +/// instead of the whole name becoming empty. +#[test] +fn property_file_keeps_a_non_utf8_function_name() { + let dir = tempfile::tempdir().unwrap(); + let output = run_child("property_file_non_utf8_child", dir.path()); + assert_eq!(output.status.code(), Some(0)); + let expected = "ASSERT\nLine: 3\nFunction: f\u{FFFD}o\n".as_bytes(); + for name in ["map2check_property", "map2check_property_assert"] { + assert_eq!( + std::fs::read(dir.path().join(name)).unwrap(), + expected, + "{name}" + ); + } +} + +#[test] +fn track_bb_log_child() { + if is_child() { + println!("{STARTED}"); + map2check_init(0); + // SAFETY: C-string literals are valid; a NULL log pointer is + // accepted (D7: the core reads its own log). + unsafe { + map2check_track_bb(3, c"main".as_ptr()); + map2check_track_bb(4, c"main".as_ptr()); + map2check_track_bb(3, c"other".as_ptr()); + map2check_track_bb(u32::MAX, c"f".as_ptr()); + __m2c_rt_witness_generate_aux_files(std::ptr::null_mut(), 0); + } + println!("{RETURNED}"); + } +} + +/// `track_bb_log.st` as `trackbb_log_to_file` writes it +/// (`TrackBBLog.c:35-44`): one row per tracked line, in order, printed with +/// `"%u;"` then `"%s\n"` (`:40-41`); `map2check_track_bb` tracks a line once +/// (`Map2CheckFunctions.c:49-55`, `is_in_tracked` `TrackBBLog.c:23-33`). +#[test] +fn track_bb_log_matches_c_format() { + let dir = tempfile::tempdir().unwrap(); + let output = run_child("track_bb_log_child", dir.path()); + assert_eq!(output.status.code(), Some(0)); + // No mode: no memory logs; no violation: no nondet log + // (`WitnessGeneration.c:15-29`). + assert_eq!( + files_in(dir.path()), + BTreeSet::from(["track_bb_log.st".to_owned()]) + ); + assert_eq!( + std::fs::read(dir.path().join("track_bb_log.st")).unwrap(), + b"3;main\n4;main\n4294967295;f\n" + ); +} + +#[test] +fn generate_aux_files_child() { + if is_child() { + println!("{STARTED}"); + map2check_set_mode(AnalysisMode::Memtrack as i32); + map2check_init(0); + tracking(); + memory(); + let violation: i8 = std::env::var("M2C_VIOLATION").unwrap().parse().unwrap(); + // SAFETY: a NULL log pointer is accepted (D7). + unsafe { __m2c_rt_witness_generate_aux_files(std::ptr::null_mut(), violation) }; + println!("{RETURNED}"); + } +} + +/// Extra environment variables of a child. +type Envs = &'static [(&'static str, &'static str)]; + +/// `generate_aux_files` (`WitnessGeneration.c:15-29`): the block log, the +/// logs of the mode (memtrack: `AnalysisModeMemtrack.c:148-152`) and the +/// nondet log only for a violation or when `MAP2CHECK_SEED_REPLAY` is set. +/// It writes whether or not `map2check_enable_witness` was called: the +/// IR module `WitnessGeneration.bc` calls it only in witness builds (D7). +#[test] +fn generate_aux_files_writes_the_nondet_log_on_violation_or_seed_replay() { + let base = [ + "allocation_log.csv", + "heap_log.csv", + "list_log.csv", + "track_bb_log.st", + ]; + let cases: [(&str, Envs, bool); 3] = [ + ("0", &[], false), + ("1", &[], true), + ("0", &[("MAP2CHECK_SEED_REPLAY", "")], true), + ]; + for (violation, extra, nondet) in cases { + let dir = tempfile::tempdir().unwrap(); + let mut envs = vec![("M2C_VIOLATION", violation)]; + envs.extend_from_slice(extra); + let output = run_child_with("generate_aux_files_child", dir.path(), &envs); + assert_eq!(output.status.code(), Some(0)); + let mut expected: BTreeSet = base.iter().map(|n| (*n).to_owned()).collect(); + if nondet { + expected.insert("klee_log.csv".to_owned()); + } + assert_eq!(files_in(dir.path()), expected, "{violation} {extra:?}"); + } + // The bytes are develop's: the logs of `drivers/FALSE-MEMTRACK.c` (the + // same calls) before its end-of-program verdict, which only appends the + // release row `0x10000100;0;1` to the allocation log + // (`AllocationLog.c:69-101`); `allocation_log_to_file` prints + // `"%p;" "%u;" "%d\n"` (`:169-171`). + let dir = tempfile::tempdir().unwrap(); + run_child_with( + "generate_aux_files_child", + dir.path(), + &[("M2C_VIOLATION", "0")], + ); + let golden = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/legacy_golden/FALSE-MEMTRACK"); + for name in ["heap_log.csv", "list_log.csv", "track_bb_log.st"] { + assert_eq!( + std::fs::read(dir.path().join(name)).unwrap(), + std::fs::read(golden.join(name)).unwrap(), + "{name}" + ); + } + assert_eq!( + std::fs::read(dir.path().join("allocation_log.csv")).unwrap(), + b"0x10000100;16;0\n" + ); +} + +#[test] +fn generate_confirmation_file_child() { + if is_child() { + println!("{STARTED}"); + __m2c_rt_witness_generate_confirmation_file(); + println!("{RETURNED}"); + } +} + +/// `generate_confirmation_file` (`WitnessGeneration.c:32-36`). +#[test] +fn generate_confirmation_file_matches_c_format() { + let dir = tempfile::tempdir().unwrap(); + let output = run_child("generate_confirmation_file_child", dir.path()); + assert_eq!(output.status.code(), Some(0)); + assert_eq!( + files_in(dir.path()), + BTreeSet::from(["map2check_checked_error".to_owned()]) + ); + assert_eq!( + std::fs::read(dir.path().join("map2check_checked_error")).unwrap(), + b"END OF TIME\n" + ); +} + +// ── AnalysisModeOverflow through the core entry points (Task 2.3, D7) ─────── +// +// The IR module `AnalysisModeOverflow.bc` forwards the C names to these +// entry points. The C drivers `drivers/OVERFLOW.c` and `drivers/DIVBYZERO.c` +// call develop's `map2check_binop_add` / `map2check_binop_sdiv` with exactly +// these arguments, so the C goldens apply unchanged. + +/// Starts a run as the drop-in runtime does: `map2check_init` (which in C +/// calls `analysis_init`, `Map2CheckFunctions.c:29-40`), then the overflow +/// module's `analysis_init`, which selects the mode. +fn start_overflow_rt(witness: bool) { + println!("{STARTED}"); + if witness { + map2check_enable_witness(); + } + map2check_init(0); + __m2c_rt_overflow_analysis_init(); +} + +#[test] +fn overflow_rt_child() { + if is_child() { + start_overflow_rt(true); + tracking(); + // SAFETY: C-string literal is valid. + unsafe { + __m2c_rt_overflow_map2check_binop_add( + i64::from(i32::MAX), + 1, + 32, + 20, + 0, + c"main".as_ptr(), + ) + }; + map2check_success(); + } +} + +#[test] +fn overflow_rt_matches_c_runtime() { + assert_matches_golden("OVERFLOW", "overflow_rt_child"); +} + +#[test] +fn div_by_zero_rt_child() { + if is_child() { + start_overflow_rt(true); + tracking(); + // SAFETY: C-string literal is valid. + unsafe { __m2c_rt_overflow_map2check_binop_sdiv(7, 0, 32, 23, 0, c"main".as_ptr()) }; + map2check_success(); + } +} + +#[test] +fn div_by_zero_rt_matches_c_runtime() { + assert_matches_golden("DIVBYZERO", "div_by_zero_rt_child"); +} + +/// One boundary operation per child, without witness. `OVERFLOW_RT_CASE` +/// selects it. +#[test] +fn overflow_rt_boundary_child() { + if is_child() { + start_overflow_rt(false); + let case = std::env::var("OVERFLOW_RT_CASE").unwrap(); + let main = c"main".as_ptr(); + // SAFETY: C-string literals are valid. + unsafe { + match case.as_str() { + "INT_MAX + 1" => { + __m2c_rt_overflow_map2check_binop_add(i64::from(i32::MAX), 1, 32, 30, 0, main) + } + "INT_MIN - 1" => { + __m2c_rt_overflow_map2check_binop_sub(i64::from(i32::MIN), 1, 32, 31, 0, main) + } + "INT_MIN / -1" => { + __m2c_rt_overflow_map2check_binop_sdiv(i64::from(i32::MIN), -1, 32, 32, 0, main) + } + "INT_MIN % -1" => { + __m2c_rt_overflow_map2check_binop_srem(i64::from(i32::MIN), -1, 32, 33, 0, main) + } + "x % 0" => __m2c_rt_overflow_map2check_binop_srem(5, 0, 32, 34, 0, main), + "INT_MAX * 2" => { + __m2c_rt_overflow_map2check_binop_mul(i64::from(i32::MAX), 2, 32, 35, 0, main) + } + "overflowError" => __m2c_rt_overflow_overflowError(36, c"f".as_ptr()), + "UINT_MAX + 1" => __m2c_rt_overflow_map2check_binop_add_uint( + u64::from(u32::MAX), + 1, + 32, + 37, + 0, + main, + ), + "0 - 1u" => __m2c_rt_overflow_map2check_binop_sub_uint(0, 1, 32, 38, 0, main), + "UINT_MAX * 2" => __m2c_rt_overflow_map2check_binop_mul_uint( + u64::from(u32::MAX), + 2, + 32, + 39, + 0, + main, + ), + other => panic!("caso desconhecido {other}"), + } + } + println!("map2check-golden-child-survived"); + map2check_success(); + println!("{RETURNED}"); + } +} + +/// The property text of the C runtime for `prp` at `line` in `function`: +/// `write_property` (`PropertyGenerator.c:86-91` for FALSE_OVERFLOW, +/// `:98-102` for FALSE_DIVBYZERO) and `write_property_overflow` (`:116-122`). +fn c_property_text(tag: &str, line: u32, function: &str) -> String { + format!("{tag}\nLine: {line}\nFunction: {function}\n") +} + +#[test] +fn overflow_rt_boundaries_write_the_c_property_text_and_abort() { + let overflow = |line, function| { + let text = c_property_text("OVERFLOW", line, function); + BTreeMap::from([ + ("map2check_property".to_owned(), text.clone()), + ("map2check_property_overflow".to_owned(), text), + ]) + }; + let cases = [ + ("INT_MAX + 1", overflow(30, "main")), + ("INT_MIN - 1", overflow(31, "main")), + ("INT_MIN / -1", overflow(32, "main")), + ("INT_MIN % -1", overflow(33, "main")), + ( + "x % 0", + // FALSE_DIVBYZERO writes only map2check_property (:98-102). + BTreeMap::from([( + "map2check_property".to_owned(), + c_property_text("FALSE-DIVBYZERO", 34, "main"), + )]), + ), + ("INT_MAX * 2", overflow(35, "main")), + ("overflowError", overflow(36, "f")), + ]; + for (case, expected) in cases { + let dir = tempfile::tempdir().unwrap(); + let output = run_child_with( + "overflow_rt_boundary_child", + dir.path(), + &[("OVERFLOW_RT_CASE", case)], + ); + let stdout = String::from_utf8_lossy(&output.stdout); + // map2check_error -> map2check_exit -> abort (Map2CheckFunctions.c:72-102). + assert_eq!(output.status.signal(), Some(6), "{case}: SIGABRT\n{stdout}"); + assert!(!stdout.contains("survived"), "{case}: o processo continuou"); + let actual: BTreeMap = files_in(dir.path()) + .into_iter() + .map(|name| { + let text = std::fs::read_to_string(dir.path().join(&name)).unwrap(); + (name, text) + }) + .collect(); + assert_eq!(actual, expected, "{case}"); + } +} + +#[test] +fn overflow_rt_unsigned_wraparound_is_not_reported_like_c() { + // AnalysisModeOverflow.c:219-248: the _uint binops are no-ops, so the + // run goes on to map2check_success, which writes NONE and returns. + for case in ["UINT_MAX + 1", "0 - 1u", "UINT_MAX * 2"] { + let dir = tempfile::tempdir().unwrap(); + let output = run_child_with( + "overflow_rt_boundary_child", + dir.path(), + &[("OVERFLOW_RT_CASE", case)], + ); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.code(), Some(0), "{case}\n{stdout}"); + assert!(stdout.contains(RETURNED), "{case}"); + assert_eq!( + files_in(dir.path()), + BTreeSet::from(["map2check_property".to_owned()]), + "{case}" + ); + assert_eq!( + std::fs::read(dir.path().join("map2check_property")).unwrap(), + b"NONE\n", + "{case}" + ); + } +} + +// ── AnalysisModeAssert and AnalysisModeNone through the core entry points +// (Task 2.4, D7) ───────────────────────────────────────────────────────── +// +// `AnalysisModeAssert.bc` / `AnalysisModeNone.bc` forward the C names to +// `__m2c_rt_assert_*` / `__m2c_rt_none_*`. The C drivers `drivers/ASSERT.c` +// and `drivers/TARGET-REACHED.c` call `map2check_assert` and +// `map2check_target_function`, so the C goldens apply unchanged. + +/// As [`start_overflow_rt`], with the init of the module under test. +fn start_rt(init: extern "C" fn(), witness: bool) { + println!("{STARTED}"); + if witness { + map2check_enable_witness(); + } + map2check_init(0); + init(); +} + +#[test] +fn assert_rt_child() { + if is_child() { + start_rt(__m2c_rt_assert_analysis_init, true); + tracking(); + // SAFETY: C-string literal is valid. + unsafe { __m2c_rt_assert_map2check_assert(0, 21, c"main".as_ptr()) }; + map2check_success(); + } +} + +#[test] +fn assert_rt_matches_c_runtime() { + assert_matches_golden("ASSERT", "assert_rt_child"); +} + +#[test] +fn none_rt_child() { + if is_child() { + start_rt(__m2c_rt_none_analysis_init, true); + tracking(); + // SAFETY: C-string literal is valid. + unsafe { map2check_target_function(c"reach_error".as_ptr(), 0, 22) }; + map2check_success(); + } +} + +#[test] +fn none_rt_target_function_matches_c_runtime() { + assert_matches_golden("TARGET-REACHED", "none_rt_child"); +} + +/// One `map2check_assert` call per child, without witness. `ASSERT_RT_CASE` +/// selects the condition. +#[test] +fn assert_rt_boundary_child() { + if is_child() { + start_rt(__m2c_rt_assert_analysis_init, false); + let condition: i32 = std::env::var("ASSERT_RT_CASE").unwrap().parse().unwrap(); + // SAFETY: C-string literal is valid. + unsafe { __m2c_rt_assert_map2check_assert(condition, 40, c"f".as_ptr()) }; + println!("map2check-golden-child-survived"); + map2check_success(); + println!("{RETURNED}"); + } +} + +#[test] +fn assert_rt_zero_writes_the_c_property_text_and_aborts() { + let dir = tempfile::tempdir().unwrap(); + let output = run_child_with( + "assert_rt_boundary_child", + dir.path(), + &[("ASSERT_RT_CASE", "0")], + ); + let stdout = String::from_utf8_lossy(&output.stdout); + // map2check_error -> map2check_exit -> abort (Map2CheckFunctions.c:72-102). + assert_eq!(output.status.signal(), Some(6), "{stdout}"); + assert!(!stdout.contains("survived")); + // write_property(ASSERT) (PropertyGenerator.c:92-96) writes + // map2check_property_assert (write_property_assert, :124-130) and then + // map2check_property, with the same text. + let text = c_property_text("ASSERT", 40, "f"); + let actual: BTreeMap = files_in(dir.path()) + .into_iter() + .map(|name| { + let t = std::fs::read_to_string(dir.path().join(&name)).unwrap(); + (name, t) + }) + .collect(); + assert_eq!( + actual, + BTreeMap::from([ + ("map2check_property".to_owned(), text.clone()), + ("map2check_property_assert".to_owned(), text), + ]) + ); +} + +#[test] +fn assert_rt_nonzero_does_not_report_like_c() { + for case in ["1", "-7"] { + let dir = tempfile::tempdir().unwrap(); + let output = run_child_with( + "assert_rt_boundary_child", + dir.path(), + &[("ASSERT_RT_CASE", case)], + ); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.code(), Some(0), "{case}\n{stdout}"); + assert!(stdout.contains(RETURNED), "{case}"); + assert_eq!( + std::fs::read(dir.path().join("map2check_property")).unwrap(), + b"NONE\n" + ); + assert_eq!( + files_in(dir.path()), + BTreeSet::from(["map2check_property".to_owned()]) + ); + } +} + +/// `map2check_target_function` in mode none (`PropertyGenerator.c:81-85`, +/// TARGET_REACHED): only `map2check_property`, byte for byte, then abort. +#[test] +fn none_rt_target_function_writes_the_c_reachability_property_and_aborts() { + let dir = tempfile::tempdir().unwrap(); + let output = run_child_with("none_rt_boundary_child", dir.path(), &[]); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.signal(), Some(6), "{stdout}"); + assert!(!stdout.contains("survived")); + assert_eq!( + files_in(dir.path()), + BTreeSet::from(["map2check_property".to_owned()]) + ); + assert_eq!( + std::fs::read(dir.path().join("map2check_property")).unwrap(), + b"TARGET-REACHED\nLine: 41\nFunction: reach_error\n" + ); +} + +#[test] +fn none_rt_boundary_child() { + if is_child() { + start_rt(__m2c_rt_none_analysis_init, false); + // SAFETY: C-string literal is valid. + unsafe { map2check_target_function(c"reach_error".as_ptr(), 0, 41) }; + println!("map2check-golden-child-survived"); + } +} + +// ── AnalysisModeMemtrack through the core entry points (Task 2.5, D7) ─────── +// +// `AnalysisModeMemtrack.bc` forwards the C names to `__m2c_rt_memtrack_*`. +// The children make the calls of `tests/legacy_golden/drivers/.c` with +// the C ABI (`map2check_track_bb`, `map2check_nondet_`, the memory entry +// points), so the C goldens apply: property files, `list_log.csv`, +// `allocation_log.csv`, `heap_log.csv`, `track_bb_log.st` and the end of the +// process. `klee_log.csv` is compared elsewhere: the witness writes it from +// the generator's log, which Task 3.1 wires (the drop-in nondet rows are +// `__m2c_rt_nondet_log_to_file`'s, Task 2.2); here only its presence is. + +const KLEE_LOG: &[&str] = &["klee_log.csv"]; + +/// `golden_tracking()` through the C ABI: the same basic blocks and nondet +/// values as the driver, so the list rows get the same C steps (5 on). +fn c_tracking() { + // SAFETY: C-string literals are valid. + unsafe { + map2check_track_bb(3, c"main".as_ptr()); + map2check_track_bb(4, c"main".as_ptr()); + map2check_track_bb(3, c"main".as_ptr()); + map2check_nondet_int(5, 0, 42, c"main".as_ptr()); + map2check_nondet_uint(6, 0, 4_000_000_000u32 as i32, c"main".as_ptr()); + map2check_nondet_char(7, 0, 253, c"main".as_ptr()); + map2check_nondet_long(8, 0, -7, c"main".as_ptr()); + map2check_nondet_double(9, 0, 2.5, c"main".as_ptr()); + } +} + +/// `golden_memory()` through `__m2c_rt_memtrack_*`, with the arena mapped +/// and `p = BLOCK` written in its slot (`map2check_free` reads it). +fn rt_memory() { + map_arena(); + // SAFETY: SLOT_P lies in the mapped arena; the other addresses are only + // stored; names are C strings. + unsafe { + __m2c_rt_memtrack_map2check_alloca(c"p".as_ptr(), addr(SLOT_P), 8, 8, 10, 1); + __m2c_rt_memtrack_map2check_function(c"main".as_ptr(), addr(FN_MAIN)); + __m2c_rt_memtrack_map2check_malloc(addr(BLOCK), 16); + (SLOT_P as *mut usize).write(BLOCK); + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(SLOT_P), + addr(BLOCK), + 1, + c"p".as_ptr(), + 11, + c"main".as_ptr(), + ); + } +} + +/// `map2check_free("p", SLOT_P, 1, line, "main")`. +fn rt_free_p(line: u32) { + // SAFETY: SLOT_P is mapped and holds BLOCK; C strings. + unsafe { + __m2c_rt_memtrack_map2check_free(c"p".as_ptr(), addr(SLOT_P), 1, line, c"main".as_ptr()) + }; +} + +fn start_memtrack_rt() { + start_rt(__m2c_rt_memtrack_analysis_init, true); + c_tracking(); + rt_memory(); +} + +#[test] +fn memtrack_rt_false_deref_child() { + if is_child() { + start_memtrack_rt(); + // SAFETY: WILD is only compared; C string. + unsafe { + __m2c_rt_memtrack_map2check_load(addr(WILD), 4); + __m2c_rt_memtrack_map2check_check_deref(14, c"main".as_ptr()); + } + map2check_success(); + } +} + +#[test] +fn memtrack_rt_false_deref_matches_c_runtime() { + assert_matches_golden_except("FALSE-DEREF", "memtrack_rt_false_deref_child", KLEE_LOG); +} + +#[test] +fn memtrack_rt_false_free_child() { + if is_child() { + start_memtrack_rt(); + rt_free_p(12); + rt_free_p(13); + map2check_success(); + } +} + +#[test] +fn memtrack_rt_double_free_matches_c_runtime() { + assert_matches_golden_except("FALSE-FREE", "memtrack_rt_false_free_child", KLEE_LOG); +} + +#[test] +fn memtrack_rt_false_free_resolved_child() { + if is_child() { + start_memtrack_rt(); + rt_free_p(12); + // SAFETY: BLOCK is only compared; C string. + unsafe { + __m2c_rt_memtrack_map2check_free_resolved_address(addr(BLOCK), 13, c"main".as_ptr(), 0) + }; + map2check_success(); + } +} + +#[test] +fn memtrack_rt_false_free_resolved_matches_c_runtime() { + assert_matches_golden_except( + "FALSE-FREE-RESOLVED", + "memtrack_rt_false_free_resolved_child", + KLEE_LOG, + ); +} + +#[test] +fn memtrack_rt_false_memtrack_child() { + if is_child() { + start_memtrack_rt(); + map2check_success(); + } +} + +#[test] +fn memtrack_rt_false_memtrack_matches_c_runtime() { + assert_matches_golden_except( + "FALSE-MEMTRACK", + "memtrack_rt_false_memtrack_child", + KLEE_LOG, + ); +} + +#[test] +fn memtrack_rt_none_child() { + if is_child() { + start_memtrack_rt(); + rt_free_p(12); + map2check_success(); + println!("{RETURNED}"); + } +} + +#[test] +fn memtrack_rt_valid_run_matches_c_runtime() { + assert_matches_golden("NONE", "memtrack_rt_none_child"); +} + +#[test] +fn memtrack_rt_unknown_child() { + if is_child() { + start_memtrack_rt(); + __m2c_rt_memtrack_map2check_malloc(std::ptr::null(), 4); + map2check_success(); + } +} + +#[test] +fn memtrack_rt_unknown_matches_c_runtime() { + assert_matches_golden_except("UNKNOWN", "memtrack_rt_unknown_child", KLEE_LOG); +} + +#[test] +fn memtrack_rt_posix_child() { + if is_child() { + start_memtrack_rt(); + // SAFETY: SLOT_Q lies in the mapped arena; names are C strings. + unsafe { + __m2c_rt_memtrack_map2check_alloca(c"q".as_ptr(), addr(SLOT_Q), 8, 8, 15, 1); + (SLOT_Q as *mut usize).write(BLOCK2); + __m2c_rt_memtrack_map2check_posix(addr(SLOT_Q), 32); + } + map2check_success(); + } +} + +#[test] +fn memtrack_rt_posix_matches_c_runtime() { + assert_matches_golden_except("POSIX-MEMALIGN", "memtrack_rt_posix_child", KLEE_LOG); +} + +/// One violation per child, without witness: `MEMTRACK_RT_CASE` selects it. +#[test] +fn memtrack_rt_boundary_child() { + if is_child() { + start_rt(__m2c_rt_memtrack_analysis_init, false); + map_arena(); + let case = std::env::var("MEMTRACK_RT_CASE").unwrap(); + let main = c"main".as_ptr(); + // SAFETY: every address read lies in the mapped arena; the others + // are only compared; names are C strings. + unsafe { + match case.as_str() { + "non-heap free" => { + // int x; int *p = &x; free(p); + __m2c_rt_memtrack_map2check_alloca(c"x".as_ptr(), addr(BLOCK), 4, 4, 3, 1); + (SLOT_P as *mut usize).write(BLOCK); + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(SLOT_P), + addr(BLOCK), + 1, + c"p".as_ptr(), + 4, + main, + ); + __m2c_rt_memtrack_map2check_free(c"p".as_ptr(), addr(SLOT_P), 1, 50, main); + } + "cstring beyond the block" => { + // char *s = malloc(4); memcpy(s, "abcd", 4); puts(s); + let s = BLOCK as *mut u8; + s.copy_from_nonoverlapping(b"abcd".as_ptr(), 4); + __m2c_rt_memtrack_map2check_malloc(addr(BLOCK), 4); + __m2c_rt_memtrack_map2check_check_cstring(s.cast_const().cast()); + __m2c_rt_memtrack_map2check_check_deref(51, c"puts_caller".as_ptr()); + } + "cstring inside the block" => { + let s = BLOCK as *mut u8; + s.copy_from_nonoverlapping(b"abc\0".as_ptr(), 4); + __m2c_rt_memtrack_map2check_malloc(addr(BLOCK), 4); + __m2c_rt_memtrack_map2check_check_cstring(s.cast_const().cast()); + __m2c_rt_memtrack_map2check_check_deref(52, main); + __m2c_rt_memtrack_map2check_free_resolved_address(addr(BLOCK), 53, main, 0); + } + "free of a literal NULL" => { + // free(0): not a LoadInst, so the pass emits the resolved + // free with isNullValid 0 (MemoryTrackPass.cpp:428-436). + __m2c_rt_memtrack_map2check_free_resolved_address( + std::ptr::null(), + 54, + main, + 0, + ); + } + "wasm leak" => { + // A lifted malloc never freed: develop free()s the + // linear-memory offset (AllocationLog.c:91) and glibc + // aborts before FALSE_MEMTRACK is written (SD-19). + __m2c_rt_memtrack_map2check_wasm_malloc(0x400, 16); + } + other => panic!("caso desconhecido {other}"), + } + } + println!("map2check-golden-child-survived"); + map2check_success(); + println!("{RETURNED}"); + } +} + +/// SD-23: develop's `find_row_with_address(NULL)` returns NULL and the +/// release writes through it (SIGSEGV, no property file); the core goes on +/// and the run ends correct. +#[test] +fn sd23_free_of_a_literal_null_goes_on_where_develop_crashes() { + let (output, files) = memtrack_rt_case("free of a literal NULL"); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.code(), Some(0), "{stdout}"); + assert!(stdout.contains(RETURNED)); + assert_eq!( + files, + BTreeMap::from([("map2check_property".to_owned(), "NONE\n".to_owned())]) + ); +} + +/// SD-19: a leaked wasm block is FALSE-MEMTRACK; the core does not free() +/// the offset as develop does. +#[test] +fn sd19_wasm_leak_is_false_memtrack_without_freeing_the_offset() { + let (output, files) = memtrack_rt_case("wasm leak"); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.signal(), Some(6), "{stdout}"); + assert_eq!( + files, + BTreeMap::from([ + ( + "map2check_property".to_owned(), + "FALSE-MEMTRACK\n".to_owned() + ), + ( + "map2check_property_memtrack".to_owned(), + "FALSE-MEMTRACK\n".to_owned() + ), + ]) + ); +} + +// ── AnalysisModeMemcleanup through __m2c_rt_memcleanup_* (Task 2.6) ───────── + +/// `golden_memory()` through `__m2c_rt_memcleanup_*`, as `rt_memory` does for +/// memtrack. +fn rt_memcleanup_memory() { + map_arena(); + // SAFETY: SLOT_P lies in the mapped arena; the other addresses are only + // stored; names are C strings. + unsafe { + __m2c_rt_memcleanup_map2check_alloca(c"p".as_ptr(), addr(SLOT_P), 8, 8, 10, 1); + __m2c_rt_memcleanup_map2check_function(c"main".as_ptr(), addr(FN_MAIN)); + __m2c_rt_memcleanup_map2check_malloc(addr(BLOCK), 16); + (SLOT_P as *mut usize).write(BLOCK); + __m2c_rt_memcleanup_map2check_add_store_pointer( + addr(SLOT_P), + addr(BLOCK), + 1, + c"p".as_ptr(), + 11, + c"main".as_ptr(), + ); + } +} + +fn start_memcleanup_rt() { + start_rt(__m2c_rt_memcleanup_analysis_init, true); + c_tracking(); + rt_memcleanup_memory(); +} + +/// `exit(0)` of a program with a reachable block still allocated: the pass +/// ends the program with `map2check_success`, whose `map2check_exit` runs +/// `analysis_is_program_correct` (`AnalysisModeMemcleanup.c:138-148`; +/// `Map2CheckFunctions.c:89-102`). `p` still points to the block. +/// The brief's case: the child calls `exit(0)` with a reachable unfreed +/// block. The pass instruments `exit` with `map2check_exit` +/// (`Map2CheckLibrary.cpp:91-95`), whose verdict (`Map2CheckFunctions.c: +/// 89-102`) is `analysis_is_program_correct` (`AnalysisModeMemcleanup.c: +/// 138-148`): FALSE-MEMCLEANUP in both property files and SIGABRT. +#[test] +fn memcleanup_reports_leak_at_map2check_exit_child() { + if is_child() { + start_memcleanup_rt(); + map2check_exit(); + println!("{RETURNED}"); + } +} + +#[test] +fn memcleanup_reports_leak_at_map2check_exit() { + assert_matches_golden_except( + "FALSE-MEMCLEANUP", + "memcleanup_reports_leak_at_map2check_exit_child", + KLEE_LOG, + ); + let dir = tempfile::tempdir().unwrap(); + let output = run_child( + "memcleanup_reports_leak_at_map2check_exit_child", + dir.path(), + ); + assert_eq!(output.status.signal(), Some(6)); + assert!(!String::from_utf8_lossy(&output.stdout).contains(RETURNED)); + for f in ["map2check_property", "map2check_property_memcleanup"] { + assert_eq!( + std::fs::read_to_string(dir.path().join(f)).unwrap(), + "FALSE-MEMCLEANUP\n", + "{f}" + ); + } +} + +#[test] +fn memcleanup_reports_leak_at_exit_child() { + if is_child() { + start_memcleanup_rt(); + map2check_success(); + } +} + +#[test] +fn memcleanup_reports_leak_at_exit() { + assert_matches_golden_except( + "FALSE-MEMCLEANUP", + "memcleanup_reports_leak_at_exit_child", + KLEE_LOG, + ); + // The golden's property text, from PropertyGenerator.c:36-40 and :77-80. + let dir = tempfile::tempdir().unwrap(); + let output = run_child("memcleanup_reports_leak_at_exit_child", dir.path()); + assert_eq!(output.status.signal(), Some(6)); + assert_eq!( + std::fs::read_to_string(dir.path().join("map2check_property")).unwrap(), + "FALSE-MEMCLEANUP\n" + ); + assert_eq!( + std::fs::read_to_string(dir.path().join("map2check_property_memcleanup")).unwrap(), + "FALSE-MEMCLEANUP\n" + ); +} + +/// Memtrack reports the leak of an unreferenced block; memcleanup does not +/// (`AnalysisModeMemcleanup.c:141`, `ListLog.c:40-65`): `p` is reassigned to +/// NULL, the run ends `NONE`. +#[test] +fn memcleanup_unreferenced_leak_is_none_child() { + if is_child() { + start_memcleanup_rt(); + // SAFETY: C strings. + unsafe { + __m2c_rt_memcleanup_map2check_add_store_pointer( + addr(SLOT_P), + std::ptr::null(), + 1, + c"p".as_ptr(), + 12, + c"main".as_ptr(), + ); + } + map2check_success(); + println!("{RETURNED}"); + } +} + +#[test] +fn memcleanup_unreferenced_leak_is_none() { + let dir = tempfile::tempdir().unwrap(); + let output = run_child("memcleanup_unreferenced_leak_is_none_child", dir.path()); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.code(), Some(0), "{stdout}"); + assert!(stdout.contains(RETURNED)); + assert_eq!( + std::fs::read_to_string(dir.path().join("map2check_property")).unwrap(), + "NONE\n" + ); + assert!(!dir.path().join("map2check_property_memcleanup").exists()); +} + +/// A freed block ends the run `NONE` (`tests/legacy_golden/NONE`). +#[test] +fn memcleanup_valid_run_child() { + if is_child() { + start_memcleanup_rt(); + // SAFETY: SLOT_P holds BLOCK; C strings. + unsafe { + __m2c_rt_memcleanup_map2check_free(c"p".as_ptr(), addr(SLOT_P), 1, 12, c"main".as_ptr()) + }; + map2check_success(); + println!("{RETURNED}"); + } +} + +#[test] +fn memcleanup_valid_run_matches_c_runtime() { + assert_matches_golden("NONE", "memcleanup_valid_run_child"); +} + +/// Memtrack's FALSE-FREE cases are not violations here (`:168`, `:342`): a +/// double free runs to the end, `NONE`. +#[test] +fn memcleanup_double_free_child() { + if is_child() { + start_memcleanup_rt(); + // SAFETY: SLOT_P holds BLOCK; C strings. + unsafe { + __m2c_rt_memcleanup_map2check_free( + c"p".as_ptr(), + addr(SLOT_P), + 1, + 12, + c"main".as_ptr(), + ); + __m2c_rt_memcleanup_map2check_free( + c"p".as_ptr(), + addr(SLOT_P), + 1, + 13, + c"main".as_ptr(), + ); + } + map2check_success(); + println!("{RETURNED}"); + } +} + +#[test] +fn memcleanup_double_free_is_not_false_free() { + let dir = tempfile::tempdir().unwrap(); + let output = run_child("memcleanup_double_free_child", dir.path()); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.code(), Some(0), "{stdout}"); + assert!(stdout.contains(RETURNED)); + assert_eq!( + std::fs::read_to_string(dir.path().join("map2check_property")).unwrap(), + "NONE\n" + ); + assert!(!dir.path().join("map2check_property_free").exists()); +} + +/// `map2check_malloc(NULL)` is UNKNOWN in both modes (`:296-302`). +#[test] +fn memcleanup_unknown_child() { + if is_child() { + start_memcleanup_rt(); + __m2c_rt_memcleanup_map2check_malloc(std::ptr::null(), 4); + map2check_success(); + } +} + +#[test] +fn memcleanup_unknown_matches_c_runtime() { + assert_matches_golden_except("UNKNOWN", "memcleanup_unknown_child", KLEE_LOG); +} + +/// `drivers/POSIX-MEMALIGN.c` in memcleanup mode: the block is stored in +/// `q`, which still points to it: the same files, ending FALSE-MEMCLEANUP +/// rather than the memtrack golden's FALSE-MEMTRACK. +#[test] +fn memcleanup_posix_child() { + if is_child() { + start_memcleanup_rt(); + // SAFETY: SLOT_Q lies in the mapped arena; names are C strings. + unsafe { + __m2c_rt_memcleanup_map2check_alloca(c"q".as_ptr(), addr(SLOT_Q), 8, 8, 15, 1); + (SLOT_Q as *mut usize).write(BLOCK2); + __m2c_rt_memcleanup_map2check_posix(addr(SLOT_Q), 32); + } + map2check_success(); + } +} + +#[test] +fn memcleanup_posix_leaves_the_memtrack_golden_logs() { + let golden = + std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/legacy_golden/POSIX-MEMALIGN"); + let dir = tempfile::tempdir().unwrap(); + let output = run_child("memcleanup_posix_child", dir.path()); + assert_eq!(output.status.signal(), Some(6)); + for f in ["allocation_log.csv", "list_log.csv", "heap_log.csv"] { + assert_eq!( + std::fs::read(dir.path().join(f)).unwrap(), + std::fs::read(golden.join(f)).unwrap(), + "{f}" + ); + } + assert_eq!( + std::fs::read_to_string(dir.path().join("map2check_property")).unwrap(), + "FALSE-MEMCLEANUP\n" + ); +} + +/// One case per child, without witness: `MEMCLEANUP_RT_CASE` selects it. +#[test] +fn memcleanup_rt_boundary_child() { + if is_child() { + start_rt(__m2c_rt_memcleanup_analysis_init, false); + map_arena(); + let case = std::env::var("MEMCLEANUP_RT_CASE").unwrap(); + let main = c"main".as_ptr(); + // SAFETY: every address read lies in the mapped arena; the others + // are only compared; names are C strings. + unsafe { + match case.as_str() { + "stack free" => { + // int x; int *p = &x; free(p); + __m2c_rt_memcleanup_map2check_alloca(c"x".as_ptr(), addr(BLOCK), 4, 4, 3, 1); + (SLOT_P as *mut usize).write(BLOCK); + __m2c_rt_memcleanup_map2check_add_store_pointer( + addr(SLOT_P), + addr(BLOCK), + 1, + c"p".as_ptr(), + 4, + main, + ); + __m2c_rt_memcleanup_map2check_free(c"p".as_ptr(), addr(SLOT_P), 1, 50, main); + } + "untracked resolved free" => { + // free(q) of an address no allocation ever recorded. + __m2c_rt_memcleanup_map2check_free_resolved_address(addr(WILD), 51, main, 0); + } + "free of a literal NULL" => { + __m2c_rt_memcleanup_map2check_free_resolved_address( + std::ptr::null(), + 54, + main, + 0, + ); + } + "invalid deref" => { + __m2c_rt_memcleanup_map2check_load(addr(WILD), 4); + __m2c_rt_memcleanup_map2check_check_deref(14, main); + } + other => panic!("caso desconhecido {other}"), + } + } + println!("map2check-golden-child-survived"); + map2check_success(); + println!("{RETURNED}"); + } +} + +fn memcleanup_rt_case(case: &str) -> (std::process::Output, BTreeMap) { + let dir = tempfile::tempdir().unwrap(); + let output = run_child_with( + "memcleanup_rt_boundary_child", + dir.path(), + &[("MEMCLEANUP_RT_CASE", case)], + ); + let files = files_in(dir.path()) + .into_iter() + .map(|name| { + let text = std::fs::read_to_string(dir.path().join(&name)).unwrap(); + (name, text) + }) + .collect(); + (output, files) +} + +/// SD-23: develop's `AnalysisModeMemcleanup.c` lets every free through +/// (`error = FALSE`, `:168`, `:342`) and writes through the NULL row of +/// `find_row_with_address` (`:176-177`, `:353-354`): SIGSEGV, no property +/// file. The core goes on and the run ends `NONE`. +#[test] +fn sd23_memcleanup_release_without_allocation_row_goes_on_where_develop_crashes() { + for case in [ + "stack free", + "untracked resolved free", + "free of a literal NULL", + ] { + let (output, files) = memcleanup_rt_case(case); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.code(), Some(0), "{case}: {stdout}"); + assert!(stdout.contains(RETURNED), "{case}"); + assert_eq!( + files, + BTreeMap::from([("map2check_property".to_owned(), "NONE\n".to_owned())]), + "{case}" + ); + } +} + +/// `map2check_load` and `map2check_check_deref` are empty (`:158`, `:294`): +/// the access memtrack reports as FALSE-DEREF goes unnoticed. +#[test] +fn memcleanup_invalid_deref_is_not_reported() { + let (output, files) = memcleanup_rt_case("invalid deref"); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.code(), Some(0), "{stdout}"); + assert_eq!( + files, + BTreeMap::from([("map2check_property".to_owned(), "NONE\n".to_owned())]) + ); +} + +/// The files `memtrack_rt_boundary_child` leaves for `case`. +fn memtrack_rt_case(case: &str) -> (std::process::Output, BTreeMap) { + let dir = tempfile::tempdir().unwrap(); + let output = run_child_with( + "memtrack_rt_boundary_child", + dir.path(), + &[("MEMTRACK_RT_CASE", case)], + ); + let files = files_in(dir.path()) + .into_iter() + .map(|name| { + let text = std::fs::read_to_string(dir.path().join(&name)).unwrap(); + (name, text) + }) + .collect(); + (output, files) +} + +#[test] +fn memtrack_rt_free_of_a_non_heap_address_writes_false_free_and_aborts() { + let (output, files) = memtrack_rt_case("non-heap free"); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.signal(), Some(6), "{stdout}"); + assert!(!stdout.contains("survived")); + // AnalysisModeMemtrack.c:367-370: write_property(FALSE_FREE) + // (PropertyGenerator.c:61-66, which writes map2check_property_free first). + let text = c_property_text("FALSE-FREE", 50, "main"); + assert_eq!( + files, + BTreeMap::from([ + ("map2check_property".to_owned(), text.clone()), + ("map2check_property_free".to_owned(), text), + ]) + ); +} + +#[test] +fn memtrack_rt_check_cstring_beyond_the_block_writes_false_deref_and_aborts() { + let (output, files) = memtrack_rt_case("cstring beyond the block"); + let stdout = String::from_utf8_lossy(&output.stdout); + assert_eq!(output.status.signal(), Some(6), "{stdout}"); + assert!(!stdout.contains("survived")); + // :159-166 sets ERROR_DEREF at the fifth byte; :309-315 writes + // FALSE_DEREF (PropertyGenerator.c:67-72, :42-48). + let text = c_property_text("FALSE-DEREF", 51, "puts_caller"); + assert_eq!( + files, + BTreeMap::from([ + ("map2check_property".to_owned(), text.clone()), + ("map2check_property_deref".to_owned(), text), + ]) + ); +} + +#[test] +fn memtrack_rt_check_cstring_inside_the_block_is_not_reported() { + let (output, files) = memtrack_rt_case("cstring inside the block"); + let stdout = String::from_utf8_lossy(&output.stdout); + // free(malloc(4)) with no tracked store: is_invalid_free (ListLog.c:117) + // is TRUE in develop, so the resolved free is FALSE-FREE. + assert_eq!(output.status.signal(), Some(6), "{stdout}"); + let text = c_property_text("FALSE-FREE", 53, "main"); + assert_eq!( + files, + BTreeMap::from([ + ("map2check_property".to_owned(), text.clone()), + ("map2check_property_free".to_owned(), text), + ]) + ); +} diff --git a/rust/src/ffi_tests.rs b/rust/src/ffi_tests.rs index 2aaf4a3..d3d5faa 100644 --- a/rust/src/ffi_tests.rs +++ b/rust/src/ffi_tests.rs @@ -48,7 +48,7 @@ fn assert_violated(property: ViolatedProperty, line: u32, function_name: &str) { #[test] fn init_leaves_state_empty_and_ok() { let _guard = fresh(); - map2check_init(); + map2check_init(0); assert_ok(); assert_eq!(snapshot(|s| s.current_step()), 0); assert!(snapshot(|s| s.memtrack.is_empty() @@ -59,11 +59,11 @@ fn init_leaves_state_empty_and_ok() { #[test] fn second_init_starts_from_clean_state() { let _guard = fresh(); - map2check_init(); + map2check_init(0); // SAFETY: fake addresses are only stored, never dereferenced. unsafe { map2check_map_malloc(addr(0x1000), 8) }; // SAFETY: C-string literal is valid and null-terminated. - unsafe { set_false_result(5, 7, c"main".as_ptr()) }; + unsafe { set_false_result(ViolatedProperty::Reachability as i32, 7, c"main".as_ptr()) }; map2check_set_memcleanup(); map2check_set_null_is_valid(); let mut value: i32 = 3; @@ -72,7 +72,7 @@ fn second_init_starts_from_clean_state() { let _ = get_next_step(); assert!(snapshot(|s| s.current_step() > 0 && !s.nondets.is_empty())); - map2check_init(); + map2check_init(0); assert_ok(); snapshot(|s| { @@ -92,7 +92,7 @@ fn reset_clears_records_result_step_and_flags() { // SAFETY: null pointer is a valid malloc result. unsafe { map2check_map_malloc(addr(0x1000), 16) }; // SAFETY: C-string literal is valid and null-terminated. - unsafe { set_false_result(5, 7, c"main".as_ptr()) }; + unsafe { set_false_result(ViolatedProperty::Reachability as i32, 7, c"main".as_ptr()) }; map2check_set_memcleanup(); map2check_set_null_is_valid(); @@ -121,7 +121,7 @@ fn success_does_not_change_state() { fn set_false_result_records_property_line_and_function() { let _guard = fresh(); // SAFETY: C-string literal is valid and null-terminated. - unsafe { set_false_result(5, 42, c"main".as_ptr()) }; + unsafe { set_false_result(ViolatedProperty::Reachability as i32, 42, c"main".as_ptr()) }; assert_violated(ViolatedProperty::Reachability, 42, "main"); } @@ -137,7 +137,7 @@ fn set_false_result_ignores_unknown_property() { fn set_false_result_accepts_null_function_name() { let _guard = fresh(); // SAFETY: null is an accepted function_name. - unsafe { set_false_result(0, 3, std::ptr::null()) }; + unsafe { set_false_result(ViolatedProperty::Overflow as i32, 3, std::ptr::null()) }; assert_violated(ViolatedProperty::Overflow, 3, ""); } @@ -146,7 +146,7 @@ fn get_current_property_reflects_last_violation() { let _guard = fresh(); assert_eq!(get_current_property(), ViolatedProperty::None); // SAFETY: C-string literal is valid and null-terminated. - unsafe { set_false_result(2, 1, c"f".as_ptr()) }; + unsafe { set_false_result(ViolatedProperty::MemsafetyDeref as i32, 1, c"f".as_ptr()) }; assert_eq!(get_current_property(), ViolatedProperty::MemsafetyDeref); } @@ -184,7 +184,7 @@ fn vcc_reset_meta_data_clears_result_but_keeps_records() { // SAFETY: fake address is never dereferenced. unsafe { map2check_map_malloc(addr(0x1000), 8) }; // SAFETY: C-string literal is valid and null-terminated. - unsafe { set_false_result(1, 9, c"main".as_ptr()) }; + unsafe { set_false_result(ViolatedProperty::MemsafetyFree as i32, 9, c"main".as_ptr()) }; vcc_reset_meta_data(); @@ -528,7 +528,7 @@ fn posix_memalign_null_block_is_unknown_in_memory_modes() { // UNKNOWN, and no store follows. let _guard = fresh(); map2check_set_mode(AnalysisMode::Memtrack as i32); - map2check_init(); + map2check_init(0); let mut slot: *const c_void = std::ptr::null(); // SAFETY: `slot` is a live `void *` for the whole call. unsafe { map2check_map_posix_memalign(std::ptr::addr_of_mut!(slot).cast(), 64) }; @@ -1119,7 +1119,7 @@ fn signed_binops_are_gated_like_the_other_binops() { for (op, a, b) in calls { state::reset(); map2check_set_mode(mode as i32); - map2check_init(); + map2check_init(0); // SAFETY: C-string literal is valid and null-terminated. unsafe { op(a, b, 32, 4, c"f".as_ptr()) }; let reported = !snapshot(|s| s.result.ok); @@ -1171,7 +1171,7 @@ fn signed_binops_called_as_c_does() { fn set_false_result_accepts_div_by_zero() { let _guard = fresh(); // SAFETY: C-string literal is valid and null-terminated. - unsafe { set_false_result(10, 3, c"main".as_ptr()) }; + unsafe { set_false_result(ViolatedProperty::DivByZero as i32, 3, c"main".as_ptr()) }; assert_violated(ViolatedProperty::DivByZero, 3, "main"); } @@ -1233,7 +1233,7 @@ fn sd03_safe_check_keeps_earlier_violation() { // SAFETY: C-string literals are valid; fake addresses are never dereferenced. unsafe { map2check_map_malloc(addr(0x1000), 16); - set_false_result(5, 1, c"main".as_ptr()); + set_false_result(ViolatedProperty::Reachability as i32, 1, c"main".as_ptr()); map2check_check_load(addr(0x1000), 2, 0, 4, c"main".as_ptr()); } assert_violated(ViolatedProperty::Reachability, 1, "main"); @@ -1283,7 +1283,7 @@ fn sd03_later_violation_does_not_overwrite_first() { c"main".as_ptr(), ); map2check_check_deref(std::ptr::null(), 0, 9, c"main".as_ptr()); - set_false_result(5, 10, c"main".as_ptr()); + set_false_result(ViolatedProperty::Reachability as i32, 10, c"main".as_ptr()); map2check_binop_add_int(i32::MAX, 1, 11, 0, c"main".as_ptr()); } assert_violated(ViolatedProperty::MemsafetyDeref, 7, "first"); @@ -1294,7 +1294,7 @@ fn sd03_later_violation_does_not_overwrite_first() { #[test] fn deref_after_free_is_invalid() { let _guard = fresh(); - map2check_init(); + map2check_init(0); // SAFETY: C-string literals are valid; fake addresses are never dereferenced. unsafe { map2check_map_malloc(addr(0x2000), 16); @@ -1781,7 +1781,7 @@ fn macro_generated_exports_match_the_hand_written_header_declarations() { #[test] fn memcleanup_reports_leak_as_memcleanup_at_exit() { let _guard = fresh(); - map2check_init(); + map2check_init(0); map2check_set_memcleanup(); // SAFETY: fake address is never dereferenced. unsafe { map2check_map_malloc(addr(0x3000), 32) }; @@ -1794,7 +1794,7 @@ fn without_memcleanup_leak_is_reported_as_memtrack_at_exit() { // develop: analysis_is_program_correct() of AnalysisModeMemtrack.c writes // FALSE_MEMTRACK; the leak check runs at exit in both modes. let _guard = fresh(); - map2check_init(); + map2check_init(0); // SAFETY: fake address is never dereferenced. unsafe { map2check_map_malloc(addr(0x3000), 32) }; map2check_check_mem_endprog(); @@ -1830,7 +1830,7 @@ fn endprog_keeps_an_earlier_violation() { #[test] fn null_is_valid_accepts_null_load() { let _guard = fresh(); - map2check_init(); + map2check_init(0); map2check_set_null_is_valid(); // SAFETY: null is never dereferenced. unsafe { map2check_check_load(std::ptr::null(), 3, 0, 4, c"main".as_ptr()) }; @@ -1861,7 +1861,7 @@ fn options_survive_init_and_reset() { let expected = (ViolatedProperty::MemsafetyMemcleanup, true); assert_eq!(observed_options(), expected); - map2check_init(); + map2check_init(0); assert_eq!(observed_options(), expected); map2check_reset(); @@ -1874,7 +1874,7 @@ fn enable_witness_is_configuration_kept_by_init_and_reset() { assert!(!state::config().witness); map2check_enable_witness(); assert!(state::config().witness); - map2check_init(); + map2check_init(0); map2check_reset(); assert!(state::config().witness); // Configuration only: it records nothing. @@ -1882,6 +1882,20 @@ fn enable_witness_is_configuration_kept_by_init_and_reset() { assert_eq!(snapshot(|s| s.current_step()), 0); } +/// `__m2c_rt_witness_enable` is the C ABI of the old +/// `map2check_enable_witness` (Task 2.10): the same configuration. +#[test] +fn witness_enable_entry_point_turns_on_the_witness_and_init_keeps_it() { + let _guard = fresh(); + assert!(!state::config().witness); + __m2c_rt_witness_enable(); + assert!(state::config().witness); + map2check_init(0); + assert!(state::config().witness); + assert_ok(); + assert_eq!(snapshot(|s| s.current_step()), 0); +} + #[test] fn malloc_null_reports_unknown_in_memory_modes_only() { // develop: map2check_malloc with a NULL block writes @@ -1958,7 +1972,7 @@ const MODES: [AnalysisMode; 6] = [ fn start(mode: AnalysisMode) { state::reset(); map2check_set_mode(mode as i32); - map2check_init(); + map2check_init(0); } /// Runs one violating call of each check family on a fresh analysis in @@ -2014,7 +2028,7 @@ use ViolatedProperty as P; fn mode_none_never_reports_memory_errors() { let _guard = fresh(); map2check_set_mode(AnalysisMode::None as i32); - map2check_init(); + map2check_init(0); // SAFETY: C-string literal is valid; fake address is never dereferenced. unsafe { map2check_check_deref(addr(0xdead), 0, 1, c"main".as_ptr()) }; assert_ok(); @@ -2024,7 +2038,7 @@ fn mode_none_never_reports_memory_errors() { fn mode_memtrack_reports_invalid_deref() { let _guard = fresh(); map2check_set_mode(AnalysisMode::Memtrack as i32); - map2check_init(); + map2check_init(0); // SAFETY: C-string literal is valid; fake address is never dereferenced. unsafe { map2check_check_deref(addr(0xdead), 0, 1, c"main".as_ptr()) }; assert_violated(ViolatedProperty::MemsafetyDeref, 1, "main"); @@ -2124,7 +2138,7 @@ fn unset_mode_keeps_every_check_active() { ]; for (slot, run) in out.iter_mut().zip(runs) { state::reset(); - map2check_init(); + map2check_init(0); run(); *slot = snapshot(|s| s.result.property); } @@ -2174,7 +2188,7 @@ fn set_mode_is_configuration_kept_by_init_and_reset() { let _guard = fresh(); for mode in MODES { map2check_set_mode(mode as i32); - map2check_init(); + map2check_init(0); assert_eq!(state::config().mode, Some(mode)); map2check_reset(); assert_eq!(state::config().mode, Some(mode)); @@ -2185,7 +2199,7 @@ fn set_mode_is_configuration_kept_by_init_and_reset() { fn reset_keeps_selected_mode() { let _guard = fresh(); map2check_set_mode(AnalysisMode::Overflow as i32); - map2check_init(); + map2check_init(0); map2check_reset(); assert_eq!(state::config().mode, Some(AnalysisMode::Overflow)); } @@ -2194,7 +2208,7 @@ fn reset_keeps_selected_mode() { fn reach_target_sets_target_reached() { let _guard = fresh(); map2check_set_mode(AnalysisMode::Target as i32); - map2check_init(); + map2check_init(0); // SAFETY: C-string literal is valid and null-terminated. unsafe { mark_target_reached(c"reach_error".as_ptr(), 0, 12) }; assert_violated(ViolatedProperty::Reachability, 12, "reach_error"); @@ -2219,7 +2233,7 @@ fn reach_target_keeps_the_first_violation() { start(AnalysisMode::Target); // SAFETY: C-string literals are valid and null-terminated. unsafe { - set_false_result(0, 3, c"f".as_ptr()); + set_false_result(ViolatedProperty::Overflow as i32, 3, c"f".as_ptr()); map2check_reach_target(c"reach_error".as_ptr(), 0, 12); } assert_violated(ViolatedProperty::Overflow, 3, "f"); @@ -2242,11 +2256,11 @@ fn reach_target_works_in_every_mode_and_accepts_null_name() { fn set_false_result_accepts_the_appended_properties() { let _guard = fresh(); // SAFETY: C-string literal is valid and null-terminated. - unsafe { set_false_result(8, 4, c"main".as_ptr()) }; + unsafe { set_false_result(ViolatedProperty::Assert as i32, 4, c"main".as_ptr()) }; assert_violated(ViolatedProperty::Assert, 4, "main"); state::reset(); // SAFETY: as above. - unsafe { set_false_result(9, 0, c"main".as_ptr()) }; + unsafe { set_false_result(ViolatedProperty::Unknown as i32, 0, c"main".as_ptr()) }; assert_violated(ViolatedProperty::Unknown, 0, "main"); } @@ -2423,11 +2437,13 @@ fn memtrack_mode_reports_any_leak_reachable_or_not() { mod wasm { use super::*; use crate::wasm::{ + __m2c_rt_wasm_allocate_funcref_table, __m2c_rt_wasm_allocate_memory, __m2c_rt_wasm_free, + __m2c_rt_wasm_free_funcref_table, __m2c_rt_wasm_free_memory, __m2c_rt_wasm_grow_memory, + __m2c_rt_wasm_init, __m2c_rt_wasm_is_initialized, __m2c_rt_wasm_trap, map2check_wasm_check_access, map2check_wasm_free, map2check_wasm_malloc, - w2c_wasi__snapshot__preview1_proc_exit, wasm_rt_allocate_funcref_table, - wasm_rt_allocate_memory, wasm_rt_free, wasm_rt_free_funcref_table, wasm_rt_free_memory, - wasm_rt_funcref_table_t, wasm_rt_grow_memory, wasm_rt_init, wasm_rt_is_initialized, - wasm_rt_memory_t, + wasm_rt_allocate_funcref_table, wasm_rt_allocate_memory, wasm_rt_free, + wasm_rt_free_funcref_table, wasm_rt_free_memory, wasm_rt_funcref_table_t, + wasm_rt_grow_memory, wasm_rt_init, wasm_rt_is_initialized, wasm_rt_memory_t, }; fn started(mode: Option) { @@ -2436,7 +2452,7 @@ mod wasm { if let Some(m) = mode { map2check_set_mode(m as i32); } - map2check_init(); + map2check_init(0); } fn mem() -> wasm_rt_memory_t { @@ -2639,7 +2655,7 @@ mod wasm { let _guard = fresh(); started(Some(AnalysisMode::Memtrack)); map2check_wasm_malloc(0x1000, 16); - map2check_init(); + map2check_init(0); assert!(snapshot(|s| s.wasm_allocs.is_empty())); map2check_wasm_malloc(0x1000, 16); map2check_reset(); @@ -2738,6 +2754,8 @@ mod wasm { fn rt_init_free_and_is_initialized_follow_the_stub() { // The stub is "always initialised for KLEE": true from the start, // false after wasm_rt_free, true again after wasm_rt_init. + let _g = INIT_FLAG.lock().unwrap_or_else(|e| e.into_inner()); + wasm_rt_init(); assert!(wasm_rt_is_initialized()); wasm_rt_free(); assert!(!wasm_rt_is_initialized()); @@ -2745,10 +2763,171 @@ mod wasm { assert!(wasm_rt_is_initialized()); } + // ── __m2c_rt_wasm_* (the core entry points of WasmRuntimeStubs.ll) ───── + // + // The IR module defines the C names `wasm_rt_*` and forwards each one to + // `__m2c_rt_wasm_*`; the expectations are those of WasmRuntimeStubs.c + // (line numbers of the develop copy cited per test). The descriptors + // cross the C ABI as `void *`. + + fn p(m: &mut wasm_rt_memory_t) -> *mut std::os::raw::c_void { + (m as *mut wasm_rt_memory_t).cast() + } + + #[test] + fn m2c_rt_wasm_allocate_memory_zeroes_and_sets_every_field() { + // WasmRuntimeStubs.c:22-35: calloc(1, page_size * initial_pages); data_end, + // page_size, size, pages and max_pages set; is64 untouched. + let mut m = mem(); + m.is64 = true; + // SAFETY: `m` is a valid descriptor; freed below. + unsafe { __m2c_rt_wasm_allocate_memory(p(&mut m), 2, 5, false, 65536) }; + assert!(!m.data.is_null()); + assert_eq!( + (m.size, m.pages, m.max_pages, m.page_size), + (2 * 65536, 2, 5, 65536) + ); + assert!(m.is64, "is64 is not written (WasmRuntimeStubs.c:34)"); + // SAFETY: data_end is one past the block that data starts. + assert_eq!(unsafe { m.data.add(m.size as usize) }, m.data_end); + // SAFETY: the block is `size` bytes long and calloc'd. + assert!((0..m.size as usize).all(|i| unsafe { *m.data.add(i) } == 0)); + // SAFETY: allocated above. + unsafe { __m2c_rt_wasm_free_memory(p(&mut m)) }; + } + + #[test] + fn m2c_rt_wasm_grow_memory_returns_old_pages_zero_fills_and_fails_with_uint32_max() { + // WasmRuntimeStubs.c:41-63. + let mut m = mem(); + // SAFETY: valid descriptor, freed below. + unsafe { + __m2c_rt_wasm_allocate_memory(p(&mut m), 1, 3, false, 65536); + *m.data = 0xAA; + assert_eq!(__m2c_rt_wasm_grow_memory(p(&mut m), 0), 1, ":47-49"); + assert_eq!(__m2c_rt_wasm_grow_memory(p(&mut m), 1), 1, ":62"); + assert_eq!((m.pages, m.size), (2, 2 * 65536)); + assert_eq!(*m.data, 0xAA, "old contents kept (realloc)"); + assert_eq!(*m.data.add(65536), 0, "new pages zeroed (:57)"); + assert_eq!(m.data.add(m.size as usize), m.data_end, ":59"); + let fail = u64::from(u32::MAX); + assert_eq!( + __m2c_rt_wasm_grow_memory(p(&mut m), 2), + fail, + "past max_pages (:44-46)" + ); + assert_eq!( + __m2c_rt_wasm_grow_memory(p(&mut m), u64::MAX), + fail, + "wrap (:44)" + ); + assert_eq!(m.pages, 2, "a failed grow changes nothing"); + m.page_size = 0; + assert_eq!(__m2c_rt_wasm_grow_memory(p(&mut m), 1), 2); + assert_eq!(m.size, 3 * 65536, "zero page_size defaults to 64 KiB (:50)"); + __m2c_rt_wasm_free_memory(p(&mut m)); + } + } + + #[test] + fn m2c_rt_wasm_free_memory_nulls_the_data_and_zeroes_the_size() { + // WasmRuntimeStubs.c:66-70: pages and max_pages are left as they were. + let mut m = mem(); + // SAFETY: valid descriptor. + unsafe { + __m2c_rt_wasm_allocate_memory(p(&mut m), 1, 3, false, 65536); + __m2c_rt_wasm_free_memory(p(&mut m)); + } + assert!(m.data.is_null()); + assert_eq!((m.size, m.pages, m.max_pages), (0, 1, 3)); + } + + #[test] + fn m2c_rt_wasm_funcref_table_allocates_and_frees() { + // WasmRuntimeStubs.c:73-84 (element size: SD-19). + // SAFETY: an all-zero table descriptor is valid. + let mut t: wasm_rt_funcref_table_t = unsafe { std::mem::zeroed() }; + let tp: *mut std::os::raw::c_void = (&mut t as *mut wasm_rt_funcref_table_t).cast(); + // SAFETY: valid descriptor, freed below. + unsafe { __m2c_rt_wasm_allocate_funcref_table(tp, 4, 10) }; + assert!(!t.data.is_null()); + assert_eq!( + (t.size, t.max_size), + (4, 0), + "max_size is not written (:77)" + ); + // SAFETY: 4 calloc'd elements. + unsafe { + assert!((*t.data.add(3)).module_instance.is_null()); + __m2c_rt_wasm_free_funcref_table(tp); + } + assert!(t.data.is_null(), ":83"); + assert_eq!(t.size, 4, "size is not reset (:81-84)"); + } + + const TRAP_CHILD_ENV: &str = "M2C_WASM_TRAP_CHILD"; + + /// Runs this test in a child process (the trap aborts) and returns its + /// end signal and the files it wrote. + fn trap_child(case: &str) -> (Option, Vec, String) { + use std::os::unix::process::ExitStatusExt; + let dir = tempfile::tempdir().unwrap(); + let out = std::process::Command::new(std::env::current_exe().unwrap()) + .args(["--exact", "ffi::tests::wasm::m2c_rt_wasm_trap_aborts"]) + .args(["--nocapture", "--test-threads=1"]) + .env(TRAP_CHILD_ENV, case) + .env(crate::output::termination::OUTPUT_DIR_ENV, dir.path()) + .output() + .unwrap(); + let mut files: Vec = std::fs::read_dir(dir.path()) + .unwrap() + .map(|e| e.unwrap().file_name().into_string().unwrap()) + .collect(); + files.sort(); + let unknown = std::fs::read_to_string(dir.path().join("map2check_property__unknown")) + .unwrap_or_default(); + (out.status.signal(), files, unknown) + } + + #[test] + fn m2c_rt_wasm_trap_aborts() { + // WasmRuntimeStubs.c:110-116: map2check_error() aborts; it never + // returns. SD-19: with a mode set the verdict is UNKNOWN (the file + // develop's map2check_error leaves for the frontend). + if let Some(case) = std::env::var_os(TRAP_CHILD_ENV) { + started((case == "mode").then_some(AnalysisMode::Memtrack)); + __m2c_rt_wasm_trap(3); + println!("returned"); + return; + } + let (signal, files, unknown) = trap_child("mode"); + assert_eq!(signal, Some(6)); + assert_eq!(files, ["map2check_property__unknown"]); + assert_eq!(unknown, "UNKNOWN\n"); + let (signal, files, _) = trap_child("no-mode"); + assert_eq!(signal, Some(6), "aborts with no mode too"); + assert!( + files.is_empty(), + "nothing is written without a mode: {files:?}" + ); + } + + /// The initialised flag is one process-wide static: the tests that flip it + /// take this lock. + static INIT_FLAG: std::sync::Mutex<()> = std::sync::Mutex::new(()); + #[test] - fn rt_proc_exit_is_a_no_op() { - // SAFETY: the instance is unused. - unsafe { w2c_wasi__snapshot__preview1_proc_exit(std::ptr::null_mut(), 7) }; + fn m2c_rt_wasm_init_free_and_is_initialized_follow_the_stub() { + // WasmRuntimeStubs.c:96-108: initialised from the start, false after + // wasm_rt_free, true after wasm_rt_init, and init/free repeat freely. + let _g = INIT_FLAG.lock().unwrap_or_else(|e| e.into_inner()); + __m2c_rt_wasm_init(); + __m2c_rt_wasm_free(); + __m2c_rt_wasm_free(); + assert!(!__m2c_rt_wasm_is_initialized()); + __m2c_rt_wasm_init(); + __m2c_rt_wasm_init(); + assert!(__m2c_rt_wasm_is_initialized()); } } @@ -2785,14 +2964,14 @@ fn alloca_of(address: usize, size: i32, primitive: i32) { #[test] fn container_is_empty_initially() { let _guard = fresh(); - map2check_init(); + map2check_init(0); assert_eq!(snapshot(|s| s.memtrack.len()), 0); } #[test] fn container_add_item_increases_size() { let _guard = fresh(); - map2check_init(); + map2check_init(0); malloc_of(0x1000, 4); assert_eq!(snapshot(|s| s.memtrack.len()), 1); } @@ -2800,7 +2979,7 @@ fn container_add_item_increases_size() { #[test] fn container_added_item_is_set() { let _guard = fresh(); - map2check_init(); + map2check_init(0); malloc_of(42, 4); assert_eq!(only_memtrack().points_to, 42); } @@ -2808,7 +2987,7 @@ fn container_added_item_is_set() { #[test] fn container_multiple_items_added_do_not_break() { let _guard = fresh(); - map2check_init(); + map2check_init(0); let n = 10_000usize; for i in 0..n { malloc_of(n - i, 4); @@ -2998,3 +3177,1970 @@ fn memtrack_release_after_calloc_leaves_no_leak() { map2check_check_mem_endprog(); assert_ok(); } + +// ── Map2CheckFunctions, TrackBBLog and PropertyGenerator (Task 2.1) ───────── +// +// The C ABI of `Map2CheckFunctions.c`, `TrackBBLog.c` and +// `PropertyGenerator.c` of Map2Check `develop`. Exports that end the run or +// write files with a mode set are pinned in `ffi_golden_tests.rs`, in a +// child process; these tests pin the effect on the analysis state. + +#[test] +fn map2check_init_takes_the_c_sv_comp_argument_and_starts_clean() { + let _guard = fresh(); + // `Map2CheckFunctions.c:29-40`: `isSvComp` only sets `sv_comp`, which + // nothing reads; any value starts the same clean analysis. + for is_sv_comp in [0, 1, -7] { + // SAFETY: fake address is never dereferenced. + unsafe { map2check_map_malloc(addr(0x1000), 8) }; + // SAFETY: C-string literal is valid and null-terminated. + unsafe { map2check_track_bb(3, c"main".as_ptr()) }; + map2check_next_current_step(); + map2check_init(is_sv_comp); + assert_ok(); + snapshot(|s| { + assert!(s.memtrack.is_empty() && s.bbtrack.is_empty()); + assert_eq!(s.current_step(), 0); + }); + assert_eq!(map2check_get_current_step(), 0); + } +} + +#[test] +fn map2check_get_current_step_is_the_c_step_counter() { + let _guard = fresh(); + map2check_init(0); + // `Map2CheckFunctions.c:30,42-43`: 0 after init, +1 per + // `map2check_next_current_step`, and nothing else moves it. + assert_eq!(map2check_get_current_step(), 0); + // SAFETY: C-string literal is valid; fake address is never dereferenced. + unsafe { + map2check_track_bb(3, c"main".as_ptr()); + map2check_map_malloc(addr(0x1000), 8); + } + assert_eq!(map2check_get_current_step(), 0); + map2check_next_current_step(); + map2check_next_current_step(); + assert_eq!(map2check_get_current_step(), 2); + assert_eq!(map2check_get_current_step(), 2, "ler não avança"); + map2check_init(0); + assert_eq!(map2check_get_current_step(), 0); +} + +#[test] +fn map2check_next_current_step_wraps_like_c_unsigned() { + let _guard = fresh(); + map2check_init(0); + state::with_state(|s| { + s.c_step = u32::MAX; + Ok(()) + }) + .unwrap(); + map2check_next_current_step(); + assert_eq!(map2check_get_current_step(), 0); +} + +#[test] +fn map2check_track_bb_appends_each_line_once() { + let _guard = fresh(); + map2check_init(0); + // `Map2CheckFunctions.c:49-55`: a row is appended only when + // `is_in_tracked` finds no row of that line; the first name stays. + // SAFETY: C-string literals are valid and null-terminated. + unsafe { + map2check_track_bb(3, c"main".as_ptr()); + map2check_track_bb(4, c"main".as_ptr()); + map2check_track_bb(3, c"other".as_ptr()); + map2check_track_bb(u32::MAX, c"f".as_ptr()); + } + let rows = snapshot(|s| { + s.bbtrack + .iter() + .map(|r| (r.line, r.function_name.clone())) + .collect::>() + }); + assert_eq!( + rows, + [ + (3, "main".to_owned()), + (4, "main".to_owned()), + (u32::MAX, "f".to_owned()) + ] + ); + assert_eq!(map2check_is_in_trackbb_container(4), 1); + assert_ok(); +} + +#[test] +fn map2check_track_bb_accepts_null_function_name() { + let _guard = fresh(); + map2check_init(0); + // SAFETY: null is an accepted function_name (SD-09). + unsafe { map2check_track_bb(9, std::ptr::null()) }; + assert_eq!( + snapshot(|s| s + .bbtrack + .iter() + .map(|r| r.function_name.clone()) + .collect::>()), + [String::new()] + ); +} + +#[test] +fn map2check_target_function_records_target_reached() { + let _guard = fresh(); + map2check_init(0); + // `Map2CheckFunctions.c:61-65`: TARGET_REACHED at `line` in `func_name`; + // `scope` is not used. Unset mode: recorded, the call returns (SD-15). + // SAFETY: C-string literal is valid and null-terminated. + unsafe { map2check_target_function(c"reach_error".as_ptr(), 3, 22) }; + assert_violated(ViolatedProperty::Reachability, 22, "reach_error"); + // SAFETY: as above; the first violation is final (SD-03). + unsafe { map2check_target_function(c"other".as_ptr(), 0, 30) }; + assert_violated(ViolatedProperty::Reachability, 22, "reach_error"); +} + +#[test] +fn map2check_target_function_accepts_null_name() { + let _guard = fresh(); + start(AnalysisMode::Target); + // SAFETY: null is an accepted name (SD-09). + unsafe { map2check_target_function(std::ptr::null(), 0, 5) }; + assert_violated(ViolatedProperty::Reachability, 5, ""); +} + +#[test] +fn map2check_destroy_releases_the_block_log_and_the_mode_containers_once() { + let _guard = fresh(); + start(AnalysisMode::Memtrack); + // SAFETY: C-string literal is valid; fake address is never dereferenced. + unsafe { + map2check_track_bb(3, c"main".as_ptr()); + map2check_map_malloc(addr(0x1000), 8); + } + // `Map2CheckFunctions.c:77-87`: frees the basic-block log, then + // `analysis_destroy` (memtrack frees its logs), once. + map2check_destroy(); + snapshot(|s| { + assert!(s.bbtrack.is_empty()); + assert!(s.memtrack.is_empty()); + assert!(s.destroyed); + }); + // SAFETY: as above. + unsafe { map2check_track_bb(4, c"main".as_ptr()) }; + map2check_destroy(); + assert_eq!( + snapshot(|s| s.bbtrack.len()), + 1, + "a segunda chamada não faz nada" + ); + assert_ok(); +} + +#[test] +fn map2check_destroy_keeps_records_of_modes_without_containers() { + let _guard = fresh(); + start(AnalysisMode::Assert); + // SAFETY: fake address is never dereferenced. + unsafe { map2check_map_malloc(addr(0x1000), 8) }; + map2check_destroy(); + // AnalysisModeAssert.c:33-35: `analysis_destroy` does nothing. + assert_eq!(snapshot(|s| s.memtrack.len()), 1); +} + +#[test] +fn map2check_exit_without_mode_runs_the_verdict_and_returns() { + let _guard = fresh(); + map2check_init(0); + // SAFETY: fake address is never dereferenced. + unsafe { map2check_map_malloc(addr(0x1000), 8) }; + // `Map2CheckFunctions.c:89-92`: with no error, the verdict of the mode + // runs. Unset mode: recorded, no files, no abort (SD-03, SD-14). + map2check_exit(); + assert_violated(ViolatedProperty::MemsafetyMemtrack, 0, ""); + assert!(snapshot(|s| s.destroyed), "map2check_destroy roda"); +} + +#[test] +fn map2check_exit_skips_the_verdict_after_an_error() { + let _guard = fresh(); + map2check_init(0); + // SAFETY: fake address is never dereferenced. + unsafe { map2check_map_malloc(addr(0x1000), 8) }; + // `:90`: `analysis_is_program_correct` runs only when `gotError` is + // FALSE; `map2check_error` sets it (`:72-75`). + map2check_error(); + assert!(snapshot(|s| s.got_error)); + assert!( + snapshot(|s| s.result.ok), + "nenhuma propriedade é registrada" + ); +} + +#[test] +fn map2check_error_without_mode_returns() { + let _guard = fresh(); + map2check_init(0); + map2check_error(); + snapshot(|s| assert!(s.got_error && s.destroyed)); + map2check_init(0); + assert!(!snapshot(|s| s.got_error), "map2check_init recomeça"); +} + +#[test] +fn write_property_without_output_directory_changes_nothing() { + let _guard = fresh(); + map2check_init(0); + // A test build without `MAP2CHECK_TEST_OUTPUT_DIR` writes nowhere; the + // export never touches the analysis state (`PropertyGenerator.c:50-106` + // only writes files). Files: `ffi_golden_tests.rs`. + // SAFETY: C-string literal is valid and null-terminated. + unsafe { write_property(ViolatedProperty::MemsafetyFree as i32, 7, c"main".as_ptr()) }; + assert_ok(); +} + +#[test] +fn set_false_result_takes_the_c_enum_values() { + let _guard = fresh(); + // `Map2CheckTypes.h:80-91`: UNKNOWN = 1, FALSE_OVERFLOW = 6. + // SAFETY: C-string literal is valid and null-terminated. + unsafe { set_false_result(1, 4, c"main".as_ptr()) }; + assert_violated(ViolatedProperty::Unknown, 4, "main"); + map2check_reset(); + // SAFETY: as above. + unsafe { set_false_result(6, 5, c"main".as_ptr()) }; + assert_violated(ViolatedProperty::Overflow, 5, "main"); + map2check_reset(); + // SAFETY: as above. 10 is `Concurrency`, 11 is no value. + unsafe { set_false_result(10, 6, c"main".as_ptr()) }; + assert_violated(ViolatedProperty::Concurrency, 6, "main"); + map2check_reset(); + // SAFETY: as above. + unsafe { set_false_result(11, 6, c"main".as_ptr()) }; + assert_ok(); +} + +#[test] +fn non_utf8_function_name_is_kept_not_dropped() { + let _guard = fresh(); + // develop prints the name with `%s`; an invalid UTF-8 byte once turned + // the whole name into "". The valid parts are kept now, and the invalid + // byte becomes U+FFFD. + // SAFETY: C-string literal is valid and null-terminated. + unsafe { set_false_result(7, 4, c"ma\xffin".as_ptr()) }; + assert_violated(ViolatedProperty::Assert, 4, "ma\u{FFFD}in"); +} + +#[test] +fn get_current_property_returns_the_c_enum_value() { + let _guard = fresh(); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { set_false_result(3, 1, c"f".as_ptr()) }; + assert_eq!(get_current_property() as i32, 3, "FALSE_DEREF"); +} + +#[test] +fn m2c_rt_witness_entry_points_without_output_directory_change_nothing() { + let _guard = fresh(); + map2check_init(0); + // SAFETY: C-string literal is valid; a NULL log pointer is accepted (D7). + unsafe { + map2check_track_bb(3, c"main".as_ptr()); + __m2c_rt_witness_generate_aux_files(std::ptr::null_mut(), 1); + } + __m2c_rt_witness_generate_confirmation_file(); + assert_ok(); + assert_eq!(snapshot(|s| s.bbtrack.len()), 1); +} + +// ── C runtime ABI: NonDetLog ──────────────────────────────────────────────── +// +// `map2check_nondet_` of develop's `NonDetLog.c`: each call appends a row +// that takes `map2check_get_current_step()` and then advances it +// (`:96-113`), and `nondet_log_to_file` writes `klee_log.csv` from the rows. + +use crate::nondet::c_log::{self, CNondetCall}; + +fn c_nondet_rows() -> Vec { + snapshot(|s| s.c_nondet.clone()) +} + +/// Calls `$export` with the extreme `$value` of its width and sign, reads the +/// row back and compares the stored bits, the type number and the CSV line +/// that `nondet_log_to_file` prints for it. +macro_rules! nondet_roundtrip { + ($test:ident, $call:expr, $value:expr, $kind:expr, $bits:expr, $csv_value:expr) => { + #[test] + fn $test() { + let _guard = fresh(); + map2check_init(0); + // `$call` is a closure over the export under test. + let call = $call; + call($value, c"main".as_ptr()); + let rows = c_nondet_rows(); + assert_eq!(rows.len(), 1); + let row = &rows[0]; + assert_eq!(row.kind, $kind); + assert_eq!(row.value_bits(), $bits); + assert_eq!((row.line, row.scope, row.step), (7, 3, 0)); + assert_eq!(row.function_name(), b"main"); + assert_eq!(map2check_get_current_step(), 1); + assert_eq!( + String::from_utf8(row.csv_row(0)).unwrap(), + format!("0;7;3;main;0;{};{}\n", $csv_value, $kind) + ); + } + }; +} + +// `enum NONDET_TYPE` numbers: Map2CheckTypes.h:41-58. +nondet_roundtrip!( + nondet_roundtrip_int, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_int(7, 3, v, n) }, + i32::MIN, + 0, + 0x8000_0000, + "-2147483648" +); +nondet_roundtrip!( + nondet_roundtrip_char, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_char(7, 3, v, n) }, + -128, + 1, + 0xffff_ff80, + "-128" +); +nondet_roundtrip!( + nondet_roundtrip_pointer, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_pointer(7, 3, v, n) }, + i32::MIN, + 2, + 0x8000_0000, + "-2147483648" +); +nondet_roundtrip!( + nondet_roundtrip_ushort, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_ushort(7, 3, v, n) }, + 65535, + 3, + 65535, + "65535" +); +nondet_roundtrip!( + nondet_roundtrip_long, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_long(7, 3, v, n) }, + i32::MIN, + 4, + 0x8000_0000, + "-2147483648" +); +nondet_roundtrip!( + nondet_roundtrip_unsigned, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_unsigned(7, 3, v, n) }, + u32::MAX, + 5, + 0xffff_ffff, + "4294967295" +); +nondet_roundtrip!( + nondet_roundtrip_ulong, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_ulong(7, 3, v, n) }, + -1, + 6, + 0xffff_ffff, + "-1" +); +nondet_roundtrip!( + nondet_roundtrip_bool, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_bool(7, 3, v, n) }, + 1, + 7, + 1, + "1" +); +nondet_roundtrip!( + nondet_roundtrip_uchar, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_uchar(7, 3, v, n) }, + 255, + 8, + 255, + "255" +); +nondet_roundtrip!( + nondet_roundtrip_pchar, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_pchar(7, 3, v, n) }, + i32::MAX, + 9, + 0x7fff_ffff, + "2147483647" +); +nondet_roundtrip!( + nondet_roundtrip_size_t, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_size_t(7, 3, v, n) }, + i32::MAX, + 10, + 0x7fff_ffff, + "2147483647" +); +nondet_roundtrip!( + nondet_roundtrip_loff_t, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_loff_t(7, 3, v, n) }, + i32::MIN, + 11, + 0x8000_0000, + "-2147483648" +); +nondet_roundtrip!( + nondet_roundtrip_sector_t, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_sector_t(7, 3, v, n) }, + i32::MAX, + 12, + 0x7fff_ffff, + "2147483647" +); +nondet_roundtrip!( + nondet_roundtrip_uint, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_uint(7, 3, v, n) }, + -1, + 13, + 0xffff_ffff, + "-1" +); +nondet_roundtrip!( + nondet_roundtrip_short, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_short(7, 3, v, n) }, + -32768, + 14, + 0xffff_8000, + "-32768" +); +nondet_roundtrip!( + nondet_roundtrip_double, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_double(7, 3, v, n) }, + f64::MAX, + 15, + f64::MAX.to_bits(), + "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000" +); +nondet_roundtrip!( + nondet_roundtrip_float, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_float(7, 3, v, n) }, + f64::from(f32::MAX), + 16, + f64::from(f32::MAX).to_bits(), + "3.40282347e+38" +); +nondet_roundtrip!( + nondet_roundtrip_longlong, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_longlong(7, 3, v, n) }, + i64::MIN, + 17, + 1 << 63, + "-9223372036854775808" +); +nondet_roundtrip!( + nondet_roundtrip_ulonglong, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_ulonglong(7, 3, v, n) }, + u64::MAX, + 18, + u64::MAX, + "18446744073709551615" +); +nondet_roundtrip!( + nondet_roundtrip_u8, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_u8(7, 3, v, n) }, + 255, + 19, + 255, + "255" +); +nondet_roundtrip!( + nondet_roundtrip_u16, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_u16(7, 3, v, n) }, + 65535, + 20, + 65535, + "65535" +); +// U32 prints through `as_unsigned` like UNSIGNED (NonDetLog.c:91). +nondet_roundtrip!( + nondet_roundtrip_u32, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_u32(7, 3, v, n) }, + -1, + 21, + 0xffff_ffff, + "4294967295" +); +nondet_roundtrip!( + nondet_roundtrip_memory_byte, + |v, n| // SAFETY: `n` is a valid C string. + unsafe { map2check_nondet_memory_byte(7, 3, v, n) }, + 255, + 22, + 255, + "255" +); + +#[test] +fn nondet_calls_take_the_step_then_advance_it() { + let _guard = fresh(); + map2check_init(0); + // `NonDetLog.c:96-113`: the row takes `map2check_get_current_step()`, and + // `helper_map2check_nondet_append_element` then advances it. + // SAFETY: C-string literals are valid and null-terminated. + unsafe { + map2check_nondet_int(1, 0, 10, c"main".as_ptr()); + map2check_next_current_step(); + map2check_nondet_int(2, 0, 11, c"main".as_ptr()); + } + let steps: Vec = c_nondet_rows().iter().map(|r| r.step).collect(); + assert_eq!(steps, [0, 2]); + assert_eq!(map2check_get_current_step(), 3); +} + +#[test] +fn nondet_function_name_is_cut_at_35_bytes_and_null_is_empty() { + let _guard = fresh(); + map2check_init(0); + let long = std::ffi::CString::new("f".repeat(40)).unwrap(); + // `strncpy(..., FUNCTION_MAX_LENGTH_NAME = 35)` and `put_string(..., 35)` + // (`NonDetLog.c:78,120`, `Map2CheckTypes.h:20`). + // SAFETY: the C string is valid; a null name is accepted. + unsafe { + map2check_nondet_int(1, 0, 1, long.as_ptr()); + map2check_nondet_int(1, 0, 1, std::ptr::null()); + } + let rows = c_nondet_rows(); + assert_eq!(rows[0].function_name(), "f".repeat(35).as_bytes()); + assert_eq!(rows[1].function_name(), b""); +} + +#[test] +fn nondet_log_init_and_destroy_empty_the_log_and_keep_the_step() { + let _guard = fresh(); + map2check_init(0); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { map2check_nondet_int(1, 0, 1, c"main".as_ptr()) }; + assert_eq!(c_nondet_rows().len(), 1); + // `NonDetLog.c:150-152`: a new container, or a freed one. + nondet_log_init(); + assert!(c_nondet_rows().is_empty()); + assert_eq!(map2check_get_current_step(), 1); + // SAFETY: as above. + unsafe { map2check_nondet_int(1, 0, 1, c"main".as_ptr()) }; + nondet_log_destroy(); + assert!(c_nondet_rows().is_empty()); + // `map2check_init` starts a new log too (`nondet_init`). + // SAFETY: as above. + unsafe { map2check_nondet_int(1, 0, 1, c"main".as_ptr()) }; + map2check_init(0); + assert!(c_nondet_rows().is_empty()); +} + +#[test] +fn nondet_log_does_not_touch_the_json_report_state() { + let _guard = fresh(); + map2check_init(0); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { map2check_nondet_int(1, 0, 1, c"main".as_ptr()) }; + assert!(snapshot( + |s| s.nondets.is_empty() && s.legacy_nondet_logs.is_empty() + )); + assert_eq!(snapshot(|s| s.current_step()), 0); + assert_ok(); +} + +/// A row of every printing arm, in the text `nondet_log_to_file` writes. +fn sample_log() -> Vec { + let rows = [ + (0, 3, 1, 5, "main", 0xffff_fffe_u64), + (5, 17, 2, 9, "main", 5), + (21, 4, 0, 10, "f", 0xffff_ffff), + (15, 8, 1, 11, "g", 2.5_f64.to_bits()), + (16, 9, 1, 12, "g", 0.1_f64.to_bits()), + (17, 1, 0, 13, "h", i64::MIN as u64), + (18, 1, 0, 14, "h", u64::MAX), + (19, 2, 0, 15, "name_of_exactly_thirty_five_chars_xyz", 200), + ]; + rows.iter() + .map(|&(kind, line, scope, step, name, bits)| { + CNondetCall::from_parts(kind, line, scope, step, name.as_bytes(), bits) + }) + .collect() +} + +#[test] +fn klee_log_csv_matches_c_format() { + // `NonDetLog.c:68-112`: `index;line;scope;function;step;value;type\n`, the + // numbers in decimal by hand (`:20-48`). INTEGER `%d` of the int (`:106`); + // UNSIGNED and U32 `%u` (`:91-92`) and once, not twice (`:82-90`); DOUBLE + // `%lf` (`:93-95`); FLOAT `%.9g` of the double narrowed to float (`:96-100`); + // LONGLONG and ULONGLONG at 64 bits (`:101-105`); the name cut at 35. + // Row 0 is INTEGER with the bits of -2 (`%d` prints -2); the name of row 7 + // has 37 characters and is cut at 35. + let expected = "\ +0;3;1;main;5;-2;0\n\ +1;17;2;main;9;5;5\n\ +2;4;0;f;10;4294967295;21\n\ +3;8;1;g;11;2.500000;15\n\ +4;9;1;g;12;0.100000001;16\n\ +5;1;0;h;13;-9223372036854775808;17\n\ +6;1;0;h;14;18446744073709551615;18\n\ +7;2;0;name_of_exactly_thirty_five_chars_x;15;200;19\n"; + let dir = tempfile::tempdir().unwrap(); + assert!(c_log::nondet_log_to_file(&sample_log(), dir.path())); + let written = std::fs::read(dir.path().join("klee_log.csv")).unwrap(); + assert_eq!(String::from_utf8(written).unwrap(), expected); +} + +#[test] +fn nondet_log_to_file_writes_an_empty_file_for_an_empty_log_and_fails_without_a_directory() { + let dir = tempfile::tempdir().unwrap(); + // `fopen("w")` creates the file even when there are no rows (`:55`). + assert!(c_log::nondet_log_to_file(&[], dir.path())); + assert_eq!(std::fs::read(dir.path().join("klee_log.csv")).unwrap(), b""); + // `fopen` fails: FALSE (`:56-58`). + assert!(!c_log::nondet_log_to_file( + &[], + &dir.path().join("does-not-exist") + )); +} + +#[test] +fn m2c_rt_nondet_log_to_file_writes_the_log_of_the_run() { + let _guard = fresh(); + map2check_init(0); + // SAFETY: C-string literals are valid and null-terminated. + unsafe { + map2check_nondet_int(3, 1, -7, c"main".as_ptr()); + map2check_nondet_unsigned(4, 1, 8, c"main".as_ptr()); + } + let dir = tempfile::tempdir().unwrap(); + std::env::set_var(crate::output::termination::OUTPUT_DIR_ENV, dir.path()); + let ok = __m2c_rt_nondet_log_to_file(); + std::env::remove_var(crate::output::termination::OUTPUT_DIR_ENV); + assert_eq!(ok, 1); + assert_eq!( + std::fs::read_to_string(dir.path().join("klee_log.csv")).unwrap(), + "0;3;1;main;0;-7;0\n1;4;1;main;1;8;5\n" + ); +} + +#[test] +fn float_g9_matches_printf() { + // Each expected text is what glibc's `printf("%.9g", (float)d)` printed + // for the same `d` (clang 21 on this host). + let cases: [(f64, &str); 21] = [ + (f64::from(f32::MAX), "3.40282347e+38"), + (1e-30_f32 as f64, "1e-30"), + (0.5, "0.5"), + (1.0, "1"), + (100.0, "100"), + (123_456_789.0, "123456792"), + (1e9, "1e+09"), + (999_999_999.0, "1e+09"), + (0.0001_f32 as f64, "9.99999975e-05"), + (0.00001_f32 as f64, "9.99999975e-06"), + (-0.0, "-0"), + (0.0, "0"), + (f64::from(f32::MIN_POSITIVE), "1.17549435e-38"), + (f64::from(1.4e-45_f32), "1.40129846e-45"), + (16_777_216.0, "16777216"), + (f64::from(0.1_f32), "0.100000001"), + (-2.5, "-2.5"), + (f64::INFINITY, "inf"), + (f64::NEG_INFINITY, "-inf"), + (f64::NAN, "nan"), + (99_999.999_9, "100000"), + ]; + for (value, text) in cases { + assert_eq!(c_log::c_float_g9(value as f32), text, "{value}"); + } + assert_eq!(c_log::c_float_g9(f32::from_bits(0xffc0_0000)), "-nan"); + assert_eq!(c_log::c_float_g9(1e8_f32), "100000000"); + assert_eq!(c_log::c_float_g9(123_456.79_f32), "123456.789"); +} + +#[test] +fn nondet_log_hooks_mirror_the_klee_concretization() { + let _guard = fresh(); + map2check_init(0); + assert_eq!(__m2c_rt_nondet_log_len(), 0); + // SAFETY: C-string literals are valid and null-terminated. + unsafe { + map2check_nondet_int(1, 0, -2, c"main".as_ptr()); + map2check_nondet_double(2, 0, 1.5, c"main".as_ptr()); + map2check_nondet_longlong(3, 0, i64::MIN, c"main".as_ptr()); + } + assert_eq!(__m2c_rt_nondet_log_len(), 3); + // `memcpy(&symbolic, &call->value, 8)` (NonDetGeneratorKlee.c:39): the int + // is zero-extended by new_nondet_call's memset, the others are bit patterns. + assert_eq!(__m2c_rt_nondet_log_get_i64(0), 0xffff_fffe); + assert_eq!(__m2c_rt_nondet_log_get_i64(1), 1.5_f64.to_bits() as i64); + assert_eq!(__m2c_rt_nondet_log_get_i64(2), i64::MIN); + assert_eq!(__m2c_rt_nondet_log_get_i64(3), 0, "out of range reads 0"); + // The write-back replaces all 8 bytes (`:42`), and the file shows it. + __m2c_rt_nondet_log_set_i64(0, 5); + __m2c_rt_nondet_log_set_i64(1, 2.5_f64.to_bits() as i64); + __m2c_rt_nondet_log_set_i64(2, -1); + __m2c_rt_nondet_log_set_i64(9, 7); + assert_eq!(__m2c_rt_nondet_log_len(), 3); + let dir = tempfile::tempdir().unwrap(); + assert!(c_log::nondet_log_to_file(&c_nondet_rows(), dir.path())); + assert_eq!( + std::fs::read_to_string(dir.path().join("klee_log.csv")).unwrap(), + "0;1;0;main;0;5;0\n1;2;0;main;1;2.500000;15\n2;3;0;main;2;-1;17\n" + ); +} + +#[test] +fn legacy_nondet_log_reaches_klee_log_csv_through_the_one_c_log_writer() { + use crate::output::legacy_files::LegacyLogs; + let _guard = fresh(); + map2check_init(0); + let mut u: std::os::raw::c_uint = u32::MAX; + let mut d: f64 = 2.5; + let long_name = std::ffi::CString::new("n".repeat(40)).unwrap(); + // SAFETY: the pointers are valid for the calls. + unsafe { + map2check_save_nondet_log_unsigned(3, 1, 5, &mut u, c"main".as_ptr()); + map2check_save_nondet_log_double(4, 1, 15, &mut d, long_name.as_ptr()); + } + let logs = snapshot(|s| LegacyLogs::replay(&s.memtrack, &s.nondets, None, false)); + // The file the witness path writes is exactly what `c_log` formats for + // the rows of the legacy log: there is no second formatter. + let dir = tempfile::tempdir().unwrap(); + logs.write_nondet_log(dir.path()).unwrap(); + let mut expected = Vec::new(); + for (i, call) in logs.nondet_calls().iter().enumerate() { + expected.extend_from_slice(&call.csv_row(i)); + } + assert_eq!( + std::fs::read(dir.path().join("klee_log.csv")).unwrap(), + expected + ); + // `strncpy(..., 35)`: the C name cut now applies to the legacy API too. + assert_eq!( + String::from_utf8(expected).unwrap(), + format!( + "0;3;1;main;0;4294967295;5\n1;4;1;{};1;2.500000;15\n", + "n".repeat(35) + ) + ); +} + +// ── C runtime ABI: AnalysisModeOverflow (core entry points, D7) ───────────── +// +// `AnalysisModeOverflow.bc` is generated IR whose functions forward to these +// `__m2c_rt_overflow_*` entry points (scripts/runtime-ir/gen_ll.py). The C +// semantics are `AnalysisModeOverflow.c` of Map2Check `develop`; the files +// they write are pinned by the golden tests (`ffi_golden_tests.rs`). + +/// `map2check_binop_{add,sub,mul,sdiv,srem}` of the C runtime +/// (bodies at `AnalysisModeOverflow.c:197-217`, `:234-243`, `:250-276`). +type RtSignedBinop = unsafe extern "C" fn( + i64, + i64, + std::os::raw::c_uint, + std::os::raw::c_uint, + std::os::raw::c_uint, + *const std::os::raw::c_char, +); + +/// `map2check_binop_{add,sub,mul}_uint` (bodies at +/// `AnalysisModeOverflow.c:224-232`, `:245-248`). +type RtUnsignedBinop = unsafe extern "C" fn( + u64, + u64, + std::os::raw::c_uint, + std::os::raw::c_uint, + std::os::raw::c_uint, + *const std::os::raw::c_char, +); + +const INT_MAX: i64 = i32::MAX as i64; +const INT_MIN: i64 = i32::MIN as i64; +const UINT_MAX: u64 = u32::MAX as u64; + +/// Calls `op` on a fresh state as the pass does (width 32, scope 0) at +/// `line` in `main` and returns the recorded verdict. +fn rt_signed(op: RtSignedBinop, a: i64, b: i64, width: u32, line: u32) -> Option

{ + state::reset(); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { op(a, b, width, line, 0, c"main".as_ptr()) }; + let result = snapshot(|s| s.result.clone()); + if result.ok { + return None; + } + assert_eq!(result.line, line); + assert_eq!(result.function_name, "main"); + Some(result.property) +} + +#[test] +fn overflow_rt_binops_report_the_c_tag_at_the_boundaries() { + let _guard = fresh(); + let add: RtSignedBinop = __m2c_rt_overflow_map2check_binop_add; + let sub: RtSignedBinop = __m2c_rt_overflow_map2check_binop_sub; + let mul: RtSignedBinop = __m2c_rt_overflow_map2check_binop_mul; + let sdiv: RtSignedBinop = __m2c_rt_overflow_map2check_binop_sdiv; + let srem: RtSignedBinop = __m2c_rt_overflow_map2check_binop_srem; + // overflowError (FALSE_OVERFLOW, `:185-188`) and divisionByZeroError + // (FALSE_DIVBYZERO, `:192-195`) as the C binops call them (`:197-276`). + let cases: [(&str, RtSignedBinop, i64, i64, Option

); 16] = [ + ("INT_MAX + 1", add, INT_MAX, 1, Some(P::Overflow)), + ("INT_MAX + 0", add, INT_MAX, 0, None), + ("INT_MIN - 1", sub, INT_MIN, 1, Some(P::Overflow)), + ("INT_MIN - 0", sub, INT_MIN, 0, None), + ("INT_MAX * 2", mul, INT_MAX, 2, Some(P::Overflow)), + ("INT_MIN * -1", mul, INT_MIN, -1, Some(P::Overflow)), + ("INT_MAX * -1", mul, INT_MAX, -1, None), + ("INT_MIN / -1", sdiv, INT_MIN, -1, Some(P::Overflow)), + ("INT_MIN / 1", sdiv, INT_MIN, 1, None), + ("7 / 0", sdiv, 7, 0, Some(P::DivByZero)), + ("0 / 0", sdiv, 0, 0, Some(P::DivByZero)), + ("7 % 0", srem, 7, 0, Some(P::DivByZero)), + ("INT_MIN % 0", srem, INT_MIN, 0, Some(P::DivByZero)), + ("INT_MIN % -1", srem, INT_MIN, -1, Some(P::Overflow)), + ("INT_MAX % -1", srem, INT_MAX, -1, None), + ("-5 % 3", srem, -5, 3, None), + ]; + for (name, op, a, b, expected) in cases { + assert_eq!(rt_signed(op, a, b, 32, 20), expected, "{name}"); + } +} + +#[test] +fn overflow_rt_binops_use_the_width_like_c() { + let _guard = fresh(); + let add: RtSignedBinop = __m2c_rt_overflow_map2check_binop_add; + let sub: RtSignedBinop = __m2c_rt_overflow_map2check_binop_sub; + let mul: RtSignedBinop = __m2c_rt_overflow_map2check_binop_mul; + let sdiv: RtSignedBinop = __m2c_rt_overflow_map2check_binop_sdiv; + // fits_in_width (`:126-134`) after the 64-bit predicates (`:141-183`); + // width 0 or >= 64 means 64 bits (`:111-124`). + assert_eq!(rt_signed(add, 127, 1, 8, 3), Some(P::Overflow)); + assert_eq!(rt_signed(add, 126, 1, 8, 3), None); + assert_eq!(rt_signed(sub, -32768, 1, 16, 3), Some(P::Overflow)); + assert_eq!(rt_signed(add, INT_MAX, 1, 64, 3), None); + assert_eq!(rt_signed(add, i64::MAX, 1, 64, 3), Some(P::Overflow)); + assert_eq!(rt_signed(add, i64::MAX, 1, 0, 3), Some(P::Overflow)); + assert_eq!(rt_signed(mul, i64::MAX, 2, 8, 3), Some(P::Overflow)); + assert_eq!(rt_signed(sub, i64::MIN, 1, 64, 3), Some(P::Overflow)); + // Only the width's own minimum overflows the division (`:259`). + assert_eq!(rt_signed(sdiv, INT_MIN, -1, 64, 3), None); + assert_eq!(rt_signed(sdiv, i64::MIN, -1, 64, 3), Some(P::Overflow)); + assert_eq!(rt_signed(sdiv, -128, -1, 8, 3), Some(P::Overflow)); +} + +#[test] +fn overflow_rt_unsigned_binops_never_report_like_c() { + let _guard = fresh(); + // "Unsigned wraparound is well-defined in C and is not an overflow under + // SV-COMP's no-overflow property, so these remain deliberate no-ops" + // (`AnalysisModeOverflow.c:219-248`). + let ops: [(&str, RtUnsignedBinop); 3] = [ + ("add_uint", __m2c_rt_overflow_map2check_binop_add_uint), + ("sub_uint", __m2c_rt_overflow_map2check_binop_sub_uint), + ("mul_uint", __m2c_rt_overflow_map2check_binop_mul_uint), + ]; + for (name, op) in ops { + for (a, b) in [(UINT_MAX, 1), (0, 1), (UINT_MAX, 2), (u64::MAX, u64::MAX)] { + state::reset(); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { op(a, b, 32, 20, 0, c"main".as_ptr()) }; + assert_ok(); + assert!(snapshot(|s| !s.got_error), "{name}({a}, {b})"); + } + // A null name is accepted: the C body never reads it. + // SAFETY: null is allowed. + unsafe { op(UINT_MAX, 1, 32, 20, 0, std::ptr::null()) }; + assert_ok(); + } +} + +#[test] +fn overflow_rt_overflow_error_records_false_overflow() { + let _guard = fresh(); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { __m2c_rt_overflow_overflowError(12, c"f".as_ptr()) }; + assert_violated(ViolatedProperty::Overflow, 12, "f"); + state::reset(); + // SAFETY: null is allowed. + unsafe { __m2c_rt_overflow_overflowError(13, std::ptr::null()) }; + assert_violated(ViolatedProperty::Overflow, 13, ""); +} + +#[test] +fn overflow_rt_first_violation_is_final() { + let _guard = fresh(); + // SAFETY: C-string literals are valid and null-terminated. + unsafe { + __m2c_rt_overflow_map2check_binop_srem(1, 0, 32, 4, 0, c"f".as_ptr()); + __m2c_rt_overflow_map2check_binop_add(INT_MAX, 1, 32, 9, 0, c"g".as_ptr()); + __m2c_rt_overflow_overflowError(10, c"h".as_ptr()); + } + assert_violated(ViolatedProperty::DivByZero, 4, "f"); +} + +#[test] +fn overflow_rt_binops_check_whatever_mode_is_set() { + let _guard = fresh(); + // The C module has no mode test: being linked is what selects it + // (`caller.cpp:575-612`), so the core entry points always check. + for mode in MODES { + state::reset(); + map2check_set_mode(mode as i32); + map2check_init(0); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { __m2c_rt_overflow_map2check_binop_add(INT_MAX, 1, 32, 4, 0, c"f".as_ptr()) }; + assert_violated(ViolatedProperty::Overflow, 4, "f"); + } +} + +#[test] +fn overflow_rt_analysis_entry_points_match_c() { + let _guard = fresh(); + map2check_init(0); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { map2check_track_bb(3, c"main".as_ptr()) }; + // analysis_init (`:279`) is empty in C; linking the module is what picks + // the overflow mode, so the core entry point selects it. + __m2c_rt_overflow_analysis_init(); + assert_eq!(state::config().mode, Some(AnalysisMode::Overflow)); + // analysis_is_program_correct (`:283`): always TRUE (1). + assert_eq!(__m2c_rt_overflow_analysis_is_program_correct(), 1); + // analysis_destroy (`:281`), analysis_generate_aux_witness_files + // (`:285`): empty. + __m2c_rt_overflow_analysis_destroy(); + __m2c_rt_overflow_analysis_generate_aux_witness_files(); + assert_ok(); + assert_eq!(snapshot(|s| s.bbtrack.len()), 1); + assert_eq!(__m2c_rt_overflow_analysis_is_program_correct(), 1); + // Even after a violation: the C verdict function reads no state. + // SAFETY: C-string literal is valid and null-terminated. + unsafe { __m2c_rt_overflow_overflowError(1, c"f".as_ptr()) }; + assert_eq!(__m2c_rt_overflow_analysis_is_program_correct(), 1); +} + +#[test] +fn overflow_rt_binops_called_as_the_ir_forwarders_do() { + let _guard = fresh(); + let main = c"main".as_ptr(); + // Safe operations, each entry point called directly with the C + // arguments the pass gives `map2check_binop_*` (operands, width, line, + // scope, name). + // SAFETY: C-string literal is valid and null-terminated. + unsafe { + __m2c_rt_overflow_map2check_binop_add(-5, 3, 32, 1, 0, main); + __m2c_rt_overflow_map2check_binop_sub(-5, 3, 32, 1, 0, main); + __m2c_rt_overflow_map2check_binop_mul(-5, 3, 32, 1, 0, main); + __m2c_rt_overflow_map2check_binop_sdiv(-5, 3, 32, 1, 0, main); + __m2c_rt_overflow_map2check_binop_srem(-5, 3, 32, 1, 0, main); + } + __m2c_rt_overflow_map2check_binop_add_uint(UINT_MAX, 1, 32, 1, 0, main); + __m2c_rt_overflow_map2check_binop_sub_uint(0, 1, 32, 1, 0, main); + __m2c_rt_overflow_map2check_binop_mul_uint(UINT_MAX, 2, 32, 1, 0, main); + assert_ok(); + // SAFETY: as above. + unsafe { __m2c_rt_overflow_map2check_binop_sub(INT_MIN, 1, 32, 2, 0, main) }; + assert_violated(ViolatedProperty::Overflow, 2, "main"); + state::reset(); + // SAFETY: as above. + unsafe { __m2c_rt_overflow_map2check_binop_mul(INT_MAX, 2, 32, 3, 0, main) }; + assert_violated(ViolatedProperty::Overflow, 3, "main"); + state::reset(); + // SAFETY: as above. + unsafe { __m2c_rt_overflow_map2check_binop_sdiv(INT_MIN, -1, 32, 4, 0, main) }; + assert_violated(ViolatedProperty::Overflow, 4, "main"); +} + +// ── C runtime ABI: AnalysisModeAssert and AnalysisModeNone (core entry points, D7) ── +// +// `AnalysisModeAssert.bc` and `AnalysisModeNone.bc` are generated IR +// (scripts/runtime-ir/gen_ll.py). Their `analysis_init` forwards to the +// `__m2c_rt__analysis_init` below, which selects the mode; the files +// are pinned by the golden tests (`ffi_golden_tests.rs`). + +#[test] +fn assert_rt_analysis_entry_points_match_c() { + let _guard = fresh(); + map2check_init(0); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { map2check_track_bb(3, c"main".as_ptr()) }; + // AnalysisModeAssert.c:29-31 analysis_init is empty; linking the module + // picks the assert mode. + __m2c_rt_assert_analysis_init(); + assert_eq!(state::config().mode, Some(AnalysisMode::Assert)); + // :37 analysis_is_program_correct is TRUE; :33-35 and :39-41 are empty. + assert_eq!(__m2c_rt_assert_analysis_is_program_correct(), 1); + __m2c_rt_assert_analysis_destroy(); + __m2c_rt_assert_analysis_generate_aux_witness_files(); + assert_ok(); + assert_eq!(snapshot(|s| s.bbtrack.len()), 1); + // Even after a violation: the C verdict function reads no state. + // SAFETY: as above. + unsafe { __m2c_rt_assert_map2check_assert(0, 1, c"f".as_ptr()) }; + assert_eq!(__m2c_rt_assert_analysis_is_program_correct(), 1); +} + +#[test] +fn assert_rt_map2check_assert_zero_records_assert_and_one_does_not() { + let _guard = fresh(); + map2check_init(0); + __m2c_rt_assert_analysis_init(); + // AnalysisModeAssert.c:18-23: only `!condition` is a violation. + for condition in [1, 2, -1, i32::MAX] { + // SAFETY: C-string literal is valid and null-terminated. + unsafe { __m2c_rt_assert_map2check_assert(condition, 9, c"main".as_ptr()) }; + assert_ok(); + } + // SAFETY: as above. + unsafe { __m2c_rt_assert_map2check_assert(0, 21, c"main".as_ptr()) }; + assert_violated(ViolatedProperty::Assert, 21, "main"); + // A NULL name reads as "" (the C would crash in fprintf %s). + state::reset(); + // SAFETY: NULL is accepted. + unsafe { __m2c_rt_assert_map2check_assert(0, 22, std::ptr::null()) }; + assert_violated(ViolatedProperty::Assert, 22, ""); +} + +#[test] +fn none_rt_analysis_init_selects_the_none_mode_and_the_verdict_is_true() { + let _guard = fresh(); + map2check_init(0); + // AnalysisModeNone.c:11-13 is empty; REACHABILITY_MODE and + // COVER_BRANCHES_MODE both link this module (caller.cpp:600-612). + __m2c_rt_none_analysis_init(); + assert_eq!(state::config().mode, Some(AnalysisMode::None)); + // SAFETY: C-string literal is valid and null-terminated. + unsafe { map2check_target_function(c"reach_error".as_ptr(), 0, 22) }; + assert_violated(ViolatedProperty::Reachability, 22, "reach_error"); + // :19 analysis_is_program_correct is TRUE, :15-17 and :21-23 are empty. + assert_eq!(__m2c_rt_none_analysis_is_program_correct(), 1); + __m2c_rt_none_analysis_destroy(); + __m2c_rt_none_analysis_generate_aux_witness_files(); +} + +// ── C runtime ABI: AnalysisModeMemtrack (core entry points, Task 2.5) ─────── +// +// `AnalysisModeMemtrack.bc` forwards the C names to `__m2c_rt_memtrack_*`. +// Each test pins the C behaviour of `AnalysisModeMemtrack.c`, +// `AllocationLog.c`, `ListLog.c` and `HeapLog.c`: the verdict, the property +// recorded and the rows of the three logs, as the C formats them. + +/// `map2check_init` (which in C calls `analysis_init`), then the module's +/// `analysis_init`, which selects the memtrack mode. +fn memtrack_rt() -> MutexGuard<'static, ()> { + let guard = fresh(); + map2check_init(0); + __m2c_rt_memtrack_analysis_init(); + guard +} + +fn c_list_log() -> String { + snapshot(|s| String::from_utf8(s.c_memory.list_log_bytes()).unwrap()) +} + +fn c_allocation_log() -> String { + snapshot(|s| String::from_utf8(s.c_memory.allocation_log_bytes()).unwrap()) +} + +fn c_heap_log() -> String { + snapshot(|s| String::from_utf8(s.c_memory.heap_log_bytes()).unwrap()) +} + +fn c_error_deref() -> bool { + snapshot(|s| s.c_memory.error_deref()) +} + +/// `golden_memory()` of `tests/legacy_golden/drivers/golden.h` through the +/// core entry points: `int *p` at 0x1000 (8 bytes), `&main` at 0x1040, +/// `p = malloc(16)` at 0x1100. +fn rt_prelude() { + // SAFETY: fake addresses are only stored and compared; names are C strings. + unsafe { + __m2c_rt_memtrack_map2check_alloca(c"p".as_ptr(), addr(0x1000), 8, 8, 10, 1); + __m2c_rt_memtrack_map2check_function(c"main".as_ptr(), addr(0x1040)); + __m2c_rt_memtrack_map2check_malloc(addr(0x1100), 16); + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(0x1000), + addr(0x1100), + 1, + c"p".as_ptr(), + 11, + c"main".as_ptr(), + ); + } +} + +#[test] +fn memtrack_rt_analysis_init_selects_the_mode_and_starts_empty_logs() { + let _guard = memtrack_rt(); + assert_eq!(state::config().mode, Some(AnalysisMode::Memtrack)); + rt_prelude(); + __m2c_rt_memtrack_map2check_load(addr(0x9000), 4); + assert!(c_error_deref()); + // AnalysisModeMemtrack.c:125-130: new containers, ERROR_DEREF = FALSE. + __m2c_rt_memtrack_analysis_init(); + assert_eq!(c_list_log(), ""); + assert_eq!(c_allocation_log(), ""); + assert_eq!(c_heap_log(), ""); + assert!(!c_error_deref()); +} + +#[test] +fn memtrack_rt_logs_have_the_c_rows_and_formats() { + let _guard = memtrack_rt(); + rt_prelude(); + // HeapLog.c:54-66 `%p;%s;%d\n`; map2check_function is size 1 (:393-397). + assert_eq!(c_heap_log(), "0x1000;p;8\n0x1040;main;1\n"); + // AllocationLog.c:161-174 `%p;%u;%d\n`. + assert_eq!(c_allocation_log(), "0x1100;16;0\n"); + // ListLog.c:138-157: id (always 0), var, points to, scope, is_free, + // is_dynamic, name, line, function, step (the C step counter, 0 here). + assert_eq!(c_list_log(), "0;0x1000;0x1100;1;0;1;p;11;main;0\n"); + assert_eq!(map2check_get_current_step(), 1); +} + +#[test] +fn memtrack_rt_list_rows_share_the_step_counter_with_the_nondet_log() { + let _guard = memtrack_rt(); + // SAFETY: C-string literal is valid. + unsafe { map2check_nondet_int(5, 0, 42, c"main".as_ptr()) }; + rt_prelude(); + assert_eq!(c_list_log(), "0;0x1000;0x1100;1;0;1;p;11;main;1\n"); +} + +#[test] +fn memtrack_rt_malloc_updates_the_row_of_a_known_address_in_place() { + let _guard = memtrack_rt(); + __m2c_rt_memtrack_map2check_malloc(addr(0x2000), 8); + __m2c_rt_memtrack_map2check_malloc(addr(0x3000), 4); + __m2c_rt_memtrack_map2check_malloc(addr(0x2000), 32); + // calloc is malloc of quantity * size (:347-349), as C int. + __m2c_rt_memtrack_map2check_calloc(addr(0x4000), 3, 5); + __m2c_rt_memtrack_map2check_calloc(addr(0x5000), 0x10000, 0x10001); + assert_eq!( + c_allocation_log(), + "0x2000;32;0\n0x3000;4;0\n0x4000;15;0\n0x5000;65536;0\n" + ); + assert_ok(); +} + +#[test] +fn memtrack_rt_malloc_of_null_is_unknown() { + let _guard = memtrack_rt(); + __m2c_rt_memtrack_map2check_malloc(std::ptr::null(), 4); + // :317-323: write_property_unknown + map2check_error, nothing recorded. + assert_violated(ViolatedProperty::Unknown, 0, ""); + assert_eq!(c_allocation_log(), ""); + // `int addr = (long)ptr`: a block whose low 32 bits are zero is NULL too. + state::reset(); + __m2c_rt_memtrack_map2check_calloc(addr(0x7_0000_0000), 1, 4); + assert_violated(ViolatedProperty::Unknown, 0, ""); +} + +#[test] +fn memtrack_rt_non_static_alloca_multiplies_size_by_primitive() { + let _guard = memtrack_rt(); + // SAFETY: fake address; C-string literal. + unsafe { + __m2c_rt_memtrack_map2check_non_static_alloca(c"vla".as_ptr(), addr(0x6000), 5, 4, 3, 1) + }; + // :380-384: size * size_of_primitive bytes. + assert_eq!(c_heap_log(), "0x6000;vla;20\n"); + __m2c_rt_memtrack_map2check_load(addr(0x6010), 4); + assert!(!c_error_deref()); + __m2c_rt_memtrack_map2check_load(addr(0x6011), 4); + assert!(c_error_deref()); +} + +#[test] +fn memtrack_rt_load_and_check_deref_follow_error_deref() { + let _guard = memtrack_rt(); + rt_prelude(); + // SAFETY (all calls below): fake addresses; C-string literals. + unsafe { + // Inside the stack slot, the function and the heap block: valid. + __m2c_rt_memtrack_map2check_load(addr(0x1000), 8); + __m2c_rt_memtrack_map2check_load(addr(0x1040), 1); + __m2c_rt_memtrack_map2check_load(addr(0x110c), 4); + __m2c_rt_memtrack_map2check_check_deref(14, c"main".as_ptr()); + } + assert!(!c_error_deref()); + assert_ok(); + // One byte past the block: map2check_load only sets ERROR_DEREF (:168-174). + __m2c_rt_memtrack_map2check_load(addr(0x110d), 4); + assert!(c_error_deref()); + assert_ok(); + // SAFETY: as above. + unsafe { + // ERROR_DEREF stays set; check_deref reports it (:309-315). + __m2c_rt_memtrack_map2check_load(addr(0x1100), 4); + __m2c_rt_memtrack_map2check_check_deref(14, c"main".as_ptr()); + } + assert_violated(ViolatedProperty::MemsafetyDeref, 14, "main"); +} + +#[test] +fn memtrack_rt_load_of_a_freed_block_is_an_invalid_deref() { + let _guard = memtrack_rt(); + rt_prelude(); + // SAFETY: fake addresses; C-string literals. + unsafe { + __m2c_rt_memtrack_map2check_free_resolved_address(addr(0x1100), 12, c"main".as_ptr(), 0); + __m2c_rt_memtrack_map2check_load(addr(0x1100), 1); + __m2c_rt_memtrack_map2check_check_deref(13, c"g".as_ptr()); + } + assert_violated(ViolatedProperty::MemsafetyDeref, 13, "g"); +} + +#[test] +fn memtrack_rt_check_cstring_reads_up_to_the_terminator_inside_the_block() { + let _guard = memtrack_rt(); + let inside: [u8; 4] = *b"ab\0x"; + let beyond: [u8; 4] = *b"abcd"; + let a = inside.as_ptr() as usize; + let b = beyond.as_ptr() as usize; + // SAFETY: the buffers are live; names are C strings. + unsafe { + __m2c_rt_memtrack_map2check_alloca(c"s".as_ptr(), addr(a), 4, 1, 1, 1); + __m2c_rt_memtrack_map2check_alloca(c"t".as_ptr(), addr(b), 4, 1, 2, 1); + __m2c_rt_memtrack_map2check_check_cstring(std::ptr::null()); + __m2c_rt_memtrack_map2check_check_cstring(inside.as_ptr().cast()); + } + assert!(!c_error_deref()); + // No terminator inside the 4 bytes: the fifth byte is outside every + // block, so ERROR_DEREF is set before it is read (:159-166). + // SAFETY: only the 4 tracked bytes are read. + unsafe { __m2c_rt_memtrack_map2check_check_cstring(beyond.as_ptr().cast()) }; + assert!(c_error_deref()); + // SAFETY: C-string literal. + unsafe { __m2c_rt_memtrack_map2check_check_deref(30, c"puts".as_ptr()) }; + assert_violated(ViolatedProperty::MemsafetyDeref, 30, "puts"); +} + +/// A live pointer slot holding `value`, as `map2check_free` reads it. +fn slot(value: usize) -> Box { + Box::new(value) +} + +#[test] +fn memtrack_rt_free_releases_the_block_and_logs_the_free_row() { + let _guard = memtrack_rt(); + let p = slot(0x1100); + let p_addr = &*p as *const usize as usize; + // SAFETY: `p` is live; the rest are fake addresses and C strings. + unsafe { + __m2c_rt_memtrack_map2check_alloca(c"p".as_ptr(), addr(p_addr), 8, 8, 10, 1); + __m2c_rt_memtrack_map2check_malloc(addr(0x1100), 16); + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(p_addr), + addr(0x1100), + 1, + c"p".as_ptr(), + 11, + c"main".as_ptr(), + ); + __m2c_rt_memtrack_map2check_free(c"p".as_ptr(), addr(p_addr), 1, 12, c"main".as_ptr()); + } + assert_ok(); + // :351-378: a FREE row for the variable, then the row of the block is + // freed with size 0. + assert_eq!( + c_list_log(), + format!("0;{p_addr:#x};0x1100;1;0;1;p;11;main;0\n0;{p_addr:#x};0x1100;1;1;0;p;12;main;1\n") + ); + assert_eq!(c_allocation_log(), "0x1100;0;1\n"); + assert_eq!(__m2c_rt_memtrack_analysis_is_program_correct(), 1); +} + +#[test] +fn memtrack_rt_double_free_is_a_false_free_after_its_row_is_logged() { + let _guard = memtrack_rt(); + let p = slot(0x1100); + let p_addr = &*p as *const usize as usize; + // SAFETY: `p` is live; the rest are fake addresses and C strings. + unsafe { + __m2c_rt_memtrack_map2check_malloc(addr(0x1100), 16); + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(p_addr), + addr(0x1100), + 1, + c"p".as_ptr(), + 11, + c"main".as_ptr(), + ); + __m2c_rt_memtrack_map2check_free(c"p".as_ptr(), addr(p_addr), 1, 12, c"main".as_ptr()); + __m2c_rt_memtrack_map2check_free(c"p".as_ptr(), addr(p_addr), 1, 13, c"main".as_ptr()); + } + assert_violated(ViolatedProperty::MemsafetyFree, 13, "main"); + // The failing free appended its FREE row before the check (:358-365). + assert_eq!(c_list_log().lines().count(), 3); + assert!(c_list_log().ends_with("0;p;13;main;2\n")); +} + +#[test] +fn memtrack_rt_free_of_a_non_heap_address_is_a_false_free() { + let _guard = memtrack_rt(); + let x = 7u64; + let x_addr = &x as *const u64 as usize; + let p = slot(x_addr); + let p_addr = &*p as *const usize as usize; + // SAFETY: `p` is live; names are C strings. + unsafe { + __m2c_rt_memtrack_map2check_alloca(c"x".as_ptr(), addr(x_addr), 8, 8, 3, 1); + // `p = &x`: a STATIC row (check_address_allocation_log, AllocationLog.c:33-51). + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(p_addr), + addr(x_addr), + 1, + c"p".as_ptr(), + 4, + c"main".as_ptr(), + ); + __m2c_rt_memtrack_map2check_free(c"p".as_ptr(), addr(p_addr), 1, 5, c"main".as_ptr()); + } + // is_invalid_free: the newest row pointing to x is not dynamic (ListLog.c:95-118). + assert_violated(ViolatedProperty::MemsafetyFree, 5, "main"); +} + +#[test] +fn memtrack_rt_free_of_a_null_pointer_does_nothing() { + let _guard = memtrack_rt(); + let p = slot(0); + // SAFETY: `p` is live; C strings. + unsafe { + __m2c_rt_memtrack_map2check_free( + c"p".as_ptr(), + (&*p as *const usize).cast(), + 1, + 5, + c"main".as_ptr(), + ) + }; + assert_ok(); + assert_eq!(c_list_log(), ""); +} + +#[test] +fn memtrack_rt_free_resolved_address_follows_is_invalid_free() { + let _guard = memtrack_rt(); + // SAFETY (all calls below): fake addresses; C strings; NULL accepted. + unsafe { + // NULL with isNullValid returns at once (:179-181). + __m2c_rt_memtrack_map2check_free_resolved_address(std::ptr::null(), 1, c"f".as_ptr(), 1); + assert_ok(); + // A tracked, stored block is a valid free. + rt_prelude(); + __m2c_rt_memtrack_map2check_free_resolved_address(addr(0x1100), 12, c"main".as_ptr(), 0); + } + assert_ok(); + // update_reference_list_log(FREE) re-points `p` at the freed block (:197-256). + assert_eq!( + c_list_log(), + "0;0x1000;0x1100;1;0;1;p;11;main;0\n0;0x1000;0x1100;1;1;0;p;12;main;1\n" + ); + assert_eq!(c_allocation_log(), "0x1100;0;1\n"); + // SAFETY: as above. + unsafe { + // Again: the newest row pointing to the block is free. + __m2c_rt_memtrack_map2check_free_resolved_address(addr(0x1100), 13, c"main".as_ptr(), 0) + }; + assert_violated(ViolatedProperty::MemsafetyFree, 13, "main"); +} + +#[test] +fn memtrack_rt_free_resolved_of_an_untracked_or_never_stored_block_is_a_false_free() { + let _guard = memtrack_rt(); + // SAFETY: fake address; C string. + unsafe { + __m2c_rt_memtrack_map2check_free_resolved_address(addr(0x5000), 3, c"main".as_ptr(), 0) + }; + assert_violated(ViolatedProperty::MemsafetyFree, 3, "main"); + // free(malloc(8)): no list row points to the block, so is_invalid_free + // is TRUE (ListLog.c:117) — develop's verdict, unlike SD-13 of the legacy + // `map2check_check_free_resolved_address`. + state::reset(); + // SAFETY: as above. + unsafe { + __m2c_rt_memtrack_map2check_malloc(addr(0x6000), 8); + __m2c_rt_memtrack_map2check_free_resolved_address(addr(0x6000), 4, c"main".as_ptr(), 0); + } + assert_violated(ViolatedProperty::MemsafetyFree, 4, "main"); +} + +#[test] +fn memtrack_rt_add_store_pointer_skips_redundant_rows_and_updates_references() { + let _guard = memtrack_rt(); + // SAFETY (all calls below): fake addresses; C strings. + unsafe { + __m2c_rt_memtrack_map2check_malloc(addr(0xA0), 8); + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(0x10), + addr(0xA0), + 1, + c"p".as_ptr(), + 1, + c"f".as_ptr(), + ); + // Same var, value and flags: redundant (:285-297), no row, no step. + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(0x10), + addr(0xA0), + 1, + c"p".as_ptr(), + 2, + c"f".as_ptr(), + ); + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(0x20), + addr(0xA0), + 2, + c"q".as_ptr(), + 3, + c"g".as_ptr(), + ); + __m2c_rt_memtrack_map2check_free_resolved_address(addr(0xA0), 4, c"h".as_ptr(), 0); + // After the free, storing the freed block again is a FREE row. + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(0x30), + addr(0xA0), + 1, + c"r".as_ptr(), + 5, + c"f".as_ptr(), + ); + } + assert_ok(); + assert_eq!( + c_list_log(), + "0;0x10;0xa0;1;0;1;p;1;f;0\n\ + 0;0x20;0xa0;2;0;1;q;3;g;1\n\ + 0;0x10;0xa0;1;1;0;p;4;f;2\n\ + 0;0x20;0xa0;2;1;0;q;4;g;3\n\ + 0;0x30;0xa0;1;1;0;r;5;f;4\n" + ); +} + +#[test] +fn memtrack_rt_posix_reads_the_slot_and_logs_malloc_and_store() { + let _guard = memtrack_rt(); + let q = slot(0x2200); + let q_addr = &*q as *const usize as usize; + // SAFETY: `q` is live and holds a `void *`. + unsafe { __m2c_rt_memtrack_map2check_posix(addr(q_addr), 32) }; + // :340-345: map2check_malloc(*ptr) and the store with scope 1, line 1, + // "pointer" and "cocoiso". + assert_eq!(c_allocation_log(), "0x2200;32;0\n"); + assert_eq!( + c_list_log(), + format!("0;{q_addr:#x};0x2200;1;0;1;pointer;1;cocoiso;0\n") + ); +} + +#[test] +fn memtrack_rt_names_are_cut_at_35_bytes_except_the_variable_name() { + let _guard = memtrack_rt(); + let long = c"a_function_name_of_more_than_35_bytes_long"; + // SAFETY: fake addresses; C strings. + unsafe { + __m2c_rt_memtrack_map2check_alloca(long.as_ptr(), addr(0x100), 4, 4, 1, 1); + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(0x10), + addr(0x100), + 1, + long.as_ptr(), + 2, + long.as_ptr(), + ); + } + // new_heap_row and new_list_row strncpy FUNCTION_MAX_LENGTH_NAME (35) + // bytes (HeapLog.c:25, ListLog.c:133); the list row keeps the pointer + // to the variable name (:132). + let cut = "a_function_name_of_more_than_35_byt"; + assert_eq!(c_heap_log(), format!("0x100;{cut};4\n")); + assert_eq!( + c_list_log(), + format!("0;0x10;0x100;1;0;0;{};2;{cut};0\n", long.to_str().unwrap()) + ); +} + +#[test] +fn memtrack_rt_is_program_correct_reports_a_leak_and_releases_it() { + let _guard = memtrack_rt(); + assert_eq!(__m2c_rt_memtrack_analysis_is_program_correct(), 1); + rt_prelude(); + // AnalysisModeMemtrack.c:138-146: valid_allocation_log appends a freed + // row for the leaked block (AllocationLog.c:69-101) and the verdict is + // FALSE_MEMTRACK; the function does not record or end the run. + assert_eq!(__m2c_rt_memtrack_analysis_is_program_correct(), 0); + assert_ok(); + assert_eq!(c_allocation_log(), "0x1100;16;0\n0x1100;0;1\n"); + // The leak is now released: a second call is TRUE. + assert_eq!(__m2c_rt_memtrack_analysis_is_program_correct(), 1); + __m2c_rt_memtrack_analysis_generate_aux_witness_files(); +} + +#[test] +fn memtrack_rt_end_of_program_reports_the_leak_through_the_core() { + let _guard = memtrack_rt(); + rt_prelude(); + map2check_check_mem_endprog(); + assert_violated(ViolatedProperty::MemsafetyMemtrack, 0, "end_of_program"); +} + +#[test] +fn memtrack_rt_destroy_releases_the_logs() { + let _guard = memtrack_rt(); + rt_prelude(); + __m2c_rt_memtrack_analysis_destroy(); + assert_eq!(c_allocation_log(), ""); + assert_eq!(c_list_log(), ""); + assert_eq!(c_heap_log(), ""); +} + +#[test] +fn memtrack_rt_wasm_checks_use_the_allocation_log() { + let _guard = memtrack_rt(); + __m2c_rt_memtrack_map2check_wasm_malloc(0, 8); + __m2c_rt_memtrack_map2check_wasm_malloc(u64::MAX, 8); + __m2c_rt_memtrack_map2check_wasm_malloc(0x400, 16); + // AnalysisModeMemtrack.c:403-416: 0 and -1 are no block. + assert_eq!(c_allocation_log(), "0x400;16;0\n"); + __m2c_rt_memtrack_map2check_wasm_check_access(0x40c, 4); + assert_ok(); + // :430-436: FALSE_OVERFLOW at line 0, function "". + __m2c_rt_memtrack_map2check_wasm_check_access(0x40d, 4); + assert_violated(ViolatedProperty::Overflow, 0, ""); + state::reset(); + __m2c_rt_memtrack_map2check_wasm_malloc(0x400, 16); + __m2c_rt_memtrack_map2check_wasm_free(0x400, 7); + // A double free finds the row again (:418-428): no error. + __m2c_rt_memtrack_map2check_wasm_free(0x400, 8); + assert_ok(); + assert_eq!(c_allocation_log(), "0x400;0;1\n"); + __m2c_rt_memtrack_map2check_wasm_free(0x800, 9); + assert_violated(ViolatedProperty::MemsafetyFree, 9, ""); +} + +#[test] +fn sd23_memtrack_rt_release_without_allocation_row_goes_on() { + let _guard = memtrack_rt(); + // SAFETY: NULL is only compared; C strings. + unsafe { + __m2c_rt_memtrack_map2check_add_store_pointer( + addr(0x10), + std::ptr::null(), + 1, + c"p".as_ptr(), + 1, + c"main".as_ptr(), + ); + __m2c_rt_memtrack_map2check_free_resolved_address(std::ptr::null(), 2, c"main".as_ptr(), 0); + } + // develop SIGSEGVs in find_row_with_address's NULL row; no verdict, no + // row, no reference update here. + assert_ok(); + assert_eq!(c_list_log(), "0;0x10;(nil);1;0;0;p;1;main;0\n"); + assert_eq!(c_allocation_log(), ""); +} + +#[test] +fn memtrack_rt_check_deref_keeps_a_negative_c_line() { + let _guard = memtrack_rt(); + __m2c_rt_memtrack_map2check_load(addr(0x9000), 1); + // SAFETY: C string. + unsafe { __m2c_rt_memtrack_map2check_check_deref(-1, c"main".as_ptr()) }; + // The int is stored as its u32 bit pattern; the property files print + // it back with %d (write_property_files: `line as i32`), so "-1". + assert_violated(ViolatedProperty::MemsafetyDeref, u32::MAX, "main"); + assert_eq!(snapshot(|s| s.result.line as i32), -1); +} + +// ── C runtime ABI: AnalysisModeMemcleanup (core entry points, Task 2.6) ───── +// +// `AnalysisModeMemcleanup.bc` forwards the C names to `__m2c_rt_memcleanup_*`, +// which share the memory rules of the memtrack entry points (`MemMode`). +// `diff AnalysisModeMemtrack.c AnalysisModeMemcleanup.c` shows five +// differences, one test each below: +// :141-146 the verdict needs a pointer variable to the last leak +// :158 map2check_load is empty (no ERROR_DEREF) +// :159 map2check_check_cstring is empty +// :168/:342 `Bool error = FALSE` in both release functions (no FALSE-FREE) +// :294 map2check_check_deref is empty +// Everything else (rows, formats, UNKNOWN, posix, calloc) is the same code. + +/// `map2check_init`, then the module's `analysis_init`, which selects the +/// memcleanup mode. +fn memcleanup_rt() -> MutexGuard<'static, ()> { + let guard = fresh(); + map2check_init(0); + __m2c_rt_memcleanup_analysis_init(); + guard +} + +/// `golden_memory()` through the memcleanup entry points: `int *p` at 0x1000, +/// `&main` at 0x1040, `p = malloc(16)` at 0x1100. +fn memcleanup_prelude() { + // SAFETY: fake addresses are only stored and compared; names are C strings. + unsafe { + __m2c_rt_memcleanup_map2check_alloca(c"p".as_ptr(), addr(0x1000), 8, 8, 10, 1); + __m2c_rt_memcleanup_map2check_function(c"main".as_ptr(), addr(0x1040)); + __m2c_rt_memcleanup_map2check_malloc(addr(0x1100), 16); + __m2c_rt_memcleanup_map2check_add_store_pointer( + addr(0x1000), + addr(0x1100), + 1, + c"p".as_ptr(), + 11, + c"main".as_ptr(), + ); + } +} + +#[test] +fn memcleanup_rt_analysis_init_selects_the_mode_and_starts_empty_logs() { + let _guard = memcleanup_rt(); + assert_eq!(state::config().mode, Some(AnalysisMode::Memcleanup)); + memcleanup_prelude(); + // AnalysisModeMemcleanup.c:125-130: new containers, ERROR_DEREF = FALSE. + __m2c_rt_memcleanup_analysis_init(); + assert_eq!(c_list_log(), ""); + assert_eq!(c_allocation_log(), ""); + assert_eq!(c_heap_log(), ""); + assert!(!c_error_deref()); + // :132-136 frees the logs. + memcleanup_prelude(); + __m2c_rt_memcleanup_analysis_destroy(); + assert_eq!(c_allocation_log(), ""); +} + +#[test] +fn memcleanup_rt_shares_the_row_formats_and_rules_of_memtrack() { + let _guard = memcleanup_rt(); + memcleanup_prelude(); + assert_eq!(c_heap_log(), "0x1000;p;8\n0x1040;main;1\n"); + assert_eq!(c_allocation_log(), "0x1100;16;0\n"); + assert_eq!(c_list_log(), "0;0x1000;0x1100;1;0;1;p;11;main;0\n"); + // :326-328 calloc is malloc of quantity * size; :296-314 revives a row. + __m2c_rt_memcleanup_map2check_calloc(addr(0x2000), 3, 5); + __m2c_rt_memcleanup_map2check_malloc(addr(0x2000), 32); + // :359-363 non_static_alloca is alloca of size * primitive bytes. + // SAFETY: fake address; C-string literal. + unsafe { + __m2c_rt_memcleanup_map2check_non_static_alloca(c"vla".as_ptr(), addr(0x6000), 5, 4, 3, 1) + }; + assert_eq!(c_allocation_log(), "0x1100;16;0\n0x2000;32;0\n"); + assert_eq!(c_heap_log(), "0x1000;p;8\n0x1040;main;1\n0x6000;vla;20\n"); + assert_ok(); +} + +#[test] +fn memcleanup_rt_malloc_of_null_is_still_unknown() { + let _guard = memcleanup_rt(); + // :296-302 is the memtrack code: write_property_unknown + map2check_error. + __m2c_rt_memcleanup_map2check_malloc(std::ptr::null(), 4); + assert_violated(ViolatedProperty::Unknown, 0, ""); + assert_eq!(c_allocation_log(), ""); +} + +#[test] +fn memcleanup_rt_posix_records_the_block_and_the_store() { + let _guard = memcleanup_rt(); + let block = slot(0x3000); + let p_addr = &*block as *const usize as usize; + // SAFETY: `block` is live and holds the block address. + unsafe { __m2c_rt_memcleanup_map2check_posix(addr(p_addr), 32) }; + // :319-324 map2check_malloc(*ptr, size) + the store of "pointer". + assert_eq!(c_allocation_log(), "0x3000;32;0\n"); + assert_eq!( + c_list_log(), + format!("0;{p_addr:#x};0x3000;1;0;1;pointer;1;cocoiso;0\n") + ); +} + +#[test] +fn memcleanup_rt_load_never_sets_error_deref() { + let _guard = memcleanup_rt(); + memcleanup_prelude(); + // :158 `void map2check_load(void *ptr, int size) {}`: the access that + // memtrack flags (memtrack_rt_load_and_check_deref_follow_error_deref) + // leaves no trace. + __m2c_rt_memcleanup_map2check_load(addr(0x9000), 4); + __m2c_rt_memcleanup_map2check_load(addr(0x110d), 4); + assert!(!c_error_deref()); +} + +#[test] +fn memcleanup_rt_check_deref_is_empty() { + let _guard = memcleanup_rt(); + // :294 `void map2check_check_deref(int line, const char *function_name) {}` + // even when the state would be an invalid dereference in memtrack mode. + __m2c_rt_memcleanup_map2check_load(addr(0x9000), 4); + // SAFETY: C-string literal. + unsafe { __m2c_rt_memcleanup_map2check_check_deref(14, c"main".as_ptr()) }; + assert_ok(); +} + +#[test] +fn memcleanup_rt_check_cstring_is_empty() { + let _guard = memcleanup_rt(); + let beyond: [u8; 4] = *b"abcd"; + // SAFETY: the buffer is live; names are C strings. Nothing is read. + unsafe { + __m2c_rt_memcleanup_map2check_alloca( + c"t".as_ptr(), + addr(beyond.as_ptr() as usize), + 4, + 1, + 2, + 1, + ); + // :160 `void map2check_check_cstring(const char *string) {}`: memtrack + // (memtrack_rt_check_cstring_reads_up_to_the_terminator_inside_the_block) + // sets ERROR_DEREF at the fifth byte. + __m2c_rt_memcleanup_map2check_check_cstring(beyond.as_ptr().cast()); + __m2c_rt_memcleanup_map2check_check_cstring(std::ptr::null()); + __m2c_rt_memcleanup_map2check_check_deref(30, c"puts".as_ptr()); + } + assert!(!c_error_deref()); + assert_ok(); +} + +#[test] +fn memcleanup_rt_free_resolved_never_reports_false_free() { + let _guard = memcleanup_rt(); + memcleanup_prelude(); + // SAFETY: fake addresses; C-string literals. + unsafe { + // :164-166 NULL with isNullValid returns. + __m2c_rt_memcleanup_map2check_free_resolved_address( + std::ptr::null(), + 11, + c"main".as_ptr(), + 1, + ); + // :168 `Bool error = FALSE`: the block is released ... + __m2c_rt_memcleanup_map2check_free_resolved_address(addr(0x1100), 12, c"main".as_ptr(), 0); + // ... and a second release is not FALSE-FREE (memtrack: + // memtrack_rt_double_free_is_a_false_free_after_its_row_is_logged). + __m2c_rt_memcleanup_map2check_free_resolved_address(addr(0x1100), 13, c"main".as_ptr(), 0); + } + assert_ok(); + assert_eq!(c_allocation_log(), "0x1100;0;1\n"); + // :179 update_reference_list_log: p gets a FREE row (line 12). + assert_eq!( + c_list_log(), + "0;0x1000;0x1100;1;0;1;p;11;main;0\n0;0x1000;0x1100;1;1;0;p;12;main;1\n" + ); +} + +#[test] +fn memcleanup_rt_free_never_reports_false_free() { + let _guard = memcleanup_rt(); + let p = slot(0x1100); + let p_addr = &*p as *const usize as usize; + // SAFETY: `p` is live; the rest are fake addresses and C strings. + unsafe { + __m2c_rt_memcleanup_map2check_malloc(addr(0x1100), 16); + __m2c_rt_memcleanup_map2check_add_store_pointer( + addr(p_addr), + addr(0x1100), + 1, + c"p".as_ptr(), + 11, + c"main".as_ptr(), + ); + __m2c_rt_memcleanup_map2check_free(c"p".as_ptr(), addr(p_addr), 1, 12, c"main".as_ptr()); + // :342 `Bool error = FALSE`: the double free that memtrack reports + // is a no-op here (the row is found again). + __m2c_rt_memcleanup_map2check_free(c"p".as_ptr(), addr(p_addr), 1, 13, c"main".as_ptr()); + } + assert_ok(); + assert_eq!(c_allocation_log(), "0x1100;0;1\n"); + assert_eq!(c_list_log().lines().count(), 3); + assert!(c_list_log().ends_with("0;p;13;main;2\n")); +} + +#[test] +fn memcleanup_rt_free_of_a_null_block_does_nothing() { + let _guard = memcleanup_rt(); + let p = slot(0); + let p_addr = &*p as *const usize as usize; + // SAFETY: `p` is live; C-string literal. + unsafe { + __m2c_rt_memcleanup_map2check_free(c"p".as_ptr(), addr(p_addr), 1, 12, c"main".as_ptr()) + }; + // :334-336 + assert_eq!(c_list_log(), ""); + assert_ok(); +} + +#[test] +fn memcleanup_rt_verdict_needs_a_pointer_to_the_last_leak() { + let _guard = memcleanup_rt(); + memcleanup_prelude(); + // :138-148: the block 0x1100 is leaked and p (0x1000) still points to it. + assert_eq!(__m2c_rt_memcleanup_analysis_is_program_correct(), 0); + // valid_allocation_log appended the freed row (AllocationLog.c:69-101), + // so the verdict is TRUE the second time. + assert_eq!(c_allocation_log(), "0x1100;16;0\n0x1100;0;1\n"); + assert_eq!(__m2c_rt_memcleanup_analysis_is_program_correct(), 1); + // memtrack reports the same leak whatever points to it. + state::reset(); + state::reset_config(); + map2check_init(0); + __m2c_rt_memcleanup_analysis_init(); + __m2c_rt_memcleanup_map2check_malloc(addr(0x1100), 16); + // :141: no pointer variable refers to it: TRUE (memtrack would be FALSE). + assert_eq!(__m2c_rt_memcleanup_analysis_is_program_correct(), 1); + assert_ok(); +} + +#[test] +fn memcleanup_rt_verdict_is_true_once_the_pointer_moves_away() { + let _guard = memcleanup_rt(); + memcleanup_prelude(); + // SAFETY: fake addresses; C strings. p = NULL after the malloc. + unsafe { + __m2c_rt_memcleanup_map2check_add_store_pointer( + addr(0x1000), + std::ptr::null(), + 1, + c"p".as_ptr(), + 12, + c"main".as_ptr(), + ) + }; + // ListLog.c:40-65: the newest row of p no longer points to the block. + assert_eq!(__m2c_rt_memcleanup_analysis_is_program_correct(), 1); +} + +#[test] +fn memcleanup_rt_verdict_writes_the_memcleanup_property_files() { + let _guard = memcleanup_rt(); + let dir = tempfile::tempdir().unwrap(); + // The verdict writes into the output directory of the test build. + std::env::set_var(crate::output::termination::OUTPUT_DIR_ENV, dir.path()); + memcleanup_prelude(); + assert_eq!(__m2c_rt_memcleanup_analysis_is_program_correct(), 0); + std::env::remove_var(crate::output::termination::OUTPUT_DIR_ENV); + // PropertyGenerator.c:36-40 and :77-80. + assert_eq!( + std::fs::read_to_string(dir.path().join("map2check_property")).unwrap(), + "FALSE-MEMCLEANUP\n" + ); + assert_eq!( + std::fs::read_to_string(dir.path().join("map2check_property_memcleanup")).unwrap(), + "FALSE-MEMCLEANUP\n" + ); +} + +#[test] +fn memcleanup_rt_generate_aux_witness_files_writes_the_three_logs() { + let _guard = memcleanup_rt(); + let dir = tempfile::tempdir().unwrap(); + std::env::set_var(crate::output::termination::OUTPUT_DIR_ENV, dir.path()); + memcleanup_prelude(); + __m2c_rt_memcleanup_analysis_generate_aux_witness_files(); + std::env::remove_var(crate::output::termination::OUTPUT_DIR_ENV); + // :150-154 heap, allocation and list logs, in the C formats. + let read = |n: &str| std::fs::read_to_string(dir.path().join(n)).unwrap(); + assert_eq!(read("heap_log.csv"), "0x1000;p;8\n0x1040;main;1\n"); + assert_eq!(read("allocation_log.csv"), "0x1100;16;0\n"); + assert_eq!(read("list_log.csv"), "0;0x1000;0x1100;1;0;1;p;11;main;0\n"); +} + +/// SD-23 in memcleanup mode: `error = FALSE` lets a release of an untracked +/// or stack address through (`AnalysisModeMemcleanup.c:168`, `:342`); develop +/// then writes `row->size` through the NULL row of `find_row_with_address` +/// (`:176-177`, `:353-354`) and gets SIGSEGV. The core goes on. +#[test] +fn sd23_memcleanup_rt_release_without_allocation_row_goes_on() { + let _guard = memcleanup_rt(); + let x = 7u64; + let x_addr = &x as *const u64 as usize; + let p = slot(x_addr); + let p_addr = &*p as *const usize as usize; + // SAFETY: `p` is live; fake addresses; C strings. `int x; int *p = &x; + // free(p);` by both release functions. + unsafe { + __m2c_rt_memcleanup_map2check_alloca(c"x".as_ptr(), addr(x_addr), 8, 8, 3, 1); + __m2c_rt_memcleanup_map2check_add_store_pointer( + addr(p_addr), + addr(x_addr), + 1, + c"p".as_ptr(), + 4, + c"main".as_ptr(), + ); + __m2c_rt_memcleanup_map2check_free(c"p".as_ptr(), addr(p_addr), 1, 50, c"main".as_ptr()); + __m2c_rt_memcleanup_map2check_free_resolved_address(addr(x_addr), 51, c"main".as_ptr(), 0); + __m2c_rt_memcleanup_map2check_free_resolved_address(addr(0x7777), 52, c"main".as_ptr(), 0); + } + // No verdict, no allocation row; only the FREE row of the store that + // map2check_free appends before it looks the block up (:337-345). + assert_ok(); + assert_eq!(c_allocation_log(), ""); + assert_eq!(c_list_log().lines().count(), 2); + assert!(c_list_log().ends_with(&format!("0;{p_addr:#x};{x_addr:#x};1;1;0;p;50;main;1\n"))); + assert_eq!(__m2c_rt_memcleanup_analysis_is_program_correct(), 1); +} + +/// `__VERIFIER_error` of the C core does nothing (`Map2CheckFunctions.c:57-59`), +/// with a mode set and without: no violation, no step, the process goes on +/// (SD-08). +#[test] +fn core_verifier_error_does_nothing_as_in_c() { + for mode in [None, Some(crate::analysismode::mode::AnalysisMode::None)] { + let _g = fresh(); + state::update_config(|c| c.mode = mode); + let seen = || snapshot(|s| (s.result.ok, s.result.property, s.result.step, s.c_step)); + let before = seen(); + __VERIFIER_error(); + assert_eq!(seen(), before, "mode {mode:?}"); + assert_ok(); + } +} + +#[test] +fn output_set_atomic_selects_the_result_file_write_mode() { + use crate::output::legacy_files::{atomic_writes, write_mode_test_lock}; + let _mode = write_mode_test_lock(); + __m2c_rt_output_set_atomic(0); + assert!(!atomic_writes()); + __m2c_rt_output_set_atomic(7); + assert!(atomic_writes()); + assert!(crate::output::legacy_files::never_downgrade()); + __m2c_rt_output_set_atomic(0); + assert!(!crate::output::legacy_files::never_downgrade()); + __m2c_rt_output_set_atomic(1); + assert!(atomic_writes(), "the default of every non-dropin build"); + assert!(crate::output::legacy_files::never_downgrade()); + crate::output::legacy_files::set_never_downgrade(false); +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 8c6cc7f..2d1afa7 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -4,7 +4,9 @@ // // Unsafe code policy: // - `ffi.rs` is the sole module containing unsafe code (FFI boundary), with -// `wasm.rs` (feature `wasm`: libc allocation and wasm-rt struct access). +// `wasm.rs` (feature `wasm`: libc allocation and wasm-rt struct access) +// and `sys.rs` (the libc calls of the result files, the environment and +// stderr). // - All other modules use `#![deny(unsafe_code)]` internally. // - Every unsafe block in `ffi.rs` is annotated with a SAFETY comment. // @@ -21,12 +23,14 @@ pub mod analysismode; pub mod bbtrack; pub mod caller; +pub mod cfmt; pub mod error; pub mod ffi; pub mod memtrack; pub mod nondet; pub mod output; pub mod state; +pub mod sys; /// wasm2c runtime (`--wasm` mode of Map2Check) — feature-gated. #[cfg(feature = "wasm")] pub mod wasm; diff --git a/rust/src/memtrack/c_mode.rs b/rust/src/memtrack/c_mode.rs new file mode 100644 index 0000000..47e608f --- /dev/null +++ b/rust/src/memtrack/c_mode.rs @@ -0,0 +1,889 @@ +// SPDX-License-Identifier: GPL-3.0-only +// +// The memory logs of Map2Check's C runtime, as the memory modes keep them +// (`AnalysisModeMemtrack.c`, `AnalysisModeMemcleanup.c`): the heap log +// (`HeapLog.c`), the allocation log (`AllocationLog.c`), the list log +// (`ListLog.c`) and the `ERROR_DEREF` flag. The `__m2c_rt_memtrack_*` core +// entry points (Task 2.5) run on these; the legacy `map2check_map_*` exports +// keep their own records (`AnalysisState::memtrack`). +// +// One container serves both container builds of develop (D3): the Array +// build links `ContainerRealloc.bc` and `--btree` links `ContainerBTree.bc` +// and `BTree.bc`; in the drop-in runtime all three are empty IR modules and +// both link this core, so the rows below are the only store. +// +// The rules are parameterised by [`MemMode`]: `AnalysisModeMemcleanup.c` +// shares the tracking of `AnalysisModeMemtrack.c` and differs only in the +// checks (no deref check, `error = FALSE` on free) and the end-of-program +// verdict, so Task 2.6 adds thin wrappers and no logic. +#![deny(unsafe_code)] + +use crate::caller::ViolatedProperty; +use crate::cfmt; +use crate::nondet::c_log::FUNCTION_MAX_LENGTH_NAME; + +/// Which memory module is linked: the checks and the verdict depend on it, +/// the tracking does not. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MemMode { + /// `AnalysisModeMemtrack.c`. + Memtrack, + /// `AnalysisModeMemcleanup.c`. + Memcleanup, +} + +/// `enum MemoryAddressStatus` (`Map2CheckTypes.h:34-39`). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AddressStatus { + Static, + Free, + Dynamic, +} + +impl AddressStatus { + /// `(isDynamic, isFree)` of the `switch (status)` in + /// `AnalysisModeMemtrack.c:229-242` and `:268-284`. + fn flags(self) -> (bool, bool) { + match self { + AddressStatus::Static => (false, false), + AddressStatus::Free => (false, true), + AddressStatus::Dynamic => (true, false), + } + } +} + +/// `char function_name[FUNCTION_MAX_LENGTH_NAME]` filled by `strncpy` +/// (`ListLog.c:133`, `HeapLog.c:25`): the name cut at 35 bytes. A name of 35 +/// bytes or more has no terminator in C, and `%s` then reads past the array; +/// the cut name is what this library prints (as `c_log` does). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CName { + bytes: [u8; FUNCTION_MAX_LENGTH_NAME], + len: u8, +} + +impl CName { + pub fn new(name: &[u8]) -> Self { + let mut bytes = [0u8; FUNCTION_MAX_LENGTH_NAME]; + let mut len = 0u8; + for (slot, &b) in bytes.iter_mut().zip(name) { + if b == 0 { + break; + } + *slot = b; + len += 1; + } + Self { bytes, len } + } + + pub fn as_bytes(&self) -> &[u8] { + &self.bytes[..usize::from(self.len)] + } +} + +/// `LIST_LOG_ROW` (`Map2CheckTypes.h:97-118`). `id` is always 0 +/// (`new_list_row`, `ListLog.c:125`). `var_name` is a pointer in C, so the +/// whole name is kept. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ListRow { + pub memory_address: u64, + pub memory_address_points_to: u64, + pub scope: u32, + pub is_dynamic: bool, + pub is_free: bool, + pub line_number: u32, + pub var_name: Vec, + pub function_name: CName, + pub step_on_execution: u32, +} + +impl ListRow { + /// `get_type_from_list_log_row` (`ListLog.c:159-168`). + fn status(&self) -> AddressStatus { + if self.is_free && !self.is_dynamic { + AddressStatus::Free + } else if self.is_dynamic { + AddressStatus::Dynamic + } else { + AddressStatus::Static + } + } +} + +/// `MEMORY_ALLOCATIONS_ROW` (`Map2CheckTypes.h:124-133`): `new_memory_row` +/// zeroes it (`AllocationLog.c:53-61`). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AllocationRow { + pub addr: u64, + pub is_free: bool, + pub size: u32, +} + +/// `MEMORY_HEAP_ROW` (`Map2CheckTypes.h:191-203`). `new_heap_row` copies the +/// variable name into `function_name` (`HeapLog.c:22-32`). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct HeapRow { + pub line: i32, + pub scope: i32, + pub value: u64, + pub size: i32, + pub size_of_primitive: i32, + pub function_name: CName, +} + +/// What a free found, for the caller to report (the report writes files +/// and may end the process, so it runs after the state lock is released). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FreeOutcome { + /// The release was recorded, or there was nothing to release. + Released, + /// `is_invalid_free` (`ListLog.c:95-118`): FALSE_FREE. The rows the C + /// appends before it reports are already appended. + InvalidFree, +} + +/// `heap_log`, `allocation_log`, `list_log` and `ERROR_DEREF` of +/// `AnalysisModeMemtrack.c:120-123`. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct CMemory { + heap: Vec, + allocation: Vec, + list: Vec, + error_deref: bool, +} + +impl CMemory { + /// Empty logs, `ERROR_DEREF` clear. + pub const fn new() -> Self { + Self { + heap: Vec::new(), + allocation: Vec::new(), + list: Vec::new(), + error_deref: false, + } + } + + /// `analysis_init` (`AnalysisModeMemtrack.c:125-130`): new containers and + /// `ERROR_DEREF = FALSE`. `analysis_destroy` (`:132-136`) frees the + /// containers; both leave this state. + pub fn clear(&mut self) { + *self = Self::default(); + } + + /// Whether any row was logged (the drop-in memory entry points ran). + pub fn has_rows(&self) -> bool { + !(self.heap.is_empty() && self.allocation.is_empty() && self.list.is_empty()) + } + + pub fn error_deref(&self) -> bool { + self.error_deref + } + + // ── AllocationLog.c, HeapLog.c, ListLog.c ─────────────────────────────── + + /// `check_address_allocation_log` (`AllocationLog.c:33-51`). + fn check_address_allocation_log(&self, address: u64) -> AddressStatus { + match self.allocation.iter().rev().find(|r| r.addr == address) { + Some(r) if r.is_free => AddressStatus::Free, + Some(_) => AddressStatus::Dynamic, + None => AddressStatus::Static, + } + } + + /// `find_row_with_address` (`AllocationLog.c:103-118`): the newest row. + fn find_row_with_address(&mut self, address: u64) -> Option<&mut AllocationRow> { + self.allocation.iter_mut().rev().find(|r| r.addr == address) + } + + /// `is_valid_allocation_address` (`AllocationLog.c:127-148`): the newest + /// row whose `[addr, addr + size - size_to_destiny + 1)` holds `address` + /// decides, valid only if not free. `unsigned long` arithmetic: the + /// `int` size is sign-extended and the sum wraps. + fn is_valid_allocation_address(&self, address: u64, size_to_destiny: i32) -> bool { + let access = i64::from(size_to_destiny) as u64; + for row in self.allocation.iter().rev() { + let bottom = row.addr; + let top = bottom + .wrapping_add(u64::from(row.size)) + .wrapping_sub(access) + .wrapping_add(1); + if bottom <= address && address < top { + return !row.is_free; + } + } + false + } + + /// `is_valid_heap_address` (`HeapLog.c:37-52`): any row whose range + /// holds `address`, free or not; `long` arithmetic. + fn is_valid_heap_address(&self, address: u64, size_to_load: i32) -> bool { + let address = address as i64; + self.heap.iter().rev().any(|row| { + let bottom = row.value as i64; + let top = bottom + .wrapping_add(i64::from(row.size)) + .wrapping_sub(i64::from(size_to_load)) + .wrapping_add(1); + bottom <= address && address < top + }) + } + + /// `is_invalid_free` (`ListLog.c:95-118`): NULL is valid; the newest list + /// row pointing to `address` decides (invalid if freed or not dynamic); + /// no row is invalid. + fn is_invalid_free(&self, address: u64) -> bool { + if address == 0 { + return false; + } + self.list + .iter() + .rev() + .find(|r| r.memory_address_points_to == address) + .map_or(true, |r| r.is_free || !r.is_dynamic) + } + + /// `is_memcleanup_error` (`ListLog.c:40-65`): some pointer whose newest + /// row still points to `address`. + fn is_memcleanup_error(&self, address: u64) -> bool { + let list = &self.list; + (0..list.len()).rev().any(|i| { + if list[i].memory_address_points_to != address { + return false; + } + let pointer = list[i].memory_address; + list[i + 1..] + .iter() + .rev() + .find(|r| r.memory_address == pointer) + .map_or(true, |r| r.memory_address_points_to == address) + }) + } + + /// `valid_allocation_log` (`AllocationLog.c:69-101`): every live row with + /// no later freed row of its address gets a freed row (size 0) + /// appended; returns the last such address, 0 when none. The rows it + /// appends are visited too (the loop reads the growing size) and are + /// free. develop also calls `free()` on the leaked block (`:91`); the + /// process ends right after, so the core does not. + pub fn valid_allocation_log(&mut self) -> u64 { + let mut error = 0; + let mut i = 0; + while i < self.allocation.len() { + let row = self.allocation[i]; + if !row.is_free { + let released = self.allocation[i + 1..] + .iter() + .any(|r| r.addr == row.addr && r.is_free); + if !released { + self.allocation.push(AllocationRow { + addr: row.addr, + is_free: true, + size: 0, + }); + error = row.addr; + } + } + i += 1; + } + error + } + + /// `new_list_row` with the current step, then + /// `map2check_next_current_step` and `append_element`. + #[allow(clippy::too_many_arguments)] + fn append_list_row( + &mut self, + step: &mut u32, + var: u64, + points_to: u64, + scope: u32, + is_dynamic: bool, + is_free: bool, + line: u32, + name: &[u8], + function_name: CName, + ) { + self.list.push(ListRow { + memory_address: var, + memory_address_points_to: points_to, + scope, + is_dynamic, + is_free, + line_number: line, + var_name: name.to_vec(), + function_name, + step_on_execution: *step, + }); + *step = step.wrapping_add(1); + } + + /// `update_reference_list_log` (`AnalysisModeMemtrack.c:197-256`): for + /// each row of the first `currentSize` that points to `address` with + /// another status, the newest row at or after it of the same variable + /// that points to `address` gets a copy with `status` appended, unless + /// it already has that status. + fn update_reference_list_log( + &mut self, + step: &mut u32, + address: u64, + status: AddressStatus, + line: u32, + ) { + let current_size = self.list.len(); + for i in 0..current_size { + let (points_to, var, i_status) = { + let r = &self.list[i]; + (r.memory_address_points_to, r.memory_address, r.status()) + }; + if points_to != address || status == i_status { + continue; + } + let Some(j_row) = self.list[i..current_size] + .iter() + .rev() + .find(|r| r.memory_address_points_to == address && r.memory_address == var) + .cloned() + else { + continue; + }; + if j_row.status() != status { + let (is_dynamic, is_free) = status.flags(); + self.append_list_row( + step, + j_row.memory_address, + j_row.memory_address_points_to, + j_row.scope, + is_dynamic, + is_free, + line, + &j_row.var_name, + j_row.function_name, + ); + } + } + } + + /// Marks the newest row of `address` free with size 0 + /// (`AnalysisModeMemtrack.c:190-192`, `:372-375`) and returns true. + /// develop dereferences the row without a NULL test, so an address with + /// no row crashes there, before `update_reference_list_log` (only + /// reachable when the free was not found invalid: NULL with + /// `isNullValid` 0, or memcleanup mode). The core stops there instead and + /// returns false. + fn release_row(&mut self, address: u64) -> bool { + match self.find_row_with_address(address) { + Some(row) => { + row.size = 0; + row.is_free = true; + true + } + None => false, + } + } + + // ── AnalysisModeMemtrack.c ────────────────────────────────────────────── + + /// `analysis_is_program_correct` (`AnalysisModeMemtrack.c:138-146`; + /// `AnalysisModeMemcleanup.c:138-148`): the property of the leak, or + /// `None`. It runs `valid_allocation_log`, which appends rows. + pub fn is_program_correct(&mut self, mode: MemMode) -> Option { + let addr = self.valid_allocation_log(); + if addr == 0 { + return None; + } + match mode { + MemMode::Memtrack => Some(ViolatedProperty::MemsafetyMemtrack), + MemMode::Memcleanup => self + .is_memcleanup_error(addr) + .then_some(ViolatedProperty::MemsafetyMemcleanup), + } + } + + /// `map2check_load` (`AnalysisModeMemtrack.c:168-174`): sets + /// `ERROR_DEREF` when no heap-log row and no live allocation covers the + /// `size` bytes at `ptr`. Empty in memcleanup (`AnalysisModeMemcleanup.c:158`). + /// + /// Not inlined: inlined into the per-byte loop of [`Self::check_cstring`], + /// LLVM unswitched that loop on the (loop-invariant) log lengths and put a + /// `freeze` on them, which KLEE 3.1 cannot execute + /// (scripts/runtime-ir/check_no_freeze.py). As a call, the logs are read + /// inside it, as `map2check_load` reads them in C. + #[inline(never)] + pub fn load(&mut self, mode: MemMode, ptr: u64, size: i32) { + if mode == MemMode::Memcleanup { + return; + } + if !self.is_valid_heap_address(ptr, size) && !self.is_valid_allocation_address(ptr, size) { + self.error_deref = true; + } + } + + /// `map2check_check_cstring` (`AnalysisModeMemtrack.c:159-166`): each + /// byte up to the terminator must be covered (`map2check_load(s + i, 1)`); + /// stops at the first invalid byte, before reading it, or at the + /// terminator. `read` reads the byte at an address. Empty in memcleanup + /// (`AnalysisModeMemcleanup.c:159`). + pub fn check_cstring(&mut self, mode: MemMode, string: u64, mut read: impl FnMut(u64) -> u8) { + if mode == MemMode::Memcleanup || string == 0 { + return; + } + let mut i: u64 = 0; + loop { + let at = string.wrapping_add(i); + self.load(mode, at, 1); + if self.error_deref || read(at) == 0 { + return; + } + i = i.wrapping_add(1); + } + } + + /// `map2check_check_deref` (`AnalysisModeMemtrack.c:309-315`): whether + /// to report FALSE_DEREF. Empty in memcleanup (`:294`). + pub fn check_deref(&self, mode: MemMode) -> bool { + mode == MemMode::Memtrack && self.error_deref + } + + /// `map2check_free_resolved_address` (`AnalysisModeMemtrack.c:176-195`). + /// Memcleanup never finds the free invalid (`AnalysisModeMemcleanup.c:168`). + pub fn free_resolved_address( + &mut self, + mode: MemMode, + step: &mut u32, + ptr: u64, + line: u32, + is_null_valid: i16, + ) -> FreeOutcome { + if ptr == 0 && is_null_valid != 0 { + return FreeOutcome::Released; + } + if mode == MemMode::Memtrack && self.is_invalid_free(ptr) { + return FreeOutcome::InvalidFree; + } + if self.release_row(ptr) { + self.update_reference_list_log(step, ptr, AddressStatus::Free, line); + } + FreeOutcome::Released + } + + /// `map2check_add_store_pointer` (`AnalysisModeMemtrack.c:258-307`): the + /// status of `value` in the allocation log; a row unless the variable's + /// newest row already has this value and these flags; then the references + /// to `value` are updated. + #[allow(clippy::too_many_arguments)] + pub fn add_store_pointer( + &mut self, + step: &mut u32, + var: u64, + value: u64, + scope: u32, + name: &[u8], + line: i32, + function_name: &[u8], + ) { + let status = self.check_address_allocation_log(value); + let (is_dynamic, is_free) = status.flags(); + let redundant = self + .list + .iter() + .rev() + .find(|r| r.memory_address == var) + .is_some_and(|r| { + r.memory_address_points_to == value + && r.is_free == is_free + && r.is_dynamic == is_dynamic + }); + if redundant { + return; + } + // `new_list_row` takes `unsigned line_number`. + let line = line as u32; + self.append_list_row( + step, + var, + value, + scope, + is_dynamic, + is_free, + line, + name, + CName::new(function_name), + ); + self.update_reference_list_log(step, value, status, line); + } + + /// `map2check_malloc` (`AnalysisModeMemtrack.c:317-335`). Returns false + /// for develop's NULL test, `int addr = (long)ptr; addr == 0` (the low 32 + /// bits), which writes UNKNOWN and ends the run before any row. + /// Otherwise the newest row of `ptr` is revived with the new size, or a + /// row is appended. `size` is an `int` stored in an `unsigned`. + pub fn malloc(&mut self, ptr: u64, size: i32) -> bool { + if ptr as u32 == 0 { + return false; + } + self.track_allocation(ptr, size as u32); + true + } + + fn track_allocation(&mut self, addr: u64, size: u32) { + match self.find_row_with_address(addr) { + Some(row) => { + row.size = size; + row.is_free = false; + } + None => self.allocation.push(AllocationRow { + addr, + is_free: false, + size, + }), + } + } + + /// `map2check_calloc` (`AnalysisModeMemtrack.c:347-349`): + /// `map2check_malloc(ptr, quantity * size)`, an `int` product (it wraps). + pub fn calloc(&mut self, ptr: u64, quantity: i32, size: i32) -> bool { + self.malloc(ptr, quantity.wrapping_mul(size)) + } + + /// `map2check_free` (`AnalysisModeMemtrack.c:351-378`) after the slot was + /// read (`points_to = *(long *)ptr`): nothing for NULL; a FREE row for the + /// variable `var` (appended before the check, `:358-365`); FALSE_FREE when + /// `is_invalid_free` (memtrack only, memcleanup has `error = FALSE`); + /// else the block is released and the references updated. + #[allow(clippy::too_many_arguments)] + pub fn free( + &mut self, + mode: MemMode, + step: &mut u32, + name: &[u8], + var: u64, + points_to: u64, + scope: u32, + line: u32, + function_name: &[u8], + ) -> FreeOutcome { + if points_to == 0 { + return FreeOutcome::Released; + } + let row_step = *step; + *step = step.wrapping_add(1); + let error = mode == MemMode::Memtrack && self.is_invalid_free(points_to); + self.list.push(ListRow { + memory_address: var, + memory_address_points_to: points_to, + scope, + is_dynamic: false, + is_free: true, + line_number: line, + var_name: name.to_vec(), + function_name: CName::new(function_name), + step_on_execution: row_step, + }); + if error { + return FreeOutcome::InvalidFree; + } + if self.release_row(points_to) { + self.update_reference_list_log(step, points_to, AddressStatus::Free, line); + } + FreeOutcome::Released + } + + /// `map2check_alloca` (`AnalysisModeMemtrack.c:386-391`). + pub fn alloca( + &mut self, + name: &[u8], + ptr: u64, + size: i32, + size_of_primitive: i32, + line: i32, + scope: i32, + ) { + self.heap.push(HeapRow { + line, + scope, + value: ptr, + size, + size_of_primitive, + function_name: CName::new(name), + }); + } + + /// `map2check_non_static_alloca` (`AnalysisModeMemtrack.c:380-384`): + /// `size * size_of_primitive` bytes, an `int` product (it wraps). + pub fn non_static_alloca( + &mut self, + name: &[u8], + ptr: u64, + size: i32, + size_of_primitive: i32, + line: i32, + scope: i32, + ) { + self.alloca( + name, + ptr, + size.wrapping_mul(size_of_primitive), + size_of_primitive, + line, + scope, + ); + } + + /// `map2check_function` (`AnalysisModeMemtrack.c:393-397`): line, scope, + /// size and primitive size 1. + pub fn function(&mut self, name: &[u8], ptr: u64) { + self.alloca(name, ptr, 1, 1, 1, 1); + } + + /// `map2check_wasm_malloc` (`AnalysisModeMemtrack.c:403-416`): offsets 0 + /// and `(uint64_t)-1` are no block; the size is cut to `unsigned`. + pub fn wasm_malloc(&mut self, offset: u64, size: u64) { + if offset == 0 || offset == u64::MAX { + return; + } + self.track_allocation(offset, size as u32); + } + + /// `map2check_wasm_free` (`AnalysisModeMemtrack.c:418-428`): false when + /// no row has `offset` (FALSE_FREE); a freed row is found again, so a + /// double free is not an error. + pub fn wasm_free(&mut self, offset: u64) -> bool { + match self.find_row_with_address(offset) { + Some(row) => { + row.is_free = true; + row.size = 0; + true + } + None => false, + } + } + + /// `map2check_wasm_check_access` (`AnalysisModeMemtrack.c:430-436`): + /// whether the access is valid; `(int)access_size`. + pub fn wasm_check_access(&self, offset: u64, access_size: u64) -> bool { + self.is_valid_allocation_address(offset, access_size as i32) + } + + // ── *_log_to_file ─────────────────────────────────────────────────────── + + /// `list_log_to_file` (`ListLog.c:138-157`). + pub fn list_log_bytes(&self) -> Vec { + let mut out = Vec::new(); + for r in &self.list { + list_log_row( + &mut out, + r.memory_address, + r.memory_address_points_to, + r.scope, + r.is_free, + r.is_dynamic, + &r.var_name, + r.line_number, + r.function_name.as_bytes(), + r.step_on_execution, + ); + } + out + } + + /// `allocation_log_to_file` (`AllocationLog.c:161-174`). + pub fn allocation_log_bytes(&self) -> Vec { + let mut out = Vec::new(); + for r in &self.allocation { + allocation_log_row(&mut out, r.addr, r.size, r.is_free); + } + out + } + + /// `heap_log_to_file` (`HeapLog.c:54-66`). + pub fn heap_log_bytes(&self) -> Vec { + let mut out = Vec::new(); + for r in &self.heap { + heap_log_row(&mut out, r.value, r.function_name.as_bytes(), r.size); + } + out + } +} + +/// One row of `list_log.csv`, the `fprintf` calls of `ListLog.c:145-154`: +/// `"%u;"` id (always 0, `new_list_row` `:125`), `"%p;"` memory address, +/// `"%p;"` points to, `"%u;"` scope, `"%d;"` is_free, `"%d;"` is_dynamic, +/// `"%s;"` var name, `"%u;"` line, `"%s;"` function name, `"%u\n"` step. +#[allow(clippy::too_many_arguments)] +pub fn list_log_row( + out: &mut Vec, + memory_address: u64, + points_to: u64, + scope: u32, + is_free: bool, + is_dynamic: bool, + var_name: &[u8], + line: u32, + function_name: &[u8], + step: u32, +) { + cfmt::put(out, b"0;"); + cfmt::put_ptr(out, memory_address); + cfmt::put_byte(out, b';'); + cfmt::put_ptr(out, points_to); + cfmt::put_byte(out, b';'); + cfmt::put_u64(out, u64::from(scope)); + cfmt::put_byte(out, b';'); + cfmt::put_byte(out, if is_free { b'1' } else { b'0' }); + cfmt::put_byte(out, b';'); + cfmt::put_byte(out, if is_dynamic { b'1' } else { b'0' }); + cfmt::put_byte(out, b';'); + cfmt::put(out, var_name); + cfmt::put_byte(out, b';'); + cfmt::put_u64(out, u64::from(line)); + cfmt::put_byte(out, b';'); + cfmt::put(out, function_name); + cfmt::put_byte(out, b';'); + cfmt::put_u64(out, u64::from(step)); + cfmt::put_byte(out, b'\n'); +} + +/// One row of `allocation_log.csv`, `AllocationLog.c:169-171`: +/// `"%p;"` address, `"%u;"` size, `"%d\n"` is_free. +pub fn allocation_log_row(out: &mut Vec, addr: u64, size: u32, is_free: bool) { + cfmt::put_ptr(out, addr); + cfmt::put_byte(out, b';'); + cfmt::put_u64(out, u64::from(size)); + cfmt::put_byte(out, b';'); + cfmt::put_byte(out, if is_free { b'1' } else { b'0' }); + cfmt::put_byte(out, b'\n'); +} + +/// One row of `heap_log.csv`, `HeapLog.c:61-63`: `"%p;"` address, `"%s;"` +/// name, `"%d\n"` size. +pub fn heap_log_row(out: &mut Vec, addr: u64, name: &[u8], size: i32) { + cfmt::put_ptr(out, addr); + cfmt::put_byte(out, b';'); + cfmt::put(out, name); + cfmt::put_byte(out, b';'); + cfmt::put_i64(out, i64::from(size)); + cfmt::put_byte(out, b'\n'); +} + +#[cfg(test)] +mod tests { + use super::*; + + fn text(bytes: Vec) -> String { + String::from_utf8(bytes).unwrap() + } + + #[test] + fn row_formats_are_those_of_the_c_fprintf_calls() { + let mut out = Vec::new(); + list_log_row(&mut out, 0x10, 0, 3, true, false, b"p", 7, b"main", 9); + assert_eq!(text(out), "0;0x10;(nil);3;1;0;p;7;main;9\n"); + let mut out = Vec::new(); + allocation_log_row(&mut out, 0xA0, u32::MAX, false); + assert_eq!(text(out), "0xa0;4294967295;0\n"); + let mut out = Vec::new(); + heap_log_row(&mut out, 0, b"x", -4); + assert_eq!(text(out), "(nil);x;-4\n"); + } + + #[test] + fn valid_allocation_log_appends_a_freed_row_per_leak_and_returns_the_last() { + let mut m = CMemory::default(); + assert_eq!(m.valid_allocation_log(), 0); + assert!(m.malloc(0xA0, 4)); + assert!(m.malloc(0xB0, 8)); + assert!(m.wasm_free(0xB0)); + assert!(m.malloc(0xC0, 2)); + assert_eq!(m.valid_allocation_log(), 0xC0); + assert_eq!( + text(m.allocation_log_bytes()), + "0xa0;4;0\n0xb0;0;1\n0xc0;2;0\n0xa0;0;1\n0xc0;0;1\n" + ); + assert_eq!(m.valid_allocation_log(), 0); + } + + #[test] + fn allocation_range_wraps_as_unsigned_long() { + let mut m = CMemory::default(); + m.wasm_malloc(0x100, 4); + assert!(m.wasm_check_access(0x100, 4)); + assert!(!m.wasm_check_access(0x101, 4)); + // (int)access_size: 2^32 + 1 is 1. + assert!(m.wasm_check_access(0x103, (1 << 32) + 1)); + // A negative int access widens the range. + assert!(m.wasm_check_access(0x104, u64::from(u32::MAX))); + } + + #[test] + fn memcleanup_mode_tracks_but_never_reports_deref_or_free() { + let mut m = CMemory::default(); + let mut step = 0; + m.load(MemMode::Memcleanup, 0x9000, 4); + assert!(!m.error_deref()); + m.check_cstring(MemMode::Memcleanup, 0x9000, |_| unreachable!()); + assert!(!m.check_deref(MemMode::Memcleanup)); + assert_eq!( + m.free_resolved_address(MemMode::Memcleanup, &mut step, 0x5000, 1, 0), + FreeOutcome::Released + ); + assert!(m.malloc(0xA0, 8)); + assert_eq!( + m.free(MemMode::Memcleanup, &mut step, b"p", 0x10, 0xA0, 1, 2, b"f"), + FreeOutcome::Released + ); + assert_eq!(text(m.allocation_log_bytes()), "0xa0;0;1\n"); + assert_eq!(text(m.list_log_bytes()), "0;0x10;0xa0;1;1;0;p;2;f;0\n"); + } + + #[test] + fn memcleanup_verdict_needs_a_pointer_to_the_last_leak() { + let mut step = 0; + let mut m = CMemory::default(); + assert!(m.malloc(0xA0, 8)); + assert_eq!( + m.clone().is_program_correct(MemMode::Memtrack), + Some(ViolatedProperty::MemsafetyMemtrack) + ); + assert_eq!(m.clone().is_program_correct(MemMode::Memcleanup), None); + m.add_store_pointer(&mut step, 0x10, 0xA0, 1, b"p", 1, b"f"); + assert_eq!( + m.clone().is_program_correct(MemMode::Memcleanup), + Some(ViolatedProperty::MemsafetyMemcleanup) + ); + m.add_store_pointer(&mut step, 0x10, 0, 1, b"p", 2, b"f"); + assert_eq!(m.is_program_correct(MemMode::Memcleanup), None); + } + + #[test] + fn a_release_without_allocation_row_stops_where_develop_crashes() { + // free_resolved_address(NULL, .., isNullValid = 0): is_invalid_free + // is FALSE for NULL and find_row_with_address finds no row; develop + // writes through NULL there. No row and no reference update here. + let mut m = CMemory::default(); + let mut step = 0; + m.add_store_pointer(&mut step, 0x10, 0, 1, b"p", 1, b"f"); + assert_eq!( + m.free_resolved_address(MemMode::Memtrack, &mut step, 0, 2, 0), + FreeOutcome::Released + ); + assert_eq!(text(m.list_log_bytes()), "0;0x10;(nil);1;0;0;p;1;f;0\n"); + assert_eq!(step, 1); + } + + #[test] + fn heap_range_is_signed_long() { + let mut m = CMemory::default(); + m.alloca(b"x", 0x100, 8, 8, 1, 1); + m.load(MemMode::Memtrack, 0x104, 4); + assert!(!m.error_deref()); + // A size larger than the slot leaves an empty range. + m.load(MemMode::Memtrack, 0x100, 9); + assert!(m.error_deref()); + } + + #[test] + fn cname_cuts_at_35_bytes_and_at_the_first_nul() { + assert_eq!(CName::new(b"ab\0cd").as_bytes(), b"ab"); + assert_eq!(CName::new(&[b'x'; 40]).as_bytes(), &[b'x'; 35][..]); + } +} diff --git a/rust/src/memtrack/mod.rs b/rust/src/memtrack/mod.rs index a5b29c9..3b26c6a 100644 --- a/rust/src/memtrack/mod.rs +++ b/rust/src/memtrack/mod.rs @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only #![deny(unsafe_code)] -use serde::Serialize; +pub mod c_mode; + use std::collections::{hash_map::DefaultHasher, HashSet}; use std::hash::BuildHasherDefault; @@ -85,16 +86,17 @@ pub fn wasm_is_valid_access(table: &[WasmAlloc], address: u64, access_size: u64) /// Single memory tracking record — analogous to `memtrack_log_t` in C. /// Addresses are `usize` (platform pointer width) instead of `long`. -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone)] +#[cfg_attr(not(feature = "dropin"), derive(serde::Serialize))] pub struct MemTrackEntry { pub step: u64, pub line: u32, pub scope: u32, /// Address of the variable holding the pointer (cast from `*const T`). - #[serde(rename = "var_mem_address")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "var_mem_address"))] pub var_addr: usize, /// Address the variable points to. - #[serde(rename = "mem_address_points_to")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "mem_address_points_to"))] pub points_to: usize, pub is_dynamic: bool, pub is_free: bool, @@ -107,7 +109,7 @@ pub struct MemTrackEntry { pub is_null_valid: bool, /// The export that recorded the entry. Only the legacy log files read it /// (`output::legacy_files`); the JSON report does not carry it. - #[serde(skip)] + #[cfg_attr(not(feature = "dropin"), serde(skip))] pub origin: MemOrigin, } diff --git a/rust/src/nondet/c_log.rs b/rust/src/nondet/c_log.rs new file mode 100644 index 0000000..d668b56 --- /dev/null +++ b/rust/src/nondet/c_log.rs @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-3.0-only +// +// The nondet log of Map2Check's C runtime (`NonDetLog.c`): the rows that the +// `map2check_nondet_` calls of the instrumented program append, and +// `nondet_log_to_file`, which writes them to `klee_log.csv`. +// +// This log is not the JSON-report log (`AnalysisState::nondets`, typed +// `NonDetValue`): its rows hold what the C `NONDET_CALL` holds, an +// `enum NONDET_TYPE` number and a union of 8 bytes, so that U8, U16, U32, +// MEMORY_BYTE, LONGLONG and ULONGLONG keep the numbers and the printing +// rules of the C file. +#![deny(unsafe_code)] + +use std::path::Path; + +use crate::cfmt; + +/// `FUNCTION_MAX_LENGTH_NAME` (`Map2CheckTypes.h:20`). +pub const FUNCTION_MAX_LENGTH_NAME: usize = 35; + +/// `klee_log_file` (`NonDetLog.c:13`). +pub const KLEE_LOG_FILE: &str = "klee_log.csv"; + +/// `enum NONDET_TYPE` (`Map2CheckTypes.h:41-58`). +pub mod kind { + pub const INTEGER: i32 = 0; + pub const CHAR: i32 = 1; + pub const POINTER: i32 = 2; + pub const USHORT: i32 = 3; + pub const LONG: i32 = 4; + pub const UNSIGNED: i32 = 5; + pub const ULONG: i32 = 6; + pub const BOOL: i32 = 7; + pub const UCHAR: i32 = 8; + pub const PCHAR: i32 = 9; + pub const SIZE_T: i32 = 10; + pub const LOFF_T: i32 = 11; + pub const SECTOR_T: i32 = 12; + pub const UINT: i32 = 13; + pub const SHORT: i32 = 14; + pub const DOUBLE: i32 = 15; + pub const FLOAT: i32 = 16; + pub const LONGLONG: i32 = 17; + pub const ULONGLONG: i32 = 18; + pub const U8: i32 = 19; + pub const U16: i32 = 20; + pub const U32: i32 = 21; + pub const MEMORY_BYTE: i32 = 22; +} + +/// One `NONDET_CALL` (`Map2CheckTypes.h:150-172`). +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CNondetCall { + /// `enum NONDET_TYPE` number. + pub kind: i32, + pub line: u32, + pub scope: u32, + pub step: u32, + /// The 8 bytes of the C `value` union, as a little-endian integer: + /// `as_int`/`as_unsigned` are the low 32 bits. + value: u64, + /// `function_name[35]` as in C, with the length up to its first NUL: no + /// heap allocation per row. + function_name: [u8; FUNCTION_MAX_LENGTH_NAME], + name_len: u8, +} + +impl CNondetCall { + /// `new_nondet_call` (`NonDetLog.c:117-142`) with the value already in + /// its union bits: the name is cut at [`FUNCTION_MAX_LENGTH_NAME`] bytes + /// (`strncpy`), and the bits above the stored member stay zero + /// (`memset`). + pub fn from_parts( + kind: i32, + line: u32, + scope: u32, + step: u32, + function_name: &[u8], + value: u64, + ) -> Self { + let mut name = [0u8; FUNCTION_MAX_LENGTH_NAME]; + let mut len = 0; + for (slot, &b) in name.iter_mut().zip(function_name) { + if b == 0 { + break; + } + *slot = b; + len += 1; + } + Self { + kind, + line, + scope, + step, + value, + function_name: name, + name_len: len, + } + } + + /// The union bits: the `int` member zero-extended for the 32-bit types, + /// the 64-bit patterns of `double` (DOUBLE, FLOAT), `long long` and + /// `unsigned long long`. + pub fn value_bits(&self) -> u64 { + self.value + } + + pub fn function_name(&self) -> &[u8] { + &self.function_name[..usize::from(self.name_len)] + } + + /// Replaces the union bits (`concretize_nondet_log` writes the + /// concretized 8 bytes back, `NonDetGeneratorKlee.c:41-42`). + pub fn set_value_bits(&mut self, bits: u64) { + self.value = bits; + } + + /// One row of `klee_log.csv` (`NonDetLog.c:68-112`): + /// `index;line;scope;function;step;value;type\n`. + pub fn csv_row(&self, index: usize) -> Vec { + let mut row = Vec::with_capacity(64); + self.put_csv_row(&mut row, index); + row + } + + /// [`Self::csv_row`] appended to `row`. + pub fn put_csv_row(&self, row: &mut Vec, index: usize) { + // `put_longlong(end, (int)i)`. + cfmt::put_i64(row, i64::from(index as i32)); + cfmt::put_byte(row, b';'); + cfmt::put_u64(row, u64::from(self.line)); + cfmt::put_byte(row, b';'); + cfmt::put_u64(row, u64::from(self.scope)); + cfmt::put_byte(row, b';'); + cfmt::put(row, self.function_name()); + cfmt::put_byte(row, b';'); + cfmt::put_u64(row, u64::from(self.step)); + cfmt::put_byte(row, b';'); + match self.kind { + // `as_unsigned`, printed once (`:91-92`). + kind::UNSIGNED | kind::U32 => cfmt::put_u64(row, u64::from(self.value as u32)), + kind::DOUBLE => cfmt::put_lf(row, f64::from_bits(self.value)), + kind::FLOAT => cfmt::put_g9(row, f64::from_bits(self.value) as f32), + kind::LONGLONG => cfmt::put_i64(row, self.value as i64), + kind::ULONGLONG => cfmt::put_u64(row, self.value), + // `as_int`. + _ => cfmt::put_i64(row, i64::from(self.value as u32 as i32)), + } + cfmt::put_byte(row, b';'); + cfmt::put_i64(row, i64::from(self.kind)); + cfmt::put_byte(row, b'\n'); + } +} + +/// `printf("%.9g", value)` of glibc (`NonDetLog.c:99`, [`cfmt::put_g9`]). +pub fn c_float_g9(value: f32) -> String { + let mut out = Vec::new(); + cfmt::put_g9(&mut out, value); + String::from_utf8_lossy(&out).into_owned() +} + +/// `nondet_log_to_file` (`NonDetLog.c:50-125`): writes every row to +/// `dir/klee_log.csv`, creating the file even for an empty log. Returns +/// `FALSE` when the file cannot be written. +pub fn nondet_log_to_file(rows: &[CNondetCall], dir: &Path) -> bool { + let mut out = Vec::new(); + for (i, row) in rows.iter().enumerate() { + row.put_csv_row(&mut out, i); + } + crate::output::legacy_files::write_atomic_bytes(dir, KLEE_LOG_FILE, &out).is_ok() +} diff --git a/rust/src/nondet/klee.rs b/rust/src/nondet/klee.rs deleted file mode 100644 index e49a79a..0000000 --- a/rust/src/nondet/klee.rs +++ /dev/null @@ -1,307 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -// -// KLEE nondeterministic value generator: the `symex` runtime of Map2Check -// (feature `klee`, D1 route A', docs/cutover/decisoes.md). -// -// Built only from klee/Cargo.toml, with rustc 1.72.1 (LLVM 16), as bitcode -// that llvm-link-16 joins to the analysed program -// (scripts/build-klee-runtime.sh). Everything this file and the rest of the -// crate use must therefore compile on rustc 1.72 (D1 condition 5): no -// `unsafe extern`, no `LazyLock`, no std API newer than 1.72. -// -// Semantics are those of Map2Check develop's C generator -// (`modules/backend/library/lib/NonDetGeneratorKlee.c`): -// -// - each `__VERIFIER_nondet_` makes a fresh object of `sizeof(type)` -// bytes symbolic with `klee_make_symbolic`, named `non_det_` as -// develop names it (`"non_det_" #type`). The frontend reads the type back -// from that name (`ktest_reader.cpp`, `decodeKtestObject`), and turns a -// `.ktest` into a fuzzer seed by concatenating the objects' bytes -// (`ktestToFuzzerBytes`): `sizeof(type)` bytes per value, in order, in -// memory order (little-endian on x86-64), which is exactly what the -// AFL++/libFuzzer reader of `libfuzzer.rs` consumes (D1 condition 9); -// - `__VERIFIER_assume` is `klee_assume`; -// - `main` calls the analysed program's renamed `main` -// (`NonDetGeneratorKlee.c:16`); -// - values that reach the output (the witness logs, the JSON report) are -// concretised first with `klee_get_value_*` ([`concretize`], D1 -// condition 7): formatting a symbolic value forks KLEE on every digit. -// -// Violations end the state through the core's `std::process::abort()` -// (`output/termination.rs`), which KLEE reports as an abort error, as it -// does for develop's `abort()` in `map2check_exit`. -#![allow(unsafe_code)] - -use std::ffi::{c_char, c_int, c_void}; - -use super::NonDetValue; -use crate::state::AnalysisState; - -extern "C" { - fn klee_make_symbolic(addr: *mut c_void, nbytes: usize, name: *const c_char); - fn klee_assume(condition: usize); - fn klee_get_value_i32(expr: i32) -> i32; - fn klee_get_value_i64(expr: i64) -> i64; - /// The analysed program's `main`, renamed by the Map2CheckLibrary pass. - fn __map2check_main__(argc: c_int, argv: *mut *mut c_char) -> c_int; -} - -/// A fresh symbolic value of `T`'s size. `name` is NUL-terminated. -/// -/// `T` is one of the integer types below: any bit pattern is a valid `T`, -/// so KLEE may give the object any value. -fn symbolic(name: &'static [u8]) -> T { - debug_assert_eq!(name.last(), Some(&0)); - let mut value = T::default(); - // SAFETY: `value` is a live local of `size_of::()` bytes, and `name` - // is a NUL-terminated static string. - unsafe { - klee_make_symbolic( - (&mut value as *mut T).cast::(), - std::mem::size_of::(), - name.as_ptr().cast::(), - ); - } - value -} - -// ── Public nondet generators ───────────────────────────────────────────────── - -macro_rules! klee_nondet { - ($(#[$doc:meta])* $fn_name:ident, $ty:ty, $name:literal) => { - $(#[$doc])* - #[no_mangle] - pub extern "C" fn $fn_name() -> $ty { - crate::ffi::ffi_guard(|| symbolic::<$ty>(concat!($name, "\0").as_bytes()), 0) - } - }; -} - -klee_nondet!(__VERIFIER_nondet_char, i8, "non_det_char"); -klee_nondet!(__VERIFIER_nondet_uchar, u8, "non_det_uchar"); -klee_nondet!(__VERIFIER_nondet_short, i16, "non_det_short"); -klee_nondet!(__VERIFIER_nondet_ushort, u16, "non_det_ushort"); -klee_nondet!(__VERIFIER_nondet_int, i32, "non_det_int"); -klee_nondet!( - /// develop has `map2check_non_det_uint` and `_unsigned`; both are - /// `__VERIFIER_nondet_uint` in the core (symbol-matrix.tsv) and the - /// frontend reads both names as a 4-byte unsigned. - __VERIFIER_nondet_uint, - u32, - "non_det_uint" -); -klee_nondet!(__VERIFIER_nondet_long, i64, "non_det_long"); -klee_nondet!(__VERIFIER_nondet_ulong, u64, "non_det_ulong"); -klee_nondet!(__VERIFIER_nondet_size_t, usize, "non_det_size_t"); -klee_nondet!( - /// glibc's `loff_t` is `long long`. - __VERIFIER_nondet_loff_t, - i64, - "non_det_loff_t" -); -klee_nondet!( - /// develop: `#define sector_t unsigned long` (`NonDetGenerator.h:112`). - __VERIFIER_nondet_sector_t, - std::os::raw::c_ulong, - "non_det_sector_t" -); - -/// develop's `bool` is `typedef int bool` (`NonDetGenerator.h:74`): a 4-byte -/// object, returned as that `int` converted to `_Bool`, as `libfuzzer.rs` -/// reads it. -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_bool() -> bool { - crate::ffi::ffi_guard(|| symbolic::(b"non_det_bool\0") != 0, false) -} - -/// Four bytes, like `int`; develop has no `float` generator. The frontend -/// does not know the name `non_det_float` and decodes it as a signed int. -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_float() -> f32 { - crate::ffi::ffi_guard(|| f32::from_bits(symbolic::(b"non_det_float\0")), 0.0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_double() -> f64 { - crate::ffi::ffi_guard(|| f64::from_bits(symbolic::(b"non_det_double\0")), 0.0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_pointer() -> *mut c_void { - // An integer turned into a pointer; nothing here dereferences it. - crate::ffi::ffi_guard( - || symbolic::(b"non_det_pointer\0") as *mut c_void, - std::ptr::null_mut(), - ) -} - -/// `MAP2CHECK_MAX_FUZZED_STRING` of the AFL++ reader (`libfuzzer.rs`). -const MAX_FUZZED_STRING: u32 = 4096; - -/// A NUL-terminated string allocated with the C `malloc`, as develop's -/// `map2check_non_det_pchar` (`NonDetGeneratorKlee.c:40-55`): a symbolic -/// `unsigned` length (object `non_det_unsigned`), NULL when it is 0 or -/// when `malloc` fails, then `length - 1` symbolic chars (`non_det_char` -/// each) and a NUL. -/// -/// Unlike develop's KLEE generator, the length is capped at 4096 as the -/// AFL++ reader caps it, so that a KLEE vector replays through the fuzzer -/// reader as the same string (D1 condition 9). -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_pchar() -> *mut c_char { - extern "C" { - fn malloc(size: usize) -> *mut c_void; - } - crate::ffi::ffi_guard( - || { - let length = symbolic::(b"non_det_unsigned\0").min(MAX_FUZZED_STRING) as usize; - if length == 0 { - return std::ptr::null_mut(); - } - // SAFETY: plain C allocation; the result is checked for NULL. - let string = unsafe { malloc(length) }.cast::(); - if string.is_null() { - return std::ptr::null_mut(); - } - for i in 0..length - 1 { - // SAFETY: `i < length`, inside the `length`-byte block. - unsafe { string.add(i).write(symbolic::(b"non_det_char\0")) }; - } - // SAFETY: `length - 1 < length`. - unsafe { string.add(length - 1).write(0) }; - string.cast() - }, - std::ptr::null_mut(), - ) -} - -// ── SV-COMP verifier primitives ────────────────────────────────────────────── - -/// `nondet_assume` of develop's KLEE generator: `klee_assume(expr)`. A -/// false condition ends the path (KLEE stops exploring it); one that is -/// provably false is a KLEE user error, as in develop. -#[no_mangle] -pub extern "C" fn __VERIFIER_assume(condition: i32) { - crate::ffi::ffi_guard( - || { - // SAFETY: KLEE intrinsic, takes the condition by value. - unsafe { klee_assume(usize::from(condition != 0)) } - }, - (), - ); -} - -/// SV-COMP reachability primitive, as in `libfuzzer.rs`: with an analysis -/// mode set it does nothing, as develop's (`Map2CheckFunctions.c:57-59`), -/// so KLEE keeps exploring; with none it records `Reachability` and aborts -/// (SD-08). -#[no_mangle] -pub extern "C" fn __VERIFIER_error() { - if crate::ffi::ffi_guard(super::record_verifier_error, false) { - std::process::abort(); - } -} - -/// SV-COMP assert primitive: `__VERIFIER_error()` when `condition` is false -/// (a no-op with a mode set, as develop's `AnalysisModeAssert.c:25-27`). -#[no_mangle] -pub extern "C" fn __VERIFIER_assert(condition: i32) { - crate::ffi::ffi_guard( - || { - if condition == 0 { - __VERIFIER_error(); - } - }, - (), - ); -} - -/// KLEE's entry point. develop's KLEE generator defines `main` the same way -/// (`NonDetGeneratorKlee.c:16`): the passes renamed the program's `main` to -/// `__map2check_main__` and inserted the mode, init and success calls. -// `argv` is only handed on to the program's own `main`, never dereferenced -// here; the signature is the C one, so the function cannot be `unsafe`. -#[allow(clippy::not_unsafe_ptr_arg_deref)] -#[no_mangle] -pub extern "C" fn main(argc: c_int, argv: *mut *mut c_char) -> c_int { - crate::ffi::ffi_guard( - // SAFETY: the analysed program's entry point, linked by llvm-link. - || unsafe { __map2check_main__(argc, argv) }, - 0, - ) -} - -// ── Concretisation before output (D1 condition 7) ──────────────────────────── - -/// A concrete value of `expr` in the current path, which the path is then -/// constrained to. Constraining it keeps the values concretised one after -/// another jointly consistent (as KLEE does when it concretises the -/// arguments of an external call such as develop's `fprintf`). -fn concrete_i64(expr: i64) -> i64 { - // SAFETY: KLEE intrinsics, by value. - unsafe { - let value = klee_get_value_i64(expr); - klee_assume(usize::from(expr == value)); - value - } -} - -fn concrete_i32(expr: i32) -> i32 { - // SAFETY: KLEE intrinsics, by value. - unsafe { - let value = klee_get_value_i32(expr); - klee_assume(usize::from(expr == value)); - value - } -} - -fn concrete_usize(expr: usize) -> usize { - concrete_i64(expr as i64) as usize -} - -fn concrete_value(value: &NonDetValue) -> NonDetValue { - use NonDetValue as V; - match *value { - V::Int(v) => V::Int(concrete_i32(v)), - V::UInt(v) => V::UInt(concrete_i32(v as i32) as u32), - V::Long(v) => V::Long(concrete_i64(v)), - V::Char(v) => V::Char(concrete_i32(i32::from(v)) as i8), - V::Float(v) => V::Float(f32::from_bits(concrete_i32(v.to_bits() as i32) as u32)), - V::Double(v) => V::Double(f64::from_bits(concrete_i64(v.to_bits() as i64) as u64)), - V::Unsigned(v) => V::Unsigned(concrete_i32(v as i32) as u32), - V::Pointer(v) => V::Pointer(concrete_usize(v)), - V::UShort(v) => V::UShort(concrete_i32(i32::from(v)) as u16), - V::Short(v) => V::Short(concrete_i32(i32::from(v)) as i16), - V::ULong(v) => V::ULong(concrete_i64(v as i64) as u64), - V::Bool(v) => V::Bool(concrete_i32(v)), - V::UChar(v) => V::UChar(concrete_i32(i32::from(v)) as u8), - V::PChar(v) => V::PChar(concrete_usize(v)), - V::SizeT(v) => V::SizeT(concrete_usize(v)), - V::LoffT(v) => V::LoffT(concrete_i64(v)), - V::SectorT(v) => V::SectorT(concrete_i64(v as i64) as u64), - } -} - -fn concretize_memtrack(entry: &mut crate::memtrack::MemTrackEntry) { - entry.var_addr = concrete_usize(entry.var_addr); - entry.points_to = concrete_usize(entry.points_to); - entry.size_destiny = concrete_usize(entry.size_destiny); - entry.size_primitive = concrete_usize(entry.size_primitive); -} - -/// Replaces every value of the state that the output formats and that the -/// program can make symbolic (nondet values; addresses and sizes of the -/// memory records, which a symbolic size or index reaches) by a concrete -/// value of the current path. Called right before the witness logs or the -/// JSON report are written (`output::prepare_for_output`). -pub(crate) fn concretize(state: &mut AnalysisState) { - for entry in &mut state.nondets { - entry.value = concrete_value(&entry.value); - } - for entry in &mut state.memtrack { - concretize_memtrack(entry); - } - if let Some(entry) = state.failed_free.as_mut() { - concretize_memtrack(entry); - } -} diff --git a/rust/src/nondet/libfuzzer.rs b/rust/src/nondet/libfuzzer.rs deleted file mode 100644 index f4442d5..0000000 --- a/rust/src/nondet/libfuzzer.rs +++ /dev/null @@ -1,992 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -// -// LibFuzzer nondeterministic value generation. -// Replaces nondet_gen_libfuzzer.c — eliminates all union type-punning -// by using f32::from_bits() / f64::from_bits() from safe Rust. -// -// Input semantics are those of Map2Check develop's AFL++ generator -// (`modules/backend/library/lib/NonDetGeneratorAFL.c`), which D1 conditions -// 4 and 9 require (docs/cutover/decisoes.md): -// -// - each value takes `sizeof(type)` bytes of the input, in order, copied -// into the value in memory order (`get_bytes_from_afl`, :67-74, and the -// generators, :76-99): little-endian on x86-64, the layout KLEE writes -// into a `.ktest` for `klee_make_symbolic(&v, sizeof v)`; -// - past the end of the input the reader rewinds (`get_next_input_from_afl`, -// :55-62): the read that finds the index at the end returns `data[0]` -// without advancing, so after every wrap `data[0]` is read twice; -// - `pchar` reads an `unsigned` length, returns NULL for 0, caps it at 4096, -// and fills `length - 1` chars plus a NUL (:101-118); -// - a failed `__VERIFIER_assume` abandons the current input and the loop -// moves on to the next one (`nondet_cancel`/`setjmp`, :31-37, :171-184). - -use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; - -static FUZZER_CURSOR: AtomicUsize = AtomicUsize::new(0); -static FUZZER_SIZE: AtomicUsize = AtomicUsize::new(0); -static FUZZER_DATA: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); -/// True while `LLVMFuzzerTestOneInput` runs the program for one input. -/// -/// Process-wide, not per thread: a program thread that calls -/// `__VERIFIER_assume(0)` while an iteration runs on another thread unwinds -/// a stack with no `catch_unwind` on it, and the process aborts. Develop's -/// cross-thread `longjmp` is undefined as well; the fuzzer drives the -/// program on one thread. -static ITERATION_ACTIVE: AtomicBool = AtomicBool::new(false); - -struct FuzzerStateReset; - -impl Drop for FuzzerStateReset { - fn drop(&mut self) { - ITERATION_ACTIVE.store(false, Ordering::SeqCst); - FUZZER_DATA.store(std::ptr::null_mut(), Ordering::SeqCst); - FUZZER_SIZE.store(0, Ordering::SeqCst); - FUZZER_CURSOR.store(0, Ordering::SeqCst); - } -} - -/// Unwind payload of a failed `__VERIFIER_assume` inside an iteration: the -/// Rust counterpart of develop's `longjmp(map2check_reject_env, 1)`. -struct InputRejected; - -/// Called by LibFuzzer (or AFL++'s libFuzzer driver) once per input. -/// -/// A failed `__VERIFIER_assume` ends the iteration early, as develop's -/// `nondet_cancel` does: it unwinds to this function, which returns 0 so the -/// fuzzer goes on with the next input. The analysis state is reset at the -/// start of every iteration (#9), so the abandoned one leaves nothing behind. -/// -/// # Safety -/// `data` must remain valid for the duration of one fuzzer iteration. -/// This is guaranteed by the LibFuzzer runtime contract. -#[no_mangle] -pub unsafe extern "C" fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32 { - let _reset = FuzzerStateReset; - let started = crate::ffi::ffi_guard( - || { - crate::state::reset(); - if data.is_null() && size != 0 { - return false; - } - // SAFETY: LibFuzzer guarantees `data` is valid for `size` bytes. - FUZZER_DATA.store(data as *mut u8, Ordering::SeqCst); - FUZZER_SIZE.store(size, Ordering::SeqCst); - FUZZER_CURSOR.store(0, Ordering::SeqCst); - ITERATION_ACTIVE.store(true, Ordering::SeqCst); - true - }, - false, - ); - if !started { - return 0; - } - unsafe extern "C-unwind" { - /// The analysed program's `main`, renamed by the Map2Check pass. - /// `C-unwind`: a failed `__VERIFIER_assume` unwinds out of it. - fn __map2check_main__() -> i32; - } - // SAFETY: user-defined entry point, linked by Map2Check instrumentation. - let run = std::panic::catch_unwind(|| unsafe { __map2check_main__() }); - if let Err(payload) = run { - if !payload.is::() { - eprintln!("[map2check] panic in FFI boundary; recovering safely"); - } - } - 0 -} - -/// Fills `N` bytes from the input as develop's `get_bytes_from_afl` does, -/// one `get_next_input_from_afl` per byte. With no input (no iteration, or an -/// empty one) every byte is 0: develop would read `data[0]` of an empty -/// buffer, out of bounds. -fn read_value() -> [u8; N] { - let mut out = [0_u8; N]; - let ptr = FUZZER_DATA.load(Ordering::Relaxed); - let size = FUZZER_SIZE.load(Ordering::Relaxed); - if ptr.is_null() || size == 0 { - return out; - } - // SAFETY: FUZZER_DATA was set from a valid LibFuzzer buffer of `size` - // bytes, which stays valid for the whole iteration. - let data = unsafe { std::slice::from_raw_parts(ptr, size) }; - let mut index = FUZZER_CURSOR.load(Ordering::Relaxed); - for byte in &mut out { - if index < size { - *byte = data[index]; - index += 1; - } else { - index = 0; - *byte = data[0]; - } - } - FUZZER_CURSOR.store(index, Ordering::Relaxed); - out -} - -// ── Public nondet generators ───────────────────────────────────────────────── - -/// develop's `bool` is `typedef int bool` (`NonDetGenerator.h:74`): four -/// bytes, returned as that `int` converted to `_Bool`. -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_bool() -> bool { - crate::ffi::ffi_guard(|| i32::from_ne_bytes(read_value()) != 0, false) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_char() -> i8 { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_uchar() -> u8 { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_short() -> i16 { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_ushort() -> u16 { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_int() -> i32 { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_uint() -> u32 { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_long() -> i64 { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_ulong() -> u64 { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_size_t() -> usize { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -/// glibc's `loff_t` is `long long`. -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_loff_t() -> i64 { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -/// develop: `#define sector_t unsigned long` (`NonDetGenerator.h:112`). -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_sector_t() -> std::os::raw::c_ulong { - crate::ffi::ffi_guard(|| ::from_ne_bytes(read_value()), 0) -} - -/// Four bytes, like `int`; develop has no `float` generator. -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_float() -> f32 { - // Uses f32::from_bits — replaces C union FloatPattern type-punning safely - crate::ffi::ffi_guard(|| f32::from_bits(u32::from_ne_bytes(read_value())), 0.0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_double() -> f64 { - // Uses f64::from_bits — replaces C union DoublePattern type-punning safely - crate::ffi::ffi_guard(|| f64::from_bits(u64::from_ne_bytes(read_value())), 0.0) -} - -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_pointer() -> *mut std::ffi::c_void { - // Constructing a pointer value from an integer is safe; only - // dereferencing it would be unsafe, and nothing here dereferences it. - crate::ffi::ffi_guard( - || usize::from_ne_bytes(read_value()) as *mut std::ffi::c_void, - std::ptr::null_mut(), - ) -} - -/// develop's `MAP2CHECK_MAX_FUZZED_STRING` (`NonDetGeneratorAFL.c:101`). -const MAX_FUZZED_STRING: u32 = 4096; - -/// A NUL-terminated string from the input, allocated with the C `malloc` so -/// the program can `free` it (`NonDetGeneratorAFL.c:103-118`): an -/// `unsigned` length, NULL when it is 0 (or when `malloc` fails), capped at -/// 4096, then `length - 1` chars, each read like `__VERIFIER_nondet_char`. -#[no_mangle] -pub extern "C" fn __VERIFIER_nondet_pchar() -> *mut std::os::raw::c_char { - extern "C" { - fn malloc(size: usize) -> *mut std::ffi::c_void; - } - crate::ffi::ffi_guard( - || { - let length = u32::from_ne_bytes(read_value()).min(MAX_FUZZED_STRING) as usize; - if length == 0 { - return std::ptr::null_mut(); - } - // SAFETY: plain C allocation; the result is checked for NULL. - let string = unsafe { malloc(length) }.cast::(); - if string.is_null() { - return std::ptr::null_mut(); - } - // SAFETY: `string` holds `length` bytes, written once each below. - let bytes = unsafe { std::slice::from_raw_parts_mut(string, length) }; - let (chars, nul) = bytes.split_at_mut(length - 1); - for c in chars { - *c = u8::from_ne_bytes(read_value()); - } - nul[0] = 0; - string.cast() - }, - std::ptr::null_mut(), - ) -} - -// ── SV-COMP verifier primitives ────────────────────────────────────────────── -// -// `__VERIFIER_assume(0)` inside a fuzzer iteration abandons that input, as -// develop's `nondet_assume` → `nondet_cancel` → `longjmp` back to the -// `__AFL_LOOP` does (NonDetGeneratorAFL.c:31-37, :171-184): no exit, no -// abort, and the next input runs in the same process. -// -// Mechanism (design note): a `longjmp` from Rust back to a `setjmp` would -// need the `setjmp` in C (Rust cannot call `setjmp` soundly, it returns -// twice) and would skip Rust frames, which is only defined when none of -// them has a destructor; `LLVMFuzzerTestOneInput` owns a drop guard. Instead -// the assume unwinds with `std::panic::resume_unwind` (no panic hook, no -// message) and `LLVMFuzzerTestOneInput` catches it with `catch_unwind`. -// Every Rust function on that path is declared `C-unwind` -// (`__VERIFIER_assume`, `map2check_fuzzer_assume`, the `__map2check_main__` -// import), which is the defined way for a Rust unwind to cross C frames -// (RFC 2945). The C frames in between are the analysed program's. clang -// marks C functions `nounwind` unless the program is compiled with -// `-fexceptions`, and unwinding out of a `nounwind` function is undefined -// in LLVM IR; without `-fexceptions` the unwind only works because x86-64 -// Linux emits `.eh_frame` by default, and with no unwind tables at all it -// aborts ("failed to initiate panic"). D1 condition 11 therefore requires -// the pipeline to compile the program with `-fexceptions` (Task 3.2). Plain -// C frames hold no cleanups, so the unwind skips what develop's `longjmp` -// skips; a frame with `__attribute__((cleanup))` compiled with -// `-fexceptions` would have its cleanup run, which `longjmp` would skip. -// The unwind needs -// `panic = "unwind"` (the `runtime` and `release` profiles); built with -// `panic = "abort"` the library keeps the exit(0) fallback below. -// -// Outside an iteration (no `LLVMFuzzerTestOneInput` on the stack, e.g. a -// program that links the library without a fuzzer driver), -// `__VERIFIER_assume(0)` keeps ending the process with exit code 0, the -// SV-COMP convention for an infeasible path (SD-07). - -/// Abandons the current fuzzer input, or ends the process with exit code 0 -/// when there is none. Never returns. -fn reject_input() -> ! { - #[cfg(panic = "unwind")] - if ITERATION_ACTIVE.load(Ordering::SeqCst) { - std::panic::resume_unwind(Box::new(InputRejected)); - } - std::process::exit(0); -} - -/// Abandons the current fuzzer input when `condition` is false (see the -/// design note above); outside an iteration, ends the process with exit 0. -#[no_mangle] -pub extern "C-unwind" fn __VERIFIER_assume(condition: i32) { - // The decision runs under ffi_guard; the unwind must not, or ffi_guard - // would catch it. - if crate::ffi::ffi_guard(|| condition == 0, false) { - reject_input(); - } -} - -/// Fuzzer-specific alias of `__VERIFIER_assume`, matching the legacy -/// `map2check_fuzzer_assume` entry point used by the LibFuzzer nondet -/// generator. -#[no_mangle] -pub extern "C-unwind" fn map2check_fuzzer_assume(condition: i32) { - if crate::ffi::ffi_guard(|| condition == 0, false) { - reject_input(); - } -} - -/// SV-COMP reachability primitive. With an analysis mode set it does -/// nothing, as in develop: the target pass reports reachability through -/// `map2check_reach_target`. With the mode unset it records a -/// `Reachability` violation, prints the JSON report and aborts (SD-08). -#[no_mangle] -pub extern "C" fn __VERIFIER_error() { - if crate::ffi::ffi_guard(super::record_verifier_error, false) { - std::process::abort(); - } -} - -/// SV-COMP assert primitive: calls `__VERIFIER_error()` when `condition` -/// is false, which with an analysis mode set does nothing (develop's -/// `__VERIFIER_assert` is empty; the assert pass emits -/// `map2check_is_valid_assert`) and with none aborts (SD-08). -#[no_mangle] -pub extern "C" fn __VERIFIER_assert(condition: i32) { - crate::ffi::ffi_guard( - || { - if condition == 0 { - __VERIFIER_error(); - } - }, - (), - ); -} - -//lv -// Cria uma função "falsa" apenas para os testes passarem no Linker -#[cfg(test)] -#[no_mangle] -pub extern "C-unwind" fn __map2check_main__() -> i32 { - // Runs the per-test hook (if any); otherwise succeeds and does nothing. - let hook = *TEST_MAIN_HOOK - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(hook) = hook { - hook(); - } - 0 -} - -/// Body of the fake `__map2check_main__`, installed by a test while it holds -/// the global test lock. -#[cfg(test)] -static TEST_MAIN_HOOK: std::sync::Mutex> = std::sync::Mutex::new(None); - -#[cfg(test)] -mod tests { - use super::*; - - // NOTE: the branches that end the process (`exit(0)` / `abort()`) would - // kill the whole `cargo test` binary if called in-process. They are - // tested at the end of this module by re-running this test binary as a - // child process; `tests/ffi_harness.c` also exercises them. - - #[test] - fn verifier_nondet_pointer_does_not_panic() { - let _guard = crate::state::test_lock(); - let _ = __VERIFIER_nondet_pointer(); - } - - #[test] - fn verifier_assume_true_is_a_no_op() { - __VERIFIER_assume(1); - } - - #[test] - fn verifier_assert_true_is_a_no_op() { - __VERIFIER_assert(1); - } - - #[test] - fn map2check_fuzzer_assume_true_is_a_no_op() { - map2check_fuzzer_assume(1); - } - - /// Installs `data` as the current fuzzer input for the duration of `f`, - /// under the global test lock, and clears it afterwards. - fn with_input(data: &[u8], f: impl FnOnce() -> R) -> R { - let _guard = crate::state::test_lock(); - let _reset = FuzzerStateReset; - FUZZER_DATA.store(data.as_ptr() as *mut u8, Ordering::SeqCst); - FUZZER_SIZE.store(data.len(), Ordering::SeqCst); - FUZZER_CURSOR.store(0, Ordering::SeqCst); - f() - } - - // ── Input byte layout (D1 conditions 4 and 9) ─────────────────────────── - // - // develop's NonDetGeneratorAFL.c copies sizeof(type) bytes per value, in - // memory order, into the value (`get_bytes_from_afl`, :67-74; the - // generators, :76-99), so a value is the little-endian reading of its - // bytes on x86-64. Past the end of the input `get_next_input_from_afl` - // (:55-62) rewinds: the read that finds the index at the end returns - // data[0] WITHOUT advancing, so data[0] is read twice after every wrap. - - /// develop's `get_next_input_from_afl`, transcribed: the bytes `count` - /// reads return from a fresh input. - fn develop_stream(data: &[u8], count: usize) -> Vec { - let mut index = 0; - (0..count) - .map(|_| { - if index < data.len() { - index += 1; - data[index - 1] - } else { - index = 0; - data[0] - } - }) - .collect() - } - - #[test] - fn develop_stream_model_rewinds_and_repeats_the_first_byte() { - // Hand-checked against NonDetGeneratorAFL.c:55-62. - assert_eq!(develop_stream(&[1, 2], 7), [1, 2, 1, 1, 2, 1, 1]); - assert_eq!(develop_stream(&[0x41], 4), [0x41; 4]); - } - - /// Reads two values with `$gen` from `data` and checks them against the - /// bytes develop's reader returns for the same input. - macro_rules! layout_case { - ($gen:ident, $ty:ty, $data:expr) => {{ - let data: &[u8] = $data; - let size = std::mem::size_of::<$ty>(); - with_input(data, || { - let first: $ty = $gen(); - let second: $ty = $gen(); - let stream = develop_stream(data, 2 * size); - let expect_first = <$ty>::from_ne_bytes(stream[..size].try_into().unwrap()); - let expect_second = <$ty>::from_ne_bytes(stream[size..].try_into().unwrap()); - assert_eq!( - first.to_ne_bytes(), - expect_first.to_ne_bytes(), - "{}: primeiro valor de {data:02x?}", - stringify!($gen) - ); - assert_eq!( - second.to_ne_bytes(), - expect_second.to_ne_bytes(), - "{}: segundo valor de {data:02x?}", - stringify!($gen) - ); - }); - }}; - } - - /// Short input (fewer bytes than the type), exact-size input, and a - /// second value that spans the wrap, for one generator. - macro_rules! layout_cases { - ($gen:ident, $ty:ty) => {{ - let size = std::mem::size_of::<$ty>(); - let bytes: Vec = (1..=2 * size as u8 + 3) - .map(|b| b.wrapping_mul(0x11)) - .collect(); - // shorter than the type (one byte when the type has one byte) - layout_case!($gen, $ty, &bytes[..size.saturating_sub(1).max(1)]); - // exactly the type: the second value is all wrap - layout_case!($gen, $ty, &bytes[..size]); - // the second value starts inside the input and wraps - layout_case!($gen, $ty, &bytes[..size + size / 2 + 1]); - }}; - } - - #[test] - fn every_generator_reads_sizeof_type_bytes_and_wraps_like_develop() { - layout_cases!(__VERIFIER_nondet_char, i8); - layout_cases!(__VERIFIER_nondet_uchar, u8); - layout_cases!(__VERIFIER_nondet_short, i16); - layout_cases!(__VERIFIER_nondet_ushort, u16); - layout_cases!(__VERIFIER_nondet_int, i32); - layout_cases!(__VERIFIER_nondet_uint, u32); - layout_cases!(__VERIFIER_nondet_long, i64); - layout_cases!(__VERIFIER_nondet_ulong, u64); - layout_cases!(__VERIFIER_nondet_size_t, usize); - layout_cases!(__VERIFIER_nondet_loff_t, i64); - layout_cases!(__VERIFIER_nondet_sector_t, std::os::raw::c_ulong); - layout_cases!(nondet_float_bits, u32); - layout_cases!(nondet_double_bits, u64); - layout_cases!(nondet_pointer_bits, usize); - } - - fn nondet_float_bits() -> u32 { - __VERIFIER_nondet_float().to_bits() - } - - fn nondet_double_bits() -> u64 { - __VERIFIER_nondet_double().to_bits() - } - - fn nondet_pointer_bits() -> usize { - __VERIFIER_nondet_pointer() as usize - } - - #[test] - fn seed_a_reads_0x41414141_like_develop() { - // The spike's seed (`printf A`): develop reads x = 0x41414141, which - // CmpLog can match against the input (D1 condition 4). - with_input(b"A", || assert_eq!(__VERIFIER_nondet_int(), 0x4141_4141)); - with_input(&[0x2a, 0, 0, 0], || assert_eq!(__VERIFIER_nondet_int(), 42)); - } - - #[test] - fn bool_consumes_four_bytes_and_is_the_int_converted_to_bool() { - with_input(&[0, 0, 0, 0, 0, 1, 0, 0, 7], || { - assert!(!__VERIFIER_nondet_bool()); // 0x00000000 - assert!(__VERIFIER_nondet_bool()); // 0x00000100: low byte 0, int != 0 - assert_eq!(__VERIFIER_nondet_uchar(), 7); // the ninth byte - }); - // short inputs wrap: 00 00 00 00 and 00 01 00 00 - with_input(&[0], || assert!(!__VERIFIER_nondet_bool())); - with_input(&[0, 1], || assert!(__VERIFIER_nondet_bool())); - } - - #[test] - fn values_are_little_endian_in_input_order() { - let mut data = Vec::new(); - data.extend_from_slice(&(-2_i64).to_le_bytes()); - data.extend_from_slice(&1.5_f32.to_bits().to_le_bytes()); - data.extend_from_slice(&(-2.25_f64).to_bits().to_le_bytes()); - data.extend_from_slice(&0x1000_u64.to_le_bytes()); - data.extend_from_slice(&(-32767_i16).to_le_bytes()); - with_input(&data, || { - assert_eq!(__VERIFIER_nondet_long(), -2); - assert_eq!(__VERIFIER_nondet_float().to_bits(), 1.5_f32.to_bits()); - assert_eq!(__VERIFIER_nondet_double().to_bits(), (-2.25_f64).to_bits()); - assert_eq!(__VERIFIER_nondet_pointer() as usize, 0x1000); - assert_eq!(__VERIFIER_nondet_short(), -32767); - }); - } - - #[test] - fn empty_input_yields_zero() { - // develop reads data[0] of an empty buffer (out of bounds); the - // library returns zero bytes instead (semantic-deltas.md). - with_input(&[], || { - assert_eq!(__VERIFIER_nondet_int(), 0); - assert_eq!(__VERIFIER_nondet_ulong(), 0); - assert!(__VERIFIER_nondet_pchar().is_null()); - }); - } - - #[test] - fn without_an_iteration_every_generator_yields_zero() { - let _guard = crate::state::test_lock(); - assert!(!__VERIFIER_nondet_bool()); - assert_eq!(__VERIFIER_nondet_char(), 0); - assert_eq!(__VERIFIER_nondet_uchar(), 0); - assert_eq!(__VERIFIER_nondet_short(), 0); - assert_eq!(__VERIFIER_nondet_ushort(), 0); - assert_eq!(__VERIFIER_nondet_int(), 0); - assert_eq!(__VERIFIER_nondet_uint(), 0); - assert_eq!(__VERIFIER_nondet_long(), 0); - assert_eq!(__VERIFIER_nondet_ulong(), 0); - assert_eq!(__VERIFIER_nondet_size_t(), 0); - assert_eq!(__VERIFIER_nondet_float().to_bits(), 0); - assert_eq!(__VERIFIER_nondet_double().to_bits(), 0); - assert!(__VERIFIER_nondet_pointer().is_null()); - assert_eq!(__VERIFIER_nondet_loff_t(), 0); - assert_eq!(__VERIFIER_nondet_sector_t(), 0); - assert!(__VERIFIER_nondet_pchar().is_null()); - } - - // ── pchar (NonDetGeneratorAFL.c:101-118) ───────────────────────────────── - - extern "C" { - fn free(ptr: *mut std::ffi::c_void); - } - - /// Takes a `__VERIFIER_nondet_pchar` result: its bytes up to the NUL, and - /// frees it with the C allocator, as the analysed program would. - fn take_pchar(p: *mut std::os::raw::c_char) -> Option> { - if p.is_null() { - return None; - } - // SAFETY: a non-null result is a NUL-terminated malloc'd string. - let bytes = unsafe { std::ffi::CStr::from_ptr(p) }.to_bytes().to_vec(); - // SAFETY: allocated with malloc by the generator; freed once. - unsafe { free(p.cast()) }; - Some(bytes) - } - - #[test] - fn pchar_reads_an_unsigned_length_then_length_minus_one_chars() { - with_input(&[3, 0, 0, 0, b'a', b'b', b'c'], || { - assert_eq!(take_pchar(__VERIFIER_nondet_pchar()), Some(b"ab".to_vec())); - // the cursor is at 'c': the next generator reads it - assert_eq!(__VERIFIER_nondet_uchar(), b'c'); - }); - with_input(&[1, 0, 0, 0], || { - assert_eq!(take_pchar(__VERIFIER_nondet_pchar()), Some(Vec::new())); - }); - with_input(&[0, 0, 0, 0], || { - assert!(__VERIFIER_nondet_pchar().is_null()) - }); - } - - #[test] - fn pchar_caps_the_length_at_4096_and_wraps_for_its_chars() { - // length 0x41414141 > 4096: capped to 4096, 4095 chars of 'A'. - with_input(b"A", || { - let s = take_pchar(__VERIFIER_nondet_pchar()).unwrap(); - assert_eq!(s.len(), 4095); - assert!(s.iter().all(|&c| c == b'A')); - }); - } - - // ── __VERIFIER_assume inside an iteration (develop: nondet_cancel) ─────── - - static AFTER_ASSUME: AtomicUsize = AtomicUsize::new(0); - - /// Fake program: tracks a block, rejects inputs whose first int is not - /// 7 (as `nondet_assume` does with `longjmp`), and counts the runs that - /// got past the assume. - fn assume_body() { - // SAFETY: fake address, only stored. - unsafe { crate::ffi::map2check_map_malloc(0x2000 as *const _, 8) }; - let x = __VERIFIER_nondet_int(); - __VERIFIER_assume(i32::from(x == 7)); - AFTER_ASSUME.fetch_add(1, Ordering::SeqCst); - } - - #[test] - fn failed_assume_abandons_only_the_current_iteration() { - let _guard = crate::state::test_lock(); - set_hook(Some(assume_body)); - AFTER_ASSUME.store(0, Ordering::SeqCst); - - let rejected = 1_i32.to_le_bytes(); - let accepted = 7_i32.to_le_bytes(); - // SAFETY: buffers are valid for their lengths for the whole call. - let rc = unsafe { LLVMFuzzerTestOneInput(rejected.as_ptr(), rejected.len()) }; - assert_eq!(rc, 0); - assert_eq!(AFTER_ASSUME.load(Ordering::SeqCst), 0, "assume não cortou"); - assert!(!ITERATION_ACTIVE.load(Ordering::SeqCst)); - assert!(FUZZER_DATA.load(Ordering::SeqCst).is_null()); - // The state is usable at once, as at the start of an iteration. - assert!(crate::state::with_state(|s| Ok(s.result.ok)).is_ok()); - - // SAFETY: as above. - let rc = unsafe { LLVMFuzzerTestOneInput(accepted.as_ptr(), accepted.len()) }; - set_hook(None); - assert_eq!(rc, 0); - assert_eq!(AFTER_ASSUME.load(Ordering::SeqCst), 1); - crate::state::with_state(|s| { - assert_eq!(s.memtrack.len(), 1, "o bloco da iteração rejeitada vazou"); - Ok(()) - }) - .unwrap(); - } - - fn fuzzer_assume_body() { - map2check_fuzzer_assume(0); - AFTER_ASSUME.fetch_add(1, Ordering::SeqCst); - } - - #[test] - fn failed_fuzzer_assume_abandons_the_iteration_too() { - let _guard = crate::state::test_lock(); - set_hook(Some(fuzzer_assume_body)); - AFTER_ASSUME.store(0, Ordering::SeqCst); - let data = [0_u8; 4]; - for _ in 0..3 { - // SAFETY: valid buffer for the whole call. - assert_eq!( - unsafe { LLVMFuzzerTestOneInput(data.as_ptr(), data.len()) }, - 0 - ); - } - set_hook(None); - assert_eq!(AFTER_ASSUME.load(Ordering::SeqCst), 0); - } - - #[test] - fn null_buffer_with_nonzero_size_yields_zero() { - let _guard = crate::state::test_lock(); - let _reset = FuzzerStateReset; - FUZZER_SIZE.store(8, Ordering::SeqCst); - assert_eq!(__VERIFIER_nondet_long(), 0); - } - - #[test] - fn fuzzer_iteration_resets_analysis_state_and_input() { - let _guard = crate::state::test_lock(); - crate::state::with_state(|s| { - s.set_false(crate::caller::ViolatedProperty::Overflow, 7, "stale"); - Ok(()) - }) - .unwrap(); - let data = [1_u8, 2, 3, 4]; - // SAFETY: `data` is valid for `data.len()` bytes for the whole call. - let rc = unsafe { LLVMFuzzerTestOneInput(data.as_ptr(), data.len()) }; - assert_eq!(rc, 0); - assert!(crate::state::with_state(|s| Ok(s.result.ok)).unwrap()); - assert!(FUZZER_DATA.load(Ordering::SeqCst).is_null()); - assert_eq!(FUZZER_SIZE.load(Ordering::SeqCst), 0); - assert_eq!(FUZZER_CURSOR.load(Ordering::SeqCst), 0); - } - - /// Fake program body: tracks one allocation and one nondet value, and - /// records the first byte of its input. - fn iteration_body() { - // SAFETY: fake addresses are only stored, never dereferenced. - unsafe { crate::ffi::map2check_map_malloc(0x1000 as *const _, 8) }; - let _ = __VERIFIER_nondet_uchar(); - let first = crate::state::with_state(|s| { - s.nondets.push(crate::nondet::NonDetEntry::new( - 1, - 1, - 0, - "main", - crate::nondet::NonDetValue::Int(7), - )); - Ok(s.current_step()) - }) - .unwrap(); - ITERATION_STEPS.fetch_add(first as usize, Ordering::SeqCst); - } - - static ITERATION_STEPS: AtomicUsize = AtomicUsize::new(0); - - fn set_hook(hook: Option) { - *TEST_MAIN_HOOK.lock().unwrap() = hook; - } - - #[test] - fn two_fuzzer_iterations_yield_independent_results() { - let _guard = crate::state::test_lock(); - set_hook(Some(iteration_body)); - ITERATION_STEPS.store(0, Ordering::SeqCst); - - let first = [0xAA_u8, 1, 2]; - // SAFETY: buffers are valid for their lengths for the whole call. - unsafe { LLVMFuzzerTestOneInput(first.as_ptr(), first.len()) }; - // Dirty the state between iterations as a crashed/violating run would. - crate::state::with_state(|s| { - s.set_false(crate::caller::ViolatedProperty::Overflow, 9, "first"); - s.next_step(); - Ok(()) - }) - .unwrap(); - let second = [0xBB_u8]; - // SAFETY: as above. - unsafe { LLVMFuzzerTestOneInput(second.as_ptr(), second.len()) }; - set_hook(None); - - crate::state::with_state(|s| { - assert!(s.result.ok, "first-run violation leaked into the second"); - assert_eq!(s.result.line, 0); - assert!(s.result.function_name.is_empty()); - // The second body ran one step-counting call (nondet_uchar) itself. - assert_eq!(s.current_step(), 1, "step counter leaked"); - assert_eq!(s.memtrack.len(), 1, "first-run allocation leaked"); - assert_eq!(s.nondets.len(), 1, "first-run nondet log leaked"); - Ok(()) - }) - .unwrap(); - assert_eq!(FUZZER_CURSOR.load(Ordering::SeqCst), 0); - } - - #[test] - fn fuzzer_iteration_rejects_null_data_with_nonzero_size() { - let _guard = crate::state::test_lock(); - // SAFETY: the entry point must reject a null buffer with non-zero size - // without reading from it; nothing is dereferenced. - let rc = unsafe { LLVMFuzzerTestOneInput(std::ptr::null(), 16) }; - assert_eq!(rc, 0); - assert!(FUZZER_DATA.load(Ordering::SeqCst).is_null()); - } - - // ── Process-terminating branches (issue #29) ───────────────────────────── - // - // Each `*_child` test only does something when `CHILD_ENV` is set: the - // parent test re-runs this same test binary filtered to that one child and - // inspects how the child process ended. - - const CHILD_ENV: &str = "MAP2CHECK_FFI_CHILD"; - const STARTED: &str = "map2check-child-started"; - const SURVIVED: &str = "map2check-child-survived"; - - fn is_child() -> bool { - std::env::var_os(CHILD_ENV).is_some() - } - - /// Runs `nondet::libfuzzer::tests::` in a separate process. - fn run_child(name: &str) -> std::process::Output { - let exe = std::env::current_exe().unwrap(); - let output = std::process::Command::new(exe) - .args(["--exact", &format!("nondet::libfuzzer::tests::{name}")]) - .args(["--nocapture", "--test-threads=1"]) - .env(CHILD_ENV, "1") - .output() - .unwrap(); - let stdout = String::from_utf8_lossy(&output.stdout); - assert!( - stdout.contains(STARTED), - "o processo filho não rodou `{name}`:\n{stdout}" - ); - assert!( - !stdout.contains(SURVIVED), - "`{name}` deveria ter encerrado o processo:\n{stdout}" - ); - output - } - - /// Asserts the child ended via `exit(0)` before libtest printed its summary. - fn assert_exited_zero(output: &std::process::Output) { - let stdout = String::from_utf8_lossy(&output.stdout); - assert_eq!(output.status.code(), Some(0), "{stdout}"); - assert!(!stdout.contains("test result:"), "{stdout}"); - } - - /// Asserts the child aborted after printing a REACHABILITY report. - fn assert_aborted_with_reachability(output: &std::process::Output) { - use std::os::unix::process::ExitStatusExt; - let stdout = String::from_utf8_lossy(&output.stdout); - assert_eq!( - output.status.signal(), - Some(6), - "esperava SIGABRT:\n{stdout}" - ); - assert!(stdout.contains("REACHABILITY"), "{stdout}"); - } - - #[test] - fn verifier_assume_false_child() { - if is_child() { - println!("{STARTED}"); - __VERIFIER_assume(0); - println!("{SURVIVED}"); - } - } - - #[test] - fn verifier_assume_false_exits_with_zero() { - assert_exited_zero(&run_child("verifier_assume_false_child")); - } - - #[test] - fn map2check_fuzzer_assume_false_child() { - if is_child() { - println!("{STARTED}"); - map2check_fuzzer_assume(0); - println!("{SURVIVED}"); - } - } - - #[test] - fn map2check_fuzzer_assume_false_exits_with_zero() { - assert_exited_zero(&run_child("map2check_fuzzer_assume_false_child")); - } - - #[test] - fn verifier_error_child() { - if is_child() { - println!("{STARTED}"); - __VERIFIER_error(); - #[allow(unreachable_code)] - { - println!("{SURVIVED}"); - } - } - } - - #[test] - fn verifier_error_aborts_and_reports_reachability() { - assert_aborted_with_reachability(&run_child("verifier_error_child")); - } - - #[test] - fn verifier_assert_false_child() { - if is_child() { - println!("{STARTED}"); - __VERIFIER_assert(0); - println!("{SURVIVED}"); - } - } - - #[test] - fn verifier_assert_false_aborts_and_reports_reachability() { - assert_aborted_with_reachability(&run_child("verifier_assert_false_child")); - } - - // With an analysis mode set (a Map2Check run), `__VERIFIER_error` and - // `__VERIFIER_assert` do nothing, as in develop (`Map2CheckFunctions.c:57-59`, - // `AnalysisModeAssert.c:25-27`): the passes carry the target and assert - // checks (`map2check_reach_target`, `map2check_is_valid_assert`). - - /// Runs a child that must survive, and returns its stdout. - fn run_surviving_child(name: &str) -> String { - let exe = std::env::current_exe().unwrap(); - let output = std::process::Command::new(exe) - .args(["--exact", &format!("nondet::libfuzzer::tests::{name}")]) - .args(["--nocapture", "--test-threads=1"]) - .env(CHILD_ENV, "1") - .output() - .unwrap(); - let stdout = String::from_utf8_lossy(&output.stdout).into_owned(); - assert!( - output.status.success(), - "`{name}` não terminou bem:\n{stdout}" - ); - assert!( - stdout.contains(STARTED), - "o filho não rodou `{name}`:\n{stdout}" - ); - assert!( - stdout.contains(SURVIVED), - "`{name}` encerrou o processo:\n{stdout}" - ); - stdout - } - - fn current_property() -> crate::caller::ViolatedProperty { - crate::ffi::get_current_property() - } - - #[test] - fn verifier_error_with_memtrack_mode_child() { - if is_child() { - println!("{STARTED}"); - crate::ffi::map2check_set_mode(1); // memtrack - crate::ffi::map2check_init(); - __VERIFIER_error(); - __VERIFIER_assert(0); - println!("after-error={:?}", current_property()); - let block = [0_i32; 4]; - let base = block.as_ptr().cast::(); - // SAFETY: only the address is recorded and checked; nothing is read. - unsafe { - crate::ffi::map2check_map_malloc(base, 16); - crate::ffi::map2check_check_load( - block.as_ptr().wrapping_add(4).cast(), - 17, - 0, - 4, - b"main\0".as_ptr().cast(), - ); - } - println!("after-deref={:?}", current_property()); - println!("{SURVIVED}"); - } - } - - #[test] - fn verifier_error_is_a_no_op_with_a_mode_and_the_deref_is_reported() { - let out = run_surviving_child("verifier_error_with_memtrack_mode_child"); - assert!(out.contains("after-error=None"), "{out}"); - assert!(out.contains("after-deref=MemsafetyDeref"), "{out}"); - } - - #[test] - fn verifier_error_with_target_mode_child() { - if is_child() { - println!("{STARTED}"); - crate::ffi::map2check_set_mode(5); // target - crate::ffi::map2check_init(); - __VERIFIER_error(); - __VERIFIER_assert(0); - println!("after-error={:?}", current_property()); - // SAFETY: NUL-terminated literal. - unsafe { crate::ffi::map2check_reach_target(b"reach_error\0".as_ptr().cast(), 0, 12) }; - println!("after-target={:?}", current_property()); - println!("{SURVIVED}"); - } - } - - #[test] - fn target_mode_reaches_only_through_map2check_reach_target() { - let out = run_surviving_child("verifier_error_with_target_mode_child"); - assert!(out.contains("after-error=None"), "{out}"); - assert!(out.contains("after-target=Reachability"), "{out}"); - } -} diff --git a/rust/src/nondet/mod.rs b/rust/src/nondet/mod.rs index d7c43bf..a6d16ec 100644 --- a/rust/src/nondet/mod.rs +++ b/rust/src/nondet/mod.rs @@ -1,7 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-only -#![cfg_attr(not(any(feature = "libfuzzer", feature = "klee")), deny(unsafe_code))] - -use serde::Serialize; +#![deny(unsafe_code)] /// Typed nondeterministic value — replaces `union Data` + `enum var_type_t`. /// Each variant carries the actual value, eliminating unsafe type-punning. @@ -11,42 +9,43 @@ use serde::Serialize; /// (`bool` is `typedef int bool`, `sector_t` is `unsigned long`, `loff_t` is /// glibc's `long long`, `pointer` and `pchar` are addresses), plus `Float`, /// which develop does not instrument. -#[derive(Debug, Clone, Serialize, PartialEq)] -#[serde(tag = "type", content = "value")] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(not(feature = "dropin"), derive(serde::Serialize))] +#[cfg_attr(not(feature = "dropin"), serde(tag = "type", content = "value"))] pub enum NonDetValue { - #[serde(rename = "INT")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "INT"))] Int(i32), - #[serde(rename = "UINT")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "UINT"))] UInt(u32), - #[serde(rename = "LONG")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "LONG"))] Long(i64), - #[serde(rename = "CHAR")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "CHAR"))] Char(i8), - #[serde(rename = "FLOAT")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "FLOAT"))] Float(f32), - #[serde(rename = "DOUBLE")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "DOUBLE"))] Double(f64), - #[serde(rename = "UNSIGNED")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "UNSIGNED"))] Unsigned(u32), - #[serde(rename = "POINTER")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "POINTER"))] Pointer(usize), - #[serde(rename = "USHORT")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "USHORT"))] UShort(u16), - #[serde(rename = "SHORT")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "SHORT"))] Short(i16), - #[serde(rename = "ULONG")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "ULONG"))] ULong(u64), - #[serde(rename = "BOOL")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "BOOL"))] Bool(i32), - #[serde(rename = "UCHAR")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "UCHAR"))] UChar(u8), - #[serde(rename = "PCHAR")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "PCHAR"))] PChar(usize), - #[serde(rename = "SIZE_T")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "SIZE_T"))] SizeT(usize), - #[serde(rename = "LOFF_T")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "LOFF_T"))] LoffT(i64), - #[serde(rename = "SECTOR_T")] + #[cfg_attr(not(feature = "dropin"), serde(rename = "SECTOR_T"))] SectorT(u64), } @@ -75,7 +74,8 @@ impl NonDetValue { } /// Analogous to `non_det_log_t` in C. -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone)] +#[cfg_attr(not(feature = "dropin"), derive(serde::Serialize))] pub struct NonDetEntry { pub step: u64, pub line: u32, @@ -96,51 +96,8 @@ impl NonDetEntry { } } -/// LibFuzzer nondeterministic value generator — feature-gated. -#[cfg(feature = "libfuzzer")] -pub mod libfuzzer; - -/// KLEE nondeterministic value generator (`symex`) — feature-gated, built -/// only from `klee/Cargo.toml` with rustc 1.72.1 (D1, route A'). Not in the -/// C header: the KLEE runtime is linked as bitcode, and its `main` and the -/// KLEE intrinsics it imports are not library API. -/// -/// cbindgen:ignore -#[cfg(feature = "klee")] -pub mod klee; - -/// Body of `__VERIFIER_error`, the same for every generator. Returns -/// whether the export must abort. -/// -/// With an analysis mode set (a Map2Check run) it does nothing, as develop's -/// `__VERIFIER_error` (`Map2CheckFunctions.c:57-59`) and `__VERIFIER_assert` -/// (`AnalysisModeAssert.c:25-27`): there the passes carry the checks, the -/// target through `map2check_reach_target` and asserts through -/// `map2check_is_valid_assert`. An abort here would end the run with no -/// property recorded, and under KLEE's `--exit-on-error-type=Abort` stop the -/// exploration before the real violation (SD-08). -/// -/// With the mode unset (direct API users), records a `Reachability` -/// violation, prints the JSON report and asks the export to abort. -#[cfg(any(feature = "libfuzzer", feature = "klee"))] -fn record_verifier_error() -> bool { - if crate::state::config().mode.is_some() { - return false; - } - if let Err(e) = crate::state::with_state(|s| { - s.set_false( - crate::caller::ViolatedProperty::Reachability, - 0, - "__VERIFIER_error", - ); - Ok(()) - }) { - eprintln!("[map2check] error: {e}"); - } - // Out of the lock (#19 ruling). - crate::output::print_report_unless_mode_set(); - true -} +/// The nondet log of the C runtime (`NonDetLog.c`) and its `klee_log.csv`. +pub mod c_log; #[cfg(test)] mod tests { diff --git a/rust/src/output/legacy_files.rs b/rust/src/output/legacy_files.rs index afe3a45..1845aef 100644 --- a/rust/src/output/legacy_files.rs +++ b/rust/src/output/legacy_files.rs @@ -24,14 +24,15 @@ // rules of `AnalysisModeMemtrack.c` / `AnalysisModeMemcleanup.c`. #![deny(unsafe_code)] -use std::fmt::Write as _; -use std::io; use std::path::Path; +use crate::cfmt; +use crate::sys::{self, FileError, FileResult}; + use crate::{ bbtrack::BasicBlockEntry, caller::{AnalysisResult, ViolatedProperty}, - memtrack::{MemOrigin, MemTrackEntry}, + memtrack::{c_mode, MemOrigin, MemTrackEntry}, nondet::{NonDetEntry, NonDetValue}, }; @@ -56,165 +57,401 @@ pub const LIST_LOG_FILE: &str = "list_log.csv"; pub const ALLOCATION_LOG_FILE: &str = "allocation_log.csv"; pub const HEAP_LOG_FILE: &str = "heap_log.csv"; -/// `%p` of glibc: `(nil)` for NULL, otherwise `0x` and lowercase hex. +/// `%p` of glibc: `(nil)` for NULL, otherwise `0x` and lowercase hex +/// ([`cfmt::put_ptr`]). pub fn c_pointer(address: usize) -> String { - if address == 0 { - "(nil)".to_owned() - } else { - format!("{address:#x}") - } + let mut out = Vec::new(); + cfmt::put_ptr(&mut out, address as u64); + String::from_utf8_lossy(&out).into_owned() } -/// `%lf` of glibc: six decimals, `inf`/`nan` with their sign. +/// `%lf` of glibc: six decimals, `inf`/`nan` with their sign +/// ([`cfmt::put_lf`]). pub fn c_double(value: f64) -> String { - let sign = if value.is_sign_negative() { "-" } else { "" }; - if value.is_nan() { - format!("{sign}nan") - } else if value.is_infinite() { - format!("{sign}inf") - } else { - format!("{value:.6}") - } + let mut out = Vec::new(); + cfmt::put_lf(&mut out, value); + String::from_utf8_lossy(&out).into_owned() } -/// Text of the verdict, as `write_property` prints it -/// (`PropertyGenerator.c:50-106`), and the extra per-property file the C -/// runtime writes next to `map2check_property`. `line` is printed with -/// `%d`, so it is shown as a C `int`. -fn property_text(result: &AnalysisResult) -> (Option, Option<&'static str>) { - let located = |tag: &str| { - format!( - "{tag}\nLine: {}\nFunction: {}\n", - result.line as i32, result.function_name - ) - }; - if result.ok { - return (Some("NONE\n".to_owned()), None); - } - match result.property { - ViolatedProperty::None => (Some("NONE\n".to_owned()), None), - ViolatedProperty::MemsafetyFree => { - (Some(located("FALSE-FREE")), Some("map2check_property_free")) - } - ViolatedProperty::MemsafetyDeref => ( - Some(located("FALSE-DEREF")), - Some("map2check_property_deref"), - ), - ViolatedProperty::Overflow => ( - Some(located("OVERFLOW")), - Some("map2check_property_overflow"), - ), - ViolatedProperty::Assert => (Some(located("ASSERT")), Some("map2check_property_assert")), - ViolatedProperty::MemsafetyMemtrack => ( - Some("FALSE-MEMTRACK\n".to_owned()), - Some("map2check_property_memtrack"), - ), - ViolatedProperty::MemsafetyMemcleanup => ( - Some("FALSE-MEMCLEANUP\n".to_owned()), - Some("map2check_property_memcleanup"), - ), - ViolatedProperty::Reachability => (Some(located("TARGET-REACHED")), None), - // `:98-102`: no per-property file. - ViolatedProperty::DivByZero => (Some(located("FALSE-DIVBYZERO")), None), - // The only UNKNOWN the C runtime reports is `write_property_unknown` - // (NULL from malloc, AnalysisModeMemtrack.c:305-311): it writes - // `map2check_property__unknown` and no `map2check_property`. - // Concurrency has no develop counterpart and is reported the same way. - ViolatedProperty::Unknown | ViolatedProperty::Concurrency => (None, None), - } -} - -/// Writes `contents` to `dir/name` atomically: the full text goes to a -/// temporary file of the same directory, which is then renamed over the -/// target. `std::fs::write` truncates the target and writes in separate -/// calls; under KLEE (`--external-calls=all`) the forked states share the -/// real file system and one state can truncate the file after another wrote -/// its violation, leaving it empty when KLEE halts on the abort. A reader of -/// the target sees the old file or the new one, never an empty or partial -/// one. +/// Whether the result files are written atomically (SD-20). Under KLEE the +/// forked states share the host file system, and a write that truncates and +/// then writes lets one state erase the violation another one wrote; under +/// AFL++ and the `none` generator nothing races, and develop's +/// `fopen("w")` / `fprintf` / `fclose` costs a third of the temp-file + +/// `rename` path on every execution (Task 2.8 measurement). /// -/// The temporary name has the pid and a counter. KLEE states -/// share the pid and each has a copy of the counter, so the file is created -/// with `create_new` and a name that already exists is skipped. -pub fn write_atomic(dir: &Path, name: &str, contents: &str) -> io::Result<()> { - use std::io::Write as _; +/// The default depends on the build. Outside `dropin` (the staticlib, cargo +/// tests, the goldens, the legacy KLEE runtime) it is atomic, as before. In +/// the drop-in core (feature `dropin`, one `Map2CheckFunctions.bc` for every +/// generator, D7) it starts plain, and the KLEE generator module turns it on +/// from its `nondet_init` through [`crate::ffi::__m2c_rt_output_set_atomic`], +/// before any file is written. The user approved this scope on 2026-10-27. +static ATOMIC_WRITES: std::sync::atomic::AtomicBool = + std::sync::atomic::AtomicBool::new(!cfg!(feature = "dropin")); + +/// Serializes the unit tests that depend on the write mode (the SD-20 tests) +/// with the ones that change it. +#[cfg(test)] +pub(crate) fn write_mode_test_lock() -> std::sync::MutexGuard<'static, ()> { + static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); + LOCK.lock() + .unwrap_or_else(std::sync::PoisonError::into_inner) +} + +/// Selects atomic (`true`) or plain (`false`) result-file writes. +pub fn set_atomic_writes(on: bool) { + ATOMIC_WRITES.store(on, std::sync::atomic::Ordering::Relaxed); +} + +/// Never downgrade a verdict (SD-20, user decision of 2026-10-27): under +/// KLEE the forked states share the host file system, and a state that ends +/// without a violation must not replace the `map2check_property` a violating +/// state already wrote. On only in the drop-in core under the KLEE generator +/// (`__m2c_rt_output_set_atomic(1)` from `NonDetGeneratorKlee`'s +/// `nondet_init`); off in plain mode and in every build that never calls it +/// (the staticlib keeps replacing, as before). +static NEVER_DOWNGRADE: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); + +/// Turns the never-downgrade rule on or off (see [`NEVER_DOWNGRADE`]). +pub fn set_never_downgrade(on: bool) { + NEVER_DOWNGRADE.store(on, std::sync::atomic::Ordering::Relaxed); +} + +/// Whether the never-downgrade rule is on. +pub fn never_downgrade() -> bool { + NEVER_DOWNGRADE.load(std::sync::atomic::Ordering::Relaxed) +} + +/// Writes `contents` to `dir/name` only if `dir/name` does not exist, and +/// atomically: the text goes to a temporary file, which is then hard-linked +/// to the target. `link(2)` fails with `EEXIST` when the target exists, in +/// one system call, so no other KLEE state can write the target between a +/// check and the replacement (there is no separate check). The temporary +/// name is removed in every case. Returns `Ok(false)` when the target +/// existed and was left alone. Never truncates, never leaves an empty file. +fn write_result_file_if_absent(dir: &Path, name: &str, contents: &[u8]) -> FileResult { + let (tmp, mut file) = create_temp(dir, name)?; + let written = file.write_all(contents); + drop(file); + let result = + written.and_then( + |()| match sys::hard_link(&tmp, &sys::c_path(dir, name.as_bytes())?) { + Ok(()) => Ok(true), + Err(e) if e.is_already_exists() => Ok(false), + Err(e) => Err(e), + }, + ); + let _ = sys::remove_file(&tmp); + result +} + +/// A new temporary file next to `dir/name` (see [`write_result_file`]): +/// its NUL-terminated path and the open file. +fn create_temp(dir: &Path, name: &str) -> FileResult<(Vec, sys::File)> { use std::sync::atomic::{AtomicU64, Ordering}; static COUNTER: AtomicU64 = AtomicU64::new(0); - - let pid = std::process::id(); + let pid = sys::process_id(); let mut attempts = 0u32; - let (tmp, mut file) = loop { + loop { let n = COUNTER.fetch_add(1, Ordering::Relaxed); - let tmp = dir.join(format!(".{name}.{pid}.{n}.tmp")); - match std::fs::OpenOptions::new() - .write(true) - .create_new(true) - .open(&tmp) - { - Ok(file) => break (tmp, file), - Err(e) if e.kind() == io::ErrorKind::AlreadyExists && attempts < 10_000 => { + let mut tmp_name = Vec::with_capacity(name.len() + 32); + tmp_name.push(b'.'); + tmp_name.extend_from_slice(name.as_bytes()); + tmp_name.push(b'.'); + cfmt::put_u64(&mut tmp_name, u64::from(pid)); + tmp_name.push(b'.'); + cfmt::put_u64(&mut tmp_name, n); + tmp_name.extend_from_slice(b".tmp"); + let tmp = sys::c_path(dir, &tmp_name)?; + match sys::File::create_new(&tmp) { + Ok(file) => return Ok((tmp, file)), + Err(e) if e.is_already_exists() && attempts < 10_000 => { attempts += 1; } Err(e) => return Err(e), } - }; - let result = file.write_all(contents.as_bytes()).and_then(|()| { + } +} + +/// Whether result files are written atomically (see [`set_atomic_writes`]). +pub fn atomic_writes() -> bool { + ATOMIC_WRITES.load(std::sync::atomic::Ordering::Relaxed) +} + +/// Writes `contents` to `dir/name`, atomically or not as +/// [`atomic_writes`] says (SD-20). +/// +/// Atomic: the full text goes to a temporary file of the same directory, +/// which is then renamed over the target. A truncating write truncates the +/// target and writes in separate calls; under KLEE (`--external-calls=all`) +/// the forked states share the real file system and one state can truncate +/// the file after another wrote its violation, leaving it empty when KLEE +/// halts on the abort. A reader of the target sees the old file or the new +/// one, never an empty or partial one. +/// +/// The temporary name has the pid and a counter. KLEE states +/// share the pid and each has a copy of the counter, so the file is created +/// with `create_new` and a name that already exists is skipped. +/// +/// Plain: the target is truncated and written in place, as develop's +/// `fopen(name, "w")` + `fprintf` + `fclose`. +pub fn write_atomic(dir: &Path, name: &str, contents: &str) -> FileResult<()> { + write_atomic_bytes(dir, name, contents.as_bytes()) +} + +/// [`write_atomic`] for contents that need not be UTF-8 (a function name is +/// a C byte string). +pub fn write_atomic_bytes(dir: &Path, name: &str, contents: &[u8]) -> FileResult<()> { + write_result_file(dir, name, contents, atomic_writes()) +} + +/// [`write_atomic_bytes`] with the mode given: `atomic` false truncates and +/// writes in place (develop's `fopen("w")`), true goes through a temporary +/// file and a rename. +fn write_result_file(dir: &Path, name: &str, contents: &[u8], atomic: bool) -> FileResult<()> { + let target = sys::c_path(dir, name.as_bytes())?; + if !atomic { + return sys::File::create(&target).and_then(|mut f| f.write_all(contents)); + } + let (tmp, mut file) = create_temp(dir, name)?; + let result = file.write_all(contents).and_then(|()| { drop(file); - std::fs::rename(&tmp, dir.join(name)) + sys::rename_file(&tmp, &target) }); if result.is_err() { - let _ = std::fs::remove_file(&tmp); + let _ = sys::remove_file(&tmp); } result } -/// Writes the property files of `result` into `dir`, as `write_property` -/// and the `write_property_*` helpers of `PropertyGenerator.c` do. -pub fn write_property_files(dir: &Path, result: &AnalysisResult) -> io::Result<()> { - let (text, extra) = property_text(result); - match text { - Some(text) => { - write_atomic(dir, PROPERTY_FILE, &text)?; - if let Some(extra) = extra { - write_atomic(dir, extra, &text)?; - } +/// `"\nLine: %d\nFunction: %s\n"`, the three `fprintf` calls of the +/// located properties of `PropertyGenerator.c`. +fn located(tag: &str, line: i32, function_name: &str) -> Vec { + let mut out = Vec::with_capacity(tag.len() + function_name.len() + 32); + cfmt::put(&mut out, tag.as_bytes()); + cfmt::put(&mut out, b"\nLine: "); + cfmt::put_i64(&mut out, i64::from(line)); + cfmt::put(&mut out, b"\nFunction: "); + cfmt::put(&mut out, function_name.as_bytes()); + cfmt::put_byte(&mut out, b'\n'); + out +} + +/// `write_property_unknown` (`PropertyGenerator.c:24-28`). The double +/// underscore of the file name is kept on purpose (SD-16). Like the other +/// `write_property_` helpers it is private to the Rust core (SD-22). +pub(crate) fn write_property_unknown(dir: &Path) -> FileResult<()> { + write_atomic(dir, PROPERTY_FILE_UNKNOWN, "UNKNOWN\n") +} + +// The helpers below write their file as `write_property` writes it (the +// same text as `map2check_property`); since Task 3.3 `write_property` +// builds that text once and writes both files itself, and the helpers stay +// for the tests of the C formats. + +/// `write_property_memtrack` (`PropertyGenerator.c:30-34`). +#[cfg(test)] +pub(crate) fn write_property_memtrack(dir: &Path) -> FileResult<()> { + write_atomic(dir, "map2check_property_memtrack", "FALSE-MEMTRACK\n") +} + +/// `write_property_memcleanup` (`PropertyGenerator.c:36-40`). +#[cfg(test)] +pub(crate) fn write_property_memcleanup(dir: &Path) -> FileResult<()> { + write_atomic(dir, "map2check_property_memcleanup", "FALSE-MEMCLEANUP\n") +} + +/// `write_property_deref` (`PropertyGenerator.c:42-48`). +#[cfg(test)] +pub(crate) fn write_property_deref(dir: &Path, line: i32, function_name: &str) -> FileResult<()> { + write_atomic_bytes( + dir, + "map2check_property_deref", + &located("FALSE-DEREF", line, function_name), + ) +} + +/// `write_property_free` (`PropertyGenerator.c:108-114`). +#[cfg(test)] +pub(crate) fn write_property_free(dir: &Path, line: i32, function_name: &str) -> FileResult<()> { + write_atomic_bytes( + dir, + "map2check_property_free", + &located("FALSE-FREE", line, function_name), + ) +} + +/// `write_property_overflow` (`PropertyGenerator.c:116-122`). +#[cfg(test)] +pub(crate) fn write_property_overflow( + dir: &Path, + line: i32, + function_name: &str, +) -> FileResult<()> { + write_atomic_bytes( + dir, + "map2check_property_overflow", + &located("OVERFLOW", line, function_name), + ) +} + +/// `write_property_assert` (`PropertyGenerator.c:124-130`). +#[cfg(test)] +pub(crate) fn write_property_assert(dir: &Path, line: i32, function_name: &str) -> FileResult<()> { + write_atomic_bytes( + dir, + "map2check_property_assert", + &located("ASSERT", line, function_name), + ) +} + +/// `write_property` (`PropertyGenerator.c:50-106`): `map2check_property` +/// with the text of `violated` and, for the properties that have one, the +/// file of its `write_property_` helper, written before +/// `map2check_property` is complete, as the C function calls the helper +/// before its own `fclose`. `Concurrency`, which has no case in the C +/// switch, leaves `map2check_property` empty. Each file is replaced +/// atomically (SD-20). +pub fn write_property( + dir: &Path, + violated: ViolatedProperty, + line: i32, + function_name: &str, +) -> FileResult<()> { + // The text of `map2check_property`; the file of the `write_property_` + // helper, when the property has one, gets the same text, and first. + let (tag, own_file, is_located) = match violated { + ViolatedProperty::None => ("NONE", None, false), + ViolatedProperty::Unknown => ("UNKNOWN", None, false), + ViolatedProperty::MemsafetyFree => ("FALSE-FREE", Some("map2check_property_free"), true), + ViolatedProperty::MemsafetyDeref => ("FALSE-DEREF", Some("map2check_property_deref"), true), + ViolatedProperty::MemsafetyMemtrack => { + ("FALSE-MEMTRACK", Some("map2check_property_memtrack"), false) } - None => write_atomic(dir, PROPERTY_FILE_UNKNOWN, "UNKNOWN\n")?, + ViolatedProperty::MemsafetyMemcleanup => ( + "FALSE-MEMCLEANUP", + Some("map2check_property_memcleanup"), + false, + ), + ViolatedProperty::Reachability => ("TARGET-REACHED", None, true), + ViolatedProperty::Overflow => ("OVERFLOW", Some("map2check_property_overflow"), true), + ViolatedProperty::Assert => ("ASSERT", Some("map2check_property_assert"), true), + // `:98-102`: no per-property file. + ViolatedProperty::DivByZero => ("FALSE-DIVBYZERO", None, true), + ViolatedProperty::Concurrency => ("", None, false), + }; + let text = if is_located { + located(tag, line, function_name) + } else if tag.is_empty() { + Vec::new() + } else { + let mut text = tag.as_bytes().to_vec(); + text.push(b'\n'); + text + }; + if let Some(name) = own_file { + write_atomic_bytes(dir, name, &text)?; + } + // Never downgrade (SD-20, KLEE only): a non-violation (NONE, UNKNOWN, + // the empty Concurrency text) never replaces an existing + // map2check_property; a violation replaces it, as fopen("w") does. + if !is_violation(violated) && never_downgrade() && atomic_writes() { + return write_result_file_if_absent(dir, PROPERTY_FILE, &text).map(|_| ()); + } + write_atomic_bytes(dir, PROPERTY_FILE, &text) +} + +/// Whether `map2check_property` with this property holds a violation (a +/// FALSE verdict the frontend reports): every property but `None`, +/// `Unknown` and `Concurrency` (written empty). +fn is_violation(violated: ViolatedProperty) -> bool { + !matches!( + violated, + ViolatedProperty::None | ViolatedProperty::Unknown | ViolatedProperty::Concurrency + ) +} + +/// Writes the property files of `result` into `dir`, as the C runtime does +/// on the path that records it: `write_property(NONE, 0, "")` for a correct +/// run (`map2check_success`, `Map2CheckFunctions.c:67-70`), and +/// `write_property` of the property otherwise. `line` is printed with `%d`, +/// so it is shown as a C `int`. The only UNKNOWN the C runtime reports is +/// `write_property_unknown` (NULL from malloc, `AnalysisModeMemtrack.c:305-311`), +/// which writes no `map2check_property`; `Concurrency`, which has no develop +/// counterpart, is reported the same way. +pub fn write_property_files(dir: &Path, result: &AnalysisResult) -> FileResult<()> { + if result.ok { + return write_property(dir, ViolatedProperty::None, 0, ""); + } + match result.property { + ViolatedProperty::Unknown | ViolatedProperty::Concurrency => write_property_unknown(dir), + property => write_property(dir, property, result.line as i32, &result.function_name), } - Ok(()) } /// Removes every property file left in `dir` by an earlier run. A missing /// file is not an error; other errors are reported on stderr. pub fn remove_stale_property_files(dir: &Path) { for name in PROPERTY_FILES { - match std::fs::remove_file(dir.join(name)) { + match sys::c_path(dir, name.as_bytes()).and_then(|p| sys::remove_file(&p)) { Ok(()) => {} - Err(e) if e.kind() == io::ErrorKind::NotFound => {} - Err(e) => eprintln!("[map2check] cannot remove stale {name}: {e}"), + Err(e) if e.is_not_found() => {} + Err(e) => { + let mut text = b"cannot remove stale ".to_vec(); + text.extend_from_slice(name.as_bytes()); + text.extend_from_slice(b": "); + e.describe(&mut text); + sys::diagnostic(&text); + } } } } /// `generate_confirmation_file` (`WitnessGeneration.c:28-32`). -pub fn write_checked_error(dir: &Path) -> io::Result<()> { +pub fn write_checked_error(dir: &Path) -> FileResult<()> { write_atomic(dir, CHECKED_ERROR_FILE, "END OF TIME\n") } -/// `trackbb_log_to_file` (`TrackBBLog.c:35-44`): `%u;%s\n` per row. -/// `map2check_track_bb` keeps only the first block of each line -/// (`Map2CheckFunctions.c:49-55`), so later rows of a line are skipped. -pub fn write_trackbb_log(dir: &Path, rows: &[BasicBlockEntry]) -> io::Result<()> { - let mut seen = crate::memtrack::FixedHashSet::default(); - let mut out = String::new(); - for row in rows.iter().filter(|r| seen.insert(r.line)) { - let _ = writeln!(out, "{};{}", row.line, row.function_name); +/// `trackbb_log_to_file` (`TrackBBLog.c:35-44`), private to the Rust core +/// (SD-22): `"%u;"` then `"%s\n"` per row (`:40-41`), in order. +pub(crate) fn trackbb_log_to_file(dir: &Path, rows: &[BasicBlockEntry]) -> FileResult<()> { + let mut out = Vec::new(); + for row in rows { + cfmt::put_u64(&mut out, u64::from(row.line)); + cfmt::put_byte(&mut out, b';'); + cfmt::put(&mut out, row.function_name.as_bytes()); + cfmt::put_byte(&mut out, b'\n'); + } + write_atomic_bytes(dir, TRACKBB_LOG_FILE, &out) +} + +/// The basic-block log of the run: `map2check_track_bb` keeps only the +/// first block of each line (`Map2CheckFunctions.c:49-55`), and the rows of +/// `map2check_save_basic_block_log`, which records every block, are reduced +/// the same way before [`trackbb_log_to_file`]. +pub fn write_trackbb_log(dir: &Path, rows: &[BasicBlockEntry]) -> FileResult<()> { + // The drop-in core records blocks only through `map2check_track_bb`, + // which already keeps one row per line; the reduction is for the rows of + // the old API (`map2check_save_basic_block_log`), which it does not have. + #[cfg(feature = "dropin")] + { + trackbb_log_to_file(dir, rows) + } + #[cfg(not(feature = "dropin"))] + { + let mut seen = crate::memtrack::FixedHashSet::default(); + let first: Vec = rows + .iter() + .filter(|r| seen.insert(r.line)) + .cloned() + .collect(); + trackbb_log_to_file(dir, &first) } - write_atomic(dir, TRACKBB_LOG_FILE, &out) } -/// The value column and the `enum NONDET_TYPE` number -/// (`Map2CheckTypes.h:41-58`) of a nondet row, as develop writes them. +/// The `enum NONDET_TYPE` number and the union bits of a nondet row, as +/// develop stores it, so that [`crate::nondet::c_log`] is the only code that +/// formats `klee_log.csv`. /// /// `NonDetPass.cpp` hands each value to its `map2check_nondet_` wrapper as /// a C `int` (`NonDetLog.c:105-150`): `int`, `unsigned`, `uint` as they are @@ -223,33 +460,32 @@ pub fn write_trackbb_log(dir: &Path, rows: &[BasicBlockEntry]) -> io::Result<()> /// which zero-extends the narrow ones (`char` -3 arrives as 253) and /// truncates the wide ones to their low 32 bits; `pointer` and `pchar` /// through `CreatePtrToInt(v, i32)` (`:224-238`, `:291-292`), the low 32 -/// bits of the address. `nondet_log_to_file` then prints `UNSIGNED` with -/// `%u`, `DOUBLE` with `%lf` and every other type with `%d` -/// (`NonDetLog.c:45-54`). `float` has no develop counterpart and is logged as +/// bits of the address. `float` has no develop counterpart and is logged as /// a double. -pub fn nondet_value_and_type(value: &NonDetValue) -> (String, i32) { +pub fn nondet_kind_and_bits(value: &NonDetValue) -> (i32, u64) { + use crate::nondet::c_log::kind as k; /// `trunc` to `i32`, as `CreateIntegerCast` narrows a wide integer. - fn low32(v: u64) -> String { - (v as u32 as i32).to_string() + fn low32(v: u64) -> u64 { + u64::from(v as u32) } match *value { - NonDetValue::Int(v) => (v.to_string(), 0), - NonDetValue::Char(v) => ((v as u8).to_string(), 1), - NonDetValue::Pointer(v) => (low32(v as u64), 2), - NonDetValue::UShort(v) => (v.to_string(), 3), - NonDetValue::Long(v) => (low32(v as u64), 4), - NonDetValue::Unsigned(v) => (v.to_string(), 5), - NonDetValue::ULong(v) => (low32(v), 6), - NonDetValue::Bool(v) => (v.to_string(), 7), - NonDetValue::UChar(v) => (v.to_string(), 8), - NonDetValue::PChar(v) => (low32(v as u64), 9), - NonDetValue::SizeT(v) => (low32(v as u64), 10), - NonDetValue::LoffT(v) => (low32(v as u64), 11), - NonDetValue::SectorT(v) => (low32(v), 12), - NonDetValue::UInt(v) => ((v as i32).to_string(), 13), - NonDetValue::Short(v) => ((v as u16).to_string(), 14), - NonDetValue::Float(v) => (c_double(f64::from(v)), 15), - NonDetValue::Double(v) => (c_double(v), 15), + NonDetValue::Int(v) => (k::INTEGER, low32(v as u32 as u64)), + NonDetValue::Char(v) => (k::CHAR, u64::from(v as u8)), + NonDetValue::Pointer(v) => (k::POINTER, low32(v as u64)), + NonDetValue::UShort(v) => (k::USHORT, u64::from(v)), + NonDetValue::Long(v) => (k::LONG, low32(v as u64)), + NonDetValue::Unsigned(v) => (k::UNSIGNED, u64::from(v)), + NonDetValue::ULong(v) => (k::ULONG, low32(v)), + NonDetValue::Bool(v) => (k::BOOL, low32(v as u32 as u64)), + NonDetValue::UChar(v) => (k::UCHAR, u64::from(v)), + NonDetValue::PChar(v) => (k::PCHAR, low32(v as u64)), + NonDetValue::SizeT(v) => (k::SIZE_T, low32(v as u64)), + NonDetValue::LoffT(v) => (k::LOFF_T, low32(v as u64)), + NonDetValue::SectorT(v) => (k::SECTOR_T, low32(v)), + NonDetValue::UInt(v) => (k::UINT, u64::from(v)), + NonDetValue::Short(v) => (k::SHORT, u64::from(v as u16)), + NonDetValue::Float(v) => (k::DOUBLE, f64::from(v).to_bits()), + NonDetValue::Double(v) => (k::DOUBLE, v.to_bits()), } } @@ -594,63 +830,69 @@ impl LegacyLogs { /// `list_log_to_file` (`ListLog.c:138-157`). The id column is always 0 /// (`new_list_row`, `ListLog.c:125`). - pub fn write_list_log(&self, dir: &Path) -> io::Result<()> { - let mut out = String::new(); + pub fn write_list_log(&self, dir: &Path) -> FileResult<()> { + let mut out = Vec::new(); for r in &self.list { - let _ = writeln!( - out, - "0;{};{};{};{};{};{};{};{};{}", - c_pointer(r.var), - c_pointer(r.points_to), + c_mode::list_log_row( + &mut out, + r.var as u64, + r.points_to as u64, r.scope, - i32::from(r.is_free), - i32::from(r.is_dynamic), - r.name, + r.is_free, + r.is_dynamic, + r.name.as_bytes(), r.line, - r.function, - r.step + r.function.as_bytes(), + r.step, ); } - write_atomic(dir, LIST_LOG_FILE, &out) + write_atomic_bytes(dir, LIST_LOG_FILE, &out) } /// `allocation_log_to_file` (`AllocationLog.c:161-174`). - pub fn write_allocation_log(&self, dir: &Path) -> io::Result<()> { - let mut out = String::new(); + pub fn write_allocation_log(&self, dir: &Path) -> FileResult<()> { + let mut out = Vec::new(); for r in &self.allocation { - let _ = writeln!( - out, - "{};{};{}", - c_pointer(r.addr), - r.size, - i32::from(r.is_free) - ); + c_mode::allocation_log_row(&mut out, r.addr as u64, r.size, r.is_free); } - write_atomic(dir, ALLOCATION_LOG_FILE, &out) + write_atomic_bytes(dir, ALLOCATION_LOG_FILE, &out) } /// `heap_log_to_file` (`HeapLog.c:54-66`). - pub fn write_heap_log(&self, dir: &Path) -> io::Result<()> { - let mut out = String::new(); + pub fn write_heap_log(&self, dir: &Path) -> FileResult<()> { + let mut out = Vec::new(); for r in &self.heap { - let _ = writeln!(out, "{};{};{}", c_pointer(r.addr), r.name, r.size); + c_mode::heap_log_row(&mut out, r.addr as u64, r.name.as_bytes(), r.size); } - write_atomic(dir, HEAP_LOG_FILE, &out) + write_atomic_bytes(dir, HEAP_LOG_FILE, &out) } - /// `nondet_log_to_file` (`NonDetLog.c:17-60`): - /// `index;line;scope;function;step;value;type`. - pub fn write_nondet_log(&self, dir: &Path) -> io::Result<()> { - let mut out = String::new(); - for (i, r) in self.nondet.iter().enumerate() { - let (value, kind) = nondet_value_and_type(&r.value); - let _ = writeln!( - out, - "{i};{};{};{};{};{value};{kind}", - r.line, r.scope, r.function, r.step - ); + /// The rows of the nondet log in the shape of `NONDET_CALL`. + pub fn nondet_calls(&self) -> Vec { + self.nondet + .iter() + .map(|r| { + let (kind, bits) = nondet_kind_and_bits(&r.value); + crate::nondet::c_log::CNondetCall::from_parts( + kind, + r.line, + r.scope, + r.step, + r.function.as_bytes(), + bits, + ) + }) + .collect() + } + + /// `nondet_log_to_file` (`NonDetLog.c:50-125`) through the one writer, + /// [`crate::nondet::c_log::nondet_log_to_file`]. + pub fn write_nondet_log(&self, dir: &Path) -> FileResult<()> { + if crate::nondet::c_log::nondet_log_to_file(&self.nondet_calls(), dir) { + Ok(()) + } else { + Err(FileError::Other("cannot write klee_log.csv")) } - write_atomic(dir, NONDET_LOG_FILE, &out) } } @@ -758,6 +1000,7 @@ mod tests { /// wide; with the old code this fails on every run seen. #[test] fn concurrent_reader_never_sees_empty_or_partial_file() { + let _mode = write_mode_test_lock(); use std::sync::atomic::{AtomicBool, Ordering}; let dir = tempfile::tempdir().unwrap(); let old = bb_rows(400_000, "old"); @@ -798,6 +1041,7 @@ mod tests { /// (its inode changes), and no temporary file is left behind. #[test] fn write_goes_through_a_temp_file_and_rename() { + let _mode = write_mode_test_lock(); use std::os::unix::fs::MetadataExt as _; let dir = tempfile::tempdir().unwrap(); let target = dir.path().join(PROPERTY_FILE); @@ -818,6 +1062,7 @@ mod tests { #[test] fn temp_name_collisions_are_skipped() { + let _mode = write_mode_test_lock(); let dir = tempfile::tempdir().unwrap(); for _ in 0..50 { write_atomic(dir.path(), "f", "x\n").unwrap(); @@ -826,6 +1071,175 @@ mod tests { assert_eq!(std::fs::read_dir(dir.path()).unwrap().count(), 1); } + /// The default outside the drop-in core: atomic, as SD-20 had it before + /// Task 2.8 (the cargo tests never build with `dropin`). + #[test] + fn result_files_are_atomic_by_default_outside_dropin() { + let _mode = write_mode_test_lock(); + assert!(atomic_writes()); + } + + /// Plain mode (the drop-in core under AFL++ and `none`): develop's + /// `fopen("w")`, the target truncated and rewritten in place (same + /// inode), no temporary file. + #[test] + fn plain_mode_truncates_the_target_in_place_like_fopen_w() { + use std::os::unix::fs::MetadataExt as _; + let dir = tempfile::tempdir().unwrap(); + write_result_file(dir.path(), "f", b"a longer first text\n", false).unwrap(); + let target = dir.path().join("f"); + let first = std::fs::metadata(&target).unwrap().ino(); + let _held = std::fs::File::open(&target).unwrap(); + write_result_file(dir.path(), "f", b"x\n", false).unwrap(); + assert_eq!(std::fs::read(&target).unwrap(), b"x\n"); + assert_eq!(first, std::fs::metadata(&target).unwrap().ino()); + assert_eq!(std::fs::read_dir(dir.path()).unwrap().count(), 1); + } + + /// Atomic mode (the KLEE generator, and every build outside `dropin`): + /// a new file renamed over the target (new inode), no temporary left. + #[test] + fn atomic_mode_replaces_the_target_by_a_rename() { + use std::os::unix::fs::MetadataExt as _; + let dir = tempfile::tempdir().unwrap(); + write_result_file(dir.path(), "f", b"first\n", true).unwrap(); + let target = dir.path().join("f"); + let first = std::fs::metadata(&target).unwrap().ino(); + let _held = std::fs::File::open(&target).unwrap(); + write_result_file(dir.path(), "f", b"x\n", true).unwrap(); + assert_eq!(std::fs::read(&target).unwrap(), b"x\n"); + assert_ne!(first, std::fs::metadata(&target).unwrap().ino()); + assert_eq!(std::fs::read_dir(dir.path()).unwrap().count(), 1); + } + + /// Runs `f` with the KLEE write mode (atomic + never downgrade) or the + /// given one, and restores the defaults of a non-dropin build after. + fn with_modes(atomic: bool, never: bool, f: impl FnOnce()) { + let _mode = write_mode_test_lock(); + set_atomic_writes(atomic); + set_never_downgrade(never); + f(); + set_atomic_writes(true); + set_never_downgrade(false); + } + + fn property(dir: &Path) -> String { + std::fs::read_to_string(dir.join(PROPERTY_FILE)).unwrap() + } + + fn only_property_files(dir: &Path) { + for e in std::fs::read_dir(dir).unwrap() { + let n = e.unwrap().file_name().into_string().unwrap(); + assert!(n.starts_with("map2check_property"), "left behind: {n}"); + } + } + + /// SD-20, never downgrade (user decision of 2026-10-27): under KLEE a + /// NONE written after a violation leaves the violation in place. + #[test] + fn never_downgrade_keeps_a_violation_against_none() { + with_modes(true, true, || { + let dir = tempfile::tempdir().unwrap(); + write_property_files( + dir.path(), + &violated(ViolatedProperty::Reachability, 19, "main"), + ) + .unwrap(); + write_property_files(dir.path(), &AnalysisResult::default()).unwrap(); + assert_eq!( + property(dir.path()), + "TARGET-REACHED\nLine: 19\nFunction: main\n" + ); + write_property(dir.path(), ViolatedProperty::Unknown, 0, "").unwrap(); + write_property(dir.path(), ViolatedProperty::Concurrency, 0, "").unwrap(); + assert_eq!( + property(dir.path()), + "TARGET-REACHED\nLine: 19\nFunction: main\n" + ); + only_property_files(dir.path()); + }); + } + + /// A NONE with no earlier file is written (atomically: link of a full + /// temporary file), and a second NONE leaves it as it is. + #[test] + fn never_downgrade_writes_none_when_there_is_no_property_file() { + with_modes(true, true, || { + let dir = tempfile::tempdir().unwrap(); + write_property_files(dir.path(), &AnalysisResult::default()).unwrap(); + assert_eq!(property(dir.path()), "NONE\n"); + write_property_files(dir.path(), &AnalysisResult::default()).unwrap(); + assert_eq!(property(dir.path()), "NONE\n"); + only_property_files(dir.path()); + }); + } + + /// A violation still replaces a NONE and an earlier violation (the last + /// writer wins among violations, as develop's fopen("w")). + #[test] + fn never_downgrade_lets_a_violation_replace_none_and_violations() { + with_modes(true, true, || { + let dir = tempfile::tempdir().unwrap(); + write_property_files(dir.path(), &AnalysisResult::default()).unwrap(); + write_property_files(dir.path(), &violated(ViolatedProperty::Assert, 5, "f")).unwrap(); + assert_eq!(property(dir.path()), "ASSERT\nLine: 5\nFunction: f\n"); + write_property_files(dir.path(), &violated(ViolatedProperty::Overflow, 7, "g")) + .unwrap(); + assert_eq!(property(dir.path()), "OVERFLOW\nLine: 7\nFunction: g\n"); + only_property_files(dir.path()); + }); + } + + /// Plain mode (AFL++, none) and the staticlib default are unchanged: a + /// NONE replaces whatever was there, as develop's fopen("w"). + #[test] + fn without_never_downgrade_none_replaces_a_violation() { + for (atomic, never) in [(false, false), (true, false), (false, true)] { + with_modes(atomic, never, || { + let dir = tempfile::tempdir().unwrap(); + write_property_files(dir.path(), &violated(ViolatedProperty::Assert, 5, "f")) + .unwrap(); + write_property_files(dir.path(), &AnalysisResult::default()).unwrap(); + assert_eq!( + property(dir.path()), + "NONE\n", + "atomic {atomic} never {never}" + ); + }); + } + } + + /// The link-or-fail primitive: an existing target is never touched, + /// the temporary file is always removed. + #[test] + fn write_if_absent_never_replaces_and_cleans_up() { + let dir = tempfile::tempdir().unwrap(); + assert!(write_result_file_if_absent(dir.path(), "f", b"one\n").unwrap()); + assert!(!write_result_file_if_absent(dir.path(), "f", b"two\n").unwrap()); + assert_eq!(std::fs::read(dir.path().join("f")).unwrap(), b"one\n"); + assert_eq!(std::fs::read_dir(dir.path()).unwrap().count(), 1); + } + + /// The mode switch reaches every writer: in plain mode the property file + /// keeps its inode. + #[test] + fn set_atomic_writes_switches_the_property_writer() { + use std::os::unix::fs::MetadataExt as _; + let _mode = write_mode_test_lock(); + let dir = tempfile::tempdir().unwrap(); + let target = dir.path().join(PROPERTY_FILE); + set_atomic_writes(false); + write_property_files(dir.path(), &violated(ViolatedProperty::Assert, 5, "main")).unwrap(); + let first = std::fs::metadata(&target).unwrap().ino(); + let _held = std::fs::File::open(&target).unwrap(); + write_property_files(dir.path(), &violated(ViolatedProperty::Assert, 6, "main")).unwrap(); + let plain_ino = std::fs::metadata(&target).unwrap().ino(); + set_atomic_writes(true); + write_property_files(dir.path(), &violated(ViolatedProperty::Assert, 7, "main")).unwrap(); + assert_eq!(first, plain_ino); + assert_ne!(first, std::fs::metadata(&target).unwrap().ino()); + } + #[test] fn div_by_zero_writes_only_map2check_property() { // PropertyGenerator.c:98-102: no per-property file for FALSE_DIVBYZERO. @@ -912,27 +1326,97 @@ mod tests { #[test] fn nondet_rows_use_develop_types_and_value_formats() { + use crate::nondet::c_log::CNondetCall as C; use crate::nondet::NonDetValue as V; - assert_eq!(nondet_value_and_type(&V::Int(-1)), ("-1".into(), 0)); - assert_eq!(nondet_value_and_type(&V::UInt(u32::MAX)), ("-1".into(), 13)); - assert_eq!(nondet_value_and_type(&V::Long(1 << 32)), ("0".into(), 4)); - assert_eq!(nondet_value_and_type(&V::Char(-3)), ("253".into(), 1)); - assert_eq!(nondet_value_and_type(&V::Short(-2)), ("65534".into(), 14)); + let row = |v: &V| { + let (kind, bits) = nondet_kind_and_bits(v); + String::from_utf8(C::from_parts(kind, 0, 0, 0, b"", bits).csv_row(0)).unwrap() + }; + assert_eq!(row(&V::Int(-1)), "0;0;0;;0;-1;0\n"); + assert_eq!(row(&V::UInt(u32::MAX)), "0;0;0;;0;-1;13\n"); + assert_eq!(row(&V::Long(1 << 32)), "0;0;0;;0;0;4\n"); + assert_eq!(row(&V::Char(-3)), "0;0;0;;0;253;1\n"); + assert_eq!(row(&V::Short(-2)), "0;0;0;;0;65534;14\n"); + assert_eq!(row(&V::Unsigned(u32::MAX)), "0;0;0;;0;4294967295;5\n"); + assert_eq!(row(&V::Pointer(0x1_2345_6789)), "0;0;0;;0;591751049;2\n"); + assert_eq!(row(&V::Float(0.5)), "0;0;0;;0;0.500000;15\n"); + assert_eq!(row(&V::Double(2.5)), "0;0;0;;0;2.500000;15\n"); + } + + /// The `write_property_` helpers of `PropertyGenerator.c`, which the + /// Rust core keeps private (SD-22): each writes its own file with the + /// format strings of the C function. + #[test] + fn write_property_helpers_match_c_format() { + let dir = tempfile::tempdir().unwrap(); + let d = dir.path(); + write_property_unknown(d).unwrap(); // :24-28 + write_property_memtrack(d).unwrap(); // :30-34 + write_property_memcleanup(d).unwrap(); // :36-40 + write_property_deref(d, 7, "f").unwrap(); // :42-48 + write_property_free(d, -2, "g").unwrap(); // :108-114 + write_property_overflow(d, 0, "").unwrap(); // :116-122 + write_property_assert(d, i32::MAX, "main").unwrap(); // :124-130 + let read = |name: &str| std::fs::read_to_string(d.join(name)).unwrap(); + assert_eq!(read("map2check_property__unknown"), "UNKNOWN\n"); + assert_eq!(read("map2check_property_memtrack"), "FALSE-MEMTRACK\n"); + assert_eq!(read("map2check_property_memcleanup"), "FALSE-MEMCLEANUP\n"); + assert_eq!( + read("map2check_property_deref"), + "FALSE-DEREF\nLine: 7\nFunction: f\n" + ); assert_eq!( - nondet_value_and_type(&V::Unsigned(u32::MAX)), - ("4294967295".into(), 5) + read("map2check_property_free"), + "FALSE-FREE\nLine: -2\nFunction: g\n" ); assert_eq!( - nondet_value_and_type(&V::Pointer(0x1_2345_6789)), - ("591751049".into(), 2) + read("map2check_property_overflow"), + "OVERFLOW\nLine: 0\nFunction: \n" ); assert_eq!( - nondet_value_and_type(&V::Float(0.5)), - ("0.500000".into(), 15) + read("map2check_property_assert"), + "ASSERT\nLine: 2147483647\nFunction: main\n" ); + assert!( + !d.join(PROPERTY_FILE).exists(), + "nenhum escreve map2check_property" + ); + } + + /// `write_property` replaces `map2check_property` and the file of its + /// own property and leaves every other file alone + /// (`PropertyGenerator.c:50-106`). + #[test] + fn write_property_truncates_only_its_own_files() { + let dir = tempfile::tempdir().unwrap(); + for name in PROPERTY_FILES { + std::fs::write(dir.path().join(name), "stale\n").unwrap(); + } + write_property(dir.path(), ViolatedProperty::Overflow, 3, "main").unwrap(); + for name in PROPERTY_FILES { + let text = std::fs::read_to_string(dir.path().join(name)).unwrap(); + if name == PROPERTY_FILE || name == "map2check_property_overflow" { + assert_eq!(text, "OVERFLOW\nLine: 3\nFunction: main\n", "{name}"); + } else { + assert_eq!(text, "stale\n", "{name}"); + } + } + } + + /// `trackbb_log_to_file` (`TrackBBLog.c:35-44`), private in the Rust + /// core (SD-22): `"%u;" "%s\n"` per row, in order. + #[test] + fn trackbb_log_to_file_matches_c_format() { + use crate::bbtrack::BasicBlockEntry; + let dir = tempfile::tempdir().unwrap(); + let rows = [ + BasicBlockEntry::new(1, 0, "main"), + BasicBlockEntry::new(2, u32::MAX, ""), + ]; + trackbb_log_to_file(dir.path(), &rows).unwrap(); assert_eq!( - nondet_value_and_type(&V::Double(2.5)), - ("2.500000".into(), 15) + std::fs::read(dir.path().join(TRACKBB_LOG_FILE)).unwrap(), + b"0;main\n4294967295;\n" ); } } diff --git a/rust/src/output/mod.rs b/rust/src/output/mod.rs index c42572c..0785b04 100644 --- a/rust/src/output/mod.rs +++ b/rust/src/output/mod.rs @@ -7,17 +7,23 @@ pub mod legacy_files; pub(crate) mod termination; +// The JSON channel is host-only: the drop-in core has none (see +// `print_report_unless_mode_set` below) and is built without serde. +#[cfg(not(feature = "dropin"))] use serde::Serialize; +#[cfg(not(feature = "dropin"))] use crate::{caller::ViolatedProperty, error::Map2CheckError, state::AnalysisState}; /// Top-level JSON output structure. /// Schema matches what Map2Check tool expects from the library. +#[cfg(not(feature = "dropin"))] #[derive(Serialize)] struct Map2CheckLog<'a> { map2check_log: LogBody<'a>, } +#[cfg(not(feature = "dropin"))] #[derive(Serialize)] struct LogBody<'a> { result: &'static str, @@ -41,6 +47,7 @@ struct LogBody<'a> { /// Serialize the complete analysis state to a JSON string. /// Replaces `print_all_containers_as_json()` in caller_lib_result.c. +#[cfg(not(feature = "dropin"))] pub fn to_json(state: &AnalysisState) -> Result { let result = &state.result; let is_mem_violation = matches!( @@ -79,25 +86,15 @@ pub fn to_json(state: &AnalysisState) -> Result { serde_json::to_string_pretty(&log).map_err(Map2CheckError::from) } -/// Makes the state safe to format, right before a report or a log file is -/// written from it. Under KLEE (feature `klee`) values the program made -/// symbolic are replaced by concrete values of the current path, so that -/// formatting them does not fork KLEE (D1 condition 7); elsewhere it does -/// nothing. -pub(crate) fn prepare_for_output(state: &mut AnalysisState) { - #[cfg(feature = "klee")] - crate::nondet::klee::concretize(state); - #[cfg(not(feature = "klee"))] - let _ = state; -} - /// Print the JSON report to stdout, as the C library did. A failed write /// (closed stdout, EPIPE) is reported on stderr instead of panicking. +#[cfg(not(feature = "dropin"))] pub fn print_json(state: &AnalysisState) -> Result<(), Map2CheckError> { write_report(&to_json(state)?); Ok(()) } +#[cfg(not(feature = "dropin"))] fn write_report(json: &str) { use std::io::Write as _; if let Err(e) = writeln!(std::io::stdout().lock(), "{json}") { @@ -110,19 +107,26 @@ fn write_report(json: &str) { /// and keeps the analysed program's stdout clean, as develop does. The /// report is serialised under the state lock and written after the lock is /// released, so stdout can never affect the state or the verdict. +#[cfg(not(feature = "dropin"))] pub fn print_report_unless_mode_set() { if crate::state::config().mode.is_some() { return; } - match crate::state::with_state(|s| { - prepare_for_output(s); - to_json(s) - }) { + match crate::state::with_state(|s| to_json(s)) { Ok(json) => write_report(&json), Err(e) => eprintln!("[map2check] error: {e}"), } } +/// The drop-in core (feature `dropin`) has no JSON channel, as the C runtime +/// has none: there `map2check_init` runs the `analysis_init` of the linked +/// mode module, which always selects a mode, so the channel above could +/// never print. Compiled out, the JSON serialiser is not in +/// `Map2CheckFunctions.bc`; its float formatting carried a `freeze`, which +/// KLEE 3.1 cannot execute (scripts/runtime-ir/check_no_freeze.py). +#[cfg(feature = "dropin")] +pub fn print_report_unless_mode_set() {} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/src/output/termination.rs b/rust/src/output/termination.rs index 17830b9..03c9253 100644 --- a/rust/src/output/termination.rs +++ b/rust/src/output/termination.rs @@ -14,9 +14,13 @@ use std::path::{Path, PathBuf}; -use super::legacy_files::{self, LegacyLogs}; +use super::legacy_files; +#[cfg(not(feature = "dropin"))] +use super::legacy_files::LegacyLogs; +#[cfg(not(feature = "dropin"))] +use crate::analysismode::mode::AnalysisMode; use crate::{ - analysismode::mode::{self, AnalysisMode}, + analysismode::mode, caller::AnalysisResult, state::{self, AnalysisState, Config}, }; @@ -46,25 +50,40 @@ fn channel(config: Config) -> Option { config.mode.and(output_dir()) } -fn report(what: &str, result: std::io::Result<()>) { +fn report(what: &str, result: crate::sys::FileResult<()>) { if let Err(e) = result { - eprintln!("[map2check] cannot write {what}: {e}"); + let mut text = b"cannot write ".to_vec(); + text.extend_from_slice(what.as_bytes()); + text.extend_from_slice(b": "); + e.describe(&mut text); + crate::sys::diagnostic(&text); } } -/// `map2check_init()` with a mode set: removes the property files of an -/// earlier run before this one writes its own. -pub(crate) fn on_init() { - if let Some(dir) = channel(state::config()) { - legacy_files::remove_stale_property_files(&dir); - } +/// `[map2check] error: ` on stderr. +pub(crate) fn log_error(e: &crate::error::Map2CheckError) { + let mut text = b"error: ".to_vec(); + e.describe(&mut text); + crate::sys::diagnostic(&text); } -/// `generate_aux_files` (`WitnessGeneration.c:14-25`) when witness -/// generation is on: the basic-block log, the logs of the memory modes -/// (`analysis_generate_aux_witness_files`, `AnalysisModeMemtrack.c:148-152`) -/// and, on a violation, the nondet log. -fn write_aux_files(dir: &Path, config: Config, s: &AnalysisState, end_of_program: bool) { +/// Set by the seed exchange of the Map2Check frontend when it replays a +/// non-violating fuzzer input to learn its nondet values +/// (`WitnessGeneration.c:23-28`); NUL-terminated for `getenv`. +const SEED_REPLAY_ENV: &[u8] = b"MAP2CHECK_SEED_REPLAY\0"; + +/// `generate_aux_files` (`WitnessGeneration.c:15-29`): the basic-block log, +/// the logs of the memory modes (`analysis_generate_aux_witness_files`, +/// `AnalysisModeMemtrack.c:148-152`) and, on a violation or when +/// `MAP2CHECK_SEED_REPLAY` is set, the nondet log. The staticlib's body; the +/// drop-in core calls the linked modules instead ([`rt_generate_aux_files`]). +#[cfg(not(feature = "dropin"))] +pub(crate) fn generate_aux_files( + dir: &Path, + config: Config, + s: &mut AnalysisState, + violation: bool, +) { report( legacy_files::TRACKBB_LOG_FILE, legacy_files::write_trackbb_log(dir, &s.bbtrack), @@ -73,52 +92,253 @@ fn write_aux_files(dir: &Path, config: Config, s: &AnalysisState, end_of_program &s.memtrack, &s.nondets, s.failed_free.as_ref(), - end_of_program, + s.end_of_program, ); if matches!( config.mode, Some(AnalysisMode::Memtrack | AnalysisMode::Memcleanup) ) { - report(legacy_files::HEAP_LOG_FILE, logs.write_heap_log(dir)); - report( + // The drop-in entry points keep develop's logs themselves + // (`__m2c_rt_memtrack_*`, Task 2.5); the Rust-only 1.x API is replayed. + if s.c_memory.has_rows() { + write_c_memory_logs(dir, &s.c_memory); + } else { + report(legacy_files::HEAP_LOG_FILE, logs.write_heap_log(dir)); + report( + legacy_files::ALLOCATION_LOG_FILE, + logs.write_allocation_log(dir), + ); + report(legacy_files::LIST_LOG_FILE, logs.write_list_log(dir)); + } + } + if violation || crate::sys::env_is_set(SEED_REPLAY_ENV) { + report(legacy_files::NONDET_LOG_FILE, logs.write_nondet_log(dir)); + } +} + +/// `analysis_generate_aux_witness_files` of the memory modes +/// (`AnalysisModeMemtrack.c:148-152`): `heap_log.csv`, `allocation_log.csv` +/// and `list_log.csv`, in that order, from develop's logs. +pub(crate) fn write_c_memory_logs(dir: &Path, memory: &crate::memtrack::c_mode::CMemory) { + for (name, bytes) in [ + (legacy_files::HEAP_LOG_FILE, memory.heap_log_bytes()), + ( legacy_files::ALLOCATION_LOG_FILE, - logs.write_allocation_log(dir), + memory.allocation_log_bytes(), + ), + (legacy_files::LIST_LOG_FILE, memory.list_log_bytes()), + ] { + report(name, legacy_files::write_atomic_bytes(dir, name, &bytes)); + } +} + +/// `generate_confirmation_file` (`WitnessGeneration.c:32-36`). +pub(crate) fn generate_confirmation_file(dir: &Path) { + report( + legacy_files::CHECKED_ERROR_FILE, + legacy_files::write_checked_error(dir), + ); +} + +/// `__m2c_rt_witness_generate_aux_files`: [`generate_aux_files`] on the +/// state of the run, into the output directory, whatever the mode and the +/// witness option (the IR module `WitnessGeneration.bc` is linked only into +/// witness builds, D7). The staticlib (host programs, tests) has no mode or +/// generator module to call, so the core writes their files itself. +#[cfg(not(feature = "dropin"))] +pub(crate) fn rt_generate_aux_files(violation: bool) { + let Some(dir) = output_dir() else { + return; + }; + let config = state::config(); + if let Err(e) = state::with_state(|s| { + generate_aux_files(&dir, config, s, violation); + Ok(()) + }) { + log_error(&e); + } +} + +/// `__m2c_rt_witness_generate_aux_files` in the drop-in core: the body of +/// `generate_aux_files` (`WitnessGeneration.c:15-29`) with the modules it is +/// linked with: `track_bb_log.st`, then `analysis_generate_aux_witness_files` +/// of the mode module (the logs of the memory modes), then, on a violation or +/// when `MAP2CHECK_SEED_REPLAY` is set, `nondet_generate_aux_witness_files` +/// of the generator module, which alone writes `klee_log.csv` +/// (`NonDetGeneratorNone.c` writes nothing, `NonDetGeneratorKlee.c:55-61` +/// pins the values first). The calls run with the state lock released. +#[cfg(feature = "dropin")] +pub(crate) fn rt_generate_aux_files(violation: bool) { + let Some(dir) = output_dir() else { + return; + }; + if let Err(e) = state::with_state(|s| { + report( + legacy_files::TRACKBB_LOG_FILE, + legacy_files::write_trackbb_log(&dir, &s.bbtrack), ); - report(legacy_files::LIST_LOG_FILE, logs.write_list_log(dir)); + Ok(()) + }) { + log_error(&e); } - if !s.result.ok { - report(legacy_files::NONDET_LOG_FILE, logs.write_nondet_log(dir)); + crate::ffi::dropin::mode_generate_aux_witness_files(); + if violation || crate::sys::env_is_set(SEED_REPLAY_ENV) { + crate::ffi::dropin::generator_generate_aux_witness_files(); + } +} + +/// `__m2c_rt_witness_generate_confirmation_file`: +/// [`generate_confirmation_file`] into the output directory. +pub(crate) fn rt_generate_confirmation_file() { + if let Some(dir) = output_dir() { + generate_confirmation_file(&dir); } } -/// `map2check_exit` after the verdict is known: aux files, then -/// `analysis_destroy`, then, on a violation, the confirmation file and the -/// abort. Returns only when there is no violation. -fn exit(dir: &Path, config: Config, end_of_program: bool) { +/// `write_property` into the output directory (`PropertyGenerator.c:50-106`), +/// whatever the mode. +pub(crate) fn write_property( + violated: crate::caller::ViolatedProperty, + line: i32, + function_name: &str, +) { + if let Some(dir) = output_dir() { + report( + legacy_files::PROPERTY_FILE, + legacy_files::write_property(&dir, violated, line, function_name), + ); + } +} + +/// `map2check_destroy` (`Map2CheckFunctions.c:77-87`): releases the +/// basic-block log and runs `analysis_destroy` of the mode, once per run. +/// The nondet log belongs to the generator (`nondet_destroy`) and stays. +pub(crate) fn destroy(config: Config, s: &mut AnalysisState) { + if s.destroyed { + return; + } + s.destroyed = true; + s.bbtrack.clear(); + mode::analysis_destroy(config.mode, s); +} + +/// The end of `map2check_exit` (`Map2CheckFunctions.c:94-101`), once the +/// verdict is known: the aux files (witness on), `map2check_destroy`, and on +/// an error the confirmation file (witness on) and the abort. With no `dir` +/// (no mode) nothing is written and nothing aborts. Returns only without an +/// error, or without `dir`. The staticlib decides the witness by +/// `config.witness` (`__m2c_rt_witness_enable`); the drop-in core by the +/// witness module it is linked with (the other `finish`). +#[cfg(not(feature = "dropin"))] +fn finish(dir: Option<&Path>, config: Config) { let failed = match state::with_state(|s| { - if config.witness { - super::prepare_for_output(s); - write_aux_files(dir, config, s, end_of_program); + let failed = s.got_error || !s.result.ok; + if let (Some(dir), true) = (dir, config.witness) { + generate_aux_files(dir, config, s, failed); } - mode::analysis_destroy(config.mode, s); - Ok(!s.result.ok) + destroy(config, s); + Ok(failed) }) { + Ok(failed) => failed, + Err(e) => match dir { + Some(dir) => unknown_and_abort(dir, config, &e), + None => { + log_error(&e); + return; + } + }, + }; + if let (true, Some(dir)) = (failed, dir) { + if config.witness { + generate_confirmation_file(dir); + } + std::process::abort(); + } +} + +/// The end of `map2check_exit` in the drop-in core, as the C +/// (`Map2CheckFunctions.c:94-101`): `generate_aux_files(&trackbb_log, +/// gotError)` of the linked witness module (`WitnessGeneration.bc` writes the +/// aux files, `WitnessGenerationNone.bc` nothing), `map2check_destroy`, and +/// on an error `generate_confirmation_file` of the witness module and the +/// abort. `config.witness` plays no part. The module calls run with the +/// state lock released. With no `dir` (no mode) no module is called and +/// nothing aborts. Returns only without an error, or without `dir`. +#[cfg(feature = "dropin")] +fn finish(dir: Option<&Path>, config: Config) { + let Some(dir) = dir else { + if let Err(e) = state::with_state(|s| { + destroy(config, s); + Ok(()) + }) { + log_error(&e); + } + return; + }; + let failed = match state::with_state(|s| Ok(s.got_error || !s.result.ok)) { Ok(failed) => failed, Err(e) => unknown_and_abort(dir, config, &e), }; + crate::ffi::dropin::witness_generate_aux_files(failed); + if let Err(e) = state::with_state(|s| { + destroy(config, s); + Ok(()) + }) { + unknown_and_abort(dir, config, &e); + } if failed { - write_checked_error(dir, config); + crate::ffi::dropin::witness_generate_confirmation_file(); std::process::abort(); } } -fn write_checked_error(dir: &Path, config: Config) { - if config.witness { - report( - legacy_files::CHECKED_ERROR_FILE, - legacy_files::write_checked_error(dir), - ); +/// `map2check_exit` (`Map2CheckFunctions.c:89-102`): with no error +/// (`gotError` FALSE, no recorded violation) the end-of-program verdict of +/// the mode runs, and a violation it finds writes its property files, as +/// `analysis_is_program_correct` of the memory modes does +/// (`AnalysisModeMemtrack.c:138-146`); then [`finish`]. Without a mode the +/// verdict is only recorded (SD-03, SD-14). +pub(crate) fn map2check_exit() { + let config = state::config(); + let dir = channel(config); + let verdict = state::with_state(|s| { + if s.got_error || !s.result.ok { + return Ok(None); + } + s.end_of_program = true; + Ok( + mode::analysis_is_program_correct(config, s).map(|property| { + s.set_false(property, 0, ""); + s.result.clone() + }), + ) + }); + match verdict { + Ok(Some(result)) => { + if let Some(dir) = &dir { + write_properties(dir, &result); + } + } + Ok(None) => {} + Err(e) => match &dir { + Some(dir) => unknown_and_abort(dir, config, &e), + None => log_error(&e), + }, + } + finish(dir.as_deref(), config); +} + +/// `map2check_error` (`Map2CheckFunctions.c:72-75`): sets `gotError` and +/// runs [`map2check_exit`], which then skips the verdict and, with a mode +/// set, aborts. +pub(crate) fn map2check_error() { + if let Err(e) = state::with_state(|s| { + s.got_error = true; + Ok(()) + }) { + log_error(&e); } + map2check_exit(); } /// The analysis state cannot be read (its lock was poisoned by an earlier @@ -130,7 +350,10 @@ fn write_checked_error(dir: &Path, config: Config) { /// process aborts. The frontend reads UNKNOWN because `map2check_property` /// is missing (`tools.cpp:98-103`). No log files: their data is unreadable. fn unknown_and_abort(dir: &Path, config: Config, error: &crate::error::Map2CheckError) -> ! { - eprintln!("[map2check] error: {error}; the verdict is UNKNOWN"); + let mut text = b"error: ".to_vec(); + error.describe(&mut text); + text.extend_from_slice(b"; the verdict is UNKNOWN"); + crate::sys::diagnostic(&text); legacy_files::remove_stale_property_files(dir); let unknown = AnalysisResult { ok: false, @@ -138,7 +361,15 @@ fn unknown_and_abort(dir: &Path, config: Config, error: &crate::error::Map2Check ..AnalysisResult::default() }; write_properties(dir, &unknown); - write_checked_error(dir, config); + #[cfg(not(feature = "dropin"))] + if config.witness { + generate_confirmation_file(dir); + } + #[cfg(feature = "dropin")] + { + let _ = config; + crate::ffi::dropin::witness_generate_confirmation_file(); + } std::process::abort(); } @@ -151,10 +382,8 @@ fn write_properties(dir: &Path, result: &AnalysisResult) { /// The first violation was just recorded (`map2check_error`, /// `Map2CheckFunctions.c:72-75`): with a mode set, writes the property -/// files, ends the run and aborts. `end_of_program` is true when the -/// violation is the end-of-program verdict, whose check appends rows to -/// develop's allocation log. Without a mode, returns at once. -pub(crate) fn on_violation(end_of_program: bool) { +/// files, ends the run and aborts. Without a mode, returns at once. +pub(crate) fn on_violation() { let config = state::config(); let Some(dir) = channel(config) else { return; @@ -163,40 +392,21 @@ pub(crate) fn on_violation(end_of_program: bool) { Ok(result) => write_properties(&dir, &result), Err(e) => unknown_and_abort(&dir, config, &e), } - exit(&dir, config, end_of_program); - // `exit` returns only without a violation, which a recorded violation + finish(Some(&dir), config); + // `finish` returns only without a violation, which a recorded violation // rules out; abort anyway rather than let the program continue. std::process::abort(); } -/// `map2check_success` (`Map2CheckFunctions.c:67-70`, `:89-102`): with a -/// mode set, writes `NONE`, runs the end-of-program verdict, ends the run, -/// and returns when the program is correct. Returns whether the file -/// channel handled the call. +/// `map2check_success` (`Map2CheckFunctions.c:67-70`): with a mode set, +/// `write_property(NONE, 0, "")` and [`map2check_exit`], which returns when +/// the program is correct. Returns whether the file channel handled the call. pub(crate) fn on_success() -> bool { let config = state::config(); let Some(dir) = channel(config) else { return false; }; write_properties(&dir, &AnalysisResult::default()); - let verdict = state::with_state(|s| { - let end_of_program = s.result.ok; - if end_of_program { - if let Some(property) = mode::analysis_is_program_correct(config, s) { - s.set_false(property, 0, ""); - } - } - Ok((end_of_program, s.result.clone())) - }); - let end_of_program = match verdict { - Ok((end_of_program, result)) => { - if !result.ok { - write_properties(&dir, &result); - } - end_of_program - } - Err(e) => unknown_and_abort(&dir, config, &e), - }; - exit(&dir, config, end_of_program); + map2check_exit(); true } diff --git a/rust/src/state.rs b/rust/src/state.rs index 81420c8..0835331 100644 --- a/rust/src/state.rs +++ b/rust/src/state.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only #![deny(unsafe_code)] -use std::sync::{Mutex, OnceLock}; +use std::sync::Mutex; use crate::{ analysismode::mode::AnalysisMode, @@ -12,9 +12,10 @@ use crate::{ nondet::NonDetEntry, }; -// `OnceLock` and not `LazyLock`: the KLEE runtime compiles this file with -// rustc 1.72 (D1 condition 5, klee/Cargo.toml), where `LazyLock` is unstable. -static ANALYSIS_STATE: OnceLock> = OnceLock::new(); +// Built at compile time (`AnalysisState::new` is const): no `OnceLock` and +// no `LazyLock` (unstable in rustc 1.72, which builds the drop-in core, D1 +// condition 5), so the core carries no once-initialisation code (Task 3.3). +static ANALYSIS_STATE: Mutex = Mutex::new(AnalysisState::new()); static CONFIG: Mutex = Mutex::new(Config::new()); @@ -23,8 +24,10 @@ static CONFIG: Mutex = Mutex::new(Config::new()); /// clear it. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Config { - /// Set by `map2check_set_mode()`. `None` means unset: every check is - /// active and the leak tag follows `memcleanup`, as before Task 2.3. + /// Set by the `__m2c_rt__analysis_init` of the linked mode module + /// (and by the Rust-only `map2check_set_mode()` of the tests). `None` + /// means unset: every check is active and the leak tag follows + /// `memcleanup`, as before Task 2.3. pub mode: Option, /// Set by `map2check_set_memcleanup()`: a leak at exit is reported as /// `MemsafetyMemcleanup` instead of `MemsafetyMemtrack`. @@ -75,14 +78,22 @@ pub(crate) fn reset_config() { update_config(|c| *c = Config::new()); } -#[derive(Debug, Default)] +#[derive(Debug)] pub struct AnalysisState { pub result: AnalysisResult, pub nondets: Vec, + /// The rows of develop's `nondet_log` (`NonDetLog.c:144`), appended by + /// the `map2check_nondet_` exports and written to `klee_log.csv` by + /// `__m2c_rt_nondet_log_to_file`. + pub c_nondet: Vec, pub memtrack: Vec, /// The offset-keyed allocation table of the wasm2c runtime /// (`map2check_wasm_*`, Task 2.8). pub wasm_allocs: Vec, + /// develop's `heap_log`, `allocation_log`, `list_log` and `ERROR_DEREF` + /// (`AnalysisModeMemtrack.c:120-123`), kept by the `__m2c_rt_memtrack_*` + /// core entry points (Task 2.5). Its list rows take `c_step`. + pub c_memory: crate::memtrack::c_mode::CMemory, pub bbtrack: Vec, pub legacy_nondet_logs: Vec>, pub legacy_bbtrack_logs: Vec>, @@ -91,11 +102,55 @@ pub struct AnalysisState { /// the list log before it checks (`AnalysisModeMemtrack.c:346-358`), so /// the legacy logs keep it; it is not a release and no check reads it. pub failed_free: Option, + /// develop's `Map2CheckCurrentStep` (`Map2CheckFunctions.c:19`): 0 after + /// `map2check_init`, read by `map2check_get_current_step` and advanced + /// only by `map2check_next_current_step`, with C `unsigned` wrap-around. + /// It is not `result.step`, which every record of this library advances. + pub c_step: u32, + /// develop's `gotError` (`Map2CheckFunctions.c:27`): set by + /// `map2check_error`, so `map2check_exit` skips the end-of-program + /// verdict and ends the run as a violation even with no property recorded. + pub got_error: bool, + /// The end-of-program verdict (`analysis_is_program_correct`) ran in this + /// run; develop's allocation log then has the rows `valid_allocation_log` + /// appends (`AllocationLog.c:69-101`), which the witness logs replay. + pub end_of_program: bool, + /// `map2check_destroy` already ran (develop's function-static + /// `alreadyReleased`, `Map2CheckFunctions.c:80-83`). Here it belongs to + /// the run and `map2check_init` clears it; develop never clears it, which + /// only changes what a second run in the same process frees. + pub destroyed: bool, +} + +impl Default for AnalysisState { + fn default() -> Self { + Self::new() + } } impl AnalysisState { + /// The state before any record. + pub const fn new() -> Self { + Self { + result: AnalysisResult::new(), + nondets: Vec::new(), + c_nondet: Vec::new(), + memtrack: Vec::new(), + wasm_allocs: Vec::new(), + c_memory: crate::memtrack::c_mode::CMemory::new(), + bbtrack: Vec::new(), + legacy_nondet_logs: Vec::new(), + legacy_bbtrack_logs: Vec::new(), + failed_free: None, + c_step: 0, + got_error: false, + end_of_program: false, + destroyed: false, + } + } + pub fn reset(&mut self) { - *self = Self::default(); + *self = Self::new(); } /// Records a violation unless one is already recorded (#36, SD-03). @@ -125,7 +180,7 @@ impl AnalysisState { } fn ensure_state() -> &'static Mutex { - ANALYSIS_STATE.get_or_init(|| Mutex::new(AnalysisState::default())) + &ANALYSIS_STATE } /// Starts a fresh analysis: forces the singleton into existence and runs the @@ -152,9 +207,10 @@ fn with_state_recovering(f: impl FnOnce(&mut AnalysisState)) { let mut guard = poisoned.into_inner(); f(&mut guard); // `Mutex::clear_poison` is unstable in rustc 1.72, which builds - // the KLEE runtime. That build has `panic = "abort"`, so no - // caught panic can poison the lock there (D1, "Custo do pin"). - #[cfg(not(feature = "klee"))] + // the drop-in core (klee/Cargo.toml). That build has + // `panic = "abort"`, so no caught panic can poison the lock there + // (D1, "Custo do pin"). + #[cfg(not(feature = "dropin"))] state.clear_poison(); } } @@ -176,6 +232,14 @@ where F: FnOnce(&mut AnalysisState) -> Result, { let state = get()?; + // The drop-in core is built with `panic = "abort"`: no panic can poison + // its lock, so there is no error to report (and no `PoisonError` text + // to format). + #[cfg(feature = "dropin")] + let mut guard = state + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + #[cfg(not(feature = "dropin"))] let mut guard = state.lock()?; f(&mut guard) } diff --git a/rust/src/sys.rs b/rust/src/sys.rs new file mode 100644 index 0000000..d095af0 --- /dev/null +++ b/rust/src/sys.rs @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-3.0-only +// +// The C library calls of the result channel: create, write, rename, link +// and remove the result files, read the environment, write a diagnostic to +// stderr. They are the calls `std::fs`, `std::env` and `std::process` made +// before (`open64`, `write`, `close`, `rename`, `linkat`, `unlink`, +// `getpid`, `getenv`, `__xpg_strerror_r`) with the same flags, so KLEE's +// POSIX model sees the same system calls; without `std::io`, `std::path` and +// `core::fmt`, which made up much of the drop-in core that `afl-clang-fast` +// compiles three times per Map2Check run (Task 3.3). +// +// With `ffi.rs` and `wasm.rs`, the only module with unsafe code; every +// unsafe block has a SAFETY comment. + +use std::ffi::c_int; +use std::path::Path; + +/// The libc functions, kept out of `include/map2check.h`: the library +/// does not define them. +/// +/// cbindgen:ignore +mod libc { + use std::ffi::{c_char, c_int, c_void}; + + extern "C" { + pub(super) fn open64(path: *const c_char, flags: c_int, ...) -> c_int; + pub(super) fn write(fd: c_int, buf: *const c_void, count: usize) -> isize; + pub(super) fn close(fd: c_int) -> c_int; + pub(super) fn rename(old: *const c_char, new: *const c_char) -> c_int; + pub(super) fn linkat( + olddirfd: c_int, + old: *const c_char, + newdirfd: c_int, + new: *const c_char, + flags: c_int, + ) -> c_int; + pub(super) fn unlink(path: *const c_char) -> c_int; + pub(super) fn getpid() -> c_int; + pub(super) fn getenv(name: *const c_char) -> *mut c_char; + pub(super) fn __errno_location() -> *mut c_int; + #[link_name = "__xpg_strerror_r"] + pub(super) fn strerror_r(errnum: c_int, buf: *mut c_char, len: usize) -> c_int; + } +} +use libc::*; + +const O_WRONLY: c_int = 0o1; +const O_CREAT: c_int = 0o100; +const O_EXCL: c_int = 0o200; +const O_TRUNC: c_int = 0o1000; +const O_CLOEXEC: c_int = 0o2000000; +const AT_FDCWD: c_int = -100; +const EINTR: c_int = 4; +const EEXIST: c_int = 17; +const ENOENT: c_int = 2; + +/// Why a result file could not be written. +#[derive(Debug, Clone, Copy)] +pub enum FileError { + /// The `errno` of the failed call. + Os(i32), + /// `write` wrote nothing. + WriteZero, + /// The path has a NUL byte. + NulInPath, + /// Old-API writers only. + Other(&'static str), +} + +impl FileError { + /// The text `std::io::Error` printed for the same error, which + /// the diagnostics kept: ` (os error )`. + pub fn describe(&self, out: &mut Vec) { + match *self { + FileError::Os(code) => { + let mut buf = [0u8; 128]; + // SAFETY: a buffer of `buf.len()` bytes; the XSI strerror_r + // writes a NUL-terminated message into it. + let ok = unsafe { strerror_r(code, buf.as_mut_ptr().cast(), buf.len()) } == 0; + if ok { + let len = buf.iter().position(|&b| b == 0).unwrap_or(buf.len()); + out.extend_from_slice(&buf[..len]); + } else { + out.extend_from_slice(b"Unknown error"); + } + out.extend_from_slice(b" (os error "); + crate::cfmt::put_i64(out, i64::from(code)); + out.push(b')'); + } + FileError::WriteZero => out.extend_from_slice(b"failed to write whole buffer"), + FileError::NulInPath => { + out.extend_from_slice(b"file name contained an unexpected NUL byte"); + } + FileError::Other(text) => out.extend_from_slice(text.as_bytes()), + } + } + + pub fn is_already_exists(&self) -> bool { + matches!(self, FileError::Os(EEXIST)) + } + + pub fn is_not_found(&self) -> bool { + matches!(self, FileError::Os(ENOENT)) + } + + fn is_interrupted(&self) -> bool { + matches!(self, FileError::Os(EINTR)) + } +} + +#[cfg(not(feature = "dropin"))] +impl std::fmt::Display for FileError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut text = Vec::new(); + self.describe(&mut text); + f.write_str(&String::from_utf8_lossy(&text)) + } +} + +pub type FileResult = Result; + +fn errno() -> FileError { + // SAFETY: `__errno_location` returns the calling thread's errno. + FileError::Os(unsafe { *__errno_location() }) +} + +/// `dir/name` as a NUL-terminated path, joined as `Path::join` joins a +/// relative name. +pub fn c_path(dir: &Path, name: &[u8]) -> FileResult> { + use std::os::unix::ffi::OsStrExt as _; + let dir = dir.as_os_str().as_bytes(); + let mut path = Vec::with_capacity(dir.len() + name.len() + 2); + path.extend_from_slice(dir); + if !dir.is_empty() && !dir.ends_with(b"/") { + path.push(b'/'); + } + path.extend_from_slice(name); + // A plain loop, not `contains`: that one is `memchr`, 125 more lines of + // IR for a path of a few dozen bytes. + if path.iter().any(|&b| b == 0) { + return Err(FileError::NulInPath); + } + path.push(0); + Ok(path) +} + +/// An open file, closed on drop (the error of `close` is ignored, as +/// `std::fs::File` ignores it). +pub struct File(c_int); + +impl File { + fn open(path: &[u8], flags: c_int) -> FileResult { + loop { + // SAFETY: `path` is NUL-terminated (`c_path`); the mode is the + // third argument of an O_CREAT open. + let fd = unsafe { open64(path.as_ptr().cast(), flags, 0o666 as c_int) }; + if fd >= 0 { + return Ok(File(fd)); + } + let e = errno(); + if !e.is_interrupted() { + return Err(e); + } + } + } + + /// `std::fs::File::create`: truncated, or created with mode 0666. + pub fn create(path: &[u8]) -> FileResult { + File::open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC) + } + + /// `OpenOptions::new().write(true).create_new(true)`. + pub fn create_new(path: &[u8]) -> FileResult { + File::open(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC) + } + + /// `Write::write_all`. + pub fn write_all(&mut self, mut bytes: &[u8]) -> FileResult<()> { + while !bytes.is_empty() { + let len = bytes.len().min(isize::MAX as usize); + // SAFETY: `bytes` is valid for `len` bytes. + let n = unsafe { write(self.0, bytes.as_ptr().cast(), len) }; + if n < 0 { + let e = errno(); + if e.is_interrupted() { + continue; + } + return Err(e); + } + if n == 0 { + return Err(FileError::WriteZero); + } + bytes = &bytes[n as usize..]; + } + Ok(()) + } +} + +impl Drop for File { + fn drop(&mut self) { + // SAFETY: the descriptor this value owns, closed once. + unsafe { close(self.0) }; + } +} + +fn check(result: c_int) -> FileResult<()> { + if result == 0 { + Ok(()) + } else { + Err(errno()) + } +} + +/// `std::fs::rename`. +pub fn rename_file(from: &[u8], to: &[u8]) -> FileResult<()> { + // SAFETY: both paths are NUL-terminated (`c_path`). + check(unsafe { rename(from.as_ptr().cast(), to.as_ptr().cast()) }) +} + +/// `std::fs::hard_link`. +pub fn hard_link(from: &[u8], to: &[u8]) -> FileResult<()> { + // SAFETY: both paths are NUL-terminated (`c_path`). + check(unsafe { + linkat( + AT_FDCWD, + from.as_ptr().cast(), + AT_FDCWD, + to.as_ptr().cast(), + 0, + ) + }) +} + +/// `std::fs::remove_file`. +pub fn remove_file(path: &[u8]) -> FileResult<()> { + // SAFETY: the path is NUL-terminated (`c_path`). + check(unsafe { unlink(path.as_ptr().cast()) }) +} + +/// `std::process::id`. +pub fn process_id() -> u32 { + // SAFETY: `getpid` has no preconditions. + unsafe { getpid() as u32 } +} + +/// Whether the environment variable `name` is set, +/// `std::env::var_os(name).is_some()`. `name` ends with its NUL byte. +pub fn env_is_set(name: &[u8]) -> bool { + if name.last() != Some(&0) || name[..name.len() - 1].iter().any(|&b| b == 0) { + return false; + } + // SAFETY: a NUL-terminated name; the result is only compared with NULL. + !unsafe { getenv(name.as_ptr().cast()) }.is_null() +} + +/// Writes one diagnostic line, `[map2check] ` + `text` + newline, to +/// stderr. The host builds go through `eprintln!`, which the test harness +/// captures; the drop-in core writes the line with one `write(2)`. +pub fn diagnostic(text: &[u8]) { + let mut line = Vec::with_capacity(text.len() + 13); + line.extend_from_slice(b"[map2check] "); + line.extend_from_slice(text); + #[cfg(not(feature = "dropin"))] + eprintln!("{}", String::from_utf8_lossy(&line)); + #[cfg(feature = "dropin")] + { + line.push(b'\n'); + let mut stderr = std::mem::ManuallyDrop::new(File(2)); + let _ = stderr.write_all(&line); + } +} diff --git a/rust/src/wasm.rs b/rust/src/wasm.rs index 9dd8e3f..ac0d9e6 100644 --- a/rust/src/wasm.rs +++ b/rust/src/wasm.rs @@ -33,9 +33,9 @@ fn record(property: ViolatedProperty, line: u32) { s.set_false(property, line, ""); Ok(first) }) { - Ok(true) => crate::output::termination::on_violation(false), + Ok(true) => crate::output::termination::on_violation(), Ok(false) => {} - Err(e) => eprintln!("[map2check] error: {e}"), + Err(e) => crate::output::termination::log_error(&e), } } @@ -46,7 +46,6 @@ fn record(property: ViolatedProperty, line: u32) { /// pass emits the call right after the call to `w2c_*_malloc`, with the /// zero-extended result and size. Offsets 0 and `(uint64_t)-1` are ignored. /// Tracking happens in every mode. -#[no_mangle] pub extern "C" fn map2check_wasm_malloc(offset: u64, size: u64) { ffi_guard( || { @@ -54,7 +53,7 @@ pub extern "C" fn map2check_wasm_malloc(offset: u64, size: u64) { memtrack::wasm_register_malloc(&mut s.wasm_allocs, offset, size); Ok(()) }) { - eprintln!("[map2check] error: {e}"); + crate::output::termination::log_error(&e); } }, (), @@ -66,7 +65,6 @@ pub extern "C" fn map2check_wasm_malloc(offset: u64, size: u64) { /// a `MEMSAFETY_FREE` violation at `line` (the pass sends the source line as /// an `i64`); an already freed block is found again and is not an error. /// The violation is reported only where the memory checks are active. -#[no_mangle] pub extern "C" fn map2check_wasm_free(offset: u64, line: i64) { ffi_guard( || { @@ -75,7 +73,7 @@ pub extern "C" fn map2check_wasm_free(offset: u64, line: i64) { }) { Ok(found) => found, Err(e) => { - eprintln!("[map2check] error: {e}"); + crate::output::termination::log_error(&e); return; } }; @@ -94,7 +92,6 @@ pub extern "C" fn map2check_wasm_free(offset: u64, line: i64) { /// store through the lifted memory. A bad access is reported as develop does, /// `FALSE_OVERFLOW` with line 0 and no function name: the report tag is /// `OVERFLOW`. Reported only where the memory checks are active. -#[no_mangle] pub extern "C" fn map2check_wasm_check_access(offset: u64, access_size: u64) { ffi_guard( || { @@ -110,7 +107,7 @@ pub extern "C" fn map2check_wasm_check_access(offset: u64, access_size: u64) { }) { Ok(valid) => valid, Err(e) => { - eprintln!("[map2check] error: {e}"); + crate::output::termination::log_error(&e); return; } }; @@ -187,8 +184,7 @@ pub mod rt { /// /// # Safety /// `mem` must point to a writable `wasm_rt_memory_t`. - #[no_mangle] - pub unsafe extern "C" fn wasm_rt_allocate_memory( + pub(super) unsafe fn wasm_rt_allocate_memory_impl( mem: *mut wasm_rt_memory_t, initial_pages: u64, max_pages: u64, @@ -218,8 +214,7 @@ pub mod rt { /// /// # Safety /// `mem` must point to a memory set up by [`wasm_rt_allocate_memory`]. - #[no_mangle] - pub unsafe extern "C" fn wasm_rt_grow_memory(mem: *mut wasm_rt_memory_t, pages: u64) -> u64 { + pub(super) unsafe fn wasm_rt_grow_memory_impl(mem: *mut wasm_rt_memory_t, pages: u64) -> u64 { ffi_guard( || { let failure = u64::from(u32::MAX); @@ -269,8 +264,7 @@ pub mod rt { /// /// # Safety /// `mem` must point to a memory set up by [`wasm_rt_allocate_memory`]. - #[no_mangle] - pub unsafe extern "C" fn wasm_rt_free_memory(mem: *mut wasm_rt_memory_t) { + pub(super) unsafe fn wasm_rt_free_memory_impl(mem: *mut wasm_rt_memory_t) { ffi_guard( || { // SAFETY: caller guarantees `mem` is a valid descriptor whose @@ -293,8 +287,7 @@ pub mod rt { /// /// # Safety /// `table` must point to a writable `wasm_rt_funcref_table_t`. - #[no_mangle] - pub unsafe extern "C" fn wasm_rt_allocate_funcref_table( + pub(super) unsafe fn wasm_rt_allocate_funcref_table_impl( table: *mut wasm_rt_funcref_table_t, elements: u32, _max_elements: u32, @@ -320,8 +313,7 @@ pub mod rt { /// # Safety /// `table` must point to a table set up by /// [`wasm_rt_allocate_funcref_table`]. - #[no_mangle] - pub unsafe extern "C" fn wasm_rt_free_funcref_table(table: *mut wasm_rt_funcref_table_t) { + pub(super) unsafe fn wasm_rt_free_funcref_table_impl(table: *mut wasm_rt_funcref_table_t) { ffi_guard( || { // SAFETY: caller guarantees `table` is valid; data is calloc'd. @@ -334,49 +326,121 @@ pub mod rt { ); } - /// WASI `proc_exit`: a no-op, so that the analysis, not the lifted - /// program, decides when the run ends. + /// The old API's body of this C name, not exported since 2.0.0 (Task + /// 2.10): the C name belongs to the IR module `WasmRuntimeStubs.ll`. + /// Rust tests call it; the body is `wasm_rt_allocate_memory_impl`, which + /// `__m2c_rt_wasm_*` also calls. /// /// # Safety - /// `instance` is never dereferenced and may be null. - #[no_mangle] - pub unsafe extern "C" fn w2c_wasi__snapshot__preview1_proc_exit( - instance: *mut c_void, - code: u32, + /// As `wasm_rt_allocate_memory_impl`. + pub unsafe extern "C" fn wasm_rt_allocate_memory( + mem: *mut wasm_rt_memory_t, + initial_pages: u64, + max_pages: u64, + is64: bool, + page_size: u32, ) { + // SAFETY: forwarded caller contract. ffi_guard( - || { - let _ = (instance, code); + || unsafe { + wasm_rt_allocate_memory_impl(mem, initial_pages, max_pages, is64, page_size) }, (), - ); + ) + } + + /// The old API's body of this C name, not exported since 2.0.0 (Task + /// 2.10): the C name belongs to the IR module `WasmRuntimeStubs.ll`. + /// Rust tests call it; the body is `wasm_rt_grow_memory_impl`, which + /// `__m2c_rt_wasm_*` also calls. + /// + /// # Safety + /// As `wasm_rt_grow_memory_impl`. + pub unsafe extern "C" fn wasm_rt_grow_memory(mem: *mut wasm_rt_memory_t, pages: u64) -> u64 { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { wasm_rt_grow_memory_impl(mem, pages) }, + u64::from(u32::MAX), + ) + } + + /// The old API's body of this C name, not exported since 2.0.0 (Task + /// 2.10): the C name belongs to the IR module `WasmRuntimeStubs.ll`. + /// Rust tests call it; the body is `wasm_rt_free_memory_impl`, which + /// `__m2c_rt_wasm_*` also calls. + /// + /// # Safety + /// As `wasm_rt_free_memory_impl`. + pub unsafe extern "C" fn wasm_rt_free_memory(mem: *mut wasm_rt_memory_t) { + // SAFETY: forwarded caller contract. + ffi_guard(|| unsafe { wasm_rt_free_memory_impl(mem) }, ()) + } + + /// The old API's body of this C name, not exported since 2.0.0 (Task + /// 2.10): the C name belongs to the IR module `WasmRuntimeStubs.ll`. + /// Rust tests call it; the body is `wasm_rt_allocate_funcref_table_impl`, which + /// `__m2c_rt_wasm_*` also calls. + /// + /// # Safety + /// As `wasm_rt_allocate_funcref_table_impl`. + pub unsafe extern "C" fn wasm_rt_allocate_funcref_table( + table: *mut wasm_rt_funcref_table_t, + elements: u32, + max_elements: u32, + ) { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { wasm_rt_allocate_funcref_table_impl(table, elements, max_elements) }, + (), + ) + } + + /// The old API's body of this C name, not exported since 2.0.0 (Task + /// 2.10): the C name belongs to the IR module `WasmRuntimeStubs.ll`. + /// Rust tests call it; the body is `wasm_rt_free_funcref_table_impl`, which + /// `__m2c_rt_wasm_*` also calls. + /// + /// # Safety + /// As `wasm_rt_free_funcref_table_impl`. + pub unsafe extern "C" fn wasm_rt_free_funcref_table(table: *mut wasm_rt_funcref_table_t) { + // SAFETY: forwarded caller contract. + ffi_guard(|| unsafe { wasm_rt_free_funcref_table_impl(table) }, ()) } /// "Always initialised for KLEE": true from the start. static INITIALIZED: AtomicBool = AtomicBool::new(true); - #[no_mangle] - pub extern "C" fn wasm_rt_is_initialized() -> bool { + pub(super) fn wasm_rt_is_initialized_impl() -> bool { ffi_guard(|| INITIALIZED.load(Ordering::Relaxed), true) } - #[no_mangle] - pub extern "C" fn wasm_rt_init() { + pub(super) fn wasm_rt_init_impl() { ffi_guard(|| INITIALIZED.store(true, Ordering::Relaxed), ()); } - #[no_mangle] - pub extern "C" fn wasm_rt_free() { + pub(super) fn wasm_rt_free_impl() { ffi_guard(|| INITIALIZED.store(false, Ordering::Relaxed), ()); } + /// Not exported since 2.0.0 (Task 2.10), as above. + pub extern "C" fn wasm_rt_is_initialized() -> bool { + ffi_guard(wasm_rt_is_initialized_impl, true) + } + + pub extern "C" fn wasm_rt_init() { + ffi_guard(wasm_rt_init_impl, ()) + } + + pub extern "C" fn wasm_rt_free() { + ffi_guard(wasm_rt_free_impl, ()) + } + /// A trap ends the run. develop calls `map2check_error()`, which writes no /// property and aborts, so the frontend reads UNKNOWN; here the run /// records an UNKNOWN verdict (a first violation, final) and aborts. /// `wasm-rt.h` declares the function `noreturn`, so it never returns, not /// even with no analysis mode set. - #[no_mangle] - pub extern "C" fn wasm_rt_trap(code: u32) -> ! { + pub(super) fn wasm_rt_trap_impl(code: u32) -> ! { ffi_guard( || { let _ = code; @@ -386,10 +450,136 @@ pub mod rt { ); std::process::abort() } + + /// Not exported since 2.0.0 (Task 2.10), as above. + pub extern "C" fn wasm_rt_trap(code: u32) -> ! { + ffi_guard(|| wasm_rt_trap_impl(code), ()); + std::process::abort() + } } pub use rt::*; +// ── Core entry points of `WasmRuntimeStubs.ll` (Task 2.9, D7 A-fwd) ───────── +// +// The IR module `WasmRuntimeStubs.ll` (`scripts/runtime-ir/gen_ll.py`) defines +// the C names `wasm_rt_*` the lifted code calls and forwards each one to the +// `__m2c_rt_wasm_*` below; `w2c_wasi__snapshot__preview1_proc_exit` has an +// empty C body (`WasmRuntimeStubs.c:90-93`) and is an empty IR define. The +// descriptors cross the C ABI as `void *` (the header does not know the wabt +// structs). The behaviour is `rt`'s, one-to-one with `WasmRuntimeStubs.c`. +// Since 2.0.0 (Task 2.10) the `wasm_rt_*` functions of `rt` are Rust only +// (tests call them); they and these entry points share the `*_impl` bodies +// (a `__m2c` function must never call the C name: the IR module defines it +// and forwards back here). + +use std::os::raw::c_void; + +/// `wasm_rt_allocate_memory` (`WasmRuntimeStubs.c:22-35`): `calloc`ed linear +/// memory of `initial_pages` pages; `is64` is not stored. +/// +/// # Safety +/// `mem` must point to a writable `wasm_rt_memory_t`. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_wasm_allocate_memory( + mem: *mut c_void, + initial_pages: u64, + max_pages: u64, + is64: bool, + page_size: u32, +) { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { + rt::wasm_rt_allocate_memory_impl(mem.cast(), initial_pages, max_pages, is64, page_size) + }, + (), + ); +} + +/// `wasm_rt_grow_memory` (`WasmRuntimeStubs.c:41-63`): the previous page +/// count, or `UINT32_MAX` on failure. +/// +/// # Safety +/// `mem` must point to a memory set up by `__m2c_rt_wasm_allocate_memory`. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_wasm_grow_memory(mem: *mut c_void, pages: u64) -> u64 { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { rt::wasm_rt_grow_memory_impl(mem.cast(), pages) }, + u64::from(u32::MAX), + ) +} + +/// `wasm_rt_free_memory` (`WasmRuntimeStubs.c:66-70`). +/// +/// # Safety +/// `mem` must point to a memory set up by `__m2c_rt_wasm_allocate_memory`. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_wasm_free_memory(mem: *mut c_void) { + // SAFETY: forwarded caller contract. + ffi_guard(|| unsafe { rt::wasm_rt_free_memory_impl(mem.cast()) }, ()); +} + +/// `wasm_rt_allocate_funcref_table` (`WasmRuntimeStubs.c:73-78`; the element +/// size is SD-19). +/// +/// # Safety +/// `table` must point to a writable `wasm_rt_funcref_table_t`. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_wasm_allocate_funcref_table( + table: *mut c_void, + elements: u32, + max_elements: u32, +) { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { rt::wasm_rt_allocate_funcref_table_impl(table.cast(), elements, max_elements) }, + (), + ); +} + +/// `wasm_rt_free_funcref_table` (`WasmRuntimeStubs.c:81-84`). +/// +/// # Safety +/// `table` must point to a table set up by +/// `__m2c_rt_wasm_allocate_funcref_table`. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_wasm_free_funcref_table(table: *mut c_void) { + // SAFETY: forwarded caller contract. + ffi_guard( + || unsafe { rt::wasm_rt_free_funcref_table_impl(table.cast()) }, + (), + ); +} + +/// `wasm_rt_init` (`WasmRuntimeStubs.c:102-104`): marks the runtime initialised. +#[no_mangle] +pub extern "C" fn __m2c_rt_wasm_init() { + ffi_guard(rt::wasm_rt_init_impl, ()); +} + +/// `wasm_rt_free` (`WasmRuntimeStubs.c:106-108`): marks it not initialised. +#[no_mangle] +pub extern "C" fn __m2c_rt_wasm_free() { + ffi_guard(rt::wasm_rt_free_impl, ()); +} + +/// `wasm_rt_is_initialized` (`WasmRuntimeStubs.c:98-100`): true from the start. +#[no_mangle] +pub extern "C" fn __m2c_rt_wasm_is_initialized() -> bool { + ffi_guard(rt::wasm_rt_is_initialized_impl, true) +} + +/// `wasm_rt_trap` (`WasmRuntimeStubs.c:110-116`): never returns. Records an +/// UNKNOWN verdict, then aborts (SD-19). +#[no_mangle] +pub extern "C" fn __m2c_rt_wasm_trap(code: u32) { + ffi_guard(|| rt::wasm_rt_trap_impl(code), ()); + // The impl aborts; a panic that ffi_guard caught must not return either. + std::process::abort() +} + #[cfg(test)] mod tests { use super::rt::*; diff --git a/scripts/build-c-abi-program.sh b/scripts/build-c-abi-program.sh new file mode 100755 index 0000000..538b9dd --- /dev/null +++ b/scripts/build-c-abi-program.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# Builds a C program against the C runtime ABI as Map2Check links it (D6, +# D7): the release staticlib plus the generated IR modules the program needs +# (scripts/runtime-ir/gen_ll.py), the way the link tests of +# scripts/runtime-ir/ do. Used by tests/ffi_harness.c and the programs of +# test/ (CI and CLAUDE.md). +# +# Usage: build-c-abi-program.sh -o [-m ]... [--lib ] +# [-I ] [-- ...] +# -m a module of gen_ll.py (e.g. AnalysisModeMemtrack, +# Map2CheckFunctions-glue, NonDetGeneratorAFL); written to +# target/runtime-ir/.ll and linked before the library +# --lib default target/release/libmap2check.a (cargo build --release --all-features) +# -I where map2check.h is (default include/) +# Environment: CLANG (default clang). +set -euo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$HERE/.." && pwd)" +CLANG="${CLANG:-clang}" +LIB="$ROOT/target/release/libmap2check.a" +INC="$ROOT/include" +OUT=""; SRC=""; MODS=(); FLAGS=() +usage() { echo "usage: $0 -o [-m ]... [--lib ] [-I

] [-- ...]" >&2; exit 2; } +while [ $# -gt 0 ]; do + case "$1" in + -o) OUT="$2"; shift 2 ;; + -m) MODS+=("$2"); shift 2 ;; + --lib) LIB="$2"; shift 2 ;; + -I) INC="$2"; shift 2 ;; + --) shift; FLAGS=("$@"); break ;; + -*) usage ;; + *) [ -z "$SRC" ] || usage; SRC="$1"; shift ;; + esac +done +[ -n "$OUT" ] && [ -n "$SRC" ] || usage +[ -f "$LIB" ] || { echo "missing $LIB (cargo build --release --all-features)" >&2; exit 2; } +LLDIR="$ROOT/target/runtime-ir" +LLS=() +for m in "${MODS[@]}"; do + python3 -I "$HERE/runtime-ir/gen_ll.py" --module "$m" "$ROOT/docs/runtime-abi/inventory.tsv" "$LLDIR" + LLS+=("$LLDIR/$m.ll") +done +mkdir -p "$(dirname "$OUT")" +# The IR modules are compiled without the sanitizer flags of the C source: +# they are the runtime, as the .bc Map2Check links are. +objs=() +T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT +for ll in "${LLS[@]}"; do + "$CLANG" --target=x86_64-pc-linux-gnu -O2 -c -x ir "$ll" -o "$T/$(basename "$ll" .ll).o" + objs+=("$T/$(basename "$ll" .ll).o") +done +"$CLANG" "${FLAGS[@]}" -I "$INC" "$SRC" "${objs[@]}" "$LIB" \ + -ldl -lpthread -lm -o "$OUT" diff --git a/scripts/build-klee-runtime.sh b/scripts/build-klee-runtime.sh deleted file mode 100755 index fa9b999..0000000 --- a/scripts/build-klee-runtime.sh +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: GPL-3.0-only -# -# Builds the runtime of the Map2Check `symex` generator as one LLVM 16 -# bitcode module (D1, route A', docs/cutover/decisoes.md): -# -# rustc 1.72.1 (LLVM 16) on klee/Cargo.toml with its own Cargo.lock, -# features `klee,wasm`, panic = abort, fat LTO, codegen-units = 1, -# --emit=llvm-bc, then `opt-16 --strip-debug` (condition 6), then std's -# backtrace symbolizer stubbed out (scripts/klee-stub-symbolizer.py, -# `opt-16 -passes=globaldce`), then the vector intrinsics KLEE cannot run -# lowered to scalar code (scripts/klee-lower-intrinsics.py), then a -# read-back with `llvm-dis-16` and the symbolizer guard. -# -# Usage: scripts/build-klee-runtime.sh -# -# Environment: -# KLEE_RUSTC_TOOLCHAIN rustup toolchain (default 1.72.1) -# OPT, LLVM_DIS, LLVM_NM, LLVM_AS, LLI LLVM 16 tools (default opt-16, -# llvm-dis-16, llvm-nm-16, llvm-as-16, lli-16) -# CARGO_TARGET_DIR target directory (default /target/klee) -# -# Every step must succeed; the script stops at the first failure and then -# leaves nothing at . -set -euo pipefail - -die() { - echo "build-klee-runtime: error: $*" >&2 - exit 1 -} - -[ $# -eq 1 ] || die "usage: $0 " -out="$1" -case "$out" in - *.bc) ;; - *) die "the output must end in .bc: $out" ;; -esac - -# A failed build must not leave an earlier (or partial) runtime behind. -rm -f "$out" - -ROOT="$(cd "$(dirname "$0")/.." && pwd)" -TOOLCHAIN="${KLEE_RUSTC_TOOLCHAIN:-1.72.1}" -OPT="${OPT:-opt-16}" -LLVM_DIS="${LLVM_DIS:-llvm-dis-16}" -LLVM_NM="${LLVM_NM:-llvm-nm-16}" -LLVM_AS="${LLVM_AS:-llvm-as-16}" -LLI="${LLI:-lli-16}" -TARGET_DIR="${CARGO_TARGET_DIR:-$ROOT/target/klee}" - -for tool in cargo rustc python3 "$OPT" "$LLVM_DIS" "$LLVM_NM" "$LLVM_AS" "$LLI"; do - command -v "$tool" >/dev/null || die "$tool not found in PATH" -done - -# The bitcode must come from LLVM 16: a newer LLVM writes attributes that -# llvm-link-16 and KLEE 3.1 reject ("Unknown attribute kind (91)"). -llvm_version="$(RUSTUP_TOOLCHAIN="$TOOLCHAIN" rustc -vV | sed -n 's/^LLVM version: //p')" -case "$llvm_version" in - 16.*) ;; - *) die "rustc $TOOLCHAIN uses LLVM '$llvm_version', not 16" ;; -esac - -deps="$TARGET_DIR/release/deps" -# Always rebuild the crate itself (not its dependencies): the rustc command -# line checked below only shows up when it is compiled, and a stale module -# would be picked up. -RUSTUP_TOOLCHAIN="$TOOLCHAIN" CARGO_TARGET_DIR="$TARGET_DIR" \ - cargo clean --release -p map2check-klee-runtime \ - --manifest-path "$ROOT/klee/Cargo.toml" >/dev/null 2>&1 || die "cargo clean failed" -if [ -d "$deps" ]; then - find "$deps" -maxdepth 1 -name 'map2check-*.bc' -delete -fi - -work="$(mktemp -d)" -trap 'rm -rf "$work"' EXIT -log="$work/cargo.log" -# --locked: the dependency graph is the one in klee/Cargo.lock (v3), never -# a fresh resolution. The profile (klee/Cargo.toml) sets panic = abort, -# lto = fat and codegen-units = 1; the check below proves rustc got them. -if ! RUSTUP_TOOLCHAIN="$TOOLCHAIN" CARGO_TARGET_DIR="$TARGET_DIR" \ - cargo rustc --locked --release --features klee,wasm --lib -v \ - --manifest-path "$ROOT/klee/Cargo.toml" -- --emit=llvm-bc >"$log" 2>&1; then - cat "$log" >&2 - die "cargo rustc failed" -fi - -crate_line="$(grep -E 'Running `.*rustc .*--crate-name map2check ' "$log" | tail -n 1 || true)" -[ -n "$crate_line" ] || { cat "$log" >&2; die "no rustc command line for the map2check crate"; } -for flag in '-C panic=abort' '-C lto' '-C codegen-units=1' '--emit=llvm-bc'; do - case "$crate_line" in - *"$flag"*) ;; - *) die "rustc was not given '$flag': $crate_line" ;; - esac -done -case "$crate_line" in - *'-C lto=thin'* | *'-C lto=off'* | *'-C lto=no'*) die "LTO is not fat: $crate_line" ;; -esac -# Coverage counters (a RUSTFLAGS or .cargo/config.toml `instrument-coverage`) -# put a load/add/store in every basic block that KLEE then executes: about a -# third of the runtime's instructions on list-ext (Task 2.7b). -case "$crate_line" in - *instrument-coverage*) die "the runtime must not be built with coverage instrumentation: $crate_line" ;; -esac - -mapfile -t modules < <(find "$deps" -maxdepth 1 -name 'map2check-*.bc') -[ "${#modules[@]}" -eq 1 ] || die "expected one map2check-*.bc in $deps, found ${#modules[@]}" - -# The debug info of the precompiled std does not survive llvm-link-16 + -# KLEE ("Broken module found"); only the runtime loses it, the analysed -# program keeps its -g. -# Everything is built in $work and moved to $out only at the end, so a -# failed step never leaves a bitcode behind. -fixtures="$ROOT/tests/klee" -"$OPT" --strip-debug "${modules[0]}" -o "$work/stripped.bc" || die "$OPT --strip-debug failed" -"$LLVM_DIS" "$work/stripped.bc" -o "$work/stripped.ll" || die "$LLVM_DIS cannot read the stripped module" - -# std's backtrace symbolizer (gimli, addr2line, miniz_oxide, rustc_demangle, -# backtrace_rs) is about half of the module and only runs when a panic -# prints a backtrace; KLEE spent about 1.6 s and 60 MB per run loading and -# re-optimising it (task 4.3, criterion 9). Its bodies become a call to -# abort, so a path that reaches them still ends as an abort and no symbol -# is left undefined; globaldce then drops what only they used. The self-test runs first: a body -# the remaining code shares must keep working (a printer-named one too), -# the printer path must abort, and the guard must reject the fixture before -# the rewrite and a stub that live code calls. -stub="$ROOT/scripts/klee-stub-symbolizer.py" -"$LLI" "$fixtures/stub-symbolizer.ll" >/dev/null 2>&1 && rc=0 || rc=$? -[ "$rc" = 42 ] || die "$LLI: the stub fixture returned $rc, not 42" -python3 -I "$stub" "$fixtures/stub-symbolizer.ll" "$work/stub-fixture.ll" >/dev/null || - die "the stub self-test fixture was rejected" -"$LLI" "$work/stub-fixture.ll" >/dev/null 2>&1 && rc=0 || rc=$? -[ "$rc" = 42 ] || die "the stub rewrite broke a shared body: the fixture returned $rc, not 42" -"$LLI" "$work/stub-fixture.ll" backtrace >/dev/null 2>&1 && rc=0 || rc=$? -[ "$rc" = 134 ] || die "the stubbed printer path did not abort: the fixture returned $rc, not 134" -if python3 -I "$stub" --check "$fixtures/stub-symbolizer.ll" >/dev/null 2>&1; then - die "the symbolizer guard accepted a module that still has the symbolizer (stub-symbolizer.ll)" -fi -python3 -I "$stub" --check "$work/stub-fixture.ll" >/dev/null || - die "the symbolizer guard rejected the rewritten fixture" -if python3 -I "$stub" --check "$fixtures/stub-symbolizer-live.ll" >/dev/null 2>&1; then - die "the symbolizer guard accepted a stub that live code calls (stub-symbolizer-live.ll)" -fi -python3 -I "$stub" "$work/stripped.ll" "$work/stubbed.ll" || die "stubbing the backtrace symbolizer failed" -"$LLVM_AS" "$work/stubbed.ll" -o "$work/stubbed.bc" || die "$LLVM_AS rejected the stubbed module" -"$OPT" -passes=globaldce "$work/stubbed.bc" -o "$work/dce.bc" || die "$OPT -passes=globaldce failed" -"$LLVM_DIS" "$work/dce.bc" -o "$work/dce.ll" || die "$LLVM_DIS cannot read the module after globaldce" - -# KLEE 3.1 cannot execute the vector/x86 intrinsics of the precompiled std -# ("unimplemented intrinsic" in core::fmt padding): replace them by scalar -# functions. The script fails on any remaining intrinsic KLEE 3.1 does not -# execute. Its two self-tests run first: the scalar versions must compute -# what the intrinsics compute (lli-16), and an unknown intrinsic must be -# rejected. -lower="$ROOT/scripts/klee-lower-intrinsics.py" -python3 -I "$lower" "$fixtures/lower-intrinsics.ll" "$work/fixture.ll" >/dev/null || - die "the lowering self-test fixture was rejected" -expected="$("$LLI" "$fixtures/lower-intrinsics.ll")" || die "$LLI failed on the fixture" -got="$("$LLI" "$work/fixture.ll")" || die "$LLI failed on the lowered fixture" -[ "$expected" = "$got" ] || - die "the scalar versions differ from the intrinsics: '$expected' vs '$got'" -if python3 -I "$lower" "$fixtures/unknown-intrinsic.ll" "$work/unknown.ll" >/dev/null 2>&1; then - die "the lowering accepted an intrinsic KLEE 3.1 cannot execute (unknown-intrinsic.ll)" -fi -python3 -I "$lower" "$work/dce.ll" "$work/lowered.ll" || - die "lowering the intrinsics failed" -"$LLVM_AS" "$work/lowered.ll" -o "$work/runtime.bc" || die "$LLVM_AS rejected the lowered module" -"$LLVM_DIS" "$work/runtime.bc" -o "$work/runtime.ll" || die "$LLVM_DIS cannot read the lowered module" -# Guard: no symbolizer function that only the backtrace printer reaches may -# keep a body. The shared bodies it prints are kept on purpose (see the -# script). -python3 -I "$stub" --check "$work/runtime.ll" || die "the backtrace symbolizer is still in the module" - -# The module must define what the symex pipeline links against, and leave -# the KLEE intrinsics to KLEE. -defined="$("$LLVM_NM" --defined-only "$work/runtime.bc")" -for sym in main __VERIFIER_nondet_int __VERIFIER_nondet_double __VERIFIER_assume \ - __VERIFIER_error map2check_init map2check_set_mode map2check_success; do - grep -Eq "[[:space:]]$sym\$" <<<"$defined" || die "$out does not define $sym" -done -undefined="$("$LLVM_NM" --undefined-only "$work/runtime.bc")" -for sym in klee_make_symbolic klee_assume __map2check_main__; do - grep -Eq "[[:space:]]$sym\$" <<<"$undefined" || die "$out does not import $sym" -done - -mkdir -p "$(dirname "$out")" -mv -f "$work/runtime.bc" "$out" || die "cannot write $out" -echo "build-klee-runtime: wrote $out (rustc $TOOLCHAIN, LLVM $llvm_version)" diff --git a/scripts/build-runtime-bc.sh b/scripts/build-runtime-bc.sh new file mode 100755 index 0000000..ae80420 --- /dev/null +++ b/scripts/build-runtime-bc.sh @@ -0,0 +1,249 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# The drop-in runtime as the 21 bitcode modules Map2Check links (the D7 +# layout A-fwd, docs/cutover/d7-layout.md): exactly the file names the +# frontend's Caller::linkLLVM passes to llvm-link, nothing else in . +# Deterministic: two builds of the same tree give the same bytes +# (scripts/test-build-runtime-bc.sh). scripts/it/overlay-install.sh puts them +# into a copy of a C install of Map2Check. +# +# Map2CheckFunctions.bc the Rust core: klee/Cargo.toml with rustc 1.72.1 +# (D1, route A'), features `dropin,wasm`, fat LTO, +# panic = abort, --emit=llvm-bc, then the D7 +# post-processing below, linked with the IR glue +# (map2check_assume*); +# the other 20 scripts/runtime-ir/gen_ll.py, assembled with +# llvm-as-16. +# +# D7 post-processing of the core ("Pós-processamento do núcleo"): +# 1. opt-16 --strip-debug (D1 condition 6); +# 2. scripts/klee-stub-symbolizer.py + opt-16 -passes=globaldce; +# 3. scripts/runtime-ir/stub_personality.py (rust_eh_personality and +# std::panicking::rust_panic_with_hook -> abort; the hook approved by the +# user on 2026-10-27); +# 4. scripts/klee-lower-intrinsics.py (KLEE 3.1 runs no vector intrinsic); +# 5. opt-16 -passes=internalize,globaldce, public = the names of +# docs/runtime-abi/inventory.tsv that no IR module defines, plus __m2c_* +# (the core never defines a name only a mode or generator module +# defines: D7, Task 2.10 note); +# 6. one triple, x86_64-pc-linux-gnu, and no "Dwarf Version" module flag +# (scripts/runtime-ir/drop_dwarf_flag.py). +# Guards: no `freeze` in the core after globaldce (KLEE 3.1 cannot execute +# it; scripts/runtime-ir/check_no_freeze.py names the functions, and the fix +# is in the Rust source, never a rewrite here); the core defines no name an +# IR module defines; every __m2c_rt_* an IR module references is defined by +# the core; the core references no klee_*/__afl_* intrinsic; the Rust part +# references no longjmp path (longjmp/setjmp, nondet_assume/cancel, +# map2check_assume*) and not the program (main, __map2check_main__), so the +# AFL++ generator's longjmp never crosses a Rust frame (D7 item 5); +# the final Map2CheckFunctions.bc has fewer than 9 500 instruction lines and +# fewer than 13 600 IR lines (the .ll that afl-clang-fast instruments and +# compiles three times per AFL++ run, and that KLEE loads and re-optimises: +# 125 829 instruction lines with std's backtrace symbolizer, 34 582 without +# it, 8 865 and 12 741 IR lines once Task 3.3 took core::fmt, std::io, +# std::fs and serde out of the drop-in build; the ceilings are those values +# plus about 7%) and no `llvm.bitreverse` (only miniz_oxide, part of the +# symbolizer, used it); the checks were in tests/klee_paths.sh until Task 2.10; +# llvm-dis-16 reads all 21 modules. +# +# Usage: build-runtime-bc.sh +# build-runtime-bc.sh --check-core (the two checks +# above on a disassembled core; scripts/runtime-ir/test-core-checks.sh) +# Environment: M2C_CORE_MAX_LINES (default 9500), the instruction-line +# ceiling; M2C_CORE_MAX_IR_LINES (default 13600), the IR-line ceiling. +# The Rust step runs on the host (rustup toolchain KLEE_RUSTC_TOOLCHAIN, +# default 1.72.1). The LLVM 16 steps run on the host when opt-16, llvm-as-16, +# llvm-dis-16, llvm-link-16 and llvm-nm-16 are on PATH, otherwise in the +# Map2Check image (IMAGE, default map2check-dev:develop-). +# Environment: CARGO_TARGET_DIR (default target/dropin-core). +set -euo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$HERE/.." && pwd)" +die() { echo "build-runtime-bc: error: $*" >&2; exit 1; } + +MODULES="Map2CheckFunctions TrackBBLog NonDetLog PropertyGenerator ContainerRealloc ContainerBTree BTree AnalysisModeMemtrack AnalysisModeMemcleanup AnalysisModeOverflow AnalysisModeAssert AnalysisModeNone AllocationLog ListLog HeapLog NonDetGeneratorNone NonDetGeneratorKlee NonDetGeneratorAFL WasmRuntimeStubs WitnessGeneration WitnessGenerationNone" + +# --------------------------------------------------------------------------- +# Instruction-line and IR-line ceilings and no llvm.bitreverse, on the final +# core's .ll. +check_core() { + local ll="$1" max="${M2C_CORE_MAX_LINES:-9500}" max_ir="${M2C_CORE_MAX_IR_LINES:-13600}" lines ir + lines="$(awk '/^define/{f=1; next} f&&/^}/{f=0; next} f{n++} END{print n+0}' "$ll")" + if [ "$lines" -ge "$max" ]; then + echo "the core has $lines instruction lines, expected < $max: is the backtrace symbolizer, core::fmt or std::io back (Task 3.3)?" >&2 + return 1 + fi + ir="$(wc -l < "$ll")" + if [ "$ir" -ge "$max_ir" ]; then + echo "the core has $ir IR lines, expected < $max_ir: afl-clang-fast compiles it three times per AFL++ run (Task 3.3)" >&2 + return 1 + fi + if grep -q 'llvm\.bitreverse' "$ll"; then + echo "the core still has llvm.bitreverse (miniz_oxide, part of the backtrace symbolizer)" >&2 + return 1 + fi + echo "core checks: $lines instruction lines (< $max), $ir IR lines (< $max_ir), no llvm.bitreverse" +} + +# --------------------------------------------------------------------------- +# LLVM 16 part. Runs with ; the work dir holds core.bc, +# ll/*.ll and abi.txt. +postprocess() { + local repo="$1" w="$2" out="$3" m + # 1. D1 condition 6: the debug info of the precompiled std does not survive + # llvm-link-16 + KLEE. + opt-16 --strip-debug "$w/core.bc" -o "$w/c0.bc" + llvm-dis-16 "$w/c0.bc" -o "$w/c0.ll" + # 2-3. std's backtrace symbolizer stubbed (self-tests: scripts/test-klee-postprocess.sh) + # and rust_eh_personality stubbed, so the unwinder (libgcc_s) is not needed. + python3 -I "$repo/scripts/klee-stub-symbolizer.py" "$w/c0.ll" "$w/c1.ll" >/dev/null + python3 -I "$repo/scripts/runtime-ir/stub_personality.py" "$w/c1.ll" "$w/c2.ll" >/dev/null + llvm-as-16 "$w/c2.ll" -o "$w/c2.bc" + opt-16 -passes=globaldce "$w/c2.bc" -o "$w/c3.bc" + llvm-dis-16 "$w/c3.bc" -o "$w/c3.ll" + # 4. Vector intrinsics KLEE 3.1 cannot execute, lowered to scalar code. + python3 -I "$repo/scripts/klee-lower-intrinsics.py" "$w/c3.ll" "$w/c4.ll" >/dev/null + python3 -I "$repo/scripts/klee-stub-symbolizer.py" --check "$w/c4.ll" >/dev/null \ + || { echo "the backtrace symbolizer is still in the core" >&2; return 1; } + # 6. One target triple for every module (clang-16's), and no DWARF flag: + # the frontend does not redirect llvm-link's stderr. + sed -i 's/^target triple = "x86_64-unknown-linux-gnu"/target triple = "x86_64-pc-linux-gnu"/' "$w/c4.ll" + grep -q '^target triple = "x86_64-pc-linux-gnu"' "$w/c4.ll" \ + || { echo "the core has another target triple" >&2; return 1; } + python3 -I "$repo/scripts/runtime-ir/drop_dwarf_flag.py" "$w/c4.ll" "$w/c4.ll" + if grep -q '"Dwarf Version"' "$w/c4.ll"; then echo "the core keeps the Dwarf Version flag" >&2; return 1; fi + llvm-as-16 "$w/c4.ll" -o "$w/c5.bc" + + # The 20 IR modules (and the glue) assembled; what they define. + mkdir -p "$w/irbc" + # Assembled from inside ll/, so source_filename is ".ll" and not + # the work directory, which differs from build to build. + for f in "$w"/ll/*.ll; do + m="$(basename "$f" .ll)" + (cd "$w/ll" && llvm-as-16 "$m.ll" -o "$w/irbc/$m.bc") + done + : > "$w/ir-defined.txt" + for f in "$w"/irbc/*.bc; do + [ "$(basename "$f")" = Map2CheckFunctions-glue.bc ] && continue + llvm-nm-16 --defined-only --extern-only "$f" 2>/dev/null | awk 'NF==3 {print $3}' >> "$w/ir-defined.txt" + done + sort -u -o "$w/ir-defined.txt" "$w/ir-defined.txt" + + # 5. Only the C ABI the core owns and the __m2c_* entry points stay + # external; std/core symbols (rust_panic, _ZN4core3fmt...) and the names a + # mode or generator module defines become internal (since Task 2.10 the + # staticlib exports none of the latter; scripts/check-export-set.sh). + { sort -u "$w/abi.txt" | comm -23 - "$w/ir-defined.txt" + llvm-nm-16 --defined-only "$w/c5.bc" | awk 'NF==3 {print $3}' | grep -E '^__m2c_' || true + } > "$w/public.txt" + opt-16 -passes=internalize,globaldce -internalize-public-api-file="$w/public.txt" "$w/c5.bc" -o "$w/core-s.bc" + + llvm-nm-16 --defined-only --extern-only "$w/core-s.bc" | awk 'NF==3 {print $3}' | sort -u > "$w/core-defined.txt" + local clash + clash="$(comm -12 "$w/core-defined.txt" "$w/ir-defined.txt" | tr '\n' ' ')" + [ -z "$clash" ] || { echo "the core defines names of an IR module: $clash" >&2; return 1; } + clash="$(llvm-nm-16 --undefined-only "$w/core-s.bc" | awk '{print $NF}' | grep -E '^(klee_|__afl_)' | tr '\n' ' ' || true)" + [ -z "$clash" ] || { echo "the core references generator intrinsics: $clash" >&2; return 1; } + # The AFL++ generator's longjmp (nondet_cancel) must never cross a Rust + # frame (D7 item 5): the Rust part references neither the jump nor any IR + # function that reaches it, nor the program (test_gen_ll.py + # test_only_the_assume_entry_points_reach_longjmp checks the IR side). + clash="$(llvm-nm-16 --undefined-only "$w/core-s.bc" | awk '{print $NF}' \ + | grep -xE '(_|__|sig)?longjmp|__longjmp_chk|_?setjmp|sigsetjmp|__sigsetjmp|nondet_assume|nondet_cancel|map2check_(crab_)?assume(_loop)?|__map2check_main__|main' \ + | tr '\n' ' ' || true)" + [ -z "$clash" ] || { echo "the Rust core references a longjmp path or the program: $clash" >&2; return 1; } + + llvm-link-16 "$w/core-s.bc" "$w/irbc/Map2CheckFunctions-glue.bc" -o "$out/Map2CheckFunctions.bc" + llvm-dis-16 "$out/Map2CheckFunctions.bc" -o "$w/final.ll" + # Every function left is reachable from the exported API: no freeze. + check_core "$w/final.ll" || return 1 + python3 -I "$repo/scripts/runtime-ir/check_no_freeze.py" "$w/final.ll" >/dev/null \ + || { echo "the core has a freeze KLEE 3.1 cannot execute (fix the Rust source)" >&2; return 1; } + for f in "$w"/irbc/*.bc; do + m="$(basename "$f" .bc)" + [ "$m" = Map2CheckFunctions-glue ] && continue + cp "$f" "$out/$m.bc" + done + llvm-nm-16 --defined-only --extern-only "$out/Map2CheckFunctions.bc" | awk 'NF==3 {print $3}' | sort -u > "$w/core-final.txt" + local missing="" + for f in "$out"/*.bc; do + [ "$(basename "$f")" = Map2CheckFunctions.bc ] && continue + while read -r s; do + grep -qx "$s" "$w/core-final.txt" || missing+=" $(basename "$f" .bc):$s" + done < <(llvm-nm-16 --undefined-only "$f" 2>/dev/null | awk '{print $NF}' | grep -E '^__m2c_rt_' || true) + done + [ -z "$missing" ] || { echo "core entry points the IR calls but the core does not define:$missing" >&2; return 1; } + local n=0 + for m in $MODULES; do + llvm-dis-16 "$out/$m.bc" -o /dev/null + n=$((n + 1)) + done + [ "$(ls "$out"/*.bc | wc -l)" = 21 ] || { echo "expected 21 modules in $out" >&2; return 1; } + echo "build-runtime-bc: $n modules readable by llvm-dis-16" +} + +if [ "${1:-}" = --check-core ]; then + [ $# -eq 2 ] || { echo "usage: $0 --check-core " >&2; exit 2; } + check_core "$2" + exit $? +fi + +if [ "${1:-}" = --postprocess ]; then + shift + MODULES="$MODULES" postprocess "$@" + exit $? +fi + +[ $# -eq 1 ] || { echo "usage: $0 " >&2; exit 2; } +mkdir -p "$1" +OUT="$(cd "$1" && pwd)" +# holds the 21 modules and nothing else: an earlier output is +# replaced, any other file is refused rather than deleted. +others="$(cd "$OUT" && ls -A | grep -vxF -f <(for m in $MODULES; do echo "$m.bc"; done; echo .work) || true)" +[ -z "$others" ] || die "$OUT has files that are not runtime modules: $(echo $others)" +rm -f "$OUT"/*.bc +TOOLCHAIN="${KLEE_RUSTC_TOOLCHAIN:-1.72.1}" +TARGET_DIR="${CARGO_TARGET_DIR:-$ROOT/target/dropin-core}" +commit="$(sed -n 's/^commit=//p' "$ROOT/vendor/MAP2CHECK_VERSION")" +IMAGE="${IMAGE:-map2check-dev:develop-${commit:0:12}}" + +llvm_version="$(RUSTUP_TOOLCHAIN="$TOOLCHAIN" rustc -vV | sed -n 's/^LLVM version: //p')" +case "$llvm_version" in 16.*) ;; *) die "rustc $TOOLCHAIN uses LLVM '$llvm_version', not 16" ;; esac + +work="$OUT/.work" +rm -rf "$work"; mkdir -p "$work/ll" +deps="$TARGET_DIR/release/deps" +# Always re-emit the crate's bitcode: cargo does not rebuild a crate it +# considers up to date, and a stale module would be picked up. +RUSTUP_TOOLCHAIN="$TOOLCHAIN" CARGO_TARGET_DIR="$TARGET_DIR" \ + cargo clean --release -p map2check-klee-runtime --manifest-path "$ROOT/klee/Cargo.toml" >/dev/null 2>&1 \ + || die "cargo clean failed" +[ -d "$deps" ] && find "$deps" -maxdepth 1 -name 'map2check-*.bc' -delete +RUSTUP_TOOLCHAIN="$TOOLCHAIN" CARGO_TARGET_DIR="$TARGET_DIR" \ + cargo rustc --quiet --locked --release --features dropin,wasm --lib \ + --manifest-path "$ROOT/klee/Cargo.toml" -- --emit=llvm-bc || die "cargo rustc failed" +mapfile -t bcs < <(find "$deps" -maxdepth 1 -name 'map2check-*.bc') +[ "${#bcs[@]}" -eq 1 ] || die "expected one map2check-*.bc in $deps, found ${#bcs[@]}" +cp "${bcs[0]}" "$work/core.bc" + +python3 -I "$ROOT/scripts/runtime-ir/gen_ll.py" "$ROOT/docs/runtime-abi/inventory.tsv" "$work/ll" +tail -n +2 "$ROOT/docs/runtime-abi/inventory.tsv" | cut -f1 > "$work/abi.txt" + +host_llvm=1 +for t in opt-16 llvm-as-16 llvm-dis-16 llvm-link-16 llvm-nm-16; do + command -v "$t" >/dev/null 2>&1 || host_llvm=0 +done +if [ "$host_llvm" = 1 ]; then + postprocess "$ROOT" "$work" "$OUT" || die "post-processing failed" +else + command -v docker >/dev/null 2>&1 || die "no LLVM 16 tools on PATH and no docker" + docker image inspect "$IMAGE" >/dev/null 2>&1 || die "no LLVM 16 tools on PATH and no image $IMAGE" + docker run --rm --network none --user "$(id -u):$(id -g)" \ + -v "$ROOT:/repo:ro" -v "$OUT:/out" "$IMAGE" \ + /repo/scripts/build-runtime-bc.sh --postprocess /repo /out/.work /out \ + || die "post-processing failed (in $IMAGE)" +fi +rm -rf "$work" +[ "$(cd "$OUT" && ls -A | sort)" = "$(for m in $MODULES; do echo "$m.bc"; done | sort)" ] \ + || die "$OUT does not hold exactly the 21 modules" +echo "build-runtime-bc: wrote the 21 modules to $OUT (rustc $TOOLCHAIN, LLVM $llvm_version)" diff --git a/scripts/check-export-set.sh b/scripts/check-export-set.sh new file mode 100755 index 0000000..d162485 --- /dev/null +++ b/scripts/check-export-set.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# Guard (Task 2.10, D6): the C ABI the staticlib exports is the Map2Check +# runtime ABI and nothing else. The set of names the library's own objects +# define with external linkage (nm --defined-only --extern-only, functions +# and data) must be exactly +# (a) the names of docs/runtime-abi/inventory.tsv owned by the core +# modules (Map2CheckFunctions, TrackBBLog, NonDetLog, +# PropertyGenerator), minus the `c-only` names of export-policy.tsv +# (SD-22) and minus every name a generated IR module defines +# (scripts/runtime-ir/gen_ll.py, the glue included); +# (b) the core entry points `__m2c_rt_*` (rust-only in export-policy.tsv), +# of which every one an IR module calls must be there. +# Only the members of the crate itself count (archive members named +# `map2check-.*.o`): std, core, compiler_builtins and the dependencies +# are the Rust toolchain's, not the library's ABI. In those members the +# toolchain also emits symbols that are not C ABI and are skipped: mangled +# Rust names (`_ZN...`, `_R...`), the allocator shim (`__rust_*`, +# `__rustc*`) and the personality references (`DW.ref.*`). +# +# Usage: check-export-set.sh [--lib ] [--inventory F] +# [--policy F] [--ll-dir D] [--without-wasm] +# default lib: target/release/libmap2check.a +# default ll-dir: every module generated here into a temporary directory +# --without-wasm the lib is built without the `wasm` feature: the +# __m2c_rt_wasm_* that WasmRuntimeStubs calls are not +# required (and must be absent) +# Exit 1 with the names missing from or extra in the staticlib. +set -euo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$HERE/.." && pwd)" +export LC_ALL=C +LIB="$ROOT/target/release/libmap2check.a" +INV="$ROOT/docs/runtime-abi/inventory.tsv" +POL="$ROOT/docs/runtime-abi/export-policy.tsv" +LLDIR="" +WASM=1 +while [ $# -gt 0 ]; do + case "$1" in + --lib) LIB="$2"; shift 2 ;; + --inventory) INV="$2"; shift 2 ;; + --policy) POL="$2"; shift 2 ;; + --ll-dir) LLDIR="$2"; shift 2 ;; + --without-wasm) WASM=0; shift ;; + *) echo "unknown option: $1" >&2; exit 2 ;; + esac +done +for f in "$LIB" "$INV" "$POL"; do + [ -f "$f" ] || { echo "missing $f" >&2; exit 2; } +done +T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT +if [ -z "$LLDIR" ]; then + LLDIR="$T/ll" + python3 -I "$ROOT/scripts/runtime-ir/gen_ll.py" "$INV" "$LLDIR" +fi +ls "$LLDIR"/*.ll >/dev/null 2>&1 || { echo "no .ll in $LLDIR" >&2; exit 2; } + +# What the IR modules define and the __m2c_rt_* they call. +cat "$LLDIR"/*.ll | sed -nE 's/^define [^@]*@"?([A-Za-z0-9_.$]+)"?\(.*/\1/p; + s/^@"?([A-Za-z0-9_.$]+)"? = (dso_local |weak |linkonce |common |hidden )*(global|constant|common|weak|linkonce)[ ].*/\1/p' \ + | sort -u > "$T/ir-defined" +[ -s "$T/ir-defined" ] || { echo "FAIL: no definition found in $LLDIR/*.ll" >&2; exit 2; } +cat "$LLDIR"/*.ll | grep -oE '@__m2c_rt_[A-Za-z0-9_]+' | tr -d '@' | sort -u > "$T/ir-calls" + +# (a) +awk -F'\t' 'NR>1 && $2=="c-only" {print $1}' "$POL" | sort -u > "$T/conly" +awk -F'\t' 'NR>1 { n=split($5, m, "+"); for (i=1;i<=n;i++) + if (m[i] ~ /^(Map2CheckFunctions|TrackBBLog|NonDetLog|PropertyGenerator)\.bc$/) { print $1; break } }' "$INV" \ + | sort -u | comm -23 - "$T/conly" | comm -23 - "$T/ir-defined" > "$T/a" +[ -s "$T/a" ] || { echo "FAIL: (a) is empty; is $INV the inventory?" >&2; exit 2; } + +# The staticlib: external definitions of the crate's own members. +nm -A --defined-only --extern-only "$LIB" 2>/dev/null > "$T/nm" || true +[ -s "$T/nm" ] || { echo "FAIL: nm found no symbol in $LIB" >&2; exit 2; } +awk 'NF==3 { k=split($1, p, ":"); m=p[k-1]; if (m ~ /^map2check-[0-9a-f]+\./) print $3 }' "$T/nm" \ + | grep -vE '^(_ZN|_R[A-Z0-9_]|__rust_|__rustc|DW\.ref\.)' | sort -u > "$T/exported" || true +[ -s "$T/exported" ] || { echo "FAIL: no member map2check-.*.o with C exports in $LIB" >&2; exit 2; } + +grep -E '^__m2c_rt_' "$T/exported" > "$T/b" || true +if [ "$WASM" = 0 ]; then + grep -vE '^__m2c_rt_wasm_' "$T/ir-calls" > "$T/ir-calls.nw" || true + mv "$T/ir-calls.nw" "$T/ir-calls" + wasm="$(grep -E '^__m2c_rt_wasm_' "$T/b" || true)" + if [ -n "$wasm" ]; then + echo "FAIL: --without-wasm, but $(basename "$LIB") exports __m2c_rt_wasm_* entries" >&2; exit 1 + fi +fi +sort -u "$T/a" "$T/b" > "$T/want" +rc=0 +extra="$(comm -13 "$T/want" "$T/exported")" +missing="$(comm -23 "$T/a" "$T/exported")" +uncalled="$(comm -23 "$T/ir-calls" "$T/b")" +if [ -n "$extra" ]; then + echo "FAIL: $(basename "$LIB") exports names outside the C runtime ABI ((a) and __m2c_rt_*):" >&2 + printf ' %s\n' $extra >&2; rc=1 +fi +if [ -n "$missing" ]; then + echo "FAIL: $(basename "$LIB") does not export core names of the C runtime ABI (a):" >&2 + printf ' %s\n' $missing >&2; rc=1 +fi +if [ -n "$uncalled" ]; then + echo "FAIL: $(basename "$LIB") does not export core entry points the IR modules call:" >&2 + printf ' %s\n' $uncalled >&2; rc=1 +fi +[ "$rc" = 0 ] && echo "OK: $(basename "$LIB") exports exactly the C runtime ABI: $(wc -l < "$T/a") core names (a) and $(wc -l < "$T/b") __m2c_rt_* entries (b)" +exit $rc diff --git a/scripts/cutover/d1-remeasure/assume.c b/scripts/cutover/d1-remeasure/assume.c index 4642ce6..ff59093 100644 --- a/scripts/cutover/d1-remeasure/assume.c +++ b/scripts/cutover/d1-remeasure/assume.c @@ -1,12 +1,12 @@ /* Assume-heavy program for the D1 re-measurement (Task 2.5b): most inputs * are rejected by an assume, and the bug sits behind a magic value. */ #include -extern void map2check_init(void); +extern void map2check_init(int isSvComp); extern void map2check_success(void); extern int __VERIFIER_nondet_int(void); extern void __VERIFIER_assume(int); int main(void) { - map2check_init(); + map2check_init(0); int x = __VERIFIER_nondet_int(); __VERIFIER_assume(x > 100); __VERIFIER_assume(x < 200); diff --git a/scripts/cutover/d1-remeasure/prog42.c b/scripts/cutover/d1-remeasure/prog42.c index 5278b2b..4ed5b08 100644 --- a/scripts/cutover/d1-remeasure/prog42.c +++ b/scripts/cutover/d1-remeasure/prog42.c @@ -1,11 +1,11 @@ /* Variant of prog.c for the fuzzers: x == 42 aborts, so reaching it is a * crash afl-fuzz can count. Same calls otherwise. */ #include -extern void map2check_init(void); +extern void map2check_init(int isSvComp); extern void map2check_success(void); extern int __VERIFIER_nondet_int(void); int main(void) { - map2check_init(); + map2check_init(0); int x = __VERIFIER_nondet_int(); if (x == 42) abort(); map2check_success(); diff --git a/scripts/fetch-map2check.sh b/scripts/fetch-map2check.sh new file mode 100755 index 0000000..28e6af7 --- /dev/null +++ b/scripts/fetch-map2check.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# Baixa o branch develop do Map2Check como cópia local sem versionamento. +# +# Uso: scripts/fetch-map2check.sh [--with-submodules] [] +# padrão: vendor/map2check (precisa ficar dentro deste repositório). +# MAP2CHECK_URL: padrão https://github.com/hbgit/Map2Check +# Escreve MAP2CHECK_VERSION (chave=valor) ao lado de . +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +URL="${MAP2CHECK_URL:-https://github.com/hbgit/Map2Check}" +BRANCH=develop + +submodules=0 +dest="" +for arg in "$@"; do + case "$arg" in + --with-submodules) submodules=1 ;; + -*) echo "uso: $0 [--with-submodules] []" >&2; exit 2 ;; + *) [ -z "$dest" ] || { echo "destino duplicado" >&2; exit 2; }; dest="$arg" ;; + esac +done +dest="${dest:-$ROOT/vendor/map2check}" + +DEST="$(realpath -m -- "$dest")" +case "$DEST" in + "$ROOT"/*) ;; + *) echo "destino fora do repositório: $DEST" >&2; exit 1 ;; +esac +[ "$DEST" != "$ROOT/.git" ] && case "$DEST" in "$ROOT/.git"/*) echo "destino dentro de .git" >&2; exit 1 ;; esac + +PARENT="$(dirname "$DEST")" +VERSION_FILE="$PARENT/MAP2CHECK_VERSION" +mkdir -p "$PARENT" +TMP="$(mktemp -d "$PARENT/.fetch-map2check.XXXXXX")" +trap 'rm -rf "$TMP"' EXIT + +# file:// precisa liberar o protocolo para submódulos; https não é afetado. +gitopts=() +case "$URL" in file://*) gitopts=(-c protocol.file.allow=always) ;; esac +cloneopts=(--depth 1 --branch "$BRANCH") +[ "$submodules" -eq 1 ] && cloneopts+=(--recurse-submodules --shallow-submodules) + +git "${gitopts[@]}" clone -q "${cloneopts[@]}" -- "$URL" "$TMP/src" + +commit="$(git -C "$TMP/src" rev-parse HEAD)" +commit_subject="$(git -C "$TMP/src" log -1 --format=%s)" +fetched="$(date -u +%Y-%m-%dT%H:%M:%SZ)" +commit_date="$(TZ=UTC git -C "$TMP/src" log -1 --format=%cd --date=format-local:%Y-%m-%dT%H:%M:%SZ)" + +# .git pode ser diretório (repositório) ou arquivo (submódulo) +find "$TMP/src" -name .git -prune -exec rm -rf {} + + +rm -rf "$DEST" +mv "$TMP/src" "$DEST" + +{ + echo "url=$URL" + echo "branch=$BRANCH" + echo "commit=$commit" + echo "commit_date=$commit_date" + echo "commit_subject=$commit_subject" + echo "fetched=$fetched" +} > "$TMP/version" +mv "$TMP/version" "$VERSION_FILE" +echo "Map2Check $BRANCH @ $commit em $DEST" diff --git a/scripts/it/Dockerfile.time b/scripts/it/Dockerfile.time new file mode 100644 index 0000000..36ae580 --- /dev/null +++ b/scripts/it/Dockerfile.time @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-3.0-only +# Imagem do Map2Check develop acrescida de /usr/bin/time (GNU time), usada pelas +# medições de tempo e pico de RSS dos testes de integração. +ARG BASE +FROM ${BASE} +USER root +RUN apt-get update \ + && apt-get install -y --no-install-recommends time \ + && rm -rf /var/lib/apt/lists/* +USER map2check diff --git a/scripts/it/build-map2check.sh b/scripts/it/build-map2check.sh new file mode 100755 index 0000000..f4680c6 --- /dev/null +++ b/scripts/it/build-map2check.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# Constrói o Map2Check develop a partir da cópia somente leitura em vendor/map2check. +# +# Uso: scripts/it/build-map2check.sh [--image-only] +# Imagens: map2check-dev:develop- (vendor/map2check/Dockerfile.dev) e +# map2check-dev:develop--time (+ /usr/bin/time, scripts/it/Dockerfile.time). +# Build: target/map2check-it/build Instalação: target/map2check-it/c +# A cópia é montada com :ro; nada é escrito em vendor/map2check. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +VENDOR="$ROOT/vendor/map2check" +OUT="$ROOT/target/map2check-it" + +image_only=0 +case "${1:-}" in + "") ;; + --image-only) image_only=1 ;; + *) echo "uso: $0 [--image-only]" >&2; exit 2 ;; +esac + +commit="$(sed -n 's/^commit=//p' "$ROOT/vendor/MAP2CHECK_VERSION")" +[ ${#commit} -ge 12 ] || { echo "commit ausente em vendor/MAP2CHECK_VERSION" >&2; exit 1; } +commit12="${commit:0:12}" +BASE="map2check-dev:develop-${commit12}" +TIME_IMG="${BASE}-time" + +if ! docker image inspect "$BASE" >/dev/null 2>&1; then + docker build -t "$BASE" -f "$VENDOR/Dockerfile.dev" "$VENDOR" +fi +if ! docker image inspect "$TIME_IMG" >/dev/null 2>&1; then + docker build -t "$TIME_IMG" --build-arg "BASE=$BASE" -f "$ROOT/scripts/it/Dockerfile.time" "$ROOT/scripts/it" +fi +[ "$image_only" -eq 0 ] || exit 0 + +mkdir -p "$OUT/home" +rm -rf "$OUT/build" "$OUT/c" +docker run --rm --user "$(id -u):$(id -g)" -e HOME=/out/home \ + -v "$VENDOR:/src:ro" -v "$OUT:/out" "$BASE" bash -euc ' + cmake -S /src -B /out/build -G Ninja \ + -DLLVM_DIR=/usr/lib/llvm-16/lib/cmake/llvm \ + -DCMAKE_INSTALL_PREFIX=/out/c + ninja -C /out/build + cmake --install /out/build --prefix /out/c + # O Map2Check instala o binário na raiz do prefixo (release/map2check); + # bin/map2check é o caminho acordado com os testes de integração. + ln -sf ../map2check /out/c/bin/map2check + # Runtime do KLEE e diretório de recursos do clang não têm regra de + # instalação; o CI do próprio Map2Check (.github/workflows/ci.yml, e2e) + # cria estes dois links. Sem lib/klee/runtime o KLEE não carrega + # klee-uclibc.bca e todo run symex/híbrido termina UNKNOWN. Os alvos + # existem dentro da imagem, onde o Map2Check roda. + mkdir -p /out/c/lib/klee + ln -sfn /opt/klee/lib/klee/runtime /out/c/lib/klee/runtime + ln -sfn /usr/lib/llvm-16/lib/clang /out/c/lib/clang + ' diff --git a/scripts/it/check-link-matrix.sh b/scripts/it/check-link-matrix.sh new file mode 100755 index 0000000..a072f55 --- /dev/null +++ b/scripts/it/check-link-matrix.sh @@ -0,0 +1,283 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# Matriz de link do runtime: monta as 120 combinações de módulos que +# Caller::linkLLVM (vendor/map2check/modules/frontend/caller.cpp) passa ao +# llvm-link e confere cada uma sobre um programa de fumaça instrumentado. +# +# Uso: scripts/it/check-link-matrix.sh [--ref [--policy ]] +# diretório com os 21 .bc do runtime (p.ex. +# target/map2check-it/c/lib, ou a saída do build Rust) +# --ref liga também a combinação com os .bc de (o runtime +# C) e exige paridade dos símbolos externos DEFINIDOS +# (llvm-nm-16 --defined-only --extern-only) entre as duas, salvo +# os listados na política (critério 4) +# --policy política de exportação (padrão docs/runtime-abi/export-policy.tsv): +# TSV "symbol side reason"; symbol é um nome ou um glob do bash, +# side é c-only (definido só pelo C) ou rust-only (só pelo +# ) +# +# Combinações (2 x 5 x 3 x 2 x 2 = 120), na ordem do linkLLVM: +# -output.bc Map2CheckFunctions TrackBBLog NonDetLog PropertyGenerator +# container array -> ContainerRealloc | btree -> ContainerBTree BTree +# mode memtrack -> AnalysisModeMemtrack AllocationLog ListLog HeapLog +# memcleanup -> AnalysisModeMemcleanup AllocationLog ListLog HeapLog +# overflow -> AnalysisModeOverflow +# assert -> AnalysisModeAssert +# none -> AnalysisModeNone (alcance e cover-branches) +# gen none -> NonDetGeneratorNone | symex -> NonDetGeneratorKlee +# afl -> NonDetGeneratorAFL +# wasm 1 -> WasmRuntimeStubs +# witness 1 -> WitnessGeneration (o -witness-result.bc) +# 0 -> WitnessGenerationNone (o -result.bc) +# +# Critérios de cada combinação (todos dentro da imagem do Map2Check): +# 1. llvm-link-16 termina sem erro (p.ex. símbolo definido em dois módulos); +# 2. llvm-nm-16 --undefined-only do resultado contido na lista permitida: +# símbolos definidos por libc.so.6/libm.so.6 da imagem, mais klee_* +# para symex, __afl_* para afl e wasm_rt_* com wasm. Referências weak +# contam como as fortes, com uma exceção: __dso_handle é aceito só como +# referência WEAK (o std do Rust o referencia assim, para os destrutores +# de thread_local; o crtbegin.o de todo executável o define); +# 3. gen none: link nativo com clang-16; gen afl: link nativo com +# afl-clang-fast -g -O2 ... -lm, como Caller::applyNonDetGenerator; +# 4. só com --ref: mesmos símbolos externos definidos que a combinação do +# C, fora da política. FAIL "parity: missing a b; extra c d". +# +# Saída: uma linha por combinação, +# PASS container= mode= gen= wasm=<0|1> witness=<0|1> +# FAIL container=... witness=<0|1> : +# e por último "matrix: PASS" (exit 0) ou "matrix: FAIL" (exit 1). Uso +# inválido ou ambiente ausente: exit 2. +# +# Programa de fumaça (Ruling R2 da Task 1.1): scripts/it/smoke/link-smoke.c, +# instrumentado pelo próprio Map2Check instalado (M2C_INSTALL) uma vez por +# modo, guardando o -output.bc em SMOKE_DIR/-output.bc. É gerado +# na primeira execução e refeito quando o fonte, a instalação ou o commit do +# develop mudam (SMOKE_DIR/stamp). +# +# Variáveis de ambiente: +# IMAGE padrão map2check-dev:develop- +# M2C_INSTALL padrão target/map2check-it/c +# SMOKE_DIR padrão target/map2check-it/smoke +# JOBS combinações em paralelo (padrão: nproc) +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +usage() { echo "uso: $0 [--ref [--policy ]] " >&2; exit 2; } +REF="" +parity=0 +POLICY="$ROOT/docs/runtime-abi/export-policy.tsv" +while [ $# -gt 1 ]; do + case "$1" in + --ref) [ $# -ge 3 ] || usage; [ -d "$2" ] || { echo "$2: diretório inexistente" >&2; exit 2; } + REF="$(cd "$2" && pwd)"; parity=1; shift 2 ;; + --policy) [ $# -ge 3 ] || usage; [ -f "$2" ] || { echo "$2: arquivo inexistente" >&2; exit 2; } + POLICY="$(cd "$(dirname "$2")" && pwd)/$(basename "$2")"; shift 2 ;; + *) usage ;; + esac +done +[ $# -eq 1 ] || usage +[ -d "$1" ] || { echo "$1: diretório inexistente" >&2; exit 2; } +LIB="$(cd "$1" && pwd)" +if [ "$parity" = 1 ]; then + [ -f "$POLICY" ] || { echo "$POLICY: política inexistente" >&2; exit 2; } +else + REF="$LIB" # montado mas não usado +fi + +commit="$(sed -n 's/^commit=//p' "$ROOT/vendor/MAP2CHECK_VERSION")" +IMAGE="${IMAGE:-map2check-dev:develop-${commit:0:12}}" +M2C_INSTALL="${M2C_INSTALL:-$ROOT/target/map2check-it/c}" +SMOKE_DIR="${SMOKE_DIR:-$ROOT/target/map2check-it/smoke}" +SMOKE_SRC="$ROOT/scripts/it/smoke/link-smoke.c" +JOBS="${JOBS:-$(nproc)}" +MODES="memtrack memcleanup overflow assert none" + +command -v docker >/dev/null 2>&1 || { echo "docker ausente" >&2; exit 2; } +docker image inspect "$IMAGE" >/dev/null 2>&1 \ + || { echo "imagem $IMAGE ausente (rode scripts/it/build-map2check.sh --image-only)" >&2; exit 2; } + +mode_flags() { + case "$1" in + memtrack) echo "--memtrack" ;; + memcleanup) echo "--memcleanup-property" ;; + overflow) echo "--check-overflow" ;; + assert) echo "--check-asserts" ;; + none) echo "--target-function --target-function-name reach_error" ;; + esac +} + +# --- programa de fumaça, um -output.bc por modo ----------------------- +stamp="$(cat "$SMOKE_SRC" "$M2C_INSTALL/bin/map2check" | sha256sum | cut -d' ' -f1) $commit" +need_smoke=0 +[ "$(cat "$SMOKE_DIR/stamp" 2>/dev/null || true)" = "$stamp" ] || need_smoke=1 +for m in $MODES; do [ -s "$SMOKE_DIR/$m-output.bc" ] || need_smoke=1; done +if [ "$need_smoke" -eq 1 ]; then + [ -x "$M2C_INSTALL/bin/map2check" ] \ + || { echo "$M2C_INSTALL/bin/map2check ausente (rode scripts/it/build-map2check.sh)" >&2; exit 2; } + echo "check-link-matrix: instrumentando $SMOKE_SRC com $M2C_INSTALL" >&2 + rm -rf "$SMOKE_DIR" + mkdir -p "$SMOKE_DIR/work" + cp "$SMOKE_SRC" "$SMOKE_DIR/work/link-smoke.c" + for m in $MODES; do + # O gerador não muda o -output.bc (Caller::callPass não depende dele); + # symex só porque é o que termina sozinho. + docker run --rm --network none --user "$(id -u):$(id -g)" -e HOME=/work \ + -v "$M2C_INSTALL:/opt/m2c:ro" -v "$SMOKE_DIR/work:/work" -w /work "$IMAGE" bash -c ' + rm -rf ./*.map2check + /opt/m2c/bin/map2check --debug --nondet-generator symex --timeout 10 $1 link-smoke.c > /work/m2c.log 2>&1 || true + cp ./*.map2check/*.map2check-output.bc /work/out.bc + ' run "$(mode_flags "$m")" \ + || { echo "falha ao instrumentar o programa de fumaça no modo $m (veja $SMOKE_DIR/work/m2c.log)" >&2; exit 2; } + mv "$SMOKE_DIR/work/out.bc" "$SMOKE_DIR/$m-output.bc" + done + rm -rf "$SMOKE_DIR/work" + echo "$stamp" > "$SMOKE_DIR/stamp" +fi + +# --- a matriz --------------------------------------------------------------- +# Uma linha por combinação, na ordem do produto. +combos="" +for ct in array btree; do + for m in $MODES; do + for g in none symex afl; do + for wa in 0 1; do + for wi in 0 1; do + combos+="$ct $m $g $wa $wi"$'\n' + done + done + done + done +done + +INNER=' +set -uo pipefail +cd /tmp +{ nm -D --defined-only /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/libm.so.6 \ + | awk "{print \$NF}" | sed "s/@.*//"; } | grep -vx __dso_handle | sort -u > /tmp/allowed-libc.txt +# Política de exportação: globs c-only e rust-only. +C_ONLY=(); RUST_ONLY=() +if [ "$PARITY" = 1 ]; then + while IFS=$'"'"'\t'"'"' read -r sym side _; do + case "$side" in c-only) C_ONLY+=("$sym") ;; rust-only) RUST_ONLY+=("$sym") ;; esac + done < <(tail -n +2 /policy.tsv) +fi +export C_ONLY_S="${C_ONLY[*]:-}" RUST_ONLY_S="${RUST_ONLY[*]:-}" +covered() { # + local s="$1" g; shift + for g in "$@"; do + # shellcheck disable=SC2053 + [[ $s == $g ]] && return 0 + done + return 1 +} +export -f covered +modules() { # + local L="$1" ct="$2" m="$3" g="$4" wa="$5" wi="$6" mods + mods="$L/Map2CheckFunctions.bc $L/TrackBBLog.bc $L/NonDetLog.bc $L/PropertyGenerator.bc" + case "$ct" in + array) mods+=" $L/ContainerRealloc.bc" ;; + btree) mods+=" $L/ContainerBTree.bc $L/BTree.bc" ;; + esac + case "$m" in + memtrack) mods+=" $L/AnalysisModeMemtrack.bc $L/AllocationLog.bc $L/ListLog.bc $L/HeapLog.bc" ;; + memcleanup) mods+=" $L/AnalysisModeMemcleanup.bc $L/AllocationLog.bc $L/ListLog.bc $L/HeapLog.bc" ;; + overflow) mods+=" $L/AnalysisModeOverflow.bc" ;; + assert) mods+=" $L/AnalysisModeAssert.bc" ;; + none) mods+=" $L/AnalysisModeNone.bc" ;; + esac + case "$g" in + none) mods+=" $L/NonDetGeneratorNone.bc" ;; + symex) mods+=" $L/NonDetGeneratorKlee.bc" ;; + afl) mods+=" $L/NonDetGeneratorAFL.bc" ;; + esac + [ "$wa" = 1 ] && mods+=" $L/WasmRuntimeStubs.bc" + if [ "$wi" = 1 ]; then mods+=" $L/WitnessGeneration.bc"; else mods+=" $L/WitnessGenerationNone.bc"; fi + echo "$mods" +} +export -f modules +one() { + local idx="$1" ct="$2" m="$3" g="$4" wa="$5" wi="$6" d mods out err und pat + d="/tmp/c$idx"; mkdir -p "$d" + out="$d/out.txt" + local name="container=$ct mode=$m gen=$g wasm=$wa witness=$wi" + mods="/smoke/$m-output.bc $(modules /lib-dir "$ct" "$m" "$g" "$wa" "$wi")" + # shellcheck disable=SC2086 + if ! llvm-link-16 $mods -o "$d/r.bc" 2> "$d/link.err"; then + err="$(grep -m1 "error" "$d/link.err" || head -n1 "$d/link.err")" + echo "FAIL $name llvm-link: $err" > "$out"; return + fi + pat="^$" + case "$g" in symex) pat="$pat|^klee_" ;; afl) pat="$pat|^__afl_" ;; esac + [ "$wa" = 1 ] && pat="$pat|^wasm_rt_" + # __dso_handle só como referência weak ("w"); forte cai na regra geral. + und="$(llvm-nm-16 --undefined-only "$d/r.bc" | awk "!(\$1 == \"w\" && \$2 == \"__dso_handle\") {print \$NF}" \ + | sort -u | comm -23 - /tmp/allowed-libc.txt | grep -Ev "$pat" | tr "\n" " " | sed "s/ $//")" + if [ -n "$und" ]; then + echo "FAIL $name undefined: $und" > "$out"; return + fi + if [ "$PARITY" = 1 ]; then + local refmods missing="" extra="" sym + refmods="/smoke/$m-output.bc $(modules /ref-dir "$ct" "$m" "$g" "$wa" "$wi")" + # shellcheck disable=SC2086 + if ! llvm-link-16 $refmods -o "$d/ref.bc" 2> "$d/ref.err"; then + echo "FAIL $name parity: a combinação de referência não liga: $(grep -m1 error "$d/ref.err")" > "$out"; return + fi + llvm-nm-16 --defined-only --extern-only "$d/r.bc" | awk "{print \$NF}" | sort -u > "$d/def.txt" + llvm-nm-16 --defined-only --extern-only "$d/ref.bc" | awk "{print \$NF}" | sort -u > "$d/refdef.txt" + read -r -a cg <<< "$C_ONLY_S"; read -r -a rg <<< "$RUST_ONLY_S" + while read -r sym; do + [ -z "$sym" ] && continue + covered "$sym" "${cg[@]}" || missing+=" $sym" + done < <(comm -13 "$d/def.txt" "$d/refdef.txt") + while read -r sym; do + [ -z "$sym" ] && continue + covered "$sym" "${rg[@]}" || extra+=" $sym" + done < <(comm -23 "$d/def.txt" "$d/refdef.txt") + if [ -n "$missing$extra" ]; then + local msg="parity:" + [ -n "$missing" ] && msg+=" missing$missing" + [ -n "$extra" ] && { [ -n "$missing" ] && msg+=";"; msg+=" extra$extra"; } + echo "FAIL $name $msg" > "$out"; return + fi + fi + case "$g" in + none) + if ! clang-16 -g "$d/r.bc" -lm -o "$d/a.out" > "$d/native.err" 2>&1; then + err="$(grep -m1 -E "error|undefined reference" "$d/native.err" || head -n1 "$d/native.err")" + echo "FAIL $name native-link(clang-16): $err" > "$out"; return + fi ;; + afl) + if ! AFL_QUIET=1 afl-clang-fast -g -O2 "$d/r.bc" -o "$d/a.out" -lm > "$d/native.err" 2>&1; then + err="$(grep -m1 -E "error|undefined reference" "$d/native.err" || head -n1 "$d/native.err")" + echo "FAIL $name native-link(afl-clang-fast): $err" > "$out"; return + fi ;; + esac + echo "PASS $name" > "$out" + rm -f "$d/r.bc" "$d/a.out" +} +export -f one +nl -ba -w1 -s" " /combos.txt | xargs -P "$JOBS" -L1 bash -c "one \"\$@\"" one +i=1 +while [ -f "/tmp/c$i/out.txt" ]; do cat "/tmp/c$i/out.txt"; i=$((i + 1)); done +' + +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +printf '%s' "$combos" > "$tmp/combos.txt" +[ "$parity" = 1 ] || POLICY="$tmp/combos.txt" # montado mas não lido +docker run --rm --network none --user "$(id -u):$(id -g)" -e HOME=/tmp -e JOBS="$JOBS" -e PARITY="$parity" \ + -v "$LIB:/lib-dir:ro" -v "$REF:/ref-dir:ro" -v "$POLICY:/policy.tsv:ro" \ + -v "$SMOKE_DIR:/smoke:ro" -v "$tmp/combos.txt:/combos.txt:ro" \ + "$IMAGE" bash -c "$INNER" > "$tmp/result.txt" + +cat "$tmp/result.txt" +total="$(grep -cE '^(PASS|FAIL) ' "$tmp/result.txt" || true)" +passed="$(grep -c '^PASS ' "$tmp/result.txt" || true)" +if [ "$total" -eq 120 ] && [ "$passed" -eq 120 ]; then + echo "matrix: PASS" + exit 0 +fi +[ "$total" -eq 120 ] || echo "check-link-matrix: $total resultados, esperados 120" >&2 +echo "matrix: FAIL" +exit 1 diff --git a/scripts/it/compare_baselines.py b/scripts/it/compare_baselines.py new file mode 100644 index 0000000..f0f686b --- /dev/null +++ b/scripts/it/compare_baselines.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-only +"""Gate de comparação entre duas linhas de base do corpus (C contra Rust). + +Uso (da raiz do repositório): + + python3 -I scripts/it/compare_baselines.py \\ + --deltas docs/cutover/deltas-aceitos.tsv \\ + [--semantic-deltas docs/semantic-deltas.md] [--corpus ] + +Entradas: TSVs de 11 colunas produzidos por scripts/it/run-corpus.sh +(task prp gen rep verdict tag line function witness wall_s maxrss_kb). +Os vereditos esperados vêm dos .yml do corpus (--corpus; padrão: a cópia +somente leitura em vendor/map2check), não do TSV: task é o caminho do .yml +relativo ao corpus sem a extensão e prp é o nome do .prp sem a extensão. +Corpus ausente, sem nenhum expected_verdict, ou chave (task, prp) dos TSVs +sem expectativa no corpus: erro, código de saída 2 (o critério `expected` +nunca passa por vacuidade). + +Agregação: cada chave (task, prp, gen) é reduzida pela moda das repetições +(veredito; witness; (tag, line, function) entre as repetições FAILED). Empate +na moda: vence o menor valor em ordem lexicográfica, para o resultado não +depender da ordem das linhas. Tempo e RSS: mediana das repetições por chave; +as razões rust/c por chave entram numa mediana global. + +Critérios (uma linha cada, `: PASS|FAIL `): + keys mesmo conjunto de chaves nos dois TSVs + reps mesmo número de repetições por chave nos dois TSVs + verdict veredito por moda igual + failed-details tag, line e function iguais quando ambos dão FAILED + witness witness presente onde o C gerou um + expected acertos contra expected_verdict, por gerador, não diminuem + deltas toda linha de deltas-aceitos.tsv cita um SD-xx existente + wall mediana de wall_rust/wall_c <= 1,10; em afl e hybrid cada + wall_rust vira max(wall_rust - 2,0 s, 0) antes da razão + (custo fixo de compilação, decisão de 2026-10-27); symex + sem desconto. A linha imprime o desconto e a mediana bruta. + timeouts contagem de TIMEOUT (vereditos por moda) por gerador não aumenta + rss mediana de maxrss_rust/maxrss_c <= 1,36 +Divergências em verdict/failed-details/witness só passam com uma linha em +deltas-aceitos.tsv (task prp gen field c_value rust_value sd_ref) cujo sd_ref +existe como título `SD-xx` em docs/semantic-deltas.md. Uma chave isolada com +razão > 1,5 gera só uma linha `warning:` (em afl e hybrid, sobre a razão de +wall com desconto). Última linha: `gate: PASS` ou +`gate: FAIL`; código de saída 0 ou 1. +""" + +import argparse +import os +import re +import statistics +import sys +from collections import Counter + +COLUMNS = ["task", "prp", "gen", "rep", "verdict", "tag", "line", "function", + "witness", "wall_s", "maxrss_kb"] +DELTA_COLUMNS = ["task", "prp", "gen", "field", "c_value", "rust_value", "sd_ref"] +WALL_MAX = 1.10 +RSS_MAX = 1.36 +WARN_RATIO = 1.5 +# Custo fixo de compilação descontado de cada wall_rust em afl e hybrid: as três +# compilações do núcleo pelo afl-clang-fast no frontend (docs/cutover/decisoes.md, +# "Critério de tempo do gate para afl e híbrido", aprovado em 2026-10-27). +COMPILE_ALLOWANCE_S = 2.0 +ALLOWANCE_GENERATORS = frozenset({"afl", "hybrid"}) +EPS = 1e-9 + +ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..")) +DEFAULT_SEMANTIC = os.path.join(ROOT, "docs", "semantic-deltas.md") +DEFAULT_CORPUS = os.path.join(ROOT, "vendor", "map2check", "tests", "regression_test", + "test_cases", "sv-benchmarks") + +# Subpropriedade de valid-memsafety -> tag impressa pelo Map2Check. +SUBPROPERTY_TAG = { + "valid-free": "FALSE-FREE", + "valid-deref": "FALSE-DEREF", + "valid-memtrack": "FALSE-MEMTRACK", + "valid-memcleanup": "FALSE-MEMCLEANUP", +} + + +def die(msg): + print("compare_baselines: " + msg, file=sys.stderr) + sys.exit(2) + + +def read_tsv(path, columns): + with open(path, encoding="utf-8") as f: + lines = [l.rstrip("\n") for l in f if l.strip()] + if not lines or lines[0].split("\t") != columns: + die("%s: cabeçalho esperado: %s" % (path, " ".join(columns))) + out = [] + for n, l in enumerate(lines[1:], start=2): + fields = l.split("\t") + if len(fields) != len(columns): + die("%s:%d: %d colunas, esperado %d" % (path, n, len(fields), len(columns))) + out.append(dict(zip(columns, fields))) + return out + + +def mode(values): + counts = Counter(values) + return sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))[0][0] + + +def allowance(gen): + return COMPILE_ALLOWANCE_S if gen in ALLOWANCE_GENERATORS else 0.0 + + +def adjusted_wall(gen, wall): + """wall_rust com o desconto de compilação do gerador (nunca negativo).""" + return max(wall - allowance(gen), 0.0) + + +def aggregate(rows): + """{(task, prp, gen): resumo por moda/mediana das repetições}.""" + groups = {} + for r in rows: + groups.setdefault((r["task"], r["prp"], r["gen"]), []).append(r) + out = {} + for key, rs in groups.items(): + failed = [(r["tag"], r["line"], r["function"]) for r in rs if r["verdict"] == "FAILED"] + tag, line, function = mode(failed) if failed else ("", "", "") + out[key] = { + "verdict": mode([r["verdict"] for r in rs]), + "tag": tag, "line": line, "function": function, + "witness": mode([r["witness"] for r in rs]), + "wall": statistics.median(float(r["wall_s"]) for r in rs), + "wall_adj": statistics.median(adjusted_wall(key[2], float(r["wall_s"])) + for r in rs), + "rss": statistics.median(float(r["maxrss_kb"]) for r in rs), + "reps": len(rs), + } + return out + + +def sd_ids(path): + with open(path, encoding="utf-8") as f: + return set(re.findall(r"^#+\s+(SD-\d+)\b", f.read(), re.M)) + + +def load_expected(corpus): + """{(task, prp): (expected_verdict, subproperty|None)} lido dos .yml.""" + out = {} + if not corpus or not os.path.isdir(corpus): + die("corpus %r não existe: sem ele não há expected_verdict " + "(use --corpus )" % corpus) + for dirpath, _, files in os.walk(corpus): + for name in files: + if not name.endswith(".yml"): + continue + path = os.path.join(dirpath, name) + task = os.path.relpath(path, corpus)[:-len(".yml")] + prp = None + with open(path, encoding="utf-8") as f: + for raw in f: + line = raw.split("#", 1)[0].strip() + m = re.match(r"-?\s*property_file:\s*(\S+)", line) + if m: + prp = os.path.basename(m.group(1).strip("'\"")) + prp = prp[:-len(".prp")] if prp.endswith(".prp") else prp + continue + m = re.match(r"expected_verdict:\s*(\S+)", line) + if m and prp: + out[(task, prp)] = (m.group(1).strip("'\"").lower(), None) + continue + m = re.match(r"subproperty:\s*(\S+)", line) + if m and prp and (task, prp) in out: + out[(task, prp)] = (out[(task, prp)][0], m.group(1).strip("'\"")) + if not out: + die("nenhum expected_verdict lido de %r (nenhum .yml de tarefa)" % corpus) + return out + + +def matches_expected(expected, verdict, tag): + want, sub = expected + if want == "true": + return verdict == "SUCCEEDED" + if want == "false": + if verdict != "FAILED": + return False + return sub is None or SUBPROPERTY_TAG.get(sub) == tag + return False + + +def main(argv=None): + ap = argparse.ArgumentParser(description=__doc__.split("\n", 1)[0]) + ap.add_argument("c_tsv") + ap.add_argument("rust_tsv") + ap.add_argument("--deltas", required=True) + ap.add_argument("--semantic-deltas", default=DEFAULT_SEMANTIC) + ap.add_argument("--corpus", default=DEFAULT_CORPUS) + a = ap.parse_args(argv) + + c = aggregate(read_tsv(a.c_tsv, COLUMNS)) + r = aggregate(read_tsv(a.rust_tsv, COLUMNS)) + deltas = read_tsv(a.deltas, DELTA_COLUMNS) + known_sd = sd_ids(a.semantic_deltas) + expected = load_expected(a.corpus) + missing = sorted({(k[0], k[1]) for k in set(c) | set(r)} - set(expected)) + if missing: + die("sem expected_verdict no corpus para: " + + ", ".join("%s %s" % m for m in missing[:10]) + + (" ..." if len(missing) > 10 else "")) + + lines, failed = [], False + + def report(name, ok, detail): + nonlocal failed + failed = failed or not ok + lines.append("%s: %s %s" % (name, "PASS" if ok else "FAIL", detail)) + + # deltas + bad_refs = [d for d in deltas if d["sd_ref"] not in known_sd] + accepted = {(d["task"], d["prp"], d["gen"], d["field"], d["c_value"], d["rust_value"]) + for d in deltas if d["sd_ref"] in known_sd} + report("deltas", not bad_refs, + "(%d linhas, sd_ref inexistente: %s)" % ( + len(deltas), ", ".join(sorted({d["sd_ref"] for d in bad_refs})) or "nenhum")) + + # keys + only_c, only_r = sorted(set(c) - set(r)), sorted(set(r) - set(c)) + report("keys", not only_c and not only_r, + "(%d C, %d Rust, só C: %d, só Rust: %d)" % (len(c), len(r), len(only_c), len(only_r))) + common = sorted(set(c) & set(r)) + rep_diff = ["%s %s %s %d->%d" % (k + (c[k]["reps"], r[k]["reps"])) + for k in common if c[k]["reps"] != r[k]["reps"]] + report("reps", not rep_diff, + "(%d chaves com contagem diferente)%s" % ( + len(rep_diff), " " + "; ".join(rep_diff[:5]) if rep_diff else "")) + + def diverge(fields): + bad, ok_acc = [], 0 + for key in common: + for field in fields(key): + cv, rv = c[key][field], r[key][field] + if cv == rv: + continue + if key + (field, cv, rv) in accepted: + ok_acc += 1 + else: + bad.append("%s %s %s %s: %r -> %r" % (key + (field, cv, rv))) + return bad, ok_acc + + def detail(bad, acc): + shown = "; ".join(bad[:5]) + ("; ..." if len(bad) > 5 else "") + return "(%d divergências, %d aceitas)%s" % (len(bad) + acc, acc, + " " + shown if bad else "") + + bad, acc = diverge(lambda k: ["verdict"]) + report("verdict", not bad, detail(bad, acc)) + + bad, acc = diverge(lambda k: ["tag", "line", "function"] + if c[k]["verdict"] == "FAILED" and r[k]["verdict"] == "FAILED" else []) + report("failed-details", not bad, detail(bad, acc)) + + bad, acc = diverge(lambda k: ["witness"] + if c[k]["witness"] == "1" and r[k]["witness"] != "1" else []) + report("witness", not bad, detail(bad, acc)) + + # expected + gens = sorted({k[2] for k in set(c) | set(r)}) + exp_parts, exp_ok = [], True + for g in gens: + def hits(agg): + return sum(1 for k, v in agg.items() if k[2] == g and (k[0], k[1]) in expected + and matches_expected(expected[(k[0], k[1])], v["verdict"], v["tag"])) + hc, hr = hits(c), hits(r) + exp_ok = exp_ok and hr >= hc + exp_parts.append("%s %d->%d" % (g, hc, hr)) + report("expected", exp_ok, "(" + ", ".join(exp_parts) + ")") + + # desempenho + def ratios(r_field, c_field): + out = [] + for key in common: + if c[key][c_field] > 0: + out.append((r[key][r_field] / c[key][c_field], key)) + return out + + def med(rs): + return statistics.median(x for x, _ in rs) if rs else 1.0 + + # wall: razão com desconto em afl/hybrid (critério) e bruta (informativa). + adj, raw = ratios("wall_adj", "wall"), ratios("wall", "wall") + m = med(adj) + per_gen = [] + for g in gens: + ga = [x for x in adj if x[1][2] == g] + gr = [x for x in raw if x[1][2] == g] + if not ga: + continue + per_gen.append("%s %.3f (bruta %.3f, desconto %.1f s)" % ( + g, med(ga), med(gr), allowance(g))) + report("wall", m <= WALL_MAX + EPS, + "(mediana %.3f, limite %.2f, %d chaves; desconto de compilação %.1f s por " + "execução em %s, symex sem desconto; mediana bruta %.3f; por gerador: %s)" % ( + m, WALL_MAX, len(adj), COMPILE_ALLOWANCE_S, + ", ".join(sorted(ALLOWANCE_GENERATORS)), med(raw), "; ".join(per_gen))) + for x, key in adj: + if x > WARN_RATIO + EPS: + note = (" (com desconto de %.1f s)" % allowance(key[2])) if allowance(key[2]) else "" + lines.append("warning: %s %s %s razão wall %.2f > %.1f%s" % ( + key + (x, WARN_RATIO, note))) + + rs = ratios("rss", "rss") + m = med(rs) + report("rss", m <= RSS_MAX + EPS, "(mediana %.3f, limite %.2f, %d chaves)" % (m, RSS_MAX, len(rs))) + for x, key in rs: + if x > WARN_RATIO + EPS: + lines.append("warning: %s %s %s razão rss %.2f > %.1f" % (key + (x, WARN_RATIO))) + + to_parts, to_ok = [], True + for g in gens: + tc = sum(1 for k, v in c.items() if k[2] == g and v["verdict"] == "TIMEOUT") + tr = sum(1 for k, v in r.items() if k[2] == g and v["verdict"] == "TIMEOUT") + to_ok = to_ok and tr <= tc + to_parts.append("%s %d->%d" % (g, tc, tr)) + report("timeouts", to_ok, "(" + ", ".join(to_parts) + ")") + + for l in lines: + print(l) + print("gate: FAIL" if failed else "gate: PASS") + return 1 if failed else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/it/overlay-install.sh b/scripts/it/overlay-install.sh new file mode 100755 index 0000000..f619f05 --- /dev/null +++ b/scripts/it/overlay-install.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# A Map2Check install that runs the drop-in runtime: a copy of a C install +# (scripts/it/build-map2check.sh) in which only lib/*.bc, the 21 runtime +# modules Caller::linkLLVM links, are replaced by the output of +# scripts/build-runtime-bc.sh. Everything else is copied with cp -a: bin/, +# the binary, the pass plugins and the symlinks lib/klee/runtime and +# lib/clang, whose targets exist only inside the Map2Check image, stay as +# they are. +# +# Usage: overlay-install.sh +# a Map2Check install whose lib/ holds the 21 C modules; +# exactly the 21 drop-in modules (other .bc are refused; other +# files such as the .a of a release package lib/ are ignored); +# created; an earlier overlay there (it has the marker +# file .map2check-overlay this script writes) is replaced, +# anything else (a C install included) is refused and left +# alone. It may not lie inside . +# The C install is never written. The overlay gets one file the C install +# does not have: .map2check-overlay, with the source install and the sha256 +# of the 21 modules. Test: scripts/it/test-overlay-install.sh. +set -euo pipefail +die() { echo "overlay-install: error: $*" >&2; exit 1; } +[ $# -eq 3 ] || { echo "usage: $0 " >&2; exit 2; } +# The 21 module names: one list for the package, its test and the overlay. +MODULES="$(tr ' +' ' ' < "$(dirname "$0")/../runtime-modules.txt")" +want="$(for m in $MODULES; do echo "$m.bc"; done | sort)" + +[ -d "$1/lib" ] || die "$1 is not a Map2Check install (no lib/)" +C="$(cd "$1" && pwd)" +[ -d "$2" ] || die "$2: no such directory" +BC="$(cd "$2" && pwd)" +[ "$(cd "$C/lib" && ls -A | grep '\.bc$' | sort)" = "$want" ] \ + || die "$C/lib does not hold exactly the 21 runtime modules" +got="$(cd "$BC" && ls -A | grep '\.bc$' | sort || true)" +[ "$got" = "$want" ] \ + || die "$BC does not hold exactly the 21 runtime modules: $(diff <(echo "$want") <(echo "$got") | grep '^[<>]' | tr '\n' ' ')" + +mkdir -p "$(dirname "$3")" +OUT="$(cd "$(dirname "$3")" && pwd)/$(basename "$3")" +case "$OUT/" in "$C/"*) die "$OUT lies inside $C" ;; esac +case "$C/" in "$OUT/"*) die "$C lies inside $OUT" ;; esac +MARKER=.map2check-overlay +if [ -e "$OUT" ] || [ -L "$OUT" ]; then + # Only a directory this script made is ever deleted: a C install has + # lib/Map2CheckFunctions.bc too, so that is no proof. + [ -d "$OUT" ] && [ ! -L "$OUT" ] && [ -f "$OUT/$MARKER" ] && [ ! -L "$OUT/$MARKER" ] \ + && head -1 "$OUT/$MARKER" | grep -q '^map2check-overlay ' \ + || die "$OUT exists and is not an earlier overlay (no $MARKER); not touched" + rm -rf "$OUT" +fi + +tmp="$OUT.tmp.$$" +trap 'rm -rf "$tmp"' EXIT +cp -a "$C" "$tmp" +for m in $MODULES; do + # Same mode as the C module it replaces; the content is the drop-in one. + cp --no-preserve=all "$BC/$m.bc" "$tmp/lib/$m.bc.new" + chmod --reference="$C/lib/$m.bc" "$tmp/lib/$m.bc.new" + mv -f "$tmp/lib/$m.bc.new" "$tmp/lib/$m.bc" +done +[ ! -e "$tmp/$MARKER" ] || die "$C has a $MARKER: it is an overlay, not a C install" +{ + echo "map2check-overlay 1" + echo "c-install $C" + echo "bc-dir $BC" + (cd "$BC" && for m in $MODULES; do sha256sum "$m.bc"; done) +} > "$tmp/$MARKER" +mv "$tmp" "$OUT" +trap - EXIT +echo "overlay-install: $OUT = $C with the 21 modules of $BC" diff --git a/scripts/it/run-corpus.sh b/scripts/it/run-corpus.sh new file mode 100755 index 0000000..025681b --- /dev/null +++ b/scripts/it/run-corpus.sh @@ -0,0 +1,195 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# Roda o frontend do Map2Check de uma instalação sobre o corpus de regressão +# SV-COMP e grava uma linha por execução num TSV de 11 colunas. +# +# Uso: scripts/it/run-corpus.sh +# instalação do Map2Check (bin/map2check + lib/), p.ex. +# target/map2check-it/c ou a cópia com os .bc do Rust. +# task prp gen rep verdict tag line function witness wall_s maxrss_kb +# +# Variáveis de ambiente: +# REPS=5 repetições de cada (task, prp, gen) +# GENERATORS="afl symex hybrid" +# afl -> --nondet-generator afl +# symex -> --nondet-generator symex +# hybrid -> sem --nondet-generator (o padrão 9.0 alterna AFL++/KLEE e +# exige --timeout, que é sempre passado) +# TIMEOUT_S=45 valor de --timeout. 45 s é o timelimit de +# tests/regression_test/xml_doc_benchexec/map2check_regression_test.xml, +# o XML do BenchExec que o próprio projeto usa para ESTE corpus (o de +# 10 s é a variante reduzida do Travis; o de 120 s é para o +# sv-benchmarks completo). +# KILL_MARGIN_S=30 o processo é morto por `timeout -k 10` em +# TIMEOUT_S + KILL_MARGIN_S segundos (75 s por padrão). --timeout só +# limita as fases de análise; compilação, instrumentação, ligação e a +# geração do witness ficam fora dele (uma execução híbrida de 45 s levou +# ~40 s de parede no total). Execução morta pelo limite externo grava +# verdict TIMEOUT (exit 124 do timeout, ou 137 após o SIGKILL). +# TASKS="" lista de tarefas (caminho do .yml relativo ao +# corpus, sem .yml); vazio = todas as 39. +# CORPUS padrão: vendor/map2check/tests/regression_test/ +# test_cases/sv-benchmarks (montado :ro) +# IMAGE padrão: map2check-dev:develop--time +# RUNS_DIR padrão: target/map2check-it/runs/- +# +# Mapeamento propriedade (.prp) -> opções do Map2Check. Segue +# utils/map2check-wrapper.py e utils/map2check-wrapper-reg-test.py do Map2Check +# (o wrapper do tool-info do BenchExec, utils/moduleBenchExec/map2check.py), +# que escolhem o modo pelo conteúdo do .prp: +# valid-memsafety (valid-free/deref/memtrack) -> --memtrack --generate-witness +# valid-memcleanup -> --memcleanup-property --generate-witness +# unreach-call (G ! call(__VERIFIER_error())) -> --target-function --generate-witness +# no-overflow -> --check-overflow --generate-witness +# termination, coverage-*, def-behavior -> não suportadas ("Unsupported +# Property" no wrapper): o par (task, prp) não é executado. +# Diferenças em relação aos wrappers, e por quê: +# * --add-invariants (unreach-call) fica de fora: exige Clam, ausente deste +# build; o Map2Check sai com 3. É o comando de reserva do próprio wrapper +# (command_line_bkp), usado quando as invariantes falham. +# * --smt-solver yices2 fica de fora: o KLEE 3.1 da imagem develop não tem +# backend metaSMT ("Did you mean '--solver-backend=yices2'?") e todo run +# symex terminaria UNKNOWN em 0,1 s. Vale o padrão do Map2Check (z3), como +# no ramo memsafety do wrapper de regressão. +# * --check-asserts não corresponde a nenhum .prp do SV-COMP. +# +# Cada (task, prp, gen, rep) roda num diretório novo sob RUNS_DIR, dentro da +# imagem -time (/usr/bin/time -f '%e %M' dá wall_s e maxrss_kb), como o usuário +# do host, com a instalação em /opt/m2c:ro e o corpus em /corpus:ro. +# Ordem: repetição mais externa, para espalhar ruído de máquina entre as chaves. +# Colunas do TSV: +# verdict SUCCEEDED | FAILED | UNKNOWN (última linha "VERIFICATION ..." da +# saída; o híbrido imprime uma por fase) | TIMEOUT (limite externo) +# | ERROR (nenhuma linha de veredito) +# tag só em FAILED: texto da linha da propriedade violada até o primeiro +# ':' (FALSE-FREE, FALSE-DEREF, FALSE-MEMTRACK, FALSE-MEMCLEANUP, +# FALSE para "FALSE: Target Reached", OVERFLOW, ...) +# line, function só em FAILED: de "file X line N function F" (vazios quando +# o Map2Check não imprime essa linha, p.ex. FALSE-MEMTRACK) +# witness 1 se witness.graphml foi gerado no diretório da execução, senão 0 +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +[ $# -eq 2 ] || { echo "uso: $0 " >&2; exit 2; } +INSTALL="$(cd "$1" && pwd)" +OUT="$2" +[ -x "$INSTALL/bin/map2check" ] || { echo "$INSTALL/bin/map2check ausente" >&2; exit 2; } + +REPS="${REPS:-5}" +GENERATORS="${GENERATORS:-afl symex hybrid}" +TIMEOUT_S="${TIMEOUT_S:-45}" +KILL_MARGIN_S="${KILL_MARGIN_S:-30}" +TASKS="${TASKS:-}" +CORPUS="$(cd "${CORPUS:-$ROOT/vendor/map2check/tests/regression_test/test_cases/sv-benchmarks}" && pwd)" +commit="$(sed -n 's/^commit=//p' "$ROOT/vendor/MAP2CHECK_VERSION")" +IMAGE="${IMAGE:-map2check-dev:develop-${commit:0:12}-time}" +stamp="$(date +%Y%m%d-%H%M%S)" +RUNS_DIR="${RUNS_DIR:-$ROOT/target/map2check-it/runs/$(basename "$OUT" .tsv)-$stamp}" +OUTER_S=$((TIMEOUT_S + KILL_MARGIN_S)) + +docker image inspect "$IMAGE" >/dev/null 2>&1 \ + || { echo "imagem $IMAGE ausente (rode scripts/it/build-map2check.sh --image-only)" >&2; exit 2; } + +prp_flags() { + case "$1" in + valid-memsafety) echo "--memtrack --generate-witness" ;; + valid-memcleanup) echo "--memcleanup-property --generate-witness" ;; + unreach-call) echo "--target-function --generate-witness" ;; + no-overflow) echo "--check-overflow --generate-witness" ;; + *) return 1 ;; + esac +} + +gen_flags() { + case "$1" in + afl) echo "--nondet-generator afl" ;; + symex) echo "--nondet-generator symex" ;; + hybrid) echo "" ;; + *) echo "gerador desconhecido: $1" >&2; exit 2 ;; + esac +} + +# Lista "taskinputprp" de todos os pares suportados. +pairs() { + local yml task dir input prp + while IFS= read -r yml; do + task="${yml#"$CORPUS"/}"; task="${task%.yml}" + if [ -n "$TASKS" ] && ! printf ' %s ' $TASKS | grep -qF " $task "; then continue; fi + dir="$(dirname "$task")" + input="$(sed -n "s/^input_files:[[:space:]]*['\"]\{0,1\}\([^'\"]*\)['\"]\{0,1\}[[:space:]]*$/\1/p" "$yml")" + [ -n "$input" ] || { echo "$yml: input_files ausente" >&2; exit 1; } + sed -n 's/^[[:space:]]*-[[:space:]]*property_file:[[:space:]]*\(.*\)$/\1/p' "$yml" | + while IFS= read -r p; do + prp="$(basename "$p" .prp)" + prp_flags "$prp" >/dev/null || continue + printf '%s\t%s\t%s\n' "$task" "$dir/$input" "$prp" + done + done < <(find "$CORPUS" -name '*.yml' | LC_ALL=C sort) +} + +# Lê out.txt/rc.txt/time.txt/witness.graphml de um diretório de execução e +# imprime "verdict\ttag\tline\tfunction\twitness\twall_s\tmaxrss_kb". +parse_run() { + local d="$1" rc verdict tag="" line="" func="" witness=0 wall=0 rss=0 clean prop + rc="$(cat "$d/rc.txt" 2>/dev/null || echo 255)" + clean="$(sed 's/\x1b\[[0-9;]*m//g' "$d/out.txt" 2>/dev/null || true)" + verdict="$(printf '%s\n' "$clean" | sed -n 's/^VERIFICATION \(SUCCEEDED\|FAILED\|UNKNOWN\)[[:space:]]*$/\1/p' | tail -n1)" + if [ "$rc" = 124 ] || [ "$rc" = 137 ]; then + verdict=TIMEOUT + elif [ -z "$verdict" ]; then + verdict=ERROR + fi + if [ "$verdict" = FAILED ]; then + # Bloco da última "Violated property:"; linhas seguintes com TAB inicial. + prop="$(printf '%s\n' "$clean" | awk '/^Violated property:/{b=""; on=1; next} on && /^\t/{b=b $0 "\n"; next} on{on=0} END{printf "%s", b}')" + if printf '%s\n' "$prop" | grep -q '^ file .* line [0-9-]* function '; then + line="$(printf '%s\n' "$prop" | sed -n 's/^ file .* line \([0-9-]*\) function \(.*\)$/\1/p' | head -n1)" + func="$(printf '%s\n' "$prop" | sed -n 's/^ file .* line \([0-9-]*\) function \(.*\)$/\2/p' | head -n1 | sed 's/[[:space:]]*$//')" + fi + tag="$(printf '%s\n' "$prop" | grep -v '^ file .* line ' | head -n1 | sed 's/^\t//; s/:.*$//; s/[[:space:]]*$//')" + fi + [ -f "$d/witness.graphml" ] && witness=1 + if [ -s "$d/time.txt" ]; then + read -r wall rss < <(tail -n1 "$d/time.txt") + fi + printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "$verdict" "$tag" "$line" "$func" "$witness" "$wall" "$rss" +} + +mapfile -t PAIRS < <(pairs) +[ ${#PAIRS[@]} -gt 0 ] || { echo "nenhum par (task, prp) selecionado" >&2; exit 1; } +total=$(( ${#PAIRS[@]} * $(wc -w <<<"$GENERATORS") * REPS )) +echo "run-corpus: ${#PAIRS[@]} pares x [$GENERATORS] x $REPS reps = $total execuções; TIMEOUT_S=$TIMEOUT_S, limite externo ${OUTER_S}s; imagem $IMAGE; runs em $RUNS_DIR" >&2 + +mkdir -p "$RUNS_DIR" "$(dirname "$OUT")" +printf 'task\tprp\tgen\trep\tverdict\ttag\tline\tfunction\twitness\twall_s\tmaxrss_kb\n' > "$OUT" + +n=0 +for rep in $(seq 1 "$REPS"); do + for pair in "${PAIRS[@]}"; do + IFS=$'\t' read -r task input prp <<<"$pair" + for gen in $GENERATORS; do + n=$((n + 1)) + d="$RUNS_DIR/${task//\//__}__${prp}__${gen}__r${rep}" + rm -rf "$d"; mkdir -p "$d" + flags="$(gen_flags "$gen") --timeout $TIMEOUT_S $(prp_flags "$prp")" + name="m2c-it-$$-$n" + # Rede de segurança no host: se o container não terminar após o limite + # externo + 60 s, é morto (docker run --rm o remove). + timeout -k 5 $((OUTER_S + 60)) docker run --rm --name "$name" --network none \ + --user "$(id -u):$(id -g)" -e HOME=/work \ + -v "$INSTALL:/opt/m2c:ro" -v "$CORPUS:/corpus:ro" -v "$d:/work" -w /work \ + "$IMAGE" bash -c ' + /usr/bin/time -f "%e %M" -o /work/time.txt \ + timeout -k 10 "$1" /opt/m2c/bin/map2check $2 "/corpus/$3" > out.txt 2> err.txt + echo $? > rc.txt + ' run "$OUTER_S" "$flags" "$input" >/dev/null 2>&1 \ + || docker kill "$name" >/dev/null 2>&1 || true + res="$(parse_run "$d")" + printf '%s\t%s\t%s\t%s\t%s\n' "$task" "$prp" "$gen" "$rep" "$res" >> "$OUT" + # Só os artefatos usados pelo parser ficam; o resto (KLEE, AFL++) é grande. + find "$d" -mindepth 1 -maxdepth 1 ! -name out.txt ! -name err.txt ! -name rc.txt \ + ! -name time.txt ! -name witness.graphml -exec rm -rf {} + 2>/dev/null || true + echo "[$n/$total] $task $prp $gen r$rep: $(cut -f1,6 <<<"$res" | tr '\t' ' ')s" >&2 + done + done +done diff --git a/scripts/it/smoke/link-smoke.c b/scripts/it/smoke/link-smoke.c new file mode 100644 index 0000000..23c5f03 --- /dev/null +++ b/scripts/it/smoke/link-smoke.c @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-3.0-only + * Programa de fumaça da matriz de link (scripts/it/check-link-matrix.sh). + * Seguro em todos os modos: aloca, escreve, soma, confere e libera, com um + * nondet limitado. Usa malloc/free (memtrack, memcleanup), aritmética com + * sinal (overflow) e reach_error (alcance). */ +#include +extern void __assert_fail(const char *, const char *, unsigned int, const char *); +void reach_error(void) { __assert_fail("0", "link-smoke.c", 9, "reach_error"); } +extern int __VERIFIER_nondet_int(void); +/* Não se chama __VERIFIER_assert: AnalysisModeAssert.bc define esse nome e o + * llvm-link do C falharia com "symbol multiply defined" no modo assert. */ +static void check(int cond) { + if (!cond) { + ERROR: { reach_error(); abort(); } + } +} +int main(void) { + int n = __VERIFIER_nondet_int(); + if (n < 0 || n > 8) + return 0; + int *a = malloc(sizeof(int) * 9); /* tamanho fixo: KLEE concretiza tamanho simbólico */ + if (a == NULL) + return 0; + int s = 0; + for (int i = 0; i <= n; i++) { + a[i] = i; + s += a[i]; + } + check(s == n * (n + 1) / 2); + free(a); + return 0; +} diff --git a/scripts/it/test-build-map2check.sh b/scripts/it/test-build-map2check.sh new file mode 100755 index 0000000..52f9d70 --- /dev/null +++ b/scripts/it/test-build-map2check.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# Teste de scripts/it/build-map2check.sh: constrói o Map2Check develop a partir +# da cópia somente leitura em vendor/map2check e confere o resultado real. +# +# Pesado (build de imagem Docker com KLEE/Clam; dezenas de minutos na primeira +# vez, depois usa cache). NÃO está ligado ao CI do GitHub. +# Sem docker, termina com código 77 (skip). +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +VENDOR="$ROOT/vendor/map2check" +INSTALL="$ROOT/target/map2check-it/c" +fail=0 +ok() { echo "ok - $1"; } +bad() { echo "FAIL - $1"; fail=1; } + +if ! command -v docker >/dev/null 2>&1 || ! docker info >/dev/null 2>&1; then + echo "SKIP: docker indisponível" >&2 + exit 77 +fi + +[ -d "$VENDOR" ] || { echo "FAIL - vendor/map2check ausente (rode scripts/fetch-map2check.sh)"; exit 1; } + +manifest() { (cd "$VENDOR" && find . -type f -print0 | LC_ALL=C sort -z | xargs -0 sha256sum); } +before="$(manifest | sha256sum)" + +"$ROOT/scripts/it/build-map2check.sh" || bad "build-map2check.sh terminou com erro" + +after="$(manifest | sha256sum)" +[ "$before" = "$after" ] && ok "manifesto sha256 de vendor/map2check inalterado" \ + || bad "vendor/map2check foi modificado pelo build" + +[ -x "$INSTALL/bin/map2check" ] && ok "bin/map2check instalado" || bad "bin/map2check ausente" +[ "$(readlink "$INSTALL/lib/klee/runtime")" = /opt/klee/lib/klee/runtime ] \ + && ok "lib/klee/runtime aponta para o runtime do KLEE da imagem" \ + || bad "lib/klee/runtime ausente (symex e híbrido terminariam UNKNOWN)" + +commit12="$(sed -n 's/^commit=//p' "$ROOT/vendor/MAP2CHECK_VERSION" | cut -c1-12)" +IMG="map2check-dev:develop-${commit12}-time" +modules="Map2CheckFunctions TrackBBLog NonDetLog PropertyGenerator ContainerRealloc ContainerBTree BTree AnalysisModeMemtrack AnalysisModeMemcleanup AnalysisModeOverflow AnalysisModeAssert AnalysisModeNone AllocationLog ListLog HeapLog NonDetGeneratorNone NonDetGeneratorKlee NonDetGeneratorAFL WasmRuntimeStubs WitnessGeneration WitnessGenerationNone" +n=0 +for m in $modules; do + n=$((n + 1)) + [ -f "$INSTALL/lib/$m.bc" ] || bad "lib/$m.bc ausente" +done +[ "$n" -eq 21 ] && ok "21 módulos esperados verificados" || bad "contagem de módulos $n != 21" + +out="$(docker run --rm --user "$(id -u):$(id -g)" -v "$INSTALL/lib:/lib-it:ro" "$IMG" \ + bash -c 'for m in '"$modules"'; do llvm-dis-16 "/lib-it/$m.bc" -o /dev/null || echo "BAD $m"; done' 2>&1)" || true +if [ -z "$out" ]; then ok "llvm-dis-16 lê os 21 .bc"; else bad "llvm-dis-16: $out"; fi + +exit $fail diff --git a/scripts/it/test-check-link-matrix.sh b/scripts/it/test-check-link-matrix.sh new file mode 100755 index 0000000..13e85d2 --- /dev/null +++ b/scripts/it/test-check-link-matrix.sh @@ -0,0 +1,178 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# Teste de scripts/it/check-link-matrix.sh sobre três diretórios de fixture: +# valid os 21 .bc do C instalados em target/map2check-it/c/lib +# -> 120 linhas PASS e "matrix: PASS" (exit 0) +# dup os mesmos .bc, mas HeapLog.bc também define wasm_rt_free_memory, +# que WasmRuntimeStubs.bc já define -> FAIL exatamente nas 24 +# combinações que ligam os dois (memtrack/memcleanup com wasm), +# PASS nas outras 96, "matrix: FAIL" (exit 1) +# undef AnalysisModeOverflow.bc passa a chamar uma função que nenhum +# módulo nem a libc define -> FAIL exatamente nas 24 combinações do +# modo overflow +# dso AnalysisModeAssert.bc passa a referenciar __dso_handle como +# símbolo FORTE -> FAIL nas 24 combinações do modo assert (só a +# referência weak é aceita) +# drop AnalysisModeMemtrack.bc perde map2check_wasm_malloc (que o +# programa de fumaça não chama): com --ref , a paridade de +# símbolos definidos falha exatamente nas 24 combinações memtrack; +# --ref do C contra ele mesmo dá 120 PASS +# +# Pesado (roda a matriz três vezes na imagem do Map2Check). Precisa da +# instalação do C (scripts/it/build-map2check.sh). NÃO está ligado ao CI. +# Sem docker, termina com código 77 (skip). +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SCRIPT="$ROOT/scripts/it/check-link-matrix.sh" +C_LIB="$ROOT/target/map2check-it/c/lib" +fail=0 +ok() { echo "ok - $1"; } +bad() { echo "FAIL - $1"; fail=1; } + +if ! command -v docker >/dev/null 2>&1 || ! docker info >/dev/null 2>&1; then + echo "SKIP: docker indisponível" >&2 + exit 77 +fi +[ -f "$C_LIB/Map2CheckFunctions.bc" ] \ + || { echo "FAIL - $C_LIB ausente (rode scripts/it/build-map2check.sh)"; exit 1; } +[ -x "$SCRIPT" ] || { echo "FAIL - $SCRIPT ausente ou não executável"; exit 1; } + +commit12="$(sed -n 's/^commit=//p' "$ROOT/vendor/MAP2CHECK_VERSION" | cut -c1-12)" +IMG="map2check-dev:develop-${commit12}" + +work="$ROOT/target/map2check-it/test-link-matrix" +rm -rf "$work" +mkdir -p "$work/dup" "$work/undef" "$work/dso" "$work/drop" +for f in dup undef dso drop; do cp "$C_LIB"/*.bc "$work/$f/"; done +cat > "$work/dup.ll" <<'EOF' +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" +define void @wasm_rt_free_memory(ptr %m) { + ret void +} +EOF +cat > "$work/undef.ll" <<'EOF' +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" +declare void @m2c_link_matrix_no_such_function() +define void @m2c_link_matrix_caller() { + call void @m2c_link_matrix_no_such_function() + ret void +} +EOF +cat > "$work/dso.ll" <<'EOF' +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" +@__dso_handle = external global i8 +define ptr @m2c_link_matrix_dso() { + ret ptr @__dso_handle +} +EOF +docker run --rm --user "$(id -u):$(id -g)" -v "$C_LIB:/l:ro" -v "$work:/w" "$IMG" bash -euc ' + llvm-as-16 /w/dup.ll -o /w/dup.bc + llvm-link-16 /l/HeapLog.bc /w/dup.bc -o /w/dup/HeapLog.bc + llvm-as-16 /w/undef.ll -o /w/undef.bc + llvm-link-16 /l/AnalysisModeOverflow.bc /w/undef.bc -o /w/undef/AnalysisModeOverflow.bc + llvm-as-16 /w/dso.ll -o /w/dso.bc + llvm-link-16 /l/AnalysisModeAssert.bc /w/dso.bc -o /w/dso/AnalysisModeAssert.bc + llvm-nm-16 --defined-only --extern-only /l/AnalysisModeMemtrack.bc | awk "{print \$NF}" \ + | grep -vx map2check_wasm_malloc > /w/drop.api + opt-16 -passes=internalize,globaldce -internalize-public-api-file=/w/drop.api \ + /l/AnalysisModeMemtrack.bc -o /w/drop/AnalysisModeMemtrack.bc +' + +# --- valid ----------------------------------------------------------------- +rc=0 +"$SCRIPT" "$C_LIB" > "$work/valid.out" 2> "$work/valid.err" || rc=$? +[ "$rc" -eq 0 ] && ok "valid: exit 0" || bad "valid: exit $rc (stderr: $(tail -n3 "$work/valid.err"))" +[ "$(tail -n1 "$work/valid.out")" = "matrix: PASS" ] && ok "valid: matrix: PASS" \ + || bad "valid: última linha '$(tail -n1 "$work/valid.out")'" +n="$(grep -c '^PASS ' "$work/valid.out" || true)" +[ "$n" -eq 120 ] && ok "valid: 120 combinações PASS" || bad "valid: $n combinações PASS" +lines="$(grep -cE '^(PASS|FAIL) ' "$work/valid.out" || true)" +[ "$lines" -eq 120 ] && ok "valid: 120 linhas de combinação" || bad "valid: $lines linhas" +# As 120 combinações são distintas e cobrem o produto inteiro. +uniq_combos="$(grep -E '^(PASS|FAIL) ' "$work/valid.out" | cut -d' ' -f2-6 | sort -u | wc -l)" +[ "$uniq_combos" -eq 120 ] && ok "valid: 120 combinações distintas" || bad "valid: $uniq_combos distintas" +for c in "container=btree mode=memcleanup gen=afl wasm=1 witness=1" \ + "container=array mode=none gen=symex wasm=0 witness=0" \ + "container=array mode=assert gen=none wasm=1 witness=0"; do + grep -qxF "PASS $c" "$work/valid.out" && ok "valid: linha 'PASS $c'" || bad "valid: falta 'PASS $c'" +done + +# --- dup ------------------------------------------------------------------- +rc=0 +"$SCRIPT" "$work/dup" > "$work/dup.out" 2> "$work/dup.err" || rc=$? +[ "$rc" -eq 1 ] && ok "dup: exit 1" || bad "dup: exit $rc" +[ "$(tail -n1 "$work/dup.out")" = "matrix: FAIL" ] && ok "dup: matrix: FAIL" \ + || bad "dup: última linha '$(tail -n1 "$work/dup.out")'" +expected_fail="$(for ct in array btree; do for m in memtrack memcleanup; do for g in none symex afl; do for wi in 0 1; do + echo "container=$ct mode=$m gen=$g wasm=1 witness=$wi"; done; done; done; done | sort)" +got_fail="$(sed -n 's/^FAIL \(container=[a-z]* mode=[a-z]* gen=[a-z]* wasm=[01] witness=[01]\).*/\1/p' "$work/dup.out" | sort)" +[ "$got_fail" = "$expected_fail" ] && ok "dup: FAIL exatamente nas 24 combinações memtrack/memcleanup + wasm" \ + || bad "dup: combinações FAIL diferentes do esperado: $(diff <(echo "$expected_fail") <(echo "$got_fail") | head -5 | tr '\n' ' ')" +n="$(grep -c '^PASS ' "$work/dup.out" || true)" +[ "$n" -eq 96 ] && ok "dup: 96 combinações PASS" || bad "dup: $n PASS" +grep -q '^FAIL container=btree mode=memcleanup gen=afl wasm=1 witness=1 .*wasm_rt_free_memory' "$work/dup.out" \ + && ok "dup: o FAIL nomeia a combinação e o símbolo duplicado" \ + || bad "dup: FAIL sem a combinação ou sem o símbolo: $(grep '^FAIL' "$work/dup.out" | head -1)" + +# --- undef ----------------------------------------------------------------- +rc=0 +"$SCRIPT" "$work/undef" > "$work/undef.out" 2> "$work/undef.err" || rc=$? +[ "$rc" -eq 1 ] && ok "undef: exit 1" || bad "undef: exit $rc" +expected_fail="$(for ct in array btree; do for g in none symex afl; do for wa in 0 1; do for wi in 0 1; do + echo "container=$ct mode=overflow gen=$g wasm=$wa witness=$wi"; done; done; done; done | sort)" +got_fail="$(sed -n 's/^FAIL \(container=[a-z]* mode=[a-z]* gen=[a-z]* wasm=[01] witness=[01]\).*/\1/p' "$work/undef.out" | sort)" +[ "$got_fail" = "$expected_fail" ] && ok "undef: FAIL exatamente nas 24 combinações do modo overflow" \ + || bad "undef: combinações FAIL diferentes do esperado: $(diff <(echo "$expected_fail") <(echo "$got_fail") | head -5 | tr '\n' ' ')" +grep -q '^FAIL container=array mode=overflow gen=symex wasm=0 witness=0 .*m2c_link_matrix_no_such_function' "$work/undef.out" \ + && ok "undef: o FAIL de symex (sem link nativo) nomeia o símbolo indefinido" \ + || bad "undef: FAIL symex sem o símbolo: $(grep '^FAIL.*gen=symex' "$work/undef.out" | head -1)" + +# --- dso ------------------------------------------------------------------- +rc=0 +"$SCRIPT" "$work/dso" > "$work/dso.out" 2> "$work/dso.err" || rc=$? +[ "$rc" -eq 1 ] && ok "dso: exit 1" || bad "dso: exit $rc" +expected_fail="$(for ct in array btree; do for g in none symex afl; do for wa in 0 1; do for wi in 0 1; do + echo "container=$ct mode=assert gen=$g wasm=$wa witness=$wi"; done; done; done; done | sort)" +got_fail="$(sed -n 's/^FAIL \(container=[a-z]* mode=[a-z]* gen=[a-z]* wasm=[01] witness=[01]\).*/\1/p' "$work/dso.out" | sort)" +[ "$got_fail" = "$expected_fail" ] && ok "dso: __dso_handle forte falha exatamente nas 24 combinações do modo assert" \ + || bad "dso: combinações FAIL diferentes do esperado: $(diff <(echo "$expected_fail") <(echo "$got_fail") | head -5 | tr '\n' ' ')" +grep -q '^FAIL container=array mode=assert gen=symex wasm=0 witness=0 undefined: __dso_handle$' "$work/dso.out" \ + && ok "dso: o FAIL nomeia __dso_handle" || bad "dso: $(grep '^FAIL.*gen=symex' "$work/dso.out" | head -1)" + +# --- paridade (--ref) ------------------------------------------------------ +rc=0 +"$SCRIPT" --ref "$C_LIB" "$C_LIB" > "$work/ref-self.out" 2> "$work/ref-self.err" || rc=$? +n="$(grep -c '^PASS ' "$work/ref-self.out" || true)" +[ "$rc" -eq 0 ] && [ "$n" -eq 120 ] && ok "ref: o C contra ele mesmo dá 120 PASS" \ + || bad "ref: C contra C exit $rc, $n PASS" +rc=0 +"$SCRIPT" --ref "$C_LIB" "$work/drop" > "$work/drop.out" 2> "$work/drop.err" || rc=$? +[ "$rc" -eq 1 ] && ok "drop: exit 1" || bad "drop: exit $rc" +expected_fail="$(for ct in array btree; do for g in none symex afl; do for wa in 0 1; do for wi in 0 1; do + echo "container=$ct mode=memtrack gen=$g wasm=$wa witness=$wi"; done; done; done; done | sort)" +got_fail="$(sed -n 's/^FAIL \(container=[a-z]* mode=[a-z]* gen=[a-z]* wasm=[01] witness=[01]\).*/\1/p' "$work/drop.out" | sort)" +[ "$got_fail" = "$expected_fail" ] && ok "drop: paridade falha exatamente nas 24 combinações memtrack" \ + || bad "drop: combinações FAIL diferentes do esperado: $(diff <(echo "$expected_fail") <(echo "$got_fail") | head -5 | tr '\n' ' ')" +grep -q '^FAIL container=btree mode=memtrack gen=afl wasm=1 witness=1 parity: missing map2check_wasm_malloc$' "$work/drop.out" \ + && ok "drop: o FAIL nomeia o símbolo que falta" || bad "drop: $(grep '^FAIL' "$work/drop.out" | head -1)" +printf 'symbol\tside\treason\nmap2check_wasm_malloc\tc-only\tfixture\n' > "$work/policy.tsv" +rc=0 +"$SCRIPT" --ref "$C_LIB" --policy "$work/policy.tsv" "$work/drop" > /dev/null 2>&1 || rc=$? +[ "$rc" -eq 0 ] && ok "drop: símbolo listado como c-only na política passa" || bad "drop com política: exit $rc" + +# --- uso ------------------------------------------------------------------- +rc=0 +"$SCRIPT" > /dev/null 2>&1 || rc=$? +[ "$rc" -eq 2 ] && ok "sem argumento: exit 2" || bad "sem argumento: exit $rc" +rc=0 +"$SCRIPT" "$work/no-such-dir" > /dev/null 2>&1 || rc=$? +[ "$rc" -eq 2 ] && ok "diretório inexistente: exit 2" || bad "diretório inexistente: exit $rc" +rc=0 +"$SCRIPT" --ref "$work/no-such-dir" "$C_LIB" > /dev/null 2>&1 || rc=$? +[ "$rc" -eq 2 ] && ok "--ref inexistente: exit 2" || bad "--ref inexistente: exit $rc" + +exit $fail diff --git a/scripts/it/test-overlay-install.sh b/scripts/it/test-overlay-install.sh new file mode 100755 index 0000000..f2461d0 --- /dev/null +++ b/scripts/it/test-overlay-install.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# Test of scripts/it/overlay-install.sh (Task 3.1). +# +# Fixtures (host only): a fake C install with bin/ (a symlink and a file), +# the binary at the root, plugins in lib/, the 21 lib/*.bc and the two +# image-only symlinks of scripts/it/build-map2check.sh (lib/klee/runtime, +# lib/clang; dangling outside the image), and a directory with the 21 drop-in +# .bc. +# overlay_replaces_only_the_modules the overlay's lib/*.bc are the drop-in +# ones; every other path (bin/, the binary, the plugins, the symlinks +# with their targets) is the same as in the C install, byte for byte; +# the C install is unchanged; +# overlay_refuses a bc-dir without one module, with a +# .bc that is not a runtime module, a C install without lib/, an +# inside the C install, an that is not an +# install, and wrong usage: non-zero exit, nothing created; +# overlay_replaces_an_earlier_overlay a second run on the same +# gives the same tree; +# overlay_marker the overlay has .map2check-overlay +# (source install, the 21 sha256), the only path the C install lacks; +# overlay_refuses_a_c_install_as_output a copy of the C install given as +# (it has lib/Map2CheckFunctions.bc but no marker) is +# refused and left byte for byte as it was. +# Real install (when M2C_INSTALL and BC_DIR exist): the same checks on +# target/map2check-it/c with the output of scripts/build-runtime-bc.sh; +# bin/ and the binary byte-identical, lib/klee/runtime and lib/clang kept. +# +# Environment: M2C_INSTALL (default target/map2check-it/c), BC_DIR (default +# target/runtime-ir/dropin-lib), OUT (default target/map2check-it/rs, the +# overlay the corpus smoke runs on). +set -euo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$HERE/../.." && pwd)" +OVERLAY="$HERE/overlay-install.sh" +MODULES="Map2CheckFunctions TrackBBLog NonDetLog PropertyGenerator ContainerRealloc ContainerBTree BTree AnalysisModeMemtrack AnalysisModeMemcleanup AnalysisModeOverflow AnalysisModeAssert AnalysisModeNone AllocationLog ListLog HeapLog NonDetGeneratorNone NonDetGeneratorKlee NonDetGeneratorAFL WasmRuntimeStubs WitnessGeneration WitnessGenerationNone" +T="$(mktemp -d)"; trap 'rm -rf "$T"' EXIT +fail=0 +ok() { echo "ok $*"; } +bad() { echo "FAIL $*" >&2; fail=1; } + +# A listing of a tree: path, type, symlink target or sha256, mode. +tree() { # [exclude-regex] + (cd "$1" && find . -mindepth 1 | LC_ALL=C sort | while read -r p; do + [ -n "${2:-}" ] && [[ "$p" =~ $2 ]] && continue + if [ -L "$p" ]; then echo "$p L $(readlink "$p")" + elif [ -d "$p" ]; then echo "$p d $(stat -c %a "$p")" + else echo "$p f $(sha256sum < "$p" | cut -c1-16) $(stat -c %a "$p")"; fi + done) +} + +# Checks : the overlay against the C install and the modules. +check() { + local c="$1" bc="$2" out="$3" label="$4" m why="" + local mods_re='^\./(lib/[A-Za-z0-9]+\.bc|\.map2check-overlay)$' + diff <(tree "$c" "$mods_re") <(tree "$out" "$mods_re") > "$T/diff" \ + || why+=" paths other than lib/*.bc differ: $(grep '^[<>]' "$T/diff" | head -5 | tr '\n' ' ');" + for m in $MODULES; do + cmp -s "$bc/$m.bc" "$out/lib/$m.bc" || why+=" lib/$m.bc is not the drop-in one;" + done + [ "$(cd "$out/lib" && ls *.bc | wc -l)" = 21 ] || why+=" lib/ has $(cd "$out/lib" && ls *.bc | wc -l) .bc;" + [ ! -e "$c/.map2check-overlay" ] || why+=" the C install got a marker;" + [ "$(head -1 "$out/.map2check-overlay" 2>/dev/null)" = "map2check-overlay 1" ] || why+=" no marker;" + [ "$(grep -c '\.bc$' "$out/.map2check-overlay" 2>/dev/null)" = 21 ] || why+=" the marker lacks the 21 sha256;" + (cd "$bc" && grep '\.bc$' "$out/.map2check-overlay" | sha256sum -c --quiet) > /dev/null 2>&1 \ + || why+=" the marker sha256 do not match $bc;" + [ "$(tree "$c/bin")" = "$(tree "$out/bin")" ] || why+=" bin/ differs;" + if [ -z "$why" ]; then ok "$label"; else bad "$label:$why"; fi +} + +# --- fixtures --------------------------------------------------------------- +C="$T/c"; BC="$T/bc" +mkdir -p "$C/bin" "$C/lib/klee" "$BC" +printf 'ELF-ish binary\n' > "$C/map2check"; chmod 755 "$C/map2check" +ln -s ../map2check "$C/bin/map2check" +printf '#!/bin/sh\n' > "$C/bin/ktest-tool"; chmod 755 "$C/bin/ktest-tool" +printf 'plugin\n' > "$C/lib/libMemoryTrackPass.so" +ln -s /opt/klee/lib/klee/runtime "$C/lib/klee/runtime" +ln -s /usr/lib/llvm-16/lib/clang "$C/lib/clang" +for m in $MODULES; do printf 'C %s\n' "$m" > "$C/lib/$m.bc"; printf 'RS %s\n' "$m" > "$BC/$m.bc"; done +before="$(tree "$C")" + +"$OVERLAY" "$C" "$BC" "$T/out" > "$T/log" 2>&1 || bad "overlay_replaces_only_the_modules: exit $?: $(tail -2 "$T/log")" +[ -d "$T/out" ] && check "$C" "$BC" "$T/out" "overlay_replaces_only_the_modules (fixture)" +[ "$(tree "$C")" = "$before" ] && ok "overlay_replaces_only_the_modules: the C install is unchanged" \ + || bad "overlay_replaces_only_the_modules: the C install changed" +[ "$(readlink "$T/out/lib/clang")" = /usr/lib/llvm-16/lib/clang ] && [ -L "$T/out/lib/klee/runtime" ] \ + && ok "overlay_replaces_only_the_modules: lib/clang and lib/klee/runtime stay symlinks" \ + || bad "overlay_replaces_only_the_modules: the image-only symlinks were not kept" + +first="$(tree "$T/out")" +"$OVERLAY" "$C" "$BC" "$T/out" > "$T/log" 2>&1 || bad "overlay_replaces_an_earlier_overlay: exit $?" +[ "$(tree "$T/out")" = "$first" ] && ok "overlay_replaces_an_earlier_overlay" \ + || bad "overlay_replaces_an_earlier_overlay: the tree changed" + +refuse() { #