FAILED: patch "[PATCH] ip: orphan prefetched skbs before multicast forwarding" failed to apply to 6.12-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x e36ce6e78fe3fc3c071a26750783b7ba081ce10d
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090318-afterglow-hash-e60c@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From e36ce6e78fe3fc3c071a26750783b7ba081ce10d Mon Sep 17 00:00:00 2001
From: Zhiling Zou <[email protected]>
Date: Thu, 13 Aug 2026 00:36:38 +0800
Subject: [PATCH] ip: orphan prefetched skbs before multicast forwarding
IPv4 and IPv6 input preserve an skb->sk association installed by bpf_sk_assign() so that local delivery can use the selected socket under RCU. Both address families can also prefetch a socket in UDP early demux. In both paths (BPF and UDP early demux) a reference is not guaranteed to be held on the socket.
When a multicast packet is not locally deliverable, IPv6 hands the original skb to ip6_mr_input(). IPv4's ip_mr_input() similarly keeps the original skb when local delivery is not needed. Either path can put the skb on an unresolved multicast route queue or forward it after the receive-side RCU section ends.
After the prefetched socket is destroyed, a later skb free invokes sock_pfree() and dereferences the stale skb->sk. Orphan the skb before each non-local multicast forwarding path. Local delivery retains the original skb; the existing skb_clone() calls provide multicast forwarding with a socket-free clone.
net/ipv4/ipmr.c +3 -0
@@ -2213,6 +2213,9 @@ int ip_mr_input(struct sk_buff *skb)if (IPCB (skb)->flags &IPSKB_FORWARDED ) goto dont_forward; +if (!local) +skb_orphan (skb); + mrt =ipmr_rt_fib_lookup (net, skb);if (IS_ERR (mrt)) {kfree_skb (skb);
net/ipv6/ip6_input.c +1 -0
@@ -622,6 +622,7 @@ int ip6_mc_input(struct sk_buff *skb)if (deliver) { skb2 =skb_clone (skb,GFP_ATOMIC ); }else { +skb_orphan (skb); skb2 = skb; skb =NULL ; }
Sasha Levin
maintainerPATCH 1/3 ipv6: adopt skb_dst_dev() and skb_dst_dev_net[_rcu]() helpers
From: Eric Dumazet <[email protected]>
[ Upstream commit 93d1cff35adc522a5d21e722eee1071f3f7dc716 ]
Use the new helpers as a step to deal with potential dst->dev races.
v2: fix typo in ipv6_rthdr_rcv() (kernel test robot <[email protected]>)
Stable-dep-of: e36ce6e78fe3 ("ip: orphan prefetched skbs before multicast forwarding")
--- include/net/inet6_hashtables.h | 2 +- include/net/ip6_tunnel.h | 2 +- net/ipv6/exthdrs.c | 8 ++++---- net/ipv6/ioam6.c | 17 +++++++++-------- net/ipv6/ip6_input.c | 6 ++++-- net/ipv6/ip6_output.c | 5 +++-- net/ipv6/ip6_tunnel.c | 2 +- net/ipv6/ip6_vti.c | 2 +- net/ipv6/netfilter.c | 4 ++-- net/ipv6/netfilter/nf_reject_ipv6.c | 2 +- net/ipv6/output_core.c | 2 +- net/ipv6/reassembly.c | 10 +++++----- net/ipv6/route.c | 4 ++-- net/ipv6/seg6_iptunnel.c | 6 +++--- net/ipv6/tcp_ipv6.c | 4 ++-- net/ipv6/xfrm6_output.c | 2 +- 16 files changed, 41 insertions(+), 37 deletions(-)
include/net/inet6_hashtables.h +1 -1
@@ -150,7 +150,7 @@ static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo, int iif, int sdif, bool *refcounted) { - struct net *net = dev_net_rcu(skb_dst(skb)->dev); + struct net *net = skb_dst_dev_net_rcu(skb);const struct ipv6hdr *ip6h =ipv6_hdr (skb);struct sock *sk;
include/net/ip6_tunnel.h +1 -1
@@ -170,7 +170,7 @@ static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb,memset (skb->cb,0 ,sizeof (struct inet6_skb_parm )); pkt_len = skb->len -skb_inner_network_offset (skb); - err =ip6_local_out (dev_net (skb_dst (skb)->dev), sk, skb); + err =ip6_local_out (skb_dst_dev_net (skb), sk, skb);if (dev) {if (unlikely (net_xmit_eval (err)))
net/ipv6/exthdrs.c +4 -4
@@ -469,7 +469,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb)return -1 ; } -if (skb_dst (skb)->dev->flags &IFF_LOOPBACK ) { +if (skb_dst_dev (skb)->flags &IFF_LOOPBACK ) {if (ipv6_hdr (skb)->hop_limit <=1 ) {__IP6_INC_STATS (net, idev,IPSTATS_MIB_INHDRERRORS ); icmpv6_send(skb, ICMPV6_TIME_EXCEED,
@@ -633,7 +633,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)return -1 ; } -if (skb_dst (skb)->dev->flags &IFF_LOOPBACK ) { +if (skb_dst_dev (skb)->flags &IFF_LOOPBACK ) {if (ipv6_hdr (skb)->hop_limit <=1 ) {__IP6_INC_STATS (net, idev,IPSTATS_MIB_INHDRERRORS ); icmpv6_send(skb, ICMPV6_TIME_EXCEED,
@@ -795,7 +795,7 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)kfree_skb (skb);return -1 ; } -if (!ipv6_chk_home_addr (dev_net (skb_dst (skb)->dev), addr)) { +if (!ipv6_chk_home_addr (skb_dst_dev_net (skb), addr)) {__IP6_INC_STATS (net, idev,IPSTATS_MIB_INADDRERRORS );kfree_skb (skb);return -1 ;
@@ -821,7 +821,7 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)return -1 ; } -if (skb_dst (skb)->dev->flags&IFF_LOOPBACK ) { +if (skb_dst_dev (skb)->flags &IFF_LOOPBACK ) {if (ipv6_hdr (skb)->hop_limit <=1 ) {__IP6_INC_STATS (net, idev,IPSTATS_MIB_INHDRERRORS ); icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
net/ipv6/ioam6.c +9 -8
@@ -714,6 +714,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,struct ioam6_schema *sc, unsigned int sclen, bool is_input) { + struct net_device *dev = skb_dst_dev(skb);struct timespec64 ts;ktime_t tstamp;u64 raw64;
@@ -730,7 +731,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,if (is_input) byte--; - raw32 =dev_net (skb_dst (skb)->dev)->ipv6.sysctl.ioam6_id; + raw32 =dev_net (dev)->ipv6.sysctl.ioam6_id; *(__be32 *)data =cpu_to_be32 ((byte <<24 ) | raw32); data +=sizeof (__be32);
@@ -746,10 +747,10 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, *(__be16 *)data =cpu_to_be16 (raw16); data +=sizeof (__be16); -if (skb_dst (skb)->dev->flags &IFF_LOOPBACK ) +if (dev->flags &IFF_LOOPBACK ) raw16 =IOAM6_U16_UNAVAILABLE ;else - raw16 = (__force u16 )READ_ONCE (__in6_dev_get (skb_dst (skb)->dev)->cnf.ioam6_id); + raw16 = (__force u16 )READ_ONCE (__in6_dev_get (dev)->cnf.ioam6_id); *(__be16 *)data =cpu_to_be16 (raw16); data +=sizeof (__be16);
@@ -801,10 +802,10 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,struct Qdisc *qdisc;__u32 qlen, backlog; -if (skb_dst (skb)->dev->flags &IFF_LOOPBACK ) { +if (dev->flags &IFF_LOOPBACK ) { *(__be32 *)data =cpu_to_be32 (IOAM6_U32_UNAVAILABLE ); }else { - queue =skb_get_tx_queue (skb_dst (skb)->dev, skb); + queue =skb_get_tx_queue (dev, skb); qdisc =rcu_dereference (queue->qdisc);qdisc_qstats_qlen_backlog (qdisc, &qlen, &backlog);
@@ -825,7 +826,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,if (is_input) byte--; - raw64 =dev_net (skb_dst (skb)->dev)->ipv6.sysctl.ioam6_id_wide; + raw64 =dev_net (dev)->ipv6.sysctl.ioam6_id_wide; *(__be64 *)data =cpu_to_be64 (((u64 )byte <<56 ) | raw64); data +=sizeof (__be64);
@@ -841,10 +842,10 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, *(__be32 *)data =cpu_to_be32 (raw32); data +=sizeof (__be32); -if (skb_dst (skb)->dev->flags &IFF_LOOPBACK ) +if (dev->flags &IFF_LOOPBACK ) raw32 =IOAM6_U32_UNAVAILABLE ;else - raw32 =READ_ONCE (__in6_dev_get (skb_dst (skb)->dev)->cnf.ioam6_id_wide); + raw32 =READ_ONCE (__in6_dev_get (dev)->cnf.ioam6_id_wide); *(__be32 *)data =cpu_to_be32 (raw32); data +=sizeof (__be32);
net/ipv6/ip6_input.c +4 -2
@@ -187,7 +187,9 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,* arrived via the sending interface (ethX), because of the * nature of scoping architecture. --yoshfuji */ -IP6CB (skb)->iif =skb_valid_dst (skb) ?ip6_dst_idev (skb_dst (skb))->dev->ifindex : dev->ifindex; +IP6CB (skb)->iif =skb_valid_dst (skb) ? +ip6_dst_idev (skb_dst (skb))->dev->ifindex : + dev->ifindex;if (unlikely (!pskb_may_pull (skb,sizeof (*hdr)))) goto err;
@@ -500,7 +502,7 @@ int ip6_mc_input(struct sk_buff *skb)struct net_device *dev;bool deliver; -__IP6_UPD_PO_STATS (dev_net (skb_dst (skb)->dev), +__IP6_UPD_PO_STATS (skb_dst_dev_net (skb),__in6_dev_get_safely (skb->dev),IPSTATS_MIB_INMCAST , skb->len);
net/ipv6/ip6_output.c +3 -2
@@ -234,8 +234,9 @@ static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *sint ip6_output (struct net *net,struct sock *sk,struct sk_buff *skb) { -struct net_device *dev =skb_dst (skb)->dev, *indev = skb->dev; -struct inet6_dev *idev =ip6_dst_idev (skb_dst (skb)); +struct dst_entry *dst =skb_dst (skb); +struct net_device *dev =dst_dev (dst), *indev = skb->dev; +struct inet6_dev *idev =ip6_dst_idev (dst); skb->protocol =htons (ETH_P_IPV6 ); skb->dev = dev;
net/ipv6/ip6_tunnel.c +1 -1
@@ -637,7 +637,7 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, }else {if (ip_route_input (skb2, eiph->daddr, eiph->saddr,ip4h_dscp (eiph), skb2->dev) || -skb_dst (skb2)->dev->type !=ARPHRD_TUNNEL6 ) +skb_dst_dev (skb2)->type !=ARPHRD_TUNNEL6 ) goto out; }
net/ipv6/ip6_vti.c +1 -1
@@ -531,7 +531,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) xmit:skb_scrub_packet (skb, !net_eq (t->net,dev_net (dev)));skb_dst_set (skb, dst); - skb->dev =skb_dst (skb)->dev; + skb->dev =dst_dev (dst); err =dst_output (t->net, skb->sk, skb);if (net_xmit_eval (err) ==0 )
net/ipv6/netfilter.c +2 -2
@@ -24,7 +24,7 @@ int ip6_route_me_harder(struct net *net, struct sock *sk_partial, struct sk_buff {const struct ipv6hdr *iph =ipv6_hdr (skb);struct sock *sk =sk_to_full_sk (sk_partial); -struct net_device *dev =skb_dst (skb)->dev; +struct net_device *dev =skb_dst_dev (skb);struct flow_keys flkeys;unsigned int hh_len;struct dst_entry *dst;
@@ -72,7 +72,7 @@ int ip6_route_me_harder(struct net *net, struct sock *sk_partial, struct sk_buff#endif /* Change in oif may mean change in hh_len. */ - hh_len =skb_dst (skb)->dev->hard_header_len; + hh_len =skb_dst_dev (skb)->hard_header_len;if (skb_headroom (skb) < hh_len &&pskb_expand_head (skb,HH_DATA_ALIGN (hh_len -skb_headroom (skb)),0 ,GFP_ATOMIC ))
net/ipv6/netfilter/nf_reject_ipv6.c +1 -1
@@ -330,7 +330,7 @@ void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb,skb_dst_set (oldskb, dst); } - fl6.flowi6_oif =l3mdev_master_ifindex (skb_dst (oldskb)->dev); + fl6.flowi6_oif =l3mdev_master_ifindex (skb_dst_dev (oldskb)); fl6.flowi6_mark =IP6_REPLY_MARK (net, oldskb->mark);security_skb_classify_flow (oldskb,flowi6_to_flowi_common (&fl6)); dst =ip6_route_output (net,NULL , &fl6);
net/ipv6/output_core.c +1 -1
@@ -141,7 +141,7 @@ int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) skb->protocol =htons (ETH_P_IPV6 );return nf_hook (NFPROTO_IPV6 ,NF_INET_LOCAL_OUT , - net, sk, skb,NULL ,skb_dst (skb)->dev, + net, sk, skb,NULL ,skb_dst_dev (skb), dst_output); }EXPORT_SYMBOL_GPL (__ip6_local_out);
net/ipv6/reassembly.c +5 -5
@@ -104,11 +104,11 @@ fq_find(struct net *net, __be32 id, const struct ipv6hdr *hdr, int iif) return container_of(q, struct frag_queue, q); } -static int ip6_frag_queue (struct frag_queue *fq,struct sk_buff *skb, +static int ip6_frag_queue (struct net *net, +struct frag_queue *fq,struct sk_buff *skb,struct frag_hdr *fhdr,int nhoff,u32 *prob_offset,int *refs) { -struct net *net =dev_net (skb_dst (skb)->dev);int offset, end, fragsize;struct sk_buff *prev_tail;struct net_device *dev;
@@ -324,10 +324,10 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb,static int ipv6_frag_rcv (struct sk_buff *skb) { +const struct ipv6hdr *hdr =ipv6_hdr (skb); +struct net *net =skb_dst_dev_net (skb);struct frag_hdr *fhdr;struct frag_queue *fq; -const struct ipv6hdr *hdr =ipv6_hdr (skb); -struct net *net =dev_net (skb_dst (skb)->dev);u8 nexthdr;int iif;
@@ -384,7 +384,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb)spin_lock (&fq->q.lock); fq->iif = iif; - ret =ip6_frag_queue (fq, skb, fhdr,IP6CB (skb)->nhoff, + ret =ip6_frag_queue (net, fq, skb, fhdr,IP6CB (skb)->nhoff, &prob_offset, &refs);spin_unlock (&fq->q.lock);
net/ipv6/route.c +2 -2
@@ -4597,7 +4597,7 @@ static int ip6_pkt_discard(struct sk_buff *skb)static int ip6_pkt_discard_out (struct net *net,struct sock *sk,struct sk_buff *skb) { - skb->dev =skb_dst (skb)->dev; + skb->dev =skb_dst_dev (skb);return ip6_pkt_drop (skb,ICMPV6_NOROUTE ,IPSTATS_MIB_OUTNOROUTES ); }
@@ -4608,7 +4608,7 @@ static int ip6_pkt_prohibit(struct sk_buff *skb)static int ip6_pkt_prohibit_out (struct net *net,struct sock *sk,struct sk_buff *skb) { - skb->dev =skb_dst (skb)->dev; + skb->dev =skb_dst_dev (skb);return ip6_pkt_drop (skb,ICMPV6_ADM_PROHIBITED ,IPSTATS_MIB_OUTNOROUTES ); }
net/ipv6/seg6_iptunnel.c +3 -3
@@ -365,7 +365,7 @@ static int __seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,#ifdef CONFIG_IPV6_SEG6_HMAC if (sr_has_hmac (isrh)) { -struct net *net =dev_net (skb_dst (skb)->dev); +struct net *net =skb_dst_dev_net (skb); err =seg6_push_hmac (net, &hdr->saddr, isrh);if (unlikely (err))
@@ -540,7 +540,7 @@ static int seg6_input_core(struct net *net, struct sock *sk,static int seg6_input_nf (struct sk_buff *skb) { -struct net_device *dev =skb_dst (skb)->dev; +struct net_device *dev =skb_dst_dev (skb);struct net *net =dev_net (skb->dev);switch (skb->protocol) {
@@ -626,7 +626,7 @@ static int seg6_output_core(struct net *net, struct sock *sk,static int seg6_output_nf (struct net *net,struct sock *sk,struct sk_buff *skb) { -struct net_device *dev =skb_dst (skb)->dev; +struct net_device *dev =skb_dst_dev (skb);switch (skb->protocol) {case htons (ETH_P_IP ):
net/ipv6/tcp_ipv6.c +2 -2
@@ -862,7 +862,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 int oif, int rst, u8 tclass, __be32 label, u32 priority, u32 txhash, struct tcp_key *key) { - struct net *net = sk ? sock_net(sk) : dev_net_rcu(skb_dst(skb)->dev); + struct net *net = sk ? sock_net(sk) : skb_dst_dev_net_rcu(skb);unsigned int tot_len =sizeof (struct tcphdr );struct sock *ctl_sk = net->ipv6.tcp_sk;const struct tcphdr *th =tcp_hdr (skb);
@@ -1036,7 +1036,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb,if (!sk && !ipv6_unicast_destination (skb))return ; - net = sk ?sock_net (sk) :dev_net_rcu (skb_dst (skb)->dev); + net = sk ?sock_net (sk) :skb_dst_dev_net_rcu (skb);/* Invalid TCP option size or twice included auth */ if (tcp_parse_auth_options (th, &md5_hash_location, &aoh))return ;
net/ipv6/xfrm6_output.c +1 -1
@@ -106,7 +106,7 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)int xfrm6_output (struct net *net,struct sock *sk,struct sk_buff *skb) {return NF_HOOK_COND (NFPROTO_IPV6 ,NF_INET_POST_ROUTING , - net, sk, skb, skb->dev,skb_dst (skb)->dev, + net, sk, skb, skb->dev,skb_dst_dev (skb), __xfrm6_output, !(IP6CB (skb)->flags &IP6SKB_REROUTED )); }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 ipv6: ip6_mc_input() and ip6_mr_input() cleanups
From: Eric Dumazet <[email protected]>
[ Upstream commit 46a94e44b9ec4d8d20108a1748c410d71a8dc759 ]
Both functions are always called under RCU.
We remove the extra rcu_read_lock()/rcu_read_unlock().
We use skb_dst_dev_net_rcu() instead of skb_dst_dev_net().
We use dev_net_rcu() instead of dev_net().
Stable-dep-of: e36ce6e78fe3 ("ip: orphan prefetched skbs before multicast forwarding")
--- net/ipv6/ip6_input.c | 29 +++++++++++------------------ net/ipv6/ip6mr.c | 9 ++++----- 2 files changed, 15 insertions(+), 23 deletions(-)
net/ipv6/ip6_input.c +11 -18
@@ -497,38 +497,32 @@ EXPORT_SYMBOL_GPL(ip6_input);int ip6_mc_input (struct sk_buff *skb) { +struct net_device *dev = skb->dev;int sdif =inet6_sdif (skb);const struct ipv6hdr *hdr; -struct net_device *dev;bool deliver; -__IP6_UPD_PO_STATS (skb_dst_dev_net (skb), -__in6_dev_get_safely (skb->dev),IPSTATS_MIB_INMCAST , - skb->len); +__IP6_UPD_PO_STATS (skb_dst_dev_net_rcu (skb), +__in6_dev_get_safely (dev),IPSTATS_MIB_INMCAST , + skb->len);/* skb->dev passed may be master dev for vrfs. */ if (sdif) { -rcu_read_lock (); - dev =dev_get_by_index_rcu (dev_net (skb->dev), sdif); + dev =dev_get_by_index_rcu (dev_net_rcu (dev), sdif);if (!dev) { -rcu_read_unlock ();kfree_skb (skb);return -ENODEV ; } - }else { - dev = skb->dev; } hdr =ipv6_hdr (skb); deliver =ipv6_chk_mcast_addr (dev, &hdr->daddr,NULL ); -if (sdif) -rcu_read_unlock ();#ifdef CONFIG_IPV6_MROUTE /* * IPv6 multicast router mode is now supported ;) */ -if (atomic_read (&dev_net (skb->dev)->ipv6.devconf_all->mc_forwarding) && +if (atomic_read (&dev_net_rcu (skb->dev)->ipv6.devconf_all->mc_forwarding) && !(ipv6_addr_type (&hdr->daddr) & (IPV6_ADDR_LOOPBACK |IPV6_ADDR_LINKLOCAL )) &&likely (!(IP6CB (skb)->flags &IP6SKB_FORWARDED ))) {
@@ -569,22 +563,21 @@ int ip6_mc_input(struct sk_buff *skb) /* unknown RA - process it normally */ } -if (deliver) +if (deliver) { skb2 =skb_clone (skb,GFP_ATOMIC ); -else { + }else { skb2 = skb; skb =NULL ; } -if (skb2) { +if (skb2)ip6_mr_input (skb2); - } } out: #endif -if (likely (deliver)) +if (likely (deliver)) {ip6_input (skb); -else { + }else {/* discard */ kfree_skb (skb); }
net/ipv6/ip6mr.c +4 -5
@@ -2222,21 +2222,20 @@ static void ip6_mr_forward(struct net *net, struct mr_table *mrt,int ip6_mr_input (struct sk_buff *skb) { +struct net_device *dev = skb->dev; +struct net *net =dev_net_rcu (dev);struct mfc6_cache *cache; -struct net *net =dev_net (skb->dev);struct mr_table *mrt;struct flowi6 fl6 = { - .flowi6_iif = skb->dev->ifindex, + .flowi6_iif = dev->ifindex, .flowi6_mark = skb->mark, };int err; -struct net_device *dev;/* skb->dev passed in is the master dev for vrfs. * Get the proper interface that does have a vif associated with it. */ - dev = skb->dev; -if (netif_is_l3_master (skb->dev)) { +if (netif_is_l3_master (dev)) { dev =dev_get_by_index_rcu (net,IPCB (skb)->iif);if (!dev) {kfree_skb (skb);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 ip: orphan prefetched skbs before multicast forwarding
In reply to Sasha LevinFrom: Zhiling Zou <[email protected]>
[ Upstream commit e36ce6e78fe3fc3c071a26750783b7ba081ce10d ]
IPv4 and IPv6 input preserve an skb->sk association installed by bpf_sk_assign() so that local delivery can use the selected socket under RCU. Both address families can also prefetch a socket in UDP early demux. In both paths (BPF and UDP early demux) a reference is not guaranteed to be held on the socket.
When a multicast packet is not locally deliverable, IPv6 hands the original skb to ip6_mr_input(). IPv4's ip_mr_input() similarly keeps the original skb when local delivery is not needed. Either path can put the skb on an unresolved multicast route queue or forward it after the receive-side RCU section ends.
After the prefetched socket is destroyed, a later skb free invokes sock_pfree() and dereferences the stale skb->sk. Orphan the skb before each non-local multicast forwarding path. Local delivery retains the original skb; the existing skb_clone() calls provide multicast forwarding with a socket-free clone.
--- net/ipv4/ipmr.c | 3 +++ net/ipv6/ip6_input.c | 1 + 2 files changed, 4 insertions(+)
net/ipv4/ipmr.c +3 -0
@@ -2140,6 +2140,9 @@ int ip_mr_input(struct sk_buff *skb)if (IPCB (skb)->flags &IPSKB_FORWARDED ) goto dont_forward; +if (!local) +skb_orphan (skb); + mrt =ipmr_rt_fib_lookup (net, skb);if (IS_ERR (mrt)) {kfree_skb (skb);
net/ipv6/ip6_input.c +1 -0
@@ -566,6 +566,7 @@ int ip6_mc_input(struct sk_buff *skb)if (deliver) { skb2 =skb_clone (skb,GFP_ATOMIC ); }else { +skb_orphan (skb); skb2 = skb; skb =NULL ; }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 1/3 ipv6: adopt skb_dst_dev() and skb_dst_dev_net[_rcu]() helpers
In reply to [email protected]From: Eric Dumazet <[email protected]>
[ Upstream commit 93d1cff35adc522a5d21e722eee1071f3f7dc716 ]
Use the new helpers as a step to deal with potential dst->dev races.
v2: fix typo in ipv6_rthdr_rcv() (kernel test robot <[email protected]>)
Stable-dep-of: e36ce6e78fe3 ("ip: orphan prefetched skbs before multicast forwarding")
--- include/net/inet6_hashtables.h | 2 +- include/net/ip6_tunnel.h | 2 +- net/ipv6/exthdrs.c | 8 ++++---- net/ipv6/ioam6.c | 17 +++++++++-------- net/ipv6/ip6_input.c | 6 ++++-- net/ipv6/ip6_output.c | 5 +++-- net/ipv6/ip6_tunnel.c | 2 +- net/ipv6/ip6_vti.c | 2 +- net/ipv6/netfilter.c | 4 ++-- net/ipv6/netfilter/nf_reject_ipv6.c | 2 +- net/ipv6/output_core.c | 2 +- net/ipv6/reassembly.c | 10 +++++----- net/ipv6/route.c | 4 ++-- net/ipv6/seg6_iptunnel.c | 6 +++--- net/ipv6/tcp_ipv6.c | 4 ++-- net/ipv6/xfrm6_output.c | 2 +- 16 files changed, 41 insertions(+), 37 deletions(-)
include/net/inet6_hashtables.h +1 -1
@@ -150,7 +150,7 @@ static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo, int iif, int sdif, bool *refcounted) { - struct net *net = dev_net_rcu(skb_dst(skb)->dev); + struct net *net = skb_dst_dev_net_rcu(skb);const struct ipv6hdr *ip6h =ipv6_hdr (skb);struct sock *sk;
include/net/ip6_tunnel.h +1 -1
@@ -170,7 +170,7 @@ static inline void ip6tunnel_xmit(struct sock *sk, struct sk_buff *skb,memset (skb->cb,0 ,sizeof (struct inet6_skb_parm )); pkt_len = skb->len -skb_inner_network_offset (skb); - err =ip6_local_out (dev_net (skb_dst (skb)->dev), sk, skb); + err =ip6_local_out (skb_dst_dev_net (skb), sk, skb);if (dev) {if (unlikely (net_xmit_eval (err)))
net/ipv6/exthdrs.c +4 -4
@@ -469,7 +469,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb)return -1 ; } -if (skb_dst (skb)->dev->flags &IFF_LOOPBACK ) { +if (skb_dst_dev (skb)->flags &IFF_LOOPBACK ) {if (ipv6_hdr (skb)->hop_limit <=1 ) {__IP6_INC_STATS (net, idev,IPSTATS_MIB_INHDRERRORS ); icmpv6_send(skb, ICMPV6_TIME_EXCEED,
@@ -633,7 +633,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)return -1 ; } -if (skb_dst (skb)->dev->flags &IFF_LOOPBACK ) { +if (skb_dst_dev (skb)->flags &IFF_LOOPBACK ) {if (ipv6_hdr (skb)->hop_limit <=1 ) {__IP6_INC_STATS (net, idev,IPSTATS_MIB_INHDRERRORS ); icmpv6_send(skb, ICMPV6_TIME_EXCEED,
@@ -795,7 +795,7 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)kfree_skb (skb);return -1 ; } -if (!ipv6_chk_home_addr (dev_net (skb_dst (skb)->dev), addr)) { +if (!ipv6_chk_home_addr (skb_dst_dev_net (skb), addr)) {__IP6_INC_STATS (net, idev,IPSTATS_MIB_INADDRERRORS );kfree_skb (skb);return -1 ;
@@ -821,7 +821,7 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)return -1 ; } -if (skb_dst (skb)->dev->flags&IFF_LOOPBACK ) { +if (skb_dst_dev (skb)->flags &IFF_LOOPBACK ) {if (ipv6_hdr (skb)->hop_limit <=1 ) {__IP6_INC_STATS (net, idev,IPSTATS_MIB_INHDRERRORS ); icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
net/ipv6/ioam6.c +9 -8
@@ -714,6 +714,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,struct ioam6_schema *sc, unsigned int sclen, bool is_input) { + struct net_device *dev = skb_dst_dev(skb);struct timespec64 ts;ktime_t tstamp;u64 raw64;
@@ -730,7 +731,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,if (is_input) byte--; - raw32 =dev_net (skb_dst (skb)->dev)->ipv6.sysctl.ioam6_id; + raw32 =dev_net (dev)->ipv6.sysctl.ioam6_id; *(__be32 *)data =cpu_to_be32 ((byte <<24 ) | raw32); data +=sizeof (__be32);
@@ -746,10 +747,10 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, *(__be16 *)data =cpu_to_be16 (raw16); data +=sizeof (__be16); -if (skb_dst (skb)->dev->flags &IFF_LOOPBACK ) +if (dev->flags &IFF_LOOPBACK ) raw16 =IOAM6_U16_UNAVAILABLE ;else - raw16 = (__force u16 )READ_ONCE (__in6_dev_get (skb_dst (skb)->dev)->cnf.ioam6_id); + raw16 = (__force u16 )READ_ONCE (__in6_dev_get (dev)->cnf.ioam6_id); *(__be16 *)data =cpu_to_be16 (raw16); data +=sizeof (__be16);
@@ -801,10 +802,10 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,struct Qdisc *qdisc;__u32 qlen, backlog; -if (skb_dst (skb)->dev->flags &IFF_LOOPBACK ) { +if (dev->flags &IFF_LOOPBACK ) { *(__be32 *)data =cpu_to_be32 (IOAM6_U32_UNAVAILABLE ); }else { - queue =skb_get_tx_queue (skb_dst (skb)->dev, skb); + queue =skb_get_tx_queue (dev, skb); qdisc =rcu_dereference (queue->qdisc);qdisc_qstats_qlen_backlog (qdisc, &qlen, &backlog);
@@ -825,7 +826,7 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb,if (is_input) byte--; - raw64 =dev_net (skb_dst (skb)->dev)->ipv6.sysctl.ioam6_id_wide; + raw64 =dev_net (dev)->ipv6.sysctl.ioam6_id_wide; *(__be64 *)data =cpu_to_be64 (((u64 )byte <<56 ) | raw64); data +=sizeof (__be64);
@@ -841,10 +842,10 @@ static void __ioam6_fill_trace_data(struct sk_buff *skb, *(__be32 *)data =cpu_to_be32 (raw32); data +=sizeof (__be32); -if (skb_dst (skb)->dev->flags &IFF_LOOPBACK ) +if (dev->flags &IFF_LOOPBACK ) raw32 =IOAM6_U32_UNAVAILABLE ;else - raw32 =READ_ONCE (__in6_dev_get (skb_dst (skb)->dev)->cnf.ioam6_id_wide); + raw32 =READ_ONCE (__in6_dev_get (dev)->cnf.ioam6_id_wide); *(__be32 *)data =cpu_to_be32 (raw32); data +=sizeof (__be32);
net/ipv6/ip6_input.c +4 -2
@@ -187,7 +187,9 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,* arrived via the sending interface (ethX), because of the * nature of scoping architecture. --yoshfuji */ -IP6CB (skb)->iif =skb_valid_dst (skb) ?ip6_dst_idev (skb_dst (skb))->dev->ifindex : dev->ifindex; +IP6CB (skb)->iif =skb_valid_dst (skb) ? +ip6_dst_idev (skb_dst (skb))->dev->ifindex : + dev->ifindex;if (unlikely (!pskb_may_pull (skb,sizeof (*hdr)))) goto err;
@@ -500,7 +502,7 @@ int ip6_mc_input(struct sk_buff *skb)struct net_device *dev;bool deliver; -__IP6_UPD_PO_STATS (dev_net (skb_dst (skb)->dev), +__IP6_UPD_PO_STATS (skb_dst_dev_net (skb),__in6_dev_get_safely (skb->dev),IPSTATS_MIB_INMCAST , skb->len);
net/ipv6/ip6_output.c +3 -2
@@ -234,8 +234,9 @@ static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *sint ip6_output (struct net *net,struct sock *sk,struct sk_buff *skb) { -struct net_device *dev =skb_dst (skb)->dev, *indev = skb->dev; -struct inet6_dev *idev =ip6_dst_idev (skb_dst (skb)); +struct dst_entry *dst =skb_dst (skb); +struct net_device *dev =dst_dev (dst), *indev = skb->dev; +struct inet6_dev *idev =ip6_dst_idev (dst); skb->protocol =htons (ETH_P_IPV6 ); skb->dev = dev;
net/ipv6/ip6_tunnel.c +1 -1
@@ -637,7 +637,7 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, }else {if (ip_route_input (skb2, eiph->daddr, eiph->saddr,ip4h_dscp (eiph), skb2->dev) || -skb_dst (skb2)->dev->type !=ARPHRD_TUNNEL6 ) +skb_dst_dev (skb2)->type !=ARPHRD_TUNNEL6 ) goto out; }
net/ipv6/ip6_vti.c +1 -1
@@ -531,7 +531,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl) xmit:skb_scrub_packet (skb, !net_eq (t->net,dev_net (dev)));skb_dst_set (skb, dst); - skb->dev =skb_dst (skb)->dev; + skb->dev =dst_dev (dst); err =dst_output (t->net, skb->sk, skb);if (net_xmit_eval (err) ==0 )
net/ipv6/netfilter.c +2 -2
@@ -24,7 +24,7 @@ int ip6_route_me_harder(struct net *net, struct sock *sk_partial, struct sk_buff {const struct ipv6hdr *iph =ipv6_hdr (skb);struct sock *sk =sk_to_full_sk (sk_partial); -struct net_device *dev =skb_dst (skb)->dev; +struct net_device *dev =skb_dst_dev (skb);struct flow_keys flkeys;unsigned int hh_len;struct dst_entry *dst;
@@ -72,7 +72,7 @@ int ip6_route_me_harder(struct net *net, struct sock *sk_partial, struct sk_buff#endif /* Change in oif may mean change in hh_len. */ - hh_len =skb_dst (skb)->dev->hard_header_len; + hh_len =skb_dst_dev (skb)->hard_header_len;if (skb_headroom (skb) < hh_len &&pskb_expand_head (skb,HH_DATA_ALIGN (hh_len -skb_headroom (skb)),0 ,GFP_ATOMIC ))
net/ipv6/netfilter/nf_reject_ipv6.c +1 -1
@@ -330,7 +330,7 @@ void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb,skb_dst_set (oldskb, dst); } - fl6.flowi6_oif =l3mdev_master_ifindex (skb_dst (oldskb)->dev); + fl6.flowi6_oif =l3mdev_master_ifindex (skb_dst_dev (oldskb)); fl6.flowi6_mark =IP6_REPLY_MARK (net, oldskb->mark);security_skb_classify_flow (oldskb,flowi6_to_flowi_common (&fl6)); dst =ip6_route_output (net,NULL , &fl6);
net/ipv6/output_core.c +1 -1
@@ -141,7 +141,7 @@ int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) skb->protocol =htons (ETH_P_IPV6 );return nf_hook (NFPROTO_IPV6 ,NF_INET_LOCAL_OUT , - net, sk, skb,NULL ,skb_dst (skb)->dev, + net, sk, skb,NULL ,skb_dst_dev (skb), dst_output); }EXPORT_SYMBOL_GPL (__ip6_local_out);
net/ipv6/reassembly.c +5 -5
@@ -104,11 +104,11 @@ fq_find(struct net *net, __be32 id, const struct ipv6hdr *hdr, int iif) return container_of(q, struct frag_queue, q); } -static int ip6_frag_queue (struct frag_queue *fq,struct sk_buff *skb, +static int ip6_frag_queue (struct net *net, +struct frag_queue *fq,struct sk_buff *skb,struct frag_hdr *fhdr,int nhoff,u32 *prob_offset,int *refs) { -struct net *net =dev_net (skb_dst (skb)->dev);int offset, end, fragsize;struct sk_buff *prev_tail;struct net_device *dev;
@@ -324,10 +324,10 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *skb,static int ipv6_frag_rcv (struct sk_buff *skb) { +const struct ipv6hdr *hdr =ipv6_hdr (skb); +struct net *net =skb_dst_dev_net (skb);struct frag_hdr *fhdr;struct frag_queue *fq; -const struct ipv6hdr *hdr =ipv6_hdr (skb); -struct net *net =dev_net (skb_dst (skb)->dev);u8 nexthdr;int iif;
@@ -384,7 +384,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb)spin_lock (&fq->q.lock); fq->iif = iif; - ret =ip6_frag_queue (fq, skb, fhdr,IP6CB (skb)->nhoff, + ret =ip6_frag_queue (net, fq, skb, fhdr,IP6CB (skb)->nhoff, &prob_offset, &refs);spin_unlock (&fq->q.lock);
net/ipv6/route.c +2 -2
@@ -4597,7 +4597,7 @@ static int ip6_pkt_discard(struct sk_buff *skb)static int ip6_pkt_discard_out (struct net *net,struct sock *sk,struct sk_buff *skb) { - skb->dev =skb_dst (skb)->dev; + skb->dev =skb_dst_dev (skb);return ip6_pkt_drop (skb,ICMPV6_NOROUTE ,IPSTATS_MIB_OUTNOROUTES ); }
@@ -4608,7 +4608,7 @@ static int ip6_pkt_prohibit(struct sk_buff *skb)static int ip6_pkt_prohibit_out (struct net *net,struct sock *sk,struct sk_buff *skb) { - skb->dev =skb_dst (skb)->dev; + skb->dev =skb_dst_dev (skb);return ip6_pkt_drop (skb,ICMPV6_ADM_PROHIBITED ,IPSTATS_MIB_OUTNOROUTES ); }
net/ipv6/seg6_iptunnel.c +3 -3
@@ -365,7 +365,7 @@ static int __seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,#ifdef CONFIG_IPV6_SEG6_HMAC if (sr_has_hmac (isrh)) { -struct net *net =dev_net (skb_dst (skb)->dev); +struct net *net =skb_dst_dev_net (skb); err =seg6_push_hmac (net, &hdr->saddr, isrh);if (unlikely (err))
@@ -540,7 +540,7 @@ static int seg6_input_core(struct net *net, struct sock *sk,static int seg6_input_nf (struct sk_buff *skb) { -struct net_device *dev =skb_dst (skb)->dev; +struct net_device *dev =skb_dst_dev (skb);struct net *net =dev_net (skb->dev);switch (skb->protocol) {
@@ -626,7 +626,7 @@ static int seg6_output_core(struct net *net, struct sock *sk,static int seg6_output_nf (struct net *net,struct sock *sk,struct sk_buff *skb) { -struct net_device *dev =skb_dst (skb)->dev; +struct net_device *dev =skb_dst_dev (skb);switch (skb->protocol) {case htons (ETH_P_IP ):
net/ipv6/tcp_ipv6.c +2 -2
@@ -862,7 +862,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 int oif, int rst, u8 tclass, __be32 label, u32 priority, u32 txhash, struct tcp_key *key) { - struct net *net = sk ? sock_net(sk) : dev_net_rcu(skb_dst(skb)->dev); + struct net *net = sk ? sock_net(sk) : skb_dst_dev_net_rcu(skb);unsigned int tot_len =sizeof (struct tcphdr );struct sock *ctl_sk = net->ipv6.tcp_sk;const struct tcphdr *th =tcp_hdr (skb);
@@ -1036,7 +1036,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb,if (!sk && !ipv6_unicast_destination (skb))return ; - net = sk ?sock_net (sk) :dev_net_rcu (skb_dst (skb)->dev); + net = sk ?sock_net (sk) :skb_dst_dev_net_rcu (skb);/* Invalid TCP option size or twice included auth */ if (tcp_parse_auth_options (th, &md5_hash_location, &aoh))return ;
net/ipv6/xfrm6_output.c +1 -1
@@ -106,7 +106,7 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)int xfrm6_output (struct net *net,struct sock *sk,struct sk_buff *skb) {return NF_HOOK_COND (NFPROTO_IPV6 ,NF_INET_POST_ROUTING , - net, sk, skb, skb->dev,skb_dst (skb)->dev, + net, sk, skb, skb->dev,skb_dst_dev (skb), __xfrm6_output, !(IP6CB (skb)->flags &IP6SKB_REROUTED )); }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 ipv6: ip6_mc_input() and ip6_mr_input() cleanups
From: Eric Dumazet <[email protected]>
[ Upstream commit 46a94e44b9ec4d8d20108a1748c410d71a8dc759 ]
Both functions are always called under RCU.
We remove the extra rcu_read_lock()/rcu_read_unlock().
We use skb_dst_dev_net_rcu() instead of skb_dst_dev_net().
We use dev_net_rcu() instead of dev_net().
Stable-dep-of: e36ce6e78fe3 ("ip: orphan prefetched skbs before multicast forwarding")
--- net/ipv6/ip6_input.c | 29 +++++++++++------------------ net/ipv6/ip6mr.c | 9 ++++----- 2 files changed, 15 insertions(+), 23 deletions(-)
net/ipv6/ip6_input.c +11 -18
@@ -497,38 +497,32 @@ EXPORT_SYMBOL_GPL(ip6_input);int ip6_mc_input (struct sk_buff *skb) { +struct net_device *dev = skb->dev;int sdif =inet6_sdif (skb);const struct ipv6hdr *hdr; -struct net_device *dev;bool deliver; -__IP6_UPD_PO_STATS (skb_dst_dev_net (skb), -__in6_dev_get_safely (skb->dev),IPSTATS_MIB_INMCAST , - skb->len); +__IP6_UPD_PO_STATS (skb_dst_dev_net_rcu (skb), +__in6_dev_get_safely (dev),IPSTATS_MIB_INMCAST , + skb->len);/* skb->dev passed may be master dev for vrfs. */ if (sdif) { -rcu_read_lock (); - dev =dev_get_by_index_rcu (dev_net (skb->dev), sdif); + dev =dev_get_by_index_rcu (dev_net_rcu (dev), sdif);if (!dev) { -rcu_read_unlock ();kfree_skb (skb);return -ENODEV ; } - }else { - dev = skb->dev; } hdr =ipv6_hdr (skb); deliver =ipv6_chk_mcast_addr (dev, &hdr->daddr,NULL ); -if (sdif) -rcu_read_unlock ();#ifdef CONFIG_IPV6_MROUTE /* * IPv6 multicast router mode is now supported ;) */ -if (atomic_read (&dev_net (skb->dev)->ipv6.devconf_all->mc_forwarding) && +if (atomic_read (&dev_net_rcu (skb->dev)->ipv6.devconf_all->mc_forwarding) && !(ipv6_addr_type (&hdr->daddr) & (IPV6_ADDR_LOOPBACK |IPV6_ADDR_LINKLOCAL )) &&likely (!(IP6CB (skb)->flags &IP6SKB_FORWARDED ))) {
@@ -569,22 +563,21 @@ int ip6_mc_input(struct sk_buff *skb) /* unknown RA - process it normally */ } -if (deliver) +if (deliver) { skb2 =skb_clone (skb,GFP_ATOMIC ); -else { + }else { skb2 = skb; skb =NULL ; } -if (skb2) { +if (skb2)ip6_mr_input (skb2); - } } out: #endif -if (likely (deliver)) +if (likely (deliver)) {ip6_input (skb); -else { + }else {/* discard */ kfree_skb (skb); }
net/ipv6/ip6mr.c +4 -5
@@ -2222,21 +2222,20 @@ static void ip6_mr_forward(struct net *net, struct mr_table *mrt,int ip6_mr_input (struct sk_buff *skb) { +struct net_device *dev = skb->dev; +struct net *net =dev_net_rcu (dev);struct mfc6_cache *cache; -struct net *net =dev_net (skb->dev);struct mr_table *mrt;struct flowi6 fl6 = { - .flowi6_iif = skb->dev->ifindex, + .flowi6_iif = dev->ifindex, .flowi6_mark = skb->mark, };int err; -struct net_device *dev;/* skb->dev passed in is the master dev for vrfs. * Get the proper interface that does have a vif associated with it. */ - dev = skb->dev; -if (netif_is_l3_master (skb->dev)) { +if (netif_is_l3_master (dev)) { dev =dev_get_by_index_rcu (net,IPCB (skb)->iif);if (!dev) {kfree_skb (skb);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 ip: orphan prefetched skbs before multicast forwarding
In reply to Sasha LevinFrom: Zhiling Zou <[email protected]>
[ Upstream commit e36ce6e78fe3fc3c071a26750783b7ba081ce10d ]
IPv4 and IPv6 input preserve an skb->sk association installed by bpf_sk_assign() so that local delivery can use the selected socket under RCU. Both address families can also prefetch a socket in UDP early demux. In both paths (BPF and UDP early demux) a reference is not guaranteed to be held on the socket.
When a multicast packet is not locally deliverable, IPv6 hands the original skb to ip6_mr_input(). IPv4's ip_mr_input() similarly keeps the original skb when local delivery is not needed. Either path can put the skb on an unresolved multicast route queue or forward it after the receive-side RCU section ends.
After the prefetched socket is destroyed, a later skb free invokes sock_pfree() and dereferences the stale skb->sk. Orphan the skb before each non-local multicast forwarding path. Local delivery retains the original skb; the existing skb_clone() calls provide multicast forwarding with a socket-free clone.
--- net/ipv4/ipmr.c | 3 +++ net/ipv6/ip6_input.c | 1 + 2 files changed, 4 insertions(+)
net/ipv4/ipmr.c +3 -0
@@ -2140,6 +2140,9 @@ int ip_mr_input(struct sk_buff *skb)if (IPCB (skb)->flags &IPSKB_FORWARDED ) goto dont_forward; +if (!local) +skb_orphan (skb); + mrt =ipmr_rt_fib_lookup (net, skb);if (IS_ERR (mrt)) {kfree_skb (skb);
net/ipv6/ip6_input.c +1 -0
@@ -566,6 +566,7 @@ int ip6_mc_input(struct sk_buff *skb)if (deliver) { skb2 =skb_clone (skb,GFP_ATOMIC ); }else { +skb_orphan (skb); skb2 = skb; skb =NULL ; }
Signature · 1 line
2.53.0