Skip to content

Commit 3489299

Browse files
committed
Merge tag 'v7.0-rc5-ksmbd-srv-fixes' of git://git.samba.org/ksmbd
Pull smb server fixes from Steve French: - Fix out of bounds write - Fix for better calculating max output buffers - Fix memory leaks in SMB2/SMB3 lock - Fix use after free - Multichannel fix * tag 'v7.0-rc5-ksmbd-srv-fixes' of git://git.samba.org/ksmbd: ksmbd: fix potencial OOB in get_file_all_info() for compound requests ksmbd: replace hardcoded hdr2_len with offsetof() in smb2_calc_max_out_buf_len() ksmbd: fix memory leaks and NULL deref in smb2_lock() ksmbd: fix use-after-free and NULL deref in smb_grant_oplock() ksmbd: do not expire session on binding failure
2 parents 46b5132 + beef263 commit 3489299

2 files changed

Lines changed: 97 additions & 48 deletions

File tree

fs/smb/server/oplock.c

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,19 @@ static void lease_del_list(struct oplock_info *opinfo)
8282
spin_unlock(&lb->lb_lock);
8383
}
8484

85-
static void lb_add(struct lease_table *lb)
85+
static struct lease_table *alloc_lease_table(struct oplock_info *opinfo)
8686
{
87-
write_lock(&lease_list_lock);
88-
list_add(&lb->l_entry, &lease_table_list);
89-
write_unlock(&lease_list_lock);
87+
struct lease_table *lb;
88+
89+
lb = kmalloc_obj(struct lease_table, KSMBD_DEFAULT_GFP);
90+
if (!lb)
91+
return NULL;
92+
93+
memcpy(lb->client_guid, opinfo->conn->ClientGUID,
94+
SMB2_CLIENT_GUID_SIZE);
95+
INIT_LIST_HEAD(&lb->lease_list);
96+
spin_lock_init(&lb->lb_lock);
97+
return lb;
9098
}
9199

92100
static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx)
@@ -1042,34 +1050,27 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2)
10421050
lease2->version = lease1->version;
10431051
}
10441052

1045-
static int add_lease_global_list(struct oplock_info *opinfo)
1053+
static void add_lease_global_list(struct oplock_info *opinfo,
1054+
struct lease_table *new_lb)
10461055
{
10471056
struct lease_table *lb;
10481057

1049-
read_lock(&lease_list_lock);
1058+
write_lock(&lease_list_lock);
10501059
list_for_each_entry(lb, &lease_table_list, l_entry) {
10511060
if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID,
10521061
SMB2_CLIENT_GUID_SIZE)) {
10531062
opinfo->o_lease->l_lb = lb;
10541063
lease_add_list(opinfo);
1055-
read_unlock(&lease_list_lock);
1056-
return 0;
1064+
write_unlock(&lease_list_lock);
1065+
kfree(new_lb);
1066+
return;
10571067
}
10581068
}
1059-
read_unlock(&lease_list_lock);
10601069

1061-
lb = kmalloc_obj(struct lease_table, KSMBD_DEFAULT_GFP);
1062-
if (!lb)
1063-
return -ENOMEM;
1064-
1065-
memcpy(lb->client_guid, opinfo->conn->ClientGUID,
1066-
SMB2_CLIENT_GUID_SIZE);
1067-
INIT_LIST_HEAD(&lb->lease_list);
1068-
spin_lock_init(&lb->lb_lock);
1069-
opinfo->o_lease->l_lb = lb;
1070+
opinfo->o_lease->l_lb = new_lb;
10701071
lease_add_list(opinfo);
1071-
lb_add(lb);
1072-
return 0;
1072+
list_add(&new_lb->l_entry, &lease_table_list);
1073+
write_unlock(&lease_list_lock);
10731074
}
10741075

