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

fix an error caused by fd reuse race when starting runc init #4452

Merged
merged 1 commit into from
Oct 21, 2024

Conversation

lifubang
Copy link
Member

@lifubang lifubang commented Oct 17, 2024

Fixes: #4294

There is a race situation when we are opening a file, if there is a
small fd was closed at that time, maybe it will be reused by safeExe.
Because of Go stdlib fds shuffling bug, if the fd of safeExe is too
small, go stdlib will dup3 it to another fd, or dup3 a other fd to this
fd, then it will cause the fd type cmd.Path refers to a random path,
and it can lead to an error "permission denied" when starting the process.

Please see #4294 and golang/go#61751.

So we should not use the original fd of safeExe, but use the fd after
shuffled by Go stdlib. Because Go stdlib will guarantee this fd refers to
the correct file.

Signed-off-by: lfbzhm lifubang@acmcoder.com

@kolyshkin
Copy link
Contributor

Related: it would be nice to finish https://go-review.googlesource.com/c/go/+/515799

@lifubang
Copy link
Member Author

Related: it would be nice to finish https://go-review.googlesource.com/c/go/+/515799

Yes, but this logic is very complex, maybe need more time to test.

Another thing I want to know, the closed fd 6/7 is opened and closed by whom? I printed the first 6 fds:

fd 0 -> /dev/null
fd 1 -> /dev/pts/3
fd 2 -> /dev/pts/3
fd 3 -> /sys/fs/cgroup
fd 4 -> anon_inode:[eventpoll]
fd 5 -> anon_inode:[eventfd]

Maybe by go runtime?

@lifubang lifubang force-pushed the fix-fd-reuse-race branch 5 times, most recently from c9ed140 to ddc70a3 Compare October 21, 2024 06:39
There is a race situation when we are opening a file, if there is a
small fd was closed at that time, maybe it will be reused by safeExe.
Because of Go stdlib fds shuffling bug, if the fd of safeExe is too
small, go stdlib will dup3 it to another fd, or dup3 a other fd to this
fd, then it will cause the fd type cmd.Path refers to a random path,
and it can lead to an error "permission denied" when starting the process.
Please see opencontainers#4294 and <golang/go#61751>.
So we should not use the original fd of safeExe, but use the fd after
shuffled by Go stdlib. Because Go stdlib will guarantee this fd refers to
the correct file.

Signed-off-by: lfbzhm <lifubang@acmcoder.com>
Copy link
Member

@cyphar cyphar left a comment

Choose a reason for hiding this comment

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

This solution is simpler than the previous one. It's still a bit ugly but at least we are using ExtraFiles "properly" now.

I'll see if I have some time to rework the Go stdlib patch.

@cyphar cyphar added this to the 1.2.0 milestone Oct 21, 2024
@lifubang
Copy link
Member Author

It's still a bit ugly

Yes, another ugly place:

exePath = "/proc/self/fd/" + strconv.Itoa(int(safeExe.Fd()))

We should set a correct valut to exePath at this line, but it's hard to move the patch code to this place. Because append some files to p.ExtraFiles will cause more problems.
It indeed seems that it's the simplest way to fix the bug at this time.

@cyphar
Copy link
Member

cyphar commented Oct 21, 2024

Yeah, it would be nice if Go supported fork+fexecve natively, but I don't think there's even a nice backward-compatible way of doing that.

@cyphar cyphar mentioned this pull request Oct 21, 2024
21 tasks
Copy link
Member

@rata rata left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for fixing this nasty bug! I guess the debugging wasn't easy :D

// So we should not use the original fd of safeExe, but use the fd after
// shuffled by Go stdlib. Because Go stdlib will guarantee this fd refers to
// the correct file.
cmd.Path = "/proc/self/fd/" + strconv.Itoa(stdioFdCount+len(cmd.ExtraFiles)-1)
Copy link
Member

Choose a reason for hiding this comment

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

Super nit: I think it would be slightly more clear if we add a comment here: https://github.com/opencontainers/runc/blob/main/libcontainer/container_linux.go#L563 saying we will override it later.

Also, IMHO having this in a function and just calling it from https://github.com/opencontainers/runc/blob/main/libcontainer/container_linux.go#L563 seems slightly better. But I don't know if it feels safer, due to this nasty bug, to do it way later when we won't have a small fd number (to be extra cautious).

In any case, this is very subjective and if the code as is feels better, I'm completely fine. And even if we want to add it, this can be a follow-up PR.

@rata rata merged commit 5190d61 into opencontainers:main Oct 21, 2024
42 checks passed
@lifubang lifubang deleted the fix-fd-reuse-race branch October 21, 2024 12:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fd resue race causes runc init can't start due to a go stdlib bug
4 participants