Skip to contentSkip to conversation

ocfs2: fix kernel BUG in ocfs2_find_victim_chain

PATCH Superseded Prithvi Tambewagh 2 participants 3 messages Dec 1, 2025

Subsystems (2)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 ocfs2: fix kernel BUG in ocfs2_find_victim_chain

syzbot reported a kernel BUG in ocfs2_find_victim_chain() because the cl_next_free_rec field of the allocation chain list is 0, triggring the BUG_ON(!cl->cl_next_free_rec) condition and panicking the kernel.

To fix this, cl_next_free_rec is checked inside the caller of ocfs2_find_victim_chain() i.e. ocfs2_claim_suballoc_bits() and if it is equal to 0, ocfs2_error() is called, to log the corruption and force the filesystem into read-only mode, to prevent further damage.

---
v1->v2:
- Remove extra line before the if statement in patch
- Add upper limit check for cl->cl_next_free_rec in the if condition

 fs/ocfs2/suballoc.c | 7 +++++++
 1 file changed, 7 insertions(+)
fs/ocfs2/suballoc.c +7 -0
index 6ac4dcd54588..1257c39c2c11 100644
@@ -1992,6 +1992,13 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
 	}
 
 	cl = (struct ocfs2_chain_list *) &fe->id2.i_chain;
+	if (!le16_to_cpu(cl->cl_next_free_rec) ||
+	    le16_to_cpu(cl->cl_next_free_rec) > le16_to_cpu(cl->cl_count)) {
+		status = ocfs2_error(ac->ac_inode->i_sb,
+					"Chain allocator dinode %llu has 0 chains\n",
+					(unsigned long long)le64_to_cpu(fe->i_blkno));
+		goto bail;
+	}
 
 	victim = ocfs2_find_victim_chain(cl);
 	ac->ac_chain = victim;

base-commit: 939f15e640f193616691d3bcde0089760e75b0d3

Signature · 1 line
2.34.1
maintainer
#1 lore raw
Quoted 27 lines · Prithvi Tambewagh

syzbot reported a kernel BUG in ocfs2_find_victim_chain() because the cl_next_free_rec field of the allocation chain list is 0, triggring the BUG_ON(!cl->cl_next_free_rec) condition and panicking the kernel.

To fix this, cl_next_free_rec is checked inside the caller of ocfs2_find_victim_chain() i.e. ocfs2_claim_suballoc_bits() and if it is equal to 0, ocfs2_error() is called, to log the corruption and force the filesystem into read-only mode, to prevent further damage.

---
v1->v2:
- Remove extra line before the if statement in patch
- Add upper limit check for cl->cl_next_free_rec in the if condition

 fs/ocfs2/suballoc.c | 7 +++++++
 1 file changed, 7 insertions(+)
fs/ocfs2/suballoc.c +0 -0
index 6ac4dcd54588..1257c39c2c11 100644

fs/ocfs2/suballoc.c static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac, · line 1998

Quoted 7 lines
fs/ocfs2/suballoc.c +4 -0
@@ -1992,6 +1992,13 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
 	}
 
 	cl = (struct ocfs2_chain_list *) &fe->id2.i_chain;
+	if (!le16_to_cpu(cl->cl_next_free_rec) ||
+	    le16_to_cpu(cl->cl_next_free_rec) > le16_to_cpu(cl->cl_count)) {
+		status = ocfs2_error(ac->ac_inode->i_sb,
+					"Chain allocator dinode %llu has 0 chains\n",

The log message also has to be updateed. How about:

if (!le16_to_cpu(cl->cl_next_free_rec) ||

    le16_to_cpu(cl->cl_next_free_rec) > le16_to_cpu(cl->cl_count)) {
	status = ocfs2_error(ac->ac_inode->i_sb,
			     "Chain allocator dinode %llu has invalid next "
			     "free chain record %u, but only %u total\n",
			     (unsigned long long)le64_to_cpu(fe->i_blkno),
			     le16_to_cpu(cl->cl_next_free_rec),
			     le16_to_cpu(cl->cl_count));

Joseph

Quoted 8 lines
fs/ocfs2/suballoc.c +3 -0
+					(unsigned long long)le64_to_cpu(fe->i_blkno));
+		goto bail;
+	}
 
 	victim = ocfs2_find_victim_chain(cl);
 	ac->ac_chain = victim;

base-commit: 939f15e640f193616691d3bcde0089760e75b0d3

#2 lore raw
Quoted 32 lines · Joseph Qi
Quoted 29 lines · Prithvi Tambewagh

syzbot reported a kernel BUG in ocfs2_find_victim_chain() because the cl_next_free_rec field of the allocation chain list is 0, triggring the BUG_ON(!cl->cl_next_free_rec) condition and panicking the kernel.

To fix this, cl_next_free_rec is checked inside the caller of ocfs2_find_victim_chain() i.e. ocfs2_claim_suballoc_bits() and if it is equal to 0, ocfs2_error() is called, to log the corruption and force the filesystem into read-only mode, to prevent further damage.

---
v1->v2:
- Remove extra line before the if statement in patch
- Add upper limit check for cl->cl_next_free_rec in the if condition

 fs/ocfs2/suballoc.c | 7 +++++++
 1 file changed, 7 insertions(+)
fs/ocfs2/suballoc.c +0 -0
index 6ac4dcd54588..1257c39c2c11 100644
@@ -1992,6 +1992,13 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
 	}
 
Quoted 7 lines

fs/ocfs2/suballoc.c static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac, · line 1998

Quoted 5 lines
fs/ocfs2/suballoc.c +4 -0
 	cl = (struct ocfs2_chain_list *) &fe->id2.i_chain;
+	if (!le16_to_cpu(cl->cl_next_free_rec) ||
+	    le16_to_cpu(cl->cl_next_free_rec) > le16_to_cpu(cl->cl_count)) {
+		status = ocfs2_error(ac->ac_inode->i_sb,
+					"Chain allocator dinode %llu has 0 chains\n",

The log message also has to be updateed. How about:

Apologies for the mistake. I will rectify it.

Quoted 9 lines

if (!le16_to_cpu(cl->cl_next_free_rec) ||

   le16_to_cpu(cl->cl_next_free_rec) > le16_to_cpu(cl->cl_count)) {
	status = ocfs2_error(ac->ac_inode->i_sb,
			     "Chain allocator dinode %llu has invalid next "
			     "free chain record %u, but only %u total\n",
			     (unsigned long long)le64_to_cpu(fe->i_blkno),
			     le16_to_cpu(cl->cl_next_free_rec),
			     le16_to_cpu(cl->cl_count));

Thanks for the feedback! I will make v3 for the patch with these suggested changes.

Quoted 12 lines

Joseph

Quoted 8 lines
fs/ocfs2/suballoc.c +3 -0
+					(unsigned long long)le64_to_cpu(fe->i_blkno));
+		goto bail;
+	}
 
 	victim = ocfs2_find_victim_chain(cl);
 	ac->ac_chain = victim;

base-commit: 939f15e640f193616691d3bcde0089760e75b0d3

Best Regards,
Prithvi