kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] KVM: arm64: Clear pending exception state before injecting a new one
@ 2025-07-14 14:46 Marc Zyngier
  2025-07-15  6:51 ` Oliver Upton
  0 siblings, 1 reply; 3+ messages in thread
From: Marc Zyngier @ 2025-07-14 14:46 UTC (permalink / raw)
  To: kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	syzbot+4e09b1432de3774b86ae

Repeatedly injecting an exception from userspace without running
the vcpu between calls results in a nasty warning, as we're not
really keen on losing already pending exceptions.

But this precaution doesn't really apply to userspace, who can
do whatever it wants (within reason). So let's simply clear any
previous exception state before injecting a new one.

Note that this is done unconditionally, even if the injection
ultimately fails.

Reported-by: syzbot+4e09b1432de3774b86ae@syzkaller.appspotmail.com
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 arch/arm64/kvm/guest.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index e2702718d56d2..ac6b26e25e191 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -843,6 +843,8 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
 	u64 esr = events->exception.serror_esr;
 	int ret = 0;
 
+	vcpu_clear_flag(vcpu, EXCEPT_MASK);
+
 	if (ext_dabt_pending)
 		ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
 
-- 
2.39.2


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

* Re: [PATCH] KVM: arm64: Clear pending exception state before injecting a new one
  2025-07-14 14:46 [PATCH] KVM: arm64: Clear pending exception state before injecting a new one Marc Zyngier
@ 2025-07-15  6:51 ` Oliver Upton
  2025-07-15  8:31   ` Marc Zyngier
  0 siblings, 1 reply; 3+ messages in thread
From: Oliver Upton @ 2025-07-15  6:51 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, syzbot+4e09b1432de3774b86ae

Hey,

On Mon, Jul 14, 2025 at 03:46:36PM +0100, Marc Zyngier wrote:
> Repeatedly injecting an exception from userspace without running
> the vcpu between calls results in a nasty warning, as we're not
> really keen on losing already pending exceptions.
> 
> But this precaution doesn't really apply to userspace, who can
> do whatever it wants (within reason). So let's simply clear any
> previous exception state before injecting a new one.
> 
> Note that this is done unconditionally, even if the injection
> ultimately fails.
> 
> Reported-by: syzbot+4e09b1432de3774b86ae@syzkaller.appspotmail.com
> Signed-off-by: Marc Zyngier <maz@kernel.org>

Thanks for taking a look at this. I think the correct fix is a bit more
involved, as:

 - ABI prior to my patches allowed dumb things like injecting both an
   SEA and SError from the same ioctl. With your patch I think you could
   still get the warning to fire with serror_pending && ext_dabt_pending

 - KVM_GET_VCPU_EVENTS is broken for 'pending' SEAs, as we assume
   they're committed in the vCPU state immediately when they're actually
   deferred to the next KVM_RUN.

I thoroughly hate the fix I have but it should address both of these
issues. Although the pending PC adjustment flags seem more like a
liability than anything else if ioctls need to flush them before
returning to userspace. Might look at a larger cleanup down the road.

Thanks,
Oliver

From 149262689dfe881542f5c5b60f9ee308a00f0596 Mon Sep 17 00:00:00 2001
From: Oliver Upton <oliver.upton@linux.dev>
Date: Mon, 14 Jul 2025 23:25:07 -0700
Subject: [PATCH] KVM: arm64: Commit exceptions from KVM_SET_VCPU_EVENTS
 immediately

syzkaller has found that it can trip a warning in KVM's exception
emulation infrastructure by repeatedly injecting exceptions into the
guest.

While it's unlikely that a reasonable VMM will do this, further
investigation of the issue reveals that KVM can potentially discard the
"pending" SEA state. While the handling of KVM_GET_VCPU_EVENTS presumes
that userspace-injected SEAs are realized immediately, in reality the
emulated exception entry is deferred until the next call to KVM_RUN.

Hack-a-fix the immediate issues by committing the pending exceptions to
the vCPU's architectural state immediately in KVM_SET_VCPU_EVENTS. This
is no different to the way KVM-injected exceptions are handled in
KVM_RUN where we potentially call __kvm_adjust_pc() before returning to
userspace.

Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
---
 arch/arm64/kvm/guest.c | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index e2702718d56d..16ba5e9ac86c 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -834,6 +834,19 @@ int __kvm_arm_vcpu_get_events(struct kvm_vcpu *vcpu,
 	return 0;
 }
 
+static void commit_pending_events(struct kvm_vcpu *vcpu)
+{
+	if (!vcpu_get_flag(vcpu, PENDING_EXCEPTION))
+		return;
+
+	/*
+	 * Reset the MMIO emulation state to avoid stepping PC after emulating
+	 * the exception entry.
+	 */
+	vcpu->mmio_needed = false;
+	kvm_call_hyp(__kvm_adjust_pc, vcpu);
+}
+
 int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
 			      struct kvm_vcpu_events *events)
 {
@@ -843,8 +856,15 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
 	u64 esr = events->exception.serror_esr;
 	int ret = 0;
 
-	if (ext_dabt_pending)
+	/*
+	 * Immediately commit the pending SEA to the vCPU's architectural
+	 * state which is necessary since we do not return a pending SEA
+	 * to userspace via KVM_GET_VCPU_EVENTS.
+	 */
+	if (ext_dabt_pending) {
 		ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
+		commit_pending_events(vcpu);
+	}
 
 	if (ret < 0)
 		return ret;
@@ -863,6 +883,12 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
 	else
 		ret = kvm_inject_serror(vcpu);
 
+	/*
+	 * We could've decided that the SError is due for immediate software
+	 * injection; commit the exception in case userspace decides it wants
+	 * to inject more exceptions for some strange reason.
+	 */
+	commit_pending_events(vcpu);
 	return (ret < 0) ? ret : 0;
 }
 
-- 
2.39.5

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

* Re: [PATCH] KVM: arm64: Clear pending exception state before injecting a new one
  2025-07-15  6:51 ` Oliver Upton
