Conversation
A self-referential list or attrset reaching a derivation argument (let r = [ r ]; ... attr = r) recursed natively through the coercion until the fiber stack faulted: SIGSEGV on eval, where Nix reports 'stack overflow; max-call-depth exceeded'. Mirror Nix's mechanism: EvalState::coerceToString counts each coercion level against the same max-call-depth budget as function calls, so the coercion here checks policy.max_call_depth (the max-call-depth setting, default 10000 as in Nix) once per level and raises the shared error. The forceThunkImpl native-stack soft guard backstops the recursion the same way it backstops deep thunk forcing, so an undersized stack degrades to a graceful error rather than a fault. Nix-parity checked across the boundary: nesting depths 5000 and 9000 evaluate to identical drvPaths on both evaluators, 11000 errors on both, and the cycle errors identically. Unit tests cover deep-finite, cyclic list, and cyclic-outPath vectors; the test binary segfaults on the parent commit.
psyclyx
added a commit
that referenced
this pull request
Aug 26, 2026
A self-referential value reached through string coercion recursed on the
native stack until the fiber faulted: 'let r = { outPath = r; }; in
toString r' was a SIGSEGV, as were the __toString and nested-list shapes
and the same values passed to builtins.path.
Count each nested coercion level against max-call-depth, as Nix's
EvalState::coerceToString does via addCallDepth, with the same native
stack backstop forceThunkImpl uses so an undersized fiber stack degrades
to an error instead of a fault. The depth is fiber state alongside
tryeval_depth, so a nested import VM continues its caller's coercion and
every clone of the walk shares one budget: the non-copying coercion in
vm/strings.zig and the toString attrs and list walks.
Cyclic values now report 'stack overflow; max-call-depth exceeded' and
finite chains are unchanged, byte-identical to the reference evaluator.
The derivation-specific coercion clones still recurse unguarded; PR #8
covers those and can adopt coercionEnter/coercionExit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A self-referential list or attrset reaching a derivation argument recurses natively through the string coercion until the fiber stack faults: SIGSEGV, where Nix reports
stack overflow; max-call-depth exceeded.Fix
Mirror Nix's mechanism:
EvalState::coerceToStringcounts each coercion level against the samemax-call-depthbudget as function calls. The coercion here checkspolicy.max_call_depth(themax-call-depthsetting, default 10000 as in Nix) once per level and raises the shared error. TheforceThunkImplnative-stack soft guard backstops the recursion the same way it backstops deep thunk forcing, so an undersized stack degrades to a graceful error rather than a fault.Verification
nix-instantiate: nesting depths 5000 and 9000 evaluate to identical drvPaths on both evaluators, 11000 errors on both, and the cycle errors identically.outPathvectors; the test binary segfaults on the parent commit.