diff mbox series

[net,v2] ip_tunnel: fix use-after-free in ip_tunnel_lookup()

Message ID 20200616160153.8479-1-ap420073@gmail.com
State Superseded
Delegated to: David Miller
Headers show
Series [net,v2] ip_tunnel: fix use-after-free in ip_tunnel_lookup() | expand

Commit Message

Taehee Yoo June 16, 2020, 4:01 p.m. UTC
In the datapath, the ip_tunnel_lookup() is used and it internally uses
fallback tunnel device pointer, which is fb_tunnel_dev.
This pointer variable should be set to NULL when a fb interface is deleted.
But there is no routine to set fb_tunnel_dev pointer to NULL.
So, this pointer will be still used after interface is deleted and
it eventually results in the use-after-free problem.

Test commands:
    ip netns add A
    ip netns add B
    ip link add eth0 type veth peer name eth1
    ip link set eth0 netns A
    ip link set eth1 netns B

    ip netns exec A ip link set lo up
    ip netns exec A ip link set eth0 up
    ip netns exec A ip link add gre1 type gre local 10.0.0.1 \
	    remote 10.0.0.2
    ip netns exec A ip link set gre1 up
    ip netns exec A ip a a 10.0.100.1/24 dev gre1
    ip netns exec A ip a a 10.0.0.1/24 dev eth0

    ip netns exec B ip link set lo up
    ip netns exec B ip link set eth1 up
    ip netns exec B ip link add gre1 type gre local 10.0.0.2 \
	    remote 10.0.0.1
    ip netns exec B ip link set gre1 up
    ip netns exec B ip a a 10.0.100.2/24 dev gre1
    ip netns exec B ip a a 10.0.0.2/24 dev eth1
    ip netns exec A hping3 10.0.100.2 -2 --flood -d 60000 &
    ip netns del B

