Re: [PATCH] kpartx: fix crash and truncated device creation with long -p delimiter

Martin Wilck <[email protected]> Wed, 15 Jul 2026 22:51:18 +0200
Newsgroups dev.linux.lists.dm-devel
Message-ID <[email protected]>
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)