Main Content

AUTOSAR C++14 Rule M0-3-2

If a function generates error information, then that error information shall be tested

Since R2020b

Description

Rule Definition

If a function generates error information, then that error information shall be tested.

Rationale

If you do not check the return value of functions that indicate error information through their return values, your program can behave unexpectedly. Errors from these functions can propagate throughout the program causing incorrect output, security vulnerabilities, and possibly system failures.

For the errno-setting functions, to see if the function call completed without errors, check errno for error values. The return values of these errno-setting functions do not indicate errors. The return value can be one of the following:

  • void

  • Even if an error occurs, the return value can be the same as the value from a successful call. Such return values are called in-band error indicators. For instance, strtol converts a string to a long integer and returns the integer. If the result of conversion overflows, the function returns LONG_MAX and sets errno to ERANGE. However, the function can also return LONG_MAX from a successful conversion. Only by checking errno can you distinguish between an error and a successful conversion.

For the errno-setting functions, you can determine if an error occurred only by checking errno.

Polyspace Implementation

The checker raises a violation when:

  • You call sensitive functions that return information about possible errors and then you ignore the return value or use the output of the function without testing the return value.

    The checker covers function from the standard library and other well-known libraries such as the POSIX library or the WinAPI library. Polyspace® considers a function as sensitive if the function call is prone to failure because of reasons such as:

    • Exhausted system resources (for example, when allocating resources).

    • Changed privileges or permissions.

    • Tainted sources when reading, writing, or converting data from external sources.

    • Unsupported features despite an existing API.

    Polyspace considers a function a critical sensitive when they perform critical tasks such as:

    • Set privileges (for example, setuid)

    • Create a jail (for example, chroot)

    • Create a process (for example, fork)

    • Create a thread (for example, pthread_create)

    • Lock or unlock mutex (for example, pthread_mutex_lock)

    • Lock or unlock memory segments (for example, mlock)

    For functions that are not critical, the checker is not flagged if you explicitly ignore the return value by casting it to void. Explicitly ignoring the return value of critical sensitive functions is flagged by Polyspace.

  • You call a function that sets errno to indicate error conditions, but do not check errno after the call. For these functions, checking errno is the only reliable way to determine if an error occurred.

    Functions that set errno on errors include:

    • fgetwc, strtol, and wcstol.

      For a comprehensive list of functions, see documentation about errno.

    • POSIX® errno-setting functions such as encrypt and setkey.

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

#include <pthread.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <cstdlib>
#define fatal_error() abort()

void initialize_1() {
    pthread_attr_t attr;
    pthread_attr_init(&attr); //Noncompliant
}

void initialize_2() {
    pthread_attr_t attr;
   (void)pthread_attr_init(&attr); //Compliant
}

void initialize_3() {
    pthread_attr_t attr;
    int result;
    result = pthread_attr_init(&attr); //Compliant
    if (result != 0) {
        /* Handle error */
        fatal_error();
    }
}

int read_file_1(int argc, char *argv[])
{
  FILE *in;
  if (argc != 2) {
    /* Handle error */
  }

  in = fmemopen (argv[1], strlen (argv[1]), "r");   
  return 0; //Noncompliant
 
}
int read_file_2(int argc, char *argv[])
{
  FILE *in;
  if (argc != 2) {
    /* Handle error */
  }

  in = fmemopen (argv[1], strlen (argv[1]), "r"); //Compliant
  if (in==NULL){
	  // Handle error
  }
  return 0; 
}

This example shows a call to the sensitive functions pthread_attr_init and fmemopen. Polyspace raises a flag if:

  • You implicitly ignore the return of the sensitive function. Explicitly ignoring the output of sensitive functions is not flagged.

  • You obtain the return value of a sensitive function but do not test the value before exiting the relevant scope. The violation is raised on the exit statement.

To be compliant, you can explicitly cast their return value to void or test the return values to check for errors.

#include <pthread.h>
#include <cstdlib>
#define fatal_error() abort()
extern void *start_routine(void *);

void returnnotchecked_1() {
    pthread_t thread_id;
    pthread_attr_t attr;
    void *res;

    (void)pthread_attr_init(&attr);
    (void)pthread_create(&thread_id, &attr, &start_routine, ((void *)0)); //Noncompliant
    pthread_join(thread_id,  &res); //Noncompliant
}

void returnnotchecked_2() {
    pthread_t thread_id;
    pthread_attr_t attr;
    void *res;
    int result;

    (void)pthread_attr_init(&attr);
    result = pthread_create(&thread_id, &attr, &start_routine, NULL); //Compliant
    if (result != 0) {
        /* Handle error */
        fatal_error();
    }

    result = pthread_join(thread_id,  &res); //Compliant
    if (result != 0) {
        /* Handle error */
        fatal_error();
    }
}

In this example, two critical functions are called: pthread_create and pthread_join. The return value of the pthread_create is ignored by casting to void, but because pthread_create is a critical function (not just a sensitive function), the rule checker still raises a violation. The other critical function, pthread_join, returns a value that is ignored implicitly.

To be compliant, check the return value of these critical functions to verify the function performed as expected.

#include<cstdlib>
#include<cerrno>
#include<climits>
#include<iostream>

int main(int argc, char *argv[]) {
    char *str, *endptr;
    int base;
    
    str = argv[1];
    base = 10;
    
    long val = strtol(str, &endptr, base); //Noncompliant
    std::cout<<"Return value of strtol() = %ld\n" << val;
    
    errno = 0;
    long val2 = strtol(str, &endptr, base); //Compliant
    if((val2 == LONG_MIN || val2 == LONG_MAX) && errno == ERANGE) {
         std::cout<<"strtol error";
         exit(EXIT_FAILURE);
    }        
    std::cout<<"Return value of strtol() = %ld\n" << val2;
}

In the noncompliant example, the return value of strtol is used without checking errno.

To be compliant, before calling strtol, set errno to zero. After a call to strtol, check the return value for LONG_MIN or LONG_MAX and errno for ERANGE.

Check Information

Group: Language independent issues
Category: Required, Non-automated

Version History

Introduced in R2020b

expand all