[Devel] [PATCH] fs/fuse kio: fix stack overrun in request_end()
Pavel Butsykin
pbutsykin at virtuozzo.com
Fri May 31 11:39:43 MSK 2019
Unexpectedly, request_end() function turned out to be recursive, which leads to
stack overrun:
[76293.902783] [<ffffffffc0faa4ff>] request_end+0x334/0x3cf [fuse]
[76293.902789] [<ffffffffc0fdc9d3>] pcs_fuse_submit+0x49d/0x4f5 [fuse_kio_pcs]
[76293.902795] [<ffffffffc0fbd775>] ? fuse_flush_writepages+0x8f/0x8f [fuse]
[76293.902800] [<ffffffffc0fdd745>] kpcs_req_send+0x210/0x3bb [fuse_kio_pcs]
[76293.902805] [<ffffffffc0faa17b>] flush_bg_queue+0x1ba/0x20a [fuse]
[76293.902810] [<ffffffffc0faa4ff>] request_end+0x334/0x3cf [fuse]
[76293.902816] [<ffffffffc0fdc9d3>] pcs_fuse_submit+0x49d/0x4f5 [fuse_kio_pcs]
[76293.902818] [<ffffffff8c57cdd0>] ? debug_check_no_locks_freed+0x2a0/0x2a0
[76293.902824] [<ffffffffc0fbd775>] ? fuse_flush_writepages+0x8f/0x8f [fuse]
[76293.902829] [<ffffffffc0fdd745>] kpcs_req_send+0x210/0x3bb [fuse_kio_pcs]
[76293.902834] [<ffffffffc0faa17b>] flush_bg_queue+0x1ba/0x20a [fuse]
[76293.902839] [<ffffffffc0faa4ff>] request_end+0x334/0x3cf [fuse]
To fix this, let's call request_end() inside kpcs_req_send() synchronously for
background requests.
Signed-off-by: Pavel Butsykin <pbutsykin at virtuozzo.com>
---
fs/fuse/kio/pcs/pcs_client_types.h | 6 +++++
fs/fuse/kio/pcs/pcs_fuse_kdirect.c | 52 ++++++++++++++++++++++++++++++++++----
2 files changed, 53 insertions(+), 5 deletions(-)
diff --git a/fs/fuse/kio/pcs/pcs_client_types.h b/fs/fuse/kio/pcs/pcs_client_types.h
index 5e72eb0fac3e..e40cd3141896 100644
--- a/fs/fuse/kio/pcs/pcs_client_types.h
+++ b/fs/fuse/kio/pcs/pcs_client_types.h
@@ -72,6 +72,12 @@ struct pcs_dentry_info {
struct list_head kq;
spinlock_t kq_lock;
+ struct {
+ struct work_struct work;
+ struct list_head queue;
+ spinlock_t lock;
+ } async_req_end;
+
struct fuse_io_cnt stat;
};
diff --git a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
index 4dad29418e0d..bf878161bb30 100644
--- a/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
+++ b/fs/fuse/kio/pcs/pcs_fuse_kdirect.c
@@ -310,7 +310,8 @@ static int fuse_pcs_kdirect_claim_op(struct fuse_conn *fc, struct file *file,
fuse_put_request(fc, req);
return err;
}
-static void fuse_size_grow_work(struct work_struct *w);
+static void fuse_size_grow_work(struct work_struct *w);
+static void fuse_async_reqs_finish(struct work_struct *w);
static int kpcs_do_file_open(struct fuse_conn *fc, struct file *file, struct inode *inode)
{
@@ -362,6 +363,9 @@ static int kpcs_do_file_open(struct fuse_conn *fc, struct file *file, struct ino
kfree(di);
return -ENOMEM;
}
+ spin_lock_init(&di->async_req_end.lock);
+ INIT_LIST_HEAD(&di->async_req_end.queue);
+ INIT_WORK(&di->async_req_end.work, fuse_async_reqs_finish);
pcs_mapping_init(&pfc->cc, &di->mapping);
pcs_set_fileinfo(di, &info);
@@ -428,6 +432,7 @@ void kpcs_inode_release(struct fuse_inode *fi)
return;
BUG_ON(!list_empty(&di->size.queue));
+ BUG_ON(!list_empty(&di->async_req_end.queue));
pcs_mapping_invalidate(&di->mapping);
pcs_mapping_deinit(&di->mapping);
/* TODO: properly destroy dentry info here!! */
@@ -844,6 +849,39 @@ static void _pcs_ff_free_end(struct fuse_conn *fc, struct fuse_req *req)
r->end(fc, req);
}
+static void fuse_async_reqs_finish(struct work_struct *w)
+{
+ struct pcs_dentry_info *di = container_of(w, struct pcs_dentry_info,
+ async_req_end.work);
+ struct fuse_conn *fc = get_fuse_conn(&di->inode->inode);
+ struct fuse_req *req, *next;
+ LIST_HEAD(pending_reqs);
+
+ spin_lock(&di->async_req_end.lock);
+ list_splice_tail_init(&di->async_req_end.queue, &pending_reqs);
+ spin_unlock(&di->async_req_end.lock);
+
+ list_for_each_entry_safe(req, next, &pending_reqs, list) {
+ list_del_init(&req->list);
+
+ TRACE("async req end: %p op:%d err:%d, di: %p\n",
+ req, req->in.h.opcode, req->out.h.error, di);
+ request_end(fc, req);
+ }
+}
+
+static inline void request_end_queue(struct pcs_dentry_info *di,
+ struct fuse_req *req)
+{
+ WARN_ON_ONCE(!test_bit(FR_BACKGROUND, &req->flags));
+
+ spin_lock(&di->async_req_end.lock);
+ if (list_empty(&di->async_req_end.queue))
+ queue_work(pcs_wq, &di->async_req_end.work);
+ list_add_tail(&req->list, &di->async_req_end.queue);
+ spin_unlock(&di->async_req_end.lock);
+}
+
static bool kqueue_insert(struct pcs_dentry_info *di, struct fuse_file *ff,
struct fuse_req *req)
{
@@ -959,7 +997,7 @@ out:
}
static void pcs_fuse_submit(struct pcs_fuse_cluster *pfc, struct fuse_req *req,
- struct fuse_file *ff)
+ struct fuse_file *ff, bool bg)
{
struct pcs_fuse_req *r = pcs_req_from_fuse(req);
struct fuse_inode *fi = get_fuse_inode(req->io_inode);
@@ -1052,7 +1090,10 @@ static void pcs_fuse_submit(struct pcs_fuse_cluster *pfc, struct fuse_req *req,
error:
DTRACE("do fuse_request_end req:%p op:%d err:%d\n", req, req->in.h.opcode, req->out.h.error);
- request_end(pfc->fc, req);
+ if (bg)
+ request_end_queue(di, req);
+ else
+ request_end(pfc->fc, req);
return;
submit:
@@ -1123,7 +1164,8 @@ static void _pcs_shrink_end(struct fuse_conn *fc, struct fuse_req *req)
TRACE("resubmit %p\n", &r->req);
list_del_init(&ireq->list);
- pcs_fuse_submit(pfc, &r->req, r->req.ff);
+ pcs_fuse_submit(pfc, &r->req, r->req.ff,
+ test_bit(FR_BACKGROUND, &r->req.flags));
}
}
@@ -1275,7 +1317,7 @@ static void kpcs_req_send(struct fuse_conn* fc, struct fuse_req *req,
atomic_inc(&req->count);
__clear_bit(FR_PENDING, &req->flags);
- pcs_fuse_submit(pfc, req, ff ? : req->ff);
+ pcs_fuse_submit(pfc, req, ff ? : req->ff, bg);
if (!bg)
wait_event(req->waitq,
test_bit(FR_FINISHED, &req->flags) && !req->end);
--
2.15.1
More information about the Devel
mailing list