Raise the open fd limit to the maximum allowed #11785

pull vii wants to merge 1 commits into bitcoin:master from vii:master changing 1 files +20 −5
  1. vii commented at 5:26 am on November 29, 2017: none

    As noted in #11368 if too many connections are made to the RPC interface, then other code will fail on open(2) syscalls with EMFILE. The result can be that the block database gets into an inconsistent state.

    On many Linux distributions, by default, each process has 1024 file descriptors; these are shared between open files and network connections. The main init code attempts to apportion them between uses, but neglects to constrain the RPC layer: https://github.com/bitcoin/bitcoin/blob/master/src/init.cpp#L907

    Unfortunately, libevent does not allow a natural way to bound the number of file-descriptors used by the evhttp server. Therefore, we have to resort to requesting to stop new connections by disabling the accept listener in the epoll event structure. This is not a good way to control load, and more connections are accepted until the next epoll cycle is triggered, but it does stop an unbounded number of connections from being created, and does prevent a high number of connections to the RPC layer from damaging the rest of the system.

    To avoid problems of a similar nature, the second patch additionally raises the rlimit of number of file descriptors as high as it can go.

    To repro the database crash and validate the fix, the following node.js fragment:

    0var uri = 'http://127.0.0.1:8332/rest/block/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f.json'
    1for (var message = 0; message < 10000; message++) {
    2    request(uri)
    3}
    

    The messages around the database crash due to open(2) failing due to too many open files

    2017-11-26 19:35:55 libevent: Error from accept() call: Too many open files 2017-11-26 19:35:55 ERROR: WriteBlockToDisk: OpenBlockFile failed 2017-11-26 19:35:55 libevent: timeout_next: event: 0x7f59001dcef0, in 15 seconds, 475453 useconds 2017-11-26 19:35:55 *** Failed to write block 2017-11-26 19:35:55 libevent: epoll_dispatch: epoll_wait reports 1 2017-11-26 19:35:55 Error: Error: A fatal internal error occurred, see debug.log for details

  2. fanquake added the label RPC/REST/ZMQ on Nov 29, 2017
  3. practicalswift commented at 8:44 am on November 29, 2017: contributor
    Nice find. Thanks for reporting!
  4. laanwj requested review from theuni on Nov 29, 2017
  5. laanwj commented at 10:01 am on November 29, 2017: member

    Thanks!

    It’s kind of disappointing that evhttp has no way to limit file descriptors. This is the so-manieth hack we need to accommodate it. It’s also not very actively maintained so trying to push any fix upstream will take a long time, if ever. I sometimes wonder if moving to libevhtp, which seems to be the quasi-standard for http servers on top of libevent, would solve these issues.

  6. promag commented at 10:08 am on November 29, 2017: member
    Should we add a stress test to travis?
  7. vii force-pushed on Nov 29, 2017
  8. vii commented at 1:47 pm on November 29, 2017: none

    @laanwj yes it is disappointing that there isn’t a better API to manage this systemic issue in evhttp.

    Even more unfortunately, some configurations in Travis use an old version of libevent that doesn’t have the bevcb callback or the ability to count added file descriptors. I’ve updated the pull request correspondingly. Protection is only available on newer libevents :(

    What programs are available in Travis? Various configurations of https://github.com/shekyan/slowhttptest would be good tests, for example

    0slowhttptest -c 40000 -r 1000 -u 'http://127.0.0.1:8332/rest/block/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f.json'
    
  9. laanwj commented at 1:13 pm on November 30, 2017: member

    I’ve updated the pull request correspondingly. Protection is only available on newer libevents :(

    I think it would make sense to add a “known issue” to the release notes, recommending people that stumble on file descriptor problems while doing a lot of separate RPC requests to upgrade their libevent.

    FWIW minimum seems to be 2.1.4:

    0$ git tag --contains 0fa107d8cbc652dacb722fcf650bb6b3ffbe8dac
    1release-2.1.4-alpha
    2release-2.1.5-beta
    3release-2.1.6-beta
    4release-2.1.7-rc
    5release-2.1.8-stable
    
  10. laanwj commented at 3:39 am on December 1, 2017: member
    This was discussed on IRC and according to @theuni this doesn’t fully solve the problem, unfortunately. He could still fairly easy crash the server with many connections. So it will have to be solved from inside libevent.
  11. theuni commented at 5:02 am on December 1, 2017: member
    Yes, I’ve spent the last 2 days trying to work out our best option here. The provided patch is a big help, but I think there are a few more things we can do, namely a libevent patch. I’ll push up some more changes tomorrow and we can discuss what it makes sense to pull in.
  12. vii commented at 1:29 pm on December 1, 2017: none

    @theuni here is a libevent patch that provides sufficient functionality to constrain the number of file-descriptors to a tight bound: https://github.com/libevent/libevent/pull/578

    In the meantime, as we work through the process to change libevent, the pull request here mitigates the original issue and defends against similar ones

  13. vii force-pushed on Dec 1, 2017
  14. theuni commented at 6:30 am on December 2, 2017: member

    @vii Nice work on the upstream patch. It’ll be great to have a real solution for this.

    I have a few concerns about the approach here:

    • event_base_get_num_events is not a great representative of the connection count, as our own timers and triggers will throw off the count
    • I think keep-alive connections need to be closed if they’re re-used once the connection limit has been reached, otherwise it becomes trivial to monopolize the available slots.
    • It unfortunately doesn’t help with libevent 2.0.x, which is by far the majority in the wild.

    I’ve adapted your approach and added in a few extras which manage to work-around the issue for libevent 2.0.x as well. With this, I’m unable to raise my fd count above the desired ceiling, no matter how hard I hammer.

    Please have a look here and see what you think: https://github.com/theuni/bitcoin/commits/http-fd-limit. Note that there’s an unlikely potential race at shutdown that I haven’t yet addressed.

  15. laanwj commented at 9:30 am on December 2, 2017: member

    Great!

    It unfortunately doesn’t help with libevent 2.0.x, which is by far the majority in the wild.

    Yes - it would be nice to have a (potentially messy) workaround for older libevent, and a clean solution for future upstream versions.

  16. vii force-pushed on Dec 4, 2017
  17. vii commented at 5:56 am on December 4, 2017: none

    @theuni - your patch looks great! I was cautious about modifying so much.

    Given the discussion, I’ve reduced my pull request to the simple improvement of making sure to request as many file descriptors as possible.

    With @theuni’s improvement to only accept connections that have data on, honest HTTP connections will go into the connection limiter. However, I suspect adversaries using things like the slowloris attack (which send incomplete requests and then pause) will not be caught and will use resources. These can be controlled if the libevent request is included, but otherwise can be detected but not perfectly controlled using the approach in the first request, which binds to the rather toothless bevcb callback.

    One way to look at this is that the root issue is that there is a process wide file-descriptor allocation defined in https://github.com/bitcoin/bitcoin/blob/master/src/init.cpp#L907 but the RPC interface isn’t accounted for. Would be great to integrate @theuni’s ConnectionLimiter into that!

    Is it worth defending against slowloris style attacks given the RPC interface is generally not accessible to adversaries?

    In this pull request, his small change to request as many file-descriptors as possible should help mitigate the original issue on many default configurations (e.g. Ubuntu).

  18. After requesting the desired number, try to increase file-descriptors to the maximum allowed 8aaaa68432
  19. vii force-pushed on Dec 17, 2017
  20. vii commented at 6:05 pm on December 17, 2017: none

    this small change to request more file-descriptors does not depend on the libevent patch (which is making progress towards acceptance) but alleviates the issue in many configurations - except for deliberate attacks

    Please review!

  21. vii commented at 3:55 am on December 18, 2017: none

    the libevent change has been merged into that repo so we can have a complete fix https://github.com/libevent/libevent/pull/578 - but the limited PR here does not depend on it

    Happy to make a more complete fix if it would be reviewed!

  22. theuni commented at 10:50 pm on December 20, 2017: member

    @vii Great work on the upstream fix!

    I’ll clean up my changes and PR them separately. I’ll go ahead and work in the bevcb for supported versions, so that we can take advantage of that when possible.

    As for raising the fd limit, I have mixed feelings. I worry that this will simply mask problems. I believe @gmaxwell was in favor, though. @gmaxwell: assuming we manage to contain this issue, do you still think maxing out the fd limit is beneficial?

  23. theuni renamed this:
    Prevent file-descriptor exhaustion from RPC layer
    Raise the open fd limit to the maximum allowed
    on Dec 20, 2017
  24. vii commented at 5:46 pm on December 30, 2017: none

    the issue that it masks is that several usages of file-descriptors are not budgeted for in the process wide file-descriptor allocation defined in https://github.com/bitcoin/bitcoin/blob/master/src/init.cpp#L907

    It would be quite difficult to carefully audit for all usages of file-descriptors as library functions may use them for various purposes. Even once the budgeting is done, this will be helpful!

    Please consider and merge

  25. fanquake requested review from gmaxwell on Dec 31, 2017
  26. laanwj commented at 7:31 pm on January 11, 2018: member
    What I’d like to know is how does other software handle this? Is it frowned upon for daemons to increase the file descriptor count without being asked? I thought the usual case for UNIX is to make changing resource limits up to the user/sysadmin, and changing them un-asked is deemed rude, but this might be different for file descriptors.
  27. vii commented at 4:30 am on January 30, 2018: none

    @laanwj we are not attempting to change the maximum, just adjusting to use the maximum we are allowed

    Note here is some similar code in the wine project - rather more battlehardened but with the same effect on Linux

    https://source.winehq.org/source/libs/wine/loader.c#L661

  28. laanwj added this to the milestone 0.16.1 on Jan 30, 2018
  29. eklitzke commented at 0:48 am on February 20, 2018: contributor
    @theuni brought this PR to my attention in a conversation we had earlier today. I was actually planning to implement the same behavior, for a completely different reason: I would like to increase the number of file descriptors that leveldb is allowed to use, because performance testing I have done makes me think that the low limit leveldb currently has is causing issues with which pages Linux decides to keep in the page cache. I think increasing the soft limit is safe and reasonable.
  30. laanwj commented at 9:23 pm on March 5, 2018: member
    @eklitzke Is that an ACK on this?
  31. laanwj removed this from the milestone 0.16.1 on May 17, 2018
  32. Nico205 referenced this in commit 67c866744d on May 17, 2018
  33. doublesharp referenced this in commit ba8f877f16 on May 26, 2018
  34. Nico205 referenced this in commit a7f57ae019 on May 26, 2018
  35. DrahtBot commented at 8:30 pm on July 20, 2018: member
  36. DrahtBot closed this on Jul 20, 2018

  37. DrahtBot reopened this on Jul 20, 2018

  38. DrahtBot commented at 11:17 am on November 5, 2018: member
  39. DrahtBot added the label Needs rebase on Nov 5, 2018
  40. laanwj commented at 3:42 pm on January 9, 2019: member
    Closing this, reviewing and development bled out and a rebase has been necessary for months. Let me know if I should reopen it.
  41. laanwj closed this on Jan 9, 2019

  42. laanwj removed the label Needs rebase on Oct 24, 2019
  43. MarcoFalke locked this on Dec 16, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-09-29 01:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me