public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: JiaJia <physicalmtea@gmail.com>
To: Peter Hilber <peter.hilber@oss.qualcomm.com>,
	Michael S. Tsirkin <mst@redhat.com>,
	Jason Wang <jasowang@redhat.com>
Cc: Xuan Zhuo <xuanzhuo@linux.alibaba.com>,
	Eugenio Pérez <eperezma@redhat.com>,
	virtualization@lists.linux.dev, linux-kernel@vger.kernel.org,
	JiaJia <physicalmtea@gmail.com>
Subject: [PATCH] virtio: rtc: tear down old virtqueues before restore
Date: Fri, 17 Apr 2026 17:08:31 +0800	[thread overview]
Message-ID: <20260417214953.793956-1-physicalmtea@gmail.com> (raw)

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

                 reply	other threads:[~2026-04-17 13:50 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260417214953.793956-1-physicalmtea@gmail.com \
    --to=physicalmtea@gmail.com \
    --cc=eperezma@redhat.com \
    --cc=jasowang@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mst@redhat.com \
    --cc=peter.hilber@oss.qualcomm.com \
    --cc=virtualization@lists.linux.dev \
    --cc=xuanzhuo@linux.alibaba.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox