From: Sean Christopherson <seanjc@google.com> Date: 2025-08-27 19:41:13
Michael,
Do you want to take this through the vhost tree? It technically fixes a KVM
bug, but this obviously touches far more vhost code than KVM code, and the
patch that needs to go into 6.17 doesn't touch KVM at all.
Fix a bug where KVM attempts to wake a vhost task that has already exited in
response to a fatal signal, and tack on a few cleanups to harden against
introducing similar bugs in the future.
The issue is firmly a KVM problem, but I opted to fix the bug by making
vhost_task_wake() safe against an exited task as doing so is far simpler and
cleaner than implementing the same functionality in KVM, and I suspect that
if there are other users of vhost_tasks in the future, then there's a good
chance they will want/expect vhost_task to handle that detail.
Note, this only started causing problems when commit 56180dd20c19 ("futex:
Use RCU-based per-CPU reference counting instead of rcuref_t") landed, so
the explosions are "new" in 6.17, but the bug has existed since KVM switched
to vhost_task back in 6.13.
v2:
- Drop the "safe" postfix variant and make the "default" vhost_task_wake()
safe. [Michael].
- Use vhost_task_wake() and __vhost_task_wake() for the public APIs, and
vhost_task_wake_up_process() for the local helper. [Michael]
- Drag the signalas back from their Spanish holiday. [Sebastian]
v1: https://lore.kernel.org/all/20250826004012.3835150-1-seanjc@google.com
Sean Christopherson (3):
vhost_task: Don't wake KVM x86's recovery thread if vhost task was
killed
vhost_task: Allow caller to omit handle_sigkill() callback
KVM: x86/mmu: Don't register a sigkill callback for NX hugepage
recovery tasks
arch/x86/kvm/mmu/mmu.c | 7 +---
drivers/vhost/vhost.c | 2 +-
include/linux/sched/vhost_task.h | 1 +
kernel/vhost_task.c | 62 +++++++++++++++++++++++++++-----
4 files changed, 56 insertions(+), 16 deletions(-)
base-commit: 1b237f190eb3d36f52dffe07a40b5eb210280e00
--
2.51.0.268.g9569e192d0-goog
From: Sean Christopherson <seanjc@google.com> Date: 2025-08-27 19:41:16
Make the "default" API for waking a vhost task safe against the underlying
task exiting due to a fatal signal. This fixes a bug in KVM x86 where KVM
attempts to wake an NX hugepage recovery task that exiting before being
explicitly stopped, resulting in a use-after-free and thus crashes, hangs,
and other badness.
Oops: general protection fault, probably for non-canonical address 0xff0e899fa1566052: 0000 [#1] SMP
CPU: 51 UID: 0 PID: 53807 Comm: tee Tainted: G S O 6.17.0-smp--38183c31756a-next #826 NONE
Tainted: [S]=CPU_OUT_OF_SPEC, [O]=OOT_MODULE
Hardware name: Google LLC Indus/Indus_QC_03, BIOS 30.110.0 09/13/2024
RIP: 0010:queued_spin_lock_slowpath+0x123/0x250
Code: ... <48> 89 8c 02 c0 da 47 a2 83 79 08 00 75 08 f3 90 83 79 08 00 74 f8
RSP: 0018:ffffbf55cffe7cf8 EFLAGS: 00010006
RAX: ff0e899fff0e8562 RBX: 0000000000d00000 RCX: ffffa39b40aefac0
RDX: 0000000000000030 RSI: fffffffffffffff8 RDI: ffffa39d0592e68c
RBP: 0000000000d00000 R08: 00000000ffffff80 R09: 0000000400000000
R10: ffffa36cce4fe401 R11: 0000000000000800 R12: 0000000000000003
R13: 0000000000000000 R14: ffffa39d0592e68c R15: ffffa39b9e672000
FS: 00007f233b2e9740(0000) GS:ffffa39b9e672000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f233b39fda0 CR3: 00000004d031f002 CR4: 00000000007726f0
PKRU: 55555554
Call Trace:
<TASK>
_raw_spin_lock_irqsave+0x50/0x60
try_to_wake_up+0x4f/0x5d0
set_nx_huge_pages+0xe4/0x1c0 [kvm]
param_attr_store+0x89/0xf0
module_attr_store+0x1e/0x30
kernfs_fop_write_iter+0xe4/0x160
vfs_write+0x2cb/0x420
ksys_write+0x7f/0xf0
do_syscall_64+0x6f/0x1f0
entry_SYSCALL_64_after_hwframe+0x4b/0x53
RIP: 0033:0x7f233b4178b3
R13: 0000000000000002 R14: 00000000226ff3d0 R15: 0000000000000002
</TASK>
Handle VHOST_TASK_FLAGS_KILLED in vhost_task_wake() instead of forcing KVM
to solve the problem, as KVM would literally just add an equivalent flag,
along with a new lock to protect said flag. In general, forcing simple
usage of vhost task to care about signals _and_ take non-trivial action to
do the right thing isn't developer friendly, and is likely to lead to
similar bugs in the future.
Keep the existing behavior for vhost (by calling __vhost_task_wake()
instead of vhost_task_wake()), as vhost_worker_killed() takes extra care
to stop and flush all workers, i.e. doesn't need the extra protection, and
because vhost_vq_work_queue() calls
vhost_worker_queue()
|
-> worker->ops->wakeup(worker)
|
-> vhost_task_wakeup()
|
-> vhost_task_wake()
while holding RCU and so can't sleep, i.e. can't take exit_mutex.
rcu_read_lock();
worker = rcu_dereference(vq->worker);
if (worker) {
queued = true;
vhost_worker_queue(worker, work);
}
rcu_read_unlock();
Debugged-by: Sebastian Andrzej Siewior [off-list ref]
Link: https://lore.kernel.org/all/aKkLEtoDXKxAAWju@google.com
Link: https://lore.kernel.org/all/aJ_vEP2EHj6l0xRT@google.com
Suggested-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Fixes: d96c77bd4eeb ("KVM: x86: switch hugepage recovery thread to vhost_task")
Cc: stable@vger.kernel.org
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
drivers/vhost/vhost.c | 2 +-
include/linux/sched/vhost_task.h | 1 +
kernel/vhost_task.c | 52 +++++++++++++++++++++++++++-----
3 files changed, 46 insertions(+), 9 deletions(-)
@@ -67,16 +67,52 @@ static int vhost_task_fn(void *data)do_exit(0);}-/**-*vhost_task_wake-wakeupthevhost_task-*@vtsk:vhost_tasktowake-*-*wakeupthevhost_taskworkerthread-*/-voidvhost_task_wake(structvhost_task*vtsk)+staticvoidvhost_task_wake_up_process(structvhost_task*vtsk){wake_up_process(vtsk->task);}++/**+*__vhost_task_wake-wakeupthevhost_task+*@vtsk:vhost_tasktowake+*+*Wakeupthevhost_taskworkerthread.Thecallerisresponsibleforensuring+*thatthetaskhasn'texited.+*/+void__vhost_task_wake(structvhost_task*vtsk)+{+/*+*CheckingVHOST_TASK_FLAGS_KILLEDcanracewithsignaldelivery,but+*aracecanonlyresultinfalsenegativesandthisisjustasanity+*check,i.e.ifKILLEDisset,thecallerisbuggynomatterwhat.+*/+if(WARN_ON_ONCE(test_bit(VHOST_TASK_FLAGS_KILLED,&vtsk->flags)))+return;++vhost_task_wake_up_process(vtsk);+}+EXPORT_SYMBOL_GPL(__vhost_task_wake);++/**+*vhost_task_wake-wakeupthevhost_taskifithasn'tbeenkilled+*@vtsk:vhost_tasktowake+*+*Wakeupthevhost_taskworkerthreadifthetaskhasn'texited,e.g.dueto+*asignal.+*/+voidvhost_task_wake(structvhost_task*vtsk)+{+guard(mutex)(&vtsk->exit_mutex);++/* Attempting to wake a task that has been explicitly stopped is a bug. */+if(WARN_ON_ONCE(test_bit(VHOST_TASK_FLAGS_STOP,&vtsk->flags)))+return;++if(test_bit(VHOST_TASK_FLAGS_KILLED,&vtsk->flags))+return;++vhost_task_wake_up_process(vtsk);+}EXPORT_SYMBOL_GPL(vhost_task_wake);/**
From: Sean Christopherson <seanjc@google.com> Date: 2025-08-27 19:41:18
Now that vhost_task provides an API to safely wake a task without relying
on the caller to react to signals, make handle_sigkill() optional and
WARN if the "unsafe" __vhost_task_wake() is used without hooking sigkill.
Requiring the user to react to sigkill adds no meaningful value, e.g. it
didn't help KVM do the right thing with respect to signals, and adding a
sanity check in __vhost_task_wake() gives developers a hint as to what
needs to be done in response to sigkill.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
kernel/vhost_task.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
From: Sean Christopherson <seanjc@google.com> Date: 2025-08-27 19:41:20
Don't register a sigkill callback with vhost_task when creating NX hugepage
recovery threads now that said callback is optional. In addition to
removing what is effectively dead code, not registering a sigkill "handler"
also guards against improper use of __vhost_task_wake().
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/mmu/mmu.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
@@ -7713,8 +7709,7 @@ static int kvm_mmu_start_lpage_recovery(struct once *once)structvhost_task*nx_thread;kvm->arch.nx_huge_page_last=get_jiffies_64();-nx_thread=vhost_task_create(kvm_nx_huge_page_recovery_worker,-kvm_nx_huge_page_recovery_worker_kill,+nx_thread=vhost_task_create(kvm_nx_huge_page_recovery_worker,NULL,kvm,"kvm-nx-lpage-recovery");if(IS_ERR(nx_thread))
Nice! This fixes things too. Either solution works for me. Or maybe do both?
Attempting to wake a task that vhost_task knows has exited (is exiting?) is a
bit gross, but even with that hardening, guarding against UAF is very nice to
have too.
Tested-by: Sean Christopherson <seanjc@google.com>
Tested this series of patches's v2 again with vhost-net regression
tests, everything works fine.
Tested-by: Lei Yang <redacted>
On Thu, Aug 28, 2025 at 4:11 AM Sebastian Andrzej Siewior
[off-list ref] wrote:
quoted hunk
On 2025-08-27 12:41:04 [-0700], Sean Christopherson wrote:
quoted
Michael,
Sean,
would the bellow work by chance? It is a quick shot but it looks
symmetrical…
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: 2025-08-28 06:48:44
On 2025-08-27 17:16:35 [-0700], Sean Christopherson wrote:
Nice! This fixes things too. Either solution works for me. Or maybe do both?
Attempting to wake a task that vhost_task knows has exited (is exiting?) is a
bit gross, but even with that hardening, guarding against UAF is very nice to
have too.
I don't mind either way.
If this is requested I can submit a proper patch.
Tested-by: Sean Christopherson <seanjc@google.com>
From: Sean Christopherson <seanjc@google.com> Date: 2025-09-15 21:03:02
On Wed, Aug 27, 2025, Sean Christopherson wrote:
Michael,
Do you want to take this through the vhost tree? It technically fixes a KVM
bug, but this obviously touches far more vhost code than KVM code, and the
patch that needs to go into 6.17 doesn't touch KVM at all.
Can this be squeezed into 6.17? I know it's very late in the cycle, and that the
KVM bug is pre-existing, but the increased impact of the bug is new in 6.17 and I
don't want 6.17 to release without a fix.
From: "Michael S. Tsirkin" <mst@redhat.com> Date: 2025-09-15 22:20:38
On Mon, Sep 15, 2025 at 02:03:00PM -0700, Sean Christopherson wrote:
On Wed, Aug 27, 2025, Sean Christopherson wrote:
quoted
Michael,
Do you want to take this through the vhost tree? It technically fixes a KVM
bug, but this obviously touches far more vhost code than KVM code, and the
patch that needs to go into 6.17 doesn't touch KVM at all.
Can this be squeezed into 6.17? I know it's very late in the cycle, and that the
KVM bug is pre-existing, but the increased impact of the bug is new in 6.17 and I
don't want 6.17 to release without a fix.
From: Sean Christopherson <seanjc@google.com> Date: 2025-09-15 22:22:46
On Mon, Sep 15, 2025, Michael S. Tsirkin wrote:
On Mon, Sep 15, 2025 at 02:03:00PM -0700, Sean Christopherson wrote:
quoted
On Wed, Aug 27, 2025, Sean Christopherson wrote:
quoted
Michael,
Do you want to take this through the vhost tree? It technically fixes a KVM
bug, but this obviously touches far more vhost code than KVM code, and the
patch that needs to go into 6.17 doesn't touch KVM at all.
Can this be squeezed into 6.17? I know it's very late in the cycle, and that the
KVM bug is pre-existing, but the increased impact of the bug is new in 6.17 and I
don't want 6.17 to release without a fix.
Nice! This fixes things too. Either solution works for me. Or maybe do both?
Attempting to wake a task that vhost_task knows has exited (is exiting?) is a
bit gross, but even with that hardening, guarding against UAF is very nice to
have too.
Tested-by: Sean Christopherson <seanjc@google.com>
From: Sean Christopherson <seanjc@google.com> Date: 2025-09-18 16:04:09
On Thu, Sep 18, 2025, Sebastian Andrzej Siewior wrote:
On 2025-09-18 11:09:05 [-0400], Michael S. Tsirkin wrote:
quoted
So how about switching to this approach then?
Instead of piling up fixes like we seem to do now ...
I don't have a strong preference for 6.17, beyond landing a fix of some kind.
I think there are three options for 6.17, in order of "least like to break
something":
1. Sebastian's get_task_struct() fix
2. This series, without the KILLED sanity check in __vhost_task_wake()
3. This series, with my fixup (with which syzbot was happy)
Longer term, I'd still like to land everything though.
quoted
Sean?
Since I am in To: here. You want me to resent my diff as a proper patch?
Ya, I think it makes sense to harden against UAF even if we fix the KVM bug more
directly.
From: "Michael S. Tsirkin" <mst@redhat.com> Date: 2025-09-18 16:08:16
On Thu, Sep 18, 2025 at 09:04:07AM -0700, Sean Christopherson wrote:
On Thu, Sep 18, 2025, Sebastian Andrzej Siewior wrote:
quoted
On 2025-09-18 11:09:05 [-0400], Michael S. Tsirkin wrote:
quoted
So how about switching to this approach then?
Instead of piling up fixes like we seem to do now ...
I don't have a strong preference for 6.17, beyond landing a fix of some kind.
I think there are three options for 6.17, in order of "least like to break
something":
1. Sebastian's get_task_struct() fix
I am just a bit apprehensive that we don't create a situation
where we leak the task struct somehow, given the limited
testing time. Can you help me get convinced that risk is 0?
2. This series, without the KILLED sanity check in __vhost_task_wake()
3. This series, with my fixup (with which syzbot was happy)
Longer term, I'd still like to land everything though.
No problem with that.
quoted
quoted
Sean?
Since I am in To: here. You want me to resent my diff as a proper patch?
Ya, I think it makes sense to harden against UAF even if we fix the KVM bug more
directly.
From: Sean Christopherson <seanjc@google.com> Date: 2025-09-18 16:52:21
On Thu, Sep 18, 2025, Michael S. Tsirkin wrote:
On Thu, Sep 18, 2025 at 09:04:07AM -0700, Sean Christopherson wrote:
quoted
On Thu, Sep 18, 2025, Sebastian Andrzej Siewior wrote:
quoted
On 2025-09-18 11:09:05 [-0400], Michael S. Tsirkin wrote:
quoted
So how about switching to this approach then?
Instead of piling up fixes like we seem to do now ...
I don't have a strong preference for 6.17, beyond landing a fix of some kind.
I think there are three options for 6.17, in order of "least like to break
something":
1. Sebastian's get_task_struct() fix
I am just a bit apprehensive that we don't create a situation
where we leak the task struct somehow, given the limited
testing time. Can you help me get convinced that risk is 0?
I doubt it, I share same similar concerns about lack of testing. So I guess
thinking about this again, #2 is probably safer since it'd only impact KVM?
quoted
2. This series, without the KILLED sanity check in __vhost_task_wake()
3. This series, with my fixup (with which syzbot was happy)
From: "Michael S. Tsirkin" <mst@redhat.com> Date: 2025-09-18 17:40:32
On Thu, Sep 18, 2025 at 09:52:19AM -0700, Sean Christopherson wrote:
On Thu, Sep 18, 2025, Michael S. Tsirkin wrote:
quoted
On Thu, Sep 18, 2025 at 09:04:07AM -0700, Sean Christopherson wrote:
quoted
On Thu, Sep 18, 2025, Sebastian Andrzej Siewior wrote:
quoted
On 2025-09-18 11:09:05 [-0400], Michael S. Tsirkin wrote:
quoted
So how about switching to this approach then?
Instead of piling up fixes like we seem to do now ...
I don't have a strong preference for 6.17, beyond landing a fix of some kind.
I think there are three options for 6.17, in order of "least like to break
something":
1. Sebastian's get_task_struct() fix
I am just a bit apprehensive that we don't create a situation
where we leak the task struct somehow, given the limited
testing time. Can you help me get convinced that risk is 0?
I doubt it, I share same similar concerns about lack of testing. So I guess
thinking about this again, #2 is probably safer since it'd only impact KVM?
I can't say I understand completely how we get that state though?
Why did the warning trigger if it's not a UAF?
quoted
quoted
2. This series, without the KILLED sanity check in __vhost_task_wake()
3. This series, with my fixup (with which syzbot was happy)
From: Sean Christopherson <seanjc@google.com> Date: 2025-09-18 17:58:10
On Thu, Sep 18, 2025, Michael S. Tsirkin wrote:
On Thu, Sep 18, 2025 at 09:52:19AM -0700, Sean Christopherson wrote:
quoted
On Thu, Sep 18, 2025, Michael S. Tsirkin wrote:
quoted
On Thu, Sep 18, 2025 at 09:04:07AM -0700, Sean Christopherson wrote:
quoted
On Thu, Sep 18, 2025, Sebastian Andrzej Siewior wrote:
quoted
On 2025-09-18 11:09:05 [-0400], Michael S. Tsirkin wrote:
quoted
So how about switching to this approach then?
Instead of piling up fixes like we seem to do now ...
I don't have a strong preference for 6.17, beyond landing a fix of some kind.
I think there are three options for 6.17, in order of "least like to break
something":
1. Sebastian's get_task_struct() fix
I am just a bit apprehensive that we don't create a situation
where we leak the task struct somehow, given the limited
testing time. Can you help me get convinced that risk is 0?
I doubt it, I share same similar concerns about lack of testing. So I guess
thinking about this again, #2 is probably safer since it'd only impact KVM?
I can't say I understand completely how we get that state though?
Why did the warning trigger if it's not a UAF?
It's purely a flaw in the sanity check itself due to the ordering in vhost_task_fn().
As is, vhost_task_fn() marks the task KILLED before invoking ->handle_sigkill(),
i.e. before vhost_worker_killed() is guaranteed to complete, and thus before
worker->killed is set. As a result, vhost can keep waking workers that have
KILLED set, but haven't actually exited. That's perfectly fine as UAF won't
occur until do_exit() is called, and that won't happen until ->handle_sigkill()
completes.
quoted
quoted
quoted
2. This series, without the KILLED sanity check in __vhost_task_wake()
3. This series, with my fixup (with which syzbot was happy)
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Date: 2025-09-18 18:11:48
vhost_task_create() creates a task and keeps a reference to its
task_struct. That task may exit early via a signal and its task_struct
will be released.
A pending vhost_task_wake() will then attempt to wake the task and
access a task_struct which is no longer there.
Acquire a reference on the task_struct while creating the thread and
release the reference while the struct vhost_task itself is removed.
If the task exits early due to a signal, then the vhost_task_wake() will
still access a valid task_struct. The wake is safe and will be skipped
in this case.
Fixes: f9010dbdce911 ("fork, vhost: Use CLONE_THREAD to fix freezer/ps regression")
Reported-by: Sean Christopherson <seanjc@google.com>
Closes: https://lore.kernel.org/all/aKkLEtoDXKxAAWju@google.com/
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
kernel/vhost_task.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
From: Sean Christopherson <seanjc@google.com> Date: 2025-09-19 21:15:47
On Thu, Sep 18, 2025, Sebastian Andrzej Siewior wrote:
vhost_task_create() creates a task and keeps a reference to its
task_struct. That task may exit early via a signal and its task_struct
will be released.
A pending vhost_task_wake() will then attempt to wake the task and
access a task_struct which is no longer there.
Acquire a reference on the task_struct while creating the thread and
release the reference while the struct vhost_task itself is removed.
If the task exits early due to a signal, then the vhost_task_wake() will
still access a valid task_struct. The wake is safe and will be skipped
in this case.
Fixes: f9010dbdce911 ("fork, vhost: Use CLONE_THREAD to fix freezer/ps regression")
Reported-by: Sean Christopherson <seanjc@google.com>
Closes: https://lore.kernel.org/all/aKkLEtoDXKxAAWju@google.com/
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
Tested-by: Sean Christopherson <seanjc@google.com>
From: "Michael S. Tsirkin" <mst@redhat.com> Date: 2025-09-21 20:56:30
Subject: that is reference -> that is referenced
On Thu, Sep 18, 2025 at 08:11:44PM +0200, Sebastian Andrzej Siewior wrote:
quoted hunk
vhost_task_create() creates a task and keeps a reference to its
task_struct. That task may exit early via a signal and its task_struct
will be released.
A pending vhost_task_wake() will then attempt to wake the task and
access a task_struct which is no longer there.
Acquire a reference on the task_struct while creating the thread and
release the reference while the struct vhost_task itself is removed.
If the task exits early due to a signal, then the vhost_task_wake() will
still access a valid task_struct. The wake is safe and will be skipped
in this case.
Fixes: f9010dbdce911 ("fork, vhost: Use CLONE_THREAD to fix freezer/ps regression")
Reported-by: Sean Christopherson <seanjc@google.com>
Closes: https://lore.kernel.org/all/aKkLEtoDXKxAAWju@google.com/
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
kernel/vhost_task.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
From: "Michael S. Tsirkin" <mst@redhat.com> Date: 2025-09-21 21:40:15
On Sun, Sep 21, 2025 at 04:56:20PM -0400, Michael S. Tsirkin wrote:
Subject: that is reference -> that is referenced
to note i fixed it for now. just dropped "that is referenced"
completely. shorter.
On Thu, Sep 18, 2025 at 08:11:44PM +0200, Sebastian Andrzej Siewior wrote:
quoted
vhost_task_create() creates a task and keeps a reference to its
task_struct. That task may exit early via a signal and its task_struct
will be released.
A pending vhost_task_wake() will then attempt to wake the task and
access a task_struct which is no longer there.
Acquire a reference on the task_struct while creating the thread and
release the reference while the struct vhost_task itself is removed.
If the task exits early due to a signal, then the vhost_task_wake() will
still access a valid task_struct. The wake is safe and will be skipped
in this case.
Fixes: f9010dbdce911 ("fork, vhost: Use CLONE_THREAD to fix freezer/ps regression")
Reported-by: Sean Christopherson <seanjc@google.com>
Closes: https://lore.kernel.org/all/aKkLEtoDXKxAAWju@google.com/
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
kernel/vhost_task.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)