]>
Commit | Line | Data |
---|---|---|
38568109 GKH |
1 | From foo@baz Thu Mar 28 21:54:17 CET 2019 |
2 | From: Eric Dumazet <edumazet@google.com> | |
3 | Date: Thu, 14 Mar 2019 20:19:47 -0700 | |
4 | Subject: tun: properly test for IFF_UP | |
5 | ||
6 | From: Eric Dumazet <edumazet@google.com> | |
7 | ||
8 | [ Upstream commit 4477138fa0ae4e1b699786ef0600863ea6e6c61c ] | |
9 | ||
10 | Same reasons than the ones explained in commit 4179cb5a4c92 | |
11 | ("vxlan: test dev->flags & IFF_UP before calling netif_rx()") | |
12 | ||
13 | netif_rx_ni() or napi_gro_frags() must be called under a strict contract. | |
14 | ||
15 | At device dismantle phase, core networking clears IFF_UP | |
16 | and flush_all_backlogs() is called after rcu grace period | |
17 | to make sure no incoming packet might be in a cpu backlog | |
18 | and still referencing the device. | |
19 | ||
20 | A similar protocol is used for gro layer. | |
21 | ||
22 | Most drivers call netif_rx() from their interrupt handler, | |
23 | and since the interrupts are disabled at device dismantle, | |
24 | netif_rx() does not have to check dev->flags & IFF_UP | |
25 | ||
26 | Virtual drivers do not have this guarantee, and must | |
27 | therefore make the check themselves. | |
28 | ||
29 | Fixes: 1bd4978a88ac ("tun: honor IFF_UP in tun_get_user()") | |
30 | Signed-off-by: Eric Dumazet <edumazet@google.com> | |
31 | Reported-by: syzbot <syzkaller@googlegroups.com> | |
32 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
33 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
34 | --- | |
35 | drivers/net/tun.c | 15 +++++++++++---- | |
36 | 1 file changed, 11 insertions(+), 4 deletions(-) | |
37 | ||
38 | --- a/drivers/net/tun.c | |
39 | +++ b/drivers/net/tun.c | |
40 | @@ -1718,9 +1718,6 @@ static ssize_t tun_get_user(struct tun_s | |
41 | int skb_xdp = 1; | |
42 | bool frags = tun_napi_frags_enabled(tfile); | |
43 | ||
44 | - if (!(tun->dev->flags & IFF_UP)) | |
45 | - return -EIO; | |
46 | - | |
47 | if (!(tun->flags & IFF_NO_PI)) { | |
48 | if (len < sizeof(pi)) | |
49 | return -EINVAL; | |
50 | @@ -1822,6 +1819,8 @@ static ssize_t tun_get_user(struct tun_s | |
51 | err = skb_copy_datagram_from_iter(skb, 0, from, len); | |
52 | ||
53 | if (err) { | |
54 | + err = -EFAULT; | |
55 | +drop: | |
56 | this_cpu_inc(tun->pcpu_stats->rx_dropped); | |
57 | kfree_skb(skb); | |
58 | if (frags) { | |
59 | @@ -1829,7 +1828,7 @@ static ssize_t tun_get_user(struct tun_s | |
60 | mutex_unlock(&tfile->napi_mutex); | |
61 | } | |
62 | ||
63 | - return -EFAULT; | |
64 | + return err; | |
65 | } | |
66 | } | |
67 | ||
68 | @@ -1913,6 +1912,12 @@ static ssize_t tun_get_user(struct tun_s | |
69 | !tfile->detached) | |
70 | rxhash = __skb_get_hash_symmetric(skb); | |
71 | ||
72 | + rcu_read_lock(); | |
73 | + if (unlikely(!(tun->dev->flags & IFF_UP))) { | |
74 | + err = -EIO; | |
75 | + goto drop; | |
76 | + } | |
77 | + | |
78 | if (frags) { | |
79 | /* Exercise flow dissector code path. */ | |
80 | u32 headlen = eth_get_headlen(skb->data, skb_headlen(skb)); | |
81 | @@ -1920,6 +1925,7 @@ static ssize_t tun_get_user(struct tun_s | |
82 | if (unlikely(headlen > skb_headlen(skb))) { | |
83 | this_cpu_inc(tun->pcpu_stats->rx_dropped); | |
84 | napi_free_frags(&tfile->napi); | |
85 | + rcu_read_unlock(); | |
86 | mutex_unlock(&tfile->napi_mutex); | |
87 | WARN_ON(1); | |
88 | return -ENOMEM; | |
89 | @@ -1947,6 +1953,7 @@ static ssize_t tun_get_user(struct tun_s | |
90 | } else { | |
91 | netif_rx_ni(skb); | |
92 | } | |
93 | + rcu_read_unlock(); | |
94 | ||
95 | stats = get_cpu_ptr(tun->pcpu_stats); | |
96 | u64_stats_update_begin(&stats->syncp); |