1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-24 11:19:35 +01:00

libexpr: fix stack overflow in deepSeq on deeply nested structures

builtins.deepSeq on deeply nested structures (e.g., a linked list with
100,000 elements) caused an uncontrolled OS-level stack overflow with
no Nix stack trace.

Fix by adding call depth tracking to forceValueDeep, 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.

Closes: https://github.com/NixOS/nix/issues/7816
This commit is contained in:
Robert Hensing 2025-11-21 23:35:13 +01:00
parent 152e7e48c1
commit 59a566db13
3 changed files with 42 additions and 0 deletions

View file

@ -2188,6 +2188,8 @@ void EvalState::forceValueDeep(Value & v)
std::set<const Value *> seen; std::set<const Value *> seen;
[&, &state(*this)](this const auto & recurse, Value & v) { [&, &state(*this)](this const auto & recurse, Value & v) {
auto _level = state.addCallDepth(v.determinePos(noPos));
if (!seen.insert(&v).second) if (!seen.insert(&v).second)
return; return;

View file

@ -0,0 +1,30 @@
error:
… while calling the 'deepSeq' builtin
at /pwd/lang/eval-fail-deepseq-stack-overflow.nix:8:1:
7| in
8| builtins.deepSeq reverseLinkedList (
| ^
9| throw "unexpected success; expected a controlled stack overflow instead"
… while evaluating the attribute 'tail'
at /pwd/lang/eval-fail-deepseq-stack-overflow.nix:6:67:
5| long = builtins.genList (x: x) 100000;
6| reverseLinkedList = builtins.foldl' (tail: head: { inherit head tail; }) null long;
| ^
7| in
(9997 duplicate frames omitted)
… while evaluating the attribute 'head'
at /pwd/lang/eval-fail-deepseq-stack-overflow.nix:6:62:
5| long = builtins.genList (x: x) 100000;
6| reverseLinkedList = builtins.foldl' (tail: head: { inherit head tail; }) null long;
| ^
7| in
error: stack overflow; max-call-depth exceeded
at /pwd/lang/eval-fail-deepseq-stack-overflow.nix:5:28:
4| let
5| long = builtins.genList (x: x) 100000;
| ^
6| reverseLinkedList = builtins.foldl' (tail: head: { inherit head tail; }) null long;

View file

@ -0,0 +1,10 @@
# Test that deepSeq on a deeply nested structure produces a controlled
# stack overflow error rather than a segfault.
let
long = builtins.genList (x: x) 100000;
reverseLinkedList = builtins.foldl' (tail: head: { inherit head tail; }) null long;
in
builtins.deepSeq reverseLinkedList (
throw "unexpected success; expected a controlled stack overflow instead"
)