Nix needs relocatable binaries
49 points by polywolf
49 points by polywolf
I would love to see $ORIGIN support for PT_INTERP in the Linux kernel. I've done static wrapper binaries for this before, and I've seen a number of other attempts (here's a good one). They're all glorious, wonderful hacks. But hacks nonetheless.
I haven't grokked the security implications. It would be helpful to see them laid out.
Pretty sure Solaris supports this, so maybe there's a way to do this securely. I'm struggling to find a reference, but the execve(2) man page has this under ENOEXEC:
The PT_INTERP program header for a setuid or setgid process image file has a relative path, or employs the $ORIGIN token
Won't many software have hardcoded paths and other constants defined at build-time that would also require recompilation for the software to work correctly?
That’s already the case, mostly for shebang lines, and Nix includes a ton of helpers for replacing these at build time.
Yes, but this problem existed regardless of local stores. Presumably downstream derivations consume the outPath via {foo}. They do need to get rebuilt if you switch one of their upstreams to a local store.
The dcrt1 patch for musl (by rcombs) solves this in userspace.
hard to find an authoritative source for this, https://www.openwall.com/lists/musl/2020/03/29/9 seems close but might be incomplete? Would love to see an updated version of the portable-dynamic branch in https://github.com/rcombs/musl but it has been some time...
There’s also —relocatable PoC in https://github.com/Mic92/wrap-buddy, which I contributed, implementing a very similar approach. I later found the musl dcrt1 patchset. Though the wrap-buddy patching also works with unmodified glibc.
Farid and I had a bit of a chat, and he popped out another blog post https://fzakaria.com/2026/06/22/hijacking-elf-entry-points-for-nixos-compatibility-or-wtf-is-wrap-buddy
Side note: setuid concerns don’t really apply much for use with nix, since those aren’t really allowed in the nix store.
Yesterday I commented:
Farid has many recent foundational posts on Nix. I'm hoping he's porting store-based OSs to Buck, as part of his recent role at Meta.
Today, this post brings these hopes ever closer!
If you are using tools like Bazel or Buck2 they likely already employ their own sandboxing via namespacing for builds. Integrating Nix into these ecosystems becomes incredibly impractical because we run into nested user namespace and mount restrictions.
If we switch to using relative paths, wouldn't that potentially break hardlinks to the binary?
Seems that way yeah. Nix (the program) seems to use mostly softlinks rn tho. Is there anything that'd require a hardlink that can't also take a store path?
Besides the fact that Nix itself has an option to automatically replace duplicate store paths with hardlinks, users can also hardlink files in the store and it's natural to expect that to work. If I write ln /nix/store/in4yc03diyvs2n2wgf3nva4hbvml8v1j-bash-interactive-5.3p9/bin/bash; ./bash that should work just fine.
Could one not make a file system mounted at /origin that would resolve to $ORIGIN? That way, it would work in shebangs and ELF without additional syntax.
If you can mount /origin and mount it, you can probably create /nix and run nix-daemon?
my thought is that one would look to avoid having extra file systems/daemons to ensure compatibility outside of NixOS without having to install/setup extra stuff
Wouldn’t it be insecure if a binary can specify a relative path to the (presumably unsafe, self-sourced) loader?
What is trusted and what is untrusted in this threat model? Are we going to execute the binary anyway, just with a known-good loader?
Why would it be less safe than the environment variables used to define the search path for other dynamically linked libraries like libc.so.6?
Well one reason is that the kernel/loader disable those features when running in secure execution mode (getauxval(AT_SECURE) != 0). The loader also has logic to ignore anything shaped like a path in SUID binaries.
Because the machine owner (and not the ELF object owner) sets or unsets the environment variables such as PATH or LD_PRELOAD. But if a malicious actor ships release/malus.o along with ../shared/malus-ld.so in a tarball, a user executing the former wouldn’t be able to easily control loading the latter. I might be missing a point. Genuinely open to perspectives.
It’s not clear to me how that would be any different than a statically linked executable from the “the binary defines what its behavior is” perspective.