| From: Ryusuke Konishi <konishi.ryusuke@gmail.com> |
| Subject: nilfs2: fix sysfs interface lifetime |
| Date: Fri, 31 Mar 2023 05:55:15 +0900 |
| |
| The current nilfs2 sysfs support has issues with the timing of creation |
| and deletion of sysfs entries, potentially leading to null pointer |
| dereferences, use-after-free, and lockdep warnings. |
| |
| Some of the sysfs attributes for nilfs2 per-filesystem instance refer to |
| metadata file "cpfile", "sufile", or "dat", but |
| nilfs_sysfs_create_device_group that creates those attributes is executed |
| before the inodes for these metadata files are loaded, and |
| nilfs_sysfs_delete_device_group which deletes these sysfs entries is |
| called after releasing their metadata file inodes. |
| |
| Therefore, access to some of these sysfs attributes may occur outside of |
| the lifetime of these metadata files, resulting in inode NULL pointer |
| dereferences or use-after-free. |
| |
| In addition, the call to nilfs_sysfs_create_device_group() is made during |
| the locking period of the semaphore "ns_sem" of nilfs object, so the |
| shrinker call caused by the memory allocation for the sysfs entries, may |
| derive lock dependencies "ns_sem" -> (shrinker) -> "locks acquired in |
| nilfs_evict_inode()". |
| |
| Since nilfs2 may acquire "ns_sem" deep in the call stack holding other |
| locks via its error handler __nilfs_error(), this causes lockdep to report |
| circular locking. This is a false positive and no circular locking |
| actually occurs as no inodes exist yet when |
| nilfs_sysfs_create_device_group() is called. Fortunately, the lockdep |
| warnings can be resolved by simply moving the call to |
| nilfs_sysfs_create_device_group() out of "ns_sem". |
| |
| This fixes these sysfs issues by revising where the device's sysfs |
| interface is created/deleted and keeping its lifetime within the lifetime |
| of the metadata files above. |
| |
| Link: https://lkml.kernel.org/r/20230330205515.6167-1-konishi.ryusuke@gmail.com |
| Fixes: dd70edbde262 ("nilfs2: integrate sysfs support into driver") |
| Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> |
| Reported-by: syzbot+979fa7f9c0d086fdc282@syzkaller.appspotmail.com |
| Link: https://lkml.kernel.org/r/0000000000003414b505f7885f7e@google.com |
| Reported-by: syzbot+5b7d542076d9bddc3c6a@syzkaller.appspotmail.com |
| Link: https://lkml.kernel.org/r/0000000000006ac86605f5f44eb9@google.com |
| Cc: Viacheslav Dubeyko <slava@dubeyko.com> |
| Cc: <stable@vger.kernel.org> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| --- |
| |
| fs/nilfs2/super.c | 2 ++ |
| fs/nilfs2/the_nilfs.c | 12 +++++++----- |
| 2 files changed, 9 insertions(+), 5 deletions(-) |
| |
| --- a/fs/nilfs2/super.c~nilfs2-fix-sysfs-interface-lifetime |
| +++ a/fs/nilfs2/super.c |
| @@ -482,6 +482,7 @@ static void nilfs_put_super(struct super |
| up_write(&nilfs->ns_sem); |
| } |
| |
| + nilfs_sysfs_delete_device_group(nilfs); |
| iput(nilfs->ns_sufile); |
| iput(nilfs->ns_cpfile); |
| iput(nilfs->ns_dat); |
| @@ -1105,6 +1106,7 @@ nilfs_fill_super(struct super_block *sb, |
| nilfs_put_root(fsroot); |
| |
| failed_unload: |
| + nilfs_sysfs_delete_device_group(nilfs); |
| iput(nilfs->ns_sufile); |
| iput(nilfs->ns_cpfile); |
| iput(nilfs->ns_dat); |
| --- a/fs/nilfs2/the_nilfs.c~nilfs2-fix-sysfs-interface-lifetime |
| +++ a/fs/nilfs2/the_nilfs.c |
| @@ -87,7 +87,6 @@ void destroy_nilfs(struct the_nilfs *nil |
| { |
| might_sleep(); |
| if (nilfs_init(nilfs)) { |
| - nilfs_sysfs_delete_device_group(nilfs); |
| brelse(nilfs->ns_sbh[0]); |
| brelse(nilfs->ns_sbh[1]); |
| } |
| @@ -305,6 +304,10 @@ int load_nilfs(struct the_nilfs *nilfs, |
| goto failed; |
| } |
| |
| + err = nilfs_sysfs_create_device_group(sb); |
| + if (unlikely(err)) |
| + goto sysfs_error; |
| + |
| if (valid_fs) |
| goto skip_recovery; |
| |
| @@ -366,6 +369,9 @@ int load_nilfs(struct the_nilfs *nilfs, |
| goto failed; |
| |
| failed_unload: |
| + nilfs_sysfs_delete_device_group(nilfs); |
| + |
| + sysfs_error: |
| iput(nilfs->ns_cpfile); |
| iput(nilfs->ns_sufile); |
| iput(nilfs->ns_dat); |
| @@ -697,10 +703,6 @@ int init_nilfs(struct the_nilfs *nilfs, |
| if (err) |
| goto failed_sbh; |
| |
| - err = nilfs_sysfs_create_device_group(sb); |
| - if (err) |
| - goto failed_sbh; |
| - |
| set_nilfs_init(nilfs); |
| err = 0; |
| out: |
| _ |