[PATCH] btrfs: handle highmem folios in read_key_bytes

From: Hongling Zeng

Date: Sun Aug 16 2026 - 22:21:03 EST


On 32-bit systems with highmem, folio_address() can return NULL for
unmapped highmem folios. When this NULL is passed as the dest parameter
to read_key_bytes() with a non-NULL dest_folio, it violates the function's
contract (which requires dest to be non-NULL when dest_folio is provided).

The original bug had two symptoms:
1. Unsigned len underflow when len -= copy_bytes executes (infinite loop)
2. The folio remains uninitialized because the copy block is skipped

Fix requires two changes:

1. Change "if (!dest)" to "if (!dest && !dest_folio)"
- Prevents the "counting-only" mode when dest_folio is provided
- Fixes the underflow/infinite loop

2. Change "if (dest)" to "if (dest || dest_folio)"
- Ensures the copy block executes when dest_folio is provided
- Allows kmap_local_folio() to properly map the highmem folio
- Actually writes data to the folio

Without the second change, the highmem folio is not populated even
though the read succeeds, causing subsequent fs-verity verification to
operate on stale or uninitialized data.

Fixes: 884937793db5 ("btrfs: convert read_key_bytes() to take a folio")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Hongling Zeng <zenghongling@xxxxxxxxxx>
---
fs/btrfs/verity.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c
index 983365a73541..80bc945c7dcb 100644
--- a/fs/btrfs/verity.c
+++ b/fs/btrfs/verity.c
@@ -351,7 +351,7 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
}

/* desc = NULL to just sum all the item lengths */
- if (!dest)
+ if (!dest && !dest_folio)
copy_end = item_end;
else
copy_end = min(offset + len, item_end);
@@ -362,7 +362,7 @@ static int read_key_bytes(struct btrfs_inode *inode, u8 key_type, u64 offset,
/* Offset from the start of item for copying */
copy_offset = offset - key.offset;

- if (dest) {
+ if (dest || dest_folio) {
if (dest_folio)
kaddr = kmap_local_folio(dest_folio, 0);

--
2.25.1