-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Summary
When updating to 0.53.0, we encountered a curious bug with Mdns.
After sending an incorrect Mdns packet, which is dropped (as expected), no packet is ever received by the peer after that, even if it is correct.
Expected behavior
An incorrect Mdns packet should not stop the polling of an InterfaceState.
Actual behavior
The polling of an InterfaceState is stopped after receiving an incorrect Mdns packet.
Relevant log output
No response
Possible Solution
Members of our team diagnosed the problem and managed to find the solution. It seems a dormant bug was present for a long time in the poll implementation of InterfaceState but it was only awakened with the merge of #4623.
rust-libp2p/protocols/mdns/src/behaviour/iface.rs
Lines 313 to 325 in cddc306
| Poll::Ready(Err(err)) if err.kind() == std::io::ErrorKind::WouldBlock => { | |
| // No more bytes available on the socket to read | |
| } | |
| Poll::Ready(Err(err)) => { | |
| tracing::error!("failed reading datagram: {}", err); | |
| } | |
| Poll::Ready(Ok(Err(err))) => { | |
| tracing::debug!("Parsing mdns packet failed: {:?}", err); | |
| } | |
| Poll::Ready(Ok(Ok(None))) | Poll::Pending => {} | |
| } | |
| return Poll::Pending; |
As we can see in the above code sample, many Poll::Ready are handled and returned as Poll::Pending. However, in the rust documentation, we can read that "When a function returns Pending, the function must also ensure that the current task is scheduled to be awoken when progress can be made". When looking at the current implementation, we can safely say that, when a Poll::Ready is caught and converted into a Poll::Pending, no scheduling is done to ensure the task will be woken up.
Even if this bug has been present for a long time, it has not caused any problems up until now. Indeed, before the previously mentioned PR, all InterfaceState were stored in the iface_states HashMap and were polled no matter what their previous returned value was (since poll was called in a loop over each InterfaceState). So, even if the poll method of InterfaceState had not scheduled its own awakening, it would still be called at the next iteration of the poll method of the mdns Behaviour.
However, now that each InterfaceState is spawned in its own task, nobody is calling poll when it returns Poll::Pending. That is why returning a Poll::Pending when internally getting a Poll::Ready result in the poll method of InterfaceState never being called again, causing packets to never be handled.
Version
v0.53.0 (master)
Would you like to work on fixing this bug ?
Yes