Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cifs: Use correct packet length in SMB2_TRANSFORM header

In smb3_init_transform_rq(), 'orig_len' was only counting the request
length, but forgot to count any data pages in the request.

Writing or creating files with the 'seal' mount option was broken.

In addition, do some code refactoring by exporting smb2_rqst_len() to
calculate the appropriate packet size and avoid duplicating the same
calculation all over the code.

The start of the io vector is either the rfc1002 length (4 bytes) or a
SMB2 header which is always > 4. Use this fact to check and skip the
rfc1002 length if requested.

Signed-off-by: Paulo Alcantara <palcantara@suse.de>
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Paulo Alcantara and committed by
Steve French
35e2cc1b d819d298

+24 -23
+3 -4
fs/cifs/smb2ops.c
··· 2485 2485 struct page **pages; 2486 2486 struct smb2_transform_hdr *tr_hdr; 2487 2487 unsigned int npages = old_rq->rq_npages; 2488 - unsigned int orig_len = 0; 2488 + unsigned int orig_len; 2489 2489 int i; 2490 2490 int rc = -ENOMEM; 2491 2491 ··· 2498 2498 new_rq->rq_npages = old_rq->rq_npages; 2499 2499 new_rq->rq_pagesz = old_rq->rq_pagesz; 2500 2500 new_rq->rq_tailsz = old_rq->rq_tailsz; 2501 - 2502 - for (i = 0; i < old_rq->rq_nvec; i++) 2503 - orig_len += old_rq->rq_iov[i].iov_len; 2504 2501 2505 2502 for (i = 0; i < npages; i++) { 2506 2503 pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); ··· 2520 2523 tr_hdr = kmalloc(sizeof(struct smb2_transform_hdr), GFP_KERNEL); 2521 2524 if (!tr_hdr) 2522 2525 goto err_free_iov; 2526 + 2527 + orig_len = smb2_rqst_len(old_rq, false); 2523 2528 2524 2529 /* fill the 2nd iov with a transform header */ 2525 2530 fill_transform_hdr(tr_hdr, orig_len, old_rq);
+2
fs/cifs/smb2proto.h
··· 113 113 extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); 114 114 extern void smb2_reconnect_server(struct work_struct *work); 115 115 extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); 116 + extern unsigned long 117 + smb2_rqst_len(struct smb_rqst *rqst, bool skip_rfc1002_marker); 116 118 117 119 /* 118 120 * SMB2 Worker functions - most of protocol specific implementation details
+5 -14
fs/cifs/smbdirect.c
··· 18 18 #include "smbdirect.h" 19 19 #include "cifs_debug.h" 20 20 #include "cifsproto.h" 21 + #include "smb2proto.h" 21 22 22 23 static struct smbd_response *get_empty_queue_buffer( 23 24 struct smbd_connection *info); ··· 2088 2087 struct kvec vec; 2089 2088 int nvecs; 2090 2089 int size; 2091 - unsigned int buflen = 0, remaining_data_length; 2090 + unsigned int buflen, remaining_data_length; 2092 2091 int start, i, j; 2093 2092 int max_iov_size = 2094 2093 info->max_send_size - sizeof(struct smbd_data_transfer); ··· 2112 2111 log_write(ERR, "expected the pdu length in 1st iov, but got %zu\n", rqst->rq_iov[0].iov_len); 2113 2112 return -EINVAL; 2114 2113 } 2115 - iov = &rqst->rq_iov[1]; 2116 - 2117 - /* total up iov array first */ 2118 - for (i = 0; i < rqst->rq_nvec-1; i++) { 2119 - buflen += iov[i].iov_len; 2120 - } 2121 2114 2122 2115 /* 2123 2116 * Add in the page array if there is one. The caller needs to set 2124 2117 * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and 2125 2118 * ends at page boundary 2126 2119 */ 2127 - if (rqst->rq_npages) { 2128 - if (rqst->rq_npages == 1) 2129 - buflen += rqst->rq_tailsz; 2130 - else 2131 - buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) - 2132 - rqst->rq_offset + rqst->rq_tailsz; 2133 - } 2120 + buflen = smb2_rqst_len(rqst, true); 2134 2121 2135 2122 if (buflen + sizeof(struct smbd_data_transfer) > 2136 2123 info->max_fragmented_send_size) { ··· 2127 2138 rc = -EINVAL; 2128 2139 goto done; 2129 2140 } 2141 + 2142 + iov = &rqst->rq_iov[1]; 2130 2143 2131 2144 cifs_dbg(FYI, "Sending smb (RDMA): smb_len=%u\n", buflen); 2132 2145 for (i = 0; i < rqst->rq_nvec-1; i++)
+14 -5
fs/cifs/transport.c
··· 201 201 return 0; 202 202 } 203 203 204 - static unsigned long 205 - smb2_rqst_len(struct smb_rqst *rqst) 204 + unsigned long 205 + smb2_rqst_len(struct smb_rqst *rqst, bool skip_rfc1002_marker) 206 206 { 207 207 unsigned int i; 208 - struct kvec *iov = rqst->rq_iov; 208 + struct kvec *iov; 209 + int nvec; 209 210 unsigned long buflen = 0; 210 211 212 + if (skip_rfc1002_marker && rqst->rq_iov[0].iov_len == 4) { 213 + iov = &rqst->rq_iov[1]; 214 + nvec = rqst->rq_nvec - 1; 215 + } else { 216 + iov = rqst->rq_iov; 217 + nvec = rqst->rq_nvec; 218 + } 219 + 211 220 /* total up iov array first */ 212 - for (i = 0; i < rqst->rq_nvec; i++) 221 + for (i = 0; i < nvec; i++) 213 222 buflen += iov[i].iov_len; 214 223 215 224 /* ··· 271 262 (char *)&val, sizeof(val)); 272 263 273 264 for (j = 0; j < num_rqst; j++) 274 - send_length += smb2_rqst_len(&rqst[j]); 265 + send_length += smb2_rqst_len(&rqst[j], true); 275 266 rfc1002_marker = cpu_to_be32(send_length); 276 267 277 268 /* Generate a rfc1002 marker for SMB2+ */