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

validate fds --preserve-fds #7125

Merged
merged 1 commit into from
Aug 5, 2020

Conversation

QiWang19
Copy link
Contributor

@QiWang19 QiWang19 commented Jul 28, 2020

validate file descriptors passed from podman run and podman exec --preserve-fds.

Signed-off-by: Qi Wang [email protected]

@@ -956,6 +956,11 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co

if ctr.config.PreserveFDs > 0 {
for fd := 3; fd < int(3+ctr.config.PreserveFDs); fd++ {
f := os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd))
Copy link
Member

Choose a reason for hiding this comment

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

I think we need to do this in cmd/podman - we probably have at least this many open FDs of our own by this point (c/storage locks, etc)

@QiWang19 QiWang19 force-pushed the fd-validate branch 3 times, most recently from 4bb0211 to 40a9bfa Compare July 29, 2020 19:54
@QiWang19
Copy link
Contributor Author

Add validation code. But this does not fix panic #6653
and system tests still fail.

[+0116s] # #|     FAIL: container read input from fd 4
[+0116s] # #| expected: 'MSdO93BIGQj5i44nOS4D'
[+0116s] # #|   actual: 'time="2020-07-29T15:16:22-04:00" level=error msg="unable to close file fd-4: \"close fd-4: bad file descriptor\""'
[+0116s] # #|         > 'time="2020-07-29T15:16:22-04:00" level=error msg="unable to cleanup network for container a22fc3e3b4aed17632f3861dfa409fadce23f10b40395526679b67f2a2d1c952: \"error closing network namespace for container a22fc3e3b4aed17632f3861dfa409fadce23f10b40395526679b67f2a2d1c952: Failed to close \\\"/run/user/30150/netns/cni-b4c7ec7e-c192-bc0a-c8e5-992b3c85d99f\\\": close /run/user/30150/netns/cni-b4c7ec7e-c192-bc0a-c8e5-992b3c85d99f: bad file descriptor\""'

@mheon
Copy link
Member

mheon commented Jul 29, 2020

Go may well be happy to create a new file from an invalid FD - we probably need a test that the FD is actually valid.

@rhatdan
Copy link
Member

rhatdan commented Jul 29, 2020

Yes look like it succeeds.

package main

import (
	"fmt"
	"os"
)

func main() {
	fd1 := os.NewFile(1, "stdout")
	if fd1 == nil {
		fmt.Println("stdout Failed")
	}
	fd5 := os.NewFile(500, "failure")
	if fd5 == nil {
		fmt.Println("failure succeeded")
	} else {
		fmt.Println("failure failed", fd5)
	}

}
go run t.go
failure failed &{0xc0000661e0}

@edsantiago
Copy link
Member

Just a heads-up: please see #7144, it looks germane.

@rhatdan
Copy link
Member

rhatdan commented Jul 29, 2020

This works.

package main

import (
	"fmt"
	"golang.org/x/sys/unix"
)

