Home | History | Annotate | Download | only in test
      1 /*
      2  * Create NUM_THREADS threads which print "1" and sleep in pause().
      3  * Then create another thread which prints "2", and re-execs the program.
      4  * The leader then either sleeps in pause(), or exits if $LEADER_EXIT is set.
      5  * This triggers "execve'ed thread replaces thread leader" case.
      6  *
      7  * gcc -Wall -Os -o threaded_execve threaded_execve.c
      8  *
      9  * Try running it under strace like this:
     10  *
     11  * # Should not be confused by traced execve-ing thread
     12  * # replacing traced leader:
     13  * strace -oLOG -f ./threaded_execve
     14  *
     15  * # Same, but different output mode. Output after execve
     16  * # should go into leader's LOG.<pid> file, not into execve'ed
     17  * # thread's log file:
     18  * strace -oLOG -ff ./threaded_execve
     19  *
     20  * # Should not be confused by non-traced execve-ing thread
     21  * # replacing traced leader:
     22  * strace -oLOG ./threaded_execve
     23  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     24  * In Linux 3.2, non-traced execve-ing thread does not
     25  * become traced after execve, even though it has pid == leader's pid
     26  * after execve. And yet, strace's waitpid doesn't return ECHILD.
     27  *
     28  * # Run for NUM seconds, not just one second.
     29  * # Watch top to check for memory leaks in strace:
     30  * strace -oLOG -f ./threaded_execve <NUM>
     31  *
     32  */
     33 #define NUM_THREADS 1
     34 
     35 #define _GNU_SOURCE 1
     36 #include <assert.h>
     37 #include <limits.h>
     38 #include <stddef.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <unistd.h>
     42 #include <errno.h>
     43 #include <stdio.h>
     44 #include <sched.h>
     45 #include <signal.h>
     46 #include <dirent.h>
     47 #include <fcntl.h>
     48 #include <sys/types.h>
     49 #include <sys/wait.h>
     50 #include <sys/syscall.h>
     51 
     52 /* Define clone2 for all arches */
     53 #ifdef __ia64__
     54 extern int __clone2(int (*fn) (void *), void *child_stack_base,
     55 		size_t stack_size, int flags, void *arg, ...);
     56 #define clone2 __clone2
     57 #elif defined(__metag__)
     58 #define clone2(func, stack_base, size, flags, arg...) \
     59         clone(func, stack_base, flags, arg)
     60 #else
     61 #define clone2(func, stack_base, size, flags, arg...) \
     62         clone(func, (stack_base) + (size), flags, arg)
     63 #endif
     64 /* Direct calls to syscalls, avoiding libc wrappers */
     65 #define syscall_tgkill(pid, tid, sig) syscall(__NR_tgkill, (pid), (tid), (sig))
     66 #define syscall_getpid() syscall(__NR_getpid)
     67 #define syscall_gettid() syscall(__NR_gettid)
     68 #define syscall_exit(v) syscall(__NR_exit, (v));
     69 
     70 static char my_name[PATH_MAX];
     71 static int leader_final_action;
     72 
     73 static int
     74 thread1(void *unused)
     75 {
     76 	write(1, "1", 1);
     77 	for(;;) pause();
     78 	return 0;
     79 }
     80 
     81 static int
     82 thread2(void *unused)
     83 {
     84 	char buf[64];
     85 	sprintf(buf, "%d", leader_final_action);
     86 	write(1, "2", 1);
     87 	usleep(20*1000);
     88 	/* This fails with ENOENT if leader has exited by now! :) */
     89 	execl("/proc/self/exe", "exe", "exe", buf, NULL);
     90 	/* So fall back to resolved name */
     91 	execl(my_name, "exe", "exe", buf, NULL);
     92 	for(;;) pause();
     93 	return 0;
     94 }
     95 
     96 static void
     97 thread_leader(void)
     98 {
     99 	/* malloc gives sufficiently aligned buffer.
    100 	 * long buf[] does not! (on ia64).
    101 	 */
    102 	int cnt = NUM_THREADS;
    103 	while (--cnt >= 0) {
    104 		/* As seen in pthread_create(): */
    105 		clone2(thread1, malloc(16 * 1024), 16 * 1024, 0
    106 			| CLONE_VM
    107 			| CLONE_FS
    108 			| CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
    109 			| 0        /* no signal to send on death */
    110 			, NULL);
    111 		usleep(20*1000);
    112 	}
    113 	clone2(thread2, malloc(16 * 1024), 16 * 1024, 0
    114 		| CLONE_VM
    115 		| CLONE_FS
    116 		| CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
    117 		| 0        /* no signal to send on death */
    118 		, NULL);
    119 
    120 	/* Various states leader can be while other thread execve's: */
    121 	switch (leader_final_action % 3) {
    122 		case 0: syscall_exit(42); /* leader is dead */
    123 		case 1: for(;;) pause(); /* leader is in syscall */
    124 		default: for(;;) continue; /* leader is in userspace */
    125 	}
    126 }
    127 
    128 int
    129 main(int argc, char **argv)
    130 {
    131 	if (readlink("/proc/self/exe", my_name, sizeof(my_name)-1) <= 0)
    132 		return 1;
    133 
    134 	setbuf(stdout, NULL);
    135 
    136 	if (argv[1] && strcmp(argv[1], "exe") == 0) {
    137 		leader_final_action = atoi(argv[2]) + 1;
    138 		thread_leader();
    139 	}
    140 
    141 	printf("%d: thread leader\n", getpid());
    142 
    143 	alarm(argv[1] ? atoi(argv[1]) : 1);
    144 	thread_leader();
    145 
    146         return 0;
    147 }
    148