From nobody Tue Apr 7 08:06:27 2026 Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 799B126F296 for ; Sat, 14 Mar 2026 12:37:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773491876; cv=none; b=ughsck6+HycLdT8lq/KkaYb9lxeBCTX5OnK0FMQ/vAFFL15Gw2jLVVQr8LzvX8FN/VPjA3YxaeESztxuVVc+FYj6M4uBC0D1aZrv+fIHFmuzIGCjk746lzZF/6M8GmwkXQOI16xSaudeMlHWxn7FYZI5XsVboeN9g+hA43J2nEQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773491876; c=relaxed/simple; bh=z5AD4JniElssw7dAZ3Z2sfNgq0aQO8sOOb3qGgHe3LU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iapTJRcaK/dGERxzoZJAq2xQXCGVfXQoiF8YKi21OY0Q18sUCQxZ697ZZPIgwFToDR4BXO8xX8zub2TOu/WmV4PUo3AxzjM81QqDJL+P8GrINbt6Wupe5dN8z+dmXgkALAbzVr2UgsfTn/XBVM/wnRUT3sKFR+9kfGJpn9tZhVc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=XR3DcV50; arc=none smtp.client-ip=209.85.214.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XR3DcV50" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-2adbfab4501so14052425ad.2 for ; Sat, 14 Mar 2026 05:37:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773491875; x=1774096675; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ch0eaxVQ4/ts2cf46sQ450Wifko7pmpNDC0czkFsQbk=; b=XR3DcV50O2XxLiQpqzCAMkbgY8hoGrNVcoru0cycEVQuzkUynVoJFkIWyRjP/71eil oO0nB6b1Glb4h+ZZ4Yn2BV7jx3kqjTzA1eRR8+y83tuP8Q34bhnXA6ujG8eGte+AOnXg l2laMb3cnQUvDXJfWEFuBA8LXRdxOF/HnIAjoZyoUGNj0FUl5lFRm2nkDh2PYDvknJI0 g0w3RJoCPEhkaWaq5HqQSt29CgeuO3Xe7SyXoXgLgkFBVrYCLYh+YeXWjuAP5qy0KXT0 5SUIjzuqEdXSKPuP70Nk0MsNk48oKZhZM8aTO8SRjx7JW7nDTUnYtBhKllOdjbhsKnDN MWUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773491875; x=1774096675; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=ch0eaxVQ4/ts2cf46sQ450Wifko7pmpNDC0czkFsQbk=; b=pg4lxx7vdRG8tPKua/uwunuyuQNBS6Mxxp3MUsnb5u6mm8N5zzbr83BA1IXe7dLUiP P/wMsD9L8W9rpauxKDKh9i7gL4yW+oPmaGx9g6EiV0pqmIOIspP55iX8rqIJrI/+3QmC 7AML2AWA3Lx5r4jqKGt1JDUBqh639/ekslV9c2r+kQnpwmXVOBXMYVqHNj0E+GW5VQub TcNeontqqHdbimSaruw42B3UY8U6gU/BDPmrvDR6hT0S8xDaXRdjH36Ze8LViJ81KvOY bgGszLNMiVTR/gQY6XQSg53wn9ERWZ4yPosCmvT7ztSrAWZHcLazS7XLSg8kwlcO+0bE 3Zcg== X-Forwarded-Encrypted: i=1; AJvYcCU7l+TNsRQ+orWyZUeR5S/U5i1YfwOKokDzYcOyWxPWeCDQKfSZRrcX2gLZg2voRLvKvMVuAq+e1s9oD2M=@vger.kernel.org X-Gm-Message-State: AOJu0YyGb/fXYz4tiJU3cnHf6Wap35iSYi2JodxCyRKHpVN4xtddC11r S8RBqA6j7g81q1H6ZSmHytIzZzwQNj+zb48y1agq47YM4SAc9kHQuvUY X-Gm-Gg: ATEYQzyl0LlqtpdNlk19p7WNdX1ZVx5OybbXkF+MUtwUX21ybqfjICgH09aJNTlasNq 35UB76PmeaPZpuXjSSvEn9IDKaDGQcA1SRbgSr51DAeNF89OhSlU9P1LRoSzQ2p31rAuvZYlBA/ FOrptQ4gibmFgH1TPkVhNHV2RiW3qIXcWwhMRrgh3DpyC/HpGlQdsQlBvKEddxeqkIRc9HJ39Q1 U+eOKw2sOsZ4Tqoa61pbF4x0SS3lhTkK4g1/v+fZMsSNz2qcV9G2sm5iOGgdis4Ez1Pd45F3TQN Uqavl8znKK52IO5QWzgSdMRyE0e+S/cnqo0fLk3RfUC+5Ozg31l0daqpQts9N7wdOtPQQDWoq0F CqJCXhKe2rPcJFoGvxqCxtDmGJj1Ie5mcCxVXbv17weinbGBVHa6E/Mb2yHNT/D84pchne2J2SH fP8fyF7/3OsY7k7390OKGhkfQEwvH8QTuxvIqhI8QN6PYY35L63ot7qvcggI9Qar1KYfXbXpI= X-Received: by 2002:a17:903:20ce:b0:2ae:6031:bc59 with SMTP id d9443c01a7336-2aecaa1504emr50150105ad.30.1773491874677; Sat, 14 Mar 2026 05:37:54 -0700 (PDT) Received: from kernel-fuzz.. ([103.172.183.54]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2aece56e0b8sm63561935ad.16.2026.03.14.05.37.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 14 Mar 2026 05:37:54 -0700 (PDT) From: ZhengYuan Huang To: dsterba@suse.com, clm@fb.com, idryomov@gmail.com Cc: linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, baijiaju1990@gmail.com, r33s3n6@gmail.com, zzzccc427@gmail.com, ZhengYuan Huang , stable@vger.kernel.org Subject: [PATCH v2 1/3] btrfs: balance: fix null-ptr-deref in chunk_usage_filter Date: Sat, 14 Mar 2026 20:37:39 +0800 Message-ID: <20260314123741.1439792-2-gality369@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260314123741.1439792-1-gality369@gmail.com> References: <20260314123741.1439792-1-gality369@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" [BUG] Running btrfs balance with a usage filter (-dusage=3DN) can trigger a null-ptr-deref when metadata corruption causes a chunk to have no corresponding block group in the in-memory cache: KASAN: null-ptr-deref in range [0x0000000000000070-0x0000000000000077] RIP: 0010:chunk_usage_filter fs/btrfs/volumes.c:3874 [inline] RIP: 0010:should_balance_chunk fs/btrfs/volumes.c:4018 [inline] RIP: 0010:__btrfs_balance fs/btrfs/volumes.c:4172 [inline] RIP: 0010:btrfs_balance+0x2024/0x42b0 fs/btrfs/volumes.c:4604 ... Call Trace: btrfs_ioctl_balance fs/btrfs/ioctl.c:3577 [inline] btrfs_ioctl+0x25cf/0x5b90 fs/btrfs/ioctl.c:5313 vfs_ioctl fs/ioctl.c:51 [inline] ... The bug is reproducible on next-20260312 with our dynamic metadata fuzzing tool, which corrupts btrfs metadata at runtime. [CAUSE] Two separate data structures are involved: 1. The on-disk chunk tree, which records every chunk (logical address space region) and is iterated by __btrfs_balance(). 2. The in-memory block group cache (fs_info->block_group_cache_tree), which is built at mount time by btrfs_read_block_groups() and holds a struct btrfs_block_group for each chunk. This cache is what the usage filter queries. On a well-formed filesystem, these two are kept in 1:1 correspondence. However, btrfs_read_block_groups() builds the cache from block group items in the extent tree, not directly from the chunk tree. A corrupted image can therefore contain a chunk item in the chunk tree whose corresponding block group item is absent from the extent tree; that chunk's block group is then never inserted into the in-memory cache. When balance iterates the chunk tree and reaches such an orphaned chunk, should_balance_chunk() calls chunk_usage_filter(), which queries the block group cache: cache =3D btrfs_lookup_block_group(fs_info, chunk_offset); chunk_used =3D cache->used; /* cache may be NULL */ btrfs_lookup_block_group() returns NULL silently when no cached entry covers chunk_offset. chunk_usage_filter() does not check the return value, so the immediately following dereference of cache->used triggers the crash. [FIX] Add a NULL check after btrfs_lookup_block_group() in chunk_usage_filter(). When the lookup fails, emit a btrfs_err() message identifying the affected bytenr and return -EUCLEAN to indicate filesystem corruption. Since the filter function now has an error return path, change its return type from bool to int (negative =3D error, 0 =3D do not balance, positive =3D balance). Update should_balance_chunk() accordingly (bool -> int, with the same convention) and add error propagation for the usage filter path. Finally, handle the new negative return in __btrfs_balance() by jumping to the existing error path, which aborts the balance operation and reports the error to userspace. After the fix, the same corruption is correctly detected and reported by the filter, and the null-ptr-deref is no longer triggered. Fixes: 5ce5b3c0916b ("Btrfs: usage filter") Cc: stable@vger.kernel.org Signed-off-by: ZhengYuan Huang --- fs/btrfs/volumes.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 2bec544d8ba3..7c21ac249383 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3863,14 +3863,20 @@ static bool chunk_usage_range_filter(struct btrfs_f= s_info *fs_info, u64 chunk_of return ret; } =20 -static bool chunk_usage_filter(struct btrfs_fs_info *fs_info, u64 chunk_of= fset, - struct btrfs_balance_args *bargs) +static int chunk_usage_filter(struct btrfs_fs_info *fs_info, u64 chunk_off= set, + struct btrfs_balance_args *bargs) { struct btrfs_block_group *cache; u64 chunk_used, user_thresh; bool ret =3D true; =20 cache =3D btrfs_lookup_block_group(fs_info, chunk_offset); + if (!cache) { + btrfs_err(fs_info, + "balance: chunk at bytenr %llu has no corresponding block group", + chunk_offset); + return -EUCLEAN; + } chunk_used =3D cache->used; =20 if (bargs->usage_min =3D=3D 0) @@ -3986,8 +3992,8 @@ static bool chunk_soft_convert_filter(u64 chunk_type,= struct btrfs_balance_args return false; } =20 -static bool should_balance_chunk(struct extent_buffer *leaf, struct btrfs_= chunk *chunk, - u64 chunk_offset) +static int should_balance_chunk(struct extent_buffer *leaf, struct btrfs_c= hunk *chunk, + u64 chunk_offset) { struct btrfs_fs_info *fs_info =3D leaf->fs_info; struct btrfs_balance_control *bctl =3D fs_info->balance_ctl; @@ -4014,9 +4020,13 @@ static bool should_balance_chunk(struct extent_buffe= r *leaf, struct btrfs_chunk } =20 /* usage filter */ - if ((bargs->flags & BTRFS_BALANCE_ARGS_USAGE) && - chunk_usage_filter(fs_info, chunk_offset, bargs)) { - return false; + if (bargs->flags & BTRFS_BALANCE_ARGS_USAGE) { + int filter_ret =3D chunk_usage_filter(fs_info, chunk_offset, bargs); + + if (filter_ret < 0) + return filter_ret; + if (filter_ret) + return false; } else if ((bargs->flags & BTRFS_BALANCE_ARGS_USAGE_RANGE) && chunk_usage_range_filter(fs_info, chunk_offset, bargs)) { return false; @@ -4172,6 +4182,10 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_= info) ret =3D should_balance_chunk(leaf, chunk, found_key.offset); =20 btrfs_release_path(path); + if (ret < 0) { + mutex_unlock(&fs_info->reclaim_bgs_lock); + goto error; + } if (!ret) { mutex_unlock(&fs_info->reclaim_bgs_lock); goto loop; --=20 2.43.0 From nobody Tue Apr 7 08:06:27 2026 Received: from mail-pj1-f45.google.com (mail-pj1-f45.google.com [209.85.216.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 67CB9279336 for ; Sat, 14 Mar 2026 12:37:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773491880; cv=none; b=pLIl/FbRIs+gbyTSKVjr1IHJZMzuh/uv1U/Kvgs+8Fyua0zLZc6wheyMLefbYQfWcAtZ0bauMHUjQi6DhmPryvZSS0RIdUENKZqxiGavcjwJqtO09cCyJuf4KQqjbT5VQ/vEH+L0lK2LTV++n5xfZ7FW7mE+1hNVUgAZpQl3tnE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773491880; c=relaxed/simple; bh=mY0wN1Iv94QmwEBTkfV+F1YpjliEDVD6TkjX5Uyamic=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KUVs2fWWV+ecfJDgdMhmbfWRorf/z/X5g2g4sTQchZDv14EFVdJq4+6KT32bupcku65w7Veb6+TyOY+UicVbtiB4OHLNPXbhqV5XD6PSptEPoP+2jy7fVi/wOThy4/XhCrgTcAdpyWz21h6jlUvJw1LGHNECRTsdg1wi9u7rgBc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=aaxia7Gx; arc=none smtp.client-ip=209.85.216.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="aaxia7Gx" Received: by mail-pj1-f45.google.com with SMTP id 98e67ed59e1d1-35691a231a7so1708577a91.3 for ; Sat, 14 Mar 2026 05:37:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773491879; x=1774096679; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=0nzVhDf4BKtWklFTMhpHjd6JpAbDZy+t4Nj2UDjpxAI=; b=aaxia7GxtzqOf/jscziIrMKulAsL99B2RDJxQZQtirJHeUTzWvjMH+fa7vJKc5CzMH Ua33dj1B52ZMJLcVDJJ3GSlca+s2T0IS971hvrxiBqix04x19w0N7pTEaDUmuFgzntBZ 6QX9/Slvdk/4aLZQWWeu/gE646EXp1A5YgHehWchMHJoXSPKUjcUCfT3d5ZHGZHySdzX xIW/kePSoMmktvydj9oxv5x5RX4L7m9BggaSUZXkA5j8fVFXiUupJCacEv5Si5Gr7Nks 4kDBhikMtVs1S8BISQ4Aezk5gjPsRngevsmbhR2urS6UZClK6l914vbduOGED282J2PD AZ6Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773491879; x=1774096679; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=0nzVhDf4BKtWklFTMhpHjd6JpAbDZy+t4Nj2UDjpxAI=; b=oK9KRRa+dXKhj+5K4YjtiHsKU1gK4ccFgR6B91EOZLaZPF15MAuCnuHwDmu9SR1sW0 wYV6S+0qgz1mXt86TRZxw5OTHl+dp6IRLw4rXNyh4dDMVT21xTfPcJic5MeTMzCUDiG6 OnP42fvk0Cx3p2gNifNVXJPNJvGy81xiqXLgRKOY0ac/tPxh0ZcR9LTNyDeYFEqbSUbC DxkH6waCD7pWXVzlNiTDUjEdgSEiJTU0M9qMQcaILdNAv1XBKsGSsXJXovXQtKTsvJ+p HG/CRUzTvSlND+VIperSt9zASFKYS08Wr1cfETTv7iV+TD/aeXZeou1BBBolD03CCZJF IKBw== X-Forwarded-Encrypted: i=1; AJvYcCWJf6lb+wYnx3Bg7e3WY7anVkH+69dirmq6Wp0AX2HG3ssUgoPLWzgHdL5l+HVoMXCj/sakUGqrTBZ6OYE=@vger.kernel.org X-Gm-Message-State: AOJu0YyfdVQOw5SxsVc977Gn3YevEVHzh/ET/JwWbccaPTwlwdoxIohV IzboEydh0rqrtgYj6vMqcSd3l0o3jkv7RNwnmkCKeKvB3qa8gvZRJEoF X-Gm-Gg: ATEYQzzgF58DtjnbNyZurHV3F+bwzThjjPyJD9aD+EG517Xu8QHlgDlfT1ySxnU4jJB jHaLCVaImBPpdN4VrP0XSaghDvzR7tKBQAFgMwriTm8REZxf6hNzSWILJ18svSaN06ZsOeIq2WO W8HedQiIf3RQIfcycPUTDZmJ7hiY+zfI9Y2qFTeRPeopsBKY8MmF+C5VVylpQC7FEL5fNzQxpLC j+v8uvraJMPLZHIOpMbyX8I+wXXwVkcU+nW3RB6IJuXdUXouwC+TThbK5v+iEdti9PR0bsSaEGL Ipkk4Jp8NVyxzfbutDlp2JIy1GOYomgXn+lQ9uYAqR7JpVejgNBJJPvK+K/rn3tveJZWTv+02VZ S4qYs4v40lOCkCH0ZP6axri7hlGqVtdMCNphUBKfOx45+hNPZg5vYq95POVckk6Rrisd4QP9V0I NqKRjw6BaBGWGW8vuaS3pKsGk4It7jTXQSOrHYcTDixOzfXYbV3NfkIqotxLuBilYp8IvZYs4= X-Received: by 2002:a17:903:3d0f:b0:2a9:62ce:1c15 with SMTP id d9443c01a7336-2aeca796da3mr62614755ad.0.1773491878653; Sat, 14 Mar 2026 05:37:58 -0700 (PDT) Received: from kernel-fuzz.. ([103.172.183.54]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2aece56e0b8sm63561935ad.16.2026.03.14.05.37.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 14 Mar 2026 05:37:58 -0700 (PDT) From: ZhengYuan Huang To: dsterba@suse.com, clm@fb.com, idryomov@gmail.com Cc: linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, baijiaju1990@gmail.com, r33s3n6@gmail.com, zzzccc427@gmail.com, ZhengYuan Huang , stable@vger.kernel.org Subject: [PATCH v2 2/3] btrfs: balance: fix null-ptr-deref in chunk_usage_range_filter Date: Sat, 14 Mar 2026 20:37:40 +0800 Message-ID: <20260314123741.1439792-3-gality369@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260314123741.1439792-1-gality369@gmail.com> References: <20260314123741.1439792-1-gality369@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" [BUG] Running btrfs balance with a usage range filter (-dusage=3Dmin..max) can trigger a null-ptr-deref when metadata corruption causes a chunk to have no corresponding block group in the in-memory cache: KASAN: null-ptr-deref in range [0x0000000000000070-0x0000000000000077] RIP: 0010:chunk_usage_range_filter fs/btrfs/volumes.c:3845 [inline] RIP: 0010:should_balance_chunk fs/btrfs/volumes.c:4031 [inline] RIP: 0010:__btrfs_balance fs/btrfs/volumes.c:4182 [inline] RIP: 0010:btrfs_balance+0x249e/0x4320 fs/btrfs/volumes.c:4618 ... Call Trace: btrfs_ioctl_balance fs/btrfs/ioctl.c:3577 [inline] btrfs_ioctl+0x25cf/0x5b90 fs/btrfs/ioctl.c:5313 vfs_ioctl fs/ioctl.c:51 [inline] ... The bug is reproducible on next-20260312 with our dynamic metadata fuzzing tool, which corrupts btrfs metadata at runtime. [CAUSE] Two separate data structures are involved: 1. The on-disk chunk tree, which records every chunk (logical address space region) and is iterated by __btrfs_balance(). 2. The in-memory block group cache (fs_info->block_group_cache_tree), which is built at mount time by btrfs_read_block_groups() and holds a struct btrfs_block_group for each chunk. This cache is what the usage range filter queries. On a well-formed filesystem, these two are kept in 1:1 correspondence. However, btrfs_read_block_groups() builds the cache from block group items in the extent tree, not directly from the chunk tree. A corrupted image can therefore contain a chunk item in the chunk tree whose corresponding block group item is absent from the extent tree; that chunk's block group is then never inserted into the in-memory cache. When balance iterates the chunk tree and reaches such an orphaned chunk, should_balance_chunk() calls chunk_usage_range_filter(), which queries the block group cache: cache =3D btrfs_lookup_block_group(fs_info, chunk_offset); chunk_used =3D cache->used; /* cache may be NULL */ btrfs_lookup_block_group() returns NULL silently when no cached entry covers chunk_offset. chunk_usage_range_filter() does not check the return value, so the immediately following dereference of cache->used triggers the crash. [FIX] Add a NULL check after btrfs_lookup_block_group() in chunk_usage_range_filter(). When the lookup fails, emit a btrfs_err() message identifying the affected bytenr and return -EUCLEAN to indicate filesystem corruption. Since chunk_usage_range_filter() now has an error return path, change its return type from bool to int (negative =3D error, 0 =3D do not balance, positive =3D balance). Update the BTRFS_BALANCE_ARGS_USAGE_RANGE branch in should_balance_chunk() to propagate negative errors instead of treating them as a normal filter result. After the fix, the same corruption is correctly detected and reported by the filter, and the null-ptr-deref is no longer triggered. Fixes: bc3094673f22 ("btrfs: extend balance filter usage to take minimum an= d maximum") Cc: stable@vger.kernel.org Signed-off-by: ZhengYuan Huang --- fs/btrfs/volumes.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 7c21ac249383..4958e074d420 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -3832,8 +3832,8 @@ static bool chunk_profiles_filter(u64 chunk_type, str= uct btrfs_balance_args *bar return true; } =20 -static bool chunk_usage_range_filter(struct btrfs_fs_info *fs_info, u64 ch= unk_offset, - struct btrfs_balance_args *bargs) +static int chunk_usage_range_filter(struct btrfs_fs_info *fs_info, u64 chu= nk_offset, + struct btrfs_balance_args *bargs) { struct btrfs_block_group *cache; u64 chunk_used; @@ -3842,6 +3842,12 @@ static bool chunk_usage_range_filter(struct btrfs_fs= _info *fs_info, u64 chunk_of bool ret =3D true; =20 cache =3D btrfs_lookup_block_group(fs_info, chunk_offset); + if (!cache) { + btrfs_err(fs_info, + "balance: chunk at bytenr %llu has no corresponding block group", + chunk_offset); + return -EUCLEAN; + } chunk_used =3D cache->used; =20 if (bargs->usage_min =3D=3D 0) @@ -4027,9 +4033,13 @@ static int should_balance_chunk(struct extent_buffer= *leaf, struct btrfs_chunk * return filter_ret; if (filter_ret) return false; - } else if ((bargs->flags & BTRFS_BALANCE_ARGS_USAGE_RANGE) && - chunk_usage_range_filter(fs_info, chunk_offset, bargs)) { - return false; + } else if (bargs->flags & BTRFS_BALANCE_ARGS_USAGE_RANGE) { + int filter_ret =3D chunk_usage_range_filter(fs_info, chunk_offset, bargs= ); + + if (filter_ret < 0) + return filter_ret; + if (filter_ret) + return false; } =20 /* devid filter */ --=20 2.43.0 From nobody Tue Apr 7 08:06:27 2026 Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1C10025F96B for ; Sat, 14 Mar 2026 12:38:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773491884; cv=none; b=VVpvsQgwg6MXgXR35KHs8mkDWmyK64I2qvD2pKUrHrXQJ3pTE5aIIsWTnHf9yTRRk/sAJzEnCarlw4BVrStcB2Fea4SjRkAsb+7Mh0yDTktIHkCGuDxvfL0cfCe0H7+CDHgYMb4rybtIZMnsmNgn290Di9NaMpa1lfAZ2yOLKqA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773491884; c=relaxed/simple; bh=TRKl+q0Zn4TfohZfg56MGKZFT66Ys4Q73PpKsm0gc5g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PcR51xkP10rB+fGUJbW1KRo7YNObDL/eOsk4+IOPiZTo7YHzFMS3NEgbpsghCp1Ss6Qq0U11j/+ZFxrjx0Ba/3gSl6igDz+ArX/RB37rAwKTj53Vhp79vSgcFBObbgew1oyCVMVytWv4LQ6sabyU0SCG8LK0Ws9StU8/Bgx8Q6A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HkuCMLu4; arc=none smtp.client-ip=209.85.214.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HkuCMLu4" Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-2adbfab4501so14052665ad.2 for ; Sat, 14 Mar 2026 05:38:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773491882; x=1774096682; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=2jZpZGRHbhNYi4Yj4ueDS5CDpIlSRqZLNbqutOeOrqM=; b=HkuCMLu4W9vJqlsB49AdKBRWxq4uTDzIvKeO4s3mv4MfEB7+iWVe7H1jmSQyY8Wkus 5cqv+xUbvytflgmA5QxgjHOp/Mw668dk0OWP8mY94ZuOTRNGKywHPfTqOTg4rCxJP0Kn 2Jcf/dpHjvJI0wKdnw7bVgnhYYH0wrDolXQ+PAlGZI02TojMIdo7oO+FmPSOaz9qHivZ 9XrMrL7gldEplyMBKDY9+QCQsZgqQ8FBj0kezrNMu0lJmJEDU0GFf1MHAIrevHEjDZA+ Hb68KQYpMca45/9z/yCc9zUN1pKre9YGzCjperZqRZ81CFnWXpVG3aWFpMfaD3K99H/o Zxag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773491882; x=1774096682; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=2jZpZGRHbhNYi4Yj4ueDS5CDpIlSRqZLNbqutOeOrqM=; b=scWx0UBOa4JIFxzUfTmjvCJBOPFlr8WcEK8UrJ8VKvjT5CUBpZ6TF6BES0CmDsLSjN Om5A/n8Asl1DQlnHUGAVj8hkXm/Iz6JKK7xn85MIqdY6reWf6FNgg8N0YK3aD3AM4+U5 Ddcg7RBxkV1xEtXabIEoFw99zaTV/LPeoBbX2bDise9QbAh6LljyNPPkQi26g832iH/H AL64Y2NH/O41EpVSXSKPJ4MPiUqVHlyNV8Jn9BaTrAJucIFxGkbdIfaa5+E9BwXO+r8X gGkQ39wjzxEt6xNRfmi+zpcsq1Cnh5AWBqh3dcEhlMIuNjktqFRid54dmpmDLP+HwUZi Ndbg== X-Forwarded-Encrypted: i=1; AJvYcCUCPamKQZP6nLQHjhqixUmWh4LSPumFlN66L8R/eW4uS8zv2/5gL+EWY3LId1+BXdy8B93jJ+zsZUvVMOE=@vger.kernel.org X-Gm-Message-State: AOJu0YyQQ0CF9cFYUSOq9mAN08qvW0YqDQwlTDaCjcHeEPdaqQ5qlei7 q+wZKQCmqVR8stlBFfvYyTSsIIoVNS1nE1ON2mATdf29Bg3twvdQPHmH X-Gm-Gg: ATEYQzzWeNmpAOxKtboj/CHWe6Deq2fX7Q3+bY0vxAvEhvbDOAGja/7rNyqzWdPVdrN M8MVaYO12Y7wmbUPVPQA8bsPE8rCExorL8y5IXIXCDfEm+fqXalc9ro1uOvzaUL3n/3n3YEFO6/ l3MXtkZ6xCo8gwwT83GPkG//AoZz246LP1VSS+h4vv69qzFXQuFY9rdfZLo/w149o4Ll9JTCzUX WW5miPm29fgVAw4AsQEh3Y8l/86Y0a7H4LQ3oVwkgE1JIA8efaW6zaYlYEX/ox09IldgjdsvFTU OoNUrls7DlhqfWWCOhhTsWcya9Y6mJh7fB+pAGFIb1+HQJCPeiC1TwVZQ6K8ky+FoxuwOkXRq+g F69HsjjJE7+0jd38dsFPM88tHjm9myM5hiH8PX7RfbuVl35j1EJ68l6UYtO8x7ICvGmMtuqkxgA 7HXKU4BP3zONjmDVwZy5vW2S8BkPN4RTjn0bvgSG4tHklxGHECMByMShFmSUYosFxeBwYbu0Y= X-Received: by 2002:a17:902:ea0f:b0:2ae:54b2:27d1 with SMTP id d9443c01a7336-2aecaaaa8d1mr72132375ad.44.1773491882447; Sat, 14 Mar 2026 05:38:02 -0700 (PDT) Received: from kernel-fuzz.. ([103.172.183.54]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2aece56e0b8sm63561935ad.16.2026.03.14.05.37.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 14 Mar 2026 05:38:01 -0700 (PDT) From: ZhengYuan Huang To: dsterba@suse.com, clm@fb.com, idryomov@gmail.com Cc: linux-btrfs@vger.kernel.org, linux-kernel@vger.kernel.org, baijiaju1990@gmail.com, r33s3n6@gmail.com, zzzccc427@gmail.com, ZhengYuan Huang Subject: [PATCH v2 3/3] btrfs: fix check_chunk_block_group_mappings() to actually iterate all chunks Date: Sat, 14 Mar 2026 20:37:41 +0800 Message-ID: <20260314123741.1439792-4-gality369@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260314123741.1439792-1-gality369@gmail.com> References: <20260314123741.1439792-1-gality369@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" [BUG] A corrupted image with a chunk present in the chunk tree but whose corresponding block group item is missing from the extent tree can be mounted successfully, even though check_chunk_block_group_mappings() is supposed to catch exactly this corruption at mount time. Once mounted, running btrfs balance with a usage filter (-dusage=3DN or -dusage=3Dmin..max) triggers a null-ptr-deref: KASAN: null-ptr-deref in range [0x0000000000000070-0x0000000000000077] RIP: 0010:chunk_usage_filter fs/btrfs/volumes.c:3874 [inline] RIP: 0010:should_balance_chunk fs/btrfs/volumes.c:4018 [inline] RIP: 0010:__btrfs_balance fs/btrfs/volumes.c:4172 [inline] RIP: 0010:btrfs_balance+0x2024/0x42b0 fs/btrfs/volumes.c:4604 The crash occurs because __btrfs_balance() iterates the on-disk chunk tree, finds the orphaned chunk, calls chunk_usage_filter() (or chunk_usage_range_filter()), which queries the in-memory block group cache via btrfs_lookup_block_group(). Since no block group was ever inserted for this chunk, the lookup returns NULL, and the subsequent dereference of cache->used crashes. [CAUSE] check_chunk_block_group_mappings() uses btrfs_find_chunk_map() to iterate the in-memory chunk map (fs_info->mapping_tree): map =3D btrfs_find_chunk_map(fs_info, start, 1); With @start =3D 0 and @length =3D 1, btrfs_find_chunk_map() looks for a chunk map that *contains* the logical address 0. If no chunk contains logical address 0, btrfs_find_chunk_map(fs_info, 0, 1) returns NULL immediately and the loop breaks after the very first iteration, having checked zero chunks. The entire verification function is therefore a no-op, and the corrupted image passes the mount-time check undetected. [FIX] Replace the btrfs_find_chunk_map() based loop with a direct in-order walk of fs_info->mapping_tree using rb_first_cached() + rb_next(), protected by mapping_tree_lock. This guarantees that every chunk map in the tree is visited regardless of the logical addresses involved. Since the mapping_tree itself is accessed under read_lock, no refcount manipulation of each map entry is needed inside the loop, so the btrfs_free_chunk_map() calls on the map are also removed. Signed-off-by: ZhengYuan Huang --- fs/btrfs/block-group.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 5322ef2ae015..25bd0d058be6 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -2319,29 +2319,22 @@ static struct btrfs_block_group *btrfs_create_block= _group_cache( */ static int check_chunk_block_group_mappings(struct btrfs_fs_info *fs_info) { - u64 start =3D 0; + struct rb_node *node; int ret =3D 0; =20 - while (1) { + read_lock(&fs_info->mapping_tree_lock); + for (node =3D rb_first_cached(&fs_info->mapping_tree); node; + node =3D rb_next(node)) { struct btrfs_chunk_map *map; struct btrfs_block_group *bg; =20 - /* - * btrfs_find_chunk_map() will return the first chunk map - * intersecting the range, so setting @length to 1 is enough to - * get the first chunk. - */ - map =3D btrfs_find_chunk_map(fs_info, start, 1); - if (!map) - break; - + map =3D rb_entry(node, struct btrfs_chunk_map, rb_node); bg =3D btrfs_lookup_block_group(fs_info, map->start); if (unlikely(!bg)) { btrfs_err(fs_info, "chunk start=3D%llu len=3D%llu doesn't have corresponding block group", map->start, map->chunk_len); ret =3D -EUCLEAN; - btrfs_free_chunk_map(map); break; } if (unlikely(bg->start !=3D map->start || bg->length !=3D map->chunk_len= || @@ -2354,14 +2347,12 @@ static int check_chunk_block_group_mappings(struct = btrfs_fs_info *fs_info) bg->start, bg->length, bg->flags & BTRFS_BLOCK_GROUP_TYPE_MASK); ret =3D -EUCLEAN; - btrfs_free_chunk_map(map); btrfs_put_block_group(bg); break; } - start =3D map->start + map->chunk_len; - btrfs_free_chunk_map(map); btrfs_put_block_group(bg); } + read_unlock(&fs_info->mapping_tree_lock); return ret; } =20 --=20 2.43.0