Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Updated references from C11->C23

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 dataBuffer overflows occur frequently when manipulating strings [Seacord 2013b]. To prevent such errors, limit either 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)

Wiki Markup
This noncompliant code example demonstrates what is commonly referred to as an _off-by-one_ error \[[Dowd 06|AA. References#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}}. 

(See STR03-C. Do not inadvertently truncate a string.)

When strings live on the heap, this rule is a specific instance of MEM35-C. Allocate sufficient memory for an object. Because strings are represented as arrays of characters, this rule is related to both ARR30-C. Do not form or use out-of-bounds pointers or array subscripts and ARR38-C. Guarantee that library functions do not form invalid pointers.

Noncompliant Code Example (Off-by-One Error)

This noncompliant code example demonstrates an off-by-one error [Dowd 2006]. The loop copies data from src to dest. However, because the loop does not account for the null-termination character, it may be incorrectly written 1 byte past the end of dest.

Code Block
bgColor#FFCCCC
langc
#include <stddef.h>
 
void copy(size_t n, char src[n], char dest[n]) {
   
Code Block
bgColor#FFCCCC
char dest[ARRAY_SIZE];
char src[ARRAY_SIZE];
size_t i;
/* ... */
 
   for (i = 0; src[i] && (i < sizeof(destn)); i++i) {
     dest[i] = src[i];
   }
   dest[i] = '\0';
/* ... */}

Compliant Solution (

...

Off-by-

...

One Error)

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

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

Noncompliant Code Example (

...

gets(

...

))

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

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

Wiki Markup
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.

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
int main(int argc, char *argv[]) {
  /* ... */
  char prog_name[128];
  strcpy(prog_name, argv[0]);
  /* ... */
}

Compliant Solution (argv) (strcpy())

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:

Code Block
bgColor#ccccff
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).

Code Block
bgColor#ccccff
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.

Code Block
bgColor#ccccff
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.

Code Block
bgColor#ccccff
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.

Code Block
bgColor#FFcccc
/* ... */
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:

Code Block
bgColor#ccccff
/* ... */
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.

Code Block
bgColor#FFcccc
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.

Code Block
bgColor#ccccff
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.

Code Block
bgColor#ccccff
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

Wiki Markup
[CVE-2009-1252|http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2009-1252] results from a violation of this rule. The Network Time Protocol (NTPd), before versions 4.2.4p7 and 4.2.5p74, contained calls to sprintf that allow an attacker to execute arbitrary code by overflowing a character array \[[xorl 2009|http://xorl.wordpress.com/2009/06/10/freebsd-sa-0911-ntpd-remote-stack-based-buffer-overflows/]\].

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

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

Wiki Markup
\[[Dowd 06|AA. References#Dowd 06]\] Chapter 7, "Program Building Blocks" (Loop Constructs 327-336)
\[[ISO/IEC 9899:1999|AA. References#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|AA. References#ISO/IEC PDTR 24772]\] "CJM String Termination," "XYW Buffer Overflow in Stack", and "XYB Buffer Overflow in Heap"
\[[MITRE|AA. References#MITRE]\] [CWE ID 119|http://cwe.mitre.org/data/definitions/119.html], "Failure to Constrain Operations within the Bounds of an Allocated Memory Buffer"
\[MITRE\] [CWE ID 120|http://cwe.mitre.org/data/definitions/120.html], "Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')"
\[MITRE\] [CWE ID 193|http://cwe.mitre.org/data/definitions/193.html], "Off-by-one Error"
\[[Seacord 05a|AA. References#Seacord 05]\] Chapter 2, "Strings"
\[[xorl 2009|AA. References#xorl 2009]\] ["FreeBSD-SA-09:11: NTPd Remote Stack Based Buffer Overflows"|http://xorl.wordpress.com/2009/06/10/freebsd-sa-0911-ntpd-remote-stack-based-buffer-overflows/]

 function, which was deprecated in the C99 Technical Corrigendum 3 and removed from C11, is inherently unsafe and should never be used because it provides no way to control how much data is read into a buffer from stdin. This noncompliant code example assumes that gets() will not read more than BUFFER_SIZE - 1 characters from stdin. This is an invalid assumption, and the resulting operation can result in a buffer overflow.

The gets() function reads characters from the stdin into a destination array until end-of-file is encountered or a newline character is read. Any newline character is discarded, and a null character is written immediately after the last character read into the array.

Code Block
bgColor#FFCCCC
langc
#include <stdio.h>
 
#define BUFFER_SIZE 1024

void func(void) {
  char buf[BUFFER_SIZE];
  if (gets(buf) == NULL) {
    /* Handle error */
  }
}

See also MSC24-C. Do not use deprecated or obsolescent functions.

Compliant Solution (fgets())

The fgets() function reads, at most, one less than the specified number of characters from a stream into an array. This solution is compliant because the number of characters copied from stdin to buf cannot exceed the allocated memory:

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <string.h>
 
enum { BUFFERSIZE = 32 };
 
void func(void) {
  char buf[BUFFERSIZE];
  int ch;

  if (fgets(buf, sizeof(buf), stdin)) {
    /* fgets() succeeded; scan for newline character */
    char *p = strchr(buf, '\n');
    if (p) {
      *p = '\0';
    } else {
      /* Newline not found; flush stdin to end of line */
      while ((ch = getchar()) != '\n' && ch != EOF)
        ;
      if (ch == EOF && !feof(stdin) && !ferror(stdin)) {
          /* Character resembles EOF; handle error */ 
      }
    }
  } else {
    /* fgets() failed; handle error */
  }
}

The fgets() function is not a strict replacement for the gets() function because fgets() retains the newline character (if read) and may also return a partial line. It is possible to use fgets() to safely process input lines too long to store in the destination array, but this is not recommended for performance reasons. Consider using one of the following compliant solutions when replacing gets().

Compliant Solution (gets_s())

The gets_s() function reads, at most, one less than the number of characters specified from the stream pointed to by stdin into an array.

The C Standard, Annex K 3.5.4.1 paragraph 4 [ISO/IEC 9899:2024], states

No additional characters are read after a new-line character (which is discarded) or after end-of-file. The discarded new-line character does not count towards number of characters read. A null character is written immediately after the last character read into the array.

If end-of-file is encountered and no characters have been read into the destination array, or if a read error occurs during the operation, then the first character in the destination array is set to the null character and the other elements of the array take unspecified values:

Code Block
bgColor#ccccff
langc
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
 
enum { BUFFERSIZE = 32 };
 
void func(void) {
  char buf[BUFFERSIZE];

  if (gets_s(buf, sizeof(buf)) == NULL) {
    /* Handle error */
  }
}

Compliant Solution (getline(), POSIX)

The getline() function is similar to the fgets() function but can dynamically allocate memory for the input buffer. If passed a null pointer, getline() dynamically allocates a buffer of sufficient size to hold the input. If passed a pointer to dynamically allocated storage that is too small to hold the contents of the string, the getline() function resizes the buffer, using realloc(), rather than truncating the input. If successful, the getline() function returns the number of characters read, which can be used to determine if the input has any null characters before the newline. The getline() function works only with dynamically allocated buffers. Allocated memory must be explicitly deallocated by the caller to avoid memory leaks. (See MEM31-C. Free dynamically allocated memory when no longer needed.)

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
void func(void) {
  int ch;
  size_t buffer_size = 32;
  char *buffer = malloc(buffer_size);
 
  if (!buffer) {
    /* Handle error */
    return;
  }

  if ((ssize_t size = getline(&buffer, &buffer_size, stdin))
        == -1) {
    /* Handle error */
  } else {
    char *p = strchr(buffer, '\n');
    if (p) {
      *p = '\0';
    } else {
      /* Newline not found; flush stdin to end of line */
      while ((ch = getchar()) != '\n' && ch != EOF)
	    ;
	  if (ch == EOF && !feof(stdin) && !ferror(stdin)) {
         /* Character resembles EOF; handle error */
      }
    }
  }
  free (buffer);
}

Note that the getline() function uses an in-band error indicator, in violation of ERR02-C. Avoid in-band error indicators.

Noncompliant Code Example (getchar())

Reading one character at a time provides more flexibility in controlling behavior, though with additional performance overhead. This noncompliant code example uses the getchar() function to read one character at a time from stdin instead of reading the entire line at once. The stdin stream is read until end-of-file is encountered or a newline character is read. Any newline character is discarded, and a null character is written immediately after the last character read into the array. Similar to the noncompliant code example that invokes gets(), there are no guarantees that this code will not result in a buffer overflow.

Code Block
bgColor#FFCCCC
langc
#include <stdio.h>
 
enum { BUFFERSIZE = 32 };
 
void func(void) {
  char buf[BUFFERSIZE];
  char *p;
  int ch;
  p = buf;
  while ((ch = getchar()) != '\n' && ch != EOF) {
    *p++ = (char)ch;
  }
  *p++ = 0;
  if (ch == EOF) {
      /* Handle EOF or error */
  }
}

After the loop ends, if ch == EOF, the loop has read through to the end of the stream without encountering a newline character, or a read error occurred before the loop encountered a newline character. To conform to FIO34-C. Distinguish between characters read from a file and EOF or WEOF, the error-handling code must verify that an end-of-file or error has occurred by calling feof() or ferror().

Compliant Solution (getchar())

In this compliant solution, characters are no longer copied to buf once index == BUFFERSIZE - 1, leaving room to null-terminate the string. The loop continues to read characters until the end of the line, the end of the file, or an error is encountered. When truncated == true, the input string has been truncated.

Code Block
bgColor#ccccff
langc
#include <stdio.h>
 
enum { BUFFERSIZE = 32 };

void func(void) {
  char buf[BUFFERSIZE];
  int ch;
  size_t index = 0;
  bool truncated = false;

  while ((ch = getchar()) != '\n' && ch != EOF) {
    if (index < sizeof(buf) - 1) {
      buf[index++] = (char)ch;
    } else {
      truncated = true;
    }
  }
  buf[index] = '\0';  /* Terminate string */
  if (ch == EOF) {
    /* Handle EOF or error */
  }
  if (truncated) {
    /* Handle truncation */
  }
}

Noncompliant Code Example (fscanf())

In this noncompliant example, the call to fscanf() can result in a write outside the character array buf:

Code Block
bgColor#ffcccc
langc
#include <stdio.h>
 
enum { BUF_LENGTH = 1024 };
 
void get_data(void) {
  char buf[BUF_LENGTH];
  if (1 != fscanf(stdin, "%s", buf)) {
    /* Handle error */
  }

  /* Rest of function */
}

Compliant Solution (fscanf())

In this compliant solution, the call to fscanf() is constrained not to overflow buf:

Code Block
bgColor#ccccff
langc
#include <stdio.h>
 
enum { BUF_LENGTH = 1024 };
 
void get_data(void) {
  char buf[BUF_LENGTH];
  if (1 != fscanf(stdin, "%1023s", buf)) {
    /* Handle error */
  }

  /* Rest of function */
}

Noncompliant Code Example (argv)

In a hosted environment, 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[]) { /* ... */ }

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

Vulnerabilities can occur when inadequate space is allocated to copy a command-line argument or other program input. In this noncompliant code example, an attacker can manipulate the contents of argv[0] to cause a buffer overflow:

Code Block
bgColor#FFcccc
langc
#include <string.h>
 
int main(int argc, char *argv[]) {
  /* Ensure argv[0] is not null */
  const char *const name = (argc && argv[0]) ? argv[0] : "";
  char prog_name[128];
  strcpy(prog_name, name);
 
  return 0;
}

Compliant Solution (argv)

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.

Code Block
bgColor#ccccff
langc
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char *argv[]) {
  /* Ensure argv[0] is not null */
  const char *const name = (argc && argv[0]) ? argv[0] : "";
  char *prog_name = (char *)malloc(strlen(name) + 1);
  if (prog_name != NULL) {
    strcpy(prog_name, name);
  } else {
    /* Handle error */
  }
  free(prog_name);
  return 0;
}

Remember to add a byte to the destination string size to accommodate the null-termination character.

Compliant Solution (argv)

The strcpy_s() function provides additional safeguards, including accepting the size of the destination buffer as an additional argument. (See STR07-C. Use the bounds-checking interfaces for string manipulation.)

Code Block
bgColor#ccccff
langc
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char *argv[]) {
  /* Ensure argv[0] is not null */
  const char *const name = (argc && 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  error */
    }
  } else {
    /* Handle error */
  }
  /* ... */
  free(prog_name);
  return 0;
}

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)

If an argument will not 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. Care must be taken to avoid assuming that argv[0] is non-null.

Code Block
bgColor#ccccff
langc
int main(int argc, char *argv[]) {
  /* Ensure argv[0] is not null */
  const char * const prog_name = (argc && argv[0]) ? argv[0] : "";
  /* ... */
  return 0;
}

Noncompliant Code Example (getenv())

According to the C Standard, 7.22.4.6 [ISO/IEC 9899:2011]

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
#include <stdlib.h>
#include <string.h>
 
void func(void) {
  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 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
#include <stdlib.h>
#include <string.h>
 
void func(void) {
  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 error */
    }  
    memcpy(buff, editor, len);
    free(buff);
  }
}

Noncompliant Code Example (sprintf())

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

Code Block
bgColor#FFcccc
langc
#include <stdio.h>
 
void func(const char *name) {
  char filename[128];
  sprintf(filename, "%s.txt", name);
}

Because the sprintf() function makes no guarantees regarding the length of the generated string, a sufficiently long string in name could generate a buffer overflow.

Compliant Solution (sprintf())

The buffer overflow in the preceding noncompliant example can be prevented by adding a precision to the %s conversion specification. If the precision is specified, no more than that many bytes are written. The precision 123 in this compliant solution ensures that filename can contain the first 123 characters of name, the .txt extension, and the null terminator.

Code Block
bgColor#ccccff
langc
#include <stdio.h>
 
void func(const char *name) {
  char filename[128];
  sprintf(filename, "%.123s.txt", name);
}

You can also use * to indicate that the precision should be provided as a variadic argument:

Code Block
bgColor#ccccff
langc
#include <stdio.h>
 
void func(const char *name) {
  char filename[128];
  sprintf(filename, "%.*s.txt", sizeof(filename) - 5, name);
}

Compliant Solution (snprintf())

A more general solution is to use the snprintf() function, which also truncates name if it will not fit in the filename.

Code Block
bgColor#ccccff
langc
#include <stdio.h>
 
void func(const char *name) {
  char filename[128];
  int result = snprintf(filename, sizeof(filename), "%s.txt", name);
  if (result != strlen(filename) {
    /* truncation occurred */
  }
}

Risk Assessment

Copying string 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

Array access out of bounds, Buffer overflow from incorrect string format specifier, Destination buffer overflow in string manipulation, Invalid use of standard library string routine, Missing null in string array, Pointer access out of bounds, Tainted NULL or non-null-terminated string, Use of dangerous standard function 

Tool

Version

Checker

Description

Astrée
Include Page
Astrée_V
Astrée_V

Supported

Astrée reports all buffer overflows resulting from copying data to a buffer that is not large enough to hold that data.

Axivion Bauhaus Suite
Include Page
Axivion Bauhaus Suite_V
Axivion Bauhaus Suite_V
CertC-STR31

Detects calls to unsafe string function that may cause buffer overflow
Detects potential buffer overruns, including those caused by unsafe usage of fscanf()

CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

LANG.MEM.BO
LANG.MEM.TO
MISC.MEM.NTERM
BADFUNC.BO.*

Buffer overrun
Type overrun
No space for null terminator
A collection of warning classes that report uses of library functions prone to internal buffer overflows

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

Coverity
Include Page
Coverity_V
Coverity_V

STRING_OVERFLOW

BUFFER_SIZE

OVERRUN

STRING_SIZE

Fully implemented

Fortify SCA

5.0



Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C2840,  C5009, C5038

C++0145, C++5009, C++5038

DF2840, DF2841, DF2842, DF2843, DF2845, DF2846, DF2847, DF2848, DF2930, DF2931, DF2932, DF2933, DF2935, DF2936, DF2937, DF2938


Klocwork

Include Page
Klocwork_V
Klocwork_V

SV.FMT_STR.BAD_SCAN_FORMAT
SV.UNBOUND_STRING_INPUT.FUNC


LDRA tool suite

Include Page
LDRA_V
LDRA_V

489 S, 109 D, 66 X, 70 X, 71 X

Partially implemented

Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C-STR31-a
CERT_C-STR31-b
CERT_C-STR31-c
CERT_C-STR31-d
CERT_C-STR31-e

Avoid accessing arrays out of bounds
Avoid overflow when writing to a buffer
Prevent buffer overflows from tainted data
Avoid buffer write overflow from tainted data
Avoid using unsafe string functions which may cause buffer overflows

PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

421, 498

Partially supported

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule STR31-C

Checks for:

  • Use of dangerous standard function
  • Missing null in string array
  • Buffer overflow from incorrect string format specifier
  • Destination buffer overflow in string manipulation
  • Insufficient destination buffer size

Rule partially covered.

PVS-Studio

Include Page
PVS-Studio_V
PVS-Studio_V

V518, V645, V727, V755

Splint

Include Page
Splint_V
Splint_V



TrustInSoft Analyzer

Include Page
TrustInSoft Analyzer_V
TrustInSoft Analyzer_V

mem_access

Exhaustively verified (see one compliant and one non-compliant example).

Related Vulnerabilities

CVE-2009-1252 results from a violation of this rule. The Network Time Protocol daemon (NTPd), before versions 4.2.4p7 and 4.2.5p74, contained calls to sprintf that allow an attacker to execute arbitrary code by overflowing a character array [xorl 2009].

CVE-2009-0587 results from a violation of this rule. Before version 2.24.5, Evolution Data Server performed unchecked arithmetic operations on the length of a user-input string and used the value to allocate space for a new buffer. An attacker could thereby execute arbitrary code by inputting a long string, resulting in incorrect allocation and buffer overflow [xorl 2009].

CVE-2021-3156 results from a violation of this rule in versions of Sudo before 1.9.5p2. Due to inconsistencies on whether backslashes are escaped, vulnerable versions of Sudo enabled a user to create a heap-based buffer overflow and exploit it to execute arbitrary code. [BC].

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

Related Guidelines

Key here (explains table format and definitions)

Taxonomy

Taxonomy item

Relationship

CERT C Secure Coding StandardSTR03-C. Do not inadvertently truncate a stringPrior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding StandardSTR07-C. Use the bounds-checking interfaces for remediation of existing string manipulation code
MSC24-C. Do not use deprecated or obsolescent functions
MEM00-C. Allocate and free memory in the same module, at the same level of abstraction
FIO34-C. Distinguish between characters read from a file and EOF or WEOF
Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013String Termination [CJM]Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013Buffer Boundary Violation (Buffer Overflow) [HCB]Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013Unchecked Array Copying [XYW]Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013Using a tainted value to write to an object using a formatted input or output function [taintformatio]Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013Tainted strings are passed to a string copying function [taintstrcpy]Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11CWE-119, Improper Restriction of Operations within the Bounds of a Memory Buffer2017-05-18: CERT: Rule subset of CWE
CWE 2.11CWE-120, Buffer Copy without Checking Size of Input ("Classic Buffer Overflow")2017-05-15: CERT: Exact
CWE 2.11CWE-123, Write-what-where Condition2017-06-12: CERT: Partial overlap
CWE 2.11CWE-125, Out-of-bounds Read2017-05-18: CERT: Partial overlap
CWE 2.11CWE-676, Off-by-one Error2017-05-18: CERT: Partial overlap

CERT-CWE Mapping Notes

Key here for mapping notes

CWE-122 and STR31-C

STR31-C = Union( CWE-122, list) where list =

  • Buffer overflows on strings in the stack or data segment

CWE-125 and STR31-C

Independent( ARR30-C, ARR38-C, EXP39-C, INT30-C)

STR31-C = Subset( Union( ARR30-C, ARR38-C))

STR32-C = Subset( ARR38-C)

Intersection( STR31-C, CWE-125) =

  • Directly reading beyond the end of a string

STR31-C – CWE-125 =

  • Directly writing beyond the end of a string

CWE-125 – STR31-C =

  • Reading beyond a non-string array
  • Reading beyond a string using library functions

CWE-676 and STR31-C

  • Independent( ENV33-C, CON33-C, STR31-C, EXP33-C, MSC30-C, ERR34-C)
  • STR31-C implies that several C string copy functions, like strcpy() are dangerous.

Intersection( CWE-676, STR31-C) =

  • Buffer Overflow resulting from invocation of the following dangerous functions:
  • gets(), fscanf(), strcpy(), sprintf()

STR31-C – CWE-676 =

  • Buffer overflow that does not involve the dangerous functions listed above.

CWE-676 - STR31-C =

  • Invocation of other dangerous functions

CWE-121 and STR31-C

STR31-C = Union( CWE-121, list) where list =

  • Buffer overflows on strings in the heap or data segment

CWE-123 and STR31-C

Independent(ARR30-C, ARR38-C)

STR31-C = Subset( Union( ARR30-C, ARR38-C))

STR32-C = Subset( ARR38-C)

Intersection( CWE-123, STR31-C) =

  • Buffer overflow that overwrites a (unrelated) pointer with untrusted data

STR31-C – CWE-123 =

  • Buffer overflow that does not overwrite a (unrelated) pointer

CWE-123 – STR31-C =

  • Arbitrary writes that do not involve buffer overflows

CWE-119 and STR31-C

Independent( ARR30-C, ARR38-C, ARR32-C, INT30-C, INT31-C, EXP39-C, EXP33-C, FIO37-C)

STR31-C = Subset( Union( ARR30-C, ARR38-C))

STR32-C = Subset( ARR38-C)

CWE-119 = Union( STR31-C, list) where list =

  • Out-of-bounds reads or writes that are not created by string copy operations

CWE-193 and STR31-C

Intersection( CWE-193, STR31-C) = Ø

CWE-193 involves an integer computation error (typically off-by-one), which is often a precursor to (slight) buffer overflow. However the two errors occur in different operations and are thus unrelated.

Bibliography

[Dowd 2006]Chapter 7, "Program Building Blocks" ("Loop Constructs," pp. 327–336)
[Drepper 2006]Section 2.1.1, "Respecting Memory Bounds"
[ISO/IEC 9899:2024]K.3.5.4.1, "The gets_s Function"
[Lai 2006]
[NIST 2006]SAMATE Reference Dataset Test Case ID 000-000-088
[Seacord 2013b]Chapter 2, "Strings"
[xorl 2009]FreeBSD-SA-09:11: NTPd Remote Stack Based Buffer Overflows

[BC]

New Linux SUDO flaw lets local users gain root privileges


...

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