pathtrace.c revision 79a79ea851250a30005d6323b20a9e30d9f336c1
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 31#include <ctype.h> 32#include <sys/param.h> 33 34#ifdef HAVE_POLL_H 35#include <poll.h> 36#endif 37#ifdef HAVE_SYS_POLL_H 38#include <sys/poll.h> 39#endif 40 41#include "syscall.h" 42 43#define MAXSELECTED 256 /* max number of "selected" paths */ 44static const char *selected[MAXSELECTED]; /* paths selected for tracing */ 45 46/* 47 * Return true if specified path matches one that we're tracing. 48 */ 49static int 50pathmatch(const char *path) 51{ 52 unsigned int i; 53 54 for (i = 0; i < ARRAY_SIZE(selected); ++i) { 55 if (selected[i] == NULL) 56 return 0; 57 if (!strcmp(path, selected[i])) 58 return 1; 59 } 60 return 0; 61} 62 63/* 64 * Return true if specified path (in user-space) matches. 65 */ 66static int 67upathmatch(struct tcb *tcp, unsigned long upath) 68{ 69 char path[PATH_MAX + 1]; 70 71 return umovestr(tcp, upath, sizeof path, path) == 0 && 72 pathmatch(path); 73} 74 75/* 76 * Return true if specified fd maps to a path we're tracing. 77 */ 78static int 79fdmatch(struct tcb *tcp, int fd) 80{ 81 const char *path = getfdpath(tcp, fd); 82 83 return path && pathmatch(path); 84} 85 86/* 87 * Add a path to the set we're tracing. 88 * Secifying NULL will delete all paths. 89 */ 90static int 91storepath(const char *path) 92{ 93 unsigned int i; 94 95 if (path == NULL) { 96 for (i = 0; i < ARRAY_SIZE(selected); ++i) 97 if (selected[i]) { 98 free((char *) selected[i]); 99 selected[i] = NULL; 100 } 101 return 0; 102 } 103 104 for (i = 0; i < ARRAY_SIZE(selected); ++i) 105 if (!selected[i]) { 106 selected[i] = path; 107 return 0; 108 } 109 110 fprintf(stderr, "Max trace paths exceeded, only using first %u\n", 111 (unsigned int) ARRAY_SIZE(selected)); 112 return -1; 113} 114 115/* 116 * Get path associated with fd. 117 */ 118const char *getfdpath(struct tcb *tcp, int fd) 119{ 120#ifdef LINUX 121 static char path[PATH_MAX+1]; 122 char linkpath[64]; 123 ssize_t n; 124 125 if (fd < 0) 126 return NULL; 127 128 snprintf(linkpath, sizeof linkpath, "/proc/%d/fd/%d", tcp->pid, fd); 129 n = readlink(linkpath, path, (sizeof path) - 1); 130 if (n <= 0) 131 return NULL; 132 path[n] = '\0'; 133 return path; 134#else 135 return NULL; 136#endif 137} 138 139/* 140 * Add a path to the set we're tracing. Also add the canonicalized 141 * version of the path. Secifying NULL will delete all paths. 142 */ 143int 144pathtrace_select(const char *path) 145{ 146 char *rpath; 147 148 if (path == NULL) 149 return storepath(path); 150 151 if (storepath(path)) 152 return -1; 153 154 rpath = realpath(path, NULL); 155 156 if (rpath == NULL) 157 return 0; 158 159 /* if realpath and specified path are same, we're done */ 160 if (!strcmp(path, rpath)) { 161 free(rpath); 162 return 0; 163 } 164 165 fprintf(stderr, "Requested path '%s' resolved into '%s'\n", 166 path, rpath); 167 return storepath(rpath); 168} 169 170/* 171 * Return true if syscall accesses a selected path 172 * (or if no paths have been specified for tracing). 173 */ 174int 175pathtrace_match(struct tcb *tcp) 176{ 177 const struct sysent *s; 178 179 if (selected[0] == NULL) 180 return 1; 181 182 s = &sysent[tcp->scno]; 183 184 if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC))) 185 return 0; 186 187 /* 188 * Check for special cases where we need to do something 189 * other than test arg[0]. 190 */ 191 192#ifdef LINUX 193 194 if (s->sys_func == sys_dup2 || 195 s->sys_func == sys_dup3 || 196 s->sys_func == sys_sendfile || 197 s->sys_func == sys_sendfile64 || 198 !strcmp(s->sys_name, "tee")) 199 { 200 /* fd, fd */ 201 return fdmatch(tcp, tcp->u_arg[0]) || 202 fdmatch(tcp, tcp->u_arg[1]); 203 } 204 205 if (s->sys_func == sys_inotify_add_watch || 206 s->sys_func == sys_faccessat || 207 s->sys_func == sys_fchmodat || 208 s->sys_func == sys_futimesat || 209 s->sys_func == sys_mkdirat || 210 s->sys_func == sys_unlinkat || 211 s->sys_func == sys_newfstatat || 212 s->sys_func == sys_mknodat || 213 s->sys_func == sys_openat || 214 s->sys_func == sys_readlinkat || 215 s->sys_func == sys_utimensat || 216 s->sys_func == sys_fchownat || 217 s->sys_func == sys_pipe2) 218 { 219 /* fd, path */ 220 return fdmatch(tcp, tcp->u_arg[0]) || 221 upathmatch(tcp, tcp->u_arg[1]); 222 } 223 224 if (s->sys_func == sys_link || 225 s->sys_func == sys_pivotroot || 226 s->sys_func == sys_rename || 227 s->sys_func == sys_symlink || 228 s->sys_func == sys_mount) 229 { 230 /* path, path */ 231 return upathmatch(tcp, tcp->u_arg[0]) || 232 upathmatch(tcp, tcp->u_arg[1]); 233 } 234 235 if (s->sys_func == sys_renameat || 236 s->sys_func == sys_linkat) 237 { 238 /* fd, path, fd, path */ 239 return fdmatch(tcp, tcp->u_arg[0]) || 240 fdmatch(tcp, tcp->u_arg[2]) || 241 upathmatch(tcp, tcp->u_arg[1]) || 242 upathmatch(tcp, tcp->u_arg[3]); 243 } 244 245 if (s->sys_func == sys_old_mmap || s->sys_func == sys_mmap) { 246 /* x, x, x, x, fd */ 247 return fdmatch(tcp, tcp->u_arg[4]); 248 } 249 250 if (s->sys_func == sys_symlinkat) { 251 /* path, fd, path */ 252 return fdmatch(tcp, tcp->u_arg[1]) || 253 upathmatch(tcp, tcp->u_arg[0]) || 254 upathmatch(tcp, tcp->u_arg[2]); 255 } 256 257 if (!strcmp(s->sys_name, "splice")) { 258 /* fd, x, fd, x, x */ 259 return fdmatch(tcp, tcp->u_arg[0]) || 260 fdmatch(tcp, tcp->u_arg[2]); 261 } 262 263 if (s->sys_func == sys_epoll_ctl) { 264 /* x, x, fd, x */ 265 return fdmatch(tcp, tcp->u_arg[2]); 266 } 267 268 if (s->sys_func == sys_select || 269 s->sys_func == sys_oldselect || 270 s->sys_func == sys_pselect6) 271 { 272 int i, j; 273 unsigned nfds; 274 long *args, oldargs[5]; 275 unsigned fdsize; 276 fd_set *fds; 277 278 if (s->sys_func == sys_oldselect) { 279 if (umoven(tcp, tcp->u_arg[0], sizeof oldargs, 280 (char*) oldargs) < 0) 281 { 282 fprintf(stderr, "umoven() failed\n"); 283 return 0; 284 } 285 args = oldargs; 286 } else 287 args = tcp->u_arg; 288 289 nfds = args[0]; 290 /* Beware of select(2^31-1, NULL, NULL, NULL) and similar... */ 291 if (args[0] > 1024*1024) 292 nfds = 1024*1024; 293 if (args[0] < 0) 294 nfds = 0; 295 fdsize = ((((nfds + 7) / 8) + sizeof(long) - 1) 296 & -sizeof(long)); 297 fds = malloc(fdsize); 298 if (!fds) 299 die_out_of_memory(); 300 301 for (i = 1; i <= 3; ++i) { 302 if (args[i] == 0) 303 continue; 304 305 if (umoven(tcp, args[i], fdsize, (char *) fds) < 0) { 306 fprintf(stderr, "umoven() failed\n"); 307 continue; 308 } 309 310 for (j = 0; j < nfds; ++j) 311 if (FD_ISSET(j, fds) && fdmatch(tcp, j)) { 312 free(fds); 313 return 1; 314 } 315 } 316 free(fds); 317 return 0; 318 } 319 320 if (s->sys_func == sys_poll || 321 s->sys_func == sys_ppoll) 322 { 323 struct pollfd fds; 324 unsigned nfds; 325 unsigned long start, cur, end; 326 327 start = tcp->u_arg[0]; 328 nfds = tcp->u_arg[1]; 329 330 end = start + sizeof(fds) * nfds; 331 332 if (nfds == 0 || end < start) 333 return 0; 334 335 for (cur = start; cur < end; cur += sizeof(fds)) 336 if ((umoven(tcp, cur, sizeof fds, (char *) &fds) == 0) 337 && fdmatch(tcp, fds.fd)) 338 return 1; 339 340 return 0; 341 } 342 343 if (s->sys_func == printargs || 344 s->sys_func == sys_pipe || 345 s->sys_func == sys_pipe2 || 346 s->sys_func == sys_eventfd2 || 347 s->sys_func == sys_eventfd || 348 s->sys_func == sys_inotify_init1 || 349 s->sys_func == sys_timerfd_create || 350 s->sys_func == sys_timerfd_settime || 351 s->sys_func == sys_timerfd_gettime || 352 s->sys_func == sys_epoll_create || 353 !strcmp(s->sys_name, "fanotify_init")) 354 { 355 /* 356 * These have TRACE_FILE or TRACE_DESCRIPTOR set, but they 357 * don't have any file descriptor or path args to test. 358 */ 359 return 0; 360 } 361#else 362#warning "path tracing only using arg[0]" 363#endif 364 365 /* 366 * Our fallback position for calls that haven't already 367 * been handled is to just check arg[0]. 368 */ 369 370 if (s->sys_flags & TRACE_FILE) 371 return upathmatch(tcp, tcp->u_arg[0]); 372 373 if (s->sys_flags & TRACE_DESC) 374 return fdmatch(tcp, tcp->u_arg[0]); 375 376 return 0; 377} 378