mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-14 21:29:37 +02:00
ksmbd: fix potencial out-of-bounds when buffer offset is invalid
[ Upstream commit c6cd2e8d2d
]
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: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
9e4937cbc1
commit
39bdc4197a
|
@ -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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
*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,
|
||||||
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:
|
||||||
|
|
|
@ -1931,7 +1931,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)) {
|
||||||
|
@ -2844,7 +2844,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)) {
|
||||||
|
@ -4309,7 +4309,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)) {
|
||||||
|
@ -4569,7 +4569,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)
|
||||||
|
@ -6216,6 +6217,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:
|
||||||
|
@ -6223,7 +6225,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:
|
||||||
{
|
{
|
||||||
|
@ -6231,7 +6233,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:
|
||||||
{
|
{
|
||||||
|
@ -6239,7 +6241,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:
|
||||||
{
|
{
|
||||||
|
@ -6247,7 +6249,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:
|
||||||
|
@ -6256,7 +6258,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);
|
||||||
}
|
}
|
||||||
|
@ -6266,7 +6268,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:
|
||||||
{
|
{
|
||||||
|
@ -6279,7 +6281,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:
|
||||||
|
@ -6287,14 +6289,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6375,7 +6377,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;
|
||||||
|
@ -7821,7 +7823,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,
|
||||||
|
@ -7934,6 +7936,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);
|
||||||
|
@ -7956,6 +7959,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));
|
||||||
|
@ -8013,7 +8018,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)
|
||||||
|
@ -8066,7 +8071,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,
|
||||||
|
@ -8079,8 +8084,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;
|
||||||
|
@ -8103,7 +8107,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);
|
||||||
|
@ -8134,7 +8138,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);
|
||||||
|
@ -8178,7 +8182,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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user