-
Notifications
You must be signed in to change notification settings - Fork 434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Limiting the number of poll loops to prevent DoS events #954
Conversation
Why not get rid of the loop and let the application decide on the strategy? Or, asking differently, why is |
I like the idea of basing the poll iteration count based on buffer sizing and data available to smoltcp, so I've updated it to calculate a maximum iteration count based on the sockets (where this makes sense). For protocols that do not buffer data in a packetized format (i.e. TCP, DHCP, DNS), I've simply set the maximum iteration count to 1, since these protocols could theoretically have an infinite sequence of packet exchange and the user application should likely figure out how to handle polling. |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #954 +/- ##
==========================================
- Coverage 79.21% 79.18% -0.03%
==========================================
Files 81 81
Lines 26820 26885 +65
==========================================
+ Hits 21245 21289 +44
- Misses 5575 5596 +21 ☔ View full report in Codecov by Sentry. |
src/iface/interface/mod.rs
Outdated
@@ -396,6 +396,9 @@ impl Interface { | |||
/// This function returns a boolean value indicating whether any packets were | |||
/// processed or emitted, and thus, whether the readiness of any socket might | |||
/// have changed. | |||
/// | |||
/// # Note | |||
/// If this function returns true, there may still be packets to be transmitted or received. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This note should explain why, because as it is the obvious response to reading this note is to do it in a loop, but this just reintroduces the original DoS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expanded the why in 6492c52, let me know if you have other verbiage you'd prefer and I'm happy to incorporate it :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@jordens While I agree in principle with the idea of basing the amount of polling on some function of the total buffer size I don't feel that smoltcp can reasonably calculate this on its own (the strategy in this PR doesn't seem reasonable to me), and this should probably be left to the application, since the final application itself is the only entity which is aware of the performance requirements and the characteristics of the platform. If we do go with smoltcp calculating the iteration count on the buffer size this should be the PHY buffer size rather than the stack buffer size. An alternative approach, more principled but also requiring considerably more work, is to make an opaque polling iterator for the socket set (i.e. use external iteration instead of the internal iteration implicitly done by |
The iteration approach sounds neat! |
Imagine that you have 1000 sockets and each of them has a single packet to send (and the TX buffer never becomes full, e.g. you're on a PC). You'll then have to iterate through the socket set 499500000 times to send all of the packets, am I wrong? |
Maybe. But I may be misundertanding you. I tought we are talking about removing the smoltcp/src/iface/interface/mod.rs Lines 434 to 449 in 7b125ef
Ignoring the fragmentation and timestamp stuff before the loop, I can't see how allowing the application to do that loop over |
@jordens Oh, you are right. I was relying too much on my memory and wasn't paying enough attention. I agree that only running a single iteration of that loop would be fine. |
There's actually another loop in |
So if we change both loops to a single iteration then the problem I described does arise. But if you change the loop in (Also, if we do limit the inner loop, is the outer loop still an issue?) |
I suspect so, although I didn't completely follow your math :)
I didn't know if this property was available on the PHY trait, but this was what I was thinking in the back of my mind when drafting up the changes. I'll take a look at this shortly, thanks for the feedback!
I believe so, yes. Since essentially the problem we observe is that the PHY is essentially generating RX tokens faster than they can be processed. Any kind of |
@ryan-summers I'm happy to proceed changing both loops to a single iteration; like Robert says this should not have detrimental side effects, as far as I can tell. |
Yup sorry, meant to get to this on Thursday, but am now traveling until Monday. I'm planning on going with the single loop approach, thanks for clarifying :) |
@whitequark Let me know if these updated changes look appropriate. I'll run this PR on hardware in my office in the next day or two to verify it as well. |
src/iface/interface/mod.rs
Outdated
/// Users should should perform other tasks before calling this function again. If executed in | ||
/// a loop, its possible that packets can arrive faster than they can be processed, which could | ||
/// cause an infinite loop. | ||
/// This function performs a bounded amount of work per call to avoid exhausting |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, typo:
/// This function performs a bounded amount of work per call to avoid exhausting | |
/// This function performs a bounded amount of work per call to avoid |
(I guess this also makes formatting uglier... sigh)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah sorry, I saw this already and removed it, and indeed the formatting is ugly
Alright, I have now tested this on hardware and confirmed the problem is still fixed with these changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, please squash and I'll merge.
Updating process loop to be defined by socket buffers when possible Further changes after hardware testing Fixing test Adding CHANGELOG entry Expanding comment after review Update src/iface/interface/mod.rs Co-authored-by: Catherine <whitequark@whitequark.org> Removing redundant phrasing in comment Updating verbiage to remove references to unsafe and safe
2bcae4a
to
8c2cef1
Compare
I've done the squash manually. However, I thought Github added the ability for contributors / admins to squash before merge? If it's not showing up on the merge options list, you can probably enable it in the Github repository settings as well :) |
Nope, this is not available when merge queue is used. Which is annoying but unfortunately the merge queue is a lot more important. |
This makes `.poll()` behave the same as before #954. Users affected by DoS concerns can use the finer-grained egress-only and single-packet-ingress-only fns.
This makes `.poll()` behave the same as before #954. Users affected by DoS concerns can use the finer-grained egress-only and single-packet-ingress-only fns.
This makes `.poll()` behave the same as before #954. Users affected by DoS concerns can use the finer-grained egress-only and single-packet-ingress-only fns.
This PR fixes #848 by limiting the number of ingress/egress tokens processed by observing the characteristics of the sockets contained in the polled socket set.
Edit: Further information on hardware testing can be found in quartiq/booster#423