Don't allow arbitrary computations in flake attributes

E.g. you can write 'edition = 201909' but not 'edition = 201909 + 0'.

Fixes #3075.
This commit is contained in:
Eelco Dolstra 2019-09-09 16:34:44 +02:00
parent 2fa7f2a56a
commit c87840ae14
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
3 changed files with 46 additions and 14 deletions

View file

@ -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 tInt: return "an integer";
case tBool: return "a boolean"; 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 tPath: return "a path";
case tNull: return "null"; case tNull: return "null";
case tAttrs: return "a set"; case tAttrs: return "a set";
@ -155,14 +155,27 @@ string showType(const Value & v)
case tApp: return "a function application"; case tApp: return "a function application";
case tLambda: return "a function"; case tLambda: return "a function";
case tBlackhole: return "a black hole"; 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: case tPrimOp:
return fmt("the built-in function '%s'", string(v.primOp->name)); return fmt("the built-in function '%s'", string(v.primOp->name));
case tPrimOpApp: case tPrimOpApp:
return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name)); return fmt("the partially applied built-in function '%s'", string(getPrimOp(v)->primOp->name));
case tExternal: return v.external->showType(); case tExternal: return v.external->showType();
case tFloat: return "a float"; default:
return showType(v.type);
} }
abort();
} }

View file

@ -338,6 +338,7 @@ private:
/* Return a string representing the type of the value `v'. */ /* Return a string representing the type of the value `v'. */
string showType(ValueType type);
string showType(const Value & v); string showType(const Value & v);
/* Decode a context string !<name>!<path> into a pair <path, /* Decode a context string !<name>!<path> into a pair <path,

View file

@ -195,6 +195,18 @@ static SourceInfo fetchFlake(EvalState & state, const FlakeRef & resolvedRef)
else abort(); 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) Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
{ {
SourceInfo sourceInfo = fetchFlake(state, 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); 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); // FIXME: symlink attack 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 sEdition = state.symbols.create("edition");
auto sEpoch = state.symbols.create("epoch"); // FIXME: remove soon 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); edition = vInfo.attrs->get(sEpoch);
if (edition) { 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) if (flake.edition > 201909)
throw Error("flake '%s' requires unsupported edition %d; please upgrade Nix", flakeRef, flake.edition); throw Error("flake '%s' requires unsupported edition %d; please upgrade Nix", flakeRef, flake.edition);
if (flake.edition < 201909) if (flake.edition < 201909)
@ -239,26 +253,30 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
} else } else
throw Error("flake '%s' lacks attribute 'edition'", flakeRef); throw Error("flake '%s' lacks attribute 'edition'", flakeRef);
if (auto description = vInfo.attrs->get(state.sDescription)) if (auto description = vInfo.attrs->get(state.sDescription)) {
flake.description = state.forceStringNoCtx(*(**description).value, *(**description).pos); expectType(state, tString, *(**description).value, *(**description).pos);
flake.description = (**description).value->string.s;
}
auto sInputs = state.symbols.create("inputs"); auto sInputs = state.symbols.create("inputs");
auto sUri = state.symbols.create("uri"); auto sUri = state.symbols.create("uri");
auto sFlake = state.symbols.create("flake"); auto sFlake = state.symbols.create("flake");
if (std::optional<Attr *> inputs = vInfo.attrs->get(sInputs)) { 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) { for (Attr inputAttr : *(*(**inputs).value).attrs) {
state.forceAttrs(*inputAttr.value, *inputAttr.pos); expectType(state, tAttrs, *inputAttr.value, *inputAttr.pos);
FlakeInput input(FlakeRef(inputAttr.name)); FlakeInput input(FlakeRef(inputAttr.name));
for (Attr attr : *(inputAttr.value->attrs)) { for (Attr attr : *(inputAttr.value->attrs)) {
if (attr.name == sUri) { 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) { } 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 } else
throw Error("flake input '%s' has an unsupported attribute '%s', at %s", throw Error("flake input '%s' has an unsupported attribute '%s', at %s",
inputAttr.name, attr.name, *attr.pos); inputAttr.name, attr.name, *attr.pos);
@ -271,7 +289,7 @@ Flake getFlake(EvalState & state, const FlakeRef & flakeRef)
auto sOutputs = state.symbols.create("outputs"); auto sOutputs = state.symbols.create("outputs");
if (auto outputs = vInfo.attrs->get(sOutputs)) { if (auto outputs = vInfo.attrs->get(sOutputs)) {
state.forceFunction(*(**outputs).value, *(**outputs).pos); expectType(state, tLambda, *(**outputs).value, *(**outputs).pos);
flake.vOutputs = (**outputs).value; flake.vOutputs = (**outputs).value;
if (flake.vOutputs->lambda.fun->matchAttrs) { if (flake.vOutputs->lambda.fun->matchAttrs) {