VYPR
Unrated severityNVD Advisory· Published May 28, 2026

CVE-2026-46118

CVE-2026-46118

Description

In the Linux kernel, the following vulnerability has been resolved:

pseries/papr-hvpipe: Fix null ptr deref in papr_hvpipe_dev_create_handle()

commit 6d3789d347a7 ("papr-hvpipe: convert papr_hvpipe_dev_create_handle() to FD_PREPARE()"), changed the create handle to FD_PREPARE(), but it caused kernel null-ptr-deref because after call to retain_and_null_ptr(src_info), src_info is re-used for adding it to the global list.

Getting the following kernel panic in papr_hvpipe_dev_create_handle() when trying to add src_info to the list. Kernel attempted to write user page (0) - exploit attempt? (uid: 0) BUG: Kernel NULL pointer dereference on write at 0x00000000 Faulting instruction address: 0xc0000000001b44a0 Oops: Kernel access of bad area, sig: 11 [#1] ... Call Trace: papr_hvpipe_dev_ioctl+0x1f4/0x48c (unreliable) sys_ioctl+0x528/0x1064 system_call_exception+0x128/0x360 system_call_vectored_common+0x15c/0x2ec

Now, the error handling with FD_PREPARE's file cleanup and __free(kfree) auto cleanup is getting too convoluted. This is mainly because we need to ensure only 1 user get the srcID handle. To simplify this, we allocate prepare the src_info in the beginning and add it to the global list under a spinlock after checking that no duplicates exist.

This simplify the error handling where if the FD_ADD fails, we can simply remove the src_info from the list and consume any pending msg in hvpipe to be cleared, after src_info became visible in the global list.

Affected products

1

Patches

6
735439394dde

Revert "papr-hvpipe: convert papr_hvpipe_dev_create_handle() to FD_PREPARE()"

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitSasha LevinMay 18, 2026Fixed in 6.18.33via kernel-cna
2 files changed · +60 20
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 10 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3d8672642c2513..431f6a3da4c5af 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -484,7 +484,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	struct file *file;
    +	long err;
    +	int fd;
     
     	spin_lock(&hvpipe_src_list_lock);
     	/*
    @@ -508,13 +511,20 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    +	fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
    +	if (fd < 0) {
    +		err = fd;
    +		goto free_buf;
    +	}
    +
    +	file = anon_inode_getfile("[papr-hvpipe]",
    +			&papr_hvpipe_handle_ops, (void *)src_info,
    +			O_RDWR);
    +	if (IS_ERR(file)) {
    +		err = PTR_ERR(file);
    +		goto free_fd;
    +	}
     
    -	retain_and_null_ptr(src_info);
     	spin_lock(&hvpipe_src_list_lock);
     	/*
     	 * If two processes are executing ioctl() for the same
    @@ -523,11 +533,22 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	 */
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock(&hvpipe_src_list_lock);
    -		return -EALREADY;
    +		err = -EALREADY;
    +		goto free_file;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock(&hvpipe_src_list_lock);
    -	return fd_publish(fdf);
    +
    +	fd_install(fd, file);
    +	return fd;
    +
    +free_file:
    +	fput(file);
    +free_fd:
    +	put_unused_fd(fd);
    +free_buf:
    +	kfree(src_info);
    +	return err;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 10 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3d8672642c2513..431f6a3da4c5af 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -484,7 +484,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	struct file *file;
    +	long err;
    +	int fd;
     
     	spin_lock(&hvpipe_src_list_lock);
     	/*
    @@ -508,13 +511,20 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    +	fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
    +	if (fd < 0) {
    +		err = fd;
    +		goto free_buf;
    +	}
    +
    +	file = anon_inode_getfile("[papr-hvpipe]",
    +			&papr_hvpipe_handle_ops, (void *)src_info,
    +			O_RDWR);
    +	if (IS_ERR(file)) {
    +		err = PTR_ERR(file);
    +		goto free_fd;
    +	}
     
    -	retain_and_null_ptr(src_info);
     	spin_lock(&hvpipe_src_list_lock);
     	/*
     	 * If two processes are executing ioctl() for the same
    @@ -523,11 +533,22 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	 */
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock(&hvpipe_src_list_lock);
    -		return -EALREADY;
    +		err = -EALREADY;
    +		goto free_file;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock(&hvpipe_src_list_lock);
    -	return fd_publish(fdf);
    +
    +	fd_install(fd, file);
    +	return fd;
    +
    +free_file:
    +	fput(file);
    +free_fd:
    +	put_unused_fd(fd);
    +free_buf:
    +	kfree(src_info);
    +	return err;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
cf51bec1560f

pseries/papr-hvpipe: Fix null ptr deref in papr_hvpipe_dev_create_handle()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git"Ritesh Harjani (IBM)"Fixed in 7.0.7via kernel-cna
2 files changed · +60 56
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 28 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3392874ebdf686..402781299497a5 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -480,23 +480,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	int fd;
     	unsigned long flags;
     
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    -	/*
    -	 * Do not allow more than one process communicates with
    -	 * each source.
    -	 */
    -	src_info = hvpipe_find_source(srcID);
    -	if (src_info) {
    -		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -		pr_err("pid(%d) is already using the source(%d)\n",
    -				src_info->tsk->pid, srcID);
    -		return -EALREADY;
    -	}
    -	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -
     	src_info = kzalloc_obj(*src_info, GFP_KERNEL_ACCOUNT);
     	if (!src_info)
     		return -ENOMEM;
    @@ -505,26 +492,42 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    -
    -	retain_and_null_ptr(src_info);
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	/*
    -	 * If two processes are executing ioctl() for the same
    -	 * source ID concurrently, prevent the second process to
    -	 * acquire FD.
    +	 * Do not allow more than one process communicates with
    +	 * each source.
     	 */
    +	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		pr_err("pid(%d) could not get the source(%d)\n",
    +				src_info->tsk->pid, srcID);
    +		kfree(src_info);
     		return -EALREADY;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -	return fd_publish(fdf);
    +
    +	fd = FD_ADD(O_RDONLY | O_CLOEXEC,
    +		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    +				      (void *)src_info, O_RDWR));
    +	if (fd < 0) {
    +		spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    +		list_del(&src_info->list);
    +		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		/*
    +		 * if we fail to add FD, that means no userspace program is
    +		 * polling. In that case if there is a msg pending because the
    +		 * interrupt was fired after the src_info was added to the
    +		 * global list, then let's consume it here, to unblock the
    +		 * hvpipe
    +		 */
    +		if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE)
    +			hvpipe_rtas_recv_msg(NULL, 0);
    +		kfree(src_info);
    +		return fd;
    +	}
    +
    +	return fd;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 28 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3392874ebdf686..402781299497a5 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -480,23 +480,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	int fd;
     	unsigned long flags;
     
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    -	/*
    -	 * Do not allow more than one process communicates with
    -	 * each source.
    -	 */
    -	src_info = hvpipe_find_source(srcID);
    -	if (src_info) {
    -		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -		pr_err("pid(%d) is already using the source(%d)\n",
    -				src_info->tsk->pid, srcID);
    -		return -EALREADY;
    -	}
    -	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -
     	src_info = kzalloc_obj(*src_info, GFP_KERNEL_ACCOUNT);
     	if (!src_info)
     		return -ENOMEM;
    @@ -505,26 +492,42 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    -
    -	retain_and_null_ptr(src_info);
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	/*
    -	 * If two processes are executing ioctl() for the same
    -	 * source ID concurrently, prevent the second process to
    -	 * acquire FD.
    +	 * Do not allow more than one process communicates with
    +	 * each source.
     	 */
    +	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		pr_err("pid(%d) could not get the source(%d)\n",
    +				src_info->tsk->pid, srcID);
    +		kfree(src_info);
     		return -EALREADY;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -	return fd_publish(fdf);
    +
    +	fd = FD_ADD(O_RDONLY | O_CLOEXEC,
    +		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    +				      (void *)src_info, O_RDWR));
    +	if (fd < 0) {
    +		spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    +		list_del(&src_info->list);
    +		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		/*
    +		 * if we fail to add FD, that means no userspace program is
    +		 * polling. In that case if there is a msg pending because the
    +		 * interrupt was fired after the src_info was added to the
    +		 * global list, then let's consume it here, to unblock the
    +		 * hvpipe
    +		 */
    +		if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE)
    +			hvpipe_rtas_recv_msg(NULL, 0);
    +		kfree(src_info);
    +		return fd;
    +	}
    +
    +	return fd;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
