From patchwork Sat Jul 13 06:51:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Xing, Cedric" X-Patchwork-Id: 11042911 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EFA8F138B for ; Sat, 13 Jul 2019 06:51:40 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DF9D4284F5 for ; Sat, 13 Jul 2019 06:51:40 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D3FB028541; Sat, 13 Jul 2019 06:51:40 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 706EA284F5 for ; Sat, 13 Jul 2019 06:51:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727503AbfGMGv3 (ORCPT ); Sat, 13 Jul 2019 02:51:29 -0400 Received: from mga11.intel.com ([192.55.52.93]:55267 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726334AbfGMGv3 (ORCPT ); Sat, 13 Jul 2019 02:51:29 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga004.jf.intel.com ([10.7.209.38]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 12 Jul 2019 23:51:28 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.63,485,1557212400"; d="scan'208";a="318200502" Received: from bxing-ubuntu.jf.intel.com ([10.23.30.27]) by orsmga004.jf.intel.com with ESMTP; 12 Jul 2019 23:51:27 -0700 From: Cedric Xing To: linux-kernel@vger.kernel.org, linux-sgx@vger.kernel.org, jarkko.sakkinen@linux.intel.com Cc: cedric.xing@intel.com, akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, kai.svahn@intel.com, kai.huang@intel.com Subject: [RFC PATCH v4 1/3] selftests/x86/sgx: Fix Makefile for SGX selftest Date: Fri, 12 Jul 2019 23:51:25 -0700 Message-Id: <757d44a0e67bfa09d97eea918bc0d20383b5e80e.1563000446.git.cedric.xing@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The original x86/sgx/Makefile didn't work when "x86/sgx" was specified as the test target, nor did it work with "run_tests" as the make target. Yet another problem was that it breaks 32-bit only build. This patch fixes those problems, along with adjustments to compiler/linker options and simplifications to the build rules. Signed-off-by: Cedric Xing --- tools/testing/selftests/x86/sgx/Makefile | 45 +++++++++--------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile index 1fd6f2708e81..3af15d7c8644 100644 --- a/tools/testing/selftests/x86/sgx/Makefile +++ b/tools/testing/selftests/x86/sgx/Makefile @@ -2,47 +2,34 @@ top_srcdir = ../../../../.. include ../../lib.mk -HOST_CFLAGS := -Wall -Werror -g $(INCLUDES) -fPIC -ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIC \ +ifeq ($(shell $(CC) -dumpmachine | cut --delimiter=- -f1),x86_64) +all: all_64 +endif + +HOST_CFLAGS := -Wall -Werror -g $(INCLUDES) +ENCL_CFLAGS := -Wall -Werror -static -nostdlib -nostartfiles -fPIE \ -fno-stack-protector -mrdrnd $(INCLUDES) TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx all_64: $(TEST_CUSTOM_PROGS) -$(TEST_CUSTOM_PROGS): $(OUTPUT)/main.o $(OUTPUT)/sgx_call.o \ - $(OUTPUT)/encl_piggy.o +$(TEST_CUSTOM_PROGS): main.c sgx_call.S $(OUTPUT)/encl_piggy.o $(CC) $(HOST_CFLAGS) -o $@ $^ -$(OUTPUT)/main.o: main.c - $(CC) $(HOST_CFLAGS) -c $< -o $@ - -$(OUTPUT)/sgx_call.o: sgx_call.S - $(CC) $(HOST_CFLAGS) -c $< -o $@ - -$(OUTPUT)/encl_piggy.o: $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss - $(CC) $(HOST_CFLAGS) -c encl_piggy.S -o $@ +$(OUTPUT)/encl_piggy.o: encl_piggy.S $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss + $(CC) $(HOST_CFLAGS) -I$(OUTPUT) -c $< -o $@ -$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf $(OUTPUT)/sgxsign +$(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf objcopy --remove-section=.got.plt -O binary $< $@ -$(OUTPUT)/encl.elf: $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o - $(CC) $(ENCL_CFLAGS) -T encl.lds -o $@ $^ +$(OUTPUT)/encl.elf: encl.lds encl.c encl_bootstrap.S + $(CC) $(ENCL_CFLAGS) -T $^ -o $@ -$(OUTPUT)/encl.o: encl.c - $(CC) $(ENCL_CFLAGS) -c $< -o $@ - -$(OUTPUT)/encl_bootstrap.o: encl_bootstrap.S - $(CC) $(ENCL_CFLAGS) -c $< -o $@ - -$(OUTPUT)/encl.ss: $(OUTPUT)/encl.bin $(OUTPUT)/sgxsign - $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss +$(OUTPUT)/encl.ss: $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin + $^ $@ $(OUTPUT)/sgxsign: sgxsign.c $(CC) -o $@ $< -lcrypto -EXTRA_CLEAN := $(OUTPUT)/sgx-selftest $(OUTPUT)/sgx-selftest.o \ - $(OUTPUT)/sgx_call.o $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss \ - $(OUTPUT)/encl.elf $(OUTPUT)/encl.o $(OUTPUT)/encl_bootstrap.o \ - $(OUTPUT)/sgxsign - -.PHONY: clean +EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(addprefix $(OUTPUT)/, \ + encl.elf encl.bin encl.ss encl_piggy.o sgxsign) From patchwork Sat Jul 13 06:51:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Xing, Cedric" X-Patchwork-Id: 11042907 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C0308912 for ; Sat, 13 Jul 2019 06:51:33 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AC937284F5 for ; Sat, 13 Jul 2019 06:51:33 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9F32028541; Sat, 13 Jul 2019 06:51:33 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 91286284F5 for ; Sat, 13 Jul 2019 06:51:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727617AbfGMGva (ORCPT ); Sat, 13 Jul 2019 02:51:30 -0400 Received: from mga11.intel.com ([192.55.52.93]:55267 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726274AbfGMGv3 (ORCPT ); Sat, 13 Jul 2019 02:51:29 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga004.jf.intel.com ([10.7.209.38]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 12 Jul 2019 23:51:28 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.63,485,1557212400"; d="scan'208";a="318200504" Received: from bxing-ubuntu.jf.intel.com ([10.23.30.27]) by orsmga004.jf.intel.com with ESMTP; 12 Jul 2019 23:51:27 -0700 From: Cedric Xing To: linux-kernel@vger.kernel.org, linux-sgx@vger.kernel.org, jarkko.sakkinen@linux.intel.com Cc: cedric.xing@intel.com, akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, kai.svahn@intel.com, kai.huang@intel.com Subject: [RFC PATCH v4 2/3] x86/vdso: Modify __vdso_sgx_enter_enclave() to allow parameter passing on untrusted stack Date: Fri, 12 Jul 2019 23:51:26 -0700 Message-Id: X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The previous __vdso_sgx_enter_enclave() requires enclaves to preserve %rsp, which prohibits enclaves from allocating and passing parameters for untrusted function calls (aka. o-calls) on the untrusted stack. This patch addresses the problem above by introducing a new ABI that preserves %rbp instead of %rsp. Then __vdso_sgx_enter_enclave() can anchor its frame using %rbp so that enclaves are allowed to allocate space on the untrusted stack by decrementing %rsp. Please note that the stack space allocated in such way will be part of __vdso_sgx_enter_enclave()'s frame so will be freed after __vdso_sgx_enter_enclave() returns. Therefore, __vdso_sgx_enter_enclave() has been revised to take a callback function as an optional parameter, which if supplied, will be invoked upon enclave exits (both AEX (Asynchronous Enclave eXit) and normal exits), with the value of %rsp left off by the enclave as a parameter to the callback. Here's the summary of API/ABI changes in this patch. More details could be found in arch/x86/entry/vdso/vsgx_enter_enclave.S. * 'struct sgx_enclave_exception' is renamed to 'struct sgx_enclave_exinfo' because it is filled upon both AEX (i.e. exceptions) and normal enclave exits. * __vdso_sgx_enter_enclave() anchors its frame using %rbp (instead of %rsp in the previous implementation). * __vdso_sgx_enter_enclave() takes one more parameter - a callback function to be invoked upon enclave exits. This callback is optional, and if not supplied, will cause __vdso_sgx_enter_enclave() to return upon enclave exits (same behavior as previous implementation). * The callback function is given as a parameter the value of %rsp at enclave exit to address data "pushed" by the enclave. A positive value returned by the callback will be treated as an ENCLU leaf for re-entering the enclave, while a zero or negative value will be passed through as the return value of __vdso_sgx_enter_enclave() to its caller. It's also safe to leave callback by longjmp() or by throwing a C++ exception. Signed-off-by: Cedric Xing --- arch/x86/entry/vdso/vsgx_enter_enclave.S | 310 +++++++++++++++++------ arch/x86/include/uapi/asm/sgx.h | 14 +- 2 files changed, 242 insertions(+), 82 deletions(-) diff --git a/arch/x86/entry/vdso/vsgx_enter_enclave.S b/arch/x86/entry/vdso/vsgx_enter_enclave.S index fe0bf6671d6d..a96542ba6945 100644 --- a/arch/x86/entry/vdso/vsgx_enter_enclave.S +++ b/arch/x86/entry/vdso/vsgx_enter_enclave.S @@ -6,96 +6,256 @@ #include "extable.h" -#define EX_LEAF 0*8 -#define EX_TRAPNR 0*8+4 -#define EX_ERROR_CODE 0*8+6 -#define EX_ADDRESS 1*8 +#define EX_LEAF 0*8 +#define EX_TRAPNR 0*8+4 +#define EX_ERROR_CODE 0*8+6 +#define EX_ADDRESS 1*8 .code64 .section .text, "ax" #ifdef SGX_KERNEL_DOC /** - * __vdso_sgx_enter_enclave() - Enter an SGX enclave + * typedef sgx_ex_callback - Callback function for __vdso_sgx_enter_enclave() * - * @leaf: **IN \%eax** - ENCLU leaf, must be EENTER or ERESUME - * @tcs: **IN \%rbx** - TCS, must be non-NULL - * @ex_info: **IN \%rcx** - Optional 'struct sgx_enclave_exception' pointer + * @rdi: value of %%rdi register at enclave exit + * @rsi: value of %%rsi register at enclave exit + * @rdx: value of %%rdx register at enclave exit + * @exinfo: pointer to a sgx_enclave_exinfo structure, which was passed to + * __vdso_sgx_enter_enclave() as input + * @r8: value of %%r8 register at enclave exit + * @r9: value of %%r9 register at enclave exit + * @tcs: TCS used by __vdso_sgx_enter_enclave() to enter the enclave, + * could be used to re-enter the + * enclave + * @ursp: value of %%rsp register at enclave exit + * + * This is the callback function to be invoked upon enclave exits, including + * normal exits (as result of EEXIT), and asynchronous exits (AEX) due to + * exceptions occurred at EENTER or within the enclave. + * + * This callback is expected to follow x86_64 ABI. * * Return: - * **OUT \%eax** - - * %0 on a clean entry/exit to/from the enclave, %-EINVAL if ENCLU leaf is - * not allowed or if TCS is NULL, %-EFAULT if ENCLU or the enclave faults - * - * **Important!** __vdso_sgx_enter_enclave() is **NOT** compliant with the - * x86-64 ABI, i.e. cannot be called from standard C code. As noted above, - * input parameters must be passed via ``%eax``, ``%rbx`` and ``%rcx``, with - * the return value passed via ``%eax``. All registers except ``%rsp`` must - * be treated as volatile from the caller's perspective, including but not - * limited to GPRs, EFLAGS.DF, MXCSR, FCW, etc... Conversely, the enclave - * being run **must** preserve the untrusted ``%rsp`` and stack. + * + * EENTER(2) - causes __vdso_sgx_enter_enclave() to issue ENCLU[EENTER] on the + * same TCS. All GPRs left off by this callback function will be passed through + * back to the enclave, except %%rax, %%rbx and %%rcx, which are clobbered by + * ENCLU[EENTER] instruction. + * + * ERESUME(3) - causes __vdso_sgx_enter_enclave() to issue ENCLU[ERESUME] on + * the same TCS. + * + * 0 (zero) or negative returned values will be returned back to + * __vdso_sgx_enter_enclave()'s caller as is. + * + * All other values will cause -EINVAL to be returned to + * __vdso_sgx_enter_enclave()'s caller. + * + * Note: All general purpose registers (GPRs) left off by the enclave are + * passed through to this function, except %%rax, %%rbx and %%rcx, which are + * used internally by __vdso_sgx_enter_enclave(). Some of those registers are + * accessible as function parameters (i.e. @rdi, @rsi, @rdx, @r8, @r9 and + * @ursp), while others can be accessed only from assembly code. */ -__vdso_sgx_enter_enclave(u32 leaf, void *tcs, - struct sgx_enclave_exception *ex_info) -{ - if (leaf != SGX_EENTER && leaf != SGX_ERESUME) - return -EINVAL; +typedef int sgx_ex_callback(long rdi, long rsi, long rdx, + struct sgx_enclave_exinfo *exinfo, + long r8, long r9, void *tcs, long ursp); - if (!tcs) - return -EINVAL; +/** + * __vdso_sgx_enter_enclave() - Enter an SGX enclave and capture exceptions + * + * @leaf: + * passed in %%eax, must be either EENTER(2) or ERESUME(3) + * @tcs: + * passed on stack at 8(%%rsp), is the linear address of TCS + * @exinfo: + * passed on stack at 0x10(%%rsp), optional, and if non-NULL, shall point + * to an sgx_enclave_exinfo structure to receive information about the + * enclave exit + * @callback: + * passed on stack at 0x18(%%rsp), optional, and if non-NULL, points to a + * callback function to be invoked at enclave exits + * + * __vdso_sgx_enter_enclave() issues either ENCLU[EENTER] or ENCLU[ERESUME] on + * @tcs depending on @leaf. + * + * IMPORTANT! This API is not compliant with x86-64 ABI but adopts a + * proprietary calling convention. Please see NOTES section below for details. + * + * On an enclave exit, @exinfo->leaf will be set to the ENCLU leaf at exit, if + * @exinfo is not NULL. That is, @exinfo->leaf may be one of the following: + * + * * EEXIT: Normal exit due to ENCLU[EEXIT] within the enclave. All other + * members will remain intact. + * + * * ERESUME: Asynchronous exit due to exceptions within the enclave. + * @exinfo->trapnr, @exinfo->error_code and @exinfo->address are + * set to the trap number, error code and fault address, + * respectively. + * + * * EENTER: Exception occurred when trying to enter the enclave. + * @exinfo->trapnr, @exinfo->error_code and @exinfo->address are + * set to the trap number, error code and fault address, + * accordingly. + * + * If @callback is NULL, 0 (zero) is returned if the enclave has been entered + * and exited normally, or -EFAULT if any exception has occurred, or -EINVAL if + * @leaf on input is neither EENTER or ERESUME. + * + * If @callback is not NULL, it is invoked at enclave exit, and then actions + * will be taken depending on its return value - i.e. positive value will be + * treated as ENCLU leaf to re-enter the enclave, while 0 (zero) or negative + * values will be returned back to the caller as is. Unrecognized leaf values + * will cause -EINVAL to be returned. + * + * Return: + * + * 0 (zero) is returned on a successful entry and normal exit from the enclave. + * + * -EINVAL is returned if @leaf is neither EENTER nor ERESUME, or if @callback + * is not NULL and returns a positive value that is neither EENTER nor ERESUME + * after the enclave exits. + * + * -EFAULT is returned if an exception has occurred at EENTER or during + * execution of the enclave and @callback is NULL, or if @callback is not NULL + * and it returns -EFAULT after the enclave exits. + * + * Other values may be returned as the return value from @callback if it is not + * NULL. + * + * Note: __vdso_sgx_enter_enclave() adopts a proprietary calling convention, + * described below: + * + * * As noted above, input parameters are passed via %%eax and the stack. + * + * * %%rbx and %%rcx must be treated as volatile as they are modified as part + * of enclaves transitions and are used as scratch regs. + * + * * %%rdx, %%rdi, %%rsi and %%r8-%%r15 are passed as is and may be freely + * modified by the enclave. Values left in those registers will not be + * altered either, so will be visiable to the callback or the caller (if no + * callback is specified). + * + * * %%rsp could be decremented by the enclave to allocate temporary space on + * the untrusted stack. Temporary space allocated this way is retained in + * the context of @callback, and will be freed (i.e. %%rsp will be + * restored) before __vdso_sgx_enter_enclave() returns. + */ +int __vdso_sgx_enter_enclave(int leaf, void *tcs, + struct sgx_enclave_exinfo *exinfo, + sgx_ex_callback *callback); +{ + while (leaf == EENTER || leaf == ERESUME) { + int rc; + try { + ENCLU[leaf]; + rc = 0; + if (exinfo) + exinfo->leaf = EEXIT; + } catch (exception) { + rc = -EFAULT; + if (exinfo) + *exinfo = exception; + } - try { - ENCLU[leaf]; - } catch (exception) { - if (e) - *e = exception; - return -EFAULT; - } + leaf = !callback ? rc: (*callback)(rdi, rsi, rdx, exinfo, + r8, r9, tcs, ursp); + } - return 0; + return leaf > 0 ? -EINVAL : leaf; } #endif + ENTRY(__vdso_sgx_enter_enclave) - /* EENTER <= leaf <= ERESUME */ - cmp $0x2, %eax - jb bad_input - - cmp $0x3, %eax - ja bad_input - - /* TCS must be non-NULL */ - test %rbx, %rbx - je bad_input - - /* Save @exception_info */ - push %rcx - - /* Load AEP for ENCLU */ - lea 1f(%rip), %rcx -1: enclu - - add $0x8, %rsp - xor %eax, %eax - ret - -bad_input: - mov $(-EINVAL), %rax - ret - -.pushsection .fixup, "ax" - /* Re-load @exception_info and fill it (if it's non-NULL) */ -2: pop %rcx - test %rcx, %rcx - je 3f - - mov %eax, EX_LEAF(%rcx) - mov %di, EX_TRAPNR(%rcx) - mov %si, EX_ERROR_CODE(%rcx) - mov %rdx, EX_ADDRESS(%rcx) -3: mov $(-EFAULT), %rax - ret -.popsection - -_ASM_VDSO_EXTABLE_HANDLE(1b, 2b) + /* Prolog */ + .cfi_startproc + push %rbp + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rbp, 0 + mov %rsp, %rbp + .cfi_def_cfa_register %rbp + +1: /* EENTER <= leaf <= ERESUME */ + cmp $0x2, %eax + jb 6f + cmp $0x3, %eax + ja 6f + + /* Load TCS and AEP */ + mov 0x10(%rbp), %rbx + lea 2f(%rip), %rcx + + /* Single ENCLU serving as both EENTER and AEP (ERESUME) */ +2: enclu + + /* EEXIT path */ + xor %ebx, %ebx +3: mov 0x18(%rbp), %rcx + jrcxz 4f + mov %eax, EX_LEAF(%rcx) + jnc 4f + mov %di, EX_TRAPNR(%rcx) + mov %si, EX_ERROR_CODE(%rcx) + mov %rdx, EX_ADDRESS(%rcx) + +4: /* Call *callback if supplied */ + mov 0x20(%rbp), %rax + test %rax, %rax + /* + * At this point, %ebx holds the effective return value, which shall be + * returned if no callback is specified + */ + cmovz %rbx, %rax + jz 7f + /* + * Align stack per x86_64 ABI. The original %rsp is saved in %rbx to be + * restored after *callback returns. + */ + mov %rsp, %rbx + and $-0x10, %rsp + /* Clear RFLAGS.DF per x86_64 ABI */ + cld + /* Parameters for *callback */ + push %rbx + push 0x10(%rbp) + /* Call *%rax via retpoline */ + call 40f + /* + * Restore %rsp to its original value left off by the enclave from last + * exit + */ + mov %rbx, %rsp + /* + * Positive return value from *callback will be interpreted as an ENCLU + * leaf, while a non-positive value will be interpreted as the return + * value to be passed back to the caller. + */ + jmp 1b +40: /* retpoline */ + call 42f +41: pause + lfence + jmp 41b +42: mov %rax, (%rsp) + ret + +5: /* Exception path */ + mov $-EFAULT, %ebx + stc + jmp 3b + +6: /* Unsupported ENCLU leaf */ + cmp $0, %eax + jle 7f + mov $-EINVAL, %eax + +7: /* Epilog */ + leave + .cfi_def_cfa %rsp, 8 + ret + .cfi_endproc + +_ASM_VDSO_EXTABLE_HANDLE(2b, 5b) ENDPROC(__vdso_sgx_enter_enclave) diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h index 9ed690a38c70..50d2b5143e5e 100644 --- a/arch/x86/include/uapi/asm/sgx.h +++ b/arch/x86/include/uapi/asm/sgx.h @@ -24,7 +24,7 @@ /** * struct sgx_enclave_create - parameter structure for the - * %SGX_IOC_ENCLAVE_CREATE ioctl + * %SGX_IOC_ENCLAVE_CREATE ioctl * @src: address for the SECS page data */ struct sgx_enclave_create { @@ -33,7 +33,7 @@ struct sgx_enclave_create { /** * struct sgx_enclave_add_page - parameter structure for the - * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl + * %SGX_IOC_ENCLAVE_ADD_PAGE ioctl * @addr: address within the ELRANGE * @src: address for the page data * @secinfo: address for the SECINFO data @@ -49,7 +49,7 @@ struct sgx_enclave_add_page { /** * struct sgx_enclave_init - parameter structure for the - * %SGX_IOC_ENCLAVE_INIT ioctl + * %SGX_IOC_ENCLAVE_INIT ioctl * @sigstruct: address for the SIGSTRUCT data */ struct sgx_enclave_init { @@ -66,16 +66,16 @@ struct sgx_enclave_set_attribute { }; /** - * struct sgx_enclave_exception - structure to report exceptions encountered in - * __vdso_sgx_enter_enclave() + * struct sgx_enclave_exinfo - structure to report exceptions encountered in + * __vdso_sgx_enter_enclave() * - * @leaf: ENCLU leaf from \%eax at time of exception + * @leaf: ENCLU leaf from \%eax at time of exception/exit * @trapnr: exception trap number, a.k.a. fault vector * @error_code: exception error code * @address: exception address, e.g. CR2 on a #PF * @reserved: reserved for future use */ -struct sgx_enclave_exception { +struct sgx_enclave_exinfo { __u32 leaf; __u16 trapnr; __u16 error_code; From patchwork Sat Jul 13 06:51:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Xing, Cedric" X-Patchwork-Id: 11042909 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 67BCC912 for ; Sat, 13 Jul 2019 06:51:37 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 54FE7284F5 for ; Sat, 13 Jul 2019 06:51:37 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4920228541; Sat, 13 Jul 2019 06:51:37 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3C159284F5 for ; Sat, 13 Jul 2019 06:51:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726274AbfGMGvc (ORCPT ); Sat, 13 Jul 2019 02:51:32 -0400 Received: from mga11.intel.com ([192.55.52.93]:55269 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727490AbfGMGvb (ORCPT ); Sat, 13 Jul 2019 02:51:31 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga004.jf.intel.com ([10.7.209.38]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 12 Jul 2019 23:51:28 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.63,485,1557212400"; d="scan'208";a="318200508" Received: from bxing-ubuntu.jf.intel.com ([10.23.30.27]) by orsmga004.jf.intel.com with ESMTP; 12 Jul 2019 23:51:27 -0700 From: Cedric Xing To: linux-kernel@vger.kernel.org, linux-sgx@vger.kernel.org, jarkko.sakkinen@linux.intel.com Cc: cedric.xing@intel.com, akpm@linux-foundation.org, dave.hansen@intel.com, sean.j.christopherson@intel.com, serge.ayoun@intel.com, shay.katz-zamir@intel.com, haitao.huang@intel.com, kai.svahn@intel.com, kai.huang@intel.com Subject: [RFC PATCH v4 3/3] selftests/x86/sgx: Augment SGX selftest to test vDSO API Date: Fri, 12 Jul 2019 23:51:27 -0700 Message-Id: <657fe13cbf962d72dc1afc0e25577d8e89225702.1563000446.git.cedric.xing@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch augments SGX selftest with two new tests. The first test exercises the newly added callback interface, by marking the whole enclave range as PROT_READ, then calling mprotect() upon #PFs to add necessary PTE permissions per PFEC (#PF Error Code) until the enclave finishes. This test also serves as an example to demonstrate the callback interface. The second test single-steps through __vdso_sgx_enter_enclave() to make sure the call stack can be unwound at every instruction within that vDSO API. Its purpose is to validate the hand-crafted CFI directives in the assembly. Besides the new tests, this patch also fixes minor problems in the Makefile, such as: * appended "--build-id=none" to ld command line to suppress the ".note.gnu.build-id section discarded" linker warning. * removed "--remove-section=.got.plt" from objcopy command line as that section would never exist in statically linked (enclave) images. Signed-off-by: Cedric Xing --- tools/testing/selftests/x86/sgx/Makefile | 6 +- tools/testing/selftests/x86/sgx/main.c | 344 ++++++++++++++++++--- tools/testing/selftests/x86/sgx/sgx_call.S | 40 ++- 3 files changed, 343 insertions(+), 47 deletions(-) diff --git a/tools/testing/selftests/x86/sgx/Makefile b/tools/testing/selftests/x86/sgx/Makefile index 3af15d7c8644..31f937e220c4 100644 --- a/tools/testing/selftests/x86/sgx/Makefile +++ b/tools/testing/selftests/x86/sgx/Makefile @@ -14,16 +14,16 @@ TEST_CUSTOM_PROGS := $(OUTPUT)/test_sgx all_64: $(TEST_CUSTOM_PROGS) $(TEST_CUSTOM_PROGS): main.c sgx_call.S $(OUTPUT)/encl_piggy.o - $(CC) $(HOST_CFLAGS) -o $@ $^ + $(CC) $(HOST_CFLAGS) -o $@ $^ -lunwind -ldl -Wl,--defsym,__image_base=0 -pie $(OUTPUT)/encl_piggy.o: encl_piggy.S $(OUTPUT)/encl.bin $(OUTPUT)/encl.ss $(CC) $(HOST_CFLAGS) -I$(OUTPUT) -c $< -o $@ $(OUTPUT)/encl.bin: $(OUTPUT)/encl.elf - objcopy --remove-section=.got.plt -O binary $< $@ + objcopy -O binary $< $@ $(OUTPUT)/encl.elf: encl.lds encl.c encl_bootstrap.S - $(CC) $(ENCL_CFLAGS) -T $^ -o $@ + $(CC) $(ENCL_CFLAGS) -T $^ -o $@ -Wl,--build-id=none $(OUTPUT)/encl.ss: $(OUTPUT)/sgxsign signing_key.pem $(OUTPUT)/encl.bin $^ $@ diff --git a/tools/testing/selftests/x86/sgx/main.c b/tools/testing/selftests/x86/sgx/main.c index e2265f841fb0..e47d6c32623f 100644 --- a/tools/testing/selftests/x86/sgx/main.c +++ b/tools/testing/selftests/x86/sgx/main.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) // Copyright(c) 2016-18 Intel Corporation. +#define _GNU_SOURCE #include #include #include @@ -9,16 +10,31 @@ #include #include #include +#include #include #include #include -#include +#include +#include +#include + +#define UNW_LOCAL_ONLY +#include + #include "encl_piggy.h" #include "defines.h" #include "../../../../../arch/x86/kernel/cpu/sgx/arch.h" #include "../../../../../arch/x86/include/uapi/asm/sgx.h" -static const uint64_t MAGIC = 0x1122334455667788ULL; +#define _Q(x) __Q(x) +#define __Q(x) #x +#define ERRLN "Line " _Q(__LINE__) + +#define X86_EFLAGS_TF (1ul << 8) + +extern char __image_base[]; +size_t eenter; +static size_t vdso_base; struct vdso_symtab { Elf64_Sym *elf_symtab; @@ -26,20 +42,11 @@ struct vdso_symtab { Elf64_Word *elf_hashtab; }; -static void *vdso_get_base_addr(char *envp[]) +static void vdso_init(void) { - Elf64_auxv_t *auxv; - int i; - - for (i = 0; envp[i]; i++); - auxv = (Elf64_auxv_t *)&envp[i + 1]; - - for (i = 0; auxv[i].a_type != AT_NULL; i++) { - if (auxv[i].a_type == AT_SYSINFO_EHDR) - return (void *)auxv[i].a_un.a_val; - } - - return NULL; + vdso_base = getauxval(AT_SYSINFO_EHDR); + if (!vdso_base) + exit(1); } static Elf64_Dyn *vdso_get_dyntab(void *addr) @@ -66,8 +73,9 @@ static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag) return NULL; } -static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab) +static bool vdso_get_symtab(struct vdso_symtab *symtab) { + void *addr = (void *)vdso_base; Elf64_Dyn *dyntab = vdso_get_dyntab(addr); symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB); @@ -138,7 +146,7 @@ static bool encl_create(int dev_fd, unsigned long bin_size, base = mmap(NULL, secs->size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, dev_fd, 0); if (base == MAP_FAILED) { - perror("mmap"); + perror(ERRLN); return false; } @@ -224,35 +232,292 @@ static bool encl_load(struct sgx_secs *secs, unsigned long bin_size) return false; } -void sgx_call(void *rdi, void *rsi, void *tcs, - struct sgx_enclave_exception *exception, - void *eenter); +int sgx_call(void *rdi, void *rsi, long rdx, void *rcx, void *r8, void *r9, + void *tcs, struct sgx_enclave_exinfo *ei, void *cb); + +static void show_enclave_exinfo(const struct sgx_enclave_exinfo *exinfop, + const char *header) +{ + static const char * const enclu_leaves[] = { + "EREPORT", + "EGETKEY", + "EENTER", + "ERESUME", + "EEXIT" + }; + static const char * const exception_names[] = { + "#DE", + "#DB", + "NMI", + "#BP", + "#OF", + "#BR", + "#UD", + "#NM", + "#DF", + "CSO", + "#TS", + "#NP", + "#SS", + "#GP", + "#PF", + "Unknown", + "#MF", + "#AC", + "#MC", + "#XM", + "#VE", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown" + }; + + printf("%s: leaf:%s(%d)", header, + enclu_leaves[exinfop->leaf], exinfop->leaf); + if (exinfop->leaf != 4) + printf(" trap:%s(%d) ec:%d addr:0x%llx\n", + exception_names[exinfop->trapnr], exinfop->trapnr, + exinfop->error_code, exinfop->address); + else + printf("\n"); +} + +static const uint64_t MAGIC = 0x1122334455667788ULL; + +/* + * test1() tests vDSO API (i.e. __vdso_sgx_enter_enclave) without supplying a + * callback function. It loads a very simple enclave that copies a 64-bit + * value from source buffer to the destination. Then it invokes the enclave + * twice. At the first time it provides all valid inputs and verifies the + * output buffer contains the same value as the source buffer. At the second + * time, it provides NULL as the TCS address to exercise the exception flow. + */ +static void test1(struct sgx_secs *secs) +{ + uint64_t result = 0; + struct sgx_enclave_exinfo exinfo; + + printf("[1] Entering the enclave without callback.\n"); + + printf("Input: 0x%lx\n Expect: Same as input\n", MAGIC); + sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL, + (void *)secs->base, &exinfo, NULL); + show_enclave_exinfo(&exinfo, " Exit"); + if (result != MAGIC) { + fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC); + exit(1); + } + printf(" Output: 0x%lx\n", result); + + printf("Input: Null TCS\n Expect: #PF at EENTER\n"); + sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL, + NULL, &exinfo, NULL); + show_enclave_exinfo(&exinfo, " Exit"); + if (exinfo.leaf != 2 /*EENTER*/ || exinfo.trapnr != 14 /*#PF*/) + exit(1); +} + +static int test2_callback(long rdi, long rsi, long rdx, + struct sgx_enclave_exinfo *ei, long r8, long r9, + void *tcs, long ursp) +{ + show_enclave_exinfo(ei, " callback"); + + switch (ei->leaf) { + case 4: + return 0; + case 3: + case 2: + switch (ei->trapnr) { + case 1: /*#DB*/ + break; + case 14:/*#PF*/ + if ((ei->error_code & 1) == 0) { + fprintf(stderr, ERRLN + ": Unexpected #PF error code\n"); + exit(1); + } + if (mprotect((void *)(ei->address & -0x1000), 0x1000, + ((ei->error_code & 2) ? PROT_WRITE : 0) | + ((ei->error_code & 0x10) ? PROT_EXEC : 0) | + PROT_READ)) { + perror(ERRLN); + exit(1); + } + break; + default: + fprintf(stderr, ERRLN ": Unexpected exception\n"); + exit(1); + } + return ei->leaf == 2 ? -EAGAIN : ei->leaf; + } + return -EINVAL; +} + +/* + * test2() tests the exception/callback mechanism of the vDSO API with a + * callback function. Firstly, it supplies all valid inputs along with a + * callback function, and verifies that exinfo contains the expected values. + * Secondly, it marks the whole enclave virtual range as read-only, and let the + * callback fixes the PTE permissions by calling mprotect() along the way. The + * callback in this test also serves an example to show how to use the callback + * interface. + */ +static void test2(struct sgx_secs *secs) +{ + uint64_t result = 0; + struct sgx_enclave_exinfo exinfo; + + printf("[2] Entering the enclave with callback.\n"); + + printf("Input: 0x%lx\n Expect: Same as input\n", MAGIC); + sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL, + (void *)secs->base, &exinfo, test2_callback); + if (result != MAGIC) { + fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC); + exit(1); + } + printf(" Output: 0x%lx\n", result); + + printf("Input: Read-only enclave (0x%lx-0x%lx)\n" + " Expect: #PFs to be fixed by callback\n", + secs->base, secs->base + (encl_bin_end - encl_bin) - 1); + if (mprotect((void *)secs->base, encl_bin_end - encl_bin, PROT_READ)) { + perror(ERRLN); + exit(1); + } + while (sgx_call((void *)&MAGIC, &result, 0, NULL, NULL, NULL, + (void *)secs->base, &exinfo, test2_callback) == -EAGAIN) + ; + show_enclave_exinfo(&exinfo, " Exit"); + if (exinfo.leaf != 4 /*EEXIT*/) + exit(1); +} + +static void *test3_caller; +static struct test3_proc_context { + unw_word_t ip, bx, sp, bp, r12, r13, r14, r15; +} test3_ctx; -int main(int argc, char *argv[], char *envp[]) +static unw_word_t test3_getcontext(unw_cursor_t *cursor, + struct test3_proc_context *ctxp) +{ + unw_get_reg(cursor, UNW_REG_IP, &ctxp->ip); + unw_get_reg(cursor, UNW_REG_SP, &ctxp->sp); + unw_get_reg(cursor, UNW_X86_64_RBX, &ctxp->bx); + unw_get_reg(cursor, UNW_X86_64_RBP, &ctxp->bp); + unw_get_reg(cursor, UNW_X86_64_R12, &ctxp->r12); + unw_get_reg(cursor, UNW_X86_64_R13, &ctxp->r13); + unw_get_reg(cursor, UNW_X86_64_R14, &ctxp->r14); + unw_get_reg(cursor, UNW_X86_64_R15, &ctxp->r15); + return ctxp->ip; +} + +static void test3_sigtrap(int sig, siginfo_t *info, ucontext_t *ctxp) +{ + static int in_vdso_eenter; + + unw_cursor_t cursor; + unw_context_t uc; + struct test3_proc_context pc; + + if (ctxp->uc_mcontext.gregs[REG_RIP] == eenter) { + in_vdso_eenter = 1; + printf(" trace started at ip:%llx (vdso:0x%llx)\n", + ctxp->uc_mcontext.gregs[REG_RIP], + ctxp->uc_mcontext.gregs[REG_RIP] - vdso_base); + } + + if (!in_vdso_eenter) + return; + + if ((void *)ctxp->uc_mcontext.gregs[REG_RIP] == test3_caller) { + in_vdso_eenter = 0; + ctxp->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF; + printf(" trace ended successfully at ip:%llx (executable:0x%llx)\n", + ctxp->uc_mcontext.gregs[REG_RIP], + ctxp->uc_mcontext.gregs[REG_RIP] - + (size_t)__image_base); + return; + } + + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + while (unw_step(&cursor) > 0 && + test3_getcontext(&cursor, &pc) != test3_ctx.ip) + ; + + if (memcmp(&pc, &test3_ctx, sizeof(pc))) { + fprintf(stderr, ERRLN ": Error unwinding\n"); + exit(1); + } +} + +__attribute__((noinline)) +static void test3_test_unwind(void (*f)(struct sgx_secs *), + struct sgx_secs *secs) +{ + test3_caller = __builtin_return_address(0); + __asm__ ("pushfq; orl %0, (%%rsp); popfq" : : "i"(X86_EFLAGS_TF)); + f(secs); +} + +/* + * test3() single-steps through the vDSO API to test out CFI directives inside + * the API. + */ +static void test3(struct sgx_secs *secs) +{ + unw_cursor_t cursor; + unw_context_t uc; + struct sigaction sa = { + .sa_sigaction = (void (*)(int, siginfo_t*, void*))test3_sigtrap, + .sa_flags = SA_SIGINFO, + }; + + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + if (unw_step(&cursor) > 0) + test3_getcontext(&cursor, &test3_ctx); + else { + fprintf(stderr, ERRLN ": error initializing unwind context\n"); + exit(1); + } + + if (sigaction(SIGTRAP, &sa, NULL) < 0) { + perror(ERRLN); + exit(1); + } + + test3_test_unwind(test1, secs); + test3_test_unwind(test2, secs); +} + +int main(void) { unsigned long bin_size = encl_bin_end - encl_bin; unsigned long ss_size = encl_ss_end - encl_ss; - struct sgx_enclave_exception exception; Elf64_Sym *eenter_sym; struct vdso_symtab symtab; struct sgx_secs secs; - uint64_t result = 0; - void *eenter; - void *addr; - - memset(&exception, 0, sizeof(exception)); - addr = vdso_get_base_addr(envp); - if (!addr) - exit(1); + vdso_init(); - if (!vdso_get_symtab(addr, &symtab)) + if (!vdso_get_symtab(&symtab)) exit(1); eenter_sym = vdso_symtab_get(&symtab, "__vdso_sgx_enter_enclave"); if (!eenter_sym) exit(1); - eenter = addr + eenter_sym->st_value; + eenter = vdso_base + eenter_sym->st_value; printf("Binary size %lu (0x%lx), SIGSTRUCT size %lu\n", bin_size, bin_size, ss_size); @@ -266,14 +531,11 @@ int main(int argc, char *argv[], char *envp[]) if (!encl_load(&secs, bin_size)) exit(1); - printf("Input: 0x%lx\n", MAGIC); - sgx_call((void *)&MAGIC, &result, (void *)secs.base, &exception, - eenter); - if (result != MAGIC) { - fprintf(stderr, "0x%lx != 0x%lx\n", result, MAGIC); - exit(1); - } + printf("--- Functional Tests ---\n"); + test1(&secs); + test2(&secs); - printf("Output: 0x%lx\n", result); - exit(0); + printf("--- Unwind Tests ---\n"); + test3(&secs); + return 0; } diff --git a/tools/testing/selftests/x86/sgx/sgx_call.S b/tools/testing/selftests/x86/sgx/sgx_call.S index 14bd0a044199..ca2c0c947758 100644 --- a/tools/testing/selftests/x86/sgx/sgx_call.S +++ b/tools/testing/selftests/x86/sgx/sgx_call.S @@ -7,9 +7,43 @@ .global sgx_call sgx_call: + .cfi_startproc + push %r15 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r15, 0 + push %r14 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r14, 0 + push %r13 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r13, 0 + push %r12 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r12, 0 push %rbx - mov $0x02, %rax - mov %rdx, %rbx - call *%r8 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rbx, 0 + push $0 + .cfi_adjust_cfa_offset 8 + push 0x48(%rsp) + .cfi_adjust_cfa_offset 8 + push 0x48(%rsp) + .cfi_adjust_cfa_offset 8 + push 0x48(%rsp) + .cfi_adjust_cfa_offset 8 + mov $2, %eax + call *eenter(%rip) + add $0x20, %rsp + .cfi_adjust_cfa_offset -0x20 pop %rbx + .cfi_adjust_cfa_offset -8 + pop %r12 + .cfi_adjust_cfa_offset -8 + pop %r13 + .cfi_adjust_cfa_offset -8 + pop %r14 + .cfi_adjust_cfa_offset -8 + pop %r15 + .cfi_adjust_cfa_offset -8 ret + .cfi_endproc