1 /*
2 * Copyright (c) 2012 The Chromium OS Authors.
3 *
4 * Based on:
5 * http://bazaar.launchpad.net/~ubuntu-bugcontrol/qa-regression-testing/master/view/head:/scripts/kernel-security/ptrace/thread-prctl.c
6 * Copyright 2011 Canonical, Ltd
7 * License: GPLv3
8 * Author: Kees Cook <kees.cook@canonical.com>
9 *
10 * Based on reproducer written by Philippe Waroquiers in:
11 * https://launchpad.net/bugs/729839
12 */
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <signal.h>
19 #include <string.h>
20 #include <pthread.h>
21 #include <sys/ptrace.h>
22 #include <sys/prctl.h>
23 #ifndef PR_SET_PTRACER
24 # define PR_SET_PTRACER 0x59616d61
25 #endif
26
27 int tracee_method = 0;
28 #define TRACEE_FORKS_FROM_TRACER 0
29 #define TRACEE_CALLS_PRCTL_FROM_MAIN 1
30 #define TRACEE_CALLS_PRCTL_FROM_THREAD 2
31
32 /* Define some distinct exit values to aid failure debugging. */
33 #define EXIT_FORK_TRACEE 1
34 #define EXIT_FORK_TRACER 2
35 #define EXIT_PIPE_COMMUNICATION 3
36 #define EXIT_PIPE_NOTIFICATION 4
37 #define EXIT_TRACEE_PIPE_READ 5
38 #define EXIT_TRACEE_UNREACHABLE 6
39 #define EXIT_TRACER_PIPE_READ 7
40 #define EXIT_TRACER_PTRACE_ATTACH 8
41 #define EXIT_TRACER_PTRACE_CONTINUE 9
42 #define EXIT_TRACER_UNREACHABLE 10
43
44 int main_does_ptrace = 0;
45
46 int ret;
47 int pipes[2];
48 int notification[2];
49 pid_t tracer, tracee;
50
thr_fn(void * v)51 static void *thr_fn(void *v)
52 {
53 printf("tracee thread started\n");
54 if (tracee_method == TRACEE_CALLS_PRCTL_FROM_THREAD) {
55 ret = prctl (PR_SET_PTRACER, tracer, 0, 0, 0);
56 printf("tracee thread prtctl result: %d\n", ret);
57 }
58 printf("tracee thread finishing\n");
59 return NULL;
60 }
61
62 void start_tracee(void);
63
tracer_main(void * data)64 void * tracer_main(void * data)
65 {
66 long ptrace_result;
67 char buf[8];
68 int saw;
69
70 tracer = getpid();
71 printf("tracer %d waiting\n", tracer);
72
73 if (tracee_method == TRACEE_FORKS_FROM_TRACER) {
74 printf("forking tracee from tracer\n");
75 start_tracee();
76 }
77
78 close(pipes[1]);
79 close(notification[0]);
80 close(notification[1]);
81
82 saw = read(pipes[0], buf, 3);
83 if (saw < 3) {
84 perror("tracer pipe read");
85 exit(EXIT_TRACER_PIPE_READ);
86 }
87
88 printf("tracer to PTRACE_ATTACH my tracee %d\n", tracee);
89 ptrace_result = ptrace(PTRACE_ATTACH, tracee, NULL, NULL);
90 if (ptrace_result != 0) {
91 fflush(NULL);
92 perror ("tracer ptrace attach has failed");
93 exit(EXIT_TRACER_PTRACE_ATTACH);
94 }
95 printf ("tracer ptrace attach successful\n");
96
97 /* Wait for signal. */
98 printf("tracer waiting for tracee to SIGSTOP\n");
99 waitpid(tracee, NULL, 0);
100
101 printf("tracer to PTRACE_CONT tracee\n");
102 ptrace_result = ptrace(PTRACE_CONT, tracee, NULL, NULL);
103 if (ptrace_result != 0) {
104 fflush(NULL);
105 perror ("tracer ptrace continue has failed");
106 exit(EXIT_TRACER_PTRACE_CONTINUE);
107 }
108 printf ("tracer ptrace continue successful\n");
109
110 printf("tracer returning 0\n");
111 fflush(NULL);
112 exit(EXIT_SUCCESS);
113
114 return NULL;
115 }
116
117 /* Tracee knows nothing, needs tracee and tracer pid. */
tracee_main(void)118 void tracee_main(void) {
119 char buf[1024];
120 int saw;
121 pthread_t thr;
122
123 tracee = getpid();
124 close(pipes[0]);
125
126 printf("tracee %d reading tracer pid\n", tracee);
127 close(notification[1]);
128 saw = read(notification[0], buf, 1024);
129 if (saw < 1) {
130 perror("pipe read");
131 exit(EXIT_TRACEE_PIPE_READ);
132 }
133 buf[saw]='\0';
134 tracer = atoi(buf);
135
136 printf("tracee %d started (expecting %d as tracer)\n", tracee, tracer);
137
138 /* Handle setting PR_SET_PTRACER. */
139 switch (tracee_method) {
140 case TRACEE_CALLS_PRCTL_FROM_MAIN:
141 ret = prctl (PR_SET_PTRACER, tracer, 0, 0, 0);
142 printf("tracee main prtctl result: %d \n", ret);
143 break;
144 case TRACEE_CALLS_PRCTL_FROM_THREAD:
145 printf("tracee thread starting\n");
146 pthread_create(&thr, NULL, thr_fn, NULL);
147 pthread_join(thr, NULL);
148 printf("tracee thread finished\n");
149 break;
150 default:
151 break;
152 }
153
154 /* Wait for Oedipal action. */
155 printf("tracee triggering tracer\n");
156 fflush(NULL);
157 write(pipes[1], "ok\n", 3);
158
159 printf("tracee waiting for master\n");
160 saw = read(notification[0], buf, 1024);
161 buf[saw] = '\0';
162
163 printf("tracee finished (%s)\n", buf);
164 exit(EXIT_SUCCESS);
165 }
166
start_tracee(void)167 void start_tracee(void)
168 {
169 fflush(NULL);
170 tracee = fork();
171 if (tracee < 0) {
172 perror("fork tracee");
173 exit(EXIT_FORK_TRACEE);
174 }
175 if (tracee == 0) {
176 tracee_main();
177 exit(EXIT_TRACEE_UNREACHABLE);
178 }
179 }
180
181 /* Tracer knows tracee, needs tracer pid. */
main(int argc,char * argv[])182 int main(int argc, char*argv[])
183 {
184 int status;
185 char buf[1024];
186
187 if (argc > 1) {
188 /* Operational states:
189 * 0: tracer forks tracee.
190 * 1: tracee calls prctl from main process.
191 * 2: tracee calls prctl from non-leader thread.
192 */
193 tracee_method = atoi(argv[1]);
194 }
195 if (argc > 2) {
196 /* Operational states:
197 * 0: ptrace happens from non-leader thread.
198 * 1: ptrace happens from main process.
199 */
200 main_does_ptrace = atoi(argv[2]) != 0;
201 }
202
203 if (tracee_method != TRACEE_FORKS_FROM_TRACER) {
204 printf("will issue prctl from %s\n",
205 tracee_method == TRACEE_CALLS_PRCTL_FROM_MAIN ?
206 "main" : "thread");
207 }
208 else {
209 printf("will fork tracee from tracer\n");
210 }
211 printf("will issue ptrace from tracer %s\n",
212 main_does_ptrace ? "main" : "thread");
213
214 printf("master is %d\n", getpid());
215
216 if (pipe(notification)<0) {
217 perror("pipe");
218 exit(EXIT_PIPE_NOTIFICATION);
219 }
220 if (pipe(pipes)<0) {
221 perror("pipe");
222 exit(EXIT_PIPE_COMMUNICATION);
223 }
224
225 if (tracee_method != TRACEE_FORKS_FROM_TRACER) {
226 printf("forking tracee from master\n");
227 start_tracee();
228 }
229
230 fflush(NULL);
231 tracer = fork();
232 if (tracer < 0) {
233 perror("fork tracer");
234 exit(EXIT_FORK_TRACER);
235 }
236 if (tracer == 0) {
237 printf("tracer is %d\n", getpid());
238 if (main_does_ptrace) {
239 tracer_main(NULL);
240 }
241 else {
242 pthread_t thread;
243 pthread_create(&thread, NULL, tracer_main, NULL);
244 pthread_join(thread, NULL);
245 }
246 exit(EXIT_TRACER_UNREACHABLE);
247 }
248
249 /* Leave the pipes for the tracee and tracer. */
250 close(pipes[0]);
251 close(pipes[1]);
252
253 /* Close our end of pid notification. */
254 close(notification[0]);
255 sprintf(buf, "%d", tracer);
256 write(notification[1], buf, strlen(buf));
257
258 printf("master waiting for tracer to finish\n");
259 fflush(NULL);
260 waitpid(tracer, &status, 0);
261
262 printf("master waiting for tracee to finish\n");
263 fflush(NULL);
264 write(notification[1], "stop", 4);
265 kill(tracee, SIGCONT); // Just in case.
266 waitpid(tracee, NULL, 0);
267
268 status = WEXITSTATUS(status);
269 printf("master saw rc %d from tracer\n", status);
270 return status;
271 }
272
273