From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pf1-f175.google.com (mail-pf1-f175.google.com [209.85.210.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 76BCE3988FA for ; Thu, 6 Aug 2026 15:03:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.175 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786028636; cv=none; b=St0HDlm1uhSzK3Jm+UtFh+8awa4uxp1+hn/TWhbhL+AyLxllHGSpj4FDkhxINOP8kzMk8xeTY3tpzfdJ2cb6EsBAghGoONGwXgvYSIyHNGvN6kn6BX4AkaEhqZVVfgmeuN+umA1fhpDecXBbXcatBBZl4+S1G8yEGHTriyLQlbQ= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786028636; c=relaxed/simple; bh=cYbc1CQoQc5pfYhgLv/MYSip5jRcSrSImN64FkQfEWk=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=oA0H/Q+c31omG7P+YXLDzAVYcCdqgUsQeLCz2nbBLay1Jkj4dh0GfvfRB07s6mTt5RaXuTKdaDzu9fdhglMRrcaQ8HGF4M7OfWj54yPHa61JlrOcINvjdPEeCEXdZ6D8d4K9By8uhTwxnmdAfxN8xYn0SuuArBXwj3iJkH6fDXQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=iD59FJdt; arc=none smtp.client-ip=209.85.210.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="iD59FJdt" Received: by mail-pf1-f175.google.com with SMTP id d2e1a72fcca58-8453427d3f4so2713734b3a.3 for ; Thu, 06 Aug 2026 08:03:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786028634; x=1786633434; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=uJS+V1CTVjT7mHslf1lQhCi1CZlnPDrIe8kNgbgFr+I=; b=iD59FJdtqQPsbHxMZmzV4/4lrhB8zzIiu89v9R7PF4eug1Z9Ksth50eFrXX5CfoA4I lRDJ3r8A8fxAlTycxueiLAx1CQrpJXUmjWYeyIFuv64BOXKksN5hqDYgG5qfNEcyNMay pHpHTojeoRUOo1bmhxbSq6aQfiD/NdaInvcLNnhKo7fwQklBCqzTZhJfroSusClRfH1T dC8ZkGxHxdaVuSO0od2xaPpgzZMwhMl4UjXfCntWY1QOatPlsvVCio8EVaYFVb5fVNFS zoJ9PY12sO9HWx9f2rWe1wQgTJRUiSbXi93t+Z7evs+gUPCq/AYoIJQ6Zpei64Ou5+lQ 3avQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786028634; x=1786633434; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=uJS+V1CTVjT7mHslf1lQhCi1CZlnPDrIe8kNgbgFr+I=; b=ksuQfn0jLuUS9ndyxqyVFUvTLZy++c5veN0l0rEj8oyy3jLBwIHmY/J3Ql6ULRLS92 I35CfXcdxli/vRL43iLWeUkLTM2unPb47fIrvnTvD8sgh+mhJX4ecbh4H7T3PSH3c34l VcS/ysC7w5sO58uhrbo5QZEm+u+WpIooDNZc3Ta/9XxNIi7L537baO7+4mR5z+zrdLUV iaYImI5ZEB/pEdvy/y7otoDhPrZiyINZ0J49Z8TtTvlZUOlr5k0o6WB6Op7Wce/5wg4K hBJoLBTHqGJw4dlCA+BMIj5t+l8y9kkS4AGAl+yBXu51oOvg3xkt8lQFxPnFq3rkxLP5 /vnA== X-Forwarded-Encrypted: i=1; AHgh+Rq16K6THRAhWsOzFliAA4UnXT58bMzNYJsU5HvH6/LsnlSWfO9mx39VLZ00KFJSAaNcGncYxAnL+i2xjoE=@vger.kernel.org X-Gm-Message-State: AOJu0Yzgrb2HCXOMR9XRcCntkAp9pU22jHmvBVDQdtE4AYk4m1pF1MDN a28pLYxdsmgame/P6VQLVzxLPSsTPz2E2S8mXRFxKpXEtspTSJfTJvOF X-Gm-Gg: AR+sD12xQOdjrwMIH/qhxueMTVc3p53tR8HYwBaIhMwf08FyuHyp16KfovRF2Qe8QXS iLu8D8CEMc2ESvDX+TV80MP6o2wyW0YwZobnueb0huaz8/+HVaMeQ1nu2XDhChkVare3i6A9KxK MSwV+IBPuvlMknBa3xATpA3sk0hQEXPJsW87zJDA+4Qce57JQriSGrOkPrhBhDR085tOlamW40Z cdV4UjPezSB1wZLGWTtkRUdDxhdei5aNw/lYI4ADgerPORGmQ7e6GSYF0Xi9gBtChHo1iStCwIv HgQ3faiwEiidGCVETIQJuNhaoeKdW+Zl/pcY/G3zQ77rbrdaiZbnUap9cM8GIvtCYFLs7XHf/UG uMbHtgtPRtqtuIM/bP3FoaPpGbCW1FTaO+GoR75rtAZBEVztEjeiu9QP+b5CEUkokNMwhyva12C yKpFAIn0lN5KhdjQctXm2WeP1G8L1RzUXZsK53RKUe7SjKbFVXWVFM7727ydvDNGHa9XAKCkRrs KsFr0wRu1A8OJlvNxuXS8z70vKRLY/urLpEl5yivv6/Xp0xQ91Km6YZgKHKbV/P9g== X-Received: by 2002:a05:6a00:2383:b0:848:44d2:72a2 with SMTP id d2e1a72fcca58-84f2e009b49mr13219705b3a.27.1786028633604; Thu, 06 Aug 2026 08:03:53 -0700 (PDT) Received: from EAIT-H54D9Q2FJQ.eait.uq.edu.au ([130.102.10.60]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84f456ba8b7sm1573356b3a.29.2026.08.06.08.03.49 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Thu, 06 Aug 2026 08:03:53 -0700 (PDT) From: Yu Zhang To: "Michael S . Tsirkin" , Jason Wang , =?UTF-8?q?Eugenio=20P=C3=A9rez?= Cc: virtualization@lists.linux.dev, kvm@vger.kernel.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Yu Zhang Subject: [PATCH] vhost-vdpa: drop the parent's vq callback before the call fd is released Date: Fri, 7 Aug 2026 01:03:23 +1000 Message-ID: <20260806150323.2154-1-yuz08559@gmail.com> X-Mailer: git-send-email 2.55.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit VHOST_SET_VRING_CALL releases the previous call eventfd inside vhost_vring_ioctl() -- it swaps the new context into vq->call_ctx.ctx and then eventfd_ctx_put()s the old one, which is a synchronous kfree(). The parent vdpa device is only told about the change afterwards, when vhost_vdpa_vring_ioctl() reaches ops->set_vq_cb(). Parent drivers cache the pointer handed to them in vdpa_callback::trigger and do not take a reference on it, so throughout that window the parent holds a dangling eventfd_ctx and may signal it. The documentation added with the field describes what signalling it means but says nothing about how long it stays valid. This is the same hazard that "vhost_vdpa: assign irq bypass producer token correctly" addressed for the irq bypass producer token, by moving vhost_vdpa_unsetup_vq_irq() ahead of the vhost_vring_ioctl() call. The producer token was only one of the two consumers of that pointer; the one the parent keeps via ->set_vq_cb() was left behind the free. With VDUSE the window is directly reachable from userspace, because the device emulation daemon can inject an interrupt at any time from a different fd, and neither side shares a lock with the other: VDUSE takes vq->irq_lock, vhost takes vhost_dev.mutex + vq->mutex. BUG: KASAN: slab-use-after-free in _raw_spin_lock_irqsave+0x76/0xe0 Write of size 4 at addr ffff8881084e8788 by task vduse_uaf/2987 _raw_spin_lock_irqsave+0x76/0xe0 eventfd_signal_mask+0x69/0x120 vduse_dev_ioctl+0x337/0x1a60 <- vduse_vq_signal_irqfd(), inlined __x64_sys_ioctl+0x120/0x170 <- VDUSE_VQ_INJECT_IRQ Allocated by task 2986: do_eventfd+0x50/0x200 __x64_sys_eventfd2+0x2e/0x40 kmalloc-64, freed 64-byte region [ffff8881084e8780, ffff8881084e87c0) One thread loops VHOST_SET_VRING_CALL on /dev/vhost-vdpa-N with a fresh eventfd and then unbinds it, while another loops VDUSE_VQ_INJECT_IRQ on /dev/vduse/. This reproduces in 5 out of 5 ten-second runs on v7.1.6 and 3 out of 3 on v7.2-rc6. With the patch there are no reports in 3 out of 3 runs on either, while the same workload still gets ~30000 interrupts per run delivered into live eventfds, so the path is still being exercised. Tell the parent to drop the callback before vhost_vring_ioctl() can free the eventfd, mirroring what is already done for the bypass producer, and restore it if the ioctl fails -- on failure the swap never happened, the old context is still installed, and leaving the parent without a callback would silently drop that vq's interrupts. Fixes: 5e68470f4e80 ("vdpa: Add eventfd for the vdpa callback") Signed-off-by: Yu Zhang --- drivers/vhost/vdpa.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index ac55275..b7833bd 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -714,13 +714,34 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, if (ops->get_status(vdpa) & VIRTIO_CONFIG_S_DRIVER_OK) vhost_vdpa_unsetup_vq_irq(v, idx); + /* + * The parent caches call_ctx.ctx in cb.trigger without + * holding a reference, so it has to stop using it + * before vhost_vring_ioctl() drops the last one. + */ + cb.callback = NULL; + cb.private = NULL; + cb.trigger = NULL; + ops->set_vq_cb(vdpa, idx, &cb); } break; } r = vhost_vring_ioctl(&v->vdev, cmd, argp); - if (r) + if (r) { + /* + * A failure here means the swap never happened and the old + * context is still installed, so give the parent back the + * callback that was torn down above. + */ + if (cmd == VHOST_SET_VRING_CALL && vq->call_ctx.ctx) { + cb.callback = vhost_vdpa_virtqueue_cb; + cb.private = vq; + cb.trigger = vq->call_ctx.ctx; + ops->set_vq_cb(vdpa, idx, &cb); + } return r; + } switch (cmd) { case VHOST_SET_VRING_ADDR: -- 2.43.0