Skip to content

Per-client resource caps on filters and subscriptions #333

Description

@arner

Part IV of #329.

Problem: creating a filter or subscription is one cheap RPC call that then holds server-side
memory (map entry, timer, growing slice, or a goroutine + channel) for up to the 5-minute expiry
(filters) or the life of the connection (subscriptions) — with no limit today on how many a single
client can accumulate. That's unbounded-allocation exposure (CWE-770): a client can create them
faster than they can expire and sustain memory growth indefinitely. Not solved by this epic's
backpressure decisions, which only protect the block-commit hot path from a slow existing
subscriber — they say nothing about how many can exist.

Depends on:

(caps both the filter registry and the subscription registry those issues create).

Scope:

  • Filters: a global cap on total concurrently-open filters (block + log combined), not a
    per-client one. fxevm's JSON-RPC surface has no auth/session concept today, and filters are
    explicitly connection-independent by design (Server-side filters (block + log) #331,
    decision 9) — there's no real client identity to attribute a per-client cap to without adding an auth layer,
    which is out of scope here. A global cap is the achievable granularity: eth_newBlockFilter/eth_newFilter
    return a standard JSON-RPC error once the cap is hit, existing filters are unaffected, and a freed slot
    (via uninstall or expiry) becomes available again. Configurable, with a conservative default —
    the exact number matters less than the cap existing and being tunable.
  • Subscriptions: a per-connection cap, which is naturally attributable — each WS connection
    already has its own notifier/context, so counting NewHeads() calls per connection is direct,
    no new identity concept needed. A legitimate client only ever needs one newHeads subscription
    per connection, so the default can be small. A global cap across all connections is worth adding
    too, as defense in depth against many-connections-each-with-one-subscription, but the
    per-connection cap is the one that actually stops a single misbehaving client.
  • Explicitly not attempting per-IP or per-identity attribution beyond "this WS connection" — that
    needs real auth/session infrastructure this epic doesn't have and shouldn't invent as a
    side-effect of a resource-cap fix.

Acceptance criteria / testing:

  • Create the configured max number of filters, assert the next one errors cleanly; uninstall one,
    assert a new one can now be created (cap is on concurrent count, not lifetime total).
  • Same shape for subscriptions, scoped to one connection: open the per-connection max, assert the
    next eth_subscribe("newHeads") on that same connection errors; a different connection can
    still open its own subscriptions independently.
  • Existing, legitimate single-filter/single-subscription usage (i.e. everything the other issues in this epic
    already test) is completely unaffected by a reasonable default cap — this is a regression risk
    worth its own explicit test, not just an assumption.

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