June 24, 2025·6 min read·engineeringnixsecuritysandbox

Lix fixes a privilege-escalation chain across build isolation boundaries

Five disclosed Lix issues connect file-descriptor transfer, build-user privileges, and path races. The fixes combine safer filesystem access with a protected build directory and optional network isolation.

A build finishing does not prove that every capability it obtained has disappeared. An open file descriptor can be transferred to another process and survive the original build. Lix's June security disclosure explains how that possibility combines with build-user privileges and privileged filesystem operations to invalidate assumptions in the daemon.

The June 24 announcement lists CVE-2025-46415, CVE-2025-46416, CVE-2025-52991, CVE-2025-52992, and CVE-2025-52993. It announces updates for the 2.91, 2.92, and 2.93 branches and asks users to update immediately. Rory McNamara of Snyk Security Labs reported the issues and helped coordinate the response.

The related Guix disclosure published today describes similar vulnerabilities in its daemon and the coordination between the affected projects. The implementations and fixes need their own attribution, but the shared problem is clear. Build isolation must account for capabilities that escape a build's expected lifetime.

I use Nix for declarative management of personal infrastructure because explicit inputs make system changes understandable. This disclosure concerns the privileged machinery implementing that model. A correct description of a build does not by itself make every operation performed by its daemon safe against a malicious build process.

Abstract sockets cross a different boundary from filesystem paths

On Linux, abstract Unix-domain sockets belong to a network namespace and do not have filesystem names. Hiding part of the filesystem does not isolate these sockets from another process in the same network namespace. That distinction creates a communication route that filesystem-oriented reasoning can miss.

Unix-domain sockets can transfer file descriptors. Once a receiving process holds a descriptor, it has a reference to the underlying object. The sender's later exit does not revoke that reference. A build can therefore pass a capability to another process before the daemon considers the build finished.

The Lix announcement connects this mechanism to the earlier CVE-2024-27297. Today's problem extends the consequences beyond the earlier output-integrity concern. A surviving capability can undermine assumptions about whether the build user's activity has ended, which then affects operations the daemon performs after a build.

Guix's disclosure explains one concrete route through a descriptor for a setuid program. Its account says the descriptor can be executed later to obtain build-user privileges outside the intended build environment. That is Guix's description of the shared vulnerability family, not a claim that every platform exposes identical exploitation steps.

The important distinction is between ending processes the daemon knows about and revoking every capability they could have transferred. Those are different conditions. A security design that assumes the second follows automatically from the first needs to account for interprocess descriptor transfer.

A compromised build user changes the race assumptions

The daemon ordinarily runs with greater privileges than build processes. Build users restrict what untrusted build code can do directly. The Lix announcement says users initiating builds should not be able to impersonate those build users or replace the directories prepared for them.

If an attacker can act as a build user after the expected build lifetime, the daemon may encounter concurrent changes it did not anticipate. Cleanup is one example. A directory tree being recursively deleted can change while the privileged process traverses it. A path chosen earlier may resolve to a different object by the time an operation uses it.

Lix identifies a specific recursive-deletion defect under CVE-2025-46415. The implementation intended to use directory file descriptors, but passed absolute paths to unlinkat in a way that defeated that intention. The directory descriptor did not constrain resolution when the supplied pathname was absolute.

That is a useful example of an API choice failing to establish the intended property. Calling a function that accepts a directory descriptor is insufficient if the path argument causes resolution to ignore it. The safety relationship depends on using the descriptor and relative name together correctly.

The correction therefore needs to address the actual object used for an operation. Remembering a string path and reopening it later leaves an interval in which another process can change its meaning. Passing directory descriptors consistently reduces reliance on that repeated name resolution.

Build-directory placement limits another opportunity

Lix also moves build staging to /nix/var/nix/builds, under daemon control. Previously, builds commonly staged in a shared temporary location such as /tmp. The announcement explains that an attacker who can arrange removal of an expected directory may replace its pathname with attacker-controlled content there.

The protected parent directory limits who can make those replacements. It complements descriptor-based access by reducing the opportunity for another user to alter the relevant names. The two changes address related conditions at different points in the process.

This relocation has practical consequences for storage. A system using a RAM-backed /tmp no longer obtains that placement automatically for builds. The new location follows the filesystem containing the Nix state directory, depending on the machine's layout. The announcement asks users relying on temporary-memory storage to reconsider the mount used for the build directory.

It specifically recommends mode=0755 if that directory uses tmpfs. Mounting a fresh world-writable temporary filesystem at the protected location would weaken the protection the relocation intended to provide. The relevant property is control of the parent directory, not merely whether the mount is in memory or on disk.

Network isolation has a separate compatibility cost

Input-addressed derivations already receive isolated network namespaces in the Linux arrangement described by Lix. Fixed-output derivations need network access for tasks such as fetching sources. Those builds had used the host network namespace, which also exposed the abstract-socket communication route.

Lix adds optional Pasta-based networking for fixed-output builds. Each build gets an isolated network namespace, with a userspace process providing outside connectivity. The announcement compares the arrangement to rootless container networking, because it does not require changing the host's network configuration for each build.

This mitigation is distinct from the default-on filesystem and descriptor corrections. The announcement says Pasta is disabled by default on stable NixOS releases and enabled on unstable channels. Its compatibility implications include networking behavior beyond ordinary TCP or UDP and workloads expecting incoming connections.

The disclosure also discusses an alternative Linux Security Module mitigation for abstract sockets. Scoping that restriction correctly is difficult, and the announcement describes it as prototype work rather than a universally enabled fix. A global ban would affect legitimate host software using the same socket mechanism.

These details matter when interpreting the word fixed. The announcement separates default-on corrections from optional isolation. Its guidance says CVE-2025-46416 requires the Pasta or LSM mitigation. A summary claiming that every configuration receives every mitigation automatically would hide a documented difference in the released response.

A handle check and a pathname check establish different facts

The announcement says Lix adds validation for incoming file descriptors as well as using descriptors more consistently. These changes address different questions. Receiving a descriptor does not establish that it refers to the kind of object the daemon expects. A descriptor can preserve an object's identity while still naming an inappropriate object for the requested operation.

Path-based checks have the inverse difficulty. A path can appear appropriate at one moment, yet resolve differently later. Correct handling needs both an appropriate object and a stable relationship to that object during use. The response's combination of validation and descriptor-based access reflects those separate requirements.

The daemon must survive hostile handle behavior

The Lix announcement discusses macOS and Linux systems with reduced sandbox settings because network and filesystem isolation differ across them. An assumption that abstract sockets are the only possible descriptor-transfer route would miss ordinary Unix sockets where filesystem access permits them.

The broader daemon requirement is to remain safe when derivations behave unexpectedly with file handles, including after their apparent termination. That is why filesystem corrections still matter independently of network isolation. Restricting communication reduces a route to retained capabilities, while descriptor-safe operations address what happens if an attacker still has concurrent influence.

I regard the layered response as necessary for a privileged build service. A configuration declaration identifies intended inputs, while the daemon must treat the code realizing those inputs as potentially hostile. The published distinction between base corrections and optional network isolation gives operators a specific account of which protections their configuration selects.

For NixOS users, the announcement distinguishes updating a Lix module pin from consuming Lix through Nixpkgs. It warns that a package change reaching a repository does not mean the corresponding channel has already advanced through its build process. The operational target is a running daemon containing the fixes, reached through the update path that actually supplies that machine's Lix package.