Skip to content

Commit b9483d0

Browse files
authored
Merge pull request from GHSA-55f3-3qvg-8pv5
Fix symlink escaping filesystem sandbox
2 parents f75148c + ad4c9cd commit b9483d0

File tree

4 files changed

+71
-4
lines changed

4 files changed

+71
-4
lines changed

lib/wasix/src/syscalls/wasi/path_open.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,13 @@ pub(crate) fn path_open_internal(
330330
// once we got the data we need from the parent, we lookup the host file
331331
// todo: extra check that opening with write access is okay
332332
let handle = {
333+
// We set create_new because the path already didn't resolve to an existing file,
334+
// so it must be created.
333335
let open_options = open_options
334336
.read(minimum_rights.read)
335337
.append(minimum_rights.append)
336338
.write(minimum_rights.write)
337-
.create_new(minimum_rights.create_new);
339+
.create_new(true);
338340

339341
if minimum_rights.read {
340342
open_flags |= Fd::READ;
@@ -349,9 +351,19 @@ pub(crate) fn path_open_internal(
349351
open_flags |= Fd::TRUNCATE;
350352
}
351353

352-
Some(wasi_try_ok_ok!(open_options
353-
.open(&new_file_host_path)
354-
.map_err(|e| { fs_error_into_wasi_err(e) })))
354+
match open_options.open(&new_file_host_path) {
355+
Ok(handle) => Some(handle),
356+
Err(err) => {
357+
// Even though the file does not exist, it still failed to create with
358+
// `AlreadyExists` error. This can happen if the path resolves to a
359+
// symlink that points outside the FS sandbox.
360+
if err == FsError::AlreadyExists {
361+
return Ok(Err(Errno::Perm));
362+
}
363+
364+
return Ok(Err(fs_error_into_wasi_err(err)));
365+
}
366+
}
355367
};
356368

357369
let new_inode = {

tests/wasi-fyi/fs_sandbox_symlink.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#[link(wasm_import_module = "wasi_snapshot_preview1")]
2+
extern "C" {
3+
pub fn path_open(
4+
fd: i32,
5+
dirflags: i32,
6+
path: i32,
7+
path_len: i32,
8+
oflags: i32,
9+
fs_rights_base: i64,
10+
fs_rights_inheriting: i64,
11+
fdflags: i32,
12+
result_fd: i32,
13+
) -> i32;
14+
}
15+
16+
const ERRNO_PERM: i32 = 63;
17+
const LOOKUPFLAGS_SYMLINK_FOLLOW: i32 = 1;
18+
const OFLAGS_CREAT: i32 = 1;
19+
const RIGHTS_FD_WRITE: i64 = 64;
20+
21+
fn main() {
22+
let link_path = "fyi/fs_sandbox_symlink.dir/link";
23+
let link_path_non_existant = "fyi/fs_sandbox_symlink.dir/link-non-existant";
24+
let mut fd: i32 = 0;
25+
26+
unsafe {
27+
let errno = path_open(
28+
5,
29+
LOOKUPFLAGS_SYMLINK_FOLLOW,
30+
link_path.as_ptr() as i32,
31+
link_path.len() as i32,
32+
OFLAGS_CREAT,
33+
RIGHTS_FD_WRITE,
34+
0,
35+
0,
36+
&mut fd as *mut i32 as i32,
37+
);
38+
assert_eq!(errno, ERRNO_PERM, "symlink cannot escape fs sandbox");
39+
40+
let errno = path_open(
41+
5,
42+
LOOKUPFLAGS_SYMLINK_FOLLOW,
43+
link_path_non_existant.as_ptr() as i32,
44+
link_path_non_existant.len() as i32,
45+
OFLAGS_CREAT,
46+
RIGHTS_FD_WRITE,
47+
0,
48+
0,
49+
&mut fd as *mut i32 as i32,
50+
);
51+
assert_eq!(errno, ERRNO_PERM, "symlink cannot escape fs sandbox");
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../non-existant

0 commit comments

Comments
 (0)