From 2fbb56446fde14a80790de9b182ae6f7c36a039a Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 12 Jun 2018 12:11:31 -0500 Subject: smb3: note that smb3.11 posix extensions mount option is experimental Signed-off-by: Steve French --- fs/cifs/connect.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs/cifs') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 96645a7d8f27..267c6f70cf98 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3029,8 +3029,11 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info) #ifdef CONFIG_CIFS_SMB311 if ((volume_info->linux_ext) && (ses->server->posix_ext_supported)) { - if (ses->server->vals->protocol_id == SMB311_PROT_ID) + if (ses->server->vals->protocol_id == SMB311_PROT_ID) { tcon->posix_extensions = true; + printk_once(KERN_WARNING + "SMB3.11 POSIX Extensions are experimental\n"); + } } #endif /* 311 */ -- cgit v1.2.3 From a93864d93977b99bda6c348a09b90a3d7ef8db3a Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 14 Jun 2018 06:48:35 +1000 Subject: cifs: add lease tracking to the cached root fid Use a read lease for the cached root fid so that we can detect when the content of the directory changes (via a break) at which time we close the handle. On next access to the root the handle will be reopened and cached again. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 12 +++++++++--- fs/cifs/cifsproto.h | 1 + fs/cifs/cifssmb.c | 8 ++++---- fs/cifs/misc.c | 7 ++++--- fs/cifs/smb2misc.c | 16 +++++++++++++++- fs/cifs/smb2ops.c | 34 +++++++++++++++++++++++++--------- 6 files changed, 58 insertions(+), 20 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 1efa2e65bc1a..ff71fbd619bf 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -883,6 +883,14 @@ cap_unix(struct cifs_ses *ses) return ses->server->vals->cap_unix & ses->capabilities; } +struct cached_fid { + bool is_valid:1; /* Do we have a useable root fid */ + struct cifs_fid *fid; + struct mutex fid_mutex; + struct cifs_tcon *tcon; + struct work_struct lease_break; +}; + /* * there is one of these for each connection to a resource on a particular * session @@ -987,9 +995,7 @@ struct cifs_tcon { struct fscache_cookie *fscache; /* cookie for share */ #endif struct list_head pending_opens; /* list of incomplete opens */ - bool valid_root_fid:1; /* Do we have a useable root fid */ - struct mutex prfid_mutex; /* prevents reopen race after dead ses*/ - struct cifs_fid *prfid; /* handle to the directory at top of share */ + struct cached_fid crfid; /* Cached root fid */ /* BB add field for back pointer to sb struct(s)? */ }; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 4e0d183c3d10..3a13b44069fe 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -552,6 +552,7 @@ enum securityEnum cifs_select_sectype(struct TCP_Server_Info *, struct cifs_aio_ctx *cifs_aio_ctx_alloc(void); void cifs_aio_ctx_release(struct kref *refcount); int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw); +void smb2_cached_lease_break(struct work_struct *work); int cifs_alloc_hash(const char *name, struct crypto_shash **shash, struct sdesc **sdesc); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 42329b25877d..d352da325de3 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -107,10 +107,10 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) } spin_unlock(&tcon->open_file_lock); - mutex_lock(&tcon->prfid_mutex); - tcon->valid_root_fid = false; - memset(tcon->prfid, 0, sizeof(struct cifs_fid)); - mutex_unlock(&tcon->prfid_mutex); + mutex_lock(&tcon->crfid.fid_mutex); + tcon->crfid.is_valid = false; + memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid)); + mutex_unlock(&tcon->crfid.fid_mutex); /* * BB Add call to invalidate_inodes(sb) for all superblocks mounted diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index af29ade195c0..bb40f3c050f6 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -117,8 +117,9 @@ tconInfoAlloc(void) INIT_LIST_HEAD(&ret_buf->openFileList); INIT_LIST_HEAD(&ret_buf->tcon_list); spin_lock_init(&ret_buf->open_file_lock); - mutex_init(&ret_buf->prfid_mutex); - ret_buf->prfid = kzalloc(sizeof(struct cifs_fid), GFP_KERNEL); + mutex_init(&ret_buf->crfid.fid_mutex); + ret_buf->crfid.fid = kzalloc(sizeof(struct cifs_fid), + GFP_KERNEL); #ifdef CONFIG_CIFS_STATS spin_lock_init(&ret_buf->stat_lock); #endif @@ -136,7 +137,7 @@ tconInfoFree(struct cifs_tcon *buf_to_free) atomic_dec(&tconInfoAllocCount); kfree(buf_to_free->nativeFileSystem); kzfree(buf_to_free->password); - kfree(buf_to_free->prfid); + kfree(buf_to_free->crfid.fid); kfree(buf_to_free); } diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index e2bec47c6845..0de87ca33e2e 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -492,10 +492,11 @@ cifs_ses_oplock_break(struct work_struct *work) { struct smb2_lease_break_work *lw = container_of(work, struct smb2_lease_break_work, lease_break); - int rc; + int rc = 0; rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key, lw->lease_state); + cifs_dbg(FYI, "Lease release rc %d\n", rc); cifs_put_tlink(lw->tlink); kfree(lw); @@ -561,6 +562,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, open->oplock = lease_state; } + return found; } @@ -603,6 +605,18 @@ smb2_is_valid_lease_break(char *buffer) return true; } spin_unlock(&tcon->open_file_lock); + + if (tcon->crfid.is_valid && + !memcmp(rsp->LeaseKey, + tcon->crfid.fid->lease_key, + SMB2_LEASE_KEY_SIZE)) { + INIT_WORK(&tcon->crfid.lease_break, + smb2_cached_lease_break); + queue_work(cifsiod_wq, + &tcon->crfid.lease_break); + spin_unlock(&cifs_tcp_ses_lock); + return true; + } } } } diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index b15f5957d645..682bcfa246be 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -323,6 +323,21 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) } #endif /* STATS2 */ +void +smb2_cached_lease_break(struct work_struct *work) +{ + struct cached_fid *cfid = container_of(work, + struct cached_fid, lease_break); + mutex_lock(&cfid->fid_mutex); + if (cfid->is_valid) { + cifs_dbg(FYI, "clear cached root file handle\n"); + SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid, + cfid->fid->volatile_fid); + cfid->is_valid = false; + } + mutex_unlock(&cfid->fid_mutex); +} + /* * Open the directory at the root of a share */ @@ -331,13 +346,13 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) struct cifs_open_parms oparams; int rc; __le16 srch_path = 0; /* Null - since an open of top of share */ - u8 oplock = SMB2_OPLOCK_LEVEL_NONE; + u8 oplock = SMB2_OPLOCK_LEVEL_II; - mutex_lock(&tcon->prfid_mutex); - if (tcon->valid_root_fid) { + mutex_lock(&tcon->crfid.fid_mutex); + if (tcon->crfid.is_valid) { cifs_dbg(FYI, "found a cached root file handle\n"); - memcpy(pfid, tcon->prfid, sizeof(struct cifs_fid)); - mutex_unlock(&tcon->prfid_mutex); + memcpy(pfid, tcon->crfid.fid, sizeof(struct cifs_fid)); + mutex_unlock(&tcon->crfid.fid_mutex); return 0; } @@ -350,10 +365,11 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) rc = SMB2_open(xid, &oparams, &srch_path, &oplock, NULL, NULL, NULL); if (rc == 0) { - memcpy(tcon->prfid, pfid, sizeof(struct cifs_fid)); - tcon->valid_root_fid = true; + memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid)); + tcon->crfid.tcon = tcon; + tcon->crfid.is_valid = true; } - mutex_unlock(&tcon->prfid_mutex); + mutex_unlock(&tcon->crfid.fid_mutex); return rc; } @@ -436,7 +452,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_open_parms oparms; struct cifs_fid fid; - if ((*full_path == 0) && tcon->valid_root_fid) + if ((*full_path == 0) && tcon->crfid.is_valid) return 0; utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); -- cgit v1.2.3 From d409014e4feeab486fb36b350abfc4c94de8be37 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 13 Jun 2018 17:05:58 -0500 Subject: smb3: increase initial number of credits requested to allow write Compared to other clients the Linux smb3 client ramps up credits very slowly, taking more than 128 operations before a maximum size write could be sent (since the number of credits requested is only 2 per small operation, causing the credit limit to grow very slowly). This lack of credits initially would impact large i/o performance, when large i/o is tried early before enough credits are built up. Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/smb2pdu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index af032e1a3eac..328e23abd241 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -990,8 +990,9 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) req->PreviousSessionId = sess_data->previous_session; req->Flags = 0; /* MBZ */ - /* to enable echos and oplocks */ - req->sync_hdr.CreditRequest = cpu_to_le16(3); + + /* enough to enable echos and oplocks and one max size write */ + req->sync_hdr.CreditRequest = cpu_to_le16(130); /* only one of SMB2 signing flags may be set in SMB2 request */ if (server->sign) -- cgit v1.2.3 From c713c8770fa5bfbeaac088cc7b959c7a6ba79f93 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 12 Jun 2018 08:00:58 +1000 Subject: cifs: push rfc1002 generation down the stack Move the generation of the 4 byte length field down the stack and generate it immediately before we start writing the data to the socket. Signed-off-by: Ronnie Sahlberg Signed-off-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 23 +++++++++++--- fs/cifs/cifsproto.h | 2 +- fs/cifs/smb2ops.c | 71 +++++++++++++++++------------------------- fs/cifs/smb2pdu.c | 38 ++++++++--------------- fs/cifs/smb2transport.c | 18 +++++------ fs/cifs/transport.c | 82 +++++++++++++++++++------------------------------ 6 files changed, 99 insertions(+), 135 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 937251cc61c0..f23ff848b158 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -37,7 +37,6 @@ #include int __cifs_calc_signature(struct smb_rqst *rqst, - int start, struct TCP_Server_Info *server, char *signature, struct shash_desc *shash) { @@ -45,16 +44,30 @@ int __cifs_calc_signature(struct smb_rqst *rqst, int rc; struct kvec *iov = rqst->rq_iov; int n_vec = rqst->rq_nvec; + int is_smb2 = server->vals->header_preamble_size == 0; - for (i = start; i < n_vec; i++) { + /* iov[0] is actual data and not the rfc1002 length for SMB2+ */ + if (is_smb2) { + rc = crypto_shash_update(shash, + iov[0].iov_base, iov[0].iov_len); + } else { + if (n_vec < 2 || iov[0].iov_len != 4) + return -EIO; + } + + for (i = 1; i < n_vec; i++) { if (iov[i].iov_len == 0) continue; if (iov[i].iov_base == NULL) { cifs_dbg(VFS, "null iovec entry\n"); return -EIO; } - if (i == 1 && iov[1].iov_len <= 4) - break; /* nothing to sign or corrupt header */ + if (is_smb2) { + if (i == 0 && iov[0].iov_len <= 4) + break; /* nothing to sign or corrupt header */ + } else + if (i == 1 && iov[1].iov_len <= 4) + break; /* nothing to sign or corrupt header */ rc = crypto_shash_update(shash, iov[i].iov_base, iov[i].iov_len); if (rc) { @@ -118,7 +131,7 @@ static int cifs_calc_signature(struct smb_rqst *rqst, return rc; } - return __cifs_calc_signature(rqst, 1, server, signature, + return __cifs_calc_signature(rqst, server, signature, &server->secmech.sdescmd5->shash); } diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 3a13b44069fe..4f9218281ff3 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -544,7 +544,7 @@ int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const unsigned char *path, char *pbuf, unsigned int *pbytes_written); -int __cifs_calc_signature(struct smb_rqst *rqst, int start, +int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server, char *signature, struct shash_desc *shash); enum securityEnum cifs_select_sectype(struct TCP_Server_Info *, diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 682bcfa246be..9153407f97e8 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -2167,7 +2167,7 @@ fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len, struct smb_rqst *old_rq) { struct smb2_sync_hdr *shdr = - (struct smb2_sync_hdr *)old_rq->rq_iov[1].iov_base; + (struct smb2_sync_hdr *)old_rq->rq_iov[0].iov_base; memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; @@ -2187,14 +2187,13 @@ static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, } /* Assumes: - * rqst->rq_iov[0] is rfc1002 length - * rqst->rq_iov[1] is tranform header - * rqst->rq_iov[2+] data to be encrypted/decrypted + * rqst->rq_iov[0] is tranform header + * rqst->rq_iov[1+] data to be encrypted/decrypted */ static struct scatterlist * init_sg(struct smb_rqst *rqst, u8 *sign) { - unsigned int sg_len = rqst->rq_nvec + rqst->rq_npages; + unsigned int sg_len = rqst->rq_nvec + rqst->rq_npages + 1; unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; struct scatterlist *sg; unsigned int i; @@ -2205,10 +2204,10 @@ init_sg(struct smb_rqst *rqst, u8 *sign) return NULL; sg_init_table(sg, sg_len); - smb2_sg_set_buf(&sg[0], rqst->rq_iov[1].iov_base + 20, assoc_data_len); - for (i = 1; i < rqst->rq_nvec - 1; i++) - smb2_sg_set_buf(&sg[i], rqst->rq_iov[i+1].iov_base, - rqst->rq_iov[i+1].iov_len); + smb2_sg_set_buf(&sg[0], rqst->rq_iov[0].iov_base + 20, assoc_data_len); + for (i = 1; i < rqst->rq_nvec; i++) + smb2_sg_set_buf(&sg[i], rqst->rq_iov[i].iov_base, + rqst->rq_iov[i].iov_len); for (j = 0; i < sg_len - 1; i++, j++) { unsigned int len, offset; @@ -2240,11 +2239,10 @@ smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key) return 1; } /* - * Encrypt or decrypt @rqst message. @rqst has the following format: - * iov[0] - rfc1002 length - * iov[1] - transform header (associate data), - * iov[2-N] and pages - data to encrypt. - * On success return encrypted data in iov[2-N] and pages, leave iov[0-1] + * Encrypt or decrypt @rqst message. @rqst[0] has the following format: + * iov[0] - transform header (associate data), + * iov[1-N] - SMB2 header and pages - data to encrypt. + * On success return encrypted data in iov[1-N] and pages, leave iov[0] * untouched. */ static int @@ -2339,10 +2337,6 @@ free_req: return rc; } -/* - * This is called from smb_send_rqst. At this point we have the rfc1002 - * header as the first element in the vector. - */ static int smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, struct smb_rqst *old_rq) @@ -2351,7 +2345,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, struct page **pages; struct smb2_transform_hdr *tr_hdr; unsigned int npages = old_rq->rq_npages; - unsigned int orig_len = get_rfc1002_length(old_rq->rq_iov[0].iov_base); + unsigned int orig_len = 0; int i; int rc = -ENOMEM; @@ -2365,24 +2359,23 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, new_rq->rq_pagesz = old_rq->rq_pagesz; new_rq->rq_tailsz = old_rq->rq_tailsz; + for (i = 0; i < old_rq->rq_nvec; i++) + orig_len += old_rq->rq_iov[i].iov_len; + for (i = 0; i < npages; i++) { pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); if (!pages[i]) goto err_free_pages; } - /* Make space for one extra iov to hold the transform header */ iov = kmalloc_array(old_rq->rq_nvec + 1, sizeof(struct kvec), GFP_KERNEL); if (!iov) goto err_free_pages; - /* copy all iovs from the old except the 1st one (rfc1002 length) */ - memcpy(&iov[2], &old_rq->rq_iov[1], - sizeof(struct kvec) * (old_rq->rq_nvec - 1)); - /* copy the rfc1002 iov */ - iov[0].iov_base = old_rq->rq_iov[0].iov_base; - iov[0].iov_len = old_rq->rq_iov[0].iov_len; + /* copy all iovs from the old */ + memcpy(&iov[1], &old_rq->rq_iov[0], + sizeof(struct kvec) * old_rq->rq_nvec); new_rq->rq_iov = iov; new_rq->rq_nvec = old_rq->rq_nvec + 1; @@ -2393,12 +2386,8 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, /* fill the 2nd iov with a transform header */ fill_transform_hdr(tr_hdr, orig_len, old_rq); - new_rq->rq_iov[1].iov_base = tr_hdr; - new_rq->rq_iov[1].iov_len = sizeof(struct smb2_transform_hdr); - - /* Update rfc1002 header */ - inc_rfc1001_len(new_rq->rq_iov[0].iov_base, - sizeof(struct smb2_transform_hdr)); + new_rq->rq_iov[0].iov_base = tr_hdr; + new_rq->rq_iov[0].iov_len = sizeof(struct smb2_transform_hdr); /* copy pages form the old */ for (i = 0; i < npages; i++) { @@ -2442,7 +2431,7 @@ smb3_free_transform_rq(struct smb_rqst *rqst) put_page(rqst->rq_pages[i]); kfree(rqst->rq_pages); /* free transform header */ - kfree(rqst->rq_iov[1].iov_base); + kfree(rqst->rq_iov[0].iov_base); kfree(rqst->rq_iov); } @@ -2459,19 +2448,17 @@ decrypt_raw_data(struct TCP_Server_Info *server, char *buf, unsigned int buf_data_size, struct page **pages, unsigned int npages, unsigned int page_data_size) { - struct kvec iov[3]; + struct kvec iov[2]; struct smb_rqst rqst = {NULL}; int rc; - iov[0].iov_base = NULL; - iov[0].iov_len = 0; - iov[1].iov_base = buf; - iov[1].iov_len = sizeof(struct smb2_transform_hdr); - iov[2].iov_base = buf + sizeof(struct smb2_transform_hdr); - iov[2].iov_len = buf_data_size; + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(struct smb2_transform_hdr); + iov[1].iov_base = buf + sizeof(struct smb2_transform_hdr); + iov[1].iov_len = buf_data_size; rqst.rq_iov = iov; - rqst.rq_nvec = 3; + rqst.rq_nvec = 2; rqst.rq_pages = pages; rqst.rq_npages = npages; rqst.rq_pagesz = PAGE_SIZE; @@ -2483,7 +2470,7 @@ decrypt_raw_data(struct TCP_Server_Info *server, char *buf, if (rc) return rc; - memmove(buf, iov[2].iov_base, buf_data_size); + memmove(buf, iov[1].iov_base, buf_data_size); server->total_read = buf_data_size + page_data_size; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 328e23abd241..c48608c5a0fb 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2595,11 +2595,10 @@ SMB2_echo(struct TCP_Server_Info *server) { struct smb2_echo_req *req; int rc = 0; - struct kvec iov[2]; + struct kvec iov[1]; struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = 2 }; + .rq_nvec = 1 }; unsigned int total_len; - __be32 rfc1002_marker; cifs_dbg(FYI, "In echo request\n"); @@ -2615,11 +2614,8 @@ SMB2_echo(struct TCP_Server_Info *server) req->sync_hdr.CreditRequest = cpu_to_le16(1); - iov[0].iov_len = 4; - rfc1002_marker = cpu_to_be32(total_len); - iov[0].iov_base = &rfc1002_marker; - iov[1].iov_len = total_len; - iov[1].iov_base = (char *)req; + iov[0].iov_len = total_len; + iov[0].iov_base = (char *)req; rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL, server, CIFS_ECHO_OP); @@ -2849,10 +2845,9 @@ smb2_async_readv(struct cifs_readdata *rdata) struct smb2_sync_hdr *shdr; struct cifs_io_parms io_parms; struct smb_rqst rqst = { .rq_iov = rdata->iov, - .rq_nvec = 2 }; + .rq_nvec = 1 }; struct TCP_Server_Info *server; unsigned int total_len; - __be32 req_len; cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n", __func__, rdata->offset, rdata->bytes); @@ -2883,12 +2878,8 @@ smb2_async_readv(struct cifs_readdata *rdata) if (smb3_encryption_required(io_parms.tcon)) flags |= CIFS_TRANSFORM_REQ; - req_len = cpu_to_be32(total_len); - - rdata->iov[0].iov_base = &req_len; - rdata->iov[0].iov_len = sizeof(__be32); - rdata->iov[1].iov_base = buf; - rdata->iov[1].iov_len = total_len; + rdata->iov[0].iov_base = buf; + rdata->iov[0].iov_len = total_len; shdr = (struct smb2_sync_hdr *)buf; @@ -3063,10 +3054,9 @@ smb2_async_writev(struct cifs_writedata *wdata, struct smb2_sync_hdr *shdr; struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); struct TCP_Server_Info *server = tcon->ses->server; - struct kvec iov[2]; + struct kvec iov[1]; struct smb_rqst rqst = { }; unsigned int total_len; - __be32 rfc1002_marker; rc = smb2_plain_req_init(SMB2_WRITE, tcon, (void **) &req, &total_len); if (rc) { @@ -3138,15 +3128,11 @@ smb2_async_writev(struct cifs_writedata *wdata, v1->length = cpu_to_le32(wdata->mr->mr->length); } #endif - /* 4 for rfc1002 length field and 1 for Buffer */ - iov[0].iov_len = 4; - rfc1002_marker = cpu_to_be32(total_len - 1 + wdata->bytes); - iov[0].iov_base = &rfc1002_marker; - iov[1].iov_len = total_len - 1; - iov[1].iov_base = (char *)req; + iov[0].iov_len = total_len - 1; + iov[0].iov_base = (char *)req; rqst.rq_iov = iov; - rqst.rq_nvec = 2; + rqst.rq_nvec = 1; rqst.rq_pages = wdata->pages; rqst.rq_offset = wdata->page_offset; rqst.rq_npages = wdata->nr_pages; @@ -3154,7 +3140,7 @@ smb2_async_writev(struct cifs_writedata *wdata, rqst.rq_tailsz = wdata->tailsz; #ifdef CONFIG_CIFS_SMB_DIRECT if (wdata->mr) { - iov[1].iov_len += sizeof(struct smbd_buffer_descriptor_v1); + iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1); rqst.rq_npages = 0; } #endif diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 349d5ccf854c..51b9437c3c7b 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -171,9 +171,7 @@ 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; - int iov_hdr_index = rqst->rq_nvec > 1 ? 1 : 0; - struct smb2_sync_hdr *shdr = - (struct smb2_sync_hdr *)iov[iov_hdr_index].iov_base; + struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; struct cifs_ses *ses; ses = smb2_find_smb_ses(server, shdr->SessionId); @@ -204,7 +202,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) return rc; } - rc = __cifs_calc_signature(rqst, iov_hdr_index, server, sigptr, + rc = __cifs_calc_signature(rqst, server, sigptr, &server->secmech.sdeschmacsha256->shash); if (!rc) @@ -414,9 +412,7 @@ 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; - int iov_hdr_index = rqst->rq_nvec > 1 ? 1 : 0; - struct smb2_sync_hdr *shdr = - (struct smb2_sync_hdr *)iov[iov_hdr_index].iov_base; + struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; struct cifs_ses *ses; ses = smb2_find_smb_ses(server, shdr->SessionId); @@ -447,7 +443,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) return rc; } - rc = __cifs_calc_signature(rqst, iov_hdr_index, server, sigptr, + rc = __cifs_calc_signature(rqst, server, sigptr, &server->secmech.sdesccmacaes->shash); if (!rc) @@ -462,7 +458,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) { int rc = 0; struct smb2_sync_hdr *shdr = - (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base; + (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; if (!(shdr->Flags & SMB2_FLAGS_SIGNED) || server->tcpStatus == CifsNeedNegotiate) @@ -635,7 +631,7 @@ smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst) { int rc; struct smb2_sync_hdr *shdr = - (struct smb2_sync_hdr *)rqst->rq_iov[1].iov_base; + (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; struct mid_q_entry *mid; smb2_seq_num_into_buf(ses->server, shdr); @@ -656,7 +652,7 @@ 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[1].iov_base; + (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; struct mid_q_entry *mid; smb2_seq_num_into_buf(server, shdr); diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 1f1a68f89110..63f25f919b24 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -241,13 +241,14 @@ __smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) int rc; struct kvec *iov = rqst->rq_iov; int n_vec = rqst->rq_nvec; - unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base); - unsigned long send_length; + unsigned int send_length; unsigned int i; size_t total_len = 0, sent, size; struct socket *ssocket = server->ssocket; struct msghdr smb_msg; int val = 1; + __be32 rfc1002_marker; + if (cifs_rdma_enabled(server) && server->smbd_conn) { rc = smbd_send(server->smbd_conn, rqst); goto smbd_done; @@ -255,26 +256,34 @@ __smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) if (ssocket == NULL) return -ENOTSOCK; - /* sanity check send length */ send_length = rqst_len(rqst); - if (send_length != smb_buf_length + 4) { - WARN(1, "Send length mismatch(send_length=%lu smb_buf_length=%u)\n", - send_length, smb_buf_length); - return -EIO; - } - - if (n_vec < 2) - return -EIO; - - cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length); - dump_smb(iov[0].iov_base, iov[0].iov_len); - dump_smb(iov[1].iov_base, iov[1].iov_len); + rfc1002_marker = cpu_to_be32(send_length); /* cork the socket */ kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK, (char *)&val, sizeof(val)); size = 0; + /* Generate a rfc1002 marker for SMB2+ */ + if (server->vals->header_preamble_size == 0) { + struct kvec hiov = { + .iov_base = &rfc1002_marker, + .iov_len = 4 + }; + iov_iter_kvec(&smb_msg.msg_iter, WRITE | ITER_KVEC, &hiov, + 1, 4); + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) + goto uncork; + + total_len += sent; + send_length += 4; + } + + cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length); + dump_smb(iov[0].iov_base, iov[0].iov_len); + dump_smb(iov[1].iov_base, iov[1].iov_len); + for (i = 0; i < n_vec; i++) size += iov[i].iov_len; @@ -308,9 +317,9 @@ uncork: kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK, (char *)&val, sizeof(val)); - if ((total_len > 0) && (total_len != smb_buf_length + 4)) { + if ((total_len > 0) && (total_len != send_length)) { cifs_dbg(FYI, "partial send (wanted=%u sent=%zu): terminating session\n", - smb_buf_length + 4, total_len); + send_length, total_len); /* * If we have only sent part of an SMB then the next SMB could * be taken as the remainder of this one. We need to kill the @@ -730,7 +739,6 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, * to the same server. We may make this configurable later or * use ses->maxReq. */ - rc = wait_for_free_request(ses->server, timeout, optype); if (rc) return rc; @@ -766,8 +774,8 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, #ifdef CONFIG_CIFS_SMB311 if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) - smb311_update_preauth_hash(ses, rqst->rq_iov+1, - rqst->rq_nvec-1); + smb311_update_preauth_hash(ses, rqst->rq_iov, + rqst->rq_nvec); #endif if (timeout == CIFS_ASYNC_OP) @@ -812,8 +820,8 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses, #ifdef CONFIG_CIFS_SMB311 if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) { struct kvec iov = { - .iov_base = buf, - .iov_len = midQ->resp_buf_size + .iov_base = resp_iov->iov_base, + .iov_len = resp_iov->iov_len }; smb311_update_preauth_hash(ses, &iov, 1); } @@ -879,39 +887,13 @@ smb2_send_recv(const unsigned int xid, struct cifs_ses *ses, const int flags, struct kvec *resp_iov) { struct smb_rqst rqst; - struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov; int rc; - int i; - __u32 count; - __be32 rfc1002_marker; - - if (n_vec + 1 > CIFS_MAX_IOV_SIZE) { - new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec), - GFP_KERNEL); - if (!new_iov) - return -ENOMEM; - } else - new_iov = s_iov; - - /* 1st iov is an RFC1002 Session Message length */ - memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec)); - - count = 0; - for (i = 1; i < n_vec + 1; i++) - count += new_iov[i].iov_len; - - rfc1002_marker = cpu_to_be32(count); - - new_iov[0].iov_base = &rfc1002_marker; - new_iov[0].iov_len = 4; memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = new_iov; - rqst.rq_nvec = n_vec + 1; + rqst.rq_iov = iov; + rqst.rq_nvec = n_vec; rc = cifs_send_recv(xid, ses, &rqst, resp_buf_type, flags, resp_iov); - if (n_vec + 1 > CIFS_MAX_IOV_SIZE) - kfree(new_iov); return rc; } -- cgit v1.2.3 From 40eff45b5dc7df73fc8926c9bd81bde2d4c8ccca Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 12 Jun 2018 08:00:59 +1000 Subject: cifs: remove smb2_send_recv() Now that we have the plumbing to pass request without an rfc1002 header all the way down to the point we write to the socket we no longer need the smb2_send_recv() function. Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 4 -- fs/cifs/smb2pdu.c | 137 ++++++++++++++++++++++++++++++++++++++++++++-------- fs/cifs/transport.c | 17 ------- 3 files changed, 116 insertions(+), 42 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 4f9218281ff3..03018be17283 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -112,10 +112,6 @@ extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *, struct kvec *, int /* nvec to send */, int * /* type of buf returned */, const int flags, struct kvec * /* resp vec */); -extern int smb2_send_recv(const unsigned int xid, struct cifs_ses *pses, - struct kvec *pkvec, int nvec_to_send, - int *pbuftype, const int flags, - struct kvec *presp); extern int SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *ptcon, struct smb_hdr *in_buf , diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index c48608c5a0fb..7daf38ab814a 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -602,6 +602,7 @@ static void assemble_neg_contexts(struct smb2_negotiate_req *req, int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) { + struct smb_rqst rqst; struct smb2_negotiate_req *req; struct smb2_negotiate_rsp *rsp; struct kvec iov[1]; @@ -673,7 +674,11 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; - rc = smb2_send_recv(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base; /* @@ -1028,6 +1033,7 @@ static int SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) { int rc; + struct smb_rqst rqst; struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base; struct kvec rsp_iov = { NULL, 0 }; @@ -1036,10 +1042,13 @@ SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) cpu_to_le16(sizeof(struct smb2_sess_setup_req) - 1 /* pad */); req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len); - /* BB add code to build os and lm fields */ + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = sess_data->iov; + rqst.rq_nvec = 2; - rc = smb2_send_recv(sess_data->xid, sess_data->ses, - sess_data->iov, 2, + /* BB add code to build os and lm fields */ + rc = cifs_send_recv(sess_data->xid, sess_data->ses, + &rqst, &sess_data->buf0_type, CIFS_LOG_ERROR | CIFS_NEG_OP, &rsp_iov); cifs_small_buf_release(sess_data->iov[0].iov_base); @@ -1377,6 +1386,7 @@ out: int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) { + struct smb_rqst rqst; struct smb2_logoff_req *req; /* response is also trivial struct */ int rc = 0; struct TCP_Server_Info *server; @@ -1414,7 +1424,11 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; - rc = smb2_send_recv(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); /* * No tcon so can't do @@ -1444,6 +1458,7 @@ int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, struct cifs_tcon *tcon, const struct nls_table *cp) { + struct smb_rqst rqst; struct smb2_tree_connect_req *req; struct smb2_tree_connect_rsp *rsp = NULL; struct kvec iov[2]; @@ -1500,7 +1515,11 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, !smb3_encryption_required(tcon)) req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; - rc = smb2_send_recv(xid, ses, iov, 2, &resp_buftype, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base; @@ -1564,6 +1583,7 @@ tcon_error_exit: int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) { + struct smb_rqst rqst; struct smb2_tree_disconnect_req *req; /* response is trivial */ int rc = 0; struct cifs_ses *ses = tcon->ses; @@ -1594,7 +1614,11 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; - rc = smb2_send_recv(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); if (rc) cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE); @@ -1892,6 +1916,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, __u8 *oplock, struct smb2_file_all_info *buf, struct kvec *err_iov, int *buftype) { + struct smb_rqst rqst; struct smb2_create_req *req; struct smb2_create_rsp *rsp; struct TCP_Server_Info *server; @@ -2044,7 +2069,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, } #endif /* SMB311 */ - rc = smb2_send_recv(xid, ses, iov, n_iov, &resp_buftype, flags, + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = n_iov; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; @@ -2100,6 +2129,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, char *in_data, u32 indatalen, char **out_data, u32 *plen /* returned data len */) { + struct smb_rqst rqst; struct smb2_ioctl_req *req; struct smb2_ioctl_rsp *rsp; struct cifs_ses *ses; @@ -2190,7 +2220,11 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; - rc = smb2_send_recv(xid, ses, iov, n_iov, &resp_buftype, flags, + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = n_iov; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base; @@ -2275,6 +2309,7 @@ int SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, int flags) { + struct smb_rqst rqst; struct smb2_close_req *req; struct smb2_close_rsp *rsp; struct cifs_ses *ses = tcon->ses; @@ -2302,7 +2337,11 @@ SMB2_close_flags(const unsigned int xid, struct cifs_tcon *tcon, iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; - rc = smb2_send_recv(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_close_rsp *)rsp_iov.iov_base; @@ -2388,6 +2427,7 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, u32 additional_info, size_t output_len, size_t min_len, void **data, u32 *dlen) { + struct smb_rqst rqst; struct smb2_query_info_req *req; struct smb2_query_info_rsp *rsp = NULL; struct kvec iov[2]; @@ -2428,7 +2468,11 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, /* 1 for Buffer */ iov[0].iov_len = total_len - 1; - rc = smb2_send_recv(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; @@ -2630,6 +2674,7 @@ int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) { + struct smb_rqst rqst; struct smb2_flush_req *req; struct cifs_ses *ses = tcon->ses; struct kvec iov[1]; @@ -2657,7 +2702,11 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; - rc = smb2_send_recv(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); if (rc != 0) { @@ -2918,6 +2967,7 @@ int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, unsigned int *nbytes, char **buf, int *buf_type) { + struct smb_rqst rqst; int resp_buftype, rc = -EACCES; struct smb2_read_plain_req *req = NULL; struct smb2_read_rsp *rsp = NULL; @@ -2938,7 +2988,11 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; - rc = smb2_send_recv(xid, ses, iov, 1, &resp_buftype, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_read_rsp *)rsp_iov.iov_base; @@ -3197,6 +3251,7 @@ int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, unsigned int *nbytes, struct kvec *iov, int n_vec) { + struct smb_rqst rqst; int rc = 0; struct smb2_write_req *req = NULL; struct smb2_write_rsp *rsp = NULL; @@ -3238,7 +3293,11 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, /* 1 for Buffer */ iov[0].iov_len = total_len - 1; - rc = smb2_send_recv(xid, io_parms->tcon->ses, iov, n_vec + 1, + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = n_vec + 1; + + rc = cifs_send_recv(xid, io_parms->tcon->ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; @@ -3310,6 +3369,7 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, int index, struct cifs_search_info *srch_inf) { + struct smb_rqst rqst; struct smb2_query_directory_req *req; struct smb2_query_directory_rsp *rsp = NULL; struct kvec iov[2]; @@ -3382,7 +3442,11 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, iov[1].iov_base = (char *)(req->Buffer); iov[1].iov_len = len; - rc = smb2_send_recv(xid, ses, iov, 2, &resp_buftype, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base; @@ -3441,6 +3505,7 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, u8 info_type, u32 additional_info, unsigned int num, void **data, unsigned int *size) { + struct smb_rqst rqst; struct smb2_set_info_req *req; struct smb2_set_info_rsp *rsp = NULL; struct kvec *iov; @@ -3496,7 +3561,11 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, iov[i].iov_len = size[i]; } - rc = smb2_send_recv(xid, ses, iov, num, &resp_buftype, flags, + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = num; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(req); rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base; @@ -3651,6 +3720,7 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, const u64 persistent_fid, const u64 volatile_fid, __u8 oplock_level) { + struct smb_rqst rqst; int rc; struct smb2_oplock_break *req = NULL; struct cifs_ses *ses = tcon->ses; @@ -3679,7 +3749,11 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; - rc = smb2_send_recv(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); if (rc) { @@ -3742,6 +3816,7 @@ int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata) { + struct smb_rqst rqst; struct smb2_query_info_rsp *rsp = NULL; struct kvec iov; struct kvec rsp_iov; @@ -3760,7 +3835,11 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; - rc = smb2_send_recv(xid, ses, &iov, 1, &resp_buftype, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(iov.iov_base); if (rc) { cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); @@ -3785,6 +3864,7 @@ int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, int level) { + struct smb_rqst rqst; struct smb2_query_info_rsp *rsp = NULL; struct kvec iov; struct kvec rsp_iov; @@ -3816,7 +3896,11 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, if (smb3_encryption_required(tcon)) flags |= CIFS_TRANSFORM_REQ; - rc = smb2_send_recv(xid, ses, &iov, 1, &resp_buftype, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = &iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); cifs_small_buf_release(iov.iov_base); if (rc) { cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); @@ -3855,6 +3939,7 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid, const __u32 num_lock, struct smb2_lock_element *buf) { + struct smb_rqst rqst; int rc = 0; struct smb2_lock_req *req = NULL; struct kvec iov[2]; @@ -3887,7 +3972,12 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon, iov[1].iov_len = count; cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); - rc = smb2_send_recv(xid, tcon->ses, iov, 2, &resp_buf_type, flags, + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + + rc = cifs_send_recv(xid, tcon->ses, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); if (rc) { @@ -3921,6 +4011,7 @@ int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, __u8 *lease_key, const __le32 lease_state) { + struct smb_rqst rqst; int rc; struct smb2_lease_ack *req = NULL; struct cifs_ses *ses = tcon->ses; @@ -3951,7 +4042,11 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon, iov[0].iov_base = (char *)req; iov[0].iov_len = total_len; - rc = smb2_send_recv(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov); + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 1; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buf_type, flags, &rsp_iov); cifs_small_buf_release(req); if (rc) { diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 63f25f919b24..8b922ffc2345 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -880,23 +880,6 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses, return rc; } -/* Like SendReceive2 but iov[0] does not contain an rfc1002 header */ -int -smb2_send_recv(const unsigned int xid, struct cifs_ses *ses, - struct kvec *iov, int n_vec, int *resp_buf_type /* ret */, - const int flags, struct kvec *resp_iov) -{ - struct smb_rqst rqst; - int rc; - - memset(&rqst, 0, sizeof(struct smb_rqst)); - rqst.rq_iov = iov; - rqst.rq_nvec = n_vec; - - rc = cifs_send_recv(xid, ses, &rqst, resp_buf_type, flags, resp_iov); - return rc; -} - int SendReceive(const unsigned int xid, struct cifs_ses *ses, struct smb_hdr *in_buf, struct smb_hdr *out_buf, -- cgit v1.2.3 From 07cd952f3ad51bc7f87dcdba0faac979ee559fd3 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Tue, 12 Jun 2018 08:01:00 +1000 Subject: cifs: update __smb_send_rqst() to take an array of requests Signed-off-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/transport.c | 79 +++++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 35 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 8b922ffc2345..13c244dfb3c1 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -202,7 +202,7 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, } static unsigned long -rqst_len(struct smb_rqst *rqst) +smb2_rqst_len(struct smb_rqst *rqst) { unsigned int i; struct kvec *iov = rqst->rq_iov; @@ -236,13 +236,14 @@ rqst_len(struct smb_rqst *rqst) } static int -__smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) +__smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, + struct smb_rqst *rqst) { - int rc; - struct kvec *iov = rqst->rq_iov; - int n_vec = rqst->rq_nvec; - unsigned int send_length; - unsigned int i; + int rc = 0; + struct kvec *iov; + int n_vec; + unsigned int send_length = 0; + unsigned int i, j; size_t total_len = 0, sent, size; struct socket *ssocket = server->ssocket; struct msghdr smb_msg; @@ -256,14 +257,14 @@ __smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) if (ssocket == NULL) return -ENOTSOCK; - send_length = rqst_len(rqst); - rfc1002_marker = cpu_to_be32(send_length); - /* cork the socket */ kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK, (char *)&val, sizeof(val)); - size = 0; + for (j = 0; j < num_rqst; j++) + send_length += smb2_rqst_len(&rqst[j]); + rfc1002_marker = cpu_to_be32(send_length); + /* Generate a rfc1002 marker for SMB2+ */ if (server->vals->header_preamble_size == 0) { struct kvec hiov = { @@ -280,35 +281,43 @@ __smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) send_length += 4; } - cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length); - dump_smb(iov[0].iov_base, iov[0].iov_len); - dump_smb(iov[1].iov_base, iov[1].iov_len); + for (j = 0; j < num_rqst; j++) { + iov = rqst[j].rq_iov; + n_vec = rqst[j].rq_nvec; - for (i = 0; i < n_vec; i++) - size += iov[i].iov_len; + cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length); + dump_smb(iov[0].iov_base, iov[0].iov_len); + dump_smb(iov[1].iov_base, iov[1].iov_len); - iov_iter_kvec(&smb_msg.msg_iter, WRITE | ITER_KVEC, iov, n_vec, size); - - rc = smb_send_kvec(server, &smb_msg, &sent); - if (rc < 0) - goto uncork; + size = 0; + for (i = 0; i < n_vec; i++) + size += iov[i].iov_len; - total_len += sent; + iov_iter_kvec(&smb_msg.msg_iter, WRITE | ITER_KVEC, + iov, n_vec, size); - /* now walk the page array and send each page in it */ - for (i = 0; i < rqst->rq_npages; i++) { - struct bio_vec bvec; - - bvec.bv_page = rqst->rq_pages[i]; - rqst_page_get_length(rqst, i, &bvec.bv_len, &bvec.bv_offset); - - iov_iter_bvec(&smb_msg.msg_iter, WRITE | ITER_BVEC, - &bvec, 1, bvec.bv_len); rc = smb_send_kvec(server, &smb_msg, &sent); if (rc < 0) - break; + goto uncork; total_len += sent; + + /* now walk the page array and send each page in it */ + for (i = 0; i < rqst[j].rq_npages; i++) { + struct bio_vec bvec; + + bvec.bv_page = rqst[j].rq_pages[i]; + rqst_page_get_length(&rqst[j], i, &bvec.bv_len, + &bvec.bv_offset); + + iov_iter_bvec(&smb_msg.msg_iter, WRITE | ITER_BVEC, + &bvec, 1, bvec.bv_len); + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) + break; + + total_len += sent; + } } uncork: @@ -344,7 +353,7 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst, int flags) int rc; if (!(flags & CIFS_TRANSFORM_REQ)) - return __smb_send_rqst(server, rqst); + return __smb_send_rqst(server, 1, rqst); if (!server->ops->init_transform_rq || !server->ops->free_transform_rq) { @@ -356,7 +365,7 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst, int flags) if (rc) return rc; - rc = __smb_send_rqst(server, &cur_rqst); + rc = __smb_send_rqst(server, 1, &cur_rqst); server->ops->free_transform_rq(&cur_rqst); return rc; } @@ -374,7 +383,7 @@ smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer, iov[1].iov_base = (char *)smb_buffer + 4; iov[1].iov_len = smb_buf_length; - return __smb_send_rqst(server, &rqst); + return __smb_send_rqst(server, 1, &rqst); } static int -- cgit v1.2.3 From cd2dca60be6f4742b63373fd7e4b445c8a634e51 Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Wed, 13 Jun 2018 13:54:14 -0300 Subject: cifs: Fix encryption/signing Since the rfc1002 generation was moved down to __smb_send_rqst(), the transform header is now in rqst->rq_iov[0]. Correctly assign the transform header pointer in crypt_message(). Signed-off-by: Paulo Alcantara Reviewed-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 9153407f97e8..c3648e9b5ec7 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -2187,7 +2187,7 @@ static inline void smb2_sg_set_buf(struct scatterlist *sg, const void *buf, } /* Assumes: - * rqst->rq_iov[0] is tranform header + * rqst->rq_iov[0] is transform header * rqst->rq_iov[1+] data to be encrypted/decrypted */ static struct scatterlist * @@ -2249,7 +2249,7 @@ static int crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc) { struct smb2_transform_hdr *tr_hdr = - (struct smb2_transform_hdr *)rqst->rq_iov[1].iov_base; + (struct smb2_transform_hdr *)rqst->rq_iov[0].iov_base; unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20; int rc = 0; struct scatterlist *sg; -- cgit v1.2.3 From e2292430c49dbbe2d54438a4c05dd937a8eeecdd Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Thu, 14 Jun 2018 15:43:16 +0200 Subject: CIFS: move default port definitions to cifsglob.h Signed-off-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 3 +++ fs/cifs/connect.c | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index ff71fbd619bf..ff280447dae5 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -33,6 +33,9 @@ #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ +#define CIFS_PORT 445 +#define RFC1001_PORT 139 + /* * The sizes of various internal tables and strings */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 267c6f70cf98..a57da1b88bdf 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -57,9 +57,6 @@ #include "smb2proto.h" #include "smbdirect.h" -#define CIFS_PORT 445 -#define RFC1001_PORT 139 - extern mempool_t *cifs_req_poolp; extern bool disable_legacy_dialects; -- cgit v1.2.3 From bead042cccca5a7c5626b851b66a30698aa0ac36 Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Thu, 14 Jun 2018 15:43:17 +0200 Subject: CIFS: complete PDU definitions for interface queries Signed-off-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/smb2pdu.h | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index a345560001ce..824dddeee3f2 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -851,8 +851,11 @@ struct validate_negotiate_info_rsp { __le16 Dialect; /* Dialect in use for the connection */ } __packed; -#define RSS_CAPABLE 0x00000001 -#define RDMA_CAPABLE 0x00000002 +#define RSS_CAPABLE cpu_to_le32(0x00000001) +#define RDMA_CAPABLE cpu_to_le32(0x00000002) + +#define INTERNETWORK cpu_to_le16(0x0002) +#define INTERNETWORKV6 cpu_to_le16(0x0017) struct network_interface_info_ioctl_rsp { __le32 Next; /* next interface. zero if this is last one */ @@ -860,7 +863,21 @@ struct network_interface_info_ioctl_rsp { __le32 Capability; /* RSS or RDMA Capable */ __le32 Reserved; __le64 LinkSpeed; - char SockAddr_Storage[128]; + __le16 Family; + __u8 Buffer[126]; +} __packed; + +struct iface_info_ipv4 { + __be16 Port; + __be32 IPv4Address; + __be64 Reserved; +} __packed; + +struct iface_info_ipv6 { + __be16 Port; + __be32 FlowInfo; + __u8 IPv6Address[16]; + __be32 ScopeId; } __packed; #define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ -- cgit v1.2.3 From b6f0dd5d75f9689d57c7ea49e52c80cabb876cb4 Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Thu, 14 Jun 2018 15:43:18 +0200 Subject: CIFS: add iface info to struct cifs_ses Signed-off-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 21 +++++++++++++++++++++ fs/cifs/misc.c | 2 ++ 2 files changed, 23 insertions(+) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index ff280447dae5..9dd5f1a3d64b 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -841,6 +841,13 @@ static inline void cifs_set_net_ns(struct TCP_Server_Info *srv, struct net *net) #endif +struct cifs_server_iface { + size_t speed; + unsigned int rdma_capable : 1; + unsigned int rss_capable : 1; + struct sockaddr_storage sockaddr; +}; + /* * Session structure. One of these for each uid session with a particular host */ @@ -878,6 +885,20 @@ struct cifs_ses { #ifdef CONFIG_CIFS_SMB311 __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; #endif /* 3.1.1 */ + + /* + * Network interfaces available on the server this session is + * connected to. + * + * Other channels can be opened by connecting and binding this + * session to interfaces from this list. + * + * iface_lock should be taken when accessing any of these fields + */ + spinlock_t iface_lock; + struct cifs_server_iface *iface_list; + size_t iface_count; + unsigned long iface_last_update; /* jiffies */ }; static inline bool diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index bb40f3c050f6..53e8362cbc4a 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -82,6 +82,7 @@ sesInfoAlloc(void) INIT_LIST_HEAD(&ret_buf->smb_ses_list); INIT_LIST_HEAD(&ret_buf->tcon_list); mutex_init(&ret_buf->session_mutex); + spin_lock_init(&ret_buf->iface_lock); } return ret_buf; } @@ -102,6 +103,7 @@ sesInfoFree(struct cifs_ses *buf_to_free) kfree(buf_to_free->user_name); kfree(buf_to_free->domainName); kzfree(buf_to_free->auth_key.response); + kfree(buf_to_free->iface_list); kzfree(buf_to_free); } -- cgit v1.2.3 From fe856be475f7cf5ffcde57341d175ce9fd09434b Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Thu, 14 Jun 2018 17:04:51 +0200 Subject: CIFS: parse and store info on iface queries Signed-off-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 155 insertions(+), 15 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index c3648e9b5ec7..b2390e9a6843 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -294,34 +294,176 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) return rsize; } -#ifdef CONFIG_CIFS_STATS2 + +static int +parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf, + size_t buf_len, + struct cifs_server_iface **iface_list, + size_t *iface_count) +{ + struct network_interface_info_ioctl_rsp *p; + struct sockaddr_in *addr4; + struct sockaddr_in6 *addr6; + struct iface_info_ipv4 *p4; + struct iface_info_ipv6 *p6; + struct cifs_server_iface *info; + ssize_t bytes_left; + size_t next = 0; + int nb_iface = 0; + int rc = 0; + + *iface_list = NULL; + *iface_count = 0; + + /* + * Fist pass: count and sanity check + */ + + bytes_left = buf_len; + p = buf; + while (bytes_left >= sizeof(*p)) { + nb_iface++; + next = le32_to_cpu(p->Next); + if (!next) { + bytes_left -= sizeof(*p); + break; + } + p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); + bytes_left -= next; + } + + if (!nb_iface) { + cifs_dbg(VFS, "%s: malformed interface info\n", __func__); + rc = -EINVAL; + goto out; + } + + if (bytes_left || p->Next) + cifs_dbg(VFS, "%s: incomplete interface info\n", __func__); + + + /* + * Second pass: extract info to internal structure + */ + + *iface_list = kcalloc(nb_iface, sizeof(**iface_list), GFP_KERNEL); + if (!*iface_list) { + rc = -ENOMEM; + goto out; + } + + info = *iface_list; + bytes_left = buf_len; + p = buf; + while (bytes_left >= sizeof(*p)) { + info->speed = le64_to_cpu(p->LinkSpeed); + info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE); + info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE); + + cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count); + cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed); + cifs_dbg(FYI, "%s: capabilities 0x%08x\n", __func__, + le32_to_cpu(p->Capability)); + + switch (p->Family) { + /* + * The kernel and wire socket structures have the same + * layout and use network byte order but make the + * conversion explicit in case either one changes. + */ + case INTERNETWORK: + addr4 = (struct sockaddr_in *)&info->sockaddr; + p4 = (struct iface_info_ipv4 *)p->Buffer; + addr4->sin_family = AF_INET; + memcpy(&addr4->sin_addr, &p4->IPv4Address, 4); + + /* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */ + addr4->sin_port = cpu_to_be16(CIFS_PORT); + + cifs_dbg(FYI, "%s: ipv4 %pI4\n", __func__, + &addr4->sin_addr); + break; + case INTERNETWORKV6: + addr6 = (struct sockaddr_in6 *)&info->sockaddr; + p6 = (struct iface_info_ipv6 *)p->Buffer; + addr6->sin6_family = AF_INET6; + memcpy(&addr6->sin6_addr, &p6->IPv6Address, 16); + + /* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */ + addr6->sin6_flowinfo = 0; + addr6->sin6_scope_id = 0; + addr6->sin6_port = cpu_to_be16(CIFS_PORT); + + cifs_dbg(FYI, "%s: ipv6 %pI6\n", __func__, + &addr6->sin6_addr); + break; + default: + cifs_dbg(VFS, + "%s: skipping unsupported socket family\n", + __func__); + goto next_iface; + } + + (*iface_count)++; + info++; +next_iface: + next = le32_to_cpu(p->Next); + if (!next) + break; + p = (struct network_interface_info_ioctl_rsp *)((u8 *)p+next); + bytes_left -= next; + } + + if (!*iface_count) { + rc = -EINVAL; + goto out; + } + +out: + if (rc) { + kfree(*iface_list); + *iface_count = 0; + *iface_list = NULL; + } + return rc; +} + + static int SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) { int rc; unsigned int ret_data_len = 0; - struct network_interface_info_ioctl_rsp *out_buf; + struct network_interface_info_ioctl_rsp *out_buf = NULL; + struct cifs_server_iface *iface_list; + size_t iface_count; + struct cifs_ses *ses = tcon->ses; rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */, NULL /* no data input */, 0 /* no data input */, (char **)&out_buf, &ret_data_len); - if (rc != 0) + if (rc != 0) { cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc); - else if (ret_data_len < sizeof(struct network_interface_info_ioctl_rsp)) { - cifs_dbg(VFS, "server returned bad net interface info buf\n"); - rc = -EINVAL; - } else { - /* Dump info on first interface */ - cifs_dbg(FYI, "Adapter Capability 0x%x\t", - le32_to_cpu(out_buf->Capability)); - cifs_dbg(FYI, "Link Speed %lld\n", - le64_to_cpu(out_buf->LinkSpeed)); + goto out; } + + rc = parse_server_interfaces(out_buf, ret_data_len, + &iface_list, &iface_count); + if (rc) + goto out; + + spin_lock(&ses->iface_lock); + kfree(ses->iface_list); + ses->iface_list = iface_list; + ses->iface_count = iface_count; + ses->iface_last_update = jiffies; + spin_unlock(&ses->iface_lock); + +out: kfree(out_buf); return rc; } -#endif /* STATS2 */ void smb2_cached_lease_break(struct work_struct *work) @@ -399,9 +541,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) if (rc) return; -#ifdef CONFIG_CIFS_STATS2 SMB3_request_interfaces(xid, tcon); -#endif /* STATS2 */ SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, FS_ATTRIBUTE_INFORMATION); -- cgit v1.2.3 From bc0fe8b207a14a6d7ecf8812edb92c8bdd5c9b2d Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Thu, 14 Jun 2018 15:43:20 +0200 Subject: CIFS: dump every session iface info Signed-off-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/cifs_debug.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'fs/cifs') diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 116146022aa1..e8db245194e7 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -126,6 +126,25 @@ static void cifs_debug_tcon(struct seq_file *m, struct cifs_tcon *tcon) seq_putc(m, '\n'); } +static void +cifs_dump_iface(struct seq_file *m, struct cifs_server_iface *iface) +{ + struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr; + + seq_printf(m, "\t\tSpeed: %zu bps\n", iface->speed); + seq_puts(m, "\t\tCapabilities: "); + if (iface->rdma_capable) + seq_puts(m, "rdma "); + if (iface->rss_capable) + seq_puts(m, "rss "); + seq_putc(m, '\n'); + if (iface->sockaddr.ss_family == AF_INET) + seq_printf(m, "\t\tIPv4: %pI4\n", &ipv4->sin_addr); + else if (iface->sockaddr.ss_family == AF_INET6) + seq_printf(m, "\t\tIPv6: %pI6\n", &ipv6->sin6_addr); +} + static int cifs_debug_data_proc_show(struct seq_file *m, void *v) { struct list_head *tmp1, *tmp2, *tmp3; @@ -312,6 +331,14 @@ skip_rdma: mid_entry->mid); } spin_unlock(&GlobalMid_Lock); + + spin_lock(&ses->iface_lock); + seq_printf(m, "\n\tServer interfaces: %zu\n", ses->iface_count); + for (j = 0; j < ses->iface_count; j++) { + seq_printf(m, "\t%d)\n", j); + cifs_dump_iface(m, &ses->iface_list[j]); + } + spin_unlock(&ses->iface_lock); } } spin_unlock(&cifs_tcp_ses_lock); -- cgit v1.2.3 From 662bf5bc0a6f7b2abf7f9125c6319f06bb2efcf9 Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Thu, 14 Jun 2018 17:34:08 -0300 Subject: cifs: Fix kernel oops when traceSMB is enabled When traceSMB is enabled through 'echo 1 > /proc/fs/cifs/traceSMB', after a mount, the following oops is triggered: [ 27.137943] BUG: unable to handle kernel paging request at ffff8800f80c268b [ 27.143396] PGD 2c6b067 P4D 2c6b067 PUD 0 [ 27.145386] Oops: 0000 [#1] SMP PTI [ 27.146186] CPU: 2 PID: 2655 Comm: mount.cifs Not tainted 4.17.0+ #39 [ 27.147174] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.0.0-prebuilt.qemu-project.org 04/01/2014 [ 27.148969] RIP: 0010:hex_dump_to_buffer+0x413/0x4b0 [ 27.149738] Code: 48 8b 44 24 08 31 db 45 31 d2 48 89 6c 24 18 44 89 6c 24 24 48 c7 c1 78 b5 23 82 4c 89 64 24 10 44 89 d5 41 89 dc 4c 8d 58 02 <44> 0f b7 00 4d 89 dd eb 1f 83 c5 01 41 01 c4 41 39 ef 0f 84 48 fe [ 27.152396] RSP: 0018:ffffc9000058f8c0 EFLAGS: 00010246 [ 27.153129] RAX: ffff8800f80c268b RBX: 0000000000000000 RCX: ffffffff8223b578 [ 27.153867] RDX: 0000000000000000 RSI: ffffffff81a55496 RDI: 0000000000000008 [ 27.154612] RBP: 0000000000000000 R08: 0000000000000020 R09: 0000000000000083 [ 27.155355] R10: 0000000000000000 R11: ffff8800f80c268d R12: 0000000000000000 [ 27.156101] R13: 0000000000000002 R14: ffffc9000058f94d R15: 0000000000000008 [ 27.156838] FS: 00007f1693a6b740(0000) GS:ffff88007fd00000(0000) knlGS:0000000000000000 [ 27.158354] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 27.159093] CR2: ffff8800f80c268b CR3: 00000000798fa001 CR4: 0000000000360ee0 [ 27.159892] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 27.160661] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 27.161464] Call Trace: [ 27.162123] print_hex_dump+0xd3/0x160 [ 27.162814] journal-offline (2658) used greatest stack depth: 13144 bytes left [ 27.162824] ? __release_sock+0x60/0xd0 [ 27.165344] ? tcp_sendmsg+0x31/0x40 [ 27.166177] dump_smb+0x39/0x40 [ 27.166972] ? vsnprintf+0x236/0x490 [ 27.167807] __smb_send_rqst.constprop.12+0x103/0x430 [ 27.168554] ? apic_timer_interrupt+0xa/0x20 [ 27.169306] smb_send_rqst+0x48/0xc0 [ 27.169984] cifs_send_recv+0xda/0x420 [ 27.170639] SMB2_negotiate+0x23d/0xfa0 [ 27.171301] ? vsnprintf+0x236/0x490 [ 27.171961] ? smb2_negotiate+0x19/0x30 [ 27.172586] smb2_negotiate+0x19/0x30 [ 27.173257] cifs_negotiate_protocol+0x70/0xd0 [ 27.173935] ? kstrdup+0x43/0x60 [ 27.174551] cifs_get_smb_ses+0x295/0xbe0 [ 27.175260] ? lock_timer_base+0x67/0x80 [ 27.175936] ? __internal_add_timer+0x1a/0x50 [ 27.176575] ? add_timer+0x10f/0x230 [ 27.177267] cifs_mount+0x101/0x1190 [ 27.177940] ? cifs_smb3_do_mount+0x144/0x5c0 [ 27.178575] cifs_smb3_do_mount+0x144/0x5c0 [ 27.179270] mount_fs+0x35/0x150 [ 27.179930] vfs_kern_mount.part.28+0x54/0xf0 [ 27.180567] do_mount+0x5ad/0xc40 [ 27.181234] ? kmem_cache_alloc_trace+0xed/0x1a0 [ 27.181916] ksys_mount+0x80/0xd0 [ 27.182535] __x64_sys_mount+0x21/0x30 [ 27.183220] do_syscall_64+0x4e/0x100 [ 27.183882] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 27.184535] RIP: 0033:0x7f169339055a [ 27.185192] Code: 48 8b 0d 41 d9 2b 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 49 89 ca b8 a5 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 0e d9 2b 00 f7 d8 64 89 01 48 [ 27.187268] RSP: 002b:00007fff7b44eb58 EFLAGS: 00000202 ORIG_RAX: 00000000000000a5 [ 27.188515] RAX: ffffffffffffffda RBX: 00007f1693a7e70e RCX: 00007f169339055a [ 27.189244] RDX: 000055b9f97f64e5 RSI: 000055b9f97f652c RDI: 00007fff7b45074f [ 27.189974] RBP: 000055b9fb8c9260 R08: 000055b9fb8ca8f0 R09: 0000000000000000 [ 27.190721] R10: 0000000000000000 R11: 0000000000000202 R12: 000055b9fb8ca8f0 [ 27.191429] R13: 0000000000000000 R14: 00007f1693a7c000 R15: 00007f1693a7e91d [ 27.192167] Modules linked in: [ 27.192797] CR2: ffff8800f80c268b [ 27.193435] ---[ end trace 67404c618badf323 ]--- The problem was that dump_smb() had been called with an invalid pointer, that is, in __smb_send_rqst(), iov[1] doesn't exist (n_vec == 1). This patch fixes it by relying on the n_vec value to dump out the smb packets. Signed-off-by: Paulo Alcantara Signed-off-by: Steve French Reviewed-by: Ronnie Sahlberg --- fs/cifs/transport.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 13c244dfb3c1..a3ea42a4cb98 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -281,17 +281,17 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, send_length += 4; } + cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length); + for (j = 0; j < num_rqst; j++) { iov = rqst[j].rq_iov; n_vec = rqst[j].rq_nvec; - cifs_dbg(FYI, "Sending smb: smb_len=%u\n", send_length); - dump_smb(iov[0].iov_base, iov[0].iov_len); - dump_smb(iov[1].iov_base, iov[1].iov_len); - size = 0; - for (i = 0; i < n_vec; i++) + for (i = 0; i < n_vec; i++) { + dump_smb(iov[i].iov_base, iov[i].iov_len); size += iov[i].iov_len; + } iov_iter_kvec(&smb_msg.msg_iter, WRITE | ITER_KVEC, iov, n_vec, size); -- cgit v1.2.3 From bea851b8babe6c87c36e97c9de0dd0bea0dd5802 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 14 Jun 2018 21:56:32 -0500 Subject: smb3: Fix mode on mkdir on smb311 mounts mkdir was not passing the mode on smb3.11 mounts with posix extensions Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 4 ++ fs/cifs/inode.c | 13 ++++- fs/cifs/smb2ops.c | 1 + fs/cifs/smb2pdu.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/smb2proto.h | 4 ++ fs/cifs/trace.h | 3 +- 6 files changed, 175 insertions(+), 3 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9dd5f1a3d64b..bd78da59a4fd 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -315,6 +315,10 @@ struct smb_version_operations { /* send echo request */ int (*echo)(struct TCP_Server_Info *); /* create directory */ + int (*posix_mkdir)(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *full_path, + struct cifs_sb_info *cifs_sb); int (*mkdir)(const unsigned int, struct cifs_tcon *, const char *, struct cifs_sb_info *); /* set info on created directory */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index f4697f548a39..a2cfb33e85c1 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1575,6 +1575,17 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) goto mkdir_out; } + server = tcon->ses->server; + +#ifdef CONFIG_CIFS_SMB311 + if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) { + rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path, + cifs_sb); + d_drop(direntry); /* for time being always refresh inode info */ + goto mkdir_out; + } +#endif /* SMB311 */ + if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb, @@ -1583,8 +1594,6 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) goto mkdir_out; } - server = tcon->ses->server; - if (!server->ops->mkdir) { rc = -ENOSYS; goto mkdir_out; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index b2390e9a6843..badcfb2f3c22 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -3313,6 +3313,7 @@ struct smb_version_operations smb311_operations = { .set_compression = smb2_set_compression, .mkdir = smb2_mkdir, .mkdir_setinfo = smb2_mkdir_setinfo, + .posix_mkdir = smb311_posix_mkdir, .rmdir = smb2_rmdir, .unlink = smb2_unlink, .rename = smb2_rename_path, diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 7daf38ab814a..810b85787c91 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1911,6 +1911,159 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, return 0; } +#ifdef CONFIG_CIFS_SMB311 +int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *full_path, + struct cifs_sb_info *cifs_sb) +{ + struct smb_rqst rqst; + struct smb2_create_req *req; + struct smb2_create_rsp *rsp; + struct TCP_Server_Info *server; + struct cifs_ses *ses = tcon->ses; + struct kvec iov[3]; /* make sure at least one for each open context */ + struct kvec rsp_iov = {NULL, 0}; + int resp_buftype; + int uni_path_len; + __le16 *copy_path = NULL; + int copy_size; + int rc = 0; + unsigned int n_iov = 2; + __u32 file_attributes = 0; + char *pc_buf = NULL; + int flags = 0; + unsigned int total_len; + __le16 *path = cifs_convert_path_to_utf16(full_path, cifs_sb); + + if (!path) + return -ENOMEM; + + cifs_dbg(FYI, "mkdir\n"); + + if (ses && (ses->server)) + server = ses->server; + else + return -EIO; + + rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len); + + if (rc) + return rc; + + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + + + req->ImpersonationLevel = IL_IMPERSONATION; + req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); + /* File attributes ignored on open (used in create though) */ + req->FileAttributes = cpu_to_le32(file_attributes); + req->ShareAccess = FILE_SHARE_ALL_LE; + req->CreateDisposition = cpu_to_le32(FILE_CREATE); + req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE); + + iov[0].iov_base = (char *)req; + /* -1 since last byte is buf[0] which is sent below (path) */ + iov[0].iov_len = total_len - 1; + + req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); + + /* [MS-SMB2] 2.2.13 NameOffset: + * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of + * the SMB2 header, the file name includes a prefix that will + * be processed during DFS name normalization as specified in + * section 3.3.5.9. Otherwise, the file name is relative to + * the share that is identified by the TreeId in the SMB2 + * header. + */ + if (tcon->share_flags & SHI1005_FLAGS_DFS) { + int name_len; + + req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; + rc = alloc_path_with_tree_prefix(©_path, ©_size, + &name_len, + tcon->treeName, path); + if (rc) { + cifs_small_buf_release(req); + return rc; + } + req->NameLength = cpu_to_le16(name_len * 2); + uni_path_len = copy_size; + path = copy_path; + } else { + uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; + /* MUST set path len (NameLength) to 0 opening root of share */ + req->NameLength = cpu_to_le16(uni_path_len - 2); + if (uni_path_len % 8 != 0) { + copy_size = roundup(uni_path_len, 8); + copy_path = kzalloc(copy_size, GFP_KERNEL); + if (!copy_path) { + cifs_small_buf_release(req); + return -ENOMEM; + } + memcpy((char *)copy_path, (const char *)path, + uni_path_len); + uni_path_len = copy_size; + path = copy_path; + } + } + + iov[1].iov_len = uni_path_len; + iov[1].iov_base = path; + req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; + + if (tcon->posix_extensions) { + if (n_iov > 2) { + struct create_context *ccontext = + (struct create_context *)iov[n_iov-1].iov_base; + ccontext->Next = + cpu_to_le32(iov[n_iov-1].iov_len); + } + + rc = add_posix_context(iov, &n_iov, mode); + if (rc) { + cifs_small_buf_release(req); + kfree(copy_path); + return rc; + } + pc_buf = iov[n_iov-1].iov_base; + } + + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = n_iov; + + rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, + &rsp_iov); + + cifs_small_buf_release(req); + rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; + + if (rc != 0) { + cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); + trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, + CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES, rc); + goto smb311_mkdir_exit; + } else + trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, + ses->Suid, CREATE_NOT_FILE, + FILE_WRITE_ATTRIBUTES); + + SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); + + /* Eventually save off posix specific response info and timestaps */ + +smb311_mkdir_exit: + kfree(copy_path); + kfree(pc_buf); + free_rsp_buf(resp_buftype, rsp); + return rc; + +} +#endif /* SMB311 */ + int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, __u8 *oplock, struct smb2_file_all_info *buf, diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index c84020057bd8..78371c1a6503 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -79,6 +79,10 @@ extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, bool set_alloc); extern int smb2_set_file_info(struct inode *inode, const char *full_path, FILE_BASIC_INFO *buf, const unsigned int xid); +extern int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, + umode_t mode, struct cifs_tcon *tcon, + const char *full_path, + struct cifs_sb_info *cifs_sb); extern int smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, struct cifs_sb_info *cifs_sb); extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path, diff --git a/fs/cifs/trace.h b/fs/cifs/trace.h index 61e74d455d90..67e413f6ee4d 100644 --- a/fs/cifs/trace.h +++ b/fs/cifs/trace.h @@ -378,7 +378,7 @@ DEFINE_EVENT(smb3_open_err_class, smb3_##name, \ TP_ARGS(xid, tid, sesid, create_options, desired_access, rc)) DEFINE_SMB3_OPEN_ERR_EVENT(open_err); - +DEFINE_SMB3_OPEN_ERR_EVENT(posix_mkdir_err); DECLARE_EVENT_CLASS(smb3_open_done_class, TP_PROTO(unsigned int xid, @@ -420,6 +420,7 @@ DEFINE_EVENT(smb3_open_done_class, smb3_##name, \ TP_ARGS(xid, fid, tid, sesid, create_options, desired_access)) DEFINE_SMB3_OPEN_DONE_EVENT(open_done); +DEFINE_SMB3_OPEN_DONE_EVENT(posix_mkdir_done); #endif /* _CIFS_TRACE_H */ -- cgit v1.2.3 From 115d5d288dc3368e3d6e7eb9ee213b342f072c23 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 14 Jun 2018 21:59:31 -0500 Subject: smb3: do not display empty interface list If server does not support listing interfaces then do not display empty "Server interfaces" line to avoid confusing users. Signed-off-by: Steve French CC: Aurelien Aptel --- fs/cifs/cifs_debug.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index e8db245194e7..bfe999505815 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -333,7 +333,9 @@ skip_rdma: spin_unlock(&GlobalMid_Lock); spin_lock(&ses->iface_lock); - seq_printf(m, "\n\tServer interfaces: %zu\n", ses->iface_count); + if (ses->iface_count) + seq_printf(m, "\n\tServer interfaces: %zu\n", + ses->iface_count); for (j = 0; j < ses->iface_count; j++) { seq_printf(m, "\t%d)\n", j); cifs_dump_iface(m, &ses->iface_list[j]); -- cgit v1.2.3 From d819d298c7258849d56eb400be436aff3ba2aae2 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 14 Jun 2018 22:30:56 -0500 Subject: smb3: fix corrupt path in subdirs on smb311 with posix Signed-off-by: Steve French --- fs/cifs/smb2misc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 0de87ca33e2e..3ff7cec2da81 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -454,7 +454,8 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) #ifdef CONFIG_CIFS_SMB311 /* SMB311 POSIX extensions paths do not include leading slash */ else if (cifs_sb_master_tlink(cifs_sb) && - cifs_sb_master_tcon(cifs_sb)->posix_extensions) { + cifs_sb_master_tcon(cifs_sb)->posix_extensions && + (from[0] == '/')) { start_of_path = from + 1; } #endif /* 311 */ -- cgit v1.2.3 From 35e2cc1ba755cf9dbd042e308b2928c868767a98 Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Fri, 15 Jun 2018 10:22:44 -0300 Subject: 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 Reviewed-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 7 +++---- fs/cifs/smb2proto.h | 2 ++ fs/cifs/smbdirect.c | 19 +++++-------------- fs/cifs/transport.c | 19 ++++++++++++++----- 4 files changed, 24 insertions(+), 23 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index badcfb2f3c22..0356b5559c71 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -2485,7 +2485,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, struct page **pages; struct smb2_transform_hdr *tr_hdr; unsigned int npages = old_rq->rq_npages; - unsigned int orig_len = 0; + unsigned int orig_len; int i; int rc = -ENOMEM; @@ -2499,9 +2499,6 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, new_rq->rq_pagesz = old_rq->rq_pagesz; new_rq->rq_tailsz = old_rq->rq_tailsz; - for (i = 0; i < old_rq->rq_nvec; i++) - orig_len += old_rq->rq_iov[i].iov_len; - for (i = 0; i < npages; i++) { pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM); if (!pages[i]) @@ -2524,6 +2521,8 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, if (!tr_hdr) goto err_free_iov; + orig_len = smb2_rqst_len(old_rq, false); + /* fill the 2nd iov with a transform header */ fill_transform_hdr(tr_hdr, orig_len, old_rq); new_rq->rq_iov[0].iov_base = tr_hdr; diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 78371c1a6503..3ae208ac2a77 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -113,6 +113,8 @@ extern int smb2_unlock_range(struct cifsFileInfo *cfile, extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); extern void smb2_reconnect_server(struct work_struct *work); extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); +extern unsigned long +smb2_rqst_len(struct smb_rqst *rqst, bool skip_rfc1002_marker); /* * SMB2 Worker functions - most of protocol specific implementation details diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c index e459c97151b3..6fd94d9ffac2 100644 --- a/fs/cifs/smbdirect.c +++ b/fs/cifs/smbdirect.c @@ -18,6 +18,7 @@ #include "smbdirect.h" #include "cifs_debug.h" #include "cifsproto.h" +#include "smb2proto.h" static struct smbd_response *get_empty_queue_buffer( struct smbd_connection *info); @@ -2087,7 +2088,7 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) struct kvec vec; int nvecs; int size; - unsigned int buflen = 0, remaining_data_length; + unsigned int buflen, remaining_data_length; int start, i, j; int max_iov_size = info->max_send_size - sizeof(struct smbd_data_transfer); @@ -2111,25 +2112,13 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) log_write(ERR, "expected the pdu length in 1st iov, but got %zu\n", rqst->rq_iov[0].iov_len); return -EINVAL; } - iov = &rqst->rq_iov[1]; - - /* total up iov array first */ - for (i = 0; i < rqst->rq_nvec-1; i++) { - buflen += iov[i].iov_len; - } /* * Add in the page array if there is one. The caller needs to set * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and * ends at page boundary */ - if (rqst->rq_npages) { - if (rqst->rq_npages == 1) - buflen += rqst->rq_tailsz; - else - buflen += rqst->rq_pagesz * (rqst->rq_npages - 1) - - rqst->rq_offset + rqst->rq_tailsz; - } + buflen = smb2_rqst_len(rqst, true); if (buflen + sizeof(struct smbd_data_transfer) > info->max_fragmented_send_size) { @@ -2139,6 +2128,8 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) goto done; } + iov = &rqst->rq_iov[1]; + cifs_dbg(FYI, "Sending smb (RDMA): smb_len=%u\n", buflen); for (i = 0; i < rqst->rq_nvec-1; i++) dump_smb(iov[i].iov_base, iov[i].iov_len); diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index a3ea42a4cb98..fb57dfbfb749 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -201,15 +201,24 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, return 0; } -static unsigned long -smb2_rqst_len(struct smb_rqst *rqst) +unsigned long +smb2_rqst_len(struct smb_rqst *rqst, bool skip_rfc1002_marker) { unsigned int i; - struct kvec *iov = rqst->rq_iov; + struct kvec *iov; + int nvec; unsigned long buflen = 0; + if (skip_rfc1002_marker && rqst->rq_iov[0].iov_len == 4) { + iov = &rqst->rq_iov[1]; + nvec = rqst->rq_nvec - 1; + } else { + iov = rqst->rq_iov; + nvec = rqst->rq_nvec; + } + /* total up iov array first */ - for (i = 0; i < rqst->rq_nvec; i++) + for (i = 0; i < nvec; i++) buflen += iov[i].iov_len; /* @@ -262,7 +271,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, (char *)&val, sizeof(val)); for (j = 0; j < num_rqst; j++) - send_length += smb2_rqst_len(&rqst[j]); + send_length += smb2_rqst_len(&rqst[j], true); rfc1002_marker = cpu_to_be32(send_length); /* Generate a rfc1002 marker for SMB2+ */ -- cgit v1.2.3 From 83ffdeadb46b61580c4c9a5319bd76d258a2963d Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Fri, 15 Jun 2018 15:58:00 -0300 Subject: cifs: Fix invalid check in __cifs_calc_signature() The following check would never evaluate to true: > if (i == 0 && iov[0].iov_len <= 4) Because 'i' always starts at 1. This patch fixes it and also move the header checks outside the for loop - which makes more sense. Signed-off-by: Paulo Alcantara Signed-off-by: Steve French --- fs/cifs/cifsencrypt.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index f23ff848b158..ee2a8ec70056 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -48,26 +48,23 @@ int __cifs_calc_signature(struct smb_rqst *rqst, /* iov[0] is actual data and not the rfc1002 length for SMB2+ */ if (is_smb2) { - rc = crypto_shash_update(shash, - iov[0].iov_base, iov[0].iov_len); + if (iov[0].iov_len <= 4) + return -EIO; + i = 0; } else { if (n_vec < 2 || iov[0].iov_len != 4) return -EIO; + i = 1; /* skip rfc1002 length */ } - for (i = 1; i < n_vec; i++) { + for (; i < n_vec; i++) { if (iov[i].iov_len == 0) continue; if (iov[i].iov_base == NULL) { cifs_dbg(VFS, "null iovec entry\n"); return -EIO; } - if (is_smb2) { - if (i == 0 && iov[0].iov_len <= 4) - break; /* nothing to sign or corrupt header */ - } else - if (i == 1 && iov[1].iov_len <= 4) - break; /* nothing to sign or corrupt header */ + rc = crypto_shash_update(shash, iov[i].iov_base, iov[i].iov_len); if (rc) { -- cgit v1.2.3 From 696e420bb2a6624478105651d5368d45b502b324 Mon Sep 17 00:00:00 2001 From: Lars Persson Date: Mon, 25 Jun 2018 14:05:25 +0200 Subject: cifs: Fix use after free of a mid_q_entry With protocol version 2.0 mounts we have seen crashes with corrupt mid entries. Either the server->pending_mid_q list becomes corrupt with a cyclic reference in one element or a mid object fetched by the demultiplexer thread becomes overwritten during use. Code review identified a race between the demultiplexer thread and the request issuing thread. The demultiplexer thread seems to be written with the assumption that it is the sole user of the mid object until it calls the mid callback which either wakes the issuer task or deletes the mid. This assumption is not true because the issuer task can be woken up earlier by a signal. If the demultiplexer thread has proceeded as far as setting the mid_state to MID_RESPONSE_RECEIVED then the issuer thread will happily end up calling cifs_delete_mid while the demultiplexer thread still is using the mid object. Inserting a delay in the cifs demultiplexer thread widens the race window and makes reproduction of the race very easy: if (server->large_buf) buf = server->bigbuf; + usleep_range(500, 4000); server->lstrp = jiffies; To resolve this I think the proper solution involves putting a reference count on the mid object. This patch makes sure that the demultiplexer thread holds a reference until it has finished processing the transaction. Cc: stable@vger.kernel.org Signed-off-by: Lars Persson Acked-by: Paulo Alcantara Reviewed-by: Ronnie Sahlberg Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 1 + fs/cifs/cifsproto.h | 1 + fs/cifs/connect.c | 8 +++++++- fs/cifs/smb1ops.c | 1 + fs/cifs/smb2ops.c | 1 + fs/cifs/smb2transport.c | 1 + fs/cifs/transport.c | 18 +++++++++++++++++- 7 files changed, 29 insertions(+), 2 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index bd78da59a4fd..a2962fd41c6f 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1416,6 +1416,7 @@ typedef int (mid_handle_t)(struct TCP_Server_Info *server, /* one of these for every pending CIFS request to the server */ struct mid_q_entry { struct list_head qhead; /* mids waiting on reply from this server */ + struct kref refcount; struct TCP_Server_Info *server; /* server corresponding to this mid */ __u64 mid; /* multiplex id */ __u32 pid; /* process id */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 03018be17283..1890f534c88b 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -82,6 +82,7 @@ extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server); extern void DeleteMidQEntry(struct mid_q_entry *midEntry); extern void cifs_delete_mid(struct mid_q_entry *mid); +extern void cifs_mid_q_entry_release(struct mid_q_entry *midEntry); extern void cifs_wake_up_task(struct mid_q_entry *mid); extern int cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a57da1b88bdf..5df2c0698cda 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -924,6 +924,7 @@ next_pdu: server->pdu_size = next_offset; } + mid_entry = NULL; if (server->ops->is_transform_hdr && server->ops->receive_transform && server->ops->is_transform_hdr(buf)) { @@ -938,8 +939,11 @@ next_pdu: length = mid_entry->receive(server, mid_entry); } - if (length < 0) + if (length < 0) { + if (mid_entry) + cifs_mid_q_entry_release(mid_entry); continue; + } if (server->large_buf) buf = server->bigbuf; @@ -956,6 +960,8 @@ next_pdu: if (!mid_entry->multiRsp || mid_entry->multiEnd) mid_entry->callback(mid_entry); + + cifs_mid_q_entry_release(mid_entry); } else if (server->ops->is_oplock_break && server->ops->is_oplock_break(buf, server)) { cifs_dbg(FYI, "Received oplock break\n"); diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index aff8ce8ba34d..646dcd149de1 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -107,6 +107,7 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer) if (compare_mid(mid->mid, buf) && mid->mid_state == MID_REQUEST_SUBMITTED && le16_to_cpu(mid->command) == buf->Command) { + kref_get(&mid->refcount); spin_unlock(&GlobalMid_Lock); return mid; } diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 0356b5559c71..e9216ce88796 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -203,6 +203,7 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf) if ((mid->mid == wire_mid) && (mid->mid_state == MID_REQUEST_SUBMITTED) && (mid->command == shdr->Command)) { + kref_get(&mid->refcount); spin_unlock(&GlobalMid_Lock); return mid; } diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 51b9437c3c7b..50592976dcb4 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -548,6 +548,7 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr, temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); memset(temp, 0, sizeof(struct mid_q_entry)); + kref_init(&temp->refcount); temp->mid = le64_to_cpu(shdr->MessageId); temp->pid = current->pid; temp->command = shdr->Command; /* Always LE */ diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index fb57dfbfb749..208ecb830466 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -61,6 +61,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS); memset(temp, 0, sizeof(struct mid_q_entry)); + kref_init(&temp->refcount); temp->mid = get_mid(smb_buffer); temp->pid = current->pid; temp->command = cpu_to_le16(smb_buffer->Command); @@ -82,6 +83,21 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) return temp; } +static void _cifs_mid_q_entry_release(struct kref *refcount) +{ + struct mid_q_entry *mid = container_of(refcount, struct mid_q_entry, + refcount); + + mempool_free(mid, cifs_mid_poolp); +} + +void cifs_mid_q_entry_release(struct mid_q_entry *midEntry) +{ + spin_lock(&GlobalMid_Lock); + kref_put(&midEntry->refcount, _cifs_mid_q_entry_release); + spin_unlock(&GlobalMid_Lock); +} + void DeleteMidQEntry(struct mid_q_entry *midEntry) { @@ -110,7 +126,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) } } #endif - mempool_free(midEntry, cifs_mid_poolp); + cifs_mid_q_entry_release(midEntry); } void -- cgit v1.2.3 From 27c32b49c3dbfe1e5f57d2b61823bf9474ae0875 Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Sat, 23 Jun 2018 14:52:23 -0300 Subject: cifs: Fix validation of signed data in smb3+ Fixes: c713c8770fa5 ("cifs: push rfc1002 generation down the stack") We failed to validate signed data returned by the server because __cifs_calc_signature() now expects to sign the actual data in iov but we were also passing down the rfc1002 length. Fix smb3_calc_signature() to calculate signature of rfc1002 length prior to passing only the actual data iov[1-N] to __cifs_calc_signature(). In addition, there are a few cases where no rfc1002 length is passed so we make sure there's one (iov_len == 4). Signed-off-by: Paulo Alcantara Reviewed-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2transport.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 50592976dcb4..1af46ca5a951 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -408,12 +408,14 @@ generate_smb311signingkey(struct cifs_ses *ses) int smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) { - int rc = 0; + int rc; 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 cifs_ses *ses; + struct shash_desc *shash = &server->secmech.sdesccmacaes->shash; + struct smb_rqst drqst; ses = smb2_find_smb_ses(server, shdr->SessionId); if (!ses) { @@ -425,8 +427,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE); rc = crypto_shash_setkey(server->secmech.cmacaes, - ses->smb3signingkey, SMB2_CMACAES_SIZE); - + ses->smb3signingkey, SMB2_CMACAES_SIZE); if (rc) { cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); return rc; @@ -437,15 +438,33 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) * so unlike smb2 case we do not have to check here if secmech are * initialized */ - rc = crypto_shash_init(&server->secmech.sdesccmacaes->shash); + rc = crypto_shash_init(shash); if (rc) { cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__); return rc; } - rc = __cifs_calc_signature(rqst, server, sigptr, - &server->secmech.sdesccmacaes->shash); + /* + * For SMB2+, __cifs_calc_signature() expects to sign only the actual + * data, that is, iov[0] should not contain a rfc1002 length. + * + * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to + * __cifs_calc_signature(). + */ + drqst = *rqst; + if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { + rc = crypto_shash_update(shash, iov[0].iov_base, + iov[0].iov_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with payload\n", + __func__); + return rc; + } + drqst.rq_iov++; + drqst.rq_nvec--; + } + rc = __cifs_calc_signature(&drqst, server, sigptr, shash); if (!rc) memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); -- cgit v1.2.3 From 8de8c4608fe9edc046c31bf82b2b7ebc1daae015 Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Sat, 23 Jun 2018 14:52:24 -0300 Subject: cifs: Fix validation of signed data in smb2 Fixes: c713c8770fa5 ("cifs: push rfc1002 generation down the stack") We failed to validate signed data returned by the server because __cifs_calc_signature() now expects to sign the actual data in iov but we were also passing down the rfc1002 length. Fix smb3_calc_signature() to calculate signature of rfc1002 length prior to passing only the actual data iov[1-N] to __cifs_calc_signature(). In addition, there are a few cases where no rfc1002 length is passed so we make sure there's one (iov_len == 4). Signed-off-by: Paulo Alcantara Reviewed-by: Ronnie Sahlberg Signed-off-by: Steve French --- fs/cifs/smb2transport.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 1af46ca5a951..719d55e63d88 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -173,6 +173,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) struct kvec *iov = rqst->rq_iov; struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; struct cifs_ses *ses; + struct shash_desc *shash = &server->secmech.sdeschmacsha256->shash; + struct smb_rqst drqst; ses = smb2_find_smb_ses(server, shdr->SessionId); if (!ses) { @@ -190,21 +192,39 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) } rc = crypto_shash_setkey(server->secmech.hmacsha256, - ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); + ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); if (rc) { cifs_dbg(VFS, "%s: Could not update with response\n", __func__); return rc; } - rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash); + rc = crypto_shash_init(shash); if (rc) { cifs_dbg(VFS, "%s: Could not init sha256", __func__); return rc; } - rc = __cifs_calc_signature(rqst, server, sigptr, - &server->secmech.sdeschmacsha256->shash); + /* + * For SMB2+, __cifs_calc_signature() expects to sign only the actual + * data, that is, iov[0] should not contain a rfc1002 length. + * + * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to + * __cifs_calc_signature(). + */ + drqst = *rqst; + if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) { + rc = crypto_shash_update(shash, iov[0].iov_base, + iov[0].iov_len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with payload\n", + __func__); + return rc; + } + drqst.rq_iov++; + drqst.rq_nvec--; + } + rc = __cifs_calc_signature(&drqst, server, sigptr, shash); if (!rc) memcpy(shdr->Signature, sigptr, SMB2_SIGNATURE_SIZE); -- cgit v1.2.3 From 81f39f951b8098b1c59b01ad10d06d7dc01c7019 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Thu, 28 Jun 2018 10:47:14 +1000 Subject: cifs: fix SMB1 breakage SMB1 mounting broke in commit 35e2cc1ba755 ("cifs: Use correct packet length in SMB2_TRANSFORM header") Fix it and also rename smb2_rqst_len to smb_rqst_len to make it less unobvious that the function is also called from CIFS/SMB1 Good job by Paulo reviewing and cleaning up Ronnie's original patch. Signed-off-by: Ronnie Sahlberg Reviewed-by: Paulo Alcantara Signed-off-by: Steve French --- fs/cifs/smb2ops.c | 2 +- fs/cifs/smb2proto.h | 4 ++-- fs/cifs/smbdirect.c | 5 +++-- fs/cifs/smbdirect.h | 4 ++-- fs/cifs/transport.c | 9 +++++---- 5 files changed, 13 insertions(+), 11 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index e9216ce88796..04578f6e306b 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -2522,7 +2522,7 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, struct smb_rqst *new_rq, if (!tr_hdr) goto err_free_iov; - orig_len = smb2_rqst_len(old_rq, false); + orig_len = smb_rqst_len(server, old_rq); /* fill the 2nd iov with a transform header */ fill_transform_hdr(tr_hdr, orig_len, old_rq); diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 3ae208ac2a77..6e6a4f2ec890 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -113,8 +113,8 @@ extern int smb2_unlock_range(struct cifsFileInfo *cfile, extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile); extern void smb2_reconnect_server(struct work_struct *work); extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server); -extern unsigned long -smb2_rqst_len(struct smb_rqst *rqst, bool skip_rfc1002_marker); +extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, + struct smb_rqst *rqst); /* * SMB2 Worker functions - most of protocol specific implementation details diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c index 6fd94d9ffac2..c55ea4e6201b 100644 --- a/fs/cifs/smbdirect.c +++ b/fs/cifs/smbdirect.c @@ -2083,8 +2083,9 @@ int smbd_recv(struct smbd_connection *info, struct msghdr *msg) * rqst: the data to write * return value: 0 if successfully write, otherwise error code */ -int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) +int smbd_send(struct TCP_Server_Info *server, struct smb_rqst *rqst) { + struct smbd_connection *info = server->smbd_conn; struct kvec vec; int nvecs; int size; @@ -2118,7 +2119,7 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) * rq_tailsz to PAGE_SIZE when the buffer has multiple pages and * ends at page boundary */ - buflen = smb2_rqst_len(rqst, true); + buflen = smb_rqst_len(server, rqst); if (buflen + sizeof(struct smbd_data_transfer) > info->max_fragmented_send_size) { diff --git a/fs/cifs/smbdirect.h b/fs/cifs/smbdirect.h index 1e419c21dc60..a11096254f29 100644 --- a/fs/cifs/smbdirect.h +++ b/fs/cifs/smbdirect.h @@ -292,7 +292,7 @@ void smbd_destroy(struct smbd_connection *info); /* Interface for carrying upper layer I/O through send/recv */ int smbd_recv(struct smbd_connection *info, struct msghdr *msg); -int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst); +int smbd_send(struct TCP_Server_Info *server, struct smb_rqst *rqst); enum mr_state { MR_READY, @@ -332,7 +332,7 @@ static inline void *smbd_get_connection( static inline int smbd_reconnect(struct TCP_Server_Info *server) {return -1; } static inline void smbd_destroy(struct smbd_connection *info) {} static inline int smbd_recv(struct smbd_connection *info, struct msghdr *msg) {return -1; } -static inline int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst) {return -1; } +static inline int smbd_send(struct TCP_Server_Info *server, struct smb_rqst *rqst) {return -1; } #endif #endif diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 208ecb830466..a341ec839c83 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -218,14 +218,15 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, } unsigned long -smb2_rqst_len(struct smb_rqst *rqst, bool skip_rfc1002_marker) +smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst) { unsigned int i; struct kvec *iov; int nvec; unsigned long buflen = 0; - if (skip_rfc1002_marker && rqst->rq_iov[0].iov_len == 4) { + if (server->vals->header_preamble_size == 0 && + rqst->rq_nvec >= 2 && rqst->rq_iov[0].iov_len == 4) { iov = &rqst->rq_iov[1]; nvec = rqst->rq_nvec - 1; } else { @@ -276,7 +277,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, __be32 rfc1002_marker; if (cifs_rdma_enabled(server) && server->smbd_conn) { - rc = smbd_send(server->smbd_conn, rqst); + rc = smbd_send(server, rqst); goto smbd_done; } if (ssocket == NULL) @@ -287,7 +288,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, (char *)&val, sizeof(val)); for (j = 0; j < num_rqst; j++) - send_length += smb2_rqst_len(&rqst[j], true); + send_length += smb_rqst_len(server, &rqst[j]); rfc1002_marker = cpu_to_be32(send_length); /* Generate a rfc1002 marker for SMB2+ */ -- cgit v1.2.3 From 6aa0c114eceec8cc61715f74a4ce91b048d7561c Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Wed, 4 Jul 2018 14:16:16 -0300 Subject: cifs: Fix memory leak in smb2_set_ea() This patch fixes a memory leak when doing a setxattr(2) in SMB2+. Signed-off-by: Paulo Alcantara Cc: stable@vger.kernel.org Signed-off-by: Steve French Reviewed-by: Aurelien Aptel --- fs/cifs/smb2ops.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 04578f6e306b..1d5985bd760b 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -856,6 +856,8 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, rc = SMB2_set_ea(xid, tcon, fid.persistent_fid, fid.volatile_fid, ea, len); + kfree(ea); + SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); return rc; -- cgit v1.2.3 From f46ecbd97f508e68a7806291a139499794874f3d Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Thu, 5 Jul 2018 11:46:42 +0200 Subject: cifs: Fix slab-out-of-bounds in send_set_info() on SMB2 ACE setting A "small" CIFS buffer is not big enough in general to hold a setacl request for SMB2, and we end up overflowing the buffer in send_set_info(). For instance: # mount.cifs //127.0.0.1/test /mnt/test -o username=test,password=test,nounix,cifsacl # touch /mnt/test/acltest # getcifsacl /mnt/test/acltest REVISION:0x1 CONTROL:0x9004 OWNER:S-1-5-21-2926364953-924364008-418108241-1000 GROUP:S-1-22-2-1001 ACL:S-1-5-21-2926364953-924364008-418108241-1000:ALLOWED/0x0/0x1e01ff ACL:S-1-22-2-1001:ALLOWED/0x0/R ACL:S-1-22-2-1001:ALLOWED/0x0/R ACL:S-1-5-21-2926364953-924364008-418108241-1000:ALLOWED/0x0/0x1e01ff ACL:S-1-1-0:ALLOWED/0x0/R # setcifsacl -a "ACL:S-1-22-2-1004:ALLOWED/0x0/R" /mnt/test/acltest this setacl will cause the following KASAN splat: [ 330.777927] BUG: KASAN: slab-out-of-bounds in send_set_info+0x4dd/0xc20 [cifs] [ 330.779696] Write of size 696 at addr ffff88010d5e2860 by task setcifsacl/1012 [ 330.781882] CPU: 1 PID: 1012 Comm: setcifsacl Not tainted 4.18.0-rc2+ #2 [ 330.783140] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 [ 330.784395] Call Trace: [ 330.784789] dump_stack+0xc2/0x16b [ 330.786777] print_address_description+0x6a/0x270 [ 330.787520] kasan_report+0x258/0x380 [ 330.788845] memcpy+0x34/0x50 [ 330.789369] send_set_info+0x4dd/0xc20 [cifs] [ 330.799511] SMB2_set_acl+0x76/0xa0 [cifs] [ 330.801395] set_smb2_acl+0x7ac/0xf30 [cifs] [ 330.830888] cifs_xattr_set+0x963/0xe40 [cifs] [ 330.840367] __vfs_setxattr+0x84/0xb0 [ 330.842060] __vfs_setxattr_noperm+0xe6/0x370 [ 330.843848] vfs_setxattr+0xc2/0xd0 [ 330.845519] setxattr+0x258/0x320 [ 330.859211] path_setxattr+0x15b/0x1b0 [ 330.864392] __x64_sys_setxattr+0xc0/0x160 [ 330.866133] do_syscall_64+0x14e/0x4b0 [ 330.876631] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 330.878503] RIP: 0033:0x7ff2e507db0a [ 330.880151] Code: 48 8b 0d 89 93 2c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 49 89 ca b8 bc 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 56 93 2c 00 f7 d8 64 89 01 48 [ 330.885358] RSP: 002b:00007ffdc4903c18 EFLAGS: 00000246 ORIG_RAX: 00000000000000bc [ 330.887733] RAX: ffffffffffffffda RBX: 000055d1170de140 RCX: 00007ff2e507db0a [ 330.890067] RDX: 000055d1170de7d0 RSI: 000055d115b39184 RDI: 00007ffdc4904818 [ 330.892410] RBP: 0000000000000001 R08: 0000000000000000 R09: 000055d1170de7e4 [ 330.894785] R10: 00000000000002b8 R11: 0000000000000246 R12: 0000000000000007 [ 330.897148] R13: 000055d1170de0c0 R14: 0000000000000008 R15: 000055d1170de550 [ 330.901057] Allocated by task 1012: [ 330.902888] kasan_kmalloc+0xa0/0xd0 [ 330.904714] kmem_cache_alloc+0xc8/0x1d0 [ 330.906615] mempool_alloc+0x11e/0x380 [ 330.908496] cifs_small_buf_get+0x35/0x60 [cifs] [ 330.910510] smb2_plain_req_init+0x4a/0xd60 [cifs] [ 330.912551] send_set_info+0x198/0xc20 [cifs] [ 330.914535] SMB2_set_acl+0x76/0xa0 [cifs] [ 330.916465] set_smb2_acl+0x7ac/0xf30 [cifs] [ 330.918453] cifs_xattr_set+0x963/0xe40 [cifs] [ 330.920426] __vfs_setxattr+0x84/0xb0 [ 330.922284] __vfs_setxattr_noperm+0xe6/0x370 [ 330.924213] vfs_setxattr+0xc2/0xd0 [ 330.926008] setxattr+0x258/0x320 [ 330.927762] path_setxattr+0x15b/0x1b0 [ 330.929592] __x64_sys_setxattr+0xc0/0x160 [ 330.931459] do_syscall_64+0x14e/0x4b0 [ 330.933314] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 330.936843] Freed by task 0: [ 330.938588] (stack is not available) [ 330.941886] The buggy address belongs to the object at ffff88010d5e2800 which belongs to the cache cifs_small_rq of size 448 [ 330.946362] The buggy address is located 96 bytes inside of 448-byte region [ffff88010d5e2800, ffff88010d5e29c0) [ 330.950722] The buggy address belongs to the page: [ 330.952789] page:ffffea0004357880 count:1 mapcount:0 mapping:ffff880108fdca80 index:0x0 compound_mapcount: 0 [ 330.955665] flags: 0x17ffffc0008100(slab|head) [ 330.957760] raw: 0017ffffc0008100 dead000000000100 dead000000000200 ffff880108fdca80 [ 330.960356] raw: 0000000000000000 0000000080100010 00000001ffffffff 0000000000000000 [ 330.963005] page dumped because: kasan: bad access detected [ 330.967039] Memory state around the buggy address: [ 330.969255] ffff88010d5e2880: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 330.971833] ffff88010d5e2900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 330.974397] >ffff88010d5e2980: 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc [ 330.976956] ^ [ 330.979226] ffff88010d5e2a00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 330.981755] ffff88010d5e2a80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [ 330.984225] ================================================================== Fix this by allocating a regular CIFS buffer in smb2_plain_req_init() if the request command is SMB2_SET_INFO. Reported-by: Jianhong Yin Fixes: 366ed846df60 ("cifs: Use smb 2 - 3 and cifsacl mount options setacl function") CC: Stable Signed-off-by: Stefano Brivio Reviewed-and-tested-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/smb2pdu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 810b85787c91..dbfb83ae6137 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -340,7 +340,10 @@ smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon, return rc; /* BB eventually switch this to SMB2 specific small buf size */ - *request_buf = cifs_small_buf_get(); + if (smb2_command == SMB2_SET_INFO) + *request_buf = cifs_buf_get(); + else + *request_buf = cifs_small_buf_get(); if (*request_buf == NULL) { /* BB should we add a retry in here if not a writepage? */ return -ENOMEM; @@ -3720,7 +3723,7 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon, rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); - cifs_small_buf_release(req); + cifs_buf_release(req); rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base; if (rc != 0) { -- cgit v1.2.3 From 7ffbe65578b44fafdef577a360eb0583929f7c6e Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Thu, 5 Jul 2018 13:46:34 -0300 Subject: cifs: Fix infinite loop when using hard mount option For every request we send, whether it is SMB1 or SMB2+, we attempt to reconnect tcon (cifs_reconnect_tcon or smb2_reconnect) before carrying out the request. So, while server->tcpStatus != CifsNeedReconnect, we wait for the reconnection to succeed on wait_event_interruptible_timeout(). If it returns, that means that either the condition was evaluated to true, or timeout elapsed, or it was interrupted by a signal. Since we're not handling the case where the process woke up due to a received signal (-ERESTARTSYS), the next call to wait_event_interruptible_timeout() will _always_ fail and we end up looping forever inside either cifs_reconnect_tcon() or smb2_reconnect(). Here's an example of how to trigger that: $ mount.cifs //foo/share /mnt/test -o username=foo,password=foo,vers=1.0,hard (break connection to server before executing bellow cmd) $ stat -f /mnt/test & sleep 140 [1] 2511 $ ps -aux -q 2511 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 2511 0.0 0.0 12892 1008 pts/0 S 12:24 0:00 stat -f /mnt/test $ kill -9 2511 (wait for a while; process is stuck in the kernel) $ ps -aux -q 2511 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 2511 83.2 0.0 12892 1008 pts/0 R 12:24 30:01 stat -f /mnt/test By using 'hard' mount point means that cifs.ko will keep retrying indefinitely, however we must allow the process to be killed otherwise it would hang the system. Signed-off-by: Paulo Alcantara Cc: stable@vger.kernel.org Reviewed-by: Aurelien Aptel Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 10 ++++++++-- fs/cifs/smb2pdu.c | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index d352da325de3..93408eab92e7 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -157,8 +157,14 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) * greater than cifs socket timeout which is 7 seconds */ while (server->tcpStatus == CifsNeedReconnect) { - wait_event_interruptible_timeout(server->response_q, - (server->tcpStatus != CifsNeedReconnect), 10 * HZ); + rc = wait_event_interruptible_timeout(server->response_q, + (server->tcpStatus != CifsNeedReconnect), + 10 * HZ); + if (rc < 0) { + cifs_dbg(FYI, "%s: aborting reconnect due to a received" + " signal by the process\n", __func__); + return -ERESTARTSYS; + } /* are we still trying to reconnect? */ if (server->tcpStatus != CifsNeedReconnect) diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index dbfb83ae6137..e31375ecaa6f 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -155,7 +155,7 @@ out: static int smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) { - int rc = 0; + int rc; struct nls_table *nls_codepage; struct cifs_ses *ses; struct TCP_Server_Info *server; @@ -166,10 +166,10 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) * for those three - in the calling routine. */ if (tcon == NULL) - return rc; + return 0; if (smb2_command == SMB2_TREE_CONNECT) - return rc; + return 0; if (tcon->tidStatus == CifsExiting) { /* @@ -212,8 +212,14 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) return -EAGAIN; } - wait_event_interruptible_timeout(server->response_q, - (server->tcpStatus != CifsNeedReconnect), 10 * HZ); + rc = wait_event_interruptible_timeout(server->response_q, + (server->tcpStatus != CifsNeedReconnect), + 10 * HZ); + if (rc < 0) { + cifs_dbg(FYI, "%s: aborting reconnect due to a received" + " signal by the process\n", __func__); + return -ERESTARTSYS; + } /* are we still trying to reconnect? */ if (server->tcpStatus != CifsNeedReconnect) @@ -231,7 +237,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon) } if (!tcon->ses->need_reconnect && !tcon->need_reconnect) - return rc; + return 0; nls_codepage = load_nls_default(); -- cgit v1.2.3 From 729c0c9dd55204f0c9a823ac8a7bfa83d36c7e78 Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Thu, 5 Jul 2018 15:10:02 +0200 Subject: cifs: Fix stack out-of-bounds in smb{2,3}_create_lease_buf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit smb{2,3}_create_lease_buf() store a lease key in the lease context for later usage on a lease break. In most paths, the key is currently sourced from data that happens to be on the stack near local variables for oplock in SMB2_open() callers, e.g. from open_shroot(), whereas smb2_open_file() properly allocates space on its stack for it. The address of those local variables holding the oplock is then passed to create_lease_buf handlers via SMB2_open(), and 16 bytes near oplock are used. This causes a stack out-of-bounds access as reported by KASAN on SMB2.1 and SMB3 mounts (first out-of-bounds access is shown here): [ 111.528823] BUG: KASAN: stack-out-of-bounds in smb3_create_lease_buf+0x399/0x3b0 [cifs] [ 111.530815] Read of size 8 at addr ffff88010829f249 by task mount.cifs/985 [ 111.532838] CPU: 3 PID: 985 Comm: mount.cifs Not tainted 4.18.0-rc3+ #91 [ 111.534656] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014 [ 111.536838] Call Trace: [ 111.537528] dump_stack+0xc2/0x16b [ 111.540890] print_address_description+0x6a/0x270 [ 111.542185] kasan_report+0x258/0x380 [ 111.544701] smb3_create_lease_buf+0x399/0x3b0 [cifs] [ 111.546134] SMB2_open+0x1ef8/0x4b70 [cifs] [ 111.575883] open_shroot+0x339/0x550 [cifs] [ 111.591969] smb3_qfs_tcon+0x32c/0x1e60 [cifs] [ 111.617405] cifs_mount+0x4f3/0x2fc0 [cifs] [ 111.674332] cifs_smb3_do_mount+0x263/0xf10 [cifs] [ 111.677915] mount_fs+0x55/0x2b0 [ 111.679504] vfs_kern_mount.part.22+0xaa/0x430 [ 111.684511] do_mount+0xc40/0x2660 [ 111.698301] ksys_mount+0x80/0xd0 [ 111.701541] do_syscall_64+0x14e/0x4b0 [ 111.711807] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 111.713665] RIP: 0033:0x7f372385b5fa [ 111.715311] Code: 48 8b 0d 99 78 2c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 49 89 ca b8 a5 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 66 78 2c 00 f7 d8 64 89 01 48 [ 111.720330] RSP: 002b:00007ffff27049d8 EFLAGS: 00000206 ORIG_RAX: 00000000000000a5 [ 111.722601] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f372385b5fa [ 111.724842] RDX: 000055c2ecdc73b2 RSI: 000055c2ecdc73f9 RDI: 00007ffff270580f [ 111.727083] RBP: 00007ffff2705804 R08: 000055c2ee976060 R09: 0000000000001000 [ 111.729319] R10: 0000000000000000 R11: 0000000000000206 R12: 00007f3723f4d000 [ 111.731615] R13: 000055c2ee976060 R14: 00007f3723f4f90f R15: 0000000000000000 [ 111.735448] The buggy address belongs to the page: [ 111.737420] page:ffffea000420a7c0 count:0 mapcount:0 mapping:0000000000000000 index:0x0 [ 111.739890] flags: 0x17ffffc0000000() [ 111.741750] raw: 0017ffffc0000000 0000000000000000 dead000000000200 0000000000000000 [ 111.744216] raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000 [ 111.746679] page dumped because: kasan: bad access detected [ 111.750482] Memory state around the buggy address: [ 111.752562] ffff88010829f100: 00 f2 f2 f2 f2 f2 f2 f2 00 00 00 00 00 00 00 00 [ 111.754991] ffff88010829f180: 00 00 f2 f2 00 00 00 00 00 00 00 00 00 00 00 00 [ 111.757401] >ffff88010829f200: 00 00 00 00 00 f1 f1 f1 f1 01 f2 f2 f2 f2 f2 f2 [ 111.759801] ^ [ 111.762034] ffff88010829f280: f2 02 f2 f2 f2 f2 f2 f2 f2 00 00 00 00 00 00 00 [ 111.764486] ffff88010829f300: f2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 111.766913] ================================================================== Lease keys are however already generated and stored in fid data on open and create paths: pass them down to the lease context creation handlers and use them. Suggested-by: AurĂ©lien Aptel Reviewed-by: Aurelien Aptel Fixes: b8c32dbb0deb ("CIFS: Request SMB2.1 leases") Signed-off-by: Stefano Brivio Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 +- fs/cifs/smb2file.c | 11 ++++------- fs/cifs/smb2ops.c | 9 +++------ fs/cifs/smb2pdu.c | 7 ++++--- fs/cifs/smb2pdu.h | 6 ++---- 5 files changed, 14 insertions(+), 21 deletions(-) (limited to 'fs/cifs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index a2962fd41c6f..c923c7854027 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -423,7 +423,7 @@ struct smb_version_operations { void (*set_oplock_level)(struct cifsInodeInfo *, __u32, unsigned int, bool *); /* create lease context buffer for CREATE request */ - char * (*create_lease_buf)(u8 *, u8); + char * (*create_lease_buf)(u8 *lease_key, u8 oplock); /* parse lease context buffer and return oplock/epoch info */ __u8 (*parse_lease_buf)(void *buf, unsigned int *epoch, char *lkey); ssize_t (*copychunk_range)(const unsigned int, diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index 788412675723..4ed10dd086e6 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c @@ -41,7 +41,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, int rc; __le16 *smb2_path; struct smb2_file_all_info *smb2_data = NULL; - __u8 smb2_oplock[17]; + __u8 smb2_oplock; struct cifs_fid *fid = oparms->fid; struct network_resiliency_req nr_ioctl_req; @@ -59,12 +59,9 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, } oparms->desired_access |= FILE_READ_ATTRIBUTES; - *smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; + smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH; - if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) - memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE); - - rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data, NULL, + rc = SMB2_open(xid, oparms, smb2_path, &smb2_oplock, smb2_data, NULL, NULL); if (rc) goto out; @@ -101,7 +98,7 @@ smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms, move_smb2_info_to_cifs(buf, smb2_data); } - *oplock = *smb2_oplock; + *oplock = smb2_oplock; out: kfree(smb2_data); kfree(smb2_path); diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 1d5985bd760b..ea92a38b2f08 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -2222,8 +2222,7 @@ smb2_create_lease_buf(u8 *lease_key, u8 oplock) if (!buf) return NULL; - buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key)); - buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8))); + memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); buf->lcontext.LeaseState = map_oplock_to_lease(oplock); buf->ccontext.DataOffset = cpu_to_le16(offsetof @@ -2249,8 +2248,7 @@ smb3_create_lease_buf(u8 *lease_key, u8 oplock) if (!buf) return NULL; - buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key)); - buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8))); + memcpy(&buf->lcontext.LeaseKey, lease_key, SMB2_LEASE_KEY_SIZE); buf->lcontext.LeaseState = map_oplock_to_lease(oplock); buf->ccontext.DataOffset = cpu_to_le16(offsetof @@ -2287,8 +2285,7 @@ smb3_parse_lease_buf(void *buf, unsigned int *epoch, char *lease_key) if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) return SMB2_OPLOCK_LEVEL_NOCHANGE; if (lease_key) - memcpy(lease_key, &lc->lcontext.LeaseKeyLow, - SMB2_LEASE_KEY_SIZE); + memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); return le32_to_cpu(lc->lcontext.LeaseState); } diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index e31375ecaa6f..3c92678cb45b 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1716,12 +1716,12 @@ parse_lease_state(struct TCP_Server_Info *server, struct smb2_create_rsp *rsp, static int add_lease_context(struct TCP_Server_Info *server, struct kvec *iov, - unsigned int *num_iovec, __u8 *oplock) + unsigned int *num_iovec, u8 *lease_key, __u8 *oplock) { struct smb2_create_req *req = iov[0].iov_base; unsigned int num = *num_iovec; - iov[num].iov_base = server->ops->create_lease_buf(oplock+1, *oplock); + iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = server->vals->create_lease_size; @@ -2181,7 +2181,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, *oplock == SMB2_OPLOCK_LEVEL_NONE) req->RequestedOplockLevel = *oplock; else { - rc = add_lease_context(server, iov, &n_iov, oplock); + rc = add_lease_context(server, iov, &n_iov, + oparms->fid->lease_key, oplock); if (rc) { cifs_small_buf_release(req); kfree(copy_path); diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 824dddeee3f2..a671adcc44a6 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -678,16 +678,14 @@ struct create_context { #define SMB2_LEASE_KEY_SIZE 16 struct lease_context { - __le64 LeaseKeyLow; - __le64 LeaseKeyHigh; + u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; __le32 LeaseState; __le32 LeaseFlags; __le64 LeaseDuration; } __packed; struct lease_context_v2 { - __le64 LeaseKeyLow; - __le64 LeaseKeyHigh; + u8 LeaseKey[SMB2_LEASE_KEY_SIZE]; __le32 LeaseState; __le32 LeaseFlags; __le64 LeaseDuration; -- cgit v1.2.3