cifs: fix smb3-encryption crashes with CONFIG_DEBUG_SG
diff mbox series

Message ID 20180219074903.6131-2-lsahlber@redhat.com
State New
Headers show
Series
  • fix smb3-encryption breakage when CONFIG_DEBUG_SG=y
Related show

Commit Message

Ronnie Sahlberg Feb. 19, 2018, 7:49 a.m. UTC
When CONFIG_DEBUG_SG is set, sg_set_buf() will verify that the passed buffer
is a valid virtual address (and not for example a stack address).

This means that we are not allowed to pass buffers allocated from the stack
into sg_set_buf() (when this configuration option is set).

Go through and fix all the places (I could find) where we were passing
a stack address into this function.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/smb2file.c |  19 +++++---
 fs/cifs/smb2ops.c  | 127 +++++++++++++++++++++++++++++++++++++----------------
 fs/cifs/smb2pdu.c  |  74 ++++++++++++++++++++-----------
 3 files changed, 151 insertions(+), 69 deletions(-)

Patch
diff mbox series

diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index 12af5dba742b..5f9d1171f41d 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -43,7 +43,7 @@  smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
 	struct smb2_file_all_info *smb2_data = NULL;
 	__u8 smb2_oplock[17];
 	struct cifs_fid *fid = oparms->fid;
