Commit 7e76f34fa103677a27d96a7cfef8ce61389a32de
Committed by
Benjamin Herrenschmidt
1 parent
4e90a2a737
Exists in
master
and in
20 other branches
powerpc/pseries: Fix buffer overflow when reading from pstore
When reading from pstore there is a buffer overflow during decompression due to the header added in unzip_oops. Remove unzip_oops and call pstore_decompress directly in nvram_pstore_read. Allocate buffer of size report_length of the oops header as header will not be deallocated in pstore. Since we have 'openssl' command line tool to decompress the compressed data, dump the compressed data in case decompression fails instead of not dumping anything. Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Showing 1 changed file with 24 additions and 46 deletions Side-by-side Diff
arch/powerpc/platforms/pseries/nvram.c
... | ... | @@ -569,35 +569,6 @@ |
569 | 569 | return ret; |
570 | 570 | } |
571 | 571 | |
572 | -static int unzip_oops(char *oops_buf, char *big_buf) | |
573 | -{ | |
574 | - struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; | |
575 | - u64 timestamp = oops_hdr->timestamp; | |
576 | - char *big_oops_data = NULL; | |
577 | - char *oops_data_buf = NULL; | |
578 | - size_t big_oops_data_sz; | |
579 | - int unzipped_len; | |
580 | - | |
581 | - big_oops_data = big_buf + sizeof(struct oops_log_info); | |
582 | - big_oops_data_sz = big_oops_buf_sz - sizeof(struct oops_log_info); | |
583 | - oops_data_buf = oops_buf + sizeof(struct oops_log_info); | |
584 | - | |
585 | - unzipped_len = nvram_decompress(oops_data_buf, big_oops_data, | |
586 | - oops_hdr->report_length, | |
587 | - big_oops_data_sz); | |
588 | - | |
589 | - if (unzipped_len < 0) { | |
590 | - pr_err("nvram: decompression failed; returned %d\n", | |
591 | - unzipped_len); | |
592 | - return -1; | |
593 | - } | |
594 | - oops_hdr = (struct oops_log_info *)big_buf; | |
595 | - oops_hdr->version = OOPS_HDR_VERSION; | |
596 | - oops_hdr->report_length = (u16) unzipped_len; | |
597 | - oops_hdr->timestamp = timestamp; | |
598 | - return 0; | |
599 | -} | |
600 | - | |
601 | 572 | static int nvram_pstore_open(struct pstore_info *psi) |
602 | 573 | { |
603 | 574 | /* Reset the iterator to start reading partitions again */ |
604 | 575 | |
... | ... | @@ -685,10 +656,9 @@ |
685 | 656 | unsigned int err_type, id_no, size = 0; |
686 | 657 | struct nvram_os_partition *part = NULL; |
687 | 658 | char *buff = NULL, *big_buff = NULL; |
688 | - int rc, sig = 0; | |
659 | + int sig = 0; | |
689 | 660 | loff_t p; |
690 | 661 | |
691 | -read_partition: | |
692 | 662 | read_type++; |
693 | 663 | |
694 | 664 | switch (nvram_type_ids[read_type]) { |
695 | 665 | |
696 | 666 | |
697 | 667 | |
698 | 668 | |
699 | 669 | |
700 | 670 | |
... | ... | @@ -749,30 +719,36 @@ |
749 | 719 | *id = id_no; |
750 | 720 | |
751 | 721 | if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { |
722 | + int length, unzipped_len; | |
723 | + | |
752 | 724 | oops_hdr = (struct oops_log_info *)buff; |
753 | - *buf = buff + sizeof(*oops_hdr); | |
725 | + length = oops_hdr->report_length; | |
726 | + *buf = kmalloc(length, GFP_KERNEL); | |
727 | + if (*buf == NULL) | |
728 | + return -ENOMEM; | |
729 | + memcpy(*buf, buff + sizeof(*oops_hdr), length); | |
730 | + time->tv_sec = oops_hdr->timestamp; | |
731 | + time->tv_nsec = 0; | |
732 | + kfree(buff); | |
754 | 733 | |
755 | 734 | if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) { |
756 | 735 | big_buff = kmalloc(big_oops_buf_sz, GFP_KERNEL); |
757 | 736 | if (!big_buff) |
758 | 737 | return -ENOMEM; |
759 | 738 | |
760 | - rc = unzip_oops(buff, big_buff); | |
739 | + unzipped_len = nvram_decompress(*buf, big_buff, | |
740 | + length, big_oops_buf_sz); | |
761 | 741 | |
762 | - if (rc != 0) { | |
763 | - kfree(buff); | |
742 | + if (unzipped_len < 0) { | |
743 | + pr_err("nvram: decompression failed, returned " | |
744 | + "rc %d\n", unzipped_len); | |
764 | 745 | kfree(big_buff); |
765 | - goto read_partition; | |
746 | + } else { | |
747 | + *buf = big_buff; | |
748 | + length = unzipped_len; | |
766 | 749 | } |
767 | - | |
768 | - oops_hdr = (struct oops_log_info *)big_buff; | |
769 | - *buf = big_buff + sizeof(*oops_hdr); | |
770 | - kfree(buff); | |
771 | 750 | } |
772 | - | |
773 | - time->tv_sec = oops_hdr->timestamp; | |
774 | - time->tv_nsec = 0; | |
775 | - return oops_hdr->report_length; | |
751 | + return length; | |
776 | 752 | } |
777 | 753 | |
778 | 754 | *buf = buff; |
... | ... | @@ -816,6 +792,7 @@ |
816 | 792 | static void __init nvram_init_oops_partition(int rtas_partition_exists) |
817 | 793 | { |
818 | 794 | int rc; |
795 | + size_t size; | |
819 | 796 | |
820 | 797 | rc = pseries_nvram_init_os_partition(&oops_log_partition); |
821 | 798 | if (rc != 0) { |
... | ... | @@ -844,8 +821,9 @@ |
844 | 821 | big_oops_buf_sz = (oops_data_sz * 100) / 45; |
845 | 822 | big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); |
846 | 823 | if (big_oops_buf) { |
847 | - stream.workspace = kmalloc(zlib_deflate_workspacesize( | |
848 | - WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); | |
824 | + size = max(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL), | |
825 | + zlib_inflate_workspacesize()); | |
826 | + stream.workspace = kmalloc(size, GFP_KERNEL); | |
849 | 827 | if (!stream.workspace) { |
850 | 828 | pr_err("nvram: No memory for compression workspace; " |
851 | 829 | "skipping compression of %s partition data\n", |