aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Kuznetsov <kuznet@ms2.inr.ac.ru>2006-02-27 00:28:32 -0800
committerChris Wright <chrisw@sous-sol.org>2006-03-01 14:36:37 -0800
commit9809ee9916825087a8729af4713ae9b555917ad5 (patch)
treefe3858a2ec0b00f53209d0c90ab5dffd2491da2d
parent5a62d3406ddd87a26d706d0b3019f4a1872713da (diff)
[PATCH] Fix a severe bug
netlink overrun was broken while improvement of netlink. Destination socket is used in the place where it was meant to be source socket, so that now overrun is never sent to user netlink sockets, when it should be, and it even can be set on kernel socket, which results in complete deadlock of rtnetlink. Suggested fix is to restore status quo passing source socket as additional argument to netlink_attachskb(). A little explanation: overrun is set on a socket, when it failed to receive some message and sender of this messages does not or even have no way to handle this error. This happens in two cases: 1. when kernel sends something. Kernel never retransmits and cannot wait for buffer space. 2. when user sends a broadcast and the message was not delivered to some recipients. Signed-off-by: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Chris Wright <chrisw@sous-sol.org>
-rw-r--r--include/linux/netlink.h3
-rw-r--r--ipc/mqueue.c3
-rw-r--r--net/netlink/af_netlink.c7
3 files changed, 8 insertions, 5 deletions
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 6a2ccf78a356..c256ebe2a7b4 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -160,7 +160,8 @@ extern int netlink_unregister_notifier(struct notifier_block *nb);
/* finegrained unicast helpers: */
struct sock *netlink_getsockbyfilp(struct file *filp);
-int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock, long timeo);
+int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock,
+ long timeo, struct sock *ssk);
void netlink_detachskb(struct sock *sk, struct sk_buff *skb);
int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol);
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 565b2fa88d49..1e306deb62ec 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -1017,7 +1017,8 @@ retry:
goto out;
}
- ret = netlink_attachskb(sock, nc, 0, MAX_SCHEDULE_TIMEOUT);
+ ret = netlink_attachskb(sock, nc, 0,
+ MAX_SCHEDULE_TIMEOUT, NULL);
if (ret == 1)
goto retry;
if (ret) {
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index fc5a735ba0ac..cb8130ba0304 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -701,7 +701,8 @@ struct sock *netlink_getsockbyfilp(struct file *filp)
* 0: continue
* 1: repeat lookup - reference dropped while waiting for socket memory.
*/
-int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock, long timeo)
+int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock,
+ long timeo, struct sock *ssk)
{
struct netlink_sock *nlk;
@@ -711,7 +712,7 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock, long t
test_bit(0, &nlk->state)) {
DECLARE_WAITQUEUE(wait, current);
if (!timeo) {
- if (!nlk->pid)
+ if (!ssk || nlk_sk(ssk)->pid == 0)
netlink_overrun(sk);
sock_put(sk);
kfree_skb(skb);
@@ -796,7 +797,7 @@ retry:
kfree_skb(skb);
return PTR_ERR(sk);
}
- err = netlink_attachskb(sk, skb, nonblock, timeo);
+ err = netlink_attachskb(sk, skb, nonblock, timeo, ssk);
if (err == 1)
goto retry;
if (err)