From be290577d9b9eee78f23ebc908a14bf758505642 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 21 Oct 2024 11:51:19 +0200 Subject: [PATCH] 6.6-stable patches added patches: nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch --- ...ry-read-errors-from-nilfs_find_entry.patch | 230 ++++++++++++++++++ queue-6.6/series | 1 + 2 files changed, 231 insertions(+) create mode 100644 queue-6.6/nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch 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 index 00000000000..212c9aa9dd1 --- /dev/null +++ b/queue-6.6/nilfs2-propagate-directory-read-errors-from-nilfs_find_entry.patch @@ -0,0 +1,230 @@ +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); + } + +@@ -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 **); diff --git a/queue-6.6/series b/queue-6.6/series index 951ce3c3775..12155ed9a0d 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -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 -- 2.47.2