On Wed, Aug 02, 2017 at 12:50:56PM +0300, Victor Krapivensky wrote: > This patch implies that we have process_vm_writev if and only if we have > process_vm_readv, and that they are either both supported or both > unsupported. > > * 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): Add description for it. > * util.c (strace_process_vm_writev): New function. > (vm_write_mem): Likewise. > (partial_poke): Likewise. > (upoken): Likewise. Note that umove* function have been moved to a separate file (ucopy.c) and changed a bit.
> --- > defs.h | 4 ++ > luajit.h | 8 +++ > luajit_lib.lua | 12 ++++ > strace.1 | 6 ++ > util.c | 172 > +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 5 files changed, 202 insertions(+) > > diff --git a/defs.h b/defs.h > index 51fe8dda..493399ee 100644 > --- a/defs.h > +++ b/defs.h > @@ -441,6 +441,10 @@ umoven_or_printaddr_ignore_syserror(struct tcb *, > kernel_ulong_t addr, > extern int > umovestr(struct tcb *, kernel_ulong_t addr, unsigned int len, char *laddr); > > +extern int > +upoken(struct tcb *tcp, kernel_ulong_t addr, unsigned int len, > + const void *laddr); > + > extern int upeek(int pid, unsigned long, kernel_ulong_t *); > extern int upoke(int pid, unsigned long, kernel_ulong_t); > > diff --git a/luajit.h b/luajit.h > index 0b999400..7ec54b7d 100644 > --- a/luajit.h > +++ b/luajit.h > @@ -151,6 +151,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) > { > @@ -270,6 +276,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 d1996409..af24715b 100644 > --- a/luajit_lib.lua > +++ b/luajit_lib.lua > @@ -251,6 +251,18 @@ function strace.read_path(addr) > return strace.read_str(addr, strace.path_max, strace.path_max + 1) > end > > +function strace.write_obj(addr, obj) > + local n = ffi.sizeof(obj) > + -- work around FFI pointer semantics > + if n == ptr_size then > + -- it may be a pointer, and it is cheap to create a local copy > + pcall(function() > + obj = ffi.typeof('$ [1]', ffi.typeof(obj))(obj) > + end) > + end > + return strace.C.upoke(addr, n, obj) == 0 > +end > + > local function parse_when(when) > if type(when) == 'table' then > return unpack(when) > diff --git a/strace.1 b/strace.1 > index c003d4f1..f1865616 100644 > --- a/strace.1 > +++ b/strace.1 > @@ -1122,6 +1122,12 @@ Reads a path C string from the current tracee process > at address \fIaddr\fR. > Returns a Lua string on success, \fBnil, "readerr"\fR on read error, and > \fBnil, "toolong"\fR if the \fBPATH_MAX\fR limit was exceeded. > .TP > +\fIstatus\fR = \fBstrace.write_obj\fR(\fIaddr\fR, \fIobj\fR) > +Wrties a FFI cdata object \fIobj\fR to the address \fIaddr\fR of the current > +tracee process's address space. > +.IP > +Returns \fBtrue\fR on success and \fBfalse\fR on failure. > +.TP > \fIstatus\fR = \fBstrace.hook\fR(\fIscname\fR, \fIwhen\fR, \fIcb\fR) > .TP > \fIstatus\fR = \fBstrace.hook_class\fR(\fIclsname\fR, \fIwhen\fR, \fIcb\fR) > diff --git a/util.c b/util.c > index 06a939e7..e2250c05 100644 > --- a/util.c > +++ b/util.c > @@ -962,6 +962,20 @@ static ssize_t strace_process_vm_readv(pid_t pid, > return syscall(__NR_process_vm_readv, (long)pid, lvec, liovcnt, rvec, > riovcnt, flags); > } > # define process_vm_readv strace_process_vm_readv > + > +/* > + * 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); > +} > +# define process_vm_writev strace_process_vm_writev > #endif /* !HAVE_PROCESS_VM_READV */ > > static ssize_t > @@ -987,6 +1001,29 @@ vm_read_mem(const pid_t pid, void *const laddr, > return process_vm_readv(pid, &local, 1, &remote, 1, 0); > } > > +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 (raddr != (kernel_ulong_t) truncated_raddr) { > + errno = EIO; > + return -1; > + } > + > + const struct iovec local = { > + .iov_base = (void *) laddr, > + .iov_len = len > + }; > + const struct iovec remote = { > + .iov_base = (void *) truncated_raddr, > + .iov_len = len > + }; > + > + return process_vm_writev(pid, &local, 1, &remote, 1, 0); > +} > + > /* > * move `len' bytes of data from process `pid' > * at address `addr' to our space at `our_addr' > @@ -1283,6 +1320,141 @@ umovestr(struct tcb *const tcp, kernel_ulong_t addr, > unsigned int len, char *lad > 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: case should have the same indentation level as switch. > + 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: case should have the same indentation level as switch. > + /* 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("partial_poke: PTRACE_POKEDATA > pid:%d @0x%" PRI_klx, > + pid, addr); > + return -1; > + } > + } > + > + return true; > +} > + > +int > +upoken(struct tcb *tcp, kernel_ulong_t addr, unsigned int len, > + const void *const our_addr) > +{ > + if (!len) > + return 0; > + > + const char *laddr = our_addr; > + int pid = tcp->pid; > + > +#if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG > + if (current_wordsize < sizeof(addr) > + && (addr & (~(kernel_ulong_t) -1U))) { > + return -1; > + } > +#endif > + > + if (!process_vm_readv_not_supported) { > + int r = vm_write_mem(pid, laddr, 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 should have the same indentation level as switch. > + process_vm_readv_not_supported = 1; > + break; > + case EPERM: > + /* operation not permitted, try PTRACE_POKEDATA > */ > + break; > + 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; > + } > + } > + > + 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: 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; > +} > + > /* > * Iteratively fetch and print up to nmemb elements of elem_size size > * from the array that starts at tracee's address start_addr. > -- > 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