Skip to content

Per-IP WS connection cap is a non-atomic read-then-increment (TOCTOU) #211

Description

@v1ktorrr0x

Bug

The per-IP WS connection cap is enforced with a non-atomic read-then-increment. Concurrent upgrades from one IP can all observe count < limit before any increment lands, then each proceed to increment — overshooting the cap.

Source

src/routes/ws.ts

653      const ipConnections = await store.getConnectionCount(`auth:${clientIp}`);
654      if (ipConnections >= MAX_CONNECTIONS_PER_IP) { ws.close(...); return; }
657      await store.incrementConnectionCount(`auth:${clientIp}`);

Same pattern in the auth-upgrade path at :822-834.

Impact

getConnectionCount and incrementConnectionCount are two separate network round-trips on Upstash. Concurrent upgrades from one IP can all observe count < limit before any increment lands, then each increment — overshooting MAX_CONNECTIONS_PER_IP. The overshoot is more severe on the Upstash/multi-replica path.

Fix

Add an atomic incrementConnectionCountIfBelow(key, limit) → { count, admitted } to SharedStore. Mirror the existing incrementRateBucket Lua script pattern at shared-store.ts:329-361: a single INCR-compare-DECR-on-reject script for Upstash, and a synchronous check-increment in InMemoryStore. Reject when !admitted.

Verification

Fire N (> cap) simultaneous upgrade requests from one IP → admitted count never exceeds the configured cap.

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