3.4: Static Analysis Integration
Overview
Static analysis tools examine source code without executing it, detecting bugs, security vulnerabilities, code smells, and compliance violations. For safety-critical embedded systems governed by ASPICE, ISO 26262, and IEC 61508, static analysis is not optional—it is a core verification activity required for certification.
This chapter covers tool selection, integration strategies, and workflows that enable continuous quality monitoring and "shift-left" defect prevention.
Key Terms
| Term | Definition |
|---|---|
| Static Analysis | Automated examination of source code without execution to find defects |
| MISRA | Motor Industry Software Reliability Association coding guidelines |
| SAST | Static Application Security Testing—security-focused static analysis |
| Quality Gate | CI/CD checkpoint that fails builds if quality thresholds are not met |
| False Positive | Analysis finding that is not a real defect |
| Taint Analysis | Tracking untrusted data through code to find injection vulnerabilities |
Tool Comparison
| Tool | Type | MISRA Support | ISO 26262 Qualified | Cost | Best For |
|---|---|---|---|---|---|
| Coverity | Commercial | Full MISRA C/C++ | Yes (TÜV SÜD) | $$$ | Automotive, aerospace |
| Polyspace | Commercial | Full MISRA | Yes | $$$ | Safety-critical, formal methods |
| SonarQube | Open-source | Plugin-based | No | Free-$$$ | Enterprise quality gates |
| Cppcheck | Open-source | Good coverage | No | Free | Lightweight IDE integration |
| Clang-Tidy | Open-source | Partial | No | Free | LLVM ecosystem |
| PC-lint Plus | Commercial | Full MISRA | No | $$ | Legacy codebases |
| Parasoft C/C++test | Commercial | Full MISRA | Yes | $$$ | DO-178C, automotive |
SonarQube Integration
SonarQube provides a centralized quality platform with dashboards, trend analysis, and quality gates. For embedded C/C++ projects, combine SonarQube with specialized analyzers.
Installation
# Docker deployment
docker run -d --name sonarqube \
-p 9000:9000 \
-v sonarqube_data:/opt/sonarqube/data \
-v sonarqube_logs:/opt/sonarqube/logs \
sonarqube:lts-community
# Wait for startup, then access http://localhost:9000
# Default credentials: admin/admin
Project Configuration
Create sonar-project.properties in your project root:
# Project identification
sonar.projectKey=door-lock-controller
sonar.projectName=Door Lock Controller ECU
sonar.projectVersion=1.0.0
# Source configuration
sonar.sources=src
sonar.tests=test
sonar.sourceEncoding=UTF-8
# C/C++ configuration (requires SonarQube Developer Edition or cxx-community plugin)
sonar.cxx.file.suffixes=.c,.h,.cpp,.hpp
sonar.cxx.includeDirectories=include,drivers/inc,rte/inc
# External analyzer reports
sonar.cxx.cppcheck.reportPaths=build/cppcheck-report.xml
sonar.cxx.clangsa.reportPaths=build/clang-sa-report.plist
sonar.cxx.coverage.reportPaths=build/coverage.xml
sonar.cxx.xunit.reportPaths=build/test-results.xml
# Quality gate
sonar.qualitygate.wait=true
CI/CD Pipeline Integration
# GitLab CI example
stages:
- analyze
- quality-gate
static-analysis:
stage: analyze
image: gcc:12
script:
# Run Cppcheck
- cppcheck --enable=all --xml --xml-version=2 src/ 2> build/cppcheck-report.xml
# Run Clang Static Analyzer
- scan-build -o build/clang-sa --plist-html make clean all
# Convert plist to SonarQube format
- python3 scripts/plist_to_sonar.py build/clang-sa/*.plist > build/clang-sa-report.plist
artifacts:
paths:
- build/*.xml
- build/*.plist
sonarqube-scan:
stage: quality-gate
image: sonarsource/sonar-scanner-cli:latest
dependencies:
- static-analysis
script:
- sonar-scanner
-Dsonar.host.url=$SONAR_HOST_URL
-Dsonar.login=$SONAR_TOKEN
allow_failure: false
Coverity Integration
Coverity is the industry standard for safety-critical static analysis, with TÜV SÜD qualification for ISO 26262 ASIL D and IEC 61508 SIL 4.
Build Capture
Coverity intercepts your build to understand code structure:
# Capture the build
cov-build --dir cov-int make clean all
# Analyze captured data
cov-analyze --dir cov-int \
--all \
--enable-constraint-fpp \
--enable-fnptr \
--enable-virtual \
--security \
--concurrency \
--aggressiveness-level high
# Generate reports
cov-format-errors --dir cov-int --html-output cov-html
cov-format-errors --dir cov-int --json-output-v7 cov-results.json
MISRA Compliance Configuration
# coverity-config.yaml
analysis:
checkers:
misra:
enabled: true
standard: MISRA-C-2012
rules:
mandatory: error
required: error
advisory: warning
security:
enabled: true
checkers:
- BUFFER_SIZE
- OVERRUN
- NEGATIVE_RETURNS
- TAINTED_SCALAR
- SQL_INJECTION
- COMMAND_INJECTION
concurrency:
enabled: true
checkers:
- RACE_CONDITION
- DEADLOCK
- LOCK_EVASION
exclusions:
paths:
- third_party/**
- generated/**
deviation_management:
require_comment: true
require_approval: true
Coverity Connect CI Integration
# GitHub Actions example
coverity-scan:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Coverity Build Capture
run: |
cov-build --dir $COV_DIR make clean all
- name: Coverity Analysis
run: |
cov-analyze --dir $COV_DIR \
--misra-config misra-2012.config \
--security \
--all
- name: Commit to Coverity Connect
run: |
cov-commit-defects --dir $COV_DIR \
--host $COV_HOST \
--https-port 8443 \
--auth-key-file $COV_AUTH_KEY \
--stream door-lock-controller \
--version $GITHUB_SHA
- name: Check for New Defects
run: |
# Fail if new high/medium defects introduced
cov-manage-im --host $COV_HOST \
--auth-key-file $COV_AUTH_KEY \
--mode defects \
--show \
--stream door-lock-controller \
--status New \
--impact High,Medium \
--exit-status-on-defect 1
Cppcheck for Lightweight Analysis
Cppcheck is ideal for fast feedback during development—run it in your IDE and as a pre-commit hook.
IDE Integration (VS Code)
// .vscode/settings.json
{
"cppcheck.enable": true,
"cppcheck.cppcheckPath": "/usr/bin/cppcheck",
"cppcheck.includePaths": [
"${workspaceFolder}/include",
"${workspaceFolder}/drivers/inc"
],
"cppcheck.defines": [
"STM32F4xx",
"USE_HAL_DRIVER"
],
"cppcheck.suppressions": [
"missingIncludeSystem",
"unmatchedSuppression"
],
"cppcheck.standard": ["c11", "c++17"],
"cppcheck.enabledChecks": ["all"],
"cppcheck.misraAddon": true
}
Pre-Commit Hook
#!/bin/bash
# .git/hooks/pre-commit
echo "Running static analysis..."
# Get list of staged C/C++ files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(c|cpp|h|hpp)$')
if [ -n "$STAGED_FILES" ]; then
# Run cppcheck on staged files
cppcheck --enable=all \
--error-exitcode=1 \
--inline-suppr \
--suppress=missingIncludeSystem \
$STAGED_FILES
if [ $? -ne 0 ]; then
echo "Static analysis found issues. Please fix before committing."
exit 1
fi
fi
echo "Static analysis passed."
exit 0
MISRA Checking with Cppcheck
# Enable MISRA addon
cppcheck --addon=misra.py \
--addon-python=/usr/bin/python3 \
--misra-severity=warning \
--suppress=misra-config \
--xml --xml-version=2 \
src/ 2> misra-report.xml
Clang Static Analyzer
Part of the LLVM toolchain, Clang Static Analyzer provides deep path-sensitive analysis.
Scan-Build Wrapper
# Analyze during build
scan-build -v -V \
-enable-checker alpha.security.ArrayBoundV2 \
-enable-checker alpha.core.PointerArithm \
-enable-checker alpha.deadcode.UnreachableCode \
-enable-checker security.insecureAPI.strcpy \
-o scan-results \
make clean all
# View HTML report
open scan-results/*/index.html
Clang-Tidy Integration
# .clang-tidy
---
Checks: >
*,
-fuchsia-*,
-google-runtime-references,
-llvm-header-guard,
-modernize-use-trailing-return-type,
cert-*,
bugprone-*,
performance-*,
readability-*,
misc-*,
cppcoreguidelines-*
WarningsAsErrors: >
bugprone-*,
cert-*
HeaderFilterRegex: '.*'
CheckOptions:
- key: readability-identifier-naming.VariableCase
value: lower_case
- key: readability-identifier-naming.FunctionCase
value: CamelCase
- key: readability-identifier-naming.MacroDefinitionCase
value: UPPER_CASE
- key: cppcoreguidelines-avoid-magic-numbers.IgnoredIntegerValues
value: '0;1;2;4;8;16;32;64;128;256'
AI-Enhanced Static Analysis
Modern AI tools can augment traditional static analysis by understanding context and reducing false positives.
AI Triage Workflow
#!/usr/bin/env python3
"""
AI-Enhanced Static Analysis Triage
Uses LLM to analyze static analysis findings,
classify severity, and suggest fixes.
"""
import json
import subprocess
from anthropic import Anthropic
def load_findings(report_path: str) -> list:
"""Load static analysis findings from JSON report."""
with open(report_path) as f:
data = json.load(f)
return data.get('issues', [])
def get_code_context(file_path: str, line: int, context_lines: int = 5) -> str:
"""Extract code context around the finding."""
with open(file_path) as f:
lines = f.readlines()
start = max(0, line - context_lines - 1)
end = min(len(lines), line + context_lines)
context = []
for i, l in enumerate(lines[start:end], start=start+1):
marker = ">>>" if i == line else " "
context.append(f"{marker} {i:4d}: {l.rstrip()}")
return "\n".join(context)
def analyze_finding(finding: dict, code_context: str) -> dict:
"""Use AI to analyze a static analysis finding."""
client = Anthropic()
prompt = f"""Analyze this static analysis finding for an embedded C/C++ project:
**Checker**: {finding['checker']}
**Severity**: {finding['severity']}
**Message**: {finding['message']}
**File**: {finding['file']}:{finding['line']}
**Code Context**:
```c
{code_context}
Provide:
- Is this a TRUE POSITIVE or likely FALSE POSITIVE? (with confidence %)
- If true positive, what is the actual risk? (Critical/High/Medium/Low)
- Suggested fix (code snippet if applicable)
- MISRA/CERT rule violated (if any)
Be concise and technical."""
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}]
)
return {
"original": finding,
"ai_analysis": response.content[0].text
}
def main(): findings = load_findings("build/static-analysis.json")
print(f"Analyzing {len(findings)} findings with AI...")
results = []
for finding in findings:
if finding['severity'] in ['error', 'warning']:
context = get_code_context(finding['file'], finding['line'])
analysis = analyze_finding(finding, context)
results.append(analysis)
# Print summary
print(f"\n[{finding['severity'].upper()}] {finding['file']}:{finding['line']}")
print(f" Checker: {finding['checker']}")
print(f" AI Analysis: {analysis['ai_analysis'][:200]}...")
# Save enhanced report
with open("build/ai-enhanced-analysis.json", "w") as f:
json.dump(results, f, indent=2)
if name == "main": main()
---
## Quality Gates
Quality gates prevent defective code from progressing through the pipeline.
### SonarQube Quality Gate Configuration
```json
{
"name": "Embedded Safety Quality Gate",
"conditions": [
{
"metric": "new_reliability_rating",
"op": "GT",
"error": "1",
"description": "No new bugs allowed"
},
{
"metric": "new_security_rating",
"op": "GT",
"error": "1",
"description": "No new vulnerabilities allowed"
},
{
"metric": "new_coverage",
"op": "LT",
"error": "80",
"description": "New code coverage >= 80%"
},
{
"metric": "new_duplicated_lines_density",
"op": "GT",
"error": "3",
"description": "Duplication < 3%"
},
{
"metric": "new_code_smells",
"op": "GT",
"error": "10",
"description": "Max 10 new code smells"
}
]
}
Custom Gate Script
#!/bin/bash
# quality-gate.sh - Custom quality gate for CI/CD
set -e
CRITICAL_MAX=0
HIGH_MAX=5
MISRA_MANDATORY_MAX=0
echo "=== Quality Gate Check ==="
# Parse Coverity results
CRITICAL=$(jq '[.issues[] | select(.impact == "Critical")] | length' cov-results.json)
HIGH=$(jq '[.issues[] | select(.impact == "High")] | length' cov-results.json)
# Parse MISRA results
MISRA_MANDATORY=$(grep -c 'Rule.*mandatory' misra-report.txt || echo 0)
echo "Critical issues: $CRITICAL (max: $CRITICAL_MAX)"
echo "High issues: $HIGH (max: $HIGH_MAX)"
echo "MISRA mandatory violations: $MISRA_MANDATORY (max: $MISRA_MANDATORY_MAX)"
# Check thresholds
if [ "$CRITICAL" -gt "$CRITICAL_MAX" ]; then
echo "FAILED: Too many critical issues"
exit 1
fi
if [ "$HIGH" -gt "$HIGH_MAX" ]; then
echo "FAILED: Too many high-severity issues"
exit 1
fi
if [ "$MISRA_MANDATORY" -gt "$MISRA_MANDATORY_MAX" ]; then
echo "FAILED: MISRA mandatory rule violations"
exit 1
fi
echo "=== Quality Gate PASSED ==="
exit 0
Deviation Management
Safety standards require documented justification for any static analysis finding that is suppressed.
Inline Suppression with Justification
/*
* DEVIATION: MISRA C:2012 Rule 11.3 (Required)
* Justification: Hardware register access requires cast from integer to pointer.
* This is the standard pattern for memory-mapped peripheral access on ARM Cortex-M.
* Risk: None - address is a known valid hardware register.
* Approved by: John Smith, 2025-01-15
* Ticket: SAFETY-1234
*/
/* cppcheck-suppress misra-c2012-11.3 */
volatile uint32_t *GPIO_PORT = (volatile uint32_t *)0x40020000;
Centralized Deviation Database
# deviations.yaml
deviations:
- id: DEV-001
rule: MISRA-C-2012-11.3
scope: drivers/gpio.c
justification: |
Hardware register access requires integer-to-pointer cast.
Standard ARM Cortex-M memory-mapped I/O pattern.
risk_analysis: |
No runtime risk. Address is compile-time constant pointing
to documented hardware register per STM32F4 reference manual.
mitigations:
- All register addresses defined in central header
- Code review required for any new register definitions
approved_by: John Smith
approved_date: 2025-01-15
ticket: SAFETY-1234
status: approved
- id: DEV-002
rule: CERT-EXP36-C
scope: src/can_driver.c:245
justification: |
Pointer to volatile required for DMA buffer alignment.
# ... additional fields
Metrics and Reporting
Track static analysis metrics over time to identify trends and measure improvement.
Key Metrics
| Metric | Target | Why It Matters |
|---|---|---|
| Critical defects | 0 | Safety-critical issues |
| High defects | < 5 per KLOC | Serious bugs |
| MISRA mandatory violations | 0 | Compliance requirement |
| False positive rate | < 20% | Tool credibility |
| Mean time to fix | < 2 days | Team responsiveness |
| Technical debt ratio | < 5% | Long-term maintainability |
Dashboard Query (SonarQube API)
import requests
import pandas as pd
def get_sonar_metrics(project_key: str, host: str, token: str) -> pd.DataFrame:
"""Fetch static analysis metrics from SonarQube."""
metrics = [
'bugs', 'vulnerabilities', 'code_smells',
'coverage', 'duplicated_lines_density',
'reliability_rating', 'security_rating'
]
response = requests.get(
f"{host}/api/measures/component",
params={
'component': project_key,
'metricKeys': ','.join(metrics)
},
auth=(token, '')
)
data = response.json()
measures = {m['metric']: m['value'] for m in data['component']['measures']}
return pd.DataFrame([measures])
Best Practices Summary
- Layer Your Tools: Run fast tools (cppcheck) in IDE, comprehensive tools (Coverity) in CI
- Fail Fast: Configure quality gates to block PRs with critical issues
- Reduce Noise: Tune configurations to minimize false positives—noisy tools get ignored
- Document Deviations: Every suppression needs justification, approval, and ticket reference
- Track Trends: Monitor defect density over time, not just absolute counts
- Integrate AI: Use AI to triage findings and prioritize fixes
- Automate Everything: Manual static analysis never happens consistently
Summary
Static analysis is a cornerstone of safety-critical embedded development. Key integration points:
| Layer | Tool | Purpose |
|---|---|---|
| IDE | Cppcheck, Clang-Tidy | Immediate feedback during coding |
| Pre-commit | Cppcheck | Prevent obvious issues from entering repo |
| CI | Coverity, SonarQube | Comprehensive analysis + quality gates |
| Release | Coverity + MISRA | Certification evidence |
Effective static analysis requires:
- Tool qualification for ISO 26262/IEC 61508 (Coverity, Polyspace)
- MISRA compliance checking for C/C++ embedded code
- Quality gates that block defective code
- Deviation management with audit trail
- Trend analysis to measure improvement