CERT C: Rule ERR33-C

Detect and handle standard library errors

Description

Rule Definition

Detect and handle standard library errors.[1]

Polyspace Implementation

This checker checks for these issues:

  • Errno not checked.

  • Returned value of a sensitive function not checked.

  • Unprotected dynamic memory allocation.

Examples

expand all

Issue

Errno not checked occurs when 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.

Risk

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.

You can determine if an error occurred only by checking errno.

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.

Fix

Before calling the function, set errno to zero.

After the function call, to see if an error occurred, compare errno to zero. Alternatively, compare errno to known error indicator values. For instance, strtol sets errno to ERANGE to indicate errors.

The error message in the Polyspace® result shows the error indicator value that you can compare to.

Example - errno Not Checked After Call to strtol
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>

int main(int argc, char *argv[]) {
    char *str, *endptr;
    int base;
    
    str = argv[1];
    base = 10;
    
    long val = strtol(str, &endptr, base);
    printf("Return value of strtol() = %ld\n", val);
}

You are using the return value of strtol without checking errno.

Correction — Check errno After Call

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.

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

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

Returned value of a sensitive function not checked occurs when you call sensitive standard functions, but you:

  • Ignore the return value.

  • Use an output or a return value without testing the validity of the return value.

For this defect, two type of functions are considered: sensitive and critical sensitive.

A sensitive function is a standard function that can encounter:

  • 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

A critical sensitive function is a sensitive function that performs one of these critical or vulnerable tasks:

  • Set privileges (for example, setuid)

  • Create a jail (for example, chroot)

  • Create a process (for example, fork)

  • Create a thread (for example, thrd_create)

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

Risk

If you do not check the return value of functions that perform sensitive or critical sensitive tasks, your program can behave unexpectedly. Errors from these functions can propagate throughout the program causing incorrect output, security vulnerabilities, and possibly system failures.

Fix

Before continuing with the program, test the return value of critical sensitive functions.

For sensitive functions, you can explicitly ignore a return value by casting the function to void. Polyspace does not raise this defect for sensitive functions cast to void. This resolution is not accepted for critical sensitive functions because they perform more vulnerable tasks.

Example - Sensitive Function Return Ignored
#include<stdio.h>
void initialize() {
	int n = 1;
	scanf("%d",&n); 
}

This example shows a call to the sensitive function scanf(). The return value of scanf() is ignored, causing a defect.

Correction — Cast Function to (void)

One possible correction is to cast the function to void. This fix informs Polyspace and any reviewers that you are explicitly ignoring the return value of the sensitive function.

#include<stdio.h>
void initialize() {
	int n = 1;
	(void) scanf("%d",&n); 
}
Correction — Test Return Value

One possible correction is to test the return value of scanf to check for errors.

#include<stdio.h>
void initialize() {
	int n,flag ;
	flag = scanf("%d",&n); 
	if(flag>0){
		//...
	}
}
Example - Critical Function Return Ignored
#include <threads.h>
int thrd_func(void);
void initialize() {
    thrd_t thr;
	int n = 1;

    (void) thrd_create(&thr,thrd_func,&n); 
}

In this example, a critical function thrd_create is called and its return value is ignored by casting to void, but because thrd_create is a critical function, Polyspace does not ignore this defect.

Correction — Test the Return Value of Critical Functions

The correction for this defect is to check the return value of these critical functions to verify the function performed as expected.

#include <threads.h>
int thrd_func(void);
void initialize() {
	thrd_t thr;
	int n = 1;
	if( thrd_success!= thrd_create(&thr,thrd_func,&n) ){
		// handle error
		
	}
}
Issue

Unprotected dynamic memory allocation occurs when you do not check after dynamic memory allocation whether the memory allocation succeeded.

Risk

When memory is dynamically allocated using malloc, calloc, or realloc, it returns a value NULL if the requested memory is not available. If the code following the allocation accesses the memory block without checking for this NULL value, this access is not protected from failures.

Fix

Check the return value of malloc, calloc, or realloc for NULL before accessing the allocated memory location.

#DEFINE SIZE 8;

int *ptr = malloc(SIZE * sizeof(int));

if(ptr) /* Check for NULL */ 
{
   /* Memory access through ptr */
}

Example - Unprotected dynamic memory allocation error
#include <stdlib.h>

void Assign_Value(void) 
{
  int* p = (int*)calloc(5, sizeof(int));

  *p = 2;  
  /* Defect: p is not checked for NULL value */

  free(p);
}

If the memory allocation fails, the function calloc returns NULL to p. Before accessing the memory through p, the code does not check whether p is NULL

Correction — Check for NULL Value

One possible correction is to check whether p has value NULL before dereference.

#include <stdlib.h>

void Assign_Value(void)
 {
   int* p = (int*)calloc(5, sizeof(int));

   /* Fix: Check if p is NULL */
   if(p!=NULL) *p = 2; 

   free(p);
 }

Check Information

Group: Rule 12. Error Handling (ERR)
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.