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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox