Skip to content

Commit 86432a6

Browse files
committed
erofs: fix unsafe pagevec reuse of hooked pclusters
There are pclusters in runtime marked with Z_EROFS_PCLUSTER_TAIL before actual I/O submission. Thus, the decompression chain can be extended if the following pcluster chain hooks such tail pcluster. As the related comment mentioned, if some page is made of a hooked pcluster and another followed pcluster, it can be reused for in-place I/O (since I/O should be submitted anyway): _______________________________________________________________ | tail (partial) page | head (partial) page | |_____PRIMARY_HOOKED___|____________PRIMARY_FOLLOWED____________| However, it's by no means safe to reuse as pagevec since if such PRIMARY_HOOKED pclusters finally move into bypass chain without I/O submission. It's somewhat hard to reproduce with LZ4 and I just found it (general protection fault) by ro_fsstressing a LZMA image for long time. I'm going to actively clean up related code together with multi-page folio adaption in the next few months. Let's address it directly for easier backporting for now. Call trace for reference: z_erofs_decompress_pcluster+0x10a/0x8a0 [erofs] z_erofs_decompress_queue.isra.36+0x3c/0x60 [erofs] z_erofs_runqueue+0x5f3/0x840 [erofs] z_erofs_readahead+0x1e8/0x320 [erofs] read_pages+0x91/0x270 page_cache_ra_unbounded+0x18b/0x240 filemap_get_pages+0x10a/0x5f0 filemap_read+0xa9/0x330 new_sync_read+0x11b/0x1a0 vfs_read+0xf1/0x190 Link: https://lore.kernel.org/r/[email protected] Fixes: 3883a79 ("staging: erofs: introduce VLE decompression support") Cc: <[email protected]> # 4.19+ Reviewed-by: Chao Yu <[email protected]> Signed-off-by: Gao Xiang <[email protected]>
1 parent 8bb7eca commit 86432a6

File tree

2 files changed

+17
-9
lines changed

2 files changed

+17
-9
lines changed

fs/erofs/zdata.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,8 @@ static bool z_erofs_try_inplace_io(struct z_erofs_collector *clt,
373373

374374
/* callers must be with collection lock held */
375375
static int z_erofs_attach_page(struct z_erofs_collector *clt,
376-
struct page *page,
377-
enum z_erofs_page_type type)
376+
struct page *page, enum z_erofs_page_type type,
377+
bool pvec_safereuse)
378378
{
379379
int ret;
380380

@@ -384,9 +384,9 @@ static int z_erofs_attach_page(struct z_erofs_collector *clt,
384384
z_erofs_try_inplace_io(clt, page))
385385
return 0;
386386

387-
ret = z_erofs_pagevec_enqueue(&clt->vector, page, type);
387+
ret = z_erofs_pagevec_enqueue(&clt->vector, page, type,
388+
pvec_safereuse);
388389
clt->cl->vcnt += (unsigned int)ret;
389-
390390
return ret ? 0 : -EAGAIN;
391391
}
392392

@@ -729,15 +729,16 @@ static int z_erofs_do_read_page(struct z_erofs_decompress_frontend *fe,
729729
tight &= (clt->mode >= COLLECT_PRIMARY_FOLLOWED);
730730

731731
retry:
732-
err = z_erofs_attach_page(clt, page, page_type);
732+
err = z_erofs_attach_page(clt, page, page_type,
733+
clt->mode >= COLLECT_PRIMARY_FOLLOWED);
733734
/* should allocate an additional short-lived page for pagevec */
734735
if (err == -EAGAIN) {
735736
struct page *const newpage =
736737
alloc_page(GFP_NOFS | __GFP_NOFAIL);
737738

738739
set_page_private(newpage, Z_EROFS_SHORTLIVED_PAGE);
739740
err = z_erofs_attach_page(clt, newpage,
740-
Z_EROFS_PAGE_TYPE_EXCLUSIVE);
741+
Z_EROFS_PAGE_TYPE_EXCLUSIVE, true);
741742
if (!err)
742743
goto retry;
743744
}

fs/erofs/zpvec.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,18 @@ static inline void z_erofs_pagevec_ctor_init(struct z_erofs_pagevec_ctor *ctor,
106106

107107
static inline bool z_erofs_pagevec_enqueue(struct z_erofs_pagevec_ctor *ctor,
108108
struct page *page,
109-
enum z_erofs_page_type type)
109+
enum z_erofs_page_type type,
110+
bool pvec_safereuse)
110111
{
111-
if (!ctor->next && type)
112-
if (ctor->index + 1 == ctor->nr)
112+
if (!ctor->next) {
113+
/* some pages cannot be reused as pvec safely without I/O */
114+
if (type == Z_EROFS_PAGE_TYPE_EXCLUSIVE && !pvec_safereuse)
115+
type = Z_EROFS_VLE_PAGE_TYPE_TAIL_SHARED;
116+
117+
if (type != Z_EROFS_PAGE_TYPE_EXCLUSIVE &&
118+
ctor->index + 1 == ctor->nr)
113119
return false;
120+
}
114121

115122
if (ctor->index >= ctor->nr)
116123
z_erofs_pagevec_ctor_pagedown(ctor, false);

0 commit comments

Comments
 (0)