|
@@ -14,9 +14,8 @@
|
|
|
.type enclave_entry, @function
|
|
|
|
|
|
enclave_entry:
|
|
|
- # On EENTER, RAX is the current SSA index (aka CSSA),
|
|
|
- # RBX is the address of TCS, RCX is the address of AEP.
|
|
|
- # Other registers are not trusted.
|
|
|
+ # On EENTER, RAX is the current SSA index (aka CSSA), RBX is the address of
|
|
|
+ # TCS, RCX is the address of IP following EENTER. Other regs are not trusted.
|
|
|
|
|
|
# x86-64 sysv abi requires %rFLAGS.DF = 0 on entry to function call.
|
|
|
cld
|
|
@@ -24,6 +23,9 @@ enclave_entry:
|
|
|
cmpq $0, %rax
|
|
|
jne .Lprepare_resume
|
|
|
|
|
|
+ # ECALL return address in RCX (filled by EENTER hardware flow)
|
|
|
+ movq %rcx, %gs:SGX_ECALL_RETURN_ADDR
|
|
|
+
|
|
|
# The following code is hardened to defend attacks from untrusted host.
|
|
|
# Any states given by the host instead of the ISA must be assumed
|
|
|
# potentially malicious.
|
|
@@ -48,24 +50,25 @@ enclave_entry:
|
|
|
cmpq $0, %gs:SGX_OCALL_PREPARED
|
|
|
jne .Lreturn_from_ocall
|
|
|
|
|
|
- movq %rcx, %gs:SGX_AEP
|
|
|
-
|
|
|
- # 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:
|
|
|
# RDI - ECALL number
|
|
|
- # RSI - prointer to ecall arguments
|
|
|
+ # RSI - pointer to ecall arguments
|
|
|
# RDX - exit target
|
|
|
# RCX (former RSP) - The untrusted stack
|
|
|
# R8 - enclave base
|
|
|
|
|
|
+ cmpq $ECALL_THREAD_RESET, %rdi
|
|
|
+ je .Lhandle_thread_reset
|
|
|
+
|
|
|
+ # Except ecall_thread_reset, ecalls are only used to start a thread (main
|
|
|
+ # or additional threads). We already checked for case of ecall_thread_reset,
|
|
|
+ # so at this point we should only get exactly one ecall per thread
|
|
|
+ cmpq $0, %gs:SGX_THREAD_STARTED
|
|
|
+ je 1f
|
|
|
+ FAIL_LOOP
|
|
|
+1:
|
|
|
+ movq $1, %gs:SGX_THREAD_STARTED
|
|
|
+
|
|
|
# calculate enclave base = RBX (trusted) - %gs:SGX_TCS_OFFSET
|
|
|
subq %gs:SGX_TCS_OFFSET, %rbx
|
|
|
movq %rbx, %r8
|
|
@@ -95,6 +98,27 @@ enclave_entry:
|
|
|
# handle_ecall will only return when invalid parameters has been passed.
|
|
|
FAIL_LOOP
|
|
|
|
|
|
+ # clear TLS variables for thread reuse
|
|
|
+.Lhandle_thread_reset:
|
|
|
+ movq $0, %gs:SGX_READY_FOR_EXCEPTIONS
|
|
|
+
|
|
|
+ # Signals are impossible at this point: benign untrusted runtime blocks
|
|
|
+ # all signals (see sgx_ocall_exit()), and even if malicious one doesn't
|
|
|
+ # block them, signals are ignored due to SGX_READY_FOR_EXCEPTIONS = 0.
|
|
|
+ movq $0, %gs:SGX_THREAD_STARTED
|
|
|
+
|
|
|
+ # Assertion: thread is reset only after special-case OCALL_EXIT which
|
|
|
+ # does *not* set SGX_OCALL_PREPARED = 1.
|
|
|
+ cmpq $0, %gs:SGX_OCALL_PREPARED
|
|
|
+ je 1f
|
|
|
+ FAIL_LOOP
|
|
|
+1:
|
|
|
+ # Instead of jumping to .Lclear_and_eexit, simply perform EEXIT because
|
|
|
+ # there is no modified state to clear in this "thread-reset" code path.
|
|
|
+ movq %gs:SGX_ECALL_RETURN_ADDR, %rbx
|
|
|
+ movq $EEXIT, %rax
|
|
|
+ ENCLU
|
|
|
+
|
|
|
.Lprepare_resume:
|
|
|
# PAL convention:
|
|
|
# RDI - external event
|
|
@@ -127,6 +151,7 @@ enclave_entry:
|
|
|
cmpl $0, %edi
|
|
|
jne .Lhandle_exception
|
|
|
|
|
|
+.Lignore_exception:
|
|
|
# clear the registers
|
|
|
xorq %rdi, %rdi
|
|
|
xorq %rsi, %rsi
|
|
@@ -180,12 +205,18 @@ enclave_entry:
|
|
|
cmpq $0, %rsi
|
|
|
je .Lsetup_exception_handler
|
|
|
|
|
|
- # Assertion:
|
|
|
+ # The usual case (bar OCALL_EXIT):
|
|
|
# SGX_OCALL_PREPARED set to 1 before SGX_STACK is set to enclave stack.
|
|
|
# SGX_OCALL_PREPARED set to 0 after SGX_STACK is set to 0.
|
|
|
cmpq $0, %gs:SGX_OCALL_PREPARED
|
|
|
jne 1f
|
|
|
- FAIL_LOOP
|
|
|
+
|
|
|
+ # At this point, we are in the exception handler, SGX_STACK != 0 but
|
|
|
+ # SGX_OCALL_PREPARED = 0. This can only happen if we are interrupted
|
|
|
+ # during a special case of never-returning OCALL_EXIT. Because the
|
|
|
+ # thread is going to exit anyway, we can ignore this exception.
|
|
|
+ jmp .Lignore_exception
|
|
|
+
|
|
|
1:
|
|
|
# At this point, we are in the exception handler,
|
|
|
# SGX_STACK=<trusted pointer to enclave stack>, SGX_OCALL_PREPARED=1,
|
|
@@ -431,6 +462,14 @@ sgx_ocall:
|
|
|
|
|
|
pushq %rbp
|
|
|
|
|
|
+ # OCALL_EXIT should never return (see sgx_ocall_exit(): it always exits
|
|
|
+ # the thread). Skip setting SGX_OCALL_PREPARED to land in special-case
|
|
|
+ # of ECALL_THREAD_RESET (issued in sgx_ocall_exit()) later. Note that if
|
|
|
+ # there is an interrupt (which usually would result in a simulated
|
|
|
+ # return of -EINTR), it will be silently ignored via .Lignore_exception.
|
|
|
+ cmpq $OCALL_EXIT, %rdi
|
|
|
+ je .Locall_after_set_ocall_prepared
|
|
|
+
|
|
|
.Locall_before_set_ocall_prepared:
|
|
|
movq $1, %gs:SGX_OCALL_PREPARED
|
|
|
.Locall_after_set_ocall_prepared:
|