Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Added PC-lint Plus to Automated Detection Table

Many functions have the option of returning a pointer to an object or returning NULL if a valid pointer cannot be produced. Some functions return arrays, which appear like a pointer to an object. However, if a function has the option of returning an array or indicating that a valid array is not possible, it should not return NULL. Instead, the function should return an empty array. Often, code that calls a function that returns an array intends merely to iterate over the array elements. In this case, the calling code need not change—iterating over the elements works correctly even if the returned array is empty, so the calling code need not check the return value for NULL.

This situation is complicated by the fact that

There are situations in which a function may return an array based on its length. In the case that an array of length zero is being returned, NULL should not be used. An empty array must be used to ensure the caller function can handle the return value correctly.

While C does not keep track of the length of an array. However, two popular methods have emerged to emulate this behavior. The first is to wrap the array in a struct with an integer storing the length. The second is to place a sentinel value at the end of the data in the array. This second approach is most commonly manifested in null-terminated byte strings (NTBSs).

Noncompliant Code Example (Struct)

The erroneous behavior results form getStock() returning NULL while main() forgets to add in a check for such a value. In this noncompliant code example, the check for item != null is missing from the if condition in function main().In the example below, there is an inventory system keeping keeps track of the total number of different items (denoted length) as well as . Each item is given an index in the array, and the value for that index is the stock of each that item. Adding a new item would increase increases length in the struct and a value would be assigned for stock. If there are no items being stocked, however, then the below code. Stocking more of an item increases the value for that item's index. For example, if 5 books and 2 erasers are in stock, the inventory would be stockOfItem[0] = 5 and stockOfItem[1] = 2, assuming books are index 0 and erasers are index 1.

The problem arises in this setup when no items are being stocked. getStock would recognize that length = 0 and would return NULL. In this noncompliant code example, erroneous behavior results from getStock returning NULL while main neglects to check for such a value. It results in an abnormal program termination after returning to the main function.

Code Block
bgColor#FFCCCC
langc
#include <stdio.h>

enum { INV_SIZE=20 };

typedef struct {
  size_t stockOfItem[INV_SIZE];
  size_t length;
} Inventory;

intsize_t *getStock(Inventory iv);

int main(void) {
  Inventory iv;
  size_t *item;

  iv.length = 0;

  /*
   * Other code that might modify the inventory but still
   * leave no items in it upon completion.
   */

  item = getStock(iv);

  printf("Stock of first item in inventory: %d%zd\n", item[0]);
  
  return 0;
}

intsize_t *getStock(Inventory iv) {
  if (iv.length == 0) {
    return NULL;
  }
  else {
    return iv.stockOfItem;
  }
}

...

This compliant solution eliminates the NULL return and simply returns the item array as is , even if it is zero-length. The main function can effectively handle this situation without exhibiting erroneous behavior. Since the array lives on the stack, it must prevent returning a value in the stack frame (as mandated by DCL30-C. Declare objects with appropriate storage durations). So the getStack() function also takes a pointer to Inventory, so that it can return a pointer to its contents safely.

Code Block
bgColor#ccccff
langc
#include <stdio.h>

enum { INV_SIZE=20 };

typedef struct {
  size_t stockOfItem[INV_SIZE];
  size_t length;
} Inventory;

intsize_t *getStock(Inventory* iv);

int main(void) {
  Inventory iv;
  size_t i;
  size_t *item;

  iv.length = 0;

  /*
   /* Other code that might modify the inventory but still
   * leave no items in it upon completion.
   */
  
  item = getStock(&iv);

  if (iv.length != 0) {
    printf("Stock of first item in inventory: %d%zd\n", item[0]);
  }

  return 0;
}

intsize_t *getStock(Inventory* iv) {
  return iv.stockOfItem->stockOfItem;
}

Noncompliant Code Example (Sentinel Value)

This noncompliant code example implements an inventory system similar to the one described previously. However, instead of storing the length of the array in a struct, a sentinel value of FINAL_ITEM is used. The value for the index following the last item is set as FINAL_ITEM. It is assumed that out-of-stock items (assigned value 0) are removed from the array, and the contents of later items are shifted to lower indexes.

