Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
c153851
chore(config): update .ctrace-analyzer.cfg
SizzleUnrlsd Mar 9, 2026
89db13b
build(cmake): update CMakeLists.txt
SizzleUnrlsd Mar 9, 2026
ddc0579
docs(readme): update README.md
SizzleUnrlsd Mar 9, 2026
5e654ec
refactor(include): update StackUsageAnalyzer.hpp
SizzleUnrlsd Mar 9, 2026
6be71e6
fix(analysis): update AllocaUsage.hpp
SizzleUnrlsd Mar 9, 2026
aa76bed
fix(analysis): update BufferWriteModel.hpp
SizzleUnrlsd Mar 9, 2026
e9bb219
fix(analysis): update ConstParamAnalysis.hpp
SizzleUnrlsd Mar 9, 2026
fb94a1f
fix(analysis): update FunctionFilter.hpp
SizzleUnrlsd Mar 9, 2026
04158c9
fix(analysis): update GlobalReadBeforeWriteAnalysis.hpp
SizzleUnrlsd Mar 9, 2026
b6987b8
fix(analysis): update InputPipeline.hpp
SizzleUnrlsd Mar 9, 2026
ad02840
fix(analysis): update IntRanges.hpp
SizzleUnrlsd Mar 9, 2026
0163258
fix(analysis): update IntegerOverflowAnalysis.hpp
SizzleUnrlsd Mar 9, 2026
affa01a
fix(analysis): update InvalidBaseReconstruction.hpp
SizzleUnrlsd Mar 9, 2026
fc82fc9
fix(analysis): update MemIntrinsicOverflow.hpp
SizzleUnrlsd Mar 9, 2026
9f65d9e
fix(analysis): update NullDerefAnalysis.hpp
SizzleUnrlsd Mar 9, 2026
78d074c
fix(analysis): update OOBReadAnalysis.hpp
SizzleUnrlsd Mar 9, 2026
c282af9
fix(analysis): update ResourceLifetimeAnalysis.hpp
SizzleUnrlsd Mar 9, 2026
2ea2468
fix(analysis): update SizeMinusKWrites.hpp
SizzleUnrlsd Mar 9, 2026
ef21216
fix(analysis): update StackBufferAnalysis.hpp
SizzleUnrlsd Mar 9, 2026
3d35f56
fix(analysis): update StackComputation.hpp
SizzleUnrlsd Mar 9, 2026
c14b35e
fix(analysis): update UninitializedVarAnalysis.hpp
SizzleUnrlsd Mar 9, 2026
f1a165e
fix(smt): update ConstraintIR.hpp
SizzleUnrlsd Mar 9, 2026
e11b3e1
fix(smt): update SmtRefinement.hpp
SizzleUnrlsd Mar 9, 2026
901b901
fix(smt): update SolverOrchestrator.hpp
SizzleUnrlsd Mar 9, 2026
ce350a2
fix(smt): update SolverTypes.hpp
SizzleUnrlsd Mar 9, 2026
a2fdf3d
fix(analyzer): update LocationResolver.hpp
SizzleUnrlsd Mar 9, 2026
b3d2cf7
fix(app): update AnalyzerApp.hpp
SizzleUnrlsd Mar 9, 2026
4c8a325
fix(cli): update ArgParser.hpp
SizzleUnrlsd Mar 9, 2026
270b4a6
fix(cli): update main.cpp
SizzleUnrlsd Mar 9, 2026
7837102
fix(analysis): update AnalyzerUtils.cpp
SizzleUnrlsd Mar 9, 2026
f38678b
fix(analysis): update ConstParamAnalysis.cpp
SizzleUnrlsd Mar 9, 2026
5981bbb
fix(analysis): update DuplicateIfCondition.cpp
SizzleUnrlsd Mar 9, 2026
05b932f
fix(analysis): update FrontendDiagnostics.cpp
SizzleUnrlsd Mar 9, 2026
5d6abb5
fix(analysis): update GlobalReadBeforeWriteAnalysis.cpp
SizzleUnrlsd Mar 9, 2026
b2b227c
fix(analysis): update InputPipeline.cpp
SizzleUnrlsd Mar 9, 2026
b9a9493
fix(analysis): update IntegerOverflowAnalysis.cpp
SizzleUnrlsd Mar 9, 2026
2893d26
fix(analysis): update InvalidBaseReconstruction.cpp
SizzleUnrlsd Mar 9, 2026
eb8b4bd
fix(analysis): update MemIntrinsicOverflow.cpp
SizzleUnrlsd Mar 9, 2026
cd0a427
fix(analysis): update OOBReadAnalysis.cpp
SizzleUnrlsd Mar 9, 2026
4aed946
fix(analysis): update ResourceLifetimeAnalysis.cpp
SizzleUnrlsd Mar 9, 2026
0fc78c9
fix(analysis): update StackBufferAnalysis.cpp
SizzleUnrlsd Mar 9, 2026
b51ba8d
fix(analysis): update StackComputation.cpp
SizzleUnrlsd Mar 9, 2026
66301d3
fix(analysis): update StackPointerEscape.cpp
SizzleUnrlsd Mar 9, 2026
d1df3ce
fix(analysis): update StackPointerEscapeInternal.hpp
SizzleUnrlsd Mar 9, 2026
9aa2372
fix(analysis): update TOCTOUAnalysis.cpp
SizzleUnrlsd Mar 9, 2026
aa12c03
fix(analysis): update UninitializedVarAnalysis.cpp
SizzleUnrlsd Mar 9, 2026
2c72f7a
fix(smt): update SmtEncoding.cpp
SizzleUnrlsd Mar 9, 2026
8f8ffe9
fix(smt): update SolverOrchestrator.cpp
SizzleUnrlsd Mar 9, 2026
8d7f961
fix(smt): update Z3Backend.cpp
SizzleUnrlsd Mar 9, 2026
dcbfe7f
fix(analyzer): update AnalysisPipeline.cpp
SizzleUnrlsd Mar 9, 2026
fdaa8d5
fix(analyzer): update DiagnosticEmitter.cpp
SizzleUnrlsd Mar 9, 2026
9f8bbb5
fix(app): update AnalyzerApp.cpp
SizzleUnrlsd Mar 9, 2026
dfc4ea8
fix(cli): update ArgParser.cpp
SizzleUnrlsd Mar 9, 2026
5d094a2
docs(architecture): update smt-implementation-plans.md
SizzleUnrlsd Mar 9, 2026
cc96b40
docs(architecture): update smt-solver-integration.md
SizzleUnrlsd Mar 9, 2026
923a5e4
test(analysis): update duplicate-if-unreachable-elseif-fp.cpp
SizzleUnrlsd Mar 9, 2026
57991e8
test(analysis): update uninitialized-beststack-est.cpp
SizzleUnrlsd Mar 9, 2026
e884e74
test(analysis): update uninitialized-default-config-wrapper.cpp
SizzleUnrlsd Mar 9, 2026
95fc707
test(analysis): update uninitialized-info-temp-map.cpp
SizzleUnrlsd Mar 9, 2026
f9dae8f
test(analysis): update uninitialized-local-maxcallee.cpp
SizzleUnrlsd Mar 9, 2026
33a6e60
test(analysis): update uninitialized-merged-return.cpp
SizzleUnrlsd Mar 9, 2026
e24dd65
test(analysis): update global-array-read-without-local-write.c
SizzleUnrlsd Mar 9, 2026
9f13664
test(analysis): update uninitialized-local-cpp-bitfield-copy-ctor.cpp
SizzleUnrlsd Mar 9, 2026
7807801
test(analysis): update uninitialized-local-cpp-bitfield-default-membe…
SizzleUnrlsd Mar 9, 2026
3f1c284
test(analysis): update uninitialized-local-cpp-bitfield-missing-init.cpp
SizzleUnrlsd Mar 9, 2026
d5fd5fc
chore(style): format code with clang-format
SizzleUnrlsd Mar 9, 2026
65ae38a
build(cmake): gate -Wreorder-init-list by compiler support
SizzleUnrlsd Mar 9, 2026
4a88cb8
ci(workflow): restrict build triggers to main and cancel stale runs
SizzleUnrlsd Mar 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .ctrace-analyzer.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ buffer-model=models/buffer-overflow/generic.txt
# compile-commands=build/compile_commands.json
analysis-profile=full
jobs=auto
compile-ir-cache-dir=.cache/compile-ir

