On Wed, 2026-07-15 at 17:36 +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:
> - 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

Thanks for the patch! It looks good to me, but I'd suggest to add an
error return to dm_find_part() of testing partname[0] in the callers.

Regards
Martin

> 
> 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,
> +                                                             part
> name,
> +                                                             size
> of(partname),
> +                                                             &par
> t_uuid, verbose)) {
> +                                             if (partname[0] ==
> '\0') {
> +                                                     fprintf(stde
> rr,
> +                                                             "par
> tition 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),

-- 
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)

Reply via email to