linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] KVM: arm64: Make vcpu flag updates non-preemptible
@ 2023-04-17  9:36 Marc Zyngier
  2023-04-17 11:40 ` Will Deacon
  0 siblings, 1 reply; 3+ messages in thread
From: Marc Zyngier @ 2023-04-17  9:36 UTC (permalink / raw)
  To: kvmarm, kvm, linux-arm-kernel
  Cc: James Morse, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Will Deacon, Catalin Marinas, Quentin Perret, Mostafa Saleh,
	stable

Per-vcpu flags are updated using a non-atomic RMW operation.
Which means it is possible to get preempted between the read and
write operations.

Another interesting thing to note is that preemption also updates
flags, as we have some flag manipulation in both the load and put
operations.

It is thus possible to lose information communicated by either
load or put, as the preempted flag update will overwrite the flags
when the thread is resumed. This is specially critical if either
load or put has stored information which depends on the physical
CPU the vcpu runs on.

This results in really elusive bugs, and kudos must be given to
Mostafa for the long hours of debugging, and finally spotting
the problem.

Fixes: e87abb73e594 ("KVM: arm64: Add helpers to manipulate vcpu flags among a set")
Reported-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Cc: stable@vger.kernel.org
---
 arch/arm64/include/asm/kvm_host.h | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index bcd774d74f34..d716cfd806e8 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -579,6 +579,19 @@ struct kvm_vcpu_arch {
 		v->arch.flagset & (m);				\
 	})
 
+/*
+ * Note that the set/clear accessors must be preempt-safe in order to
+ * avoid nesting them with load/put which also manipulate flags...
+ */
+#ifdef __KVM_NVHE_HYPERVISOR__
+/* the nVHE hypervisor is always non-preemptible */
+#define __vcpu_flags_preempt_disable()
+#define __vcpu_flags_preempt_enable()
+#else
+#define __vcpu_flags_preempt_disable()	preempt_disable()
+#define __vcpu_flags_preempt_enable()	preempt_enable()
+#endif
+
 #define __vcpu_set_flag(v, flagset, f, m)			\
 	do {							\
 		typeof(v->arch.flagset) *fset;			\
@@ -586,9 +599,11 @@ struct kvm_vcpu_arch {
 		__build_check_flag(v, flagset, f, m);		\
 								\
 		fset = &v->arch.flagset;			\
+		__vcpu_flags_preempt_disable();			\
 		if (HWEIGHT(m) > 1)				\
 			*fset &= ~(m);				\
 		*fset |= (f);					\
+		__vcpu_flags_preempt_enable();			\
 	} while (0)
 
 #define __vcpu_clear_flag(v, flagset, f, m)			\
@@ -598,7 +613,9 @@ struct kvm_vcpu_arch {
 		__build_check_flag(v, flagset, f, m);		\
 								\
 		fset = &v->arch.flagset;			\
+		__vcpu_flags_preempt_disable();			\
 		*fset &= ~(m);					\
+		__vcpu_flags_preempt_enable();			\
 	} while (0)
 
 #define vcpu_get_flag(v, ...)	__vcpu_get_flag((v), __VA_ARGS__)
-- 
2.34.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH] KVM: arm64: Make vcpu flag updates non-preemptible
  2023-04-17  9:36 [PATCH] KVM: arm64: Make vcpu flag updates non-preemptible Marc Zyngier
@ 2023-04-17 11:40 ` Will Deacon
  2023-04-17 12:35   ` Marc Zyngier
  0 siblings, 1 reply; 3+ messages in thread
From: Will Deacon @ 2023-04-17 11:40 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvmarm, kvm, linux-arm-kernel, James Morse, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Catalin Marinas, Quentin Perret,
	Mostafa Saleh, stable

On Mon, Apr 17, 2023 at 10:36:29AM +0100, Marc Zyngier wrote:
> Per-vcpu flags are updated using a non-atomic RMW operation.
> Which means it is possible to get preempted between the read and
> write operations.
> 
> Another interesting thing to note is that preemption also updates
> flags, as we have some flag manipulation in both the load and put
> operations.
> 
> It is thus possible to lose information communicated by either
> load or put, as the preempted flag update will overwrite the flags
> when the thread is resumed. This is specially critical if either
> load or put has stored information which depends on the physical
> CPU the vcpu runs on.
> 
> This results in really elusive bugs, and kudos must be given to
> Mostafa for the long hours of debugging, and finally spotting
> the problem.
> 
> Fixes: e87abb73e594 ("KVM: arm64: Add helpers to manipulate vcpu flags among a set")
> Reported-by: Mostafa Saleh <smostafa@google.com>
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> Cc: stable@vger.kernel.org
> ---
>  arch/arm64/include/asm/kvm_host.h | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index bcd774d74f34..d716cfd806e8 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -579,6 +579,19 @@ struct kvm_vcpu_arch {
>  		v->arch.flagset & (m);				\
>  	})
>  
> +/*
> + * Note that the set/clear accessors must be preempt-safe in order to
> + * avoid nesting them with load/put which also manipulate flags...
> + */
> +#ifdef __KVM_NVHE_HYPERVISOR__
> +/* the nVHE hypervisor is always non-preemptible */
> +#define __vcpu_flags_preempt_disable()
> +#define __vcpu_flags_preempt_enable()
> +#else
> +#define __vcpu_flags_preempt_disable()	preempt_disable()
> +#define __vcpu_flags_preempt_enable()	preempt_enable()
> +#endif

If it makes things cleaner, we could define local (empty) copies of these
preempt_*() macros at EL2 to save you having to wrap them here. Up to you.

>  #define __vcpu_set_flag(v, flagset, f, m)			\
>  	do {							\
>  		typeof(v->arch.flagset) *fset;			\
> @@ -586,9 +599,11 @@ struct kvm_vcpu_arch {
>  		__build_check_flag(v, flagset, f, m);		\
>  								\
>  		fset = &v->arch.flagset;			\
> +		__vcpu_flags_preempt_disable();			\
>  		if (HWEIGHT(m) > 1)				\
>  			*fset &= ~(m);				\
>  		*fset |= (f);					\
> +		__vcpu_flags_preempt_enable();			\
>  	} while (0)
>  
>  #define __vcpu_clear_flag(v, flagset, f, m)			\
> @@ -598,7 +613,9 @@ struct kvm_vcpu_arch {
>  		__build_check_flag(v, flagset, f, m);		\
>  								\
>  		fset = &v->arch.flagset;			\
> +		__vcpu_flags_preempt_disable();			\
>  		*fset &= ~(m);					\
> +		__vcpu_flags_preempt_enable();			\
>  	} while (0)
>  
>  #define vcpu_get_flag(v, ...)	__vcpu_get_flag((v), __VA_ARGS__)

Given that __vcpu_get_flag() is still preemptible, we should probably
add a READ_ONCE() in there when we access the relevant flags field. In
practice, they're all single-byte fields so it should be ok, but I think
the READ_ONCE() is still worthwhile.

Will

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

* Re: [PATCH] KVM: arm64: Make vcpu flag updates non-preemptible
  2023-04-17 11:40 ` Will Deacon
