diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2019-02-15 15:09:35 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-02-20 23:06:28 -0500 |
commit | ae3b564179bfd06f32d051b9e5d72ce4b2a07c37 (patch) | |
tree | 97d40baf6c1a1b952800fd5e2ab5b348696d79a3 /net/unix/af_unix.c | |
parent | a8fef9ba58c9966ddb1fec916d8d8137c9d8bc89 (diff) |
missing barriers in some of unix_sock ->addr and ->path accesses
Several u->addr and u->path users are not holding any locks in
common with unix_bind(). unix_state_lock() is useless for those
purposes.
u->addr is assign-once and *(u->addr) is fully set up by the time
we set u->addr (all under unix_table_lock). u->path is also
set in the same critical area, also before setting u->addr, and
any unix_sock with ->path filled will have non-NULL ->addr.
So setting ->addr with smp_store_release() is all we need for those
"lockless" users - just have them fetch ->addr with smp_load_acquire()
and don't even bother looking at ->path if they see NULL ->addr.
Users of ->addr and ->path fall into several classes now:
1) ones that do smp_load_acquire(u->addr) and access *(u->addr)
and u->path only if smp_load_acquire() has returned non-NULL.
2) places holding unix_table_lock. These are guaranteed that
*(u->addr) is seen fully initialized. If unix_sock is in one of the
"bound" chains, so's ->path.
3) unix_sock_destructor() using ->addr is safe. All places
that set u->addr are guaranteed to have seen all stores *(u->addr)
while holding a reference to u and unix_sock_destructor() is called
when (atomic) refcount hits zero.
4) unix_release_sock() using ->path is safe. unix_bind()
is serialized wrt unix_release() (normally - by struct file
refcount), and for the instances that had ->path set by unix_bind()
unix_release_sock() comes from unix_release(), so they are fine.
Instances that had it set in unix_stream_connect() either end up
attached to a socket (in unix_accept()), in which case the call
chain to unix_release_sock() and serialization are the same as in
the previous case, or they never get accept'ed and unix_release_sock()
is called when the listener is shut down and its queue gets purged.
In that case the listener's queue lock provides the barriers needed -
unix_stream_connect() shoves our unix_sock into listener's queue
under that lock right after having set ->path and eventual
unix_release_sock() caller picks them from that queue under the
same lock right before calling unix_release_sock().
5) unix_find_other() use of ->path is pointless, but safe -
it happens with successful lookup by (abstract) name, so ->path.dentry
is guaranteed to be NULL there.
earlier-variant-reviewed-by: "Paul E. McKenney" <paulmck@linux.ibm.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
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 | 57 |
1 files changed, 33 insertions, 24 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 74d1eed7cbd4..a95d479caeea 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
@@ -890,7 +890,7 @@ retry: | |||
890 | addr->hash ^= sk->sk_type; | 890 | addr->hash ^= sk->sk_type; |
891 | 891 | ||
892 | __unix_remove_socket(sk); | 892 | __unix_remove_socket(sk); |
893 | u->addr = addr; | 893 | smp_store_release(&u->addr, addr); |
894 | __unix_insert_socket(&unix_socket_table[addr->hash], sk); | 894 | __unix_insert_socket(&unix_socket_table[addr->hash], sk); |
895 | spin_unlock(&unix_table_lock); | 895 | spin_unlock(&unix_table_lock); |
896 | err = 0; | 896 | err = 0; |
@@ -1060,7 +1060,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) | |||
1060 | 1060 | ||
1061 | err = 0; | 1061 | err = 0; |
1062 | __unix_remove_socket(sk); | 1062 | __unix_remove_socket(sk); |
1063 | u->addr = addr; | 1063 | smp_store_release(&u->addr, addr); |
1064 | __unix_insert_socket(list, sk); | 1064 | __unix_insert_socket(list, sk); |
1065 | 1065 | ||
1066 | out_unlock: | 1066 | out_unlock: |
@@ -1331,15 +1331,29 @@ restart: | |||
1331 | RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq); | 1331 | RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq); |
1332 | otheru = unix_sk(other); | 1332 | otheru = unix_sk(other); |
1333 | 1333 | ||
1334 | /* copy address information from listening to new sock*/ | 1334 | /* copy address information from listening to new sock |
1335 | if (otheru->addr) { | 1335 | * |
1336 | refcount_inc(&otheru->addr->refcnt); | 1336 | * The contents of *(otheru->addr) and otheru->path |
1337 | newu->addr = otheru->addr; | 1337 | * are seen fully set up here, since we have found |
1338 | } | 1338 | * otheru in hash under unix_table_lock. Insertion |
1339 | * into the hash chain we'd found it in had been done | ||
1340 | * in an earlier critical area protected by unix_table_lock, | ||
1341 | * the same one where we'd set *(otheru->addr) contents, | ||
1342 | * as well as otheru->path and otheru->addr itself. | ||
1343 | * | ||
1344 | * Using smp_store_release() here to set newu->addr | ||
1345 | * is enough to make those stores, as well as stores | ||
1346 | * to newu->path visible to anyone who gets newu->addr | ||
1347 | * by smp_load_acquire(). IOW, the same warranties | ||
1348 | * as for unix_sock instances bound in unix_bind() or | ||
1349 | * in unix_autobind(). | ||
1350 | */ | ||
1339 | if (otheru->path.dentry) { | 1351 | if (otheru->path.dentry) { |
1340 | path_get(&otheru->path); | 1352 | path_get(&otheru->path); |
1341 | newu->path = otheru->path; | 1353 | newu->path = otheru->path; |
1342 | } | 1354 | } |
1355 | refcount_inc(&otheru->addr->refcnt); | ||
1356 | smp_store_release(&newu->addr, otheru->addr); | ||
1343 | 1357 | ||
1344 | /* Set credentials */ | 1358 | /* Set credentials */ |
1345 | copy_peercred(sk, other); | 1359 | copy_peercred(sk, other); |
@@ -1453,7 +1467,7 @@ out: | |||
1453 | static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int peer) | 1467 | static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int peer) |
1454 | { | 1468 | { |
1455 | struct sock *sk = sock->sk; | 1469 | struct sock *sk = sock->sk; |
1456 | struct unix_sock *u; | 1470 | struct unix_address *addr; |
1457 | DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr, uaddr); | 1471 | DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr, uaddr); |
1458 | int err = 0; | 1472 | int err = 0; |
1459 | 1473 | ||
@@ -1468,19 +1482,15 @@ static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int peer) | |||
1468 | sock_hold(sk); | 1482 | sock_hold(sk); |
1469 | } | 1483 | } |
1470 | 1484 | ||
1471 | u = unix_sk(sk); | 1485 | addr = smp_load_acquire(&unix_sk(sk)->addr); |
1472 | unix_state_lock(sk); | 1486 | if (!addr) { |
1473 | if (!u->addr) { | ||
1474 | sunaddr->sun_family = AF_UNIX; | 1487 | sunaddr->sun_family = AF_UNIX; |
1475 | sunaddr->sun_path[0] = 0; | 1488 | sunaddr->sun_path[0] = 0; |
1476 | err = sizeof(short); | 1489 | err = sizeof(short); |
1477 | } else { | 1490 | } else { |
1478 | struct unix_address *addr = u->addr; | ||
1479 | |||
1480 | err = addr->len; | 1491 | err = addr->len; |
1481 | memcpy(sunaddr, addr->name, addr->len); | 1492 | memcpy(sunaddr, addr->name, addr->len); |
1482 | } | 1493 | } |
1483 | unix_state_unlock(sk); | ||
1484 | sock_put(sk); | 1494 | sock_put(sk); |
1485 | out: | 1495 | out: |
1486 | return err; | 1496 | return err; |
@@ -2073,11 +2083,11 @@ static int unix_seqpacket_recvmsg(struct socket *sock, struct msghdr *msg, | |||
2073 | 2083 | ||
2074 | static void unix_copy_addr(struct msghdr *msg, struct sock *sk) | 2084 | static void unix_copy_addr(struct msghdr *msg, struct sock *sk) |
2075 | { | 2085 | { |
2076 | struct unix_sock *u = unix_sk(sk); | 2086 | struct unix_address *addr = smp_load_acquire(&unix_sk(sk)->addr); |
2077 | 2087 | ||
2078 | if (u->addr) { | 2088 | if (addr) { |
2079 | msg->msg_namelen = u->addr->len; | 2089 | msg->msg_namelen = addr->len; |
2080 | memcpy(msg->msg_name, u->addr->name, u->addr->len); | 2090 | memcpy(msg->msg_name, addr->name, addr->len); |
2081 | } | 2091 | } |
2082 | } | 2092 | } |
2083 | 2093 | ||
@@ -2581,15 +2591,14 @@ static int unix_open_file(struct sock *sk) | |||
2581 | if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) | 2591 | if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) |
2582 | return -EPERM; | 2592 | return -EPERM; |
2583 | 2593 | ||
2584 | unix_state_lock(sk); | 2594 | if (!smp_load_acquire(&unix_sk(sk)->addr)) |
2595 | return -ENOENT; | ||
2596 | |||
2585 | path = unix_sk(sk)->path; | 2597 | path = unix_sk(sk)->path; |
2586 | if (!path.dentry) { | 2598 | if (!path.dentry) |
2587 | unix_state_unlock(sk); | ||
2588 | return -ENOENT; | 2599 | return -ENOENT; |
2589 | } | ||
2590 | 2600 | ||
2591 | path_get(&path); | 2601 | path_get(&path); |
2592 | unix_state_unlock(sk); | ||
2593 | 2602 | ||
2594 | fd = get_unused_fd_flags(O_CLOEXEC); | 2603 | fd = get_unused_fd_flags(O_CLOEXEC); |
2595 | if (fd < 0) | 2604 | if (fd < 0) |
@@ -2830,7 +2839,7 @@ static int unix_seq_show(struct seq_file *seq, void *v) | |||
2830 | (s->sk_state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING), | 2839 | (s->sk_state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING), |
2831 | sock_i_ino(s)); | 2840 | sock_i_ino(s)); |
2832 | 2841 | ||
2833 | if (u->addr) { | 2842 | if (u->addr) { // under unix_table_lock here |
2834 | int i, len; | 2843 | int i, len; |
2835 | seq_putc(seq, ' '); | 2844 | seq_putc(seq, ' '); |
2836 | 2845 | ||