summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSujal Tuladhar <sujaltuladhar1231@gmail.com>2026-08-02 00:53:03 +0900
committerSteve French <stfrench@microsoft.com>2026-08-03 11:21:22 -0500
commitab88cb66cb0028cb8038b64c2fa71b0f3e91d5f2 (patch)
tree21fdb885425bced6c7062b953830cfa62e3cf109
parentba3afa8ccd154962c4a6b975b6e8b11027c5ab95 (diff)
downloadlinux-ab88cb66cb0028cb8038b64c2fa71b0f3e91d5f2.tar.gz
linux-ab88cb66cb0028cb8038b64c2fa71b0f3e91d5f2.zip
ksmbd: apply the pre-authentication PDU limit when decompressing
ksmbd_conn_handler_loop() caps a request from an unauthenticated connection at SMB3_MAX_MSGSIZE, and only allows the larger SMB3_MAX_MSGSIZE + conn->vals->max_write_size once the connection has authenticated. ksmbd_decompress_request() runs inside that same loop but applies the authenticated limit unconditionally, and then allocates from it. The unauthenticated cap is not re-applied afterwards, as the caller only refreshes pdu_size from the new RFC1002 header. An unauthenticated client that negotiates SMB 3.1.1 with a compression context can therefore send a 104 byte chained transform whose OriginalCompressedSegmentSize is SMB3_MAX_MSGSIZE + max_write_size and have ksmbd kvmalloc() that much memory, 4210693 bytes by default. The payload costs the client nothing, because a SMB3_COMPRESS_PATTERN payload expands an eight byte structure into arbitrarily many output bytes. The decompressed PDU is rejected later by ksmbd_smb2_check_message(), but that happens in the worker, after the allocation has been made and conn->req_running has been incremented, and it results in an error response rather than dropping the connection. A client that stops reading its socket keeps each work queued for up to KSMBD_TCP_SEND_TIMEOUT while ksmbd_conn_write() holds conn->srv_mutex, so the allocations accumulate up to server_conf.max_inflight_req per connection. Move the limit into ksmbd_max_allowed_pdu_size() and call it from both sites, so the authentication state is consulted in one place and the two ceilings cannot drift apart again. Fixes: a08de24c2b85 ("ksmbd: negotiate and decode SMB2 compression") Signed-off-by: Sujal Tuladhar <sujaltuladhar1231@gmail.com> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
-rw-r--r--fs/smb/server/compress.c2
-rw-r--r--fs/smb/server/connection.c6
-rw-r--r--fs/smb/server/connection.h9
3 files changed, 11 insertions, 6 deletions
diff --git a/fs/smb/server/compress.c b/fs/smb/server/compress.c
index 7e13cae705e2..01d1771ff663 100644
--- a/fs/smb/server/compress.c
+++ b/fs/smb/server/compress.c
@@ -64,7 +64,7 @@ int ksmbd_decompress_request(struct ksmbd_conn *conn)
return -EINVAL;
}
- max_allowed_pdu_size = SMB3_MAX_MSGSIZE + conn->vals->max_write_size;
+ max_allowed_pdu_size = ksmbd_max_allowed_pdu_size(conn);
if (out_size < sizeof(struct smb2_pdu) ||
out_size > max_allowed_pdu_size ||
out_size > MAX_STREAM_PROT_LEN)
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index dee8e4aced99..ef6f202f4024 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -488,11 +488,7 @@ recheck:
pdu_size = get_rfc1002_len(hdr_buf);
ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size);
- if (ksmbd_conn_good(conn))
- max_allowed_pdu_size =
- SMB3_MAX_MSGSIZE + conn->vals->max_write_size;
- else
- max_allowed_pdu_size = SMB3_MAX_MSGSIZE;
+ max_allowed_pdu_size = ksmbd_max_allowed_pdu_size(conn);
if (pdu_size > max_allowed_pdu_size) {
pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n",
diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
index 2a194ee36fb4..0e4ebfac5558 100644
--- a/fs/smb/server/connection.h
+++ b/fs/smb/server/connection.h
@@ -210,6 +210,15 @@ static inline bool ksmbd_conn_good(struct ksmbd_conn *conn)
return READ_ONCE(conn->status) == KSMBD_SESS_GOOD;
}
+static inline unsigned int
+ksmbd_max_allowed_pdu_size(struct ksmbd_conn *conn)
+{
+ if (ksmbd_conn_good(conn))
+ return SMB3_MAX_MSGSIZE + conn->vals->max_write_size;
+
+ return SMB3_MAX_MSGSIZE;
+}
+
static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn)
{
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE;