Linux EXT4 FS development
 help / color / mirror / Atom feed
* [PATCH] libblkid: avoid undefined shifts and an unaligned read in the probes
@ 2026-09-21 16:29 Akira Patafio
  0 siblings, 0 replies; only message in thread
From: Akira Patafio @ 2026-09-21 16:29 UTC (permalink / raw)
  To: Theodore Ts'o; +Cc: linux-ext4

Three sites in lib/blkid/probe.c operate on values taken straight from a
superblock, which on any system that probes removable media is
attacker-supplied.

probe_jfs() uses js_l2bsize and js_l2pbsize as shift exponents on a
32-bit 1U.  Both are 16-bit on-disk fields, so any value of 32 or more
makes the shift undefined.  No such value can describe a valid 32-bit
block size, so reject the superblock rather than shift by it.

exfat_next_cluster() casts the result of get_buffer() to uint32_t * and
dereferences it.  get_buffer() returns a pointer into a byte buffer at an
offset computed from fat_block_start, so it carries no alignment
guarantee and the offset is under the volume's control; the dereference
is undefined when that offset is not 4-byte aligned.  Copy the bytes out
with memcpy() instead, which the compiler turns back into a load where
unaligned access is permitted.

Neither has a memory-safety consequence: the shifts only produce a wrong
comparison, and get_buffer() already bounds-checks the read it returns.
This is a correctness and undefined-behaviour fix.

Found with libFuzzer and UBSan (clang 18, aarch64) over a corpus of
NTFS/exFAT/JFS superblocks.  util-linux made equivalent changes to its
fork of this code in 585815c1f8f7 ("libblkid: exfat - avoid undefined
shift") and 9c82a8ca123a ("libblkid: ntfs: avoid UB in signed shift").

Signed-off-by: Akira Patafio <kokokoala4211@gmail.com>
---
 lib/blkid/probe.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/lib/blkid/probe.c b/lib/blkid/probe.c
index 6a3bb24..689ee6d 100644
--- a/lib/blkid/probe.c
+++ b/lib/blkid/probe.c
@@ -854,13 +854,16 @@ static int probe_jfs(struct blkid_probe *probe,
 {
 	struct jfs_super_block *js;
 	const char *label = 0;
+	uint16_t l2bsize, l2pbsize;
 
 	js = (struct jfs_super_block *)buf;
 
-	if (blkid_le32(js->js_bsize) != (1U << blkid_le16(js->js_l2bsize)))
+	l2bsize = blkid_le16(js->js_l2bsize);
+	if (l2bsize >= 32 || blkid_le32(js->js_bsize) != (1U << l2bsize))
 		return 1;
 
-	if (blkid_le32(js->js_pbsize) != (1U << blkid_le16(js->js_l2pbsize)))
+	l2pbsize = blkid_le16(js->js_l2pbsize);
+	if (l2pbsize >= 32 || blkid_le32(js->js_pbsize) != (1U << l2pbsize))
 		return 1;
 
 	if ((blkid_le16(js->js_l2bsize) - blkid_le16(js->js_l2pbsize)) !=
@@ -1461,14 +1464,19 @@ static uint32_t exfat_next_cluster(struct blkid_probe *probe,
                                    const struct exfat_super_block *sb,
                                    uint32_t cluster)
 {
-    uint32_t *next;
+    uint32_t next;
     uint64_t offset;
+    unsigned char *buf;
 
     offset = exfat_block_to_offset(sb, sb->fat_block_start)
             + (uint64_t) cluster * sizeof (cluster);
-    next = (uint32_t *)get_buffer(probe, offset, sizeof (uint32_t));
+    buf = get_buffer(probe, offset, sizeof (uint32_t));
+    if (!buf)
+        return 0;
 
-    return next ? *next : 0;
+    memcpy(&next, buf, sizeof(next));
+
+    return next;
 }
 
 static struct exfat_entry_label *find_exfat_entry_label(
-- 
2.50.1


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-21 16:29 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-21 16:29 [PATCH] libblkid: avoid undefined shifts and an unaligned read in the probes Akira Patafio

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox