aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2013-11-21 10:50:58 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-12-08 10:29:26 -0500
commit026bb4057a0473132bdfa910bd2089de1a7af3f6 (patch)
tree93c3ec3a9b118a322ea0b1f55acdb1997369fafc /net
parent1acb97aeff84a72999eeb9cbea523a0079c5ce6d (diff)
packet: fix use after free race in send path when dev is released
[ Upstream commit e40526cb20b5ee53419452e1f03d97092f144418 ] Salam reported a use after free bug in PF_PACKET that occurs when we're sending out frames on a socket bound device and suddenly the net device is being unregistered. It appears that commit 827d9780 introduced a possible race condition between {t,}packet_snd() and packet_notifier(). In the case of a bound socket, packet_notifier() can drop the last reference to the net_device and {t,}packet_snd() might end up suddenly sending a packet over a freed net_device. To avoid reverting 827d9780 and thus introducing a performance regression compared to the current state of things, we decided to hold a cached RCU protected pointer to the net device and maintain it on write side via bind spin_lock protected register_prot_hook() and __unregister_prot_hook() calls. In {t,}packet_snd() path, we access this pointer under rcu_read_lock through packet_cached_dev_get() that holds reference to the device to prevent it from being freed through packet_notifier() while we're in send path. This is okay to do as dev_put()/dev_hold() are per-cpu counters, so this should not be a performance issue. Also, the code simplifies a bit as we don't need need_rls_dev anymore. Fixes: 827d978037d7 ("af-packet: Use existing netdev reference for bound sockets.") Reported-by: Salam Noureddine <noureddine@aristanetworks.com> Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Signed-off-by: Salam Noureddine <noureddine@aristanetworks.com> Cc: Ben Greear <greearb@candelatech.com> Cc: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net')
-rw-r--r--net/packet/af_packet.c59
-rw-r--r--net/packet/internal.h1
2 files changed, 37 insertions, 23 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 92e2d0201f90..f0aa20c2bfbf 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -244,11 +244,15 @@ static void __fanout_link(struct sock *sk, struct packet_sock *po);
244static void register_prot_hook(struct sock *sk) 244static void register_prot_hook(struct sock *sk)
245{ 245{
246 struct packet_sock *po = pkt_sk(sk); 246 struct packet_sock *po = pkt_sk(sk);
247
247 if (!po->running) { 248 if (!po->running) {
248 if (po->fanout) 249 if (po->fanout) {
249 __fanout_link(sk, po); 250 __fanout_link(sk, po);
250 else 251 } else {
251 dev_add_pack(&po->prot_hook); 252 dev_add_pack(&po->prot_hook);
253 rcu_assign_pointer(po->cached_dev, po->prot_hook.dev);
254 }
255
252 sock_hold(sk); 256 sock_hold(sk);
253 po->running = 1; 257 po->running = 1;
254 } 258 }
@@ -266,10 +270,13 @@ static void __unregister_prot_hook(struct sock *sk, bool sync)
266 struct packet_sock *po = pkt_sk(sk); 270 struct packet_sock *po = pkt_sk(sk);
267 271
268 po->running = 0; 272 po->running = 0;
269 if (po->fanout) 273 if (po->fanout) {
270 __fanout_unlink(sk, po); 274 __fanout_unlink(sk, po);
271 else 275 } else {
272 __dev_remove_pack(&po->prot_hook); 276 __dev_remove_pack(&po->prot_hook);
277 RCU_INIT_POINTER(po->cached_dev, NULL);
278 }
279
273 __sock_put(sk); 280 __sock_put(sk);
274 281
275 if (sync) { 282 if (sync) {
@@ -2041,12 +2048,24 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
2041 return tp_len; 2048 return tp_len;
2042} 2049}
2043 2050
2051static struct net_device *packet_cached_dev_get(struct packet_sock *po)
2052{
2053 struct net_device *dev;
2054
2055 rcu_read_lock();
2056 dev = rcu_dereference(po->cached_dev);
2057 if (dev)
2058 dev_hold(dev);
2059 rcu_read_unlock();
2060
2061 return dev;
2062}
2063
2044static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) 2064static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
2045{ 2065{
2046 struct sk_buff *skb; 2066 struct sk_buff *skb;
2047 struct net_device *dev; 2067 struct net_device *dev;
2048 __be16 proto; 2068 __be16 proto;
2049 bool need_rls_dev = false;
2050 int err, reserve = 0; 2069 int err, reserve = 0;
2051 void *ph; 2070 void *ph;
2052 struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name; 2071 struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name;
@@ -2059,7 +2078,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
2059 mutex_lock(&po->pg_vec_lock); 2078 mutex_lock(&po->pg_vec_lock);
2060 2079
2061 if (saddr == NULL) { 2080 if (saddr == NULL) {
2062 dev = po->prot_hook.dev; 2081 dev = packet_cached_dev_get(po);
2063 proto = po->num; 2082 proto = po->num;
2064 addr = NULL; 2083 addr = NULL;
2065 } else { 2084 } else {
@@ -2073,19 +2092,17 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
2073 proto = saddr->sll_protocol; 2092 proto = saddr->sll_protocol;
2074 addr = saddr->sll_addr; 2093 addr = saddr->sll_addr;
2075 dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex); 2094 dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
2076 need_rls_dev = true;
2077 } 2095 }
2078 2096
2079 err = -ENXIO; 2097 err = -ENXIO;
2080 if (unlikely(dev == NULL)) 2098 if (unlikely(dev == NULL))
2081 goto out; 2099 goto out;
2082
2083 reserve = dev->hard_header_len;
2084
2085 err = -ENETDOWN; 2100 err = -ENETDOWN;
2086 if (unlikely(!(dev->flags & IFF_UP))) 2101 if (unlikely(!(dev->flags & IFF_UP)))
2087 goto out_put; 2102 goto out_put;
2088 2103
2104 reserve = dev->hard_header_len;
2105
2089 size_max = po->tx_ring.frame_size 2106 size_max = po->tx_ring.frame_size
2090 - (po->tp_hdrlen - sizeof(struct sockaddr_ll)); 2107 - (po->tp_hdrlen - sizeof(struct sockaddr_ll));
2091 2108
@@ -2162,8 +2179,7 @@ out_status:
2162 __packet_set_status(po, ph, status); 2179 __packet_set_status(po, ph, status);
2163 kfree_skb(skb); 2180 kfree_skb(skb);
2164out_put: 2181out_put:
2165 if (need_rls_dev) 2182 dev_put(dev);
2166 dev_put(dev);
2167out: 2183out:
2168 mutex_unlock(&po->pg_vec_lock); 2184 mutex_unlock(&po->pg_vec_lock);
2169 return err; 2185 return err;
@@ -2201,7 +2217,6 @@ static int packet_snd(struct socket *sock,
2201 struct sk_buff *skb; 2217 struct sk_buff *skb;
2202 struct net_device *dev; 2218 struct net_device *dev;
2203 __be16 proto; 2219 __be16 proto;
2204 bool need_rls_dev = false;
2205 unsigned char *addr; 2220 unsigned char *addr;
2206 int err, reserve = 0; 2221 int err, reserve = 0;
2207 struct virtio_net_hdr vnet_hdr = { 0 }; 2222 struct virtio_net_hdr vnet_hdr = { 0 };
@@ -2217,7 +2232,7 @@ static int packet_snd(struct socket *sock,
2217 */ 2232 */
2218 2233
2219 if (saddr == NULL) { 2234 if (saddr == NULL) {
2220 dev = po->prot_hook.dev; 2235 dev = packet_cached_dev_get(po);
2221 proto = po->num; 2236 proto = po->num;
2222 addr = NULL; 2237 addr = NULL;
2223 } else { 2238 } else {
@@ -2229,19 +2244,17 @@ static int packet_snd(struct socket *sock,
2229 proto = saddr->sll_protocol; 2244 proto = saddr->sll_protocol;
2230 addr = saddr->sll_addr; 2245 addr = saddr->sll_addr;
2231 dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex); 2246 dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex);
2232 need_rls_dev = true;
2233 } 2247 }
2234 2248
2235 err = -ENXIO; 2249 err = -ENXIO;
2236 if (dev == NULL) 2250 if (unlikely(dev == NULL))
2237 goto out_unlock; 2251 goto out_unlock;
2238 if (sock->type == SOCK_RAW)
2239 reserve = dev->hard_header_len;
2240
2241 err = -ENETDOWN; 2252 err = -ENETDOWN;
2242 if (!(dev->flags & IFF_UP)) 2253 if (unlikely(!(dev->flags & IFF_UP)))
2243 goto out_unlock; 2254 goto out_unlock;
2244 2255
2256 if (sock->type == SOCK_RAW)
2257 reserve = dev->hard_header_len;
2245 if (po->has_vnet_hdr) { 2258 if (po->has_vnet_hdr) {
2246 vnet_hdr_len = sizeof(vnet_hdr); 2259 vnet_hdr_len = sizeof(vnet_hdr);
2247 2260
@@ -2375,15 +2388,14 @@ static int packet_snd(struct socket *sock,
2375 if (err > 0 && (err = net_xmit_errno(err)) != 0) 2388 if (err > 0 && (err = net_xmit_errno(err)) != 0)
2376 goto out_unlock; 2389 goto out_unlock;
2377 2390
2378 if (need_rls_dev) 2391 dev_put(dev);
2379 dev_put(dev);
2380 2392
2381 return len; 2393 return len;
2382 2394
2383out_free: 2395out_free:
2384 kfree_skb(skb); 2396 kfree_skb(skb);
2385out_unlock: 2397out_unlock:
2386 if (dev && need_rls_dev) 2398 if (dev)
2387 dev_put(dev); 2399 dev_put(dev);
2388out: 2400out:
2389 return err; 2401 return err;
@@ -2603,6 +2615,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol,
2603 po = pkt_sk(sk); 2615 po = pkt_sk(sk);
2604 sk->sk_family = PF_PACKET; 2616 sk->sk_family = PF_PACKET;
2605 po->num = proto; 2617 po->num = proto;
2618 RCU_INIT_POINTER(po->cached_dev, NULL);
2606 2619
2607 sk->sk_destruct = packet_sock_destruct; 2620 sk->sk_destruct = packet_sock_destruct;
2608 sk_refcnt_debug_inc(sk); 2621 sk_refcnt_debug_inc(sk);
diff --git a/net/packet/internal.h b/net/packet/internal.h
index c4e4b4561207..1035fa2d909c 100644
--- a/net/packet/internal.h
+++ b/net/packet/internal.h
@@ -113,6 +113,7 @@ struct packet_sock {
113 unsigned int tp_loss:1; 113 unsigned int tp_loss:1;
114 unsigned int tp_tx_has_off:1; 114 unsigned int tp_tx_has_off:1;
115 unsigned int tp_tstamp; 115 unsigned int tp_tstamp;
116 struct net_device __rcu *cached_dev;
116 struct packet_type prot_hook ____cacheline_aligned_in_smp; 117 struct packet_type prot_hook ____cacheline_aligned_in_smp;
117}; 118};
118 119