From 2e57c8fde7335793b4d4da78a08946342403ba54 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Feb 2020 22:28:54 +0000 Subject: [PATCH] vfs: fix device count leak on vrele racing with vgone The race is: CPU1 CPU2 devfs_reclaim_vchr make v_usecount 0 VI_LOCK sees v_usecount == 0, no updates vp->v_rdev = NULL; ... VI_UNLOCK VI_LOCK v_decr_devcount sees v_rdev == NULL, no updates In this scenario si_devcount decrement is not performed. Note this can only happen if the vnode lock is not held. Reviewed by: kib Tested by: pho Differential Revision: https://reviews.freebsd.org/D23529 --- sys/kern/vfs_subr.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 70c8e777b00..71772f311a3 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -3192,12 +3192,22 @@ vputx(struct vnode *vp, enum vputx_op func) * count which provides liveness of the vnode, in which case we * have to vdrop. */ - if (!refcount_release(&vp->v_usecount)) { - if (func == VPUTX_VPUT) - VOP_UNLOCK(vp); - return; + if (__predict_false(vp->v_type == VCHR && func == VPUTX_VRELE)) { + if (refcount_release_if_not_last(&vp->v_usecount)) + return; + VI_LOCK(vp); + if (!refcount_release(&vp->v_usecount)) { + VI_UNLOCK(vp); + return; + } + } else { + if (!refcount_release(&vp->v_usecount)) { + if (func == VPUTX_VPUT) + VOP_UNLOCK(vp); + return; + } + VI_LOCK(vp); } - VI_LOCK(vp); v_decr_devcount(vp); /* * By the time we got here someone else might have transitioned