diff options
62 files changed, 1501 insertions, 683 deletions
diff --git a/clang-tidy/meson.build b/clang-tidy/meson.build deleted file mode 100644 index c59164a72..000000000 --- a/clang-tidy/meson.build +++ /dev/null @@ -1,13 +0,0 @@ -project('lix-clang-tidy', ['cpp', 'c'], - version : '0.1', - default_options : ['warning_level=3', 'cpp_std=c++20']) - -llvm = dependency('Clang', version: '>= 14', modules: ['libclang']) -sources = files( - 'HasPrefixSuffix.cc', - 'LixClangTidyChecks.cc', - 'FixIncludes.cc', -) - -shared_module('lix-clang-tidy', sources, - dependencies: llvm) diff --git a/doc/internal-api/doxygen.cfg.in b/doc/internal-api/doxygen.cfg.in index 55bccebd7..73fba6948 100644 --- a/doc/internal-api/doxygen.cfg.in +++ b/doc/internal-api/doxygen.cfg.in @@ -20,7 +20,7 @@ OUTPUT_DIRECTORY = @docdir@ # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "Nix, the purely functional package manager; unstable internal interfaces" +PROJECT_BRIEF = "Lix: A modern, delicious implementation of the Nix package manager; unstable internal interfaces" # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index f270d31a4..7a61de643 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -345,7 +345,7 @@ const redirects = { "linux": "uninstall.html#linux", "macos": "uninstall.html#macos", "uninstalling": "uninstall.html", - } + }, "contributing/hacking.html": { "nix-with-flakes": "#building-nix-with-flakes", "classic-nix": "#building-nix", diff --git a/doc/manual/rl-next/clang-tidy-sorta.md b/doc/manual/rl-next/clang-tidy-sorta.md new file mode 100644 index 000000000..5f3f84348 --- /dev/null +++ b/doc/manual/rl-next/clang-tidy-sorta.md @@ -0,0 +1,10 @@ +--- +synopsis: "clang-tidy support" +cls: 1697 +issues: fj#147 +credits: jade +category: Development +--- + +`clang-tidy` can be used to lint Lix with a limited set of lints using `ninja -C build clang-tidy` and `ninja -C build clang-tidy-fix`. +In practice, this fixes the built-in meson rule that was used the same as above being broken ever since precompiled headers were introduced. diff --git a/doc/manual/rl-next/clicolor-clarity.md b/doc/manual/rl-next/clicolor-clarity.md new file mode 100644 index 000000000..8a289e362 --- /dev/null +++ b/doc/manual/rl-next/clicolor-clarity.md @@ -0,0 +1,26 @@ +--- +synopsis: "Better usage of colour control environment variables" +cls: [1699, 1702] +credits: [jade] +category: Improvements +--- + +Lix now heeds `NO_COLOR`/`NOCOLOR` for more output types, such as that used in `nix search`, `nix flake metadata` and similar. + +It also now supports `CLICOLOR_FORCE`/`FORCE_COLOR` to force colours regardless of whether there is a terminal on the other side. + +It now follows rules compatible with those described on <https://bixense.com/clicolors/> with `CLICOLOR` defaulted to enabled. + +That is to say, the following procedure is followed in order: +- NO_COLOR or NOCOLOR set + + Always disable colour +- CLICOLOR_FORCE or FORCE_COLOR set + + Enable colour +- The output is a tty; TERM != "dumb" + + Enable colour +- Otherwise + + Disable colour diff --git a/doc/manual/rl-next/flake-metadata-time.md b/doc/manual/rl-next/flake-metadata-time.md new file mode 100644 index 000000000..8664b5a75 --- /dev/null +++ b/doc/manual/rl-next/flake-metadata-time.md @@ -0,0 +1,28 @@ +--- +synopsis: "`nix flake metadata` prints modified date" +cls: 1700 +credits: jade +category: Improvements +--- + +Ever wonder "gee, when *did* I update nixpkgs"? +Wonder no more, because `nix flake metadata` now simply tells you the times every locked flake input was updated: + +``` +<...> +Description: The purely functional package manager +Path: /nix/store/c91yi8sxakc2ry7y4ac1smzwka4l5p78-source +Revision: c52cff582043838bbe29768e7da232483d52b61d-dirty +Last modified: 2024-07-31 22:15:54 +Inputs: +├───flake-compat: github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33 +│ Last modified: 2023-10-04 06:37:54 +├───nix2container: github:nlewo/nix2container/3853e5caf9ad24103b13aa6e0e8bcebb47649fe4 +│ Last modified: 2024-07-10 13:15:56 +├───nixpkgs: github:NixOS/nixpkgs/e21630230c77140bc6478a21cd71e8bb73706fce +│ Last modified: 2024-07-25 11:26:27 +├───nixpkgs-regression: github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2 +│ Last modified: 2022-01-24 11:20:45 +└───pre-commit-hooks: github:cachix/git-hooks.nix/f451c19376071a90d8c58ab1a953c6e9840527fd + Last modified: 2024-07-15 04:21:09 +``` diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index cea6ee3bf..33197b0ba 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -427,6 +427,7 @@ I grepped `src/` for `get[eE]nv\("` to find the mentions in Lix code. - `NIX_SHOW_STATS_PATH` - Writes those statistics into a file at the given path instead of stdout. Undocumented. - `NIX_SHOW_SYMBOLS` - Dumps the symbol table into the show-stats json output. - `TERM` - If `dumb` or unset, disables ANSI colour output. +- `FORCE_COLOR`, `CLICOLOR_FORCE` - Enables ANSI colour output if `NO_COLOR`/`NOCOLOR` not set. - `NO_COLOR`, `NOCOLOR` - Disables ANSI colour output. - `_NIX_DEVELOPER_SHOW_UNKNOWN_LOCATIONS` - Highlights unknown locations in errors. - `NIX_PROFILE` - Selects which profile `nix-env` will operate on. Documented elsewhere. diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index ec21b9990..ee93f5672 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -292,6 +292,12 @@ Derivations can declare some infrequently used optional attributes. (associative) arrays. For example, the attribute `hardening.format = true` ends up as the Bash associative array element `${hardening[format]}`. + > **Warning** + > + > If set to `true`, other advanced attributes such as [`allowedReferences`](#adv-attr-allowedReferences), [`allowedReferences`](#adv-attr-allowedReferences), [`allowedRequisites`](#adv-attr-allowedRequisites), + [`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites), maxSize, and maxClosureSize. + will have no effect. + - [`outputChecks`]{#adv-attr-outputChecks}\ When using [structured attributes](#adv-attr-structuredAttrs), the `outputChecks` attribute allows defining checks per-output. @@ -326,7 +332,6 @@ Derivations can declare some infrequently used optional attributes. ``` - [`unsafeDiscardReferences`]{#adv-attr-unsafeDiscardReferences}\ - When using [structured attributes](#adv-attr-structuredAttrs), the attribute `unsafeDiscardReferences` is an attribute set with a boolean value for each output name. If set to `true`, it disables scanning the output for runtime dependencies. @@ -1,5 +1,5 @@ { - description = "The purely functional package manager"; + description = "Lix: A modern, delicious implementation of the Nix package manager"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05-small"; @@ -197,6 +197,8 @@ busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; }; + lix-clang-tidy = final.callPackage ./subprojects/lix-clang-tidy { }; + # Export the patched version of boehmgc that Lix uses into the overlay # for consumers of this flake. boehmgc-nix = final.nix.passthru.boehmgc-nix; @@ -290,6 +292,24 @@ werror = true; }; + # Although this might be nicer to do with pre-commit, that would + # require adding 12MB of nodejs to the dev shell, whereas building it + # in CI with Nix avoids that at a cost of slower feedback on rarely + # touched files. + jsSyntaxCheck = + let + nixpkgs = nixpkgsFor.x86_64-linux.native; + inherit (nixpkgs) pkgs; + docSources = lib.fileset.toSource { + root = ./doc; + fileset = lib.fileset.fileFilter (f: f.hasExt "js") ./doc; + }; + in + pkgs.runCommand "js-syntax-check" { } '' + find ${docSources} -type f -print -exec ${pkgs.nodejs-slim}/bin/node --check '{}' ';' + touch $out + ''; + # Make sure that nix-env still produces the exact same result # on a particular version of Nixpkgs. evalNixpkgs = @@ -384,6 +404,8 @@ rec { inherit (nixpkgsFor.${system}.native) nix; default = nix; + + inherit (nixpkgsFor.${system}.native) lix-clang-tidy; } // ( lib.optionalAttrs (builtins.elem system linux64BitSystems) { @@ -25,3 +25,13 @@ install *OPTIONS: (build OPTIONS) # Run tests test *OPTIONS: meson test -C build --print-errorlogs {{ OPTIONS }} + +alias clang-tidy := lint + +lint: + ninja -C build clang-tidy + +alias clang-tidy-fix := lint-fix + +lint-fix: + ninja -C build clang-tidy-fix diff --git a/maintainers/check-syscalls.sh b/maintainers/check-syscalls.sh index cd72ac23b..72b629438 100755 --- a/maintainers/check-syscalls.sh +++ b/maintainers/check-syscalls.sh @@ -2,6 +2,6 @@ set -e -diff -u <(awk < src/libstore/build/local-derivation-goal.cc '/BEGIN extract-syscalls/ { extracting = 1; next } +diff -u <(awk < src/libstore/platform/linux.cc '/BEGIN extract-syscalls/ { extracting = 1; next } match($0, /allowSyscall\(ctx, SCMP_SYS\(([^)]*)\)\);|\/\/ skip ([^ ]*)/, result) { print result[1] result[2] } /END extract-syscalls/ { extracting = 0; next }') <(tail -n+2 "$1" | cut -d, -f 1) diff --git a/meson.build b/meson.build index ed50dff78..8577657d9 100644 --- a/meson.build +++ b/meson.build @@ -548,3 +548,5 @@ if enable_tests subdir('tests/unit') subdir('tests/functional') endif + +subdir('meson/clang-tidy') diff --git a/meson.options b/meson.options index fc2050809..679d88347 100644 --- a/meson.options +++ b/meson.options @@ -1,7 +1,7 @@ # vim: filetype=meson option('enable-build', type : 'boolean', value : true, - description : 'Set to false to not actually build. Only really makes sense with -Dinternal-api-docs=true', + description : 'set to false to not actually build. Only really makes sense with -Dinternal-api-docs=true', ) option('gc', type : 'feature', @@ -37,7 +37,7 @@ option('tests-brief', type : 'boolean', value : false, ) option('profile-build', type : 'feature', value: 'disabled', - description : 'whether to enable -ftime-trace in clang builds, allowing for speeding up the build.' + description : 'whether to enable -ftime-trace in clang builds, allowing for diagnosing the cause of build time.' ) option('store-dir', type : 'string', value : '/nix/store', @@ -68,3 +68,7 @@ option('profile-dir', type : 'string', value : 'etc/profile.d', option('enable-pch-std', type : 'boolean', value : true, description : 'whether to use precompiled headers for C++\'s standard library (breaks clangd if you\'re using GCC)', ) + +option('lix-clang-tidy-checks-path', type : 'string', value : '', + description: 'path to lix-clang-tidy-checks library file, if providing it externally. Uses an internal one if this is not set', +) diff --git a/meson/clang-tidy/build_required_targets.py b/meson/clang-tidy/build_required_targets.py new file mode 100755 index 000000000..5c0e9641e --- /dev/null +++ b/meson/clang-tidy/build_required_targets.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +import subprocess + +def get_targets_of_rule(build_root: str, rule_name: str) -> list[str]: + return subprocess.check_output(['ninja', '-C', build_root, '-t', 'targets', 'rule', rule_name]).decode().strip().splitlines() + +def ninja_build(build_root: str, targets: list[str]): + subprocess.check_call(['ninja', '-C', build_root, '--', *targets]) + +def main(): + import argparse + ap = argparse.ArgumentParser(description='Builds required targets for clang-tidy') + ap.add_argument('build_root', help='Ninja build root', type=str) + + args = ap.parse_args() + + targets = [t for t in get_targets_of_rule(args.build_root, 'CUSTOM_COMMAND') if t.endswith('gen.hh')] + ninja_build(args.build_root, targets) + +if __name__ == '__main__': + main() diff --git a/meson/clang-tidy/clean_compdb.py b/meson/clang-tidy/clean_compdb.py new file mode 100755 index 000000000..6736fe63a --- /dev/null +++ b/meson/clang-tidy/clean_compdb.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# Deletes the PCH arguments from a compilation database, to workaround nixpkgs +# stdenv having a cc-wrapper that is impossible to use for anything except cc +# itself, for example, clang-tidy. + +import json +import shlex + + +def process_compdb(compdb: list[dict]) -> list[dict]: + + def munch_command(args: list[str]) -> list[str]: + out = [] + eat_next = False + for i, arg in enumerate(args): + if arg == '-fpch-preprocess': + # as used with gcc + continue + elif arg == '-include-pch' or (arg == '-include' and args[i + 1] == 'precompiled-headers.hh'): + # -include-pch some-pch (clang), or -include some-pch (gcc) + eat_next = True + continue + if not eat_next: + out.append(arg) + eat_next = False + return out + + def chomp(item: dict) -> dict: + item = item.copy() + item['command'] = shlex.join(munch_command(shlex.split(item['command']))) + return item + + return [chomp(x) for x in compdb if not x['file'].endswith('precompiled-headers.hh')] + + +def main(): + import argparse + ap = argparse.ArgumentParser( + description='Delete pch arguments from compilation database') + ap.add_argument('input', + type=argparse.FileType('r'), + help='Input json file') + ap.add_argument('output', + type=argparse.FileType('w'), + help='Output json file') + args = ap.parse_args() + + input_json = json.load(args.input) + json.dump(process_compdb(input_json), args.output, indent=2) + + +if __name__ == '__main__': + main() diff --git a/meson/clang-tidy/fake.cc b/meson/clang-tidy/fake.cc new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/meson/clang-tidy/fake.cc diff --git a/meson/clang-tidy/meson.build b/meson/clang-tidy/meson.build new file mode 100644 index 000000000..9aba5fbdc --- /dev/null +++ b/meson/clang-tidy/meson.build @@ -0,0 +1,98 @@ +# The clang-tidy target for Lix + +run_clang_tidy = find_program('run-clang-tidy', required : false) +# Although this looks like it wants to be pkg-config, pkg-config does not +# really work for *plugins*, which are executable-like .so files that also +# cannot be found via find_program. Fun! +if get_option('lix-clang-tidy-checks-path') != '' + lix_clang_tidy_so = get_option('lix-clang-tidy-checks-path') + lix_clang_tidy_so_found = true +else + lix_clang_tidy_subproj = subproject( + 'lix-clang-tidy', + required : false, + default_options : {'build-by-default': false} + ) + if lix_clang_tidy_subproj.found() + lix_clang_tidy_so = lix_clang_tidy_subproj.get_variable('lix_clang_tidy') + lix_clang_tidy_so_found = true + else + lix_clang_tidy_so_found = false + endif +endif + +# Due to numerous problems, such as: +# - Meson does not expose pch targets, but *fine*, I can just ask Ninja for +# them with `ninja -t targets rule cpp_PCH` and build them manually: +# https://github.com/mesonbuild/meson/issues/13499 +# - Nixpkgs stdenv buries the cc-wrapper under a giant pile of assumptions +# about the cc-wrapper actually being used on the cc of a stdenv, rather than +# independently for clang-tidy, and we need to use cc-wrapper to get the +# correct hardening flags so that clang-tidy can actually parse the PCH file +# +# I give up. I am going to delete the damn PCH args and then it will work. +meson.add_postconf_script( + python, + meson.current_source_dir() / 'clean_compdb.py', + meson.global_build_root() / 'compile_commands.json', + meson.current_build_dir() / 'compile_commands.json', +) + +# Horrible hack to get around not being able to depend on another target's +# generated headers in any way in the meson DSL +# https://github.com/mesonbuild/meson/issues/12817 which was incorrectly +# closed, if you *actually* need to generate the files once. +# Also related: https://github.com/mesonbuild/meson/issues/3667 +# +# Or we could ban meson generators because their design is broken. +build_all_generated_headers = custom_target( + command : [ + python, + meson.current_source_dir() / 'build_required_targets.py', + meson.global_build_root(), + ], + output : 'generated_headers.stamp', + build_by_default : false, + build_always_stale : true, +) + +if lix_clang_tidy_so_found + run_clang_tidy_args = [ + '-load', + lix_clang_tidy_so, + '-p', + # We have to workaround a run-clang-tidy bug too, so we must give the + # directory name rather than the actual compdb file. + # https://github.com/llvm/llvm-project/issues/101440 + meson.current_build_dir(), + '-quiet', + ] + run_target( + 'clang-tidy', + command : [ + # XXX: This explicitly invokes it with python because of a nixpkgs bug + # where clang-unwrapped does not patch interpreters in run-clang-tidy. + # However, making clang-unwrapped depend on python is also silly, so idk. + python, + run_clang_tidy, + run_clang_tidy_args, + '-warnings-as-errors', + '*', + ], + depends : [ + build_all_generated_headers, + ], + ) + run_target( + 'clang-tidy-fix', + command : [ + python, + run_clang_tidy, + run_clang_tidy_args, + '-fix', + ], + depends : [ + build_all_generated_headers, + ], + ) +endif diff --git a/package.nix b/package.nix index 7ebe2721b..be2f0010d 100644 --- a/package.nix +++ b/package.nix @@ -58,8 +58,11 @@ buildUnreleasedNotes ? true, internalApiDocs ? false, + # Support garbage collection in the evaluator. + enableGC ? sanitize == null || !builtins.elem "address" sanitize, # List of Meson sanitize options. Accepts values of b_sanitize, e.g. # "address", "undefined", "thread". + # Enabling the "address" sanitizer will disable garbage collection in the evaluator. sanitize ? null, # Turn compiler warnings into errors. werror ? false, @@ -118,10 +121,7 @@ let # The internal API docs need these for the build, but if we're not building # Nix itself, then these don't need to be propagated. - maybePropagatedInputs = [ - boehmgc-nix - nlohmann_json - ]; + maybePropagatedInputs = lib.optional enableGC boehmgc-nix ++ [ nlohmann_json ]; # .gitignore has already been processed, so any changes in it are irrelevant # at this point. It is not represented verbatim for test purposes because @@ -179,10 +179,9 @@ stdenv.mkDerivation (finalAttrs: { mesonFlags = let - sanitizeOpts = lib.optionals (sanitize != null) ( - [ "-Db_sanitize=${builtins.concatStringsSep "," sanitize}" ] - ++ lib.optional (builtins.elem "address" sanitize) "-Dgc=disabled" - ); + sanitizeOpts = lib.optional ( + sanitize != null + ) "-Db_sanitize=${builtins.concatStringsSep "," sanitize}"; in lib.optionals hostPlatform.isLinux [ # You'd think meson could just find this in PATH, but busybox is in buildInputs, @@ -196,6 +195,7 @@ stdenv.mkDerivation (finalAttrs: { # mesonConfigurePhase automatically passes -Dauto_features=enabled, # so we must explicitly enable or disable features that we are not passing # dependencies for. + (lib.mesonEnable "gc" enableGC) (lib.mesonEnable "internal-api-docs" internalApiDocs) (lib.mesonBool "enable-tests" finalAttrs.finalPackage.doCheck) (lib.mesonBool "enable-docs" canRunInstalled) @@ -502,7 +502,7 @@ stdenv.mkDerivation (finalAttrs: { PATH=$prefix/bin''${PATH:+:''${PATH}} unset PYTHONPATH - export MANPATH=$out/share/man''${MANPATH:+:''${MANPATH}} + export MANPATH=$out/share/man:''${MANPATH:-} # Make bash completion work. XDG_DATA_DIRS+=:$out/share diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a925ce2d8..741a24e3c 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -249,6 +249,12 @@ EvalState::EvalState( , sRight(symbols.create("right")) , sWrong(symbols.create("wrong")) , sStructuredAttrs(symbols.create("__structuredAttrs")) + , sAllowedReferences(symbols.create("allowedReferences")) + , sAllowedRequisites(symbols.create("allowedRequisites")) + , sDisallowedReferences(symbols.create("disallowedReferences")) + , sDisallowedRequisites(symbols.create("disallowedRequisites")) + , sMaxSize(symbols.create("maxSize")) + , sMaxClosureSize(symbols.create("maxClosureSize")) , sBuilder(symbols.create("builder")) , sArgs(symbols.create("args")) , sContentAddressed(symbols.create("__contentAddressed")) @@ -2802,20 +2808,20 @@ Expr & EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr<Sta } -Expr & EvalState::parseExprFromString(std::string s_, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv) +Expr & EvalState::parseExprFromString(std::string s_, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv, const ExperimentalFeatureSettings & xpSettings) { // NOTE this method (and parseStdin) must take care to *fully copy* their input // into their respective Pos::Origin until the parser stops overwriting its input // data. auto s = make_ref<std::string>(s_); s_.append("\0\0", 2); - return *parse(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv); + return *parse(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv, xpSettings); } -Expr & EvalState::parseExprFromString(std::string s, const SourcePath & basePath) +Expr & EvalState::parseExprFromString(std::string s, const SourcePath & basePath, const ExperimentalFeatureSettings & xpSettings) { - return parseExprFromString(std::move(s), basePath, staticBaseEnv); + return parseExprFromString(std::move(s), basePath, staticBaseEnv, xpSettings); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index e54eede40..ff45efc08 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -13,7 +13,6 @@ #include "search-path.hh" #include "repl-exit-status.hh" -#include <gc/gc_allocator.h> #include <map> #include <optional> #include <unordered_map> @@ -162,7 +161,10 @@ public: const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls, sFile, sLine, sColumn, sFunctor, sToString, - sRight, sWrong, sStructuredAttrs, sBuilder, sArgs, + sRight, sWrong, sStructuredAttrs, + sAllowedReferences, sAllowedRequisites, sDisallowedReferences, sDisallowedRequisites, + sMaxSize, sMaxClosureSize, + sBuilder, sArgs, sContentAddressed, sImpure, sOutputHash, sOutputHashAlgo, sOutputHashMode, sRecurseForDerivations, @@ -342,8 +344,8 @@ public: /** * Parse a Nix expression from the specified string. */ - Expr & parseExprFromString(std::string s, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv); - Expr & parseExprFromString(std::string s, const SourcePath & basePath); + Expr & parseExprFromString(std::string s, const SourcePath & basePath, std::shared_ptr<StaticEnv> & staticEnv, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); + Expr & parseExprFromString(std::string s, const SourcePath & basePath, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); Expr & parseStdin(); @@ -566,7 +568,8 @@ private: size_t length, Pos::Origin origin, const SourcePath & basePath, - std::shared_ptr<StaticEnv> & staticEnv); + std::shared_ptr<StaticEnv> & staticEnv, + const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); /** * Current Nix call stack depth, used with `max-call-depth` setting to throw stack overflow hopefully before we run out of system stack. diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index adcf7fd10..558b3e9b9 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -53,7 +53,7 @@ void ConfigFile::apply() bool trusted = whitelist.count(baseName); if (!trusted) { - switch (nix::fetchSettings.acceptFlakeConfig) { + switch (nix::fetchSettings.acceptFlakeConfig.get()) { case AcceptFlakeConfig::True: { trusted = true; break; diff --git a/src/libexpr/parser/parser.cc b/src/libexpr/parser/parser.cc index a00586c36..68aa3ddc5 100644 --- a/src/libexpr/parser/parser.cc +++ b/src/libexpr/parser/parser.cc @@ -12,7 +12,6 @@ #include "state.hh" #include <charconv> -#include <clocale> #include <memory> // flip this define when doing parser development to enable some g checks. @@ -254,7 +253,8 @@ struct AttrState : SubexprState { std::vector<AttrName> attrs; - void pushAttr(auto && attr, PosIdx) { attrs.emplace_back(std::move(attr)); } + template <typename T> + void pushAttr(T && attr, PosIdx) { attrs.emplace_back(std::forward<T>(attr)); } }; template<> struct BuildAST<grammar::attr::simple> { @@ -290,7 +290,8 @@ struct InheritState : SubexprState { std::unique_ptr<Expr> from; PosIdx fromPos; - void pushAttr(auto && attr, PosIdx pos) { attrs.emplace_back(std::move(attr), pos); } + template <typename T> + void pushAttr(T && attr, PosIdx pos) { attrs.emplace_back(std::forward<T>(attr), pos); } }; template<> struct BuildAST<grammar::inherit::from> { @@ -630,7 +631,7 @@ template<> struct BuildAST<grammar::expr::path> : p::maybe_nothing {}; template<> struct BuildAST<grammar::expr::uri> { static void apply(const auto & in, ExprState & s, State & ps) { - static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals); + bool noURLLiterals = ps.xpSettings.isEnabled(Xp::NoUrlLiterals); if (noURLLiterals) throw ParseError({ .msg = HintFmt("URL literals are disabled"), @@ -831,7 +832,8 @@ Expr * EvalState::parse( size_t length, Pos::Origin origin, const SourcePath & basePath, - std::shared_ptr<StaticEnv> & staticEnv) + std::shared_ptr<StaticEnv> & staticEnv, + const ExperimentalFeatureSettings & xpSettings) { parser::State s = { symbols, @@ -839,6 +841,7 @@ Expr * EvalState::parse( basePath, positions.addOrigin(origin, length), exprSymbols, + xpSettings }; parser::ExprState x; diff --git a/src/libexpr/parser/state.hh b/src/libexpr/parser/state.hh index 29889152e..30803a37e 100644 --- a/src/libexpr/parser/state.hh +++ b/src/libexpr/parser/state.hh @@ -19,6 +19,7 @@ struct State SourcePath basePath; PosTable::Origin origin; const Expr::AstSymbols & s; + const ExperimentalFeatureSettings & xpSettings; void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0951a54de..561492f86 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1213,6 +1213,20 @@ drvName, Bindings * attrs, Value & v) handleOutputs(ss); } + if (i->name == state.sAllowedReferences) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'allowedReferences'; use 'outputChecks.<output>.allowedReferences' instead", drvName); + if (i->name == state.sAllowedRequisites) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'allowedRequisites'; use 'outputChecks.<output>.allowedRequisites' instead", drvName); + if (i->name == state.sDisallowedReferences) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedReferences'; use 'outputChecks.<output>.disallowedReferences' instead", drvName); + if (i->name == state.sDisallowedRequisites) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedRequisites'; use 'outputChecks.<output>.disallowedRequisites' instead", drvName); + if (i->name == state.sMaxSize) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'maxSize'; use 'outputChecks.<output>.maxSize' instead", drvName); + if (i->name == state.sMaxClosureSize) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'maxClosureSize'; use 'outputChecks.<output>.maxClosureSize' instead", drvName); + + } else { auto s = state.coerceToString(pos, *i->value, context, context_below, true).toOwned(); drv.env.emplace(key, s); diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index fb5ccc6f1..db380e07c 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -42,11 +42,7 @@ #include <sched.h> #include <sys/param.h> #include <sys/mount.h> -#include <sys/prctl.h> #include <sys/syscall.h> -#if HAVE_SECCOMP -#include <seccomp.h> -#endif #define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) #endif @@ -61,14 +57,6 @@ extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, namespace nix { -namespace { -/** - * The system for which Nix is compiled. - */ -[[gnu::unused]] -constexpr const std::string_view nativeSystem = SYSTEM; -} - void handleDiffHook( uid_t uid, uid_t gid, const Path & tryA, const Path & tryB, @@ -1361,593 +1349,6 @@ void LocalDerivationGoal::chownToBuilder(const Path & path) throw SysError("cannot change ownership of '%1%'", path); } -#if HAVE_SECCOMP - -static void allowSyscall(scmp_filter_ctx ctx, int syscall) { - if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 0) != 0) - throw SysError("unable to add seccomp rule"); -} - -#define ALLOW_CHMOD_IF_SAFE(ctx, syscall, modePos) \ - if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 1, SCMP_A##modePos(SCMP_CMP_MASKED_EQ, S_ISUID | S_ISGID, 0)) != 0 || \ - seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), syscall, 1, SCMP_A##modePos(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) != 0 || \ - seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), syscall, 1, SCMP_A##modePos(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) != 0) \ - throw SysError("unable to add seccomp rule"); - -#endif - -void setupSeccomp() -{ -#if __linux__ -#if HAVE_SECCOMP - scmp_filter_ctx ctx; - - // Pretend that syscalls we don't yet know about don't exist. - // This is the best option for compatibility: after all, they did in fact not exist not too long ago. - if (!(ctx = seccomp_init(SCMP_ACT_ERRNO(ENOSYS)))) - throw SysError("unable to initialize seccomp mode 2"); - - Finally cleanup([&]() { - seccomp_release(ctx); - }); - - if (nativeSystem == "x86_64-linux" && - seccomp_arch_add(ctx, SCMP_ARCH_X86) != 0) - throw SysError("unable to add 32-bit seccomp architecture"); - - if (nativeSystem == "x86_64-linux" && - seccomp_arch_add(ctx, SCMP_ARCH_X32) != 0) - throw SysError("unable to add X32 seccomp architecture"); - - if (nativeSystem == "aarch64-linux" && - seccomp_arch_add(ctx, SCMP_ARCH_ARM) != 0) - printError("unable to add ARM seccomp architecture; this may result in spurious build failures if running 32-bit ARM processes"); - - if (nativeSystem == "mips64-linux" && - seccomp_arch_add(ctx, SCMP_ARCH_MIPS) != 0) - printError("unable to add mips seccomp architecture"); - - if (nativeSystem == "mips64-linux" && - seccomp_arch_add(ctx, SCMP_ARCH_MIPS64N32) != 0) - printError("unable to add mips64-*abin32 seccomp architecture"); - - if (nativeSystem == "mips64el-linux" && - seccomp_arch_add(ctx, SCMP_ARCH_MIPSEL) != 0) - printError("unable to add mipsel seccomp architecture"); - - if (nativeSystem == "mips64el-linux" && - seccomp_arch_add(ctx, SCMP_ARCH_MIPSEL64N32) != 0) - printError("unable to add mips64el-*abin32 seccomp architecture"); - - // This list is intended for machine consumption. - // Please keep its format, order and BEGIN/END markers. - // - // Currently, it is up to date with libseccomp 2.5.5 and glibc 2.39. - // Run check-syscalls to determine which new syscalls should be added. - // New syscalls must be audited and handled in a way that blocks the following dangerous operations: - // * Creation of non-empty setuid/setgid files - // * Creation of extended attributes (including ACLs) - // - // BEGIN extract-syscalls - allowSyscall(ctx, SCMP_SYS(accept)); - allowSyscall(ctx, SCMP_SYS(accept4)); - allowSyscall(ctx, SCMP_SYS(access)); - allowSyscall(ctx, SCMP_SYS(acct)); - allowSyscall(ctx, SCMP_SYS(add_key)); - allowSyscall(ctx, SCMP_SYS(adjtimex)); - allowSyscall(ctx, SCMP_SYS(afs_syscall)); - allowSyscall(ctx, SCMP_SYS(alarm)); - allowSyscall(ctx, SCMP_SYS(arch_prctl)); - allowSyscall(ctx, SCMP_SYS(arm_fadvise64_64)); - allowSyscall(ctx, SCMP_SYS(arm_sync_file_range)); - allowSyscall(ctx, SCMP_SYS(bdflush)); - allowSyscall(ctx, SCMP_SYS(bind)); - allowSyscall(ctx, SCMP_SYS(bpf)); - allowSyscall(ctx, SCMP_SYS(break)); - allowSyscall(ctx, SCMP_SYS(breakpoint)); - allowSyscall(ctx, SCMP_SYS(brk)); - allowSyscall(ctx, SCMP_SYS(cachectl)); - allowSyscall(ctx, SCMP_SYS(cacheflush)); - allowSyscall(ctx, SCMP_SYS(cachestat)); - allowSyscall(ctx, SCMP_SYS(capget)); - allowSyscall(ctx, SCMP_SYS(capset)); - allowSyscall(ctx, SCMP_SYS(chdir)); - // skip chmod (dangerous) - allowSyscall(ctx, SCMP_SYS(chown)); - allowSyscall(ctx, SCMP_SYS(chown32)); - allowSyscall(ctx, SCMP_SYS(chroot)); - allowSyscall(ctx, SCMP_SYS(clock_adjtime)); - allowSyscall(ctx, SCMP_SYS(clock_adjtime64)); - allowSyscall(ctx, SCMP_SYS(clock_getres)); - allowSyscall(ctx, SCMP_SYS(clock_getres_time64)); - allowSyscall(ctx, SCMP_SYS(clock_gettime)); - allowSyscall(ctx, SCMP_SYS(clock_gettime64)); - allowSyscall(ctx, SCMP_SYS(clock_nanosleep)); - allowSyscall(ctx, SCMP_SYS(clock_nanosleep_time64)); - allowSyscall(ctx, SCMP_SYS(clock_settime)); - allowSyscall(ctx, SCMP_SYS(clock_settime64)); - allowSyscall(ctx, SCMP_SYS(clone)); - allowSyscall(ctx, SCMP_SYS(clone3)); - allowSyscall(ctx, SCMP_SYS(close)); - allowSyscall(ctx, SCMP_SYS(close_range)); - allowSyscall(ctx, SCMP_SYS(connect)); - allowSyscall(ctx, SCMP_SYS(copy_file_range)); - allowSyscall(ctx, SCMP_SYS(creat)); - allowSyscall(ctx, SCMP_SYS(create_module)); - allowSyscall(ctx, SCMP_SYS(delete_module)); - allowSyscall(ctx, SCMP_SYS(dup)); - allowSyscall(ctx, SCMP_SYS(dup2)); - allowSyscall(ctx, SCMP_SYS(dup3)); - allowSyscall(ctx, SCMP_SYS(epoll_create)); - allowSyscall(ctx, SCMP_SYS(epoll_create1)); - allowSyscall(ctx, SCMP_SYS(epoll_ctl)); - allowSyscall(ctx, SCMP_SYS(epoll_ctl_old)); - allowSyscall(ctx, SCMP_SYS(epoll_pwait)); - allowSyscall(ctx, SCMP_SYS(epoll_pwait2)); - allowSyscall(ctx, SCMP_SYS(epoll_wait)); - allowSyscall(ctx, SCMP_SYS(epoll_wait_old)); - allowSyscall(ctx, SCMP_SYS(eventfd)); - allowSyscall(ctx, SCMP_SYS(eventfd2)); - allowSyscall(ctx, SCMP_SYS(execve)); - allowSyscall(ctx, SCMP_SYS(execveat)); - allowSyscall(ctx, SCMP_SYS(exit)); - allowSyscall(ctx, SCMP_SYS(exit_group)); - allowSyscall(ctx, SCMP_SYS(faccessat)); - allowSyscall(ctx, SCMP_SYS(faccessat2)); - allowSyscall(ctx, SCMP_SYS(fadvise64)); - allowSyscall(ctx, SCMP_SYS(fadvise64_64)); - allowSyscall(ctx, SCMP_SYS(fallocate)); - allowSyscall(ctx, SCMP_SYS(fanotify_init)); - allowSyscall(ctx, SCMP_SYS(fanotify_mark)); - allowSyscall(ctx, SCMP_SYS(fchdir)); - // skip fchmod (dangerous) - // skip fchmodat (dangerous) - // skip fchmodat2 (dangerous) - allowSyscall(ctx, SCMP_SYS(fchown)); - allowSyscall(ctx, SCMP_SYS(fchown32)); - allowSyscall(ctx, SCMP_SYS(fchownat)); - allowSyscall(ctx, SCMP_SYS(fcntl)); - allowSyscall(ctx, SCMP_SYS(fcntl64)); - allowSyscall(ctx, SCMP_SYS(fdatasync)); - allowSyscall(ctx, SCMP_SYS(fgetxattr)); - allowSyscall(ctx, SCMP_SYS(finit_module)); - allowSyscall(ctx, SCMP_SYS(flistxattr)); - allowSyscall(ctx, SCMP_SYS(flock)); - allowSyscall(ctx, SCMP_SYS(fork)); - allowSyscall(ctx, SCMP_SYS(fremovexattr)); - allowSyscall(ctx, SCMP_SYS(fsconfig)); - // skip fsetxattr (dangerous) - allowSyscall(ctx, SCMP_SYS(fsmount)); - allowSyscall(ctx, SCMP_SYS(fsopen)); - allowSyscall(ctx, SCMP_SYS(fspick)); - allowSyscall(ctx, SCMP_SYS(fstat)); - allowSyscall(ctx, SCMP_SYS(fstat64)); - allowSyscall(ctx, SCMP_SYS(fstatat64)); - allowSyscall(ctx, SCMP_SYS(fstatfs)); - allowSyscall(ctx, SCMP_SYS(fstatfs64)); - allowSyscall(ctx, SCMP_SYS(fsync)); - allowSyscall(ctx, SCMP_SYS(ftime)); - allowSyscall(ctx, SCMP_SYS(ftruncate)); - allowSyscall(ctx, SCMP_SYS(ftruncate64)); - allowSyscall(ctx, SCMP_SYS(futex)); - allowSyscall(ctx, SCMP_SYS(futex_requeue)); - allowSyscall(ctx, SCMP_SYS(futex_time64)); - allowSyscall(ctx, SCMP_SYS(futex_wait)); - allowSyscall(ctx, SCMP_SYS(futex_waitv)); - allowSyscall(ctx, SCMP_SYS(futex_wake)); - allowSyscall(ctx, SCMP_SYS(futimesat)); - allowSyscall(ctx, SCMP_SYS(getcpu)); - allowSyscall(ctx, SCMP_SYS(getcwd)); - allowSyscall(ctx, SCMP_SYS(getdents)); - allowSyscall(ctx, SCMP_SYS(getdents64)); - allowSyscall(ctx, SCMP_SYS(getegid)); - allowSyscall(ctx, SCMP_SYS(getegid32)); - allowSyscall(ctx, SCMP_SYS(geteuid)); - allowSyscall(ctx, SCMP_SYS(geteuid32)); - allowSyscall(ctx, SCMP_SYS(getgid)); - allowSyscall(ctx, SCMP_SYS(getgid32)); - allowSyscall(ctx, SCMP_SYS(getgroups)); - allowSyscall(ctx, SCMP_SYS(getgroups32)); - allowSyscall(ctx, SCMP_SYS(getitimer)); - allowSyscall(ctx, SCMP_SYS(get_kernel_syms)); - allowSyscall(ctx, SCMP_SYS(get_mempolicy)); - allowSyscall(ctx, SCMP_SYS(getpeername)); - allowSyscall(ctx, SCMP_SYS(getpgid)); - allowSyscall(ctx, SCMP_SYS(getpgrp)); - allowSyscall(ctx, SCMP_SYS(getpid)); - allowSyscall(ctx, SCMP_SYS(getpmsg)); - allowSyscall(ctx, SCMP_SYS(getppid)); - allowSyscall(ctx, SCMP_SYS(getpriority)); - allowSyscall(ctx, SCMP_SYS(getrandom)); - allowSyscall(ctx, SCMP_SYS(getresgid)); - allowSyscall(ctx, SCMP_SYS(getresgid32)); - allowSyscall(ctx, SCMP_SYS(getresuid)); - allowSyscall(ctx, SCMP_SYS(getresuid32)); - allowSyscall(ctx, SCMP_SYS(getrlimit)); - allowSyscall(ctx, SCMP_SYS(get_robust_list)); - allowSyscall(ctx, SCMP_SYS(getrusage)); - allowSyscall(ctx, SCMP_SYS(getsid)); - allowSyscall(ctx, SCMP_SYS(getsockname)); - allowSyscall(ctx, SCMP_SYS(getsockopt)); - allowSyscall(ctx, SCMP_SYS(get_thread_area)); - allowSyscall(ctx, SCMP_SYS(gettid)); - allowSyscall(ctx, SCMP_SYS(gettimeofday)); - allowSyscall(ctx, SCMP_SYS(get_tls)); - allowSyscall(ctx, SCMP_SYS(getuid)); - allowSyscall(ctx, SCMP_SYS(getuid32)); - allowSyscall(ctx, SCMP_SYS(getxattr)); - allowSyscall(ctx, SCMP_SYS(gtty)); - allowSyscall(ctx, SCMP_SYS(idle)); - allowSyscall(ctx, SCMP_SYS(init_module)); - allowSyscall(ctx, SCMP_SYS(inotify_add_watch)); - allowSyscall(ctx, SCMP_SYS(inotify_init)); - allowSyscall(ctx, SCMP_SYS(inotify_init1)); - allowSyscall(ctx, SCMP_SYS(inotify_rm_watch)); - allowSyscall(ctx, SCMP_SYS(io_cancel)); - allowSyscall(ctx, SCMP_SYS(ioctl)); - allowSyscall(ctx, SCMP_SYS(io_destroy)); - allowSyscall(ctx, SCMP_SYS(io_getevents)); - allowSyscall(ctx, SCMP_SYS(ioperm)); - allowSyscall(ctx, SCMP_SYS(io_pgetevents)); - allowSyscall(ctx, SCMP_SYS(io_pgetevents_time64)); - allowSyscall(ctx, SCMP_SYS(iopl)); - allowSyscall(ctx, SCMP_SYS(ioprio_get)); - allowSyscall(ctx, SCMP_SYS(ioprio_set)); - allowSyscall(ctx, SCMP_SYS(io_setup)); - allowSyscall(ctx, SCMP_SYS(io_submit)); - // skip io_uring_enter (may become dangerous) - // skip io_uring_register (may become dangerous) - // skip io_uring_setup (may become dangerous) - allowSyscall(ctx, SCMP_SYS(ipc)); - allowSyscall(ctx, SCMP_SYS(kcmp)); - allowSyscall(ctx, SCMP_SYS(kexec_file_load)); - allowSyscall(ctx, SCMP_SYS(kexec_load)); - allowSyscall(ctx, SCMP_SYS(keyctl)); - allowSyscall(ctx, SCMP_SYS(kill)); - allowSyscall(ctx, SCMP_SYS(landlock_add_rule)); - allowSyscall(ctx, SCMP_SYS(landlock_create_ruleset)); - allowSyscall(ctx, SCMP_SYS(landlock_restrict_self)); - allowSyscall(ctx, SCMP_SYS(lchown)); - allowSyscall(ctx, SCMP_SYS(lchown32)); - allowSyscall(ctx, SCMP_SYS(lgetxattr)); - allowSyscall(ctx, SCMP_SYS(link)); - allowSyscall(ctx, SCMP_SYS(linkat)); - allowSyscall(ctx, SCMP_SYS(listen)); - allowSyscall(ctx, SCMP_SYS(listxattr)); - allowSyscall(ctx, SCMP_SYS(llistxattr)); - allowSyscall(ctx, SCMP_SYS(_llseek)); - allowSyscall(ctx, SCMP_SYS(lock)); - allowSyscall(ctx, SCMP_SYS(lookup_dcookie)); - allowSyscall(ctx, SCMP_SYS(lremovexattr)); - allowSyscall(ctx, SCMP_SYS(lseek)); - // skip lsetxattr (dangerous) - allowSyscall(ctx, SCMP_SYS(lstat)); - allowSyscall(ctx, SCMP_SYS(lstat64)); - allowSyscall(ctx, SCMP_SYS(madvise)); - allowSyscall(ctx, SCMP_SYS(map_shadow_stack)); - allowSyscall(ctx, SCMP_SYS(mbind)); - allowSyscall(ctx, SCMP_SYS(membarrier)); - allowSyscall(ctx, SCMP_SYS(memfd_create)); - allowSyscall(ctx, SCMP_SYS(memfd_secret)); - allowSyscall(ctx, SCMP_SYS(migrate_pages)); - allowSyscall(ctx, SCMP_SYS(mincore)); - allowSyscall(ctx, SCMP_SYS(mkdir)); - allowSyscall(ctx, SCMP_SYS(mkdirat)); - allowSyscall(ctx, SCMP_SYS(mknod)); - allowSyscall(ctx, SCMP_SYS(mknodat)); - allowSyscall(ctx, SCMP_SYS(mlock)); - allowSyscall(ctx, SCMP_SYS(mlock2)); - allowSyscall(ctx, SCMP_SYS(mlockall)); - allowSyscall(ctx, SCMP_SYS(mmap)); - allowSyscall(ctx, SCMP_SYS(mmap2)); - allowSyscall(ctx, SCMP_SYS(modify_ldt)); - allowSyscall(ctx, SCMP_SYS(mount)); - allowSyscall(ctx, SCMP_SYS(mount_setattr)); - allowSyscall(ctx, SCMP_SYS(move_mount)); - allowSyscall(ctx, SCMP_SYS(move_pages)); - allowSyscall(ctx, SCMP_SYS(mprotect)); - allowSyscall(ctx, SCMP_SYS(mpx)); - allowSyscall(ctx, SCMP_SYS(mq_getsetattr)); - allowSyscall(ctx, SCMP_SYS(mq_notify)); - allowSyscall(ctx, SCMP_SYS(mq_open)); - allowSyscall(ctx, SCMP_SYS(mq_timedreceive)); - allowSyscall(ctx, SCMP_SYS(mq_timedreceive_time64)); - allowSyscall(ctx, SCMP_SYS(mq_timedsend)); - allowSyscall(ctx, SCMP_SYS(mq_timedsend_time64)); - allowSyscall(ctx, SCMP_SYS(mq_unlink)); - allowSyscall(ctx, SCMP_SYS(mremap)); - allowSyscall(ctx, SCMP_SYS(msgctl)); - allowSyscall(ctx, SCMP_SYS(msgget)); - allowSyscall(ctx, SCMP_SYS(msgrcv)); - allowSyscall(ctx, SCMP_SYS(msgsnd)); - allowSyscall(ctx, SCMP_SYS(msync)); - allowSyscall(ctx, SCMP_SYS(multiplexer)); - allowSyscall(ctx, SCMP_SYS(munlock)); - allowSyscall(ctx, SCMP_SYS(munlockall)); - allowSyscall(ctx, SCMP_SYS(munmap)); - allowSyscall(ctx, SCMP_SYS(name_to_handle_at)); - allowSyscall(ctx, SCMP_SYS(nanosleep)); - allowSyscall(ctx, SCMP_SYS(newfstatat)); - allowSyscall(ctx, SCMP_SYS(_newselect)); - allowSyscall(ctx, SCMP_SYS(nfsservctl)); - allowSyscall(ctx, SCMP_SYS(nice)); - allowSyscall(ctx, SCMP_SYS(oldfstat)); - allowSyscall(ctx, SCMP_SYS(oldlstat)); - allowSyscall(ctx, SCMP_SYS(oldolduname)); - allowSyscall(ctx, SCMP_SYS(oldstat)); - allowSyscall(ctx, SCMP_SYS(olduname)); - allowSyscall(ctx, SCMP_SYS(open)); - allowSyscall(ctx, SCMP_SYS(openat)); - allowSyscall(ctx, SCMP_SYS(openat2)); - allowSyscall(ctx, SCMP_SYS(open_by_handle_at)); - allowSyscall(ctx, SCMP_SYS(open_tree)); - allowSyscall(ctx, SCMP_SYS(pause)); - allowSyscall(ctx, SCMP_SYS(pciconfig_iobase)); - allowSyscall(ctx, SCMP_SYS(pciconfig_read)); - allowSyscall(ctx, SCMP_SYS(pciconfig_write)); - allowSyscall(ctx, SCMP_SYS(perf_event_open)); - allowSyscall(ctx, SCMP_SYS(personality)); - allowSyscall(ctx, SCMP_SYS(pidfd_getfd)); - allowSyscall(ctx, SCMP_SYS(pidfd_open)); - allowSyscall(ctx, SCMP_SYS(pidfd_send_signal)); - allowSyscall(ctx, SCMP_SYS(pipe)); - allowSyscall(ctx, SCMP_SYS(pipe2)); - allowSyscall(ctx, SCMP_SYS(pivot_root)); - allowSyscall(ctx, SCMP_SYS(pkey_alloc)); - allowSyscall(ctx, SCMP_SYS(pkey_free)); - allowSyscall(ctx, SCMP_SYS(pkey_mprotect)); - allowSyscall(ctx, SCMP_SYS(poll)); - allowSyscall(ctx, SCMP_SYS(ppoll)); - allowSyscall(ctx, SCMP_SYS(ppoll_time64)); - allowSyscall(ctx, SCMP_SYS(prctl)); - allowSyscall(ctx, SCMP_SYS(pread64)); - allowSyscall(ctx, SCMP_SYS(preadv)); - allowSyscall(ctx, SCMP_SYS(preadv2)); - allowSyscall(ctx, SCMP_SYS(prlimit64)); - allowSyscall(ctx, SCMP_SYS(process_madvise)); - allowSyscall(ctx, SCMP_SYS(process_mrelease)); - allowSyscall(ctx, SCMP_SYS(process_vm_readv)); - allowSyscall(ctx, SCMP_SYS(process_vm_writev)); - allowSyscall(ctx, SCMP_SYS(prof)); - allowSyscall(ctx, SCMP_SYS(profil)); - allowSyscall(ctx, SCMP_SYS(pselect6)); - allowSyscall(ctx, SCMP_SYS(pselect6_time64)); - allowSyscall(ctx, SCMP_SYS(ptrace)); - allowSyscall(ctx, SCMP_SYS(putpmsg)); - allowSyscall(ctx, SCMP_SYS(pwrite64)); - allowSyscall(ctx, SCMP_SYS(pwritev)); - allowSyscall(ctx, SCMP_SYS(pwritev2)); - allowSyscall(ctx, SCMP_SYS(query_module)); - allowSyscall(ctx, SCMP_SYS(quotactl)); - allowSyscall(ctx, SCMP_SYS(quotactl_fd)); - allowSyscall(ctx, SCMP_SYS(read)); - allowSyscall(ctx, SCMP_SYS(readahead)); - allowSyscall(ctx, SCMP_SYS(readdir)); - allowSyscall(ctx, SCMP_SYS(readlink)); - allowSyscall(ctx, SCMP_SYS(readlinkat)); - allowSyscall(ctx, SCMP_SYS(readv)); - allowSyscall(ctx, SCMP_SYS(reboot)); - allowSyscall(ctx, SCMP_SYS(recv)); - allowSyscall(ctx, SCMP_SYS(recvfrom)); - allowSyscall(ctx, SCMP_SYS(recvmmsg)); - allowSyscall(ctx, SCMP_SYS(recvmmsg_time64)); - allowSyscall(ctx, SCMP_SYS(recvmsg)); - allowSyscall(ctx, SCMP_SYS(remap_file_pages)); - allowSyscall(ctx, SCMP_SYS(removexattr)); - allowSyscall(ctx, SCMP_SYS(rename)); - allowSyscall(ctx, SCMP_SYS(renameat)); - allowSyscall(ctx, SCMP_SYS(renameat2)); - allowSyscall(ctx, SCMP_SYS(request_key)); - allowSyscall(ctx, SCMP_SYS(restart_syscall)); - allowSyscall(ctx, SCMP_SYS(riscv_flush_icache)); - allowSyscall(ctx, SCMP_SYS(rmdir)); - allowSyscall(ctx, SCMP_SYS(rseq)); - allowSyscall(ctx, SCMP_SYS(rtas)); - allowSyscall(ctx, SCMP_SYS(rt_sigaction)); - allowSyscall(ctx, SCMP_SYS(rt_sigpending)); - allowSyscall(ctx, SCMP_SYS(rt_sigprocmask)); - allowSyscall(ctx, SCMP_SYS(rt_sigqueueinfo)); - allowSyscall(ctx, SCMP_SYS(rt_sigreturn)); - allowSyscall(ctx, SCMP_SYS(rt_sigsuspend)); - allowSyscall(ctx, SCMP_SYS(rt_sigtimedwait)); - allowSyscall(ctx, SCMP_SYS(rt_sigtimedwait_time64)); - allowSyscall(ctx, SCMP_SYS(rt_tgsigqueueinfo)); - allowSyscall(ctx, SCMP_SYS(s390_guarded_storage)); - allowSyscall(ctx, SCMP_SYS(s390_pci_mmio_read)); - allowSyscall(ctx, SCMP_SYS(s390_pci_mmio_write)); - allowSyscall(ctx, SCMP_SYS(s390_runtime_instr)); - allowSyscall(ctx, SCMP_SYS(s390_sthyi)); - allowSyscall(ctx, SCMP_SYS(sched_getaffinity)); - allowSyscall(ctx, SCMP_SYS(sched_getattr)); - allowSyscall(ctx, SCMP_SYS(sched_getparam)); - allowSyscall(ctx, SCMP_SYS(sched_get_priority_max)); - allowSyscall(ctx, SCMP_SYS(sched_get_priority_min)); - allowSyscall(ctx, SCMP_SYS(sched_getscheduler)); - allowSyscall(ctx, SCMP_SYS(sched_rr_get_interval)); - allowSyscall(ctx, SCMP_SYS(sched_rr_get_interval_time64)); - allowSyscall(ctx, SCMP_SYS(sched_setaffinity)); - allowSyscall(ctx, SCMP_SYS(sched_setattr)); - allowSyscall(ctx, SCMP_SYS(sched_setparam)); - allowSyscall(ctx, SCMP_SYS(sched_setscheduler)); - allowSyscall(ctx, SCMP_SYS(sched_yield)); - allowSyscall(ctx, SCMP_SYS(seccomp)); - allowSyscall(ctx, SCMP_SYS(security)); - allowSyscall(ctx, SCMP_SYS(select)); - allowSyscall(ctx, SCMP_SYS(semctl)); - allowSyscall(ctx, SCMP_SYS(semget)); - allowSyscall(ctx, SCMP_SYS(semop)); - allowSyscall(ctx, SCMP_SYS(semtimedop)); - allowSyscall(ctx, SCMP_SYS(semtimedop_time64)); - allowSyscall(ctx, SCMP_SYS(send)); - allowSyscall(ctx, SCMP_SYS(sendfile)); - allowSyscall(ctx, SCMP_SYS(sendfile64)); - allowSyscall(ctx, SCMP_SYS(sendmmsg)); - allowSyscall(ctx, SCMP_SYS(sendmsg)); - allowSyscall(ctx, SCMP_SYS(sendto)); - allowSyscall(ctx, SCMP_SYS(setdomainname)); - allowSyscall(ctx, SCMP_SYS(setfsgid)); - allowSyscall(ctx, SCMP_SYS(setfsgid32)); - allowSyscall(ctx, SCMP_SYS(setfsuid)); - allowSyscall(ctx, SCMP_SYS(setfsuid32)); - allowSyscall(ctx, SCMP_SYS(setgid)); - allowSyscall(ctx, SCMP_SYS(setgid32)); - allowSyscall(ctx, SCMP_SYS(setgroups)); - allowSyscall(ctx, SCMP_SYS(setgroups32)); - allowSyscall(ctx, SCMP_SYS(sethostname)); - allowSyscall(ctx, SCMP_SYS(setitimer)); - allowSyscall(ctx, SCMP_SYS(set_mempolicy)); - allowSyscall(ctx, SCMP_SYS(set_mempolicy_home_node)); - allowSyscall(ctx, SCMP_SYS(setns)); - allowSyscall(ctx, SCMP_SYS(setpgid)); - allowSyscall(ctx, SCMP_SYS(setpriority)); - allowSyscall(ctx, SCMP_SYS(setregid)); - allowSyscall(ctx, SCMP_SYS(setregid32)); - allowSyscall(ctx, SCMP_SYS(setresgid)); - allowSyscall(ctx, SCMP_SYS(setresgid32)); - allowSyscall(ctx, SCMP_SYS(setresuid)); - allowSyscall(ctx, SCMP_SYS(setresuid32)); - allowSyscall(ctx, SCMP_SYS(setreuid)); - allowSyscall(ctx, SCMP_SYS(setreuid32)); - allowSyscall(ctx, SCMP_SYS(setrlimit)); - allowSyscall(ctx, SCMP_SYS(set_robust_list)); - allowSyscall(ctx, SCMP_SYS(setsid)); - allowSyscall(ctx, SCMP_SYS(setsockopt)); - allowSyscall(ctx, SCMP_SYS(set_thread_area)); - allowSyscall(ctx, SCMP_SYS(set_tid_address)); - allowSyscall(ctx, SCMP_SYS(settimeofday)); - allowSyscall(ctx, SCMP_SYS(set_tls)); - allowSyscall(ctx, SCMP_SYS(setuid)); - allowSyscall(ctx, SCMP_SYS(setuid32)); - // skip setxattr (dangerous) - allowSyscall(ctx, SCMP_SYS(sgetmask)); - allowSyscall(ctx, SCMP_SYS(shmat)); - allowSyscall(ctx, SCMP_SYS(shmctl)); - allowSyscall(ctx, SCMP_SYS(shmdt)); - allowSyscall(ctx, SCMP_SYS(shmget)); - allowSyscall(ctx, SCMP_SYS(shutdown)); - allowSyscall(ctx, SCMP_SYS(sigaction)); - allowSyscall(ctx, SCMP_SYS(sigaltstack)); - allowSyscall(ctx, SCMP_SYS(signal)); - allowSyscall(ctx, SCMP_SYS(signalfd)); - allowSyscall(ctx, SCMP_SYS(signalfd4)); - allowSyscall(ctx, SCMP_SYS(sigpending)); - allowSyscall(ctx, SCMP_SYS(sigprocmask)); - allowSyscall(ctx, SCMP_SYS(sigreturn)); - allowSyscall(ctx, SCMP_SYS(sigsuspend)); - allowSyscall(ctx, SCMP_SYS(socket)); - allowSyscall(ctx, SCMP_SYS(socketcall)); - allowSyscall(ctx, SCMP_SYS(socketpair)); - allowSyscall(ctx, SCMP_SYS(splice)); - allowSyscall(ctx, SCMP_SYS(spu_create)); - allowSyscall(ctx, SCMP_SYS(spu_run)); - allowSyscall(ctx, SCMP_SYS(ssetmask)); - allowSyscall(ctx, SCMP_SYS(stat)); - allowSyscall(ctx, SCMP_SYS(stat64)); - allowSyscall(ctx, SCMP_SYS(statfs)); - allowSyscall(ctx, SCMP_SYS(statfs64)); - allowSyscall(ctx, SCMP_SYS(statx)); - allowSyscall(ctx, SCMP_SYS(stime)); - allowSyscall(ctx, SCMP_SYS(stty)); - allowSyscall(ctx, SCMP_SYS(subpage_prot)); - allowSyscall(ctx, SCMP_SYS(swapcontext)); - allowSyscall(ctx, SCMP_SYS(swapoff)); - allowSyscall(ctx, SCMP_SYS(swapon)); - allowSyscall(ctx, SCMP_SYS(switch_endian)); - allowSyscall(ctx, SCMP_SYS(symlink)); - allowSyscall(ctx, SCMP_SYS(symlinkat)); - allowSyscall(ctx, SCMP_SYS(sync)); - allowSyscall(ctx, SCMP_SYS(sync_file_range)); - allowSyscall(ctx, SCMP_SYS(sync_file_range2)); - allowSyscall(ctx, SCMP_SYS(syncfs)); - allowSyscall(ctx, SCMP_SYS(syscall)); - allowSyscall(ctx, SCMP_SYS(_sysctl)); - allowSyscall(ctx, SCMP_SYS(sys_debug_setcontext)); - allowSyscall(ctx, SCMP_SYS(sysfs)); - allowSyscall(ctx, SCMP_SYS(sysinfo)); - allowSyscall(ctx, SCMP_SYS(syslog)); - allowSyscall(ctx, SCMP_SYS(sysmips)); - allowSyscall(ctx, SCMP_SYS(tee)); - allowSyscall(ctx, SCMP_SYS(tgkill)); - allowSyscall(ctx, SCMP_SYS(time)); - allowSyscall(ctx, SCMP_SYS(timer_create)); - allowSyscall(ctx, SCMP_SYS(timer_delete)); - allowSyscall(ctx, SCMP_SYS(timerfd)); - allowSyscall(ctx, SCMP_SYS(timerfd_create)); - allowSyscall(ctx, SCMP_SYS(timerfd_gettime)); - allowSyscall(ctx, SCMP_SYS(timerfd_gettime64)); - allowSyscall(ctx, SCMP_SYS(timerfd_settime)); - allowSyscall(ctx, SCMP_SYS(timerfd_settime64)); - allowSyscall(ctx, SCMP_SYS(timer_getoverrun)); - allowSyscall(ctx, SCMP_SYS(timer_gettime)); - allowSyscall(ctx, SCMP_SYS(timer_gettime64)); - allowSyscall(ctx, SCMP_SYS(timer_settime)); - allowSyscall(ctx, SCMP_SYS(timer_settime64)); - allowSyscall(ctx, SCMP_SYS(times)); - allowSyscall(ctx, SCMP_SYS(tkill)); - allowSyscall(ctx, SCMP_SYS(truncate)); - allowSyscall(ctx, SCMP_SYS(truncate64)); - allowSyscall(ctx, SCMP_SYS(tuxcall)); - allowSyscall(ctx, SCMP_SYS(ugetrlimit)); - allowSyscall(ctx, SCMP_SYS(ulimit)); - allowSyscall(ctx, SCMP_SYS(umask)); - allowSyscall(ctx, SCMP_SYS(umount)); - allowSyscall(ctx, SCMP_SYS(umount2)); - allowSyscall(ctx, SCMP_SYS(uname)); - allowSyscall(ctx, SCMP_SYS(unlink)); - allowSyscall(ctx, SCMP_SYS(unlinkat)); - allowSyscall(ctx, SCMP_SYS(unshare)); - allowSyscall(ctx, SCMP_SYS(uselib)); - allowSyscall(ctx, SCMP_SYS(userfaultfd)); - allowSyscall(ctx, SCMP_SYS(usr26)); - allowSyscall(ctx, SCMP_SYS(usr32)); - allowSyscall(ctx, SCMP_SYS(ustat)); - allowSyscall(ctx, SCMP_SYS(utime)); - allowSyscall(ctx, SCMP_SYS(utimensat)); - allowSyscall(ctx, SCMP_SYS(utimensat_time64)); - allowSyscall(ctx, SCMP_SYS(utimes)); - allowSyscall(ctx, SCMP_SYS(vfork)); - allowSyscall(ctx, SCMP_SYS(vhangup)); - allowSyscall(ctx, SCMP_SYS(vm86)); - allowSyscall(ctx, SCMP_SYS(vm86old)); - allowSyscall(ctx, SCMP_SYS(vmsplice)); - allowSyscall(ctx, SCMP_SYS(vserver)); - allowSyscall(ctx, SCMP_SYS(wait4)); - allowSyscall(ctx, SCMP_SYS(waitid)); - allowSyscall(ctx, SCMP_SYS(waitpid)); - allowSyscall(ctx, SCMP_SYS(write)); - allowSyscall(ctx, SCMP_SYS(writev)); - // END extract-syscalls - - // chmod family: prevent adding setuid/setgid bits to existing files. - // The Nix store does not support setuid/setgid, and even their temporary creation can weaken the security of the sandbox. - ALLOW_CHMOD_IF_SAFE(ctx, SCMP_SYS(chmod), 1); - ALLOW_CHMOD_IF_SAFE(ctx, SCMP_SYS(fchmod), 1); - ALLOW_CHMOD_IF_SAFE(ctx, SCMP_SYS(fchmodat), 2); - ALLOW_CHMOD_IF_SAFE(ctx, SCMP_SYS(fchmodat2), 2); - - // setxattr family: prevent creation of extended attributes or ACLs. - // Not all filesystems support them, and they're incompatible with the NAR format. - if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 || - seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), 0) != 0 || - seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0) - throw SysError("unable to add seccomp rule"); - - // Set the NO_NEW_PRIVS prctl flag. - // This both makes loading seccomp filters work for unprivileged users, - // and is an additional security measure in its own right. - if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 1) != 0) - throw SysError("unable to set 'no new privileges' seccomp attribute"); - - if (seccomp_load(ctx) != 0) - throw SysError("unable to load seccomp BPF program"); -#else - // Still set the no-new-privileges flag if libseccomp is not available. - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) - throw SysError("PR_SET_NO_NEW_PRIVS failed"); -#endif -#endif -} - void LocalDerivationGoal::runChild() { @@ -1960,7 +1361,7 @@ void LocalDerivationGoal::runChild() commonChildInit(); - setupSeccomp(); + setupSyscallFilter(); bool setUser = true; @@ -3116,6 +2517,24 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo }; if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) { + if (get(*structuredAttrs, "allowedReferences")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'allowedReferences'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "allowedRequisites")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'allowedRequisites'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "disallowedRequisites")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'disallowedRequisites'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "disallowedReferences")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'disallowedReferences'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "maxSize")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'maxSize'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "maxClosureSize")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'maxClosureSize'; use 'outputChecks' instead"); + } if (auto outputChecks = get(*structuredAttrs, "outputChecks")) { if (auto output = get(*outputChecks, outputName)) { Checks checks; diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/build/local-derivation-goal.hh index 237417b42..e87f2c696 100644 --- a/src/libstore/build/local-derivation-goal.hh +++ b/src/libstore/build/local-derivation-goal.hh @@ -340,6 +340,12 @@ protected: virtual Pid startChild(std::function<void()> openSlave); /** + * Set up the system call filtering required for the sandbox. + * This currently only has an effect on Linux. + */ + virtual void setupSyscallFilter() {} + + /** * Execute the builder, replacing the current process. * Generally this means an `execve` call. */ diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index fcb947f96..9cb805444 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -477,8 +477,17 @@ struct curlFileTransfer : public FileTransfer ~curlFileTransfer() { - stopWorkerThread(); - + try { + stopWorkerThread(); + } catch (nix::Error & e) { + // This can only fail if a socket to our own process cannot be + // written to, so it is always a bug in the program if it fails. + // + // Joining the thread would probably only cause a deadlock if this + // happened, so just die on purpose. + printError("failed to join curl file transfer worker thread: %1%", e.what()); + std::terminate(); + } workerThread.join(); if (curlm) curl_multi_cleanup(curlm); diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc index 833482815..d0897b81f 100644 --- a/src/libstore/machines.cc +++ b/src/libstore/machines.cc @@ -33,7 +33,7 @@ Machine::Machine(decltype(storeUri) storeUri, systemTypes(systemTypes), sshKey(sshKey), maxJobs(maxJobs), - speedFactor(speedFactor == 0.0f ? 1.0f : std::move(speedFactor)), + speedFactor(speedFactor == 0.0f ? 1.0f : speedFactor), supportedFeatures(supportedFeatures), mandatoryFeatures(mandatoryFeatures), sshPublicHostKey(sshPublicHostKey) diff --git a/src/libstore/platform/linux.cc b/src/libstore/platform/linux.cc index 204f62b71..03b8bc0be 100644 --- a/src/libstore/platform/linux.cc +++ b/src/libstore/platform/linux.cc @@ -8,8 +8,23 @@ #include <grp.h> #include <regex> +#include <sys/prctl.h> + +#if HAVE_SECCOMP +#include <linux/filter.h> +#include <seccomp.h> +#endif namespace nix { + +namespace { +/** + * The system for which Nix is compiled. + */ +[[gnu::unused]] +constexpr const std::string_view nativeSystem = SYSTEM; +} + static RegisterStoreImplementation<LinuxLocalStore, LocalStoreConfig> regLocalStore; static void readProcLink(const std::string & file, UncheckedRoots & roots) @@ -119,6 +134,617 @@ void LinuxLocalStore::findPlatformRoots(UncheckedRoots & unchecked) readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked); } +#if HAVE_SECCOMP + +static void allowSyscall(scmp_filter_ctx ctx, int syscall) { + if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 0) != 0) + throw SysError("unable to add seccomp rule"); +} + +#define ALLOW_CHMOD_IF_SAFE(ctx, syscall, modePos) \ + if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscall, 1, SCMP_A##modePos(SCMP_CMP_MASKED_EQ, S_ISUID | S_ISGID, 0)) != 0 || \ + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), syscall, 1, SCMP_A##modePos(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) != 0 || \ + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), syscall, 1, SCMP_A##modePos(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) != 0) \ + throw SysError("unable to add seccomp rule"); + +static std::vector<struct sock_filter> compileSyscallFilter() +{ + scmp_filter_ctx ctx; + + // Pretend that syscalls we don't yet know about don't exist. + // This is the best option for compatibility: after all, they did in fact not exist not too long ago. + if (!(ctx = seccomp_init(SCMP_ACT_ERRNO(ENOSYS)))) + throw SysError("unable to initialize seccomp mode 2"); + + Finally cleanup([&]() { + seccomp_release(ctx); + }); + + if (nativeSystem == "x86_64-linux" && + seccomp_arch_add(ctx, SCMP_ARCH_X86) != 0) + throw SysError("unable to add 32-bit seccomp architecture"); + + if (nativeSystem == "x86_64-linux" && + seccomp_arch_add(ctx, SCMP_ARCH_X32) != 0) + throw SysError("unable to add X32 seccomp architecture"); + + if (nativeSystem == "aarch64-linux" && + seccomp_arch_add(ctx, SCMP_ARCH_ARM) != 0) + printError("unable to add ARM seccomp architecture; this may result in spurious build failures if running 32-bit ARM processes"); + + if (nativeSystem == "mips64-linux" && + seccomp_arch_add(ctx, SCMP_ARCH_MIPS) != 0) + printError("unable to add mips seccomp architecture"); + + if (nativeSystem == "mips64-linux" && + seccomp_arch_add(ctx, SCMP_ARCH_MIPS64N32) != 0) + printError("unable to add mips64-*abin32 seccomp architecture"); + + if (nativeSystem == "mips64el-linux" && + seccomp_arch_add(ctx, SCMP_ARCH_MIPSEL) != 0) + printError("unable to add mipsel seccomp architecture"); + + if (nativeSystem == "mips64el-linux" && + seccomp_arch_add(ctx, SCMP_ARCH_MIPSEL64N32) != 0) + printError("unable to add mips64el-*abin32 seccomp architecture"); + + // This list is intended for machine consumption. + // Please keep its format, order and BEGIN/END markers. + // + // Currently, it is up to date with libseccomp 2.5.5 and glibc 2.39. + // Run check-syscalls to determine which new syscalls should be added. + // New syscalls must be audited and handled in a way that blocks the following dangerous operations: + // * Creation of non-empty setuid/setgid files + // * Creation of extended attributes (including ACLs) + // + // BEGIN extract-syscalls + allowSyscall(ctx, SCMP_SYS(accept)); + allowSyscall(ctx, SCMP_SYS(accept4)); + allowSyscall(ctx, SCMP_SYS(access)); + allowSyscall(ctx, SCMP_SYS(acct)); + allowSyscall(ctx, SCMP_SYS(add_key)); + allowSyscall(ctx, SCMP_SYS(adjtimex)); + allowSyscall(ctx, SCMP_SYS(afs_syscall)); + allowSyscall(ctx, SCMP_SYS(alarm)); + allowSyscall(ctx, SCMP_SYS(arch_prctl)); + allowSyscall(ctx, SCMP_SYS(arm_fadvise64_64)); + allowSyscall(ctx, SCMP_SYS(arm_sync_file_range)); + allowSyscall(ctx, SCMP_SYS(bdflush)); + allowSyscall(ctx, SCMP_SYS(bind)); + allowSyscall(ctx, SCMP_SYS(bpf)); + allowSyscall(ctx, SCMP_SYS(break)); + allowSyscall(ctx, SCMP_SYS(breakpoint)); + allowSyscall(ctx, SCMP_SYS(brk)); + allowSyscall(ctx, SCMP_SYS(cachectl)); + allowSyscall(ctx, SCMP_SYS(cacheflush)); + allowSyscall(ctx, SCMP_SYS(cachestat)); + allowSyscall(ctx, SCMP_SYS(capget)); + allowSyscall(ctx, SCMP_SYS(capset)); + allowSyscall(ctx, SCMP_SYS(chdir)); + // skip chmod (dangerous) + allowSyscall(ctx, SCMP_SYS(chown)); + allowSyscall(ctx, SCMP_SYS(chown32)); + allowSyscall(ctx, SCMP_SYS(chroot)); + allowSyscall(ctx, SCMP_SYS(clock_adjtime)); + allowSyscall(ctx, SCMP_SYS(clock_adjtime64)); + allowSyscall(ctx, SCMP_SYS(clock_getres)); + allowSyscall(ctx, SCMP_SYS(clock_getres_time64)); + allowSyscall(ctx, SCMP_SYS(clock_gettime)); + allowSyscall(ctx, SCMP_SYS(clock_gettime64)); + allowSyscall(ctx, SCMP_SYS(clock_nanosleep)); + allowSyscall(ctx, SCMP_SYS(clock_nanosleep_time64)); + allowSyscall(ctx, SCMP_SYS(clock_settime)); + allowSyscall(ctx, SCMP_SYS(clock_settime64)); + allowSyscall(ctx, SCMP_SYS(clone)); + allowSyscall(ctx, SCMP_SYS(clone3)); + allowSyscall(ctx, SCMP_SYS(close)); + allowSyscall(ctx, SCMP_SYS(close_range)); + allowSyscall(ctx, SCMP_SYS(connect)); + allowSyscall(ctx, SCMP_SYS(copy_file_range)); + allowSyscall(ctx, SCMP_SYS(creat)); + allowSyscall(ctx, SCMP_SYS(create_module)); + allowSyscall(ctx, SCMP_SYS(delete_module)); + allowSyscall(ctx, SCMP_SYS(dup)); + allowSyscall(ctx, SCMP_SYS(dup2)); + allowSyscall(ctx, SCMP_SYS(dup3)); + allowSyscall(ctx, SCMP_SYS(epoll_create)); + allowSyscall(ctx, SCMP_SYS(epoll_create1)); + allowSyscall(ctx, SCMP_SYS(epoll_ctl)); + allowSyscall(ctx, SCMP_SYS(epoll_ctl_old)); + allowSyscall(ctx, SCMP_SYS(epoll_pwait)); + allowSyscall(ctx, SCMP_SYS(epoll_pwait2)); + allowSyscall(ctx, SCMP_SYS(epoll_wait)); + allowSyscall(ctx, SCMP_SYS(epoll_wait_old)); + allowSyscall(ctx, SCMP_SYS(eventfd)); + allowSyscall(ctx, SCMP_SYS(eventfd2)); + allowSyscall(ctx, SCMP_SYS(execve)); + allowSyscall(ctx, SCMP_SYS(execveat)); + allowSyscall(ctx, SCMP_SYS(exit)); + allowSyscall(ctx, SCMP_SYS(exit_group)); + allowSyscall(ctx, SCMP_SYS(faccessat)); + allowSyscall(ctx, SCMP_SYS(faccessat2)); + allowSyscall(ctx, SCMP_SYS(fadvise64)); + allowSyscall(ctx, SCMP_SYS(fadvise64_64)); + allowSyscall(ctx, SCMP_SYS(fallocate)); + allowSyscall(ctx, SCMP_SYS(fanotify_init)); + allowSyscall(ctx, SCMP_SYS(fanotify_mark)); + allowSyscall(ctx, SCMP_SYS(fchdir)); + // skip fchmod (dangerous) + // skip fchmodat (dangerous) + // skip fchmodat2 (dangerous) + allowSyscall(ctx, SCMP_SYS(fchown)); + allowSyscall(ctx, SCMP_SYS(fchown32)); + allowSyscall(ctx, SCMP_SYS(fchownat)); + allowSyscall(ctx, SCMP_SYS(fcntl)); + allowSyscall(ctx, SCMP_SYS(fcntl64)); + allowSyscall(ctx, SCMP_SYS(fdatasync)); + allowSyscall(ctx, SCMP_SYS(fgetxattr)); + allowSyscall(ctx, SCMP_SYS(finit_module)); + allowSyscall(ctx, SCMP_SYS(flistxattr)); + allowSyscall(ctx, SCMP_SYS(flock)); + allowSyscall(ctx, SCMP_SYS(fork)); + allowSyscall(ctx, SCMP_SYS(fremovexattr)); + allowSyscall(ctx, SCMP_SYS(fsconfig)); + // skip fsetxattr (dangerous) + allowSyscall(ctx, SCMP_SYS(fsmount)); + allowSyscall(ctx, SCMP_SYS(fsopen)); + allowSyscall(ctx, SCMP_SYS(fspick)); + allowSyscall(ctx, SCMP_SYS(fstat)); + allowSyscall(ctx, SCMP_SYS(fstat64)); + allowSyscall(ctx, SCMP_SYS(fstatat64)); + allowSyscall(ctx, SCMP_SYS(fstatfs)); + allowSyscall(ctx, SCMP_SYS(fstatfs64)); + allowSyscall(ctx, SCMP_SYS(fsync)); + allowSyscall(ctx, SCMP_SYS(ftime)); + allowSyscall(ctx, SCMP_SYS(ftruncate)); + allowSyscall(ctx, SCMP_SYS(ftruncate64)); + allowSyscall(ctx, SCMP_SYS(futex)); + allowSyscall(ctx, SCMP_SYS(futex_requeue)); + allowSyscall(ctx, SCMP_SYS(futex_time64)); + allowSyscall(ctx, SCMP_SYS(futex_wait)); + allowSyscall(ctx, SCMP_SYS(futex_waitv)); + allowSyscall(ctx, SCMP_SYS(futex_wake)); + allowSyscall(ctx, SCMP_SYS(futimesat)); + allowSyscall(ctx, SCMP_SYS(getcpu)); + allowSyscall(ctx, SCMP_SYS(getcwd)); + allowSyscall(ctx, SCMP_SYS(getdents)); + allowSyscall(ctx, SCMP_SYS(getdents64)); + allowSyscall(ctx, SCMP_SYS(getegid)); + allowSyscall(ctx, SCMP_SYS(getegid32)); + allowSyscall(ctx, SCMP_SYS(geteuid)); + allowSyscall(ctx, SCMP_SYS(geteuid32)); + allowSyscall(ctx, SCMP_SYS(getgid)); + allowSyscall(ctx, SCMP_SYS(getgid32)); + allowSyscall(ctx, SCMP_SYS(getgroups)); + allowSyscall(ctx, SCMP_SYS(getgroups32)); + allowSyscall(ctx, SCMP_SYS(getitimer)); + allowSyscall(ctx, SCMP_SYS(get_kernel_syms)); + allowSyscall(ctx, SCMP_SYS(get_mempolicy)); + allowSyscall(ctx, SCMP_SYS(getpeername)); + allowSyscall(ctx, SCMP_SYS(getpgid)); + allowSyscall(ctx, SCMP_SYS(getpgrp)); + allowSyscall(ctx, SCMP_SYS(getpid)); + allowSyscall(ctx, SCMP_SYS(getpmsg)); + allowSyscall(ctx, SCMP_SYS(getppid)); + allowSyscall(ctx, SCMP_SYS(getpriority)); + allowSyscall(ctx, SCMP_SYS(getrandom)); + allowSyscall(ctx, SCMP_SYS(getresgid)); + allowSyscall(ctx, SCMP_SYS(getresgid32)); + allowSyscall(ctx, SCMP_SYS(getresuid)); + allowSyscall(ctx, SCMP_SYS(getresuid32)); + allowSyscall(ctx, SCMP_SYS(getrlimit)); + allowSyscall(ctx, SCMP_SYS(get_robust_list)); + allowSyscall(ctx, SCMP_SYS(getrusage)); + allowSyscall(ctx, SCMP_SYS(getsid)); + allowSyscall(ctx, SCMP_SYS(getsockname)); + allowSyscall(ctx, SCMP_SYS(getsockopt)); + allowSyscall(ctx, SCMP_SYS(get_thread_area)); + allowSyscall(ctx, SCMP_SYS(gettid)); + allowSyscall(ctx, SCMP_SYS(gettimeofday)); + allowSyscall(ctx, SCMP_SYS(get_tls)); + allowSyscall(ctx, SCMP_SYS(getuid)); + allowSyscall(ctx, SCMP_SYS(getuid32)); + allowSyscall(ctx, SCMP_SYS(getxattr)); + allowSyscall(ctx, SCMP_SYS(gtty)); + allowSyscall(ctx, SCMP_SYS(idle)); + allowSyscall(ctx, SCMP_SYS(init_module)); + allowSyscall(ctx, SCMP_SYS(inotify_add_watch)); + allowSyscall(ctx, SCMP_SYS(inotify_init)); + allowSyscall(ctx, SCMP_SYS(inotify_init1)); + allowSyscall(ctx, SCMP_SYS(inotify_rm_watch)); + allowSyscall(ctx, SCMP_SYS(io_cancel)); + allowSyscall(ctx, SCMP_SYS(ioctl)); + allowSyscall(ctx, SCMP_SYS(io_destroy)); + allowSyscall(ctx, SCMP_SYS(io_getevents)); + allowSyscall(ctx, SCMP_SYS(ioperm)); + allowSyscall(ctx, SCMP_SYS(io_pgetevents)); + allowSyscall(ctx, SCMP_SYS(io_pgetevents_time64)); + allowSyscall(ctx, SCMP_SYS(iopl)); + allowSyscall(ctx, SCMP_SYS(ioprio_get)); + allowSyscall(ctx, SCMP_SYS(ioprio_set)); + allowSyscall(ctx, SCMP_SYS(io_setup)); + allowSyscall(ctx, SCMP_SYS(io_submit)); + // skip io_uring_enter (may become dangerous) + // skip io_uring_register (may become dangerous) + // skip io_uring_setup (may become dangerous) + allowSyscall(ctx, SCMP_SYS(ipc)); + allowSyscall(ctx, SCMP_SYS(kcmp)); + allowSyscall(ctx, SCMP_SYS(kexec_file_load)); + allowSyscall(ctx, SCMP_SYS(kexec_load)); + allowSyscall(ctx, SCMP_SYS(keyctl)); + allowSyscall(ctx, SCMP_SYS(kill)); + allowSyscall(ctx, SCMP_SYS(landlock_add_rule)); + allowSyscall(ctx, SCMP_SYS(landlock_create_ruleset)); + allowSyscall(ctx, SCMP_SYS(landlock_restrict_self)); + allowSyscall(ctx, SCMP_SYS(lchown)); + allowSyscall(ctx, SCMP_SYS(lchown32)); + allowSyscall(ctx, SCMP_SYS(lgetxattr)); + allowSyscall(ctx, SCMP_SYS(link)); + allowSyscall(ctx, SCMP_SYS(linkat)); + allowSyscall(ctx, SCMP_SYS(listen)); + allowSyscall(ctx, SCMP_SYS(listxattr)); + allowSyscall(ctx, SCMP_SYS(llistxattr)); + allowSyscall(ctx, SCMP_SYS(_llseek)); + allowSyscall(ctx, SCMP_SYS(lock)); + allowSyscall(ctx, SCMP_SYS(lookup_dcookie)); + allowSyscall(ctx, SCMP_SYS(lremovexattr)); + allowSyscall(ctx, SCMP_SYS(lseek)); + // skip lsetxattr (dangerous) + allowSyscall(ctx, SCMP_SYS(lstat)); + allowSyscall(ctx, SCMP_SYS(lstat64)); + allowSyscall(ctx, SCMP_SYS(madvise)); + allowSyscall(ctx, SCMP_SYS(map_shadow_stack)); + allowSyscall(ctx, SCMP_SYS(mbind)); + allowSyscall(ctx, SCMP_SYS(membarrier)); + allowSyscall(ctx, SCMP_SYS(memfd_create)); + allowSyscall(ctx, SCMP_SYS(memfd_secret)); + allowSyscall(ctx, SCMP_SYS(migrate_pages)); + allowSyscall(ctx, SCMP_SYS(mincore)); + allowSyscall(ctx, SCMP_SYS(mkdir)); + allowSyscall(ctx, SCMP_SYS(mkdirat)); + allowSyscall(ctx, SCMP_SYS(mknod)); + allowSyscall(ctx, SCMP_SYS(mknodat)); + allowSyscall(ctx, SCMP_SYS(mlock)); + allowSyscall(ctx, SCMP_SYS(mlock2)); + allowSyscall(ctx, SCMP_SYS(mlockall)); + allowSyscall(ctx, SCMP_SYS(mmap)); + allowSyscall(ctx, SCMP_SYS(mmap2)); + allowSyscall(ctx, SCMP_SYS(modify_ldt)); + allowSyscall(ctx, SCMP_SYS(mount)); + allowSyscall(ctx, SCMP_SYS(mount_setattr)); + allowSyscall(ctx, SCMP_SYS(move_mount)); + allowSyscall(ctx, SCMP_SYS(move_pages)); + allowSyscall(ctx, SCMP_SYS(mprotect)); + allowSyscall(ctx, SCMP_SYS(mpx)); + allowSyscall(ctx, SCMP_SYS(mq_getsetattr)); + allowSyscall(ctx, SCMP_SYS(mq_notify)); + allowSyscall(ctx, SCMP_SYS(mq_open)); + allowSyscall(ctx, SCMP_SYS(mq_timedreceive)); + allowSyscall(ctx, SCMP_SYS(mq_timedreceive_time64)); + allowSyscall(ctx, SCMP_SYS(mq_timedsend)); + allowSyscall(ctx, SCMP_SYS(mq_timedsend_time64)); + allowSyscall(ctx, SCMP_SYS(mq_unlink)); + allowSyscall(ctx, SCMP_SYS(mremap)); + allowSyscall(ctx, SCMP_SYS(msgctl)); + allowSyscall(ctx, SCMP_SYS(msgget)); + allowSyscall(ctx, SCMP_SYS(msgrcv)); + allowSyscall(ctx, SCMP_SYS(msgsnd)); + allowSyscall(ctx, SCMP_SYS(msync)); + allowSyscall(ctx, SCMP_SYS(multiplexer)); + allowSyscall(ctx, SCMP_SYS(munlock)); + allowSyscall(ctx, SCMP_SYS(munlockall)); + allowSyscall(ctx, SCMP_SYS(munmap)); + allowSyscall(ctx, SCMP_SYS(name_to_handle_at)); + allowSyscall(ctx, SCMP_SYS(nanosleep)); + allowSyscall(ctx, SCMP_SYS(newfstatat)); + allowSyscall(ctx, SCMP_SYS(_newselect)); + allowSyscall(ctx, SCMP_SYS(nfsservctl)); + allowSyscall(ctx, SCMP_SYS(nice)); + allowSyscall(ctx, SCMP_SYS(oldfstat)); + allowSyscall(ctx, SCMP_SYS(oldlstat)); + allowSyscall(ctx, SCMP_SYS(oldolduname)); + allowSyscall(ctx, SCMP_SYS(oldstat)); + allowSyscall(ctx, SCMP_SYS(olduname)); + allowSyscall(ctx, SCMP_SYS(open)); + allowSyscall(ctx, SCMP_SYS(openat)); + allowSyscall(ctx, SCMP_SYS(openat2)); + allowSyscall(ctx, SCMP_SYS(open_by_handle_at)); + allowSyscall(ctx, SCMP_SYS(open_tree)); + allowSyscall(ctx, SCMP_SYS(pause)); + allowSyscall(ctx, SCMP_SYS(pciconfig_iobase)); + allowSyscall(ctx, SCMP_SYS(pciconfig_read)); + allowSyscall(ctx, SCMP_SYS(pciconfig_write)); + allowSyscall(ctx, SCMP_SYS(perf_event_open)); + allowSyscall(ctx, SCMP_SYS(personality)); + allowSyscall(ctx, SCMP_SYS(pidfd_getfd)); + allowSyscall(ctx, SCMP_SYS(pidfd_open)); + allowSyscall(ctx, SCMP_SYS(pidfd_send_signal)); + allowSyscall(ctx, SCMP_SYS(pipe)); + allowSyscall(ctx, SCMP_SYS(pipe2)); + allowSyscall(ctx, SCMP_SYS(pivot_root)); + allowSyscall(ctx, SCMP_SYS(pkey_alloc)); + allowSyscall(ctx, SCMP_SYS(pkey_free)); + allowSyscall(ctx, SCMP_SYS(pkey_mprotect)); + allowSyscall(ctx, SCMP_SYS(poll)); + allowSyscall(ctx, SCMP_SYS(ppoll)); + allowSyscall(ctx, SCMP_SYS(ppoll_time64)); + allowSyscall(ctx, SCMP_SYS(prctl)); + allowSyscall(ctx, SCMP_SYS(pread64)); + allowSyscall(ctx, SCMP_SYS(preadv)); + allowSyscall(ctx, SCMP_SYS(preadv2)); + allowSyscall(ctx, SCMP_SYS(prlimit64)); + allowSyscall(ctx, SCMP_SYS(process_madvise)); + allowSyscall(ctx, SCMP_SYS(process_mrelease)); + allowSyscall(ctx, SCMP_SYS(process_vm_readv)); + allowSyscall(ctx, SCMP_SYS(process_vm_writev)); + allowSyscall(ctx, SCMP_SYS(prof)); + allowSyscall(ctx, SCMP_SYS(profil)); + allowSyscall(ctx, SCMP_SYS(pselect6)); + allowSyscall(ctx, SCMP_SYS(pselect6_time64)); + allowSyscall(ctx, SCMP_SYS(ptrace)); + allowSyscall(ctx, SCMP_SYS(putpmsg)); + allowSyscall(ctx, SCMP_SYS(pwrite64)); + allowSyscall(ctx, SCMP_SYS(pwritev)); + allowSyscall(ctx, SCMP_SYS(pwritev2)); + allowSyscall(ctx, SCMP_SYS(query_module)); + allowSyscall(ctx, SCMP_SYS(quotactl)); + allowSyscall(ctx, SCMP_SYS(quotactl_fd)); + allowSyscall(ctx, SCMP_SYS(read)); + allowSyscall(ctx, SCMP_SYS(readahead)); + allowSyscall(ctx, SCMP_SYS(readdir)); + allowSyscall(ctx, SCMP_SYS(readlink)); + allowSyscall(ctx, SCMP_SYS(readlinkat)); + allowSyscall(ctx, SCMP_SYS(readv)); + allowSyscall(ctx, SCMP_SYS(reboot)); + allowSyscall(ctx, SCMP_SYS(recv)); + allowSyscall(ctx, SCMP_SYS(recvfrom)); + allowSyscall(ctx, SCMP_SYS(recvmmsg)); + allowSyscall(ctx, SCMP_SYS(recvmmsg_time64)); + allowSyscall(ctx, SCMP_SYS(recvmsg)); + allowSyscall(ctx, SCMP_SYS(remap_file_pages)); + allowSyscall(ctx, SCMP_SYS(removexattr)); + allowSyscall(ctx, SCMP_SYS(rename)); + allowSyscall(ctx, SCMP_SYS(renameat)); + allowSyscall(ctx, SCMP_SYS(renameat2)); + allowSyscall(ctx, SCMP_SYS(request_key)); + allowSyscall(ctx, SCMP_SYS(restart_syscall)); + allowSyscall(ctx, SCMP_SYS(riscv_flush_icache)); + allowSyscall(ctx, SCMP_SYS(rmdir)); + allowSyscall(ctx, SCMP_SYS(rseq)); + allowSyscall(ctx, SCMP_SYS(rtas)); + allowSyscall(ctx, SCMP_SYS(rt_sigaction)); + allowSyscall(ctx, SCMP_SYS(rt_sigpending)); + allowSyscall(ctx, SCMP_SYS(rt_sigprocmask)); + allowSyscall(ctx, SCMP_SYS(rt_sigqueueinfo)); + allowSyscall(ctx, SCMP_SYS(rt_sigreturn)); + allowSyscall(ctx, SCMP_SYS(rt_sigsuspend)); + allowSyscall(ctx, SCMP_SYS(rt_sigtimedwait)); + allowSyscall(ctx, SCMP_SYS(rt_sigtimedwait_time64)); + allowSyscall(ctx, SCMP_SYS(rt_tgsigqueueinfo)); + allowSyscall(ctx, SCMP_SYS(s390_guarded_storage)); + allowSyscall(ctx, SCMP_SYS(s390_pci_mmio_read)); + allowSyscall(ctx, SCMP_SYS(s390_pci_mmio_write)); + allowSyscall(ctx, SCMP_SYS(s390_runtime_instr)); + allowSyscall(ctx, SCMP_SYS(s390_sthyi)); + allowSyscall(ctx, SCMP_SYS(sched_getaffinity)); + allowSyscall(ctx, SCMP_SYS(sched_getattr)); + allowSyscall(ctx, SCMP_SYS(sched_getparam)); + allowSyscall(ctx, SCMP_SYS(sched_get_priority_max)); + allowSyscall(ctx, SCMP_SYS(sched_get_priority_min)); + allowSyscall(ctx, SCMP_SYS(sched_getscheduler)); + allowSyscall(ctx, SCMP_SYS(sched_rr_get_interval)); + allowSyscall(ctx, SCMP_SYS(sched_rr_get_interval_time64)); + allowSyscall(ctx, SCMP_SYS(sched_setaffinity)); + allowSyscall(ctx, SCMP_SYS(sched_setattr)); + allowSyscall(ctx, SCMP_SYS(sched_setparam)); + allowSyscall(ctx, SCMP_SYS(sched_setscheduler)); + allowSyscall(ctx, SCMP_SYS(sched_yield)); + allowSyscall(ctx, SCMP_SYS(seccomp)); + allowSyscall(ctx, SCMP_SYS(security)); + allowSyscall(ctx, SCMP_SYS(select)); + allowSyscall(ctx, SCMP_SYS(semctl)); + allowSyscall(ctx, SCMP_SYS(semget)); + allowSyscall(ctx, SCMP_SYS(semop)); + allowSyscall(ctx, SCMP_SYS(semtimedop)); + allowSyscall(ctx, SCMP_SYS(semtimedop_time64)); + allowSyscall(ctx, SCMP_SYS(send)); + allowSyscall(ctx, SCMP_SYS(sendfile)); + allowSyscall(ctx, SCMP_SYS(sendfile64)); + allowSyscall(ctx, SCMP_SYS(sendmmsg)); + allowSyscall(ctx, SCMP_SYS(sendmsg)); + allowSyscall(ctx, SCMP_SYS(sendto)); + allowSyscall(ctx, SCMP_SYS(setdomainname)); + allowSyscall(ctx, SCMP_SYS(setfsgid)); + allowSyscall(ctx, SCMP_SYS(setfsgid32)); + allowSyscall(ctx, SCMP_SYS(setfsuid)); + allowSyscall(ctx, SCMP_SYS(setfsuid32)); + allowSyscall(ctx, SCMP_SYS(setgid)); + allowSyscall(ctx, SCMP_SYS(setgid32)); + allowSyscall(ctx, SCMP_SYS(setgroups)); + allowSyscall(ctx, SCMP_SYS(setgroups32)); + allowSyscall(ctx, SCMP_SYS(sethostname)); + allowSyscall(ctx, SCMP_SYS(setitimer)); + allowSyscall(ctx, SCMP_SYS(set_mempolicy)); + allowSyscall(ctx, SCMP_SYS(set_mempolicy_home_node)); + allowSyscall(ctx, SCMP_SYS(setns)); + allowSyscall(ctx, SCMP_SYS(setpgid)); + allowSyscall(ctx, SCMP_SYS(setpriority)); + allowSyscall(ctx, SCMP_SYS(setregid)); + allowSyscall(ctx, SCMP_SYS(setregid32)); + allowSyscall(ctx, SCMP_SYS(setresgid)); + allowSyscall(ctx, SCMP_SYS(setresgid32)); + allowSyscall(ctx, SCMP_SYS(setresuid)); + allowSyscall(ctx, SCMP_SYS(setresuid32)); + allowSyscall(ctx, SCMP_SYS(setreuid)); + allowSyscall(ctx, SCMP_SYS(setreuid32)); + allowSyscall(ctx, SCMP_SYS(setrlimit)); + allowSyscall(ctx, SCMP_SYS(set_robust_list)); + allowSyscall(ctx, SCMP_SYS(setsid)); + allowSyscall(ctx, SCMP_SYS(setsockopt)); + allowSyscall(ctx, SCMP_SYS(set_thread_area)); + allowSyscall(ctx, SCMP_SYS(set_tid_address)); + allowSyscall(ctx, SCMP_SYS(settimeofday)); + allowSyscall(ctx, SCMP_SYS(set_tls)); + allowSyscall(ctx, SCMP_SYS(setuid)); + allowSyscall(ctx, SCMP_SYS(setuid32)); + // skip setxattr (dangerous) + allowSyscall(ctx, SCMP_SYS(sgetmask)); + allowSyscall(ctx, SCMP_SYS(shmat)); + allowSyscall(ctx, SCMP_SYS(shmctl)); + allowSyscall(ctx, SCMP_SYS(shmdt)); + allowSyscall(ctx, SCMP_SYS(shmget)); + allowSyscall(ctx, SCMP_SYS(shutdown)); + allowSyscall(ctx, SCMP_SYS(sigaction)); + allowSyscall(ctx, SCMP_SYS(sigaltstack)); + allowSyscall(ctx, SCMP_SYS(signal)); + allowSyscall(ctx, SCMP_SYS(signalfd)); + allowSyscall(ctx, SCMP_SYS(signalfd4)); + allowSyscall(ctx, SCMP_SYS(sigpending)); + allowSyscall(ctx, SCMP_SYS(sigprocmask)); + allowSyscall(ctx, SCMP_SYS(sigreturn)); + allowSyscall(ctx, SCMP_SYS(sigsuspend)); + allowSyscall(ctx, SCMP_SYS(socket)); + allowSyscall(ctx, SCMP_SYS(socketcall)); + allowSyscall(ctx, SCMP_SYS(socketpair)); + allowSyscall(ctx, SCMP_SYS(splice)); + allowSyscall(ctx, SCMP_SYS(spu_create)); + allowSyscall(ctx, SCMP_SYS(spu_run)); + allowSyscall(ctx, SCMP_SYS(ssetmask)); + allowSyscall(ctx, SCMP_SYS(stat)); + allowSyscall(ctx, SCMP_SYS(stat64)); + allowSyscall(ctx, SCMP_SYS(statfs)); + allowSyscall(ctx, SCMP_SYS(statfs64)); + allowSyscall(ctx, SCMP_SYS(statx)); + allowSyscall(ctx, SCMP_SYS(stime)); + allowSyscall(ctx, SCMP_SYS(stty)); + allowSyscall(ctx, SCMP_SYS(subpage_prot)); + allowSyscall(ctx, SCMP_SYS(swapcontext)); + allowSyscall(ctx, SCMP_SYS(swapoff)); + allowSyscall(ctx, SCMP_SYS(swapon)); + allowSyscall(ctx, SCMP_SYS(switch_endian)); + allowSyscall(ctx, SCMP_SYS(symlink)); + allowSyscall(ctx, SCMP_SYS(symlinkat)); + allowSyscall(ctx, SCMP_SYS(sync)); + allowSyscall(ctx, SCMP_SYS(sync_file_range)); + allowSyscall(ctx, SCMP_SYS(sync_file_range2)); + allowSyscall(ctx, SCMP_SYS(syncfs)); + allowSyscall(ctx, SCMP_SYS(syscall)); + allowSyscall(ctx, SCMP_SYS(_sysctl)); + allowSyscall(ctx, SCMP_SYS(sys_debug_setcontext)); + allowSyscall(ctx, SCMP_SYS(sysfs)); + allowSyscall(ctx, SCMP_SYS(sysinfo)); + allowSyscall(ctx, SCMP_SYS(syslog)); + allowSyscall(ctx, SCMP_SYS(sysmips)); + allowSyscall(ctx, SCMP_SYS(tee)); + allowSyscall(ctx, SCMP_SYS(tgkill)); + allowSyscall(ctx, SCMP_SYS(time)); + allowSyscall(ctx, SCMP_SYS(timer_create)); + allowSyscall(ctx, SCMP_SYS(timer_delete)); + allowSyscall(ctx, SCMP_SYS(timerfd)); + allowSyscall(ctx, SCMP_SYS(timerfd_create)); + allowSyscall(ctx, SCMP_SYS(timerfd_gettime)); + allowSyscall(ctx, SCMP_SYS(timerfd_gettime64)); + allowSyscall(ctx, SCMP_SYS(timerfd_settime)); + allowSyscall(ctx, SCMP_SYS(timerfd_settime64)); + allowSyscall(ctx, SCMP_SYS(timer_getoverrun)); + allowSyscall(ctx, SCMP_SYS(timer_gettime)); + allowSyscall(ctx, SCMP_SYS(timer_gettime64)); + allowSyscall(ctx, SCMP_SYS(timer_settime)); + allowSyscall(ctx, SCMP_SYS(timer_settime64)); + allowSyscall(ctx, SCMP_SYS(times)); + allowSyscall(ctx, SCMP_SYS(tkill)); + allowSyscall(ctx, SCMP_SYS(truncate)); + allowSyscall(ctx, SCMP_SYS(truncate64)); + allowSyscall(ctx, SCMP_SYS(tuxcall)); + allowSyscall(ctx, SCMP_SYS(ugetrlimit)); + allowSyscall(ctx, SCMP_SYS(ulimit)); + allowSyscall(ctx, SCMP_SYS(umask)); + allowSyscall(ctx, SCMP_SYS(umount)); + allowSyscall(ctx, SCMP_SYS(umount2)); + allowSyscall(ctx, SCMP_SYS(uname)); + allowSyscall(ctx, SCMP_SYS(unlink)); + allowSyscall(ctx, SCMP_SYS(unlinkat)); + allowSyscall(ctx, SCMP_SYS(unshare)); + allowSyscall(ctx, SCMP_SYS(uselib)); + allowSyscall(ctx, SCMP_SYS(userfaultfd)); + allowSyscall(ctx, SCMP_SYS(usr26)); + allowSyscall(ctx, SCMP_SYS(usr32)); + allowSyscall(ctx, SCMP_SYS(ustat)); + allowSyscall(ctx, SCMP_SYS(utime)); + allowSyscall(ctx, SCMP_SYS(utimensat)); + allowSyscall(ctx, SCMP_SYS(utimensat_time64)); + allowSyscall(ctx, SCMP_SYS(utimes)); + allowSyscall(ctx, SCMP_SYS(vfork)); + allowSyscall(ctx, SCMP_SYS(vhangup)); + allowSyscall(ctx, SCMP_SYS(vm86)); + allowSyscall(ctx, SCMP_SYS(vm86old)); + allowSyscall(ctx, SCMP_SYS(vmsplice)); + allowSyscall(ctx, SCMP_SYS(vserver)); + allowSyscall(ctx, SCMP_SYS(wait4)); + allowSyscall(ctx, SCMP_SYS(waitid)); + allowSyscall(ctx, SCMP_SYS(waitpid)); + allowSyscall(ctx, SCMP_SYS(write)); + allowSyscall(ctx, SCMP_SYS(writev)); + // END extract-syscalls + + // chmod family: prevent adding setuid/setgid bits to existing files. + // The Nix store does not support setuid/setgid, and even their temporary creation can weaken the security of the sandbox. + ALLOW_CHMOD_IF_SAFE(ctx, SCMP_SYS(chmod), 1); + ALLOW_CHMOD_IF_SAFE(ctx, SCMP_SYS(fchmod), 1); + ALLOW_CHMOD_IF_SAFE(ctx, SCMP_SYS(fchmodat), 2); + ALLOW_CHMOD_IF_SAFE(ctx, SCMP_SYS(fchmodat2), 2); + + // setxattr family: prevent creation of extended attributes or ACLs. + // Not all filesystems support them, and they're incompatible with the NAR format. + if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 || + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), 0) != 0 || + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0) + throw SysError("unable to add seccomp rule"); + + Pipe filterPipe; + filterPipe.create(); + auto filterBytes_ = std::async([&]() { + return drainFD(filterPipe.readSide.get()); + }); + if (seccomp_export_bpf(ctx, filterPipe.writeSide.get()) != 0) + throw SysError("unable to compile seccomp BPF program"); + filterPipe.writeSide.close(); + auto filterBytes = filterBytes_.get(); + + assert(filterBytes.size() % sizeof(struct sock_filter) == 0); + std::vector<struct sock_filter> filter(filterBytes.size() / sizeof(struct sock_filter)); + std::memcpy(filter.data(), filterBytes.data(), filterBytes.size()); + return filter; +} + +static const std::vector<struct sock_filter> &getSyscallFilter() +{ + static auto filter = compileSyscallFilter(); + return filter; +} + +#endif + +void LinuxLocalDerivationGoal::setupSyscallFilter() +{ + // Set the NO_NEW_PRIVS prctl flag. + // This both makes loading seccomp filters work for unprivileged users, + // and is an additional security measure in its own right. + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) + throw SysError("PR_SET_NO_NEW_PRIVS failed"); +#if HAVE_SECCOMP + const auto &seccompBPF = getSyscallFilter(); + assert(seccompBPF.size() <= std::numeric_limits<unsigned short>::max()); + struct sock_fprog fprog = { + .len = static_cast<unsigned short>(seccompBPF.size()), + // the kernel does not actually write to the filter + .filter = const_cast<struct sock_filter *>(seccompBPF.data()), + }; + if (syscall(SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &fprog) != 0) + throw SysError("unable to load seccomp BPF program"); +#endif +} + void LinuxLocalDerivationGoal::prepareSandbox() { /* Create a temporary directory in which we set up the chroot @@ -208,6 +834,13 @@ void LinuxLocalDerivationGoal::prepareSandbox() Pid LinuxLocalDerivationGoal::startChild(std::function<void()> openSlave) { +#if HAVE_SECCOMP + // Our seccomp filter program is surprisingly expensive to compile (~10ms). + // For this reason, we precompile it once and then cache it. + // This has to be done in the parent so that all builds get to use the same cache. + getSyscallFilter(); +#endif + // If we're not sandboxing no need to faff about, use the fallback if (!useChroot) { return LocalDerivationGoal::startChild(openSlave); diff --git a/src/libstore/platform/linux.hh b/src/libstore/platform/linux.hh index 2173205bc..c8842e09c 100644 --- a/src/libstore/platform/linux.hh +++ b/src/libstore/platform/linux.hh @@ -59,6 +59,11 @@ private: */ void killSandbox(bool getStatus) override; + /** + * Set up system call filtering using seccomp, unless disabled at build time. + * This also sets the NO_NEW_PRIVS flag. + */ + void setupSyscallFilter() override; bool supportsUidRange() override { diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 5fb33ef56..d4da18f14 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -192,7 +192,7 @@ static Generator<Entry> parseObject(Source & source, const Path & path) #define EXPECT(raw, kind) \ do { \ const auto s = readString(source); \ - if (s != raw) { \ + if (s != (raw)) { \ throw badArchive("expected " kind " tag"); \ } \ co_yield MetadataString{s}; \ diff --git a/src/libutil/file-descriptor.cc b/src/libutil/file-descriptor.cc index ab69b5754..037cd5297 100644 --- a/src/libutil/file-descriptor.cc +++ b/src/libutil/file-descriptor.cc @@ -131,7 +131,7 @@ AutoCloseFD::AutoCloseFD(AutoCloseFD && that) : fd{that.fd} } -AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that) +AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that) noexcept(false) { close(); fd = that.fd; diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index a762dc940..0ce82f273 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -229,7 +229,7 @@ Hash::Hash(std::string_view rest, HashType type, bool isSRI) for (unsigned int n = 0; n < rest.size(); ++n) { char c = rest[rest.size() - n - 1]; - unsigned char digit; + size_t digit; for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ if (base32Chars[digit] == c) break; if (digit >= 32) diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 53460f729..cbeb7aa36 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -37,7 +37,7 @@ void Logger::warn(const std::string & msg) void Logger::writeToStdout(std::string_view s) { - writeFull(STDOUT_FILENO, s); + writeFull(STDOUT_FILENO, filterANSIEscapes(s, !shouldANSI(), std::numeric_limits<unsigned int>::max(), false)); writeFull(STDOUT_FILENO, "\n"); } diff --git a/src/libutil/shlex.cc b/src/libutil/shlex.cc index 21fa0502a..b923fef65 100644 --- a/src/libutil/shlex.cc +++ b/src/libutil/shlex.cc @@ -62,6 +62,8 @@ std::vector<std::string> shell_split(const std::string & input) begin = ++iterator; } break; + // no other relevant cases; silence exhaustiveness compiler warning + default: break; } } diff --git a/src/libutil/terminal.cc b/src/libutil/terminal.cc index b58331d04..2ba1cb81b 100644 --- a/src/libutil/terminal.cc +++ b/src/libutil/terminal.cc @@ -9,12 +9,25 @@ namespace nix { bool shouldANSI() { - return isatty(STDERR_FILENO) - && getEnv("TERM").value_or("dumb") != "dumb" - && !(getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value()); + // Implements the behaviour described by https://bixense.com/clicolors/ + // As well as https://force-color.org/ for compatibility, since it fits in the same shape. + // NO_COLOR CLICOLOR CLICOLOR_FORCE Colours? + // set x x No + // unset x set Yes + // unset x unset If attached to a terminal + // [we choose the "modern" approach of colour-by-default] + auto compute = []() -> bool { + bool mustNotColour = getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value(); + bool shouldForce = getEnv("CLICOLOR_FORCE").has_value() || getEnv("FORCE_COLOR").has_value(); + bool isTerminal = isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb"; + return !mustNotColour && (shouldForce || isTerminal); + }; + static bool cached = compute(); + return cached; } -std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width) +// 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 t, e; size_t w = 0; @@ -43,7 +56,7 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w t += e; } - else if (*i == '\t') { + else if (*i == '\t' && eatTabs) { i++; t += ' '; w++; while (w < (size_t) width && w % 8) { t += ' '; w++; diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 43df5bd70..2c422ecff 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -9,6 +9,15 @@ namespace nix { /** * Determine whether ANSI escape sequences are appropriate for the * present output. + * + * This follows the rules described on https://bixense.com/clicolors/ + * with CLICOLOR defaulted to enabled (and thus ignored). + * + * That is to say, the following procedure is followed in order: + * - NO_COLOR or NOCOLOR set -> always disable colour + * - CLICOLOR_FORCE or FORCE_COLOR set -> enable colour + * - The output is a tty; TERM != "dumb" -> enable colour + * - Otherwise -> disable colour */ bool shouldANSI(); @@ -21,7 +30,8 @@ bool shouldANSI(); */ std::string filterANSIEscapes(std::string_view s, bool filterAll = false, - unsigned int width = std::numeric_limits<unsigned int>::max()); + unsigned int width = std::numeric_limits<unsigned int>::max(), + bool eatTabs = true); /** * Recalculate the window size, updating a global variable. Used in the diff --git a/src/libutil/url.cc b/src/libutil/url.cc index 87146ca56..2de50dd4d 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -63,7 +63,7 @@ std::string percentDecode(std::string_view in) if (i + 2 >= in.size()) throw BadURL("invalid URI parameter '%s'", in); try { - decoded += std::stoul(std::string(in, i + 1, 2), 0, 16); + decoded += char8_t(std::stoul(std::string(in, i + 1, 2), 0, 16)); i += 3; } catch (...) { throw BadURL("invalid URI parameter '%s'", in); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 9d18b81b8..672930342 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -209,6 +209,11 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON { auto lockedFlake = lockFlake(); auto & flake = lockedFlake.flake; + auto formatTime = [](time_t time) -> std::string { + std::ostringstream os{}; + os << std::put_time(std::localtime(&time), "%F %T"); + return os.str(); + }; if (json) { nlohmann::json j; @@ -260,7 +265,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON if (auto lastModified = flake.lockedRef.input.getLastModified()) logger->cout( ANSI_BOLD "Last modified:" ANSI_NORMAL " %s", - std::put_time(std::localtime(&*lastModified), "%F %T")); + formatTime(*lastModified)); if (!lockedFlake.lockFile.root->inputs.empty()) logger->cout(ANSI_BOLD "Inputs:" ANSI_NORMAL); @@ -275,16 +280,25 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON bool last = i + 1 == node.inputs.size(); if (auto lockedNode = std::get_if<0>(&input.second)) { - logger->cout("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s", - prefix + (last ? treeLast : treeConn), input.first, + // ├───agenix: github:ryantm/agenix/8d37c5bdeade12b6479c85acd133063ab53187a0 + logger->cout("%s%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s", + prefix, last ? treeLast : treeConn, input.first, (*lockedNode)->lockedRef); + // ├───lix: https://git.lix.systems/api/v1/repos/lix-project <....> + // │ Last modified: 2024-07-31 21:01:34 + if (auto lastModified = (*lockedNode)->lockedRef.input.getLastModified()) { + logger->cout("%s%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s", + prefix, last ? treeNull : treeLine, "Last modified", formatTime(*lastModified)); + } + bool firstVisit = visited.insert(*lockedNode).second; if (firstVisit) recurse(**lockedNode, prefix + (last ? treeNull : treeLine)); } else if (auto follows = std::get_if<1>(&input.second)) { - logger->cout("%s" ANSI_BOLD "%s" ANSI_NORMAL " follows input '%s'", - prefix + (last ? treeLast : treeConn), input.first, + // │ ├───darwin follows input 'flake-utils' + logger->cout("%s%s" ANSI_BOLD "%s" ANSI_NORMAL " follows input '%s'", + prefix, last ? treeLast : treeConn, input.first, printInputPath(*follows)); } } diff --git a/src/nix/meson.build b/src/nix/meson.build index 97387e402..80223a390 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -1,8 +1,8 @@ -generate_manpage_gen = gen_header.process(meson.project_source_root() / 'doc/manual/generate-manpage.nix') - -utils_gen = gen_header.process(meson.project_source_root() / 'doc/manual/utils.nix') - -get_env_gen = gen_header.process('get-env.sh') +nix_generated_headers = [ + gen_header.process(meson.project_source_root() / 'doc/manual/generate-manpage.nix'), + gen_header.process(meson.project_source_root() / 'doc/manual/utils.nix'), + gen_header.process('get-env.sh'), +] # src/nix/profile.cc includes src/nix/profile.md, which includes "doc/files/profiles.md.gen.hh". # Unfortunately, https://github.com/mesonbuild/meson/issues/2320. @@ -18,7 +18,7 @@ run_command( meson.current_build_dir() / 'doc/files/profiles.md', check : true, ) -profiles_md_gen = gen_header.process( +nix_generated_headers += gen_header.process( meson.current_build_dir() / 'doc/files/profiles.md', preserve_path_from : meson.current_build_dir(), ) @@ -74,10 +74,7 @@ nix_sources = files( nix = executable( 'nix', nix_sources, - generate_manpage_gen, - utils_gen, - get_env_gen, - profiles_md_gen, + nix_generated_headers, nix2_commands_sources, dependencies : [ libasanoptions, diff --git a/clang-tidy/.clang-format b/subprojects/lix-clang-tidy/.clang-format index cd8995543..cd8995543 100644 --- a/clang-tidy/.clang-format +++ b/subprojects/lix-clang-tidy/.clang-format diff --git a/clang-tidy/.editorconfig b/subprojects/lix-clang-tidy/.editorconfig index 19ee09eec..19ee09eec 100644 --- a/clang-tidy/.editorconfig +++ b/subprojects/lix-clang-tidy/.editorconfig diff --git a/clang-tidy/FixIncludes.cc b/subprojects/lix-clang-tidy/FixIncludes.cc index 602d3d355..602d3d355 100644 --- a/clang-tidy/FixIncludes.cc +++ b/subprojects/lix-clang-tidy/FixIncludes.cc diff --git a/clang-tidy/FixIncludes.hh b/subprojects/lix-clang-tidy/FixIncludes.hh index ea890cd39..ea890cd39 100644 --- a/clang-tidy/FixIncludes.hh +++ b/subprojects/lix-clang-tidy/FixIncludes.hh diff --git a/clang-tidy/HasPrefixSuffix.cc b/subprojects/lix-clang-tidy/HasPrefixSuffix.cc index e29b67e7c..e29b67e7c 100644 --- a/clang-tidy/HasPrefixSuffix.cc +++ b/subprojects/lix-clang-tidy/HasPrefixSuffix.cc diff --git a/clang-tidy/HasPrefixSuffix.hh b/subprojects/lix-clang-tidy/HasPrefixSuffix.hh index 8693b6767..8693b6767 100644 --- a/clang-tidy/HasPrefixSuffix.hh +++ b/subprojects/lix-clang-tidy/HasPrefixSuffix.hh diff --git a/clang-tidy/LixClangTidyChecks.cc b/subprojects/lix-clang-tidy/LixClangTidyChecks.cc index b3503dd3a..b3503dd3a 100644 --- a/clang-tidy/LixClangTidyChecks.cc +++ b/subprojects/lix-clang-tidy/LixClangTidyChecks.cc diff --git a/clang-tidy/README.md b/subprojects/lix-clang-tidy/README.md index c2d1cb258..c2d1cb258 100644 --- a/clang-tidy/README.md +++ b/subprojects/lix-clang-tidy/README.md diff --git a/subprojects/lix-clang-tidy/default.nix b/subprojects/lix-clang-tidy/default.nix new file mode 100644 index 000000000..1bfc2d9a4 --- /dev/null +++ b/subprojects/lix-clang-tidy/default.nix @@ -0,0 +1,44 @@ +{ + lib, + stdenv, + cmake, + meson, + ninja, + llvmPackages, +}: +let + inherit (lib) fileset; +in +stdenv.mkDerivation { + pname = "lix-clang-tidy-checks"; + # Setting the version to the Lix version is just going to cause pointless + # rebuilds due to versionSuffix and similar, and I cannot conceive of a usage + # where we actually care about its version since this is internal-only. + version = "0.1"; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + (fileset.fileFilter ( + { hasExt, ... }: + builtins.any hasExt [ + "cc" + "hh" + ] + ) ./.) + ]; + }; + + nativeBuildInputs = [ + meson + cmake + ninja + ]; + + buildInputs = [ + llvmPackages.llvm + llvmPackages.clang-unwrapped.dev + ]; +} diff --git a/subprojects/lix-clang-tidy/meson.build b/subprojects/lix-clang-tidy/meson.build new file mode 100644 index 000000000..ef0226420 --- /dev/null +++ b/subprojects/lix-clang-tidy/meson.build @@ -0,0 +1,18 @@ +project('lix-clang-tidy', ['cpp', 'c'], + version : '0.1', + default_options : ['warning_level=3', 'cpp_std=c++20'] +) + +llvm = dependency('Clang', version: '>= 17', modules: ['libclang']) +sources = files( + 'HasPrefixSuffix.cc', + 'LixClangTidyChecks.cc', + 'FixIncludes.cc', +) + +lix_clang_tidy = shared_module('lix-clang-tidy', sources, + dependencies: llvm, + # overrides build_by_default, see https://github.com/mesonbuild/meson/issues/13498 + install : get_option('build-by-default'), + build_by_default : get_option('build-by-default') +) diff --git a/subprojects/lix-clang-tidy/meson.options b/subprojects/lix-clang-tidy/meson.options new file mode 100644 index 000000000..57f0f713b --- /dev/null +++ b/subprojects/lix-clang-tidy/meson.options @@ -0,0 +1,3 @@ +option('build-by-default', type : 'boolean', value : true, + description : 'set to false to not actually build targets by default. This is a hack to deal with meson lacking a build_by_default default option and building subprojects by default' +) diff --git a/tests/functional/flakes/flake-metadata.sh b/tests/functional/flakes/flake-metadata.sh new file mode 100644 index 000000000..ab5a69b3a --- /dev/null +++ b/tests/functional/flakes/flake-metadata.sh @@ -0,0 +1,36 @@ +source ./common.sh + +flakeDir=$TEST_ROOT/flake +mkdir -p "$flakeDir" + +cat > "$flakeDir/flake.nix" <<-'EOF' +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable-small"; + flake-utils.url = "github:numtide/flake-utils"; + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + lanzaboote = { + url = "github:nix-community/lanzaboote"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.flake-utils.follows = "flake-utils"; + inputs.flake-compat.follows = "flake-compat"; + }; + }; + + outputs = { ... }: {}; +} +EOF + +cp flake-metadata/flake.lock "$flakeDir" +touch -d @1000 "$flakeDir/flake.nix" "$flakeDir/flake.lock" "$flakeDir" + +# For some reason we use NIX_STORE_DIR which causes unstable paths. This is +# goofy. We can just use --store, which sets rootDir and does not have this +# problem. +actualStore=$NIX_STORE_DIR +unset NIX_STORE_DIR +NOCOLOR=1 TZ=UTC LC_ALL=C.UTF-8 nix flake metadata --store "$actualStore" "$flakeDir" | grep -v -e 'Locked URL:' -e 'Resolved URL:' > "$TEST_ROOT/metadata.out.actual" +diff -u flake-metadata/metadata.out.expected "$TEST_ROOT/metadata.out.actual" diff --git a/tests/functional/flakes/flake-metadata/flake.lock b/tests/functional/flakes/flake-metadata/flake.lock new file mode 100644 index 000000000..62b7dc3e8 --- /dev/null +++ b/tests/functional/flakes/flake-metadata/flake.lock @@ -0,0 +1,245 @@ +{ + "nodes": { + "crane": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1711299236, + "narHash": "sha256-6/JsyozOMKN8LUGqWMopKTSiK8N79T8Q+hcxu2KkTXg=", + "owner": "ipetkov", + "repo": "crane", + "rev": "880573f80d09e18a11713f402b9e6172a085449f", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709336216, + "narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "pre-commit-hooks-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "lanzaboote": { + "inputs": { + "crane": "crane", + "flake-compat": [ + "flake-compat" + ], + "flake-parts": "flake-parts", + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks-nix": "pre-commit-hooks-nix", + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1713369831, + "narHash": "sha256-G4OGxvlIIjphpkxcRAkf1QInYsAeqbfNh6Yl1JLy2uM=", + "owner": "nix-community", + "repo": "lanzaboote", + "rev": "850f27322239f8cfa56b122cc9a278ab99a49015", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "lanzaboote", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1719824438, + "narHash": "sha256-pY0wosAgcr9W4vmGML0T3BVhQiGuKoozCbs2t+Je1zc=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "7f993cdf26ccef564eabf31fdb40d140821e12bc", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable-small", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks-nix": { + "inputs": { + "flake-compat": [ + "lanzaboote", + "flake-compat" + ], + "flake-utils": [ + "lanzaboote", + "flake-utils" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1710923068, + "narHash": "sha256-6hOpUiuxuwpXXc/xfJsBUJeqqgGI+JMJuLo45aG3cKc=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "e611897ddfdde3ed3eaac4758635d7177ff78673", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "lanzaboote": "lanzaboote", + "nixpkgs": "nixpkgs" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "lanzaboote", + "flake-utils" + ], + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1711246447, + "narHash": "sha256-g9TOluObcOEKewFo2fR4cn51Y/jSKhRRo4QZckHLop0=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "dcc802a6ec4e9cc6a1c8c393327f0c42666f22e4", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/tests/functional/flakes/flake-metadata/metadata.out.expected b/tests/functional/flakes/flake-metadata/metadata.out.expected new file mode 100644 index 000000000..72e57eca7 --- /dev/null +++ b/tests/functional/flakes/flake-metadata/metadata.out.expected @@ -0,0 +1,36 @@ +Path: /nix/store/gaxb42z68bcr8lch467shvmnhjjzgd8b-source +Last modified: 1970-01-01 00:16:40 +Inputs: +├───flake-compat: github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33 +│ Last modified: 2023-10-04 13:37:54 +├───flake-utils: github:numtide/flake-utils/b1d9ab70662946ef0850d488da1c9019f3a9752a +│ Last modified: 2024-03-11 08:33:50 +│ └───systems: github:nix-systems/default/da67096a3b9bf56a91d16901293e51ba5b49a27e +│ Last modified: 2023-04-09 08:27:08 +├───lanzaboote: github:nix-community/lanzaboote/850f27322239f8cfa56b122cc9a278ab99a49015 +│ Last modified: 2024-04-17 16:03:51 +│ ├───crane: github:ipetkov/crane/880573f80d09e18a11713f402b9e6172a085449f +│ │ Last modified: 2024-03-24 16:53:56 +│ │ └───nixpkgs follows input 'lanzaboote/nixpkgs' +│ ├───flake-compat follows input 'flake-compat' +│ ├───flake-parts: github:hercules-ci/flake-parts/f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2 +│ │ Last modified: 2024-03-01 23:36:56 +│ │ └───nixpkgs-lib follows input 'lanzaboote/nixpkgs' +│ ├───flake-utils follows input 'flake-utils' +│ ├───nixpkgs follows input 'nixpkgs' +│ ├───pre-commit-hooks-nix: github:cachix/pre-commit-hooks.nix/e611897ddfdde3ed3eaac4758635d7177ff78673 +│ │ Last modified: 2024-03-20 08:24:28 +│ │ ├───flake-compat follows input 'lanzaboote/flake-compat' +│ │ ├───flake-utils follows input 'lanzaboote/flake-utils' +│ │ ├───gitignore: github:hercules-ci/gitignore.nix/637db329424fd7e46cf4185293b9cc8c88c95394 +│ │ │ Last modified: 2024-02-28 02:28:52 +│ │ │ └───nixpkgs follows input 'lanzaboote/pre-commit-hooks-nix/nixpkgs' +│ │ ├───nixpkgs follows input 'lanzaboote/nixpkgs' +│ │ └───nixpkgs-stable: github:NixOS/nixpkgs/614b4613980a522ba49f0d194531beddbb7220d3 +│ │ Last modified: 2024-03-17 17:16:56 +│ └───rust-overlay: github:oxalica/rust-overlay/dcc802a6ec4e9cc6a1c8c393327f0c42666f22e4 +│ Last modified: 2024-03-24 02:14:07 +│ ├───flake-utils follows input 'lanzaboote/flake-utils' +│ └───nixpkgs follows input 'lanzaboote/nixpkgs' +└───nixpkgs: github:nixos/nixpkgs/7f993cdf26ccef564eabf31fdb40d140821e12bc + Last modified: 2024-07-01 09:00:38 diff --git a/tests/functional/lang/eval-okay-derivation-legacy.err.exp b/tests/functional/lang/eval-okay-derivation-legacy.err.exp new file mode 100644 index 000000000..94f0854dd --- /dev/null +++ b/tests/functional/lang/eval-okay-derivation-legacy.err.exp @@ -0,0 +1,6 @@ +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'allowedReferences'; use 'outputChecks.<output>.allowedReferences' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'allowedRequisites'; use 'outputChecks.<output>.allowedRequisites' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedReferences'; use 'outputChecks.<output>.disallowedReferences' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedRequisites'; use 'outputChecks.<output>.disallowedRequisites' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'maxClosureSize'; use 'outputChecks.<output>.maxClosureSize' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'maxSize'; use 'outputChecks.<output>.maxSize' instead diff --git a/tests/functional/lang/eval-okay-derivation-legacy.exp b/tests/functional/lang/eval-okay-derivation-legacy.exp new file mode 100644 index 000000000..4f374a1aa --- /dev/null +++ b/tests/functional/lang/eval-okay-derivation-legacy.exp @@ -0,0 +1 @@ +"/nix/store/mzgwvrjjir216ra58mwwizi8wj6y9ddr-eval-okay-derivation-legacy" diff --git a/tests/functional/lang/eval-okay-derivation-legacy.nix b/tests/functional/lang/eval-okay-derivation-legacy.nix new file mode 100644 index 000000000..b529cdf90 --- /dev/null +++ b/tests/functional/lang/eval-okay-derivation-legacy.nix @@ -0,0 +1,12 @@ +(builtins.derivationStrict { + name = "eval-okay-derivation-legacy"; + system = "x86_64-linux"; + builder = "/dontcare"; + __structuredAttrs = true; + allowedReferences = [ ]; + disallowedReferences = [ ]; + allowedRequisites = [ ]; + disallowedRequisites = [ ]; + maxSize = 1234; + maxClosureSize = 12345; +}).out diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 7a9c7182f..2b5dfe422 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -69,8 +69,9 @@ functional_tests_scripts = [ 'flakes/unlocked-override.sh', 'flakes/absolute-paths.sh', 'flakes/build-paths.sh', - 'flakes/flake-registry.sh', 'flakes/flake-in-submodule.sh', + 'flakes/flake-metadata.sh', + 'flakes/flake-registry.sh', 'flakes/subdir-flake.sh', 'gc.sh', 'nix-collect-garbage-d.sh', diff --git a/tests/functional/search.sh b/tests/functional/search.sh index d9c7a75da..1a2a20089 100644 --- a/tests/functional/search.sh +++ b/tests/functional/search.sh @@ -29,6 +29,8 @@ nix search -f search.nix '' ^ | grepQuiet hello ## Tests for multiple regex/match highlighting +# FIXME: possibly not test this with colour in the future +export CLICOLOR_FORCE=1 e=$'\x1b' # grep doesn't support \e, \033 or even \x1b # Multiple overlapping regexes (( $(nix search -f search.nix '' 'oo' 'foo' 'oo' | grep -c "$e\[32;1mfoo$e\\[0;1m") == 1 )) diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index 01dcbb34c..745aa168d 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -26,9 +26,9 @@ namespace nix { , state({}, store) { } - Value eval(std::string input, bool forceValue = true) { + Value eval(std::string input, bool forceValue = true, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings) { Value v; - Expr & e = state.parseExprFromString(input, state.rootPath(CanonPath::root)); + Expr & e = state.parseExprFromString(input, state.rootPath(CanonPath::root), xpSettings); state.eval(e, v); if (forceValue) state.forceValue(v, noPos); diff --git a/tests/unit/libexpr/trivial.cc b/tests/unit/libexpr/trivial.cc index 171727ac7..19b62aff8 100644 --- a/tests/unit/libexpr/trivial.cc +++ b/tests/unit/libexpr/trivial.cc @@ -59,6 +59,11 @@ namespace nix { ASSERT_THAT(v, IsFloatEq(1.234)); } + TEST_F(TrivialExpressionTest, pointfloat) { + auto v = eval(".234"); + ASSERT_THAT(v, IsFloatEq(0.234)); + } + TEST_F(TrivialExpressionTest, updateAttrs) { auto v = eval("{ a = 1; } // { b = 2; a = 3; }"); ASSERT_THAT(v, IsAttrsOfSize(2)); @@ -81,6 +86,18 @@ namespace nix { ASSERT_THAT(v, IsTrue()); } + TEST_F(TrivialExpressionTest, urlLiteral) { + auto v = eval("https://nixos.org"); + ASSERT_THAT(v, IsStringEq("https://nixos.org")); + } + + TEST_F(TrivialExpressionTest, noUrlLiteral) { + ExperimentalFeatureSettings mockXpSettings; + mockXpSettings.set("experimental-features", "no-url-literals"); + + ASSERT_THROW(eval("https://nixos.org", true, mockXpSettings), Error); + } + TEST_F(TrivialExpressionTest, withFound) { auto v = eval("with { a = 23; }; a"); ASSERT_THAT(v, IsIntEq(23)); diff --git a/tests/unit/libutil-support/tests/cli-literate-parser.cc b/tests/unit/libutil-support/tests/cli-literate-parser.cc index f74fe85eb..3aa55859c 100644 --- a/tests/unit/libutil-support/tests/cli-literate-parser.cc +++ b/tests/unit/libutil-support/tests/cli-literate-parser.cc @@ -1,20 +1,16 @@ #include "cli-literate-parser.hh" #include "escape-string.hh" -#include "escape-char.hh" -#include "libexpr/print.hh" #include "types.hh" #include <ranges> #include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/trim.hpp> #include <iostream> -#include <memory> #include <sstream> #include <variant> #include "cli-literate-parser.hh" #include "escape-string.hh" #include "fmt.hh" -#include "libexpr/print.hh" #include "shlex.hh" #include "types.hh" #include "strings.hh" @@ -361,9 +357,8 @@ const char * ParseError::what() const noexcept return what_->c_str(); } else { auto escaped = escapeString(rest, {.maxLength = 256, .escapeNonPrinting = true}); - auto hint = - new HintFmt("Parse error: Expected %1%, got:\n%2%", expected, Uncolored(escaped)); - what_ = hint->str(); + auto hint = HintFmt("Parse error: Expected %1%, got:\n%2%", expected, Uncolored(escaped)); + what_ = hint.str(); return what_->c_str(); } } |