diff options
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 | ||