1 /*
2 * Copyright (c) 2011 Comtrol Corp.
3 * Copyright (c) 2011-2023 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 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16
17 #include "largefile_wrappers.h"
18 #include "number_set.h"
19 #include "sen.h"
20 #include "xstring.h"
21
22 struct path_set global_path_set;
23
24 /*
25 * Return true if specified path matches one that we're tracing.
26 */
27 static bool
28 pathmatch(const char *path, struct path_set *set)
29 {
30 if (!set)
31 return false;
32
33 for (unsigned int i = 0; i < set->num_selected; ++i) {
34 if (strcmp(path, set->paths_selected[i].path) == 0)
35 return true;
36 }
37 return false;
38 }
39
40 /*
41 * Return true if specified path (in user-space) matches.
42 */
43 static bool
44 upathmatch(struct tcb *const tcp, const kernel_ulong_t upath,
45 struct path_set *set)
46 {
47 if (!set)
48 return false;
49
50 char path[PATH_MAX + 1];
51
52 return umovestr(tcp, upath, sizeof(path), path) > 0 &&
53 pathmatch(path, set);
54 }
55
56 /*
57 * Return true if specified fd maps to a path we're tracing.
58 */
59 static bool
60 fdmatch(struct tcb *tcp, int fd, struct path_set *set, struct number_set *fdset)
61 {
62 if (fdset && fd >= 0 && is_number_in_set(fd, fdset))
63 return true;
64 if (!set)
65 return false;
66
67 char path[PATH_MAX + 1];
68 int n = getfdpath(tcp, fd, path, sizeof(path));
69
70 return n >= 0 && pathmatch(path, set);
71 }
72
73 /*
74 * Add a path to the set we're tracing.
75 * Specifying NULL will delete all paths.
76 */
77 static void
78 storepath(const char *path, struct path_set *set)
79 {
80 if (pathmatch(path, set))
81 return; /* already in table */
82
83 if (set->num_selected >= set->size)
84 set->paths_selected =
85 xgrowarray(set->paths_selected, &set->size,
86 sizeof(set->paths_selected[0]));
87
88 set->paths_selected[set->num_selected++].path = path;
89 }
90
91 int
92 get_proc_pid_fd_path(int proc_pid, int fd, char *buf, unsigned bufsize,
93 bool *deleted)
94 {
95 char linkpath[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3];
96 ssize_t n;
97
98 if (fd < 0)
99 return -1;
100
101 xsprintf(linkpath, "/proc/%u/fd/%u", proc_pid, fd);
102 n = readlink(linkpath, buf, bufsize - 1);
103 if (n < 0)
104 goto end;
105
106 /*
107 * NB: if buf is too small, readlink doesn't fail,
108 * it returns truncated result (IOW: n == bufsize - 1).
109 */
110 buf[n] = '\0';
111 if (deleted)
112 *deleted = false;
113
114 /*
115 * Try to figure out if the kernel has appended " (deleted)"
116 * to the end of a potentially unlinked path and set deleted
117 * if it is the case.
118 */
119 static const char del_sfx[] = " (deleted)";
120 if ((size_t) n <= sizeof(del_sfx))
121 goto end;
122
123 char *del = buf + n + 1 - sizeof(del_sfx);
124
125 if (memcmp(del, del_sfx, sizeof(del_sfx)))
126 goto end;
127
128 strace_stat_t st_link;
129 strace_stat_t st_path;
130 int rc = stat_file(linkpath, &st_link);
131
132 if (rc)
133 goto end;
134
135 rc = lstat_file(buf, &st_path);
136
137 if (rc ||
138 (st_link.st_ino != st_path.st_ino) ||
139 (st_link.st_dev != st_path.st_dev)) {
140 *del = '\0';
141 n = del - buf + 1;
142 if (deleted)
143 *deleted = true;
144 }
145
146 end:
147 return n;
148 }
149
150 /*
151 * Get path associated with fd of a process with pid.
152 */
153 int
154 getfdpath_pid(pid_t pid, int fd, char *buf, unsigned bufsize, bool *deleted)
155 {
156 if (fd < 0)
157 return -1;
158
159 int proc_pid = get_proc_pid(pid);
160 if (!proc_pid)
161 return -1;
162
163 return get_proc_pid_fd_path(proc_pid, fd, buf, bufsize, deleted);
164 }
165
166 /*
167 * Add a path to the set we're tracing. Also add the canonicalized
168 * version of the path. Specifying NULL will delete all paths.
169 */
170 void
171 pathtrace_select_set(const char *path, struct path_set *set)
172 {
173 char *rpath;
174
175 storepath(path, set);
176
177 rpath = realpath(path, NULL);
178
179 if (rpath == NULL)
180 return;
181
182 /* if realpath and specified path are same, we're done */
183 if (strcmp(path, rpath) == 0) {
184 free(rpath);
185 return;
186 }
187
188 if (!is_number_in_set(QUIET_PATH_RESOLVE, quiet_set)) {
189 char *path_quoted = xmalloc(strlen(path) * 4 + 4);
190 char *rpath_quoted = xmalloc(strlen(rpath) * 4 + 4);
191
192 string_quote(path, path_quoted, strlen(path) + 1,
193 QUOTE_0_TERMINATED, NULL);
194 string_quote(rpath, rpath_quoted, strlen(rpath) + 1,
195 QUOTE_0_TERMINATED, NULL);
196
197 error_msg("Requested path %s resolved into %s",
198 path_quoted, rpath_quoted);
199
200 free(path_quoted);
201 free(rpath_quoted);
202 }
203 storepath(rpath, set);
204 }
205
206 static bool
207 match_xselect_args(struct tcb *tcp, const kernel_ulong_t *args,
208 struct path_set *set, struct number_set *fdset)
209 {
210 /* Kernel truncates arg[0] to int, we do the same. */
211 int nfds = (int) args[0];
212 /* Kernel rejects negative nfds, so we don't parse it either. */
213 if (nfds <= 0)
214 return false;
215 /* Beware of select(2^31-1, NULL, NULL, NULL) and similar... */
216 if (nfds > 1024*1024)
217 nfds = 1024*1024;
218 unsigned int fdsize = (((nfds + 7) / 8) + current_wordsize-1) & -current_wordsize;
219 fd_set *fds = xmalloc(fdsize);
220
221 for (unsigned int i = 1; i <= 3; ++i) {
222 if (args[i] == 0)
223 continue;
224 if (umoven(tcp, args[i], fdsize, fds) < 0)
225 continue;
226 for (int j = 0;; ++j) {
227 j = next_set_bit(fds, j, nfds);
228 if (j < 0)
229 break;
230 if (fdmatch(tcp, j, set, fdset)) {
231 free(fds);
232 return true;
233 }
234 }
235 }
236
237 free(fds);
238 return false;
239 }
240
241 /*
242 * Return true if syscall accesses a selected path
243 * (or if no paths have been specified for tracing).
244 */
245 bool
246 pathtrace_match_set(struct tcb *tcp, struct path_set *set,
247 struct number_set *fdset)
248 {
249 const struct_sysent *s;
250
251 s = tcp_sysent(tcp);
252
253 if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
254 return false;
255
256 /*
257 * Check for special cases where we need to do something
258 * other than test arg[0].
259 */
260
261 switch (s->sen) {
262 case SEN_dup2:
263 case SEN_dup3:
264 case SEN_kexec_file_load:
265 case SEN_sendfile:
266 case SEN_sendfile64:
267 case SEN_tee:
268 /* fd, fd */
269 return fdmatch(tcp, tcp->u_arg[0], set, fdset) ||
270 fdmatch(tcp, tcp->u_arg[1], set, fdset);
271
272 case SEN_execveat:
273 case SEN_faccessat:
274 case SEN_faccessat2:
275 case SEN_fchmodat:
276 case SEN_fchmodat2:
277 case SEN_fchownat:
278 case SEN_fspick:
279 case SEN_fstatat64:
280 case SEN_futimesat:
281 case SEN_inotify_add_watch:
282 case SEN_mkdirat:
283 case SEN_mknodat:
284 case SEN_mount_setattr:
285 case SEN_name_to_handle_at:
286 case SEN_newfstatat:
287 case SEN_open_tree:
288 case SEN_openat:
289 case SEN_openat2:
290 case SEN_readlinkat:
291 case SEN_statx:
292 case SEN_unlinkat:
293 case SEN_utimensat_time32:
294 case SEN_utimensat_time64:
295 /* fd, path */
296 return fdmatch(tcp, tcp->u_arg[0], set, fdset) ||
297 upathmatch(tcp, tcp->u_arg[1], set);
298
299 case SEN_link:
300 case SEN_mount:
301 case SEN_pivotroot:
302 /* path, path */
303 return upathmatch(tcp, tcp->u_arg[0], set) ||
304 upathmatch(tcp, tcp->u_arg[1], set);
305
306 case SEN_quotactl:
307 case SEN_symlink:
308 /* x, path */
309 return upathmatch(tcp, tcp->u_arg[1], set);
310
311 case SEN_linkat:
312 case SEN_move_mount:
313 case SEN_renameat2:
314 case SEN_renameat:
315 /* fd, path, fd, path */
316 return fdmatch(tcp, tcp->u_arg[0], set, fdset) ||
317 fdmatch(tcp, tcp->u_arg[2], set, fdset) ||
318 upathmatch(tcp, tcp->u_arg[1], set) ||
319 upathmatch(tcp, tcp->u_arg[3], set);
320
321 #if HAVE_ARCH_OLD_MMAP
322 case SEN_old_mmap:
323 # if HAVE_ARCH_OLD_MMAP_PGOFF
324 case SEN_old_mmap_pgoff:
325 # endif
326 {
327 kernel_ulong_t *args =
328 fetch_indirect_syscall_args(tcp, tcp->u_arg[0], 6);
329
330 return args && fdmatch(tcp, args[4], set, fdset);
331 }
332 #endif /* HAVE_ARCH_OLD_MMAP */
333
334 case SEN_mmap:
335 case SEN_mmap_4koff:
336 case SEN_mmap_pgoff:
337 case SEN_ARCH_mmap:
338 /* x, x, x, x, fd */
339 return fdmatch(tcp, tcp->u_arg[4], set, fdset);
340
341 case SEN_symlinkat:
342 /* x, fd, path */
343 return fdmatch(tcp, tcp->u_arg[1], set, fdset) ||
344 upathmatch(tcp, tcp->u_arg[2], set);
345
346 case SEN_copy_file_range:
347 case SEN_splice:
348 /* fd, x, fd, x, x, x */
349 return fdmatch(tcp, tcp->u_arg[0], set, fdset) ||
350 fdmatch(tcp, tcp->u_arg[2], set, fdset);
351
352 case SEN_epoll_ctl:
353 /* x, x, fd, x */
354 return fdmatch(tcp, tcp->u_arg[2], set, fdset);
355
356
357 case SEN_fanotify_mark:
358 {
359 /* x, x, mask (64 bit), fd, path */
360 unsigned long long mask = 0;
361 unsigned int argn = getllval(tcp, &mask, 2);
362 return fdmatch(tcp, tcp->u_arg[argn], set, fdset) ||
363 upathmatch(tcp, tcp->u_arg[argn + 1], set);
364 }
365 #if HAVE_ARCH_OLD_SELECT
366 case SEN_oldselect:
367 {
368 kernel_ulong_t *args =
369 fetch_indirect_syscall_args(tcp, tcp->u_arg[0], 5);
370
371 return args && match_xselect_args(tcp, args, set, fdset);
372 }
373 #endif
374 case SEN_pselect6_time32:
375 case SEN_pselect6_time64:
376 case SEN_select:
377 return match_xselect_args(tcp, tcp->u_arg, set, fdset);
378 case SEN_poll_time32:
379 case SEN_poll_time64:
380 case SEN_ppoll_time32:
381 case SEN_ppoll_time64:
382 {
383 struct pollfd fds;
384
385 const kernel_ulong_t start = tcp->u_arg[0];
386 unsigned int nfds = tcp->u_arg[1];
387 if (nfds > 1024 * 1024)
388 nfds = 1024 * 1024;
389 const kernel_ulong_t end = start + sizeof(fds) * nfds;
390
391 if (nfds == 0 || end < start)
392 return false;
393
394 for (kernel_ulong_t cur = start; cur < end; cur += sizeof(fds)) {
395 if (umove(tcp, cur, &fds))
396 break;
397 if (fdmatch(tcp, fds.fd, set, fdset))
398 return true;
399 }
400
401 return false;
402 }
403
404 case SEN_fsconfig: {
405 /* x, x, x, maybe path, maybe fd */
406 const unsigned int cmd = tcp->u_arg[1];
407 switch (cmd) {
408 case 3 /* FSCONFIG_SET_PATH */:
409 case 4 /* FSCONFIG_SET_PATH_EMPTY */:
410 return fdmatch(tcp, tcp->u_arg[4], set, fdset)
411 || upathmatch(tcp, tcp->u_arg[3], set);
412 case 5 /* FSCONFIG_SET_FD */:
413 return fdmatch(tcp, tcp->u_arg[4], set, fdset);
414 }
415
416 return false;
417 }
418
419 case SEN_bpf:
420 case SEN_epoll_create:
421 case SEN_epoll_create1:
422 case SEN_eventfd2:
423 case SEN_eventfd:
424 case SEN_fanotify_init:
425 case SEN_fsopen:
426 case SEN_inotify_init:
427 case SEN_inotify_init1:
428 case SEN_io_uring_setup:
429 case SEN_landlock_create_ruleset:
430 case SEN_memfd_create:
431 case SEN_memfd_secret:
432 case SEN_mq_open:
433 case SEN_perf_event_open:
434 case SEN_pidfd_open:
435 case SEN_pipe:
436 case SEN_pipe2:
437 case SEN_printargs:
438 case SEN_socket:
439 case SEN_socketpair:
440 case SEN_timerfd_create:
441 case SEN_userfaultfd:
442 /*
443 * These have TRACE_FILE or TRACE_DESC or TRACE_NETWORK set,
444 * but they don't have any file descriptor or path args to test.
445 */
446 return false;
447 }
448
449 /*
450 * Our fallback position for calls that haven't already
451 * been handled is to just check arg[0].
452 */
453
454 if (s->sys_flags & TRACE_FILE)
455 return upathmatch(tcp, tcp->u_arg[0], set);
456
457 if (s->sys_flags & (TRACE_DESC | TRACE_NETWORK))
458 return fdmatch(tcp, tcp->u_arg[0], set, fdset);
459
460 return false;
461 }