Versions Compared

Key

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

Several C Standard Library standard library functions perform bytewise operations on objects. For instance instance, std::memcmp() compares the bytes comprising the object representation of two objects, and std::memcpy() copies the bytes comprising an object representation into a destination buffer. However, for some object types, this it results in undefined or abnormal program behavior.

The C++ Standard, [class], paragraph 6 [ISO/IEC 14882-2014], states the following:

A trivially copyable class is a class that:
  — has no non-trivial copy constructors,
  — has no non-trivial move constructors,
  — has no non-trivial copy assignment operators,
  — has no non-trivial move assignment operators, and
  — has a trivial destructor.
A trivial class is a class that has a default constructor, has no non-trivial default constructors, and is trivially copyable. [Note: In particular, a trivially copyable or trivial class does not have virtual functions or virtual base classes. — end note]

Additionally, the C++ Standard, [class], paragraph 7, states the following:

A standard-layout class is a class that:
  — has no non-static data members of type non-standard-layout class (or array of such types) or reference,
  — has no virtual functions and no virtual base classes,
  — has the same access control for all non-static data members,
  — has no non-standard-layout base classes,
  — either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
  — has no base classes of the same type as the first non-static data member.

Do not use std::memset() to initialize an object of nontrivial class type as it may not properly initialize the value representation of the object. Do not use std::memcpy() (or related bytewise copy functions) to initialize a copy of an object of nontrivial class type, as it may not properly initialize the value representation of the copy. Do not use std::memcmp() (or related bytewise comparison functions) to compare objects of nonstandard-layout class type, as it may not properly compare the value representations of the objects. In all cases, it is best to prefer the alternatives:.

C Standard Library FunctionC++ Equivalent Functionality
std::memset()Class constructor
std::memcpy()
,

std::memmove()
,

std::strcpy() 
Class copy constructor or operator=() 
std::memcmp()
,

std::strcmp()
operator<(), operator>(), operator==(), or operator!=()

Noncompliant Code Example

In this noncompliant code example, a nontrivial class object is initialized by calling its default constructor , but is later reinitialized to its default state using std::memset(). This , which does not properly reinitialize the object, leading . Improper reinitialization leads to class invariants not holding in later uses of the object.

Code Block
bgColor#FFCCCC
langcpp
#include <cstring>
#include <iostream>
 
class C {
  int ScalingFactorscalingFactor;
  int OtherDataotherData;
 
public:
  C() : ScalingFactorscalingFactor(1) {}
  
  void SetOtherDataset_other_data(int Ii);
  int f(int i) {
    return i / ScalingFactorscalingFactor;
  }
  // ...
};
 
void f() {
  C c;
  
  // ... codeCode that mutates c ... 
  
  // Reinitialize c to its default state
  std::memset(&c, 0, sizeof(C));
  
  std::cout << c.f(100) << std::endl;
}

Note that the The above noncompliant code example is compliant with EXP62-CPP. Do not access the bits of an object representation that are not part of the object's value representation because all of the bits in the value representation are also used in the object representation of C.

...

In this compliant solution, the call to std::memset() is replaced with a default-initialized copy-and-swap operation called clear(). This operation ensures that the object is initialized to its default state properly, and it behaves properly for object types that have optimized assignment operators that fail to clear all data members of the object being assigned into.

Code Block
bgColor#ccccff
langcpp
#include <iostream>
#include <utility>
 
class C {
  int ScalingFactorscalingFactor;
  int OtherDataotherData;
 
public:
  C() : ScalingFactorscalingFactor(1) {}
  
  void SetOtherDataset_other_data(int Ii);
  int f(int i) {
    return i / ScalingFactorscalingFactor;
  }
  // ...
};
 
template <typename T>
T& clear(T &o) {
  using std::swap;
  T empty;
  swap(o, empty);
  return o;
}

void f() {
  C c;
  
  // ... codeCode that mutates c ... 
  
  // Reinitialize c to its default state
  clear(c);
  
  std::cout << c.f(100) << std::endl;
}

...

In this noncompliant code example, std::memcpy() is used to create a copy of an object of nontrivial type C. However, because each object instance attempts to delete the int * in C::~C(), this can lead to double-free vulnerabilities may occur because the same pointer value will be copied into c2.

