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

libct/cg/OpenFile: fix/improve openat2 handling #3030

Merged
merged 2 commits into from
Sep 3, 2021

Conversation

kolyshkin
Copy link
Contributor

@kolyshkin kolyshkin commented Jun 15, 2021

  1. While looking into libct/cg: openat2 optimization not working across container boundaries #3026, I started writing a test case and found out that
    OpenFile("/sys/fs/cgroup", name) falls back to using open and fstatfs.
    This is caused by a peculiarities of its dir and name arguments handling,
    and was never discovered before because of the fallback.

    Fix the bug, add the test cases.

  2. Improve the error message for case like in the issue libct/cg: openat2 optimization not working across container boundaries #3026, based on @cyphar suggestion.

See commit messages for more details.

@kolyshkin kolyshkin requested review from cyphar and AkihiroSuda June 15, 2021 16:38
@kolyshkin kolyshkin changed the title libct/cg/OpenFile: improve openat2 handling libct/cg/OpenFile: fix/improve openat2 handling Jun 18, 2021
@kolyshkin
Copy link
Contributor Author

No need to backport it to 1.0 -- this is a bug, but the fallback works so the impact is pretty low

@kolyshkin kolyshkin added this to the 1.1.0 milestone Jul 12, 2021
@kolyshkin
Copy link
Contributor Author

This is a real bug fix, can @opencontainers/runc-maintainers PTAL 🙏🏻 ?

Fix reading cgroup files from the top cgroup directory, i.e.
/sys/fs/cgroup.

The code was working for for any subdirectory of /sys/fs/cgroup, but
for dir="/sys/fs/cgroup" a fallback (open and fstatfs) was used, because
of the way the function worked with the dir argument.

Fix those cases, and add unit tests to make sure they work. While at it,
make the rules for dir and name components more relaxed, and add test
cases for this, too.

While at it, improve OpenFile documentation, and remove a duplicated
doc comment for openFile.

Without these fixes, the unit test fails the following cases:

    file_test.go:67: case {path:/sys/fs/cgroup name:cgroup.controllers}: fallback
    file_test.go:67: case {path:/sys/fs/cgroup/ name:cgroup.controllers}: openat2 /sys/fs/cgroup//cgroup.controllers: invalid cross-device link
    file_test.go:67: case {path:/sys/fs/cgroup/ name:/cgroup.controllers}: openat2 /sys/fs/cgroup///cgroup.controllers: invalid cross-device link
    file_test.go:67: case {path:/ name:/sys/fs/cgroup/cgroup.controllers}: fallback
    file_test.go:67: case {path:/ name:sys/fs/cgroup/cgroup.controllers}: fallback
    file_test.go:67: case {path:/sys/fs/cgroup/cgroup.controllers name:}: openat2 /sys/fs/cgroup/cgroup.controllers/: not a directory

Here "fallback" means openat2-based implementation fails, and the fallback code
is used (and works).

Signed-off-by: Kir Kolyshkin <[email protected]>
Comment on lines +124 to +130
path := path.Join(dir, file)
if prepareOpenat2() != nil {
return openFallback(dir, file, flags, mode)
return openFallback(path, flags, mode)
}
reldir := strings.TrimPrefix(dir, cgroupfsPrefix)
if len(reldir) == len(dir) { // non-standard path, old system?
return openFallback(dir, file, flags, mode)
relPath := strings.TrimPrefix(path, cgroupfsPrefix)
if len(relPath) == len(path) { // non-standard path, old system?
return openFallback(path, flags, mode)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

To help reviewers.

Basically, the code previously assumed that dir starts with /sys/fs/cgroup/ (including the ending slash), which is not always the case (such as when calling OpenFile("/sys/fs/cgroup", "cgroup.controllers")). So, the fallback was used for this case, which is not correct.

This commit removes such assumption, using path.Join(dir, file) as a full path. This simplifies the code, and helps the case above, as well as something like OpenFile("/sys", "fs/cgroup/cgroup.controllers").

hqhq
hqhq previously approved these changes Aug 11, 2021
Copy link
Contributor

@hqhq hqhq left a comment

Choose a reason for hiding this comment

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

LGTM


// Enhance the Path in the error to contain the
// cgroupFd value and the directory it is opened to,
// for example: "@[fd 7:/!=/sys/fs/cgroup]/cpu.stat".
Copy link
Member

Choose a reason for hiding this comment

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

Is this convention already widely used, or newly invented here?

Rather than defining a new microformat, can we just wrap os.PathError with our own struct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this convention already widely used, or newly invented here?

Newly invented (originally comes from #3026 (comment))

We are already using "fd N" in a few places where we operate on a file descriptor:

[kir@kir-rhat runc]$ git grep PathError | grep -w fd
libcontainer/rootfs_linux.go:		return &os.PathError{Op: "fchdir", Path: "fd " + strconv.Itoa(newroot), Err: err}
libcontainer/rootfs_linux.go:		return &os.PathError{Op: "fchdir", Path: "fd " + strconv.Itoa(oldroot), Err: err}
libcontainer/setns_init_linux.go:		return &os.PathError{Op: "close log pipe", Path: "fd " + strconv.Itoa(l.logFd), Err: err}
libcontainer/standard_init_linux.go:		return &os.PathError{Op: "close log pipe", Path: "fd " + strconv.Itoa(l.logFd), Err: err}
vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go:		return "", &os.PathError{Op: "getsockopt", Path: "fd " + strconv.Itoa(int(fd)), Err: err}

but in this case something more useful is needed.

Rather than defining a new microformat, can we just wrap os.PathError with our own struct?

Done

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@AkihiroSuda PTAL ^^^

opencontainers/runc issue 3026 describes a scenario in which OpenFile
failed to open a legitimate existing cgroupfs file. Added debug
(similar to what this commit does) shown that cgroupFd is no longer
opened to "/sys/fs/cgroup", but to "/" (it's not clear what caused it,
and the source code is not available, but they might be using the same
process on the both sides of the container/chroot/pivot_root/mntns
boundary, or remounting /sys/fs/cgroup).

Consider such use incorrect, but give a helpful hint as two what is
going on by wrapping the error in a more useful message.

NB: this can potentially be fixed by reopening the cgroupFd once we
detected that it's screwed, and retrying openat2. Alas I do not have
a test case for this, so left this as a TODO suggestion.

Signed-off-by: Kir Kolyshkin <[email protected]>
@kolyshkin
Copy link
Contributor Author

@cyphar @thaJeztah @mrunalp PTAL

Copy link
Member

@thaJeztah thaJeztah 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!

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

Successfully merging this pull request may close these issues.

4 participants