* Makefile.am (strace_SOURCES): Add defs_shared.h and ffi.h. * basic_filters.c (syscall_classes): make global and terminate by a null entry. (lookup_class): use global null entry-terminated variable. * defs.h (TCB_AD_HOC_INJECT, TCB_HOOK): New TCB flags. (QUAL_HOOK_ENTRY, QUAL_HOOK_EXIT): New qualifier flags. (RVAL_HOOKED): New return value flag. (struct ioctlent, struct tcb): move to... * defs_shared.h: ...new file. * ffi.h: New file. * filter_qualify.c (qual_flags): Introduce QUALBIT macro and use it. * number_set.c (number_unsetbit, remove_number_from_set, extend_set_with_number, make_number_set_universal, extend_set_array_with_number, make_number_set_array_universal): New functions. * strace.c (enum trace_event): Introduce new TE_SYSCALL_STOP_HOOK_EXIT trace event. (enum hook_state): New enumeration. (trace_syscall): Add a hook state argument. (dispatch_event): Add a "hooked" argument, support invoking with TE_SYSCALL_STOP_HOOK_EXIT event. * syscall.c: (errnoent_vec, nerrnoent_vec, signalent_vec, nsignalent_vec, ioctlent_vec, nioctlent_vec, personality_wordsize, personality_klongsize, personality_names): New global variables. (update_personality): Use personality_names for reporting personality name. (tcb_inject_opts): Introduce step argument, change return type to struct inject_data, rename to tcb_inject_data. (tamper_with_syscall_entering): Don't copy inject_vec here and do counter decrement logic here; pass true as a second argument to tcb_inject_data. (tamper_with_syscall_exiting): Pass false as a second argument to tcb_inject_data. (syscall_ad_hoc_inject): New function. (syscall_entering_trace): Perform ad hoc injection even if the syscall is not traced. (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 inclusing with FFI_CDEF defined. --- Makefile.am | 2 + basic_filters.c | 60 ++++++++-------- defs.h | 57 +++++---------- defs_shared.h | 63 ++++++++++++++++ ffi.h | 19 +++++ filter_qualify.c | 20 +++--- number_set.c | 47 ++++++++++++ number_set.h | 16 +++++ strace.c | 63 ++++++++++++---- syscall.c | 216 +++++++++++++++++++++++++++++++++++++++---------------- sysent.h | 24 ++++++- 11 files changed, 430 insertions(+), 157 deletions(-) create mode 100644 defs_shared.h create mode 100644 ffi.h
diff --git a/Makefile.am b/Makefile.am index 4aa9846c..0ab788b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -107,6 +107,7 @@ strace_SOURCES = \ copy_file_range.c \ count.c \ defs.h \ + defs_shared.h \ desc.c \ dirent.c \ dirent64.c \ @@ -132,6 +133,7 @@ strace_SOURCES = \ fetch_struct_stat.c \ fetch_struct_stat64.c \ fetch_struct_statfs.c \ + ffi.h \ file_handle.c \ file_ioctl.c \ filter_qualify.c \ diff --git a/basic_filters.c b/basic_filters.c index 7b7f0a54..0f31fd32 100644 --- a/basic_filters.c +++ b/basic_filters.c @@ -94,40 +94,38 @@ qualify_syscall_regex(const char *s, struct number_set *set) return found; } +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 }, + {} +}; + 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; } } diff --git a/defs.h b/defs.h index 34261f2e..b6828748 100644 --- a/defs.h +++ b/defs.h @@ -175,11 +175,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; - #define INJECT_F_SIGNAL 1 #define INJECT_F_RETVAL 2 @@ -197,39 +192,7 @@ struct inject_opts { #define MAX_ERRNO_VALUE 4095 -/* Trace Control Block */ -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 */ @@ -253,6 +216,8 @@ 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 */ +#define TCB_HOOK 0x400 /* there is Lua hook for this syscall entry or exit */ /* qualifier flags */ #define QUAL_TRACE 0x001 /* this system call should be traced */ @@ -260,6 +225,8 @@ struct tcb { #define QUAL_VERBOSE 0x004 /* decode the structures of this syscall */ #define QUAL_RAW 0x008 /* print all args in hex for this syscall */ #define QUAL_INJECT 0x010 /* tamper with this system call on purpose */ +#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) @@ -318,6 +285,8 @@ extern const struct xlat whence_codes[]; #define RVAL_IOCTL_DECODED 0200 /* ioctl sub-parser successfully decoded the argument */ +#define RVAL_HOOKED 0400 /* there is Lua hook for this syscall entry or exit */ + #define IOCTL_NUMBER_UNKNOWN 0 #define IOCTL_NUMBER_HANDLED 1 #define IOCTL_NUMBER_STOP_LOOKUP 010 @@ -362,6 +331,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; @@ -950,6 +920,17 @@ 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]; + +extern const int personality_wordsize[SUPPORTED_PERSONALITIES]; +extern const int personality_klongsize[SUPPORTED_PERSONALITIES]; +extern const char *const personality_names[]; + #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..1113fc0c --- /dev/null +++ b/defs_shared.h @@ -0,0 +1,63 @@ +/* + * 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; +) + +/* Trace Control Block */ +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 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 */ + 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/filter_qualify.c b/filter_qualify.c index 5b0ef28c..71766fd1 100644 --- a/filter_qualify.c +++ b/filter_qualify.c @@ -360,14 +360,14 @@ qualify(const char *str) unsigned int qual_flags(const unsigned int scno) { - return (is_number_in_set_array(scno, trace_set, current_personality) - ? QUAL_TRACE : 0) - | (is_number_in_set_array(scno, abbrev_set, current_personality) - ? QUAL_ABBREV : 0) - | (is_number_in_set_array(scno, verbose_set, current_personality) - ? QUAL_VERBOSE : 0) - | (is_number_in_set_array(scno, raw_set, current_personality) - ? QUAL_RAW : 0) - | (is_number_in_set_array(scno, inject_set, current_personality) - ? QUAL_INJECT : 0); +#define QUALBIT(set, qualbit) \ + (is_number_in_set_array(scno, set, current_personality) ? qualbit : 0) + + return QUALBIT(trace_set, QUAL_TRACE) + | QUALBIT(abbrev_set, QUAL_ABBREV) + | QUALBIT(verbose_set, QUAL_VERBOSE) + | QUALBIT(raw_set, QUAL_RAW) + | QUALBIT(inject_set, QUAL_INJECT) + ; +#undef QUALBIT } diff --git a/number_set.c b/number_set.c index b8aa28c7..a2e049a1 100644 --- a/number_set.c +++ b/number_set.c @@ -50,6 +50,12 @@ number_setbit(const unsigned int i, number_slot_t *const vec) vec[i / BITS_PER_SLOT] |= (number_slot_t) 1 << (i % BITS_PER_SLOT); } +static void +number_unsetbit(const unsigned int i, number_slot_t *const vec) +{ + vec[i / BITS_PER_SLOT] &= ~((number_slot_t) 1 << (i % BITS_PER_SLOT)); +} + static bool number_isset(const unsigned int i, const number_slot_t *const vec) { @@ -97,6 +103,29 @@ add_number_to_set(const unsigned int number, struct number_set *const set) } void +remove_number_from_set(const unsigned int number, struct number_set *const set) +{ + if (number / BITS_PER_SLOT < set->nslots) + number_unsetbit(number, set->vec); +} + +void +extend_set_with_number(const unsigned int number, struct number_set *const set) +{ + if (set->not) + remove_number_from_set(number, set); + else + add_number_to_set(number, set); +} + +void +make_number_set_universal(struct number_set *const set) +{ + free(set->vec); + *set = (struct number_set) { .not = true }; +} + +void add_number_to_set_array(const unsigned int number, struct number_set *const set, const unsigned int idx) { @@ -104,6 +133,14 @@ add_number_to_set_array(const unsigned int number, struct number_set *const set, } void +extend_set_array_with_number(const unsigned int number, + struct number_set *const set, + const unsigned int idx) +{ + extend_set_with_number(number, &set[idx]); +} + +void clear_number_set_array(struct number_set *const set, const unsigned int nmemb) { unsigned int i; @@ -125,6 +162,16 @@ invert_number_set_array(struct number_set *const set, const unsigned int nmemb) set[i].not = !set[i].not; } +void +make_number_set_array_universal(struct number_set *set, + const unsigned int nmemb) +{ + unsigned int i; + + for (i = 0; i < nmemb; ++i) + make_number_set_universal(&set[i]); +} + struct number_set * alloc_number_set_array(const unsigned int nmemb) { diff --git a/number_set.h b/number_set.h index ec53bc1b..953b1545 100644 --- a/number_set.h +++ b/number_set.h @@ -45,6 +45,22 @@ extern void add_number_to_set(unsigned int number, struct number_set *); extern void +remove_number_from_set(unsigned int number, struct number_set *); + +extern void +extend_set_with_number(unsigned int number, struct number_set *); + +extern void +make_number_set_universal(struct number_set *); + +extern void +make_number_set_array_universal(struct number_set *, unsigned int nmemb); + +extern void +extend_set_array_with_number(const unsigned int number, + struct number_set *const set, const unsigned int idx); + +extern void add_number_to_set_array(unsigned int number, struct number_set *, unsigned int idx); extern void diff --git a/strace.c b/strace.c index 6ed86a6f..7b8a0e24 100644 --- a/strace.c +++ b/strace.c @@ -2216,6 +2216,9 @@ enum trace_event { */ TE_SYSCALL_STOP, + /* Syscall entry or exit, after hook. */ + TE_SYSCALL_STOP_HOOK_EXIT, + /* * Tracee received signal with number WSTOPSIG(*pstatus); signal info * is written to *si. Restart the tracee (with that signal number @@ -2406,24 +2409,47 @@ next_event(int *pstatus, siginfo_t *si) } } +enum hook_state { + HOOK_ENTER, + HOOK_EXIT, + HOOK_IGNORE, +}; + static int -trace_syscall(struct tcb *tcp, unsigned int *sig) +trace_syscall(struct tcb *tcp, unsigned int *sig, enum hook_state state) { if (entering(tcp)) { - int res = syscall_entering_decode(tcp); - switch (res) { - case 0: - return 0; - case 1: + int res; + switch (state) { + case HOOK_ENTER: + case HOOK_IGNORE: + res = syscall_entering_decode(tcp); + if (res == 0) + return 0; + if (res == 1) + if (state == HOOK_ENTER && + (tcp->qual_flg & QUAL_HOOK_ENTRY)) + return RVAL_HOOKED; + /* Fall through */ + case HOOK_EXIT: res = syscall_entering_trace(tcp, sig); } syscall_entering_finish(tcp, res); return res; } else { - struct timeval tv = {}; - int res = syscall_exiting_decode(tcp, &tv); - if (res != 0) { - res = syscall_exiting_trace(tcp, tv, res); + static struct timeval tv; + int res = 1; + switch (state) { + case HOOK_ENTER: + case HOOK_IGNORE: + res = syscall_exiting_decode(tcp, &tv); + if (res == 1 && state == HOOK_ENTER && + (tcp->qual_flg & QUAL_HOOK_EXIT)) + return RVAL_HOOKED; + /* Fall through */ + case HOOK_EXIT: + if (res != 0) + res = syscall_exiting_trace(tcp, tv, res); } syscall_exiting_finish(tcp); return res; @@ -2432,10 +2458,11 @@ trace_syscall(struct tcb *tcp, unsigned int *sig) /* Returns true iff the main trace loop has to continue. */ static bool -dispatch_event(enum trace_event ret, int *pstatus, siginfo_t *si) +dispatch_event(enum trace_event ret, int *pstatus, siginfo_t *si, bool hooked) { unsigned int restart_op = PTRACE_SYSCALL; unsigned int restart_sig = 0; + int res; switch (ret) { case TE_BREAK: @@ -2448,7 +2475,17 @@ dispatch_event(enum trace_event ret, int *pstatus, siginfo_t *si) break; case TE_SYSCALL_STOP: - if (trace_syscall(current_tcp, &restart_sig) < 0) { + case TE_SYSCALL_STOP_HOOK_EXIT: + res = trace_syscall(current_tcp, &restart_sig, + hooked ? (ret == TE_SYSCALL_STOP ? HOOK_ENTER : HOOK_EXIT) : + HOOK_IGNORE); + + if (res == RVAL_HOOKED) { + current_tcp->flags |= TCB_HOOK; + return true; + } + + if (res < 0) { /* * ptrace() failed in trace_syscall(). * Likely a result of process disappearing mid-flight. @@ -2593,7 +2630,7 @@ main(int argc, char *argv[]) int status; siginfo_t si; - while (dispatch_event(next_event(&status, &si), &status, &si)) + while (dispatch_event(next_event(&status, &si), &status, &si, false)) ; terminate(); } diff --git a/syscall.c b/syscall.c index b1047feb..363d65c3 100644 --- a/syscall.c +++ b/syscall.c @@ -160,6 +160,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 @@ -170,6 +180,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 @@ -180,6 +200,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 @@ -190,6 +230,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; @@ -222,29 +282,51 @@ const struct_sysent *const sysent_vec[SUPPORTED_PERSONALITIES] = { #endif }; +const int personality_wordsize[SUPPORTED_PERSONALITIES] = { + PERSONALITY0_WORDSIZE, +#if SUPPORTED_PERSONALITIES > 1 + PERSONALITY1_WORDSIZE, +#endif +#if SUPPORTED_PERSONALITIES > 2 + PERSONALITY2_WORDSIZE, +#endif +}; + +const int personality_klongsize[SUPPORTED_PERSONALITIES] = { + PERSONALITY0_KLONGSIZE, +#if SUPPORTED_PERSONALITIES > 1 + PERSONALITY1_KLONGSIZE, +#endif +#if SUPPORTED_PERSONALITIES > 2 + PERSONALITY2_KLONGSIZE, +#endif +}; + +const char *const personality_names[] = +# if defined X86_64 + {"64 bit", "32 bit", "x32"} +# elif defined X32 + {"x32", "32 bit"} +# elif SUPPORTED_PERSONALITIES == 2 + {"64 bit", "32 bit"} +# else + {STRINGIFY_VAL(__WORDSIZE) " bit"} +# endif + ; + #if SUPPORTED_PERSONALITIES > 1 + unsigned current_personality; # ifndef current_wordsize unsigned current_wordsize; -static const int personality_wordsize[SUPPORTED_PERSONALITIES] = { - PERSONALITY0_WORDSIZE, - PERSONALITY1_WORDSIZE, -# if SUPPORTED_PERSONALITIES > 2 - PERSONALITY2_WORDSIZE, # endif -}; +# ifndef current_klongsize +unsigned current_klongsize; # endif # ifndef current_klongsize unsigned current_klongsize; -static const int personality_klongsize[SUPPORTED_PERSONALITIES] = { - PERSONALITY0_KLONGSIZE, - PERSONALITY1_KLONGSIZE, -# if SUPPORTED_PERSONALITIES > 2 - PERSONALITY2_KLONGSIZE, -# endif -}; # endif void @@ -307,21 +389,10 @@ update_personality(struct tcb *tcp, unsigned int personality) return; tcp->currpers = personality; -# undef PERSONALITY_NAMES -# if defined X86_64 -# define PERSONALITY_NAMES {"64 bit", "32 bit", "x32"} -# elif defined X32 -# define PERSONALITY_NAMES {"x32", "32 bit"} -# elif SUPPORTED_PERSONALITIES == 2 -# define PERSONALITY_NAMES {"64 bit", "32 bit"} -# endif -# ifdef PERSONALITY_NAMES if (!qflag) { - static const char *const names[] = PERSONALITY_NAMES; error_msg("[ Process PID=%d runs in %s mode. ]", - tcp->pid, names[personality]); + tcp->pid, personality_names[personality]); } -# endif } #endif @@ -539,18 +610,11 @@ static int arch_set_success(struct tcb *); struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES]; -static struct inject_opts * -tcb_inject_opts(struct tcb *tcp) +static struct inject_data +tcb_inject_data(struct tcb *tcp, bool step) { - return (scno_in_range(tcp->scno) && tcp->inject_vec[current_personality]) - ? &tcp->inject_vec[current_personality][tcp->scno] : NULL; -} - - -static long -tamper_with_syscall_entering(struct tcb *tcp, unsigned int *signo) -{ - if (!tcp->inject_vec[current_personality]) { + if (step && !tcp->inject_vec[current_personality] && + inject_vec[current_personality]) { tcp->inject_vec[current_personality] = xcalloc(nsyscalls, sizeof(**inject_vec)); memcpy(tcp->inject_vec[current_personality], @@ -558,21 +622,31 @@ tamper_with_syscall_entering(struct tcb *tcp, unsigned int *signo) nsyscalls * sizeof(**inject_vec)); } - struct inject_opts *opts = tcb_inject_opts(tcp); - - if (!opts || opts->first == 0) - return 0; - - --opts->first; - - if (opts->first != 0) - return 0; + struct inject_opts *opts = + scno_in_range(tcp->scno) && tcp->inject_vec[current_personality] + ? &tcp->inject_vec[current_personality][tcp->scno] : NULL; + struct inject_data res = {}; + if (opts) { + if (step) { + if (opts->first != 0 && --opts->first == 0) { + res = opts->data; + opts->first = opts->step; + } + } else { + res = opts->data; + } + } + return res; +} - opts->first = opts->step; +static long +tamper_with_syscall_entering(struct tcb *tcp, unsigned int *signo) +{ + struct inject_data data = tcb_inject_data(tcp, true); - if (opts->data.flags & INJECT_F_SIGNAL) - *signo = opts->data.signo; - if (opts->data.flags & INJECT_F_RETVAL && !arch_set_scno(tcp, -1)) + if (data.flags & INJECT_F_SIGNAL) + *signo = data.signo; + if ((data.flags & INJECT_F_RETVAL) && !arch_set_scno(tcp, -1)) tcp->flags |= TCB_TAMPERED; return 0; @@ -581,22 +655,22 @@ 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_data data = tcb_inject_data(tcp, false); - if (!opts) + if (!(data.flags & INJECT_F_RETVAL)) return 0; - if (opts->data.rval >= 0) { + if (data.rval >= 0) { kernel_long_t u_rval = tcp->u_rval; - tcp->u_rval = opts->data.rval; + tcp->u_rval = data.rval; if (arch_set_success(tcp)) { tcp->u_rval = u_rval; } else { tcp->u_error = 0; } } else { - unsigned long new_error = -opts->data.rval; + unsigned long new_error = -data.rval; if (new_error != tcp->u_error && new_error <= MAX_ERRNO_VALUE) { unsigned long u_error = tcp->u_error; @@ -659,6 +733,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) { @@ -679,20 +759,20 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig) if (!traced(tcp) || (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 (inject(tcp)) tamper_with_syscall_entering(tcp, sig); if (cflag == CFLAG_ONLY_STATS) { - return 0; + goto maybe_ad_hoc_tamper; } #ifdef USE_LIBUNWIND @@ -707,6 +787,11 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig) int res = raw(tcp) ? 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 @@ -747,21 +832,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); @@ -970,7 +1062,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) */ -- 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