Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 4 additions & 0 deletions src/typemap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ sysdefs = { path = "../sysdefs" }
default = ["fast"]
fast = []
secure = []

[dev-dependencies]
dashmap = "5.1"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you make this import in Cage lib public so that we don't need to re-dependent here? This can help us avoid version mismatch between typemap and cage.

parking_lot = "0.12"
Comment thread
Yaxuan-w marked this conversation as resolved.
Outdated
65 changes: 65 additions & 0 deletions src/typemap/src/path_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,69 @@ mod tests {
assert_eq!(path_without_trailing_slashes("/"), "/");
assert_eq!(path_without_trailing_slashes("///"), "/");
}

use dashmap::DashMap;

// Builds a minimal cage and registers it under 'cageid', so
// normpath(path, cageid) can find it via cage::get_cage()
// Every field besides 'cwd' is irrelevant to path logic, they're
// filled with empty/default values to satisfy the struct
fn make_test_cage(cageid: u64, cwd: &str) {
cage::cagetable_init();

let test_cage = cage::Cage {
cageid,
parent: cageid,
cwd: cage::RwLock::new(cage::Arc::new(PathBuf::from(cwd))),
rev_shm: cage::Mutex::new(Vec::new()),
signalhandler: DashMap::new(),
sigset: cage::AtomicU64::new(0),
pending_signals: cage::RwLock::new(vec![]),
epoch_handler: DashMap::new(),
os_tid_map: DashMap::new(),
main_threadid: cage::RwLock::new(0),
interval_timer: cage::IntervalTimer::new(cageid),
zombies: cage::RwLock::new(vec![]),
child_num: cage::AtomicU64::new(0),
vmmap: cage::RwLock::new(cage::Vmmap::new()),
final_exit_status: cage::RwLock::new(None),
exit_group_initiated: cage::AtomicBool::new(false),
is_dead: cage::AtomicBool::new(false),
grate_inflight: cage::AtomicU64::new(0),
};
cage::add_cage(cageid, test_cage);
}
#[test]
//ISO-004: an absolute path must always be rebuilt from the virtual root
// never passed through some other branch unmodified
fn normpath_confines_absolute_path_to_virtual_root() {
let cageid = 1500;
make_test_cage(cageid, "/");

let result = normpath(PathBuf::from("/etc/passwd"), cageid);

assert_eq!(result, PathBuf::from("/etc/passwd"));
}
Comment thread
vidyalakshmir marked this conversation as resolved.

#[test]
//ISO-004: excess ".." must clamp at the virtual root instead of
// going negative even when climbing from a real nested cwd
fn normpath_clamps_excess_parent_dir_at_root() {
let cageid = 1501;
make_test_cage(cageid, "/home/user/project");

let result = normpath(PathBuf::from("../../../../../../etc/passwd"), cageid);
assert_eq!(result, PathBuf::from("/etc/passwd"));
}

#[test]
// ISO-004: ordinary ".." must still resolve correctly, not just get
// clamped away, proves the clamp isn't overly aggressive
fn normpath_resolves_ordinary_parent_dir_correctly() {
let cageid = 1502;
make_test_cage(cageid, "/a/b");

let result = normpath(PathBuf::from("foo/../../bar"), cageid);
assert_eq!(result, PathBuf::from("/a/bar"));
}
}
45 changes: 45 additions & 0 deletions tests/unit-tests/file_tests/deterministic/symlink_confinement.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main() {
unlink("evil_link");

assert(symlink("/etc/passwd", "evil_link") == 0);

errno = 0;
int direct_fd = open("/etc/passwd", O_RDONLY);
int direct_errno = errno;

errno = 0;
int link_fd = open("evil_link", O_RDONLY);
int link_errno = errno;

if(direct_fd == -1) {
assert(link_fd == -1);
assert(link_errno == direct_errno);
} else {
assert(link_fd != -1);

char direct_buf[256];
char link_buf[256];
ssize_t direct_n = read(direct_fd, direct_buf, sizeof(direct_buf));
ssize_t link_n = read(link_fd, link_buf, sizeof(link_buf));

assert(direct_n >= 0);
assert(link_n == direct_n);
assert(memcmp(direct_buf, link_buf, (size_t)direct_n) == 0);

close(direct_fd);
close(link_fd);
}

unlink("evil_link");

printf("symlink_confinement test: PASS\n");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This test asserts that opening via evil_link behaves identically to opening /etc/passwd directly. if confinement were totally broken and both opens hit the host's real /etc/passwd, the two would still match each other and the test would still PASS.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah I agree with this.

return 0;

}