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 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)
In this noncompliant code example, an inventory system keeps 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 increases length
in the struct. 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
NOTE: WORK IN PROGRESS (Just grabbing the topic before someone else)
Sometimes null
is returned intentionally to account for zero available instances. This practice can lead to vulnerabilities when the client code does not handle the null
return case.
Noncompliant Code Example
The erroneous behavior is caused due to the server returning null
while the client forgets to add in a check for such a value. This noncompliant example shows how the check item != null
condition is missing from the if
condition in class Client
.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> enum { INV_SIZE=20 }import java.util.Arrays; classtypedef Inventorystruct { private static int[] itemsize_t stockOfItem[INV_SIZE]; size_t public Inventory(length; } Inventory; size_t *getStock(Inventory iv); int main(void) { Inventory iv; size_t *item; iv.length = new int[20]; } public static int[] getStock( 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: %zd\n", item[0]); return 0; } size_t *getStock(Inventory iv) { if if(itemiv.length == 0) { return nullNULL; } else { return itemiv.stockOfItem; } } |
Compliant Solution
This compliant solution eliminates the NULL
return and returns the item
array, 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 | ||||
---|---|---|---|---|
| ||||
#include <stdio.h> enum { public class ClientINV_SIZE=20 }; typedef struct { size_t public static void main(String[] argsstockOfItem[INV_SIZE]; size_t length; } Inventory; size_t *getStock(Inventory* iv); int main(void) { Inventory iv; size_t *item; Inventory iv.length = new Inventory(); int[]0; /* * Other code that might modify the inventory but still * leave no items in it upon completion. */ item = Inventory.getStock(&iv); if (Arrays.asList(item[1]).contains(1))iv.length != 0) { System.out.printlnprintf("Almost out of stock!" + item); } } } |
Compliant Solution
Wiki Markup |
---|
This compliant solution eliminates the {{null}} return and simply returns the {{item}} array as is even if it is zero-length. The client can effectively handle this situation without exhibiting erroneous behavior. Be careful that the client does not try to access individual elements of a zero-length array such as {{item\[1\]}} while following this recommendation. |
Code Block | ||
---|---|---|
| ||
import java.util.Arrays;
class Inventory {
private static int[] item;
public Inventory() {
item = new int[20];
item[2] = 1; //quantity of item 2 remaining is 1, almost out!
}
public static int[] getStock() {
return item;
}
}
public class Client {
public static void main(String[] args) {
Inventory iv = new Inventory();
int[] item = Inventory.getStock();
if (Arrays.asList(item[1]).contains(1)) {
System.out.println("Almost out of stock!" + item);
}
}
}
|
Risk Assessment
Stock of first item in inventory: %zd\n", item[0]);
}
return 0;
}
size_t *getStock(Inventory* iv) {
return iv->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 incorrectly returns NULL
instead of a pointer to an empty array when no items are in stock. The null return is improperly handled by the main
function, which is attempting to print out the returned array, and an abnormal program termination results.
Code Block | ||||
---|---|---|---|---|
| ||||
#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] != FINAL_ITEM; i++) {
printf("Item stock: %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] != FINAL_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)
This compliant solution correctly returns an empty array in the sortedArray
function. If the size of the array is 0, 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 | ||||
---|---|---|---|---|
| ||||
#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] != FINAL_ITEM; i++) {
printf("Item stock: %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] != FINAL_ITEM; i++);
if (i == 0) {
size_t *emptyArray = (size_t*) malloc(sizeof(size_t));
if(emptyArray == NULL) {
/* Handle memory error */
}
emptyArray[0] = FINAL_ITEM;
return emptyArray;
}
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 can Returning null
rather than a zero-length array may lead to vulnerabilities when the client code does not handle null
NULL
properly. Abnormal program termination can result when the calling function performs operations on NULL
.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|
MSC19- |
C |
Low |
Unlikely |
High | P1 | L3 |
Other Languages
...
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Parasoft C/C++test |
| CERT_C-MSC19-a | Avoid accessing arrays out of bounds | ||||||
PC-lint Plus |
| 413, 418, 419, 420, 473, | Partially supported |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule 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" |
...
MET02-J. Avoid ambiguous uses of overloading 09. Methods (MET) MET30-J. Follow the general contract while overriding the equals method