From patchwork Tue Sep 15 11:28:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jarkko Sakkinen X-Patchwork-Id: 11778597 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 53CCD6CA for ; Wed, 16 Sep 2020 00:53:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 32D1D20BED for ; Wed, 16 Sep 2020 00:53:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (2048-bit key) header.d=kapsi.fi header.i=@kapsi.fi header.b="qBRTKhAO" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726627AbgIPAxB (ORCPT ); Tue, 15 Sep 2020 20:53:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34830 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726340AbgIOLcs (ORCPT ); Tue, 15 Sep 2020 07:32:48 -0400 Received: from mail.kapsi.fi (mail.kapsi.fi [IPv6:2001:67c:1be8::25]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8D4CEC061223; Tue, 15 Sep 2020 04:28:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=kapsi.fi; s=20161220; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=sadeI2gbThXzStB3S9SzIozhtg74//aDDuEmas83WBg=; b=qBRTKhAOQt6lvoEm4H4u6ih9nE fvaaZuOAg0Ovkg8/sIckqYbTmrEQfWWXwT4b8s5oQ0/HTTUdCz3cOBDM3ZlJM5t1mC2YRbrRkXW/Z VdBXcOv/N3Ga+8HBUjguGtZ0b6qfuCCYe4BuQHbdJr1E/LMWJ60ezxIR7oPAseT6+p32e/ljV0hQq SYmW0DaIueYR6hTBywQgEn5vDOgbSVCDz7LAaNV6vIsHjdPSM39q6ZF2WhPES0a+mdSLLfdGyiNTn oxdIl3eRP4dZPdCOsBaIa0zpAX7FSak8M4G5LJZUImfBWgb8ATfH7Qvku4XYzzR8rpDbymV/hNvZv RxEuiYuw==; Received: from 83-245-197-237.elisa-laajakaista.fi ([83.245.197.237] helo=localhost) by mail.kapsi.fi with esmtpsa (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1kI98m-0000kp-0C; Tue, 15 Sep 2020 14:28:52 +0300 From: Jarkko Sakkinen To: x86@kernel.org, linux-sgx@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Jarkko Sakkinen , Jethro Beekman , Haitao Huang , Chunyang Hui , Jordan Hand , Nathaniel McCallum , Seth Moore , Darren Kenny , Sean Christopherson , Suresh Siddha , akpm@linux-foundation.org, andriy.shevchenko@linux.intel.com, asapek@google.com, bp@alien8.de, cedric.xing@intel.com, chenalexchen@google.com, conradparker@google.com, cyhanish@google.com, dave.hansen@intel.com, haitao.huang@intel.com, josh@joshtriplett.org, kai.huang@intel.com, kai.svahn@intel.com, kmoy@google.com, ludloff@google.com, luto@kernel.org, nhorman@redhat.com, puiterwijk@redhat.com, rientjes@google.com, tglx@linutronix.de, yaozhangx@google.com Subject: [PATCH v38 14/24] x86/sgx: Add SGX_IOC_ENCLAVE_INIT Date: Tue, 15 Sep 2020 14:28:32 +0300 Message-Id: <20200915112842.897265-15-jarkko.sakkinen@linux.intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200915112842.897265-1-jarkko.sakkinen@linux.intel.com> References: <20200915112842.897265-1-jarkko.sakkinen@linux.intel.com> MIME-Version: 1.0 X-SA-Exim-Connect-IP: 83.245.197.237 X-SA-Exim-Mail-From: jjs@kapsi.fi X-SA-Exim-Scanned: No (on mail.kapsi.fi); SAEximRunCond expanded to false Sender: linux-sgx-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sgx@vger.kernel.org Add an ioctl that performs ENCLS[EINIT], which locks down the measurement and initializes the enclave for entrance. After this, new pages can no longer be added. Acked-by: Jethro Beekman Tested-by: Jethro Beekman Tested-by: Haitao Huang Tested-by: Chunyang Hui Tested-by: Jordan Hand Tested-by: Nathaniel McCallum Tested-by: Seth Moore Tested-by: Darren Kenny Reviewed-by: Darren Kenny Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson Co-developed-by: Suresh Siddha Signed-off-by: Suresh Siddha Signed-off-by: Jarkko Sakkinen --- arch/x86/include/uapi/asm/sgx.h | 11 ++ arch/x86/kernel/cpu/sgx/encl.h | 2 + arch/x86/kernel/cpu/sgx/ioctl.c | 195 ++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h index c42a2ad3ca0b..7729730d8580 100644 --- a/arch/x86/include/uapi/asm/sgx.h +++ b/arch/x86/include/uapi/asm/sgx.h @@ -23,6 +23,8 @@ enum sgx_page_flags { _IOW(SGX_MAGIC, 0x00, struct sgx_enclave_create) #define SGX_IOC_ENCLAVE_ADD_PAGES \ _IOWR(SGX_MAGIC, 0x01, struct sgx_enclave_add_pages) +#define SGX_IOC_ENCLAVE_INIT \ + _IOW(SGX_MAGIC, 0x02, struct sgx_enclave_init) /** * struct sgx_enclave_create - parameter structure for the @@ -50,4 +52,13 @@ struct sgx_enclave_add_pages { __u64 flags; }; +/** + * struct sgx_enclave_init - parameter structure for the + * %SGX_IOC_ENCLAVE_INIT ioctl + * @sigstruct: address for the SIGSTRUCT data + */ +struct sgx_enclave_init { + __u64 sigstruct; +}; + #endif /* _UAPI_ASM_X86_SGX_H */ diff --git a/arch/x86/kernel/cpu/sgx/encl.h b/arch/x86/kernel/cpu/sgx/encl.h index 8ff445476657..0448d22d3010 100644 --- a/arch/x86/kernel/cpu/sgx/encl.h +++ b/arch/x86/kernel/cpu/sgx/encl.h @@ -70,6 +70,8 @@ struct sgx_encl { struct xarray page_array; struct sgx_encl_page secs; cpumask_t cpumask; + unsigned long attributes; + unsigned long attributes_mask; }; extern const struct vm_operations_struct sgx_vm_ops; diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c index 202680a06c17..de2ed4f35ffb 100644 --- a/arch/x86/kernel/cpu/sgx/ioctl.c +++ b/arch/x86/kernel/cpu/sgx/ioctl.c @@ -16,6 +16,9 @@ #include "encl.h" #include "encls.h" +/* A per-cpu cache for the last known values of IA32_SGXLEPUBKEYHASHx MSRs. */ +static DEFINE_PER_CPU(u64 [4], sgx_lepubkeyhash_cache); + static u32 sgx_calc_ssa_frame_size(u32 miscselect, u64 xfrm) { u32 size_max = PAGE_SIZE; @@ -127,6 +130,9 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs) encl->base = secs->base; encl->size = secs->size; encl->ssaframesize = secs->ssa_frame_size; + encl->attributes = secs->attributes; + encl->attributes_mask = SGX_ATTR_DEBUG | SGX_ATTR_MODE64BIT | + SGX_ATTR_KSS; /* * Set SGX_ENCL_CREATED only after the enclave is fully prepped. This @@ -482,6 +488,192 @@ static long sgx_ioc_enclave_add_pages(struct sgx_encl *encl, void __user *arg) return c; } +static int __sgx_get_key_hash(struct crypto_shash *tfm, const void *modulus, + void *hash) +{ + SHASH_DESC_ON_STACK(shash, tfm); + + shash->tfm = tfm; + + return crypto_shash_digest(shash, modulus, SGX_MODULUS_SIZE, hash); +} + +static int sgx_get_key_hash(const void *modulus, void *hash) +{ + struct crypto_shash *tfm; + int ret; + + tfm = crypto_alloc_shash("sha256", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + ret = __sgx_get_key_hash(tfm, modulus, hash); + + crypto_free_shash(tfm); + return ret; +} + +static void sgx_update_lepubkeyhash_msrs(u64 *lepubkeyhash, bool enforce) +{ + u64 *cache; + int i; + + cache = per_cpu(sgx_lepubkeyhash_cache, smp_processor_id()); + for (i = 0; i < 4; i++) { + if (enforce || (lepubkeyhash[i] != cache[i])) { + wrmsrl(MSR_IA32_SGXLEPUBKEYHASH0 + i, lepubkeyhash[i]); + cache[i] = lepubkeyhash[i]; + } + } +} + +static int sgx_einit(struct sgx_sigstruct *sigstruct, void *token, + struct sgx_epc_page *secs, u64 *lepubkeyhash) +{ + int ret; + + preempt_disable(); + sgx_update_lepubkeyhash_msrs(lepubkeyhash, false); + ret = __einit(sigstruct, token, sgx_get_epc_addr(secs)); + if (ret == SGX_INVALID_EINITTOKEN) { + sgx_update_lepubkeyhash_msrs(lepubkeyhash, true); + ret = __einit(sigstruct, token, sgx_get_epc_addr(secs)); + } + preempt_enable(); + return ret; +} + +static int sgx_encl_init(struct sgx_encl *encl, struct sgx_sigstruct *sigstruct, + void *token) +{ + u64 mrsigner[4]; + int ret; + int i; + int j; + + /* Deny initializing enclaves with attributes (namely provisioning) + * that have not been explicitly allowed. + */ + if (encl->attributes & ~encl->attributes_mask) + return -EACCES; + + ret = sgx_get_key_hash(sigstruct->modulus, mrsigner); + if (ret) + return ret; + + mutex_lock(&encl->lock); + + /* + * ENCLS[EINIT] is interruptible because it has such a high latency, + * e.g. 50k+ cycles on success. If an IRQ/NMI/SMI becomes pending, + * EINIT may fail with SGX_UNMASKED_EVENT so that the event can be + * serviced. + */ + for (i = 0; i < SGX_EINIT_SLEEP_COUNT; i++) { + for (j = 0; j < SGX_EINIT_SPIN_COUNT; j++) { + ret = sgx_einit(sigstruct, token, encl->secs.epc_page, + mrsigner); + if (ret == SGX_UNMASKED_EVENT) + continue; + else + break; + } + + if (ret != SGX_UNMASKED_EVENT) + break; + + msleep_interruptible(SGX_EINIT_SLEEP_TIME); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto err_out; + } + } + + if (ret & ENCLS_FAULT_FLAG) { + if (encls_failed(ret)) + ENCLS_WARN(ret, "EINIT"); + + sgx_encl_destroy(encl); + ret = -EFAULT; + } else if (ret) { + pr_debug("EINIT returned %d\n", ret); + ret = -EPERM; + } else { + atomic_or(SGX_ENCL_INITIALIZED, &encl->flags); + } + +err_out: + mutex_unlock(&encl->lock); + return ret; +} + +/** + * sgx_ioc_enclave_init - handler for %SGX_IOC_ENCLAVE_INIT + * + * @filep: open file to /dev/sgx + * @arg: userspace pointer to a struct sgx_enclave_init instance + * + * Flush any outstanding enqueued EADD operations and perform EINIT. The + * Launch Enclave Public Key Hash MSRs are rewritten as necessary to match + * the enclave's MRSIGNER, which is caculated from the provided sigstruct. + * + * Return: + * 0 on success, + * SGX error code on EINIT failure, + * -errno otherwise + */ +static long sgx_ioc_enclave_init(struct sgx_encl *encl, void __user *arg) +{ + struct sgx_sigstruct *sigstruct; + struct sgx_enclave_init einit; + struct page *initp_page; + void *token; + int ret; + + if ((atomic_read(&encl->flags) & SGX_ENCL_INITIALIZED) || + !(atomic_read(&encl->flags) & SGX_ENCL_CREATED)) + return -EINVAL; + + if (copy_from_user(&einit, arg, sizeof(einit))) + return -EFAULT; + + initp_page = alloc_page(GFP_KERNEL); + if (!initp_page) + return -ENOMEM; + + sigstruct = kmap(initp_page); + token = (void *)((unsigned long)sigstruct + PAGE_SIZE / 2); + memset(token, 0, SGX_LAUNCH_TOKEN_SIZE); + + if (copy_from_user(sigstruct, (void __user *)einit.sigstruct, + sizeof(*sigstruct))) { + ret = -EFAULT; + goto out; + } + + /* + * A legacy field used with Intel signed enclaves. These used to mean + * regular and architectural enclaves. The CPU only accepts these values + * but they do not have any other meaning. + * + * Thus, reject any other values. + */ + if (sigstruct->header.vendor != 0x0000 && + sigstruct->header.vendor != 0x8086) { + ret = -EINVAL; + goto out; + } + + ret = sgx_encl_init(encl, sigstruct, token); + +out: + kunmap(initp_page); + __free_page(initp_page); + return ret; +} + + long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { struct sgx_encl *encl = filep->private_data; @@ -503,6 +695,9 @@ long sgx_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) case SGX_IOC_ENCLAVE_ADD_PAGES: ret = sgx_ioc_enclave_add_pages(encl, (void __user *)arg); break; + case SGX_IOC_ENCLAVE_INIT: + ret = sgx_ioc_enclave_init(encl, (void __user *)arg); + break; default: ret = -ENOIOCTLCMD; break;