]> granicus.if.org Git - strace/blob - pathtrace.c
pathtrace.c: introduce user-provided sets of paths
[strace] / pathtrace.c
1 /*
2  * Copyright (c) 2011 Comtrol Corp.
3  * Copyright (c) 2011-2017 The strace developers.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29
30 #include "defs.h"
31 #include <sys/param.h>
32 #include <poll.h>
33
34 #include "syscall.h"
35
36 struct path_set global_path_set;
37
38 /*
39  * Return true if specified path matches one that we're tracing.
40  */
41 static bool
42 pathmatch(const char *path, struct path_set *set)
43 {
44         unsigned i;
45
46         for (i = 0; i < set->num_selected; ++i) {
47                 if (strcmp(path, set->paths_selected[i]) == 0)
48                         return true;
49         }
50         return false;
51 }
52
53 /*
54  * Return true if specified path (in user-space) matches.
55  */
56 static bool
57 upathmatch(struct tcb *const tcp, const kernel_ulong_t upath,
58            struct path_set *set)
59 {
60         char path[PATH_MAX + 1];
61
62         return umovestr(tcp, upath, sizeof(path), path) > 0 &&
63                 pathmatch(path, set);
64 }
65
66 /*
67  * Return true if specified fd maps to a path we're tracing.
68  */
69 static bool
70 fdmatch(struct tcb *tcp, int fd, struct path_set *set)
71 {
72         char path[PATH_MAX + 1];
73         int n = getfdpath(tcp, fd, path, sizeof(path));
74
75         return n >= 0 && pathmatch(path, set);
76 }
77
78 /*
79  * Add a path to the set we're tracing.
80  * Specifying NULL will delete all paths.
81  */
82 static void
83 storepath(const char *path, struct path_set *set)
84 {
85         unsigned i;
86
87         if (pathmatch(path, set))
88                 return; /* already in table */
89
90         i = set->num_selected++;
91         set->paths_selected = xreallocarray(set->paths_selected,
92                                             set->num_selected,
93                                             sizeof(set->paths_selected[0]));
94         set->paths_selected[i] = path;
95 }
96
97 /*
98  * Get path associated with fd.
99  */
100 int
101 getfdpath(struct tcb *tcp, int fd, char *buf, unsigned bufsize)
102 {
103         char linkpath[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3];
104         ssize_t n;
105
106         if (fd < 0)
107                 return -1;
108
109         sprintf(linkpath, "/proc/%u/fd/%u", tcp->pid, fd);
110         n = readlink(linkpath, buf, bufsize - 1);
111         /*
112          * NB: if buf is too small, readlink doesn't fail,
113          * it returns truncated result (IOW: n == bufsize - 1).
114          */
115         if (n >= 0)
116                 buf[n] = '\0';
117         return n;
118 }
119
120 /*
121  * Add a path to the set we're tracing.  Also add the canonicalized
122  * version of the path.  Secifying NULL will delete all paths.
123  */
124 void
125 pathtrace_select_set(const char *path, struct path_set *set)
126 {
127         char *rpath;
128
129         storepath(path, set);
130
131         rpath = realpath(path, NULL);
132
133         if (rpath == NULL)
134                 return;
135
136         /* if realpath and specified path are same, we're done */
137         if (strcmp(path, rpath) == 0) {
138                 free(rpath);
139                 return;
140         }
141
142         error_msg("Requested path '%s' resolved into '%s'", path, rpath);
143         storepath(rpath, set);
144 }
145
146 /*
147  * Return true if syscall accesses a selected path
148  * (or if no paths have been specified for tracing).
149  */
150 bool
151 pathtrace_match_set(struct tcb *tcp, struct path_set *set)
152 {
153         const struct_sysent *s;
154
155         s = tcp->s_ent;
156
157         if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
158                 return false;
159
160         /*
161          * Check for special cases where we need to do something
162          * other than test arg[0].
163          */
164
165         switch (s->sen) {
166         case SEN_dup2:
167         case SEN_dup3:
168         case SEN_kexec_file_load:
169         case SEN_sendfile:
170         case SEN_sendfile64:
171         case SEN_tee:
172                 /* fd, fd */
173                 return fdmatch(tcp, tcp->u_arg[0], set) ||
174                         fdmatch(tcp, tcp->u_arg[1], set);
175
176         case SEN_faccessat:
177         case SEN_fchmodat:
178         case SEN_fchownat:
179         case SEN_fstatat64:
180         case SEN_futimesat:
181         case SEN_inotify_add_watch:
182         case SEN_mkdirat:
183         case SEN_mknodat:
184         case SEN_name_to_handle_at:
185         case SEN_newfstatat:
186         case SEN_openat:
187         case SEN_readlinkat:
188         case SEN_statx:
189         case SEN_unlinkat:
190         case SEN_utimensat:
191                 /* fd, path */
192                 return fdmatch(tcp, tcp->u_arg[0], set) ||
193                         upathmatch(tcp, tcp->u_arg[1], set);
194
195         case SEN_link:
196         case SEN_mount:
197         case SEN_pivotroot:
198                 /* path, path */
199                 return upathmatch(tcp, tcp->u_arg[0], set) ||
200                         upathmatch(tcp, tcp->u_arg[1], set);
201
202         case SEN_quotactl:
203                 /* x, path */
204                 return upathmatch(tcp, tcp->u_arg[1], set);
205
206         case SEN_linkat:
207         case SEN_renameat2:
208         case SEN_renameat:
209                 /* fd, path, fd, path */
210                 return fdmatch(tcp, tcp->u_arg[0], set) ||
211                         fdmatch(tcp, tcp->u_arg[2], set) ||
212                         upathmatch(tcp, tcp->u_arg[1], set) ||
213                         upathmatch(tcp, tcp->u_arg[3], set);
214
215         case SEN_old_mmap:
216 #if defined(S390)
217         case SEN_old_mmap_pgoff:
218 #endif
219         case SEN_mmap:
220         case SEN_mmap_4koff:
221         case SEN_mmap_pgoff:
222         case SEN_ARCH_mmap:
223                 /* x, x, x, x, fd */
224                 return fdmatch(tcp, tcp->u_arg[4], set);
225
226         case SEN_symlinkat:
227                 /* path, fd, path */
228                 return fdmatch(tcp, tcp->u_arg[1], set) ||
229                         upathmatch(tcp, tcp->u_arg[0], set) ||
230                         upathmatch(tcp, tcp->u_arg[2], set);
231
232         case SEN_copy_file_range:
233         case SEN_splice:
234                 /* fd, x, fd, x, x, x */
235                 return fdmatch(tcp, tcp->u_arg[0], set) ||
236                         fdmatch(tcp, tcp->u_arg[2], set);
237
238         case SEN_epoll_ctl:
239                 /* x, x, fd, x */
240                 return fdmatch(tcp, tcp->u_arg[2], set);
241
242
243         case SEN_fanotify_mark:
244                 /* x, x, x, fd, path */
245                 return fdmatch(tcp, tcp->u_arg[3], set) ||
246                         upathmatch(tcp, tcp->u_arg[4], set);
247
248         case SEN_oldselect:
249         case SEN_pselect6:
250         case SEN_select:
251         {
252                 int     i, j;
253                 int     nfds;
254                 kernel_ulong_t *args;
255                 kernel_ulong_t select_args[5];
256                 unsigned int oldselect_args[5];
257                 unsigned int fdsize;
258                 fd_set *fds;
259
260                 if (SEN_oldselect == s->sen) {
261                         if (sizeof(*select_args) == sizeof(*oldselect_args)) {
262                                 if (umove(tcp, tcp->u_arg[0], &select_args)) {
263                                         return false;
264                                 }
265                         } else {
266                                 unsigned int n;
267
268                                 if (umove(tcp, tcp->u_arg[0], &oldselect_args)) {
269                                         return false;
270                                 }
271
272                                 for (n = 0; n < 5; ++n) {
273                                         select_args[n] = oldselect_args[n];
274                                 }
275                         }
276                         args = select_args;
277                 } else {
278                         args = tcp->u_arg;
279                 }
280
281                 /* Kernel truncates arg[0] to int, we do the same. */
282                 nfds = (int) args[0];
283                 /* Kernel rejects negative nfds, so we don't parse it either. */
284                 if (nfds <= 0)
285                         return false;
286                 /* Beware of select(2^31-1, NULL, NULL, NULL) and similar... */
287                 if (nfds > 1024*1024)
288                         nfds = 1024*1024;
289                 fdsize = (((nfds + 7) / 8) + current_wordsize-1) & -current_wordsize;
290                 fds = xmalloc(fdsize);
291
292                 for (i = 1; i <= 3; ++i) {
293                         if (args[i] == 0)
294                                 continue;
295                         if (umoven(tcp, args[i], fdsize, fds) < 0) {
296                                 continue;
297                         }
298                         for (j = 0;; j++) {
299                                 j = next_set_bit(fds, j, nfds);
300                                 if (j < 0)
301                                         break;
302                                 if (fdmatch(tcp, j, set)) {
303                                         free(fds);
304                                         return true;
305                                 }
306                         }
307                 }
308                 free(fds);
309                 return false;
310         }
311
312         case SEN_poll:
313         case SEN_ppoll:
314         {
315                 struct pollfd fds;
316                 unsigned nfds;
317                 kernel_ulong_t start, cur, end;
318
319                 start = tcp->u_arg[0];
320                 nfds = tcp->u_arg[1];
321
322                 end = start + sizeof(fds) * nfds;
323
324                 if (nfds == 0 || end < start)
325                         return false;
326
327                 for (cur = start; cur < end; cur += sizeof(fds))
328                         if ((umove(tcp, cur, &fds) == 0)
329                             && fdmatch(tcp, fds.fd, set))
330                                 return true;
331
332                 return false;
333         }
334
335         case SEN_bpf:
336         case SEN_epoll_create:
337         case SEN_epoll_create1:
338         case SEN_eventfd2:
339         case SEN_eventfd:
340         case SEN_fanotify_init:
341         case SEN_inotify_init1:
342         case SEN_memfd_create:
343         case SEN_perf_event_open:
344         case SEN_pipe:
345         case SEN_pipe2:
346         case SEN_printargs:
347         case SEN_socket:
348         case SEN_socketpair:
349         case SEN_timerfd_create:
350         case SEN_timerfd_gettime:
351         case SEN_timerfd_settime:
352         case SEN_userfaultfd:
353                 /*
354                  * These have TRACE_FILE or TRACE_DESCRIPTOR or TRACE_NETWORK set,
355                  * but they don't have any file descriptor or path args to test.
356                  */
357                 return false;
358         }
359
360         /*
361          * Our fallback position for calls that haven't already
362          * been handled is to just check arg[0].
363          */
364
365         if (s->sys_flags & TRACE_FILE)
366                 return upathmatch(tcp, tcp->u_arg[0], set);
367
368         if (s->sys_flags & (TRACE_DESC | TRACE_NETWORK))
369                 return fdmatch(tcp, tcp->u_arg[0], set);
370
371         return false;
372 }