Home | History | Annotate | Download | only in strace
      1 /*
      2  * Copyright (c) 1991, 1992 Paul Kranenburg <pk (at) cs.few.eur.nl>
      3  * Copyright (c) 1993 Branko Lankester <branko (at) hacktic.nl>
      4  * Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs (at) world.std.com>
      5  * Copyright (c) 1996-1999 Wichert Akkerman <wichert (at) cistron.nl>
      6  * Copyright (c) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
      7  *                     Linux for s390 port by D.J. Barrow
      8  *                    <barrow_dj (at) mail.yahoo.com,djbarrow (at) de.ibm.com>
      9  * Copyright (c) 1999-2018 The strace developers.
     10  * All rights reserved.
     11  *
     12  * Redistribution and use in source and binary forms, with or without
     13  * modification, are permitted provided that the following conditions
     14  * are met:
     15  * 1. Redistributions of source code must retain the above copyright
     16  *    notice, this list of conditions and the following disclaimer.
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  * 3. The name of the author may not be used to endorse or promote products
     21  *    derived from this software without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     33  */
     34 
     35 #include "defs.h"
     36 #include <sys/uio.h>
     37 #include <asm/unistd.h>
     38 
     39 #include "scno.h"
     40 #include "ptrace.h"
     41 
     42 static bool process_vm_readv_not_supported;
     43 
     44 #ifndef HAVE_PROCESS_VM_READV
     45 /*
     46  * Need to do this since process_vm_readv() is not yet available in libc.
     47  * When libc is updated, only "static bool process_vm_readv_not_supported"
     48  * line remains.
     49  * The name is different to avoid potential collision with OS headers.
     50  */
     51 static ssize_t strace_process_vm_readv(pid_t pid,
     52 		 const struct iovec *lvec,
     53 		 unsigned long liovcnt,
     54 		 const struct iovec *rvec,
     55 		 unsigned long riovcnt,
     56 		 unsigned long flags)
     57 {
     58 	return syscall(__NR_process_vm_readv,
     59 		       (long) pid, lvec, liovcnt, rvec, riovcnt, flags);
     60 }
     61 # define process_vm_readv strace_process_vm_readv
     62 #endif /* !HAVE_PROCESS_VM_READV */
     63 
     64 static ssize_t
     65 vm_read_mem(const pid_t pid, void *const laddr,
     66 	    const kernel_ulong_t raddr, const size_t len)
     67 {
     68 	const unsigned long truncated_raddr = raddr;
     69 
     70 #if SIZEOF_LONG < SIZEOF_KERNEL_LONG_T
     71 	if (raddr != (kernel_ulong_t) truncated_raddr) {
     72 		errno = EIO;
     73 		return -1;
     74 	}
     75 #endif
     76 
     77 	const struct iovec local = {
     78 		.iov_base = laddr,
     79 		.iov_len = len
     80 	};
     81 	const struct iovec remote = {
     82 		.iov_base = (void *) truncated_raddr,
     83 		.iov_len = len
     84 	};
     85 
     86 	const ssize_t rc = process_vm_readv(pid, &local, 1, &remote, 1, 0);
     87 	if (rc < 0 && errno == ENOSYS)
     88 		process_vm_readv_not_supported = true;
     89 
     90 	return rc;
     91 }
     92 
     93 static bool
     94 tracee_addr_is_invalid(kernel_ulong_t addr)
     95 {
     96 	return
     97 #if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
     98 		current_wordsize < sizeof(addr) && addr & ~(kernel_ulong_t) -1U;
     99 #else
    100 		false;
    101 #endif
    102 }
    103 
    104 /* legacy method of copying from tracee */
    105 static int
    106 umoven_peekdata(const int pid, kernel_ulong_t addr, unsigned int len,
    107 		void *laddr)
    108 {
    109 	unsigned int nread = 0;
    110 	unsigned int residue = addr & (sizeof(long) - 1);
    111 
    112 	while (len) {
    113 		addr &= -sizeof(long);		/* aligned address */
    114 
    115 		errno = 0;
    116 		union {
    117 			long val;
    118 			char x[sizeof(long)];
    119 		} u = { .val = ptrace(PTRACE_PEEKDATA, pid, addr, 0) };
    120 
    121 		switch (errno) {
    122 			case 0:
    123 				break;
    124 			case ESRCH: case EINVAL:
    125 				/* these could be seen if the process is gone */
    126 				return -1;
    127 			case EFAULT: case EIO: case EPERM:
    128 				/* address space is inaccessible */
    129 				if (nread) {
    130 					perror_msg("umoven: short read (%u < %u) @0x%" PRI_klx,
    131 						   nread, nread + len, addr - nread);
    132 				}
    133 				return -1;
    134 			default:
    135 				/* all the rest is strange and should be reported */
    136 				perror_msg("umoven: PTRACE_PEEKDATA pid:%d @0x%" PRI_klx,
    137 					    pid, addr);
    138 				return -1;
    139 		}
    140 
    141 		unsigned int m = MIN(sizeof(long) - residue, len);
    142 		memcpy(laddr, &u.x[residue], m);
    143 		residue = 0;
    144 		addr += sizeof(long);
    145 		laddr += m;
    146 		nread += m;
    147 		len -= m;
    148 	}
    149 
    150 	return 0;
    151 }
    152 
    153 /*
    154  * Copy `len' bytes of data from process `pid'
    155  * at address `addr' to our space at `our_addr'.
    156  */
    157 int
    158 umoven(struct tcb *const tcp, kernel_ulong_t addr, unsigned int len,
    159        void *const our_addr)
    160 {
    161 	if (tracee_addr_is_invalid(addr))
    162 		return -1;
    163 
    164 	const int pid = tcp->pid;
    165 
    166 	if (process_vm_readv_not_supported)
    167 		return umoven_peekdata(pid, addr, len, our_addr);
    168 
    169 	int r = vm_read_mem(pid, our_addr, addr, len);
    170 	if ((unsigned int) r == len)
    171 		return 0;
    172 	if (r >= 0) {
    173 		error_msg("umoven: short read (%u < %u) @0x%" PRI_klx,
    174 			  (unsigned int) r, len, addr);
    175 		return -1;
    176 	}
    177 	switch (errno) {
    178 		case ENOSYS:
    179 		case EPERM:
    180 			/* try PTRACE_PEEKDATA */
    181 			return umoven_peekdata(pid, addr, len, our_addr);
    182 		case ESRCH:
    183 			/* the process is gone */
    184 			return -1;
    185 		case EFAULT: case EIO:
    186 			/* address space is inaccessible */
    187 			return -1;
    188 		default:
    189 			/* all the rest is strange and should be reported */
    190 			perror_msg("process_vm_readv: pid:%d @0x%" PRI_klx,
    191 				    pid, addr);
    192 			return -1;
    193 	}
    194 }
    195 
    196 /*
    197  * Like umoven_peekdata but make the additional effort of looking
    198  * for a terminating zero byte.
    199  */
    200 static int
    201 umovestr_peekdata(const int pid, kernel_ulong_t addr, unsigned int len,
    202 		  void *laddr)
    203 {
    204 	unsigned int nread = 0;
    205 	unsigned int residue = addr & (sizeof(long) - 1);
    206 	void *const orig_addr = laddr;
    207 
    208 	while (len) {
    209 		addr &= -sizeof(long);		/* aligned address */
    210 
    211 		errno = 0;
    212 		union {
    213 			unsigned long val;
    214 			char x[sizeof(long)];
    215 		} u = { .val = ptrace(PTRACE_PEEKDATA, pid, addr, 0) };
    216 
    217 		switch (errno) {
    218 			case 0:
    219 				break;
    220 			case ESRCH: case EINVAL:
    221 				/* these could be seen if the process is gone */
    222 				return -1;
    223 			case EFAULT: case EIO: case EPERM:
    224 				/* address space is inaccessible */
    225 				if (nread) {
    226 					perror_msg("umovestr: short read (%d < %d) @0x%" PRI_klx,
    227 						   nread, nread + len, addr - nread);
    228 				}
    229 				return -1;
    230 			default:
    231 				/* all the rest is strange and should be reported */
    232 				perror_msg("umovestr: PTRACE_PEEKDATA pid:%d @0x%" PRI_klx,
    233 					   pid, addr);
    234 				return -1;
    235 		}
    236 
    237 		unsigned int m = MIN(sizeof(long) - residue, len);
    238 		memcpy(laddr, &u.x[residue], m);
    239 		while (residue < sizeof(long))
    240 			if (u.x[residue++] == '\0')
    241 				return (laddr - orig_addr) + residue;
    242 		residue = 0;
    243 		addr += sizeof(long);
    244 		laddr += m;
    245 		nread += m;
    246 		len -= m;
    247 	}
    248 
    249 	return 0;
    250 }
    251 
    252 /*
    253  * Like `umove' but make the additional effort of looking
    254  * for a terminating zero byte.
    255  *
    256  * Returns < 0 on error, strlen + 1  if NUL was seen,
    257  * else 0 if len bytes were read but no NUL byte seen.
    258  *
    259  * Note: there is no guarantee we won't overwrite some bytes
    260  * in laddr[] _after_ terminating NUL (but, of course,
    261  * we never write past laddr[len-1]).
    262  */
    263 int
    264 umovestr(struct tcb *const tcp, kernel_ulong_t addr, unsigned int len,
    265 	 char *laddr)
    266 {
    267 	if (tracee_addr_is_invalid(addr))
    268 		return -1;
    269 
    270 	const int pid = tcp->pid;
    271 
    272 	if (process_vm_readv_not_supported)
    273 		return umovestr_peekdata(pid, addr, len, laddr);
    274 
    275 	const size_t page_size = get_pagesize();
    276 	const size_t page_mask = page_size - 1;
    277 	unsigned int nread = 0;
    278 
    279 	while (len) {
    280 		/*
    281 		 * Don't cross pages, otherwise we can get EFAULT
    282 		 * and fail to notice that terminating NUL lies
    283 		 * in the existing (first) page.
    284 		 */
    285 		unsigned int chunk_len = len > page_size ? page_size : len;
    286 		unsigned int end_in_page = (addr + chunk_len) & page_mask;
    287 		if (chunk_len > end_in_page) /* crosses to the next page */
    288 			chunk_len -= end_in_page;
    289 
    290 		int r = vm_read_mem(pid, laddr, addr, chunk_len);
    291 		if (r > 0) {
    292 			char *nul_addr = memchr(laddr, '\0', r);
    293 
    294 			if (nul_addr)
    295 				return (nul_addr - laddr) + 1;
    296 			addr += r;
    297 			laddr += r;
    298 			nread += r;
    299 			len -= r;
    300 			continue;
    301 		}
    302 		switch (errno) {
    303 			case ENOSYS:
    304 			case EPERM:
    305 				/* try PTRACE_PEEKDATA */
    306 				if (!nread)
    307 					return umovestr_peekdata(pid, addr,
    308 								 len, laddr);
    309 				ATTRIBUTE_FALLTHROUGH;
    310 			case EFAULT: case EIO:
    311 				/* address space is inaccessible */
    312 				if (nread)
    313 					perror_msg("umovestr: short read (%d < %d) @0x%" PRI_klx,
    314 						   nread, nread + len, addr - nread);
    315 				return -1;
    316 			case ESRCH:
    317 				/* the process is gone */
    318 				return -1;
    319 			default:
    320 				/* all the rest is strange and should be reported */
    321 				perror_msg("process_vm_readv: pid:%d @0x%" PRI_klx,
    322 					    pid, addr);
    323 				return -1;
    324 		}
    325 	}
    326 
    327 	return 0;
    328 }
    329