Repository navigation
Description
Problem
tuple::decode recurses on the NESTED tag with no depth limit, so a crafted
key of repeated 0x05 bytes aborts the whole process with a stack overflow —
an error no caller can catch or recover from.
Evidence (main 42796be5)
pkgs/id/src/tuple.rs:380—pub fn decode(bytes: &[u8]) -> Result<Vec<TupleValue>>
walks the input viadecode_one.pkgs/id/src/tuple.rs:393,428—decode_one'sNESTEDarm loops by calling
itself recursively (match decode_one(data, pos, true)?). There is no depth
counter, no input-length guard, and no cap anywhere in the file (the only
MAX-style checks in the decoder are the per-field canonicality rules added
for id(tuple): decode accepts negative ints that wrap to zero/positive and NaN floats — the negative branch has no range check, unlike the positive branch #1610).- Contrast in the same crate:
pkgs/id/src/chain.rsdecodes peer-supplied CBOR
with an explicit.recursion_limit(64)(RECURSION_LIMIT, chain.rs:39,350).
The tuple decoder — which parses the same class of untrusted bytes — has no
equivalent. - Reachability: the decoder runs on iroh-docs entry keys, which are written by
other replicas of a shared namespace:pkgs/id/src/tags.rs:585,609
(decode_alpha_key/decode_omega_keycalldecode(raw)on the raw key)
andtags.rs:1401-1415(entry_to_tag_alpha/entry_to_tag_omega, called
from the tag query paths at tags.rs:1240,1281,1383,1395). One crafted key in
a synced α/Ω doc crashes every node that lists/searches that namespace's tags.
Reproduction (verified this run)
Probe crate on the laptop (~/git/fleet/probe-2026-10-29-tuple-depth/) that
compiles the crate's own tuple.rs at main 42796be5 verbatim
(md5 8fd9b15126d0772d28bb984bd86f83f2, identical in both clones) and calls
tuple::decode on [0x05; N] ++ [0x00; N]:
- N=1,000 and N=10,000 on a 2 MiB stack: decode OK.
- N=50,000 (a 100 KB input) on a 2 MiB stack:
thread has overflowed its stack / fatal runtime error: stack overflow, aborting, exit 134 (SIGABRT). - N=100,000 on an 8 MiB stack: same abort, exit 134.
A stack overflow in Rust is a process abort, not a Result::Err — the
?-based error handling in every caller is irrelevant once it trips.
Why it matters
Tag namespaces sync between peers; the α/Ω key space is not written only by
this node. A single malicious or corrupt entry key turns a routine tag
list/search on any replica into a full process crash (denial of service that
repeats on every query until the entry is removed by other means). The crate
already treats this exact risk as real for CBOR headers (recursion limit 64);
tuple keys deserve the same bound.
Acceptance criteria
tuple::decoderejects nesting deeper than a named, documented limit
(consistent with the crate's CBORRECURSION_LIMITof 64, or a justified
value) with a normalErr, at any input size and on a small (e.g. 2 MiB)
stack — no abort.- Regression tests:
[0x05; 100_000] ++ [0x00; 100_000], a just-over-limit
nested value, and a legitimately nested value at/under the limit (round-trip
through the encoder) — the first two error, the last decodes. decode_alpha_key/decode_omega_keysurface the depth error as an
ordinary tag error (no panic/abort) on a crafted doc entry key.- The encoder side is considered too: if the encoder can still produce
nesting deeper than the decoder accepts, that asymmetry is either fixed or
explicitly documented.
Activity
developing-today commentedon Oct 29, 2026
CODE-1023
dezren39 commentedon Oct 29, 2026
🤖 Agent note · filed
tuple::decodewith a laptop probe compiling the crate's owntuple.rsat main42796be5(50k nesting aborts on a 2 MiB stack, 100k on 8 MiB, exit 134); labelled and enqueued a triage packet to repo-health-code-pilot to pin the depth limit.RECURSION_LIMIT=64precedent) and routes a small-fix; done when over-limit nested keys return a normalErrinstead of aborting the process.hidden_files/issue-code-tuple-nested-depth-body.md; probe~/git/fleet/probe-2026-10-29-tuple-depth/(laptop); packethidden_files/queue-pilot/packet-2121-triage.jsondezren39 commentedon Oct 31, 2026
Corroborating evidence from new-issue-creator-code-b (2026-10-30 run, main 0281d10): re-verified with a verbatim-copy probe of
pkgs/id/src/tuple.rson the laptop (~/git/fleet/probe-2026-10-30-laneB-tupledepth/).thread 'main' has overflowed its stack, process abort (exit 134).So the abort threshold on a realistic worker thread is ~40 KB of crafted key, not 100 KB. Control: a legit depth-3 nested tuple decodes Ok. No depth limit exists in
decode/decode_one(tuple.rs:383-448).dezren39 commentedon Nov 1, 2026
🤖 Agent note · picked up
ses_e5e6a4e752bffeTFtzKdBW9aTf95 remaining items
dezren39 commentedon Nov 2, 2026
agent-note:v1
event: picked-up
task: 5100 (repo-health-code-pilot, OpenChamber scheduled lane)
state: claimed
verified: Fresh state confirms #2121 OPEN and assigned to Drew; PR #4083 is OPEN on the task branch and authored by Drew. Prior saved progress explicitly says not to touch Drew-owned PR/worktree. No files or PR changes made.
next: park 30 minutes; resume only after explicit release/handoff, then revalidate checks and reviews.
dezren39 commentedon Nov 2, 2026
agent-note:v1
event: parked
task: 5100 (repo-health-code-pilot, OpenChamber scheduled lane)
state: parked until 2026-11-02T06:42:30Z
blocker: #2121 remains assigned to Drew and PR #4083 is open on the task branch with Drew as author. The saved hard-stop forbids touching that PR or worktree. No source or PR changes were made.
resume: after Drew explicitly releases or hands off the work; then revalidate #2121, PR #4083, checks and reviews. Queue task parked for 30 minutes.