...
The successful completion or failure of each of the standard library functions listed in the following table 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.
Standard Library Functions
Function | Successful Return | Error Return |
---|---|---|
| Pointer to space |
|
|
| Nonzero |
|
| Nonzero |
|
| Nonzero |
| Pointer to matching element |
|
| Pointer to matching element |
|
| Converted wide character |
|
| Number of bytes |
|
| Number of bytes |
|
| Pointer to space |
|
| Processor time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Nonzero |
|
|
|
|
|
|
| Character read |
|
|
| Nonzero, |
| Pointer to string |
|
| Wide character read |
|
| Pointer to stream |
|
|
| Nonzero |
| Number of characters (nonnegative) | Negative |
| Number of characters (nonnegative) | Negative |
| Character written |
|
| Nonnegative |
|
fputwc() | Wide character written | WEOF |
| Nonnegative |
|
| Elements read | Elements read |
| Pointer to stream |
|
|
| Nonzero |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
|
| Nonzero |
|
| Nonzero, |
| File position |
|
| Number of wide characters (nonnegative) | Negative |
| Number of wide characters (nonnegative) | Negative |
| Elements written | Elements written |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Character read |
|
| Character read |
|
| Pointer to string |
|
| Pointer to string |
|
| Pointer to string |
|
| Wide character read |
|
| Wide character read |
|
| Pointer to broken-down time |
|
| Pointer to broken-down time |
|
| Pointer to broken-down time |
|
| Pointer to broken-down time |
|
| Pointer to space |
|
| Number of bytes |
|
| Number of bytes or status |
|
| Number of bytes or status |
|
| Number of bytes or status |
|
| Number of bytes or status |
|
| Number of non-null elements |
|
|
| Nonzero |
| Number of non-null elements |
|
|
| Nonzero |
| Number of bytes |
|
| Pointer to located character |
|
| Calendar time |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Number of characters (nonnegative) | Negative |
| Character written |
|
| Wide character written |
|
|
| Nonzero |
| Pointer to space |
|
|
| Nonzero |
|
| Nonzero |
| Pointer to string |
|
|
| Nonzero |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Pointer to previous function |
|
| Number of characters that would be written (nonnegative) | Negative |
| Number of characters that would be written (nonnegative) | Negative |
| Number of non-null characters written | Negative |
| Number of non-null characters written | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Pointer to located character |
|
|
| Nonzero |
| Number of non-null characters |
|
| Pointer to located character |
|
| Pointer to located character |
|
| Pointer to located string |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Pointer to first character of a token |
|
| Pointer to first character of a token |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Length of transformed string |
|
| Number of non-null wide characters | Negative |
| Number of non-null wide characters | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
|
|
|
|
|
|
|
|
|
|
|
|
| Calendar time |
|
| Base |
|
| Pointer to stream |
|
|
| Nonzero |
| Non-null pointer |
|
|
| Nonzero |
|
|
|
| Value of thread-specific storage |
|
|
|
|
| Character pushed back |
|
| Character pushed back |
|
| Number of characters (nonnegative) | Negative |
| Number of characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of wide characters (nonnegative) | Negative |
| Number of wide characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of characters that would be written (nonnegative) | Negative |
| Number of characters that would be written (nonnegative) | Negative |
| Number of non-null characters (nonnegative) | Negative |
| Number of non-null characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of non-null wide characters | Negative |
| Number of non-null wide characters | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of wide characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
| Number of bytes stored |
|
| Pointer to located wide character |
|
| Number of non-null wide characters |
|
| Pointer to located wide character |
|
| Pointer to located wide character |
|
| Number of non-null bytes |
|
|
| Nonzero |
| Pointer to located wide string |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Pointer to first wide character of a token |
|
| Pointer to first wide character of a token |
|
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Number of non-null bytes |
|
|
| Nonzero |
| Converted value |
|
| Converted value |
|
| Converted value |
|
| Length of transformed wide string |
|
| Converted character |
|
| Number of bytes stored |
|
| Number of bytes stored |
|
| Valid argument to |
|
| Valid argument to |
|
| Pointer to located wide character |
|
| Number of wide characters (nonnegative) | Negative |
| Number of conversions (nonnegative) |
|
| Number of conversions (nonnegative) |
|
When Note: According to 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 should verify end-of-file and file errors for the functions in this table as follows:
...
The 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
. 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 see FIO13-C. Never push back anything other than one read character).)
Noncompliant Code Example (setlocale()
)
In this noncompliant code example, the function utf8_to_wcs()
attempts to convert a sequence of UTF-8 characters to wide characters. It first invokes setlocale()
to set the global locale to the implementation-defined
but does not check for failure. The "
en_US.UTF-8"setlocale()
function 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 wcs
.
...
In this noncompliant code example, temp_num
, tmp2
, and num_of_records
are derived from a tainted source. Consequently, an attacker can easily cause calloc()
to fail by providing a large value for num_of_records
.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> #include <string.h> enum { SIG_DESC_SIZE = 32 }; typedef struct { char sig_desc[SIG_DESC_SIZE]; } signal_info; void func(size_t num_of_records, size_t temp_num, const char *tmp2) { signal_info *start = (signal_info *)calloc(num_of_records, sizeof(signal_info)); signal_info *point = start + temp_num - 1; if (tmp2 == NULL) { /* Handle error */ } memcpy(point->sig_desc, tmp2, SIG_DESC_SIZE); point->sig_desc[ SIG_DESC_SIZE - 1] = '\0'; /* ... */ } |
When calloc()
fails, it returns a null pointer that is assigned to start
. If start
is null, an attacker can provide a value for temp_num
that, when scaled by the the sizeof(signal_info)
, references a writable address to which control is eventually transferred. The contents of the string referenced by tmp2
can then be used to overwrite the address, resulting in an arbitrary code execution vulnerability.
Compliant Solution (calloc()
)
To correct this error, ensure the pointer returned by calloc()
is not null.:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> #include <string.h> enum { SIG_DESC_SIZE = 32 }; typedef struct { char sig_desc[SIG_DESC_SIZE]; } signal_info; void func(size_t num_of_records, size_t temp_num, const char *tmp2) { signal_info *point; signal_info *start = (signal_info *)calloc(num_of_records, sizeof(signal_info)); if (start == NULL) { /* Handle allocation error */ } else if (tmp2 == NULL) { /* Handle error */ } point = start + temp_num - 1; memcpy(point->sig_desc, tmp2, SIG_DESC_SIZE); point->sig_desc[ SIG_DESC_SIZE - 1] = '\0'; /* ... */ } |
...
In this compliant solution, the result of realloc()
is assigned to the temporary pointer q
and validated before it is assigned to the original pointer p
. :
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> void *p; void func(size_t new_size) { void *q; if (new_size == 0) { /* Handle error */ } q = realloc(p, new_size); if (q == NULL) { /* Handle error */ } else { p = q; } } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> size_t read_at(FILE *file, long offset, void *buf, void *buf, size_t nbytes) { fseek(file, offset, SEEK_SET); return fread(buf, 1, nbytes, file); } |
...
According to the C Standard, the fseek()
function returns a nonzero value to indicate that an error occurred. Test This compliant solution tests for this condition before reading from a file to eliminate the chance of operating on the wrong portion of the file if fseek()
fails. 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 | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> size_t read_at(FILE *file, long offset, void *buf, 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 this noncompliant code example, snprintf()
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 has undefined behavior because the character buffer is uninitialized and need not be null-terminated.
...
This compliant solution does not assume that snprintf()
will succeed regardless of its arguments and . It tests the return value of the function before of snprintf()
before subsequently using the buffer the function was passed to format output intoformatted buffer. This compliant solution also treats the case where the static buffer is not large enough for snprintf()
to append the terminating null character as an error.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <string.h> 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 || n >= sizeof(buf)) { /* Handle snprintf() error */ strcpy(buf, "unknown error"); } log_message(buf); } |
Compliant Solution (snprintf(
null)
)
When If unknown, the length of the formatted string is unbounded, you can first invoke can be discovered by invoking snprintf()
with a null buffer pointer to determine the size of required for the output, then dynamically allocate allocating a buffer of sufficient size, and finally call calling snprintf()
again 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 must 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, dynamically allocate one of a sufficient size dynamically:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> #include <stdlib.h> #include <string.h> extern void log_message(const char *); 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); } } |
...
ERR33-EX1: It is acceptable to ignore the return value of a function that cannot fail, or a function whose return value is inconsequential, or if an error condition need not be diagnosed. The function's results should be explicitly cast to void
to signify programmer intent. Return values from the functions in the following functions table do not need to be checked because their historical use has overwhelmingly omitted error checking, and the consequences are not relevant to security.
Functions the Return Values of Which Need Not Be Checked
Function | Successful Return | Error Return |
---|---|---|
| Character written |
|
| Wide character written |
|
| Nonnegative |
|
| Number of characters (nonnegative) | Negative |
| Number of wide characters (nonnegative) | Negative |
kill_dependency() | The input parameter | NA |
memcpy() , wmemcpy() | The destination input parameter | NA |
memmove() , wmemmove() | The destination input parameter | NA |
strcpy() , wcscpy() | The destination input parameter | NA |
strncpy() , wcsncpy() | The destination input parameter | NA |
strcat() , wcscat() | The destination input parameter | NA |
strncat() , wcsncat() | The destination input parameter | NA |
memset() , wmemset() | The destination input parameter | NA |
...
The vulnerability in Adobe Flash [VU#159523] arises because Flash neglects to check the return value from calloc()
. Even when calloc()
returns NULL
returns a null pointer, Flash writes to an offset from the return value. Dereferencing NULL
usually Dereferencing a null pointer usually results in a program crash, but dereferencing an offset from NULL
allows from a null pointer allows an exploit to succeed without crashing the program.
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
...
CERT C Secure Coding Standard | ERR00-C. Adopt and implement a consistent and comprehensive error-handling policy |
CERT C++ Secure Coding Standard | ERR10-CPP. Check for error conditions FIO04-CPP. Detect and handle input and output errors |
ISO/IEC TS 17961:2013 | Failing to detect and handle standard library errors [liberr] |
MITRE CWE | CWE-252, Unchecked return valueReturn Value CWE-253, Incorrect check of function return valueCheck of Function Return Value CWE-390, Detection of error condition Error Condition without actionAction CWE-391, Unchecked error conditionError Condition |
Bibliography
[DHS 2006] | Handle All Errors Safely |
[Henricson 1997] | Recommendation 12.1, "Check for All Errors Reported from Functions" |
[ISO/IEC 9899:2011] | Subclause 7.21.7.10, "The ungetc Function" |
[VU#159523] |
...