FAILED: patch "[PATCH] ipv6: rpl: fix NULL dereference of idev in ipv6_rpl_srh_rcv()" failed to apply to 5.10-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 5.10-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-5.10.y
git checkout FETCH_HEAD
git cherry-pick -x f826df95332c07380206dbd54178b6eefb311aba
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026090351-marbling-tiny-a334@gregkh' --subject-prefix 'PATCH 5.10.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From f826df95332c07380206dbd54178b6eefb311aba Mon Sep 17 00:00:00 2001
From: Andrea Mayer <[email protected]>
Date: Mon, 17 Aug 2026 15:26:44 +0200
Subject: [PATCH] ipv6: rpl: fix NULL dereference of idev in ipv6_rpl_srh_rcv()
ipv6_rpl_srh_rcv() dereferences idev from __in6_dev_get() without a NULL check when reading idev->cnf.rpl_seg_enabled.
When the device's MTU drops below IPV6_MIN_MTU, addrconf_ifdown() clears dev->ip6_ptr through RCU_INIT_POINTER(). A packet that passed the idev check in ip6_rcv_core() can then reach ipv6_rpl_srh_rcv() with dev->ip6_ptr already NULL.
Reproduced by flooding the receiving interface with ping6 traffic while flapping its MTU between 1500 and 1200:
BUG: KASAN: null-ptr-deref in ipv6_rpl_srh_rcv+0xb3/0x1070 Read of size 4 at addr 00000000000006b4 by task ping6/394 CPU: 2 UID: 0 PID: 394 Comm: ping6 Not tainted 7.2.0-rc7-micro-vm-dev-00095-g24ef02f934ee #240 PREEMPT(full) Call Trace: <IRQ> kasan_report+0xc6/0x100 ipv6_rpl_srh_rcv+0xb3/0x1070 ip6_protocol_deliver_rcu+0x759/0x9a0 ip6_input_finish+0xa8/0x1b0 ip6_input+0xe1/0x490 ipv6_rcv+0x33d/0x460 __netif_receive_skb_one_core+0xd6/0x130 process_backlog+0x2cc/0xa00 __napi_poll.constprop.0+0x56/0x270 net_rx_action+0x327/0x730 handle_softirqs+0x11e/0x630 do_softirq+0xb3/0xf0 </IRQ>
Both ipv6_rpl_srh_rcv() and ipv6_srh_rcv() are called only from ipv6_rthdr_rcv(), which already has an idev lookup.
Fix the NULL dereference on the RPL path by checking idev in ipv6_rthdr_rcv(), before it calls either function. The callees take idev as an argument and no longer call __in6_dev_get(), so the packet is now dropped in one place, with SKB_DROP_REASON_IPV6DISABLED on both paths.
net/ipv6/exthdrs.c +12 -14
@@ -368,23 +368,16 @@ static void seg6_update_csum(struct sk_buff *skb) (__be32 *)addr); } -static int ipv6_srh_rcv (struct sk_buff *skb) +static int ipv6_srh_rcv (struct sk_buff *skb,struct inet6_dev *idev) {struct inet6_skb_parm *opt =IP6CB (skb);struct net *net =dev_net (skb->dev);struct ipv6_sr_hdr *hdr; -struct inet6_dev *idev;struct in6_addr *addr;int accept_seg6; hdr = (struct ipv6_sr_hdr *)skb_transport_header (skb); - idev =__in6_dev_get (skb->dev); -if (!idev) { -kfree_skb (skb); -return -1 ; - } - accept_seg6 =min (READ_ONCE (net->ipv6.devconf_all->seg6_enabled),READ_ONCE (idev->cnf.seg6_enabled));
@@ -485,12 +478,11 @@ static int ipv6_srh_rcv(struct sk_buff *skb)return -1 ; } -static int ipv6_rpl_srh_rcv (struct sk_buff *skb) +static int ipv6_rpl_srh_rcv (struct sk_buff *skb,struct inet6_dev *idev) {struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr;struct inet6_skb_parm *opt =IP6CB (skb);struct net *net =dev_net (skb->dev); -struct inet6_dev *idev;struct ipv6hdr *oldhdr;unsigned int chdr_len;unsigned char *buf;
@@ -499,8 +491,6 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)u64 n =0 ;u32 r; - idev =__in6_dev_get (skb->dev); - accept_rpl_seg =min (READ_ONCE (net->ipv6.devconf_all->rpl_seg_enabled),READ_ONCE (idev->cnf.rpl_seg_enabled));if (!accept_rpl_seg) {
@@ -689,10 +679,14 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)switch (hdr->type) {case IPV6_SRCRT_TYPE_4 :/* segment routing */ -return ipv6_srh_rcv (skb); +if (!idev) + goto disabled; +return ipv6_srh_rcv (skb, idev);case IPV6_SRCRT_TYPE_3 :/* rpl segment routing */ -return ipv6_rpl_srh_rcv (skb); +if (!idev) + goto disabled; +return ipv6_rpl_srh_rcv (skb, idev);default :break ; }
@@ -837,6 +831,10 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)icmpv6_param_prob (skb,ICMPV6_HDR_FIELD , (&hdr->type) -skb_network_header (skb));return -1 ; + +disabled: +kfree_skb_reason (skb,SKB_DROP_REASON_IPV6DISABLED ); +return -1 ; }static const struct inet6_protocol rthdr_protocol = {
Sasha Levin
maintainerPATCH 1/2 ipv6/addrconf: annotate data-races around devconf fields (II)
From: Eric Dumazet <[email protected]>
[ Upstream commit 2f0ff05a44302c91af54a5f9efe1b65b7681540e ]
Final (?) round of this series.
Annotate lockless reads on following devconf fields, because they be changed concurrently from /proc/net/ipv6/conf.
- accept_dad
- optimistic_dad
- use_optimistic
- use_oif_addrs_only
- ra_honor_pio_life
- keep_addr_on_down
- ndisc_notify
- ndisc_evict_nocarrier
- suppress_frag_ndisc
- addr_gen_mode
- seg6_enabled
- ioam6_enabled
- ioam6_id
- ioam6_id_wide
- drop_unicast_in_l2_multicast
- mldv[12]_unsolicited_report_interval
- force_mld_version
- force_tllao
- accept_untracked_na
- drop_unsolicited_na
- accept_source_route
[ Backport to 5.10: omit ioam6.c and annotations for fields and control
flow only present in newer kernels. Preserve the applicable data-race annotations, and annotate rpl_seg_enabled as well so the dependent IPv6 RPL fix applies against its expected context. ]
[ sashal: Reduced backport -- upstream 2f0ff05a44302 touches 7 file(s), this
backport carries 6. Not backported here:
net/ipv6/ioam6.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: f826df95332c ("ipv6: rpl: fix NULL dereference of idev in ipv6_rpl_srh_rcv()")
--- net/ipv6/addrconf.c | 47 +++++++++++++++++++++++--------------------- net/ipv6/exthdrs.c | 20 +++++++++---------- net/ipv6/ip6_input.c | 2 +- net/ipv6/mcast.c | 14 ++++++------- net/ipv6/ndisc.c | 12 +++++------ net/ipv6/seg6_hmac.c | 8 +++++--- 6 files changed, 54 insertions(+), 49 deletions(-)
net/ipv6/addrconf.c +25 -22
@@ -1492,15 +1492,17 @@ static inline int ipv6_saddr_preferred(int type)return 0 ; } -static bool ipv6_use_optimistic_addr (struct net *net, -struct inet6_dev *idev) +static bool ipv6_use_optimistic_addr (const struct net *net, +const struct inet6_dev *idev) {#ifdef CONFIG_IPV6_OPTIMISTIC_DAD if (!idev)return false; -if (!net->ipv6.devconf_all->optimistic_dad && !idev->cnf.optimistic_dad) +if (!READ_ONCE (net->ipv6.devconf_all->optimistic_dad) && + !READ_ONCE (idev->cnf.optimistic_dad))return false; -if (!net->ipv6.devconf_all->use_optimistic && !idev->cnf.use_optimistic) +if (!READ_ONCE (net->ipv6.devconf_all->use_optimistic) && + !READ_ONCE (idev->cnf.use_optimistic))return false;return true;
@@ -1509,13 +1511,14 @@ static bool ipv6_use_optimistic_addr(struct net *net,#endif } -static bool ipv6_allow_optimistic_dad (struct net *net, -struct inet6_dev *idev) +static bool ipv6_allow_optimistic_dad (const struct net *net, +const struct inet6_dev *idev) {#ifdef CONFIG_IPV6_OPTIMISTIC_DAD if (!idev)return false; -if (!net->ipv6.devconf_all->optimistic_dad && !idev->cnf.optimistic_dad) +if (!READ_ONCE (net->ipv6.devconf_all->optimistic_dad) && + !READ_ONCE (idev->cnf.optimistic_dad))return false;return true;
@@ -1797,7 +1800,7 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, idev =__in6_dev_get (dst_dev);if ((dst_type &IPV6_ADDR_MULTICAST ) || dst.scope <=IPV6_ADDR_SCOPE_LINKLOCAL || - (idev && idev->cnf.use_oif_addrs_only)) { + (idev &&READ_ONCE (idev->cnf.use_oif_addrs_only))) { use_oif_addr = true; } }
@@ -2628,8 +2631,8 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, };#ifdef CONFIG_IPV6_OPTIMISTIC_DAD -if ((net->ipv6.devconf_all->optimistic_dad || - in6_dev->cnf.optimistic_dad) && +if ((READ_ONCE (net->ipv6.devconf_all->optimistic_dad) || +READ_ONCE (in6_dev->cnf.optimistic_dad)) && !net->ipv6.devconf_all->forwarding && sllao) cfg.ifa_flags |=IFA_F_OPTIMISTIC ;#endif
@@ -3226,8 +3229,8 @@ void addrconf_add_linklocal(struct inet6_dev *idev,struct inet6_ifaddr *ifp;#ifdef CONFIG_IPV6_OPTIMISTIC_DAD -if ((dev_net (idev->dev)->ipv6.devconf_all->optimistic_dad || - idev->cnf.optimistic_dad) && +if ((READ_ONCE (dev_net (idev->dev)->ipv6.devconf_all->optimistic_dad) || +READ_ONCE (idev->cnf.optimistic_dad)) && !dev_net (idev->dev)->ipv6.devconf_all->forwarding) cfg.ifa_flags |=IFA_F_OPTIMISTIC ;#endif
@@ -3784,10 +3787,10 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister)*/ if (!unregister && !idev->cnf.disable_ipv6) {/* aggregate the system setting and interface setting */ -int _keep_addr = net->ipv6.devconf_all->keep_addr_on_down; +int _keep_addr =READ_ONCE (net->ipv6.devconf_all->keep_addr_on_down);if (!_keep_addr) - _keep_addr = idev->cnf.keep_addr_on_down; + _keep_addr =READ_ONCE (idev->cnf.keep_addr_on_down); keep_addr = (_keep_addr >0 ); }
@@ -4009,8 +4012,8 @@ static void addrconf_dad_begin(struct inet6_ifaddr *ifp) net =dev_net (dev);if (dev->flags&(IFF_NOARP |IFF_LOOPBACK ) || - (net->ipv6.devconf_all->accept_dad <1 && - idev->cnf.accept_dad <1 ) || + (READ_ONCE (net->ipv6.devconf_all->accept_dad) <1 && +READ_ONCE (idev->cnf.accept_dad) <1 ) || !(ifp->flags&IFA_F_TENTATIVE ) || ifp->flags &IFA_F_NODAD ) {bool send_na = false;
@@ -4102,8 +4105,8 @@ static void addrconf_dad_work(struct work_struct *w) action =DAD_ABORT ; ifp->state =INET6_IFADDR_STATE_POSTDAD ; -if ((dev_net (idev->dev)->ipv6.devconf_all->accept_dad >1 || - idev->cnf.accept_dad >1 ) && +if ((READ_ONCE (dev_net (idev->dev)->ipv6.devconf_all->accept_dad) >1 || +READ_ONCE (idev->cnf.accept_dad) >1 ) && !idev->cnf.disable_ipv6 && !(ifp->flags &IFA_F_STABLE_PRIVACY )) {struct in6_addr addr;
@@ -4241,8 +4244,8 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id,/* send unsolicited NA if enabled */ if (send_na && - (ifp->idev->cnf.ndisc_notify || -dev_net (dev)->ipv6.devconf_all->ndisc_notify)) { + (READ_ONCE (ifp->idev->cnf.ndisc_notify) || +READ_ONCE (dev_net (dev)->ipv6.devconf_all->ndisc_notify))) { ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifp->addr, /*router=*/ !!ifp->idev->cnf.forwarding, /*solicited=*/ false, /*override=*/ true,
@@ -6389,7 +6392,7 @@ static int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write, }else if (&net->ipv6.devconf_all->addr_gen_mode == ctl->data) {struct net_device *dev; - net->ipv6.devconf_dflt->addr_gen_mode = new_val; +WRITE_ONCE (net->ipv6.devconf_dflt->addr_gen_mode, new_val);for_each_netdev (net, dev) { idev =__in6_dev_get (dev); if (idev &&
@@ -6400,7 +6403,7 @@ static int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write, } } - *((u32 *)ctl->data) = new_val; +WRITE_ONCE (*((u32 *)ctl->data), new_val); } out:
net/ipv6/exthdrs.c +10 -10
@@ -368,9 +368,8 @@ static int ipv6_srh_rcv(struct sk_buff *skb)return -1 ; } - accept_seg6 = net->ipv6.devconf_all->seg6_enabled; -if (accept_seg6 > idev->cnf.seg6_enabled) - accept_seg6 = idev->cnf.seg6_enabled; + accept_seg6 =min (READ_ONCE (net->ipv6.devconf_all->seg6_enabled), +READ_ONCE (idev->cnf.seg6_enabled));if (!accept_seg6) {kfree_skb (skb);
@@ -491,9 +490,8 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) idev =__in6_dev_get (skb->dev); - accept_rpl_seg = net->ipv6.devconf_all->rpl_seg_enabled; -if (accept_rpl_seg > idev->cnf.rpl_seg_enabled) - accept_rpl_seg = idev->cnf.rpl_seg_enabled; + accept_rpl_seg =min (READ_ONCE (net->ipv6.devconf_all->rpl_seg_enabled), +READ_ONCE (idev->cnf.rpl_seg_enabled));if (!accept_rpl_seg) {kfree_skb (skb);
@@ -674,11 +672,13 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)struct ipv6_rt_hdr *hdr;struct rt0_hdr *rthdr;struct net *net =dev_net (skb->dev); -int accept_source_route = net->ipv6.devconf_all->accept_source_route; +int accept_source_route; - idev =__in6_dev_get (skb->dev); -if (idev && accept_source_route > idev->cnf.accept_source_route) - accept_source_route = idev->cnf.accept_source_route; + accept_source_route =READ_ONCE (net->ipv6.devconf_all->accept_source_route); + +if (idev) + accept_source_route =min (accept_source_route, +READ_ONCE (idev->cnf.accept_source_route));if (!pskb_may_pull (skb,skb_transport_offset (skb) +8 ) || !pskb_may_pull(skb, (skb_transport_offset(skb) +
net/ipv6/ip6_input.c +1 -1
@@ -229,7 +229,7 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,if (!ipv6_addr_is_multicast (&hdr->daddr) && (skb->pkt_type ==PACKET_BROADCAST || skb->pkt_type ==PACKET_MULTICAST ) && - idev->cnf.drop_unicast_in_l2_multicast) +READ_ONCE (idev->cnf.drop_unicast_in_l2_multicast)) goto err;/* RFC4291 2.7
net/ipv6/mcast.c +7 -7
@@ -162,9 +162,9 @@ static int unsolicited_report_interval(struct inet6_dev *idev)int iv;if (mld_in_v1_mode (idev)) - iv = idev->cnf.mldv1_unsolicited_report_interval; + iv =READ_ONCE (idev->cnf.mldv1_unsolicited_report_interval);else - iv = idev->cnf.mldv2_unsolicited_report_interval; + iv =READ_ONCE (idev->cnf.mldv2_unsolicited_report_interval);return iv >0 ? iv :1 ; }
@@ -1218,15 +1218,15 @@ static bool mld_marksources(struct ifmcaddr6 *pmc, int nsrcs,static int mld_force_mld_version (const struct inet6_dev *idev) { +const struct net *net =dev_net (idev->dev); +int all_force; + + all_force =READ_ONCE (net->ipv6.devconf_all->force_mld_version);/* Normally, both are 0 here. If enforcement to a particular is * being used, individual device enforcement will have a lower * precedence over 'all' device (.../conf/all/force_mld_version). */ - -if (dev_net (idev->dev)->ipv6.devconf_all->force_mld_version !=0 ) -return dev_net (idev->dev)->ipv6.devconf_all->force_mld_version; -else -return idev->cnf.force_mld_version; +return all_force ?:READ_ONCE (idev->cnf.force_mld_version); }static bool mld_in_v2_mode_only (const struct inet6_dev *idev)
net/ipv6/ndisc.c +6 -6
@@ -450,7 +450,7 @@ static void ip6_nd_hdr(struct sk_buff *skb,rcu_read_lock (); idev =__in6_dev_get (skb->dev); - tclass = idev ? idev->cnf.ndisc_tclass :0 ; + tclass = idev ?READ_ONCE (idev->cnf.ndisc_tclass) :0 ;rcu_read_unlock ();skb_push (skb,sizeof (*hdr));
@@ -538,7 +538,7 @@ void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, src_addr = solicited_addr;if (ifp->flags &IFA_F_OPTIMISTIC ) override = false; - inc_opt |= ifp->idev->cnf.force_tllao; + inc_opt |=READ_ONCE (ifp->idev->cnf.force_tllao);in6_ifa_put (ifp); }else { if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
@@ -991,7 +991,7 @@ static void ndisc_recv_na(struct sk_buff *skb)* and thus should not be accepted. */ if (!msg->icmph.icmp6_solicited && idev && - idev->cnf.drop_unsolicited_na) +READ_ONCE (idev->cnf.drop_unsolicited_na))return ;if (!ndisc_parse_options (dev, msg->opt, ndoptlen, &ndopts)) {
@@ -1737,7 +1737,7 @@ static bool ndisc_suppress_frag_ndisc(struct sk_buff *skb)if (!idev)return true;if (IP6CB (skb)->flags &IP6SKB_FRAGMENTED && - idev->cnf.suppress_frag_ndisc) { +READ_ONCE (idev->cnf.suppress_frag_ndisc)) {net_warn_ratelimited ("Received fragmented ndisc packet. Carefully consider disabling suppress_frag_ndisc.\n" );return true; }
@@ -1812,8 +1812,8 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, idev =in6_dev_get (dev);if (!idev)break ; -if (idev->cnf.ndisc_notify || - net->ipv6.devconf_all->ndisc_notify) +if (READ_ONCE (idev->cnf.ndisc_notify) || +READ_ONCE (net->ipv6.devconf_all->ndisc_notify))ndisc_send_unsol_na (dev);in6_dev_put (idev);break ;
net/ipv6/seg6_hmac.c +5 -3
@@ -243,6 +243,7 @@ bool seg6_hmac_validate_skb(struct sk_buff *skb)struct sr6_tlv_hmac *tlv;struct ipv6_sr_hdr *srh;struct inet6_dev *idev; +int require_hmac; idev =__in6_dev_get (skb->dev);if (!idev)
@@ -252,16 +253,17 @@ bool seg6_hmac_validate_skb(struct sk_buff *skb) tlv =seg6_get_tlv_hmac (srh); + require_hmac =READ_ONCE (idev->cnf.seg6_require_hmac);/* mandatory check but no tlv */ -if (idev->cnf.seg6_require_hmac >0 && !tlv) +if (require_hmac >0 && !tlv)return false;/* no check */ -if (idev->cnf.seg6_require_hmac <0 ) +if (require_hmac <0 )return true;/* check only if present */ -if (idev->cnf.seg6_require_hmac ==0 && !tlv) +if (require_hmac ==0 && !tlv)return true;/* now, seg6_require_hmac >= 0 && tlv */
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 ipv6: rpl: fix NULL dereference of idev in ipv6_rpl_srh_rcv()
From: Andrea Mayer <[email protected]>
[ Upstream commit f826df95332c07380206dbd54178b6eefb311aba ]
ipv6_rpl_srh_rcv() dereferences idev from __in6_dev_get() without a NULL check when reading idev->cnf.rpl_seg_enabled.
When the device's MTU drops below IPV6_MIN_MTU, addrconf_ifdown() clears dev->ip6_ptr through RCU_INIT_POINTER(). A packet that passed the idev check in ip6_rcv_core() can then reach ipv6_rpl_srh_rcv() with dev->ip6_ptr already NULL.
Reproduced by flooding the receiving interface with ping6 traffic while flapping its MTU between 1500 and 1200:
BUG: KASAN: null-ptr-deref in ipv6_rpl_srh_rcv+0xb3/0x1070 Read of size 4 at addr 00000000000006b4 by task ping6/394 CPU: 2 UID: 0 PID: 394 Comm: ping6 Not tainted 7.2.0-rc7-micro-vm-dev-00095-g24ef02f934ee #240 PREEMPT(full) Call Trace: <IRQ> kasan_report+0xc6/0x100 ipv6_rpl_srh_rcv+0xb3/0x1070 ip6_protocol_deliver_rcu+0x759/0x9a0 ip6_input_finish+0xa8/0x1b0 ip6_input+0xe1/0x490 ipv6_rcv+0x33d/0x460 __netif_receive_skb_one_core+0xd6/0x130 process_backlog+0x2cc/0xa00 __napi_poll.constprop.0+0x56/0x270 net_rx_action+0x327/0x730 handle_softirqs+0x11e/0x630 do_softirq+0xb3/0xf0 </IRQ>
Both ipv6_rpl_srh_rcv() and ipv6_srh_rcv() are called only from ipv6_rthdr_rcv(), which already has an idev lookup.
Fix the NULL dereference on the RPL path by checking idev in ipv6_rthdr_rcv(), before it calls either function. The callees take idev as an argument and no longer call __in6_dev_get(), so the packet is now dropped in one place, with SKB_DROP_REASON_IPV6DISABLED on both paths.
--- net/ipv6/exthdrs.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-)
net/ipv6/exthdrs.c +12 -14
@@ -351,23 +351,16 @@ static void seg6_update_csum(struct sk_buff *skb) (__be32 *)addr); } -static int ipv6_srh_rcv (struct sk_buff *skb) +static int ipv6_srh_rcv (struct sk_buff *skb,struct inet6_dev *idev) {struct inet6_skb_parm *opt =IP6CB (skb);struct net *net =dev_net (skb->dev);struct ipv6_sr_hdr *hdr; -struct inet6_dev *idev;struct in6_addr *addr;int accept_seg6; hdr = (struct ipv6_sr_hdr *)skb_transport_header (skb); - idev =__in6_dev_get (skb->dev); -if (!idev) { -kfree_skb (skb); -return -1 ; - } - accept_seg6 =min (READ_ONCE (net->ipv6.devconf_all->seg6_enabled),READ_ONCE (idev->cnf.seg6_enabled));
@@ -473,12 +466,11 @@ static int ipv6_srh_rcv(struct sk_buff *skb)return -1 ; } -static int ipv6_rpl_srh_rcv (struct sk_buff *skb) +static int ipv6_rpl_srh_rcv (struct sk_buff *skb,struct inet6_dev *idev) {struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr;struct inet6_skb_parm *opt =IP6CB (skb);struct net *net =dev_net (skb->dev); -struct inet6_dev *idev;struct ipv6hdr *oldhdr;struct in6_addr addr;unsigned int chdr_len;
@@ -488,8 +480,6 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)u64 n =0 ;u32 r; - idev =__in6_dev_get (skb->dev); - accept_rpl_seg =min (READ_ONCE (net->ipv6.devconf_all->rpl_seg_enabled),READ_ONCE (idev->cnf.rpl_seg_enabled));
@@ -700,10 +690,14 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)switch (hdr->type) {case IPV6_SRCRT_TYPE_4 :/* segment routing */ -return ipv6_srh_rcv (skb); +if (!idev) + goto disabled; +return ipv6_srh_rcv (skb, idev);case IPV6_SRCRT_TYPE_3 :/* rpl segment routing */ -return ipv6_rpl_srh_rcv (skb); +if (!idev) + goto disabled; +return ipv6_rpl_srh_rcv (skb, idev);default :break ; }
@@ -851,6 +845,10 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)icmpv6_param_prob (skb,ICMPV6_HDR_FIELD , (&hdr->type) -skb_network_header (skb));return -1 ; + +disabled: +kfree_skb (skb); +return -1 ; }static const struct inet6_protocol rthdr_protocol = {
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 1/2 ipv6/addrconf: annotate data-races around devconf fields (II)
In reply to [email protected]From: Eric Dumazet <[email protected]>
[ Upstream commit 2f0ff05a44302c91af54a5f9efe1b65b7681540e ]
Final (?) round of this series.
Annotate lockless reads on following devconf fields, because they be changed concurrently from /proc/net/ipv6/conf.
- accept_dad
- optimistic_dad
- use_optimistic
- use_oif_addrs_only
- ra_honor_pio_life
- keep_addr_on_down
- ndisc_notify
- ndisc_evict_nocarrier
- suppress_frag_ndisc
- addr_gen_mode
- seg6_enabled
- ioam6_enabled
- ioam6_id
- ioam6_id_wide
- drop_unicast_in_l2_multicast
- mldv[12]_unsolicited_report_interval
- force_mld_version
- force_tllao
- accept_untracked_na
- drop_unsolicited_na
- accept_source_route
Backport notes for Linux 5.10:
Keep only the routing-header annotations in net/ipv6/exthdrs.c. The addrconf, input, multicast, neighbour-discovery, HMAC and IOAM hunks are not needed by f826df95332c07380206dbd54178b6eefb311aba. In particular, this tree lacks IOAM and several newer devconf fields and drop reasons.
Also annotate rpl_seg_enabled with READ_ONCE() and min(), and use swap() for the existing RPL address exchange, eliminating its temporary address. These changes supply the exact RPL context expected by the target without adding functions or importing unrelated receive-path refactors. Keep the existing SRv6 NULL check for the target to move to the common caller.
Remove the redundant second idev lookup in ipv6_rthdr_rcv() while adapting the accept_source_route annotation. Use the already acquired RCU pointer.
Provide a macro local to the routing-header section mapping kfree_skb_reason(skb, reason) to kfree_skb(skb). Linux 5.10 has no skb drop reason API; this permits the unchanged target to build while retaining this branch's existing packet-freeing semantics. No reason enum or new function is introduced.
[ sashal: Reduced backport -- upstream 2f0ff05a44302 touches 7 file(s), this
backport carries 1. Not backported here:
net/ipv6/addrconf.c
net/ipv6/ioam6.c
net/ipv6/ip6_input.c
net/ipv6/mcast.c
net/ipv6/ndisc.c
net/ipv6/seg6_hmac.c
This note is generated from the file lists only; see the resolution record for the reasoning. ]
Stable-dep-of: f826df95332c ("ipv6: rpl: fix NULL dereference of idev in ipv6_rpl_srh_rcv()")
--- net/ipv6/exthdrs.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-)
net/ipv6/exthdrs.c +16 -15
@@ -368,9 +368,8 @@ static int ipv6_srh_rcv(struct sk_buff *skb)return -1 ; } - accept_seg6 = net->ipv6.devconf_all->seg6_enabled; -if (accept_seg6 > idev->cnf.seg6_enabled) - accept_seg6 = idev->cnf.seg6_enabled; + accept_seg6 =min (READ_ONCE (net->ipv6.devconf_all->seg6_enabled), +READ_ONCE (idev->cnf.seg6_enabled));if (!accept_seg6) {kfree_skb (skb);
@@ -481,7 +480,6 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)struct net *net =dev_net (skb->dev);struct inet6_dev *idev;struct ipv6hdr *oldhdr; -struct in6_addr addr;unsigned int chdr_len;unsigned char *buf;int accept_rpl_seg;
@@ -491,10 +489,8 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) idev =__in6_dev_get (skb->dev); - accept_rpl_seg = net->ipv6.devconf_all->rpl_seg_enabled; -if (accept_rpl_seg > idev->cnf.rpl_seg_enabled) - accept_rpl_seg = idev->cnf.rpl_seg_enabled; - + accept_rpl_seg =min (READ_ONCE (net->ipv6.devconf_all->rpl_seg_enabled), +READ_ONCE (idev->cnf.rpl_seg_enabled));if (!accept_rpl_seg) {kfree_skb (skb);return -1 ;
@@ -593,9 +589,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)return -1 ; } - addr =ipv6_hdr (skb)->daddr; -ipv6_hdr (skb)->daddr = ohdr->rpl_segaddr[i]; - ohdr->rpl_segaddr[i] = addr; +swap (ipv6_hdr (skb)->daddr, ohdr->rpl_segaddr[i]);ipv6_rpl_srh_compress (chdr, ohdr, &ipv6_hdr (skb)->daddr, n);
@@ -663,6 +657,9 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)Routing header. ********************************/ +/* This stable tree has no skb drop-reason accounting. */ +#define kfree_skb_reason (skb, reason) kfree_skb(skb) +/* called with rcu_read_lock() */ static int ipv6_rthdr_rcv (struct sk_buff *skb) {
@@ -674,11 +671,13 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)struct ipv6_rt_hdr *hdr;struct rt0_hdr *rthdr;struct net *net =dev_net (skb->dev); -int accept_source_route = net->ipv6.devconf_all->accept_source_route; +int accept_source_route; - idev =__in6_dev_get (skb->dev); -if (idev && accept_source_route > idev->cnf.accept_source_route) - accept_source_route = idev->cnf.accept_source_route; + accept_source_route =READ_ONCE (net->ipv6.devconf_all->accept_source_route); + +if (idev) + accept_source_route =min (accept_source_route, +READ_ONCE (idev->cnf.accept_source_route));if (!pskb_may_pull (skb,skb_transport_offset (skb) +8 ) || !pskb_may_pull(skb, (skb_transport_offset(skb) +
@@ -858,6 +857,8 @@ static const struct inet6_protocol rthdr_protocol = { .flags = INET6_PROTO_NOPOLICY, }; +#undef kfree_skb_reason +static const struct inet6_protocol destopt_protocol = { .handler = ipv6_destopt_rcv, .flags =INET6_PROTO_NOPOLICY ,
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/2 ipv6: rpl: fix NULL dereference of idev in ipv6_rpl_srh_rcv()
From: Andrea Mayer <[email protected]>
[ Upstream commit f826df95332c07380206dbd54178b6eefb311aba ]
ipv6_rpl_srh_rcv() dereferences idev from __in6_dev_get() without a NULL check when reading idev->cnf.rpl_seg_enabled.
When the device's MTU drops below IPV6_MIN_MTU, addrconf_ifdown() clears dev->ip6_ptr through RCU_INIT_POINTER(). A packet that passed the idev check in ip6_rcv_core() can then reach ipv6_rpl_srh_rcv() with dev->ip6_ptr already NULL.
Reproduced by flooding the receiving interface with ping6 traffic while flapping its MTU between 1500 and 1200:
BUG: KASAN: null-ptr-deref in ipv6_rpl_srh_rcv+0xb3/0x1070 Read of size 4 at addr 00000000000006b4 by task ping6/394 CPU: 2 UID: 0 PID: 394 Comm: ping6 Not tainted 7.2.0-rc7-micro-vm-dev-00095-g24ef02f934ee #240 PREEMPT(full) Call Trace: <IRQ> kasan_report+0xc6/0x100 ipv6_rpl_srh_rcv+0xb3/0x1070 ip6_protocol_deliver_rcu+0x759/0x9a0 ip6_input_finish+0xa8/0x1b0 ip6_input+0xe1/0x490 ipv6_rcv+0x33d/0x460 __netif_receive_skb_one_core+0xd6/0x130 process_backlog+0x2cc/0xa00 __napi_poll.constprop.0+0x56/0x270 net_rx_action+0x327/0x730 handle_softirqs+0x11e/0x630 do_softirq+0xb3/0xf0 </IRQ>
Both ipv6_rpl_srh_rcv() and ipv6_srh_rcv() are called only from ipv6_rthdr_rcv(), which already has an idev lookup.
Fix the NULL dereference on the RPL path by checking idev in ipv6_rthdr_rcv(), before it calls either function. The callees take idev as an argument and no longer call __in6_dev_get(), so the packet is now dropped in one place, with SKB_DROP_REASON_IPV6DISABLED on both paths.
--- net/ipv6/exthdrs.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-)
net/ipv6/exthdrs.c +12 -14
@@ -351,23 +351,16 @@ static void seg6_update_csum(struct sk_buff *skb) (__be32 *)addr); } -static int ipv6_srh_rcv (struct sk_buff *skb) +static int ipv6_srh_rcv (struct sk_buff *skb,struct inet6_dev *idev) {struct inet6_skb_parm *opt =IP6CB (skb);struct net *net =dev_net (skb->dev);struct ipv6_sr_hdr *hdr; -struct inet6_dev *idev;struct in6_addr *addr;int accept_seg6; hdr = (struct ipv6_sr_hdr *)skb_transport_header (skb); - idev =__in6_dev_get (skb->dev); -if (!idev) { -kfree_skb (skb); -return -1 ; - } - accept_seg6 =min (READ_ONCE (net->ipv6.devconf_all->seg6_enabled),READ_ONCE (idev->cnf.seg6_enabled));
@@ -473,12 +466,11 @@ static int ipv6_srh_rcv(struct sk_buff *skb)return -1 ; } -static int ipv6_rpl_srh_rcv (struct sk_buff *skb) +static int ipv6_rpl_srh_rcv (struct sk_buff *skb,struct inet6_dev *idev) {struct ipv6_rpl_sr_hdr *hdr, *ohdr, *chdr;struct inet6_skb_parm *opt =IP6CB (skb);struct net *net =dev_net (skb->dev); -struct inet6_dev *idev;struct ipv6hdr *oldhdr;unsigned int chdr_len;unsigned char *buf;
@@ -487,8 +479,6 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)u64 n =0 ;u32 r; - idev =__in6_dev_get (skb->dev); - accept_rpl_seg =min (READ_ONCE (net->ipv6.devconf_all->rpl_seg_enabled),READ_ONCE (idev->cnf.rpl_seg_enabled));if (!accept_rpl_seg) {
@@ -699,10 +689,14 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)switch (hdr->type) {case IPV6_SRCRT_TYPE_4 :/* segment routing */ -return ipv6_srh_rcv (skb); +if (!idev) + goto disabled; +return ipv6_srh_rcv (skb, idev);case IPV6_SRCRT_TYPE_3 :/* rpl segment routing */ -return ipv6_rpl_srh_rcv (skb); +if (!idev) + goto disabled; +return ipv6_rpl_srh_rcv (skb, idev);default :break ; }
@@ -850,6 +844,10 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)icmpv6_param_prob (skb,ICMPV6_HDR_FIELD , (&hdr->type) -skb_network_header (skb));return -1 ; + +disabled: +kfree_skb_reason (skb,SKB_DROP_REASON_IPV6DISABLED ); +return -1 ; }static const struct inet6_protocol rthdr_protocol = {
Signature · 1 line
2.53.0