diff options
author | Sagi Grimberg <sagig@mellanox.com> | 2015-07-15 03:55:37 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2015-07-24 01:53:05 -0400 |
commit | f5a8b3a796db01b639435515b3adc003b9f27387 (patch) | |
tree | 3ff4e026ec2af94cbef2cef9d365a5faf141059c | |
parent | 12306b425d0dbab7b60f54e02d67cf3dfae494d1 (diff) |
scsi: Protect against buffer possible overflow in scsi_set_sense_information
Make sure that the input sense buffer has sufficient length
to fit the information descriptor (12 additional bytes).
Modify scsi_set_sense_information to receive the sense buffer
length and adjust its callers scsi target and libata.
(Fix patch fuzz in scsi_set_sense_information - nab)
Reported-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Sagi Grimberg <sagig@mellanox.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Cc: Tejun Heo <tj@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r-- | drivers/ata/libata-scsi.c | 4 | ||||
-rw-r--r-- | drivers/scsi/scsi_common.c | 13 | ||||
-rw-r--r-- | drivers/target/target_core_transport.c | 14 | ||||
-rw-r--r-- | include/scsi/scsi_common.h | 2 |
4 files changed, 27 insertions, 6 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 3131adcc1f87..2fb7c79e727f 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c | |||
@@ -289,7 +289,9 @@ void ata_scsi_set_sense_information(struct scsi_cmnd *cmd, | |||
289 | return; | 289 | return; |
290 | 290 | ||
291 | information = ata_tf_read_block(tf, NULL); | 291 | information = ata_tf_read_block(tf, NULL); |
292 | scsi_set_sense_information(cmd->sense_buffer, information); | 292 | scsi_set_sense_information(cmd->sense_buffer, |
293 | SCSI_SENSE_BUFFERSIZE, | ||
294 | information); | ||
293 | } | 295 | } |
294 | 296 | ||
295 | static ssize_t | 297 | static ssize_t |
diff --git a/drivers/scsi/scsi_common.c b/drivers/scsi/scsi_common.c index ee6bdf43a8ea..c126966130ab 100644 --- a/drivers/scsi/scsi_common.c +++ b/drivers/scsi/scsi_common.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <linux/bug.h> | 5 | #include <linux/bug.h> |
6 | #include <linux/kernel.h> | 6 | #include <linux/kernel.h> |
7 | #include <linux/string.h> | 7 | #include <linux/string.h> |
8 | #include <linux/errno.h> | ||
8 | #include <asm/unaligned.h> | 9 | #include <asm/unaligned.h> |
9 | #include <scsi/scsi_common.h> | 10 | #include <scsi/scsi_common.h> |
10 | 11 | ||
@@ -249,10 +250,13 @@ EXPORT_SYMBOL(scsi_build_sense_buffer); | |||
249 | * scsi_set_sense_information - set the information field in a | 250 | * scsi_set_sense_information - set the information field in a |
250 | * formatted sense data buffer | 251 | * formatted sense data buffer |
251 | * @buf: Where to build sense data | 252 | * @buf: Where to build sense data |
253 | * @buf_len: buffer length | ||
252 | * @info: 64-bit information value to be set | 254 | * @info: 64-bit information value to be set |
253 | * | 255 | * |
256 | * Return value: | ||
257 | * 0 on success or EINVAL for invalid sense buffer length | ||
254 | **/ | 258 | **/ |
255 | void scsi_set_sense_information(u8 *buf, u64 info) | 259 | int scsi_set_sense_information(u8 *buf, int buf_len, u64 info) |
256 | { | 260 | { |
257 | if ((buf[0] & 0x7f) == 0x72) { | 261 | if ((buf[0] & 0x7f) == 0x72) { |
258 | u8 *ucp, len; | 262 | u8 *ucp, len; |
@@ -263,6 +267,11 @@ void scsi_set_sense_information(u8 *buf, u64 info) | |||
263 | buf[7] = len + 0xc; | 267 | buf[7] = len + 0xc; |
264 | ucp = buf + 8 + len; | 268 | ucp = buf + 8 + len; |
265 | } | 269 | } |
270 | |||
271 | if (buf_len < len + 0xc) | ||
272 | /* Not enough room for info */ | ||
273 | return -EINVAL; | ||
274 | |||
266 | ucp[0] = 0; | 275 | ucp[0] = 0; |
267 | ucp[1] = 0xa; | 276 | ucp[1] = 0xa; |
268 | ucp[2] = 0x80; /* Valid bit */ | 277 | ucp[2] = 0x80; /* Valid bit */ |
@@ -272,5 +281,7 @@ void scsi_set_sense_information(u8 *buf, u64 info) | |||
272 | buf[0] |= 0x80; | 281 | buf[0] |= 0x80; |
273 | put_unaligned_be64(info, &buf[3]); | 282 | put_unaligned_be64(info, &buf[3]); |
274 | } | 283 | } |
284 | |||
285 | return 0; | ||
275 | } | 286 | } |
276 | EXPORT_SYMBOL(scsi_set_sense_information); | 287 | EXPORT_SYMBOL(scsi_set_sense_information); |
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 2bece607ca0f..7fb031bbcc8d 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c | |||
@@ -2729,7 +2729,7 @@ static const struct sense_info sense_info_table[] = { | |||
2729 | }, | 2729 | }, |
2730 | }; | 2730 | }; |
2731 | 2731 | ||
2732 | static void translate_sense_reason(struct se_cmd *cmd, sense_reason_t reason) | 2732 | static int translate_sense_reason(struct se_cmd *cmd, sense_reason_t reason) |
2733 | { | 2733 | { |
2734 | const struct sense_info *si; | 2734 | const struct sense_info *si; |
2735 | u8 *buffer = cmd->sense_buffer; | 2735 | u8 *buffer = cmd->sense_buffer; |
@@ -2756,7 +2756,11 @@ static void translate_sense_reason(struct se_cmd *cmd, sense_reason_t reason) | |||
2756 | 2756 | ||
2757 | scsi_build_sense_buffer(0, buffer, si->key, asc, ascq); | 2757 | scsi_build_sense_buffer(0, buffer, si->key, asc, ascq); |
2758 | if (si->add_sector_info) | 2758 | if (si->add_sector_info) |
2759 | scsi_set_sense_information(buffer, cmd->bad_sector); | 2759 | return scsi_set_sense_information(buffer, |
2760 | cmd->scsi_sense_length, | ||
2761 | cmd->bad_sector); | ||
2762 | |||
2763 | return 0; | ||
2760 | } | 2764 | } |
2761 | 2765 | ||
2762 | int | 2766 | int |
@@ -2774,10 +2778,14 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, | |||
2774 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); | 2778 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); |
2775 | 2779 | ||
2776 | if (!from_transport) { | 2780 | if (!from_transport) { |
2781 | int rc; | ||
2782 | |||
2777 | cmd->se_cmd_flags |= SCF_EMULATED_TASK_SENSE; | 2783 | cmd->se_cmd_flags |= SCF_EMULATED_TASK_SENSE; |
2778 | translate_sense_reason(cmd, reason); | ||
2779 | cmd->scsi_status = SAM_STAT_CHECK_CONDITION; | 2784 | cmd->scsi_status = SAM_STAT_CHECK_CONDITION; |
2780 | cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER; | 2785 | cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER; |
2786 | rc = translate_sense_reason(cmd, reason); | ||
2787 | if (rc) | ||
2788 | return rc; | ||
2781 | } | 2789 | } |
2782 | 2790 | ||
2783 | trace_target_cmd_complete(cmd); | 2791 | trace_target_cmd_complete(cmd); |
diff --git a/include/scsi/scsi_common.h b/include/scsi/scsi_common.h index 156d673db900..11571b2a831e 100644 --- a/include/scsi/scsi_common.h +++ b/include/scsi/scsi_common.h | |||
@@ -62,7 +62,7 @@ extern bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len, | |||
62 | struct scsi_sense_hdr *sshdr); | 62 | struct scsi_sense_hdr *sshdr); |
63 | 63 | ||
64 | extern void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq); | 64 | extern void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq); |
65 | extern void scsi_set_sense_information(u8 *buf, u64 info); | 65 | int scsi_set_sense_information(u8 *buf, int buf_len, u64 info); |
66 | extern const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len, | 66 | extern const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len, |
67 | int desc_type); | 67 | int desc_type); |
68 | 68 | ||