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

Reply via email to