Skip to content

[relay] Topic subscription set grows without bound #113

@intendednull

Description

@intendednull

Parent: #108

Problem

crates/relay/src/main.rs:177-207:

async fn topic_announce_listener(
    mut events: <willow_network::iroh::IrohNetwork as Network>::Events,
    network: willow_network::iroh::IrohNetwork,
) {
    let mut subscribed: HashSet<String> = HashSet::new();

    while let Some(Ok(event)) = events.next().await {
        if let GossipEvent::Received(msg) = event {
            if let Some((willow_common::WireMessage::TopicAnnounce { topics }, _)) =
                willow_common::unpack_wire(&msg.content)
            {
                for topic_str in topics {
                    if subscribed.insert(topic_str.clone()) {
                        let topic_id = willow_network::topic_id(&topic_str);
                        match network.subscribe(topic_id, vec![]).await {
                            // ...
                        }
                    }
                }
            }
        }
    }
}
  • subscribed: HashSet<String> grows without any cap.
  • Each unique announcement triggers network.subscribe.
  • Topic strings are not validated (length, charset).
  • unpack_wire does require a valid Ed25519 signature, so only authenticated peers can hit this — but any misbehaving peer (intentional or buggy) can eat arbitrary relay memory by announcing unique random strings.

Fix

  1. Cap subscribed at MAX_TOPICS = 10_000 (tunable constant). Reject additional announces once the cap is hit, logging at warn level.
  2. Validate each topic_str: reject len() > 256, reject anything outside a conservative charset (e.g. [A-Za-z0-9_/:.-]).
  3. Optional: track announce rate per signer and rate-limit egregious peers.

Suggested constants

const MAX_TOPICS: usize = 10_000;
const MAX_TOPIC_LEN: usize = 256;

fn topic_str_is_valid(s: &str) -> bool {
    s.len() <= MAX_TOPIC_LEN
        && s.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '/' | ':' | '.' | '-'))
}

Test

Relay currently has zero tests. Add one:

  1. Start the relay.
  2. From a signed test peer, publish many TopicAnnounce messages with unique random topic strings.
  3. Assert the relay's subscribed set plateaus at MAX_TOPICS.
  4. Assert topic strings violating the charset or length are rejected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions