four ksmbd server fixes, including three for stable

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmQvWyAACgkQiiy9cAdy
 T1FSnAv/dqpNpgkqeC0qhytnJfcDCzuSDWcMlB7Nuyd3HcLRJAzdIs9T+Zphfrkj
 sDGbznjAdNt8+vw7uKIl25NEgm2UbX4SiYeaQX5wUqcrnksbzXyBiZG0Y2Hr0SKi
 MwIq75Lj7vA2wtFuCWM5Q3vTAqSE0RDp+m+LYjb8peLjRw2OJ8siaLdiK1l0e2PD
 YaWJg04yrzl14Uk8A+Qz/+EsNz0jHt7hQ9HETsNzbRVaIHBYfIpqgGQHILxEdU6a
 EqzbYAfYdmitGv2guj1iEMsgeJN3P8DQOQ2Nb80IdIcoq/Erq2q++T0O6xdy/84g
 PdFZD14fsAe7ao5lGc2K5DQPGLSIF7ZNkVaCJEa3chLpXxmnwqt3TLUzbj0jP7Yv
 huM7dEDpmVUosIahycKcR5vVjwB9lnCy18AIFwHZzdabzZRgNPx8eCAkjc7+HwD0
 RZ3zcZpqxY3aEUNbktnp7XNJo9ifo+gIcNQC/yAoINY7IfK3wQWamC3FZpo1fFqU
 jkUsq3lq
 =xypJ
 -----END PGP SIGNATURE-----

Merge tag '6.3-rc5-ksmbd-server-fixes' of git://git.samba.org/ksmbd

Pull ksmbd server fixes from Steve French:
 "Four fixes, three for stable:

   - slab out of bounds fix

   - lock cancellation fix

   - minor cleanup to address clang warning

   - fix for xfstest 551 (wrong parms passed to kvmalloc)"

* tag '6.3-rc5-ksmbd-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: fix slab-out-of-bounds in init_smb2_rsp_hdr
  ksmbd: delete asynchronous work from list
  ksmbd: remove unused is_char_allowed function
  ksmbd: do not call kvmalloc() with __GFP_NORETRY | __GFP_NO_WARN
This commit is contained in:
Linus Torvalds 2023-04-07 13:10:23 -07:00
commit a1e6fff395
8 changed files with 141 additions and 80 deletions

View File

