linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] x86/umip: Fix UMIP insn decoder false positives
@ 2025-08-08 17:23 Sean Christopherson
  2025-08-08 17:23 ` [PATCH 1/3] x86/umip: Check that the instruction opcode is at least two bytes Sean Christopherson
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Sean Christopherson @ 2025-08-08 17:23 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86
  Cc: linux-kernel, Dan Snyder, Sean Christopherson

Fix two false positives scenarios where the UMIP #GP logic will incorrectly
trigger emulation, e.g. due to a partially decoded instruction, or on
instructions like VMLAUNCH that usurp the register form of '0f 01'.

Tested with the hack-a-test patch at the end, but I haven't done any testing
using a real userspace (neither positive nor negative testing).

Sean Christopherson (3):
  x86/umip: Check that the instruction opcode is at least two bytes
  x86/umip: Fix decoding of register forms of 0F 01 (SGDT and SIDT
    aliases)
  *** DO NOT MERGE *** x86/umip: Lazy person's KUnit test for UMIP
    emulation

 arch/x86/kernel/umip.c | 71 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 69 insertions(+), 2 deletions(-)


base-commit: ce0b5eedcb753697d43f61dd2e27d68eb5d3150f
-- 
2.50.1.703.g449372360f-goog


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] x86/umip: Check that the instruction opcode is at least two bytes
  2025-08-08 17:23 [PATCH 0/3] x86/umip: Fix UMIP insn decoder false positives Sean Christopherson
@ 2025-08-08 17:23 ` Sean Christopherson
  2025-08-14 14:42   ` Peter Zijlstra
  2025-08-08 17:23 ` [PATCH 2/3] x86/umip: Fix decoding of register forms of 0F 01 (SGDT and SIDT aliases) Sean Christopherson
  2025-08-08 17:23 ` [PATCH 3/3] *** DO NOT MERGE *** x86/umip: Lazy person's KUnit test for UMIP emulation Sean Christopherson
  2 siblings, 1 reply; 6+ messages in thread
From: Sean Christopherson @ 2025-08-08 17:23 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86
  Cc: linux-kernel, Dan Snyder, Sean Christopherson

When checking for a potential UMIP violation on #GP, verify the decoder
found at least two opcode bytes to avoid false positives when the kernel
encounters an unknown instruction that starts with 0f.  Because the array
of opcode.bytes is zero-initialized by insn_init(), peeking at bytes[1]
will misinterpret garbage as a potential SLDT or STR instruction, and can
incorrectly trigger emulation.

E.g. if a vpalignr instruction

   62 83 c5 05 0f 08 ff     vpalignr xmm17{k5},xmm23,XMMWORD PTR [r8],0xff

hits a #GP, the kernel emulates it as STR and squashes the #GP (and
corrupts the userspace code stream).

Arguably the check should look for exactly two bytes, but no three byte
opcodes use '0f 00 xx' or '0f 01 xx' as an escape, i.e. it should be
impossible to get a false positive if the first two opcode bytes match
'0f 00' or '0f 01'.  Go with a more conservative check with respect to the
existing code to minimize the chances of breaking userspace, e.g. due to
decoder weirdness.

Fixes: 1e5db223696a ("x86/umip: Add emulation code for UMIP instructions")
Reported-by: Dan Snyder <dansnyder@google.com>
Analyzed-by; Nick Bray <ncbray@google.com>
Cc: stable@vger.kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kernel/umip.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
index 5a4b21389b1d..406ac01ce16d 100644
--- a/arch/x86/kernel/umip.c
+++ b/arch/x86/kernel/umip.c
@@ -156,8 +156,8 @@ static int identify_insn(struct insn *insn)
 	if (!insn->modrm.nbytes)
 		return -EINVAL;
 
-	/* All the instructions of interest start with 0x0f. */
-	if (insn->opcode.bytes[0] != 0xf)
+	/* The instructions of interest have 2-byte opcodes: 0F 00 or 0F 01. */
+	if (insn->opcode.nbytes < 2 || insn->opcode.bytes[0] != 0xf)
 		return -EINVAL;
 
 	if (insn->opcode.bytes[1] == 0x1) {
-- 
2.50.1.703.g449372360f-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] x86/umip: Fix decoding of register forms of 0F 01 (SGDT and SIDT aliases)
  2025-08-08 17:23 [PATCH 0/3] x86/umip: Fix UMIP insn decoder false positives Sean Christopherson
  2025-08-08 17:23 ` [PATCH 1/3] x86/umip: Check that the instruction opcode is at least two bytes Sean Christopherson
