From: Qihang <q.h.hack.winter@gmail.com>
To: Miklos Szeredi <miklos@szeredi.hu>
Cc: "German Maglione" <gmaglione@redhat.com>,
"Vivek Goyal" <vgoyal@redhat.com>,
"Stefan Hajnoczi" <stefanha@redhat.com>,
"Eugenio Pérez" <eperezma@redhat.com>,
fuse-devel@lists.linux.dev, virtualization@lists.linux.dev,
linux-fsdevel@vger.kernel.org, stable@vger.kernel.org,
Qihang <q.h.hack.winter@gmail.com>
Subject: [PATCH] fuse: virtio-fs: don't process completions of aborted requests
Date: Tue, 20 Oct 2026 21:53:50 +0800 [thread overview]
Message-ID: <20261020135350.7751-1-q.h.hack.winter@gmail.com> (raw)
fuse_chan_abort() ends every request on the processing lists with
-ECONNABORTED, but virtio-fs keeps the request itself as the
used-buffer token of the virtqueue. When the host stalls a request
until the request timeout aborts the connection and then completes the
descriptor, virtio_fs_requests_done_work() gets the already freed
request from virtqueue_get_buf() and moves its list node
(list_move_tail()) and reads its arguments (copy_args_from_argbuf()),
a use-after-free in the guest kernel.
Let the transport know about the abort and skip completions of requests
that the abort has already ended. This is safe because any request
still represented by a virtqueue token when the abort starts must have
been queued before virtio_fs_abort() set aborted:
virtio_fs_enqueue_req() checks connected, adds the request to the
virtqueue, and puts it on the processing list while holding fsvq->lock.
virtio_fs_abort() takes the same lock before setting connected = false
and aborted = true, and fuse_chan_abort() only splices and ends the
processing lists after the transport callback returned. A racing
enqueue therefore either completes before the abort and is subsequently
ended by it, or observes connected == false and returns -ENOTCONN
without creating a token.
in_flight is transport-owned accounting: it is incremented when a
token is queued and must still be dropped when the virtqueue completion
is consumed, even though the FUSE request itself has already been ended
by the abort.
Fixes: a62a8ef9d97d ("virtio-fs: add virtiofs filesystem")
Cc: stable@vger.kernel.org
Signed-off-by: Qihang <q.h.hack.winter@gmail.com>
---
fs/fuse/dev.c | 4 ++++
fs/fuse/fuse_dev_i.h | 5 +++++
fs/fuse/virtio_fs.c | 26 ++++++++++++++++++++++++++
3 files changed, 35 insertions(+)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 4fec31fc0b84..90b30c5034c4 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2178,6 +2178,10 @@ void fuse_chan_abort(struct fuse_chan *fch, bool abort_with_err)
spin_unlock(&fch->bg_lock);
fuse_chan_set_initialized(fch, NULL);
+
+ if (fiq->ops->abort)
+ fiq->ops->abort(fiq);
+
list_for_each_entry(fud, &fch->devices, entry) {
struct fuse_pqueue *fpq = &fud->pq;
diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h
index 4b412a76225f..47697adf0eb6 100644
--- a/fs/fuse/fuse_dev_i.h
+++ b/fs/fuse/fuse_dev_i.h
@@ -148,6 +148,11 @@ struct fuse_iqueue_ops {
*/
void (*send_req)(struct fuse_iqueue *fiq, struct fuse_req *req);
+ /**
+ * @abort: Abort transport-specific state
+ */
+ void (*abort)(struct fuse_iqueue *fiq);
+
/**
* @release: Clean up when fuse_iqueue is destroyed
*/
diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c
index f15e516ebcb5..2db5faf5d865 100644
--- a/fs/fuse/virtio_fs.c
+++ b/fs/fuse/virtio_fs.c
@@ -55,6 +55,7 @@ struct virtio_fs_vq {
struct work_struct dispatch_work;
struct fuse_dev *fud;
bool connected;
+ bool aborted;
long in_flight;
struct completion in_flight_zero; /* No inflight requests */
struct kobject *kobj;
@@ -842,6 +843,15 @@ static void virtio_fs_requests_done_work(struct work_struct *work)
virtqueue_disable_cb(vq);
while ((req = virtqueue_get_buf(vq, &len)) != NULL) {
+ if (fsvq->aborted) {
+ /*
+ * fuse_chan_abort() ended every request this
+ * queue still owns; the token may already be
+ * freed, do not touch it.
+ */
+ dec_in_flight_req(fsvq);
+ continue;
+ }
if (!virtio_fs_verify_response(req, len)) {
req->out.h.error = -EIO;
req->out.h.len = sizeof(struct fuse_out_header);
@@ -1541,10 +1551,26 @@ static void virtio_fs_send_req(struct fuse_iqueue *fiq, struct fuse_req *req)
}
}
+static void virtio_fs_abort(struct fuse_iqueue *fiq)
+{
+ struct virtio_fs *fs = fiq->priv;
+ int i;
+
+ for (i = 0; i < fs->nvqs; i++) {
+ struct virtio_fs_vq *fsvq = &fs->vqs[i];
+
+ spin_lock(&fsvq->lock);
+ fsvq->connected = false;
+ fsvq->aborted = true;
+ spin_unlock(&fsvq->lock);
+ }
+}
+
static const struct fuse_iqueue_ops virtio_fs_fiq_ops = {
.send_forget = virtio_fs_send_forget,
.send_interrupt = virtio_fs_send_interrupt,
.send_req = virtio_fs_send_req,
+ .abort = virtio_fs_abort,
.release = virtio_fs_fiq_release,
};
base-commit: ac86e1112a6a
--
2.51.0
reply other threads:[~2026-10-20 13:54 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=20261020135350.7751-1-q.h.hack.winter@gmail.com \
--to=q.h.hack.winter@gmail.com \
--cc=eperezma@redhat.com \
--cc=fuse-devel@lists.linux.dev \
--cc=gmaglione@redhat.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=miklos@szeredi.hu \
--cc=stable@vger.kernel.org \
--cc=stefanha@redhat.com \
--cc=vgoyal@redhat.com \
--cc=virtualization@lists.linux.dev \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.