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