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
fgetsfunction returnssif 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" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
empty_test |
Do not assume that fgets() or fgetws() returns a nonempty string when successful. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
strlen_functions¶
strlen_functions : set[bauhaus.analysis.config.FunctionName] = {'strlen'}