From nobody Sun Feb 8 04:57:33 2026 Received: from mail-pf1-f181.google.com (mail-pf1-f181.google.com [209.85.210.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 837D9155757 for ; Sat, 31 Jan 2026 14:04:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868287; cv=none; b=VqgNDeXq24kC2Spe6jiNAZ5MD8X5eXXQSYR2+AHfXQhTBgGw51kGIb8tI1dGSXoK/GVCnm+kRLHezLEvCDS6BfRp9T6/er9jl9x+R/egv5M+NCLCMga/HjIbOHEqXbSnduMngSJ5jh/BYoqB5PPfFzp6qweeOJ4EXibnzA6hSgg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769868287; c=relaxed/simple; bh=mwBp7x4hVmaNNrPtxczG3bkd4QIaoO+QTTP3OUZ8WoE=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=RIBzGuoqtrtHfJTmyEJislbF7snc6k1MQuTcv/EXMZENMPWkTOcy1YjniIGgMzV+1yRdvkN+lAyYP52UFtCjDJXWe8xyTmx2SHvweapyynclvlkwLd9l8c/7te/TChd9+wA/x3X0Jb7hdbL3v4yLPWIEAsE+KgsbWcDmgMOGCuo= 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=Twf2s1Mc; arc=none smtp.client-ip=209.85.210.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="Twf2s1Mc" Received: by mail-pf1-f181.google.com with SMTP id d2e1a72fcca58-81f4e136481so1475095b3a.3 for ; Sat, 31 Jan 2026 06:04:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769868286; x=1770473086; 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=f+uFfXX98rrE6cCUxtqdP0wdsRpFU7nQkUtzyDsPgvw=; b=Twf2s1McOXt5UdZ4j7bETMyAFu+cmIXxm4DOxxczhNN0rFKEdtIVlEUNjvbiz6RY++ 6XCFBUBGWCtHrWCN6tzOrj22BEYHQO0yC+IaqXgFYtYE3mOQWo3UTgoDJcv8o2eW83Db mW128FmK9O0JlUtfOHn9tish1fnfgb2f5SH0VClEsRKWwKdW8v5iJgUqxpy/fyG8K4Pn E4F7wjpfuQHCb537IZLrIJs4kql+a+lyBxv3fBQbfx06yMxNc/CfnqKPJ8YxUV3SiCnK 0gz0VR9lEhg3EwmKQxyI+1NKoOd9r+M3xkltfNZSbKSpu6c9sG1O095ILkCsaC3CDXmX QskQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769868286; x=1770473086; 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=f+uFfXX98rrE6cCUxtqdP0wdsRpFU7nQkUtzyDsPgvw=; b=ICoNYu8nKpgp55ZVdc+nUSih6PXWCnbKVsOMrIfGdqgF0tVRw5W86qK++g+dgpiHt+ huHZRc0HvtTel+jPyC59tBwoNpLJC/+WbW/ztG8Q1D6fpGKAwepBSBIZ+IfT4V9+O4G/ yYGNlrVrdWPY3HSyJBleSwXCXte6P66fKDwdXCw8j2Pe7ZogOrH6hv/p0O8erbBh32zT 3AfKYGQzEPpwoY48qUHMdqrmPmyFBBmmhFy9AZd3ryhTHyQP3zX7RfrA6HpaaBo24yvt psCIrcbmygshGSGOyW84Y2us+okZ5+RQn5hRymU1KgIBuODx8P5fqZFGpDA7um5DBnvF ZAJw== X-Forwarded-Encrypted: i=1; AJvYcCU2l/n1dAaNIkAXhzGeau3GeOXC9od+E8vjh5JKQVrkuxWCH3WLUW2uWwOE4aqD0L6RHwECg0gQeM28d2o=@vger.kernel.org X-Gm-Message-State: AOJu0Yz+uc7lznEvvQUSAFWoZSDoaQvU2jHgJe6R2jOs6mCylMzxgJOK 2NP9JKd4vD+ACDovz5KwewHHkc2qzqnAIEDVhXD2+S9fv3bisu8x6fK0 X-Gm-Gg: AZuq6aICXnzDZ6VOuLYXPIbWqdqCbKef2D5PSCBA47PxRcReAiIkN0uOWWul2BxmI+a ziRyHrSPkGAejgfWwR++h6Cn/VInHuw4bsYC1sRYmz8HbsvW68cf9CUf3Ue6gMg8nbabMoMwM4Z wlHpx0bwUyr+keRcsP2kHtFTgn4nlkNnoc/nloMXFqSvA8BkQ8d7uKo9VlH8psMELjXpsrY/Yqt xwkLzhOn11XTzRA+ZJbchMDBYT/RGZiu9oXmzmwkuJMNTI2RMZuX2GrnF4BrysaqUTXHt1xsIyv 7SaSTRtuir29MBzMZ+a3/67FW3PRNkbHiugw0LDGzyKp8aJvrL6IHMbZsV/oe0eGAdQTD5phA7j ctoYUJzVO749tbEZvK2i3uaTVVvZqAdPrIirOOPPy6NptPAKBZ7ykNyy/t7un0QF5MbsD1m3+Qx nsaU8RSmupNW4W6eRjftbqoOxplgSqTgOSuHy5I4s= X-Received: by 2002:a05:6a00:1392:b0:81f:4a36:1c7c with SMTP id d2e1a72fcca58-823ab67551cmr5939571b3a.23.1769868285687; Sat, 31 Jan 2026 06:04:45 -0800 (PST) Received: from Shardul.tailddf38c.ts.net ([223.185.36.73]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-82379b1f45dsm10021792b3a.5.2026.01.31.06.04.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 31 Jan 2026 06:04:45 -0800 (PST) From: Shardul Bankar X-Google-Original-From: Shardul Bankar To: slava@dubeyko.com, glaubitz@physik.fu-berlin.de, frank.li@vivo.com, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org Cc: janak@mpiricsoftware.com, shardulsb08@gmail.com, Shardul Bankar , syzbot+1c8ff72d0cd8a50dfeaa@syzkaller.appspotmail.com Subject: [PATCH v3] hfsplus: validate btree bitmap during mount and handle corruption gracefully Date: Sat, 31 Jan 2026 19:34:38 +0530 Message-Id: <20260131140438.2296273-1-shardul.b@mpiricsoftware.com> X-Mailer: git-send-email 2.34.1 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" Add bitmap validation during HFS+ btree open to detect corruption where node 0 (header node) is not marked allocated. Syzkaller reported issues with corrupted HFS+ images where the btree allocation bitmap indicates that the header node is free. Node 0 must always be allocated as it contains the btree header record and the allocation bitmap itself. Violating this invariant can lead to kernel panics or undefined behavior when the filesystem attempts to allocate blocks or manipulate the btree. The validation checks the node allocation bitmap in the btree header node (record #2) and verifies that bit 7 (MSB) of the first byte is set. Implementation details: - Perform validation inside hfs_btree_open() to allow identifying the specific tree (Extents, Catalog, or Attributes) involved. - Use hfs_bnode_find() and hfs_brec_lenoff() to safely access the bitmap record using existing infrastructure, ensuring correct handling of multi-page nodes and endianness. - If corruption is detected, print a warning identifying the specific btree and force the filesystem to mount read-only (SB_RDONLY). This prevents kernel panics from corrupted syzkaller-generated images while enabling data recovery by allowing the mount to proceed in read-only mode rather than failing completely. Reported-by: syzbot+1c8ff72d0cd8a50dfeaa@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=3D1c8ff72d0cd8a50dfeaa Link: https://lore.kernel.org/all/b78c1e380a17186b73bc8641b139eca56a8de964.= camel@ibm.com/ Signed-off-by: Shardul Bankar --- v3: - Moved validation logic inline into hfs_btree_open() to allow reporting the specific corrupted tree ID. - Replaced custom offset calculations with existing hfs_bnode_find() and hfs_brec_lenoff() infrastructure to handle node sizes and page boundaries correctly. - Removed temporary 'btree_bitmap_corrupted' superblock flag; setup SB_RDONLY directly upon detection. - Moved logging to hfs_btree_open() to include the specific tree ID in the warning message - Used explicit bitwise check (&) instead of test_bit() to ensure portability. test_bit() bit-numbering is architecture-dependent (e.g., bit 0 vs bit 7 can swap meanings on BE vs LE), whereas masking 0x80 consistently targets the MSB required by the HFS+ on-disk format. v2: - Fix compiler warning about comparing u16 bitmap_off with PAGE_SIZE which can exceed u16 maximum on some architectures - Cast bitmap_off to unsigned int for the PAGE_SIZE comparison to avoid tautological constant-out-of-range comparison warning. - Link: https://lore.kernel.org/oe-kbuild-all/202601251011.kJUhBF3P-lkp@i= ntel.com/ fs/hfsplus/btree.c | 27 +++++++++++++++++++++++++++ include/linux/hfs_common.h | 2 ++ 2 files changed, 29 insertions(+) diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 229f25dc7c49..ae81608ba3cf 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -135,9 +135,12 @@ struct hfs_btree *hfs_btree_open(struct super_block *s= b, u32 id) struct hfs_btree *tree; struct hfs_btree_header_rec *head; struct address_space *mapping; + struct hfs_bnode *node; + u16 len, bitmap_off; struct inode *inode; struct page *page; unsigned int size; + u8 bitmap_byte; =20 tree =3D kzalloc(sizeof(*tree), GFP_KERNEL); if (!tree) @@ -242,6 +245,30 @@ struct hfs_btree *hfs_btree_open(struct super_block *s= b, u32 id) =20 kunmap_local(head); put_page(page); + + /* + * Validate bitmap: node 0 (header node) must be marked allocated. + */ + + node =3D hfs_bnode_find(tree, 0); + if (IS_ERR(node)) + goto free_inode; + + len =3D hfs_brec_lenoff(node, + HFSPLUS_BTREE_HDR_MAP_REC, &bitmap_off); + + if (len !=3D 0 && bitmap_off >=3D sizeof(struct hfs_bnode_desc)) { + hfs_bnode_read(node, &bitmap_byte, bitmap_off, 1); + if (!(bitmap_byte & HFSPLUS_BTREE_NODE0_BIT)) { + pr_warn("(%s): Btree 0x%x bitmap corruption detected, forcing read-only= .\n", + sb->s_id, id); + pr_warn("Run fsck.hfsplus to repair.\n"); + sb->s_flags |=3D SB_RDONLY; + } + } + + hfs_bnode_put(node); + return tree; =20 fail_page: diff --git a/include/linux/hfs_common.h b/include/linux/hfs_common.h index dadb5e0aa8a3..8d21d476cb57 100644 --- a/include/linux/hfs_common.h +++ b/include/linux/hfs_common.h @@ -510,7 +510,9 @@ struct hfs_btree_header_rec { #define HFSPLUS_NODE_MXSZ 32768 #define HFSPLUS_ATTR_TREE_NODE_SIZE 8192 #define HFSPLUS_BTREE_HDR_NODE_RECS_COUNT 3 +#define HFSPLUS_BTREE_HDR_MAP_REC 2 /* Map (bitmap) record in header node= */ #define HFSPLUS_BTREE_HDR_USER_BYTES 128 +#define HFSPLUS_BTREE_NODE0_BIT 0x80 =20 /* btree key type */ #define HFSPLUS_KEY_CASEFOLDING 0xCF /* case-insensitive */ --=20 2.34.1