]> granicus.if.org Git - strace/blob - pathtrace.c
pathtrace: fix missing syscalls
[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         case SEN_symlink:
204                 /* x, path */
205                 return upathmatch(tcp, tcp->u_arg[1], set);
206
207         case SEN_linkat:
208         case SEN_renameat2:
209         case SEN_renameat:
210                 /* fd, path, fd, path */
211                 return fdmatch(tcp, tcp->u_arg[0], set) ||
212                         fdmatch(tcp, tcp->u_arg[2], set) ||
213                         upathmatch(tcp, tcp->u_arg[1], set) ||
214                         upathmatch(tcp, tcp->u_arg[3], set);
215
216         case SEN_old_mmap:
217 #if defined(S390)
218         case SEN_old_mmap_pgoff:
219 #endif
220         case SEN_mmap:
221         case SEN_mmap_4koff:
222         case SEN_mmap_pgoff:
223         case SEN_ARCH_mmap:
224                 /* x, x, x, x, fd */
225                 return fdmatch(tcp, tcp->u_arg[4], set);
226
227         case SEN_symlinkat:
228                 /* path, fd, path */
229                 return fdmatch(tcp, tcp->u_arg[1], set) ||
230                         upathmatch(tcp, tcp->u_arg[0], set) ||
231                         upathmatch(tcp, tcp->u_arg[2], set);
232
233         case SEN_copy_file_range:
234         case SEN_splice:
235                 /* fd, x, fd, x, x, x */
236                 return fdmatch(tcp, tcp->u_arg[0], set) ||
237                         fdmatch(tcp, tcp->u_arg[2], set);
238
239         case SEN_epoll_ctl:
240                 /* x, x, fd, x */
241                 return fdmatch(tcp, tcp->u_arg[2], set);
242
243
244         case SEN_fanotify_mark:
245         {
246                 /* x, x, mask (64 bit), fd, path */
247                 unsigned long long mask = 0;
248                 int argn = getllval(tcp, &mask, 2);
249                 return fdmatch(tcp, tcp->u_arg[argn], set) ||
250                         upathmatch(tcp, tcp->u_arg[argn + 1], set);
251         }
252         case SEN_oldselect:
253         case SEN_pselect6:
254         case SEN_select:
255         {
256                 int     i, j;
257                 int     nfds;
258                 kernel_ulong_t *args;
259                 kernel_ulong_t select_args[5];
260                 unsigned int oldselect_args[5];
261                 unsigned int fdsize;
262                 fd_set *fds;
263
264                 if (SEN_oldselect == s->sen) {
265                         if (sizeof(*select_args) == sizeof(*oldselect_args)) {
266                                 if (umove(tcp, tcp->u_arg[0], &select_args)) {
267                                         return false;
268                                 }
269                         } else {
270                                 unsigned int n;
271
272                                 if (umove(tcp, tcp->u_arg[0], &oldselect_args)) {
273                                         return false;
274                                 }
275
276                                 for (n = 0; n < 5; ++n) {
277                                         select_args[n] = oldselect_args[n];
278                                 }
279                         }
280                         args = select_args;
281                 } else {
282                         args = tcp->u_arg;
283                 }
284
285                 /* Kernel truncates arg[0] to int, we do the same. */
286                 nfds = (int) args[0];
287                 /* Kernel rejects negative nfds, so we don't parse it either. */
288                 if (nfds <= 0)
289                         return false;
290                 /* Beware of select(2^31-1, NULL, NULL, NULL) and similar... */
291                 if (nfds > 1024*1024)
292                         nfds = 1024*1024;
293                 fdsize = (((nfds + 7) / 8) + current_wordsize-1) & -current_wordsize;
294                 fds = xmalloc(fdsize);
295
296                 for (i = 1; i <= 3; ++i) {
297                         if (args[i] == 0)
298                                 continue;
299                         if (umoven(tcp, args[i], fdsize, fds) < 0) {
300                                 continue;
301                         }
302                         for (j = 0;; j++) {
303                                 j = next_set_bit(fds, j, nfds);
304                                 if (j < 0)
305                                         break;
306                                 if (fdmatch(tcp, j, set)) {
307                                         free(fds);
308                                         return true;
309                                 }
310                         }
311                 }
312                 free(fds);
313                 return false;
314         }
315
316         case SEN_poll:
317         case SEN_ppoll:
318         {
319                 struct pollfd fds;
320                 unsigned nfds;
321                 kernel_ulong_t start, cur, end;
322
323                 start = tcp->u_arg[0];
324                 nfds = tcp->u_arg[1];
325
326                 end = start + sizeof(fds) * nfds;
327
328                 if (nfds == 0 || end < start)
329                         return false;
330
331                 for (cur = start; cur < end; cur += sizeof(fds))
332                         if ((umove(tcp, cur, &fds) == 0)
333                             && fdmatch(tcp, fds.fd, set))
334                                 return true;
335
336                 return false;
337         }
338
339         case SEN_bpf:
340         case SEN_epoll_create:
341         case SEN_epoll_create1:
342         case SEN_eventfd2:
343         case SEN_eventfd:
344         case SEN_fanotify_init:
345         case SEN_inotify_init:
346         case SEN_inotify_init1:
347         case SEN_memfd_create:
348         case SEN_perf_event_open:
349         case SEN_pipe:
350         case SEN_pipe2:
351         case SEN_printargs:
352         case SEN_socket:
353         case SEN_socketpair:
354         case SEN_timerfd_create:
355         case SEN_timerfd_gettime:
356         case SEN_timerfd_settime:
357         case SEN_userfaultfd:
358                 /*
359                  * These have TRACE_FILE or TRACE_DESCRIPTOR or TRACE_NETWORK set,
360                  * but they don't have any file descriptor or path args to test.
361                  */
362                 return false;
363         }
364
365         /*
366          * Our fallback position for calls that haven't already
367          * been handled is to just check arg[0].
368          */
369
370         if (s->sys_flags & TRACE_FILE)
371                 return upathmatch(tcp, tcp->u_arg[0], set);
372
373         if (s->sys_flags & (TRACE_DESC | TRACE_NETWORK))
374                 return fdmatch(tcp, tcp->u_arg[0], set);
375
376         return false;
377 }