Lets get schizophrenic ...by doing our laundry Matthis Kruse - - PowerPoint PPT Presentation

let s get schizophrenic
SMART_READER_LITE
LIVE PREVIEW

Lets get schizophrenic ...by doing our laundry Matthis Kruse - - PowerPoint PPT Presentation

Lets get schizophrenic ...by doing our laundry Matthis Kruse @Natriumchlorid Saarland University struct Paul { virtual int do_life(); }; struct Sarah : Paul { int do_life() override; }; #include <new> struct Paul { virtual


slide-1
SLIDE 1

Let’s get schizophrenic

...by doing our laundry Matthis Kruse @Natriumchlorid

Saarland University

slide-2
SLIDE 2

struct Paul { virtual int do_life(); }; struct Sarah : Paul { int do_life() override; };

slide-3
SLIDE 3

#include <new> struct Paul { virtual int do_life(); }; struct Sarah : Paul { int do_life() override { new(this) Paul; return 2; } }; int Paul::do_life() { new(this) Sarah; return 1; }

slide-4
SLIDE 4

int main(int argc, char** argv) { Sarah sarah; const int n = sarah.do_life(); const int m = sarah.do_life(); return n + m; // is ??? }

slide-5
SLIDE 5

int main(int argc, char** argv) { Sarah sarah; const int n = sarah.do_life(); const int m = sarah.do_life(); return n + m; // is ??? }

U n d e fi n e d B e h a v i

  • r
slide-6
SLIDE 6

6.6.3.8 basic.life

If, after the lifetime of an object has ended and before the storage which the object

  • ccupied is reused or released, a new object is created at the storage location which

the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if: ◮ the storage for the new object exactly overlays the storage location which the

  • riginal object occupied, and

◮ the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and ◮ the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and ◮ neither the original object nor the new object is a potentially-overlapping subobject.

slide-7
SLIDE 7

6.6.3.8 basic.life

If, after the lifetime of an object has ended and before the storage which the object

  • ccupied is reused or released, a new object is created at the storage location which

the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if: ◮ the storage for the new object exactly overlays the storage location which the

  • riginal object occupied, and

◮ the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and ◮ the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and ◮ neither the original object nor the new object is a potentially-overlapping subobject.

slide-8
SLIDE 8

6.6.3.8 basic.life

If, after the lifetime of an object has ended and before the storage which the object

  • ccupied is reused or released, a new object is created at the storage location which

the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if: ◮ the storage for the new object exactly overlays the storage location which the

  • riginal object occupied, and

◮ the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and ◮ the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and ◮ neither the original object nor the new object is a potentially-overlapping subobject.

slide-9
SLIDE 9

6.6.3.8 basic.life

If, after the lifetime of an object has ended and before the storage which the object

  • ccupied is reused or released, a new object is created at the storage location which

the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if: ◮ the storage for the new object exactly overlays the storage location which the

  • riginal object occupied, and

◮ the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and ◮ the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and ◮ neither the original object nor the new object is a potentially-overlapping subobject.

slide-10
SLIDE 10

6.6.3.8 basic.life

If, after the lifetime of an object has ended and before the storage which the object

  • ccupied is reused or released, a new object is created at the storage location which

the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if: ◮ the storage for the new object exactly overlays the storage location which the

  • riginal object occupied, and

◮ the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and ◮ the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and ◮ neither the original object nor the new object is a potentially-overlapping subobject.

slide-11
SLIDE 11

std::launder to the rescue!

slide-12
SLIDE 12

On std::launder()

template <class T> constexpr T* launder(T* p) noexcept; // (since C++17)

slide-13
SLIDE 13

On std::launder()

template <class T> constexpr T* launder(T* p) noexcept; // (since C++17) template <class T> // (since C++20) [[nodiscard]] constexpr T* launder(T* p) noexcept;

slide-14
SLIDE 14

On std::launder()

template <class T> constexpr T* launder(T* p) noexcept; // (since C++17) template <class T> // (since C++20) [[nodiscard]] constexpr T* launder(T* p) noexcept; ◮ the pointer p represents the address A of a byte in memory ◮ an object X is located at the address A ◮ X is within its lifetime ◮ the type of X is the same as T, ignoring cv-qualifiers at every level ◮ every byte that would be reachable through the result is reachable through p

slide-15
SLIDE 15

int main(int argc, char** argv) { Sarah sarah; const int n = sarah.do_life(); const int m = std::launder(&sarah)->do_life(); return n + m; // is ??? }

slide-16
SLIDE 16

int main(int argc, char** argv) { Sarah sarah; const int n = sarah.do_life(); const int m = std::launder(&sarah)->do_life(); return n + m; // is 3 }

slide-17
SLIDE 17

The why

slide-18
SLIDE 18

#include <new> struct storage { const int num; }; int main() { storage* data = new storage { 31 }; const int old = data->num; new(data) storage { 12 }; return old + data->num; // is ??? }

slide-19
SLIDE 19

#include <new> struct storage { const int num; }; int main() { storage* data = new storage { 31 }; const int old = data->num; new(data) storage { 12 }; return old + data->num; // is ??? }

U n d e fi n e d B e h a v i

  • r

!

slide-20
SLIDE 20

6.6.3.8 basic.life

If, after the lifetime of an object has ended and before the storage which the object

  • ccupied is reused or released, a new object is created at the storage location which

the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if: ◮ the storage for the new object exactly overlays the storage location which the

  • riginal object occupied, and

◮ the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and ◮ the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and ◮ neither the original object nor the new object is a potentially-overlapping subobject.

slide-21
SLIDE 21

6.6.3.8 basic.life

If, after the lifetime of an object has ended and before the storage which the object

  • ccupied is reused or released, a new object is created at the storage location which

the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if: ◮ the storage for the new object exactly overlays the storage location which the

  • riginal object occupied, and

◮ the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and ◮ the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and ◮ neither the original object nor the new object is a potentially-overlapping subobject.

slide-22
SLIDE 22

6.6.3.8 basic.life

If, after the lifetime of an object has ended and before the storage which the object

  • ccupied is reused or released, a new object is created at the storage location which

the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if: ◮ the storage for the new object exactly overlays the storage location which the

  • riginal object occupied, and

◮ the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and ◮ the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and ◮ neither the original object nor the new object is a potentially-overlapping subobject.

slide-23
SLIDE 23

#include <new> struct storage { const int num; }; int main() { storage* data = new storage { 31 }; const int old = data->num; new(data) storage { 12 }; return old + std::launder(data)->num; // is ??? }

slide-24
SLIDE 24

#include <new> struct storage { const int num; }; int main() { storage* data = new storage { 31 }; const int old = data->num; new(data) storage { 12 }; return old + std::launder(data)->num; // is 43 }

slide-25
SLIDE 25

#include <new> struct storage { const int num; }; int main() { storage* data = new storage { 31 }; const int old = data->num; data = new(data) storage { 12 }; return old + data->num; // also 43 }

slide-26
SLIDE 26

struct U { const int i; }; std::vector<U> B; B.push_back({ 42 }); B.clear(); B.push_back({ 48988 }); const int v = B[0].i;

slide-27
SLIDE 27

struct U { const int i; }; std::vector<U> B; B.push_back({ 42 }); B.clear(); B.push_back({ 48988 }); const int v = B[0].i; // <- UB

slide-28
SLIDE 28

struct U { const int i; }; std::vector<U> B; B.push_back({ 42 }); B.clear(); B.push_back({ 48988 }); const int v = B[0].i; // <- UB Idea: Use memory laundering!

slide-29
SLIDE 29

T& operator[](std::size_t i) { return std::launder(_elems)[i]; }

slide-30
SLIDE 30

T& operator[](std::size_t i) { return std::launder(_elems)[i]; } ...but be careful, there is undefined behavior lurking in there...

slide-31
SLIDE 31

T& operator[](std::size_t i) { return std::launder(_elems)[i]; } ...but be careful, there is undefined behavior lurking in there... ...because elems might not be a raw pointer type.

slide-32
SLIDE 32

T& operator[](std::size_t i) { return std::launder(_elems)[i]; } ...but be careful, there is undefined behavior lurking in there... ...because elems might not be a raw pointer type. std::allocator_traits<A>::pointer must only fullfill the std::pointer traits requirements.

slide-33
SLIDE 33

The how

slide-34
SLIDE 34

/// Pointer optimization barrier [ptr.launder] template<typename _Tp> [[nodiscard]] constexpr _Tp* launder(_Tp* __p) noexcept { return __builtin_launder(__p); } “Money laundering is used to prevent people from tracing where you got your money

  • from. Memory laundering is used to prevent the compiler from tracing where you got

your object from, thus forcing it to avoid any optimizations that may no longer apply.”

slide-35
SLIDE 35

Thank you for your attention!