ISO/IEC JTC1 SC22 WG21
N4303
Richard Smith, richard@metafoo.co.uk
Hubert Tong, hstong@ca.ibm.com
2014-11-21

Pointer safety and placement new

Abstract

This paper describes a usage pattern that occurs with the reserved placement forms of operator new (§18.6.1.3 [new.delete.placement]) which was not supported by the Standard until C++14. It provides reasoning for why the pattern is harmful and it suggests a mechanism to be used where the difficultly of converting is a plain syntactic transformation.

Problem description

The reserved placement forms of operator new (hereafter referred to as placement new functions) are intended to facilitate constructing an object at a known address.

Whereas it is plainly obvious that it is not possible to reliably refer to the memory allocated by allocation functions other than placement new functions without using the value returned by the allocation function, it is possible for placement new functions.

Users take advantage of this apparent possibility by referring to the new object through pointer values obtained from casting a pointer to the object which originally held the storage.

Consider:

#include <new>
#include <cassert>

struct A { char buf[1]; };
struct B { char buf[1]; };

int main(void) {
    A a{ { 0 } };
    B *bp = static_cast<B *>(static_cast<void *>(&a));
    new (bp) B{ { 1 } };
    assert(bp->buf[0] == 1);
}

Prior to the adoption of the resolution for DR 1412 [CWG1412], the value of bp is unspecified at the point of its initialization and its subsequent passing to operator new via the new-expression. Said pointer may be null, insufficiently aligned or otherwise dangerous to use. [1]

The resolution to DR 1412 modifies the specification of static_cast such that the cast now reliably points to the start of the memory associated with a. Unfortunately this allows placement new to foul analyses which use address value propagation to refine aliasing based on knowing where the pointer points. In the example above, such an optimization may find that the address of bp->buf[0] to be associated with the object a. From that finding, the optimizer may then decide that accesses to the memory ought to be done through a glvalue which may access the stored value of a without invoking undefined behavior (§3.10 [basic.lval]). The initialization done via the new-expression is not such an access.

In addition to the above, CWG 1776 [CWG1776] was opened because a language requirement exists which enables optimization but makes it difficult to implement std::optional. As mentioned in [1776ref1], the difficultly can be resolved by having a constexpr mechanism which sanitizes pointer values. The proposed solution below provides such a mechanism.

Proposed solution

A mechanism is proposed which provides an interface that takes a pointer to a specific type and returns a pointer value which points to the object of that type (ignoring cv-qualification) located at that memory. The intention is that this mechanism will be taken by a compiler to be opaque to address value propagation analysis as appropriate.

Add to §18.6 [support.dynamic] paragraph 1:

namespace std {
  template <typename T>
    constexpr T* launder(T* p) noexcept;
}

Add a new subclause under §18.6 [support.dynamic]:

template <typename T> constexpr T* launder(T* p) noexcept;

Required behavior: An invocation of this function may be used in core constant expressions whenever the value of its argument may be used in a core constant expression.

Requires: p represents the address A of a byte in memory and an object Obj whose cv-unqualified type is remove_cv_t<T> is located at the address A.

Returns: A value of type T * which points to Obj.

Attributions

The authors thank Gabriel Dos Reis, chair of SG12 for providing time to discuss the concerns described herein during the SG12 working session. The authors would also like to thank Chris Bowler of IBM Canada and any others who may have been unintentionally missed for their valuable input and feedback on this paper.

Notes

[1] This calls attention to the fact that the placement new functions cannot be implemented such that they satisfy both the constraint in §3.7.4 [basic.stc.dynamic] and the requirements of §18.6.1.3 [new.delete.placement]. The forthcoming resolution for CWG 1910 [CWG1910] is expected to handle this issue.

References

[CWG1412] Problems in specifying pointer conversions
[CWG1776] Replacement of class objects containing reference members
[CWG1910] “Shall” requirement applied to runtime behavior
[1776ref1] Implementability of std::optional, https://groups.google.com/a/isocpp.org/d/msg/std-proposals/93ebFsxCjvQ/Q5LUnO8339wJ