fix(libexpr/eval-inline): get rid of references to nullptr env

When diagnosing infinite recursion references to nullptr `Env` can be formed.
This happens only with `ExprBlackHole` is evaluated, which always leads to
`InfiniteRecursionError`.

UBSAN log for one such case:

```
../src/libexpr/eval-inline.hh:94:31: runtime error: reference binding to null pointer of type 'Env'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../src/libexpr/eval-inline.hh:94:31 in
```
This commit is contained in:
Sergei Zimmerman 2024-11-14 11:03:58 +03:00
parent 32becc87fe
commit 1800853b2a
3 changed files with 11 additions and 3 deletions

View File

@ -87,11 +87,15 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
{
if (v.isThunk()) {
Env * env = v.payload.thunk.env;
assert(env || v.isBlackhole());
Expr * expr = v.payload.thunk.expr;
try {
v.mkBlackhole();
//checkInterrupt();
expr->eval(*this, *env, v);
if (env) [[likely]]
expr->eval(*this, *env, v);
else
ExprBlackHole::throwInfiniteRecursionError(*this, v);
} catch (...) {
v.mkThunk(env, expr);
tryFixupBlackHolePos(v, pos);

View File

@ -2052,9 +2052,12 @@ void ExprPos::eval(EvalState & state, Env & env, Value & v)
state.mkPos(v, pos);
}
void ExprBlackHole::eval(EvalState & state, Env & env, Value & v)
void ExprBlackHole::eval(EvalState & state, [[maybe_unused]] Env & env, Value & v)
{
throwInfiniteRecursionError(state, v);
}
[[gnu::noinline]] [[noreturn]] void ExprBlackHole::throwInfiniteRecursionError(EvalState & state, Value &v) {
state.error<InfiniteRecursionError>("infinite recursion encountered")
.atPos(v.determinePos(noPos))
.debugThrow();

View File

@ -468,6 +468,7 @@ struct ExprBlackHole : Expr
void show(const SymbolTable & symbols, std::ostream & str) const override {}
void eval(EvalState & state, Env & env, Value & v) override;
void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) override {}
[[noreturn]] static void throwInfiniteRecursionError(EvalState & state, Value & v);
};
extern ExprBlackHole eBlackHole;