]> granicus.if.org Git - strace/blob - pathtrace.c
tests: fix format warnings on x32
[strace] / pathtrace.c
1 /*
2  * Copyright (c) 2011 Comtrol Corp.
3  * Copyright (c) 2011-2019 The strace developers.
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: LGPL-2.1-or-later
7  *
8  */
9
10 #include "defs.h"
11 #include <limits.h>
12 #include <poll.h>
13
14 #include "syscall.h"
15 #include "xstring.h"
16
17 struct path_set global_path_set;
18
19 /*
20  * Return true if specified path matches one that we're tracing.
21  */
22 static bool
23 pathmatch(const char *path, struct path_set *set)
24 {
25         unsigned i;
26
27         for (i = 0; i < set->num_selected; ++i) {
28                 if (strcmp(path, set->paths_selected[i]) == 0)
29                         return true;
30         }
31         return false;
32 }
33
34 /*
35  * Return true if specified path (in user-space) matches.
36  */
37 static bool
38 upathmatch(struct tcb *const tcp, const kernel_ulong_t upath,
39            struct path_set *set)
40 {
41         char path[PATH_MAX + 1];
42
43         return umovestr(tcp, upath, sizeof(path), path) > 0 &&
44                 pathmatch(path, set);
45 }
46
47 /*
48  * Return true if specified fd maps to a path we're tracing.
49  */
50 static bool
51 fdmatch(struct tcb *tcp, int fd, struct path_set *set)
52 {
53         char path[PATH_MAX + 1];
54         int n = getfdpath(tcp, fd, path, sizeof(path));
55
56         return n >= 0 && pathmatch(path, set);
57 }
58
59 /*
60  * Add a path to the set we're tracing.
61  * Specifying NULL will delete all paths.
62  */
63 static void
64 storepath(const char *path, struct path_set *set)
65 {
66         if (pathmatch(path, set))
67                 return; /* already in table */
68
69         if (set->num_selected >= set->size)
70                 set->paths_selected =
71                         xgrowarray(set->paths_selected, &set->size,
72                                    sizeof(set->paths_selected[0]));
73
74         set->paths_selected[set->num_selected++] = path;
75 }
76
77 /*
78  * Get path associated with fd.
79  */
80 int
81 getfdpath(struct tcb *tcp, int fd, char *buf, unsigned bufsize)
82 {
83         char linkpath[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3];
84         ssize_t n;
85
86         if (fd < 0)
87                 return -1;
88
89         xsprintf(linkpath, "/proc/%u/fd/%u", tcp->pid, fd);
90         n = readlink(linkpath, buf, bufsize - 1);
91         /*
92          * NB: if buf is too small, readlink doesn't fail,
93          * it returns truncated result (IOW: n == bufsize - 1).
94          */
95         if (n >= 0)
96                 buf[n] = '\0';
97         return n;
98 }
99
100 /*
101  * Add a path to the set we're tracing.  Also add the canonicalized
102  * version of the path.  Specifying NULL will delete all paths.
103  */
104 void
105 pathtrace_select_set(const char *path, struct path_set *set)
106 {
107         char *rpath;
108
109         storepath(path, set);
110
111         rpath = realpath(path, NULL);
112
113         if (rpath == NULL)
114                 return;
115
116         /* if realpath and specified path are same, we're done */
117         if (strcmp(path, rpath) == 0) {
118                 free(rpath);
119                 return;
120         }
121
122         error_msg("Requested path '%s' resolved into '%s'", path, rpath);
123         storepath(rpath, set);
124 }
125
126 static bool
127 match_xselect_args(struct tcb *tcp, const kernel_ulong_t *args,
128                    struct path_set *set)
129 {
130         /* Kernel truncates arg[0] to int, we do the same. */
131         int nfds = (int) args[0];
132         /* Kernel rejects negative nfds, so we don't parse it either. */
133         if (nfds <= 0)
134                 return false;
135         /* Beware of select(2^31-1, NULL, NULL, NULL) and similar... */
136         if (nfds > 1024*1024)
137                 nfds = 1024*1024;
138         unsigned int fdsize = (((nfds + 7) / 8) + current_wordsize-1) & -current_wordsize;
139         fd_set *fds = xmalloc(fdsize);
140
141         for (unsigned int i = 1; i <= 3; ++i) {
142                 if (args[i] == 0)
143                         continue;
144                 if (umoven(tcp, args[i], fdsize, fds) < 0)
145                         continue;
146                 for (int j = 0;; ++j) {
147                         j = next_set_bit(fds, j, nfds);
148                         if (j < 0)
149                                 break;
150                         if (fdmatch(tcp, j, set)) {
151                                 free(fds);
152                                 return true;
153                         }
154                 }
155         }
156
157         free(fds);
158         return false;
159 }
160
161 /*
162  * Return true if syscall accesses a selected path
163  * (or if no paths have been specified for tracing).
164  */
165 bool
166 pathtrace_match_set(struct tcb *tcp, struct path_set *set)
167 {
168         const struct_sysent *s;
169
170         s = tcp_sysent(tcp);
171
172         if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
173                 return false;
174
175         /*
176          * Check for special cases where we need to do something
177          * other than test arg[0].
178          */
179
180         switch (s->sen) {
181         case SEN_dup2:
182         case SEN_dup3:
183         case SEN_kexec_file_load:
184         case SEN_sendfile:
185         case SEN_sendfile64:
186         case SEN_tee:
187                 /* fd, fd */
188                 return fdmatch(tcp, tcp->u_arg[0], set) ||
189                         fdmatch(tcp, tcp->u_arg[1], set);
190
191         case SEN_execveat:
192         case SEN_faccessat:
193         case SEN_fchmodat:
194         case SEN_fchownat:
195         case SEN_fspick:
196         case SEN_fstatat64:
197         case SEN_futimesat:
198         case SEN_inotify_add_watch:
199         case SEN_mkdirat:
200         case SEN_mknodat:
201         case SEN_name_to_handle_at:
202         case SEN_newfstatat:
203         case SEN_open_tree:
204         case SEN_openat:
205         case SEN_readlinkat:
206         case SEN_statx:
207         case SEN_unlinkat:
208         case SEN_utimensat_time32:
209         case SEN_utimensat_time64:
210                 /* fd, path */
211                 return fdmatch(tcp, tcp->u_arg[0], set) ||
212                         upathmatch(tcp, tcp->u_arg[1], set);
213
214         case SEN_link:
215         case SEN_mount:
216         case SEN_pivotroot:
217                 /* path, path */
218                 return upathmatch(tcp, tcp->u_arg[0], set) ||
219                         upathmatch(tcp, tcp->u_arg[1], set);
220
221         case SEN_quotactl:
222         case SEN_symlink:
223                 /* x, path */
224                 return upathmatch(tcp, tcp->u_arg[1], set);
225
226         case SEN_linkat:
227         case SEN_move_mount:
228         case SEN_renameat2:
229         case SEN_renameat:
230                 /* fd, path, fd, path */
231                 return fdmatch(tcp, tcp->u_arg[0], set) ||
232                         fdmatch(tcp, tcp->u_arg[2], set) ||
233                         upathmatch(tcp, tcp->u_arg[1], set) ||
234                         upathmatch(tcp, tcp->u_arg[3], set);
235
236 #if HAVE_ARCH_OLD_MMAP
237         case SEN_old_mmap:
238 # if HAVE_ARCH_OLD_MMAP_PGOFF
239         case SEN_old_mmap_pgoff:
240 # endif
241         {
242                 kernel_ulong_t *args =
243                         fetch_indirect_syscall_args(tcp, tcp->u_arg[0], 6);
244
245                 return args && fdmatch(tcp, args[4], set);
246         }
247 #endif /* HAVE_ARCH_OLD_MMAP */
248
249         case SEN_mmap:
250         case SEN_mmap_4koff:
251         case SEN_mmap_pgoff:
252         case SEN_ARCH_mmap:
253                 /* x, x, x, x, fd */
254                 return fdmatch(tcp, tcp->u_arg[4], set);
255
256         case SEN_symlinkat:
257                 /* x, fd, path */
258                 return fdmatch(tcp, tcp->u_arg[1], set) ||
259                         upathmatch(tcp, tcp->u_arg[2], set);
260
261         case SEN_copy_file_range:
262         case SEN_splice:
263                 /* fd, x, fd, x, x, x */
264                 return fdmatch(tcp, tcp->u_arg[0], set) ||
265                         fdmatch(tcp, tcp->u_arg[2], set);
266
267         case SEN_epoll_ctl:
268                 /* x, x, fd, x */
269                 return fdmatch(tcp, tcp->u_arg[2], set);
270
271
272         case SEN_fanotify_mark:
273         {
274                 /* x, x, mask (64 bit), fd, path */
275                 unsigned long long mask = 0;
276                 int argn = getllval(tcp, &mask, 2);
277                 return fdmatch(tcp, tcp->u_arg[argn], set) ||
278                         upathmatch(tcp, tcp->u_arg[argn + 1], set);
279         }
280 #if HAVE_ARCH_OLD_SELECT
281         case SEN_oldselect:
282         {
283                 kernel_ulong_t *args =
284                         fetch_indirect_syscall_args(tcp, tcp->u_arg[0], 5);
285
286                 return args && match_xselect_args(tcp, args, set);
287         }
288 #endif
289         case SEN_pselect6_time32:
290         case SEN_pselect6_time64:
291         case SEN_select:
292                 return match_xselect_args(tcp, tcp->u_arg, set);
293         case SEN_poll_time32:
294         case SEN_poll_time64:
295         case SEN_ppoll_time32:
296         case SEN_ppoll_time64:
297         {
298                 struct pollfd fds;
299                 unsigned nfds;
300                 kernel_ulong_t start, cur, end;
301
302                 start = tcp->u_arg[0];
303                 nfds = tcp->u_arg[1];
304
305                 if (nfds > 1024 * 1024)
306                         nfds = 1024 * 1024;
307                 end = start + sizeof(fds) * nfds;
308
309                 if (nfds == 0 || end < start)
310                         return false;
311
312                 for (cur = start; cur < end; cur += sizeof(fds)) {
313                         if (umove(tcp, cur, &fds))
314                                 break;
315                         if (fdmatch(tcp, fds.fd, set))
316                                 return true;
317                 }
318
319                 return false;
320         }
321
322         case SEN_fsconfig: {
323                 /* x, x, x, maybe path, maybe fd */
324                 const unsigned int cmd = tcp->u_arg[1];
325                 switch (cmd) {
326                         case 3 /* FSCONFIG_SET_PATH */:
327                         case 4 /* FSCONFIG_SET_PATH_EMPTY */:
328                                 return fdmatch(tcp, tcp->u_arg[4], set) ||
329                                         upathmatch(tcp, tcp->u_arg[3], set);
330                         case 5 /* FSCONFIG_SET_FD */:
331                                 return fdmatch(tcp, tcp->u_arg[4], set);
332                 }
333
334                 return false;
335         }
336
337         case SEN_accept4:
338         case SEN_accept:
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_fsmount:
346         case SEN_fsopen:
347         case SEN_inotify_init:
348         case SEN_inotify_init1:
349         case SEN_io_uring_enter:
350         case SEN_io_uring_register:
351         case SEN_io_uring_setup:
352         case SEN_memfd_create:
353         case SEN_mq_getsetattr:
354         case SEN_mq_notify:
355         case SEN_mq_open:
356         case SEN_mq_timedreceive_time32:
357         case SEN_mq_timedreceive_time64:
358         case SEN_mq_timedsend_time32:
359         case SEN_mq_timedsend_time64:
360         case SEN_perf_event_open:
361         case SEN_pidfd_open:
362         case SEN_pipe:
363         case SEN_pipe2:
364         case SEN_printargs:
365         case SEN_signalfd4:
366         case SEN_signalfd:
367         case SEN_socket:
368         case SEN_socketpair:
369         case SEN_timerfd_create:
370         case SEN_timerfd_gettime32:
371         case SEN_timerfd_gettime64:
372         case SEN_timerfd_settime32:
373         case SEN_timerfd_settime64:
374         case SEN_userfaultfd:
375                 /*
376                  * These have TRACE_FILE or TRACE_DESCRIPTOR or TRACE_NETWORK set,
377                  * but they don't have any file descriptor or path args to test.
378                  */
379                 return false;
380         }
381
382         /*
383          * Our fallback position for calls that haven't already
384          * been handled is to just check arg[0].
385          */
386
387         if (s->sys_flags & TRACE_FILE)
388                 return upathmatch(tcp, tcp->u_arg[0], set);
389
390         if (s->sys_flags & (TRACE_DESC | TRACE_NETWORK))
391                 return fdmatch(tcp, tcp->u_arg[0], set);
392
393         return false;
394 }