Title
`std::start_lifetime_as` inadvertently has undefined behavior due to use of `std::bit_cast`
Status
new
Section
[obj.lifetime]
Submitter
Jan Schultke

Created on 2024-10-23.00:00:00 last changed 1 month ago

Messages

Date: 2024-10-31.08:08:16

Proposed resolution:

This wording is relative to N4993.

  1. Modify [obj.lifetime] as indicated:

    [Drafting note: The proposed resolution does not alter the behavior for erroneous bits. Therefore, a call to std::start_lifetime_as may 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 ([intro.object]) within the denoted region consisting of an object a of type `T` whose address is `p`, and objects nested within a, as follows: The object representation of a is the contents of the storage prior to the call to `start_lifetime_as`. The value of each created object o of trivially copyable type ([basic.types.general]) `U` is determined in the same manner as for a call to bit_cast<U>(E) ([bit.cast]), where `E` is an lvalue of type `U` denoting o, except that the storage is not accessed and that for each indeterminate bit b in the value representation of the result, the smallest object containing that bit b has indeterminate value where the behavior would otherwise be undefined. The value of any other created object is unspecified.

Date: 2024-10-23.00:00:00

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` ([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 ([bit.cast] paragraph 2), and such an `int` is the smallest enclosing object of some of the indeterminate bits.

History
Date User Action Args
2024-10-26 16:00:25adminsetmessages: + msg14442
2024-10-23 00:00:00admincreate