Common Weakness Enumerations can be found at https://cwe.mitre.org/data/definitions/699.html
CWE-129: Improper Validation of Array Index
Description
The product uses untrusted input when calculating or using
an array index, but the product does not validate or incorrectly validates the
index to ensure the index references a valid position within the array.
Demonstrative Examples
Example 1 : Java language
public
String getValue(int index) {
return array[index];
}
|
This may result in ArrayIndexOutOfBounds Exception being
raised if index is outside the range of the array.
Example 2: Java language
private void
buildList (int untrustedListSize) {
if ( 0 > untrustedListSize ) {
die(“Negative value supplied for list
size, die evil hacker!”);
}
Widget[] list = new Widget[ untrustedListSize
];
list[0] = new Widget()’
}
|
This example attempts to build a list from a user-specified
value, and even checks to ensure a non-negative value is supplied. If, however,
a 0 value is provided, the code will build an array of size 0 and then try to
store a new Widget in the first location, causing an exception to be thrown.
Example 3: C language
int
getValueFromArray(int *array, int len, int index) {
int value;
// check
that the array index is less than the maximum
// length of
the array
if (index
< len) {
// get the
value at the specified index of the array
value =
array[index];
}
// if array
index is invalid then output error message
// and
return value indicating error
else {
printf("Value
is: %d\n", array[index]);
value = -1;
}
return
value;
}
|
This method only verifies that the given array index is less
than the maximum length of the array but does not check for the minimum value (CWE-839).
This will allow a negative value to be accepted as the input array index, which
will result in a out of bounds read (CWE-125)
and may allow access to sensitive memory. The input array index should be
checked to verify that is within the maximum and minimum range required for the
array (CWE-129).
Example 4: C language
int main
(int argc, char **argv) {
char *items[] = {"boat",
"car", "truck", "train"};
int index = GetUntrustedOffset();
printf("You selected %s\n",
items[index-1]);
}
|
The programmer allows the user to specify which element in
the list to select, however an attacker can provide an out-of-bounds offset,
resulting in a buffer over-read (CWE-126).
Potential Mitigations using the Ada language
Ada allows the programmer to define numeric types and
subtypes with a specified range of valid values. All instances of a specified
numeric type must all satisfy the range validity requirements specified for
that type.
Example 3 above defines a function returning an int. The
function has two parameters, a pointer to an int, which is intended to point to
an array of ints, and an int index value which is intended to specify an index
in the array.
Within the function a check is made for an invalid index
value and the int value -1 is returned if an error is detected. Nowhere in the
description of the array or the function is there any restriction on the range
of values any of these ints can contain. For instance, it is completely valid
for the array to contain negative values, including -1. If -1 is also used to
indicate an error then the program can never distinguish between an error and
an array value of -1. Much must be changed to fix this fault.
Mitigation 1:
function
getIndex(index : Positive) return String with
Pre => index in String_Array’Range;
function
getIndex(index : Positive) return String is
begin
return String_Array(index);
end
getIndex;
|
The function specification establishes a pre-condition
requiring the parameter index to be within the range of index values associated
with String_Array. The pre-condition assures that the index value used within
the getIndex function is a valid index of String_Array.
Mitigation 2:
generic
type Element_Type is private;
package
List_Handler is
type List(Size : Positive) is tagged
private;
private
type Element_Array is array (Positive
range <>) of Element_Type;
type List(Size : Positive) is tagged
record
Buffer : Element_Array(1..Size);
end record;
end
List_Handler;
|
The package specification defines a generic package that can
be instantiated with any type, creating a “List” of that type. The type List is
defined with a discriminant value named Size which determines the size of the
array to be used for a particular instance of List. The discriminant Size is
required to be a value of the pre-defined subtype Positive. The minimum valid
value for an instance of Positive is 1. This prevents the size of the Buffer
element in List from being either negative or 0.
Mitigation 3
type
Int_Array is array(Natural range <>) of Integer;
...
function
getValueFromArray(Nums : Int_Array; index : Natural) return Integer
with Pre => index in Nums’Range;
function
getValueFromArray(Nums : Int_Array; index : Natural) return Integer is
begin
return Nums(index);
end
getValueFromArray;
|
The type Int_Array is defined as being indexed by a value of
the pre-defined subtype Natural. The minimum value of an instance of Natural is
0. The pre-condition assures that the value of index is within the valid index
values for any instance of Int_Array passed to the function. The function
getValueFromArray always returns a valid array element when the pre-condition
is satisfied. If the pre-condition is not satisfied the call results in an
Assertion_Error exception being raised.
Mitigation 3 alternate
This mitigation fixes the indexing problems without the use
of pre-conditions.
type
Result_Type(Is_Valid : Boolean) is record
case Is_Valid is
when True => Value : Integer;
when False => null;
end case;
end record;
type
Int_Array is array(Natural range <>) of Integer;
function
getValueFromArray(Nums : Int_Array; Index : Natural)
return
Result_Type is
begin
if Index in Nums’Range then
return (Is_Valid => True, Value
=> Nums(Index));
else
return (Is_Valid => False);
end if;
end
getValueFromArray;
|
The type Result_Type is defined as a variant record. When the discriminant, named Is_Valid, is True the
type contains two fields; Is_Valid and Value. When the discriminant Is_Valid is
False the type only contains the field Is_Valid.
The conditional expression tests the parameter Index. If
Index contains a value within the valid range of index values for the array
Nums then the function returns an instance of Result_Type with the field
Is_Valid containing True and the field Value containing the integer value that
was in Nums(Index), otherwise the function returns an instance of Result_Type
only containing the field Is_Valid and that field contains a False value.
This solution clearly separates the concept of an erroneous
index value from any value contained in the array to be searched. Use of the
variant record allows the function to return two values at the same time. No
Value field is returned when the index is invalid because to do so would
suggest that a valid value was found. This solution prevents a programmer from erroneously
using an invalid return value.
Mitigation 4
with
Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with
Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with
Ada.Integer_IO; use Ada.Integer_IO;
procedure
main is
subtype Item_Index is Integer range 1..4;
type Item_List is array(Index_Type) of
Unbounded_String;
function getOffset return Item_Index is
Offset : Integer;
begin
loop
Put(“Enter a number in the range of 1
through 4: “);
Get(Offset);
exit when Offset in Item_Index;
end loop;
return Offset;
end getOffset;
Items : Item_List :=
(To_Unbounded_String(“boat”),
To_Unbounded_String(“car”),
To_Unbounded_String(“truck”),
To_Unbounded_String(“train”));
index : Item_Index := getOffset;
begin
Put_Line(“You selected “ &
To_String(Item_List(index)));
end main;
|
The programmer allows the user to specify the element to
select and the selection will only access the array after a valid index value
has been chosen.
The function getOffset takes no parameters and returns a
value of the subtype Item_Index. The simple loop statement continually prompts
the user until the user enters a valid value. The function then returns that
value. The array of Unbounded_String is only accessed after a valid index value
has been obtained from the user.
Conclusion
These examples show that CWE-129 Improper Validation of
Array Index is far simpler to avoid using Ada than using C, C++, or Java.