Skip to content
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

Use bsd_closefrom.c from openssh-portable to fix #189 #255

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

thomasjm
Copy link
Contributor

I'm trying to fix #189.

Closing file descriptors in a reliable and portable way is not simple. The best summary I found of the different approaches available is in this StackOverflow answer.

Modern versions of glibc (>= 2.34) and/or the Linux kernel (>= 5.9) have added support for the closefrom and close_range functions, which allow us to performantly do what we want. A good solution will use these functions when available, and otherwise use fallbacks. Putting together the fallbacks is a little involved.

In this PR I added the BSD-licensed bsd_closefrom.c file from https://github.com/openssh/openssh-portable/ and use it to implement the FD-closing logic from this library.

I haven't tested this yet, I'm putting it up to see how it does in CI and solicit feedback on the approach. Thanks!

Copy link
Collaborator

@snoyberg snoyberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to me. @bgamari would you mind looking as well? You're far more familiar with the C code in this repo.

@thomasjm
Copy link
Contributor Author

Note: this isn't linking against musl libc at the moment, I probably need to get the macros in order...

@bgamari
Copy link
Contributor

bgamari commented Mar 13, 2023

@thomasjm, what is the status of this? Does this now work against musl? The implementation looks good on a cursory look although this does need to be rebased so I have held off on a thorough review.

One thing that I am admittedly worried about here is the potential for C symbol name clashes. In particular, process's cbits exposes many symbols with extremely generic names; this is certainly not a new problem but the fact that we now use code from OpenSSH increases the chance that things will break in the wild. Ideally, Haskell libraries would prefix all exported C symbols with a semi-unique prefix (e.g. hs_process).

@thomasjm
Copy link
Contributor Author

I ended up going with a workaround where I just lowered the max file descriptors in my Kubernetes environments, so this PR isn't quite done.

IIRC I was still fumbling around with CMake options and having trouble getting this to build with musl.

I can help push it over the finish line if there's interest.

@bgamari
Copy link
Contributor

bgamari commented Mar 14, 2023

It does seem like a nice improvement; it would be great to get it upstream.

process.cabal Outdated Show resolved Hide resolved
@bgamari
Copy link
Contributor

bgamari commented Mar 17, 2023

Looks good save one small question.

closefrom_excluding(int lowfd, int excludingFd) {
// Try using the close_range syscall, provided in Linux kernel >= 5.9.
// We do this directly because not all C libs provide a wrapper (like musl)
long ret = syscall(CLOSE_RANGE_SYSCALL_NUMBER, lowfd, excludingFd - 1, 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is a little dodgy due to the direct syscall. It should be good on Linux kernels because Linux has very strong guarantees about syscall stability. For other POSIX platforms we probably need to gate this behind a macro. Another option would be to not even try using the close_range syscall, and just loop from lowFd to excludingFd, assuming that excludingFd will be reasonably small.

FWIW there's a possibility that close_range will be added to musl in the future: https://inbox.vuxu.org/musl/[email protected]/T/#u

It would help me move this along if I had an easy way to test musl builds. I have a somewhat convoluted method using Haskell.nix, but do you happen to know a good way @bgamari ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would likely just use GHC's CI infrastructure. That is, open a draft MR against ghc/ghc bumping the process submodule to your branch. This will get tested against Alpine 3.12.

@bgamari
Copy link
Contributor

bgamari commented Mar 23, 2023

Do let me know when you would like a review on this, @thomasjm .

@thomasjm
Copy link
Contributor Author

Thanks @bgamari, I think it's just about ready for review!

Ideally, Haskell libraries would prefix all exported C symbols with a semi-unique prefix (e.g. hs_process).

I've done this in the latest commit, does that look like what you had in mind?

The GHC pipeline is passing at this point. I think what we could still use is some test(s) that file descriptors actually get closed properly in fork_exec.c.

I'm also wondering what happens on *BSD or other platforms where the syscall could potentially differ. Poking around in the FreeBSD source it looks like the arguments match Linux. I'm still scared of the direct syscall, although I really want this to work on static Musl builds. FWIW, I just pushed a commit to bring bsd_closefrom.c up to date with a couple upstream changes I noticed we were missing. One of those changes involves an actual call close_range, gated on HAVE_CLOSE_RANGE. We should probably be consistent about this: if we decide a close_range syscall is okay, we should replace that call in bsd_closefrom.c also.

@thomasjm thomasjm requested a review from bgamari March 24, 2023 09:32
process.cabal Outdated Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Speed up close_fds with the new close_range() Linux/FreeBSD syscall
3 participants