Regression-test deep-nesting reply decode (depth-safe, no abort) - #147
Open
aterga wants to merge 2 commits into
Open
Regression-test deep-nesting reply decode (depth-safe, no abort)#147aterga wants to merge 2 commits into
aterga wants to merge 2 commits into
Conversation
Adds decode_reply_rejects_a_deep_opt_chain_instead_of_aborting: the DEPTH counterpart to the existing vec-null breadth-bomb test. A compact reply can nest hundreds of thousands of `opt` levels (`type t = opt t`, one wire byte per level); candid's decoder, Display, and the tree's recursive Drop each recurse per level, so an unbounded chain would overflow the deep-stack thread and abort the process (dropping every session). REPLY_DECODING_QUOTA bounds breadth, not depth. Depth is bounded by candid's own stacker::remaining_stack() recursion guard, which returns an ordinary decode error before the stack is exhausted, on any stack size. The test pins that a 300k-deep opt reply degrades to "not decodable" rather than crashing, guarding against a candid downgrade/regression that would drop the guard. No production code change — the behavior is already correct; this locks it in. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a regression test ensuring deeply nested Candid replies fail safely rather than aborting.
Changes:
- Constructs a 300,000-level recursive
optreply. - Exercises type-less reply decoding and checks for graceful failure.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
… fallback Review fix: the assertion accepted `on_deep_stack`'s "(could not spawn …)" fallback, which never reaches candid's recursion guard — so a thread-spawn failure would pass the test without exercising the behavior it pins. Require "not decodable" (as the breadth-bomb test does), so the test only passes when candid's guard actually rejected the deep chain. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/calls.rs:1539
- This new explanation contradicts the security model documented for
decode_replyatsrc/calls.rs:730-737and for its existing regression test atsrc/calls.rs:2252-2260, which still state that candid has no depth bound and that the 64 MiB worker stack prevents an attacker-triggered abort. Since this PR establishes that candid's recursion guard is the actual unbounded-depth protection, update those comments too so future changes do not rely on the larger stack as the safety boundary.
// dropping every session. `REPLY_DECODING_QUOTA` bounds BREADTH, not depth;
// DEPTH is bounded by candid's own `stacker::remaining_stack()` recursion
// guard, which returns a decode error before the stack is exhausted (on any
// stack size). This pins that: a 300k-deep `opt` reply must degrade to the
// ordinary "not decodable" error, never a crash. It regression-guards against
sea-snake
approved these changes
Aug 20, 2026
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.
What
Adds
decode_reply_rejects_a_deep_opt_chain_instead_of_aborting— the depth counterpart to the existingvec nullbreadth-bomb test. No production code changes; the behavior is already correct, and this pins it.Why
A security scan flagged a possible CWE-674 process abort: an attacker-chosen canister can return a compact reply that nests hundreds of thousands of
optlevels (type t = opt t, one wire byte per level). candid's decoder,IDLArgs'Display, and the decoded tree's recursiveDropeach recurse once per level, so an unbounded chain could overflow the 64 MiB deep-stack thread — an uncatchable abort that drops every concurrent session.Triage found the finding does not reproduce:
REPLY_DECODING_QUOTAbounds breadth (allocation), and depth is bounded by candid's ownstacker::remaining_stack()recursion guard (candid/src/utils.rs), which fires on every compound value level (opt / vec / record / variant / tuple) and returns an ordinary decode error before the stack is exhausted — on any stack size. That guard landed in candid PR #417 (2023), well before this crate's0.10.31floor, and was hardened in #700 (2026), which is in the locked0.10.35.The test
Builds the exact attack — a ~300 KB type-less reply nesting 300,000
optlevels — and assertsdecode_replyreturns the ordinary "not decodable" error instead of aborting. It regression-guards against a candid downgrade or a change that drops the recursion guard.Notes
MVP, no stability commitment (https://mcp.internetcomputer.org/terms). Test-only; documents that reply-decode depth-safety depends on candid's recursion guard, so the candid floor must stay at a stacker-guarded version.
🤖 Generated with Claude Code