Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Daniel Martensson and committed by
David S. Miller
5bbed92d 28bd2049

+53 -32
+49 -30
drivers/net/caif/caif_hsi.c
··· 18 18 #include <linux/sched.h> 19 19 #include <linux/if_arp.h> 20 20 #include <linux/timer.h> 21 + #include <linux/rtnetlink.h> 21 22 #include <net/caif/caif_layer.h> 22 23 #include <net/caif/caif_hsi.h> 23 24 ··· 349 348 cfhsi_tx_done(cfhsi); 350 349 } 351 350 352 - static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi, 353 - bool *dump) 351 + static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) 354 352 { 355 353 int xfer_sz = 0; 356 354 int nfrms = 0; ··· 360 360 (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { 361 361 dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", 362 362 __func__); 363 - *dump = true; 364 - return 0; 363 + return -EPROTO; 365 364 } 366 365 367 366 /* Check for embedded CAIF frame. */ ··· 378 379 len |= ((*(pfrm+1)) << 8) & 0xFF00; 379 380 len += 2; /* Add FCS fields. */ 380 381 382 + /* Sanity check length of CAIF frame. */ 383 + if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { 384 + dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n", 385 + __func__); 386 + return -EPROTO; 387 + } 381 388 382 389 /* Allocate SKB (OK even in IRQ context). */ 383 390 skb = alloc_skb(len + 1, GFP_ATOMIC); ··· 428 423 if (desc->header & CFHSI_PIGGY_DESC) 429 424 xfer_sz += CFHSI_DESC_SZ; 430 425 431 - if (xfer_sz % 4) { 426 + if ((xfer_sz % 4) || (xfer_sz > (CFHSI_BUF_SZ_RX - CFHSI_DESC_SZ))) { 432 427 dev_err(&cfhsi->ndev->dev, 433 428 "%s: Invalid payload len: %d, ignored.\n", 434 429 __func__, xfer_sz); 435 - xfer_sz = 0; 436 - *dump = true; 430 + return -EPROTO; 437 431 } 438 432 return xfer_sz; 439 433 } 440 434 441 - static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi, 442 - bool *dump) 435 + static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) 443 436 { 444 437 int rx_sz = 0; 445 438 int nfrms = 0; ··· 449 446 (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) { 450 447 dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", 451 448 __func__); 452 - *dump = true; 453 - return -EINVAL; 449 + return -EPROTO; 454 450 } 455 451 456 452 /* Set frame pointer to start of payload. */ ··· 471 469 u8 *pcffrm = NULL; 472 470 int len = 0; 473 471 474 - if (WARN_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ)) { 475 - dev_err(&cfhsi->ndev->dev, "%s: Invalid payload.\n", 476 - __func__); 477 - *dump = true; 478 - return -EINVAL; 479 - } 480 - 481 472 /* CAIF frame starts after head padding. */ 482 473 pcffrm = pfrm + *pfrm + 1; 483 474 ··· 478 483 len = *pcffrm; 479 484 len |= ((*(pcffrm + 1)) << 8) & 0xFF00; 480 485 len += 2; /* Add FCS fields. */ 486 + 487 + /* Sanity check length of CAIF frames. */ 488 + if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { 489 + dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n", 490 + __func__); 491 + return -EPROTO; 492 + } 481 493 482 494 /* Allocate SKB (OK even in IRQ context). */ 483 495 skb = alloc_skb(len + 1, GFP_ATOMIC); ··· 530 528 int res; 531 529 int desc_pld_len = 0; 532 530 struct cfhsi_desc *desc = NULL; 533 - bool dump = false; 534 531 535 532 desc = (struct cfhsi_desc *)cfhsi->rx_buf; 536 533 ··· 545 544 spin_unlock_bh(&cfhsi->lock); 546 545 547 546 if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { 548 - desc_pld_len = cfhsi_rx_desc(desc, cfhsi, &dump); 547 + desc_pld_len = cfhsi_rx_desc(desc, cfhsi); 549 548 if (desc_pld_len == -ENOMEM) 550 549 goto restart; 550 + if (desc_pld_len == -EPROTO) 551 + goto out_of_sync; 551 552 } else { 552 553 int pld_len; 553 554 554 555 if (!cfhsi->rx_state.piggy_desc) { 555 - pld_len = cfhsi_rx_pld(desc, cfhsi, &dump); 556 + pld_len = cfhsi_rx_pld(desc, cfhsi); 556 557 if (pld_len == -ENOMEM) 557 558 goto restart; 559 + if (pld_len == -EPROTO) 560 + goto out_of_sync; 558 561 cfhsi->rx_state.pld_len = pld_len; 559 562 } else { 560 563 pld_len = cfhsi->rx_state.pld_len; ··· 572 567 cfhsi->rx_state.piggy_desc = true; 573 568 574 569 /* Extract piggy-backed descriptor. */ 575 - desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi, &dump); 570 + desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi); 576 571 if (desc_pld_len == -ENOMEM) 577 572 goto restart; 578 573 ··· 582 577 */ 583 578 memcpy((u8 *)desc, (u8 *)piggy_desc, 584 579 CFHSI_DESC_SHORT_SZ); 585 - } 586 - } 587 580 588 - if (unlikely(dump)) { 589 - size_t rx_offset = cfhsi->rx_ptr - cfhsi->rx_buf; 590 - dev_err(&cfhsi->ndev->dev, "%s: RX offset: %u.\n", 591 - __func__, (unsigned) rx_offset); 592 - print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, 593 - cfhsi->rx_buf, cfhsi->rx_len + rx_offset); 581 + if (desc_pld_len == -EPROTO) 582 + goto out_of_sync; 583 + } 594 584 } 595 585 596 586 memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state)); ··· 622 622 BUG(); 623 623 } 624 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 634 static void cfhsi_rx_slowpath(unsigned long arg) ··· 809 802 /* Cancel pending RX requests. */ 810 803 cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); 811 804 805 + } 806 + 807 + static void cfhsi_out_of_sync(struct work_struct *work) 808 + { 809 + struct cfhsi *cfhsi = NULL; 810 + 811 + cfhsi = container_of(work, struct cfhsi, out_of_sync_work); 812 + 813 + rtnl_lock(); 814 + dev_close(cfhsi->ndev); 815 + rtnl_unlock(); 812 816 } 813 817 814 818 static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) ··· 1041 1023 /* Initialize the work queues. */ 1042 1024 INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); 1043 1025 INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down); 1026 + INIT_WORK(&cfhsi->out_of_sync_work, cfhsi_out_of_sync); 1044 1027 1045 1028 /* Clear all bit fields. */ 1046 1029 clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
+4 -2
include/net/caif/caif_hsi.h
··· 52 52 /* 53 53 * Maximum bytes transferred in one transfer. 54 54 */ 55 - /* TODO: 4096 is temporary... */ 56 - #define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * 4096) 55 + #define CFHSI_MAX_CAIF_FRAME_SZ 4096 56 + 57 + #define CFHSI_MAX_PAYLOAD_SZ (CFHSI_MAX_PKTS * CFHSI_MAX_CAIF_FRAME_SZ) 57 58 58 59 /* Size of the complete HSI TX buffer. */ 59 60 #define CFHSI_BUF_SZ_TX (CFHSI_DESC_SZ + CFHSI_MAX_PAYLOAD_SZ) ··· 144 143 struct list_head list; 145 144 struct work_struct wake_up_work; 146 145 struct work_struct wake_down_work; 146 + struct work_struct out_of_sync_work; 147 147 struct workqueue_struct *wq; 148 148 wait_queue_head_t wake_up_wait; 149 149 wait_queue_head_t wake_down_wait;