fs: ocfs2: fix kernel BUG in ocfs2_find_victim_chain
Subsystems (2)
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
@@ -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
Joseph Qi
maintainerQuoted 23 lines · Prithvi Tambewagh
syzbot reported a kernel BUG in
ocfs2_find_victim_chain()because thecl_next_free_recfield of the allocation chain list is 0, triggring theBUG_ON(!cl->cl_next_free_rec)condition and panicking the kernel.To fix this,
cl_next_free_recis checked inside the caller ofocfs2_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.Reported-by [email protected]Tested-by [email protected]Signed-off-by Prithvi Tambewagh <[email protected]>--- fs/ocfs2/suballoc.c | 7 +++++++ 1 file changed, 7 insertions(+)fs/ocfs2/suballoc.c +0
-0
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
Quoted 52 lines · Joseph Qi
Quoted 23 lines · Prithvi Tambewagh
syzbot reported a kernel BUG in
ocfs2_find_victim_chain()because thecl_next_free_recfield of the allocation chain list is 0, triggring theBUG_ON(!cl->cl_next_free_rec)condition and panicking the kernel.To fix this,
cl_next_free_recis checked inside the caller ofocfs2_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.Reported-by [email protected]Tested-by [email protected]Signed-off-by Prithvi Tambewagh <[email protected]>--- fs/ocfs2/suballoc.c | 7 +++++++ 1 file changed, 7 insertions(+)fs/ocfs2/suballoc.c +0
-0fs/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,
JosephQuoted 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
Joseph Qi
maintainerQuoted 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 thecl_next_free_recfield of the allocation chain list is 0, triggring theBUG_ON(!cl->cl_next_free_rec)condition and panicking the kernel.To fix this,
cl_next_free_recis checked inside the caller ofocfs2_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.Reported-by [email protected]Tested-by [email protected]Signed-off-by Prithvi Tambewagh <[email protected]>---
fs/ocfs2/suballoc.c | 7 +++++++1 file changed, 7 insertions(+)fs/ocfs2/suballoc.c +0
-0fs/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,
JosephQuoted 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:
939f15e640f193616691d3bcde0089760e75b0d3Hello 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
Quoted 32 lines · Joseph Qi
Quoted 23 lines · Prithvi Tambewagh
syzbot reported a kernel BUG in
ocfs2_find_victim_chain()because thecl_next_free_recfield of the allocation chain list is 0, triggring theBUG_ON(!cl->cl_next_free_rec)condition and panicking the kernel.To fix this,
cl_next_free_recis checked inside the caller ofocfs2_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.Reported-by [email protected]Tested-by [email protected]Signed-off-by Prithvi Tambewagh <[email protected]>--- fs/ocfs2/suballoc.c | 7 +++++++ 1 file changed, 7 insertions(+)fs/ocfs2/suballoc.c +0
-0fs/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,
JosephQuoted 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
Joseph Qi
maintainerQuoted 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 thecl_next_free_recfield of the allocation chain list is 0, triggring theBUG_ON(!cl->cl_next_free_rec)condition and panicking the kernel.To fix this,
cl_next_free_recis checked inside the caller ofocfs2_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.Reported-by [email protected]Tested-by [email protected]Signed-off-by Prithvi Tambewagh <[email protected]>---
fs/ocfs2/suballoc.c | 7 +++++++1 file changed, 7 insertions(+)fs/ocfs2/suballoc.c +0
-0fs/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 u16ocfs2_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_recgreater than or equal tocl->cl_countwill 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
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 thecl_next_free_recfield of the allocation chain list is 0, triggring theBUG_ON(!cl->cl_next_free_rec)condition and panicking the kernel.To fix this,
cl_next_free_recis checked inside the caller ofocfs2_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.Reported-by [email protected]Tested-by [email protected]Signed-off-by Prithvi Tambewagh <[email protected]>---
fs/ocfs2/suballoc.c | 7 +++++++1 file changed, 7 insertions(+)fs/ocfs2/suballoc.c +0
-0fs/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 u16ocfs2_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_recmay equal tocl_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_recgreater than or equal tocl->cl_countwill 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