Hello! Welcome to your tenth lesson.
In our previous session, we established the "what" and "why" behind key MISRA-C guidelines for pointer arithmetic and casting. You learned that these rules are not arbitrary but are foundational for building verifiably safe software, a principle that aligns directly with your work in ISO 26262 and ASPICE environments.
Today, we transition from theory to practice. Our learning outcome is to analyze C code to detect violations of critical MISRA-C pointer safety rules. You'll act as a senior code reviewer or a static analysis tool, using your knowledge to systematically hunt for non-compliances in code. We will expand on the rules from last time and introduce new ones related to pointer boundaries, object lifetimes, and more complex type conversions.
1. Pointer Boundaries and Relational Operations
One of the most frequent sources of critical bugs is out-of-bounds memory access. MISRA provides a set of strict rules to ensure pointer arithmetic and comparisons stay within well-defined memory regions.
Rule 18.1: Confining Pointer Arithmetic
While we briefly discussed Rule 18.4 (preferring [] over +), Rule 18.1 is the underlying safety principle: A pointer resulting from arithmetic on a pointer operand shall address an element of the same array as that pointer operand. This includes the address "one-past-the-end," but no further. Dereferencing that one-past-the-end pointer is strictly forbidden and leads to undefined behavior.
The following resource provides an excellent, in-depth analysis of this rule, including how static analysis tools interpret its nuances.
MISRA C:2023 Rule 18.1 - A pointer resulting from ...
This document from MathWorks provides a detailed breakdown of MISRA C:2023 Rule 18.1. It's especially useful for its examples that distinguish between calculating an address and accessing it.
Please read the 'Rule Definition', 'Rationale', and the 'Polyspace Implementation' sections. Pay close attention to the examples regarding structures and multidimensional arrays, as they highlight subtle but important distinctions.
From that reading, consider these key analysis points:
- Calculating vs. Dereferencing: You can calculate a pointer to one-past-the-end of an array (e.g.,
int* p = &arr[10];for a 10-element array), but you cannot dereference it (int x = *p;is a violation). This is crucial for loop termination conditions. - Structs and Arrays: The distinction between
&myStruct.elem1[11](compliant, as the address is within the largermyStructobject) andmyStruct.elem1[11](non-compliant, as the access is outside theelem1array bounds) is a sophisticated point that trips up many developers. - Single Objects: A single variable is treated as an array of one element. Thus,
int x; int* p = &x + 1;is compliant, but*p = 0;is not.
Rules 18.2 & 18.3: Pointer Subtraction and Comparison
Related to Rule 18.1 are the rules for how pointers can be compared.
- Rule 18.2: Subtraction between pointers shall only be applied to pointers that address elements of the same array.
- Rule 18.3: The relational operators
>,>=,<and<=shall not be applied to objects of pointer type except where they point into the same object.

