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