With Lina✨ & Cyan💎
#include <iostream>
using namespace std;
int main() {
int a = 0;
void *p = &a;
cout << "&a is " << p << " nya!" << endl;
}
&a is 0x7fffc6ba5ddc nya!
This is a virtual address!
↓
0x7fffc6ba5ddc
↓
0x814745ddc
↑
This is the physical address!
40 bits for virtual address (1 TiB)
16 KiB pages → 67,108,864 pages total
8 bytes per page address
=
512 MiB page table!!!!
Translation Table Base Address Table
00
: -01
: Wait For Idle02
: Wait 203
: Doorbell04
: Write 8-bit reg05
: Write 32-bit reg06
: Write 64-bit reg07
: -08
: Read 8-bit reg09
: Read 32-bit reg0a
: Read 64-bit reg0b
: Wait 32-bit reg0c
: Wait 64-bit reg0d
: -0e
: Store 32-bit
0f
: Store 64-bit
10
: Load 32-bit
11
: Load 64-bit
12
: ?13
: ?14
: ?15
: Test (If)
16
: Logic op
17
: Add 16-bit
18
: Retire19
: Timestamp1a
: KTrace1b
: ?1c
: ?1d
: ?1e
: Jump (Cond)
1f
: ?20
: ?21
: ?22
: Start Vertex23
: Finish Vertex24
: Start Fragment25
: Finish Fragment26
: Start Blit27
: Finish Blit28
: ?29
: Start Compute2a
: Finish Compute2b
: ?2c
: ?Indirect read/writes?R0 = *R1;
Multiply?R1 = 10 × R0;
But...
They forgot to blocklist that one!!!!
GPU can now read/write page tables directly!!
We can read/write stuff from the microsequence, but we can't call the uPPL...
We can't modify the firmware code directly...
Return-oriented Programming (ROP)!
g_calltwo
: Call two functions...
But the first argument is a pointer to a pointer to a pointer to the function
And the second one is a pointer to 0x70 before a pointer to the function
g_store
: Store a value to memory...
But it actually stores to the address you pass plus 0xa78
It's hard to demo this all working in practice...
Challenge Accepted!
R0 | 0x0000000000000000 | R1 | 0x0000000000000000 |
X0 | |
X1 | |
X2 | |
X3 | |
X4 | |
X5 | |
X6 | |
X7 | |
X8 | |
X9 | |
X10 | |
X11 | |
X12 | |
X13 | |
X14 | |
X15 | |
X16 | |
X17 | |
X18 | |
X19 | |
X20 | |
X21 | |
X22 | |
X23 | |
X24 | |
X25 | |
X26 | |
X27 | |
X28 | |
X29 | |
LR | |
SP | |
PC |