Compare commits

..
12 Commits
Author SHA1 Message Date
Raito Bezarius 600aff6dec release: 2.91.2 "Dragon's Breath"
Release produced with releng/create_release.xsh

Change-Id: Iaae75be51d51a553c29720e00f98565d314783bb
2025-06-24 00:18:54 +02:00
Raito Bezarius de67cf2666 release: release notes for 2.91.2
Release created with releng/create_release.xsh

Change-Id: Idf78ea5315a8e6c3f44085605f2b2884754783ea
2025-06-24 00:17:16 +02:00
Raito Bezarius 013df0ae74 version: 2.91.1 -> 2.91.2
Fixes CVE-2025-46415, CVE-2025-46416, CVE-2025-52991, CVE-2025-52992,
and CVE-2025-52993.

Change-Id: Ie1d97a4e1569bf3b601753665b20cdeb5427e5ca
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-24 00:17:16 +02:00
Raito Bezarius 875bf80541 flake: bump nix2container
skopeo had a patch that doesn't apply anymore.

That's unfortunate. We force bump nix2container to resume releng
building.

Change-Id: Ic04d962bd444fd5619de6e0842d3535f8bd1b296
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-24 00:17:16 +02:00
Raito Bezariusandeldritch horrors 3401d234f8 libstore/build: automatic clean up of unsuccessfully built scratch outputs
When a build fails, its scratch output paths are not cleaned up.

Until recently, this was deemed not a problem but as part of the effort
to harden the Nix builds and protect these paths against being part of a
staged attack (race conditions, etc.), we automatically cleanup after
failed builds.

Change-Id: I58481b1cc83826298b9d80d37fecf81f117ccb09
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-23 20:46:36 +00:00
eldritch horrors a53f28aa80 libstore: don't default build-dir to temp-dir
if a build directory is accessible to other users it is possible to
smuggle data in and out of build directories. usually this ins only
a build purity problem, but in combination with other issues it can
be used to break out of a build sandbox. to prevent this we default
to using a subdirectory of nixStateDir (which is more restrictive).

Change-Id: Iacfc9b50534de158618c815f9fb99d7dae1be4d0
2025-06-23 20:46:15 +00:00
eldritch horrorsandPuck Meerburg f58b74cb5b libstore: use pasta for FODs if available
This allows using a userspace program, pasta, to handle comms between
the build sandbox, and the outside world; allowing for full isolation
including the network namespace, closing the "fixed-output derivation
talks to the host over an abstract domain socket" hole for good.

Co-Authored-By: Puck Meerburg <puck@puckipedia.com>
Change-Id: Ifd499b7dbb3784600a6e842fede65fc031ff9f15
2025-06-23 20:45:08 +00:00
eldritch horrorsandRaito Bezarius 3ba1c8dab8 libutil: add capability support to runProgram2
launching pasta to not run as root will ambient require capabilities.

Change-Id: I1dd2506a1fa3944a9d9062123ef8a74903c597ea
2025-06-23 16:44:49 +02:00
eldritch horrorsandRaito Bezarius 395661e833 libutil: add generic redirections runProgram2
explicit stderr redirection makes mergeStderrToStdout unnecessary also.

Change-Id: I63de929e6dc53f6c5ceb2d43c2ce288bfc04d872
2025-06-23 16:44:49 +02:00
eldritch horrorsandRaito Bezarius b92ac2da34 libutil: make RunningProgram more useful
make it moveable, make it killable, and add a stdout fd accessor.

Change-Id: I2387cbe8ac67b899a322cd6c7d306ef9ea7abcd0
2025-06-23 16:44:49 +02:00
Raito Bezarius 255704a98c libutil: ensure that _deletePath does NOT use absolute paths with dirfds
When calling `_deletePath` with a parent file descriptor, `openat` is
made effective by using relative paths to the directory file descriptor.

To avoid the problem, the signature is changed to resist misuse with an
assert in the prologue of the function.

Change-Id: I6b3fc766bad2afe54dc27d47d1df3873e188de96
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-23 16:44:49 +02:00
Raito Bezarius a6dcd6bae0 libstore: ensure that passAsFile is created in the original temp dir
This ensures that `passAsFile` data is created inside the expected
temporary build directory by `openat()` from the parent directory file
descriptor.

This avoids a TOCTOU which is part of the attack chain of CVE-????.

