Skip to content

Commit 1170957

Browse files
edumazetkuba-moo
authored andcommitted
ipv6: use RCU in ip6_output()
Use RCU in ip6_output() in order to use dst_dev_rcu() to prevent possible UAF. We can remove rcu_read_lock()/rcu_read_unlock() pairs from ip6_finish_output2(). Fixes: 4a6ce2b ("net: introduce a new function dst_dev_put()") Signed-off-by: Eric Dumazet <[email protected]> Reviewed-by: David Ahern <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 9085e56 commit 1170957

File tree

1 file changed

+15
-14
lines changed

1 file changed

+15
-14
lines changed

net/ipv6/ip6_output.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
6161
{
6262
struct dst_entry *dst = skb_dst(skb);
63-
struct net_device *dev = dst_dev(dst);
63+
struct net_device *dev = dst_dev_rcu(dst);
6464
struct inet6_dev *idev = ip6_dst_idev(dst);
6565
unsigned int hh_len = LL_RESERVED_SPACE(dev);
6666
const struct in6_addr *daddr, *nexthop;
@@ -70,15 +70,12 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
7070

7171
/* Be paranoid, rather than too clever. */
7272
if (unlikely(hh_len > skb_headroom(skb)) && dev->header_ops) {
73-
/* Make sure idev stays alive */
74-
rcu_read_lock();
73+
/* idev stays alive because we hold rcu_read_lock(). */
7574
skb = skb_expand_head(skb, hh_len);
7675
if (!skb) {
7776
IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
78-
rcu_read_unlock();
7977
return -ENOMEM;
8078
}
81-
rcu_read_unlock();
8279
}
8380

8481
hdr = ipv6_hdr(skb);
@@ -123,23 +120,20 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
123120

124121
IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
125122

126-
rcu_read_lock();
127123
nexthop = rt6_nexthop(dst_rt6_info(dst), daddr);
128124
neigh = __ipv6_neigh_lookup_noref(dev, nexthop);
129125

130126
if (IS_ERR_OR_NULL(neigh)) {
131127
if (unlikely(!neigh))
132128
neigh = __neigh_create(&nd_tbl, nexthop, dev, false);
133129
if (IS_ERR(neigh)) {
134-
rcu_read_unlock();
135130
IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTNOROUTES);
136131
kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL);
137132
return -EINVAL;
138133
}
139134
}
140135
sock_confirm_neigh(skb, neigh);
141136
ret = neigh_output(neigh, skb, false);
142-
rcu_read_unlock();
143137
return ret;
144138
}
145139

@@ -233,22 +227,29 @@ static int ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *s
233227
int ip6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
234228
{
235229
struct dst_entry *dst = skb_dst(skb);
236-
struct net_device *dev = dst_dev(dst), *indev = skb->dev;
237-
struct inet6_dev *idev = ip6_dst_idev(dst);
230+
struct net_device *dev, *indev = skb->dev;
231+
struct inet6_dev *idev;
232+
int ret;
238233

239234
skb->protocol = htons(ETH_P_IPV6);
235+
rcu_read_lock();
236+
dev = dst_dev_rcu(dst);
237+
idev = ip6_dst_idev(dst);
240238
skb->dev = dev;
241239

242240
if (unlikely(!idev || READ_ONCE(idev->cnf.disable_ipv6))) {
243241
IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
242+
rcu_read_unlock();
244243
kfree_skb_reason(skb, SKB_DROP_REASON_IPV6DISABLED);
245244
return 0;
246245
}
247246

248-
return NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
249-
net, sk, skb, indev, dev,
250-
ip6_finish_output,
251-
!(IP6CB(skb)->flags & IP6SKB_REROUTED));
247+
ret = NF_HOOK_COND(NFPROTO_IPV6, NF_INET_POST_ROUTING,
248+
net, sk, skb, indev, dev,
249+
ip6_finish_output,
250+
!(IP6CB(skb)->flags & IP6SKB_REROUTED));
251+
rcu_read_unlock();
252+
return ret;
252253
}
253254
EXPORT_SYMBOL(ip6_output);
254255

0 commit comments

Comments
 (0)