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 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 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 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
CodeSonar | 8.1p0 | (general) | CodeSonar considers the possibility that fgets() and fgetws() may return empty strings; warnings of various classes may be triggered depending on subsequent operations on those strings. For example, the "Noncompliant Code Example" cited above would trigger a Buffer Underrun warning. |
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++ Coding Standard | VOID FIO37-CPP. Do not assume character data has been read |
MITRE CWE | CWE-119, Improper Restriction of Operations within the Bounds of a Memory Buffer CWE-241, Improper Handling of Unexpected Data Type |
Bibliography
[ISO/IEC 9899:2011] | 7.21.7.2, "The |
[Lai 2006] | |
[Seacord 2013] | Chapter 2, "Strings" |