Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: REM Cost Reform

When two pointers are subtracted, both must point to elements of the same array object or to one past the last element of the array object; the result is the difference of the subscripts of the two array elements. LikewiseSimilarly, when two iterators are subtracted (including via std::distance()), both iterators must refer to elements in the same container , or to the value returned by the end() method of the container. This restriction exists because pointer subtraction in C++ produces the number of objects between the two pointers, not the number of bytes.

Similarly, comparing pointers gives the positions of the pointers relative to each other within the array. Subtracting or comparing pointers that do not refer to the same array can result in erroneous behavior.

It is acceptable to subtract or compare two member pointers within a single struct object, suitably cast, because any object can be treated as an array of unsigned char. However, when doing so remember to account for the effects of alignment and padding on the structure.

Comparison of pointers using == or != is always permitted; this rule only applies to the inequality operators (< <= > >=), as well as pointer subtraction.

Noncompliant Code Example (Arrays)

object or must be obtained via a call to end() (or cend()) on the same container object.

If two unrelated iterators (including pointers) are subtracted, the operation results in undefined behavior [ISO/IEC 14882-2014]. Do not subtract two iterators (including pointers) unless both point into the same container or one past the end of the same container.

Noncompliant Code Example

This noncompliant code example attempts to determine whether the pointer test is within the range [r, r + n]. However, when test does not point within the given range, as in this example, the subtraction produces undefined behaviorIn this noncompliant code example pointer subtraction is used to determine how many free elements are left in the nums array.

Code Block
bgColor#ffcccc
langcpp
int nums[SIZE];
char *strings[SIZE];
int *next_num_ptr = nums;
int free_bytes;

/* increment next_num_ptr as array fills */

free_bytes = strings - (char **)next_num_ptr;

The first incorrect assumption is that nums and strings arrays are necessarily contiguous in memory. The second is that free_bytes is the number of bytes available. The subtraction returns the number of elements between next_num_ptr and strings.

Compliant Solution (Arrays)

In this compliant solution, the number of free elements is kept as a counter and adjusted on every array operation. It is also calculated in terms of free elements instead of bytes. This prevents further mathematical errors.

Code Block
bgColor#ccccff
langcpp
int nums[SIZE];
char *strings[SIZE];
int *next_num_ptr = nums;
int free_bytes;

/* increment next_num_ptr as array fills */

free_bytes = (&(nums[SIZE]) - next_num_ptr) * sizeof(int);

Noncompliant Code Example (Vectors)

#include <cstddef>
#include <iostream>
 
template <typename Ty>
bool in_range(const Ty *test, const Ty *r, size_t n) {
  return 0 < (test - r) && (test - r) < (std::ptrdiff_t)n;
}
 
void f() {
  double foo[10];
  double *x = &foo[0];
  double bar;
  std::cout << std::boolalpha << in_range(&bar, x, 10);
}

Noncompliant Code Example

In this noncompliant code example, the in_range() function is implemented using a comparison expression instead of subtraction. The C++ Standard, [expr.rel], paragraph 4 [ISO/IEC 14882-2014], states the following:

If two operands p and q compare equal, p<=q and p>=q both yield true and p<q and p>q both yield false. Otherwise, if a pointer p compares greater than a pointer q, p>=q, p>q, q<=p, and q<p all yield true and p<=q, p<q, q>=p, and q>p all yield false. Otherwise, the result of each of the operators is unspecified.

Page properties
hiddentrue

The "Thus" statement below is incomplete. What is required to make such a statement is p3, which is a total mess because it doesn't say WHAT should happen when two pointers not of the same container are compared. The assumption is that this is what causes us to fall into the final "otherwise" clause of p4, but I think a core issue may be in order. Once that is resolved, we can update this section accordingly.

Thus, comparing two pointers that do not point into the same container or one past the end of the container results in unspecified behavior. Although the following example is an improvement over the previous noncompliant code example, it does not result in portable code and may fail when executed on a segmented memory architecture (such as some antiquated x86 variants). Consequently, it is noncompliant.

Code Block
bgColor#ffcccc
langcpp
#include <iostream>
 
template <typename Ty>
bool in_range(const Ty *test, const Ty *r, size_t n) {
  return test >= r && test < (r + n);
}
 
void f() {
  double foo[10];
  double *x = &foo[0];
  double bar;
  std::cout << std::boolalpha << in_range(&bar, x, 10);
}

Noncompliant Code Example

This noncompliant code example is roughly equivalent to the previous example, except that it uses iterators in place of raw pointers. As with the previous example, the in_range_impl() function exhibits unspecified behavior when the iterators do not refer into the same container because the operational semantics of a < b on a random access iterator are b - a > 0, and >= is implemented in terms of <In this noncompliant code example two iterators are subtracted that don't refer to the same vector.

Code Block
bgColor#ffcccc
langcpp
vector<int> nums1(10, 0);
vector<int> nums2(10, 0);
vector<int>::iterator i1 = nums1.begin();
vector<int>::iterator i2 = nums2.begin();

int distance = i2 - i1;