# Output behavior
warnings-only=true
Expand All @@ -19,8 +20,8 @@ timing=false
# Inter-TU analysis
resource-cross-tu=true
uninitialized-cross-tu=true
#resource-summary-cache-dir=.cache/resource-lifetime
resource-summary-cache-memory-only=true
resource-summary-cache-dir=.cache/resource-lifetime
resource-summary-cache-memory-only=false

# SMT configuration
smt=on
Expand Down
12 changes: 7 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
name: Build

on:
push:
branches:
- "**"
pull_request:
branches:
- "**"
branches: [main]
push:
branches: [main]
workflow_dispatch:

concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
cancel-in-progress: true

env:
CCACHE_DIR: ${{ github.workspace }}/.ccache

Expand Down
37 changes: 34 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ project(stack_usage_analyzer)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
include(CheckLLVMVersion OPTIONAL)
include(CheckCXXCompilerFlag)

if(COMMAND check_llvm_version)
set(LLVM_MIN_REQUIRED_VERSION "19" CACHE STRING "Minimum required LLVM version")
Expand Down Expand Up @@ -51,9 +52,15 @@ message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
# Options de build
option(BUILD_CLI "Build stack_usage_analyzer CLI tool" ON)
option(BUILD_SHARED_LIB "Build shared library variant" ON)
option(ENABLE_STACK_USAGE "Emit per-function stack usage (.su) files" OFF)
option(ENABLE_STACK_USAGE "Emit per-function stack usage (.su) files" ON)
option(ENABLE_WARN_PADDED "Enable -Wpadded warnings" ON)
option(ENABLE_WARN_REORDER_INIT_LIST "Enable -Wreorder-init-list when supported" ON)
option(ENABLE_Z3_BACKEND "Enable optional Z3 SMT backend if Z3 is available" ON)