The following code attempts to return an array of the items in stock, sorted by the amount of each item in stock. The arraySort function The code below also incorrectly returns NULL instead of a pointer to an empty array . arraySort returns NULL when the size of the array is zero. This will be improperly when no items are in stock. The null return is improperly handled by the main function, which is attempting to print out the resulting array. This will result in an returned array, and an abnormal program termination results.

Code Block
bgColor#FFCCCC
langc
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

enum { FINAL_ITEM=SIZE_MAX, INV_SIZE=20 };

size_t *arraySort(size_t *array);

int main(void) {
  size_t i;
  size_t stockOfItem[INV_SIZE];
  size_t *sortedArray;


  /* Other code that might use stockarray but leaves it empty */

  sortedArray = arraySort(stockOfItem);
  
  for (i = 0; sortedArray[i] != -1FINAL_ITEM; i++) {
	printf("Item stock: %d%zd", sortedArray[i]);
  }
  
  return 0;
}

/* Create new sorted array */
size_t *arraySort(size_t *array) {
	  size_t i;
	  size_t *sortedArray;
	
	  for(i = 0; array[i] != -1FINAL_ITEM; i++);
	  
	  if (i == 0) {
		    return NULL;
	  }

	  sortedArray = (size_t*) malloc(sizeof(size_t)*i);
	  if (sortedArray == NULL) {
		    /* Handle memory error */
	  }
	
	  /* Add sorted data to array */

  return sortedArray;
}

...

Compliant Solution (Sentinel Value)

The example below correctly This compliant solution correctly returns an empty array in the sortedArray function. If the size of the array is zero0, then sortedArray allocates an array of size 1 and fills it with the sentinel value. It can then successfully return that array to the caller function.

Code Block
bgColor#ccccff
lang#FFCCCCc
#include <stdio.h>
#include <stdint.h>
#include <malloc.h>

enum { FINAL_ITEM=SIZE_MAX, INV_SIZE=20 };

size_t *arraySort(size_t *array);

int main(void) {
  size_t i;
  size_t stockOfItem[INV_SIZE];
  size_t *sortedArray;


  /* Other code that might use stockarray but leaves it empty */

  sortedArray = arraySort(stockOfItem);
  
  for (i = 0; sortedArray[i] != -1FINAL_ITEM; i++) {
    printf("Item stock: %d%zd", sortedArray[i]);
  }
  
  return 0;
}

/* Create new sorted array */
size_t *arraySort(size_t *array) {
  size_t i;
  size_t *sortedArray;

  for(i = 0; array[i] != -1FINAL_ITEM; i++);

  if (i == 0) {
    size_t sortedArray*emptyArray = (size_t*) malloc(sizeof(size_t));
    if(sortedArrayemptyArray == NULL) {
      /* Handle memory error */
    }
    sortedArrayemptyArray[0] = -1FINAL_ITEM;
    return sortedArrayemptyArray;
  }
  
  sortedArray = (size_t*) malloc(sizeof(size_t)*i);
  if (sortedArray == NULL) {
    /* Handle memory error */
  }

  /* Add sorted data to array */

  return sortedArray;
 }

Risk Assessment

Returning NULL rather than a zero-length array may can lead to vulnerabilities when the client code does not handle null NULL properly. This Abnormal program termination can result in abnormal program termination when the calling function performs operations on NULL.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MSC19-C

low

Low

unlikely

Unlikely

high

High

P1

L3

Other Languages

...

Automated Detection

Tool

Version

Checker

Description

Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C-MSC19-a
CERT_C-MSC19-b

Avoid accessing arrays out of bounds
Avoid null pointer dereferencing

PC-lint Plus

Include Page
PC-lint Plus_V
PC-lint Plus_V

413, 418, 419, 420, 473,
613, 661, 662, 668, 669,
670

Partially supported

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this guideline on the CERT website.

References

Wiki Markup
\[[Bloch 08|AA. Java References#Bloch 08]\] Item 43: return empty arrays or collections, not nulls

Bibliography

[Bloch 2008]Item 43, "Return Empty Arrays or Collections, Not Nulls"


...

Image Added Image Added Image Removed      49. Miscellaneous (MSC)      Image Modified