Browse Source

[LibOS] Emulate `syscall` instruction

If `syscall` instruction is trapped by SIGSYS or SIGILL, emulate it by
calling `syscalldb` function.
Isaku Yamahata 5 years ago
parent
commit
6abaa112fc

+ 1 - 0
LibOS/shim/include/shim_internal.h

@@ -178,6 +178,7 @@ static inline void do_pause (void);
 /* definition for syscall table */
 void handle_signal (bool delayed_only);
 long convert_pal_errno (long err);
+void syscall_wrapper(void);
 
 #define PAL_ERRNO  convert_pal_errno(PAL_NATIVE_ERRNO)
 

+ 2 - 0
LibOS/shim/include/shim_types.h

@@ -323,6 +323,8 @@ typedef struct ucontext {
     struct _libc_fpstate __fpregs_mem;
 } ucontext_t;
 
+#define RED_ZONE_SIZE   128
+
 /* bits/ustat.h */
 struct __kernel_ustat
   {

+ 49 - 2
LibOS/shim/src/bookkeep/shim_signal.c

@@ -396,6 +396,15 @@ ret_fault:
     return has_fault;
 }
 
+void __attribute__((weak)) syscall_wrapper(void)
+{
+    /*
+     * work around for link.
+     * syscalldb.S is excluded for libsysdb_debug.so so it fails to link
+     * due to missing syscall_wrapper.
+     */
+}
+
 static void illegal_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
 {
     struct shim_vma_val vma;
@@ -407,8 +416,46 @@ static void illegal_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
         if (context)
             debug("illegal instruction at 0x%08lx\n", context->IP);
 
-        deliver_signal(ALLOC_SIGINFO(SIGILL, ILL_ILLOPC,
-                                     si_addr, (void *) arg), context);
+        uint8_t * rip = (uint8_t*)context->IP;
+        /*
+         * Emulate syscall instruction (opcode 0x0f 0x05);
+         * syscall instruction is prohibited in
+         *   Linux-SGX PAL and raises a SIGILL exception and
+         *   Linux PAL with seccomp and raise SIGSYS exception.
+         */
+#if 0
+        if (rip[-2] == 0x0f && rip[-1] == 0x05) {
+            /* TODO: once finished, remove "#if 0" above. */
+            /*
+             * SIGSYS case (can happen with Linux PAL with seccomp)
+             * rip points to the address after syscall instruction
+             * %rcx: syscall instruction must put an
+             *       instruction-after-syscall in rcx
+             */
+            context->rax = siginfo->si_syscall; /* PAL_CONTEXT doesn't
+                                                 * include a member
+                                                 * corresponding to
+                                                 * siginfo_t::si_syscall yet.
+                                                 */
+            context->rcx = (long)rip;
+            context->r11 = context->efl;
+            context->rip = (long)&syscall_wrapper;
+        } else
+#endif
+        if (rip[0] == 0x0f && rip[1] == 0x05) {
+            /*
+             * SIGILL case (can happen in Linux-SGX PAL)
+             * %rcx: syscall instruction must put an instruction-after-syscall
+             *       in rcx. See the syscall_wrapper in syscallas.S
+             * TODO: check SIGILL and ILL_ILLOPN
+             */
+            context->rcx = (long)rip + 2;
+            context->r11 = context->efl;
+            context->rip = (long)&syscall_wrapper;
+        } else {
+            deliver_signal(ALLOC_SIGINFO(SIGILL, ILL_ILLOPC,
+                                         si_addr, (void *) arg), context);
+        }
     } else {
         internal_fault("Internal illegal fault", arg, context);
     }

+ 3 - 0
LibOS/shim/src/generated-offsets.c

@@ -13,5 +13,8 @@ void dummy(void)
     OFFSET_T(TCB_RET_IP, shim_tcb_t, context.ret_ip);
     OFFSET_T(TCB_REGS, shim_tcb_t, context.regs);
     DEFINE(SHIM_REGS_SIZE, sizeof(struct shim_regs));
+
+    /* definitions */
+    DEFINE(RED_ZONE_SIZE, RED_ZONE_SIZE);
 }
 

+ 32 - 0
LibOS/shim/src/syscallas.S

@@ -28,6 +28,8 @@
         .global syscalldb
         .type syscalldb, @function
         .extern shim_table, debug_unsupp
+        .global syscall_wrapper
+        .type syscall_wrapper, @function
 
 
 syscalldb:
@@ -35,6 +37,7 @@ syscalldb:
 
         # Create shim_regs struct on the stack.
         pushfq
+        cld
         pushq %rbp
         pushq %rbx
         pushq %rdi
@@ -112,3 +115,32 @@ isundef:
 
         .cfi_endproc
         .size syscalldb, .-syscalldb
+
+        /*
+         * syscall_wrapper: emulate syscall instruction
+         *   prohibited in e.g. Linux-SGX PAL which raises a SIGILL exception
+         *
+         * input:
+         * %rcx: Instruction address to continue app execution after trapped
+         *       syscall instruction
+         * %r11: rflags on entering syscall
+         *
+         * FIXME: preserve rflags.
+         *        remember that clone-child can't use parent stack.
+         */
+syscall_wrapper:
+        .cfi_startproc
+
+        subq $RED_ZONE_SIZE, %rsp
+        callq *syscalldb@GOTPCREL(%rip)
+        addq $RED_ZONE_SIZE, %rsp
+#if 0
+        # TODO: once clone emulation is fixed, remove this #if 0
+        xchg %r11, (%rsp)
+        popfq
+        pushq %r11
+#endif
+        jmp *%rcx
+
+        .cfi_endproc
+        .size syscall_wrapper, .-syscall_wrapper