The fgets() and fgetws() functions are typically used to read a newline-terminated line of input from a stream. Both functions read at most one less than the number of narrow or wide characters specified by an argument n from a stream to a string. Truncation errors can occur if n - 1 is less than the number of characters appearing in the input string prior to the new-line narrow or wide character (which is retained) or after end-of-file. This can result in the accidental truncation of user input.
Noncompliant Code Example
This noncompliant code example copies the input string into a buffer, and assumes it captured all of the user's input.
#include <stdbool.h>
#include <stdio.h>
bool get_data(char *buffer, int size) {
if (fgets(buffer, size, stdin)) {
return true;
}
return false;
}
void func(void) {
char buf[8];
if (get_data(buf, sizeof(buf))) {
printf("The user input %s\n", buf);
} else {
printf("Error getting data from the user\n");
}
}
However, if the last character in buf is not a newline and the stream is not at the end-of-file marker, the buffer was too small to contain all of the data from the user. For example, because the buffer is only 8 characters in length, if the user input "Hello World\n", the buffer would contain "Hello W" terminated by a null character.
Compliant Solution
This compliant solution examines the end-of-file marker for the stream and the last character in the buffer to determine whether it is a newline or not. If it is the end of file, or the last character is a newline, then the buffer contains all of the user's input. However, if the last character is not at the end-of-file and not a newline then the user's input has been truncated.
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
bool get_data(char *buffer, int size) {
if (fgets(buffer, size, stdin)) {
size_t len = strlen(buffer);
return feof(stdin) || buffer[len] == '\n';
}
return false;
}
void func(void) {
char buf[8];
if (get_data(buf, sizeof(buf))) {
printf("The user input %s\n", buf);
} else {
printf("Error getting data from the user\n");
}
}
Risk Assessment
Incorrectly assuming a newline character is read by fgets() or fgetws() can result in data truncation.
Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
FIO20-C | medium | likely | medium | P12 | L1 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
| CERT C++ Secure Coding Standard | FIO36-CPP. Do not assume a new-line character is read when using fgets() |
Bibliography
| [Lai 2006] | |
| [Seacord 2013] | Chapter 2, "Strings" |