Since C++ vector iterators are usually implemented as pointers to contiguous memory on the heap, much like array pointers, one might expect distance to have a value of 10. However the C++ standard leaves this as undefined. On Linux with G++ 4.3.2, distance receives an unexpected value of 12.

Compliant Solution (Vectors)

#include <iostream>
#include <iterator>
#include <vector>

template <typename RandIter>
bool in_range_impl(RandIter test, RandIter r_begin, RandIter r_end, std::random_access_iterator_tag) {
  return test >= r_begin && test < r_end;
}
 
template <typename Iter>
bool in_range(Iter test, Iter r_begin, Iter r_end) {
  typename std::iterator_traits<Iter>::iterator_category cat;
  return in_range_impl(test, r_begin, r_end, cat);
}
 
void f() {
  std::vector<double> foo(10);
  std::vector<double> bar(1);
  std::cout << std::boolalpha << in_range(bar.begin(), foo.begin(), foo.end());
}

Noncompliant Code Example

In this noncompliant code example, std::less<> is used in place of the < operator. The C++ Standard, [comparisons], paragraph 14 [ISO/IEC 14882-2014], states the following:

For templates greater, less, greater_equal, and less_equal, the specializations for any pointer type yield a total order, even if the built-in operators <, >, <=, >= do not.

Although this approach yields a total ordering, the definition of that total ordering is still unspecified by the implementation. For instance, the following statement could result in the assertion triggering for a given, unrelated pair of pointers, a and b: assert(std::less<T *>()(a, b) == std::greater<T *>()(a, b));. Consequently, this noncompliant code example is still nonportable and, on common implementations of std::less<>, may even result in undefined behavior when the < operator is invoked.

Code Block
bgColor#ffcccc
langcpp
#include <functional>
#include <iostream>
 
template <typename Ty>
bool in_range(const Ty *test, const Ty *r, size_t n) {
  std::less<const Ty *> less;
  return !less(test, r) && less(test, r + n);
}
 
void f() {
  double foo[10];
  double *x = &foo[0];
  double bar;
  std::cout << std::boolalpha << in_range(&bar, x, 10);
}

Compliant Solution

This compliant solution demonstrates a fully portable, but likely inefficient, implementation of in_range() that compares test against each possible address in the range [r, n]. A compliant solution that is both efficient and fully portable is currently unknownIn this compliant solution, both iterators point to the same vector, and the distance variable receives the expected value of 10.

Code Block
bgColor#ccccff
langcpp
vector<int> nums1(10, 0);
vector<int> nums2(10, 0);
vector<int>::iterator i1 = nums1.begin();
vector<int>::iterator i2 = nums1.end();

int distance = i2 - i1;
#include <iostream>
 
template <typename Ty>
bool in_range(const Ty *test, const Ty *r, size_t n) {
  auto *cur = reinterpret_cast<const unsigned char *>(r);
  auto *end = reinterpret_cast<const unsigned char *>(r + n);
  auto *testPtr = reinterpret_cast<const unsigned char *>(test);
 
  for (; cur != end; ++cur) {
    if (cur == testPtr) {
      return true;
    }
  }
  return false;
}
 
void f() {
  double foo[10];
  double *x = &foo[0];
  double bar;
  std::cout << std::boolalpha << in_range(&bar, x, 10);
}

Risk Assessment

Rule

Severity

Likelihood

Remediation Cost

Detectable

Repairable

Priority

Level

ARR36-CPP

medium

probable

medium

P8

L2

CTR54-CPP

Medium

Probable

No

No

P4

L3

Automated Detection

Tool

Version

Checker

Description

Astrée

Include Page
Astrée_V
Astrée_V

invalid_pointer_subtraction
invalid_pointer_comparison

CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

LANG.STRUCT.CUP
LANG.STRUCT.SUP

Comparison of Unrelated Pointers
Subtraction of Unrelated Pointers

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

DF2668, DF2761, DF2762, DF2763, DF2766, DF2767, DF2768


LDRA tool suite
Include Page
LDRA_V
LDRA_V

70 S, 87 S, 437 S, 438 S

Enhanced Enforcement

Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_CPP-CTR54-a
CERT_CPP-CTR54-b
CERT_CPP-CTR54-c

Do not compare iterators from different containers
Do not compare two unrelated pointers
Do not subtract two pointers that do not address elements of the same array

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: CTR54-CPPChecks for subtraction or comparison between iterators from different containers (rule partially covered).

Related Vulnerabilities

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

Other Languages

...

Related Guidelines

...

MITRE CWECWE-469, Use of Pointer Subtraction to Determine Size

Bibliography

...

2003]Section 5.3, "Pointers

...

"

...


Section 5.7, "Expressions

...

Involving Pointers" 
[ISO/IEC 14882-

...

...

Subclause 5.7, "Additive

...

Operators"
Subclause 5.9, "Relational Operators"
Subclause 20.9.5, "Comparisons"


...

Image Added Image Added Image Added [MITRE 07] CWE ID 469, "Use of Pointer Subtraction to Determine Size"ARR35-CPP. Do not allow loops to iterate beyond the end of an array or container      06. Arrays and the STL (ARR)      ARR37-CPP. Do not add or subtract an integer to a pointer to a non-array object