Versions Compared

Key

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

...

Wiki Markup
This noncompliant code example demonstrates what is commonly referred to as an _off-by-one_ error \[[Dowd 2006|AA. Bibliography#Dowd 06]\]. The loop copies data from {{src}} to {{dest}}. However, the null terminator may incorrectly be written one byte past the end of {{dest}} because the loop does not account for the null-termination character that must be appended to {{dest}}.

Code Block
bgColor#FFCCCC
langc
char dest[ARRAY_SIZE];
char src[ARRAY_SIZE];
size_t i;
/* ... */
for (i=0; src[i] && (i < sizeof(dest)); i++) {
  dest[i] = src[i];
}
dest[i] = '\0';
/* ... */

...

To correct this example, the loop termination condition must be modified to account for the null-termination character that is appended to dest.

Code Block
bgColor#ccccff
langc
char dest[ARRAY_SIZE];
char src[ARRAY_SIZE];
size_t i;
/* ... */
for (i=0; src[i] && (i < sizeof(dest)-1); i++) {
  dest[i] = src[i];
}
dest[i] = '\0';
/* ... */

...

Arguments read from the command line are stored in process memory. The function main(), called at program startup, is typically declared as follows when the program accepts command-line arguments:

Code Block
bgColor#FFcccc
langc
int main(int argc, char *argv[]) { /* ... */ }

...

Wiki Markup
The parameters {{argc}} and {{argv}} and the strings pointed to by the {{argv}} array are not modifiable by the program and retain their last-stored values between program startup and program termination.  This requires that a copy of these parameters be made before the strings can be modified. Vulnerabilities can occur when inadequate space is allocated to copy a command-line argument. In this noncompliant code example, the contents of {{argv\[0\]}} can be manipulated by an attacker to cause a buffer overflow:

Code Block
bgColor#FFcccc
langc
int main(int argc, char *argv[]) {
  /* ... */
  char prog_name[128];
  strcpy(prog_name, argv[0]);
  /* ... */
}

...

Wiki Markup
The {{strlen()}} function can be used to determine the length of the strings referenced by {{argv\[0\]}} through {{argv\[argc-1\]}} so that adequate memory can be dynamically allocated. Note that care must be taken to avoid assuming that {{argv\[0\]}} is non-null.

Code Block
bgColor#ccccff
langc
int main(int argc, char *argv[]) {
  /* Be prepared for argv[0] to be null */
  const char* const name = argv[0] ? argv[0] : "";
  char *prog_name = (char *)malloc(strlen(name) + 1);
  if (prog_name != NULL) {
    strcpy(prog_name, name);
  }
  else {
    /* Failed to allocate memory - recover */
  }
  /* ... */
}

...

Wiki Markup
The {{strcpy_s()}} function provides additional safeguards, including accepting the size of the destination buffer as an additional argument. (See recommendation [STR07-C. Use TR 24731 for remediation of existing string manipulation code].) Do not assume that {{argv\[0\]}} is non-null.

Code Block
bgColor#ccccff
langc
int main(int argc, char *argv[]) {
  /* Be prepared for argv[0] to be null */
  const char* const name = argv[0] ? argv[0] : "";

  char * prog_name;
  size_t prog_size;

  prog_size = strlen(name) + 1;
  prog_name = (char *)malloc(prog_size);

  if (prog_name != NULL) {
    if (strcpy_s(prog_name, prog_size, name)) {
      /* Handle strcpy_s() error */
    }
  }
  else {

    /* Failed to allocate memory - recover */
  }
  /* ... */
}

...

Wiki Markup
The C standard {{memcpy()}} function provide a similar capability to {{strcpy_s()}}, but is universally available. Note that care must be taken to avoid assuming that {{argv\[0\]}} is non-null. Note also that {{memcpy}} must not be called with a null pointer even when the second (size) argument is zero.

Code Block
bgColor#ccccff
langc
int main(int argc, char *argv[]) {
  /* Be prepared for argv[0] to be null */
  const char* const name = argv[0] ? argv[0] : "";

  char *prog_name;
  size_t prog_size;

  prog_size = strlen(name) + 1;
  prog_name = (char *)malloc(prog_size);

  if (prog_name != NULL) {
    memcpy(prog_name, name, prog_size);
  }
  else {
    /* Failed to allocate memory - recover */
  }
  /* ... */
}

...

Wiki Markup
If an argument is not going to be modified or concatenated, there is no reason to make a copy of the string. Not copying a string is the best way to prevent a buffer overflow, and is also the most efficient solution. Note that care must be taken to avoid assuming that {{argv\[0\]}} is non-null.

Code Block
bgColor#ccccff
langc
int main(int argc, char *argv[]) {
  /* Be prepared for argv[0] to be null */
  const char *prog_name = argv[0] ? argv[0] : "";
  size_t prog_size;
  /* ... */
}

...

The getenv() function searches an environment list, provided by the host environment, for a string that matches the string pointed to by name. The set of environment names and the method for altering the environment list are implementation-defined. Environment variables can be arbitrarily large, and copying them into fixed-length arrays without first determining the size and allocating adequate storage can result in a buffer overflow.

Code Block
bgColor#FFcccc
langc
/* ... */
char buff[256];
char *editor = getenv("EDITOR");
if (editor == NULL) {
  /* EDITOR environment variable not set */
} else {
  strcpy(buff, editor);
}
/* ... */

...

Environmental variables are loaded into process memory when the program is loaded. As a result, the length of these null-terminated byte strings can be determined by calling the strlen() function, and the resulting length can be used to allocate adequate dynamic memory:

Code Block
bgColor#ccccff
langc
/* ... */
char *buff;
char *editor = getenv("EDITOR");
if (editor == NULL) {
  /* EDITOR environment variable not set */
} else {
  size_t len = strlen(editor)+1;
  buff = (char *)malloc(len);
  if (buff == NULL) {
    /* Handle malloc() error */
  }
  memcpy(buff, editor, len);
}
/* ... */

...

In this example, name refers to an external string; it could have originated from user input, or from the file system, or from the network. The program constructs a filename from the string in preparation for opening the file.

Code Block
bgColor#FFcccc
langc
char* name; /* initialized externally */
char filename[128];
sprintf( filename, "%s.txt", name);
/* open filename * /

...

The buffer overflow can be prevented by providing a precision length to the %s specifier. The value 123 ensures that filename can contain the first 123 characters of name, the ".txt" extension, and the null terminator.

Code Block
bgColor#ccccff
langc
char* name; /* initialized externally */
char filename[128];
sprintf( filename, "%.123s.txt", name);
/* open filename * /

...

A more general solution is to use the snprintf() function.

Code Block
bgColor#ccccff
langc
char* name; /* initialized externally */
char filename[128];
snprintf( filename, sizeof( filename), "%s.txt", name);
/* open filename * /

...