sendme: Service introduction circuit ignore flow control
As preamble, circuit level SENDMEs are end-to-end which means they go from client to exit and vice versa. In other words, they can be described to be from edge connection to edge connection.
Which is exactly where it goes wrong for hidden service cells. First of all, they are not DATA cells so the SENDME logic is entirely ignored for all of them. They are all "circuit establishment" cells (see the list below)...
case RELAY_COMMAND_ESTABLISH_INTRO: case RELAY_COMMAND_ESTABLISH_RENDEZVOUS: case RELAY_COMMAND_INTRODUCE1: case RELAY_COMMAND_INTRODUCE2: case RELAY_COMMAND_INTRODUCE_ACK: case RELAY_COMMAND_RENDEZVOUS1: case RELAY_COMMAND_RENDEZVOUS2: case RELAY_COMMAND_INTRO_ESTABLISHED: case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED:
... except one single cell which is the
INTRODUCE2 cell. A large number of these cells can be put on the same introduction service circuit, basically for each client introducing.
Which means that the intropoint can send as much cell as it wants on the service circuit without being bound by the SENDME flow control logic. Plainly speaking, intro points do not wait for an acknowledgement of the service to send more data, they just shove it all on the circuit.
This most likely render the introduction DoS (#29607 (moved)) worst because the service actually constantly receives introduction requests as they queue up massively due to the intro point sending them non stop.
If there would be flow control on that circuit, a heavily loaded service (in CPU) would take a while to handle all introduction requests and then the SENDME cell towards the intro point would be only sent when the last request is actually handled and likely have CPU for new ones.
This also prevents us basically from implementing armadev's proposal in #15516 (moved) (https://trac.torproject.org/projects/tor/ticket/15516#comment:28).