/* * Copyright (c) 1991, 1992 Paul Kranenburg <pk@cs.few.eur.nl> * Copyright (c) 1993 Branko Lankester <branko@hacktic.nl> * Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs@world.std.com> * Copyright (c) 1996-1999 Wichert Akkerman <wichert@cistron.nl> * Copyright (c) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation * Linux for s390 port by D.J. Barrow * <barrow_dj@mail.yahoo.com,djbarrow@de.ibm.com> * Copyright (c) 1999-2017 The strace developers. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "defs.h" #include <sys/uio.h> #include <asm/unistd.h> #include "scno.h" #include "ptrace.h" static bool process_vm_readv_not_supported; #ifndef HAVE_PROCESS_VM_READV /* * Need to do this since process_vm_readv() is not yet available in libc. * When libc is updated, only "static bool process_vm_readv_not_supported" * line remains. * The name is different to avoid potential collision with OS headers. */ static ssize_t strace_process_vm_readv(pid_t pid, const struct iovec *lvec, unsigned long liovcnt, const struct iovec *rvec, unsigned long riovcnt, unsigned long flags) { return syscall(__NR_process_vm_readv, (long) pid, lvec, liovcnt, rvec, riovcnt, flags); } # define process_vm_readv strace_process_vm_readv #endif /* !HAVE_PROCESS_VM_READV */ static ssize_t vm_read_mem(const pid_t pid, void *const laddr, const kernel_ulong_t raddr, const size_t len) { const unsigned long truncated_raddr = raddr; #if SIZEOF_LONG < SIZEOF_KERNEL_LONG_T if (raddr != (kernel_ulong_t) truncated_raddr) { errno = EIO; return -1; } #endif const struct iovec local = { .iov_base = laddr, .iov_len = len }; const struct iovec remote = { .iov_base = (void *) truncated_raddr, .iov_len = len }; const ssize_t rc = process_vm_readv(pid, &local, 1, &remote, 1, 0); if (rc < 0 && errno == ENOSYS) process_vm_readv_not_supported = true; return rc; } static bool tracee_addr_is_invalid(kernel_ulong_t addr) { return #if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG current_wordsize < sizeof(addr) && addr & ~(kernel_ulong_t) -1U; #else false; #endif } /* legacy method of copying from tracee */ static int umoven_peekdata(const int pid, kernel_ulong_t addr, unsigned int len, void *laddr) { unsigned int nread = 0; unsigned int residue = addr & (sizeof(long) - 1); while (len) { addr &= -sizeof(long); /* aligned address */ errno = 0; union { long val; char x[sizeof(long)]; } u = { .val = ptrace(PTRACE_PEEKDATA, pid, addr, 0) }; switch (errno) { case 0: break; case ESRCH: case EINVAL: /* these could be seen if the process is gone */ return -1; case EFAULT: case EIO: case EPERM: /* address space is inaccessible */ if (nread) { perror_msg("umoven: short read (%u < %u) @0x%" PRI_klx, nread, nread + len, addr - nread); } return -1; default: /* all the rest is strange and should be reported */ perror_msg("umoven: PTRACE_PEEKDATA pid:%d @0x%" PRI_klx, pid, addr); return -1; } unsigned int m = MIN(sizeof(long) - residue, len); memcpy(laddr, &u.x[residue], m); residue = 0; addr += sizeof(long); laddr += m; nread += m; len -= m; } return 0; } /* * Copy `len' bytes of data from process `pid' * at address `addr' to our space at `our_addr'. */ int umoven(struct tcb *const tcp, kernel_ulong_t addr, unsigned int len, void *const our_addr) { if (tracee_addr_is_invalid(addr)) return -1; const int pid = tcp->pid; if (process_vm_readv_not_supported) return umoven_peekdata(pid, addr, len, our_addr); int r = vm_read_mem(pid, our_addr, addr, len); if ((unsigned int) r == len) return 0; if (r >= 0) { error_msg("umoven: short read (%u < %u) @0x%" PRI_klx, (unsigned int) r, len, addr); return -1; } switch (errno) { case ENOSYS: case EPERM: /* try PTRACE_PEEKDATA */ return umoven_peekdata(pid, addr, len, our_addr); case ESRCH: /* the process is gone */ return -1; case EFAULT: case EIO: /* address space is inaccessible */ return -1; default: /* all the rest is strange and should be reported */ perror_msg("process_vm_readv: pid:%d @0x%" PRI_klx, pid, addr); return -1; } } /* * Like umoven_peekdata but make the additional effort of looking * for a terminating zero byte. */ static int umovestr_peekdata(const int pid, kernel_ulong_t addr, unsigned int len, void *laddr) { unsigned int nread = 0; unsigned int residue = addr & (sizeof(long) - 1); void *const orig_addr = laddr; while (len) { addr &= -sizeof(long); /* aligned address */ errno = 0; union { unsigned long val; char x[sizeof(long)]; } u = { .val = ptrace(PTRACE_PEEKDATA, pid, addr, 0) }; switch (errno) { case 0: break; case ESRCH: case EINVAL: /* these could be seen if the process is gone */ return -1; case EFAULT: case EIO: case EPERM: /* address space is inaccessible */ if (nread) { perror_msg("umovestr: short read (%d < %d) @0x%" PRI_klx, nread, nread + len, addr - nread); } return -1; default: /* all the rest is strange and should be reported */ perror_msg("umovestr: PTRACE_PEEKDATA pid:%d @0x%" PRI_klx, pid, addr); return -1; } unsigned int m = MIN(sizeof(long) - residue, len); memcpy(laddr, &u.x[residue], m); while (residue < sizeof(long)) if (u.x[residue++] == '\0') return (laddr - orig_addr) + residue; residue = 0; addr += sizeof(long); laddr += m; nread += m; len -= m; } return 0; } /* * Like `umove' but make the additional effort of looking * for a terminating zero byte. * * Returns < 0 on error, strlen + 1 if NUL was seen, * else 0 if len bytes were read but no NUL byte seen. * * Note: there is no guarantee we won't overwrite some bytes * in laddr[] _after_ terminating NUL (but, of course, * we never write past laddr[len-1]). */ int umovestr(struct tcb *const tcp, kernel_ulong_t addr, unsigned int len, char *laddr) { if (tracee_addr_is_invalid(addr)) return -1; const int pid = tcp->pid; if (process_vm_readv_not_supported) return umovestr_peekdata(pid, addr, len, laddr); const size_t page_size = get_pagesize(); const size_t page_mask = page_size - 1; unsigned int nread = 0; while (len) { /* * Don't cross pages, otherwise we can get EFAULT * and fail to notice that terminating NUL lies * in the existing (first) page. */ unsigned int chunk_len = len > page_size ? page_size : len; unsigned int end_in_page = (addr + chunk_len) & page_mask; if (chunk_len > end_in_page) /* crosses to the next page */ chunk_len -= end_in_page; int r = vm_read_mem(pid, laddr, addr, chunk_len); if (r > 0) { char *nul_addr = memchr(laddr, '\0', r); if (nul_addr) return (nul_addr - laddr) + 1; addr += r; laddr += r; nread += r; len -= r; continue; } switch (errno) { case ENOSYS: case EPERM: /* try PTRACE_PEEKDATA */ if (!nread) return umovestr_peekdata(pid, addr, len, laddr); /* fall through */ case EFAULT: case EIO: /* address space is inaccessible */ if (nread) perror_msg("umovestr: short read (%d < %d) @0x%" PRI_klx, nread, nread + len, addr - nread); return -1; case ESRCH: /* the process is gone */ return -1; default: /* all the rest is strange and should be reported */ perror_msg("process_vm_readv: pid:%d @0x%" PRI_klx, pid, addr); return -1; } } return 0; }