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

runtimetest: add validation of mount order #88

Closed
wants to merge 1 commit into from

Conversation

cyphar
Copy link
Member

@cyphar cyphar commented Jun 2, 2016

In order to make sure that runtimes correctly implement the ordering of
[]spec.Mount, we need to check /proc/1/mountinfo (keeping in mind that
Unix allows a user to mount over the top of an existing mountpoint --
thus masking it). We only run this test if there happen to be two
mountpoints in the list where one is a parent of the other.

An extension of this might be to check all of the nested mounts
specified in the spec.

Signed-off-by: Aleksa Sarai asarai@suse.de

@wking
Copy link
Contributor

wking commented Jun 2, 2016 via email

@cyphar
Copy link
Member Author

cyphar commented Jun 2, 2016

I just realised this implementation is way too crazy. We should just iterate through /proc/1/mountinfo and then make sure that (from the bottom to make sure we don't get false positives from the parent processes' mount namespace) the order is right.

@liangchenye
Copy link
Member

A little overlap :) @wking But luckily I'm working on mount existence validation and Aleksa is working on mount order.
@cyphar I think we can share and use a same mount lib.
In #81 I used docker/pkg/mount at the beginning, now I pull part of that into ocitools/cmd/runtimetest/mount.
The advantage of adding mount sub directory is we could support multiple platforms.
Would you mind to rebase your code once #81 is done?

@cyphar
Copy link
Member Author

cyphar commented Jun 2, 2016

@liangchenye Sure, but I can't remember if docker/pkg/mount uses mountinfo or mount. We need to use mountinfo if we want to get the right list of mountpoints (specifically for masked ones).

@liangchenye
Copy link
Member

@cyphar it uses mountinfo, the mountinfo struct also has 'parentID' field.
https://github.com/liangchenye/ocitools/blob/mount/cmd/runtimetest/mount/mountinfo_linux.go

@liangchenye liangchenye mentioned this pull request Jun 2, 2016
76 tasks
@cyphar
Copy link
Member Author

cyphar commented Jun 2, 2016

Okay cool, we can use that then. Less work for me. :P

@mrunalp
Copy link
Contributor

mrunalp commented Jun 7, 2016

#81 has been merged so this can be updated. Thanks!

@Mashimiao
Copy link

ping @cyphar need rebase and update.

@cyphar
Copy link
Member Author

cyphar commented Oct 7, 2016

I haven't looked at the code in a few months, but I think I've updated this properly. PTAL.

@@ -489,6 +605,7 @@ func validate(context *cli.Context) error {
validateMaskedPaths,
validateROPaths,
validateOOMScoreAdj,
validateMountOrder,

Choose a reason for hiding this comment

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

As mount is not Linux specified, I think we'd better put it into defaultValidations.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup. Fixed. I forgot that I switched to pkg/mount. :P

}