1b9f7aafa44f

pseries/papr-hvpipe: Fix null ptr deref in papr_hvpipe_dev_create_handle()

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git"Ritesh Harjani (IBM)"Fixed in 7.1-rc3via kernel-cna
2 files changed · +60 56
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 28 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3392874ebdf686..402781299497a5 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -480,23 +480,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	int fd;
     	unsigned long flags;
     
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    -	/*
    -	 * Do not allow more than one process communicates with
    -	 * each source.
    -	 */
    -	src_info = hvpipe_find_source(srcID);
    -	if (src_info) {
    -		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -		pr_err("pid(%d) is already using the source(%d)\n",
    -				src_info->tsk->pid, srcID);
    -		return -EALREADY;
    -	}
    -	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -
     	src_info = kzalloc_obj(*src_info, GFP_KERNEL_ACCOUNT);
     	if (!src_info)
     		return -ENOMEM;
    @@ -505,26 +492,42 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    -
    -	retain_and_null_ptr(src_info);
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	/*
    -	 * If two processes are executing ioctl() for the same
    -	 * source ID concurrently, prevent the second process to
    -	 * acquire FD.
    +	 * Do not allow more than one process communicates with
    +	 * each source.
     	 */
    +	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		pr_err("pid(%d) could not get the source(%d)\n",
    +				src_info->tsk->pid, srcID);
    +		kfree(src_info);
     		return -EALREADY;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -	return fd_publish(fdf);
    +
    +	fd = FD_ADD(O_RDONLY | O_CLOEXEC,
    +		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    +				      (void *)src_info, O_RDWR));
    +	if (fd < 0) {
    +		spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    +		list_del(&src_info->list);
    +		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		/*
    +		 * if we fail to add FD, that means no userspace program is
    +		 * polling. In that case if there is a msg pending because the
    +		 * interrupt was fired after the src_info was added to the
    +		 * global list, then let's consume it here, to unblock the
    +		 * hvpipe
    +		 */
    +		if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE)
    +			hvpipe_rtas_recv_msg(NULL, 0);
    +		kfree(src_info);
    +		return fd;
    +	}
    +
    +	return fd;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 28 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3392874ebdf686..402781299497a5 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -480,23 +480,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	int fd;
     	unsigned long flags;
     
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    -	/*
    -	 * Do not allow more than one process communicates with
    -	 * each source.
    -	 */
    -	src_info = hvpipe_find_source(srcID);
    -	if (src_info) {
    -		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -		pr_err("pid(%d) is already using the source(%d)\n",
    -				src_info->tsk->pid, srcID);
    -		return -EALREADY;
    -	}
    -	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -
     	src_info = kzalloc_obj(*src_info, GFP_KERNEL_ACCOUNT);
     	if (!src_info)
     		return -ENOMEM;
    @@ -505,26 +492,42 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    -
    -	retain_and_null_ptr(src_info);
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	/*
    -	 * If two processes are executing ioctl() for the same
    -	 * source ID concurrently, prevent the second process to
    -	 * acquire FD.
    +	 * Do not allow more than one process communicates with
    +	 * each source.
     	 */
    +	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		pr_err("pid(%d) could not get the source(%d)\n",
    +				src_info->tsk->pid, srcID);
    +		kfree(src_info);
     		return -EALREADY;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -	return fd_publish(fdf);
    +
    +	fd = FD_ADD(O_RDONLY | O_CLOEXEC,
    +		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    +				      (void *)src_info, O_RDWR));
    +	if (fd < 0) {
    +		spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    +		list_del(&src_info->list);
    +		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		/*
    +		 * if we fail to add FD, that means no userspace program is
    +		 * polling. In that case if there is a msg pending because the
    +		 * interrupt was fired after the src_info was added to the
    +		 * global list, then let's consume it here, to unblock the
    +		 * hvpipe
    +		 */
    +		if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE)
    +			hvpipe_rtas_recv_msg(NULL, 0);
    +		kfree(src_info);
    +		return fd;
    +	}
    +
    +	return fd;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