The rationale is straightforward: comparing or subtracting addresses of unrelated objects (e.g., two different local variables, or variables in different memory segments) gives a result that is implementation-defined and has no portable meaning.
Test your understanding!
Analyze the following function. Identify at least three distinct violations of the MISRA rules we've just discussed (18.1, 18.2, 18.3). Explain your reasoning for each.
#include <stdint.h>
#include <stddef.h>
void analyze_buffers(void) {
uint32_t primary_buf[8];
uint32_t secondary_buf[8];
uint32_t *p1 = &primary_buf[0];
uint32_t *p2 = &primary_buf[8]; // One past the end
uint32_t *p3 = &secondary_buf[4];
// Check 1
if (p1 < p3) {
/* do something */
}
// Check 2
ptrdiff_t diff = p2 - p3;
// Check 3
*p2 = 0xDEADBEEF;
}
Show answer
-
Violation in Check 1:
if (p1 < p3)- Rule: 18.3 (Relational operators shall only be applied to pointers pointing to the same object).
- Reasoning:
p1points intoprimary_buf, whilep3points intosecondary_buf. These are two different, unrelated array objects. Comparing their addresses is not portable and has no defined behavior in C.
-
Violation in Check 2:
ptrdiff_t diff = p2 - p3;- Rule: 18.2 (Subtraction between pointers shall only be applied to pointers that address elements of the same array).
- Reasoning: Similar to the first violation,
p2andp3point into different arrays. Subtracting them is not allowed.
-
Violation in Check 3:
*p2 = 0xDEADBEEF;- Rule: 18.1 (implied). This is a dereference of an out-of-bounds pointer.
- Reasoning:
p2was initialized to&primary_buf[8], which is the address one-past-the-end of the array. While creating this pointer is compliant, dereferencing it to write a value is a classic buffer overflow and a serious violation.
2. Pointers and Object Lifetime
A particularly dangerous category of bugs involves pointers that outlive the data they point to. These are called dangling pointers.
Rule 18.6: Address of an Object with Automatic Storage
The rule states: The address of an object with automatic storage shall not be copied to another object that persists after the first object has ceased to exist.
"Automatic storage" refers to local variables stored on the stack. When a function returns, its stack frame is deallocated, and any pointers to variables within that frame become invalid.
The following resource provides concise examples of this common and critical error.
Safe and Secure Software - MISRA C:2012
The article 'Safe and Secure Software - MISRA C:2012' provides clear examples of Rule 18.6 violations.
Please scroll to the section 'Pointers and arrays' and focus on the code examples for Rule 18.6. Notice the two different ways a dangling pointer can be created: by returning it directly or by storing it in a more persistent location (like a global pointer).
Analyzing code for this violation means tracking where pointers are assigned. If you see the address-of operator (&) applied to a local variable, you must check if that address is:
- Returned from the function.
- Assigned to a global or static pointer.
- Assigned to a pointer passed in as a function argument which is then stored elsewhere.
This type of bug can be maddening to debug, as the memory location might work correctly for a short time until the stack space is overwritten by a subsequent function call—a classic symptom you might have encountered when debugging with Trace32.
3. A Deeper Look at Pointer Conversion Violations
In the last lesson, we covered the most common casting rules. Now, let's broaden our analytical scope to spot a wider range of conversion-related violations. The following reading covers a catalog of these rules.
Safe and Secure Software - MISRA C:2012
Let's revisit 'Safe and Secure Software - MISRA C:2012' to review a comprehensive list of pointer conversion rules.
Read the entire 'Pointer type conversions' section (Rules 11.1 through 11.9). We covered some of these last time, but pay special attention to the rules you haven't seen: 11.1 (function pointers), 11.5 (void*), and 11.8 (const/volatile).
Let's highlight a few key rules for analysis:
- Rule 11.1 (Function Pointers): Casting between different function pointer types or between a function pointer and an object/integer pointer is forbidden. This is critical for event-driven systems or callback architectures (common in AUTOSAR) where type-safe function dispatch is essential.
- Rule 11.5 (void to object):** A conversion from
void*to a pointer to object should not be implicit. This is aRequiredrule, meaning it can be deviated with justification. In C,mallocreturnsvoid*, and assigning its result toint *pis an implicit conversion. MISRA C:2012 requires an explicit cast (int *p = (int*)malloc(...)), though this is a point of debate in the wider C community. The key is that any such conversion must be deliberate and auditable. - Rule 11.8 (Casting away qualifiers): A cast must not remove
constorvolatile. This is a mandatory safety rule. Casting awayconstcan lead to modification of what should be read-only data. Casting awayvolatileis even more dangerous in an embedded context, as it tells the compiler it can optimize away reads/writes to a hardware register, breaking device driver logic.
Test your understanding!
You are reviewing a colleague's code for a generic message dispatcher. Identify the MISRA violations related to the conversion rules we just discussed.
#include <stdint.h>
#include <stdlib.h>
// Type for a generic handler
typedef void (*generic_handler_t)(void *data);
// Type for a specific handler
typedef void (*int_handler_t)(int *data);
void process_int(int *data) { /* ... */ }
void register_handler(generic_handler_t *handler_slot, const void *new_handler) {
// Violation 1?
*handler_slot = (generic_handler_t)new_handler;
}
void dispatch(void) {
const int payload = 123;
int_handler_t specific_handler;
// Violation 2?
register_handler((generic_handler_t*)&specific_handler, &process_int);
// Violation 3?
specific_handler = (int_handler_t) &payload;
}
Show answer
-
Violation 1:
*handler_slot = (generic_handler_t)new_handler;- Rule: 11.8 (A cast shall not remove
constqualification). - Reasoning: The
new_handlerparameter is aconst void*, but it's being cast togeneric_handler_t, which is avoid (*)(void*). This cast removes theconstqualifier from the object it points to (the function itself). While function pointers andconstcan be complex, this pattern is flagged by MISRA as it discards a qualifier. A better function signature would beconst generic_handler_t new_handler.
- Rule: 11.8 (A cast shall not remove
-
Violation 2:
register_handler((generic_handler_t*)&specific_handler, &process_int);- Rule: 11.3 (A cast shall not be performed between a pointer to object type and a pointer to a different object type). This is a subtle instance.
- Reasoning:
&specific_handleris a pointer to a function pointer (int_handler_t*). It is being cast to a different pointer-to-function-pointer type (generic_handler_t*). This is a cast between two different pointer types and is a violation.
-
Violation 3:
specific_handler = (int_handler_t) &payload;- Rule: 11.1 (Conversions shall not be performed between a pointer to function and any other type).
- Reasoning: This code is casting a pointer-to-object (
const int*, the address ofpayload) into a pointer-to-function (int_handler_t). This is a severe type confusion. Attempting to callspecific_handlerwould try to execute data as code, leading to a crash.
Conclusion
Today you put on the hat of a static analysis tool, learning to spot critical MISRA C violations in code. We've seen that compliant pointer usage is about discipline: respecting array boundaries, understanding object lifetimes, and preserving the integrity of the type system.
Key Takeaways for Code Analysis:
- Boundary Checks: Always verify that pointer arithmetic, subtraction, and comparisons are confined to a single, valid object. Be extra vigilant about off-by-one errors that lead to dereferencing a one-past-the-end pointer.
- Scope and Lifetime: When you see the address of a local (stack) variable being taken, trace its usage. If it's returned or stored in a location with a longer lifetime, it's a dangling pointer violation (Rule 18.6).
- Conversion Scrutiny: Treat every pointer cast with suspicion. Check for conversions between incompatible function pointers (Rule 11.1), casts that discard qualifiers like
constorvolatile(Rule 11.8), and improper mixing of pointers and integers (Rule 11.4).
Mastering this analytical skill is invaluable. It not only helps you write better code but also allows you to more effectively review team members' code and correctly interpret the output from tools like Coverity and Helix QAC.
Preview of the Next Lesson
Detecting violations is the first half of the battle. The second, more constructive half, is fixing them. In our next lesson, we will focus on the final learning outcome of this module: Refactor non-compliant code involving pointers to meet MISRA-C standards and functional safety goals. We will take the non-compliant snippets from today and work through safe, compliant, and efficient alternatives.