~ubuntu-branches/ubuntu/wily/apparmor/wily

« back to all changes in this revision

Viewing changes to tests/regression/apparmor/ptrace.c

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook
  • Date: 2011-04-27 10:38:07 UTC
  • mfrom: (5.1.118 natty)
  • Revision ID: james.westby@ubuntu.com-20110427103807-ym3rhwys6o84ith0
Tags: 2.6.1-2
debian/copyright: clarify for some full organization names.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <stdio.h>
 
2
#include <string.h>
 
3
#include <stdlib.h>
 
4
#include <unistd.h>
 
5
#include <sys/types.h>
 
6
#include <sys/wait.h>
 
7
#include <sys/ptrace.h>
 
8
#include <signal.h>
 
9
#include <sys/user.h>
 
10
#include <errno.h>
 
11
 
 
12
#define NUM_CHLD_SYSCALLS 10
 
13
 
 
14
#define PARENT_TRACE 0
 
15
#define CHILD_TRACE 1
 
16
#define HELPER_TRACE 2
 
17
 
 
18
extern char **environ;
 
19
 
 
20
int interp_status(int status)
 
21
{
 
22
        int rc;
 
23
 
 
24
        if (WIFEXITED(status)) {
 
25
                if (WEXITSTATUS(status) == 0) {
 
26
                        rc = 0;
 
27
                } else {
 
28
                        rc = -WEXITSTATUS(status);
 
29
                }
 
30
        } else {
 
31
                rc = -ECONNABORTED;     /* overload to mean child signal */
 
32
        }
 
33
 
 
34
        return rc;
 
35
}
 
36
 
 
37
/* return 0 on success.  Child failure -errorno, parent failure errno */
 
38
int do_parent(pid_t pid, int trace, int num_syscall)
 
39
{
 
40
        struct user regs;
 
41
        int status, i;
 
42
        unsigned int rc;
 
43
 
 
44
        /* child is paused */
 
45
        rc = alarm(5);
 
46
        if (rc != 0) {
 
47
                fprintf(stderr, "FAIL: unexpected alarm already set\n");
 
48
                return errno;
 
49
        }
 
50
 
 
51
//fprintf(stderr, "waiting ... ");
 
52
        if (waitpid(pid, &status, WUNTRACED) == -1)
 
53
                return errno;
 
54
        if (!WIFSTOPPED(status))
 
55
                return interp_status(status);
 
56
//fprintf(stderr, " done initial wait\n");
 
57
        if (trace) {
 
58
                /* this sends a child SIGSTOP */
 
59
                if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
 
60
                        perror("FAIL: parent ptrace(PTRACE_ATTACH) failed - ");
 
61
                        return errno;
 
62
                }
 
63
        } else {
 
64
                /* continue child so it can attach to parent */
 
65
                kill(pid, SIGCONT);
 
66
//fprintf(stderr, "waiting2 ... ");
 
67
        if (waitpid(pid, &status, WUNTRACED) == -1)
 
68
                return errno;
 
69
//fprintf(stderr, " done\n");
 
70
 
 
71
        if (!WIFSTOPPED(status))
 
72
                return interp_status(status);
 
73
 
 
74
        }
 
75
 
 
76
        for (i = 0; i < num_syscall * 2; i++){
 
77
                /* this will restart stopped child */
 
78
                if (ptrace(PTRACE_SYSCALL, pid, NULL, 0) == -1) {
 
79
                        perror("FAIL: parent ptrace(PTRACE_SINGLESTEP) failed - ");
 
80
                        return errno;
 
81
                }
 
82
 
 
83
//fprintf(stderr, "waiting3 ... ");
 
84
                if (waitpid(pid, &status, WUNTRACED) == -1)
 
85
                        return errno;
 
86
//fprintf(stderr, " done\n");
 
87
 
 
88
                if (!WIFSTOPPED(status))
 
89
                        return interp_status(status);
 
90
        
 
91
                memset(&regs, 0, sizeof(regs));
 
92
                if (ptrace(PTRACE_GETREGS, pid, NULL, &regs) == -1) {
 
93
                        perror("FAIL:  parent ptrace(PTRACE_GETREGS) failed - ");
 
94
                        return errno;
 
95
                }
 
96
        }
 
97
 
 
98
        if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) {
 
99
                perror("FAIL:  parent ptrace(PTRACE_DETACH) failed - ");
 
100
                return errno;
 
101
        }
 
102
        return 0;
 
103
}
 
104
 
 
105
/* returns 0 on success or error code of failure */
 
