mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 06:01:57 +00:00
Four ksmbd server fixes, three for stable
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmbpBKIACgkQiiy9cAdy T1H/TQv+NEjnpJMuqTYRMRdU6prcDoESszQD/hmMCRExGs9rupZxpGioW/Su7URN m7WGJlbjWKGB5z5MaP5ur24hoiRUT5nYEEKkTyJ4OmMbRDMnpUsLxvOieVXUMsR6 eZ+o/zHdblda54OA48+J7v+0L79xk7wesYbyWagFRzb+GOaIZe1y5BMYDwBWe8ac KJ0TfZxFmFpbwLN88hCejrFXSK/c6vi9uxgKyB1xTgBPKQTjMeF1caSGDxMF+SrW gNDP72/ZqoANyBxJUFdPGGEhv3aftRVku3CaLuZcTKdpHcxn9GXEK63gR9oDOEhW ZCqhifPMm0bTeKF3eCvy8WmkxWWB4KBy8IBIUm7HnmJLo87ctUxDDT5v9XAmCo1R zz9AAY7QY/IDKUFzais1AWu4lQNd1vQM/O635ahMH7YgIKUHnDhpHQAXnuCO6dk8 iIB6Ghb4cPuztQuy2LOiJ3AIco7O3F7VRJtV0rz/QHv0P1M9yswWyu+LfLZhYkMd VciukaZ+ =pUoF -----END PGP SIGNATURE----- Merge tag '6.12-rc-ksmbd-server-fixes-part1' of git://git.samba.org/ksmbd Pull smb server updates from Steve French: "Four ksmbd server fixes, three for stable: - Fix an issue where the directory can't be deleted if the share is on a file system that does not provide dot and dotdot entries - Fix file creation failure if the parent name of pathname is case sensitive - Fix write failure with FILE_APPEND_DATA flags - Add reference count to connection struct to protect UAF of oplocks on multichannel" * tag '6.12-rc-ksmbd-server-fixes-part1' of git://git.samba.org/ksmbd: ksmbd: handle caseless file creation ksmbd: make __dir_empty() compatible with POSIX ksmbd: add refcnt to ksmbd_conn struct ksmbd: allow write with FILE_APPEND_DATA
This commit is contained in:
commit
39898f0925
@ -39,7 +39,8 @@ void ksmbd_conn_free(struct ksmbd_conn *conn)
|
||||
xa_destroy(&conn->sessions);
|
||||
kvfree(conn->request_buf);
|
||||
kfree(conn->preauth_info);
|
||||
kfree(conn);
|
||||
if (atomic_dec_and_test(&conn->refcnt))
|
||||
kfree(conn);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,6 +69,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
|
||||
conn->um = NULL;
|
||||
atomic_set(&conn->req_running, 0);
|
||||
atomic_set(&conn->r_count, 0);
|
||||
atomic_set(&conn->refcnt, 1);
|
||||
conn->total_credits = 1;
|
||||
conn->outstanding_credits = 0;
|
||||
|
||||
|
@ -106,6 +106,7 @@ struct ksmbd_conn {
|
||||
bool signing_negotiated;
|
||||
__le16 signing_algorithm;
|
||||
bool binding;
|
||||
atomic_t refcnt;
|
||||
};
|
||||
|
||||
struct ksmbd_conn_ops {
|
||||
|
@ -51,6 +51,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
|
||||
init_waitqueue_head(&opinfo->oplock_brk);
|
||||
atomic_set(&opinfo->refcount, 1);
|
||||
atomic_set(&opinfo->breaking_cnt, 0);
|
||||
atomic_inc(&opinfo->conn->refcnt);
|
||||
|
||||
return opinfo;
|
||||
}
|
||||
@ -124,6 +125,8 @@ static void free_opinfo(struct oplock_info *opinfo)
|
||||
{
|
||||
if (opinfo->is_lease)
|
||||
free_lease(opinfo);
|
||||
if (opinfo->conn && atomic_dec_and_test(&opinfo->conn->refcnt))
|
||||
kfree(opinfo->conn);
|
||||
kfree(opinfo);
|
||||
}
|
||||
|
||||
@ -163,9 +166,7 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
|
||||
!atomic_inc_not_zero(&opinfo->refcount))
|
||||
opinfo = NULL;
|
||||
else {
|
||||
atomic_inc(&opinfo->conn->r_count);
|
||||
if (ksmbd_conn_releasing(opinfo->conn)) {
|
||||
atomic_dec(&opinfo->conn->r_count);
|
||||
atomic_dec(&opinfo->refcount);
|
||||
opinfo = NULL;
|
||||
}
|
||||
@ -177,26 +178,11 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
|
||||
return opinfo;
|
||||
}
|
||||
|
||||
static void opinfo_conn_put(struct oplock_info *opinfo)
|
||||
void opinfo_put(struct oplock_info *opinfo)
|
||||
{
|
||||
struct ksmbd_conn *conn;
|
||||
|
||||
if (!opinfo)
|
||||
return;
|
||||
|
||||
conn = opinfo->conn;
|
||||
/*
|
||||
* Checking waitqueue to dropping pending requests on
|
||||
* disconnection. waitqueue_active is safe because it
|
||||
* uses atomic operation for condition.
|
||||
*/
|
||||
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
|
||||
wake_up(&conn->r_count_q);
|
||||
opinfo_put(opinfo);
|
||||
}
|
||||
|
||||
void opinfo_put(struct oplock_info *opinfo)
|
||||
{
|
||||
if (!atomic_dec_and_test(&opinfo->refcount))
|
||||
return;
|
||||
|
||||
@ -1127,14 +1113,11 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
|
||||
if (!atomic_inc_not_zero(&opinfo->refcount))
|
||||
continue;
|
||||
|
||||
atomic_inc(&opinfo->conn->r_count);
|
||||
if (ksmbd_conn_releasing(opinfo->conn)) {
|
||||
atomic_dec(&opinfo->conn->r_count);
|
||||
if (ksmbd_conn_releasing(opinfo->conn))
|
||||
continue;
|
||||
}
|
||||
|
||||
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
|
||||
opinfo_conn_put(opinfo);
|
||||
opinfo_put(opinfo);
|
||||
}
|
||||
}
|
||||
up_read(&p_ci->m_lock);
|
||||
@ -1167,13 +1150,10 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
|
||||
if (!atomic_inc_not_zero(&opinfo->refcount))
|
||||
continue;
|
||||
|
||||
atomic_inc(&opinfo->conn->r_count);
|
||||
if (ksmbd_conn_releasing(opinfo->conn)) {
|
||||
atomic_dec(&opinfo->conn->r_count);
|
||||
if (ksmbd_conn_releasing(opinfo->conn))
|
||||
continue;
|
||||
}
|
||||
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
|
||||
opinfo_conn_put(opinfo);
|
||||
opinfo_put(opinfo);
|
||||
}
|
||||
}
|
||||
up_read(&p_ci->m_lock);
|
||||
@ -1252,7 +1232,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
|
||||
prev_opinfo = opinfo_get_list(ci);
|
||||
if (!prev_opinfo ||
|
||||
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) {
|
||||
opinfo_conn_put(prev_opinfo);
|
||||
opinfo_put(prev_opinfo);
|
||||
goto set_lev;
|
||||
}
|
||||
prev_op_has_lease = prev_opinfo->is_lease;
|
||||
@ -1262,19 +1242,19 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
|
||||
if (share_ret < 0 &&
|
||||
prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
|
||||
err = share_ret;
|
||||
opinfo_conn_put(prev_opinfo);
|
||||
opinfo_put(prev_opinfo);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
|
||||
prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
|
||||
opinfo_conn_put(prev_opinfo);
|
||||
opinfo_put(prev_opinfo);
|
||||
goto op_break_not_needed;
|
||||
}
|
||||
|
||||
list_add(&work->interim_entry, &prev_opinfo->interim_list);
|
||||
err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II);
|
||||
opinfo_conn_put(prev_opinfo);
|
||||
opinfo_put(prev_opinfo);
|
||||
if (err == -ENOENT)
|
||||
goto set_lev;
|
||||
/* Check all oplock was freed by close */
|
||||
@ -1337,14 +1317,14 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work,
|
||||
return;
|
||||
if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
|
||||
brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
|
||||
opinfo_conn_put(brk_opinfo);
|
||||
opinfo_put(brk_opinfo);
|
||||
return;
|
||||
}
|
||||
|
||||
brk_opinfo->open_trunc = is_trunc;
|
||||
list_add(&work->interim_entry, &brk_opinfo->interim_list);
|
||||
oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II);
|
||||
opinfo_conn_put(brk_opinfo);
|
||||
opinfo_put(brk_opinfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1376,11 +1356,8 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
if (!atomic_inc_not_zero(&brk_op->refcount))
|
||||
continue;
|
||||
|
||||
atomic_inc(&brk_op->conn->r_count);
|
||||
if (ksmbd_conn_releasing(brk_op->conn)) {
|
||||
atomic_dec(&brk_op->conn->r_count);
|
||||
if (ksmbd_conn_releasing(brk_op->conn))
|
||||
continue;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
if (brk_op->is_lease && (brk_op->o_lease->state &
|
||||
@ -1411,7 +1388,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
brk_op->open_trunc = is_trunc;
|
||||
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE);
|
||||
next:
|
||||
opinfo_conn_put(brk_op);
|
||||
opinfo_put(brk_op);
|
||||
rcu_read_lock();
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
@ -496,7 +496,7 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
int err = 0;
|
||||
|
||||
if (work->conn->connection_type) {
|
||||
if (!(fp->daccess & FILE_WRITE_DATA_LE)) {
|
||||
if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) {
|
||||
pr_err("no right to write(%pD)\n", fp->filp);
|
||||
err = -EACCES;
|
||||
goto out;
|
||||
@ -1115,9 +1115,10 @@ static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen,
|
||||
struct ksmbd_readdir_data *buf;
|
||||
|
||||
buf = container_of(ctx, struct ksmbd_readdir_data, ctx);
|
||||
buf->dirent_count++;
|
||||
if (!is_dot_dotdot(name, namlen))
|
||||
buf->dirent_count++;
|
||||
|
||||
return buf->dirent_count <= 2;
|
||||
return !buf->dirent_count;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1137,7 +1138,7 @@ int ksmbd_vfs_empty_dir(struct ksmbd_file *fp)
|
||||
readdir_data.dirent_count = 0;
|
||||
|
||||
err = iterate_dir(fp->filp, &readdir_data.ctx);
|
||||
if (readdir_data.dirent_count > 2)
|
||||
if (readdir_data.dirent_count)
|
||||
err = -ENOTEMPTY;
|
||||
else
|
||||
err = 0;
|
||||
@ -1166,7 +1167,7 @@ static bool __caseless_lookup(struct dir_context *ctx, const char *name,
|
||||
if (cmp < 0)
|
||||
cmp = strncasecmp((char *)buf->private, name, namlen);
|
||||
if (!cmp) {
|
||||
memcpy((char *)buf->private, name, namlen);
|
||||
memcpy((char *)buf->private, name, buf->used);
|
||||
buf->dirent_count = 1;
|
||||
return false;
|
||||
}
|
||||
@ -1234,10 +1235,7 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
|
||||
char *filepath;
|
||||
size_t path_len, remain_len;
|
||||
|
||||
filepath = kstrdup(name, GFP_KERNEL);
|
||||
if (!filepath)
|
||||
return -ENOMEM;
|
||||
|
||||
filepath = name;
|
||||
path_len = strlen(filepath);
|
||||
remain_len = path_len;
|
||||
|
||||
@ -1280,10 +1278,9 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
|
||||
err = -EINVAL;
|
||||
out2:
|
||||
path_put(parent_path);
|
||||
out1:
|
||||
kfree(filepath);
|
||||
}
|
||||
|
||||
out1:
|
||||
if (!err) {
|
||||
err = mnt_want_write(parent_path->mnt);
|
||||
if (err) {
|
||||
|
@ -863,6 +863,8 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
|
||||
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
|
||||
if (op->conn != conn)
|
||||
continue;
|
||||
if (op->conn && atomic_dec_and_test(&op->conn->refcnt))
|
||||
kfree(op->conn);
|
||||
op->conn = NULL;
|
||||
}
|
||||
up_write(&ci->m_lock);
|
||||
@ -965,6 +967,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
|
||||
if (op->conn)
|
||||
continue;
|
||||
op->conn = fp->conn;
|
||||
atomic_inc(&op->conn->refcnt);
|
||||
}
|
||||
up_write(&ci->m_lock);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user