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: correctly check for a readable directory #625

Merged

Conversation

dongsupark
Copy link
Contributor

So far tests linux_masked_paths.t & linux_readonly_paths.t have failed with error messages like cannot test read access for "/dirname". That's because testReadAccess() only checked if the given path is a regular file, returning an error for every other case. testReadAccess() should actually take care of another case of a given
path being a directory, to be able to correctly check for its readability.

Also run testFileReadAccess() or testFileWriteAccess() for all file types, not only a normal file, because the runtime spec does not mandate the type of masked files. It could be actually any file, including a character device like /dev/null, especially in case of runc.

Found by @alban.

Signed-off-by: Dongsu Park [email protected]

@alban
Copy link
Contributor

alban commented Apr 18, 2018

With this branch, I get correctly formatted TAP output for both linux_masked_paths.t and linux_readonly_paths.t.

But I get this test failure with runc:

$ sudo validation/linux_masked_paths.t
...
not ok 275 - cannot read masked path "/masked-dir"

Is it a bug in the test or in runc?

@alban
Copy link
Contributor

alban commented Apr 18, 2018

I think this needs 2 changes:

  • in validation/linux_masked_paths.go: create a file in testDir, so that the directory is not empty
  • in cmd/runtimetest/main.go::testDirectoryReadAccess(): don't ignore the return value of ioutil.ReadDir() with _ and return false when ioutil.ReadDir() gives an empty array.

The rationale is the same as explained in the comment of testFileReadAccess() (see also this discussion).

@dongsupark dongsupark force-pushed the dongsu/fix-readable-dir branch from bb2ceb4 to 76d61a3 Compare April 18, 2018 13:55
@dongsupark
Copy link
Contributor Author

@alban Done.
Created a file under testDir, and added a check for an empty array.

@alban
Copy link
Contributor

alban commented Apr 18, 2018

@dongsupark linux_masked_paths.t now works fine with runc but the test for linux_masked_paths.t became "skipped":

$ sudo validation/linux_masked_paths.t
...
ok 284 # SKIP linux.readonlyPaths not set

This is because testDir does not contain any files. Could you create a file in linux_readonly_paths.go in the same way you did in linux_masked_paths.go?

@dongsupark dongsupark force-pushed the dongsu/fix-readable-dir branch from 76d61a3 to c736b62 Compare April 18, 2018 15:05
@alban
Copy link
Contributor

alban commented Apr 18, 2018

I didn't test correctly before... It works for me now:

$ sudo validation/linux_masked_paths.t|grep -E 'mask|read'
ok 275 - cannot read masked path "/masked-dir"
ok 276 - cannot read masked path "/masked-file"
ok 284 # SKIP linux.readonlyPaths not set
$ sudo validation/linux_readonly_paths.t|grep -E 'mask|read'
ok 275 # SKIP linux.maskedPaths not set
ok 283 - "/readonly-dir" (linux.readonlyPaths[0]) is not writable
ok 284 - "/readonly-file" (linux.readonlyPaths[1]) is not writable

// In case of a directory, we should check its readability in a special way.
// In other files, we should not check its Mode explicitly, because the runtime
// spec does not mandate the type of masked files. It could be a regular file
// or a character file (/dev/null), which is the case for runtimes like runc.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd rather continue to test the mode. For example, the runtime may use a permission change to make a symlink read-only (or not read/writable at all), and we don't want to use testFileReadAccess to test symlink readability. I'd rather support /dev/null and other character devices with logic like:

switch fi.Mode&os.ModeType {
case 0, os.ModeDevice|os.ModeCharDevice:
    return testFileReadAccess(path)
case os.ModeDir:
    return restDirectoryReadAccess(path)
}
return false, fmt.Errorf("cannot test read access for %q (mode %d)", path, fi.Mode())

if files, err := ioutil.ReadDir(path); err != nil || len(files) == 0 {
// err from reading from a directory should not be considered as test failure,
// it just means that the test program successfully assessed that
// the directory is not readable.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we want to mask all errors like this. For example, we expect readonlyPaths entries to be readable, and I expect the tests should exit nonzero if, for example, this call returns an IsNotExist error. Can we follow testFileReadAccess and whitelist some errors as acceptable blockers (e.g. EACCES)? And can we also follow testFileReadAccess by only using non-empty test directories and treating successful-but-empty reads as “not readable”?

// In case of a directory, we should check its readability in a special way.
// In other files, we should not check its Mode explicitly, because the runtime
// spec does not mandate the type of masked files. It could be a regular file
// or a character file (/dev/null), which is the case for runtimes like runc.
Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer keeping the explicit mode checks to avoid calling testFileWriteAccess on types where its logic doesn't make sense. And if runtimes seek to block write access by binding /dev/null over the path in question, we'll need a more sophisticated check than what testFileWriteAccess does now, because you can certainly write a byte to /dev/null.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@wking I'm not sure I understand. Can you please elaborate how we can do a more sophisticated check for writeability of /dev/null?
I don't see a general way to check if a character device can be written. AFAIK the read/write behavior varies a lot for each character device. The best we can do is to check for the file mode, isn't it?

@dongsupark
Copy link
Contributor Author

@wking I pushed a commit to address your review comments as much as possible. I hope it looks ok to you.

if err != nil {
return err
}
defer tmpfile.Close()
Copy link

Choose a reason for hiding this comment

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

I didn't understand why do that. I tested it as if it had no effect on the results.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@q384566678 You're right.
What I wanted to do was defer os.Remove(tmpfile.Name()). Fixed it.

Dongsu Park added 2 commits June 6, 2018 12:36
So far tests linux_masked_paths.t & linux_readonly_paths.t have failed
with error messages like `cannot test read access for "/dirname"`.
That's because `testReadAccess()` only checked if the given path is a
regular file, returning an error for every other case.
`testReadAccess()` should actually take care of another case of a given
path being a directory, to be able to correctly check for its readability.

Also run `testFileReadAccess()` or `testFileWriteAccess()` for all file
types, not only a normal file, because the runtime spec does not mandate
the type of masked files. It could be actually a character device like
`/dev/null`, especially in case of runc.

Found by @alban.

Signed-off-by: Dongsu Park <[email protected]>
Instead of calling `testFileReadAccess()` for all file types, we should
strictly check for file types, to return `errAccess` for other file
types. This error will be skipped during error checks in
`validateMaskedPaths()` and `validateReadonlyPaths()`, so that further
tests can be done even when a single test failed.

Suggested by @wking

Signed-off-by: Dongsu Park <[email protected]>
@dongsupark dongsupark force-pushed the dongsu/fix-readable-dir branch from 04fc4ac to 708de67 Compare June 6, 2018 10:44
@zhouhao3
Copy link

zhouhao3 commented Jun 7, 2018

LGTM

@dongsupark Merge it first, and then you can rebase #644. Maybe we can also change masked-path test.

Approved with PullApprove

@zhouhao3 zhouhao3 merged commit b13ff6a into opencontainers:master Jun 7, 2018
@zhouhao3
Copy link

zhouhao3 commented Jun 7, 2018

Maybe we can also change masked-path test.

Sorry, I didn't notice that you had already done it. Well done!

@dongsupark dongsupark deleted the dongsu/fix-readable-dir branch June 7, 2018 06:06
@dongsupark
Copy link
Contributor Author

@q384566678 Done, rebased #643 as well as #644. :-)

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.

4 participants