commit acb383f1dcb4f1e79b66d4be3a0b6f519a957b0d upstream.
Richard observed a forever loop of erofs_read_raw_page() [1] which can be generated by forcely setting ->u.i_blkaddr to 0xdeadbeef (as my understanding block layer can handle access beyond end of device correctly).
After digging into that, it seems the problem is highly related with directories and then I found the root cause is an improper error handling in erofs_readdir().
Let's fix it now.
[1] https://lore.kernel.org/r/1163995781.68824.1566084358245.JavaMail.zimbra@nod...
Reported-by: Richard Weinberger richard@nod.at Fixes: 3aa8ec716e52 ("staging: erofs: add directory operations") Cc: stable@vger.kernel.org # 4.19+ Reviewed-by: Chao Yu yuchao0@huawei.com Signed-off-by: Gao Xiang gaoxiang25@huawei.com Link: https://lore.kernel.org/r/20190818125457.25906-1-hsiangkao@aol.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org [ Gao Xiang: Since earlier kernels don't define EFSCORRUPTED, let's use original error code instead. ] Signed-off-by: Gao Xiang gaoxiang25@huawei.com --- drivers/staging/erofs/dir.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/staging/erofs/dir.c b/drivers/staging/erofs/dir.c index dbf6a151886c..b11cecd0a21d 100644 --- a/drivers/staging/erofs/dir.c +++ b/drivers/staging/erofs/dir.c @@ -99,8 +99,15 @@ static int erofs_readdir(struct file *f, struct dir_context *ctx) unsigned int nameoff, maxsize;
dentry_page = read_mapping_page(mapping, i, NULL); - if (IS_ERR(dentry_page)) - continue; + if (dentry_page == ERR_PTR(-ENOMEM)) { + err = -ENOMEM; + break; + } else if (IS_ERR(dentry_page)) { + errln("fail to readdir of logical block %u of nid %llu", + i, EROFS_V(dir)->nid); + err = PTR_ERR(dentry_page); + break; + }
de = (struct erofs_dirent *)kmap(dentry_page);
commit ee45197c807895e156b2be0abcaebdfc116487c8 upstream.
As reported by erofs_utils fuzzer, a logical page can belong to at most 2 compressed clusters, if one compressed cluster is corrupted, but the other has been ready in submitting chain.
The chain needs to submit anyway in order to keep the page working properly (page unlocked with PG_error set, PG_uptodate not set).
Let's fix it now.
Fixes: 3883a79abd02 ("staging: erofs: introduce VLE decompression support") Cc: stable@vger.kernel.org # 4.19+ Signed-off-by: Gao Xiang gaoxiang25@huawei.com Reviewed-by: Chao Yu yuchao0@huawei.com Link: https://lore.kernel.org/r/20190819103426.87579-2-gaoxiang25@huawei.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org [ Gao Xiang: Manually backport to v5.3.y stable. ] Signed-off-by: Gao Xiang gaoxiang25@huawei.com --- drivers/staging/erofs/unzip_vle.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/staging/erofs/unzip_vle.c b/drivers/staging/erofs/unzip_vle.c index f0dab81ff816..438d78db69cf 100644 --- a/drivers/staging/erofs/unzip_vle.c +++ b/drivers/staging/erofs/unzip_vle.c @@ -1498,19 +1498,18 @@ static int z_erofs_vle_normalaccess_readpage(struct file *file, err = z_erofs_do_read_page(&f, page, &pagepool); (void)z_erofs_vle_work_iter_end(&f.builder);
- if (err) { + /* if some compressed cluster ready, need submit them anyway */ + z_erofs_submit_and_unzip(&f, &pagepool, true); + + if (err) errln("%s, failed to read, err [%d]", __func__, err); - goto out; - }
- z_erofs_submit_and_unzip(&f, &pagepool, true); -out: if (f.map.mpage) put_page(f.map.mpage);
/* clean up the remaining free pages */ put_pages_list(&pagepool); - return 0; + return err; }
static int z_erofs_vle_normalaccess_readpages(struct file *filp,
commit 138e1a0990e80db486ab9f6c06bd5c01f9a97999 upstream.
As reported by erofs-utils fuzzer, these error handling path will be entered to handle corrupted images.
Lack of erofs_workgroup_puts will cause unmounting unsuccessfully.
Fix these return values to EFSCORRUPTED as well.
Fixes: 3883a79abd02 ("staging: erofs: introduce VLE decompression support") Cc: stable@vger.kernel.org # 4.19+ Signed-off-by: Gao Xiang gaoxiang25@huawei.com Reviewed-by: Chao Yu yuchao0@huawei.com Link: https://lore.kernel.org/r/20190819103426.87579-4-gaoxiang25@huawei.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org [ Gao Xiang: Older kernel versions don't have length validity check and EFSCORRUPTED, thus backport pageofs check for now. ] Signed-off-by: Gao Xiang gaoxiang25@huawei.com --- drivers/staging/erofs/unzip_vle.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/staging/erofs/unzip_vle.c b/drivers/staging/erofs/unzip_vle.c index 438d78db69cf..5069cc4c9fb8 100644 --- a/drivers/staging/erofs/unzip_vle.c +++ b/drivers/staging/erofs/unzip_vle.c @@ -393,7 +393,11 @@ z_erofs_vle_work_lookup(const struct z_erofs_vle_work_finder *f) /* if multiref is disabled, `primary' is always true */ primary = true;
- DBG_BUGON(work->pageofs != f->pageofs); + if (work->pageofs != f->pageofs) { + DBG_BUGON(1); + erofs_workgroup_put(egrp); + return ERR_PTR(-EIO); + }
/* * lock must be taken first to avoid grp->next == NIL between
commit 598bb8913d015150b7734b55443c0e53e7189fc7 upstream.
As reported by erofs-utils fuzzer, Lookback distance should be a positive number, so it should be actually looked back rather than spinning.
Fixes: 02827e1796b3 ("staging: erofs: add erofs_map_blocks_iter") Cc: stable@vger.kernel.org # 4.19+ Signed-off-by: Gao Xiang gaoxiang25@huawei.com Reviewed-by: Chao Yu yuchao0@huawei.com Link: https://lore.kernel.org/r/20190819103426.87579-7-gaoxiang25@huawei.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org [ Gao Xiang: Since earlier kernels don't define EFSCORRUPTED, let's use EIO instead. ] Signed-off-by: Gao Xiang gaoxiang25@huawei.com --- drivers/staging/erofs/zmap.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/staging/erofs/zmap.c b/drivers/staging/erofs/zmap.c index c2359321ca13..30e6d02d30de 100644 --- a/drivers/staging/erofs/zmap.c +++ b/drivers/staging/erofs/zmap.c @@ -350,6 +350,12 @@ static int vle_extent_lookback(struct z_erofs_maprecorder *m,
switch (m->type) { case Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD: + if (!m->delta[0]) { + errln("invalid lookback distance 0 at nid %llu", + vi->nid); + DBG_BUGON(1); + return -EIO; + } return vle_extent_lookback(m, m->delta[0]); case Z_EROFS_VLE_CLUSTER_TYPE_PLAIN: map->m_flags &= ~EROFS_MAP_ZIPPED;
commit e12a0ce2fa69798194f3a8628baf6edfbd5c548f upstream.
As reported by erofs-utils fuzzer, currently, multiref (ondisk deduplication) hasn't been supported for now, we should forbid it properly.
Fixes: 3883a79abd02 ("staging: erofs: introduce VLE decompression support") Cc: stable@vger.kernel.org # 4.19+ Signed-off-by: Gao Xiang gaoxiang25@huawei.com Reviewed-by: Chao Yu yuchao0@huawei.com Link: https://lore.kernel.org/r/20190821140152.229648-1-gaoxiang25@huawei.com Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org [ Gao Xiang: Since earlier kernels don't define EFSCORRUPTED, let's use EIO instead. ] Signed-off-by: Gao Xiang gaoxiang25@huawei.com --- drivers/staging/erofs/unzip_vle.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/erofs/unzip_vle.c b/drivers/staging/erofs/unzip_vle.c index 5069cc4c9fb8..155cee68fed5 100644 --- a/drivers/staging/erofs/unzip_vle.c +++ b/drivers/staging/erofs/unzip_vle.c @@ -943,6 +943,7 @@ static int z_erofs_vle_unzip(struct super_block *sb, for (i = 0; i < nr_pages; ++i) pages[i] = NULL;
+ err = 0; z_erofs_pagevec_ctor_init(&ctor, Z_EROFS_NR_INLINE_PAGEVECS, work->pagevec, 0);
@@ -964,8 +965,17 @@ static int z_erofs_vle_unzip(struct super_block *sb, pagenr = z_erofs_onlinepage_index(page);
DBG_BUGON(pagenr >= nr_pages); - DBG_BUGON(pages[pagenr]);
+ /* + * currently EROFS doesn't support multiref(dedup), + * so here erroring out one multiref page. + */ + if (pages[pagenr]) { + DBG_BUGON(1); + SetPageError(pages[pagenr]); + z_erofs_onlinepage_endio(pages[pagenr]); + err = -EIO; + } pages[pagenr] = page; } sparsemem_pages = i; @@ -975,7 +985,6 @@ static int z_erofs_vle_unzip(struct super_block *sb, overlapped = false; compressed_pages = grp->compressed_pages;
- err = 0; for (i = 0; i < clusterpages; ++i) { unsigned int pagenr;
@@ -999,7 +1008,12 @@ static int z_erofs_vle_unzip(struct super_block *sb, pagenr = z_erofs_onlinepage_index(page);
DBG_BUGON(pagenr >= nr_pages); - DBG_BUGON(pages[pagenr]); + if (pages[pagenr]) { + DBG_BUGON(1); + SetPageError(pages[pagenr]); + z_erofs_onlinepage_endio(pages[pagenr]); + err = -EIO; + } ++sparsemem_pages; pages[pagenr] = page;
linux-stable-mirror@lists.linaro.org