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
This commit is contained in:
commit
34892992d0
2 files changed
+97
-48
No files matched your search
+45
-27
@@ -82,11 +82,19 @@ static void lease_del_list(struct oplock_info *opinfo)
|
||||
spin_unlock(&lb->lb_lock);
|
||||
}
|
||||
|
||||
static void lb_add(struct lease_table *lb)
|
||||
static struct lease_table *alloc_lease_table(struct oplock_info *opinfo)
|
||||
{
|
||||
write_lock(&lease_list_lock);
|
||||
list_add(&lb->l_entry, &lease_table_list);
|
||||
write_unlock(&lease_list_lock);
|
||||
struct lease_table *lb;
|
||||
|
||||
lb = kmalloc_obj(struct lease_table, KSMBD_DEFAULT_GFP);
|
||||
if (!lb)
|
||||
return NULL;
|
||||
|
||||
memcpy(lb->client_guid, opinfo->conn->ClientGUID,
|
||||
SMB2_CLIENT_GUID_SIZE);
|
||||
INIT_LIST_HEAD(&lb->lease_list);
|
||||
spin_lock_init(&lb->lb_lock);
|
||||
return lb;
|
||||
}
|
||||
|
||||
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)
|
||||
lease2->version = lease1->version;
|
||||
}
|
||||
|
||||
static int add_lease_global_list(struct oplock_info *opinfo)
|
||||
static void add_lease_global_list(struct oplock_info *opinfo,
|
||||
struct lease_table *new_lb)
|
||||
{
|
||||
struct lease_table *lb;
|
||||
|
||||
read_lock(&lease_list_lock);
|
||||
write_lock(&lease_list_lock);
|
||||
list_for_each_entry(lb, &lease_table_list, l_entry) {
|
||||
if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID,
|
||||
SMB2_CLIENT_GUID_SIZE)) {
|
||||
opinfo->o_lease->l_lb = lb;
|
||||
lease_add_list(opinfo);
|
||||
read_unlock(&lease_list_lock);
|
||||
return 0;
|
||||
write_unlock(&lease_list_lock);
|
||||
kfree(new_lb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
read_unlock(&lease_list_lock);
|
||||
|
||||
lb = kmalloc_obj(struct lease_table, KSMBD_DEFAULT_GFP);
|
||||
if (!lb)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(lb->client_guid, opinfo->conn->ClientGUID,
|
||||
SMB2_CLIENT_GUID_SIZE);
|
||||
INIT_LIST_HEAD(&lb->lease_list);
|
||||
spin_lock_init(&lb->lb_lock);
|
||||
opinfo->o_lease->l_lb = lb;
|
||||
opinfo->o_lease->l_lb = new_lb;
|
||||
lease_add_list(opinfo);
|
||||
lb_add(lb);
|
||||
return 0;
|
||||
list_add(&new_lb->l_entry, &lease_table_list);
|
||||
write_unlock(&lease_list_lock);
|
||||
}
|
||||
|
||||
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,
|
||||
int err = 0;
|
||||
struct oplock_info *opinfo = NULL, *prev_opinfo = NULL;
|
||||
struct ksmbd_inode *ci = fp->f_ci;
|
||||
struct lease_table *new_lb = NULL;
|
||||
bool prev_op_has_lease;
|
||||
__le32 prev_op_state = 0;
|
||||
|
||||
@@ -1291,21 +1293,37 @@ set_lev:
|
||||
set_oplock_level(opinfo, req_op_level, lctx);
|
||||
|
||||
out:
|
||||
/*
|
||||
* Set o_fp before any publication so that concurrent readers
|
||||
* (e.g. find_same_lease_key() on the lease list) that
|
||||
* dereference opinfo->o_fp don't hit a NULL pointer.
|
||||
*
|
||||
* Keep the original publication order so concurrent opens can
|
||||
* still observe the in-flight grant via ci->m_op_list, but make
|
||||
* everything after opinfo_add() no-fail by preallocating any new
|
||||
* lease_table first.
|
||||
*/
|
||||
opinfo->o_fp = fp;
|
||||
if (opinfo->is_lease) {
|
||||
new_lb = alloc_lease_table(opinfo);
|
||||
if (!new_lb) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
opinfo_count_inc(fp);
|
||||
opinfo_add(opinfo, fp);
|
||||
|
||||
if (opinfo->is_lease) {
|
||||
err = add_lease_global_list(opinfo);
|
||||
if (err)
|
||||
goto err_out;
|
||||
}
|
||||
if (opinfo->is_lease)
|
||||
add_lease_global_list(opinfo, new_lb);
|
||||
|
||||
rcu_assign_pointer(fp->f_opinfo, opinfo);
|
||||
opinfo->o_fp = fp;
|
||||
|
||||
return 0;
|
||||
err_out:
|
||||
__free_opinfo(opinfo);
|
||||
kfree(new_lb);
|
||||
opinfo_put(opinfo);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
+52
-21
@@ -1939,8 +1939,14 @@ out_err:
|
||||
if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION)
|
||||
try_delay = true;
|
||||
|
||||
sess->last_active = jiffies;
|
||||
sess->state = SMB2_SESSION_EXPIRED;
|
||||
/*
|
||||
* For binding requests, session belongs to another
|
||||
* connection. Do not expire it.
|
||||
*/
|
||||
if (!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
|
||||
sess->last_active = jiffies;
|
||||
sess->state = SMB2_SESSION_EXPIRED;
|
||||
}
|
||||
ksmbd_user_session_put(sess);
|
||||
work->sess = NULL;
|
||||
if (try_delay) {
|
||||
@@ -4446,8 +4452,9 @@ int smb2_query_dir(struct ksmbd_work *work)
|
||||
d_info.wptr = (char *)rsp->Buffer;
|
||||
d_info.rptr = (char *)rsp->Buffer;
|
||||
d_info.out_buf_len =
|
||||
smb2_calc_max_out_buf_len(work, 8,
|
||||
le32_to_cpu(req->OutputBufferLength));
|
||||
smb2_calc_max_out_buf_len(work,
|
||||
offsetof(struct smb2_query_directory_rsp, Buffer),
|
||||
le32_to_cpu(req->OutputBufferLength));
|
||||
if (d_info.out_buf_len < 0) {
|
||||
rc = -EINVAL;
|
||||
goto err_out;
|
||||
@@ -4714,8 +4721,9 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
}
|
||||
|
||||
buf_free_len =
|
||||
smb2_calc_max_out_buf_len(work, 8,
|
||||
le32_to_cpu(req->OutputBufferLength));
|
||||
smb2_calc_max_out_buf_len(work,
|
||||
offsetof(struct smb2_query_info_rsp, Buffer),
|
||||
le32_to_cpu(req->OutputBufferLength));
|
||||
if (buf_free_len < 0)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -4932,7 +4940,8 @@ static int get_file_all_info(struct ksmbd_work *work,
|
||||
int conv_len;
|
||||
char *filename;
|
||||
u64 time;
|
||||
int ret;
|
||||
int ret, buf_free_len, filename_len;
|
||||
struct smb2_query_info_req *req = ksmbd_req_buf_next(work);
|
||||
|
||||
if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) {
|
||||
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,
|
||||
if (IS_ERR(filename))
|
||||
return PTR_ERR(filename);
|
||||
|
||||
filename_len = strlen(filename);
|
||||
buf_free_len = smb2_calc_max_out_buf_len(work,
|
||||
offsetof(struct smb2_query_info_rsp, Buffer) +
|
||||
offsetof(struct smb2_file_all_info, FileName),
|
||||
le32_to_cpu(req->OutputBufferLength));
|
||||
if (buf_free_len < (filename_len + 1) * 2) {
|
||||
kfree(filename);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS,
|
||||
AT_STATX_SYNC_AS_STAT);
|
||||
if (ret) {
|
||||
@@ -4987,7 +5006,8 @@ static int get_file_all_info(struct ksmbd_work *work,
|
||||
file_info->Mode = fp->coption;
|
||||
file_info->AlignmentRequirement = 0;
|
||||
conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename,
|
||||
PATH_MAX, conn->local_nls, 0);
|
||||
min(filename_len, PATH_MAX),
|
||||
conn->local_nls, 0);
|
||||
conv_len *= 2;
|
||||
file_info->FileNameLength = cpu_to_le32(conv_len);
|
||||
rsp->OutputBufferLength =
|
||||
@@ -5041,8 +5061,9 @@ static int get_file_stream_info(struct ksmbd_work *work,
|
||||
file_info = (struct smb2_file_stream_info *)rsp->Buffer;
|
||||
|
||||
buf_free_len =
|
||||
smb2_calc_max_out_buf_len(work, 8,
|
||||
le32_to_cpu(req->OutputBufferLength));
|
||||
smb2_calc_max_out_buf_len(work,
|
||||
offsetof(struct smb2_query_info_rsp, Buffer),
|
||||
le32_to_cpu(req->OutputBufferLength));
|
||||
if (buf_free_len < 0)
|
||||
goto out;
|
||||
|
||||
@@ -7586,14 +7607,15 @@ retry:
|
||||
rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL);
|
||||
skip:
|
||||
if (smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) {
|
||||
locks_free_lock(flock);
|
||||
kfree(smb_lock);
|
||||
if (!rc) {
|
||||
ksmbd_debug(SMB, "File unlocked\n");
|
||||
} else if (rc == -ENOENT) {
|
||||
rsp->hdr.Status = STATUS_NOT_LOCKED;
|
||||
err = rc;
|
||||
goto out;
|
||||
}
|
||||
locks_free_lock(flock);
|
||||
kfree(smb_lock);
|
||||
} else {
|
||||
if (rc == FILE_LOCK_DEFERRED) {
|
||||
void **argv;
|
||||
@@ -7662,6 +7684,9 @@ skip:
|
||||
spin_unlock(&work->conn->llist_lock);
|
||||
ksmbd_debug(SMB, "successful in taking lock\n");
|
||||
} else {
|
||||
locks_free_lock(flock);
|
||||
kfree(smb_lock);
|
||||
err = rc;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -7692,13 +7717,17 @@ out:
|
||||
struct file_lock *rlock = NULL;
|
||||
|
||||
rlock = smb_flock_init(filp);
|
||||
rlock->c.flc_type = F_UNLCK;
|
||||
rlock->fl_start = smb_lock->start;
|
||||
rlock->fl_end = smb_lock->end;
|
||||
if (rlock) {
|
||||
rlock->c.flc_type = F_UNLCK;
|
||||
rlock->fl_start = smb_lock->start;
|
||||
rlock->fl_end = smb_lock->end;
|
||||
|
||||
rc = vfs_lock_file(filp, F_SETLK, rlock, NULL);
|
||||
if (rc)
|
||||
pr_err("rollback unlock fail : %d\n", rc);
|
||||
rc = vfs_lock_file(filp, F_SETLK, rlock, NULL);
|
||||
if (rc)
|
||||
pr_err("rollback unlock fail : %d\n", rc);
|
||||
} else {
|
||||
pr_err("rollback unlock alloc failed\n");
|
||||
}
|
||||
|
||||
list_del(&smb_lock->llist);
|
||||
spin_lock(&work->conn->llist_lock);
|
||||
@@ -7708,7 +7737,8 @@ out:
|
||||
spin_unlock(&work->conn->llist_lock);
|
||||
|
||||
locks_free_lock(smb_lock->fl);
|
||||
locks_free_lock(rlock);
|
||||
if (rlock)
|
||||
locks_free_lock(rlock);
|
||||
kfree(smb_lock);
|
||||
}
|
||||
out2:
|
||||
@@ -8191,8 +8221,9 @@ int smb2_ioctl(struct ksmbd_work *work)
|
||||
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));
|
||||
ret = smb2_calc_max_out_buf_len(work,
|
||||
offsetof(struct smb2_ioctl_rsp, Buffer),
|
||||
le32_to_cpu(req->MaxOutputResponse));
|
||||
if (ret < 0) {
|
||||
rsp->hdr.Status = STATUS_INVALID_PARAMETER;
|
||||
goto out;
|
||||
|
||||
Reference in new issue
Block a user