735439394dde

Revert "papr-hvpipe: convert papr_hvpipe_dev_create_handle() to FD_PREPARE()"

2 files changed · +60 20
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 10 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3d8672642c2513..431f6a3da4c5af 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -484,7 +484,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	struct file *file;
    +	long err;
    +	int fd;
     
     	spin_lock(&hvpipe_src_list_lock);
     	/*
    @@ -508,13 +511,20 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    +	fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
    +	if (fd < 0) {
    +		err = fd;
    +		goto free_buf;
    +	}
    +
    +	file = anon_inode_getfile("[papr-hvpipe]",
    +			&papr_hvpipe_handle_ops, (void *)src_info,
    +			O_RDWR);
    +	if (IS_ERR(file)) {
    +		err = PTR_ERR(file);
    +		goto free_fd;
    +	}
     
    -	retain_and_null_ptr(src_info);
     	spin_lock(&hvpipe_src_list_lock);
     	/*
     	 * If two processes are executing ioctl() for the same
    @@ -523,11 +533,22 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	 */
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock(&hvpipe_src_list_lock);
    -		return -EALREADY;
    +		err = -EALREADY;
    +		goto free_file;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock(&hvpipe_src_list_lock);
    -	return fd_publish(fdf);
    +
    +	fd_install(fd, file);
    +	return fd;
    +
    +free_file:
    +	fput(file);
    +free_fd:
    +	put_unused_fd(fd);
    +free_buf:
    +	kfree(src_info);
    +	return err;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 10 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3d8672642c2513..431f6a3da4c5af 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -484,7 +484,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	struct file *file;
    +	long err;
    +	int fd;
     
     	spin_lock(&hvpipe_src_list_lock);
     	/*
    @@ -508,13 +511,20 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    +	fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
    +	if (fd < 0) {
    +		err = fd;
    +		goto free_buf;
    +	}
    +
    +	file = anon_inode_getfile("[papr-hvpipe]",
    +			&papr_hvpipe_handle_ops, (void *)src_info,
    +			O_RDWR);
    +	if (IS_ERR(file)) {
    +		err = PTR_ERR(file);
    +		goto free_fd;
    +	}
     
    -	retain_and_null_ptr(src_info);
     	spin_lock(&hvpipe_src_list_lock);
     	/*
     	 * If two processes are executing ioctl() for the same
    @@ -523,11 +533,22 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	 */
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock(&hvpipe_src_list_lock);
    -		return -EALREADY;
    +		err = -EALREADY;
    +		goto free_file;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock(&hvpipe_src_list_lock);
    -	return fd_publish(fdf);
    +
    +	fd_install(fd, file);
    +	return fd;
    +
    +free_file:
    +	fput(file);
    +free_fd:
    +	put_unused_fd(fd);
    +free_buf:
    +	kfree(src_info);
    +	return err;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
