From: Jiayuan Chen <jiayuan.chen@linux.dev>
To: Jakub Kicinski <kuba@kernel.org>
Cc: netdev@vger.kernel.org, john.fastabend@gmail.com,
sd@queasysnail.net, davem@davemloft.net, edumazet@google.com,
pabeni@redhat.com, horms@kernel.org, wilfred.mallawa@wdc.com,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH net] tls: fix TX context confusion in the max payload size setsockopt
Date: Tue, 1 Sep 2026 14:37:10 +0800 [thread overview]
Message-ID: <f777c2f2-3bf8-44e4-be5e-81dee14d445f@linux.dev> (raw)
In-Reply-To: <20260901031931.1636980-1-kuba@kernel.org>
on 9/1/26 11:19 AM, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> tls: fix TX context confusion in the max payload size setsockopt
>
> The patch replaces the unchecked tls_sw_ctx_tx() dereference in
> do_tls_setsockopt_tx_payload_len() with a new helper,
> tls_tx_record_is_open(), that dispatches on ctx->tx_conf so the TLS_HW
> path looks at tls_offload_ctx_tx(ctx)->open_record instead of a field of
> the wrong structure. The rest of the option handling is unchanged.
>
>> diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c
>> index fbb274287aa5..e4f288c1fa34 100644
>> --- a/net/tls/tls_main.c
>> +++ b/net/tls/tls_main.c
>> @@ -833,15 +833,29 @@ static int do_tls_setsockopt_no_pad(struct sock *sk, sockptr_t optval,
>> return rc;
>> }
>>
>> +/* priv_ctx_tx holds a different structure on each TX path, so tx_conf has to
>> + * say which open record to look at.
>> + */
>> +static bool tls_tx_record_is_open(struct tls_context *ctx)
>> +{
>> + switch (ctx->tx_conf) {
>> + case TLS_SW:
>> + return !!tls_sw_ctx_tx(ctx)->open_rec;
>> + case TLS_HW:
>> + return !!tls_offload_ctx_tx(ctx)->open_record;
>> + default:
>> + return false;
>> + }
>> +}
>> +
>> static int do_tls_setsockopt_tx_payload_len(struct sock *sk, sockptr_t optval,
>> unsigned int optlen)
>> {
>> struct tls_context *ctx = tls_get_ctx(sk);
>> - struct tls_sw_context_tx *sw_ctx = tls_sw_ctx_tx(ctx);
>> u16 value;
>> bool tls_13 = ctx->prot_info.version == TLS_1_3_VERSION;
>>
>> - if (sw_ctx && sw_ctx->open_rec)
>> + if (tls_tx_record_is_open(ctx))
>> return -EBUSY;
> [Severity: High]
> For the TLS_HW arm, is open_record enough to tell that no record is being
> built? do_tls_setsockopt() calls this helper with only the socket lock
> held:
>
> net/tls/tls_main.c:do_tls_setsockopt() {
> case TLS_TX_MAX_PAYLOAD_LEN:
> lock_sock(sk);
> rc = do_tls_setsockopt_tx_payload_len(sk, optval, optlen);
> release_sock(sk);
> }
>
> but the offload TX path is serialized by ctx->tx_lock, and it drops the
> socket lock at a point where open_record is already NULL:
>
> net/tls/tls_device.c:tls_push_record() {
> offload_ctx->open_record = NULL;
> ...
> return tls_push_sg(sk, ctx, offload_ctx->sg_tx_data, 0, flags);
> }
>
> tls_push_sg() calls tcp_sendmsg_locked(), which for a blocking sendmsg()
> with a full send buffer waits in sk_stream_wait_memory() and releases the
> socket lock there. tls_push_data() has a second such wait on the
> tls_do_allocation() failure path.
>
> So can a concurrent setsockopt() see open_record == NULL, take the "not
> open" branch and publish a smaller ctx->tx_max_payload_len while a sender
> is parked mid-sendmsg holding tx_lock?
Yes, this window exists, but it seems to be a pre-existing issue. I
think I can fix it by taking tx_lock.
> If so, does the offload path then keep using the old limit for the rest of
> that sendmsg? tls_push_data() latches it once, before the loop, and never
> refreshes it:
>
> net/tls/tls_device.c:tls_push_data() {
> max_open_record_len = tls_ctx->tx_max_payload_len +
> prot->prepend_size;
> do {
> ...
> copy = min_t(size_t, size, max_open_record_len - record->len);
> }
>
> That would mean setsockopt returns 0 while records larger than the new
> limit continue to go out, which does not match what
> Documentation/networking/tls.rst says about TLS_TX_MAX_PAYLOAD_LEN ("the
> kernel enforces the specified limit on all outgoing TLS records. No
> plaintext fragment will exceed this size").
>
> There is a follow-on question about the next call. If the sender leaves a
> record open across the syscall (MSG_MORE with record->num_frags <
> MAX_SKB_FRAGS - 1), the next tls_push_data() latches the new, smaller
> max_open_record_len while record->len is still the old, larger value.
> max_open_record_len is u32 and record->len is int, so does
>
> copy = min_t(size_t, size, max_open_record_len - record->len);
>
> wrap around here and drop the record-size clamp entirely, letting
> tls_append_frag() grow the record past the 2^14 plaintext maximum before
> "record->len >= max_open_record_len" closes it?
>
> Note that tls_sw_sendmsg_locked() re-reads the limit on every iteration:
>
> net/tls/tls_sw.c:tls_sw_sendmsg_locked() {
> record_room = tls_ctx->tx_max_payload_len - msg_pl->sg.size;
> }
>
> so the latched offload value looks like the odd one out.
>
> Would it be better to serialize the check and the store against the lock
> that actually protects record building? Since tls_device_sendmsg() takes
> tx_lock before lock_sock:
>
> net/tls/tls_device.c:tls_device_sendmsg() {
> mutex_lock(&tls_ctx->tx_lock);
> lock_sock(sk);
> }
>
> tx_lock is the outer lock, so taking it inside the already socket-locked
> helper is not an option; the acquisition in do_tls_setsockopt() would have
> to be reordered (or the helper made to wait for in-flight senders, as
> tls_sk_proto_cleanup() does on sk_write_pending).
>
> One smaller point: the plain read of tls_ctx->tx_max_payload_len in
> tls_push_data() against the plain write in
> do_tls_setsockopt_tx_payload_len() is an unannotated data race that KCSAN
> would flag.
I don't think so: all tls_push_data() callers hold the socket lock, and the
write is under the socket lock too, so the accesses are serialized.
prev parent reply other threads:[~2026-09-01 6:37 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 2:56 [PATCH net] tls: fix TX context confusion in the max payload size setsockopt Jiayuan Chen
2026-09-01 3:19 ` Jakub Kicinski
2026-09-01 6:37 ` Jiayuan Chen [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f777c2f2-3bf8-44e4-be5e-81dee14d445f@linux.dev \
--to=jiayuan.chen@linux.dev \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=john.fastabend@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sd@queasysnail.net \
--cc=wilfred.mallawa@wdc.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.