MISRA-C++ Rules & Rationale
"MISRA-C++ 2023 mandatory and advisory rules for safety-critical C++: what they prohibit, why, and how to enforce them with clang-tidy and cppcheck."
TL;DR
MISRA-C++ is a coding standard for safety-critical C++ (automotive, aerospace, medical). MISRA-C++ 2023 covers C++17. Rules are mandatory (must follow), required (must follow unless formally deviated), or advisory (should follow). Enforce with clang-tidy's readability-* and cppcoreguidelines-* checks, or commercial tools like PC-lint, CodeSonar, or Klocwork.
Why MISRA exists
Certain C++ features, while valid, are too prone to misuse in safety-critical software:
- Dynamic memory (
new/delete) — heap fragmentation, non-deterministic timing - Exceptions — non-deterministic control flow, stack unwinding overhead
- RTTI (
dynamic_cast,typeid) — runtime overhead, code size - Recursion — unknown stack depth
- Undefined behavior — implementation-defined behavior varies across compilers
MISRA replaces "it works on our compiler" with deterministic, verifiable behavior.
Key MISRA-C++ 2023 rules
Memory management
// MISRA Rule 21.6.1: No dynamic heap allocation on safety path
// BAD
void* buf = malloc(size); // banned
std::string s = "hello"; // heap allocation
std::vector<int> v = {1, 2, 3}; // heap allocation
// OK — all memory allocated at startup, never freed
static std::array<uint8_t, 4096> rx_buffer;
static MyQueue<Event, 64> event_queue;
// Pattern: allocate all memory at init, use pool allocators
class SystemAllocator {
static std::array<std::byte, 64 * 1024> pool_;
static size_t offset_;
public:
static void* alloc(size_t n) {
// Only called during init phase
void* p = pool_.data() + offset_;
offset_ += (n + 7) & ~7u;
return p;
}
// No free() — static lifetime
};Exceptions
// MISRA Rule 15.0.1: Exception handling shall not be used
// Compile with: -fno-exceptions
// BAD
try {
riskyOperation();
} catch (const std::exception& e) {
// banned
}
// MISRA pattern — return-code based error handling
enum class Status { OK, ERROR_TIMEOUT, ERROR_INVALID };
Status readSensor(SensorId id, int32_t& out_value) {
if (!validateId(id)) return Status::ERROR_INVALID;
if (!waitReady(id)) return Status::ERROR_TIMEOUT;
out_value = readRegister(id);
return Status::OK;
}
// C++23: std::expected as MISRA-friendly error handling
#include <expected>
std::expected<int32_t, Status> readSensor(SensorId id);Dynamic dispatch
// MISRA Rule 13.1.1: virtual functions allowed but with restrictions
// Virtual destructors required for polymorphic base classes
// MISRA-compliant virtual hierarchy
class Sensor {
public:
virtual ~Sensor() = default;
virtual Status read(int32_t& val) = 0; // pure virtual allowed
protected:
// No virtual in constructors/destructors of derived
};
// MISRA 13.1.3: dynamic_cast shall not be used
// BAD
Derived* d = dynamic_cast<Derived*>(base_ptr);
// OK — use visitor pattern or type tag instead
enum class SensorType { TEMPERATURE, PRESSURE };
class Sensor {
public:
virtual SensorType type() const = 0;
};Recursion
// MISRA Rule 17.2: No recursion (unbounded stack depth)
// BAD
int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1); // banned
}
// OK — iterative version
int factorial(int n) {
int result = 1;
for (int i = 2; i <= n; ++i)
result *= i;
return result;
}
// For tree traversal — use explicit stack with bounded depth
void traverseTree(Node* root) {
static std::array<Node*, MAX_DEPTH> stack;
int top = 0;
stack[top++] = root;
while (top > 0) {
Node* node = stack[--top];
process(node);
if (node->right && top < MAX_DEPTH) stack[top++] = node->right;
if (node->left && top < MAX_DEPTH) stack[top++] = node->left;
}
}Type safety
// MISRA Rule 10.1: Implicit conversions that lose information are prohibited
// BAD
uint32_t u = 0xFFFFFFFF;
int32_t s = u; // narrowing — banned
float f = 1234567890; // precision loss — banned
// OK — explicit casts when you know what you're doing
int32_t s = static_cast<int32_t>(u & 0x7FFFFFFF);
// MISRA Rule 5.0.1: Use typedef for fixed-width types
// BAD: int, long, char are implementation-defined width
int counter = 0;
// OK
#include <cstdint>
int32_t counter = 0;
uint8_t byte_val = 0;
uint16_t reg_val = 0;Initialization
// MISRA Rule 9.1: All variables must be initialized before use
// BAD
int32_t result;
if (condition) result = compute();
use(result); // UB if !condition
// OK
int32_t result = 0;
if (condition) result = compute();
use(result);
// Or better — structured initialization
int32_t result = condition ? compute() : 0;Enforcement tools
clang-tidy MISRA-adjacent checks
# Relevant clang-tidy checks (not full MISRA, but covers many rules)
clang-tidy src/*.cpp -- \
-checks=\
cppcoreguidelines-*,\
readability-*,\
bugprone-*,\
performance-*,\
modernize-*
# Specific checks matching MISRA rules
# No dynamic memory: cppcoreguidelines-no-malloc
# Initialize variables: cppcoreguidelines-init-variables
# Fixed-width types: cppcoreguidelines-avoid-magic-numberscppcheck
# MISRA C 2012 check (use as proxy for C++ patterns)
cppcheck --addon=misra --suppressions-list=misra_suppressions.txt src/
# MISRA C++ 2008 (older standard, still useful)
cppcheck --enable=all --template='{file}:{line}: {id}: {message}' src/Commercial tools
For DO-178C, ISO 26262, or IEC 61508 compliance, you typically need:
- PC-lint Plus (Gimpel) — traditional MISRA enforcement
- Polyspace Bug Finder (MathWorks) — formal verification + MISRA
- Klocwork — MISRA + security
- Axivion Bauhaus Suite — architecture + MISRA
MISRA deviation process
When a rule genuinely cannot be followed (e.g., using new in a memory-managed init phase), document a formal deviation:
// MISRA-C++ 2023 Rule 21.6.1 Deviation
// Deviation Ref: DEV-2024-001
// Justification: Dynamic allocation permitted during system initialization
// phase only (before scheduler start). All allocations use
// the SystemAllocator pool with bounded size.
// Reviewed by: Safety Manager (2024-01-15)
// Risk assessment: LOW — bounded allocation, no runtime deallocation
void* buf = SystemAllocator::alloc(sizeof(ControlBlock)); // NOLINTDeviations must be reviewed and tracked in a deviation register.
MISRA vs AUTOSAR C++
AUTOSAR Adaptive Platform C++ Coding Guidelines build on MISRA-C++ 2008 but:
- Allow C++14 features
- Have additional rules for functional safety
- Are required for AUTOSAR Adaptive implementations
- Published as a separate document (AUTOSAR AP Guidelines for C++)
For new automotive projects, check which standard your OEM requires.