CertC-POS54¶
Detect and handle POSIX library errors
Required inputs: IR
All standard library functions, including I/O 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. In addition to the C standard library functions mentioned in ERR33-C. Detect and handle standard library errors, the following functions defined in POSIX require error checking (list is not all-inclusive).
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.
| Function | Successful Return | Error Return | errno |
|---|---|---|---|
fmemopen() |
Pointer to a
FILE object
|
NULL |
ENOMEM |
open_memstream() |
Pointer to a
FILE object
|
NULL |
ENOMEM |
posix_memalign() |
0 |
Nonzero | Unchanged |
Setting
errno is a POSIX [
ISO/IEC
9945:2008] extension to the C Standard. On error,
posix_memalign() returns a value that corresponds to one of
the constants defined in the
<errno.h> header. The function does not set
errno. The
posix_memalign() function is optional and is not required to
be provided by POSIX-conforming implementations.
Noncompliant Code Example (POSIX)
In this noncompliant code example,
fmemopen() and
open_memstream() are assumed to succeed. However, if
the calls fail, the two file pointers
in and
out will be null and the program will have
undefined
behavior.
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
FILE *out;
FILE *in;
size_t size;
char *ptr;
if (argc != 2) {
/* Handle error */
}
in = fmemopen(argv[1], strlen(argv[1]), "r");
/* Use in */
out = open_memstream(&ptr, &size);
/* Use out */
return 0;
}
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:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
FILE *out;
FILE *in;
size_t size;
char *ptr;
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 */
return 0;
}
Exceptions
ERR33-C-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.
ERR33-C-EX2: Ignore the return value of a function that cannot
fail or whose return value cannot signify that an error condition need not be
diagnosed. For example,
strcpy() is one such function.
Return values from the following functions do not need to be checked because their historical use has overwhelmingly omitted error checking, and the consequences are not relevant to security.
| Function | Successful Return | Error Return |
|---|---|---|
printf() |
Number of characters (nonnegative) | Negative |
putchar() |
Character written | EOF |
puts() |
Nonnegative | EOF (negative) |
putwchar() |
Wide character written | WEOF |
vprintf() |
Number of characters (nonnegative) | Negative |
vwprintf() |
Number of wide characters (nonnegative) | Negative |
wprintf() |
Number of wide characters (nonnegative) | Negative |
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.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| POS54-C | High | Likely | Medium | P18 | L1 |
Related Guidelines
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"
|
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
discarded_return_with_entity |
Return value of function discarded. |
None |
False |
handle_posix_error |
Detect and handle POSIX library errors. |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
allowed_functions¶
allowed_functions : set[bauhaus.analysis.config.FunctionName] = {'memcpy', 'memmove', 'memset', 'strcat', 'strcpy', 'strncat', 'strncpy'}
check_operators¶
check_operators : bool = False
null_check_macro¶
null_check_macro : bauhaus.analysis.config.MacroName = ''
NULL.
report_references¶
report_references : bool = False