* [PATCH net v2] amt: don't read the IP source address from a reallocated skb header
@ 2026-06-17 12:34 Michael Bommarito
2026-06-21 22:00 ` Jakub Kicinski
2026-06-26 11:19 ` [PATCH net v3] amt: don't read the IP header from a reallocated skb head Michael Bommarito
0 siblings, 2 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-06-17 12:34 UTC (permalink / raw)
To: Taehee Yoo, David S . Miller, Jakub Kicinski, Paolo Abeni,
Eric Dumazet
Cc: Andrew Lunn, netdev, linux-kernel
amt_update_handler() caches iph = ip_hdr(skb) and then calls
pskb_may_pull(). pskb_may_pull() can reallocate the skb head: the new
head is allocated and the old one is freed. The cached iph is not
refreshed, so the following tunnel lookup reads iph->saddr from the
freed head. On an AMT relay this lookup runs for every incoming
membership update, before the update's nonce and response MAC are
validated.
The sibling handlers amt_multicast_data_handler() and
amt_membership_query_handler() re-read ip_hdr() after the pull and are
not affected; only amt_update_handler() keeps the pre-pull pointer.
Snapshot the source address before the pulls and match against the
snapshot.
The stale read was confirmed by instrumentation rather than a sanitizer:
after the head is reallocated the comparison reads from the freed old
head. KASAN does not flag it because the skb head is released through
the page-fragment free path, which is not poisoned on free.
Fixes: cbc21dc1cfe9 ("amt: add data plane of amt interface")
Acked-by: Taehee Yoo <ap420073@gmail.com>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
v2: per Taehee Yoo's review
(https://lore.kernel.org/all/CAMArcTWCg4x1bxrzr+XHc_FqbzJELCMu+tE=x8Jhewgr-_A3Rw@mail.gmail.com/):
- retag the subject as [PATCH net] (this is a bug fix);
- drop Cc: stable -- the Fixes tag is enough for the stable backport
process to pick it up;
- carry Taehee Yoo's Acked-by.
No code change from v1.
v1: https://lore.kernel.org/all/20260614155539.3106537-1-michael.bommarito@gmail.com/
Confirmed on x86_64 by instrumenting the comparison: with the update
packet built so the first pskb_may_pull() reallocates the head (it pulls
bytes out of a page fragment with no tailroom), the read runs against
the freed old head -- the head pointer moves and the old page's refcount
is 0. Neither generic KASAN nor arm64 HW-tag KASAN reports it: page-
fragment frees are not synchronously poisoned, and under MTE the freed
page keeps a tag matching the stale pointer, so this class of stale-
header read escapes the usual fuzzing oracles. On a live relay the freed
head is also exposed to reuse by later skb allocations.
amtdbg: cmp reads iph=...e000 (skb->head=...384380) stale_head=1 ref=0
A KUnit covering the re-read can follow separately.
drivers/net/amt.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index f2f3139..af6e28d 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -2455,8 +2455,10 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
struct ethhdr *eth;
struct iphdr *iph;
int len, hdr_size;
+ __be32 saddr;
iph = ip_hdr(skb);
+ saddr = iph->saddr;
hdr_size = sizeof(*amtmu) + sizeof(struct udphdr);
if (!pskb_may_pull(skb, hdr_size))
@@ -2472,7 +2474,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
skb_reset_network_header(skb);
list_for_each_entry_rcu(tunnel, &amt->tunnel_list, list) {
- if (tunnel->ip4 == iph->saddr) {
+ if (tunnel->ip4 == saddr) {
if ((amtmu->nonce == tunnel->nonce &&
amtmu->response_mac == tunnel->mac)) {
mod_delayed_work(amt_wq, &tunnel->gc_wq,
base-commit: 5200f5f493f79f14bbdc349e402a40dfb32f23c8
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH net v2] amt: don't read the IP source address from a reallocated skb header
2026-06-17 12:34 [PATCH net v2] amt: don't read the IP source address from a reallocated skb header Michael Bommarito
@ 2026-06-21 22:00 ` Jakub Kicinski
2026-06-22 8:58 ` Taehee Yoo
2026-06-26 11:19 ` [PATCH net v3] amt: don't read the IP header from a reallocated skb head Michael Bommarito
1 sibling, 1 reply; 5+ messages in thread
From: Jakub Kicinski @ 2026-06-21 22:00 UTC (permalink / raw)
To: Michael Bommarito
Cc: Taehee Yoo, David S . Miller, Paolo Abeni, Eric Dumazet,
Andrew Lunn, netdev, linux-kernel
On Wed, 17 Jun 2026 08:34:43 -0400 Michael Bommarito wrote:
> amt_update_handler() caches iph = ip_hdr(skb) and then calls
> pskb_may_pull(). pskb_may_pull() can reallocate the skb head: the new
> head is allocated and the old one is freed. The cached iph is not
> refreshed, so the following tunnel lookup reads iph->saddr from the
> freed head. On an AMT relay this lookup runs for every incoming
> membership update, before the update's nonce and response MAC are
> validated.
>
> The sibling handlers amt_multicast_data_handler() and
> amt_membership_query_handler() re-read ip_hdr() after the pull and are
> not affected; only amt_update_handler() keeps the pre-pull pointer.
Sashikos point out a bunch more of these in AMT:
https://sashiko.dev/#/patchset/20260617123443.3586930-1-michael.bommarito@gmail.com
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260617123443.3586930-1-michael.bommarito@gmail.com
Let's fix them all with one patch?
--
pw-bot: cr
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net v2] amt: don't read the IP source address from a reallocated skb header
2026-06-21 22:00 ` Jakub Kicinski
@ 2026-06-22 8:58 ` Taehee Yoo
2026-06-22 12:37 ` Michael Bommarito
0 siblings, 1 reply; 5+ messages in thread
From: Taehee Yoo @ 2026-06-22 8:58 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Michael Bommarito, David S . Miller, Paolo Abeni, Eric Dumazet,
Andrew Lunn, netdev, linux-kernel
On Mon, Jun 22, 2026 at 7:00 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Wed, 17 Jun 2026 08:34:43 -0400 Michael Bommarito wrote:
> > amt_update_handler() caches iph = ip_hdr(skb) and then calls
> > pskb_may_pull(). pskb_may_pull() can reallocate the skb head: the new
> > head is allocated and the old one is freed. The cached iph is not
> > refreshed, so the following tunnel lookup reads iph->saddr from the
> > freed head. On an AMT relay this lookup runs for every incoming
> > membership update, before the update's nonce and response MAC are
> > validated.
> >
> > The sibling handlers amt_multicast_data_handler() and
> > amt_membership_query_handler() re-read ip_hdr() after the pull and are
> > not affected; only amt_update_handler() keeps the pre-pull pointer.
>
> Sashikos point out a bunch more of these in AMT:
> https://sashiko.dev/#/patchset/20260617123443.3586930-1-michael.bommarito@gmail.com
> https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260617123443.3586930-1-michael.bommarito@gmail.com
>
> Let's fix them all with one patch?
Agreed.
Michael, could you please fix the remaining ones Sashiko flagged?
Thanks a lot!
Taehee Yoo
> --
> pw-bot: cr
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net v2] amt: don't read the IP source address from a reallocated skb header
2026-06-22 8:58 ` Taehee Yoo
@ 2026-06-22 12:37 ` Michael Bommarito
0 siblings, 0 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-06-22 12:37 UTC (permalink / raw)
To: Taehee Yoo
Cc: Jakub Kicinski, David S . Miller, Paolo Abeni, Eric Dumazet,
Andrew Lunn, netdev, linux-kernel
On Mon, Jun 22, 2026 at 4:58 AM Taehee Yoo <ap420073@gmail.com> wrote:
> > Let's fix them all with one patch?
>
> Agreed.
> Michael, could you please fix the remaining ones Sashiko flagged?
Sure, will do
Thanks,
Mike
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v3] amt: don't read the IP header from a reallocated skb head
2026-06-17 12:34 [PATCH net v2] amt: don't read the IP source address from a reallocated skb header Michael Bommarito
2026-06-21 22:00 ` Jakub Kicinski
@ 2026-06-26 11:19 ` Michael Bommarito
1 sibling, 0 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-06-26 11:19 UTC (permalink / raw)
To: Taehee Yoo, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel
Several AMT receive paths cache a pointer into the skb header
(ip_hdr() / ipv6_hdr()) and then call a helper that can reallocate
the skb head before reading the cached pointer:
amt_rcv() caches ip_hdr(skb), then amt_parse_type()
does pskb_may_pull() before reading
iph->saddr;
amt_update_handler() caches ip_hdr(skb), then pskb_may_pull() /
iptunnel_pull_header() before reading
iph->saddr;
amt_igmpv3_report_handler() caches ip_hdr(skb), then ip_mc_may_pull()
in the record loop before reading
iph->saddr;
amt_mldv2_report_handler() caches ipv6_hdr(skb), then
ipv6_mc_may_pull() in the record loop
before reading ip6h->saddr.
pskb_may_pull() and the *_mc_may_pull() helpers can reallocate the
skb head; when they do, the old head is freed and the cached pointer
dangles, so the later source-address read is a use-after-free of the
freed head.
The only field used after the pull in each case is the source
address, which is stable across the pull. Snapshot it before the
pull and use the snapshot.
The sibling handlers that re-derive ip_hdr() after the pull
(amt_multicast_data_handler(), amt_membership_query_handler()) and
the handlers that read the source address with no intervening pull
(amt_discovery_handler(), amt_request_handler(), the IGMPv2/MLDv1
report and leave handlers) are not affected.
Fixes: cbc21dc1cfe9 ("amt: add data plane of amt interface")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
v3: address Jakub Kicinski's and Taehee Yoo's review of v2: fix all of
the stale-iph reads Sashiko flagged in amt.c in one patch, not just
amt_update_handler(). v2 fixed only amt_update_handler(); this also
covers amt_rcv(), amt_igmpv3_report_handler() and
amt_mldv2_report_handler(), which have the same pre-pull cached
header read. The amt_update_handler() change is functionally the
same as v2 (snapshot the source address before the pull).
v2: https://lore.kernel.org/all/20260617123443.3586930-1-michael.bommarito@gmail.com/
Built for x86_64 (CONFIG_AMT=m) with W=1, no new warnings; checkpatch
--strict clean.
drivers/net/amt.c | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index 724a8163a5142..7094bdab0f463 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -2000,13 +2000,15 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb,
struct igmpv3_report *ihrv3 = igmpv3_report_hdr(skb);
int len = skb_transport_offset(skb) + sizeof(*ihrv3);
void *zero_grec = (void *)&igmpv3_zero_grec;
- struct iphdr *iph = ip_hdr(skb);
struct amt_group_node *gnode;
union amt_addr group, host;
struct igmpv3_grec *grec;
+ __be32 saddr;
u16 nsrcs;
int i;
+ saddr = ip_hdr(skb)->saddr;
+
for (i = 0; i < ntohs(ihrv3->ngrec); i++) {
len += sizeof(*grec);
if (!ip_mc_may_pull(skb, len))
@@ -2022,7 +2024,7 @@ static void amt_igmpv3_report_handler(struct amt_dev *amt, struct sk_buff *skb,
memset(&group, 0, sizeof(union amt_addr));
group.ip4 = grec->grec_mca;
memset(&host, 0, sizeof(union amt_addr));
- host.ip4 = iph->saddr;
+ host.ip4 = saddr;
gnode = amt_lookup_group(tunnel, &group, &host, false);
if (!gnode) {
gnode = amt_add_group(amt, tunnel, &group, &host,
@@ -2162,13 +2164,15 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb,
struct mld2_report *mld2r = (struct mld2_report *)icmp6_hdr(skb);
int len = skb_transport_offset(skb) + sizeof(*mld2r);
void *zero_grec = (void *)&mldv2_zero_grec;
- struct ipv6hdr *ip6h = ipv6_hdr(skb);
struct amt_group_node *gnode;
union amt_addr group, host;
struct mld2_grec *grec;
+ struct in6_addr saddr;
u16 nsrcs;
int i;
+ saddr = ipv6_hdr(skb)->saddr;
+
for (i = 0; i < ntohs(mld2r->mld2r_ngrec); i++) {
len += sizeof(*grec);
if (!ipv6_mc_may_pull(skb, len))
@@ -2184,7 +2188,7 @@ static void amt_mldv2_report_handler(struct amt_dev *amt, struct sk_buff *skb,
memset(&group, 0, sizeof(union amt_addr));
group.ip6 = grec->grec_mca;
memset(&host, 0, sizeof(union amt_addr));
- host.ip6 = ip6h->saddr;
+ host.ip6 = saddr;
gnode = amt_lookup_group(tunnel, &group, &host, true);
if (!gnode) {
gnode = amt_add_group(amt, tunnel, &group, &host,
@@ -2455,8 +2459,10 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
struct ethhdr *eth;
struct iphdr *iph;
int len, hdr_size;
+ __be32 saddr;
iph = ip_hdr(skb);
+ saddr = iph->saddr;
hdr_size = sizeof(*amtmu) + sizeof(struct udphdr);
if (!pskb_may_pull(skb, hdr_size))
@@ -2472,7 +2478,7 @@ static bool amt_update_handler(struct amt_dev *amt, struct sk_buff *skb)
skb_reset_network_header(skb);
list_for_each_entry_rcu(tunnel, &amt->tunnel_list, list) {
- if (tunnel->ip4 == iph->saddr) {
+ if (tunnel->ip4 == saddr) {
if ((amtmu->nonce == tunnel->nonce &&
amtmu->response_mac == tunnel->mac)) {
mod_delayed_work(amt_wq, &tunnel->gc_wq,
@@ -2772,7 +2778,7 @@ static void amt_gw_rcv(struct amt_dev *amt, struct sk_buff *skb)
static int amt_rcv(struct sock *sk, struct sk_buff *skb)
{
struct amt_dev *amt;
- struct iphdr *iph;
+ __be32 saddr;
int type;
bool err;
@@ -2785,7 +2791,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
}
skb->dev = amt->dev;
- iph = ip_hdr(skb);
+ saddr = ip_hdr(skb)->saddr;
type = amt_parse_type(skb);
if (type == -1) {
err = true;
@@ -2795,7 +2801,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
if (amt->mode == AMT_MODE_GATEWAY) {
switch (type) {
case AMT_MSG_ADVERTISEMENT:
- if (iph->saddr != amt->discovery_ip) {
+ if (saddr != amt->discovery_ip) {
netdev_dbg(amt->dev, "Invalid Relay IP\n");
err = true;
goto drop;
@@ -2807,7 +2813,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
}
goto out;
case AMT_MSG_MULTICAST_DATA:
- if (iph->saddr != amt->remote_ip) {
+ if (saddr != amt->remote_ip) {
netdev_dbg(amt->dev, "Invalid Relay IP\n");
err = true;
goto drop;
@@ -2818,7 +2824,7 @@ static int amt_rcv(struct sock *sk, struct sk_buff *skb)
else
goto out;
case AMT_MSG_MEMBERSHIP_QUERY:
- if (iph->saddr != amt->remote_ip) {
+ if (saddr != amt->remote_ip) {
netdev_dbg(amt->dev, "Invalid Relay IP\n");
err = true;
goto drop;
base-commit: ab9de95c9cf952332ab79453b4b5d1bfca8e514f
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-06-26 11:19 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-17 12:34 [PATCH net v2] amt: don't read the IP source address from a reallocated skb header Michael Bommarito
2026-06-21 22:00 ` Jakub Kicinski
2026-06-22 8:58 ` Taehee Yoo
2026-06-22 12:37 ` Michael Bommarito
2026-06-26 11:19 ` [PATCH net v3] amt: don't read the IP header from a reallocated skb head Michael Bommarito
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox