diff options
author | kib <kib@FreeBSD.org> | 2008-10-28 12:08:36 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2008-10-28 12:08:36 +0000 |
commit | 86b5e61ab2f215486d5afad7de1d637bebd75dca (patch) | |
tree | 54c8262d09ac7c24f5e64ffba47cb0d5fdd18358 | |
parent | 19fb7eaf9faed3abf045ff604c41ccb8dfc240af (diff) | |
download | FreeBSD-src-86b5e61ab2f215486d5afad7de1d637bebd75dca.zip FreeBSD-src-86b5e61ab2f215486d5afad7de1d637bebd75dca.tar.gz |
Protect check for v_pollinfo == NULL and assignment of the newly allocated
vpollinfo with vnode interlock. Fully initialize vpollinfo before putting
pointer to it into vp->v_pollinfo.
Discussed with: dwhite
Tested by: pho
MFC after: 1 week
-rw-r--r-- | sys/kern/vfs_subr.c | 36 |
1 files changed, 22 insertions, 14 deletions
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index c8b4d97..e2d5734 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -113,7 +113,7 @@ static void vgonel(struct vnode *); static void vfs_knllock(void *arg); static void vfs_knlunlock(void *arg); static int vfs_knllocked(void *arg); - +static void destroy_vpollinfo(struct vpollinfo *vi); /* * Enable Giant pushdown based on whether or not the vm is mpsafe in this @@ -819,11 +819,8 @@ vdestroy(struct vnode *vp) #ifdef MAC mac_vnode_destroy(vp); #endif - if (vp->v_pollinfo != NULL) { - knlist_destroy(&vp->v_pollinfo->vpi_selinfo.si_note); - mtx_destroy(&vp->v_pollinfo->vpi_lock); - uma_zfree(vnodepoll_zone, vp->v_pollinfo); - } + if (vp->v_pollinfo != NULL) + destroy_vpollinfo(vp->v_pollinfo); #ifdef INVARIANTS /* XXX Elsewhere we can detect an already freed vnode via NULL v_op. */ vp->v_op = NULL; @@ -3197,6 +3194,14 @@ vbusy(struct vnode *vp) mtx_unlock(&vnode_free_list_mtx); } +static void +destroy_vpollinfo(struct vpollinfo *vi) +{ + knlist_destroy(&vi->vpi_selinfo.si_note); + mtx_destroy(&vi->vpi_lock); + uma_zfree(vnodepoll_zone, vi); +} + /* * Initalize per-vnode helper structure to hold poll-related state. */ @@ -3205,15 +3210,20 @@ v_addpollinfo(struct vnode *vp) { struct vpollinfo *vi; + if (vp->v_pollinfo != NULL) + return; vi = uma_zalloc(vnodepoll_zone, M_WAITOK); + mtx_init(&vi->vpi_lock, "vnode pollinfo", NULL, MTX_DEF); + knlist_init(&vi->vpi_selinfo.si_note, vp, vfs_knllock, + vfs_knlunlock, vfs_knllocked); + VI_LOCK(vp); if (vp->v_pollinfo != NULL) { - uma_zfree(vnodepoll_zone, vi); + VI_UNLOCK(vp); + destroy_vpollinfo(vi); return; } vp->v_pollinfo = vi; - mtx_init(&vp->v_pollinfo->vpi_lock, "vnode pollinfo", NULL, MTX_DEF); - knlist_init(&vp->v_pollinfo->vpi_selinfo.si_note, vp, vfs_knllock, - vfs_knlunlock, vfs_knllocked); + VI_UNLOCK(vp); } /* @@ -3228,8 +3238,7 @@ int vn_pollrecord(struct vnode *vp, struct thread *td, int events) { - if (vp->v_pollinfo == NULL) - v_addpollinfo(vp); + v_addpollinfo(vp); mtx_lock(&vp->v_pollinfo->vpi_lock); if (vp->v_pollinfo->vpi_revents & events) { /* @@ -4065,8 +4074,7 @@ vfs_kqfilter(struct vop_kqfilter_args *ap) kn->kn_hook = (caddr_t)vp; - if (vp->v_pollinfo == NULL) - v_addpollinfo(vp); + v_addpollinfo(vp); if (vp->v_pollinfo == NULL) return (ENOMEM); knl = &vp->v_pollinfo->vpi_selinfo.si_note; |