Skip to content

Conversation

@roberth
Copy link
Member

@roberth roberth commented Nov 22, 2025

Motivation

I realized #14613 was probably not complete, so I ran an analysis with a script that finds call graphs.
That produced the set of functions, see Result below. Call graph cycles involving virtual method calls were not supported, but probably not a big deal.

Context

Result

Fixed in Previous PR

Function Commit
EvalState::forceValueDeep (deepSeq) 59a566d
(list index in deepSeq traces) a812b6c
printValueAsJSON (toJSON) c7e1c61

Fixed in This PR

Function Commit
getDerivations 5166cee
Printer::print / printList / printAttrs 03ca960
printValueAsXML 3965b68
EvalState::eqValues / assertEqValues 075242b
EvalState::coerceToString 4167686
PackageInfo::checkMeta c2d2a0f
printAmbiguous fd1ecfb

Already Protected (no changes needed)

Function Reason
EvalState::callFunction Has addCallDepth (original protection)
EvalState::getDoc Has addCallDepth (added August 2024)
EvalState::coerceToPath Recurses only after callFunction (transitively protected)
EvalState::autoCallFunction Recurses only after callFunction (transitively protected)
eval_cache::AttrCursor::getAttrPath Depth bounded by evaluation
eval_cache::AttrCursor::getKey Depth bounded by evaluation
eval_cache::AttrCursor::getValue Depth bounded by evaluation

Deferred

Function Reason
ParserState::addAttr Parser-time recursion; would require invasive changes to bison grammar/semantic actions to track nesting depth, if that even works. Parser itself currently masks addAttr recursion.

Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

…ures

nix-instantiate on deeply nested structures with recurseForDerivations
(e.g., `let x = { recurseForDerivations = true; more = x; }; in x`)
caused an uncontrolled OS-level stack overflow with no Nix stack trace.

Fix by adding call depth tracking to getDerivations, integrating with
Nix's existing max-call-depth mechanism. Now produces a controlled
"stack overflow; max-call-depth exceeded" error with a proper stack
trace.
This way it can be handled reliably.
…ures

Non-cyclic structures can be infinitely deep when values are lazily
produced (e.g., `let f = n: { inner = f (n + 1); }; in f 0`). Since f
returns immediately with a thunk, Nix call depth stays at 1, but
Printer::print recurses on the C++ stack when printing.

We check print depth against max-call-depth rather than incrementing
the callDepth counter, because accessing an attribute is not a call.

StackOverflowError is always re-thrown because stack overflow is a
serious condition that expressions should avoid, unlike say `throw`,
which can be part of legitimate expression patterns.
…ures

printAmbiguous (used by nix-instantiate --eval and nix-env) had a depth
parameter, but all callers passed INT_MAX, effectively disabling the
limit. The function relied on the C++ stack to eventually overflow,
which could cause uncontrolled SIGSEGV crashes on deeply nested
pre-forced structures.

Now printAmbiguous checks depth against max-call-depth (default 10000)
and throws StackOverflowError with a proper trace, consistent with
other recursive value traversal functions.

The function signature is updated to take EvalState& to access the
settings and throw proper errors. The depth parameter now counts up
from 0 instead of down from INT_MAX.
@roberth roberth requested a review from edolstra as a code owner November 22, 2025 23:20
@github-actions github-actions bot added new-cli Relating to the "nix" command with-tests Issues related to testing. PRs with tests have some priority labels Nov 22, 2025
@Ericson2314 Ericson2314 added this pull request to the merge queue Nov 23, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Nov 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new-cli Relating to the "nix" command with-tests Issues related to testing. PRs with tests have some priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants