aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Fries <David@Fries.net>2014-04-08 23:37:07 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-04-16 17:07:51 -0400
commit593ceb0c7046c640cf463022189428a45219f595 (patch)
treebda2945990912476effbe15289da3d4072c568d0
parenta82cb8b91a37b6015f171a90dc4670e4c8e12e12 (diff)
w1: fix netlink refcnt leak on error path
If the message type is W1_MASTER_CMD or W1_SLAVE_CMD, then a reference is taken when searching for the slave or master device. If there isn't any following data m->len (mlen is a copy) is 0 and packing up the message for later execution is skipped leaving nothing to decrement the reference counts. Way back when, m->len was checked before the search that increments the reference count, but W1_LIST_MASTERS has no additional data, the check was moved in 9be62e0b2fadaf5ff causing this bug. This change reorders to put the check before the reference count is incremented avoiding the problem. Signed-off-by: David Fries <David@Fries.net> Acked-by: Evgeniy Polyakov <zbr@ioremap.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/w1/w1_netlink.c44
1 files changed, 26 insertions, 18 deletions
diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c
index 5234964fe001..a02704a59321 100644
--- a/drivers/w1/w1_netlink.c
+++ b/drivers/w1/w1_netlink.c
@@ -300,12 +300,6 @@ static int w1_process_command_root(struct cn_msg *msg,
300 struct w1_netlink_msg *w; 300 struct w1_netlink_msg *w;
301 u32 *id; 301 u32 *id;
302 302
303 if (mcmd->type != W1_LIST_MASTERS) {
304 printk(KERN_NOTICE "%s: msg: %x.%x, wrong type: %u, len: %u.\n",
305 __func__, msg->id.idx, msg->id.val, mcmd->type, mcmd->len);
306 return -EPROTO;
307 }
308
309 cn = kmalloc(PAGE_SIZE, GFP_KERNEL); 303 cn = kmalloc(PAGE_SIZE, GFP_KERNEL);
310 if (!cn) 304 if (!cn)
311 return -ENOMEM; 305 return -ENOMEM;
@@ -441,6 +435,9 @@ static void w1_process_cb(struct w1_master *dev, struct w1_async_cmd *async_cmd)
441 w1_netlink_send_error(&node->block->msg, node->m, cmd, 435 w1_netlink_send_error(&node->block->msg, node->m, cmd,
442 node->block->portid, err); 436 node->block->portid, err);
443 437
438 /* ref taken in w1_search_slave or w1_search_master_id when building
439 * the block
440 */
444 if (sl) 441 if (sl)
445 w1_unref_slave(sl); 442 w1_unref_slave(sl);
446 else 443 else
@@ -503,30 +500,42 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
503 500
504 msg_len = msg->len; 501 msg_len = msg->len;
505 while (msg_len && !err) { 502 while (msg_len && !err) {
506 struct w1_reg_num id;
507 u16 mlen = m->len;
508 503
509 dev = NULL; 504 dev = NULL;
510 sl = NULL; 505 sl = NULL;
511 506
512 memcpy(&id, m->id.id, sizeof(id));
513#if 0
514 printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n",
515 __func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len);
516#endif
517 if (m->len + sizeof(struct w1_netlink_msg) > msg_len) { 507 if (m->len + sizeof(struct w1_netlink_msg) > msg_len) {
518 err = -E2BIG; 508 err = -E2BIG;
519 break; 509 break;
520 } 510 }
521 511
512 /* execute on this thread, no need to process later */
513 if (m->type == W1_LIST_MASTERS) {
514 err = w1_process_command_root(msg, m, nsp->portid);
515 goto out_cont;
516 }
517
518 /* All following message types require additional data,
519 * check here before references are taken.
520 */
521 if (!m->len) {
522 err = -EPROTO;
523 goto out_cont;
524 }
525
526 /* both search calls take reference counts */
522 if (m->type == W1_MASTER_CMD) { 527 if (m->type == W1_MASTER_CMD) {
523 dev = w1_search_master_id(m->id.mst.id); 528 dev = w1_search_master_id(m->id.mst.id);
524 } else if (m->type == W1_SLAVE_CMD) { 529 } else if (m->type == W1_SLAVE_CMD) {
525 sl = w1_search_slave(&id); 530 sl = w1_search_slave((struct w1_reg_num *)m->id.id);
526 if (sl) 531 if (sl)
527 dev = sl->master; 532 dev = sl->master;
528 } else { 533 } else {
529 err = w1_process_command_root(msg, m, nsp->portid); 534 printk(KERN_NOTICE
535 "%s: msg: %x.%x, wrong type: %u, len: %u.\n",
536 __func__, msg->id.idx, msg->id.val,
537 m->type, m->len);
538 err = -EPROTO;
530 goto out_cont; 539 goto out_cont;
531 } 540 }
532 541
@@ -536,8 +545,6 @@ static void w1_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
536 } 545 }
537 546
538 err = 0; 547 err = 0;
539 if (!mlen)
540 goto out_cont;
541 548
542 atomic_inc(&block->refcnt); 549 atomic_inc(&block->refcnt);
543 node->async.cb = w1_process_cb; 550 node->async.cb = w1_process_cb;
@@ -557,7 +564,8 @@ out_cont:
557 if (err) 564 if (err)
558 w1_netlink_send_error(msg, m, NULL, nsp->portid, err); 565 w1_netlink_send_error(msg, m, NULL, nsp->portid, err);
559 msg_len -= sizeof(struct w1_netlink_msg) + m->len; 566 msg_len -= sizeof(struct w1_netlink_msg) + m->len;
560 m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); 567 m = (struct w1_netlink_msg *)(((u8 *)m) +
568 sizeof(struct w1_netlink_msg) + m->len);
561 569
562 /* 570 /*
563 * Let's allow requests for nonexisting devices. 571 * Let's allow requests for nonexisting devices.