]> granicus.if.org Git - strace/blob - pathtrace.c
Update test/* directory, it seem to be a bit bit-rotted
[strace] / pathtrace.c
1 /*
2  * Copyright (c) 2011, Comtrol Corp.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28
29 #include "defs.h"
30
31 #include <ctype.h>
32 #include <limits.h>
33
34 #ifdef HAVE_POLL_H
35 #include <poll.h>
36 #endif
37 #ifdef HAVE_SYS_POLL_H
38 #include <sys/poll.h>
39 #endif
40
41 #include "syscall.h"
42
43 #define NumElem(a)  (int)(sizeof(a) / sizeof((a)[0]))
44
45 #define MAXSELECTED  256        /* max number of "selected" paths */
46 static const char *selected[MAXSELECTED];       /* paths selected for tracing */
47
48 /*
49  * Return true if specified path matches one that we're tracing.
50  */
51 static int
52 pathmatch(const char *path)
53 {
54         int     i;
55
56         for (i = 0; i < NumElem(selected); ++i)
57         {
58                 if (selected[i] == NULL)
59                         return 0;
60                 if (!strcmp(path, selected[i]))
61                         return 1;
62         }
63         return 0;
64 }
65
66 /*
67  * Return true if specified path (in user-space) matches.
68  */
69 static int
70 upathmatch(struct tcb *tcp, unsigned long upath)
71 {
72         char    path[PATH_MAX + 1];
73
74         return umovestr(tcp, upath, sizeof path, path) == 0 &&
75                 pathmatch(path);
76 }
77
78 /*
79  * Return true if specified fd maps to a path we're tracing.
80  */
81 static int
82 fdmatch(struct tcb *tcp, int fd)
83 {
84         const char *path = getfdpath(tcp, fd);
85
86         return path && pathmatch(path);
87 }
88
89 /*
90  * Add a path to the set we're tracing.
91  * Secifying NULL will delete all paths.
92  */
93 static int
94 storepath(const char *path)
95 {
96         int     i;
97
98         if (path == NULL)
99         {
100                 for (i = 0; i < NumElem(selected); ++i)
101                         if (selected[i])
102                         {
103                                 free((char *) selected[i]);
104                                 selected[i] = NULL;
105                         }
106                 return 0;
107         }
108
109         for (i = 0; i < NumElem(selected); ++i)
110                 if (!selected[i])
111                 {
112                         selected[i] = path;
113                         return 0;
114                 }
115
116         fprintf(stderr, "Max trace paths exceeded, only using first %d\n",
117                 NumElem(selected));
118         return -1;
119 }
120
121 /*
122  * Get path associated with fd.
123  */
124 const char *getfdpath(struct tcb *tcp, int fd)
125 {
126 #ifdef LINUX
127         static char path[PATH_MAX+1];
128         char linkpath[64];
129         ssize_t n;
130
131         if (fd < 0)
132                 return NULL;
133
134         snprintf(linkpath, sizeof linkpath, "/proc/%d/fd/%d", tcp->pid, fd);
135         n = readlink(linkpath, path, (sizeof path) - 1);
136         if (n <= 0)
137                 return NULL;
138         path[n] = '\0';
139         return path;
140 #else
141         return NULL;
142 #endif
143 }
144
145 /*
146  * Add a path to the set we're tracing.  Also add the canonicalized
147  * version of the path.  Secifying NULL will delete all paths.
148  */
149 int
150 pathtrace_select(const char *path)
151 {
152         char   *rpath;
153
154         if (path == NULL)
155                 return storepath(path);
156
157         if (storepath(path))
158                 return -1;
159
160         rpath = realpath(path, NULL);
161
162         if (rpath == NULL)
163                 return 0;
164
165         /* if realpath and specified path are same, we're done */
166         if (!strcmp(path, rpath))
167         {
168                 free(rpath);
169                 return 0;
170         }
171
172         fprintf(stderr, "Requested path '%s' resolved into '%s'\n",
173                 path, rpath);
174         return storepath(rpath);
175 }
176
177 /*
178  * Return true if syscall accesses a selected path
179  * (or if no paths have been specified for tracing).
180  */
181 int
182 pathtrace_match(struct tcb *tcp)
183 {
184         const struct sysent *s;
185
186         if (selected[0] == NULL)
187                 return 1;
188
189         s = &sysent[tcp->scno];
190
191         if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC)))
192                 return 0;
193
194         /*
195          * Check for special cases where we need to do something
196          * other than test arg[0].
197          */
198
199 #ifdef LINUX
200
201         if (s->sys_func == sys_dup2 ||
202             s->sys_func == sys_dup3 ||
203             s->sys_func == sys_sendfile ||
204             s->sys_func == sys_sendfile64 ||
205             !strcmp(s->sys_name, "tee"))
206         {
207                 /* fd, fd */
208                 return fdmatch(tcp, tcp->u_arg[0]) ||
209                         fdmatch(tcp, tcp->u_arg[1]);
210         }
211
212         if (s->sys_func == sys_inotify_add_watch ||
213             s->sys_func == sys_faccessat ||
214             s->sys_func == sys_fchmodat ||
215             s->sys_func == sys_futimesat ||
216             s->sys_func == sys_mkdirat ||
217             s->sys_func == sys_unlinkat ||
218             s->sys_func == sys_newfstatat ||
219             s->sys_func == sys_mknodat ||
220             s->sys_func == sys_openat ||
221             s->sys_func == sys_readlinkat ||
222             s->sys_func == sys_utimensat ||
223             s->sys_func == sys_fchownat ||
224             s->sys_func == sys_pipe2)
225         {
226                 /* fd, path */
227                 return fdmatch(tcp, tcp->u_arg[0]) ||
228                         upathmatch(tcp, tcp->u_arg[1]);
229         }
230
231         if (s->sys_func == sys_link ||
232             s->sys_func == sys_pivotroot ||
233             s->sys_func == sys_rename ||
234             s->sys_func == sys_symlink ||
235             s->sys_func == sys_mount)
236         {
237                 /* path, path */
238                 return upathmatch(tcp, tcp->u_arg[0]) ||
239                         upathmatch(tcp, tcp->u_arg[1]);
240         }
241
242         if (s->sys_func == sys_renameat ||
243             s->sys_func == sys_linkat)
244         {
245                 /* fd, path, fd, path */
246                 return fdmatch(tcp, tcp->u_arg[0]) ||
247                         fdmatch(tcp, tcp->u_arg[2]) ||
248                         upathmatch(tcp, tcp->u_arg[1]) ||
249                         upathmatch(tcp, tcp->u_arg[3]);
250         }
251
252         if (s->sys_func == sys_old_mmap || s->sys_func == sys_mmap)
253         {
254                 /* x, x, x, x, fd */
255                 return fdmatch(tcp, tcp->u_arg[4]);
256         }
257
258         if (s->sys_func == sys_symlinkat)
259         {
260                 /* path, fd, path */
261                 return fdmatch(tcp, tcp->u_arg[1]) ||
262                         upathmatch(tcp, tcp->u_arg[0]) ||
263                         upathmatch(tcp, tcp->u_arg[2]);
264         }
265
266         if (!strcmp(s->sys_name, "splice"))
267         {
268                 /* fd, x, fd, x, x */
269                 return fdmatch(tcp, tcp->u_arg[0]) ||
270                         fdmatch(tcp, tcp->u_arg[2]);
271         }
272
273         if (s->sys_func == sys_epoll_ctl)
274         {
275                 /* x, x, fd, x */
276                 return fdmatch(tcp, tcp->u_arg[2]);
277         }
278
279         if (s->sys_func == sys_select ||
280             s->sys_func == sys_oldselect ||
281             s->sys_func == sys_pselect6)
282         {
283                 int     i, j, nfds;
284                 long   *args, oldargs[5];
285                 unsigned fdsize;
286                 fd_set *fds;
287
288                 if (s->sys_func == sys_oldselect)
289                 {
290                         if (umoven(tcp, tcp->u_arg[0], sizeof oldargs,
291                                    (char*) oldargs) < 0)
292                         {
293                                 fprintf(stderr, "umoven() failed\n");
294                                 return 0;
295                         }
296                         args = oldargs;
297                 } else
298                         args = tcp->u_arg;
299
300                 nfds = args[0];
301                 fdsize = ((((nfds + 7) / 8) + sizeof(long) - 1)
302                           & -sizeof(long));
303                 fds = malloc(fdsize);
304
305                 if (fds == NULL)
306                 {
307                         fprintf(stderr, "out of memory\n");
308                         return 0;
309                 }
310
311                 for (i = 1; i <= 3; ++i)
312                 {
313                         if (args[i] == 0)
314                                 continue;
315
316                         if (umoven(tcp, args[i], fdsize, (char *) fds) < 0)
317                         {
318                                 fprintf(stderr, "umoven() failed\n");
319                                 continue;
320                         }
321
322                         for (j = 0; j < nfds; ++j)
323                                 if (FD_ISSET(j, fds) && fdmatch(tcp, j))
324                                 {
325                                         free(fds);
326                                         return 1;
327                                 }
328                 }
329                 free(fds);
330                 return 0;
331         }
332
333         if (s->sys_func == sys_poll ||
334             s->sys_func == sys_ppoll)
335         {
336                 struct pollfd fds;
337                 unsigned nfds;
338                 unsigned long start, cur, end;
339
340                 start = tcp->u_arg[0];
341                 nfds = tcp->u_arg[1];
342
343                 end = start + sizeof(fds) * nfds;
344
345                 if (nfds == 0 || end < start)
346                         return 0;
347
348                 for (cur = start; cur < end; cur += sizeof(fds))
349                         if ((umoven(tcp, cur, sizeof fds, (char *) &fds) == 0)
350                             && fdmatch(tcp, fds.fd))
351                                 return 1;
352
353                 return 0;
354         }
355
356         if (s->sys_func == printargs ||
357             s->sys_func == sys_pipe ||
358             s->sys_func == sys_pipe2 ||
359             s->sys_func == sys_eventfd2 ||
360             s->sys_func == sys_eventfd ||
361             s->sys_func == sys_inotify_init1 ||
362             s->sys_func == sys_timerfd_create ||
363             s->sys_func == sys_timerfd_settime ||
364             s->sys_func == sys_timerfd_gettime ||
365             s->sys_func == sys_epoll_create ||
366             !strcmp(s->sys_name, "fanotify_init"))
367         {
368                 /*
369                  * These have TRACE_FILE or TRACE_DESCRIPTOR set, but they
370                  * don't have any file descriptor or path args to test.
371                  */
372                 return 0;
373         }
374 #else
375 #warning "path tracing only using arg[0]"
376 #endif
377
378         /*
379          * Our fallback position for calls that haven't already
380          * been handled is to just check arg[0].
381          */
382
383         if (s->sys_flags & TRACE_FILE)
384                 return upathmatch(tcp, tcp->u_arg[0]);
385
386         if (s->sys_flags & TRACE_DESC)
387                 return fdmatch(tcp, tcp->u_arg[0]);
388
389         return 0;
390 }