/* * Check decoding of waitid syscall. * * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@altlinux.org> * Copyright (c) 2016-2017 The strace developers. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "tests.h" #include <assert.h> #include <signal.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> #include <sys/resource.h> #include <asm/unistd.h> static const char * sprint_rusage(const struct rusage *const ru) { static char buf[1024]; snprintf(buf, sizeof(buf), "{ru_utime={tv_sec=%lld, tv_usec=%llu}" ", ru_stime={tv_sec=%lld, tv_usec=%llu}" #if VERBOSE ", ru_maxrss=%llu" ", ru_ixrss=%llu" ", ru_idrss=%llu" ", ru_isrss=%llu" ", ru_minflt=%llu" ", ru_majflt=%llu" ", ru_nswap=%llu" ", ru_inblock=%llu" ", ru_oublock=%llu" ", ru_msgsnd=%llu" ", ru_msgrcv=%llu" ", ru_nsignals=%llu" ", ru_nvcsw=%llu" ", ru_nivcsw=%llu}" #else ", ...}" #endif , (long long) ru->ru_utime.tv_sec , zero_extend_signed_to_ull(ru->ru_utime.tv_usec) , (long long) ru->ru_stime.tv_sec , zero_extend_signed_to_ull(ru->ru_stime.tv_usec) #if VERBOSE , zero_extend_signed_to_ull(ru->ru_maxrss) , zero_extend_signed_to_ull(ru->ru_ixrss) , zero_extend_signed_to_ull(ru->ru_idrss) , zero_extend_signed_to_ull(ru->ru_isrss) , zero_extend_signed_to_ull(ru->ru_minflt) , zero_extend_signed_to_ull(ru->ru_majflt) , zero_extend_signed_to_ull(ru->ru_nswap) , zero_extend_signed_to_ull(ru->ru_inblock) , zero_extend_signed_to_ull(ru->ru_oublock) , zero_extend_signed_to_ull(ru->ru_msgsnd) , zero_extend_signed_to_ull(ru->ru_msgrcv) , zero_extend_signed_to_ull(ru->ru_nsignals) , zero_extend_signed_to_ull(ru->ru_nvcsw) , zero_extend_signed_to_ull(ru->ru_nivcsw) #endif ); return buf; } #define CASE(x) case x: return #x static const char * si_code_2_name(const int code) { switch (code) { #ifdef CLD_EXITED CASE(CLD_EXITED); #endif #ifdef CLD_KILLED CASE(CLD_KILLED); #endif #ifdef CLD_DUMPED CASE(CLD_DUMPED); #endif #ifdef CLD_TRAPPED CASE(CLD_TRAPPED); #endif #ifdef CLD_STOPPED CASE(CLD_STOPPED); #endif #ifdef CLD_CONTINUED CASE(CLD_CONTINUED); #endif default: perror_msg_and_fail("unknown si_code %d", code); } } static const char * sprint_siginfo(const siginfo_t *const si, const char *const status_text) { static char buf[1024]; snprintf(buf, sizeof(buf), "{si_signo=SIGCHLD" ", si_code=%s" ", si_pid=%u" ", si_uid=%u" ", si_status=%s" ", si_utime=%llu" ", si_stime=%llu}", si_code_2_name(si->si_code), si->si_pid, si->si_uid, status_text, zero_extend_signed_to_ull(si->si_utime), zero_extend_signed_to_ull(si->si_stime)); return buf; } static unsigned long poison(unsigned int v) { return (unsigned long) 0xfacefeed00000000ULL | v; } static long do_waitid(const unsigned int idtype, const unsigned int id, const siginfo_t *const infop, const unsigned int options, const struct rusage *const rusage) { sigset_t mask = {}; sigaddset(&mask, SIGCHLD); assert(sigprocmask(SIG_BLOCK, &mask, NULL) == 0); long rc = syscall(__NR_waitid, poison(idtype), poison(id), infop, poison(options), rusage); assert(sigprocmask(SIG_UNBLOCK, &mask, NULL) == 0); return rc; } int main(void) { tprintf("%s", ""); int fds[2]; if (pipe(fds)) perror_msg_and_fail("pipe"); pid_t pid; pid = fork(); if (pid < 0) perror_msg_and_fail("fork"); if (!pid) { char c; (void) close(1); assert(read(0, &c, sizeof(c)) == 1); return 42; } (void) close(0); if (do_waitid(P_PID, pid, 0, WNOHANG|WEXITED, 0)) perror_msg_and_fail("waitid #1"); tprintf("waitid(P_PID, %d, NULL, WNOHANG|WEXITED, NULL) = 0\n", pid); TAIL_ALLOC_OBJECT_CONST_PTR(siginfo_t, sinfo); memset(sinfo, 0, sizeof(*sinfo)); TAIL_ALLOC_OBJECT_CONST_PTR(struct rusage, rusage); if (do_waitid(P_PID, pid, sinfo, WNOHANG|WEXITED|WSTOPPED, rusage)) perror_msg_and_fail("waitid #2"); tprintf("waitid(P_PID, %d, {}, WNOHANG|WEXITED|WSTOPPED, %s) = 0\n", pid, sprint_rusage(rusage)); assert(write(1, "", 1) == 1); (void) close(1); if (do_waitid(P_PID, pid, sinfo, WEXITED, rusage)) perror_msg_and_fail("waitid #3"); tprintf("waitid(P_PID, %d, %s, WEXITED, %s) = 0\n", pid, sprint_siginfo(sinfo, "42"), sprint_rusage(rusage)); pid = fork(); if (pid < 0) perror_msg_and_fail("fork"); if (!pid) { (void) raise(SIGUSR1); return 1; } if (do_waitid(P_PID, pid, sinfo, WEXITED, rusage)) perror_msg_and_fail("waitid #4"); tprintf("waitid(P_PID, %d, %s, WEXITED, %s) = 0\n", pid, sprint_siginfo(sinfo, "SIGUSR1"), sprint_rusage(rusage)); if (pipe(fds)) perror_msg_and_fail("pipe"); pid = fork(); if (pid < 0) perror_msg_and_fail("fork"); if (!pid) { (void) close(1); raise(SIGSTOP); char c; assert(read(0, &c, sizeof(c)) == 1); return 0; } (void) close(0); if (do_waitid(P_PID, pid, sinfo, WSTOPPED, rusage)) perror_msg_and_fail("waitid #5"); tprintf("waitid(P_PID, %d, %s, WSTOPPED, %s) = 0\n", pid, sprint_siginfo(sinfo, "SIGSTOP"), sprint_rusage(rusage)); if (kill(pid, SIGCONT)) perror_msg_and_fail("kill(SIGCONT)"); #if defined WCONTINUED if (do_waitid(P_PID, pid, sinfo, WCONTINUED, rusage)) perror_msg_and_fail("waitid #6"); tprintf("waitid(P_PID, %d, %s, WCONTINUED, %s) = 0\n", pid, sprint_siginfo(sinfo, "SIGCONT"), sprint_rusage(rusage)); #endif /* WCONTINUED */ assert(write(1, "", 1) == 1); (void) close(1); if (do_waitid(P_PID, pid, sinfo, WEXITED, rusage)) perror_msg_and_fail("waitid #7"); tprintf("waitid(P_PID, %d, %s, WEXITED, %s) = 0\n", pid, sprint_siginfo(sinfo, "0"), sprint_rusage(rusage)); long rc = do_waitid(P_ALL, -1, sinfo, WEXITED|WSTOPPED, rusage); tprintf("waitid(P_ALL, -1, %p, WEXITED|WSTOPPED, %p)" " = %ld %s (%m)\n", sinfo, rusage, rc, errno2name()); tprintf("%s\n", "+++ exited with 0 +++"); return 0; }