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.
C99, section Subclause 7.1923.7.2 , "The fgets() function", paragraph 3 says:of the C Standard paragraph 3 [ISO/IEC 9899:2024] 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. If a read error occurs during the operation, the members of the array have unspecified values and a null pointer is returned.
The wide-character function fgetws() has the same behavior. Therefore, if fgets() returns or fgetws() returns a non-null pointer, we can assume it actually filled it is safe to assume that the array with contains data. However, assuming that it filled it is erroneous to assume that the array with a non-empty null-terminated byte string (NTBS) is erroneous, contains a nonempty string because the data it placed in the array may contain null characters.
Noncompliant Code Example
This noncompliant code example attempts to remove the trailing new-line newline (\n) from an input line. The fgets() function is typically used to read a newnewline-line 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.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h> #include <string.h> enum { BUFFER_SIZE = 1024 }; void func(void) { char buf[BUFSIZ + 1BUFFER_SIZE]; if (fgets(buf, sizeof(buf), stdin) == NULL) { /* Handle error */ } buf[strlen(buf) - 1] = '\0'; } |
The {{Wiki Markup 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 06|AA. Bibliography#Lai 06]\2006]. If the first character in {{buf}} is a null character, {{strlen(buf)}} returns 0 and a 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 new-line newline character in the string , if it exists (see FIO36-C. Do not assume a new-line character is read when using fgets()).:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h> #include <string.h> enum { BUFFER_SIZE = 1024 }; void func(void) { char buf[BUFSIZ + 1BUFFER_SIZE]; char *p; if (fgets(buf, sizeof(buf), stdin)) { p = strchr(buf, '\n'); if (p) { *p = '\0'; } } else { /* Handle error condition */ } } |
Risk Assessment
Assuming Incorrectly assuming that character data has been read can result in an out-of-bounds memory write or other flawed logic.
Rule | Severity | Likelihood |
|---|
Detectable | Repairable | Priority | Level |
|---|---|---|---|
FIO37-C |
High | Probable |
Yes |
Yes |
P18 | L1 |
Automated Detection
Tool |
|---|
Fortify SCA Version 5.0 can detect violations of this rule.
...
Version | Checker | Description | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Astrée |
| Supported: Astrée reports defects due to returned (empty) strings. | ||||||||
| Axivion Bauhaus Suite |
| CertC-FIO37 | ||||||||
| CodeSonar |
| (general) | 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 |
...
|
...
, which could be |
...
−1. The crux of this rule is that a string returned by |
...
they would need to |
...
be enumerated before ROSE could detect them.) | ||||||||||
| Cppcheck Premium |
| premium-cert-fio37-c | ||||||||
| Helix QAC |
| DF4911, DF4912, DF4913 | ||||||||
| Klocwork |
| CERT.FIO.FGETS | ||||||||
| LDRA tool suite |
| 44 S | Enhanced enforcement | |||||||
| Parasoft C/C++test |
| CERT_C-FIO37-a | Avoid accessing arrays out of bounds | |||||||
| Polyspace Bug Finder |
| CERT C: Rule FIO37-C | Checks for use of indeterminate string (rule fully covered) |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Other Languages
This rule appears in the C++ Secure Coding Standard as FIO37-CPP. Do not assume character data has been read.
References
| Wiki Markup |
|---|
\[[ISO/IEC 9899:1999|AA. Bibliography#ISO/IEC 9899-1999]\] Section 7.19.7.2, "The {{fgets}} function"
\[[Lai 06|AA. Bibliography#Lai 06]\]
\[[MITRE 07|AA. Bibliography#MITRE 07]\] [CWE ID 119|http://cwe.mitre.org/data/definitions/119.html], "Failure to Constrain Operations within the Bounds of an Allocated Memory Buffer," [CWE ID 241|http://cwe.mitre.org/data/definitions/241.html], and "Failure to Handle Wrong Data Type"
\[[Seacord 05a|AA. Bibliography#Seacord 05]\] Chapter 2, "Strings" |
Related Guidelines
Key here (explains table format and definitions)
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 |
CERT-CWE Mapping Notes
Key here for mapping notes
CWE-241 and FIO37-C
CWE-241 = Union( FIO37-C, list) where list =
- Improper handling of unexpected data type that does not come from the fgets() function.
Bibliography
| [ISO/IEC 9899:2024] | Subclause 7.23.7.2, "The |
| [Lai 2006] | |
| [Seacord 2013] | Chapter 2, "Strings" |
...
FIO36-C. Do not assume a new-line character is read when using fgets() 09. Input Output (FIO) FIO38-C. Do not use a copy of a FILE object for input and output