]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libblkid: iso9660: validate root directory to reduce false positives
authorKarel Zak <kzak@redhat.com>
Tue, 24 Feb 2026 11:41:39 +0000 (12:41 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 24 Feb 2026 11:41:39 +0000 (12:41 +0100)
The CD001 magic signature at 32KB offset can match file data content
on other filesystems (e.g. an .iso file stored on XFS whose data
blocks happen to land at the device offset where blkid looks for the
ISO 9660 Primary Volume Descriptor). This causes blkid to report
ambivalent results for a device that has only one real filesystem.

Add validation of the root directory record from the PVD: read the
root directory extent and verify that the first entry is a valid "."
self-reference (file_id_len == 1, file_id == 0x00, extent location
pointing back to itself). This check reliably rejects false positive
CD001 signatures because the root directory LBA from the PVD points
to a different location on the device that contains unrelated data.

Addresses: https://github.com/util-linux/util-linux/issues/4031
Signed-off-by: Karel Zak <kzak@redhat.com>
libblkid/src/superblocks/iso9660.c

index a7a364f30af8ace0a4aced4ccfd70052f5718398..4ceaa4aca59968effb7b203f21027ef5def2e79d 100644 (file)
@@ -281,6 +281,50 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
        uint16_t logical_block_size = isonum_723(pvd->logical_block_size, false);
        uint32_t space_size = isonum_733(pvd->space_size, false);
 
+       if (logical_block_size == 0)
+               return 1;
+
+       /*
+        * Verify the root directory record to reduce false positives.
+        *
+        * The CD001 magic at 32KB can match data content on other
+        * filesystems (e.g. an .iso file stored on XFS). Validate that
+        * the root directory extent actually contains a valid directory
+        * entry -- the first entry must be the "." self-reference with
+        * file_id = 0x00 pointing back to the same LBA.
+        */
+       {
+               const unsigned char *rdr = is_hs ?
+                       pvd->hs.root_dir_record : pvd->iso.root_dir_record;
+               uint32_t root_lba = isonum_731(rdr + 2);
+               uint32_t root_len = isonum_731(rdr + 10);
+               uint64_t root_off = (uint64_t) root_lba * logical_block_size;
+               const unsigned char *rootdata;
+
+               if (root_len == 0)
+                       return 1;
+
+               rootdata = blkid_probe_get_buffer(pr, root_off,
+                               root_len < 2048 ? root_len : 2048);
+               if (!rootdata)
+                       return errno ? -errno : 1;
+
+               /* The first directory entry must be "." (self-reference):
+                *   - dr_len >= 34 (minimum directory record size)
+                *   - file_id_len == 1
+                *   - file_id == 0x00 (the "." identifier)
+                *   - extent location == root_lba (points to itself)
+                */
+               if (rootdata[0] < 34 ||
+                   rootdata[32] != 1 ||
+                   rootdata[33] != 0x00 ||
+                   isonum_731(rootdata + 2) != root_lba) {
+                       DBG(LOWPROBE, ul_debug("ISO9660: root dir validation "
+                               "failed (false positive CD001 signature?)"));
+                       return 1;
+               }
+       }
+
        blkid_probe_set_fsblocksize(pr, logical_block_size);
        blkid_probe_set_block_size(pr, logical_block_size);
        blkid_probe_set_fssize(pr, (uint64_t) space_size * logical_block_size);