Re: memory leak in vhost_net_ioctl

From: Jason Wang
Date: Thu Jun 13 2019 - 11:57:10 EST



On 2019/6/6 äå10:40, Hillf Danton wrote:

On Wed, 05 Jun 2019 16:42:05 -0700 (PDT) syzbot wrote:
Hello,

syzbot found the following crash on:

HEAD commit:ÂÂÂ 788a0249 Merge tag 'arc-5.2-rc4' of git://git.kernel.org/p..
git tree:ÂÂÂÂÂÂ upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=15dc9ea6a00000
kernel config: https://syzkaller.appspot.com/x/.config?x=d5c73825cbdc7326
dashboard link: https://syzkaller.appspot.com/bug?extid=0789f0c7e45efd7bb643
compiler:ÂÂÂÂÂÂ gcc (GCC) 9.0.0 20181231 (experimental)
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=10b31761a00000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=124892c1a00000

IMPORTANT: if you fix the bug, please add the following tag to the commit:
Reported-by: syzbot+0789f0c7e45efd7bb643@xxxxxxxxxxxxxxxxxxxxxxxxx

udit: type=1400 audit(1559768703.229:36): avc: denied { map } for pid=7116 comm="syz-executor330" path="/root/syz-executor330334897" dev="sda1" ino=16461 scontext=unconfined_u:system_r:insmod_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=1
executing program
executing program
BUG: memory leak
unreferenced object 0xffff88812421fe40 (size 64):
ÂÂ comm "syz-executor330", pid 7117, jiffies 4294949245 (age 13.030s)
ÂÂ hex dump (first 32 bytes):
 01 00 00 00 20 69 6f 63 00 00 00 00 64 65 76 2f .... ioc....dev/
ÂÂÂÂ 50 fe 21 24 81 88 ff ff 50 fe 21 24 81 88 ff ff P.!$....P.!$....
ÂÂ backtrace:
ÂÂÂÂ [<00000000ae0c4ae0>] kmemleak_alloc_recursive include/linux/kmemleak.h:55 [inline]
ÂÂÂÂ [<00000000ae0c4ae0>] slab_post_alloc_hook mm/slab.h:439 [inline]
ÂÂÂÂ [<00000000ae0c4ae0>] slab_alloc mm/slab.c:3326 [inline]
ÂÂÂÂ [<00000000ae0c4ae0>] kmem_cache_alloc_trace+0x13d/0x280 mm/slab.c:3553
ÂÂÂÂ [<0000000079ebab38>] kmalloc include/linux/slab.h:547 [inline]
ÂÂÂÂ [<0000000079ebab38>] vhost_net_ubuf_alloc drivers/vhost/net.c:241 [inline]
ÂÂÂÂ [<0000000079ebab38>] vhost_net_set_backend drivers/vhost/net.c:1534 [inline]
ÂÂÂÂ [<0000000079ebab38>] vhost_net_ioctl+0xb43/0xc10 drivers/vhost/net.c:1716
ÂÂÂÂ [<000000009f6204a2>] vfs_ioctl fs/ioctl.c:46 [inline]
ÂÂÂÂ [<000000009f6204a2>] file_ioctl fs/ioctl.c:509 [inline]
ÂÂÂÂ [<000000009f6204a2>] do_vfs_ioctl+0x62a/0x810 fs/ioctl.c:696
ÂÂÂÂ [<00000000b45866de>] ksys_ioctl+0x86/0xb0 fs/ioctl.c:713
ÂÂÂÂ [<00000000dfb41eb8>] __do_sys_ioctl fs/ioctl.c:720 [inline]
ÂÂÂÂ [<00000000dfb41eb8>] __se_sys_ioctl fs/ioctl.c:718 [inline]
ÂÂÂÂ [<00000000dfb41eb8>] __x64_sys_ioctl+0x1e/0x30 fs/ioctl.c:718
ÂÂÂÂ [<0000000049c1f547>] do_syscall_64+0x76/0x1a0 arch/x86/entry/common.c:301
ÂÂÂÂ [<0000000029cc8ca7>] entry_SYSCALL_64_after_hwframe+0x44/0xa9

