Branch data Line data Source code
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 : : #include <sys/param.h>
31 : : #include <poll.h>
32 : :
33 : : #include "syscall.h"
34 : :
35 : : const char **paths_selected = NULL;
36 : : static unsigned num_selected = 0;
37 : :
38 : : /*
39 : : * Return true if specified path matches one that we're tracing.
40 : : */
41 : : static int
42 : 16062 : pathmatch(const char *path)
43 : : {
44 : : unsigned i;
45 : :
46 [ + + ]: 70773 : for (i = 0; i < num_selected; ++i) {
47 [ + + ]: 55738 : if (strcmp(path, paths_selected[i]) == 0)
48 : 1027 : return 1;
49 : : }
50 : 15035 : return 0;
51 : : }
52 : :
53 : : /*
54 : : * Return true if specified path (in user-space) matches.
55 : : */
56 : : static int
57 : 1556 : upathmatch(struct tcb *const tcp, const kernel_ulong_t upath)
58 : : {
59 : : char path[PATH_MAX + 1];
60 : :
61 [ + - + + ]: 3112 : return umovestr(tcp, upath, sizeof path, path) > 0 &&
62 : 1556 : pathmatch(path);
63 : : }
64 : :
65 : : /*
66 : : * Return true if specified fd maps to a path we're tracing.
67 : : */
68 : : static int
69 : 14421 : fdmatch(struct tcb *tcp, int fd)
70 : : {
71 : : char path[PATH_MAX + 1];
72 : 14421 : int n = getfdpath(tcp, fd, path, sizeof(path));
73 : :
74 [ + + ][ + + ]: 14421 : return n >= 0 && pathmatch(path);
75 : : }
76 : :
77 : : /*
78 : : * Add a path to the set we're tracing.
79 : : * Specifying NULL will delete all paths.
80 : : */
81 : : static void
82 : 136 : storepath(const char *path)
83 : : {
84 : : unsigned i;
85 : :
86 [ - + ]: 136 : if (pathmatch(path))
87 : 0 : return; /* already in table */
88 : :
89 : 136 : i = num_selected++;
90 : 136 : paths_selected = xreallocarray(paths_selected, num_selected,
91 : : sizeof(paths_selected[0]));
92 : 136 : paths_selected[i] = path;
93 : : }
94 : :
95 : : /*
96 : : * Get path associated with fd.
97 : : */
98 : : int
99 : 14979 : getfdpath(struct tcb *tcp, int fd, char *buf, unsigned bufsize)
100 : : {
101 : : char linkpath[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3];
102 : : ssize_t n;
103 : :
104 [ + + ]: 14979 : if (fd < 0)
105 : 51 : return -1;
106 : :
107 : 14928 : sprintf(linkpath, "/proc/%u/fd/%u", tcp->pid, fd);
108 : 14928 : n = readlink(linkpath, buf, bufsize - 1);
109 : : /*
110 : : * NB: if buf is too small, readlink doesn't fail,
111 : : * it returns truncated result (IOW: n == bufsize - 1).
112 : : */
113 [ + - ]: 14928 : if (n >= 0)
114 : 14928 : buf[n] = '\0';
115 : 14979 : return n;
116 : : }
117 : :
118 : : /*
119 : : * Add a path to the set we're tracing. Also add the canonicalized
120 : : * version of the path. Secifying NULL will delete all paths.
121 : : */
122 : : void
123 : 116 : pathtrace_select(const char *path)
124 : : {
125 : : char *rpath;
126 : :
127 : 116 : storepath(path);
128 : :
129 : 116 : rpath = realpath(path, NULL);
130 : :
131 [ + + ]: 116 : if (rpath == NULL)
132 : 45 : return;
133 : :
134 : : /* if realpath and specified path are same, we're done */
135 [ + + ]: 71 : if (strcmp(path, rpath) == 0) {
136 : 51 : free(rpath);
137 : 51 : return;
138 : : }
139 : :
140 : 20 : error_msg("Requested path '%s' resolved into '%s'", path, rpath);
141 : 20 : storepath(rpath);
142 : : }
143 : :
144 : : /*
145 : : * Return true if syscall accesses a selected path
146 : : * (or if no paths have been specified for tracing).
147 : : */
148 : : int
149 : 15926 : pathtrace_match(struct tcb *tcp)
150 : : {
151 : : const struct_sysent *s;
152 : :
153 : 15926 : s = tcp->s_ent;
154 : :
155 [ - + ]: 15926 : if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
156 : 0 : return 0;
157 : :
158 : : /*
159 : : * Check for special cases where we need to do something
160 : : * other than test arg[0].
161 : : */
162 : :
163 [ - + - - : 15926 : switch (s->sen) {
- - - - -
- - - -
+ ]
164 : : case SEN_dup2:
165 : : case SEN_dup3:
166 : : case SEN_kexec_file_load:
167 : : case SEN_sendfile:
168 : : case SEN_sendfile64:
169 : : case SEN_tee:
170 : : /* fd, fd */
171 [ # # # # ]: 0 : return fdmatch(tcp, tcp->u_arg[0]) ||
172 : 0 : fdmatch(tcp, tcp->u_arg[1]);
173 : :
174 : : case SEN_faccessat:
175 : : case SEN_fchmodat:
176 : : case SEN_fchownat:
177 : : case SEN_fstatat64:
178 : : case SEN_futimesat:
179 : : case SEN_inotify_add_watch:
180 : : case SEN_mkdirat:
181 : : case SEN_mknodat:
182 : : case SEN_name_to_handle_at:
183 : : case SEN_newfstatat:
184 : : case SEN_openat:
185 : : case SEN_readlinkat:
186 : : case SEN_statx:
187 : : case SEN_unlinkat:
188 : : case SEN_utimensat:
189 : : /* fd, path */
190 [ + - + - ]: 102 : return fdmatch(tcp, tcp->u_arg[0]) ||
191 : 51 : upathmatch(tcp, tcp->u_arg[1]);
192 : :
193 : : case SEN_link:
194 : : case SEN_mount:
195 : : case SEN_pivotroot:
196 : : /* path, path */
197 [ # # # # ]: 0 : return upathmatch(tcp, tcp->u_arg[0]) ||
198 : 0 : upathmatch(tcp, tcp->u_arg[1]);
199 : :
200 : : case SEN_quotactl:
201 : : /* x, path */
202 : 0 : return upathmatch(tcp, tcp->u_arg[1]);
203 : :
204 : : case SEN_linkat:
205 : : case SEN_renameat2:
206 : : case SEN_renameat:
207 : : /* fd, path, fd, path */
208 [ # # ]: 0 : return fdmatch(tcp, tcp->u_arg[0]) ||
209 [ # # ]: 0 : fdmatch(tcp, tcp->u_arg[2]) ||
210 [ # # ][ # # ]: 0 : upathmatch(tcp, tcp->u_arg[1]) ||
211 : 0 : upathmatch(tcp, tcp->u_arg[3]);
212 : :
213 : : case SEN_old_mmap:
214 : : #if defined(S390)
215 : : case SEN_old_mmap_pgoff:
216 : : #endif
217 : : case SEN_mmap:
218 : : case SEN_mmap_4koff:
219 : : case SEN_mmap_pgoff:
220 : : case SEN_ARCH_mmap:
221 : : /* x, x, x, x, fd */
222 : 0 : return fdmatch(tcp, tcp->u_arg[4]);
223 : :
224 : : case SEN_symlinkat:
225 : : /* path, fd, path */
226 [ # # ]: 0 : return fdmatch(tcp, tcp->u_arg[1]) ||
227 [ # # ][ # # ]: 0 : upathmatch(tcp, tcp->u_arg[0]) ||
228 : 0 : upathmatch(tcp, tcp->u_arg[2]);
229 : :
230 : : case SEN_copy_file_range:
231 : : case SEN_splice:
232 : : /* fd, x, fd, x, x, x */
233 [ # # # # ]: 0 : return fdmatch(tcp, tcp->u_arg[0]) ||
234 : 0 : fdmatch(tcp, tcp->u_arg[2]);
235 : :
236 : : case SEN_epoll_ctl:
237 : : /* x, x, fd, x */
238 : 0 : return fdmatch(tcp, tcp->u_arg[2]);
239 : :
240 : :
241 : : case SEN_fanotify_mark:
242 : : /* x, x, x, fd, path */
243 [ # # # # ]: 0 : return fdmatch(tcp, tcp->u_arg[3]) ||
244 : 0 : upathmatch(tcp, tcp->u_arg[4]);
245 : :
246 : : case SEN_oldselect:
247 : : case SEN_pselect6:
248 : : case SEN_select:
249 : : {
250 : : int i, j;
251 : : int nfds;
252 : : kernel_ulong_t *args;
253 : : kernel_ulong_t select_args[5];
254 : : unsigned int oldselect_args[5];
255 : : unsigned int fdsize;
256 : : fd_set *fds;
257 : :
258 [ # # ]: 0 : if (SEN_oldselect == s->sen) {
259 : : if (sizeof(*select_args) == sizeof(*oldselect_args)) {
260 : : if (umove(tcp, tcp->u_arg[0], &select_args)) {
261 : : return 0;
262 : : }
263 : : } else {
264 : : unsigned int n;
265 : :
266 [ # # ]: 0 : if (umove(tcp, tcp->u_arg[0], &oldselect_args)) {
267 : 0 : return 0;
268 : : }
269 : :
270 [ # # ]: 0 : for (n = 0; n < 5; ++n) {
271 : 0 : select_args[n] = oldselect_args[n];
272 : : }
273 : : }
274 : 0 : args = select_args;
275 : : } else {
276 : 0 : args = tcp->u_arg;
277 : : }
278 : :
279 : : /* Kernel truncates arg[0] to int, we do the same. */
280 : 0 : nfds = (int) args[0];
281 : : /* Kernel rejects negative nfds, so we don't parse it either. */
282 [ # # ]: 0 : if (nfds <= 0)
283 : 0 : return 0;
284 : : /* Beware of select(2^31-1, NULL, NULL, NULL) and similar... */
285 [ # # ]: 0 : if (nfds > 1024*1024)
286 : 0 : nfds = 1024*1024;
287 : 0 : fdsize = (((nfds + 7) / 8) + current_wordsize-1) & -current_wordsize;
288 : 0 : fds = xmalloc(fdsize);
289 : :
290 [ # # ]: 0 : for (i = 1; i <= 3; ++i) {
291 [ # # ]: 0 : if (args[i] == 0)
292 : 0 : continue;
293 [ # # ]: 0 : if (umoven(tcp, args[i], fdsize, fds) < 0) {
294 : 0 : continue;
295 : : }
296 : 0 : for (j = 0;; j++) {
297 : 0 : j = next_set_bit(fds, j, nfds);
298 [ # # ]: 0 : if (j < 0)
299 : 0 : break;
300 [ # # ]: 0 : if (fdmatch(tcp, j)) {
301 : 0 : free(fds);
302 : 0 : return 1;
303 : : }
304 : : }
305 : : }
306 : 0 : free(fds);
307 : 0 : return 0;
308 : : }
309 : :
310 : : case SEN_poll:
311 : : case SEN_ppoll:
312 : : {
313 : : struct pollfd fds;
314 : : unsigned nfds;
315 : : kernel_ulong_t start, cur, end;
316 : :
317 : 0 : start = tcp->u_arg[0];
318 : 0 : nfds = tcp->u_arg[1];
319 : :
320 : 0 : end = start + sizeof(fds) * nfds;
321 : :
322 [ # # ][ # # ]: 0 : if (nfds == 0 || end < start)
323 : 0 : return 0;
324 : :
325 [ # # ]: 0 : for (cur = start; cur < end; cur += sizeof(fds))
326 [ # # ]: 0 : if ((umove(tcp, cur, &fds) == 0)
327 [ # # ]: 0 : && fdmatch(tcp, fds.fd))
328 : 0 : return 1;
329 : :
330 : 0 : return 0;
331 : : }
332 : :
333 : : case SEN_bpf:
334 : : case SEN_epoll_create:
335 : : case SEN_epoll_create1:
336 : : case SEN_eventfd2:
337 : : case SEN_eventfd:
338 : : case SEN_fanotify_init:
339 : : case SEN_inotify_init1:
340 : : case SEN_memfd_create:
341 : : case SEN_perf_event_open:
342 : : case SEN_pipe:
343 : : case SEN_pipe2:
344 : : case SEN_printargs:
345 : : case SEN_socket:
346 : : case SEN_socketpair:
347 : : case SEN_timerfd_create:
348 : : case SEN_timerfd_gettime:
349 : : case SEN_timerfd_settime:
350 : : case SEN_userfaultfd:
351 : : /*
352 : : * These have TRACE_FILE or TRACE_DESCRIPTOR or TRACE_NETWORK set,
353 : : * but they don't have any file descriptor or path args to test.
354 : : */
355 : 0 : return 0;
356 : : }
357 : :
358 : : /*
359 : : * Our fallback position for calls that haven't already
360 : : * been handled is to just check arg[0].
361 : : */
362 : :
363 [ + + ]: 15875 : if (s->sys_flags & TRACE_FILE)
364 : 1505 : return upathmatch(tcp, tcp->u_arg[0]);
365 : :
366 [ + - ]: 14370 : if (s->sys_flags & (TRACE_DESC | TRACE_NETWORK))
367 : 14370 : return fdmatch(tcp, tcp->u_arg[0]);
368 : :
369 : 0 : return 0;
370 : : }
|