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 errno only to distinguish among the various causes of an error, such as "File not found" or "Permission denied." (Typically, you use perror or strerror to print these discriminating error messages.) It's only necessary to detect errors with errno when 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 is atoi [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 setting errno to 0, calling the function, and then testing errno. (Setting errno to 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 errno and return an  out-of-band error indicator
  • Those that set errno and 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 set errno but are not required to.
  • For numeric conversion functions in the strtod, strtol, wcstod, and wcstol families, if the correct result is outside the range of representable values, an appropriate minimum or maximum value is returned and the value ERANGE is stored in errno. For floating-point conversion functions in the strtod and wcstod families, if an underflow occurs, whether errno acquires the value ERANGE is implementation-defined. If the conversion fails,  0 is returned and  errno is not set.
  • The numeric conversion function atof() and those in the atoi family "need not affect the value of" errno.
  • For mathematical functions in <math.h>, if the integer expression math_errhandling & MATH_ERRNO is nonzero, on a domain error, errno acquires the value EDOM; on an overflow with default rounding or if the mathematical result is an exact infinity from finite arguments, errno acquires the value ERANGE; and on an underflow, whether errno acquires the value ERANGE is implementation-defined.
  • If a request made by calling signal() cannot be honored, a value of SIG_ERR is returned and a positive value is stored in errno.
  • The byte I/O functions, wide-character I/O functions, and multibyte conversion functions store the value of the macro EILSEQ in errno if and only if an encoding error occurs.
  • On failure, fgetpos() and fsetpos() return nonzero and store an  implementation-defined positive value in errno.
  • On failure, ftell() returns -1L and stores an  implementation-defined positive value in errno.
  • The perror() function maps the error number in errno to a message and writes it to stderr.

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]
Excerpt from SEI CERT C++ Coding Standard [https://cmu-sei.github.io/secure-coding-standards/sei-cert-c-coding-standard/rules/error-handling-err/err30-c], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

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

errno_clearers

errno_clearers : set[bauhaus.analysis.config.QualifiedName] = set()

The (unused!) set of functions that reset errno to 0.
 

errno_readers

errno_readers : set[bauhaus.analysis.config.QualifiedName] = {'perror'}

The set of functions that read from errno.
 

not_setting_errno

not_setting_errno : set[bauhaus.analysis.config.QualifiedName] = set()

The set of functions that definitely do not change the errno value.
 

skip_out_of_band

skip_out_of_band : bool = True

Skip calls where in case of error, the return value is not a valid value.
 

standard_posix

standard_posix : bool = False

If true, use POSIX.1 standard. Otherwise, use C standard.
 

with_following_errno_read

with_following_errno_read : bool = True

Whether to only report when calls followed by an errno check.