[Virtio-fs] [PATCH 4/9] virtio-fs: fix use-after-free against virtio_fs_vq's fuse_dev info

Liu Bo bo.liu at linux.alibaba.com
Tue Apr 16 18:03:17 UTC 2019


From: Xiaoguang Wang <xiaoguang.wang at linux.alibaba.com>

When running xfstests generic/135, we hit below panic:
[  376.386031] Workqueue: events virtio_fs_hiprio_done_work [virtio_fs]
[  376.386086] RIP: 0010:_raw_spin_lock+0xc/0x20
[  376.386132] Code: 00 f0 0f b1 17 75 02 f3 c3 89 c6 e8 6e b6 91 ff 66
90 c3 90 66 2e 0f 1f 84 00 00 00 00 00 66 66 66 66 90 31 c0 ba 01 00 00
00 <f0> 0f b1 17 75 02 f3 c3 89 c6 e8 45 b6 91 ff 66 90 c3 66 90 66 66
[  376.386234] RSP: 0018:ffffc900024f7e50 EFLAGS: 00010246
[  376.386273] RAX: 0000000000000000 RBX: ffff888136c08000 RCX:
ffff88813b862520
[  376.386316] RDX: 0000000000000001 RSI: ffff88813b014ab0 RDI:
000000000000000c
[  376.386361] RBP: 000000000000000c R08: 000073746e657665 R09:
8080808080808080
[  376.386404] R10: 0000000000000000 R11: fffffffffffffff8 R12:
ffff88813b866600
[  376.386447] R13: 0000000000000000 R14: ffff888136eee180 R15:
ffff8881374bece8
[  376.386491] FS:  0000000000000000(0000) GS:ffff88813b840000(0000)
knlGS:0000000000000000
[  376.386539] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  376.386577] CR2: 000000000000000c CR3: 000000000200a003 CR4:
00000000000606e0
[  376.386624] Call Trace:
[  376.386693]  virtio_fs_hiprio_done_work+0x32/0xa0 [virtio_fs]
[  376.386745]  process_one_work+0x19e/0x380
[  376.386787]  worker_thread+0x4f/0x3b0
[  376.386821]  ? rescuer_thread+0x350/0x350
[  376.386858]  kthread+0xf6/0x130
[  376.386895]  ? kthread_create_worker_on_cpu+0x70/0x70
[  376.386933]  ret_from_fork+0x1f/0x30

After some investigations, the root cause is FORGET requests in
virtio-fs not being ref counted, then virtio_fs_vq's fuse_dev
info maybe freed in virtio_fs_free_devs(), but we still have
un-finished FORGET requests, see below race:

kernel issue FORGET request      |
                                 |  user issue umount operation
                                 |
                                 |  virtio_kill_sb
                                 |  ==> fuse_kill_sb_anon
                                 |  ====> fuse_sb_destroy
                                 |  ======> fuse_wait_aborted
                                 |         Note: here we won't wait FORGET
                                 |         request
                                 |  ==> virtio_fs_free_devs
                                 |      free fuse_dev info
                                 |
                                 |
FORGET request is issued to host |
                                 |
FORGET request is completed, and |
virtio_fs_hiprio_done_work() will|
be called, then use-after-free   |
against fuse_dev occurs.         |
                                 |

Reviewed-by: Liu Bo <bo.liu at linux.alibaba.com>
Reviewed-by: Joseph Qi <joseph.qi at linux.alibaba.com>
Signed-off-by: Xiaoguang Wang <xiaoguang.wang at linux.alibaba.com>
---
 fs/fuse/virtio_fs.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c
index 68fd11b..c55f1b1 100644
--- a/fs/fuse/virtio_fs.c
+++ b/fs/fuse/virtio_fs.c
@@ -181,6 +181,7 @@ static void virtio_fs_hiprio_done_work(struct work_struct *work)
 	struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq,
 						 done_work);
 	struct fuse_pqueue *fpq = &fsvq->fud->pq;
+	struct fuse_conn *fc = fsvq->fud->fc;
 	struct virtqueue *vq = fsvq->vq;
 
 	/* Free completed FUSE_FORGET requests */
@@ -191,8 +192,10 @@ static void virtio_fs_hiprio_done_work(struct work_struct *work)
 
 		virtqueue_disable_cb(vq);
 
-		while ((req = virtqueue_get_buf(vq, &len)) != NULL)
+		while ((req = virtqueue_get_buf(vq, &len)) != NULL) {
 			kfree(req);
+			fuse_drop_waiting(fc);
+		}
 	} while (!virtqueue_enable_cb(vq) && likely(!virtqueue_is_broken(vq)));
 	spin_unlock(&fpq->lock);
 }
@@ -208,6 +211,7 @@ static void virtio_fs_hiprio_dispatch_work(struct work_struct *work)
 	struct virtio_fs_vq *fsvq = container_of(work, struct virtio_fs_vq,
 						 dispatch_work.work);
 	struct fuse_pqueue *fpq = &fsvq->fud->pq;
+	struct fuse_conn *fc = fsvq->fud->fc;
 	struct virtqueue *vq = fsvq->vq;
 	struct scatterlist sg;
 	struct scatterlist *sgs[] = {&sg};
@@ -246,6 +250,7 @@ static void virtio_fs_hiprio_dispatch_work(struct work_struct *work)
 			spin_unlock(&fpq->lock);
 			return;
 		}
+		atomic_inc(&fc->num_waiting);
 
 		notify = virtqueue_kick_prepare(vq);
 		spin_unlock(&fpq->lock);
@@ -747,6 +752,7 @@ static int virtio_fs_restore(struct virtio_device *vdev)
 static void virtio_fs_wake_forget_and_unlock(struct fuse_iqueue *fiq)
 __releases(fiq->waitq.lock)
 {
+	struct fuse_conn *fc;
 	struct fuse_forget_link *link;
 	struct virtio_fs_forget *forget;
 	struct fuse_pqueue *fpq;
@@ -813,6 +819,9 @@ static void virtio_fs_wake_forget_and_unlock(struct fuse_iqueue *fiq)
 		goto out;
 	}
 
+	fc = fsvq->fud->fc;
+	atomic_inc(&fc->num_waiting);
+
 	notify = virtqueue_kick_prepare(vq);
 
 	spin_unlock(&fpq->lock);
-- 
1.8.3.1




More information about the Virtio-fs mailing list