Move the work of esas2r_ioctl_handler() into esas2r_ioctl_buf() which
works on a kernel copy of struct atto_express_ioctl. The SCSI ioctl and
the character device keep copying in and out in
esas2r_ioctl_handler().

The procfs file implements ->proc_ioctl_buf() with esas2r_ioctl_buf()
and lists every command from EXPRESS_IOCTL_MIN to EXPRESS_IOCTL_MAX, so
procfs copies the struct in before it pins the entry and back out after
unpinning it. Commands outside of that range fail with -ENOTTY instead
of -ENOTSUPP now, a NULL argument with -EFAULT.

Signed-off-by: Christian Brauner (Amutable) <[email protected]>
---
 drivers/scsi/esas2r/esas2r.h       |  2 ++
 drivers/scsi/esas2r/esas2r_ioctl.c | 58 ++++++++++++++++++++++----------------
 drivers/scsi/esas2r/esas2r_main.c  | 44 +++++++++++++++++++++++------
 3 files changed, 72 insertions(+), 32 deletions(-)

diff --git a/drivers/scsi/esas2r/esas2r.h b/drivers/scsi/esas2r/esas2r.h
index 10101f50b282..4d75b8adbb3b 100644
--- a/drivers/scsi/esas2r/esas2r.h
+++ b/drivers/scsi/esas2r/esas2r.h
@@ -964,6 +964,8 @@ struct esas2r_adapter {
 const char *esas2r_info(struct Scsi_Host *);
 int esas2r_write_params(struct esas2r_adapter *a, struct esas2r_request *rq,
                        struct esas2r_sas_nvram *data);
+int esas2r_ioctl_buf(void *hostdata, unsigned int cmd,
+                    struct atto_express_ioctl *ioctl);
 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,
diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c 
b/drivers/scsi/esas2r/esas2r_ioctl.c
index f99318f61c93..eb39ba8263b3 100644
--- a/drivers/scsi/esas2r/esas2r_ioctl.c
+++ b/drivers/scsi/esas2r/esas2r_ioctl.c
@@ -1279,37 +1279,25 @@ int esas2r_write_params(struct esas2r_adapter *a, 
struct esas2r_request *rq,
 }
 
 
-/* This function only cares about ATTO-specific ioctls (atto_express_ioctl) */
-int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
+/*
+ * This function only cares about ATTO-specific ioctls (atto_express_ioctl).
+ * @ioctl is a kernel copy of what the caller passed. It goes back to the
+ * caller unless this fails.
+ */
+int esas2r_ioctl_buf(void *hostdata, unsigned int cmd,
+                    struct atto_express_ioctl *ioctl)
 {
-       struct atto_express_ioctl *ioctl = NULL;
        struct esas2r_adapter *a;
        struct esas2r_request *rq;
        u16 code;
        int err;
 
-       esas2r_log(ESAS2R_LOG_DEBG, "ioctl (%p, %x, %p)", hostdata, cmd, arg);
-
-       if ((arg == NULL)
-           || (cmd < EXPRESS_IOCTL_MIN)
-           || (cmd > EXPRESS_IOCTL_MAX))
-               return -ENOTSUPP;
-
-       ioctl = memdup_user(arg, sizeof(struct atto_express_ioctl));
-       if (IS_ERR(ioctl)) {
-               esas2r_log(ESAS2R_LOG_WARN,
-                          "ioctl_handler access_ok failed for cmd %u, address 
%p",
-                          cmd, arg);
-               return PTR_ERR(ioctl);
-       }
-
        /* verify the signature */
 
        if (memcmp(ioctl->header.signature,
                   EXPRESS_IOCTL_SIGNATURE,
                   EXPRESS_IOCTL_SIGNATURE_SIZE) != 0) {
                esas2r_log(ESAS2R_LOG_WARN, "invalid signature");
-               kfree(ioctl);
 
                return -ENOTSUPP;
        }
@@ -1350,7 +1338,6 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int 
cmd, void __user *arg)
                        esas2r_adapters[ioctl->header.channel] == NULL) {
                        ioctl->header.return_code = IOCTL_BAD_CHANNEL;
                        esas2r_log(ESAS2R_LOG_WARN, "bad channel value");
-                       kfree(ioctl);
 
                        return -ENOTSUPP;
                }
@@ -1403,7 +1390,6 @@ int esas2r_ioctl_handler(void *hostdata, unsigned int 
cmd, void __user *arg)
 
                rq = esas2r_alloc_request(a);
                if (rq == NULL) {
-                       kfree(ioctl);
                        esas2r_log(ESAS2R_LOG_WARN,
                           "could not allocate an internal request");
                        return -ENOMEM;
@@ -1515,19 +1501,43 @@ ioctl_done:
 
        }
 
