7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Chuck Lever commit acfbea6c5e27c68d6b6d12849eb2b7f8466ccf06 upstream. bl_pipe_downcall() wakes bl_resolve_deviceid() on any write of the right size, without checking that blkmapd has read the upcall being replied to. A write that arrives first returns the waiter while its struct rpc_pipe_msg is still queued on the pipe, which then holds a list_head into a dead stack frame. Reaching the pipe takes root, so the trigger is a broken or hostile blkmapd. The wait has two further defects. bl_resolve_deviceid() sets TASK_UNINTERRUPTIBLE only after rpc_queue_upcall() has made the message visible, so a reply that lands in between wakes a running task and the schedule() that follows sleeps forever holding bl_mutex. And nn->bl_mount_reply is never reset, so when the pipe purges an unread upcall the waiter takes the previous reply as its own. Move the message into struct nfs_net, where bl_mutex already limits the pipe to one upcall per net namespace. Accept a reply only while blkmapd has read the whole message and no earlier reply has been taken, and reject any other write with -EINVAL. Wait for that reply on a completion so it cannot slip past the sleep, and check msg->errno before trusting it. Fixes: fe0a9b740881 ("pnfsblock: add device operations") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260901-alemi-v2-5-e163f94a3a6e@kernel.org Signed-off-by: Chuck Lever Signed-off-by: Greg Kroah-Hartman --- fs/nfs/blocklayout/blocklayout.h | 5 --- fs/nfs/blocklayout/rpc_pipefs.c | 58 ++++++++++++++++++++++++--------------- fs/nfs/netns.h | 5 ++- 3 files changed, 41 insertions(+), 27 deletions(-) --- a/fs/nfs/blocklayout/blocklayout.h +++ b/fs/nfs/blocklayout/blocklayout.h @@ -161,11 +161,6 @@ BLK_LSEG2EXT(struct pnfs_layout_segment return BLK_LO2EXT(lseg->pls_layout); } -struct bl_pipe_msg { - struct rpc_pipe_msg msg; - wait_queue_head_t *bl_wq; -}; - struct bl_msg_hdr { u8 type; u16 totallen; /* length of entire message, including hdr itself */ --- a/fs/nfs/blocklayout/rpc_pipefs.c +++ b/fs/nfs/blocklayout/rpc_pipefs.c @@ -55,17 +55,15 @@ bl_resolve_deviceid(struct nfs_server *s struct net *net = server->nfs_client->cl_net; struct nfs_net *nn = net_generic(net, nfs_net_id); struct bl_dev_msg *reply = &nn->bl_mount_reply; - struct bl_pipe_msg bl_pipe_msg; - struct rpc_pipe_msg *msg = &bl_pipe_msg.msg; + struct rpc_pipe_msg *msg = &nn->bl_pipe_msg; + struct rpc_pipe *pipe = nn->bl_device_pipe; struct bl_msg_hdr *bl_msg; - DECLARE_WAITQUEUE(wq, current); dev_t dev = 0; int rc; dprintk("%s CREATING PIPEFS MESSAGE\n", __func__); mutex_lock(&nn->bl_mutex); - bl_pipe_msg.bl_wq = &nn->bl_wq; b->simple.len += 4; /* single volume */ if (b->simple.len > PAGE_SIZE) @@ -83,17 +81,15 @@ bl_resolve_deviceid(struct nfs_server *s nfs4_encode_simple(msg->data + sizeof(*bl_msg), b); dprintk("%s CALLING USERSPACE DAEMON\n", __func__); - add_wait_queue(&nn->bl_wq, &wq); - rc = rpc_queue_upcall(nn->bl_device_pipe, msg); - if (rc < 0) { - remove_wait_queue(&nn->bl_wq, &wq); + reinit_completion(&nn->bl_done); + rc = rpc_queue_upcall(pipe, msg); + if (rc < 0) goto out_free_data; - } - set_current_state(TASK_UNINTERRUPTIBLE); - schedule(); - remove_wait_queue(&nn->bl_wq, &wq); + wait_for_completion(&nn->bl_done); + if (msg->errno < 0) + goto out_free_data; if (reply->status != BL_DEVICE_REQUEST_PROC) { printk(KERN_WARNING "%s failed to decode device: %d\n", __func__, reply->status); @@ -113,26 +109,46 @@ static ssize_t bl_pipe_downcall(struct f { struct nfs_net *nn = net_generic(file_inode(filp)->i_sb->s_fs_info, nfs_net_id); + struct rpc_pipe *pipe = nn->bl_device_pipe; + struct bl_dev_msg reply; + bool accepted; - if (mlen != sizeof (struct bl_dev_msg)) + if (mlen != sizeof(reply)) return -EINVAL; - - if (copy_from_user(&nn->bl_mount_reply, src, mlen) != 0) + if (copy_from_user(&reply, src, mlen) != 0) return -EFAULT; - wake_up(&nn->bl_wq); - + /* + * Accept a reply only after blkmapd has read the whole upcall. + * Completion retires the message, here and in + * bl_pipe_destroy_msg(), so a later reply finds nothing in + * flight. + */ + spin_lock(&pipe->lock); + accepted = rpc_msg_is_inflight(&nn->bl_pipe_msg); + if (accepted) { + nn->bl_pipe_msg.copied = 0; + nn->bl_mount_reply = reply; + complete(&nn->bl_done); + } + spin_unlock(&pipe->lock); + if (!accepted) + return -EINVAL; return mlen; } static void bl_pipe_destroy_msg(struct rpc_pipe_msg *msg) { - struct bl_pipe_msg *bl_pipe_msg = - container_of(msg, struct bl_pipe_msg, msg); + struct nfs_net *nn = container_of(msg, struct nfs_net, bl_pipe_msg); + struct rpc_pipe *pipe = nn->bl_device_pipe; if (msg->errno >= 0) return; - wake_up(bl_pipe_msg->bl_wq); + + spin_lock(&pipe->lock); + msg->copied = 0; + spin_unlock(&pipe->lock); + complete(&nn->bl_done); } static const struct rpc_pipe_ops bl_upcall_ops = { @@ -221,7 +237,7 @@ static int nfs4blocklayout_net_init(stru int err; mutex_init(&nn->bl_mutex); - init_waitqueue_head(&nn->bl_wq); + init_completion(&nn->bl_done); nn->bl_device_pipe = rpc_mkpipe_data(&bl_upcall_ops, 0); if (IS_ERR(nn->bl_device_pipe)) return PTR_ERR(nn->bl_device_pipe); --- a/fs/nfs/netns.h +++ b/fs/nfs/netns.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include struct bl_dev_msg { int32_t status; @@ -21,8 +23,9 @@ struct nfs_netns_client; struct nfs_net { struct cache_detail *nfs_dns_resolve; struct rpc_pipe *bl_device_pipe; + struct rpc_pipe_msg bl_pipe_msg; struct bl_dev_msg bl_mount_reply; - wait_queue_head_t bl_wq; + struct completion bl_done; struct mutex bl_mutex; struct list_head nfs_client_list; struct list_head nfs_volume_list;