Skip to content

Commit ee70999

Browse files
konisakpm00
authored andcommitted
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/[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]>
1 parent 28097f7 commit ee70999

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
@@ -400,7 +400,7 @@ int nilfs_inode_by_name(struct inode *dir, const struct qstr *qstr, ino_t *ino)
400400
return 0;
401401
}
402402

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,
404404
struct folio *folio, struct inode *inode)
405405
{
406406
size_t from = offset_in_folio(folio, de);
@@ -410,11 +410,15 @@ void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de,
410410

411411
folio_lock(folio);
412412
err = nilfs_prepare_chunk(folio, from, to);
413-
BUG_ON(err);
413+
if (unlikely(err)) {
414+
folio_unlock(folio);
415+
return err;
416+
}
414417
de->inode = cpu_to_le64(inode->i_ino);
415418
de->file_type = fs_umode_to_ftype(inode->i_mode);
416419
nilfs_commit_chunk(folio, mapping, from, to);
417420
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
421+
return 0;
418422
}
419423

420424
/*
@@ -543,7 +547,10 @@ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct folio *folio)
543547
from = (char *)pde - kaddr;
544548
folio_lock(folio);
545549
err = nilfs_prepare_chunk(folio, from, to);
546-
BUG_ON(err);
550+
if (unlikely(err)) {
551+
folio_unlock(folio);
552+
goto out;
553+
}
547554
if (pde)
548555
pde->rec_len = nilfs_rec_len_to_disk(to - from);
549556
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 mnt_idmap *idmap,
406406
err = PTR_ERR(new_de);
407407
goto out_dir;
408408
}
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);
410410
folio_release_kmap(new_folio, new_de);
411+
if (unlikely(err))
412+
goto out_dir;
411413
nilfs_mark_inode_dirty(new_dir);
412414
inode_set_ctime_current(new_inode);
413415
if (dir_de)
@@ -430,28 +432,27 @@ static int nilfs_rename(struct mnt_idmap *idmap,
430432
*/
431433
inode_set_ctime_current(old_inode);
432434

433-
nilfs_delete_entry(old_de, old_folio);
434-
435-
if (dir_de) {
436-
nilfs_set_link(old_inode, dir_de, dir_folio, new_dir);
437-
folio_release_kmap(dir_folio, dir_de);
438-
drop_nlink(old_dir);
435+
err = nilfs_delete_entry(old_de, old_folio);
436+
if (likely(!err)) {
437+
if (dir_de) {
438+
err = nilfs_set_link(old_inode, dir_de, dir_folio,
439+
new_dir);
440+
drop_nlink(old_dir);
441+
}
442+
nilfs_mark_inode_dirty(old_dir);
439443
}
440-
folio_release_kmap(old_folio, old_de);
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
folio_release_kmap(dir_folio, dir_de);
451449
out_old:
452450
folio_release_kmap(old_folio, old_de);
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
@@ -261,8 +261,8 @@ struct nilfs_dir_entry *nilfs_find_entry(struct inode *, const struct qstr *,
261261
int nilfs_delete_entry(struct nilfs_dir_entry *, struct folio *);
262262
int nilfs_empty_dir(struct inode *);
263263
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);
266266

267267
/* file.c */
268268
extern int nilfs_sync_file(struct file *, loff_t, loff_t, int);

0 commit comments

Comments
 (0)