diff options
Diffstat (limited to 'fs/cifs/misc.c')
| -rw-r--r-- | fs/cifs/misc.c | 116 | 
1 files changed, 65 insertions, 51 deletions
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index a09e077ba92..2a930a752a7 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -236,10 +236,7 @@ __u16 GetNextMid(struct TCP_Server_Info *server)  {  	__u16 mid = 0;  	__u16 last_mid; -	int   collision; - -	if (server == NULL) -		return mid; +	bool collision;  	spin_lock(&GlobalMid_Lock);  	last_mid = server->CurrentMid; /* we do not want to loop forever */ @@ -252,24 +249,38 @@ __u16 GetNextMid(struct TCP_Server_Info *server)  	(and it would also have to have been a request that  	 did not time out) */  	while (server->CurrentMid != last_mid) { -		struct list_head *tmp;  		struct mid_q_entry *mid_entry; +		unsigned int num_mids; -		collision = 0; +		collision = false;  		if (server->CurrentMid == 0)  			server->CurrentMid++; -		list_for_each(tmp, &server->pending_mid_q) { -			mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - -			if ((mid_entry->mid == server->CurrentMid) && -			    (mid_entry->midState == MID_REQUEST_SUBMITTED)) { +		num_mids = 0; +		list_for_each_entry(mid_entry, &server->pending_mid_q, qhead) { +			++num_mids; +			if (mid_entry->mid == server->CurrentMid && +			    mid_entry->midState == MID_REQUEST_SUBMITTED) {  				/* This mid is in use, try a different one */ -				collision = 1; +				collision = true;  				break;  			}  		} -		if (collision == 0) { + +		/* +		 * if we have more than 32k mids in the list, then something +		 * is very wrong. Possibly a local user is trying to DoS the +		 * box by issuing long-running calls and SIGKILL'ing them. If +		 * we get to 2^16 mids then we're in big trouble as this +		 * function could loop forever. +		 * +		 * Go ahead and assign out the mid in this situation, but force +		 * an eventual reconnect to clean out the pending_mid_q. +		 */ +		if (num_mids > 32768) +			server->tcpStatus = CifsNeedReconnect; + +		if (!collision) {  			mid = server->CurrentMid;  			break;  		} @@ -381,29 +392,31 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,  }  static int -checkSMBhdr(struct smb_hdr *smb, __u16 mid) +check_smb_hdr(struct smb_hdr *smb, __u16 mid)  { -	/* Make sure that this really is an SMB, that it is a response, -	   and that the message ids match */ -	if ((*(__le32 *) smb->Protocol == cpu_to_le32(0x424d53ff)) && -		(mid == smb->Mid)) { -		if (smb->Flags & SMBFLG_RESPONSE) -			return 0; -		else { -		/* only one valid case where server sends us request */ -			if (smb->Command == SMB_COM_LOCKING_ANDX) -				return 0; -			else -				cERROR(1, "Received Request not response"); -		} -	} else { /* bad signature or mid */ -		if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) -			cERROR(1, "Bad protocol string signature header %x", -				*(unsigned int *) smb->Protocol); -		if (mid != smb->Mid) -			cERROR(1, "Mids do not match"); +	/* does it have the right SMB "signature" ? */ +	if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) { +		cERROR(1, "Bad protocol string signature header 0x%x", +			*(unsigned int *)smb->Protocol); +		return 1;  	} -	cERROR(1, "bad smb detected. The Mid=%d", smb->Mid); + +	/* Make sure that message ids match */ +	if (mid != smb->Mid) { +		cERROR(1, "Mids do not match. received=%u expected=%u", +			smb->Mid, mid); +		return 1; +	} + +	/* if it's a response then accept */ +	if (smb->Flags & SMBFLG_RESPONSE) +		return 0; + +	/* only one valid case where server sends us request */ +	if (smb->Command == SMB_COM_LOCKING_ANDX) +		return 0; + +	cERROR(1, "Server sent request, not response. mid=%u", smb->Mid);  	return 1;  } @@ -448,7 +461,7 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length)  		return 1;  	} -	if (checkSMBhdr(smb, mid)) +	if (check_smb_hdr(smb, mid))  		return 1;  	clc_len = smbCalcSize_LE(smb); @@ -465,25 +478,26 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length)  			if (((4 + len) & 0xFFFF) == (clc_len & 0xFFFF))  				return 0; /* bcc wrapped */  		} -		cFYI(1, "Calculated size %d vs length %d mismatch for mid %d", +		cFYI(1, "Calculated size %u vs length %u mismatch for mid=%u",  				clc_len, 4 + len, smb->Mid); -		/* Windows XP can return a few bytes too much, presumably -		an illegal pad, at the end of byte range lock responses -		so we allow for that three byte pad, as long as actual -		received length is as long or longer than calculated length */ -		/* We have now had to extend this more, since there is a -		case in which it needs to be bigger still to handle a -		malformed response to transact2 findfirst from WinXP when -		access denied is returned and thus bcc and wct are zero -		but server says length is 0x21 bytes too long as if the server -		forget to reset the smb rfc1001 length when it reset the -		wct and bcc to minimum size and drop the t2 parms and data */ -		if ((4+len > clc_len) && (len <= clc_len + 512)) -			return 0; -		else { -			cERROR(1, "RFC1001 size %d bigger than SMB for Mid=%d", + +		if (4 + len < clc_len) { +			cERROR(1, "RFC1001 size %u smaller than SMB for mid=%u",  					len, smb->Mid);  			return 1; +		} else if (len > clc_len + 512) { +			/* +			 * Some servers (Windows XP in particular) send more +			 * data than the lengths in the SMB packet would +			 * indicate on certain calls (byte range locks and +			 * trans2 find first calls in particular). While the +			 * client can handle such a frame by ignoring the +			 * trailing data, we choose limit the amount of extra +			 * data to 512 bytes. +			 */ +			cERROR(1, "RFC1001 size %u more than 512 bytes larger " +				  "than SMB for mid=%u", len, smb->Mid); +			return 1;  		}  	}  	return 0;  |