diff options
Diffstat (limited to 'net')
-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; |