HomeAbout UsContact Us

Static Analysis Tools for Embedded C: Cppcheck, MISRA, and Clang Static Analyzer

By Jithin Tom
July 11, 2026
3 min read
Static Analysis Tools for Embedded C: Cppcheck, MISRA, and Clang Static Analyzer

Table Of Contents

01
Why Static Analysis Matters in Embedded C
02
Tool Comparison at a Glance
03
Cppcheck: Fast, Pragmatic, CI-Friendly
04
Clang Static Analyzer: Path-Sensitive Symbolic Execution
05
MISRA C:2012 Compliance for Safety-Critical Code
06
Tiered CI Pipeline Architecture
07
Interpreting and Triaging Results
08
ASCII Art: Tiered Pipeline Data Flow
09
Summary
10
Related Reading
11
References
12
Frequently Asked Questions

Static analysis catches undefined behavior, resource leaks, and coding-standard violations before they reach hardware. For embedded C, three tools dominate: Cppcheck for fast pattern-based checks, Clang Static Analyzer for deep path-sensitive bug hunting, and MISRA checkers for compliance-critical codebases. This article walks through integrating all three into a CI pipeline, interpreting their output, and suppressing false positives without weakening the safety net.

Why Static Analysis Matters in Embedded C

Embedded C runs close to hardware with no memory protection, no exceptions, and no garbage collector. A single buffer overrun, use-after-free, or signed-integer overflow can brick a device or open a safety hazard. Compilers catch syntax errors and some UB at -Wall -Wextra -Wpedantic, but they do not model data flow across function boundaries, track pointer lifetimes, or enforce coding standards like MISRA C:2012.

Static analysis fills this gap. It operates on the AST or IR without executing code, scaling to millions of lines and catching classes of bugs that testing misses: dead code, unreachable paths, violated preconditions, and rule violations that correlate with field failures.

Tool Comparison at a Glance

ToolAnalysis TypeSpeedFalse Positive RateMISRA CoverageBest For
CppcheckPattern/ASTFastLow (warning/style)Partial (addon)CI gate, style, quick wins
Clang Static AnalyzerPath-sensitive symbolicSlowMediumNoneDeep bugs, memory safety
Commercial MISRA (LDRA, Parasoft)Full complianceSlowLow (qualified)Complete (decidable + undecidable)Certification evidence

Use all three in a tiered pipeline: Cppcheck on every PR, Clang SA nightly, MISRA tool on release branches.

Cppcheck: Fast, Pragmatic, CI-Friendly

Cppcheck excels at finding null-pointer derefs, buffer overflows, uninitialized variables, and style violations with minimal setup. It parses C/C++ directly (no compile commands needed) and supports --enable=all plus addons.

Essential Flags for Embedded CI

cppcheck --enable=warning,performance,portability,style \
--std=c11 \
--platform=stm32f4 \
--inline-suppr \
--suppress=missingIncludeSystem \
--suppress=unusedFunction \
--force \
--xml \
--output-file=cppcheck-report.xml \
src/
  • --platform=stm32f4 (or your MCU) defines platform-specific macros and sizes.
  • --inline-suppr allows // cppcheck-suppress comments in source.
  • --suppress=missingIncludeSystem silences noise from vendor HAL headers you cannot fix.
  • --xml produces machine-readable output for CI parsers (GitLab SAST, GitHub Code Scanning).

MISRA Addon

Cppcheck’s MISRA addon checks a subset of decidable rules:

cppcheck --addon=misra.py --enable=warning --xml src/ > misra-cppcheck.xml

Output includes rule IDs (e.g., MISRA2012-Rule-10.1). Rules requiring manual review (undecidable) are reported as information severity — they do not fail the build but flag for human audit.

Suppressing False Positives Inline

uint32_t crc32_calculate(const uint8_t *data, size_t len)
{
uint32_t crc = 0xFFFFFFFFu;
// cppcheck-suppress knownConditionTrueFalse ; len validated by caller
if (len == 0) {
return ~crc;
}
// ...
}

Document the justification in the comment. Audit suppressions quarterly.

Clang Static Analyzer: Path-Sensitive Symbolic Execution

Clang SA models execution paths, tracking symbolic values through branches, loops, and function calls. It finds bugs Cppcheck misses: complex null-deref chains, use-after-free across modules, division-by-zero on computed divisors, and lock-order inversions.

Running via scan-build

scan-build -o clang-sa-report \
-enable-checker core,unix,cplusplus,security \
-analyzer-config display-progress=true \
make clean all
  • -enable-checker selects checker groups. For embedded, prioritize core (null, div-zero, deadcode), unix (POSIX API misuse), and security (taint, format-string).
  • HTML report (clang-sa-report/) shows exploded path graphs — click through to see the exact path to the bug.

Interpreting Path-Sensitive Reports

