| From 34f333ed98300447d16f1d39bab7e241d5cc8579 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 28 Feb 2024 23:43:58 +0100 |
| Subject: tls: fix peeking with sync+async decryption |
| |
| From: Sabrina Dubroca <sd@queasysnail.net> |
| |
| [ Upstream commit 6caaf104423d809b49a67ee6500191d063b40dc6 ] |
| |
| If we peek from 2 records with a currently empty rx_list, and the |
| first record is decrypted synchronously but the second record is |
| decrypted async, the following happens: |
| 1. decrypt record 1 (sync) |
| 2. copy from record 1 to the userspace's msg |
| 3. queue the decrypted record to rx_list for future read(!PEEK) |
| 4. decrypt record 2 (async) |
| 5. queue record 2 to rx_list |
| 6. call process_rx_list to copy data from the 2nd record |
| |
| We currently pass copied=0 as skip offset to process_rx_list, so we |
| end up copying once again from the first record. We should skip over |
| the data we've already copied. |
| |
| Seen with selftest tls.12_aes_gcm.recv_peek_large_buf_mult_recs |
| |
| Fixes: 692d7b5d1f91 ("tls: Fix recvmsg() to be able to peek across multiple records") |
| Signed-off-by: Sabrina Dubroca <sd@queasysnail.net> |
| Link: https://lore.kernel.org/r/1b132d2b2b99296bfde54e8a67672d90d6d16e71.1709132643.git.sd@queasysnail.net |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/tls/tls_sw.c | 9 ++++++--- |
| 1 file changed, 6 insertions(+), 3 deletions(-) |
| |
| diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c |
| index c6ad435a44218..2bd27b77769cb 100644 |
| --- a/net/tls/tls_sw.c |
| +++ b/net/tls/tls_sw.c |
| @@ -2023,6 +2023,7 @@ int tls_sw_recvmsg(struct sock *sk, |
| struct strp_msg *rxm; |
| struct tls_msg *tlm; |
| ssize_t copied = 0; |
| + ssize_t peeked = 0; |
| bool async = false; |
| int target, err; |
| bool is_kvec = iov_iter_is_kvec(&msg->msg_iter); |
| @@ -2170,8 +2171,10 @@ int tls_sw_recvmsg(struct sock *sk, |
| if (err < 0) |
| goto put_on_rx_list_err; |
| |
| - if (is_peek) |
| + if (is_peek) { |
| + peeked += chunk; |
| goto put_on_rx_list; |
| + } |
| |
| if (partially_consumed) { |
| rxm->offset += chunk; |
| @@ -2210,8 +2213,8 @@ int tls_sw_recvmsg(struct sock *sk, |
| |
| /* Drain records from the rx_list & copy if required */ |
| if (is_peek || is_kvec) |
| - err = process_rx_list(ctx, msg, &control, copied, |
| - decrypted, is_peek, NULL); |
| + err = process_rx_list(ctx, msg, &control, copied + peeked, |
| + decrypted - peeked, is_peek, NULL); |
| else |
| err = process_rx_list(ctx, msg, &control, 0, |
| async_copy_bytes, is_peek, NULL); |
| -- |
| 2.43.0 |
| |