What went wrong
coturn's --multiplex-peer mode lets many TURN allocations share relay sockets. To route an incoming datagram back to the right allocation, each relay thread kept a demultiplexing table from an exact peer endpoint: an IP address and port: to the session that owned it.
TURN permissions use a different unit: they authorize a peer IP address, regardless of port. One valid permission could therefore cover thousands of distinct endpoints, and every new port named by the client could add another entry to the shared table. The permission count was bounded; the state created from those permissions was not.
Removal made the mismatch more expensive. Permission expiry and session teardown scanned the table shared by every allocation on the relay thread. As the table grew, one allocation increased both the stored state and the cleanup work paid by its neighbours.
Why it mattered
The maintainer's advisory rates the issue Medium and lists every version through 4.15.0 as affected. Exploitation required multiplex-peer mode and either an authenticated TURN client or a deployment that did not require authentication. It did not bypass peer permissions or allow server-side request forgery.
Within those conditions, one client could keep introducing new ports for an already permitted IP. The resulting table growth consumed memory, while repeated full-table scans amplified CPU use during cleanup. Because the table belonged to the worker rather than to one allocation, the degradation could affect unrelated sessions handled by the same thread.
The same failure did not require hostile intent. A legitimate high-volume workload with many ephemeral peer ports could create the pattern naturally. This was therefore both an availability vulnerability and a robustness problem at a shared-resource boundary.
How it was fixed
coturn 4.16.0 introduced --multiplex-peer-max-peers, a per-allocation endpoint limit with a default of 256. Once an allocation reaches that limit, further permission and channel-binding requests receive error 508, while additional SEND endpoints are dropped. Normal allocations below the limit keep their previous behaviour.
The repair also added a reverse index from each session to its own endpoints. Expiry and teardown now visit the endpoints owned by that session instead of scanning unrelated entries in the worker's shared table. The state has a per-owner budget, and its cleanup cost follows the owner rather than the size of the shared structure.
The public pull request includes focused unit and integration coverage: registration stops at the configured limit, the limit applies independently to each session, released slots can be reused, and deregistration does not traverse entries belonging to another session. Upgrading to 4.16.0 or later applies both parts of the repair.
After the patch
Authentication answers who may ask a system to do work. It does not answer how much memory, CPU time or shared state one authenticated principal may consume. A permission limit is equally incomplete when the permission and the resource it creates are counted in different units.
A useful review question is therefore: what is the actual key of the stored object? If policy counts IP addresses while storage counts IP-and-port pairs, the smaller policy space does not bound the larger state space. Shared indexes should carry an explicit per-owner budget, and their removal paths should be proportional to that owner's entries wherever possible.