if(ENABLE_WARN_REORDER_INIT_LIST)
check_cxx_compiler_flag("-Wreorder-init-list" CTRACE_STACK_HAS_WREORDER_INIT_LIST)
endif()

# ===========================
# Communs Sources
# ===========================
Expand Down Expand Up @@ -114,7 +121,6 @@ if(ENABLE_Z3_BACKEND)
endif()
endif()

include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})

# llvm_map_components_to_libnames(llvm_libs
Expand All @@ -128,6 +134,10 @@ add_library(stack_usage_analyzer_lib STATIC
${STACK_ANALYZER_SOURCES}
)

if(ENABLE_WARN_REORDER_INIT_LIST AND CTRACE_STACK_HAS_WREORDER_INIT_LIST)
target_compile_options(stack_usage_analyzer_lib PRIVATE -Wreorder-init-list)
endif()

if(CTRACE_STACK_HAVE_Z3_BACKEND)
target_compile_definitions(stack_usage_analyzer_lib PUBLIC CTRACE_STACK_ENABLE_Z3_BACKEND=1)
if(TARGET z3::libz3)
Expand All @@ -137,7 +147,7 @@ if(CTRACE_STACK_HAVE_Z3_BACKEND)
elseif(DEFINED Z3_LIBRARIES)
target_link_libraries(stack_usage_analyzer_lib PUBLIC ${Z3_LIBRARIES})
if(DEFINED Z3_INCLUDE_DIRS)
target_include_directories(stack_usage_analyzer_lib PUBLIC ${Z3_INCLUDE_DIRS})
target_include_directories(stack_usage_analyzer_lib SYSTEM PUBLIC ${Z3_INCLUDE_DIRS})
endif()
endif()
endif()
Expand All @@ -162,9 +172,21 @@ target_include_directories(stack_usage_analyzer_lib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

target_include_directories(stack_usage_analyzer_lib SYSTEM
PUBLIC
${LLVM_INCLUDE_DIRS}
)

# Treat external dependency headers as system includes to reduce warning noise
# from third-party code when project warning levels are strict.
target_include_directories(stack_usage_analyzer_lib SYSTEM
PUBLIC
$<TARGET_PROPERTY:cc::compilerlib_static,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:coretrace::logger,INTERFACE_INCLUDE_DIRECTORIES>
)

# ALIAS FOR USE WITH FETCHCONTENT
add_library(coretrace::stack_usage_analyzer_lib ALIAS stack_usage_analyzer_lib)

Expand Down Expand Up @@ -215,6 +237,10 @@ if(BUILD_CLI)
main.cpp
)

if(ENABLE_WARN_REORDER_INIT_LIST AND CTRACE_STACK_HAS_WREORDER_INIT_LIST)
target_compile_options(stack_usage_analyzer PRIVATE -Wreorder-init-list)
endif()

target_link_libraries(stack_usage_analyzer
PRIVATE
stack_usage_analyzer_lib
Expand All @@ -226,6 +252,11 @@ if(BUILD_CLI)
target_compile_options(stack_usage_analyzer PRIVATE -fstack-usage)
endif()

if(ENABLE_WARN_PADDED AND CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
target_compile_options(stack_usage_analyzer_lib PRIVATE -Wpadded)
target_compile_options(stack_usage_analyzer PRIVATE -Wpadded)
endif()

if(ENABLE_DEBUG_ASAN)
target_compile_options(stack_usage_analyzer PRIVATE -fsanitize=address -fno-omit-frame-pointer -g)
target_link_options(stack_usage_analyzer PRIVATE -fsanitize=address)
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ Ready-to-adapt workflow examples:
--no-resource-cross-tu disables cross-TU resource summaries
--resource-summary-cache-dir=<path> sets cache directory for cross-TU resource summaries (default: .cache/resource-lifetime)
--resource-summary-cache-memory-only keeps cross-TU summary cache in memory only (process-local, no files)
--compile-ir-cache-dir=<path> enables dependency-aware LLVM IR compile cache for unchanged source files
--timing prints compile/analysis timings to stderr
--config=<path> loads optional key=value config file (CLI flags override config values)
--print-effective-config prints resolved runtime config to stderr
Expand Down Expand Up @@ -268,6 +269,8 @@ To generate `compile_commands.json` with CMake, configure with
If analysis feels slow, `--compdb-fast` disables heavy flags (optimizations,
sanitizers, profiling) while keeping include paths and macros.
For multi-file runs, `--jobs=<N|auto>` parallelizes input loading; with cross-TU enabled it also parallelizes summary construction.
`--compile-ir-cache-dir=<path>` reuses compiled LLVM IR for unchanged translation units
based on source/dependency stamps, which reduces repeated C/C++ frontend cost across runs.
When inputs are auto-discovered from `compile_commands.json`, `_deps` entries are skipped by default
to keep analysis focused on project code; use `--include-compdb-deps` to opt back in.

Expand Down Expand Up @@ -305,6 +308,7 @@ Supported keys:
- `uninitialized-cross-tu`
- `resource-summary-cache-dir`
- `resource-summary-cache-memory-only`
- `compile-ir-cache-dir`

Example file:

Expand All @@ -315,6 +319,7 @@ buffer-model=models/buffer-overflow/generic.txt
compile-commands=build/compile_commands.json
analysis-profile=full
jobs=auto
compile-ir-cache-dir=.cache/compile-ir
smt=on
smt-backend=z3
smt-rules=recursion,integer-overflow,size-minus-k,stack-buffer,oob-read
Expand Down
151 changes: 151 additions & 0 deletions docs/architecture/smt-implementation-plans.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
Voici comment je vois l'évolution, ordonnée par ROI décroissant et dépendances techniques.

---

## Phase 1 — Encoder LLVM IR → ConstraintIR expressif
*Le verrou qui bloque tout le reste*

Aujourd'hui `encodeRangeConstraints` ne sait encoder que des intervalles. Pour que Z3 apporte une vraie valeur sur les autres analyses, il faut un encoder qui traduit les instructions LLVM en expressions symboliques.

**Ce qu'il faut couvrir :**

| LLVM IR | ConstraintIR |
|---|---|
| `add nsw i32 %a, %b` | `Add(sym_a, sym_b, 32)` + assertion overflow = poison |
| `icmp slt i32 %x, 42` | `Slt(sym_x, Const(42, 32))` |
| `br i1 %cond, label %then, label %else` | Edge constraint sur les deux branches |
| `phi [%v1, %bb1], [%v2, %bb2]` | Disjonction : `Or(And(from_bb1, eq(sym, v1)), And(from_bb2, eq(sym, v2)))` |
| `getelementptr [10 x i32], ptr %p, i64 0, i64 %idx` | `Add(base, Mul(idx, 4))` avec bound `0 ≤ idx < 10` |
| `select i1 %c, i32 %a, i32 %b` | `ITE(cond, sym_a, sym_b)` (nécessite ajout `ExprKind::Select`) |
| `llvm.assume(i1 %x)` | Assertion directe |
| `freeze` | Marquer le symbole comme "valeur définie" |

**Livrable :** un `LlvmConstraintEncoder` qui prend un chemin de blocs basiques et produit un `ConstraintIR` complet. Le `ConstraintIrBuilder` est déjà prêt — c'est la traversée LLVM qui manque.

---

## Phase 2 — Onboarding IntegerOverflow + SizeMinusK
*Le plus gros gisement de FP réductibles*

Avec l'encoder de la phase 1, on peut brancher le SMT sur les analyses à fort FP :

**IntegerOverflowAnalysis** — Aujourd'hui l'analyse détecte les overflows sur les arguments de `malloc`/`memcpy`. Le pattern type de FP :
```c
size_t n = get_count(); // range inconnu
void *p = malloc(n * sizeof(int)); // flaggé overflow
```
Avec Z3 : encoder `n * 4` en bitvector 64 bits, vérifier si l'overflow est satisfiable sous les contraintes de chemin. Si `UNSAT` → supprimer le diagnostic.

**SizeMinusKWrites** — Même principe : les off-by-one sont souvent des FP quand la taille est prouvablement > k.

**Point d'intégration :** après la passe heuristique existante, avant émission. Exactement ce que le doc prévoit. Le `RecursionConstraintEvaluator` sert de modèle — créer un `OverflowConstraintEvaluator` analogue.

---

## Phase 3 — StackBuffer + OOBRead path-sensitive
*Précision sur les accès mémoire*

L'analyse de buffer overflow actuelle est flow-insensitive — elle ne suit pas les contraintes de chemin sur les indices. Avec le SMT :

```c
int buf[10];
int idx = get_index();
if (idx >= 0 && idx < 10) {
buf[idx] = 42; // aujourd'hui : flaggé potentiel overflow
// avec SMT : UNSAT → sûr
}
```

C'est le même pattern que la récursion : BFS avec accumulation de contraintes, sauf que la question est "cet accès est-il dans les bornes ?" au lieu de "cette base case est-elle atteignable ?".

**Possibilité de factoriser** la traversée path-sensitive de `hasFeasibleNonRecursiveReturnPath` en un framework réutilisable :

```
PathExplorer<Question>
- traverse le CFG en BFS
- accumule les contraintes
- pose une Question à chaque point d'intérêt
- retourne Feasible / Infeasible / Inconclusive
```

---

## Phase 4 — TypeConfusion avec raisonnement struct-layout
*Les 91 FPs documentés*

C'est le plus gros lot de FP en volume mais le plus complexe à encoder. Il faut :

1. Encoder les layouts de structs (offsets des champs, tailles) comme contraintes
2. Pour chaque "conflit de type" détecté, vérifier si les deux vues sont sur des sous-objets légalement imbriqués
3. Si Z3 prouve que `offset_A + size_A ≤ offset_B` ou que les deux accès sont au même sous-objet → `UNSAT` → supprimer

Ça nécessite probablement un ajout au `ConstraintIR` : des contraintes de type mémoire/layout, pas seulement arithmétiques.

---

## Phase 5 — DiagnosticRefiner comme composant autonome
*Quand plusieurs analyses utilisent le SMT*

Dès que 3+ analyses passent par le SMT, la logique de décision (keep/suppress/downgrade/tag inconclusive) mérite d'être extraite :

```cpp
class DiagnosticRefiner {
RefinementDecision refine(const Diagnostic& original,
const SmtDecision& decision,
const RulePolicy& policy) const;
};

enum class RefinementDecision {
Keep, // SAT ou pas de SMT
Suppress, // UNSAT confirmé
Downgrade, // UNSAT mais confidence < seuil → Warning→Info
MarkInconclusive // Unknown/Timeout
};
```

Avec des politiques par rule :
- `recursion` : suppress agressif (UNSAT = pas de bug)
- `integer-overflow` : downgrade plutôt que suppress (risque d'encoding bug)
- `type-confusion` : suppress seulement en dual-consensus

---

## Phase 6 — Cache + Observabilité
*Quand le volume de queries justifie l'investissement*

**Cache :**
- Clé : hash du `ConstraintIR` normalisé + mode/backend
- Valeur : `SmtStatus`
- Portée : process-local (un seul run d'analyse)
- ROI : significatif en mode portfolio/cross-check ou quand plusieurs analyses posent des questions similaires sur les mêmes fonctions

**Observabilité :**
- Compteurs : queries total / sat / unsat / unknown / timeout / error
- Latence : P50/P95/P99 par backend
- Suppressions : count par rule
- Format : JSON ou intégration dans le `--timing` existant

---

## Phase 7 — Améliorations Z3 avancées
*Optimisations pour passer à l'échelle*

- **Incremental solving** : réutiliser le `z3::solver` avec `push/pop` au lieu de recréer un contexte par query. Gros gain sur les traversées de chemin où les queries partagent un préfixe commun
- **Tactics** : utiliser `z3::tactic("simplify") & z3::tactic("solve-eqs") & z3::tactic("bit-blast") & z3::tactic("sat")` pour les formules bitvector plutôt que le solveur par défaut
- **Parallel portfolio réel** : `std::async` pour lancer Z3 et cvc5 en parallèle dans le mode portfolio (aujourd'hui c'est séquentiel)

---

## Résumé visuel

```
Phase 1: Encoder LLVM→ConstraintIR ← verrou technique
Phase 2: IntegerOverflow + SizeMinusK ← plus gros ROI en FP
Phase 3: StackBuffer + OOBRead ← path-sensitive generalisé
Phase 4: TypeConfusion ← 91 FPs mais complexe
Phase 5: DiagnosticRefiner autonome ← quand 3+ rules utilisent SMT
Phase 6: Cache + Observabilité ← quand le volume le justifie
Phase 7: Incrémental + tactics + // ← scaling
```

Les phases 1-2 sont les plus urgentes. Les phases 3-4 dépendent de la qualité de l'encoder. Les phases 5-7 sont de l'optimisation.
Loading
Loading