[PATCH v10 5/6] x86/sgx: Implement ENCLS[EUPDATESVN]

Elena Reshetova posted 6 patches 2 months ago
[PATCH v10 5/6] x86/sgx: Implement ENCLS[EUPDATESVN]
Posted by Elena Reshetova 2 months ago
All running enclaves and cryptographic assets (such as internal SGX
encryption keys) are assumed to be compromised whenever an SGX-related
microcode update occurs. To mitigate this assumed compromise the new
supervisor SGX instruction ENCLS[EUPDATESVN] can generate fresh
cryptographic assets.

Before executing EUPDATESVN, all SGX memory must be marked as unused.
This requirement ensures that no potentially compromised enclave
survives the update and allows the system to safely regenerate
cryptographic assets.

Add the method to perform ENCLS[EUPDATESVN].

Signed-off-by: Elena Reshetova <elena.reshetova@intel.com>
---
 arch/x86/kernel/cpu/sgx/encls.h |  5 +++
 arch/x86/kernel/cpu/sgx/main.c  | 61 +++++++++++++++++++++++++++++++++
 2 files changed, 66 insertions(+)

diff --git a/arch/x86/kernel/cpu/sgx/encls.h b/arch/x86/kernel/cpu/sgx/encls.h
index 99004b02e2ed..d9160c89a93d 100644
--- a/arch/x86/kernel/cpu/sgx/encls.h
+++ b/arch/x86/kernel/cpu/sgx/encls.h
@@ -233,4 +233,9 @@ static inline int __eaug(struct sgx_pageinfo *pginfo, void *addr)
 	return __encls_2(EAUG, pginfo, addr);
 }
 
+/* Attempt to update CPUSVN at runtime. */
+static inline int __eupdatesvn(void)
+{
+	return __encls_ret_1(EUPDATESVN, "");
+}
 #endif /* _X86_ENCLS_H */
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index 3a5cbd1c170e..5aae0c881963 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -16,6 +16,7 @@
 #include <linux/vmalloc.h>
 #include <asm/msr.h>
 #include <asm/sgx.h>
+#include <asm/archrandom.h>
 #include "driver.h"
 #include "encl.h"
 #include "encls.h"
@@ -917,6 +918,66 @@ int sgx_set_attribute(unsigned long *allowed_attributes,
 }
 EXPORT_SYMBOL_GPL(sgx_set_attribute);
 
