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