NOTE:
The report detail and one-shot reproducer was produced with aid of gpt-5.6-sol.
I lack sufficient background with arch level details.
That said:
The repro is consistent with the one-shot reproducer.
Reproducer has been used with success with macOS 26.5 on an Apple Silicon Macbook Pro.
Dependencies:
- Docker Desktop needs to be installed (confirmed repro with v4.82.0)
- Docker VMM is enabled (from Settings -> Virtual Machine Options)
Zero rush given the workaround, and happy to fill in any other detail!
Nix 2.35.1 segfaults under qemu-x86_64 on arm64
Summary
The official nixos/nix:2.35.1 linux/amd64 image segfaults during
nix process startup when it is executed by qemu-user on an arm64 Docker
host. The minimal command nix --version exits with status 139 before
printing a version.
The official Nix 2.34.8 amd64 binary starts in the same environment, so this
is a regression between Nix 2.34 and 2.35 rather than a general failure to
execute amd64 programs. Nix 2.35 is the first release that links mimalloc by
default for non-GC allocations:
https://github.com/NixOS/nix/blob/2.35.1/doc/manual/source/release-notes/rl-2.35.md#improvements
Setting QEMU_GUEST_BASE=0x800000000000 makes the same unmodified Nix 2.35.1
binary start successfully. QEMU syscall traces show that the default layout
returns non-canonical x86_64 addresses to anonymous mappings immediately
before the fault. With the fixed guest base, the corresponding mappings are
below 0x0000800000000000 and Nix reaches normal CLI parsing.
The evidence points to an interaction between qemu-user guest address
placement and the allocator linked into Nix 2.35. It does not yet establish
whether the final fix belongs in QEMU, mimalloc, or Nix.
Environment
- Host hardware: Apple Silicon / arm64
- Docker backend: Docker Desktop LinuxKit on arm64
- BuildKit: v0.30.0,
docker-container driver
- LinuxKit kernel: 6.12.76, 4 KiB pages
- Emulation: qemu-x86_64 10.2.3 through binfmt_misc
- Target platform:
linux/amd64
- Failing image:
nixos/nix:2.35.1@sha256:377d4887aca98f0dfa12971c1ea6d6a625a435d8b610d4c95a436843da6fbfd1
- Control image:
nixos/nix:2.34.8@sha256:1a711b619c8a713eff32c3f8d8781b3b4d0130cb91c0a57f67e87abfeeb90b01
- Failing amd64 image manifest:
sha256:d78540374f6a886653cba47d5c3f61c5a41d42e2a8db2607b8d68cb226fd463e
- Failing Nix store output:
/nix/store/q6yfdws28aj556jlz5yayaggiddmb0b5-nix-2.35.1
- Dynamic loader:
/nix/store/8kvxvr3pmsypxiypq4g8zy13glnfr7nx-glibc-2.42-67/lib/ld-linux-x86-64.so.2
The original failure occurred while building a production amd64 image on the
arm64 Docker host. The first Nix command in the Dockerfile was:
nix --accept-flake-config --no-filter-syscalls build \
--no-write-lock-file --no-update-lock-file --show-trace \
--out-link /tmp/image-build-root \
.#imageBuildRoot
It failed approximately 145 ms after process launch, before evaluation,
substitution, or derivation output appeared.
There is no evidence of this failure on native amd64 hardware. QEMU is used
only while producing and validating an amd64 image from an arm64 build host.
Expected result
Actual result
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Segmentation fault
The command exits with status 139.
One-shot reproducer
Prerequisites are Docker Buildx on an arm64 host and a builder that executes
linux/amd64 through qemu-user. On Docker Desktop for Mac, disable Rosetta
for amd64 emulation so the requested platform actually uses QEMU.
Save the following as reproduce-nix-235-qemu.sh, make it executable, and
run it. The generated Dockerfile performs three checks in one build:
- Nix 2.34.8 starts without a guest-base override.
- Nix 2.35.1 without the override exits 139.
- The same Nix 2.35.1 image starts with the fixed guest base.
#!/usr/bin/env bash
set -euo pipefail
case "$(uname -m)" in
arm64 | aarch64) ;;
*)
echo "This reproducer requires an arm64 host." >&2
exit 2
;;
esac
workdir="$(mktemp -d "${TMPDIR:-/tmp}/nix-235-qemu.XXXXXX")"
trap 'rm -rf "$workdir"' EXIT
cat >"$workdir/Dockerfile" <<'DOCKERFILE'
FROM nixos/nix:2.34.8@sha256:1a711b619c8a713eff32c3f8d8781b3b4d0130cb91c0a57f67e87abfeeb90b01 AS nix-234-control
RUN nix --version \
&& touch /tmp/nix-234-started
FROM nixos/nix:2.35.1@sha256:377d4887aca98f0dfa12971c1ea6d6a625a435d8b610d4c95a436843da6fbfd1
ARG BUILDARCH
ARG TARGETARCH
COPY --from=nix-234-control /tmp/nix-234-started /tmp/nix-234-started
RUN set -eux; \
test -f /tmp/nix-234-started; \
test "$BUILDARCH" = arm64; \
test "$TARGETARCH" = amd64
RUN set -u; \
set +e; \
nix --version; \
nix_status="$?"; \
set -e; \
printf 'plain Nix 2.35.1 exit status: %s\n' "$nix_status"; \
if [ "$nix_status" -ne 139 ]; then \
echo "Expected unmodified Nix 2.35.1 to exit 139." >&2; \
exit 1; \
fi
RUN QEMU_GUEST_BASE=0x800000000000 nix --version
DOCKERFILE
docker buildx inspect --bootstrap >/dev/null
docker buildx build \
--no-cache \
--platform linux/amd64 \
--progress=plain \
"$workdir"
Buildx may warn that no output destination was specified. That is harmless;
the reproducer intentionally leaves its result only in the build cache.
The significant output should resemble:
nix (Nix) 2.34.8
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Segmentation fault
plain Nix 2.35.1 exit status: 139
nix (Nix) 2.35.1
If the second check prints a version instead of exiting 139, confirm that the
builder is arm64 and is using qemu-user rather than Rosetta or a native amd64
node.
Direct binary reproduction
The failure was also reproduced without BuildKit after extracting the
linux/amd64 filesystem from the pinned nixos/nix:2.35.1 image and executing
its untouched Nix binary with the static arm64 qemu-x86_64 10.2.3 binary from
tonistiigi/binfmt.
The default invocation exited 139:
$ env -u LD_LIBRARY_PATH qemu-x86_64 \
-L /tmp/nix-amd64-root \
/tmp/nix-amd64-root/nix/store/q6yfdws28aj556jlz5yayaggiddmb0b5-nix-2.35.1/bin/nix
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
The same binary with the guest-base setting reached Nix CLI parsing instead
of receiving SIGSEGV:
$ env -u LD_LIBRARY_PATH QEMU_GUEST_BASE=0x800000000000 \
qemu-x86_64 \
-L /tmp/nix-amd64-root \
/tmp/nix-amd64-root/nix/store/q6yfdws28aj556jlz5yayaggiddmb0b5-nix-2.35.1/bin/nix
error: no subcommand specified
The CLI error is expected for this direct emulator invocation and proves that
allocator and process initialization completed. The Buildx reproducer uses
binfmt_misc and preserves the normal nix --version argument vector.
Version comparison
The official Nix 2.34.8 x86_64 binary was extracted from its release archive
and run with the same qemu-x86_64 10.2.3 executable. It reached normal CLI
parsing without QEMU_GUEST_BASE.
| Nix version |
Default QEMU layout |
Fixed guest base |
| 2.34.8 |
Starts |
Not required |
| 2.35.1 |
SIGSEGV, exit 139 |
Starts |
Nix 2.35 release notes identify the allocator change directly:
The nix binary now links mimalloc by default, replacing glibc's malloc for
all non-GC allocations.
The release notes also state that distributors can disable it at build time
with -Dmimalloc=disabled. No custom allocator or binary patching is present
in the official Docker image used here.
Related Nix change:
#15596
QEMU syscall trace
With QEMU_STRACE=1 and no fixed guest base, the final anonymous mappings and
fault were:
mmap(NULL,2359296,PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE,-1,0)
= 0x0000ffffa5e1f000
mmap(NULL,65536,PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE,-1,0)
= 0x0000ffffa5dfd000
mmap(NULL,131072,PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE,-1,0)
= 0x0000ffffa5dd5000
munmap(0x0000ffffa5dd5000,45056) = 0
munmap(0x0000ffffa5df0000,20480) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=1,
si_addr=0x0ffffffefc873e7f} ---
Addresses beginning with 0x0000ffff... are not canonical under the common
48-bit x86_64 virtual-address model: bit 47 is set while bits 63 through 48
are clear. The reported fault address is also non-canonical.
With QEMU_GUEST_BASE=0x800000000000, the corresponding mappings were in the
canonical lower range and the process continued:
mmap(NULL,2359296,PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE,-1,0)
= 0x00007fff8da35000
mmap(NULL,65536,PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE,-1,0)
= 0x00007fff8da1c000
mmap(NULL,131072,PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE,-1,0)
= 0x00007fff8d9f4000
QEMU exposes this setting as both -B address and the
QEMU_GUEST_BASE environment variable:
https://gitlab.com/qemu-project/qemu/-/blob/v10.2.3/linux-user/main.c
No native backtrace or allocator symbol resolution has been collected yet.
The syscall trace, version boundary, and successful guest-base override make
the address-layout interaction reproducible, but the exact faulting mimalloc
operation remains to be identified.
Local workaround
The local Dockerfile exports the fixed guest base before each Nix command,
but only for an arm64 BuildKit worker targeting amd64:
if [ "$BUILDARCH" = arm64 ] && [ "$TARGETARCH" = amd64 ]; then
export QEMU_GUEST_BASE=0x800000000000
fi
nix build ...
The setting is a build argument inherited only by intermediate Nix-backed
stages. It is not stored as an ENV value in the final image, and it is not
applied to native builds or amd64-hosted arm64 emulation.
Upstream questions
- Should qemu-x86_64 prevent guest-visible anonymous mappings from landing
in the non-canonical 0x0000ffff... range on an arm64 host?
- Is mimalloc expected to reject or recover from such mapping results before
deriving allocator metadata or pointers from them?
- Would Nix accept a qemu-user startup test for the mimalloc-enabled binary,
or an allocator configuration change if QEMU cannot guarantee the needed
address layout?
- What additional trace or symbolized core would be most useful to determine
whether this should be fixed in QEMU, mimalloc, or Nix?
NOTE:
The report detail and one-shot reproducer was produced with aid of gpt-5.6-sol.
I lack sufficient background with arch level details.
That said:
The repro is consistent with the one-shot reproducer.
Reproducer has been used with success with macOS 26.5 on an Apple Silicon Macbook Pro.
Dependencies:
Zero rush given the workaround, and happy to fill in any other detail!
Nix 2.35.1 segfaults under qemu-x86_64 on arm64
Summary
The official
nixos/nix:2.35.1linux/amd64 image segfaults duringnixprocess startup when it is executed by qemu-user on an arm64 Dockerhost. The minimal command
nix --versionexits with status 139 beforeprinting a version.
The official Nix 2.34.8 amd64 binary starts in the same environment, so this
is a regression between Nix 2.34 and 2.35 rather than a general failure to
execute amd64 programs. Nix 2.35 is the first release that links mimalloc by
default for non-GC allocations:
https://github.com/NixOS/nix/blob/2.35.1/doc/manual/source/release-notes/rl-2.35.md#improvements
Setting
QEMU_GUEST_BASE=0x800000000000makes the same unmodified Nix 2.35.1binary start successfully. QEMU syscall traces show that the default layout
returns non-canonical x86_64 addresses to anonymous mappings immediately
before the fault. With the fixed guest base, the corresponding mappings are
below
0x0000800000000000and Nix reaches normal CLI parsing.The evidence points to an interaction between qemu-user guest address
placement and the allocator linked into Nix 2.35. It does not yet establish
whether the final fix belongs in QEMU, mimalloc, or Nix.
Environment
docker-containerdriverlinux/amd64nixos/nix:2.35.1@sha256:377d4887aca98f0dfa12971c1ea6d6a625a435d8b610d4c95a436843da6fbfd1nixos/nix:2.34.8@sha256:1a711b619c8a713eff32c3f8d8781b3b4d0130cb91c0a57f67e87abfeeb90b01sha256:d78540374f6a886653cba47d5c3f61c5a41d42e2a8db2607b8d68cb226fd463e/nix/store/q6yfdws28aj556jlz5yayaggiddmb0b5-nix-2.35.1/nix/store/8kvxvr3pmsypxiypq4g8zy13glnfr7nx-glibc-2.42-67/lib/ld-linux-x86-64.so.2The original failure occurred while building a production amd64 image on the
arm64 Docker host. The first Nix command in the Dockerfile was:
It failed approximately 145 ms after process launch, before evaluation,
substitution, or derivation output appeared.
There is no evidence of this failure on native amd64 hardware. QEMU is used
only while producing and validating an amd64 image from an arm64 build host.
Expected result
Actual result
The command exits with status 139.
One-shot reproducer
Prerequisites are Docker Buildx on an arm64 host and a builder that executes
linux/amd64through qemu-user. On Docker Desktop for Mac, disable Rosettafor amd64 emulation so the requested platform actually uses QEMU.
Save the following as
reproduce-nix-235-qemu.sh, make it executable, andrun it. The generated Dockerfile performs three checks in one build:
Buildx may warn that no output destination was specified. That is harmless;
the reproducer intentionally leaves its result only in the build cache.
The significant output should resemble:
If the second check prints a version instead of exiting 139, confirm that the
builder is arm64 and is using qemu-user rather than Rosetta or a native amd64
node.
Direct binary reproduction
The failure was also reproduced without BuildKit after extracting the
linux/amd64 filesystem from the pinned
nixos/nix:2.35.1image and executingits untouched Nix binary with the static arm64 qemu-x86_64 10.2.3 binary from
tonistiigi/binfmt.The default invocation exited 139:
The same binary with the guest-base setting reached Nix CLI parsing instead
of receiving SIGSEGV:
The CLI error is expected for this direct emulator invocation and proves that
allocator and process initialization completed. The Buildx reproducer uses
binfmt_misc and preserves the normal
nix --versionargument vector.Version comparison
The official Nix 2.34.8 x86_64 binary was extracted from its release archive
and run with the same qemu-x86_64 10.2.3 executable. It reached normal CLI
parsing without
QEMU_GUEST_BASE.Nix 2.35 release notes identify the allocator change directly:
The release notes also state that distributors can disable it at build time
with
-Dmimalloc=disabled. No custom allocator or binary patching is presentin the official Docker image used here.
Related Nix change:
#15596
QEMU syscall trace
With
QEMU_STRACE=1and no fixed guest base, the final anonymous mappings andfault were:
Addresses beginning with
0x0000ffff...are not canonical under the common48-bit x86_64 virtual-address model: bit 47 is set while bits 63 through 48
are clear. The reported fault address is also non-canonical.
With
QEMU_GUEST_BASE=0x800000000000, the corresponding mappings were in thecanonical lower range and the process continued:
QEMU exposes this setting as both
-B addressand theQEMU_GUEST_BASEenvironment variable:https://gitlab.com/qemu-project/qemu/-/blob/v10.2.3/linux-user/main.c
No native backtrace or allocator symbol resolution has been collected yet.
The syscall trace, version boundary, and successful guest-base override make
the address-layout interaction reproducible, but the exact faulting mimalloc
operation remains to be identified.
Local workaround
The local Dockerfile exports the fixed guest base before each Nix command,
but only for an arm64 BuildKit worker targeting amd64:
The setting is a build argument inherited only by intermediate Nix-backed
stages. It is not stored as an
ENVvalue in the final image, and it is notapplied to native builds or amd64-hosted arm64 emulation.
Upstream questions
in the non-canonical
0x0000ffff...range on an arm64 host?deriving allocator metadata or pointers from them?
or an allocator configuration change if QEMU cannot guarantee the needed
address layout?
whether this should be fixed in QEMU, mimalloc, or Nix?