[PATCH] jfs: fix task hung in lmLogClose

From: Edward Adam Davis
Date: Sun Mar 31 2024 - 04:50:31 EST


[Syzbot reported]
INFO: task syz-executor394:6204 blocked for more than 143 seconds.
Not tainted 6.8.0-rc7-syzkaller-g707081b61156 #0
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz-executor394 state:D stack:0 pid:6204 tgid:6204 ppid:6201 flags:0x0000000c
Call trace:
__switch_to+0x314/0x560 arch/arm64/kernel/process.c:553
context_switch kernel/sched/core.c:5400 [inline]
__schedule+0x1498/0x24b4 kernel/sched/core.c:6727
__schedule_loop kernel/sched/core.c:6802 [inline]
schedule+0xb8/0x19c kernel/sched/core.c:6817
schedule_preempt_disabled+0x18/0x2c kernel/sched/core.c:6874
__mutex_lock_common+0xbd8/0x21a0 kernel/locking/mutex.c:684
__mutex_lock kernel/locking/mutex.c:752 [inline]
mutex_lock_nested+0x2c/0x38 kernel/locking/mutex.c:804
lmLogClose+0xc8/0x4d4 fs/jfs/jfs_logmgr.c:1444
jfs_umount+0x274/0x360 fs/jfs/jfs_umount.c:114
jfs_put_super+0x90/0x188 fs/jfs/super.c:194
generic_shutdown_super+0x128/0x2b8 fs/super.c:641
kill_block_super+0x44/0x90 fs/super.c:1675
deactivate_locked_super+0xc4/0x12c fs/super.c:472
deactivate_super+0xe0/0x100 fs/super.c:505
cleanup_mnt+0x34c/0x3dc fs/namespace.c:1267
__cleanup_mnt+0x20/0x30 fs/namespace.c:1274
task_work_run+0x230/0x2e0 kernel/task_work.c:180
resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
do_notify_resume+0x178/0x1f4 arch/arm64/kernel/entry-common.c:151
exit_to_user_mode_prepare arch/arm64/kernel/entry-common.c:169 [inline]
exit_to_user_mode arch/arm64/kernel/entry-common.c:178 [inline]
el0_svc+0xac/0x168 arch/arm64/kernel/entry-common.c:713
el0t_64_sync_handler+0x84/0xfc arch/arm64/kernel/entry-common.c:730
el0t_64_sync+0x190/0x194 arch/arm64/kernel/entry.S:598
[Fix]
The jfs_log_mutex cannot distinguish different superblocks, resulting in multiple
different superblocks competing for jfs_log_mutex.

Reported-and-tested-by: syzbot+c824290332add8067111@xxxxxxxxxxxxxxxxxxxxxxxxx
Signed-off-by: Edward Adam Davis <eadavis@xxxxxx>
---
fs/jfs/jfs_incore.h | 1 +
fs/jfs/jfs_logmgr.c | 26 +++++++++++++-------------
fs/jfs/super.c | 1 +
3 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/fs/jfs/jfs_incore.h b/fs/jfs/jfs_incore.h
index 10934f9a11be..56d336a49985 100644
--- a/fs/jfs/jfs_incore.h
+++ b/fs/jfs/jfs_incore.h
@@ -197,6 +197,7 @@ struct jfs_sb_info {
kgid_t gid; /* gid to override on-disk gid */
uint umask; /* umask to override on-disk umask */
uint minblks_trim; /* minimum blocks, for online trim */
+ struct mutex simutex;
};

