-
Notifications
You must be signed in to change notification settings - Fork 354
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 --preserve-fds, eliminate stray FD being passed into container #2893
Conversation
00bfd41
to
97574b1
Compare
@aidanhs , failing oci CI -
|
97574b1
to
c92c2e0
Compare
The failure was a problem with some changes that I made to try and make closing FDs happen later - unfortunately seccomp got in the way. I've now backed out those changes as strictly speaking they were unnecessary. Have run the tests locally and they seem to pass. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your PR! Probably you are right, but I'm not sure. So, may I ask you to write an e2e test to clarify?
https://containers.github.io/youki/developer/e2e/rust_oci_test.html
Also, I've updated the original PR. Thanks for your advice. It makes sense a lot.
#2663
c92c2e0
to
fc5c131
Compare
There are now three integration tests:
This was harder than I expected because:
|
3b83a72
to
20d9be4
Compare
} | ||
|
||
fn open_devnull_no_cloexec() -> Result<(fs::File, RawFd)> { | ||
// Rust std by default sets cloexec, so we undo it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
/// can the test group be executed in parallel (both the tests | ||
/// within it, and alongside other test groups) | ||
parallel: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@YJDoc2 WDYT? I prefer to introduce this flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@YJDoc2 Do you want to have additional time to check it?
@@ -545,3 +546,47 @@ pub fn test_io_priority_class(spec: &Spec, io_priority_class: IOPriorityClass) { | |||
eprintln!("error ioprio_get expected priority {expected_priority:?}, got {priority}") | |||
} | |||
} | |||
|
|||
pub fn validate_fd_control(_spec: &Spec) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aidanhs May I ask you to resolve the conflicts? Others look good to me. |
Hey, I'll need some time to check the changes done in test functions. probably till this weekend. |
Signed-off-by: Aidan Hobson Sayers <[email protected]>
Signed-off-by: Aidan Hobson Sayers <[email protected]>
Test harness additionally needed to support 1. tests that cannot run in parallel 2. tests that need to customise create arguments Signed-off-by: Aidan Hobson Sayers <[email protected]>
Signed-off-by: Aidan Hobson Sayers <[email protected]>
20d9be4
to
32d5dde
Compare
Have rebased and now use the new |
Signed-off-by: Yashodhan Joshi <[email protected]>
Signed-off-by: Yashodhan Joshi <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM from test side.
Hey just a couple of comments here :
|
The two commits look fine. Personally I'd have left in the TODO at f302ea0#diff-d45900789a82e7875774e0c4bdae819850026ea66be9d25fa96a4b5d736ecdf7L21, modifying it to say "the CreateOptions argument could be used to pass the pidfile" - I added that comment because I saw the test could be cleaned up to use existing test functions, but I didn't want to spend effort on it for this PR so a note for future visitors. But I don't mind either way! |
@yihuaf Could you mind double-checking this PR for sure? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch and good job on the detailed tests.
@@ -279,6 +283,11 @@ impl Syscall for LinuxSyscall { | |||
errno | |||
})?; | |||
|
|||
close(newroot).map_err(|errno| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this explicit close required? Does the O_CLOEXEC
take care of closing after the function returns, or there is a specific reason the fd will not be closed unless we explicitly call close?
// to ensure we don't leak any file descriptors to the intermediate process. | ||
// Please refer to https://github.com/opencontainers/runc/security/advisories/GHSA-xr7r-f8xq-vfvv for more details. | ||
let syscall = container_args.syscall.create_syscall(); | ||
syscall.close_range(0).map_err(|err| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this revert is correct. Reading the security advisory, the action called out in the advisory is to set O_CLOEXEC
on all FDs other than stdio. The close_range
in the --preserve-fds
check in the init process should take care of all this case.
You are correct. The Further, your analysis is correct. The preserve-fds logic works correctly before the fix. Leaking the |
Thanks all! |
#2663 seems to have broken preserve-fds by unconditionally closing everything aside from stdio in the container main process.
I've fixed this by effectively reverting part of that PR. Typically I'd expect it to be a bad idea to revert security fixes, but unfortunately I don't really understand much of the diagnosis at #2663 and can't get it to stack up against my observations of previous and current behavior of youki.
Specifically:
I checked out 04f8f2d (the commit before the fix PR was merged) and couldn't reproduce this. With a default config generated by
youki spec
and a command of"ls", "-al", "/proc/self/fd"
I get the following output:This is what I'd expect - preserve fds (#177) already closes all the FDs in the init process before execution. FD 3 here is
ls
opening the directory.I can reproduce a leak by passing
--preserve-fds 10
(I get something at FD 7, which seems to be the rootfs being referenced) - but the fix solves this by just making the preserve-fds flag useless. I fix this by actually closing the culprit directory, and then additionally verifying by hand that passing--preserve-fds 100
does not list any other stray FDs that are being passed down.Again I don't understand this - the preserve-fds PR does exactly that by marking everything as O_CLOEXEC before process execution? It is not ideal that passing
--preserve-fds
leaks something, but I've fixed that in this PR in a more precise way.I also don't understand this - the preserve fds PR sets the O_CLOEXEC flag in the init process, before execing the user process. It seems better to do this as late as possible, so fewer file descriptors can slip in? The vulnerability fix puts it in the main process, giving ample opportunity for other FDs to be opened and leaked.
Perhaps there's more detail that would help my confusion in the review in the private repository that would help? Per:
Edit: oops, sorry for the mentions, those were unintentional