Skip to content

Commit f7db8fd

Browse files
namjaejeonsmfrench
authored andcommitted
ksmbd: add validation in smb2_ioctl
Add validation for request/response buffer size check in smb2_ioctl and fsctl_copychunk() take copychunk_ioctl_req pointer and the other arguments instead of smb2_ioctl_req structure and remove an unused smb2_ioctl_req argument of fsctl_validate_negotiate_info. Cc: Tom Talpey <[email protected]> Cc: Ronnie Sahlberg <[email protected]> Cc: Ralph Böhme <[email protected]> Cc: Steve French <[email protected]> Cc: Sergey Senozhatsky <[email protected]> Acked-by: Hyunchul Lee <[email protected]> Signed-off-by: Colin Ian King <[email protected]> Signed-off-by: Namjae Jeon <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 64570fb commit f7db8fd

File tree

3 files changed

+86
-31
lines changed

3 files changed

+86
-31
lines changed

fs/ksmbd/smb2pdu.c

Lines changed: 84 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7023,24 +7023,26 @@ int smb2_lock(struct ksmbd_work *work)
70237023
return err;
70247024
}
70257025

7026-
static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req,
7026+
static int fsctl_copychunk(struct ksmbd_work *work,
7027+
struct copychunk_ioctl_req *ci_req,
7028+
unsigned int cnt_code,
7029+
unsigned int input_count,
7030+
unsigned long long volatile_id,
7031+
unsigned long long persistent_id,
70277032
struct smb2_ioctl_rsp *rsp)
70287033
{
7029-
struct copychunk_ioctl_req *ci_req;
70307034
struct copychunk_ioctl_rsp *ci_rsp;
70317035
struct ksmbd_file *src_fp = NULL, *dst_fp = NULL;
70327036
struct srv_copychunk *chunks;
70337037
unsigned int i, chunk_count, chunk_count_written = 0;
70347038
unsigned int chunk_size_written = 0;
70357039
loff_t total_size_written = 0;
7036-
int ret, cnt_code;
7040+
int ret = 0;
70377041

7038-
cnt_code = le32_to_cpu(req->CntCode);
7039-
ci_req = (struct copychunk_ioctl_req *)&req->Buffer[0];
70407042
ci_rsp = (struct copychunk_ioctl_rsp *)&rsp->Buffer[0];
70417043

7042-
rsp->VolatileFileId = req->VolatileFileId;
7043-
rsp->PersistentFileId = req->PersistentFileId;
7044+
rsp->VolatileFileId = cpu_to_le64(volatile_id);
7045+
rsp->PersistentFileId = cpu_to_le64(persistent_id);
70447046
ci_rsp->ChunksWritten =
70457047
cpu_to_le32(ksmbd_server_side_copy_max_chunk_count());
70467048
ci_rsp->ChunkBytesWritten =
@@ -7050,12 +7052,13 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req,
70507052

70517053
chunks = (struct srv_copychunk *)&ci_req->Chunks[0];
70527054
chunk_count = le32_to_cpu(ci_req->ChunkCount);
7055+
if (chunk_count == 0)
7056+
goto out;
70537057
total_size_written = 0;
70547058

70557059
/* verify the SRV_COPYCHUNK_COPY packet */
70567060
if (chunk_count > ksmbd_server_side_copy_max_chunk_count() ||
7057-
le32_to_cpu(req->InputCount) <
7058-
offsetof(struct copychunk_ioctl_req, Chunks) +
7061+
input_count < offsetof(struct copychunk_ioctl_req, Chunks) +
70597062
chunk_count * sizeof(struct srv_copychunk)) {
70607063
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
70617064
return -EINVAL;
@@ -7076,9 +7079,7 @@ static int fsctl_copychunk(struct ksmbd_work *work, struct smb2_ioctl_req *req,
70767079

70777080
src_fp = ksmbd_lookup_foreign_fd(work,
70787081
le64_to_cpu(ci_req->ResumeKey[0]));
7079-
dst_fp = ksmbd_lookup_fd_slow(work,
7080-
le64_to_cpu(req->VolatileFileId),
7081-
le64_to_cpu(req->PersistentFileId));
7082+
dst_fp = ksmbd_lookup_fd_slow(work, volatile_id, persistent_id);
70827083
ret = -EINVAL;
70837084
if (!src_fp ||
70847085
src_fp->persistent_id != le64_to_cpu(ci_req->ResumeKey[1])) {
@@ -7153,8 +7154,8 @@ static __be32 idev_ipv4_address(struct in_device *idev)
71537154
}
71547155

71557156
static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn,
7156-
struct smb2_ioctl_req *req,
7157-
struct smb2_ioctl_rsp *rsp)
7157+
struct smb2_ioctl_rsp *rsp,
7158+
unsigned int out_buf_len)
71587159
{
71597160
struct network_interface_info_ioctl_rsp *nii_rsp = NULL;
71607161
int nbytes = 0;
@@ -7166,6 +7167,12 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn,
71667167

71677168
rtnl_lock();
71687169
for_each_netdev(&init_net, netdev) {
7170+
if (out_buf_len <
7171+
nbytes + sizeof(struct network_interface_info_ioctl_rsp)) {
7172+
rtnl_unlock();
7173+
return -ENOSPC;
7174+
}
7175+
71697176
if (netdev->type == ARPHRD_LOOPBACK)
71707177
continue;
71717178

@@ -7245,23 +7252,23 @@ static int fsctl_query_iface_info_ioctl(struct ksmbd_conn *conn,
72457252
if (nii_rsp)
72467253
nii_rsp->Next = 0;
72477254

7248-
if (!nbytes) {
7249-
rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL;
7250-
return -EINVAL;
7251-
}
7252-
72537255
rsp->PersistentFileId = cpu_to_le64(SMB2_NO_FID);
72547256
rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID);
72557257
return nbytes;
72567258
}
72577259

72587260
static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn,
72597261
struct validate_negotiate_info_req *neg_req,
7260-
struct validate_negotiate_info_rsp *neg_rsp)
7262+
struct validate_negotiate_info_rsp *neg_rsp,
7263+
unsigned int in_buf_len)
72617264
{
72627265
int ret = 0;
72637266
int dialect;
72647267

7268+
if (in_buf_len < sizeof(struct validate_negotiate_info_req) +
7269+
le16_to_cpu(neg_req->DialectCount) * sizeof(__le16))
7270+
return -EINVAL;
7271+
72657272
dialect = ksmbd_lookup_dialect_by_id(neg_req->Dialects,
72667273
neg_req->DialectCount);
72677274
if (dialect == BAD_PROT_ID || dialect != conn->dialect) {
@@ -7295,7 +7302,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn,
72957302
static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id,
72967303
struct file_allocated_range_buffer *qar_req,
72977304
struct file_allocated_range_buffer *qar_rsp,
7298-
int in_count, int *out_count)
7305+
unsigned int in_count, unsigned int *out_count)
72997306
{
73007307
struct ksmbd_file *fp;
73017308
loff_t start, length;
@@ -7322,7 +7329,8 @@ static int fsctl_query_allocated_ranges(struct ksmbd_work *work, u64 id,
73227329
}
73237330

73247331
static int fsctl_pipe_transceive(struct ksmbd_work *work, u64 id,
7325-
int out_buf_len, struct smb2_ioctl_req *req,
7332+
unsigned int out_buf_len,
7333+
struct smb2_ioctl_req *req,
73267334
struct smb2_ioctl_rsp *rsp)
73277335
{
73287336
struct ksmbd_rpc_command *rpc_resp;
@@ -7436,8 +7444,7 @@ int smb2_ioctl(struct ksmbd_work *work)
74367444
{
74377445
struct smb2_ioctl_req *req;
74387446
struct smb2_ioctl_rsp *rsp, *rsp_org;
7439-
int cnt_code, nbytes = 0;
7440-
int out_buf_len;
7447+
unsigned int cnt_code, nbytes = 0, out_buf_len, in_buf_len;
74417448
u64 id = KSMBD_NO_FID;
74427449
struct ksmbd_conn *conn = work->conn;
74437450
int ret = 0;
@@ -7466,7 +7473,11 @@ int smb2_ioctl(struct ksmbd_work *work)
74667473

74677474
cnt_code = le32_to_cpu(req->CntCode);
74687475
out_buf_len = le32_to_cpu(req->MaxOutputResponse);
7469-
out_buf_len = min(KSMBD_IPC_MAX_PAYLOAD, out_buf_len);
7476+
out_buf_len =
7477+
min_t(u32, work->response_sz - work->next_smb2_rsp_hdr_off -
7478+
(offsetof(struct smb2_ioctl_rsp, Buffer) - 4),
7479+
out_buf_len);
7480+
in_buf_len = le32_to_cpu(req->InputCount);
74707481

74717482
switch (cnt_code) {
74727483
case FSCTL_DFS_GET_REFERRALS:
@@ -7494,6 +7505,7 @@ int smb2_ioctl(struct ksmbd_work *work)
74947505
break;
74957506
}
74967507
case FSCTL_PIPE_TRANSCEIVE:
7508+
out_buf_len = min_t(u32, KSMBD_IPC_MAX_PAYLOAD, out_buf_len);
74977509
nbytes = fsctl_pipe_transceive(work, id, out_buf_len, req, rsp);
74987510
break;
74997511
case FSCTL_VALIDATE_NEGOTIATE_INFO:
@@ -7502,9 +7514,16 @@ int smb2_ioctl(struct ksmbd_work *work)
75027514
goto out;
75037515
}
75047516

7517+
if (in_buf_len < sizeof(struct validate_negotiate_info_req))
7518+
return -EINVAL;
7519+
7520+
if (out_buf_len < sizeof(struct validate_negotiate_info_rsp))
7521+
return -EINVAL;
7522+
75057523
ret = fsctl_validate_negotiate_info(conn,
75067524
(struct validate_negotiate_info_req *)&req->Buffer[0],
7507-
(struct validate_negotiate_info_rsp *)&rsp->Buffer[0]);
7525+
(struct validate_negotiate_info_rsp *)&rsp->Buffer[0],
7526+
in_buf_len);
75087527
if (ret < 0)
75097528
goto out;
75107529

@@ -7513,9 +7532,10 @@ int smb2_ioctl(struct ksmbd_work *work)
75137532
rsp->VolatileFileId = cpu_to_le64(SMB2_NO_FID);
75147533
break;
75157534
case FSCTL_QUERY_NETWORK_INTERFACE_INFO:
7516-
nbytes = fsctl_query_iface_info_ioctl(conn, req, rsp);
7517-
if (nbytes < 0)
7535+
ret = fsctl_query_iface_info_ioctl(conn, rsp, out_buf_len);
7536+
if (ret < 0)
75187537
goto out;
7538+
nbytes = ret;
75197539
break;
75207540
case FSCTL_REQUEST_RESUME_KEY:
75217541
if (out_buf_len < sizeof(struct resume_key_ioctl_rsp)) {
@@ -7540,15 +7560,33 @@ int smb2_ioctl(struct ksmbd_work *work)
75407560
goto out;
75417561
}
75427562

7563+
if (in_buf_len < sizeof(struct copychunk_ioctl_req)) {
7564+
ret = -EINVAL;
7565+
goto out;
7566+
}
7567+
75437568
if (out_buf_len < sizeof(struct copychunk_ioctl_rsp)) {
75447569
ret = -EINVAL;
75457570
goto out;
75467571
}
75477572

75487573
nbytes = sizeof(struct copychunk_ioctl_rsp);
7549-
fsctl_copychunk(work, req, rsp);
7574+
rsp->VolatileFileId = req->VolatileFileId;
7575+
rsp->PersistentFileId = req->PersistentFileId;
7576+
fsctl_copychunk(work,
7577+
(struct copychunk_ioctl_req *)&req->Buffer[0],
7578+
le32_to_cpu(req->CntCode),
7579+
le32_to_cpu(req->InputCount),
7580+
le64_to_cpu(req->VolatileFileId),
7581+
le64_to_cpu(req->PersistentFileId),
7582+
rsp);
75507583
break;
75517584
case FSCTL_SET_SPARSE:
7585+
if (in_buf_len < sizeof(struct file_sparse)) {
7586+
ret = -EINVAL;
7587+
goto out;
7588+
}
7589+
75527590
ret = fsctl_set_sparse(work, id,
75537591
(struct file_sparse *)&req->Buffer[0]);
75547592
if (ret < 0)
@@ -7567,6 +7605,11 @@ int smb2_ioctl(struct ksmbd_work *work)
75677605
goto out;
75687606
}
75697607