@ 2025-08-08 17:23 ` Sean Christopherson
  2025-08-14 14:42   ` Peter Zijlstra
  2025-08-08 17:23 ` [PATCH 3/3] *** DO NOT MERGE *** x86/umip: Lazy person's KUnit test for UMIP emulation Sean Christopherson
  2 siblings, 1 reply; 6+ messages in thread
From: Sean Christopherson @ 2025-08-08 17:23 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86
  Cc: linux-kernel, Dan Snyder, Sean Christopherson

Filter out the register forms of 0F 01 when determining whether or not to
emulate in response to a potential UMIP violation #GP, as SGDT and SIDT
only accept memory operands.  The register variants of 0F 01 are used to
encode instructions for things like VMX and SGX, i.e. not checking the Mod
field would cause the kernel incorrectly emulate on #GP, e.g. due to a CPL
violation on VMLAUNCH.

Fixes: 1e5db223696a ("x86/umip: Add emulation code for UMIP instructions")
Cc: stable@vger.kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kernel/umip.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
index 406ac01ce16d..d432f3824f0c 100644
--- a/arch/x86/kernel/umip.c
+++ b/arch/x86/kernel/umip.c
@@ -163,8 +163,19 @@ static int identify_insn(struct insn *insn)
 	if (insn->opcode.bytes[1] == 0x1) {
 		switch (X86_MODRM_REG(insn->modrm.value)) {
 		case 0:
+			/* The reg form of 0F 01 /0 encodes VMX instructions. */
+			if (X86_MODRM_MOD(insn->modrm.value) == 3)
+				return -EINVAL;
+
 			return UMIP_INST_SGDT;
 		case 1:
+			/*
+			 * The reg form of 0F 01 /1 encodes MONITOR/MWAIT,
+			 * STAC/CLAC, and ENCLS.
+			 */
+			if (X86_MODRM_MOD(insn->modrm.value) == 3)
+				return -EINVAL;
+
 			return UMIP_INST_SIDT;
 		case 4:
 			return UMIP_INST_SMSW;
-- 
2.50.1.703.g449372360f-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] *** DO NOT MERGE *** x86/umip: Lazy person's KUnit test for UMIP emulation
  2025-08-08 17:23 [PATCH 0/3] x86/umip: Fix UMIP insn decoder false positives Sean Christopherson
  2025-08-08 17:23 ` [PATCH 1/3] x86/umip: Check that the instruction opcode is at least two bytes Sean Christopherson
  2025-08-08 17:23 ` [PATCH 2/3] x86/umip: Fix decoding of register forms of 0F 01 (SGDT and SIDT aliases) Sean Christopherson
@ 2025-08-08 17:23 ` Sean Christopherson
  2 siblings, 0 replies; 6+ messages in thread
From: Sean Christopherson @ 2025-08-08 17:23 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86
  Cc: linux-kernel, Dan Snyder, Sean Christopherson

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kernel/umip.c | 56 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
index d432f3824f0c..26621d5ea308 100644
--- a/arch/x86/kernel/umip.c
+++ b/arch/x86/kernel/umip.c
@@ -194,6 +194,62 @@ static int identify_insn(struct insn *insn)
 	}
 }
 
