From: Sean Christopherson <seanjc@google.com>
To: Mathias Krause <minipli@grsecurity.net>
Cc: kvm@vger.kernel.org, Chao Gao <chao.gao@intel.com>,
Paolo Bonzini <pbonzini@redhat.com>
Subject: Re: [kvm-unit-tests PATCH v3 17/17] x86/cet: Add testcases to verify KVM rejects emulation of CET instructions
Date: Fri, 14 Nov 2025 12:42:58 -0800 [thread overview]
Message-ID: <aReUUkuMjnlYKzTR@google.com> (raw)
In-Reply-To: <fc886a22-49f3-4627-8ba6-933099e7640d@grsecurity.net>
On Fri, Nov 14, 2025, Mathias Krause wrote:
> On 14.11.25 01:12, Sean Christopherson wrote:
> > +#define SHSTK_TEST_UNSUPPORTED_INSTRUCTION(insn) \
> > +do { \
> > + uint8_t vector = __CET_TEST_UNSUPPORTED_INSTRUCTION(insn); \
> > + \
> > + report(vector == UD_VECTOR, "Wanted #UD on %s, got %s", \
> > + insn, exception_mnemonic(vector)); \
> > +} while (0)
> > +
> > +/*
> > + * Treat IRET as unsupported with IBT even though the minimal interactions with
> > + * IBT _could_ be easily emulated by KVM, as KVM doesn't support emulating IRET
> > + * outside of Real Mode.
> > + */
> > +#define CET_TEST_UNSUPPORTED_INSTRUCTIONS(CET) \
> > +do { \
> > + CET##_TEST_UNSUPPORTED_INSTRUCTION("callq *%%rax"); \
> > + CET##_TEST_UNSUPPORTED_INSTRUCTION("lcall *%0"); \
>
> Maybe spell out that this is a 32bit far call ("lcalll *%0"), as only
> these are supported on AMD *and* Intel? (Intel can do 64bit ones too.)
>
> > + CET##_TEST_UNSUPPORTED_INSTRUCTION("syscall"); \
> > + CET##_TEST_UNSUPPORTED_INSTRUCTION("sysenter"); \
> > + CET##_TEST_UNSUPPORTED_INSTRUCTION("iretq"); \
>
> These, if emulated, would be rather disastrous to the test. :D
> - SYSCALL messes badly with the register state.
> - SYSENTER should #UD in long mode on AMD anyway but Intel allows it.
> However, it'll likely #GP early, prior to messing with register state,
> as IA32_SYSENTER_CS doesn't get initialized as the CPU expects it to
> be.
> - The stack frame for IRETQ isn't properly set up (lacks CS, RFLAGS, SS,
> RSP) but even if it'll #GP early for the non-canonical address, it'll
> have modified RSP, making the recovery "pop %rax" pop an unrelated
> stack word, likely making the code crash soon after.
>
> So these instructions really *rely* on KVM to #UD on these early.
> Dunno, but it feels like the tests should be made more robust wrt.
> possible future emulation by KVM?
I don't necessarily disagree, but I'm not convinced it'd be worth the effort to
properly set everything up _and_ keep it functional. Unless KVM is buggy, the
fallback will be completely untested. I hate user-hostile tests as much as the
next person (probably more), but for this one my vote is to punt and cross our
fingers that KVM never screws up.
> > +} while (0)
> > +
> > +static uint64_t cet_shstk_emulation(void)
> > +{
> > + CET_TEST_UNSUPPORTED_INSTRUCTIONS(SHSTK);
> > +
> > + SHSTK_TEST_UNSUPPORTED_INSTRUCTION("call 1f");
> > + SHSTK_TEST_UNSUPPORTED_INSTRUCTION("retq");
> > + SHSTK_TEST_UNSUPPORTED_INSTRUCTION("retq $10");
> > + SHSTK_TEST_UNSUPPORTED_INSTRUCTION("lretq");
> > + SHSTK_TEST_UNSUPPORTED_INSTRUCTION("lretq $10");
> > +
> > + /* Do a handful of JMPs to verify they aren't impacted by SHSTK. */
> > + asm volatile(KVM_FEP "jmp 1f\n\t"
> > + "1:\n\t"
> > + KVM_FEP "lea 2f(%%rip), %%rax\n\t"
> > + KVM_FEP "jmp *%%rax\n\t"
> ^^^^^^^^^^
> Fortunately, this indirect branch runs only with shadow stack enabled,
> no IBT. If it would, than below misses an ENDBR64.
50/50 as to whether I noticed that or just got lucky :-)
> vvv
> > + "2:\n\t"> + KVM_FEP "push $" xstr(USER_CS) "\n\t"
> > + KVM_FEP "lea 3f(%%rip), %%rax\n\t"
> > + KVM_FEP "push %%rax\n\t"
> > + /* Manually encode ljmpq, which gas doesn't recognize :-( */
>
> LJMPQ is actually only supported on Intel systems. AMD doesn't support
> it and one has to emulate it via "push $cs; push $rip; lretq".
Well that explains why gas wasn't being very nice to me. I'll tweak the comment.
> Dunno if it's a feature or bug of KVM to emulate LJMPQ fine on AMD -- if it
> does, that is!
Bug? Sort of? KVM won't emulate it on #UD, so it's only very limited emulation.
E.g. the guest would have to force emulation, put the jump source in emulated MMIO,
or play TLB games to get KVM to emulate
> > + KVM_FEP ".byte 0x48\n\t"
> > + "ljmpl *(%%rsp)\n\t"
> > + "3:\n\t"
> > + KVM_FEP "pop %%rax\n\t"
> > + KVM_FEP "pop %%rax\n\t"
> > + ::: "eax");
> > +
> > + return 0;
> > +}
> > +
>
> > +/*
> > + * Don't invoke printf() or report() in the IBT testcase, as it will likely
> > + * generate an indirect branch without an endbr64 annotation and thus #CP.
>
> Uhm, x86/Makefile.x86_64 has this:
>
> fcf_protection_full := $(call cc-option, -fcf-protection=full,)
> COMMON_CFLAGS += -mno-red-zone -mno-sse -mno-sse2 $(fcf_protection_full)
> [...]
> ifneq ($(fcf_protection_full),)
> tests += $(TEST_DIR)/cet.$(exe)
> endif
>
> So all code that needs it should have a leading ENDBR64 or the cet test
> wouldn't be part of the test suite.
>
> However, I also notice it isn't working...
>
> [digging in, hours later...]
>
> After tweaking the exception handling to be able to tweak the IBT
> tracker state (which would be stuck at WAIT_FOR_ENDBRANCH for a missing
> ENDBR64), it still wouldn't work for me. Bummer! Further instrumentation
> showed, the code triggered within exception_mnemonic() which caused only
> more question marks -- it's just a simple switch, right? Though, looking
> at the disassembly made it crystal clear:
>
> 000000000040707c <exception_mnemonic>:
> 40707c: endbr64
> 407080: cmp $0x1e,%edi
> 407083: ja 407117 <exception_mnemonic+0x9b>
> 407089: mov %edi,%edi
> 40708b: notrack jmp *0x4107e0(,%rdi,8)
> ::
> 4070b1: mov $0x411c7c,%eax # <-- #CP(3) here
>
> The switch block caused gcc to emit a jump table and an indirect jump
> for it (to 40708b). The jump is prefixed with 'notrack' to keep the size
> of the jump target stubs small, allowing for omitting the ENDBR64
> instruction. However, the IBT test code doesn't enable that in the MSR,
> leading the CPU to raising a #CP(3) for this indirect jump. D'oh!
>
> So we can either disable the generation of jump tables in gcc
> (-fno-jump-tables) or just allow the 'notrack' handling. I guess the
> latter is easier.
And much more helpful for people like me, who know just enough about IBT to be
dangerous, but don't fully understand how everything works the end-to-end.
> Progress, but still something to fix, namely the "ljmp *%0" which is,
> apparently, successfully branching to address 0 and then triggering a
> #GP(0). But maybe that's just because I'm on an old version of the KVM
> CET series, haven't build v6.18-rcX yet.
I suspect it's your old version, it passes on my end.
> Anyhow, one less puzzle, so should be good?
Ya, I'll slot this in for the next version (instead of having it be a fixup).
prev parent reply other threads:[~2025-11-14 20:43 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-14 0:12 [kvm-unit-tests PATCH v3 00/17] x86: Improve CET tests Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 01/17] x86/run_in_user: Add an "end branch" marker on the user_mode destination Sean Christopherson
2025-11-14 15:51 ` Mathias Krause
2025-11-14 20:46 ` Sean Christopherson
2025-11-15 5:09 ` Mathias Krause
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 02/17] x86: cet: Pass virtual addresses to invlpg Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 03/17] x86: cet: Remove unnecessary memory zeroing for shadow stack Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 04/17] x86: cet: Directly check for #CP exception in run_in_user() Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 05/17] x86: cet: Validate #CP error code Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 06/17] x86: cet: Use report_skip() Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 07/17] x86: cet: Drop unnecessary casting Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 08/17] x86: cet: Validate writing unaligned values to SSP MSR causes #GP Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 09/17] x86: cet: Validate CET states during VMX transitions Sean Christopherson
2025-11-14 8:10 ` Mathias Krause
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 10/17] x86: cet: Make shadow stack less fragile Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 11/17] x86: cet: Simplify IBT test Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 12/17] x86: cet: Use symbolic values for the #CP error codes Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 13/17] x86: cet: Test far returns too Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 14/17] x86: Avoid top-most page for vmalloc on x86-64 Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 15/17] x86/cet: Run SHSTK and IBT tests as appropriate if either feature is supported Sean Christopherson
2025-11-14 8:59 ` Mathias Krause
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 16/17] x86/cet: Drop the "intel_" prefix from the CET testcase Sean Christopherson
2025-11-14 0:12 ` [kvm-unit-tests PATCH v3 17/17] x86/cet: Add testcases to verify KVM rejects emulation of CET instructions Sean Christopherson
2025-11-14 15:19 ` Mathias Krause
2025-11-14 20:42 ` Sean Christopherson [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aReUUkuMjnlYKzTR@google.com \
--to=seanjc@google.com \
--cc=chao.gao@intel.com \
--cc=kvm@vger.kernel.org \
--cc=minipli@grsecurity.net \
--cc=pbonzini@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.