[LTS 8.6] CVE-2026-31669, CVE-2026-46135, CVE-2026-43198 - #1561
Conversation
|
🤖 Validation Checks In Progress Workflow run: https://github.com/ctrliq/kernel-src-tree/actions/runs/33412080664 |
🔍 Interdiff Analysis
================================================================================
* DELTA DIFFERENCES - code changes that differ between the patches *
================================================================================
--- b/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1123,14 +1123,12 @@
static void tcp_v6_mapped_child_init(struct sock *newsk, const struct sock *sk)
{
struct inet_sock *newinet = inet_sk(newsk);
- struct tcp6_sock *newtcp6sk;
- struct ipv6_pinfo *newnp = inet6_sk(newsk);
- const struct ipv6_pinfo *np = inet6_sk(sk);
+ struct ipv6_pinfo *newnp;
- newtcp6sk = (struct tcp6_sock *)newsk;
- inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
+ newinet->pinet6 = newnp = tcp_inet6_sk(newsk);
+ newinet->ipv6_fl_list = NULL;
- memcpy(newnp, np, sizeof(struct ipv6_pinfo));
+ memcpy(newnp, tcp_inet6_sk(sk), sizeof(struct ipv6_pinfo));
newnp->saddr = newsk->sk_v6_rcv_saddr;
@@ -1138,13 +1136,12 @@
if (sk_is_mptcp(newsk))
mptcpv6_handle_mapped(newsk, true);
newsk->sk_backlog_rcv = tcp_v4_do_rcv;
-#if CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
tcp_sk(newsk)->af_specific = &tcp_sock_ipv6_mapped_specific;
#endif
newnp->ipv6_mc_list = NULL;
newnp->ipv6_ac_list = NULL;
- newnp->ipv6_fl_list = NULL;
newnp->pktoptions = NULL;
newnp->opt = NULL;
@@ -1153,7 +1150,7 @@
newnp->mcast_hops = newinet->mc_ttl;
newnp->rcv_flowinfo = 0;
- if (np->repflow)
+ if (inet6_test_bit(REPFLOW, sk))
newnp->flow_label = 0;
}
@@ -1179,10 +1176,62 @@
#endif
struct flowi6 fl6;
- if (skb->protocol == htons(ETH_P_IP))
- return tcp_v4_syn_recv_sock(sk, skb, req, dst,
- req_unhash, own_req,
- tcp_v6_mapped_child_init);
+ if (skb->protocol == htons(ETH_P_IP)) {
+ /*
+ * v6 mapped
+ */
+
+ newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst,
+ req_unhash, own_req);
+
+ if (!newsk)
+ return NULL;
+
+ newtcp6sk = (struct tcp6_sock *)newsk;
+ inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
+
+ newinet = inet_sk(newsk);
+ newnp = inet6_sk(newsk);
+ newtp = tcp_sk(newsk);
+
+ memcpy(newnp, np, sizeof(struct ipv6_pinfo));
+
+ newnp->saddr = newsk->sk_v6_rcv_saddr;
+
+ inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
+ if (sk_is_mptcp(newsk))
+ mptcpv6_handle_mapped(newsk, true);
+ newsk->sk_backlog_rcv = tcp_v4_do_rcv;
+#ifdef CONFIG_TCP_MD5SIG
+ newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
+#endif
+
+ newnp->ipv6_mc_list = NULL;
+ newnp->ipv6_ac_list = NULL;
+ newnp->ipv6_fl_list = NULL;
+ newnp->pktoptions = NULL;
+ newnp->opt = NULL;
+ newnp->mcast_oif = inet_iif(skb);
+ newnp->mcast_hops = ip_hdr(skb)->ttl;
+ newnp->rcv_flowinfo = 0;
+ if (np->repflow)
+ newnp->flow_label = 0;
+
+ /*
+ * No need to charge this sock to the relevant IPv6 refcnt debug socks count
+ * here, tcp_create_openreq_child now does this for us, see the comment in
+ * that function for the gory details. -acme
+ */
+
+ /* It is tricky place. Until this moment IPv4 tcp
+ worked with IPv6 icsk.icsk_af_ops.
+ Sync it now.
+ */
+ tcp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
+
+ return newsk;
+ }
+
ireq = inet_rsk(req);
if (sk_acceptq_is_full(sk))
################################################################################
! REJECTED PATCH2 HUNKS - could not be compared; manual review needed !
################################################################################
--- b/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1369,61 +1406,10 @@
#endif
struct flowi6 fl6;
- if (skb->protocol == htons(ETH_P_IP)) {
- /*
- * v6 mapped
- */
-
- newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst,
- req_unhash, own_req);
-
- if (!newsk)
- return NULL;
-
- newinet = inet_sk(newsk);
- newinet->pinet6 = tcp_inet6_sk(newsk);
- newinet->ipv6_fl_list = NULL;
-
- newnp = tcp_inet6_sk(newsk);
- newtp = tcp_sk(newsk);
-
- memcpy(newnp, np, sizeof(struct ipv6_pinfo));
-
- newnp->saddr = newsk->sk_v6_rcv_saddr;
-
- inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
- if (sk_is_mptcp(newsk))
- mptcpv6_handle_mapped(newsk, true);
- newsk->sk_backlog_rcv = tcp_v4_do_rcv;
-#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
- newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
-#endif
-
- newnp->ipv6_mc_list = NULL;
- newnp->ipv6_ac_list = NULL;
- newnp->pktoptions = NULL;
- newnp->opt = NULL;
- newnp->mcast_oif = inet_iif(skb);
- newnp->mcast_hops = ip_hdr(skb)->ttl;
- newnp->rcv_flowinfo = 0;
- if (inet6_test_bit(REPFLOW, sk))
- newnp->flow_label = 0;
-
- /*
- * No need to charge this sock to the relevant IPv6 refcnt debug socks count
- * here, tcp_create_openreq_child now does this for us, see the comment in
- * that function for the gory details. -acme
- */
-
- /* It is tricky place. Until this moment IPv4 tcp
- worked with IPv6 icsk.icsk_af_ops.
- Sync it now.
- */
- tcp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
-
- return newsk;
- }
-
+ if (skb->protocol == htons(ETH_P_IP))
+ return tcp_v4_syn_recv_sock(sk, skb, req, dst,
+ req_unhash, own_req,
+ tcp_v6_mapped_child_init);
ireq = inet_rsk(req);
if (sk_acceptq_is_full(sk))
================================================================================
* CONTEXT DIFFERENCES - surrounding code differences between the patches *
================================================================================
--- b/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -39,7 +39,7 @@
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
bool *own_req);
u16 net_header_len;
- u16 net_frag_header_len;
- u16 sockaddr_len;
+ int (*setsockopt)(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen);
--- b/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -462,3 +462,3 @@
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
-int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
+int tcp_v4_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len);
int tcp_connect(struct sock *sk);
--- b/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -200,7 +200,7 @@
bool own_req;
child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst,
NULL, &own_req);
if (child) {
refcount_set(&req->rsk_refcnt, 1);
- tcp_sk(child)->tsoffset = tsoff;
+ sock_rps_save_rxhash(child, skb);
--- b/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1491,6 +1750,6 @@
}
sk_setup_caps(newsk, dst);
tcp_ca_openreq_child(newsk, dst);
- tcp_sync_mss(newsk, dst_mtu(dst));
+ tcp_sync_mss(newsk, dst4_mtu(dst));
--- b/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1127,2 +1320,3 @@
{
+ const struct ipv6_pinfo *np = tcp_inet6_sk(sk);
struct inet_request_sock *ireq;
@@ -1150,11 +1343,11 @@
if (!newsk)
return NULL;
- newtcp6sk = (struct tcp6_sock *)newsk;
- inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
-
newinet = inet_sk(newsk);
- newnp = inet6_sk(newsk);
+ newinet->pinet6 = tcp_inet6_sk(newsk);
+ newinet->ipv6_fl_list = NULL;
+
+ newnp = tcp_inet6_sk(newsk);
newtp = tcp_sk(newsk);
memcpy(newnp, np, sizeof(struct ipv6_pinfo));
@@ -1165,19 +1358,18 @@
if (sk_is_mptcp(newsk))
mptcpv6_handle_mapped(newsk, true);
newsk->sk_backlog_rcv = tcp_v4_do_rcv;
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
#endif
newnp->ipv6_mc_list = NULL;
newnp->ipv6_ac_list = NULL;
- newnp->ipv6_fl_list = NULL;
newnp->pktoptions = NULL;
newnp->opt = NULL;
newnp->mcast_oif = inet_iif(skb);
newnp->mcast_hops = ip_hdr(skb)->ttl;
newnp->rcv_flowinfo = 0;
- if (np->repflow)
+ if (inet6_test_bit(REPFLOW, sk))
newnp->flow_label = 0;
/*
================================================================================
* ONLY IN PATCH2 - files not modified by patch1 *
================================================================================
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -124,7 +124,9 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req)
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk))
{
struct smc_sock *smc;
struct sock *child;
@@ -142,7 +144,7 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
/* passthrough to original syn recv sock fct */
child = smc->ori_af_ops->syn_recv_sock(sk, skb, req, dst, req_unhash,
- own_req);
+ own_req, opt_child_init);
/* child must not inherit smc or its ops */
if (child) {
rcu_assign_sk_user_data(child, NULL);
================================================================================
* DELTA DIFFERENCES - code changes that differ between the patches *
================================================================================
--- b/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -919,15 +919,8 @@
iov.iov_base = icresp;
iov.iov_len = sizeof(*icresp);
ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len);
- if (ret < 0) {
- spin_lock_bh(&queue->state_lock);
- if (queue->state == NVMET_TCP_Q_DISCONNECTING) {
- spin_unlock_bh(&queue->state_lock);
- return -ESHUTDOWN;
- }
- spin_unlock_bh(&queue->state_lock);
+ if (ret < 0)
return ret; /* queue removal will cleanup */
- }
spin_lock_bh(&queue->state_lock);
if (queue->state == NVMET_TCP_Q_DISCONNECTING) {
################################################################################
! REJECTED PATCH2 HUNKS - could not be compared; manual review needed !
################################################################################
--- b/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -921,6 +934,11 @@
iov.iov_len = sizeof(*icresp);
ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len);
if (ret < 0) {
+ spin_lock_bh(&queue->state_lock);
+ if (queue->state == NVMET_TCP_Q_DISCONNECTING) {
+ spin_unlock_bh(&queue->state_lock);
+ return -ESHUTDOWN;
+ }
queue->state = NVMET_TCP_Q_FAILED;
return ret; /* queue removal will cleanup */
}
@@ -922,6 +940,7 @@
ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len);
if (ret < 0) {
queue->state = NVMET_TCP_Q_FAILED;
+ spin_unlock_bh(&queue->state_lock);
return ret; /* queue removal will cleanup */
}
================================================================================
* CONTEXT DIFFERENCES - surrounding code differences between the patches *
================================================================================
--- b/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -904,7 +905,9 @@
iov.iov_len = sizeof(*icresp);
ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len);
- if (ret < 0)
+ if (ret < 0) {
+ queue->state = NVMET_TCP_Q_FAILED;
return ret; /* queue removal will cleanup */
+ }
queue->state = NVMET_TCP_Q_LIVE;
nvmet_prepare_receive_pdu(queue);
################################################################################
! REJECTED PATCH2 HUNKS - could not be compared; manual review needed !
################################################################################
--- b/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -2172,6 +2178,8 @@
#endif
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+void __init mptcp_subflow_v6_init(void)
+{
/* In struct mptcp_subflow_request_sock, we assume the TCP request sock
* structures for v4 and v6 have the same size. It should not changed in
* the future but better to make sure to be warned if it is no longer
@@ -2212,8 +2220,2 @@
#endif
-#endif
-
- mptcp_diag_subflow_init(&subflow_ulp_ops);
-
- if (tcp_register_ulp(&subflow_ulp_ops) != 0)
- panic("MPTCP: failed to register subflows to ULP\n");
}
================================================================================
* CONTEXT DIFFERENCES - surrounding code differences between the patches *
================================================================================
--- b/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -3598,3 +3598,3 @@
mptcp_v6_prot = mptcp_prot;
- strcpy(mptcp_v6_prot.name, "MPTCPv6");
+ strscpy(mptcp_v6_prot.name, "MPTCPv6", sizeof(mptcp_v6_prot.name));
mptcp_v6_prot.slab = NULL;
--- b/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -593,6 +593,6 @@
void __init mptcp_proto_init(void);
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
int __init mptcp_proto_v6_init(void);
#endif
-struct sock *mptcp_sk_clone(const struct sock *sk,
+struct sock *mptcp_sk_clone_init(const struct sock *sk,
--- b/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -1730,5 +1732,5 @@
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
- subflow_request_sock_ipv6_ops = tcp_request_sock_ipv6_ops;
- subflow_request_sock_ipv6_ops.route_req = subflow_v6_route_req;
-
+ /* In struct mptcp_subflow_request_sock, we assume the TCP request sock
+ * structures for v4 and v6 have the same size. It should not changed in
+ * the future but better to make sure to be warned if it is no longerThis is an automated interdiff check for backported commits. |
JIRA PR Check Results3 commit(s) with issues found: Commit
|
|
❌ Validation checks completed with issues View full results: https://github.com/ctrliq/kernel-src-tree/actions/runs/33412080664 |
| if (sk_is_mptcp(newsk)) | ||
| mptcpv6_handle_mapped(newsk, true); | ||
| newsk->sk_backlog_rcv = tcp_v4_do_rcv; | ||
| #if CONFIG_TCP_MD5SIG |
There was a problem hiding this comment.
Upstream has #if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO) so shouldn't we have #ifdef CONFIG_TCP_MD5SIG here?
There was a problem hiding this comment.
Yes, of course, it should be what was in tcp_v6_syn_recv_sock, so #ifdef CONFIG_TCP_MD5SIG, thanks for catching that. The #if got there by a mistake when reconciling my version with that from rocky8_10 . That backport was such a burden, would be better if I never looked at it
4e185dd to
d8df222
Compare
jira VULN-184569 cve CVE-2026-43198 commit-author Eric Dumazet <edumazet@google.com> commit 858d2a4 upstream-diff | 1. Omitted changes to the `smc_tcp_syn_recv_sock()' function as it's not present in LTS 8.6 codebase (introduced in 8270d9c ("net/smc: Limit backlog connections")). 2. The `tcp_v6_mapped_child_init()' function differs from the upstream to the extent that `tcp_v6_syn_recv_sock()' from which it was extracted differ, specifically its `skb->protocol == htons(ETH_P_IP)' branch. Code in tcp_v6_syn_recv_sock() after the call to tcp_v4_syn_recv_sock() is done too late. After tcp_v4_syn_recv_sock(), the child socket is already visible from TCP ehash table and other cpus might use it. Since newinet->pinet6 is still pointing to the listener ipv6_pinfo bad things can happen as syzbot found. Move the problematic code in tcp_v6_mapped_child_init() and call this new helper from tcp_v4_syn_recv_sock() before the ehash insertion. This allows the removal of one tcp_sync_mss(), since tcp_v4_syn_recv_sock() will call it with the correct context. Fixes: 1da177e ("Linux-2.6.12-rc2") Reported-by: syzbot+937b5bbb6a815b3e5d0b@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/69949275.050a0220.2eeac1.0145.GAE@google.com/ Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/20260217161205.2079883-1-edumazet@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> (cherry picked from commit d707e4a97b50ae768d18426355045159f0f2394e) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
jira VULN-187106 cve CVE-2026-46135 commit-author Chaitanya Kulkarni <kch@nvidia.com> commit 5293a88 upstream-diff In the upstream the ICResp send failure path contained `queue->state = NVMET_TCP_Q_FAILED' assignment before return and it was preserved by the fix (it was introduced in 675b453 ("nvmet-tcp: enable TLS handshake upcall"), conflict-merged in 90d624a, missing from LTS 8.6). Similarly the LTS 8.6 version did _not_ contain this assignment and that was preserved in the backport as well. Provided the CVE-2026-46135 bug condition didn't occur and `nvmet_tcp_handle_icreq()' didn't bail out early with `-ESHUTDOWN', not setting `queue->state' leaves it with the `NVMET_TCP_Q_CONNECTING' value which it had to contain for the `nvmet_tcp_handle_icreq()' to be called in the first place. Since it exits with a non-zero value the `nvmet_tcp_socket_error()' shuts down the socket, which triggers `nvmet_tcp_state_change()' callback carrying out the queue teardown in `nvmet_tcp_schedule_release_queue()'. The `queue->state != NVMET_TCP_Q_DISCONNECTING' condition is passed and the queue is released properly. nvmet_tcp_handle_icreq() updates queue->state after sending an Initialization Connection Response (ICResp), but it does so without serializing against target-side queue teardown. If an NVMe/TCP host sends an Initialization Connection Request (ICReq) and immediately closes the connection, target-side teardown may start in softirq context before io_work drains the already buffered ICReq. In that case, nvmet_tcp_schedule_release_queue() sets queue->state to NVMET_TCP_Q_DISCONNECTING and drops the queue reference under state_lock. If io_work later processes that ICReq, nvmet_tcp_handle_icreq() can still overwrite the state back to NVMET_TCP_Q_LIVE. That defeats the DISCONNECTING-state guard in nvmet_tcp_schedule_release_queue() and allows a later socket state change to re-enter teardown and issue a second kref_put() on an already released queue. The ICResp send failure path has the same problem. If teardown has already moved the queue to DISCONNECTING, a send error can still overwrite the state with NVMET_TCP_Q_FAILED, again reopening the window for a second teardown path to drop the queue reference. Fix this by serializing both post-send state transitions with state_lock and bailing out if teardown has already started. Use -ESHUTDOWN as an internal sentinel for that bail-out path rather than propagating it as a transport error like -ECONNRESET. Keep nvmet_tcp_socket_error() setting rcv_state to NVMET_TCP_RECV_ERR before honoring that sentinel so receive-side parsing stays quiesced until the existing release path completes. Fixes: c46a6465bac2 ("nvmet-tcp: add NVMe over TCP target driver") Cc: stable@vger.kernel.org Reported-by: Shivam Kumar <skumar47@syr.edu> Tested-by: Shivam Kumar <kumar.shivam43666@gmail.com> Signed-off-by: Chaitanya Kulkarni <kch@nvidia.com> Signed-off-by: Keith Busch <kbusch@kernel.org> (cherry picked from commit 7f39b4e844e63db6a6b4c4bba59347c21aa9f808) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
jira VULN-182698 cve CVE-2026-31669 commit-author Jiayuan Chen <jiayuan.chen@linux.dev> commit 9b55b25 upstream-diff Conflicts due to multiple updates to the `mptcp_subflow_init' function. Preserved the split of `mptcp_subflow_init' along the `IS_ENABLED(CONFIG_MPTCP_IPV6)' line. The ehash table lookups are lockless and rely on SLAB_TYPESAFE_BY_RCU to guarantee socket memory stability during RCU read-side critical sections. Both tcp_prot and tcpv6_prot have their slab caches created with this flag via proto_register(). However, MPTCP's mptcp_subflow_init() copies tcpv6_prot into tcpv6_prot_override during inet_init() (fs_initcall, level 5), before inet6_init() (module_init/device_initcall, level 6) has called proto_register(&tcpv6_prot). At that point, tcpv6_prot.slab is still NULL, so tcpv6_prot_override.slab remains NULL permanently. This causes MPTCP v6 subflow child sockets to be allocated via kmalloc (falling into kmalloc-4k) instead of the TCPv6 slab cache. The kmalloc-4k cache lacks SLAB_TYPESAFE_BY_RCU, so when these sockets are freed without SOCK_RCU_FREE (which is cleared for child sockets by design), the memory can be immediately reused. Concurrent ehash lookups under rcu_read_lock can then access freed memory, triggering a slab-use-after-free in __inet_lookup_established. Fix this by splitting the IPv6-specific initialization out of mptcp_subflow_init() into a new mptcp_subflow_v6_init(), called from mptcp_proto_v6_init() before protocol registration. This ensures tcpv6_prot_override.slab correctly inherits the SLAB_TYPESAFE_BY_RCU slab cache. Fixes: b19bc29 ("mptcp: implement delegated actions") Cc: stable@vger.kernel.org Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev> Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> Link: https://patch.msgid.link/20260406031512.189159-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski <kuba@kernel.org> (cherry picked from commit 6b644e703c07d879025067d7e2a6fac8db3b2ac1) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
d8df222 to
9f399b4
Compare
|
LTS 8.6 rebase |
|
🤖 Validation Checks In Progress Workflow run: https://github.com/ctrliq/kernel-src-tree/actions/runs/33438169952 |
🔍 Interdiff Analysis
================================================================================
* DELTA DIFFERENCES - code changes that differ between the patches *
================================================================================
--- b/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1123,14 +1123,12 @@
static void tcp_v6_mapped_child_init(struct sock *newsk, const struct sock *sk)
{
struct inet_sock *newinet = inet_sk(newsk);
- struct tcp6_sock *newtcp6sk;
- struct ipv6_pinfo *newnp = inet6_sk(newsk);
- const struct ipv6_pinfo *np = inet6_sk(sk);
+ struct ipv6_pinfo *newnp;
- newtcp6sk = (struct tcp6_sock *)newsk;
- inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
+ newinet->pinet6 = newnp = tcp_inet6_sk(newsk);
+ newinet->ipv6_fl_list = NULL;
- memcpy(newnp, np, sizeof(struct ipv6_pinfo));
+ memcpy(newnp, tcp_inet6_sk(sk), sizeof(struct ipv6_pinfo));
newnp->saddr = newsk->sk_v6_rcv_saddr;
@@ -1138,13 +1136,12 @@
if (sk_is_mptcp(newsk))
mptcpv6_handle_mapped(newsk, true);
newsk->sk_backlog_rcv = tcp_v4_do_rcv;
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
tcp_sk(newsk)->af_specific = &tcp_sock_ipv6_mapped_specific;
#endif
newnp->ipv6_mc_list = NULL;
newnp->ipv6_ac_list = NULL;
- newnp->ipv6_fl_list = NULL;
newnp->pktoptions = NULL;
newnp->opt = NULL;
@@ -1153,7 +1150,7 @@
newnp->mcast_hops = newinet->mc_ttl;
newnp->rcv_flowinfo = 0;
- if (np->repflow)
+ if (inet6_test_bit(REPFLOW, sk))
newnp->flow_label = 0;
}
@@ -1179,10 +1176,62 @@
#endif
struct flowi6 fl6;
- if (skb->protocol == htons(ETH_P_IP))
- return tcp_v4_syn_recv_sock(sk, skb, req, dst,
- req_unhash, own_req,
- tcp_v6_mapped_child_init);
+ if (skb->protocol == htons(ETH_P_IP)) {
+ /*
+ * v6 mapped
+ */
+
+ newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst,
+ req_unhash, own_req);
+
+ if (!newsk)
+ return NULL;
+
+ newtcp6sk = (struct tcp6_sock *)newsk;
+ inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
+
+ newinet = inet_sk(newsk);
+ newnp = inet6_sk(newsk);
+ newtp = tcp_sk(newsk);
+
+ memcpy(newnp, np, sizeof(struct ipv6_pinfo));
+
+ newnp->saddr = newsk->sk_v6_rcv_saddr;
+
+ inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
+ if (sk_is_mptcp(newsk))
+ mptcpv6_handle_mapped(newsk, true);
+ newsk->sk_backlog_rcv = tcp_v4_do_rcv;
+#ifdef CONFIG_TCP_MD5SIG
+ newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
+#endif
+
+ newnp->ipv6_mc_list = NULL;
+ newnp->ipv6_ac_list = NULL;
+ newnp->ipv6_fl_list = NULL;
+ newnp->pktoptions = NULL;
+ newnp->opt = NULL;
+ newnp->mcast_oif = inet_iif(skb);
+ newnp->mcast_hops = ip_hdr(skb)->ttl;
+ newnp->rcv_flowinfo = 0;
+ if (np->repflow)
+ newnp->flow_label = 0;
+
+ /*
+ * No need to charge this sock to the relevant IPv6 refcnt debug socks count
+ * here, tcp_create_openreq_child now does this for us, see the comment in
+ * that function for the gory details. -acme
+ */
+
+ /* It is tricky place. Until this moment IPv4 tcp
+ worked with IPv6 icsk.icsk_af_ops.
+ Sync it now.
+ */
+ tcp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
+
+ return newsk;
+ }
+
ireq = inet_rsk(req);
if (sk_acceptq_is_full(sk))
################################################################################
! REJECTED PATCH2 HUNKS - could not be compared; manual review needed !
################################################################################
--- b/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1369,61 +1406,10 @@
#endif
struct flowi6 fl6;
- if (skb->protocol == htons(ETH_P_IP)) {
- /*
- * v6 mapped
- */
-
- newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst,
- req_unhash, own_req);
-
- if (!newsk)
- return NULL;
-
- newinet = inet_sk(newsk);
- newinet->pinet6 = tcp_inet6_sk(newsk);
- newinet->ipv6_fl_list = NULL;
-
- newnp = tcp_inet6_sk(newsk);
- newtp = tcp_sk(newsk);
-
- memcpy(newnp, np, sizeof(struct ipv6_pinfo));
-
- newnp->saddr = newsk->sk_v6_rcv_saddr;
-
- inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
- if (sk_is_mptcp(newsk))
- mptcpv6_handle_mapped(newsk, true);
- newsk->sk_backlog_rcv = tcp_v4_do_rcv;
-#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
- newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
-#endif
-
- newnp->ipv6_mc_list = NULL;
- newnp->ipv6_ac_list = NULL;
- newnp->pktoptions = NULL;
- newnp->opt = NULL;
- newnp->mcast_oif = inet_iif(skb);
- newnp->mcast_hops = ip_hdr(skb)->ttl;
- newnp->rcv_flowinfo = 0;
- if (inet6_test_bit(REPFLOW, sk))
- newnp->flow_label = 0;
-
- /*
- * No need to charge this sock to the relevant IPv6 refcnt debug socks count
- * here, tcp_create_openreq_child now does this for us, see the comment in
- * that function for the gory details. -acme
- */
-
- /* It is tricky place. Until this moment IPv4 tcp
- worked with IPv6 icsk.icsk_af_ops.
- Sync it now.
- */
- tcp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
-
- return newsk;
- }
-
+ if (skb->protocol == htons(ETH_P_IP))
+ return tcp_v4_syn_recv_sock(sk, skb, req, dst,
+ req_unhash, own_req,
+ tcp_v6_mapped_child_init);
ireq = inet_rsk(req);
if (sk_acceptq_is_full(sk))
================================================================================
* CONTEXT DIFFERENCES - surrounding code differences between the patches *
================================================================================
--- b/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -39,7 +39,7 @@
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
bool *own_req);
u16 net_header_len;
- u16 net_frag_header_len;
- u16 sockaddr_len;
+ int (*setsockopt)(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen);
--- b/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -462,3 +462,3 @@
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
-int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
+int tcp_v4_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len);
int tcp_connect(struct sock *sk);
--- b/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -200,7 +200,7 @@
bool own_req;
child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst,
NULL, &own_req);
if (child) {
refcount_set(&req->rsk_refcnt, 1);
- tcp_sk(child)->tsoffset = tsoff;
+ sock_rps_save_rxhash(child, skb);
--- b/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1491,6 +1750,6 @@
}
sk_setup_caps(newsk, dst);
tcp_ca_openreq_child(newsk, dst);
- tcp_sync_mss(newsk, dst_mtu(dst));
+ tcp_sync_mss(newsk, dst4_mtu(dst));
--- b/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1127,2 +1320,3 @@
{
+ const struct ipv6_pinfo *np = tcp_inet6_sk(sk);
struct inet_request_sock *ireq;
@@ -1150,11 +1343,11 @@
if (!newsk)
return NULL;
- newtcp6sk = (struct tcp6_sock *)newsk;
- inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
-
newinet = inet_sk(newsk);
- newnp = inet6_sk(newsk);
+ newinet->pinet6 = tcp_inet6_sk(newsk);
+ newinet->ipv6_fl_list = NULL;
+
+ newnp = tcp_inet6_sk(newsk);
newtp = tcp_sk(newsk);
memcpy(newnp, np, sizeof(struct ipv6_pinfo));
@@ -1165,19 +1358,18 @@
if (sk_is_mptcp(newsk))
mptcpv6_handle_mapped(newsk, true);
newsk->sk_backlog_rcv = tcp_v4_do_rcv;
-#ifdef CONFIG_TCP_MD5SIG
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
#endif
newnp->ipv6_mc_list = NULL;
newnp->ipv6_ac_list = NULL;
- newnp->ipv6_fl_list = NULL;
newnp->pktoptions = NULL;
newnp->opt = NULL;
newnp->mcast_oif = inet_iif(skb);
newnp->mcast_hops = ip_hdr(skb)->ttl;
newnp->rcv_flowinfo = 0;
- if (np->repflow)
+ if (inet6_test_bit(REPFLOW, sk))
newnp->flow_label = 0;
/*
================================================================================
* ONLY IN PATCH2 - files not modified by patch1 *
================================================================================
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -124,7 +124,9 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
struct request_sock *req,
struct dst_entry *dst,
struct request_sock *req_unhash,
- bool *own_req)
+ bool *own_req,
+ void (*opt_child_init)(struct sock *newsk,
+ const struct sock *sk))
{
struct smc_sock *smc;
struct sock *child;
@@ -142,7 +144,7 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
/* passthrough to original syn recv sock fct */
child = smc->ori_af_ops->syn_recv_sock(sk, skb, req, dst, req_unhash,
- own_req);
+ own_req, opt_child_init);
/* child must not inherit smc or its ops */
if (child) {
rcu_assign_sk_user_data(child, NULL);
================================================================================
* DELTA DIFFERENCES - code changes that differ between the patches *
================================================================================
--- b/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -919,15 +919,8 @@
iov.iov_base = icresp;
iov.iov_len = sizeof(*icresp);
ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len);
- if (ret < 0) {
- spin_lock_bh(&queue->state_lock);
- if (queue->state == NVMET_TCP_Q_DISCONNECTING) {
- spin_unlock_bh(&queue->state_lock);
- return -ESHUTDOWN;
- }
- spin_unlock_bh(&queue->state_lock);
+ if (ret < 0)
return ret; /* queue removal will cleanup */
- }
spin_lock_bh(&queue->state_lock);
if (queue->state == NVMET_TCP_Q_DISCONNECTING) {
################################################################################
! REJECTED PATCH2 HUNKS - could not be compared; manual review needed !
################################################################################
--- b/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -921,6 +934,11 @@
iov.iov_len = sizeof(*icresp);
ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len);
if (ret < 0) {
+ spin_lock_bh(&queue->state_lock);
+ if (queue->state == NVMET_TCP_Q_DISCONNECTING) {
+ spin_unlock_bh(&queue->state_lock);
+ return -ESHUTDOWN;
+ }
queue->state = NVMET_TCP_Q_FAILED;
return ret; /* queue removal will cleanup */
}
@@ -922,6 +940,7 @@
ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len);
if (ret < 0) {
queue->state = NVMET_TCP_Q_FAILED;
+ spin_unlock_bh(&queue->state_lock);
return ret; /* queue removal will cleanup */
}
================================================================================
* CONTEXT DIFFERENCES - surrounding code differences between the patches *
================================================================================
--- b/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -904,7 +905,9 @@
iov.iov_len = sizeof(*icresp);
ret = kernel_sendmsg(queue->sock, &msg, &iov, 1, iov.iov_len);
- if (ret < 0)
+ if (ret < 0) {
+ queue->state = NVMET_TCP_Q_FAILED;
return ret; /* queue removal will cleanup */
+ }
queue->state = NVMET_TCP_Q_LIVE;
nvmet_prepare_receive_pdu(queue);
################################################################################
! REJECTED PATCH2 HUNKS - could not be compared; manual review needed !
################################################################################
--- b/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -2172,6 +2178,8 @@
#endif
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+void __init mptcp_subflow_v6_init(void)
+{
/* In struct mptcp_subflow_request_sock, we assume the TCP request sock
* structures for v4 and v6 have the same size. It should not changed in
* the future but better to make sure to be warned if it is no longer
@@ -2212,8 +2220,2 @@
#endif
-#endif
-
- mptcp_diag_subflow_init(&subflow_ulp_ops);
-
- if (tcp_register_ulp(&subflow_ulp_ops) != 0)
- panic("MPTCP: failed to register subflows to ULP\n");
}
================================================================================
* CONTEXT DIFFERENCES - surrounding code differences between the patches *
================================================================================
--- b/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -3598,3 +3598,3 @@
mptcp_v6_prot = mptcp_prot;
- strcpy(mptcp_v6_prot.name, "MPTCPv6");
+ strscpy(mptcp_v6_prot.name, "MPTCPv6", sizeof(mptcp_v6_prot.name));
mptcp_v6_prot.slab = NULL;
--- b/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -593,6 +593,6 @@
void __init mptcp_proto_init(void);
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
int __init mptcp_proto_v6_init(void);
#endif
-struct sock *mptcp_sk_clone(const struct sock *sk,
+struct sock *mptcp_sk_clone_init(const struct sock *sk,
--- b/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -1730,5 +1732,5 @@
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
- subflow_request_sock_ipv6_ops = tcp_request_sock_ipv6_ops;
- subflow_request_sock_ipv6_ops.route_req = subflow_v6_route_req;
-
+ /* In struct mptcp_subflow_request_sock, we assume the TCP request sock
+ * structures for v4 and v6 have the same size. It should not changed in
+ * the future but better to make sure to be warned if it is no longerThis is an automated interdiff check for backported commits. |
JIRA PR Check Results3 commit(s) with issues found: Commit
|
|
❌ Validation checks completed with issues View full results: https://github.com/ctrliq/kernel-src-tree/actions/runs/33438169952 |
| { | ||
| struct inet_sock *newinet = inet_sk(newsk); | ||
| struct tcp6_sock *newtcp6sk; | ||
| struct ipv6_pinfo *newnp = inet6_sk(newsk); |
There was a problem hiding this comment.
This is taken too early.
The original code its taken after the new pinet6 is assigned
newtcp6sk = (struct tcp6_sock *)newsk;
inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
newinet = inet_sk(newsk);
newnp = inet6_sk(newsk);
This should probably just be
struct ipv6_pinfo *newnp;
<then>
newtcp6sk = (struct tcp6_sock *)newsk;
inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
newnp = inet6_sk(newsk);
Based on how this works it seems like we'd be retaining and modifying the original pinet6.
https://github.com/ctrliq/kernel-src-tree/blob/ciqlts8_6/include/linux/ipv6.h#L342-L345
Otherwise the reset of the PR description for this section I agree with what is it, just the ordering.
[LTS 8.6]
Commits
CVE-2026-31669
CVE-2026-46135
The problem with the backport of the upstream fix is that, in the upstream, the modification in the send-failure code branch assumes that
queue->state = NVMET_TCP_Q_FAILEDis in placekernel-src-tree/drivers/nvme/target/tcp.c
Line 911 in bad44c9
while the LTS 8.6 version does not contain such state setting. Moreover, this exact state isn't even defined in that version
kernel-src-tree/drivers/nvme/target/tcp.c
Lines 98 to 102 in ea7073f
While the immediate intuition may be to leave this branch without
queue->statemodification even in the presence of introduced-ESHUTDOWNbail-out, the solutions in other versions with similar situation are contradictory. Stablelinux-5.15.ybackport 9c63cf80895a70eb4fcfcaa725bb1ac9ae76f02b, for example, doesn't set thequeue->state, whilerocky8_10backport (embedded in 4ecf105) does, although to a different state from the upstream:kernel-src-tree/drivers/nvme/target/tcp.c
Line 912 in f92f5b2
Given those contradictory precedents and the subtle nature of the bug involving race condition between protocol state transitions and queue teardowns a deeper analysis was warranted.
The upstream bug scenario
Two communication sides: host and target. The bug occurs on the target side.
Host initiates connection, a socket is opened, and sends ICReq to target. The ICReq reaches target, where it will be handled by
nvmet_tcp_handle_icreq(), in a process thread. Immediately after sending ICReq the host closes the socket. This triggers a software interrupt on the target side andnvmet_tcp_state_change()is calledkernel-src-tree/drivers/nvme/target/tcp.c
Lines 1643 to 1668 in bad44c9
which falls to
nvmet_tcp_schedule_release_queue():kernel-src-tree/drivers/nvme/target/tcp.c
Lines 1376 to 1388 in bad44c9
The code branches off to
queuerelease (upon receiving ICReq thequeue->stateisNVMET_TCP_Q_CONNECTING):kernel-src-tree/drivers/nvme/target/tcp.c
Lines 1384 to 1385 in bad44c9
The
queue->stateis set toNVMET_TCP_Q_DISCONNECTINGand the queue is released withkref_put().All of this may happen before the ICReq is dequeued and fully handled. This work is done in the process context, in the
io_workkernel-src-tree/drivers/nvme/target/tcp.c
Line 1895 in bad44c9
function
nvmet_tcp_io_workkernel-src-tree/drivers/nvme/target/tcp.c
Line 1407 in bad44c9
The chain of calls leading to
nvmet_tcp_handle_icreq()handling the ICReq is following:kernel-src-tree/drivers/nvme/target/tcp.c
Line 1417 in bad44c9
kernel-src-tree/drivers/nvme/target/tcp.c
Line 1354 in bad44c9
kernel-src-tree/drivers/nvme/target/tcp.c
Line 1322 in bad44c9
kernel-src-tree/drivers/nvme/target/tcp.c
Line 1217 in bad44c9
kernel-src-tree/drivers/nvme/target/tcp.c
Line 1030 in bad44c9
Upon calling
nvmet_tcp_handle_icreq()thequeue->stateisNVMET_TCP_Q_CONNECTINGkernel-src-tree/drivers/nvme/target/tcp.c
Line 1024 in bad44c9
The ICResp is constructed
kernel-src-tree/drivers/nvme/target/tcp.c
Lines 894 to 908 in bad44c9
and sent back to the host
kernel-src-tree/drivers/nvme/target/tcp.c
Line 909 in bad44c9
Then, after
nvmet_tcp_schedule_release_queue()done its work in softirq in reaction to the closed socket and setqueue->state = NVMET_TCP_Q_DISCONNECTING, thenvmet_tcp_handle_icreq()function setsqueue->statetoNVMET_TCP_Q_LIVE, reopening the connection again,kernel-src-tree/drivers/nvme/target/tcp.c
Line 915 in bad44c9
or to
NVMET_TCP_Q_FAILEDkernel-src-tree/drivers/nvme/target/tcp.c
Line 911 in bad44c9
Now, a change of the socket state can trigger
nvmet_tcp_schedule_release_queue()again, and becausequeue->stateis no longerNVMET_TCP_Q_DISCONNECTINGthe queue teardown code path is hit again, on an already releasedqueuekernel-src-tree/drivers/nvme/target/tcp.c
Lines 1384 to 1385 in bad44c9
The upstream fix
Right before the queue state change in
nvmet_tcp_handle_icreq(), whether it's toNVMET_TCP_Q_FAILEDorNVMET_TCP_Q_LIVE, a condition described above is detected by checking ifqueue->statedidn't becomeNVMET_TCP_Q_DISCONNECTINGsince the last check atkernel-src-tree/drivers/nvme/target/tcp.c
Line 1024 in bad44c9
If that's the case the request handling is interrupted with
-ESHUTDOWN. If not, the code proceeds to alter thequeue->stateand continues protocol processing. In either case the handling ofqueue->stateis guarded byqueue->state_lockspinlock to serialize it with the possible concurrent access in the softirqnvmet_tcp_schedule_release_queue()callback.If the request handling is interrupted the
-ESHUTDOWNreturn code is eventually passed tonvmet_tcp_socket_error()asstatusargument, atkernel-src-tree/drivers/nvme/target/tcp.c
Line 1356 in bad44c9
The setting
queue->rcv_state = NVMET_TCP_RECV_ERRremains unchanged, including now also the newly introduced casestatus == -ESHUTDOWN. This prevents attempts of processing the ICReq again. From the commit's message:The "staying quiescent" refers to this fragment
kernel-src-tree/drivers/nvme/target/tcp.c
Lines 1318 to 1319 in bad44c9
Once
queue->state_recvis set toNVMET_TCP_RECV_ERRthe processing of any messages from the host is effectively suppressed.Verdict for LTS 8.6
For the LTS 8.6 version it is correct for the ICResp send failure path in
nvmet_tcp_handle_icreq()to remain without setting thequeue->state.Because of
kernel-src-tree/drivers/nvme/target/tcp.c
Line 977 in ea7073f
not setting
queue->stateto anything in the ICResp send failure path equals to setting it toNVMET_TCP_Q_CONNECTING. From the fixing commit's message:The "reopening of the window" refers to the previously mentioned "defeat of the DISCONNECTING-state guard":
kernel-src-tree/drivers/nvme/target/tcp.c
Line 1261 in ea7073f
In the upstream both
NVMET_TCP_Q_LIVEandNVMET_TCP_Q_FAILEDstates would pass the test and proceed to the second teardown (if not for the patch). This situation doesn't differ from LTS 8.6'sNVMET_TCP_Q_LIVEandNVMET_TCP_Q_CONNECTING, both also different fromNVMET_TCP_Q_DISCONNECTING, upholding the patch's logic.In particular the
queue->stateshould not be set toNVMET_TCP_Q_DISCONNECTINGin the ICResp send failure path, as it's done inrocky8_10, because it's not accompanied by the queue teardown, which happens only in thenvmet_tcp_schedule_release_queue()function, and when it's eventually called from thenvmet_tcp_state_change()callback as the result of closing the socket (kernel_sock_shutdown(...)) in thenvmet_tcp_socket_error()status-handling function, the teardown will be skipped, resulting in leaks.CVE-2026-43198
In the upstream the
tcp_v6_mapped_child_init()function was created fromtcp_v6_syn_recv_sock()- the code was extracted to be executed at different point in the sequence. The LTS 8.6 backport maintains this logic. Since the upstream version oftcp_v6_syn_recv_sock()differs from what can be found in LTS 8.6 thetcp_v6_mapped_child_init()also differs in the same way.What may be confusing is that the Rocky 8.10 backport of that same fix (theoretically 0010a14, technically embedded in the buildable 0010a14) contains
tcp_v6_mapped_child_init()which is different from what is proposed in this PR, despitetcp_v6_syn_recv_sock()from before the fix being the same in both versions (at least up to the modifiedskb->protocol == htons(ETH_P_IP)branch).This discrepancy suggested the following scenarios (at least):
To eliminate the possibility of (1) or (4) the solution differences were analyzed.
Extracted fragment, identical in both versions:
kernel-src-tree/net/ipv6/tcp_ipv6.c
Lines 1127 to 1181 in ea7073f
Function
tcp_v6_mapped_child_init()in Rocky 8.10 after fix:kernel-src-tree/net/ipv6/tcp_ipv6.c
Lines 1097 to 1131 in 0010a14
Function
tcp_v6_mapped_child_init()in the proposed solution for CVE-2026-43198 fix on LTS 8.6:kernel-src-tree/net/ipv6/tcp_ipv6.c
Lines 1123 to 1158 in da0c06b
After the eliminating the syntactically equivalent differences what remains is the value of
newnp. Rocky 8.10:LTS 8.6:
The LTS 8.6 version reflects directly what was in
tcp_v6_syn_recv_sock()before the fix, the Rocky 8.10 version does not. These values are not strictly equivalent unless some additional conditions hold true.The socket structs can be arranged in "type inheritance" hierarchy, where the "base class" is the first field:
sockinet_sockipv6_pinfo pinet6inet_connection_socktcp_socktcp6_sockipv6_pinfo inet6With
newskbeing of typetcp6_sock(at least), the "newtcp6sk" access method recovers thetcp6_sock::inet6field. whileinet6_sk(newsk)returnsinet_sock::pinet6. Seekernel-src-tree/include/linux/ipv6.h
Lines 342 to 345 in ea7073f
These fields can nevertheless be, for all intents and purposes of
tcp_v6_syn_recv_sock()function, the same. This is suggested by the commit 93a77c1, not backported to neither Rocky 8.10 nor LTS 8.6. It introduces functiontcp_inet6_sk()abstracting away the "newtcp6sk"inet6access method, but it also converts theinet6_sk(X)instances totcp_inet6_sk(X)(see 93a77c1#diff-8b341e52e57c996bc4f294087ab526ac0b1c3c47e045557628cc24277cbfda0dL1088-L1098).This suggests option (4): both solutions are correct, but Rocky 8.10 incorporates also the change from commit 93a77c1. For the LTS 8.6 solution a more straightforward implementation of
tcp_v6_mapped_child_init()was chosen, reflecting the code extracted fromtcp_v6_syn_recv_sock()directly.kABI check: passed
Boot test: passed
boot-test.log
Kselftests: passed relative
Reference
kselftests–ciqlts8_6–run1.log
kselftests–ciqlts8_6–run2.log
Patch
kselftests–ciqlts8_6-CVE-batch-44–run1.log
kselftests–ciqlts8_6-CVE-batch-44–run2.log
Comparison
The tests results for the reference and the patch are the same.
full-test-results-comparison.log