diff --git a/src/libexpr-tests/trivial.cc b/src/libexpr-tests/trivial.cc index a287ce4d1..d112c269a 100644 --- a/src/libexpr-tests/trivial.cc +++ b/src/libexpr-tests/trivial.cc @@ -1,4 +1,5 @@ #include "nix/expr/tests/libexpr.hh" +#include "nix/util/tests/gmock-matchers.hh" namespace nix { // Testing of trivial expressions @@ -160,7 +161,8 @@ TEST_F(TrivialExpressionTest, assertPassed) ASSERT_THAT(v, IsIntEq(123)); } -class AttrSetMergeTrvialExpressionTest : public TrivialExpressionTest, public testing::WithParamInterface +class AttrSetMergeTrvialExpressionTest : public TrivialExpressionTest, + public ::testing::WithParamInterface {}; TEST_P(AttrSetMergeTrvialExpressionTest, attrsetMergeLazy) @@ -196,7 +198,7 @@ TEST_P(AttrSetMergeTrvialExpressionTest, attrsetMergeLazy) INSTANTIATE_TEST_SUITE_P( attrsetMergeLazy, AttrSetMergeTrvialExpressionTest, - testing::Values("{ a.b = 1; a.c = 2; }", "{ a = { b = 1; }; a = { c = 2; }; }")); + ::testing::Values("{ a.b = 1; a.c = 2; }", "{ a = { b = 1; }; a = { c = 2; }; }")); // The following macros ultimately define 48 tests (16 variations on three // templates). Each template tests an expression that can be written in 2^4 @@ -339,4 +341,18 @@ TEST_F(TrivialExpressionTest, orCantBeUsed) { ASSERT_THROW(eval("let or = 1; in or"), Error); } + +TEST_F(TrivialExpressionTest, tooManyFormals) +{ + std::string expr = "let f = { "; + for (uint32_t i = 0; i <= std::numeric_limits::max(); ++i) { + expr += fmt("arg%d, ", i); + } + expr += " }: 0 in; f {}"; + ASSERT_THAT( + [&]() { eval(expr); }, + ::testing::ThrowsMessage(::nix::testing::HasSubstrIgnoreANSIMatcher( + "too many formal arguments, implementation supports at most 65535"))); +} + } /* namespace nix */ diff --git a/src/libexpr/include/nix/expr/nixexpr.hh b/src/libexpr/include/nix/expr/nixexpr.hh index 4962c7857..fd7ed2a67 100644 --- a/src/libexpr/include/nix/expr/nixexpr.hh +++ b/src/libexpr/include/nix/expr/nixexpr.hh @@ -13,6 +13,8 @@ #include "nix/expr/eval-error.hh" #include "nix/util/pos-idx.hh" #include "nix/expr/counter.hh" +#include "nix/util/pos-table.hh" +#include "nix/util/error.hh" namespace nix { @@ -535,6 +537,7 @@ public: DocComment docComment; ExprLambda( + const PosTable & positions, std::pmr::polymorphic_allocator & alloc, PosIdx pos, Symbol arg, @@ -548,7 +551,15 @@ public: , formalsStart(alloc.allocate_object(nFormals)) , body(body) { - std::ranges::copy(formals.formals, formalsStart); + if (formals.formals.size() > nFormals) [[unlikely]] { + auto err = Error( + "too many formal arguments, implementation supports at most %1%", + std::numeric_limits::max()); + if (pos) + err.atPos(positions[pos]); + throw err; + } + std::uninitialized_copy_n(formals.formals.begin(), nFormals, formalsStart); }; ExprLambda(PosIdx pos, Symbol arg, Expr * body) @@ -560,8 +571,13 @@ public: , formalsStart(nullptr) , body(body) {}; - ExprLambda(std::pmr::polymorphic_allocator & alloc, PosIdx pos, FormalsBuilder formals, Expr * body) - : ExprLambda(alloc, pos, Symbol(), formals, body) {}; + ExprLambda( + const PosTable & positions, + std::pmr::polymorphic_allocator & alloc, + PosIdx pos, + FormalsBuilder formals, + Expr * body) + : ExprLambda(positions, alloc, pos, Symbol(), formals, body) {}; void setName(Symbol name) override; std::string showNamePos(const EvalState & state) const; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 02fc4c3b6..29586ed98 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -186,7 +186,7 @@ expr_function | formal_set ':' expr_function[body] { state->validateFormals($formal_set); - auto me = new ExprLambda(state->alloc, CUR_POS, std::move($formal_set), $body); + auto me = new ExprLambda(state->positions, state->alloc, CUR_POS, std::move($formal_set), $body); $$ = me; SET_DOC_POS(me, @1); } @@ -194,7 +194,7 @@ expr_function { auto arg = state->symbols.create($ID); state->validateFormals($formal_set, CUR_POS, arg); - auto me = new ExprLambda(state->alloc, CUR_POS, arg, std::move($formal_set), $body); + auto me = new ExprLambda(state->positions, state->alloc, CUR_POS, arg, std::move($formal_set), $body); $$ = me; SET_DOC_POS(me, @1); } @@ -202,7 +202,7 @@ expr_function { auto arg = state->symbols.create($ID); state->validateFormals($formal_set, CUR_POS, arg); - auto me = new ExprLambda(state->alloc, CUR_POS, arg, std::move($formal_set), $body); + auto me = new ExprLambda(state->positions, state->alloc, CUR_POS, arg, std::move($formal_set), $body); $$ = me; SET_DOC_POS(me, @1); }