Skip to content

Commit 1293388

Browse files
authored
Merge pull request #12544 from xokdvium/debugger-use-after-free
libexpr: Fix use-after-free of StaticEnv::up
2 parents b628adc + 0d50045 commit 1293388

File tree

6 files changed

+46
-14
lines changed

6 files changed

+46
-14
lines changed

src/libcmd/repl.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ NixRepl::NixRepl(const LookupPath & lookupPath, nix::ref<Store> store, ref<EvalS
129129
: AbstractNixRepl(state)
130130
, debugTraceIndex(0)
131131
, getValues(getValues)
132-
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv.get()))
132+
, staticEnv(new StaticEnv(nullptr, state->staticBaseEnv))
133133
, runNixPtr{runNix}
134134
, interacter(make_unique<ReadlineLikeInteracter>(getDataDir() + "/repl-history"))
135135
{

src/libexpr/nixexpr.cc

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
310310
const StaticEnv * curEnv;
311311
Level level;
312312
int withLevel = -1;
313-
for (curEnv = env.get(), level = 0; curEnv; curEnv = curEnv->up, level++) {
313+
for (curEnv = env.get(), level = 0; curEnv; curEnv = curEnv->up.get(), level++) {
314314
if (curEnv->isWith) {
315315
if (withLevel == -1) withLevel = level;
316316
} else {
@@ -331,7 +331,7 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
331331
"undefined variable '%1%'",
332332
es.symbols[name]
333333
).atPos(pos).debugThrow();
334-
for (auto * e = env.get(); e && !fromWith; e = e->up)
334+
for (auto * e = env.get(); e && !fromWith; e = e->up.get())
335335
fromWith = e->isWith;
336336
this->level = withLevel;
337337
}
@@ -379,7 +379,7 @@ std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources(
379379
// and displacement, and nothing else is allowed to access it. ideally we'd
380380
// not even *have* an expr that grabs anything from this env since it's fully
381381
// invisible, but the evaluator does not allow for this yet.
382-
auto inner = std::make_shared<StaticEnv>(nullptr, env.get(), 0);
382+
auto inner = std::make_shared<StaticEnv>(nullptr, env, 0);
383383
for (auto from : *inheritFromExprs)
384384
from->bindVars(es, env);
385385

@@ -393,7 +393,7 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
393393

394394
if (recursive) {
395395
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
396-
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs.size());
396+
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs.size());
397397

398398
Displacement displ = 0;
399399
for (auto & i : attrs)
@@ -440,7 +440,7 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
440440
es.exprEnvs.insert(std::make_pair(this, env));
441441

442442
auto newEnv = std::make_shared<StaticEnv>(
443-
nullptr, env.get(),
443+
nullptr, env,
444444
(hasFormals() ? formals->formals.size() : 0) +
445445
(!arg ? 0 : 1));
446446

@@ -474,7 +474,7 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
474474
void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
475475
{
476476
auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> {
477-
auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size());
477+
auto newEnv = std::make_shared<StaticEnv>(nullptr, env, attrs->attrs.size());
478478

479479
Displacement displ = 0;
480480
for (auto & i : attrs->attrs)
@@ -500,7 +500,7 @@ void ExprWith::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
500500
es.exprEnvs.insert(std::make_pair(this, env));
501501

502502
parentWith = nullptr;
503-
for (auto * e = env.get(); e && !parentWith; e = e->up)
503+
for (auto * e = env.get(); e && !parentWith; e = e->up.get())
504504
parentWith = e->isWith;
505505

506506
/* Does this `with' have an enclosing `with'? If so, record its
@@ -509,14 +509,14 @@ void ExprWith::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> &
509509
const StaticEnv * curEnv;
510510
Level level;
511511
prevWith = 0;
512-
for (curEnv = env.get(), level = 1; curEnv; curEnv = curEnv->up, level++)
512+
for (curEnv = env.get(), level = 1; curEnv; curEnv = curEnv->up.get(), level++)
513513
if (curEnv->isWith) {
514514
prevWith = level;
515515
break;
516516
}
517517

518518
attrs->bindVars(es, env);
519-
auto newEnv = std::make_shared<StaticEnv>(this, env.get());
519+
auto newEnv = std::make_shared<StaticEnv>(this, env);
520520
body->bindVars(es, newEnv);
521521
}
522522

src/libexpr/nixexpr.hh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,13 +480,16 @@ extern ExprBlackHole eBlackHole;
480480
struct StaticEnv
481481
{
482482
ExprWith * isWith;
483-
const StaticEnv * up;
483+
std::shared_ptr<const StaticEnv> up;
484484

485485
// Note: these must be in sorted order.
486486
typedef std::vector<std::pair<Symbol, Displacement>> Vars;
487487
Vars vars;
488488

489-
StaticEnv(ExprWith * isWith, const StaticEnv * up, size_t expectedSize = 0) : isWith(isWith), up(up) {
489+
StaticEnv(ExprWith * isWith, std::shared_ptr<const StaticEnv> up, size_t expectedSize = 0)
490+
: isWith(isWith)
491+
, up(std::move(up))
492+
{
490493
vars.reserve(expectedSize);
491494
};
492495

src/libexpr/primops.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ static void scopedImport(EvalState & state, const PosIdx pos, SourcePath & path,
238238
Env * env = &state.allocEnv(vScope->attrs()->size());
239239
env->up = &state.baseEnv;
240240

241-
auto staticEnv = std::make_shared<StaticEnv>(nullptr, state.staticBaseEnv.get(), vScope->attrs()->size());
241+
auto staticEnv = std::make_shared<StaticEnv>(nullptr, state.staticBaseEnv, vScope->attrs()->size());
242242

243243
unsigned int displ = 0;
244244
for (auto & attr : *vScope->attrs()) {

tests/functional/flakes/debugger.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env bash
2+
3+
source ./common.sh
4+
5+
requireGit
6+
7+
flakeDir="$TEST_ROOT/flake"
8+
createGitRepo "$flakeDir"
9+
10+
cat >"$flakeDir/flake.nix" <<EOF
11+
{
12+
inputs = {
13+
};
14+
15+
outputs =
16+
_:
17+
let
18+
in
19+
{
20+
packages.$system.default = throw "oh no";
21+
};
22+
}
23+
EOF
24+
25+
git -C "$flakeDir" add flake.nix
26+
27+
# regression #12527 and #11286
28+
echo ":env" | expect 1 nix eval "$flakeDir#packages.${system}.default" --debugger

tests/functional/flakes/meson.build

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ suites += {
2828
'commit-lock-file-summary.sh',
2929
'non-flake-inputs.sh',
3030
'relative-paths.sh',
31-
'symlink-paths.sh'
31+
'symlink-paths.sh',
32+
'debugger.sh'
3233
],
3334
'workdir': meson.current_source_dir(),
3435
}

0 commit comments

Comments
 (0)