From d15a75fafba0a0a6d9a1181515fbd12a92054797 Mon Sep 17 00:00:00 2001 From: "Hui, Chunyang" Date: Fri, 30 Apr 2021 04:08:46 +0000 Subject: [PATCH] Add support for POSIX_SPAWN_SETSIGMASK and POSIX_SPAWN_SETSIGDEF --- src/libos/src/entry.rs | 1 + src/libos/src/process/do_clone.rs | 5 +- src/libos/src/process/do_spawn/mod.rs | 32 +++ src/libos/src/process/mod.rs | 2 + src/libos/src/process/process/builder.rs | 15 +- src/libos/src/process/spawn_attribute.rs | 87 ++++++++ src/libos/src/process/syscalls.rs | 19 +- src/libos/src/process/thread/builder.rs | 9 +- src/libos/src/signal/sig_dispositions.rs | 24 +- src/libos/src/syscall/mod.rs | 7 +- test/Makefile | 4 +- test/naughty_child/Makefile | 5 + test/naughty_child/main.c | 127 +++++++++++ test/spawn_attribute/Makefile | 5 + test/spawn_attribute/main.c | 270 +++++++++++++++++++++++ 15 files changed, 596 insertions(+), 16 deletions(-) create mode 100644 src/libos/src/process/spawn_attribute.rs create mode 100644 test/naughty_child/Makefile create mode 100644 test/naughty_child/main.c create mode 100644 test/spawn_attribute/Makefile create mode 100644 test/spawn_attribute/main.c diff --git a/src/libos/src/entry.rs b/src/libos/src/entry.rs index 23b2f9c3..789186de 100644 --- a/src/libos/src/entry.rs +++ b/src/libos/src/entry.rs @@ -256,6 +256,7 @@ fn do_new_process( argv, &env_concat, &file_actions, + None, host_stdio_fds, current, )?; diff --git a/src/libos/src/process/do_clone.rs b/src/libos/src/process/do_clone.rs index 66dfe81d..c555d83a 100644 --- a/src/libos/src/process/do_clone.rs +++ b/src/libos/src/process/do_clone.rs @@ -51,6 +51,7 @@ pub fn do_clone( let rlimits = current.rlimits().clone(); let fs = current.fs().clone(); let name = current.name().clone(); + let sig_mask = current.sig_mask().read().unwrap().clone(); let mut builder = ThreadBuilder::new() .process(current.process().clone()) @@ -59,12 +60,14 @@ pub fn do_clone( .fs(fs) .files(files) .name(name) - .rlimits(rlimits); + .rlimits(rlimits) + .sig_mask(sig_mask); if let Some(ctid) = ctid { builder = builder.clear_ctid(ctid); } builder.build()? }; + trace!("new thread sigmask = {:?}", new_thread_ref.sig_mask()); let new_tid = new_thread_ref.tid(); table::add_thread(new_thread_ref.clone()); info!("Thread created: tid = {}", new_tid); diff --git a/src/libos/src/process/do_spawn/mod.rs b/src/libos/src/process/do_spawn/mod.rs index 4216c494..5671b146 100644 --- a/src/libos/src/process/do_spawn/mod.rs +++ b/src/libos/src/process/do_spawn/mod.rs @@ -5,6 +5,7 @@ use self::aux_vec::{AuxKey, AuxVec}; use self::exec_loader::{load_exec_file_hdr_to_vec, load_file_hdr_to_vec}; use super::elf_file::{ElfFile, ElfHeader, ProgramHeaderExt}; use super::process::ProcessBuilder; +use super::spawn_attribute::SpawnAttr; use super::task::Task; use super::thread::ThreadName; use super::{table, task, ProcessRef, ThreadRef}; @@ -25,6 +26,7 @@ pub fn do_spawn( argv: &[CString], envp: &[CString], file_actions: &[FileAction], + spawn_attributes: Option, current_ref: &ThreadRef, ) -> Result { let exec_now = true; @@ -33,6 +35,7 @@ pub fn do_spawn( argv, envp, file_actions, + spawn_attributes, None, current_ref, exec_now, @@ -45,6 +48,7 @@ pub fn do_spawn_without_exec( argv: &[CString], envp: &[CString], file_actions: &[FileAction], + spawn_attributes: Option, host_stdio_fds: &HostStdioFds, current_ref: &ThreadRef, ) -> Result { @@ -54,6 +58,7 @@ pub fn do_spawn_without_exec( argv, envp, file_actions, + spawn_attributes, Some(host_stdio_fds), current_ref, exec_now, @@ -65,6 +70,7 @@ fn do_spawn_common( argv: &[CString], envp: &[CString], file_actions: &[FileAction], + spawn_attributes: Option, host_stdio_fds: Option<&HostStdioFds>, current_ref: &ThreadRef, exec_now: bool, @@ -74,6 +80,7 @@ fn do_spawn_common( argv, envp, file_actions, + spawn_attributes, host_stdio_fds, current_ref, )?; @@ -97,6 +104,7 @@ fn new_process( argv: &[CString], envp: &[CString], file_actions: &[FileAction], + spawn_attributes: Option, host_stdio_fds: Option<&HostStdioFds>, current_ref: &ThreadRef, ) -> Result { @@ -188,6 +196,28 @@ fn new_process( let fs_ref = Arc::new(RwLock::new(current_ref.fs().read().unwrap().clone())); let sched_ref = Arc::new(SgxMutex::new(current_ref.sched().lock().unwrap().clone())); let rlimit_ref = Arc::new(SgxMutex::new(current_ref.rlimits().lock().unwrap().clone())); + let sig_mask = if spawn_attributes.is_some() && spawn_attributes.unwrap().sig_mask.is_some() + { + spawn_attributes.unwrap().sig_mask.unwrap() + } else { + current_ref.sig_mask().read().unwrap().clone() + }; + trace!("new process sigmask = {:?}", sig_mask); + + let mut sig_dispositions = current_ref + .process() + .sig_dispositions() + .read() + .unwrap() + .clone(); + sig_dispositions.inherit(); + if spawn_attributes.is_some() && spawn_attributes.unwrap().sig_default.is_some() { + let sig_default_set = spawn_attributes.unwrap().sig_default.unwrap(); + sig_default_set.iter().for_each(|b| { + sig_dispositions.set_default(b); + }) + } + trace!("new process sig_dispositions = {:?}", sig_dispositions); // Make the default thread name to be the process's corresponding elf file name let elf_name = elf_path.rsplit('/').collect::>()[0]; @@ -202,7 +232,9 @@ fn new_process( .rlimits(rlimit_ref) .fs(fs_ref) .files(files_ref) + .sig_mask(sig_mask) .name(thread_name) + .sig_dispositions(sig_dispositions) .build()? }; diff --git a/src/libos/src/process/mod.rs b/src/libos/src/process/mod.rs index 915c0e77..fb0ced35 100644 --- a/src/libos/src/process/mod.rs +++ b/src/libos/src/process/mod.rs @@ -24,6 +24,7 @@ pub use self::do_exit::handle_force_exit; pub use self::do_futex::{futex_wait, futex_wake}; pub use self::do_spawn::do_spawn_without_exec; pub use self::process::{Process, ProcessFilter, ProcessStatus, IDLE}; +pub use self::spawn_attribute::posix_spawnattr_t; pub use self::syscalls::*; pub use self::task::Task; pub use self::term_status::{ForcedExitStatus, TermStatus}; @@ -39,6 +40,7 @@ mod do_spawn; mod do_wait4; mod prctl; mod process; +mod spawn_attribute; mod syscalls; mod term_status; mod thread; diff --git a/src/libos/src/process/process/builder.rs b/src/libos/src/process/process/builder.rs index e2f69f88..34bde53c 100644 --- a/src/libos/src/process/process/builder.rs +++ b/src/libos/src/process/process/builder.rs @@ -6,7 +6,7 @@ use super::super::{ }; use super::{Process, ProcessInner}; use crate::prelude::*; -use crate::signal::{SigDispositions, SigQueues}; +use crate::signal::{SigDispositions, SigQueues, SigSet}; #[derive(Debug)] pub struct ProcessBuilder { @@ -18,6 +18,7 @@ pub struct ProcessBuilder { exec_path: Option, parent: Option, no_parent: bool, + sig_dispositions: Option, } impl ProcessBuilder { @@ -30,6 +31,7 @@ impl ProcessBuilder { exec_path: None, parent: None, no_parent: false, + sig_dispositions: None, } } @@ -53,6 +55,11 @@ impl ProcessBuilder { self } + pub fn sig_dispositions(mut self, sig_dispositions: SigDispositions) -> Self { + self.sig_dispositions = Some(sig_dispositions); + self + } + pub fn task(mut self, task: Task) -> Self { self.thread_builder(|tb| tb.task(task)) } @@ -73,6 +80,10 @@ impl ProcessBuilder { self.thread_builder(|tb| tb.files(files)) } + pub fn sig_mask(mut self, sig_mask: SigSet) -> Self { + self.thread_builder(|tb| tb.sig_mask(sig_mask)) + } + pub fn rlimits(mut self, rlimits: ResourceLimitsRef) -> Self { self.thread_builder(|tb| tb.rlimits(rlimits)) } @@ -99,7 +110,7 @@ impl ProcessBuilder { let exec_path = self.exec_path.take().unwrap_or_default(); let parent = self.parent.take().map(|parent| RwLock::new(parent)); let inner = SgxMutex::new(ProcessInner::new()); - let sig_dispositions = RwLock::new(SigDispositions::new()); + let sig_dispositions = RwLock::new(self.sig_dispositions.unwrap_or_default()); let sig_queues = RwLock::new(SigQueues::new()); let forced_exit_status = ForcedExitStatus::new(); Arc::new(Process { diff --git a/src/libos/src/process/spawn_attribute.rs b/src/libos/src/process/spawn_attribute.rs new file mode 100644 index 00000000..bdbc0558 --- /dev/null +++ b/src/libos/src/process/spawn_attribute.rs @@ -0,0 +1,87 @@ +use super::*; +use crate::signal::{sigset_t, SigSet}; +use crate::util::mem_util::from_user::check_ptr; + +// Note: This is the Rust representation of `posix_spawnattr_t` defined in libc. +// The name of the elements follow the glibc style. The comments show the name in musl. +// Elements other than the listed ones are ignored because we don't care for now because +// Only POSIX_SPAWN_SETSIGDEF and POSIX_SPAWN_SETSIGMASK are supported now. +#[repr(C)] +#[derive(Debug)] +pub struct posix_spawnattr_t { + flags: SpawnAttributeFlags, // __flags + pgrp: i32, // __pgrp + sd: SpawnAttrSigSet, // __def + ss: SpawnAttrSigSet, // __mask +} + +// Glibc and musl use 128 bytes to represent sig_set_t +type SpawnAttrSigSet = [sigset_t; 16]; + +bitflags! { + pub struct SpawnAttributeFlags: u16 { + const POSIX_SPAWN_RESETIDS = 1; // 0x1 + const POSIX_SPAWN_SETPGROUP = 1 << 1; // 0x2 + const POSIX_SPAWN_SETSIGDEF = 1 << 2; // 0x4 + const POSIX_SPAWN_SETSIGMASK = 1 << 3; // 0x8 + const POSIX_SPAWN_SETSCHEDPARAM = 1 << 4; // 0x10 + const POSIX_SPAWN_SETSCHEDULER = 1 << 5; // 0x20 + } +} + +impl SpawnAttributeFlags { + fn supported(&self) -> bool { + let unsupported_flags = SpawnAttributeFlags::all() + - SpawnAttributeFlags::POSIX_SPAWN_SETSIGDEF + - SpawnAttributeFlags::POSIX_SPAWN_SETSIGMASK; + if self.intersects(unsupported_flags) { + false + } else { + true + } + } +} + +#[derive(Default, Debug, Copy, Clone)] +pub struct SpawnAttr { + pub sig_mask: Option, + pub sig_default: Option, +} + +pub fn clone_spawn_atrributes_safely( + attr_ptr: *const posix_spawnattr_t, +) -> Result> { + if attr_ptr != std::ptr::null() { + check_ptr(attr_ptr)?; + } else { + return Ok(None); + } + + let spawn_attr = unsafe { &*attr_ptr }; + let mut safe_attr = SpawnAttr::default(); + if spawn_attr.flags.is_empty() { + return Ok(None); + } + + if !spawn_attr.flags.supported() { + warn!( + "Unsupported flags contained. Attribute flags: {:?}", + spawn_attr.flags + ); + } + + if spawn_attr + .flags + .contains(SpawnAttributeFlags::POSIX_SPAWN_SETSIGDEF) + { + safe_attr.sig_default = Some(SigSet::from_c(spawn_attr.sd[0])); + } + if spawn_attr + .flags + .contains(SpawnAttributeFlags::POSIX_SPAWN_SETSIGMASK) + { + safe_attr.sig_mask = Some(SigSet::from_c(spawn_attr.ss[0])); + } + + Ok(Some(safe_attr)) +} diff --git a/src/libos/src/process/syscalls.rs b/src/libos/src/process/syscalls.rs index 7142f092..bbc1f6fa 100644 --- a/src/libos/src/process/syscalls.rs +++ b/src/libos/src/process/syscalls.rs @@ -5,6 +5,7 @@ use super::do_spawn::FileAction; use super::do_wait4::WaitOptions; use super::prctl::PrctlCmd; use super::process::ProcessFilter; +use super::spawn_attribute::{clone_spawn_atrributes_safely, posix_spawnattr_t, SpawnAttr}; use crate::prelude::*; use crate::time::{timespec_t, ClockID}; use crate::util::mem_util::from_user::*; @@ -16,19 +17,22 @@ pub fn do_spawn_for_musl( argv: *const *const i8, envp: *const *const i8, fdop_list: *const FdOp, + attribute_list: *const posix_spawnattr_t, ) -> Result { check_mut_ptr(child_pid_ptr)?; let path = clone_cstring_safely(path)?.to_string_lossy().into_owned(); let argv = clone_cstrings_safely(argv)?; let envp = clone_cstrings_safely(envp)?; let file_actions = clone_file_actions_safely(fdop_list)?; + let spawn_attrs = clone_spawn_atrributes_safely(attribute_list)?; let current = current!(); debug!( - "spawn: path: {:?}, argv: {:?}, envp: {:?}, fdop: {:?}", - path, argv, envp, file_actions + "spawn: path: {:?}, argv: {:?}, envp: {:?}, fdop: {:?}, spawn_attr: {:?}", + path, argv, envp, file_actions, spawn_attrs ); - let child_pid = super::do_spawn::do_spawn(&path, &argv, &envp, &file_actions, ¤t)?; + let child_pid = + super::do_spawn::do_spawn(&path, &argv, &envp, &file_actions, spawn_attrs, ¤t)?; unsafe { *child_pid_ptr = child_pid }; Ok(0) @@ -92,19 +96,22 @@ pub fn do_spawn_for_glibc( argv: *const *const i8, envp: *const *const i8, fa: *const SpawnFileActions, + attribute_list: *const posix_spawnattr_t, ) -> Result { check_mut_ptr(child_pid_ptr)?; let path = clone_cstring_safely(path)?.to_string_lossy().into_owned(); let argv = clone_cstrings_safely(argv)?; let envp = clone_cstrings_safely(envp)?; let file_actions = clone_file_actions_from_fa_safely(fa)?; + let spawn_attrs = clone_spawn_atrributes_safely(attribute_list)?; let current = current!(); debug!( - "spawn: path: {:?}, argv: {:?}, envp: {:?}, actions: {:?}", - path, argv, envp, file_actions + "spawn: path: {:?}, argv: {:?}, envp: {:?}, actions: {:?}, attributes: {:?}", + path, argv, envp, file_actions, spawn_attrs ); - let child_pid = super::do_spawn::do_spawn(&path, &argv, &envp, &file_actions, ¤t)?; + let child_pid = + super::do_spawn::do_spawn(&path, &argv, &envp, &file_actions, spawn_attrs, ¤t)?; unsafe { *child_pid_ptr = child_pid }; Ok(0) diff --git a/src/libos/src/process/thread/builder.rs b/src/libos/src/process/thread/builder.rs index ee66feb8..fddacde5 100644 --- a/src/libos/src/process/thread/builder.rs +++ b/src/libos/src/process/thread/builder.rs @@ -20,6 +20,7 @@ pub struct ThreadBuilder { files: Option, sched: Option, rlimits: Option, + sig_mask: Option, clear_ctid: Option>, name: Option, } @@ -35,6 +36,7 @@ impl ThreadBuilder { files: None, sched: None, rlimits: None, + sig_mask: None, clear_ctid: None, name: None, } @@ -70,6 +72,11 @@ impl ThreadBuilder { self } + pub fn sig_mask(mut self, sig_mask: SigSet) -> Self { + self.sig_mask = Some(sig_mask); + self + } + pub fn sched(mut self, sched: SchedAgentRef) -> Self { self.sched = Some(sched); self @@ -108,8 +115,8 @@ impl ThreadBuilder { let sched = self.sched.unwrap_or_default(); let rlimits = self.rlimits.unwrap_or_default(); let name = RwLock::new(self.name.unwrap_or_default()); + let sig_mask = RwLock::new(self.sig_mask.unwrap_or_default()); let sig_queues = RwLock::new(SigQueues::new()); - let sig_mask = RwLock::new(SigSet::new_empty()); let sig_tmp_mask = RwLock::new(SigSet::new_empty()); let sig_stack = SgxMutex::new(None); let profiler = if cfg!(feature = "syscall_timing") { diff --git a/src/libos/src/signal/sig_dispositions.rs b/src/libos/src/signal/sig_dispositions.rs index 736ad77b..d126430d 100644 --- a/src/libos/src/signal/sig_dispositions.rs +++ b/src/libos/src/signal/sig_dispositions.rs @@ -27,10 +27,32 @@ impl SigDispositions { self.map[idx] = sa; } + pub fn set_default(&mut self, num: SigNum) { + let idx = Self::num_to_idx(num); + self.map[idx] = SigAction::Dfl; + } + pub fn iter<'a>(&'a self) -> SigDispositionsIter<'a> { SigDispositionsIter::new(self) } + // inherit sigdispositions for child process, user defined sigaction should be set to default + pub fn inherit(&mut self) { + for mut sigaction in &mut self.map { + match sigaction { + SigAction::User { + handler_addr, + flags, + restorer_addr, + mask, + } => { + *sigaction = SigAction::Dfl; + } + _ => {} + } + } + } + fn num_to_idx(num: SigNum) -> usize { (num.as_u8() - MIN_STD_SIG_NUM) as usize } @@ -81,7 +103,7 @@ impl Default for SigDispositions { impl fmt::Debug for SigDispositions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "SigDispositions "); + write!(f, "SigDispositions (only none-default is shown) "); let non_default_dispositions = self.iter().filter(|(_, action)| **action != SigAction::Dfl); f.debug_map().entries(non_default_dispositions).finish() } diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 0dcb8708..ca81f297 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -42,7 +42,8 @@ use crate::net::{ use crate::process::{ do_arch_prctl, do_clone, do_exit, do_exit_group, do_futex, do_getegid, do_geteuid, do_getgid, do_getpgid, do_getpid, do_getppid, do_gettid, do_getuid, do_prctl, do_set_tid_address, - do_spawn_for_glibc, do_spawn_for_musl, do_wait4, pid_t, FdOp, SpawnFileActions, ThreadStatus, + do_spawn_for_glibc, do_spawn_for_musl, do_wait4, pid_t, posix_spawnattr_t, FdOp, + SpawnFileActions, ThreadStatus, }; use crate::sched::{do_getcpu, do_sched_getaffinity, do_sched_setaffinity, do_sched_yield}; use crate::signal::{ @@ -412,8 +413,8 @@ macro_rules! process_syscall_table_with_callback { (Mlock2 = 325) => handle_unsupported(), // Occlum-specific system calls - (SpawnGlibc = 359) => do_spawn_for_glibc(child_pid_ptr: *mut u32, path: *const i8, argv: *const *const i8, envp: *const *const i8, fa: *const SpawnFileActions), - (SpawnMusl = 360) => do_spawn_for_musl(child_pid_ptr: *mut u32, path: *const i8, argv: *const *const i8, envp: *const *const i8, fdop_list: *const FdOp), + (SpawnGlibc = 359) => do_spawn_for_glibc(child_pid_ptr: *mut u32, path: *const i8, argv: *const *const i8, envp: *const *const i8, fa: *const SpawnFileActions, attribute_list: *const posix_spawnattr_t), + (SpawnMusl = 360) => do_spawn_for_musl(child_pid_ptr: *mut u32, path: *const i8, argv: *const *const i8, envp: *const *const i8, fdop_list: *const FdOp, attribute_list: *const posix_spawnattr_t), (HandleException = 361) => do_handle_exception(info: *mut sgx_exception_info_t, fpregs: *mut FpRegs, context: *mut CpuContext), (HandleInterrupt = 362) => do_handle_interrupt(info: *mut sgx_interrupt_info_t, fpregs: *mut FpRegs, context: *mut CpuContext), (MountRootFS = 363) => do_mount_rootfs(key_ptr: *const sgx_key_128bit_t, occlum_json_mac_ptr: *const sgx_aes_gcm_128bit_tag_t), diff --git a/test/Makefile b/test/Makefile index 04c0ed6d..ec7eda82 100644 --- a/test/Makefile +++ b/test/Makefile @@ -13,12 +13,12 @@ PASS_LOG = $(BUILD_DIR)/test/.pass FAIL_LOG = $(BUILD_DIR)/test/.fail # Dependencies: need to be compiled but not to run by any Makefile target -TEST_DEPS := client data_sink +TEST_DEPS := client data_sink naughty_child # Tests: need to be compiled and run by test-% target TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe time \ truncate readdir mkdir open stat link symlink chmod chown tls pthread uname rlimit \ server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \ - ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait + ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait spawn_attribute # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput diff --git a/test/naughty_child/Makefile b/test/naughty_child/Makefile new file mode 100644 index 00000000..02032dff --- /dev/null +++ b/test/naughty_child/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := -g +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/naughty_child/main.c b/test/naughty_child/main.c new file mode 100644 index 00000000..a5b28f2d --- /dev/null +++ b/test/naughty_child/main.c @@ -0,0 +1,127 @@ +// This test file may used by other test to test children behaviour spawned. +#include +#include +#include +#include +#include +#include "test.h" + +void sigio_handler(int sig) { + printf("[child] SIGIO is caught in child!\n"); +} + +void sigabort_handler(int sig) { + printf("[child] sigabort is caught in child! This shouldn't happen!\n"); + exit(-1); +} + +// Parent process has set the sigmask of this child process to block SIGABORT by inheritage or posix_spawnattr_t +int test_spawn_attribute_sigmask() { + printf("[child] Run a child process with pid = %d and ppid = %d\n", getpid(), getppid()); + +#ifndef __GLIBC__ + // musl can perform extra checks + struct __sigset_t current_block_sigmask; + struct __sigset_t test; +#else + sigset_t current_block_sigmask, test; +#endif + + sigprocmask(0, NULL, ¤t_block_sigmask); + sigemptyset(&test); + sigaddset(&test, SIGABRT); + +#ifndef __GLIBC__ + if (current_block_sigmask.__bits[0] != test.__bits[0]) { + THROW_ERROR("[child] signask in child process is wrong"); + } +#endif + signal(SIGIO, sigio_handler); + signal(SIGABRT, sigabort_handler); + raise(SIGIO); + raise(SIGABRT); + + printf("[child] child test_spawn_attribute_sigmask - [Ok]\n"); + return 0; +} + +// Parent process will set the sigaction of SIGALRM and SIGILL to SIG_IGN and SIGIO to user-defined handler. Then use posix_spawn attribute to set +// SIGALRM to SIG_DEF. +// Child process should inherit the ignore action of SIGILL and change SIGALRM and SIGIO sigaction to SIG_DEF. +int test_spawn_attribute_sigdef() { + struct sigaction action; + + sigaction(SIGALRM, NULL, &action); + if (action.sa_handler != SIG_DFL) { + THROW_ERROR("[child] sig handler of SIGALRM is wrong"); + } + + sigaction(SIGIO, NULL, &action); + if (action.sa_handler != SIG_DFL) { + THROW_ERROR("[child] sig handler of SIGIO is wrong"); + } + + sigaction(SIGILL, NULL, &action); + if (action.sa_handler != SIG_IGN) { + THROW_ERROR("[child] sig handler of SIGILL is wrong"); + } + + printf("[child] child test_spawn_attribute_sigdef - [Ok]\n"); + return 0; +} + +// ============================================================================ +// Test suite +// ============================================================================ + +#define TEST_NAME_MAX 20 + +int start_test(const char *test_name) { + if (strcmp(test_name, "sigmask") == 0) { + return test_spawn_attribute_sigmask(); + } else if (strcmp(test_name, "sigdef") == 0) { + return test_spawn_attribute_sigdef(); + } else { + fprintf(stderr, "[child] test case not found\n"); + return -1; + } +} + +void print_usage() { + fprintf(stderr, "Usage:\n nauty_child [-t testcase1] [-t testcase2] ...\n\n"); + fprintf(stderr, " Now support testcase: \n"); +} + +int main(int argc, char *argv[]) { + if (argc <= 1) { + print_usage(); + return 0; + } + + int opt; + char *testcase_name = calloc(1, TEST_NAME_MAX); + while ((opt = getopt(argc, argv, "t:")) != -1) { + switch (opt) { + case 't': { + int len = strlen(optarg); + if (len >= TEST_NAME_MAX) { + THROW_ERROR("[child] test case name too long"); + } + memset(testcase_name, 0, TEST_NAME_MAX); + strncpy(testcase_name, optarg, len + 1); + printf("[child] start testcase: %s\n", testcase_name); + int ret = start_test(testcase_name); + if (ret != 0) { + THROW_ERROR("[child] test case failure"); + } + } + break; + default: + print_usage(); + exit(-1); + } + } + + free(testcase_name); + return 0; +} diff --git a/test/spawn_attribute/Makefile b/test/spawn_attribute/Makefile new file mode 100644 index 00000000..e8a4506c --- /dev/null +++ b/test/spawn_attribute/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := -lpthread -g +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/spawn_attribute/main.c b/test/spawn_attribute/main.c new file mode 100644 index 00000000..0194c9bc --- /dev/null +++ b/test/spawn_attribute/main.c @@ -0,0 +1,270 @@ +#include +#include +#include +#include +#include +#include +#include "test.h" + +void sigchld_handler(int sig) { + printf("SIGCHLD is caught in father process!\n"); +} + +void sigio_handler(int sig) { + printf("SIGIO is caught in father process!\n"); +} + +static void *thread_func(void *_arg) { +#ifndef __GLIBC__ + // musl can perform extra checks + // child thread sigmask should be same with father thread + struct __sigset_t *father_thread_mask = (struct __sigset_t *)_arg; + struct __sigset_t current_mask; + sigprocmask(0, NULL, ¤t_mask); + assert(father_thread_mask->__bits[0] == current_mask.__bits[0]); + printf("[child thread] father: %ld, child: %ld\n", father_thread_mask->__bits[0], + current_mask.__bits[0]); +#endif + + // SIGIO is IGNORED and shouldn't be handled + raise(SIGIO); + printf("[child thread] SIGIO is ignored\n"); + raise(SIGABRT); + printf("[child thread] SIGABRT is sigmasked\n"); + + // change sigmask in child thread and monitor in father thread +#ifndef __GLIBC__ + struct __sigset_t new_sigmask; +#else + sigset_t new_sigmask; +#endif + sigemptyset(&new_sigmask); + sigaddset(&new_sigmask, SIGALRM); + sigprocmask(SIG_BLOCK, &new_sigmask, NULL); + + // change SIGIO sigaction in child thread and monitor in father thread + signal(SIGIO, sigio_handler); + printf("[child thread] SIGIO handler is changed\n"); + return NULL; +} + +// Each thread of a process has its own sigmask but a process has the same sigaction for different thread。 +// Father thread set SIGIO to SIG_IGN, and block SIGABRT. Child thread inherits the sigmask and sigaction and +// change both sigmask and sigaction. Father thread's sigmask is not changed but sigaction of SIGIO is changed. +int test_thread_inheritage() { + int ret; + printf("Run a parent process has pid = %d and ppid = %d\n", getpid(), getppid()); + + signal(SIGIO, SIG_IGN); + raise(SIGIO); // this should be ignored + printf("SIGIO is ignored.\n"); + +#ifndef __GLIBC__ + struct __sigset_t sig_set; +#else + sigset_t sig_set; +#endif + sigemptyset (&sig_set); + sigaddset(&sig_set, SIGABRT); + sigprocmask(SIG_BLOCK, &sig_set, NULL); + + // child thread will change the sigmask and change sigaction of SIGIO to signal handler + pthread_t tid; + ret = pthread_create(&tid, NULL, thread_func, (void *)&sig_set); + if (ret != 0) { + THROW_ERROR("create child error"); + } + + pthread_join(tid, NULL); + +#ifndef __GLIBC__ + // sigmask of father thread shouldn't be changed by child thread + struct __sigset_t current_block_sigmask_master; + sigprocmask(0, NULL, ¤t_block_sigmask_master); + assert(current_block_sigmask_master.__bits[0] == sig_set.__bits[0]); +#endif + + // SIGIO sigaction should be changed by child thread + printf("SIGIO should be handled:\n"); + raise(SIGIO); // this should be handled + return 0; +} + +// Parent process sets the sigmask of this child process to block SIGABORT by inheritage or posix_spawnattr_t. +int test_spawn_attribute_setsigmask() { + int ret, child_pid, status; + printf("Run a parent process has pid = %d and ppid = %d\n", getpid(), getppid()); + + // construct child process args + int child_argc = 3; // ./nauty_child -t sigmask + char **child_argv = calloc(1, sizeof(char *) * (child_argc + 1)); + child_argv[0] = strdup("naughty_child"); + child_argv[1] = strdup("-t"); + child_argv[2] = strdup("sigmask"); + + signal(SIGIO, sigio_handler); + sigset_t sig_set; + sigemptyset (&sig_set); + sigaddset(&sig_set, SIGABRT); + sigprocmask(SIG_BLOCK, &sig_set, NULL); + // child process should inherit sigmask to block SIGABRT + ret = posix_spawn(&child_pid, "/bin/naughty_child", NULL, NULL, child_argv, NULL); + if (ret != 0) { + THROW_ERROR("failed to spawn a child process\n"); + } + printf("Spawn a new proces successfully (pid = %d)\n", child_pid); + + ret = waitpid(child_pid, &status, 0); + if (ret < 0) { + THROW_ERROR("failed to wait4 the child process\n"); + } + printf("child process %d exit status = %d\n", child_pid, status); + if (status != 0) { + THROW_ERROR("child process exit with error"); + } + + // make parent process block SIGIO + sigaddset(&sig_set, SIGIO); + sigprocmask(SIG_BLOCK, &sig_set, NULL); + + posix_spawnattr_t attr; + posix_spawnattr_init(&attr); + + posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK); + sigdelset(&sig_set, SIGIO); // Child process don't block SIGIO + posix_spawnattr_setsigmask(&attr, &sig_set); + + ret = posix_spawn(&child_pid, "/bin/naughty_child", NULL, &attr, child_argv, NULL); + if (ret != 0) { + THROW_ERROR("failed to spawn a child process"); + } + printf("Spawn a new proces successfully (pid = %d)\n", child_pid); + + ret = waitpid(child_pid, &status, 0); + if (ret < 0) { + THROW_ERROR("failed to wait4 the child process"); + } + printf("child process %d exit status = %d\n", child_pid, status); + if (status != 0) { + THROW_ERROR("child process exit with error"); + } + + return 0; +} + +// Parent process sets the sigaction of SIGALRM and SIGILL to SIG_IGN and SIGIO to user-defined handler. Then use posix_spawn attribute to set +// SIGALRM to SIG_DEF for child process. +// Child process should inherit the ignore action of SIGILL and change SIGALRM and SIGIO sigaction to SIG_DEF. +int test_spawn_attribute_setsigdef() { + int ret, child_pid, status; + printf("Run a parent process has pid = %d and ppid = %d\n", getpid(), getppid()); + + // construct child process args + int child_argc = 3; // ./nauty_child -t sigdef + char **child_argv = calloc(1, sizeof(char *) * (child_argc + 1)); + child_argv[0] = strdup("naughty_child"); + child_argv[1] = strdup("-t"); + child_argv[2] = strdup("sigdef"); + + // parent process ignore SIGALRM and SIGILL and use user-defined signal handler for SIGIO + signal(SIGIO, sigio_handler); + signal(SIGILL, SIG_IGN); + signal(SIGALRM, SIG_IGN); + raise(SIGIO); + raise(SIGILL); + raise(SIGALRM); + printf("parent process shouldn't handle SIGALRM and SIGILL\n"); + + // use spawn attribute to set SIGALRM to default action + sigset_t child_default_sigset; + sigemptyset(&child_default_sigset); + sigaddset(&child_default_sigset, SIGALRM); + posix_spawnattr_t attr; + posix_spawnattr_init(&attr); + posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGDEF); + posix_spawnattr_setsigdefault(&attr, &child_default_sigset); + + // child process should inherit sigaction to ignore SIGILL and set SIGIO and SIGALRM to SIG_DEF + ret = posix_spawn(&child_pid, "/bin/naughty_child", NULL, &attr, child_argv, NULL); + if (ret != 0) { + THROW_ERROR("failed to spawn a child process"); + } + ret = waitpid(child_pid, &status, 0); + if (ret < 0) { + THROW_ERROR("failed to wait4 the child process"); + } + printf("child process %d exit status = %d\n", child_pid, status); + if (status != 0) { + THROW_ERROR("child process exit with error"); + } + + raise(SIGIO); + raise(SIGILL); + raise(SIGALRM); + printf("parent process shouldn't handle SIGALRM and SIGILL\n"); + return 0; +} + +// Create child process to pass naughty_child test by posix spawn attributes. +int test_multiple_spawn_attribute() { + int ret, child_pid, status; + printf("Run a parent process has pid = %d and ppid = %d\n", getpid(), getppid()); + + // construct child process args + int child_argc = 5; // ./naughty_child -t sigdef -t sigmask + char **child_argv = calloc(1, sizeof(char *) * (child_argc + 1)); + child_argv[0] = strdup("naughty_child"); + child_argv[1] = strdup("-t"); + child_argv[2] = + strdup("sigdef"); // child process SIGALRM and SIGIO have default action and SIGILL is ignored + child_argv[3] = strdup("-t"); + child_argv[4] = strdup("sigmask"); // child process block SIGABORT + + posix_spawnattr_t attr; + posix_spawnattr_init(&attr); + posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK); + + // use spawn attribute to set SIGALRM and SIGIO to default action + sigset_t child_default_sigset; + sigemptyset(&child_default_sigset); + sigaddset(&child_default_sigset, SIGALRM); + sigaddset(&child_default_sigset, SIGIO); + posix_spawnattr_setsigdefault(&attr, &child_default_sigset); + signal(SIGILL, SIG_IGN); // child will inherit this + + sigset_t child_sigmask; + sigemptyset(&child_sigmask); + sigaddset(&child_sigmask, SIGABRT); + posix_spawnattr_setsigmask(&attr, &child_sigmask); + + // child process should inherit sigaction to ignore SIGABRT and set SIGIO to SIG_DEF + ret = posix_spawn(&child_pid, "/bin/naughty_child", NULL, &attr, child_argv, NULL); + if (ret != 0) { + THROW_ERROR("failed to spawn a child process"); + } + ret = waitpid(child_pid, &status, 0); + if (ret < 0) { + THROW_ERROR("failed to wait4 the child process"); + } + printf("child process %d exit status = %d\n", child_pid, status); + if (status != 0) { + THROW_ERROR("child process exit with error"); + } + + return 0; +} + +// ============================================================================ +// Test suite +// ============================================================================ + +static test_case_t test_cases[] = { + TEST_CASE(test_thread_inheritage), + TEST_CASE(test_spawn_attribute_setsigmask), + TEST_CASE(test_spawn_attribute_setsigdef), + TEST_CASE(test_multiple_spawn_attribute), +}; + +int main() { + return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); +}