Never return or expose the address of a stack variable beyond its lifetime. Use heap allocation, copied data, or caller-owned buffers when a value must outlive the function call. Compile with warnings and sanitiser checks, and review pointer use carefully to prevent dangling references and memory corruption.
Why returning a stack variable address is unsafe
Returning the address of a stack variable creates a pointer to memory that stops being valid when the function returns. The stack frame is then free to be reused, so later reads or writes can become undefined behaviour, data corruption, or a crash. In security-sensitive code, that bug can also expose sensitive data or create an exploitable memory safety flaw.
The core problem is lifetime mismatch. A stack object is owned by the current call frame, but the caller now holds a reference that outlives that frame. Even if the code appears to work in testing, small changes in compiler optimisation, inlining, or call ordering can make the failure intermittent and difficult to reproduce.
When reviewing this pattern, treat it as a memory safety issue first, not just a style problem. If the caller needs data after return, the code must transfer ownership, copy the value, or place the storage somewhere with a longer lifetime than the function itself. That is the boundary that determines whether the pointer remains valid.
Which design choices remove the lifetime hazard?
The safest mitigation is to avoid exporting stack addresses altogether. If the returned value must persist, allocate it on the heap with clear ownership rules, return a copied structure or scalar value, or let the caller provide the buffer so the callee only writes into memory the caller owns. These patterns keep the lifetime explicit and prevent a dangling reference from escaping the function.
Where the data is small, returning by value is often the cleanest option because modern compilers can optimise copies efficiently. Where the data is larger or variable-sized, an out-parameter or caller-owned buffer can be better, provided the interface also documents the expected size and ownership. Heap allocation is appropriate when the value must outlive the call and the API can clearly define who frees it.
Defensive compilation helps, but it is a backstop rather than the primary fix. Warnings, sanitiser builds, and static analysis can catch some invalid lifetime patterns during development and testing, but they do not make an unsafe interface safe in production. The interface itself still needs to enforce correct ownership and lifetime.
How do you spot and prevent this in code review?
Look for functions that return pointers, references, or addresses to local variables, local arrays, temporary structs, or data derived from them. Also inspect helper functions that pass local storage into callbacks, store stack addresses in globals, or hand them to asynchronous code. The bug often appears indirectly, not only in an obvious CWE Top 25 Guide pattern of returning a local address.
Reviewers should also check whether a returned pointer is later used after a control-flow change, error path, or deferred execution. The immediate caller may be safe while a downstream consumer is not. That is why pointer flow analysis matters as much as the original return statement.
For broader secure coding context, the bug sits in the same family as a NIST Cybersecurity Framework 2.0 governance issue: the organisation needs a repeatable way to identify memory-safety weaknesses before they become defects in released software. In practice, that means pairing review standards with build-time checks and test coverage, not relying on developer memory alone.
Risk and Threat Considerations
Dangling stack references are dangerous because they can degrade from a simple crash into memory corruption, information exposure, or an exploitable primitive if the stale pointer is later dereferenced in a sensitive code path. The risk rises when the returned address reaches parsing, authentication, or privilege-related logic, where corrupted state has higher impact.
Failure mechanism: The function returns an address into a stack frame that is later reused, so the pointer can reference overwritten or attacker-influenced data, or trigger invalid access when dereferenced.
Impact: The result can be denial of service, corrupted application state, leakage of adjacent memory, or a pathway to broader memory corruption exploitation depending on how the stale reference is used.
Standards & Framework Alignment
This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.
CIS Controls v8, NIST CSF 2.0 and OWASP ASVS set the governance and control requirements practitioners need to meet.
| Framework | Control / Reference | Relevance |
|---|---|---|
| CIS Controls v8 | CIS-16 — Application Software Security | Covers secure coding review and memory-safety defect prevention in software. |
| Recommendation — Apply secure coding review and testing to block dangling-pointer defects before release. | ||
| NIST CSF 2.0 | PR.IP-01 — Policies and Processes Are In Place to Manage and Reduce Risk | Addresses the process controls needed to catch unsafe coding patterns consistently. |
| Recommendation — Define secure coding checks that prevent invalid pointer lifetimes from reaching production. | ||
| OWASP ASVS | V15 — Secure Coding and Architecture | Directly supports design-time prevention of unsafe lifetime and memory-ownership bugs. |
| Recommendation — Review APIs and data flow so returned values do not depend on expired stack storage. | ||
Practitioner Guidance
What to prioritise: Fix the interface, not just the symptom. If the value must survive the call, make ownership explicit through return-by-value, heap allocation with a documented free path, or caller-supplied storage.
What to verify: Confirm that no returned pointer, reference, callback argument, or stored global depends on a local object whose lifetime ends at function exit. Sanitiser runs should be used to validate the fix, not to justify keeping a fragile interface.
Common mistake: Replacing one unsafe return with another hidden lifetime dependency, such as caching the stack address in a static variable or handing it to asynchronous code.
Practitioner takeaway: The safest design is the one that makes lifetime obvious in the API, because if callers can outlive the callee, stack storage is the wrong place for the data.
Related resources from NHI Mgmt Group
Deepen Your Knowledge
Free weekly newsletter
Subscribe to the NHI & AI Identity Journal
The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.
Bonus 33% off our NHI Course when you subscribe.
Reviewed and updated by the NHIMG editorial team on October 19, 2026.
NHI Mgmt Group — the #1 independent authority on Non-Human Identity, IAM, and Agentic AI security. nhimg.org