Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
6 changes: 5 additions & 1 deletion src/cage/src/cage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use once_cell::sync::Lazy;
/// interaction and increases efficiency.
pub use parking_lot::{Mutex, RwLock};
pub use std::path::{Path, PathBuf};
pub use std::sync::atomic::{AtomicBool, AtomicI32, AtomicPtr, AtomicU64, Ordering};
pub use std::sync::atomic::{AtomicBool, AtomicI32, AtomicPtr, AtomicU32, AtomicU64, Ordering};
pub use std::sync::Arc;
use sysdefs::constants::lind_platform_const::MAX_CAGEID;
use sysdefs::constants::sys_const::EXIT_SUCCESS;
Expand Down Expand Up @@ -251,6 +251,9 @@ pub struct Cage {
/// Could also be moved to wasmtime/crate/lind-3i if grate_inflight
/// tracking is considered a VMContext-level concern.
pub grate_inflight: AtomicU64,
/// The file mode creation mask for this cage. Applied during file/directory
/// creation to restrict permissions. Mirrors Linux per-process umask.
pub umask: AtomicU32,
}

/// We achieve an O(1) complexity for our cage map implementation through the following three approaches:
Expand Down Expand Up @@ -464,6 +467,7 @@ mod tests {
exit_group_initiated: AtomicBool::new(false),
is_dead: AtomicBool::new(false),
grate_inflight: AtomicU64::new(0),
umask: AtomicU32::new(0o022),
};

add_cage(2, test_cage);
Expand Down
1 change: 1 addition & 0 deletions src/glibc/lind_syscall/lind_syscall_num.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
#define FCHMOD_SYSCALL 91
#define CHOWN_SYSCALL 92
#define LCHOWN_SYSCALL 94
#define UMASK_SYSCALL 95

#define GETUID_SYSCALL 102
#define GETGID_SYSCALL 104
Expand Down
9 changes: 7 additions & 2 deletions src/glibc/sysdeps/unix/sysv/linux/umask.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

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.

Seems this file is not used. Should we remove?

#include <syscall-template.h>
#include <lind_syscall_num.h>

mode_t
umask (mode_t mask)
{
return -1;
}
return (mode_t) MAKE_LEGACY_SYSCALL (
UMASK_SYSCALL, "syscall|umask", (uint64_t) mask,
NOTUSED, NOTUSED, NOTUSED, NOTUSED, NOTUSED, TRANSLATE_ERRNO_ON);
}

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.

Add new line

