[PATCH v9 2/3] Introduce upoken function and expose it to LuaJIT scripts
Victor Krapivensky
krapivenskiy.va at phystech.edu
Tue Jul 25 12:59:52 UTC 2017
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.upoke.
* luajit_lib.lua (write_obj): New function.
* strace.1 (LUAJIT SCRIPTING): Describe changes.
* util.c (strace_process_vm_writev): New function.
(vm_write_mem): Likewise.
(partial_poke): Likewise.
(upoken): Likewise.
---
defs.h | 3 +
luajit.h | 8 +++
luajit_lib.lua | 10 ++++
strace.1 | 6 ++
util.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 199 insertions(+)
diff --git a/defs.h b/defs.h
index 0de6cacc..900220f1 100644
--- a/defs.h
+++ b/defs.h
@@ -441,6 +441,9 @@ 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 *, 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 154caf6c..c486e752 100644
--- a/luajit.h
+++ b/luajit.h
@@ -149,6 +149,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_arr(const char **set, size_t nset)
{
@@ -264,6 +270,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_arr, "path_match_arr",
const char **, size_t);
diff --git a/luajit_lib.lua b/luajit_lib.lua
index 9b303d96..2f80ef3b 100644
--- a/luajit_lib.lua
+++ b/luajit_lib.lua
@@ -235,6 +235,16 @@ 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
+ obj = ffi.typeof('$ [1]', ffi.typeof(obj))(obj)
+ end
+ assert(strace.upoke(addr, n, obj) == 0, 'cannot write')
+end
+
local function parse_when(when)
if type(when) == 'table' then
return unpack(when)
diff --git a/strace.1 b/strace.1
index 798dffcd..56cf7737 100644
--- a/strace.1
+++ b/strace.1
@@ -1063,6 +1063,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
+\fBstrace.write_obj\fR(\fIaddr\fR, \fIobj\fR)
+Writes a FFI cdata object \fIobj\fR to the current tracee process' address space
+at address \fIaddr\fR.
+.IP
+Raises an error on failure.
+.TP
\fBstrace.monitor_name\fR(\fIscname\fR, \fIwhen\fR)
Marks syscall with name \fIscname\fR as to be returned from
\fBstrace.next_sc()\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:
+ 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 -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:
+ 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
More information about the Strace-devel
mailing list