From nobody Thu Apr 9 11:16:18 2026 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (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 14DFA3803C9 for ; Mon, 9 Mar 2026 10:36:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773052609; cv=none; b=kg76cmvz0Dna0jGdicE0WNR7Vo/5EvNVjWKE72Z4qS3XzXVTD8I6Pp1xMQPc2I4iCLlHtnERTLrn/s35x+ndot3SREdpL45cI58EPFdYA+1NFNF6c1HyTB45laChQr2TcDJi2hzsr6fZwehSzhWOEcUVIUBINXBXqo0bgMEncuw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773052609; c=relaxed/simple; bh=4nj5Md+bH/7O6KXxrnkTKVE1FOcPcbm3kVld80GmTi4=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=TK68OU6OZOzVzWt4SnTu6O/g63gZ0QUZGAjSO8ZUvfKO8y0m7p5tX33lQHE1xwbk2J/LhbiW34JNDLNs0uPgn4K5vAkRNE7J+XxZw90lCfaMx0agu4KIottsg9uy/BByMd720IohZGHU/vkeHSg5TH5LjjfGQD1dPURuU8YmyFY= 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=lBEG712f; arc=none smtp.client-ip=209.85.214.176 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="lBEG712f" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2ad9f316d68so50281985ad.2 for ; Mon, 09 Mar 2026 03:36:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1773052607; x=1773657407; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=3FQLEzus73hEHckeZ2hSuCSlybXmlMdL0gTM45TGOIc=; b=lBEG712fUmnvyMCUnrrt8jWlOjWDX4cSg8VDqxXxKgTPZuK+f1Fadl4HZVI/M3BNyO JrmjYbO0PiQ5RIInKykbjHi+Ho55/8QB2uc+DWy+O1Q4kFk0+ALTvrk+pbGb7R0Hk89J O3nraS4bpyy9JNOxeOMHDPlEiyeTRTjNmqNQcVqMF0Hlr3XuppDnj4n/a5lBiH5CrIbS eZwLari13pllsIVdX6Np3tRiM5fLAWr24T4T/z0q85oFM36221L/faXiAlquu8WmpmTK qAe6tNwa/OFvfkcgO7vwrG+aeG/Llyh2TSQFOZA+k2H5jiDhoWwn2Rw8H6S7P+Vg2wn6 bb7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1773052607; x=1773657407; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=3FQLEzus73hEHckeZ2hSuCSlybXmlMdL0gTM45TGOIc=; b=vjIzpced2RHqS37xEDU5Jqh84tVr/+x06F2rOiomvaA/9vvIKRviD+R/ODEuekylko B8lk9iFlZFhmqn9+a/p7QUZtcWhkq0m9uWggl1OoiUtPLOvlDrptRrgQFlaFdTMFN5vl W/zZGoobUal4FkhQQ5m93z1XYJ3EzGDuQUUFDGSSmK7e8oFoTy/BSOGdgeqoP6tdG8OJ f8X1yHk9J+LZkJGVgwrUND5RXcF4H7Sejlsv9AU0op60Mu/yHZ+i+wdvDJN884akNP8F 2j0ivb0fwVcwZlg+Pws+dL3hPacijFzKxRjPHXRX6tysoEqmECMTAm4TBO1K12t+VZa+ TmzA== X-Forwarded-Encrypted: i=1; AJvYcCWJepXVJD+MFl3XRNEsOSrzp2kv2gaRxYpwujMfhzUb1NjeY51MNHfqvIRtNgzVqr99FNnuFSzf/R1l+Xc=@vger.kernel.org X-Gm-Message-State: AOJu0Yx3ZwgfrdT43MaX59lLQtTHwrOjPcSxGcuMDwqexyOwRWelkVL0 KG+NOtEBB+FXj7g/G6FjMaUUrJvrGxG3XGUFTYntO9THaikfzOzYykrR X-Gm-Gg: ATEYQzz0+CR0PA+5qV+FlW3yBijduAs2OS9jCLHHlximh6XBIS24YgHa+D4fmaXxyjX F3uoE2Rku+elzY28HO1eCsgunP6YcV+hbG2eOgXC0e0zXt36KerHJulXn4uLQ5t55kbKoGA6qLa ExnNGHtvrlYeCNpNIiS2xhiOaj8ZZUg8OHKHYqJqcl6Llm5uwVx8mIv0Qmgp+dwKlVMdhjpM5PW o6a6odZro65PSaqzArRN/MT2V+zXLD+gGrA9Sq3n/4c+/YTUrhz6sZdjiHvjKGHg9qaZNmqL0K4 Bpd2ow2wI/RrBCnxuOLXXI1vC72RigCJRNkv3oRd1FemQZCmldjISkwg9JigQo6mr/+jGfgul1E k8iUXwTgD2Y9brg8l2PzDel44ZQ5nZgBHKdU+0+ayijz+WQm5xncApLsx0a7OB0zIisr0uQFQ4w RUy8FBmxw0AeRATR7ojJFtuExXCWju1qkDJr0aUSCnpKekcsXwpAxTijElT7Qwrrs2vUFMig== X-Received: by 2002:a17:903:19ce:b0:2ad:e975:4735 with SMTP id d9443c01a7336-2ae8239798emr100153435ad.20.1773052607096; Mon, 09 Mar 2026 03:36:47 -0700 (PDT) Received: from kernel-fuzz.. ([138.199.21.245]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2ae83e57b14sm145078525ad.19.2026.03.09.03.36.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 09 Mar 2026 03:36:46 -0700 (PDT) From: ZhengYuan Huang To: dsterba@suse.com, clm@fb.com Cc: osandov@fb.com, 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] btrfs: validate free space bitmap size before testing bits Date: Mon, 9 Mar 2026 18:36:38 +0800 Message-ID: <20260309103638.1500791-1-gality369@gmail.com> X-Mailer: git-send-email 2.43.0 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" A corrupt free-space-tree leaf can contain a FREE_SPACE_BITMAP item whose on-disk item size does not match the bitmap size implied by key.offset. The free-space-tree loading path currently uses key.offset to iterate bitmap coverage, but does not verify that the item size matches free_space_bitmap_size(fs_info, key.offset). This allows a zero-sized or otherwise truncated bitmap item to be consumed as if it contained valid bitmap data. Once bit access runs past the valid extent buffer range, the computed folio index can reach an unpopulated eb->folios[] slot and trigger a NULL dereference in assert_eb_folio_uptodate(). Fix this by validating FREE_SPACE_BITMAP item sizes in load_free_space_bitmaps() before testing any bits. If the on-disk item size does not match the expected bitmap size, treat the free-space-tree leaf as corrupt and fail loading it with -EUCLEAN. Also add a defensive range check in extent_buffer_test_bit() so that corrupt metadata cannot drive bitmap bit access beyond the extent buffer even if a bad caller reaches that helper. The bug is reproducible on 7.0.0-rc2-next-20260306 with a dynamic metadata fuzzing tool that injects single-bit corruptions into btrfs leaf blocks at runtime. After this change, the corrupt bitmap item is rejected and the filesystem reports corruption instead of crashing. Fixes: a5ed91828518 ("Btrfs: implement the free space B-tree") Cc: stable@vger.kernel.org # 4.5+ Signed-off-by: ZhengYuan Huang --- Root cause =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D The direct fault is a NULL dereference in assert_eb_folio_uptodate(), reached from the free-space-tree bitmap loading path: caching_thread() -> btrfs_load_free_space_tree() -> load_free_space_bitmaps() -> btrfs_free_space_test_bit() -> extent_buffer_test_bit() -> assert_eb_folio_uptodate() The corrupted metadata pattern is a FREE_SPACE_BITMAP item whose item_size is smaller than the bitmap size described by key.offset. In the reproducer, multiple bitmap items had item_size =3D=3D 0 while key.offset still described non-empty bitmap ranges. For one failing item, the instrumented run showed: leaf_len =3D 16384 ptr =3D 16312 item_size =3D 0 expected =3D 402 key.type =3D BTRFS_FREE_SPACE_BITMAP_KEY So only 72 bytes remained in the leaf data area, while the bitmap range described by key.offset required 402 bytes of bitmap data. The existing code did not validate that mismatch before iterating over bitmap bits. btrfs_free_space_test_bit() uses btrfs_item_ptr_offset() as the bitmap start, and extent_buffer_test_bit() then translates the bit access into a folio index. Without a range check, once start + BIT_BYTE(nr) goes past eb->len, the computed folio index can exceed the populated folio range of the extent buffer. extent_buffer objects are zero-initialized and only the first num_extent_folios(eb) entries in eb->folios[] are populated. An access past that range can therefore hit a NULL eb->folios[] slot, which is then dereferenced by assert_eb_folio_uptodate() via folio_test_uptodate(). Reproduction (v6.18, x86_64, KASAN) =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D The PoC is relatively large, so it is provided separately through google dr= ive: https://drive.google.com/drive/folders/1eB6QzkGViZhlq8xouE5WSVRU0fovu0qw To reproduce the issue: 1. Build the PoC program: gcc poc.c -o poc 2. Build the ublk helper program from the ublk codebase, which is used to provide the runtime corruption capability: g++ -std=3Dc++20 -fcoroutines -O2 -o standalone_replay \ standalone_replay_btrfs.cpp targets/ublksrv_tgt.cpp \ -I. -Iinclude -Itargets/include \ -L./lib/.libs -lublksrv -luring -lpthread 3. Attach the crafted image through ublk: ./standalone_replay add -t loop -f /path/to/image 4. Run the PoC: ./poc This reliably reproduces the bug. Test notes =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D The reproducer was verified on 7.0.0-rc2-next-20260306 with runtime single-bit corruption injected into btrfs leaf blocks. I have not yet retested it on the latest stable kernel, but I can do so if needed. Fix =3D=3D=3D Two complementary defences are added: 1. In load_free_space_bitmaps() (free-space-tree.c), validate that the on-disk item_size equals free_space_bitmap_size(fs_info, key.offset) before entering the per-sector bit-reading loop. A mismatch is a clear sign of on-disk corruption; log a specific error message and return -EUCLEAN so the caller can handle it gracefully instead of walking off the end of the leaf. 2. In extent_buffer_test_bit() (extent_io.c), call check_eb_range() before eb_bitmap_offset(), mirroring the pattern already used by read_extent_buffer() and extent_buffer_get_byte(). This makes the function safe against any caller that passes an out-of-range (start, nr) pair, regardless of how the corruption reached this point. Defence (1) catches the specific free-space-tree path at the semantic layer and produces a meaningful log entry. Defence (2) is a generic safety net for the low-level helper that prevents the NULL-folio crash for any future caller that might bypass the upper-layer check. KASAN reports =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D BUG: KASAN: null-ptr-deref in instrument_atomic_read include/linux/instrume= nted.h:68 [inline] BUG: KASAN: null-ptr-deref in _test_bit include/asm-generic/bitops/instrume= nted-non-atomic.h:141 [inline] BUG: KASAN: null-ptr-deref in folio_test_uptodate include/linux/page-flags.= h:787 [inline] BUG: KASAN: null-ptr-deref in assert_eb_folio_uptodate+0x198/0x2b0 fs/btrfs= /extent_io.c:4071 Read of size 8 at addr 0000000000000000 by task kworker/u8:0/12 CPU: 1 UID: 0 PID: 12 Comm: kworker/u8:0 Not tainted 6.18.0+ #12 PREEMPT(vo= luntary)=20 Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996)= , BIOS 1.16.3-debian-1.16.3-2 04/01/2014 Workqueue: btrfs-cache btrfs_work_helper Call Trace: dump_stack_lvl+0xbe/0x130 print_report+0x437/0x650 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? early_section include/linux/mmzone.h:2184 [inline] ? pfn_valid include/linux/mmzone.h:2196 [inline] ? __virt_addr_valid+0xca/0x4c0 arch/x86/mm/physaddr.c:65 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? kasan_addr_to_slab+0xd/0xb0 mm/kasan/common.c:46 kasan_report+0xfb/0x140 ? instrument_atomic_read include/linux/instrumented.h:68 [inline] ? _test_bit include/asm-generic/bitops/instrumented-non-atomic.h:141 [inlin= e] ? folio_test_uptodate include/linux/page-flags.h:787 [inline] ? assert_eb_folio_uptodate+0x198/0x2b0 fs/btrfs/extent_io.c:4071 ? instrument_atomic_read include/linux/instrumented.h:68 [inline] ? _test_bit include/asm-generic/bitops/instrumented-non-atomic.h:141 [inlin= e] ? folio_test_uptodate include/linux/page-flags.h:787 [inline] ? assert_eb_folio_uptodate+0x198/0x2b0 fs/btrfs/extent_io.c:4071 kasan_check_range+0x11c/0x200 __kasan_check_read+0x11/0x20 assert_eb_folio_uptodate+0x198/0x2b0 extent_buffer_test_bit+0xce/0x200 btrfs_free_space_test_bit+0x1b3/0x270 ? __pfx_btrfs_free_space_test_bit+0x10/0x10 include/linux/sched/mm.h:332 ? __asan_memmove+0x30/0x80 mm/kasan/shadow.c:95 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? read_extent_buffer+0x114/0x3d0 fs/btrfs/extent_io.c:3946 btrfs_load_free_space_tree+0x57a/0xe40 ? __pfx_btrfs_load_free_space_tree+0x10/0x10 fs/btrfs/free-space-tree.c:1492 ? __entry_text_end+0x1025b9/0x1025bd ? __kasan_check_write+0x14/0x30 mm/kasan/shadow.c:37 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? instrument_atomic_write include/linux/instrumented.h:82 [inline] ? atomic_long_set include/linux/atomic/atomic-instrumented.h:3223 [inline] ? __rwsem_set_reader_owned kernel/locking/rwsem.c:177 [inline] ? rwsem_set_reader_owned kernel/locking/rwsem.c:182 [inline] ? rwsem_read_trylock kernel/locking/rwsem.c:257 [inline] ? rwsem_read_trylock kernel/locking/rwsem.c:249 [inline] ? __down_read_common kernel/locking/rwsem.c:1260 [inline] ? __down_read kernel/locking/rwsem.c:1274 [inline] ? down_read+0x1c5/0x4a0 kernel/locking/rwsem.c:1539 ? hung_task_set_blocker include/linux/hung_task.h:55 [inline] ? rwsem_down_read_slowpath+0xbd0/0xca0 kernel/locking/rwsem.c:1070 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? trace_hardirqs_on+0x53/0x60 kernel/trace/trace_preemptirq.c:79 caching_thread+0x3d5/0x1f20 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? save_trace+0x54/0x390 kernel/locking/lockdep.c:587 ? __pfx_caching_thread+0x10/0x10 fs/btrfs/block-group.c:533 ? __entry_text_end+0x1025b9/0x1025bd ? instrument_atomic_read_write include/linux/instrumented.h:96 [inline] ? atomic_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:130= 1 [inline] ? queued_spin_lock include/asm-generic/qspinlock.h:111 [inline] ? do_raw_spin_lock+0x133/0x290 kernel/locking/spinlock_debug.c:116 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? find_held_lock+0x31/0x90 kernel/locking/lockdep.c:5350 ? spin_unlock include/linux/spinlock.h:391 [inline] ? thresh_exec_hook fs/btrfs/async-thread.c:203 [inline] ? btrfs_work_helper+0x1a2/0xa50 fs/btrfs/async-thread.c:311 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? pv_queued_spin_unlock arch/x86/include/asm/paravirt.h:562 [inline] ? queued_spin_unlock arch/x86/include/asm/qspinlock.h:57 [inline] ? do_raw_spin_unlock+0x14b/0x200 kernel/locking/spinlock_debug.c:142 btrfs_work_helper+0x1d4/0xa50 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 process_one_work+0x8e0/0x1980 ? __pfx_process_one_work+0x10/0x10 include/linux/list.h:226 ? move_linked_works+0x1a8/0x2c0 kernel/workqueue.c:1165 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? assign_work+0x19d/0x240 kernel/workqueue.c:1206 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? __lock_is_held kernel/locking/lockdep.c:5601 [inline] ? lock_is_held_type+0xa3/0x130 kernel/locking/lockdep.c:5940 worker_thread+0x683/0xf80 ? __pfx_worker_thread+0x10/0x10 kernel/workqueue.c:3570 kthread+0x3f0/0x850 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? __pfx_kthread+0x10/0x10 arch/x86/include/asm/bitops.h:202 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? trace_hardirqs_on+0x53/0x60 kernel/trace/trace_preemptirq.c:79 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? __raw_spin_unlock_irq include/linux/spinlock_api_smp.h:159 [inline] ? _raw_spin_unlock_irq+0x27/0x70 kernel/locking/spinlock.c:202 ? srso_alias_return_thunk+0x5/0xfbef5 arch/x86/lib/retpoline.S:220 ? spin_unlock_irq include/linux/spinlock.h:401 [inline] ? calculate_sigpending+0x7c/0xb0 kernel/signal.c:194 ? __pfx_kthread+0x10/0x10 arch/x86/include/asm/bitops.h:202 ret_from_fork+0x50f/0x610 ? __pfx_kthread+0x10/0x10 arch/x86/include/asm/bitops.h:202 ret_from_fork_asm+0x1a/0x30 --- fs/btrfs/extent_io.c | 10 ++++++++++ fs/btrfs/free-space-tree.c | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 23273d0e6f22..14da72a9a950 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -4254,6 +4254,16 @@ bool extent_buffer_test_bit(const struct extent_buff= er *eb, unsigned long start, size_t offset; u8 *kaddr; =20 + /* + * Defend against a corrupt bitmap item whose item_size is smaller + * than what key.offset implies: if start + BIT_BYTE(nr) would fall + * outside this extent buffer, eb_bitmap_offset() would compute an + * out-of-bounds folio index, and assert_eb_folio_uptodate() would + * then dereference a NULL eb->folios[] slot. + */ + if (check_eb_range(eb, start, BIT_BYTE(nr) + 1)) + return false; + eb_bitmap_offset(eb, start, nr, &i, &offset); assert_eb_folio_uptodate(eb, i); kaddr =3D folio_address(eb->folios[i]); diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index d86541073d42..04fde74c35e5 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -1555,6 +1555,8 @@ static int load_free_space_bitmaps(struct btrfs_cachi= ng_control *caching_ctl, u64 end, offset; u64 total_found =3D 0; u32 extent_count =3D 0; + u32 expected_bitmap_size; + u32 actual_bitmap_size; int ret; =20 block_group =3D caching_ctl->block_group; @@ -1578,6 +1580,23 @@ static int load_free_space_bitmaps(struct btrfs_cach= ing_control *caching_ctl, ASSERT(key.type =3D=3D BTRFS_FREE_SPACE_BITMAP_KEY); ASSERT(key.objectid < end && key.objectid + key.offset <=3D end); =20 + /* + * Validate the on-disk item size matches what we compute + * from key.offset. A zero-sized (or otherwise wrong-sized) + * bitmap item would cause extent_buffer_test_bit() to walk + * past the end of the leaf, ultimately dereferencing a NULL + * folio pointer in assert_eb_folio_uptodate(). + */ + expected_bitmap_size =3D free_space_bitmap_size(fs_info, key.offset); + actual_bitmap_size =3D btrfs_item_size(path->nodes[0], path->slots[0]); + if (unlikely(actual_bitmap_size !=3D expected_bitmap_size)) { + btrfs_err(fs_info, + "corrupt free space bitmap for block group %llu: objectid=3D%llu exp= ected item size %u got %u", + block_group->start, key.objectid, + expected_bitmap_size, actual_bitmap_size); + return -EUCLEAN; + } + offset =3D key.objectid; while (offset < key.objectid + key.offset) { bool bit_set; --=20 2.43.0