--- /dev/null
+/*
+ * Check decoding of threads when a non-leader thread invokes execve.
+ *
+ * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
+ * 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 <asm/unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+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 */] <pid changed to %u ...>\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 */] <unfinished ...>\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 */] <unfinished ...>\n"
+ "%-5d <... nanosleep resumed> <unfinished ...>)"
+ " = ?\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 <unfinished ...>\n",
+ leader, sigsetsize);
+ close(fds[1]);
+ (void) k_sigsuspend(&mask);
+ break;
+ case ACTION_nanosleep:
+ printf("%-5d nanosleep({tv_sec=%u, tv_nsec=0}"
+ ", <unfinished ...>\n",
+ leader, (unsigned int) ts.tv_sec);
+ close(fds[1]);
+ (void) nanosleep(&ts, 0);
+ break;
+ }
+
+ return 1;
+}
--- /dev/null
+#!/bin/sh
+#
+# Check decoding of threads when a non-leader thread invokes execve.
+#
+# Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
+# 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