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