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.
Subclause 7.21.7.2 of the of the C Standard [ISO/IEC 9899:2011] sayssays,
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 asume assume that the array contains a nonempty string because the data may contain null characters.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h> #include <string.h> #defineenum { 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.
...
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().):
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h> #include <string.h> #defineenum { 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 Incorrectly assuming that character data has been read can result in an out-of-bounds memory write or other flawed logic.
...
| 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 Improper Restriction of Operations within the bounds of an allocated memory bufferBounds of a Memory Buffer CWE-241, Failure to handle wrong data typeImproper Handling of Unexpected Data Type |
Bibliography
| [ISO/IEC 9899:2011] | Subclause 7.21.7.2, "The |
| [Lai 2006] | |
| [Seacord 2013] | Chapter 2, "Strings" |
...