Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using std::launder to get a pointer to an active object member from a pointer to an inactive object?

This question followes this one

Let's consider this example code:

struct sso
  {
  union{
    struct {
      char* ptr;
      char size_r[8];
      } large_str;
    char short_str[16];
    };

  bool is_short_str() const{
    return *std::launder(short_str+15)=='\0'; //UB?
    }
  };

If short_str is not the active member dereferencing the pointer without std::launder would be UB. Let's consider that the ABI is well specified and that we know that size_r[7] is at the same address as short_str[15]. Does std::launder(short_str+15) return a pointer to size_r[7] when short_str is not the active member of the union?


Nota: I think this is the case because [ptr.launder]/3

A byte of storage is reachable through a pointer value that points to an object Y if it is within the storage occupied by Y, an object that is pointer-interconvertible with Y, or the immediately-enclosing array object if Y is an array element.

like image 346
Oliv Avatar asked Jan 10 '18 13:01

Oliv


1 Answers

Let's consider that the ABI is well specified and that we know that size_r[7] is at the same address as short_str[15]

It depends entirely on what that guarantee means exactly.

A compiler is free to guarantee that

Sso.short_str[15]

can be accessed and modified and everything even when Sso.large_str is currently active, and get exactly the semantics you expect.

Or it is free not to give that guarantee.

There is no restriction on the behavior or programs that are ill-formed or exhibit undefined behavior.

As there is no object there, &Sso.short_str[15] isn't pointer-interconvertible with anything. An object that isn't there doesn't have the "same address" as another object.

Launder is defined in terms of a pointer to a pre-existing object. That pointer is then destroyed, and a new object with the same address is created (which is well defined). std::launder then lets you take the pointer to the object that no longer exists and get a pointer to the existing object.

What you are doing is not that. If you took &short_str[15] when it was engaged, you'd have a pointer to an object. And the ABI could say that this was at the same address as size_r[7]. And now std::launder would be in the domain of validity.

But the compiler could just go a step further and define that short_str[15] refers to the same object as size_r[7] even if it isn't active.

The weakest ABI guarantee that I could see being consistent with your stuff would only work if you took the address of short_str[15] when it was active; later, you would engage the large_str, and then you could launder from &short_str[15] to &size_r[7]. The strongest ABI guarantee that is consistent with your statement makes the call to std::launder not required. Somewhere in the middle std::launder would be required.

like image 177
Yakk - Adam Nevraumont Avatar answered Nov 16 '22 20:11

Yakk - Adam Nevraumont