]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 18 Feb 2025 17:35:01 +0000 (18:35 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 18 Feb 2025 17:35:01 +0000 (18:35 +0100)
added patches:
nilfs2-do-not-force-clear-folio-if-buffer-is-referenced.patch
nilfs2-do-not-output-warnings-when-clearing-dirty-buffers.patch
nilfs2-protect-access-to-buffers-with-no-active-references.patch

queue-5.4/nilfs2-do-not-force-clear-folio-if-buffer-is-referenced.patch [new file with mode: 0644]
queue-5.4/nilfs2-do-not-output-warnings-when-clearing-dirty-buffers.patch [new file with mode: 0644]
queue-5.4/nilfs2-protect-access-to-buffers-with-no-active-references.patch [new file with mode: 0644]
queue-5.4/series

diff --git a/queue-5.4/nilfs2-do-not-force-clear-folio-if-buffer-is-referenced.patch b/queue-5.4/nilfs2-do-not-force-clear-folio-if-buffer-is-referenced.patch
new file mode 100644 (file)
index 0000000..aa3999d
--- /dev/null
@@ -0,0 +1,156 @@
+From stable+bounces-114255-greg=kroah.com@vger.kernel.org Fri Feb  7 15:25:29 2025
+From: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+Date: Fri,  7 Feb 2025 23:23:48 +0900
+Subject: nilfs2: do not force clear folio if buffer is referenced
+To: stable@vger.kernel.org, Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Andrew Morton <akpm@linux-foundation.org>
+Message-ID: <20250207142512.6129-3-konishi.ryusuke@gmail.com>
+
+From: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+
+commit ca76bb226bf47ff04c782cacbd299f12ddee1ec1 upstream.
+
+Patch series "nilfs2: protect busy buffer heads from being force-cleared".
+
+This series fixes the buffer head state inconsistency issues reported by
+syzbot that occurs when the filesystem is corrupted and falls back to
+read-only, and the associated buffer head use-after-free issue.
+
+This patch (of 2):
+
+Syzbot has reported that after nilfs2 detects filesystem corruption and
+falls back to read-only, inconsistencies in the buffer state may occur.
+
+One of the inconsistencies is that when nilfs2 calls mark_buffer_dirty()
+to set a data or metadata buffer as dirty, but it detects that the buffer
+is not in the uptodate state:
+
+ WARNING: CPU: 0 PID: 6049 at fs/buffer.c:1177 mark_buffer_dirty+0x2e5/0x520
+  fs/buffer.c:1177
+ ...
+ Call Trace:
+  <TASK>
+  nilfs_palloc_commit_alloc_entry+0x4b/0x160 fs/nilfs2/alloc.c:598
+  nilfs_ifile_create_inode+0x1dd/0x3a0 fs/nilfs2/ifile.c:73
+  nilfs_new_inode+0x254/0x830 fs/nilfs2/inode.c:344
+  nilfs_mkdir+0x10d/0x340 fs/nilfs2/namei.c:218
+  vfs_mkdir+0x2f9/0x4f0 fs/namei.c:4257
+  do_mkdirat+0x264/0x3a0 fs/namei.c:4280
+  __do_sys_mkdirat fs/namei.c:4295 [inline]
+  __se_sys_mkdirat fs/namei.c:4293 [inline]
+  __x64_sys_mkdirat+0x87/0xa0 fs/namei.c:4293
+  do_syscall_x64 arch/x86/entry/common.c:52 [inline]
+  do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83
+  entry_SYSCALL_64_after_hwframe+0x77/0x7f
+
+The other is when nilfs_btree_propagate(), which propagates the dirty
+state to the ancestor nodes of a b-tree that point to a dirty buffer,
+detects that the origin buffer is not dirty, even though it should be:
+
+ WARNING: CPU: 0 PID: 5245 at fs/nilfs2/btree.c:2089
+  nilfs_btree_propagate+0xc79/0xdf0 fs/nilfs2/btree.c:2089
+ ...
+ Call Trace:
+  <TASK>
+  nilfs_bmap_propagate+0x75/0x120 fs/nilfs2/bmap.c:345
+  nilfs_collect_file_data+0x4d/0xd0 fs/nilfs2/segment.c:587
+  nilfs_segctor_apply_buffers+0x184/0x340 fs/nilfs2/segment.c:1006
+  nilfs_segctor_scan_file+0x28c/0xa50 fs/nilfs2/segment.c:1045
+  nilfs_segctor_collect_blocks fs/nilfs2/segment.c:1216 [inline]
+  nilfs_segctor_collect fs/nilfs2/segment.c:1540 [inline]
+  nilfs_segctor_do_construct+0x1c28/0x6b90 fs/nilfs2/segment.c:2115
+  nilfs_segctor_construct+0x181/0x6b0 fs/nilfs2/segment.c:2479
+  nilfs_segctor_thread_construct fs/nilfs2/segment.c:2587 [inline]
+  nilfs_segctor_thread+0x69e/0xe80 fs/nilfs2/segment.c:2701
+  kthread+0x2f0/0x390 kernel/kthread.c:389
+  ret_from_fork+0x4b/0x80 arch/x86/kernel/process.c:147
+  ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:244
+  </TASK>
+
+Both of these issues are caused by the callbacks that handle the
+page/folio write requests, forcibly clear various states, including the
+working state of the buffers they hold, at unexpected times when they
+detect read-only fallback.
+
+Fix these issues by checking if the buffer is referenced before clearing
+the page/folio state, and skipping the clear if it is.
+
+[konishi.ryusuke@gmail.com: adjusted for page/folio conversion]
+Link: https://lkml.kernel.org/r/20250107200202.6432-1-konishi.ryusuke@gmail.com
+Link: https://lkml.kernel.org/r/20250107200202.6432-2-konishi.ryusuke@gmail.com
+Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+Reported-by: syzbot+b2b14916b77acf8626d7@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=b2b14916b77acf8626d7
+Reported-by: syzbot+d98fd19acd08b36ff422@syzkaller.appspotmail.com
+Link: https://syzkaller.appspot.com/bug?extid=d98fd19acd08b36ff422
+Fixes: 8c26c4e2694a ("nilfs2: fix issue with flush kernel thread after remount in RO mode because of driver's internal error or metadata corruption")
+Tested-by: syzbot+b2b14916b77acf8626d7@syzkaller.appspotmail.com
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nilfs2/page.c |   35 +++++++++++++++++++++++++++++------
+ 1 file changed, 29 insertions(+), 6 deletions(-)
+
+--- a/fs/nilfs2/page.c
++++ b/fs/nilfs2/page.c
+@@ -389,24 +389,44 @@ void nilfs_clear_dirty_pages(struct addr
+ /**
+  * nilfs_clear_dirty_page - discard dirty page
+  * @page: dirty page that will be discarded
++ *
++ * nilfs_clear_dirty_page() clears working states including dirty state for
++ * the page and its buffers.  If the page has buffers, clear only if it is
++ * confirmed that none of the buffer heads are busy (none have valid
++ * references and none are locked).
+  */
+ void nilfs_clear_dirty_page(struct page *page)
+ {
+       BUG_ON(!PageLocked(page));
+-      ClearPageUptodate(page);
+-      ClearPageMappedToDisk(page);
+-      ClearPageChecked(page);
+-
+       if (page_has_buffers(page)) {
+-              struct buffer_head *bh, *head;
++              struct buffer_head *bh, *head = page_buffers(page);
+               const unsigned long clear_bits =
+                       (BIT(BH_Uptodate) | BIT(BH_Dirty) | BIT(BH_Mapped) |
+                        BIT(BH_Async_Write) | BIT(BH_NILFS_Volatile) |
+                        BIT(BH_NILFS_Checked) | BIT(BH_NILFS_Redirected) |
+                        BIT(BH_Delay));
++              bool busy, invalidated = false;
+-              bh = head = page_buffers(page);
++recheck_buffers:
++              busy = false;
++              bh = head;
++              do {
++                      if (atomic_read(&bh->b_count) | buffer_locked(bh)) {
++                              busy = true;
++                              break;
++                      }
++              } while (bh = bh->b_this_page, bh != head);
++
++              if (busy) {
++                      if (invalidated)
++                              return;
++                      invalidate_bh_lrus();
++                      invalidated = true;
++                      goto recheck_buffers;
++              }
++
++              bh = head;
+               do {
+                       lock_buffer(bh);
+                       set_mask_bits(&bh->b_state, clear_bits, 0);
+@@ -414,6 +434,9 @@ void nilfs_clear_dirty_page(struct page
+               } while (bh = bh->b_this_page, bh != head);
+       }
++      ClearPageUptodate(page);
++      ClearPageMappedToDisk(page);
++      ClearPageChecked(page);
+       __nilfs_clear_page_dirty(page);
+ }
diff --git a/queue-5.4/nilfs2-do-not-output-warnings-when-clearing-dirty-buffers.patch b/queue-5.4/nilfs2-do-not-output-warnings-when-clearing-dirty-buffers.patch
new file mode 100644 (file)
index 0000000..8ea79f1
--- /dev/null
@@ -0,0 +1,149 @@
+From stable+bounces-114254-greg=kroah.com@vger.kernel.org Fri Feb  7 15:25:28 2025
+From: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+Date: Fri,  7 Feb 2025 23:23:47 +0900
+Subject: nilfs2: do not output warnings when clearing dirty buffers
+To: stable@vger.kernel.org, Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Andrew Morton <akpm@linux-foundation.org>
+Message-ID: <20250207142512.6129-2-konishi.ryusuke@gmail.com>
+
+From: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+
+commit 299910dcb4525ac0274f3efa9527876315ba4f67 upstream.
+
+After detecting file system corruption and degrading to a read-only mount,
+dirty folios and buffers in the page cache are cleared, and a large number
+of warnings are output at that time, often filling up the kernel log.
+
+In this case, since the degrading to a read-only mount is output to the
+kernel log, these warnings are not very meaningful, and are rather a
+nuisance in system management and debugging.
+
+The related nilfs2-specific page/folio routines have a silent argument
+that suppresses the warning output, but since it is not currently used
+meaningfully, remove both the silent argument and the warning output.
+
+[konishi.ryusuke@gmail.com: adjusted for page/folio conversion]
+Link: https://lkml.kernel.org/r/20240816090128.4561-1-konishi.ryusuke@gmail.com
+Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: ca76bb226bf4 ("nilfs2: do not force clear folio if buffer is referenced")
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nilfs2/inode.c |    4 ++--
+ fs/nilfs2/mdt.c   |    6 +++---
+ fs/nilfs2/page.c  |   20 +++-----------------
+ fs/nilfs2/page.h  |    4 ++--
+ 4 files changed, 10 insertions(+), 24 deletions(-)
+
+--- a/fs/nilfs2/inode.c
++++ b/fs/nilfs2/inode.c
+@@ -170,7 +170,7 @@ static int nilfs_writepages(struct addre
+       int err = 0;
+       if (sb_rdonly(inode->i_sb)) {
+-              nilfs_clear_dirty_pages(mapping, false);
++              nilfs_clear_dirty_pages(mapping);
+               return -EROFS;
+       }
+@@ -193,7 +193,7 @@ static int nilfs_writepage(struct page *
+                * have dirty pages that try to be flushed in background.
+                * So, here we simply discard this dirty page.
+                */
+-              nilfs_clear_dirty_page(page, false);
++              nilfs_clear_dirty_page(page);
+               unlock_page(page);
+               return -EROFS;
+       }
+--- a/fs/nilfs2/mdt.c
++++ b/fs/nilfs2/mdt.c
+@@ -410,7 +410,7 @@ nilfs_mdt_write_page(struct page *page,
+                * have dirty pages that try to be flushed in background.
+                * So, here we simply discard this dirty page.
+                */
+-              nilfs_clear_dirty_page(page, false);
++              nilfs_clear_dirty_page(page);
+               unlock_page(page);
+               return -EROFS;
+       }
+@@ -631,10 +631,10 @@ void nilfs_mdt_restore_from_shadow_map(s
+       if (mi->mi_palloc_cache)
+               nilfs_palloc_clear_cache(inode);
+-      nilfs_clear_dirty_pages(inode->i_mapping, true);
++      nilfs_clear_dirty_pages(inode->i_mapping);
+       nilfs_copy_back_pages(inode->i_mapping, shadow->inode->i_mapping);
+-      nilfs_clear_dirty_pages(ii->i_assoc_inode->i_mapping, true);
++      nilfs_clear_dirty_pages(ii->i_assoc_inode->i_mapping);
+       nilfs_copy_back_pages(ii->i_assoc_inode->i_mapping,
+                             NILFS_I(shadow->inode)->i_assoc_inode->i_mapping);
+--- a/fs/nilfs2/page.c
++++ b/fs/nilfs2/page.c
+@@ -355,9 +355,8 @@ repeat:
+ /**
+  * nilfs_clear_dirty_pages - discard dirty pages in address space
+  * @mapping: address space with dirty pages for discarding
+- * @silent: suppress [true] or print [false] warning messages
+  */
+-void nilfs_clear_dirty_pages(struct address_space *mapping, bool silent)
++void nilfs_clear_dirty_pages(struct address_space *mapping)
+ {
+       struct pagevec pvec;
+       unsigned int i;
+@@ -378,7 +377,7 @@ void nilfs_clear_dirty_pages(struct addr
+                        * was acquired.  Skip processing in that case.
+                        */
+                       if (likely(page->mapping == mapping))
+-                              nilfs_clear_dirty_page(page, silent);
++                              nilfs_clear_dirty_page(page);
+                       unlock_page(page);
+               }
+@@ -390,19 +389,11 @@ void nilfs_clear_dirty_pages(struct addr
+ /**
+  * nilfs_clear_dirty_page - discard dirty page
+  * @page: dirty page that will be discarded
+- * @silent: suppress [true] or print [false] warning messages
+  */
+-void nilfs_clear_dirty_page(struct page *page, bool silent)
++void nilfs_clear_dirty_page(struct page *page)
+ {
+-      struct inode *inode = page->mapping->host;
+-      struct super_block *sb = inode->i_sb;
+-
+       BUG_ON(!PageLocked(page));
+-      if (!silent)
+-              nilfs_warn(sb, "discard dirty page: offset=%lld, ino=%lu",
+-                         page_offset(page), inode->i_ino);
+-
+       ClearPageUptodate(page);
+       ClearPageMappedToDisk(page);
+       ClearPageChecked(page);
+@@ -418,11 +409,6 @@ void nilfs_clear_dirty_page(struct page
+               bh = head = page_buffers(page);
+               do {
+                       lock_buffer(bh);
+-                      if (!silent)
+-                              nilfs_warn(sb,
+-                                         "discard dirty block: blocknr=%llu, size=%zu",
+-                                         (u64)bh->b_blocknr, bh->b_size);
+-
+                       set_mask_bits(&bh->b_state, clear_bits, 0);
+                       unlock_buffer(bh);
+               } while (bh = bh->b_this_page, bh != head);
+--- a/fs/nilfs2/page.h
++++ b/fs/nilfs2/page.h
+@@ -41,8 +41,8 @@ void nilfs_page_bug(struct page *);
+ int nilfs_copy_dirty_pages(struct address_space *, struct address_space *);
+ void nilfs_copy_back_pages(struct address_space *, struct address_space *);
+-void nilfs_clear_dirty_page(struct page *, bool);
+-void nilfs_clear_dirty_pages(struct address_space *, bool);
++void nilfs_clear_dirty_page(struct page *page);
++void nilfs_clear_dirty_pages(struct address_space *mapping);
+ void nilfs_mapping_init(struct address_space *mapping, struct inode *inode);
+ unsigned int nilfs_page_count_clean_buffers(struct page *, unsigned int,
+                                           unsigned int);
diff --git a/queue-5.4/nilfs2-protect-access-to-buffers-with-no-active-references.patch b/queue-5.4/nilfs2-protect-access-to-buffers-with-no-active-references.patch
new file mode 100644 (file)
index 0000000..1fbddf9
--- /dev/null
@@ -0,0 +1,60 @@
+From stable+bounces-114256-greg=kroah.com@vger.kernel.org Fri Feb  7 15:25:30 2025
+From: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+Date: Fri,  7 Feb 2025 23:23:49 +0900
+Subject: nilfs2: protect access to buffers with no active references
+To: stable@vger.kernel.org, Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Andrew Morton <akpm@linux-foundation.org>
+Message-ID: <20250207142512.6129-4-konishi.ryusuke@gmail.com>
+
+From: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+
+commit 367a9bffabe08c04f6d725032cce3d891b2b9e1a upstream.
+
+nilfs_lookup_dirty_data_buffers(), which iterates through the buffers
+attached to dirty data folios/pages, accesses the attached buffers without
+locking the folios/pages.
+
+For data cache, nilfs_clear_folio_dirty() may be called asynchronously
+when the file system degenerates to read only, so
+nilfs_lookup_dirty_data_buffers() still has the potential to cause use
+after free issues when buffers lose the protection of their dirty state
+midway due to this asynchronous clearing and are unintentionally freed by
+try_to_free_buffers().
+
+Eliminate this race issue by adjusting the lock section in this function.
+
+[konishi.ryusuke@gmail.com: adjusted for page/folio conversion]
+Link: https://lkml.kernel.org/r/20250107200202.6432-3-konishi.ryusuke@gmail.com
+Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
+Fixes: 8c26c4e2694a ("nilfs2: fix issue with flush kernel thread after remount in RO mode because of driver's internal error or metadata corruption")
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nilfs2/segment.c |    4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/fs/nilfs2/segment.c
++++ b/fs/nilfs2/segment.c
+@@ -732,7 +732,6 @@ static size_t nilfs_lookup_dirty_data_bu
+               }
+               if (!page_has_buffers(page))
+                       create_empty_buffers(page, i_blocksize(inode), 0);
+-              unlock_page(page);
+               bh = head = page_buffers(page);
+               do {
+@@ -742,11 +741,14 @@ static size_t nilfs_lookup_dirty_data_bu
+                       list_add_tail(&bh->b_assoc_buffers, listp);
+                       ndirties++;
+                       if (unlikely(ndirties >= nlimit)) {
++                              unlock_page(page);
+                               pagevec_release(&pvec);
+                               cond_resched();
+                               return ndirties;
+                       }
+               } while (bh = bh->b_this_page, bh != head);
++
++              unlock_page(page);
+       }
+       pagevec_release(&pvec);
+       cond_resched();
index 87899787a7ff5c0d4b57c36b706ad46b982d6429..db458cf534692257908940b044f30fadcd8578ae 100644 (file)
@@ -203,3 +203,6 @@ openvswitch-use-rcu-protection-in-ovs_vport_cmd_fill.patch
 ndisc-extend-rcu-protection-in-ndisc_send_skb.patch
 mips-fix-mips_get_syscall_arg-for-o32.patch
 alpha-replace-hardcoded-stack-offsets-with-autogenerated-ones.patch
+nilfs2-do-not-output-warnings-when-clearing-dirty-buffers.patch
+nilfs2-do-not-force-clear-folio-if-buffer-is-referenced.patch
+nilfs2-protect-access-to-buffers-with-no-active-references.patch