]> Git Repo - linux.git/blobdiff - fs/cifs/smb2ops.c
cifs: fix NULL ptr dereference in smb2_ioctl_query_info()
[linux.git] / fs / cifs / smb2ops.c
index 88ebb1a2dca4051a4e0e2db7f545d05f50830c37..db23f5b404bad9706294f6c1279b015250fd7a16 100644 (file)
@@ -1637,6 +1637,7 @@ smb2_ioctl_query_info(const unsigned int xid,
        unsigned int size[2];
        void *data[2];
        int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR;
+       void (*free_req1_func)(struct smb_rqst *r);
 
        vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
        if (vars == NULL)
@@ -1646,17 +1647,18 @@ smb2_ioctl_query_info(const unsigned int xid,
 
        resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
 
-       if (copy_from_user(&qi, arg, sizeof(struct smb_query_info)))
-               goto e_fault;
-
+       if (copy_from_user(&qi, arg, sizeof(struct smb_query_info))) {
+               rc = -EFAULT;
+               goto free_vars;
+       }
        if (qi.output_buffer_length > 1024) {
-               kfree(vars);
-               return -EINVAL;
+               rc = -EINVAL;
+               goto free_vars;
        }
 
        if (!ses || !server) {
-               kfree(vars);
-               return -EIO;
+               rc = -EIO;
+               goto free_vars;
        }
 
        if (smb3_encryption_required(tcon))
@@ -1665,8 +1667,8 @@ smb2_ioctl_query_info(const unsigned int xid,
        if (qi.output_buffer_length) {
                buffer = memdup_user(arg + sizeof(struct smb_query_info), qi.output_buffer_length);
                if (IS_ERR(buffer)) {
-                       kfree(vars);
-                       return PTR_ERR(buffer);
+                       rc = PTR_ERR(buffer);
+                       goto free_vars;
                }
        }
 
@@ -1705,48 +1707,45 @@ smb2_ioctl_query_info(const unsigned int xid,
        rc = SMB2_open_init(tcon, server,
                            &rqst[0], &oplock, &oparms, path);
        if (rc)
-               goto iqinf_exit;
+               goto free_output_buffer;
        smb2_set_next_command(tcon, &rqst[0]);
 
        /* Query */
        if (qi.flags & PASSTHRU_FSCTL) {
                /* Can eventually relax perm check since server enforces too */
-               if (!capable(CAP_SYS_ADMIN))
+               if (!capable(CAP_SYS_ADMIN)) {
                        rc = -EPERM;
-               else  {
-                       rqst[1].rq_iov = &vars->io_iov[0];
-                       rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
-
-                       rc = SMB2_ioctl_init(tcon, server,
-                                            &rqst[1],
-                                            COMPOUND_FID, COMPOUND_FID,
-                                            qi.info_type, true, buffer,
-                                            qi.output_buffer_length,
-                                            CIFSMaxBufSize -
-                                            MAX_SMB2_CREATE_RESPONSE_SIZE -
-                                            MAX_SMB2_CLOSE_RESPONSE_SIZE);
+                       goto free_open_req;
                }
+               rqst[1].rq_iov = &vars->io_iov[0];
+               rqst[1].rq_nvec = SMB2_IOCTL_IOV_SIZE;
+
+               rc = SMB2_ioctl_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID,
+                                    qi.info_type, true, buffer, qi.output_buffer_length,
+                                    CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE -
+                                    MAX_SMB2_CLOSE_RESPONSE_SIZE);
+               free_req1_func = SMB2_ioctl_free;
        } else if (qi.flags == PASSTHRU_SET_INFO) {
                /* Can eventually relax perm check since server enforces too */
-               if (!capable(CAP_SYS_ADMIN))
+               if (!capable(CAP_SYS_ADMIN)) {
                        rc = -EPERM;
-               else if (qi.output_buffer_length < 8)
+                       goto free_open_req;
+               }
+               if (qi.output_buffer_length < 8) {
                        rc = -EINVAL;
-               else {
-                       rqst[1].rq_iov = &vars->si_iov[0];
-                       rqst[1].rq_nvec = 1;
-
-                       /* MS-FSCC 2.4.13 FileEndOfFileInformation */
-                       size[0] = 8;
-                       data[0] = buffer;
-
-                       rc = SMB2_set_info_init(tcon, server,
-                                       &rqst[1],
-                                       COMPOUND_FID, COMPOUND_FID,
-                                       current->tgid,
-                                       FILE_END_OF_FILE_INFORMATION,
-                                       SMB2_O_INFO_FILE, 0, data, size);
+                       goto free_open_req;
                }
+               rqst[1].rq_iov = &vars->si_iov[0];
+               rqst[1].rq_nvec = 1;
+
+               /* MS-FSCC 2.4.13 FileEndOfFileInformation */
+               size[0] = 8;
+               data[0] = buffer;
+
+               rc = SMB2_set_info_init(tcon, server, &rqst[1], COMPOUND_FID, COMPOUND_FID,
+                                       current->tgid, FILE_END_OF_FILE_INFORMATION,
+                                       SMB2_O_INFO_FILE, 0, data, size);
+               free_req1_func = SMB2_set_info_free;
        } else if (qi.flags == PASSTHRU_QUERY_INFO) {
                rqst[1].rq_iov = &vars->qi_iov[0];
                rqst[1].rq_nvec = 1;
@@ -1757,6 +1756,7 @@ smb2_ioctl_query_info(const unsigned int xid,
                                  qi.info_type, qi.additional_information,
                                  qi.input_buffer_length,
                                  qi.output_buffer_length, buffer);
+               free_req1_func = SMB2_query_info_free;
        } else { /* unknown flags */
                cifs_tcon_dbg(VFS, "Invalid passthru query flags: 0x%x\n",
                              qi.flags);
@@ -1764,7 +1764,7 @@ smb2_ioctl_query_info(const unsigned int xid,
        }
 
        if (rc)
-               goto iqinf_exit;
+               goto free_open_req;
        smb2_set_next_command(tcon, &rqst[1]);
        smb2_set_related(&rqst[1]);
 
@@ -1775,14 +1775,14 @@ smb2_ioctl_query_info(const unsigned int xid,
        rc = SMB2_close_init(tcon, server,
                             &rqst[2], COMPOUND_FID, COMPOUND_FID, false);
        if (rc)
-               goto iqinf_exit;
+               goto free_req_1;
        smb2_set_related(&rqst[2]);
 
        rc = compound_send_recv(xid, ses, server,
                                flags, 3, rqst,
                                resp_buftype, rsp_iov);
        if (rc)
-               goto iqinf_exit;
+               goto out;
 
        /* No need to bump num_remote_opens since handle immediately closed */
        if (qi.flags & PASSTHRU_FSCTL) {
@@ -1792,18 +1792,22 @@ smb2_ioctl_query_info(const unsigned int xid,
                        qi.input_buffer_length = le32_to_cpu(io_rsp->OutputCount);
                if (qi.input_buffer_length > 0 &&
                    le32_to_cpu(io_rsp->OutputOffset) + qi.input_buffer_length
-                   > rsp_iov[1].iov_len)
-                       goto e_fault;
+                   > rsp_iov[1].iov_len) {
+                       rc = -EFAULT;
+                       goto out;
+               }
 
                if (copy_to_user(&pqi->input_buffer_length,
                                 &qi.input_buffer_length,
-                                sizeof(qi.input_buffer_length)))
-                       goto e_fault;
+                                sizeof(qi.input_buffer_length))) {
+                       rc = -EFAULT;
+                       goto out;
+               }
 
                if (copy_to_user((void __user *)pqi + sizeof(struct smb_query_info),
                                 (const void *)io_rsp + le32_to_cpu(io_rsp->OutputOffset),
                                 qi.input_buffer_length))
-                       goto e_fault;
+                       rc = -EFAULT;
        } else {
                pqi = (struct smb_query_info __user *)arg;
                qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
@@ -1811,28 +1815,30 @@ smb2_ioctl_query_info(const unsigned int xid,
                        qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength);
                if (copy_to_user(&pqi->input_buffer_length,
                                 &qi.input_buffer_length,
-                                sizeof(qi.input_buffer_length)))
-                       goto e_fault;
+                                sizeof(qi.input_buffer_length))) {
+                       rc = -EFAULT;
+                       goto out;
+               }
 
                if (copy_to_user(pqi + 1, qi_rsp->Buffer,
                                 qi.input_buffer_length))
-                       goto e_fault;
+                       rc = -EFAULT;
        }
 
- iqinf_exit:
-       cifs_small_buf_release(rqst[0].rq_iov[0].iov_base);
-       cifs_small_buf_release(rqst[1].rq_iov[0].iov_base);
-       cifs_small_buf_release(rqst[2].rq_iov[0].iov_base);
+out:
        free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
        free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
        free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
-       kfree(vars);
+       SMB2_close_free(&rqst[2]);
+free_req_1:
+       free_req1_func(&rqst[1]);
+free_open_req:
+       SMB2_open_free(&rqst[0]);
+free_output_buffer:
        kfree(buffer);
+free_vars:
+       kfree(vars);
        return rc;
-
-e_fault:
-       rc = -EFAULT;
-       goto iqinf_exit;
 }
 
 static ssize_t
This page took 0.038809 seconds and 4 git commands to generate.