netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] tun: fix use-after-free when register netdev failed
@ 2019-08-15  8:18 Yang Yingliang
  2019-08-15  9:21 ` Jason Wang
  2019-08-15  9:35 ` Eric Dumazet
  0 siblings, 2 replies; 5+ messages in thread
From: Yang Yingliang @ 2019-08-15  8:18 UTC (permalink / raw)
  To: netdev; +Cc: jasowang, xiyou.wangcong, davem, yangyingliang

I got a UAF repport in tun driver when doing fuzzy test:

[  466.269490] ==================================================================
[  466.271792] BUG: KASAN: use-after-free in tun_chr_read_iter+0x2ca/0x2d0
[  466.271806] Read of size 8 at addr ffff888372139250 by task tun-test/2699
[  466.271810]
[  466.271824] CPU: 1 PID: 2699 Comm: tun-test Not tainted 5.3.0-rc1-00001-g5a9433db2614-dirty #427
[  466.271833] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014
[  466.271838] Call Trace:
[  466.271858]  dump_stack+0xca/0x13e
[  466.271871]  ? tun_chr_read_iter+0x2ca/0x2d0
[  466.271890]  print_address_description+0x79/0x440
[  466.271906]  ? vprintk_func+0x5e/0xf0
[  466.271920]  ? tun_chr_read_iter+0x2ca/0x2d0
[  466.271935]  __kasan_report+0x15c/0x1df
[  466.271958]  ? tun_chr_read_iter+0x2ca/0x2d0
[  466.271976]  kasan_report+0xe/0x20
[  466.271987]  tun_chr_read_iter+0x2ca/0x2d0
[  466.272013]  do_iter_readv_writev+0x4b7/0x740
[  466.272032]  ? default_llseek+0x2d0/0x2d0
[  466.272072]  do_iter_read+0x1c5/0x5e0
[  466.272110]  vfs_readv+0x108/0x180
[  466.299007]  ? compat_rw_copy_check_uvector+0x440/0x440
[  466.299020]  ? fsnotify+0x888/0xd50
[  466.299040]  ? __fsnotify_parent+0xd0/0x350
[  466.299064]  ? fsnotify_first_mark+0x1e0/0x1e0
[  466.304548]  ? vfs_write+0x264/0x510
[  466.304569]  ? ksys_write+0x101/0x210
[  466.304591]  ? do_preadv+0x116/0x1a0
[  466.304609]  do_preadv+0x116/0x1a0
[  466.309829]  do_syscall_64+0xc8/0x600
[  466.309849]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
[  466.309861] RIP: 0033:0x4560f9
[  466.309875] Code: 00 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48
[  466.309889] RSP: 002b:00007ffffa5166e8 EFLAGS: 00000206 ORIG_RAX: 0000000000000127
[  466.322992] RAX: ffffffffffffffda RBX: 0000000000400460 RCX: 00000000004560f9
[  466.322999] RDX: 0000000000000003 RSI: 00000000200008c0 RDI: 0000000000000003
[  466.323007] RBP: 00007ffffa516700 R08: 0000000000000004 R09: 0000000000000000
[  466.323014] R10: 0000000000000000 R11: 0000000000000206 R12: 000000000040cb10
[  466.323021] R13: 0000000000000000 R14: 00000000006d7018 R15: 0000000000000000
[  466.323057]
[  466.323064] Allocated by task 2605:
[  466.335165]  save_stack+0x19/0x80
[  466.336240]  __kasan_kmalloc.constprop.8+0xa0/0xd0
[  466.337755]  kmem_cache_alloc+0xe8/0x320
[  466.339050]  getname_flags+0xca/0x560
[  466.340229]  user_path_at_empty+0x2c/0x50
[  466.341508]  vfs_statx+0xe6/0x190
[  466.342619]  __do_sys_newstat+0x81/0x100
[  466.343908]  do_syscall_64+0xc8/0x600
[  466.345303]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
[  466.347034]
[  466.347517] Freed by task 2605:
[  466.348471]  save_stack+0x19/0x80
[  466.349476]  __kasan_slab_free+0x12e/0x180
[  466.350726]  kmem_cache_free+0xc8/0x430
[  466.351874]  putname+0xe2/0x120
[  466.352921]  filename_lookup+0x257/0x3e0
[  466.354319]  vfs_statx+0xe6/0x190
[  466.355498]  __do_sys_newstat+0x81/0x100
[  466.356889]  do_syscall_64+0xc8/0x600
[  466.358037]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
[  466.359567]
[  466.360050] The buggy address belongs to the object at ffff888372139100
[  466.360050]  which belongs to the cache names_cache of size 4096
[  466.363735] The buggy address is located 336 bytes inside of
[  466.363735]  4096-byte region [ffff888372139100, ffff88837213a100)
[  466.367179] The buggy address belongs to the page:
[  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

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 <hulkci@huawei.com>
Signed-off-by: Yang Yingliang <yangyingliang@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;
+			}
 		}
 		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);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] tun: fix use-after-free when register netdev failed
  2019-08-15  8:18 [PATCH] tun: fix use-after-free when register netdev failed Yang Yingliang
