diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h
index a763edd05fe4..10101f50b282 100644
--- a/drivers/scsi/esas2r/esas2r.h
+++ b/drivers/scsi/esas2r/esas2r.h
@@ -967,7 +967,7 @@ int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg);
int esas2r_ioctl(struct scsi_device *dev, unsigned int cmd, void __user *arg);
u8 handle_hba_ioctl(struct esas2r_adapter *a,
- struct atto_ioctl *ioctl_hba);
+ struct atto_ioctl *ioctl_hba, size_t room);
enum scsi_qc_status esas2r_queuecommand(struct Scsi_Host *host,
struct scsi_cmnd *cmd);
int esas2r_show_info(struct seq_file *m, struct Scsi_Host *sh);
diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c b/drivers/scsi/esas2r/esas2r_ioctl.c
index 3f7c1d131ec3..f99318f61c93 100644
--- a/drivers/scsi/esas2r/esas2r_ioctl.c
+++ b/drivers/scsi/esas2r/esas2r_ioctl.c
@@ -311,17 +311,27 @@ static int smp_ioctl_callback(struct esas2r_adapter *a,
return true;
}
+/* The room from @member to the end of a struct atto_express_ioctl. */
+#define EXPRESS_IOCTL_ROOM(member) \
+ (sizeof(struct atto_express_ioctl) - \
+ offsetof(struct atto_express_ioctl, member))
+
static u8 handle_smp_ioctl(struct esas2r_adapter *a, struct atto_ioctl_smp *si)
{
struct esas2r_buffered_ioctl bi;
+ u64 length = (u64)sizeof(struct atto_ioctl_smp) +
+ le32_to_cpu(si->req_length) +
+ le32_to_cpu(si->rsp_length);
+
+ /* The request and the response follow @si in the same buffer. */
+ if (length > EXPRESS_IOCTL_ROOM(data.ioctl_smp))
+ return IOCTL_INVALID_PARAM;
memset(&bi, 0, sizeof(bi));
bi.a = a;
bi.ioctl = si;
- bi.length = sizeof(struct atto_ioctl_smp)
- + le32_to_cpu(si->req_length)
- + le32_to_cpu(si->rsp_length);
+ bi.length = length;
bi.offset = 0;
bi.callback = smp_ioctl_callback;
return handle_buffered_ioctl(&bi);@@ -1221,11 +1231,16 @@ static void hba_ioctl_done_callback(struct esas2r_adapter *a,
}
}
+/* @room is the size of the buffer that starts with @ioctl_hba. */
u8 handle_hba_ioctl(struct esas2r_adapter *a,
- struct atto_ioctl *ioctl_hba)
+ struct atto_ioctl *ioctl_hba, size_t room)
{
struct esas2r_buffered_ioctl bi;
+ /* The data follows @ioctl_hba in the same buffer. */
+ if (ioctl_hba->data_length > room - sizeof(struct atto_ioctl))
+ return IOCTL_INVALID_PARAM;
+
memset(&bi, 0, sizeof(bi));
bi.a = a;@@ -1349,31 +1364,27 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
err = esas2r_write_fw(a,
(char *)ioctl->data.fwrw.image,
0,
- sizeof(struct
- atto_express_ioctl));
+ EXPRESS_IOCTL_ROOM(data.fwrw.image));
if (err >= 0) {
err = esas2r_read_fw(a,
(char *)ioctl->data.fwrw.
image,
0,
- sizeof(struct
- atto_express_ioctl));
+ EXPRESS_IOCTL_ROOM(data.fwrw.image));
}
} else if (ioctl->data.fwrw.img_type == FW_IMG_FS_API) {
err = esas2r_write_fs(a,
(char *)ioctl->data.fwrw.image,
0,
- sizeof(struct
- atto_express_ioctl));
+ EXPRESS_IOCTL_ROOM(data.fwrw.image));
if (err >= 0) {
err = esas2r_read_fs(a,
(char *)ioctl->data.fwrw.
image,
0,
- sizeof(struct
- atto_express_ioctl));
+ EXPRESS_IOCTL_ROOM(data.fwrw.image));
}
} else {
ioctl->header.return_code = IOCTL_BAD_FLASH_IMGTYPE;@@ -1441,9 +1452,9 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
break;
case EXPRESS_IOCTL_HBA:
- ioctl->header.return_code = handle_hba_ioctl(a,
- &ioctl->data.
- ioctl_hba);
+ ioctl->header.return_code =
+ handle_hba_ioctl(a, &ioctl->data.ioctl_hba,
+ EXPRESS_IOCTL_ROOM(data.ioctl_hba));
break;
case EXPRESS_IOCTL_VDA:
diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c
index ada278c24c51..75a70ae9441b 100644
--- a/drivers/scsi/esas2r/esas2r_main.c
+++ b/drivers/scsi/esas2r/esas2r_main.c
@@ -178,7 +178,8 @@ static ssize_t read_hw(struct file *file, struct kobject *kobj,
if (!a->local_atto_ioctl)
return -ENOMEM;
- if (handle_hba_ioctl(a, a->local_atto_ioctl) != IOCTL_SUCCESS)
+ if (handle_hba_ioctl(a, a->local_atto_ioctl,
+ sizeof(struct atto_ioctl)) != IOCTL_SUCCESS)
return -ENOMEM;
memcpy(buf, a->local_atto_ioctl, length);
--
2.53.0