CWE-457, Use of Uninitialized Variable, covers code that reads a variable before it has been given a value. The result is unpredictable or unintended behavior.
It matters most to teams writing C and C++, where the language does not set stack variables for you. It also appears in PHP, Perl and other languages where a missing value takes a default that the logic did not expect.
How it happens
MITRE explains the mechanism. In C and C++, stack variables are not initialized by default. They hold whatever the stack contained before the function was called. An attacker can sometimes control or read those contents. In other languages, an uninitialized variable can receive a default value with security implications, depending on the logic. MITRE adds that an uninitialized variable can sometimes indicate a typo.
Two examples from the entry. First, PHP:
if (isset($_POST['names'])) {
$nameArray = $_POST['names'];
}
echo "Hello " . $nameArray['first'];
If the POST array is absent, $nameArray is never set and the echo hits an error, which could lead to further exploit.
Second, a C switch statement meant to set two variables before they are used:
int aN, bN;
switch (ctl) {
case -1: aN = 0; bN = 0; break;
case 0: aN = i; bN = -i; break;
case 1: aN = i + NEXT_SZ; bN = i - NEXT_SZ; break;
default: aN = -1; aN = -1; break;
}
repaint(aN, bN);
In the default case the programmer set aN twice by accident, so bN is undefined when repaint reads it. MITRE notes an attacker who can trigger this might crash the program, and under the right circumstances may control the value by affecting the stack before the call. The fix is to initialize both variables where they are declared.
Observed CVEs from the entry:
- CVE-2019-15900: a chain where an sscanf call checks if a username and group exist, but its return value is not checked, so an uninitialized variable is tested.
- CVE-2008-3688: a denial of service from an uninitialized variable allowing an infinite loop during a connection to an unresponsive server.
- CVE-2008-0081: an uninitialized variable led to code execution in a popular desktop application.
- CVE-2007-4682: crafted input triggered a dereference of an uninitialized object pointer.
What an attacker gains
From the record:
- Availability and integrity: initial variables usually contain junk that cannot be trusted. This can cause denial of service or alter control flow in unexpected ways. An attacker can sometimes pre-initialize the variable through earlier actions.
- Authorization: strings that are not initialized are especially dangerous, since many functions expect a null at the end, and only at the end, of a string.
How to find it
MITRE lists two methods:
- Fuzzing, which generates many varied inputs and runs the code against them, and can surface crashes and memory corruption even with random inputs.
- Automated static analysis (SAST), which models data flow through the source or binary without running it.
Add compiler warnings to the list. Most compilers flag uninitialized reads when warnings are on. In languages that do not require declarations, run in a mode that reports undeclared or unknown variables, since that may reveal a typo.
How to fix it
MITRE's guidance:
- Ensure that critical variables are initialized before first use.
- Enable compiler warnings about uninitialized variables.
- In languages that do not require explicit declaration, run or compile in a mode that reports undeclared or unknown variables.
- Choose a language that is not susceptible to these issues.
- Consider mitigating technologies such as safe string libraries and container abstractions.
The simplest habit works well: declare and set in the same statement.
int aN = 0, bN = 0;
Try it yourself
A static scan of a code sample shows how data flow analysis highlights reads of unset variables.
Click through it at your own pace, or open it full screen.
Quick check
Why are uninitialized strings especially risky?
Many functions expect a null terminator at the end of a string and nowhere else. An uninitialized buffer may contain nulls in the wrong places or none at all.
What is the cheapest defense?
Initialize at declaration and compile with warnings enabled. Both are listed in MITRE's mitigations.