Commit 4b8474a0 authored by Eric Dumazet's avatar Eric Dumazet Committed by Jakub Kicinski
Browse files

ipv4: icmp: convert to dev_net_rcu()

__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: dde1bc0e ("[NETNS]: Add namespace for ICMP replying code.")
Closes: https://lore.kernel.org/netdev/20250203153633.46ce0337@kernel.org/


Reported-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20250205155120.1676781-9-edumazet@google.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 13951219
Loading
Loading
Loading
Loading
+17 −14
Original line number Diff line number Diff line
@@ -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)
{
	struct ipcm_cookie ipc;
	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;
	struct ipcm_cookie ipc;
	struct flowi4 fl4;
	struct sock *sk;
	struct inet_sock *inet;
@@ -608,12 +608,14 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
	struct sock *sk;

	if (!rt)
		goto out;
		return;

	rcu_read_lock();

	if (rt->dst.dev)
		net = dev_net(rt->dst.dev);
		net = dev_net_rcu(rt->dst.dev);
	else if (skb_in->dev)
		net = dev_net(skb_in->dev);
		net = dev_net_rcu(skb_in->dev);
	else
		goto out;

@@ -785,7 +787,8 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
	icmp_xmit_unlock(sk);
out_bh_enable:
	local_bh_enable();
out:;
out:
	rcu_read_unlock();
}
EXPORT_SYMBOL(__icmp_send);

@@ -834,7 +837,7 @@ static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
	 * avoid additional coding at protocol handlers.
	 */
	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;
	}

@@ -868,7 +871,7 @@ static enum skb_drop_reason icmp_unreach(struct sk_buff *skb)
	struct net *net;
	u32 info = 0;

	net = dev_net(skb_dst(skb)->dev);
	net = dev_net_rcu(skb_dst(skb)->dev);

	/*
	 *	Incomplete header ?
@@ -979,7 +982,7 @@ static enum skb_drop_reason icmp_unreach(struct sk_buff *skb)
static enum skb_drop_reason icmp_redirect(struct sk_buff *skb)
{
	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;
	}

@@ -1011,7 +1014,7 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb)
	struct icmp_bxm icmp_param;
	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? */
	if (READ_ONCE(net->ipv4.sysctl_icmp_echo_ignore_all))
		return SKB_NOT_DROPPED_YET;
@@ -1040,9 +1043,9 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb)

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_echo_iio *iio, _iio;
	struct net *net = dev_net(skb->dev);
	struct inet6_dev *in6_dev;
	struct in_device *in_dev;
	struct net_device *dev;
@@ -1181,7 +1184,7 @@ static enum skb_drop_reason icmp_timestamp(struct sk_buff *skb)
	return SKB_NOT_DROPPED_YET;

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;
}

@@ -1198,7 +1201,7 @@ int icmp_rcv(struct sk_buff *skb)
{
	enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
	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;

	if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
@@ -1371,9 +1374,9 @@ int icmp_err(struct sk_buff *skb, u32 info)
	struct iphdr *iph = (struct iphdr *)skb->data;
	int offset = iph->ihl<<2;
	struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset);
	struct net *net = dev_net_rcu(skb->dev);
	int type = icmp_hdr(skb)->type;
	int code = icmp_hdr(skb)->code;
	struct net *net = dev_net(skb->dev);

	/*
	 * Use ping_err to handle all icmp errors except those