diff --git a/LibOS/shim/include/shim_signal.h b/LibOS/shim/include/shim_signal.h index 615af7c2..847eabed 100644 --- a/LibOS/shim/include/shim_signal.h +++ b/LibOS/shim/include/shim_signal.h @@ -5,6 +5,8 @@ #include "shim_types.h" #include "ucontext.h" +#define __WCOREDUMP_BIT 0x80 + void sigaction_make_defaults(struct __kernel_sigaction* sig_action); void thread_sigaction_reset_on_execve(struct shim_thread* thread); diff --git a/LibOS/shim/include/shim_table.h b/LibOS/shim/include/shim_table.h index c404401b..eb02e5bf 100644 --- a/LibOS/shim/include/shim_table.h +++ b/LibOS/shim/include/shim_table.h @@ -384,7 +384,8 @@ int shim_do_fork(void); int shim_do_vfork(void); int shim_do_execve(const char* file, const char** argv, const char** envp); noreturn int shim_do_exit(int error_code); -pid_t shim_do_wait4(pid_t pid, int* stat_addr, int option, struct __kernel_rusage* ru); +long shim_do_waitid(int which, pid_t id, siginfo_t* infop, int options, struct __kernel_rusage* ru); +long shim_do_wait4(pid_t pid, int* stat_addr, int options, struct __kernel_rusage* ru); int shim_do_kill(pid_t pid, int sig); int shim_do_uname(struct new_utsname* buf); int shim_do_semget(key_t key, int nsems, int semflg); diff --git a/LibOS/shim/src/bookkeep/shim_signal.c b/LibOS/shim/src/bookkeep/shim_signal.c index af90f25f..3a7b5ae2 100644 --- a/LibOS/shim/src/bookkeep/shim_signal.c +++ b/LibOS/shim/src/bookkeep/shim_signal.c @@ -844,8 +844,6 @@ int append_signal(struct shim_thread* thread, siginfo_t* info) { return 0; } -#define __WCOREDUMP_BIT 0x80 - static void sighandler_kill(int sig, siginfo_t* info, void* ucontext) { __UNUSED(info); __UNUSED(ucontext); diff --git a/LibOS/shim/src/shim_parser.c b/LibOS/shim/src/shim_parser.c index 7a5a94d4..e94836d5 100644 --- a/LibOS/shim/src/shim_parser.c +++ b/LibOS/shim/src/shim_parser.c @@ -49,7 +49,8 @@ static void parse_ioctlop(va_list*); static void parse_fcntlop(va_list*); static void parse_seek(va_list*); static void parse_at_fdcwd(va_list*); -static void parse_wait_option(va_list*); +static void parse_wait_options(va_list*); +static void parse_waitid_which(va_list*); struct parser_table { int slow; @@ -122,7 +123,7 @@ struct parser_table { [__NR_execve] = {.slow = 1, .parser = {NULL, &parse_exec_args, &parse_exec_envp}}, [__NR_exit] = {.slow = 0, .parser = {NULL}}, - [__NR_wait4] = {.slow = 1, .parser = {NULL, NULL, &parse_wait_option, NULL}}, + [__NR_wait4] = {.slow = 1, .parser = {NULL, NULL, &parse_wait_options, NULL}}, [__NR_kill] = {.slow = 0, .parser = {NULL, &parse_signum, }}, [__NR_uname] = {.slow = 0, .parser = {NULL}}, [__NR_semget] = {.slow = 0, .parser = {NULL}}, @@ -320,7 +321,8 @@ struct parser_table { [__NR_mq_notify] = {.slow = 0, .parser = {NULL}}, [__NR_mq_getsetattr] = {.slow = 0, .parser = {NULL}}, [__NR_kexec_load] = {.slow = 0, .parser = {NULL}}, - [__NR_waitid] = {.slow = 1, .parser = {NULL}}, + [__NR_waitid] = {.slow = 1, + .parser = {&parse_waitid_which, NULL, NULL, &parse_wait_options, NULL}}, [__NR_add_key] = {.slow = 0, .parser = {NULL}}, [__NR_request_key] = {.slow = 0, .parser = {NULL}}, [__NR_keyctl] = {.slow = 0, .parser = {NULL}}, @@ -450,6 +452,27 @@ static inline int is_pointer(const char* type) { debug_vprintf(fmt, ap); \ } while (0) +struct flag_table { + const char *name; + int flag; +}; + +static int parse_flags(int flags, const struct flag_table* all_flags, size_t count) { + bool first = true; + for (size_t i = 0; i < count; i++) + if (flags & all_flags[i].flag) { + if (first) + first = false; + else + PUTCH('|'); + + PUTS(all_flags[i].name); + flags &= ~all_flags[i].flag; + } + + return flags; +} + static inline void parse_string_arg(va_list* ap) { va_list ap_test_arg; va_copy(ap_test_arg, *ap); @@ -649,10 +672,7 @@ static void parse_clone_flags(va_list* ap) { #define FLG(n) \ { "CLONE_" #n, CLONE_##n, } - const struct { - const char* name; - int flag; - } all_flags[] = { + const struct flag_table all_flags[] = { FLG(VM), FLG(FS), FLG(FILES), @@ -678,16 +698,7 @@ static void parse_clone_flags(va_list* ap) { }; #undef FLG - bool printed = false; - for (size_t i = 0; i < ARRAY_SIZE(all_flags); i++) - if (flags & all_flags[i].flag) { - if (printed) - PUTCH('|'); - else - printed = true; - PUTS(all_flags[i].name); - flags &= ~all_flags[i].flag; - } + flags = parse_flags(flags, all_flags, ARRAY_SIZE(all_flags)); #define CLONE_SIGNAL_MASK 0xff int exit_signal = flags & CLONE_SIGNAL_MASK; @@ -1312,9 +1323,45 @@ static void parse_at_fdcwd(va_list* ap) { } } -static void parse_wait_option(va_list* ap) { - int option = va_arg(*ap, int); +static void parse_wait_options(va_list* ap) { + int flags = va_arg(*ap, int); - if (option & WNOHANG) - PUTS("WNOHANG"); +#define FLG(n) { #n, n } + const struct flag_table all_flags[] = { + FLG(WNOHANG), + FLG(WNOWAIT), + FLG(WEXITED), + FLG(WSTOPPED), + FLG(WCONTINUED), + FLG(WUNTRACED), + }; +#undef FLG + + flags = parse_flags(flags, all_flags, ARRAY_SIZE(all_flags)); + if (flags) + PRINTF("|0x%x", flags); +} + +static void parse_waitid_which(va_list* ap) { + int which = va_arg(*ap, int); + + switch (which) { + case P_ALL: + PUTS("P_ALL"); + break; + case P_PID: + PUTS("P_PID"); + break; + case P_PGID: + PUTS("P_PGID"); + break; +#ifdef P_PIDFD + case P_PIDFD: + PUTS("P_PIDFD"); + break; +#endif + default: + PRINTF("%d", which); + break; + } } diff --git a/LibOS/shim/src/shim_syscalls.c b/LibOS/shim/src/shim_syscalls.c index 4efb9ae3..f38038c9 100644 --- a/LibOS/shim/src/shim_syscalls.c +++ b/LibOS/shim/src/shim_syscalls.c @@ -307,8 +307,12 @@ DEFINE_SHIM_SYSCALL(execve, 3, shim_do_execve, int, const char*, file, const cha /* exit: sys/shim_exit.c */ DEFINE_SHIM_SYSCALL(exit, 1, shim_do_exit, int, int, error_code) +/* waitid: sys/shim_wait.c */ +DEFINE_SHIM_SYSCALL(waitid, 5, shim_do_waitid, long, int, which, pid_t, id, siginfo_t*, infop, + int, options, struct __kernel_rusage*, ru) + /* wait4: sys/shim_wait.c */ -DEFINE_SHIM_SYSCALL(wait4, 4, shim_do_wait4, pid_t, pid_t, pid, int*, stat_addr, int, option, +DEFINE_SHIM_SYSCALL(wait4, 4, shim_do_wait4, long, pid_t, pid, int*, stat_addr, int, options, struct __kernel_rusage*, ru) /* kill: sys/shim_sigaction.c */ @@ -840,9 +844,6 @@ SHIM_SYSCALL_RETURN_ENOSYS(kexec_load, 4, int, unsigned long, entry, unsigned lo struct kexec_segment*, segments, unsigned long, flags) */ -SHIM_SYSCALL_RETURN_ENOSYS(waitid, 5, int, int, which, pid_t, pid, siginfo_t*, infop, int, options, - struct __kernel_rusage*, ru) - /* SHIM_SYSCALL_RETURN_ENOSYS(add_key, 5, int, const char*, type, const char*, description, const void*, payload, size_t, plen, key_serial_t, destringid) diff --git a/LibOS/shim/src/sys/shim_wait.c b/LibOS/shim/src/sys/shim_wait.c index f6a8ccb7..d7c17dfc 100644 --- a/LibOS/shim/src/sys/shim_wait.c +++ b/LibOS/shim/src/sys/shim_wait.c @@ -15,25 +15,43 @@ #include "pal.h" #include "pal_error.h" #include "shim_internal.h" +#include "shim_signal.h" #include "shim_table.h" #include "shim_thread.h" #include "shim_utils.h" -pid_t shim_do_wait4(pid_t pid, int* status, int option, struct __kernel_rusage* ru) { + +/* For wait4() return value */ +#define WCOREFLAG 0x80 + + +long shim_do_waitid(int which, pid_t id, siginfo_t* infop, int options, struct __kernel_rusage* ru) { struct shim_thread* cur = get_cur_thread(); struct shim_thread* thread = NULL; - int ret = 0; + int ret; __UNUSED(ru); - if (option & ~(WNOHANG | WUNTRACED | WCONTINUED | __WNOTHREAD | __WCLONE | __WALL)) { + /* Note that we don't support WSTOPPED or WCONTINUED correctly. */ + if (options & ~(WNOHANG | WNOWAIT | WEXITED | WSTOPPED | WCONTINUED | + __WNOTHREAD| __WCLONE | __WALL)) return -EINVAL; - } - if (pid > 0) { + if (!(options & (WEXITED | WSTOPPED | WCONTINUED))) + return -EINVAL; + + if (!(which == P_PGID || which == P_ALL || which == P_PID)) + return -EINVAL; + + if (infop && test_user_memory(infop, sizeof(*infop), /*write=*/true)) + return -EFAULT; + + if (which == P_PID) { + pid_t pid = id; + if (!(thread = lookup_thread(pid))) return -ECHILD; - if (!(option & WNOHANG)) { + if (!(options & WNOHANG)) { block_pid: object_wait_with_retry(thread->exit_event); } @@ -42,13 +60,18 @@ pid_t shim_do_wait4(pid_t pid, int* status, int option, struct __kernel_rusage* if (thread->is_alive) { unlock(&thread->lock); - if (!(option & WNOHANG)) + if (!(options & WNOHANG)) goto block_pid; put_thread(thread); + + if (infop) { + infop->si_pid = 0; + infop->si_signo = 0; + } return 0; } - if (!LIST_EMPTY(thread, siblings)) { + if (!(options & WNOWAIT) && !LIST_EMPTY(thread, siblings)) { debug("reaping thread %p\n", thread); struct shim_thread* parent = thread->parent; assert(parent); @@ -75,7 +98,7 @@ pid_t shim_do_wait4(pid_t pid, int* status, int option, struct __kernel_rusage* return -ECHILD; } - if (!(option & WNOHANG)) { + if (!(options & WNOHANG)) { block: if (cur->child_exit_event) while (LISTP_EMPTY(&cur->exited_children)) { @@ -85,47 +108,126 @@ pid_t shim_do_wait4(pid_t pid, int* status, int option, struct __kernel_rusage* } } - if (pid == 0 || pid < -1) { - if (pid == 0) - pid = -cur->pgid; + if (which == P_PGID) { + IDTYPE pgid; + if (id == 0) + pgid = cur->pgid; + else + pgid = id; LISTP_FOR_EACH_ENTRY(thread, &cur->exited_children, siblings) { - if (thread->pgid == (IDTYPE)-pid) + if (thread->pgid == pgid) { + get_thread(thread); goto found_child; + } } - if (!(option & WNOHANG)) + if (!(options & WNOHANG)) goto block; } else { + assert(which == P_ALL); + if (!LISTP_EMPTY(&cur->exited_children)) { thread = LISTP_FIRST_ENTRY(&cur->exited_children, struct shim_thread, siblings); + get_thread(thread); goto found_child; } } unlock(&cur->lock); + + if (infop) { + infop->si_pid = 0; + infop->si_signo = 0; + } return 0; found_child: - LISTP_DEL_INIT(thread, &cur->exited_children, siblings); - put_thread(cur); - thread->parent = NULL; + if (!(options & WNOWAIT)) { + LISTP_DEL_INIT(thread, &cur->exited_children, siblings); + put_thread(cur); + thread->parent = NULL; - if (LISTP_EMPTY(&cur->exited_children)) - DkEventClear(cur->child_exit_event); + if (LISTP_EMPTY(&cur->exited_children)) + DkEventClear(cur->child_exit_event); + } unlock(&cur->lock); found: - if (status) { - /* Bits 0--7 are for the signal, if any. - * Bits 8--15 are for the exit code */ - *status = thread->term_signal; - *status |= ((thread->exit_code & 0xff) << 8); + ret = 0; + if (infop) { + infop->si_pid = thread->tid; + infop->si_uid = thread->uid; + infop->si_signo = SIGCHLD; + + if (thread->term_signal == 0) { + infop->si_code = CLD_EXITED; + infop->si_status = thread->exit_code; + } else if (thread->term_signal & __WCOREDUMP_BIT) { + infop->si_code = CLD_DUMPED; + infop->si_status = thread->term_signal & ~__WCOREDUMP_BIT; + } else { + infop->si_code = CLD_KILLED; + infop->si_status = thread->term_signal; + } } - ret = thread->tid; - del_thread(thread); + if (!(options & WNOWAIT)) + del_thread(thread); + put_thread(thread); return ret; } + +long shim_do_wait4(pid_t pid, int* status, int options, struct __kernel_rusage* ru) { + int which; + pid_t id; + siginfo_t info; + + /* Note that only WNOHANG is handled correctly. */ + if (options & ~(WNOHANG | WUNTRACED | WCONTINUED | __WNOTHREAD | __WCLONE | __WALL)) { + return -EINVAL; + } + + if (status && test_user_memory(status, sizeof(*status), /*write=*/true)) + return -EFAULT; + + /* Prepare options for shim_do_waitid(). */ + options |= WEXITED; + if (options & WUNTRACED) + options &= ~WUNTRACED; + + if (pid < -1) { + which = P_PGID; + id = -pid; + } else if (pid == -1) { + which = P_ALL; + id = 0; + } else if (pid == 0) { + which = P_PGID; + id = 0; + } else { + which = P_PID; + id = pid; + } + + info.si_pid = 0; + int ret = shim_do_waitid(which, id, &info, options, ru); + if (ret < 0) + return ret; + + if (info.si_pid == 0) + return 0; + + if (status) { + if (info.si_code == CLD_EXITED) { + *status = (info.si_status & 0xff) << 8; + } else if (info.si_code == CLD_DUMPED) { + *status = info.si_status | WCOREFLAG; + } else { + *status = info.si_status; + } + } + return info.si_pid; +} diff --git a/LibOS/shim/test/ltp/ltp-sgx.cfg b/LibOS/shim/test/ltp/ltp-sgx.cfg index 1c3e0444..8ba823d5 100644 --- a/LibOS/shim/test/ltp/ltp-sgx.cfg +++ b/LibOS/shim/test/ltp/ltp-sgx.cfg @@ -3685,9 +3685,7 @@ skip = yes [wait402] skip = yes -[waitid01] -skip = yes - +# Not supported: waitid with WSTOPPED and WCONTINUED [waitid02] skip = yes diff --git a/LibOS/shim/test/ltp/ltp.cfg b/LibOS/shim/test/ltp/ltp.cfg index d4df4f94..b7113a54 100644 --- a/LibOS/shim/test/ltp/ltp.cfg +++ b/LibOS/shim/test/ltp/ltp.cfg @@ -3164,18 +3164,18 @@ skip = yes [vmsplice02] skip = yes +# tries to open '/proc/2/stat' [wait401] timeout = 40 must-pass = 2 3 +# tries to open '/proc/sys/kernel/pid_max' [wait402] skip = yes -[waitid01] -skip = yes - +# Not supported: waitid with WSTOPPED and WCONTINUED [waitid02] skip = yes