Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

nilfs2: handle errors that nilfs_prepare_chunk() may return

Patch series "nilfs2: fix issues with rename operations".

This series fixes BUG_ON check failures reported by syzbot around rename
operations, and a minor behavioral issue where the mtime of a child
directory changes when it is renamed instead of moved.


This patch (of 2):

The directory manipulation routines nilfs_set_link() and
nilfs_delete_entry() rewrite the directory entry in the folio/page
previously read by nilfs_find_entry(), so error handling is omitted on the
assumption that nilfs_prepare_chunk(), which prepares the buffer for
rewriting, will always succeed for these. And if an error is returned, it
triggers the legacy BUG_ON() checks in each routine.

This assumption is wrong, as proven by syzbot: the buffer layer called by
nilfs_prepare_chunk() may call nilfs_get_block() if necessary, which may
fail due to metadata corruption or other reasons. This has been there all
along, but improved sanity checks and error handling may have made it more
reproducible in fuzzing tests.

Fix this issue by adding missing error paths in nilfs_set_link(),
nilfs_delete_entry(), and their caller nilfs_rename().

Link: https://lkml.kernel.org/r/20250111143518.7901-1-konishi.ryusuke@gmail.com
Link: https://lkml.kernel.org/r/20250111143518.7901-2-konishi.ryusuke@gmail.com
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
Reported-by: syzbot+32c3706ebf5d95046ea1@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=32c3706ebf5d95046ea1
Reported-by: syzbot+1097e95f134f37d9395c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1097e95f134f37d9395c
Fixes: 2ba466d74ed7 ("nilfs2: directory entry operations")
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Ryusuke Konishi and committed by
Andrew Morton
ee70999a 28097f7b

+27 -19
+10 -3
fs/nilfs2/dir.c
··· 400 400 return 0; 401 401 } 402 402 403 - void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, 403 + int nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, 404 404 struct folio *folio, struct inode *inode) 405 405 { 406 406 size_t from = offset_in_folio(folio, de); ··· 410 410 411 411 folio_lock(folio); 412 412 err = nilfs_prepare_chunk(folio, from, to); 413 - BUG_ON(err); 413 + if (unlikely(err)) { 414 + folio_unlock(folio); 415 + return err; 416 + } 414 417 de->inode = cpu_to_le64(inode->i_ino); 415 418 de->file_type = fs_umode_to_ftype(inode->i_mode); 416 419 nilfs_commit_chunk(folio, mapping, from, to); 417 420 inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); 421 + return 0; 418 422 } 419 423 420 424 /* ··· 547 543 from = (char *)pde - kaddr; 548 544 folio_lock(folio); 549 545 err = nilfs_prepare_chunk(folio, from, to); 550 - BUG_ON(err); 546 + if (unlikely(err)) { 547 + folio_unlock(folio); 548 + goto out; 549 + } 551 550 if (pde) 552 551 pde->rec_len = nilfs_rec_len_to_disk(to - from); 553 552 dir->inode = 0;
+15 -14
fs/nilfs2/namei.c
··· 406 406 err = PTR_ERR(new_de); 407 407 goto out_dir; 408 408 } 409 - nilfs_set_link(new_dir, new_de, new_folio, old_inode); 409 + err = nilfs_set_link(new_dir, new_de, new_folio, old_inode); 410 410 folio_release_kmap(new_folio, new_de); 411 + if (unlikely(err)) 412 + goto out_dir; 411 413 nilfs_mark_inode_dirty(new_dir); 412 414 inode_set_ctime_current(new_inode); 413 415 if (dir_de) ··· 432 430 */ 433 431 inode_set_ctime_current(old_inode); 434 432 435 - nilfs_delete_entry(old_de, old_folio); 436 - 437 - if (dir_de) { 438 - nilfs_set_link(old_inode, dir_de, dir_folio, new_dir); 439 - folio_release_kmap(dir_folio, dir_de); 440 - drop_nlink(old_dir); 433 + err = nilfs_delete_entry(old_de, old_folio); 434 + if (likely(!err)) { 435 + if (dir_de) { 436 + err = nilfs_set_link(old_inode, dir_de, dir_folio, 437 + new_dir); 438 + drop_nlink(old_dir); 439 + } 440 + nilfs_mark_inode_dirty(old_dir); 441 441 } 442 - folio_release_kmap(old_folio, old_de); 443 - 444 - nilfs_mark_inode_dirty(old_dir); 445 442 nilfs_mark_inode_dirty(old_inode); 446 - 447 - err = nilfs_transaction_commit(old_dir->i_sb); 448 - return err; 449 443 450 444 out_dir: 451 445 if (dir_de) ··· 449 451 out_old: 450 452 folio_release_kmap(old_folio, old_de); 451 453 out: 452 - nilfs_transaction_abort(old_dir->i_sb); 454 + if (likely(!err)) 455 + err = nilfs_transaction_commit(old_dir->i_sb); 456 + else 457 + nilfs_transaction_abort(old_dir->i_sb); 453 458 return err; 454 459 } 455 460
+2 -2
fs/nilfs2/nilfs.h
··· 261 261 int nilfs_delete_entry(struct nilfs_dir_entry *, struct folio *); 262 262 int nilfs_empty_dir(struct inode *); 263 263 struct nilfs_dir_entry *nilfs_dotdot(struct inode *, struct folio **); 264 - void nilfs_set_link(struct inode *, struct nilfs_dir_entry *, 265 - struct folio *, struct inode *); 264 + int nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, 265 + struct folio *folio, struct inode *inode); 266 266 267 267 /* file.c */ 268 268 extern int nilfs_sync_file(struct file *, loff_t, loff_t, int);