10751076
static void set_oplock_level(struct oplock_info *opinfo, int level,
@@ -1189,6 +1190,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
11891190
int err = 0;
11901191
struct oplock_info *opinfo = NULL, *prev_opinfo = NULL;
11911192
struct ksmbd_inode *ci = fp->f_ci;
1193+
struct lease_table *new_lb = NULL;
11921194
bool prev_op_has_lease;
11931195
__le32 prev_op_state = 0;
11941196

@@ -1291,21 +1293,37 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
12911293
set_oplock_level(opinfo, req_op_level, lctx);
12921294

12931295
out:
1294-
opinfo_count_inc(fp);
1295-
opinfo_add(opinfo, fp);
1296-
1296+
/*
1297+
* Set o_fp before any publication so that concurrent readers
1298+
* (e.g. find_same_lease_key() on the lease list) that
1299+
* dereference opinfo->o_fp don't hit a NULL pointer.
1300+
*
1301+
* Keep the original publication order so concurrent opens can
1302+
* still observe the in-flight grant via ci->m_op_list, but make
1303+
* everything after opinfo_add() no-fail by preallocating any new
1304+
* lease_table first.
1305+
*/
1306+
opinfo->o_fp = fp;
12971307
if (opinfo->is_lease) {
1298-
err = add_lease_global_list(opinfo);
1299-
if (err)
1308+
new_lb = alloc_lease_table(opinfo);
1309+
if (!new_lb) {
1310+
err = -ENOMEM;
13001311
goto err_out;
1312+
}
13011313
}
13021314

1315+
opinfo_count_inc(fp);
1316+
opinfo_add(opinfo, fp);
1317+
1318+
if (opinfo->is_lease)
1319+
add_lease_global_list(opinfo, new_lb);
1320+
13031321
rcu_assign_pointer(fp->f_opinfo, opinfo);
1304-
opinfo->o_fp = fp;
13051322

13061323
return 0;
13071324
err_out:
1308-
__free_opinfo(opinfo);
1325+
kfree(new_lb);
1326+
opinfo_put(opinfo);
13091327
return err;
13101328
}
13111329

fs/smb/server/smb2pdu.c

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,8 +1939,14 @@ int smb2_sess_setup(struct ksmbd_work *work)
19391939
if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION)
19401940
try_delay = true;
19411941

