[PATCH net] ipv4: icmp: reject non-unicast input routes in icmp_route_lookup

Dong Chenchen <[email protected]>
Newsgroups org.kernel.vger.netdev
Message-ID <[email protected]>
When the forward output route cannot be used in icmp_route_lookup(),
it enters the "reverse path" and calls ip_route_input() on fl4_dec.daddr,
the original packet's source address.

ip_route_input() only returns an error for truly invalid packets. For
local, broadcast, multicast and unreachable addresses it still succeeds
and returns an input route whose dst.output is set to ip_rt_bug().  The
existing check only rejects RTN_LOCAL routes, so the other route types
can still be returned and later used for output, syzkaller triggering a
WARN_ON_ONCE() in ip_rt_bug() as bellow:

 ------------[ cut here ]------------
 WARNING: net/ipv4/route.c:1273 at ip_rt_bug+0x14/0x20
 RIP: 0010:ip_rt_bug+0x14/0x20
 Call Trace:
  ip_push_pending_frames+0xfa/0x100
  __icmp_send+0x905/0xf10
  ip_options_compile+0xc0/0xd0
  ip_rcv_finish_core+0x321/0xae0
  ip_rcv+0x1de/0x260
  __netif_receive_skb_one_core+0x11a/0x130
  netif_receive_skb+0x7b/0x260
  tun_get_user+0x11bf/0x1c10
 ------------[ cut here ]------------

If the original source address is unroutable, ip_route_input() returns a
RTN_UNREACHABLE input route with err == 0, and the ICMP reply is then sent
through ip_rt_bug().

Only RTN_UNICAST input routes have dst.output set to ip_output() and are
safe to use for output.  Reject any input route that is not RTN_UNICAST.

Fixes: 8b7817f3a959 ("[IPSEC]: Add ICMP host relookup support")
Signed-off-by: Dong Chenchen <[email protected]>
---
 net/ipv4/icmp.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 0caedfc7ca92..d58ac2f23f9f 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -584,12 +584,14 @@ static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,
 		 * At this point, fl4_dec.daddr should NOT be local (we
 		 * checked fl4_dec.saddr above). However, a race condition
 		 * may occur if the address is added to the interface
-		 * concurrently. In that case, ip_route_input() returns a
-		 * LOCAL route with dst.output=ip_rt_bug, which must not
-		 * be used for output.
+		 * concurrently, or if fl4_dec.daddr is otherwise unroutable.
+		 * In that case, ip_route_input() returns an input route
+		 * (RTN_LOCAL, RTN_BROADCAST, RTN_UNREACHABLE or
+		 * RTN_MULTICAST) with dst.output set to ip_rt_bug(), which
+		 * must not be used for output.
 		 */
-		if (!err && rt2 && rt2->rt_type == RTN_LOCAL) {
-			net_warn_ratelimited("detected local route for %pI4 during ICMP sending, src %pI4\n",
+		if (!err && rt2 && rt2->rt_type != RTN_UNICAST) {
+			net_warn_ratelimited("detected unusable input route for %pI4 during ICMP sending, src %pI4\n",
 					     &fl4_dec.daddr, &fl4_dec.saddr);
 			dst_release(&rt2->dst);
 			err = -EINVAL;
-- 
2.25.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.