• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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