aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRasmus Villemoes <linux@rasmusvillemoes.dk>2015-11-04 16:52:28 -0500
committerMartin K. Petersen <martin.petersen@oracle.com>2015-11-09 12:39:27 -0500
commit1faf072c0e3ab0bc41fc1d343883dac704b82946 (patch)
treee1ddaa85e1cd51f2ee6572282adbe6cba9fcfa31
parent7c59a0d46125d8c47c840e874d2cc9dd082afdf7 (diff)
hpsa: fix multiple issues in path_info_show
path_info_show() seems to be broken in multiple ways. First, there's 817 return snprintf(buf, output_len+1, "%s%s%s%s%s%s%s%s", 818 path[0], path[1], path[2], path[3], 819 path[4], path[5], path[6], path[7]); so hopefully output_len contains the combined length of the eight strings. Otherwise, snprintf will stop copying to the output buffer, but still end up reporting that combined length - which in turn would result in user-space getting a bunch of useless nul bytes (thankfully the upper sysfs layer seems to clear the output buffer before passing it to the various ->show routines). But we have 767 output_len = snprintf(path[i], 768 PATH_STRING_LEN, "[%d:%d:%d:%d] %20.20s ", 769 h->scsi_host->host_no, 770 hdev->bus, hdev->target, hdev->lun, 771 scsi_device_type(hdev->devtype)); so output_len at best contains the length of the last string printed. Inside the loop, we then otherwise add to output_len. By magic, we still have PATH_STRING_LEN available every time... This wouldn't really be a problem if the bean-counting has been done properly and each line actually does fit in 50 bytes, and maybe it does, but I don't immediately see why. Suppose we end up taking this branch: 802 output_len += snprintf(path[i] + output_len, 803 PATH_STRING_LEN, 804 "BOX: %hhu BAY: %hhu %s\n", 805 box, bay, active); An optimistic estimate says this uses strlen("BOX: 1 BAY: 2 Active\n") which is 21. Now add the 20 bytes guaranteed by the %20.20s and then some for the rest of that format string, and we're easily over 50 bytes. I don't think we can get over 100 bytes even being pessimistic, so this just means we'll scribble into the next path[i+1] and maybe get that overwritten later, leading to some garbled output (in fact, since we'd overwrite the previous string's 0-terminator, we could end up with one very long string and then print various suffixes of that, leading to much more than 400 bytes of output). Except of course when we're filling path[7], where overrunning it means writing random stuff to the kernel stack, which is usually a lot of fun. We can fix all of that and get rid of the 400 byte stack buffer by simply writing directly to the given output buffer, which the upper layer guarantees is at least PAGE_SIZE. s[c]nprintf doesn't care where it is writing to, so this doesn't make the spin lock hold time any longer. Using scnprintf ensures that output_len always represents the number of bytes actually written to the buffer, so we'll report the proper amount to the upper layer. Reviewed-by: Hannes Reinecke <hare@suse.de> Reviewed-by: Tomas Henzl <thenzl@redhat.com> Reviewed-by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com> Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk> Signed-off-by: Don Brace <don.brace@pmcs.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r--drivers/scsi/hpsa.c38
1 files changed, 17 insertions, 21 deletions
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 64638d56fb65..910b2d1513c0 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -734,7 +734,6 @@ static ssize_t host_show_hp_ssd_smart_path_enabled(struct device *dev,
734} 734}
735 735
736#define MAX_PATHS 8 736#define MAX_PATHS 8
737#define PATH_STRING_LEN 50
738 737
739static ssize_t path_info_show(struct device *dev, 738static ssize_t path_info_show(struct device *dev,
740 struct device_attribute *attr, char *buf) 739 struct device_attribute *attr, char *buf)
@@ -750,9 +749,7 @@ static ssize_t path_info_show(struct device *dev,
750 u8 path_map_index = 0; 749 u8 path_map_index = 0;
751 char *active; 750 char *active;
752 unsigned char phys_connector[2]; 751 unsigned char phys_connector[2];
753 unsigned char path[MAX_PATHS][PATH_STRING_LEN];
754 752
755 memset(path, 0, MAX_PATHS * PATH_STRING_LEN);
756 sdev = to_scsi_device(dev); 753 sdev = to_scsi_device(dev);
757 h = sdev_to_hba(sdev); 754 h = sdev_to_hba(sdev);
758 spin_lock_irqsave(&h->devlock, flags); 755 spin_lock_irqsave(&h->devlock, flags);
@@ -772,8 +769,9 @@ static ssize_t path_info_show(struct device *dev,
772 else 769 else
773 continue; 770 continue;
774 771
775 output_len = snprintf(path[i], 772 output_len += scnprintf(buf + output_len,
776 PATH_STRING_LEN, "[%d:%d:%d:%d] %20.20s ", 773 PAGE_SIZE - output_len,
774 "[%d:%d:%d:%d] %20.20s ",
777 h->scsi_host->host_no, 775 h->scsi_host->host_no,
778 hdev->bus, hdev->target, hdev->lun, 776 hdev->bus, hdev->target, hdev->lun,
779 scsi_device_type(hdev->devtype)); 777 scsi_device_type(hdev->devtype));
@@ -781,9 +779,9 @@ static ssize_t path_info_show(struct device *dev,
781 if (hdev->external || 779 if (hdev->external ||
782 hdev->devtype == TYPE_RAID || 780 hdev->devtype == TYPE_RAID ||
783 is_logical_device(hdev)) { 781 is_logical_device(hdev)) {
784 output_len += snprintf(path[i] + output_len, 782 output_len += snprintf(buf + output_len,
785 PATH_STRING_LEN, "%s\n", 783 PAGE_SIZE - output_len,
786 active); 784 "%s\n", active);
787 continue; 785 continue;
788 } 786 }
789 787
@@ -795,35 +793,33 @@ static ssize_t path_info_show(struct device *dev,
795 if (phys_connector[1] < '0') 793 if (phys_connector[1] < '0')
796 phys_connector[1] = '0'; 794 phys_connector[1] = '0';
797 if (hdev->phys_connector[i] > 0) 795 if (hdev->phys_connector[i] > 0)
798 output_len += snprintf(path[i] + output_len, 796 output_len += snprintf(buf + output_len,
799 PATH_STRING_LEN, 797 PAGE_SIZE - output_len,
800 "PORT: %.2s ", 798 "PORT: %.2s ",
801 phys_connector); 799 phys_connector);
802 if (hdev->devtype == TYPE_DISK && hdev->expose_device) { 800 if (hdev->devtype == TYPE_DISK && hdev->expose_device) {
803 if (box == 0 || box == 0xFF) { 801 if (box == 0 || box == 0xFF) {
804 output_len += snprintf(path[i] + output_len, 802 output_len += snprintf(buf + output_len,
805 PATH_STRING_LEN, 803 PAGE_SIZE - output_len,
806 "BAY: %hhu %s\n", 804 "BAY: %hhu %s\n",
807 bay, active); 805 bay, active);
808 } else { 806 } else {
809 output_len += snprintf(path[i] + output_len, 807 output_len += snprintf(buf + output_len,
810 PATH_STRING_LEN, 808 PAGE_SIZE - output_len,
811 "BOX: %hhu BAY: %hhu %s\n", 809 "BOX: %hhu BAY: %hhu %s\n",
812 box, bay, active); 810 box, bay, active);
813 } 811 }
814 } else if (box != 0 && box != 0xFF) { 812 } else if (box != 0 && box != 0xFF) {
815 output_len += snprintf(path[i] + output_len, 813 output_len += snprintf(buf + output_len,
816 PATH_STRING_LEN, "BOX: %hhu %s\n", 814 PAGE_SIZE - output_len, "BOX: %hhu %s\n",
817 box, active); 815 box, active);
818 } else 816 } else
819 output_len += snprintf(path[i] + output_len, 817 output_len += snprintf(buf + output_len,
820 PATH_STRING_LEN, "%s\n", active); 818 PAGE_SIZE - output_len, "%s\n", active);
821 } 819 }
822 820
823 spin_unlock_irqrestore(&h->devlock, flags); 821 spin_unlock_irqrestore(&h->devlock, flags);
824 return snprintf(buf, output_len+1, "%s%s%s%s%s%s%s%s", 822 return output_len;
825 path[0], path[1], path[2], path[3],
826 path[4], path[5], path[6], path[7]);
827} 823}
828 824
829static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL); 825static DEVICE_ATTR(raid_level, S_IRUGO, raid_level_show, NULL);