Skip to content

libstore: escape store paths when unparsing derivations - #16343

Open
awsmadi wants to merge 2 commits into
NixOS:masterfrom
awsmadi:pr/aterm-escape-store-paths
Open

libstore: escape store paths when unparsing derivations#16343
awsmadi wants to merge 2 commits into
NixOS:masterfrom
awsmadi:pr/aterm-escape-store-paths

Conversation

@awsmadi

@awsmadi awsmadi commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

printUnquotedString escapes nothing, so store paths written through it land in the
.drv verbatim. Harmless on Unix, where store paths contain no character
printString would escape. On Windows they contain backslashes, and the reader's
escape table then mis-decodes them: \n in ...\nix\store becomes a literal
newline and the path fails to round-trip.

The artefact indicts itself. One .drv carried the same store path twice, unescaped
in the outputs field and escaped in the env field:

Derive([("out","C:\ProgramData\nix\store/bk8vf...-winhello",...
   ...  [("out","C:\\ProgramData\\nix\\store/bk8vf...-winhello"),

Reading it back gave path 'C:ProgramData<newline>ixstore/...' is not in the Nix store.

This switches the four store-path writes in unparse to printString.

Nothing can change on Unix. printString is a superset of printUnquotedString:
for input containing none of ", \, \n, \r, \t the two produce identical
bytes, and store paths on Unix contain none of them. So no .drv, derivation hash
or cache entry moves. Checked rather than assumed: nix-store-tests is unmoved at
764 passed / 12 failed across both arms with the same twelve names, and a
deliberately different store hash does move the output, so the comparison could
have detected a change.

This does not make building work on Windows. Realising a derivation stops earlier
for want of a builder.

@awsmadi
awsmadi marked this pull request as ready for review August 21, 2026 19:24
@awsmadi
awsmadi requested a review from Ericson2314 as a code owner August 21, 2026 19:24
@Ericson2314

Copy link
Copy Markdown
Member

Let's do this after #16284

printUnquotedString() escapes nothing, so store paths written through it went
into the .drv verbatim. On Unix that is harmless -- store paths contain no
character printString() would escape -- but on Windows they contain backslashes,
which the reader's escape table then mis-decodes: `\n` in `...\nix\store` becomes
a literal newline, and the path fails to round-trip.

A single .drv therefore disagreed with itself, carrying the same store path
unescaped in the outputs field and correctly escaped in the env field. Reading it
back gave: path 'C:ProgramData<newline>ixstore/...' is not in the Nix store.

Use printString() for the four store-path writes. printString() is a superset of
printUnquotedString(): for any input containing none of " \ \n \r \t the two
produce byte-identical output, so this cannot change a .drv, a derivation hash or
a cache entry on Unix. Verified: nix-store-tests is unmoved at 764 passed / 12
failed with the same twelve test names.

Not claimed: that this makes building work on Windows. Realising a derivation
stops earlier, at an explicit UnimplementedError, because no Windows derivation
builder exists.

Assisted-by: Claude Code (claude-opus-5)
@awsmadi
awsmadi force-pushed the pr/aterm-escape-store-paths branch from a2cefb0 to bbf2132 Compare August 24, 2026 15:28
Assisted-by: Claude Code (claude-opus-5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants