* configure.ac: check for process_vm_writev's availability. * defs.h: Add declaration for upoken. * luajit.h (func_upoke): New wrapper function. (init_luajit): Expose it as strace.C.upoke. * luajit_lib.lua (write_obj): New function. * strace.1 (LUA SCRIPTING): Describe new functions. * ucopy.c (process_vm_writev_not_supported): New global flag. (strace_process_vm_writev): New function. (vm_write_mem): Likewise. (partial_poke): Likewise. (upoken_peekpoke): Likewise. (upoken): Likewise. --- configure.ac | 1 + defs.h | 3 + luajit.h | 8 +++ luajit_lib.lua | 4 ++ strace.1.in | 30 ++++++++++ ucopy.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 223 insertions(+)
diff --git a/configure.ac b/configure.ac index 2c166403..f1b81aeb 100644 --- a/configure.ac +++ b/configure.ac @@ -282,6 +282,7 @@ AC_CHECK_FUNCS(m4_normalize([ prctl preadv process_vm_readv + process_vm_writev pwritev readahead signalfd diff --git a/defs.h b/defs.h index 8707edb2..367d5684 100644 --- a/defs.h +++ b/defs.h @@ -429,6 +429,9 @@ umovestr(struct tcb *, kernel_ulong_t addr, unsigned int len, char *laddr); extern int upeek(int pid, unsigned long, kernel_ulong_t *); extern int upoke(int pid, unsigned long, kernel_ulong_t); +extern int upoken(struct tcb *, kernel_ulong_t addr, unsigned int len, + const void *laddr); + extern bool print_array(struct tcb *, kernel_ulong_t start_addr, diff --git a/luajit.h b/luajit.h index a707446f..f01505ec 100644 --- a/luajit.h +++ b/luajit.h @@ -95,6 +95,12 @@ func_umove_str(kernel_ulong_t addr, size_t len, char *laddr) return current_tcp ? umovestr(current_tcp, addr, len, laddr) : -1; } +static int +func_upoke(kernel_ulong_t addr, size_t len, const void *laddr) +{ + return current_tcp ? upoken(current_tcp, addr, len, laddr) : -1; +} + static bool func_path_match(const char **set, size_t nset) { @@ -267,6 +273,8 @@ init_luajit(const char *scriptfile) kernel_ulong_t, size_t, void *); EXPOSE_FUNC(int, func_umove_str, "umove_str", kernel_ulong_t, size_t, char *); + EXPOSE_FUNC(int, func_upoke, "upoke", + kernel_ulong_t, size_t, const void *); EXPOSE_FUNC(bool, func_path_match, "path_match", const char **, size_t); diff --git a/luajit_lib.lua b/luajit_lib.lua index 188e818b..fe344257 100644 --- a/luajit_lib.lua +++ b/luajit_lib.lua @@ -326,6 +326,10 @@ function strace.read_path(addr) return strace.read_str(addr, strace.path_max - 1, strace.path_max) end +function strace.write_obj(addr, obj) + return strace.C.upoke(addr, ffi.sizeof(obj), make_cdata_ptr(obj)) == 0 +end + local function parse_when(when) if type(when) == 'table' then return unpack(when) diff --git a/strace.1.in b/strace.1.in index 5ab650dd..d4531331 100644 --- a/strace.1.in +++ b/strace.1.in @@ -1013,6 +1013,20 @@ Note: there is no guarantee it won't overwrite some bytes in \fIladdr\fR after terminating NUL (but, of course, it never writes past .IR laddr[len-1] ). .TP +\fIstatus\fR = \fBstrace.C.upoke\fR(\fIaddr\fR, \fIlen\fR, \fIladdr\fR) +C type: +.B int (*)(kernel_ulong_t, size_t, const void *) +.IP +Copies ("pokes") +.I len +bytes of data from the local address +.I laddr +to the address +.I addr +of the current tracee process' address space. +.IP +Returns 0 on success and \-1 on failure. +.TP \fIstatus\fR = \fBstrace.C.path_match\fR(\fIset\fR, \fInset\fR) C type: .B bool (*)(const char **, size_t) @@ -1412,6 +1426,22 @@ Returns a Lua string on success, \fBnil, "readerr"\fR on read error, and .B PATH_MAX limit was exceeded. .TP +\fIstatus\fR = \fBstrace.write_obj\fR(\fIaddr\fR, \fIobj\fR) +Writes a FFI cdata object +.I obj +to the address +.I addr +of the current tracee process's address space. +.IP +Returns +.B true +on success and +.B false +on failure. +.IP +See the note for +.BR strace.read_obj . +.TP \fIstatus\fR = \fBstrace.hook\fR(\fIscname\fR, \fIwhen\fR, \fIcb\fR) .TQ \fIstatus\fR = \fBstrace.hook_class\fR(\fIclsname\fR, \fIwhen\fR, \fIcb\fR) diff --git a/ucopy.c b/ucopy.c index c3be4170..7f4e99ce 100644 --- a/ucopy.c +++ b/ucopy.c @@ -40,6 +40,7 @@ #include "ptrace.h" static bool process_vm_readv_not_supported; +static bool process_vm_writev_not_supported; #ifndef HAVE_PROCESS_VM_READV /* @@ -61,6 +62,19 @@ static ssize_t strace_process_vm_readv(pid_t pid, # define process_vm_readv strace_process_vm_readv #endif /* !HAVE_PROCESS_VM_READV */ +#ifndef HAVE_PROCESS_VM_WRITEV +/* The same goes for process_vm_writev(). */ +static ssize_t strace_process_vm_writev(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_writev, (long)pid, lvec, liovcnt, rvec, + riovcnt, flags); +} +#endif /* !HAVE_PROCESS_VM_WRITEV */ + static ssize_t vm_read_mem(const pid_t pid, void *const laddr, const kernel_ulong_t raddr, const size_t len) @@ -90,6 +104,35 @@ vm_read_mem(const pid_t pid, void *const laddr, return rc; } +static ssize_t +vm_write_mem(const pid_t pid, const 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 = (void *) laddr, + .iov_len = len + }; + const struct iovec remote = { + .iov_base = (void *) truncated_raddr, + .iov_len = len + }; + + const ssize_t rc = process_vm_writev(pid, &local, 1, &remote, 1, 0); + if (rc < 0 && errno == ENOSYS) + process_vm_writev_not_supported = true; + + return rc; +} + static bool tracee_addr_is_invalid(kernel_ulong_t addr) { @@ -324,3 +367,137 @@ umovestr(struct tcb *const tcp, kernel_ulong_t addr, unsigned int len, return 0; } + +static bool +partial_poke(int pid, kernel_ulong_t addr, unsigned int len, const void *laddr, + unsigned int off) +{ + union { + long val; + char x[sizeof(long)]; + } u; + errno = 0; + 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 false; + case EFAULT: case EIO: case EPERM: + /* address space is inaccessible */ + return false; + default: + /* all the rest is strange and should be reported */ + perror_msg("partial_poke: PTRACE_PEEKDATA pid:%d @0x%" PRI_klx, + pid, addr); + return false; + } + + memcpy(u.x + off, laddr, len); + + /* now write it back */ + if (ptrace(PTRACE_POKEDATA, pid, addr, u.val) < 0) { + switch (errno) { + case ESRCH: case EINVAL: + /* these could be seen if the process is gone */ + return false; + case EFAULT: case EIO: case EPERM: + /* address space is inaccessible, or write is prohibited */ + return false; + default: + /* all the rest is strange and should be reported */ + perror_msg("partial_poke: PTRACE_POKEDATA pid:%d @0x%" PRI_klx, + pid, addr); + return false; + } + } + + return true; +} + +static int +upoken_peekpoke(struct tcb *tcp, kernel_ulong_t addr, unsigned int len, + const void *const our_addr) +{ + const char *laddr = our_addr; + int pid = tcp->pid; + + if (addr & (sizeof(long) - 1)) { + /* addr not a multiple of sizeof(long) */ + unsigned n = addr & (sizeof(long) - 1); /* residue */ + addr &= -sizeof(long); /* aligned address */ + unsigned m = MIN(sizeof(long) - n, len); + if (!partial_poke(pid, addr, m, laddr, n)) + return -1; + addr += sizeof(long); + laddr += m; + len -= m; + } + + while (len >= sizeof(long)) { + if (ptrace(PTRACE_POKEDATA, pid, addr, * (const long *) laddr) < 0) { + switch (errno) { + 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, or write is prohibited */ + return -1; + default: + /* all the rest is strange and should be reported */ + perror_msg("upoken_peekpoke: PTRACE_POKEDATA pid:%d @0x%" PRI_klx, + pid, addr); + return -1; + } + } + addr += sizeof(long); + laddr += sizeof(long); + len -= sizeof(long); + } + + if (len) { + if (!partial_poke(pid, addr, len, laddr, 0)) + return -1; + } + + return 0; +} + +int +upoken(struct tcb *tcp, kernel_ulong_t addr, unsigned int len, + const void *const our_addr) +{ + if (!len) + return 0; + if (tracee_addr_is_invalid(addr)) + return -1; + + if (process_vm_writev_not_supported) + return upoken_peekpoke(tcp, addr, len, our_addr); + + int r = vm_write_mem(tcp->pid, our_addr, addr, len); + if ((unsigned int) r == len) + return 0; + if (r >= 0) { + error_msg("upoken: short write (%u < %u) @0x%" PRI_klx, + (unsigned int) r, len, addr); + return -1; + } + switch (errno) { + case ENOSYS: + case EPERM: + /* try PTRACE_PEEKDATA/PTRACE_POKEDATA */ + return upoken_peekpoke(tcp, 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_writev"); + return -1; + } +} -- 2.11.0 ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Strace-devel mailing list Strace-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/strace-devel