Skip to content

Commit

Permalink
app/baresgx: minimal bare-metal test enclave
Browse files Browse the repository at this point in the history
This directory contains a minimal test enclave hand written in
assembly using the `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.

See also: <https://github.com/jovanbulck/bare-sgx>
  • Loading branch information
jovanbulck committed Sep 19, 2024
1 parent f527d53 commit 1483dc8
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
*.mk
*.mod
*.swn
*.elf
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
42 changes: 42 additions & 0 deletions app/baresgx/Makefile
Original file line number Diff line number Diff line change
@@ -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)
10 changes: 10 additions & 0 deletions app/baresgx/README.md
Original file line number Diff line number Diff line change
@@ -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.
11 changes: 11 additions & 0 deletions app/baresgx/enclave/Makefile
Original file line number Diff line number Diff line change
@@ -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
41 changes: 41 additions & 0 deletions app/baresgx/enclave/encl.S
Original file line number Diff line number Diff line change
@@ -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

40 changes: 40 additions & 0 deletions app/baresgx/enclave/encl.lds
Original file line number Diff line number Diff line change
@@ -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")
100 changes: 100 additions & 0 deletions app/baresgx/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include <stdio.h>
#include <signal.h>
#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;
}
1 change: 1 addition & 0 deletions sdk/bare-sgx
Submodule bare-sgx added at 0e198f

0 comments on commit 1483dc8

Please sign in to comment.