cifs: sanitize length checking in coalesce_t2 (try #3)

There are a couple of places in this code where these values can wrap or
go negative, and that could potentially end up overflowing the buffer.
Ensure that that doesn't happen. Do all of the length calculation and
checks first, and only perform the memcpy after they pass.

Also, increase some stack variables to 32 bits to ensure that they don't
wrap without being detected.

Finally, change the error codes to be a bit more descriptive of any
problems detected. -EINVAL isn't very accurate.

Cc: stable@kernel.org
Reported-and-Acked-by: David Howells <dhowells@redhat.com>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>

authored by Jeff Layton and committed by Steve French 2a2047bc fcda7f45

+16 -6
+16 -6
fs/cifs/connect.c
··· 274 274 char *data_area_of_target; 275 275 char *data_area_of_buf2; 276 276 int remaining; 277 - __u16 byte_count, total_data_size, total_in_buf, total_in_buf2; 277 + unsigned int byte_count, total_in_buf; 278 + __u16 total_data_size, total_in_buf2; 278 279 279 280 total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); 280 281 ··· 288 287 remaining = total_data_size - total_in_buf; 289 288 290 289 if (remaining < 0) 291 - return -EINVAL; 290 + return -EPROTO; 292 291 293 292 if (remaining == 0) /* nothing to do, ignore */ 294 293 return 0; ··· 309 308 data_area_of_target += total_in_buf; 310 309 311 310 /* copy second buffer into end of first buffer */ 312 - memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); 313 311 total_in_buf += total_in_buf2; 312 + /* is the result too big for the field? */ 313 + if (total_in_buf > USHRT_MAX) 314 + return -EPROTO; 314 315 put_unaligned_le16(total_in_buf, &pSMBt->t2_rsp.DataCount); 316 + 317 + /* fix up the BCC */ 315 318 byte_count = get_bcc_le(pTargetSMB); 316 319 byte_count += total_in_buf2; 320 + /* is the result too big for the field? */ 321 + if (byte_count > USHRT_MAX) 322 + return -EPROTO; 317 323 put_bcc_le(byte_count, pTargetSMB); 318 324 319 325 byte_count = pTargetSMB->smb_buf_length; 320 326 byte_count += total_in_buf2; 321 - 322 - /* BB also add check that we are not beyond maximum buffer size */ 323 - 327 + /* don't allow buffer to overflow */ 328 + if (byte_count > CIFSMaxBufSize) 329 + return -ENOBUFS; 324 330 pTargetSMB->smb_buf_length = byte_count; 331 + 332 + memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); 325 333 326 334 if (remaining == total_in_buf2) { 327 335 cFYI(1, "found the last secondary response");