Browse Source

[Pal] Correctly save/restore XSAVE area on signal handling

Previously, XSAVE area (floating-point regs, MPX regs, etc.) was not
handled at all during signal handling. This led to application signal
handlers not being able to e.g. examine FP registers or modify them.

This commit adds correct XSAVE area handling:
- Introduce generic PAL struct PAL_XREGS_STATE to hold XSAVE area.
- Copy FP registers to and from signal-handler context in Linux PAL.
- Prepare Linux-SGX PAL for save/restore of XSAVE area via helpers
  init_xsave_size(), save_xregs(), and restore_xregs().

Note that this commit adds complete XSAVE area handling to Linux PAL,
and only prepares Linux-SGX PAL via helper functions. Next commit
will add complete XSAVE area handling to Linux-SGX PAL.
Isaku Yamahata 6 years ago
parent
commit
98acf101d9

+ 1 - 1
Pal/src/host/Linux-SGX/Makefile

@@ -15,7 +15,7 @@ ASFLAGS += $(defs)
 enclave-objs = $(addprefix db_,files devices pipes eventfd sockets streams memory \
 		 threading mutex events process object main rtld \
 		 exception misc) \
-	       $(addprefix enclave_,ocalls ecalls framework platform pages untrusted)
+	       $(addprefix enclave_,ocalls ecalls framework platform pages untrusted xstate)
 enclave-asm-objs = enclave_entry
 urts-objs = $(addprefix sgx_,enclave framework platform main rtld thread process exception graphene) \
 	    quote/aesm.pb-c clone-x86_64

+ 3 - 0
Pal/src/host/Linux-SGX/enclave_ecalls.c

@@ -70,6 +70,9 @@ void handle_ecall (long ecall_index, void * ecall_args, void * exit_target,
             return;
         }
 
+        /* xsave size must be initialized early */
+        init_xsave_size(ms->ms_sec_info->enclave_attributes.xfrm);
+
         /* pal_linux_main is responsible to check the passed arguments */
         pal_linux_main(ms->ms_args, ms->ms_args_size,
                        ms->ms_env, ms->ms_env_size,

+ 72 - 0
Pal/src/host/Linux-SGX/enclave_entry.S

@@ -784,3 +784,75 @@ _restore_sgx_context:
 .Ltmp_rip_saved2:
 	jmp *%gs:SGX_TMP_RIP
 	.cfi_endproc
+
+	# void __save_xregs(PAL_XREGS_STATE* xsave_area)
+	#   RDI: argument: pointer to xsave_area
+	#   R11: return address: in order to not touch stack
+	#                        In some situations, stack isn't available.
+	#   RAX, RDX: clobbered
+	.global __save_xregs
+	.type __save_xregs, @function
+__save_xregs:
+	.cfi_startproc
+	movl xsave_enabled(%rip), %eax
+	cmpl $0, %eax
+	jz 1f
+
+	# clear xsave header
+	movq $0, XSAVE_HEADER_OFFSET + 0 * 8(%rdi)
+	movq $0, XSAVE_HEADER_OFFSET + 1 * 8(%rdi)
+	movq $0, XSAVE_HEADER_OFFSET + 2 * 8(%rdi)
+	movq $0, XSAVE_HEADER_OFFSET + 3 * 8(%rdi)
+	movq $0, XSAVE_HEADER_OFFSET + 4 * 8(%rdi)
+	movq $0, XSAVE_HEADER_OFFSET + 5 * 8(%rdi)
+	movq $0, XSAVE_HEADER_OFFSET + 6 * 8(%rdi)
+	movq $0, XSAVE_HEADER_OFFSET + 7 * 8(%rdi)
+
+	movl $0xffffffff, %eax
+	movl $0xffffffff, %edx
+	xsave64 (%rdi)
+	jmp *%r11
+1:
+	fxsave64 (%rdi)
+	jmp *%r11
+	.cfi_endproc
+
+	# void save_xregs(PAL_XREGS_STATE* xsave_area)
+	.global save_xregs
+	.type save_xregs, @function
+save_xregs:
+	.cfi_startproc
+	popq %r11
+	jmp __save_xregs
+	.cfi_endproc
+
+	# void restore_xregs(const PAL_XREGS_STATE* xsave_area)
+	#   RDI: argument: pointer to xsave_area
+	#   R11: return address: in order to not touch stack
+	#                        In some situations, stack isn't available.
+	#   RAX, RDX: clobbered
+	.global __restore_xregs
+	.type __restore_xregs, @function
+__restore_xregs:
+	.cfi_startproc
+	movl xsave_enabled(%rip), %eax
+	cmpl $0, %eax
+	jz 1f
+
+	movl $0xffffffff, %eax
+	movl $0xffffffff, %edx
+	xrstor64 (%rdi)
+	jmp *%r11
+1:
+	fxrstor64 (%rdi)
+	jmp *%r11
+	.cfi_endproc
+
+	# void restore_xregs(const PAL_XREGS_STATE* xsave_area)
+	.global restore_xregs
+	.type restore_xregs, @function
+restore_xregs:
+	.cfi_startproc
+	popq %r11
+	jmp __restore_xregs
+	.cfi_endproc

+ 97 - 0
Pal/src/host/Linux-SGX/enclave_xstate.c

@@ -0,0 +1,97 @@
+/*
+ * this function is shamelessly stolen from linux-sgx
+ * https://github.com/intel/linux-sgx/blob/9ddec08fb98c1636ed3b1a77bbc4fa3520344ede/sdk/trts/trts_xsave.cpp
+ * It has BSD lisence.
+ */
+
+/*
+ * Copyright (C) 2011-2019 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Intel Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <pal_linux.h>
+#include <pal_linux_error.h>
+#include <pal_internal.h>
+#include <pal_debug.h>
+#include <pal_error.h>
+#include <pal_security.h>
+#include <pal_crypto.h>
+#include <api.h>
+#include <list.h>
+#include <stdbool.h>
+
+#include "enclave_pages.h"
+
+int xsave_enabled = 0;
+uint64_t xsave_features = 0;
+uint32_t xsave_size = 0;
+//FXRSTOR only cares about the first 512 bytes, while
+//XRSTOR in compacted mode will ignore the first 512 bytes.
+const uint32_t xsave_reset_state[XSAVE_RESET_STATE_SIZE/sizeof(uint32_t)]
+__attribute__((aligned(PAL_XSTATE_ALIGN))) = {
+    0x037F, 0, 0, 0, 0, 0, 0x1F80, 0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0x80000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // XCOMP_BV[63] = 1, compaction mode
+};
+
+void init_xsave_size(uint64_t xfrm) {
+    const struct {
+        uint64_t bits;
+        uint32_t size;
+    } xsave_size_table[] = { // Note that the xsave_size should be in ascending order
+        {SGX_XFRM_LEGACY, 512 + 64},                    // 512 for legacy features, 64 for xsave header
+        {SGX_XFRM_AVX,    512 + 64 + 256},              // 256 for YMM0_H - YMM15_H registers
+        {SGX_XFRM_MPX,    512 + 64 + 256 + 256},        // 256 for MPX
+        {SGX_XFRM_AVX512, 512 + 64 + 256 + 256 + 1600}, // 1600 for k0 - k7, ZMM0_H - ZMM15_H, ZMM16 - ZMM31
+    };
+
+    /* fxsave/fxrstore as fallback */
+    xsave_enabled = 0;
+    xsave_features = PAL_XFEATURE_MASK_FPSSE;
+    xsave_size = 512 + 64;
+    if (!xfrm || (xfrm & SGX_XFRM_RESERVED)) {
+        SGX_DBG(DBG_I, "xsave is disabled, xfrm 0x%lx\n", xfrm);
+        return;
+    }
+
+    xsave_enabled = (xfrm == SGX_XFRM_LEGACY) ? 0 : 1;
+    for (size_t i = 0; i < ARRAY_SIZE(xsave_size_table); i++) {
+        if ((xfrm & xsave_size_table[i].bits) == xsave_size_table[i].bits) {
+            xsave_features = xfrm;
+            xsave_size = xsave_size_table[i].size;
+        }
+    }
+    SGX_DBG(DBG_I, "xsave is enabled with xsave_size: %u\n", xsave_size);
+}

+ 10 - 5
Pal/src/host/Linux-SGX/generated-offsets.c

@@ -1,13 +1,14 @@
 #include <stddef.h>
 #include <asm/errno.h>
 
-#include "sgx_arch.h"
-#include "sgx_tls.h"
+#include "ecall_types.h"
+#include "ocall_types.h"
+#include "pal.h"
 #include "pal_linux.h"
 #include "pal_linux_defs.h"
 #include "pal_security.h"
-#include "ecall_types.h"
-#include "ocall_types.h"
+#include "sgx_arch.h"
+#include "sgx_tls.h"
 
 #include <generated-offsets-build.h>
 
@@ -158,5 +159,9 @@ void dummy(void)
 
     /* Ocall Index */
     DEFINE(OCALL_EXIT, OCALL_EXIT);
-}
 
+    /* fp regs */
+    OFFSET_T(XSAVE_HEADER_OFFSET, PAL_XREGS_STATE, header);
+    DEFINE(PAL_XSTATE_ALIGN, PAL_XSTATE_ALIGN);
+    DEFINE(PAL_FP_XSTATE_MAGIC2_SIZE, PAL_FP_XSTATE_MAGIC2_SIZE);
+}

+ 9 - 0
Pal/src/host/Linux-SGX/pal_linux.h

@@ -113,6 +113,15 @@ extern char __text_start, __text_end, __data_start, __data_end;
 typedef struct { char bytes[32]; } sgx_checksum_t;
 typedef struct { char bytes[16]; } sgx_stub_t;
 
+extern int xsave_enabled;
+extern uint64_t xsave_features;
+extern uint32_t xsave_size;
+#define XSAVE_RESET_STATE_SIZE (512 + 64)  // 512 for legacy regs, 64 for xsave header
+extern const uint32_t xsave_reset_state[];
+
+void init_xsave_size(uint64_t xfrm);
+void save_xregs(PAL_XREGS_STATE* xsave_area);
+void restore_xregs(const PAL_XREGS_STATE* xsave_area);
 noreturn void _restore_sgx_context(sgx_cpu_context_t* uc);
 
 int init_trusted_files (void);

+ 1 - 0
Pal/src/host/Linux-SGX/sgx_arch.h

@@ -78,6 +78,7 @@ typedef uint8_t sgx_isvfamily_id_t[SGX_ISV_FAMILY_ID_SIZE];
 #define SGX_XFRM_AVX    0x06ULL
 #define SGX_XFRM_MPX    0x18ULL
 #define SGX_XFRM_AVX512 0xe6ULL
+#define SGX_XFRM_RESERVED (~(SGX_XFRM_LEGACY | SGX_XFRM_AVX | SGX_XFRM_MPX | SGX_XFRM_AVX512))
 
 #define SGX_MISCSELECT_EXINFO 0x01UL
 

+ 4 - 1
Pal/src/host/Linux/db_exception.c

@@ -189,8 +189,10 @@ static void _DkGenericEventTrigger (PAL_IDX event_num, PAL_EVENT_HANDLER upcall,
     PAL_EVENT event;
     event.event_num = event_num;
 
-    if (uc)
+    if (uc) {
         memcpy(&event.context, uc->uc_mcontext.gregs, sizeof(PAL_CONTEXT));
+        event.context.fpregs = (PAL_XREGS_STATE*)uc->uc_mcontext.fpregs;
+    }
 
     event.uc = uc;
 
@@ -417,5 +419,6 @@ void _DkExceptionReturn (void * event)
     if (e->uc) {
         /* copy the context back to ucontext */
         memcpy(e->uc->uc_mcontext.gregs, &e->context, sizeof(PAL_CONTEXT));
+        e->uc->uc_mcontext.fpregs = (struct _libc_fpstate*)e->context.fpregs;
     }
 }

+ 126 - 0
Pal/src/pal.h

@@ -95,12 +95,138 @@ static inline PAL_TCB * pal_get_tcb (void)
     return tcb;
 }
 
