1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-08 19:46:02 +01:00

Merge pull request #14439 from NixOS/no-buffer-overflows

libexpr: Do not overflow heap buffer when there are too many formal a…
This commit is contained in:
John Ericson 2025-11-01 14:48:11 +00:00 committed by GitHub
commit 6fa7510055
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 40 additions and 8 deletions

View file

@ -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<const char *>
class AttrSetMergeTrvialExpressionTest : public TrivialExpressionTest,
public ::testing::WithParamInterface<const char *>
{};
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<uint16_t>::max(); ++i) {
expr += fmt("arg%d, ", i);
}
expr += " }: 0 in; f {}";
ASSERT_THAT(
[&]() { eval(expr); },
::testing::ThrowsMessage<Error>(::nix::testing::HasSubstrIgnoreANSIMatcher(
"too many formal arguments, implementation supports at most 65535")));
}
} /* namespace nix */

View file

@ -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<char> & alloc,
PosIdx pos,
Symbol arg,
@ -548,7 +551,15 @@ public:
, formalsStart(alloc.allocate_object<Formal>(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<decltype(nFormals)>::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<char> & alloc, PosIdx pos, FormalsBuilder formals, Expr * body)
: ExprLambda(alloc, pos, Symbol(), formals, body) {};
ExprLambda(
const PosTable & positions,
std::pmr::polymorphic_allocator<char> & 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;

View file

@ -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);
}