Skip to content

Commit ae3b564

Browse files
Al Virodavem330
authored andcommitted
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" <[email protected]> Signed-off-by: Al Viro <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent a8fef9b commit ae3b564

File tree

3 files changed

+41
-29
lines changed

3 files changed

+41
-29
lines changed

net/unix/af_unix.c

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ static int unix_autobind(struct socket *sock)
890890
addr->hash ^= sk->sk_type;
891891

892892
__unix_remove_socket(sk);
893-
u->addr = addr;
893+
smp_store_release(&u->addr, addr);
894894
__unix_insert_socket(&unix_socket_table[addr->hash], sk);
895895
spin_unlock(&unix_table_lock);
896896
err = 0;
@@ -1060,7 +1060,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
10601060

10611061
err = 0;
10621062
__unix_remove_socket(sk);
1063-
u->addr = addr;
1063+
smp_store_release(&u->addr, addr);
10641064
__unix_insert_socket(list, sk);
10651065

10661066
out_unlock:
@@ -1331,15 +1331,29 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
13311331
RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);
13321332
otheru = unix_sk(other);
13331333

1334-
/* copy address information from listening to new sock*/
1335-
if (otheru->addr) {
1336-
refcount_inc(&otheru->addr->refcnt);
1337-
newu->addr = otheru->addr;
1338-
}
1334+
/* copy address information from listening to new sock
1335+
*
1336+
* The contents of *(otheru->addr) and otheru->path
1337+
* are seen fully set up here, since we have found
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+
*/
13391351
if (otheru->path.dentry) {
13401352
path_get(&otheru->path);
13411353
newu->path = otheru->path;
13421354
}
1355+
refcount_inc(&otheru->addr->refcnt);
1356+
smp_store_release(&newu->addr, otheru->addr);
13431357

13441358
/* Set credentials */
13451359
copy_peercred(sk, other);
@@ -1453,7 +1467,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags,
14531467
static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int peer)
14541468
{
14551469
struct sock *sk = sock->sk;
1456-
struct unix_sock *u;
1470+
struct unix_address *addr;
14571471
DECLARE_SOCKADDR(struct sockaddr_un *, sunaddr, uaddr);
14581472
int err = 0;
14591473

@@ -1468,19 +1482,15 @@ static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int peer)
14681482
sock_hold(sk);
14691483
}
14701484

1471-
u = unix_sk(sk);
1472-
unix_state_lock(sk);
1473-
if (!u->addr) {
1485+
addr = smp_load_acquire(&unix_sk(sk)->addr);
1486+
if (!addr) {
14741487
sunaddr->sun_family = AF_UNIX;
14751488
sunaddr->sun_path[0] = 0;
14761489
err = sizeof(short);
14771490
} else {
1478-
struct unix_address *addr = u->addr;
1479-
14801491
err = addr->len;
14811492
memcpy(sunaddr, addr->name, addr->len);
14821493
}
1483-
unix_state_unlock(sk);
14841494
sock_put(sk);
14851495
out:
14861496
return err;
@@ -2073,11 +2083,11 @@ static int unix_seqpacket_recvmsg(struct socket *sock, struct msghdr *msg,
20732083

20742084
static void unix_copy_addr(struct msghdr *msg, struct sock *sk)
20752085
{
2076-
struct unix_sock *u = unix_sk(sk);
2086+
struct unix_address *addr = smp_load_acquire(&unix_sk(sk)->addr);
20772087

2078-
if (u->addr) {
2079-
msg->msg_namelen = u->addr->len;
2080-
memcpy(msg->msg_name, u->addr->name, u->addr->len);
2088+
if (addr) {
2089+
msg->msg_namelen = addr->len;
2090+
memcpy(msg->msg_name, addr->name, addr->len);
20812091
}
20822092
}
20832093

@@ -2581,15 +2591,14 @@ static int unix_open_file(struct sock *sk)
25812591
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
25822592
return -EPERM;
25832593

2584-
unix_state_lock(sk);
2594+
if (!smp_load_acquire(&unix_sk(sk)->addr))
2595+
return -ENOENT;
2596+
25852597
path = unix_sk(sk)->path;
2586-
if (!path.dentry) {
2587-
unix_state_unlock(sk);
2598+
if (!path.dentry)
25882599
return -ENOENT;
2589-
}
25902600

25912601
path_get(&path);
2592-
unix_state_unlock(sk);
25932602

25942603
fd = get_unused_fd_flags(O_CLOEXEC);
25952604
if (fd < 0)
@@ -2830,7 +2839,7 @@ static int unix_seq_show(struct seq_file *seq, void *v)
28302839
(s->sk_state == TCP_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING),
28312840
sock_i_ino(s));
28322841

2833-
if (u->addr) {
2842+
if (u->addr) { // under unix_table_lock here
28342843
int i, len;
28352844
seq_putc(seq, ' ');
28362845

net/unix/diag.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
static int sk_diag_dump_name(struct sock *sk, struct sk_buff *nlskb)
1212
{
13-
struct unix_address *addr = unix_sk(sk)->addr;
13+
/* might or might not have unix_table_lock */
14+
struct unix_address *addr = smp_load_acquire(&unix_sk(sk)->addr);
1415

1516
if (!addr)
1617
return 0;

security/lsm_audit.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
321321
if (a->u.net->sk) {
322322
struct sock *sk = a->u.net->sk;
323323
struct unix_sock *u;
324+
struct unix_address *addr;
324325
int len = 0;
325326
char *p = NULL;
326327

@@ -351,14 +352,15 @@ static void dump_common_audit_data(struct audit_buffer *ab,
351352
#endif
352353
case AF_UNIX:
353354
u = unix_sk(sk);
355+
addr = smp_load_acquire(&u->addr);
356+
if (!addr)
357+
break;
354358
if (u->path.dentry) {
355359
audit_log_d_path(ab, " path=", &u->path);
356360
break;
357361
}
358-
if (!u->addr)
359-
break;
360-
len = u->addr->len-sizeof(short);
361-
p = &u->addr->name->sun_path[0];
362+
len = addr->len-sizeof(short);
363+
p = &addr->name->sun_path[0];
362364
audit_log_format(ab, " path=");
363365
if (*p)
364366
audit_log_untrustedstring(ab, p);

0 commit comments

Comments
 (0)