Skip to content

fix(sync): bound entry key size in validate_entry to prevent replica exhaustion#94

Open
emrul wants to merge 1 commit into
n0-computer:mainfrom
aster-rpc:fix/bound-entry-key-size
Open

fix(sync): bound entry key size in validate_entry to prevent replica exhaustion#94
emrul wants to merge 1 commit into
n0-computer:mainfrom
aster-rpc:fix/bound-entry-key-size

Conversation

@emrul
Copy link
Copy Markdown

@emrul emrul commented Apr 12, 2026

Description

validate_entry does not check the length of an entry's key. The codec's MAX_MESSAGE_SIZE (1 GiB) is the only implicit upper bound, so a sync peer can submit signed entries with keys up to roughly 1 GiB. Every replica that syncs such an entry persists it — a peer-controllable memory / disk amplifier.

This PR adds MAX_ENTRY_KEY_SIZE = 4096 and rejects entries whose key exceeds it with a new ValidationFailure::KeyTooLarge { actual, max }. The check runs before signature verification so oversized entries are dropped without spending ed25519 crypto work. 4 KiB is deliberately generous for legitimate uses (paths, UUIDs, short binary keys); the constant is trivial to tune.

The added test_peer_entry_with_oversized_key_rejected drives the peer path via Replica::insert_remote_entry: a 1 MiB key is rejected with KeyTooLarge, a key of exactly MAX_ENTRY_KEY_SIZE is accepted. I confirmed the same test body returns Ok on upstream main before the fix, so the vector is reachable today.

Breaking Changes

Entries submitted via sync (or any path that goes through validate_entry) whose key exceeds 4 KiB are now rejected with ValidationFailure::KeyTooLarge { actual, max }. Previously they were accepted up to roughly 1 GiB. Users that legitimately use keys larger than 4 KiB will need to shorten them or recompile with adjusted MAX_ENTRY_KEY_SIZE. ValidationFailure also gains a new KeyTooLarge variant so code doing exhaustive matches will need to be updated.

Notes & open questions

  • 4 KiB is a judgment call; asking reviewers to judge if its the right detail for iroh-docs?
  • Rejection is deliberately placed before signature verification to avoid spending crypto on garbage; flagging in case you'd rather verify first for audit/logging purposes.

Change checklist

  • Self-review.
  • Documentation updates following the style guide, if relevant.
  • Tests if relevant.
  • All breaking changes documented.

@n0bot n0bot Bot added this to iroh Apr 12, 2026
@github-project-automation github-project-automation Bot moved this to 🚑 Needs Triage in iroh Apr 12, 2026
@Frando
Copy link
Copy Markdown
Member

Frando commented Apr 20, 2026

Thanks, this looks sensible. Can you fix the formatting lints and rebase on main? Also, if you could edit the PR description to have a # Breaking changes noting the behavior change, and maybe collapse some of the other sections into less headers. No need for the test plan, we only merge PRs where all tests pass.

@emrul
Copy link
Copy Markdown
Author

emrul commented Apr 20, 2026

Thanks, this looks sensible. Can you fix the formatting lints and rebase on main? Also, if you could edit the PR description to have a # Breaking changes noting the behavior change, and maybe collapse some of the other sections into less headers. No need for the test plan, we only merge PRs where all tests pass.

Thank you - yes I can do that this week (currently travelling so bear with me!)

…tion

Sync-peer-supplied entries were accepted with arbitrarily large keys,
bounded only by the codec's MAX_MESSAGE_SIZE (1 GiB). `validate_entry`
did not check key length, so a peer that opened a sync session could
push entries with huge keys and force every replica that synced them
to persist them — a peer-controllable memory and disk amplifier.

Note: the key-size check has to live in `validate_entry` (or an
equivalent post-deserialization hook), not in `RecordIdentifier::new`,
because `RecordIdentifier` derives `Deserialize` and peer-supplied
entries reach the replica via serde, bypassing the constructor
entirely.

Fix: add `MAX_ENTRY_KEY_SIZE = 4096` and reject any entry whose key
exceeds it with a new `ValidationFailure::KeyTooLarge { actual, max }`.
The check runs before signature verification so oversized entries are
rejected without spending crypto work. 4 KiB is deliberately generous
for legitimate use cases (keys are short identifiers / paths); tunable
if upstream prefers a different limit.

Reproduction: the added `test_peer_entry_with_oversized_key_rejected`
test builds a 1 MiB key into a `SignedEntry` and feeds it through
`insert_remote_entry`. Before the bound the test's predecessor
(`test_peer_entry_with_oversized_key_accepted`, run against upstream)
confirmed the entry was accepted. With the bound in place the test
asserts `KeyTooLarge` is returned, and that a key at exactly
`MAX_ENTRY_KEY_SIZE` is still accepted.
@emrul emrul force-pushed the fix/bound-entry-key-size branch from 7bd068b to d5faea1 Compare April 21, 2026 08:28
@emrul
Copy link
Copy Markdown
Author

emrul commented Apr 21, 2026

@Frando - followed the PR template and updated per your feedback. Hope its good now and thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 🚑 Needs Triage

Development

Successfully merging this pull request may close these issues.

2 participants