...
In the common case, on implementations that make use of a program stack, this value defaults to whichever values are currently stored in stack memory. Uninitialized memory often contains—but is not guaranteed to contain—zeros. Uninitialized memory has indeterminate value, which for objects of some types can be a trap representation. Reading uninitialized memory by an lvalue of a type other than unsigned char
is undefined behavior, (see undefined behavior 10 in and undefined behavior 12 in Annex J of the C Standard), it can cause a program to behave in an unexpected manner and provide an avenue for attack.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <ctype.h> #include <string.h> int do_auth(void) { char *username; char *password; /* Get username and password from user, return -1 if invalid */ } void report_error(const char *msg) { const char *error_log; char buffer[24]; sprintf(buffer, "Error: %s", error_log); printf("%s\n", buffer); } int main(void) { if (do_auth() == -1) { report_error("Unable to login"); } return 0; } |
Noncompliant Code Example
In this noncompliant example, the array elements a[n..2n]
are uninitialized when they are accessed in the for loop.
Code Block | ||||
---|---|---|---|---|
| ||||
void g(double *a, size_t n) {
a = (double *)realloc(a, (n * 2 + 1) * sizeof(double));
if (a != NULL) {
for (size_t i = 0; i != n * 2 + 1; ++i) {
if (a[i] < 0) {
a[i] = -a[i]; /* violation */
}
}
/* ... */
free(a);
}
}
|
Compliant Solution
In this compliant example, the array elements a[n..2n]
are initialized to 0
when they are accessed in the for loop.
Code Block | ||||
---|---|---|---|---|
| ||||
void g(double *a, size_t n) {
a = (double *)calloc(a, (n * 2 + 1) * sizeof(double));
if (a != NULL) {
for (size_t i = 0; i != n * 2 + 1; ++i) {
if (a[i] < 0) {
a[i] = -a[i];
}
}
/* ... */
free(a);
}
} |
Noncompliant Code Example
In this noncompliant code example, the report_error()
function has been modified so that error_log
is properly initialized.
...
This solution is still problematic because a buffer overflow will occur if the null-terminated byte string referenced by msg
is greater than 17 bytes, including the NULL
terminator. The solution also makes use of a "magic number," which should be avoided. (See DCL06-C. Use meaningful symbolic constants to represent literal values.)
Compliant Solution
In this compliant solution, the magic number is abstracted, and the buffer overflow is eliminated.
Code Block | ||||
---|---|---|---|---|
| ||||
enum {max_buffer = 24}; void report_error(const char *msg) { const char *error_log = msg; char buffer[max_buffer]; snprintf(buffer, sizeof(buffer), "Error: %s", error_log); printf("%s\n", buffer); } |
Compliant Solution
A much simpler, less error prone, and better-performing compliant solution is shown here:
...