FAILED: patch "[PATCH] smb: client: restrict implied bcc[0] exemption to responses" failed to apply to 5.15-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x 53b7c271f06be4dd5cfc8c6ef552a8355c891a7f
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026071307-payment-valium-e77f@gregkh' --subject-prefix 'PATCH 5.15.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 53b7c271f06be4dd5cfc8c6ef552a8355c891a7f Mon Sep 17 00:00:00 2001
From: Shoichiro Miyamoto <[email protected]>
Date: Tue, 7 Jul 2026 20:23:58 +0900
Subject: [PATCH] smb: client: restrict implied bcc[0] exemption to responses
without data area
smb2_check_message() has a long-standing quirk that accepts a response whose calculated length is one byte larger than the bytes actually received ("server can return one byte more due to implied bcc[0]"). This was introduced to accommodate servers that omit the trailing bcc[0] overlap byte when no data area is present.
However, the exemption is applied unconditionally, regardless of whether the command actually carries a data area (has_smb2_data_area[]). When a response with a data area is subject to the +1 exemption, the reported data can extend one byte beyond the bytes actually received, yet smb2_check_message() still accepts it. The subsequent decoder then reads past the end of the receive buffer. This is reachable during NEGOTIATE and SESSION_SETUP, before the session is established.
The resulting out-of-bounds reads are visible under KASAN when mounting against a non-conforming server; both the SPNEGO/negTokenInit and the NTLMSSP challenge decoders are affected:
BUG: KASAN: slab-out-of-bounds in asn1_ber_decoder+0x16a7/0x1b00 Read of size 1 at addr ffff8880084d67c0 by task mount.cifs/81 CPU: 1 UID: 0 PID: 81 Comm: mount.cifs Not tainted 7.1.0-rc6 #1 Call Trace: <TASK> dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 asn1_ber_decoder+0x16a7/0x1b00 decode_negTokenInit+0x19/0x30 SMB2_negotiate+0x31d9/0x4c90 cifs_negotiate_protocol+0x1f2/0x3f0 cifs_get_smb_ses+0x93f/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Allocated by task 85: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 0 bytes to the right of allocated 448-byte region [ffff8880084d6600, ffff8880084d67c0) which belongs to the cache cifs_small_rq of size 448 BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x36/0x50 Read of size 329 at addr ffff88800726c678 by task mount.cifs/89 CPU: 0 UID: 0 PID: 89 Comm: mount.cifs Tainted: G B 7.1.0-rc6 #1 Call Trace: <TASK> dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 kasan_check_range+0x10f/0x1e0 __asan_memcpy+0x23/0x60 kmemdup_noprof+0x36/0x50 decode_ntlmssp_challenge+0x457/0x680 SMB2_sess_auth_rawntlmssp_negotiate+0x6f0/0xcb0 SMB2_sess_setup+0x219/0x4f0 cifs_setup_session+0x248/0xaf0 cifs_get_smb_ses+0xf79/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Allocated by task 93: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 120 bytes inside of allocated 448-byte region [ffff88800726c600, ffff88800726c7c0) which belongs to the cache cifs_small_rq of size 448
Restrict the +1 exemption to responses that have no data area, so that it still covers the bcc[0] omission it was meant for. When a data area is present, the +1 discrepancy instead means the reported data length overruns the received buffer, so the response must be rejected.
fs/smb/client/smb2misc.c +26 -6
@@ -19,6 +19,8 @@#include "nterr.h" #include "cached_dir.h" +static unsigned int __smb2_calc_size (void *buf,bool *have_data); +static int check_smb2_hdr (struct smb2_hdr *shdr,__u64 mid) {
@@ -145,6 +147,7 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,int command;__u32 calc_len;/* calculated length */ __u64 mid; +bool have_data;/* If server is a channel, select the primary channel */ pserver =SERVER_IS_CHAN (server) ? server->primary_server : server;
@@ -228,7 +231,8 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len, } } - calc_len =smb2_calc_size (buf); + have_data = false; + calc_len =__smb2_calc_size (buf, &have_data);/* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */
@@ -247,8 +251,13 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,/* Windows 7 server returns 24 bytes more */ if (calc_len +24 == len && command ==SMB2_OPLOCK_BREAK_HE )return 0 ; -/* server can return one byte more due to implied bcc[0] */ -if (calc_len == len +1 ) +/* +* Server can return one byte more due to implied bcc[0]. +* Allow it only when there is no data area; if data_length > 0 +* the +1 gap indicates an overreported data length rather than +* the bcc[0] omission. +*/ +if (calc_len == len +1 && !have_data)return 0 ;/*
@@ -409,14 +418,17 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr)/* * Calculate the size of the SMB message based on the fixed header * portion, the number of word parameters and the data portion of the message. +* If have_data is non-NULL, it is set to true when a non-empty data area was +* found (data_length > 0), allowing callers to distinguish the implied bcc[0] +* case (no data area) from an overreported data length. */ -unsigned int -smb2_calc_size (void *buf) +static unsigned int +__smb2_calc_size (void *buf,bool *have_data) {struct smb2_pdu *pdu = buf;struct smb2_hdr *shdr = &pdu->hdr;int offset;/* the offset from the beginning of SMB to data area */ -int data_length;/* the length of the variable length data area */ +int data_length =0 ;/* the length of the variable length data area */ /* Structure Size has already been checked to make sure it is 64 */ int len =le16_to_cpu (shdr->StructureSize);
@@ -449,9 +461,17 @@ smb2_calc_size(void *buf) } calc_size_exit:cifs_dbg (FYI ,"SMB2 len %d\n" , len); +if (have_data) + *have_data = (data_length >0 );return len; } +unsigned int +smb2_calc_size (void *buf) +{ +return __smb2_calc_size (buf,NULL ); +} +/* Note: caller must free return buffer */ __le16 *cifs_convert_path_to_utf16 (const char *from,struct cifs_sb_info *cifs_sb)
Sasha Levin
maintainerPATCH 1/5 cifs: Create a new shared file holding smb2 pdu definitions
From: Ronnie Sahlberg <[email protected]>
[ Upstream commit 0d35e382e4e96a4fd97a1438bc1b11a91d2d85a6 ]
This file will contain all the definitions we need for SMB2 packets and will follow the naming convention of MS-SMB2.PDF as closely as possible to make it easier to cross-reference beween the definitions and the standard.
The content of this file will mostly consist of migration of existing definitions in the cifs/smb2.pdu.h and ksmbd/smb2pdu.h files with some additional tweaks as the two files have diverged.
This patch introduces the new smbfs_common/smb2pdu.h file and migrates the SMB2 header as well as TREE_CONNECT and TREE_DISCONNECT to the shared file.
Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area")
--- fs/cifs/cifsfs.c | 1 - fs/cifs/cifsglob.h | 3 +- fs/cifs/connect.c | 4 +- fs/cifs/misc.c | 2 +- fs/cifs/smb2maperror.c | 16 +- fs/cifs/smb2misc.c | 43 +++-- fs/cifs/smb2ops.c | 65 +++---- fs/cifs/smb2pdu.c | 106 ++++++----- fs/cifs/smb2pdu.h | 373 ++++---------------------------------- fs/cifs/smb2proto.h | 2 +- fs/cifs/smb2transport.c | 36 ++-- fs/smbfs_common/smb2pdu.h | 318 ++++++++++++++++++++++++++++++++ 12 files changed, 493 insertions(+), 476 deletions(-) create mode 100644 fs/smbfs_common/smb2pdu.h
fs/cifs/cifsfs.c +0 -1
@@ -38,7 +38,6 @@#include <linux/key-type.h> #include "cifs_spnego.h" #include "fscache.h" -#include "smb2pdu.h" #ifdef CONFIG_CIFS_DFS_UPCALL #include "dfs_cache.h" #endif
fs/cifs/cifsglob.h +2 -1
@@ -22,6 +22,7 @@#include <crypto/internal/hash.h> #include <linux/scatterlist.h> #include <uapi/linux/cifs/cifs_mount.h> +#include "../smbfs_common/smb2pdu.h" #include "smb2pdu.h" #include "smb2glob.h"
@@ -794,7 +795,7 @@ revert_current_mid(struct TCP_Server_Info *server, const unsigned int val)static inline void revert_current_mid_from_hdr (struct TCP_Server_Info *server, -const struct smb2_sync_hdr *shdr) +const struct smb2_hdr *shdr) {unsigned int num =le16_to_cpu (shdr->CreditCharge);
fs/cifs/connect.c +2 -2
@@ -722,7 +722,7 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)static unsigned int smb2_get_credits_from_hdr (char *buffer,struct TCP_Server_Info *server) { -struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer; +struct smb2_hdr *shdr = (struct smb2_hdr *)buffer;/* * SMB1 does not use credits.
@@ -926,7 +926,7 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)static void smb2_add_credits_from_hdr (char *buffer,struct TCP_Server_Info *server) { -struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer; +struct smb2_hdr *shdr = (struct smb2_hdr *)buffer;int scredits, in_flight;/*
fs/cifs/misc.c +1 -1
@@ -150,7 +150,7 @@ cifs_buf_get(void)* SMB2 header is bigger than CIFS one - no problems to clean some * more bytes for CIFS. */ -size_t buf_size =sizeof (struct smb2_sync_hdr ); +size_t buf_size =sizeof (struct smb2_hdr );/* * We could use negotiated size instead of max_msgsize -
fs/cifs/smb2maperror.c +10 -6
@@ -2439,14 +2439,16 @@ smb2_print_status(__le32 status)int map_smb2_to_linux_error (char *buf,bool log_err) { -struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; +struct smb2_hdr *shdr = (struct smb2_hdr *)buf;unsigned int i;int rc = -EIO ;__le32 smb2err = shdr->Status;if (smb2err ==0 ) { -trace_smb3_cmd_done (shdr->TreeId, shdr->SessionId, -le16_to_cpu (shdr->Command),le64_to_cpu (shdr->MessageId)); +trace_smb3_cmd_done (le32_to_cpu (shdr->Id.SyncId.TreeId), +le64_to_cpu (shdr->SessionId), +le16_to_cpu (shdr->Command), +le64_to_cpu (shdr->MessageId));return 0 ; }
@@ -2470,8 +2472,10 @@ map_smb2_to_linux_error(char *buf, bool log_err)cifs_dbg (FYI ,"Mapping SMB2 status code 0x%08x to POSIX err %d\n" ,__le32_to_cpu (smb2err), rc); -trace_smb3_cmd_err (shdr->TreeId, shdr->SessionId, -le16_to_cpu (shdr->Command), -le64_to_cpu (shdr->MessageId),le32_to_cpu (smb2err), rc); +trace_smb3_cmd_err (le32_to_cpu (shdr->Id.SyncId.TreeId), +le64_to_cpu (shdr->SessionId), +le16_to_cpu (shdr->Command), +le64_to_cpu (shdr->MessageId), +le32_to_cpu (smb2err), rc);return rc; }
fs/cifs/smb2misc.c +21 -22
119 lines not on this page · raw message
fs/cifs/smb2ops.c +33 -32
241 lines not on this page · raw message
fs/cifs/smb2pdu.c +52 -54
383 lines not on this page · raw message
fs/cifs/smb2pdu.h +35 -338
589 lines not on this page · raw message
fs/cifs/smb2proto.h +1 -1
@@ -25,7 +25,7 @@ extern int smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *server);extern unsigned int smb2_calc_size (void *buf,struct TCP_Server_Info *server);extern char *smb2_get_data_area_len (int *off,int *len, -struct smb2_sync_hdr *shdr); +struct smb2_hdr *shdr);extern __le16 *cifs_convert_path_to_utf16 (const char *from,struct cifs_sb_info *cifs_sb);
fs/cifs/smb2transport.c +18 -18
@@ -19,7 +19,6 @@#include <linux/mempool.h> #include <linux/highmem.h> #include <crypto/aead.h> -#include "smb2pdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "smb2proto.h"
@@ -213,14 +212,14 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,unsigned char smb2_signature[SMB2_HMACSHA256_SIZE ];unsigned char *sigptr = smb2_signature;struct kvec *iov = rqst->rq_iov; -struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0 ].iov_base; +struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0 ].iov_base;struct cifs_ses *ses;struct shash_desc *shash;struct crypto_shash *hash;struct sdesc *sdesc =NULL ;struct smb_rqst drqst; - ses =smb2_find_smb_ses (server, shdr->SessionId); + ses =smb2_find_smb_ses (server,le64_to_cpu (shdr->SessionId));if (unlikely (!ses)) {cifs_server_dbg (VFS ,"%s: Could not find session\n" , __func__);return -ENOENT ;
@@ -549,14 +548,14 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,unsigned char smb3_signature[SMB2_CMACAES_SIZE ];unsigned char *sigptr = smb3_signature;struct kvec *iov = rqst->rq_iov; -struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0 ].iov_base; +struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0 ].iov_base;struct shash_desc *shash;struct crypto_shash *hash;struct sdesc *sdesc =NULL ;struct smb_rqst drqst;u8 key[SMB3_SIGN_KEY_SIZE ]; - rc =smb2_get_sign_key (shdr->SessionId, server, key); + rc =smb2_get_sign_key (le64_to_cpu (shdr->SessionId), server, key);if (unlikely (rc)) {cifs_server_dbg (VFS ,"%s: Could not get signing key\n" , __func__);return rc;
@@ -628,12 +627,12 @@ static int smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) { int rc = 0; -struct smb2_sync_hdr *shdr; +struct smb2_hdr *shdr;struct smb2_sess_setup_req *ssr;bool is_binding;bool is_signed; - shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0 ].iov_base; + shdr = (struct smb2_hdr *)rqst->rq_iov[0 ].iov_base; ssr = (struct smb2_sess_setup_req *)shdr; is_binding = shdr->Command == SMB2_SESSION_SETUP &&
@@ -659,8 +658,8 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) {unsigned int rc;char server_response_sig[SMB2_SIGNATURE_SIZE ]; -struct smb2_sync_hdr *shdr = - (struct smb2_sync_hdr *)rqst->rq_iov[0 ].iov_base; +struct smb2_hdr *shdr = + (struct smb2_hdr *)rqst->rq_iov[0 ].iov_base; if ((shdr->Command == SMB2_NEGOTIATE) || (shdr->Command == SMB2_SESSION_SETUP) ||
@@ -706,7 +705,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)*/ static inline void smb2_seq_num_into_buf (struct TCP_Server_Info *server, -struct smb2_sync_hdr *shdr) +struct smb2_hdr *shdr) {unsigned int i, num =le16_to_cpu (shdr->CreditCharge);
@@ -717,7 +716,7 @@ smb2_seq_num_into_buf(struct TCP_Server_Info *server, }static struct mid_q_entry * -smb2_mid_entry_alloc (const struct smb2_sync_hdr *shdr, +smb2_mid_entry_alloc (const struct smb2_hdr *shdr,struct TCP_Server_Info *server) {struct mid_q_entry *temp;
@@ -749,14 +748,15 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,atomic_inc (&midCount); temp->mid_state =MID_REQUEST_ALLOCATED ; -trace_smb3_cmd_enter (shdr->TreeId, shdr->SessionId, -le16_to_cpu (shdr->Command), temp->mid); +trace_smb3_cmd_enter (le32_to_cpu (shdr->Id.SyncId.TreeId), +le64_to_cpu (shdr->SessionId), +le16_to_cpu (shdr->Command), temp->mid);return temp; }static int smb2_get_mid_entry (struct cifs_ses *ses,struct TCP_Server_Info *server, -struct smb2_sync_hdr *shdr,struct mid_q_entry **mid) +struct smb2_hdr *shdr,struct mid_q_entry **mid) {if (server->tcpStatus == CifsExiting)return -ENOENT ;
@@ -824,8 +824,8 @@ smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, struct smb_rqst *rqst) {int rc; -struct smb2_sync_hdr *shdr = - (struct smb2_sync_hdr *)rqst->rq_iov[0 ].iov_base; +struct smb2_hdr *shdr = + (struct smb2_hdr *)rqst->rq_iov[0 ].iov_base;struct mid_q_entry *mid;smb2_seq_num_into_buf (server, shdr);
@@ -850,8 +850,8 @@ struct mid_q_entry * smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) { int rc; -struct smb2_sync_hdr *shdr = - (struct smb2_sync_hdr *)rqst->rq_iov[0 ].iov_base; +struct smb2_hdr *shdr = + (struct smb2_hdr *)rqst->rq_iov[0 ].iov_base;struct mid_q_entry *mid; if (server->tcpStatus == CifsNeedNegotiate &&
fs/smbfs_common/smb2pdu.h new file +318 -0
318 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/5 cifs: remove check of list iterator against head past the loop body
From: Jakob Koschel <[email protected]>
[ Upstream commit 00c796eecba4898194ea549679797ee28f89a92f ]
When list_for_each_entry() completes the iteration over the whole list without breaking the loop, the iterator value will be a bogus pointer computed based on the head element.
While it is safe to use the pointer to determine if it was computed based on the head element, either with list_entry_is_head() or &pos->member == head, using the iterator variable after the loop should be avoided.
In preparation to limit the scope of a list iterator to the list traversal loop, use a dedicated pointer to point to the found element [1].
Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area")
--- fs/cifs/smb2misc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
fs/cifs/smb2misc.c +6 -4
@@ -150,16 +150,18 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr)struct smb2_transform_hdr *thdr = (struct smb2_transform_hdr *)buf;struct cifs_ses *ses =NULL ; +struct cifs_ses *iter;/* decrypt frame now that it is completely read in */ spin_lock (&cifs_tcp_ses_lock); -list_for_each_entry (ses, &srvr->smb_ses_list, smb_ses_list) { -if (ses->Suid ==le64_to_cpu (thdr->SessionId)) +list_for_each_entry (iter, &srvr->smb_ses_list, smb_ses_list) { +if (iter->Suid ==le64_to_cpu (thdr->SessionId)) { + ses = iter;break ; + } }spin_unlock (&cifs_tcp_ses_lock); -if (list_entry_is_head (ses, &srvr->smb_ses_list, - smb_ses_list)) { +if (!ses) {cifs_dbg (VFS ,"no decryption - session id not found\n" );return 1 ; }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/5 smb2: small refactor in smb2_check_message()
In reply to Sasha LevinFrom: Enzo Matsumiya <[email protected]>
[ Upstream commit da3847894fddc27ca95d5ac0012f444a77a5e0c3 ]
If the command is SMB2_IOCTL, OutputLength and OutputContext are optional and can be zero, so return early and skip calculated length check.
Move the mismatched length message to the end of the check, to avoid unnecessary logs when the check was not a real miscalculation.
Also change the pr_warn_once() to a pr_warn() so we're sure to get a log for the real mismatches.
Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area")
--- fs/cifs/connect.c | 13 ++++++------- fs/cifs/smb2misc.c | 47 +++++++++++++++++++++++++++------------------- 2 files changed, 34 insertions(+), 26 deletions(-)
fs/cifs/connect.c +6 -7
@@ -890,19 +890,18 @@ int cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) { char *buf = server->large_buf ? server->bigbuf : server->smallbuf; -int length; +int rc;/* * We know that we received enough to get to the MID as we * checked the pdu_length earlier. Now check to see -* if the rest of the header is OK. We borrow the length -* var for the rest of the loop to avoid a new stack var. +* if the rest of the header is OK. * * 48 bytes is enough to display the header and a little bit * into the payload for debugging purposes. */ - length = server->ops->check_message (buf, server->total_read, server); -if (length !=0 ) + rc = server->ops->check_message (buf, server->total_read, server); +if (rc)cifs_dump_mem ("Bad SMB: " , buf, min_t(unsigned int, server->total_read, 48));
@@ -917,9 +916,9 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)return -1 ;if (!mid) -return length; +return rc; -handle_mid (mid, server, buf, length); +handle_mid (mid, server, buf, rc);return 0 ; }
fs/cifs/smb2misc.c +28 -19
92 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/5 cifs: remove unused server parameter from calc_smb_size()
In reply to Sasha LevinFrom: Enzo Matsumiya <[email protected]>
[ Upstream commit 68ed14496b032b0c9ef21b38ee45c6c8f3a18ff1 ]
This parameter is unused by the called function
Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area")
--- fs/cifs/cifs_debug.c | 2 +- fs/cifs/cifsglob.h | 2 +- fs/cifs/cifsproto.h | 2 +- fs/cifs/misc.c | 2 +- fs/cifs/netmisc.c | 2 +- fs/cifs/readdir.c | 6 ++---- fs/cifs/smb2misc.c | 4 ++-- fs/cifs/smb2ops.c | 2 +- fs/cifs/smb2proto.h | 2 +- 9 files changed, 11 insertions(+), 13 deletions(-)
fs/cifs/cifs_debug.c +1 -1
@@ -42,7 +42,7 @@ void cifs_dump_detail(void *buf, struct TCP_Server_Info *server) smb->Command, smb->Status.CifsError, smb->Flags, smb->Flags2, smb->Mid, smb->Pid);cifs_dbg (VFS ,"smb buf %p len %u\n" , smb, - server->ops->calc_smb_size (smb, server)); + server->ops->calc_smb_size (smb));#endif /* CONFIG_CIFS_DEBUG2 */ }
fs/cifs/cifsglob.h +1 -1
@@ -389,7 +389,7 @@ struct smb_version_operations {int (*close_dir)(const unsigned int ,struct cifs_tcon *,struct cifs_fid *);/* calculate a size of SMB message */ -unsigned int (*calc_smb_size)(void *buf,struct TCP_Server_Info *ptcpi); +unsigned int (*calc_smb_size)(void *buf);/* check for STATUS_PENDING and process the response if yes */ bool (*is_status_pending)(char *buf,struct TCP_Server_Info *server);/* check for STATUS_NETWORK_SESSION_EXPIRED */
fs/cifs/cifsproto.h +1 -1
@@ -148,7 +148,7 @@ extern int cifs_get_writable_path(struct cifs_tcon *tcon, const char *name,extern struct cifsFileInfo *find_readable_file (struct cifsInodeInfo *,bool );extern int cifs_get_readable_path (struct cifs_tcon *tcon,const char *name,struct cifsFileInfo **ret_file); -extern unsigned int smbCalcSize (void *buf,struct TCP_Server_Info *server); +extern unsigned int smbCalcSize (void *buf);extern int decode_negTokenInit (unsigned char *security_blob,int length,struct TCP_Server_Info *server);extern int cifs_convert_address (struct sockaddr *dst,const char *src,int len);
fs/cifs/misc.c +1 -1
@@ -357,7 +357,7 @@ checkSMB(char *buf, unsigned int total_read, struct TCP_Server_Info *server)/* otherwise, there is enough to get to the BCC */ if (check_smb_hdr (smb))return -EIO ; - clc_len =smbCalcSize (smb, server); + clc_len =smbCalcSize (smb);if (4 + rfclen != total_read) { cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n",
fs/cifs/netmisc.c +1 -1
@@ -912,7 +912,7 @@ map_and_check_smb_error(struct mid_q_entry *mid, bool logErr)* portion, the number of word parameters and the data portion of the message */ unsigned int -smbCalcSize (void *buf,struct TCP_Server_Info *server) +smbCalcSize (void *buf) {struct smb_hdr *ptr = (struct smb_hdr *)buf; return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
fs/cifs/readdir.c +2 -4
@@ -808,8 +808,7 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, end_of_smb = cfile->srch_inf.ntwrk_buf_start + server->ops->calc_smb_size ( - cfile->srch_inf.ntwrk_buf_start, - server); + cfile->srch_inf.ntwrk_buf_start); cur_ent = cfile->srch_inf.srch_entries_start; first_entry_in_buffer = cfile->srch_inf.index_of_last_entry
@@ -1008,8 +1007,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)cifs_dbg (FYI ,"loop through %d times filling dir for net buf %p\n" , num_to_fill, cifsFile->srch_inf.ntwrk_buf_start); max_len = tcon->ses->server->ops->calc_smb_size ( - cifsFile->srch_inf.ntwrk_buf_start, - tcon->ses->server); + cifsFile->srch_inf.ntwrk_buf_start); end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; tmp_buf =kmalloc (UNICODE_NAME_MAX ,GFP_KERNEL );
fs/cifs/smb2misc.c +2 -2
@@ -221,7 +221,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) } } - calc_len =smb2_calc_size (buf, server); + calc_len =smb2_calc_size (buf);/* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */
@@ -403,7 +403,7 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr)* portion, the number of word parameters and the data portion of the message. */ unsigned int -smb2_calc_size (void *buf,struct TCP_Server_Info *srvr) +smb2_calc_size (void *buf) {struct smb2_pdu *pdu = (struct smb2_pdu *)buf;struct smb2_hdr *shdr = &pdu->hdr;
fs/cifs/smb2ops.c +1 -1
@@ -374,7 +374,7 @@ smb2_dump_detail(void *buf, struct TCP_Server_Info *server) shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, shdr->Id.SyncId.ProcessId);cifs_server_dbg (VFS ,"smb buf %p len %u\n" , buf, - server->ops->calc_smb_size (buf, server)); + server->ops->calc_smb_size (buf));#endif }
fs/cifs/smb2proto.h +1 -1
@@ -23,7 +23,7 @@ struct smb_rqst;extern int map_smb2_to_linux_error (char *buf,bool log_err);extern int smb2_check_message (char *buf,unsigned int length,struct TCP_Server_Info *server); -extern unsigned int smb2_calc_size (void *buf,struct TCP_Server_Info *server); +extern unsigned int smb2_calc_size (void *buf);extern char *smb2_get_data_area_len (int *off,int *len,struct smb2_hdr *shdr); extern __le16 *cifs_convert_path_to_utf16(const char *from,
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/5 smb: client: restrict implied bcc[0] exemption to responses without data area
In reply to Sasha LevinFrom: Shoichiro Miyamoto <[email protected]>
[ Upstream commit 53b7c271f06be4dd5cfc8c6ef552a8355c891a7f ]
smb2_check_message() has a long-standing quirk that accepts a response whose calculated length is one byte larger than the bytes actually received ("server can return one byte more due to implied bcc[0]"). This was introduced to accommodate servers that omit the trailing bcc[0] overlap byte when no data area is present.
However, the exemption is applied unconditionally, regardless of whether the command actually carries a data area (has_smb2_data_area[]). When a response with a data area is subject to the +1 exemption, the reported data can extend one byte beyond the bytes actually received, yet smb2_check_message() still accepts it. The subsequent decoder then reads past the end of the receive buffer. This is reachable during NEGOTIATE and SESSION_SETUP, before the session is established.
The resulting out-of-bounds reads are visible under KASAN when mounting against a non-conforming server; both the SPNEGO/negTokenInit and the NTLMSSP challenge decoders are affected:
BUG: KASAN: slab-out-of-bounds in asn1_ber_decoder+0x16a7/0x1b00 Read of size 1 at addr ffff8880084d67c0 by task mount.cifs/81 CPU: 1 UID: 0 PID: 81 Comm: mount.cifs Not tainted 7.1.0-rc6 #1 Call Trace: <TASK> dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 asn1_ber_decoder+0x16a7/0x1b00 decode_negTokenInit+0x19/0x30 SMB2_negotiate+0x31d9/0x4c90 cifs_negotiate_protocol+0x1f2/0x3f0 cifs_get_smb_ses+0x93f/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Allocated by task 85: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 0 bytes to the right of allocated 448-byte region [ffff8880084d6600, ffff8880084d67c0) which belongs to the cache cifs_small_rq of size 448 BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x36/0x50 Read of size 329 at addr ffff88800726c678 by task mount.cifs/89 CPU: 0 UID: 0 PID: 89 Comm: mount.cifs Tainted: G B 7.1.0-rc6 #1 Call Trace: <TASK> dump_stack_lvl+0x4e/0x70 print_report+0x157/0x4c9 kasan_report+0xce/0x100 kasan_check_range+0x10f/0x1e0 __asan_memcpy+0x23/0x60 kmemdup_noprof+0x36/0x50 decode_ntlmssp_challenge+0x457/0x680 SMB2_sess_auth_rawntlmssp_negotiate+0x6f0/0xcb0 SMB2_sess_setup+0x219/0x4f0 cifs_setup_session+0x248/0xaf0 cifs_get_smb_ses+0xf79/0x17e0 cifs_mount_get_session+0x7f/0x3a0 cifs_mount+0xb4/0xcf0 cifs_smb3_do_mount+0x23a/0x1500 smb3_get_tree+0x3b0/0x630 vfs_get_tree+0x82/0x2d0 fc_mount+0x10/0x1b0 path_mount+0x50d/0x1de0 __x64_sys_mount+0x20b/0x270 do_syscall_64+0xee/0x590 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK> Allocated by task 93: kmem_cache_alloc_noprof+0x106/0x380 mempool_alloc_noprof+0x116/0x1e0 cifs_small_buf_get+0x31/0x80 allocate_buffers+0x10d/0x2b0 cifs_demultiplex_thread+0x1d5/0x1d50 kthread+0x2c6/0x390 ret_from_fork+0x36e/0x5a0 ret_from_fork_asm+0x1a/0x30 The buggy address is located 120 bytes inside of allocated 448-byte region [ffff88800726c600, ffff88800726c7c0) which belongs to the cache cifs_small_rq of size 448
Restrict the +1 exemption to responses that have no data area, so that it still covers the bcc[0] omission it was meant for. When a data area is present, the +1 discrepancy instead means the reported data length overruns the received buffer, so the response must be rejected.
--- fs/cifs/smb2misc.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-)
fs/cifs/smb2misc.c +26 -6
@@ -17,6 +17,8 @@#include "smb2glob.h" #include "nterr.h" +static unsigned int __smb2_calc_size (void *buf,bool *have_data); +static int check_smb2_hdr (struct smb2_hdr *shdr,__u64 mid) {
@@ -141,6 +143,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)int command;__u32 calc_len;/* calculated length */ __u64 mid; +bool have_data;/* * Add function to do table lookup of StructureSize by command
@@ -221,7 +224,8 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) } } - calc_len =smb2_calc_size (buf); + have_data = false; + calc_len =__smb2_calc_size (buf, &have_data);/* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might * be 0, and not a real miscalculation */
@@ -239,8 +243,13 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)/* Windows 7 server returns 24 bytes more */ if (calc_len +24 == len && command ==SMB2_OPLOCK_BREAK_HE )return 0 ; -/* server can return one byte more due to implied bcc[0] */ -if (calc_len == len +1 ) +/* +* Server can return one byte more due to implied bcc[0]. +* Allow it only when there is no data area; if data_length > 0 +* the +1 gap indicates an overreported data length rather than +* the bcc[0] omission. +*/ +if (calc_len == len +1 && !have_data)return 0 ;/*
@@ -401,14 +410,17 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr)/* * Calculate the size of the SMB message based on the fixed header * portion, the number of word parameters and the data portion of the message. +* If have_data is non-NULL, it is set to true when a non-empty data area was +* found (data_length > 0), allowing callers to distinguish the implied bcc[0] +* case (no data area) from an overreported data length. */ -unsigned int -smb2_calc_size (void *buf) +static unsigned int +__smb2_calc_size (void *buf,bool *have_data) {struct smb2_pdu *pdu = (struct smb2_pdu *)buf;struct smb2_hdr *shdr = &pdu->hdr;int offset;/* the offset from the beginning of SMB to data area */ -int data_length;/* the length of the variable length data area */ +int data_length =0 ;/* the length of the variable length data area */ /* Structure Size has already been checked to make sure it is 64 */ int len =le16_to_cpu (shdr->StructureSize);
@@ -441,9 +453,17 @@ smb2_calc_size(void *buf) } calc_size_exit:cifs_dbg (FYI ,"SMB2 len %d\n" , len); +if (have_data) + *have_data = (data_length >0 );return len; } +unsigned int +smb2_calc_size (void *buf) +{ +return __smb2_calc_size (buf,NULL ); +} +/* Note: caller must free return buffer */ __le16 *cifs_convert_path_to_utf16 (const char *from,struct cifs_sb_info *cifs_sb)
Signature · 1 line
2.53.0