]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libblkid: dos: validate EBR data and links within extended partition
authorKarel Zak <kzak@redhat.com>
Wed, 25 Mar 2026 10:54:28 +0000 (11:54 +0100)
committerKarel Zak <kzak@redhat.com>
Wed, 1 Apr 2026 08:48:06 +0000 (10:48 +0200)
The parse_dos_extended() function processes EBR (Extended Boot Record)
chains, but does not properly validate that partitions and links stay
within the master extended partition area [ex_start, ex_start+ex_size).

For EBR data partitions at index 0 and 1, there are no bounds checks
at all, unlike entries at index 2 and 3. This allows a crafted disk
image to register partitions at arbitrary sectors via uint32_t overflow
in the abs_start calculation (cur_start + start wraps to a small value).

Fix by:
 - using 64-bit arithmetic for the abs_start calculation to avoid
   uint32_t wraparound
 - applying the extended partition area bounds check to all EBR data
   entries, not just index >= 2
 - validating EBR link targets stay within the extended area and
   advance forward (preventing backward links that could loop)

Reported-by: Michele Piccinni <piccinni.michele@gmail.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
libblkid/src/partitions/dos.c

index d5a8aa3ae03a140fbd82d6b2f8c22377e68fe91f..5c78aac21d841f85088f28e540c3c580b78bdfdc 100644 (file)
@@ -46,6 +46,7 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
 {
        blkid_partlist ls = blkid_probe_get_partlist(pr);
        uint32_t cur_start = ex_start, cur_size = ex_size;
+       uint64_t ex_end = (uint64_t) ex_start + ex_size;
        const unsigned char *data;
        int ct_nodata = 0;      /* count ext.partitions without data partitions */
        int i;
@@ -88,24 +89,31 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
                /* Parse data partition */
                for (p = p0, i = 0; i < 4; i++, p++) {
                        uint32_t abs_start;
+                       uint64_t abs;
                        blkid_partition par;
 
                        /* the start is relative to the parental ext.partition */
                        start = dos_partition_get_start(p) * ssf;
                        size = dos_partition_get_size(p) * ssf;
-                       abs_start = cur_start + start;  /* absolute start */
 
                        if (!size || is_extended(p))
                                continue;
+
+                       abs = (uint64_t) cur_start + start;
+
+                       /* data partition must be within the extended area */
+                       if (abs < ex_start || abs + size > ex_end) {
+                               DBG(LOWPROBE, ul_debug("#%d: EBR data partition outside "
+                                       "extended -- ignore", i + 1));
+                               continue;
+                       }
+                       abs_start = (uint32_t) abs;
+
                        if (i >= 2) {
-                               /* extra checks to detect real data on
+                               /* extra check to detect real data on
                                 * 3rd and 4th entries */
                                if (start + size > cur_size)
                                        continue;
-                               if (abs_start < ex_start)
-                                       continue;
-                               if (abs_start + size > ex_start + ex_size)
-                                       continue;
                        }
 
                        /* Avoid recursive non-empty links, see ct_nodata counter */
@@ -142,8 +150,22 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
                if (i == 4)
                        goto leave;
 
-               cur_start = ex_start + start;
-               cur_size = size;
+               {
+                       uint64_t next = (uint64_t) ex_start + start;
+
+                       if (next + size > ex_end) {
+                               DBG(LOWPROBE, ul_debug("EBR link outside "
+                                       "extended area -- leave"));
+                               goto leave;
+                       }
+                       if (next <= cur_start) {
+                               DBG(LOWPROBE, ul_debug("EBR link does not "
+                                       "advance -- leave"));
+                               goto leave;
+                       }
+                       cur_start = (uint32_t) next;
+                       cur_size = size;
+               }
        }
 leave:
        return BLKID_PROBE_OK;