Browse Source

[Pal/Linux-SGX] Make state checks in enclave_entry more rigid

This change enforces that:

 1. .Lreturn_from_ocall can only be reached if we have prepared the
     stack for it.

 2. only one ecall is called per thread.

The check before didn't cover all cases. For example it was possible to
call ECALL_ENCLAVE_START with an NULL pointer as ecall_args and then
enclave_entry would jump to .Lreturn_from_ocall on the next enclave
entry even though the stack was never prepared for an ocall.
Simon Gaiser 6 years ago
parent
commit
55a6ecb69f

+ 2 - 0
Pal/src/host/Linux-SGX/asm-offsets.c

@@ -62,6 +62,8 @@ void dummy(void)
     OFFSET(SGX_USTACK_TOP, enclave_tls, ustack_top);
     OFFSET(SGX_USTACK_TOP, enclave_tls, ustack_top);
     OFFSET(SGX_USTACK, enclave_tls, ustack);
     OFFSET(SGX_USTACK, enclave_tls, ustack);
     OFFSET(SGX_THREAD, enclave_tls, thread);
     OFFSET(SGX_THREAD, enclave_tls, thread);
+    OFFSET(SGX_OCALL_PREPARED, enclave_tls, ocall_prepared);
+    OFFSET(SGX_ECALL_CALLED, enclave_tls, ecall_called);
 
 
     /* sgx_arch_tcs_t */
     /* sgx_arch_tcs_t */
     DEFINE(TCS_SIZE, sizeof(sgx_arch_tcs_t));
     DEFINE(TCS_SIZE, sizeof(sgx_arch_tcs_t));

+ 20 - 3
Pal/src/host/Linux-SGX/enclave_entry.S

@@ -44,11 +44,24 @@ enclave_entry:
 	# from a OCALL in the untrusted PAL. Attackers can manipulate RDI
 	# from a OCALL in the untrusted PAL. Attackers can manipulate RDI
 	# to deceive the trusted PAL.
 	# to deceive the trusted PAL.
 
 
-	# A safe design: check if %gs:SGX_EXIT_TARGET is ever assigned
-	movq %gs:SGX_EXIT_TARGET, %rcx
-	cmpq $0, %rcx
+	# This thread can be interrupted but then the above check branches to
+	# .Lhandle_resume. So the outside can't re-enter the checks below in
+	# the middle.
+
+	# Only jump to .Lreturn_from_ocall if we have prepared the stack for
+	# it.
+	cmpq $0, %gs:SGX_OCALL_PREPARED
 	jne .Lreturn_from_ocall
 	jne .Lreturn_from_ocall
 
 
+	# Ecalls are only used to start a thread (either the main or an
+	# additional thread). So per thread we should only get exactly one
+	# ecall. Enforce this here.
+	cmpq $0, %gs:SGX_ECALL_CALLED
+	je 1f
+	FAIL_LOOP
+1:
+	movq $1, %gs:SGX_ECALL_CALLED
+
 	# PAL convention:
 	# PAL convention:
 	# RDI - index in ecall_table
 	# RDI - index in ecall_table
 	# RSI - prointer to ecall arguments
 	# RSI - prointer to ecall arguments
@@ -244,6 +257,8 @@ sgx_ocall:
 
 
 	pushq %rbp
 	pushq %rbp
 
 
+	movq $1, %gs:SGX_OCALL_PREPARED
+
 	jmp .Leexit
 	jmp .Leexit
 
 
 .Leexit:
 .Leexit:
@@ -272,6 +287,8 @@ sgx_ocall:
 	# RDI - return value
 	# RDI - return value
 	# RSI - external event (if there is any)
 	# RSI - external event (if there is any)
 
 
+	movq $0, %gs:SGX_OCALL_PREPARED
+
 	movq %rdi, %rax
 	movq %rdi, %rax
 
 
 	# restore FSBASE if necessary
 	# restore FSBASE if necessary

+ 2 - 0
Pal/src/host/Linux-SGX/sgx_tls.h

@@ -17,6 +17,8 @@ struct enclave_tls {
     void *   ustack_top;
     void *   ustack_top;
     void *   ustack;
     void *   ustack;
     struct pal_handle_thread * thread;
     struct pal_handle_thread * thread;
+    uint64_t ocall_prepared;
+    uint64_t ecall_called;
 };
 };
 
 
 #ifndef DEBUG
 #ifndef DEBUG