Skip to content

Commit af2ddfd

Browse files
committed
libexpr: Fix use-after-free of StaticEnv::up
It's not very clear what the ownership model is here, but one thing is certain: `.up` can't be destroyed before the StaticEnv that refers to it is. Changing a non-owning pointer to taking shared ownership of the parent `StaticEnv` prevents the `.up` from being freed. I'm not a huge fan of the inverted ownership, where child `StaticEnv` takes a refcount of the parent, but this seems like the least intrusive way to fix the use-after-free. This shouldn't cause any shared_ptr cycles to appear (hopefully).
1 parent 61f49de commit af2ddfd

File tree

4 files changed

+16
-13
lines changed

4 files changed

+16
-13
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()) {

0 commit comments

Comments
 (0)