Static Analysis Tools â Infer, Clang SA and SonarQube Guide
In this tutorial, you'll learn about Static Analysis Tools. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Static analysis tools examine source code without executing it, finding bugs, security vulnerabilities, and code quality issues at compile time using techniques from Formal Verification and Abstract Interpretation.
Learning Path
flowchart LR A["Abstract Interpretation"] --> B["Static Analysis Tools
Infer, Clang SA"] B --> C["SAT Solvers"] B --> D["Industrial Formal Verification"] style B fill:#f90,color:#fff,stroke-width:2px
What you'll learn: How to use Infer, Clang Static Analyzer, and SonarQube for automated code review, their underlying analysis engines, and how to integrate them into CI pipelines.
Why it matters: Static analysis catches defects early in development when they are cheapest to fix. {{< ilink="Rust" "Rust's" >}} borrow checker is a static analyzer that prevents memory bugs at compile time.
Real-world use: Doda Browser runs Infer on every pull request to detect null pointer dereferences, resource leaks, and concurrency bugs before they reach production.
Prerequisites
Basic compiler concepts and familiarity with C, Java, or Python.
What Are Static Analysis Tools?
Static analysis tools inspect program code without executing it. They detect potential defects by building an abstract model of the program and checking it against a set of rules. The techniques range from simple pattern matching to full-blown Abstract Interpretation and Symbolic Execution.
Production static analyzers fall into three categories: linters (surface-level), bug finders (deep inter-procedural analysis), and sound analyzers (mathematically guarantee the absence of certain bugs).
Step-by-Step: Using Infer
Infer is Facebook's static analyzer that uses separation logic to reason about heap-allocated data.
Step 1: Install Infer
# Install Infer on Ubuntu
curl -sSL https://github.com/facebook/infer/releases/download/v1.2.0/infer-linux-x86_64-v1.2.0.tar.xz | tar xJ
export PATH=$PATH:$(pwd)/infer-linux-x86_64-v1.2.0/bin
Step 2: Run Infer on a C File
// example.c
#include <stdlib.h>
void leak_example() {
int *p = (int *)malloc(sizeof(int));
// p is never freed - Infer catches this
*p = 42;
}
void null_deref(int *p) {
if (p != NULL) {
*p = 10;
}
// safe: null check present
}
infer -- clang -c example.c
Expected output:
example.c:5: error: Resource Leak
memory allocated by malloc() is not deallocated.
3. void leak_example() {
4. int *p = (int *)malloc(sizeof(int));
5. *p = 42;
^
6. }
Step 3: Run Infer on a Java Project
cd my-java-project
infer -- javac src/*.java
Step-by-Step: Using Clang Static Analyzer
Step 1: Run scan-build
scan-build clang -c program.c
Step 2: Trigger a Bug
#include <stdlib.h>
void use_after_free() {
int *p = (int *)malloc(sizeof(int));
*p = 1;
free(p);
*p = 2; // Use-after-free bug
}
scan-build clang -c use_after_free.c
Expected output:
use_after_free.c:7:5: warning: Use of memory after it is freed
*p = 2;
^ ~
1 warning generated.
Step 3: Programmatic Analysis with clang-tidy
import subprocess
def run_clang_tidy(filepath):
result = subprocess.run(
["clang-tidy", filepath, "--", "-std=c11"],
capture_output=True, text=True
)
return result.stdout
warnings = run_clang_tidy("buggy.c")
print(f"Found {len(warnings.split(chr(10)))} warnings")
Expected output:
Found 3 warnings
Step-by-Step: Building a Simple Analyzer
Step 1: Define Rules
class SimpleAnalyzer:
def __init__(self):
self.rules = []
def add_rule(self, name, check_fn):
self.rules.append((name, check_fn))
def analyze(self, source_lines):
findings = []
for name, check in self.rules:
result = check(source_lines)
if result:
findings.append((name, result))
return findings
def check_malloc_without_free(source):
malloc_lines = []
free_lines = []
for i, line in enumerate(source):
if "malloc" in line:
malloc_lines.append(i)
if "free" in line:
free_lines.append(i)
issues = []
for ml in malloc_lines:
if not any(abs(ml - fl) < 20 for fl in free_lines):
issues.append(f"Line {ml+1}: malloc without matching free")
return issues
source = [
"void f() {",
" int *p = malloc(4);",
" *p = 42;",
"}]
]
analyzer = SimpleAnalyzer()
analyzer.add_rule("MallocWithoutFree", check_malloc_without_free)
print(analyzer.analyze(source))
Expected output:
[('MallocWithoutFree', ['Line 2: malloc without matching free'])]
Common Errors
1. False Positives Overwhelm Developers
Too many false positives cause alert fatigue. Tune analyzer sensitivity and suppress known false patterns with inline annotations.
2. Not Running in CI
Running analyzers only locally means bugs ship to production. Integrate Infer or Clang SA into CI with blocking on critical findings.
3. Ignoring Inter-Procedural Analysis
Single-function analysis misses bugs that cross function boundaries. Infer and Clang SA perform inter-procedural analysis by default.
4. Over-reliance on Default Configurations
Default configurations detect common patterns but may miss project-specific issues. Customize rules for your codebase's conventions.
5. Confusing Linting with Deep Static Analysis
Linters check surface-level style and simple patterns. Deep analyzers like Infer and Clang SA find complex bugs using Abstract Interpretation and Symbolic Execution.
Practice Questions
Q1: What is the difference between a linter and a static analyzer?
A linter checks style and surface-level patterns. A static analyzer performs deep inter-procedural analysis using formal methods like Abstract Interpretation.
Q2: How does Infer find resource leaks?
Infer uses separation logic to track ownership of heap-allocated memory. If a pointer goes out of scope without being freed or transferred, Infer reports a leak.
Q3: What types of bugs does Clang SA detect?
Use-after-free, memory leaks, null pointer dereferences, division by zero, and dead stores.
Q4: How do static analyzers handle function pointers?
They use pointer analysis to compute a superset of possible targets, then analyze each target's effects on the abstract state.
Q5: Can static analysis replace testing?
No. Static analysis finds different classes of bugs than testing. Testing finds logic errors in concrete executions; static analysis finds potential defects in all executions.
Challenge
Write a Python-based static analyzer that detects unreachable code after a return statement inside a function. Handle conditionals and loops. Test it on a sample C function with multiple return paths.
FAQ
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro