[PATCH v2 0/3] btrfs: fix balance NULL derefs and chunk/bg mapping verification

ZhengYuan Huang posted 3 patches 3 weeks, 2 days ago
There is a newer version of this series
fs/btrfs/block-group.c | 21 ++++++------------
fs/btrfs/volumes.c     | 48 +++++++++++++++++++++++++++++++-----------
2 files changed, 42 insertions(+), 27 deletions(-)
[PATCH v2 0/3] btrfs: fix balance NULL derefs and chunk/bg mapping verification
Posted by ZhengYuan Huang 3 weeks, 2 days ago
This series fixes two NULL dereferences in btrfs balance usage filters and
the underlying mount-time verification bug that lets the corresponding
chunk/block-group inconsistency go undetected.

The balance crashes happen when metadata corruption leaves a chunk present
in the chunk tree but without a corresponding block group in the in-memory
block group cache. In that case, the usage filters call
btrfs_lookup_block_group() and dereference the returned pointer without
checking for NULL.

The first two patches add the missing NULL checks and propagate -EUCLEAN
back to userspace instead of crashing. They are split because the usage
and usage-range filters were introduced by different commits, which should
also make backporting easier, as suggested by Qu Wenruo.

The third patch fixes the root cause on the mount-time verification side.
check_chunk_block_group_mappings() is supposed to verify that every chunk
has a matching block group, but its current iteration starts with
btrfs_find_chunk_map(fs_info, 0, 1). If no chunk contains logical address
0, the lookup returns NULL immediately and the loop exits without checking
any chunk at all. As a result, the corrupted mapping can survive mount and
only crash later when balance reaches it.

This series makes btrfs reject the inconsistency earlier at mount time,
and also hardens the balance filters so the corruption is reported as
-EUCLEAN instead of triggering a NULL dereference.

Changes since v1:
- split the two balance filter fixes into separate patches
- reworked the third patch to fix the case where
  check_chunk_block_group_mappings() does not actually check all chunk
  mappings

[NOTE]
Some of the changelogs may repeat parts of the bug analysis, which can
make the series somewhat verbose. I did that intentionally because I was
trying to follow the usual expectation that each patch should be able to
stand on its own and explain the specific issue it fixes. In particular,
I wanted each patch to describe its own immediate cause clearly, even
where the overall trigger path overlaps with the others. If that is not
the preferred style here, I would be happy to rework the changelogs and
resend the series in a different form.

Also, in a previous reply, Qu Wenruo suggested adding a separate
chunk/block-group consistency check. After looking into that, I found
that btrfs already has a function intended for this purpose,
check_chunk_block_group_mappings(). Patch 3 is based on the observation
that this check exists, but due to its current iteration logic it can
exit without checking any chunk mappings at all.

Since I am not very familiar with all the details of btrfs internals, if
my analysis of patch 3 is flawed, or if the fix is not the right one, I
would greatly appreciate any correction or guidance, and I will revise
and resend the patch accordingly.

ZhengYuan Huang (3):
  btrfs: balance: handle missing block groups in usage filter
  btrfs: balance: handle missing block groups in usage range filter
  btrfs: fix check_chunk_block_group_mappings() to iterate all chunk maps

 fs/btrfs/block-group.c | 21 ++++++------------
 fs/btrfs/volumes.c     | 48 +++++++++++++++++++++++++++++++-----------
 2 files changed, 42 insertions(+), 27 deletions(-)

-- 
2.43.0
Re: [PATCH v2 0/3] btrfs: fix balance NULL derefs and chunk/bg mapping verification
Posted by David Sterba 2 weeks ago
On Sat, Mar 14, 2026 at 08:37:38PM +0800, ZhengYuan Huang wrote:
> This series fixes two NULL dereferences in btrfs balance usage filters and
> the underlying mount-time verification bug that lets the corresponding
> chunk/block-group inconsistency go undetected.
> 
> The balance crashes happen when metadata corruption leaves a chunk present
> in the chunk tree but without a corresponding block group in the in-memory
> block group cache. In that case, the usage filters call
> btrfs_lookup_block_group() and dereference the returned pointer without
> checking for NULL.
> 
> The first two patches add the missing NULL checks and propagate -EUCLEAN
> back to userspace instead of crashing. They are split because the usage
> and usage-range filters were introduced by different commits, which should
> also make backporting easier, as suggested by Qu Wenruo.
> 
> The third patch fixes the root cause on the mount-time verification side.
> check_chunk_block_group_mappings() is supposed to verify that every chunk
> has a matching block group, but its current iteration starts with
> btrfs_find_chunk_map(fs_info, 0, 1). If no chunk contains logical address
> 0, the lookup returns NULL immediately and the loop exits without checking
> any chunk at all. As a result, the corrupted mapping can survive mount and
> only crash later when balance reaches it.
> 
> This series makes btrfs reject the inconsistency earlier at mount time,
> and also hardens the balance filters so the corruption is reported as
> -EUCLEAN instead of triggering a NULL dereference.

As I understand it you're using some advanced fuzzing tool (patch 1
mentions runtime fuzzing), so the errors would not normally happen. With
fuzzing it depends on the capabilities, at runtime it is possible to
confuse the filesystem so much that sipmle checks can't detect it.

Here checking if block group lookups are ok makes sense in general.
There are existing checks that seem to be following the same logic like
in unpin_extent_range().

> 
> Changes since v1:
> - split the two balance filter fixes into separate patches
> - reworked the third patch to fix the case where
>   check_chunk_block_group_mappings() does not actually check all chunk
>   mappings
> 
> [NOTE]
> Some of the changelogs may repeat parts of the bug analysis, which can
> make the series somewhat verbose. I did that intentionally because I was
> trying to follow the usual expectation that each patch should be able to
> stand on its own and explain the specific issue it fixes.

This is good, thanks. For simple fixes or cleanups it's fine to
make a vague reference to the main patch or a "in the previous/followup
patches".
Re: [PATCH v2 0/3] btrfs: fix balance NULL derefs and chunk/bg mapping verification
Posted by ZhengYuan Huang 2 weeks ago
On Tue, Mar 24, 2026 at 1:33 AM David Sterba <dsterba@suse.cz> wrote:
> As I understand it you're using some advanced fuzzing tool (patch 1
> mentions runtime fuzzing), so the errors would not normally happen. With
> fuzzing it depends on the capabilities, at runtime it is possible to
> confuse the filesystem so much that sipmle checks can't detect it.
>
> Here checking if block group lookups are ok makes sense in general.
> There are existing checks that seem to be following the same logic like
> in unpin_extent_range().

Thanks for your review.

Yes, we are using an in-house runtime fuzzing tool.
However, after further investigation of this bug, we found that it is
not limited to fuzzing-only scenarios. The issue can be reliably
triggered by using a crafted filesystem image together with normal syscalls.

So this may not be purely a fuzzing artifact, but rather a potential
robustness issue that could be hit in practice.

> This is good, thanks. For simple fixes or cleanups it's fine to
> make a vague reference to the main patch or a "in the previous/followup
> patches".

Thanks for the guidance, I’ll continue to follow this convention for
changelogs.

Thanks,
ZhengYuan Huang