Skip to content

Commit 34061d6

Browse files
hcleesmfrench
authored andcommitted
ksmbd: validate OutputBufferLength of QUERY_DIR, QUERY_INFO, IOCTL requests
Validate OutputBufferLength of QUERY_DIR, QUERY_INFO, IOCTL requests and check the free size of response buffer for these requests. Acked-by: Namjae Jeon <[email protected]> Signed-off-by: Hyunchul Lee <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 7a33488 commit 34061d6

File tree

1 file changed

+52
-16
lines changed

1 file changed

+52
-16
lines changed

fs/ksmbd/smb2pdu.c

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3762,6 +3762,24 @@ static int verify_info_level(int info_level)
37623762
return 0;
37633763
}
37643764

3765+
static int smb2_calc_max_out_buf_len(struct ksmbd_work *work,
3766+
unsigned short hdr2_len,
3767+
unsigned int out_buf_len)
3768+
{
3769+
int free_len;
3770+
3771+
if (out_buf_len > work->conn->vals->max_trans_size)
3772+
return -EINVAL;
3773+
3774+
free_len = (int)(work->response_sz -
3775+
(get_rfc1002_len(work->response_buf) + 4)) -
3776+
hdr2_len;
3777+
if (free_len < 0)
3778+
return -EINVAL;
3779+
3780+
return min_t(int, out_buf_len, free_len);
3781+
}
3782+
37653783
int smb2_query_dir(struct ksmbd_work *work)
37663784
{
37673785
struct ksmbd_conn *conn = work->conn;
@@ -3838,9 +3856,13 @@ int smb2_query_dir(struct ksmbd_work *work)
38383856
memset(&d_info, 0, sizeof(struct ksmbd_dir_info));
38393857
d_info.wptr = (char *)rsp->Buffer;
38403858
d_info.rptr = (char *)rsp->Buffer;
3841-
d_info.out_buf_len = (work->response_sz - (get_rfc1002_len(rsp_org) + 4));
3842-
d_info.out_buf_len = min_t(int, d_info.out_buf_len, le32_to_cpu(req->OutputBufferLength)) -
3843-
sizeof(struct smb2_query_directory_rsp);
3859+
d_info.out_buf_len =
3860+
smb2_calc_max_out_buf_len(work, 8,
3861+
le32_to_cpu(req->OutputBufferLength));
3862+
if (d_info.out_buf_len < 0) {
3863+
rc = -EINVAL;
3864+
goto err_out;
3865+
}
38443866
d_info.flags = srch_flag;
38453867

38463868
/*
@@ -4074,12 +4096,11 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
40744096
le32_to_cpu(req->Flags));
40754097
}
40764098

4077-
buf_free_len = work->response_sz -
4078-
(get_rfc1002_len(rsp_org) + 4) -
4079-
sizeof(struct smb2_query_info_rsp);
4080-
4081-
if (le32_to_cpu(req->OutputBufferLength) < buf_free_len)
4082-
buf_free_len = le32_to_cpu(req->OutputBufferLength);
4099+
buf_free_len =
4100+
smb2_calc_max_out_buf_len(work, 8,
4101+
le32_to_cpu(req->OutputBufferLength));
4102+
if (buf_free_len < 0)
4103+
return -EINVAL;
40834104

40844105
rc = ksmbd_vfs_listxattr(path->dentry, &xattr_list);
40854106
if (rc < 0) {
@@ -4390,6 +4411,8 @@ static void get_file_stream_info(struct ksmbd_work *work,
43904411
struct path *path = &fp->filp->f_path;
43914412
ssize_t xattr_list_len;
43924413
int nbytes = 0, streamlen, stream_name_len, next, idx = 0;
4414+
int buf_free_len;
4415+
struct smb2_query_info_req *req = ksmbd_req_buf_next(work);
43934416

43944417
generic_fillattr(file_mnt_user_ns(fp->filp), file_inode(fp->filp),
43954418
&stat);
@@ -4403,6 +4426,12 @@ static void get_file_stream_info(struct ksmbd_work *work,
44034426
goto out;
44044427
}
44054428

4429+
buf_free_len =
4430+
smb2_calc_max_out_buf_len(work, 8,
4431+
le32_to_cpu(req->OutputBufferLength));
4432+
if (buf_free_len < 0)
4433+
goto out;
4434+
44064435
while (idx < xattr_list_len) {
44074436
stream_name = xattr_list + idx;
44084437
streamlen = strlen(stream_name);
@@ -4427,6 +4456,10 @@ static void get_file_stream_info(struct ksmbd_work *work,
44274456
streamlen = snprintf(stream_buf, streamlen + 1,
44284457
":%s", &stream_name[XATTR_NAME_STREAM_LEN]);
44294458

4459+
next = sizeof(struct smb2_file_stream_info) + streamlen * 2;
4460+
if (next > buf_free_len)
4461+
break;
4462+
44304463
file_info = (struct smb2_file_stream_info *)&rsp->Buffer[nbytes];
44314464
streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName,
44324465
stream_buf, streamlen,
@@ -4437,12 +4470,13 @@ static void get_file_stream_info(struct ksmbd_work *work,
44374470
file_info->StreamSize = cpu_to_le64(stream_name_len);
44384471
file_info->StreamAllocationSize = cpu_to_le64(stream_name_len);
44394472

4440-
next = sizeof(struct smb2_file_stream_info) + streamlen;
44414473
nbytes += next;
4474+
buf_free_len -= next;
44424475
file_info->NextEntryOffset = cpu_to_le32(next);
44434476
}
44444477

4445-
if (!S_ISDIR(stat.mode)) {
4478+
if (!S_ISDIR(stat.mode) &&
4479+
buf_free_len >= sizeof(struct smb2_file_stream_info) + 7 * 2) {
44464480
file_info = (struct smb2_file_stream_info *)
44474481
&rsp->Buffer[nbytes];
44484482
streamlen = smbConvertToUTF16((__le16 *)file_info->StreamName,
@@ -7453,11 +7487,13 @@ int smb2_ioctl(struct ksmbd_work *work)
74537487
}
74547488

74557489
cnt_code = le32_to_cpu(req->CntCode);
7456-
out_buf_len = le32_to_cpu(req->MaxOutputResponse);
7457-
out_buf_len =
7458-
min_t(u32, work->response_sz - work->next_smb2_rsp_hdr_off -
7459-
(offsetof(struct smb2_ioctl_rsp, Buffer) - 4),
7460-
out_buf_len);
7490+
ret = smb2_calc_max_out_buf_len(work, 48,
7491+
le32_to_cpu(req->MaxOutputResponse));
7492+
if (ret < 0) {
7493+
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
7494+
goto out;
7495+
}
7496+
out_buf_len = (unsigned int)ret;
74617497
in_buf_len = le32_to_cpu(req->InputCount);
74627498

74637499
switch (cnt_code) {

0 commit comments

Comments
 (0)