...
Function | Successful Return | Error Return |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 as follows:
...
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 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 addition to the C standard library functions above, POSIX defines the following functions designed specifically to dynamically allocate memory.
Function | Successful Return | Error Return |
|
---|---|---|---|
| pointer to a FILE object |
|
|
| pointer to a FILE object |
|
|
| 0 | non-zero #2 | unchanged #2 |
...
Anchor | ||||
---|---|---|---|---|
|
Noncompliant Code Example (setlocale()
)
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 "en_US.UTF-8"
but does not check for failure. setlocale()
will fail by
...
Noncompliant Code Example (setlocale()
)
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 "en_US.UTF-8"
but does not check for failure. 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 | ||||
---|---|---|---|---|
| ||||
void *p; void *q; size_t new_size= /* nonzero size */; q = realloc(p, new_size); if (q == NULL) { /* Handle error */ } else { p = q; } |
Noncompliant Code Example (fseek()
)
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.
Code Block | ||||
---|---|---|---|---|
| ||||
size_t read_at(FILE *file, long offset, void *buf, size_t nbytes) { fseek(file, offset, SEEK_SET); return fread(buf, 1, nbytes, file); } |
Compliant Solution (fseek()
)
According to the C standard, the fseek()
function returns a non-zero 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); } |
Noncompliant Code Example (snprintf()
)
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 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); /* ... */ } |
Compliant Solution (snprintf()
)
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 NUL-terminate the formatted string in case the provided buffer wasn't large enough for snprintf()
to append the terminating NUL.
...
Code Block | ||||
---|---|---|---|---|
| ||||
void f(int i, int width, int prec) { char buffer[20]; char *buf = buffer; int n = sizeof buffer; const 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); } |
...
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);
}
|
Note that this solution correctly handles memory allocation errors in accordance with MEM32-C. Detect and handle memory allocation errors and uses the goto
statement as suggested in MEM12-C. Consider using a Goto-Chain when leaving a function on error when using and releasing resources.
Implementation Details
POSIX
In addition to the C standard library functions above, following is an incomplete list of functions defined in POSIX that require error checking.
Function | Successful Return | Error Return |
|
---|---|---|---|
| pointer to a FILE object |
|
|
| pointer to a FILE object |
|
|
| 0 | non-zero #2 | unchanged #2 |
SettingAnchor 1 1 errno
is a POSIX [ISO/IEC 9945:2008] extension to the C standard.
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.
Noncompliant Code Example (POSIX
)
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 be NULL
and the program has undefined behavior.
Code Block | ||||
---|---|---|---|---|
| ||||
int main(int argc, char *argv[])
{
FILE *out, *in;
if (argc != 2) {
/* Handle Error */
}
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 Error */
}
in = fmemopen(argv[1], strlen(argv[1]), "r");
if (in == NULL){
/* Handle Error */
}
/* Use in */
out = open_memstream(&ptr, &size);
if (out == NULL){
/* Handle Error */
}
/* Use out */
} |
Exceptions
EXP12-EX1: The exception from EXP12-C. Do not ignore values returned by functions still applies. If the return value is inconsequential or if any errors can be safely ignored, such as for functions called because of their side effects, the function should be explicitly cast to void
to signify programmer intent. For an example of this exception, see "Compliant Solution (Remove Existing Destination File)" under "Portable Behavior" in FIO10-C. Take care when using the rename() function.
...
Function | Successful Return | Error Return |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
...