From 713c31a3a225b6239a37ac7fbfb394567f8b3519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Jean?= Date: Sun, 20 Sep 2026 18:15:03 +0000 Subject: [PATCH] ksmbd: fix SMB2 CREATE response buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit smb2_allocate_rsp_buf() uses the MAX_CIFS_SMALL_BUFFER_SIZE (448-byte) buffer for a single SMB2_CREATE response. That buffer also holds a 4-byte length field, which only leaves 444 bytes for the SMB2 body. The AAPL response made this buffer too small. After 8f1b796ff113, a request with AAPL response contexts may need at least 456 bytes. In the 456-byte case, create_aapl_rsp_buf() is appended last, clears 128 bytes, and writes 12 bytes past the allocation. KASAN reports: BUG: KASAN: slab-out-of-bounds in create_aapl_rsp_buf+0x31/0x6e0 Write of size 128 at addr ffff88800370954c by task kworker/0:0/9 ... create_aapl_rsp_buf+0x31/0x6e0 smb2_open+0x58f9/0xef10 ... smb2_allocate_rsp_buf+0x19d/0x370 ... The buggy address is located 332 bytes inside of allocated 448-byte region [ffff888003709400, ffff8880037095c0) Reserve enough space for any fixed CREATE response KSMBD can build. Store the required allocation size in the per-dialect values table. This keeps the small buffer for other commands and avoids using the max transaction buffer for every CREATE. Fixes: 8f1b796ff113 ("ksmbd: add AAPL kAAPL_SERVER_QUERY create context support") Assisted-by: Codex:gpt-5 Signed-off-by: Jérémy Jean Signed-off-by: Namjae Jeon --- fs/smb/common/smbglob.h | 1 + fs/smb/server/smb2ops.c | 33 +++++++++++++++++++++++++++++++++ fs/smb/server/smb2pdu.c | 6 +++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/fs/smb/common/smbglob.h b/fs/smb/common/smbglob.h index d9c7e6e7af29..fdf840888062 100644 --- a/fs/smb/common/smbglob.h +++ b/fs/smb/common/smbglob.h @@ -40,6 +40,7 @@ struct smb_version_values { size_t create_disk_id_size; size_t create_posix_size; size_t create_aapl_size; + size_t create_rsp_size; }; static inline unsigned int get_rfc1002_len(void *buf) diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c index 4578291fb172..747276b171bd 100644 --- a/fs/smb/server/smb2ops.c +++ b/fs/smb/server/smb2ops.c @@ -13,6 +13,35 @@ #include "server.h" #include "stats.h" +/* + * work->response_sz includes the RFC1002 length field while smb_get_msg() + * skips over it. Durable v1 and v2 response contexts are mutually exclusive, + * and POSIX CREATE contexts are only negotiated for SMB3.1.1. + */ +#define SMB2_CREATE_RSP_SIZE(lease_size, durable_size) \ + (sizeof(__be32) + offsetof(struct smb2_create_rsp, Buffer) + \ + (lease_size) + (durable_size) + \ + sizeof(struct create_mxac_rsp) + \ + sizeof(struct create_disk_id_rsp) + \ + AAPL_RSP_MAX_SIZE) + +#define SMB21_CREATE_RSP_SIZE \ + SMB2_CREATE_RSP_SIZE(sizeof(struct create_lease), \ + sizeof(struct create_durable_rsp)) + +#define SMB3_CREATE_DURABLE_RSP_SIZE \ + ((sizeof(struct create_durable_rsp) > \ + sizeof(struct create_durable_rsp_v2)) ? \ + sizeof(struct create_durable_rsp) : \ + sizeof(struct create_durable_rsp_v2)) + +#define SMB3_CREATE_RSP_SIZE \ + SMB2_CREATE_RSP_SIZE(sizeof(struct create_lease_v2), \ + SMB3_CREATE_DURABLE_RSP_SIZE) + +#define SMB311_CREATE_RSP_SIZE \ + (SMB3_CREATE_RSP_SIZE + sizeof(struct create_posix_rsp)) + static struct smb_version_values smb21_server_values = { .version_string = SMB21_VERSION_STRING, .protocol_id = SMB21_PROT_ID, @@ -38,6 +67,7 @@ static struct smb_version_values smb21_server_values = { .create_disk_id_size = sizeof(struct create_disk_id_rsp), .create_posix_size = sizeof(struct create_posix_rsp), .create_aapl_size = AAPL_RSP_MAX_SIZE, + .create_rsp_size = SMB21_CREATE_RSP_SIZE, }; static struct smb_version_values smb30_server_values = { @@ -66,6 +96,7 @@ static struct smb_version_values smb30_server_values = { .create_disk_id_size = sizeof(struct create_disk_id_rsp), .create_posix_size = sizeof(struct create_posix_rsp), .create_aapl_size = AAPL_RSP_MAX_SIZE, + .create_rsp_size = SMB3_CREATE_RSP_SIZE, }; static struct smb_version_values smb302_server_values = { @@ -94,6 +125,7 @@ static struct smb_version_values smb302_server_values = { .create_disk_id_size = sizeof(struct create_disk_id_rsp), .create_posix_size = sizeof(struct create_posix_rsp), .create_aapl_size = AAPL_RSP_MAX_SIZE, + .create_rsp_size = SMB3_CREATE_RSP_SIZE, }; static struct smb_version_values smb311_server_values = { @@ -122,6 +154,7 @@ static struct smb_version_values smb311_server_values = { .create_disk_id_size = sizeof(struct create_disk_id_rsp), .create_posix_size = sizeof(struct create_posix_rsp), .create_aapl_size = AAPL_RSP_MAX_SIZE, + .create_rsp_size = SMB311_CREATE_RSP_SIZE, }; static struct smb_version_ops smb2_0_server_ops = { diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 59c97a635799..45dce9c30b6b 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -855,14 +855,18 @@ static void smb2_update_lock_sequence(struct ksmbd_work *work, int smb2_allocate_rsp_buf(struct ksmbd_work *work) { struct smb2_hdr *hdr = smb_get_msg(work->request_buf); + struct smb_version_values *vals = work->conn->vals; size_t small_sz = MAX_CIFS_SMALL_BUFFER_SIZE; - size_t large_sz = small_sz + work->conn->vals->max_trans_size; + size_t large_sz = small_sz + vals->max_trans_size; size_t sz = small_sz; int cmd = le16_to_cpu(hdr->Command); if (cmd == SMB2_IOCTL_HE || cmd == SMB2_QUERY_DIRECTORY_HE) sz = large_sz; + if (cmd == SMB2_CREATE_HE) + sz = max_t(size_t, sz, vals->create_rsp_size); + if (cmd == SMB2_QUERY_INFO_HE) { struct smb2_query_info_req *req;