+/**
+ * sgx_update_svn() - Attempt to call ENCLS[EUPDATESVN].
+ * This instruction attempts to update CPUSVN to the
+ * currently loaded microcode update SVN and generate new
+ * cryptographic assets. Must be called when EPC is empty.
+ * Most of the time, there will be no update and that's OK.
+ * If the failure is due to SGX_INSUFFICIENT_ENTROPY, the
+ * operation can be safely retried. In other failure cases,
+ * the retry should not be attempted.
+ *
+ * Return:
+ * 0: Success or not supported
+ * -EAGAIN: Can be safely retried, failure is due to lack of
+ *  entropy in RNG.
+ * -EIO: Unexpected error, retries are not advisable.
+ */
+static int __maybe_unused sgx_update_svn(void)
+{
+	int ret;
+
+	/*
+	 * If EUPDATESVN is not available, it is ok to
+	 * silently skip it to comply with legacy behavior.
+	 */
+	if (!cpu_feature_enabled(X86_FEATURE_SGX_EUPDATESVN))
+		return 0;
+
+	for (int i = 0; i < RDRAND_RETRY_LOOPS; i++) {
+		ret = __eupdatesvn();
+
+		/* Stop on success or unexpected errors: */
+		if (ret != SGX_INSUFFICIENT_ENTROPY)
+			break;
+	}
+
+	/*
+	 * SVN successfully updated.
+	 * Let users know when the update was successful.
+	 */
+	if (!ret)
+		pr_info("SVN updated successfully\n");
+
+	if (!ret || ret == SGX_NO_UPDATE)
+		return 0;
+
+	/*
+	 * SVN update failed due to lack of entropy in DRNG.
+	 * Indicate to userspace that it should retry.
+	 */
+	if (ret == SGX_INSUFFICIENT_ENTROPY)
+		return -EAGAIN;
+
+	/*
+	 * EUPDATESVN was called when EPC is empty, all other error
+	 * codes are unexpected.
+	 */
+	ENCLS_WARN(ret, "EUPDATESVN");
+	return -EIO;
+}
+
 int sgx_inc_usage_count(void)
 {
 	return 0;
-- 
2.45.2
Re: [PATCH v10 5/6] x86/sgx: Implement ENCLS[EUPDATESVN]
Posted by Dave Hansen 2 months ago
The changelog is missing a tidbit about the fact that this is still dead
code until sgx_inc_usage_count() gets a real implementation.

On 8/1/25 04:25, Elena Reshetova wrote:
...
> +/**
> + * sgx_update_svn() - Attempt to call ENCLS[EUPDATESVN].
> + * This instruction attempts to update CPUSVN to the
> + * currently loaded microcode update SVN and generate new
> + * cryptographic assets. Must be called when EPC is empty.

As a general rule, I'd much rather have the "Must be $FOO" rules written
in code than in a comment, or along with a comment:

	/* EPC is guaranteed to be empty when there are no users: */
	WARN(count, "Elevated usage count...");

> + * Most of the time, there will be no update and that's OK.

This should go with the check for SGX_NO_UPDATE, not here.

> + * If the failure is due to SGX_INSUFFICIENT_ENTROPY, the
> + * operation can be safely retried. In other failure cases,
> + * the retry should not be attempted.

Ditto. This is rewriting the code in comments.

> + * Return:
> + * 0: Success or not supported
> + * -EAGAIN: Can be safely retried, failure is due to lack of
> + *  entropy in RNG.
> + * -EIO: Unexpected error, retries are not advisable.
> + */
> +static int __maybe_unused sgx_update_svn(void)
> +{
> +	int ret;
> +
> +	/*
> +	 * If EUPDATESVN is not available, it is ok to
> +	 * silently skip it to comply with legacy behavior.
> +	 */
> +	if (!cpu_feature_enabled(X86_FEATURE_SGX_EUPDATESVN))
> +		return 0;
> +
> +	for (int i = 0; i < RDRAND_RETRY_LOOPS; i++) {
> +		ret = __eupdatesvn();
> +
> +		/* Stop on success or unexpected errors: */
> +		if (ret != SGX_INSUFFICIENT_ENTROPY)
> +			break;
> +	}
> +
> +	/*
> +	 * SVN successfully updated.
> +	 * Let users know when the update was successful.
> +	 */
> +	if (!ret)
> +		pr_info("SVN updated successfully\n");
> +
> +	if (!ret || ret == SGX_NO_UPDATE)
> +		return 0;
> +
> +	/*
> +	 * SVN update failed due to lack of entropy in DRNG.
> +	 * Indicate to userspace that it should retry.
> +	 */
> +	if (ret == SGX_INSUFFICIENT_ENTROPY)
> +		return -EAGAIN;

There are four cases to handle. Doesn't it make sense to just write it
as four literal "case"s?

	switch (ret) {
	case 0:
		pr_info("...");
		return 0;
	case SGX_NO_UPDATE:
		return 0;
	case SGX_INSUFFICIENT_ENTROPY:
		return -EAGAIN;
	default:
		break;
	}


> +	ENCLS_WARN(ret, "EUPDATESVN");
> +	return -EIO;
> +}
> +
>  int sgx_inc_usage_count(void)
>  {
>  	return 0;
RE: [PATCH v10 5/6] x86/sgx: Implement ENCLS[EUPDATESVN]
Posted by Reshetova, Elena 2 months ago

> -----Original Message-----
> From: Hansen, Dave <dave.hansen@intel.com>
> Sent: Friday, August 1, 2025 8:13 PM
> To: Reshetova, Elena <elena.reshetova@intel.com>
> Cc: jarkko@kernel.org; seanjc@google.com; Huang, Kai
> <kai.huang@intel.com>; mingo@kernel.org; linux-sgx@vger.kernel.org; linux-
> kernel@vger.kernel.org; x86@kernel.org; Mallick, Asit K
> <asit.k.mallick@intel.com>; Scarlata, Vincent R <vincent.r.scarlata@intel.com>;
> Cai, Chong <chongc@google.com>; Aktas, Erdem <erdemaktas@google.com>;
> Annapurve, Vishal <vannapurve@google.com>; Bondarevska, Nataliia
> <bondarn@google.com>; Raynor, Scott <scott.raynor@intel.com>
> Subject: Re: [PATCH v10 5/6] x86/sgx: Implement ENCLS[EUPDATESVN]
> 
> The changelog is missing a tidbit about the fact that this is still dead
> code until sgx_inc_usage_count() gets a real implementation.

Will add. 

> 
> On 8/1/25 04:25, Elena Reshetova wrote:
> ...
> > +/**
> > + * sgx_update_svn() - Attempt to call ENCLS[EUPDATESVN].
> > + * This instruction attempts to update CPUSVN to the
> > + * currently loaded microcode update SVN and generate new
> > + * cryptographic assets. Must be called when EPC is empty.
> 
> As a general rule, I'd much rather have the "Must be $FOO" rules written
> in code than in a comment, or along with a comment:
> 
> 	/* EPC is guaranteed to be empty when there are no users: */
> 	WARN(count, "Elevated usage count...");

I will change to do it this way. 

> 
> > + * Most of the time, there will be no update and that's OK.
> 
> This should go with the check for SGX_NO_UPDATE, not here.

Ok, will fix. 

> 
> > + * If the failure is due to SGX_INSUFFICIENT_ENTROPY, the
> > + * operation can be safely retried. In other failure cases,
> > + * the retry should not be attempted.
> 
> Ditto. This is rewriting the code in comments.

Ok, will drop. 

> 
> > + * Return:
> > + * 0: Success or not supported
> > + * -EAGAIN: Can be safely retried, failure is due to lack of
> > + *  entropy in RNG.
> > + * -EIO: Unexpected error, retries are not advisable.
> > + */
> > +static int __maybe_unused sgx_update_svn(void)
> > +{
> > +	int ret;
> > +
> > +	/*
> > +	 * If EUPDATESVN is not available, it is ok to
> > +	 * silently skip it to comply with legacy behavior.
> > +	 */
> > +	if (!cpu_feature_enabled(X86_FEATURE_SGX_EUPDATESVN))
> > +		return 0;
> > +
> > +	for (int i = 0; i < RDRAND_RETRY_LOOPS; i++) {
> > +		ret = __eupdatesvn();
> > +
> > +		/* Stop on success or unexpected errors: */
> > +		if (ret != SGX_INSUFFICIENT_ENTROPY)
> > +			break;
> > +	}
> > +
> > +	/*
> > +	 * SVN successfully updated.
> > +	 * Let users know when the update was successful.
> > +	 */
> > +	if (!ret)
> > +		pr_info("SVN updated successfully\n");
> > +
> > +	if (!ret || ret == SGX_NO_UPDATE)
> > +		return 0;
> > +
> > +	/*
> > +	 * SVN update failed due to lack of entropy in DRNG.
> > +	 * Indicate to userspace that it should retry.
> > +	 */
> > +	if (ret == SGX_INSUFFICIENT_ENTROPY)
> > +		return -EAGAIN;
> 
> There are four cases to handle. Doesn't it make sense to just write it
> as four literal "case"s?
> 
> 	switch (ret) {
> 	case 0:
> 		pr_info("...");
> 		return 0;
> 	case SGX_NO_UPDATE:
> 		return 0;
> 	case SGX_INSUFFICIENT_ENTROPY:
> 		return -EAGAIN;
> 	default:
> 		break;
> 	}
> 
> 

Will re-write accordingly. 

Thank you very much  for your prompt review Dave!

Best Regards,
Elena.