diff options
Diffstat (limited to 'src/libexpr/eval.cc')
-rw-r--r-- | src/libexpr/eval.cc | 115 |
1 files changed, 71 insertions, 44 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 353097f89713..f41905787f9e 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -13,17 +13,14 @@ #include <sys/time.h> #include <sys/resource.h> +#include <sys/time.h> +#include <sys/resource.h> + #if HAVE_BOEHMGC #include <gc/gc.h> #include <gc/gc_cpp.h> -#define NEW new (UseGC) - -#else - -#define NEW new - #endif @@ -34,7 +31,7 @@ static char * dupString(const char * s) { char * t; #if HAVE_BOEHMGC - t = GC_strdup(s); + t = GC_STRDUP(s); #else t = strdup(s); #endif @@ -43,20 +40,6 @@ static char * dupString(const char * s) } -/* Note: Various places expect the allocated memory to be zeroed. */ -static void * allocBytes(size_t n) -{ - void * p; -#if HAVE_BOEHMGC - p = GC_malloc(n); -#else - p = calloc(n, 1); -#endif - if (!p) throw std::bad_alloc(); - return p; -} - - static void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v) { checkInterrupt(); @@ -199,8 +182,15 @@ void initGC() #if HAVE_BOEHMGC /* Initialise the Boehm garbage collector. */ + + /* Don't look for interior pointers. This reduces the odds of + misdetection a bit. */ GC_set_all_interior_pointers(0); + /* We don't have any roots in data segments, so don't scan from + there. */ + GC_set_no_dls(1); + GC_INIT(); GC_set_oom_fn(oomHandler); @@ -307,15 +297,17 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store) assert(gcInitialised); + static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes"); + /* Initialise the Nix expression search path. */ - if (!settings.pureEval) { + if (!evalSettings.pureEval) { Strings paths = parseNixPath(getEnv("NIX_PATH", "")); for (auto & i : _searchPath) addToSearchPath(i); for (auto & i : paths) addToSearchPath(i); } addToSearchPath("nix=" + canonPath(settings.nixDataDir + "/nix/corepkgs", true)); - if (settings.restrictEval || settings.pureEval) { + if (evalSettings.restrictEval || evalSettings.pureEval) { allowedPaths = PathSet(); for (auto & i : searchPath) { @@ -344,7 +336,6 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store) EvalState::~EvalState() { - fileEvalCache.clear(); } @@ -352,25 +343,37 @@ Path EvalState::checkSourcePath(const Path & path_) { if (!allowedPaths) return path_; + auto i = resolvedPaths.find(path_); + if (i != resolvedPaths.end()) + return i->second; + bool found = false; + /* First canonicalize the path without symlinks, so we make sure an + * attacker can't append ../../... to a path that would be in allowedPaths + * and thus leak symlink targets. + */ + Path abspath = canonPath(path_); + for (auto & i : *allowedPaths) { - if (isDirOrInDir(path_, i)) { + if (isDirOrInDir(abspath, i)) { found = true; break; } } if (!found) - throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path_); + throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", abspath); /* Resolve symlinks. */ - debug(format("checking access to '%s'") % path_); - Path path = canonPath(path_, true); + debug(format("checking access to '%s'") % abspath); + Path path = canonPath(abspath, true); for (auto & i : *allowedPaths) { - if (isDirOrInDir(path, i)) + if (isDirOrInDir(path, i)) { + resolvedPaths[path_] = path; return path; + } } throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path); @@ -379,13 +382,13 @@ Path EvalState::checkSourcePath(const Path & path_) void EvalState::checkURI(const std::string & uri) { - if (!settings.restrictEval) return; + if (!evalSettings.restrictEval) return; /* 'uri' should be equal to a prefix, or in a subdirectory of a prefix. Thus, the prefix https://github.co does not permit access to https://github.com. Note: this allows 'http://' and 'https://' as prefixes for any http/https URI. */ - for (auto & prefix : settings.allowedUris.get()) + for (auto & prefix : evalSettings.allowedUris.get()) if (uri == prefix || (uri.size() > prefix.size() && prefix.size() > 0 @@ -443,7 +446,7 @@ Value * EvalState::addPrimOp(const string & name, string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; Symbol sym = symbols.create(name2); v->type = tPrimOp; - v->primOp = NEW PrimOp(primOp, arity, sym); + v->primOp = new PrimOp(primOp, arity, sym); staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; baseEnv.values[baseEnvDispl++] = v; baseEnv.values[0]->attrs->push_back(Attr(sym, v)); @@ -562,12 +565,12 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) if (!var.fromWith) return env->values[var.displ]; while (1) { - if (!env->haveWithAttrs) { + if (env->type == Env::HasWithExpr) { if (noEval) return 0; Value * v = allocValue(); evalAttrs(*env->up, (Expr *) env->values[0], *v); env->values[0] = v; - env->haveWithAttrs = true; + env->type = Env::HasWithAttrs; } Bindings::iterator j = env->values[0]->attrs->find(var.name); if (j != env->values[0]->attrs->end()) { @@ -581,10 +584,19 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) } +std::atomic<uint64_t> nrValuesFreed{0}; + +void finalizeValue(void * obj, void * data) +{ + nrValuesFreed++; +} + Value * EvalState::allocValue() { nrValues++; - return (Value *) allocBytes(sizeof(Value)); + auto v = (Value *) allocBytes(sizeof(Value)); + //GC_register_finalizer_no_order(v, finalizeValue, nullptr, nullptr, nullptr); + return v; } @@ -597,6 +609,7 @@ Env & EvalState::allocEnv(size_t size) nrValuesInEnvs += size; Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *)); env->size = (decltype(Env::size)) size; + env->type = Env::Plain; /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */ @@ -716,7 +729,17 @@ void EvalState::evalFile(const Path & path_, Value & v) } printTalkative("evaluating file '%1%'", path2); - Expr * e = parseExprFromFile(checkSourcePath(path2)); + Expr * e = nullptr; + + auto j = fileParseCache.find(path2); + if (j != fileParseCache.end()) + e = j->second; + + if (!e) + e = parseExprFromFile(checkSourcePath(path2)); + + fileParseCache[path2] = e; + try { eval(e, v); } catch (Error & e) { @@ -1059,6 +1082,8 @@ void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos) void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & pos) { + forceValue(fun, pos); + if (fun.type == tPrimOp || fun.type == tPrimOpApp) { callPrimOp(fun, arg, v, pos); return; @@ -1074,10 +1099,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po auto & fun2 = *allocValue(); fun2 = fun; /* !!! Should we use the attr pos here? */ - forceValue(*found->value, pos); Value v2; callFunction(*found->value, fun2, v2, pos); - forceValue(v2, pos); return callFunction(v2, arg, v, pos); } } @@ -1164,7 +1187,6 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) if (fun.type == tAttrs) { auto found = fun.attrs->find(sFunctor); if (found != fun.attrs->end()) { - forceValue(*found->value); Value * v = allocValue(); callFunction(*found->value, fun, *v, noPos); forceValue(*v); @@ -1199,7 +1221,7 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) Env & env2(state.allocEnv(1)); env2.up = &env; env2.prevWith = prevWith; - env2.haveWithAttrs = false; + env2.type = Env::HasWithExpr; env2.values[0] = (Value *) attrs; body->eval(state, env2, v); @@ -1548,7 +1570,6 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, if (v.type == tAttrs) { auto i = v.attrs->find(sToString); if (i != v.attrs->end()) { - forceValue(*i->value, pos); Value v1; callFunction(*i->value, v, v1, pos); return coerceToString(pos, v1, context, coerceMore, copyToStore); @@ -1857,9 +1878,10 @@ size_t valueSize(Value & v) size_t sz = sizeof(Env) + sizeof(Value *) * env.size; - for (size_t i = 0; i < env.size; ++i) - if (env.values[i]) - sz += doValue(*env.values[i]); + if (env.type != Env::HasWithExpr) + for (size_t i = 0; i < env.size; ++i) + if (env.values[i]) + sz += doValue(*env.values[i]); if (env.up) sz += doEnv(*env.up); @@ -1888,4 +1910,9 @@ std::ostream & operator << (std::ostream & str, const ExternalValueBase & v) { } +EvalSettings evalSettings; + +static GlobalConfig::Register r1(&evalSettings); + + } |