diff --git a/.gitignore b/.gitignore index 0ef7b5a..fb56baa 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ *.mk *.mod *.swn +*.elf diff --git a/.gitmodules b/.gitmodules index 9ac9d02..51bbaf0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "sdk/gramine"] path = sdk/gramine/gramine url = https://github.com/gramineproject/gramine.git +[submodule "sdk/bare-sgx"] + path = sdk/bare-sgx + url = https://github.com/jovanbulck/bare-sgx.git diff --git a/app/baresgx/Makefile b/app/baresgx/Makefile new file mode 100644 index 0000000..739b85e --- /dev/null +++ b/app/baresgx/Makefile @@ -0,0 +1,42 @@ +LIBSGXSTEP_DIR = ../.. +LIBSGXSTEP = $(LIBSGXSTEP_DIR)/libsgxstep +-include $(LIBSGXSTEP)/Makefile.config +LIBSGXSTEP_SILENT = 1 + +BARESGX = ../../sdk/bare-sgx/urts +ENCLAVE = enclave + + +SUBDIRS = $(ENCLAVE) $(BARESGX) $(LIBSGXSTEP) + +CC := gcc +LD := gcc +INCLUDE := -I$(BARESGX)/include/ -I$(LIBSGXSTEP_DIR) +CFLAGS := -Wall -g -fPIC +LDFLAGS := -z noexecstack -pthread -lbaresgx-urts -lsgx-step -L$(BARESGX) -L$(LIBSGXSTEP) -lcrypto -lelf + +SOURCES = $(shell ls *.c) +OBJECTS = $(SOURCES:.c=.o) +OUTPUT = app + +BUILDDIRS = $(SUBDIRS:%=build-%) +CLEANDIRS = $(SUBDIRS:%=clean-%) + +MAKEFLAGS = --silent + +all: $(OUTPUT) + +$(OUTPUT): $(BUILDDIRS) $(OBJECTS) + $(LD) $(OBJECTS) $(LDFLAGS) -o $(OUTPUT) + +%.o : %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDE) -c $< -o $@ + +$(BUILDDIRS): + $(MAKE) -C $(@:build-%=%) + +$(CLEANDIRS): + $(MAKE) clean -C $(@:clean-%=%) + +clean: $(CLEANDIRS) + rm -f $(OBJECTS) $(OUTPUT) diff --git a/app/baresgx/README.md b/app/baresgx/README.md new file mode 100644 index 0000000..ec3fbc9 --- /dev/null +++ b/app/baresgx/README.md @@ -0,0 +1,10 @@ +# Minimal baresgx test enclave + +This directory contains a minimal test enclave hand written in assembly using +the [`bare-sgx`](https://github.com/jovanbulck/bare-sgx) runtime. This setup +allows you to load the enclave on a bare-metal Linux platform without needing +any bloated SDK installation. + +The test enclave consists of a single code page with _exactly_ 100 instructions, +which can be precisely single-stepped by SGX-Step, making it an ideal resource +for testing and debugging. diff --git a/app/baresgx/enclave/Makefile b/app/baresgx/enclave/Makefile new file mode 100644 index 0000000..91b9bf0 --- /dev/null +++ b/app/baresgx/enclave/Makefile @@ -0,0 +1,11 @@ +.PHONY: all clean + +CFLAGS += -Wall -Werror -static-pie -nostdlib -ffreestanding -fPIE \ + -fno-stack-protector -mrdrnd $(INCLUDES) +LDFLAGS := -Wl,-T,encl.lds,--build-id=none + +encl.elf: encl.S + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +clean: + rm -f *.elf *.o diff --git a/app/baresgx/enclave/encl.S b/app/baresgx/enclave/encl.S new file mode 100644 index 0000000..fc89a0e --- /dev/null +++ b/app/baresgx/enclave/encl.S @@ -0,0 +1,41 @@ + .section ".tcs", "aw" + .balign 4096 + + .fill 1, 8, 0 # STATE (set by CPU) + .fill 1, 8, 0 # FLAGS + .quad encl_ssa_tcs1 # OSSA + .fill 1, 4, 0 # CSSA (set by CPU) + .fill 1, 4, 1 # NSSA + .quad encl_entry # OENTRY + .fill 1, 8, 0 # AEP (set by EENTER and ERESUME) + .fill 1, 8, 0 # OFSBASE + .fill 1, 8, 0 # OGSBASE + .fill 1, 4, 0xFFFFFFFF # FSLIMIT + .fill 1, 4, 0xFFFFFFFF # GSLIMIT + .fill 4024, 1, 0 # Reserved + + /* + * Minimal test enclave with a single code page with exactly 100 + * instructions. + */ + .text + .align 0x1000 +encl_entry: + mov encl_secret(%rip), %rsi + + .rept 96 + nop + .endr + + # EEXIT + mov %rcx, %rbx + mov $4, %rax + enclu + + .data +encl_ssa_tcs1: + .space 4096 + +encl_secret: + .quad 0xdeadbeefcafebabe + diff --git a/app/baresgx/enclave/encl.lds b/app/baresgx/enclave/encl.lds new file mode 100644 index 0000000..32ca571 --- /dev/null +++ b/app/baresgx/enclave/encl.lds @@ -0,0 +1,40 @@ +OUTPUT_FORMAT(elf64-x86-64) + +PHDRS +{ + tcs PT_LOAD; + text PT_LOAD; + data PT_LOAD; +} + +SECTIONS +{ + . = 0; + __encl_base = .; + .tcs : { + *(.tcs*) + } : tcs + + . = ALIGN(4096); + .text : { + *(.text*) + *(.rodata*) + } : text + + . = ALIGN(4096); + .data : { + *(.data.encl_buffer) + *(.data*) + } : data + + /DISCARD/ : { + *(.comment*) + *(.note*) + *(.debug*) + *(.eh_frame*) + *(.dyn*) + *(.gnu.hash) + } +} + +ASSERT(!DEFINED(_GLOBAL_OFFSET_TABLE_), "Libcalls through GOT are not supported in enclaves") diff --git a/app/baresgx/main.c b/app/baresgx/main.c new file mode 100644 index 0000000..5bbd81e --- /dev/null +++ b/app/baresgx/main.c @@ -0,0 +1,100 @@ +#include +#include +#include "baresgx/urts.h" +#include "libsgxstep/debug.h" +#include "libsgxstep/enclave.h" +#include "libsgxstep/pt.h" +#include "libsgxstep/apic.h" +#include "libsgxstep/idt.h" +#include "libsgxstep/cache.h" +#include "libsgxstep/elf_parser.h" + +#define ENCLAVE_PATH "enclave/encl.elf" +#define ENCLAVE_DBG 1 +#define SINGLE_STEP_ENABLE 1 + +void *encl_page = NULL; +uint64_t *pte_encl = 0; +int do_step = 0, step_cnt = 0, zero_cnt = 0; + +void aep_cb_func(void) +{ + uint64_t erip = edbgrd_erip() - (uint64_t)get_enclave_base(); + info("^^ enclave RIP=%#lx; ACCESSED=%lu", erip, ACCESSED(*pte_encl)); + + if (do_step && ACCESSED(*pte_encl)) step_cnt++; + else if (do_step) zero_cnt++; + + /* + * NOTE: We explicitly clear the "accessed" bit of the _unprotected_ PTE + * referencing the enclave code page about to be executed, so as to be able + * to filter out "zero-step" results that won't set the accessed bit. + * + * Clearing the PTE "accessed" bit forces the CPU to take a ucode-assisted + * page-table walk for the first instruction following ERESUME, which + * causes that instruction to be much longer. We additionally flush this + * PTE from the cache to further delay the page-table walk and increase the + * landing space for the timer interrupt. + */ + *pte_encl = MARK_NOT_ACCESSED(*pte_encl); + flush(pte_encl); + + if (do_step) + { + apic_timer_irq(SGX_STEP_TIMER_INTERVAL); + } +} + +void handle_fault(int sig) +{ + info("caught signal %d; restoring trigger page access rights", sig); + *pte_encl = MARK_EXECUTABLE(*pte_encl); + do_step = 1; +} + +int main(void) +{ + void *tcs; + uint64_t rv; + idt_t idt = {0}; + + /************************************************************************/ + info_event("loading baresgx enclave"); + tcs = baresgx_load_elf_enclave(ENCLAVE_PATH, ENCLAVE_DBG); + print_enclave_info(); + register_symbols(ENCLAVE_PATH); + + info("dry run"); + rv = baresgx_enter_enclave(tcs, /*arg=*/0); + printf("\tL enclave returned %lx\n", rv); + + /************************************************************************/ + info_event("configuring attacker runtime"); + register_aep_cb(aep_cb_func); + ASSERT( signal(SIGSEGV, handle_fault) != SIG_ERR); + + encl_page = get_symbol_offset("encl_entry") + get_enclave_base(); + info("entry page at %p", encl_page); + ASSERT(pte_encl = remap_page_table_level(encl_page, PTE)); + ASSERT(PRESENT(*pte_encl)); + print_pte(pte_encl); + #if SINGLE_STEP_ENABLE + *pte_encl = MARK_EXECUTE_DISABLE(*pte_encl); + + map_idt(&idt); + install_kernel_irq_handler(&idt, __ss_irq_handler, IRQ_VECTOR); + apic_timer_oneshot(IRQ_VECTOR); + + __ss_irq_fired = 0; + apic_timer_irq( SGX_STEP_TIMER_INTERVAL ); + while (!__ss_irq_fired); + info("APIC timer IRQ handler seems to be working"); + #endif + + /************************************************************************/ + info_event("single-stepping baresgx enclave"); + rv = baresgx_enter_enclave(tcs, /*arg=*/0); + info("enclave returned %lx; step_cnt=%d; zero_cnt=%d\n", rv, step_cnt, zero_cnt); + + return 0; +} diff --git a/sdk/bare-sgx b/sdk/bare-sgx new file mode 160000 index 0000000..0e198fa --- /dev/null +++ b/sdk/bare-sgx @@ -0,0 +1 @@ +Subproject commit 0e198fa107fb6972f27430b06f73700e8dff6457