7608+
if (in_buf_len < sizeof(struct file_zero_data_information)) {
7609+
ret = -EINVAL;
7610+
goto out;
7611+
}
7612+
75707613
zero_data =
75717614
(struct file_zero_data_information *)&req->Buffer[0];
75727615

@@ -7586,6 +7629,11 @@ int smb2_ioctl(struct ksmbd_work *work)
75867629
break;
75877630
}
75887631
case FSCTL_QUERY_ALLOCATED_RANGES:
7632+
if (in_buf_len < sizeof(struct file_allocated_range_buffer)) {
7633+
ret = -EINVAL;
7634+
goto out;
7635+
}
7636+
75897637
ret = fsctl_query_allocated_ranges(work, id,
75907638
(struct file_allocated_range_buffer *)&req->Buffer[0],
75917639
(struct file_allocated_range_buffer *)&rsp->Buffer[0],
@@ -7626,6 +7674,11 @@ int smb2_ioctl(struct ksmbd_work *work)
76267674
struct duplicate_extents_to_file *dup_ext;
76277675
loff_t src_off, dst_off, length, cloned;
76287676

7677+
if (in_buf_len < sizeof(struct duplicate_extents_to_file)) {
7678+
ret = -EINVAL;
7679+
goto out;
7680+
}
7681+
76297682
dup_ext = (struct duplicate_extents_to_file *)&req->Buffer[0];
76307683

76317684
fp_in = ksmbd_lookup_fd_slow(work, dup_ext->VolatileFileHandle,
@@ -7696,6 +7749,8 @@ int smb2_ioctl(struct ksmbd_work *work)
76967749
rsp->hdr.Status = STATUS_OBJECT_NAME_NOT_FOUND;
76977750
else if (ret == -EOPNOTSUPP)
76987751
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
7752+
else if (ret == -ENOSPC)
7753+
rsp->hdr.Status = STATUS_BUFFER_TOO_SMALL;
76997754
else if (ret < 0 || rsp->hdr.Status == 0)
77007755
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
77017756
smb2_set_err_rsp(work);

fs/ksmbd/vfs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1023,7 +1023,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
10231023

10241024
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
10251025
struct file_allocated_range_buffer *ranges,
1026-
int in_count, int *out_count)
1026+
unsigned int in_count, unsigned int *out_count)
10271027
{
10281028
struct file *f = fp->filp;
10291029
struct inode *inode = file_inode(fp->filp);

fs/ksmbd/vfs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
166166
struct file_allocated_range_buffer;
167167
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
168168
struct file_allocated_range_buffer *ranges,
169-
int in_count, int *out_count);
169+
unsigned int in_count, unsigned int *out_count);
170170
int ksmbd_vfs_unlink(struct user_namespace *user_ns,
171171
struct dentry *dir, struct dentry *dentry);
172172
void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat);

0 commit comments

Comments
 (0)