]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ksmbd: apply the pre-authentication PDU limit when decompressing
authorSujal Tuladhar <sujaltuladhar1231@gmail.com>
Sat, 1 Aug 2026 15:53:03 +0000 (00:53 +0900)
committerSteve French <stfrench@microsoft.com>
Mon, 3 Aug 2026 16:21:22 +0000 (11:21 -0500)
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>
fs/smb/server/compress.c
fs/smb/server/connection.c
fs/smb/server/connection.h

index 7e13cae705e2f5ae77c8f0b26bbee445edc71032..01d1771ff6636b31817e3ece7b9db7a18b634643 100644 (file)
@@ -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)
index dee8e4aced9946534a4d78a5c7a7967d6b303db3..ef6f202f4024c05231775f19fd0d679ce40ddcf8 100644 (file)
@@ -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",
index 2a194ee36fb40cf55050313b22d8837f0e2c39b3..0e4ebfac5558890ca64a2f91fdbe7d7a329ca6e2 100644 (file)
@@ -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;