Skip to content

fix(proto): prevent logical plan serialization stack overflow (#24124) - #31

Merged
askalt merged 2 commits into
release-52.3.0from
askalt/fix-serialization-stack-overflow
Sep 29, 2026
Merged

askalt merged 2 commits into
release-52.3.0from
askalt/fix-serialization-stack-overflow

Conversation

@askalt

@askalt askalt commented Sep 28, 2026 •

Copy link
Copy Markdown
  • Cherry-pick the fix of the logical plan encoding stack overflow error.
  • Apply the same logic for the decoding path.

…#24124)

- Closes apache#23823.

`LogicalPlanNode::try_from_logical_plan` recursively serializes logical
plans
to protobuf. In debug builds, its large `match` compiled to a 202,304 B
stack
frame, so ten nested `SubqueryAlias` nodes over an `EmptyRelation`
overflowed
a 2 MiB thread stack.

Each arm was isolated independently against the original dispatcher:

| Independent frame effect | Arms | Examples |
| --- | ---: | --- |
| >=10 KiB reduction | 4 | TableScan: 34,608 B; Join: 11,392 B; Dml /
RecursiveQuery: 10,320 B |
| 5-8 KiB reduction | 17 | Projection, Filter, Aggregate, Repartition,
Unnest, Copy |
| 2-4 KiB reduction | 7 | Values, EmptyRelation, Union, Extension |
| <=64 B effect | 9 | Several DDL/statement arms; Subquery adds 16 B |

These effects are not additive: each change alters the compiler's layout
of
the same `match` frame. Isolating the 15 arms with the largest
independent
reductions still left a 58,560 B frame; isolating all 37 arms reduced it
to
1,680 B.

- Isolate every `try_from_logical_plan` match arm behind a debug-only
non-inlined helper, preventing arm-local temporaries from inflating the
  recursive dispatcher frame.
- Add the opt-in `datafusion-proto/recursive_protection` feature using
the
  existing DataFusion recursion pattern.
- Add child-process stack-safety regressions for the original 2
MiB-stack
  reproducer and feature-gated stack growth.

With `recursive_protection`, the dispatcher frame measures 1,648 B. The
helper is only forced out of line in debug builds.

Yes.

- `cargo fmt --all --check`
- `cargo check -p datafusion-proto --all-features`
- `cargo test -p datafusion-proto --lib`
- Stack-safety regression: 100 nested aliases on a 2 MiB stack.
- Feature-gated stack-growth regression: 2,000 nested aliases with
  `recursive_protection`.

`datafusion-proto` gains an opt-in `recursive_protection` feature.
Existing
protobuf wire format, conversion behavior, and default features are
unchanged.
@github-actions github-actions Bot added the proto label Sep 28, 2026
Apply the same approach as applied for the encoding part.
@askalt
askalt force-pushed the askalt/fix-serialization-stack-overflow branch from c2be1f7 to 6f9e921 Compare September 28, 2026 14:08
@askalt
askalt requested a review from LLDay September 28, 2026 16:00
@askalt

askalt commented Sep 28, 2026

Copy link
Copy Markdown
Author

Upstream PR (the decode part fix): apache#25831

@LLDay LLDay left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@askalt
askalt merged commit db25f4e into release-52.3.0 Sep 29, 2026
49 of 58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants