Main Content

CERT C++: CTR50-CPP

Guarantee that container indices and iterators are within the valid range

Description

Rule Definition

Guarantee that container indices and iterators are within the valid range.1

Polyspace Implementation

The rule checker checks for these issues:

  • Array access out of bounds

  • Array access with tainted index

  • Pointer dereference with tainted offset

  • Potentially negative container index

Extend Checker

A default Bug Finder analysis might not flag an Array access out of bounds issue when the input values are unknown and only a subset of inputs cause the issue. To check for the Array access out of bounds issue caused by specific system input values, run a stricter Bug Finder analysis. See Extend Bug Finder Checkers to Find Defects from Specific System Input Values.

A default Bug Finder analysis might not flag an Array access with tainted index or Pointer dereference with tainted offset issue for certain inputs that originate outside of the current analysis boundary. See Sources of Tainting in a Polyspace Analysis. To consider any data that does not originate in the current scope of Polyspace analysis as tainted, use the command line option -consider-analysis-perimeter-as-trust-boundary.

Examples

expand all

Issue

Array access out of bounds occurs when an array index falls outside the range [0...array_size-1] during array access.

Risk

Accessing an array outside its bounds is undefined behavior. You can read an unpredictable value or try to access a location that is not allowed and encounter a segmentation fault.

Fix

The fix depends on the root cause of the defect. For instance, you accessed an array inside a loop and one of these situations happened:

  • The upper bound of the loop is too large.

  • You used an array index that is the same as the loop index instead of being one less than the loop index.

To fix the issue, you have to modify the loop bound or the array index.

Another reason why an array index can exceed array bounds is a prior conversion from signed to unsigned integers. The conversion can result in a wrap around of the index value, eventually causing the array index to exceed the array bounds.

Often the result details show a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the result details do not show the event history, you can trace back using right-click options in the source code and see previous related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface.

See examples of fixes below.

If you do not want to fix the issue, add comments to your result or code to avoid another review. See:

Example - Array Access Out of Bounds Error
#include <stdio.h>

void fibonacci(void)
{
    int i;
    int fib[10];
 
    for (i = 0; i < 10; i++) 
       {
        if (i < 2) 
            fib[i] = 1;
         else 
            fib[i] = fib[i-1] + fib[i-2];
       }

    printf("The 10-th Fibonacci number is %i .\n", fib[i]);  //Noncompliant
}

The array fib is assigned a size of 10. An array index for fib has allowed values of [0,1,2,...,9]. The variable i has a value 10 when it comes out of the for-loop. Therefore, the printf statement attempts to access fib[10] through i.

Correction — Keep Array Index Within Array Bounds

One possible correction is to print fib[i-1] instead of fib[i] after the for-loop.

#include <stdio.h>

void fibonacci(void)
{
   int i;
   int fib[10];

   for (i = 0; i < 10; i++) 
    {
        if (i < 2) 
            fib[i] = 1;
        else 
            fib[i] = fib[i-1] + fib[i-2];
    }

    /* Fix: Print fib[9] instead of fib[10] */
    printf("The 10-th Fibonacci number is %i .\n", fib[i-1]); 
}

The printf statement accesses fib[9] instead of fib[10].

Issue

Array access with tainted index detects reading or writing to an array by using a tainted index that has not been validated.

Risk

The index might be outside the valid array range. If the tainted index is outside the array range, it can cause:

  • Buffer underflow/underwrite — writing to memory before the beginning of the buffer.

  • Buffer overflow — writing to memory after the end of a buffer.

  • Over-reading a buffer — accessing memory after the end of the targeted buffer.

  • Under-reading a buffer, or accessing memory before the beginning of the targeted buffer.

An attacker can use an invalid read or write operation create to problems in your program.

Fix

Before using the index to access the array, validate the index value to make sure that it is inside the array range.

Example - Use Index to Return Buffer Value
#include <stdlib.h>
#include <stdio.h>
#define SIZE100 100
extern int tab[SIZE100];
static int tainted_int_source(void) {
  return strtol(getenv("INDEX"),NULL,10);
}
int taintedarrayindex(void) {
	int num = tainted_int_source();
    return tab[num];  //Noncompliant
}

In this example, the index num accesses the array tab. The index num is obtained from an unsecure source and the function taintedarrayindex does not check to see if num is inside the range of tab.

Correction — Check Range Before Use

One possible correction is to check that num is in range before using it.

#include <stdlib.h>
#include <stdio.h>
#define SIZE100 100
extern int tab[SIZE100];
static int tainted_int_source(void) {
	return strtol(getenv("INDEX"),NULL,10);
}
int taintedarrayindex(void) {
	int num = tainted_int_source();
	if (num >= 0 && num < SIZE100) {
		return tab[num]; 
	} else {
		return -1;
	}
}

Issue

Pointer dereference with tainted offset detects pointer dereferencing, either reading or writing, using an offset variable from an unknown or unsecure source.

This check focuses on dynamically allocated buffers. For static buffer offsets, see Array access with tainted index.

Risk

