[PATCH v2] ksmbd: fix SMB2 CREATE response buffer overflow

From: Jérémy Jean

Date: Sun Sep 20 2026 - 14:17:09 EST


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 <Jeremy.Jean@xxxxxxxxxxxxxxxxx>

---

Changes in v2:
- This version implements the maintainer suggestion to precompute the sizes
for each SMB dialect.

v1: https://lore.kernel.org/all/20260919165828.2230488-2-Jeremy.Jean@xxxxxxxxxxxxxxxxx/

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 b7ce67094626..15b1b3789d9f 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -860,14 +860,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;

--
2.47.3