public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] virtio: rtc: tear down old virtqueues before restore
@ 2026-04-17  9:08 JiaJia
  0 siblings, 0 replies; only message in thread
From: JiaJia @ 2026-04-17  9:08 UTC (permalink / raw)
  To: Peter Hilber, Michael S. Tsirkin, Jason Wang
  Cc: Xuan Zhuo, Eugenio Pérez, virtualization, linux-kernel,
	JiaJia

virtio_device_restore() resets the device and restores the negotiated
features before calling ->restore(). viortc_freeze() intentionally
leaves the existing virtqueues in place so the alarm queue can still
wake the system, but viortc_restore() immediately calls
viortc_init_vqs() without first deleting those old queues.

If virtqueue reinitialization fails on virtio-pci, the transport error
path runs vp_del_vqs() against a new vp_dev->vqs array while vdev->vqs
still holds the old virtqueues. vp_del_vqs() then looks up queue state
through that new array and can dereference a NULL info pointer in
vp_del_vq(), crashing the guest kernel during restore.

Delete any old virtqueues before rebuilding them, and clear the cached
queue pointers so later message requests fail cleanly if restore does
not complete.

Signed-off-by: JiaJia <physicalmtea@gmail.com>
---
Runtime evidence from a standard hibernation test_resume run on a guest
with virtio-rtc-pci (no fault injection):

  KASAN null-ptr-deref report in vp_del_vq.isra.0+0x70/0xd0

  Call trace:
    vp_del_vq.isra.0+0x70/0xd0
    vp_del_vqs+0xec/0x358
    vp_find_vqs_msix+0x24c/0x6a8
    vp_find_vqs+0x88/0x360
    vp_modern_find_vqs+0x20/0x90
    viortc_init_vqs+0xd0/0x128
    viortc_restore+0x28/0xb8
    virtio_device_restore_priv+0x184/0x288
    virtio_pci_restore+0x44/0x58

 drivers/virtio/virtio_rtc_driver.c | 38 ++++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 5 deletions(-)

diff --git a/drivers/virtio/virtio_rtc_driver.c b/drivers/virtio/virtio_rtc_driver.c
index a57d5e06e..c1adde27d 100644
--- a/drivers/virtio/virtio_rtc_driver.c
+++ b/drivers/virtio/virtio_rtc_driver.c
@@ -427,6 +427,15 @@ static int viortc_msg_xfer(struct viortc_vq *vq, struct viortc_msg *msg,
 	sg_init_one(out_sg, msg->req, msg->req_size);
 	sg_init_one(in_sg, msg->resp, msg->resp_cap);
 
+	if (!vq->vq) {
+		/*
+		 * Keep runtime interfaces in a safe failed state if restore
+		 * teardown removed the virtqueues.
+		 */
+		viortc_msg_release(msg);
+		return -ENODEV;
+	}
+
 	spin_lock_irqsave(&vq->lock, flags);
 
 	ret = virtqueue_add_sgs(vq->vq, sgs, 1, 1, msg, GFP_ATOMIC);
@@ -478,6 +487,17 @@ static int viortc_msg_xfer(struct viortc_vq *vq, struct viortc_msg *msg,
 	return 0;
 }
 
+static void viortc_del_vqs(struct viortc_dev *viortc)
+{
+	struct virtio_device *vdev = viortc->vdev;
+	unsigned int i;
+
+	vdev->config->del_vqs(vdev);
+
+	for (i = 0; i < ARRAY_SIZE(viortc->vqs); i++)
+		viortc->vqs[i].vq = NULL;
+}
+
 /*
  * common message handle macros for messages of different types
  */
@@ -1316,7 +1336,7 @@ static int viortc_probe(struct virtio_device *vdev)
 
 err_reset_vdev:
 	virtio_reset_device(vdev);
-	vdev->config->del_vqs(vdev);
+	viortc_del_vqs(viortc);
 
 	return ret;
 }
@@ -1332,7 +1352,7 @@ static void viortc_remove(struct virtio_device *vdev)
 	viortc_clocks_deinit(viortc);
 
 	virtio_reset_device(vdev);
-	vdev->config->del_vqs(vdev);
+	viortc_del_vqs(viortc);
 }
 
 static int viortc_freeze(struct virtio_device *dev)
@@ -1353,9 +1373,11 @@ static int viortc_restore(struct virtio_device *dev)
 	bool notify = false;
 	int ret;
 
+	viortc_del_vqs(viortc);
+
 	ret = viortc_init_vqs(viortc);
 	if (ret)
-		return ret;
+		goto err_del_vqs;
 
 	alarm_viortc_vq = &viortc->vqs[VIORTC_ALARMQ];
 	alarm_vq = alarm_viortc_vq->vq;
@@ -1364,16 +1386,22 @@ static int viortc_restore(struct virtio_device *dev)
 		ret = viortc_populate_vq(viortc, alarm_viortc_vq,
 					 VIORTC_ALARMQ_BUF_CAP, false);
 		if (ret)
-			return ret;
+			goto err_del_vqs;
 
 		notify = virtqueue_kick_prepare(alarm_vq);
 	}
 
 	virtio_device_ready(dev);
 
-	if (notify && !virtqueue_notify(alarm_vq))
+	if (notify && !virtqueue_notify(alarm_vq)) {
 		ret = -EIO;
+		goto err_del_vqs;
+	}
+
+	return ret;
 
+err_del_vqs:
+	viortc_del_vqs(viortc);
 	return ret;
 }
 
-- 
2.34.1

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-04-17 13:50 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-17  9:08 [PATCH] virtio: rtc: tear down old virtqueues before restore JiaJia

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox