Re: userret: returning with the following locks held

From: Zhenlei Huang <zlei_at_FreeBSD.org>
Date: Thu, 19 Mar 2026 15:47:07 UTC

> On Mar 19, 2026, at 2:01 AM, Konstantin Belousov <kostikbel@gmail.com> wrote:
> 
> 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.

Thanks ! I'll take care of the locking issue (  syscall returned without lock released ).

I'll report back if I re-encounter this issue.

Best regards,
Zhenlei