BUG: memory leak
unreferenced object 0xffff88812421fa80 (size 64):
ÂÂ comm "syz-executor330", pid 7130, jiffies 4294949755 (age 7.930s)
ÂÂ hex dump (first 32 bytes):
ÂÂÂÂ 01 00 00 00 01 00 00 00 00 00 00 00 2f 76 69 72 ............/vir
ÂÂÂÂ 90 fa 21 24 81 88 ff ff 90 fa 21 24 81 88 ff ff ..!$......!$....
ÂÂ backtrace:
ÂÂÂÂ [<00000000ae0c4ae0>] kmemleak_alloc_recursive include/linux/kmemleak.h:55 [inline]
ÂÂÂÂ [<00000000ae0c4ae0>] slab_post_alloc_hook mm/slab.h:439 [inline]
ÂÂÂÂ [<00000000ae0c4ae0>] slab_alloc mm/slab.c:3326 [inline]
ÂÂÂÂ [<00000000ae0c4ae0>] kmem_cache_alloc_trace+0x13d/0x280 mm/slab.c:3553
ÂÂÂÂ [<0000000079ebab38>] kmalloc include/linux/slab.h:547 [inline]
ÂÂÂÂ [<0000000079ebab38>] vhost_net_ubuf_alloc drivers/vhost/net.c:241 [inline]
ÂÂÂÂ [<0000000079ebab38>] vhost_net_set_backend drivers/vhost/net.c:1534 [inline]
ÂÂÂÂ [<0000000079ebab38>] vhost_net_ioctl+0xb43/0xc10 drivers/vhost/net.c:1716
ÂÂÂÂ [<000000009f6204a2>] vfs_ioctl fs/ioctl.c:46 [inline]
ÂÂÂÂ [<000000009f6204a2>] file_ioctl fs/ioctl.c:509 [inline]
ÂÂÂÂ [<000000009f6204a2>] do_vfs_ioctl+0x62a/0x810 fs/ioctl.c:696
ÂÂÂÂ [<00000000b45866de>] ksys_ioctl+0x86/0xb0 fs/ioctl.c:713
ÂÂÂÂ [<00000000dfb41eb8>] __do_sys_ioctl fs/ioctl.c:720 [inline]
ÂÂÂÂ [<00000000dfb41eb8>] __se_sys_ioctl fs/ioctl.c:718 [inline]
ÂÂÂÂ [<00000000dfb41eb8>] __x64_sys_ioctl+0x1e/0x30 fs/ioctl.c:718
ÂÂÂÂ [<0000000049c1f547>] do_syscall_64+0x76/0x1a0 arch/x86/entry/common.c:301
ÂÂÂÂ [<0000000029cc8ca7>] entry_SYSCALL_64_after_hwframe+0x44/0xa9



---
This bug is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@xxxxxxxxxxxxxxxxx

syzbot will keep track of this bug report. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
syzbot can test patches for this bug, for details see:
https://goo.gl/tpsmEJ#testing-patches

Ignore my noise if you have no interest seeing the syzbot report.

After commit c38e39c378f46f ("vhost-net: fix use-after-free in
vhost_net_flush") flush would no longer free ubuf, just wait until ubuf users
disappear instead.

The following diff, in hope that may perhaps help you handle the memory leak,
makes flush able to free ubuf in the path of file release.

Thanks
Hillf
---
drivers/vhost/net.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 3beb401..dcf20b6 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -141,6 +141,7 @@ struct vhost_net {
ÂÂÂÂunsigned tx_zcopy_err;
ÂÂÂÂ/* Flush in progress. Protected by tx vq lock. */
ÂÂÂÂbool tx_flush;
+ÂÂÂ bool ld;ÂÂÂ /* Last dinner */
ÂÂÂÂ/* Private page frag */
ÂÂÂÂstruct page_frag page_frag;
ÂÂÂÂ/* Refcount bias of page frag */
@@ -1283,6 +1284,7 @@ static int vhost_net_open(struct inode *inode, struct file *f)
ÂÂÂÂn = kvmalloc(sizeof *n, GFP_KERNEL | __GFP_RETRY_MAYFAIL);
ÂÂÂÂif (!n)
ÂÂÂÂÂÂÂ return -ENOMEM;
+ÂÂÂ n->ld = false;
ÂÂÂÂvqs = kmalloc_array(VHOST_NET_VQ_MAX, sizeof(*vqs), GFP_KERNEL);
ÂÂÂÂif (!vqs) {
ÂÂÂÂÂÂÂ kvfree(n);
@@ -1376,7 +1378,10 @@ static void vhost_net_flush(struct vhost_net *n)
ÂÂÂÂÂÂÂ n->tx_flush = true;
ÂÂÂÂÂÂÂ mutex_unlock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex);
ÂÂÂÂÂÂÂ /* Wait for all lower device DMAs done. */
- vhost_net_ubuf_put_and_wait(n->vqs[VHOST_NET_VQ_TX].ubufs);
+ÂÂÂÂÂÂÂ if (n->ld)
+ vhost_net_ubuf_put_wait_and_free(n->vqs[VHOST_NET_VQ_TX].ubufs);
+ÂÂÂÂÂÂÂ else
+ vhost_net_ubuf_put_and_wait(n->vqs[VHOST_NET_VQ_TX].ubufs);
ÂÂÂÂÂÂÂ mutex_lock(&n->vqs[VHOST_NET_VQ_TX].vq.mutex);
ÂÂÂÂÂÂÂ n->tx_flush = false;
atomic_set(&n->vqs[VHOST_NET_VQ_TX].ubufs->refcount, 1);
@@ -1403,6 +1408,7 @@ static int vhost_net_release(struct inode *inode, struct file *f)
ÂÂÂÂsynchronize_rcu();
ÂÂÂÂ/* We do an extra flush before freeing memory,
ÂÂÂÂ * since jobs can re-queue themselves. */
+ÂÂÂ n->ld = true;
ÂÂÂÂvhost_net_flush(n);
ÂÂÂÂkfree(n->vqs[VHOST_NET_VQ_RX].rxq.queue);
ÂÂÂÂkfree(n->vqs[VHOST_NET_VQ_TX].xdp);
--


This is basically a kfree(ubuf) after the second vhost_net_flush() in vhost_net_release().

Could you please post a formal patch?

Thanks