Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Wiki Markup
Local, automatic variables can assume _unexpected_ values if they are used before they are initialized. C99 specifies "If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate." \[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] In practice, this value defaults to whichever values are currently stored in stack memory. While unitialized memory often contains zero, this is not guaranteed. This can consequently cause a program to behave in an unpredictable or unplanned manner, and may provide an avenue for attack. Some compilers warn about unitialized variables, but these can be ignored by the programmer. As a result, it is necessary to guarantee that all local variables are initialized with a default value. The value assigned should be documented as the _default value_ for that variable in the comments associated with that variable's declaration.

Non-Compliant Code Example

...

In this example, two functions are called consecutively. The first function, func1(...) is passed an integer entered by a user. That integer is stored in variable: i for the duration of the function. The second function func2() declares a local integer variable: j. j is not initialized before being checked against a constant value, CONDITION_CHECK. Because j is uninitialized, it assumes whatever value is at that location in the stack, in this case the value of i from func1(). As a result, if the user entered 42, the condition statement if (j == CONDITION_CHECK) succeeds.

Code Block
#define CONDITION_CHECK  42
void func1(int arg) { int i = arg; }
void func2(void) {
  int j;

  if (j == CONDITION_CHECK) puts("Condition passed!!\n");
  else puts("ERROR: Condition failed\n");
}

...
func1(i);  /* the value of i originates from an untrusted source */
func2();
...

Compliant Solution

...

The local, automatic variable j should be initialized to a default value.

Code Block
#define CONDITION_CHECK  42
void func1(int arg) { int i = arg; }
void func2(void) {
  int j = 0;   /* initialize j to 0 */

  if (j == CONDITION_CHECK) puts("Condition passed!!\n");
  else puts("ERROR: Condition failed\n");
}

...
func1(i);  /* the value of i originates from an untrusted source */
func2();
...

...

Non-Compliant Code Example

...

In this example , user input is copied to the character array buf. The logit() function copies the user input to another buffer, buffer, and prints it to standard output. Next, the runit() routine is called. This routine declares an array of 50 characters, buf, and a character pointer, ptr. However, because ptr is not initialized it references data used by the last function called, in this case, the contents of the user controlled data copied in the logit() function. When the data referred to by ptr is copied to buf using an unbounded strcpy(), ptr is dereferenced and the data in that location is copied to buf. If the memory referred to by ptr contains more than 50 characters without a null character, a buffer overflow will occur.derived mercy 06, the programmer mistakely fails to set the local variable mesg to the msg argument in the log_error function. When the sprintf() call dereferences the mesg pointer, it actually dereferences the address that was supplied in the username buffer which in this case is the address of "password". The sprintf() call copies all of the data supplied in "password" until a NULL byte is reached. Because the 'password' buffer is larger than{{buffer}} a buffer overflow occurs.

Code Block

int do_auth
Code Block

#define BUF_SIZE 150

void runit(void) {
  char  char buf[50];username[MAX_USER], password[MAX_PASS];

  puts("Please enter char *ptryour username: ");

    memsetfgets(buf,0,50username, MAX_USER, stdin);
  puts("Please  strcpy(buf,ptr);
}

void logit(char *str) {
    char buffer[BUF_SIZE];

    strcpy(buffer, str);
    printf("The message: %s\nhas been logged\n",buffer);
}

int main(int argc, char *argv[]) {
    char buf[BUF_SIZEenter your password: ");
  fgets(password, MAX_PASS, stdin);

  if (!strcmp(username, "user") && !strcmp(password, "password")) {
    return 0;
  }
  return -1;
}

void log_error(int farray, char *msg) {  
  char *err, *mesg;
  char buffer[24];

  sprintf(buffer,  strcpy(buf, argv[1]"Error: %s", mesg);
    logit(bufprintf("%s\n", buffer);
    runit();
}

h2. Compliant Solution 2

The local variable ptr should be initialized to a default value, in this case NULL.

Code Block

#define BUF_SIZE 150

void runit
int main(void) {
  if  char buf[50];(do_auth() == -1) {
    char *ptr = NULL; /* initialize ptr to 0 */

    memset(buf,0,50);
    strcpy(buf,ptr);
}

void logit(char *str) {
    char buffer[BUF_SIZE];
    int i;
    for (i=0; i < BUF_SIZE; ++i) buffer[i] = '\0';

    strcpy(buffer, str);
    printf("The message: %s\nhas been logged\n",buffer);
}

int main(int argc, char *argv[]) {
    char buf[BUF_SIZE];

    strcpy(buf, argv[1])log_error(ERR_CRITIC | ERR_AUTH, "Unable to login");
  }
  return 0;
}

Compliant Solution

In the compliant solution, mesg is initialized to msg as shown below.

Code Block

void log_error(int farray, char *msg) {
  char *mesg = msg;
  char buffer[24];

  sprintf(buffer, "Error: %s", mesg);

  printf("%s\n", buffer);
}

This solution is compliant provided that the null-terminated byte string referenced by msg is 17 bytes or less including the null terminator. A much simpler, less error prone, and better performing solution is shown below:

Code Block

void log_error(char *msg) {
  printf("Error: %s\n", msg);
}

...
log_error("Unable to login");
...;
    logit(buf);
    runit();
}

Priority: P2 Level: L2

Uninitialized variables are relatively unlikely to result in an exploitable vulnerability because most compilers provide warnings when an uninitialized variable is referenced and most programmers take these warnings seriously.

Component

Value

Severity

1 (low)

Likelihood

1 (unlikely)

Remediation cost

2 (high)

References