/* jfs_sb_info commit_state */
diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c
index 73389c68e251..b5609a7618e5 100644
--- a/fs/jfs/jfs_logmgr.c
+++ b/fs/jfs/jfs_logmgr.c
@@ -155,7 +155,6 @@ do { \
*/
static LIST_HEAD(jfs_external_logs);
static struct jfs_log *dummy_log;
-static DEFINE_MUTEX(jfs_log_mutex);

/*
* forward references
@@ -1068,19 +1067,19 @@ int lmLogOpen(struct super_block *sb)
if (sbi->mntflag & JFS_INLINELOG)
return open_inline_log(sb);

- mutex_lock(&jfs_log_mutex);
+ mutex_lock(&sbi->simutex);
list_for_each_entry(log, &jfs_external_logs, journal_list) {
if (file_bdev(log->bdev_file)->bd_dev == sbi->logdev) {
if (!uuid_equal(&log->uuid, &sbi->loguuid)) {
jfs_warn("wrong uuid on JFS journal");
- mutex_unlock(&jfs_log_mutex);
+ mutex_unlock(&sbi->simutex);
return -EINVAL;
}
/*
* add file system to log active file system list
*/
if ((rc = lmLogFileSystem(log, sbi, 1))) {
- mutex_unlock(&jfs_log_mutex);
+ mutex_unlock(&sbi->simutex);
return rc;
}
goto journal_found;
@@ -1088,7 +1087,7 @@ int lmLogOpen(struct super_block *sb)
}

if (!(log = kzalloc(sizeof(struct jfs_log), GFP_KERNEL))) {
- mutex_unlock(&jfs_log_mutex);
+ mutex_unlock(&sbi->simutex);
return -ENOMEM;
}
INIT_LIST_HEAD(&log->sb_list);
@@ -1130,7 +1129,7 @@ int lmLogOpen(struct super_block *sb)
sbi->log = log;
LOG_UNLOCK(log);

- mutex_unlock(&jfs_log_mutex);
+ mutex_unlock(&sbi->simutex);
return 0;

/*
@@ -1144,7 +1143,7 @@ int lmLogOpen(struct super_block *sb)
fput(bdev_file);

free: /* free log descriptor */
- mutex_unlock(&jfs_log_mutex);
+ mutex_unlock(&sbi->simutex);
kfree(log);

jfs_warn("lmLogOpen: exit(%d)", rc);
@@ -1187,12 +1186,13 @@ static int open_inline_log(struct super_block *sb)
static int open_dummy_log(struct super_block *sb)
{
int rc;
+ struct jfs_sb_info *sbi = JFS_SBI(sb);

- mutex_lock(&jfs_log_mutex);
+ mutex_lock(&sbi->simutex);
if (!dummy_log) {
dummy_log = kzalloc(sizeof(struct jfs_log), GFP_KERNEL);
if (!dummy_log) {
- mutex_unlock(&jfs_log_mutex);
+ mutex_unlock(&sbi->simutex);
return -ENOMEM;
}
INIT_LIST_HEAD(&dummy_log->sb_list);
@@ -1205,7 +1205,7 @@ static int open_dummy_log(struct super_block *sb)
if (rc) {
kfree(dummy_log);
dummy_log = NULL;
- mutex_unlock(&jfs_log_mutex);
+ mutex_unlock(&sbi->simutex);
return rc;
}
}
@@ -1214,7 +1214,7 @@ static int open_dummy_log(struct super_block *sb)
list_add(&JFS_SBI(sb)->log_list, &dummy_log->sb_list);
JFS_SBI(sb)->log = dummy_log;
LOG_UNLOCK(dummy_log);
- mutex_unlock(&jfs_log_mutex);
+ mutex_unlock(&sbi->simutex);

return 0;
}
@@ -1441,7 +1441,7 @@ int lmLogClose(struct super_block *sb)

jfs_info("lmLogClose: log:0x%p", log);

- mutex_lock(&jfs_log_mutex);
+ mutex_lock(&sbi->simutex);
LOG_LOCK(log);
list_del(&sbi->log_list);
LOG_UNLOCK(log);
@@ -1490,7 +1490,7 @@ int lmLogClose(struct super_block *sb)
kfree(log);

out:
- mutex_unlock(&jfs_log_mutex);
+ mutex_unlock(&sbi->simutex);
jfs_info("lmLogClose: exit(%d)", rc);
return rc;
}
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index e1be21ca5d6e..23628ca3990c 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -504,6 +504,7 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
sbi->uid = INVALID_UID;
sbi->gid = INVALID_GID;
sbi->umask = -1;
+ mutex_init(&sbi->simutex);

/* initialize the mount flag and determine the default error handler */
flag = JFS_ERR_REMOUNT_RO;
--
2.43.0