Require flake.nix to be an attrset (not a non-trivial thunk)

This commit is contained in:
Eelco Dolstra 2019-09-09 17:34:38 +02:00
parent c87840ae14
commit f97d3753a1
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
4 changed files with 28 additions and 8 deletions

View file

@ -179,6 +179,18 @@ string showType(const Value & v)
} }
bool Value::isTrivial() const
{
return
type != tApp
&& type != tPrimOpApp
&& (type != tThunk
|| (dynamic_cast<ExprAttrs *>(thunk.expr)
&& ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty())
|| dynamic_cast<ExprLambda *>(thunk.expr));
}
#if HAVE_BOEHMGC #if HAVE_BOEHMGC
/* Called when the Boehm GC runs out of memory. */ /* Called when the Boehm GC runs out of memory. */
static void * oomHandler(size_t requested) static void * oomHandler(size_t requested)
@ -749,7 +761,7 @@ Value * ExprPath::maybeThunk(EvalState & state, Env & env)
} }
void EvalState::evalFile(const Path & path_, Value & v) void EvalState::evalFile(const Path & path_, Value & v, bool mustBeTrivial)
{ {
auto path = checkSourcePath(path_); auto path = checkSourcePath(path_);
@ -778,6 +790,11 @@ void EvalState::evalFile(const Path & path_, Value & v)
fileParseCache[path2] = e; fileParseCache[path2] = e;
try { try {
// Enforce that 'flake.nix' is a direct attrset, not a
// computation.
if (mustBeTrivial &&
!(dynamic_cast<ExprAttrs *>(e)))
throw Error("file '%s' must be an attribute set", path);
eval(e, v); eval(e, v);
} catch (Error & e) { } catch (Error & e) {
addErrorPrefix(e, "while evaluating the file '%1%':\n", path2); addErrorPrefix(e, "while evaluating the file '%1%':\n", path2);

View file

@ -157,8 +157,9 @@ public:
Expr * parseStdin(); Expr * parseStdin();
/* Evaluate an expression read from the given file to normal /* Evaluate an expression read from the given file to normal
form. */ form. Optionally enforce that the top-level expression is
void evalFile(const Path & path, Value & v); trivial (i.e. doesn't require arbitrary computation). */
void evalFile(const Path & path, Value & v, bool mustBeTrivial = false);
void resetFileCache(); void resetFileCache();

View file

@ -198,9 +198,7 @@ static SourceInfo fetchFlake(EvalState & state, const FlakeRef & resolvedRef)
static void expectType(EvalState & state, ValueType type, static void expectType(EvalState & state, ValueType type,
Value & value, const Pos & pos) Value & value, const Pos & pos)
{ {
if (value.type == tThunk && if (value.type == tThunk && value.isTrivial())
((type == tAttrs && dynamic_cast<ExprAttrs *>(value.thunk.expr)) ||
(type == tLambda && dynamic_cast<ExprLambda *>(value.thunk.expr))))
state.forceValue(value, pos); state.forceValue(value, pos);
if (value.type != type) if (value.type != type)
throw Error("expected %s but got %s at %s", throw Error("expected %s but got %s at %s",
@ -231,8 +229,7 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", resolvedRef, resolvedRef.subdir); throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", resolvedRef, resolvedRef.subdir);
Value vInfo; Value vInfo;
// FIXME: don't evaluate vInfo. state.evalFile(realFlakeFile, vInfo, true); // FIXME: symlink attack
state.evalFile(realFlakeFile, vInfo); // FIXME: symlink attack
expectType(state, tAttrs, vInfo, Pos(state.symbols.create(realFlakeFile), 0, 0)); expectType(state, tAttrs, vInfo, Pos(state.symbols.create(realFlakeFile), 0, 0));

View file

@ -170,6 +170,11 @@ struct Value
{ {
return type == tList1 ? 1 : type == tList2 ? 2 : bigList.size; return type == tList1 ? 1 : type == tList2 ? 2 : bigList.size;
} }
/* Check whether forcing this value requires a trivial amount of
computation. In particular, function applications are
non-trivial. */
bool isTrivial() const;
}; };