CertC++-MEM54¶
Provide placement new with properly aligned pointers to sufficient storage capacity
Required inputs: IR
When invoked by a
new expression for a given type, the default global
non-placement forms of
operator new attempt to allocate sufficient storage for an object
of the type and, if successful, return a pointer with alignment suitable for
any object with a fundamental alignment requirement. However, the default
placement
new operator simply returns the given pointer back to the
caller without guaranteeing that there is sufficient space in which to
construct the object or ensuring that the pointer meets the proper alignment
requirements. The C++ Standard, [expr.new], paragraph 16 [
ISO/IEC
14882-2014], nonnormatively states the following:
[Note: when the allocation function returns a value other than null, it must be a pointer to a block of storage in which space for the object has been reserved. The block of storage is assumed to be appropriately aligned and of the requested size. The address of the created object will not necessarily be the same as that of the block if the object is an array. -end note]
(This note is a reminder of the general requirements specified by the C++
Standard, [basic.stc.dynamic.allocation], paragraph 1, which apply to
placement
new operators by virtue of [basic.stc.dynamic],
paragraph 3.)
In addition, the standard provides the following example later in the same section:
new(2, f) T[5]results in a callof operator new[](sizeof(T) * 5 + y, 2, f).Here,
...andyare non-negative unspecified values representing array allocation overhead; the result of the new-expression will be offset by this amount from the value returned byoperator new[]. This overhead may be applied in all array new-expressions, including those referencing the library functionoperator new[](std::size_t, void*)and other placement allocation functions. The amount of overhead may vary from one invocation of new to another.
Do not pass a pointer that is not suitably aligned for the object being
constructed to placement
new. Doing so results in an object being constructed at a
misaligned location, which results in
undefined
behavior. Do not pass a pointer that has insufficient storage capacity for
the object being constructed, including the overhead required for arrays. Doing
so may result in initialization of memory outside of the bounds of the object
being constructed, which results in undefined behavior.
Finally, do not use placement
new[] on any platform that does not specify a limit for the
overhead it requires.
Noncompliant Code Example
In this noncompliant code example, a pointer to a
short is passed to placement
new, which is attempting to initialize a
long. On architectures where
sizeof(short) < sizeof(long), this results in
undefined
behavior. This example, and subsequent ones, all assume the pointer
returned by placement
new will not be used after the lifetime of its underlying
storage has ended. For instance, the pointer will not be stored in a
static global variable and dereferenced after the call to
f() has ended. This assumption is in conformance with
MEM50-CPP.
Do not access freed memory.
#include <new>
void f() {
short s;
long *lp = ::new (&s) long;
}
Noncompliant Code Example
This noncompliant code example ensures that the
long is constructed into a buffer of sufficient size. However, it
does not ensure that the alignment requirements are met for the pointer passed
into placement
new. To make this example clearer, an additional local variable
c has also been declared.
#include <new>
void f() {
char c; // Used elsewhere in the function
unsigned char buffer[sizeof(long)];
long *lp = ::new (buffer) long;
// ...
}
Compliant Solution (
alignas)
In this compliant solution, the
alignas declaration specifier is used to ensure the buffer is
appropriately aligned for a
long.
#include <new>
void f() {
char c; // Used elsewhere in the function
alignas(long) unsigned char buffer[sizeof(long)];
long *lp = ::new (buffer) long;
// ...
}
Compliant Solution (
std::aligned_storage)
This compliant solution ensures that the
long is constructed into a buffer of sufficient size and with
suitable alignment.
#include <new>
void f() {
char c; // Used elsewhere in the function
std::aligned_storage<sizeof(long), alignof(long)>::type buffer;
long *lp = ::new (&buffer) long;
// ...
}
Noncompliant Code Example (Failure to Account for Array Overhead)
This noncompliant code example attempts to allocate sufficient storage of the
appropriate alignment for the array of objects of
S. However, it fails to account for the overhead an
implementation may add to the amount of storage for array objects. The
overhead (commonly referred to as a cookie) is necessary to store the number of
elements in the array so that the array delete expression or the exception
unwinding mechanism can invoke the type's destructor on each successfully
constructed element of the array. While some implementations are able to
avoid allocating space for the cookie in some situations, assuming they do in
all cases is unsafe.
#include <new>
struct S {
S ();
~S ();
};
void f() {
const unsigned N = 32;
alignas(S) unsigned char buffer[sizeof(S) * N];
S *sp = ::new (buffer) S[N];
// ...
// Destroy elements of the array.
for (size_t i = 0; i != n; ++i) {
sp[i].~S();
}
}
Compliant Solution (Clang/GCC)
The amount of overhead required by array new expressions is unspecified but
ideally would be documented by quality implementations. The following compliant
solution is specifically for the
Clangand GNU
GCCcompilers, which guarantee that the overhead for dynamic array allocations
is a single value of type
size_t. (Note that this value is often treated as a "-1th" element
in the array, so the actual space used may be larger.) To verify that the
assumption is, in fact, safe, the compliant solution also overloads the
placement
new[] operator to accept the buffer size as a third argument and
verifies that it is not smaller than the total amount of storage required.
#include <cstddef>
#include <new>
#if defined(__clang__) || defined(__GNUG__)
const size_t overhead = sizeof(size_t);
#else
static_assert(false, "you need to determine the size of your implementation's array overhead");
const size_t overhead = 0; // Declaration prevents additional diagnostics about overhead being undefined; the value used does not matter.
#endif
struct S {
S();
~S();
};
void *operator new[](size_t n, void *p, size_t bufsize) {
if (n > bufsize) {
throw std::bad_array_new_length();
}
return p;
}
void f() {
const size_t n = 32;
alignas(S) unsigned char buffer[sizeof(S) * n + std::max(overhead, alignof(S))];
S *sp = ::new (buffer, sizeof(buffer)) S[n];
// ...
// Destroy elements of the array.
for (size_t i = 0; i != n; ++i) {
sp[i].~S();
}
}
Porting this compliant solution to other implementations requires adding similar conditional definitions of the overhead constant, depending on the constraints of the platform.
Risk Assessment
Passing improperly aligned pointers or pointers to insufficient storage to
placement
new expressions can result in
undefined
behavior, including buffer overflow and
abnormal
termination.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| MEM54-CPP | High | Likely | Medium | P18 | L1 |
Related Guidelines
| SEI CERT C++ Coding Standard | MEM50-CPP. Do not access freed memory |
Bibliography
| [ ISO/IEC 14882-2014] | Subclause 3.7.4, "Dynamic Storage Duration" Subclause 5.3.4, "New" |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
array_new |
Ensure to account for the overhead of array new expressions, i.e., storage required for array cookies |
None |
False |
improper_alignment |
Improper alignment {} when using placement new operator, intended storage has alignment {} |
None |
False |
insufficient_storage |
Insufficient space when using placement new operator: Object has {} bits, intended storage has {} bits |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions