All standard library functions, including input/output functions and memory allocation functions, return either a valid value or a value of the correct return type that indicates an error (for example, −1 or a null pointer). Assuming that all calls to such functions will succeed and failing to check the return value for an indication of an error is a dangerous practice that may lead to unexpected or undefined behavior when an error occurs. It is essential that programs detect and appropriately handle all errors in accordance with an error-handling policy, as discussed in ERR00-C. Adopt and implement a consistent and comprehensive error-handling policy.
...
The successful completion or failure of each of the standard library functions listed in the following table below shall be determined either by comparing the function’s return value with the value listed in the column labeled “Error Return” or by calling one of the library functions mentioned in the footnotes to the same column.
Function | Successful Return | Error Return |
---|---|---|
| pointer Pointer to space |
|
|
|
|
|
|
|
|
|
|
| pointer Pointer to matching element |
|
| pointer Pointer to matching element |
|
| converted Converted wide character |
|
| number Number of bytes |
|
| number Number of bytes |
|
| pointer Pointer to space |
|
| processor Processor time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| character Character read |
|
|
|
|
| pointer Pointer to string |
|
| wide Wide character read |
|
| pointer Pointer to stream |
|
|
|
|
| number Number of characters (nonnegative) |
|
| number Number of characters (nonnegative) |
|
| character Character written |
|
|
|
|
|
|
|
| elements Elements readelements | Elements read |
| pointer Pointer to stream |
|
|
|
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
|
|
|
|
|
|
| file File position |
|
| number Number of wide characters (nonnegative) |
|
| number Number of wide characters (nonnegative) |
|
| elements Elements writtenelements | Elements written |
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| character Character read |
|
| character Character read |
|
| pointer Pointer to string |
|
| pointer Pointer to string |
|
| pointer Pointer to string |
|
| wide Wide character read |
|
| wide Wide character read |
|
| pointer Pointer to broken-down time |
|
| pointer Pointer to broken-down time |
|
| pointer Pointer to broken-down time |
|
| pointer Pointer to broken-down time |
|
| pointer Pointer to space |
|
| number Number of bytes |
|
| number Number of bytes or status |
|
| number Number of bytes or status |
|
| number Number of bytes or status |
|
| number Number of bytes or status |
|
| number Number of non-null elements |
|
|
|
|
| number Number of non-null elements |
|
|
|
|
| number Number of bytes |
|
| pointer Pointer to located character |
|
| calendar Calendar time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| number Number of characters (nonnegative) |
|
| character Character written |
|
| wide Wide character written |
|
|
|
|
| pointer Pointer to space |
|
|
|
|
|
|
|
| pointer Pointer to string |
|
|
|
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| pointer Pointer to previous function |
|
| number Number of characters that would be written (nonnegative) |
|
| number Number of characters that would be written (nonnegative) |
|
| number Number of non-null characters written |
|
| number Number of non-null characters written |
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| pointer Pointer to located character |
|
|
|
|
| number Number of non-null characters |
|
| pointer Pointer to located character |
|
| pointer Pointer to located character |
|
| pointer Pointer to located string |
|
| converted Converted value |
|
| converted Converted value |
|
| converted Converted value |
|
| pointer Pointer to first character of a token |
|
| pointer Pointer to first character of a token |
|
| converted Converted value |
|
| converted Converted value |
|
| converted Converted value |
|
| converted Converted value |
|
| converted Converted value |
|
| converted Converted value |
|
| length Length of transformed string |
|
| number Number of non-null wide characters |
|
| number Number of non-null wide characters |
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
|
|
|
|
|
|
|
|
|
|
|
|
| calendar Calendar time |
|
|
|
|
| pointer Pointer to stream |
|
|
|
|
| nonNon-null pointer |
|
|
|
|
|
|
|
| value Value of thread-specific storage |
|
|
|
|
| character Character pushed back |
|
| character Character pushed back |
|
| number Number of characters (nonnegative) |
|
| number Number of characters (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of wide characters (nonnegative) |
|
| number Number of wide characters (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of characters (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of characters that would be written (nonnegative) |
|
| number Number of characters that would be written (nonnegative) |
|
| number Number of non-null characters (nonnegative) |
|
| number Number of non-null characters (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of non-null wide characters |
|
| number Number of non-null wide characters |
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of wide characters (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of bytes stored |
|
| pointer Pointer to located wide character |
|
| number Number of non-null wide characters |
|
| pointer Pointer to located wide character |
|
| pointer Pointer to located wide character |
|
| number Number of non-null bytes |
|
|
|
|
| pointer Pointer to located wide string |
|
| converted Converted value |
|
| converted Converted value |
|
| converted Converted value |
|
| pointer Pointer to first wide character of a token |
|
| pointer Pointer to first wide character of a token |
|
| converted Converted value |
|
| converted Converted value |
|
| converted Converted value |
|
| number Number of non-null bytes |
|
|
|
|
| converted Converted value |
|
| converted Converted value |
|
| converted Converted value |
|
| length Length of transformed wide string |
|
| converted Converted character |
|
| number Number of bytes stored |
|
| number Number of bytes stored |
|
| valid Valid argument to |
|
| valid Valid argument to |
|
| pointer Pointer to located wide character |
|
| number Number of wide characters (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
| number Number of conversions (nonnegative) |
|
When FIO35-C. Use feof() and ferror() to detect end-of-file and file errors when sizeof(int) == sizeof(char) applies, callers shall determine the success or failure of the functions above in this table as follows:
by By callingAnchor use_ferror use_ferror ferror
by By callingAnchor use_feof_or_ferror use_feof_or_ferror ferror
andfeof
Anchor | ||||
---|---|---|---|---|
|
ungetc()
function does not set the error indicator even when it fails, so it is not possible to check for errors reliably unless it is known that the argument is not equal to EOF
. C99 The C Standard [ISO/IEC 9899:2011] states that "one character of pushback is guaranteed," so this should not be an issue if at most one character is ever pushed back before reading again. (See FIO13-C. Never push back anything other than one read character.)...
In this noncompliant example, the function utf8_to_ucs()
attempts to convert a sequence of UTF-8 characters to UCS. It first invokes setlocale()
to set the global locale to
but does not check for failure. "
en_US.UTF-8"setlocale()
will fail by returning a null pointer, for example, when the locale is not installed. (The function may fail for other reasons, as well, such as the lack of resources.) Depending on the sequence of characters pointed to by utf8
, the subsequent call to mbstowcs()
may fail or result in the function storing an unexpected sequence of wide characters in the supplied buffer ucs
.
...
Code Block | ||||
---|---|---|---|---|
| ||||
signal_info * start = malloc(num_of_records * sizeof(signal_info)); if (start == NULL) { /* Handle Allocationallocation Errorerror */ } signal_info * point = (signal_info *)start; point = start + temp_num - 1; memcpy(point->sig_desc, tmp2, strlen(tmp2)); /* ... */ |
...
In this noncompliant code example, input_string
is copied into dynamically allocated memory referenced by str
. However, the result of malloc()
is not checked before str
is referenced. Consequently, if malloc()
fails, the program has undefined behavior. (See undefined behavior 109behavior 109 in Annex J of the C Standard.) In practice, an abnormal termination of the process typically occurs, providing an opportunity for a denial-of-service attack. In some cases, it may be the source of other vulnerabilities, as well. (See the Related Vulnerabilities section.) See also MEM32-C. Detect and handle memory allocation errors.
...
In this noncompliant code example, the fseek()
function is used to set the file position to a location offset
in the file referred to by file
prior to reading a sequence of bytes from the file. However, if an I/O error occurs during the seek operation the subsequent read will read will fill the buffer with the wrong contents.
...
Compliant Solution (fseek()
)
According to the C standardStandard, the fseek()
function returns a non-zero nonzero value to indicate that an error occurred [ISO/IEC 9899:2011]. Testing for this condition before proceeding to read from the file eliminates the chance of operating on the wrong portion of the file if fseek()
failed. Always test the returned value to make sure an error did not occur before operating on the file. If an error does occur, handle it appropriately.
Code Block | ||||
---|---|---|---|---|
| ||||
size_t read_at(FILE *file, long offset, void *buf, size_t nbytes) {
if (fseek(file, offset, SEEK_SET) != 0) {
/* Indicate error to caller. */
return 0;
}
return fread(buf, 1, nbytes, file);
}
|
...
In the following noncompliant code example, snprinf()
is assumed to succeed. However, if the call fails (for example, because of insufficient memory, as described in GNU libc bug 441945), the subsequent call to log_message()
is likely to result in undefined behavior since the character buffer is not initialized and need not be NUL null-terminated.
Code Block | ||||
---|---|---|---|---|
| ||||
extern void log_message(const char*); void f(int i, int width, int prec) { char buf[40]; snprintf(buf, sizeof buf, "i = %*.*i", width, prec, i); log_message(buf); /* ... */ } |
...
A compliant solution avoids assuming that snprintf()
succeeds regardless of its arguments and tests the return value of the function before using the buffer the function was passed to format output into. In addition, a compliant solution takes care to NULnull-terminate the formatted string in case the provided buffer wasn't large enough for snprintf()
to append the terminating NULnull.
Code Block | ||||
---|---|---|---|---|
| ||||
extern void log_message(const char*); void f(int i, int width, int prec) { char buf[40]; int n; n = snprintf(buf, sizeof buf, "i = %*.*i", width, prec, i); if (n < 0) { /* Handle snprintf() error */ strcpy(buf, "unknown error"); } /* NULNull-terminate buffer in case of overflow */ buf[sizeof buf - 1] = '\0'; log_message(buf); } |
When the length of the formatted string is unbounded, it is best to invoke the function twice, once with a NULL
buffer to determine the size of output, then dynamically allocate a buffer of sufficient size, and finally call snprintf()
again to format the output into the dynamically allocated buffer. Even with this approach the success of all calls still needs to be tested, and any errors still need to be appropriately handled. A possible optimization is to first attempt to format the string into a reasonably small buffer allocated on the stack and only when the buffer turns out to be too small allocate one of a sufficient size dynamically. This approach is shown in the following compliant solution below.
Code Block | ||||
---|---|---|---|---|
| ||||
void f(int i, int width, int prec) { char buffer[20]; char *buf = buffer; int n = sizeof buffer; const char fmt[] = "i = %*.*i"; n = snprintf(buf, n, fmt, width, prec, i); if (n < 0) { /* Handle snprintf() error */ strcpy(buffer, "unknown error"); goto write_log; } if (n < sizeof buffer) goto write_log; buf = (char*)malloc(n + 1); if (NULL == buf) { /* Handle malloc() error */ strcpy(buffer, "unknown error"); goto write_log; } n = snprintf(buf, n, fmt, width, prec, i); if (n < 0) { /* Handle snprintf() error */ strcpy(buffer, "unknown error"); } write_log: log_message(buf); if (buf != buffer) free(buf); } |
...
Function | Successful Return | Error Return |
|
---|---|---|---|
| pointer to a |
|
|
| pointer to a |
|
|
| 0 | non-zero Nonzero #2 | unchanged #2 |
SettingAnchor 1 1 errno
is a POSIX [ISO/IEC 9945:2008] extension to the C standardStandard.
On error,Anchor 2 2 posix_memalign()
returns a value that corresponds to one of the constants defined in the<errno.h>
header. The function does not seterrno
. Theposix_memalign()
function is optional and is not required to be provided by conforming implementations.
...
In the following noncompliant code example, fmemopen()
and open_memstream()
are assumed to succeed. However, if the calls fails, the two file pointers in
and out
will out
will be NULL
and the program has undefined behavior.
Code Block | ||||
---|---|---|---|---|
| ||||
int main(int argc, char *argv[]) { FILE *out, *in; if (argc != 2) { /* Handle Errorerror */ } in = fmemopen(argv[1], strlen(argv[1]), "r"); /* violation */ /* Use in */ out = open_memstream(&ptr, &size); /* violation */ /* Use out */ } |
Compliant Solution (POSIX)
A compliant solution avoids assuming that fmemopen()
and open_memstream()
succeed regardless of its arguments and tests the return value of the function before using the file pointers in
and out
.
Code Block | ||||
---|---|---|---|---|
| ||||
int main(int argc, char *argv[]) { FILE *out, *in; if (argc != 2) { /* Handle Errorerror */ } in = fmemopen(argv[1], strlen(argv[1]), "r"); if (in == NULL){ /* Handle Errorerror */ } /* Use in */ out = open_memstream(&ptr, &size); if (out == NULL){ /* Handle Errorerror */ } /* Use out */ } |
Exceptions
...
Function | Successful Return | Error Return |
---|---|---|
| number Number of characters (nonnegative) |
|
| character Character written |
|
|
|
|
| wide Wide character written |
|
| number Number of characters (nonnegative) |
|
| number Number of wide characters (nonnegative) |
|
| number Number of wide characters (nonnegative) |
|
Risk Assessment
Failing to detect error conditions can lead to unpredictable results, including abnormal program termination and denial-of-service attacks or, in some situations, could even allow an attacker to run arbitrary code.
...
Tool | Version | Checker | Description | |||||||
---|---|---|---|---|---|---|---|---|---|---|
Compass/ROSE | Can detect violations of this recommendation when checking for violations of EXP12-C. Do not ignore values returned by functions and EXP34-C. Do not dereference null pointers. | |||||||||
| CHECKED_RETURN | Finds inconsistencies in how function call return values are handled. Coverity Prevent cannot discover all violations of this recommendation, so further verification is necessary. | ||||||||
Fortify SCA | 5.0Compass/ROSE |
| Can detect violations of this recommendation when checking for violations of EXP12-C. Do not ignore values returned by functions and EXP34-C. Do not dereference null pointers.. | |||||||
| 80 D | Partially implemented. | ||||||||
PRQA QA-C |
| 3200 | Partially implemented. | |||||||
Fortify SCA | V. 5.0 |
|
Related Coding Practices
Related Coding Practices
Coding Coding Practice | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
medium | unlikely | medium | P2 | L3 | |
low | probable | high | P2 | L3 | |
FLP32-C. Prevent or detect domain and range errors in math functions | medium | probable | medium | P8 | L2 |
high | likely | medium | P18 | L1 | |
medium | probable | high | P4 | L3 | |
FIO33-C. Detect and handle input output errors resulting in undefined behavior | high | probable | medium | P12 | L1 |
low | unlikely | medium | P2 | L3 | |
ERR00-C. Adopt and implement a consistent and comprehensive error-handling policy | medium | probable | high | P4 | L3 |
low | unlikely | high | P1 | L3 | |
medium | probable | high | P4 | L3 | |
API04-C. Provide a consistent and usable error-checking mechanism | medium | unlikely | medium | P2 | L3 |
low | likely | medium | P6 | L2 | |
low | likely | medium | P6 | L2 |
...
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
...
...
FIO04-CPP. Detect and handle input and output errors | |
ISO/IEC TR 17961 (Draft) | Failing to detect and handle standard library errors [liberr] |
MITRE CWE |
...
...
Unchecked return value |
...
...
...
Incorrect check of function return value |
...
...
...
-390, |
...
Detection of error condition without action |
...
...
...
-391, |
...
Unchecked error condition |
...
Bibliography
ISO/IEC 9899:2011 Sections 7.21.3, "Files," 7.21.4, "Operations on files," and 7.21.9, "File positioning functions"
[DHS 2006] | Handle All Errors Safely |
[Henricson 1997] | Recommendation 12.1, "Check for All Errors Reported from Functions" |
[ |
...
] | Section 7. |
...
21.7. |
...
10, " |
...
The ungetc Function" |
ISO/IEC TR 17961 (Draft) Failing to detect and handle standard library errors [liberr]
Bibliography
[DHS 2006]. Handle All Errors Safely.
[Henricson 1997] Recommendation 12.1, Check for all errors reported from functions