Versions Compared

Key

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

Standard-layout types can be used to communicate with code written in other programming languages, as the layout of the type is strictly specified. The C++ Standard, [class], paragraph 7 [ISO/IEC 14882-2014], defines a standard-layout classes class as ones a class that

  • Do does not have virtual functions,
  • Have has the same access control for all nonstatic data members,
  • Have has no base classes of the same type as the first nonstatic data member,
  • Have has nonstatic data members declared in only one class within the class hierarchy, and
  • Recursivelyrecursively, do does not have nonstatic data members of nonstandard-layout type.

An execution boundary is the delimitation between code compiled by differing compilers, including different versions of a compiler produced by the same vendor. For instance, a function may be declared in a header file but defined in a library that is loaded at runtime. The execution boundary is exists between the call site in the executable and the function implementation in the library. Such boundaries are also called ABI (application binary interface) boundaries because they relate to the interoperability of application binaries.

Assumptions must not be made concerning Do not make any assumptions about the specific layout of objects with nonstandard-layout types. For objects compiled by one compiler that are referenced by code compiled by a different compiler, such assumptions cause correctness and portability concerns. The layout of the object generated by the first compiler is not guaranteed to be identical to the layout generated by the second compiler, even if both compilers are conforming C++ implementations. However, some implementations may document binary compatibility guarantees that can be relied on for passing nonstandard-layout objects between execution boundaries.

A special instance of this guidance involves non-C++ code compiled by a different compiler, such as C standard library implementations that are exposed via the C++ standard library. C standard library functions are exposed with C++ signatures, and the type system frequently assists in ensuring that types match appropriately. This process disallows passing a pointer to a C++ object to a function expecting a char * without additional work to suppress the type mismatch. However, some C standard library functions accept a void * for which any C++ pointer type will suffice. Passing a pointer to a nonstandard-layout type in this situation results in indeterminate behavior because it depends on the behavior of the other language as well as on the layout of the given object. For more information, see rule EXP56-CPP. Do not call a function with a mismatched language linkage.

Pass a nonstandard-layout type object across execution boundaries only when only when both sides of the execution boundary adhere to the same ABI—for instance, ABI. This is permissible if the same version of the a compiler is used to compile both sides of the execution boundary, or if the compiler used to compile both sides of the execution boundary is ABI-compatible across multiple versions, or if the differing compilers used document that they adhere to the same ABI.

Noncompliant Code Example

In this This noncompliant code example , a nonstandard-layout type object is passed across execution boundaries. The object type is defined in a header file used by both a library and an application. The application creates an instance of the objectassumes that there is a library whose header is library.h, an application (represented by application.cpp), and that the library and application are not ABI-compatible. Therefore, the contents of library.h constitute an execution boundary. A nonstandard-layout type object S is passed across this execution boundary. The application creates an instance of an object of this type, then passes a reference to the object to a function defined by the library. However, if , crossing the execution boundary. Because the layout is not guaranteed to be compatible across execution boundaries because the compilers do not conform to the same ABI, it is a portability issue likely resulting the boundary, this results in unexpected behavior.

Code Block
bgColor#FFCCCC
langcpp
// library.h
struct S {
  virtual void f() { /* ... */ }
};
 
void func(S &s); // Implemented by the library, calls S::f()
 
// application.cpp
#include "library.h"
 
void fg() {
  S s;
  func(s);
}

Compliant Solution

If This example would be compliant if the library and the application conform conformed to the same ABI, either explicitly through vendor documentation or implicitly by virtue of using the same compiler version to compile both the library and the application, then the noncompliant code example is instead a compliant solution. However, if .

 Compliant Solution

Because the library and application do not conform to the same ABI, the this compliant solution requires modification of modifies the library and application to work with a standard-layout type. Furthermore, it also adds a static_assert() to help guard against future code changes that accidentally modify S to no longer be a standard-layout type.

Page properties
hiddentrue

This CS is incredibly unsatisfying, but is still true.

Code Block
bgColor#ccccff
langcpp
// library.h
#include <type_traits>

struct S {
  void f() { /* ... */ } // No longer virtual
};
static_assert(std::is_standard_layout<S>::value, "S is required to be a standard layout type");

void func(S &s); // Implemented by the library, calls S::f()

// application.cpp
#include "library.h"

void fg() {
  S s;
  func(s);
}

Noncompliant Code Example

In this noncompliant code example,  std::memset() is used to clear the internal state of an object that is not of a standard-layout type. An implementation may store a vtable within the object instance, which is subsequently overwritten by the call to std::memset(), leading to undefined behavior when virtual method dispatch is required.

Code Block
bgColor#FFCCCC
langcpp
#include <cstring>

struct S {
  int i, j, k;
 
  // ...

  virtual void f();
};

void f() {
  S *s = new S;
  // ...
  std::memset(s, 0, sizeof(S));
  // ...
  s->f();
}

Compliant Solution

In this compliant solution, the data members of S are cleared explicitly instead of calling std::memset():

Code Block
bgColor#ccccff
langcpp
struct S {
  int i, j, k;
 
  // ...

  virtual void f();
  void clear() { i = j = k = 0; }
};

void f() {
  S *s = new S;
  // ...
  s->clear();
  // ...
  s->f();
}

Noncompliant Code Example

In this noncompliant code example, a pointer to an object of nonstandard-layout type is passed to a function that has a "Fortran" language linkage. Language linkages other than "C" and "C++" are conditionally supported with implementation-defined semantics [ISO/IEC 14882-2014]. If the implementation does not support this language linkage, the code is ill-formed. Assuming that the language linkage is supported, any operations performed on the object passed may result in indeterminate behavior, which could have security implications.

Code Block
bgColor#FFCCCC
langcpp
struct B {
  int i, j;
};
 
struct D : B {
  float f;
};
 
extern "Fortran" void func(void *);
 
void ffoo(D *d) {
  func(d);
}

Compliant Solution

In this compliant solution, the nonstandard-layout type object is serialized into a local standard-layout type object, which is then passed to the Fortran function:.

Code Block
bgColor#ccccff
langcpp
struct B {
  int i, j;
};

struct D : B {
  float f;
};

extern "Fortran" void func(void *);

void ffoo(D *d) {
  struct {
    int i, j;
    float f;
  } temp;
 
  temp.i = d->i;
  temp.j = d->j;
  temp.f = d->f;

  func(&temp);
}

...

The effects of passing objects of nonstandard-layout type across execution boundaries depends on what operations are performed on the object within the callee as well as what subsequent operations are performed on the object from the caller. The effects can range from correct or benign behavior to undefined behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

EXP60-CPP

High

Probable

Medium

P12

L1

Automated Detection

Tool

Version

Checker

Description

Clang

Include Page
Clang_V
Clang_V
-Wdynamic-class-memaccessCatches instances where the vtable pointer will be overwritten
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

DF4741, DF4742, DF4743


Klocwork
Include Page
Klocwork_V
Klocwork_V
CERT.EXPR.PASS_NON_STD_LAYOUT
Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_CPP-EXP60-aDo not pass a nonstandard-layout type object across execution boundaries
Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: EXP60-CPPChecks for non-standard layout objects passed across execution boundaries (rule fully covered).

Related Vulnerabilities

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

Related Guidelines

Bibliography

[ISO/IEC 14882-2014]Clause 9, "Classes"
Subclause 7.5, "Linkage Specifications"
 
 


...

Image Modified Image Modified Image Modified