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 standard template library 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 parameters, 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 tableSize
, but fails to check the lower bound. Because pos
is 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 | ||||
---|---|---|---|---|
| ||||
#include <cstddef>
void insert_in_table(int *table, std::size_t tableSize, int pos, int value) {
if (pos >= tableSize) {
// Handle error
return;
}
table[pos] = value;
} |
Compliant Solution (size_t
)
In this compliant solution, the parameter pos
is declared as size_t
, which prevents the passing of negative arguments.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <cstddef> void enum { TABLESIZE = 100 }; int *table = NULL; int insert_in_table(int *table, std::size_t tableSize, std::size_t pos, int value) { if (!tablepos >= tableSize) { // Handle error return; } table[pos] = (int *)malloc(sizeof(int) * TABLESIZE); }value; } |
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 bounds.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <cstddef> #include <new> void insert_in_table(int *table, std::size_t tableSize, std::size_t pos, int value) { // #1 if (pos >= TABLESIZEtableSize) { // Handle error return -1; } table[pos] = value; } template return 0; } <std::size_t N> void insert_in_table(int (&table)[N], std::size_t pos, int value) { // #2 insert_in_table(table, N, pos, value); } void f() { // Exposition only int table1[100]; int *table2 = new int[100]; insert_in_table(table1, 0, 0); // Calls #2 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 The function performs a range check to ensure that that pos
does does not exceed the upper bound of the array but fails to check the lower bound for table
. Because pos
has been container. Because pos
is declared as a (signed) int
long long
, this parameter can easily assume a negative value. On systems where std::vector::size_type
is ultimately implemented as an unsigned int
(such as with Microsoft Visual Studio 2013), the usual arithmetic conversions applied for the comparison expression will convert the unsigned value to a signed value. If pos
has a negative value, this comparison will not fail, resulting in a write outside the bounds of the memory referenced by table
.
...
std::vector
object when the negative value is interpreted as a large unsigned value in the indexing operator.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <vector>
void insert_in_table(std::vector<int> &table, long long pos, int value) {
if (pos >= table.size()) {
// Handle error
return;
}
table[pos] = value;
}
|
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)ensures that the comparison expression will fail when a large, positive value (converted from a negative argument) is given.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <vector>
void insert_in_table(std::vector<int> &table, std::size_t pos, int value) {
if (pos >= table.size()) {
// Handle error
return;
}
table[pos] = value;
}
|
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 a std::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 ERR55-CPP. Honor exception specifications.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <vector> void enum { TABLESIZE = 100 }; int *table = NULL; int 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, the f_imp()
function is given the (correct) ending iterator e
for a container, and b
is an iterator from the same container. However, it is possible that b
is not within the valid range of its container. For instance, if the container were empty, b
would equal e
and be improperly dereferenced.
Code Block | ||||
---|---|---|---|---|
| ||||
#include (!table<iterator> template <typename ForwardIterator> void f_imp(ForwardIterator b, ForwardIterator e, int val, std::forward_iterator_tag) { do table{ *b++ = (int *)mallocval; } 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 b.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iterator> template <typename ForwardIterator> void f_imp(ForwardIterator b, ForwardIterator e, int val, std::forward_iterator_tag) { while (b != e(sizeof(int) * TABLESIZE); } if (pos >= TABLESIZE) { return -1*b++ = val; } } template <typename ForwardIterator> void f(ForwardIterator b, ForwardIterator e, int val) { table[pos] = valuetypename std::iterator_traits<ForwardIterator>::iterator_category cat; return 0f_imp(b, e, val, cat); } |
Risk Assessment
Using an invalid array or container index can result in an arbitrary memory overwrite or abnormal program termination.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|
CTR50-CPP |
High |
Likely |
High | P9 | L2 |
Automated Detection
Tool |
---|
The LDRA tool suite Version 7.6.0 can detect violations of this rule.
Klocwork Version 8.0.4.16 can detect violations of this rule with the ABR, ABV.TAINTED, and SV.TAINTED.INDEX_ACCESS checkers.
Compass/ROSE can detect some violations of this rule. In particular, if a signed index to an array is being verified, it ensures that the value is also compared against 0.
Version | Checker | Description | |||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| overflow_upon_dereference | |||||||
CodeSonar |
| LANG.MEM.BO | Buffer overrun | ||||||
Helix QAC |
| C++3139, C++3140 DF2891 | |||||||
Klocwork |
| ABV.ANY_SIZE_ARRAY ABV.GENERAL ABV.GENERAL.MULTIDIMENSION ABV.STACK ABV.TAINTED SV.TAINTED.ALLOC_SIZE SV.TAINTED.CALL.INDEX_ACCESS SV.TAINTED.CALL.LOOP_BOUND SV.TAINTED.INDEX_ACCESS | |||||||
LDRA tool suite |
| 45 D, 47 S, 476 S, 489 S, 64 X, 66 X, 68 X, 69 X, 70 X, 71 X, 79 X | Partially implemented | ||||||
Parasoft C/C++test |
| CERT_CPP-CTR50-a | Guarantee that container indices are within the valid range | ||||||
Polyspace Bug Finder |
| CERT C++: CTR50-CPP | Checks for:
Rule partially covered. | ||||||
PVS-Studio |
| V781 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the the CERT website.
Other Languages
...
Related Guidelines
...
References
...
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 CWE 129, Improper Validation of Array Index |
Bibliography
[ISO/IEC 14882-2014] | Clause 23, "Containers Library" |
[ISO/IEC TR 24772-2013] | Boundary Beginning Violation [XYX] Wrap-Around Error [XYY] Unchecked Array Indexing [XYZ] |
[Viega 2005] | Section |
...
5.2.13, |
...
"Unchecked Array Indexing" |
...
array indexing"ARR02-CPP. Explicitly specify array bounds, even if implicitly defined by an initializer 06. Arrays (ARR) ARR31-CPP. Use consistent array notation across all source files