@ 2019-08-15  9:21 ` Jason Wang
  2019-08-15 12:55   ` Yang Yingliang
  2019-08-15  9:35 ` Eric Dumazet
  1 sibling, 1 reply; 5+ messages in thread
From: Jason Wang @ 2019-08-15  9:21 UTC (permalink / raw)
  To: Yang Yingliang, netdev; +Cc: xiyou.wangcong, davem


On 2019/8/15 下午4:18, Yang Yingliang wrote:
> I got a UAF repport in tun driver when doing fuzzy test:
>
> [  466.269490] ==================================================================
> [  466.271792] BUG: KASAN: use-after-free in tun_chr_read_iter+0x2ca/0x2d0
> [  466.271806] Read of size 8 at addr ffff888372139250 by task tun-test/2699
> [  466.271810]
> [  466.271824] CPU: 1 PID: 2699 Comm: tun-test Not tainted 5.3.0-rc1-00001-g5a9433db2614-dirty #427
> [  466.271833] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014
> [  466.271838] Call Trace:
> [  466.271858]  dump_stack+0xca/0x13e
> [  466.271871]  ? tun_chr_read_iter+0x2ca/0x2d0
> [  466.271890]  print_address_description+0x79/0x440
> [  466.271906]  ? vprintk_func+0x5e/0xf0
> [  466.271920]  ? tun_chr_read_iter+0x2ca/0x2d0
> [  466.271935]  __kasan_report+0x15c/0x1df
> [  466.271958]  ? tun_chr_read_iter+0x2ca/0x2d0
> [  466.271976]  kasan_report+0xe/0x20
> [  466.271987]  tun_chr_read_iter+0x2ca/0x2d0
> [  466.272013]  do_iter_readv_writev+0x4b7/0x740
> [  466.272032]  ? default_llseek+0x2d0/0x2d0
> [  466.272072]  do_iter_read+0x1c5/0x5e0
> [  466.272110]  vfs_readv+0x108/0x180
> [  466.299007]  ? compat_rw_copy_check_uvector+0x440/0x440
> [  466.299020]  ? fsnotify+0x888/0xd50
> [  466.299040]  ? __fsnotify_parent+0xd0/0x350
> [  466.299064]  ? fsnotify_first_mark+0x1e0/0x1e0
> [  466.304548]  ? vfs_write+0x264/0x510
> [  466.304569]  ? ksys_write+0x101/0x210
> [  466.304591]  ? do_preadv+0x116/0x1a0
> [  466.304609]  do_preadv+0x116/0x1a0
> [  466.309829]  do_syscall_64+0xc8/0x600
> [  466.309849]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
> [  466.309861] RIP: 0033:0x4560f9
> [  466.309875] Code: 00 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48
> [  466.309889] RSP: 002b:00007ffffa5166e8 EFLAGS: 00000206 ORIG_RAX: 0000000000000127
> [  466.322992] RAX: ffffffffffffffda RBX: 0000000000400460 RCX: 00000000004560f9
> [  466.322999] RDX: 0000000000000003 RSI: 00000000200008c0 RDI: 0000000000000003
> [  466.323007] RBP: 00007ffffa516700 R08: 0000000000000004 R09: 0000000000000000
> [  466.323014] R10: 0000000000000000 R11: 0000000000000206 R12: 000000000040cb10
> [  466.323021] R13: 0000000000000000 R14: 00000000006d7018 R15: 0000000000000000
> [  466.323057]
> [  466.323064] Allocated by task 2605:
> [  466.335165]  save_stack+0x19/0x80
> [  466.336240]  __kasan_kmalloc.constprop.8+0xa0/0xd0
> [  466.337755]  kmem_cache_alloc+0xe8/0x320
> [  466.339050]  getname_flags+0xca/0x560
> [  466.340229]  user_path_at_empty+0x2c/0x50
> [  466.341508]  vfs_statx+0xe6/0x190
> [  466.342619]  __do_sys_newstat+0x81/0x100
> [  466.343908]  do_syscall_64+0xc8/0x600
> [  466.345303]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
> [  466.347034]
> [  466.347517] Freed by task 2605:
> [  466.348471]  save_stack+0x19/0x80
> [  466.349476]  __kasan_slab_free+0x12e/0x180
> [  466.350726]  kmem_cache_free+0xc8/0x430
> [  466.351874]  putname+0xe2/0x120
> [  466.352921]  filename_lookup+0x257/0x3e0
> [  466.354319]  vfs_statx+0xe6/0x190
> [  466.355498]  __do_sys_newstat+0x81/0x100
> [  466.356889]  do_syscall_64+0xc8/0x600
> [  466.358037]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
> [  466.359567]
> [  466.360050] The buggy address belongs to the object at ffff888372139100
> [  466.360050]  which belongs to the cache names_cache of size 4096
> [  466.363735] The buggy address is located 336 bytes inside of
> [  466.363735]  4096-byte region [ffff888372139100, ffff88837213a100)
> [  466.367179] The buggy address belongs to the page:
> [  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
>
> 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.


Good catch.

Some comments inline.


>
> Fixes: eb0fb363f920 ("tuntap: attach queue 0 before registering netdevice")
> Reported-by: Hulk Robot <hulkci@huawei.com>
> Signed-off-by: Yang Yingliang <yangyingliang@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;
> +			}
>   		}
>   		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);


This looks just a duplicated of netdev->state? However it lacks 
sufficient synchronization like barriers or locks. How about:

- call tun_set_real_num_queues() before register_netdevice() this can 
have the same result as what  eb0fb363f920 did.
- move tun_attach() after register_netdevice() this makes sure we won't 
publish tfile->tun until we are sure at least one refcnt is held by 
register_netdevice()?

Thanks


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] tun: fix use-after-free when register netdev failed
  2019-08-15  8:18 [PATCH] tun: fix use-after-free when register netdev failed Yang Yingliang
  2019-08-15  9:21 ` Jason Wang
@ 2019-08-15  9:35 ` Eric Dumazet
  2019-08-15 13:01   ` Yang Yingliang
  1 sibling, 1 reply; 5+ messages in thread
From: Eric Dumazet @ 2019-08-15  9:35 UTC (permalink / raw)
  To: Yang Yingliang, netdev; +Cc: jasowang, xiyou.wangcong, davem



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().

> 
> 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 <hulkci@huawei.com>
> Signed-off-by: Yang Yingliang <yangyingliang@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.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] tun: fix use-after-free when register netdev failed
  2019-08-15  9:21 ` Jason Wang
@ 2019-08-15 12:55   ` Yang Yingliang
  0 siblings, 0 replies; 5+ messages in thread
From: Yang Yingliang @ 2019-08-15 12:55 UTC (permalink / raw)
  To: Jason Wang, netdev; +Cc: xiyou.wangcong, davem



On 2019/8/15 17:21, Jason Wang wrote:
>
> On 2019/8/15 下午4:18, Yang Yingliang wrote:
>> I got a UAF repport in tun driver when doing fuzzy test:
>>
>> [  466.269490] 
>> ==================================================================
>> [  466.271792] BUG: KASAN: use-after-free in 
>> tun_chr_read_iter+0x2ca/0x2d0
>> [  466.271806] Read of size 8 at addr ffff888372139250 by task 
>> tun-test/2699
>> [  466.271810]
>> [  466.271824] CPU: 1 PID: 2699 Comm: tun-test Not tainted 
>> 5.3.0-rc1-00001-g5a9433db2614-dirty #427
>> [  466.271833] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), 
>> BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014
>> [  466.271838] Call Trace:
>> [  466.271858]  dump_stack+0xca/0x13e
>> [  466.271871]  ? tun_chr_read_iter+0x2ca/0x2d0
>> [  466.271890]  print_address_description+0x79/0x440
>> [  466.271906]  ? vprintk_func+0x5e/0xf0
>> [  466.271920]  ? tun_chr_read_iter+0x2ca/0x2d0
>> [  466.271935]  __kasan_report+0x15c/0x1df
>> [  466.271958]  ? tun_chr_read_iter+0x2ca/0x2d0
>> [  466.271976]  kasan_report+0xe/0x20
>> [  466.271987]  tun_chr_read_iter+0x2ca/0x2d0
>> [  466.272013]  do_iter_readv_writev+0x4b7/0x740
>> [  466.272032]  ? default_llseek+0x2d0/0x2d0
>> [  466.272072]  do_iter_read+0x1c5/0x5e0
>> [  466.272110]  vfs_readv+0x108/0x180
>> [  466.299007]  ? compat_rw_copy_check_uvector+0x440/0x440
>> [  466.299020]  ? fsnotify+0x888/0xd50
>> [  466.299040]  ? __fsnotify_parent+0xd0/0x350
>> [  466.299064]  ? fsnotify_first_mark+0x1e0/0x1e0
>> [  466.304548]  ? vfs_write+0x264/0x510
>> [  466.304569]  ? ksys_write+0x101/0x210
>> [  466.304591]  ? do_preadv+0x116/0x1a0
>> [  466.304609]  do_preadv+0x116/0x1a0
>> [  466.309829]  do_syscall_64+0xc8/0x600
>> [  466.309849]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
>> [  466.309861] RIP: 0033:0x4560f9
>> [  466.309875] Code: 00 00 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 
>> 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 
>> 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 
>> 89 01 48
>> [  466.309889] RSP: 002b:00007ffffa5166e8 EFLAGS: 00000206 ORIG_RAX: 
>> 0000000000000127
>> [  466.322992] RAX: ffffffffffffffda RBX: 0000000000400460 RCX: 
>> 00000000004560f9
>> [  466.322999] RDX: 0000000000000003 RSI: 00000000200008c0 RDI: 
>> 0000000000000003
>> [  466.323007] RBP: 00007ffffa516700 R08: 0000000000000004 R09: 
>> 0000000000000000
>> [  466.323014] R10: 0000000000000000 R11: 0000000000000206 R12: 
>> 000000000040cb10
>> [  466.323021] R13: 0000000000000000 R14: 00000000006d7018 R15: 
>> 0000000000000000
>> [  466.323057]
>> [  466.323064] Allocated by task 2605:
>> [  466.335165]  save_stack+0x19/0x80
>> [  466.336240]  __kasan_kmalloc.constprop.8+0xa0/0xd0
>> [  466.337755]  kmem_cache_alloc+0xe8/0x320
>> [  466.339050]  getname_flags+0xca/0x560
>> [  466.340229]  user_path_at_empty+0x2c/0x50
>> [  466.341508]  vfs_statx+0xe6/0x190
>> [  466.342619]  __do_sys_newstat+0x81/0x100
>> [  466.343908]  do_syscall_64+0xc8/0x600
>> [  466.345303]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
>> [  466.347034]
>> [  466.347517] Freed by task 2605:
>> [  466.348471]  save_stack+0x19/0x80
>> [  466.349476]  __kasan_slab_free+0x12e/0x180
>> [  466.350726]  kmem_cache_free+0xc8/0x430
>> [  466.351874]  putname+0xe2/0x120
>> [  466.352921]  filename_lookup+0x257/0x3e0
>> [  466.354319]  vfs_statx+0xe6/0x190
>> [  466.355498]  __do_sys_newstat+0x81/0x100
>> [  466.356889]  do_syscall_64+0xc8/0x600
>> [  466.358037]  entry_SYSCALL_64_after_hwframe+0x49/0xbe
>> [  466.359567]
>> [  466.360050] The buggy address belongs to the object at 
>> ffff888372139100
>> [  466.360050]  which belongs to the cache names_cache of size 4096
>> [  466.363735] The buggy address is located 336 bytes inside of
>> [  466.363735]  4096-byte region [ffff888372139100, ffff88837213a100)
>> [  466.367179] The buggy address belongs to the page:
>> [  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
>>
>> 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.
>
>
> Good catch.
>
> Some comments inline.
>
>
>>
>> Fixes: eb0fb363f920 ("tuntap: attach queue 0 before registering 
>> netdevice")
>> Reported-by: Hulk Robot <hulkci@huawei.com>
>> Signed-off-by: Yang Yingliang <yangyingliang@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;
>> +            }
>>           }
>>           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);
>
>
> This looks just a duplicated of netdev->state? However it lacks 
> sufficient synchronization like barriers or locks. How about:
It's not same, register_netdevice() will return error if 
call_netdevice_notifiers() failed after dev->reg_state is set to 
NETREG_REGISTERED.
>
> - call tun_set_real_num_queues() before register_netdevice() this can 
> have the same result as what  eb0fb363f920 did.
> - move tun_attach() after register_netdevice() this makes sure we 
> won't publish tfile->tun until we are sure at least one refcnt is held 
> by register_netdevice()?
Yes, I think this way is better, I will try this later.
>
> Thanks
>
>
> .
>



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH] tun: fix use-after-free when register netdev failed
  2019-08-15  9:35 ` Eric Dumazet
@ 2019-08-15 13:01   ` Yang Yingliang
  0 siblings, 0 replies; 5+ messages in thread
From: Yang Yingliang @ 2019-08-15 13:01 UTC (permalink / raw)
  To: Eric Dumazet, netdev; +Cc: jasowang, xiyou.wangcong, davem, Yangyingliang



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 <hulkci@huawei.com>
>> Signed-off-by: Yang Yingliang <yangyingliang@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.
>
> .
>



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2019-08-15 13:12 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-15  8:18 [PATCH] tun: fix use-after-free when register netdev failed Yang Yingliang
2019-08-15  9:21 ` Jason Wang
2019-08-15 12:55   ` Yang Yingliang
2019-08-15  9:35 ` Eric Dumazet
2019-08-15 13:01   ` Yang Yingliang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).