]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ksmbd: fix potencial out-of-bounds when buffer offset is invalid
authorNamjae Jeon <linkinjeon@kernel.org>
Mon, 18 Mar 2024 23:40:48 +0000 (08:40 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 2 May 2025 05:44:20 +0000 (07:44 +0200)
commit c6cd2e8d2d9aa7ee35b1fa6a668e32a22a9753da upstream.

I found potencial out-of-bounds when buffer offset fields of a few requests
is invalid. This patch set the minimum value of buffer offset field to
->Buffer offset to validate buffer length.

Cc: stable@vger.kernel.org
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Bin Lan <bin.lan.cn@windriver.com>
Signed-off-by: He Zhe <zhe.he@windriver.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ksmbd/smb2misc.c
fs/ksmbd/smb2pdu.c

index 4d1211bde19008585b98ba6d011124c5cc62f032..9e54ecc9d4adac075b496a2eabec7ed97a9b7c25 100644 (file)
@@ -102,7 +102,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
                *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength);
                break;
        case SMB2_TREE_CONNECT:
-               *off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset);
+               *off = max_t(unsigned short int,
+                            le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset),
+                            offsetof(struct smb2_tree_connect_req, Buffer));
                *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength);
                break;
        case SMB2_CREATE:
@@ -129,11 +131,15 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
                break;
        }
        case SMB2_QUERY_INFO:
-               *off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset);
+               *off = max_t(unsigned int,
+                            le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset),
+                            offsetof(struct smb2_query_info_req, Buffer));
                *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength);
                break;
        case SMB2_SET_INFO:
-               *off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset);
+               *off = max_t(unsigned int,
+                            le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset),
+                            offsetof(struct smb2_set_info_req, Buffer));
                *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength);
                break;
        case SMB2_READ:
@@ -143,7 +149,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
        case SMB2_WRITE:
                if (((struct smb2_write_req *)hdr)->DataOffset ||
                    ((struct smb2_write_req *)hdr)->Length) {
-                       *off = max_t(unsigned int,
+                       *off = max_t(unsigned short int,
                                     le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset),
                                     offsetof(struct smb2_write_req, Buffer) - 4);
                        *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length);
@@ -154,7 +160,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
                *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength);
                break;
        case SMB2_QUERY_DIRECTORY:
-               *off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset);
+               *off = max_t(unsigned short int,
+                            le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset),
+                            offsetof(struct smb2_query_directory_req, Buffer));
                *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength);
                break;
        case SMB2_LOCK:
@@ -169,7 +177,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
                break;
        }
        case SMB2_IOCTL:
-               *off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset);
+               *off = max_t(unsigned int,
+                            le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset),
+                            offsetof(struct smb2_ioctl_req, Buffer));
                *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount);
                break;
        default:
index 3dfe0acf21a5daf60d5d1fa1fd056787a018e996..38fa2c0e81a8f40084ea93b1c11ad498f6b67c1b 100644 (file)
@@ -1967,7 +1967,7 @@ int smb2_tree_connect(struct ksmbd_work *work)
 
        WORK_BUFFERS(work, req, rsp);
 
-       treename = smb_strndup_from_utf16(req->Buffer,
+       treename = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->PathOffset),
                                          le16_to_cpu(req->PathLength), true,
                                          conn->local_nls);
        if (IS_ERR(treename)) {
@@ -2714,7 +2714,7 @@ int smb2_open(struct ksmbd_work *work)
                        goto err_out2;
                }
 
