summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2008-10-28 12:08:36 +0000
committerkib <kib@FreeBSD.org>2008-10-28 12:08:36 +0000
commit86b5e61ab2f215486d5afad7de1d637bebd75dca (patch)
tree54c8262d09ac7c24f5e64ffba47cb0d5fdd18358
parent19fb7eaf9faed3abf045ff604c41ccb8dfc240af (diff)
downloadFreeBSD-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.c36
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;
OpenPOWER on IntegriCloud