diff options
author | Daniel Martensson <daniel.martensson@stericsson.com> | 2011-10-13 07:29:28 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-10-19 03:25:42 -0400 |
commit | 5bbed92d3d8dab1f28945eec9fb15b6f50bf8669 (patch) | |
tree | 158b09ec74eb3d4c7fa8e46de0576bad21f4d94c /drivers/net/caif | |
parent | 28bd2049428202cb3bc982536ed3de3c69ae120a (diff) |
caif-hsi: Added sanity check for length of CAIF frames
Added sanity check for length of CAIF frames, and tear down of
CAIF link-layer device upon protocol error.
Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/caif')
-rw-r--r-- | drivers/net/caif/caif_hsi.c | 79 |
1 files changed, 49 insertions, 30 deletions
diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 1e1f0a372ffa..e9e7cbf01a5f 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
19 | #include <linux/if_arp.h> | 19 | #include <linux/if_arp.h> |
20 | #include <linux/timer.h> | 20 | #include <linux/timer.h> |
21 | #include <linux/rtnetlink.h> | ||
21 | #include <net/caif/caif_layer.h> | 22 | #include <net/caif/caif_layer.h> |
22 | #include <net/caif/caif_hsi.h> | 23 | #include <net/caif/caif_hsi.h> |
23 | 24 | ||
@@ -348,8 +349,7 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv) | |||
348 | cfhsi_tx_done(cfhsi); | 349 | cfhsi_tx_done(cfhsi); |
349 | } | 350 | } |
350 | 351 | ||
351 | static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, | 352 | static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) |
352 | bool *dump) | ||
353 | { | 353 | { |
354 | int xfer_sz = 0; | 354 | int xfer_sz = 0; |
355 | int nfrms = 0; | 355 | int nfrms = 0; |
@@ -360,8 +360,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, | |||
360 | (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { | 360 | (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { |
361 | dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", | 361 | dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", |
362 | __func__); | 362 | __func__); |
363 | *dump = true; | 363 | return -EPROTO; |
364 | return 0; | ||
365 | } | 364 | } |
366 | 365 | ||
367 | /* Check for embedded CAIF frame. */ | 366 | /* Check for embedded CAIF frame. */ |
@@ -379,6 +378,12 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, | |||
379 | len |= ((*(pfrm+1)) << 8) & 0xFF00; | 378 | len |= ((*(pfrm+1)) << 8) & 0xFF00; |
380 | len += 2; /* Add FCS fields. */ | 379 | len += 2; /* Add FCS fields. */ |
381 | 380 | ||
381 | /* Sanity check length of CAIF frame. */ | ||
382 | if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { | ||
383 | dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n", | ||
384 | __func__); | ||
385 | return -EPROTO; | ||
386 | } | ||
382 | 387 | ||
383 | /* Allocate SKB (OK even in IRQ context). */ | 388 | /* Allocate SKB (OK even in IRQ context). */ |
384 | skb = alloc_skb(len + 1, GFP_ATOMIC); | 389 | skb = alloc_skb(len + 1, GFP_ATOMIC); |
@@ -423,18 +428,16 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, | |||
423 | if (desc->header & CFHSI_PIGGY_DESC) | 428 | if (desc->header & CFHSI_PIGGY_DESC) |
424 | xfer_sz += CFHSI_DESC_SZ; | 429 | xfer_sz += CFHSI_DESC_SZ; |
425 | 430 | ||
426 | if (xfer_sz % 4) { | 431 | if ((xfer_sz % 4) || (xfer_sz > (CFHSI_BUF_SZ_RX - CFHSI_DESC_SZ))) { |
427 | dev_err(&cfhsi->ndev->dev, | 432 | dev_err(&cfhsi->ndev->dev, |
428 | "%s: Invalid payload len: %d, ignored.\n", | 433 | "%s: Invalid payload len: %d, ignored.\n", |
429 | __func__, xfer_sz); | 434 | __func__, xfer_sz); |
430 | xfer_sz = 0; | 435 | return -EPROTO; |
431 | *dump = true; | ||
432 | } | 436 | } |
433 | return xfer_sz; | 437 | return xfer_sz; |
434 | } | 438 | } |
435 | 439 | ||
436 | static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, | 440 | static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) |
437 | bool *dump) | ||
438 | { | 441 | { |
439 | int rx_sz = 0; | 442 | int rx_sz = 0; |
440 | int nfrms = 0; | 443 | int nfrms = 0; |
@@ -446,8 +449,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, | |||
446 | (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) { | 449 | (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) { |
447 | dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", | 450 | dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", |
448 | __func__); | 451 | __func__); |
449 | *dump = true; | 452 | return -EPROTO; |
450 | return -EINVAL; | ||
451 | } | 453 | } |
452 | 454 | ||
453 | /* Set frame pointer to start of payload. */ | 455 | /* Set frame pointer to start of payload. */ |
@@ -469,13 +471,6 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, | |||
469 | u8 *pcffrm = NULL; | 471 | u8 *pcffrm = NULL; |
470 | int len = 0; | 472 | int len = 0; |
471 | 473 | ||
472 | if (WARN_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ)) { | ||
473 | dev_err(&cfhsi->ndev->dev, "%s: Invalid payload.\n", | ||
474 | __func__); | ||
475 | *dump = true; | ||
476 | return -EINVAL; | ||
477 | } | ||
478 | |||
479 | /* CAIF frame starts after head padding. */ | 474 | /* CAIF frame starts after head padding. */ |
480 | pcffrm = pfrm + *pfrm + 1; | 475 | pcffrm = pfrm + *pfrm + 1; |
481 | 476 | ||
@@ -484,6 +479,13 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, | |||
484 | len |= ((*(pcffrm + 1)) << 8) & 0xFF00; | 479 | len |= ((*(pcffrm + 1)) << 8) & 0xFF00; |
485 | len += 2; /* Add FCS fields. */ | 480 | len += 2; /* Add FCS fields. */ |
486 | 481 | ||
482 | /* Sanity check length of CAIF frames. */ | ||
483 | if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { | ||
484 | dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n", | ||
485 | __func__); | ||
486 | return -EPROTO; | ||
487 | } | ||
488 | |||
487 | /* Allocate SKB (OK even in IRQ context). */ | 489 | /* Allocate SKB (OK even in IRQ context). */ |
488 | skb = alloc_skb(len + 1, GFP_ATOMIC); | 490 | skb = alloc_skb(len + 1, GFP_ATOMIC); |
489 | if (!skb) { | 491 | if (!skb) { |
@@ -528,7 +530,6 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) | |||
528 | int res; | 530 | int res; |
529 | int desc_pld_len = 0; | 531 | int desc_pld_len = 0; |
530 | struct cfhsi_desc *desc = NULL; | 532 | struct cfhsi_desc *desc = NULL; |
531 | bool dump = false; | ||
532 | 533 | ||
533 | desc = (struct cfhsi_desc *)cfhsi->rx_buf; | 534 | desc = (struct cfhsi_desc *)cfhsi->rx_buf; |
534 | 535 | ||
@@ -544,16 +545,20 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) | |||
544 | spin_unlock_bh(&cfhsi->lock); | 545 | spin_unlock_bh(&cfhsi->lock); |
545 | 546 | ||
546 | if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { | 547 | if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { |
547 | desc_pld_len = cfhsi_rx_desc(desc, cfhsi, &dump); | 548 | desc_pld_len = cfhsi_rx_desc(desc, cfhsi); |
548 | if (desc_pld_len == -ENOMEM) | 549 | if (desc_pld_len == -ENOMEM) |
549 | goto restart; | 550 | goto restart; |
551 | if (desc_pld_len == -EPROTO) | ||
552 | goto out_of_sync; | ||
550 | } else { | 553 | } else { |
551 | int pld_len; | 554 | int pld_len; |
552 | 555 | ||
553 | if (!cfhsi->rx_state.piggy_desc) { | 556 | if (!cfhsi->rx_state.piggy_desc) { |
554 | pld_len = cfhsi_rx_pld(desc, cfhsi, &dump); | 557 | pld_len = cfhsi_rx_pld(desc, cfhsi); |
555 | if (pld_len == -ENOMEM) | 558 | if (pld_len == -ENOMEM) |
556 | goto restart; | 559 | goto restart; |
560 | if (pld_len == -EPROTO) | ||
561 | goto out_of_sync; | ||
557 | cfhsi->rx_state.pld_len = pld_len; | 562 | cfhsi->rx_state.pld_len = pld_len; |
558 | } else { | 563 | } else { |
559 | pld_len = cfhsi->rx_state.pld_len; | 564 | pld_len = cfhsi->rx_state.pld_len; |
@@ -567,7 +572,7 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) | |||
567 | cfhsi->rx_state.piggy_desc = true; | 572 | cfhsi->rx_state.piggy_desc = true; |
568 | 573 | ||
569 | /* Extract piggy-backed descriptor. */ | 574 | /* Extract piggy-backed descriptor. */ |
570 | desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi, &dump); | 575 | desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi); |
571 | if (desc_pld_len == -ENOMEM) | 576 | if (desc_pld_len == -ENOMEM) |
572 | goto restart; | 577 | goto restart; |
573 | 578 | ||
@@ -577,15 +582,10 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) | |||
577 | */ | 582 | */ |
578 | memcpy((u8 *)desc, (u8 *)piggy_desc, | 583 | memcpy((u8 *)desc, (u8 *)piggy_desc, |
579 | CFHSI_DESC_SHORT_SZ); | 584 | CFHSI_DESC_SHORT_SZ); |
580 | } | ||
581 | } | ||
582 | 585 | ||
583 | if (unlikely(dump)) { | 586 | if (desc_pld_len == -EPROTO) |
584 | size_t rx_offset = cfhsi->rx_ptr - cfhsi->rx_buf; | 587 | goto out_of_sync; |
585 | dev_err(&cfhsi->ndev->dev, "%s: RX offset: %u.\n", | 588 | } |
586 | __func__, (unsigned) rx_offset); | ||
587 | print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, | ||
588 | cfhsi->rx_buf, cfhsi->rx_len + rx_offset); | ||
589 | } | 589 | } |
590 | 590 | ||
591 | memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state)); | 591 | memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state)); |
@@ -622,6 +622,13 @@ restart: | |||
622 | BUG(); | 622 | BUG(); |
623 | } | 623 | } |
624 | mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1); | 624 | mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1); |
625 | return; | ||
626 | |||
627 | out_of_sync: | ||
628 | dev_err(&cfhsi->ndev->dev, "%s: Out of sync.\n", __func__); | ||
629 | print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, | ||
630 | cfhsi->rx_buf, CFHSI_DESC_SZ); | ||
631 | schedule_work(&cfhsi->out_of_sync_work); | ||
625 | } | 632 | } |
626 | 633 | ||
627 | static void cfhsi_rx_slowpath(unsigned long arg) | 634 | static void cfhsi_rx_slowpath(unsigned long arg) |
@@ -804,6 +811,17 @@ static void cfhsi_wake_down(struct work_struct *work) | |||
804 | 811 | ||
805 | } | 812 | } |
806 | 813 | ||
814 | static void cfhsi_out_of_sync(struct work_struct *work) | ||
815 | { | ||
816 | struct cfhsi *cfhsi = NULL; | ||
817 | |||
818 | cfhsi = container_of(work, struct cfhsi, out_of_sync_work); | ||
819 | |||
820 | rtnl_lock(); | ||
821 | dev_close(cfhsi->ndev); | ||
822 | rtnl_unlock(); | ||
823 | } | ||
824 | |||
807 | static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) | 825 | static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) |
808 | { | 826 | { |
809 | struct cfhsi *cfhsi = NULL; | 827 | struct cfhsi *cfhsi = NULL; |
@@ -1023,6 +1041,7 @@ int cfhsi_probe(struct platform_device *pdev) | |||
1023 | /* Initialize the work queues. */ | 1041 | /* Initialize the work queues. */ |
1024 | INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); | 1042 | INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); |
1025 | INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down); | 1043 | INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down); |
1044 | INIT_WORK(&cfhsi->out_of_sync_work, cfhsi_out_of_sync); | ||
1026 | 1045 | ||
1027 | /* Clear all bit fields. */ | 1046 | /* Clear all bit fields. */ |
1028 | clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); | 1047 | clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); |