@ -112,10 +112,8 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
struct ksmbd_conn *conn = work->conn;
struct list_head *requests_queue = NULL;
if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) {
if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE)
requests_queue = &conn->requests;
work->synchronous = true;
}
if (requests_queue) {
atomic_inc(&conn->req_running);
@ -136,14 +134,14 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
if (!work->multiRsp)
atomic_dec(&conn->req_running);
spin_lock(&conn->request_lock);
if (!work->multiRsp) {
spin_lock(&conn->request_lock);
list_del_init(&work->request_entry);
if (!work->synchronous)
list_del_init(&work->async_request_entry);
spin_unlock(&conn->request_lock);
if (work->asynchronous)
release_async_work(work);
ret = 0;
}
spin_unlock(&conn->request_lock);
wake_up_all(&conn->req_running_q);
return ret;
@ -326,10 +324,7 @@ int ksmbd_conn_handler_loop(void *p)
/* 4 for rfc1002 length field */
size = pdu_size + 4;
conn->request_buf = kvmalloc(size,
GFP_KERNEL |
__GFP_NOWARN |
__GFP_NORETRY);
conn->request_buf = kvmalloc(size, GFP_KERNEL);
if (!conn->request_buf)
break;

View File

@ -68,7 +68,7 @@ struct ksmbd_work {
/* Request is encrypted */
bool encrypted:1;
/* Is this SYNC or ASYNC ksmbd_work */
bool synchronous:1;
bool asynchronous:1;
bool need_invalidate_rkey:1;
unsigned int remote_key;

View File

@ -289,10 +289,7 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn)
work->request_buf = conn->request_buf;
conn->request_buf = NULL;
if (ksmbd_init_smb_server(work)) {
ksmbd_free_work_struct(work);
return -EINVAL;
}
ksmbd_init_smb_server(work);
ksmbd_conn_enqueue_request(work);
atomic_inc(&conn->r_count);

View File

@ -229,9 +229,6 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
struct smb2_negotiate_rsp *rsp;
struct ksmbd_conn *conn = work->conn;
if (conn->need_neg == false)
return -EINVAL;
*(__be32 *)work->response_buf =
cpu_to_be32(conn->vals->header_size);
@ -498,12 +495,6 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work)
rsp_hdr->SessionId = rcv_hdr->SessionId;
memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16);
work->synchronous = true;
if (work->async_id) {
ksmbd_release_id(&conn->async_ida, work->async_id);
work->async_id = 0;
}
return 0;
}
@ -644,7 +635,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
pr_err("Failed to alloc async message id\n");
return id;
}
work->synchronous = false;
work->asynchronous = true;
work->async_id = id;
rsp_hdr->Id.AsyncId = cpu_to_le64(id);
@ -664,6 +655,24 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
return 0;
}
void release_async_work(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
spin_lock(&conn->request_lock);
list_del_init(&work->async_request_entry);
spin_unlock(&conn->request_lock);
work->asynchronous = 0;
work->cancel_fn = NULL;
kfree(work->cancel_argv);
work->cancel_argv = NULL;
if (work->async_id) {
ksmbd_release_id(&conn->async_ida, work->async_id);
work->async_id = 0;
}
}
void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status)
{
struct smb2_hdr *rsp_hdr;
@ -7045,13 +7054,9 @@ skip:
ksmbd_vfs_posix_lock_wait(flock);
spin_lock(&work->conn->request_lock);
spin_lock(&fp->f_lock);
list_del(&work->fp_entry);
work->cancel_fn = NULL;
kfree(argv);
spin_unlock(&fp->f_lock);
spin_unlock(&work->conn->request_lock);
if (work->state != KSMBD_WORK_ACTIVE) {
list_del(&smb_lock->llist);
@ -7069,6 +7074,7 @@ skip:
work->send_no_response = 1;
goto out;
}
init_smb2_rsp_hdr(work);
smb2_set_err_rsp(work);
rsp->hdr.Status =
@ -7081,7 +7087,7 @@ skip:
spin_lock(&work->conn->llist_lock);
list_del(&smb_lock->clist);
spin_unlock(&work->conn->llist_lock);
release_async_work(work);
goto retry;
} else if (!rc) {
spin_lock(&work->conn->llist_lock);

View File

@ -486,6 +486,7 @@ int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects,
struct file_lock *smb_flock_init(struct file *f);
int setup_async_work(struct ksmbd_work *work, void (*fn)(void **),
void **arg);
void release_async_work(struct ksmbd_work *work);
void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status);
struct channel *lookup_chann_list(struct ksmbd_session *sess,
struct ksmbd_conn *conn);

View File

@ -283,20 +283,121 @@ err_out:
return BAD_PROT_ID;
}
int ksmbd_init_smb_server(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
#define SMB_COM_NEGOTIATE_EX 0x0
if (conn->need_neg == false)
/**
* get_smb1_cmd_val() - get smb command value from smb header
* @work: smb work containing smb header
*
* Return: smb command value
*/
static u16 get_smb1_cmd_val(struct ksmbd_work *work)
{
return SMB_COM_NEGOTIATE_EX;
}
/**
* init_smb1_rsp_hdr() - initialize smb negotiate response header
* @work: smb work containing smb request
*
* Return: 0 on success, otherwise -EINVAL
*/
static int init_smb1_rsp_hdr(struct ksmbd_work *work)
{
struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf;
struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf;
/*
* Remove 4 byte direct TCP header.
*/
*(__be32 *)work->response_buf =
cpu_to_be32(sizeof(struct smb_hdr) - 4);
rsp_hdr->Command = SMB_COM_NEGOTIATE;
*(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER;
rsp_hdr->Flags = SMBFLG_RESPONSE;
rsp_hdr->Flags2 = SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS |
SMBFLG2_EXT_SEC | SMBFLG2_IS_LONG_NAME;
rsp_hdr->Pid = rcv_hdr->Pid;
rsp_hdr->Mid = rcv_hdr->Mid;
return 0;
}
/**
* smb1_check_user_session() - check for valid session for a user
* @work: smb work containing smb request buffer
*
* Return: 0 on success, otherwise error
*/
static int smb1_check_user_session(struct ksmbd_work *work)
{
unsigned int cmd = work->conn->ops->get_cmd_val(work);
if (cmd == SMB_COM_NEGOTIATE_EX)
return 0;
init_smb3_11_server(conn);
return -EINVAL;
}
/**
* smb1_allocate_rsp_buf() - allocate response buffer for a command
* @work: smb work containing smb request
*
* Return: 0 on success, otherwise -ENOMEM
*/
static int smb1_allocate_rsp_buf(struct ksmbd_work *work)
{
work->response_buf = kmalloc(MAX_CIFS_SMALL_BUFFER_SIZE,
GFP_KERNEL | __GFP_ZERO);
work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;
if (!work->response_buf) {
pr_err("Failed to allocate %u bytes buffer\n",
MAX_CIFS_SMALL_BUFFER_SIZE);
return -ENOMEM;
}
if (conn->ops->get_cmd_val(work) != SMB_COM_NEGOTIATE)
conn->need_neg = false;
return 0;
}
static struct smb_version_ops smb1_server_ops = {
.get_cmd_val = get_smb1_cmd_val,
.init_rsp_hdr = init_smb1_rsp_hdr,
.allocate_rsp_buf = smb1_allocate_rsp_buf,
.check_user_session = smb1_check_user_session,
};
static int smb1_negotiate(struct ksmbd_work *work)
{
return ksmbd_smb_negotiate_common(work, SMB_COM_NEGOTIATE);
}
static struct smb_version_cmds smb1_server_cmds[1] = {
[SMB_COM_NEGOTIATE_EX] = { .proc = smb1_negotiate, },
};
static void init_smb1_server(struct ksmbd_conn *conn)
{
conn->ops = &smb1_server_ops;
conn->cmds = smb1_server_cmds;
conn->max_cmds = ARRAY_SIZE(smb1_server_cmds);
}
void ksmbd_init_smb_server(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
__le32 proto;
if (conn->need_neg == false)
return;
proto = *(__le32 *)((struct smb_hdr *)work->request_buf)->Protocol;
if (proto == SMB1_PROTO_NUMBER)
init_smb1_server(conn);
else
init_smb3_11_server(conn);
}
int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level,
struct ksmbd_file *dir,
struct ksmbd_dir_info *d_info,
@ -444,20 +545,10 @@ static int smb_handle_negotiate(struct ksmbd_work *work)
ksmbd_debug(SMB, "Unsupported SMB1 protocol\n");
/*
* Remove 4 byte direct TCP header, add 2 byte bcc and
* 2 byte DialectIndex.
*/
*(__be32 *)work->response_buf =
cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2 + 2);
/* Add 2 byte bcc and 2 byte DialectIndex. */
inc_rfc1001_len(work->response_buf, 4);
neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS;
neg_rsp->hdr.Command = SMB_COM_NEGOTIATE;
*(__le32 *)neg_rsp->hdr.Protocol = SMB1_PROTO_NUMBER;
neg_rsp->hdr.Flags = SMBFLG_RESPONSE;
neg_rsp->hdr.Flags2 = SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS |
SMBFLG2_EXT_SEC | SMBFLG2_IS_LONG_NAME;
neg_rsp->hdr.WordCount = 1;
neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect);
neg_rsp->ByteCount = 0;
@ -473,24 +564,13 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command)
ksmbd_negotiate_smb_dialect(work->request_buf);
ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
if (command == SMB2_NEGOTIATE_HE) {
struct smb2_hdr *smb2_hdr = smb2_get_msg(work->request_buf);
if (smb2_hdr->ProtocolId != SMB2_PROTO_NUMBER) {
ksmbd_debug(SMB, "Downgrade to SMB1 negotiation\n");
command = SMB_COM_NEGOTIATE;
}
}
if (command == SMB2_NEGOTIATE_HE) {
ret = smb2_handle_negotiate(work);
init_smb2_neg_rsp(work);
return ret;
}
if (command == SMB_COM_NEGOTIATE) {
if (__smb2_negotiate(conn)) {
conn->need_neg = true;
init_smb3_11_server(conn);
init_smb2_neg_rsp(work);
ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n");

View File

@ -427,7 +427,7 @@ bool ksmbd_smb_request(struct ksmbd_conn *conn);
int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count);
int ksmbd_init_smb_server(struct ksmbd_work *work);
void ksmbd_init_smb_server(struct ksmbd_work *work);
struct ksmbd_kstat;
int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work,

View File

@ -113,24 +113,6 @@ cp_convert:
goto out;
}
/*
* is_char_allowed() - check for valid character
* @ch: input character to be checked
*
* Return: 1 if char is allowed, otherwise 0
*/
static inline int is_char_allowed(char *ch)
{
/* check for control chars, wildcards etc. */
if (!(*ch & 0x80) &&
(*ch <= 0x1f ||
*ch == '?' || *ch == '"' || *ch == '<' ||
*ch == '>' || *ch == '|'))
return 0;
return 1;
}
/*
* smb_from_utf16() - convert utf16le string to local charset
* @to: destination buffer