Skip to content

fix: guard u64 handle precision loss at Deno rawFetch and Tauri FFI boundary - #254

Merged
momics merged 1 commit into
mainfrom
fix/u64-handle-precision-loss
Jun 26, 2026
Merged

fix: guard u64 handle precision loss at Deno rawFetch and Tauri FFI boundary#254
momics merged 1 commit into
mainfrom
fix/u64-handle-precision-loss

Conversation

@momics

@momics momics commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Summary

Opaque bigint handles are slotmap u64 keys (32-bit slot index + 32-bit generation). Two FFI paths converted them to JS number via raw Number(...) with no safe-range check, so a handle above 2^53 would silently round and address the wrong resource. The generic Deno call() path already guarded this exact case, making these two paths inconsistent outliers.

Closes #252.

Changes

  • Deno (packages/iroh-http-deno/src/adapter.ts): extracted a shared bigintToSafeNumber() guard and used it in both the generic call() replacer and the rawFetch fast-path, so the fast-path now throws RangeError like the slow path instead of silently truncating.
  • Tauri guest-js (packages/iroh-http-tauri/guest-js/index.ts): added a u64Handle() boundary guard and applied it to every opaque bigint handle/token site (nextChunk, sendChunk, finishBody, cancelRequest, cancelFetch, rawFetch, respond_to_request). Endpoint handles are u32 numbers and were left unchanged.
  • Tests (packages/iroh-http-deno/test/adapter.test.ts): added regression tests asserting out-of-range handles are rejected rather than silently truncated.
  • deno.lock: resolved @std/assert entry pulled in by the new test's assertThrows.

Verification

  • New Deno regression tests pass.
  • npm run typecheck clean across all packages.
  • deno fmt applied.

Notes / out of scope

  • This PR addresses only the precision-loss class. The audit's Tauri rawServe() handler-drain gap (no tracking of pending handler promises, unlike Deno's await Promise.allSettled([...pending])) is intentionally not included — it needs the Rust-side serve drain semantics confirmed first and will be filed separately.
  • The audit's claimed Deno iroh_http_start_fetch_cb callback race was investigated and dismissed: the FFI symbol is blocking and fetchResolvers.set runs synchronously before the await, so the cross-thread callback cannot be delivered first.
  • The Tauri fix takes the "reject unsafe values before IPC" route (consistent with the existing Node/Deno guard) rather than the larger "transport handles as decimal strings + parse u64 in Rust" change, which would touch every plugin command signature.

…oundary

Opaque bigint handles are slotmap u64 keys (32-bit slot + 32-bit
generation). Two FFI paths converted them to JS number via raw
Number(...) with no safe-range check, so a handle above 2^53 would
silently round and address the wrong resource.

- Deno: extract a shared bigintToSafeNumber() guard and use it in both
  the generic call() replacer and the rawFetch fast-path, so the fast
  path now throws RangeError like the slow path instead of truncating.
- Tauri guest-js: add a u64Handle() boundary guard and apply it to every
  opaque bigint handle/token site (nextChunk, sendChunk, finishBody,
  cancelRequest, cancelFetch, rawFetch, respond_to_request). Endpoint
  handles are u32 numbers and left unchanged.
- Add Deno regression tests asserting out-of-range handles are rejected
  rather than silently truncated.

Closes #252
@momics
momics merged commit b53fb57 into main Jun 26, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Guard against u64 handle precision loss in Deno rawFetch fast-path and Tauri FFI boundary

1 participant