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 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 returnss
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 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> #define 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 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. (See FIO20-C. Avoid unintentional truncation when using fgets() or fgetws().)
#include <stdio.h> #include <string.h> #define 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 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 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Compass/ROSE |
|
| Could detect some violations of this rule. In particular, it could detect the noncompliant code example by searching for |
5.0 |
|
|
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
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 Standard | FIO37-CPP. Do not assume character data has been read |
MITRE CWE | CWE-119, Failure to constrain operations within the bounds of an allocated memory buffer CWE-241, Failure to handle wrong data type |
Bibliography
[ISO/IEC 9899:2011] | Subclause 7.21.7.2, "The |
[Lai 2006] | |
[Seacord 2013] | Chapter 2, "Strings" |