+static __init int umip_identify_insn(const unsigned char *buf)
+{
+	struct insn insn = {
+		.addr_bytes = 8,
+		.opnd_bytes = 4,
+	};
+	int r;
+
+	insn_init(&insn, buf, MAX_INSN_SIZE, 1);
+
+	r = insn_get_length(&insn);
+	if (r) {
+		pr_warn("insn_get_length returned '%d'\n", r);
+		return r;
+	}
+
+	return identify_insn(&insn);
+}
+
+static __init int umip_insn_test(void)
+{
+	unsigned char vpalignr[MAX_INSN_SIZE] = { 0x62, 0x83, 0xc5, 0x05, 0x0f, 0x08, 0xff };
+	unsigned char insn_0f00[MAX_INSN_SIZE] = { 0x0f, 0x00 };
+	unsigned char insn_0f01[MAX_INSN_SIZE] = { 0x0f, 0x01 };
+	int r, i;
+
+	r = umip_identify_insn(vpalignr);
+	WARN_ON(r != -EINVAL);
+
+	for (i = 0; i <= 0xff; i++) {
+		insn_0f00[2] = i;
+		r = umip_identify_insn(insn_0f00);
+		if (X86_MODRM_REG(i) > 1)
+			WARN_ON(r != -EINVAL);
+		else if (X86_MODRM_REG(i) == 0)
+			WARN_ON(r != UMIP_INST_SLDT);
+		else
+			WARN_ON(r != UMIP_INST_STR);
+
+		insn_0f01[2] = i;
+		r = umip_identify_insn(insn_0f01);
+		if (X86_MODRM_REG(i) == 2 || X86_MODRM_REG(i) == 3 || X86_MODRM_REG(i) > 4)
+			WARN_ON(r != -EINVAL);
+		else if (X86_MODRM_REG(i) < 2 && i >= 0xc0)
+			WARN_ON(r != -EINVAL);
+		else if (X86_MODRM_REG(i) == 0)
+			WARN_ON(r != UMIP_INST_SGDT);
+		else if (X86_MODRM_REG(i) == 1)
+			WARN_ON(r != UMIP_INST_SIDT);
+		else
+			WARN_ON(r != UMIP_INST_SMSW);
+	}
+	return 0;
+}
+subsys_initcall(umip_insn_test);
+
 /**
  * emulate_umip_insn() - Emulate UMIP instructions and return dummy values
  * @insn:	Instruction structure with operands
-- 
2.50.1.703.g449372360f-goog


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/3] x86/umip: Check that the instruction opcode is at least two bytes
  2025-08-08 17:23 ` [PATCH 1/3] x86/umip: Check that the instruction opcode is at least two bytes Sean Christopherson
@ 2025-08-14 14:42   ` Peter Zijlstra
  0 siblings, 0 replies; 6+ messages in thread
From: Peter Zijlstra @ 2025-08-14 14:42 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	linux-kernel, Dan Snyder

On Fri, Aug 08, 2025 at 10:23:56AM -0700, Sean Christopherson wrote:
> When checking for a potential UMIP violation on #GP, verify the decoder
> found at least two opcode bytes to avoid false positives when the kernel
> encounters an unknown instruction that starts with 0f.  Because the array
> of opcode.bytes is zero-initialized by insn_init(), peeking at bytes[1]
> will misinterpret garbage as a potential SLDT or STR instruction, and can
> incorrectly trigger emulation.
> 
> E.g. if a vpalignr instruction
> 
>    62 83 c5 05 0f 08 ff     vpalignr xmm17{k5},xmm23,XMMWORD PTR [r8],0xff
> 
> hits a #GP, the kernel emulates it as STR and squashes the #GP (and
> corrupts the userspace code stream).
> 
> Arguably the check should look for exactly two bytes, but no three byte
> opcodes use '0f 00 xx' or '0f 01 xx' as an escape, i.e. it should be
> impossible to get a false positive if the first two opcode bytes match
> '0f 00' or '0f 01'.  Go with a more conservative check with respect to the
> existing code to minimize the chances of breaking userspace, e.g. due to
> decoder weirdness.
> 
> Fixes: 1e5db223696a ("x86/umip: Add emulation code for UMIP instructions")
> Reported-by: Dan Snyder <dansnyder@google.com>
> Analyzed-by; Nick Bray <ncbray@google.com>
> Cc: stable@vger.kernel.org
> Signed-off-by: Sean Christopherson <seanjc@google.com>

Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>

> ---
>  arch/x86/kernel/umip.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
> index 5a4b21389b1d..406ac01ce16d 100644
> --- a/arch/x86/kernel/umip.c
> +++ b/arch/x86/kernel/umip.c
> @@ -156,8 +156,8 @@ static int identify_insn(struct insn *insn)
>  	if (!insn->modrm.nbytes)
>  		return -EINVAL;
>  
> -	/* All the instructions of interest start with 0x0f. */
> -	if (insn->opcode.bytes[0] != 0xf)
> +	/* The instructions of interest have 2-byte opcodes: 0F 00 or 0F 01. */
> +	if (insn->opcode.nbytes < 2 || insn->opcode.bytes[0] != 0xf)
>  		return -EINVAL;
>  
>  	if (insn->opcode.bytes[1] == 0x1) {
> -- 
> 2.50.1.703.g449372360f-goog
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/3] x86/umip: Fix decoding of register forms of 0F 01 (SGDT and SIDT aliases)
  2025-08-08 17:23 ` [PATCH 2/3] x86/umip: Fix decoding of register forms of 0F 01 (SGDT and SIDT aliases) Sean Christopherson
@ 2025-08-14 14:42   ` Peter Zijlstra
  0 siblings, 0 replies; 6+ messages in thread
