Use of Uninitialized Resource
Description
Use of Uninitialized Resource occurs when software uses or accesses a resource that has not been properly initialized. When resources such as variables, memory buffers, file handles, or data structures are used without initialization, they may contain indeterminate values from previous operations or garbage data. This leads to unpredictable program behavior, crashes, security vulnerabilities, or information disclosure. The issue is particularly severe in low-level languages where memory is not automatically initialized.
Risk
Using uninitialized resources can have serious security implications. Uninitialized memory may contain sensitive data from previous operations, leading to information leakage. Uninitialized variables used in security decisions (like user IDs or privilege levels) may default to privileged values. Programs may crash due to invalid data in uninitialized resources. Control flow may be altered in unexpected ways if branch conditions depend on uninitialized values. In access control contexts, uninitialized variables may inadvertently grant elevated privileges.
Solution
Always initialize variables, buffers, and data structures before use. Use language features that enforce initialization (constructors, initializer lists). Enable compiler warnings for uninitialized variable usage. Use static analysis tools to detect potential use of uninitialized data. In C/C++, use memset or calloc to zero-initialize memory allocations. In security-critical code, explicitly set all sensitive variables to safe default values. Consider using languages with mandatory initialization or safer defaults. Implement defensive programming with assertions to verify initialization state.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Application Data - Uninitialized resource may contain sensitive data from previous uses, leading to information disclosure. |
| Availability | Scope: Availability DoS: Crash/Exit/Restart - Uninitialized values may cause program flow changes leading to crashes or unexpected exits. |
| Integrity, Access Control | Scope: Integrity, Access Control Gain Privileges - Uninitialized variables in security decisions may default to privileged values. |
Example Code
Vulnerable Code
// Vulnerable: Uninitialized buffer with strcat
#include <stdio.h>
#include <string.h>
void vulnerable_concat(char *input) {
char buffer[100]; // Uninitialized!
// Vulnerable: strcat appends to existing content
// buffer contains garbage, strcat looks for null terminator
strcat(buffer, input); // May overflow or read garbage
printf("Result: %s\n", buffer);
}
// Attack: buffer may contain long garbage before null
// causing strcat to overflow when it finally finds null
// Vulnerable: Uninitialized variable in conditional path
int vulnerable_process_data(int flag, int *result) {
int value; // Uninitialized!
if (flag == 1) {
value = compute_value();
}
// Vulnerable: if flag != 1, value remains uninitialized
*result = value; // May use garbage value
return 0;
}
// Vulnerable: Uninitialized UID in access control
#include <unistd.h>
int vulnerable_check_access(char *username) {
uid_t uid; // Uninitialized - may default to 0 (root)!
if (username != NULL && strlen(username) > 0) {
uid = lookup_user_id(username);
}
// Vulnerable: If username is NULL or empty, uid is uninitialized
// On many systems, stack memory may contain 0, granting root access
if (uid == 0) {
return ADMIN_ACCESS; // Unintentionally granted!
}
return USER_ACCESS;
}
// Vulnerable: Uninitialized struct members
typedef struct {
char *name;
int privilege_level;
char data[256];
} UserContext;
void vulnerable_create_context(UserContext *ctx, char *name) {
// Only initializes name, leaves other fields uninitialized
ctx->name = strdup(name);
// Vulnerable: privilege_level and data contain garbage
// privilege_level might be 0 (admin) by chance
}
# Vulnerable: Variable not initialized in all paths
def vulnerable_get_user_role(user_id):
# role is not initialized
if user_id > 0:
user = database.get_user(user_id)
if user:
role = user.role
# Vulnerable: if user_id <= 0 or user not found, role is undefined
return role # UnboundLocalError or garbage from previous scope
// Vulnerable: Object fields not initialized
public class VulnerableConfig {
private String adminPassword; // Not initialized
private int maxRetries; // Defaults to 0
private boolean securityEnabled; // Defaults to false
public VulnerableConfig(boolean useDefaults) {
if (useDefaults) {
this.adminPassword = "admin123";
this.maxRetries = 3;
this.securityEnabled = true;
}
// Vulnerable: If useDefaults is false, fields have unsafe defaults
}
public boolean checkSecurity() {
return securityEnabled; // May be false when it shouldn't be
}
}
Fixed Code
// Fixed: Initialize buffer before use
#include <stdio.h>
#include <string.h>
void fixed_concat(char *input) {
char buffer[100];
// Fixed: Initialize buffer to empty string
buffer[0] = '\0';
// Or use: memset(buffer, 0, sizeof(buffer));
// Now strcat works correctly
if (input != NULL && strlen(input) < sizeof(buffer)) {
strcat(buffer, input);
}
printf("Result: %s\n", buffer);
}
// Fixed: Initialize variable and ensure all paths set it
int fixed_process_data(int flag, int *result) {
int value = 0; // Fixed: Initialize to safe default
if (flag == 1) {
value = compute_value();
} else {
// Fixed: Explicitly handle other cases
value = default_value();
}
*result = value;
return 0;
}
// Alternative: Return error if value cannot be set
int better_process_data(int flag, int *result) {
if (flag != 1) {
return -1; // Error: cannot compute value
}
*result = compute_value();
return 0;
}
// Fixed: Initialize UID to unprivileged value
#include <unistd.h>
int fixed_check_access(char *username) {
// Fixed: Initialize to unprivileged value
uid_t uid = (uid_t)-1; // -1 is typically "nobody" or invalid
if (username != NULL && strlen(username) > 0) {
uid = lookup_user_id(username);
// Verify lookup succeeded
if (uid == (uid_t)-1) {
return ACCESS_DENIED;
}
} else {
// Fixed: Explicitly deny access for invalid input
return ACCESS_DENIED;
}
if (uid == 0) {
return ADMIN_ACCESS;
}
return USER_ACCESS;
}
// Fixed: Initialize all struct members
typedef struct {
char *name;
int privilege_level;
char data[256];
} UserContext;
// Fixed: Initialization function that sets all fields
int fixed_create_context(UserContext *ctx, char *name) {
if (ctx == NULL || name == NULL) {
return -1;
}
// Fixed: Initialize all fields
ctx->name = strdup(name);
if (ctx->name == NULL) {
return -1;
}
ctx->privilege_level = PRIVILEGE_NONE; // Explicit safe default
memset(ctx->data, 0, sizeof(ctx->data));
return 0;
}
// Better: Use designated initializers (C99+)
UserContext create_context_safe(char *name) {
UserContext ctx = {
.name = name ? strdup(name) : NULL,
.privilege_level = PRIVILEGE_NONE,
.data = {0}
};
return ctx;
}
# Fixed: Initialize variable in all paths
def fixed_get_user_role(user_id):
# Fixed: Initialize to safe default
role = "guest" # Default role
if user_id > 0:
user = database.get_user(user_id)
if user and hasattr(user, 'role'):
role = user.role
return role
# Better: Explicit error handling
def better_get_user_role(user_id):
if user_id <= 0:
raise ValueError("Invalid user ID")
user = database.get_user(user_id)
if not user:
raise LookupError(f"User {user_id} not found")
if not hasattr(user, 'role'):
return "guest" # Default for users without role
return user.role
// Fixed: Initialize all fields explicitly
public class FixedConfig {
private String adminPassword;
private int maxRetries;
private boolean securityEnabled;
// Fixed: Always initialize to safe defaults
public FixedConfig() {
this.adminPassword = null; // No default password
this.maxRetries = 3;
this.securityEnabled = true; // Secure by default
}
public FixedConfig(boolean useCustom) {
this(); // Call default constructor first
if (useCustom) {
// Override with custom values
loadCustomConfig();
}
}
private void loadCustomConfig() {
// Load from config file, still maintaining safe defaults
this.adminPassword = ConfigLoader.getString("admin.password", null);
this.maxRetries = ConfigLoader.getInt("max.retries", 3);
this.securityEnabled = ConfigLoader.getBoolean("security.enabled", true);
}
public boolean checkSecurity() {
return securityEnabled; // Always initialized
}
}
// Fixed: Use calloc for zero-initialized allocations
#include <stdlib.h>
#include <string.h>
typedef struct {
int id;
char name[64];
int permissions;
} UserRecord;
UserRecord* fixed_allocate_user() {
// Fixed: Use calloc to zero-initialize
UserRecord *user = (UserRecord*)calloc(1, sizeof(UserRecord));
if (user == NULL) {
return NULL;
}
// All fields are now zero-initialized
// Set specific fields as needed
user->id = generate_user_id();
return user;
}
// Alternative: Explicit initialization after malloc
UserRecord* fixed_allocate_user_v2() {
UserRecord *user = (UserRecord*)malloc(sizeof(UserRecord));
if (user == NULL) {
return NULL;
}
// Fixed: Explicitly initialize all fields
memset(user, 0, sizeof(UserRecord));
user->id = generate_user_id();
strncpy(user->name, "Unknown", sizeof(user->name) - 1);
user->permissions = PERM_NONE;
return user;
}
CVE Examples
- CVE-2019-9805: Memory corruption caused by use of uninitialized value when reading data.
- CVE-2008-0081: Code execution possible due to uninitialized variables in parsing logic.
- CVE-2008-2934: Crash caused by use of a freed, uninitialized pointer.
- CVE-2008-4197: Privilege escalation via uninitialized memory access.
- CVE-2007-4682: Information disclosure through uninitialized memory read.
Related CWEs
- CWE-665: Improper Initialization (parent)
- CWE-457: Use of Uninitialized Variable (child)
- CWE-909: Missing Initialization of Resource (related)
- CWE-824: Access of Uninitialized Pointer (related)
References
- MITRE Corporation. "CWE-908: Use of Uninitialized Resource." https://cwe.mitre.org/data/definitions/908.html
- CERT C Secure Coding Standard. "EXP33-C. Do not read uninitialized memory."
- CERT C Secure Coding Standard. "DCL30-C. Declare objects with appropriate storage durations."