Commit bf8b79e4 authored by Thomas Graf's avatar Thomas Graf Committed by David S. Miller

[NETLINK]: Convert core netlink handling to new netlink api

Fixes a theoretical memory and locking leak when the size of
the netlink header would exceed the skb tailroom.
Signed-off-by: default avatarThomas Graf <tgraf@suug.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fe4944e5
...@@ -1147,7 +1147,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, ...@@ -1147,7 +1147,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
if (len > sk->sk_sndbuf - 32) if (len > sk->sk_sndbuf - 32)
goto out; goto out;
err = -ENOBUFS; err = -ENOBUFS;
skb = alloc_skb(len, GFP_KERNEL); skb = nlmsg_new(len, GFP_KERNEL);
if (skb==NULL) if (skb==NULL)
goto out; goto out;
...@@ -1341,19 +1341,18 @@ static int netlink_dump(struct sock *sk) ...@@ -1341,19 +1341,18 @@ static int netlink_dump(struct sock *sk)
struct netlink_callback *cb; struct netlink_callback *cb;
struct sk_buff *skb; struct sk_buff *skb;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
int len; int len, err = -ENOBUFS;
skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL); skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL);
if (!skb) if (!skb)
return -ENOBUFS; goto errout;
spin_lock(&nlk->cb_lock); spin_lock(&nlk->cb_lock);
cb = nlk->cb; cb = nlk->cb;
if (cb == NULL) { if (cb == NULL) {
spin_unlock(&nlk->cb_lock); err = -EINVAL;
kfree_skb(skb); goto errout_skb;
return -EINVAL;
} }
len = cb->dump(skb, cb); len = cb->dump(skb, cb);
...@@ -1365,8 +1364,12 @@ static int netlink_dump(struct sock *sk) ...@@ -1365,8 +1364,12 @@ static int netlink_dump(struct sock *sk)
return 0; return 0;
} }
nlh = NLMSG_NEW_ANSWER(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI); nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI);
memcpy(NLMSG_DATA(nlh), &len, sizeof(len)); if (!nlh)
goto errout_skb;
memcpy(nlmsg_data(nlh), &len, sizeof(len));
skb_queue_tail(&sk->sk_receive_queue, skb); skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_data_ready(sk, skb->len); sk->sk_data_ready(sk, skb->len);
...@@ -1378,8 +1381,11 @@ static int netlink_dump(struct sock *sk) ...@@ -1378,8 +1381,11 @@ static int netlink_dump(struct sock *sk)
netlink_destroy_callback(cb); netlink_destroy_callback(cb);
return 0; return 0;
nlmsg_failure: errout_skb:
return -ENOBUFS; spin_unlock(&nlk->cb_lock);
kfree_skb(skb);
errout:
return err;
} }
int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
...@@ -1431,11 +1437,11 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) ...@@ -1431,11 +1437,11 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
int size; int size;
if (err == 0) if (err == 0)
size = NLMSG_SPACE(sizeof(struct nlmsgerr)); size = nlmsg_total_size(sizeof(*errmsg));
else else
size = NLMSG_SPACE(4 + NLMSG_ALIGN(nlh->nlmsg_len)); size = nlmsg_total_size(sizeof(*errmsg) + nlmsg_len(nlh));
skb = alloc_skb(size, GFP_KERNEL); skb = nlmsg_new(size, GFP_KERNEL);
if (!skb) { if (!skb) {
struct sock *sk; struct sock *sk;
...@@ -1451,16 +1457,15 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) ...@@ -1451,16 +1457,15 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
rep = __nlmsg_put(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, rep = __nlmsg_put(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq,
NLMSG_ERROR, sizeof(struct nlmsgerr), 0); NLMSG_ERROR, sizeof(struct nlmsgerr), 0);
errmsg = NLMSG_DATA(rep); errmsg = nlmsg_data(rep);
errmsg->error = err; errmsg->error = err;
memcpy(&errmsg->msg, nlh, err ? nlh->nlmsg_len : sizeof(struct nlmsghdr)); memcpy(&errmsg->msg, nlh, err ? nlh->nlmsg_len : sizeof(*nlh));
netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
} }
static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
struct nlmsghdr *, int *)) struct nlmsghdr *, int *))
{ {
unsigned int total_len;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
int err; int err;
...@@ -1470,8 +1475,6 @@ static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, ...@@ -1470,8 +1475,6 @@ static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len) if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len)
return 0; return 0;
total_len = min(NLMSG_ALIGN(nlh->nlmsg_len), skb->len);
if (cb(skb, nlh, &err) < 0) { if (cb(skb, nlh, &err) < 0) {
/* Not an error, but we have to interrupt processing /* Not an error, but we have to interrupt processing
* here. Note: that in this case we do not pull * here. Note: that in this case we do not pull
...@@ -1483,7 +1486,7 @@ static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, ...@@ -1483,7 +1486,7 @@ static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
} else if (nlh->nlmsg_flags & NLM_F_ACK) } else if (nlh->nlmsg_flags & NLM_F_ACK)
netlink_ack(skb, nlh, 0); netlink_ack(skb, nlh, 0);
skb_pull(skb, total_len); netlink_queue_skip(nlh, skb);
} }
return 0; return 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