Netdev List
 help / color / mirror / Atom feed
* [PATCH net v2] tcp: do not send a SYNACK to a broadcast or multicast address
@ 2026-10-08 11:49 Theodor Arsenij Larionov Trichkine
  2026-10-08 11:56 ` netdev-bot+sinfo
  2026-10-09  2:23 ` Jiayuan Chen
  0 siblings, 2 replies; 3+ messages in thread
From: Theodor Arsenij Larionov Trichkine @ 2026-10-08 11:49 UTC (permalink / raw)
  To: edumazet, ncardwell, kuniyu, davem, kuba, pabeni
  Cc: horms, netdev, linux-kernel, Theodor Arsenij Larionov Trichkine

tcp_v4_conn_request() drops a SYN sent to a broadcast or multicast
address. The SYNACK route has no such check. A SYN with a multicast
source address can reach a listener when it is looped back with its
dst attached: ip_rcv_finish_core() then skips ip_route_input_noref()
and its martian source check. A raw IP_HDRINCL socket sending to a
local address does this, as does re-injection by nft dup or the
iptables TEE target.

If the SYN destination is an address on a non-loopback device and the
source is a group joined there (such as 224.0.0.1), the SYNACK route
has RTCF_MULTICAST and RTCF_LOCAL set, and ip_build_and_send_pkt()
sends it through ip_mc_output(). skb->sk of a SYNACK is the request
socket, so sk_mc_loop() reads inet_flags past the end of it:

BUG: KASAN: slab-out-of-bounds in sk_mc_loop+0x111/0x170
Read of size 8 at addr ffff88800f2b7f10 by task repro-raw/470
Call Trace:
 <IRQ>
 sk_mc_loop+0x111/0x170
 ip_mc_output+0x355/0x930
 ip_build_and_send_pkt+0xb87/0xc50
 tcp_v4_send_synack+0x500/0x6f0
 tcp_conn_request+0x2135/0x2d90
 tcp_v4_conn_request+0xa5/0x210
 tcp_rcv_state_process+0x136e/0x6920
 tcp_v4_do_rcv+0x339/0xb10
 tcp_v4_rcv+0x34ab/0x3ab0
 ip_protocol_deliver_rcu+0x6e/0x3e0
 ip_local_deliver_finish+0x34d/0x510
 ip_local_deliver+0x1bc/0x310
 ip_rcv+0x390/0x410
 __netif_receive_skb_one_core+0x199/0x1e0
 process_backlog+0x239/0x680
 __napi_poll+0xb5/0x650
 net_rx_action+0x980/0xd60
 handle_softirqs+0x17f/0x590
 do_softirq+0x3f/0x60
 </IRQ>
 <TASK>
 __local_bh_enable_ip+0x66/0x80
 __dev_queue_xmit+0xa65/0x3520
 ip_finish_output2+0xb00/0x16f0
 ip_output+0x2ad/0x4a0
 raw_sendmsg+0x245b/0x28e0
 inet_sendmsg+0x121/0x150
 __sys_sendto+0x450/0x4e0
 do_syscall_64+0xf6/0x500
 </TASK>

Allocated by task 470:
 inet_reqsk_alloc+0x97/0x6f0
 tcp_conn_request+0x4c6/0x2d90
 tcp_v4_conn_request+0xa5/0x210

The buggy address belongs to the object at ffff88800f2b7d60
 which belongs to the cache request_sock_TCP of size 312
The buggy address is located 120 bytes to the right of
 allocated 312-byte region [ffff88800f2b7d60, ffff88800f2b7e98)

Apply the same check to the SYNACK route in inet_csk_route_req().

Fixes: ca6fb0651883 ("tcp: attach SYNACK messages to request sockets instead of listener")
Suggested-by: Eric Dumazet <edumazet@kernel.org>
Signed-off-by: Theodor Arsenij Larionov Trichkine <theodorlarionov@gmail.com>
---
v2:
- Reject broadcast/multicast SYNACK routes in inet_csk_route_req() (Eric Dumazet).
- Describe how the SYN reaches the listener; add KASAN splat and repro.
v1: https://lore.kernel.org/netdev/20261008100423.1256884-1-theodorlarionov@gmail.com/

Reproducer (unprivileged, user + network namespace):

// gcc -O2 -static -o repro repro.c && ./repro
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define LOCAL_ADDR "10.0.0.1"	/* address of dummy0 */
#define MCAST_SRC  "224.0.0.1"	/* all-hosts, joined on every interface */
#define PORT       20000

static void die(const char *m) { perror(m); exit(1); }

static void wr(const char *path, const char *buf)
{
	int fd = open(path, O_WRONLY);

	if (fd < 0 || write(fd, buf, strlen(buf)) < 0)
		die(path);
	close(fd);
}

