diff options
author | kib <kib@FreeBSD.org> | 2012-01-01 18:45:59 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2012-01-01 18:45:59 +0000 |
commit | c3ff56243b9d716e74544bef440aa812f115faed (patch) | |
tree | 3be9dcc8513306b58cd8a6557f5f3ca233d27033 /sys/kern/vfs_lookup.c | |
parent | 1e9f485778ee7ae90cbcab204d6fd07a86a924c9 (diff) | |
download | FreeBSD-src-c3ff56243b9d716e74544bef440aa812f115faed.zip FreeBSD-src-c3ff56243b9d716e74544bef440aa812f115faed.tar.gz |
Avoid double-unlock or double unreference for ndp->ni_dvp when the vnode dp
lock upgrade right after the 'success' label fails.
In collaboration with: pho
MFC after: 1 week
Diffstat (limited to 'sys/kern/vfs_lookup.c')
-rw-r--r-- | sys/kern/vfs_lookup.c | 21 |
1 files changed, 15 insertions, 6 deletions
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index e2aad7c..29ad019 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -508,12 +508,14 @@ lookup(struct nameidata *ndp) int dvfslocked; /* VFS Giant state for parent */ int tvfslocked; int lkflags_save; + int ni_dvp_unlocked; /* * Setup: break out flag bits into variables. */ dvfslocked = (ndp->ni_cnd.cn_flags & GIANTHELD) != 0; vfslocked = 0; + ni_dvp_unlocked = 0; ndp->ni_cnd.cn_flags &= ~GIANTHELD; wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT); KASSERT(cnp->cn_nameiop == LOOKUP || wantparent, @@ -861,8 +863,10 @@ unionlookup: /* * Symlink code always expects an unlocked dvp. */ - if (ndp->ni_dvp != ndp->ni_vp) + if (ndp->ni_dvp != ndp->ni_vp) { VOP_UNLOCK(ndp->ni_dvp, 0); + ni_dvp_unlocked = 1; + } goto success; } @@ -909,14 +913,17 @@ nextname: VREF(ndp->ni_startdir); } if (!wantparent) { + ni_dvp_unlocked = 2; if (ndp->ni_dvp != dp) vput(ndp->ni_dvp); else vrele(ndp->ni_dvp); VFS_UNLOCK_GIANT(dvfslocked); dvfslocked = 0; - } else if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp != dp) + } else if ((cnp->cn_flags & LOCKPARENT) == 0 && ndp->ni_dvp != dp) { VOP_UNLOCK(ndp->ni_dvp, 0); + ni_dvp_unlocked = 1; + } if (cnp->cn_flags & AUDITVNODE1) AUDIT_ARG_VNODE1(dp); @@ -945,10 +952,12 @@ success: return (0); bad2: - if (dp != ndp->ni_dvp) - vput(ndp->ni_dvp); - else - vrele(ndp->ni_dvp); + if (ni_dvp_unlocked != 2) { + if (dp != ndp->ni_dvp && !ni_dvp_unlocked) + vput(ndp->ni_dvp); + else + vrele(ndp->ni_dvp); + } bad: if (!dpunlocked) vput(dp); |