patch "staging: erofs: compressed_pages should not be accessed again after" added to staging-testing
Subsystems (2)
This is a note to let you know that I've just added the patch titled
staging: erofs: compressed_pages should not be accessed again after
to my staging git tree which can be found at
git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
in the staging-testing branch.
The patch will show up in the next release of the linux-next tree (usually sometime within the next 24 hours during the week.)
The patch will be merged to the staging-next branch sometime soon, after it passes testing, and the merge window is open.
If you have any questions about this process, please let me know.
From af692e117cb8cd9d3d844d413095775abc1217f9 Mon Sep 17 00:00:00 2001
From: Gao Xiang <[email protected]>
Date: Wed, 27 Feb 2019 13:33:30 +0800
Subject: staging: erofs: compressed_pages should not be accessed again after
freed
This patch resolves the following page use-after-free issue, z_erofs_vle_unzip:
...
for (i = 0; i < nr_pages; ++i) {
...
z_erofs_onlinepage_endio(page); (1)
}
for (i = 0; i < clusterpages; ++i) {
page = compressed_pages[i];
if (page->mapping == mngda) (2)
continue;
/* recycle all individual staging pages */
(void)z_erofs_gather_if_stagingpage(page_pool, page); (3)
WRITE_ONCE(compressed_pages[i], NULL);
}
...After (1) is executed, page is freed and could be then reused, if compressed_pages is scanned after that, it could fall info (2) or
(3) by mistake and that could finally be in a mess.
This patch aims to solve the above issue only with little changes as much as possible in order to make the fix backport easier.
--- drivers/staging/erofs/unzip_vle.c | 38 ++++++++++++++------------- drivers/staging/erofs/unzip_vle.h | 3 +-- drivers/staging/erofs/unzip_vle_lz4.c | 19 ++++++-------- 3 files changed, 29 insertions(+), 31 deletions(-)
drivers/staging/erofs/unzip_vle.c +20 -18
@@ -986,11 +986,10 @@ static int z_erofs_vle_unzip(struct super_block *sb,if (llen > grp->llen) llen = grp->llen; - err =z_erofs_vle_unzip_fast_percpu (compressed_pages, - clusterpages, pages, llen, work->pageofs, - z_erofs_onlinepage_endio); + err =z_erofs_vle_unzip_fast_percpu (compressed_pages, clusterpages, + pages, llen, work->pageofs);if (err != -ENOTSUPP ) - goto out_percpu; + goto out;if (sparsemem_pages >= nr_pages) goto skip_allocpage;
@@ -1011,8 +1010,25 @@ static int z_erofs_vle_unzip(struct super_block *sb,erofs_vunmap (vout, nr_pages); out: +/* must handle all compressed pages before endding pages */ +for (i =0 ; i < clusterpages; ++i) { + page = compressed_pages[i]; + +#ifdef EROFS_FS_HAS_MANAGED_CACHE +if (page->mapping ==MNGD_MAPPING (sbi)) +continue ; +#endif +/* recycle all individual staging pages */ + (void )z_erofs_gather_if_stagingpage (page_pool, page); + +WRITE_ONCE (compressed_pages[i],NULL ); + } +for (i =0 ; i < nr_pages; ++i) { page = pages[i]; +if (!page) +continue ; +DBG_BUGON (!page->mapping);/* recycle all individual staging pages */
@@ -1025,20 +1041,6 @@ static int z_erofs_vle_unzip(struct super_block *sb,z_erofs_onlinepage_endio (page); } -out_percpu: -for (i =0 ; i < clusterpages; ++i) { - page = compressed_pages[i]; - -#ifdef EROFS_FS_HAS_MANAGED_CACHE -if (page->mapping ==MNGD_MAPPING (sbi)) -continue ; -#endif -/* recycle all individual staging pages */ - (void )z_erofs_gather_if_stagingpage (page_pool, page); - -WRITE_ONCE (compressed_pages[i],NULL ); - } -if (pages == z_pagemap_global)mutex_unlock (&z_pagemap_global_lock);else if (unlikely (pages != pages_onstack))
drivers/staging/erofs/unzip_vle.h +1 -2
@@ -218,8 +218,7 @@ int z_erofs_vle_plain_copy(struct page **compressed_pages,int z_erofs_vle_unzip_fast_percpu (struct page **compressed_pages,unsigned int clusterpages,struct page **pages,unsigned int outlen, -unsigned short pageofs, -void (*endio)(struct page *)); +unsigned short pageofs); int z_erofs_vle_unzip_vmap(struct page **compressed_pages, unsigned int clusterpages, void *vaddr, unsigned int llen,
drivers/staging/erofs/unzip_vle_lz4.c +8 -11
@@ -125,8 +125,7 @@ int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages,unsigned int clusterpages, struct page **pages, unsigned int outlen, - unsigned short pageofs, - void (*endio)(struct page *)) + unsigned short pageofs) { void *vin, *vout;unsigned int nr_pages, i, j;
@@ -148,19 +147,16 @@ int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages, ret =z_erofs_unzip_lz4 (vin, vout + pageofs, clusterpages *PAGE_SIZE , outlen); -if (ret >=0 ) { - outlen = ret; - ret =0 ; - } +if (ret <0 ) + goto out; + ret =0 ;for (i =0 ; i < nr_pages; ++i) { j =min ((unsigned int )PAGE_SIZE - pageofs, outlen);if (pages[i]) { -if (ret <0 ) { -SetPageError (pages[i]); - }else if (clusterpages ==1 && - pages[i] == compressed_pages[0 ]) { +if (clusterpages ==1 && + pages[i] == compressed_pages[0 ]) {memcpy (vin + pageofs, vout + pageofs, j); }else {void *dst =kmap_atomic (pages[i]);
@@ -168,12 +164,13 @@ int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages,memcpy (dst + pageofs, vout + pageofs, j);kunmap_atomic (dst); } -endio (pages[i]); } vout +=PAGE_SIZE ; outlen -= j; pageofs =0 ; } + +out:preempt_enable ();if (clusterpages ==1 )
Signature · 1 line
2.21.0