static void run(const char *cmd)
{
	if (system(cmd))
		fprintf(stderr, "failed: %s\n", cmd);
}

static uint16_t csum(const uint8_t *p, int len)
{
	uint32_t s = 0;
	int i;

	for (i = 0; i + 1 < len; i += 2)
		s += (p[i] << 8) | p[i + 1];
	if (i < len)
		s += p[i] << 8;
	while (s >> 16)
		s = (s & 0xffff) + (s >> 16);
	return ~s;
}

int main(void)
{
	char map[64];
	int uid = getuid(), gid = getgid();

	if (unshare(CLONE_NEWUSER | CLONE_NEWNET))
		die("unshare");
	wr("/proc/self/setgroups", "deny");
	snprintf(map, sizeof(map), "0 %d 1", uid);
	wr("/proc/self/uid_map", map);
	snprintf(map, sizeof(map), "0 %d 1", gid);
	wr("/proc/self/gid_map", map);

	run("ip link set lo up");
	run("ip link add dummy0 type dummy");
	run("ip addr add " LOCAL_ADDR "/24 dev dummy0");
	run("ip link set dummy0 up");

	int l = socket(AF_INET, SOCK_STREAM, 0);
	struct sockaddr_in a = {
		.sin_family = AF_INET,
		.sin_port = htons(PORT),
	};
	if (l < 0 || bind(l, (struct sockaddr *)&a, sizeof(a)) || listen(l, 128))
		die("listen");

	int raw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
	if (raw < 0)
		die("raw socket");

	uint8_t pkt[40] = { 0 }, ph[32];
	uint32_t saddr = inet_addr(MCAST_SRC), daddr = inet_addr(LOCAL_ADDR);

	pkt[0] = 0x45;			/* IPv4, ihl 5 */
	pkt[3] = sizeof(pkt);		/* tot_len */
	pkt[8] = 64;			/* ttl */
	pkt[9] = IPPROTO_TCP;
	memcpy(pkt + 12, &saddr, 4);
	memcpy(pkt + 16, &daddr, 4);
	pkt[22] = PORT >> 8;		/* dport */
	pkt[23] = PORT & 0xff;
	pkt[32] = 5 << 4;		/* doff */
	pkt[33] = 0x02;			/* SYN */
	pkt[34] = 0x40;			/* window */

	for (int i = 0; i < 100; i++) {
		uint16_t c, sport = 10000 + i;
		struct sockaddr_in to = {
			.sin_family = AF_INET,
			.sin_addr.s_addr = daddr,
		};

		pkt[20] = sport >> 8;
		pkt[21] = sport & 0xff;
		pkt[36] = pkt[37] = 0;
		memcpy(ph, pkt + 12, 8);	/* pseudo header */
		ph[8] = 0;
		ph[9] = IPPROTO_TCP;
		ph[10] = 0;
		ph[11] = 20;
		memcpy(ph + 12, pkt + 20, 20);
		c = csum(ph, sizeof(ph));
		pkt[36] = c >> 8;
		pkt[37] = c & 0xff;
		if (sendto(raw, pkt, sizeof(pkt), 0, (struct sockaddr *)&to, sizeof(to)) < 0)
			die("sendto");
	}
	sleep(1);
	return 0;
}

 net/ipv4/inet_connection_sock.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 6a30f1138454..aa928015cd14 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -779,6 +779,9 @@ struct dst_entry *inet_csk_route_req(const struct sock *sk,
 		goto no_route;
 	if (opt && opt->opt.is_strictroute && rt->rt_uses_gateway)
 		goto route_err;
+	/* Never send a SYNACK to a broadcast or multicast destination. */
+	if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
+		goto route_err;
 	rcu_read_unlock();
 	return &rt->dst;
 

base-commit: 6d25ffca055a77787c21a36b66c253f76239411b
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH net v2] tcp: do not send a SYNACK to a broadcast or multicast address
  2026-10-08 11:49 [PATCH net v2] tcp: do not send a SYNACK to a broadcast or multicast address Theodor Arsenij Larionov Trichkine
@ 2026-10-08 11:56 ` netdev-bot+sinfo
  2026-10-09  2:23 ` Jiayuan Chen
  1 sibling, 0 replies; 3+ messages in thread
From: netdev-bot+sinfo @ 2026-10-08 11:56 UTC (permalink / raw)
  To: Theodor Arsenij Larionov Trichkine
  Cc: edumazet, ncardwell, kuniyu, davem, kuba, pabeni, horms, netdev,
	linux-kernel

Hi!

This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:

 - How the issue was discovered, e.g. hit in production, hit during
   development, syzbot report, manual code inspection, LLM or static
   analysis tool scan.

Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.

