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 (at) 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 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 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. */ 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 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. */ 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