CertC-FIO37

Do not assume that fgets() or fgetws() returns a nonempty string when successful

Required inputs: IR

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 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 C Standard [ ISO/IEC 9899:2011] says,

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 assume that the array contains a nonempty string because the data may contain null characters.

Noncompliant Code Example

This noncompliant code example attempts to remove the trailing newline ( \n) from an input line. The fgets() function is typically used to read a newline-terminated line of input from a stream. It takes a size parameter for the destination buffer and copies, at most, size - 1 characters from a stream to a character array.

#include <stdio.h>
#include <string.h>
 
enum { 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.

Compliant Solution

This compliant solution uses strchr() to replace the newline character in the string if it exists:

#include <stdio.h>
#include <string.h>
 
enum { 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

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

Rule Severity Likelihood Remediation Cost Priority Level
FIO37-C High Probable Medium P12 L1
Related Guidelines
Taxonomy Taxonomy item Relationship
CERT C Secure Coding Standard FIO14-C. Understand the difference between text mode and binary mode with file streams Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard FIO20-C. Avoid unintentional truncation when using fgets() or fgetws() Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-241, Improper Handling of Unexpected Data Type 2017-07-05: CERT: Rule subset of CWE
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"
Excerpt from SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition) and SEI CERT C Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/rules/input-output-fio/fio37-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

empty_test

Do not assume that fgets() or fgetws() returns a nonempty string when successful.

None

False

Options

strlen_functions

strlen_functions : set[bauhaus.analysis.config.FunctionName] = {'strlen'}

Functions which test for empty strings.