[PATCH] ocfs2: fix NULL deref in ocfs2_wait_for_recovery after recovery_exit

Jiakai Xu posted 1 patch 6 days, 2 hours ago
fs/ocfs2/journal.c | 4 ++++
1 file changed, 4 insertions(+)
[PATCH] ocfs2: fix NULL deref in ocfs2_wait_for_recovery after recovery_exit
Posted by Jiakai Xu 6 days, 2 hours ago
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
Re: [PATCH] ocfs2: fix NULL deref in ocfs2_wait_for_recovery after recovery_exit
Posted by Joseph Qi 5 days, 16 hours ago

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);
Re: [PATCH] ocfs2: fix NULL deref in ocfs2_wait_for_recovery after recovery_exit
Posted by Jiakai Xu 6 days, 2 hours ago
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