Re: [PATCH v2] nSVM: Check injected event consistency

Teddy Astie <[email protected]> Tue, 28 Jul 2026 16:04:20 +0200
Newsgroups gmane.comp.emulators.xen.devel
Message-ID <1785247466.8631fc262581453bbf619ec5b2062170.19fa90a823a000e099@vates.tech>
Le 16/07/2026 à 17:41, Abdelkareem Abdelsaamad a écrit :
> On the AMD platforms, allowing a VMRUN instruction with a malformed VMCB has
> debugging complications, security and performance implications. The APM volume
> 2 15.20 [1] states two possibilities that result in a VMRUN exit with
> VMEXIT_INVALID due to injected events. These are either
> • Reserved values of TYPE have been specified.
> • TYPE = 3 (exception) has been specified with a vector that does not
>    correspond to an exception (this includes vector 2, which is an NMI, not
>    an exception).
> Extend the VMCB checks to check for such inconsistency.
> 
> The collection of the invalid exception vectors are picked from the upstream KVM
> commit ("7e79f71bca5c" KVM: nSVM: Add missing consistency check for EVENTINJ).
> 
> [1] https://docs.amd.com/v/u/en-US/24593_3.44_APM_Vol2
> 
> Signed-off-by: Abdelkareem Abdelsaamad <[email protected]>
> ---
> Changes in v2:
> - Remove the redundant SVM_EVENT_INJ_TYPE_MASK and SVM_EVENT_INJ_VEC_MASK
>    constants.
> - Correct the Injected Event Type consistency check to disallow the injection
>    of reserved type 1 events.
> ---
> Testing:
>   - Using a locally developed XTF nested virt setup, I manually tested VMRUN
>     instruction handling with a malformed VMCB:
>     1) Inject event with the type (7).
>        The hypervisor logs show the message
>        (XEN) [  645.155609] d2v0[nsvm_vmcb_prepare4vmrun]: eventinj: Invalid Injected
>              Event Type: (0x7)
>     2) Inject event with the exception value (3) and the vector value (2) for NMI.
>        The hypervisor logs show the message
>        (XEN) [  645.157277] d2v0[nsvm_vmcb_prepare4vmrun]: eventinj: Invalid Injected Event.
>               Exception type: (0x3), with a vector: (0x2) does not belong to an exception
> 
>   - CI tests:
> https://gitlab.com/xen-project/people/aabdelsa/xen/-/pipelines/2682300446
> ---
>   xen/arch/x86/hvm/svm/vmcb.c | 40 +++++++++++++++++++++++++++++++++++++
>   1 file changed, 40 insertions(+)
> 
> diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c
> index 975a1eaef8..c31d2a6f58 100644
> --- a/xen/arch/x86/hvm/svm/vmcb.c
> +++ b/xen/arch/x86/hvm/svm/vmcb.c
> @@ -320,6 +320,31 @@ void svm_vmcb_dump(const char *from, const struct vmcb_struct *vmcb)
>       svm_dump_sel("  TR", &vmcb->tr);
>   }
>   
> +static bool is_valid_svm_vmcb_injected_exception_vector(
> +    const struct vmcb_struct *vmcb, uint8_t vmcb_injected_vector)
> +{
> +    return ( (vmcb_injected_vector == X86_EXC_DE) ||
> +             (vmcb_injected_vector == X86_EXC_DB) ||
> +             (vmcb_injected_vector == X86_EXC_BP) ||
> +             (vmcb_injected_vector == X86_EXC_OF) ||
> +             (vmcb_injected_vector == X86_EXC_BR) ||

This particular exception is special. AMD APM states that this event is 
"impossible" if the guest is in 64-bit mode and will cause 
VMEXIT_INVALID in such case.

 > If the VMM attempts to inject an event that is impossible for the 
guest mode (e.g., a #BR exception when the guest is in 64-bit mode), the 
event injection will fail and no guest state instructions will be 
executed; VMRUN will immediately exit with an error code of VMEXIT_INVALID.

So this one likely want a additional check for hvm_guest_x86_mode() != 
X86_MODE_64BIT.

It looks like #OF has the same quirk (invalid in 64-bits mode).

Though I don't know if any other exception has a similar behavior though.

> +             (vmcb_injected_vector == X86_EXC_UD) ||
> +             (vmcb_injected_vector == X86_EXC_NM) ||
> +             (vmcb_injected_vector == X86_EXC_DF) ||
> +             (vmcb_injected_vector == X86_EXC_TS) ||
> +             (vmcb_injected_vector == X86_EXC_NP) ||
> +             (vmcb_injected_vector == X86_EXC_SS) ||
> +             (vmcb_injected_vector == X86_EXC_GP) ||
> +             (vmcb_injected_vector == X86_EXC_PF) ||
> +             (vmcb_injected_vector == X86_EXC_MF) ||
> +             (vmcb_injected_vector == X86_EXC_AC) ||
> +             (vmcb_injected_vector == X86_EXC_MC) ||
> +             (vmcb_injected_vector == X86_EXC_XM) ||
> +             (vmcb_injected_vector == X86_EXC_HV) ||
> +             (vmcb_injected_vector == X86_EXC_SX) ||
> +             (vmcb_get_sev_es(vmcb) && vmcb_injected_vector == X86_EXC_VC) );
> +}

I think using a switch here would help making things more readable, 
especially if we need to add additional comparisons in specific cases 
(SEV-ES for #VC, !64-bits for #BR and #OF, ...).

I have in mind something like

   switch (vmcb_injected_vector)
   {
   case X86_EXC_OF:
   case X86_EXC_BR:
       return hvm_guest_x86_mode(v) != X86_MODE_64BIT;

   (all other special cases, ...)

   case X86_EXC_UD:
   (all other simple cases ...)
       return true;

   default:
       return false;
   }

> +
>   bool svm_vmcb_isvalid(
>       const char *from, const struct vmcb_struct *vmcb, const struct vcpu *v,
>       bool verbose)
> @@ -330,6 +355,12 @@ bool svm_vmcb_isvalid(
>       unsigned long cr4 = vmcb_get_cr4(vmcb);
>       unsigned long valid;
>       uint64_t efer = vmcb_get_efer(vmcb);
> +    uint8_t vmcb_injected_type = vmcb->event_inj.type;
> +    uint8_t vmcb_injected_vector = vmcb->event_inj.vector;
> +    uint8_t vmcb_valid_event_inj_types_mask = (1 << X86_ET_EXT_INTR) |
> +                                              (1 << X86_ET_NMI) |
> +                                              (1 << X86_ET_HW_EXC) |
> +                                              (1 << X86_ET_SW_INT);
>   
>   #define PRINTF(fmt, args...) do { \
>       if ( !verbose ) return true; \
> @@ -392,6 +423,15 @@ bool svm_vmcb_isvalid(
>           PRINTF("eventinj: MBZ bits are set (%#"PRIx64")\n",
>                  vmcb->event_inj.raw);
>   
> +    if ( !((1 << vmcb_injected_type) & vmcb_valid_event_inj_types_mask) )
> +        PRINTF("eventinj: Invalid Injected Event Type: (%#"PRIx8")\n",
> +               vmcb_injected_type);
> +
> +    if ( (vmcb_injected_type == X86_ET_HW_EXC) &&
> +         !is_valid_svm_vmcb_injected_exception_vector(vmcb, vmcb_injected_vector) )
> +        PRINTF("eventinj: Invalid Injected Event. Exception type: (%#"PRIx8"),"
> +               " with a vector: (%#"PRIx8") does not belong to an exception\n",
> +               vmcb_injected_type, vmcb_injected_vector);
>   #undef PRINTF
>       return ret;
>   }

Teddy
OpenPGP_0x660FA9D102CBCFD0.asc (application/pgp-keys, 2.4 KB)
-----BEGIN PGP PUBLIC KEY BLOCK-----

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
x9fhaZEsniw8/bYgC3igkk5YJiOa
=lUIA
-----END PGP PUBLIC KEY BLOCK-----
OpenPGP_signature.asc (application/pgp-signature, 665 B)
-----BEGIN PGP SIGNATURE-----

wsD5BAABCAAjFiEEGAIew9LzHY3pdrqtZg+p0QLLz9AFAmpotuQFAwAAAAAACgkQZg+p0QLLz9AC
KQv9FJ2Q7sxUL7m4nQYo7tErfaVWpbjIJTTA7IWbdbAoyHLeQxvVI3v/Rt1GMRzfrXW8FJRMkcQ0
CoCQB+J4JCO443hgPMcJW7clP7Kh9AU8zCIgtXtkn+n0+Swd+4mfnNcRWTbhjXbH5vHbzUjk2Xll
9Iao9zHc9j3ZSoFhYDhNvSQlLeTMgXmTKKr8eJa8EcVaO41X2sdiZriIc0tQuFwa6SBrAGzQ1eNx
JDfIkFn9nZ8Ra/hwiF+K9AYvTXEg4Bu8l1TfZi7HWjIGpK7NYwkHyKdBXb2MO0Llrhnnhn/+NVjA
WPvjSO9A1Wc2awNbrhEoXzAPVU7x9lnPnyb/MjqXuJsCk/n37QbuM9BrOfO+tA8RwjAa0/YwB3PK
esTXvXBc1D+8YvpeYYq78URznrwIfst2JGHvVGSODmfEgEvVLsP3tVdiNcOomK6Bvp/ImH1pz+I1
dpCd1976vs+5QQA6TZutMQI2YvSYwGmh2famAbnL3TcapgBhLNwad9HlSH/+
=wUoG
-----END PGP SIGNATURE-----