Commit c6cd2e8d authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French

ksmbd: fix potencial out-of-bounds when buffer offset is invalid

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: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent a80a486d
...@@ -101,7 +101,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -101,7 +101,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); *len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength);
break; break;
case SMB2_TREE_CONNECT: 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); *len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength);
break; break;
case SMB2_CREATE: case SMB2_CREATE:
...@@ -110,7 +112,6 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -110,7 +112,6 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
max_t(unsigned short int, max_t(unsigned short int,
le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset), le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset),
offsetof(struct smb2_create_req, Buffer)); offsetof(struct smb2_create_req, Buffer));
unsigned short int name_len = unsigned short int name_len =
le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength); le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);
...@@ -131,11 +132,15 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -131,11 +132,15 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
break; break;
} }
case SMB2_QUERY_INFO: 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); *len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength);
break; break;
case SMB2_SET_INFO: 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); *len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength);
break; break;
case SMB2_READ: case SMB2_READ:
...@@ -145,7 +150,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -145,7 +150,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
case SMB2_WRITE: case SMB2_WRITE:
if (((struct smb2_write_req *)hdr)->DataOffset || if (((struct smb2_write_req *)hdr)->DataOffset ||
((struct smb2_write_req *)hdr)->Length) { ((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), le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset),
offsetof(struct smb2_write_req, Buffer)); offsetof(struct smb2_write_req, Buffer));
*len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length); *len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length);
...@@ -156,7 +161,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -156,7 +161,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
*len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength); *len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength);
break; break;
case SMB2_QUERY_DIRECTORY: 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); *len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength);
break; break;
case SMB2_LOCK: case SMB2_LOCK:
...@@ -171,7 +178,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len, ...@@ -171,7 +178,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
break; break;
} }
case SMB2_IOCTL: 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); *len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount);
break; break;
default: default:
......
...@@ -1927,7 +1927,7 @@ int smb2_tree_connect(struct ksmbd_work *work) ...@@ -1927,7 +1927,7 @@ int smb2_tree_connect(struct ksmbd_work *work)
WORK_BUFFERS(work, req, rsp); 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, le16_to_cpu(req->PathLength), true,
conn->local_nls); conn->local_nls);
if (IS_ERR(treename)) { if (IS_ERR(treename)) {
...@@ -2840,7 +2840,7 @@ int smb2_open(struct ksmbd_work *work) ...@@ -2840,7 +2840,7 @@ int smb2_open(struct ksmbd_work *work)
goto err_out2; 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), le16_to_cpu(req->NameLength),
work->conn->local_nls); work->conn->local_nls);
if (IS_ERR(name)) { if (IS_ERR(name)) {
...@@ -4305,7 +4305,7 @@ int smb2_query_dir(struct ksmbd_work *work) ...@@ -4305,7 +4305,7 @@ int smb2_query_dir(struct ksmbd_work *work)
} }
srch_flag = req->Flags; 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, le16_to_cpu(req->FileNameLength), 1,
conn->local_nls); conn->local_nls);
if (IS_ERR(srch_ptr)) { if (IS_ERR(srch_ptr)) {
...@@ -4565,7 +4565,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -4565,7 +4565,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
sizeof(struct smb2_ea_info_req)) sizeof(struct smb2_ea_info_req))
return -EINVAL; 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 { } else {
/* need to send all EAs, if no specific EA is requested*/ /* need to send all EAs, if no specific EA is requested*/
if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY) if (le32_to_cpu(req->Flags) & SL_RETURN_SINGLE_ENTRY)
...@@ -6211,6 +6212,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -6211,6 +6212,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
struct ksmbd_share_config *share) struct ksmbd_share_config *share)
{ {
unsigned int buf_len = le32_to_cpu(req->BufferLength); unsigned int buf_len = le32_to_cpu(req->BufferLength);
char *buffer = (char *)req + le16_to_cpu(req->BufferOffset);
switch (req->FileInfoClass) { switch (req->FileInfoClass) {
case FILE_BASIC_INFORMATION: case FILE_BASIC_INFORMATION:
...@@ -6218,7 +6220,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -6218,7 +6220,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
if (buf_len < sizeof(struct smb2_file_basic_info)) if (buf_len < sizeof(struct smb2_file_basic_info))
return -EINVAL; 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: case FILE_ALLOCATION_INFORMATION:
{ {
...@@ -6226,7 +6228,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -6226,7 +6228,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL; return -EINVAL;
return set_file_allocation_info(work, fp, 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: case FILE_END_OF_FILE_INFORMATION:
{ {
...@@ -6234,7 +6236,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -6234,7 +6236,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL; return -EINVAL;
return set_end_of_file_info(work, fp, 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: case FILE_RENAME_INFORMATION:
{ {
...@@ -6242,7 +6244,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -6242,7 +6244,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL; return -EINVAL;
return set_rename_info(work, fp, return set_rename_info(work, fp,
(struct smb2_file_rename_info *)req->Buffer, (struct smb2_file_rename_info *)buffer,
buf_len); buf_len);
} }
case FILE_LINK_INFORMATION: case FILE_LINK_INFORMATION:
...@@ -6251,7 +6253,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -6251,7 +6253,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL; return -EINVAL;
return smb2_create_link(work, work->tcon->share_conf, 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, buf_len, fp->filp,
work->conn->local_nls); work->conn->local_nls);
} }
...@@ -6261,7 +6263,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -6261,7 +6263,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
return -EINVAL; return -EINVAL;
return set_file_disposition_info(fp, return set_file_disposition_info(fp,
(struct smb2_file_disposition_info *)req->Buffer); (struct smb2_file_disposition_info *)buffer);
} }
case FILE_FULL_EA_INFORMATION: case FILE_FULL_EA_INFORMATION:
{ {
...@@ -6274,7 +6276,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -6274,7 +6276,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
if (buf_len < sizeof(struct smb2_ea_info)) if (buf_len < sizeof(struct smb2_ea_info))
return -EINVAL; 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); buf_len, &fp->filp->f_path, true);
} }
case FILE_POSITION_INFORMATION: case FILE_POSITION_INFORMATION:
...@@ -6282,14 +6284,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, ...@@ -6282,14 +6284,14 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp,
if (buf_len < sizeof(struct smb2_file_pos_info)) if (buf_len < sizeof(struct smb2_file_pos_info))
return -EINVAL; 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: case FILE_MODE_INFORMATION:
{ {
if (buf_len < sizeof(struct smb2_file_mode_info)) if (buf_len < sizeof(struct smb2_file_mode_info))
return -EINVAL; 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);
} }
} }
...@@ -6370,7 +6372,7 @@ int smb2_set_info(struct ksmbd_work *work) ...@@ -6370,7 +6372,7 @@ int smb2_set_info(struct ksmbd_work *work)
} }
rc = smb2_set_info_sec(fp, rc = smb2_set_info_sec(fp,
le32_to_cpu(req->AdditionalInformation), le32_to_cpu(req->AdditionalInformation),
req->Buffer, (char *)req + le16_to_cpu(req->BufferOffset),
le32_to_cpu(req->BufferLength)); le32_to_cpu(req->BufferLength));
ksmbd_revert_fsids(work); ksmbd_revert_fsids(work);
break; break;
...@@ -7816,7 +7818,7 @@ static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id, ...@@ -7816,7 +7818,7 @@ static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id,
struct smb2_ioctl_rsp *rsp) struct smb2_ioctl_rsp *rsp)
{ {
struct ksmbd_rpc_command *rpc_resp; 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; int nbytes = 0;
rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf, rpc_resp = ksmbd_rpc_ioctl(work->sess, id, data_buf,
...@@ -7929,6 +7931,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7929,6 +7931,7 @@ int smb2_ioctl(struct ksmbd_work *work)
u64 id = KSMBD_NO_FID; u64 id = KSMBD_NO_FID;
struct ksmbd_conn *conn = work->conn; struct ksmbd_conn *conn = work->conn;
int ret = 0; int ret = 0;
char *buffer;
if (work->next_smb2_rcv_hdr_off) { if (work->next_smb2_rcv_hdr_off) {
req = ksmbd_req_buf_next(work); req = ksmbd_req_buf_next(work);
...@@ -7951,6 +7954,8 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -7951,6 +7954,8 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out; goto out;
} }
buffer = (char *)req + le32_to_cpu(req->InputOffset);
cnt_code = le32_to_cpu(req->CtlCode); cnt_code = le32_to_cpu(req->CtlCode);
ret = smb2_calc_max_out_buf_len(work, 48, ret = smb2_calc_max_out_buf_len(work, 48,
le32_to_cpu(req->MaxOutputResponse)); le32_to_cpu(req->MaxOutputResponse));
...@@ -8008,7 +8013,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -8008,7 +8013,7 @@ int smb2_ioctl(struct ksmbd_work *work)
} }
ret = fsctl_validate_negotiate_info(conn, 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], (struct validate_negotiate_info_rsp *)&rsp->Buffer[0],
in_buf_len); in_buf_len);
if (ret < 0) if (ret < 0)
...@@ -8061,7 +8066,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -8061,7 +8066,7 @@ int smb2_ioctl(struct ksmbd_work *work)
rsp->VolatileFileId = req->VolatileFileId; rsp->VolatileFileId = req->VolatileFileId;
rsp->PersistentFileId = req->PersistentFileId; rsp->PersistentFileId = req->PersistentFileId;
fsctl_copychunk(work, fsctl_copychunk(work,
(struct copychunk_ioctl_req *)&req->Buffer[0], (struct copychunk_ioctl_req *)buffer,
le32_to_cpu(req->CtlCode), le32_to_cpu(req->CtlCode),
le32_to_cpu(req->InputCount), le32_to_cpu(req->InputCount),
req->VolatileFileId, req->VolatileFileId,
...@@ -8074,8 +8079,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -8074,8 +8079,7 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out; goto out;
} }
ret = fsctl_set_sparse(work, id, ret = fsctl_set_sparse(work, id, (struct file_sparse *)buffer);
(struct file_sparse *)&req->Buffer[0]);
if (ret < 0) if (ret < 0)
goto out; goto out;
break; break;
...@@ -8098,7 +8102,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -8098,7 +8102,7 @@ int smb2_ioctl(struct ksmbd_work *work)
} }
zero_data = zero_data =
(struct file_zero_data_information *)&req->Buffer[0]; (struct file_zero_data_information *)buffer;
off = le64_to_cpu(zero_data->FileOffset); off = le64_to_cpu(zero_data->FileOffset);
bfz = le64_to_cpu(zero_data->BeyondFinalZero); bfz = le64_to_cpu(zero_data->BeyondFinalZero);
...@@ -8129,7 +8133,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -8129,7 +8133,7 @@ int smb2_ioctl(struct ksmbd_work *work)
} }
ret = fsctl_query_allocated_ranges(work, id, 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], (struct file_allocated_range_buffer *)&rsp->Buffer[0],
out_buf_len / out_buf_len /
sizeof(struct file_allocated_range_buffer), &nbytes); sizeof(struct file_allocated_range_buffer), &nbytes);
...@@ -8173,7 +8177,7 @@ int smb2_ioctl(struct ksmbd_work *work) ...@@ -8173,7 +8177,7 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out; 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, fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle,
dup_ext->PersistentFileHandle); dup_ext->PersistentFileHandle);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment