On Thu, Jul 13, 2017 at 09:24:03PM +0200, Eugene Syromiatnikov wrote: > On Tue, Jul 11, 2017 at 02:48:48PM +0300, Victor Krapivensky wrote: > > * .gitignore: Add luajit_lib.h. > > * Makefile.am: Build with LuaJIT if configured so. > > (strace_SOURCES): Add defs_shared.h, ffi.h. > > (luajit_lib.h): Auto-generate from luajit_lib.lua. > > * configure.ac: Add new --with-luajit configure option. > > * defs.h (TCB_AD_HOC_INJECT): new TCB flag. > > (QUAL_HOOK_ENTRY, QUAL_HOOK_EXIT): new qual flags. > > (struct tcb): If built with LuaJIT support, include currpers field even > > if SUPPORTED_PERSONALITIES is 0. > > If built with LuaJIT support, include new ad_hoc_inject_opts field. > > Move definitions that need to be fed to LuaJIT's FFI to... > > * defs_shared.h: ...new file. > > * ffi.h: New file. > > * luajit.h: Likewise. > > * luajit_lib.lua: Likewise. > > > * qualify.c (syscall_classes): move syscall classes list to the global > > scope, terminate it with a null entry. > > [...] > > (lookup_class): Use global, null entry-terminated list of syscall > > classes. > Btw, Nikolay's patch "Split qualify.c into basic_filters.c and > filter_qualify.c" > also touches this code. It's probably good idea to move this change into > a separate patch so Nikolay could accommodate for it. > > The other approach is to rebase your patchset on top of Nikolay's one > (at least, it makes sense to me). > > > (qual_flags): If built with LuaJIT support, return QUAL_HOOK_ENTRY and > > QUAL_HOOK_EXIT flags. > > (set_hook_qual): New function. > > * strace.1 (LUAJIT SCRIPTING): New section. > > * strace.c (alloctcb): update the condition of presence of currpers > > field. > > (droptcb): If built with LuaJIT support, free ad_hoc_inject_opts. > > (init): New -l option (if built with LuaJIT support). > > (main): run Lua script, if built with LuaJIT support and a script > > was provided. > > * syscall.c (errnoent_vec, nerrnoent_vec, signalent_vec, > > nsignalent_vec, ioctlent_vec, nioctlent_vec): New global variables. > > > (tcb_inject_opts): introduce a second argument indicating whether tcp's > > inject_vec should be copied from the global inject_vec if needed. > > If built with LuaJIT support and TCB_AD_HOC_INJECT flag is set, return > > tcp's ad_hoc_inject_opts. > > (tamper_with_syscall_entering): Don't copy inject_vec here; instead, > > pass true as a second argument to tcb_inject_opts. > > (tamper_with_syscall_exiting): Pass false as a second argument to > > tcb_inject_opts. > > (syscall_ad_hoc_inject): New function. > > (syscall_entering_trace): perform ad hoc injection even if the syscall > > is not traced. > That's the other collision with Nikolay's changes. His implementation > is broken currently, but, overall, I think that in the end it's going be > something closer to what he proposes. > > So, the solution could be forming a separate patch that introduces > copying of injection options on each call. > > > (syscall_exiting_decode): don't return 0 ("bail out") if exiting hook is > > set up for this syscall, or if an ad hoc injection was performed. > > Call tamper_with_syscall_exiting on success. > > (syscall_exiting_trace): Don't call tamper_with_syscall_exiting, check > > if the syscall is not traced again. > > (syscall_exiting_finish): Clear TCB_AD_HOC_INJECT bit. > > * sysent.h: Modify to support inclusion with FFI_CDEF. > > --- > > .gitignore | 1 + > > Makefile.am | 17 +++ > > configure.ac | 36 ++++++ > > defs.h | 53 +++------ > > defs_shared.h | 65 +++++++++++ > > ffi.h | 19 +++ > > luajit.h | 350 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > luajit_lib.lua | 364 > > +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > qualify.c | 84 ++++++++----- > > strace.1 | 364 > > +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > > strace.c | 36 +++++- > > syscall.c | 117 ++++++++++++++++--- > > sysent.h | 24 +++- > > 13 files changed, 1438 insertions(+), 92 deletions(-) > > create mode 100644 defs_shared.h > > create mode 100644 ffi.h > > create mode 100644 luajit.h > > create mode 100644 luajit_lib.lua > > > > diff --git a/.gitignore b/.gitignore > > index 0885bcb5..a5d0c361 100644 > > --- a/.gitignore > > +++ b/.gitignore > > @@ -32,6 +32,7 @@ > > /libmpers-m32.a > > /libmpers-mx32.a > > /libstrace.a > > +/luajit_lib.h > > /m32_funcs.h > > /m32_printer_decls.h > > /m32_printer_defs.h > > diff --git a/Makefile.am b/Makefile.am > > index 05c2475a..f7797192 100644 > > --- a/Makefile.am > > +++ b/Makefile.am > > @@ -106,6 +106,7 @@ strace_SOURCES = \ > > copy_file_range.c \ > > count.c \ > > defs.h \ > > + defs_shared.h \ > > desc.c \ > > dirent.c \ > > dirent64.c \ > > @@ -128,6 +129,7 @@ strace_SOURCES = \ > > fetch_struct_stat.c \ > > fetch_struct_stat64.c \ > > fetch_struct_statfs.c \ > > + ffi.h \ > > file_handle.c \ > > file_ioctl.c \ > > flock.c \ > > @@ -293,6 +295,12 @@ strace_LDFLAGS += $(libunwind_LDFLAGS) > > strace_LDADD += $(libunwind_LIBS) > > endif > > > > +if USE_LUAJIT > > +strace_SOURCES += luajit.h > > +strace_CPPFLAGS += $(LUAJIT_CFLAGS) > > +strace_LDADD += $(LUAJIT_LIBS) > > +endif > > + > > @CODE_COVERAGE_RULES@ > > CODE_COVERAGE_BRANCH_COVERAGE = 1 > > CODE_COVERAGE_GENHTML_OPTIONS = $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) \ > > @@ -821,6 +829,7 @@ EXTRA_DIST = \ > > linux/xtensa/set_scno.c \ > > linux/xtensa/syscallent.h \ > > linux/xtensa/userent.h \ > > + luajit_lib.lua \ > > mpers.awk \ > > mpers.sh \ > > mpers_test.sh \ > > @@ -847,6 +856,9 @@ $(srcdir)/.version: > > strace_SOURCES_c = \ > > $(filter %.c,$(strace_SOURCES)) $(filter %.c,$(libstrace_a_SOURCES)) > > > > +luajit_lib.h: luajit_lib.lua > > + sed 's/["\\]/\\\0/g;s/.*/"\0\\n"/' $< > $@ > > + > > sys_func.h: $(patsubst %,$(srcdir)/%,$(strace_SOURCES_c)) > > for f in $^; do \ > > sed -n 's/^SYS_FUNC(.*/extern &;/p' $$f; \ > > @@ -925,6 +937,11 @@ CLEANFILES = $(ioctl_redefs_h) $(ioctlent_h) > > $(mpers_preproc_files) \ > > native_printer_decls.h native_printer_defs.h printers.h sen.h > > sys_func.h > > DISTCLEANFILES = gnu/stubs-32.h gnu/stubs-x32.h > > > > +if USE_LUAJIT > > +BUILT_SOURCES += luajit_lib.h > > +CLEANFILES += luajit_lib.h > > +endif > > + > > include scno.am > > > > $(strace_OBJECTS): scno.h > > diff --git a/configure.ac b/configure.ac > > index 97fd149f..9f731f41 100644 > > --- a/configure.ac > > +++ b/configure.ac > > @@ -726,6 +726,42 @@ AC_SUBST(dl_LIBS) > > > > AC_PATH_PROG([PERL], [perl]) > > > > +dnl LuaJIT scripting support > > +use_luajit=no > > +force_luajit=no > > +luajit_lib=luajit > > +LUAJIT_LIBS= > > +LUAJIT_CFLAGS= > > +AC_ARG_WITH([luajit], > > + [AS_HELP_STRING([--with-luajit], > > + [build with LuaJIT scripting support])], > > + [case "${withval}" in > > + yes) force_luajit=yes ;; > > + check) ;; > > + *) force_luajit=yes; luajit_lib="${withval}" ;; > > + esac], > > + [:] > > +) > > +AS_IF([test "x$luajit_lib" != xno], > > + [PKG_CHECK_MODULES([LUAJIT], > > + [$luajit_lib], > > + [use_luajit=yes], > > + [AS_IF([test "x$force_luajit" = xyes], > > + [AC_MSG_ERROR([cannot find luajit library: > > $luajit_lib])] > > + )] > > + )] > > +) > > + > > +dnl enable LuaJIT > > +AC_MSG_CHECKING([whether to enable Lua scripting]) > > +if test "x$use_luajit" = xyes; then > > + AC_DEFINE([USE_LUAJIT], 1, [Enable Lua scripting support]) > > + AC_SUBST(LUAJIT_LIBS) > > + AC_SUBST(LUAJIT_CFLAGS) > > +fi > > +AM_CONDITIONAL([USE_LUAJIT], [test "x$use_luajit" = xyes]) > > +AC_MSG_RESULT([$use_luajit]) > > + > > dnl stack trace with libunwind > > libunwind_CPPFLAGS= > > libunwind_LDFLAGS= > > diff --git a/defs.h b/defs.h > > index 21f1704e..bae22cd1 100644 > > --- a/defs.h > > +++ b/defs.h > > @@ -192,11 +192,6 @@ extern char *stpcpy(char *dst, const char *src); > > # define PERSONALITY2_INCLUDE_FUNCS "empty.h" > > #endif > > > > -typedef struct ioctlent { > > - const char *symbol; > > - unsigned int code; > > -} struct_ioctlent; > > - > > struct inject_opts { > > uint16_t first; > > uint16_t step; > > @@ -207,39 +202,7 @@ struct inject_opts { > > #define MAX_ERRNO_VALUE 4095 > > #define INJECT_OPTS_RVAL_DEFAULT (-(MAX_ERRNO_VALUE + 1)) > > > > -/* Trace Control Block */ > This comment has been lost during the move to the new place. > > > -struct tcb { > > - int flags; /* See below for TCB_ values */ > > - int pid; /* If 0, this tcb is free */ > > - int qual_flg; /* qual_flags[scno] or DEFAULT_QUAL_FLAGS + RAW > > */ > > - unsigned long u_error; /* Error code */ > > - kernel_ulong_t scno; /* System call number */ > > - kernel_ulong_t u_arg[MAX_ARGS]; /* System call arguments */ > > - kernel_long_t u_rval; /* Return value */ > > -#if SUPPORTED_PERSONALITIES > 1 > > - unsigned int currpers; /* Personality at the time of scno update */ > > -#endif > > - int sys_func_rval; /* Syscall entry parser's return value */ > > - int curcol; /* Output column for this process */ > > - FILE *outf; /* Output file for this process */ > > - const char *auxstr; /* Auxiliary info from syscall (see RVAL_STR) */ > > - void *_priv_data; /* Private data for syscall decoding functions > > */ > > - void (*_free_priv_data)(void *); /* Callback for freeing priv_data */ > > - const struct_sysent *s_ent; /* sysent[scno] or dummy struct for bad > > scno */ > > - const struct_sysent *s_prev_ent; /* for "resuming interrupted SYSCALL" > > msg */ > > - struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES]; > > - struct timeval stime; /* System time usage as of last process wait */ > > - struct timeval dtime; /* Delta for system time usage */ > > - struct timeval etime; /* Syscall entry time */ > > - > > -#ifdef USE_LIBUNWIND > > - struct UPT_info *libunwind_ui; > > - struct mmap_cache_t *mmap_cache; > > - unsigned int mmap_cache_size; > > - unsigned int mmap_cache_generation; > > - struct queue_t *queue; > > -#endif > > -}; > > +#include "defs_shared.h" > > > > /* TCB flags */ > > /* We have attached to this process, but did not see it stopping yet */ > > @@ -263,6 +226,7 @@ struct tcb { > > #define TCB_TAMPERED 0x40 /* A syscall has been tampered with */ > > #define TCB_HIDE_LOG 0x80 /* We should hide everything (until > > execve) */ > > #define TCB_SKIP_DETACH_ON_FIRST_EXEC 0x100 /* -b execve should > > skip detach on first execve */ > > +#define TCB_AD_HOC_INJECT 0x200 /* an ad hoc injection was performed by > > Lua script */ > > > > /* qualifier flags */ > > #define QUAL_TRACE 0x001 /* this system call should be traced */ > > @@ -273,6 +237,8 @@ struct tcb { > > #define QUAL_SIGNAL 0x100 /* report events with this signal */ > > #define QUAL_READ 0x200 /* dump data read from this file descriptor */ > > #define QUAL_WRITE 0x400 /* dump data written to this file descriptor */ > > +#define QUAL_HOOK_ENTRY 0x800 /* return this syscall on entry from > > next_sc() */ > > +#define QUAL_HOOK_EXIT 0x1000 /* return this syscall on exit from > > next_sc() */ > > > > #define DEFAULT_QUAL_FLAGS (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE) > > > > @@ -361,6 +327,7 @@ typedef enum { > > CFLAG_ONLY_STATS, > > CFLAG_BOTH > > } cflag_t; > > +extern const struct syscall_class syscall_classes[]; > > extern cflag_t cflag; > > extern bool debug_flag; > > extern bool Tflag; > > @@ -683,6 +650,9 @@ extern struct number_set signal_set; > > extern bool is_number_in_set(unsigned int number, const struct number_set > > *); > > extern void qualify(const char *); > > extern unsigned int qual_flags(const unsigned int); > > +#ifdef USE_LUAJIT > > +extern void set_hook_qual(unsigned int scno, unsigned int pers, bool > > entry_hook, bool exit_hook); > Overly long line. > > > +#endif > > > > #define DECL_IOCTL(name) \ > > extern int \ > > @@ -963,6 +933,13 @@ extern const char *const errnoent0[]; > > extern const char *const signalent0[]; > > extern const struct_ioctlent ioctlent0[]; > > > > +extern const char *const *errnoent_vec[SUPPORTED_PERSONALITIES]; > > +extern const char *const *signalent_vec[SUPPORTED_PERSONALITIES]; > > +extern const struct_ioctlent *const ioctlent_vec[SUPPORTED_PERSONALITIES]; > > +extern const unsigned int nerrnoent_vec[SUPPORTED_PERSONALITIES]; > > +extern const unsigned int nsignalent_vec[SUPPORTED_PERSONALITIES]; > > +extern const unsigned int nioctlent_vec[SUPPORTED_PERSONALITIES]; > > + > > #if SUPPORTED_PERSONALITIES > 1 > > extern const struct_sysent *sysent; > > extern const char *const *errnoent; > > diff --git a/defs_shared.h b/defs_shared.h > > new file mode 100644 > > index 00000000..9609749b > > --- /dev/null > > +++ b/defs_shared.h > > @@ -0,0 +1,65 @@ > > +/* > > + * Should only be included without FFI_CDEF from defs.h, so no include > > guards. > > + */ > > + > > +#include "ffi.h" > > + > > +FFI_CONTENT( > > +struct syscall_class { > > + const char *name; > > + unsigned int value; > > +}; > > +) > > + > > +FFI_CONTENT( > > +typedef struct ioctlent { > > + const char *symbol; > > + unsigned int code; > > +} struct_ioctlent; > > +) > > + > > +FFI_CONTENT( > > +struct tcb { > > + int flags; /* See below for TCB_ values */ > > + int pid; /* If 0, this tcb is free */ > > + int qual_flg; /* qual_flags[scno] or DEFAULT_QUAL_FLAGS + RAW > > */ > > + unsigned long u_error; /* Error code */ > > + kernel_ulong_t scno; /* System call number */ > > + kernel_ulong_t u_arg[MAX_ARGS]; /* System call arguments */ > > + kernel_long_t u_rval; /* Return value */ > > +) > > + > > +#if defined(USE_LUAJIT) || SUPPORTED_PERSONALITIES > 1 > > +FFI_CONTENT( > > + unsigned int currpers; /* Personality at the time of scno update */ > > +) > > +#endif > > + > > +#ifndef FFI_CDEF > > + int sys_func_rval; /* Syscall entry parser's return value */ > > + int curcol; /* Output column for this process */ > > + FILE *outf; /* Output file for this process */ > > + const char *auxstr; /* Auxiliary info from syscall (see RVAL_STR) */ > > + void *_priv_data; /* Private data for syscall decoding functions > > */ > > + void (*_free_priv_data)(void *); /* Callback for freeing priv_data */ > > + const struct_sysent *s_ent; /* sysent[scno] or dummy struct for bad > > scno */ > > + const struct_sysent *s_prev_ent; /* for "resuming interrupted SYSCALL" > > msg */ > > +# ifdef USE_LUAJIT > > + struct inject_opts *ad_hoc_inject_opts; > > +# endif > > + struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES]; > > + struct timeval stime; /* System time usage as of last process wait */ > > + struct timeval dtime; /* Delta for system time usage */ > > + struct timeval etime; /* Syscall entry time */ > > +# ifdef USE_LIBUNWIND > > + struct UPT_info *libunwind_ui; > > + struct mmap_cache_t *mmap_cache; > > + unsigned int mmap_cache_size; > > + unsigned int mmap_cache_generation; > > + struct queue_t *queue; > > +# endif > > +#endif /* !FFI_CDEF */ > > + > > +FFI_CONTENT( > > +}; > > +) > > diff --git a/ffi.h b/ffi.h > > new file mode 100644 > > index 00000000..7b89e7a4 > > --- /dev/null > > +++ b/ffi.h > > @@ -0,0 +1,19 @@ > > +#ifndef STRACE_FFI_H > > +#define STRACE_FFI_H > > + > > +#include "macros.h" > > + > > +#define FFI_CONCAT(a, b) a ## b > > +#define FFI_CONCAT2(a, b) FFI_CONCAT(a, b) > > + > > +/* > > + * FFI_CONTENT expands to FFI_CONTENT_ (which strigifies its arguments) > > when > > + * FFI_CDEF is defined, and to FFI_CONTENT_FFI_CDEF (which simply expands > > to its > > + * arguments) when it is not. > > + */ > > +#define FFI_CONTENT FFI_CONCAT2(FFI_CONTENT_, FFI_CDEF) > > + > > +#define FFI_CONTENT_(...) STRINGIFY(__VA_ARGS__) > > +#define FFI_CONTENT_FFI_CDEF(...) __VA_ARGS__ > > + > > +#endif /* !STRACE_FFI_H */ > > diff --git a/luajit.h b/luajit.h > > new file mode 100644 > > index 00000000..231466af > > --- /dev/null > > +++ b/luajit.h > > @@ -0,0 +1,350 @@ > > +/* > > + * Should only be included from strace.c, so no include guards. > > + */ > > + > > +#include <lualib.h> > > +#include <lauxlib.h> > > + > > +#define L script_L > > + > > +static struct tcb * > > +func_next_sc(void) > > +{ > > + static struct timeval tv = {}; > > + static bool first = true; > > + > > +#define MAYBE_RESTART(res, sig) > > \ > > + do { > > \ > > + if ((res) >= 0 && ptrace_restart(PTRACE_SYSCALL, current_tcp, > > sig) < 0) { \ > > + /* Note: ptrace_restart emitted error message */ > > \ > > + exit_code = 1; > > \ > > + goto term; > > \ > > + } > > \ > Overly long lines. > > > + } while (0) > > + > > > + if (!first) { > > + if (!current_tcp) > > + return NULL; > > + > > + unsigned int sig = 0; > > + int res; > > + if (entering(current_tcp)) { > > + res = syscall_entering_trace(current_tcp, &sig); > > + syscall_entering_finish(current_tcp, res); > > + } else { > > + res = syscall_exiting_trace(current_tcp, tv, 1); > > + syscall_exiting_finish(current_tcp); > > + } > > + MAYBE_RESTART(res, sig); > > + } > > + first = false; > Why is this? Are you trying to simulate TCB_HIDE_LOG behaviour? It is > needed only in case of running a process under strace, but not in case > of attaching to a process. > Why what exactly? We need to perform tracing/tampering of the last syscall returned from next_sc (if this is not the last call to it). See the documentation for strace.next_sc in the man page.
> > + > > + while (1) { > > + int status; > > + siginfo_t si; > > + enum trace_event ret = next_event(&status, &si); > > + if (ret == TE_SYSCALL_STOP) { > > + unsigned int sig = 0; > > + int res; > > + if (entering(current_tcp)) { > > + res = syscall_entering_decode(current_tcp); > > + switch (res) { > > + case 0: > > + break; > > + case 1: > > + if (current_tcp->qual_flg & > > QUAL_HOOK_ENTRY) > Overly long line. > > > + return current_tcp; > > + res = > > syscall_entering_trace(current_tcp, &sig); > Overly long line. > > > + /* fall through */ > > + default: > > + syscall_entering_finish(current_tcp, > > res); > > + } > > + } else { > > + res = syscall_exiting_decode(current_tcp, &tv); > > + switch (res) { > > + case 0: > > + break; > > + case 1: > > + if (current_tcp->qual_flg & > > QUAL_HOOK_EXIT) > Overly long line. > > > + return current_tcp; > > + /* fall through */ > > + default: > > + res = > > syscall_exiting_trace(current_tcp, tv, res); > Overly long line. > > > + } > > + syscall_exiting_finish(current_tcp); > > + } > > + MAYBE_RESTART(res, sig); > > + } else { > > + if (!dispatch_event(ret, &status, &si)) > > + goto term; > > + } > > + } > > +#undef MAYBE_RESTART > > +term: > > + current_tcp = NULL; > > + return NULL; > > +} > This function looks like a partial reimplementation of > dispatch_event/trace_syscall logic; personally, I'd prefer to see this > functionality to be implemented inside those function themselves (as I > anticipate the introduction of other tracing backends, to be honest). > I see no reason for moving the implementation somewhere, nor can I imagine a way to do it. next_event/dispatch_event were introduced to enable alternative tracing loops that occasionally need to intervene and reimplement some parts of the tracing process (the pull-style API). > > + > > +static bool > > +func_monitor(unsigned int scno, unsigned int pers, bool entry_hook, bool > > exit_hook) > Overly long line. > > > +{ > > + if (pers >= SUPPORTED_PERSONALITIES || scno >= nsyscall_vec[pers]) > > + return false; > > + set_hook_qual(scno, pers, entry_hook, exit_hook); > > + return true; > > +} > > + > > +static void > > +prepare_ad_hoc_inject(void) > > +{ > > + struct inject_opts *opts = current_tcp->ad_hoc_inject_opts; > > + if (!opts) { > > + opts = current_tcp->ad_hoc_inject_opts = xmalloc(sizeof(*opts)); > > + opts->first = 1; > > + opts->step = 1; > > + } > > + if (!(current_tcp->flags & TCB_AD_HOC_INJECT)) { > > + opts->signo = 0; > > + opts->rval = INJECT_OPTS_RVAL_DEFAULT; > > + current_tcp->qual_flg |= QUAL_INJECT; > > + current_tcp->flags |= TCB_AD_HOC_INJECT; > > + } > > +} > > + > > +static bool > > +func_inject_signo(int signo) > > +{ > > + if (!current_tcp || exiting(current_tcp)) > > + /* Too late! */ > > + return false; > > + if (signo <= 0 || signo > SIGRTMAX) > > + return false; > > + prepare_ad_hoc_inject(); > > + current_tcp->ad_hoc_inject_opts->signo = signo; > > + return true; > > +} > > + > > +static bool > > +func_inject_retval(int retval) > > +{ > > + if (!current_tcp || exiting(current_tcp)) > > + /* Too late! */ > > + return false; > > + if (retval < -MAX_ERRNO_VALUE) > > + return false; > > + prepare_ad_hoc_inject(); > > + current_tcp->ad_hoc_inject_opts->rval = retval; > > + return true; > > +} > > + > > +static int > > +func_umove(kernel_ulong_t addr, size_t len, void *laddr) > > +{ > > + return current_tcp ? umoven(current_tcp, addr, len, laddr) : -1; > > +} > > + > > +static int > > +func_umove_str(kernel_ulong_t addr, size_t len, char *laddr) > > +{ > > + return current_tcp ? umovestr(current_tcp, addr, len, laddr) : -1; > > +} > > + > > +static bool > > +func_path_match_arr(const char **set, size_t nset) > > +{ > > + if (!current_tcp) > > + return false; > > + struct path_set s = {set, nset}; > > + return pathtrace_match_set(current_tcp, &s); > > +} > > + > > +static const char * > > +get_lua_msg(void) > > +{ > > + const char *msg = lua_tostring(L, -1); > > + return msg ? msg : "(error object can't be converted to string)"; > > +} > > + > > +static void > > +assert_lua_impl(int ret, const char *expr, const char *file, int line) > > +{ > > + if (ret == 0) > > + return; > > + error_msg_and_die("assert_lua(%s) failed at %s:%d: %s", expr, file, > > + line, get_lua_msg()); > > +} > > + > > +#define assert_lua(expr) assert_lua_impl(expr, #expr, __FILE__, __LINE__) > > + > > +static void > > +check_lua(int ret) > > +{ > > + if (ret == 0) > > + return; > > + error_msg_and_die("lua: %s", get_lua_msg()); > > +} > > + > > +#ifdef LUA_FFILIBNAME > > +# define FFILIBNAME LUA_FFILIBNAME > > +#else > > +/* non-LuaJIT */ > > +# define FFILIBNAME "ffi" > > +#endif > > + > > +#ifdef LUA_BITLIBNAME > > +# define BITLIBNAME LUA_BITLIBNAME > > +#else > > +/* Lua <=5.1 (non-LuaJIT) */ > Missing space before "5.1". > > > +# define BITLIBNAME "bit" > > +#endif > > + > > +static void > > +init_luajit(const char *scriptfile) > > +{ > > + if (L) > > + /* already initialized? */ > > + error_msg_and_help("multiple -l arguments"); > > + > > + if (!(L = luaL_newstate())) > > + error_msg_and_die("luaL_newstate failed (out of memory?)"); > > + > > + luaL_openlibs(L); > Btw, what are possible security implications of enabling OS and I/O libs for > Lua > scripts? I mean, most security people do not expect that strace's ability in > regards to OS and I/O behaviour are changed from "strictly specified" to > "arbitrary". It's probably good idea to provide some mechanism to limit > ability > for the strace's Lua scripts interaction with the external system (not to > mention that ability to arbitrarily tamper tracee's behaviour is also external > interaction, but that's a different story, albeit quite related). > > I'm raising this question as I'm afraid that distributions will tend to > disable > (or rather not enable) this feature due to possible security implications > (like, > running some daemon under strace considered sort of safe, and enabling Lua > scripting defies this implication), so providing some configuration mechanisms > would probably be enough to convince them not doing so. > Well, once you have access to the FFI library, it's all over -- you can run basically any code you want. So, for such a configuration mechanism to work, it would have to proxy it somehow. > > + > > + lua_getglobal(L, "require"); /* L: require */ > > + lua_pushstring(L, FFILIBNAME); /* L: require str */ > > + assert_lua(lua_pcall(L, 1, 1, 0)); /* L: ffi */ > > + lua_getfield(L, -1, "cdef"); /* L: ffi cdef */ > > + luaL_Buffer b; > > + luaL_buffinit(L, &b); /* L: ffi cdef ? */ > > + { > > + char buf[128]; > > + snprintf(buf, sizeof(buf), > > + "typedef int%d_t kernel_long_t;" > > + "typedef uint%d_t kernel_ulong_t;", > > + (int) sizeof(kernel_long_t) * 8, > > + (int) sizeof(kernel_ulong_t) * 8); > > + luaL_addstring(&b, buf); /* L: ffi cdef ? */ > > + } > > + const char *defs = > > +#define FFI_CDEF > > +#include "sysent.h" > > +#include "defs_shared.h" > > +#undef FFI_CDEF > > + ; > > + luaL_addstring(&b, defs); /* L: ffi cdef ? */ > > + luaL_pushresult(&b); /* L: ffi cdef str */ > > + assert_lua(lua_pcall(L, 1, 0, 0)); /* L: ffi */ > > + > > + lua_newtable(L); /* L: ffi table */ > > + > > + lua_getfield(L, -2, "cast"); /* L: ffi table cast */ > > + lua_remove(L, -3); /* L: table cast */ > > + > > +#define EXPOSE_FUNC(rettype, ptr, name, ...) > > \ > > + do { > > \ > > + rettype (*fptr_)(__VA_ARGS__) = ptr; > > \ > > + lua_pushvalue(L, -1); /* L: table cast cast */ > > \ > > + lua_pushstring(L, #rettype " (*)(" #__VA_ARGS__ ")"); > > \ > > + /* L: table cast cast str */ > > \ > > + lua_pushlightuserdata(L, * (void **) (&fptr_)); > > \ > > + /* L: table cast cast str ptr */ > > \ > > + assert_lua(lua_pcall(L, 2, 1, 0)); /* L: table cast value */ > > \ > > + lua_setfield(L, -3, name); /* L: table cast */ > > \ > > + } while (0) > > + > > + EXPOSE_FUNC(bool, func_monitor, "monitor", > > + unsigned int, unsigned int, bool, bool); > > + EXPOSE_FUNC(struct tcb *, func_next_sc, "next_sc", > > + void); > > + EXPOSE_FUNC(bool, func_inject_signo, "inject_signo", > > + int); > > + EXPOSE_FUNC(bool, func_inject_retval, "inject_retval", > > + int); > > + EXPOSE_FUNC(int, func_umove, "umove", > > + kernel_ulong_t, size_t, void *); > > + EXPOSE_FUNC(int, func_umove_str, "umove_str", > > + kernel_ulong_t, size_t, char *); > > + EXPOSE_FUNC(bool, func_path_match_arr, "path_match_arr", > > + const char **, size_t); > Taking into the account naming pattern, the duplication in ptr/name > values can be avoided. > Could you please elaborate on this one? > > + > > +#undef EXPOSE_FUNC > > + > > +#define EXPOSE(type, ptr, name) > > \ > > + do { > > \ > > + /* Get a compilation error/warning on type mismatch */ > > \ > > + type tmp_ = ptr; > > \ > > + (void) tmp_; > > \ > > + lua_pushvalue(L, -1); /* L: table cast cast */ > > \ > > + lua_pushstring(L, #type); /* L: table cast cast str */ > > \ > > + lua_pushlightuserdata(L, (void *) ptr); /* L: table cast cast > > str ptr */ \ > > + assert_lua(lua_pcall(L, 2, 1, 0)); /* L: table cast value */ > > \ > > + lua_setfield(L, -3, name); /* L: table cast */ > > \ > Overly long lines. > > > + } while (0) > > + > > + EXPOSE(const struct_sysent *const *, sysent_vec, "sysent_vec"); > > + EXPOSE(const char *const **, errnoent_vec, "errnoent_vec"); > > + EXPOSE(const char *const **, signalent_vec, "signalent_vec"); > > + EXPOSE(const struct_ioctlent *const *, ioctlent_vec, "ioctlent_vec"); > > + > > + EXPOSE(const unsigned int *, nsyscall_vec, /*(!)*/ "nsysent_vec"); > But… why? > For the consistency? > > + EXPOSE(const unsigned int *, nerrnoent_vec, "nerrnoent_vec"); > > + EXPOSE(const unsigned int *, nsignalent_vec, "nsignalent_vec"); > > + EXPOSE(const unsigned int *, nioctlent_vec, "nioctlent_vec"); > > + > > + EXPOSE(const struct syscall_class *, syscall_classes, > > "syscall_classes"); > > + > > +#undef EXPOSE > > + > > + lua_pop(L, 1); /* L: table */ > > + > > + lua_pushinteger(L, SUPPORTED_PERSONALITIES); /* L: table int */ > > + lua_setfield(L, -2, "npersonalities"); /* L: table */ > > + > > + lua_pushinteger(L, MAX_ARGS); /* L: table int */ > > + lua_setfield(L, -2, "max_args"); /* L: table */ > > + > > + lua_pushinteger(L, PATH_MAX); /* L: table int */ > > + lua_setfield(L, -2, "path_max"); /* L: table */ > > + > > + lua_setglobal(L, "strace"); /* L: - */ > > + > > + const char *code = > > +#include "luajit_lib.h" > > + ; > > + assert_lua(luaL_loadstring(L, code)); /* L: chunk */ > > + > > + lua_newtable(L); /* L: chunk table */ > > + > > + lua_pushstring(L, FFILIBNAME); /* L: chunk table str */ > > + lua_setfield(L, -2, "ffilibname"); /* L: chunk table */ > > + lua_pushstring(L, BITLIBNAME); /* L: chunk table str */ > > + lua_setfield(L, -2, "bitlibname"); /* L: chunk table */ > > + lua_pushinteger(L, TCB_INSYSCALL); /* L: chunk table int */ > > + lua_setfield(L, -2, "tcb_insyscall"); /* L: chunk table */ > > + lua_pushinteger(L, QUAL_TRACE); /* L: chunk table int */ > > + lua_setfield(L, -2, "qual_trace"); /* L: chunk table */ > > + lua_pushinteger(L, QUAL_ABBREV); /* L: chunk table int */ > > + lua_setfield(L, -2, "qual_abbrev"); /* L: chunk table */ > > + lua_pushinteger(L, QUAL_VERBOSE); /* L: chunk table int */ > > + lua_setfield(L, -2, "qual_verbose"); /* L: chunk table */ > > + lua_pushinteger(L, QUAL_RAW); /* L: chunk table int */ > > + lua_setfield(L, -2, "qual_raw"); /* L: chunk table */ > > + > > + assert_lua(lua_pcall(L, 1, 1, 0)); /* L: func */ > > + > > + assert_lua(luaL_loadfile(L, scriptfile)); /* L: func chunk */ > > +} > > + > > +static void ATTRIBUTE_NORETURN > > +run_luajit(void) > > +{ > > + /* L: func chunk */ > > + check_lua(lua_pcall(L, 0, 0, 0)); /* L: func */ > > + check_lua(lua_pcall(L, 0, 0, 0)); /* L: - */ > > + terminate(); > > +} > > + > > +#undef FFILIBNAME > > +#undef BITLIBNAME > > +#undef assert_lua > > +#undef L > > diff --git a/luajit_lib.lua b/luajit_lib.lua > > new file mode 100644 > > index 00000000..9c35c0fa > > --- /dev/null > > +++ b/luajit_lib.lua > > @@ -0,0 +1,364 @@ > > +-- This "chunk" of code is loaded and run before the script is. > > +-- > > +-- To quote https://www.lua.org/manual/5.1/manual.html#2.4.1, > > +-- "Lua handles a chunk as the body of an anonymous function with a > > variable > > +-- number of arguments (see §2.5.9). As such, chunks can define local > > +-- variables, receive arguments, and return values." > > +-- > > +-- Thanks to Lua's support for closures, all the local variables defined > > here > > +-- will not leak to another chunks (i.e., the script), but all the > > functions > > +-- defined here can still access them. > > +-- > > +-- strace calls this chunk with a single argument: a table with data that > > should > > +-- not be exposed to the script, but is needed for some API functions > > defined > > +-- here. > > +-- > > +-- strace expects this chunk to return another function that will be run > > after > > +-- the script returns. > > +-- > > +-- Arguments passed to this chunk are accessible through the "..." vararg > > +-- expression. The following line uses Lua's "adjust" assignment semantics > > to > > +-- assign the first argument to a local variable "priv". > > +local priv = ... > > + > > +local ffi = require(priv.ffilibname) > > +ffi.cdef[[ > > +int strcmp(const char *, const char *); > > +char *realpath(const char *, char *); > > +void free(void *); > > +]] > > +local bit = require(priv.bitlibname) > > + > > +local entry_cbs, exit_cbs, at_exit_cb = {}, {}, nil > > +for p = 0, strace.npersonalities - 1 do > > + entry_cbs[p] = {} > > + exit_cbs[p] = {} > > +end > > + > > +function print(...) > > + local sep = '' > > + for i = 1, select('#', ...) do > > + io.stderr:write(sep .. tostring(select(i, ...))) > > + sep = '\t' > > + end > > + io.stderr:write('\n') > > +end > > + > > +local function chain(f, g) > > + if not f then > > + return g > > + end > > + return function(...) > > + f(...) > > + g(...) > > + end > > +end > > + > > +function strace.entering(tcp) > > + return bit.band(tcp.flags, priv.tcb_insyscall) == 0 > > +end > > + > > +function strace.exiting(tcp) > > + return bit.band(tcp.flags, priv.tcb_insyscall) ~= 0 > > +end > > + > > +local function alter_trace_opt(flagbit, tcp, ...) > > + if strace.exiting(tcp) then > > + error('altering tracing options must be done on syscall entry') > > + end > > + -- i.e., if ... is empty, or the first element of ... is true > > + if select('#', ...) == 0 or select(1, ...) then > > + tcp.qual_flg = bit.bor(tcp.qual_flg, flagbit) > > + else > > + tcp.qual_flg = bit.band(tcp.qual_flg, bit.bnot(flagbit)) > > + end > > +end > > +function strace.trace (tcp, ...) alter_trace_opt(priv.qual_trace, tcp, > > ...) end > > +function strace.abbrev (tcp, ...) alter_trace_opt(priv.qual_abbrev, tcp, > > ...) end > > +function strace.verbose(tcp, ...) alter_trace_opt(priv.qual_verbose, tcp, > > ...) end > > +function strace.raw (tcp, ...) alter_trace_opt(priv.qual_raw, tcp, > > ...) end > > + > > +function strace.ptr_to_kulong(ptr) > > + return ffi.cast('kernel_ulong_t', ffi.cast('unsigned long', ptr)) > > +end > > + > > +function strace.at_exit(f) > > + at_exit_cb = chain(at_exit_cb, f) > > +end > > + > > > +function strace.get_err_name(err, pers) > > + pers = pers or 0 > > + if err < 0 or err > strace.nerrnoent_vec[pers] then > > + return nil > > + end > > + local s = strace.errnoent_vec[pers][err] > > + return s ~= nil and ffi.string(s) or nil > > +end > > + > > +function strace.get_sc_name(scno, pers) > > + pers = pers or 0 > > + if scno < 0 or scno >= strace.nsysent_vec[pers] then > > + return nil > > + end > > + local s = strace.sysent_vec[pers][scno].sys_name > > + return s ~= nil and ffi.string(s) or nil > > +end > > + > > +function strace.get_ioctl_name(code, pers) > > + pers = pers or 0 > > + -- we could have provided a definition for stdlib's bsearch() and used > > + -- it, but LuaJIT's FFI manual says generated callbacks are a limited > > + -- resource and also slow. So implement binary search ourselves. > > + local lb, rb = ffi.cast('unsigned int', 0), strace.nioctlent_vec[pers] > > + if rb == 0 then > > + return nil > > + end > > + local arr = strace.ioctlent_vec[pers] > > + while rb - lb > 1 do > > + local mid = lb + (rb - lb) / 2 > > + if arr[mid].code <= code then > > + lb = mid > > + else > > + rb = mid > > + end > > + end > > + return arr[lb].code == code and ffi.string(arr[lb].symbol) or nil > > +end > > + > > +function strace.get_scno(scname, pers) > > + pers = pers or 0 > > + local cstr = ffi.cast('const char *', scname) > > + for i = 0, tonumber(strace.nsysent_vec[pers]) - 1 do > > + local s = strace.sysent_vec[pers][i].sys_name > > + if s ~= nil and ffi.C.strcmp(s, cstr) == 0 then > > + return i > > + end > > + end > > + return nil > > +end > > + > > +function strace.get_signo(signame, pers) > > + pers = pers or 0 > > + local cstr = ffi.cast('const char *', signame) > > + for i = 0, tonumber(strace.nsignalent_vec[pers]) - 1 do > > + local s = strace.signalent_vec[pers][i] > > + if s ~= nil and ffi.C.strcmp(s, cstr) == 0 then > > + return i > > + end > > + end > > + return nil > > +end > > + > > +function strace.get_errno(errname, pers) > > + pers = pers or 0 > > + local cstr = ffi.cast('const char *', errname) > > + for i = 0, tonumber(strace.nerrnoent_vec[pers]) - 1 do > > + local s = strace.errnoent_vec[pers][i] > > + if s ~= nil and ffi.C.strcmp(s, cstr) == 0 then > > + return i > > + end > > + end > > + return nil > > +end > What I don't like here is that these functions are re-implementation of > the code already present in qualify.c/strace.c/syscall.c. Can't that > code just be exposed to Lua and these functions be just thin wrappers > around them? > Don't know. We certainly need to all exposed "vectors" so that the script could search for these values faster (say, put that in a table). Why to expose functions if we can implement it in terms of these "vectors"? > > + > > +function strace.inject_signal(tcp, sig) > > + if type(sig) == 'string' then > > + sig = strace.get_signo(sig, tcp.currpers) > > + if not sig then > > + error('signal not found') > > + end > > + end > > + if not strace.inject_signo(sig) then > > + error('cannot inject signal') > > + end > > +end > > + > > +function strace.inject_error(tcp, err) > > + if type(err) == 'string' then > > + err = strace.get_errno(err, tcp.currpers) > > + if not err then > > + error('error not found') > > + end > > + end > > + if err <= 0 then > > + error('err must be positive') > > + end > > + if not strace.inject_retval(-err) then > > + error('cannot inject error') > > + end > > +end > > + > > +local ptr_size = ffi.sizeof('void *') > > + > > +function strace.read_obj(addr, ct, ...) > > + local obj = ffi.new(ct, ...) > > + 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 another copy > > + local t = ffi.typeof(obj) > > + local arr = ffi.typeof('$ [1]', t)() > > + return strace.umove(addr, n, arr) == 0 and t(arr[0]) or nil > > + else > > + return strace.umove(addr, n, obj) == 0 and obj or nil > > + end > > +end > > + > > +function strace.read_str(addr, maxsz, bufsz) > > + -- convert it to Lua number to prevent underflows > > + maxsz = tonumber(maxsz or 4 * 1024 * 1024) > > + bufsz = bufsz or 1024 > > + local t = {} > > + local buf = ffi.new('char[?]', bufsz) > > + while true do > > + local r = strace.umove_str(addr, bufsz, buf) > > + if r < 0 then > > + return nil, 'readerr' > > + elseif r == 0 then > > + maxsz = maxsz - bufsz > > + if maxsz < 0 then > > + return nil, 'toolong' > > + end > > + t[#t + 1] = ffi.string(buf, bufsz) > > + addr = addr + bufsz > > + else > > + local s = ffi.string(buf) > > + if #s > maxsz then > > + return nil, 'toolong' > > + end > > + return table.concat(t) .. s > > + end > > + end > > +end > > + > > +function strace.read_path(addr) > > + return strace.read_str(addr, strace.path_max, strace.path_max + 1) > > +end > > + > > +local function register_hook(scno, pers, on_entry, on_exit, cb) > > + assert(not not strace.monitor(scno, pers, on_entry, on_exit)) > > + pers = tonumber(pers) > > + scno = tonumber(scno) > > + if on_entry then > > + entry_cbs[pers][scno] = chain(entry_cbs[pers][scno], cb) > > + end > > + if on_exit then > > + exit_cbs[pers][scno] = chain(exit_cbs[pers][scno], cb) > > + end > > +end > > + > > +local function parse_when(when) > > + if when == 'entering' then > > + return true, false > > + elseif when == 'exiting' then > > + return false, true > > + elseif when == 'both' then > > + return true, true > > + else > > + error('unknown "when" value') > > + end > > +end > > + > > +function strace.monitor_all() > > + for p = 0, strace.npersonalities - 1 do > > + for i = 0, tonumber(strace.nsysent_vec[p]) - 1 do > > + strace.monitor(i, p, true, true) > > + end > > + end > > +end > And what if syscall with a number unknown to strace is invoked? It's > probably a good idea to have a "default cb" for the cases when there is > no callback and scno is not good. > > > Btw, considering table size: mips syscall table is over 6000 entries long. > OK, something need to be done here. Maybe allow func_monitor to accept "non-good" syscall numbers, and introduce func_monitor_all that would just turn hook_entry_set/hook_exit_set into empty sets with "not" flag set? > > + > > +function strace.hook(scname, when, cb) > > + if type(scname) == 'table' then > > + for _, elem in pairs(scname) do > > + strace.hook(elem, when, cb) > > + end > > + return > > + end > > + local on_entry, on_exit = parse_when(when) > > + local found = false > > + for p = 0, strace.npersonalities - 1 do > > + local scno = strace.get_scno(scname, p) > > + if scno then > > + register_hook(scno, p, on_entry, on_exit, cb) > > + found = true > > + end > > + end > > + if not found then > > + error('syscall not found') > > + end > > +end > > + > > +function strace.hook_class(clsname, when, cb) > > + if type(clsname) == 'table' then > > + for _, elem in pairs(scname) do > > + strace.hook_class(elem, when, cb) > > + end > > + return > > + end > > + local cstr = ffi.cast('const char *', clsname) > > + local flag = nil > > + local ptr = strace.syscall_classes > > + while ptr.name ~= nil do > > + if ffi.C.strcmp(ptr.name, cstr) == 0 then > > + flag = ptr.value > > + break > > + end > > + ptr = ptr + 1 > > + end > > + if not flag then > > + error('syscall class not found') > > + end > > + local on_entry, on_exit = parse_when(when) > > + for p = 0, strace.npersonalities - 1 do > > + for i = 0, tonumber(strace.nsysent_vec[p]) - 1 do > > + if bit.band(strace.sysent_vec[p][i].sys_flags, flag) ~= > > 0 then > Overly long line. > > > + register_hook(i, p, on_entry, on_exit, cb) > > + end > > + end > > + end > > +end > > + > > +function strace.hook_scno(scno, when, cb, pers) > > + if type(scno) == 'table' then > > + for _, elem in pairs(scname) do > > + strace.hook_scno(elem, when, cb) > > + end > > + return > > + end > > + pers = pers or 0 > > + local on_entry, on_exit = parse_when(when) > > + reigster_hook(scno, pers, on_entry, on_exit, cb) > "reigster" > > I think you can rather easily support every variation of syscall > qualification by passing a syscall qualification string to func_monitor > (or some other function, since bitmask for updating cb arrays is needed) and > parsing it there instead of trying to handle it in Lua (on the other hand, > transparent handling of arrays of qualifications in Lua is certainly good). > > > +end > > + > > +function strace.path_match(set) > > + if type(set) ~= 'table' then > > + set = {set} > > + end > > + local nset = #set > > + return not not strace.path_match_arr( > > + ffi.new('const char *[?]', nset, set), nset) > > +end > > + > > +function strace.get_fd_path(tcp, fd) > > + local buf = ffi.new('char[?]', strace.path_max + 1) > > + local r = ffi.C.realpath(string.format( > > + '/proc/%u/fd/%u', tonumber(tcp.pid), tonumber(fd)), buf) > > + return r ~= nil and ffi.string(r) or nil > > +end > Why this function is needed? > I thought it might be useful for scripts. > > + > > +return function() > > + while true do > > + local tcp = strace.next_sc() > > + if tcp == nil then > > + break > > + end > > + local cb = (strace.entering(tcp) and entry_cbs or exit_cbs) > > + [tonumber(tcp.currpers)][tonumber(tcp.scno)] > > + if cb then > > + cb(tcp) > > + end > > + end > > + if at_exit_cb then > > + at_exit_cb() > > + end > > +end > > diff --git a/qualify.c b/qualify.c > > index 3df4805a..15f8d40e 100644 > > --- a/qualify.c > > +++ b/qualify.c > > @@ -30,6 +30,31 @@ > > #include "nsig.h" > > #include <regex.h> > > > > +const struct syscall_class syscall_classes[] = { > > + { "desc", TRACE_DESC }, > > + { "file", TRACE_FILE }, > > + { "memory", TRACE_MEMORY }, > > + { "process", TRACE_PROCESS }, > > + { "signal", TRACE_SIGNAL }, > > + { "ipc", TRACE_IPC }, > > + { "network", TRACE_NETWORK }, > > + { "%desc", TRACE_DESC }, > > + { "%file", TRACE_FILE }, > > + { "%memory", TRACE_MEMORY }, > > + { "%process", TRACE_PROCESS }, > > + { "%signal", TRACE_SIGNAL }, > > + { "%ipc", TRACE_IPC }, > > + { "%network", TRACE_NETWORK }, > > + { "%stat", TRACE_STAT }, > > + { "%lstat", TRACE_LSTAT }, > > + { "%fstat", TRACE_FSTAT }, > > + { "%%stat", TRACE_STAT_LIKE }, > > + { "%statfs", TRACE_STATFS }, > > + { "%fstatfs", TRACE_FSTATFS }, > > + { "%%statfs", TRACE_STATFS_LIKE }, > > + {} > > +}; > > + > > typedef unsigned int number_slot_t; > > #define BITS_PER_SLOT (sizeof(number_slot_t) * 8) > > > > @@ -48,6 +73,10 @@ static struct number_set > > inject_set[SUPPORTED_PERSONALITIES]; > > static struct number_set raw_set[SUPPORTED_PERSONALITIES]; > > static struct number_set trace_set[SUPPORTED_PERSONALITIES]; > > static struct number_set verbose_set[SUPPORTED_PERSONALITIES]; > > +#ifdef USE_LUAJIT > > +static struct number_set hook_entry_set[SUPPORTED_PERSONALITIES]; > > +static struct number_set hook_exit_set[SUPPORTED_PERSONALITIES]; > > +#endif > > > > static void > > number_setbit(const unsigned int i, number_slot_t *const vec) > > @@ -245,37 +274,10 @@ qualify_syscall_regex(const char *s, struct > > number_set *set) > > static unsigned int > > lookup_class(const char *s) > > { > > - static const struct { > > - const char *name; > > - unsigned int value; > > - } syscall_class[] = { > > - { "desc", TRACE_DESC }, > > - { "file", TRACE_FILE }, > > - { "memory", TRACE_MEMORY }, > > - { "process", TRACE_PROCESS }, > > - { "signal", TRACE_SIGNAL }, > > - { "ipc", TRACE_IPC }, > > - { "network", TRACE_NETWORK }, > > - { "%desc", TRACE_DESC }, > > - { "%file", TRACE_FILE }, > > - { "%memory", TRACE_MEMORY }, > > - { "%process", TRACE_PROCESS }, > > - { "%signal", TRACE_SIGNAL }, > > - { "%ipc", TRACE_IPC }, > > - { "%network", TRACE_NETWORK }, > > - { "%stat", TRACE_STAT }, > > - { "%lstat", TRACE_LSTAT }, > > - { "%fstat", TRACE_FSTAT }, > > - { "%%stat", TRACE_STAT_LIKE }, > > - { "%statfs", TRACE_STATFS }, > > - { "%fstatfs", TRACE_FSTATFS }, > > - { "%%statfs", TRACE_STATFS_LIKE }, > > - }; > > - > > - unsigned int i; > > - for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) { > > - if (strcmp(s, syscall_class[i].name) == 0) { > > - return syscall_class[i].value; > > + const struct syscall_class *c; > > + for (c = syscall_classes; c->name; ++c) { > > + if (strcmp(s, c->name) == 0) { > > + return c->value; > > } > > } > > > > @@ -693,5 +695,23 @@ qual_flags(const unsigned int scno) > > | (is_number_in_set(scno, &raw_set[current_personality]) > > ? QUAL_RAW : 0) > > | (is_number_in_set(scno, &inject_set[current_personality]) > > - ? QUAL_INJECT : 0); > > + ? QUAL_INJECT : 0) > > +#ifdef USE_LUAJIT > > + | (is_number_in_set(scno, &hook_entry_set[current_personality]) > > + ? QUAL_HOOK_ENTRY : 0) > > + | (is_number_in_set(scno, &hook_exit_set[current_personality]) > > + ? QUAL_HOOK_EXIT : 0) > > +#endif > > + ; > > +} > > + > > +#ifdef USE_LUAJIT > > +void > > +set_hook_qual(unsigned int scno, unsigned int pers, bool entry_hook, bool > > exit_hook) > > +{ > > + if (entry_hook) > > + add_number_to_set(scno, &hook_entry_set[pers]); > > + if (exit_hook) > > + add_number_to_set(scno, &hook_exit_set[pers]); > > } > > +#endif > > diff --git a/strace.1 b/strace.1 > > index af9704f5..1e12e13b 100644 > > --- a/strace.1 > > +++ b/strace.1 > > @@ -751,6 +751,13 @@ Unless this option is used setuid and setgid programs > > are executed > > without effective privileges. > > .SS Miscellaneous > > .TP 12 > > +.BI "\-l " filename > > +Load and run LuaJIT script from > > +.I filename > > +(experimental). This option is available only if > Newline before "This". > > > +.B strace > > +is built with LuaJIT scripting support. > > +.TP > > .B \-d > > Show some debugging output of > > .B strace > > @@ -766,6 +773,363 @@ Print the help summary. > > .B \-V > > Print the version number of > > .BR strace . > > +.SH LUAJIT SCRIPTING > > +If built with LuaJIT support, \fBstrace\fR can execute LuaJIT scripts. > Why "LuaJIT scripts" and not "Lua scripts"? > OK, will change this. > > +A script file is passed to the \fB\-l\fR option. > > +.PP > > +\fBstrace\fR provides the built-in module \fBstrace\fR, which contains > > various > > +functions and constants. > > +.PP > > +Before any tracing takes place, the script is run. At this stage, it can > > either: > Newline before "At". > > > +.IP \[bu] 2 > In the man-pages project ".IP \(bu 3" is usually used. > > > +implement its own tracing loop by selecting syscalls it wants to be > > notified > > +about with \fBstrace.monitor\fR/\fBstrace.monitor_all\fR and calling > I'd prefer using .B/.BR in the regular text, a it's a bit more readable. > > > +\fBstrace.next_sc\fR in a loop until it returns \fBNULL\fR (or return > > earlier; > > +in this case, the installed hooks for the remaining syscalls are run). > > +Note that \fBstrace\fR performs tracing/tampering of a syscall on the next > > +\fBstrace.next_sc\fR call; > > +.IP \[bu] > > +install syscall and at-exit hooks with \fBstrace.hook\fR, > > +\fBstrace.hook_class\fR, \fBstrace.hook_scno\fR and \fBstrace.at_exit\fR. > > +.PP > > +Then, \fBstrace\fR enters its own tracing loop, and all the installed > > hooks are > > +run. > > Some mention that FFI library is used (along with the link to its > reference) would also be nice here. > > > +.SS Example > It probably makes sense to put examples section after the reference. > And add more examples, probably. > > > +The following script counts the number of processes (including threads) > > spawned > > +by the tracee. > > +Note that you would probably want to launch \fBstrace\fR with \fB\-f\fR > > option, > > +so that children also be traced. > > +.CW > > +n = 0 > > +strace.hook({'clone', 'fork', 'vfork'}, 'exiting', function(tcp) > > + if tcp.u_rval ~= -1 then > > + n = n + 1 > > + end > > +end) > > +strace.at_exit(function() print('Processes spawned:', n) end) > > +.CE > > +.SS FFI definitions > > +.CW > > +typedef /*unspecified*/ kernel_long_t; > > +typedef /*unspecified*/ kernel_ulong_t; > Missing spaces inside comment. Also, I'd rather prefer > "implementation/architecture-specific". > > > + > > +typedef struct sysent { > > + unsigned nargs; /* Number of arguments */ > > + int sys_flags; /* Flags. Currently, only meaningful in the > > + * context of struct syscall_class::value > > field: > > + * a syscall belongs to a class iff > > + * (class.value & syscall.sys_flags) != 0. */ > > + const char *sys_name; /* Name */ > > +} struct_sysent; > > + > > +struct syscall_class { > > + const char *name; /* Name */ > > + unsigned int value; /* Flag bit, see the comment on struct > > + * sysent::sys_flags field. */ > > +}; > > + > > +typedef struct ioctlent { > > + const char *symbol; > > + unsigned int code; > > +} struct_ioctlent; > > + > > +/* Trace control block */ > > +struct tcb { > > + int pid; /* Tracee's PID */ > > + unsigned long u_error; /* Error code */ > > + kernel_ulong_t scno; /* System call number */ > > + kernel_ulong_t u_arg[/*MAX_ARGS*/]; /* System call arguments */ > Spaces around "MAX_ARGS". > > > + kernel_ulong_t u_rval; /* Return value */ > > + unsigned int currpers; /* Current personality */ > > +}; > > +.CE > > +.SS strace module: C function pointers > > +Note: be careful with boxed boolean values and use \fBnot not > > \fIboxed_bool\fR > > +when in doubt. > > +In particular, an \fBassert\fR on a boxed boolean will never raise an > > error. > > +.TP > > +\fIstatus\fR = \fBstrace.monitor\fR(\fIscno\fR, \fIpers\fR, > > \fIon_entry\fR, \fIon_exit\fR) > > +C type: > > +.B bool (*)(unsigned int, unsigned int, bool, bool) > > +.IP > > +Marks the syscall with number \fIscno\fR on personality \fIpers\fR as to be > > +returned from \fBstrace.next_sc\fR. > > Btw, personality numbers are strace's implementation details which is > not described anywhere. It's probably good idea to provide interface (in > strace.hook) which allows specifying personality names. And default for > the high-level functions should be, probably, adding hook for the all > personalities. > Yeah, I thought on that, though rather that personality names should be exposed to Lua. :) > > +If \fIon_entry\fR is \fBtrue\fR, it is marked as to be returned on syscall > > +entry, and if \fIon_exit\fR is \fBtrue\fR, it is marked as to be returned > > on > > +syscall exit. > > Here's another discrepancy with hook interface, maybe monitor shouldn't > be exposed at all. > I think for script to be able to implement its own tracing loop is a pretty important feature, and I see no way to do it without the monitor function. Maybe the hook functions should take two booleans instead of this "when" string. > > +.IP > > +Note that this "marking" is a one-way process, and specifying \fBfalse\fR > > as any > > +of the flags does not undo any previous calls to \fBstrace.monitor\fR. > > +.TP > > +\fItcp\fR = \fBstrace.next_sc\fR() > > +C type: > > +.B struct tcb * (*)(void) > > +.IP > > +If this is not the first call to \fBstrace.next_sc\fR, performs tracing and > > +tampering of the previous syscall. > > +.IP > > +Waits for the next monitored syscall to happen, and returns a pointer to > > its > > +tracing control block. > "trace control block"? > Yep. > > +.IP > > +If \fBstrace\fR needs to be terminated (e.g. last tracee has terminated, or > "has been terminated" > > > +\fBstrace\fR has been interrupted), returns \fBNULL\fR. Once it returned > Newline before "Once" > > > +\fBNULL\fR, all subsequent calls to it will also return \fBNULL\fR. > > +.TP > > +\fIstatus\fR = \fBstrace.inject_signo\fR(\fIsigno\fR) > > +C type: > > +.B bool (*)(int) > > +.IP > > +Deliver a signal with number \fIsigno\fR to the current tracee. > > +.IP > > +Note that this must be done on syscall entry. > > +.TP > > +\fIstatus\fR = \fBstrace.inject_retval\fR(\fIval\fR) > > +C type: > > +.B bool (*)(int) > > +.IP > > +Injects a return value to the current syscall invocation. > > +.IP > > +Note that this must be done on syscall entry. > > +.TP > > +\fIret\fR = \fBstrace.umove\fR(\fIaddr\fR, \fIlen\fR, \fIladdr\fR) > > +C type: > > +.B int (*)(kernel_ulong_t, size_t, void *) > > +.IP > > +Copies ("moves") \fIlen\fR bytes of data from the current tracee process at > > +address \fIaddr\fR to a local address \fIladdr\fR. > > +Returns 0 on success and \-1 on failure. > > +.TP > > +\fIret\fR = \fBstrace.umove_str\fR(\fIaddr\fR, \fIlen\fR, \fIladdr\fR) > > +C type: > > +.B int (*)(kernel_ulong_t, size_t, char *) > > +.IP > > +Like \fBstrace.umove\fR, but makes the additional effort of looking for a > > +terminating zero byte. > > +Returns a negative value on failure, a positive value if a NUL was seen, > > and 0 > > +if \fIlen\fR byes were read but no NUL seen. > > +.IP > > +Note: there is no guarantee it won't overwrite some bytes in \fIladdr\fR > > after > > +terminating NUL (but, of course, it never writes past \fIladdr[len-1]\fR). > > +.TP > > +\fIstatus\fR = \fBstrace.path_match_arr\fR(\fIset\fR, \fInset\fR) > > +C type: > > +.B bool (*)(const char **, size_t) > > +.IP > > +Returns \fBtrue\fR if the current syscall accesses one of the paths from a > > given > > +set of paths, and \fBfalse\fR otherwise. > > +.IP > > +Note: for string path arguments, the path is compared against the set; for > > file > > +descriptor arguments, the abolute path to the file behind the file > > descriptor is > > +compared against the set. > > +.SS strace module: proper functions > "proper Lua functions" > > > +Glossary: > > +.IP \[bu] 2 > > +an \fIinteger\fR means either an integer Lua number or a cdata integer > > type; > > +.IP \[bu] > > +a \fIboolean\fR means either a Lua boolean or a cdata \fIbool\fR. > > +.IP \[bu] > > +a \fIstring\fR means either a Lua string or a cdata C string. > > +.PP > > +General conventions: > > +.IP \[bu] 2 > > +a \fItcp\fR argument is either the return value of the last invocation of > > +\fBstrace.next_sc\fR (but not a null pointer), or the argument passed to a > > hook > > +callback function. > > +.IP \[bu] > > +A \fIpers\fR argument is an \fIinteger\fR and always defaults to 0. > This is definitely unexpected when tracees with other personalities > are traced. Passing tcp to all these functions may solve this. OK. > > Btw, these two enumerations have inconsistent punctuation and > capitalisation. > > > +.IP \[bu] > > +An \fIaddr\fR argument is a cdata \fIkernel_ulong_t\fR. > > +.TP > > +\fIstatus\fR = \fBstrace.entering\fR(\fItcp\fR) > > +Returns \fBtrue\fR if this is a syscall entry, and \fBfalse\fR otherwise. > > +.TP > > +\fIstatusfR = \fBstrace.exiting\fR(\fItcp\fR) > > +Returns \fBtrue\fR if this is a syscall exit, and \fBfalse\fR otherwise. > > +.TP > > +\fBstrace.trace\fR(\fItcp\fR[, \fIflag\fR]) > > +.TP > > +\fBstrace.abbrev\fR(\fItcp\fR[, \fIflag\fR]) > > +.TP > > +\fBstrace.verbose\fR(\fItcp\fR[, \fIflag\fR]) > > +.TP > > +\fBstrace.raw\fR(\fItcp\fR[, \fIflag\fR]) > > +These functions alter corresponding trace options. > > +\fIflag\fR is a \fIboolean\fR, defaults to \fBtrue\fR. > > +.TP > > +\fIaddr\fR = \fBstrace.ptr_to_kulong\fR(\fIptr\fR) > > +Converts a cdata pointer to a \fBkernel_ulong_t\fR. > > +.TP > > +\fBstrace.at_exit\fR(\fIfunc\fR) > > +Registers a function \fIfunc\fR to be run when \fBstrace\fR needs to > > +be terminated. > > +.TP > > +\fIname\fR = \fBstrace.get_err_name\fR(\fIerrno\fR[, \fIpers\fR]) > > +Returns error name (e.g. \fB"ENOENT"\fR) as Lua string by its errno number > > +\fIerrno\fR for personality \fIpers\fR, or \fBnil\fR if \fIerrno\fR is > > invalid. > > +.IP > > +\fIerrno\fR is an \fIinteger\fR. > > +.TP > > +\fIname\fR = \fBstrace.get_sc_name\fR(\fIscno\fR[, \fIpers\fR]) > > +Returns syscall name as Lua string by its number for personality > > \fIpers\fR, or > > +\fBnil\fR if \fIscno\fR is invalid. > > +.IP > > +\fIscno\fR is an \fIinteger\fR. > > +.TP > > +\fIname\fR = \fBstrace.get_ioctl_name\fR(\fIreqcode\fR[, \fIpers\fR]) > > +Returns ioctl symbol name (e.g. \fB"TIOCGWINSZ"\fR) as Lua string by its > > request > > +code \fIreqcode\fR for personality \fIpers\fR, or \fBnil\fR if > > \fIreqcode\fR is > > +invalid. > > +.IP > > +\fIreqcode\fR is an \fIinteger\fR. > > +.TP > > +\fIscno\fR = \fBstrace.get_scno\fR(\fIscname\fR[, \fIpers\fR]) > > +Returns syscall number by its name for personality \fIpers\fR, or > > \fBnil\fR if > > +no such syscall was found. > > +.IP > > +\fIscname\fR is a \fIstring\fR. > > +.TP > > +\fIsigno\fR = \fBstrace.get_signo\fR(\fIsigname\fR[, \fIpers\fR]) > > +Returns signal number by its name (e.g. \fB"SIGSEGV"\fR) for personality > > +\fIpers\fR, or \fBnil\fR if no such signal was found. > > +.IP > > +\fIsigname\fR is \fIstring\fR. > > +.TP > > +\fIerrno\fR = \fBstrace.get_errno\fR(\fIerrname\fR[, \fIpers\fR]) > > +Returns errno number by its name (e.g. \fB"ENOENT"\fR) for personality > "error number" > > > +\fIpers\fR, or \fBnil\fR if no such error was found. > > +.IP > > +\fIsigname\fR is a \fIstring\fR. > > +.TP > > +\fBstrace.inject_signal\fR(\fItcp\fR, \fIsig\fR) > > +Delivers a signal to the tracee. > > +\fIsig\fR is either signal number (an \fIinteger\fR) or name (a > > \fIstring\fR). > > +.IP > > +Note that this must be done on syscall entry. > > +.IP > > +Raises an error on failure. > > +.TP > > +\fBstrace.inject_error\fR(\fItcp\fR, \fIerr\fR) > > +Injects an error into a current syscall invocation. > > +\fIerr\fR is either errno number (an \fIinteger\fR) or error name (a > > +\fIstring\fR). > > +.IP > > +Note that this must be done on syscall entry. > > +.IP > > +Raises an error on failure. > > +.TP > > +\fIobj\fR = \fBstrace.read_obj\fR(\fIaddr\fR, \fIct\fR[, \fInelem\fR]) > > +Reads an object of type \fIct\fR from the current tracee process at address > > +\fIaddr\fR. > > +\fIct\fR is either a \fIcdecl\fR (a Lua string), a \fIcdata\fR serving as a > > +template type, or a \fIctype\fR (special kind of \fIcdata\fR returned by > > +\fBffi.typeof\fR). > > +.IP > > +VLA/VLS types require the \fInelem\fR argument (an \fIinteger\fR). > > Note that tracee with different personality than strace has different > type sizes and structure layouts (that's why all the mpers stuff). > Umm... OK, but what exactly should I note here? > > +.IP > > +Returns an object on success and \fBnil\fR on failure. > > +.TP > > +\fIstr\fR[, \fIerr_msg\fR] = \fBstrace.read_str\fR(\fIaddr\fR[, > > \fImaxsz\fR[, \fIbufsz\fR]]) > > +Reads a C string from the current tracee process at address \fIaddr\fR > > using an > > +intermediate buffer of size \fIbufsz\fR and stopping at \fImaxsz\fR bytes. > > +.IP > > +\fImaxsz\fR and \fIbufsz\fR are \fIintegers\fR. > > +\fImaxsz\fR defaults to 4 Mb, \fIbufsz\fR to 1 Kb. > > +.IP > > +Returns a Lua string on success, \fBnil, "readerr"\fR on read error, and > > +\fBnil, "toolong"\fR if the \fImaxsz\fR limit was exceeded. > > +.TP > > +\fIstr\fR[, \fIerr_msg\fR] = \fBstrace.read_path\fR(\fIaddr\fR) > > +Reads a path C string from the current tracee process at address > > \fIaddr\fR. > > +.IP > > +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.monitor_all\fR() > > +Marks all syscalls on all personalities as to be returned from > > +\fBstrace.next_sc\fR both on entry and on exit. > > +.TP > > +\fBstrace.hook\fR(\fIscname\fR, \fIwhen\fR, \fIcb\fR) > > +.TP > > +\fBstrace.hook_class\fR(\fIclsname\fR, \fIwhen\fR, \fIcb\fR) > > +.TP > > +\fBstrace.hook_scno\fR(\fIscno\fR, \fIwhen\fR, \fIcb\fR[, \fIpers\fR]) > > +These functions register a function \fIcb\fR to be run when a syscall with > > the > > +given name (or with a name from a given set thereof), belonging to a class > > with > > +the given name (or with a name from a given set thereof), or with the given > > +number (or with a number from a given set thereof) on personality > > \fIpers\fR, > > +happens. > > +.IP > > +It will be run on syscall entry if \fIwhen\fR is \fB"entering"\fR, syscall > > exit > > +if \fIwhen\fR is \fB"exiting"\fR, or both if \fIwhen\fR is \fB"both"\fR > > +(\fIwhen\fR is a Lua string). > > +.IP > > +A pointer to the trace control block is passed as the only argument to > > \fIcb\fR. > > +.IP > > +\fIscname\fR and \fIclsname\fR are either \fIstrings\fR or tables thereof. > > +\fIscno\fR is enther an \fIinteger\fR or a table thereof. > > +.IP > > +Raise an error on failure. > > +.TP > > +\fIstatus\fR = \fBstrace.path_match\fR(\fIset\fR) > > +Returns \fBtrue\fR if the current syscall accesses a given path, or one of > > the > > +paths from the given set of paths; and \fBfalse\fR otherwise (see the note > > for > > +\fBstrace.path_match_arr\fR). > > +.IP > > +\fIset\fR is either a \fIstring\fR or a table thereof. > > +.TP > > +\fIstr\fR = \fBstrace.get_fd_path\fR(\fItcp\fR, \fIfd\fR) > > +Returns path associated with file descriptor \fIfd\fR for the current > > tracee > > +process as a Lua string, or \fBnil\fR on failure. > > +.IP > > +\fIfd\fR is an \fIinteger\fR. > > +.SS strace module: constants > > +.TP > > +.B strace.npersonalities > > +Number of supported personalities (an integer Lua number). > > +.TP > > +.B strace.max_args > > +Size of \fBstruct tcb::u_arg\fR array (an integer Lua number). > > +.TP > > +.B strace.path_max > > +Value of \fBPATH_MAX\fR constant (an integer Lua number). > > +.TP > > +.B strace.sysent_vec > > +Array of syscall tables for each of the supported personalities. > > +\fBstrace.sysent_vec\fR[\fIpers\fR][\fIscno\fR] is a \fBstruct_sysent\fR > > for > > +syscall number \fIscno\fR on personality \fIpers\fR. > > +May contain null entries (which have a NULL \fBsys_name\fR field). > > +.TP > > +.B strace.errnoent_vec > > +Array of error name tables for each of the supported personalities. > > +\fBstrace.errnoent_vec\fR[\fIpers\fR][\fIerrno\fR] is either a null > > pointer or a > > +C string with the name of error \fIerrno\fR on personality \fIpers\fR. > > +.TP > > +.B strace.signalent_vec > > +Array of signal name tables for each of the supported personalities. > > +\fBstrace.signalent_vec\fR[\fIpers\fR][\fIsigno\fR] is either a null > > pointer or > > +a C string with the name of signal \fIsigno\fR on personality \fIpers\fR. > > +.TP > > +.B strace.ioctlent_vec > > +Arrays of sorted known ioctl symbols, sorted by code, for each of the > > supported > > +personalities. > > +\fBstrace.ioctlent_vec\fR[\fIpers\fR][\fIi\fR] is the \fIi\fR-th, ranked by > > +code, \fBstruct_ioctlent\fR for personality \fIpers\fR. > > +.TP > > +.B strace.nsysent_vec > > +.TP > > +.B strace.nerrnoent_vec > > +.TP > > +.B strace.nsignalent_vec > > +.TP > > +.B strace.nioctlent_vec > > +These are \fBstrace.npersonalities\fR-sized arrays containing sizes of > > subarrays > > +of > > +.BR strace.sysent_vec ", " strace.errnoent_vec ", " strace.signalent_vec > > ", and " strace.ioctlent_vec > > +correspondingly. > > +.TP > > +.B strace.syscall_classes > > +Array of \fBstruct syscall_class\fR, with a terminating null entry (which > > has a > > +NULL \fBname\fR field). > > .SH DIAGNOSTICS > > When > > .I command > > diff --git a/strace.c b/strace.c > > index 955a1c9f..1ec38112 100644 > > --- a/strace.c > > +++ b/strace.c > > @@ -45,6 +45,9 @@ > > # include <sys/prctl.h> > > #endif > > #include <asm/unistd.h> > > +#ifdef USE_LUAJIT > > +# include <lua.h> > > +#endif > > > > #include "scno.h" > > #include "ptrace.h" > > @@ -169,6 +172,11 @@ static volatile sig_atomic_t interrupted; > > static volatile int interrupted; > > #endif > > > > +#ifdef USE_LUAJIT > > +static lua_State *script_L = NULL; > > +static void init_luajit(const char *scriptfile); > > +#endif > > + > > #ifndef HAVE_STRERROR > > > > #if !HAVE_DECL_SYS_ERRLIST > > @@ -219,6 +227,11 @@ Output format:\n\ > > -k obtain stack trace between each syscall (experimental)\n\ > > " > > #endif > > +#ifdef USE_LUAJIT > > +"\ > > + -l file run a Lua script from FILE\n\ > > +" > > +#endif > > "\ > > -o file send trace output to FILE instead of stderr\n\ > > -q suppress messages about attaching, detaching, etc.\n\ > > @@ -772,7 +785,7 @@ alloctcb(int pid) > > if (!tcp->pid) { > > memset(tcp, 0, sizeof(*tcp)); > > tcp->pid = pid; > > -#if SUPPORTED_PERSONALITIES > 1 > > +#if defined(USE_LUAJIT) || SUPPORTED_PERSONALITIES > 1 > > tcp->currpers = current_personality; > > #endif > > > > @@ -828,6 +841,10 @@ droptcb(struct tcb *tcp) > > if (tcp->pid == 0) > > return; > > > > +#ifdef USE_LUAJIT > > + free(tcp->ad_hoc_inject_opts); > > +#endif > > + > > int p; > > for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) > > free(tcp->inject_vec[p]); > > @@ -1648,6 +1665,9 @@ init(int argc, char *argv[]) > > #ifdef USE_LIBUNWIND > > "k" > > #endif > > +#ifdef USE_LUAJIT > > + "l:" > > +#endif > Side note: as a list of optional features grows, we probably need > some indication which features strace binary supports, somewhere in > strace -V, probably. > > > "D" > > "a:e:o:O:p:s:S:u:E:P:I:")) != EOF) { > > switch (c) { > > @@ -1758,6 +1778,11 @@ init(int argc, char *argv[]) > > stack_trace_enabled = true; > > break; > > #endif > > +#ifdef USE_LUAJIT > > + case 'l': > > + init_luajit(optarg); > > + break; > > +#endif > > case 'E': > > if (putenv(optarg) < 0) > > perror_msg_and_die("putenv"); > > @@ -2639,6 +2664,10 @@ terminate(void) > > exit(exit_code); > > } > > > > +#ifdef USE_LUAJIT > > +# include "luajit.h" > > +#endif > > + > > int > > main(int argc, char *argv[]) > > { > > @@ -2646,6 +2675,11 @@ main(int argc, char *argv[]) > > > > exit_code = !nprocs; > > > > +#ifdef USE_LUAJIT > > + if (script_L) > > + run_luajit(); > > +#endif > > + > > int status; > > siginfo_t si; > > while (dispatch_event(next_event(&status, &si), &status, &si)) > > diff --git a/syscall.c b/syscall.c > > index 02626c73..955ed2f4 100644 > > --- a/syscall.c > > +++ b/syscall.c > > @@ -196,6 +196,16 @@ enum { > > #endif > > }; > > > > +const char *const *errnoent_vec[SUPPORTED_PERSONALITIES] = { > > + errnoent0, > > +#if SUPPORTED_PERSONALITIES > 1 > > + errnoent1, > > +# if SUPPORTED_PERSONALITIES > 2 > > + errnoent2, > > +# endif > > +#endif > > +}; > > + > > enum { > > nerrnos0 = ARRAY_SIZE(errnoent0) > > #if SUPPORTED_PERSONALITIES > 1 > > @@ -206,6 +216,16 @@ enum { > > #endif > > }; > > > > +const unsigned int nerrnoent_vec[] = { > > + nerrnos0, > > +#if SUPPORTED_PERSONALITIES > 1 > > + nerrnos1, > > +# if SUPPORTED_PERSONALITIES > 2 > > + nerrnos2, > > +# endif > > +#endif > > +}; > > + > > enum { > > nsignals0 = ARRAY_SIZE(signalent0) > > #if SUPPORTED_PERSONALITIES > 1 > > @@ -216,6 +236,26 @@ enum { > > #endif > > }; > > > > +const char *const *signalent_vec[SUPPORTED_PERSONALITIES] = { > > + signalent0, > > +#if SUPPORTED_PERSONALITIES > 1 > > + signalent1, > > +# if SUPPORTED_PERSONALITIES > 2 > > + signalent2, > > +# endif > > +#endif > > +}; > > + > > +const unsigned int nsignalent_vec[] = { > > + nsignals0, > > +#if SUPPORTED_PERSONALITIES > 1 > > + nsignals1, > > +# if SUPPORTED_PERSONALITIES > 2 > > + nsignals2, > > +# endif > > +#endif > > +}; > > + > > enum { > > nioctlents0 = ARRAY_SIZE(ioctlent0) > > #if SUPPORTED_PERSONALITIES > 1 > > @@ -226,6 +266,26 @@ enum { > > #endif > > }; > > > > +const unsigned int nioctlent_vec[] = { > > + nioctlents0, > > +#if SUPPORTED_PERSONALITIES > 1 > > + nioctlents1, > > +# if SUPPORTED_PERSONALITIES > 2 > > + nioctlents2, > > +# endif > > +#endif > > +}; > > + > > +const struct_ioctlent *const ioctlent_vec[SUPPORTED_PERSONALITIES] = { > > + ioctlent0, > > +#if SUPPORTED_PERSONALITIES > 1 > > + ioctlent1, > > +# if SUPPORTED_PERSONALITIES > 2 > > + ioctlent2, > > +# endif > > +#endif > > +}; > > + > > #if SUPPORTED_PERSONALITIES > 1 > > const struct_sysent *sysent = sysent0; > > const char *const *errnoent = errnoent0; > > @@ -580,8 +640,19 @@ static int arch_set_success(struct tcb *); > > struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES]; > > > > static struct inject_opts * > > -tcb_inject_opts(struct tcb *tcp) > > +tcb_inject_opts(struct tcb *tcp, bool copy_if_needed) > > { > > +#ifdef USE_LUAJIT > > + if (tcp->flags & TCB_AD_HOC_INJECT) > > + return tcp->ad_hoc_inject_opts; > > +#endif > > + if (copy_if_needed && !tcp->inject_vec[current_personality]) { > > + tcp->inject_vec[current_personality] = > > + xcalloc(nsyscalls, sizeof(**inject_vec)); > > + memcpy(tcp->inject_vec[current_personality], > > + inject_vec[current_personality], > > + nsyscalls * sizeof(**inject_vec)); > > + } > > return (scno_in_range(tcp->scno) && > > tcp->inject_vec[current_personality]) > > ? &tcp->inject_vec[current_personality][tcp->scno] : NULL; > > } > > @@ -590,15 +661,7 @@ tcb_inject_opts(struct tcb *tcp) > > static long > > tamper_with_syscall_entering(struct tcb *tcp, unsigned int *signo) > > { > > - if (!tcp->inject_vec[current_personality]) { > > - tcp->inject_vec[current_personality] = > > - xcalloc(nsyscalls, sizeof(**inject_vec)); > > - memcpy(tcp->inject_vec[current_personality], > > - inject_vec[current_personality], > > - nsyscalls * sizeof(**inject_vec)); > > - } > > - > > - struct inject_opts *opts = tcb_inject_opts(tcp); > > + struct inject_opts *opts = tcb_inject_opts(tcp, true); > > > > if (!opts || opts->first == 0) > > return 0; > > @@ -621,7 +684,7 @@ tamper_with_syscall_entering(struct tcb *tcp, unsigned > > int *signo) > > static long > > tamper_with_syscall_exiting(struct tcb *tcp) > > { > > - struct inject_opts *opts = tcb_inject_opts(tcp); > > + struct inject_opts *opts = tcb_inject_opts(tcp, false); > > > > if (!opts) > > return 0; > > @@ -699,6 +762,12 @@ syscall_entering_decode(struct tcb *tcp) > > return 1; > > } > > > > +static bool > > +syscall_ad_hoc_injected(struct tcb *tcp) > > +{ > > + return (tcp->qual_flg & QUAL_INJECT) && (tcp->flags & > > TCB_AD_HOC_INJECT); > > +} > > + > > int > > syscall_entering_trace(struct tcb *tcp, unsigned int *sig) > > { > > @@ -721,13 +790,13 @@ syscall_entering_trace(struct tcb *tcp, unsigned int > > *sig) > > || (tracing_paths && !pathtrace_match(tcp)) > > ) { > > tcp->flags |= TCB_FILTERED; > > - return 0; > > + goto maybe_ad_hoc_tamper; > > } > > > > tcp->flags &= ~TCB_FILTERED; > > > > if (hide_log(tcp)) { > > - return 0; > > + goto maybe_ad_hoc_tamper; > > } > > > > if (tcp->qual_flg & QUAL_INJECT) > > @@ -750,6 +819,11 @@ syscall_entering_trace(struct tcb *tcp, unsigned int > > *sig) > > ? printargs(tcp) : tcp->s_ent->sys_func(tcp); > > fflush(tcp->outf); > > return res; > > + > > +maybe_ad_hoc_tamper: > > + if (syscall_ad_hoc_injected(tcp)) > > + tamper_with_syscall_entering(tcp, sig); > > + return 0; > > } > > > > void > > @@ -790,21 +864,28 @@ syscall_exiting_decode(struct tcb *tcp, struct > > timeval *ptv) > > } > > #endif > > > > - if (filtered(tcp) || hide_log(tcp)) > > + if ((filtered(tcp) || hide_log(tcp)) > > + && !(tcp->qual_flg & QUAL_HOOK_EXIT) && !syscall_ad_hoc_injected(tcp)) > > return 0; > > > > get_regs(tcp->pid); > > #if SUPPORTED_PERSONALITIES > 1 > > update_personality(tcp, tcp->currpers); > > #endif > > - return get_regs_error ? -1 : get_syscall_result(tcp); > > + if (get_regs_error || get_syscall_result(tcp) == -1) > > + return -1; > > + > > + if (syserror(tcp) && syscall_tampered(tcp)) > > + tamper_with_syscall_exiting(tcp); > > + > > + return 1; > > } > > > > int > > syscall_exiting_trace(struct tcb *tcp, struct timeval tv, int res) > > { > > - if (syserror(tcp) && syscall_tampered(tcp)) > > - tamper_with_syscall_exiting(tcp); > > + if (filtered(tcp) || hide_log(tcp)) > > + return 0; > > > > if (cflag) { > > count_syscall(tcp, &tv); > > @@ -1013,7 +1094,7 @@ syscall_exiting_trace(struct tcb *tcp, struct timeval > > tv, int res) > > void > > syscall_exiting_finish(struct tcb *tcp) > > { > > - tcp->flags &= ~(TCB_INSYSCALL | TCB_TAMPERED); > > + tcp->flags &= ~(TCB_INSYSCALL | TCB_TAMPERED | TCB_AD_HOC_INJECT); > > tcp->sys_func_rval = 0; > > free_tcb_priv_data(tcp); > > } > > diff --git a/sysent.h b/sysent.h > > index 92de7468..15b83693 100644 > > --- a/sysent.h > > +++ b/sysent.h > > @@ -1,13 +1,31 @@ > > -#ifndef STRACE_SYSENT_H > > -#define STRACE_SYSENT_H > > +#if !defined(STRACE_SYSENT_H) || defined(FFI_CDEF) > > +#ifndef FFI_CDEF > > +# define STRACE_SYSENT_H > > +#endif > > > > +#include "ffi.h" > > + > > +FFI_CONTENT( > > typedef struct sysent { > > unsigned nargs; > > int sys_flags; > > +) > > +/* We don't want to expose sen and sys_func to LuaJIT */ > > +#ifdef FFI_CDEF > > +FFI_CONTENT( > > + int priv1; > > + void *priv2; > > +) > > +#else > > +FFI_CONTENT( > > int sen; > > int (*sys_func)(); > > +) > > +#endif > > +FFI_CONTENT( > > const char *sys_name; > > } struct_sysent; > > +) > > > > #define TRACE_FILE 00000001 /* Trace file-related > > syscalls. */ > > #define TRACE_IPC 00000002 /* Trace IPC-related > > syscalls. */ > > @@ -29,4 +47,4 @@ typedef struct sysent { > > #define TRACE_FSTAT 00400000 /* Trace > > *fstat{,at}{,64} syscalls. */ > > #define TRACE_STAT_LIKE 01000000 /* Trace > > *{,l,f}stat{,x,at}{,64} syscalls. */ > > > > -#endif /* !STRACE_SYSENT_H */ > > +#endif /* !defined(STRACE_SYSENT_H) || defined(FFI_CDEF) */ > > -- > i> 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 ------------------------------------------------------------------------------ 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