summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authoranton <anton@cvs.openbsd.org>2019-08-26 18:56:30 +0000
committeranton <anton@cvs.openbsd.org>2019-08-26 18:56:30 +0000
commit70a2f7d18ba0761a8e7fe20bb76d527f0e263eec (patch)
tree09aef6c3db24477402629fc3652e5898f9f4e1f9 /sys/kern
parent55cc054a0127bed4a8be071ce5ebbede999753dc (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.c5
-rw-r--r--sys/kern/vfs_vnops.c20
-rw-r--r--sys/kern/vfs_vops.c14
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));
}