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 35const char **paths_selected = NULL; 36static unsigned num_selected = 0; 37 38/* 39 * Return true if specified path matches one that we're tracing. 40 */ 41static int 42pathmatch(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 */ 56static int 57upathmatch(struct tcb *tcp, unsigned long 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 */ 68static int 69fdmatch(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 */ 81static void 82storepath(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 */ 98int 99getfdpath(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 */ 122void 123pathtrace_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 */ 148int 149pathtrace_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_futimesat: 178 case SEN_inotify_add_watch: 179 case SEN_mkdirat: 180 case SEN_mknodat: 181 case SEN_name_to_handle_at: 182 case SEN_newfstatat: 183 case SEN_openat: 184 case SEN_pipe2: 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_splice: 230 /* fd, x, fd, x, x */ 231 return fdmatch(tcp, tcp->u_arg[0]) || 232 fdmatch(tcp, tcp->u_arg[2]); 233 234 case SEN_epoll_ctl: 235 /* x, x, fd, x */ 236 return fdmatch(tcp, tcp->u_arg[2]); 237 238 239 case SEN_fanotify_mark: 240 /* x, x, x, fd, path */ 241 return fdmatch(tcp, tcp->u_arg[3]) || 242 upathmatch(tcp, tcp->u_arg[4]); 243 244 case SEN_oldselect: 245 case SEN_pselect6: 246 case SEN_select: 247 { 248 int i, j; 249 int nfds; 250 long *args, oldargs[5]; 251 unsigned fdsize; 252 fd_set *fds; 253 254 args = tcp->u_arg; 255 if (SEN_oldselect == s->sen) { 256 if (umoven(tcp, tcp->u_arg[0], sizeof oldargs, 257 oldargs) < 0) 258 { 259 error_msg("umoven() failed"); 260 return 0; 261 } 262 args = oldargs; 263 } 264 265 /* Kernel truncates arg[0] to int, we do the same. */ 266 nfds = (int) args[0]; 267 /* Kernel rejects negative nfds, so we don't parse it either. */ 268 if (nfds <= 0) 269 return 0; 270 /* Beware of select(2^31-1, NULL, NULL, NULL) and similar... */ 271 if (nfds > 1024*1024) 272 nfds = 1024*1024; 273 fdsize = (((nfds + 7) / 8) + current_wordsize-1) & -current_wordsize; 274 fds = xmalloc(fdsize); 275 276 for (i = 1; i <= 3; ++i) { 277 if (args[i] == 0) 278 continue; 279 if (umoven(tcp, args[i], fdsize, fds) < 0) { 280 error_msg("umoven() failed"); 281 continue; 282 } 283 for (j = 0;; j++) { 284 j = next_set_bit(fds, j, nfds); 285 if (j < 0) 286 break; 287 if (fdmatch(tcp, j)) { 288 free(fds); 289 return 1; 290 } 291 } 292 } 293 free(fds); 294 return 0; 295 } 296 297 case SEN_poll: 298 case SEN_ppoll: 299 { 300 struct pollfd fds; 301 unsigned nfds; 302 unsigned long start, cur, end; 303 304 start = tcp->u_arg[0]; 305 nfds = tcp->u_arg[1]; 306 307 end = start + sizeof(fds) * nfds; 308 309 if (nfds == 0 || end < start) 310 return 0; 311 312 for (cur = start; cur < end; cur += sizeof(fds)) 313 if ((umoven(tcp, cur, sizeof fds, &fds) == 0) 314 && fdmatch(tcp, fds.fd)) 315 return 1; 316 317 return 0; 318 } 319 320 case SEN_bpf: 321 case SEN_epoll_create: 322 case SEN_epoll_create1: 323 case SEN_eventfd2: 324 case SEN_eventfd: 325 case SEN_fanotify_init: 326 case SEN_inotify_init1: 327 case SEN_memfd_create: 328 case SEN_perf_event_open: 329 case SEN_pipe: 330 case SEN_printargs: 331 case SEN_socket: 332 case SEN_socketpair: 333 case SEN_timerfd_create: 334 case SEN_timerfd_gettime: 335 case SEN_timerfd_settime: 336 case SEN_userfaultfd: 337 /* 338 * These have TRACE_FILE or TRACE_DESCRIPTOR or TRACE_NETWORK set, 339 * but they don't have any file descriptor or path args to test. 340 */ 341 return 0; 342 } 343 344 /* 345 * Our fallback position for calls that haven't already 346 * been handled is to just check arg[0]. 347 */ 348 349 if (s->sys_flags & TRACE_FILE) 350 return upathmatch(tcp, tcp->u_arg[0]); 351 352 if (s->sys_flags & (TRACE_DESC | TRACE_NETWORK)) 353 return fdmatch(tcp, tcp->u_arg[0]); 354 355 return 0; 356} 357