eval: Don't expose partially constructed sets
This turns several instances of unsoundness into runtime infinite recursion errors Fixes https://github.com/NixOS/nix/issues/7012, https://github.com/NixOS/nix/issues/3241 Change-Id: Id8d352c5a78ef08d8913f07fe83f55c875684714
This commit is contained in:
@@ -158,13 +158,22 @@ void ExprSet::eval(EvalState & state, Env & env, Value & v)
|
||||
|
||||
/* Dynamic attrs apply *after* rec and __overrides. */
|
||||
for (auto & i : dynamicAttrs) {
|
||||
/* Before evaluating dynamic attrs, we blackhole the output attrset and only restore it after the operation.
|
||||
* This is to avoid exposing the partially constructed set as a value, see
|
||||
* http://github.com/NixOS/nix/issues/7012. Any accesses to the output attrset will thus infrec.
|
||||
*/
|
||||
Value vBackup = v;
|
||||
Value nameVal;
|
||||
i.nameExpr->eval(state, *dynamicEnv, nameVal);
|
||||
state.forceValue(nameVal, i.pos);
|
||||
if (nameVal.type() == nNull) {
|
||||
continue;
|
||||
{
|
||||
KJ_DEFER(v = vBackup);
|
||||
v = Value{NewValueAs::blackhole};
|
||||
i.nameExpr->eval(state, *dynamicEnv, nameVal);
|
||||
state.forceValue(nameVal, i.pos);
|
||||
if (nameVal.type() == nNull) {
|
||||
continue;
|
||||
}
|
||||
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
|
||||
}
|
||||
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
|
||||
auto nameSym = state.ctx.symbols.create(nameVal.str());
|
||||
auto j = v.attrs()->get(nameSym);
|
||||
if (j) {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
error:
|
||||
… while evaluating a
|
||||
at /pwd/in.nix:1:84:
|
||||
1| let b = builtins.toJSON a; a = { foo = "bar"; "${builtins.seq b "baz"}" = b; }; in a
|
||||
| ^
|
||||
2|
|
||||
|
||||
… while calling the 'seq' builtin
|
||||
at /pwd/in.nix:1:50:
|
||||
1| let b = builtins.toJSON a; a = { foo = "bar"; "${builtins.seq b "baz"}" = b; }; in a
|
||||
| ^
|
||||
2|
|
||||
|
||||
… while calling the 'toJSON' builtin
|
||||
at /pwd/in.nix:1:9:
|
||||
1| let b = builtins.toJSON a; a = { foo = "bar"; "${builtins.seq b "baz"}" = b; }; in a
|
||||
| ^
|
||||
2|
|
||||
|
||||
error: infinite recursion encountered
|
||||
@@ -0,0 +1,50 @@
|
||||
error:
|
||||
… from call site
|
||||
at /pwd/in.nix:4:11:
|
||||
3| b = { ${lenName a} = null; };
|
||||
4| a = { ${lenName b} = null; };
|
||||
| ^
|
||||
5|
|
||||
|
||||
… while calling 'lenName'
|
||||
at /pwd/in.nix:2:13:
|
||||
1| let
|
||||
2| lenName = x: toString (builtins.length (builtins.attrNames x));
|
||||
| ^
|
||||
3| b = { ${lenName a} = null; };
|
||||
|
||||
… while calling the 'toString' builtin
|
||||
at /pwd/in.nix:2:16:
|
||||
1| let
|
||||
2| lenName = x: toString (builtins.length (builtins.attrNames x));
|
||||
| ^
|
||||
3| b = { ${lenName a} = null; };
|
||||
|
||||
… while calling the 'length' builtin
|
||||
at /pwd/in.nix:2:26:
|
||||
1| let
|
||||
2| lenName = x: toString (builtins.length (builtins.attrNames x));
|
||||
| ^
|
||||
3| b = { ${lenName a} = null; };
|
||||
|
||||
… while evaluating the first argument passed to builtins.length
|
||||
|
||||
… while calling the 'attrNames' builtin
|
||||
at /pwd/in.nix:2:43:
|
||||
1| let
|
||||
2| lenName = x: toString (builtins.length (builtins.attrNames x));
|
||||
| ^
|
||||
3| b = { ${lenName a} = null; };
|
||||
|
||||
… while evaluating the argument passed to builtins.attrNames
|
||||
|
||||
… from call site
|
||||
at /pwd/in.nix:3:11:
|
||||
2| lenName = x: toString (builtins.length (builtins.attrNames x));
|
||||
3| b = { ${lenName a} = null; };
|
||||
| ^
|
||||
4| a = { ${lenName b} = null; };
|
||||
|
||||
(6 duplicate frames omitted)
|
||||
|
||||
error: infinite recursion encountered
|
||||
@@ -0,0 +1 @@
|
||||
let b = builtins.toJSON a; a = { foo = "bar"; "${builtins.seq b "baz"}" = b; }; in a
|
||||
@@ -0,0 +1,8 @@
|
||||
let
|
||||
lenName = x: toString (builtins.length (builtins.attrNames x));
|
||||
b = { ${lenName a} = null; };
|
||||
a = { ${lenName b} = null; };
|
||||
|
||||
in
|
||||
|
||||
[ a b ]
|
||||
Reference in New Issue
Block a user