Main Content

CWE Rule 253

Incorrect Check of Function Return Value

Since R2023a

Description

Rule Description

The software incorrectly checks a return value from a function, which prevents the software from detecting errors or exceptional conditions.

Polyspace Implementation

The rule checker checks for these issues:

  • Errno not reset

  • Unprotected dynamic memory allocation

Examples

expand all

Issue

This issue 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

The errno is not clean and can contain values from a previous call. Checking errno for errors can give the false impression that an error occurred.

errno is set to zero at program startup but subsequently, errno is not reset by a C standard library function. You must explicitly set errno to zero 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;
}
Issue

This issue occurs when you access dynamically allocated memory without first checking if the prior 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.

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;  //Noncompliant
  /* 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);
 }
Example — Unprotected dynamic memory allocation error only on dereference
#include <stdlib.h>
#include<string.h>
typedef struct recordType {
    const char* id;
    const char* data;
} RECORD;

RECORD* MakerecordType(const char *id,unsigned int size){
    RECORD *rec = (RECORD *)calloc(1, sizeof(RECORD));
    rec->id = strdup(id);  //Noncompliant

    const char *newData = (char *)calloc(1, size);
    rec->data = newData;
    return rec;
}

In this example, the checker raises a defect when you dereference the pointer rec without checking for a NULL value from the prior dynamic memory allocation.

A similar issue happens with the pointer newData. However, a defect is not raised because the pointer is not dereferenced but simply copied over to rec->data. Simply copying over a possibly null pointer is not an issue by itself. For instance, callers of the recordType_new function might check for NULL value of rec->data before dereferencing, thereby avoiding a null pointer dereference.

Check Information

Category: Error Conditions, Return Values, Status Codes

Version History

Introduced in R2023a