We have observed a rather worrying memory exhaustion behaviour in russh==0.61, triggered by the following use case:
- Create a
Server that listens to connections.
- On a new connection, grab a
session.handle(), and pump it with data (something that can happen if SSH is used to proxy data transfers).
- Observe that even if
handle.data(channel, response).await returns, this is not a guarantee that the output buffer is flushed or even limited. In our case, we have attempted to transfer ~3GiB worth of data, all of which ended up in memory, potentially leading into OOMs.
Our conjecture is that the culprit is server::Session::data() hands the data directly off to Encryped::data_with_writer(), which, inturns, executes channel.pending_data.push_back((buf0, None, 0));. channel.pending_data is an unbounded VecDeque, with no checks on its maximum size, and this applies no backpressure on the sender.
We have observed a rather worrying memory exhaustion behaviour in
russh==0.61, triggered by the following use case:Serverthat listens to connections.session.handle(), and pump it with data (something that can happen if SSH is used to proxy data transfers).handle.data(channel, response).awaitreturns, this is not a guarantee that the output buffer is flushed or even limited. In our case, we have attempted to transfer ~3GiB worth of data, all of which ended up in memory, potentially leading into OOMs.Our conjecture is that the culprit is
server::Session::data()hands the data directly off toEncryped::data_with_writer(), which, inturns, executeschannel.pending_data.push_back((buf0, None, 0));.channel.pending_datais an unboundedVecDeque, with no checks on its maximum size, and this applies no backpressure on the sender.