You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 28 Next »

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. Similarly, when two iterators are subtracted (including via std::distance()), both iterators must refer to the same container object, or 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 [rr + n). However, when test does not point within the given range as in this example, it results undefined behavior.

#include <cstddef>
#include <iostream>
 
template <typename Ty>
bool in_range(const Ty *test, const Ty *r, size_t n) {
  return (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, states [ISO/IEC 14882-2014]:

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.

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

#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. This is because the operational semantics of a < b on a random access iterator are: b - a > 0, and >= is implemented in terms of <.

#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<T> is used in place of the < operator. The C++ Standard, [comparisons], paragraph 14, states [ISO/IEC 14882-2014]:

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.

While this approach yields a total ordering, the definition of that total ordering is 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 indeed on common implementations of std::less<T>, may even result in undefined behavior when the < operator is invoked.

#include <functional>
#include <iostream>
 
template <typename Ty>
bool in_range(const Ty *test, const Ty *r, size_t n) {
  std::less<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

In this compliant solution, a fully-portable, but likely inefficient equality comparing test against each possible address in the range [r, n) is used to implement the in_range() function:

#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 *test_ptr = reinterpret_cast<const unsigned char *>(test);
 
  for (; cur != end; ++cur) {
    if (cur == test_ptr) {
      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

Priority

Level

CTR36-CPP

Medium

Probable

Medium

P8

L2

Automated Detection

Tool

Version

Checker

Description

PRQA QA-C++

4.4

2761,2762,2763,2766,

2767,2768,2771,2772,

2773

 

Related Vulnerabilities

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

Related Guidelines

Bibliography

[ISO/IEC 14882-2014]

5.7, "Additive Operators"
5.9, "Relational Operators"
20.9.5, "Comparisons"

[Banahan 03]5.3, "Pointers"
5.7, "Expressions Involving Pointers" 

 

  • No labels