diff options
author | Pavel Shilovsky <pshilov@microsoft.com> | 2019-07-22 14:38:22 -0400 |
---|---|---|
committer | Steve French <stfrench@microsoft.com> | 2019-08-05 23:50:13 -0400 |
commit | 3edeb4a4146dc3b54d6fa71b7ee0585cb52ebfdf (patch) | |
tree | 6c4487f7274644d38f128e31e37bba38f9880085 /fs/cifs | |
parent | e99c63e4d86d3a94818693147b469fa70de6f945 (diff) |
SMB3: Fix potential memory leak when processing compound chain
When a reconnect happens in the middle of processing a compound chain
the code leaks a buffer from the memory pool. Fix this by properly
checking for a return code and freeing buffers in case of error.
Also maintain a buf variable to be equal to either smallbuf or bigbuf
depending on a response buffer size while parsing a chain and when
returning to the caller.
Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com>
Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/smb2ops.c | 29 |
1 files changed, 17 insertions, 12 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index a5bc1b671c12..19589922ef2b 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -4070,7 +4070,6 @@ receive_encrypted_standard(struct TCP_Server_Info *server, | |||
4070 | { | 4070 | { |
4071 | int ret, length; | 4071 | int ret, length; |
4072 | char *buf = server->smallbuf; | 4072 | char *buf = server->smallbuf; |
4073 | char *tmpbuf; | ||
4074 | struct smb2_sync_hdr *shdr; | 4073 | struct smb2_sync_hdr *shdr; |
4075 | unsigned int pdu_length = server->pdu_size; | 4074 | unsigned int pdu_length = server->pdu_size; |
4076 | unsigned int buf_size; | 4075 | unsigned int buf_size; |
@@ -4100,18 +4099,15 @@ receive_encrypted_standard(struct TCP_Server_Info *server, | |||
4100 | return length; | 4099 | return length; |
4101 | 4100 | ||
4102 | next_is_large = server->large_buf; | 4101 | next_is_large = server->large_buf; |
4103 | one_more: | 4102 | one_more: |
4104 | shdr = (struct smb2_sync_hdr *)buf; | 4103 | shdr = (struct smb2_sync_hdr *)buf; |
4105 | if (shdr->NextCommand) { | 4104 | if (shdr->NextCommand) { |
4106 | if (next_is_large) { | 4105 | if (next_is_large) |
4107 | tmpbuf = server->bigbuf; | ||
4108 | next_buffer = (char *)cifs_buf_get(); | 4106 | next_buffer = (char *)cifs_buf_get(); |
4109 | } else { | 4107 | else |
4110 | tmpbuf = server->smallbuf; | ||
4111 | next_buffer = (char *)cifs_small_buf_get(); | 4108 | next_buffer = (char *)cifs_small_buf_get(); |
4112 | } | ||
4113 | memcpy(next_buffer, | 4109 | memcpy(next_buffer, |
4114 | tmpbuf + le32_to_cpu(shdr->NextCommand), | 4110 | buf + le32_to_cpu(shdr->NextCommand), |
4115 | pdu_length - le32_to_cpu(shdr->NextCommand)); | 4111 | pdu_length - le32_to_cpu(shdr->NextCommand)); |
4116 | } | 4112 | } |
4117 | 4113 | ||
@@ -4140,12 +4136,21 @@ receive_encrypted_standard(struct TCP_Server_Info *server, | |||
4140 | pdu_length -= le32_to_cpu(shdr->NextCommand); | 4136 | pdu_length -= le32_to_cpu(shdr->NextCommand); |
4141 | server->large_buf = next_is_large; | 4137 | server->large_buf = next_is_large; |
4142 | if (next_is_large) | 4138 | if (next_is_large) |
4143 | server->bigbuf = next_buffer; | 4139 | server->bigbuf = buf = next_buffer; |
4144 | else | 4140 | else |
4145 | server->smallbuf = next_buffer; | 4141 | server->smallbuf = buf = next_buffer; |
4146 | |||
4147 | buf += le32_to_cpu(shdr->NextCommand); | ||
4148 | goto one_more; | 4142 | goto one_more; |
4143 | } else if (ret != 0) { | ||
4144 | /* | ||
4145 | * ret != 0 here means that we didn't get to handle_mid() thus | ||
4146 | * server->smallbuf and server->bigbuf are still valid. We need | ||
4147 | * to free next_buffer because it is not going to be used | ||
4148 | * anywhere. | ||
4149 | */ | ||
4150 | if (next_is_large) | ||
4151 | free_rsp_buf(CIFS_LARGE_BUFFER, next_buffer); | ||
4152 | else | ||
4153 | free_rsp_buf(CIFS_SMALL_BUFFER, next_buffer); | ||
4149 | } | 4154 | } |
4150 | 4155 | ||
4151 | return ret; | 4156 | return ret; |