-	struct network_resiliency_req nr_ioctl_req;
+	struct network_resiliency_req *nr_ioctl_req;
 
 	smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
 	if (smb2_path == NULL) {
@@ -69,14 +69,23 @@  smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
 		goto out;
 
 
-	 if (oparms->tcon->use_resilient) {
-		nr_ioctl_req.Timeout = 0; /* use server default (120 seconds) */
-		nr_ioctl_req.Reserved = 0;
+	if (oparms->tcon->use_resilient) {
+		nr_ioctl_req = kzalloc(sizeof(struct network_resiliency_req),
+				       GFP_KERNEL);
+		if (!nr_ioctl_req) {
+			cifs_dbg(VFS, "failed to allocate nr_ioctl_req\n");
+			rc = -ENOMEM;
+			goto out;
+		}
+		nr_ioctl_req->Timeout = 0; /* use server default (120 seconds) */
+		nr_ioctl_req->Reserved = 0;
 		rc = SMB2_ioctl(xid, oparms->tcon, fid->persistent_fid,
 			fid->volatile_fid, FSCTL_LMR_REQUEST_RESILIENCY,
 			true /* is_fsctl */,
-			(char *)&nr_ioctl_req, sizeof(nr_ioctl_req),
+			(char *)nr_ioctl_req,
+			sizeof(struct network_resiliency_req),
 			NULL, NULL /* no return info */);
+		kfree(nr_ioctl_req);
 		if (rc == -EOPNOTSUPP) {
 			cifs_dbg(VFS,
 			     "resiliency not supported by server, disabling\n");
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index eb68e2fcc500..b73fc16928c5 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -991,6 +991,7 @@  static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
 		struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse)
 {
 	struct cifsInodeInfo *cifsi;
+	__u8 *buf;
 	int rc;
 
 	cifsi = CIFS_I(inode);
@@ -1015,10 +1016,18 @@  static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon,
 	if (tcon->broken_sparse_sup)
 		return false;
 
+	buf = kzalloc(1, GFP_KERNEL);
+	if (!buf) {
+		cifs_dbg(VFS, "failed to allocate buf for set_sparse\n");
+		return -ENOMEM;
+	}
+
+	*buf = setsparse;
 	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
 			cfile->fid.volatile_fid, FSCTL_SET_SPARSE,
 			true /* is_fctl */,
-			&setsparse, 1, NULL, NULL);
+			buf, 1, NULL, NULL);
+	kfree(buf);
 	if (rc) {
 		tcon->broken_sparse_sup = true;
 		cifs_dbg(FYI, "set sparse rc = %d\n", rc);
@@ -1046,12 +1055,9 @@  smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
 	 */
 	inode = d_inode(cfile->dentry);
 
-	if (!set_alloc && (size > inode->i_size + 8192)) {
-		__u8 set_sparse = 1;
-
-		/* whether set sparse succeeds or not, extend the file */
-		smb2_set_sparse(xid, tcon, cfile, inode, set_sparse);
-	}
+	/* whether set sparse succeeds or not, extend the file */
+	if (!set_alloc && (size > inode->i_size + 8192))
+		smb2_set_sparse(xid, tcon, cfile, inode, 1);
 
 	return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
 			    cfile->fid.volatile_fid, cfile->pid, &eof, false);
@@ -1065,7 +1071,7 @@  smb2_duplicate_extents(const unsigned int xid,
 {
 	int rc;
 	unsigned int ret_data_len;
-	struct duplicate_extents_to_file dup_ext_buf;
+	struct duplicate_extents_to_file *dup_ext_buf;
 	struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
 
 	/* server fileays advertise duplicate extent support with this flag */
@@ -1073,11 +1079,17 @@  smb2_duplicate_extents(const unsigned int xid,
 	     FILE_SUPPORTS_BLOCK_REFCOUNTING) == 0)
 		return -EOPNOTSUPP;
 
-	dup_ext_buf.VolatileFileHandle = srcfile->fid.volatile_fid;
-	dup_ext_buf.PersistentFileHandle = srcfile->fid.persistent_fid;
-	dup_ext_buf.SourceFileOffset = cpu_to_le64(src_off);
-	dup_ext_buf.TargetFileOffset = cpu_to_le64(dest_off);
-	dup_ext_buf.ByteCount = cpu_to_le64(len);
+	dup_ext_buf = kzalloc(sizeof(struct duplicate_extents_to_file),
+			      GFP_KERNEL);
+	if (!dup_ext_buf) {
+		cifs_dbg(VFS, "failed to allocate duplicate_extents_to_file\n");
+		return -ENOMEM;
+	}
+	dup_ext_buf->VolatileFileHandle = srcfile->fid.volatile_fid;
+	dup_ext_buf->PersistentFileHandle = srcfile->fid.persistent_fid;
+	dup_ext_buf->SourceFileOffset = cpu_to_le64(src_off);
+	dup_ext_buf->TargetFileOffset = cpu_to_le64(dest_off);
+	dup_ext_buf->ByteCount = cpu_to_le64(len);
 	cifs_dbg(FYI, "duplicate extents: src off %lld dst off %lld len %lld",
 		src_off, dest_off, len);
 
@@ -1089,7 +1101,7 @@  smb2_duplicate_extents(const unsigned int xid,
 			trgtfile->fid.volatile_fid,
 			FSCTL_DUPLICATE_EXTENTS_TO_FILE,
 			true /* is_fsctl */,
-			(char *)&dup_ext_buf,
+			(char *)dup_ext_buf,
 			sizeof(struct duplicate_extents_to_file),
 			NULL,
 			&ret_data_len);
@@ -1098,6 +1110,7 @@  smb2_duplicate_extents(const unsigned int xid,
 		cifs_dbg(FYI, "non-zero response length in duplicate extents");
 
 duplicate_extents_out:
+	kfree(dup_ext_buf);
 	return rc;
 }
 
@@ -1113,22 +1126,30 @@  static int
 smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
 		   struct cifsFileInfo *cfile)
 {
-	struct fsctl_set_integrity_information_req integr_info;
+	struct fsctl_set_integrity_information_req *integr_info;
 	unsigned int ret_data_len;
+	int rc;
 
-	integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED);
-	integr_info.Flags = 0;
-	integr_info.Reserved = 0;
+	integr_info = kzalloc(sizeof(struct fsctl_set_integrity_information_req),
+			      GFP_KERNEL);
+	if (!integr_info) {
+		cifs_dbg(VFS, "failed to allocate integr_info\n");
+		return -ENOMEM;
+	}
+	integr_info->ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED);
+	integr_info->Flags = 0;
+	integr_info->Reserved = 0;
 
-	return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
 			cfile->fid.volatile_fid,
 			FSCTL_SET_INTEGRITY_INFORMATION,
 			true /* is_fsctl */,
-			(char *)&integr_info,
+			(char *)integr_info,
 			sizeof(struct fsctl_set_integrity_information_req),
 			NULL,
 			&ret_data_len);
