You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 99 Next »

Copying data to a buffer that is not large enough to hold that data results in a buffer overflow. While not limited to null-terminated byte strings (NTBS), buffer overflows often occurs when manipulating NTBS data. To prevent such errors, limit copies either through truncation (see STR03-C. Do not inadvertently truncate a null-terminated byte string) or, preferably, ensure that the destination is of sufficient size to hold the character data to be copied and the null-termination character.

Noncompliant Code Example (off-by-one error)

This noncompliant code example demonstrates what is commonly referred to as an off-by-one error [[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

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';
/* ... */

Compliant Solution (off-by-one error)

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

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';
/* ... */

Noncompliant Code Example (argv) (strcpy())

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:

int main(int argc, char *argv[]) { /* ... */ }

Command-line arguments are passed to main() as pointers to null-terminated byte strings in the array members argv[0] through argv[argc-1]. If the value of argc is greater than zero, the string pointed to by argv[0] is, by convention, the program name. If the value of argc is greater than one, the strings referenced by argv[1] through argv[argc-1] are the actual program arguments.

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:

int main(int argc, char *argv[]) {
  /* ... */
  char prog_name[128];
  strcpy(prog_name, argv[0]);
  /* ... */
}

Compliant Solution (argv) (strcpy())

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:

int main(int argc, char *argv[]) {
  /* ... */
  char *prog_name = (char *)malloc(strlen(argv[0])+1);
  if (prog_name != NULL) {
    strcpy(prog_name, argv[0]);
  }
  else {
    /* Couldn't get the memory - recover */
  }
  /* ... */
}

Remember to add a byte to accommodate the null-terminated byte string.

Compliant Solution (argv) (strcpy_s())

The strcpy_s() function provides additional safeguards, including accepting the size of the destination buffer as an additional argument (see STR07-C. Use TR 24731 for remediation of existing string manipulation code).

int main(int argc, char *argv[]) {
  /* ... */
  char * prog_name;
  size_t prog_size;

  prog_size = strlen(argv[0])+1;
  prog_name = (char *)malloc(prog_size);

  if (prog_name != NULL) {
    if (strcpy_s(prog_name, prog_size, argv[0])) {
      /* Handle strcpy_s() error */
    }
  }
  else {

    /* Couldn't get the memory - recover */
  }
  /* ... */
}

The strcpy_s() function can be used to copy data to or from dynamically allocated memory or a statically allocated array. If insufficient space is available strcpy_s() returns an error.

Compliant Solution (argv) (memcpy())

The C standard memcpy() function provide a similar capability to strcpy_s(), but is universally available.

int main(int argc, char *argv[]) {
  /* ... */
  char *prog_name;
  size_t prog_size;

  prog_size = strlen(argv[0])+1;
  prog_name = (char *)malloc(prog_size);

  if (prog_name != NULL) {
    memcpy(prog_name, argv[0], prog_size);
  }
  else {
    /* Couldn't get the memory - recover */
  }
  /* ... */
}

The memcpy() function differs from strcpy_s() in that it never returns an error. The memcpy() function returns a pointer to the destination string (that is, its first argument). However, memcpy() does not validate that the destination pointer has enough space for the memory being copied, and cannot be used if the source and destination strings overlap.

Compliant Solution (argv)

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.

int main(int argc, char *argv[]) {
  /* ... */
  const char *progname = argv[0];
  size_t prog_size;
  /* ... */
}

Noncompliant Code Example (getenv())

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.

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

Compliant Solution (getenv())

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 used to allocate adequate dynamic memory:

/* ... */
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);
}
/* ... */

Noncompliant Code Example (sprintf())

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.

char* name; /* initialized externally */
char filename[128];
sprintf( filename, "%s.txt", name);
/* open filename * /

However, since the sprintf function makes no guarantees regarding the length of the string generated, a sufficiently-long string in name could generate a buffer overflow.

Compliant Solution (sprintf())

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.

char* name; /* initialized externally */
char filename[128];
sprintf( filename, "%.123s.txt", name);
/* open filename * /

Compliant Solution (snprintf())

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

char* name; /* initialized externally */
char filename[128];
snprintf( filename, sizeof( filename), "%s.txt", name);
/* open filename * /

Risk Assessment

Copying NTBS data to a buffer that is too small to hold that data results in a buffer overflow. Attackers can exploit this condition to execute arbitrary code with the permissions of the vulnerable process.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

STR31-C

high

likely

medium

P18

L1

Automated Detection

The LDRA tool suite Version 7.6.0 can detect violations of this rule.

Fortify SCA Version 5.0 can detect violations of this rule.

Splint Version 3.1.1 can detect violations of this rule.

Compass/ROSE can detect violations of the rule. However, it is unable to handle cases involving strcpy_s() or manual string copies such as the one in the first example.

Klocwork Version 8.0.4.16 can detect violations of this rule with the NNTS, SV.STRBO.BOUND_COPY, SV.STRBO.BOUND_SPRINTF, SV.STRBO.UNBOUND_COPY, SV.STRBO.UNBOUND_SPRINTF, and SV.USAGERULES.UNBOUNDED_STRING_COPY checkers.

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

1.) CVE-2009-1252 results from a violation of this rule: the Network Time Protocol (NTPd), before versions 4.2.4p7 and 4.2.5p74, contains calls to sprintf that allow execution of arbitrary code by overflowing a character array [[xorl 2009]].

Other Languages

This rule appears in the C++ Secure Coding Standard as STR31-CPP. Guarantee that storage for character arrays has sufficient space for character data and the null terminator.

References

[[Dowd 06]] Chapter 7, "Program Building Blocks" (Loop Constructs 327-336)
[[ISO/IEC 9899:1999]] Section 7.1.1, "Definitions of terms," Section 7.21, "String handling <string.h>," Section 5.1.2.2.1, "Program startup," and Section 7.20.4.5, "The getenv function"
[[ISO/IEC PDTR 24772]] "CJM String Termination," "XYW Buffer Overflow in Stack", and "XYB Buffer Overflow in Heap"
[[MITRE 07]] CWE ID 119, "Failure to Constrain Operations within the Bounds of an Allocated Memory Buffer," CWE ID 193, "Off-by-one Error"
[[Seacord 05a]] Chapter 2, "Strings"
[[xorl 2009]] FreeBSD-SA-09:11: NTPd Remote Stack Based Buffer Overflows


      07. Characters and Strings (STR)      STR32-C. Null-terminate byte strings as required

  • No labels