README.md

April 29, 2026 · View on GitHub

Static Analyzer Factory logo

SAF — Static Analyzer Factory

License: MIT Python 3.12+ Rust 1.85+

A Rust-powered static analysis framework with a Python SDK for finding bugs in C/C++ programs. SAF turns LLVM IR into analyzable graphs — pointer analysis, value-flow, taint tracking — and exposes them through a clean Python API and CLI.

Key Features

  • Pointer analysis — Andersen-style with field sensitivity, context-sensitive (k-CFA), and flow-sensitive variants
  • Value-flow graphs — SSA + memory + interprocedural edges for precise data-flow tracking
  • Taint analysis — source/sink/sanitizer framework with trace extraction
  • IFDS solver — interprocedural, finite, distributive subset analysis
  • Built-in checkers — memory leaks, null dereference, double-free, use-after-free, and more
  • Python SDK — first-class API for scripting custom analyses
  • CLI — full analysis pipeline from the command line
  • Deterministic — identical inputs always produce byte-identical outputs
  • SARIF export — standard format for IDE and CI integration

Quick Start

SAF runs inside Docker. Two image variants are published, one per supported LLVM version — pick the tag whose LLVM matches the clang you use to compile your source.

git clone https://github.com/Static-Analyzer-Factory/static-analyzer-factory.git
cd static-analyzer-factory
make shell            # dev shell backed by LLVM 18 (default)
make shell-llvm22     # dev shell backed by LLVM 22 (opt-in)

See docs/book/src/getting-started/llvm-versions.md for the support policy and forward-incompatibility caveats.

Python SDK

from saf import Project, sources, sinks

proj = Project.open("program.ll")
q = proj.query()

# Find taint flows from user input to dangerous sinks
findings = q.taint_flow(
    sources=sources.function_param("main", 1),   # argv
    sinks=sinks.call("system", arg_index=0),
)

for f in findings:
    print(f"{f.severity}: {f.message}")
    print(f"  {f.source_location} -> {f.sink_location}")

CLI

# Run all built-in checkers
saf run program.ll --checkers all --format json --output findings.json

# Export call graph as DOT
saf export callgraph --input program.ll --format dot --output cg.dot

# Query points-to set for a specific value
saf query points-to 0x00000042 --input program.ll

Architecture

crates/
  saf-core/       # AIR (Analysis IR), config, deterministic IDs
  saf-frontends/  # LLVM bitcode + AIR-JSON frontends
  saf-analysis/   # CFG, call graph, PTA, value-flow, checkers
  saf-cli/        # Command-line interface
  saf-python/     # Python SDK (PyO3 bindings)
  saf-wasm/       # Browser build (playground)

Data flow:

Input (.ll / .bc)
  → Frontend → AIR (canonical IR)
    → Graph builders (CFG, call graph, def-use)
      → Pointer analysis → Value-flow graph
        → Queries & checkers → Findings (JSON / SARIF)

Benchmark Results

SAF's memory-safety checkers evaluated on the NIST Juliet C/C++ Test Suite, compared against SVF and Lotus.

Memory Leak (CWE-401) — 1,408 tests

ToolTPFPFNTNPrecisionRecallF1
SAF694851661389.1%97.7%0.932
SVF6661444455482.2%93.8%0.876

Double Free (CWE-415) — 385 tests

ToolTPFPFNTNPrecisionRecallF1
SAF18051518597.3%92.3%0.947
SVF170025190100.0%87.2%0.932
Lotus163343215682.7%83.6%0.829

Use-After-Free (CWE-416) — 236 tests

ToolTPFPFNTNPrecisionRecallF1
SAF9042811495.7%76.3%0.849
Lotus92142610486.8%78.0%0.784

Null Pointer Dereference (CWE-476) — 468 tests

ToolTPFPFNTNPrecisionRecallF1
SAF188794615570.4%80.3%0.750
Lotus199553517978.3%85.0%0.792

Comparison with Other Tools

SAF is one of several program-analysis frameworks for LLVM IR and source code. The table below shows how SAF and its peers handle the dimensions program-analysis users compare on most often. Each peer has its own strengths — SVF is the established LLVM-IR value-flow framework, Phasar is the reference IFDS/IDE solver, Lotus bundles a broad catalog of alias analyses and concurrency analyses, CodeQL has the largest query library across many languages, and Infer ships deep checker libraries for industrial source-level analysis. The table aims to be informative, not a ranking. The same data is also rendered at the Comparison page.

Compared with: SVF · Phasar · Lotus · CodeQL · Infer

