Commit 0a77d947 authored by Namjae Jeon's avatar Namjae Jeon
Browse files

ksmbd: check outstanding simultaneous SMB operations



If Client send simultaneous SMB operations to ksmbd, It exhausts too much
memory through the "ksmbd_work_cache”. It will cause OOM issue.
ksmbd has a credit mechanism but it can't handle this problem. This patch
add the check if it exceeds max credits to prevent this problem by assuming
that one smb request consumes at least one credit.

Cc: stable@vger.kernel.org # v5.15+
Reported-by: default avatarNorbert Szetei <norbert@doyensec.com>
Tested-by: default avatarNorbert Szetei <norbert@doyensec.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent b8fc56fb
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
	atomic_set(&conn->req_running, 0);
	atomic_set(&conn->r_count, 0);
	atomic_set(&conn->refcnt, 1);
	atomic_set(&conn->mux_smb_requests, 0);
	conn->total_credits = 1;
	conn->outstanding_credits = 0;

+1 −0
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@ struct ksmbd_conn {
	__le16				signing_algorithm;
	bool				binding;
	atomic_t			refcnt;
	atomic_t			mux_smb_requests;
};

struct ksmbd_conn_ops {
+10 −6
Original line number Diff line number Diff line
@@ -270,6 +270,7 @@ static void handle_ksmbd_work(struct work_struct *wk)

	ksmbd_conn_try_dequeue_request(work);
	ksmbd_free_work_struct(work);
	atomic_dec(&conn->mux_smb_requests);
	/*
	 * Checking waitqueue to dropping pending requests on
	 * disconnection. waitqueue_active is safe because it
@@ -291,6 +292,15 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn)
	struct ksmbd_work *work;
	int err;

	err = ksmbd_init_smb_server(conn);
	if (err)
		return 0;

	if (atomic_inc_return(&conn->mux_smb_requests) >= conn->vals->max_credits) {
		atomic_dec_return(&conn->mux_smb_requests);
		return -ENOSPC;
	}

	work = ksmbd_alloc_work_struct();
	if (!work) {
		pr_err("allocation for work failed\n");
@@ -301,12 +311,6 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn)
	work->request_buf = conn->request_buf;
	conn->request_buf = NULL;

	err = ksmbd_init_smb_server(work);
	if (err) {
		ksmbd_free_work_struct(work);
		return 0;
	}

	ksmbd_conn_enqueue_request(work);
	atomic_inc(&conn->r_count);
	/* update activity on connection */
+7 −3
Original line number Diff line number Diff line
@@ -388,6 +388,10 @@ static struct smb_version_ops smb1_server_ops = {
	.set_rsp_status = set_smb1_rsp_status,
};

static struct smb_version_values smb1_server_values = {
	.max_credits = SMB2_MAX_CREDITS,
};

static int smb1_negotiate(struct ksmbd_work *work)
{
	return ksmbd_smb_negotiate_common(work, SMB_COM_NEGOTIATE);
@@ -399,18 +403,18 @@ static struct smb_version_cmds smb1_server_cmds[1] = {

static int init_smb1_server(struct ksmbd_conn *conn)
{
	conn->vals = &smb1_server_values;
	conn->ops = &smb1_server_ops;
	conn->cmds = smb1_server_cmds;
	conn->max_cmds = ARRAY_SIZE(smb1_server_cmds);
	return 0;
}

int ksmbd_init_smb_server(struct ksmbd_work *work)
int ksmbd_init_smb_server(struct ksmbd_conn *conn)
{
	struct ksmbd_conn *conn = work->conn;
	__le32 proto;

	proto = *(__le32 *)((struct smb_hdr *)work->request_buf)->Protocol;
	proto = *(__le32 *)((struct smb_hdr *)conn->request_buf)->Protocol;
	if (conn->need_neg == false) {
		if (proto == SMB1_PROTO_NUMBER)
			return -EINVAL;
+1 −1
Original line number Diff line number Diff line
@@ -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);
int ksmbd_init_smb_server(struct ksmbd_conn *conn);

struct ksmbd_kstat;
int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work,