When invoked by a new
expression expression for a given type, the default global non-placement forms of C++ 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 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 operators by virtue of [basic.stc.dynamic], paragraph 3.)
In addition, the standard provides the following example later on in the same section:
new(2, f) T[5]
results in a callof operator new[](sizeof(T) * 5 + y, 2, f).
Here,
x
...
andy
are 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)
, it this results in undefined behavior. This example, and subsequent ones, all assume the pointer created returned by placement new
will 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.
...
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 has c
has also been inserteddeclared.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <new> void f() { char c; // Used elsewhere in the function unsigned char buffer[sizeof(long)]; long *lp = ::new (buffer) long; // ... } |
Compliant Solution (
...
alignas
)
This In this compliant solution ensures that the long
is constructed into a buffer of sufficient size and with suitable alignment:, the alignas
declaration specifier is used to ensure the buffer is appropriately aligned for a long.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <new> #include <type_traits> void f() { char c; // Used elsewhere in the function std::aligned_storage<sizeofalignas(long), alignof unsigned char buffer[sizeof(long)>::type buffer]; long *lp = ::new (&buffer) long; // ... } |
Compliant Solution (
...
std::aligned_storage
)
In this This compliant solution , the alignas
declaration specifier is used to ensure the buffer is appropriately aligned for a long
:ensures that the long
is constructed into a buffer of sufficient size and with suitable alignment.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <new> void f() { char c; // Used elsewhere in the function alignasstd::aligned_storage<sizeof(long) unsigned char buffer[sizeof, 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 struct 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.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <new> struct S { void *p; 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 should ideally would be documented by quality implementations. The following snippet outlines an example compliant solution that's portable to is specifically for the Clang and GNU GCC compilers. Porting it to other implementations requires adding similar conditional definitions of the Overhead
constant. When an implementation does not document the overhead, assuming it's at least as large as twice the size of sizeof(size_t)
should typically be safe. , 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 defines an overload of also overloads the placement operator new that accepts new[]
operator to accept the buffer size as a third argument and verifies that it is at least as large as not smaller than the total amount of storage required.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <cstddef> #include <new> #if defined(__clang__) || defined(__GNUG__) const size_t Overheadoverhead = sizeof(size_t); #else static_assert(false, "you need to determine the size of your implementation's array overhead"); const size_t Overheadoverhead = 2 * sizeof(size_t); #endif void* 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) { assertif (n <=> bufsize); { // alternatively, throw an exceptionstd::bad_array_new_length(); } return p; } void f() { const size_t Nn = 32; alignas(S) unsigned char buffer[sizeof(S) * Nn + Overheadstd::max(overhead, alignof(S))]; S *sp = ::new (buffer, sizeof (buffer)) S [Nn]; // ... // Destroy elements of the array. for (size_t i = 0; i != Nn; ++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 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 |
Automated Detection
Tool | Version | Checker | Description |
---|
Axivion Bauhaus Suite |
| CertC++-MEM54 | |||||||
CodeSonar |
| LANG.MEM.BO | Buffer Overrun | ||||||
Helix QAC |
| C++3119, C++3128, DF3520, DF3521, DF3522, DF3523 | |||||||
LDRA tool suite |
| 597 S | Enhanced Enforcement | ||||||
Parasoft C/C++test |
| CERT_CPP-MEM54-a | Do not pass a pointer that has insufficient storage capacity or that is not suitably aligned for the object being constructed to placement 'new' | ||||||
Polyspace Bug Finder |
| CERT C++: MEM54-CPP | Checks for placement new used with insufficient storage or misaligned pointers (rule fully covered) | ||||||
PVS-Studio |
| V752 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Bibliography
...