Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Page properties
hiddentrue

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 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
bgColor#ffcccc
langcpp
#include <cstddef>
 
void insert_in_table(
enum { TABLESIZE = 100 };

int *table = NULL;

int insert_in_table(, std::size_t tableSize, int pos, int value) {
  if (!table) {
    table = new int[TABLESIZE];pos >= tableSize) {
  }
  if// (pos >= TABLESIZE) {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 (

...

size_t)

In this compliant solution, the parameter pos is declared as size_t, which prevents passing the 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
bgColor#ccccff
langcpp
#include <cstddef>
 
void insert_in_table(
enum { TABLESIZE = 100 };

int *table = NULL;

int insert_in_table(, std::size_t tableSize, std::size_t pos, int value) {
  if (!table) {
    table = new int[TABLESIZE];pos >= tableSize) {
  }
  if// (pos >= TABLESIZE) {Handle error
    return -1;
  }
  table[pos] = value;
  return 0;
}

Compliant Solution (

...

Non-Type Templates)

Page properties
hiddentrue

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 Wiki MarkupSpecialized 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
bgColor#ccccff
langcpp
#include <cstddef>
template <typename T>#include <new>

void clear(T array[], insert_in_table(int *table, std::size_t n tableSize, std::size_t pos, int value) { // #1
  forif (size_t i = 0; i < n; ++i) {
    array[ipos >= tableSize) {
    // Handle error
    return;
  }
  table[pos] = 0value;
  }
}

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);
}
 
int int_array[12];

clear(int_array);void f() {
  // deduceExposition Tonly
 is int, and that n is 12

Wiki Markup
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)

The above code examples perform the same when working with vectors.

 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 function performs a range check to ensure that pos does not exceed the upper bound of the container. Because pos is declared as a (signed) long long, this parameter can 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 std::vector object when the negative value is interpreted as a large unsigned value in the indexing operator.

Code Block
bgColor#ffcccc
langcpp
#include <vector>
 
void insert_in_table(std::vector<int> &table, long long
Code Block
bgColor#ffcccc

vector<int> table;

int insert_in_table(int pos, int value) {
  if (pos >= table.size()) {
    return -1// Handle error
    return;
  }
  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)ensures that the comparison expression will fail when a large, positive value (converted from a negative argument) is given.

Code Block
bgColor#ccccff
langcpp
#include <vector>
vector<int> table;

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 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
bgColor#ccccff
langcpp
#include <vector>
vector<int> table;

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
hiddentrue

This example could use an xref to some (TBD) guideline about type-safe template meta-programming. That would explain the extra efforts involving f_imp().

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
bgColor#ffcccc
langcpp
#include <iterator>
 
template <typename ForwardIterator>
void f_imp(ForwardIterator b, ForwardIterator e, int val, std::forward_iterator_tag) {
  do {
    *b++ = val;
  } while (b return!= 0e);
}

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
bgColor#ccccff
langcpp
#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

ARR30

CTR50-CPP

high

High

likely

Likely

high

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

Include Page
Astrée_V
Astrée_V

overflow_upon_dereference
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

LANG.MEM.BO
LANG.MEM.BU
LANG.MEM.TO
LANG.MEM.TU
LANG.MEM.TBA
LANG.STRUCT.PBB
LANG.STRUCT.PPE
LANG.STRUCT.PARITH

Buffer overrun
Buffer underrun
Type overrun
Type underrun
Tainted buffer access
Pointer before beginning of object
Pointer past end of object
Pointer Arithmetic

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C++3139, C++3140

DF2891


Klocwork
Include Page
Klocwork_V
Klocwork_V
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
Include Page
LDRA_V
LDRA_V

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
Include Page
Parasoft_V
Parasoft_V
CERT_CPP-CTR50-a
Guarantee that container indices are within the valid range
Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: CTR50-CPP

Checks for:

  • Array access out of bounds
  • Array access with tainted index
  • Pointer dereference with tainted offset

Rule partially covered.

PVS-Studio

Include Page
PVS-Studio_V
PVS-Studio_V

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 CWECWE 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"
Subclause 24.2.1, "In General" 

[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"


...

Image Added Image Added Image Added array indexing"ARR02-CPP. Explicitly specify array bounds, even if implicitly defined by an initializer      06. Arrays and the STL (ARR)      ARR31-CPP. Use consistent array notation across all source files