@ 2023-04-17 12:35   ` Marc Zyngier
  0 siblings, 0 replies; 3+ messages in thread
From: Marc Zyngier @ 2023-04-17 12:35 UTC (permalink / raw)
  To: Will Deacon
  Cc: kvmarm, kvm, linux-arm-kernel, James Morse, Suzuki K Poulose,
	Oliver Upton, Zenghui Yu, Catalin Marinas, Quentin Perret,
	Mostafa Saleh, stable

On Mon, 17 Apr 2023 12:40:26 +0100,
Will Deacon <will@kernel.org> wrote:
> 
> On Mon, Apr 17, 2023 at 10:36:29AM +0100, Marc Zyngier wrote:
> > Per-vcpu flags are updated using a non-atomic RMW operation.
> > Which means it is possible to get preempted between the read and
> > write operations.
> > 
> > Another interesting thing to note is that preemption also updates
> > flags, as we have some flag manipulation in both the load and put
> > operations.
> > 
> > It is thus possible to lose information communicated by either
> > load or put, as the preempted flag update will overwrite the flags
> > when the thread is resumed. This is specially critical if either
> > load or put has stored information which depends on the physical
> > CPU the vcpu runs on.
> > 
> > This results in really elusive bugs, and kudos must be given to
> > Mostafa for the long hours of debugging, and finally spotting
> > the problem.
> > 
> > Fixes: e87abb73e594 ("KVM: arm64: Add helpers to manipulate vcpu flags among a set")
> > Reported-by: Mostafa Saleh <smostafa@google.com>
> > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > Cc: stable@vger.kernel.org
> > ---
> >  arch/arm64/include/asm/kvm_host.h | 17 +++++++++++++++++
> >  1 file changed, 17 insertions(+)
> > 
> > diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> > index bcd774d74f34..d716cfd806e8 100644
> > --- a/arch/arm64/include/asm/kvm_host.h
> > +++ b/arch/arm64/include/asm/kvm_host.h
> > @@ -579,6 +579,19 @@ struct kvm_vcpu_arch {
> >  		v->arch.flagset & (m);				\
> >  	})
> >  
> > +/*
> > + * Note that the set/clear accessors must be preempt-safe in order to
> > + * avoid nesting them with load/put which also manipulate flags...
> > + */
> > +#ifdef __KVM_NVHE_HYPERVISOR__
> > +/* the nVHE hypervisor is always non-preemptible */
> > +#define __vcpu_flags_preempt_disable()
> > +#define __vcpu_flags_preempt_enable()
> > +#else
> > +#define __vcpu_flags_preempt_disable()	preempt_disable()
> > +#define __vcpu_flags_preempt_enable()	preempt_enable()
> > +#endif
> 
> If it makes things cleaner, we could define local (empty) copies of these
> preempt_*() macros at EL2 to save you having to wrap them here. Up to you.

Nah, that's fine. This is subtle enough stuff that I'm happy to see it
all exposed in the same location.

> >  #define __vcpu_set_flag(v, flagset, f, m)			\
> >  	do {							\
> >  		typeof(v->arch.flagset) *fset;			\
> > @@ -586,9 +599,11 @@ struct kvm_vcpu_arch {
> >  		__build_check_flag(v, flagset, f, m);		\
> >  								\
> >  		fset = &v->arch.flagset;			\
> > +		__vcpu_flags_preempt_disable();			\
> >  		if (HWEIGHT(m) > 1)				\
> >  			*fset &= ~(m);				\
> >  		*fset |= (f);					\
> > +		__vcpu_flags_preempt_enable();			\
> >  	} while (0)
> >  
> >  #define __vcpu_clear_flag(v, flagset, f, m)			\
> > @@ -598,7 +613,9 @@ struct kvm_vcpu_arch {
> >  		__build_check_flag(v, flagset, f, m);		\
> >  								\
> >  		fset = &v->arch.flagset;			\
> > +		__vcpu_flags_preempt_disable();			\
> >  		*fset &= ~(m);					\
> > +		__vcpu_flags_preempt_enable();			\
> >  	} while (0)
> >  
> >  #define vcpu_get_flag(v, ...)	__vcpu_get_flag((v), __VA_ARGS__)
> 
> Given that __vcpu_get_flag() is still preemptible, we should probably
> add a READ_ONCE() in there when we access the relevant flags field. In
> practice, they're all single-byte fields so it should be ok, but I think
> the READ_ONCE() is still worthwhile.

Yup, good point. People are already talking about expanding some of
the fields for $REASON, so they may become larger than a single byte.
And READ_ONCE() makes it clear that there is some level of atomicity
required here as well.

I'll respin this shortly.

Thanks,

	M.

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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

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

end of thread, other threads:[~2023-04-17 12:36 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-04-17  9:36 [PATCH] KVM: arm64: Make vcpu flag updates non-preemptible Marc Zyngier
2023-04-17 11:40 ` Will Deacon
2023-04-17 12:35   ` 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).