Access of Uninitialized Pointer
Description
Access of Uninitialized Pointer is a memory safety vulnerability where software accesses or uses a pointer variable that has not been initialized to a valid memory address. Uninitialized pointers contain indeterminate values—whatever data happened to be in that memory location. When dereferenced, such pointers may reference arbitrary memory locations, causing the program to read from or write to unexpected memory areas. If the pointer is used as a function pointer, arbitrary functions could be invoked. The severity of this vulnerability depends on memory layout, memory management behaviors, and how the product operates.
Risk
This vulnerability can lead to crashes, information disclosure, or code execution. When an uninitialized pointer is dereferenced for reading, it may access sensitive data from other parts of memory, or it may access invalid memory causing a crash. When used for writing, it can corrupt arbitrary memory locations, potentially overwriting security-critical data or function pointers. Attackers who can influence the contents of uninitialized memory (through heap spraying or other techniques) can control where the pointer points, enabling exploitation for arbitrary code execution. The unpredictable nature of uninitialized data makes this vulnerability particularly dangerous in security-sensitive contexts.
Solution
Always initialize pointers when they are declared, either to a valid address or to NULL. Use compiler warnings (-Wuninitialized, -Werror) to catch uninitialized variable usage. Initialize all local variables at declaration, especially in C/C++. Use static analysis tools to detect potential use of uninitialized pointers. In C++, prefer RAII patterns and smart pointers that enforce initialization. Consider using languages with automatic memory management where uninitialized pointer access is not possible. When a pointer might not be assigned in all code paths, initialize it to NULL and check before dereferencing. Enable runtime sanitizers like MemorySanitizer during testing to detect uninitialized memory access.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Memory - Uninitialized pointers used in read operations could expose sensitive memory contents from arbitrary locations. |
| Availability | Scope: Availability DoS: Crash, Exit, or Restart - Invalid memory references cause crashes when accessing inaccessible or malformed memory locations. |
| Integrity, Confidentiality, Availability | Scope: Integrity, Confidentiality, Availability Execute Unauthorized Code or Commands - If used as a function pointer or for writes, attackers may achieve arbitrary code execution. |
Example Code
Vulnerable Code
// Vulnerable: Pointer declared but not initialized
void vulnerable_uninitialized(int condition) {
char *ptr; // Uninitialized - contains garbage value
if (condition) {
ptr = malloc(100);
strcpy(ptr, "data");
}
// Vulnerable: ptr may be uninitialized if condition is false
printf("Value: %s\n", ptr); // Dereferences garbage address
}
// Vulnerable: Pointer not initialized in all code paths
int vulnerable_conditional(int type, char **out_ptr) {
char *buffer; // Uninitialized
if (type == 1) {
buffer = malloc(256);
strcpy(buffer, "Type 1 data");
} else if (type == 2) {
buffer = malloc(512);
strcpy(buffer, "Type 2 data");
}
// Vulnerable: if type != 1 and type != 2, buffer is uninitialized
*out_ptr = buffer; // May store garbage pointer
return 0;
}
// Vulnerable: Function pointer not initialized
typedef void (*callback_t)(void);
void vulnerable_callback(int registered) {
callback_t handler; // Uninitialized function pointer
if (registered) {
handler = registered_callback;
}
// Vulnerable: If not registered, handler is garbage
handler(); // Calls arbitrary address!
}
// Vulnerable: Struct with uninitialized pointer member
struct node {
int value;
struct node *next; // May be uninitialized
};
void vulnerable_list(void) {
struct node head; // next pointer is uninitialized
head.value = 1;
// Forgot to set head.next = NULL
// Later traversal
struct node *current = &head;
while (current != NULL) {
printf("%d\n", current->value);
current = current->next; // Follows garbage pointer!
}
}
// Vulnerable: Pointer in constructor not initialized
class VulnerableClass {
private:
char *buffer;
int size;
public:
VulnerableClass(int sz) {
size = sz;
// Forgot to initialize buffer!
}
void write(const char *data) {
// buffer is uninitialized
strcpy(buffer, data); // Writes to arbitrary memory
}
};
// Vulnerable: Uninitialized in error path
char* vulnerable_read_file(const char *filename) {
FILE *fp;
char *content; // Uninitialized
long size;
fp = fopen(filename, "r");
if (!fp) {
return content; // Returns uninitialized pointer!
}
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
content = malloc(size + 1);
fread(content, 1, size, fp);
content[size] = '\0';
fclose(fp);
return content;
}
Fixed Code
// Fixed: Initialize pointer at declaration
void fixed_uninitialized(int condition) {
char *ptr = NULL; // Fixed: Initialized to NULL
if (condition) {
ptr = malloc(100);
if (ptr) {
strcpy(ptr, "data");
}
}
// Fixed: Check before dereference
if (ptr != NULL) {
printf("Value: %s\n", ptr);
free(ptr);
}
}
// Fixed: Initialize and handle all code paths
int fixed_conditional(int type, char **out_ptr) {
char *buffer = NULL; // Fixed: Initialize to NULL
if (type == 1) {
buffer = malloc(256);
if (buffer) strcpy(buffer, "Type 1 data");
} else if (type == 2) {
buffer = malloc(512);
if (buffer) strcpy(buffer, "Type 2 data");
} else {
// Fixed: Handle default case
*out_ptr = NULL;
return -1; // Unknown type
}
*out_ptr = buffer;
return buffer ? 0 : -1;
}
// Fixed: Initialize function pointer
typedef void (*callback_t)(void);
static void default_handler(void) {
// No-op default handler
}
void fixed_callback(int registered) {
callback_t handler = default_handler; // Fixed: Safe default
if (registered) {
handler = registered_callback;
}
// Safe: handler always points to valid function
handler();
}
// Fixed: Initialize all struct members
struct node {
int value;
struct node *next;
};
void fixed_list(void) {
struct node head = {0}; // Fixed: Zero-initialize all members
// Or explicitly:
// struct node head;
// head.value = 1;
// head.next = NULL;
head.value = 1;
struct node *current = &head;
while (current != NULL) {
printf("%d\n", current->value);
current = current->next; // Safe: next is NULL
}
}
// Fixed: Initialize in constructor initializer list
class FixedClass {
private:
char *buffer;
int size;
public:
FixedClass(int sz) : buffer(nullptr), size(sz) {
// Fixed: buffer initialized in initializer list
if (size > 0) {
buffer = new char[size];
}
}
~FixedClass() {
delete[] buffer;
}
void write(const char *data) {
if (buffer != nullptr && strlen(data) < static_cast<size_t>(size)) {
strcpy(buffer, data);
}
}
};
// Fixed: Return NULL on error
char* fixed_read_file(const char *filename) {
FILE *fp;
char *content = NULL; // Fixed: Initialize to NULL
long size;
fp = fopen(filename, "r");
if (!fp) {
return NULL; // Fixed: Return known value
}
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
content = malloc(size + 1);
if (content) {
fread(content, 1, size, fp);
content[size] = '\0';
}
fclose(fp);
return content; // Returns NULL or valid pointer
}
Related CWEs
- CWE-119: Improper Restriction of Operations within Bounds of Memory Buffer (parent)
- CWE-457: Use of Uninitialized Variable (related - general form)
- CWE-125: Out-of-bounds Read (can follow)
- CWE-787: Out-of-bounds Write (can follow)
- CWE-908: Use of Uninitialized Resource (parent)
References
- MITRE Corporation. "CWE-824: Access of Uninitialized Pointer." https://cwe.mitre.org/data/definitions/824.html
- CERT C Secure Coding Standard. "EXP33-C. Do not read uninitialized memory."
- Google. "MemorySanitizer." https://github.com/google/sanitizers/wiki/MemorySanitizer