A typical report traces:

  1. Entry — function entry with symbolic arguments.
  2. Branch — a condition constrains a symbol (e.g., ptr != NULL).
  3. Dereference — later, *ptr is accessed on a path where the constraint was not met.
  4. Sink — the bug manifests.

If the path is infeasible (dead code), mark it with __attribute__((analyzer_noreturn)) on the aborting function or add an assert() that Clang SA respects.

Integrating with CMake

find_program(CLANG_SA scan-build)
if (CLANG_SA)
add_custom_target(clang-static-analysis
COMMAND ${CLANG_SA} -o ${CMAKE_BINARY_DIR}/clang-sa
--use-cc=${CMAKE_C_COMPILER}
--use-c++=${CMAKE_CXX_COMPILER}
${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}
COMMENT "Running Clang Static Analyzer"
VERBATIM
)
endif()

Run via make clang-static-analysis or cmake --build build --target clang-static-analysis in nightly CI.

MISRA C:2012 Compliance for Safety-Critical Code

MISRA C:2012 defines 159 rules (16 directives, 143 rules) across categories: required, mandatory, advisory. Required rules must be met (no violations). Mandatory violations need formal deviation. Advisory violations need justification.

Tool Qualification

For ISO 26262 ASIL-D or IEC 61508 SIL-3, the static analysis tool itself must be qualified (TQL-1 or equivalent). LDRA, Parasoft C/C++test, and QA-C have qualification kits. Cppcheck and Clang SA are not qualified — they supplement, not replace, the qualified tool.

Typical Compliance Workflow

1. Run qualified MISRA tool on full codebase (release branch)
2. Categorize violations:
- Required: zero tolerance → fix or redesign
- Mandatory: file deviation with risk assessment
- Advisory: fix or justify in coding standard appendix
3. Generate compliance matrix (rule → file:line → status)
4. Archive reports for audit

Common MISRA Rules That Bite Embedded Developers

RuleTitleTypical ViolationFix Pattern
Dir 4.6typedef for integer typesuint32_t used directlytypedef uint32_t u32_t; in std_types.h
Rule 10.1No implicit integer conversionuint8_t a = 255 + 1;uint8_t a = (uint8_t)(255u + 1u);
Rule 11.6No cast to/from pointer to integer(uintptr_t)ptrUse uintptr_t from <stdint.h>, document necessity
Rule 17.2No recursionRecursive DFSRewrite iterative with explicit stack
Rule 21.3No dynamic memorymalloc/freeStatic pools, memory pools, stack allocation

Tiered CI Pipeline Architecture

┌─────────────────────────────────────────────────────────────┐
│ CI Pipeline Stages │
├─────────────────┬─────────────────────┬─────────────────────┤
│ Pull Request │ Nightly Build │ Release Branch │
├─────────────────┼─────────────────────┼─────────────────────┤
│ cppcheck │ clang-static-analyzer│ qualified MISRA │
│ (fast, warn) │ (deep, path-sens) │ (full compliance) │
│ │ │ │
│ fail on: │ fail on: │ fail on: │
│ - warning │ - high-confidence │ - any required │
│ - style │ bug reports │ - any mandatory │
│ │ │ without deviation│
└─────────────────┴─────────────────────┴─────────────────────┘

GitHub Actions Example

name: Static Analysis
on:
pull_request:
schedule: [cron: '0 2 * * *'] # nightly
workflow_dispatch:
jobs:
cppcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install cppcheck
run: sudo apt-get update && sudo apt-get install -y cppcheck
- name: Run cppcheck
run: |
cppcheck --enable=warning,performance,portability,style \
--std=c11 --platform=stm32f4 --inline-suppr \
--suppress=missingIncludeSystem --force \
--xml --output-file=cppcheck.xml src/
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: cppcheck.xml
category: cppcheck
clang-sa:
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install clang
run: sudo apt-get update && sudo apt-get install -y clang
- name: Run scan-build
run: |
scan-build -o clang-sa-report \
-enable-checker core,unix,security \
make clean all
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: clang-static-analysis
path: clang-sa-report/
misra:
if: github.ref == 'refs/heads/release/*' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run qualified MISRA tool (example: LDRA)
run: |
# Requires licensed tool in runner image
ldra_tbcl -config misra2012.tbcl -run
- name: Upload compliance matrix
uses: actions/upload-artifact@v4
with:
name: misra-compliance
path: misra-report/

Interpreting and Triaging Results

Cppcheck Severity Mapping

Cppcheck SeverityCI Action
errorFail build — memory safety, UB
warningFail build — likely bug
styleWarn only — maintainability
performanceWarn only — optimization hint
portabilityWarn only — cross-platform risk
information (MISRA)Manual review queue

Clang SA Bug Classes Worth Fixing Immediately

  1. Null pointer dereferencecore.NullDereference
  2. Division by zerocore.DivideZero
  3. Use after free / use after scopeunix.Malloc, core.StackAddressEscape
  4. Buffer overflow (tainted)security.FloatLoopCounter, security.insecureAPI.strcpy