cparts := filepath.SplitList(child)
for i, part := range filepath.SplitList(parent) {

Choose a reason for hiding this comment

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

I think here may be a little problem.
I'm not sure what SplitList() here works for. Is a mount destination can be "pathA:pathB"?
But path in Windows generally looks like "C:\test". And mount path is not allowed nested in Windows.

Copy link
Member Author

@cyphar cyphar Oct 7, 2016

Choose a reason for hiding this comment

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

I'm not sure what SplitList() here works for. Is a mount destination can be "pathA:pathB"?

SplitList splits a path into each component. So "A/B" becomes []string{"A", "B"}. This code is checking if one path is lexically the parent of another. While this isn't bulletproof there isn't a nice way to deal with other cases like symlinks (though I could try using ResolveSymlinks).

But path in Windows generally looks like "C:\test". And mount path is not allowed nested in Windows.

I don't use Windows. If Windows doesn't support nested mounts, then this code is not useful for Windows (there is no meaningful concept of "order" when mounting things if you can't mask mounts with other mounts).

Copy link

@Mashimiao Mashimiao Oct 7, 2016

Choose a reason for hiding this comment

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

I'm not sure what SplitList() here works for. Is a mount destination can be "pathA:pathB"?

SplitList splits a path into each component. So "A/B" becomes []string{"A", "B"}. This code is checking if one path is lexically the parent of another. While this isn't bulletproof there isn't a nice way to deal with other cases like symlinks (though I could try using ResolveSymlinks).

I think SplitList is not used for that.
In go document's example, it works like "/a/b/c:/usr/bin" becomes []string{"/a/b/c", "/usr/bin"}.
And I tested, it really works like this.
Did I miss something?

But path in Windows generally looks like "C:\test". And mount path is not allowed nested in Windows.

I don't use Windows. If Windows doesn't support nested mounts, then this code is not useful for Windows.

In config.md of runtime-spec, "For the Windows operating system, one mount destination MUST NOT be nested within another mount. (Ex: c:\foo and c:\foo\bar)."
So, I think we should make exception for Windows.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think SplitList is not used for that.

You're quite right. I've fixed it to use filepath.ToSlash and then strings.Split.

So, I think we should make exception for Windows.

Sure. I've added it using runtime.GOOS -- is that fine?

Choose a reason for hiding this comment

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

I think it's fine

@cyphar cyphar force-pushed the add-mount-order-check branch 3 times, most recently from d9dddea to a282e7c Compare October 7, 2016 09:47
In order to make sure that runtimes correctly implement the ordering of
[]spec.Mount, we need to check /proc/1/mountinfo (keeping in mind that
Unix allows a user to mount over the top of an existing mountpoint --
thus masking it). We only run this test if there happen to be two
mountpoints in the list where one is a parent of the other. We also
don't run it on Windows.

An extension of this might be to check all of the nested mounts
specified in the spec.

Signed-off-by: Aleksa Sarai <asarai@suse.de>
return fmt.Errorf("expected %q to be a mountpoint", A)
}

// B must be a mountpoint iff A was first.

Choose a reason for hiding this comment

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

iff -> if

Copy link
Member Author

@cyphar cyphar Oct 8, 2016

Choose a reason for hiding this comment

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

iff == if and only if. It's a mathematical statement which is like a two-way if-then statement. https://en.wikipedia.org/wiki/If_and_only_if

Choose a reason for hiding this comment

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

Oh, got it.
But I think using general and easy understood expression may be better.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll write out the words "if and only if" if it'll make you feel better. The reason I wrote it like that is because the test is testing that precise statement, namely that B will only be a mountpoint if A was first (and that if A is first then B will be a mountpoint).

cparts := strings.Split(child, "/")
for i, part := range strings.Split(parent, "/") {
if cparts[i] != part {
return false

Choose a reason for hiding this comment

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

maybe we use filepath.Rel() can reduce the loop

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure. I remember that I didn't use it for /some/ reason but I can't remember what it was -- so we'll just switch it.

@Mashimiao
Copy link

It seems currently we can just check one pair of parent and child mount points.
For the whole case, how about:
first, find all pairs of parent, child mount points
second , check whether all pairs are mounted in valid order

@cyphar how do you think about it?

@cyphar
Copy link
Member Author

cyphar commented Oct 8, 2016

We could do that. It's just probably going to be a bit tricky to track which orderings matter (if you have mountpoints for /a, /a/b, /a/b/c in arbitrary orders what is an efficient way to check it correctly). I'm also quite busy with other patches at the moment, so if you want to carry it yourself feel free.

As I said in the actual patch, that is something we could do. It's just a bit tricky to actually test properly. I can probably take a look at it in a few weeks. Right now I'm swamped with cri-o and some runC PRs. And also university exams.

@Mashimiao
Copy link

@cyphar OK, got it.

@Mashimiao
Copy link

close in favour of #444 and #456

@Mashimiao Mashimiao closed this Sep 6, 2017
@cyphar cyphar deleted the add-mount-order-check branch September 6, 2017 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants