Netdev List
 help / color / mirror / Atom feed
From: Julian Anastasov <ja@ssi.bg>
To: Jakub Kicinski <kuba@kernel.org>
Cc: pablo@netfilter.org, netfilter-devel@vger.kernel.org,
	davem@davemloft.net, netdev@vger.kernel.org, pabeni@redhat.com,
	edumazet@google.com, horms@kernel.org, fw@strlen.de
Subject: Re: [PATCH net 4/7] ipvs: revalidate ihl before icmp_send
Date: Fri, 11 Sep 2026 12:56:31 +0300 (EEST)	[thread overview]
Message-ID: <117ea9ac-0af3-9801-4ab4-78999f94849a@ssi.bg> (raw)
In-Reply-To: <20260911004648.2877894-1-kuba@kernel.org>


	Hello,

On Thu, 10 Sep 2026, Jakub Kicinski wrote:

> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> ipvs: revalidate ihl before icmp_send
> 
> This adds a check in ip_vs_in_icmp() that drops the packet when the
> embedded IP header's ihl no longer matches the length that was validated
> from the frag copy, sets the transport header before icmp_send(), and
> passes the tunnel protocol to ipv4_update_pmtu() instead of 0.
> 
> Link: https://sashiko.dev/#/patchset/20260806105211.34622-1-ja%40ssi.bg
> 
> A few questions below.

	I'll send v2 to address the problems, thanks!

