Re: userret: returning with the following locks held
Date: Wed, 18 Mar 2026 18:01:45 UTC
On Wed, Mar 18, 2026 at 05:36:00PM +0800, Zhenlei Huang wrote:
> Hi,
>
> While I'm working on the if_detach() / if_vmove() racing issue and I wrote
> a test and try to repeat the race condition and got this assert panic,
>
> ```
> <6>epair1b: link state changed to UP
> userret: returning with the following locks held:
> exclusive sx ifnet_detach_sx (ifnet_detach_sx) r = 1 (0xffffffff83ab75e0) locked @ /home/zlei/freebsd-src/sys/net/if.c:1286
> panic: witness_warn
> cpuid = 2
> time = 1773824038
> KDB: stack backtrace:
> db_trace_self_wrapper() at db_trace_self_wrapper+0xa5/frame 0xfffffe00ec001710
> kdb_backtrace() at kdb_backtrace+0xc6/frame 0xfffffe00ec001870
> vpanic() at vpanic+0x214/frame 0xfffffe00ec001a10
> panic() at panic+0xb5/frame 0xfffffe00ec001ad0
> witness_warn() at witness_warn+0x7f7/frame 0xfffffe00ec001c30
> userret() at userret+0x11c/frame 0xfffffe00ec001d10
> amd64_syscall() at amd64_syscall+0x694/frame 0xfffffe00ec001f30
> fast_syscall_common() at fast_syscall_common+0xf8/frame 0xfffffe00ec001f30
> --- syscall (19, FreeBSD ELF64, compat.lseek), rip = 0xfd288de65ba, rsp = 0xfd284aad4b8, rbp = 0xfd284aad500 ---
> Uptime: 29m8s
> Dumping 1355 out of 16137 MB:..2%..11%..21%..31%..41%..51%..61%..71%..81%..91%
> ```
>
> The line 1286 of sys/net/if.c is ( my WIP revision ),
>
> ```
> 1250 static int
> 1251 if_vmove_reclaim(struct thread *td, char *ifname, int jid)
> 1252 {
> ...
> 1282 /* Get interface back from child jail/vnet. */
> 1283 found = if_unlink_ifnet(ifp, true);
> 1284 MPASS(found);
> 1285 sx_xlock(&ifnet_detach_sxlock);
> 1286 if_vmove(ifp, vnet_dst);
> 1287 sx_xunlock(&ifnet_detach_sxlock);
> 1288
> 1289 /* Report the new if_xname back to the userland. */
> 1290 sprintf(ifname, "%s", ifp->if_xname);
> 1291
> 1292 prison_free(pr);
> 1293 CURVNET_RESTORE();
> 1294 return (0);
> 1295 }
> ```
>
> That puzzled me a lot. The current thread only reaches to `if_vmove()` , how does it happen to userret() ?
>
> Is that witness warn is false report ?
I doubt that this is false report.
userret() is the common code that prepares thread for return from kernel
to usermode. As part of the preparation, it checks that no locks is
left owned by the thread. What you see is the result of the failing
check.
In other words, you have some syscall that locked a lock, did not released
it, then returned. The layer that manages return to userspace caught the
issue.