| From 32278c677947ae2f042c9535674a7fff9a245dd3 Mon Sep 17 00:00:00 2001 |
| From: Sean Christopherson <seanjc@google.com> |
| Date: Fri, 8 Aug 2025 10:23:56 -0700 |
| Subject: x86/umip: Check that the instruction opcode is at least two bytes |
| |
| From: Sean Christopherson <seanjc@google.com> |
| |
| commit 32278c677947ae2f042c9535674a7fff9a245dd3 upstream. |
| |
| 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. |
| |
| Analyzed by Nick Bray <ncbray@google.com>. |
| |
| Fixes: 1e5db223696a ("x86/umip: Add emulation code for UMIP instructions") |
| Reported-by: Dan Snyder <dansnyder@google.com> |
| Signed-off-by: Sean Christopherson <seanjc@google.com> |
| Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> |
| Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/x86/kernel/umip.c | 4 ++-- |
| 1 file changed, 2 insertions(+), 2 deletions(-) |
| |
| --- a/arch/x86/kernel/umip.c |
| +++ b/arch/x86/kernel/umip.c |
| @@ -156,8 +156,8 @@ static int identify_insn(struct insn *in |
| 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) { |