Skip to content

Cosmos CW: H256::from_slice panics after hex/base64 event decode on wrong-length attributes #9112

Description

@jiapy97

Summary

On Cosmos CosmWasm (CW) indexer paths, event attributes are decoded with fallible hex::decode / base64 decode (returning Result), then passed to H256::from_slice without a length check.

H256::from_slice panics when the decoded byte length is not exactly 32. Callers use ? and expect a recoverable Err, not a task/process abort.

This is an availability / robustness issue (Cosmos CW indexer panic), not a claim of fund theft, ISM bypass, or message forgery.

Related patterns (different crates/paths):

Type Availability / panicking parse
Severity Medium (DoS of Cosmos CW indexing under malformed event attributes)
Honest baseline Honest contracts typically emit 32-byte hex hashes; practical triggers need malformed/malicious event attributes or a bad data plane

Environment

Item Value
Repo hyperlane-xyz/hyperlane-monorepo
Commit checked 12d763327282cfa856d7e31eb79f815f753c23c3
Area rust/main/chains/hyperlane-cosmos (CW)

Root cause pattern

// Pattern (approx.)
Some(H256::from_slice(hex::decode(value)?.as_slice()))
// or base64 decode then from_slice
Step Behavior
hex::decode("00") Ok(vec![0])success, length 1
H256::from_slice(&[0]) panic (len ≠ 32)
Expected wrong length → Err(...) via ? chain

So: valid hex of the wrong length never becomes a ? error; it panics inside from_slice.


Call sites (approx. lines @ pin)

File Lines (approx.) Notes
rust/main/chains/hyperlane-cosmos/src/cw/merkle_tree_hook.rs ~247 hex path
same file ~251 base64 variant (same from_slice pattern)
rust/main/chains/hyperlane-cosmos/src/cw/interchain_gas.rs ~123 hex path
same file nearby base64 variant if present

Byte source: chain/RPC event attribute strings (hash-like fields). Normal modules emit 32-byte encodings; malformed or adversarial attributes can be shorter/longer but still valid hex/base64.


Expected vs actual

Expected Actual
Non-32-byte payload after hex/base64 decode Err / skip event H256::from_slice panic
Same crate style elsewhere fallible parse + map_err CW paths use unchecked from_slice

Proof of concept

Primitive behavior (same as used after decode):

H256::from_slice(len=0)  → panic
H256::from_slice(len=1)  → panic   # e.g. hex::decode("00")
H256::from_slice(len=31) → panic
H256::from_slice(len=32) → ok

Minimal illustration:

use hyperlane_core::H256;

fn main() {
    // Simulates: hex::decode("00")? → 1 byte → from_slice
    let one = hex::decode("00").unwrap();
    assert_eq!(one.len(), 1);
    let _ = H256::from_slice(&one); // panics
}
// Pattern matching production style:
fn parse_attr(value: &str) -> Result<H256, Box<dyn std::error::Error>> {
    Ok(H256::from_slice(hex::decode(value)?.as_slice())) // panics if len != 32
}
// parse_attr("00") → panic, not Err
// parse_attr("00".repeat(32)) with 32 hex pairs → ok if length matches

Impact

  • Cosmos CW indexer code that assumes only Result::Err on bad attributes may abort on panic.
  • Affects availability of indexing under malformed event attributes (or a compromised/buggy event source).
  • Does not claim unauthorized mint/transfer or ISM forgery.
  • Honest mainnet modules that always emit canonical 32-byte hex make accidental triggers less likely; the API contract is still broken for defensive parsing.

Suggested fix

  1. After hex::decode / base64 decode, check bytes.len() == 32 and return a typed error before from_slice.
  2. Prefer fallible constructors (TryFrom, or a helper h256_from_slice(bytes) -> Result<H256, _>).
  3. Align with other Hyperlane paths that already use map_err / length checks instead of panicking parses.
  4. Unit tests: short/long hex and base64 attributes must return Err, must not panic.

Example sketch:

let raw = hex::decode(value)?;
if raw.len() != 32 {
    return Err(/* InvalidLength / Decoding */);
}
let hash = H256::from_slice(&raw);

Out of scope


Related

Same root theme as other reports: fallible decode (Result) + panicking from_slice, used on network/event-derived data.

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions