Skip to content

Commit 1ee2d45

Browse files
konisgregkh
authored andcommitted
nilfs2: handle errors that nilfs_prepare_chunk() may return
commit ee70999a988b8abc3490609142f50ebaa8344432 upstream. 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/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Ryusuke Konishi <[email protected]> Reported-by: [email protected] Closes: https://syzkaller.appspot.com/bug?extid=32c3706ebf5d95046ea1 Reported-by: [email protected] Closes: https://syzkaller.appspot.com/bug?extid=1097e95f134f37d9395c Fixes: 2ba466d ("nilfs2: directory entry operations") Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 9823193 commit 1ee2d45

File tree

3 files changed

+27
-19
lines changed

3 files changed

+27
-19
lines changed

fs/nilfs2/dir.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino)
444444
return 0;
445445
}
446446

447-
void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de,
447+
int nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de,
448448
struct page *page, struct inode *inode)
449449
{
450450
unsigned int from = (char *)de - (char *)page_address(page);
@@ -454,11 +454,15 @@ void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de,
454454

455455
lock_page(page);
456456
err = nilfs_prepare_chunk(page, from, to);
457-
BUG_ON(err);
457+
if (unlikely(err)) {
458+
unlock_page(page);
459+
return err;
460+
}
458461
de->inode = cpu_to_le64(inode->i_ino);
459462
nilfs_set_de_type(de, inode);
460463
nilfs_commit_chunk(page, mapping, from, to);
461464
dir->i_mtime = dir->i_ctime = current_time(dir);
465+
return 0;
462466
}
463467

464468
/*
@@ -590,7 +594,10 @@ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page)
590594
from = (char *)pde - (char *)page_address(page);
591595
lock_page(page);
592596
err = nilfs_prepare_chunk(page, from, to);
593-
BUG_ON(err);
597+
if (unlikely(err)) {
598+
unlock_page(page);
599+
goto out;
600+
}
594601
if (pde)
595602
pde->rec_len = nilfs_rec_len_to_disk(to - from);
596603
dir->inode = 0;

fs/nilfs2/namei.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,10 @@ static int nilfs_rename(struct user_namespace *mnt_userns,
406406
err = PTR_ERR(new_de);
407407
goto out_dir;
408408
}
409-
nilfs_set_link(new_dir, new_de, new_page, old_inode);
409+
err = nilfs_set_link(new_dir, new_de, new_page, old_inode);
410410
nilfs_put_page(new_page);
411+
if (unlikely(err))
412+
goto out_dir;
411413
nilfs_mark_inode_dirty(new_dir);
412414
new_inode->i_ctime = current_time(new_inode);
413415
if (dir_de)
@@ -430,28 +432,27 @@ static int nilfs_rename(struct user_namespace *mnt_userns,
430432
*/
431433
old_inode->i_ctime = current_time(old_inode);
432434

433-
nilfs_delete_entry(old_de, old_page);
434-
435-
if (dir_de) {
436-
nilfs_set_link(old_inode, dir_de, dir_page, new_dir);
437-
nilfs_put_page(dir_page);
438-
drop_nlink(old_dir);
435+
err = nilfs_delete_entry(old_de, old_page);
436+
if (likely(!err)) {
437+
if (dir_de) {
438+
err = nilfs_set_link(old_inode, dir_de, dir_page,
439+
new_dir);
440+
drop_nlink(old_dir);
441+
}
442+
nilfs_mark_inode_dirty(old_dir);
439443
}
440-
nilfs_put_page(old_page);
441-
442-
nilfs_mark_inode_dirty(old_dir);
443444
nilfs_mark_inode_dirty(old_inode);
444445

445-
err = nilfs_transaction_commit(old_dir->i_sb);
446-
return err;
447-
448446
out_dir:
449447
if (dir_de)
450448
nilfs_put_page(dir_page);
451449
out_old:
452450
nilfs_put_page(old_page);
453451
out:
454-
nilfs_transaction_abort(old_dir->i_sb);
452+
if (likely(!err))
453+
err = nilfs_transaction_commit(old_dir->i_sb);
454+
else
455+
nilfs_transaction_abort(old_dir->i_sb);
455456
return err;
456457
}
457458

fs/nilfs2/nilfs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,8 @@ nilfs_find_entry(struct inode *, const struct qstr *, struct page **);
240240
extern int nilfs_delete_entry(struct nilfs_dir_entry *, struct page *);
241241
extern int nilfs_empty_dir(struct inode *);
242242
extern struct nilfs_dir_entry *nilfs_dotdot(struct inode *, struct page **);
243-
extern void nilfs_set_link(struct inode *, struct nilfs_dir_entry *,
244-
struct page *, struct inode *);
243+
int nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de,
244+
struct page *page, struct inode *inode);
245245

246246
static inline void nilfs_put_page(struct page *page)
247247
{

0 commit comments

Comments
 (0)