-
+	kfree(integr_info);
+	return rc;
 }
 
 static int
@@ -1682,7 +1703,7 @@  static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
 	struct inode *inode;
 	struct cifsInodeInfo *cifsi;
 	struct cifsFileInfo *cfile = file->private_data;
-	struct file_zero_data_information fsctl_buf;
+	struct file_zero_data_information *fsctl_buf;
 	long rc;
 	unsigned int xid;
 
@@ -1710,18 +1731,29 @@  static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
 	 * which for a non sparse file would zero the newly extended range
 	 */
 	if (keep_size == false)
-		if (i_size_read(inode) < offset + len)
-			return -EOPNOTSUPP;
+		if (i_size_read(inode) < offset + len) {
+			rc = -EOPNOTSUPP;
+			goto out;
+		}
 
 	cifs_dbg(FYI, "offset %lld len %lld", offset, len);
 
-	fsctl_buf.FileOffset = cpu_to_le64(offset);
-	fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
+	fsctl_buf = kzalloc(sizeof(struct file_zero_data_information),
+			    GFP_KERNEL);
+	if (!fsctl_buf) {
+		cifs_dbg(VFS, "failed to allocate fsctl_buf\n");
+		rc = -ENOMEM;
+		goto out;
+	}
+	fsctl_buf->FileOffset = cpu_to_le64(offset);
+	fsctl_buf->BeyondFinalZero = cpu_to_le64(offset + len);
 
 	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
 			cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
-			true /* is_fctl */, (char *)&fsctl_buf,
+			true /* is_fctl */, (char *)fsctl_buf,
 			sizeof(struct file_zero_data_information), NULL, NULL);
+	kfree(fsctl_buf);
+out:
 	free_xid(xid);
 	return rc;
 }
@@ -1732,10 +1764,9 @@  static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
 	struct inode *inode;
 	struct cifsInodeInfo *cifsi;
 	struct cifsFileInfo *cfile = file->private_data;
-	struct file_zero_data_information fsctl_buf;
+	struct file_zero_data_information *fsctl_buf;
 	long rc;
 	unsigned int xid;
-	__u8 set_sparse = 1;
 
 	xid = get_xid();
 
@@ -1744,18 +1775,28 @@  static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
 
 	/* Need to make file sparse, if not already, before freeing range. */
 	/* Consider adding equivalent for compressed since it could also work */
-	if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse))
-		return -EOPNOTSUPP;
-
+	if (!smb2_set_sparse(xid, tcon, cfile, inode, 1)) {
+		rc = -EOPNOTSUPP;
+		goto out;
+	}
 	cifs_dbg(FYI, "offset %lld len %lld", offset, len);
 
-	fsctl_buf.FileOffset = cpu_to_le64(offset);
-	fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len);
+	fsctl_buf = kzalloc(sizeof(struct file_zero_data_information),
+			    GFP_KERNEL);
+	if (!fsctl_buf) {
+		cifs_dbg(VFS, "failed to allocate fsctl_buf\n");
+		rc -ENOMEM;
+		goto out;
+	}
+	fsctl_buf->FileOffset = cpu_to_le64(offset);
+	fsctl_buf->BeyondFinalZero = cpu_to_le64(offset + len);
 
 	rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
 			cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA,
-			true /* is_fctl */, (char *)&fsctl_buf,
+			true /* is_fctl */, (char *)fsctl_buf,
 			sizeof(struct file_zero_data_information), NULL, NULL);
