On Wed, Jul 27, 2016 at 08:41:16PM +0200, Nahim El Atmani wrote: > From: Nahim El Atmani <n...@lse.epita.fr> > > From: Nahim El Atmani <nahim+...@naam.me> > > * defs.h: Add new qualifier and struct fault_opts > * linux/x86_64/fault.h: New file. > (fault_set_sc_err): New function. > (fault_discard_sc): Likewise. > * Makefile.am: Add it. > * syscall.c (reallocate_fault): New function. > (qualify_one): Also extend the size of faults_vec. > (qual_fault): New function. > (qual_signal): Also reallocate faults_vec. > (fault_syscall_enter): New function. > (fault_syscall_exit): Likewise. > (trace_syscall_entering): Discard syscall if needed. > (trace_syscall_exiting): Set syscall's return error value, print when > syscall > has been discarded. > > Signed-off-by: Nahim El Atmani <nahim+...@naam.me> > Reviewed-By: Gabriel Laskar <gabr...@lse.epita.fr> > --- > * The fault injection is currently only available for i386 & x86_64, hence a > new configure flag have been created (--enable-fault-injection) to keep the > build clean on other architectures. > * Concerning the struct qual_options, the name 'fault' and short name 'f' are > not fixed, feel free to comment if something better comes to your mind. > * Regarding the error number checks in qual_fault(), they're light on purpose > to allow one to return any kind of return value. > * There is still one TODO left which is supporting the fuzzy approach using > percentage as an occurrence. It should comes soon since all the option > parsing is done. On that note fuzzing means we want to be able to reproduce, > so we have to keep the seed somewhere and take it as an input. I my opinion > using the environment for this kind of things is better than adding a new > option, what do you think? > > Makefile.am | 5 ++ > configure.ac | 22 ++++++- > defs.h | 25 ++++++++ > linux/i386/fault.h | 42 +++++++++++++ > linux/x86_64/fault.h | 42 +++++++++++++ > syscall.c | 174 > ++++++++++++++++++++++++++++++++++++++++++++++++++- > 6 files changed, 307 insertions(+), 3 deletions(-) > create mode 100644 linux/i386/fault.h > create mode 100644 linux/x86_64/fault.h > > diff --git a/Makefile.am b/Makefile.am > index 1e7554e..5272708 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -659,6 +659,11 @@ EXTRA_DIST = \ > xlat/gen.sh \ > xlate.el > > +if ENABLE_FAULT_INJECTION > +EXTRA_DIST += linux/x86_64/fault.h \ > + linux/i386/fault.h > +endif > + > .PHONY: srpm > srpm: dist-xz > rpmbuild --define '%_srcrpmdir .' -ts $(distdir).tar.xz > diff --git a/configure.ac b/configure.ac > index 4af1649..d7302a6 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -680,6 +680,27 @@ AC_SUBST(dl_LIBS) > > AC_PATH_PROG([PERL], [perl]) > > +AC_ARG_ENABLE([fault-injection], > + [AS_HELP_STRING([--enable-fault-injection], > + [enable fault injection support (x86_64 only)])], > + [], [enable_fault_injection=no]) > +case "$enable_fault_injection" in > + yes) enable_fault_injection=1 ;; > + no) enable_fault_injection=0 ;; > + *) AC_MSG_ERROR([bad value $enable_fault_injection for fault-injection > option]) ;; > +esac > +AC_DEFINE_UNQUOTED([ENABLE_FAULT_INJECTION], [$enable_fault_injection], > + [Define to 1 if you want fault injection support.]) > + > +AM_CONDITIONAL([ENABLE_FAULT_INJECTION], [test "x$enable_fault_injection" = > x1]) > +AC_MSG_CHECKING([for fault injection support]) > +if [test $enable_fault_injection] > +then > + AC_MSG_RESULT([yes]) > +else > + AC_MSG_RESULT([no]) > +fi > + > dnl stack trace with libunwind > libunwind_CPPFLAGS= > libunwind_LDFLAGS= > @@ -767,7 +788,6 @@ if test "x$use_libunwind" = xyes; then > AC_SUBST(libunwind_CPPFLAGS) > fi > AM_CONDITIONAL([USE_LIBUNWIND], [test "x$use_libunwind" = xyes]) > -AC_MSG_RESULT([$use_libunwind]) > > if test "$arch" = mips && test "$no_create" != yes; then > mkdir -p linux/mips > diff --git a/defs.h b/defs.h > index 2edf943..d4b0dde 100644 > --- a/defs.h > +++ b/defs.h > @@ -363,6 +363,9 @@ struct tcb { > #define QUAL_SIGNAL 0x010 /* report events with this signal */ > #define QUAL_READ 0x020 /* dump data read on this file descriptor */ > #define QUAL_WRITE 0x040 /* dump data written to this file descriptor */ > +#if ENABLE_FAULT_INJECTION > +#define QUAL_FAULT 0x080 /* this system call fail on purpose */ > +#endif > typedef uint8_t qualbits_t; > > #define DEFAULT_QUAL_FLAGS (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE) > @@ -458,6 +461,28 @@ enum iov_decode { > IOV_DECODE_NETLINK > }; > > +#if ENABLE_FAULT_INJECTION > +/* Fault injection qualifiers concerning syscalls: > + * FAULT_ENTER: already discarded, but error is not yet propagated > + * FAULT_DONE: already discarded and we were using FAULT_AT (prevent > overflow) > + * FAULT_AT: discard syscall at the nth time > + * FAULT_EVERY: discard syscall every nth time > + * FAULT_FUZZY: discard syscall on a random basis every nth percent of the > time > + */ > +#define FAULT_ENTER 1 > +#define FAULT_EVERY (1 << 2) > +#define FAULT_FUZZY (1 << 3) > +#define FAULT_AT (1 << 4) > +#define FAULT_DONE (1 << 5) > + > +struct fault_opts { > + int err; > + int cnt; > + int occ; > + qualbits_t flag; > +}; > +#endif > + > typedef enum { > CFLAG_NONE = 0, > CFLAG_ONLY_STATS, > diff --git a/linux/i386/fault.h b/linux/i386/fault.h > new file mode 100644 > index 0000000..7306a7f > --- /dev/null > +++ b/linux/i386/fault.h > @@ -0,0 +1,42 @@ > +/* > + * Copyright (c) 2016 Nahim El Atmani <nahim+...@naam.me> > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * 3. The name of the author may not be used to endorse or promote products > + * derived from this software without specific prior written permission. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR > + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES > + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. > + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, > + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT > + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF > + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + */ > + > +static inline long > +fault_set_sc_err(struct tcb *tcp, int error) > +{ > + return ptrace(PTRACE_POKEUSER, tcp->pid, > + offsetof(struct user, regs.eax), > + (unsigned long long int)error); > +} > + > +static inline long > +fault_discard_sc(struct tcb *tcp) > +{ > + return ptrace(PTRACE_POKEUSER, tcp->pid, > + offsetof(struct user, regs.orig_eax), > + (unsigned long long int)-1); > +} > diff --git a/linux/x86_64/fault.h b/linux/x86_64/fault.h > new file mode 100644 > index 0000000..17dcbee > --- /dev/null > +++ b/linux/x86_64/fault.h > @@ -0,0 +1,42 @@ > +/* > + * Copyright (c) 2016 Nahim El Atmani <nahim+...@naam.me> > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * 3. The name of the author may not be used to endorse or promote products > + * derived from this software without specific prior written permission. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR > + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES > + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. > + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, > + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT > + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF > + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + */ > + > +static inline long > +fault_set_sc_err(struct tcb *tcp, int error) > +{ > + return ptrace(PTRACE_POKEUSER, tcp->pid, > + offsetof(struct user, regs.rax), > + (unsigned long long int)error); > +} > + > +static inline long > +fault_discard_sc(struct tcb *tcp) > +{ > + return ptrace(PTRACE_POKEUSER, tcp->pid, > + offsetof(struct user, regs.orig_rax), > + (unsigned long long int)-1); > +} > diff --git a/syscall.c b/syscall.c > index cf087d6..b886094 100644 > --- a/syscall.c > +++ b/syscall.c > @@ -266,6 +266,14 @@ enum { > MIN_QUALS = MAX_NSYSCALLS > 255 ? MAX_NSYSCALLS : 255 > }; > > +#if ENABLE_FAULT_INJECTION > +#include "fault.h" > +struct fault_opts *faults_vec[SUPPORTED_PERSONALITIES]; > +#define syscall_failed(tcp) \ > + (((tcp)->qual_flg & QUAL_FAULT) && \ > + (faults_vec[current_personality][(tcp)->scno].flag & FAULT_ENTER)) > +#endif > + > #if SUPPORTED_PERSONALITIES > 1 > unsigned current_personality; > > @@ -359,6 +367,9 @@ update_personality(struct tcb *tcp, unsigned int > personality) > } > #endif > > +#if ENABLE_FAULT_INJECTION > +static int qual_fault(); > +#endif > static int qual_syscall(), qual_signal(), qual_desc(); > > static const struct qual_options { > @@ -384,6 +395,10 @@ static const struct qual_options { > { QUAL_WRITE, "write", qual_desc, "descriptor" }, > { QUAL_WRITE, "writes", qual_desc, "descriptor" }, > { QUAL_WRITE, "w", qual_desc, "descriptor" }, > +#if ENABLE_FAULT_INJECTION > + { QUAL_FAULT, "fault", qual_fault, "fault argument"}, > + { QUAL_FAULT, "f", qual_fault, "fault argument"}, > +#endif > { 0, NULL, NULL, NULL }, > }; > > @@ -406,6 +421,15 @@ reallocate_qual(const unsigned int n) > num_quals = n; > } > > +#if ENABLE_FAULT_INJECTION > +static inline void > +reallocate_fault(const unsigned int n) > +{ > + reallocate_vec((void **)faults_vec, num_quals, > + sizeof(struct fault_opts), n); > +} > +#endif > + > static int > find_errno_by_name(const char *name) > { > @@ -439,8 +463,12 @@ qualify_one(const unsigned int n, unsigned int bitflag, > const int not, const int > { > int p; > > - if (num_quals <= n) > + if (num_quals <= n) { > reallocate_qual(n + 1); > +#if ENABLE_FAULT_INJECTION > + reallocate_fault(n + 1); > +#endif > + } > > for (p = 0; p < SUPPORTED_PERSONALITIES; p++) { > if (pers == p || pers < 0) { > @@ -481,6 +509,78 @@ qual_syscall(const char *s, const unsigned int bitflag, > const int not) > return rc; > } > > +#if ENABLE_FAULT_INJECTION > +static int > +qual_fault(const char *s, const unsigned int bitflag, const int not) > +{ > + int i, p, negative; > + struct fault_opts opts; > + char *ms, *ss, *end, *saveptr; > + > + ms = ss = xstrdup(s); > + ss = strtok_r(ss, ":", &saveptr); > + if (!ss) > + goto bad_format; > + > + ss = strtok_r(NULL, ":", &saveptr); > + if (!ss) > + goto bad_format; > + > + opts.occ = strtol(ss, &end, 10); > + if (end == ss || (*end != '\0' && *end != '%' && *end != '.') > + || errno == ERANGE || errno == EINVAL || opts.occ < 1 > + || (*end == '%' && opts.occ > 100)) > + goto bad_format; > + switch (*end) { > + case '%': opts.flag |= FAULT_FUZZY; break; > + case '.': opts.flag |= FAULT_EVERY; break; > + default: opts.flag |= FAULT_AT; > + } > + > + ss = strtok_r(NULL, ":", &saveptr); > + if (!ss) > + goto bad_format; > + > + negative = 0; > + if (*ss == '-') { > + negative = 1; > + ++ss; > + } > + > + if (*ss >= '0' && *ss <= '9') {
We should be able to pass a negative number as an error value, no? This also justify the usage of string_to_int instead of string_to_uint. > + if (-1 == (opts.err = string_to_int(ss))) > + goto bad_format; > + } > + else { > + opts.err = find_errno_by_name(ss); > + if (opts.err < 0) > + goto bad_format; > + } > + if (negative) > + opts.err *= -1; > + > + for (p = 0; p < SUPPORTED_PERSONALITIES; p++) { > + if (*ms >= '0' && *ms <= '9') > + i = string_to_uint(ms); > + else > + i = find_scno_by_name(ms, p); > + > + if (i < 0) > + goto bad_format; > + > + qualify_one(i, bitflag, not, -1); > + memcpy(&faults_vec[p][i], &opts, sizeof(struct fault_opts)); > + } > + > + free(ms); > + return 0; > + > +bad_format: > + free(ms); > + return -1; > +} > +#endif > + > static int > qual_signal(const char *s, const unsigned int bitflag, const int not) > { > @@ -546,8 +646,12 @@ qualify(const char *s) > int not; > unsigned int i; > > - if (num_quals == 0) > + if (num_quals == 0) { > reallocate_qual(MIN_QUALS); > +#if ENABLE_FAULT_INJECTION > + reallocate_fault(MIN_QUALS); > +#endif > + } > > opt = &qual_options[0]; > for (i = 0; (p = qual_options[i].option_name); i++) { > @@ -808,6 +912,41 @@ static void get_error(struct tcb *, const bool); > static int getregs_old(pid_t); > #endif > > +#if ENABLE_FAULT_INJECTION > +static long > +fault_syscall_enter(struct tcb *tcp) > +{ > + struct fault_opts *opts = &faults_vec[current_personality][tcp->scno]; > + > + if (opts->flag & FAULT_DONE) > + return 0; > + opts->cnt++; > + if ((opts->flag & FAULT_EVERY) && (opts->cnt % opts->occ)) > + return 0; > + if ((opts->flag & FAULT_AT) && (opts->cnt != opts->occ)) > + return 0; > + if (opts->flag & FAULT_FUZZY) /* TODO: Support the fuzzy way */ > + return 0; > + opts->flag |= FAULT_ENTER; > + > + return fault_discard_sc(tcp); > +} > + > +static inline long > +fault_syscall_exit(struct tcb *tcp) > +{ > + struct fault_opts *opts = &faults_vec[current_personality][tcp->scno]; > + > + if (!(opts->flag & FAULT_ENTER)) > + return 0; > + else if (opts->flag & FAULT_AT) > + opts->flag |= FAULT_DONE; > + > + tcp->u_error = opts->err; > + return fault_set_sc_err(tcp, opts->err); > +} > +#endif > + > static int > trace_syscall_entering(struct tcb *tcp) > { > @@ -868,6 +1007,12 @@ trace_syscall_entering(struct tcb *tcp) > return 0; > } > > +#if ENABLE_FAULT_INJECTION > + if (tcp->qual_flg & QUAL_FAULT) > + if (fault_syscall_enter(tcp)) > + res = -1; > +#endif > + > tcp->flags &= ~TCB_FILTERED; > > if (cflag == CFLAG_ONLY_STATS || hide_log_until_execve) { > @@ -922,9 +1067,17 @@ trace_syscall_exiting(struct tcb *tcp) > update_personality(tcp, tcp->currpers); > #endif > res = (get_regs_error ? -1 : get_syscall_result(tcp)); > + > if (filtered(tcp) || hide_log_until_execve) > goto ret; > > +#if ENABLE_FAULT_INJECTION > + if (tcp->qual_flg & QUAL_FAULT > + && (faults_vec[current_personality][tcp->scno].flag & FAULT_ENTER)) > + if (fault_syscall_exit(tcp)) > + res = -1; > +#endif > + > if (cflag) { > count_syscall(tcp, &tv); > if (cflag == CFLAG_ONLY_STATS) { > @@ -985,13 +1138,24 @@ trace_syscall_exiting(struct tcb *tcp) > tprints(") "); > tabto(); > u_error = tcp->u_error; > + > if (tcp->qual_flg & QUAL_RAW) { > if (u_error) > tprintf("= -1 (errno %ld)", u_error); > else > tprintf("= %#lx", tcp->u_rval); > +#if ENABLE_FAULT_INJECTION > + if (syscall_failed(tcp)) { > + tprints("(DISCARDED)"); > + faults_vec[current_personality][tcp->scno].flag &= > ~FAULT_ENTER; > + } > +#endif > } > +#if ENABLE_FAULT_INJECTION > + else if ((!(sys_res & RVAL_NONE) && u_error) || syscall_failed(tcp)) { > +#else > else if (!(sys_res & RVAL_NONE) && u_error) { > +#endif > switch (u_error) { > /* Blocked signals do not interrupt any syscalls. > * In this case syscalls don't return ERESTARTfoo codes. > @@ -1054,6 +1218,12 @@ trace_syscall_exiting(struct tcb *tcp) > else > tprintf("= -1 ERRNO_%lu (%s)", u_error, > strerror(u_error)); > +#if ENABLE_FAULT_INJECTION > + if (syscall_failed(tcp)) { > + faults_vec[current_personality][tcp->scno].flag > &= ~FAULT_ENTER; > + tprintf("(DISCARDED)"); > + } > +#endif > break; > } > if ((sys_res & RVAL_STR) && tcp->auxstr) > -- > Nahim El Atmani > > > ------------------------------------------------------------------------------ > _______________________________________________ > Strace-devel mailing list > Strace-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/strace-devel -- Gabriel Laskar ------------------------------------------------------------------------------ _______________________________________________ Strace-devel mailing list Strace-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/strace-devel