...
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 | ||
---|---|---|
| ||
#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 | ||
---|---|---|
| ||
#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 | ||
---|---|---|
| ||
#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 | ||
---|---|---|
| ||
#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 |
...