Re: [PATCH v2] kpartx: fix crash and truncated device creation with long -p delimiter
Martin Wilck <[email protected]> Thu, 16 Jul 2026 09:39:42 +0200
| Newsgroups | dev.linux.lists.dm-devel |
|---|---|
| Message-ID | <[email protected]> |
On Thu, 2026-07-16 at 14:39 +0800, [email protected] wrote: > 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: > - Having dm_find_part() return -1 when format_partname() fails, so > that callers can test the return value directly (negative for > error, > 0 for not found, positive for found) > - In the ADD/UPDATE loops (both main and container), checking the > return value: negative means name too long (skip with error > message), > 0 means not found (create), positive means found (reload) > - In the DELETE loop, only proceeding with removal when > dm_find_part() > returns positive (partition found), skipping when it returns zero > (not found) or negative (error) > - 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 | 2 +- > kpartx/kpartx.c | 52 ++++++++++++++++++++++++++++++++------------ > -- > 2 files changed, 37 insertions(+), 17 deletions(-) > > diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c > index 45dac585..c50d6368 100644 > --- a/kpartx/devmapper.c > +++ b/kpartx/devmapper.c > @@ -646,7 +646,7 @@ int dm_find_part(const char *parent, const char > *delim, int part, > if (!format_partname(name, namesiz, parent, delim, part)) { > if (verbose) > fprintf(stderr, "partname too small\n"); > - return 0; > + return -1; > } > > r = dm_map_present(name, part_uuid); > diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c > index cfd82128..72f0bb70 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); > + int res = dm_find_part(mapname, > delim, j + 1, uuid, > + partname, > sizeof(partname), > + &part_uuid, > verbose); > + if (res < 0) { > + fprintf(stderr, > + "partition name too > long for partition %d, skipping\n", > + j + 1); Please move this message to dm_find_part() as well. Thanks Martin > + r++; > + continue; > + } else if (res == 0) { > + 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); > + int res = > dm_find_part(mapname, delim, j + 1, uuid, > + part > name, > + size > of(partname), > + &par > t_uuid, verbose); > + if (res < 0) { > + fprintf(stderr, > + "partition > name too long for partition %d, skipping\n", > + j + 1); > + r++; > + continue; > + } else if (res == 0) { > + op = > DM_DEVICE_CREATE; > + } else { > + op = > DM_DEVICE_RELOAD; > + } > > if (part_uuid && uuid) { > if (check_uuid(uuid, > part_uuid, &reason) != 0) { > @@ -570,11 +590,11 @@ main(int argc, char **argv){ > } > > for (j = MAXSLICES-1; j >= 0; j--) { > - char *part_uuid, *reason; > - if (slices[j].size || > - !dm_find_part(mapname, delim, j > + 1, uuid, > - partname, > sizeof(partname), > - &part_uuid, > verbose)) > + char *part_uuid = NULL, *reason; > + int res = dm_find_part(mapname, > delim, j + 1, uuid, > + partname, > sizeof(partname), > + &part_uuid, > verbose); > + if (slices[j].size || res <= 0) > continue; > > if (part_uuid && uuid) { -- Dr. Martin Wilck <[email protected]> SUSE Software Solutions Germany GmbH, Frankenstr. 146, 90461 Nürnberg, Germany Geschäftsführer: Jochen Jaser, Andrew McDonald (HRB 36809,AG Nürnberg)