aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/caif
diff options
context:
space:
mode:
authorDaniel Martensson <daniel.martensson@stericsson.com>2011-10-13 07:29:28 -0400
committerDavid S. Miller <davem@davemloft.net>2011-10-19 03:25:42 -0400
commit5bbed92d3d8dab1f28945eec9fb15b6f50bf8669 (patch)
tree158b09ec74eb3d4c7fa8e46de0576bad21f4d94c /drivers/net/caif
parent28bd2049428202cb3bc982536ed3de3c69ae120a (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.c79
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
351static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, 352static 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
436static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, 440static 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
627out_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
627static void cfhsi_rx_slowpath(unsigned long arg) 634static 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
814static 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
807static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) 825static 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);