enclave_entry.S 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  1. #include "sgx_arch.h"
  2. #include "asm-offsets.h"
  3. # In some cases, like bogus parameters passed to enclave_entry, it's tricky to
  4. # return cleanly (passing the correct return address to EEXIT, OCALL_EXIT can
  5. # be interrupted, etc.). Since those cases should only ever happen with a
  6. # malicious urts, just go into an endless loop.
  7. .macro FAIL_LOOP
  8. .Lfail_loop\@:
  9. jmp .Lfail_loop\@
  10. .endm
  11. .macro CHECK_IF_SIGNAL_STACK_IS_USED stack_reg, label_on_stack, label_out_of_stack
  12. cmpq %gs:SGX_SIG_STACK_LOW, \stack_reg
  13. jb \label_out_of_stack
  14. cmpq %gs:SGX_SIG_STACK_HIGH, \stack_reg
  15. ja \label_out_of_stack
  16. jmp \label_on_stack
  17. .endm
  18. .global enclave_entry
  19. .type enclave_entry, @function
  20. enclave_entry:
  21. # On EENTER, RAX is the current SSA index (aka CSSA), RBX is the address of
  22. # TCS, RCX is the address of IP following EENTER. Other regs are not trusted.
  23. # x86-64 sysv abi requires %rFLAGS.DF = 0 on entry to function call.
  24. cld
  25. cmpq $0, %rax
  26. jne .Lprepare_resume
  27. # ECALL return address in RCX (filled by EENTER hardware flow)
  28. movq %rcx, %gs:SGX_ECALL_RETURN_ADDR
  29. # The following code is hardened to defend attacks from untrusted host.
  30. # Any states given by the host instead of the ISA must be assumed
  31. # potentially malicious.
  32. #
  33. # For instance, Jo Van Bulck contributed a detailed vulnerability report
  34. # in https://github.com/oscarlab/graphene/issues/28. (Fixed)
  35. # Brief description of the vulnerabilities:
  36. # The previous implementation does not check the index of entry
  37. # functions (RDI at enclave entry) given by the untrusted PAL.
  38. # An attacker can cause overflow/underflow to jump to random
  39. # locaion in enclaves. Moreover, we used a specific index
  40. # (RETURN_FROM_OCALL) to tell if the control flow is returned
  41. # from a OCALL in the untrusted PAL. Attackers can manipulate RDI
  42. # to deceive the trusted PAL.
  43. # This thread can be interrupted but then the above check branches to
  44. # .Lprepare_resume. So the outside can't re-enter the checks below in
  45. # the middle.
  46. # Only jump to .Lreturn_from_ocall if we have prepared the stack for
  47. # it.
  48. cmpq $0, %gs:SGX_PRE_OCALL_STACK
  49. jne .Lreturn_from_ocall
  50. # PAL convention:
  51. # RDI - ECALL number
  52. # RSI - pointer to ecall arguments
  53. # RDX - exit target
  54. # RCX (former RSP) - The untrusted stack
  55. # R8 - enclave base
  56. cmpq $ECALL_THREAD_RESET, %rdi
  57. je .Lhandle_thread_reset
  58. # Except ecall_thread_reset, ecalls are only used to start a thread (main
  59. # or additional threads). We already checked for case of ecall_thread_reset,
  60. # so at this point we should only get exactly one ecall per thread
  61. cmpq $0, %gs:SGX_THREAD_STARTED
  62. je 1f
  63. FAIL_LOOP
  64. 1:
  65. movq $1, %gs:SGX_THREAD_STARTED
  66. # calculate enclave base = RBX (trusted) - %gs:SGX_TCS_OFFSET
  67. subq %gs:SGX_TCS_OFFSET, %rbx
  68. movq %rbx, %r8
  69. # push untrusted stack address to RCX
  70. movq %rsp, %rcx
  71. # switch to enclave stack: enclave base + %gs:SGX_INITIAL_STACK_OFFSET
  72. addq %gs:SGX_INITIAL_STACK_OFFSET, %rbx
  73. movq %rbx, %rsp
  74. # clear the rest of register states
  75. xorq %rax, %rax
  76. xorq %rbx, %rbx
  77. xorq %r9, %r9
  78. xorq %r10, %r10
  79. xorq %r11, %r11
  80. xorq %r12, %r12
  81. xorq %r13, %r13
  82. xorq %r14, %r14
  83. xorq %r15, %r15
  84. # clear the Alignment Check flag (%rFLAGS.AC) to prevent #AC-fault side channel;
  85. # this overrides 8B on enclave stack but stack is not used at this point anyway
  86. pushfq
  87. andq $(~RFLAGS_AC), (%rsp)
  88. popfq
  89. # Clear "extended" state (FPU aka x87, SSE, AVX, ...).
  90. # TODO: We currently clear only state covered by FXRSTOR but not by XRSTOR
  91. # (e.g., no clearing of YMM/ZMM regs). This is because we didn't read
  92. # the value of XFRM yet, so we don't know whether XRSTOR is safe at
  93. # this point.
  94. leaq xsave_reset_state(%rip), %rax
  95. fxrstor (%rax)
  96. xorq %rax, %rax
  97. # register states need to be carefully checked, so we move the handling
  98. # to handle_ecall() in enclave_ecalls.c
  99. callq handle_ecall
  100. # handle_ecall will only return when invalid parameters has been passed.
  101. FAIL_LOOP
  102. # clear TLS variables for thread reuse
  103. .Lhandle_thread_reset:
  104. movq $0, %gs:SGX_READY_FOR_EXCEPTIONS
  105. # Assertion: thread is reset only after special-case OCALL_EXIT.
  106. cmpq $0, %gs:SGX_OCALL_EXIT_CALLED
  107. jne 1f
  108. FAIL_LOOP
  109. 1:
  110. # At this point, the thread has completely exited from the point of view
  111. # of LibOS. We can now set *clear_child_tid to 0, which will trigger
  112. # async helper thread in LibOS, who will wake up parent thread if any.
  113. cmpq $0, %gs:SGX_CLEAR_CHILD_TID
  114. je 1f
  115. movq %gs:SGX_CLEAR_CHILD_TID, %rbx
  116. movl $0, (%rbx)
  117. 1:
  118. # Signals are impossible at this point: benign untrusted runtime blocks
  119. # all signals (see sgx_ocall_exit()), and even if malicious one doesn't
  120. # block them, signals are ignored due to SGX_READY_FOR_EXCEPTIONS = 0.
  121. movq $0, %gs:SGX_THREAD_STARTED
  122. movq $0, %gs:SGX_OCALL_EXIT_CALLED
  123. movq $0, %gs:SGX_PRE_OCALL_STACK
  124. # Instead of jumping to .Lclear_and_eexit, simply perform EEXIT because
  125. # there is no modified state to clear in this "thread-reset" code path.
  126. movq %gs:SGX_ECALL_RETURN_ADDR, %rbx
  127. movq $EEXIT, %rax
  128. ENCLU
  129. .Lprepare_resume:
  130. # PAL convention:
  131. # RDI - external event
  132. # Nested exceptions at the host-OS level are disallowed:
  133. # - Synchronous exceptions are assumed to never happen during
  134. # prepare_resume;
  135. # - Asynchronous signals are not nested by benign host OS because
  136. # we mask asynchronous signals on signal handler.
  137. # If malicious host OS injects a nested signal, CSSA != 1 and we go
  138. # into FAIL_LOOP. Currently this check is assertion only because it
  139. # is also enforced by EENTER since enclave is created with NSSA=2.
  140. cmpq $1, %rax
  141. je 1f
  142. FAIL_LOOP
  143. 1:
  144. movq %gs:SGX_GPR, %rbx
  145. movq %rdi, %rsi
  146. xorq %rdi, %rdi
  147. movl SGX_GPR_EXITINFO(%rbx), %edi
  148. testl $0x80000000, %edi
  149. jnz .Lhandle_exception
  150. movl %esi, %edi
  151. # use external event - only the first 8 bits count
  152. andl $0xff, %edi
  153. cmpl $0, %edi
  154. jne .Lhandle_exception
  155. .Lignore_exception:
  156. # clear the registers
  157. xorq %rdi, %rdi
  158. xorq %rsi, %rsi
  159. # exit address in RDX, mov it to RBX
  160. movq %rdx, %rbx
  161. jmp .Lclear_and_eexit
  162. .Lhandle_exception:
  163. # If this enclave thread has not been initialized yet, we should not
  164. # try to call an event handler yet.
  165. cmpq $0, %gs:SGX_READY_FOR_EXCEPTIONS
  166. jne 1f
  167. FAIL_LOOP
  168. 1:
  169. # Beware of races between host signal delivery and handling %rsp in
  170. # this entry code. Consider the following scenario:
  171. #
  172. # 1. We are inside the enclave but %rsp isn't restored yet to something
  173. # inside the enclave. That's for example the case when returning from
  174. # an ocall.
  175. # 2. The enclave gets interrupted. The not restored %rsp is pushed into
  176. # SGX_GPR_RSP by the processor.
  177. # 3. The host enters the enclave again and indicates that there's a new
  178. # signal.
  179. # 4. SGX_GPR_RSP points to the untrusted stack
  180. #
  181. # The below code should be fine since it detects an interrupted ocall
  182. # and restores %rsp from SGX_PRE_OCALL_STACK before exception handling
  183. # (see below for full details)
  184. # The stack swap logic does not need to be atomic because nested
  185. # exceptions are disallowed by SGX due to TCS.NSSA == 2 (thus,
  186. # .Lhandle_exception logic cannot be nested)
  187. # Check if we got interrupted during an ocall case (except OCALL_EXIT),
  188. # i.e. SGX_PRE_OCALL_STACK is set.
  189. movq %gs:SGX_PRE_OCALL_STACK, %rsi
  190. cmpq $0, %rsi
  191. jne .Lhandle_interrupted_ocall
  192. # If this is not the case check if OCALL_EXIT has been called. If this
  193. # is not the case setup the exception handler for the non-ocall case.
  194. cmpq $0, %gs:SGX_OCALL_EXIT_CALLED
  195. je .Lsetup_exception_handler
  196. # We are interrupted during the never-returning OCALL_EXIT. Because the
  197. # thread is going to exit anyway, we can ignore this exception.
  198. jmp .Lignore_exception
  199. .Lhandle_interrupted_ocall:
  200. # At this point, we are in the exception handler and
  201. # SGX_PRE_OCALL_STACK=<trusted pointer to enclave stack>. I.e. we are
  202. # interrupted during handling of enclave's sgx_ocall/return_from_ocall
  203. # assembly code.
  204. #
  205. # Triggering the exception handler while SGX_PRE_OCALL_STACK != 0 would
  206. # be problematic because it could itself issue nested ocalls. This
  207. # would mean the SGX_PRE_OCALL_STACK logic would need to handle
  208. # nesting.
  209. #
  210. # Instead if we're in such situation, we emulate it as if %rip reached to
  211. # the safe point, .Lreturn_from_ocall_after_stack_restore.
  212. #
  213. # Ocall sequence:
  214. # 1. call sgx_ocall()
  215. # 2. SGX_PRE_OCALL_STACK=%rsp: save trusted stack
  216. # 3. EEXIT
  217. # 4. untrusted PAL which issues real host system call
  218. # 5. EENTER (and start from enclave_entry)
  219. # 6. .Lreturn_from_ocall:
  220. # 7. (%rsp, SGX_STACK) = (SGX_STACK, 0): restore trusted stack
  221. # 8. .Lreturn_from_ocall_after_stack_restore:
  222. # 9. call _DkHandleExternalEvent() if interrupted
  223. # 10. return from sgx_ocall() to the caller
  224. #
  225. # It is also required that sgx_ocall() be atomic regarding to async exception.
  226. # When host async signal arrives, sgx_ocall() should result in EINTR.
  227. #
  228. # There are three possibilities when exactly host async signal arrives:
  229. # A. before exiting enclave to perform host syscall
  230. # B. after exiting enclave and before re-entering enclave
  231. # (i.e., during untrusted execution of host syscall)
  232. # C. after re-entering enclave but before returning to sgx_ocall().
  233. #
  234. # Note that Case A didn't even issue host syscall, Case B may have
  235. # interrupted host syscall (but maybe interrupt came after successful
  236. # host syscall), and Case C was interrupted after successful host
  237. # syscall. In Case C, the result of host system call must be preserved
  238. # to be replayed in later invocation.
  239. #
  240. # On host async signal we treat these cases as follows:
  241. # A. right-before EEXIT (2. in above sequence, before 2. got executed
  242. # we don't land here):
  243. # - set EINTR and forward %rip to exception handler
  244. # B. during untrusted PAL (3. - 4. in above sequence):
  245. # - code in _DkTerminateSighandler() must handle this case
  246. # TODO: fix _DkTerminateSighandler() to not lose the result of successful
  247. # system call.
  248. # C. right-after EENTER (5. - 7. in above sequence):
  249. # - ocall succeeded, forward %rip to exception handler
  250. # Find out which of cases A, B, or C happened:
  251. # - copy rip at which the enclave was interrupted into %rax,
  252. # - copy the boundaries between cases A, B, and C into %r11,
  253. # - compare enclave's rip against these boundaries (%rax vs %r11).
  254. movq SGX_GPR_RIP(%rbx), %rax
  255. leaq .Locall_about_to_eexit_begin(%rip), %r11
  256. cmpq %r11, %rax
  257. jb .Lhandle_interrupted_ocall_case_c
  258. leaq .Locall_about_to_eexit_end(%rip), %r11
  259. cmpq %r11, %rax
  260. jae .Lhandle_interrupted_ocall_case_c
  261. # Case A. We are right-before EEXIT for ocall in between
  262. # [.Locall_about_to_eexit_begin, .Locall_about_to_eexit_end)
  263. # Skip EEXIT as if ocall returned EINTR.
  264. # If there is registered signal handler for the current exception,
  265. # _DkHandleExternalEvent() will be called (and thus we need to save
  266. # %rdi = <external event>) before returning from ocall.
  267. movq $-EINTR, SGX_GPR_RDI(%rbx) # return value for .Lreturn_from_ocall
  268. # fallthrough to Case C.
  269. # This code cannot land in Case B because:
  270. # (1) this code path (.Lhandle_exception) is triggered only if we haven't
  271. # yet exited the enclave when signal arrived, and
  272. # (2) in Case B, we exited the enclave and signal arrived while in
  273. # untrusted code. The two conditions cannot be true at the same time,
  274. # so Case B never happens here (Case B results in return_from_ocall code
  275. # path below).
  276. .Lhandle_interrupted_ocall_case_c:
  277. # Case C. We are right-after EENTER returning from successful ocall.
  278. # Move %rip to .Lreturn_from_ocall_after_stack_restore and let
  279. # _DkHandleExternalEvent() handle the exception.
  280. # SGX_GPR_RDI(%rbx): don't touch successful ocall result.
  281. movq %rdi, SGX_GPR_RSI(%rbx) # external event for .Lreturn_from_ocall
  282. leaq .Lreturn_from_ocall_after_stack_restore(%rip), %rax
  283. movq %rax, SGX_GPR_RIP(%rbx)
  284. movq %rsi, SGX_GPR_RSP(%rbx)
  285. movq $0, %gs:SGX_PRE_OCALL_STACK
  286. andq $(~(RFLAGS_DF | RFLAGS_AC)), SGX_GPR_RFLAGS(%rbx)
  287. jmp .Leexit_exception
  288. .Lsetup_exception_handler:
  289. # The thread got interrupted outside of ocall handling (see above for
  290. # that special case). We inject a call to _DkExceptionHandler into the
  291. # interrupted thread which will handle the exception on ERESUME.
  292. # The last instructions of _restore_sgx_context need to be atomic for
  293. # the code below (see _restore_sgx_context for more details). So
  294. # emulate this if we were interrupted there.
  295. leaq .Ltmp_rip_saved0(%rip), %rax
  296. cmpq %rax, SGX_GPR_RIP(%rbx)
  297. je .Lemulate_tmp_rip_saved0
  298. leaq .Ltmp_rip_saved1(%rip), %rax
  299. cmpq %rax, SGX_GPR_RIP(%rbx)
  300. je .Lemulate_tmp_rip_saved1
  301. leaq .Ltmp_rip_saved2(%rip), %rax
  302. cmpq %rax, SGX_GPR_RIP(%rbx)
  303. je .Lemulate_tmp_rip_saved2
  304. jmp .Lemulate_tmp_rip_end
  305. .Lemulate_tmp_rip_saved0:
  306. # emulate movq SGX_CPU_CONTEXT_R15 - SGX_CPU_CONTEXT_RIP(%rsp), %r15
  307. movq SGX_GPR_RSP(%rbx), %rax
  308. movq SGX_CPU_CONTEXT_R15 - SGX_CPU_CONTEXT_RIP(%rax), %rax
  309. movq %rax, SGX_GPR_R15(%rbx)
  310. .Lemulate_tmp_rip_saved1:
  311. # emulate movq SGX_CPU_CONTEXT_RSP - SGX_CPU_CONTEXT_RIP(%rsp), %rsp
  312. movq SGX_GPR_RSP(%rbx), %rax
  313. movq SGX_CPU_CONTEXT_RSP - SGX_CPU_CONTEXT_RIP(%rax), %rax
  314. movq %rax, SGX_GPR_RSP(%rbx)
  315. .Lemulate_tmp_rip_saved2:
  316. # emulate jmp *%gs:SGX_TMP_RIP
  317. movq %gs:SGX_TMP_RIP, %rax
  318. movq %rax, SGX_GPR_RIP(%rbx)
  319. .Lemulate_tmp_rip_end:
  320. movq SGX_GPR_RSP(%rbx), %rsi
  321. CHECK_IF_SIGNAL_STACK_IS_USED %rsi, .Lon_signal_stack, .Lout_of_signal_stack
  322. .Lout_of_signal_stack:
  323. movq %gs:SGX_SIG_STACK_HIGH, %rsi
  324. # When switching to the not yet used signal stack we don't need to reserve
  325. # a redzone. So move the stack pointer up here to undo the move down below.
  326. addq $RED_ZONE_SIZE, %rsi
  327. # Setup stack for the signal handler, _DkExceptionHandler().
  328. # _restore_sgx_context() must be used to return back to the
  329. # original context.
  330. # Stack layout:
  331. # 8-bytes padding: (8 mod 16) bytes aligned for x86 ABI
  332. # NOTE: there is no saved rip to return.
  333. # sgx_cpu_context_t: 144 bytes
  334. # xsave area: PAL_XSTATE_ALIGN=64 bytes aligned
  335. # padding if necessary
  336. # RED_ZONE unless newly switching to signal stack
  337. #define STACK_PADDING_SIZE (PAL_FP_XSTATE_MAGIC2_SIZE + 8)
  338. #define STACK_FRAME_SUB \
  339. (SGX_CPU_CONTEXT_SIZE + RED_ZONE_SIZE + STACK_PADDING_SIZE)
  340. .Lon_signal_stack:
  341. movl xsave_size(%rip), %eax
  342. addq $STACK_FRAME_SUB, %rax
  343. subq %rax, %rsi
  344. # Align xsave area to 64 bytes after sgx_cpu_context_t
  345. andq $~(PAL_XSTATE_ALIGN - 1), %rsi
  346. subq $SGX_CPU_CONTEXT_XSTATE_ALIGN_SUB, %rsi
  347. # we have exitinfo in RDI, swap with the one on GPR
  348. # and dump into the context
  349. xchgq %rdi, SGX_GPR_RDI(%rbx) # 1st argument for _DkExceptionHandler()
  350. movq %rdi, SGX_CPU_CONTEXT_RDI(%rsi)
  351. # dump the rest of context
  352. movq SGX_GPR_RAX(%rbx), %rdi
  353. movq %rdi, SGX_CPU_CONTEXT_RAX(%rsi)
  354. movq SGX_GPR_RCX(%rbx), %rdi
  355. movq %rdi, SGX_CPU_CONTEXT_RCX(%rsi)
  356. movq SGX_GPR_RDX(%rbx), %rdi
  357. movq %rdi, SGX_CPU_CONTEXT_RDX(%rsi)
  358. movq SGX_GPR_RBX(%rbx), %rdi
  359. movq %rdi, SGX_CPU_CONTEXT_RBX(%rsi)
  360. movq SGX_GPR_RSP(%rbx), %rdi
  361. movq %rdi, SGX_CPU_CONTEXT_RSP(%rsi)
  362. movq SGX_GPR_RBP(%rbx), %rdi
  363. movq %rdi, SGX_CPU_CONTEXT_RBP(%rsi)
  364. movq SGX_GPR_RSI(%rbx), %rdi
  365. movq %rdi, SGX_CPU_CONTEXT_RSI(%rsi)
  366. /* rdi is saved above */
  367. movq SGX_GPR_R8(%rbx), %rdi
  368. movq %rdi, SGX_CPU_CONTEXT_R8(%rsi)
  369. movq SGX_GPR_R9(%rbx), %rdi
  370. movq %rdi, SGX_CPU_CONTEXT_R9(%rsi)
  371. movq SGX_GPR_R10(%rbx), %rdi
  372. movq %rdi, SGX_CPU_CONTEXT_R10(%rsi)
  373. movq SGX_GPR_R11(%rbx), %rdi
  374. movq %rdi, SGX_CPU_CONTEXT_R11(%rsi)
  375. movq SGX_GPR_R12(%rbx), %rdi
  376. movq %rdi, SGX_CPU_CONTEXT_R12(%rsi)
  377. movq SGX_GPR_R13(%rbx), %rdi
  378. movq %rdi, SGX_CPU_CONTEXT_R13(%rsi)
  379. movq SGX_GPR_R14(%rbx), %rdi
  380. movq %rdi, SGX_CPU_CONTEXT_R14(%rsi)
  381. movq SGX_GPR_R15(%rbx), %rdi
  382. movq %rdi, SGX_CPU_CONTEXT_R15(%rsi)
  383. movq SGX_GPR_RFLAGS(%rbx), %rdi
  384. movq %rdi, SGX_CPU_CONTEXT_RFLAGS(%rsi)
  385. movq SGX_GPR_RIP(%rbx), %rdi
  386. movq %rdi, SGX_CPU_CONTEXT_RIP(%rsi)
  387. # Pass pointer to sgx_cpu_context_t and PAL_XREGS_STATE to _DkExceptionHandler
  388. movq %rsi, SGX_GPR_RSI(%rbx) # 2nd argument for _DkExceptionHandler()
  389. movq %rsi, SGX_GPR_RDX(%rbx)
  390. addq $SGX_CPU_CONTEXT_SIZE, SGX_GPR_RDX(%rbx) # 3rd argument for _DkExceptionHandler()
  391. # TODO: save EXINFO in MISC region
  392. # x86-64 sysv abi requires 16B alignment of stack before call instruction
  393. # which implies a (8 mod 16)B alignment on function entry (due to implicit
  394. # push %rip). Since we already aligned xsave area above, this requirement
  395. # is satisfied.
  396. subq $8, %rsi
  397. movq %rsi, SGX_GPR_RSP(%rbx)
  398. # clear RFLAGS.DF to conform to the SysV ABI, clear RFLAGS.AC to prevent
  399. # the #AC-fault side channel
  400. andq $(~(RFLAGS_DF | RFLAGS_AC)), SGX_GPR_RFLAGS(%rbx)
  401. # new RIP is the exception handler
  402. leaq _DkExceptionHandler(%rip), %rdi
  403. movq %rdi, SGX_GPR_RIP(%rbx)
  404. movq %rdx, %rbx
  405. leaq SGX_CPU_CONTEXT_SIZE + 8(%rsi), %rdi
  406. leaq 1f(%rip), %r11
  407. jmp __save_xregs
  408. 1:
  409. movq %rbx, %rdx
  410. .Leexit_exception:
  411. # clear the registers
  412. xorq %rdi, %rdi
  413. xorq %rsi, %rsi
  414. # exit address in RDX, mov it to RBX
  415. movq %rdx, %rbx
  416. jmp .Lclear_and_eexit
  417. .global sgx_ocall
  418. .type sgx_ocall, @function
  419. sgx_ocall:
  420. # arguments:
  421. # RDI: OCALL number (code)
  422. # RSI: OCALL args on untrusted stack (ms)
  423. #
  424. # sgx_cpu_context_t:
  425. # RAX = 0: place holder
  426. # RCX
  427. # ...
  428. # RFLAGS
  429. # RIP
  430. # xsave area
  431. # xregs
  432. # (padding)
  433. # --- stack may be non-contiguous as we may switch the stack to signal stack
  434. # previous RBP
  435. # previous RIP: pushed by callq
  436. .cfi_startproc
  437. pushq %rbp
  438. .cfi_adjust_cfa_offset 8
  439. movq %rsp, %rbp
  440. .cfi_offset %rbp, -16
  441. .cfi_def_cfa_register %rbp
  442. CHECK_IF_SIGNAL_STACK_IS_USED %rsp, .Lon_signal_stack_ocall, .Lout_of_signal_stack_ocall
  443. .Lout_of_signal_stack_ocall:
  444. movq %gs:SGX_SIG_STACK_HIGH, %rsp
  445. .Lon_signal_stack_ocall:
  446. movl xsave_size(%rip), %eax
  447. addq $STACK_PADDING_SIZE, %rax
  448. subq %rax, %rsp
  449. andq $~(PAL_XSTATE_ALIGN - 1), %rsp
  450. pushq %rdx
  451. pushq %rdi
  452. movq %rsp, %rdi
  453. addq $2 * 8, %rdi # adjust pushq %rdx; pushq %rdi above
  454. callq save_xregs
  455. popq %rdi
  456. popq %rdx
  457. movq 8(%rbp), %rax
  458. pushq %rax # previous RIP
  459. pushfq
  460. # Under GDB, single-stepping sets Trap Flag (TP) of EFLAGS,
  461. # thus TP=1 is stored on pushfq above. Upon consequent popfq,
  462. # TP is 1, resulting in spurious trap. Reset TP here.
  463. andq $~0x100, (%rsp)
  464. pushq %r15
  465. pushq %r14
  466. pushq %r13
  467. pushq %r12
  468. pushq %r11
  469. pushq %r10
  470. pushq %r9
  471. pushq %r8
  472. pushq %rdi
  473. pushq %rsi
  474. movq (%rbp), %rax
  475. pushq %rax # previous RBP
  476. leaq 16(%rbp), %rax
  477. pushq %rax # previous RSP
  478. pushq %rbx
  479. pushq %rdx
  480. pushq %rcx
  481. pushq $0 # placeholder for RAX
  482. # OCALL_EXIT should never return (see sgx_ocall_exit(): it always exits
  483. # the thread). Skip setting SGX_PRE_OCALL_STACK to land in special-case
  484. # of ECALL_THREAD_RESET (issued in sgx_ocall_exit()) later. Note that if
  485. # there is an interrupt (which usually would result in a simulated
  486. # return of -EINTR), it will be silently ignored via
  487. # .Lignore_exception.
  488. cmpq $OCALL_EXIT, %rdi
  489. jne 1f
  490. movq $1, %gs:SGX_OCALL_EXIT_CALLED
  491. jmp .Locall_about_to_eexit_begin
  492. 1:
  493. movq %rsp, %gs:SGX_PRE_OCALL_STACK
  494. .Locall_about_to_eexit_begin:
  495. # From here .Lhandle_exception can mess with our state (%rip and %rsp).
  496. # We therefore need to be extremely careful when making changes here.
  497. #
  498. # It's ok to use the untrusted stack and exit target below without
  499. # checks since the processor will ensure that after exiting enclave
  500. # mode in-enclave memory can't be accessed.
  501. movq %gs:SGX_USTACK, %rsp
  502. #ifdef DEBUG
  503. # Push %rip of some code inside __morestack() on untrusted stack.
  504. # At sgx_entry(), GDB deduces saved_rip by looking at CFA-8 = %rsp.
  505. leaq .Lfor_cfa_debug_info(%rip), %r8
  506. pushq %r8
  507. #endif
  508. movq %gs:SGX_EXIT_TARGET, %rbx
  509. .cfi_endproc
  510. # fallthrough
  511. # Clear other registers and similar state and then call EEXIT
  512. #
  513. # Arguments for EEXIT/untrusted code (not cleared):
  514. #
  515. # %rbx: exit target
  516. # %rsp: untrusted stack
  517. # %rdi, %rsi: (optional) arguments to untrusted code.
  518. .Lclear_and_eexit:
  519. #ifdef DEBUG
  520. # Enclave and untrusted stacks are split (segmented). GDB refuses to
  521. # unwind such stacks because it looks like stack frames "jump" back
  522. # and forth. Luckily, GDB special-cases stack frames for a function
  523. # with hardcoded name "__morestack". Declare this dummy function
  524. # to make GDB happy.
  525. .global __morestack
  526. .type __morestack, @function
  527. __morestack:
  528. #endif
  529. .cfi_startproc
  530. # Clear "extended" state (FPU aka x87, SSE, AVX, ...).
  531. # pal_sec.enclave_attributes.xfrm will always be zero before
  532. # init_enclave has been called by pal_linux_main. So during early init
  533. # nothing should use features not covered by fxrstor, like AVX.
  534. movq %rdi, %r10
  535. leaq xsave_reset_state(%rip), %rdi
  536. leaq 1f(%rip), %r11
  537. jmp __restore_xregs
  538. 1:
  539. movq %r10, %rdi
  540. 2:
  541. # %rax is argument to EEXIT
  542. # %rbx is argument to EEXIT
  543. # %rcx is set to AEP by EEXIT
  544. xorq %rdx, %rdx
  545. # %rsi, %rdi are arguments to the untrusted code
  546. #ifdef DEBUG
  547. .Lfor_cfa_debug_info:
  548. # Leave %rbp pointing to OCALL function on trusted stack.
  549. #else
  550. # In non-debug mode, clear %rbp to not leak trusted stack address.
  551. xorq %rbp, %rbp
  552. #endif
  553. # %rsp points to untrusted stack
  554. xorq %r8, %r8
  555. xorq %r9, %r9
  556. xorq %r10, %r10
  557. xorq %r11, %r11
  558. xorq %r12, %r12
  559. xorq %r13, %r13
  560. xorq %r14, %r14
  561. subq %r15, %r15 # use sub to set flags to a fixed value
  562. movq $EEXIT, %rax
  563. ENCLU
  564. .Locall_about_to_eexit_end:
  565. ud2 # We should never get here.
  566. .cfi_endproc
  567. .Lreturn_from_ocall:
  568. # PAL convention:
  569. # RDI - return value
  570. # RSI - external event (if there is any)
  571. # restore the stack
  572. movq %gs:SGX_PRE_OCALL_STACK, %rsp
  573. movq $0, %gs:SGX_PRE_OCALL_STACK
  574. .Lreturn_from_ocall_after_stack_restore:
  575. # sgx_cpu_context_t::rax = %rdi
  576. movq %rdi, SGX_CPU_CONTEXT_RAX(%rsp) # return value
  577. # restore FSBASE if necessary
  578. movq %gs:SGX_FSBASE, %rbx
  579. cmpq $0, %rbx
  580. je .Lno_fsbase
  581. .byte 0xf3, 0x48, 0x0f, 0xae, 0xd3 /* WRFSBASE %RBX */
  582. .Lno_fsbase:
  583. # Check if there was a signal
  584. cmpq $0, %rsi
  585. jne .Lexternal_event
  586. movq %rsp, %rdi # %rdi = sgx_cpu_context_t* uc
  587. movq %rsp, %rsi
  588. addq $SGX_CPU_CONTEXT_SIZE, %rsi # %rsi = PAL_XREGS_STATE* xregs_state
  589. # _restore_sgx_context restores rflags and fp registers. So we don't have to
  590. # sanitize them like below.
  591. jmp _restore_sgx_context
  592. # NOTREACHED
  593. .Lexternal_event:
  594. # clear the Alignment Check flag (%rFLAGS.AC) to prevent #AC-fault side channel;
  595. pushfq
  596. andq $(~RFLAGS_AC), (%rsp)
  597. popfq
  598. leaq xsave_reset_state(%rip), %rdi
  599. callq restore_xregs
  600. movq %rsi, %rdi # 1st argument = PAL_NUM event
  601. movq %rsp, %rsi # 2nd argument = sgx_cpu_context_t* uc
  602. leaq SGX_CPU_CONTEXT_SIZE(%rsp), %rdx # 3rd argument = PAL_XREGS_STATE* xregs_state
  603. callq _DkHandleExternalEvent
  604. # NOTREACHED
  605. # noreturn void _restore_sgx_context(sgx_cpu_context_t* uc, PAL_XREGS_STATE* xsave_area);
  606. # Restore an sgx_cpu_context_t as generated by .Lhandle_exception. Execution will
  607. # continue as specified by the rip in the context.
  608. # If RDI (uc) points into the signal stack we need to ensure that
  609. # until the last read from there RSP points there or
  610. # .Lsetup_exception_handler might mess with it because it would think
  611. # that the signal stack is not in use. In this case we assume that RSP
  612. # points into the signal stack when we get called.
  613. # (Also keep the redzone in mind, see asserts for sgx_cpu_context_t in sgx_arch.h)
  614. .global _restore_sgx_context
  615. .type _restore_sgx_context, @function
  616. _restore_sgx_context:
  617. .cfi_startproc
  618. xchgq %rdi, %rsi
  619. callq restore_xregs
  620. movq %rsi, %r15
  621. movq SGX_CPU_CONTEXT_RAX(%r15), %rax
  622. movq SGX_CPU_CONTEXT_RCX(%r15), %rcx
  623. movq SGX_CPU_CONTEXT_RDX(%r15), %rdx
  624. movq SGX_CPU_CONTEXT_RBX(%r15), %rbx
  625. # For %rsp see below.
  626. movq SGX_CPU_CONTEXT_RBP(%r15), %rbp
  627. movq SGX_CPU_CONTEXT_RSI(%r15), %rsi
  628. movq SGX_CPU_CONTEXT_RDI(%r15), %rdi
  629. movq SGX_CPU_CONTEXT_R8(%r15), %r8
  630. movq SGX_CPU_CONTEXT_R9(%r15), %r9
  631. movq SGX_CPU_CONTEXT_R10(%r15), %r10
  632. movq SGX_CPU_CONTEXT_R11(%r15), %r11
  633. movq SGX_CPU_CONTEXT_R12(%r15), %r12
  634. movq SGX_CPU_CONTEXT_R13(%r15), %r13
  635. movq SGX_CPU_CONTEXT_R14(%r15), %r14
  636. # R15 will be restored below
  637. leaq SGX_CPU_CONTEXT_RFLAGS(%r15), %rsp
  638. popfq
  639. # See the comment at .Lsetup_exception_handler.
  640. #
  641. # The use of SGX_TMP_RIP (enclave_tls::tmp_rip per-enclave-thread field) must be atomic.
  642. # Consider a data race:
  643. # (1) thread handles a previous exception in SSA=0,
  644. # (2) thread is done and returns from exception handler via restore_sgx_context(),
  645. # (3) in the middle of _restore_sgx_context() a new exception arrives,
  646. # (4) the exception handler for this new exception is prepared in SSA=1,
  647. # (5) thread returns back to SSA=0 and handles this new exception,
  648. # (6) thread is done and returns from exception handler via _restore_sgx_context()
  649. # and updates SGX_TMP_RIP (overwrites enclave_tls::tmp_rip). Now the thread returned in
  650. # the middle of _restore_sgx_context() and will try to jmp *%gs:SGX_TMP_RIP but this value
  651. # is lost, and SIGILL/SEGFAULT follows.
  652. #
  653. # The last 4 instructions that restore RIP, RSP and R15 (needed
  654. # as tmp reg) need to be atomic from the point of view of
  655. # .Lsetup_exception_handler.
  656. #
  657. # The reason is that .Lsetup_exception_handler can interrupt us in the
  658. # middle and the nested exception handler that it injects would mess
  659. # with %gs:SGX_TMP_RIP when it calls us to return (%gs:SGX_TMP_RIP is a
  660. # single memory location per thread, so not re-entry save).
  661. #
  662. # Since they are not atomic, .Lsetup_exception_handler will emulate this
  663. # behavior if it gets called while executing them (see there).
  664. # RSP currently points to RIP so need relative addressing to restore RIP, R15, and RSP
  665. movq SGX_CPU_CONTEXT_RIP - SGX_CPU_CONTEXT_RIP(%rsp), %r15
  666. movq %r15, %gs:SGX_TMP_RIP
  667. .Ltmp_rip_saved0:
  668. movq SGX_CPU_CONTEXT_R15 - SGX_CPU_CONTEXT_RIP(%rsp), %r15
  669. .Ltmp_rip_saved1:
  670. movq SGX_CPU_CONTEXT_RSP - SGX_CPU_CONTEXT_RIP(%rsp), %rsp
  671. .Ltmp_rip_saved2:
  672. jmp *%gs:SGX_TMP_RIP
  673. .cfi_endproc
  674. # void __save_xregs(PAL_XREGS_STATE* xsave_area)
  675. # RDI: argument: pointer to xsave_area
  676. # R11: return address: in order to not touch stack
  677. # In some situations, stack isn't available.
  678. # RAX, RDX: clobbered
  679. .global __save_xregs
  680. .type __save_xregs, @function
  681. __save_xregs:
  682. .cfi_startproc
  683. movl xsave_enabled(%rip), %eax
  684. cmpl $0, %eax
  685. jz 1f
  686. # clear xsave header
  687. movq $0, XSAVE_HEADER_OFFSET + 0 * 8(%rdi)
  688. movq $0, XSAVE_HEADER_OFFSET + 1 * 8(%rdi)
  689. movq $0, XSAVE_HEADER_OFFSET + 2 * 8(%rdi)
  690. movq $0, XSAVE_HEADER_OFFSET + 3 * 8(%rdi)
  691. movq $0, XSAVE_HEADER_OFFSET + 4 * 8(%rdi)
  692. movq $0, XSAVE_HEADER_OFFSET + 5 * 8(%rdi)
  693. movq $0, XSAVE_HEADER_OFFSET + 6 * 8(%rdi)
  694. movq $0, XSAVE_HEADER_OFFSET + 7 * 8(%rdi)
  695. movl $0xffffffff, %eax
  696. movl $0xffffffff, %edx
  697. xsave64 (%rdi)
  698. jmp *%r11
  699. 1:
  700. fxsave64 (%rdi)
  701. jmp *%r11
  702. .cfi_endproc
  703. # void save_xregs(PAL_XREGS_STATE* xsave_area)
  704. .global save_xregs
  705. .type save_xregs, @function
  706. save_xregs:
  707. .cfi_startproc
  708. popq %r11
  709. jmp __save_xregs
  710. .cfi_endproc
  711. # void restore_xregs(const PAL_XREGS_STATE* xsave_area)
  712. # RDI: argument: pointer to xsave_area
  713. # R11: return address: in order to not touch stack
  714. # In some situations, stack isn't available.
  715. # RAX, RDX: clobbered
  716. .global __restore_xregs
  717. .type __restore_xregs, @function
  718. __restore_xregs:
  719. .cfi_startproc
  720. movl xsave_enabled(%rip), %eax
  721. cmpl $0, %eax
  722. jz 1f
  723. movl $0xffffffff, %eax
  724. movl $0xffffffff, %edx
  725. xrstor64 (%rdi)
  726. jmp *%r11
  727. 1:
  728. fxrstor64 (%rdi)
  729. jmp *%r11
  730. .cfi_endproc
  731. # void restore_xregs(const PAL_XREGS_STATE* xsave_area)
  732. .global restore_xregs
  733. .type restore_xregs, @function
  734. restore_xregs:
  735. .cfi_startproc
  736. popq %r11
  737. jmp __restore_xregs
  738. .cfi_endproc