Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package nix for openSUSE:Factory checked in at 2026-04-08 17:15:46 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/nix (Old) and /work/SRC/openSUSE:Factory/.nix.new.21863 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "nix" Wed Apr 8 17:15:46 2026 rev:16 rq:1345082 version:2.34.5 Changes: -------- --- /work/SRC/openSUSE:Factory/nix/nix.changes 2026-03-28 20:15:49.951567742 +0100 +++ /work/SRC/openSUSE:Factory/.nix.new.21863/nix.changes 2026-04-08 17:16:06.852867664 +0200 @@ -1,0 +2,8 @@ +Tue Apr 7 20:26:10 UTC 2026 - Marcus Rueckert <[email protected]> + +- Update to version 2.34.5: + https://discourse.nixos.org/t/nix-security-advisory-privilege-escalation-via-symlink-following-during-fod-output-registration/76900 + - libstore: Use landlock with LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET + for new enough kernels + +------------------------------------------------------------------- Old: ---- nix-2.34.4.tar.gz New: ---- nix-2.34.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ nix.spec ++++++ --- /var/tmp/diff_new_pack.RdLFfz/_old 2026-04-08 17:16:07.480893485 +0200 +++ /var/tmp/diff_new_pack.RdLFfz/_new 2026-04-08 17:16:07.484893649 +0200 @@ -26,7 +26,7 @@ %endif Name: nix -Version: 2.34.4 +Version: 2.34.5 Release: 0 Summary: The purely functional package manager License: LGPL-2.1-only ++++++ nix-2.34.4.tar.gz -> nix-2.34.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/.version new/nix-2.34.5/.version --- old/nix-2.34.4/.version 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/.version 2026-04-06 20:27:24.000000000 +0200 @@ -1 +1 @@ -2.34.4 +2.34.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/packaging/binary-tarball.nix new/nix-2.34.5/packaging/binary-tarball.nix --- old/nix-2.34.4/packaging/binary-tarball.nix 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/packaging/binary-tarball.nix 2026-04-06 20:27:24.000000000 +0200 @@ -79,7 +79,8 @@ fn=$out/$dir.tar.xz mkdir -p $out/nix-support echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products - tar cfJ $fn \ + tar cf - \ + --sort=name \ --owner=0 --group=0 --mode=u+rw,uga+r \ --mtime='1970-01-01' \ --absolute-names \ @@ -95,5 +96,5 @@ $TMPDIR/install-freebsd-multi-user.sh \ $TMPDIR/install-multi-user \ $TMPDIR/reginfo \ - $(cat ${installerClosureInfo}/store-paths) + $(cat ${installerClosureInfo}/store-paths) | xz --threads=1 > $fn '' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/src/libstore/filetransfer.cc new/nix-2.34.5/src/libstore/filetransfer.cc --- old/nix-2.34.4/src/libstore/filetransfer.cc 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/src/libstore/filetransfer.cc 2026-04-06 20:27:24.000000000 +0200 @@ -794,7 +794,7 @@ }; std::priority_queue<ref<TransferItem>, std::vector<ref<TransferItem>>, EmbargoComparator> incoming; - std::vector<ref<TransferItem>> unpause; + std::vector<std::weak_ptr<Item>> unpause; private: bool quitting = false; public: @@ -967,8 +967,15 @@ return res; }(); - for (auto & item : unpause) - item->unpause(); + for (auto & item : unpause) { + /* The transfer might have completed (failed) between it getting + enqueued for unpause and by the time the worker thread picked + it up. */ + auto ptr = item.lock(); + if (!ptr) + continue; + static_cast<TransferItem &>(*ptr).unpause(); + } } debug("download thread shutting down"); @@ -1008,7 +1015,7 @@ } wakeupMulti(); - return ItemHandle(static_cast<Item &>(*item)); + return ItemHandle(item.get_ptr()); } ItemHandle enqueueFileTransfer(const FileTransferRequest & request, Callback<FileTransferResult> callback) override @@ -1023,7 +1030,7 @@ return enqueueItem(make_ref<TransferItem>(*this, request, std::move(callback))); } - void unpauseTransfer(ref<TransferItem> item) + void unpauseTransfer(std::weak_ptr<Item> item) { auto state(state_.lock()); state->unpause.push_back(std::move(item)); @@ -1032,7 +1039,9 @@ void unpauseTransfer(ItemHandle handle) override { - unpauseTransfer(ref{static_cast<TransferItem &>(handle.item.get()).shared_from_this()}); + /* The transfer might have completed (more likely failed) when we want + to wake it up. That's why we must use a weak_ptr throughout. */ + unpauseTransfer(handle.item); } }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/src/libstore/include/nix/store/filetransfer.hh new/nix-2.34.5/src/libstore/include/nix/store/filetransfer.hh --- old/nix-2.34.4/src/libstore/include/nix/store/filetransfer.hh 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/src/libstore/include/nix/store/filetransfer.hh 2026-04-06 20:27:24.000000000 +0200 @@ -337,10 +337,10 @@ */ struct ItemHandle { - std::reference_wrapper<Item> item; + std::weak_ptr<Item> item; friend struct FileTransfer; - ItemHandle(Item & item) + explicit ItemHandle(std::weak_ptr<Item> item) : item(item) { } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/src/libstore/include/nix/store/local-store.hh new/nix-2.34.5/src/libstore/include/nix/store/local-store.hh --- old/nix-2.34.4/src/libstore/include/nix/store/local-store.hh 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/src/libstore/include/nix/store/local-store.hh 2026-04-06 20:27:24.000000000 +0200 @@ -505,6 +505,8 @@ friend struct PathSubstitutionGoal; friend struct DerivationGoal; + /* Only used for createTempDirInStore. */ + friend class DerivationBuilderImpl; }; } // namespace nix diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/src/libstore/local-store.cc new/nix-2.34.5/src/libstore/local-store.cc --- old/nix-2.34.4/src/libstore/local-store.cc 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/src/libstore/local-store.cc 2026-04-06 20:27:24.000000000 +0200 @@ -1283,8 +1283,9 @@ do { /* There is a slight possibility that `tmpDir' gets deleted by the GC between createTempDir() and when we acquire a lock on it. - We'll repeat until 'tmpDir' exists and we've locked it. */ - tmpDirFn = createTempDir(std::filesystem::path{config->realStoreDir.get()}, "tmp"); + We'll repeat until 'tmpDir' exists and we've locked it. + Make the directory accessible only to the current user. */ + tmpDirFn = createTempDir(std::filesystem::path{config->realStoreDir.get()}, "tmp", /*mode=*/0700); tmpDirFd = openDirectory(tmpDirFn); if (!tmpDirFd) { continue; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/src/libstore/meson.build new/nix-2.34.5/src/libstore/meson.build --- old/nix-2.34.4/src/libstore/meson.build 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/src/libstore/meson.build 2026-04-06 20:27:24.000000000 +0200 @@ -79,6 +79,11 @@ configdata_priv.set(define_name, define_value) endforeach +if host_machine.system() == 'linux' + has_landlock = cxx.has_header('linux/landlock.h') + configdata_priv.set('HAVE_LANDLOCK', has_landlock.to_int()) +endif + has_acl_support = cxx.has_header('sys/xattr.h') \ and cxx.has_function('llistxattr') \ and cxx.has_function('lremovexattr') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/src/libstore/unix/build/derivation-builder.cc new/nix-2.34.5/src/libstore/unix/build/derivation-builder.cc --- old/nix-2.34.4/src/libstore/unix/build/derivation-builder.cc 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/src/libstore/unix/build/derivation-builder.cc 2026-04-06 20:27:24.000000000 +0200 @@ -1595,6 +1595,13 @@ assert(output && scratchPath); auto actualPath = realPathInHost(store.printStorePath(*scratchPath)); + /* An optional file descriptor of a directory used for intermediate + operations. */ + AutoCloseFD tempDirFd; + /* RAII cleanup of a temporary directory inside the store that is used + for intermediate operations. */ + AutoDelete delTempDir; + auto finish = [&](StorePath finalStorePath) { /* Store the final path */ finalOutputs.insert_or_assign(outputName, finalStorePath); @@ -1742,6 +1749,25 @@ return newInfo0; }; + auto moveOutputToTempDir = [&]() -> void { + std::filesystem::path tempDir; + std::tie(tempDir, tempDirFd) = store.createTempDirInStore(); + delTempDir = AutoDelete(tempDir); + + auto tmpOutput = tempDir / "x"; + + /* Serialise and create a fresh copy of the output to break + any stale writable file descriptors. Copy through the + serialisation/deserialisation. TODO: Use copyRecursive here and + make use of reflinking. */ + auto source = sinkToSource([&](Sink & nextSink) { dumpPath(actualPath, nextSink); }); + restorePath(tmpOutput, *source, store.config->getLocalSettings().fsyncStorePaths); + /* This makes it slightly harder to make sense of the control flow. The rule + of thumb is that actualPath points to the current location of the stuff + that we'll end up registering. */ + actualPath = std::move(tmpOutput); + }; + ValidPathInfo newInfo = std::visit( overloaded{ @@ -1769,14 +1795,7 @@ [&](const DerivationOutput::CAFixed & dof) { auto & wanted = dof.ca.hash; - - // Replace the output by a fresh copy of itself to make sure - // that there's no stale file descriptor pointing to it - std::filesystem::path tmpOutput = actualPath.native() + ".tmp"; - copyFile(actualPath, tmpOutput, true); - - std::filesystem::rename(tmpOutput, actualPath); - + moveOutputToTempDir(); return newInfoFromCA( DerivationOutput::CAFloating{ .method = dof.ca.method, @@ -1793,6 +1812,7 @@ }, [&](const DerivationOutput::Impure & doi) { + moveOutputToTempDir(); return newInfoFromCA( DerivationOutput::CAFloating{ .method = doi.method, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/src/libstore/unix/build/linux-derivation-builder.cc new/nix-2.34.5/src/libstore/unix/build/linux-derivation-builder.cc --- old/nix-2.34.4/src/libstore/unix/build/linux-derivation-builder.cc 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/src/libstore/unix/build/linux-derivation-builder.cc 2026-04-06 20:27:24.000000000 +0200 @@ -1,5 +1,7 @@ #ifdef __linux__ +# include "store-config-private.hh" + # include "nix/store/globals.hh" # include "nix/store/personality.hh" # include "nix/store/filetransfer.hh" @@ -9,6 +11,10 @@ # include "nix/util/serialise.hh" # include "linux/fchmodat2-compat.hh" +# include <algorithm> +# include <string_view> +# include <cstdint> + # include <sys/ioctl.h> # include <net/if.h> # include <netinet/ip.h> @@ -17,11 +23,16 @@ # include <sys/param.h> # include <sys/mount.h> # include <sys/syscall.h> +# include <sys/prctl.h> # if HAVE_SECCOMP # include <seccomp.h> # endif +# if HAVE_LANDLOCK +# include <linux/landlock.h> +# endif + # define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) namespace nix { @@ -125,6 +136,77 @@ # endif } +# if HAVE_LANDLOCK && defined(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET) + +# define DO_LANDLOCK 1 + +/* We are using LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET on best-effort basis. There are no glibc wrappers for now. */ + +static int landlockCreateRuleset(const ::landlock_ruleset_attr * attr, std::size_t size, std::uint32_t flags) +{ + return ::syscall(__NR_landlock_create_ruleset, attr, size, flags); +} + +static int landlockRestrictSelf(Descriptor rulesetFd, std::uint32_t flags) +{ + return ::syscall(__NR_landlock_restrict_self, rulesetFd, flags); +} + +static int getLandlockAbiVersion() +{ + int abiVersion = landlockCreateRuleset(nullptr, 0, LANDLOCK_CREATE_RULESET_VERSION); + return abiVersion; +} + +static void setupLandlock() +{ + bool landlockSupportsScopeAbstractUnixSocket = []() { + int abiVersion = getLandlockAbiVersion(); + if (abiVersion >= 6) + /* All good, we can use LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET. See + https://docs.kernel.org/userspace-api/landlock.html#abstract-unix-socket-abi-6 */ + return true; + + if (abiVersion == -1) { + debug("landlock is not available"); + return false; + } + + debug("landlock version %d does not support LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET", abiVersion); + return false; + }(); + + /* Bail out early if landlock is not enabled or LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET wouldn't work. + TODO: Consider adding more landlock rules for filesystem access as defense-in-depth on top. */ + if (!landlockSupportsScopeAbstractUnixSocket) + return; + + ::landlock_ruleset_attr attr = { + /* This prevents multiple FODs from communicating with each other + via abstract sockets. Note that cooperating processes outside the + sandbox can still connect to an abstract socket created by the FOD. To + mitigate that issue entirely we'd still need network namespaces. */ + .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, + }; + + /* This better not fail - if the kernel reports a new enough ABI version we + should treat any errors as fatal from now on. */ + AutoCloseFD rulesetFd = landlockCreateRuleset(&attr, sizeof(attr), 0); + if (!rulesetFd) + throw SysError("failed to create a landlock ruleset"); + + if (landlockRestrictSelf(rulesetFd.get(), 0) == -1) + throw SysError("failed to apply landlock"); + + debug("applied landlock sandboxing"); +} + +# else + +# define DO_LANDLOCK 0 + +# endif + static void doBind(const std::filesystem::path & source, const std::filesystem::path & target, bool optional = false) { debug("bind mounting %1% to %2%", PathFmt(source), PathFmt(target)); @@ -165,8 +247,27 @@ { auto & localSettings = store.config->getLocalSettings(); + /* Set the NO_NEW_PRIVS before doing seccomp/landlock setup. + landlock_restrict_self requires either NO_NEW_PRIVS or CAP_SYS_ADMIN. + With user namespaces we do get CAP_SYS_ADMIN. */ + if (!localSettings.allowNewPrivileges) + if (::prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) + throw SysError("failed to set PR_SET_NO_NEW_PRIVS"); + setupSeccomp(localSettings); +# if DO_LANDLOCK + try { + setupLandlock(); + } catch (SysError & e) { + if (e.errNo != EPERM) + throw; + /* If allowNewPrivileges is true and we don't have CAP_SYS_ADMIN + this code path might be hit. */ + warn("setting up landlock: %s", e.message()); + } +# endif + linux::setPersonality({ .system = drv.platform, .impersonateLinux26 = localSettings.impersonateLinux26, @@ -760,4 +861,6 @@ } // namespace nix +# undef DO_LANDLOCK + #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/tests/functional/local-overlay-store/stale-file-handle-inner.sh new/nix-2.34.5/tests/functional/local-overlay-store/stale-file-handle-inner.sh --- old/nix-2.34.4/tests/functional/local-overlay-store/stale-file-handle-inner.sh 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/tests/functional/local-overlay-store/stale-file-handle-inner.sh 2026-04-06 20:27:24.000000000 +0200 @@ -36,8 +36,12 @@ buildInStore "$storeB" } -# Without remounting, we should encounter errors -expectStderr 1 triggerStaleFileHandle | grepQuiet 'Stale file handle' +# Without remounting, we should encounter errors. However, this doesn't seem to +# happen on Linux 6.19+ anymore. +# +# See https://github.com/NixOS/nixpkgs/issues/496466 +( expectStderr 1 triggerStaleFileHandle | grepQuiet 'Stale file handle' ) || \ + skipTest "Couldn't trigger the error" # Configure remount-hook and reset OverlayFS storeB="$storeB&remount-hook=$PWD/remount.sh" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nix-2.34.4/tests/nixos/ca-fd-leak/default.nix new/nix-2.34.5/tests/nixos/ca-fd-leak/default.nix --- old/nix-2.34.4/tests/nixos/ca-fd-leak/default.nix 2026-03-25 22:29:36.000000000 +0100 +++ new/nix-2.34.5/tests/nixos/ca-fd-leak/default.nix 2026-04-06 20:27:24.000000000 +0200 @@ -78,7 +78,7 @@ # Build the smuggled derivation. # This will connect to the smuggler server and send it the file descriptor - machine.succeed(r""" + sender_output = machine.succeed(r""" nix-build -E ' builtins.derivation { name = "smuggled"; @@ -89,9 +89,13 @@ outputHash = builtins.hashString "sha256" "hello, world\n"; builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ]; - }' + }' 2>&1 """.strip()) + # Landlock's LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET prevents a sandboxed process + # from connecting to an abstract socket created in an unrelated landlock domain. + # There's no such flag for preventing inbound connections. + assert "connect: Operation not permitted" in sender_output # Tell the smuggler server that we're done machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}")
