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