The default global allocators will attempt to allocate sufficient storage for an object, and if successful, return a pointer with suitable alignment for that object. 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, nonnormatively states [ISO/IEC 14882-2014]:
[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.)
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. Doing so may result in initialization of memory outside of the bounds of the object being constructed, which results in undefined behavior.
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 created 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 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 more obvious, an additional local variable has been inserted.
#include <new> void f() { char c; // Used elsewhere in the function 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> #include <type_traits> void f() { char c; // Used elsewhere in the function std::aligned_storage<sizeof(long), alignof(long)>::type buffer; 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; // ... }
Risk Assessment
Providing improperly-aligned pointers to placement new can result in undefined behavior, including abnormal termination.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MEM45-CPP | Medium | Likely | Medium | P8 | L2 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Bibliography
[ISO/IEC 14882-2014] | 5.3.4, "New" 3.7.4, "Dynamic Storage Duration" |