diff options
author | WANG Cong <xiyou.wangcong@gmail.com> | 2017-01-23 14:17:35 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-01-24 14:30:56 -0500 |
commit | 0fb44559ffd67de8517098b81f675fa0210f13f0 (patch) | |
tree | 4fe2e2d2f415b7b831e8c3ee999a1915a24d2d4e /net/unix/af_unix.c | |
parent | a59b7e0246774e28193126fe7fdbbd0ae9c67dcc (diff) |
af_unix: move unix_mknod() out of bindlock
Dmitry reported a deadlock scenario:
unix_bind() path:
u->bindlock ==> sb_writer
do_splice() path:
sb_writer ==> pipe->mutex ==> u->bindlock
In the unix_bind() code path, unix_mknod() does not have to
be done with u->bindlock held, since it is a pure fs operation,
so we can just move unix_mknod() out.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Tested-by: Dmitry Vyukov <dvyukov@google.com>
Cc: Rainer Weikusat <rweikusat@mobileactivedefense.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/unix/af_unix.c')
-rw-r--r-- | net/unix/af_unix.c | 27 |
1 files changed, 16 insertions, 11 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 127656ebe7be..cef79873b09d 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
@@ -995,6 +995,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
995 | unsigned int hash; | 995 | unsigned int hash; |
996 | struct unix_address *addr; | 996 | struct unix_address *addr; |
997 | struct hlist_head *list; | 997 | struct hlist_head *list; |
998 | struct path path = { NULL, NULL }; | ||
998 | 999 | ||
999 | err = -EINVAL; | 1000 | err = -EINVAL; |
1000 | if (sunaddr->sun_family != AF_UNIX) | 1001 | if (sunaddr->sun_family != AF_UNIX) |
@@ -1010,9 +1011,20 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
1010 | goto out; | 1011 | goto out; |
1011 | addr_len = err; | 1012 | addr_len = err; |
1012 | 1013 | ||
1014 | if (sun_path[0]) { | ||
1015 | umode_t mode = S_IFSOCK | | ||
1016 | (SOCK_INODE(sock)->i_mode & ~current_umask()); | ||
1017 | err = unix_mknod(sun_path, mode, &path); | ||
1018 | if (err) { | ||
1019 | if (err == -EEXIST) | ||
1020 | err = -EADDRINUSE; | ||
1021 | goto out; | ||
1022 | } | ||
1023 | } | ||
1024 | |||
1013 | err = mutex_lock_interruptible(&u->bindlock); | 1025 | err = mutex_lock_interruptible(&u->bindlock); |
1014 | if (err) | 1026 | if (err) |
1015 | goto out; | 1027 | goto out_put; |
1016 | 1028 | ||
1017 | err = -EINVAL; | 1029 | err = -EINVAL; |
1018 | if (u->addr) | 1030 | if (u->addr) |
@@ -1029,16 +1041,6 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
1029 | atomic_set(&addr->refcnt, 1); | 1041 | atomic_set(&addr->refcnt, 1); |
1030 | 1042 | ||
1031 | if (sun_path[0]) { | 1043 | if (sun_path[0]) { |
1032 | struct path path; | ||
1033 | umode_t mode = S_IFSOCK | | ||
1034 | (SOCK_INODE(sock)->i_mode & ~current_umask()); | ||
1035 | err = unix_mknod(sun_path, mode, &path); | ||
1036 | if (err) { | ||
1037 | if (err == -EEXIST) | ||
1038 | err = -EADDRINUSE; | ||
1039 | unix_release_addr(addr); | ||
1040 | goto out_up; | ||
1041 | } | ||
1042 | addr->hash = UNIX_HASH_SIZE; | 1044 | addr->hash = UNIX_HASH_SIZE; |
1043 | hash = d_backing_inode(path.dentry)->i_ino & (UNIX_HASH_SIZE - 1); | 1045 | hash = d_backing_inode(path.dentry)->i_ino & (UNIX_HASH_SIZE - 1); |
1044 | spin_lock(&unix_table_lock); | 1046 | spin_lock(&unix_table_lock); |
@@ -1065,6 +1067,9 @@ out_unlock: | |||
1065 | spin_unlock(&unix_table_lock); | 1067 | spin_unlock(&unix_table_lock); |
1066 | out_up: | 1068 | out_up: |
1067 | mutex_unlock(&u->bindlock); | 1069 | mutex_unlock(&u->bindlock); |
1070 | out_put: | ||
1071 | if (err) | ||
1072 | path_put(&path); | ||
1068 | out: | 1073 | out: |
1069 | return err; | 1074 | return err; |
1070 | } | 1075 | } |