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;
+}