From 254a8b2c7c9e726d05cf8e95fcdcddc7442fc166 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 21 Oct 2024 11:50:37 +0200 Subject: [PATCH] 5.4-stable patches added patches: erofs-fix-lz4-inplace-decompression.patch nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch --- .../erofs-fix-lz4-inplace-decompression.patch | 128 ++++++++++ ...ry-read-errors-from-nilfs_find_entry.patch | 231 ++++++++++++++++++ queue-5.4/series | 2 + 3 files changed, 361 insertions(+) create mode 100644 queue-5.4/erofs-fix-lz4-inplace-decompression.patch create mode 100644 queue-5.4/nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch diff --git a/queue-5.4/erofs-fix-lz4-inplace-decompression.patch b/queue-5.4/erofs-fix-lz4-inplace-decompression.patch new file mode 100644 index 00000000000..c78743e805a --- /dev/null +++ b/queue-5.4/erofs-fix-lz4-inplace-decompression.patch @@ -0,0 +1,128 @@ +From 3c12466b6b7bf1e56f9b32c366a3d83d87afb4de Mon Sep 17 00:00:00 2001 +From: Gao Xiang +Date: Wed, 6 Dec 2023 12:55:34 +0800 +Subject: erofs: fix lz4 inplace decompression + +From: Gao Xiang + +commit 3c12466b6b7bf1e56f9b32c366a3d83d87afb4de upstream. + +Currently EROFS can map another compressed buffer for inplace +decompression, that was used to handle the cases that some pages of +compressed data are actually not in-place I/O. + +However, like most simple LZ77 algorithms, LZ4 expects the compressed +data is arranged at the end of the decompressed buffer and it +explicitly uses memmove() to handle overlapping: + __________________________________________________________ + |_ direction of decompression --> ____ |_ compressed data _| + +Although EROFS arranges compressed data like this, it typically maps two +individual virtual buffers so the relative order is uncertain. +Previously, it was hardly observed since LZ4 only uses memmove() for +short overlapped literals and x86/arm64 memmove implementations seem to +completely cover it up and they don't have this issue. Juhyung reported +that EROFS data corruption can be found on a new Intel x86 processor. +After some analysis, it seems that recent x86 processors with the new +FSRM feature expose this issue with "rep movsb". + +Let's strictly use the decompressed buffer for lz4 inplace +decompression for now. Later, as an useful improvement, we could try +to tie up these two buffers together in the correct order. + +Reported-and-tested-by: Juhyung Park +Closes: https://lore.kernel.org/r/CAD14+f2AVKf8Fa2OO1aAUdDNTDsVzzR6ctU_oJSmTyd6zSYR2Q@mail.gmail.com +Fixes: 0ffd71bcc3a0 ("staging: erofs: introduce LZ4 decompression inplace") +Fixes: 598162d05080 ("erofs: support decompress big pcluster for lz4 backend") +Cc: stable # 5.4+ +Tested-by: Yifan Zhao +Signed-off-by: Gao Xiang +Link: https://lore.kernel.org/r/20231206045534.3920847-1-hsiangkao@linux.alibaba.com +Signed-off-by: Greg Kroah-Hartman +--- + fs/erofs/decompressor.c | 24 ++++++++++++++---------- + 1 file changed, 14 insertions(+), 10 deletions(-) + +--- a/fs/erofs/decompressor.c ++++ b/fs/erofs/decompressor.c +@@ -24,7 +24,8 @@ struct z_erofs_decompressor { + */ + int (*prepare_destpages)(struct z_erofs_decompress_req *rq, + struct list_head *pagepool); +- int (*decompress)(struct z_erofs_decompress_req *rq, u8 *out); ++ int (*decompress)(struct z_erofs_decompress_req *rq, u8 *out, ++ u8 *obase); + char *name; + }; + +@@ -114,10 +115,13 @@ static void *generic_copy_inplace_data(s + return tmp; + } + +-static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, u8 *out) ++static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, u8 *out, ++ u8 *obase) + { ++ const uint nrpages_out = PAGE_ALIGN(rq->pageofs_out + ++ rq->outputsize) >> PAGE_SHIFT; + unsigned int inputmargin, inlen; +- u8 *src; ++ u8 *src, *src2; + bool copied, support_0padding; + int ret; + +@@ -125,6 +129,7 @@ static int z_erofs_lz4_decompress(struct + return -EOPNOTSUPP; + + src = kmap_atomic(*rq->in); ++ src2 = src; + inputmargin = 0; + support_0padding = false; + +@@ -148,16 +153,15 @@ static int z_erofs_lz4_decompress(struct + if (rq->inplace_io) { + const uint oend = (rq->pageofs_out + + rq->outputsize) & ~PAGE_MASK; +- const uint nr = PAGE_ALIGN(rq->pageofs_out + +- rq->outputsize) >> PAGE_SHIFT; +- + if (rq->partial_decoding || !support_0padding || +- rq->out[nr - 1] != rq->in[0] || ++ rq->out[nrpages_out - 1] != rq->in[0] || + rq->inputsize - oend < + LZ4_DECOMPRESS_INPLACE_MARGIN(inlen)) { + src = generic_copy_inplace_data(rq, src, inputmargin); + inputmargin = 0; + copied = true; ++ } else { ++ src = obase + ((nrpages_out - 1) << PAGE_SHIFT); + } + } + +@@ -178,7 +182,7 @@ static int z_erofs_lz4_decompress(struct + if (copied) + erofs_put_pcpubuf(src); + else +- kunmap_atomic(src); ++ kunmap_atomic(src2); + return ret; + } + +@@ -248,7 +252,7 @@ static int z_erofs_decompress_generic(st + return PTR_ERR(dst); + + rq->inplace_io = false; +- ret = alg->decompress(rq, dst); ++ ret = alg->decompress(rq, dst, NULL); + if (!ret) + copy_from_pcpubuf(rq->out, dst, rq->pageofs_out, + rq->outputsize); +@@ -282,7 +286,7 @@ static int z_erofs_decompress_generic(st + dst_maptype = 2; + + dstmap_out: +- ret = alg->decompress(rq, dst + rq->pageofs_out); ++ ret = alg->decompress(rq, dst + rq->pageofs_out, dst); + + if (!dst_maptype) + kunmap_atomic(dst); diff --git a/queue-5.4/nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch b/queue-5.4/nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch new file mode 100644 index 00000000000..d5e400565c3 --- /dev/null +++ b/queue-5.4/nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch @@ -0,0 +1,231 @@ +From 08cfa12adf888db98879dbd735bc741360a34168 Mon Sep 17 00:00:00 2001 +From: Ryusuke Konishi +Date: Fri, 4 Oct 2024 12:35:31 +0900 +Subject: nilfs2: propagate directory read errors from nilfs_find_entry() + +From: Ryusuke Konishi + +commit 08cfa12adf888db98879dbd735bc741360a34168 upstream. + +Syzbot reported that a task hang occurs in vcs_open() during a fuzzing +test for nilfs2. + +The root cause of this problem is that in nilfs_find_entry(), which +searches for directory entries, ignores errors when loading a directory +page/folio via nilfs_get_folio() fails. + +If the filesystem images is corrupted, and the i_size of the directory +inode is large, and the directory page/folio is successfully read but +fails the sanity check, for example when it is zero-filled, +nilfs_check_folio() may continue to spit out error messages in bursts. + +Fix this issue by propagating the error to the callers when loading a +page/folio fails in nilfs_find_entry(). + +The current interface of nilfs_find_entry() and its callers is outdated +and cannot propagate error codes such as -EIO and -ENOMEM returned via +nilfs_find_entry(), so fix it together. + +Link: https://lkml.kernel.org/r/20241004033640.6841-1-konishi.ryusuke@gmail.com +Fixes: 2ba466d74ed7 ("nilfs2: directory entry operations") +Signed-off-by: Ryusuke Konishi +Reported-by: Lizhi Xu +Closes: https://lkml.kernel.org/r/20240927013806.3577931-1-lizhi.xu@windriver.com +Reported-by: syzbot+8a192e8d090fa9a31135@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=8a192e8d090fa9a31135 +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + fs/nilfs2/dir.c | 50 +++++++++++++++++++++++++++----------------------- + fs/nilfs2/namei.c | 39 ++++++++++++++++++++++++++------------- + fs/nilfs2/nilfs.h | 2 +- + 3 files changed, 54 insertions(+), 37 deletions(-) + +--- a/fs/nilfs2/dir.c ++++ b/fs/nilfs2/dir.c +@@ -331,6 +331,8 @@ static int nilfs_readdir(struct file *fi + * returns the page in which the entry was found, and the entry itself + * (as a parameter - res_dir). Page is returned mapped and unlocked. + * Entry is guaranteed to be valid. ++ * ++ * On failure, returns an error pointer and the caller should ignore res_page. + */ + struct nilfs_dir_entry * + nilfs_find_entry(struct inode *dir, const struct qstr *qstr, +@@ -358,22 +360,24 @@ nilfs_find_entry(struct inode *dir, cons + do { + char *kaddr = nilfs_get_page(dir, n, &page); + +- if (!IS_ERR(kaddr)) { +- de = (struct nilfs_dir_entry *)kaddr; +- kaddr += nilfs_last_byte(dir, n) - reclen; +- while ((char *) de <= kaddr) { +- if (de->rec_len == 0) { +- nilfs_error(dir->i_sb, +- "zero-length directory entry"); +- nilfs_put_page(page); +- goto out; +- } +- if (nilfs_match(namelen, name, de)) +- goto found; +- de = nilfs_next_entry(de); ++ if (IS_ERR(kaddr)) ++ return ERR_CAST(kaddr); ++ ++ de = (struct nilfs_dir_entry *)kaddr; ++ kaddr += nilfs_last_byte(dir, n) - reclen; ++ while ((char *)de <= kaddr) { ++ if (de->rec_len == 0) { ++ nilfs_error(dir->i_sb, ++ "zero-length directory entry"); ++ nilfs_put_page(page); ++ goto out; + } +- nilfs_put_page(page); ++ if (nilfs_match(namelen, name, de)) ++ goto found; ++ de = nilfs_next_entry(de); + } ++ nilfs_put_page(page); ++ + if (++n >= npages) + n = 0; + /* next page is past the blocks we've got */ +@@ -386,7 +390,7 @@ nilfs_find_entry(struct inode *dir, cons + } + } while (n != start); + out: +- return NULL; ++ return ERR_PTR(-ENOENT); + + found: + *res_page = page; +@@ -431,19 +435,19 @@ fail: + return NULL; + } + +-ino_t nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr) ++int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino) + { +- ino_t res = 0; + struct nilfs_dir_entry *de; + struct page *page; + + de = nilfs_find_entry(dir, qstr, &page); +- if (de) { +- res = le64_to_cpu(de->inode); +- kunmap(page); +- put_page(page); +- } +- return res; ++ if (IS_ERR(de)) ++ return PTR_ERR(de); ++ ++ *ino = le64_to_cpu(de->inode); ++ kunmap(page); ++ put_page(page); ++ return 0; + } + + /* Releases the page */ +--- a/fs/nilfs2/namei.c ++++ b/fs/nilfs2/namei.c +@@ -55,12 +55,20 @@ nilfs_lookup(struct inode *dir, struct d + { + struct inode *inode; + ino_t ino; ++ int res; + + if (dentry->d_name.len > NILFS_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + +- ino = nilfs_inode_by_name(dir, &dentry->d_name); +- inode = ino ? nilfs_iget(dir->i_sb, NILFS_I(dir)->i_root, ino) : NULL; ++ res = nilfs_inode_by_name(dir, &dentry->d_name, &ino); ++ if (res) { ++ if (res != -ENOENT) ++ return ERR_PTR(res); ++ inode = NULL; ++ } else { ++ inode = nilfs_iget(dir->i_sb, NILFS_I(dir)->i_root, ino); ++ } ++ + return d_splice_alias(inode, dentry); + } + +@@ -261,10 +269,11 @@ static int nilfs_do_unlink(struct inode + struct page *page; + int err; + +- err = -ENOENT; + de = nilfs_find_entry(dir, &dentry->d_name, &page); +- if (!de) ++ if (IS_ERR(de)) { ++ err = PTR_ERR(de); + goto out; ++ } + + inode = d_inode(dentry); + err = -EIO; +@@ -358,10 +367,11 @@ static int nilfs_rename(struct inode *ol + if (unlikely(err)) + return err; + +- err = -ENOENT; + old_de = nilfs_find_entry(old_dir, &old_dentry->d_name, &old_page); +- if (!old_de) ++ if (IS_ERR(old_de)) { ++ err = PTR_ERR(old_de); + goto out; ++ } + + if (S_ISDIR(old_inode->i_mode)) { + err = -EIO; +@@ -378,10 +388,12 @@ static int nilfs_rename(struct inode *ol + if (dir_de && !nilfs_empty_dir(new_inode)) + goto out_dir; + +- err = -ENOENT; +- new_de = nilfs_find_entry(new_dir, &new_dentry->d_name, &new_page); +- if (!new_de) ++ new_de = nilfs_find_entry(new_dir, &new_dentry->d_name, ++ &new_page); ++ if (IS_ERR(new_de)) { ++ err = PTR_ERR(new_de); + goto out_dir; ++ } + nilfs_set_link(new_dir, new_de, new_page, old_inode); + nilfs_mark_inode_dirty(new_dir); + new_inode->i_ctime = current_time(new_inode); +@@ -435,14 +447,15 @@ out: + */ + static struct dentry *nilfs_get_parent(struct dentry *child) + { +- unsigned long ino; ++ ino_t ino; ++ int res; + struct inode *inode; + struct qstr dotdot = QSTR_INIT("..", 2); + struct nilfs_root *root; + +- ino = nilfs_inode_by_name(d_inode(child), &dotdot); +- if (!ino) +- return ERR_PTR(-ENOENT); ++ res = nilfs_inode_by_name(d_inode(child), &dotdot, &ino); ++ if (res) ++ return ERR_PTR(res); + + root = NILFS_I(d_inode(child))->i_root; + +--- a/fs/nilfs2/nilfs.h ++++ b/fs/nilfs2/nilfs.h +@@ -233,7 +233,7 @@ static inline __u32 nilfs_mask_flags(umo + + /* dir.c */ + extern int nilfs_add_link(struct dentry *, struct inode *); +-extern ino_t nilfs_inode_by_name(struct inode *, const struct qstr *); ++int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino); + extern int nilfs_make_empty(struct inode *, struct inode *); + extern struct nilfs_dir_entry * + nilfs_find_entry(struct inode *, const struct qstr *, struct page **); diff --git a/queue-5.4/series b/queue-5.4/series index c4c9a57ed57..9307db83f5c 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -379,3 +379,5 @@ usb-serial-option-add-telit-fn920c04-mbim-compositions.patch parport-proper-fix-for-array-out-of-bounds-access.patch x86-resctrl-annotate-get_mem_config-functions-as-__init.patch x86-apic-always-explicitly-disarm-tsc-deadline-timer.patch +nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch +erofs-fix-lz4-inplace-decompression.patch -- 2.47.3