Upgrade to upstream tip of tree strace.
Change-Id: I56ccbfbb64885c61f160145f181e42dab78adefb
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644
index 0000000..c73b64a
--- /dev/null
+++ b/test/.gitignore
@@ -0,0 +1,14 @@
+vfork
+fork
+sig
+skodic
+clone
+leaderkill
+childthread
+sigkill_rain
+wait_must_be_interruptible
+threaded_execve
+mtd
+ubi
+select
+sigreturn
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..cc7d47a
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,17 @@
+CFLAGS += -Wall
+
+PROGS = \
+ vfork fork sig skodic clone leaderkill childthread \
+ sigkill_rain wait_must_be_interruptible threaded_execve \
+ mtd ubi select sigreturn
+
+all: $(PROGS)
+
+leaderkill: LDFLAGS += -pthread
+
+childthread: LDFLAGS += -pthread
+
+clean distclean:
+ rm -f *.o core $(PROGS) *.gdb
+
+.PHONY: all clean distclean
diff --git a/test/README b/test/README
new file mode 100644
index 0000000..7fae09b
--- /dev/null
+++ b/test/README
@@ -0,0 +1,17 @@
+To run a test:
+* Run make
+* Run resulting executable(s) under strace
+* Check strace output and/or program's output and exitcode
+
+To add a new test:
+* Add its .c source to this dir
+* Add it to "all" and "clean" targets in Makefile
+* Add it to .gitignore file
+
+Please spend some time making your testcase understandable.
+For example, it may print an explanation how it should be used
+(which strace options to use, and what to look for in strace output).
+
+If possible, make it so that your testcase detects error/bug
+it is intended to test for, and prints error message and exits with 1
+if the bug is detected, instead of relying on user to peruse strace output.
diff --git a/test/childthread.c b/test/childthread.c
new file mode 100644
index 0000000..c86b9cf
--- /dev/null
+++ b/test/childthread.c
@@ -0,0 +1,60 @@
+/* Test exit of a child of a TCB_EXITING child where the toplevel process starts
+ * waiting on it. The middle one gets detached and strace must update the
+ * toplevel process'es number of attached children to 0.
+ *
+ * gcc -o test/childthread test/childthread.c -Wall -ggdb2 -pthread;./strace -f ./test/childthread
+ * It must print: write(1, "OK\n", ...
+ */
+
+#include <pthread.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+static void *start0(void *arg)
+{
+ pause();
+ /* NOTREACHED */
+ assert(0);
+}
+
+int main(int argc, char *argv[])
+{
+ pthread_t thread0;
+ pid_t child, got_pid;
+ int status;
+ int i;
+
+ child = fork();
+
+ switch (child) {
+ case -1:
+ assert(0);
+ case 0:
+ i = pthread_create(&thread0, NULL, start0, NULL);
+ assert(i == 0);
+ /* The thread must be initialized, it becomes thread-child of this
+ process-child (child of a child of the toplevel process). */
+ sleep(1);
+ /* Here the child TCB cannot be deallocated as there still exist
+ * children (the thread child in START0). */
+ exit(42);
+ /* NOTREACHED */
+ assert(0);
+ default:
+ /* We must not be waiting in WAITPID when the child double-exits. */
+ sleep(2);
+ /* PID must be -1. */
+ got_pid = waitpid(-1, &status, 0);
+ assert(got_pid == child);
+ assert(WIFEXITED(status));
+ assert(WEXITSTATUS(status) == 42);
+ puts("OK");
+ exit(0);
+ }
+
+ /* NOTREACHED */
+ assert(0);
+}
diff --git a/test/clone.c b/test/clone.c
new file mode 100644
index 0000000..bdce8b1
--- /dev/null
+++ b/test/clone.c
@@ -0,0 +1,21 @@
+/* for CLONE_foo: */
+#define _GNU_SOURCE 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <unistd.h>
+
+int child(void* arg)
+{
+ write(1, "clone\n", 6);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char stack[4096];
+ clone(child, stack+4000, CLONE_VM|CLONE_FS|CLONE_FILES, NULL);
+ write(1, "original\n", 9);
+ exit(0);
+}
diff --git a/test/fork.c b/test/fork.c
new file mode 100644
index 0000000..1425e2d
--- /dev/null
+++ b/test/fork.c
@@ -0,0 +1,15 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+int main(int argc, char *argv[])
+{
+ if (fork() == 0) {
+ write(1, "child\n", 6);
+ } else {
+ wait(0);
+ write(1, "parent\n", 7);
+ }
+
+ exit(0);
+}
diff --git a/test/leaderkill.c b/test/leaderkill.c
new file mode 100644
index 0000000..c24a9f0
--- /dev/null
+++ b/test/leaderkill.c
@@ -0,0 +1,64 @@
+/* Test handle_group_exit() handling of a thread leader still alive with its
+ * thread child calling exit_group() and proper passing of the process exit
+ * code to the process parent of this whole thread group.
+ *
+ * gcc -o test/leaderkill test/leaderkill.c -Wall -ggdb2 -pthread;./test/leaderkill & pid=$!;sleep 1;strace -o x -q ./strace -f -p $pid
+ * It must print: write(1, "OK\n", ...
+ */
+
+#include <pthread.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+static void *start0(void *arg)
+{
+ sleep(1);
+ exit(42);
+}
+
+static void *start1(void *arg)
+{
+ pause();
+ /* NOTREACHED */
+ assert(0);
+}
+
+int main(int argc, char *argv[])
+{
+ pthread_t thread0;
+ pthread_t thread1;
+ pid_t child, got_pid;
+ int status;
+ int i;
+
+ sleep(2);
+
+ child = fork();
+
+ switch (child) {
+ case -1:
+ abort();
+ case 0:
+ i = pthread_create(&thread0, NULL, start0, NULL);
+ assert(i == 0);
+ i = pthread_create(&thread1, NULL, start1, NULL);
+ assert(i == 0);
+ pause();
+ /* NOTREACHED */
+ assert(0);
+ break;
+ default:
+ got_pid = waitpid(child, &status, 0);
+ assert(got_pid == child);
+ assert(WIFEXITED(status));
+ assert(WEXITSTATUS(status) == 42);
+ puts("OK");
+ exit(0);
+ }
+
+ /* NOTREACHED */
+ abort();
+}
diff --git a/test/mmap_offset_decode.c b/test/mmap_offset_decode.c
new file mode 100644
index 0000000..34a708e
--- /dev/null
+++ b/test/mmap_offset_decode.c
@@ -0,0 +1,31 @@
+/* Should strace show byte or page offsets in mmap syscalls
+ * which take page offset parameters?
+ *
+ * At the time of writing, sys_mmap() converts page to byte offsets,
+ * but only for SH64! But this routine is used on i386 too - by mmap2 syscall,
+ * which uses page offsets too. As it stands now, SH64 and i386 are inconsistent.
+ *
+ * sys_old_mmap() is used for old mmap syscall, which uses byte offset -
+ * should be ok.
+ * sys_mmap64() is currently buggy (should print bogus offset, but I can't
+ * test it right now. What arch/bitness invokes sys_mmap64?)
+ *
+ * This program is intended for testing what strace actually shows. Usage:
+ * $ gcc test/mmap_offset_decode.c -o mmap_offset_decode -static
+ * $ strace ./mmap_offset_decode
+ *
+ * As of today (2011-08), on i386 strace prints page offset.
+ * Fixed 2013-02-19. Now all mmaps on all arches should show byte offsets.
+ */
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+#include <sys/mman.h>
+#include <errno.h>
+int main()
+{
+ /* 0x1000 is meant to be page size multiplier */
+ mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1,
+ 0x7fff0000LL * 0x1000);
+ return errno != 0;
+}
diff --git a/test/mtd.c b/test/mtd.c
new file mode 100644
index 0000000..b9fc695
--- /dev/null
+++ b/test/mtd.c
@@ -0,0 +1,49 @@
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+
+int main() {
+ int fd = open("/dev/null", 0);
+ struct mtd_info_user minfo;
+ struct erase_info_user einfo;
+ struct erase_info_user64 einfo64;
+ struct mtd_oob_buf mbuf;
+ struct mtd_oob_buf64 mbuf64;
+ struct region_info_user rinfo;
+ /* struct otp_info oinfo; */
+ struct mtd_ecc_stats estat;
+ struct mtd_write_req mreq;
+ struct nand_oobinfo ninfo;
+ struct nand_ecclayout_user nlay;
+ off_t f = 333;
+
+ memset(&einfo, 0, sizeof(einfo));
+ memset(&einfo64, 0xff, sizeof(einfo64));
+
+ ioctl(fd, MEMGETINFO, &minfo);
+
+ ioctl(fd, MEMERASE, &einfo);
+ ioctl(fd, MEMERASE64, &einfo64);
+
+ ioctl(fd, MEMGETBADBLOCK, &f);
+ int i = 0;
+ ioctl(fd, OTPSELECT, &i);
+ ioctl(fd, MEMSETBADBLOCK, &f);
+
+ ioctl(fd, MEMREADOOB, &mbuf);
+ ioctl(fd, MEMREADOOB64, &mbuf64);
+
+ ioctl(fd, MEMGETREGIONINFO, &rinfo);
+
+ ioctl(fd, ECCGETSTATS, &estat);
+ ioctl(fd, MEMWRITE, &mreq);
+
+ ioctl(fd, MEMGETOOBSEL, &ninfo);
+ ioctl(fd, ECCGETLAYOUT, &nlay);
+
+ return 0;
+}
diff --git a/test/procpollable.c b/test/procpollable.c
new file mode 100644
index 0000000..7bc5efa
--- /dev/null
+++ b/test/procpollable.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/procfs.h>
+#include <sys/stropts.h>
+#include <poll.h>
+
+int main(int argc, char *argv[])
+{
+ int pid;
+ char proc[32];
+ FILE *pfp;
+ struct pollfd pfd;
+
+ pid = fork();
+ if (pid == 0) {
+ pause();
+ exit(0);
+ }
+
+ sprintf(proc, "/proc/%d", pid);
+
+ pfp = fopen(proc, "r+");
+ if (pfp == NULL)
+ goto fail;
+
+ if (ioctl(fileno(pfp), PIOCSTOP, NULL) < 0)
+ goto fail;
+
+ pfd.fd = fileno(pfp);
+ pfd.events = POLLPRI;
+
+ if (poll(&pfd, 1, 0) < 0)
+ goto fail;
+
+ if (!(pfd.revents & POLLPRI))
+ goto fail;
+
+ kill(pid, SIGKILL);
+ exit(0);
+fail:
+ kill(pid, SIGKILL);
+ exit(1);
+}
diff --git a/test/select.c b/test/select.c
new file mode 100644
index 0000000..0810fff
--- /dev/null
+++ b/test/select.c
@@ -0,0 +1,34 @@
+/* dave@treblig.org */
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+char buffer[1024*1024*2];
+
+int main()
+{
+ fd_set rds;
+ struct timeval timeout;
+
+ FD_ZERO(&rds);
+ FD_SET(2, &rds);
+ /* Start with a nice simple select */
+ select(3, &rds, &rds, &rds, NULL);
+
+ /* Now the crash case that trinity found, negative nfds
+ * but with a pointer to a large chunk of valid memory.
+ */
+ FD_ZERO((fd_set*)buffer);
+ FD_SET(2,(fd_set*)buffer);
+ select(-1, (fd_set *)buffer, NULL, NULL, NULL);
+
+ /* Another variant, with nfds exceeding allowed limit. */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 100;
+ select(FD_SETSIZE + 1, (fd_set *)buffer, NULL, NULL, &timeout);
+
+ return 0;
+}
diff --git a/test/sfd.c b/test/sfd.c
new file mode 100644
index 0000000..815da80
--- /dev/null
+++ b/test/sfd.c
@@ -0,0 +1,40 @@
+#include <fcntl.h>
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+ int pid = atoi(argv[1]);
+ int sfd;
+ char sname[32];
+ char buf[1024];
+ char *s;
+ int i;
+ int signal, blocked, ignore, caught;
+
+ sprintf(sname, "/proc/%d/stat", pid);
+
+ sfd = open(sname, O_RDONLY);
+ if (sfd == -1) {
+ perror(sname);
+ return 1;
+ }
+
+ i = read(sfd, buf, 1024);
+ buf[i] = '\0';
+
+ for (i = 0, s = buf; i < 30; i++) {
+ while (*++s != ' ') {
+ if (!*s)
+ break;
+ }
+ }
+
+ if (sscanf(s, "%d%d%d%d", &signal, &blocked, &ignore, &caught) != 4) {
+ fprintf(stderr, "/proc/pid/stat format error\n");
+ return 1;
+ }
+
+ printf("%8x %8x %8x %8x\n", signal, blocked, ignore, caught);
+
+ return 0;
+}
diff --git a/test/sig.c b/test/sig.c
new file mode 100644
index 0000000..1678b02
--- /dev/null
+++ b/test/sig.c
@@ -0,0 +1,19 @@
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+void interrupt()
+{
+ write(2, "xyzzy\n", 6);
+}
+
+int main(int argc, char *argv[])
+{
+ char buf[1024];
+
+ signal(SIGINT, interrupt);
+ read(0, buf, 1024);
+ write(2, "qwerty\n", 7);
+
+ return 0;
+}
diff --git a/test/sigkill_rain.c b/test/sigkill_rain.c
new file mode 100644
index 0000000..59af55b
--- /dev/null
+++ b/test/sigkill_rain.c
@@ -0,0 +1,84 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+static const struct sockaddr sa;
+
+int main(int argc, char *argv[])
+{
+ int loops;
+ int pid;
+ sigset_t set;
+
+ printf(
+"Please run me under 'strace -f -oLOG', and examine LOG file for incorrect\n"
+"decoding of interrupted syscalls: grep for 'sendto', '??" /* anti-trigraph gap */ "?', 'unavailable'.\n"
+"Pass number of iterations in argv[1] (default: 999).\n"
+ );
+ fflush(NULL);
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+
+ loops = 999;
+ if (argv[1])
+ loops = atoi(argv[1]);
+
+ while (--loops >= 0) {
+ pid = fork();
+
+ if (pid < 0)
+ exit(1);
+
+ if (!pid) {
+ /* child */
+ int child = getpid();
+
+ loops = 99;
+ while (--loops) {
+ pid = fork();
+
+ if (pid < 0)
+ exit(1);
+
+ if (!pid) {
+ /* grandchild: kill child */
+ kill(child, SIGKILL);
+ exit(0);
+ }
+
+ /* Add various syscalls you want to test here.
+ * strace will decode them and suddenly find
+ * process disappearing.
+ * But leave at least one case "empty", so that
+ * "kill grandchild" happens quicker.
+ * This produces cases when strace can't even
+ * decode syscall number before process dies.
+ */
+ switch (loops & 1) {
+ case 0:
+ break; /* intentionally empty */
+ case 1:
+ sendto(-1, "Hello cruel world", 17, 0, &sa, sizeof(sa));
+ break;
+ }
+
+ /* kill grandchild */
+ kill(pid, SIGKILL);
+ }
+
+ exit(0);
+ }
+
+ /* parent */
+ wait(NULL);
+ }
+
+ return 0;
+}
diff --git a/test/sigreturn.c b/test/sigreturn.c
new file mode 100644
index 0000000..246a3ce
--- /dev/null
+++ b/test/sigreturn.c
@@ -0,0 +1,30 @@
+/*
+ * Check that strace output contains RT_1 RT_3 RT_31 RT_32 here:
+ * rt_sigprocmask(SIG_BLOCK, [CHLD RT_1 RT_3 RT_31 RT_32], NULL, 8) = 0
+ * and here:
+ * sigreturn() (mask [CHLD RT_1 RT_3 RT_31 RT_32]) = 0
+ *
+ * On x86, both 32-bit and 64-bit strace needs to be checked.
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+
+void null_handler(int sig)
+{
+}
+
+int main(int argc, char *argv[])
+{
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, SIGCHLD);
+ sigaddset(&set, 33);
+ sigaddset(&set, 35);
+ sigaddset(&set, 63);
+ sigaddset(&set, 64);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+ signal(SIGWINCH, null_handler);
+ raise(SIGWINCH);
+ return 0;
+}
diff --git a/test/skodic.c b/test/skodic.c
new file mode 100644
index 0000000..4dcd955
--- /dev/null
+++ b/test/skodic.c
@@ -0,0 +1,37 @@
+/* This demonstrates races: kernel may actually open other file then
+ * you read at strace output. Create /tmp/delme with 10K of zeros and
+ * 666 mode, then run this under strace. If you see open successfull
+ * open of /etc/shadow, you know you've seen a race.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int main(int argc, char *argv[])
+{
+ char *c;
+ int fd;
+
+ fd = open("/tmp/delme", O_RDWR);
+ c = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ *c = 0;
+
+ if (fork()) {
+ while (1) {
+ strcpy(c, "/etc/passwd");
+ strcpy(c, "/etc/shadow");
+ }
+ } else {
+ while (1)
+ if ((fd = open(c, 0)) != -1)
+ close(fd);
+ }
+
+ return 0;
+}
diff --git a/test/threaded_execve.c b/test/threaded_execve.c
new file mode 100644
index 0000000..4c21688
--- /dev/null
+++ b/test/threaded_execve.c
@@ -0,0 +1,147 @@
+/*
+ * Create NUM_THREADS threads which print "1" and sleep in pause().
+ * Then create another thread which prints "2", and re-execs the program.
+ * The leader then either sleeps in pause(), or exits if $LEADER_EXIT is set.
+ * This triggers "execve'ed thread replaces thread leader" case.
+ *
+ * gcc -Wall -Os -o threaded_execve threaded_execve.c
+ *
+ * Try running it under strace like this:
+ *
+ * # Should not be confused by traced execve-ing thread
+ * # replacing traced leader:
+ * strace -oLOG -f ./threaded_execve
+ *
+ * # Same, but different output mode. Output after execve
+ * # should go into leader's LOG.<pid> file, not into execve'ed
+ * # thread's log file:
+ * strace -oLOG -ff ./threaded_execve
+ *
+ * # Should not be confused by non-traced execve-ing thread
+ * # replacing traced leader:
+ * strace -oLOG ./threaded_execve
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * In Linux 3.2, non-traced execve-ing thread does not
+ * become traced after execve, even though it has pid == leader's pid
+ * after execve. And yet, strace's waitpid doesn't return ECHILD.
+ *
+ * # Run for NUM seconds, not just one second.
+ * # Watch top to check for memory leaks in strace:
+ * strace -oLOG -f ./threaded_execve <NUM>
+ *
+ */
+#define NUM_THREADS 1
+
+#define _GNU_SOURCE 1
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sched.h>
+#include <signal.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/syscall.h>
+
+/* Define clone2 for all arches */
+#ifdef __ia64__
+extern int __clone2(int (*fn) (void *), void *child_stack_base,
+ size_t stack_size, int flags, void *arg, ...);
+#define clone2 __clone2
+#elif defined(__metag__)
+#define clone2(func, stack_base, size, flags, arg...) \
+ clone(func, stack_base, flags, arg)
+#else
+#define clone2(func, stack_base, size, flags, arg...) \
+ clone(func, (stack_base) + (size), flags, arg)
+#endif
+/* Direct calls to syscalls, avoiding libc wrappers */
+#define syscall_tgkill(pid, tid, sig) syscall(__NR_tgkill, (pid), (tid), (sig))
+#define syscall_getpid() syscall(__NR_getpid)
+#define syscall_gettid() syscall(__NR_gettid)
+#define syscall_exit(v) syscall(__NR_exit, (v));
+
+static char my_name[PATH_MAX];
+static int leader_final_action;
+
+static int
+thread1(void *unused)
+{
+ write(1, "1", 1);
+ for(;;) pause();
+ return 0;
+}
+
+static int
+thread2(void *unused)
+{
+ char buf[64];
+ sprintf(buf, "%d", leader_final_action);
+ write(1, "2", 1);
+ usleep(20*1000);
+ /* This fails with ENOENT if leader has exited by now! :) */
+ execl("/proc/self/exe", "exe", "exe", buf, NULL);
+ /* So fall back to resolved name */
+ execl(my_name, "exe", "exe", buf, NULL);
+ for(;;) pause();
+ return 0;
+}
+
+static void
+thread_leader(void)
+{
+ /* malloc gives sufficiently aligned buffer.
+ * long buf[] does not! (on ia64).
+ */
+ int cnt = NUM_THREADS;
+ while (--cnt >= 0) {
+ /* As seen in pthread_create(): */
+ clone2(thread1, malloc(16 * 1024), 16 * 1024, 0
+ | CLONE_VM
+ | CLONE_FS
+ | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
+ | 0 /* no signal to send on death */
+ , NULL);
+ usleep(20*1000);
+ }
+ clone2(thread2, malloc(16 * 1024), 16 * 1024, 0
+ | CLONE_VM
+ | CLONE_FS
+ | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
+ | 0 /* no signal to send on death */
+ , NULL);
+
+ /* Various states leader can be while other thread execve's: */
+ switch (leader_final_action % 3) {
+ case 0: syscall_exit(42); /* leader is dead */
+ case 1: for(;;) pause(); /* leader is in syscall */
+ default: for(;;) continue; /* leader is in userspace */
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ if (readlink("/proc/self/exe", my_name, sizeof(my_name)-1) <= 0)
+ return 1;
+
+ setbuf(stdout, NULL);
+
+ if (argv[1] && strcmp(argv[1], "exe") == 0) {
+ leader_final_action = atoi(argv[2]) + 1;
+ thread_leader();
+ }
+
+ printf("%d: thread leader\n", getpid());
+
+ alarm(argv[1] ? atoi(argv[1]) : 1);
+ thread_leader();
+
+ return 0;
+}
diff --git a/test/ubi.c b/test/ubi.c
new file mode 100644
index 0000000..5062c83
--- /dev/null
+++ b/test/ubi.c
@@ -0,0 +1,54 @@
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <mtd/ubi-user.h>
+
+#define zero(x) memset(&x, 0, sizeof(x))
+
+int main() {
+ int fd = open("/dev/null", 0);
+ struct ubi_mkvol_req mkvol = {
+ .vol_id = 3,
+ .alignment = 124,
+ .bytes = 1125899906842624ULL,
+ .vol_type = 3,
+ .name_len = 7,
+ .name = "foobar",
+ };
+ struct ubi_rsvol_req rsvol = {
+ .bytes = 1125899906842624ULL,
+ .vol_id = -3,
+ };
+ struct ubi_rnvol_req rnvol = {
+ .count = 300,
+ };
+ struct ubi_attach_req attach;
+ struct ubi_map_req map;
+ struct ubi_set_vol_prop_req prop = {
+ .property = 1,
+ .value = 1125899906842624ULL,
+ };
+ uint64_t bytes = ((uint64_t)1 << 50) | 0x123;
+
+ ioctl(fd, UBI_IOCMKVOL, &mkvol);
+ ioctl(fd, UBI_IOCRSVOL, &rsvol);
+ ioctl(fd, UBI_IOCRNVOL, &rnvol);
+ ioctl(fd, UBI_IOCATT, &attach);
+ ioctl(fd, UBI_IOCVOLUP, &bytes);
+ ioctl(fd, UBI_IOCEBMAP, &map);
+ ioctl(fd, UBI_IOCSETVOLPROP, &prop);
+ zero(prop);
+ ioctl(fd, UBI_IOCSETVOLPROP, &prop);
+ ioctl(fd, UBI_IOCRMVOL, 1);
+ ioctl(fd, UBI_IOCDET, 2);
+ ioctl(fd, UBI_IOCEBER, 3);
+ ioctl(fd, UBI_IOCEBCH, 4);
+ ioctl(fd, UBI_IOCEBUNMAP, 5);
+ ioctl(fd, UBI_IOCEBISMAP, 6);
+
+ return 0;
+}
diff --git a/test/vfork.c b/test/vfork.c
new file mode 100644
index 0000000..f49a473
--- /dev/null
+++ b/test/vfork.c
@@ -0,0 +1,15 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+int main(int argc, char *argv[])
+{
+ if (vfork() == 0) {
+ write(1, "child\n", 6);
+ } else {
+ wait(0);
+ write(1, "parent\n", 7);
+ }
+
+ exit(0);
+}
diff --git a/test/wait_must_be_interruptible.c b/test/wait_must_be_interruptible.c
new file mode 100644
index 0000000..3fb3449
--- /dev/null
+++ b/test/wait_must_be_interruptible.c
@@ -0,0 +1,84 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Expected order is:
+ * Child signals parent
+ * Parent got signal
+ * Child will exit now
+ *
+ * The bug we test for: under strace -f, last two lines are swapped
+ * because wait syscall is suspended by strace and thus can't be interrupted.
+ */
+
+static const char msg1[] = "Child signals parent\n";
+static const char msg2[] = "Parent got signal\n";
+static const char msg3[] = "Child will exit now\n";
+
+static void handler(int s)
+{
+ write(1, msg2, sizeof(msg2)-1);
+}
+
+static void test()
+{
+ /* Note: in Linux, signal() installs handler with SA_RESTART flag,
+ * therefore wait will be restarted.
+ */
+ signal(SIGALRM, handler);
+
+ if (fork() == 0) {
+ /* child */
+ sleep(1);
+ write(1, msg1, sizeof(msg1)-1);
+ kill(getppid(), SIGALRM);
+ sleep(1);
+ write(1, msg3, sizeof(msg3)-1);
+ _exit(0);
+ }
+
+ /* parent */
+ wait(NULL);
+ _exit(0);
+}
+
+int main()
+{
+ char buf1[80];
+ char buf2[80];
+ char buf3[80];
+ int pipefd[2];
+
+ printf("Please run me under 'strace -f'\n");
+
+ pipe(pipefd);
+
+ if (fork() == 0) {
+ if (pipefd[1] != 1) {
+ dup2(pipefd[1], 1);
+ close(pipefd[1]);
+ }
+ test();
+ }
+
+ if (pipefd[0] != 0) {
+ dup2(pipefd[0], 0);
+ close(pipefd[0]);
+ }
+ fgets(buf1, 80, stdin); printf("%s", buf1);
+ fgets(buf2, 80, stdin); printf("%s", buf2);
+ fgets(buf3, 80, stdin); printf("%s", buf3);
+
+ if (strcmp(buf1, msg1) != 0
+ || strcmp(buf2, msg2) != 0
+ || strcmp(buf3, msg3) != 0
+ ) {
+ printf("ERROR! Expected order:\n%s%s%s", msg1, msg2, msg3);
+ return 1;
+ }
+ printf("Good: wait seems to be correctly interrupted by signals\n");
+ return 0;
+}
diff --git a/test/x32_lseek.c b/test/x32_lseek.c
new file mode 100644
index 0000000..6faa2fa
--- /dev/null
+++ b/test/x32_lseek.c
@@ -0,0 +1,38 @@
+// Test program which explores whether lseek syscall (not llseek!)
+// on x32 uses 64-bit offset argument.
+// IOW: does _kernel_ truncate it on entry?
+// The answer appears to be "no, full 64-bit offset is used".
+// strace must show it correctly too - tricky if strace itself is x32 one!
+//
+// Build: x86_64-gcc -static -Wall -ox32_lseek x32_lseek.c
+// Run: $ strace ./x32_lseek 2>&1 | grep lseek | grep 1250999896321
+// lseek(0, 1250999896321, SEEK_SET) = 1250999896321
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+// Ensure we are compiling to 64 bits
+struct bug { int t[sizeof(long) > 4 ? 1 : -1]; };
+int main(int argc, char **argv)
+{
+ long ofs = 0x12345678901;
+ errno = 0;
+ close(0);
+ if (open("/etc/passwd", O_RDONLY))
+ return 1;
+ long r = syscall(
+ (long) (__NR_lseek | 0x40000000), // make x32 call
+ (long) (0),
+ (long) (ofs),
+ (long) (SEEK_SET)
+ );
+ printf("pos:%ld(0x%lx) errno:%m\n", r, r);
+ if (!errno)
+ printf((r == ofs) ? "64-bit offset used\n" : "Kernel truncated offset\n");
+ return 0;
+}
diff --git a/test/x32_mmap.c b/test/x32_mmap.c
new file mode 100644
index 0000000..cbef36d
--- /dev/null
+++ b/test/x32_mmap.c
@@ -0,0 +1,49 @@
+// Test program which explores whether mmap's ofs parameter
+// is 64-bit, and whether it needs to be shifted << PAGE_SHIFT.
+// Apparently it is 64-bit and isn't shifted.
+//
+// Build: x86_64-gcc -static -Wall -ox32_mmap x32_mmap.c
+// Typical output:
+// 7f9390696000-7f93906a6000 r--s 12345670000 08:06 2224545 /etc/passwd
+// ^^^^^^^^^^^
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+// Ensure we are compiling to 64 bits
+struct bug { int t[sizeof(long) > 4 ? 1 : -1]; };
+int main(int argc, char **argv)
+{
+ long ofs = 0x12345670000; // fails if not page-aligned
+ errno = 0;
+ close(0);
+ if (open("/etc/passwd", O_RDONLY))
+ return 1;
+ long r = syscall(
+ (long) (__NR_mmap | 0x40000000), // make x32 call
+ (long) (0), // start
+ (long) (0x10000), // len
+ (long) (PROT_READ), // prot
+ (long) (MAP_SHARED), // flags
+ (long) (0), // fd
+ (long) (ofs) // ofs
+ );
+ printf("ret:0x%lx errno:%m\n", r);
+
+ char buf[16*1024];
+ sprintf(buf, "/proc/%d/maps", getpid());
+ int fd = open(buf, O_RDONLY);
+ if (fd > 0) {
+ int sz = read(fd, buf, sizeof(buf));
+ if (sz > 0)
+ write(1, buf, sz);
+ }
+
+ return 0;
+}