1942-
sess->last_active = jiffies;
1943-
sess->state = SMB2_SESSION_EXPIRED;
1942+
/*
1943+
* For binding requests, session belongs to another
1944+
* connection. Do not expire it.
1945+
*/
1946+
if (!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
1947+
sess->last_active = jiffies;
1948+
sess->state = SMB2_SESSION_EXPIRED;
1949+
}
19441950
ksmbd_user_session_put(sess);
19451951
work->sess = NULL;
19461952
if (try_delay) {
@@ -4446,8 +4452,9 @@ int smb2_query_dir(struct ksmbd_work *work)
44464452
d_info.wptr = (char *)rsp->Buffer;
44474453
d_info.rptr = (char *)rsp->Buffer;
44484454
d_info.out_buf_len =
4449-
smb2_calc_max_out_buf_len(work, 8,
4450-
le32_to_cpu(req->OutputBufferLength));
4455+
smb2_calc_max_out_buf_len(work,
4456+
offsetof(struct smb2_query_directory_rsp, Buffer),
4457+
le32_to_cpu(req->OutputBufferLength));
44514458
if (d_info.out_buf_len < 0) {
44524459
rc = -EINVAL;
44534460
goto err_out;
@@ -4714,8 +4721,9 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
47144721
}
47154722

47164723
buf_free_len =
4717-
smb2_calc_max_out_buf_len(work, 8,
4718-
le32_to_cpu(req->OutputBufferLength));
4724+
smb2_calc_max_out_buf_len(work,
4725+
offsetof(struct smb2_query_info_rsp, Buffer),
4726+
le32_to_cpu(req->OutputBufferLength));
47194727
if (buf_free_len < 0)
47204728
return -EINVAL;
47214729

@@ -4932,7 +4940,8 @@ static int get_file_all_info(struct ksmbd_work *work,
49324940
int conv_len;
49334941
char *filename;
49344942
u64 time;
4935-
int ret;
4943+
int ret, buf_free_len, filename_len;
4944+
struct smb2_query_info_req *req = ksmbd_req_buf_next(work);
49364945

49374946
if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
49384947
ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n",
@@ -4944,6 +4953,16 @@ static int get_file_all_info(struct ksmbd_work *work,
49444953
if (IS_ERR(filename))
49454954
return PTR_ERR(filename);
49464955

4956+
filename_len = strlen(filename);
4957+
buf_free_len = smb2_calc_max_out_buf_len(work,
4958+
offsetof(struct smb2_query_info_rsp, Buffer) +
4959+
offsetof(struct smb2_file_all_info, FileName),
4960+
le32_to_cpu(req->OutputBufferLength));
4961+
if (buf_free_len < (filename_len + 1) * 2) {
4962+
kfree(filename);
4963+
return -EINVAL;
4964+
}
4965+
49474966
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
49484967
AT_STATX_SYNC_AS_STAT);
49494968
if (ret) {
@@ -4987,7 +5006,8 @@ static int get_file_all_info(struct ksmbd_work *work,
49875006
file_info->Mode = fp->coption;
49885007
file_info->AlignmentRequirement = 0;
49895008
conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename,
4990-
PATH_MAX, conn->local_nls, 0);
5009+
min(filename_len, PATH_MAX),
5010+
conn->local_nls, 0);
49915011
conv_len *= 2;
49925012
file_info->FileNameLength = cpu_to_le32(conv_len);
49935013
rsp->OutputBufferLength =
@@ -5041,8 +5061,9 @@ static int get_file_stream_info(struct ksmbd_work *work,
50415061
file_info = (struct smb2_file_stream_info *)rsp->Buffer;
50425062

50435063
buf_free_len =
5044-
smb2_calc_max_out_buf_len(work, 8,
5045-
le32_to_cpu(req->OutputBufferLength));
5064+
smb2_calc_max_out_buf_len(work,
5065+
offsetof(struct smb2_query_info_rsp, Buffer),
5066+
le32_to_cpu(req->OutputBufferLength));
50465067
if (buf_free_len < 0)
50475068
goto out;
50485069

@@ -7586,14 +7607,15 @@ int smb2_lock(struct ksmbd_work *work)
75867607
rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL);
75877608
skip:
75887609
if (smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) {
7610+
locks_free_lock(flock);
7611+
kfree(smb_lock);
75897612
if (!rc) {
75907613
ksmbd_debug(SMB, "File unlocked\n");
75917614
} else if (rc == -ENOENT) {
75927615
rsp->hdr.Status = STATUS_NOT_LOCKED;
7616+
err = rc;
75937617
goto out;
75947618
}
7595-
locks_free_lock(flock);
7596-
kfree(smb_lock);
75977619
} else {
75987620
if (rc == FILE_LOCK_DEFERRED) {
75997621
void **argv;
@@ -7662,6 +7684,9 @@ int smb2_lock(struct ksmbd_work *work)
76627684
spin_unlock(&work->conn->llist_lock);
76637685
ksmbd_debug(SMB, "successful in taking lock\n");
76647686
} else {
7687+
locks_free_lock(flock);
7688+
kfree(smb_lock);
7689+
err = rc;
76657690
goto out;
76667691
}
76677692
}
@@ -7692,13 +7717,17 @@ int smb2_lock(struct ksmbd_work *work)
76927717
struct file_lock *rlock = NULL;
76937718

76947719
rlock = smb_flock_init(filp);
7695-
rlock->c.flc_type = F_UNLCK;
7696-
rlock->fl_start = smb_lock->start;
7697-
rlock->fl_end = smb_lock->end;
7720+
if (rlock) {
7721+
rlock->c.flc_type = F_UNLCK;
7722+
rlock->fl_start = smb_lock->start;
7723+
rlock->fl_end = smb_lock->end;
76987724

7699-
rc = vfs_lock_file(filp, F_SETLK, rlock, NULL);
7700-
if (rc)
7701-
pr_err("rollback unlock fail : %d\n", rc);
7725+
rc = vfs_lock_file(filp, F_SETLK, rlock, NULL);
7726+
if (rc)
7727+
pr_err("rollback unlock fail : %d\n", rc);
7728+
} else {
7729+
pr_err("rollback unlock alloc failed\n");
7730+
}
77027731

77037732
list_del(&smb_lock->llist);
77047733
spin_lock(&work->conn->llist_lock);
@@ -7708,7 +7737,8 @@ int smb2_lock(struct ksmbd_work *work)
77087737
spin_unlock(&work->conn->llist_lock);
77097738

77107739
locks_free_lock(smb_lock->fl);
7711-
locks_free_lock(rlock);
7740+
if (rlock)
7741+
locks_free_lock(rlock);
77127742
kfree(smb_lock);
77137743
}
77147744
out2:
@@ -8191,8 +8221,9 @@ int smb2_ioctl(struct ksmbd_work *work)
81918221
buffer = (char *)req + le32_to_cpu(req->InputOffset);
81928222

81938223
cnt_code = le32_to_cpu(req->CtlCode);
8194-
ret = smb2_calc_max_out_buf_len(work, 48,
8195-
le32_to_cpu(req->MaxOutputResponse));
8224+
ret = smb2_calc_max_out_buf_len(work,
8225+
offsetof(struct smb2_ioctl_rsp, Buffer),
8226+
le32_to_cpu(req->MaxOutputResponse));
81968227
if (ret < 0) {
81978228
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
81988229
goto out;

0 commit comments

Comments
 (0)