diff options
author | Sucheta Chakraborty <sucheta@dut6195.unminc.com> | 2010-03-07 19:14:48 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-03-08 13:45:30 -0500 |
commit | b7eff1007fea3d153a9a5c0f872304ec19412bbb (patch) | |
tree | 6fb11b3fac7310e96ab45300029a096cd763b08f /drivers/net/qlcnic | |
parent | 9ab17b3968f9521bb4fffd8767953d2b0148aad0 (diff) |
qlcnic: validate unified fw image
Validate all sections of unified fw image, before accessing them,
to avoid seg fault.
Signed-off-by: Sucheta Chakraborty <sucheta@dut6195.unminc.com>
Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/qlcnic')
-rw-r--r-- | drivers/net/qlcnic/qlcnic_init.c | 146 |
1 files changed, 139 insertions, 7 deletions
diff --git a/drivers/net/qlcnic/qlcnic_init.c b/drivers/net/qlcnic/qlcnic_init.c index f0df9717aece..21a6e9f3dac5 100644 --- a/drivers/net/qlcnic/qlcnic_init.c +++ b/drivers/net/qlcnic/qlcnic_init.c | |||
@@ -568,21 +568,123 @@ struct uni_table_desc *qlcnic_get_table_desc(const u8 *unirom, int section) | |||
568 | return NULL; | 568 | return NULL; |
569 | } | 569 | } |
570 | 570 | ||
571 | #define FILEHEADER_SIZE (14 * 4) | ||
572 | |||
571 | static int | 573 | static int |
572 | qlcnic_set_product_offs(struct qlcnic_adapter *adapter) | 574 | qlcnic_validate_header(struct qlcnic_adapter *adapter) |
573 | { | 575 | { |
574 | struct uni_table_desc *ptab_descr; | ||
575 | const u8 *unirom = adapter->fw->data; | 576 | const u8 *unirom = adapter->fw->data; |
576 | u32 i; | 577 | struct uni_table_desc *directory = (struct uni_table_desc *) &unirom[0]; |
578 | __le32 fw_file_size = adapter->fw->size; | ||
577 | __le32 entries; | 579 | __le32 entries; |
580 | __le32 entry_size; | ||
581 | __le32 tab_size; | ||
582 | |||
583 | if (fw_file_size < FILEHEADER_SIZE) | ||
584 | return -EINVAL; | ||
585 | |||
586 | entries = cpu_to_le32(directory->num_entries); | ||
587 | entry_size = cpu_to_le32(directory->entry_size); | ||
588 | tab_size = cpu_to_le32(directory->findex) + (entries * entry_size); | ||
589 | |||
590 | if (fw_file_size < tab_size) | ||
591 | return -EINVAL; | ||
592 | |||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | static int | ||
597 | qlcnic_validate_bootld(struct qlcnic_adapter *adapter) | ||
598 | { | ||
599 | struct uni_table_desc *tab_desc; | ||
600 | struct uni_data_desc *descr; | ||
601 | const u8 *unirom = adapter->fw->data; | ||
602 | int idx = cpu_to_le32(*((int *)&unirom[adapter->file_prd_off] + | ||
603 | QLCNIC_UNI_BOOTLD_IDX_OFF)); | ||
604 | __le32 offs; | ||
605 | __le32 tab_size; | ||
606 | __le32 data_size; | ||
607 | |||
608 | tab_desc = qlcnic_get_table_desc(unirom, QLCNIC_UNI_DIR_SECT_BOOTLD); | ||
609 | |||
610 | if (!tab_desc) | ||
611 | return -EINVAL; | ||
612 | |||
613 | tab_size = cpu_to_le32(tab_desc->findex) + | ||
614 | (cpu_to_le32(tab_desc->entry_size * (idx + 1))); | ||
615 | |||
616 | if (adapter->fw->size < tab_size) | ||
617 | return -EINVAL; | ||
618 | |||
619 | offs = cpu_to_le32(tab_desc->findex) + | ||
620 | (cpu_to_le32(tab_desc->entry_size) * (idx)); | ||
621 | descr = (struct uni_data_desc *)&unirom[offs]; | ||
622 | |||
623 | data_size = descr->findex + cpu_to_le32(descr->size); | ||
624 | |||
625 | if (adapter->fw->size < data_size) | ||
626 | return -EINVAL; | ||
627 | |||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | static int | ||
632 | qlcnic_validate_fw(struct qlcnic_adapter *adapter) | ||
633 | { | ||
634 | struct uni_table_desc *tab_desc; | ||
635 | struct uni_data_desc *descr; | ||
636 | const u8 *unirom = adapter->fw->data; | ||
637 | int idx = cpu_to_le32(*((int *)&unirom[adapter->file_prd_off] + | ||
638 | QLCNIC_UNI_FIRMWARE_IDX_OFF)); | ||
639 | __le32 offs; | ||
640 | __le32 tab_size; | ||
641 | __le32 data_size; | ||
642 | |||
643 | tab_desc = qlcnic_get_table_desc(unirom, QLCNIC_UNI_DIR_SECT_FW); | ||
644 | |||
645 | if (!tab_desc) | ||
646 | return -EINVAL; | ||
647 | |||
648 | tab_size = cpu_to_le32(tab_desc->findex) + | ||
649 | (cpu_to_le32(tab_desc->entry_size * (idx + 1))); | ||
650 | |||
651 | if (adapter->fw->size < tab_size) | ||
652 | return -EINVAL; | ||
653 | |||
654 | offs = cpu_to_le32(tab_desc->findex) + | ||
655 | (cpu_to_le32(tab_desc->entry_size) * (idx)); | ||
656 | descr = (struct uni_data_desc *)&unirom[offs]; | ||
657 | data_size = descr->findex + cpu_to_le32(descr->size); | ||
658 | |||
659 | if (adapter->fw->size < data_size) | ||
660 | return -EINVAL; | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | static int | ||
666 | qlcnic_validate_product_offs(struct qlcnic_adapter *adapter) | ||
667 | { | ||
668 | struct uni_table_desc *ptab_descr; | ||
669 | const u8 *unirom = adapter->fw->data; | ||
578 | int mn_present = qlcnic_has_mn(adapter); | 670 | int mn_present = qlcnic_has_mn(adapter); |
671 | __le32 entries; | ||
672 | __le32 entry_size; | ||
673 | __le32 tab_size; | ||
674 | u32 i; | ||
579 | 675 | ||
580 | ptab_descr = qlcnic_get_table_desc(unirom, | 676 | ptab_descr = qlcnic_get_table_desc(unirom, |
581 | QLCNIC_UNI_DIR_SECT_PRODUCT_TBL); | 677 | QLCNIC_UNI_DIR_SECT_PRODUCT_TBL); |
582 | if (ptab_descr == NULL) | 678 | if (!ptab_descr) |
583 | return -1; | 679 | return -EINVAL; |
584 | 680 | ||
585 | entries = cpu_to_le32(ptab_descr->num_entries); | 681 | entries = cpu_to_le32(ptab_descr->num_entries); |
682 | entry_size = cpu_to_le32(ptab_descr->entry_size); | ||
683 | tab_size = cpu_to_le32(ptab_descr->findex) + (entries * entry_size); | ||
684 | |||
685 | if (adapter->fw->size < tab_size) | ||
686 | return -EINVAL; | ||
687 | |||
586 | nomn: | 688 | nomn: |
587 | for (i = 0; i < entries; i++) { | 689 | for (i = 0; i < entries; i++) { |
588 | 690 | ||
@@ -609,7 +711,37 @@ nomn: | |||
609 | mn_present = 0; | 711 | mn_present = 0; |
610 | goto nomn; | 712 | goto nomn; |
611 | } | 713 | } |
612 | return -1; | 714 | return -EINVAL; |
715 | } | ||
716 | |||
717 | static int | ||
718 | qlcnic_validate_unified_romimage(struct qlcnic_adapter *adapter) | ||
719 | { | ||
720 | if (qlcnic_validate_header(adapter)) { | ||
721 | dev_err(&adapter->pdev->dev, | ||
722 | "unified image: header validation failed\n"); | ||
723 | return -EINVAL; | ||
724 | } | ||
725 | |||
726 | if (qlcnic_validate_product_offs(adapter)) { | ||
727 | dev_err(&adapter->pdev->dev, | ||
728 | "unified image: product validation failed\n"); | ||
729 | return -EINVAL; | ||
730 | } | ||
731 | |||
732 | if (qlcnic_validate_bootld(adapter)) { | ||
733 | dev_err(&adapter->pdev->dev, | ||
734 | "unified image: bootld validation failed\n"); | ||
735 | return -EINVAL; | ||
736 | } | ||
737 | |||
738 | if (qlcnic_validate_fw(adapter)) { | ||
739 | dev_err(&adapter->pdev->dev, | ||
740 | "unified image: firmware validation failed\n"); | ||
741 | return -EINVAL; | ||
742 | } | ||
743 | |||
744 | return 0; | ||
613 | } | 745 | } |
614 | 746 | ||
615 | static | 747 | static |
@@ -858,7 +990,7 @@ qlcnic_validate_firmware(struct qlcnic_adapter *adapter) | |||
858 | u8 fw_type = adapter->fw_type; | 990 | u8 fw_type = adapter->fw_type; |
859 | 991 | ||
860 | if (fw_type == QLCNIC_UNIFIED_ROMIMAGE) { | 992 | if (fw_type == QLCNIC_UNIFIED_ROMIMAGE) { |
861 | if (qlcnic_set_product_offs(adapter)) | 993 | if (qlcnic_validate_unified_romimage(adapter)) |
862 | return -EINVAL; | 994 | return -EINVAL; |
863 | 995 | ||
864 | min_size = QLCNIC_UNI_FW_MIN_SIZE; | 996 | min_size = QLCNIC_UNI_FW_MIN_SIZE; |