|
| 1 | +ip6mr: fix tables suspicious RCU usage |
| 2 | + |
| 3 | +jira LE-2522 |
| 4 | +Rebuild_History Non-Buildable kernel-5.14.0-503.29.1.el9_5 |
| 5 | +commit-author Paolo Abeni < [email protected]> |
| 6 | +commit f1553c9894b4dbeb10a2ab15ab1aa113b3b4047c |
| 7 | +Empty-Commit: Cherry-Pick Conflicts during history rebuild. |
| 8 | +Will be included in final tarball splat. Ref for failed cherry-pick at: |
| 9 | +ciq/ciq_backports/kernel-5.14.0-503.29.1.el9_5/f1553c98.failed |
| 10 | + |
| 11 | +Several places call ip6mr_get_table() with no RCU nor RTNL lock. |
| 12 | +Add RCU protection inside such helper and provide a lockless variant |
| 13 | +for the few callers that already acquired the relevant lock. |
| 14 | + |
| 15 | +Note that some users additionally reference the table outside the RCU |
| 16 | +lock. That is actually safe as the table deletion can happen only |
| 17 | +after all table accesses are completed. |
| 18 | + |
| 19 | +Fixes: e2d57766e674 ("net: Provide compat support for SIOCGETMIFCNT_IN6 and SIOCGETSGCNT_IN6.") |
| 20 | +Fixes: d7c31cbde4bc ("net: ip6mr: add RTM_GETROUTE netlink op") |
| 21 | + Reviewed-by: David Ahern < [email protected]> |
| 22 | + Signed-off-by: Paolo Abeni < [email protected]> |
| 23 | +(cherry picked from commit f1553c9894b4dbeb10a2ab15ab1aa113b3b4047c) |
| 24 | + Signed-off-by: Jonathan Maple < [email protected]> |
| 25 | + |
| 26 | +# Conflicts: |
| 27 | +# net/ipv6/ip6mr.c |
| 28 | +diff --cc net/ipv6/ip6mr.c |
| 29 | +index d690d9627206,4147890fe98f..000000000000 |
| 30 | +--- a/net/ipv6/ip6mr.c |
| 31 | ++++ b/net/ipv6/ip6mr.c |
| 32 | +@@@ -429,7 -446,6 +444,10 @@@ static void *ip6mr_vif_seq_start(struc |
| 33 | + |
| 34 | + iter->mrt = mrt; |
| 35 | + |
| 36 | +++<<<<<<< HEAD |
| 37 | + + read_lock(&mrt_lock); |
| 38 | +++======= |
| 39 | +++>>>>>>> f1553c9894b4 (ip6mr: fix tables suspicious RCU usage) |
| 40 | + return mr_vif_seq_start(seq, pos); |
| 41 | + } |
| 42 | + |
| 43 | +@@@ -2275,13 -2304,15 +2293,23 @@@ int ip6mr_get_route(struct net *net, st |
| 44 | + int err; |
| 45 | + struct mr_table *mrt; |
| 46 | + struct mfc6_cache *cache; |
| 47 | + - struct rt6_info *rt = dst_rt6_info(skb_dst(skb)); |
| 48 | + + struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); |
| 49 | + |
| 50 | +++<<<<<<< HEAD |
| 51 | + + mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); |
| 52 | + + if (!mrt) |
| 53 | + + return -ENOENT; |
| 54 | + + |
| 55 | + + read_lock(&mrt_lock); |
| 56 | +++======= |
| 57 | ++ rcu_read_lock(); |
| 58 | ++ mrt = __ip6mr_get_table(net, RT6_TABLE_DFLT); |
| 59 | ++ if (!mrt) { |
| 60 | ++ rcu_read_unlock(); |
| 61 | ++ return -ENOENT; |
| 62 | ++ } |
| 63 | ++ |
| 64 | +++>>>>>>> f1553c9894b4 (ip6mr: fix tables suspicious RCU usage) |
| 65 | + cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr); |
| 66 | + if (!cache && skb->dev) { |
| 67 | + int vif = ip6mr_find_vif(mrt, skb->dev); |
| 68 | +@@@ -2502,6 -2532,95 +2530,98 @@@ errout |
| 69 | + rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE_R, -ENOBUFS); |
| 70 | + } |
| 71 | + |
| 72 | +++<<<<<<< HEAD |
| 73 | +++======= |
| 74 | ++ static const struct nla_policy ip6mr_getroute_policy[RTA_MAX + 1] = { |
| 75 | ++ [RTA_SRC] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)), |
| 76 | ++ [RTA_DST] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)), |
| 77 | ++ [RTA_TABLE] = { .type = NLA_U32 }, |
| 78 | ++ }; |
| 79 | ++ |
| 80 | ++ static int ip6mr_rtm_valid_getroute_req(struct sk_buff *skb, |
| 81 | ++ const struct nlmsghdr *nlh, |
| 82 | ++ struct nlattr **tb, |
| 83 | ++ struct netlink_ext_ack *extack) |
| 84 | ++ { |
| 85 | ++ struct rtmsg *rtm; |
| 86 | ++ int err; |
| 87 | ++ |
| 88 | ++ err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, ip6mr_getroute_policy, |
| 89 | ++ extack); |
| 90 | ++ if (err) |
| 91 | ++ return err; |
| 92 | ++ |
| 93 | ++ rtm = nlmsg_data(nlh); |
| 94 | ++ if ((rtm->rtm_src_len && rtm->rtm_src_len != 128) || |
| 95 | ++ (rtm->rtm_dst_len && rtm->rtm_dst_len != 128) || |
| 96 | ++ rtm->rtm_tos || rtm->rtm_table || rtm->rtm_protocol || |
| 97 | ++ rtm->rtm_scope || rtm->rtm_type || rtm->rtm_flags) { |
| 98 | ++ NL_SET_ERR_MSG_MOD(extack, |
| 99 | ++ "Invalid values in header for multicast route get request"); |
| 100 | ++ return -EINVAL; |
| 101 | ++ } |
| 102 | ++ |
| 103 | ++ if ((tb[RTA_SRC] && !rtm->rtm_src_len) || |
| 104 | ++ (tb[RTA_DST] && !rtm->rtm_dst_len)) { |
| 105 | ++ NL_SET_ERR_MSG_MOD(extack, "rtm_src_len and rtm_dst_len must be 128 for IPv6"); |
| 106 | ++ return -EINVAL; |
| 107 | ++ } |
| 108 | ++ |
| 109 | ++ return 0; |
| 110 | ++ } |
| 111 | ++ |
| 112 | ++ static int ip6mr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, |
| 113 | ++ struct netlink_ext_ack *extack) |
| 114 | ++ { |
| 115 | ++ struct net *net = sock_net(in_skb->sk); |
| 116 | ++ struct in6_addr src = {}, grp = {}; |
| 117 | ++ struct nlattr *tb[RTA_MAX + 1]; |
| 118 | ++ struct mfc6_cache *cache; |
| 119 | ++ struct mr_table *mrt; |
| 120 | ++ struct sk_buff *skb; |
| 121 | ++ u32 tableid; |
| 122 | ++ int err; |
| 123 | ++ |
| 124 | ++ err = ip6mr_rtm_valid_getroute_req(in_skb, nlh, tb, extack); |
| 125 | ++ if (err < 0) |
| 126 | ++ return err; |
| 127 | ++ |
| 128 | ++ if (tb[RTA_SRC]) |
| 129 | ++ src = nla_get_in6_addr(tb[RTA_SRC]); |
| 130 | ++ if (tb[RTA_DST]) |
| 131 | ++ grp = nla_get_in6_addr(tb[RTA_DST]); |
| 132 | ++ tableid = nla_get_u32_default(tb[RTA_TABLE], 0); |
| 133 | ++ |
| 134 | ++ mrt = __ip6mr_get_table(net, tableid ?: RT_TABLE_DEFAULT); |
| 135 | ++ if (!mrt) { |
| 136 | ++ NL_SET_ERR_MSG_MOD(extack, "MR table does not exist"); |
| 137 | ++ return -ENOENT; |
| 138 | ++ } |
| 139 | ++ |
| 140 | ++ /* entries are added/deleted only under RTNL */ |
| 141 | ++ rcu_read_lock(); |
| 142 | ++ cache = ip6mr_cache_find(mrt, &src, &grp); |
| 143 | ++ rcu_read_unlock(); |
| 144 | ++ if (!cache) { |
| 145 | ++ NL_SET_ERR_MSG_MOD(extack, "MR cache entry not found"); |
| 146 | ++ return -ENOENT; |
| 147 | ++ } |
| 148 | ++ |
| 149 | ++ skb = nlmsg_new(mr6_msgsize(false, mrt->maxvif), GFP_KERNEL); |
| 150 | ++ if (!skb) |
| 151 | ++ return -ENOBUFS; |
| 152 | ++ |
| 153 | ++ err = ip6mr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid, |
| 154 | ++ nlh->nlmsg_seq, cache, RTM_NEWROUTE, 0); |
| 155 | ++ if (err < 0) { |
| 156 | ++ kfree_skb(skb); |
| 157 | ++ return err; |
| 158 | ++ } |
| 159 | ++ |
| 160 | ++ return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); |
| 161 | ++ } |
| 162 | ++ |
| 163 | +++>>>>>>> f1553c9894b4 (ip6mr: fix tables suspicious RCU usage) |
| 164 | + static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) |
| 165 | + { |
| 166 | + const struct nlmsghdr *nlh = cb->nlh; |
| 167 | +* Unmerged path net/ipv6/ip6mr.c |
0 commit comments