@ 2025-07-15  8:31   ` Marc Zyngier
  0 siblings, 0 replies; 3+ messages in thread
From: Marc Zyngier @ 2025-07-15  8:31 UTC (permalink / raw)
  To: Oliver Upton
  Cc: kvmarm, linux-arm-kernel, kvm, Joey Gouly, Suzuki K Poulose,
	Zenghui Yu, syzbot+4e09b1432de3774b86ae

On Tue, 15 Jul 2025 07:51:09 +0100,
Oliver Upton <oliver.upton@linux.dev> wrote:
> 
> Hey,
> 
> On Mon, Jul 14, 2025 at 03:46:36PM +0100, Marc Zyngier wrote:
> > Repeatedly injecting an exception from userspace without running
> > the vcpu between calls results in a nasty warning, as we're not
> > really keen on losing already pending exceptions.
> > 
> > But this precaution doesn't really apply to userspace, who can
> > do whatever it wants (within reason). So let's simply clear any
> > previous exception state before injecting a new one.
> > 
> > Note that this is done unconditionally, even if the injection
> > ultimately fails.
> > 
> > Reported-by: syzbot+4e09b1432de3774b86ae@syzkaller.appspotmail.com
> > Signed-off-by: Marc Zyngier <maz@kernel.org>
> 
> Thanks for taking a look at this. I think the correct fix is a bit more
> involved, as:
> 
>  - ABI prior to my patches allowed dumb things like injecting both an
>    SEA and SError from the same ioctl. With your patch I think you could
>    still get the warning to fire with serror_pending && ext_dabt_pending
> 
>  - KVM_GET_VCPU_EVENTS is broken for 'pending' SEAs, as we assume
>    they're committed in the vCPU state immediately when they're actually
>    deferred to the next KVM_RUN.
> 
> I thoroughly hate the fix I have but it should address both of these
> issues. Although the pending PC adjustment flags seem more like a
> liability than anything else if ioctls need to flush them before
> returning to userspace. Might look at a larger cleanup down the road.
> 
> Thanks,
> Oliver
> 
> From 149262689dfe881542f5c5b60f9ee308a00f0596 Mon Sep 17 00:00:00 2001
> From: Oliver Upton <oliver.upton@linux.dev>
> Date: Mon, 14 Jul 2025 23:25:07 -0700
> Subject: [PATCH] KVM: arm64: Commit exceptions from KVM_SET_VCPU_EVENTS
>  immediately
> 
> syzkaller has found that it can trip a warning in KVM's exception
> emulation infrastructure by repeatedly injecting exceptions into the
> guest.
> 
> While it's unlikely that a reasonable VMM will do this, further
> investigation of the issue reveals that KVM can potentially discard the
> "pending" SEA state. While the handling of KVM_GET_VCPU_EVENTS presumes
> that userspace-injected SEAs are realized immediately, in reality the
> emulated exception entry is deferred until the next call to KVM_RUN.
> 
> Hack-a-fix the immediate issues by committing the pending exceptions to
> the vCPU's architectural state immediately in KVM_SET_VCPU_EVENTS. This
> is no different to the way KVM-injected exceptions are handled in
> KVM_RUN where we potentially call __kvm_adjust_pc() before returning to
> userspace.
> 
> Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
> ---
>  arch/arm64/kvm/guest.c | 28 +++++++++++++++++++++++++++-
>  1 file changed, 27 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
> index e2702718d56d..16ba5e9ac86c 100644
> --- a/arch/arm64/kvm/guest.c
> +++ b/arch/arm64/kvm/guest.c
> @@ -834,6 +834,19 @@ int __kvm_arm_vcpu_get_events(struct kvm_vcpu *vcpu,
>  	return 0;
>  }
>  
> +static void commit_pending_events(struct kvm_vcpu *vcpu)
> +{
> +	if (!vcpu_get_flag(vcpu, PENDING_EXCEPTION))
> +		return;
> +
> +	/*
> +	 * Reset the MMIO emulation state to avoid stepping PC after emulating
> +	 * the exception entry.
> +	 */
> +	vcpu->mmio_needed = false;
> +	kvm_call_hyp(__kvm_adjust_pc, vcpu);
> +}
> +
>  int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
>  			      struct kvm_vcpu_events *events)
>  {
> @@ -843,8 +856,15 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
>  	u64 esr = events->exception.serror_esr;
>  	int ret = 0;
>  
> -	if (ext_dabt_pending)
> +	/*
> +	 * Immediately commit the pending SEA to the vCPU's architectural
> +	 * state which is necessary since we do not return a pending SEA
> +	 * to userspace via KVM_GET_VCPU_EVENTS.
> +	 */
> +	if (ext_dabt_pending) {
>  		ret = kvm_inject_sea_dabt(vcpu, kvm_vcpu_get_hfar(vcpu));
> +		commit_pending_events(vcpu);
> +	}
>  
>  	if (ret < 0)
>  		return ret;
> @@ -863,6 +883,12 @@ int __kvm_arm_vcpu_set_events(struct kvm_vcpu *vcpu,
>  	else
>  		ret = kvm_inject_serror(vcpu);
>  
> +	/*
> +	 * We could've decided that the SError is due for immediate software
> +	 * injection; commit the exception in case userspace decides it wants
> +	 * to inject more exceptions for some strange reason.
> +	 */
> +	commit_pending_events(vcpu);
>  	return (ret < 0) ? ret : 0;
>  }

Yup, this seems looks reasonable. I would have liked the commit helper
to be used just before vcpu_put(), but obviously, that's not the right
thing to do because of the mmio_needed field.

Reviewed-by: Marc Zyngier <maz@kernel.org>

	M.

-- 
Without deviation from the norm, progress is not possible.

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

end of thread, other threads:[~2025-07-15  8:31 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-14 14:46 [PATCH] KVM: arm64: Clear pending exception state before injecting a new one Marc Zyngier
2025-07-15  6:51 ` Oliver Upton
2025-07-15  8:31   ` Marc Zyngier

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).