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 118 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 |
---|---|---|---|
Astrée | 24.04 | bad-function | Fully checked |
Axivion Bauhaus Suite | 7.2.0 | CertC-ERR07 | |
LDRA tool suite | 9.7.1 | 44 S, 593 S, 594 S | Partially implemented |
Parasoft C/C++test | 2023.1 | CERT_C-ERR07-a | The library functions atof, atoi and atol from library stdlib.h shall not be used |
PC-lint Plus | 1.4 | 586 | Fully supported |
RuleChecker | 24.04 | bad-function | Fully checked |
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()" |
7 Comments
Robert Seacord
I think we've adopted the solution elsewhere of having a black list of functions such as
atoi()
for which better functions are available and flagging these invocations. This is not a complete solution as you state, but it is a partial solution, and if implemented in an extensible manner should be fairly robust in practice.Martin Sebor
Out of curiosity, where is this assertion supported (bullet 3 in Noncompliant Code Example (
atoi()
)):AFAICS, the behavior of these functions is only specified on success (Except for the behavior on error, they are equivalent to...).
Robert Seacord (Manager)
This page says that:
And this page which might be the same source.
The MSDN documentation says:
Beats me how converting a string into a number can "overflow".
Probably safer/more correct to say "Some implementations return 0 if the string does not represent an integer but C99 only specifies the behavior of these functions on success"
I would be surprised to learn of an implementation that does anything besides return 0, but obviously this behavior is not guaranteed.
David Svoboda
I always assumed that overflow during a string-to-int conversion occurs if the string contains a valid mathematical number, but the number is out of range. eg trying to read 100000 into a 16-bit (short) int. I suppose in that case, an implementation of atoi() might return 100000 % 2^16.
Martin Sebor
undefined behavior 113 applies to
atof
,atoi
,atol
, andatoll
. (Callingstrtol
is safe and cannot overflow or lead to undefined behavior.)For
atof(nptr)
, the reason is because the function returns(float)strtod(nptr, (char **)NULL)
and the conversion fromdouble
tofloat
can cause undefined behavior 16 .atoi(nptr)
returns(int)strtol(nptr, (char **)NULL, 10)
so I assume the reason is similar, although rather than undefined behavior when during the conversion fromlong
toint
"[...] the value cannot be represented [...] either the result is implementation-defined or an implementation-defined signal is raised." (6.3.1.3, para 3).As for the various documentation pages promising 0, I'm guessing that's because
strtol()
is required to return0
on conversion error so an implementation whereatoi(nptr)
just returns(int)strtol(nptr, (char **)NULL, 10)
a conversion error will return 0. But, as Robert says, the spec doesn't guarantee it.Robert Seacord
This rule overlaps with recommendation INT06-C. Use strtol() or a related function to convert a string token to an integer
David Svoboda
Agreed. We might decide that rec is an instance of this rec...do we have any similar instances of this rec?