Splat looks like:
[  133.319668][    C3] BUG: KASAN: use-after-free in ip_tunnel_lookup+0x9d6/0xde0
[  133.343852][    C3] Read of size 4 at addr ffff8880b1701c84 by task hping3/1222
[  133.344724][    C3]
[  133.345002][    C3] CPU: 3 PID: 1222 Comm: hping3 Not tainted 5.7.0+ #591
[  133.345814][    C3] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[  133.373336][    C3] Call Trace:
[  133.374792][    C3]  <IRQ>
[  133.375205][    C3]  dump_stack+0x96/0xdb
[  133.375789][    C3]  print_address_description.constprop.6+0x2cc/0x450
[  133.376720][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
[  133.377431][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
[  133.378130][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
[  133.378851][    C3]  kasan_report+0x154/0x190
[  133.379494][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
[  133.380200][    C3]  ip_tunnel_lookup+0x9d6/0xde0
[  133.380894][    C3]  __ipgre_rcv+0x1ab/0xaa0 [ip_gre]
[  133.381630][    C3]  ? rcu_read_lock_sched_held+0xc0/0xc0
[  133.382429][    C3]  gre_rcv+0x304/0x1910 [ip_gre]
[ ... ]

Suggested-by: Eric Dumazet <eric.dumazet@gmail.com>
Fixes: c54419321455 ("GRE: Refactor GRE tunneling code.")
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
---

v1 -> v2:
 - Do not add a new variable.

 net/ipv4/ip_tunnel.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

Comments

Eric Dumazet June 16, 2020, 4:16 p.m. UTC | #1
On 6/16/20 9:01 AM, Taehee Yoo wrote:
> In the datapath, the ip_tunnel_lookup() is used and it internally uses
> fallback tunnel device pointer, which is fb_tunnel_dev.
> This pointer variable should be set to NULL when a fb interface is deleted.
> But there is no routine to set fb_tunnel_dev pointer to NULL.
> So, this pointer will be still used after interface is deleted and
> it eventually results in the use-after-free problem.
> 
> Test commands:
>     ip netns add A
>     ip netns add B
>     ip link add eth0 type veth peer name eth1
>     ip link set eth0 netns A
>     ip link set eth1 netns B
> 
>     ip netns exec A ip link set lo up
>     ip netns exec A ip link set eth0 up
>     ip netns exec A ip link add gre1 type gre local 10.0.0.1 \
> 	    remote 10.0.0.2
>     ip netns exec A ip link set gre1 up
>     ip netns exec A ip a a 10.0.100.1/24 dev gre1
>     ip netns exec A ip a a 10.0.0.1/24 dev eth0
> 
>     ip netns exec B ip link set lo up
>     ip netns exec B ip link set eth1 up
>     ip netns exec B ip link add gre1 type gre local 10.0.0.2 \
> 	    remote 10.0.0.1
>     ip netns exec B ip link set gre1 up
>     ip netns exec B ip a a 10.0.100.2/24 dev gre1
>     ip netns exec B ip a a 10.0.0.2/24 dev eth1
>     ip netns exec A hping3 10.0.100.2 -2 --flood -d 60000 &
>     ip netns del B
> 
> Splat looks like:
> [  133.319668][    C3] BUG: KASAN: use-after-free in ip_tunnel_lookup+0x9d6/0xde0
> [  133.343852][    C3] Read of size 4 at addr ffff8880b1701c84 by task hping3/1222
> [  133.344724][    C3]
> [  133.345002][    C3] CPU: 3 PID: 1222 Comm: hping3 Not tainted 5.7.0+ #591
> [  133.345814][    C3] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
> [  133.373336][    C3] Call Trace:
> [  133.374792][    C3]  <IRQ>
> [  133.375205][    C3]  dump_stack+0x96/0xdb
> [  133.375789][    C3]  print_address_description.constprop.6+0x2cc/0x450
> [  133.376720][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
> [  133.377431][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
> [  133.378130][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
> [  133.378851][    C3]  kasan_report+0x154/0x190
> [  133.379494][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
> [  133.380200][    C3]  ip_tunnel_lookup+0x9d6/0xde0
> [  133.380894][    C3]  __ipgre_rcv+0x1ab/0xaa0 [ip_gre]
> [  133.381630][    C3]  ? rcu_read_lock_sched_held+0xc0/0xc0
> [  133.382429][    C3]  gre_rcv+0x304/0x1910 [ip_gre]
> [ ... ]
> 
> Suggested-by: Eric Dumazet <eric.dumazet@gmail.com>
> Fixes: c54419321455 ("GRE: Refactor GRE tunneling code.")
> Signed-off-by: Taehee Yoo <ap420073@gmail.com>
> ---
> 
> v1 -> v2:
>  - Do not add a new variable.
> 
>  net/ipv4/ip_tunnel.c | 13 ++++++++-----
>  1 file changed, 8 insertions(+), 5 deletions(-)
> 
> diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
> index f4f1d11eab50..701f150f11e1 100644
> --- a/net/ipv4/ip_tunnel.c
> +++ b/net/ipv4/ip_tunnel.c
> @@ -85,9 +85,10 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
>  				   __be32 remote, __be32 local,
>  				   __be32 key)
>  {
> -	unsigned int hash;
>  	struct ip_tunnel *t, *cand = NULL;
>  	struct hlist_head *head;
> +	struct net_device *ndev;
> +	unsigned int hash;
>  
>  	hash = ip_tunnel_hash(key, remote);
>  	head = &itn->tunnels[hash];
> @@ -162,8 +163,9 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
>  	if (t && t->dev->flags & IFF_UP)
>  		return t;
>  
> -	if (itn->fb_tunnel_dev && itn->fb_tunnel_dev->flags & IFF_UP)
> -		return netdev_priv(itn->fb_tunnel_dev);
> +	ndev = READ_ONCE(itn->fb_tunnel_dev);
> +	if (ndev && ndev->flags & IFF_UP)
> +		return netdev_priv(ndev);
>  
>  	return NULL;
>  }
> @@ -1260,8 +1262,9 @@ void ip_tunnel_uninit(struct net_device *dev)
>  
>  	itn = net_generic(net, tunnel->ip_tnl_net_id);
>  	/* fb_tunnel_dev will be unregisted in net-exit call. */

Is this comment still correct ?

> -	if (itn->fb_tunnel_dev != dev)
> -		ip_tunnel_del(itn, netdev_priv(dev));
> +	ip_tunnel_del(itn, netdev_priv(dev));
> +	if (itn->fb_tunnel_dev == dev)
> +		WRITE_ONCE(itn->fb_tunnel_dev, NULL);
>  
>  	dst_cache_reset(&tunnel->dst_cache);
>  }
>
Taehee Yoo June 16, 2020, 4:37 p.m. UTC | #2
On Wed, 17 Jun 2020 at 01:16, Eric Dumazet <eric.dumazet@gmail.com> wrote:
>

Hi Eric,
Thank you for the review :)

>
> On 6/16/20 9:01 AM, Taehee Yoo wrote:
> > In the datapath, the ip_tunnel_lookup() is used and it internally uses
> > fallback tunnel device pointer, which is fb_tunnel_dev.
> > This pointer variable should be set to NULL when a fb interface is deleted.
> > But there is no routine to set fb_tunnel_dev pointer to NULL.
> > So, this pointer will be still used after interface is deleted and
> > it eventually results in the use-after-free problem.
> >
> > Test commands:
> >     ip netns add A
> >     ip netns add B
> >     ip link add eth0 type veth peer name eth1
> >     ip link set eth0 netns A
> >     ip link set eth1 netns B
> >
> >     ip netns exec A ip link set lo up
> >     ip netns exec A ip link set eth0 up
> >     ip netns exec A ip link add gre1 type gre local 10.0.0.1 \
> >           remote 10.0.0.2
> >     ip netns exec A ip link set gre1 up
> >     ip netns exec A ip a a 10.0.100.1/24 dev gre1
> >     ip netns exec A ip a a 10.0.0.1/24 dev eth0
> >
> >     ip netns exec B ip link set lo up
> >     ip netns exec B ip link set eth1 up
> >     ip netns exec B ip link add gre1 type gre local 10.0.0.2 \
> >           remote 10.0.0.1
> >     ip netns exec B ip link set gre1 up
> >     ip netns exec B ip a a 10.0.100.2/24 dev gre1
> >     ip netns exec B ip a a 10.0.0.2/24 dev eth1
> >     ip netns exec A hping3 10.0.100.2 -2 --flood -d 60000 &
> >     ip netns del B
> >
> > Splat looks like:
> > [  133.319668][    C3] BUG: KASAN: use-after-free in ip_tunnel_lookup+0x9d6/0xde0
> > [  133.343852][    C3] Read of size 4 at addr ffff8880b1701c84 by task hping3/1222
> > [  133.344724][    C3]
> > [  133.345002][    C3] CPU: 3 PID: 1222 Comm: hping3 Not tainted 5.7.0+ #591
> > [  133.345814][    C3] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
> > [  133.373336][    C3] Call Trace:
> > [  133.374792][    C3]  <IRQ>
> > [  133.375205][    C3]  dump_stack+0x96/0xdb
> > [  133.375789][    C3]  print_address_description.constprop.6+0x2cc/0x450
> > [  133.376720][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
> > [  133.377431][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
> > [  133.378130][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
> > [  133.378851][    C3]  kasan_report+0x154/0x190
> > [  133.379494][    C3]  ? ip_tunnel_lookup+0x9d6/0xde0
> > [  133.380200][    C3]  ip_tunnel_lookup+0x9d6/0xde0
> > [  133.380894][    C3]  __ipgre_rcv+0x1ab/0xaa0 [ip_gre]
> > [  133.381630][    C3]  ? rcu_read_lock_sched_held+0xc0/0xc0
> > [  133.382429][    C3]  gre_rcv+0x304/0x1910 [ip_gre]
> > [ ... ]
> >
> > Suggested-by: Eric Dumazet <eric.dumazet@gmail.com>
> > Fixes: c54419321455 ("GRE: Refactor GRE tunneling code.")
> > Signed-off-by: Taehee Yoo <ap420073@gmail.com>
> > ---
> >
> > v1 -> v2:
> >  - Do not add a new variable.
> >
> >  net/ipv4/ip_tunnel.c | 13 ++++++++-----
> >  1 file changed, 8 insertions(+), 5 deletions(-)
> >
> > diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
> > index f4f1d11eab50..701f150f11e1 100644
> > --- a/net/ipv4/ip_tunnel.c
> > +++ b/net/ipv4/ip_tunnel.c
> > @@ -85,9 +85,10 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
> >                                  __be32 remote, __be32 local,
> >                                  __be32 key)
> >  {
> > -     unsigned int hash;
> >       struct ip_tunnel *t, *cand = NULL;
> >       struct hlist_head *head;
> > +     struct net_device *ndev;
> > +     unsigned int hash;
> >
> >       hash = ip_tunnel_hash(key, remote);
> >       head = &itn->tunnels[hash];
> > @@ -162,8 +163,9 @@ struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
> >       if (t && t->dev->flags & IFF_UP)
> >               return t;
> >
> > -     if (itn->fb_tunnel_dev && itn->fb_tunnel_dev->flags & IFF_UP)
> > -             return netdev_priv(itn->fb_tunnel_dev);
> > +     ndev = READ_ONCE(itn->fb_tunnel_dev);
> > +     if (ndev && ndev->flags & IFF_UP)
> > +             return netdev_priv(ndev);
> >
> >       return NULL;
> >  }
> > @@ -1260,8 +1262,9 @@ void ip_tunnel_uninit(struct net_device *dev)
> >
> >       itn = net_generic(net, tunnel->ip_tnl_net_id);
> >       /* fb_tunnel_dev will be unregisted in net-exit call. */
>
> Is this comment still correct ?
>

I think this comment is not valid anymore.
I will remove this comment in a v3 patch.

Thanks a lot!
Taehee Yoo
diff mbox series

Patch

diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index f4f1d11eab50..701f150f11e1 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -85,9 +85,10 @@  struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
 				   __be32 remote, __be32 local,
 				   __be32 key)
 {
-	unsigned int hash;
 	struct ip_tunnel *t, *cand = NULL;
 	struct hlist_head *head;
+	struct net_device *ndev;
+	unsigned int hash;
 
 	hash = ip_tunnel_hash(key, remote);
 	head = &itn->tunnels[hash];
@@ -162,8 +163,9 @@  struct ip_tunnel *ip_tunnel_lookup(struct ip_tunnel_net *itn,
 	if (t && t->dev->flags & IFF_UP)
 		return t;
 
-	if (itn->fb_tunnel_dev && itn->fb_tunnel_dev->flags & IFF_UP)
-		return netdev_priv(itn->fb_tunnel_dev);
+	ndev = READ_ONCE(itn->fb_tunnel_dev);
+	if (ndev && ndev->flags & IFF_UP)
+		return netdev_priv(ndev);
 
 	return NULL;
 }
@@ -1260,8 +1262,9 @@  void ip_tunnel_uninit(struct net_device *dev)
 
 	itn = net_generic(net, tunnel->ip_tnl_net_id);
 	/* fb_tunnel_dev will be unregisted in net-exit call. */
-	if (itn->fb_tunnel_dev != dev)
-		ip_tunnel_del(itn, netdev_priv(dev));
+	ip_tunnel_del(itn, netdev_priv(dev));
+	if (itn->fb_tunnel_dev == dev)
+		WRITE_ONCE(itn->fb_tunnel_dev, NULL);
 
 	dst_cache_reset(&tunnel->dst_cache);
 }