From: Peter Zijlstra @ 2025-08-14 14:42 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	linux-kernel, Dan Snyder

On Fri, Aug 08, 2025 at 10:23:57AM -0700, Sean Christopherson wrote:
> Filter out the register forms of 0F 01 when determining whether or not to
> emulate in response to a potential UMIP violation #GP, as SGDT and SIDT
> only accept memory operands.  The register variants of 0F 01 are used to
> encode instructions for things like VMX and SGX, i.e. not checking the Mod
> field would cause the kernel incorrectly emulate on #GP, e.g. due to a CPL
> violation on VMLAUNCH.
> 
> Fixes: 1e5db223696a ("x86/umip: Add emulation code for UMIP instructions")
> Cc: stable@vger.kernel.org
> Signed-off-by: Sean Christopherson <seanjc@google.com>

Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>

> ---
>  arch/x86/kernel/umip.c | 11 +++++++++++
>  1 file changed, 11 insertions(+)
> 
> diff --git a/arch/x86/kernel/umip.c b/arch/x86/kernel/umip.c
> index 406ac01ce16d..d432f3824f0c 100644
> --- a/arch/x86/kernel/umip.c
> +++ b/arch/x86/kernel/umip.c
> @@ -163,8 +163,19 @@ static int identify_insn(struct insn *insn)
>  	if (insn->opcode.bytes[1] == 0x1) {
>  		switch (X86_MODRM_REG(insn->modrm.value)) {
>  		case 0:
> +			/* The reg form of 0F 01 /0 encodes VMX instructions. */
> +			if (X86_MODRM_MOD(insn->modrm.value) == 3)
> +				return -EINVAL;
> +
>  			return UMIP_INST_SGDT;
>  		case 1:
> +			/*
> +			 * The reg form of 0F 01 /1 encodes MONITOR/MWAIT,
> +			 * STAC/CLAC, and ENCLS.
> +			 */
> +			if (X86_MODRM_MOD(insn->modrm.value) == 3)
> +				return -EINVAL;
> +
>  			return UMIP_INST_SIDT;
>  		case 4:
>  			return UMIP_INST_SMSW;
> -- 
> 2.50.1.703.g449372360f-goog
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-08-14 14:42 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-08 17:23 [PATCH 0/3] x86/umip: Fix UMIP insn decoder false positives Sean Christopherson
2025-08-08 17:23 ` [PATCH 1/3] x86/umip: Check that the instruction opcode is at least two bytes Sean Christopherson
2025-08-14 14:42   ` Peter Zijlstra
2025-08-08 17:23 ` [PATCH 2/3] x86/umip: Fix decoding of register forms of 0F 01 (SGDT and SIDT aliases) Sean Christopherson
2025-08-14 14:42   ` Peter Zijlstra
2025-08-08 17:23 ` [PATCH 3/3] *** DO NOT MERGE *** x86/umip: Lazy person's KUnit test for UMIP emulation Sean Christopherson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).