When invoked by a new
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 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:
...
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
...
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 Clang and GNU GCC compilers, 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 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 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(); } } |
...
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 |
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
...