From nobody Thu Oct 15 23:34:48 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4832E4D09E4 for ; Wed, 14 Oct 2026 08:59:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791968394; cv=none; b=MIicFhJsPlz+ZjMxF/QVbs3+tUa2iX0Kf7G0xBeowazPf9wunHtVzZZCQnH0XFebTqClsOp1zRjqShJJ+T++/rLq8iACXWd6nVsDu075D2c0XxJZFo2UJDxoFkKclknkXb5XK2bcxuj6L7/ohuEOBejt+KvHrxU2ufpv7QdGy9g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791968394; c=relaxed/simple; bh=pV2EFKrhwH3ccCz0jbg2t/PlG8SRvN9O99MOWFt+Mg0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=c05/E8CL44ADn63qyO/CBUSF/ZoRjptJmIKpu7+5YdC5X3hypiM3yBybr7GoMXYTiwDVa0auL04UogRODySGAGs8VAzOIh8l9Ez1She27RGUkmYyXJx6645gHNruo0rq//dNrZX0m6ptaBZO3NYSOQzGp41f++H5OsFlsW/k5GE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WdBd/pcf; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WdBd/pcf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5DB371F00893; Wed, 14 Oct 2026 08:59:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1791968383; bh=qaU+1pPTN8tFI7Dg4CDmANYdYcJYA8PQKztjnHUkRfE=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=WdBd/pcfFSOrw7W5QzCj2iy1yPbudMLF8NuotPnmaxoEaYrw8WIGdY3dYUI1YQUgq Q40p3kIst+6IVdueTvk0zdxva+A5/aIkdQ4URdrs429ZfIZSK371kre2Senjh4pwHo ZiL9ml8xz+FnWuA08T6zgbP/XtDVU8VCxnZw/ZpoKu/5svEA7g7/0Kus3NYRVDwMu1 4BcnALdbIDp+8LFdaCVcf4aGQzOCvVjsIHNxYI01DNwnxS7/WTXyldzwzDrkZJE3UY jOclVy4tyBM9VvChqzyJS5gAbGDCy/istxNI90sB01htATKMy7ADc6o7p49tiYtG4k To3Ilvwh3Zysg== From: Geliang Tang To: mptcp@lists.linux.dev Cc: Geliang Tang Subject: [PATCH mptcp-next 1/2] tls: implement tls_disconnect Date: Wed, 14 Oct 2026 16:59:35 +0800 Message-ID: <6ffac9e36dab9b4f5fdb6e55a744b35f0d51ffb4.1791967771.git.tanggeliang@kylinos.cn> X-Mailer: git-send-email 2.55.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Geliang Tang Implement disconnect support for TLS sockets via connect(AF_UNSPEC). The handler invokes the underlying disconnect() first, then on success cancels TX work (for TLS_SW), calls tls_sk_proto_free() to restore original protocol operations and clear TLS state, cleans up RX context and frees the TLS context. RX context uses kfree(), TLS context uses kfree_rcu() for RCU safety. tls_sk_proto_free() restores sk_prot, sk_socket->ops, sk_write_space, and sk_destruct to their original values, clears icsk_ulp_data and icsk_ulp_ops when free_ctx is true, and frees the TX sub-context. The RX context and main TLS context are freed separately by tls_disconnect(). Add sk_proto_ops field to tls_context to save the original sk_socket->ops. Unlike close() where the socket is destroyed, disconnect leaves the socket alive for potential reconnection. sk_prot and sk_socket->ops are restored inside tls_sk_proto_free() to prevent NULL pointer dereference from concurrent poll/read/splice operations that dispatch through sk_socket->ops. After releasing the socket lock to cancel TX work, re-check the context pointer to handle concurrent close(). Clear icsk_ulp_ops when freeing the context to prevent tcp_set_ulp from failing with -EEXIST on reconnection. Signed-off-by: Geliang Tang --- include/net/tls.h | 12 +++-- net/tls/tls_main.c | 112 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 108 insertions(+), 16 deletions(-) diff --git a/include/net/tls.h b/include/net/tls.h index e57bef58851e..25ad76df97cb 100644 --- a/include/net/tls.h +++ b/include/net/tls.h @@ -193,6 +193,10 @@ enum tls_context_flags { * tls_dev_del call in tls_device_down if it happens simultaneously. */ TLS_RX_DEV_CLOSED =3D 2, + /* tls_disconnect() is in progress, prevent concurrent close() from + * freeing resources while disconnect is using them. + */ + TLS_DISCONNECTING =3D 3, }; =20 struct cipher_context { @@ -259,6 +263,7 @@ struct tls_context { =20 /* cache cold stuff */ struct proto *sk_proto; + const struct proto_ops *sk_proto_ops; struct sock *sk; =20 void (*sk_destruct)(struct sock *sk); @@ -374,10 +379,11 @@ static inline struct tls_context *tls_get_ctx(const s= truct sock *sk) { const struct inet_connection_sock *icsk =3D inet_csk(sk); =20 - /* Use RCU on icsk_ulp_data only for sock diag code, - * TLS data path doesn't need rcu_dereference(). + /* Use rcu_access_pointer() to safely read icsk_ulp_data, + * as it can be cleared concurrently by tls_disconnect() + * or tls_sk_proto_close(). */ - return (__force void *)icsk->icsk_ulp_data; + return (__force void *)rcu_access_pointer(icsk->icsk_ulp_data); } =20 static inline struct tls_sw_context_rx *tls_sw_ctx_rx( diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index 8bdb78718a8d..d00e56af628a 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -358,32 +358,71 @@ static void tls_sk_proto_cleanup(struct sock *sk, } } =20 -static void tls_sk_proto_close(struct sock *sk, long timeout) +static void tls_sk_proto_free(struct sock *sk, + struct tls_context *ctx, + bool free_ctx) { struct inet_connection_sock *icsk =3D inet_csk(sk); - struct tls_context *ctx =3D tls_get_ctx(sk); long timeo =3D sock_sndtimeo(sk, 0); - bool free_ctx; - - if (ctx->tx_conf =3D=3D TLS_SW) - tls_sw_cancel_work_tx(ctx); - - lock_sock(sk); - free_ctx =3D ctx->tx_conf !=3D TLS_HW && ctx->rx_conf !=3D TLS_HW; =20 if (ctx->tx_conf !=3D TLS_BASE || ctx->rx_conf !=3D TLS_BASE) tls_sk_proto_cleanup(sk, ctx, timeo); =20 write_lock_bh(&sk->sk_callback_lock); - if (free_ctx) - rcu_assign_pointer(icsk->icsk_ulp_data, NULL); WRITE_ONCE(sk->sk_prot, ctx->sk_proto); + if (sk->sk_socket) + WRITE_ONCE(sk->sk_socket->ops, ctx->sk_proto_ops); if (sk->sk_write_space =3D=3D tls_write_space) sk->sk_write_space =3D ctx->sk_write_space; + if (sk->sk_destruct =3D=3D tls_device_sk_destruct) + sk->sk_destruct =3D ctx->sk_destruct; + if (free_ctx) { + rcu_assign_pointer(icsk->icsk_ulp_data, NULL); + if (icsk->icsk_ulp_ops) { + /* Balance try_module_get in __tcp_ulp_find_autoload */ + module_put(icsk->icsk_ulp_ops->owner); + icsk->icsk_ulp_ops =3D NULL; + } + } write_unlock_bh(&sk->sk_callback_lock); - release_sock(sk); if (ctx->tx_conf =3D=3D TLS_SW) tls_sw_free_ctx_tx(ctx); +} + +static void tls_sk_proto_close(struct sock *sk, long timeout) +{ + struct tls_context *ctx; + bool free_ctx; + + ctx =3D tls_get_ctx(sk); + if (!ctx) { + sk->sk_prot->close(sk, timeout); + return; + } + + if (ctx->tx_conf =3D=3D TLS_SW) + tls_sw_cancel_work_tx(ctx); + + lock_sock(sk); + /* Re-read ctx under lock, as it may have been cleared by disconnect */ + ctx =3D tls_get_ctx(sk); + if (!ctx) { + release_sock(sk); + sk->sk_prot->close(sk, timeout); + return; + } + + /* Check if disconnect is in progress after acquiring lock */ + if (test_bit(TLS_DISCONNECTING, &ctx->flags)) { + release_sock(sk); + ctx->sk_proto->close(sk, timeout); + return; + } + + free_ctx =3D ctx->tx_conf !=3D TLS_HW && ctx->rx_conf !=3D TLS_HW; + tls_sk_proto_free(sk, ctx, free_ctx); + release_sock(sk); + if (ctx->rx_conf =3D=3D TLS_SW || ctx->rx_conf =3D=3D TLS_HW) tls_sw_strparser_done(ctx); if (ctx->rx_conf =3D=3D TLS_SW) @@ -904,7 +943,52 @@ static int tls_setsockopt(struct sock *sk, int level, = int optname, =20 static int tls_disconnect(struct sock *sk, int flags) { - return -EOPNOTSUPP; + struct tls_context *ctx; + bool free_ctx; + int rc; + + ctx =3D tls_get_ctx(sk); + if (!ctx) + return 0; + + if (!ctx->sk_proto->disconnect) + return -EOPNOTSUPP; + + set_bit(TLS_DISCONNECTING, &ctx->flags); + + rc =3D ctx->sk_proto->disconnect(sk, flags); + if (rc) { + clear_bit(TLS_DISCONNECTING, &ctx->flags); + return rc; + } + + if (ctx->tx_conf =3D=3D TLS_SW) { + struct tls_sw_context_tx *sw_ctx_tx =3D tls_sw_ctx_tx(ctx); + + set_bit(BIT_TX_CLOSING, &sw_ctx_tx->tx_bitmask); + set_bit(BIT_TX_SCHEDULED, &sw_ctx_tx->tx_bitmask); + + release_sock(sk); + disable_delayed_work_sync(&sw_ctx_tx->tx_work.work); + lock_sock(sk); + } + + free_ctx =3D ctx->tx_conf !=3D TLS_HW && ctx->rx_conf !=3D TLS_HW; + tls_sk_proto_free(sk, ctx, free_ctx); + + if (ctx->rx_conf =3D=3D TLS_SW) { + struct tls_sw_context_rx *sw_ctx =3D tls_sw_ctx_rx(ctx); + + release_sock(sk); + tls_strp_done(&sw_ctx->strp); + tls_sw_free_ctx_rx(ctx); + lock_sock(sk); + } + + if (free_ctx) + tls_ctx_free(sk, ctx); + + return 0; } =20 struct tls_context *tls_ctx_create(struct sock *sk) @@ -918,6 +1002,8 @@ struct tls_context *tls_ctx_create(struct sock *sk) =20 mutex_init(&ctx->tx_lock); ctx->sk_proto =3D READ_ONCE(sk->sk_prot); + if (sk->sk_socket) + ctx->sk_proto_ops =3D READ_ONCE(sk->sk_socket->ops); ctx->sk =3D sk; /* Release semantic of rcu_assign_pointer() ensures that * ctx->sk_proto is visible before changing sk->sk_prot in --=20 2.55.0 From nobody Thu Oct 15 23:34:48 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 439F3488223 for ; Wed, 14 Oct 2026 08:59:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791968390; cv=none; b=Yda6JYNIeZPGd5HOE9RMdCXobTUHDebEJSgzHtvVytcTxwYgdyF/evbXBu7lVID+iYr2tewnNMSGcSFnA3AUb6rAE0iVBHTryyC686iWUjnZsjLQnSBNRd1bMXeRlO2rnZaqCgX80YpKeoFXS5eL8qVFE18o6g5+GqTFX+Ohhcs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791968390; c=relaxed/simple; bh=oVcOwR0M46fCD00pE9/CVA9WFOddW+Sw9i/Qskj2BVc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=B7+PA3wmCJV+luDFHh4uu8BWlhQwJTyNGyCxMd0fI8k6kJYrmnsCd/j54p9j0ei+6+M+FV4Zmcqr2Z4AZIn0IpXnoQn7fK1p90fjuCDEVzre+YxiTsTHkzH8bUCd8HwaWKBhPdXjWQ4ypW5yfYtbD8KB+ubpVVMCK04B7PKjx9s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OkdsY5V0; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="OkdsY5V0" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3794B1F00898; Wed, 14 Oct 2026 08:59:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1791968385; bh=iHBE2Mjg65gq4Kz3vDMStX2hxqEnkP51NZqzB6UpznM=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=OkdsY5V08cFWkeND/FsQBBd2fI0OKsJ8jdfupTFu0QqS1zHZ8JVB3zV0QvNtNJY1g WnAYdYzNG+Htp9Oka+aQrInDWFwy7jixhcomIlOq8IHOTIzXEBqFJkOLzDK6dgqKmm UvTQSQk1PXdm22IKGn3O2eek79kH3Ha4gutz6QkhdrpDtmZezImGpAC0KdLiDJWwGx rThD+ueyj7FTLWbMZn81qs03l42Ub9TZHM8+Rc2ixKqO2BnO53UYo04jFol4QRMeYR Ml3K9YBlx4Fla9CpWqjb7Ugs1RWk3fcnm0/iTvi3SjqaF4hM/7is2yhQWrwvahEXMI mabmWtZQBsKkw== From: Geliang Tang To: mptcp@lists.linux.dev Cc: Geliang Tang Subject: [PATCH mptcp-next 2/2] selftests: tls: update disconnect tests Date: Wed, 14 Oct 2026 16:59:36 +0800 Message-ID: <8895fee0bf85912767c9f52cdfe320438c136432.1791967771.git.tanggeliang@kylinos.cn> X-Mailer: git-send-email 2.55.0 In-Reply-To: References: Precedence: bulk X-Mailing-List: mptcp@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Geliang Tang Update the TLS disconnect test to verify the full disconnect/reconnect cycle. The test first establishes a TLS connection and verifies data transfer works. It then disconnects both ends of the connection using connect(AF_UNSPEC) and verifies that send/recv operations fail with appropriate error codes (EPIPE/ECONNRESET for send, ENOTCONN for recv). The client socket is then reconnected to a new server, TLS is re-established, and data transfer is verified to work again. This ensures TLS sockets can be properly disconnected and reconnected without state corruption. Signed-off-by: Geliang Tang --- tools/testing/selftests/net/tls.c | 57 +++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/ne= t/tls.c index 9d3cd4fff062..ada2f43d05d3 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -1985,8 +1985,10 @@ TEST_F(tls_basic, disconnect) int send_len =3D strlen(test_str) + 1; struct tls_crypto_info_keys key; struct sockaddr_in addr; + socklen_t len; char buf[20]; int ret; + int sfd; =20 if (self->notls) return; @@ -2005,14 +2007,65 @@ TEST_F(tls_basic, disconnect) ret =3D setsockopt(self->cfd, SOL_TLS, TLS_RX, &key, key.len); ASSERT_EQ(ret, 0); =20 + bzero(buf, sizeof(buf)); + EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len); + EXPECT_EQ(memcmp(buf, test_str, send_len), 0); + addr.sin_family =3D AF_UNSPEC; addr.sin_addr.s_addr =3D htonl(INADDR_ANY); addr.sin_port =3D 0; ret =3D connect(self->cfd, &addr, sizeof(addr)); - EXPECT_EQ(ret, -1); - EXPECT_EQ(errno, EOPNOTSUPP); + EXPECT_EQ(ret, 0); + + /* Verify send/recv fail after disconnect. */ + EXPECT_EQ(send(self->cfd, buf, sizeof(buf), + MSG_DONTWAIT | MSG_NOSIGNAL), -1); + EXPECT_TRUE(errno =3D=3D EPIPE || errno =3D=3D ECONNRESET); + EXPECT_EQ(recv(self->cfd, buf, sizeof(buf), MSG_DONTWAIT), -1); + EXPECT_EQ(errno, ENOTCONN); + + /* Disconnect fd as well. */ + ret =3D connect(self->fd, &addr, sizeof(addr)); + ASSERT_EQ(ret, 0); + EXPECT_EQ(send(self->fd, buf, sizeof(buf), + MSG_DONTWAIT | MSG_NOSIGNAL), -1); + EXPECT_TRUE(errno =3D=3D EPIPE || errno =3D=3D ECONNRESET); + EXPECT_EQ(recv(self->fd, buf, sizeof(buf), MSG_DONTWAIT), -1); + EXPECT_EQ(errno, ENOTCONN); + + /* Reconnect fd to a new server and re-setup TLS. */ + addr.sin_family =3D AF_INET; + addr.sin_port =3D 0; + sfd =3D socket(AF_INET, SOCK_STREAM, 0); + EXPECT_GE(sfd, 0); + if (sfd < 0) + return; + + /* sfd not closed on ASSERT failure, kernel cleans up on test abort */ + ASSERT_EQ(bind(sfd, &addr, sizeof(addr)), 0); + ASSERT_EQ(listen(sfd, 10), 0); + len =3D sizeof(addr); + ASSERT_EQ(getsockname(sfd, &addr, &len), 0); + ASSERT_EQ(connect(self->fd, (struct sockaddr *)&addr, sizeof(addr)), 0); =20 + ret =3D accept(sfd, &addr, &len); + close(sfd); + ASSERT_GE(ret, 0); + close(self->cfd); + self->cfd =3D ret; + + ASSERT_EQ(setsockopt(self->fd, IPPROTO_TCP, TCP_ULP, "tls", + sizeof("tls")), 0); + ASSERT_EQ(setsockopt(self->cfd, IPPROTO_TCP, TCP_ULP, "tls", + sizeof("tls")), 0); + ASSERT_EQ(setsockopt(self->fd, SOL_TLS, TLS_TX, &key, key.len), 0); + ASSERT_EQ(setsockopt(self->cfd, SOL_TLS, TLS_RX, &key, key.len), 0); + + /* Verify data transfer works after reconnect. */ + EXPECT_EQ(send(self->fd, test_str, send_len, 0), send_len); + bzero(buf, sizeof(buf)); EXPECT_EQ(recv(self->cfd, buf, send_len, 0), send_len); + EXPECT_EQ(memcmp(buf, test_str, send_len), 0); } =20 TEST_F(tls, rekey) --=20 2.55.0