<feed xmlns='http://www.w3.org/2005/Atom'>
<title>guix/tests/nar.scm, branch master</title>
<subtitle>Transactional package manager, declarative GNU/Linux distribution, reproducible deployment tool, and more! https://guix.gnu.org</subtitle>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/'/>
<entry>
<title>guix: serialization: validate directory entry names in fold-archive [security fix].</title>
<updated>2026-07-02T17:42:47+00:00</updated>
<author>
<name>Reepca Russelstein</name>
<email>reepca@russelstein.xyz</email>
</author>
<published>2026-06-08T05:21:43+00:00</published>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/commit/?id=ed0a9721f8a20d6ddcf6a0495302f502b3f7bb17'/>
<id>ed0a9721f8a20d6ddcf6a0495302f502b3f7bb17</id>
<content type='text'>
Also validate that they are in strictly ascending order, which also ensures
that there are no duplicate names.

When 'guix substitute' fetches a nar, it does so with a validly-signed hash
already known from the narinfo.  But it can't verify that the hash of the nar
it's currently fetching matches until the full nar is downloaded.  Until then,
'download-nar' will extract the nar into the specified destination using
'restore-path', which avoids having to keep a file of unbounded size in
memory.  Critically, this means that the input that 'restore-path' (and by
extension 'fold-archive') is processing is untrusted, since substitute server
TLS certificates aren't verified (the narinfo signatures are supposed to make
it unnecessary).

As such, the scope of harm that can be caused by a malicious nar being
processed by 'restore-file' needs to be minimized.

* guix/serialization.scm (valid-nar-file-name?): new procedure.
  (fold-archive): Use it to verify that directory entry names do not contain
  '/' or '\0', and they are not equal to ".", "..", or "".  Also verify that
  they are in strictly ascending order.
  (call-with-port*): new procedure.
  (dump-file): use O_EXCL and O_NOFOLLOW.  This precaution ensures that even
  if 'restore-file' is somehow tricked into writing to a symlink (not
  currently believed to be possible), it will result in an error.
* guix/store/deduplication.scm (call-with-fresh-output-file): new procedure.
  (dump-file/deduplicate): use it for similar reasons as 'dump-file'.
* tests/nar.scm (call-with-tree-port, port-bad-nar?): new procedures.
  ("write-file-tree + fold-archive, unsorted directory entries",
   "write-file-tree + fold-archive, duplicate directory entries",
   "write-file-tree + fold-archive, invalid directory entries"): new tests.
* tests/publish.scm (call-with-temporary-output-filename): new procedure.
  ("/nar/*", "/nar/gzip/*", "/nar/lzip/*", "/nar/zstd/*", "/nar/ with properly
  encoded '+' sign"): use it in these test cases so that the output filename
  doesn't name an already-existing file.

Change-Id: I41f248c13d7af787233afad5cae102056329a68b
Signed-off-by: Ludovic Courtès &lt;ludo@gnu.org&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Also validate that they are in strictly ascending order, which also ensures
that there are no duplicate names.

When 'guix substitute' fetches a nar, it does so with a validly-signed hash
already known from the narinfo.  But it can't verify that the hash of the nar
it's currently fetching matches until the full nar is downloaded.  Until then,
'download-nar' will extract the nar into the specified destination using
'restore-path', which avoids having to keep a file of unbounded size in
memory.  Critically, this means that the input that 'restore-path' (and by
extension 'fold-archive') is processing is untrusted, since substitute server
TLS certificates aren't verified (the narinfo signatures are supposed to make
it unnecessary).

As such, the scope of harm that can be caused by a malicious nar being
processed by 'restore-file' needs to be minimized.

* guix/serialization.scm (valid-nar-file-name?): new procedure.
  (fold-archive): Use it to verify that directory entry names do not contain
  '/' or '\0', and they are not equal to ".", "..", or "".  Also verify that
  they are in strictly ascending order.
  (call-with-port*): new procedure.
  (dump-file): use O_EXCL and O_NOFOLLOW.  This precaution ensures that even
  if 'restore-file' is somehow tricked into writing to a symlink (not
  currently believed to be possible), it will result in an error.
* guix/store/deduplication.scm (call-with-fresh-output-file): new procedure.
  (dump-file/deduplicate): use it for similar reasons as 'dump-file'.
* tests/nar.scm (call-with-tree-port, port-bad-nar?): new procedures.
  ("write-file-tree + fold-archive, unsorted directory entries",
   "write-file-tree + fold-archive, duplicate directory entries",
   "write-file-tree + fold-archive, invalid directory entries"): new tests.
* tests/publish.scm (call-with-temporary-output-filename): new procedure.
  ("/nar/*", "/nar/gzip/*", "/nar/lzip/*", "/nar/zstd/*", "/nar/ with properly
  encoded '+' sign"): use it in these test cases so that the output filename
  doesn't name an already-existing file.

Change-Id: I41f248c13d7af787233afad5cae102056329a68b
Signed-off-by: Ludovic Courtès &lt;ludo@gnu.org&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>daemon: Do not deduplicate files smaller than 8 KiB.</title>
<updated>2021-11-16T13:34:28+00:00</updated>
<author>
<name>Ludovic Courtès</name>
<email>ludo@gnu.org</email>
</author>
<published>2021-11-13T20:47:15+00:00</published>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/commit/?id=472a0e82a52a3d5d841e1dfad6b13e26082a5750'/>
<id>472a0e82a52a3d5d841e1dfad6b13e26082a5750</id>
<content type='text'>
Files smaller than 8 KiB typically represent ~70% of the entries in
/gnu/store/.links but only contribute to ~4% of the space savings
afforded by deduplication.

Not considering these files for deduplication speeds up file insertion
in the store and, more importantly, leaves 'removeUnusedLinks' with
fewer entries to traverse, thereby speeding it up proportionally.

Partly fixes &lt;https://issues.guix.gnu.org/24937&gt;.

* config-daemon.ac: Remove symlink hard link check and CAN_LINK_SYMLINK
definition.
* guix/store/deduplication.scm (%deduplication-minimum-size): New
variable.
(deduplicate)[loop]: Do not recurse when FILE's size is below
%DEDUPLICATION-MINIMUM-SIZE.
(dump-port): New procedure.
(dump-file/deduplicate)[hash]: Turn into...
[dump-and-compute-hash]: ... this thunk.
Call 'deduplicate' only when SIZE is greater than
%DEDUPLICATION-MINIMUM-SIZE; otherwise call 'dump-port'.
* nix/libstore/gc.cc (LocalStore::removeUnusedLinks): Drop files where
st.st_size &lt; deduplicationMinSize.
* nix/libstore/local-store.hh (deduplicationMinSize): New declaration.
* nix/libstore/optimise-store.cc (deduplicationMinSize): New variable.
(LocalStore::optimisePath_): Return when PATH is a symlink or smaller
than 'deduplicationMinSize'.
* tests/derivations.scm ("identical files are deduplicated"): Produce
files bigger than %DEDUPLICATION-MINIMUM-SIZE.
* tests/nar.scm ("restore-file-set with directories (signed, valid)"):
Likewise.
* tests/store-deduplication.scm ("deduplicate, below %deduplication-minimum-size"):
New test.
("deduplicate", "deduplicate, ENOSPC"): Produce files bigger than
%DEDUPLICATION-MINIMUM-SIZE.
* tests/store.scm ("substitute, deduplication"): Likewise.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Files smaller than 8 KiB typically represent ~70% of the entries in
/gnu/store/.links but only contribute to ~4% of the space savings
afforded by deduplication.

Not considering these files for deduplication speeds up file insertion
in the store and, more importantly, leaves 'removeUnusedLinks' with
fewer entries to traverse, thereby speeding it up proportionally.

Partly fixes &lt;https://issues.guix.gnu.org/24937&gt;.

* config-daemon.ac: Remove symlink hard link check and CAN_LINK_SYMLINK
definition.
* guix/store/deduplication.scm (%deduplication-minimum-size): New
variable.
(deduplicate)[loop]: Do not recurse when FILE's size is below
%DEDUPLICATION-MINIMUM-SIZE.
(dump-port): New procedure.
(dump-file/deduplicate)[hash]: Turn into...
[dump-and-compute-hash]: ... this thunk.
Call 'deduplicate' only when SIZE is greater than
%DEDUPLICATION-MINIMUM-SIZE; otherwise call 'dump-port'.
* nix/libstore/gc.cc (LocalStore::removeUnusedLinks): Drop files where
st.st_size &lt; deduplicationMinSize.
* nix/libstore/local-store.hh (deduplicationMinSize): New declaration.
* nix/libstore/optimise-store.cc (deduplicationMinSize): New variable.
(LocalStore::optimisePath_): Return when PATH is a symlink or smaller
than 'deduplicationMinSize'.
* tests/derivations.scm ("identical files are deduplicated"): Produce
files bigger than %DEDUPLICATION-MINIMUM-SIZE.
* tests/nar.scm ("restore-file-set with directories (signed, valid)"):
Likewise.
* tests/store-deduplication.scm ("deduplicate, below %deduplication-minimum-size"):
New test.
("deduplicate", "deduplicate, ENOSPC"): Produce files bigger than
%DEDUPLICATION-MINIMUM-SIZE.
* tests/store.scm ("substitute, deduplication"): Likewise.
</pre>
</div>
</content>
</entry>
<entry>
<title>nar: Deduplicate files right as they are restored.</title>
<updated>2020-12-15T16:32:09+00:00</updated>
<author>
<name>Ludovic Courtès</name>
<email>ludo@gnu.org</email>
</author>
<published>2020-12-10T10:21:14+00:00</published>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/commit/?id=2718c29c3fb9f9de2ec897248ad49ae11ca39b7a'/>
<id>2718c29c3fb9f9de2ec897248ad49ae11ca39b7a</id>
<content type='text'>
This avoids having to traverse and re-read the files that we have just
restored, thereby reducing I/O.

* guix/serialization.scm (dump-file): New procedure.
(restore-file): Add #:dump-file parameter and honor it.
* guix/store/deduplication.scm (tee, dump-file/deduplicate): New
procedures.
* guix/nar.scm (restore-one-item): Pass #:dump-file to 'restore-file'.
(finalize-store-file): Pass #:deduplicate? #f to 'register-items'.
* tests/nar.scm &lt;top level&gt;: Call 'setenv' to set "NIX_STORE".
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
This avoids having to traverse and re-read the files that we have just
restored, thereby reducing I/O.

* guix/serialization.scm (dump-file): New procedure.
(restore-file): Add #:dump-file parameter and honor it.
* guix/store/deduplication.scm (tee, dump-file/deduplicate): New
procedures.
* guix/nar.scm (restore-one-item): Pass #:dump-file to 'restore-file'.
(finalize-store-file): Pass #:deduplicate? #f to 'register-items'.
* tests/nar.scm &lt;top level&gt;: Call 'setenv' to set "NIX_STORE".
</pre>
</div>
</content>
</entry>
<entry>
<title>serialization: 'restore-file' sets canonical timestamp and permissions.</title>
<updated>2020-12-15T16:32:09+00:00</updated>
<author>
<name>Ludovic Courtès</name>
<email>ludo@gnu.org</email>
</author>
<published>2020-12-09T21:16:35+00:00</published>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/commit/?id=ed7d02f7c198970ce3fe94bcee47592963326446'/>
<id>ed7d02f7c198970ce3fe94bcee47592963326446</id>
<content type='text'>
* guix/serialization.scm (restore-file): Set the permissions and mtime
of FILE.
* guix/nar.scm (finalize-store-file): Pass #:reset-timestamps? #f to
'register-items'.
* tests/nar.scm (rm-rf): Add 'chmod' calls to ensure files are writable.
("write-file + restore-file with symlinks"): Ensure every file in OUTPUT
passes 'canonical-file?'.
* tests/guix-archive.sh: Run "chmod -R +w" before "rm -rf".
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
* guix/serialization.scm (restore-file): Set the permissions and mtime
of FILE.
* guix/nar.scm (finalize-store-file): Pass #:reset-timestamps? #f to
'register-items'.
* tests/nar.scm (rm-rf): Add 'chmod' calls to ensure files are writable.
("write-file + restore-file with symlinks"): Ensure every file in OUTPUT
passes 'canonical-file?'.
* tests/guix-archive.sh: Run "chmod -R +w" before "rm -rf".
</pre>
</div>
</content>
</entry>
<entry>
<title>serialization: 'fold-archive' notifies about directory processing completion.</title>
<updated>2020-12-15T16:32:09+00:00</updated>
<author>
<name>Ludovic Courtès</name>
<email>ludo@gnu.org</email>
</author>
<published>2020-12-09T20:50:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/commit/?id=465d2cb286170933577de045e6e6dad7205bfe10'/>
<id>465d2cb286170933577de045e6e6dad7205bfe10</id>
<content type='text'>
* guix/serialization.scm (fold-archive): Call PROC with a
'directory-complete tag when done with a directory.
(restore-file): Handle it.
* guix/scripts/archive.scm (list-contents): Likewise.
* guix/scripts/challenge.scm (archive-contents): Likewise.
* tests/nar.scm ("write-file-tree + fold-archive"): Adjust accordingly.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
* guix/serialization.scm (fold-archive): Call PROC with a
'directory-complete tag when done with a directory.
(restore-file): Handle it.
* guix/scripts/archive.scm (list-contents): Likewise.
* guix/scripts/challenge.scm (archive-contents): Likewise.
* tests/nar.scm ("write-file-tree + fold-archive"): Adjust accordingly.
</pre>
</div>
</content>
</entry>
<entry>
<title>serialization: Add 'fold-archive'.</title>
<updated>2019-12-12T16:56:57+00:00</updated>
<author>
<name>Ludovic Courtès</name>
<email>ludo@gnu.org</email>
</author>
<published>2019-12-04T21:05:31+00:00</published>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/commit/?id=12c1afcdbdc984c760d00932bce64288b385bbc9'/>
<id>12c1afcdbdc984c760d00932bce64288b385bbc9</id>
<content type='text'>
* guix/serialization.scm (read-contents): Remove.
(read-file-type, fold-archive): New procedures.
(restore-file): Rewrite in terms of 'fold-archive'.
* tests/nar.scm ("write-file-tree + fold-archive")
("write-file-tree + fold-archive, flat file"): New tests.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
* guix/serialization.scm (read-contents): Remove.
(read-file-type, fold-archive): New procedures.
(restore-file): Rewrite in terms of 'fold-archive'.
* tests/nar.scm ("write-file-tree + fold-archive")
("write-file-tree + fold-archive, flat file"): New tests.
</pre>
</div>
</content>
</entry>
<entry>
<title>serialization: 'restore-file' errors out upon non-convertible file names.</title>
<updated>2019-01-18T16:51:34+00:00</updated>
<author>
<name>Ludovic Courtès</name>
<email>ludo@gnu.org</email>
</author>
<published>2019-01-18T13:23:31+00:00</published>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/commit/?id=9fe3f11398e858f1d06120bd046cab506efc86dc'/>
<id>9fe3f11398e858f1d06120bd046cab506efc86dc</id>
<content type='text'>
Fixes &lt;https://bugs.gnu.org/33603&gt;.
Reported by Maxim Cournoyer &lt;maxim.cournoyer@gmail.com&gt;.

* guix/serialization.scm (port-conversion-strategy): New variable.
(restore-file): Parameterize it.
* tests/nar.scm ("restore-file with non-UTF8 locale"): New test.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Fixes &lt;https://bugs.gnu.org/33603&gt;.
Reported by Maxim Cournoyer &lt;maxim.cournoyer@gmail.com&gt;.

* guix/serialization.scm (port-conversion-strategy): New variable.
(restore-file): Parameterize it.
* tests/nar.scm ("restore-file with non-UTF8 locale"): New test.
</pre>
</div>
</content>
</entry>
<entry>
<title>deduplication: Restore directory mtime and permissions after deduplication.</title>
<updated>2018-11-13T13:59:46+00:00</updated>
<author>
<name>Ludovic Courtès</name>
<email>ludo@gnu.org</email>
</author>
<published>2018-11-13T13:20:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/commit/?id=f5a2724ae453f4a4b55ff848f4ad7e30efb6eef8'/>
<id>f5a2724ae453f4a4b55ff848f4ad7e30efb6eef8</id>
<content type='text'>
Fixes &lt;https://bugs.gnu.org/33361&gt;.

* guix/store/deduplication.scm (replace-with-link): Call 'set-file-time'
and 'chmod' after 'rename-file'.
* tests/nar.scm ("restore-file-set with directories (signed, valid)"):
New test.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Fixes &lt;https://bugs.gnu.org/33361&gt;.

* guix/store/deduplication.scm (replace-with-link): Call 'set-file-time'
and 'chmod' after 'rename-file'.
* tests/nar.scm ("restore-file-set with directories (signed, valid)"):
New test.
</pre>
</div>
</content>
</entry>
<entry>
<title>tests: Check file canonicalization for 'restore-file-set'.</title>
<updated>2018-11-13T13:59:46+00:00</updated>
<author>
<name>Ludovic Courtès</name>
<email>ludo@gnu.org</email>
</author>
<published>2018-11-13T10:38:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/commit/?id=8390869811f56f5b2ff947efb9d48bcf219a0444'/>
<id>8390869811f56f5b2ff947efb9d48bcf219a0444</id>
<content type='text'>
* guix/tests.scm (canonical-file?): New procedure.
* tests/nar.scm ("restore-file-set (signed, valid)"): Check that every
item of FILES matches 'canonical-file?'.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
* guix/tests.scm (canonical-file?): New procedure.
* tests/nar.scm ("restore-file-set (signed, valid)"): Check that every
item of FILES matches 'canonical-file?'.
</pre>
</div>
</content>
</entry>
<entry>
<title>tests: Remove check for a feature that appeared in Guile 2.0.10.</title>
<updated>2018-11-13T13:59:45+00:00</updated>
<author>
<name>Ludovic Courtès</name>
<email>ludo@gnu.org</email>
</author>
<published>2018-11-13T10:15:41+00:00</published>
<link rel='alternate' type='text/html' href='https://git.vineetk.net/guix/commit/?id=3ed56ad09b1d2be15e98d195b56886ec14899518'/>
<id>3ed56ad09b1d2be15e98d195b56886ec14899518</id>
<content type='text'>
* tests/nar.scm: Remove test for 'open-sha256-input-port'.
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
* tests/nar.scm: Remove test for 'open-sha256-input-port'.
</pre>
</div>
</content>
</entry>
</feed>
