Skip to content

fix(bridge): bound disk I/O in position-encoding conversion - #486

Merged
bug-ops merged 2 commits into
mainfrom
position-encoding-conversion-re
Sep 21, 2026
Merged

bug-ops merged 2 commits into
mainfrom
position-encoding-conversion-re

Conversation

@bug-ops

@bug-ops bug-ops commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Summary

Converting an LSP location to MCP coordinates fell through to a whole-file disk read with no cache whenever the target line wasn't already in DocumentTracker's in-memory state, and every response looped over every returned location with no cap. A hostile or malfunctioning LSP server could turn one references/goto/workspace-symbol/call-hierarchy/inlay-hints/rename response into on the order of a terabyte of disk I/O for a single MCP tool call (CWE-405, CWE-770).

  • Line text read from disk is now memoized per (path, line) for the lifetime of one response, and read incrementally instead of loading the whole file.
  • A per-response byte budget bounds total disk I/O across every EncodingCtx-mediated handler, charged as bytes are scanned so it cannot be bypassed by a failed read (invalid UTF-8, size-limit truncation, or a nonexistent path).
  • references, goto-definition/implementation/type-definition, and workspace-symbol additionally cap the number of locations/symbols normalized per response and report truncation to the caller via a new truncated field.

Follow-ups filed separately (out of scope for this PR, by agreement during review)

  • Tracker-hit (in-memory) path is memoized but not budgeted; count-uncapped handlers can still burn unbudgeted in-memory scanning on a large tracked document.
  • call_hierarchy/inlay-hints/workspace-edit handlers have no item-count cap (their disk I/O is bounded by the shared byte budget; in-memory conversions for a huge item count are not).
  • The per-response byte budget is a hardcoded constant rather than derived from the operator-configured max_file_size.

Test plan

  • cargo +nightly fmt --all -- --check
  • cargo clippy --all-targets --all-features --workspace -- -D warnings
  • cargo nextest run --workspace --all-features --lib --bins (968 passed, 1 skipped)
  • RUSTDOCFLAGS="-D warnings --deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --all-features

Closes #474

Converting an LSP location to MCP coordinates fell through to a
whole-file disk read with no cache whenever the target line wasn't
already in DocumentTracker's in-memory state, and every response
looped over every returned location with no cap. A hostile or
malfunctioning LSP server could turn one references/goto/workspace-
symbol/call-hierarchy/inlay-hints/rename response into on the order
of a terabyte of disk I/O for a single MCP tool call.

Line text read from disk is now memoized per (path, line) for the
lifetime of one response, and read incrementally instead of loading
the whole file. A per-response byte budget bounds total disk I/O
across every EncodingCtx-mediated handler regardless of how many
distinct files or lines are touched, charged as bytes are scanned so
it cannot be bypassed by a failed read (invalid UTF-8, size-limit
truncation, or a nonexistent path). References, goto-definition/
implementation/type-definition, and workspace-symbol additionally
cap the number of locations/symbols normalized per response and
report truncation to the caller via a new `truncated` field.
@bug-ops
bug-ops force-pushed the position-encoding-conversion-re branch from 3e40015 to 9823864 Compare September 21, 2026 12:24
@bug-ops
bug-ops merged commit 3aa65ec into main Sep 21, 2026
29 checks passed
@bug-ops
bug-ops deleted the position-encoding-conversion-re branch September 21, 2026 12:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation mcpls-core mcpls-core crate changes rust Rust code changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

position-encoding conversion re-reads whole files per location, amplifying one tool call into thousands of disk reads

1 participant