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 | Comments |
---|---|---|
|
| No error indication, undefined behavior on error |
|
| No error indication, undefined behavior on error |
|
| No error indication, undefined behavior on error |
|
| No error indication, undefined behavior on error |
|
| No error indication, silent failure on error |
|
| No error indication, silent failure on error |
ctime | asctime /localtime | Undefined behavior if |
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 |
---|---|
|
|
|
|
|
|
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
errno
on 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 |
Automated Detection
This rule in general cannot be detected, although various examples can be detected by simply scanning for functions that have equivalent functions with better error handling.
Tool | Version | Checker | Description |
---|---|---|---|
LDRA tool suite | 9.7.1 | 44 S, 593 S, 594 S | Partially implemented |
Parasoft C/C++test | 2023.1 | MISRA2012-RULE-21_7 | Partially implemented, detects ato* functions |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
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()" |