Home | History | Annotate | Download | only in strace
      1 /*
      2  * Copyright (c) 2011 Comtrol Corp.
      3  * Copyright (c) 2011-2018 The strace developers.
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  *
     28  */
     29 
     30 #include "defs.h"
     31 #include <limits.h>
     32 #include <poll.h>
     33 
     34 #include "syscall.h"
     35 #include "xstring.h"
     36 
     37 struct path_set global_path_set;
     38 
     39 /*
     40  * Return true if specified path matches one that we're tracing.
     41  */
     42 static bool
     43 pathmatch(const char *path, struct path_set *set)
     44 {
     45 	unsigned i;
     46 
     47 	for (i = 0; i < set->num_selected; ++i) {
     48 		if (strcmp(path, set->paths_selected[i]) == 0)
     49 			return true;
     50 	}
     51 	return false;
     52 }
     53 
     54 /*
     55  * Return true if specified path (in user-space) matches.
     56  */
     57 static bool
     58 upathmatch(struct tcb *const tcp, const kernel_ulong_t upath,
     59 	   struct path_set *set)
     60 {
     61 	char path[PATH_MAX + 1];
     62 
     63 	return umovestr(tcp, upath, sizeof(path), path) > 0 &&
     64 		pathmatch(path, set);
     65 }
     66 
     67 /*
     68  * Return true if specified fd maps to a path we're tracing.
     69  */
     70 static bool
     71 fdmatch(struct tcb *tcp, int fd, struct path_set *set)
     72 {
     73 	char path[PATH_MAX + 1];
     74 	int n = getfdpath(tcp, fd, path, sizeof(path));
     75 
     76 	return n >= 0 && pathmatch(path, set);
     77 }
     78 
     79 /*
     80  * Add a path to the set we're tracing.
     81  * Specifying NULL will delete all paths.
     82  */
     83 static void
     84 storepath(const char *path, struct path_set *set)
     85 {
     86 	if (pathmatch(path, set))
     87 		return; /* already in table */
     88 
     89 	if (set->num_selected >= set->size)
     90 		set->paths_selected =
     91 			xgrowarray(set->paths_selected, &set->size,
     92 				   sizeof(set->paths_selected[0]));
     93 
     94 	set->paths_selected[set->num_selected++] = path;
     95 }
     96 
     97 /*
     98  * Get path associated with fd.
     99  */
    100 int
    101 getfdpath(struct tcb *tcp, int fd, char *buf, unsigned bufsize)
    102 {
    103 	char linkpath[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3];
    104 	ssize_t n;
    105 
    106 	if (fd < 0)
    107 		return -1;
    108 
    109 	xsprintf(linkpath, "/proc/%u/fd/%u", tcp->pid, fd);
    110 	n = readlink(linkpath, buf, bufsize - 1);
    111 	/*
    112 	 * NB: if buf is too small, readlink doesn't fail,
    113 	 * it returns truncated result (IOW: n == bufsize - 1).
    114 	 */
    115 	if (n >= 0)
    116 		buf[n] = '\0';
    117 	return n;
    118 }
    119 
    120 /*
    121  * Add a path to the set we're tracing.  Also add the canonicalized
    122  * version of the path.  Specifying NULL will delete all paths.
    123  */
    124 void
    125 pathtrace_select_set(const char *path, struct path_set *set)
    126 {
    127 	char *rpath;
    128 
    129 	storepath(path, set);
    130 
    131 	rpath = realpath(path, NULL);
    132 
    133 	if (rpath == NULL)
    134 		return;
    135 
    136 	/* if realpath and specified path are same, we're done */
    137 	if (strcmp(path, rpath) == 0) {
    138 		free(rpath);
    139 		return;
    140 	}
    141 
    142 	error_msg("Requested path '%s' resolved into '%s'", path, rpath);
    143 	storepath(rpath, set);
    144 }
    145 
    146 static bool
    147 match_xselect_args(struct tcb *tcp, const kernel_ulong_t *args,
    148 		   struct path_set *set)
    149 {
    150 	/* Kernel truncates arg[0] to int, we do the same. */
    151 	int nfds = (int) args[0];
    152 	/* Kernel rejects negative nfds, so we don't parse it either. */
    153 	if (nfds <= 0)
    154 		return false;
    155 	/* Beware of select(2^31-1, NULL, NULL, NULL) and similar... */
    156 	if (nfds > 1024*1024)
    157 		nfds = 1024*1024;
    158 	unsigned int fdsize = (((nfds + 7) / 8) + current_wordsize-1) & -current_wordsize;
    159 	fd_set *fds = xmalloc(fdsize);
    160 
    161 	for (unsigned int i = 1; i <= 3; ++i) {
    162 		if (args[i] == 0)
    163 			continue;
    164 		if (umoven(tcp, args[i], fdsize, fds) < 0)
    165 			continue;
    166 		for (int j = 0;; ++j) {
    167 			j = next_set_bit(fds, j, nfds);
    168 			if (j < 0)
    169 				break;
    170 			if (fdmatch(tcp, j, set)) {
    171 				free(fds);
    172 				return true;
    173 			}
    174 		}
    175 	}
    176 
    177 	free(fds);
    178 	return false;
    179 }
    180 
    181 /*
    182  * Return true if syscall accesses a selected path
    183  * (or if no paths have been specified for tracing).
    184  */
    185 bool
    186 pathtrace_match_set(struct tcb *tcp, struct path_set *set)
    187 {
    188 	const struct_sysent *s;
    189 
    190 	s = tcp->s_ent;
    191 
    192 	if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
    193 		return false;
    194 
    195 	/*
    196 	 * Check for special cases where we need to do something
    197 	 * other than test arg[0].
    198 	 */
    199 
    200 	switch (s->sen) {
    201 	case SEN_dup2:
    202 	case SEN_dup3:
    203 	case SEN_kexec_file_load:
    204 	case SEN_sendfile:
    205 	case SEN_sendfile64:
    206 	case SEN_tee:
    207 		/* fd, fd */
    208 		return fdmatch(tcp, tcp->u_arg[0], set) ||
    209 			fdmatch(tcp, tcp->u_arg[1], set);
    210 
    211 	case SEN_execveat:
    212 	case SEN_faccessat:
    213 	case SEN_fchmodat:
    214 	case SEN_fchownat:
    215 	case SEN_fstatat64:
    216 	case SEN_futimesat:
    217 	case SEN_inotify_add_watch:
    218 	case SEN_mkdirat:
    219 	case SEN_mknodat:
    220 	case SEN_name_to_handle_at:
    221 	case SEN_newfstatat:
    222 	case SEN_openat:
    223 	case SEN_readlinkat:
    224 	case SEN_statx:
    225 	case SEN_unlinkat:
    226 	case SEN_utimensat:
    227 		/* fd, path */
    228 		return fdmatch(tcp, tcp->u_arg[0], set) ||
    229 			upathmatch(tcp, tcp->u_arg[1], set);
    230 
    231 	case SEN_link:
    232 	case SEN_mount:
    233 	case SEN_pivotroot:
    234 		/* path, path */
    235 		return upathmatch(tcp, tcp->u_arg[0], set) ||
    236 			upathmatch(tcp, tcp->u_arg[1], set);
    237 
    238 	case SEN_quotactl:
    239 	case SEN_symlink:
    240 		/* x, path */
    241 		return upathmatch(tcp, tcp->u_arg[1], set);
    242 
    243 	case SEN_linkat:
    244 	case SEN_renameat2:
    245 	case SEN_renameat:
    246 		/* fd, path, fd, path */
    247 		return fdmatch(tcp, tcp->u_arg[0], set) ||
    248 			fdmatch(tcp, tcp->u_arg[2], set) ||
    249 			upathmatch(tcp, tcp->u_arg[1], set) ||
    250 			upathmatch(tcp, tcp->u_arg[3], set);
    251 
    252 #if HAVE_ARCH_OLD_MMAP
    253 	case SEN_old_mmap:
    254 # if HAVE_ARCH_OLD_MMAP_PGOFF
    255 	case SEN_old_mmap_pgoff:
    256 # endif
    257 	{
    258 		kernel_ulong_t *args =
    259 			fetch_indirect_syscall_args(tcp, tcp->u_arg[0], 6);
    260 
    261 		return args && fdmatch(tcp, args[4], set);
    262 	}
    263 #endif /* HAVE_ARCH_OLD_MMAP */
    264 
    265 	case SEN_mmap:
    266 	case SEN_mmap_4koff:
    267 	case SEN_mmap_pgoff:
    268 	case SEN_ARCH_mmap:
    269 		/* x, x, x, x, fd */
    270 		return fdmatch(tcp, tcp->u_arg[4], set);
    271 
    272 	case SEN_symlinkat:
    273 		/* x, fd, path */
    274 		return fdmatch(tcp, tcp->u_arg[1], set) ||
    275 			upathmatch(tcp, tcp->u_arg[2], set);
    276 
    277 	case SEN_copy_file_range:
    278 	case SEN_splice:
    279 		/* fd, x, fd, x, x, x */
    280 		return fdmatch(tcp, tcp->u_arg[0], set) ||
    281 			fdmatch(tcp, tcp->u_arg[2], set);
    282 
    283 	case SEN_epoll_ctl:
    284 		/* x, x, fd, x */
    285 		return fdmatch(tcp, tcp->u_arg[2], set);
    286 
    287 
    288 	case SEN_fanotify_mark:
    289 	{
    290 		/* x, x, mask (64 bit), fd, path */
    291 		unsigned long long mask = 0;
    292 		int argn = getllval(tcp, &mask, 2);
    293 		return fdmatch(tcp, tcp->u_arg[argn], set) ||
    294 			upathmatch(tcp, tcp->u_arg[argn + 1], set);
    295 	}
    296 #if HAVE_ARCH_OLD_SELECT
    297 	case SEN_oldselect:
    298 	{
    299 		kernel_ulong_t *args =
    300 			fetch_indirect_syscall_args(tcp, tcp->u_arg[0], 5);
    301 
    302 		return args && match_xselect_args(tcp, args, set);
    303 	}
    304 #endif
    305 	case SEN_pselect6:
    306 	case SEN_select:
    307 		return match_xselect_args(tcp, tcp->u_arg, set);
    308 	case SEN_poll:
    309 	case SEN_ppoll:
    310 	{
    311 		struct pollfd fds;
    312 		unsigned nfds;
    313 		kernel_ulong_t start, cur, end;
    314 
    315 		start = tcp->u_arg[0];
    316 		nfds = tcp->u_arg[1];
    317 
    318 		if (nfds > 1024 * 1024)
    319 			nfds = 1024 * 1024;
    320 		end = start + sizeof(fds) * nfds;
    321 
    322 		if (nfds == 0 || end < start)
    323 			return false;
    324 
    325 		for (cur = start; cur < end; cur += sizeof(fds)) {
    326 			if (umove(tcp, cur, &fds))
    327 				break;
    328 			if (fdmatch(tcp, fds.fd, set))
    329 				return true;
    330 		}
    331 
    332 		return false;
    333 	}
    334 
    335 	case SEN_accept4:
    336 	case SEN_accept:
    337 	case SEN_bpf:
    338 	case SEN_epoll_create:
    339 	case SEN_epoll_create1:
    340 	case SEN_eventfd2:
    341 	case SEN_eventfd:
    342 	case SEN_fanotify_init:
    343 	case SEN_inotify_init:
    344 	case SEN_inotify_init1:
    345 	case SEN_memfd_create:
    346 	case SEN_mq_getsetattr:
    347 	case SEN_mq_notify:
    348 	case SEN_mq_open:
    349 	case SEN_mq_timedreceive:
    350 	case SEN_mq_timedsend:
    351 	case SEN_perf_event_open:
    352 	case SEN_pipe:
    353 	case SEN_pipe2:
    354 	case SEN_printargs:
    355 	case SEN_signalfd4:
    356 	case SEN_signalfd:
    357 	case SEN_socket:
    358 	case SEN_socketpair:
    359 	case SEN_timerfd_create:
    360 	case SEN_timerfd_gettime:
    361 	case SEN_timerfd_settime:
    362 	case SEN_userfaultfd:
    363 		/*
    364 		 * These have TRACE_FILE or TRACE_DESCRIPTOR or TRACE_NETWORK set,
    365 		 * but they don't have any file descriptor or path args to test.
    366 		 */
    367 		return false;
    368 	}
    369 
    370 	/*
    371 	 * Our fallback position for calls that haven't already
    372 	 * been handled is to just check arg[0].
    373 	 */
    374 
    375 	if (s->sys_flags & TRACE_FILE)
    376 		return upathmatch(tcp, tcp->u_arg[0], set);
    377 
    378 	if (s->sys_flags & (TRACE_DESC | TRACE_NETWORK))
    379 		return fdmatch(tcp, tcp->u_arg[0], set);
    380 
    381 	return false;
    382 }
    383