+	kfree(fsctl_buf);
+out:
 	free_xid(xid);
 	return rc;
 }
@@ -1808,7 +1849,7 @@  static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
 		if ((off > 8192) || (off + len + 8192 < i_size_read(inode)))
 			return -EOPNOTSUPP;
 
-		rc = smb2_set_sparse(xid, tcon, cfile, inode, false);
+		rc = smb2_set_sparse(xid, tcon, cfile, inode, 0);
 	}
 	/* BB: else ... in future add code to extend file and set sparse */
 
@@ -2128,7 +2169,7 @@  crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
 	unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 24;
 	int rc = 0;
 	struct scatterlist *sg;
-	u8 sign[SMB2_SIGNATURE_SIZE] = {};
+	u8 *sign;
 	u8 key[SMB3_SIGN_KEY_SIZE];
 	struct aead_request *req;
 	char *iv;
@@ -2143,7 +2184,6 @@  crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
 			 enc ? "en" : "de");
 		return 0;
 	}
-
 	rc = smb3_crypto_aead_allocate(server);
 	if (rc) {
 		cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
@@ -2170,6 +2210,13 @@  crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
 		return -ENOMEM;
 	}
 
+	sign = kzalloc(SMB2_SIGNATURE_SIZE, GFP_KERNEL);
+	if (!sign) {
+		cifs_dbg(VFS, "%s: Failed to kzalloc sign", __func__);
+		rc = -ENOMEM;
+		goto free_req;
+	}
+
 	if (!enc) {
 		memcpy(sign, &tr_hdr->Signature, SMB2_SIGNATURE_SIZE);
 		crypt_len += SMB2_SIGNATURE_SIZE;
@@ -2179,7 +2226,7 @@  crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
 	if (!sg) {
 		cifs_dbg(VFS, "%s: Failed to init sg", __func__);
 		rc = -ENOMEM;
-		goto free_req;
+		goto free_sign;
 	}
 
 	iv_len = crypto_aead_ivsize(tfm);
@@ -2207,6 +2254,8 @@  crypt_message(struct TCP_Server_Info *server, struct smb_rqst *rqst, int enc)
 	kfree(iv);
 free_sg:
 	kfree(sg);
+free_sign:
+	kfree(sign);
 free_req:
 	kfree(req);
 	return rc;
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 63778ac22fd9..eca036faa755 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -609,7 +609,7 @@  SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
 {
 	int rc = 0;
-	struct validate_negotiate_info_req vneg_inbuf;
+	struct validate_negotiate_info_req *vneg_inbuf;
 	struct validate_negotiate_info_rsp *pneg_rsp = NULL;
 	u32 rsplen;
 	u32 inbuflen; /* max of 4 dialects */
@@ -639,50 +639,57 @@  int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
 	if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
 		cifs_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n");
 
-	vneg_inbuf.Capabilities =
+	vneg_inbuf = kzalloc(sizeof(struct validate_negotiate_info_req),
+			     GFP_KERNEL);
+	if (!vneg_inbuf) {
+		cifs_dbg(VFS, "failed to allocate vneg_inbuf\n");
+		return -ENOMEM;
+	}
+
+	vneg_inbuf->Capabilities =
 			cpu_to_le32(tcon->ses->server->vals->req_capabilities);
-	memcpy(vneg_inbuf.Guid, tcon->ses->server->client_guid,
+	memcpy(vneg_inbuf->Guid, tcon->ses->server->client_guid,
 					SMB2_CLIENT_GUID_SIZE);
 
 	if (tcon->ses->sign)
-		vneg_inbuf.SecurityMode =
+		vneg_inbuf->SecurityMode =
 			cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
 	else if (global_secflags & CIFSSEC_MAY_SIGN)
-		vneg_inbuf.SecurityMode =
+		vneg_inbuf->SecurityMode =
 			cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
 	else
-		vneg_inbuf.SecurityMode = 0;
+		vneg_inbuf->SecurityMode = 0;
 
 
 	if (strcmp(tcon->ses->server->vals->version_string,
 		SMB3ANY_VERSION_STRING) == 0) {
-		vneg_inbuf.Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
-		vneg_inbuf.Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
-		vneg_inbuf.DialectCount = cpu_to_le16(2);
+		vneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
+		vneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
+		vneg_inbuf->DialectCount = cpu_to_le16(2);
 		/* structure is big enough for 3 dialects, sending only 2 */
 		inbuflen = sizeof(struct validate_negotiate_info_req) - 2;
 	} else if (strcmp(tcon->ses->server->vals->version_string,
 		SMBDEFAULT_VERSION_STRING) == 0) {
-		vneg_inbuf.Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
-		vneg_inbuf.Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
-		vneg_inbuf.Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
-		vneg_inbuf.DialectCount = cpu_to_le16(3);
+		vneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
+		vneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
+		vneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
+		vneg_inbuf->DialectCount = cpu_to_le16(3);
 		/* structure is big enough for 3 dialects */
 		inbuflen = sizeof(struct validate_negotiate_info_req);
 	} else {
 		/* otherwise specific dialect was requested */
-		vneg_inbuf.Dialects[0] =
+		vneg_inbuf->Dialects[0] =
 			cpu_to_le16(tcon->ses->server->vals->protocol_id);
-		vneg_inbuf.DialectCount = cpu_to_le16(1);
+		vneg_inbuf->DialectCount = cpu_to_le16(1);
 		/* structure is big enough for 3 dialects, sending only 1 */
 		inbuflen = sizeof(struct validate_negotiate_info_req) - 4;
 	}
 
 	rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
 		FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
-		(char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req),
+		(char *)vneg_inbuf, sizeof(struct validate_negotiate_info_req),
 		(char **)&pneg_rsp, &rsplen);
-
+	kfree(vneg_inbuf);
 	if (rc != 0) {
 		cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc);
 		return -EIO;
@@ -1997,16 +2004,24 @@  SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
 		     u64 persistent_fid, u64 volatile_fid)
 {
 	int rc;
-	struct  compress_ioctl fsctl_input;
+	struct  compress_ioctl *fsctl_input;
 	char *ret_data = NULL;
 
-	fsctl_input.CompressionState =
+	fsctl_input = kzalloc(sizeof(struct  compress_ioctl),
+			     GFP_KERNEL);
+	if (!fsctl_input) {
+		cifs_dbg(VFS, "failed to allocate fsctl_input\n");
+		return -ENOMEM;
+	}
+
+	fsctl_input->CompressionState =
 			cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
 
 	rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
 			FSCTL_SET_COMPRESSION, true /* is_fsctl */,
 			(char *)&fsctl_input /* data input */,
 			2 /* in data len */, &ret_data /* out data */, NULL);
+	kfree(fsctl_input);
 
 	cifs_dbg(FYI, "set compression rc %d\n", rc);
 
@@ -3613,15 +3628,24 @@  SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
 	  const __u64 length, const __u64 offset, const __u32 lock_flags,
 	  const bool wait)
 {
-	struct smb2_lock_element lock;
+	struct smb2_lock_element *lock;
+	int rc;
 
-	lock.Offset = cpu_to_le64(offset);
-	lock.Length = cpu_to_le64(length);
-	lock.Flags = cpu_to_le32(lock_flags);
+	lock = kzalloc(sizeof(struct smb2_lock_element), GFP_KERNEL);
+	if (!lock) {
+		cifs_dbg(VFS, "failed to allocate smb2_lock_element\n");
+		return -ENOMEM;
+	}
+	lock->Offset = cpu_to_le64(offset);
+	lock->Length = cpu_to_le64(length);
+	lock->Flags = cpu_to_le32(lock_flags);
 	if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK)
-		lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
+		lock->Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
+
+	rc = smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, lock);
+	kfree(lock);
 
-	return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock);
+	return rc;
 }
 
 int