Skip to contentSkip to conversation

fs: ocfs2: fix kernel BUG in ocfs2_find_victim_chain

PATCH Superseded Prithvi Tambewagh 2 participants 7 messages Nov 30, 2025 – Dec 1, 2025

Subsystems (2)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 fs: 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.

---
 fs/ocfs2/suballoc.c | 7 +++++++
 1 file changed, 7 insertions(+)
fs/ocfs2/suballoc.c +7 -0
index 6ac4dcd54588..84bb2d11c2aa 100644
@@ -1993,6 +1993,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) == 0) {
+		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 23 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.

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

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

Quoted 3 lines
fs/ocfs2/suballoc.c +0 -0
@@ -1993,6 +1993,13 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
 
 	cl = (struct ocfs2_chain_list *) &fe->id2.i_chain;
 

This blank line can be eliminated.

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

Quoted 1 line
fs/ocfs2/suballoc.c +1 -0
+	if (le16_to_cpu(cl->cl_next_free_rec) == 0) {

Better to add the upper limit check as well. e.g.

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

Thanks,
Joseph

Quoted 11 lines
fs/ocfs2/suballoc.c +6 -0
+		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

#2 lore raw
Quoted 52 lines · Joseph Qi
Quoted 23 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.

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

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

Quoted 3 lines
fs/ocfs2/suballoc.c +0 -0
@@ -1993,6 +1993,13 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
 
 	cl = (struct ocfs2_chain_list *) &fe->id2.i_chain;
 

This blank line can be eliminated.

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

Quoted 1 line
fs/ocfs2/suballoc.c +1 -0
+	if (le16_to_cpu(cl->cl_next_free_rec) == 0) {

Better to add the upper limit check as well. e.g.

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

Thanks,
Joseph

Quoted 10 lines
fs/ocfs2/suballoc.c +6 -0
+		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;
 
Quoted 2 lines
Quoted 1 line

base-commit: 939f15e640f193616691d3bcde0089760e75b0d3

Hello Joseph,

Thanks for the feedback! I will make the changes in the patch. I wanted to confirm that the new patch should be sent as v2 on this same thread, along with the change log since v1, right?

Thanks,
Prithvi

maintainer
#3 lore raw
Quoted 53 lines · Prithvi Tambewagh
Quoted 52 lines · Joseph Qi
Quoted 23 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.

---
 fs/ocfs2/suballoc.c | 7 +++++++

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

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

Quoted 3 lines
fs/ocfs2/suballoc.c +0 -0
@@ -1993,6 +1993,13 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
 
      cl = (struct ocfs2_chain_list *) &fe->id2.i_chain;
 

This blank line can be eliminated.

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

Quoted 1 line
fs/ocfs2/suballoc.c +1 -0
+    if (le16_to_cpu(cl->cl_next_free_rec) == 0) {

Better to add the upper limit check as well. e.g.

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

Thanks,
Joseph

Quoted 10 lines
fs/ocfs2/suballoc.c +6 -0
+        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;
 
Quoted 7 lines
Quoted 2 lines
Quoted 1 line

base-commit: 939f15e640f193616691d3bcde0089760e75b0d3

Hello Joseph,

Thanks for the feedback! I will make the changes in the patch. I wanted to confirm that the new patch should be sent as v2 on this same thread, along with the change log since v1, right?

No need on the same thread, just v2 with the change log since v1 is enough.

Joseph

#4 lore raw
In reply to Joseph Qi
Quoted 32 lines · Joseph Qi
Quoted 23 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.

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

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

Quoted 3 lines
fs/ocfs2/suballoc.c +0 -0
@@ -1993,6 +1993,13 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
 
 	cl = (struct ocfs2_chain_list *) &fe->id2.i_chain;
 

This blank line can be eliminated.

Quoted 6 lines

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

Quoted 1 line
fs/ocfs2/suballoc.c +1 -0
+	if (le16_to_cpu(cl->cl_next_free_rec) == 0) {

Better to add the upper limit check as well. e.g.

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

Hello Joseph,

I went through the code in fs/ocfs2/suballoc.c, like this function

static inline u16 ocfs2_find_smallest_chain(struct ocfs2_chain_list *cl)

{
	u16 curr, best;

	best = curr = 0;
	while (curr < le16_to_cpu(cl->cl_count)) {
		if (le32_to_cpu(cl->cl_recs[best].c_total) >
		    le32_to_cpu(cl->cl_recs[curr].c_total))
			best = curr;
		curr++;
	}
	return best;
}

and in function ocfs2_block_group_alloc() these lines

if (le16_to_cpu(cl->cl_next_free_rec) < le16_to_cpu(cl->cl_count))

	le16_add_cpu(&cl->cl_next_free_rec, 1);

and observed that according to the architecture of ocfs2, the chain list is in the form of 0-indexed array. In that case, the change you suggested for upper limit, could be re-written as

le16_to_cpu(cl->cl_next_free_rec) >= le16_to_cpu(cl->cl_count)

since value of cl->cl_next_free_rec greater than or equal to cl->cl_count will indicate that there are no available chains. Can you please review this?

Thank you,
Prithvi

Quoted 16 lines

Thanks,
Joseph

Quoted 11 lines
fs/ocfs2/suballoc.c +6 -0
+		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

maintainer
#5 lore raw
Quoted 57 lines · Prithvi Tambewagh
Quoted 32 lines · Joseph Qi
Quoted 23 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.

---
 fs/ocfs2/suballoc.c | 7 +++++++

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

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

Quoted 3 lines
fs/ocfs2/suballoc.c +0 -0
@@ -1993,6 +1993,13 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
 
      cl = (struct ocfs2_chain_list *) &fe->id2.i_chain;
 

This blank line can be eliminated.

Quoted 6 lines

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

Quoted 1 line
fs/ocfs2/suballoc.c +1 -0
+    if (le16_to_cpu(cl->cl_next_free_rec) == 0) {

Better to add the upper limit check as well. e.g.

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

Hello Joseph,

I went through the code in fs/ocfs2/suballoc.c, like this function static inline u16 ocfs2_find_smallest_chain(struct ocfs2_chain_list *cl) {
    u16 curr, best;

    best = curr = 0;
    while (curr < le16_to_cpu(cl->cl_count)) { if (le32_to_cpu(cl->cl_recs[best].c_total) > le32_to_cpu(cl->cl_recs[curr].c_total)) best = curr;
        curr++;
    }
    return best;
}

Quoted 4 lines

and in function ocfs2_block_group_alloc() these lines if (le16_to_cpu(cl->cl_next_free_rec) < le16_to_cpu(cl->cl_count)) le16_add_cpu(&cl->cl_next_free_rec, 1);

After this, cl_next_free_rec may equal to cl_count.

Quoted 5 lines

and observed that according to the architecture of ocfs2, the chain list is in the form of 0-indexed array. In that case, the change you suggested for upper limit, could be re-written as le16_to_cpu(cl->cl_next_free_rec) >= le16_to_cpu(cl->cl_count)

since value of cl->cl_next_free_rec greater than or equal to cl->cl_count will indicate that there are no available chains. Can you please review this?

Yes, it's full. But 'cl_next_free_rec == cl_count' is a designed behavior, see mkfs or fsck.

Joseph

#6 lore raw
Quoted 67 lines · Joseph Qi
Quoted 57 lines · Prithvi Tambewagh
Quoted 32 lines · Joseph Qi
Quoted 23 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.

---
 fs/ocfs2/suballoc.c | 7 +++++++

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

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

Quoted 3 lines
fs/ocfs2/suballoc.c +0 -0
@@ -1993,6 +1993,13 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac,
 
      cl = (struct ocfs2_chain_list *) &fe->id2.i_chain;
 

This blank line can be eliminated.

Quoted 6 lines

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

Quoted 1 line
fs/ocfs2/suballoc.c +1 -0
+    if (le16_to_cpu(cl->cl_next_free_rec) == 0) {

Better to add the upper limit check as well. e.g.

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

Hello Joseph,

I went through the code in fs/ocfs2/suballoc.c, like this function static inline u16 ocfs2_find_smallest_chain(struct ocfs2_chain_list *cl) {
    u16 curr, best;

    best = curr = 0;
    while (curr < le16_to_cpu(cl->cl_count)) { if (le32_to_cpu(cl->cl_recs[best].c_total) > le32_to_cpu(cl->cl_recs[curr].c_total)) best = curr;
        curr++;
    }
    return best;
}

Quoted 4 lines

and in function ocfs2_block_group_alloc() these lines if (le16_to_cpu(cl->cl_next_free_rec) < le16_to_cpu(cl->cl_count)) le16_add_cpu(&cl->cl_next_free_rec, 1);

After this, cl_next_free_rec may equal to cl_count.

Quoted 6 lines
Quoted 5 lines

and observed that according to the architecture of ocfs2, the chain list is in the form of 0-indexed array. In that case, the change you suggested for upper limit, could be re-written as le16_to_cpu(cl->cl_next_free_rec) >= le16_to_cpu(cl->cl_count)

since value of cl->cl_next_free_rec greater than or equal to cl->cl_count will indicate that there are no available chains. Can you please review this?

Yes, it's full. But 'cl_next_free_rec == cl_count' is a designed behavior, see mkfs or fsck.

I get it. We are trying to catch a state of disk corruption, so your suggestion

le16_to_cpu(cl->cl_next_free_rec) > le16_to_cpu(cl->cl_count)

fits best here. Thanks...I will make v2 for the patch.

Best Reards,
Prithvi

Quoted 3 lines

Joseph