Code Block
bgColor#FFCCCC
langcpp
#include <cstring>
 
class C {
  int *Ii;
 
public:
  C() : Ii(nullptr) {}
  ~C() { delete I; i; }
 
  void set(int val) {
    if (i) { delete i; }
    i = new int{val};
  }
 
  // ...
};
 
void f(C &c1) {
  C c2;
  std::memcpy(&c2, &c1, sizeof(C));  
}

...

In this compliant solution, C defines an assignment operator that is used instead of calling std::memcpy():.

Code Block
bgColor#ccccff
langcpp
class C {
  int *Ii;
 
public:
  C() : Ii(nullptr) {}
  ~C() { delete I; }
 
i; }
 
  void set(int val) {
    if (i) { delete i; }
    i = new int{val};
  }

  C &operator=(const C &RHSrhs) noexcept(false) {
    if (this != &RHSrhs) {
      int *Oo = nullptr;
      if (RHSrhs.Ii) {
        Oo = new int;
        *Oo = *RHSrhs.Ii;
      }
      // Does not modify this unless allocation succeeds.
      delete Ii;
      Ii = O;      o;
    }
    return *this;
  }
 
  // ...
};
 
void f(C &c1) {
  C c2 = c1;
}

...

In this noncompliant code example, std::memcmp() is used to compared two objects of nonstandard-layout type. Because std::memcmp() performs a bytewise comparison of the object representations, if the implementation uses a vtable pointer as part of the object representation, this it will compare vtable pointers. If the dynamic type of either c1 or c2 is a derived class of type C, the comparison may fail despite the value representation of either object.

Code Block
bgColor#FFCCCC
langcpp
#include <cstring>
 
class C {
  int Ii;
 
public:
  virtual void f();
  
  // ...
};
 
void f(C &c1, C &c2) {
  if (!std::memcmp(&c1, &c2, sizeof(C))) {
    // ...
  }
}

Because a vtable is not part of an object's value representation, comparing it with std::memcmp() also violates EXP62-CPP. Do not access the bits of an object representation that are not part of the object's value representation.

Compliant Solution

In this compliant solution, C defines an equality operator that is used instead of calling std::memcmp(). This solution ensures that only the value representation of the objects is considered when performing the comparison.

Code Block
bgColor#ccccff
langcpp
class C {
  int Ii;
 
public:
  virtual void f();
  
  bool operator==(const C &RHSrhs) const {
    return RHSrhs.Ii == Ii;
  }

  // ...
};
 
void f(C &c1, C &c2) {
  if (c1 == c2) {
    // ...
  }
}

...

Most violations of this rule will result in abnormal program behavior. However, overwriting implementation details of the object representation can lead to code execution vulnerabilities.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

OOP57-CPP

High

Probable

High

P6

L2

Automated Detection

Tool

Version

Checker

Description

  

 

Astrée

Include Page
Astrée_V
Astrée_V

stdlib-use-ato
stdlib-use
stdlib-use-getenv
stdlib-use-system
include-time
stdlib-use-string-unbounded
Partially checked
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

BADFUNC.MEMCMP

BADFUNC.MEMSET

Use of memcmp

Use of memset

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C++5017, C++5038
Klocwork
Include Page
Klocwork_V
Klocwork_V

CERT.OOP.CSTD_FUNC_USE 


LDRA tool suite
Include Page
LDRA_V
LDRA_V

44 S

Enhanced Enforcement

Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_CPP-OOP57-a
CERT_CPP-OOP57-b

Do not initialize objects with a non-trivial class type using C standard library functions
Do not compare objects of nonstandard-layout class type with C standard library functions

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: OOP57-CPPChecks for bytewise operations on nontrivial class object (rule fully covered)
PVS-Studio

Include Page
PVS-Studio_V
PVS-Studio_V

V598, V780
RuleChecker
Include Page
RuleChecker_V
RuleChecker_V
stdlib-use-ato
stdlib-use
stdlib-use-getenv
stdlib-use-system
include-time
stdlib-use-string-unbounded

Partially checked
 

Related Vulnerabilities

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

Related Guidelines

Bibliography

[ISO/IEC 14882-2014]Subclause 3.9, "Types"
Subclause 3.10, "Lvalues and Rvalues"
Clause 9, "Classes" 

 


...

Image Modified Image Modified Image Modified