Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: ate STR35-C

...

Code Block
bgColor#ccccff
langc
enum { ARRAY_SIZE = 32 };
 
void func(void) {
  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 (gets())

The gets() 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 BUFSIZ - 1 characters from stdin. This is an invalid assumption, and the resulting operation can cause a buffer overflow. Note further that BUFSIZ is a macro integer constant, defined in stdio.h, representing a suggested argument to setvbuf() and not the maximum size of such an input buffer.

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>
 
void func(void) {
  char buf[BUFSIZ];
  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 a specified number of characters from a stream into an array. This solution is compliant because the number of bytes 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 succeeds; 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')
            && !feof(stdin)
            && !ferror(stdin))
        ;
    }
  } else {
    /* fgets failed; handle error */
  }
}

The fgets() function, however, is not a strict replacement for the gets() function because fgets() retains the newline character (if read) but 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(), Annex K)

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.

Annex K, subclause K.3.5.4.1, of the C Standard [ISO/IEC 9899:2011] 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__
#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 behaves similarly to fgets() but offers several extra features. First, if the input line is too long, it resizes the buffer, using realloc(), rather than truncating the input. Second, if successful, it returns the number of characters read, which is useful in determining whether the input has any null characters before the newline.

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')
	     && !feof(stdin)
	     && !ferror(stdin))
        ;
    }
  }
  free (buffer);
}

getline() works only with buffers allocated with malloc(). If passed a NULL pointer, getline() allocates a buffer of sufficient size to hold the input. As such, the user must explicitly free() the buffer later. Do not pass NULL to getline() because this is a violation of MEM00-C. Allocate and free memory in the same module, at the same level of abstraction.

Code Block
bgColor#ccccff
langc
#include <stdio.h>
 
enum { CHARS_TO_READ = 9 };
 
void func(void) {
  char buf[CHARS_TO_READ + 1];
  scanf("%9s", buf);
}

Noncompliant Code Example (getchar())

This noncompliant cod 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 previous example, 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')
         && !feof(stdin)
         && !ferror(stdin)) {
    *p++ = ch;
  }
  *p++ = 0;
}

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.

Code Block
bgColor#ccccff
langc
#include <stdio.h>
 
enum { BUFFERSIZE = 32 };
void func(void) {
  unsigned char buf[BUFFERSIZE];
  int ch;
  int index = 0;
  int chars_read = 0;
 
  while (((ch = getchar()) != '\n')
          && !feof(stdin)
          && !ferror(stderr)) {
    if (index < sizeof(buf) - 1) {
      buf[index++] = (unsigned char)ch;
    }
    chars_read++;
  }
  buf[index] = '\0';  /* Terminate NTBS */
  if (feof(stdin)) {
    /* Handle EOF */
  }
  if (ferror(stdin)) {
    /* Handle error */
  }
  if (chars_read > index) {
    /* Handle truncation */
  }
}

After the loop ends, if feof(stdin) != 0, the loop has read through to the end of the file without encountering a newline character. Similarly, if ferror(stdin) != 0, a read error occurred before the loop encountered a newline character, and if chars_read > index, the input string has been truncated. FIO34-C. Use int to capture the return value of character IO functions is also applied in this solution.

Reading one character at a time provides more flexibility in controlling behavior without additional performance overhead.

The following test for the while loop is normally sufficient:

Code Block
while (((ch = getchar()) != '\n') && ch != EOF)

See FIO35-C. Use feof() and ferror() to detect end-of-file and file errors when sizeof(int) == sizeof(char) for the case where feof() and ferror() must be used instead.

Noncompliant Code Example (argv)

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:

...

Tool

Version

Checker

Description

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

Coverity6.5

STRING_OVERFLOW

STRING_SIZE

SECURE_CODING

Fully Implemented

Fully implemented

Fully implemented

Fortify SCA

5.0

 

 

Klocwork

Include Page
Klocwork_V
Klocwork_VKlocwork_V

NNTS.TAINTED
SV.STRBO.GETS
SV.USAGERULES.UNBOUNDED_STRING_COPY

 

 

LDRA tool suite

Include Page
LDRA_V
LDRA_V

 

 

Splint

Include Page
Splint_V
Splint_V

 

 

PRQA QA-C
Include Page
PRQA_V
PRQA_V
warncall for 'gets'Partially implemented

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].

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

Related Guidelines

...

-193, Off-by-one error

Bibliography

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

...