aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/inet_diag.c
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2007-12-02 23:51:25 -0500
committerHerbert Xu <herbert@gondor.apana.org.au>2007-12-02 23:51:25 -0500
commitd523a328fb0271e1a763e985a21f2488fd816e7e (patch)
treec877f39b7719531646d7066085f1fc2af39be5ed /net/ipv4/inet_diag.c
parent2b1e300a9dfc3196ccddf6f1d74b91b7af55e416 (diff)
[INET]: Fix inet_diag dead-lock regression
The inet_diag register fix broke inet_diag module loading because the loaded module had to take the same mutex that's already held by the loader in order to register the new handler. This patch fixes it by introducing a separate mutex to protect the handling of handlers. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'net/ipv4/inet_diag.c')
-rw-r--r--net/ipv4/inet_diag.c67
1 files changed, 46 insertions, 21 deletions
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 6b3fffb554b6..e468e7a7aac4 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -51,6 +51,29 @@ static struct sock *idiagnl;
51#define INET_DIAG_PUT(skb, attrtype, attrlen) \ 51#define INET_DIAG_PUT(skb, attrtype, attrlen) \
52 RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) 52 RTA_DATA(__RTA_PUT(skb, attrtype, attrlen))
53 53
54static DEFINE_MUTEX(inet_diag_table_mutex);
55
56static const struct inet_diag_handler *inet_diag_lock_handler(int type)
57{
58#ifdef CONFIG_KMOD
59 if (!inet_diag_table[type])
60 request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
61 NETLINK_INET_DIAG, type);
62#endif
63
64 mutex_lock(&inet_diag_table_mutex);
65 if (!inet_diag_table[type])
66 return ERR_PTR(-ENOENT);
67
68 return inet_diag_table[type];
69}
70
71static inline void inet_diag_unlock_handler(
72 const struct inet_diag_handler *handler)
73{
74 mutex_unlock(&inet_diag_table_mutex);
75}
76
54static int inet_csk_diag_fill(struct sock *sk, 77static int inet_csk_diag_fill(struct sock *sk,
55 struct sk_buff *skb, 78 struct sk_buff *skb,
56 int ext, u32 pid, u32 seq, u16 nlmsg_flags, 79 int ext, u32 pid, u32 seq, u16 nlmsg_flags,
@@ -235,9 +258,12 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
235 struct inet_hashinfo *hashinfo; 258 struct inet_hashinfo *hashinfo;
236 const struct inet_diag_handler *handler; 259 const struct inet_diag_handler *handler;
237 260
238 handler = inet_diag_table[nlh->nlmsg_type]; 261 handler = inet_diag_lock_handler(nlh->nlmsg_type);
239 BUG_ON(handler == NULL); 262 if (!handler)
263 return -ENOENT;
264
240 hashinfo = handler->idiag_hashinfo; 265 hashinfo = handler->idiag_hashinfo;
266 err = -EINVAL;
241 267
242 if (req->idiag_family == AF_INET) { 268 if (req->idiag_family == AF_INET) {
243 sk = inet_lookup(hashinfo, req->id.idiag_dst[0], 269 sk = inet_lookup(hashinfo, req->id.idiag_dst[0],
@@ -255,11 +281,12 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
255 } 281 }
256#endif 282#endif
257 else { 283 else {
258 return -EINVAL; 284 goto unlock;
259 } 285 }
260 286
287 err = -ENOENT;
261 if (sk == NULL) 288 if (sk == NULL)
262 return -ENOENT; 289 goto unlock;
263 290
264 err = -ESTALE; 291 err = -ESTALE;
265 if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE || 292 if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE ||
@@ -296,6 +323,8 @@ out:
296 else 323 else
297 sock_put(sk); 324 sock_put(sk);
298 } 325 }
326unlock:
327 inet_diag_unlock_handler(handler);
299 return err; 328 return err;
300} 329}
301 330
@@ -678,8 +707,10 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
678 const struct inet_diag_handler *handler; 707 const struct inet_diag_handler *handler;
679 struct inet_hashinfo *hashinfo; 708 struct inet_hashinfo *hashinfo;
680 709
681 handler = inet_diag_table[cb->nlh->nlmsg_type]; 710 handler = inet_diag_lock_handler(cb->nlh->nlmsg_type);
682 BUG_ON(handler == NULL); 711 if (!handler)
712 goto no_handler;
713
683 hashinfo = handler->idiag_hashinfo; 714 hashinfo = handler->idiag_hashinfo;
684 715
685 s_i = cb->args[1]; 716 s_i = cb->args[1];
@@ -743,7 +774,7 @@ skip_listen_ht:
743 } 774 }
744 775
745 if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV))) 776 if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
746 return skb->len; 777 goto unlock;
747 778
748 for (i = s_i; i < hashinfo->ehash_size; i++) { 779 for (i = s_i; i < hashinfo->ehash_size; i++) {
749 struct inet_ehash_bucket *head = &hashinfo->ehash[i]; 780 struct inet_ehash_bucket *head = &hashinfo->ehash[i];
@@ -805,6 +836,9 @@ next_dying:
805done: 836done:
806 cb->args[1] = i; 837 cb->args[1] = i;
807 cb->args[2] = num; 838 cb->args[2] = num;
839unlock:
840 inet_diag_unlock_handler(handler);
841no_handler:
808 return skb->len; 842 return skb->len;
809} 843}
810 844
@@ -816,15 +850,6 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
816 nlmsg_len(nlh) < hdrlen) 850 nlmsg_len(nlh) < hdrlen)
817 return -EINVAL; 851 return -EINVAL;
818 852
819#ifdef CONFIG_KMOD
820 if (inet_diag_table[nlh->nlmsg_type] == NULL)
821 request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
822 NETLINK_INET_DIAG, nlh->nlmsg_type);
823#endif
824
825 if (inet_diag_table[nlh->nlmsg_type] == NULL)
826 return -ENOENT;
827
828 if (nlh->nlmsg_flags & NLM_F_DUMP) { 853 if (nlh->nlmsg_flags & NLM_F_DUMP) {
829 if (nlmsg_attrlen(nlh, hdrlen)) { 854 if (nlmsg_attrlen(nlh, hdrlen)) {
830 struct nlattr *attr; 855 struct nlattr *attr;
@@ -861,13 +886,13 @@ int inet_diag_register(const struct inet_diag_handler *h)
861 if (type >= INET_DIAG_GETSOCK_MAX) 886 if (type >= INET_DIAG_GETSOCK_MAX)
862 goto out; 887 goto out;
863 888
864 mutex_lock(&inet_diag_mutex); 889 mutex_lock(&inet_diag_table_mutex);
865 err = -EEXIST; 890 err = -EEXIST;
866 if (inet_diag_table[type] == NULL) { 891 if (inet_diag_table[type] == NULL) {
867 inet_diag_table[type] = h; 892 inet_diag_table[type] = h;
868 err = 0; 893 err = 0;
869 } 894 }
870 mutex_unlock(&inet_diag_mutex); 895 mutex_unlock(&inet_diag_table_mutex);
871out: 896out:
872 return err; 897 return err;
873} 898}
@@ -880,9 +905,9 @@ void inet_diag_unregister(const struct inet_diag_handler *h)
880 if (type >= INET_DIAG_GETSOCK_MAX) 905 if (type >= INET_DIAG_GETSOCK_MAX)
881 return; 906 return;
882 907
883 mutex_lock(&inet_diag_mutex); 908 mutex_lock(&inet_diag_table_mutex);
884 inet_diag_table[type] = NULL; 909 inet_diag_table[type] = NULL;
885 mutex_unlock(&inet_diag_mutex); 910 mutex_unlock(&inet_diag_table_mutex);
886} 911}
887EXPORT_SYMBOL_GPL(inet_diag_unregister); 912EXPORT_SYMBOL_GPL(inet_diag_unregister);
888 913
@@ -897,7 +922,7 @@ static int __init inet_diag_init(void)
897 goto out; 922 goto out;
898 923
899 idiagnl = netlink_kernel_create(&init_net, NETLINK_INET_DIAG, 0, 924 idiagnl = netlink_kernel_create(&init_net, NETLINK_INET_DIAG, 0,
900 inet_diag_rcv, &inet_diag_mutex, THIS_MODULE); 925 inet_diag_rcv, NULL, THIS_MODULE);
901 if (idiagnl == NULL) 926 if (idiagnl == NULL)
902 goto out_free_table; 927 goto out_free_table;
903 err = 0; 928 err = 0;