• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
thread1(void * unused)74 thread1(void *unused)
75 {
76 	write(1, "1", 1);
77 	for(;;) pause();
78 	return 0;
79 }
80 
81 static int
thread2(void * unused)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
thread_leader(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
main(int argc,char ** argv)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