diff mbox series

[net] net/tls: fix corrupted data in recvmsg

Message ID 1605326982-2487-1-git-send-email-vfedorenko@novek.ru (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series [net] net/tls: fix corrupted data in recvmsg | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for net
netdev/subject_prefix success Link
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 7 lines checked
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/header_inline success Link
netdev/stable success Stable not CCed

Commit Message

Vadim Fedorenko Nov. 14, 2020, 4:09 a.m. UTC
If tcp socket has more data than Encrypted Handshake Message then
tls_sw_recvmsg will try to decrypt next record instead of returning
full control message to userspace as mentioned in comment. The next
message - usually Application Data - gets corrupted because it uses
zero copy for decryption that's why the data is not stored in skb
for next iteration. Disable zero copy for this case.

Fixes: 692d7b5d1f91 ("tls: Fix recvmsg() to be able to peek across multiple records")
Signed-off-by: Vadim Fedorenko <vfedorenko@novek.ru>
---
 net/tls/tls_sw.c | 1 +
 1 file changed, 1 insertion(+)

Comments

Jakub Kicinski Nov. 15, 2020, 2:12 a.m. UTC | #1
On Sat, 14 Nov 2020 07:09:42 +0300 Vadim Fedorenko wrote:
> If tcp socket has more data than Encrypted Handshake Message then
> tls_sw_recvmsg will try to decrypt next record instead of returning
> full control message to userspace as mentioned in comment. The next
> message - usually Application Data - gets corrupted because it uses
> zero copy for decryption that's why the data is not stored in skb
> for next iteration. Disable zero copy for this case.
> 
> Fixes: 692d7b5d1f91 ("tls: Fix recvmsg() to be able to peek across multiple records")
> Signed-off-by: Vadim Fedorenko <vfedorenko@novek.ru>

Do you have some BPF in the mix?

I don't see how we would try to go past a non-data record otherwise, 
since the loop ends like this:

		if (tls_sw_advance_skb(sk, skb, chunk)) {
			/* Return full control message to
			 * userspace before trying to parse
			 * another message type
			 */
			msg->msg_flags |= MSG_EOR;
			if (ctx->control != TLS_RECORD_TYPE_DATA)
				goto recv_end;
		} else {
			break;
		}
Vadim Fedorenko Nov. 15, 2020, 2:26 a.m. UTC | #2
No, I don't have any BPFs in test.
If we have Application Data in TCP queue then tls_sw_advance_skb
will change ctx->control from 0x16 to 0x17 (TLS_RECORD_TYPE_DATA)
and the loop will continue. The patched if will make zc = true and
data will be decrypted into msg->msg_iter.
After that the loop will break on:
                 if (!control)
                         control = tlm->control;
                 else if (control != tlm->control)
                         goto recv_end;

and the data will be lost.
Next call to recvmsg will find ctx->decrypted set to true and will
copy the unencrypted data from skb assuming that it has been decrypted
already.

The patch that I put into Fixes: changed the check you mentioned to
ctx->control, but originally it was checking the value of control that
was stored before calling to tls_sw_advance_skb.

On 15.11.2020 02:12, Jakub Kicinski wrote:
> On Sat, 14 Nov 2020 07:09:42 +0300 Vadim Fedorenko wrote:
>> If tcp socket has more data than Encrypted Handshake Message then
>> tls_sw_recvmsg will try to decrypt next record instead of returning
>> full control message to userspace as mentioned in comment. The next
>> message - usually Application Data - gets corrupted because it uses
>> zero copy for decryption that's why the data is not stored in skb
>> for next iteration. Disable zero copy for this case.
>>
>> Fixes: 692d7b5d1f91 ("tls: Fix recvmsg() to be able to peek across multiple records")
>> Signed-off-by: Vadim Fedorenko <vfedorenko@novek.ru>
> Do you have some BPF in the mix?
>
> I don't see how we would try to go past a non-data record otherwise,
> since the loop ends like this:
>
> 		if (tls_sw_advance_skb(sk, skb, chunk)) {
> 			/* Return full control message to
> 			 * userspace before trying to parse
> 			 * another message type
> 			 */
> 			msg->msg_flags |= MSG_EOR;
> 			if (ctx->control != TLS_RECORD_TYPE_DATA)
> 				goto recv_end;
> 		} else {
> 			break;
> 		}
Jakub Kicinski Nov. 15, 2020, 3:54 a.m. UTC | #3
Please don't top post.

On Sun, 15 Nov 2020 02:26:30 +0000 Vadim Fedorenko wrote:
> No, I don't have any BPFs in test.
> If we have Application Data in TCP queue then tls_sw_advance_skb
> will change ctx->control from 0x16 to 0x17 (TLS_RECORD_TYPE_DATA)
> and the loop will continue.

Ah! Missed that, unpausing the parser will make it serve us another
message and overwrite ctx.

> The patched if will make zc = true and
> data will be decrypted into msg->msg_iter.
> After that the loop will break on:
>                  if (!control)
>                          control = tlm->control;
>                  else if (control != tlm->control)
>                          goto recv_end;
>
> and the data will be lost.
> Next call to recvmsg will find ctx->decrypted set to true and will
> copy the unencrypted data from skb assuming that it has been decrypted
> already.
> 
> The patch that I put into Fixes: changed the check you mentioned to
> ctx->control, but originally it was checking the value of control that
> was stored before calling to tls_sw_advance_skb.

Is there a reason why we wouldn't just go back to checking the stored
control? Or better - put your condition there (control != ctx->control)?
Decrypting the next record seems unnecessary given we can't return it.
Vadim Fedorenko Nov. 15, 2020, 4:11 a.m. UTC | #4
On 15.11.2020 03:54, Jakub Kicinski wrote:
> Please don't top post.
>
> On Sun, 15 Nov 2020 02:26:30 +0000 Vadim Fedorenko wrote:
>> No, I don't have any BPFs in test.
>> If we have Application Data in TCP queue then tls_sw_advance_skb
>> will change ctx->control from 0x16 to 0x17 (TLS_RECORD_TYPE_DATA)
>> and the loop will continue.
> Ah! Missed that, unpausing the parser will make it serve us another
> message and overwrite ctx.
>
>> The patched if will make zc = true and
>> data will be decrypted into msg->msg_iter.
>> After that the loop will break on:
>>                   if (!control)
>>                           control = tlm->control;
>>                   else if (control != tlm->control)
>>                           goto recv_end;
>>
>> and the data will be lost.
>> Next call to recvmsg will find ctx->decrypted set to true and will
>> copy the unencrypted data from skb assuming that it has been decrypted
>> already.
>>
>> The patch that I put into Fixes: changed the check you mentioned to
>> ctx->control, but originally it was checking the value of control that
>> was stored before calling to tls_sw_advance_skb.
> Is there a reason why we wouldn't just go back to checking the stored
> control? Or better - put your condition there (control != ctx->control)?
> Decrypting the next record seems unnecessary given we can't return it.
Going back to checking stored control is working in TLS 1.2 but I cannot
test it with TLS 1.3. But it looks like it will work too in that case.
Variable control should store correct value to pass it in control message
and it's not chaged after that.
Ok, will send v2 with check reverted, thanks!
diff mbox series

Patch

diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 95ab5545..e040be1 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -1808,6 +1808,7 @@  int tls_sw_recvmsg(struct sock *sk,
 
 		if (to_decrypt <= len && !is_kvec && !is_peek &&
 		    ctx->control == TLS_RECORD_TYPE_DATA &&
+		    (!control || ctx->control == control) &&
 		    prot->version != TLS_1_3_VERSION &&
 		    !bpf_strp_enabled)
 			zc = true;