Commit 2c9030ee authored by Stanislav Kinsbursky's avatar Stanislav Kinsbursky Committed by Trond Myklebust

SUNRPC: check RPC inode's pipe reference before dereferencing

There are 2 tightly bound objects: pipe data (created for kernel needs, has
reference to dentry, which depends on PipeFS mount/umount) and PipeFS
dentry/inode pair (created on mount for user-space needs). They both
independently may have or have not a valid reference to each other.
This means, that we have to make sure, that pipe->dentry reference is valid on
upcalls, and dentry->pipe reference is valid on downcalls. The latter check is
absent - my fault.
IOW, PipeFS dentry can be opened by some process (rpc.idmapd for example), but
it's pipe data can belong to NFS mount, which was unmounted already and thus
pipe data was destroyed.
To fix this, pipe reference have to be set to NULL on rpc_unlink() and checked
on PipeFS file operations instead of pipe->dentry check.

Note: PipeFS "poll" file operation will be updated in next patch, because it's
logic is more complicated.
Signed-off-by: default avatarStanislav Kinsbursky <skinsbursky@parallels.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent e9dbca8d
...@@ -174,6 +174,7 @@ rpc_close_pipes(struct inode *inode) ...@@ -174,6 +174,7 @@ rpc_close_pipes(struct inode *inode)
pipe->ops->release_pipe(inode); pipe->ops->release_pipe(inode);
cancel_delayed_work_sync(&pipe->queue_timeout); cancel_delayed_work_sync(&pipe->queue_timeout);
rpc_inode_setowner(inode, NULL); rpc_inode_setowner(inode, NULL);
RPC_I(inode)->pipe = NULL;
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
} }
...@@ -203,12 +204,13 @@ rpc_destroy_inode(struct inode *inode) ...@@ -203,12 +204,13 @@ rpc_destroy_inode(struct inode *inode)
static int static int
rpc_pipe_open(struct inode *inode, struct file *filp) rpc_pipe_open(struct inode *inode, struct file *filp)
{ {
struct rpc_pipe *pipe = RPC_I(inode)->pipe; struct rpc_pipe *pipe;
int first_open; int first_open;
int res = -ENXIO; int res = -ENXIO;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
if (pipe->dentry == NULL) pipe = RPC_I(inode)->pipe;
if (pipe == NULL)
goto out; goto out;
first_open = pipe->nreaders == 0 && pipe->nwriters == 0; first_open = pipe->nreaders == 0 && pipe->nwriters == 0;
if (first_open && pipe->ops->open_pipe) { if (first_open && pipe->ops->open_pipe) {
...@@ -229,12 +231,13 @@ rpc_pipe_open(struct inode *inode, struct file *filp) ...@@ -229,12 +231,13 @@ rpc_pipe_open(struct inode *inode, struct file *filp)
static int static int
rpc_pipe_release(struct inode *inode, struct file *filp) rpc_pipe_release(struct inode *inode, struct file *filp)
{ {
struct rpc_pipe *pipe = RPC_I(inode)->pipe; struct rpc_pipe *pipe;
struct rpc_pipe_msg *msg; struct rpc_pipe_msg *msg;
int last_close; int last_close;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
if (pipe->dentry == NULL) pipe = RPC_I(inode)->pipe;
if (pipe == NULL)
goto out; goto out;
msg = filp->private_data; msg = filp->private_data;
if (msg != NULL) { if (msg != NULL) {
...@@ -270,12 +273,13 @@ static ssize_t ...@@ -270,12 +273,13 @@ static ssize_t
rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset)
{ {
struct inode *inode = filp->f_path.dentry->d_inode; struct inode *inode = filp->f_path.dentry->d_inode;
struct rpc_pipe *pipe = RPC_I(inode)->pipe; struct rpc_pipe *pipe;
struct rpc_pipe_msg *msg; struct rpc_pipe_msg *msg;
int res = 0; int res = 0;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
if (pipe->dentry == NULL) { pipe = RPC_I(inode)->pipe;
if (pipe == NULL) {
res = -EPIPE; res = -EPIPE;
goto out_unlock; goto out_unlock;
} }
...@@ -313,13 +317,12 @@ static ssize_t ...@@ -313,13 +317,12 @@ static ssize_t
rpc_pipe_write(struct file *filp, const char __user *buf, size_t len, loff_t *offset) rpc_pipe_write(struct file *filp, const char __user *buf, size_t len, loff_t *offset)
{ {
struct inode *inode = filp->f_path.dentry->d_inode; struct inode *inode = filp->f_path.dentry->d_inode;
struct rpc_pipe *pipe = RPC_I(inode)->pipe;
int res; int res;
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
res = -EPIPE; res = -EPIPE;
if (pipe->dentry != NULL) if (RPC_I(inode)->pipe != NULL)
res = pipe->ops->downcall(filp, buf, len); res = RPC_I(inode)->pipe->ops->downcall(filp, buf, len);
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return res; return res;
} }
...@@ -344,16 +347,18 @@ static long ...@@ -344,16 +347,18 @@ static long
rpc_pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) rpc_pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{ {
struct inode *inode = filp->f_path.dentry->d_inode; struct inode *inode = filp->f_path.dentry->d_inode;
struct rpc_pipe *pipe = RPC_I(inode)->pipe; struct rpc_pipe *pipe;
int len; int len;
switch (cmd) { switch (cmd) {
case FIONREAD: case FIONREAD:
spin_lock(&pipe->lock); mutex_lock(&inode->i_mutex);
if (pipe->dentry == NULL) { pipe = RPC_I(inode)->pipe;
spin_unlock(&pipe->lock); if (pipe == NULL) {
mutex_unlock(&inode->i_mutex);
return -EPIPE; return -EPIPE;
} }
spin_lock(&pipe->lock);
len = pipe->pipelen; len = pipe->pipelen;
if (filp->private_data) { if (filp->private_data) {
struct rpc_pipe_msg *msg; struct rpc_pipe_msg *msg;
...@@ -361,6 +366,7 @@ rpc_pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -361,6 +366,7 @@ rpc_pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
len += msg->len - msg->copied; len += msg->len - msg->copied;
} }
spin_unlock(&pipe->lock); spin_unlock(&pipe->lock);
mutex_unlock(&inode->i_mutex);
return put_user(len, (int __user *)arg); return put_user(len, (int __user *)arg);
default: default:
return -EINVAL; return -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment