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