The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH net v2] tcp: do not send a SYNACK to a broadcast or multicast address
  2026-10-08 11:49 [PATCH net v2] tcp: do not send a SYNACK to a broadcast or multicast address Theodor Arsenij Larionov Trichkine
  2026-10-08 11:56 ` netdev-bot+sinfo
@ 2026-10-09  2:23 ` Jiayuan Chen
  1 sibling, 0 replies; 3+ messages in thread
From: Jiayuan Chen @ 2026-10-09  2:23 UTC (permalink / raw)
  To: Theodor Arsenij Larionov Trichkine, edumazet, ncardwell, kuniyu,
	davem, kuba, pabeni
  Cc: horms, netdev, linux-kernel


On 10/8/26 7:49 PM, Theodor Arsenij Larionov Trichkine wrote:
> tcp_v4_conn_request() drops a SYN sent to a broadcast or multicast
> address. The SYNACK route has no such check. A SYN with a multicast
> source address can reach a listener when it is looped back with its
> dst attached: ip_rcv_finish_core() then skips ip_route_input_noref()
> and its martian source check. A raw IP_HDRINCL socket sending to a
> local address does this, as does re-injection by nft dup or the
> iptables TEE target.
>
> If the SYN destination is an address on a non-loopback device and the
> source is a group joined there (such as 224.0.0.1), the SYNACK route
> has RTCF_MULTICAST and RTCF_LOCAL set, and ip_build_and_send_pkt()
> sends it through ip_mc_output(). skb->sk of a SYNACK is the request
> socket, so sk_mc_loop() reads inet_flags past the end of it:
>
> BUG: KASAN: slab-out-of-bounds in sk_mc_loop+0x111/0x170
> Read of size 8 at addr ffff88800f2b7f10 by task repro-raw/470
> Call Trace:
>   <IRQ>
>   sk_mc_loop+0x111/0x170
>   ip_mc_output+0x355/0x930
>   ip_build_and_send_pkt+0xb87/0xc50
>   tcp_v4_send_synack+0x500/0x6f0
>   tcp_conn_request+0x2135/0x2d90
>   tcp_v4_conn_request+0xa5/0x210
>   tcp_rcv_state_process+0x136e/0x6920
>   tcp_v4_do_rcv+0x339/0xb10
>   tcp_v4_rcv+0x34ab/0x3ab0
>   ip_protocol_deliver_rcu+0x6e/0x3e0
>   ip_local_deliver_finish+0x34d/0x510
>   ip_local_deliver+0x1bc/0x310
>   ip_rcv+0x390/0x410
>   __netif_receive_skb_one_core+0x199/0x1e0
>   process_backlog+0x239/0x680
>   __napi_poll+0xb5/0x650
>   net_rx_action+0x980/0xd60
>   handle_softirqs+0x17f/0x590
>   do_softirq+0x3f/0x60
>   </IRQ>
>   <TASK>
>   __local_bh_enable_ip+0x66/0x80
>   __dev_queue_xmit+0xa65/0x3520
>   ip_finish_output2+0xb00/0x16f0
>   ip_output+0x2ad/0x4a0
>   raw_sendmsg+0x245b/0x28e0
>   inet_sendmsg+0x121/0x150
>   __sys_sendto+0x450/0x4e0
>   do_syscall_64+0xf6/0x500
>   </TASK>
>
> Allocated by task 470:
>   inet_reqsk_alloc+0x97/0x6f0
>   tcp_conn_request+0x4c6/0x2d90
>   tcp_v4_conn_request+0xa5/0x210
>
> The buggy address belongs to the object at ffff88800f2b7d60
>   which belongs to the cache request_sock_TCP of size 312
> The buggy address is located 120 bytes to the right of
>   allocated 312-byte region [ffff88800f2b7d60, ffff88800f2b7e98)
>
> Apply the same check to the SYNACK route in inet_csk_route_req().
>
> Fixes: ca6fb0651883 ("tcp: attach SYNACK messages to request sockets instead of listener")
> Suggested-by: Eric Dumazet <edumazet@kernel.org>
> Signed-off-by: Theodor Arsenij Larionov Trichkine <theodorlarionov@gmail.com>


Reviewed-by: Jiayuan Chen <jiayuan.chen@linux.dev>


> ---
> v2:
> - Reject broadcast/multicast SYNACK routes in inet_csk_route_req() (Eric Dumazet).
> - Describe how the SYN reaches the listener; add KASAN splat and repro.
> v1: https://lore.kernel.org/netdev/20261008100423.1256884-1-theodorlarionov@gmail.com/


I think SYN with 192.x.x.255 as source can also tirgger this warning 
when rp_filter is disabled.


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-10-09  2:24 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-08 11:49 [PATCH net v2] tcp: do not send a SYNACK to a broadcast or multicast address Theodor Arsenij Larionov Trichkine
2026-10-08 11:56 ` netdev-bot+sinfo
2026-10-09  2:23 ` Jiayuan Chen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox