diff options
author | anton <anton@cvs.openbsd.org> | 2019-08-26 18:56:30 +0000 |
---|---|---|
committer | anton <anton@cvs.openbsd.org> | 2019-08-26 18:56:30 +0000 |
commit | 70a2f7d18ba0761a8e7fe20bb76d527f0e263eec (patch) | |
tree | 09aef6c3db24477402629fc3652e5898f9f4e1f9 /sys/kern | |
parent | 55cc054a0127bed4a8be071ce5ebbede999753dc (diff) |
When a thread tries to exclusively lock a vnode, the same thread must
ensure that any other thread currently trying to acquire the underlying
vnode lock has observed that the same vnode is about to be exclusively
locked. Such threads must then sleep until the exclusive lock has been
released and then try to acquire the lock again. Otherwise, exclusive
access to the vnode cannot be guaranteed.
Thanks to naddy@ and visa@ for testing; ok visa@
Reported-by: syzbot+374d0e7e2400004957f7@syzkaller.appspotmail.com
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/vfs_subr.c | 5 | ||||
-rw-r--r-- | sys/kern/vfs_vnops.c | 20 | ||||
-rw-r--r-- | sys/kern/vfs_vops.c | 14 |
3 files changed, 34 insertions, 5 deletions
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index 5a67cd31b28..64de71d0680 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_subr.c,v 1.292 2019/07/25 01:43:21 cheloha Exp $ */ +/* $OpenBSD: vfs_subr.c,v 1.293 2019/08/26 18:56:29 anton Exp $ */ /* $NetBSD: vfs_subr.c,v 1.53 1996/04/22 01:39:13 christos Exp $ */ /* @@ -705,6 +705,9 @@ vputonfreelist(struct vnode *vp) if (vp->v_usecount != 0) panic("Use count is not zero!"); + if (vp->v_lockcount != 0) + panic("%s: lock count is not zero", __func__); + if (vp->v_bioflag & VBIOONFREELIST) { vprint("vnode already on free list: ", vp); panic("vnode already on free list"); diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index a2f481ce6b8..353fc7a3feb 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_vnops.c,v 1.106 2019/08/13 07:09:21 anton Exp $ */ +/* $OpenBSD: vfs_vnops.c,v 1.107 2019/08/26 18:56:29 anton Exp $ */ /* $NetBSD: vfs_vnops.c,v 1.20 1996/02/04 02:18:41 christos Exp $ */ /* @@ -558,9 +558,23 @@ vn_lock(struct vnode *vp, int flags) tsleep(vp, PINOD, "vn_lock", 0); error = ENOENT; } else { + vp->v_lockcount++; error = VOP_LOCK(vp, flags); - if (error == 0) - return (error); + vp->v_lockcount--; + if (error == 0) { + if ((vp->v_flag & VXLOCK) == 0) + return (0); + + /* + * The vnode was exclusively locked while + * acquiring the requested lock. Release it and + * try again. + */ + error = ENOENT; + VOP_UNLOCK(vp); + if (vp->v_lockcount == 0) + wakeup_one(&vp->v_lockcount); + } } } while (flags & LK_RETRY); return (error); diff --git a/sys/kern/vfs_vops.c b/sys/kern/vfs_vops.c index 9c39ab07705..e606c0b8af7 100644 --- a/sys/kern/vfs_vops.c +++ b/sys/kern/vfs_vops.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vfs_vops.c,v 1.21 2019/05/03 14:24:13 visa Exp $ */ +/* $OpenBSD: vfs_vops.c,v 1.22 2019/08/26 18:56:29 anton Exp $ */ /* * Copyright (c) 2010 Thordur I. Bjornsson <thib@openbsd.org> * @@ -46,6 +46,7 @@ #include <sys/vnode.h> #include <sys/unistd.h> #include <sys/systm.h> +#include <sys/lock.h> /* LK_DRAIN */ #ifdef VFSLCKDEBUG #include <sys/systm.h> /* for panic() */ @@ -599,6 +600,17 @@ VOP_LOCK(struct vnode *vp, int flags) if (vp->v_op->vop_lock == NULL) return (EOPNOTSUPP); + if ((flags & LK_DRAIN) && vp->v_lockcount > 0) { + /* + * Ensure that any thread currently waiting on the same lock has + * observed that the vnode is about to be exclusively locked + * before continuing. + */ + KASSERT(vp->v_flag & VXLOCK); + tsleep(&vp->v_lockcount, PINOD, "vop_lock", 0); + KASSERT(vp->v_lockcount == 0); + } + return ((vp->v_op->vop_lock)(&a)); } |