[CIFS] Workaround incomplete byte length returned by some servers on small SMB responses

Signed-off-by: Steve French <sfrench@us.ibm.com>

+30 -14
+1 -1
fs/cifs/cifsproto.h
··· 55 55 struct smb_hdr * /* input */ , 56 56 struct smb_hdr * /* out */ , 57 57 int * /* bytes returned */); 58 - extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); 58 + extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); 59 59 extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *); 60 60 extern int is_size_safe_to_change(struct cifsInodeInfo *); 61 61 extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *);
+29 -13
fs/cifs/misc.c
··· 418 418 } 419 419 420 420 int 421 - checkSMB(struct smb_hdr *smb, __u16 mid, int length) 421 + checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) 422 422 { 423 423 __u32 len = smb->smb_buf_length; 424 424 __u32 clc_len; /* calculated length */ 425 425 cFYI(0, ("checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len)); 426 - if (((unsigned int)length < 2 + sizeof (struct smb_hdr)) || 427 - (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)) { 428 - if ((unsigned int)length < 2 + sizeof (struct smb_hdr)) { 429 - if (((unsigned int)length >= 430 - sizeof (struct smb_hdr) - 1) 426 + 427 + if (length < 2 + sizeof (struct smb_hdr)) { 428 + if ((length >= sizeof (struct smb_hdr) - 1) 431 429 && (smb->Status.CifsError != 0)) { 432 - smb->WordCount = 0; 433 - /* some error cases do not return wct and bcc */ 430 + smb->WordCount = 0; 431 + /* some error cases do not return wct and bcc */ 432 + return 0; 433 + } else if ((length == sizeof(struct smb_hdr) + 1) && 434 + (smb->WordCount == 0)) { 435 + char * tmp = (char *)smb; 436 + /* Need to work around a bug in two servers here */ 437 + /* First, check if the part of bcc they sent was zero */ 438 + if (tmp[sizeof(struct smb_hdr)] == 0) { 439 + /* some servers return only half of bcc 440 + * on simple responses (wct, bcc both zero) 441 + * in particular have seen this on 442 + * ulogoffX and FindClose. This leaves 443 + * one byte of bcc potentially unitialized 444 + */ 445 + /* zero rest of bcc */ 446 + tmp[sizeof(struct smb_hdr)+1] = 0; 434 447 return 0; 435 - } else { 436 - cERROR(1, ("Length less than smb header size")); 437 448 } 449 + cERROR(1,("rcvd invalid byte count (bcc)")); 450 + } else { 451 + cERROR(1, ("Length less than smb header size")); 438 452 } 439 - if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) 440 - cERROR(1, ("smb length greater than MaxBufSize, mid=%d", 453 + return 1; 454 + } 455 + if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { 456 + cERROR(1, ("smb length greater than MaxBufSize, mid=%d", 441 457 smb->Mid)); 442 458 return 1; 443 459 } ··· 462 446 return 1; 463 447 clc_len = smbCalcSize_LE(smb); 464 448 465 - if(4 + len != (unsigned int)length) { 449 + if(4 + len != length) { 466 450 cERROR(1, ("Length read does not match RFC1001 length %d",len)); 467 451 return 1; 468 452 }