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

feat(sandbox): add fd sandbox #857

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Conversation

n0toose
Copy link
Member

@n0toose n0toose commented Jan 8, 2025

No description provided.

@n0toose n0toose requested a review from jounathaen January 8, 2025 21:52
@n0toose
Copy link
Member Author

n0toose commented Jan 8, 2025

this was slightly more complex than expected because of unlink, need to take a more careful look at the correctness, but this is a proof of concept

image

Copy link

codecov bot commented Jan 8, 2025

Codecov Report

Attention: Patch coverage is 55.66038% with 47 lines in your changes missing coverage. Please review.

Project coverage is 71.31%. Comparing base (da4b63e) to head (6bd9b26).

Files with missing lines Patch % Lines
src/isolation/filemap.rs 44.18% 24 Missing ⚠️
src/hypercall.rs 56.81% 19 Missing ⚠️
src/linux/x86_64/kvm_cpu.rs 78.94% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #857      +/-   ##
==========================================
- Coverage   71.86%   71.31%   -0.56%     
==========================================
  Files          23       23              
  Lines        3064     3134      +70     
==========================================
+ Hits         2202     2235      +33     
- Misses        862      899      +37     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@n0toose n0toose marked this pull request as draft January 8, 2025 22:08
@n0toose n0toose force-pushed the sandbox-fd branch 2 times, most recently from bf3f183 to 2bbae9b Compare January 8, 2025 22:14
@n0toose
Copy link
Member Author

n0toose commented Jan 8, 2025

this definitely works with both temporary files and mapped ones (and mapped directories), but breaks with hello_c and rusty_demo, as rusty_demo seemingly directly writes to 2

image

@n0toose
Copy link
Member Author

n0toose commented Jan 8, 2025

works now

image

@jounathaen
Copy link
Member

Have you also considered the special file descriptors (1,2 & 3) that are opened on the hypervisor per default?

@n0toose
Copy link
Member Author

n0toose commented Jan 9, 2025

Have you also considered the special file descriptors (1,2 & 3) that are opened on the hypervisor per default?

In my bruteforcing attempts to get this to work, I think I completely forgot about 3 existing.

1 is already being handled by the hypercall, have to investigate the other case scenarios

		if syswrite.fd == 1 {
			// fd 0 is stdout
			let bytes = unsafe {
				parent_vm
					.mem
					.slice_at(guest_phys_addr, syswrite.len)
					.map_err(|e| {
						io::Error::new(
							io::ErrorKind::InvalidInput,
							format!("invalid syswrite buffer: {e:?}"),
						)
					})?
			};

@n0toose
Copy link
Member Author

n0toose commented Jan 9, 2025

I presume that I'll probably have to take note of stdin's security implications and see if I can handle that case.

@n0toose n0toose marked this pull request as ready for review January 10, 2025 10:56
@n0toose
Copy link
Member Author

n0toose commented Jan 10, 2025

All checks pass. This change should probably be manually tested on macOS too, just to be sure. I will leave the commit squashing for later.

Copy link
Member Author

@n0toose n0toose left a comment

Choose a reason for hiding this comment

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

Given that this change is somewhat complex, I left a few remarks addressing some of its behavior as well as specific points for review, so as to prevent churn / bugs.

@@ -171,8 +190,7 @@ pub fn write<B: VirtualizationBackend>(
)
.unwrap();

if syswrite.fd == 1 {
// fd 0 is stdout
if syswrite.fd == 1 || syswrite.fd == 2 {
Copy link
Member Author

Choose a reason for hiding this comment

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

Not 1000% sure whether this is sane.

Copy link
Member

Choose a reason for hiding this comment

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

Looks fine, at least until we have separate handling for stderr

src/isolation/filemap.rs Show resolved Hide resolved
src/isolation/filemap.rs Show resolved Hide resolved
src/hypercall.rs Outdated Show resolved Hide resolved
src/hypercall.rs Outdated Show resolved Hide resolved
src/hypercall.rs Outdated
pub fn close(sysclose: &mut CloseParams, file_map: &mut UhyveFileMap) {
if file_map.get_fd_presence(sysclose.fd.into_raw_fd()) {
unsafe { sysclose.ret = libc::close(sysclose.fd) }
if sysclose.ret >= 0 {
Copy link
Member Author

Choose a reason for hiding this comment

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

Is allowing close on the fds 0, 1, 2 violating isolation? Should we handle this in a different way?

Copy link
Member

Choose a reason for hiding this comment

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

I found this comment: https://stackoverflow.com/a/4973065/6551168
So it might be ok for the kernel to close them, but as uhyve still needs them, I think ignoring this close is the way to go.

@n0toose n0toose force-pushed the sandbox-fd branch 3 times, most recently from 823ea51 to 5802165 Compare January 10, 2025 12:09
This extends UhyveFileMap with a sandbox mapping file descriptors to
maps, so as to prevent the opening of file descriptors that belong to
Uhyve (or to that of other VMs, when Uhyve exposes such functionality
to the user).

There is no Linux equivalent of unlink that is capable of removing
files using a file descriptor. We did not want to keep a file
descriptor of an unlinked file available to the VM. Therefore, we
used a BiMap that associate fds to guest paths and guest paths to fds
(instead of a simple container containing fds).
If an unlink takes place while the file is open, this would make the
subsequent close fail, as soon as the file object goes out of scope
and gets dropped. We introduced a HashSet that temporarily caches
such unlinked file descriptors.
@n0toose
Copy link
Member Author

n0toose commented Jan 10, 2025

caught a bug that occurs when unlinking a file and then subsequently dropping it - unlinked files present in the map will cause an error later, as we'd have already removed the fd during an unlink. this keeps it in a HashSet until it is actually dropped - this only happens if the file is already present in the fdmap map<>fd association

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.

2 participants