Skip to content

Commit 9085e56

Browse files
edumazetkuba-moo
authored andcommitted
ipv6: use RCU in ip6_xmit()
Use RCU in ip6_xmit() in order to use dst_dev_rcu() to prevent possible UAF. 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 b775ecf commit 9085e56

File tree

1 file changed

+21
-14
lines changed

1 file changed

+21
-14
lines changed

net/ipv6/ip6_output.c

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -268,35 +268,36 @@ bool ip6_autoflowlabel(struct net *net, const struct sock *sk)
268268
int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
269269
__u32 mark, struct ipv6_txoptions *opt, int tclass, u32 priority)
270270
{
271-
struct net *net = sock_net(sk);
272271
const struct ipv6_pinfo *np = inet6_sk(sk);
273272
struct in6_addr *first_hop = &fl6->daddr;
274273
struct dst_entry *dst = skb_dst(skb);
275-
struct net_device *dev = dst_dev(dst);
276274
struct inet6_dev *idev = ip6_dst_idev(dst);
277275
struct hop_jumbo_hdr *hop_jumbo;
278276
int hoplen = sizeof(*hop_jumbo);
277+
struct net *net = sock_net(sk);
279278
unsigned int head_room;
279+
struct net_device *dev;
280280
struct ipv6hdr *hdr;
281281
u8 proto = fl6->flowi6_proto;
282282
int seg_len = skb->len;
283-
int hlimit = -1;
283+
int ret, hlimit = -1;
284284
u32 mtu;
285285

286+
rcu_read_lock();
287+
288+
dev = dst_dev_rcu(dst);
286289
head_room = sizeof(struct ipv6hdr) + hoplen + LL_RESERVED_SPACE(dev);
287290
if (opt)
288291
head_room += opt->opt_nflen + opt->opt_flen;
289292

290293
if (unlikely(head_room > skb_headroom(skb))) {
291-
/* Make sure idev stays alive */
292-
rcu_read_lock();
294+
/* idev stays alive while we hold rcu_read_lock(). */
293295
skb = skb_expand_head(skb, head_room);
294296
if (!skb) {
295297
IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
296-
rcu_read_unlock();
297-
return -ENOBUFS;
298+
ret = -ENOBUFS;
299+
goto unlock;
298300
}
299-
rcu_read_unlock();
300301
}
301302

302303
if (opt) {
@@ -358,17 +359,21 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
358359
* skb to its handler for processing
359360
*/
360361
skb = l3mdev_ip6_out((struct sock *)sk, skb);
361-
if (unlikely(!skb))
362-
return 0;
362+
if (unlikely(!skb)) {
363+
ret = 0;
364+
goto unlock;
365+
}
363366

364367
/* hooks should never assume socket lock is held.
365368
* we promote our socket to non const
366369
*/
367-
return NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
368-
net, (struct sock *)sk, skb, NULL, dev,
369-
dst_output);
370+
ret = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
371+
net, (struct sock *)sk, skb, NULL, dev,
372+
dst_output);
373+
goto unlock;
370374
}
371375

376+
ret = -EMSGSIZE;
372377
skb->dev = dev;
373378
/* ipv6_local_error() does not require socket lock,
374379
* we promote our socket to non const
@@ -377,7 +382,9 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
377382

378383
IP6_INC_STATS(net, idev, IPSTATS_MIB_FRAGFAILS);
379384
kfree_skb(skb);
380-
return -EMSGSIZE;
385+
unlock:
386+
rcu_read_unlock();
387+
return ret;
381388
}
382389
EXPORT_SYMBOL(ip6_xmit);
383390

0 commit comments

Comments
 (0)