* [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