CertC++-ERR30¶
Take care when reading errno
Required inputs: IR
The value of
errno is initialized to zero at program startup, but it is never
subsequently set to zero by any C standard library function. The value of
errno may be set to nonzero by a C standard library function call
whether or not there is an error, provided the use of
errno is not documented in the description of the function. It is
meaningful for a program to inspect the contents of
errno only after an error might have occurred. More precisely,
errno is meaningful only after a library function that sets
errno on error has returned an error code.
According to Question 20.4 of C-FAQ [ Summit 2005],
In general, you should detect errors by checking return values, and use
errnoonly to distinguish among the various causes of an error, such as "File not found" or "Permission denied." (Typically, you useperrororstrerrorto print these discriminating error messages.) It's only necessary to detect errors witherrnowhen a function does not have a unique, unambiguous, out-of-band error return (that is, because all of its possible return values are valid; one example isatoi [sic]). In these cases (and in these cases only; check the documentation to be sure whether a function allows this), you can detect errors by settingerrnoto 0, calling the function, and then testingerrno. (Settingerrnoto 0 first is important, as no library function ever does that for you.)
Note that
atoi() is not required to set the value of
errno.
Library functions fall into the following categories:
- Those that set
errnoand return an out-of-band error indicator - Those that set
errnoand return an in-band error indicator - Those that do not promise to set
errno - Those with differing standards documentation
Library Functions that Set
errno and Return an Out-of-Band Error Indicator
The C Standard specifies that the functions listed in the following table set
errno and return an
out-of-band
error indicator. That is, their return value on error can never be returned
by a successful call.
A program may check
errno after invoking these library functions but is not required
to do so. The program should not check the value of
errno without first verifying that the function returned an error
indicator. For example,
errno should not be checked after calling
signal() without first ensuring that
signal() actually returned
SIG_ERR.
Functions That Set
errno and Return an Out-of-Band Error Indicator
| Function Name | Return Value | errno Value |
|---|---|---|
ftell() |
-1L |
Positive |
fgetpos(),
fsetpos()
|
Nonzero | Positive |
mbrtowc(),
mbsrtowcs()
|
(size_t)(-1) |
EILSEQ |
signal() |
SIG_ERR |
Positive |
wcrtomb(),
wcsrtombs()
|
(size_t)(-1) |
EILSEQ |
mbrtoc16(),
mbrtoc32()
|
(size_t)(-1) |
EILSEQ |
c16rtomb(),
c32rtomb()
|
(size_t)(-1) |
EILSEQ |
Library Functions that Set
errno and Return an In-Band Error Indicator
The C Standard specifies that the functions listed in the following table set
errno and return an
in-band
error indicator. That is, the return value when an error occurs is also a
valid return value for successful calls. For example, the
strtoul() function returns
ULONG_MAX and sets
errno to
ERANGE if an error occurs. Because
ULONG_MAX is a valid return value, the only way to confirm that an
error occurred when LONG_MAX is returned is to check
errno.
The
fgetwc() and
fputwc() functions return
WEOF in multiple cases, only one of which results in setting
errno. The string conversion functions will return the maximum or
minimum representable value and set
errno to
ERANGE if the converted value cannot be represented by the data
type. However, if the conversion cannot happen because the input is invalid,
the function will return
0, and the output pointer parameter will be assigned the
value of the input pointer parameter, provided the output parameter is
non-null.
A program that uses
errno for error checking a function that returns an in-band
error indicator must set
errno to
0 before calling one of these library functions and then inspect
errno before a subsequent library function call.
Functions that Set
errno and Return an In-Band Error
Indicator
| Function Name | Return Value | errno Value |
|---|---|---|
fgetwc(),
fputwc()
|
WEOF |
EILSEQ |
strtol(),
wcstol()
|
LONG_MIN or
LONG_MAX
|
ERANGE |
strtoll(),
wcstoll()
|
LLONG_MIN or
LLONG_MAX
|
ERANGE |
strtoul(),
wcstoul()
|
ULONG_MAX |
ERANGE |
strtoull(),
wcstoull()
|
ULLONG_MAX |
ERANGE |
strtoumax(),
wcstoumax()
|
UINTMAX_MAX |
ERANGE |
strtod(),
wcstod()
|
0 or
±HUGE_VAL
|
ERANGE |
strtof(),
wcstof()
|
0 or
±HUGE_VALF
|
ERANGE |
strtold(),
wcstold()
|
0 or
±HUGE_VALL
|
ERANGE |
strtoimax(),
wcstoimax()
|
INTMAX_MIN,
INTMAX_MAX
|
ERANGE |
Library Functions that Do Not Promise to Set
errno
The C Standard fails to document the behavior of
errno for some functions. For example, the
setlocale() function normally returns a null pointer in the event
of an error, but no guarantees are made about setting
errno.
After calling one of these functions, a program should not rely solely on the
value of
errno to determine if an error occurred. The function might have
altered
errno, but this does not ensure that
errno will properly indicate an error condition. If the program
does check
errno after calling one of these functions, it should set
errno to 0 before the function call.
Library Functions with Differing Standards Documentation
Some functions behave differently regarding
errno in various standards. The
fopen() function is one such example. When
fopen() encounters an error, it returns a null pointer. The C
Standard makes no mention of
errno when describing
fopen(). However, POSIX.1 declares that when
fopen() encounters an error, it returns a null pointer and sets
errno to a value indicating the error [
IEEE
Std 1003.1-2013]. The implication is that a program conforming to C
but not to POSIX (such as a Windows program) should not check
errno after calling
fopen(), but a POSIX program may check
errno if
fopen() returns a null pointer.
Library Functions and
errno
The following uses of
errno are documented in the C Standard:
- Functions defined in
<complex.h>may seterrnobut are not required to. - For numeric conversion functions in the
strtod,strtol,wcstod, andwcstolfamilies, if the correct result is outside the range of representable values, an appropriate minimum or maximum value is returned and the valueERANGEis stored inerrno. For floating-point conversion functions in thestrtodandwcstodfamilies, if an underflow occurs, whethererrnoacquires the valueERANGEis implementation-defined. If the conversion fails,0is returned anderrnois not set. - The numeric conversion function
atof()and those in theatoifamily "need not affect the value of"errno. - For mathematical functions in
<math.h>, if the integer expressionmath_errhandling & MATH_ERRNOis nonzero, on a domain error,errnoacquires the valueEDOM; on an overflow with default rounding or if the mathematical result is an exact infinity from finite arguments,errnoacquires the valueERANGE; and on an underflow, whethererrnoacquires the valueERANGEis implementation-defined. - If a request made by calling
signal()cannot be honored, a value ofSIG_ERRis returned and a positive value is stored inerrno. - The byte I/O functions, wide-character I/O functions, and multibyte
conversion functions store the value of the macro
EILSEQinerrnoif and only if an encoding error occurs. - On failure,
fgetpos()andfsetpos()return nonzero and store an implementation-defined positive value inerrno. - On failure,
ftell()returns-1Land stores an implementation-defined positive value inerrno. - The
perror()function maps the error number inerrnoto a message and writes it tostderr.
The POSIX.1 standard defines the use of
errno by many more functions (including the C standard
library function). POSIX also has a small set of functions that are exceptions
to the rule. These functions have no return value reserved to indicate an
error, but they still set
errno on error. To detect an error, an application must set
errno to
0 before calling the function and check whether it is nonzero
after the call. Affected functions include
strcoll(),
strxfrm(),
strerror(),
wcscoll(),
wcsxfrm(), and
fwide(). The C Standard allows these functions to set
errno to a nonzero value on success. Consequently, this type of
error checking should be performed only on POSIX systems.
Noncompliant Code Example (
strtoul())
This noncompliant code example fails to set
errno to
0 before invoking
strtoul(). If an error occurs,
strtoul() returns a valid value (
ULONG_MAX), so
errno is the only means of determining if
strtoul() ran successfully.
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
void func(const char *c_str) {
unsigned long number;
char *endptr;
number = strtoul(c_str, &endptr, 0);
if (endptr == c_str || (number == ULONG_MAX
&& errno == ERANGE)) {
/* Handle error */
} else {
/* Computation succeeded */
}
}
Any error detected in this manner may have occurred earlier in the program or may not represent an actual error.
Compliant Solution (
strtoul())
This compliant solution sets
errno to
0 before the call to
strtoul() and inspects
errno after the call:
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
void func(const char *c_str) {
unsigned long number;
char *endptr;
errno = 0;
number = strtoul(c_str, &endptr, 0);
if (endptr == c_str || (number == ULONG_MAX
&& errno == ERANGE)) {
/* Handle error */
} else {
/* Computation succeeded */
}
}
Noncompliant Code Example (
ftell())
This noncompliant code example, after calling
ftell(), examines
errno without first checking whether the out-of-band indicator
returned by
ftell() indicates an error.
#include <errno.h>
#include <stdio.h>
void func(FILE* fp) {
errno=0;
ftell(fp);
if (errno) {
perror("ftell");
}
}
Compliant Solution (
ftell())
This compliant solution first detects that
ftell() failed using its out-of-band error indicator. Once an
error has been confirmed, reading
errno (implicitly by using the
perror() function) is permitted.
#include <errno.h>
#include <stdio.h>
void func(FILE* fp) {
if (ftell(fp) == -1) {
perror("ftell");
}
}
Noncompliant Code Example (
fopen())
This noncompliant code example may fail to diagnose errors because
fopen() might not set
errno even if an error occurs:
#include <errno.h>
#include <stdio.h>
void func(const char *filename) {
FILE *fileptr;
errno = 0;
fileptr = fopen(filename, "rb");
if (errno != 0) {
/* Handle error */
}
}
Compliant Solution (
fopen(), C)
The C Standard makes no mention of
errno when describing
fopen(). In this compliant solution, the results of the call
to
fopen() are used to determine failure and
errno is not checked:
#include <stdio.h>
void func(const char *filename) {
FILE *fileptr = fopen(filename, "rb");
if (fileptr == NULL) {
/* An error occurred in fopen() */
}
}
Compliant Solution (
fopen(), POSIX)
In this compliant solution,
errno is checked only after an error has already been detected by
another means:
#include <errno.h>
#include <stdio.h>
void func(const char *filename) {
FILE *fileptr;
errno = 0;
fileptr = fopen(filename, "rb");
if (fileptr == NULL) {
/*
* An error occurred in fopen(); now it's valid
* to examine errno.
*/
perror(filename);
}
}
Risk Assessment
The improper use of
errno may result in failing to detect an error condition or in
incorrectly identifying an error condition when none exists.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| ERR30-C | Medium | Probable | Medium | P8 | L2 |
Related Guidelines
| Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CERT C Secure Coding Standard | EXP12-C. Do not ignore values returned by functions | Prior to 2018-01-12: CERT: Unspecified Relationship |
| ISO/IEC TS 17961:2013 | Incorrectly setting and using
errno [inverrno]
|
Prior to 2018-01-12: CERT: Unspecified Relationship |
| CWE 2.11 | CWE-456, Missing Initialization of a Variable | 2017-07-05: CERT: Rule subset of CWE |
Bibliography
| [ Brainbell.com] | Macros and Miscellaneous Pitfalls |
| [ Horton 1990] | Section 11, p. 168 Section 14, p. 254 |
| [ IEEE Std 1003.1-2013] | XSH, System Interfaces,
fopen
|
| [ Koenig 1989] | Section 5.4, p. 73 |
| [ Summit 2005] |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
misplaced_errno_check |
errno should only be tested after a call to an errno-setting function. |
None |
False |
missing_error_handling |
The program should not check the value of errno without first verifying that the function returned an error indicator. |
None |
False |
uncleared_errno |
errno should be set to zero before calling an errno-setting function. |
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
errno_clearers¶
errno_clearers : set[bauhaus.analysis.config.QualifiedName] = set()
errno to 0.
errno_readers¶
errno_readers : set[bauhaus.analysis.config.QualifiedName] = {'perror'}
errno.
not_setting_errno¶
not_setting_errno : set[bauhaus.analysis.config.QualifiedName] = set()
skip_out_of_band¶
skip_out_of_band : bool = True
standard_posix¶
standard_posix : bool = False
with_following_errno_read¶
with_following_errno_read : bool = True