btrfs: raid56: verify reconstructed tree blocks
Data sectors recovered from parity are checked against the csum tree
before they are trusted: recover_vertical() calls verify_one_sector() for
each rebuilt sector, and a mismatch fails the read. Metadata gets none of
that. fill_data_csums() returns early for anything that is not a data
block group, so csum_bitmap is NULL, and verify_one_sector() then returns
success without looking at anything:
if (!rbio->csum_bitmap || !rbio->csum_buf)
return 0;
So a metadata rbio reconstructs a lost column, marks it uptodate having
verified nothing, and hands it back. A wrong reconstruction is caught
much later by validate_extent_buffer(), one layer up, after the block has
already been returned and cached -- and in the raid6 case, where several
candidate reconstructions exist, raid56 cannot tell which one is right
because it has no way to check any of them.
A tree block can be verified, just not a sector at a time: its csum lives
in its own header and covers the whole nodesize block, so the check has to
wait until every sector of the block has been recovered. Do it after the
vertical recovery loop rather than inside it. A tree block never straddles
a column -- nodesize and BTRFS_STRIPE_LEN are both powers of two with
nodesize <= BTRFS_STRIPE_LEN, and full stripes are stripe-length aligned,
so each column starts on a nodesize boundary and tree blocks tile it
exactly.
Only the blocks the caller asked for are checked. Those are known to be
live tree blocks; the rest of a stripe may be free space, which has no
header and would fail every test. For the same reason this runs only when
rebuilding a read: recover_sectors() is also reached from the
read-modify-write path, where the sectors actually reconstructed are the
ones read off the surviving columns and any of them may be free space. A
reconstruction feeding an RMW's parity therefore stays unverified; knowing
which ranges hold live metadata is not something this layer can do.
Tested with a 3-device raid5 filesystem with raid5 metadata, 3000 files:
each device in turn missing and corrupted recovers byte-exactly, and with
two devices corrupted -- where reconstruction necessarily produces a wrong
block -- raid56 now rejects it itself:
raid56: reconstructed tree block
40796160 has bytenr
8993213254959039756,
cannot be trusted
Assisted-by: Claude:claude-fable-5