ipv4: icmp: convert to dev_net_rcu()

[ Upstream commit 4b8474a0951e605d2a27a2c483da4eb4b8c63760 ]

__icmp_send() must ensure rcu_read_lock() is held, as spotted
by Jakub.

Other ICMP uses of dev_net() seem safe, change them to dev_net_rcu()
to get LOCKDEP support.

Fixes: dde1bc0e6f ("[NETNS]: Add namespace for ICMP replying code.")
Closes: https://lore.kernel.org/netdev/20250203153633.46ce0337@kernel.org/
Reported-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20250205155120.1676781-9-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Eric Dumazet
2025-02-05 15:51:16 +00:00
committed by Greg Kroah-Hartman
parent 4583748b65
commit 3c925321a5
+17 -14
View File
@@ -399,10 +399,10 @@ static void icmp_push_reply(struct sock *sk,
static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
{ {
struct ipcm_cookie ipc;
struct rtable *rt = skb_rtable(skb); struct rtable *rt = skb_rtable(skb);
struct net *net = dev_net(rt->dst.dev); struct net *net = dev_net_rcu(rt->dst.dev);
bool apply_ratelimit = false; bool apply_ratelimit = false;
struct ipcm_cookie ipc;
struct flowi4 fl4; struct flowi4 fl4;
struct sock *sk; struct sock *sk;
struct inet_sock *inet; struct inet_sock *inet;
@@ -610,12 +610,14 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
struct sock *sk; struct sock *sk;
if (!rt) if (!rt)
goto out; return;
rcu_read_lock();
if (rt->dst.dev) if (rt->dst.dev)
net = dev_net(rt->dst.dev); net = dev_net_rcu(rt->dst.dev);
else if (skb_in->dev) else if (skb_in->dev)
net = dev_net(skb_in->dev); net = dev_net_rcu(skb_in->dev);
else else
goto out; goto out;
@@ -786,7 +788,8 @@ out_unlock:
icmp_xmit_unlock(sk); icmp_xmit_unlock(sk);
out_bh_enable: out_bh_enable:
local_bh_enable(); local_bh_enable();
out:; out:
rcu_read_unlock();
} }
EXPORT_SYMBOL(__icmp_send); EXPORT_SYMBOL(__icmp_send);
@@ -835,7 +838,7 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
* avoid additional coding at protocol handlers. * avoid additional coding at protocol handlers.
*/ */
if (!pskb_may_pull(skb, iph->ihl * 4 + 8)) { if (!pskb_may_pull(skb, iph->ihl * 4 + 8)) {
__ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS); __ICMP_INC_STATS(dev_net_rcu(skb->dev), ICMP_MIB_INERRORS);
return; return;
} }
@@ -869,7 +872,7 @@ static enum skb_drop_reason icmp_unreach(struct sk_buff *skb)
struct net *net; struct net *net;
u32 info = 0; u32 info = 0;
net = dev_net(skb_dst(skb)->dev); net = dev_net_rcu(skb_dst(skb)->dev);
/* /*
* Incomplete header ? * Incomplete header ?
@@ -980,7 +983,7 @@ out_err:
static enum skb_drop_reason icmp_redirect(struct sk_buff *skb) static enum skb_drop_reason icmp_redirect(struct sk_buff *skb)
{ {
if (skb->len < sizeof(struct iphdr)) { if (skb->len < sizeof(struct iphdr)) {
__ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS); __ICMP_INC_STATS(dev_net_rcu(skb->dev), ICMP_MIB_INERRORS);
return SKB_DROP_REASON_PKT_TOO_SMALL; return SKB_DROP_REASON_PKT_TOO_SMALL;
} }
@@ -1012,7 +1015,7 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb)
struct icmp_bxm icmp_param; struct icmp_bxm icmp_param;
struct net *net; struct net *net;
net = dev_net(skb_dst(skb)->dev); net = dev_net_rcu(skb_dst(skb)->dev);
/* should there be an ICMP stat for ignored echos? */ /* should there be an ICMP stat for ignored echos? */
if (READ_ONCE(net->ipv4.sysctl_icmp_echo_ignore_all)) if (READ_ONCE(net->ipv4.sysctl_icmp_echo_ignore_all))
return SKB_NOT_DROPPED_YET; return SKB_NOT_DROPPED_YET;
@@ -1041,9 +1044,9 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb)
bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr) bool icmp_build_probe(struct sk_buff *skb, struct icmphdr *icmphdr)
{ {
struct net *net = dev_net_rcu(skb->dev);
struct icmp_ext_hdr *ext_hdr, _ext_hdr; struct icmp_ext_hdr *ext_hdr, _ext_hdr;
struct icmp_ext_echo_iio *iio, _iio; struct icmp_ext_echo_iio *iio, _iio;
struct net *net = dev_net(skb->dev);
struct inet6_dev *in6_dev; struct inet6_dev *in6_dev;
struct in_device *in_dev; struct in_device *in_dev;
struct net_device *dev; struct net_device *dev;
@@ -1182,7 +1185,7 @@ static enum skb_drop_reason icmp_timestamp(struct sk_buff *skb)
return SKB_NOT_DROPPED_YET; return SKB_NOT_DROPPED_YET;
out_err: out_err:
__ICMP_INC_STATS(dev_net(skb_dst(skb)->dev), ICMP_MIB_INERRORS); __ICMP_INC_STATS(dev_net_rcu(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
return SKB_DROP_REASON_PKT_TOO_SMALL; return SKB_DROP_REASON_PKT_TOO_SMALL;
} }
@@ -1199,7 +1202,7 @@ int icmp_rcv(struct sk_buff *skb)
{ {
enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED; enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct rtable *rt = skb_rtable(skb); struct rtable *rt = skb_rtable(skb);
struct net *net = dev_net(rt->dst.dev); struct net *net = dev_net_rcu(rt->dst.dev);
struct icmphdr *icmph; struct icmphdr *icmph;
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
@@ -1372,9 +1375,9 @@ int icmp_err(struct sk_buff *skb, u32 info)
struct iphdr *iph = (struct iphdr *)skb->data; struct iphdr *iph = (struct iphdr *)skb->data;
int offset = iph->ihl<<2; int offset = iph->ihl<<2;
struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset); struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset);
struct net *net = dev_net_rcu(skb->dev);
int type = icmp_hdr(skb)->type; int type = icmp_hdr(skb)->type;
int code = icmp_hdr(skb)->code; int code = icmp_hdr(skb)->code;
struct net *net = dev_net(skb->dev);
/* /*
* Use ping_err to handle all icmp errors except those * Use ping_err to handle all icmp errors except those