[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: Thomas Graf <tgraf@suug.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
fe4944e59c
commit
bf8b79e444
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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 *,
|
|||||||
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 *,
|
|||||||
} 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;
|
||||||
|
Loading…
Reference in New Issue
Block a user