DimensionSAFSVFPhasarLotusCodeQLInfer
Primary IR / targetLLVM IR (C/C++)LLVM IR (C/C++)LLVM IR (C/C++)LLVM IR (C/C++)Source DB (10+ langs)Source AST (C/C++/ObjC/Java)
Pointer analysisCI, FS, CS, DDACI, FS, CS, DDAComputed internally (CG + AA)Many bundled (DyckAA, SparrowAA, Sea-DSA, …)Different paradigmDifferent paradigm
PTA solver backendsWorklist + Datalog (Ascent)Worklist (wave + bit-vector)LLVM AA + own CG algorithmsInclusion + unification + DDAQL → Datalog evaluationSeparation logic
Value-flow / SVFGYesYes (headline)No (IFDS-based)DyckVFG variantDataFlow moduleNo
Memory SSAYes (hybrid: skeleton + demand-driven)Yes (MemSSA + MemRegion)No (IFDS-based instead)DyckVFG insteadDifferent paradigmDifferent paradigm
IFDS / IDE solverYesNoYes (specialty)Bundles PhasarNoNo
Taint analysisYes (source/sink/sanitizer)Built on SVFGYes (IFDS + IDE clients)Within KINTYes (TaintTracking::Global)Yes (Quandary + Pulse)
Numeric / abstract domainsIntervals, octagons, nullness, SCCPIntervals, numeric, relational (AE)Monotone framework (intra + inter)Symbolic execution + constant-time analysisRange analysis (built into queries)Pulse abstract domain + InferBO intervals
Concurrency / MTALockset + MHPLockAnalysis, MHP, TCTOut of scopeRace, MPI, OpenMP, CUDA, kernelRace-detection queriesRacerD
SMT-backed reasoningZ3 (conditions, reachability, alias)Saber path-sensitive solverPathSensitivity moduleSMT solvers + KINT + symbolic executionDatalog evaluation, no SMTPulse uses SMT
Built-in checkers5+ memory + taint3 SABER (leak, dbl-free, file)15+ IFDS/IDE clientsMany (FiTx, KINT, Saber, Concurrency, Security)Hundreds per language30+ (Pulse, RacerD, …)
Custom checker authoringDeclarative YAML specs (may_reach modes)C++ subclassing (Saber framework)C++ IFDS/IDE problem subclassesC++ checker plug-insQL queries (the language is the checker)OCaml + Infer.AI abstract domain
Interactive graph query APIPython SDK + CLI (points-to, flows, taint)C++ API (raw graph traversal)C++ API (solver results)C++ API (raw)QL (queries are the model)Results only
Specialized data structuresRoaring + FxHash + frozen indexer + RayonBit-vector points-to (BVDataPTAImpl)EdgeFunctionSingletonCache + SOOVaries by bundled backendBDDs + Datalog indexesBi-abductive summaries + cache
SARIF exportNativeNoNoNoNative (default)External adapter
LicenseMITAGPL-3.0MITMIT (mixed deps)Queries MIT; CLI proprietaryMIT
Primary SDKPython (Rust core)C++ (Pysvf wrapper)C++ (C++20)C++QL (DSL)OCaml
Multi-LLVM (simultaneous)LLVM 18 + 22One per build (broad history)LLVM 16 + 17LLVM 14N/AN/A
Browser / WASM playgroundYes (Pyodide + WASM)NoNoNoNoNo
Byte-deterministic outputContractual (NFR-DET-001)Not advertised as contractNot advertised as contractNot advertised as contractNot advertised as contractNot advertised as contract
AI-agent skills2 (feature-dev, checker-dev)NoNoAGENTS.md presentNoNo

Every claim above is sourced in plans/189-research-notes.md.

Known Limitations

Analysis precision:

  • Default pointer analysis is context-insensitive; context-sensitive (k-CFA), flow-sensitive, and demand-driven variants are available but may be slower on large programs
  • Array elements are treated as a single abstract object — no per-index tracking

Indirect calls:

  • Indirect call resolution depends on PTA precision — targets that PTA misses are invisible to downstream analyses (ICFG, IFDS, taint)
  • When a call site resolves to multiple targets, only the first is used in value-flow and SVFG

Not yet supported:

  • Source-level frontends (Clang AST, rust-analyzer) — architecture is ready, implementation is planned
  • Symbolic execution

AI-Assisted Development

SAF ships coding-agent skills that guide AI assistants through SAF-specific development workflows. These work with Claude Code, Codex, and other coding agents.

SkillPurposeInstall
saf-feature-dev8-phase workflow for adding features (frontends, analysis, SDK, CLI)claude plugin add skills/saf-feature-dev/claude-code
saf-checker-devSpec-first workflow for creating bug-finding checkersclaude plugin add skills/saf-checker-dev/claude-code

These skills provide SAF-specific guidance including e2e testing recipes, SAF_LOG debug instrumentation, benchmark validation, and determinism checks. See each skill's README for details.

Documentation

  • Docs — concepts, API reference, getting started
  • Tutorials — step-by-step guides from hello-taint to custom checkers
  • Playground — try SAF in the browser (WASM build)
  • API Docs — Rust API reference (rustdoc)

Contributing

See CONTRIBUTING.md for development setup, coding conventions, and PR guidelines.

License

MIT