On 2019/8/15 17:35, Eric Dumazet wrote:

On 8/15/19 10:18 AM, Yang Yingliang wrote:
I got a UAF repport in tun driver when doing fuzzy test:


[  466.368604] page:ffffea000dc84e00 refcount:1 mapcount:0 
mapping:ffff8883df1b4f00 index:0x0 compound_mapcount: 0
[  466.371582] flags: 0x2fffff80010200(slab|head)
[  466.372910] raw: 002fffff80010200 dead000000000100 dead000000000122 
ffff8883df1b4f00
[  466.375209] raw: 0000000000000000 0000000000070007 00000001ffffffff 
0000000000000000
[  466.377778] page dumped because: kasan: bad access detected
[  466.379730]
[  466.380288] Memory state around the buggy address:
[  466.381844]  ffff888372139100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 
fb
[  466.384009]  ffff888372139180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 
fb
[  466.386131] >ffff888372139200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 
fb
[  466.388257]                                                  ^
[  466.390234]  ffff888372139280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 
fb
[  466.392512]  ffff888372139300: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb 
fb
[  466.394667] 
==================================================================

tun_chr_read_iter() accessed the memory which freed by free_netdev()
called by tun_set_iff():

        CPUA                            CPUB
     tun_set_iff()
       alloc_netdev_mqs()
       tun_attach()
                                    tun_chr_read_iter()
                                      tun_get()
       register_netdevice()
       tun_detach_all()
         synchronize_net()
                                      tun_do_read()
                                        tun_ring_recv()
                                          schedule()
       free_netdev()
                                      tun_put() <-- UAF
UAF on what exactly ? The dev_hold() should prevent the free_netdev().

register_netdevice() is failed, so the dev is freed directly in free_netdev
().


Set a new bit in tun->flag if register_netdevice() successed,
without this bit, tun_get() returns NULL to avoid using a
freed tun pointer.

Fixes: eb0fb363f920 ("tuntap: attach queue 0 before registering netdevice")
Reported-by: Hulk Robot <hul...@huawei.com>
Signed-off-by: Yang Yingliang <yangyingli...@huawei.com>
---
  drivers/net/tun.c | 10 ++++++++--
  1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index db16d7a13e00..cbd60c276c40 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -115,6 +115,7 @@ do {                                                        
        \
  /* High bits in flags field are unused. */
  #define TUN_VNET_LE     0x80000000
  #define TUN_VNET_BE     0x40000000
+#define TUN_DEV_REGISTERED     0x20000000
#define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \
                      IFF_MULTI_QUEUE | IFF_NAPI | IFF_NAPI_FRAGS)
@@ -719,8 +720,10 @@ static void __tun_detach(struct tun_file *tfile, bool 
clean)
                        netif_carrier_off(tun->dev);
if (!(tun->flags & IFF_PERSIST) &&
-                           tun->dev->reg_state == NETREG_REGISTERED)
+                           tun->dev->reg_state == NETREG_REGISTERED) {
                                unregister_netdevice(tun->dev);
+                               tun->flags &= ~TUN_DEV_REGISTERED;
Isn't this done too late ?

+                       }
                }
                if (tun)
                        xdp_rxq_info_unreg(&tfile->xdp_rxq);
@@ -884,8 +887,10 @@ static struct tun_struct *tun_get(struct tun_file *tfile)
rcu_read_lock();
        tun = rcu_dereference(tfile->tun);
-       if (tun)
+       if (tun && (tun->flags & TUN_DEV_REGISTERED))
                dev_hold(tun->dev);
+       else
+               tun = NULL;
        rcu_read_unlock();
return tun;
@@ -2836,6 +2841,7 @@ static int tun_set_iff(struct net *net, struct file 
*file, struct ifreq *ifr)
                err = register_netdevice(tun->dev);
                if (err < 0)
                        goto err_detach;
+               tun->flags |= TUN_DEV_REGISTERED;
        }
netif_carrier_on(tun->dev);


So tun_get() will return NULL as long as  tun_set_iff() (TUNSETIFF ioctl()) has 
not yet been called ?

This could break some applications, since tun_get() is used from poll() and 
other syscalls.
I will try Wang's sugguestion later, if it's OK, I will drop this patch.

.



Reply via email to