]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 21 Oct 2024 09:50:37 +0000 (11:50 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 21 Oct 2024 09:50:37 +0000 (11:50 +0200)
added patches:
erofs-fix-lz4-inplace-decompression.patch
nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch

queue-5.4/erofs-fix-lz4-inplace-decompression.patch [new file with mode: 0644]
queue-5.4/nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch [new file with mode: 0644]
queue-5.4/series

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 (file)
index 0000000..c78743e
--- /dev/null
@@ -0,0 +1,128 @@
+From 3c12466b6b7bf1e56f9b32c366a3d83d87afb4de Mon Sep 17 00:00:00 2001
+From: Gao Xiang <hsiangkao@linux.alibaba.com>
+Date: Wed, 6 Dec 2023 12:55:34 +0800
+Subject: erofs: fix lz4 inplace decompression
+
+From: Gao Xiang <hsiangkao@linux.alibaba.com>
+
+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 <qkrwngud825@gmail.com>
+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 <stable@vger.kernel.org> # 5.4+
+Tested-by: Yifan Zhao <zhaoyifan@sjtu.edu.cn>
+Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
+Link: https://lore.kernel.org/r/20231206045534.3920847-1-hsiangkao@linux.alibaba.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..d5e4005
--- /dev/null
@@ -0,0 +1,231 @@
+From 08cfa12adf888db98879dbd735bc741360a34168 Mon Sep 17 00:00:00 2001
+From: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+Date: Fri, 4 Oct 2024 12:35:31 +0900
+Subject: nilfs2: propagate directory read errors from nilfs_find_entry()
+
+From: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+
+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 <konishi.ryusuke@gmail.com>
+Reported-by: Lizhi Xu <lizhi.xu@windriver.com>
+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: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 **);
index c4c9a57ed575f16393407881fc252f987fca88b0..9307db83f5cc5fef222c50f187577466150fe4bb 100644 (file)
@@ -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