From 70a2f7d18ba0761a8e7fe20bb76d527f0e263eec Mon Sep 17 00:00:00 2001 From: anton Date: Mon, 26 Aug 2019 18:56:30 +0000 Subject: 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 --- sys/kern/vfs_subr.c | 5 ++++- sys/kern/vfs_vnops.c | 20 +++++++++++++++++--- sys/kern/vfs_vops.c | 14 +++++++++++++- 3 files changed, 34 insertions(+), 5 deletions(-) (limited to 'sys/kern') 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 * @@ -46,6 +46,7 @@ #include #include #include +#include /* LK_DRAIN */ #ifdef VFSLCKDEBUG #include /* 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)); } -- cgit v1.2.3