106
int do_child(char *argv[], int child_trace, int helper)
 
107
{
 
108
        if (helper) {
 
109
                /* for helper we want to transition before ptrace occurs
 
110
                 * so don't stop here, let the helper do that
 
111
                 */
 
112
                if (child_trace) {
 
113
                         putenv("_tracer=child");
 
114
                } else {
 
115
                         putenv("_tracer=parent");
 
116
                }
 
117
//fprintf(stderr, "child trace %d\n", child_trace);
 
118
        } else {
 
119
                /* stop child to ensure it doesn't finish before it is traced */
 
120
                if (raise(SIGSTOP) != 0){
 
121
                        perror("FAIL: child/helper SIGSTOP itself failed -");
 
122
                        return errno;
 
123
                }
 
124
 
 
125
                if (child_trace) {
 
126
                        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1){
 
127
                                perror("FAIL: child ptrace(PTRACE_TRACEME) failed - ");
 
128
                                return errno;
 
129
                        }
 
130
                        if (raise(SIGSTOP) != 0){
 
131
                                perror("FAIL: child SIGSTOP itself failed -");
 
132
                                return errno;
 
133
                        }
 
134
                        /* ok we're stopped, wait for parent to trace (continue) us */
 
135
                }
 
136
 
 
137
        }
 
138
 
 
139
        execve(argv[0], argv, environ);
 
140
 
 
141
        perror("FAIL: child exec failed - ");
 
142
 
 
143
        return errno;
 
144
}
 
145
 
 
146
/* make pid_t global so the alarm handler can kill off children */
 
147
pid_t pid;
 
148
 
 
149
void sigalrm_handler(int sig) {
 
150
        fprintf(stderr, "FAIL: parent timed out waiting for child\n");
 
151
        kill(pid, SIGKILL);
 
152
        exit(1);
 
153
}
 
154
 
 
155
int main(int argc, char *argv[])
 
156
{
 
157
        int parent_trace = 1,
 
158
            use_helper = 0,
 
159
            num_syscall = NUM_CHLD_SYSCALLS, 
 
160
            opt,
 
161
            ret = 0;
 
162
        const char *usage = "usage: %s [-c] [-n #syscall] program [args ...]\n";
 
163
        char **args;
 
164
 
 
165
        if (signal(SIGALRM, sigalrm_handler) == SIG_ERR) {
 
166
                perror ("FAIL - signal failed: ");
 
167
                return(1);
 
168
        }
 
169
 
 
170
        opterr = 0;
 
171
        while (1) {
 
172
                opt = getopt(argc, argv, "chn:");
 
173
 
 
174
                if (opt == -1)
 
175
                        break;
 
176
                switch (opt) {
 
177
                        case 'c': parent_trace = 0;
 
178
                                  break;
 
179
                        case 'h': use_helper = 1;
 
180
                                  break;
 
181
                        case 'n': num_syscall = atoi(optarg); 
 
182
                                  break;
 
183
                        default:
 
184
                                  fprintf(stderr, usage, argv[0]);
 
185
                                  break;
 
186
                }
 
187
        }
 
188
 
 
189
        if (argc < 2) {
 
190
                fprintf(stderr, usage, argv[0]);
 
191
                return 1;
 
192
        }
 
193
 
 
194
        args = &argv[optind];
 
195
 
 
196
        pid = fork();
 
197
        if (pid > 0){   /*parent */
 
198
                int stat;
 
199
 
 
200
                ret = do_parent(pid, parent_trace, num_syscall);
 
201
 
 
202
                kill(pid, SIGKILL);
 
203
 
 
204
                if (ret >= 0) {
 
205
                        /* wait for child */
 
206
                        waitpid(pid, &stat, 0);
 
207
                }
 
208
 
 
209
                if (ret > 0) {
 
210
                        perror("FAIL: parent failed: ");
 
211
                } else if (ret == 0) {
 
212
                        printf("PASS\n");
 
213
                        return 0;
 
214
                } else if (ret == -ECONNABORTED) {
 
215
                        errno = -ret;
 
216
                        perror("FAIL: child killed: ");
 
217
                } else {
 
218
                        errno = -ret;
 
219
                        perror("FAIL: child failed: ");
 
220
                }
 
221
        } else if (pid == 0) {  /* child */
 
222
                if (do_child(args, !parent_trace, use_helper))
 
223
                        return 0;
 
224
                        
 
225
        } else {
 
226
                perror("FAIL: fork failed - ");
 
227
        }
 
228
 
 
229
        return ret;
 
230
}