std::start_lifetime_as inadvertently has undefined behavior due to use of std::bit_castSection: 20.2.6 [obj.lifetime] Status: New Submitter: Jan Schultke Opened: 2024-10-23 Last modified: 2024-10-31
Priority: Not Prioritized
View other active issues in [obj.lifetime].
View all other issues in [obj.lifetime].
View all issues with New status.
Discussion:
Consider the motivating example from P2590R2: Explicit lifetime management:
struct X { int a, b; };
X* make_x() {
X* p = std::start_lifetime_as<X>(myMalloc(sizeof(struct X));
p->a = 1;
p->b = 2;
return p;
}
Assuming that myMalloc does not initialize the bytes of storage, this example has undefined behavior because
the value of the resulting object of trivially copyable type X is determined as if by calling
std::bit_cast<X>(a) for the implicitly-created object a of type X
(20.2.6 [obj.lifetime] paragraph 3), whose object representation is filled with indeterminate bytes
obtained from myMalloc. Such a call to std::bit_cast has undefined behavior because std::bit_cast
does not tolerate the creation of an int where bits in the value representation are indeterminate
(22.11.3 [bit.cast] paragraph 2), and such an int is the smallest enclosing object of some of the
indeterminate bits.
Proposed resolution:
This wording is relative to N4993.
Modify 20.2.6 [obj.lifetime] as indicated:
[Drafting note: The proposed resolution does not alter the behavior for erroneous bits. Therefore, a call to
std::start_lifetime_asmay have erroneous behavior when used on storage with indeterminate bits, despite not accessing that storage. An alternative resolution would be to produce objects whose value is erroneous.]
template<class T> T* start_lifetime_as(void* p) noexcept; template<class T> const T* start_lifetime_as(const void* p) noexcept; template<class T> volatile T* start_lifetime_as(volatile void* p) noexcept; template<class T> const volatile T* start_lifetime_as(const volatile void* p) noexcept;-1- Mandates: […]
-2- Preconditions: […] -3- Effects: Implicitly creates objects (6.8.2 [intro.object]) within the denoted region consisting of an objectaof typeTwhose address isp, and objects nested withina, as follows: The object representation ofais the contents of the storage prior to the call tostart_lifetime_as. The value of each created objectoof trivially copyable type (6.9.1 [basic.types.general])Uis determined in the same manner as for a call tobit_cast<U>(E)(22.11.3 [bit.cast]), whereEis an lvalue of typeUdenotingo, except that the storage is not accessed and that for each indeterminate bitbin the value representation of the result, the smallest object containing that bitbhas indeterminate value where the behavior would otherwise be undefined. The value of any other created object is unspecified.