Lower priority: dead stores (core.DeadStores), unused return values (core.unusedReturnValue) — often intentional in embedded.

ASCII Art: Tiered Pipeline Data Flow

+-------------------------------------------------------------------+
| STATIC ANALYSIS PIPELINE |
+-------------------------------------------------------------------+
| |
| PULL REQUEST |
| | |
| v |
| +----------------+ |
| | CPPCHECK | --fast, pattern-based--> SARIF (GitHub UI) |
| | (warning, | |
| | style, MISRA)| |
| +----------------+ |
| | |
| | FAIL on error/warning |
| v |
| MERGE |
| |
| NIGHTLY (schedule) |
| | |
| v |
| +----------------+ |
| | CLANG SA | --path-sensitive, deep--> HTML Report |
| | (core, unix, | symbolic execution (artifact) |
| | security) | |
| +----------------+ |
| | |
| | FAIL on high-confidence bugs |
| v |
| |
| RELEASE BRANCH |
| | |
| v |
| +----------------+ |
| | QUALIFIED MISRA| --full compliance--> Compliance Matrix |
| | TOOL (LDRA/ | 159 rules (audit artifact) |
| | Parasoft) | |
| +----------------+ |
| | |
| | FAIL on required/mandatory w/o deviation |
| v |
| DEPLOY |
| |
+-------------------------------------------------------------------+

Summary

Static analysis is not a silver bullet — it produces false positives, misses concurrency bugs, and cannot verify runtime behavior. But a tiered pipeline (Cppcheck on PRs, Clang SA nightly, qualified MISRA on releases) catches entire classes of defects before they hit hardware. The key is tuning suppressions, triaging reports daily, and treating the compliance matrix as a living artifact, not a one-time audit checkbox.

References

  1. MISRA C:2012 Guidelines, 3rd Edition, 1st Revision (2020), MIRA Ltd.
  2. CERT C Coding Standard, 2016 Edition, SEI/CERT, Carnegie Mellon University.
  3. Clang Static Analyzer Documentation, LLVM Project, https://clang-analyzer.llvm.org/
  4. Cppcheck Manual, http://cppcheck.sourceforge.net/manual.pdf
  5. ISO 26262-6:2018, Road vehicles — Functional safety — Part 6: Product development at the software level.
  6. LDRA Tool Suite Qualification Kit for ISO 26262, LDRA Ltd., https://www.ldra.com/iso-26262-qualification/

Frequently Asked Questions

What is the difference between MISRA C and CERT C coding standards?

MISRA C is a proprietary standard focused on C/C++ for safety-critical embedded systems with strict rules (e.g., no dynamic memory, no recursion). CERT C is an open standard from SEI/CERT targeting security vulnerabilities across all C applications. MISRA is mandatory in automotive/avionics; CERT is advisory for security hardening.

Can Cppcheck replace MISRA compliance checking?

Cppcheck covers a subset of MISRA rules (mostly decidable, statically checkable ones) via its `--addon=misra.py` addon. It cannot prove compliance for undecidable rules requiring manual review or runtime checks. Full MISRA compliance requires a qualified tool (e.g., LDRA, Parasoft) and manual evidence for undecidable rules.

How does Clang Static Analyzer differ from Cppcheck?

Clang Static Analyzer uses path-sensitive symbolic execution on the Clang AST, modeling execution paths to find deep bugs (null derefs, use-after-free, division by zero). Cppcheck uses pattern-based AST matching with limited path sensitivity, trading depth for speed and fewer false positives on simpler checks.

Should static analysis run on every CI build or only on release branches?

Run fast, low-false-positive tools (Cppcheck `--enable=warning,style`) on every PR/commit. Run expensive, path-sensitive analyses (Clang Static Analyzer, MISRA full suite) on nightly or release branches to avoid slowing CI feedback loops. Fail the build only on high-confidence findings.

How do you suppress false positives without disabling the check entirely?

Use inline suppressions: `// cppcheck-suppress [check-id]` for Cppcheck, `// NOLINTNEXTLINE(check-name)` for clang-tidy/Clang SA, or `/*lint -e{msg_id} */` for PC-lint. Document the justification in a comment. Avoid blanket suppressions; prefer narrowing scope to the specific line/function.

Tags

embedded-cstatic-analysiscppcheckmisraclangcode-quality

Share


Previous Article
Rate Monotonic Priority Ceiling Protocol Fixes Priority Inversion
Jithin Tom

Jithin Tom

A Closer Look at C/C++, RTOS, and Embedded Systems

Related Posts

Atomic Operations in Embedded C: Lock-Free Synchronization for Cortex-M
Atomic Operations in Embedded C: Lock-Free Synchronization for Cortex-M
July 03, 2026
2 min
© 2026, All Rights Reserved.
Powered By Netlyft

Quick Links

Advertise with usAbout UsContact Us

Social Media