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

net: ReadMsgUnix should pass MSG_CMSG_CLOEXEC on Linux #42765

Open
rittneje opened this issue Nov 22, 2020 · 7 comments · May be fixed by #42768
Open

net: ReadMsgUnix should pass MSG_CMSG_CLOEXEC on Linux #42765

rittneje opened this issue Nov 22, 2020 · 7 comments · May be fixed by #42768
Milestone

Comments

@rittneje
Copy link

@rittneje rittneje commented Nov 22, 2020

The implementation of ReadMsgUnix ultimately does not pass any flags to the recvmsg syscall. This means that any file descriptors sent in via a Unix rights SCM message will not be marked with the close-on-exec flag. For consistent behavior with how Go handles all other file descriptors on Linux, the MSG_CMSG_CLOEXEC flag should be passed. The need for this is the same as all other uses of the various CLOEXEC flags - to prevent a race condition where a child process is forked (and exec'd) after the file descriptor is created and before it can be marked close-on-exec, and is thus leaked.

Unfortunately, I don't think there's any equivalent of this flag on Mac, so there's no easy way to address the inherent race condition there.

@HowJMay
Copy link
Contributor

@HowJMay HowJMay commented Nov 22, 2020

Let me take this issue.
I will use func Recvmsg to replace the current function call of func (*FD) ReadMsg

HowJMay added a commit to HowJMay/go that referenced this issue Nov 22, 2020
As mentioned in golang#42765, calling `recvmsg` syscall on Linux should come
with `MSG_CMSG_CLOEXEC` flag.

Fixes golang#42765
@HowJMay HowJMay linked a pull request that will close this issue Nov 22, 2020
@gopherbot
Copy link

@gopherbot gopherbot commented Nov 22, 2020

Change https://golang.org/cl/272226 mentions this issue: net: Pass MSG_CMSG_CLOEXEC flag in ReadMsgUnix

HowJMay added a commit to HowJMay/go that referenced this issue Nov 23, 2020
As mentioned in golang#42765, calling `recvmsg` syscall on Linux should come
with `MSG_CMSG_CLOEXEC` flag.

Fixes golang#42765
HowJMay added a commit to HowJMay/go that referenced this issue Nov 23, 2020
As mentioned in golang#42765, calling `recvmsg` syscall on Linux should come
with `MSG_CMSG_CLOEXEC` flag.

Fixes golang#42765
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Nov 23, 2020

I think that ideally we should aim for the same behavior on all Unix systems. That means that when MSG_CMSG_CLOEXEC is available we should use it, and when it is not available we should, if len(oob) > 0, acquire syscall.ForkLock and then make sure that we set the O_CLOEXEC flag on any descriptors.

@rittneje
Copy link
Author

@rittneje rittneje commented Nov 23, 2020

@ianlancetaylor To prevent all leakages, you would have to hold the lock during the recvmsg syscall. Does that have the potential to cause a problem?

Assuming it's okay, then yes, you could parse the SCMs, iterate through all of them, and then parse each one where Level == syscall.SOL_SOCKET && Type == syscall.SCM_RIGHTS. However, in practice, this will unfortunately lead to double parsing.

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Nov 24, 2020

Good point, we can't hold ForkLock while waiting for network I/O. I guess we'll have to allow for a window when the descriptor is not marked close-on-exec.

Yes, double parsing will be required, but only on systems that don't support MSG_CMSG_CLOEXEC. That set of systems will presumably diminish over time

@HowJMay
Copy link
Contributor

@HowJMay HowJMay commented Nov 25, 2020

So for an OS that MSG_CMSG_CLOEXEC is available, we can use syscall.Recvmsg() directly with MSG_CMSG_CLOEXEC flag. And if MSG_CMSG_CLOEXEC is not available, then double parse the SCMs?

@rittneje
Copy link
Author

@rittneje rittneje commented Nov 25, 2020

Yes, "double parse" in as much as the net package will parse the SCMs to find all the fds, and then the actual client will presumably parse the SCMs again to use the fds. It also sounds like you need not bother with the fork lock at all since there's no way to fully guarantee the fds won't be leaked anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

4 participants
You can’t perform that action at this time.