Main Content

CERT C++: ERR30-C

Set errno to zero before calling a library function known to set errno, and check errno only after the function returns a value indicating failure

Description

Rule Definition

Set errno to zero before calling a library function known to set errno, and check errno only after the function returns a value indicating failure.1

Polyspace Implementation

The rule checker checks for these issues:

  • Misuse of errno.

  • Errno not reset.

Examples

expand all

Issue

Misuse of errno occurs when you check errno for error conditions in situations where checking errno does not guarantee the absence of errors. In some cases, checking errno can lead to false positives.

For instance, you check errno following calls to the functions:

  • fopen: If you follow the ISO® Standard, the function might not set errno on errors.

  • atof: If you follow the ISO Standard, the function does not set errno.

  • signal: The errno value indicates an error only if the function returns the SIG_ERR error indicator.

Risk

The ISO C Standard does not enforce that these functions set errno on errors. Whether the functions set errno or not is implementation-dependent.

To detect errors, if you check errno alone, the validity of this check also becomes implementation-dependent.

In some cases, the errno value indicates an error only if the function returns a specific error indicator. If you check errno before checking the function return value, you can see false positives.

Fix

For information on how to detect errors, see the documentation for that specific function.

Typically, the functions return an out-of-band error indicator to indicate errors. For instance:

  • fopen returns a null pointer if an error occurs.

  • signal returns the SIG_ERR error indicator and sets errno to a positive value. Check errno only after you have checked the function return value.

Example - Incorrectly Checking for errno After fopen Call
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define fatal_error() abort()

const char *temp_filename = "/tmp/demo.txt";

FILE *func()
{
    FILE *fileptr;
    errno = 0;
    fileptr = fopen(temp_filename, "w+b");
    if (errno != 0) { //Noncompliant
        if (fileptr != NULL) {
            (void)fclose(fileptr);
        }
        /* Handle error */
        fatal_error();
    }
    return fileptr;
}

In this example, errno is the first variable that is checked after a call to fopen. You might expect that fopen changes errno to a nonzero value if an error occurs. If you run this code with an implementation of fopen that does not set errno on errors, you might miss an error condition. In this situation, fopen can return a null pointer that escapes detection.

Correction — Check Return Value of fopen After Call

One possible correction is to only check the return value of fopen for a null pointer.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define fatal_error() abort()

const char *temp_filename = "/tmp/demo.txt";

FILE *func()
{
    FILE *fileptr;
    fileptr = fopen(temp_filename, "w+b");
    if (fileptr == NULL) { 
        fatal_error();
    }
    return fileptr;
}
Issue

Errno not reset occurs when you do not reset errno before calling a function that sets errno to indicate error conditions. However, you check errno for those error conditions after the function call.

Risk

An errno-setting function sets errno to nonzero values to indicate error conditions.

If you do not set errno to zero before calling an errno-setting function,a nonzero value of errno might be left over from a previous call to an errno-setting function. Using errno to check errors can then lead you to falsely conclude that an error occurred from the most recent call.

errno is set to 0 at program startup but is not automatically reset after an error occurs. You must explicitly set errno to 0 when required.

Fix

Before calling a function that sets errno to indicate error conditions, reset errno to zero explicitly.

Example - errno Not Reset Before Call to strtod
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <float.h>

#define fatal_error() abort()

double func(const char *s1, const char *s2)
{
    double f1;
    f1 = strtod (s1, NULL);//Noncompliant      
    if (0 == errno) {  
      double f2 = strtod (s2, NULL); 
        if (0 == errno) {        
            long double result = (long double)f1 + f2;
            if ((result <= (long double)DBL_MAX) && (result >= (long double)-DBL_MAX)) 
				  {
                return (double)result;
            }
        }
    }
    fatal_error();
    return 0.0;
}

In this example, errno is not reset to 0 before the first call to strtod. Checking errno for 0 later can lead to a false positive.

Correction — Reset errno Before Call

One possible correction is to reset errno to 0 before calling strtod.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <float.h>

#define fatal_error() abort()

double func(const char *s1, const char *s2)
{
    double f1;
    errno = 0;                   
    f1 = strtod (s1, NULL);
    if (0 == errno) {            
      double f2 = strtod (s2, NULL);  
        if (0 == errno) {       
            long double result = (long double)f1 + f2;
            if ((result <= (long double)DBL_MAX) && (result >= (long double)-DBL_MAX)) 
  			{
                return (double)result;
            }
        }
    }
    fatal_error();
    return 0.0;
}

Check Information

Group: 08. Exceptions and Error Handling (ERR)

Version History

Introduced in R2019a


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.