Versions Compared

Key

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

Errors can occur when incorrect assumptions are made about the type of data being read. These assumptions may be violated, for example, when binary data has been read from a file instead of text from a user's terminal . (See or the output of a process is piped to stdin (see FIO14-C. Understand the difference between text mode and binary mode with file streams). ) On some systems, it may also be possible to input a null byte (as well as other binary codes) from the keyboard.

Subclause 7.21.7.2 of the of the C Standard [ISO/IEC 9899:2011]  sayssays,

The fgets function returns s if successful. If end-of-file is encountered and no characters have been read into the array, the contents of the array remain unchanged and a null pointer is returned.

The wide-character function fgetws() has the same behavior. Therefore, if fgets() or fgetws() returns a non-null pointer, it is safe to assume that the array contains data. However, it is erroneous to asume assume that the array contains a nonempty string because the data may contain null characters.

...

Code Block
bgColor#FFCCCC
langc
#include <stdio.h>
#include <string.h>
 
#defineenum { BUFFER_SIZE = 1024 };

void func(void) {
  char buf[BUFFER_SIZE];

  if (fgets(buf, sizeof(buf), stdin) == NULL) {
    /* Handle error */
  }
  buf[strlen(buf) - 1] = '\0';
}

The strlen() function computes the length of a string by determining the number of characters that precede the terminating null character. A problem occurs if the first character read from the input by fgets() happens to be a null character. This may occur, for example, if a binary data file is read by the fgets() call [Lai 2006]. If the first character in buf is a null character, strlen(buf) returns 0, the expression strlen(buf) - 1 wraps around to a large positive value, and a write-outside-array-bounds error occurs.

...

This compliant solution uses strchr() to replace the newline character in the string if it exists. (See FIO20-C. Avoid unintentional truncation when using fgets() or fgetws().):

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <string.h>
 
#defineenum { BUFFER_SIZE = 1024 };

void func(void) {
  char buf[BUFFER_SIZE];
  char *p;

  if (fgets(buf, sizeof(buf), stdin)) {
    p = strchr(buf, '\n');
    if (p) {
      *p = '\0';
    }
  } else {
    /* Handle error */
  }
}

Risk Assessment

Assuming Incorrectly assuming that character data has been read can result in an out-of-bounds memory write or other flawed logic.

...

CERT C Secure Coding Standard FIO14-C. Understand the difference between text mode and binary mode with file streams
FIO20-C. Avoid unintentional truncation when using fgets() or fgetws()
CERT C++ Secure Coding StandardFIO37-CPP. Do not assume character data has been read
MITRE CWECWE-119, Failure to constrain operations Improper Restriction of Operations within the bounds of an allocated memory bufferBounds of a Memory Buffer
CWE-241, Failure to handle wrong data typeImproper Handling of Unexpected Data Type

Bibliography

[ISO/IEC 9899:2011]

Subclause 7.21.7.2, "The fgets Function"
Subclause 7.29.3.2, "The fgetws Function"

[Lai 2006] 
[Seacord 2013]Chapter 2, "Strings"

...