Commit d523a328 authored by Herbert Xu's avatar Herbert Xu

[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: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 2b1e300a
...@@ -51,6 +51,29 @@ static struct sock *idiagnl; ...@@ -51,6 +51,29 @@ static struct sock *idiagnl;
#define INET_DIAG_PUT(skb, attrtype, attrlen) \ #define INET_DIAG_PUT(skb, attrtype, attrlen) \
RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) RTA_DATA(__RTA_PUT(skb, attrtype, attrlen))
static DEFINE_MUTEX(inet_diag_table_mutex);
static const struct inet_diag_handler *inet_diag_lock_handler(int type)
{
#ifdef CONFIG_KMOD
if (!inet_diag_table[type])
request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
NETLINK_INET_DIAG, type);
#endif
mutex_lock(&inet_diag_table_mutex);
if (!inet_diag_table[type])
return ERR_PTR(-ENOENT);
return inet_diag_table[type];
}
static inline void inet_diag_unlock_handler(
const struct inet_diag_handler *handler)
{
mutex_unlock(&inet_diag_table_mutex);
}
static int inet_csk_diag_fill(struct sock *sk, static int inet_csk_diag_fill(struct sock *sk,
struct sk_buff *skb, struct sk_buff *skb,
int ext, u32 pid, u32 seq, u16 nlmsg_flags, 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,9 +258,12 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
struct inet_hashinfo *hashinfo; struct inet_hashinfo *hashinfo;
const struct inet_diag_handler *handler; const struct inet_diag_handler *handler;
handler = inet_diag_table[nlh->nlmsg_type]; handler = inet_diag_lock_handler(nlh->nlmsg_type);
BUG_ON(handler == NULL); if (!handler)
return -ENOENT;
hashinfo = handler->idiag_hashinfo; hashinfo = handler->idiag_hashinfo;
err = -EINVAL;
if (req->idiag_family == AF_INET) { if (req->idiag_family == AF_INET) {
sk = inet_lookup(hashinfo, req->id.idiag_dst[0], 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,11 +281,12 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
} }
#endif #endif
else { else {
return -EINVAL; goto unlock;
} }
err = -ENOENT;
if (sk == NULL) if (sk == NULL)
return -ENOENT; goto unlock;
err = -ESTALE; err = -ESTALE;
if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE || if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE ||
...@@ -296,6 +323,8 @@ static int inet_diag_get_exact(struct sk_buff *in_skb, ...@@ -296,6 +323,8 @@ static int inet_diag_get_exact(struct sk_buff *in_skb,
else else
sock_put(sk); sock_put(sk);
} }
unlock:
inet_diag_unlock_handler(handler);
return err; return err;
} }
...@@ -678,8 +707,10 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -678,8 +707,10 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
const struct inet_diag_handler *handler; const struct inet_diag_handler *handler;
struct inet_hashinfo *hashinfo; struct inet_hashinfo *hashinfo;
handler = inet_diag_table[cb->nlh->nlmsg_type]; handler = inet_diag_lock_handler(cb->nlh->nlmsg_type);
BUG_ON(handler == NULL); if (!handler)
goto no_handler;
hashinfo = handler->idiag_hashinfo; hashinfo = handler->idiag_hashinfo;
s_i = cb->args[1]; s_i = cb->args[1];
...@@ -743,7 +774,7 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -743,7 +774,7 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
} }
if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV))) if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV)))
return skb->len; goto unlock;
for (i = s_i; i < hashinfo->ehash_size; i++) { for (i = s_i; i < hashinfo->ehash_size; i++) {
struct inet_ehash_bucket *head = &hashinfo->ehash[i]; struct inet_ehash_bucket *head = &hashinfo->ehash[i];
...@@ -805,6 +836,9 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -805,6 +836,9 @@ static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
done: done:
cb->args[1] = i; cb->args[1] = i;
cb->args[2] = num; cb->args[2] = num;
unlock:
inet_diag_unlock_handler(handler);
no_handler:
return skb->len; return skb->len;
} }
...@@ -816,15 +850,6 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -816,15 +850,6 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
nlmsg_len(nlh) < hdrlen) nlmsg_len(nlh) < hdrlen)
return -EINVAL; return -EINVAL;
#ifdef CONFIG_KMOD
if (inet_diag_table[nlh->nlmsg_type] == NULL)
request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
NETLINK_INET_DIAG, nlh->nlmsg_type);
#endif
if (inet_diag_table[nlh->nlmsg_type] == NULL)
return -ENOENT;
if (nlh->nlmsg_flags & NLM_F_DUMP) { if (nlh->nlmsg_flags & NLM_F_DUMP) {
if (nlmsg_attrlen(nlh, hdrlen)) { if (nlmsg_attrlen(nlh, hdrlen)) {
struct nlattr *attr; struct nlattr *attr;
...@@ -861,13 +886,13 @@ int inet_diag_register(const struct inet_diag_handler *h) ...@@ -861,13 +886,13 @@ int inet_diag_register(const struct inet_diag_handler *h)
if (type >= INET_DIAG_GETSOCK_MAX) if (type >= INET_DIAG_GETSOCK_MAX)
goto out; goto out;
mutex_lock(&inet_diag_mutex); mutex_lock(&inet_diag_table_mutex);
err = -EEXIST; err = -EEXIST;
if (inet_diag_table[type] == NULL) { if (inet_diag_table[type] == NULL) {
inet_diag_table[type] = h; inet_diag_table[type] = h;
err = 0; err = 0;
} }
mutex_unlock(&inet_diag_mutex); mutex_unlock(&inet_diag_table_mutex);
out: out:
return err; return err;
} }
...@@ -880,9 +905,9 @@ void inet_diag_unregister(const struct inet_diag_handler *h) ...@@ -880,9 +905,9 @@ void inet_diag_unregister(const struct inet_diag_handler *h)
if (type >= INET_DIAG_GETSOCK_MAX) if (type >= INET_DIAG_GETSOCK_MAX)
return; return;
mutex_lock(&inet_diag_mutex); mutex_lock(&inet_diag_table_mutex);
inet_diag_table[type] = NULL; inet_diag_table[type] = NULL;
mutex_unlock(&inet_diag_mutex); mutex_unlock(&inet_diag_table_mutex);
} }
EXPORT_SYMBOL_GPL(inet_diag_unregister); EXPORT_SYMBOL_GPL(inet_diag_unregister);
...@@ -897,7 +922,7 @@ static int __init inet_diag_init(void) ...@@ -897,7 +922,7 @@ static int __init inet_diag_init(void)
goto out; goto out;
idiagnl = netlink_kernel_create(&init_net, NETLINK_INET_DIAG, 0, idiagnl = netlink_kernel_create(&init_net, NETLINK_INET_DIAG, 0,
inet_diag_rcv, &inet_diag_mutex, THIS_MODULE); inet_diag_rcv, NULL, THIS_MODULE);
if (idiagnl == NULL) if (idiagnl == NULL)
goto out_free_table; goto out_free_table;
err = 0; err = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment