2024-02-20, 17:50:53 -0800, Jakub Kicinski wrote: > On Tue, 20 Feb 2024 00:10:58 +0100 Sabrina Dubroca wrote: > > 2024-02-19, 12:07:03 -0800, Jakub Kicinski wrote: > > > On Thu, 15 Feb 2024 17:17:31 +0100 Sabrina Dubroca wrote: > > > > @@ -1772,7 +1772,8 @@ static int process_rx_list(struct tls_sw_context_rx *ctx, > > > > u8 *control, > > > > size_t skip, > > > > size_t len, > > > > - bool is_peek) > > > > + bool is_peek, > > > > + bool *more) > > > > { > > > > struct sk_buff *skb = skb_peek(&ctx->rx_list); > > > > struct tls_msg *tlm; > > > > > > > @@ -1844,6 +1845,10 @@ static int process_rx_list(struct tls_sw_context_rx *ctx, > > > > > > > > out: > > > > return copied ? : err; > > > > +more: > > > > + if (more) > > > > + *more = true; > > > > + goto out; > > > > > > Patches look correct, one small nit here - > > > > > > I don't have great ideas how to avoid the 7th argument completely but > > > > I hesitated between this patch and a variant combining is_peek and > > more into a single u8 *flags, but that felt a bit messy (or does that > > fall into what you describe as "not [having] great ideas"? :)) > > I guess it saves a register, it seems a bit better but then it's a > truly in/out argument :) We already do that with darg all over the receive code, so it shouldn't be too confusing to readers. It can be named flags_inout if you think that would help, or have a comment like above tls_decrypt_sg. > > > I think it'd be a little cleaner if we either: > > > - passed in err as an output argument (some datagram code does that > > > IIRC), then function can always return copied directly, or > > > > (yes, __skb_wait_for_more_packets, __skb_try_recv_datagram, and their > > variants) > > > > > - passed copied as an output argument, and then we can always return > > > err? > > > > Aren't those 2 options adding an 8th argument? > > No, no, still 7, if we separate copied from err - checking err < 0 > is enough to know that we need to exit. Right, I realized that you probably meant something like that as I was going to bed last night. It's not exactly enough, since tls_record_content_type will return 0 on a content type mismatch. We'll have to translate that into an "error". I think it would be a bit nicer to set err=1 and then check err != 0 in tls_sw_recvmsg (we can document that in a comment above process_rx_list) rather than making up a fake errno. See diff [1]. Or we could swap the 0/1 returns from tls_record_content_type and switch the err <= 0 tests to err != 0 after the existing calls, then process_rx_list doesn't have a weird special case [2]. What do you think? > Differently put, perhaps, my preference is to pass an existing entity > (err or copied), rather that conjure new concept (more) on one end and > interpret it on the other. > > > I tend to find ">= 0 on success, otherwise errno" more readable, > > probably because that's a very common pattern (either for recvmsg > > style of cases, or all the ERR_PTR type situations). > > Right it definitely is a good pattern. I think passing copied via > argument would give us those semantics still? For recvmsg sure, but not for process_rx_list. > > > I like the former a little better because we won't have to special case > > > NULL for the "after async decryption" call sites. > > > > We could also pass &rx_more every time and not check for NULL. > > > > What do you want to clean up more specifically? The number of > > arguments, the backwards goto, the NULL check before setting *more, > > something else/all of the above? > > Not compiled, but what I had in mind was something along the lines of: copied is a ssize_t (but ret isn't), so the change gets a bit uglier :( ------------ 8< ------------ [1] fix by setting err=1 in process_rx_list diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 43dd0d82b6ed..711504614da7 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1766,13 +1766,19 @@ static void tls_rx_rec_done(struct tls_sw_context_rx *ctx) * decrypted records into the buffer provided by caller zero copy is not * true. Further, the records are removed from the rx_list if it is not a peek * case and the record has been consumed completely. + * + * Return: + * - 0 if len bytes were copied + * - 1 if < len bytes were copied due to a record type mismatch + * - <0 if an error occurred */ static int process_rx_list(struct tls_sw_context_rx *ctx, struct msghdr *msg, u8 *control, size_t skip, size_t len, - bool is_peek) + bool is_peek, + ssize_t *out_copied) { struct sk_buff *skb = skb_peek(&ctx->rx_list); struct tls_msg *tlm; @@ -1802,8 +1808,11 @@ static int process_rx_list(struct tls_sw_context_rx *ctx, tlm = tls_msg(skb); err = tls_record_content_type(msg, tlm, control); - if (err <= 0) + if (err <= 0) { + if (err == 0) + err = 1; goto out; + } err = skb_copy_datagram_msg(skb, rxm->offset + skip, msg, chunk); @@ -1843,7 +1852,8 @@ static int process_rx_list(struct tls_sw_context_rx *ctx, err = 0; out: - return copied ? : err; + *out_copied = copied; + return err; } static bool @@ -1966,11 +1976,10 @@ int tls_sw_recvmsg(struct sock *sk, goto end; /* Process pending decrypted records. It must be non-zero-copy */ - err = process_rx_list(ctx, msg, &control, 0, len, is_peek); - if (err < 0) + err = process_rx_list(ctx, msg, &control, 0, len, is_peek, &copied); + if (err != 0) goto end; - copied = err; if (len <= copied || (copied && control != TLS_RECORD_TYPE_DATA)) goto end; @@ -2114,6 +2123,7 @@ int tls_sw_recvmsg(struct sock *sk, recv_end: if (async) { + ssize_t ret2; int ret; /* Wait for all previously submitted records to be decrypted */ @@ -2130,10 +2140,10 @@ 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); + decrypted, is_peek, &ret2); else err = process_rx_list(ctx, msg, &control, 0, - async_copy_bytes, is_peek); + async_copy_bytes, is_peek, &ret2); } copied += decrypted; ------------ 8< ------------ [2] fixing the bug by changing tls_record_content_type as well diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 43dd0d82b6ed..3da62ba97945 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1734,6 +1734,11 @@ int decrypt_skb(struct sock *sk, struct scatterlist *sgout) return tls_decrypt_sg(sk, NULL, sgout, &darg); } +/* Return: + * - 0 on success + * - 1 if the record's type doesn't match the value in control + * - <0 if an error occurred + */ static int tls_record_content_type(struct msghdr *msg, struct tls_msg *tlm, u8 *control) { @@ -1751,10 +1756,10 @@ static int tls_record_content_type(struct msghdr *msg, struct tls_msg *tlm, return -EIO; } } else if (*control != tlm->control) { - return 0; + return 1; } - return 1; + return 0; } static void tls_rx_rec_done(struct tls_sw_context_rx *ctx) @@ -1766,13 +1771,19 @@ static void tls_rx_rec_done(struct tls_sw_context_rx *ctx) * decrypted records into the buffer provided by caller zero copy is not * true. Further, the records are removed from the rx_list if it is not a peek * case and the record has been consumed completely. + * + * Return: + * - 0 if len bytes were copied + * - 1 if < len bytes were copied due to a record type mismatch + * - <0 if an error occurred */ static int process_rx_list(struct tls_sw_context_rx *ctx, struct msghdr *msg, u8 *control, size_t skip, size_t len, - bool is_peek) + bool is_peek, + ssize_t *out_copied) { struct sk_buff *skb = skb_peek(&ctx->rx_list); struct tls_msg *tlm; @@ -1784,7 +1795,7 @@ static int process_rx_list(struct tls_sw_context_rx *ctx, tlm = tls_msg(skb); err = tls_record_content_type(msg, tlm, control); - if (err <= 0) + if (err != 0) goto out; if (skip < rxm->full_len) @@ -1802,7 +1813,7 @@ static int process_rx_list(struct tls_sw_context_rx *ctx, tlm = tls_msg(skb); err = tls_record_content_type(msg, tlm, control); - if (err <= 0) + if (err != 0) goto out; err = skb_copy_datagram_msg(skb, rxm->offset + skip, @@ -1843,7 +1854,8 @@ static int process_rx_list(struct tls_sw_context_rx *ctx, err = 0; out: - return copied ? : err; + *out_copied = copied; + return err; } static bool @@ -1966,11 +1978,10 @@ int tls_sw_recvmsg(struct sock *sk, goto end; /* Process pending decrypted records. It must be non-zero-copy */ - err = process_rx_list(ctx, msg, &control, 0, len, is_peek); - if (err < 0) + err = process_rx_list(ctx, msg, &control, 0, len, is_peek, &copied); + if (err != 0) goto end; - copied = err; if (len <= copied || (copied && control != TLS_RECORD_TYPE_DATA)) goto end; @@ -2032,7 +2043,7 @@ int tls_sw_recvmsg(struct sock *sk, * For tls1.3, we disable async. */ err = tls_record_content_type(msg, tls_msg(darg.skb), &control); - if (err <= 0) { + if (err != 0) { DEBUG_NET_WARN_ON_ONCE(darg.zc); tls_rx_rec_done(ctx); put_on_rx_list_err: @@ -2114,6 +2125,7 @@ int tls_sw_recvmsg(struct sock *sk, recv_end: if (async) { + ssize_t ret2; int ret; /* Wait for all previously submitted records to be decrypted */ @@ -2130,10 +2142,10 @@ 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); + decrypted, is_peek, &ret2); else err = process_rx_list(ctx, msg, &control, 0, - async_copy_bytes, is_peek); + async_copy_bytes, is_peek, &ret2); } copied += decrypted; -- Sabrina