...
In 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 object, then passes a reference to the object to a function defined by the library. However, if 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 in unexpected behavior.
Code Block |
---|
|
// library.h
struct S {
virtual void f() { /* ... */ }
};
void func(S &s); // Implemented by the library, calls S::f()
// application.cpp
#include "library.h"
void f() {
S s;
func(s);
} |
Compliant Solution
If the library and the application conform 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 the library and application do not conform to the same ABI, the compliant solution requires modification of the library and application to work with a standard-layout type.
Page properties |
---|
|
This CS is incredibly unsatisfying, but is still true. |
Code Block |
---|
|
// 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 f() {
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 |
---|
|
#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 |
---|
|
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 |
---|
|
struct B {
int i, j;
};
struct D : B {
float f;
};
void f(D *d) {
extern "Fortran" void func(void *);
func(d);
} |
Compliant Solution
In this compliant solution, the nonstandard-layout type object is serialized into a local standard-layout type object, that is then passed to the Fortran function:
...