Checker Framework
The Checker Framework provides a unified infrastructure for static bug detection across multiple vulnerability categories. It integrates various analysis techniques to detect security vulnerabilities, concurrency bugs, and numerical errors in LLVM bitcode.
Location: lib/Checker/
Headers: include/Checker/
Tool Frontend: lotus-check with engine runners in tools/checker/
Overview
The Checker Framework consists of several checker categories, all unified through a centralized bug reporting system:
AE Checkers – Abstract-execution-based memory-safety bug detection
FiTx Checkers – Daily development-friendly bug detection using typestate analysis (path-insensitive, return-code aware; see Suzuki et al., USENIX ATC 2024)
KINT Checkers – Numerical bugs (overflow, division by zero, array bounds) using SMT solving and function summaries
IFDS Taint Checker – Configurable source-to-sink information-flow analysis
Concurrency Checkers – Thread safety and parallel-runtime issues (data races, deadlocks, atomicity violations, OpenMP bugs, MPI bugs) using MHP, lock set, OpenMP, and MPI analyses
Pulse Checker – Memory safety and other bugs using biabductive analysis with path-sensitive interprocedural reasoning
Saber Checkers – Source-sink bug detection over sparse value-flow graphs
SymEx Checker – Symbolic-execution bug checks backed by the top-level
SymbolicExecutionengine
All checkers report bugs through the centralized BugReportMgr system, enabling unified output formats (JSON, SARIF) and consistent bug reporting across all analysis tools. The repository builds a single checker binary, lotus-check; each invocation selects one engine with --engine=<name>.
Choosing a Checker
The lotus-check --engine values name analysis engines, not mutually exclusive
vulnerability categories. Several engines intentionally cover the same bug
class while making different precision, scalability, and reporting trade-offs.
Choose the engine according to the workflow and evidence needed; do not infer
that two engines with the same bug class have identical semantics or coverage.
The following table is a navigation aid for the currently exposed checks. A
listed engine supports the corresponding class, but --checks=all only
enables that engine’s own checks.
Bug class |
Start with |
Other applicable engines |
Typical use |
|---|---|---|---|
Use-after-free |
|
|
Witness-oriented investigation |
Null-pointer dereference |
|
|
Memory-safety review |
Buffer or array bounds error |
|
|
Buffer accesses or index arithmetic |
Integer overflow, division by zero, bad shift |
|
|
Numerical-error analysis |
Memory leak |
|
|
Value-flow or typestate resource checking |
Double free |
|
|
Allocation/free protocol checking |
Uninitialized read or use |
|
|
Initialization-state checking |
Tainted data reaches a sink |
|
|
Configurable source-to-sink analysis |
Data race, deadlock, or parallel-runtime error |
|
— |
Thread, OpenMP, or MPI analysis |
API protocol or project-specific policy |
|
|
Declarative checks or fast development feedback |
Quick selection
Use
fitxfor fast, translation-unit-oriented feedback during routine development.Use
aefor a broad abstract-execution memory-safety pass.Use
pulsewhen a path-sensitive, witness-oriented diagnosis is most useful; its bounded analysis can miss bugs outside retained paths.Use
symexwhen SMT-backed path feasibility and symbolic numeric reasoning are needed, accepting a potentially higher analysis cost.Use
saberfor sparse value-flow source/sink checks: memory leaks, double frees, and file-descriptor leaks.Use
kintfor numerical bugs. Its taint analysis supports this purpose; it is not a replacement for the configurabletaintsource-to-sink tool.
Running multiple engines
There is currently no aggregate command that runs multiple native engines or
deduplicates their output across separate lotus-check invocations. Each
frontend may deduplicate reports produced in its own run, but findings from two
engines should be triaged as independent evidence. Record the engine value and
its options with exported JSON or SARIF reports so that overlapping findings
remain distinguishable.
Checker Framework
The Checker Framework (lib/Checker/Framework/) provides the shared
infrastructure for defining and running bug checkers and for reporting their
findings. Declarative checkers are defined through specification files rather
than hardcoded C++ logic, enabling new checks without modifying the checker
engine.
Components:
CheckerSpec — A declarative rule specification with metadata (id, title, severity, category), a rule kind (ForbiddenCall, SourceSink, ApiProtocol), and associated capabilities.
CheckerSpecLoader — Loads
CheckerSpecinstances from YAML/JSON files or directories. Enables packaging reusable check rule sets.CheckerRegistry — Central registry that manages both declarative specs (loaded at runtime from spec files) and native checkers (compiled in). Supports id-based lookup, category filtering, and engine-kind selection.
CheckerDriver — Orchestrates checker execution: selects and runs checkers from the registry over a
CheckerContext, collects diagnostics, and emits results to theBugReportMgr.CheckerDiagnostic — Structured diagnostic with bug type, severity, source location, message, suggestion, confidence, and optional trace steps. Convertible to the
BugReportformat for unified reporting.CheckerContext — Per-module execution context providing the LLVM module and an optional alias-analysis wrapper.
CheckerValidator — Validates checker specifications for consistency (e.g., missing required fields, unknown rule kinds).
BugReport — Bug report data structures with source location information.
BugReportMgr — Centralized bug report management (singleton pattern).
BugTypes — Bug type definitions, classifications, and CWE mappings.
SARIF — SARIF format output support.
ReportOptions — Report configuration options (JSON, SARIF output).
Rule Kinds:
ForbiddenCall — Flags calls to specified functions (e.g.,
system,gets). Pattern: a simple function-name allow/block list.SourceSink — Tracks data flow from sources to sinks with optional sanitizers. Generalizes taint-style vulnerability detection.
ApiProtocol — Checks acquire/use/release protocols for resources (locks, file handles, reference counts). Tracks state transitions and reports leaks, use-after-release, and double-acquire.
Native — A hardcoded C++ checker that registers via the registry API.
Usage:
# Run a declarative checker from a spec file
./build/bin/lotus-check --engine=generic input.bc --checks=forbidden.system
# Load all specs from a directory
./build/bin/lotus-check --engine=generic input.bc --generic.spec-dir=./checker-specs/
# List generic checker ids and native engine entries
./build/bin/lotus-check --list-checkers
#include "Checker/Framework/CheckerRegistry.h"
#include "Checker/Framework/CheckerSpecLoader.h"
lotus::checker::CheckerRegistry registry;
lotus::checker::CheckerSpecLoader loader;
// Load specs from a directory
auto specs = loader.loadFromDirectory("./checker-specs/");
for (const auto &spec : specs.get()) {
registry.registerDeclarative(spec);
}
// Look up and run a checker
auto *descriptor = registry.findById("forbidden.system");
// ... execute via CheckerDriver
Components
Concurrency Checkers (lib/Checker/Concurrency/):
ConcurrencyChecker.cpp– Main concurrency checker coordinatorDataRaceChecker.cpp– Data race detection using MHP (May Happen in Parallel) analysisDeadlockChecker.cpp– Deadlock detection using lock set analysisAtomicityChecker.cpp– Atomicity violation detectionOpenMPChecker.cpp– Dedicated OpenMP bug checks built on OpenMP task analysisMPIChecker.cpp– Dedicated MPI bug checks built on MPI communication/RMA analysis
AE Checkers (lib/Checker/AE/):
AbstractInterpretation.cpp– Fixpoint engine for abstract executionAEDetector.cpp– Bug-specific detector integrationAbstractState.*/AbstractValue.*– State and value abstractions
FiTx Bug Checkers (lib/Checker/FiTx/):
frontend/Framework.cpp– Main FiTx pass; typestate-based daily development-friendly checkers (Suzuki et al., USENIX ATC 2024)frontend/Analyzer.cpp– CFG-based typestate analysis with return-code aware state propagationFramework_IR/Analyzer.cpp– IR builder; collects return values for function summariesDetector/– Typestate definitions per bug pattern
KINT Numerical Checkers (lib/Checker/KINT/):
MKintPass.cpp– Main KINT pass for integer overflow, division by zero, array bounds checkingMKintBugreport.cpp– Bug report generation for KINTMKintSummary.cpp– Interprocedural SMT function-summary constructionSummaryEncoding.cpp– SMT summary representation and instantiationKINTTaintAnalysis.cpp– Taint analysis integration for tracking untrusted dataBugDetection.cpp– Bug detection and reporting logicOptions.cpp– Command-line option parsingLog.cpp– Logging utilitiesUtils.cpp– Utility functions
Pulse Checker (lib/Checker/Pulse/):
PulseChecker.cpp– Main bug finder using biabductive analysisPulseDomain.cpp– Execution domain abstractionPulseAbductiveDomain.h– Core abstract domain with biabductionPulseOperations.cpp– Core memory operations (readDeref, writeDeref, etc.)PulseDisjunctiveDomain.cpp– Disjunctive analysis for path-sensitive reasoningPulseLoopAbstraction.cpp– Loop abstraction with wideningPulseSummary.cpp– Function summary representation and applicationPulseTaint.cpp– Taint analysis for security vulnerabilitiesPulseModels.cpp– Library function modelsPulseDiagnostic.cpp– Rich diagnostic reporting with traces
Saber Checker (lib/Checker/Saber/):
LeakChecker.cpp– Leak detection over source-sink flowsDoubleFreeChecker.cpp– Double-free detectionFileChecker.cpp– File-descriptor leak checkingSaberCheckerAPI.cpp– Reusable checker API surface
Symbolic Execution Checker (lib/SymbolicExecution/):
AnalysisDriver.cpp– Whole-module symbolic execution driverAnalysisState*.cpp– Symbolic state, summaries, taint updates, and bug queriesPathCondSolver.cpp– SMT-backed feasibility checking for path conditionsSymbolicExecutionWrapper.cpp– LLVM pass wrapper used bylotus-check --engine=symex
Debug Info Analysis (lib/Analysis/DebugInfo/):
DebugInfoAnalysis.cpp– Debug information extraction from LLVM metadata
Build Targets
FiTxChecker– FiTx typestate-based bug checker libraryPulseChecker– Pulse biabductive analysis checker libraryCanarySymbolicExecution– Symbolic-execution engine library used bysymexlotus-check– Unified checker frontendtools/checker/lotus-check-ae.cpp– AE engine runnertools/checker/lotus-check-fitx.cpp– FiTx engine runnertools/checker/lotus-check-kint.cpp– KINT engine runnertools/checker/lotus-check-concur.cpp– Concurrency engine runnertools/checker/lotus-check-pulse.cpp– Pulse engine runnertools/checker/lotus-check-saber.cpp– Saber engine runnertools/checker/lotus-check-symex.cpp– Symbolic-execution engine runnertools/checker/lotus-check-taint.cpp– Taint-analysis engine runner
Usage
KINT Tool:
./build/bin/lotus-check --engine=kint input.bc --checks=all
./build/bin/lotus-check --engine=kint input.bc --checks=int-overflow,div-by-zero
./build/bin/lotus-check --engine=kint input.bc --report-json=report.json
Concurrency Tool:
./build/bin/lotus-check --engine=concur input.bc --checks=data-race
./build/bin/lotus-check --engine=concur input.bc --checks=deadlock,atomicity
./build/bin/lotus-check --engine=concur input.bc --checks=openmp,mpi
./build/bin/lotus-check --engine=concur input.bc --report-json=report.json
Pulse Tool:
./build/bin/lotus-check --engine=pulse input.bc
./build/bin/lotus-check --engine=pulse input.bc --verbose
./build/bin/lotus-check --engine=pulse input.bc --log-level=debug
Programmatic Usage
All checkers integrate with the centralized BugReportMgr:
#include "Checker/Framework/BugReportMgr.h"
// Access centralized reports emitted by checker frontends
BugReportMgr& mgr = BugReportMgr::get_instance();
mgr.print_summary(outs());
mgr.generate_json_report(jsonFile, BugReportMgr::ReportFilter{});
Bug Types
The framework detects the following bug categories:
Memory Safety: Null pointer dereference, use-after-free, uninitialized reads, invalid accesses, memory leaks
Numerical Errors: Integer overflow, division by zero, bad shift, array out-of-bounds, dead branches
Concurrency: Data races, deadlocks, atomicity violations, lock mismatches, condition-variable misuse, OpenMP runtime misuse, MPI protocol/RMA bugs
Security: Taint errors (untrusted data flows), use-after-free, null dereferences
Performance: Unnecessary copies, const-refable parameters
All bug types are classified by importance (LOW, MEDIUM, HIGH) and category (SECURITY, ERROR, WARNING, PERFORMANCE) with CWE mappings.
Integration Points
UnderApproxAA: Used by PulseChecker for must-alias canonicalization
Z3 SMT Solver: Used by KINT for path-sensitive verification
Biabductive Analysis: Used by PulseChecker for precise bug detection
LLVM Pass Infrastructure: Standard pass registration for integration