fuse: fix page dereference after free
After unlock_request() pages from the ap->pages[] array may be put (e.g. by aborting the connection) and the pages can be freed. Prevent use after free by grabbing a reference to the page before calling unlock_request(). The original patch was created by Pradeep P V K. Reported-by: Pradeep P V K <ppvk@codeaurora.org> Cc: <stable@vger.kernel.org> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
parent
9a752d18c8
commit
d78092e493
|
@ -785,15 +785,16 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
|
||||||
struct page *newpage;
|
struct page *newpage;
|
||||||
struct pipe_buffer *buf = cs->pipebufs;
|
struct pipe_buffer *buf = cs->pipebufs;
|
||||||
|
|
||||||
|
get_page(oldpage);
|
||||||
err = unlock_request(cs->req);
|
err = unlock_request(cs->req);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto out_put_old;
|
||||||
|
|
||||||
fuse_copy_finish(cs);
|
fuse_copy_finish(cs);
|
||||||
|
|
||||||
err = pipe_buf_confirm(cs->pipe, buf);
|
err = pipe_buf_confirm(cs->pipe, buf);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto out_put_old;
|
||||||
|
|
||||||
BUG_ON(!cs->nr_segs);
|
BUG_ON(!cs->nr_segs);
|
||||||
cs->currbuf = buf;
|
cs->currbuf = buf;
|
||||||
|
@ -833,7 +834,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
|
||||||
err = replace_page_cache_page(oldpage, newpage, GFP_KERNEL);
|
err = replace_page_cache_page(oldpage, newpage, GFP_KERNEL);
|
||||||
if (err) {
|
if (err) {
|
||||||
unlock_page(newpage);
|
unlock_page(newpage);
|
||||||
return err;
|
goto out_put_old;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_page(newpage);
|
get_page(newpage);
|
||||||
|
@ -852,14 +853,19 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
|
||||||
if (err) {
|
if (err) {
|
||||||
unlock_page(newpage);
|
unlock_page(newpage);
|
||||||
put_page(newpage);
|
put_page(newpage);
|
||||||
return err;
|
goto out_put_old;
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock_page(oldpage);
|
unlock_page(oldpage);
|
||||||
|
/* Drop ref for ap->pages[] array */
|
||||||
put_page(oldpage);
|
put_page(oldpage);
|
||||||
cs->len = 0;
|
cs->len = 0;
|
||||||
|
|
||||||
return 0;
|
err = 0;
|
||||||
|
out_put_old:
|
||||||
|
/* Drop ref obtained in this function */
|
||||||
|
put_page(oldpage);
|
||||||
|
return err;
|
||||||
|
|
||||||
out_fallback_unlock:
|
out_fallback_unlock:
|
||||||
unlock_page(newpage);
|
unlock_page(newpage);
|
||||||
|
@ -868,10 +874,10 @@ out_fallback:
|
||||||
cs->offset = buf->offset;
|
cs->offset = buf->offset;
|
||||||
|
|
||||||
err = lock_request(cs->req);
|
err = lock_request(cs->req);
|
||||||
if (err)
|
if (!err)
|
||||||
return err;
|
err = 1;
|
||||||
|
|
||||||
return 1;
|
goto out_put_old;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
|
static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
|
||||||
|
@ -883,14 +889,16 @@ static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
|
||||||
if (cs->nr_segs >= cs->pipe->max_usage)
|
if (cs->nr_segs >= cs->pipe->max_usage)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
|
get_page(page);
|
||||||
err = unlock_request(cs->req);
|
err = unlock_request(cs->req);
|
||||||
if (err)
|
if (err) {
|
||||||
|
put_page(page);
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
fuse_copy_finish(cs);
|
fuse_copy_finish(cs);
|
||||||
|
|
||||||
buf = cs->pipebufs;
|
buf = cs->pipebufs;
|
||||||
get_page(page);
|
|
||||||
buf->page = page;
|
buf->page = page;
|
||||||
buf->offset = offset;
|
buf->offset = offset;
|
||||||
buf->len = count;
|
buf->len = count;
|
||||||
|
|
Loading…
Reference in New Issue