1b9f7aafa44f

pseries/papr-hvpipe: Fix null ptr deref in papr_hvpipe_dev_create_handle()

2 files changed · +60 56
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 28 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3392874ebdf686..402781299497a5 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -480,23 +480,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	int fd;
     	unsigned long flags;
     
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    -	/*
    -	 * Do not allow more than one process communicates with
    -	 * each source.
    -	 */
    -	src_info = hvpipe_find_source(srcID);
    -	if (src_info) {
    -		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -		pr_err("pid(%d) is already using the source(%d)\n",
    -				src_info->tsk->pid, srcID);
    -		return -EALREADY;
    -	}
    -	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -
     	src_info = kzalloc_obj(*src_info, GFP_KERNEL_ACCOUNT);
     	if (!src_info)
     		return -ENOMEM;
    @@ -505,26 +492,42 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    -
    -	retain_and_null_ptr(src_info);
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	/*
    -	 * If two processes are executing ioctl() for the same
    -	 * source ID concurrently, prevent the second process to
    -	 * acquire FD.
    +	 * Do not allow more than one process communicates with
    +	 * each source.
     	 */
    +	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		pr_err("pid(%d) could not get the source(%d)\n",
    +				src_info->tsk->pid, srcID);
    +		kfree(src_info);
     		return -EALREADY;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -	return fd_publish(fdf);
    +
    +	fd = FD_ADD(O_RDONLY | O_CLOEXEC,
    +		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    +				      (void *)src_info, O_RDWR));
    +	if (fd < 0) {
    +		spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    +		list_del(&src_info->list);
    +		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		/*
    +		 * if we fail to add FD, that means no userspace program is
    +		 * polling. In that case if there is a msg pending because the
    +		 * interrupt was fired after the src_info was added to the
    +		 * global list, then let's consume it here, to unblock the
    +		 * hvpipe
    +		 */
    +		if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE)
    +			hvpipe_rtas_recv_msg(NULL, 0);
    +		kfree(src_info);
    +		return fd;
    +	}
    +
    +	return fd;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 28 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3392874ebdf686..402781299497a5 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -480,23 +480,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	int fd;
     	unsigned long flags;
     
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    -	/*
    -	 * Do not allow more than one process communicates with
    -	 * each source.
    -	 */
    -	src_info = hvpipe_find_source(srcID);
    -	if (src_info) {
    -		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -		pr_err("pid(%d) is already using the source(%d)\n",
    -				src_info->tsk->pid, srcID);
    -		return -EALREADY;
    -	}
    -	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -
     	src_info = kzalloc_obj(*src_info, GFP_KERNEL_ACCOUNT);
     	if (!src_info)
     		return -ENOMEM;
    @@ -505,26 +492,42 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    -
    -	retain_and_null_ptr(src_info);
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	/*
    -	 * If two processes are executing ioctl() for the same
    -	 * source ID concurrently, prevent the second process to
    -	 * acquire FD.
    +	 * Do not allow more than one process communicates with
    +	 * each source.
     	 */
    +	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		pr_err("pid(%d) could not get the source(%d)\n",
    +				src_info->tsk->pid, srcID);
    +		kfree(src_info);
     		return -EALREADY;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -	return fd_publish(fdf);
    +
    +	fd = FD_ADD(O_RDONLY | O_CLOEXEC,
    +		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    +				      (void *)src_info, O_RDWR));
    +	if (fd < 0) {
    +		spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    +		list_del(&src_info->list);
    +		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		/*
    +		 * if we fail to add FD, that means no userspace program is
    +		 * polling. In that case if there is a msg pending because the
    +		 * interrupt was fired after the src_info was added to the
    +		 * global list, then let's consume it here, to unblock the
    +		 * hvpipe
    +		 */
    +		if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE)
    +			hvpipe_rtas_recv_msg(NULL, 0);
    +		kfree(src_info);
    +		return fd;
    +	}
    +
    +	return fd;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
