Home | History | Annotate | Download | only in src
      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