Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

While C does not keep track of the length of an array, 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 (NTBS).

Noncompliant Code Example (Struct)

The erroneous behavior results from 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.

Wiki Markup
In the example below, there is an inventory system keeping track of the total number of different items (denoted {{length}}). Each item is given an index in the array, and the value for that index is the stock of that item. Adding a new item would increase {{length}} in the struct. Stocking more of an item would increase the value for that item's index. For example, if 5 books and 2 erasers were in stock, the inventory would be {{stockOfItem\[0\] = 5}} and {{stockOfItem\[1\] = 2}}, assuming books were index 0 and erasers were index 1.

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

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

enum { INV_SIZE=20 };

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

size_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\n", item[0]);
  
  return 0;
}

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

...

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

enum { INV_SIZE=20 };

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

size_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\n", item[0]);
  }
  
  return 0;
}

size_t *getStock(Inventory iv) {
  return iv.stockOfItem;
}

...

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

...

Code Block
bgColor#FFCCCC
#include <stdio.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", 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*/
}

...

The example below correctly returns an empty array in the sortedArray function. If the size of the array is zero, then sortedArray allocates an array of size 1 and fills it with the sentinel value (assumed to be -1). It can then successfully return that array to the caller function.

Code Block
bgColor#ccccff
#include <stdio.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", 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) {
    static sortedArrayemptyArray = (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*/
}

...

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MSC19-C

low

unlikely

high

P1

L3

Related Vulnerabilities

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

Other Languages

This guideline appears in the Java Secure Coding Standard as MET03-J. For methods that return an array or collection prefer returning an empty array or collection over a null value.

Related Vulnerabilities

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

References

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

...