aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2019-09-09 16:34:44 +0200
committerEelco Dolstra <edolstra@gmail.com>2019-09-09 16:34:44 +0200
commitc87840ae14eea84b5910cb0b188ec3fb32cc1466 (patch)
treefbd5a0707c47b3a3d2a96771c1509cedc7afcbad
parent2fa7f2a56a5c2fe11c1a0daceee5cf0584b69be9 (diff)
Don't allow arbitrary computations in flake attributes
E.g. you can write 'edition = 201909' but not 'edition = 201909 + 0'. Fixes #3075.
-rw-r--r--src/libexpr/eval.cc23
-rw-r--r--src/libexpr/eval.hh1
-rw-r--r--src/libexpr/flake/flake.cc36
3 files changed, 46 insertions, 14 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index fa79b0d5e..25b50da7e 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -141,12 +141,12 @@ const Value *getPrimOp(const Value &v) {
}
-string showType(const Value & v)
+string showType(ValueType type)
{
- switch (v.type) {
+ switch (type) {
case tInt: return "an integer";
case tBool: return "a boolean";
- case tString: return v.string.context ? "a string with context" : "a string";
+ case tString: return "a string";
case tPath: return "a path";
case tNull: return "null";
case tAttrs: return "a set";
@@ -155,14 +155,27 @@ string showType(const Value & v)
case tApp: return "a function application";
case tLambda: return "a function";
case tBlackhole: return "a black hole";
+ case tPrimOp: return "a built-in function";
+ case tPrimOpApp: return "a partially applied built-in function";
+ case tExternal: return "an external value";
+ case tFloat: return "a float";
+ }
+ abort();
+}
+
+
+string showType(const Value & v)
+{
+ switch (v.type) {
+ case tString: return v.string.context ? "a string with context" : "a string";
case tPrimOp:
return fmt("the built-in function '%s'", string(v.primOp->name));
case tPrimOpApp:
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
case tExternal: return v.external->showType();
- case tFloat: return "a float";
+ default:
+ return showType(v.type);
}
- abort();
}
diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh
index 5e976f196..468a826ca 100644
--- a/src/libexpr/eval.hh
+++ b/src/libexpr/eval.hh
@@ -338,6 +338,7 @@ private:
/* Return a string representing the type of the value `v'. */
+string showType(ValueType type);
string showType(const Value & v);
/* Decode a context string ‘!<name>!<path>’ into a pair <path,
diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc
index 9e25fc116..c10906731 100644
--- a/src/libexpr/flake/flake.cc
+++ b/src/libexpr/flake/flake.cc
@@ -195,6 +195,18 @@ static SourceInfo fetchFlake(EvalState & state, const FlakeRef & resolvedRef)
else abort();
}
+static void expectType(EvalState & state, ValueType type,
+ Value & value, const Pos & pos)
+{
+ if (value.type == tThunk &&
+ ((type == tAttrs && dynamic_cast<ExprAttrs *>(value.thunk.expr)) ||
+ (type == tLambda && dynamic_cast<ExprLambda *>(value.thunk.expr))))
+ state.forceValue(value, pos);
+ if (value.type != type)
+ throw Error("expected %s but got %s at %s",
+ showType(type), showType(value.type), pos);
+}
+
Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
{
SourceInfo sourceInfo = fetchFlake(state, flakeRef);
@@ -219,9 +231,10 @@ 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);
Value vInfo;
+ // FIXME: don't evaluate vInfo.
state.evalFile(realFlakeFile, vInfo); // FIXME: symlink attack
- state.forceAttrs(vInfo);
+ expectType(state, tAttrs, vInfo, Pos(state.symbols.create(realFlakeFile), 0, 0));
auto sEdition = state.symbols.create("edition");
auto sEpoch = state.symbols.create("epoch"); // FIXME: remove soon
@@ -231,7 +244,8 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
edition = vInfo.attrs->get(sEpoch);
if (edition) {
- flake.edition = state.forceInt(*(**edition).value, *(**edition).pos);
+ expectType(state, tInt, *(**edition).value, *(**edition).pos);
+ flake.edition = (**edition).value->integer;
if (flake.edition > 201909)
throw Error("flake '%s' requires unsupported edition %d; please upgrade Nix", flakeRef, flake.edition);
if (flake.edition < 201909)
@@ -239,26 +253,30 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
} else
throw Error("flake '%s' lacks attribute 'edition'", flakeRef);
- if (auto description = vInfo.attrs->get(state.sDescription))
- flake.description = state.forceStringNoCtx(*(**description).value, *(**description).pos);
+ if (auto description = vInfo.attrs->get(state.sDescription)) {
+ expectType(state, tString, *(**description).value, *(**description).pos);
+ flake.description = (**description).value->string.s;
+ }
auto sInputs = state.symbols.create("inputs");
auto sUri = state.symbols.create("uri");
auto sFlake = state.symbols.create("flake");
if (std::optional<Attr *> inputs = vInfo.attrs->get(sInputs)) {
- state.forceAttrs(*(**inputs).value, *(**inputs).pos);
+ expectType(state, tAttrs, *(**inputs).value, *(**inputs).pos);
for (Attr inputAttr : *(*(**inputs).value).attrs) {
- state.forceAttrs(*inputAttr.value, *inputAttr.pos);
+ expectType(state, tAttrs, *inputAttr.value, *inputAttr.pos);
FlakeInput input(FlakeRef(inputAttr.name));
for (Attr attr : *(inputAttr.value->attrs)) {
if (attr.name == sUri) {
- input.ref = state.forceStringNoCtx(*attr.value, *attr.pos);
+ expectType(state, tString, *attr.value, *attr.pos);
+ input.ref = std::string(attr.value->string.s);
} else if (attr.name == sFlake) {
- input.isFlake = state.forceBool(*attr.value, *attr.pos);
+ expectType(state, tBool, *attr.value, *attr.pos);
+ input.isFlake = attr.value->boolean;
} else
throw Error("flake input '%s' has an unsupported attribute '%s', at %s",
inputAttr.name, attr.name, *attr.pos);
@@ -271,7 +289,7 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
auto sOutputs = state.symbols.create("outputs");
if (auto outputs = vInfo.attrs->get(sOutputs)) {
- state.forceFunction(*(**outputs).value, *(**outputs).pos);
+ expectType(state, tLambda, *(**outputs).value, *(**outputs).pos);
flake.vOutputs = (**outputs).value;
if (flake.vOutputs->lambda.fun->matchAttrs) {