commit 8646e53633f314e4d746a988240d3b951a92f94a upstream. Invoke rseq's NOTIFY_RESUME handler when processing the flag prior to transferring to a KVM guest, which is roughly equivalent to an exit to userspace and processes many of the same pending actions. While the task cannot be in an rseq critical section as the KVM path is reachable only by via ioctl(KVM_RUN), the side effects that apply to rseq outside of a critical section still apply, e.g. the current CPU needs to be updated if the task is migrated. Clearing TIF_NOTIFY_RESUME without informing rseq can lead to segfaults and other badness in userspace VMMs that use rseq in combination with KVM, e.g. due to the CPU ID being stale after task migration. Fixes: 72c3c0fe54a3 ("x86/kvm: Use generic xfer to guest work function") Reported-by: Peter Foley <pefoley@google.com> Bisected-by: Doug Evans <dje@google.com> Acked-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> Cc: Shakeel Butt <shakeelb@google.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: stable@vger.kernel.org Signed-off-by: Sean Christopherson <seanjc@google.com> Message-Id: <20210901203030.1292304-2-seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> [sean: Resolve benign conflict due to unrelated access_ok() check in 5.10] Signed-off-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
52 lines
1.2 KiB
C
52 lines
1.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include <linux/entry-kvm.h>
|
|
#include <linux/kvm_host.h>
|
|
|
|
static int xfer_to_guest_mode_work(struct kvm_vcpu *vcpu, unsigned long ti_work)
|
|
{
|
|
do {
|
|
int ret;
|
|
|
|
if (ti_work & _TIF_SIGPENDING) {
|
|
kvm_handle_signal_exit(vcpu);
|
|
return -EINTR;
|
|
}
|
|
|
|
if (ti_work & _TIF_NEED_RESCHED)
|
|
schedule();
|
|
|
|
if (ti_work & _TIF_NOTIFY_RESUME) {
|
|
tracehook_notify_resume(NULL);
|
|
rseq_handle_notify_resume(NULL, NULL);
|
|
}
|
|
|
|
ret = arch_xfer_to_guest_mode_handle_work(vcpu, ti_work);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ti_work = READ_ONCE(current_thread_info()->flags);
|
|
} while (ti_work & XFER_TO_GUEST_MODE_WORK || need_resched());
|
|
return 0;
|
|
}
|
|
|
|
int xfer_to_guest_mode_handle_work(struct kvm_vcpu *vcpu)
|
|
{
|
|
unsigned long ti_work;
|
|
|
|
/*
|
|
* This is invoked from the outer guest loop with interrupts and
|
|
* preemption enabled.
|
|
*
|
|
* KVM invokes xfer_to_guest_mode_work_pending() with interrupts
|
|
* disabled in the inner loop before going into guest mode. No need
|
|
* to disable interrupts here.
|
|
*/
|
|
ti_work = READ_ONCE(current_thread_info()->flags);
|
|
if (!(ti_work & XFER_TO_GUEST_MODE_WORK))
|
|
return 0;
|
|
|
|
return xfer_to_guest_mode_work(vcpu, ti_work);
|
|
}
|
|
EXPORT_SYMBOL_GPL(xfer_to_guest_mode_handle_work);
|