The index might be outside the valid array range. If the tainted index is outside the array range, it can cause:

  • Buffer underflow/underwrite, or writing to memory before the beginning of the buffer.

  • Buffer overflow, or writing to memory after the end of a buffer.

  • Over reading a buffer, or accessing memory after the end of the targeted buffer.

  • Under-reading a buffer, or accessing memory before the beginning of the targeted buffer.

An attacker can use an invalid read or write to compromise your program.

Fix

Validate the index before you use the variable to access the pointer. Check to make sure that the variable is inside the valid range and does not overflow.

Example - Dereference Pointer Array
#include <stdio.h>
#include <stdlib.h>
enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};
extern void read_pint(int*);

int taintedptroffset(void) {
    int offset;
    scanf("%d",&offset);
    int* pint = (int*)calloc(SIZE10, sizeof(int));
    int c = 0;
    if(pint) {
        /* Filling array */
        read_pint(pint);
        c = pint[offset]; //Noncompliant
        free(pint);
    }
    return c;
}

In this example, the function initializes an integer pointer pint. The pointer is dereferenced using the input index offset. The value of offset could be outside the pointer range, causing an out-of-range error.

Correction — Check Index Before Dereference

One possible correction is to validate the value of offset. Continue with the pointer dereferencing only if offset is inside the valid range.

#include <stdlib.h>
#include <stdio.h>
enum {
    SIZE10  =  10,
    SIZE100 = 100,
    SIZE128 = 128
};
extern void read_pint(int*);

int taintedptroffset(void) {
    int offset;
    scanf("%d",&offset);
    int* pint = (int*)calloc(SIZE10, sizeof(int));
    int c = 0;
    if (pint) {
        /* Filling array */
        read_pint(pint);
        if (offset>0 && offset<SIZE10) {
            c = pint[offset];
        }
        free(pint);
    }
    return c;
}
Issue

The issue Potentially negative container index occurs when:

  • You use a potentially negative index to access a standard template library (STL) container that provides random access by using operator[]. For example:

    std::vector v<int>;
    int index;
    //index becomes negative
    v[index]; // index is implicitly cast into unsigned int(index)

  • You explicitly cast a potentially negative value into an unsigned integer and use the converted value as the index of an STL container. For instance:

    std::vector v<int>;
    int index;
    //index  becomes negative
    v[static_cast<size_t>(index)]; // size_t has an unsigned type

This issue does not apply to associative containers.

Risk

STL containers expect an unsigned index. To use a signed integer as an index, the index must be cast to an unsigned integer type, either explicitly or implicitly.

If a negative index is cast to an unsigned index, the resulting unsigned value might be larger than the container expects. Using such a value as the index of an STL container results in an out-of-bounds access. For instance, in the preceding code, if index is -1, the index of the vector v might become UINT_MAX after an implicit or explicit cast. The unexpectedly large value of index makes the access to v[index]out of bounds.

Fix

To fix this issue, check the index of the STL container for negative values before accessing the container. Alternatively, use functions that perform range checking, such as the at() method of STL containers.

Example — Potentially Negative Index Causes Out of Bound Access

In this example, the function insert_at() obtains the value of index by calling the external function getIndex(). The function insert_at() checks if the value of index is greater than a valid size, but does not check for a negative index value. Polyspace® reports two violations of this rule in this code:

#include <array>
typedef long long LL;
extern LL getIndex();
void insert_at(std::array<int,1024>& container, LL index, int value)
{
	index = getIndex();
	if (index >= container.size())
	{
		// Handle error
		return;
	}

	container[index] = value;  //Noncompliant- a negative index might be 
                                 // implicitly converted to size_t

	container[static_cast<size_t>(index)] = value; // Noncompliant - a negative 
                                                     // index might be converted to size_t
}
Correction — Use Unsigned Types as Container Index

To fix this issue, declare index as an unsigned integer. Explicitly cast the value getIndex() returns into size_t and check for large values. In this code, if getIndex() returns a negative value, index becomes unexpectedly large because of the explicit cast. The large value triggers the if() condition and the function returns before the defect.

#include <array>
typedef long long LL;
typedef unsigned long long ULL;
extern LL getIndex();
void insert_at(std::array<int,1024>& container, ULL index, int value)
{
	index = static_cast<ULL>(getIndex());
	if (index >= container.size())
	{
		// Handle error
		return;
	}

	container[index] = value;  //Compliant
	container[static_cast<size_t>(index)] = value; //Compliant
}
Correction — Use at() Method

The at() method of STL containers performs bounds checking. In this code, if index becomes unexpectedly large after a cast from signed to unsigned integer, container.at(index) raises an std::out_of_range exception.

#include <array>
typedef long long LL;
extern LL getIndex();
void insert_at(std::array<int,1024>& container, LL index, int value)
{
	index = getIndex();
	if (index >= container.size())
	{
		// Handle error
		return;
	}
    try{
	container.at(index) = value;  //Compliant
	container.at(static_cast<size_t>(index)) = value; //Compliant
    }catch(std::exception& e){
        // check for std::out_of_range
    }
}

Check Information

Group: 04. Containers (CTR)

Version History

Introduced in R2019a

expand all


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.