[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 struct smb_hdr * /* input */ , 56 struct smb_hdr * /* out */ , 57 int * /* bytes returned */); 58 - extern int checkSMB(struct smb_hdr *smb, __u16 mid, int length); 59 extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *); 60 extern int is_size_safe_to_change(struct cifsInodeInfo *); 61 extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *);
··· 55 struct smb_hdr * /* input */ , 56 struct smb_hdr * /* out */ , 57 int * /* bytes returned */); 58 + extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); 59 extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *); 60 extern int is_size_safe_to_change(struct cifsInodeInfo *); 61 extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *);
+29 -13
fs/cifs/misc.c
··· 418 } 419 420 int 421 - checkSMB(struct smb_hdr *smb, __u16 mid, int length) 422 { 423 __u32 len = smb->smb_buf_length; 424 __u32 clc_len; /* calculated length */ 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) 431 && (smb->Status.CifsError != 0)) { 432 - smb->WordCount = 0; 433 - /* some error cases do not return wct and bcc */ 434 return 0; 435 - } else { 436 - cERROR(1, ("Length less than smb header size")); 437 } 438 } 439 - if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) 440 - cERROR(1, ("smb length greater than MaxBufSize, mid=%d", 441 smb->Mid)); 442 return 1; 443 } ··· 462 return 1; 463 clc_len = smbCalcSize_LE(smb); 464 465 - if(4 + len != (unsigned int)length) { 466 cERROR(1, ("Length read does not match RFC1001 length %d",len)); 467 return 1; 468 }
··· 418 } 419 420 int 421 + checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) 422 { 423 __u32 len = smb->smb_buf_length; 424 __u32 clc_len; /* calculated length */ 425 cFYI(0, ("checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len)); 426 + 427 + if (length < 2 + sizeof (struct smb_hdr)) { 428 + if ((length >= sizeof (struct smb_hdr) - 1) 429 && (smb->Status.CifsError != 0)) { 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; 447 return 0; 448 } 449 + cERROR(1,("rcvd invalid byte count (bcc)")); 450 + } else { 451 + cERROR(1, ("Length less than smb header size")); 452 } 453 + return 1; 454 + } 455 + if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { 456 + cERROR(1, ("smb length greater than MaxBufSize, mid=%d", 457 smb->Mid)); 458 return 1; 459 } ··· 446 return 1; 447 clc_len = smbCalcSize_LE(smb); 448 449 + if(4 + len != length) { 450 cERROR(1, ("Length read does not match RFC1001 length %d",len)); 451 return 1; 452 }