libexpr: Check primop arity earlier

This commit is contained in:
Robert Hensing 2023-11-16 11:10:25 +01:00
parent ba3cb4a049
commit 0daccb1121
4 changed files with 33 additions and 10 deletions

View file

@ -723,6 +723,23 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info)
} }
void PrimOp::check()
{
if (arity > maxPrimOpArity) {
throw Error("primop arity must not exceed %1%", maxPrimOpArity);
}
}
void Value::mkPrimOp(PrimOp * p)
{
p->check();
clearValue();
internalType = tPrimOp;
primOp = p;
}
Value * EvalState::addPrimOp(PrimOp && primOp) Value * EvalState::addPrimOp(PrimOp && primOp)
{ {
/* Hack to make constants lazy: turn them into a application of /* Hack to make constants lazy: turn them into a application of
@ -1692,8 +1709,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* We have all the arguments, so call the primop with /* We have all the arguments, so call the primop with
the previous and new arguments. */ the previous and new arguments. */
assert(arity < 64); Value * vArgs[maxPrimOpArity];
Value * vArgs[64];
auto n = argsDone; auto n = argsDone;
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left) for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left)
vArgs[--n] = arg->primOpApp.right; vArgs[--n] = arg->primOpApp.right;

View file

@ -18,6 +18,12 @@
namespace nix { namespace nix {
/**
* We put a limit on primop arity because it lets us use a fixed size array on
* the stack. 16 is already an impractical number of arguments. Use an attrset
* argument for such overly complicated functions.
*/
constexpr size_t maxPrimOpArity = 64;
class Store; class Store;
class EvalState; class EvalState;
@ -71,6 +77,12 @@ struct PrimOp
* Optional experimental for this to be gated on. * Optional experimental for this to be gated on.
*/ */
std::optional<ExperimentalFeature> experimentalFeature; std::optional<ExperimentalFeature> experimentalFeature;
/**
* Validity check to be performed by functions that introduce primops,
* such as RegisterPrimOp() and Value::mkPrimOp().
*/
void check();
}; };
/** /**

View file

@ -114,7 +114,8 @@ TEST_F(ValuePrintingTests, vLambda)
TEST_F(ValuePrintingTests, vPrimOp) TEST_F(ValuePrintingTests, vPrimOp)
{ {
Value vPrimOp; Value vPrimOp;
vPrimOp.mkPrimOp(nullptr); PrimOp primOp{};
vPrimOp.mkPrimOp(&primOp);
test(vPrimOp, "<PRIMOP>"); test(vPrimOp, "<PRIMOP>");
} }

View file

@ -354,13 +354,7 @@ public:
// Value will be overridden anyways // Value will be overridden anyways
} }
inline void mkPrimOp(PrimOp * p) void mkPrimOp(PrimOp * p);
{
clearValue();
internalType = tPrimOp;
primOp = p;
}
inline void mkPrimOpApp(Value * l, Value * r) inline void mkPrimOpApp(Value * l, Value * r)
{ {