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

queue-6.6/nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch [new file with mode: 0644]
queue-6.6/series

diff --git a/queue-6.6/nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch b/queue-6.6/nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch
new file mode 100644 (file)
index 0000000..212c9aa
--- /dev/null
@@ -0,0 +1,230 @@
+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);
+ }
+@@ -263,10 +271,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;
+@@ -361,10 +370,11 @@ static int nilfs_rename(struct mnt_idmap
+       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;
+@@ -381,10 +391,12 @@ static int nilfs_rename(struct mnt_idmap
+               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);
+               inode_set_ctime_current(new_inode);
+@@ -438,13 +450,14 @@ out:
+  */
+ static struct dentry *nilfs_get_parent(struct dentry *child)
+ {
+-      unsigned long ino;
++      ino_t ino;
++      int res;
+       struct inode *inode;
+       struct nilfs_root *root;
+-      ino = nilfs_inode_by_name(d_inode(child), &dotdot_name);
+-      if (!ino)
+-              return ERR_PTR(-ENOENT);
++      res = nilfs_inode_by_name(d_inode(child), &dotdot_name, &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 951ce3c37752f60f6bd6aea1820aa12aac45be31..12155ed9a0df8131b10946f92922a6396928fe87 100644 (file)
@@ -120,3 +120,4 @@ tcp-fix-mptcp-dss-corruption-due-to-large-pmtu-xmit.patch
 selftests-mptcp-join-change-capture-checksum-as-bool.patch
 selftests-mptcp-join-test-for-prohibited-mpc-to-port-based-endp.patch
 selftests-mptcp-remove-duplicated-variables.patch
+nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch