The majority of the 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.
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.
...
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 7.31.3.10 paragraph 3 [ISO/IEC 9899:20112024] states that "one
)ne wide 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.)
...
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, size_t tmp2_size_bytes) {
signal_info *start = (signal_info *)calloc(num_of_records,
sizeof(signal_info));
if (tmp2 == NULL) {
/* Handle error */
} else if (temp_num > num_of_records || temp_num == 0) {
/* Handle error */
} else if (tmp2_size_bytes < SIG_DESC_SIZE) {
/* Handle error */
}
signal_info *point = start + temp_num - 1;
memcpy(point->sig_desc, tmp2, SIG_DESC_SIZE);
point->sig_desc[SIG_DESC_SIZE - 1] = '\0';
/* ... */
free(start);
} |
...
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, size_t tmp2_size_bytes) {
signal_info *start = (signal_info *)calloc(num_of_records,
sizeof(signal_info));
if (start == NULL) {
/* Handle allocation error */
} else if (tmp2 == NULL) {
/* Handle error */
} else if (temp_num > num_of_records || temp_num == 0) {
/* Handle error */
} else if (tmp2_size_bytes < SIG_DESC_SIZE) {
/* Handle error */
}
signal_info *point = start + temp_num - 1;
memcpy(point->sig_desc, tmp2, SIG_DESC_SIZE);
point->sig_desc[SIG_DESC_SIZE - 1] = '\0';
/* ... */
free(start);
} |
...
ERR33-C-EX1: It is acceptable to ignore the return value of a function if:
- that function cannot fail
...
- .
- its return value is inconsequential; that is, it does not indicate an error.
- it is one of a handful of functions whose return values are not traditionally checked.
These functions are listed in the following table:, or when 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 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 for which Return Values Need Not Be Checked
Function | Successful Return | Error Return |
---|---|---|
| Character written |
|
| Wide character written |
|
| Nonnegative |
|
| 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 |
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 |
---|---|---|---|---|---|
ERR33-C | High | Likely | Medium | P18 | L1 |
Automated Detection
The return value of a call to fprintf()
or one of its variants (vfprintf()
, wfprintf()
, vwfprintf()
) or one of the file output functions fputc()
, fputwc()
, fputs()
, fputws()
may be ignored if the output is being directed to stdout
or stderr
. Otherwise, the return value must be checked.
If a function's return value is to be ignored, it is recommended that the function's return value should be explicitly cast to void to signify the programmer's intent:
Code Block | ||||
---|---|---|---|---|
| ||||
int main() {
(void) fprintf(stdout, "Hello, world\n"); // fprintf() return value safely ignored
}
|
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 |
---|---|---|---|---|---|
ERR33-C | High | Likely | Medium | P18 | L1 |
Automated Detection
Tool | Version | Checker | Description | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Astrée |
| error-information-unused error-information-unused-computed | Partially checked | |||||||||
Axivion Bauhaus Suite |
| CertC-ERR33 | ||||||||||
CodeSonar |
| LANG.FUNCS.IRV | Ignored return value Missing Test of Error Code Non-zero Error Code | |||||||||
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 | |||||||||||
Coverity |
| MISRA C 2012 Rule 22.8 MISRA C 2012 Rule 22.9 MISRA C 2012 Rule 22.10 | Implemented | |||||||||
Helix QAC |
| C3200 C++3802, C++3803, C++3804 DF2820, DF2821, DF2822, DF2823, DF2824, DF2930, DF2931, DF2932, DF2933, DF2934 | ||||||||||
Klocwork |
| NPD.CHECK.MUST | ||||||||||
LDRA tool suite |
| 80 D | Partially implemented | |||||||||
Parasoft C/C++test |
| CERT_C-ERR33-a | The value returned by a standard library function that may return an error should be used | |||||||||
Parasoft Insure++ | Runtime analysis | |||||||||||
PC-lint Plus |
| 534 | Partially supported | |||||||||
| Checks for:
Rule partially covered. | |||||||||||
RuleChecker |
| error-information-unused | Partially checked | |||||||||
TrustInSoft Analyzer |
| pointer arithmetic | Exhaustively verified. | |||||||||
Tool | Version | Checker | Description | Astrée | ||||||||
Include Page | Astrée_V | Astrée_V | Supported, but no explicit checker | CodeSonar | ||||||||
Include Page | CodeSonar_V | CodeSonar_V | LANG.FUNCS.IRV | Ignored return value | 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 | Coverity | |||||
Include Page | Coverity_V | Coverity_V | MISRA C 2012 Rule 22.8 MISRA C 2012 Rule 22.9 MISRA C 2012 Rule 22.10 | Implemented | LDRA tool suite | |||||||
Include Page | LDRA_V | LDRA_V | 80 D | Partially implemented | ||||||||
Parasoft C/C++test | 9.5 | CODSTA-122_{a,b}, BD-PB-NP | Partially implemented | |||||||||
Parasoft Insure++ | fully implemented at runtime | PRQA QA-C++ | ||||||||||
Include Page | cplusplus:PRQA QA-C++_V | cplusplus:PRQA QA-C++_V | 2820, 2821, 2822, 2823, 2824, 2930, 2931, 2932, 2933, 2934, 3802, 3803, 3804 | PRQA QA-C | ||||||||
Include Page | PRQA QA-C_v | PRQA QA-C_v | 3200 | Partially implemented |
Related Vulnerabilities
The vulnerability in Adobe Flash [VU#159523] arises because Flash neglects to check the return value from calloc()
. Even when calloc()
returns a null pointer, Flash writes to an offset from the return value. Dereferencing a null pointer usually results in a program crash, but dereferencing an offset from a null pointer allows an exploit to succeed without crashing the program.
...
[DHS 2006] | Handle All Errors Safely |
[Henricson 1997] | Recommendation 12.1, "Check for All Errors Reported from Functions" |
[ISO/IEC 9899:20112024] | Subclause 7.2131.73.10, "The ungetc Function" |
[VU#159523] |
...