CERT.CTR.PTR_ARITH_POLYMORPHIC

Do not use pointer arithmetic on polymorphic objects.

The CERT.CTR.PTR_ARITH_POLYMORPHIC checker detects pointer arithmetic and array subscripting through a base-class pointer when the underlying array stores derived-class objects. Because pointer arithmetic uses the declared pointee type, the code can compute the wrong element address and invoke undefined behavior.

Vulnerability and risk

When code treats an array of derived-class objects as though it were an array of base-class objects, incrementing the base-class pointer or using array subscripting steps through memory by the wrong element size. This can access the wrong memory location and lead to undefined behavior.

Mitigation and prevention

Avoid pointer arithmetic and array subscripting through base-class pointers when the underlying array stores derived-class objects. If you need heterogeneous polymorphic objects, store pointers, such as Base*, instead of value objects in the array or container.

Vulnerable code example

Copy
#include <cstddef>
  #include <iostream>

  struct S {
    int i;
    virtual ~S() = default;
  };

  struct T : S {
    double d;
  };

  void f(const S *someSes, std::size_t count)
  {
    for (const S *end = someSes + count; someSes != end; ++someSes) {
      std::cout << someSes->i << '\n';
    }
  }

  int main()
  {
    T test[5];
    f(test, 5); /* Noncompliant */
}

In this noncompliant example, f() accepts a pointer to S, but the caller passes an array of T. The loop performs pointer arithmetic on S* even though the underlying array elements are T, so the code has undefined behavior.

Fixed code example

Copy
#include <cstddef>
  #include <iostream>

  struct S {
    int i;
    virtual ~S() = default;
  };

  struct T : S {
    double d;
  };

  void f(S *const *someSes, std::size_t count)
  {
    for (S *const *end = someSes + count; someSes != end; ++someSes) {
      std::cout << (*someSes)->i << '\n';
    }
  }

  int main()
  {
    T first;
    T second;
    S *test[] = { &first, &second };
    f(test, 2);
}

In the compliant solution, the array stores pointers instead of derived-class objects by value. Pointer arithmetic now applies to elements of uniform size, so iterating through the array does not reinterpret a derived-class array as a base-class array.

External guidance