From: Namjae Jeon <linkinjeon@kernel.org>
[ Upstream commit c6cd2e8d2d9aa7ee35b1fa6a668e32a22a9753da ]
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>
Signed-off-by: Vamsi Krishna Brahmajosyula <vamsi-krishna.brahmajosyula@broadcom.com>
---
fs/smb/server/smb2misc.c | 23 +++++++++++++------
fs/smb/server/smb2pdu.c | 48 ++++++++++++++++++++++------------------
2 files changed, 42 insertions(+), 29 deletions(-)
diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c
index 7c872ffb4b0a..727cb49926ee 100644
--- a/fs/smb/server/smb2misc.c
+++ b/fs/smb/server/smb2misc.c
@@ -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);
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:
@@ -110,7 +112,6 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
max_t(unsigned short int,
le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset),
offsetof(struct smb2_create_req, Buffer));
-
unsigned short int name_len =
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;
}
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:
@@ -145,7 +150,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));
*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);
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:
@@ -171,7 +178,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:
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index 9b5847bf9b2a..7e068c4187a8 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -1961,7 +1961,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)) {
@@ -2723,7 +2723,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)) {
@@ -4096,7 +4096,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)) {
@@ -4357,7 +4357,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)
@@ -5971,6 +5972,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:
@@ -5978,7 +5980,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:
{
@@ -5986,7 +5988,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:
{
@@ -5994,7 +5996,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:
{
@@ -6002,7 +6004,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:
@@ -6011,7 +6013,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);
}
@@ -6021,7 +6023,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:
{
@@ -6034,7 +6036,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:
@@ -6042,14 +6044,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);
}
}
@@ -6130,7 +6132,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;
@@ -7576,7 +7578,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,
@@ -7689,6 +7691,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);
@@ -7711,6 +7714,8 @@ int smb2_ioctl(struct ksmbd_work *work)
goto out;
}
+ buffer = (char *)req + le32_to_cpu(req->InputOffset);
+
cnt_code = le32_to_cpu(req->CtlCode);
ret = smb2_calc_max_out_buf_len(work, 48,
le32_to_cpu(req->MaxOutputResponse));
@@ -7768,7 +7773,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)
@@ -7821,7 +7826,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->CtlCode),
le32_to_cpu(req->InputCount),
req->VolatileFileId,
@@ -7834,8 +7839,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;
@@ -7858,7 +7862,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);
@@ -7889,7 +7893,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);
@@ -7933,7 +7937,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);
--
2.39.4
© 2016 - 2024 Red Hat, Inc.