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)