cf51bec1560f

pseries/papr-hvpipe: Fix null ptr deref in papr_hvpipe_dev_create_handle()

2 files changed · +60 56
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 28 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3392874ebdf686..402781299497a5 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -480,23 +480,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	int fd;
     	unsigned long flags;
     
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    -	/*
    -	 * Do not allow more than one process communicates with
    -	 * each source.
    -	 */
    -	src_info = hvpipe_find_source(srcID);
    -	if (src_info) {
    -		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -		pr_err("pid(%d) is already using the source(%d)\n",
    -				src_info->tsk->pid, srcID);
    -		return -EALREADY;
    -	}
    -	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -
     	src_info = kzalloc_obj(*src_info, GFP_KERNEL_ACCOUNT);
     	if (!src_info)
     		return -ENOMEM;
    @@ -505,26 +492,42 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    -
    -	retain_and_null_ptr(src_info);
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	/*
    -	 * If two processes are executing ioctl() for the same
    -	 * source ID concurrently, prevent the second process to
    -	 * acquire FD.
    +	 * Do not allow more than one process communicates with
    +	 * each source.
     	 */
    +	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		pr_err("pid(%d) could not get the source(%d)\n",
    +				src_info->tsk->pid, srcID);
    +		kfree(src_info);
     		return -EALREADY;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -	return fd_publish(fdf);
    +
    +	fd = FD_ADD(O_RDONLY | O_CLOEXEC,
    +		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    +				      (void *)src_info, O_RDWR));
    +	if (fd < 0) {
    +		spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    +		list_del(&src_info->list);
    +		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		/*
    +		 * if we fail to add FD, that means no userspace program is
    +		 * polling. In that case if there is a msg pending because the
    +		 * interrupt was fired after the src_info was added to the
    +		 * global list, then let's consume it here, to unblock the
    +		 * hvpipe
    +		 */
    +		if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE)
    +			hvpipe_rtas_recv_msg(NULL, 0);
    +		kfree(src_info);
    +		return fd;
    +	}
    +
    +	return fd;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    
  • arch/powerpc/platforms/pseries/papr-hvpipe.c+30 28 modified
    diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    index 3392874ebdf686..402781299497a5 100644
    --- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
    +++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
    @@ -480,23 +480,10 @@ static const struct file_operations papr_hvpipe_handle_ops = {
     
     static int papr_hvpipe_dev_create_handle(u32 srcID)
     {
    -	struct hvpipe_source_info *src_info __free(kfree) = NULL;
    +	struct hvpipe_source_info *src_info;
    +	int fd;
     	unsigned long flags;
     
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    -	/*
    -	 * Do not allow more than one process communicates with
    -	 * each source.
    -	 */
    -	src_info = hvpipe_find_source(srcID);
    -	if (src_info) {
    -		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -		pr_err("pid(%d) is already using the source(%d)\n",
    -				src_info->tsk->pid, srcID);
    -		return -EALREADY;
    -	}
    -	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -
     	src_info = kzalloc_obj(*src_info, GFP_KERNEL_ACCOUNT);
     	if (!src_info)
     		return -ENOMEM;
    @@ -505,26 +492,42 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
     	src_info->tsk = current;
     	init_waitqueue_head(&src_info->recv_wqh);
     
    -	FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
    -		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    -				      (void *)src_info, O_RDWR));
    -	if (fdf.err)
    -		return fdf.err;
    -
    -	retain_and_null_ptr(src_info);
    -	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	/*
    -	 * If two processes are executing ioctl() for the same
    -	 * source ID concurrently, prevent the second process to
    -	 * acquire FD.
    +	 * Do not allow more than one process communicates with
    +	 * each source.
     	 */
    +	spin_lock_irqsave(&hvpipe_src_list_lock, flags);
     	if (hvpipe_find_source(srcID)) {
     		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		pr_err("pid(%d) could not get the source(%d)\n",
    +				src_info->tsk->pid, srcID);
    +		kfree(src_info);
     		return -EALREADY;
     	}
     	list_add(&src_info->list, &hvpipe_src_list);
     	spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    -	return fd_publish(fdf);
    +
    +	fd = FD_ADD(O_RDONLY | O_CLOEXEC,
    +		   anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
    +				      (void *)src_info, O_RDWR));
    +	if (fd < 0) {
    +		spin_lock_irqsave(&hvpipe_src_list_lock, flags);
    +		list_del(&src_info->list);
    +		spin_unlock_irqrestore(&hvpipe_src_list_lock, flags);
    +		/*
    +		 * if we fail to add FD, that means no userspace program is
    +		 * polling. In that case if there is a msg pending because the
    +		 * interrupt was fired after the src_info was added to the
    +		 * global list, then let's consume it here, to unblock the
    +		 * hvpipe
    +		 */
    +		if (src_info->hvpipe_status & HVPIPE_MSG_AVAILABLE)
    +			hvpipe_rtas_recv_msg(NULL, 0);
    +		kfree(src_info);
    +		return fd;
    +	}
    +
    +	return fd;
     }
     
     /*
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Use-after-free / NULL-pointer dereference: `retain_and_null_ptr(src_info)` sets the pointer to NULL, but the pointer is subsequently dereferenced when adding `src_info` to the global list."

Attack vector

An unprivileged local user can trigger a kernel NULL-pointer dereference by calling the `ioctl()` syscall on the papr-hvpipe device, which reaches `papr_hvpipe_dev_create_handle()` [patch_id=2898556]. After the faulty `FD_PREPARE()` conversion, `retain_and_null_ptr(src_info)` sets the `src_info` pointer to NULL, but the code then attempts to dereference that NULL pointer when adding `src_info` to the global list under the spinlock [patch_id=2898556]. The kernel oops shows the faulting instruction address at 0xc0000000001b44a0 and reports "Kernel attempted to write user page (0)" [patch_id=2898556].

Affected code

The vulnerability is in `papr_hvpipe_dev_create_handle()` in `arch/powerpc/platforms/pseries/papr-hvpipe.c`. The faulty commit (09c15bbbed53) converted the function to use `FD_PREPARE()` and called `retain_and_null_ptr(src_info)`, which set `src_info` to NULL before it was later re-used to add the entry to the global list `hvpipe_src_list` [patch_id=2898556].

What the fix does

The fix (commit 1b9f7aafa44f) restructures `papr_hvpipe_dev_create_handle()` to allocate `src_info` first, then add it to the global list under a spinlock after checking for duplicates, and only then call `FD_ADD()` [patch_id=2898556]. This avoids the use-after-null problem because `retain_and_null_ptr()` is removed entirely. If `FD_ADD()` fails, the fix properly removes `src_info` from the list, consumes any pending message via `hvpipe_rtas_recv_msg()`, and frees the memory [patch_id=2898556]. A companion revert (patch 735439394dde) undoes the original faulty `FD_PREPARE()` conversion [patch_id=2898555].

Preconditions

  • inputLocal access to the papr-hvpipe device (ioctl syscall)
  • configThe system must be a PowerPC pseries platform with the papr-hvpipe driver loaded

Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.