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