Page MenuHomeFreeBSD

witness: Record the first acquired file and line for recursable locks
ClosedPublic

Authored by zlei on Sep 12 2025, 8:09 AM.
Tags
None
Referenced Files
Unknown Object (File)
Wed, Oct 15, 2:22 AM
Unknown Object (File)
Wed, Oct 15, 1:19 AM
Unknown Object (File)
Sat, Oct 11, 5:24 AM
Unknown Object (File)
Fri, Oct 10, 11:33 PM
Unknown Object (File)
Fri, Oct 10, 4:26 PM
Unknown Object (File)
Fri, Oct 10, 4:26 PM
Unknown Object (File)
Fri, Oct 10, 4:26 PM
Unknown Object (File)
Fri, Oct 10, 4:26 PM
Subscribers

Details

Summary

and the last acquired file and line to witness object.

For recursable locks, unfortunately current implementation records only
the recurse count and the last acquired file and line, but does not
restore the previous acquired file and line on unlock. Hence it is
possible to report false acquired file and line, and that may mislead
developers and make the report by users harder to analyse.

Since subsequent recurse locks do not affect how witness order check,
record the first acquired file and line so that the logic is much clear.

Reported by: bz
See also: https://lists.freebsd.org/archives/freebsd-current/2025-June/007944.html
MFC after: 2 weeks

Test Plan

Test with hand crafted kernel module.

The Makefile and source file,

PACKAGE=witness
SRCS	= witness.c
KMOD	= witness

.include <bsd.kmod.mk>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/kernel.h>

#include <sys/lock.h>
#include <sys/sx.h>

static struct sx wit_sxlock;
SX_SYSINIT_FLAGS(wit_sx, &wit_sxlock, "witness_sx", SX_RECURSE);

static void f1(void);
static void f2(void);
static void f3(void);

static __noinline void
f1(void)
{
	sx_xlock(&wit_sxlock); /* line 21 */
	f2();
	sx_xunlock(&wit_sxlock);
}

static __noinline void
f2(void)
{
	sx_xlock(&wit_sxlock); /* line 29 */
	f3();
	sx_xunlock(&wit_sxlock);

	/* XXX trigger witness assert panic */
	sx_slock(&wit_sxlock); /* line 34 */
	sx_sunlock(&wit_sxlock);
}

static __noinline void
f3(void)
{
	sx_xlock(&wit_sxlock); /* line 41 */
	sx_xunlock(&wit_sxlock);
}



static int
module_load(module_t mod, int cmd, void *arg)
{
	int error;

	error = 0;
	switch (cmd) {
	case MOD_LOAD:
		f1();
		break;
	case MOD_UNLOAD:
		break;
	default:
		error = EOPNOTSUPP;
		break;
	}
	return (error);
}

static moduledata_t mod_data = {
	"witness",
	module_load,
	0
};

DECLARE_MODULE(witness, mod_data, SI_SUB_EXEC, SI_ORDER_ANY);

Before the fix, witness report

shared lock of (sx) witness_sx @ witness.c:34
while exclusively locked from witness.c:41
panic: excl->share

After the fix,

shared lock of (sx) witness_sx @ witness.c:34
while exclusively locked from witness.c:21
panic: excl->share

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

zlei requested review of this revision.Sep 12 2025, 8:09 AM

In the mailing list, @bz suggested to list the chain of the recursive lock. I think that is useful but requires some extra effort. I'm still working on it, will publish when ready.

This revision is now accepted and ready to land.Sep 12 2025, 11:36 AM
sys/kern/subr_witness.c
1531–1532

Presumably we should deduplicate these assignments and move them before the find_instance() call?

Refactored a little to avoid duplicating, as suggested by Mark.

This revision now requires review to proceed.Tue, Sep 16, 6:07 PM
zlei marked an inline comment as done.Tue, Sep 16, 6:08 PM
This revision is now accepted and ready to land.Tue, Sep 16, 9:17 PM