From 0aaa6976ddcce8224124cacfc88380c6e5c355d3 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Mon, 28 Nov 2016 00:31:59 +0000 Subject: [PATCH] tests: check decoding of threads when a non-leader thread invokes execve * tests/threads-execve.c: New file. * tests/threads-execve.test: New test. * tests/.gitignore: Add threads-execve. * tests/Makefile.am (check_PROGRAMS): Likewise. (threads_execve_LDADD): New variable. (MISC_TESTS): Add threads-execve.test. --- tests/.gitignore | 1 + tests/Makefile.am | 3 + tests/threads-execve.c | 241 ++++++++++++++++++++++++++++++++++++++ tests/threads-execve.test | 41 +++++++ 4 files changed, 286 insertions(+) create mode 100644 tests/threads-execve.c create mode 100755 tests/threads-execve.test diff --git a/tests/.gitignore b/tests/.gitignore index ebd626fb..2970f649 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -315,6 +315,7 @@ sync_file_range2 sysinfo syslog tee +threads-execve time timer_create timer_xettime diff --git a/tests/Makefile.am b/tests/Makefile.am index 24f29cbf..45428496 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -373,6 +373,7 @@ check_PROGRAMS = \ sysinfo \ syslog \ tee \ + threads-execve \ time \ timer_create \ timer_xettime \ @@ -436,6 +437,7 @@ preadv_pwritev_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64 pwritev_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64 stat64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64 statfs_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64 +threads_execve_LDADD = -lrt -lpthread $(LDADD) times_LDADD = -lrt $(LDADD) truncate64_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64 uio_CPPFLAGS = $(AM_CPPFLAGS) -D_FILE_OFFSET_BITS=64 @@ -815,6 +817,7 @@ MISC_TESTS = \ strace-t.test \ strace-tt.test \ strace-ttt.test \ + threads-execve.test \ vfork-f.test \ # end of MISC_TESTS diff --git a/tests/threads-execve.c b/tests/threads-execve.c new file mode 100644 index 00000000..c3faa14a --- /dev/null +++ b/tests/threads-execve.c @@ -0,0 +1,241 @@ +/* + * Check decoding of threads when a non-leader thread invokes execve. + * + * Copyright (c) 2016 Dmitry V. Levin + * 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 +#include +#include +#include +#include +#include +#include +#include + +static pid_t leader; +static pid_t tid; + +static void +handler(int signo) +{ +} + +static unsigned int sigsetsize; +static long +k_sigsuspend(const sigset_t *const set) +{ + return syscall(__NR_rt_sigsuspend, set, sigsetsize); +} + +static pid_t +k_gettid(void) +{ + return syscall(__NR_gettid); +} + +static void +get_sigsetsize(void) +{ + static const struct sigaction sa = { .sa_handler = handler }; + if (sigaction(SIGUSR1, &sa, NULL)) + perror_msg_and_fail("sigaction"); + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &mask, NULL)) + perror_msg_and_fail("sigprocmask"); + + raise(SIGUSR1); + + sigemptyset(&mask); + for (sigsetsize = sizeof(mask) / sizeof(long); + sigsetsize; sigsetsize >>= 1) { + long rc = k_sigsuspend(&mask); + if (!rc) + error_msg_and_fail("rt_sigsuspend"); + if (EINTR == errno) + break; + printf("%-5d rt_sigsuspend(%p, %u) = %s\n", + leader, &mask, sigsetsize, sprintrc(rc)); + } + if (!sigsetsize) + perror_msg_and_fail("rt_sigsuspend"); + printf("%-5d rt_sigsuspend([], %u) = ? ERESTARTNOHAND" + " (To be restarted if no handler)\n", leader, sigsetsize); +} + +enum { + ACTION_exit = 0, + ACTION_rt_sigsuspend, + ACTION_nanosleep, + NUMBER_OF_ACTIONS +}; + +static const unsigned int NUMBER_OF_ITERATIONS = 4; +static unsigned int action; +static int fds[2]; + +static unsigned int +arglen(char **args) +{ + char **p; + + for (p = args; *p; ++p) + ; + + return p - args; +} + +static void * +thread(void *arg) +{ + tid = k_gettid(); + + static char buf[sizeof(action) * 3]; + sprintf(buf, "%u", action + 1); + + char **argv = arg; + argv[2] = buf; + + if (read(fds[0], fds, sizeof(fds[0]))) + perror_msg_and_fail("execve"); + + struct timespec ts = { .tv_nsec = 100000000 }; + (void) clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL); + + ts.tv_nsec = 12345; + printf("%-5d nanosleep({tv_sec=0, tv_nsec=%u}, NULL) = 0\n", + tid, (unsigned int) ts.tv_nsec); + + switch (action % NUMBER_OF_ACTIONS) { + case ACTION_exit: + printf("%-5d execve(\"%s\", [\"%s\", \"%s\", \"%s\"]" + ", [/* %u vars */] \n", + tid, argv[0], argv[0], argv[1], argv[2], + arglen(environ), leader); + break; + case ACTION_rt_sigsuspend: + printf("%-5d execve(\"%s\", [\"%s\", \"%s\", \"%s\"]" + ", [/* %u vars */] \n" + "%-5d <... rt_sigsuspend resumed>) = ?\n", + tid, argv[0], argv[0], argv[1], argv[2], + arglen(environ), + leader); + break; + case ACTION_nanosleep: + printf("%-5d execve(\"%s\", [\"%s\", \"%s\", \"%s\"]" + ", [/* %u vars */] \n" + "%-5d <... nanosleep resumed> )" + " = ?\n", + tid, argv[0], argv[0], argv[1], argv[2], + arglen(environ), + leader); + break; + } + + printf("%-5d +++ superseded by execve in pid %u +++\n" + "%-5d <... execve resumed> ) = 0\n", + leader, tid, + leader); + + (void) nanosleep(&ts, NULL); + execve(argv[0], argv, environ); + perror_msg_and_fail("execve"); +} + +int +main(int ac, char **av) +{ + setvbuf(stdout, NULL, _IONBF, 0); + leader = getpid(); + + if (ac < 3) { + struct timespec ts = { .tv_nsec = 1 }; + if (clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL)) + perror_msg_and_skip("clock_nanosleep CLOCK_REALTIME"); + + printf("%-5d execve(\"%s\", [\"%s\"], [/* %u vars */]) = 0\n", + leader, av[0], av[0], arglen(environ)); + + get_sigsetsize(); + static char buf[sizeof(sigsetsize) * 3]; + sprintf(buf, "%u", sigsetsize); + + char *argv[] = { av[0], buf, (char *) "0", NULL }; + printf("%-5d execve(\"%s\", [\"%s\", \"%s\", \"%s\"]" + ", [/* %u vars */]) = 0\n", + leader, argv[0], argv[0], argv[1], argv[2], + arglen(environ)); + execve(argv[0], argv, environ); + perror_msg_and_fail("execve"); + } + + sigsetsize = atoi(av[1]); + action = atoi(av[2]); + + if (action >= NUMBER_OF_ACTIONS * NUMBER_OF_ITERATIONS) { + printf("%-5d +++ exited with 0 +++\n", leader); + return 0; + } + + if (pipe(fds)) + perror_msg_and_fail("pipe"); + + pthread_t t; + errno = pthread_create(&t, NULL, thread, av); + if (errno) + perror_msg_and_fail("pthread_create"); + + struct timespec ts = { .tv_sec = 123 }; + sigset_t mask; + sigemptyset(&mask); + + switch (action % NUMBER_OF_ACTIONS) { + case ACTION_exit: + printf("%-5d exit(42) = ?\n", leader); + close(fds[1]); + (void) syscall(__NR_exit, 42); + break; + case ACTION_rt_sigsuspend: + printf("%-5d rt_sigsuspend([], %u \n", + leader, sigsetsize); + close(fds[1]); + (void) k_sigsuspend(&mask); + break; + case ACTION_nanosleep: + printf("%-5d nanosleep({tv_sec=%u, tv_nsec=0}" + ", \n", + leader, (unsigned int) ts.tv_sec); + close(fds[1]); + (void) nanosleep(&ts, 0); + break; + } + + return 1; +} diff --git a/tests/threads-execve.test b/tests/threads-execve.test new file mode 100755 index 00000000..49d69b5e --- /dev/null +++ b/tests/threads-execve.test @@ -0,0 +1,41 @@ +#!/bin/sh +# +# Check decoding of threads when a non-leader thread invokes execve. +# +# Copyright (c) 2016 Dmitry V. Levin +# 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. + +. "${srcdir=.}/init.sh" + +# Check that PTRACE_EVENT_EXEC support in kernel is good enough. +# The kernel before commit v3.1-rc1~308^2~7 reported 0 instead of old pid. +uname_r="$(uname -r)" +case "$uname_r" in + 3.[2-9]*|3.1[0-9]*|[4-9].*|[123][0-9]*) ;; + *) skip_ "the kernel release $uname_r is not 3.1 or newer" ;; +esac + +run_strace_match_diff -a21 \ + -f -esignal=none -e trace=execve,exit,nanosleep,rt_sigsuspend -- 2.40.0