Recent from talks
Contribute something
Nothing was collected or created yet.
Dangling pointer
View on Wikipedia
Dangling pointers and wild pointers in computer programming are pointers that do not point to a valid object of the appropriate type. These are special cases of memory safety violations. More generally, dangling references and wild references are references that do not resolve to a valid destination.
Dangling pointers arise during object destruction, when an object that is pointed to by a given pointer is deleted or deallocated, without modifying the value of that said pointer, so that the pointer still points to the memory location of the deallocated memory. The system may reallocate the previously freed memory, and if the program then dereferences the (now) dangling pointer, unpredictable behavior may result, as the memory may now contain completely different data. If the program writes to memory referenced by a dangling pointer, a silent corruption of unrelated data may result, leading to subtle bugs that can be extremely difficult to find. If the memory has been reallocated to another process, then attempting to dereference the dangling pointer can cause segmentation faults (UNIX, Linux) or general protection faults (Windows). If the program has sufficient privileges to allow it to overwrite the bookkeeping data used by the kernel's memory allocator, the corruption can cause system instabilities. In object-oriented languages with garbage collection, dangling references are prevented by only destroying objects that are unreachable, meaning they do not have any incoming pointers; this is ensured either by tracing or reference counting. However, a finalizer may create new references to an object, requiring object resurrection to prevent a dangling reference.
Wild pointers, also called uninitialized pointers, arise when a pointer is used prior to initialization to some known state, which is possible in some programming languages. They show the same erratic behavior as dangling pointers, though they are less likely to stay undetected because many compilers will raise a warning at compile time if declared variables are accessed before being initialized.[1]
Cause of dangling pointers
[edit]In many languages (e.g., the C programming language) deleting an object from memory explicitly or by destroying the stack frame on return does not alter associated pointers. The pointer still points to the same location in memory even though that location may now be used for other purposes.
A straightforward example is shown below:
{
char* dp = NULL;
// ...
{
char c;
dp = &c;
}
// c falls out of scope
// dp is now a dangling pointer
}
If the operating system is able to detect run-time references to null pointers, a solution to the above is to assign 0 (null) to dp immediately before the inner block is exited. Another solution would be to somehow guarantee dp is not used again without further initialization.
Another frequent source of dangling pointers is a jumbled combination of malloc() and free() library calls: a pointer becomes dangling when the block of memory it points to is freed. As with the previous example one way to avoid this is to make sure to reset the pointer to null after freeing its reference—as demonstrated below.
#include <stdlib.h>
void func() {
char* dp = (char*)malloc(sizeof(char) * 10);
// ...
free(dp); // dp now becomes a dangling pointer
dp = NULL; // dp is no longer dangling
// ...
}
An all too common misstep is returning addresses of a stack-allocated local variable: once a called function returns, the space for these variables gets deallocated and technically they have "garbage values".
int* func(void) {
int num = 1234;
// ...
return #
}
Attempts to read from the pointer may still return the correct value (1234) for a while after calling func, but any functions called thereafter may overwrite the stack storage allocated for num with other values and the pointer would no longer work correctly. If a pointer to num must be returned, num must have scope beyond the function—it might be declared as static.
Manual deallocation without dangling reference
[edit]Antoni Kreczmar (1945–1996) has created a complete object management system which is free of dangling reference phenomenon.[2] A similar approach was proposed by Fisher and LeBlanc[3] under the name Locks-and-keys.
Cause of wild pointers
[edit]Wild pointers are created by omitting necessary initialization prior to first use. Thus, strictly speaking, every pointer in programming languages which do not enforce initialization begins as a wild pointer.
This most often occurs due to jumping over the initialization, not by omitting it. Most compilers are able to warn about this.
int f(int i) {
char* dp; // dp is a wild pointer
static char* scp; /* scp is not a wild pointer:
* static variables are initialized to 0
* at start and retain their values from
* the last call afterwards.
* Using this feature may be considered bad
* style if not commented */
}
Security holes involving dangling pointers
[edit]
Like buffer-overflow bugs, dangling/wild pointer bugs frequently become security holes. For example, if the pointer is used to make a virtual function call, a different address (possibly pointing at exploit code) may be called due to the vtable pointer being overwritten. Alternatively, if the pointer is used for writing to memory, some other data structure may be corrupted. Even if the memory is only read once the pointer becomes dangling, it can lead to information leaks (if interesting data is put in the next structure allocated there) or to privilege escalation (if the now-invalid memory is used in security checks). When a dangling pointer is used after it has been freed without allocating a new chunk of memory to it, this becomes known as a "use after free" vulnerability.[4] For example, CVE-2014-1776 is a use-after-free vulnerability in Microsoft Internet Explorer 6 through 11[5] that was used by zero-day attacks by an advanced persistent threat.[6]
Avoiding dangling pointer errors
[edit]In C, the simplest technique is to implement an alternative version of the free() (or alike) function which guarantees the reset of the pointer. However, this technique will not clear other pointer variables which may contain a copy of the pointer.
#include <assert.h>
#include <stdlib.h>
// Safe version of free()
static void safeFree(void** pp) {
// in debug mode, abort if pp is NULL
assert(pp);
// free(NULL) works properly, so no check is required besides the assert in debug mode
free(*pp); // deallocate chunk, note that free(NULL) is valid
*pp = NULL; // reset original pointer
}
int f(int i) {
char* p = NULL;
char* p2;
p = (char*)malloc(1000); // get a chunk
p2 = p; // copy the pointer
// use the chunk here
safeFree((void**)&p); // safety freeing; does not affect p2 variable
safeFree((void**)&p); // this second call won't fail as p is reset to NULL
char c = *p2; // p2 is still a dangling pointer, so this is undefined behavior.
return i + c;
}
The alternative version can be used even to guarantee the validity of an empty pointer before calling malloc():
safeFree(&p); // I'm not sure if chunk has been released */
p = (char*)malloc(1000); // allocate now
These uses can be masked through #define directives to construct useful macros (a common one being #define XFREE(ptr) safeFree((void**)&(ptr))), creating something like a metalanguage or can be embedded into a tool library apart. In every case, programmers using this technique should use the safe versions in every instance where free() would be used; failing in doing so leads again to the problem. Also, this solution is limited to the scope of a single program or project, and should be properly documented.
Among more structured solutions, a popular technique to avoid dangling pointers in C++ is to use smart pointers. A smart pointer typically uses reference counting to reclaim objects. Some other techniques include the tombstones method and the locks-and-keys method.[3]
Another approach is to use the Boehm garbage collector, a conservative garbage collector that replaces standard memory allocation functions in C and C++ with a garbage collector. This approach completely eliminates dangling pointer errors by disabling frees, and reclaiming objects by garbage collection.
Another approach is to use a system such as CHERI, which stores pointers with additional metadata which may prevent invalid accesses by including lifetime information in pointers. CHERI typically requires support in the CPU to conduct these additional checks.
In languages like Java, dangling pointers cannot occur because there is no mechanism to explicitly deallocate memory. Rather, the garbage collector may deallocate memory, but only when the object is no longer reachable from any references.
In the language Rust, the type system has been extended to include also the variables lifetimes and resource acquisition is initialization. Unless one disables the features of the language, dangling pointers will be caught at compile time and reported as programming errors.
Dangling pointer detection
[edit]To expose dangling pointer errors, one common programming technique is to set pointers to the null pointer or to an invalid address once the storage they point to has been released. When the null pointer is dereferenced (in most languages) the program will immediately terminate—there is no potential for data corruption or unpredictable behavior. This makes the underlying programming mistake easier to find and resolve. This technique does not help when there are multiple copies of the pointer.
Some debuggers will automatically overwrite and destroy data that has been freed, usually with a specific pattern, such as 0xDEADBEEF (Microsoft's Visual C/C++ debugger, for example, uses 0xCC, 0xCD or 0xDD depending on what has been freed[7]). This usually prevents the data from being reused by making it useless and also very prominent (the pattern serves to show the programmer that the memory has already been freed).
Tools such as Polyspace, TotalView, Valgrind, Mudflap,[8] AddressSanitizer, or tools based on LLVM[9] can also be used to detect uses of dangling pointers.
Other tools (SoftBound, Insure++, and CheckPointer) instrument the source code to collect and track legitimate values for pointers ("metadata") and check each pointer access against the metadata for validity.
Another strategy, when suspecting a small set of classes, is to temporarily make all their member functions virtual: after the class instance has been destructed/freed, its pointer to the Virtual Method Table is set to NULL, and any call to a member function will crash the program and it will show the guilty code in the debugger.
The ARM64 memory tagging extension (MTE) - disabled by default on Linux systems, but can be enabled on Android 16 - triggers a segmentation fault when it detects use-after-free and buffer overflow.[10][11]
See also
[edit]References
[edit]- ^ "Warning Options - Using the GNU Compiler Collection (GCC)".
- ^ Gianna Cioni, Antoni Kreczmar, Programmed deallocation without dangling reference, Information Processing Letters, v. 18, 1984, pp. 179–185
- ^ a b C. N. Fisher, R. J. Leblanc, The implementation of run-time diagnostics in Pascal , IEEE Transactions on Software Engineering, 6(4):313–319, 1980.
- ^ Dalci, Eric; anonymous author; CWE Content Team (May 11, 2012). "CWE-416: Use After Free". Common Weakness Enumeration. Mitre Corporation. Retrieved April 28, 2014.
{{cite web}}:|author2=has generic name (help) - ^ "CVE-2014-1776". Common Vulnerabilities and Exposures (CVE). 2014-01-29. Archived from the original on 2017-04-30. Retrieved 2017-05-16.
- ^ Chen, Xiaobo; Caselden, Dan; Scott, Mike (April 26, 2014). "New Zero-Day Exploit targeting Internet Explorer Versions 9 through 11 Identified in Targeted Attacks". FireEye Blog. FireEye. Retrieved April 28, 2014.[permanent dead link]
- ^ Visual C++ 6.0 memory-fill patterns
- ^ Mudflap Pointer Debugging
- ^ Dhurjati, D. and Adve, V. Efficiently Detecting All Dangling Pointer Uses in Production Servers
- ^ "Arm memory tagging extension". Android Open Source Project. Retrieved 2025-06-11.
- ^ Goodin, Dan (2025-05-13). "Google introduces Advanced Protection mode for its most at-risk Android users". Ars Technica. Retrieved 2025-06-11.
Dangling pointer
View on Grokipediamalloc and free or new and delete.[1][3]
Dangling pointers typically occur in two common scenarios: when a pointer to dynamically allocated memory on the heap is not updated after deallocation, or when a pointer is returned from a function referencing a local variable whose scope has ended, causing the memory to be reclaimed.[1] For instance, if multiple pointers reference the same heap-allocated object and one is used to delete it without nullifying the others, the remaining pointers become dangling.[2] In the former case, the pointer retains the original address despite the memory being available for reuse by the system, potentially leading to the pointer accessing unrelated or corrupted data.[3]
The use of a dangling pointer, known as a dereference, constitutes a use-after-free error, which triggers undefined behavior in the program.[3] This can manifest as subtle bugs, such as reading stale or modified values from the memory location, program crashes, or severe security vulnerabilities including information leakage, privilege escalation, or control-flow hijacking by malicious actors.[3] Compilers rarely detect these issues at compile time, and the behavior may appear correct initially, complicating debugging as small code changes can expose latent errors.[2] For example, in web browsers like Chromium, use-after-free bugs linked to dangling pointers accounted for a significant portion of critical vulnerabilities between 2011 and 2013.[3]
To mitigate dangling pointers, best practices include setting the pointer to nullptr (or NULL) immediately after deallocation to cause a detectable null pointer dereference if accessed erroneously, rather than silent undefined behavior.[1] Advanced techniques, such as runtime nullification systems that track and invalidate pointers upon memory freeing, have been proposed to enforce memory safety automatically, though they introduce performance overhead.[3] In modern C++, smart pointers like std::unique_ptr and std::shared_ptr from the standard library help prevent these issues by managing memory lifetimes automatically.[1]
Fundamentals
Definition and Characteristics
A dangling pointer is a pointer that refers to memory that has been deallocated, such as by a call tofree() or realloc() in C, while the pointer variable itself remains in scope and retains its value.[4] In C++, it similarly arises when a pointer references an object after its lifetime has ended, without the pointer being updated.[5]
Key characteristics of a dangling pointer include its initial validity as a legitimate address to allocated memory, which becomes invalid upon deallocation or lifetime expiration; the pointer's value does not change automatically, leading to potential reuse of the memory for unrelated data; and any attempt to dereference or access it invokes undefined behavior as specified in the C and C++ standards.[4][5] This issue is prevalent in languages like C and C++ that permit manual memory management, where programmers must explicitly handle allocation and deallocation.[4]
The following C example demonstrates a basic dangling pointer scenario with dynamic allocation:
#include <stdlib.h>
int main(void) {
int *ptr = malloc(sizeof(int)); // Allocates memory and assigns address to ptr
if (ptr != NULL) {
*ptr = 42; // Valid write to allocated memory
free(ptr); // Deallocates the memory, making ptr dangling
// *ptr = 100; // Undefined behavior: access after deallocation
}
// ptr still holds the original address but points to invalid memory
return 0;
}
#include <stdlib.h>
int main(void) {
int *ptr = malloc(sizeof(int)); // Allocates memory and assigns address to ptr
if (ptr != NULL) {
*ptr = 42; // Valid write to allocated memory
free(ptr); // Deallocates the memory, making ptr dangling
// *ptr = 100; // Undefined behavior: access after deallocation
}
// ptr still holds the original address but points to invalid memory
return 0;
}
free(ptr), the memory is returned to the heap, but ptr unchangedly references it; any subsequent dereference, such as the commented line, results in undefined behavior per C Standard section 7.20.3.2.[4]
Conceptually, the state evolves as follows:
Before deallocation:
- Allocated memory block:
[ valid | value = 42 ] - Pointer:
ptr ───→ [ valid memory ]
- Memory block:
[ freed / invalid ](potentially reusable) - Pointer:
ptr ───→ [ freed memory ](dangling reference)
Distinction from Related Pointer Errors
Dangling pointers are often confused with null pointers, which are explicitly initialized to point to no valid memory location, typically represented by the value 0 or NULL.[6] Unlike dangling pointers that reference previously allocated but now invalid memory, dereferencing a null pointer generally results in an immediate segmentation fault or process termination due to accessing an unmapped address space.[7] This predictable failure behavior contrasts with dangling pointers, where dereference may succeed temporarily if the memory has been reallocated, leading to subtle data corruption rather than instant crashes.[6] Wild pointers, also known as uninitialized pointers, differ from dangling pointers in that they hold arbitrary garbage values because they have not been assigned any valid address.[6] While a dangling pointer starts as valid but becomes invalid after the target object's deallocation, a wild pointer never points to intended memory from the outset, making its dereference unpredictable and often immediately erroneous.[6] Both can cause undefined behavior, but the distinction lies in their lifecycle: wild pointers lack initialization, whereas dangling pointers arise from post-invalidation persistence.[6] Use-after-free errors represent the misuse of a dangling pointer, where the act of accessing or dereferencing the invalidated pointer constitutes the vulnerability, rather than the pointer's state alone.[8] A dangling pointer describes the condition of the pointer after memory deallocation, but use-after-free specifically refers to the erroneous operation on it, potentially enabling exploits like code injection if the memory is reused maliciously.[8] This separation emphasizes that while all use-after-free instances involve dangling pointers, not all dangling pointers lead to use-after-free if never accessed post-invalidation.[8]| Pointer Error Type | Cause | Symptoms | Languages Affected |
|---|---|---|---|
| Dangling Pointer | Deallocation or expiration of pointed-to memory while pointer persists | Potential data corruption or delayed crashes if memory reused; undefined behavior on dereference | C, C++, manual memory management languages like Rust (unsafe mode) |
| Null Pointer | Explicit initialization to NULL or 0 | Immediate segmentation fault or process abort on dereference | C, C++, Java, C# (unsafe), most pointer-supporting languages |
| Wild Pointer | Lack of initialization, holding garbage values | Unpredictable reads/writes; immediate crashes or corruption | C, C++, languages without automatic pointer initialization |
Causes
Memory Deallocation
In languages such as C and C++, explicit memory deallocation on the heap is a primary cause of dangling pointers. Thefree() function in C, or the delete operator in C++, releases dynamically allocated memory back to the system, rendering any pointers referencing that memory invalid for further access. However, these deallocation operations do not modify or nullify the pointer variables themselves, which retain their original address values. Subsequent dereferencing of such pointers results in undefined behavior, as the memory may be reused by the system for other purposes or left in an indeterminate state.[9][10][11]
Consider the following C example, which illustrates this mechanism:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = malloc(sizeof(int)); // Allocate memory on the heap
if (ptr == NULL) return 1;
*ptr = 42; // Assign a value
printf("Value before free: %d\n", *ptr); // Valid access: outputs 42
free(ptr); // Deallocate the memory; ptr now points to freed memory
// ptr is not set to NULL, so it remains a dangling pointer
// printf("Value after free: %d\n", *ptr); // Undefined behavior if uncommented
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = malloc(sizeof(int)); // Allocate memory on the heap
if (ptr == NULL) return 1;
*ptr = 42; // Assign a value
printf("Value before free: %d\n", *ptr); // Valid access: outputs 42
free(ptr); // Deallocate the memory; ptr now points to freed memory
// ptr is not set to NULL, so it remains a dangling pointer
// printf("Value after free: %d\n", *ptr); // Undefined behavior if uncommented
return 0;
}
free(ptr), the pointer ptr holds the address of the deallocated block, and any attempt to read or write through it invokes undefined behavior, potentially leading to crashes, incorrect data, or security issues.[9] In C++, a similar issue arises with new and delete, where delete ptr; invalidates ptr without altering its value.[11]
This problem is particularly pronounced with heap deallocation, which involves programmer-controlled dynamic allocation, in contrast to automatic stack-based deallocation handled by scope rules. When multiple pointers reference the same heap-allocated memory—such as through assignment or passing by address—deallocating via one pointer invalidates all others, amplifying the risk if not all are properly managed. For instance, if two pointers p1 and p2 both point to the same malloc()-ed block, calling free(p1) leaves p2 dangling, and using p2 thereafter is undefined.[9][10]
Dangling pointers due to manual memory deallocation have been prevalent since the introduction of the C language in the early 1970s at Bell Labs, where pointers and explicit heap management were adopted from earlier languages like B to enable efficient system programming without garbage collection. This design choice prioritized performance and control but introduced risks inherent to low-level memory handling that persist in C and its derivatives.
Object Lifetime Expiration
In languages with scoped storage duration, such as C++, the lifetime of an automatic object—typically a local variable—begins upon its allocation and initialization and ends when the enclosing scope exits, such as at the conclusion of a function or block statement.[5] This automatic deallocation invalidates any pointers or references that point to the object if those pointers escape the scope, resulting in dangling pointers that refer to memory no longer associated with a valid object.[5] Unlike explicit memory deallocation on the heap, this process is implicit and managed by the compiler, tying the object's destruction directly to lexical scope boundaries.[12] A common scenario arises when a function returns a pointer to a local automatic object, allowing the pointer to outlive the object's scope. For instance, consider the following C++ code:int* createArray() {
int localArray[5] = {1, 2, 3, 4, 5};
return localArray; // Pointer to localArray escapes scope
}
int main() {
int* ptr = createArray(); // ptr now dangles after createArray returns
// Accessing *ptr invokes [undefined behavior](/page/Undefined_behavior)
}
int* createArray() {
int localArray[5] = {1, 2, 3, 4, 5};
return localArray; // Pointer to localArray escapes scope
}
int main() {
int* ptr = createArray(); // ptr now dangles after createArray returns
// Accessing *ptr invokes [undefined behavior](/page/Undefined_behavior)
}
localArray is destroyed upon createArray's return, rendering ptr a dangling pointer whose dereference leads to undefined behavior as defined in the C++ standard.
In C++, the RAII (Resource Acquisition Is Initialization) idiom further emphasizes scope-bound lifetimes, where objects acquire resources in constructors and release them in destructors called at scope exit. However, this can exacerbate dangling issues if references or pointers to automatic objects or temporaries are returned or stored externally. For example, binding a reference to a temporary object extends the temporary's lifetime only to the end of the full-expression unless explicitly managed, but pointers to such temporaries do not receive this extension and become dangling immediately after the expression evaluates.
This mechanism is less prevalent in garbage-collected languages like Java, where automatic memory management prevents most dangling pointers by tracking live references and reclaiming unreachable objects. Nonetheless, constructs like weak references (e.g., WeakReference in Java) can indirectly relate, as they permit object collection without strong retention, potentially leaving the reference cleared (set to null) rather than dangling, though misuse might simulate similar invalid access patterns.[13]
Consequences
Runtime Behaviors
When a dangling pointer is dereferenced or used in a program, it invokes undefined behavior as specified in the C standard, where the outcome is not required to be predictable or consistent across implementations.[14] Possible results include immediate program crashes via hardware-detected memory access violations, silent data corruption if the pointed-to memory has been reused by another allocation, or the program producing apparently correct but unreliable output because the reused memory contains coincidental valid data.[15] In severe cases, such access can overwrite adjacent memory regions, leading to further instability or cascading errors later in execution. Common symptoms of dangling pointer usage manifest as runtime errors during dereference operations, such as attempts to read from or write to the invalid address.[15] This may trigger a crash if the memory is no longer mapped, yield garbage values if the location holds unrelated data from a subsequent allocation, or cause unintended modifications to other program variables if the write operation succeeds on reused memory. Additionally, if the dangling pointer influences control flow—such as in conditional branches or loop counters—it can result in infinite loops or skipped code paths, exacerbating the unpredictability.[14] The following pseudocode illustrates a simple case in C where a pointer becomes dangling after deallocation, leading to potential garbage output or corruption upon reuse:#include <stdlib.h>
int main() {
int *ptr = malloc(sizeof(int));
*ptr = 42; // Valid write
free(ptr); // Pointer now dangles
// Memory may be unmapped or reused
int value = *ptr; // Undefined: may crash, read garbage, or read new data if reused
// If writing: *ptr = 100; could corrupt another allocation
return value; // Unreliable result
}
#include <stdlib.h>
int main() {
int *ptr = malloc(sizeof(int));
*ptr = 42; // Valid write
free(ptr); // Pointer now dangles
// Memory may be unmapped or reused
int value = *ptr; // Undefined: may crash, read garbage, or read new data if reused
// If writing: *ptr = 100; could corrupt another allocation
return value; // Unreliable result
}