-               name = smb2_get_name(req->Buffer,
+               name = smb2_get_name((char *)req + le16_to_cpu(req->NameOffset),
                                     le16_to_cpu(req->NameLength),
                                     work->conn->local_nls);
                if (IS_ERR(name)) {
@@ -4086,7 +4086,7 @@ int smb2_query_dir(struct ksmbd_work *work)
        }
 
        srch_flag = req->Flags;
-       srch_ptr = smb_strndup_from_utf16(req->Buffer,
+       srch_ptr = smb_strndup_from_utf16((char *)req + le16_to_cpu(req->FileNameOffset),
                                          le16_to_cpu(req->FileNameLength), 1,
                                          conn->local_nls);
        if (IS_ERR(srch_ptr)) {
@@ -4346,7 +4346,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
                    sizeof(struct smb2_ea_info_req))
                        return -EINVAL;
 
-               ea_req = (struct smb2_ea_info_req *)req->Buffer;
+               ea_req = (struct smb2_ea_info_req *)((char *)req +
+                                                    le16_to_cpu(req->InputBufferOffset));
        } else {
                /* need to send all EAs, if no specific EA is requested*/
                if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY)
@@ -5952,6 +5953,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
                              struct ksmbd_share_config *share)
 {
        unsigned int buf_len = le32_to_cpu(req->BufferLength);
+       char *buffer = (char *)req + le16_to_cpu(req->BufferOffset);
 
        switch (req->FileInfoClass) {
        case FILE_BASIC_INFORMATION:
@@ -5959,7 +5961,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
                if (buf_len < sizeof(struct smb2_file_basic_info))
                        return -EINVAL;
 
-               return set_file_basic_info(fp, (struct smb2_file_basic_info *)req->Buffer, share);
+               return set_file_basic_info(fp, (struct smb2_file_basic_info *)buffer, share);
        }
        case FILE_ALLOCATION_INFORMATION:
        {
@@ -5967,7 +5969,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
                        return -EINVAL;
 
                return set_file_allocation_info(work, fp,
-                                               (struct smb2_file_alloc_info *)req->Buffer);
+                                               (struct smb2_file_alloc_info *)buffer);
        }
        case FILE_END_OF_FILE_INFORMATION:
        {
@@ -5975,7 +5977,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
                        return -EINVAL;
 
                return set_end_of_file_info(work, fp,
-                                           (struct smb2_file_eof_info *)req->Buffer);
+                                           (struct smb2_file_eof_info *)buffer);
        }
        case FILE_RENAME_INFORMATION:
        {
@@ -5983,7 +5985,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
                        return -EINVAL;
 
                return set_rename_info(work, fp,
-                                      (struct smb2_file_rename_info *)req->Buffer,
+                                      (struct smb2_file_rename_info *)buffer,
                                       buf_len);
        }
        case FILE_LINK_INFORMATION:
@@ -5992,7 +5994,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
                        return -EINVAL;
 
                return smb2_create_link(work, work->tcon->share_conf,
-                                       (struct smb2_file_link_info *)req->Buffer,
+                                       (struct smb2_file_link_info *)buffer,
                                        buf_len, fp->filp,
                                        work->conn->local_nls);
        }
@@ -6002,7 +6004,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
                        return -EINVAL;
 
                return set_file_disposition_info(fp,
-                                                (struct smb2_file_disposition_info *)req->Buffer);
+                                                (struct smb2_file_disposition_info *)buffer);
        }
        case FILE_FULL_EA_INFORMATION:
        {
@@ -6015,7 +6017,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
                if (buf_len < sizeof(struct smb2_ea_info))
                        return -EINVAL;
 
-               return smb2_set_ea((struct smb2_ea_info *)req->Buffer,
+               return smb2_set_ea((struct smb2_ea_info *)buffer,
                                   buf_len, &fp->filp->f_path, true);
        }
        case FILE_POSITION_INFORMATION:
@@ -6023,14 +6025,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
                if (buf_len < sizeof(struct smb2_file_pos_info))
                        return -EINVAL;
 
-               return set_file_position_info(fp, (struct smb2_file_pos_info *)req->Buffer);
+               return set_file_position_info(fp, (struct smb2_file_pos_info *)buffer);
        }
        case FILE_MODE_INFORMATION:
        {
                if (buf_len < sizeof(struct smb2_file_mode_info))
                        return -EINVAL;
 
-               return set_file_mode_info(fp, (struct smb2_file_mode_info *)req->Buffer);
+               return set_file_mode_info(fp, (struct smb2_file_mode_info *)buffer);
        }
        }
 
