fs/ocfs2/journal.c | 4 ++++ 1 file changed, 4 insertions(+)
In ocfs2_recovery_exit(), the recovery_map is freed via kfree() but
osb->recovery_map is not set to NULL afterward. If ocfs2_wait_for_recovery()
is called subsequently (e.g. during inode eviction in the dismount path or
via a racing stat() syscall), ocfs2_recovery_completed() dereferences the
stale pointer, leading to a NULL pointer dereference at rm->rm_used.
Fix this by:
1. Setting osb->recovery_map = NULL after kfree() in ocfs2_recovery_exit()
2. Adding a NULL guard in ocfs2_recovery_completed() so that it safely
returns "recovery completed" when the map has been freed
Fixes: 553abd046af6 ("ocfs2: Change the recovery map to an array of node numbers.")
Signed-off-by: Jiakai Xu <xujiakai24@mails.ucas.ac.cn>
---
fs/ocfs2/journal.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index f9bf3bac085d..5529a177f90e 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -243,6 +243,7 @@ void ocfs2_recovery_exit(struct ocfs2_super *osb)
/* XXX: Should we bug if there are dirty entries? */
kfree(rm);
+ osb->recovery_map = NULL;
}
static int __ocfs2_recovery_map_test(struct ocfs2_super *osb,
@@ -1225,6 +1226,9 @@ static int ocfs2_recovery_completed(struct ocfs2_super *osb)
int empty;
struct ocfs2_recovery_map *rm = osb->recovery_map;
+ if (!rm)
+ return 1;
+
spin_lock(&osb->osb_lock);
empty = (rm->rm_used == 0);
spin_unlock(&osb->osb_lock);
--
2.34.1
On 5/19/26 9:36 AM, Jiakai Xu wrote:
> In ocfs2_recovery_exit(), the recovery_map is freed via kfree() but
> osb->recovery_map is not set to NULL afterward. If ocfs2_wait_for_recovery()
> is called subsequently (e.g. during inode eviction in the dismount path or
evict_inodes() runs before ocfs2_recovery_exit(), which is called through put_super.
> via a racing stat() syscall), ocfs2_recovery_completed() dereferences the
> stale pointer, leading to a NULL pointer dereference at rm->rm_used.
>
> Fix this by:
> 1. Setting osb->recovery_map = NULL after kfree() in ocfs2_recovery_exit()
> 2. Adding a NULL guard in ocfs2_recovery_completed() so that it safely
> returns "recovery completed" when the map has been freed
>
> Fixes: 553abd046af6 ("ocfs2: Change the recovery map to an array of node numbers.")
> Signed-off-by: Jiakai Xu <xujiakai24@mails.ucas.ac.cn>
> ---
> fs/ocfs2/journal.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
> index f9bf3bac085d..5529a177f90e 100644
> --- a/fs/ocfs2/journal.c
> +++ b/fs/ocfs2/journal.c
> @@ -243,6 +243,7 @@ void ocfs2_recovery_exit(struct ocfs2_super *osb)
> /* XXX: Should we bug if there are dirty entries? */
>
> kfree(rm);
> + osb->recovery_map = NULL;
> }
>
> static int __ocfs2_recovery_map_test(struct ocfs2_super *osb,
> @@ -1225,6 +1226,9 @@ static int ocfs2_recovery_completed(struct ocfs2_super *osb)
> int empty;
> struct ocfs2_recovery_map *rm = osb->recovery_map;
>
Seems still cannot fully close the race?
Thanks,
Joseph
> + if (!rm)
> + return 1;
> +
> spin_lock(&osb->osb_lock);
> empty = (rm->rm_used == 0);
> spin_unlock(&osb->osb_lock);
This was found by fuzzing. Here is the report: BUG: kernel NULL pointer dereference, address: 0000000000000000 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 28fdd067 P4D 51bb2067 PUD 0 Oops: Oops: 0000 [#1] SMP NOPTI CPU: 0 UID: 0 PID: 216737 Comm: syz.2.26512 Not tainted 6.18.5 #1 PREEMPT(full) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 RIP: 0010:ocfs2_recovery_completed home/zzzrrll/tmp/linux/fs/ocfs2/journal.c:1234 [inline] RIP: 0010:ocfs2_wait_for_recovery+0x45/0x180 home/zzzrrll/tmp/linux/fs/ocfs2/journal.c:1242 Code: 07 65 49 8b 07 48 89 44 24 28 e8 96 23 28 ff 2e 2e 2e 31 c0 49 8b ae 38 01 00 00 49 8d 9e c8 00 00 00 48 89 df e8 cb 45 41 03 <8b> 6d 00 31 ff 89 ee e8 af 27 28 ff 48 89 df e8 37 47 41 03 85 ed RSP: 0018:ffa000000e1bbb58 EFLAGS: 00010202 RAX: 0000000000000000 RBX: ff1100002ac9a4c8 RCX: 0000000000000001 RDX: ffa000001873b000 RSI: 0000000000000000 RDI: ff1100002ac9a4c8 RBP: 0000000000000000 R08: ffffffff823b5a33 R09: ffa000000e1bbd70 R10: 0000000000000002 R11: ffffffff823c6940 R12: 0000000000000000 R13: ff1100002ac9a400 R14: ff1100002ac9a400 R15: ffffffff896a4020 FS: 00007f74f0df8640(0000) GS:ff110000f4370000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 000000005177e000 CR4: 0000000000751ef0 PKRU: 80000000 Call Trace: <TASK> ocfs2_inode_lock_full_nested+0xec/0xca0 home/zzzrrll/tmp/linux/fs/ocfs2/dlmglue.c:2457 ocfs2_inode_revalidate+0x90/0x300 home/zzzrrll/tmp/linux/fs/ocfs2/inode.c:1352 ocfs2_getattr+0x49/0x180 home/zzzrrll/tmp/linux/fs/ocfs2/file.c:1316 vfs_getattr_nosec+0x120/0x1b0 home/zzzrrll/tmp/linux/fs/stat.c:213 vfs_getattr home/zzzrrll/tmp/linux/fs/stat.c:262 [inline] vfs_statx_path home/zzzrrll/tmp/linux/fs/stat.c:299 [inline] vfs_statx+0x116/0x240 home/zzzrrll/tmp/linux/fs/stat.c:356 vfs_fstatat home/zzzrrll/tmp/linux/fs/stat.c:375 [inline] vfs_stat home/zzzrrll/tmp/linux/include/linux/fs.h:3593 [inline] __do_sys_newstat home/zzzrrll/tmp/linux/fs/stat.c:515 [inline] __se_sys_newstat+0x6a/0x240 home/zzzrrll/tmp/linux/fs/stat.c:509 do_syscall_x64 home/zzzrrll/tmp/linux/arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xc6/0xfa0 home/zzzrrll/tmp/linux/arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f74effae16d Code: 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007f74f0df7f98 EFLAGS: 00000246 ORIG_RAX: 0000000000000004 RAX: ffffffffffffffda RBX: 00007f74f01f5fa0 RCX: 00007f74effae16d RDX: 0000000000000000 RSI: 0000200000002200 RDI: 00002000000021c0 RBP: 00007f74f00480f0 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007f74f01f6038 R14: 00007f74f01f5fa0 R15: 00007f74f0dd8000 </TASK> Modules linked in: CR2: 0000000000000000 ---[ end trace 0000000000000000 ]--- RIP: 0010:ocfs2_recovery_completed home/zzzrrll/tmp/linux/fs/ocfs2/journal.c:1234 [inline] RIP: 0010:ocfs2_wait_for_recovery+0x45/0x180 home/zzzrrll/tmp/linux/fs/ocfs2/journal.c:1242 Code: 07 65 49 8b 07 48 89 44 24 28 e8 96 23 28 ff 2e 2e 2e 31 c0 49 8b ae 38 01 00 00 49 8d 9e c8 00 00 00 48 89 df e8 cb 45 41 03 <8b> 6d 00 31 ff 89 ee e8 af 27 28 ff 48 89 df e8 37 47 41 03 85 ed RSP: 0018:ffa000000e1bbb58 EFLAGS: 00010202 RAX: 0000000000000000 RBX: ff1100002ac9a4c8 RCX: 0000000000000001 RDX: ffa000001873b000 RSI: 0000000000000000 RDI: ff1100002ac9a4c8 RBP: 0000000000000000 R08: ffffffff823b5a33 R09: ffa000000e1bbd70 R10: 0000000000000002 R11: ffffffff823c6940 R12: 0000000000000000 R13: ff1100002ac9a400 R14: ff1100002ac9a400 R15: ffffffff896a4020 FS: 00007f74f0df8640(0000) GS:ff110000f4370000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 000000005177e000 CR4: 0000000000751ef0 PKRU: 80000000 ---------------- Code disassembly (best guess), 1 bytes skipped: 0: 65 49 8b 07 mov %gs:(%r15),%rax 4: 48 89 44 24 28 mov %rax,0x28(%rsp) 9: e8 96 23 28 ff call 0xff2823a4 e: 2e 2e 2e 31 c0 cs cs cs xor %eax,%eax 13: 49 8b ae 38 01 00 00 mov 0x138(%r14),%rbp 1a: 49 8d 9e c8 00 00 00 lea 0xc8(%r14),%rbx 21: 48 89 df mov %rbx,%rdi 24: e8 cb 45 41 03 call 0x34145f4 * 29: 8b 6d 00 mov 0x0(%rbp),%ebp <-- trapping instruction 2c: 31 ff xor %edi,%edi 2e: 89 ee mov %ebp,%esi 30: e8 af 27 28 ff call 0xff2827e4 35: 48 89 df mov %rbx,%rdi 38: e8 37 47 41 03 call 0x3414774 3d: 85 ed test %ebp,%ebp
© 2016 - 2026 Red Hat, Inc.