diff options
author | Thomas Graf <tgraf@suug.ch> | 2006-08-05 02:03:29 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-09-22 17:53:44 -0400 |
commit | bf8b79e444a748963c71d2a58709e1ce5597e1b5 (patch) | |
tree | 4f598fc078926f3729cc615d0a5a00c2d1e57967 /net/netlink | |
parent | fe4944e59c357f945f81bc67edb7ed1392e875ad (diff) |
[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>
Diffstat (limited to 'net/netlink')
-rw-r--r-- | net/netlink/af_netlink.c | 41 |
1 files changed, 22 insertions, 19 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 8b85036ba8e3..0f36ddc0b72d 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -1147,7 +1147,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, | |||
1147 | if (len > sk->sk_sndbuf - 32) | 1147 | if (len > sk->sk_sndbuf - 32) |
1148 | goto out; | 1148 | goto out; |
1149 | err = -ENOBUFS; | 1149 | err = -ENOBUFS; |
1150 | skb = alloc_skb(len, GFP_KERNEL); | 1150 | skb = nlmsg_new(len, GFP_KERNEL); |
1151 | if (skb==NULL) | 1151 | if (skb==NULL) |
1152 | goto out; | 1152 | goto out; |
1153 | 1153 | ||
@@ -1341,19 +1341,18 @@ static int netlink_dump(struct sock *sk) | |||
1341 | struct netlink_callback *cb; | 1341 | struct netlink_callback *cb; |
1342 | struct sk_buff *skb; | 1342 | struct sk_buff *skb; |
1343 | struct nlmsghdr *nlh; | 1343 | struct nlmsghdr *nlh; |
1344 | int len; | 1344 | int len, err = -ENOBUFS; |
1345 | 1345 | ||
1346 | skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL); | 1346 | skb = sock_rmalloc(sk, NLMSG_GOODSIZE, 0, GFP_KERNEL); |
1347 | if (!skb) | 1347 | if (!skb) |
1348 | return -ENOBUFS; | 1348 | goto errout; |
1349 | 1349 | ||
1350 | spin_lock(&nlk->cb_lock); | 1350 | spin_lock(&nlk->cb_lock); |
1351 | 1351 | ||
1352 | cb = nlk->cb; | 1352 | cb = nlk->cb; |
1353 | if (cb == NULL) { | 1353 | if (cb == NULL) { |
1354 | spin_unlock(&nlk->cb_lock); | 1354 | err = -EINVAL; |
1355 | kfree_skb(skb); | 1355 | goto errout_skb; |
1356 | return -EINVAL; | ||
1357 | } | 1356 | } |
1358 | 1357 | ||
1359 | len = cb->dump(skb, cb); | 1358 | len = cb->dump(skb, cb); |
@@ -1365,8 +1364,12 @@ static int netlink_dump(struct sock *sk) | |||
1365 | return 0; | 1364 | return 0; |
1366 | } | 1365 | } |
1367 | 1366 | ||
1368 | nlh = NLMSG_NEW_ANSWER(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI); | 1367 | nlh = nlmsg_put_answer(skb, cb, NLMSG_DONE, sizeof(len), NLM_F_MULTI); |
1369 | memcpy(NLMSG_DATA(nlh), &len, sizeof(len)); | 1368 | if (!nlh) |
1369 | goto errout_skb; | ||
1370 | |||
1371 | memcpy(nlmsg_data(nlh), &len, sizeof(len)); | ||
1372 | |||
1370 | skb_queue_tail(&sk->sk_receive_queue, skb); | 1373 | skb_queue_tail(&sk->sk_receive_queue, skb); |
1371 | sk->sk_data_ready(sk, skb->len); | 1374 | sk->sk_data_ready(sk, skb->len); |
1372 | 1375 | ||
@@ -1378,8 +1381,11 @@ static int netlink_dump(struct sock *sk) | |||
1378 | netlink_destroy_callback(cb); | 1381 | netlink_destroy_callback(cb); |
1379 | return 0; | 1382 | return 0; |
1380 | 1383 | ||
1381 | nlmsg_failure: | 1384 | errout_skb: |
1382 | return -ENOBUFS; | 1385 | spin_unlock(&nlk->cb_lock); |
1386 | kfree_skb(skb); | ||
1387 | errout: | ||
1388 | return err; | ||
1383 | } | 1389 | } |
1384 | 1390 | ||
1385 | int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, | 1391 | 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 | int size; | 1437 | int size; |
1432 | 1438 | ||
1433 | if (err == 0) | 1439 | if (err == 0) |
1434 | size = NLMSG_SPACE(sizeof(struct nlmsgerr)); | 1440 | size = nlmsg_total_size(sizeof(*errmsg)); |
1435 | else | 1441 | else |
1436 | size = NLMSG_SPACE(4 + NLMSG_ALIGN(nlh->nlmsg_len)); | 1442 | size = nlmsg_total_size(sizeof(*errmsg) + nlmsg_len(nlh)); |
1437 | 1443 | ||
1438 | skb = alloc_skb(size, GFP_KERNEL); | 1444 | skb = nlmsg_new(size, GFP_KERNEL); |
1439 | if (!skb) { | 1445 | if (!skb) { |
1440 | struct sock *sk; | 1446 | struct sock *sk; |
1441 | 1447 | ||
@@ -1451,16 +1457,15 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err) | |||
1451 | 1457 | ||
1452 | rep = __nlmsg_put(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, | 1458 | rep = __nlmsg_put(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, |
1453 | NLMSG_ERROR, sizeof(struct nlmsgerr), 0); | 1459 | NLMSG_ERROR, sizeof(struct nlmsgerr), 0); |
1454 | errmsg = NLMSG_DATA(rep); | 1460 | errmsg = nlmsg_data(rep); |
1455 | errmsg->error = err; | 1461 | errmsg->error = err; |
1456 | memcpy(&errmsg->msg, nlh, err ? nlh->nlmsg_len : sizeof(struct nlmsghdr)); | 1462 | memcpy(&errmsg->msg, nlh, err ? nlh->nlmsg_len : sizeof(*nlh)); |
1457 | netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); | 1463 | netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); |
1458 | } | 1464 | } |
1459 | 1465 | ||
1460 | static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, | 1466 | static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, |
1461 | struct nlmsghdr *, int *)) | 1467 | struct nlmsghdr *, int *)) |
1462 | { | 1468 | { |
1463 | unsigned int total_len; | ||
1464 | struct nlmsghdr *nlh; | 1469 | struct nlmsghdr *nlh; |
1465 | int err; | 1470 | int err; |
1466 | 1471 | ||
@@ -1470,8 +1475,6 @@ static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, | |||
1470 | if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len) | 1475 | if (nlh->nlmsg_len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len) |
1471 | return 0; | 1476 | return 0; |
1472 | 1477 | ||
1473 | total_len = min(NLMSG_ALIGN(nlh->nlmsg_len), skb->len); | ||
1474 | |||
1475 | if (cb(skb, nlh, &err) < 0) { | 1478 | if (cb(skb, nlh, &err) < 0) { |
1476 | /* Not an error, but we have to interrupt processing | 1479 | /* Not an error, but we have to interrupt processing |
1477 | * here. Note: that in this case we do not pull | 1480 | * 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 | } else if (nlh->nlmsg_flags & NLM_F_ACK) | 1486 | } else if (nlh->nlmsg_flags & NLM_F_ACK) |
1484 | netlink_ack(skb, nlh, 0); | 1487 | netlink_ack(skb, nlh, 0); |
1485 | 1488 | ||
1486 | skb_pull(skb, total_len); | 1489 | netlink_queue_skip(nlh, skb); |
1487 | } | 1490 | } |
1488 | 1491 | ||
1489 | return 0; | 1492 | return 0; |