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

Prevent parent directory from being opened without being preopened wasi #463

Merged
merged 2 commits into from
May 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Blocks of changes will separated by version increments.

## **[Unreleased]**

- [#463](https://github.com/wasmerio/wasmer/pull/463) Fix bug in WASI path_open allowing one level above preopened dir to be accessed
- [#461](https://github.com/wasmerio/wasmer/pull/461) Prevent passing negative lengths in various places in the runtime C API
- [#459](https://github.com/wasmerio/wasmer/pull/459) Add monotonic and real time clocks for wasi on windows
- [#447](https://github.com/wasmerio/wasmer/pull/447) Add trace macro (`--features trace`) for more verbose debug statements
Expand Down
180 changes: 100 additions & 80 deletions lib/wasi/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1463,97 +1463,117 @@ pub fn path_open(
"Looking for file {} in directory {:#?}",
file_name, cumulative_path
);

cumulative_path.push(file_name);
let file_path = cumulative_path;

let out_fd = if let Kind::Dir { entries, .. } = &mut state.fs.inodes[cur_dir_inode].kind {
if let Some(child) = entries.get(file_name).cloned() {
let child_inode_val = &state.fs.inodes[child];
// early return based on flags
if o_flags & __WASI_O_EXCL != 0 {
return __WASI_EEXIST;
}
if o_flags & __WASI_O_DIRECTORY != 0 {
match &child_inode_val.kind {
Kind::Dir { .. } => (),
Kind::Symlink { .. } => {
unimplemented!("Symlinks not yet supported in path_open")
}
_ => return __WASI_ENOTDIR,
}
let out_fd = if let Kind::Dir {
entries, parent, ..
} = &mut state.fs.inodes[cur_dir_inode].kind
{
// short circuit logic if attempting to get parent
if file_name == ".." {
if let Some(p) = parent {
let parent_inode = *p;
wasi_try!(state.fs.create_fd(
fs_rights_base,
fs_rights_inheriting,
fs_flags,
parent_inode
))
} else {
return __WASI_EACCES;
}
// do logic on child
wasi_try!(state
.fs
.create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child))
} else {
debug!("Attempting to load file from host system");
let file_metadata = file_path.metadata();
// if entry does not exist in parent directory, try to lazily
// load it; possibly creating or truncating it if flags set
let kind = if file_metadata.is_ok() && file_metadata.unwrap().is_dir() {
// special dir logic
Kind::Dir {
parent: Some(cur_dir_inode),
path: file_path.clone(),
entries: Default::default(),
if let Some(child) = entries.get(file_name).cloned() {
let child_inode_val = &state.fs.inodes[child];
// early return based on flags
if o_flags & __WASI_O_EXCL != 0 {
return __WASI_EEXIST;
}
if o_flags & __WASI_O_DIRECTORY != 0 {
match &child_inode_val.kind {
Kind::Dir { .. } => (),
Kind::Symlink { .. } => {
unimplemented!("Symlinks not yet supported in path_open")
}
_ => return __WASI_ENOTDIR,
}
}
// do logic on child
wasi_try!(state
.fs
.create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child))
} else {
// file is not a dir
let real_opened_file = {
let mut open_options = std::fs::OpenOptions::new();
let open_options = open_options.read(true);
let open_options = if fs_rights_base & __WASI_RIGHT_FD_WRITE != 0 {
open_options.write(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_CREAT != 0 {
debug!(
"File {:?} may be created when opened if it does not exist",
&file_path
);
open_options.create(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_TRUNC != 0 {
debug!("File {:?} will be truncated when opened", &file_path);
open_options.truncate(true)
} else {
open_options
};
debug!("Opening host file {:?}", &file_path);
let real_open_file =
wasi_try!(open_options.open(&file_path).map_err(|_| __WASI_EIO));
debug!("Attempting to load file from host system");

let file_metadata = file_path.metadata();
// if entry does not exist in parent directory, try to lazily
// load it; possibly creating or truncating it if flags set
let kind = if file_metadata.is_ok() && file_metadata.unwrap().is_dir() {
// special dir logic
Kind::Dir {
parent: Some(cur_dir_inode),
path: file_path.clone(),
entries: Default::default(),
}
} else {
// file is not a dir
let real_opened_file = {
let mut open_options = std::fs::OpenOptions::new();
let open_options = open_options.read(true);
let open_options = if fs_rights_base & __WASI_RIGHT_FD_WRITE != 0 {
open_options.write(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_CREAT != 0 {
debug!(
"File {:?} may be created when opened if it does not exist",
&file_path
);
open_options.create(true)
} else {
open_options
};
let open_options = if o_flags & __WASI_O_TRUNC != 0 {
debug!("File {:?} will be truncated when opened", &file_path);
open_options.truncate(true)
} else {
open_options
};
debug!("Opening host file {:?}", &file_path);
let real_open_file =
wasi_try!(open_options.open(&file_path).map_err(|_| __WASI_EIO));

real_open_file
real_open_file
};
Kind::File {
handle: WasiFile::HostFile(real_opened_file),
}
};
Kind::File {
handle: WasiFile::HostFile(real_opened_file),
}
};

// record lazily loaded or newly created fd
let new_inode = state.fs.inodes.insert(InodeVal {
stat: wasi_try!(get_stat_for_kind(&kind).ok_or(__WASI_EIO)),
is_preopened: false,
name: file_name.clone(),
kind,
});

// reborrow to insert entry
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind {
entries.insert(file_name.clone(), new_inode);
// record lazily loaded or newly created fd
let new_inode = state.fs.inodes.insert(InodeVal {
stat: wasi_try!(get_stat_for_kind(&kind).ok_or(__WASI_EIO)),
is_preopened: false,
name: file_name.clone(),
kind,
});

// reborrow to insert entry
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind {
entries.insert(file_name.clone(), new_inode);
}
let new_fd = wasi_try!(state.fs.create_fd(
fs_rights_base,
fs_rights_inheriting,
fs_flags,
new_inode,
));

new_fd
}
let new_fd = wasi_try!(state.fs.create_fd(
fs_rights_base,
fs_rights_inheriting,
fs_flags,
new_inode,
));

new_fd
}
} else {
// working_dir did not match on Kind::Dir
Expand Down
9 changes: 9 additions & 0 deletions lib/wasi/tests/wasitests/fs_sandbox_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#[test]
fn test_fs_sandbox_test() {
assert_wasi_output!(
"../../wasitests/fs_sandbox_test.wasm",
"fs_sandbox_test",
vec![],
"../../wasitests/fs_sandbox_test.out"
);
}
1 change: 1 addition & 0 deletions lib/wasi/tests/wasitests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
mod _common;
mod create_dir;
mod file_metadata;
mod fs_sandbox_test;
mod hello;
mod mapdir;
mod quine;
Binary file added lib/wasi/wasitests/fs_sandbox_test
Binary file not shown.
1 change: 1 addition & 0 deletions lib/wasi/wasitests/fs_sandbox_test.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reading the parent directory was okay? false
10 changes: 10 additions & 0 deletions lib/wasi/wasitests/fs_sandbox_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
fn main() {
#[cfg(target = "wasi")]
let result = std::fs::read_dir("..");
#[cfg(not(target = "wasi"))]
let result: Result<(), String> = Err("placeholder".to_string());
println!(
"Reading the parent directory was okay? {:?}",
result.is_ok()
);
}
Binary file added lib/wasi/wasitests/fs_sandbox_test.wasm
Binary file not shown.