From nobody Sat Jun 13 02:06:48 2026 Received: from m16.mail.163.com (m16.mail.163.com [220.197.31.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9CEF53FB7E5 for ; Mon, 11 May 2026 13:58:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.31.5 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778507903; cv=none; b=Fp2UNwt/tLyhxiBdtRxR4S/jxk4Q3yxAsb56pdkoesHaX+WVhXNau8TxTuVWneDcP6jDaDhFNOcNlsFUTsLyfm4uuuO0pe6jeyqdySqSrz119GXwlqUQwJThPj9zqQ7Z9TDkKxzfoTeHlnn7TgeXZjhXvHeGg3Pe+YHq2xPl2kQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778507903; c=relaxed/simple; bh=op7SbYL5kSuhHpS6LCR5YxqViIiGOx4CnjH3eMcra/k=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=rw/smHLIhUvve0P6yqKBsY04y3tGYj9kqdYD8gcT/SI9tJDIU3k+/OKmLcNZ8N0NIAFLsXUcnwzMnpbEC+vMpm9qD80cVdzqFhR9T5hwscd0yRevEtV2AQeA6u2iFzNayat3SbWq+2ccNaTfEj4CBG9AcUg+mmgb1oMDGY2ZLRI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=BzhEHMlw; arc=none smtp.client-ip=220.197.31.5 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="BzhEHMlw" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-Id:MIME-Version; bh=q0 1y3RTORKIkaxoHONEofepqzuUkfYt1NawukREtHX0=; b=BzhEHMlw6aWR119lwB 2nke3XTgCQOriGotmd4gIYgrMrKt0rCJJnR+FEnId+nwHPMpcDzOwZU+ceWHuwyr 1r8PeHKqRgOCFRO5J6ipPUQLZQrZETWj0Zk7jvuImqMLr/jCMUAIMFXeIuroqSp6 8CSK+Ft1FADHWzcb7OXX9EB4k= Received: from flipped.. (unknown []) by gzga-smtp-mtada-g0-0 (Coremail) with SMTP id _____wCnLlNT4AFqNDvbAg--.29944S2; Mon, 11 May 2026 21:57:40 +0800 (CST) From: Sicong Huang To: fei1.li@intel.com Cc: acrn-dev@lists.projectacrn.org, gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, Sicong Huang Subject: [PATCH v1] virt: acrn: Fix irqfd use-after-free during async shutdown Date: Mon, 11 May 2026 21:57:37 +0800 Message-Id: <20260511135737.2285411-1-congei42@163.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: _____wCnLlNT4AFqNDvbAg--.29944S2 X-Coremail-Antispam: 1Uf129KBjvJXoWxCw48tFy3Jw1xtw45tFWkWFg_yoWrCw15pw 43u3y5Kr4xJrW29rs8Krn5uF15trWrWa17twn3Ca1fKFs0yr13XF1UAryUKr1rKFZ7WrW3 A348tw45WFyUtFJanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x0zELID-UUUUU= X-CM-SenderInfo: 5frqwvrlusqiywtou0bp/xtbC9hQoG2oB4FQlngAA31 Content-Type: text/plain; charset="utf-8" ACRN irqfd registers a custom waitqueue callback on the eventfd. When the eventfd is released, eventfd_release() wakes the waitqueue with EPOLLHUP, and hsm_irqfd_wakeup() queues irqfd->shutdown on vm->irqfd_wq. The irqfd object can also be removed by ACRN_IOCTL_IRQFD with ACRN_IRQFD_FLAG_DEASSIGN. In that path, acrn_irqfd_deassign() removes the waitqueue entry and frees the hsm_irqfd object. These two paths can race. If EPOLLHUP queues the shutdown work before deassign frees the object, the work item may run after kfree() and recover the freed hsm_irqfd via container_of(). It then dereferences irqfd->vm while taking irqfds_lock. A possible race is: CPU0 CPU1 eventfd_release() wake_up_poll(EPOLLHUP) hsm_irqfd_wakeup() queue_work(&irqfd->shutdown) acrn_irqfd_deassign() hsm_irqfd_shutdown() list_del_init() eventfd_ctx_remove_wait_queue() kfree(irqfd) //free here! hsm_irqfd_shutdown_work() irqfd =3D container_of(work, ...) vm =3D irqfd->vm //UAF! Fix this by separating logical shutdown from object release. First remove the irqfd from the VM list and eventfd waitqueue, then synchronously cancel any pending/running shutdown work before freeing the object. Also tear down irqfds before destroying the irqfd workqueue, so eventfd wakeups cannot queue work after the workqueue has been destroyed. Signed-off-by: Sicong Huang --- drivers/virt/acrn/irqfd.c | 47 ++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/drivers/virt/acrn/irqfd.c b/drivers/virt/acrn/irqfd.c index acf8cd5f8f8c..659fd40d9aa5 100644 --- a/drivers/virt/acrn/irqfd.c +++ b/drivers/virt/acrn/irqfd.c @@ -44,30 +44,37 @@ static void acrn_irqfd_inject(struct hsm_irqfd *irqfd) irqfd->msi.msi_data); } =20 -static void hsm_irqfd_shutdown(struct hsm_irqfd *irqfd) +static bool hsm_irqfd_shutdown(struct hsm_irqfd *irqfd) { u64 cnt; =20 lockdep_assert_held(&irqfd->vm->irqfds_lock); =20 + if (list_empty(&irqfd->list)) + return false; + /* remove from wait queue */ list_del_init(&irqfd->list); eventfd_ctx_remove_wait_queue(irqfd->eventfd, &irqfd->wait, &cnt); eventfd_ctx_put(irqfd->eventfd); - kfree(irqfd); + + return true; } =20 static void hsm_irqfd_shutdown_work(struct work_struct *work) { struct hsm_irqfd *irqfd; struct acrn_vm *vm; + bool free; =20 irqfd =3D container_of(work, struct hsm_irqfd, shutdown); vm =3D irqfd->vm; mutex_lock(&vm->irqfds_lock); - if (!list_empty(&irqfd->list)) - hsm_irqfd_shutdown(irqfd); + free =3D hsm_irqfd_shutdown(irqfd); mutex_unlock(&vm->irqfds_lock); + + if (free) + kfree(irqfd); } =20 /* Called with wqh->lock held and interrupts disabled */ @@ -170,7 +177,7 @@ static int acrn_irqfd_assign(struct acrn_vm *vm, struct= acrn_irqfd *args) static int acrn_irqfd_deassign(struct acrn_vm *vm, struct acrn_irqfd *args) { - struct hsm_irqfd *irqfd, *tmp; + struct hsm_irqfd *irqfd, *tmp, *to_free =3D NULL; struct eventfd_ctx *eventfd; =20 eventfd =3D eventfd_ctx_fdget(args->fd); @@ -180,13 +187,19 @@ static int acrn_irqfd_deassign(struct acrn_vm *vm, mutex_lock(&vm->irqfds_lock); list_for_each_entry_safe(irqfd, tmp, &vm->irqfds, list) { if (irqfd->eventfd =3D=3D eventfd) { - hsm_irqfd_shutdown(irqfd); + if (hsm_irqfd_shutdown(irqfd)) + to_free =3D irqfd; break; } } mutex_unlock(&vm->irqfds_lock); eventfd_ctx_put(eventfd); =20 + if (to_free) { + cancel_work_sync(&to_free->shutdown); + kfree(to_free); + } + return 0; } =20 @@ -219,9 +232,23 @@ void acrn_irqfd_deinit(struct acrn_vm *vm) struct hsm_irqfd *irqfd, *next; =20 dev_dbg(acrn_dev.this_device, "VM %u irqfd deinit.\n", vm->vmid); + + for (;;) { + irqfd =3D NULL; + + mutex_lock(&vm->irqfds_lock); + if (!list_empty(&vm->irqfds)) { + irqfd =3D list_first_entry(&vm->irqfds, struct hsm_irqfd, list); + hsm_irqfd_shutdown(irqfd); + } + mutex_unlock(&vm->irqfds_lock); + + if (!irqfd) + break; + + cancel_work_sync(&irqfd->shutdown); + kfree(irqfd); + } + destroy_workqueue(vm->irqfd_wq); - mutex_lock(&vm->irqfds_lock); - list_for_each_entry_safe(irqfd, next, &vm->irqfds, list) - hsm_irqfd_shutdown(irqfd); - mutex_unlock(&vm->irqfds_lock); } --=20 2.34.1