diff options
Diffstat (limited to 'net/unix/af_unix.c')
| -rw-r--r-- | net/unix/af_unix.c | 43 |
1 files changed, 38 insertions, 5 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 453ede86a65b..87c794d8fa2d 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c | |||
| @@ -858,6 +858,31 @@ out_mknod_parent: | |||
| 858 | goto out_up; | 858 | goto out_up; |
| 859 | } | 859 | } |
| 860 | 860 | ||
| 861 | static void unix_state_double_lock(struct sock *sk1, struct sock *sk2) | ||
| 862 | { | ||
| 863 | if (unlikely(sk1 == sk2) || !sk2) { | ||
| 864 | unix_state_lock(sk1); | ||
| 865 | return; | ||
| 866 | } | ||
| 867 | if (sk1 < sk2) { | ||
| 868 | unix_state_lock(sk1); | ||
| 869 | unix_state_lock_nested(sk2); | ||
| 870 | } else { | ||
| 871 | unix_state_lock(sk2); | ||
| 872 | unix_state_lock_nested(sk1); | ||
| 873 | } | ||
| 874 | } | ||
| 875 | |||
| 876 | static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2) | ||
| 877 | { | ||
| 878 | if (unlikely(sk1 == sk2) || !sk2) { | ||
| 879 | unix_state_unlock(sk1); | ||
| 880 | return; | ||
| 881 | } | ||
| 882 | unix_state_unlock(sk1); | ||
| 883 | unix_state_unlock(sk2); | ||
| 884 | } | ||
| 885 | |||
| 861 | static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, | 886 | static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, |
| 862 | int alen, int flags) | 887 | int alen, int flags) |
| 863 | { | 888 | { |
| @@ -877,11 +902,19 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, | |||
| 877 | !unix_sk(sk)->addr && (err = unix_autobind(sock)) != 0) | 902 | !unix_sk(sk)->addr && (err = unix_autobind(sock)) != 0) |
| 878 | goto out; | 903 | goto out; |
| 879 | 904 | ||
| 905 | restart: | ||
| 880 | other=unix_find_other(sunaddr, alen, sock->type, hash, &err); | 906 | other=unix_find_other(sunaddr, alen, sock->type, hash, &err); |
| 881 | if (!other) | 907 | if (!other) |
| 882 | goto out; | 908 | goto out; |
| 883 | 909 | ||
| 884 | unix_state_lock(sk); | 910 | unix_state_double_lock(sk, other); |
| 911 | |||
| 912 | /* Apparently VFS overslept socket death. Retry. */ | ||
| 913 | if (sock_flag(other, SOCK_DEAD)) { | ||
| 914 | unix_state_double_unlock(sk, other); | ||
| 915 | sock_put(other); | ||
| 916 | goto restart; | ||
| 917 | } | ||
| 885 | 918 | ||
| 886 | err = -EPERM; | 919 | err = -EPERM; |
| 887 | if (!unix_may_send(sk, other)) | 920 | if (!unix_may_send(sk, other)) |
| @@ -896,7 +929,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, | |||
| 896 | * 1003.1g breaking connected state with AF_UNSPEC | 929 | * 1003.1g breaking connected state with AF_UNSPEC |
| 897 | */ | 930 | */ |
| 898 | other = NULL; | 931 | other = NULL; |
| 899 | unix_state_lock(sk); | 932 | unix_state_double_lock(sk, other); |
| 900 | } | 933 | } |
| 901 | 934 | ||
| 902 | /* | 935 | /* |
| @@ -905,19 +938,19 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, | |||
| 905 | if (unix_peer(sk)) { | 938 | if (unix_peer(sk)) { |
| 906 | struct sock *old_peer = unix_peer(sk); | 939 | struct sock *old_peer = unix_peer(sk); |
| 907 | unix_peer(sk)=other; | 940 | unix_peer(sk)=other; |
| 908 | unix_state_unlock(sk); | 941 | unix_state_double_unlock(sk, other); |
| 909 | 942 | ||
| 910 | if (other != old_peer) | 943 | if (other != old_peer) |
| 911 | unix_dgram_disconnected(sk, old_peer); | 944 | unix_dgram_disconnected(sk, old_peer); |
| 912 | sock_put(old_peer); | 945 | sock_put(old_peer); |
| 913 | } else { | 946 | } else { |
| 914 | unix_peer(sk)=other; | 947 | unix_peer(sk)=other; |
| 915 | unix_state_unlock(sk); | 948 | unix_state_double_unlock(sk, other); |
| 916 | } | 949 | } |
| 917 | return 0; | 950 | return 0; |
| 918 | 951 | ||
| 919 | out_unlock: | 952 | out_unlock: |
| 920 | unix_state_unlock(sk); | 953 | unix_state_double_unlock(sk, other); |
| 921 | sock_put(other); | 954 | sock_put(other); |
| 922 | out: | 955 | out: |
| 923 | return err; | 956 | return err; |
