diff --git a/src/glibc/lind_syscall/lind_syscall_num.h b/src/glibc/lind_syscall/lind_syscall_num.h index 40f072414..c144b4b8a 100644 --- a/src/glibc/lind_syscall/lind_syscall_num.h +++ b/src/glibc/lind_syscall/lind_syscall_num.h @@ -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 diff --git a/src/glibc/sysdeps/unix/sysv/linux/umask.c b/src/glibc/sysdeps/unix/sysv/linux/umask.c index 3872dfbbc..0be21f4bb 100644 --- a/src/glibc/sysdeps/unix/sysv/linux/umask.c +++ b/src/glibc/sysdeps/unix/sysv/linux/umask.c @@ -1,9 +1,13 @@ #include #include #include +#include +#include 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); } diff --git a/src/rawposix/src/fs_calls.rs b/src/rawposix/src/fs_calls.rs index 6e7b3fd6f..ce79cc594 100644 --- a/src/rawposix/src/fs_calls.rs +++ b/src/rawposix/src/fs_calls.rs @@ -566,6 +566,38 @@ pub extern "C" fn mknod_syscall( ret } +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) & 0o777; + + 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" + ); + } + + unsafe { libc::umask(mask as libc::mode_t) as i32 } +} + /// Reference to Linux: https://man7.org/linux/man-pages/man2/pipe.2.html /// /// Linux `pipe()` syscall is equivalent to calling `pipe2()` with flags set to zero. diff --git a/src/rawposix/src/syscall_table.rs b/src/rawposix/src/syscall_table.rs index f399cc11f..ae4007096 100644 --- a/src/rawposix/src/syscall_table.rs +++ b/src/rawposix/src/syscall_table.rs @@ -17,7 +17,8 @@ use super::fs_calls::{ 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, + umask_syscall, unlink_syscall, unlinkat_syscall, utimensat_syscall, write_syscall, + writev_syscall, }; use super::init::RawCallFunc; use super::net_calls::{ @@ -129,6 +130,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), diff --git a/src/sysdefs/src/constants/syscall_const.rs b/src/sysdefs/src/constants/syscall_const.rs index b5062559b..385275362 100644 --- a/src/sysdefs/src/constants/syscall_const.rs +++ b/src/sysdefs/src/constants/syscall_const.rs @@ -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; diff --git a/tests/unit-tests/file_tests/deterministic/umask.c b/tests/unit-tests/file_tests/deterministic/umask.c new file mode 100644 index 000000000..7cd197a0b --- /dev/null +++ b/tests/unit-tests/file_tests/deterministic/umask.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include + +static int failures; + +#define CHECK(message, condition) \ + do { \ + if (condition) { \ + printf("PASS: %s\n", message); \ + } else { \ + printf("FAIL: %s\n", message); \ + failures++; \ + } \ + } while (0) + +static mode_t fd_mode(int fd) { + struct stat st; + return fstat(fd, &st) == 0 ? st.st_mode & 0777 : (mode_t)-1; +} + +int main(void) { + const char *unmasked_path = "testfiles/umask_unmasked"; + const char *default_path = "testfiles/umask_default"; + const char *private_path = "testfiles/umask_private"; + const char *directory_path = "testfiles/umask_directory"; + const char *fork_path = "testfiles/umask_fork"; + + unlink(unmasked_path); + unlink(default_path); + unlink(private_path); + unlink(fork_path); + rmdir(directory_path); + + CHECK("initial umask is 0022", umask(0000) == 0022); + + int fd = open(unmasked_path, O_CREAT | O_RDWR, 0666); + CHECK("umask 0000 permits mode 0666", fd >= 0 && fd_mode(fd) == 0666); + if (fd >= 0) close(fd); + + CHECK("umask returns previous value", umask(0022) == 0000); + fd = open(default_path, O_CREAT | O_RDWR, 0666); + CHECK("umask 0022 produces mode 0644", fd >= 0 && fd_mode(fd) == 0644); + if (fd >= 0) close(fd); + + CHECK("changing umask returns 0022", umask(0077) == 0022); + fd = open(private_path, O_CREAT | O_RDWR, 0666); + CHECK("umask 0077 produces mode 0600", fd >= 0 && fd_mode(fd) == 0600); + if (fd >= 0) close(fd); + + CHECK("changing umask returns 0077", umask(0027) == 0077); + CHECK("umask 0027 produces directory mode 0750", + mkdir(directory_path, 0777) == 0); + struct stat st; + CHECK("created directory has mode 0750", + stat(directory_path, &st) == 0 && (st.st_mode & 0777) == 0750); + + CHECK("set mask for fork inheritance", umask(0077) == 0027); + pid_t child = fork(); + if (child == 0) { + int child_fd = open(fork_path, O_CREAT | O_RDWR, 0666); + int child_ok = child_fd >= 0 && fd_mode(child_fd) == 0600; + if (child_fd >= 0) close(child_fd); + _exit(child_ok ? 0 : 1); + } + + int status = 0; + CHECK("fork child creates with inherited umask", + child > 0 && waitpid(child, &status, 0) == child && + WIFEXITED(status) && WEXITSTATUS(status) == 0); + CHECK("parent retains umask", umask(01777) == 0077); + CHECK("masked high bits are discarded", umask(0022) == 0777); + + unlink(unmasked_path); + unlink(default_path); + unlink(private_path); + unlink(fork_path); + rmdir(directory_path); + + printf("Result: %s\n", failures == 0 ? "PASS" : "FAIL"); + return failures == 0 ? 0 : 1; +}