70 changes: 68 additions & 2 deletions src/rawposix/src/fs_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use cage::{
use dashmap::mapref::entry::Entry::{Occupied, Vacant};
use fdtables;
use libc::c_void;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU32, AtomicU64};
use std::sync::atomic::{Ordering, Ordering::*};
use std::sync::Arc;
use sysdefs::constants::err_const::{get_errno, handle_errno, syscall_error, Errno};
use sysdefs::constants::fs_const::{
Expand Down Expand Up @@ -86,6 +88,7 @@ pub extern "C" fn openat_syscall(
};
let oflag = sc_convert_sysarg_to_i32(oflag_arg, oflag_cageid, cageid);
let mode = sc_convert_sysarg_to_u32(mode_arg, mode_cageid, cageid);

if !(sc_unusedarg(arg5, arg5_cageid) && sc_unusedarg(arg6, arg6_cageid)) {
panic!(
"{}: unused arguments contain unexpected values -- security violation",
Expand Down Expand Up @@ -128,8 +131,11 @@ pub extern "C" fn openat_syscall(
Err(_) => return syscall_error(Errno::EINVAL, "openat", "invalid path"),
};

let kernel_fd =
unsafe { libc::openat(host_fd, c_path.as_ptr(), oflag, mode as libc::mode_t) };
let cage = get_cage(cageid).unwrap();
let umask = cage.umask.load(Ordering::Relaxed);
let masked_mode = (mode & !umask) as libc::mode_t;
let kernel_fd = unsafe { libc::openat(host_fd, c_path.as_ptr(), oflag, masked_mode) };

if kernel_fd < 0 {
return handle_errno(get_errno(), "openat_syscall");
}
Expand Down Expand Up @@ -201,6 +207,10 @@ pub extern "C" fn open_syscall(
);
}

let cage = get_cage(cageid).unwrap();
let umask = cage.umask.load(Ordering::Relaxed);
let mode = mode & !umask;

// Get the kernel fd first
let kernel_fd = unsafe { libc::open(path.as_ptr(), oflag, mode) };

Expand Down Expand Up @@ -502,6 +512,10 @@ pub extern "C" fn mkdir_syscall(
);
}

let cage = get_cage(cageid).unwrap();
let umask = cage.umask.load(Ordering::Relaxed);
let mode = mode & !umask;

let ret = unsafe { libc::mkdir(path.as_ptr(), mode) };
// Error handling
if ret < 0 {
Expand Down Expand Up @@ -557,6 +571,9 @@ pub extern "C" fn mknod_syscall(
"mknod_syscall"
);
}
let cage = get_cage(cageid).unwrap();
let umask = cage.umask.load(Ordering::Relaxed);
let mode = mode & !umask;

let ret = unsafe { libc::mknod(path.as_ptr(), mode, dev) };
if ret < 0 {
Expand Down Expand Up @@ -5524,3 +5541,52 @@ pub extern "C" fn listxattr_syscall(
// Convert ssize_t to i32 safely
ret.try_into().unwrap_or(i32::MAX)
}

/// Reference to Linux: https://man7.org/linux/man-pages/man2/umask.2.html
///
/// Linux `umask()` syscall sets the calling process's file mode creation mask
/// to `mask & 0777` and returns the previous value of the mask. The umask is
/// applied during file/directory creation (open, mkdir, mknod) to restrict
/// permissions. Since each cage is isolated, the umask is stored per-cage in
/// the Cage struct rather than delegating to the host kernel.
///
/// ## Input:
/// - cageid: current cage identifier
/// - mask_arg: the new umask value (only the lower 9 bits are used)
/// - arg2-arg6: additional arguments which are expected to be unused
///
/// ## Returns:
/// - Always succeeds and returns the previous umask value.
pub extern "C" fn umask_syscall(
cageid: u64,
mask_arg: u64,
mask_cageid: u64,
arg2: u64,
arg2_cageid: u64,
arg3: u64,
arg3_cageid: u64,
arg4: u64,
arg4_cageid: u64,
arg5: u64,
arg5_cageid: u64,
arg6: u64,
arg6_cageid: u64,
) -> i32 {
let mask = sc_convert_sysarg_to_u32(mask_arg, mask_cageid, cageid);

if !(sc_unusedarg(arg2, arg2_cageid)
&& sc_unusedarg(arg3, arg3_cageid)
&& sc_unusedarg(arg4, arg4_cageid)
&& sc_unusedarg(arg5, arg5_cageid)
&& sc_unusedarg(arg6, arg6_cageid))
{
panic!(
"{}: unused arguments contain unexpected values -- security violation",
"umask_syscall"
);
}

let cage = get_cage(cageid).unwrap();
let old_mask = cage.umask.swap(mask & 0o777, Ordering::SeqCst);
old_mask as i32
}
3 changes: 2 additions & 1 deletion src/rawposix/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use fdtables;
use parking_lot::{Mutex, RwLock};
use std::ffi::CString;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering::*};
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU32, AtomicU64, Ordering::*};
use std::sync::Arc;
use sysdefs::constants::{
EXIT_SUCCESS, FDKIND_KERNEL, INIT_CAGEID, MAIN_THREADID, RAWPOSIX_CAGEID, STDERR_FILENO,
Expand Down Expand Up @@ -287,6 +287,7 @@ pub fn rawposix_start(verbosity: isize) {
exit_group_initiated: AtomicBool::new(false),
is_dead: AtomicBool::new(false),
grate_inflight: AtomicU64::new(0),
umask: AtomicU32::new(0o022),

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.

Can we have comments about default value here?

};

// Add cage to cagetable
Expand Down
5 changes: 3 additions & 2 deletions src/rawposix/src/sys_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use libc::sched_yield;
use parking_lot::{Mutex, RwLock};
use std::ffi::CString;
use std::path::PathBuf;
use std::sync::atomic::Ordering::*;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64};
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU32, AtomicU64};
use std::sync::atomic::{Ordering, Ordering::*};
use std::sync::Arc;
use std::time::Duration;
use sysdefs::constants::err_const::{syscall_error, Errno, VERBOSE};
Expand Down Expand Up @@ -148,6 +148,7 @@ pub extern "C" fn fork_syscall(
exit_group_initiated: AtomicBool::new(false),
is_dead: AtomicBool::new(false),
grate_inflight: AtomicU64::new(0),
umask: AtomicU32::new(selfcage.umask.load(Ordering::SeqCst)),
};

