CertC-ERR07¶
Prefer functions that support error checking over equivalent functions that don’t
Required inputs: IR
When you have a choice of two functions to accomplish the same task, prefer the one with better error checking and reporting.
The following table shows a list of C standard library functions that provide limited or no error checking and reporting along with preferable alternatives:
| Function | Preferable Alternative |
Comments |
|---|---|---|
atof |
strtod |
No error indication, undefined behavior on error |
atoi |
strtol |
No error indication, undefined behavior on error |
atol |
strtol |
No error indication, undefined behavior on error |
atoll |
strtoll |
No error indication, undefined behavior on error |
rewind |
fseek |
No error indication, silent failure on error |
setbuf |
setvbuf |
No error indication, silent failure on error |
ctime |
asctime/
localtime
|
Undefined behavior if
localtime fails
|
Noncompliant Code Example (
atoi())
This noncompliant code example converts the string token stored in the static
array
buff to a signed integer value using the
atoi() function:
int si;
if (argc > 1) {
si = atoi(argv[1]);
}
The
atoi(),
atol(), and
atoll() functions convert the initial portion of a string token to
int,
long int, and
long long int representation respectively. Except for the behavior
on error, they are equivalent as follows:
| Call | Equivalent on Success |
|---|---|
atoi(nptr) |
(int)strtol(nptr, (char **)NULL, 10) |
atol(nptr) |
strtol(nptr, (char **)NULL, 10) |
atoll(nptr) |
strtoll(nptr, (char **)NULL, 10) |
Unfortunately,
atoi() and related functions lack a mechanism for reporting errors
for invalid values. Specifically, the
atoi(),
atol(), and
atoll() functions
- Do not need to set
errnoon an error. - Have undefined behavior if the value of the result cannot be represented. (See undefined behavior 119 of Annex J of the C Standard.)
- Return 0 if the string does not represent an integer (which is indistinguishable from a correctly formatted, zero-denoting input string), but the C Standard only specifies the behavior of these functions on success.
See also MSC24-C. Do not use deprecated or obsolescent functions.
Compliant Solution (
strtol())
The
strtol(),
strtoll(),
strtoul(), and
strtoull() functions convert a null-terminated byte string to
long int,
long long int,
unsigned long int, and
unsigned long long int representation respectively.
This compliant solution uses
strtol() to convert a string token to an integer and ensures that
the value is in the range of
int:
long sl;
int si;
char *end_ptr;
if (argc > 1) {
errno = 0;
sl = strtol(argv[1], &end_ptr, 10);
if ((sl == LONG_MIN || sl == LONG_MAX)
&& errno != 0)
{
perror("strtol error");
}
else if (end_ptr == argv[1]) {
if (puts("error encountered during conversion") == EOF) {
/* Handle error */
}
}
else if (sl > INT_MAX) {
printf("%ld too large!\n", sl);
}
else if (sl < INT_MIN) {
printf("%ld too small!\n", sl);
}
else if ('\0' != *end_ptr) {
if (puts("extra characters on input line\n") == EOF) {
/* Handle error */
}
}
else {
si = (int)sl;
}
}
Both the noncompliant code example and the compliant solution are taken from ERR34-C. Detect errors when converting a string to a number.
Noncompliant Code Example (
rewind())
This noncompliant code example sets the file position indicator of an input
stream back to the beginning using
rewind():
char *file_name;
FILE *fp;
/* Initialize file_name */
fp = fopen(file_name, "r");
if (fp == NULL) {
/* Handle open error */
}
/* Read data */
rewind(fp);
/* Continue */
It is impossible to determine if
rewind() succeeded.
Compliant Solution (
fseek())
This compliant solution uses
fseek() instead of
rewind() and checks to see if the operation succeeded:
char *file_name;
FILE *fp;
/* Initialize file_name */
fp = fopen(file_name, "r");
if (fp == NULL) {
/* Handle open error */
}
/* Read data */
if (fseek(fp, 0L, SEEK_SET) != 0) {
/* Handle repositioning error */
}
/* Continue */
Noncompliant Code Example (
setbuf())
This noncompliant code example calls
setbuf() with a
buf argument of
NULL:
FILE *file; /* Setup file */ setbuf(file, NULL); /* ... */
It is not possible to determine if the call to
setbuf() succeeded.
Implementation Details
On 4.2BSD and 4.3BSD systems,
setbuf() always uses a suboptimal buffer size and should be
avoided.
Compliant Solution (
setvbuf())
This compliant solution calls
setvbuf(), which returns nonzero if the operation failed:
FILE *file;
char *buf = NULL;
/* Setup file */
if (setvbuf(file, buf, buf ? _IOFBF : _IONBF, BUFSIZ) != 0) {
/* Handle error */
}
/* ... */
Risk Assessment
Although it is rare for a violation of this rule to result in a security vulnerability, it can easily result in lost or misinterpreted data.
| Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| ERR07-C | Medium | Probable | Medium | P8 | L2 |
Related Guidelines
| MITRE CWE |
CWE-20, Improper Input Validation CWE-79, Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') CWE-89, Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') CWE-91, XML Injection (aka Blind XPath Injection) CWE-94, Improper Control of Generation of Code ('Code Injection') CWE-114, Process Control CWE-601, URL Redirection to Untrusted Site ('Open Redirect') CWE-676, Use of potentially dangerous function |
Bibliography
| [ Klein 2002] | "Bullet Proof Integer Input Using
strtol()"
|
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
error_checking_alternative |
Prefer functions that support error checking over equivalent functions that don’t (i.e., replace ‘{}’ by ‘{}’). |
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
replacements¶
replacements
Mapping of non-error-checking functions to error-checking replacements.Type: dict[str, str]
Default:
{ 'atof': 'strtod', 'atoi': 'strtol', 'atol': 'strtol', 'atoll': 'strtoll', 'ctime': 'asctime/localtime', 'rewind': 'fseek', 'setbuf': 'setvbuf' }