+#ifdef __x86_64__
+union pal_csgsfs {
+    struct {
+        uint16_t cs;
+        uint16_t gs;
+        uint16_t fs;
+        uint16_t ss;
+    };
+    uint64_t csgsfs;
+};
+
+/* adopt Linux style fp layout, _libc_fpstate of glibc:
+ * Because self-contained definition is needed for Pal definition,
+ * same layout is defined with PAL prefix.
+ */
+#define PAL_FP_XSTATE_MAGIC1        0x46505853U
+#define PAL_FP_XSTATE_MAGIC2        0x46505845U
+#define PAL_FP_XSTATE_MAGIC2_SIZE   (sizeof(PAL_FP_XSTATE_MAGIC2))
+
+enum PAL_XFEATURE {
+    PAL_XFEATURE_FP,
+    PAL_XFEATURE_SSE,
+    PAL_XFEATURE_YMM,
+    PAL_XFEATURE_BNDREGS,
+    PAL_XFEATURE_BNDCSR,
+    PAL_XFEATURE_OPMASK,
+    PAL_XFEATURE_ZMM_Hi256,
+    PAL_XFEATURE_Hi16_ZMM,
+    PAL_XFEATURE_PT,
+    PAL_XFEATURE_PKRU,
+
+    PAL_XFEATURE_MAX,
+};
+
+#define PAL_XFEATURE_MASK_FP                (1UL << PAL_XFEATURE_FP)
+#define PAL_XFEATURE_MASK_SSE               (1UL << PAL_XFEATURE_SSE)
+#define PAL_XFEATURE_MASK_YMM               (1UL << PAL_XFEATURE_YMM)
+#define PAL_XFEATURE_MASK_BNDREGS           (1UL << PAL_XFEATURE_BNDREGS)
+#define PAL_XFEATURE_MASK_BNDCSR            (1UL << PAL_XFEATURE_BNDCSR)
+#define PAL_XFEATURE_MASK_OPMASK            (1UL << PAL_XFEATURE_OPMASK)
+#define PAL_XFEATURE_MASK_ZMM_Hi256         (1UL << PAL_XFEATURE_ZMM_Hi256)
+#define PAL_XFEATURE_MASK_Hi16_ZMM          (1UL << PAL_XFEATURE_Hi16_ZMM)
+#define PAL_XFEATURE_MASK_PT                (1UL << PAL_XFEATURE_PT)
+#define PAL_XFEATURE_MASK_PKRU              (1UL << PAL_XFEATURE_PKRU)
+
+#define PAL_XFEATURE_MASK_FPSSE             (PAL_XFEATURE_MASK_FP \
+                                             | PAL_XFEATURE_MASK_SSE)
+#define PAL_XFEATURE_MASK_AVX512            (PAL_XFEATURE_MASK_OPMASK \
+                                             | PAL_XFEATURE_MASK_ZMM_Hi256 \
+                                             | PAL_XFEATURE_MASK_Hi16_ZMM)
+
+typedef struct {
+    uint32_t magic1;        /* PAL_FP_XSTATE_MAGIC1 */
+    uint32_t extended_size; /* xsave_size */
+    uint64_t xfeatures;     /* XSAVE feature */
+    uint32_t xstate_size;   /* xsave_size + PAL_FP_STATE_MAGIC2_SIZE */
+    uint32_t padding[7];
+} PAL_FPX_SW_BYTES;
+
+typedef struct {
+    uint32_t cwd;
+    uint32_t swd;
+    uint32_t twd;
+    uint32_t fip;
+    uint32_t fcs;
+    uint32_t foo;
+    uint32_t fos;
+    uint32_t st_space[20];
+    uint8_t ftop;
+    uint8_t changed;
+    uint8_t lookahead;
+    uint8_t no_update;
+    uint8_t rm;
+    uint8_t alimit;
+    void* info; /* struct math_emu_info */
+    uint32_t entry_eip;
+} PAL_SWREGS_STATE;
+
+typedef struct {
+    uint16_t significand[4];
+    uint16_t exponent;
+    uint16_t padding[3];
+} PAL_FPXREG;
+
+typedef struct {
+    uint32_t element[4];
+} PAL_XMMREG;
+
+typedef struct {
+    /* 64-bit FXSAVE format.  */
+    uint16_t cwd;
+    uint16_t swd;
+    uint16_t ftw;
+    uint16_t fop;
+    uint64_t rip;
+    uint64_t rdp;
+    uint32_t mxcsr;
+    uint32_t mxcr_mask;
+    PAL_FPXREG st[8];
+    PAL_XMMREG xmm[16];
+    union {
+        uint32_t padding[24];
+        struct {
+            uint32_t padding2[12];
+            PAL_FPX_SW_BYTES sw_reserved;
+        };
+    };
+} PAL_FPSTATE;
+
+typedef struct {
+    uint64_t xfeatures;
+    uint64_t xcomp_bv;
+    uint64_t reserved[6];
+} __attribute__((packed)) PAL_XSTATE_HEADER;
+
+#define PAL_XSTATE_ALIGN 64
+
+typedef struct {
+    PAL_FPSTATE fpstate;
+    PAL_XSTATE_HEADER header;
+} __attribute__((packed, aligned(PAL_XSTATE_ALIGN))) PAL_XREGS_STATE;
+#else
+# error "Unsupported architecture"
+#endif
+
 typedef struct {
 #ifdef __x86_64__
     PAL_NUM r8, r9, r10, r11, r12, r13, r14, r15;
     PAL_NUM rdi, rsi, rbp, rbx, rdx, rax, rcx;
     PAL_NUM rsp, rip;
     PAL_NUM efl, csgsfs, err, trapno, oldmask, cr2;
+    PAL_XREGS_STATE* fpregs;
 #else
 # error "Unsupported architecture"
 #endif