func main() {

	if _, err := unix.FcntlInt(1, unix.F_GETFL, 0); err != nil {
		fmt.Println(1, err)
	}
	if _, err := unix.FcntlInt(400, unix.F_GETFL, 0); err != nil {
		fmt.Println(400, err)
		if err == unix.EBADF {
			fmt.Println("BINGO")
		} else {
			fmt.Println("BoNGO")
		}

	}

@rhatdan
Copy link
Member

rhatdan commented Jul 29, 2020

The call seems to return err = 0 on success. Not nil, but I think as long as we check for EBADF, then we should be safe.

@QiWang19
Copy link
Contributor Author

I didn't see the output from this success case, I guess the err is nil, can't we just return err as long as the err != nil, without checking EBADF?

if _, err := unix.FcntlInt(1, unix.F_GETFL, 0); err != nil {
		fmt.Println(1, err)
	}

@rhatdan
Copy link
Member

rhatdan commented Jul 30, 2020

The preserve-fds should fail on remote client and should also not be executed on Windows.
When I was testing my test program, I was constantly seeing err != nil, Not sure why. But I added the check for EBADF to make sure the error we were looking for was correct.

@rhatdan
Copy link
Member

rhatdan commented Jul 30, 2020

How about?

import (
	"fmt"
	"syscall"

	"github.com/pkg/errors"
	"golang.org/x/sys/unix"
)

func verifyFD(fd int) error {
	if _, err := unix.FcntlInt(uintptr(fd), unix.F_GETFL, 0); err != nil && err != syscall.Errno(0) {
		return errors.Wrapf(err, "failed to verify %d file descriptor", fd)
	}
	return nil
}

var fd uint
for fd = 3; fd < 3+runOpts.PreserveFDs; fd++ {
_, err := unix.FcntlInt(uintptr(fd), unix.F_GETFL, 0); err != nil && err != syscall.Errno(0) {
return err
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be a wrapped error to tell the user which FD is invalid.

Copy link
Member

Choose a reason for hiding this comment

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

The FcntlInt code needs to be in a util_linux.go branch.
Does podman-remote throw an error when you do --preserver-fds?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added utils_linux.go --preserver-fds is disabled in remote.

Copy link
Member

Choose a reason for hiding this comment

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

I think you want to have similar code in exec.go and perhaps attach.go as well?

@QiWang19 QiWang19 force-pushed the fd-validate branch 3 times, most recently from ae93f3c to a10c42e Compare July 30, 2020 14:50
@rhatdan
Copy link
Member

rhatdan commented Jul 30, 2020

Your going to need verifyfd in the utils_unsupported.go as well.

@QiWang19 QiWang19 changed the title validate fds run --preserve-fds validate fds --preserve-fds Jul 30, 2020
@rhatdan
Copy link
Member

rhatdan commented Jul 30, 2020

Almost there, now I need a test to make sure it errors out with the correct error

podman run --preserve-fd 2 alpine true

Should return an error containing bad fd.

func verifyFD(fd int) error {
if _, err := unix.FcntlInt(uintptr(fd), unix.F_GETFL, 0); err != nil && err != syscall.Errno(0) {
return errors.Wrapf(err, "failed to verify %d file descriptor", fd)
}
Copy link
Member

Choose a reason for hiding this comment

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

this won't detect fds that are opened by Podman.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If run podman run --preserve-fds, it's possible that the file descriptor I want to pass has already opened by podman, so this won't detect that?

@giuseppe
Copy link
Member

I don't think it can be solved at this level.

Files can be opened by the Go runtime so we don't know what FDs are valid and what are not.

rootless already stores at startup what ds were open before the Go runtime kicks in. I think we could take advantage of that and be sure that these fds were already opened when the program started (open_files_set in rootless_linux.c).

@rhatdan
Copy link
Member

rhatdan commented Jul 31, 2020

Do we have access to these within golang?

@giuseppe
Copy link
Member

Do we have access to these within golang?

we will need to provide a wrapper, something like:

diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index d3e43e44d..6d353d2e2 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -225,6 +225,14 @@ can_use_shortcut ()
   return ret;
 }
 
+int
+is_fd_inherited(int fd)
+{
+  if (open_files_set == NULL || fd >= open_files_max_fd || fd < 0)
+    return false;
+  return FD_ISSET (fd % FD_SETSIZE, &(open_files_set[fd / FD_SETSIZE])) ? 1 : 0;
+}
+
 static void __attribute__((constructor)) init()
 {
   const char *xdg_runtime_dir;
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index b1f200cc2..2fe915cab 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -32,6 +32,7 @@ extern uid_t rootless_gid();
 extern int reexec_in_user_namespace(int ready, char *pause_pid_file_path, char *file_to_read, int fd);
 extern int reexec_in_user_namespace_wait(int pid, int options);
 extern int reexec_userns_join(int pid, char *pause_pid_file_path);
+extern int is_fd_inherited(int fd);
 */
 import "C"
 
@@ -495,3 +496,10 @@ func ConfigurationMatches() (bool, error) {
 
        return matches(GetRootlessGID(), gids, currentGIDs), nil
 }
+
+func IsFdInherited(fd int) bool {
+       if int(C.is_fd_inherited(C.int(fd))) > 0 {
+               return true
+       }
+       return false
+}
diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go
index 1499b737f..203d56acb 100644
--- a/pkg/rootless/rootless_unsupported.go
+++ b/pkg/rootless/rootless_unsupported.go
@@ -64,3 +64,7 @@ func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) {
 func ReadMappingsProc(path string) ([]idtools.IDMap, error) {
        return nil, nil
 }
+
+func IsFdInherited(fd int) bool {
+       return false
+}

@QiWang19
Copy link
Contributor Author

@giuseppe Are you going to open PR to fix this? Do you mind I grab the code above PR?

@giuseppe
Copy link
Member

@giuseppe Are you going to open PR to fix this? Do you mind I grab the code above PR?

please go forward and use it in this PR

@QiWang19
Copy link
Contributor Author

@giuseppe Any ideas why the test fail? podman run --preserve-fds & podman exec preserve fds sanity check https://api.cirrus-ci.com/v1/task/5511032351227904/logs/integration_test.log

@giuseppe
Copy link
Member

giuseppe commented Aug 1, 2020

@giuseppe Any ideas why the test fail? podman run --preserve-fds & podman exec preserve fds sanity check https://api.cirrus-ci.com/v1/task/5511032351227904/logs/integration_test.log

looks like there is an issue in the code I've provided you, can you try with this version for is_fd_inherited?


int
is_fd_inherited(int fd)
{
  if (open_files_set == NULL || fd > open_files_max_fd || fd < 0)
    return 0;

  return FD_ISSET(fd % FD_SETSIZE, &(open_files_set[fd / FD_SETSIZE])) ? 1 : 0;
}

@QiWang19 QiWang19 linked an issue Aug 3, 2020 that may be closed by this pull request
@QiWang19
Copy link
Contributor Author

QiWang19 commented Aug 3, 2020

@edsantiago @rhatdan @giuseppe PTAL

@rhatdan
Copy link
Member

rhatdan commented Aug 3, 2020

/approve
LGTM
If you need to do another PR, you should check the error message in your test to make sure it says something like
failed to verify %d file descriptor

@openshift-ci-robot openshift-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 3, 2020
@QiWang19 QiWang19 force-pushed the fd-validate branch 2 times, most recently from 534d1e9 to 7f41015 Compare August 3, 2020 20:34
@@ -110,6 +111,12 @@ func exec(_ *cobra.Command, args []string) error {

execOpts.Envs = envLib.Join(execOpts.Envs, cliEnv)

for fd := 3; fd < int(3+execOpts.PreserveFDs); fd++ {
if !rootless.IsFdInherited(fd) {
return errors.Errorf("failed to verify %d file descriptor", fd)
Copy link
Member

Choose a reason for hiding this comment

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

This is confusing. Shouldn't it read file descriptor %d?

Copy link
Member

Choose a reason for hiding this comment

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

Actually" failed to verify" itself doesn't really make sense to me as an end user. How about "file descriptor %d is not open" or "...is not available for I/O" or something along those lines? @giuseppe since you have the best understanding of what this code is actually validating, could you suggest a phrasing that is both technically accurate and helpful to the end user?

Copy link
Member

Choose a reason for hiding this comment

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

The code is verifying it a file descriptor actually exists. I think "file descriptor %d is not open" is correct.
Users are telling podman to leak file descriptor 3 and 4 in to a container, and if 3 and 4 are not open, then something went wrong.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe "file descriptor %d is not available - the preserve-fds option requires that file descriptors must be passed"

@@ -125,6 +125,11 @@ func run(cmd *cobra.Command, args []string) error {
if err := createInit(cmd); err != nil {
return err
}
for fd := 3; fd < int(3+runOpts.PreserveFDs); fd++ {
if !rootless.IsFdInherited(fd) {
return errors.Errorf("failed to verify %d file descriptor", fd)
Copy link
Member

Choose a reason for hiding this comment

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

same here

@QiWang19 QiWang19 force-pushed the fd-validate branch 3 times, most recently from d302eac to 7bffb4f Compare August 4, 2020 18:01
validate file descriptors passed from podman run and podman exec --preserve-fds.

Signed-off-by: Qi Wang <[email protected]>
@QiWang19
Copy link
Contributor Author

QiWang19 commented Aug 4, 2020

@edsantiago @rhatdan @mheon PTAL

@rhatdan
Copy link
Member

rhatdan commented Aug 5, 2020

LGTM

Copy link
Member

@giuseppe giuseppe left a comment

Choose a reason for hiding this comment

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

/lgtm

@openshift-ci-robot openshift-ci-robot added the lgtm Indicates that a PR is ready to be merged. label Aug 5, 2020
@openshift-ci-robot
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: giuseppe, QiWang19, rhatdan

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-merge-robot openshift-merge-robot merged commit 4797190 into containers:master Aug 5, 2020
@github-actions github-actions bot added the locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments. label Sep 24, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. locked - please file new issue/PR Assist humans wanting to comment on an old issue or PR with locked comments.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

podman exec --preserve-fds=2: panic
7 participants