* 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 ec14cd84..db034cfa 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 e3710627..018b829e 100644
--- a/luajit_lib.lua
+++ b/luajit_lib.lua
@@ -351,6 +351,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 call_ufunc_on(strace.C.upoke, addr, 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 a48f3cf2..04739084 100644
--- a/strace.1.in
+++ b/strace.1.in
@@ -1002,6 +1002,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)
@@ -1435,6 +1449,22 @@ Returns a Lua string on success and
 .B nil
 and an error message string on failure.
 .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

Reply via email to