Skip to content

delegate_host: four more unvalidated host-length paths with the same truncation shape #101

Description

@sanity

Found while reviewing #98. Not introduced there — #98 added guards to list_subscriptions and, per the systematic-debugging rule, the same pattern should have been grepped for at the time. It was not, so filing it.

The pattern

A host function returns an i64/i32 length, and the delegate allocates or truncates on it without validating. Two consequences:

  • as usize truncates on wasm32, where usize is 32 bits. A returned 2^32 becomes 0, which surfaces to the caller as an empty result rather than an error — indistinguishable from a genuine empty.
  • Vec::truncate with an oversized argument is a documented no-op, so a host that reports writing more than the buffer holds leaves the zero-filled tail in place, to be decoded as valid-looking data.

The sites

All in rust/src/delegate_host.rs:

Function Issue
list_secrets (~:523) vec![0u8; len as usize] on an unvalidated positive i64; then truncate(written as usize) with written never checked against len
get_contract_state (~:572) same shape
ctx_read (~:331) buf.truncate(read.max(0) as usize), read unchecked against the buffer
get_secret (~:420) same shape

Why it matters less than it looks, and still matters

The host is trusted, so this is not an attack surface — it is a malfunctioning-host surface, and the failure is silent in the direction that matters: a caller receives a plausible empty or short result instead of an error. That is the same conflation #98 argued was worth a Result return type for list_subscriptions.

Suggested fix

#98 extracted the decisions from list_subscriptions into two pure functions, validate_list_len and resolve_written, specifically so they are compiled and tested on the host target — the wasm-only bodies are otherwise type-checked by CI and executed by nothing. The same treatment generalises: one shared validation helper, host-side table-driven tests, and the unsafe extern call left as the only wasm-gated part.

Note this interacts with #100: cargo test runs on the host only, so any logic left inside #[cfg(target_family = "wasm")] has no test coverage at all today.

[AI-assisted - Claude]

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions