diff --git a/meson.build b/meson.build index 672db4ddf..e6151e0a2 100644 --- a/meson.build +++ b/meson.build @@ -437,7 +437,9 @@ add_project_arguments( language : 'cpp', ) -if cxx.get_id() in ['gcc', 'clang'] +# We turn off the production UBSan if the slower dev UBSan is requested, to +# give better diagnostics. +if cxx.get_id() in ['gcc', 'clang'] and 'undefined' not in get_option('b_sanitize') # 2024-03-24: jade benchmarked the default sanitize reporting in clang and got # a regression of about 10% on hackage-packages.nix with clang. So we are trapping instead. # @@ -452,6 +454,11 @@ if cxx.get_id() in ['gcc', 'clang'] add_project_arguments(sanitize_args, language: 'cpp') add_project_link_arguments(sanitize_args, language: 'cpp') endif +# Clang's default of -no-shared-libsan on Linux causes link errors; on macOS it defaults to shared. +# GCC defaults to shared libsan so is fine. +if cxx.get_id() == 'clang' and get_option('b_sanitize') != '' + add_project_link_arguments('-shared-libsan', language : 'cpp') +endif add_project_link_arguments('-pthread', language : 'cpp') if cxx.get_linker_id() in ['ld.bfd', 'ld.gold'] diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 105fd3e9d..0bdf1b9a5 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -63,11 +63,9 @@ Strings EvalSettings::getDefaultNixPath() } }; - if (!evalSettings.restrictEval && !evalSettings.pureEval) { - add(getNixDefExpr() + "/channels"); - add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); - add(rootChannelsDir()); - } + add(getNixDefExpr() + "/channels"); + add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); + add(rootChannelsDir()); return res; } diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index cd73d195f..4673c509b 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -75,8 +75,17 @@ struct EvalSettings : Config R"( Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state: - - Restrict file system and network access to files specified by cryptographic hash - - Disable [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime) + - File system and network access is restricted to accesses to immutable data only: + - Path literals relative to the home directory like `~/lix` are rejected at parse time. + - Access to absolute paths that did not result from Nix language evaluation is rejected when such paths are given as parameters to builtins like, for example, [`builtins.readFile`](@docroot@/language/builtins.md#builtins-readFile). + + Access is nonetheless allowed to (absolute) paths in the Nix store that are returned by builtins like [`builtins.filterSource`](@docroot@/language/builtins.md#builtins-filterSource), [`builtins.fetchTarball`](@docroot@/language/builtins.md#builtins-fetchTarball) and similar. + - Impure fetches such as not specifying a commit ID for `builtins.fetchGit` or not specifying a hash for `builtins.fetchTarball` are rejected. + - In flakes, access to relative paths outside of the root of the flake's source tree (often, a git repository) is rejected. + - The evaluator ignores `NIX_PATH`, `-I` and the `nix-path` setting. Thus, [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath) is an empty list. + - The builtins [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime) are absent from `builtins`. + - [`builtins.getEnv`](@docroot@/language/builtin-constants.md#builtins-currentSystem) always returns empty string for any variable. + - [`builtins.storePath`](@docroot@/language/builtins.md#builtins-storePath) throws an error (Lix may change this, tracking issue: ) )" }; @@ -98,6 +107,7 @@ struct EvalSettings : Config allowed to access `https://github.com/NixOS/patchelf.git`. )"}; + Setting traceFunctionCalls{this, false, "trace-function-calls", R"( If set to `true`, the Nix evaluator will trace every function call. diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 8d6cdaad7..9bd27e22d 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -418,7 +418,7 @@ EvalState::EvalState( } if (evalSettings.restrictEval || evalSettings.pureEval) { - allowedPaths = PathSet(); + allowedPaths = std::optional(PathSet()); for (auto & i : searchPath.elements) { auto r = resolveSearchPathPath(i.path); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 4b7e61dfe..20851da70 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -923,14 +923,15 @@ static RegisterPrimOp primop_getEnv({ .args = {"s"}, .doc = R"( `getEnv` returns the value of the environment variable *s*, or an - empty string if the variable doesn’t exist. This function should be + empty string if the variable doesn't exist. This function should be used with care, as it can introduce all sorts of nasty environment dependencies in your Nix expression. - `getEnv` is used in Nix Packages to locate the file - `~/.nixpkgs/config.nix`, which contains user-local settings for Nix - Packages. (That is, it does a `getEnv "HOME"` to locate the user’s - home directory.) + `getEnv` is used in nixpkgs for evil impurities such as locating the file + `~/.config/nixpkgs/config.nix` which contains user-local settings for nixpkgs. + (That is, it does a `getEnv "HOME"` to locate the user's home directory.) + + When in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval), this function always returns an empty string. )", .fun = prim_getEnv, }); @@ -1506,6 +1507,7 @@ static RegisterPrimOp primop_storePath({ in a new path (e.g. `/nix/store/ld01dnzc…-source-source`). Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval). + Lix may change this, tracking issue: See also [`builtins.fetchClosure`](#builtins-fetchClosure). )", diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc index 20b5befe4..8fcb10325 100644 --- a/src/libmain/common-args.cc +++ b/src/libmain/common-args.cc @@ -1,5 +1,6 @@ #include "common-args.hh" #include "args/root.hh" +#include "error.hh" #include "globals.hh" #include "loggers.hh" #include "logging.hh" @@ -14,14 +15,14 @@ MixCommonArgs::MixCommonArgs(const std::string & programName) .shortName = 'v', .description = "Increase the logging verbosity level.", .category = loggingCategory, - .handler = {[]() { verbosity = (Verbosity) (verbosity + 1); }}, + .handler = {[]() { verbosity = verbosityFromIntClamped(int(verbosity) + 1); }}, }); addFlag({ .longName = "quiet", .description = "Decrease the logging verbosity level.", .category = loggingCategory, - .handler = {[]() { verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError; }}, + .handler = {[]() { verbosity = verbosityFromIntClamped(int(verbosity) - 1); }}, }); addFlag({ diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 97ba994ad..46399b0a8 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -1553,11 +1553,12 @@ void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result) Goal::waiteeDone(waitee, result); if (!useDerivation) return; - auto & fullDrv = *dynamic_cast(drv.get()); auto * dg = dynamic_cast(&*waitee); if (!dg) return; + auto & fullDrv = *dynamic_cast(drv.get()); + auto * nodeP = fullDrv.inputDrvs.findSlot(DerivedPath::Opaque { .path = dg->drvPath }); if (!nodeP) return; auto & outputs = nodeP->value; diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 0884f9f32..06dfba0df 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -45,6 +45,8 @@ typedef enum { lvlVomit } Verbosity; +Verbosity verbosityFromIntClamped(int val); + /** * The lines of code surrounding an error. */ diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index b01bb4dd4..fecbc89ac 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -5,6 +5,7 @@ #include "position.hh" #include "terminal.hh" +#include #include #include #include @@ -110,6 +111,12 @@ public: Verbosity verbosity = lvlInfo; +Verbosity verbosityFromIntClamped(int val) +{ + int clamped = std::clamp(val, int(lvlError), int(lvlVomit)); + return static_cast(clamped); +} + void writeToStderr(std::string_view s) { try {