Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

When iterating over elements of a container, the iterators used must iterate over a valid range. An iterator range is a pair of iterators that refer to the first and past-the-end elements of the range, respectively.

A valid iterator range is a range where:

Accessing two iterators which do not refer into the same container or accessing invalidated iterators results in undefined behavior.

Several generic STL algorithms, such as std::remove() and std::unique(), remove instances of elements from a container without shrinking the size of the container. Instead, these algorithms return a ForwardIterator to indicate the partition within the container after which elements are no longer valid. The elements in the container that precede the returned iterator are valid elements with specified values, while the elements that succeed the returned iterator are valid but have unspecified values. Accessing unspecified values of elements iterated over results in unspecified behavior. Frequently, the erase-remove idiom is used to shrink the size of the container when using these algorithms.

Noncompliant Code Example

In this noncompliant example, the two iterators that delimit the range point into the same container, but the first iterator doesn't precede the second. On each iteration of its internal loop, std::for_each() compares the first iterator with the second for equality, and as long as they are not equal it will continue to increment the first iterator. Incrementing the iterator representing the past-the-end element of the range results in undefined behavior.

Code Block
bgColor#FFcccc
langcpp
#include <algorithm>
#include <iostream>
#include <vector>
 
void f(const std::vector<int> &C) {
  std::for_each(C.end(), C.begin(), [](int I) { std::cout << I; });
}

Invalid iterator ranges can also result from comparison functions that return true for equal values. See CTR57-CPP. Provide a valid ordering predicate for more information about comparators.

Compliant Solution

In this compliant solution, the iterator values passed to std::for_each() are passed in the proper order:

Code Block
bgColor#ccccff
langcpp
#include <algorithm>
#include <iostream>
#include <vector>
 
void f(const std::vector<int> &C) {
  std::for_each(C.begin(), C.end(), [](int I) { std::cout << I; });
}

Noncompliant Code Example

In this noncompliant code example, iterators from different containers are passed for the same iterator range. While many STL implementations will compile this code and exhibit reasonable behavior, there is no requirement that an STL implementation treat a default-initialized iterator as a synonym for end().

Code Block
bgColor#FFcccc
langcpp
#include <algorithm>
#include <iostream>
#include <vector>
 
void f(const std::vector<int> &C) {
  std::vector<int>::const_iterator E;
  std::for_each(C.begin(), E, [](int I) { std::cout << I; });
}

Compliant Solution

In this compliant solution, the proper iterator generated by a call to end() is passed:

Code Block
bgColor#ccccff
langcpp
#include <algorithm>
#include <iostream>
#include <vector>
 
void f(const std::vector<int> &C) {
  std::for_each(C.begin(), C.end(), [](int I) { std::cout << I; });
}

Noncompliant Code Example

In this noncompliant code example, elements matching 42 are removed from the given container. The contents of the container are then printed to the standard output stream. However, if any elements were removed from the container, the range-based for loop iterates over an invalid iterator range, resulting in unspecified behavior.

Code Block
bgColor#ffcccc
langcpp
#include <algorithm>
#include <iostream>
#include <vector>
 
void f(std::vector<int> &C) {
  std::remove(C.begin(), C.end(), 42);
  for (auto V : C) {
    std::cout << "Container element: " << V << std::endl;
  }
}

Compliant Solution

In this compliant solution, elements removed by the standard algorithm are subsequently erased from the given container. This ensures that valid iterator range is used by the range-based for loop.

Code Block
bgColor#ccccff
langcpp
#include <algorithm>
#include <iostream>
#include <vector>
 
void f(std::vector<int> &C) {
  C.erase(std::remove(C.begin(), C.end(), 42), C.end());
  for (auto V : C) {
    std::cout << "Container element: " << V << std::endl;
  }
}

Risk Assessment

Using an invalid iterator range is similar to allowing a buffer overflow, which can lead to an attacker running arbitrary code.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CTR53-CPP

High

Probable

High

P6

L2

Automated Detection

Tool

Version

Checker

Description

    

Related Vulnerabilities

The fun with erase() article by Chris Rohlf discusses the exploit potential of a program that calls vector::erase() with invalid iterator ranges.

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

Bibliography

[ISO/IEC 14882-2014]

24, "Iterators Library"
25.3, "Mutating Sequence Operations" 

[Meyers 01]Item 32, "Follow remove-like algorithms with erase if you really want to remove something"