[CIFS] Fix buffer overflow if server sends corrupt response to small
request

In SendReceive() function in transport.c - it memcpy's
message payload into a buffer passed via out_buf param. The function
assumes that all buffers are of size (CIFSMaxBufSize +
MAX_CIFS_HDR_SIZE) , unfortunately it is also called with smaller
(MAX_CIFS_SMALL_BUFFER_SIZE) buffers.  There are eight callers
(SMB worker functions) which are primarily affected by this change:

TreeDisconnect, uLogoff, Close, findClose, SetFileSize, SetFileTimes,
Lock and PosixLock

CC: Dave Kleikamp <shaggy@austin.ibm.com>
CC: Przemyslaw Wegrzyn <czajnik@czajsoft.pl>
Acked-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 7ed32b3..50b623a 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -308,7 +308,7 @@
 
 static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op)
 {
-	if (long_op == -1) {
+	if (long_op == CIFS_ASYNC_OP) {
 		/* oplock breaks must not be held up */
 		atomic_inc(&ses->server->inFlight);
 	} else {
@@ -337,7 +337,7 @@
 				   as they are allowed to block on server */
 
 				/* update # of requests on the wire to server */
-				if (long_op < 3)
+				if (long_op != CIFS_BLOCKING_OP)
 					atomic_inc(&ses->server->inFlight);
 				spin_unlock(&GlobalMid_Lock);
 				break;
@@ -415,17 +415,48 @@
 	}
 }
 
+
+/*
+ *
+ * Send an SMB Request.  No response info (other than return code)
+ * needs to be parsed.
+ *
+ * flags indicate the type of request buffer and how long to wait
+ * and whether to log NT STATUS code (error) before mapping it to POSIX error
+ *
+ */
+int
+SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses,
+		struct smb_hdr *in_buf, int flags)
+{
+	int rc;
+	struct kvec iov[1];
+	int resp_buf_type;
+
+	iov[0].iov_base = (char *)in_buf;
+	iov[0].iov_len = in_buf->smb_buf_length + 4;
+	flags |= CIFS_NO_RESP;
+	rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags);
+#ifdef CONFIG_CIFS_DEBUG2
+	cFYI(1, ("SendRcvNoR flags %d rc %d", flags, rc));
+#endif
+	return rc;
+}
+
 int
 SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
 	     struct kvec *iov, int n_vec, int *pRespBufType /* ret */,
-	     const int long_op, const int logError)
+	     const int flags)
 {
 	int rc = 0;
+	int long_op;
 	unsigned int receive_len;
 	unsigned long timeout;
 	struct mid_q_entry *midQ;
 	struct smb_hdr *in_buf = iov[0].iov_base;
 
+	long_op = flags & CIFS_TIMEOUT_MASK;
+
 	*pRespBufType = CIFS_NO_BUFFER;  /* no response buf yet */
 
 	if ((ses == NULL) || (ses->server == NULL)) {
@@ -483,15 +514,22 @@
 	if (rc < 0)
 		goto out;
 
-	if (long_op == -1)
-		goto out;
-	else if (long_op == 2) /* writes past end of file can take loong time */
+	if (long_op == CIFS_STD_OP)
+		timeout = 15 * HZ;
+	else if (long_op == CIFS_VLONG_OP) /* e.g. slow writes past EOF */
 		timeout = 180 * HZ;
-	else if (long_op == 1)
+	else if (long_op == CIFS_LONG_OP)
 		timeout = 45 * HZ; /* should be greater than
 			servers oplock break timeout (about 43 seconds) */
-	else
-		timeout = 15 * HZ;
+	else if (long_op == CIFS_ASYNC_OP)
+		goto out;
+	else if (long_op == CIFS_BLOCKING_OP)
+		timeout = 0x7FFFFFFF; /*  large, but not so large as to wrap */
+	else {
+		cERROR(1, ("unknown timeout flag %d", long_op));
+		rc = -EIO;
+		goto out;
+	}
 
 	/* wait for 15 seconds or until woken up due to response arriving or
 	   due to last connection to this server being unmounted */
@@ -566,7 +604,8 @@
 			}
 
 			/* BB special case reconnect tid and uid here? */
-			rc = map_smb_to_linux_error(midQ->resp_buf, logError);
+			rc = map_smb_to_linux_error(midQ->resp_buf,
+						flags & CIFS_LOG_ERROR);
 
 			/* convert ByteCount if necessary */
 			if (receive_len >= sizeof(struct smb_hdr) - 4
@@ -574,8 +613,10 @@
 			    (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
 				BCC(midQ->resp_buf) =
 					le16_to_cpu(BCC_LE(midQ->resp_buf));
-			midQ->resp_buf = NULL;  /* mark it so will not be freed
-						by DeleteMidQEntry */
+			if ((flags & CIFS_NO_RESP) == 0)
+				midQ->resp_buf = NULL;  /* mark it so buf will
+							   not be freed by
+							   DeleteMidQEntry */
 		} else {
 			rc = -EIO;
 			cFYI(1, ("Bad MID state?"));
@@ -663,17 +704,25 @@
 	if (rc < 0)
 		goto out;
 
-	if (long_op == -1)
-		goto out;
-	else if (long_op == 2) /* writes past end of file can take loong time */
-		timeout = 180 * HZ;
-	else if (long_op == 1)
-		timeout = 45 * HZ; /* should be greater than
-			servers oplock break timeout (about 43 seconds) */
-	else
+	if (long_op == CIFS_STD_OP)
 		timeout = 15 * HZ;
 	/* wait for 15 seconds or until woken up due to response arriving or
 	   due to last connection to this server being unmounted */
+	else if (long_op == CIFS_ASYNC_OP)
+		goto out;
+	else if (long_op == CIFS_VLONG_OP) /* writes past EOF can be slow */
+		timeout = 180 * HZ;
+	else if (long_op == CIFS_LONG_OP)
+		timeout = 45 * HZ; /* should be greater than
+			servers oplock break timeout (about 43 seconds) */
+	else if (long_op == CIFS_BLOCKING_OP)
+		timeout = 0x7FFFFFFF; /* large but no so large as to wrap */
+	else {
+		cERROR(1, ("unknown timeout flag %d", long_op));
+		rc = -EIO;
+		goto out;
+	}
+
 	if (signal_pending(current)) {
 		/* if signal pending do not hold up user for full smb timeout
 		but we still give response a chance to complete */
@@ -812,7 +861,7 @@
 	pSMB->hdr.Mid = GetNextMid(ses->server);
 
 	return SendReceive(xid, ses, in_buf, out_buf,
-			&bytes_returned, 0);
+			&bytes_returned, CIFS_STD_OP);
 }
 
 int
@@ -844,7 +893,7 @@
 	   to the same server. We may make this configurable later or
 	   use ses->maxReq */
 
-	rc = wait_for_free_request(ses, 3);
+	rc = wait_for_free_request(ses, CIFS_BLOCKING_OP);
 	if (rc)
 		return rc;