[PATCH] btrfs: send: reject extents for non-regular inodes

ZhengYuan Huang <[email protected]>
Newsgroups org.kernel.vger.linux-btrfs,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
[BUG]
A corrupted subvolume tree can leave an EXTENT_DATA item attached to an
inode whose mode is not S_IFREG or S_IFLNK. During send, such an item can
be treated as file data and crash through a NULL address_space operation:

  BUG: kernel NULL pointer dereference, address: 0000000000000000
  #PF: supervisor instruction fetch in kernel mode
  #PF: error_code(0x0010) - not-present page
  Call Trace:
    <TASK>
    read_pages+0x80b/0xb30 mm/readahead.c:173
    page_cache_ra_unbounded+0x40d/0x890 mm/readahead.c:302
    do_page_cache_ra mm/readahead.c:332 [inline]
    page_cache_ra_order+0xa16/0xcd0 mm/readahead.c:535
    page_cache_sync_ra+0x5ce/0x9d0 mm/readahead.c:626
    page_cache_sync_readahead include/linux/pagemap.h:1379 [inline]
    put_file_data fs/btrfs/send.c:5224 [inline]
    send_write fs/btrfs/send.c:5291 [inline]
    send_extent_data+0x16b2/0x29b0 fs/btrfs/send.c:5715
    send_write_or_clone fs/btrfs/send.c:6135 [inline]
    process_extent+0x5d4/0x17b0 fs/btrfs/send.c:6504
    changed_extent fs/btrfs/send.c:7079 [inline]
    changed_cb+0x22f9/0x3cd0 fs/btrfs/send.c:7245
    full_send_tree fs/btrfs/send.c:7318 [inline]
    send_subvol fs/btrfs/send.c:7910 [inline]
    btrfs_ioctl_send+0x46a9/0x57f0 fs/btrfs/send.c:8248
    ...

[CAUSE]
process_extent() skips extent items for symlinks but assumes every other
inode with an extent item is a regular file. For a corrupted non-regular
inode, btrfs_iget() does not install the regular file address_space
operations. The readahead fallback can then call a NULL read_folio
callback before the existing validation in btrfs_get_extent() can run.

[FIX]
Reject extent items for inode types other than regular files and symlinks
at the common send extent-processing boundary. Symlink handling is left
unchanged because send emits symlink data from read_symlink(). This covers
full, incremental and new-generation sends without adding a check to the
regular I/O path.

Signed-off-by: ZhengYuan Huang <[email protected]>
---
 fs/btrfs/send.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index dca3570168c7..f88623bbc491 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -6417,6 +6417,13 @@ static int process_extent(struct send_ctx *sctx,
 
 	if (S_ISLNK(sctx->cur_inode_mode))
 		return 0;
+	if (unlikely(!S_ISREG(sctx->cur_inode_mode))) {
+		btrfs_crit(sctx->send_root->fs_info,
+			   "send: extent for non-regular inode %llu root %llu mode 0%llo",
+			   key->objectid, btrfs_root_id(sctx->send_root),
+			   sctx->cur_inode_mode & S_IFMT);
+		return -EUCLEAN;
+	}
 
 	if (sctx->parent_root && !sctx->cur_inode_new) {
 		ret = is_extent_unchanged(sctx, path, key);
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.