Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/kvm: Avoid freeing stack-allocated node in kvm_async_pf_queue_task

kvm_async_pf_queue_task() can incorrectly try to kfree() a node
allocated on the stack of kvm_async_pf_task_wait_schedule().

This occurs when a task requests a PF while another task's PF request
with the same token is still pending. Since the token is derived from
the (u32)address in exc_page_fault(), two different tasks can generate
the same token.

Currently, kvm_async_pf_queue_task() assumes that any entry found in the
list is a dummy entry and tries to kfree() it. To fix this, add a flag
to the node structure to distinguish stack-allocated nodes, and only
kfree() the node if it is a dummy entry.

Signed-off-by: Ryosuke Yasuoka <ryasuoka@redhat.com>
Message-ID: <20251206140939.144038-1-ryasuoka@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

authored by

Ryosuke Yasuoka and committed by
Paolo Bonzini
95cc9e7c c8ebd433

+16 -3
+16 -3
arch/x86/kernel/kvm.c
··· 89 89 struct swait_queue_head wq; 90 90 u32 token; 91 91 int cpu; 92 + bool dummy; 92 93 }; 93 94 94 95 static struct kvm_task_sleep_head { ··· 121 120 raw_spin_lock(&b->lock); 122 121 e = _find_apf_task(b, token); 123 122 if (e) { 124 - /* dummy entry exist -> wake up was delivered ahead of PF */ 125 - hlist_del(&e->link); 123 + struct kvm_task_sleep_node *dummy = NULL; 124 + 125 + /* 126 + * The entry can either be a 'dummy' entry (which is put on the 127 + * list when wake-up happens ahead of APF handling completion) 128 + * or a token from another task which should not be touched. 129 + */ 130 + if (e->dummy) { 131 + hlist_del(&e->link); 132 + dummy = e; 133 + } 134 + 126 135 raw_spin_unlock(&b->lock); 127 - kfree(e); 136 + kfree(dummy); 128 137 return false; 129 138 } 130 139 131 140 n->token = token; 132 141 n->cpu = smp_processor_id(); 142 + n->dummy = false; 133 143 init_swait_queue_head(&n->wq); 134 144 hlist_add_head(&n->link, &b->list); 135 145 raw_spin_unlock(&b->lock); ··· 243 231 } 244 232 dummy->token = token; 245 233 dummy->cpu = smp_processor_id(); 234 + dummy->dummy = true; 246 235 init_swait_queue_head(&dummy->wq); 247 236 hlist_add_head(&dummy->link, &b->list); 248 237 dummy = NULL;