Page properties | ||
---|---|---|
| ||
Better normative wording is badly needed. |
Ensuring that array references are within the bounds of the array is almost entirely the responsibility of the programmer. Likewise, when using STL vectors, the programmer is responsible for ensuring integer indexes are within the bounds of the vector.
Noncompliant Code Example (
...
Pointers)
This noncompliant code example shows a function insert_in_table()
that has two int
paramters, pos
and value
, both of which can be influenced by data originating from untrusted sources. The The function uses a global variable table
to determine if storage has been allocated for an array of 100 integer elements and allocates the memory if it has not already been allocatedperforms a range check to ensure that pos
does not exceed the upper bound of the array, specified by table_size
, but fails to check the lower bound. Because pos
has been declared as a (signed) int
, this parameter can assume a negative value, resulting in a write outside the bounds of the memory referenced by table
.
Code Block | ||||
---|---|---|---|---|
| ||||
enum { TABLESIZE = 100 }; #include <cstddef> void insert_in_table(int *table = NULL; int insert_in_table(, std::size_t table_size, int pos, int value) { if (!tablepos >= table_size) { table// = new int[TABLESIZE]; } if (pos >= TABLESIZE) {Handle error return -1; } table[pos] = value; return 0; } |
...
Compliant Solution (
...
size_t
)
In this compliant solution, the parameter pos
is declared as size_t
, which prevents passing of negative arguments (see INT01-CPP. Use rsize_t or size_t for all integer values representing the size of an object).
Code Block | ||||
---|---|---|---|---|
| ||||
enum { TABLESIZE = 100 }; #include <cstddef> void insert_in_table(int *table = NULL; int insert_in_table(, std::size_t table_size, std::size_t pos, int value) { if (!tablepos >= table_size) { table// = new int[TABLESIZE]; } if (pos >= TABLESIZE) { return -1Handle error return; } table[pos] = value; return 0; } |
Compliant Solution (
...
Non-Type Templates)
...
Page properties | ||
---|---|---|
| ||
I think this CS should be removed as it doesn't really add much benefit to this discussion. It seems like it would be a far better fit as a recommendation in DCL to declare functions accepting pointer/size (array) to have a non-type template overload where the array size is deduced automatically. If we picked a more convincing NCCE, it might make sense to include a CS of this type here. |
Non-type templates can be used to define functions accepting an array type where the array bounds are deduced at compile time. This compliant solution is functionally equivalent to the previous bounds-checking one, except that it additionally supports calling insert_in_table()
with an array of known boundsSpecialized function templates can be used to define functions that accept an array of a generic type T
of size n
. The compiler can perform template argument deduction for function templates to properly deduce both the type and size. This compliant solution defines a function template for a function clear()
that takes a template parameter array[]
of T
elements and an actual length parameter of type size_t
. This is not particularly useful yet, as we have already seen that passing an array with an explicit size parameter is a common (but error prone) idiom. However, you can also define a specialization of the function template that includes a template parameter n
of type size_t
in addition to the original type parameter. The inline function clear()
has one parameter: an array of type T
elements of fixed length n
.
Code Block | ||||
---|---|---|---|---|
| ||||
template#include <typename T><cstddef> #include <new> void clear(T array[], size_t n) { for (size_t i = 0; i < n; ++i) { array[i] = 0; }insert_in_table(int *table, std::size_t table_size, std::size_t pos, int value) { // #1 if (pos >= table_size) { // Handle error return; } table[pos] = value; } template <typename T, <std::size_t n>N> inline void clear(Tinsert_in_table(int (&arraytable)[n]N], std::size_t pos, int value) { // #2 clear(arrayinsert_in_table(table, N, pos, nvalue); } void f() { // Exposition only int table1[100]; int *table2 = new int_array[12100]; clear(int_array insert_in_table(table1, 0, 0); // Calls deduce#2 T is int, and that n is 12 |
The function template and specialized function template can be used in a straightforward manner. This example declares an array of 12 integers named int_array
and invokes the clear()
function passing int_array
as an argument. The compiler matches this invocation to inline void clear(T (&array)[n])
because this definition most closely matches the actual argument type of array of int
. The compiler deduces that the type T
is int
and that n
is 12, separating the size n
from the pointer to the array of int
before invoking the function template for clear()
. This use of specialized function templates guarantees that the clear()
function has the correct array size.
Noncompliant Code Example (Vectors)
insert_in_table(table2, 0, 0); // Error, no matching function call
insert_in_table(table1, 100, 0, 0); // Calls #1
insert_in_table(table2, 100, 0, 0); // Calls #1
delete [] table2;
} |
Noncompliant Code Example (std::vector
)
In this noncompliant code example, a std::vector
is used in place of a pointer and size pair. The function performs a range check to ensure that pos
does not exceed the upper bound of the array but fails to check the lower bound for table
. Because pos
has been declared as a (signed) int
, this parameter can assume a negative value, resulting in a write outside the bounds of the std::vector
objectThe above code examples perform the same when working with vectors.
Code Block | ||||
---|---|---|---|---|
| ||||
vector<int>#include table;<vector> intvoid insert_in_table(std::vector<int> &table, int pos, int value) { if (pos >= table._size()) { // Handle error return -1; } table[pos] = value; return 0; } |
The function performs a range check to ensure that pos
does not exceed the upper bound of the array but fails to check the lower bound for table
. Because pos
has been declared as a (signed) int
, this parameter can easily assume a negative value, resulting in a write outside the bounds of the memory referenced by table
.
Compliant Solution (
...
std::vector
, size_t
)
In this compliant solution, the parameter pos
is declared as size_t
, which prevents passing of negative arguments (see INT01-CPP. Use rsize_t or size_t for all integer values representing the size of an object).
Code Block | ||||
---|---|---|---|---|
| ||||
vector<int>#include table;<vector> intvoid insert_in_table(std::vector<int> &table, std::size_t pos, int value) { if (pos >= table._size()) { // Handle error return -1; } table[pos] = value; return 0; } |
Compliant Solution (
...
std::vector::at()
)
In this compliant solution, access to the vector is accomplished with the at()
method. This method provides bounds checking, throwing an out_of_range
exception if pos
is not a valid index value. The insert_in_table()
function is declared with noexcept(false)
in compliance with ERR30-CPP. Try to recover gracefully from unexpected errors.
Code Block | ||||
---|---|---|---|---|
| ||||
vector<int>#include table;<vector> intvoid insert_in_table(std::vector<int> &table, std::size_t pos, int value) noexcept(false) { table.at(pos) = value; } |
Noncompliant Code Example (Iterators)
Page properties | ||
---|---|---|
| ||
This example could use an xref to some (TBD) guideline about type-safe template meta-programming. That would explain the extra efforts involving |
In this noncompliant code example, it is possible that the function is given a valid iterator, but that the iterator is not within a valid range. For instance, if f()
were called with iterators obtained from an empty container, the end()
iterator could be improperly dereferenced.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iterator> template return 0; } <typename ForwardIterator> void f_imp(ForwardIterator B, ForwardIterator E, int Val, std::forward_iterator_tag) { do { *B++ = Val; } while (B != E); } template <typename ForwardIterator> void f(ForwardIterator B, ForwardIterator E, int Val) { typename std::iterator_traits<ForwardIterator>::iterator_category Cat; f_imp(B, E, Val, Cat); } |
Compliant Solution
This compliant solution tests for iterator validity before attempting to dereference the forward iterator:
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iterator>
template <typename ForwardIterator>
void f_imp(ForwardIterator B, ForwardIterator E, int Val, std::forward_iterator_tag) {
while (B != E) {
*B++ = Val;
}
}
template <typename ForwardIterator>
void f(ForwardIterator B, ForwardIterator E, int Val) {
typename std::iterator_traits<ForwardIterator>::iterator_category Cat;
f_imp(B, E, Val, Cat);
} |
Risk Assessment
Using an invalid array or vector container index can result in an arbitrary memory overwrite or abnormal program termination.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
ARR30CTR30-CPP | highHigh | likelyLikely | highHigh | P9 | L2 |
Automated Detection
Tool | Version | Checker | LDRA | 7.6Description | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Coverity | 7.5 | INTEGER_OVERFLOW | Implemented | ||||||||||
Implemented | Klocwork8.0.4.16 | ABR ABV.TAINTED SV.TAINTED.INDEX_ACCESS | Implemented | Compass/ROSE | Implemented | G++ |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the the CERT website.
Other Languages
This rule appears in the C Secure Coding Standard as VOID Guarantee that array indices are within the valid range.
Bibliography
...
Related Guidelines
CERT C Coding Standard | ARR30-C. Do not form or use out-of-bounds pointers or array subscripts |
MITRE CWE | CWE 119, Failure to Constrain Operations within the Bounds of a Memory Buffer |
...
...
...
129, |
...
Improper Validation of Array Index |
Bibliography
[ISO/IEC 14882-2014] | 23, "Containers Library" |
[Viega 05] | Section 5.2.13, "Unchecked Array Indexing" |
[ISO/IEC PDTR 24772] | "XYX Boundary Beginning Violation," "XYY Wrap-around Error," and "XYZ Unchecked Array Indexing" |
array indexing"CTR04-CPP. Assume responsibility for cleaning up data referenced by a container of pointers 06006. Containers (CTR)