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 */
}
}
|