[Bug 299836] [linuxulator][arm64] Linux brand does not set LINUX_BI_FUTEX_REQUEUE, musl containers deadlock on FUTEX_REQUEUE
- Reply: bugzilla-noreply_a_freebsd.org: "[Bug 299836] [linuxulator][arm64] Linux brand does not set LINUX_BI_FUTEX_REQUEUE, musl containers deadlock on FUTEX_REQUEUE"
- Reply: bugzilla-noreply_a_freebsd.org: "[Bug 299836] [linuxulator][arm64] Linux brand does not set LINUX_BI_FUTEX_REQUEUE, musl containers deadlock on FUTEX_REQUEUE"
- Reply: bugzilla-noreply_a_freebsd.org: "[Bug 299836] Linux brand does not set LINUX_BI_FUTEX_REQUEUE, musl containers deadlock on FUTEX_REQUEUE"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 23 Oct 2026 09:01:26 UTC
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=299836
Bug ID: 299836
Summary: [linuxulator][arm64] Linux brand does not set
LINUX_BI_FUTEX_REQUEUE, musl containers deadlock on
FUTEX_REQUEUE
Product: Base System
Version: 15.1-RELEASE
Hardware: Any
OS: Any
Status: New
Severity: Affects Many People
Priority: ---
Component: kern
Assignee: bugs@FreeBSD.org
Reporter: amutu@amutu.com
linux_futex() only allows FUTEX_REQUEUE when the process' ELF brandinfo sets
the
LINUX_BI_FUTEX_REQUEUE flag; otherwise the operation is logged as unsupported
and EINVAL is
returned:
case LINUX_FUTEX_REQUEUE:
...
/*
* The above is true, however musl libc does make use of the
* futex requeue operation, allow operation for brands which
* set LINUX_BI_FUTEX_REQUEUE bit of Brandinfo flags.
*/
bi = td->td_proc->p_elf_brandinfo;
if (bi == NULL || ((bi->flags & LINUX_BI_FUTEX_REQUEUE)) == 0)
return (EINVAL);
The flag is set by the amd64 and i386 brands — amd64 even carries a dedicated
linux_muslbrand
entry with interp_path = "/lib/ld-musl-x86_64.so.1". The arm64 brand does not
set it, and arm64 has
no musl brand at all:
/* sys/arm64/linux/linux_sysvec.c */
static const Elf64_Brandinfo linux_glibc2brand = {
.brand = ELFOSABI_LINUX,
.machine = EM_AARCH64,
.compat_3_brand = "Linux",
.interp_path = "/lib64/ld-linux-x86-64.so.2",
.sysvec = &elf_linux_sysvec,
.interp_newpath = NULL,
.brand_note = &linux64_brandnote,
.flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE
};
const Elf64_Brandinfo *linux_brandlist[] = {
&linux_glibc2brand,
NULL
};
musl uses FUTEX_REQUEUE in its condition-variable path. With EINVAL returned,
waiters are never
requeued/woken, the wakeup is lost, and the process deadlocks: every thread
ends up sleeping in
linux_sys_futex and no further syscalls are issued.
In practice this is the second blocker for musl-based Linux containers on
arm64. After the
linux_mremap() fix (PR #1) is applied, the CPU spin is gone but node -e still
never completes;
dmesg shows:
linux: jid 15 pid 4476 (DelayedTaskSche): unsupported FUTEX_REQUEUE
【Steps to reproduce】
1. Apply the linux_mremap() fix (PR 299835:
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=299835), otherwise the
process spins before reaching this bug.
2. Run any Alpine/musl Linux container that starts Node.js:
sudo podman run --rm --os=linux ghcr.io/gethomepage/homepage:latest node -e
"console.log(1)"
Observed: the process hangs with 0% CPU, all threads in umtxq_sleep, and no
mremap/futex
traffic afterwards; dmesg contains unsupported FUTEX_REQUEUE.
$ sudo procstat -kk <pid>
mi_switch ... _sleep ... umtxq_sleep ... linux_sys_futex ... (all threads)
$ sudo dtrace -n 'fbt::linux_*:entry /pid==<pid>/ { @[probefunc]=count(); }
tick-2s {exit(0);}'
(no output — zero syscalls in two seconds)
【Fix】
Allow FUTEX_REQUEUE on arm64 by setting the brand flag. A faithful port of the
amd64 layout (a
separate linux_muslbrand with /lib/ld-musl-aarch64.so.1) is also possible and
probably desirable,
but the flag alone is sufficient and has no effect on glibc binaries, which do
not issue this
operation.
--- a/sys/arm64/linux/linux_sysvec.c
+++ b/sys/arm64/linux/linux_sysvec.c
@@ -601,7 +601,8 @@ static const Elf64_Brandinfo linux_glibc2brand = {
.sysvec = &elf_linux_sysvec,
.interp_newpath = NULL,
.brand_note = &linux64_brandnote,
- .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE
+ .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE |
+ LINUX_BI_FUTEX_REQUEUE
};
Affected versions checked: releng/15.1 and main; the arm64 file is identical in
both.
【Test plan】
- After the patch dmesg no longer reports unsupported FUTEX_REQUEUE, and
podman run --rm --os=linux <alpine image> node -e "console.log('ok')" prints ok
and exits 0.
- End-to-end: the original workload starts and serves requests —
$ sudo podman run --rm --os=linux --network=host \
-e HOSTNAME=0.0.0.0 -e HOMEPAGE_ALLOWED_HOSTS=gethomepage.dev \
ghcr.io/gethomepage/homepage:latest
▲ Next.js 16.3.8
- Network: http://0.0.0.0:3000
✓ Ready in 0ms
$ curl -H "Host: gethomepage.dev" -o /dev/null -w "%{http_code}\n"
http://127.0.0.1:3000/
200
--
You are receiving this mail because:
You are the assignee for the bug.