[PATCH] kpartx: fix crash and truncated device creation with long -p delimiter
[email protected] Wed, 15 Jul 2026 17:36:21 +0800
| Newsgroups | dev.linux.lists.dm-devel |
|---|---|
| Message-ID | <[email protected]> |
From: Kou Wenqi <[email protected]> When the -p delimiter is long enough to make the formatted partition name exceed PARTNAME_SIZE (128 bytes), three issues occur: 1. format_partname() fails but snprintf has already written a truncated name into the buffer. dm_find_part() returns 0 and the caller proceeds to dm_addmap() with the truncated name, creating a device that was never intended. 2. dm_find_part() returns early without setting *part_uuid. The uninitialized local variable part_uuid then gets passed to check_uuid() -> strchr(), causing a SIGSEGV. 3. The callers cannot distinguish between "partition not found, create new" and "name construction failed" since both return 0. Fix by: - Clearing the name buffer in format_partname() on overflow, so that callers can detect the failure by checking partname[0] - In the ADD/UPDATE loops, after dm_find_part() returns 0, checking whether partname is empty and skipping the partition with an error message instead of proceeding with dm_addmap() - Initializing part_uuid to NULL in all three partition loop bodies (ADD/UPDATE main loop, container partition loop, DELETE loop) so that the "if (part_uuid && uuid)" guard correctly skips the UUID check when dm_find_part() returns early Reproduce steps: # Create test image dd if=/dev/zero of=/tmp/vhlg-test.img bs=1M count=10 parted /tmp/vhlg-test.img mklabel msdos parted /tmp/vhlg-test.img mkpart primary ext4 1MiB 5MiB # Reproduce kpartx -a -p $(python3 -c "print('A'*200)") /tmp/vhlg-test.img # Cleanup kpartx -d /tmp/vhlg-test.img rm -f /tmp/vhlg-test.img Signed-off-by: Kou Wenqi <[email protected]> --- kpartx/devmapper.c | 4 +++- kpartx/kpartx.c | 44 ++++++++++++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 45dac585..9eb6eeca 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -113,8 +113,10 @@ strip_slash (char * device) static int format_partname(char *buf, size_t bufsiz, const char *mapname, const char *delim, int part) { - if (safe_snprintf(buf, bufsiz, "%s%s%d", mapname, delim, part)) + if (safe_snprintf(buf, bufsiz, "%s%s%d", mapname, delim, part)) { + buf[0] = '\0'; return 0; + } strip_slash(buf); return 1; } diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index cfd82128..5972a251 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -437,7 +437,7 @@ main(int argc, char **argv){ case UPDATE: /* ADD and UPDATE share the same code that adds new partitions. */ for (j = 0, c = 0; j < n; j++) { - char *part_uuid, *reason; + char *part_uuid = NULL, *reason; if (slices[j].size == 0) continue; @@ -454,10 +454,20 @@ main(int argc, char **argv){ exit(1); } - op = (dm_find_part(mapname, delim, j + 1, uuid, - partname, sizeof(partname), - &part_uuid, verbose) ? - DM_DEVICE_RELOAD : DM_DEVICE_CREATE); + if (!dm_find_part(mapname, delim, j + 1, uuid, + partname, sizeof(partname), + &part_uuid, verbose)) { + if (partname[0] == '\0') { + fprintf(stderr, + "partition name too long for partition %d, skipping\n", + j + 1); + r++; + continue; + } + op = DM_DEVICE_CREATE; + } else { + op = DM_DEVICE_RELOAD; + } if (part_uuid && uuid) { if (check_uuid(uuid, part_uuid, &reason) != 0) { @@ -500,7 +510,7 @@ main(int argc, char **argv){ d = c; while (c) { for (j = 0; j < n; j++) { - char *part_uuid, *reason; + char *part_uuid = NULL, *reason; int k = slices[j].container - 1; if (slices[j].size == 0) @@ -526,11 +536,21 @@ main(int argc, char **argv){ exit(1); } - op = (dm_find_part(mapname, delim, j + 1, uuid, - partname, - sizeof(partname), - &part_uuid, verbose) ? - DM_DEVICE_RELOAD : DM_DEVICE_CREATE); + if (!dm_find_part(mapname, delim, j + 1, uuid, + partname, + sizeof(partname), + &part_uuid, verbose)) { + if (partname[0] == '\0') { + fprintf(stderr, + "partition name too long for partition %d, skipping\n", + j + 1); + r++; + continue; + } + op = DM_DEVICE_CREATE; + } else { + op = DM_DEVICE_RELOAD; + } if (part_uuid && uuid) { if (check_uuid(uuid, part_uuid, &reason) != 0) { @@ -570,7 +590,7 @@ main(int argc, char **argv){ } for (j = MAXSLICES-1; j >= 0; j--) { - char *part_uuid, *reason; + char *part_uuid = NULL, *reason; if (slices[j].size || !dm_find_part(mapname, delim, j + 1, uuid, partname, sizeof(partname), -- 2.43.0