+       return 0;
+}
+
+int esas2r_ioctl_handler(void *hostdata, unsigned int cmd, void __user *arg)
+{
+       struct atto_express_ioctl *ioctl __free(kfree) = NULL;
+       int err;
+
+       esas2r_log(ESAS2R_LOG_DEBG, "ioctl (%p, %x, %p)", hostdata, cmd, arg);
+
+       if ((arg == NULL)
+           || (cmd < EXPRESS_IOCTL_MIN)
+           || (cmd > EXPRESS_IOCTL_MAX))
+               return -ENOTSUPP;
+
+       ioctl = memdup_user(arg, sizeof(struct atto_express_ioctl));
+       if (IS_ERR(ioctl)) {
+               esas2r_log(ESAS2R_LOG_WARN,
+                          "ioctl_handler access_ok failed for cmd %u, address 
%p",
+                          cmd, arg);
+               return PTR_ERR(ioctl);
+       }
+
+       err = esas2r_ioctl_buf(hostdata, cmd, ioctl);
+       if (err)
+               return err;
+
        /* Always copy the buffer back, if only to pick up the status */
        err = copy_to_user(arg, ioctl, sizeof(struct atto_express_ioctl));
        if (err != 0) {
                esas2r_log(ESAS2R_LOG_WARN,
                           "ioctl_handler copy_to_user didn't copy everything 
(err %d, cmd %u)",
                           err, cmd);
-               kfree(ioctl);
 
                return -EFAULT;
        }
 
-       kfree(ioctl);
-
        return 0;
 }
 
diff --git a/drivers/scsi/esas2r/esas2r_main.c 
b/drivers/scsi/esas2r/esas2r_main.c
index 75a70ae9441b..720be2a0907d 100644
--- a/drivers/scsi/esas2r/esas2r_main.c
+++ b/drivers/scsi/esas2r/esas2r_main.c
@@ -613,14 +613,6 @@ static const struct file_operations esas2r_proc_fops = {
        .unlocked_ioctl = esas2r_proc_ioctl,
 };
 
-static const struct proc_ops esas2r_proc_ops = {
-       .proc_lseek             = default_llseek,
-       .proc_ioctl             = esas2r_proc_ioctl,
-#ifdef CONFIG_COMPAT
-       .proc_compat_ioctl      = compat_ptr_ioctl,
-#endif
-};
-
 static struct Scsi_Host *esas2r_proc_host;
 static int esas2r_proc_major;
 
@@ -630,6 +622,42 @@ long esas2r_proc_ioctl(struct file *fp, unsigned int cmd, 
unsigned long arg)
                                    cmd, (void __user *)arg);
 }
 
+static long esas2r_proc_ioctl_buf(struct file *fp, unsigned int cmd, void *arg)
+{
+       return esas2r_ioctl_buf(esas2r_proc_host->hostdata, cmd, arg);
+}
+
+#define ESAS2R_PROC_IOCTL(_cmd)                                                
\
+       PROC_IOCTL_ARG(_cmd, sizeof(struct atto_express_ioctl),         \
+                      sizeof(struct atto_express_ioctl))
+
+/* esas2r_ioctl_buf() takes every command from EXPRESS_IOCTL_MIN to _MAX. */
+static const struct proc_ioctl_arg esas2r_proc_ioctl_args[] = {
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_RW_FIRMWARE),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_READ_PARAMS),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_WRITE_PARAMS),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_FC_API),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_GET_CHANNELS),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_CHAN_INFO),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_DEFAULT_PARAMS),
+       ESAS2R_PROC_IOCTL(EXPRESS_ADDR_MEMORY),
+       ESAS2R_PROC_IOCTL(EXPRESS_RW_MEMORY),
+       ESAS2R_PROC_IOCTL(EXPRESS_TSDK_DUMP),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_SMP),
+       ESAS2R_PROC_IOCTL(EXPRESS_CSMI),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_HBA),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_VDA),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_GET_ID),
+       ESAS2R_PROC_IOCTL(EXPRESS_IOCTL_GET_MOD_INFO),
+};
+
+static const struct proc_ops esas2r_proc_ops = {
+       .proc_lseek             = default_llseek,
+       .proc_ioctl_buf         = esas2r_proc_ioctl_buf,
+       .proc_ioctl_args        = esas2r_proc_ioctl_args,
+       .proc_nr_ioctl_args     = ARRAY_SIZE(esas2r_proc_ioctl_args),
+};
+
 static void __exit esas2r_exit(void)
 {
        esas2r_log(ESAS2R_LOG_INFO, "%s called", __func__);

-- 
2.53.0


Reply via email to