> 
> > diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
> > index ba0957798bad0..c41fac3ed873e 100644
> > --- a/net/netfilter/ipvs/ip_vs_core.c
> > +++ b/net/netfilter/ipvs/ip_vs_core.c
> > @@ -1934,7 +1934,8 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
> >  			IP_VS_DBG(12, "ICMP for %s %pI4->%pI4: mtu=%u\n",
> >  				  outer_proto, &ip_hdr(skb)->saddr,
> >  				  &ip_hdr(skb)->daddr, mtu);
> > -			ipv4_update_pmtu(skb, ipvs->net, mtu, 0, 0);
> > +			ipv4_update_pmtu(skb, ipvs->net, mtu, 0,
> > +					 ip_hdr(skb)->protocol);
> 
> [Severity: Medium]
> 
> The new protocol argument becomes fl4.flowi4_proto inside
> ipv4_update_pmtu()->__build_flow_key(), and it is used both by
> __ip_route_output_key() and by the fib_lookup() in
> __ip_rt_update_pmtu().
> 
> The route IPVS actually transmits these tunnel packets on is looked up
> and cached by do_output_route4(), which leaves flowi4_proto at zero:
> 
> net/netfilter/ipvs/ip_vs_xmit.c:do_output_route4() {
> 	memset(&fl4, 0, sizeof(fl4));
> 	fl4.daddr = daddr;
> 	fl4.flowi4_flags = (rt_mode & IP_VS_RT_MODE_KNOWN_NH) ?
> 			   FLOWI_FLAG_KNOWN_NH : 0;
> 	...
> }
> 
> and fib4_rule_match() keys on that field:
> 
> net/ipv4/fib_rules.c:fib4_rule_match() {
> 	...
> 	if (rule->ip_proto && (rule->ip_proto != fl4->flowi4_proto))
> 		return 0;
> 	...
> }
> 
> With an FRA_IP_PROTO rule configured, can the two lookups now land in
> different tables, so the exception ends up on a nexthop IPVS never
> consults?  The read-back immediately below would then still return the
> unreduced MTU:
> 
> 			if (dest) {
> 				struct ip_vs_dest_dst *dest_dst;
> 
> 				dest_dst = rcu_dereference(dest->dest_dst);
> 				if (dest_dst)
> 					mtu = dst_mtu(dest_dst->dst_cache);
> 			}
> 
> and the ICMP relayed to the client would advertise the stale larger
> value, leaving the passive PMTUD added by f2edb9f7706d ineffective for
> such setups.
> 
> The multipath variant of this does not seem to apply, since
> __ip_rt_update_pmtu() installs the exception on every path when
> fib_info_num_path(res.fi) > 1.
> 
> Other in-tree callers of ipv4_update_pmtu() pass the real protocol, but
> they also set flowi4_proto on their transmit lookup; IPVS does not.
> Would it make sense to set flowi4_proto in do_output_route4() as well,
> so the two keys agree?
> 
> [Severity: Medium]
> 
> Under the same assumption the commit message makes, that a local process
> can mutate the bytes between the frag read and the pull, is the protocol
> byte read here trusted?
> 
> Everything that authorized this tunnel was checked on the on-stack copy
> taken earlier in ip_vs_in_icmp():
> 
> 	cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
> 	if (!cih)
> 		return NF_ACCEPT;	/* The packet looks wrong, ignore */
> 	hlen_ipip = cih->ihl * 4;
> 	if (!(cih->version == 4 && hlen_ipip >= sizeof(struct iphdr)))
> 		return NF_ACCEPT;	/* The packet looks wrong, ignore */
> 	raddr = (union nf_inet_addr *)&cih->daddr;
> 	...
> 	if (cih->protocol == IPPROTO_IPIP) {
> 	...
> 	} else if ((cih->protocol == IPPROTO_UDP ||
> 		    cih->protocol == IPPROTO_GRE) &&
> 
> whereas ip_hdr(skb)->protocol here is re-read from the linear area after
> pskb_may_pull(skb, hlen_ipip).  If those bytes can change, the value fed
> to ipv4_update_pmtu(), and hence fl4.flowi4_proto for
> __ip_route_output_key() and update_or_create_fnhe(), is not necessarily
> the IPIP/UDP/GRE value that was validated, which works against the
> "provide correct protocol to ipv4_update_pmtu()" goal.
> 
> Would saving cih->protocol into a local at validation time and passing
> that local here be preferable to re-reading the header?
> 
> For what it is worth, the saddr/daddr/tos that __build_flow_key() reads
> from skb->data at this call site have the same exposure, but that part
> predates this patch.
> 
> >  			/* Client uses PMTUD? */
> >  			if (!(frag_off & htons(IP_DF)))
> >  				goto ignore_tunnel;
> > @@ -1960,6 +1961,12 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
> >  		/* Ensure the IP header is present in headroom */
> >  		if (!pskb_may_pull(skb, hlen_orig))
> >  			goto ignore_tunnel;
> > +		skb_set_transport_header(skb, hlen_orig);
> > +		/* Before now we may used ihl from skb frag, revalidate it after
> > +		 * copying it into skb head to prevent out-of-bounds access
> > +		 */
> > +		if (ip_hdr(skb)->ihl * 4 != hlen_orig)
> > +			goto ignore_tunnel;
> 
> [Severity: Medium]
> 
> Is the "out-of-bounds access" wording in this comment, and in the commit
> message, accurate for the current tree?
> 
> The commit message says:
> 
>     "we must be careful and revalidate the embedded headers after
>     reading them from the skb frags to prevent out-of-bounds
>     access"
> 
> and
> 
>     "local process can change the ihl field and after
>     pskb_may_pull() we can see larger value which is a problem
>     for the icmp_send() call."
> 
> Every ihl-derived access reachable from this icmp_send() call looks
> bounds-checked.  The tracepoint in include/trace/events/icmp.h reads the
> quoted header through skb_header_pointer() and copes with a NULL return:
> 
> 	if (iph->protocol == IPPROTO_UDP)
> 		uh = skb_header_pointer(skb,
> 					skb_network_offset(skb) +
> 					(iph->ihl << 2),
> 					sizeof(_uh), &_uh);
> 
> 	if (!uh) {
> 	...
> 
> __icmp_send() does the same for its inner probe:
> 
> net/ipv4/icmp.c:__icmp_send() {
> 	...
> 		itp = skb_header_pointer(skb_in,
> 					 skb_network_header(skb_in) +
> 					 (iph->ihl << 2) + ...
> 		if (!itp)
> 			goto out;
> 	...
> }
> 
> and __ip_options_echo() works on IPCB(skb)->opt, which is zeroed just
> above the pull:
> 
> 		memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
> 
> so it returns immediately on optlen == 0.  With that, does a mutated ihl
> today produce anything worse than wrong or zeroed tracepoint fields?
> 
> Related question on the Fixes tag:
> 
>     Fixes: 7fcc2fe39fed ("net: icmp: avoid invalid transport header access in icmp_send tracepoint")
> 
> That commit replaced the skb_transport_header() dereference in the
> tracepoint with the bounds-checked skb_header_pointer() read shown above,
> so it introduced the ihl-based offset but not an out-of-bounds access.
> Since there is no remaining skb_transport_header() consumer in the
> icmp_send() path, what does the added skb_set_transport_header(skb,
> hlen_orig) feed?  Could the tag and the comment be reworded to describe
> this as hardening plus a transport-header fixup, rather than a fix for an
> out-of-bounds read?
> 
> >  		IP_VS_DBG(12, "Sending ICMP for %pI4->%pI4: t=%u, c=%u, i=%u\n",
> >  			&ip_hdr(skb)->saddr, &ip_hdr(skb)->daddr,
> >  			type, code, ntohl(info));
> 

Regards

--
Julian Anastasov <ja@ssi.bg>


  reply	other threads:[~2026-09-11  9:56 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-09 22:18 [PATCH net 0/7] Netfilter/IPVS fixes for net Pablo Neira Ayuso
2026-09-09 22:18 ` [PATCH net 1/7] netfilter: nft_nat: fully initialise new_addr in netmap setup Pablo Neira Ayuso
2026-09-09 22:18 ` [PATCH net 2/7] netfilter: nf_tables: fix device name and prefix match in hook lookup Pablo Neira Ayuso
2026-09-09 22:18 ` [PATCH net 3/7] netfilter: nf_nat: unregister and release hooks on error Pablo Neira Ayuso
2026-09-11  0:46   ` Jakub Kicinski
2026-09-09 22:18 ` [PATCH net 4/7] ipvs: revalidate ihl before icmp_send Pablo Neira Ayuso
2026-09-11  0:46   ` Jakub Kicinski
2026-09-11  9:56     ` Julian Anastasov [this message]
2026-09-09 22:18 ` [PATCH net 5/7] netfilter: flowtable: hold reference on ct until flow is released Pablo Neira Ayuso
2026-09-09 22:18 ` [PATCH net 6/7] netfilter: xt_IDLETIMER: allocate timer with kzalloc() Pablo Neira Ayuso
2026-09-11  0:46   ` Jakub Kicinski
2026-09-09 22:18 ` [PATCH net 7/7] netfilter: hold reference on module during netlink dump Pablo Neira Ayuso
2026-09-11  0:46   ` Jakub Kicinski
2026-09-11  0:49 ` [PATCH net 0/7] Netfilter/IPVS fixes for net Jakub Kicinski

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=117ea9ac-0af3-9801-4ab4-78999f94849a@ssi.bg \
    --to=ja@ssi.bg \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=fw@strlen.de \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=pablo@netfilter.org \
    /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