Change-Id: Ie5273446c4a19403088d0389ae8e3f473af8879a
Signed-off-by: Raito Bezarius <raito@lix.systems>
2025-06-23 16:44:44 +02:00
52 changed files with 317 additions and 485 deletions
-3
View File
@@ -16,8 +16,6 @@ Checks:
- -bugprone-unchecked-optional-access - -bugprone-unchecked-optional-access
# many warnings, seems like a questionable lint # many warnings, seems like a questionable lint
- -bugprone-branch-clone - -bugprone-branch-clone
# extremely noisy before clang 19: https://github.com/llvm/llvm-project/issues/93959
- -bugprone-multi-level-implicit-pointer-conversion
# all thrown exceptions must derive from std::exception # all thrown exceptions must derive from std::exception
- hicpp-exception-baseclass - hicpp-exception-baseclass
# capturing async lambdas are dangerous # capturing async lambdas are dangerous
@@ -35,4 +33,3 @@ Checks:
CheckOptions: CheckOptions:
bugprone-reserved-identifier.AllowedIdentifiers: '__asan_default_options' bugprone-reserved-identifier.AllowedIdentifiers: '__asan_default_options'
bugprone-unused-return-value.AllowCastToVoid: true
-55
View File
@@ -1,59 +1,4 @@
# Lix 2.91 "Dragon's Breath" (2024-08-12) # Lix 2.91 "Dragon's Breath" (2024-08-12)
# Lix 2.91.3 (2025-06-30)
## Fixes
- Revert CVE-2025-52992 failed mitigation [fj#883](https://git.lix.systems/lix-project/lix/issues/883) [fj#887](https://git.lix.systems/lix-project/lix/issues/887) [cl/3420](https://gerrit.lix.systems/c/lix/+/3420) [cl/3524](https://gerrit.lix.systems/c/lix/+/3524) [cl/3523](https://gerrit.lix.systems/c/lix/+/3523) [cl/3522](https://gerrit.lix.systems/c/lix/+/3522)
Following the initial mitigation of **CVE-2025-52992** in `cl/3420`, we
received reports of **unexpected deletion of in-use store paths**.
Upon investigation, we found that the patch did **not correctly cancel all
automatic deleters**, resulting in potentially critical path loss during normal
operation.
Given the severity and time-sensitive nature of the situation ([see incident
report](https://lix.systems/blog/2025-06-27-lix-critical-bug/)), we evaluated
possible options to repair the behavior safely. However, we concluded that a
rushed fix would either:
* **Overdelete**, i.e. breaking running systems, or,
* **Underdelete**, effectively **reopening CVE-2025-52992** while leaving
orphaned paths behind.
As **CVE-2025-52992 has no known exploit vector**, and correctness is critical
in the Lix project, we have **fully reverted the previous mitigations**.
Moving forward, the Lix team will rework this code path in a **long-term,
correctness-first fix** on the main branch. We will explore backporting it to
stable channels once its safety is assured.
We are deeply sorry for the stability incident and the Lix team remain
available for assisting you in recovering your systems.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) and [eldritch horrors](https://git.lix.systems/pennae) for this.
- Fallback to safe temp dir when build-dir is unwritable [fj#876](https://git.lix.systems/lix-project/lix/issues/876) [cl/3502](https://gerrit.lix.systems/c/lix/+/3502)
Non-daemon builds started failing with a permission error after introducing the `build-dir` option:
```
$ nix build --store ~/scratch nixpkgs#hello --rebuild
error: creating directory '/nix/var/nix/builds/nix-build-hello-2.12.2.drv-0': Permission denied
```
This happens because:
1. These builds are not run via the daemon, which owns `/nix/var/nix/builds`.
2. The user lacks permissions for that path.
We considered making `build-dir` a store-level option and defaulting it to `<chroot-root>/nix/var/nix/builds` for chroot stores, but opted instead for a fallback: if the default fails, Nix now creates a safe build directory under `/tmp`.
To avoid CVE-2025-52991, the fallback uses an extra path component between `/tmp` and the build dir.
**Note**: this fallback clutters `/tmp` with build directories that are not cleaned up. To prevent this, explicitly set `build-dir` to a path managed by Lix, even for local workloads.
Many thanks to [Raito Bezarius](https://git.lix.systems/raito) and [eldritch horrors](https://git.lix.systems/pennae) for this.
# Lix 2.91.2 (2025-06-23) # Lix 2.91.2 (2025-06-23)
## Breaking Changes ## Breaking Changes
+39 -38
View File
@@ -62,37 +62,38 @@ let
++ autoLayered ++ autoLayered
++ extraPkgs; ++ extraPkgs;
users = { users =
{
root = { root = {
uid = 0; uid = 0;
shell = "${pkgs.bashInteractive}/bin/bash"; shell = "${pkgs.bashInteractive}/bin/bash";
home = "/root"; home = "/root";
gid = 0; gid = 0;
groups = [ "root" ]; groups = [ "root" ];
description = "System administrator"; description = "System administrator";
};
nobody = {
uid = 65534;
shell = "${pkgs.shadow}/bin/nologin";
home = "/var/empty";
gid = 65534;
groups = [ "nobody" ];
description = "Unprivileged account (don't use!)";
};
}
// lib.listToAttrs (
map (n: {
name = "nixbld${toString n}";
value = {
uid = 30000 + n;
gid = 30000;
groups = [ "nixbld" ];
description = "Nix build user ${toString n}";
}; };
}) (lib.lists.range 1 32)
); nobody = {
uid = 65534;
shell = "${pkgs.shadow}/bin/nologin";
home = "/var/empty";
gid = 65534;
groups = [ "nobody" ];
description = "Unprivileged account (don't use!)";
};
}
// lib.listToAttrs (
map (n: {
name = "nixbld${toString n}";
value = {
uid = 30000 + n;
gid = 30000;
groups = [ "nixbld" ];
description = "Nix build user ${toString n}";
};
}) (lib.lists.range 1 32)
);
groups = { groups = {
root.gid = 0; root.gid = 0;
@@ -192,11 +193,13 @@ let
in in
'' ''
{ {
${lib.concatStringsSep "\n" ( ${
builtins.map (output: '' lib.concatStringsSep "\n" (
${output} = { outPath = "${lib.getOutput output drv}"; }; builtins.map (output: ''
'') outputs ${output} = { outPath = "${lib.getOutput output drv}"; };
)} '') outputs
)
}
outputs = [ ${lib.concatStringsSep " " (builtins.map (x: "\"${x}\"") outputs)} ]; outputs = [ ${lib.concatStringsSep " " (builtins.map (x: "\"${x}\"") outputs)} ];
name = "${drv.name}"; name = "${drv.name}";
outPath = "${drv}"; outPath = "${drv}";
@@ -358,10 +361,8 @@ let
"org.opencontainers.image.source" = "https://git.lix.systems/lix-project/lix"; "org.opencontainers.image.source" = "https://git.lix.systems/lix-project/lix";
"org.opencontainers.image.vendor" = "Lix project"; "org.opencontainers.image.vendor" = "Lix project";
"org.opencontainers.image.version" = pkgs.nix.version; "org.opencontainers.image.version" = pkgs.nix.version;
"org.opencontainers.image.description" = "org.opencontainers.image.description" = "Minimal Lix container image, with some batteries included.";
"Minimal Lix container image, with some batteries included."; } // lib.optionalAttrs (lixRevision != null) { "org.opencontainers.image.revision" = lixRevision; };
}
// lib.optionalAttrs (lixRevision != null) { "org.opencontainers.image.revision" = lixRevision; };
}; };
meta = { meta = {
Generated
+8 -67
View File
@@ -16,22 +16,6 @@
"type": "github" "type": "github"
} }
}, },
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1633514407,
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"nix2container": { "nix2container": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -48,44 +32,18 @@
"type": "github" "type": "github"
} }
}, },
"nix_2_18": {
"inputs": {
"flake-compat": [
"flake-compat"
],
"lowdown-src": "lowdown-src",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": [
"nixpkgs-regression"
]
},
"locked": {
"lastModified": 1730375271,
"narHash": "sha256-RrOFlDGmRXcVRV2p2HqHGqvzGNyWoD0Dado/BNlJ1SI=",
"owner": "NixOS",
"repo": "nix",
"rev": "0f665ff6779454f2117dcc32e44380cda7f45523",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "2.18.9",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1705033721, "lastModified": 1735651292,
"narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", "narHash": "sha256-YLbzcBtYo1/FEzFsB3AnM16qFc6fWPMIoOuSoDwvg9g=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", "rev": "0da3c44a9460a26d2025ec3ed2ec60a895eb1114",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-23.05-small", "ref": "nixos-24.05-small",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
@@ -106,30 +64,14 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_2": {
"locked": {
"lastModified": 1757198069,
"narHash": "sha256-m3VUcOD4rTs8J7S+3dOjWMrAjw6RcITC3XYQ98zhEFs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0747026fc57ecb9c28901c7f7a2b5dc40e8af43c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.05-small",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": { "pre-commit-hooks": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1733318908, "lastModified": 1721042469,
"narHash": "sha256-SVQVsbafSM1dJ4fpgyBqLZ+Lft+jcQuMtEL3lQWx2Sk=", "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=",
"owner": "cachix", "owner": "cachix",
"repo": "git-hooks.nix", "repo": "git-hooks.nix",
"rev": "6f4e2a2112050951a314d2733a994fbab94864c6", "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -142,8 +84,7 @@
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"nix2container": "nix2container", "nix2container": "nix2container",
"nix_2_18": "nix_2_18", "nixpkgs": "nixpkgs",
"nixpkgs": "nixpkgs_2",
"nixpkgs-regression": "nixpkgs-regression", "nixpkgs-regression": "nixpkgs-regression",
"pre-commit-hooks": "pre-commit-hooks" "pre-commit-hooks": "pre-commit-hooks"
} }
+8 -33
View File
@@ -2,19 +2,8 @@
description = "Lix: A modern, delicious implementation of the Nix package manager"; description = "Lix: A modern, delicious implementation of the Nix package manager";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05-small"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05-small";
nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
# Required because Nix 2.18 is not in Nixpkgs ≥ 25.05 anymore.
nix_2_18 = {
url = "github:NixOS/nix/2.18.9";
# NOTE(Raito): this is not possible because patches on libseccomp does not apply anymore on this Nix.
# Let's keep the latest known nixpkgs useable with Nix 2.18 for our tests.
# inputs.nixpkgs.follows = "nixpkgs";
inputs.nixpkgs-regression.follows = "nixpkgs-regression";
inputs.flake-compat.follows = "flake-compat";
};
pre-commit-hooks = { pre-commit-hooks = {
url = "github:cachix/git-hooks.nix"; url = "github:cachix/git-hooks.nix";
flake = false; flake = false;
@@ -36,7 +25,6 @@
nixpkgs-regression, nixpkgs-regression,
pre-commit-hooks, pre-commit-hooks,
nix2container, nix2container,
nix_2_18,
flake-compat, flake-compat,
}: }:
@@ -173,16 +161,6 @@
{ {
nixStable = prev.nix; nixStable = prev.nix;
nixVersions = prev.nixVersions // {
nix_2_3 = prev.nixVersions.nix_2_3.overrideAttrs (old: {
meta = old.meta // {
knownVulnerabilities = [ ];
};
});
# Nix 2.18 has been removed from Nixpkgs ≥ 25.05, so we need to reintroduce it ourselves for our tests.
nix_2_18 = nix_2_18.outputs.packages.${currentStdenv.hostPlatform.system}.default;
};
# Forward from the previous stage as we dont want it to pick the lowdown override # Forward from the previous stage as we dont want it to pick the lowdown override
nixUnstable = prev.nixUnstable; nixUnstable = prev.nixUnstable;
@@ -368,16 +346,13 @@
in in
pkgs.symlinkJoin { pkgs.symlinkJoin {
name = "nixpkgs-lib-tests"; name = "nixpkgs-lib-tests";
paths = [ paths =
testWithNix [ testWithNix ]
] # FIXME: This is disabled on darwin due to a nixpkgs bug https://github.com/NixOS/nixpkgs/issues/319147
# NOTE: nixpkgs 25.05 is being ... *creative*, and requires this dance to override # After that is fixed, it should be restored to use lib/tests/release.nix as before, rather than this reimplementation.
# the evaluator used for the test. it will break again in the future, don't worry. ++ lib.optionals pkgs.stdenv.isLinux [
++ lib.optionals pkgs.stdenv.isLinux [ (import (nixpkgs + "/pkgs/test/release") { inherit pkgs lib nix; })
((pkgs.callPackage "${nixpkgs}/ci/eval" { inherit nix; }).attrpathsSuperset { ];
evalSystem = system;
})
];
} }
); );
}; };
-6
View File
@@ -301,11 +301,6 @@ editline = dependency('libeditline', 'editline', version : '>=1.14', required :
lowdown = dependency('lowdown', version : '>=0.9.0', required : true, include_type : 'system') lowdown = dependency('lowdown', version : '>=0.9.0', required : true, include_type : 'system')
# TODO(sterni): drop the corresponding #ifdef after NixOS 25.05 is EOL which still distributes lowdown < 1.4.0
if lowdown.version().version_compare('>= 1.4.0')
add_project_arguments('-DLOWDOWN_SEPARATE_TERM_OPTS', language: 'cpp')
endif
# HACK(Qyriad): rapidcheck's pkg-config doesn't include the libs lol # HACK(Qyriad): rapidcheck's pkg-config doesn't include the libs lol
# Note: technically we 'check' for rapidcheck twice, for the internal-api-docs handling above, # Note: technically we 'check' for rapidcheck twice, for the internal-api-docs handling above,
# but Meson will cache the result of the first one, and the required : arguments are different. # but Meson will cache the result of the first one, and the required : arguments are different.
@@ -480,7 +475,6 @@ add_project_arguments(
# It would be nice for our headers to be idempotent instead. # It would be nice for our headers to be idempotent instead.
'-include', 'config.h', '-include', 'config.h',
'-Wno-deprecated-declarations', '-Wno-deprecated-declarations',
'-Wno-missing-field-initializers',
'-Wimplicit-fallthrough', '-Wimplicit-fallthrough',
'-Werror=switch', '-Werror=switch',
'-Werror=switch-enum', '-Werror=switch-enum',
+67 -65
View File
@@ -179,13 +179,12 @@ stdenv.mkDerivation (finalAttrs: {
VERSION_SUFFIX = versionSuffix; VERSION_SUFFIX = versionSuffix;
outputs = [ outputs =
"out" [ "out" ]
] ++ lib.optionals (!finalAttrs.dontBuild) [
++ lib.optionals (!finalAttrs.dontBuild) [ "dev"
"dev" "doc"
"doc" ];
];
dontBuild = lintInsteadOfBuild; dontBuild = lintInsteadOfBuild;
@@ -221,65 +220,67 @@ stdenv.mkDerivation (finalAttrs: {
# We only include CMake so that Meson can locate toml11, which only ships CMake dependency metadata. # We only include CMake so that Meson can locate toml11, which only ships CMake dependency metadata.
dontUseCmakeConfigure = true; dontUseCmakeConfigure = true;
nativeBuildInputs = [ nativeBuildInputs =
python3 [
meson python3
ninja meson
cmake ninja
] cmake
++ [ ]
(lib.getBin lowdown-unsandboxed) ++ [
mdbook (lib.getBin lowdown-unsandboxed)
mdbook-linkcheck mdbook
] mdbook-linkcheck
++ [ ]
pkg-config ++ [
pkg-config
# Tests # Tests
git git
mercurial mercurial
jq jq
lsof lsof
] ]
++ lib.optional hostPlatform.isLinux util-linuxMinimal ++ lib.optional hostPlatform.isLinux util-linuxMinimal
++ lib.optional (!officialRelease && buildUnreleasedNotes) build-release-notes ++ lib.optional (!officialRelease && buildUnreleasedNotes) build-release-notes
++ lib.optional internalApiDocs doxygen ++ lib.optional internalApiDocs doxygen
++ lib.optionals lintInsteadOfBuild [ ++ lib.optionals lintInsteadOfBuild [
# required for a wrapped clang-tidy # required for a wrapped clang-tidy
llvmPackages.clang-tools llvmPackages.clang-tools
# required for run-clang-tidy # required for run-clang-tidy
llvmPackages.clang-unwrapped llvmPackages.clang-unwrapped
]; ];
buildInputs = [ buildInputs =
curl [
bzip2 curl
xz bzip2
brotli xz
editline-lix brotli
openssl editline-lix
sqlite openssl
libarchive sqlite
boost libarchive
lowdown boost
libsodium lowdown
toml11 libsodium
lix-doc toml11
pegtl lix-doc
] pegtl
++ lib.optionals hostPlatform.isLinux [ ]
libseccomp ++ lib.optionals hostPlatform.isLinux [
busybox-sandbox-shell libseccomp
passt-lix busybox-sandbox-shell
] passt-lix
++ lib.optional internalApiDocs rapidcheck ]
++ lib.optional hostPlatform.isx86_64 libcpuid ++ lib.optional internalApiDocs rapidcheck
# There have been issues building these dependencies ++ lib.optional hostPlatform.isx86_64 libcpuid
++ lib.optional (hostPlatform.canExecute buildPlatform) aws-sdk-cpp-nix # There have been issues building these dependencies
++ lib.optionals (finalAttrs.dontBuild) maybePropagatedInputs ++ lib.optional (hostPlatform.canExecute buildPlatform) aws-sdk-cpp-nix
# I am so sorry. This is because checkInputs are required to pass ++ lib.optionals (finalAttrs.dontBuild) maybePropagatedInputs
# configure, but we don't actually want to *run* the checks here. # I am so sorry. This is because checkInputs are required to pass
++ lib.optionals lintInsteadOfBuild finalAttrs.checkInputs; # configure, but we don't actually want to *run* the checks here.
++ lib.optionals lintInsteadOfBuild finalAttrs.checkInputs;
checkInputs = [ checkInputs = [
gtest gtest
@@ -426,6 +427,7 @@ stdenv.mkDerivation (finalAttrs: {
just, just,
nixfmt-rfc-style, nixfmt-rfc-style,
skopeo, skopeo,
xonsh,
# Lix specific packages # Lix specific packages
pre-commit-checks, pre-commit-checks,
@@ -445,7 +447,7 @@ stdenv.mkDerivation (finalAttrs: {
p.requests p.requests
p.xdg-base-dirs p.xdg-base-dirs
p.packaging p.packaging
p.xonsh (p.toPythonModule xonsh.passthru.unwrapped)
] ]
); );
pythonEnv = python3.withPackages pythonPackages; pythonEnv = python3.withPackages pythonPackages;
+13 -12
View File
@@ -35,18 +35,19 @@ perl.pkgs.toPerlModule (
ninja ninja
]; ];
buildInputs = [ buildInputs =
nix [
curl nix
bzip2 curl
xz bzip2
perl xz
boost perl
perlPackages.DBI boost
perlPackages.DBDSQLite perlPackages.DBI
] perlPackages.DBDSQLite
++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium ]
++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security; ++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium
++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security;
# Nixpkgs' Meson hook likes to set this to "plain". # Nixpkgs' Meson hook likes to set this to "plain".
mesonBuildType = "debugoptimized"; mesonBuildType = "debugoptimized";
+4 -1
View File
@@ -19,7 +19,10 @@ endif
cxx = meson.get_compiler('cpp') cxx = meson.get_compiler('cpp')
perl = find_program('perl') # Really version 5.8.0, but Perl's version string is of the form
# "This is perl 5, version 38, subversion 2", for 5.38.2, so as far
# as Meson is concerned, the version of Perl we need is 8 or greater.
perl = find_program('perl', version : '>=8')
# "compiler to convert Perl XS code into C code" # "compiler to convert Perl XS code into C code"
xsubpp = find_program('xsubpp') xsubpp = find_program('xsubpp')
+2 -17
View File
@@ -11,28 +11,13 @@ namespace nix {
std::string renderMarkdownToTerminal(std::string_view markdown) std::string renderMarkdownToTerminal(std::string_view markdown)
{ {
int windowWidth = getWindowSize().second; int windowWidth = getWindowSize().second;
size_t lowdown_cols = std::max(windowWidth - 5, 60);
struct lowdown_opts opts{ struct lowdown_opts opts {
.type = LOWDOWN_TERM, .type = LOWDOWN_TERM,
#ifdef LOWDOWN_SEPARATE_TERM_OPTS
.term =
{
.cols = lowdown_cols,
.width = 0,
.hmargin = 0,
.hpadding = 4,
.vmargin = 0,
.centre = 0,
},
// maxdepth needs to be part of the ifdefs to match declaration order
.maxdepth = 20, .maxdepth = 20,
#else .cols = (size_t) std::max(windowWidth - 5, 60),
.maxdepth = 20,
.cols = lowdown_cols,
.hmargin = 0, .hmargin = 0,
.vmargin = 0, .vmargin = 0,
#endif /* LOWDOWN_SEPARATE_TERM_OPTS */
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES, .feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
.oflags = LOWDOWN_TERM_NOLINK, .oflags = LOWDOWN_TERM_NOLINK,
}; };
+1
View File
@@ -129,6 +129,7 @@ std::pair<SourcePath, uint32_t> findPackageFilename(EvalState & state, Value & v
try { try {
auto colon = fn.rfind(':'); auto colon = fn.rfind(':');
if (colon == std::string::npos) fail(); if (colon == std::string::npos) fail();
std::string filename(fn, 0, colon);
auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos)); auto lineno = std::stoi(std::string(fn, colon + 1, std::string::npos));
return {CanonPath(fn.substr(0, colon)), lineno}; return {CanonPath(fn.substr(0, colon)), lineno};
} catch (std::invalid_argument & e) { } catch (std::invalid_argument & e) {
+1 -1
View File
@@ -212,7 +212,7 @@ struct ExprAttrs : Expr
AttrDef() { }; AttrDef() { };
template<typename T> template<typename T>
T chooseByKind(const T & plain, const T & inherited, const T & inheritedFrom) const const T & chooseByKind(const T & plain, const T & inherited, const T & inheritedFrom) const
{ {
switch (kind) { switch (kind) {
case Kind::Plain: case Kind::Plain:
+3 -6
View File
@@ -624,13 +624,10 @@ struct nothing : p::nothing<Rule> {
static_assert(!std::is_base_of_v<semantic, Rule>); static_assert(!std::is_base_of_v<semantic, Rule>);
}; };
template<typename Self, typename OpCtx, typename AttrPathT, typename ExprT> template<typename Self, typename OpCtx, typename AttrPathT, typename ExprT>
struct operator_semantics struct operator_semantics {
{
private:
operator_semantics() = default;
friend Self;
public:
struct has_attr : grammar::op::has_attr { struct has_attr : grammar::op::has_attr {
AttrPathT path; AttrPathT path;
}; };
+9 -3
View File
@@ -250,7 +250,9 @@ std::optional<Hash> Input::getNarHash() const
std::optional<std::string> Input::getRef() const std::optional<std::string> Input::getRef() const
{ {
return maybeGetStrAttr(attrs, "ref"); if (auto s = maybeGetStrAttr(attrs, "ref"))
return *s;
return {};
} }
std::optional<Hash> Input::getRev() const std::optional<Hash> Input::getRev() const
@@ -271,12 +273,16 @@ std::optional<Hash> Input::getRev() const
std::optional<uint64_t> Input::getRevCount() const std::optional<uint64_t> Input::getRevCount() const
{ {
return maybeGetIntAttr(attrs, "revCount"); if (auto n = maybeGetIntAttr(attrs, "revCount"))
return *n;
return {};
} }
std::optional<time_t> Input::getLastModified() const std::optional<time_t> Input::getLastModified() const
{ {
return maybeGetIntAttr(attrs, "lastModified"); if (auto n = maybeGetIntAttr(attrs, "lastModified"))
return *n;
return {};
} }
ParsedURL InputScheme::toURL(const Input & input) const ParsedURL InputScheme::toURL(const Input & input) const
+1 -1
View File
@@ -130,7 +130,7 @@ std::optional<std::string> readHeadCached(const std::string & actualUrl)
// This function must behave the same way, so we return the expired // This function must behave the same way, so we return the expired
// cached ref here. // cached ref here.
warn("could not get HEAD ref for repository '%s'; using expired cached ref '%s'", actualUrl, *cachedRef); warn("could not get HEAD ref for repository '%s'; using expired cached ref '%s'", actualUrl, *cachedRef);
return cachedRef; return *cachedRef;
} }
return std::nullopt; return std::nullopt;
+1 -1
View File
@@ -1212,7 +1212,7 @@ HookReply DerivationGoal::tryBuildHook()
hook->toHook.writeSide.reset(); hook->toHook.writeSide.reset();
/* Create the log file and pipe. */ /* Create the log file and pipe. */
openLogFile(); Path logFile = openLogFile();
std::set<int> fds; std::set<int> fds;
fds.insert(hook->fromHook.readSide.get()); fds.insert(hook->fromHook.readSide.get());
+53 -89
View File
@@ -332,7 +332,7 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull()
if (statvfs(localStore.realStoreDir.get().c_str(), &st) == 0 && if (statvfs(localStore.realStoreDir.get().c_str(), &st) == 0 &&
(uint64_t) st.f_bavail * st.f_bsize < required) (uint64_t) st.f_bavail * st.f_bsize < required)
diskFull = true; diskFull = true;
if (statvfs(tmpDirRoot.c_str(), &st) == 0 && if (statvfs(tmpDir.c_str(), &st) == 0 &&
(uint64_t) st.f_bavail * st.f_bsize < required) (uint64_t) st.f_bavail * st.f_bsize < required)
diskFull = true; diskFull = true;
} }
@@ -363,9 +363,13 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeCheck()
void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck() void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()
{ {
/* Delete unused redirected outputs (when doing hash rewriting). */ /* In the past, redirected outputs were manually tracked for deletion.
for (auto & i : redirectedOutputs) * Now that we have the scratch outputs cleaner which are a superset of
deletePath(worker.store.Store::toRealPath(i.second)); * redirected outputs, we just fire all uncancelled automatic deleters now.
*
* This should clean up any paths that IS NOT registered in the database.
*/
scratchOutputsCleaner.clear();
/* Delete the chroot (if we were using one). */ /* Delete the chroot (if we were using one). */
autoDelChroot.reset(); /* this runs the destructor */ autoDelChroot.reset(); /* this runs the destructor */
@@ -475,88 +479,25 @@ void LocalDerivationGoal::startBuilder()
settings.thisSystem, settings.thisSystem,
concatStringsSep<StringSet>(", ", worker.store.systemFeatures)); concatStringsSep<StringSet>(", ", worker.store.systemFeatures));
try { createDirs(settings.buildDir.get());
auto buildDir = worker.buildDirOverride.value_or(settings.buildDir.get());
createDirs(buildDir); /* Create a temporary directory where the build will take
place. */
/* Create a temporary directory where the build will take tmpDir = createTempDir(
place. */ settings.buildDir.get(),
tmpDirRoot = "nix-build-" + std::string(drvPath.name()),
createTempDir(buildDir, "nix-build-" + std::string(drvPath.name()), false, false, 0700); false,
} catch (SysError & e) { false,
/* 0700
* Fallback to the global tmpdir and create a safe space there );
* only if it's a permission error.
*/
if (e.errNo != EACCES) {
throw;
}
auto globalTmp = defaultTempDir();
createDirs(globalTmp);
#if __APPLE__
/* macOS filesystem namespacing does not exist, to avoid breaking builds, we need to weaken
* the mode bits on the top-level directory. This avoids issues like
* https://github.com/NixOS/nix/pull/11031. */
constexpr int toplevelDirMode = 0755;
#else
constexpr int toplevelDirMode = 0700;
#endif
auto nixBuildsTmp =
createTempDir(globalTmp, fmt("nix-builds-%s", geteuid()), false, false, toplevelDirMode);
warn(
"Failed to use the system-wide build directory '%s', falling back to a temporary "
"directory inside '%s'",
settings.buildDir.get(),
nixBuildsTmp
);
worker.buildDirOverride = nixBuildsTmp;
tmpDirRoot = createTempDir(
nixBuildsTmp, "nix-build-" + std::string(drvPath.name()), false, false, 0700
);
}
/* The TOCTOU between the previous mkdir call and this open call is unavoidable due to /* The TOCTOU between the previous mkdir call and this open call is unavoidable due to
* POSIX semantics.*/ * POSIX semantics.*/
tmpDirRootFd = AutoCloseFD{open(tmpDirRoot.c_str(), O_RDONLY | O_NOFOLLOW | O_DIRECTORY)}; tmpDirFd = AutoCloseFD{open(tmpDir.c_str(), O_RDONLY | O_NOFOLLOW | O_DIRECTORY)};
if (!tmpDirRootFd)
throw SysError("failed to open the build temporary directory descriptor '%1%'", tmpDirRoot);
// place the actual build directory in a subdirectory of tmpDirRoot. if
// we do not do this a build can `chown 777` its build directory and so
// make it accessible to everyone in the system, breaking isolation. we
// also need the intermediate level to be inaccessible to others. build
// processes must be able to at least traverse to the directory though,
// without being able to chmod. this means either mode 0750 or 0710. we
// cannot use 0710 because the libarchive we link with is compiled with
// an old apple sdk that does not have O_SEARCH, which makes libarchive
// try to open tmpDirRoot for *read* and fail because g+r is not set. a
// future update to nixpkgs may fix this. until then we do not lose any
// security by setting mode 0750 because we use only a single subdir in
// tmpDirRoot, so being able to list its parent doesn't break anything.
//
// use a short name to not increase the path length too much on darwin.
// darwin has a severe sockaddr_un path length limitation, so this does
// make a difference over more evocative names. we use `b` for `build`.
tmpDir = tmpDirRoot + "/b";
if (mkdirat(tmpDirRootFd.get(), "b", 0700)) {
throw SysError("failed to create the build temporary directory '%1%'", tmpDir);
}
tmpDirFd = AutoCloseFD{openat(tmpDirRootFd.get(), "b", O_RDONLY | O_NOFOLLOW | O_DIRECTORY)};
if (!tmpDirFd) if (!tmpDirFd)
throw SysError("failed to open the build temporary directory descriptor '%1%'", tmpDir); throw SysError("failed to open the build temporary directory descriptor '%1%'", tmpDir);
chownToBuilder(tmpDirFd); chownToBuilder(tmpDirFd);
if (buildUser) {
if (fchown(tmpDirRootFd.get(), -1, buildUser->getGID()) == -1) {
throw SysError("cannot change ownership of '%1%'", tmpDirRoot);
}
if (fchmod(tmpDirRootFd.get(), 0750) == -1) {
throw SysError("cannot change mode of '%1%'", tmpDirRoot);
}
}
for (auto & [outputName, status] : initialOutputs) { for (auto & [outputName, status] : initialOutputs) {
/* Set scratch path we'll actually use during the build. /* Set scratch path we'll actually use during the build.
@@ -586,6 +527,10 @@ void LocalDerivationGoal::startBuilder()
to use a temporary path */ to use a temporary path */
makeFallbackPath(status.known->path); makeFallbackPath(status.known->path);
scratchOutputs.insert_or_assign(outputName, scratchPath); scratchOutputs.insert_or_assign(outputName, scratchPath);
/* Schedule this scratch output path for automatic deletion
* if we do not cancel it, e.g. when registering the outputs.
*/
scratchOutputsCleaner.insert_or_assign(outputName, worker.store.printStorePath(scratchPath));
/* Substitute output placeholders with the scratch output paths. /* Substitute output placeholders with the scratch output paths.
We'll use during the build. */ We'll use during the build. */
@@ -608,8 +553,6 @@ void LocalDerivationGoal::startBuilder()
std::string h2 { scratchPath.hashPart() }; std::string h2 { scratchPath.hashPart() };
inputRewrites[h1] = h2; inputRewrites[h1] = h2;
} }
redirectedOutputs.insert_or_assign(std::move(fixedFinalPath), std::move(scratchPath));
} }
/* Construct the environment passed to the builder. */ /* Construct the environment passed to the builder. */
@@ -778,7 +721,7 @@ void LocalDerivationGoal::startBuilder()
printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second); printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second);
/* Create the log file. */ /* Create the log file. */
openLogFile(); Path logFile = openLogFile();
/* Create a pseudoterminal to get the output of the builder. */ /* Create a pseudoterminal to get the output of the builder. */
builderOut = AutoCloseFD{posix_openpt(O_RDWR | O_NOCTTY)}; builderOut = AutoCloseFD{posix_openpt(O_RDWR | O_NOCTTY)};
@@ -1903,8 +1846,6 @@ void LocalDerivationGoal::runChild()
if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") { if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
Strings sandboxArgs; Strings sandboxArgs;
sandboxArgs.push_back("_NIX_BUILD_TOP");
sandboxArgs.push_back(tmpDir);
sandboxArgs.push_back("_GLOBAL_TMP_DIR"); sandboxArgs.push_back("_GLOBAL_TMP_DIR");
sandboxArgs.push_back(globalTmpDir); sandboxArgs.push_back(globalTmpDir);
if (allowLocalNetworking) { if (allowLocalNetworking) {
@@ -2441,6 +2382,10 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
localStore.registerValidPaths({{oldInfo.path, oldInfo}}); localStore.registerValidPaths({{oldInfo.path, oldInfo}});
} }
/* Don't register anything, since we already have the
previous versions which we're comparing.
NOTE: this means that the `.check` path will be automatically deleted.
*/
continue; continue;
} }
@@ -2464,8 +2409,13 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
/* If it's a CA path, register it right away. This is necessary if it /* If it's a CA path, register it right away. This is necessary if it
isn't statically known so that we can safely unlock the path before isn't statically known so that we can safely unlock the path before
the next iteration */ the next iteration */
if (newInfo.ca) if (newInfo.ca) {
localStore.registerValidPaths({{newInfo.path, newInfo}}); localStore.registerValidPaths({{newInfo.path, newInfo}});
/* Cancel automatic deletion of that output if it was a scratch output. */
if (auto cleaner = scratchOutputsCleaner.extract(outputName)) {
cleaner.mapped().cancel();
}
}
infos.emplace(outputName, std::move(newInfo)); infos.emplace(outputName, std::move(newInfo));
} }
@@ -2493,6 +2443,13 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
infos2.insert_or_assign(newInfo.path, newInfo); infos2.insert_or_assign(newInfo.path, newInfo);
} }
localStore.registerValidPaths(infos2); localStore.registerValidPaths(infos2);
/* Cancel automatic deletion of that output if it was a scratch output that we just registered. */
for (auto & [outputName, _ ] : infos) {
if (auto cleaner = scratchOutputsCleaner.extract(outputName)) {
cleaner.mapped().cancel();
}
}
} }
/* In case of a fixed-output derivation hash mismatch, throw an /* In case of a fixed-output derivation hash mismatch, throw an
@@ -2526,6 +2483,13 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
builtOutputs.emplace(outputName, thisRealisation); builtOutputs.emplace(outputName, thisRealisation);
} }
/* NOTE: At this point, all outputs MAY NOT have been registered.
* Therefore, there may remains auto-deleters pending in the cleaner list (`scratchOutputsCleaner`).
*
* They will be finally deleted but we have no way to assert they all have been, e.g.
* `assert(scratchOutputsCleaner.size() == 0)` cannot be written.
*/
return builtOutputs; return builtOutputs;
} }
@@ -2715,16 +2679,16 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
void LocalDerivationGoal::deleteTmpDir(bool force) void LocalDerivationGoal::deleteTmpDir(bool force)
{ {
if (tmpDirRoot != "") { if (tmpDir != "") {
/* Don't keep temporary directories for builtins because they /* Don't keep temporary directories for builtins because they
might have privileged stuff (like a copy of netrc). */ might have privileged stuff (like a copy of netrc). */
if (settings.keepFailed && !force && !drv->isBuiltin()) { if (settings.keepFailed && !force && !drv->isBuiltin()) {
printError("note: keeping build directory '%s'", tmpDirRoot); printError("note: keeping build directory '%s'", tmpDir);
chmod(tmpDirRoot.c_str(), 0755); chmod(tmpDir.c_str(), 0755);
} }
else else
deletePath(tmpDirRoot); deletePath(tmpDir);
tmpDirRoot = ""; tmpDir = "";
} }
} }
+15 -4
View File
@@ -29,12 +29,12 @@ struct LocalDerivationGoal : public DerivationGoal
/** /**
* The temporary directory. * The temporary directory.
*/ */
Path tmpDirRoot, tmpDir; Path tmpDir;
/** /**
* The temporary directory file descriptor * The temporary directory file descriptor
*/ */
AutoCloseFD tmpDirRootFd, tmpDirFd; AutoCloseFD tmpDirFd;
/** /**
* The path of the temporary directory in the sandbox. * The path of the temporary directory in the sandbox.
@@ -107,8 +107,6 @@ struct LocalDerivationGoal : public DerivationGoal
* Hash rewriting. * Hash rewriting.
*/ */
StringMap inputRewrites, outputRewrites; StringMap inputRewrites, outputRewrites;
typedef map<StorePath, StorePath> RedirectedOutputs;
RedirectedOutputs redirectedOutputs;
/** /**
* The outputs paths used during the build. * The outputs paths used during the build.
@@ -125,6 +123,19 @@ struct LocalDerivationGoal : public DerivationGoal
* self-references. * self-references.
*/ */
OutputPathMap scratchOutputs; OutputPathMap scratchOutputs;
/**
* Output paths used during the build are scheduled for
* automatic cleanup unless they have been successfully built.
*
* `registerOutputs` take care of cancelling the cleanups
* and clearing this vector.
*
* `startBuilder` take care of filling this vector
* as `scratchOutputs` gets filled.
*
* This is a map from output names to automatic delete handles.
*/
std::map<std::string, AutoDelete> scratchOutputsCleaner;
/** /**
* Path registration info from the previous round, if we're * Path registration info from the previous round, if we're
+2 -4
View File
@@ -26,14 +26,12 @@ R""(
; Allow getpwuid. ; Allow getpwuid.
(allow mach-lookup (global-name "com.apple.system.opendirectoryd.libinfo")) (allow mach-lookup (global-name "com.apple.system.opendirectoryd.libinfo"))
; Access to /tmp and the build directory. ; Access to /tmp.
; The network-outbound/network-inbound ones are for unix domain sockets, which ; The network-outbound/network-inbound ones are for unix domain sockets, which
; we allow access to in TMPDIR (but if we allow them more broadly, you could in ; we allow access to in TMPDIR (but if we allow them more broadly, you could in
; theory escape the sandbox) ; theory escape the sandbox)
(allow file* process-exec network-outbound network-inbound (allow file* process-exec network-outbound network-inbound
(literal "/tmp") (literal "/tmp") (subpath TMPDIR))
(subpath TMPDIR)
(subpath (param "_NIX_BUILD_TOP")))
; Some packages like to read the system version. ; Some packages like to read the system version.
(allow file-read* (allow file-read*
-1
View File
@@ -161,7 +161,6 @@ public:
Store & store; Store & store;
Store & evalStore; Store & evalStore;
std::optional<Path> buildDirOverride;
struct HookState { struct HookState {
std::unique_ptr<HookInstance> instance; std::unique_ptr<HookInstance> instance;
+1 -1
View File
@@ -93,7 +93,7 @@ WireFormatGenerator CommonProto::Serialise<std::optional<ContentAddress>>::write
{ {
return [](std::string s) -> WireFormatGenerator { return [](std::string s) -> WireFormatGenerator {
co_yield s; co_yield s;
}(caOpt ? renderContentAddress(caOpt) : ""); }(caOpt ? renderContentAddress(*caOpt) : "");
} }
} }
+2 -9
View File
@@ -45,15 +45,8 @@ std::string SecretKey::signDetached(std::string_view data) const
{ {
unsigned char sig[crypto_sign_BYTES]; unsigned char sig[crypto_sign_BYTES];
unsigned long long sigLen; unsigned long long sigLen;
crypto_sign_detached( crypto_sign_detached(sig, &sigLen, charptr_cast<const unsigned char *>(data.data()), data.size(),
sig, charptr_cast<const unsigned char *>(key.data()));
&sigLen,
// the following is not a string function so no null termination issues are possible here.
// NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
charptr_cast<const unsigned char *>(data.data()),
data.size(),
charptr_cast<const unsigned char *>(key.data())
);
return name + ":" + base64Encode(std::string(reinterpret_cast<char *>(sig), sigLen)); return name + ":" + base64Encode(std::string(reinterpret_cast<char *>(sig), sigLen));
} }
+5
View File
@@ -13,9 +13,14 @@ void Store::exportPaths(const StorePathSet & paths, Sink & sink)
auto sorted = topoSortPaths(paths); auto sorted = topoSortPaths(paths);
std::reverse(sorted.begin(), sorted.end()); std::reverse(sorted.begin(), sorted.end());
std::string doneLabel("paths exported");
//logger->incExpected(doneLabel, sorted.size());
for (auto & path : sorted) { for (auto & path : sorted) {
//Activity act(*logger, lvlInfo, "exporting path '%s'", path);
sink << 1; sink << 1;
exportPath(path, sink); exportPath(path, sink);
//logger->incProgress(doneLabel);
} }
sink << 0; sink << 0;
+1
View File
@@ -152,6 +152,7 @@ struct NarAccessor : public FSAccessor
NarMember * find(const Path & path) NarMember * find(const Path & path)
{ {
Path canon = path == "" ? "" : canonPath(path);
NarMember * current = &root; NarMember * current = &root;
auto end = path.end(); auto end = path.end();
for (auto it = path.begin(); it != end; ) { for (auto it = path.begin(); it != end; ) {
+1 -1
View File
@@ -120,7 +120,7 @@ std::string NarInfo::to_string(const Store & store) const
res += "Sig: " + sig + "\n"; res += "Sig: " + sig + "\n";
if (ca) if (ca)
res += "CA: " + renderContentAddress(ca) + "\n"; res += "CA: " + renderContentAddress(*ca) + "\n";
return res; return res;
} }
+1 -1
View File
@@ -145,7 +145,7 @@ SQLiteStmt::Use::~Use()
SQLiteStmt::Use & SQLiteStmt::Use::operator () (std::string_view value, bool notNull) SQLiteStmt::Use & SQLiteStmt::Use::operator () (std::string_view value, bool notNull)
{ {
if (notNull) { if (notNull) {
if (sqlite3_bind_text(stmt, curArg++, value.data(), value.length(), SQLITE_TRANSIENT) != SQLITE_OK) if (sqlite3_bind_text(stmt, curArg++, value.data(), -1, SQLITE_TRANSIENT) != SQLITE_OK)
SQLiteError::throw_(stmt.db, "binding argument"); SQLiteError::throw_(stmt.db, "binding argument");
} else } else
bind(); bind();
+2 -1
View File
@@ -1322,7 +1322,8 @@ std::optional<StorePath> Store::getBuildDerivationPath(const StorePath & path)
if (!path.isDerivation()) { if (!path.isDerivation()) {
try { try {
auto info = queryPathInfo(path); auto info = queryPathInfo(path);
return info->deriver; if (!info->deriver) return std::nullopt;
return *info->deriver;
} catch (InvalidPath &) { } catch (InvalidPath &) {
return std::nullopt; return std::nullopt;
} }
-1
View File
@@ -239,7 +239,6 @@ struct BrotliCompressionSink : ChunkedCompressionSink
void writeInternal(std::string_view data) override void writeInternal(std::string_view data) override
{ {
// NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
auto next_in = charptr_cast<const uint8_t *>(data.data()); auto next_in = charptr_cast<const uint8_t *>(data.data());
size_t avail_in = data.size(); size_t avail_in = data.size();
uint8_t * next_out = outbuf; uint8_t * next_out = outbuf;
+1 -1
View File
@@ -475,7 +475,7 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed)
throw SysError("opening directory '%1%'", path); throw SysError("opening directory '%1%'", path);
} }
_deletePath(dirfd.get(), std::string(baseNameOf(path)), bytesFreed); _deletePath(dirfd.get(), baseNameOf(path).data(), bytesFreed);
} }
+1 -1
View File
@@ -25,7 +25,7 @@ std::string hiliteMatches(
out.append(s.substr(last_end, m.position() - last_end)); out.append(s.substr(last_end, m.position() - last_end));
// Merge continous matches // Merge continous matches
ssize_t end = start + m.length(); ssize_t end = start + m.length();
for (++it; it != matches.end() && (*it).position() <= end; ++it) { while (++it != matches.end() && (*it).position() <= end) {
auto n = *it; auto n = *it;
ssize_t nend = start + (n.position() - start + n.length()); ssize_t nend = start + (n.position() - start + n.length());
if (nend > end) if (nend > end)
+1 -1
View File
@@ -38,6 +38,6 @@ const nlohmann::json & ensureType(
nlohmann::json(expectedType).type_name(), nlohmann::json(expectedType).type_name(),
value.type_name()); value.type_name());
return value; // NOLINT(bugprone-return-const-ref-from-parameter) return value;
} }
} }
+6 -6
View File
@@ -258,12 +258,12 @@ extern Verbosity verbosity;
* level. Note that this has to be implemented as a macro to ensure that the * level. Note that this has to be implemented as a macro to ensure that the
* arguments are evaluated lazily. * arguments are evaluated lazily.
*/ */
#define printMsgUsing(loggerParam, level, args...) \ #define printMsgUsing(loggerParam, level, args...) \
do { \ do { \
auto _lix_logger_print_lvl = level; \ auto __lvl = level; \
if (_lix_logger_print_lvl <= nix::verbosity) { \ if (__lvl <= nix::verbosity) { \
loggerParam->log(_lix_logger_print_lvl, fmt(args)); \ loggerParam->log(__lvl, fmt(args)); \
} \ } \
} while (0) } while (0)
#define printMsg(level, args...) printMsgUsing(logger, level, args) #define printMsg(level, args...) printMsgUsing(logger, level, args)
+1 -1
View File
@@ -53,7 +53,7 @@ void RefScanSink::operator () (std::string_view data)
previous fragment and the start of the current fragment. */ previous fragment and the start of the current fragment. */
auto s = tail; auto s = tail;
auto tailLen = std::min(data.size(), refLength); auto tailLen = std::min(data.size(), refLength);
s.append(data.data(), tailLen); // NOLINT(bugprone-suspicious-stringview-data-usage) s.append(data.data(), tailLen);
search(s, hashes, seen); search(s, hashes, seen);
search(data, hashes, seen); search(data, hashes, seen);
+2 -1
View File
@@ -75,6 +75,7 @@ void Source::operator () (char * data, size_t len)
void Source::drainInto(Sink & sink) void Source::drainInto(Sink & sink)
{ {
std::string s;
std::array<char, 8192> buf; std::array<char, 8192> buf;
while (true) { while (true) {
size_t n; size_t n;
@@ -250,7 +251,7 @@ Error readError(Source & source)
auto type = readString(source); auto type = readString(source);
assert(type == "Error"); assert(type == "Error");
auto level = (Verbosity) readInt(source); auto level = (Verbosity) readInt(source);
readString(source); // removed (name) auto name = readString(source); // removed
auto msg = readString(source); auto msg = readString(source);
ErrorInfo info { ErrorInfo info {
.level = level, .level = level,
+1 -1
View File
@@ -34,7 +34,7 @@ bool shouldANSI(StandardOutputStream fileno)
// FIXME(jade): replace with TerminalCodeEater. wowie this is evil code. // FIXME(jade): replace with TerminalCodeEater. wowie this is evil code.
std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width, bool eatTabs) std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width, bool eatTabs)
{ {
std::string t; std::string t, e;
size_t w = 0; size_t w = 0;
auto i = s.begin(); auto i = s.begin();
+3 -9
View File
@@ -80,18 +80,12 @@ typename T::mapped_type * get(T & map, const typename T::key_type & key)
* Get a value for the specified key from an associate container, or a default value if the key isn't present. * Get a value for the specified key from an associate container, or a default value if the key isn't present.
*/ */
template <class T> template <class T>
const typename T::mapped_type & getOr( const typename T::mapped_type & getOr(T & map,
T & map [[clang::lifetimebound]],
const typename T::key_type & key, const typename T::key_type & key,
const typename T::mapped_type & defaultValue [[clang::lifetimebound]] const typename T::mapped_type & defaultValue)
)
{ {
auto i = map.find(key); auto i = map.find(key);
if (i == map.end()) { if (i == map.end()) return defaultValue;
/* FIXME(Raito): `[[clang::lifetimebound]]` has no effect on `defaultValue` warning. */
// NOLINTNEXTLINE(bugprone-return-const-ref-from-parameter)
return defaultValue;
}
return i->second; return i->second;
} }
+1 -1
View File
@@ -1313,7 +1313,7 @@ static void opSwitchGeneration(Globals & globals, Strings opFlags, Strings opArg
throw UsageError("exactly one argument expected"); throw UsageError("exactly one argument expected");
if (auto dstGen = string2Int<GenerationNumber>(opArgs.front())) if (auto dstGen = string2Int<GenerationNumber>(opArgs.front()))
switchGeneration(globals.profile, dstGen, globals.dryRun); switchGeneration(globals.profile, *dstGen, globals.dryRun);
else else
throw UsageError("expected a generation number"); throw UsageError("expected a generation number");
} }
+2
View File
@@ -116,6 +116,8 @@ struct CmdBundle : InstallableCommand
}, },
}); });
auto outPathS = store->printStorePath(outPath);
if (!outLink) { if (!outLink) {
auto * attr = vRes->attrs->get(evalState->sName); auto * attr = vRes->attrs->get(evalState->sName);
if (!attr) if (!attr)
+1 -1
View File
@@ -114,7 +114,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON
std::cout << '\t'; std::cout << '\t';
Strings ss; Strings ss;
if (info->ultimate) ss.push_back("ultimate"); if (info->ultimate) ss.push_back("ultimate");
if (info->ca) ss.push_back("ca:" + renderContentAddress(info->ca)); if (info->ca) ss.push_back("ca:" + renderContentAddress(*info->ca));
for (auto & sig : info->sigs) ss.push_back(sig); for (auto & sig : info->sigs) ss.push_back(sig);
std::cout << concatStringsSep(" ", ss); std::cout << concatStringsSep(" ", ss);
} }
+1
View File
@@ -160,6 +160,7 @@ struct CmdSearch : InstallableCommand, MixJSON
{"description", description}, {"description", description},
}; };
} else { } else {
auto name2 = hiliteMatches(name.name, nameMatches, ANSI_GREEN, "\e[0;2m");
if (results > 1) logger->cout(""); if (results > 1) logger->cout("");
logger->cout( logger->cout(
"* %s%s", "* %s%s",
+6
View File
@@ -40,9 +40,13 @@ struct CmdCopySigs : StorePathsCommand
ThreadPool pool; ThreadPool pool;
std::string doneLabel = "done";
std::atomic<size_t> added{0}; std::atomic<size_t> added{0};
//logger->setExpected(doneLabel, storePaths.size());
auto doPath = [&](const Path & storePathS) { auto doPath = [&](const Path & storePathS) {
//Activity act(*logger, lvlInfo, "getting signatures for '%s'", storePath);
checkInterrupt(); checkInterrupt();
@@ -74,6 +78,8 @@ struct CmdCopySigs : StorePathsCommand
store->addSignatures(storePath, newSigs); store->addSignatures(storePath, newSigs);
added += newSigs.size(); added += newSigs.size();
} }
//logger->incProgress(doneLabel);
}; };
for (auto & storePath : storePaths) for (auto & storePath : storePaths)
+1 -2
View File
@@ -34,7 +34,6 @@ private:
OptionalFileEntryRef File, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, StringRef SearchPath, StringRef RelativePath,
const Module *Imported, const Module *Imported,
bool ModuleImported,
SrcMgr::CharacteristicKind FileType) override; SrcMgr::CharacteristicKind FileType) override;
}; };
@@ -47,7 +46,7 @@ void FixIncludesCallbacks::LexedFileChanged(FileID, LexedFileChangeReason,
void FixIncludesCallbacks::InclusionDirective( void FixIncludesCallbacks::InclusionDirective(
SourceLocation, const Token &, StringRef FileName, bool IsAngled, SourceLocation, const Token &, StringRef FileName, bool IsAngled,
CharSourceRange FilenameRange, OptionalFileEntryRef File, StringRef, CharSourceRange FilenameRange, OptionalFileEntryRef File, StringRef,
StringRef, const Module *, bool, SrcMgr::CharacteristicKind) { StringRef, const Module *, SrcMgr::CharacteristicKind) {
if (Ignore) if (Ignore)
return; return;
+1 -1
View File
@@ -80,4 +80,4 @@ out="$(nix-build 2>&1 failing.nix \
[[ "$out" =~ .*"note: keeping build directory".* ]] [[ "$out" =~ .*"note: keeping build directory".* ]]
build_dir="$(grep "note: keeping build" <<< "$out" | sed -E "s/^(.*)note: keeping build directory '(.*)'(.*)$/\2/")" build_dir="$(grep "note: keeping build" <<< "$out" | sed -E "s/^(.*)note: keeping build directory '(.*)'(.*)$/\2/")"
[[ "foo" = $(<"$build_dir"/b/bar) ]] [[ "foo" = $(<"$build_dir"/bar) ]]
-25
View File
@@ -170,28 +170,3 @@ test "$(<<<"$out" grep -E '^error:' | wc -l)" = 3
<<<"$out" grepQuiet -E "error: 2 dependencies of derivation '.*-x4\\.drv' failed to build" <<<"$out" grepQuiet -E "error: 2 dependencies of derivation '.*-x4\\.drv' failed to build"
<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x3\\.drv'" <<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x3\\.drv'"
<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x2\\.drv'" <<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x2\\.drv'"
# Ensure when if the system build dir is inaccessible, we can still build things
BUILD_DIR=$(mktemp -d)
chmod 0000 "$BUILD_DIR"
nix --build-dir "$BUILD_DIR" build -E 'with import ./config.nix; mkDerivation { name = "test"; buildCommand = "echo rawr > $out"; }' --impure --no-link
# ensure that the build directory parent is not world-accessible
chmod 0755 "$BUILD_DIR"
FIFO="$BUILD_DIR/fifo"
mkfifo "$FIFO"
(
echo > "$FIFO"
trap 'echo > "$FIFO"' EXIT
mode=$(stat -c %a $BUILD_DIR/b/*)
[ "$mode" = "700" -o "$mode" = "710" ]
) &
nix build --build-dir "$BUILD_DIR/b" -E '
with import ./config.nix; mkDerivation {
name = "test";
buildCommand = "cat '"$FIFO"'; cat '"$FIFO"' > $out";
}' \
--extra-sandbox-paths "$FIFO" \
--impure \
--no-link
wait
+1 -1
View File
@@ -45,7 +45,7 @@ test_custom_build_dir() {
[ "$status" = "100" ] [ "$status" = "100" ]
[[ 1 == "$(count "$customBuildDir/nix-build-"*)" ]] [[ 1 == "$(count "$customBuildDir/nix-build-"*)" ]]
local buildDir="$customBuildDir/nix-build-"* local buildDir="$customBuildDir/nix-build-"*
grep $checkBuildId $buildDir/b/checkBuildId grep $checkBuildId $buildDir/checkBuildId
} }
test_custom_build_dir test_custom_build_dir
@@ -1,11 +1,12 @@
nix-repl> 1 + 1 nix-repl> 1 + 1
2 2
nix-repl> :doc builtins.add nix-repl> :doc builtins.head
Synopsis: builtins.add e1 e2 Synopsis: builtins.head list
Return the sum of the numbers e1 and e2.
Return the first element of a list; abort evaluation if
the argument isnt a list or is an empty list. You can
test whether a list is empty by comparing it with [].
nix-repl> f = a: "" + a nix-repl> f = a: "" + a
+3 -2
View File
@@ -12,8 +12,6 @@ let
defaults = { defaults = {
nixpkgs.pkgs = nixpkgsFor.${system}.native; nixpkgs.pkgs = nixpkgsFor.${system}.native;
nix.checkAllErrors = false; nix.checkAllErrors = false;
# nixos-option fails to build with lix and no tests use any of the tools
system.disableInstallerTools = true;
}; };
_module.args.nixpkgs = nixpkgs; _module.args.nixpkgs = nixpkgs;
_module.args.system = system; _module.args.system = system;
@@ -148,6 +146,9 @@ in
symlinkResolvconf = runNixOSTestFor "x86_64-linux" ./symlink-resolvconf.nix; symlinkResolvconf = runNixOSTestFor "x86_64-linux" ./symlink-resolvconf.nix;
# Use this test to test things that cannot easily be tested under chroot Nix stores in functional test suite.
non-chroot-misc = runNixOSTestFor "x86_64-linux" ./non-chroot-misc;
noNewPrivilegesInSandbox = runNixOSTestFor "x86_64-linux" ./no-new-privileges/sandbox.nix; noNewPrivilegesInSandbox = runNixOSTestFor "x86_64-linux" ./no-new-privileges/sandbox.nix;
noNewPrivilegesOutsideSandbox = runNixOSTestFor "x86_64-linux" ./no-new-privileges/no-sandbox.nix; noNewPrivilegesOutsideSandbox = runNixOSTestFor "x86_64-linux" ./no-new-privileges/no-sandbox.nix;
+1 -1
View File
@@ -76,7 +76,7 @@ in
# Fetching from a server with an untrusted cert should fail. # Fetching from a server with an untrusted cert should fail.
err = machine.fail("nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://bad/index.html\"; hash = \"sha256-rsBwZF/lPuOzdjBZN2E08FjMM3JHyXit0Xi2zN+wAZ8=\"; }' 2>&1") err = machine.fail("nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://bad/index.html\"; hash = \"sha256-rsBwZF/lPuOzdjBZN2E08FjMM3JHyXit0Xi2zN+wAZ8=\"; }' 2>&1")
print(err) print(err)
assert "SSL peer certificate or SSH remote key was not OK" in err assert "SSL certificate problem: self-signed certificate" in err or "SSL peer certificate or SSH remote key was not OK" in err
# Fetching from a server with a trusted cert should work via environment variable override. # Fetching from a server with a trusted cert should work via environment variable override.
machine.succeed("NIX_SSL_CERT_FILE=/tmp/cafile.pem nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://bad/index.html\"; hash = \"sha256-rsBwZF/lPuOzdjBZN2E08FjMM3JHyXit0Xi2zN+wAZ8=\"; }'") machine.succeed("NIX_SSL_CERT_FILE=/tmp/cafile.pem nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://bad/index.html\"; hash = \"sha256-rsBwZF/lPuOzdjBZN2E08FjMM3JHyXit0Xi2zN+wAZ8=\"; }'")
+2 -2
View File
@@ -45,12 +45,12 @@ in {
server.wait_for_unit("network-online.target") server.wait_for_unit("network-online.target")
client.wait_for_unit("network-online.target") client.wait_for_unit("network-online.target")
client.succeed("mkdir -m 700 /root/.ssh || [[ -d /root/.ssh ]]") client.succeed("mkdir -m 700 /root/.ssh")
client.copy_from_host("key", "/root/.ssh/id_ed25519") client.copy_from_host("key", "/root/.ssh/id_ed25519")
client.succeed("chmod 600 /root/.ssh/id_ed25519") client.succeed("chmod 600 /root/.ssh/id_ed25519")
# Install the SSH key on the server. # Install the SSH key on the server.
server.succeed("mkdir -m 700 /root/.ssh || [[ -d /root/.ssh ]]") server.succeed("mkdir -m 700 /root/.ssh")
server.copy_from_host("key.pub", "/root/.ssh/authorized_keys") server.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
server.wait_for_unit("sshd.service") server.wait_for_unit("sshd.service")
client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world' >&2") client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world' >&2")
+34
View File
@@ -0,0 +1,34 @@
{ ... }:
# Misc things we want to test inside of a non redirected, non chroot Nix store.
let
nonAutoCleaningFailingDerivationCode = ''
derivation {
name = "scratch-failing";
system = builtins.currentSystem;
builder = "/bin/sh";
args = [ (builtins.toFile "builder.sh" "echo bonjour > $out; echo out: $out; false") ];
}
'';
in
{
name = "non-chroot-sandbox-misc";
nodes.machine = {
};
testScript = { nodes }: ''
import re
start_all()
# You might ask yourself why write such a convoluted thing?
# The condition for fooling Nix into NOT cleaning up the output path are non trivial and unclear.
# This is one of those: create a derivation, mkdir or touch the $out path, communicate it back.
# Even with a sandboxed Lix, you will observe leftovers before 2.93.0. After this version, this test passes.
result = machine.fail("""nix-build --substituters "" -E '${nonAutoCleaningFailingDerivationCode}' 2>&1""")
match = re.search(r'out: (\S+)', result)
assert match is not None, "Did not find Nix store path in the result of the failing build"
outpath = match.group(1).strip()
print(f"Found Nix store path: {outpath}")
machine.fail(f'stat {outpath}')
'';
}
-1
View File
@@ -48,7 +48,6 @@ in
imports = [ test.config.builders.config ]; imports = [ test.config.builders.config ];
services.openssh.enable = true; services.openssh.enable = true;
virtualisation.writableStore = true; virtualisation.writableStore = true;
virtualisation.additionalPaths = [ config.system.build.extraUtils ];
nix.settings.sandbox = true; nix.settings.sandbox = true;
nix.settings.substituters = lib.mkForce [ ]; nix.settings.substituters = lib.mkForce [ ];
}; };
+2 -2
View File
@@ -1,5 +1,5 @@
{ {
"version": "2.91.3", "version": "2.91.2",
"official_release": false, "official_release": true,
"release_name": "Dragon's Breath" "release_name": "Dragon's Breath"
} }