Skip to content

Commit

Permalink
ksmbd: check outstanding simultaneous SMB operations
Browse files Browse the repository at this point in the history
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: [email protected] # v5.15+
Reported-by: Norbert Szetei <[email protected]>
Tested-by: Norbert Szetei <[email protected]>
Signed-off-by: Namjae Jeon <[email protected]>
  • Loading branch information
namjaejeon committed Dec 2, 2024
1 parent 1ce02cc commit 28b0ceb
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 10 deletions.
1 change: 1 addition & 0 deletions connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,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;

Expand Down
1 change: 1 addition & 0 deletions connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ struct ksmbd_conn {
__le16 signing_algorithm;
bool binding;
atomic_t refcnt;
atomic_t mux_smb_requests;
};

struct ksmbd_conn_ops {
Expand Down
16 changes: 10 additions & 6 deletions server.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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");
Expand All @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions smb1ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ static struct smb_version_values smb1_server_values = {
.cap_large_files = CAP_LARGE_FILES,
.signing_enabled = SECMODE_SIGN_ENABLED,
.signing_required = SECMODE_SIGN_REQUIRED,
.max_credits = SMB2_MAX_CREDITS,
};

static struct smb_version_ops smb1_server_ops = {
Expand Down
10 changes: 7 additions & 3 deletions smb_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,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);
Expand All @@ -436,19 +440,19 @@ 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;
}
#endif

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;
Expand Down
2 changes: 1 addition & 1 deletion smb_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,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);

bool ksmbd_pdu_size_has_room(unsigned int pdu);

Expand Down

0 comments on commit 28b0ceb

Please sign in to comment.