Re: [PATCH v15 18/41] KVM: x86: Don't emulate instructions affected by CET features

From: Sean Christopherson

Date: Wed Sep 17 2025 - 17:15:37 EST


On Wed, Sep 17, 2025, Chao Gao wrote:
> On Fri, Sep 12, 2025 at 04:22:56PM -0700, Sean Christopherson wrote:
> >From: Yang Weijiang <weijiang.yang@xxxxxxxxx>
> >
> >Don't emulate branch instructions, e.g. CALL/RET/JMP etc., that are
> >affected by Shadow Stacks and/or Indirect Branch Tracking when said
> >features are enabled in the guest, as fully emulating CET would require
> >significant complexity for no practical benefit (KVM shouldn't need to
> >emulate branch instructions on modern hosts). Simply doing nothing isn't
> >an option as that would allow a malicious entity to subvert CET
> >protections via the emulator.
> >
> >Note! On far transfers, do NOT consult the current privilege level and
> >instead treat SHSTK/IBT as being enabled if they're enabled for User *or*
> >Supervisor mode. On inter-privilege level far transfers, SHSTK and IBT
> >can be in play for the target privilege level, i.e. checking the current
> >privilege could get a false negative, and KVM doesn't know the target
> >privilege level until emulation gets under way.
>
> I modified KUT's cet.c to verify that near jumps, near returns, and far
> transfers (e.g., IRET) trigger the emulation failure logic added by this
> patch when guests enable Shadow Stack or IBT.
>
> I found only one minor issue: near return instructions were not tagged with
> ShadowStack.

Heh, I had just found this through inspection.

> The following diff fixes this issue:
>
> diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
> index e4be54a677b0..b1c9816bd5c6 100644
> --- a/arch/x86/kvm/emulate.c
> +++ b/arch/x86/kvm/emulate.c
> @@ -4326,8 +4326,8 @@ static const struct opcode opcode_table[256] = {
> X8(I(DstReg | SrcImm64 | Mov, em_mov)),
> /* 0xC0 - 0xC7 */
> G(ByteOp | Src2ImmByte, group2), G(Src2ImmByte, group2),
> - I(ImplicitOps | NearBranch | SrcImmU16 | IsBranch, em_ret_near_imm),
> - I(ImplicitOps | NearBranch | IsBranch, em_ret),
> + I(ImplicitOps | NearBranch | SrcImmU16 | IsBranch | ShadowStack, em_ret_near_imm),
> + I(ImplicitOps | NearBranch | IsBranch | ShadowStack, em_ret),

Tangentially directly related to this bug, I think we should manual annotation
where possible. I don't see an easy way to do that for ShadowStack, but for IBT
we can use IsBranch, NearBranch and the SrcXXX operance to detect IBT-affected
instructions. It's obviously more complex, but programmatically detecting
indirect branches should be less error prone. I'll do so in the next version.

> I(DstReg | SrcMemFAddr | ModRM | No64 | Src2ES, em_lseg),
> I(DstReg | SrcMemFAddr | ModRM | No64 | Src2DS, em_lseg),
> G(ByteOp, group11), G(0, group11),
>
>
> And for reference, below are the changes I made to KUT's cet.c

I now have a more comprehensive set of testcases, and it can be upstreamed
(relies on KVM's default behavior of injecting #UD at CPL==3 on failed emulation).