-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
proposal: runtime: add a way to check if fd is used by go runtime #67639
Comments
Adding a bit of context here as the CVE text referenced above is quite long. There exists a container-specific (chroot-specific) attack vector, for which having CLOEXEC flag for an open (directory) FD is not a remediation. Those FDs had to be explicitly closed before spawning a child. |
In addition to the above, Go 1.23 adds pidfd support for Linux, so there will be another class of file descriptors used internally by Go runtime -- pidfds. Those are probably also need to be reported. |
@kolyshkin Are the pidfds used internally or are they all just associated with things Go program users need? If they're not used by the Go runtime internally, we can just |
Can this be explained better without reference to the very large CVE text? |
It is used internally by
Sorry for being brief before, please see below for answers. The summary is: we need a way to close all file descriptors
Those that are owned (were opened and used internally) by the go runtime, [and are essential for go runtime to function properly]. (1) (I'm not entirely sure if the square bracketed part should be part of the definition or not).
I'd like to add it breaks it badly (runtime panics).
Yes, I just realized that I made a mistake above suggesting that we include pidfd into the same category, crossing the line between "fds owned by go runtime" (as in pollfd) and "fds owned by go standard library" (as in a pidfds used by os, or /dev/random fd used by crypto/rand). I think we only need ones from the first category only (as defined in (1) above). If we'll also need those owned by packages from the standard library, I guess those packages can add a way to identify if a certain fd is owned by them. (2)
I guess, similar to (2) above, those package could have similar interfaces / ways to figure out if an fd is owned by them. |
This proposal has been added to the active column of the proposals project |
@kolyshkin Can you explain why there is a distinction between descriptors owned by the Go runtime, and descriptors owned by other parts of the Go standard library, and, for that matter, descriptors owned by other packages that may be part of the program? When does your program need to close all the descriptors? Why is it OK to close a descriptor held by some package but not OK to close a descriptor held by the runtime? Thanks. |
I think that for some scenarios it might be beneficial to know if an fd is used by some package. In our use case (runc), though, we're closing all fds above a specific one right before calling |
It sounds like you have a loop over fds and want to close all of them except the runtime epoll descriptor. Assuming your program does not have other epoll descriptors, or assuming that the security problem causing you to pre-close fds is itself not about some other epoll descriptor, then it seems like it would be enough to just not close any epoll descriptors in that loop. Is there some way to ask Linux "is this an epoll descriptor?" and then just skip those? |
I wonder whether this could be detected via an error code from epoll api, so creating a fd and using these two errors: man epoll_ctl(2):
So while trying to remove a fd (created only for epoll detection), i guess we would get EINVAL when the fd is not an epoll, and ENOENT when it is an epoll. |
So, this "close all fds" code is run right before doing You might be thinking "why not just use I appreciate this is a very niche thing, but we currently need |
As discussed above, arbitrary Go packages can hold file descriptors open. That doesn't matter for your use case, because you presumably only have a single goroutine running and you only care about descriptors used by the runtime package that are not under your control. So this is a very very special purpose use case. Only people with this exact use case are going to care about descriptors used by the runtime and not by other packages. At the same time, it's hard to imagine that the runtime will ever use any other descriptors. It's possible, of course. Many things are possible. But it seems very unlikely. So we have the combination of two unlikely cases. That is not a good argument for new API that needs to be documented and maintained forever. If there is some other workaround, we should use that. |
@kolyshkin you probably wants something different. |
If skipping all the epolls works, then I would strongly suggest doing that. If this hypothetical about a different long-lived file descriptor comes to pass, we can always revisit then. It doesn't seem worth introducing this concept before we need it. |
Based on the discussion above, this proposal seems like a likely decline. |
No change in consensus, so declined. |
Proposal Details
While working on fixing CVE-2024-21626, we had to implement a function that closes all file descriptors above a specific one (similar to what close_range(2) does). Once we did that, we found out that closing go's internal poll fds results in subsequent panic from go runtime, so we had to exclude those.
The only way possible way to do so was to use
internal/poll.IsPollDescriptor
(viago:linkname
kludge). You can see the code doing this here (initially added by this commit).We'd like this functionality as a part of public Go API. Perhaps something like this:
Cc @cyphar
The text was updated successfully, but these errors were encountered: