Static Resource Allocation in Functional Safety — IEC 61508
Static resource allocation is a software design technique where all memory and resources are reserved at compile-time — with no dynamic allocation at runtime. It eliminates a whole class of systematic failures related to unpredictable memory behaviour, making it a core technique for safety-related software under IEC 61508.
What is static resource allocation?
Static resource allocation means all variables, objects, and buffers are allocated at compile-time. No dynamic variables or objects are created while the program runs. Because the memory layout is fixed, the compiler and static analysis tools can verify sizes and addresses, and the system avoids runtime effects such as fragmentation or allocation failure.
How it supports functional safety
By eliminating runtime allocation, the technique removes a whole class of systematic failures related to unpredictable memory behaviour — insufficient heap, undetected overlay or overwrite, and allocator defects.
With a fixed layout, timing remains deterministic and verification becomes simpler. Although it primarily addresses systematic issues, the fixed structure also makes it easier to detect manifestations of random or common-cause hardware faults in memory (e.g. stuck bits), because expected addresses and bounds are known and can be checked.
The key question is: can you prove that your safety-related software never allocates memory at runtime — including in third-party libraries?
When to use
- Safety functions must meet strict real-time deadlines and worst-case execution time analysis
- The standard or safety case restricts dynamic allocation (e.g. IEC 61508-3 Tables A.2/B.1; Annex C.2.6.3)
- Independent verification needs a static, auditable memory map and bounded resource usage
Inputs and outputs
Inputs
- Hardware memory map (RAM/flash, stack limits, MMU/MPU configuration)
- Worst-case resource demands (buffers, task stacks, queues, tables)
Outputs
- Fixed allocation plan (symbols, sections, linker script)
- Evidence: compiler maps, static-analysis reports, and allocation ban checks
Procedure
- Inventory memory needs. Enumerate all buffers, task stacks, message queues, and persistent objects. Define credible worst-case sizes.
- Design the layout. Assign static storage (globals, static locals, fixed-size pools). Define sections in the linker script (e.g. .bss, .data, dedicated IO buffers).
- Ban dynamic allocation. Remove and forbid
malloc/calloc/realloc/free,new/delete. Configure build rules and LINT to fail on use — including in third-party libraries. - Prove bounds. Add compile-time asserts and runtime guards. Generate and review the map file. Verify each buffer has explicit bounds checks at interfaces.
- Validate timing. Measure worst-case execution time with the static layout. Confirm no allocation hotspots exist in task paths.
- Document and test. Record assumptions, margins, and safe reactions for overflow conditions. Include unit and integration tests that exercise near-capacity cases.
Worked example — reactor feed valve controller
A safety PLC regulates a reactor feed valve. Loss or corruption of control data due to heap exhaustion could leave the valve command undefined. By allocating fixed-size input/output buffers and prohibiting dynamic allocation, the control loop always has memory, and any oversize frame is detected and handled safely.
Code-level example
// Example: static buffers and bounds checks (C-like)
#define RX_FRAME_MAX 64
#define TX_FRAME_MAX 64
static uint8_t rx_frame[RX_FRAME_MAX];
static uint8_t tx_frame[TX_FRAME_MAX];
bool handle_rx(const uint8_t* data, size_t len) {
if (len > RX_FRAME_MAX) {
// SAFE REACTION: discard frame, raise diagnostic, hold last known safe output
raise_diagnostic(DIAG_RX_OVERSIZE);
return false;
}
memcpy(rx_frame, data, len);
return true;
}
void control_step(void) {
// No dynamic allocation anywhere in the control path
size_t out_len = build_command(tx_frame, TX_FRAME_MAX);
if (out_len == 0) {
// SAFE REACTION: enter safe state (close valve)
command_valve(CLOSE_VALVE);
} else {
send_command(tx_frame, out_len);
}
}
Quality criteria
- Determinism: No runtime allocation in safety-related paths; timing shown for worst-case conditions.
- Completeness: Every buffer, stack, and queue has a justified size and documented headroom margin.
- Verification: Automated checks (lint/build rules) enforce allocator ban; map file and static-analysis reports reviewed.
- Protection: Bounds checks at all interfaces; safe reactions specified, implemented, and tested.
Common pitfalls
Hidden allocators in libraries
Third-party libraries may call malloc internally without documenting it.
Mitigation: Audit dependencies. Link with stubs that fail the build on allocation calls.
Underestimated worst case leading to overflow
Buffer sizes based on typical conditions rather than credible worst-case scenarios.
Mitigation: Derive sizes from credible worst-case scenarios. Add assertions and tests at capacity limits.
Excessive over-allocation wastes RAM
Defensive over-sizing consumes memory needed by other functions.
Mitigation: Iterate sizing with measurements. Separate safety-critical from non-critical buffers.
Assuming "static" implies "safe"
Static allocation alone does not prevent out-of-bounds writes or data corruption.
Mitigation: Still implement bounds checks, diagnostics, and safe reactions.
Frequently asked questions
Does IEC 61508 always forbid dynamic memory?
No. It requires restrictions where dynamic behaviour cannot be accurately predicted by static analysis. Many safety cases choose a full ban in safety-related paths to simplify assurance.
How do I deal with variable-length payloads without malloc?
Use fixed-size buffers with length fields, ring buffers, or statically sized memory pools. Validate lengths and apply safe reactions on overflow.
Can I mix static allocation in safety code with dynamic allocation elsewhere?
Yes, if freedom-from-interference is demonstrated (e.g. MPU/partitioning) so non-safety allocation cannot impact safety resources.
What evidence should I keep for audits?
Allocator ban rules, dependency audits, linker map files, static-analysis reports, worst-case sizing rationale, and tests showing correct safe reactions at capacity.
Related techniques
- Defensive programming — complements static allocation with explicit checks and safe defaults
- Range checks — ensures inputs never exceed fixed buffer sizes
References
- IEC 61508-3:2010 — Tables A.2/B.1; Annex C.2.6.3
- MISRA C — Guidelines for the Use of the C Language in Critical Systems
- ISO 26262-6:2018 — Product development at the software level
Go deeper — IEC 61508 Certification Course
Our IEC 61508 course covers software safety techniques, architectural design, SIL verification, and safety case preparation — for engineers building safety-related systems.
Explore the course → Ask us a question