Commit f713ae93 authored by Linus Torvalds's avatar Linus Torvalds Committed by Greg Kroah-Hartman

Revert "af_unix: Fix splice-bind deadlock"

[ Upstream commit 38f7bd94 ]

This reverts commit c845acb3.

It turns out that it just replaces one deadlock with another one: we can
still get the wrong lock ordering with the readlock due to overlayfs
calling back into the filesystem layer and still taking the vfs locks
after the readlock.

The proper solution ends up being to just split the readlock into two
pieces: the bind lock (taken *outside* the vfs locks) and the IO lock
(taken *inside* the filesystem locks).  The two locks are independent
anyway.
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Reviewed-by: default avatarShmulik Ladkani <shmulik.ladkani@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3fdf742b
...@@ -953,20 +953,32 @@ static struct sock *unix_find_other(struct net *net, ...@@ -953,20 +953,32 @@ static struct sock *unix_find_other(struct net *net,
return NULL; return NULL;
} }
static int unix_mknod(struct dentry *dentry, const struct path *path, umode_t mode, static int unix_mknod(const char *sun_path, umode_t mode, struct path *res)
struct path *res)
{ {
int err; struct dentry *dentry;
struct path path;
int err = 0;
/*
* Get the parent directory, calculate the hash for last
* component.
*/
dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
err = PTR_ERR(dentry);
if (IS_ERR(dentry))
return err;
err = security_path_mknod(path, dentry, mode, 0); /*
* All right, let's create it.
*/
err = security_path_mknod(&path, dentry, mode, 0);
if (!err) { if (!err) {
err = vfs_mknod(d_inode(path->dentry), dentry, mode, 0); err = vfs_mknod(d_inode(path.dentry), dentry, mode, 0);
if (!err) { if (!err) {
res->mnt = mntget(path->mnt); res->mnt = mntget(path.mnt);
res->dentry = dget(dentry); res->dentry = dget(dentry);
} }
} }
done_path_create(&path, dentry);
return err; return err;
} }
...@@ -977,12 +989,10 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -977,12 +989,10 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct unix_sock *u = unix_sk(sk); struct unix_sock *u = unix_sk(sk);
struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr; struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
char *sun_path = sunaddr->sun_path; char *sun_path = sunaddr->sun_path;
int err, name_err; int err;
unsigned int hash; unsigned int hash;
struct unix_address *addr; struct unix_address *addr;
struct hlist_head *list; struct hlist_head *list;
struct path path;
struct dentry *dentry;
err = -EINVAL; err = -EINVAL;
if (sunaddr->sun_family != AF_UNIX) if (sunaddr->sun_family != AF_UNIX)
...@@ -998,34 +1008,14 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -998,34 +1008,14 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
goto out; goto out;
addr_len = err; addr_len = err;
name_err = 0;
dentry = NULL;
if (sun_path[0]) {
/* Get the parent directory, calculate the hash for last
* component.
*/
dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
if (IS_ERR(dentry)) {
/* delay report until after 'already bound' check */
name_err = PTR_ERR(dentry);
dentry = NULL;
}
}
err = mutex_lock_interruptible(&u->readlock); err = mutex_lock_interruptible(&u->readlock);
if (err) if (err)
goto out_path; goto out;
err = -EINVAL; err = -EINVAL;
if (u->addr) if (u->addr)
goto out_up; goto out_up;
if (name_err) {
err = name_err == -EEXIST ? -EADDRINUSE : name_err;
goto out_up;
}
err = -ENOMEM; err = -ENOMEM;
addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL); addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL);
if (!addr) if (!addr)
...@@ -1036,11 +1026,11 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -1036,11 +1026,11 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
addr->hash = hash ^ sk->sk_type; addr->hash = hash ^ sk->sk_type;
atomic_set(&addr->refcnt, 1); atomic_set(&addr->refcnt, 1);
if (dentry) { if (sun_path[0]) {
struct path u_path; struct path path;
umode_t mode = S_IFSOCK | umode_t mode = S_IFSOCK |
(SOCK_INODE(sock)->i_mode & ~current_umask()); (SOCK_INODE(sock)->i_mode & ~current_umask());
err = unix_mknod(dentry, &path, mode, &u_path); err = unix_mknod(sun_path, mode, &path);
if (err) { if (err) {
if (err == -EEXIST) if (err == -EEXIST)
err = -EADDRINUSE; err = -EADDRINUSE;
...@@ -1048,9 +1038,9 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -1048,9 +1038,9 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
goto out_up; goto out_up;
} }
addr->hash = UNIX_HASH_SIZE; addr->hash = UNIX_HASH_SIZE;
hash = d_real_inode(dentry)->i_ino & (UNIX_HASH_SIZE - 1); hash = d_real_inode(path.dentry)->i_ino & (UNIX_HASH_SIZE - 1);
spin_lock(&unix_table_lock); spin_lock(&unix_table_lock);
u->path = u_path; u->path = path;
list = &unix_socket_table[hash]; list = &unix_socket_table[hash];
} else { } else {
spin_lock(&unix_table_lock); spin_lock(&unix_table_lock);
...@@ -1073,10 +1063,6 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -1073,10 +1063,6 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
spin_unlock(&unix_table_lock); spin_unlock(&unix_table_lock);
out_up: out_up:
mutex_unlock(&u->readlock); mutex_unlock(&u->readlock);
out_path:
if (dentry)
done_path_create(&path, dentry);
out: out:
return err; return err;
} }
......
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