aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Graf <tgraf@suug.ch>2006-08-05 02:03:29 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-09-22 17:53:44 -0400
commitbf8b79e444a748963c71d2a58709e1ce5597e1b5 (patch)
tree4f598fc078926f3729cc615d0a5a00c2d1e57967
parentfe4944e59c357f945f81bc67edb7ed1392e875ad (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>
-rw-r--r--net/netlink/af_netlink.c41
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
1381nlmsg_failure: 1384errout_skb:
1382 return -ENOBUFS; 1385 spin_unlock(&nlk->cb_lock);
1386 kfree_skb(skb);
1387errout:
1388 return err;
1383} 1389}
1384 1390
1385int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, 1391int 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
1460static int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *, 1466static 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;