@@ -6111,7 +6113,7 @@ int smb2_set_info(struct ksmbd_work *work)
                }
                rc = smb2_set_info_sec(fp,
                                       le32_to_cpu(req->AdditionalInformation),
-                                      req->Buffer,
+                                      (char *)req + le16_to_cpu(req->BufferOffset),
                                       le32_to_cpu(req->BufferLength));
                ksmbd_revert_fsids(work);
                break;
@@ -7563,7 +7565,7 @@ static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id,
                                 struct smb2_ioctl_rsp *rsp)
 {
        struct ksmbd_rpc_command *rpc_resp;
-       char *data_buf = (char *)&req->Buffer[0];
+       char *data_buf = (char *)req + le32_to_cpu(req->InputOffset);
        int nbytes = 0;
 
        rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf,
@@ -7676,6 +7678,7 @@ int smb2_ioctl(struct ksmbd_work *work)
        u64 id = KSMBD_NO_FID;
        struct ksmbd_conn *conn = work->conn;
        int ret = 0;
+       char *buffer;
 
        if (work->next_smb2_rcv_hdr_off) {
                req = ksmbd_req_buf_next(work);
@@ -7698,6 +7701,7 @@ int smb2_ioctl(struct ksmbd_work *work)
                goto out;
        }
 
+       buffer = (char *)req + le32_to_cpu(req->InputOffset);
        cnt_code = le32_to_cpu(req->CntCode);
        ret = smb2_calc_max_out_buf_len(work, 48,
                                        le32_to_cpu(req->MaxOutputResponse));
@@ -7755,7 +7759,7 @@ int smb2_ioctl(struct ksmbd_work *work)
                }
 
                ret = fsctl_validate_negotiate_info(conn,
-                       (struct validate_negotiate_info_req *)&req->Buffer[0],
+                       (struct validate_negotiate_info_req *)buffer,
                        (struct validate_negotiate_info_rsp *)&rsp->Buffer[0],
                        in_buf_len);
                if (ret < 0)
@@ -7808,7 +7812,7 @@ int smb2_ioctl(struct ksmbd_work *work)
                rsp->VolatileFileId = req->VolatileFileId;
                rsp->PersistentFileId = req->PersistentFileId;
                fsctl_copychunk(work,
-                               (struct copychunk_ioctl_req *)&req->Buffer[0],
+                               (struct copychunk_ioctl_req *)buffer,
                                le32_to_cpu(req->CntCode),
                                le32_to_cpu(req->InputCount),
                                req->VolatileFileId,
@@ -7821,8 +7825,7 @@ int smb2_ioctl(struct ksmbd_work *work)
                        goto out;
                }
 
-               ret = fsctl_set_sparse(work, id,
-                                      (struct file_sparse *)&req->Buffer[0]);
+               ret = fsctl_set_sparse(work, id, (struct file_sparse *)buffer);
                if (ret < 0)
                        goto out;
                break;
@@ -7845,7 +7848,7 @@ int smb2_ioctl(struct ksmbd_work *work)
                }
 
                zero_data =
-                       (struct file_zero_data_information *)&req->Buffer[0];
+                       (struct file_zero_data_information *)buffer;
 
                off = le64_to_cpu(zero_data->FileOffset);
                bfz = le64_to_cpu(zero_data->BeyondFinalZero);
@@ -7876,7 +7879,7 @@ int smb2_ioctl(struct ksmbd_work *work)
                }
 
                ret = fsctl_query_allocated_ranges(work, id,
-                       (struct file_allocated_range_buffer *)&req->Buffer[0],
+                       (struct file_allocated_range_buffer *)buffer,
                        (struct file_allocated_range_buffer *)&rsp->Buffer[0],
                        out_buf_len /
                        sizeof(struct file_allocated_range_buffer), &nbytes);
@@ -7920,7 +7923,7 @@ int smb2_ioctl(struct ksmbd_work *work)
                        goto out;
                }
 
-               dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0];
+               dup_ext = (struct duplicate_extents_to_file *)buffer;
 
                fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle,
                                             dup_ext->PersistentFileHandle);