// increment child counter for parent
Expand Down
5 changes: 3 additions & 2 deletions src/rawposix/src/syscall_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use super::fs_calls::{
read_syscall, readlink_syscall, readlinkat_syscall, readv_syscall, rename_syscall,
renameat2_syscall, renameat_syscall, rmdir_syscall, setxattr_syscall, shmat_syscall,
shmctl_syscall, shmdt_syscall, shmget_syscall, stat_syscall, statfs_syscall, symlink_syscall,
symlinkat_syscall, sync_file_range_syscall, truncate_syscall, unlink_syscall, unlinkat_syscall,
utimensat_syscall, write_syscall, writev_syscall,
symlinkat_syscall, sync_file_range_syscall, truncate_syscall, umask_syscall, unlink_syscall,
unlinkat_syscall, utimensat_syscall, write_syscall, writev_syscall,
};
use super::init::RawCallFunc;
use super::net_calls::{
Expand Down Expand Up @@ -129,6 +129,7 @@ pub const SYSCALL_TABLE: &[(u64, RawCallFunc)] = &[
(syscall_const::FCHMOD_SYSCALL as u64, fchmod_syscall),
(syscall_const::CHOWN_SYSCALL as u64, chown_syscall),
(syscall_const::LCHOWN_SYSCALL as u64, lchown_syscall),
(syscall_const::UMASK_SYSCALL as u64, umask_syscall),
(syscall_const::GETUID_SYSCALL as u64, getuid_syscall),
(syscall_const::GETGID_SYSCALL as u64, getgid_syscall),
(syscall_const::GETEUID_SYSCALL as u64, geteuid_syscall),
Expand Down
1 change: 1 addition & 0 deletions src/sysdefs/src/constants/syscall_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ pub const CHMOD_SYSCALL: i32 = 90;
pub const FCHMOD_SYSCALL: i32 = 91;
pub const CHOWN_SYSCALL: i32 = 92;
pub const LCHOWN_SYSCALL: i32 = 94;
pub const UMASK_SYSCALL: i32 = 95;
pub const GETUID_SYSCALL: i32 = 102;
pub const GETGID_SYSCALL: i32 = 104;
pub const GETEUID_SYSCALL: i32 = 107;
Expand Down
88 changes: 88 additions & 0 deletions tests/unit-tests/file_tests/deterministic/umask.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Before running this test:
* 1. Ensure the test directory exists in $LIND_FS_ROOT.
* 2. No pre-existing files named "umask_test_file.txt" or "umask_test_dir".
*/


#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>

int main() {
mode_t old_mask;
int fd;
struct stat st;

/* Cleanup from any previous run */
rmdir("testfiles/umask_test_dir");
unlink("testfiles/umask_test_file.txt");
unlink("testfiles/umask_test_file2.txt");


/* Test 1: umask returns previous mask */
old_mask = umask(0022);
if (old_mask != 0022) {
printf("FAIL: expected initial umask 0022, got %04o\n", old_mask);
return 1;
}
umask(old_mask); /* restore */
printf("PASS: umask returns previous mask\n");

/* Test 2: umask applied on open — file created with 0666 & ~0022 = 0644 */
umask(0022);
fd = open("testfiles/umask_test_file.txt", O_CREAT | O_WRONLY, 0666);
if (fd == -1) {
perror("open failed");
return 1;
}
close(fd);

if (stat("testfiles/umask_test_file.txt", &st) == -1) {
perror("stat failed");
return 1;
}
if ((st.st_mode & 0777) != 0644) {
printf("FAIL: expected file perms 0644, got %04o\n", st.st_mode & 0777);
return 1;
}
printf("PASS: umask applied correctly on open (0644)\n");

/* Test 3: umask applied on mkdir — dir created with 0777 & ~0022 = 0755 */
umask(0022);
if (mkdir("testfiles/umask_test_dir", 0777) == -1) {
perror("mkdir failed");
return 1;
}
if (stat("testfiles/umask_test_dir", &st) == -1) {
perror("stat on dir failed");
return 1;
}
if ((st.st_mode & 0777) != 0755) {
printf("FAIL: expected dir perms 0755, got %04o\n", st.st_mode & 0777);
return 1;
}
printf("PASS: umask applied correctly on mkdir (0755)\n");

/* Test 4: changing umask affects subsequent creates */
umask(0077);
fd = open("testfiles/umask_test_file2.txt", O_CREAT | O_WRONLY, 0666);
if (fd == -1) {
perror("open failed");
return 1;
}
close(fd);
if (stat("testfiles/umask_test_file2.txt", &st) == -1) {
perror("stat failed");
return 1;
}
if ((st.st_mode & 0777) != 0600) {
printf("FAIL: expected file perms 0600, got %04o\n", st.st_mode & 0777);
return 1;
}
printf("PASS: umask 0077 applied correctly on open (0600)\n");

printf("All umask tests passed.\n");
return 0;
}

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.

Add new line