diff options
Diffstat (limited to 'src/libexpr/eval.cc')
-rw-r--r-- | src/libexpr/eval.cc | 222 |
1 files changed, 144 insertions, 78 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 63de2d60a147..2473157b3f63 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -43,13 +43,14 @@ 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 = malloc(n); + p = calloc(n, 1); #endif if (!p) throw std::bad_alloc(); return p; @@ -293,6 +294,10 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store) , sWrong(symbols.create("wrong")) , sStructuredAttrs(symbols.create("__structuredAttrs")) , sBuilder(symbols.create("builder")) + , sArgs(symbols.create("args")) + , sOutputHash(symbols.create("outputHash")) + , sOutputHashAlgo(symbols.create("outputHashAlgo")) + , sOutputHashMode(symbols.create("outputHashMode")) , repair(NoRepair) , store(store) , baseEnv(allocEnv(128)) @@ -300,15 +305,36 @@ EvalState::EvalState(const Strings & _searchPath, ref<Store> store) { countCalls = getEnv("NIX_COUNT_CALLS", "0") != "0"; - restricted = settings.restrictEval; - assert(gcInitialised); + static_assert(sizeof(Env) == 16); + /* Initialise the Nix expression search path. */ - Strings paths = parseNixPath(getEnv("NIX_PATH", "")); - for (auto & i : _searchPath) addToSearchPath(i); - for (auto & i : paths) addToSearchPath(i); - addToSearchPath("nix=" + settings.nixDataDir + "/nix/corepkgs"); + if (!settings.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) { + allowedPaths = PathSet(); + + for (auto & i : searchPath) { + auto r = resolveSearchPathElem(i); + if (!r.first) continue; + + auto path = r.second; + + if (store->isInStore(r.second)) { + PathSet closure; + store->computeFSClosure(store->toStorePath(r.second), closure); + for (auto & path : closure) + allowedPaths->insert(path); + } else + allowedPaths->insert(r.second); + } + } clearValue(vEmptySet); vEmptySet.type = tAttrs; @@ -326,38 +352,42 @@ EvalState::~EvalState() Path EvalState::checkSourcePath(const Path & path_) { - if (!restricted) return path_; + if (!allowedPaths) return path_; + + auto i = resolvedPaths.find(path_); + if (i != resolvedPaths.end()) + return i->second; + + bool found = false; + + for (auto & i : *allowedPaths) { + if (isDirOrInDir(path_, i)) { + found = true; + break; + } + } + + if (!found) + throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path_); /* Resolve symlinks. */ debug(format("checking access to '%s'") % path_); Path path = canonPath(path_, true); - for (auto & i : searchPath) { - auto r = resolveSearchPathElem(i); - if (!r.first) continue; - if (path == r.second || isInDir(path, r.second)) + for (auto & i : *allowedPaths) { + if (isDirOrInDir(path, i)) { + resolvedPaths[path_] = path; return path; + } } - /* To support import-from-derivation, allow access to anything in - the store. FIXME: only allow access to paths that have been - constructed by this evaluation. */ - if (store->isInStore(path)) return path; - -#if 0 - /* Hack to support the chroot dependencies of corepkgs (see - corepkgs/config.nix.in). */ - if (path == settings.nixPrefix && isStorePath(settings.nixPrefix)) - return path; -#endif - - throw RestrictedPathError(format("access to path '%1%' is forbidden in restricted mode") % path_); + throw RestrictedPathError("access to path '%1%' is forbidden in restricted mode", path); } void EvalState::checkURI(const std::string & uri) { - if (!restricted) return; + if (!settings.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 @@ -371,11 +401,33 @@ void EvalState::checkURI(const std::string & uri) && (prefix[prefix.size() - 1] == '/' || uri[prefix.size()] == '/'))) return; + /* If the URI is a path, then check it against allowedPaths as + well. */ + if (hasPrefix(uri, "/")) { + checkSourcePath(uri); + return; + } + + if (hasPrefix(uri, "file://")) { + checkSourcePath(std::string(uri, 7)); + return; + } + throw RestrictedPathError("access to URI '%s' is forbidden in restricted mode", uri); } -void EvalState::addConstant(const string & name, Value & v) +Path EvalState::toRealPath(const Path & path, const PathSet & context) +{ + // FIXME: check whether 'path' is in 'context'. + return + !context.empty() && store->isInStore(path) + ? store->toRealPath(path) + : path; +}; + + +Value * EvalState::addConstant(const string & name, Value & v) { Value * v2 = allocValue(); *v2 = v; @@ -383,12 +435,18 @@ void EvalState::addConstant(const string & name, Value & v) baseEnv.values[baseEnvDispl++] = v2; string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v2)); + return v2; } -void EvalState::addPrimOp(const string & name, - unsigned int arity, PrimOpFun primOp) +Value * EvalState::addPrimOp(const string & name, + size_t arity, PrimOpFun primOp) { + if (arity == 0) { + Value v; + primOp(*this, noPos, nullptr, v); + return addConstant(name, v); + } Value * v = allocValue(); string name2 = string(name, 0, 2) == "__" ? string(name, 2) : name; Symbol sym = symbols.create(name2); @@ -397,6 +455,7 @@ void EvalState::addPrimOp(const string & name, staticBaseEnv.vars[symbols.create(name)] = baseEnvDispl; baseEnv.values[baseEnvDispl++] = v; baseEnv.values[0]->attrs->push_back(Attr(sym, v)); + return v; } @@ -487,7 +546,7 @@ Value & mkString(Value & v, const string & s, const PathSet & context) { mkString(v, s.c_str()); if (!context.empty()) { - unsigned int n = 0; + size_t n = 0; v.string.context = (const char * *) allocBytes((context.size() + 1) * sizeof(char *)); for (auto & i : context) @@ -506,17 +565,17 @@ void mkPath(Value & v, const char * s) inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) { - for (unsigned int l = var.level; l; --l, env = env->up) ; + for (size_t l = var.level; l; --l, env = env->up) ; 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()) { @@ -525,7 +584,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) } if (!env->prevWith) throwUndefinedVarError("undefined variable '%1%' at %2%", var.name, var.pos); - for (unsigned int l = env->prevWith; l; --l, env = env->up) ; + for (size_t l = env->prevWith; l; --l, env = env->up) ; } } @@ -537,24 +596,24 @@ Value * EvalState::allocValue() } -Env & EvalState::allocEnv(unsigned int size) +Env & EvalState::allocEnv(size_t size) { - assert(size <= std::numeric_limits<decltype(Env::size)>::max()); + if (size > std::numeric_limits<decltype(Env::size)>::max()) + throw Error("environment size %d is too big", size); nrEnvs++; nrValuesInEnvs += size; Env * env = (Env *) allocBytes(sizeof(Env) + size * sizeof(Value *)); - env->size = size; + env->size = (decltype(Env::size)) size; + env->type = Env::Plain; - /* Clear the values because maybeThunk() and lookupVar fromWith expect this. */ - for (unsigned i = 0; i < size; ++i) - env->values[i] = 0; + /* We assume that env->values has been cleared by the allocator; maybeThunk() and lookupVar fromWith expect this. */ return *env; } -void EvalState::mkList(Value & v, unsigned int size) +void EvalState::mkList(Value & v, size_t size) { clearValue(v); if (size == 1) @@ -589,7 +648,7 @@ void EvalState::mkThunk_(Value & v, Expr * expr) void EvalState::mkPos(Value & v, Pos * pos) { - if (pos) { + if (pos && pos->file.set()) { mkAttrs(v, 3); mkString(*allocAttr(v, sFile), pos->file); mkInt(*allocAttr(v, sLine), pos->line); @@ -649,8 +708,10 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env) } -void EvalState::evalFile(const Path & path, Value & v) +void EvalState::evalFile(const Path & path_, Value & v) { + auto path = checkSourcePath(path_); + FileEvalCache::iterator i; if ((i = fileEvalCache.find(path)) != fileEvalCache.end()) { v = i->second; @@ -764,7 +825,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) /* The recursive attributes are evaluated in the new environment, while the inherited attributes are evaluated in the original environment. */ - unsigned int displ = 0; + size_t displ = 0; for (auto & i : attrs) { Value * vAttr; if (hasOverrides && !i.second.inherited) { @@ -838,7 +899,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) /* The recursive attributes are evaluated in the new environment, while the inherited attributes are evaluated in the original environment. */ - unsigned int displ = 0; + size_t displ = 0; for (auto & i : attrs->attrs) env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); @@ -849,7 +910,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) void ExprList::eval(EvalState & state, Env & env, Value & v) { state.mkList(v, elems.size()); - for (unsigned int n = 0; n < elems.size(); ++n) + for (size_t n = 0; n < elems.size(); ++n) v.listElems()[n] = elems[n]->maybeThunk(state, env); } @@ -971,22 +1032,22 @@ void ExprApp::eval(EvalState & state, Env & env, Value & v) void EvalState::callPrimOp(Value & fun, Value & arg, Value & v, const Pos & pos) { /* Figure out the number of arguments still needed. */ - unsigned int argsDone = 0; + size_t argsDone = 0; Value * primOp = &fun; while (primOp->type == tPrimOpApp) { argsDone++; primOp = primOp->primOpApp.left; } assert(primOp->type == tPrimOp); - unsigned int arity = primOp->primOp->arity; - unsigned int argsLeft = arity - argsDone; + auto arity = primOp->primOp->arity; + auto argsLeft = arity - argsDone; if (argsLeft == 1) { /* We have all the arguments, so call the primop. */ /* Put all the arguments in an array. */ Value * vArgs[arity]; - unsigned int n = arity - 1; + auto n = arity - 1; vArgs[n--] = &arg; for (Value * arg = &fun; arg->type == tPrimOpApp; arg = arg->primOpApp.left) vArgs[n--] = arg->primOpApp.right; @@ -1035,13 +1096,13 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po ExprLambda & lambda(*fun.lambda.fun); - unsigned int size = + auto size = (lambda.arg.empty() ? 0 : 1) + (lambda.matchAttrs ? lambda.formals->formals.size() : 0); Env & env2(allocEnv(size)); env2.up = fun.lambda.env; - unsigned int displ = 0; + size_t displ = 0; if (!lambda.matchAttrs) env2.values[displ++] = &arg; @@ -1055,7 +1116,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v, const Pos & po /* For each formal argument, get the actual argument. If there is no matching actual argument but the formal argument has a default, use the default. */ - unsigned int attrsUsed = 0; + size_t attrsUsed = 0; for (auto & i : lambda.formals->formals) { Bindings::iterator j = arg.attrs->find(i.name); if (j == arg.attrs->end()) { @@ -1147,7 +1208,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); @@ -1253,15 +1314,15 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) } -void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists, const Pos & pos) +void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos) { nrListConcats++; Value * nonEmpty = 0; - unsigned int len = 0; - for (unsigned int n = 0; n < nrLists; ++n) { + size_t len = 0; + for (size_t n = 0; n < nrLists; ++n) { forceList(*lists[n], pos); - unsigned int l = lists[n]->listSize(); + auto l = lists[n]->listSize(); len += l; if (l) nonEmpty = lists[n]; } @@ -1273,9 +1334,10 @@ void EvalState::concatLists(Value & v, unsigned int nrLists, Value * * lists, co mkList(v, len); auto out = v.listElems(); - for (unsigned int n = 0, pos = 0; n < nrLists; ++n) { - unsigned int l = lists[n]->listSize(); - memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *)); + for (size_t n = 0, pos = 0; n < nrLists; ++n) { + auto l = lists[n]->listSize(); + if (l) + memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *)); pos += l; } } @@ -1368,7 +1430,7 @@ void EvalState::forceValueDeep(Value & v) } else if (v.isList()) { - for (unsigned int n = 0; n < v.listSize(); ++n) + for (size_t n = 0; n < v.listSize(); ++n) recurse(*v.listElems()[n]); } }; @@ -1520,7 +1582,7 @@ string EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, if (v.isList()) { string result; - for (unsigned int n = 0; n < v.listSize(); ++n) { + for (size_t n = 0; n < v.listSize(); ++n) { result += coerceToString(pos, *v.listElems()[n], context, coerceMore, copyToStore); if (n < v.listSize() - 1 @@ -1546,7 +1608,7 @@ string EvalState::copyPathToStore(PathSet & context, const Path & path) dstPath = srcToStore[path]; else { dstPath = settings.readOnlyMode - ? store->computeStorePathForPath(checkSourcePath(path)).first + ? store->computeStorePathForPath(baseNameOf(path), checkSourcePath(path)).first : store->addToStore(baseNameOf(path), checkSourcePath(path), true, htSHA256, defaultPathFilter, repair); srcToStore[path] = dstPath; printMsg(lvlChatty, format("copied source '%1%' -> '%2%'") @@ -1607,7 +1669,7 @@ bool EvalState::eqValues(Value & v1, Value & v2) case tList2: case tListN: if (v1.listSize() != v2.listSize()) return false; - for (unsigned int n = 0; n < v1.listSize(); ++n) + for (size_t n = 0; n < v1.listSize(); ++n) if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false; return true; @@ -1668,10 +1730,13 @@ void EvalState::printStats() printMsg(v, format(" time elapsed: %1%") % cpuTime); printMsg(v, format(" size of a value: %1%") % sizeof(Value)); printMsg(v, format(" size of an attr: %1%") % sizeof(Attr)); - printMsg(v, format(" environments allocated: %1% (%2% bytes)") % nrEnvs % bEnvs); - printMsg(v, format(" list elements: %1% (%2% bytes)") % nrListElems % bLists); + printMsg(v, format(" environments allocated count: %1%") % nrEnvs); + printMsg(v, format(" environments allocated bytes: %1%") % bEnvs); + printMsg(v, format(" list elements count: %1%") % nrListElems); + printMsg(v, format(" list elements bytes: %1%") % bLists); printMsg(v, format(" list concatenations: %1%") % nrListConcats); - printMsg(v, format(" values allocated: %1% (%2% bytes)") % nrValues % bValues); + printMsg(v, format(" values allocated count: %1%") % nrValues); + printMsg(v, format(" values allocated bytes: %1%") % bValues); printMsg(v, format(" sets allocated: %1% (%2% bytes)") % nrAttrsets % bAttrsets); printMsg(v, format(" right-biased unions: %1%") % nrOpUpdates); printMsg(v, format(" values copied in right-biased unions: %1%") % nrOpUpdateValuesCopied); @@ -1695,26 +1760,26 @@ void EvalState::printStats() v = lvlInfo; printMsg(v, format("calls to %1% primops:") % primOpCalls.size()); - typedef std::multimap<unsigned int, Symbol> PrimOpCalls_; + typedef std::multimap<size_t, Symbol> PrimOpCalls_; PrimOpCalls_ primOpCalls_; for (auto & i : primOpCalls) - primOpCalls_.insert(std::pair<unsigned int, Symbol>(i.second, i.first)); + primOpCalls_.insert(std::pair<size_t, Symbol>(i.second, i.first)); for (auto i = primOpCalls_.rbegin(); i != primOpCalls_.rend(); ++i) printMsg(v, format("%1$10d %2%") % i->first % i->second); printMsg(v, format("calls to %1% functions:") % functionCalls.size()); - typedef std::multimap<unsigned int, ExprLambda *> FunctionCalls_; + typedef std::multimap<size_t, ExprLambda *> FunctionCalls_; FunctionCalls_ functionCalls_; for (auto & i : functionCalls) - functionCalls_.insert(std::pair<unsigned int, ExprLambda *>(i.second, i.first)); + functionCalls_.insert(std::pair<size_t, ExprLambda *>(i.second, i.first)); for (auto i = functionCalls_.rbegin(); i != functionCalls_.rend(); ++i) printMsg(v, format("%1$10d %2%") % i->first % i->second->showNamePos()); printMsg(v, format("evaluations of %1% attributes:") % attrSelects.size()); - typedef std::multimap<unsigned int, Pos> AttrSelects_; + typedef std::multimap<size_t, Pos> AttrSelects_; AttrSelects_ attrSelects_; for (auto & i : attrSelects) - attrSelects_.insert(std::pair<unsigned int, Pos>(i.second, i.first)); + attrSelects_.insert(std::pair<size_t, Pos>(i.second, i.first)); for (auto i = attrSelects_.rbegin(); i != attrSelects_.rend(); ++i) printMsg(v, format("%1$10d %2%") % i->first % i->second); @@ -1765,7 +1830,7 @@ size_t valueSize(Value & v) if (seen.find(v.listElems()) == seen.end()) { seen.insert(v.listElems()); sz += v.listSize() * sizeof(Value *); - for (unsigned int n = 0; n < v.listSize(); ++n) + for (size_t n = 0; n < v.listSize(); ++n) sz += doValue(*v.listElems()[n]); } break; @@ -1801,9 +1866,10 @@ size_t valueSize(Value & v) size_t sz = sizeof(Env) + sizeof(Value *) * env.size; - for (unsigned int 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); |