From fbba9a6c56ae14432edfe3275d37c96f4f4f8143 Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Sat, 17 Oct 2026 16:55:11 -0400 Subject: [PATCH 01/74] docs: record the Map2Check migration merge commit --- docs/cutover/decisoes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/cutover/decisoes.md b/docs/cutover/decisoes.md index c9d9025..130a8b6 100644 --- a/docs/cutover/decisoes.md +++ b/docs/cutover/decisoes.md @@ -248,3 +248,7 @@ 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. From 9292029d7a421e285a18f28933befe9faf90c4aa Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 11:17:01 -0400 Subject: [PATCH 02/74] docs: add mandatory agent rules for remote actions, scope and vendored Map2Check Restrict gh/git remote actions to explicit requests, keep all changes in this repository, and require the Map2Check copy used by integration tests to live unversioned under vendor/map2check/ (now ignored by git). --- .github/copilot-instructions.md | 26 ++++++++++++++++++++++++++ .gitignore | 3 +++ CLAUDE.md | 26 ++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index bc0d98e..67a66fe 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -62,3 +62,29 @@ 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 para o diretório `vendor/map2check/` (ou outro que o usuário indicar), + por meio de `git clone --depth 1 vendor/map2check` ou do tarball de uma 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 a versão ou o commit 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/.gitignore b/.gitignore index 0ab168c..df9848a 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ map2check.h # Arquivos de release (scripts/package-release.sh) dist/ + +# Cópia local do Map2Check para testes de integração (sem versionamento) +vendor/map2check/ diff --git a/CLAUDE.md b/CLAUDE.md index f2dd884..d69bee4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -77,6 +77,32 @@ 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 para o diretório `vendor/map2check/` (ou outro que o usuário indicar), + por meio de `git clone --depth 1 vendor/map2check` ou do tarball de uma 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 a versão ou o commit 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 From e8de5967c50930b0307c61723b0a604270570743 Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 11:35:02 -0400 Subject: [PATCH 03/74] docs: restrict the cutover plan to this repository and pin Map2Check to develop Mark the Map2Check-side steps of the cutover plan (Task 5.1 steps 5-6, Tasks 6.1-6.4) as maintainer-owned and add the agent's local tasks: 5.3 (read-only Map2Check copy under vendor/map2check/ for the gate), 5.1 step 7, 6.0 (local precondition check) and 6.4a (this repository's docs). Require the integration-test copy of Map2Check to come from the develop branch, in the plan and in the mandatory agent rules. --- .github/copilot-instructions.md | 8 ++- CLAUDE.md | 8 ++- .../2026-10-15-cutover-runtime-c-map2check.md | 64 +++++++++++++++---- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 67a66fe..48f5f55 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -78,13 +78,15 @@ consult those files before changing how analysis modes are linked. Map2Check, nem em qualquer outro repositório. 3. **Map2Check para testes de integração: cópia local e sem versionamento.** - - Baixe o Map2Check para o diretório `vendor/map2check/` (ou outro que o usuário indicar), - por meio de `git clone --depth 1 vendor/map2check` ou do tarball de uma release. + - 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 a versão ou o commit baixado (por exemplo, em `vendor/MAP2CHECK_VERSION`) para + - 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/CLAUDE.md b/CLAUDE.md index d69bee4..f21fb8f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -92,13 +92,15 @@ At analysis end, `print_all_containers_as_json()` serializes the state to RFC 82 Map2Check, nem em qualquer outro repositório. 3. **Map2Check para testes de integração: cópia local e sem versionamento.** - - Baixe o Map2Check para o diretório `vendor/map2check/` (ou outro que o usuário indicar), - por meio de `git clone --depth 1 vendor/map2check` ou do tarball de uma release. + - 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 a versão ou o commit baixado (por exemplo, em `vendor/MAP2CHECK_VERSION`) para + - 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/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..75f4f9d 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,13 @@ **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). +> **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 +1365,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 +1381,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 +1443,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 +1481,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). From 2bacf5ce9320bacbfcac5bf2a5cf182426d88774 Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 12:43:12 -0400 Subject: [PATCH 04/74] docs: restart the Map2Check cutover as a drop-in replacement of the C runtime The merge of Map2Check PR #76 (a0f8b9cd) left develop with a force push on 2026-10-22, so the old cutover plan is superseded. The new plan builds the 21 bitcode modules Map2Check develop links, from Rust, and proves them against the C runtime with a differential gate, without changing Map2Check. --- .../2026-10-15-cutover-runtime-c-map2check.md | 2 + .../2026-10-26-runtime-drop-in-map2check.md | 304 ++++++++++++++++++ 2 files changed, 306 insertions(+) create mode 100644 docs/superpowers/plans/2026-10-26-runtime-drop-in-map2check.md 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 75f4f9d..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,6 +10,8 @@ **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 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..1247d11 --- /dev/null +++ b/docs/superpowers/plans/2026-10-26-runtime-drop-in-map2check.md @@ -0,0 +1,304 @@ +# 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`. +- Desempenho (critérios mantidos do plano anterior, aprovados pelo mantenedor): mediana de `wall_rust / wall_c` ≤ 1,10; 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`, `scripts/build-klee-runtime.sh`, pós-processamento `scripts/klee-*.py`), 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). + +- [ ] **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"`). + +- [ ] **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. +- [ ] **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`. + +--- + +## Fase 4 — Gate diferencial + +### Task 4.1: Primeira execução 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 → 4.1 → 4.2 → 5.1. From e4910912904133d741dcf282c4e84386aad4b566 Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 12:45:02 -0400 Subject: [PATCH 05/74] chore: fetch Map2Check develop as an unversioned local copy and record the restart --- .github/workflows/CI.yml | 3 ++ TODO.md | 6 +++ docs/cutover/decisoes.md | 14 +++++++ scripts/fetch-map2check.sh | 67 +++++++++++++++++++++++++++++ scripts/test-fetch-map2check.sh | 74 +++++++++++++++++++++++++++++++++ vendor/MAP2CHECK_VERSION | 6 +++ 6 files changed, 170 insertions(+) create mode 100755 scripts/fetch-map2check.sh create mode 100755 scripts/test-fetch-map2check.sh create mode 100644 vendor/MAP2CHECK_VERSION diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 664f23e..843e206 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -46,6 +46,9 @@ 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 diff --git a/TODO.md b/TODO.md index b9b2510..7632962 100644 --- a/TODO.md +++ b/TODO.md @@ -155,6 +155,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/docs/cutover/decisoes.md b/docs/cutover/decisoes.md index 130a8b6..83b836f 100644 --- a/docs/cutover/decisoes.md +++ b/docs/cutover/decisoes.md @@ -252,3 +252,17 @@ The C tests drove internal containers with no Rust export, so each assertion goe ## 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). 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/test-fetch-map2check.sh b/scripts/test-fetch-map2check.sh new file mode 100755 index 0000000..a60cae3 --- /dev/null +++ b/scripts/test-fetch-map2check.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# Teste offline de scripts/fetch-map2check.sh (fixture local via file://). +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +FETCH="$ROOT/scripts/fetch-map2check.sh" +fail=0 +ok() { echo "ok - $1"; } +bad() { echo "FAIL - $1"; fail=1; } + +# O destino precisa ficar dentro do repositório (o script recusa o resto). +mkdir -p "$ROOT/target" +WORK="$(mktemp -d "$ROOT/target/test-fetch-map2check.XXXXXX")" +FIX="$(mktemp -d)" +trap 'rm -rf "$WORK" "$FIX"' EXIT + +G() { git -c user.name=t -c user.email=t@example.com -c commit.gpgsign=false -c protocol.file.allow=always "$@"; } + +# Submódulo +G init -q -b main "$FIX/sub" +echo sub > "$FIX/sub/sub.txt" +G -C "$FIX/sub" add . && G -C "$FIX/sub" commit -q -m sub + +# Superprojeto: develop (com submódulo) e other (HEAD do remoto) +G init -q -b develop "$FIX/super" +echo dev > "$FIX/super/file.txt" +G -C "$FIX/super" add . && G -C "$FIX/super" commit -q -m "develop commit" +G -C "$FIX/super" submodule add -q "file://$FIX/sub" modules/sub +G -C "$FIX/super" commit -q -m "add submodule" +DEV="$(G -C "$FIX/super" rev-parse HEAD)" +G -C "$FIX/super" checkout -q -b other +echo other > "$FIX/super/file.txt" +G -C "$FIX/super" commit -q -a -m "other commit" +export MAP2CHECK_URL="file://$FIX/super" + +DEST="$WORK/vendor/map2check" +VER="$WORK/vendor/MAP2CHECK_VERSION" + +# Sem submódulos +rc=0; "$FETCH" "$DEST" >/dev/null 2>&1 || rc=$? +[ "$rc" -eq 0 ] && ok "download sai 0" || bad "download sai 0 (rc=$rc)" +[ "$(cat "$DEST/file.txt" 2>/dev/null)" = dev ] && ok "clona develop mesmo com other como HEAD" || bad "clona develop" +[ -z "$(find "$DEST" -name .git 2>/dev/null)" ] && ok "nenhum .git sobra" || bad "nenhum .git sobra" +missing="" +for k in url branch commit commit_date commit_subject fetched; do + grep -q "^$k=." "$VER" 2>/dev/null || missing="$missing $k" +done +[ -z "$missing" ] && ok "MAP2CHECK_VERSION tem as 6 chaves" || bad "chaves ausentes:$missing" +[ "$(sed -n 's/^commit=//p' "$VER")" = "$DEV" ] && ok "commit igual ao develop" || bad "commit igual ao develop" +[ "$(sed -n 's/^branch=//p' "$VER")" = develop ] && ok "branch=develop" || bad "branch=develop" +[ -z "$(ls -A "$DEST/modules/sub" 2>/dev/null)" ] && ok "submódulo vazio sem --with-submodules" || bad "submódulo vazio" + +# Segundo download substitui o destino +echo stale > "$DEST/stale.txt" +"$FETCH" "$DEST" >/dev/null 2>&1 || true +[ ! -e "$DEST/stale.txt" ] && [ "$(cat "$DEST/file.txt")" = dev ] && ok "segundo download substitui o destino" || bad "segundo download substitui" + +# Com submódulos +"$FETCH" --with-submodules "$DEST" >/dev/null 2>&1 || true +[ "$(cat "$DEST/modules/sub/sub.txt" 2>/dev/null)" = sub ] && ok "--with-submodules traz o submódulo" || bad "--with-submodules" +[ -z "$(find "$DEST" -name .git 2>/dev/null)" ] && ok "nenhum .git com submódulos" || bad "nenhum .git com submódulos" + +# Destino fora do repositório +OUT="$(mktemp -d)" +rc=0; "$FETCH" "$OUT/m2c" >/dev/null 2>&1 || rc=$? +if [ "$rc" -ne 0 ] && [ ! -e "$OUT/m2c" ]; then ok "destino fora do repositório recusado"; else bad "destino fora recusado (rc=$rc)"; fi +rm -rf "$OUT" +rc=0; "$FETCH" "$ROOT/../x" >/dev/null 2>&1 || rc=$? +[ "$rc" -ne 0 ] && ok "destino com .. recusado" || bad "destino com .. recusado" +rc=0; "$FETCH" "$ROOT" >/dev/null 2>&1 || rc=$? +[ "$rc" -ne 0 ] && [ -d "$ROOT/.git" ] && ok "raiz do repositório recusada" || bad "raiz recusada" + +exit "$fail" diff --git a/vendor/MAP2CHECK_VERSION b/vendor/MAP2CHECK_VERSION new file mode 100644 index 0000000..4898dcc --- /dev/null +++ b/vendor/MAP2CHECK_VERSION @@ -0,0 +1,6 @@ +url=https://github.com/hbgit/Map2Check +branch=develop +commit=3e914528ec5742d8ae4bc191589ca4669827c7ad +commit_date=2026-10-26T15:27:51Z +commit_subject=ci: limit greetings workflow token permissions +fetched=2026-10-26T16:44:57Z From f0c5c914d67e969366a94c27ca06c68490d4b3bb Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 12:52:18 -0400 Subject: [PATCH 06/74] docs: inventory the C runtime ABI of Map2Check develop --- docs/runtime-abi/inventory.tsv | 193 ++++++++++++++++++ docs/runtime-abi/rust-gap.tsv | 193 ++++++++++++++++++ scripts/runtime-abi-inventory.py | 277 ++++++++++++++++++++++++++ scripts/test_runtime_abi_inventory.py | 143 +++++++++++++ 4 files changed, 806 insertions(+) create mode 100644 docs/runtime-abi/inventory.tsv create mode 100644 docs/runtime-abi/rust-gap.tsv create mode 100755 scripts/runtime-abi-inventory.py create mode 100644 scripts/test_runtime_abi_inventory.py diff --git a/docs/runtime-abi/inventory.tsv b/docs/runtime-abi/inventory.tsv new file mode 100644 index 0000000..637c775 --- /dev/null +++ b/docs/runtime-abi/inventory.tsv @@ -0,0 +1,193 @@ +symbol kind signature module referenced_by +B_TREE_CREATE func B_TREE (const char *) BTree.bc runtime +B_TREE_FREE func void (B_TREE *) BTree.bc runtime +B_TREE_INSERT func char (B_TREE *, B_TREE_ROW *) BTree.bc runtime +B_TREE_INSERT_NONFULL func char (B_TREE *, B_TREE_ROW *, B_TREE_PAGE *) BTree.bc - +B_TREE_PAGE_CREATE func B_TREE_PAGE *(B_TREE *) BTree.bc - +B_TREE_SEARCH func B_TREE_ROW *(B_TREE *, unsigned int) BTree.bc runtime +B_TREE_SPLIT_CHILD func char (B_TREE *, B_TREE_PAGE *, int, B_TREE_PAGE *) BTree.bc - +CHECK_AND_RELEASE_PAGES func void (B_TREE *) BTree.bc - +DISK_READ func char (B_TREE *, fpos_t *, B_TREE_PAGE *) BTree.bc - +DISK_WRITE func char (B_TREE *, B_TREE_PAGE *) BTree.bc - +DumpTree func void (B_TREE *) BTree.bc - +DumpTreeHelper func void (B_TREE *, unsigned int, B_TREE_PAGE *) BTree.bc - +DumpTreePage func void (B_TREE_PAGE *) BTree.bc - +DumpTreePageChildren func void (B_TREE_PAGE *) BTree.bc - +ERROR_DEREF data char AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - +Map2CheckCurrentStep data unsigned int Map2CheckFunctions.bc - +__VERIFIER_assert func void (int) AnalysisModeAssert.bc pass+frontend +__VERIFIER_error func void () Map2CheckFunctions.bc pass+frontend +__afl_fuzz_alt data unsigned char[1048576] NonDetGeneratorAFL.bc - +__afl_fuzz_alt_ptr data unsigned char * NonDetGeneratorAFL.bc - +__afl_sharedmem_fuzzing data int NonDetGeneratorAFL.bc - +allocation_log data MAP2CHECK_CONTAINER AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - +allocation_log_to_file func void (MAP2CHECK_CONTAINER *) AllocationLog.bc runtime +analysis_destroy func void () AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime +analysis_generate_aux_witness_files func void () AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime +analysis_init func void () AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime +analysis_is_program_correct func char () AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime +append_element func char (MAP2CHECK_CONTAINER *, void *) ContainerBTree.bc+ContainerRealloc.bc runtime +check_address_allocation_log func enum MemoryAddressStatus (MAP2CHECK_CONTAINER *, long) AllocationLog.bc runtime +divisionByZeroError func void (unsigned int, const char *) AnalysisModeOverflow.bc - +find_row_with_address func MEMORY_ALLOCATIONS_ROW *(MAP2CHECK_CONTAINER *, void *) AllocationLog.bc runtime +free_container func char (MAP2CHECK_CONTAINER *) ContainerBTree.bc+ContainerRealloc.bc runtime +generate_aux_files func void (MAP2CHECK_CONTAINER *, char) WitnessGeneration.bc+WitnessGenerationNone.bc runtime +generate_confirmation_file func void () WitnessGeneration.bc+WitnessGenerationNone.bc runtime +get_element_at func void *(unsigned int, MAP2CHECK_CONTAINER) ContainerBTree.bc+ContainerRealloc.bc runtime +get_next_input_from_afl func uint8_t () NonDetGeneratorAFL.bc - +get_old_reference func long (long, MAP2CHECK_CONTAINER *) ListLog.bc - +get_type_from_list_log_row func enum MemoryAddressStatus (LIST_LOG_ROW *) ListLog.bc runtime +gotError data char Map2CheckFunctions.bc - +heap_log data MAP2CHECK_CONTAINER AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - +heap_log_file data const char * HeapLog.bc - +heap_log_to_file func void (MAP2CHECK_CONTAINER *) HeapLog.bc runtime +helper_map2check_nondet_append_element func void (NONDET_CALL) NonDetLog.bc - +is_deref_error func char (long, MAP2CHECK_CONTAINER *) ListLog.bc - +is_in_tracked func char (unsigned int, MAP2CHECK_CONTAINER *) TrackBBLog.bc runtime +is_invalid_free func char (long, MAP2CHECK_CONTAINER *) ListLog.bc runtime +is_memcleanup_error func char (long, MAP2CHECK_CONTAINER *) ListLog.bc runtime +is_valid_allocation_address func char (MAP2CHECK_CONTAINER *, void *, int) AllocationLog.bc runtime +is_valid_heap_address func char (MAP2CHECK_CONTAINER *, void *, int) HeapLog.bc runtime +klee_log_file data const char * NonDetLog.bc - +list_log data MAP2CHECK_CONTAINER AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - +list_log_file data const char * ListLog.bc - +list_log_to_file func void (MAP2CHECK_CONTAINER *) ListLog.bc runtime +main func int (int, char **) | int () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass+frontend +map2check_add_store_pointer func void (void *, void *, unsigned int, const char *, int, const char *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_afl_data data const uint8_t * NonDetGeneratorAFL.bc - +map2check_afl_size data size_t NonDetGeneratorAFL.bc - +map2check_alloca func void (const char *, void *, int, int, int, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_assert func void (int, int, const char *) AnalysisModeAssert.bc pass +map2check_assume func void (int) Map2CheckFunctions.bc pass +map2check_assume_loop func void (char) Map2CheckFunctions.bc pass +map2check_binop_add func 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 *) AnalysisModeOverflow.bc pass +map2check_binop_mul func 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 *) AnalysisModeOverflow.bc pass +map2check_binop_sdiv func 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 *) AnalysisModeOverflow.bc pass +map2check_binop_sub func 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 *) AnalysisModeOverflow.bc pass +map2check_calloc func void (void *, int, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_check_cstring func void (const char *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_check_deref func void (int, const char *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_crab_assume func void (char) Map2CheckFunctions.bc pass +map2check_destroy func void () Map2CheckFunctions.bc - +map2check_error func void () Map2CheckFunctions.bc runtime +map2check_exit func void () Map2CheckFunctions.bc - +map2check_free func 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) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_function func void (const char *, void *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_get_current_step func unsigned int () Map2CheckFunctions.bc runtime +map2check_init func void (int) Map2CheckFunctions.bc pass +map2check_load func void (void *, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_malloc func void (void *, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_next_current_step func void () Map2CheckFunctions.bc runtime +map2check_non_det_bool func bool () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_char func char () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_double func double () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_float func float () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_int func int () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_loff_t func loff_t () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_long func long () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_longlong func longlong () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_memory func void (void *, size_t) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_pchar func char *() NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_pointer func pointer () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_sector_t func unsigned long () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_short func short () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_size_t func size_t () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_u16 func u16 () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_u32 func u32 () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_u8 func u8 () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_uchar func unsigned char () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_uint func unsigned int () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_ulong func unsigned long () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_ulonglong func ulonglong () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_det_unsigned func unsigned int () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass +map2check_non_det_ushort func ushort () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass +map2check_non_static_alloca func 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 *) NonDetLog.bc pass +map2check_nondet_char func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_double func void (unsigned int, unsigned int, double, const char *) NonDetLog.bc pass +map2check_nondet_float func void (unsigned int, unsigned int, double, const char *) NonDetLog.bc pass +map2check_nondet_get_log func MAP2CHECK_CONTAINER () NonDetLog.bc runtime +map2check_nondet_int func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_loff_t func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_long func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_longlong func 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 *) NonDetLog.bc runtime +map2check_nondet_pchar func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_pointer func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_sector_t func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_short func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_size_t func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_u16 func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_u32 func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_u8 func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_uchar func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_uint func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_nondet_ulong func 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 *) NonDetLog.bc pass +map2check_nondet_unsigned func void (unsigned int, unsigned int, unsigned int, const char *) NonDetLog.bc pass +map2check_nondet_ushort func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass +map2check_posix func void (void *, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass +map2check_success func void () Map2CheckFunctions.bc pass +map2check_target_function func void (const char *, int, int) Map2CheckFunctions.bc pass +map2check_track_bb func void (unsigned int, const char *) Map2CheckFunctions.bc pass +map2check_wasm_check_access func void (uint64_t, uint64_t) AnalysisModeMemtrack.bc pass +map2check_wasm_free func void (uint64_t, int) AnalysisModeMemtrack.bc pass +map2check_wasm_malloc func void (uint64_t, uint64_t) AnalysisModeMemtrack.bc pass +mark_allocation_log func char (MAP2CHECK_CONTAINER *, long) AllocationLog.bc - +mark_deallocation_log func char (MAP2CHECK_CONTAINER *, long) AllocationLog.bc - +mark_heap_log func char (MAP2CHECK_CONTAINER *, MEMORY_HEAP_ROW) HeapLog.bc - +new_container func MAP2CHECK_CONTAINER (enum Container_Type) ContainerBTree.bc+ContainerRealloc.bc runtime +new_heap_row func MEMORY_HEAP_ROW (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) ListLog.bc runtime +new_memory_row func MEMORY_ALLOCATIONS_ROW (long, char) AllocationLog.bc runtime +new_nondet_call func NONDET_CALL (enum NONDET_TYPE, unsigned int, unsigned int, void *, const char *, unsigned int) NonDetLog.bc - +nondet_assume func void (int) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime +nondet_cancel func void () NonDetGeneratorAFL.bc - +nondet_destroy func void () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime +nondet_generate_aux_witness_files func void () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime +nondet_init func void () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime +nondet_log data MAP2CHECK_CONTAINER NonDetLog.bc - +nondet_log_destroy func void () NonDetLog.bc runtime +nondet_log_init func void () NonDetLog.bc runtime +nondet_log_to_file func char (MAP2CHECK_CONTAINER) NonDetLog.bc runtime +overflowError func void (unsigned int, const char *) AnalysisModeOverflow.bc pass +release_unreleased_addresses func void (MAP2CHECK_CONTAINER *) AllocationLog.bc - +sv_comp data char Map2CheckFunctions.bc - +trackbb_log data MAP2CHECK_CONTAINER Map2CheckFunctions.bc - +trackbb_log_file data const char * TrackBBLog.bc - +trackbb_log_to_file func void (MAP2CHECK_CONTAINER *) TrackBBLog.bc runtime +trackbb_new_row func TRACK_BB_ROW (unsigned int, const char *) TrackBBLog.bc runtime +unknown_not_supported func void () AnalysisModeOverflow.bc - +update_reference_list_log func void (long, enum MemoryAddressStatus, unsigned int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - +valid_allocation_log func long (MAP2CHECK_CONTAINER *) AllocationLog.bc runtime +violated_property_file data const char * PropertyGenerator.bc - +violated_property_file_assert data const char * PropertyGenerator.bc - +violated_property_file_deref data const char * PropertyGenerator.bc - +violated_property_file_free data const char * PropertyGenerator.bc - +violated_property_file_memcleanup data const char * PropertyGenerator.bc - +violated_property_file_memtrack data const char * PropertyGenerator.bc - +violated_property_file_overflow data const char * PropertyGenerator.bc - +violated_property_file_unknown data const char * PropertyGenerator.bc - +w2c_wasi__snapshot__preview1_proc_exit func void (struct w2c_wasi__snapshot__preview1 *, uint32_t) WasmRuntimeStubs.bc - +wasm_rt_allocate_funcref_table func void (wasm_rt_funcref_table_t *, uint32_t, uint32_t) WasmRuntimeStubs.bc - +wasm_rt_allocate_memory func void (wasm_rt_memory_t *, uint64_t, uint64_t, bool, uint32_t) WasmRuntimeStubs.bc - +wasm_rt_free func void (void) WasmRuntimeStubs.bc - +wasm_rt_free_funcref_table func void (wasm_rt_funcref_table_t *) WasmRuntimeStubs.bc - +wasm_rt_free_memory func void (wasm_rt_memory_t *) WasmRuntimeStubs.bc - +wasm_rt_grow_memory func uint64_t (wasm_rt_memory_t *, uint64_t) WasmRuntimeStubs.bc - +wasm_rt_init func void (void) WasmRuntimeStubs.bc - +wasm_rt_is_initialized func bool (void) WasmRuntimeStubs.bc - +wasm_rt_trap func void (wasm_rt_trap_t) WasmRuntimeStubs.bc - +write_property func void (enum ViolatedProperty, int, const char *) PropertyGenerator.bc runtime +write_property_assert func void (int, const char *) PropertyGenerator.bc - +write_property_deref func void (int, const char *) PropertyGenerator.bc runtime +write_property_free func void (int, const char *) PropertyGenerator.bc runtime +write_property_memcleanup func void () PropertyGenerator.bc runtime +write_property_memtrack func void () PropertyGenerator.bc runtime +write_property_overflow func void (int, const char *) PropertyGenerator.bc - +write_property_unknown func void () PropertyGenerator.bc runtime diff --git a/docs/runtime-abi/rust-gap.tsv b/docs/runtime-abi/rust-gap.tsv new file mode 100644 index 0000000..153e03d --- /dev/null +++ b/docs/runtime-abi/rust-gap.tsv @@ -0,0 +1,193 @@ +symbol status rust_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 different void (int32_t) +__VERIFIER_error different 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 different void (const void *, uint32_t, uint32_t, const char *) +map2check_crab_assume missing - +map2check_destroy missing - +map2check_error missing - +map2check_exit missing - +map2check_free missing - +map2check_free_resolved_address missing - +map2check_function missing - +map2check_get_current_step missing - +map2check_init different void (void) +map2check_load missing - +map2check_malloc missing - +map2check_next_current_step missing - +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 missing - +map2check_nondet_char missing - +map2check_nondet_double missing - +map2check_nondet_float missing - +map2check_nondet_get_log missing - +map2check_nondet_int missing - +map2check_nondet_loff_t missing - +map2check_nondet_long missing - +map2check_nondet_longlong missing - +map2check_nondet_memory_byte missing - +map2check_nondet_pchar missing - +map2check_nondet_pointer missing - +map2check_nondet_sector_t missing - +map2check_nondet_short missing - +map2check_nondet_size_t missing - +map2check_nondet_u16 missing - +map2check_nondet_u32 missing - +map2check_nondet_u8 missing - +map2check_nondet_uchar missing - +map2check_nondet_uint missing - +map2check_nondet_ulong missing - +map2check_nondet_ulonglong missing - +map2check_nondet_unsigned missing - +map2check_nondet_ushort missing - +map2check_posix missing - +map2check_success different void (void) +map2check_target_function missing - +map2check_track_bb missing - +map2check_wasm_check_access same void (uint64_t, uint64_t) +map2check_wasm_free different void (uint64_t, int64_t) +map2check_wasm_malloc same void (uint64_t, uint64_t) +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 missing - +nondet_log_init missing - +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 different (not declared in header) +wasm_rt_allocate_funcref_table different (not declared in header) +wasm_rt_allocate_memory different (not declared in header) +wasm_rt_free different (not declared in header) +wasm_rt_free_funcref_table different (not declared in header) +wasm_rt_free_memory different (not declared in header) +wasm_rt_grow_memory different (not declared in header) +wasm_rt_init different (not declared in header) +wasm_rt_is_initialized different (not declared in header) +wasm_rt_trap different (not declared in header) +write_property missing - +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/scripts/runtime-abi-inventory.py b/scripts/runtime-abi-inventory.py new file mode 100755 index 0000000..f83f1fc --- /dev/null +++ b/scripts/runtime-abi-inventory.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-only +"""Inventory the ABI of the Map2Check C runtime and compare it with this library. + +Writes two tab-separated files to --out-dir, sorted by symbol: + + inventory.tsv symbol kind signature module referenced_by + rust-gap.tsv symbol status rust_signature + +A row exists for every external symbol defined (nm types T/D/B/R/C/V/W) by an +object compiled from modules/backend/library/lib/*.c. `signature` is the +`type.qualType` clang prints in its JSON AST for the definition; `module` lists +the .bc files that define it (joined by '+'); `referenced_by` is a subset of +pass+frontend+runtime ('-' when empty): + + pass the name is a string literal (or the expansion of a `"prefix" #arg` + macro body for every invocation) in modules/backend/pass/*.{cpp,hpp} + frontend the name is a string literal in modules/frontend/*.{cpp,hpp} + runtime the symbol is undefined in one runtime object and defined in another + +WasmRuntimeStubs.c includes wasm-rt.h from WABT. When the host has no WABT the +script falls back (-idirafter, so a real wasm-rt.h always wins) to a minimal +shim written to a temporary directory; it declares the WABT typedef names the +file uses, so the qualType spelling is the same as with WABT. + +rust-gap.tsv compares each inventory symbol with the external symbols defined +in --rust-lib (nm) and the declarations of --rust-header (clang JSON AST): +`same` needs an equal qualType, `different` otherwise, `missing` when the +archive does not define the symbol. + +Regenerate the committed files from the repository root with: + + cargo build --release --all-features + python3 -I scripts/runtime-abi-inventory.py --map2check vendor/map2check \\ + --out-dir docs/runtime-abi --rust-lib target/release/libmap2check.a \\ + --rust-header include/map2check.h + +Needs clang and nm (binutils or llvm) on PATH. +""" + +import argparse +import glob +import json +import os +import re +import subprocess +import sys +import tempfile + +DEFINED_TYPES = set("TDBRCVW") +WASM_SHIM = """\ +/* Minimal stand-in for WABT's wasm-rt.h (runtime-abi-inventory.py). */ +#ifndef WASM_RT_SHIM_H +#define WASM_RT_SHIM_H +#include +#include +typedef enum { WASM_RT_TRAP_NONE } wasm_rt_trap_t; +typedef struct { + uint8_t *data; + uint8_t *data_end; + uint64_t pages, max_pages, size; + uint32_t page_size; + bool is64; +} wasm_rt_memory_t; +typedef struct { + void *data; + uint32_t max_size, size; +} wasm_rt_funcref_table_t; +#endif +""" + +STRING_RE = re.compile(r'"((?:[^"\\\n]|\\.)*)"') +IDENT_RE = re.compile(r"^[A-Za-z_.][A-Za-z0-9_.]*$") +DEFINE_RE = re.compile(r"^[ \t]*#[ \t]*define[ \t]+(\w+)\(([^)]*)\)((?:[^\n]*\\\n)*[^\n]*)", re.M) +STRINGIFY_RE = re.compile(r'"((?:[^"\\\n]|\\.)*)"\s*#\s*(\w+)') + + +def run(cmd, **kwargs): + return subprocess.run(cmd, check=True, capture_output=True, text=True, **kwargs).stdout + + +def strip_comments(text): + """Remove C/C++ comments, keeping string literals intact.""" + pattern = re.compile(r'//[^\n]*|/\*.*?\*/|"(?:[^"\\\n]|\\.)*"|\'(?:[^\'\\\n]|\\.)*\'', re.S) + + def keep(match): + token = match.group(0) + if token.startswith("/"): + return "\n" * token.count("\n") if token.startswith("/*") else "" + return token + + return pattern.sub(keep, text) + + +def split_args(text): + """Split a macro argument list on top-level commas.""" + args, depth, current = [], 0, "" + for char in text: + if char == "," and depth == 0: + args.append(current.strip()) + current = "" + continue + depth += char == "(" + depth -= char == ")" + current += char + args.append(current.strip()) + return args + + +def macro_names(text): + """Names produced by `"prefix" #param` in macro bodies, for every invocation.""" + names = set() + for match in DEFINE_RE.finditer(text): + macro, params = match.group(1), [p.strip() for p in match.group(2).split(",")] + templates = STRINGIFY_RE.findall(match.group(3)) + if not templates: + continue + body_free = text[: match.start()] + text[match.end():] + for call in re.finditer(r"\b%s\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)" % re.escape(macro), body_free): + args = split_args(call.group(1)) + if len(args) != len(params): + continue + for prefix, param in templates: + if param in params: + names.add(prefix + args[params.index(param)]) + return names + + +def referenced_names(files): + """Identifier-shaped string literals plus macro expansions found in files.""" + names = set() + for path in files: + with open(path, encoding="utf-8", errors="replace") as handle: + text = strip_comments(handle.read()) + names.update(s for s in STRING_RE.findall(text) if IDENT_RE.match(s)) + names.update(n for n in macro_names(text) if IDENT_RE.match(n)) + return names + + +def nm_symbols(path): + """Return (defined {name: type}, undefined set) for an object or archive.""" + defined, undefined = {}, set() + for line in run(["nm", path]).splitlines(): + fields = line.split() + if len(fields) == 3 and fields[1] in DEFINED_TYPES: + defined[fields[2]] = fields[1] + elif len(fields) == 2 and fields[0] == "U": + undefined.add(fields[1]) + return defined, undefined + + +def ast_declarations(source, flags): + """Map name -> (kind, qualType) of top-level declarations; definitions win.""" + dump = run(["clang", "-w", "-fsyntax-only", "-Xclang", "-ast-dump=json"] + flags + [source]) + decls = {} + for node in json.loads(dump).get("inner", []): + if node.get("kind") not in ("FunctionDecl", "VarDecl") or "name" not in node: + continue + kind = "func" if node["kind"] == "FunctionDecl" else "data" + if kind == "func": + is_def = any(c.get("kind") == "CompoundStmt" for c in node.get("inner", [])) + else: + is_def = node.get("storageClass") != "extern" or "init" in node + name = node["name"] + if is_def or name not in decls or not decls[name][2]: + decls[name] = (kind, node["type"]["qualType"], is_def) + return {name: (kind, sig) for name, (kind, sig, _) in decls.items()} + + +def compile_runtime(lib_dir, header_dir, work): + """Compile every runtime .c; return {module: (defined, undefined, decls)}.""" + shim = os.path.join(work, "shim") + os.makedirs(shim, exist_ok=True) + with open(os.path.join(shim, "wasm-rt.h"), "w", encoding="utf-8") as handle: + handle.write(WASM_SHIM) + flags = ["-I", header_dir, "-I", lib_dir, "-idirafter", shim] + modules = {} + for source in sorted(glob.glob(os.path.join(lib_dir, "*.c"))): + base = os.path.splitext(os.path.basename(source))[0] + obj = os.path.join(work, base + ".o") + run(["clang", "-w", "-c", "-O0"] + flags + ["-o", obj, source]) + defined, undefined = nm_symbols(obj) + modules[base + ".bc"] = (defined, undefined, ast_declarations(source, flags)) + return modules + + +def build_inventory(m2c, work): + backend = os.path.join(m2c, "modules", "backend") + lib_dir = os.path.join(backend, "library", "lib") + modules = compile_runtime(lib_dir, os.path.join(backend, "library", "header"), work) + + def sources(directory): + return sorted(glob.glob(os.path.join(directory, "*.cpp")) + glob.glob(os.path.join(directory, "*.hpp"))) + + pass_names = referenced_names(sources(os.path.join(backend, "pass"))) + frontend_names = referenced_names(sources(os.path.join(m2c, "modules", "frontend"))) + + rows = {} + for module, (defined, _, decls) in modules.items(): + for name, nm_type in defined.items(): + row = rows.setdefault(name, {"modules": [], "sigs": [], "kind": None}) + row["modules"].append(module) + kind, sig = decls.get(name, ("func" if nm_type == "T" else "data", "?")) + row["kind"] = row["kind"] or kind + if sig not in row["sigs"]: + row["sigs"].append(sig) + + for name, row in rows.items(): + refs = [] + if name in pass_names: + refs.append("pass") + if name in frontend_names: + refs.append("frontend") + if any(name in undefined and module not in row["modules"] + for module, (_, undefined, _) in modules.items()): + refs.append("runtime") + row["refs"] = "+".join(refs) or "-" + row["signature"] = " | ".join(row["sigs"]) + unresolved = sorted(pass_names - set(rows)) + return rows, unresolved + + +def rust_gap(rows, rust_lib, rust_header): + defined, _ = nm_symbols(rust_lib) + decls = ast_declarations(rust_header, ["-x", "c"]) + gap = {} + for name, row in rows.items(): + if name not in defined: + gap[name] = ("missing", "-") + elif name not in decls: + gap[name] = ("different", "(not declared in header)") + else: + sig = decls[name][1] + gap[name] = ("same" if sig == row["signature"] else "different", sig) + return gap + + +def write_tsv(path, header, lines): + with open(path, "w", encoding="utf-8") as handle: + handle.write("\t".join(header) + "\n") + for line in lines: + handle.write("\t".join(line) + "\n") + + +def main(argv=None): + parser = argparse.ArgumentParser( + description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("--map2check", required=True, help="Map2Check source tree (vendor/map2check)") + parser.add_argument("--out-dir", required=True, help="directory for inventory.tsv and rust-gap.tsv") + parser.add_argument("--rust-lib", required=True, help="static library to compare (libmap2check.a)") + parser.add_argument("--rust-header", required=True, help="its C header (include/map2check.h)") + args = parser.parse_args(argv) + + with tempfile.TemporaryDirectory() as work: + rows, unresolved = build_inventory(args.map2check, work) + gap = rust_gap(rows, args.rust_lib, args.rust_header) + + os.makedirs(args.out_dir, exist_ok=True) + names = sorted(rows) + write_tsv(os.path.join(args.out_dir, "inventory.tsv"), + ["symbol", "kind", "signature", "module", "referenced_by"], + ([n, rows[n]["kind"], rows[n]["signature"], "+".join(sorted(rows[n]["modules"])), rows[n]["refs"]] + for n in names)) + write_tsv(os.path.join(args.out_dir, "rust-gap.tsv"), ["symbol", "status", "rust_signature"], + ([n, gap[n][0], gap[n][1]] for n in names)) + + counts = {s: sum(1 for g in gap.values() if g[0] == s) for s in ("same", "different", "missing")} + print("defined: %d" % len(rows)) + print("rust: same %(same)d, different %(different)d, missing %(missing)d" % counts) + print("pass-referenced names not defined by the runtime (%d):" % len(unresolved)) + for name in unresolved: + print(" " + name) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/test_runtime_abi_inventory.py b/scripts/test_runtime_abi_inventory.py new file mode 100644 index 0000000..dd48c18 --- /dev/null +++ b/scripts/test_runtime_abi_inventory.py @@ -0,0 +1,143 @@ +# SPDX-License-Identifier: GPL-3.0-only +"""Tests for scripts/runtime-abi-inventory.py. + +Run with: python3 -I -m unittest scripts/test_runtime_abi_inventory.py +""" + +import os +import shutil +import subprocess +import sys +import tempfile +import textwrap +import unittest + +SCRIPT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "runtime-abi-inventory.py") +TOOLS = ("clang", "nm", "ar") + + +def write(path, text): + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w", encoding="utf-8") as handle: + handle.write(textwrap.dedent(text).lstrip("\n")) + + +def build_fixture(root): + """A minimal Map2Check tree: two runtime .c, one header, one pass, one frontend file.""" + lib = os.path.join(root, "modules", "backend", "library") + write(os.path.join(lib, "header", "Fix.h"), """ + int map2check_x(int a, const char *s); + extern int shared_counter; + void helper(void); + """) + write(os.path.join(lib, "lib", "A.c"), """ + #include "../header/Fix.h" + int shared_counter = 0; + static int hidden(void) { return 3; } + int map2check_x(int a, const char *s) { helper(); return a + hidden() + (s != 0); } + int map2check_non_det_int(void) { return 1; } + unsigned map2check_non_det_unsigned(void) { return 2u; } + """) + write(os.path.join(lib, "lib", "B.c"), """ + #include "../header/Fix.h" + void helper(void) {} + int map2check_non_det_int(void) { return 0; } + long unused_fn(long x) { return x; } + """) + write(os.path.join(root, "modules", "backend", "pass", "P.cpp"), """ + // unused_fn is only mentioned in a comment and must not count. + /* "unused_fn" neither. */ + void P::setup(Function &F) { + auto x = F.getParent()->getOrInsertFunction("map2check_x", I32, PtrTy); + auto m = F.getParent()->getOrInsertFunction("map2check_missing", VoidTy); + } + #define CALL_IF_HELPER(type, nondet_type, c_type) \\ + if ((calleeFunction->getName() == ("__VERIFIER_nondet_" #c_type))) { \\ + this->instrumentNonDet(NonDetType::nondet_type, calleeFunction); \\ + } else if ((calleeFunction->getName() == ("map2check_non_det_" #c_type))) { \\ + this->instrumentNonDet##type(callInst, Ctx); \\ + } + void P::run(CallInst *callInst) { + CALL_IF_HELPER(Integer, INTEGER, int) + CALL_IF_HELPER(Unsigned, UNSIGNED, unsigned) + } + """) + write(os.path.join(root, "modules", "frontend", "F.cpp"), """ + std::string counter = "shared_counter"; + """) + + +def build_rust_side(root, work): + """A stand-in for libmap2check.a and include/map2check.h.""" + header = os.path.join(root, "rust", "map2check.h") + write(header, """ + int map2check_x(int a, const char *s); + long map2check_non_det_int(void); + """) + source = os.path.join(work, "rustlib.c") + write(source, """ + int map2check_x(int a, const char *s) { return a + (s != 0); } + long map2check_non_det_int(void) { return 0; } + """) + obj = os.path.join(work, "rustlib.o") + archive = os.path.join(root, "rust", "libfake.a") + subprocess.run(["clang", "-w", "-c", "-o", obj, source], check=True) + subprocess.run(["ar", "rcs", archive, obj], check=True) + return archive, header + + +def read_lines(path): + with open(path, encoding="utf-8") as handle: + return handle.read().splitlines() + + +@unittest.skipUnless(all(shutil.which(t) for t in TOOLS), "needs clang, nm and ar") +class InventoryTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = os.path.join(self.tmp.name, "map2check") + self.out = os.path.join(self.tmp.name, "out") + build_fixture(self.root) + self.archive, self.header = build_rust_side(self.root, self.tmp.name) + self.result = subprocess.run( + [sys.executable, "-I", SCRIPT, + "--map2check", self.root, "--out-dir", self.out, + "--rust-lib", self.archive, "--rust-header", self.header], + capture_output=True, text=True) + + def tearDown(self): + self.tmp.cleanup() + + def test_exit_status(self): + self.assertEqual(self.result.returncode, 0, self.result.stderr) + + def test_inventory_rows(self): + self.assertEqual(read_lines(os.path.join(self.out, "inventory.tsv")), [ + "symbol\tkind\tsignature\tmodule\treferenced_by", + "helper\tfunc\tvoid (void)\tB.bc\truntime", + "map2check_non_det_int\tfunc\tint (void)\tA.bc+B.bc\tpass", + "map2check_non_det_unsigned\tfunc\tunsigned int (void)\tA.bc\tpass", + "map2check_x\tfunc\tint (int, const char *)\tA.bc\tpass", + "shared_counter\tdata\tint\tA.bc\tfrontend", + "unused_fn\tfunc\tlong (long)\tB.bc\t-", + ]) + + def test_rust_gap_rows(self): + self.assertEqual(read_lines(os.path.join(self.out, "rust-gap.tsv")), [ + "symbol\tstatus\trust_signature", + "helper\tmissing\t-", + "map2check_non_det_int\tdifferent\tlong (void)", + "map2check_non_det_unsigned\tmissing\t-", + "map2check_x\tsame\tint (int, const char *)", + "shared_counter\tmissing\t-", + "unused_fn\tmissing\t-", + ]) + + def test_reports_unresolved_pass_names(self): + self.assertIn("map2check_missing", self.result.stdout) + self.assertIn("__VERIFIER_nondet_unsigned", self.result.stdout) + self.assertIn("defined: 6", self.result.stdout) + + +if __name__ == "__main__": + unittest.main() From 14c2fcb114578a2c0a076bd2f2341de6392028a2 Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 13:04:25 -0400 Subject: [PATCH 07/74] fix: add canonical signatures and external consumers to the runtime ABI inventory --- docs/runtime-abi/inventory.tsv | 386 +++++++++++++------------- docs/runtime-abi/rust-gap.tsv | 386 +++++++++++++------------- scripts/runtime-abi-inventory.py | 173 ++++++++++-- scripts/test_runtime_abi_inventory.py | 125 +++++++-- 4 files changed, 641 insertions(+), 429 deletions(-) diff --git a/docs/runtime-abi/inventory.tsv b/docs/runtime-abi/inventory.tsv index 637c775..724e2c9 100644 --- a/docs/runtime-abi/inventory.tsv +++ b/docs/runtime-abi/inventory.tsv @@ -1,193 +1,193 @@ -symbol kind signature module referenced_by -B_TREE_CREATE func B_TREE (const char *) BTree.bc runtime -B_TREE_FREE func void (B_TREE *) BTree.bc runtime -B_TREE_INSERT func char (B_TREE *, B_TREE_ROW *) BTree.bc runtime -B_TREE_INSERT_NONFULL func char (B_TREE *, B_TREE_ROW *, B_TREE_PAGE *) BTree.bc - -B_TREE_PAGE_CREATE func B_TREE_PAGE *(B_TREE *) BTree.bc - -B_TREE_SEARCH func B_TREE_ROW *(B_TREE *, unsigned int) BTree.bc runtime -B_TREE_SPLIT_CHILD func char (B_TREE *, B_TREE_PAGE *, int, B_TREE_PAGE *) BTree.bc - -CHECK_AND_RELEASE_PAGES func void (B_TREE *) BTree.bc - -DISK_READ func char (B_TREE *, fpos_t *, B_TREE_PAGE *) BTree.bc - -DISK_WRITE func char (B_TREE *, B_TREE_PAGE *) BTree.bc - -DumpTree func void (B_TREE *) BTree.bc - -DumpTreeHelper func void (B_TREE *, unsigned int, B_TREE_PAGE *) BTree.bc - -DumpTreePage func void (B_TREE_PAGE *) BTree.bc - -DumpTreePageChildren func void (B_TREE_PAGE *) BTree.bc - -ERROR_DEREF data char AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - -Map2CheckCurrentStep data unsigned int Map2CheckFunctions.bc - -__VERIFIER_assert func void (int) AnalysisModeAssert.bc pass+frontend -__VERIFIER_error func void () Map2CheckFunctions.bc pass+frontend -__afl_fuzz_alt data unsigned char[1048576] NonDetGeneratorAFL.bc - -__afl_fuzz_alt_ptr data unsigned char * NonDetGeneratorAFL.bc - -__afl_sharedmem_fuzzing data int NonDetGeneratorAFL.bc - -allocation_log data MAP2CHECK_CONTAINER AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - -allocation_log_to_file func void (MAP2CHECK_CONTAINER *) AllocationLog.bc runtime -analysis_destroy func void () AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime -analysis_generate_aux_witness_files func void () AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime -analysis_init func void () AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime -analysis_is_program_correct func char () AnalysisModeAssert.bc+AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc+AnalysisModeNone.bc+AnalysisModeOverflow.bc runtime -append_element func char (MAP2CHECK_CONTAINER *, void *) ContainerBTree.bc+ContainerRealloc.bc runtime -check_address_allocation_log func enum MemoryAddressStatus (MAP2CHECK_CONTAINER *, long) AllocationLog.bc runtime -divisionByZeroError func void (unsigned int, const char *) AnalysisModeOverflow.bc - -find_row_with_address func MEMORY_ALLOCATIONS_ROW *(MAP2CHECK_CONTAINER *, void *) AllocationLog.bc runtime -free_container func char (MAP2CHECK_CONTAINER *) ContainerBTree.bc+ContainerRealloc.bc runtime -generate_aux_files func void (MAP2CHECK_CONTAINER *, char) WitnessGeneration.bc+WitnessGenerationNone.bc runtime -generate_confirmation_file func void () WitnessGeneration.bc+WitnessGenerationNone.bc runtime -get_element_at func void *(unsigned int, MAP2CHECK_CONTAINER) ContainerBTree.bc+ContainerRealloc.bc runtime -get_next_input_from_afl func uint8_t () NonDetGeneratorAFL.bc - -get_old_reference func long (long, MAP2CHECK_CONTAINER *) ListLog.bc - -get_type_from_list_log_row func enum MemoryAddressStatus (LIST_LOG_ROW *) ListLog.bc runtime -gotError data char Map2CheckFunctions.bc - -heap_log data MAP2CHECK_CONTAINER AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - -heap_log_file data const char * HeapLog.bc - -heap_log_to_file func void (MAP2CHECK_CONTAINER *) HeapLog.bc runtime -helper_map2check_nondet_append_element func void (NONDET_CALL) NonDetLog.bc - -is_deref_error func char (long, MAP2CHECK_CONTAINER *) ListLog.bc - -is_in_tracked func char (unsigned int, MAP2CHECK_CONTAINER *) TrackBBLog.bc runtime -is_invalid_free func char (long, MAP2CHECK_CONTAINER *) ListLog.bc runtime -is_memcleanup_error func char (long, MAP2CHECK_CONTAINER *) ListLog.bc runtime -is_valid_allocation_address func char (MAP2CHECK_CONTAINER *, void *, int) AllocationLog.bc runtime -is_valid_heap_address func char (MAP2CHECK_CONTAINER *, void *, int) HeapLog.bc runtime -klee_log_file data const char * NonDetLog.bc - -list_log data MAP2CHECK_CONTAINER AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - -list_log_file data const char * ListLog.bc - -list_log_to_file func void (MAP2CHECK_CONTAINER *) ListLog.bc runtime -main func int (int, char **) | int () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass+frontend -map2check_add_store_pointer func void (void *, void *, unsigned int, const char *, int, const char *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass -map2check_afl_data data const uint8_t * NonDetGeneratorAFL.bc - -map2check_afl_size data size_t NonDetGeneratorAFL.bc - -map2check_alloca func void (const char *, void *, int, int, int, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass -map2check_assert func void (int, int, const char *) AnalysisModeAssert.bc pass -map2check_assume func void (int) Map2CheckFunctions.bc pass -map2check_assume_loop func void (char) Map2CheckFunctions.bc pass -map2check_binop_add func 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 *) AnalysisModeOverflow.bc pass -map2check_binop_mul func 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 *) AnalysisModeOverflow.bc pass -map2check_binop_sdiv func 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 *) AnalysisModeOverflow.bc pass -map2check_binop_sub func 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 *) AnalysisModeOverflow.bc pass -map2check_calloc func void (void *, int, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass -map2check_check_cstring func void (const char *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass -map2check_check_deref func void (int, const char *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass -map2check_crab_assume func void (char) Map2CheckFunctions.bc pass -map2check_destroy func void () Map2CheckFunctions.bc - -map2check_error func void () Map2CheckFunctions.bc runtime -map2check_exit func void () Map2CheckFunctions.bc - -map2check_free func 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) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass -map2check_function func void (const char *, void *) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass -map2check_get_current_step func unsigned int () Map2CheckFunctions.bc runtime -map2check_init func void (int) Map2CheckFunctions.bc pass -map2check_load func void (void *, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass -map2check_malloc func void (void *, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass -map2check_next_current_step func void () Map2CheckFunctions.bc runtime -map2check_non_det_bool func bool () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass -map2check_non_det_char func char () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_double func double () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_float func float () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_int func int () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_loff_t func loff_t () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass -map2check_non_det_long func long () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_longlong func longlong () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_memory func void (void *, size_t) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_pchar func char *() NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass -map2check_non_det_pointer func pointer () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_sector_t func unsigned long () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass -map2check_non_det_short func short () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_size_t func size_t () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass -map2check_non_det_u16 func u16 () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_u32 func u32 () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_u8 func u8 () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_uchar func unsigned char () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass -map2check_non_det_uint func unsigned int () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_ulong func unsigned long () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass -map2check_non_det_ulonglong func ulonglong () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_det_unsigned func unsigned int () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc pass -map2check_non_det_ushort func ushort () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc pass -map2check_non_static_alloca func 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 *) NonDetLog.bc pass -map2check_nondet_char func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_double func void (unsigned int, unsigned int, double, const char *) NonDetLog.bc pass -map2check_nondet_float func void (unsigned int, unsigned int, double, const char *) NonDetLog.bc pass -map2check_nondet_get_log func MAP2CHECK_CONTAINER () NonDetLog.bc runtime -map2check_nondet_int func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_loff_t func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_long func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_longlong func 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 *) NonDetLog.bc runtime -map2check_nondet_pchar func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_pointer func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_sector_t func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_short func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_size_t func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_u16 func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_u32 func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_u8 func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_uchar func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_uint func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_nondet_ulong func 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 *) NonDetLog.bc pass -map2check_nondet_unsigned func void (unsigned int, unsigned int, unsigned int, const char *) NonDetLog.bc pass -map2check_nondet_ushort func void (unsigned int, unsigned int, int, const char *) NonDetLog.bc pass -map2check_posix func void (void *, int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc pass -map2check_success func void () Map2CheckFunctions.bc pass -map2check_target_function func void (const char *, int, int) Map2CheckFunctions.bc pass -map2check_track_bb func void (unsigned int, const char *) Map2CheckFunctions.bc pass -map2check_wasm_check_access func void (uint64_t, uint64_t) AnalysisModeMemtrack.bc pass -map2check_wasm_free func void (uint64_t, int) AnalysisModeMemtrack.bc pass -map2check_wasm_malloc func void (uint64_t, uint64_t) AnalysisModeMemtrack.bc pass -mark_allocation_log func char (MAP2CHECK_CONTAINER *, long) AllocationLog.bc - -mark_deallocation_log func char (MAP2CHECK_CONTAINER *, long) AllocationLog.bc - -mark_heap_log func char (MAP2CHECK_CONTAINER *, MEMORY_HEAP_ROW) HeapLog.bc - -new_container func MAP2CHECK_CONTAINER (enum Container_Type) ContainerBTree.bc+ContainerRealloc.bc runtime -new_heap_row func MEMORY_HEAP_ROW (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) ListLog.bc runtime -new_memory_row func MEMORY_ALLOCATIONS_ROW (long, char) AllocationLog.bc runtime -new_nondet_call func NONDET_CALL (enum NONDET_TYPE, unsigned int, unsigned int, void *, const char *, unsigned int) NonDetLog.bc - -nondet_assume func void (int) NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime -nondet_cancel func void () NonDetGeneratorAFL.bc - -nondet_destroy func void () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime -nondet_generate_aux_witness_files func void () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime -nondet_init func void () NonDetGeneratorAFL.bc+NonDetGeneratorKlee.bc+NonDetGeneratorNone.bc runtime -nondet_log data MAP2CHECK_CONTAINER NonDetLog.bc - -nondet_log_destroy func void () NonDetLog.bc runtime -nondet_log_init func void () NonDetLog.bc runtime -nondet_log_to_file func char (MAP2CHECK_CONTAINER) NonDetLog.bc runtime -overflowError func void (unsigned int, const char *) AnalysisModeOverflow.bc pass -release_unreleased_addresses func void (MAP2CHECK_CONTAINER *) AllocationLog.bc - -sv_comp data char Map2CheckFunctions.bc - -trackbb_log data MAP2CHECK_CONTAINER Map2CheckFunctions.bc - -trackbb_log_file data const char * TrackBBLog.bc - -trackbb_log_to_file func void (MAP2CHECK_CONTAINER *) TrackBBLog.bc runtime -trackbb_new_row func TRACK_BB_ROW (unsigned int, const char *) TrackBBLog.bc runtime -unknown_not_supported func void () AnalysisModeOverflow.bc - -update_reference_list_log func void (long, enum MemoryAddressStatus, unsigned int) AnalysisModeMemcleanup.bc+AnalysisModeMemtrack.bc - -valid_allocation_log func long (MAP2CHECK_CONTAINER *) AllocationLog.bc runtime -violated_property_file data const char * PropertyGenerator.bc - -violated_property_file_assert data const char * PropertyGenerator.bc - -violated_property_file_deref data const char * PropertyGenerator.bc - -violated_property_file_free data const char * PropertyGenerator.bc - -violated_property_file_memcleanup data const char * PropertyGenerator.bc - -violated_property_file_memtrack data const char * PropertyGenerator.bc - -violated_property_file_overflow data const char * PropertyGenerator.bc - -violated_property_file_unknown data const char * PropertyGenerator.bc - -w2c_wasi__snapshot__preview1_proc_exit func void (struct w2c_wasi__snapshot__preview1 *, uint32_t) WasmRuntimeStubs.bc - -wasm_rt_allocate_funcref_table func void (wasm_rt_funcref_table_t *, uint32_t, uint32_t) WasmRuntimeStubs.bc - -wasm_rt_allocate_memory func void (wasm_rt_memory_t *, uint64_t, uint64_t, bool, uint32_t) WasmRuntimeStubs.bc - -wasm_rt_free func void (void) WasmRuntimeStubs.bc - -wasm_rt_free_funcref_table func void (wasm_rt_funcref_table_t *) WasmRuntimeStubs.bc - -wasm_rt_free_memory func void (wasm_rt_memory_t *) WasmRuntimeStubs.bc - -wasm_rt_grow_memory func uint64_t (wasm_rt_memory_t *, uint64_t) WasmRuntimeStubs.bc - -wasm_rt_init func void (void) WasmRuntimeStubs.bc - -wasm_rt_is_initialized func bool (void) WasmRuntimeStubs.bc - -wasm_rt_trap func void (wasm_rt_trap_t) WasmRuntimeStubs.bc - -write_property func void (enum ViolatedProperty, int, const char *) PropertyGenerator.bc runtime -write_property_assert func void (int, const char *) PropertyGenerator.bc - -write_property_deref func void (int, const char *) PropertyGenerator.bc runtime -write_property_free func void (int, const char *) PropertyGenerator.bc runtime -write_property_memcleanup func void () PropertyGenerator.bc runtime -write_property_memtrack func void () PropertyGenerator.bc runtime -write_property_overflow func void (int, const char *) PropertyGenerator.bc - -write_property_unknown func void () PropertyGenerator.bc runtime +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/rust-gap.tsv b/docs/runtime-abi/rust-gap.tsv index 153e03d..0301db9 100644 --- a/docs/runtime-abi/rust-gap.tsv +++ b/docs/runtime-abi/rust-gap.tsv @@ -1,193 +1,193 @@ -symbol status rust_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 different void (int32_t) -__VERIFIER_error different 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 different void (const void *, uint32_t, uint32_t, const char *) -map2check_crab_assume missing - -map2check_destroy missing - -map2check_error missing - -map2check_exit missing - -map2check_free missing - -map2check_free_resolved_address missing - -map2check_function missing - -map2check_get_current_step missing - -map2check_init different void (void) -map2check_load missing - -map2check_malloc missing - -map2check_next_current_step missing - -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 missing - -map2check_nondet_char missing - -map2check_nondet_double missing - -map2check_nondet_float missing - -map2check_nondet_get_log missing - -map2check_nondet_int missing - -map2check_nondet_loff_t missing - -map2check_nondet_long missing - -map2check_nondet_longlong missing - -map2check_nondet_memory_byte missing - -map2check_nondet_pchar missing - -map2check_nondet_pointer missing - -map2check_nondet_sector_t missing - -map2check_nondet_short missing - -map2check_nondet_size_t missing - -map2check_nondet_u16 missing - -map2check_nondet_u32 missing - -map2check_nondet_u8 missing - -map2check_nondet_uchar missing - -map2check_nondet_uint missing - -map2check_nondet_ulong missing - -map2check_nondet_ulonglong missing - -map2check_nondet_unsigned missing - -map2check_nondet_ushort missing - -map2check_posix missing - -map2check_success different void (void) -map2check_target_function missing - -map2check_track_bb missing - -map2check_wasm_check_access same void (uint64_t, uint64_t) -map2check_wasm_free different void (uint64_t, int64_t) -map2check_wasm_malloc same void (uint64_t, uint64_t) -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 missing - -nondet_log_init missing - -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 different (not declared in header) -wasm_rt_allocate_funcref_table different (not declared in header) -wasm_rt_allocate_memory different (not declared in header) -wasm_rt_free different (not declared in header) -wasm_rt_free_funcref_table different (not declared in header) -wasm_rt_free_memory different (not declared in header) -wasm_rt_grow_memory different (not declared in header) -wasm_rt_init different (not declared in header) -wasm_rt_is_initialized different (not declared in header) -wasm_rt_trap different (not declared in header) -write_property missing - -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 - +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 same void (int32_t) void (int) +__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 different void (const void *, uint32_t, uint32_t, const char *) void (const void *, unsigned int, unsigned int, const char *) +map2check_crab_assume missing - - +map2check_destroy missing - - +map2check_error missing - - +map2check_exit missing - - +map2check_free missing - - +map2check_free_resolved_address missing - - +map2check_function missing - - +map2check_get_current_step missing - - +map2check_init different void (void) void (void) +map2check_load missing - - +map2check_malloc missing - - +map2check_next_current_step missing - - +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 missing - - +map2check_nondet_char missing - - +map2check_nondet_double missing - - +map2check_nondet_float missing - - +map2check_nondet_get_log missing - - +map2check_nondet_int missing - - +map2check_nondet_loff_t missing - - +map2check_nondet_long missing - - +map2check_nondet_longlong missing - - +map2check_nondet_memory_byte missing - - +map2check_nondet_pchar missing - - +map2check_nondet_pointer missing - - +map2check_nondet_sector_t missing - - +map2check_nondet_short missing - - +map2check_nondet_size_t missing - - +map2check_nondet_u16 missing - - +map2check_nondet_u32 missing - - +map2check_nondet_u8 missing - - +map2check_nondet_uchar missing - - +map2check_nondet_uint missing - - +map2check_nondet_ulong missing - - +map2check_nondet_ulonglong missing - - +map2check_nondet_unsigned missing - - +map2check_nondet_ushort missing - - +map2check_posix missing - - +map2check_success same void (void) void (void) +map2check_target_function missing - - +map2check_track_bb missing - - +map2check_wasm_check_access same void (uint64_t, uint64_t) void (unsigned long, unsigned long) +map2check_wasm_free different void (uint64_t, int64_t) void (unsigned long, long) +map2check_wasm_malloc same void (uint64_t, uint64_t) void (unsigned long, unsigned long) +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 missing - - +nondet_log_init missing - - +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 undeclared - - +wasm_rt_allocate_funcref_table undeclared - - +wasm_rt_allocate_memory undeclared - - +wasm_rt_free undeclared - - +wasm_rt_free_funcref_table undeclared - - +wasm_rt_free_memory undeclared - - +wasm_rt_grow_memory undeclared - - +wasm_rt_init undeclared - - +wasm_rt_is_initialized undeclared - - +wasm_rt_trap undeclared - - +write_property missing - - +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/scripts/runtime-abi-inventory.py b/scripts/runtime-abi-inventory.py index f83f1fc..87114bd 100755 --- a/scripts/runtime-abi-inventory.py +++ b/scripts/runtime-abi-inventory.py @@ -4,19 +4,29 @@ Writes two tab-separated files to --out-dir, sorted by symbol: - inventory.tsv symbol kind signature module referenced_by - rust-gap.tsv symbol status rust_signature + inventory.tsv symbol kind signature canonical_signature module referenced_by + rust-gap.tsv symbol status rust_signature rust_canonical_signature A row exists for every external symbol defined (nm types T/D/B/R/C/V/W) by an object compiled from modules/backend/library/lib/*.c. `signature` is the -`type.qualType` clang prints in its JSON AST for the definition; `module` lists -the .bc files that define it (joined by '+'); `referenced_by` is a subset of -pass+frontend+runtime ('-' when empty): +`type.qualType` clang prints in its JSON AST for the definition, typedef names +kept as written. `canonical_signature` is the same type with every typedef +resolved to its underlying type (recursively, from the TypedefDecls of the same +translation unit), a bare `bool` that is not a typedef written `_Bool`, and an +empty parameter list `T ()` written `T (void)`. Struct, union and enum types +stay `struct `; clang already names an anonymous typedef'd struct by its +typedef name, so `typedef struct {...} X` reads `struct X`. When files define +a symbol with different types, the variants are joined with ' | ' (only +`main`). `module` lists the .bc files that define it (joined by '+'); +`referenced_by` is a subset of pass+frontend+runtime+external ('-' when +empty): pass the name is a string literal (or the expansion of a `"prefix" #arg` macro body for every invocation) in modules/backend/pass/*.{cpp,hpp} frontend the name is a string literal in modules/frontend/*.{cpp,hpp} runtime the symbol is undefined in one runtime object and defined in another + external the symbol is consumed by code neither the passes nor the frontend + emit; decided only by the EXTERNAL_PREFIXES/EXTERNAL_NAMES rules WasmRuntimeStubs.c includes wasm-rt.h from WABT. When the host has no WABT the script falls back (-idirafter, so a real wasm-rt.h always wins) to a minimal @@ -24,9 +34,14 @@ file uses, so the qualType spelling is the same as with WABT. rust-gap.tsv compares each inventory symbol with the external symbols defined -in --rust-lib (nm) and the declarations of --rust-header (clang JSON AST): -`same` needs an equal qualType, `different` otherwise, `missing` when the -archive does not define the symbol. +in --rust-lib (nm) and the declarations of --rust-header (clang JSON AST), +canonicalized the same way: `same` when the canonical signatures are equal +(one of them, for a symbol with several variants), `different` otherwise, +`missing` when the archive does not define the symbol and `undeclared` when the +archive defines it but the header does not declare it. + +A symbol defined by a runtime object with no top-level declaration in the +clang AST (e.g. one defined in top-level asm) is an error. Regenerate the committed files from the repository root with: @@ -48,6 +63,28 @@ import tempfile DEFINED_TYPES = set("TDBRCVW") + +# `external` consumers: code that is neither emitted by a pass nor named by the +# frontend, yet links against a runtime definition by name. +EXTERNAL_PREFIXES = ( + # wasm2c-lifted programs call the wasm-rt API (WasmRuntimeStubs.c replaces + # WABT's wasm-rt-impl) and the WASI imports, which are named w2c__*. + "wasm_rt_", + "w2c_", + # AFL++: __AFL_FUZZ_INIT() (NonDetGeneratorAFL.c) defines __afl_* objects + # that afl-compiler-rt and the __AFL_FUZZ_TESTCASE_* macros read. + "__afl_", +) +EXTERNAL_NAMES = ( + # Process entry point: called by the C startup files (native/AFL builds) + # and by KLEE, which starts every run at main. + "main", +) +C_KEYWORDS = { + "void", "char", "short", "int", "long", "float", "double", "signed", + "unsigned", "_Bool", "const", "volatile", "restrict", "struct", "union", + "enum", "__int128", "_Complex", +} WASM_SHIM = """\ /* Minimal stand-in for WABT's wasm-rt.h (runtime-abi-inventory.py). */ #ifndef WASM_RT_SHIM_H @@ -149,11 +186,83 @@ def nm_symbols(path): return defined, undefined -def ast_declarations(source, flags): - """Map name -> (kind, qualType) of top-level declarations; definitions win.""" +class Canonicalizer: + """Resolve typedef names in clang type strings, using one TU's TypedefDecls.""" + + TOKEN_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*|[0-9]+|\S") + + def __init__(self, typedefs): + self.typedefs = typedefs + self.cache = {} + + def resolve(self, name, seen=()): + if name not in self.cache: + if name in seen: + raise ValueError("typedef cycle at %s" % name) + self.cache[name] = self.canonical(self.typedefs[name], seen + (name,)) + return self.cache[name] + + def canonical(self, type_str, seen=()): + tokens = self.TOKEN_RE.findall(type_str) + out, i = [], 0 + while i < len(tokens): + tok = tokens[i] + if tok in ("struct", "union", "enum") and i + 1 < len(tokens): + out += [tok, tokens[i + 1]] + i += 2 + continue + if tok in self.typedefs and tok not in C_KEYWORDS: + under = self.resolve(tok, seen) + quals = [] + while out and out[-1] in ("const", "volatile"): + quals.insert(0, out.pop()) + if any(c in under for c in "[("): + raise ValueError("cannot inline typedef %s = %s in %r" % (tok, under, type_str)) + if quals and under.endswith("*"): + out += self.TOKEN_RE.findall(under) + quals # const pointer -> void *const + else: + out += quals + self.TOKEN_RE.findall(under) + i += 1 + continue + out.append("_Bool" if tok == "bool" else tok) + i += 1 + return self.join(out) + + @staticmethod + def join(tokens): + text = "" + for tok in tokens: + if not text: + text = tok + elif tok in (",", ")", "]", "["): + text += tok + elif tok == "(": + text += tok if text.endswith(("*", "(", ")")) else " " + tok + elif tok == "*": + text += tok if text.endswith(("*", "(")) else " " + tok + elif text.endswith(("(", "[", "*")): + text += tok + else: + text += " " + tok + return text + + +def canonical_signature(canon, kind, qual_type): + sig = canon.canonical(qual_type) + if kind == "func" and sig.endswith("()"): + sig = sig[:-2] + "(void)" + return sig + + +def ast_declarations(source, flags, wanted): + """Map name -> (kind, qualType, canonical) of the top-level declarations of the + names in `wanted`; definitions win over plain declarations.""" dump = run(["clang", "-w", "-fsyntax-only", "-Xclang", "-ast-dump=json"] + flags + [source]) + nodes = json.loads(dump).get("inner", []) + canon = Canonicalizer({n["name"]: n["type"]["qualType"] for n in nodes + if n.get("kind") == "TypedefDecl" and "name" in n}) decls = {} - for node in json.loads(dump).get("inner", []): + for node in nodes: if node.get("kind") not in ("FunctionDecl", "VarDecl") or "name" not in node: continue kind = "func" if node["kind"] == "FunctionDecl" else "data" @@ -164,7 +273,8 @@ def ast_declarations(source, flags): name = node["name"] if is_def or name not in decls or not decls[name][2]: decls[name] = (kind, node["type"]["qualType"], is_def) - return {name: (kind, sig) for name, (kind, sig, _) in decls.items()} + return {name: (kind, sig, canonical_signature(canon, kind, sig)) + for name, (kind, sig, _) in decls.items() if name in wanted} def compile_runtime(lib_dir, header_dir, work): @@ -180,7 +290,12 @@ def compile_runtime(lib_dir, header_dir, work): obj = os.path.join(work, base + ".o") run(["clang", "-w", "-c", "-O0"] + flags + ["-o", obj, source]) defined, undefined = nm_symbols(obj) - modules[base + ".bc"] = (defined, undefined, ast_declarations(source, flags)) + decls = ast_declarations(source, flags, defined) + orphans = sorted(set(defined) - set(decls)) + if orphans: + raise SystemExit("error: %s defines %s with no top-level declaration in the clang AST" + % (source, ", ".join(orphans))) + modules[base + ".bc"] = (defined, undefined, decls) return modules @@ -198,12 +313,14 @@ def sources(directory): rows = {} for module, (defined, _, decls) in modules.items(): for name, nm_type in defined.items(): - row = rows.setdefault(name, {"modules": [], "sigs": [], "kind": None}) + row = rows.setdefault(name, {"modules": [], "sigs": [], "canons": [], "kind": None}) row["modules"].append(module) - kind, sig = decls.get(name, ("func" if nm_type == "T" else "data", "?")) + kind, sig, canon = decls[name] row["kind"] = row["kind"] or kind if sig not in row["sigs"]: row["sigs"].append(sig) + if canon not in row["canons"]: + row["canons"].append(canon) for name, row in rows.items(): refs = [] @@ -214,24 +331,27 @@ def sources(directory): if any(name in undefined and module not in row["modules"] for module, (_, undefined, _) in modules.items()): refs.append("runtime") + if name.startswith(EXTERNAL_PREFIXES) or name in EXTERNAL_NAMES: + refs.append("external") row["refs"] = "+".join(refs) or "-" row["signature"] = " | ".join(row["sigs"]) + row["canonical"] = " | ".join(row["canons"]) unresolved = sorted(pass_names - set(rows)) return rows, unresolved def rust_gap(rows, rust_lib, rust_header): defined, _ = nm_symbols(rust_lib) - decls = ast_declarations(rust_header, ["-x", "c"]) + decls = ast_declarations(rust_header, ["-x", "c"], rows) gap = {} for name, row in rows.items(): if name not in defined: - gap[name] = ("missing", "-") + gap[name] = ("missing", "-", "-") elif name not in decls: - gap[name] = ("different", "(not declared in header)") + gap[name] = ("undeclared", "-", "-") else: - sig = decls[name][1] - gap[name] = ("same" if sig == row["signature"] else "different", sig) + _, sig, canon = decls[name] + gap[name] = ("same" if canon in row["canons"] else "different", sig, canon) return gap @@ -258,15 +378,16 @@ def main(argv=None): os.makedirs(args.out_dir, exist_ok=True) names = sorted(rows) write_tsv(os.path.join(args.out_dir, "inventory.tsv"), - ["symbol", "kind", "signature", "module", "referenced_by"], - ([n, rows[n]["kind"], rows[n]["signature"], "+".join(sorted(rows[n]["modules"])), rows[n]["refs"]] + ["symbol", "kind", "signature", "canonical_signature", "module", "referenced_by"], + ([n, rows[n]["kind"], rows[n]["signature"], rows[n]["canonical"], "+".join(sorted(rows[n]["modules"])), rows[n]["refs"]] for n in names)) - write_tsv(os.path.join(args.out_dir, "rust-gap.tsv"), ["symbol", "status", "rust_signature"], - ([n, gap[n][0], gap[n][1]] for n in names)) + write_tsv(os.path.join(args.out_dir, "rust-gap.tsv"), ["symbol", "status", "rust_signature", "rust_canonical_signature"], + ([n] + list(gap[n]) for n in names)) - counts = {s: sum(1 for g in gap.values() if g[0] == s) for s in ("same", "different", "missing")} + counts = {s: sum(1 for g in gap.values() if g[0] == s) + for s in ("same", "different", "missing", "undeclared")} print("defined: %d" % len(rows)) - print("rust: same %(same)d, different %(different)d, missing %(missing)d" % counts) + print("rust: same %(same)d, different %(different)d, missing %(missing)d, undeclared %(undeclared)d" % counts) print("pass-referenced names not defined by the runtime (%d):" % len(unresolved)) for name in unresolved: print(" " + name) diff --git a/scripts/test_runtime_abi_inventory.py b/scripts/test_runtime_abi_inventory.py index dd48c18..f47b45a 100644 --- a/scripts/test_runtime_abi_inventory.py +++ b/scripts/test_runtime_abi_inventory.py @@ -1,9 +1,15 @@ # SPDX-License-Identifier: GPL-3.0-only """Tests for scripts/runtime-abi-inventory.py. -Run with: python3 -I -m unittest scripts/test_runtime_abi_inventory.py +Run from the repository root with: + + python3 -I -m unittest discover -s scripts -p test_runtime_abi_inventory.py + +(`python3 -I -m unittest scripts/test_runtime_abi_inventory.py` does not work: +-I drops the current directory from sys.path, so the module cannot be found.) """ +import importlib.util import os import shutil import subprocess @@ -26,6 +32,10 @@ def build_fixture(root): """A minimal Map2Check tree: two runtime .c, one header, one pass, one frontend file.""" lib = os.path.join(root, "modules", "backend", "library") write(os.path.join(lib, "header", "Fix.h"), """ + typedef int bool; + typedef void *pointer; + typedef struct { int a; } ANON; + typedef struct NAMED { int b; } NAMED; int map2check_x(int a, const char *s); extern int shared_counter; void helper(void); @@ -37,6 +47,9 @@ def build_fixture(root): int map2check_x(int a, const char *s) { helper(); return a + hidden() + (s != 0); } int map2check_non_det_int(void) { return 1; } unsigned map2check_non_det_unsigned(void) { return 2u; } + bool map2check_non_det_bool(void) { return 1; } + pointer map2check_ptr() { return 0; } + ANON take_anon(ANON a, NAMED *n, const pointer p) { (void)n; (void)p; return a; } """) write(os.path.join(lib, "lib", "B.c"), """ #include "../header/Fix.h" @@ -44,6 +57,12 @@ def build_fixture(root): int map2check_non_det_int(void) { return 0; } long unused_fn(long x) { return x; } """) + write(os.path.join(lib, "lib", "W.c"), """ + #include + int __afl_sharedmem_fuzzing = 1; + bool wasm_rt_is_initialized(void) { return 1; } + void w2c_env_exit(int code) { (void)code; } + """) write(os.path.join(root, "modules", "backend", "pass", "P.cpp"), """ // unused_fn is only mentioned in a comment and must not count. /* "unused_fn" neither. */ @@ -60,6 +79,8 @@ def build_fixture(root): void P::run(CallInst *callInst) { CALL_IF_HELPER(Integer, INTEGER, int) CALL_IF_HELPER(Unsigned, UNSIGNED, unsigned) + CALL_IF_HELPER(Bool, BOOL, bool) + auto p = F.getParent()->getOrInsertFunction("map2check_ptr", PtrTy); } """) write(os.path.join(root, "modules", "frontend", "F.cpp"), """ @@ -71,13 +92,21 @@ def build_rust_side(root, work): """A stand-in for libmap2check.a and include/map2check.h.""" header = os.path.join(root, "rust", "map2check.h") write(header, """ - int map2check_x(int a, const char *s); + #include + #include + int32_t map2check_x(int32_t a, const char *s); long map2check_non_det_int(void); + bool map2check_non_det_bool(void); + void *map2check_ptr(void); """) source = os.path.join(work, "rustlib.c") write(source, """ + #include int map2check_x(int a, const char *s) { return a + (s != 0); } long map2check_non_det_int(void) { return 0; } + bool map2check_non_det_bool(void) { return 1; } + void *map2check_ptr(void) { return 0; } + bool wasm_rt_is_initialized(void) { return 1; } """) obj = os.path.join(work, "rustlib.o") archive = os.path.join(root, "rust", "libfake.a") @@ -112,31 +141,93 @@ def test_exit_status(self): self.assertEqual(self.result.returncode, 0, self.result.stderr) def test_inventory_rows(self): + self.maxDiff = None self.assertEqual(read_lines(os.path.join(self.out, "inventory.tsv")), [ - "symbol\tkind\tsignature\tmodule\treferenced_by", - "helper\tfunc\tvoid (void)\tB.bc\truntime", - "map2check_non_det_int\tfunc\tint (void)\tA.bc+B.bc\tpass", - "map2check_non_det_unsigned\tfunc\tunsigned int (void)\tA.bc\tpass", - "map2check_x\tfunc\tint (int, const char *)\tA.bc\tpass", - "shared_counter\tdata\tint\tA.bc\tfrontend", - "unused_fn\tfunc\tlong (long)\tB.bc\t-", + "symbol\tkind\tsignature\tcanonical_signature\tmodule\treferenced_by", + "__afl_sharedmem_fuzzing\tdata\tint\tint\tW.bc\texternal", + "helper\tfunc\tvoid (void)\tvoid (void)\tB.bc\truntime", + "map2check_non_det_bool\tfunc\tbool (void)\tint (void)\tA.bc\tpass", + "map2check_non_det_int\tfunc\tint (void)\tint (void)\tA.bc+B.bc\tpass", + "map2check_non_det_unsigned\tfunc\tunsigned int (void)\tunsigned int (void)\tA.bc\tpass", + "map2check_ptr\tfunc\tpointer ()\tvoid *(void)\tA.bc\tpass", + "map2check_x\tfunc\tint (int, const char *)\tint (int, const char *)\tA.bc\tpass", + "shared_counter\tdata\tint\tint\tA.bc\tfrontend", + "take_anon\tfunc\tANON (ANON, NAMED *, const pointer)" + "\tstruct ANON (struct ANON, struct NAMED *, void *const)\tA.bc\t-", + "unused_fn\tfunc\tlong (long)\tlong (long)\tB.bc\t-", + "w2c_env_exit\tfunc\tvoid (int)\tvoid (int)\tW.bc\texternal", + "wasm_rt_is_initialized\tfunc\tbool (void)\t_Bool (void)\tW.bc\texternal", ]) def test_rust_gap_rows(self): self.assertEqual(read_lines(os.path.join(self.out, "rust-gap.tsv")), [ - "symbol\tstatus\trust_signature", - "helper\tmissing\t-", - "map2check_non_det_int\tdifferent\tlong (void)", - "map2check_non_det_unsigned\tmissing\t-", - "map2check_x\tsame\tint (int, const char *)", - "shared_counter\tmissing\t-", - "unused_fn\tmissing\t-", + "symbol\tstatus\trust_signature\trust_canonical_signature", + "__afl_sharedmem_fuzzing\tmissing\t-\t-", + "helper\tmissing\t-\t-", + "map2check_non_det_bool\tdifferent\t_Bool (void)\t_Bool (void)", + "map2check_non_det_int\tdifferent\tlong (void)\tlong (void)", + "map2check_non_det_unsigned\tmissing\t-\t-", + "map2check_ptr\tsame\tvoid *(void)\tvoid *(void)", + "map2check_x\tsame\tint32_t (int32_t, const char *)\tint (int, const char *)", + "shared_counter\tmissing\t-\t-", + "take_anon\tmissing\t-\t-", + "unused_fn\tmissing\t-\t-", + "w2c_env_exit\tmissing\t-\t-", + "wasm_rt_is_initialized\tundeclared\t-\t-", ]) def test_reports_unresolved_pass_names(self): self.assertIn("map2check_missing", self.result.stdout) self.assertIn("__VERIFIER_nondet_unsigned", self.result.stdout) - self.assertIn("defined: 6", self.result.stdout) + self.assertIn("defined: 12", self.result.stdout) + + +class CanonicalizerTest(unittest.TestCase): + """The type printer must reproduce clang's spelling when no typedef is involved.""" + + def setUp(self): + spec = importlib.util.spec_from_file_location("inventory", SCRIPT) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + self.module = module + + def test_round_trip_without_typedefs(self): + canon = self.module.Canonicalizer({}) + for text in ("void (*)(int)", "int (*(*)(void))[3]", "const char *const *", + "unsigned char[1048576]", "void *(unsigned int, struct X)", + "char **(void)", "int (int, char **)", "enum E (union U *)"): + self.assertEqual(canon.canonical(text), text) + + def test_typedefs_resolved(self): + canon = self.module.Canonicalizer({ + "pointer": "void *", "u8": "unsigned char", "ROW": "struct ROW", + "ROWP": "ROW *", "bool": "int"}) + self.assertEqual(canon.canonical("const pointer"), "void *const") + self.assertEqual(canon.canonical("ROWP (const u8 *, bool)"), + "struct ROW *(const unsigned char *, int)") + self.assertEqual(self.module.canonical_signature(canon, "func", "pointer ()"), "void *(void)") + self.assertEqual(self.module.Canonicalizer({}).canonical("bool (void)"), "_Bool (void)") + + +@unittest.skipUnless(all(shutil.which(t) for t in TOOLS), "needs clang, nm and ar") +class MissingDeclarationTest(unittest.TestCase): + def test_symbol_without_declaration_fails(self): + with tempfile.TemporaryDirectory() as tmp: + root = os.path.join(tmp, "map2check") + build_fixture(root) + archive, header = build_rust_side(root, tmp) + write(os.path.join(root, "modules", "backend", "library", "lib", "Asm.c"), """ + __asm__(".globl asm_only\\nasm_only:\\n ret\\n"); + """) + result = subprocess.run( + [sys.executable, "-I", SCRIPT, "--map2check", root, + "--out-dir", os.path.join(tmp, "out"), + "--rust-lib", archive, "--rust-header", header], + capture_output=True, text=True) + self.assertNotEqual(result.returncode, 0) + self.assertIn("asm_only", result.stderr) + self.assertIn("Asm.c", result.stderr) + self.assertFalse(os.path.exists(os.path.join(tmp, "out", "inventory.tsv"))) if __name__ == "__main__": From 3301cedff1472ba8676650224cc65d67e360fd23 Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 13:25:28 -0400 Subject: [PATCH 08/74] test: build Map2Check develop from the read-only vendor copy --- scripts/it/Dockerfile.time | 10 ++++++ scripts/it/build-map2check.sh | 49 +++++++++++++++++++++++++++++ scripts/it/test-build-map2check.sh | 50 ++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 scripts/it/Dockerfile.time create mode 100755 scripts/it/build-map2check.sh create mode 100755 scripts/it/test-build-map2check.sh 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..4cc89f6 --- /dev/null +++ b/scripts/it/build-map2check.sh @@ -0,0 +1,49 @@ +#!/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 + ' diff --git a/scripts/it/test-build-map2check.sh b/scripts/it/test-build-map2check.sh new file mode 100755 index 0000000..0ef5ed2 --- /dev/null +++ b/scripts/it/test-build-map2check.sh @@ -0,0 +1,50 @@ +#!/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" + +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 From 2e068416feb60a30393a5456dfde55915a3383cc Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 15:49:45 -0400 Subject: [PATCH 09/74] test: corpus runner, gate comparator and C baseline for Map2Check develop --- docs/cutover/baselines/c-3e914528ec57.tsv | 616 ++++++++++++++++++++++ docs/cutover/deltas-aceitos.tsv | 1 + scripts/it/build-map2check.sh | 8 + scripts/it/compare_baselines.py | 266 ++++++++++ scripts/it/run-corpus.sh | 195 +++++++ scripts/it/test-build-map2check.sh | 3 + scripts/it/test_compare_baselines.py | 321 +++++++++++ 7 files changed, 1410 insertions(+) create mode 100644 docs/cutover/baselines/c-3e914528ec57.tsv create mode 100644 docs/cutover/deltas-aceitos.tsv create mode 100644 scripts/it/compare_baselines.py create mode 100755 scripts/it/run-corpus.sh create mode 100644 scripts/it/test_compare_baselines.py 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/deltas-aceitos.tsv b/docs/cutover/deltas-aceitos.tsv new file mode 100644 index 0000000..1a3be47 --- /dev/null +++ b/docs/cutover/deltas-aceitos.tsv @@ -0,0 +1 @@ +task prp gen field c_value rust_value sd_ref diff --git a/scripts/it/build-map2check.sh b/scripts/it/build-map2check.sh index 4cc89f6..f4680c6 100755 --- a/scripts/it/build-map2check.sh +++ b/scripts/it/build-map2check.sh @@ -46,4 +46,12 @@ docker run --rm --user "$(id -u):$(id -g)" -e HOME=/out/home \ # 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/compare_baselines.py b/scripts/it/compare_baselines.py new file mode 100644 index 0000000..61f8cf1 --- /dev/null +++ b/scripts/it/compare_baselines.py @@ -0,0 +1,266 @@ +#!/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. + +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 + 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 + 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:`. Ú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 +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 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), + "rss": statistics.median(float(r["maxrss_kb"]) for r in 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): + return out + 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("'\"")) + 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) + + 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)) + + 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(field): + out = [] + for key in common: + if c[key][field] > 0: + out.append((r[key][field] / c[key][field], key)) + return out + + for field, name, limit in (("wall", "wall", WALL_MAX), ("rss", "rss", RSS_MAX)): + rs = ratios(field) + med = statistics.median(x for x, _ in rs) if rs else 1.0 + report(name, med <= limit + EPS, "(mediana %.3f, limite %.2f, %d chaves)" % (med, limit, len(rs))) + for x, key in rs: + if x > WARN_RATIO + EPS: + lines.append("warning: %s %s %s razão %s %.2f > %.1f" % (key + (name, 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/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/test-build-map2check.sh b/scripts/it/test-build-map2check.sh index 0ef5ed2..52f9d70 100755 --- a/scripts/it/test-build-map2check.sh +++ b/scripts/it/test-build-map2check.sh @@ -32,6 +32,9 @@ after="$(manifest | sha256sum)" || 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" diff --git a/scripts/it/test_compare_baselines.py b/scripts/it/test_compare_baselines.py new file mode 100644 index 0000000..c77bb33 --- /dev/null +++ b/scripts/it/test_compare_baselines.py @@ -0,0 +1,321 @@ +# SPDX-License-Identifier: GPL-3.0-only +"""Testes de scripts/it/compare_baselines.py (um caso por critério do gate). + +Rodar da raiz do repositório: + + python3 -I -m unittest discover -s scripts/it -p 'test_*.py' + +Com -I o diretório corrente sai do sys.path, então o módulo é carregado pelo +caminho explícito, ao lado deste arquivo. +""" + +import contextlib +import importlib.util +import io +import os +import shutil +import tempfile +import unittest + +HERE = os.path.dirname(os.path.abspath(__file__)) +_spec = importlib.util.spec_from_file_location( + "compare_baselines", os.path.join(HERE, "compare_baselines.py")) +cb = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(cb) + +HEADER = "task\tprp\tgen\trep\tverdict\ttag\tline\tfunction\twitness\twall_s\tmaxrss_kb" +DELTAS_HEADER = "task\tprp\tgen\tfield\tc_value\trust_value\tsd_ref" +SEMANTIC = """# Semantic deltas + +### SD-03 — Violations do not stop the analysis; the first one is final + +text + +### SD-07 — `__VERIFIER_assume(0)` abandons the fuzzer input +""" + + +def rows(task, prp="valid-memsafety", gen="afl", verdicts=("SUCCEEDED",) * 5, + tag="", line="", function="", witness=None, wall=10.0, rss=1000): + """Uma linha TSV por repetição. wall/rss podem ser escalares ou listas.""" + out = [] + for i, v in enumerate(verdicts): + failed = v == "FAILED" + w = witness if witness is not None else (1 if v in ("FAILED", "SUCCEEDED") else 0) + wl = wall[i] if isinstance(wall, (list, tuple)) else wall + rs = rss[i] if isinstance(rss, (list, tuple)) else rss + out.append("\t".join([ + task, prp, gen, str(i + 1), v, + tag if failed else "", str(line) if failed else "", + function if failed else "", str(w), "%.2f" % wl, str(rs)])) + return out + + +class CompareBaselinesTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.mkdtemp() + self.semantic = self._write("semantic-deltas.md", SEMANTIC) + self.deltas = self._write("deltas.tsv", DELTAS_HEADER + "\n") + self.corpus = os.path.join(self.tmp, "corpus") + os.makedirs(os.path.join(self.corpus, "d")) + self._yml("d/safe", [("valid-memsafety", "true", None)]) + self._yml("d/bug", [("valid-memsafety", "false", "valid-free")]) + self._yml("d/reach", [("unreach-call", "false", None)]) + + def tearDown(self): + shutil.rmtree(self.tmp) + + def _write(self, name, text): + path = os.path.join(self.tmp, name) + with open(path, "w", encoding="utf-8") as f: + f.write(text) + return path + + def _yml(self, task, props): + lines = ["format_version: '1.0'", "", "input_files: 'x.i'", "", "properties:"] + for prp, verdict, sub in props: + lines.append(" - property_file: ../properties/%s.prp" % prp) + lines.append(" expected_verdict: %s" % verdict) + if sub: + lines.append(" subproperty: %s" % sub) + with open(os.path.join(self.corpus, task + ".yml"), "w", encoding="utf-8") as f: + f.write("\n".join(lines) + "\n") + + def _tsv(self, name, lines): + return self._write(name, HEADER + "\n" + "\n".join(lines) + "\n") + + def _deltas(self, entries): + return self._write("deltas.tsv", DELTAS_HEADER + "\n" + + "".join("\t".join(e) + "\n" for e in entries)) + + def run_gate(self, c_lines, rust_lines): + c = self._tsv("c.tsv", c_lines) + r = self._tsv("rust.tsv", rust_lines) + buf = io.StringIO() + with contextlib.redirect_stdout(buf): + code = cb.main([c, r, "--deltas", self.deltas, + "--semantic-deltas", self.semantic, + "--corpus", self.corpus]) + out = buf.getvalue().splitlines() + return code, out + + def criterion(self, out, name): + hits = [l for l in out if l.startswith(name + ":")] + self.assertEqual(len(hits), 1, "criterion %r in %r" % (name, out)) + return hits[0] + + def assertGate(self, code, out, passed): + self.assertEqual(out[-1], "gate: PASS" if passed else "gate: FAIL", out) + self.assertEqual(code, 0 if passed else 1) + + # --- baseline ---------------------------------------------------------- + + def base(self): + return (rows("d/safe") + + rows("d/bug", verdicts=("FAILED",) * 5, tag="FALSE-FREE", + line=12, function="main") + + rows("d/reach", prp="unreach-call", verdicts=("FAILED",) * 5, + tag="FALSE", line=7, function="__VERIFIER_assert")) + + def test_identical_baselines_pass_with_one_line_per_criterion(self): + code, out = self.run_gate(self.base(), self.base()) + self.assertGate(code, out, True) + for name in ("keys", "verdict", "failed-details", "witness", "expected", + "deltas", "wall", "timeouts", "rss"): + self.assertIn("PASS", self.criterion(out, name)) + + def test_missing_key_fails(self): + code, out = self.run_gate(self.base(), rows("d/safe")) + self.assertIn("FAIL", self.criterion(out, "keys")) + self.assertGate(code, out, False) + + # --- veredito por moda -------------------------------------------------- + + def test_verdict_by_mode_of_five_reps_pass(self): + c = rows("d/bug", verdicts=("FAILED",) * 5, tag="FALSE-FREE", line=12, function="main") + r = rows("d/bug", verdicts=("FAILED", "UNKNOWN", "FAILED", "UNKNOWN", "FAILED"), + tag="FALSE-FREE", line=12, function="main") + code, out = self.run_gate(c, r) + self.assertIn("PASS", self.criterion(out, "verdict")) + self.assertGate(code, out, True) + + def test_verdict_by_mode_of_five_reps_fail(self): + c = rows("d/bug", verdicts=("FAILED",) * 5, tag="FALSE-FREE", line=12, function="main") + r = rows("d/bug", verdicts=("FAILED", "UNKNOWN", "UNKNOWN", "UNKNOWN", "FAILED"), + tag="FALSE-FREE", line=12, function="main") + code, out = self.run_gate(c, r) + self.assertIn("FAIL", self.criterion(out, "verdict")) + self.assertGate(code, out, False) + + # --- tag / Line / Function em FAILED ------------------------------------- + + def _failed_detail_case(self, **rust): + args = dict(tag="FALSE-FREE", line=12, function="main") + c = rows("d/bug", verdicts=("FAILED",) * 5, **args) + args.update(rust) + r = rows("d/bug", verdicts=("FAILED",) * 5, **args) + return self.run_gate(c, r) + + def test_failed_same_details_pass(self): + code, out = self._failed_detail_case() + self.assertIn("PASS", self.criterion(out, "failed-details")) + self.assertGate(code, out, True) + + def test_failed_tag_differs_fails(self): + code, out = self._failed_detail_case(tag="FALSE-DEREF") + self.assertIn("FAIL", self.criterion(out, "failed-details")) + self.assertGate(code, out, False) + + def test_failed_line_differs_fails(self): + code, out = self._failed_detail_case(line=13) + self.assertIn("FAIL", self.criterion(out, "failed-details")) + self.assertGate(code, out, False) + + def test_failed_function_differs_fails(self): + code, out = self._failed_detail_case(function="free_all") + self.assertIn("FAIL", self.criterion(out, "failed-details")) + self.assertGate(code, out, False) + + # --- witness ------------------------------------------------------------ + + def test_witness_missing_where_c_had_one_fails(self): + c = rows("d/safe", witness=1) + r = rows("d/safe", witness=0) + code, out = self.run_gate(c, r) + self.assertIn("FAIL", self.criterion(out, "witness")) + self.assertGate(code, out, False) + + def test_extra_witness_passes(self): + c = rows("d/safe", witness=0) + r = rows("d/safe", witness=1) + code, out = self.run_gate(c, r) + self.assertIn("PASS", self.criterion(out, "witness")) + self.assertGate(code, out, True) + + # --- acertos contra expected_verdict ------------------------------------- + + def test_expected_matches_must_not_decrease(self): + # Divergência de veredito aceita por delta válido, para isolar o critério. + self._deltas([("d/safe", "valid-memsafety", "afl", "verdict", + "SUCCEEDED", "UNKNOWN", "SD-03")]) + c = rows("d/safe") + r = rows("d/safe", verdicts=("UNKNOWN",) * 5) + code, out = self.run_gate(c, r) + self.assertIn("PASS", self.criterion(out, "verdict")) + self.assertIn("FAIL", self.criterion(out, "expected")) + self.assertGate(code, out, False) + + def test_expected_matches_increase_passes(self): + self._deltas([("d/safe", "valid-memsafety", "afl", "verdict", + "UNKNOWN", "SUCCEEDED", "SD-03")]) + c = rows("d/safe", verdicts=("UNKNOWN",) * 5) + r = rows("d/safe") + code, out = self.run_gate(c, r) + self.assertIn("PASS", self.criterion(out, "expected")) + self.assertGate(code, out, True) + + def test_expected_subproperty_requires_matching_tag(self): + self.assertTrue(cb.matches_expected(("false", "valid-free"), "FAILED", "FALSE-FREE")) + self.assertFalse(cb.matches_expected(("false", "valid-free"), "FAILED", "FALSE-DEREF")) + self.assertTrue(cb.matches_expected(("false", None), "FAILED", "FALSE")) + self.assertTrue(cb.matches_expected(("true", None), "SUCCEEDED", "")) + self.assertFalse(cb.matches_expected(("true", None), "UNKNOWN", "")) + + # --- deltas aceitos ----------------------------------------------------- + + def test_divergence_accepted_with_existing_sd_ref(self): + self._deltas([("d/bug", "valid-memsafety", "afl", "line", "12", "13", "SD-07")]) + code, out = self._failed_detail_case(line=13) + self.assertIn("PASS", self.criterion(out, "failed-details")) + self.assertIn("PASS", self.criterion(out, "deltas")) + self.assertGate(code, out, True) + + def test_divergence_with_unknown_sd_ref_is_rejected(self): + self._deltas([("d/bug", "valid-memsafety", "afl", "line", "12", "13", "SD-99")]) + code, out = self._failed_detail_case(line=13) + self.assertIn("FAIL", self.criterion(out, "failed-details")) + self.assertIn("FAIL", self.criterion(out, "deltas")) + self.assertGate(code, out, False) + + # --- desempenho --------------------------------------------------------- + + def _perf_case(self, wall_r=10.0, rss_r=1000, n=5): + c, r = [], [] + for i in range(n): + c += rows("d/t%d" % i, wall=10.0, rss=1000) + r += rows("d/t%d" % i, wall=wall_r, rss=rss_r) + return self.run_gate(c, r) + + def test_wall_median_1_10_passes(self): + code, out = self._perf_case(wall_r=11.0) + self.assertIn("PASS", self.criterion(out, "wall")) + self.assertGate(code, out, True) + + def test_wall_median_1_11_fails(self): + code, out = self._perf_case(wall_r=11.1) + self.assertIn("FAIL", self.criterion(out, "wall")) + self.assertGate(code, out, False) + + def test_rss_median_1_36_passes(self): + code, out = self._perf_case(rss_r=1360) + self.assertIn("PASS", self.criterion(out, "rss")) + self.assertGate(code, out, True) + + def test_rss_median_1_37_fails(self): + code, out = self._perf_case(rss_r=1370) + self.assertIn("FAIL", self.criterion(out, "rss")) + self.assertGate(code, out, False) + + def test_wall_is_median_over_reps_then_over_tasks(self): + # Mediana por tarefa de [10,10,10,30,30] = 10: razão 1,0. + c = rows("d/safe", wall=10.0) + r = rows("d/safe", wall=[10.0, 30.0, 10.0, 30.0, 10.0]) + code, out = self.run_gate(c, r) + self.assertIn("PASS", self.criterion(out, "wall")) + self.assertGate(code, out, True) + + def test_single_task_ratio_above_1_5_only_warns(self): + c, r = [], [] + for i in range(5): + c += rows("d/t%d" % i, wall=10.0, rss=1000) + r += rows("d/t%d" % i, wall=20.0 if i == 0 else 10.0, rss=1000) + code, out = self.run_gate(c, r) + warns = [l for l in out if l.startswith("warning:")] + self.assertEqual(len(warns), 1, out) + self.assertIn("d/t0", warns[0]) + self.assertIn("PASS", self.criterion(out, "wall")) + self.assertGate(code, out, True) + + def test_more_timeouts_fail(self): + self._deltas([("d/x", "valid-memsafety", "symex", "verdict", + "UNKNOWN", "TIMEOUT", "SD-03")]) + c = rows("d/x", gen="symex", verdicts=("UNKNOWN",) * 5) + r = rows("d/x", gen="symex", verdicts=("TIMEOUT",) * 5) + code, out = self.run_gate(c, r) + line = self.criterion(out, "timeouts") + self.assertIn("FAIL", line) + self.assertIn("symex", line) + self.assertGate(code, out, False) + + def test_fewer_timeouts_pass(self): + self._deltas([("d/x", "valid-memsafety", "symex", "verdict", + "TIMEOUT", "UNKNOWN", "SD-03")]) + c = rows("d/x", gen="symex", verdicts=("TIMEOUT",) * 5) + r = rows("d/x", gen="symex", verdicts=("UNKNOWN",) * 5) + code, out = self.run_gate(c, r) + self.assertIn("PASS", self.criterion(out, "timeouts")) + self.assertGate(code, out, True) + + # --- formato ------------------------------------------------------------ + + def test_rejects_tsv_without_eleven_columns(self): + c = self._write("bad.tsv", HEADER + "\n" + "a\tb\tc\n") + with self.assertRaises(SystemExit): + with contextlib.redirect_stderr(io.StringIO()): + cb.main([c, c, "--deltas", self.deltas, + "--semantic-deltas", self.semantic, + "--corpus", self.corpus]) + + +if __name__ == "__main__": + unittest.main() From 28fb0d3eeff30bb43ce002707a3c3946b745d777 Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 15:53:32 -0400 Subject: [PATCH 10/74] fix: fail the gate on missing expectations and mismatched repetition counts --- docs/cutover/decisoes.md | 4 +++ scripts/it/compare_baselines.py | 20 +++++++++++++- scripts/it/test_compare_baselines.py | 39 ++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/docs/cutover/decisoes.md b/docs/cutover/decisoes.md index 83b836f..cd54d3d 100644 --- a/docs/cutover/decisoes.md +++ b/docs/cutover/decisoes.md @@ -266,3 +266,7 @@ The C tests drove internal containers with no Rust export, so each assertion goe - **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. diff --git a/scripts/it/compare_baselines.py b/scripts/it/compare_baselines.py index 61f8cf1..1407ad2 100644 --- a/scripts/it/compare_baselines.py +++ b/scripts/it/compare_baselines.py @@ -13,6 +13,9 @@ 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 @@ -22,6 +25,7 @@ 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 @@ -105,6 +109,7 @@ def aggregate(rows): "witness": mode([r["witness"] for r in rs]), "wall": statistics.median(float(r["wall_s"]) for r in rs), "rss": statistics.median(float(r["maxrss_kb"]) for r in rs), + "reps": len(rs), } return out @@ -118,7 +123,8 @@ def load_expected(corpus): """{(task, prp): (expected_verdict, subproperty|None)} lido dos .yml.""" out = {} if not corpus or not os.path.isdir(corpus): - return out + 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"): @@ -141,6 +147,8 @@ def load_expected(corpus): 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 @@ -169,6 +177,11 @@ def main(argv=None): 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 @@ -190,6 +203,11 @@ def report(name, ok, detail): 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 diff --git a/scripts/it/test_compare_baselines.py b/scripts/it/test_compare_baselines.py index c77bb33..93c2569 100644 --- a/scripts/it/test_compare_baselines.py +++ b/scripts/it/test_compare_baselines.py @@ -61,6 +61,8 @@ def setUp(self): self._yml("d/safe", [("valid-memsafety", "true", None)]) self._yml("d/bug", [("valid-memsafety", "false", "valid-free")]) self._yml("d/reach", [("unreach-call", "false", None)]) + for t in ("d/x", "d/t0", "d/t1", "d/t2", "d/t3", "d/t4"): + self._yml(t, [("valid-memsafety", "true", None)]) def tearDown(self): shutil.rmtree(self.tmp) @@ -124,6 +126,43 @@ def test_identical_baselines_pass_with_one_line_per_criterion(self): "deltas", "wall", "timeouts", "rss"): self.assertIn("PASS", self.criterion(out, name)) + def test_rep_count_mismatch_fails(self): + c = rows("d/safe") + r = rows("d/safe", verdicts=("SUCCEEDED",) * 4) + code, out = self.run_gate(c, r) + line = self.criterion(out, "reps") + self.assertIn("FAIL", line) + self.assertIn("d/safe", line) + self.assertGate(code, out, False) + + def test_rep_count_equal_passes(self): + code, out = self.run_gate(self.base(), self.base()) + self.assertIn("PASS", self.criterion(out, "reps")) + + def _expect_exit2(self, corpus, lines): + c = self._tsv("c.tsv", lines) + err = io.StringIO() + with self.assertRaises(SystemExit) as cm: + with contextlib.redirect_stderr(err), contextlib.redirect_stdout(io.StringIO()): + cb.main([c, c, "--deltas", self.deltas, + "--semantic-deltas", self.semantic, "--corpus", corpus]) + self.assertEqual(cm.exception.code, 2) + return err.getvalue() + + def test_missing_corpus_exits_2(self): + msg = self._expect_exit2(os.path.join(self.tmp, "nope"), rows("d/safe")) + self.assertIn("expected_verdict", msg) + + def test_corpus_without_yml_exits_2(self): + empty = os.path.join(self.tmp, "empty") + os.makedirs(empty) + msg = self._expect_exit2(empty, rows("d/safe")) + self.assertIn("expected_verdict", msg) + + def test_key_without_expectation_exits_2(self): + msg = self._expect_exit2(self.corpus, rows("d/unlisted")) + self.assertIn("d/unlisted", msg) + def test_missing_key_fails(self): code, out = self.run_gate(self.base(), rows("d/safe")) self.assertIn("FAIL", self.criterion(out, "keys")) From 7d829198da1b9a209dc6a0401d999d87aa4cb6ed Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 16:38:57 -0400 Subject: [PATCH 11/74] test: link matrix of the 120 runtime module combinations of Caller::linkLLVM --- scripts/it/check-link-matrix.sh | 204 +++++++++++++++++++++++++++ scripts/it/smoke/link-smoke.c | 32 +++++ scripts/it/test-check-link-matrix.sh | 122 ++++++++++++++++ 3 files changed, 358 insertions(+) create mode 100755 scripts/it/check-link-matrix.sh create mode 100644 scripts/it/smoke/link-smoke.c create mode 100755 scripts/it/test-check-link-matrix.sh diff --git a/scripts/it/check-link-matrix.sh b/scripts/it/check-link-matrix.sh new file mode 100755 index 0000000..99632d0 --- /dev/null +++ b/scripts/it/check-link-matrix.sh @@ -0,0 +1,204 @@ +#!/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 +# diretório com os 21 .bc do runtime (p.ex. +# target/map2check-it/c/lib, ou a saída do build Rust) +# +# 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 e __dso_handle +# (definido pelo crtbegin.o de todo executável; o std do Rust o +# referencia como weak para os destrutores de thread_local), mais klee_* +# para symex, __afl_* para afl e wasm_rt_* com wasm. Referências weak +# contam como as fortes; +# 3. gen none: link nativo com clang-16; gen afl: link nativo com +# afl-clang-fast -g -O2 ... -lm, como Caller::applyNonDetGenerator. +# +# 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)" +[ $# -eq 1 ] || { echo "uso: $0 " >&2; exit 2; } +[ -d "$1" ] || { echo "$1: diretório inexistente" >&2; exit 2; } +LIB="$(cd "$1" && pwd)" + +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/@.*//"; echo __dso_handle; } | sort -u > /tmp/allowed-libc.txt +one() { + local idx="$1" ct="$2" m="$3" g="$4" wa="$5" wi="$6" L=/lib-dir 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 $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 + # 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_" + und="$(llvm-nm-16 --undefined-only "$d/r.bc" | awk "{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 + 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" +docker run --rm --network none --user "$(id -u):$(id -g)" -e HOME=/tmp -e JOBS="$JOBS" \ + -v "$LIB:/lib-dir: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/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-check-link-matrix.sh b/scripts/it/test-check-link-matrix.sh new file mode 100755 index 0000000..edc6358 --- /dev/null +++ b/scripts/it/test-check-link-matrix.sh @@ -0,0 +1,122 @@ +#!/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 +# +# 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" +cp "$C_LIB"/*.bc "$work/dup/" +cp "$C_LIB"/*.bc "$work/undef/" +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 +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 +' + +# --- 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)" + +# --- 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" + +exit $fail From e045d30e57e70f04348da772e120061ed216eab1 Mon Sep 17 00:00:00 2001 From: Herbert Rocha Date: Mon, 26 Oct 2026 16:38:57 -0400 Subject: [PATCH 12/74] docs: D7 module layout spike for the drop-in runtime --- docs/cutover/d7-layout.md | 164 ++++++ spikes/d7-layout/build.sh | 118 ++++ spikes/d7-layout/core/Cargo.lock | 7 + spikes/d7-layout/core/Cargo.toml | 30 + spikes/d7-layout/core/src/lib.rs | 507 +++++++++++++++++ spikes/d7-layout/gen-b/afl-loop/Cargo.lock | 7 + spikes/d7-layout/gen-b/afl-loop/Cargo.toml | 23 + spikes/d7-layout/gen-b/afl-loop/src/lib.rs | 108 ++++ spikes/d7-layout/gen-b/afl/Cargo.lock | 7 + spikes/d7-layout/gen-b/afl/Cargo.toml | 23 + spikes/d7-layout/gen-b/afl/src/lib.rs | 66 +++ spikes/d7-layout/gen-b/common.rs | 69 +++ spikes/d7-layout/gen-b/klee/Cargo.lock | 7 + spikes/d7-layout/gen-b/klee/Cargo.toml | 23 + spikes/d7-layout/gen-b/klee/src/lib.rs | 88 +++ spikes/d7-layout/gen-b/none/Cargo.lock | 7 + spikes/d7-layout/gen-b/none/Cargo.toml | 23 + spikes/d7-layout/gen-b/none/src/lib.rs | 46 ++ spikes/d7-layout/measure-afl.sh | 46 ++ spikes/d7-layout/measure-klee.sh | 41 ++ spikes/d7-layout/progs/assume.c | 24 + spikes/d7-layout/progs/magic.c | 13 + spikes/d7-layout/scripts/gen_ll.py | 553 +++++++++++++++++++ spikes/d7-layout/scripts/stub_personality.py | 25 + 24 files changed, 2025 insertions(+) create mode 100644 docs/cutover/d7-layout.md create mode 100755 spikes/d7-layout/build.sh create mode 100644 spikes/d7-layout/core/Cargo.lock create mode 100644 spikes/d7-layout/core/Cargo.toml create mode 100644 spikes/d7-layout/core/src/lib.rs create mode 100644 spikes/d7-layout/gen-b/afl-loop/Cargo.lock create mode 100644 spikes/d7-layout/gen-b/afl-loop/Cargo.toml create mode 100644 spikes/d7-layout/gen-b/afl-loop/src/lib.rs create mode 100644 spikes/d7-layout/gen-b/afl/Cargo.lock create mode 100644 spikes/d7-layout/gen-b/afl/Cargo.toml create mode 100644 spikes/d7-layout/gen-b/afl/src/lib.rs create mode 100644 spikes/d7-layout/gen-b/common.rs create mode 100644 spikes/d7-layout/gen-b/klee/Cargo.lock create mode 100644 spikes/d7-layout/gen-b/klee/Cargo.toml create mode 100644 spikes/d7-layout/gen-b/klee/src/lib.rs create mode 100644 spikes/d7-layout/gen-b/none/Cargo.lock create mode 100644 spikes/d7-layout/gen-b/none/Cargo.toml create mode 100644 spikes/d7-layout/gen-b/none/src/lib.rs create mode 100755 spikes/d7-layout/measure-afl.sh create mode 100755 spikes/d7-layout/measure-klee.sh create mode 100644 spikes/d7-layout/progs/assume.c create mode 100644 spikes/d7-layout/progs/magic.c create mode 100644 spikes/d7-layout/scripts/gen_ll.py create mode 100644 spikes/d7-layout/scripts/stub_personality.py diff --git a/docs/cutover/d7-layout.md b/docs/cutover/d7-layout.md new file mode 100644 index 0000000..ae1fd4b --- /dev/null +++ b/docs/cutover/d7-layout.md @@ -0,0 +1,164 @@ +# 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: proposta, aguardando aprovação do usuário (Step 6). As Fases 2 e 3 dependem dela.** + +## 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.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.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.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.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.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` (o mesmo de `scripts/build-klee-runtime.sh`); +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; +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_*`. + +## 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 + `__dso_handle`, mais `klee_*` (symex), `__afl_*` (afl), `wasm_rt_*` (wasm); link nativo `clang-16` (gerador none) e `afl-clang-fast -g -O2 ... -lm` (afl). + +`__dso_handle` entrou na lista: o `std` o referencia como weak (destrutores de `thread_local`) e ele é definido pelo `crtbegin.o` de todo executável. É 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. + +## 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 | + +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 | + +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 é só onde ficam o leitor de bytes, o `pchar`/`memory` e a concretização do KLEE: IR gerado em A-fwd (os três `.ll` de gerador somam 756 linhas, a maior parte as 21 funções por tipo, de 5 linhas cada) contra Rust `no_std` em B. +- **O grafo de módulos do C é preservado.** Em A-fwd cada `.bc` define as mesmas funções externas que o `.c` homônimo, 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`). Uma combinação tem o mesmo conjunto de definições externas que no C. 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. + +## 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_*` e os 12 `map2check_*` de memória → `__m2c_rt_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. **Triple:** o núcleo sai com `x86_64-unknown-linux-gnu`, o C e o IR com `x86_64-pc-linux-gnu`; o `llvm-link` só avisa. Na Fase 3, o IR deve usar a triple do núcleo, ou o build deve fixar uma só. +6. **Variáveis C sem consumidor externo** (`trackbb_log`, `nondet_log`, `gotError`, `Map2CheckCurrentStep`, nomes de arquivo `violated_property_file*`) não são exportadas; o `generate_aux_files` do witness recebe um ponteiro nulo no lugar de `&trackbb_log` e lê o estado do núcleo. + +## Alternativas + +- **B** (geradores em crates Rust `no_std`): passa nos mesmos critérios (matriz 120/120, KLEE 5/5, AFL++ 101% e 95%). 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 A-fwd**: 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; os intrínsecos KLEE/AFL++, `main` e `setjmp`/`longjmp` ficam só no IR dos geradores. +2. **Símbolos exportados por módulo**: cada `.bc` define as funções externas que o `.c` homônimo define **e que têm consumidor fora do módulo** (`referenced_by` ≠ `-` no inventário). As auxiliares internas do C (container, logs, `B_TREE_*`, `write_property_*` só usados pelo runtime) **não** são exportadas. Consequência observável: um programa que define, por exemplo, `append_element` 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`, intrínsecos rebaixados, `internalize` com a lista do inventário. +4. **`__dso_handle` na lista permitida da matriz**, 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`. + +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.sh $s target/map2check-it/d7/$s + scripts/it/check-link-matrix.sh 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/spikes/d7-layout/build.sh b/spikes/d7-layout/build.sh new file mode 100755 index 0000000..9456594 --- /dev/null +++ b/spikes/d7-layout/build.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# D7 spike (Task 1.1): builds the 21 runtime modules for one layout strategy. +# +# Usage: spikes/d7-layout/build.sh +# fwd A refined: Rust core = Map2CheckFunctions.bc; the other 20 are IR +# forwarders/stubs; generator IR holds the KLEE/AFL++ intrinsics, +# main and setjmp/longjmp. +# alit A as written in the plan: generator logic in the core, klee_* and +# the AFL reject hook extern_weak, selection by strong/weak global. +# b B: generator modules are Rust no_std crates (AFL: + IR main with +# setjmp/longjmp). +# b-loop B with the AFL++ persistent loop in Rust and no longjmp (a failed +# assume ends the process). +# +# Rust: rustc 1.72.1 (LLVM 16) on the host, fat LTO, --emit=llvm-bc (D1, A'). +# LLVM 16 tools: inside the Map2Check image (IMAGE). +set -euo pipefail + +HERE="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$HERE/../.." && pwd)" +[ $# -eq 2 ] || { echo "uso: $0 " >&2; exit 2; } +strategy="$1" +mkdir -p "$2" +OUT="$(cd "$2" && pwd)" +commit="$(sed -n 's/^commit=//p' "$ROOT/vendor/MAP2CHECK_VERSION")" +IMAGE="${IMAGE:-map2check-dev:develop-${commit:0:12}}" +TOOLCHAIN="${D7_RUSTC:-1.72.1}" + +work="$OUT/.work" +rm -rf "$work" +mkdir -p "$work/ll" + +emit() { # [cargo args...] + local dir="$1" dest="$2" + shift 2 + # A fresh crate build every time: cargo does not re-emit the .bc of a + # crate it considers up to date. + (cd "$dir" && RUSTUP_TOOLCHAIN="$TOOLCHAIN" cargo clean --quiet --release) + (cd "$dir" && RUSTUP_TOOLCHAIN="$TOOLCHAIN" cargo rustc --quiet --release --lib "$@" -- --emit=llvm-bc) + cp "$dir"/target/release/deps/*.bc "$dest" +} + +case "$strategy" in + fwd | b | b-loop) emit "$HERE/core" "$work/core.bc" ;; + alit) emit "$HERE/core" "$work/core.bc" --features a_literal ;; + *) echo "estratégia desconhecida: $strategy" >&2; exit 2 ;; +esac +case "$strategy" in + b | b-loop) + for g in none klee afl afl-loop; do emit "$HERE/gen-b/$g" "$work/gen-$g.bc"; done ;; +esac + +ll_strategy="$strategy" +[ "$strategy" = b-loop ] && ll_strategy=b +tail -n +2 "$ROOT/docs/runtime-abi/inventory.tsv" | cut -f1 > "$work/abi.txt" +python3 -I "$HERE/scripts/gen_ll.py" "$ll_strategy" "$ROOT/docs/runtime-abi/inventory.tsv" "$work/ll" + +docker run --rm --network none --user "$(id -u):$(id -g)" -v "$OUT:/out" \ + -v "$ROOT/scripts:/repo/scripts:ro" -v "$HERE:/spike:ro" "$IMAGE" bash -euc ' + s="$1"; w=/out/.work + # 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/core-s.bc" + # The post-processing of scripts/build-klee-runtime.sh (std backtrace + # symbolizer stubbed, vector intrinsics lowered for KLEE 3.1), plus + # rust_eh_personality stubbed so the unwinder (libgcc_s) is not needed. + llvm-dis-16 "$w/core-s.bc" -o "$w/c0.ll" + python3 -I /repo/scripts/klee-stub-symbolizer.py "$w/c0.ll" "$w/c1.ll" >/dev/null + python3 -I /spike/scripts/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" + python3 -I /repo/scripts/klee-lower-intrinsics.py "$w/c3.ll" "$w/c4.ll" >/dev/null + llvm-as-16 "$w/c4.ll" -o "$w/c5.bc" + # Only the C ABI (inventory.tsv) and the __m2c_rt_* entry points stay + # external; std/core symbols (rust_panic, _ZN4core3fmt...) become internal, + # so they cannot collide with the program or with another Rust module. + keep() { # + { cat /out/.work/abi.txt; llvm-nm-16 --defined-only "$1" | awk "{print \$3}" | grep -E "^(__m2c_|map2check_afl_)" || true; } > "$1.api" + opt-16 -passes=internalize,globaldce -internalize-public-api-file="$1.api" "$1" -o "$2" + } + keep "$w/c5.bc" "$w/core-s.bc" + if [ "$s" = alit ]; then + llvm-dis-16 "$w/core-s.bc" -o "$w/core.ll" + sed -i -e "s/^declare \(.*\)@klee_make_symbolic(/declare extern_weak \1@klee_make_symbolic(/" \ + -e "s/^declare \(.*\)@__m2c_afl_reject(/declare extern_weak \1@__m2c_afl_reject(/" \ + -e "s/^@__m2c_sel_generator = /@__m2c_sel_generator = weak /" "$w/core.ll" + llvm-as-16 "$w/core.ll" -o "$w/core-s.bc" + fi + llvm-as-16 "$w/ll/Map2CheckFunctions-glue.ll" -o "$w/glue.bc" + llvm-link-16 "$w/core-s.bc" "$w/glue.bc" -o /out/Map2CheckFunctions.bc + for f in "$w"/ll/*.ll; do + m="$(basename "$f" .ll)" + case "$m" in Map2CheckFunctions-glue | b-afl-main) continue ;; esac + llvm-as-16 "$f" -o "/out/$m.bc" + done + case "$s" in + b | b-loop) + opt-16 --strip-debug "$w/gen-none.bc" -o "$w/gn.bc"; keep "$w/gn.bc" /out/NonDetGeneratorNone.bc + opt-16 --strip-debug "$w/gen-klee.bc" -o "$w/gk.bc"; keep "$w/gk.bc" /out/NonDetGeneratorKlee.bc + if [ "$s" = b ]; then + opt-16 --strip-debug "$w/gen-afl.bc" -o "$w/gen-afl-s.bc" + llvm-as-16 "$w/ll/b-afl-main.ll" -o "$w/b-afl-main.bc" + llvm-link-16 "$w/gen-afl-s.bc" "$w/b-afl-main.bc" -o "$w/ga.bc" + else + opt-16 --strip-debug "$w/gen-afl-loop.bc" -o "$w/ga.bc" + fi + keep "$w/ga.bc" /out/NonDetGeneratorAFL.bc ;; + esac + n=0 + for m in Map2CheckFunctions TrackBBLog NonDetLog PropertyGenerator ContainerRealloc ContainerBTree BTree AnalysisModeMemtrack AnalysisModeMemcleanup AnalysisModeOverflow AnalysisModeAssert AnalysisModeNone AllocationLog ListLog HeapLog NonDetGeneratorNone NonDetGeneratorKlee NonDetGeneratorAFL WasmRuntimeStubs WitnessGeneration WitnessGenerationNone; do + llvm-dis-16 "/out/$m.bc" -o /dev/null + n=$((n + 1)) + done + echo "build: $s -> $n modules readable by llvm-dis-16" +' build "$strategy" +rm -rf "$work" diff --git a/spikes/d7-layout/core/Cargo.lock b/spikes/d7-layout/core/Cargo.lock new file mode 100644 index 0000000..1e4d6ca --- /dev/null +++ b/spikes/d7-layout/core/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "d7-core" +version = "0.0.0" diff --git a/spikes/d7-layout/core/Cargo.toml b/spikes/d7-layout/core/Cargo.toml new file mode 100644 index 0000000..59c72e5 --- /dev/null +++ b/spikes/d7-layout/core/Cargo.toml @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-3.0-only +# D7 spike (Task 1.1): Rust core of the drop-in runtime, built with rustc +# 1.72.1 (LLVM 16) as one fat-LTO bitcode module -> Map2CheckFunctions.bc. +# Prototype only: implements what the link-smoke program reaches, stubs the rest. +[package] +name = "d7-core" +version = "0.0.0" +edition = "2021" +rust-version = "1.72" +publish = false + +[lib] +name = "d7core" +crate-type = ["staticlib"] +path = "src/lib.rs" + +[features] +# Strategy A as written in the plan: generator logic in the core, klee_* and +# the AFL reject hook referenced from Rust and turned extern_weak afterwards, +# selection by a strong global in the generator .ll over a weak default. +a_literal = [] + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +panic = "abort" +debug = false + +[workspace] diff --git a/spikes/d7-layout/core/src/lib.rs b/spikes/d7-layout/core/src/lib.rs new file mode 100644 index 0000000..c8112e0 --- /dev/null +++ b/spikes/d7-layout/core/src/lib.rs @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-3.0-only +//! D7 spike (Task 1.1): the Rust core that becomes `Map2CheckFunctions.bc`. +//! +//! It owns the state and the logic. The other 20 modules are thin LLVM IR +//! (`../ll`, `../scripts/gen_ll.py`) that define exactly the external symbols +//! the C module of the same name defines, forwarding to the `__m2c_rt_*` +//! entry points below. The core reaches the other modules only through the +//! symbols the C runtime itself crosses between modules (`analysis_*`, +//! `nondet_*`, `generate_*`), so the C module graph is kept as is. +//! +//! Prototype scope: what `scripts/it/smoke/link-smoke.c` reaches in the five +//! modes (core, bb tracking, nondet log, property file, assert, overflow +//! add/sub/mul, witness files); the memory-safety entry points are stubs +//! emitted by gen_ll.py. +#![allow(clippy::missing_safety_doc)] + +use std::ffi::{c_char, c_int, c_uint, CStr}; +use std::io::Write; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Mutex; + +// Cross-module symbols, defined by the mode, generator and witness modules, +// exactly as Map2CheckFunctions.c sees them. +extern "C" { + fn analysis_init(); + fn analysis_destroy(); + fn analysis_is_program_correct() -> c_char; + fn analysis_generate_aux_witness_files(); + fn nondet_init(); + fn nondet_destroy(); + fn nondet_generate_aux_witness_files(); + fn generate_aux_files(trackbb_log: *mut u8, violation: c_char); + fn generate_confirmation_file(); + fn abort() -> !; +} + +#[derive(Clone, Copy)] +struct NondetCall { + line: u32, + scope: u32, + value: i64, + function: usize, + step: u32, + kind: u32, +} + +struct State { + step: u32, + sv_comp: bool, + got_error: bool, + trackbb: Vec<(u32, usize)>, + nondet: Vec, +} + +static STATE: Mutex = Mutex::new(State { + step: 0, + sv_comp: false, + got_error: false, + trackbb: Vec::new(), + nondet: Vec::new(), +}); +static RELEASED: AtomicBool = AtomicBool::new(false); + +fn state() -> std::sync::MutexGuard<'static, State> { + STATE.lock().unwrap_or_else(|e| e.into_inner()) +} + +unsafe fn cstr(p: *const c_char) -> &'static str { + if p.is_null() { + "" + } else { + CStr::from_ptr(p).to_str().unwrap_or("") + } +} + +// ---------------------------------------------------------------- core ABI + +#[no_mangle] +pub extern "C" fn map2check_init(is_sv_comp: c_int) { + { + let mut s = state(); + s.step = 0; + s.trackbb = Vec::new(); + if is_sv_comp != 0 { + s.sv_comp = true; + } + } + unsafe { + analysis_init(); + nondet_init(); + } +} + +#[no_mangle] +pub extern "C" fn map2check_get_current_step() -> c_uint { + state().step +} + +#[no_mangle] +pub extern "C" fn map2check_next_current_step() { + state().step += 1; +} + +#[no_mangle] +pub unsafe extern "C" fn map2check_track_bb(line: c_uint, function_name: *const c_char) { + let mut s = state(); + if !s.trackbb.iter().rev().any(|(l, _)| *l == line) { + s.trackbb.push((line, function_name as usize)); + } +} + +#[no_mangle] +pub extern "C" fn __VERIFIER_error() {} + +#[no_mangle] +pub unsafe extern "C" fn map2check_target_function(func: *const c_char, _scope: c_int, line: c_int) { + write_property(5, line, func); + map2check_error(); +} + +#[no_mangle] +pub extern "C" fn map2check_success() { + unsafe { write_property(0, 0, b"\0".as_ptr().cast()) }; + map2check_exit(); +} + +#[no_mangle] +pub extern "C" fn map2check_error() { + state().got_error = true; + map2check_exit(); +} + +#[no_mangle] +pub extern "C" fn map2check_destroy() { + if RELEASED.swap(true, Ordering::SeqCst) { + return; + } + state().trackbb = Vec::new(); + unsafe { + analysis_destroy(); + nondet_destroy(); + } +} + +#[no_mangle] +pub extern "C" fn map2check_exit() { + let mut got_error = state().got_error; + if !got_error { + got_error = unsafe { analysis_is_program_correct() } == 0; + state().got_error = got_error; + } + // The C passes &trackbb_log; the Rust witness module reads the state. + unsafe { generate_aux_files(std::ptr::null_mut(), got_error as c_char) }; + map2check_destroy(); + if got_error { + unsafe { + generate_confirmation_file(); + abort(); + } + } +} + +// ------------------------------------------------------- PropertyGenerator + +fn put_file(name: &str, text: &str) { + if let Ok(mut f) = std::fs::File::create(name) { + let _ = f.write_all(text.as_bytes()); + } +} + +#[no_mangle] +pub unsafe extern "C" fn write_property(violated: c_int, line: c_int, func: *const c_char) { + let f = cstr(func); + let loc = |tag: &str| format!("{tag}\nLine: {line}\nFunction: {f}\n"); + let text = match violated { + 0 => "NONE\n".to_string(), + 1 => "UNKNOWN\n".to_string(), + 2 => loc("FALSE-FREE"), + 3 => loc("FALSE-DEREF"), + 4 => "FALSE-MEMTRACK\n".to_string(), + 5 => loc("TARGET-REACHED"), + 6 => { + put_file("map2check_property_overflow", &loc("OVERFLOW")); + loc("OVERFLOW") + } + 7 => { + put_file("map2check_property_assert", &loc("ASSERT")); + loc("ASSERT") + } + 8 => "FALSE-MEMCLEANUP\n".to_string(), + _ => loc("FALSE-DIVBYZERO"), + }; + put_file("map2check_property", &text); +} + +// --------------------------------------------------------------- NonDetLog + +const T_INTEGER: u32 = 0; +const T_UNSIGNED: u32 = 1; +const T_DOUBLE: u32 = 4; +const T_FLOAT: u32 = 5; +const T_LONGLONG: u32 = 18; +const T_ULONGLONG: u32 = 19; +const T_MEMORY_BYTE: u32 = 23; + +fn log_nondet(kind: u32, line: c_uint, scope: c_uint, value: i64, func: *const c_char) { + let mut s = state(); + let step = s.step; + s.nondet.push(NondetCall { + line, + scope, + value, + function: func as usize, + step, + kind, + }); + s.step += 1; +} + +macro_rules! nondet_log_fns { + ($($name:ident: $ty:ty => $kind:expr;)*) => {$( + #[no_mangle] + pub extern "C" fn $name(line: c_uint, scope: c_uint, value: $ty, func: *const c_char) { + log_nondet($kind, line, scope, value as i64, func); + } + )*}; +} + +// Type codes: only the ones the writer distinguishes matter in the spike. +nondet_log_fns! { + map2check_nondet_int: c_int => T_INTEGER; + map2check_nondet_char: c_int => 2; + map2check_nondet_pointer: c_int => 3; + map2check_nondet_ushort: c_int => 6; + map2check_nondet_short: c_int => 7; + map2check_nondet_long: c_int => 8; + map2check_nondet_ulong: c_int => 9; + map2check_nondet_bool: c_int => 10; + map2check_nondet_uchar: c_int => 11; + map2check_nondet_pchar: c_int => 12; + map2check_nondet_size_t: c_int => 13; + map2check_nondet_loff_t: c_int => 14; + map2check_nondet_sector_t: c_int => 15; + map2check_nondet_uint: c_int => 16; + map2check_nondet_u8: c_int => 20; + map2check_nondet_u16: c_int => 21; + map2check_nondet_u32: c_int => 22; + map2check_nondet_memory_byte: c_int => T_MEMORY_BYTE; + map2check_nondet_unsigned: c_uint => T_UNSIGNED; + map2check_nondet_longlong: i64 => T_LONGLONG; + map2check_nondet_ulonglong: u64 => T_ULONGLONG; +} + +#[no_mangle] +pub extern "C" fn map2check_nondet_double(line: c_uint, scope: c_uint, value: f64, func: *const c_char) { + log_nondet(T_DOUBLE, line, scope, value.to_bits() as i64, func); +} + +#[no_mangle] +pub extern "C" fn map2check_nondet_float(line: c_uint, scope: c_uint, value: f64, func: *const c_char) { + log_nondet(T_FLOAT, line, scope, value.to_bits() as i64, func); +} + +#[no_mangle] +pub extern "C" fn nondet_log_init() { + state().nondet = Vec::new(); +} + +#[no_mangle] +pub extern "C" fn nondet_log_destroy() { + state().nondet = Vec::new(); +} + +/// Rows of the nondet log, for the KLEE generator's concretization loop. +#[no_mangle] +pub extern "C" fn __m2c_rt_nondet_log_len() -> c_uint { + state().nondet.len() as c_uint +} + +/// Address of row `i`'s 64-bit value. Valid until the log changes; the KLEE +/// generator only uses it at exit, after the program stopped logging. +#[no_mangle] +pub extern "C" fn __m2c_rt_nondet_log_value(i: c_uint) -> *mut i64 { + let mut s = state(); + match s.nondet.get_mut(i as usize) { + Some(row) => &mut row.value, + None => std::ptr::null_mut(), + } +} + +/// `nondet_log_to_file(map2check_nondet_get_log())` of the C generators. +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_nondet_log_to_file() { + let rows: Vec = state().nondet.clone(); + let mut out = String::new(); + for (i, r) in rows.iter().enumerate() { + let func = cstr(r.function as *const c_char); + let value = match r.kind { + T_UNSIGNED => (r.value as u32).to_string(), + T_DOUBLE => format!("{:.6}", f64::from_bits(r.value as u64)), + T_FLOAT => format!("{}", f64::from_bits(r.value as u64) as f32), + T_LONGLONG => r.value.to_string(), + T_ULONGLONG => (r.value as u64).to_string(), + _ => (r.value as i32).to_string(), + }; + out.push_str(&format!( + "{i};{};{};{func};{};{value};{}\n", + r.line, r.scope, r.step, r.kind + )); + } + put_file("klee_log.csv", &out); +} + +// ----------------------------------------------------- AnalysisModeAssert + +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_assert_map2check_assert(cond: c_int, line: c_int, func: *const c_char) { + if cond == 0 { + write_property(7, line, func); + map2check_error(); + } +} + +// --------------------------------------------------- AnalysisModeOverflow + +fn fits_signed(v: i128, width: c_uint) -> bool { + let w = width.clamp(1, 64); + let min = -(1i128 << (w - 1)); + let max = (1i128 << (w - 1)) - 1; + v >= min && v <= max +} + +fn fits_unsigned(v: i128, width: c_uint) -> bool { + let w = width.clamp(1, 64); + v >= 0 && v < (1i128 << w) +} + +unsafe fn overflow_at(line: c_uint, func: *const c_char) { + write_property(6, line as c_int, func); + map2check_error(); +} + +macro_rules! binop { + ($name:ident, $t:ty, $op:tt, $fits:ident) => { + #[no_mangle] + pub unsafe extern "C" fn $name(a: $t, b: $t, width: c_uint, line: c_uint, _scope: c_uint, func: *const c_char) { + if !$fits((a as i128) $op (b as i128), width) { + overflow_at(line, func); + } + } + }; +} + +binop!(__m2c_rt_overflow_map2check_binop_add, i64, +, fits_signed); +binop!(__m2c_rt_overflow_map2check_binop_sub, i64, -, fits_signed); +binop!(__m2c_rt_overflow_map2check_binop_mul, i64, *, fits_signed); +binop!(__m2c_rt_overflow_map2check_binop_add_uint, u64, +, fits_unsigned); +binop!(__m2c_rt_overflow_map2check_binop_sub_uint, u64, -, fits_unsigned); +binop!(__m2c_rt_overflow_map2check_binop_mul_uint, u64, *, fits_unsigned); + +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_overflow_map2check_binop_sdiv( + a: i64, + b: i64, + width: c_uint, + line: c_uint, + _scope: c_uint, + func: *const c_char, +) { + if b == 0 { + write_property(9, line as c_int, func); + map2check_error(); + } else if !fits_signed((a as i128) / (b as i128), width) { + overflow_at(line, func); + } +} + +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_overflow_map2check_binop_srem( + a: i64, + b: i64, + width: c_uint, + line: c_uint, + scope: c_uint, + func: *const c_char, +) { + __m2c_rt_overflow_map2check_binop_sdiv(a, b, width, line, scope, func); +} + +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_overflow_overflowError(line: c_uint, func: *const c_char) { + overflow_at(line, func); +} + +// ------------------------------------------------------- WitnessGeneration + +#[no_mangle] +pub unsafe extern "C" fn __m2c_rt_witness_generate_aux_files(_log: *mut u8, violation: c_char) { + let rows: Vec<(u32, usize)> = state().trackbb.clone(); + let mut out = String::new(); + for (line, func) in rows { + out.push_str(&format!("{line};{}\n", cstr(func as *const c_char))); + } + put_file("track_bb_log.st", &out); + analysis_generate_aux_witness_files(); + if violation != 0 || std::env::var_os("MAP2CHECK_SEED_REPLAY").is_some() { + nondet_generate_aux_witness_files(); + } +} + +#[no_mangle] +pub extern "C" fn __m2c_rt_witness_generate_confirmation_file() { + put_file("map2check_checked_error", "END OF TIME\n"); +} + +// ------------------------------------------- strategy A as in the plan + +#[cfg(feature = "a_literal")] +mod a_literal { + //! The generator lives in the core. The generator .ll only defines + //! `main` and a strong `__m2c_sel_generator` over the core's weak 0; the + //! post-processing turns the core's `klee_make_symbolic` and + //! `__m2c_afl_reject` declarations into extern_weak and the selector + //! definition into a weak one. + use super::*; + use std::sync::atomic::AtomicU32; + + extern "C" { + fn klee_make_symbolic(addr: *mut u8, nbytes: usize, name: *const c_char); + fn __m2c_afl_reject() -> !; + fn _exit(code: c_int) -> !; + } + + /// 0 none, 1 klee, 2 afl. Mutable so LLVM cannot fold it before the + /// linkage becomes weak. + #[no_mangle] + pub static __m2c_sel_generator: AtomicU32 = AtomicU32::new(0); + + #[no_mangle] + pub static mut map2check_afl_data: *const u8 = std::ptr::null(); + #[no_mangle] + pub static mut map2check_afl_size: usize = 0; + #[no_mangle] + pub static mut map2check_afl_index: usize = 0; + + unsafe fn afl_bytes(out: *mut u8, n: usize) { + for i in 0..n { + let b = if map2check_afl_index < map2check_afl_size { + let v = *map2check_afl_data.add(map2check_afl_index); + map2check_afl_index += 1; + v + } else { + 0 + }; + *out.add(i) = b; + } + } + + #[no_mangle] + pub unsafe extern "C" fn map2check_non_det_int() -> c_int { + let mut v: c_int = 0; + match __m2c_sel_generator.load(Ordering::Relaxed) { + 1 => klee_make_symbolic((&mut v as *mut c_int).cast(), 4, b"non_det_int\0".as_ptr().cast()), + 2 => afl_bytes((&mut v as *mut c_int).cast(), 4), + _ => {} + } + v + } + + #[no_mangle] + pub unsafe extern "C" fn nondet_assume(expr: c_int) { + if expr != 0 { + return; + } + match __m2c_sel_generator.load(Ordering::Relaxed) { + 1 => { + let mut pruned = 0u8; + klee_make_symbolic(&mut pruned, 1, b"map2check_pruned\0".as_ptr().cast()); + _exit(0) + } + // longjmp out of this Rust frame: the defect of this strategy. + 2 => __m2c_afl_reject(), + _ => {} + } + } + + #[no_mangle] + pub extern "C" fn nondet_init() { + if __m2c_sel_generator.load(Ordering::Relaxed) != 0 { + nondet_log_init(); + } + } + + #[no_mangle] + pub extern "C" fn nondet_destroy() { + if __m2c_sel_generator.load(Ordering::Relaxed) != 0 { + nondet_log_destroy(); + } + } + + #[no_mangle] + pub unsafe extern "C" fn nondet_generate_aux_witness_files() { + if __m2c_sel_generator.load(Ordering::Relaxed) != 0 { + __m2c_rt_nondet_log_to_file(); + } + } +} diff --git a/spikes/d7-layout/gen-b/afl-loop/Cargo.lock b/spikes/d7-layout/gen-b/afl-loop/Cargo.lock new file mode 100644 index 0000000..6d96888 --- /dev/null +++ b/spikes/d7-layout/gen-b/afl-loop/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "d7-gen-afl-loop" +version = "0.0.0" diff --git a/spikes/d7-layout/gen-b/afl-loop/Cargo.toml b/spikes/d7-layout/gen-b/afl-loop/Cargo.toml new file mode 100644 index 0000000..651a710 --- /dev/null +++ b/spikes/d7-layout/gen-b/afl-loop/Cargo.toml @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-3.0-only +# D7 spike (Task 1.1), strategy B: the afl-loop nondet generator as a no_std crate +# of its own, emitted as LLVM 16 bitcode and linked next to the core. +[package] +name = "d7-gen-afl-loop" +version = "0.0.0" +edition = "2021" +rust-version = "1.72" +publish = false + +[lib] +name = "d7gen_afl_loop" +crate-type = ["staticlib"] +path = "src/lib.rs" + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +panic = "abort" +debug = false + +[workspace] diff --git a/spikes/d7-layout/gen-b/afl-loop/src/lib.rs b/spikes/d7-layout/gen-b/afl-loop/src/lib.rs new file mode 100644 index 0000000..d70a3e4 --- /dev/null +++ b/spikes/d7-layout/gen-b/afl-loop/src/lib.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-only +//! D7 spike, strategy B, AFL++ variant without longjmp: the persistent loop +//! is Rust (no IR), and a failed assume ends the process (_exit(0)), which +//! makes afl-fuzz restart the target through the fork server. +#![no_std] +use core::ffi::{c_int, c_uint}; + +#[path = "../../common.rs"] +#[macro_use] +mod common; + +extern "C" { + fn __map2check_main__(argc: c_int, argv: *mut *mut u8) -> c_int; + fn __afl_persistent_loop(max: c_uint) -> c_int; + fn read(fd: c_int, buf: *mut u8, n: usize) -> isize; + fn _exit(code: c_int) -> !; + static __afl_connected: c_int; + static mut __afl_fuzz_len: *mut c_uint; + static __afl_fuzz_ptr: *const u8; + fn malloc(n: usize) -> *mut u8; + fn nondet_log_init(); + fn nondet_log_destroy(); + fn map2check_nondet_memory_byte(line: c_uint, scope: c_uint, v: c_int, f: *const u8); + fn __m2c_rt_nondet_log_to_file(); +} + +#[no_mangle] +pub static mut map2check_afl_data: *const u8 = core::ptr::null(); +#[no_mangle] +pub static mut map2check_afl_size: usize = 0; +#[no_mangle] +pub static mut map2check_afl_index: usize = 0; + +#[no_mangle] +pub unsafe extern "C" fn get_next_input_from_afl() -> u8 { + if map2check_afl_index < map2check_afl_size { + let b = *map2check_afl_data.add(map2check_afl_index); + map2check_afl_index += 1; + b + } else { + 0 + } +} + +unsafe fn bytes(p: *mut u8, n: usize, _name: *const u8) { + for i in 0..n { + *p.add(i) = get_next_input_from_afl(); + } +} +nondet_types!(bytes); + +#[no_mangle] +pub unsafe extern "C" fn map2check_non_det_memory(dest: *mut u8, size: usize) { + bytes(dest, size, core::ptr::null()); + for i in 0..size { + map2check_nondet_memory_byte(0, 0, *dest.add(i) as c_int, b"__VERIFIER_nondet_memory\0".as_ptr()); + } +} + +#[no_mangle] +pub unsafe extern "C" fn nondet_init() { + nondet_log_init(); +} + +#[no_mangle] +pub unsafe extern "C" fn nondet_destroy() { + nondet_log_destroy(); +} + +#[no_mangle] +pub unsafe extern "C" fn nondet_generate_aux_witness_files() { + __m2c_rt_nondet_log_to_file(); +} + +#[no_mangle] +pub static mut __afl_sharedmem_fuzzing: c_int = 1; +#[no_mangle] +pub static mut __afl_fuzz_alt: [u8; 1048576] = [0; 1048576]; +#[used] +static AFL_PERSISTENT_SIG: [u8; 23] = *b"##SIG_AFL_PERSISTENT##\0"; + +#[no_mangle] +pub unsafe extern "C" fn nondet_assume(expr: c_int) { + if expr == 0 { + _exit(0); + } +} + +#[no_mangle] +pub unsafe extern "C" fn main(_argc: c_int, _argv: *mut *mut u8) -> c_int { + let sig = core::ptr::read_volatile(&AFL_PERSISTENT_SIG[0]); + let _ = sig; + while __afl_persistent_loop(if __afl_connected != 0 { 10000 } else { 1 }) != 0 { + let (buf, len) = if !__afl_fuzz_ptr.is_null() { + (__afl_fuzz_ptr, *__afl_fuzz_len as usize) + } else { + let alt = core::ptr::addr_of_mut!(__afl_fuzz_alt) as *mut u8; + let r = read(0, alt, 1048576); + *__afl_fuzz_len = if r < 0 { 0 } else { r as c_uint }; + (alt as *const u8, if r < 0 { 0 } else { r as usize }) + }; + map2check_afl_data = buf; + map2check_afl_size = len; + map2check_afl_index = 0; + __map2check_main__(0, core::ptr::null_mut()); + } + 0 +} diff --git a/spikes/d7-layout/gen-b/afl/Cargo.lock b/spikes/d7-layout/gen-b/afl/Cargo.lock new file mode 100644 index 0000000..37947df --- /dev/null +++ b/spikes/d7-layout/gen-b/afl/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "d7-gen-afl" +version = "0.0.0" diff --git a/spikes/d7-layout/gen-b/afl/Cargo.toml b/spikes/d7-layout/gen-b/afl/Cargo.toml new file mode 100644 index 0000000..a598687 --- /dev/null +++ b/spikes/d7-layout/gen-b/afl/Cargo.toml @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-3.0-only +# D7 spike (Task 1.1), strategy B: the afl nondet generator as a no_std crate +# of its own, emitted as LLVM 16 bitcode and linked next to the core. +[package] +name = "d7-gen-afl" +version = "0.0.0" +edition = "2021" +rust-version = "1.72" +publish = false + +[lib] +name = "d7gen_afl" +crate-type = ["staticlib"] +path = "src/lib.rs" + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +panic = "abort" +debug = false + +[workspace] diff --git a/spikes/d7-layout/gen-b/afl/src/lib.rs b/spikes/d7-layout/gen-b/afl/src/lib.rs new file mode 100644 index 0000000..9c2aee0 --- /dev/null +++ b/spikes/d7-layout/gen-b/afl/src/lib.rs @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-3.0-only +//! D7 spike, strategy B: NonDetGeneratorAFL as a no_std crate. main, +//! nondet_assume (setjmp/longjmp) and the AFL++ globals are IR +//! (b-afl-main.ll, written by scripts/gen_ll.py) linked into this module. +#![no_std] +use core::ffi::{c_int, c_uint}; + +#[path = "../../common.rs"] +#[macro_use] +mod common; + +extern "C" { + fn malloc(n: usize) -> *mut u8; + fn nondet_log_init(); + fn nondet_log_destroy(); + fn map2check_nondet_memory_byte(line: c_uint, scope: c_uint, v: c_int, f: *const u8); + fn __m2c_rt_nondet_log_to_file(); +} + +#[no_mangle] +pub static mut map2check_afl_data: *const u8 = core::ptr::null(); +#[no_mangle] +pub static mut map2check_afl_size: usize = 0; +#[no_mangle] +pub static mut map2check_afl_index: usize = 0; + +#[no_mangle] +pub unsafe extern "C" fn get_next_input_from_afl() -> u8 { + if map2check_afl_index < map2check_afl_size { + let b = *map2check_afl_data.add(map2check_afl_index); + map2check_afl_index += 1; + b + } else { + 0 + } +} + +unsafe fn bytes(p: *mut u8, n: usize, _name: *const u8) { + for i in 0..n { + *p.add(i) = get_next_input_from_afl(); + } +} +nondet_types!(bytes); + +#[no_mangle] +pub unsafe extern "C" fn map2check_non_det_memory(dest: *mut u8, size: usize) { + bytes(dest, size, core::ptr::null()); + for i in 0..size { + map2check_nondet_memory_byte(0, 0, *dest.add(i) as c_int, b"__VERIFIER_nondet_memory\0".as_ptr()); + } +} + +#[no_mangle] +pub unsafe extern "C" fn nondet_init() { + nondet_log_init(); +} + +#[no_mangle] +pub unsafe extern "C" fn nondet_destroy() { + nondet_log_destroy(); +} + +#[no_mangle] +pub unsafe extern "C" fn nondet_generate_aux_witness_files() { + __m2c_rt_nondet_log_to_file(); +} diff --git a/spikes/d7-layout/gen-b/common.rs b/spikes/d7-layout/gen-b/common.rs new file mode 100644 index 0000000..dc8ec8b --- /dev/null +++ b/spikes/d7-layout/gen-b/common.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-3.0-only +// D7 spike, strategy B: shared by the no_std generator crates (included +// with #[path]). `nondet_types!(reader)` defines map2check_non_det_ for the +// types NonDetGeneratorKlee.c/NonDetGeneratorAFL.c define, each filled by +// `reader(ptr, size, name)`. +#[macro_export] +macro_rules! nondet_types { + ($reader:path) => { + nondet_types!(@one $reader, map2check_non_det_char, i8, b"non_det_char\0"); + nondet_types!(@one $reader, map2check_non_det_pointer, usize, b"non_det_pointer\0"); + nondet_types!(@one $reader, map2check_non_det_ushort, u16, b"non_det_ushort\0"); + nondet_types!(@one $reader, map2check_non_det_short, i16, b"non_det_short\0"); + nondet_types!(@one $reader, map2check_non_det_long, i64, b"non_det_long\0"); + nondet_types!(@one $reader, map2check_non_det_ulong, u64, b"non_det_ulong\0"); + nondet_types!(@one $reader, map2check_non_det_bool, i32, b"non_det_bool\0"); + nondet_types!(@one $reader, map2check_non_det_uchar, u8, b"non_det_uchar\0"); + nondet_types!(@one $reader, map2check_non_det_size_t, u64, b"non_det_size_t\0"); + nondet_types!(@one $reader, map2check_non_det_loff_t, i64, b"non_det_loff_t\0"); + nondet_types!(@one $reader, map2check_non_det_sector_t, u64, b"non_det_sector_t\0"); + nondet_types!(@one $reader, map2check_non_det_double, f64, b"non_det_double\0"); + nondet_types!(@one $reader, map2check_non_det_int, i32, b"non_det_int\0"); + nondet_types!(@one $reader, map2check_non_det_uint, u32, b"non_det_uint\0"); + nondet_types!(@one $reader, map2check_non_det_unsigned, u32, b"non_det_unsigned\0"); + nondet_types!(@one $reader, map2check_non_det_float, f32, b"non_det_float\0"); + nondet_types!(@one $reader, map2check_non_det_longlong, i64, b"non_det_longlong\0"); + nondet_types!(@one $reader, map2check_non_det_ulonglong, u64, b"non_det_ulonglong\0"); + nondet_types!(@one $reader, map2check_non_det_u8, u8, b"non_det_u8\0"); + nondet_types!(@one $reader, map2check_non_det_u16, u16, b"non_det_u16\0"); + nondet_types!(@one $reader, map2check_non_det_u32, u32, b"non_det_u32\0"); + + #[no_mangle] + pub unsafe extern "C" fn map2check_non_det_pchar() -> *mut u8 { + let mut len = map2check_non_det_unsigned(); + if len == 0 { + return core::ptr::null_mut(); + } + if len > 4096 { + len = 4096; + } + let s = malloc(len as usize); + if s.is_null() { + return s; + } + let mut i = 0u32; + while i < len - 1 { + *s.add(i as usize) = map2check_non_det_char() as u8; + i += 1; + } + *s.add(i as usize) = 0; + s + } + }; + (@one $reader:path, $name:ident, $t:ty, $label:expr) => { + #[no_mangle] + pub unsafe extern "C" fn $name() -> $t { + let mut v = core::mem::MaybeUninit::<$t>::zeroed(); + $reader(v.as_mut_ptr().cast(), core::mem::size_of::<$t>(), $label.as_ptr()); + v.assume_init() + } + }; +} + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo) -> ! { + extern "C" { + fn abort() -> !; + } + unsafe { abort() } +} diff --git a/spikes/d7-layout/gen-b/klee/Cargo.lock b/spikes/d7-layout/gen-b/klee/Cargo.lock new file mode 100644 index 0000000..c9cdabf --- /dev/null +++ b/spikes/d7-layout/gen-b/klee/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "d7-gen-klee" +version = "0.0.0" diff --git a/spikes/d7-layout/gen-b/klee/Cargo.toml b/spikes/d7-layout/gen-b/klee/Cargo.toml new file mode 100644 index 0000000..93478a3 --- /dev/null +++ b/spikes/d7-layout/gen-b/klee/Cargo.toml @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-3.0-only +# D7 spike (Task 1.1), strategy B: the klee nondet generator as a no_std crate +# of its own, emitted as LLVM 16 bitcode and linked next to the core. +[package] +name = "d7-gen-klee" +version = "0.0.0" +edition = "2021" +rust-version = "1.72" +publish = false + +[lib] +name = "d7gen_klee" +crate-type = ["staticlib"] +path = "src/lib.rs" + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +panic = "abort" +debug = false + +[workspace] diff --git a/spikes/d7-layout/gen-b/klee/src/lib.rs b/spikes/d7-layout/gen-b/klee/src/lib.rs new file mode 100644 index 0000000..eb1ea5f --- /dev/null +++ b/spikes/d7-layout/gen-b/klee/src/lib.rs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-3.0-only +//! D7 spike, strategy B: NonDetGeneratorKlee as a no_std crate. +#![no_std] +use core::ffi::{c_char, c_int, c_uint}; + +#[path = "../../common.rs"] +#[macro_use] +mod common; + +extern "C" { + fn __map2check_main__(argc: c_int, argv: *mut *mut c_char) -> c_int; + fn klee_make_symbolic(addr: *mut u8, nbytes: usize, name: *const u8); + fn klee_get_value_i64(v: i64) -> i64; + fn klee_assume(cond: usize); + fn _exit(code: c_int) -> !; + fn malloc(n: usize) -> *mut u8; + fn free(p: *mut u8); + fn memcpy(d: *mut u8, s: *const u8, n: usize) -> *mut u8; + // the core (Map2CheckFunctions.bc) + fn nondet_log_init(); + fn nondet_log_destroy(); + fn map2check_nondet_memory_byte(line: c_uint, scope: c_uint, v: c_int, f: *const u8); + fn __m2c_rt_nondet_log_len() -> c_uint; + fn __m2c_rt_nondet_log_value(i: c_uint) -> *mut i64; + fn __m2c_rt_nondet_log_to_file(); +} + +unsafe fn symbolic(p: *mut u8, n: usize, name: *const u8) { + klee_make_symbolic(p, n, name); +} +nondet_types!(symbolic); + +#[no_mangle] +pub unsafe extern "C" fn main(argc: c_int, argv: *mut *mut c_char) -> c_int { + __map2check_main__(argc, argv) +} + +#[no_mangle] +pub unsafe extern "C" fn map2check_non_det_memory(dest: *mut u8, size: usize) { + if size == 0 { + return; + } + let b = malloc(size); + if b.is_null() { + return; + } + klee_make_symbolic(b, size, b"non_det_memory\0".as_ptr()); + memcpy(dest, b, size); + for i in 0..size { + map2check_nondet_memory_byte(0, 0, *b.add(i) as c_int, b"__VERIFIER_nondet_memory\0".as_ptr()); + } + free(b); +} + +#[no_mangle] +pub unsafe extern "C" fn nondet_assume(expr: c_int) { + if expr == 0 { + let mut pruned = 0u8; + klee_make_symbolic(&mut pruned, 1, b"map2check_pruned\0".as_ptr()); + _exit(0); + } +} + +#[no_mangle] +pub unsafe extern "C" fn nondet_init() { + nondet_log_init(); +} + +#[no_mangle] +pub unsafe extern "C" fn nondet_destroy() { + nondet_log_destroy(); +} + +#[no_mangle] +pub unsafe extern "C" fn nondet_generate_aux_witness_files() { + let n = __m2c_rt_nondet_log_len(); + if n > 1024 { + return; + } + for i in 0..n { + let p = __m2c_rt_nondet_log_value(i); + let v = *p; + let k = klee_get_value_i64(v); + klee_assume((v == k) as usize); + *p = k; + } + __m2c_rt_nondet_log_to_file(); +} diff --git a/spikes/d7-layout/gen-b/none/Cargo.lock b/spikes/d7-layout/gen-b/none/Cargo.lock new file mode 100644 index 0000000..f82016e --- /dev/null +++ b/spikes/d7-layout/gen-b/none/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "d7-gen-none" +version = "0.0.0" diff --git a/spikes/d7-layout/gen-b/none/Cargo.toml b/spikes/d7-layout/gen-b/none/Cargo.toml new file mode 100644 index 0000000..edb8e56 --- /dev/null +++ b/spikes/d7-layout/gen-b/none/Cargo.toml @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-3.0-only +# D7 spike (Task 1.1), strategy B: the none nondet generator as a no_std crate +# of its own, emitted as LLVM 16 bitcode and linked next to the core. +[package] +name = "d7-gen-none" +version = "0.0.0" +edition = "2021" +rust-version = "1.72" +publish = false + +[lib] +name = "d7gen_none" +crate-type = ["staticlib"] +path = "src/lib.rs" + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +panic = "abort" +debug = false + +[workspace] diff --git a/spikes/d7-layout/gen-b/none/src/lib.rs b/spikes/d7-layout/gen-b/none/src/lib.rs new file mode 100644 index 0000000..ee03bae --- /dev/null +++ b/spikes/d7-layout/gen-b/none/src/lib.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-3.0-only +//! D7 spike, strategy B: NonDetGeneratorNone as a no_std crate. +#![no_std] +use core::ffi::c_int; + +#[path = "../../common.rs"] +#[macro_use] +mod common; + +extern "C" { + fn __map2check_main__() -> c_int; + fn memset(p: *mut u8, c: c_int, n: usize) -> *mut u8; +} + +#[no_mangle] +pub unsafe extern "C" fn main() -> c_int { + __map2check_main__(); + 0 +} + +macro_rules! zero { + ($($name:ident: $t:ty;)*) => {$( + #[no_mangle] + pub extern "C" fn $name() -> $t { 0 as $t } + )*}; +} +zero! { + map2check_non_det_int: i32; map2check_non_det_uint: u32; map2check_non_det_char: i8; + map2check_non_det_ushort: u16; map2check_non_det_short: i16; map2check_non_det_double: f64; + map2check_non_det_pointer: usize; map2check_non_det_long: i64; map2check_non_det_float: f32; + map2check_non_det_longlong: i64; map2check_non_det_ulonglong: u64; map2check_non_det_u8: u8; + map2check_non_det_u16: u16; map2check_non_det_u32: u32; +} + +#[no_mangle] +pub unsafe extern "C" fn map2check_non_det_memory(dest: *mut u8, size: usize) { + memset(dest, 0, size); +} +#[no_mangle] +pub extern "C" fn nondet_assume(_expr: c_int) {} +#[no_mangle] +pub extern "C" fn nondet_init() {} +#[no_mangle] +pub extern "C" fn nondet_destroy() {} +#[no_mangle] +pub extern "C" fn nondet_generate_aux_witness_files() {} diff --git a/spikes/d7-layout/measure-afl.sh b/spikes/d7-layout/measure-afl.sh new file mode 100755 index 0000000..efd69fa --- /dev/null +++ b/spikes/d7-layout/measure-afl.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-only +# D7 spike (Task 1.1): AFL++ throughput and time to first crash for one +# installation. The frontend itself builds the binaries (map2check --debug +# --nondet-generator afl: afl-clang-fast -g -O2 on -result.bc, plus the +# AFL_LLVM_CMPLOG=1 companion); then afl-fuzz runs REPS times, in sequence, +# with the frontend's environment and flags (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 afl-fuzz -i -o afl-out -c -- +# under `timeout SECS`. +# +# Usage: measure-afl.sh