Compare commits
17 Commits
master
...
NikolayMar
Author | SHA1 | Date | |
---|---|---|---|
6ab67a54de | |||
|
cb1f64ef3a | ||
|
af319a087d | ||
|
85aac65364 | ||
|
fb0ff98257 | ||
|
7ac26b2274 | ||
|
077bac4a1f | ||
|
37f33a4686 | ||
|
302f2d16d0 | ||
|
2de045936f | ||
|
7d93fbe00e | ||
|
70a8481f6d | ||
|
59b2dbbdbd | ||
|
dfc18a8a98 | ||
|
f00b9df5cb | ||
|
ae02a6cbb8 | ||
|
4c2d68c14b |
@ -2,6 +2,8 @@ language: c
|
||||
|
||||
dist: trusty
|
||||
|
||||
group: deprecated-2017Q4
|
||||
|
||||
before_install: ./travis-install.sh
|
||||
|
||||
script: ./travis-build.sh
|
||||
|
@ -88,6 +88,7 @@ strace_SOURCES = \
|
||||
affinity.c \
|
||||
aio.c \
|
||||
alpha.c \
|
||||
basic_actions.c \
|
||||
basic_filters.c \
|
||||
bind.c \
|
||||
bjm.c \
|
||||
@ -136,7 +137,11 @@ strace_SOURCES = \
|
||||
fetch_struct_statfs.c \
|
||||
file_handle.c \
|
||||
file_ioctl.c \
|
||||
filter_action.c \
|
||||
filter_expression.c \
|
||||
filter_parse.c \
|
||||
filter_qualify.c \
|
||||
filter.c \
|
||||
filter.h \
|
||||
flock.c \
|
||||
flock.h \
|
||||
|
4
NEWS
4
NEWS
@ -1,6 +1,10 @@
|
||||
Noteworthy changes in release ?.?? (????-??-??)
|
||||
===============================================
|
||||
|
||||
* Changes in command line syntax
|
||||
* New syntax for -e option for filter-based expressions.
|
||||
Old syntax is still supported.
|
||||
|
||||
* Improvements
|
||||
* Implemented decoding of netlink descriptor attributes as file descriptors.
|
||||
* Implemented decoding of hugetlb page size selection flags.
|
||||
|
130
basic_actions.c
Normal file
130
basic_actions.c
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "filter.h"
|
||||
|
||||
bool
|
||||
is_traced(struct tcb *tcp)
|
||||
{
|
||||
return traced(tcp);
|
||||
}
|
||||
|
||||
bool
|
||||
not_injected(struct tcb *tcp)
|
||||
{
|
||||
return !inject(tcp);
|
||||
}
|
||||
|
||||
void *
|
||||
parse_null(const char *str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
apply_trace(struct tcb *tcp, void *priv_data)
|
||||
{
|
||||
tcp->qual_flg |= QUAL_TRACE;
|
||||
}
|
||||
|
||||
void
|
||||
apply_raw(struct tcb *tcp, void *priv_data)
|
||||
{
|
||||
tcp->qual_flg |= QUAL_RAW;
|
||||
}
|
||||
|
||||
void
|
||||
apply_abbrev(struct tcb *tcp, void *priv_data)
|
||||
{
|
||||
tcp->qual_flg |= QUAL_ABBREV;
|
||||
}
|
||||
|
||||
void
|
||||
apply_verbose(struct tcb *tcp, void *priv_data)
|
||||
{
|
||||
tcp->qual_flg |= QUAL_VERBOSE;
|
||||
}
|
||||
|
||||
void
|
||||
apply_inject(struct tcb *tcp, void *priv_data)
|
||||
{
|
||||
struct inject_opts *opts = priv_data;
|
||||
|
||||
tcp->qual_flg |= QUAL_INJECT;
|
||||
if (!tcp->inject_vec[current_personality])
|
||||
tcp->inject_vec[current_personality] =
|
||||
xcalloc(nsyscalls, sizeof(struct inject_opts));
|
||||
if (scno_in_range(tcp->scno)
|
||||
&& !tcp->inject_vec[current_personality][tcp->scno].data.flags)
|
||||
tcp->inject_vec[current_personality][tcp->scno] = *opts;
|
||||
}
|
||||
|
||||
static void *
|
||||
parse_inject_common(const char *str, bool fault_tokens_only,
|
||||
const char *description)
|
||||
{
|
||||
struct inject_opts *opts = xmalloc(sizeof(struct inject_opts));
|
||||
char *buf = xstrdup(str);
|
||||
|
||||
parse_inject_common_args(buf, opts, fault_tokens_only, false);
|
||||
if (!opts->data.flags)
|
||||
error_msg_and_die("invalid %s argument '%s'",
|
||||
description, str ? str : "");
|
||||
free(buf);
|
||||
return opts;
|
||||
}
|
||||
|
||||
void *
|
||||
parse_inject(const char *str)
|
||||
{
|
||||
return parse_inject_common(str, false, "inject");
|
||||
}
|
||||
|
||||
void
|
||||
apply_fault(struct tcb *tcp, void *priv_data)
|
||||
{
|
||||
apply_inject(tcp, priv_data);
|
||||
}
|
||||
|
||||
void *
|
||||
parse_fault(const char *str)
|
||||
{
|
||||
return parse_inject_common(str, true, "fault");
|
||||
}
|
||||
|
||||
void
|
||||
apply_read(struct tcb *tcp, void *_priv_data)
|
||||
{
|
||||
tcp->qual_flg |= QUAL_READ;
|
||||
}
|
||||
|
||||
void
|
||||
apply_write(struct tcb *tcp, void *_priv_data)
|
||||
{
|
||||
tcp->qual_flg |= QUAL_WRITE;
|
||||
}
|
160
basic_filters.c
160
basic_filters.c
@ -29,6 +29,7 @@
|
||||
#include "defs.h"
|
||||
#include "number_set.h"
|
||||
#include "filter.h"
|
||||
#include "syscall.h"
|
||||
#include <regex.h>
|
||||
|
||||
static bool
|
||||
@ -95,7 +96,7 @@ qualify_syscall_regex(const char *s, struct number_set *set)
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
lookup_class(const char *s)
|
||||
lookup_class(const char *s, bool qualify_mode)
|
||||
{
|
||||
static const struct {
|
||||
const char *name;
|
||||
@ -126,6 +127,8 @@ lookup_class(const char *s)
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
|
||||
if (!qualify_mode && *s != '%')
|
||||
continue;
|
||||
if (strcmp(s, syscall_class[i].name) == 0) {
|
||||
return syscall_class[i].value;
|
||||
}
|
||||
@ -135,9 +138,9 @@ lookup_class(const char *s)
|
||||
}
|
||||
|
||||
static bool
|
||||
qualify_syscall_class(const char *s, struct number_set *set)
|
||||
qualify_syscall_class(const char *s, struct number_set *set, bool qualify_mode)
|
||||
{
|
||||
const unsigned int n = lookup_class(s);
|
||||
const unsigned int n = lookup_class(s, qualify_mode);
|
||||
if (!n)
|
||||
return false;
|
||||
|
||||
@ -180,7 +183,7 @@ qualify_syscall_name(const char *s, struct number_set *set)
|
||||
}
|
||||
|
||||
static bool
|
||||
qualify_syscall(const char *token, struct number_set *set)
|
||||
qualify_syscall(const char *token, struct number_set *set, bool qualify_mode)
|
||||
{
|
||||
bool ignore_fail = false;
|
||||
|
||||
@ -192,7 +195,7 @@ qualify_syscall(const char *token, struct number_set *set)
|
||||
return qualify_syscall_number(token, set) || ignore_fail;
|
||||
if (*token == '/')
|
||||
return qualify_syscall_regex(token + 1, set) || ignore_fail;
|
||||
return qualify_syscall_class(token, set)
|
||||
return qualify_syscall_class(token, set, qualify_mode)
|
||||
|| qualify_syscall_name(token, set)
|
||||
|| ignore_fail;
|
||||
}
|
||||
@ -203,7 +206,7 @@ qualify_syscall(const char *token, struct number_set *set)
|
||||
*/
|
||||
void
|
||||
qualify_syscall_tokens(const char *const str, struct number_set *const set,
|
||||
const char *const name)
|
||||
bool qualify_mode)
|
||||
{
|
||||
/* Clear all sets. */
|
||||
clear_number_set_array(set, SUPPORTED_PERSONALITIES);
|
||||
@ -213,10 +216,15 @@ qualify_syscall_tokens(const char *const str, struct number_set *const set,
|
||||
* of the remaining specification.
|
||||
*/
|
||||
const char *s = str;
|
||||
handle_inversion:
|
||||
while (*s == '!') {
|
||||
invert_number_set_array(set, SUPPORTED_PERSONALITIES);
|
||||
++s;
|
||||
if (qualify_mode) {
|
||||
/*
|
||||
* Each leading ! character means inversion
|
||||
* of the remaining specification.
|
||||
*/
|
||||
while (*s == '!') {
|
||||
invert_number_set_array(set, SUPPORTED_PERSONALITIES);
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(s, "none") == 0) {
|
||||
@ -227,8 +235,8 @@ handle_inversion:
|
||||
*/
|
||||
return;
|
||||
} else if (strcmp(s, "all") == 0) {
|
||||
s = "!none";
|
||||
goto handle_inversion;
|
||||
invert_number_set_array(set, SUPPORTED_PERSONALITIES);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -245,25 +253,52 @@ handle_inversion:
|
||||
|
||||
for (token = strtok_r(copy, ",", &saveptr); token;
|
||||
token = strtok_r(NULL, ",", &saveptr)) {
|
||||
done = qualify_syscall(token, set);
|
||||
done = qualify_syscall(token, set, qualify_mode);
|
||||
if (!done) {
|
||||
error_msg_and_die("invalid %s '%s'", name, token);
|
||||
error_msg_and_die("invalid system call '%s'", token);
|
||||
}
|
||||
}
|
||||
|
||||
free(copy);
|
||||
|
||||
if (!done) {
|
||||
error_msg_and_die("invalid %s '%s'", name, str);
|
||||
error_msg_and_die("invalid system call '%s'", str);
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
parse_syscall_filter(const char *str, bool qualify_mode)
|
||||
{
|
||||
struct number_set *set;
|
||||
|
||||
set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
|
||||
qualify_syscall_tokens(str, set, qualify_mode);
|
||||
return set;
|
||||
}
|
||||
|
||||
bool
|
||||
run_syscall_filter(struct tcb *tcp, void *priv_data)
|
||||
{
|
||||
struct number_set *set = priv_data;
|
||||
|
||||
return is_number_in_set_array(tcp->scno, set, current_personality);
|
||||
}
|
||||
|
||||
void
|
||||
free_syscall_filter(void *priv_data)
|
||||
{
|
||||
struct number_set *set = priv_data;
|
||||
|
||||
free_number_set_array(set, SUPPORTED_PERSONALITIES);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add numbers to SET according to STR specification.
|
||||
*/
|
||||
void
|
||||
qualify_tokens(const char *const str, struct number_set *const set,
|
||||
string_to_uint_func func, const char *const name)
|
||||
string_to_uint_func func, const char *const name,
|
||||
bool qualify_mode)
|
||||
{
|
||||
/* Clear the set. */
|
||||
clear_number_set_array(set, 1);
|
||||
@ -273,10 +308,15 @@ qualify_tokens(const char *const str, struct number_set *const set,
|
||||
* of the remaining specification.
|
||||
*/
|
||||
const char *s = str;
|
||||
handle_inversion:
|
||||
while (*s == '!') {
|
||||
invert_number_set_array(set, 1);
|
||||
++s;
|
||||
if (qualify_mode) {
|
||||
/*
|
||||
* Each leading ! character means inversion
|
||||
* of the remaining specification.
|
||||
*/
|
||||
while (*s == '!') {
|
||||
invert_number_set_array(set, 1);
|
||||
++s;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(s, "none") == 0) {
|
||||
@ -287,8 +327,8 @@ handle_inversion:
|
||||
*/
|
||||
return;
|
||||
} else if (strcmp(s, "all") == 0) {
|
||||
s = "!none";
|
||||
goto handle_inversion;
|
||||
invert_number_set_array(set, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -319,3 +359,79 @@ handle_inversion:
|
||||
error_msg_and_die("invalid %s '%s'", name, str);
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
parse_fd_filter(const char *str, bool qualify_mode)
|
||||
{
|
||||
struct number_set *set;
|
||||
|
||||
set = alloc_number_set_array(1);
|
||||
qualify_tokens(str, set, string_to_uint, "descriptor", qualify_mode);
|
||||
return set;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_fd_in_set(struct tcb *tcp, int fd, void *data) {
|
||||
struct number_set *set = data;
|
||||
|
||||
if (fd < 0)
|
||||
return false;
|
||||
return is_number_in_set(fd, set);
|
||||
}
|
||||
|
||||
bool
|
||||
run_fd_filter(struct tcb *tcp, void *priv_data)
|
||||
{
|
||||
struct number_set *set = priv_data;
|
||||
|
||||
/*
|
||||
* mq_timedsend and mq_timedreceive are not marked as descriptor
|
||||
* syscalls, but they can be dumped with -e read/write.
|
||||
*/
|
||||
switch (tcp->s_ent->sen) {
|
||||
case SEN_mq_timedsend:
|
||||
case SEN_mq_timedreceive:
|
||||
return is_fd_in_set(tcp, tcp->u_arg[0], set);
|
||||
}
|
||||
|
||||
return match_fd_common(tcp, &is_fd_in_set, set);
|
||||
}
|
||||
|
||||
void
|
||||
free_fd_filter(void *priv_data)
|
||||
{
|
||||
struct number_set *set = priv_data;
|
||||
|
||||
free_number_set_array(set, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
void *
|
||||
parse_path_filter(const char *path, bool qualify_mode)
|
||||
{
|
||||
struct path_set *set = xcalloc(1, sizeof(struct path_set));
|
||||
|
||||
pathtrace_select_set(path, set);
|
||||
return set;
|
||||
}
|
||||
|
||||
bool
|
||||
run_path_filter(struct tcb *tcp, void *priv_data)
|
||||
{
|
||||
struct path_set *set = priv_data;
|
||||
|
||||
return pathtrace_match_set(tcp, set);
|
||||
}
|
||||
|
||||
void
|
||||
free_path_filter(void *priv_data)
|
||||
{
|
||||
struct path_set *set = priv_data;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < set->num_selected; ++i)
|
||||
free((char *) set->paths_selected[i]);
|
||||
free(set->paths_selected);
|
||||
free(set);
|
||||
return;
|
||||
}
|
||||
|
17
defs.h
17
defs.h
@ -251,6 +251,11 @@ 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_READ 0x020 /* dump data read in this syscall */
|
||||
#define QUAL_WRITE 0x040 /* dump data written in this syscall */
|
||||
#ifdef USE_LIBUNWIND
|
||||
# define QUAL_STACKTRACE 0x080 /* do the stack trace */
|
||||
#endif
|
||||
|
||||
#define DEFAULT_QUAL_FLAGS (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE)
|
||||
|
||||
@ -260,8 +265,13 @@ struct tcb {
|
||||
#define traced(tcp) ((tcp)->qual_flg & QUAL_TRACE)
|
||||
#define verbose(tcp) ((tcp)->qual_flg & QUAL_VERBOSE)
|
||||
#define abbrev(tcp) ((tcp)->qual_flg & QUAL_ABBREV)
|
||||
#define dump_read(tcp) ((tcp)->qual_flg & QUAL_READ)
|
||||
#define dump_write(tcp) ((tcp)->qual_flg & QUAL_WRITE)
|
||||
#define raw(tcp) ((tcp)->qual_flg & QUAL_RAW)
|
||||
#define inject(tcp) ((tcp)->qual_flg & QUAL_INJECT)
|
||||
#ifdef USE_LIBUNWIND
|
||||
# define stacktrace(tcp) ((tcp)->qual_flg & QUAL_STACKTRACE)
|
||||
#endif
|
||||
#define filtered(tcp) ((tcp)->flags & TCB_FILTERED)
|
||||
#define hide_log(tcp) ((tcp)->flags & TCB_HIDE_LOG)
|
||||
|
||||
@ -371,7 +381,7 @@ extern struct path_set {
|
||||
extern unsigned xflag;
|
||||
extern unsigned followfork;
|
||||
#ifdef USE_LIBUNWIND
|
||||
/* if this is true do the stack trace for every system call */
|
||||
/* if this is true do the initialization of stack tracing mechanism */
|
||||
extern bool stack_trace_enabled;
|
||||
#endif
|
||||
extern unsigned ptrace_setoptions;
|
||||
@ -647,8 +657,9 @@ print_struct_statfs64(struct tcb *, kernel_ulong_t addr, kernel_ulong_t size);
|
||||
|
||||
extern void print_ifindex(unsigned int);
|
||||
|
||||
extern void qualify(const char *);
|
||||
extern unsigned int qual_flags(const unsigned int);
|
||||
extern void filtering_parse(const char *);
|
||||
extern void filtering_parsing_finish(void);
|
||||
extern void filter_syscall(struct tcb *);
|
||||
|
||||
#define DECL_IOCTL(name) \
|
||||
extern int \
|
||||
|
130
filter.c
Normal file
130
filter.c
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "filter.h"
|
||||
|
||||
#define FILTER_TYPE(name) \
|
||||
{#name, parse_ ## name ## _filter, run_ ## name ## _filter, \
|
||||
free_ ## name ## _filter}
|
||||
/* End of FILTER_TYPE definition. */
|
||||
|
||||
static const struct filter_type {
|
||||
const char *name;
|
||||
void *(*parse_filter)(const char *, bool);
|
||||
bool (*run_filter)(struct tcb *, void *);
|
||||
void (*free_priv_data)(void *);
|
||||
} filter_types[] = {
|
||||
FILTER_TYPE(syscall),
|
||||
FILTER_TYPE(fd),
|
||||
FILTER_TYPE(path),
|
||||
};
|
||||
#undef FILTER_TYPE
|
||||
|
||||
struct filter {
|
||||
const struct filter_type *type;
|
||||
void *priv_data;
|
||||
};
|
||||
|
||||
static const struct filter_type *
|
||||
lookup_filter_type(const char *str)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(filter_types); i++) {
|
||||
if (!strcmp(filter_types[i].name, str))
|
||||
return &filter_types[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct filter *
|
||||
add_filter_to_array(struct filter **filters, unsigned int *nfilters,
|
||||
const char *name)
|
||||
{
|
||||
const struct filter_type *type = lookup_filter_type(name);
|
||||
struct filter *filter;
|
||||
|
||||
if (!type)
|
||||
error_msg_and_die("invalid filter '%s'", name);
|
||||
*filters = xreallocarray(*filters, ++(*nfilters),
|
||||
sizeof(struct filter));
|
||||
filter = &((*filters)[*nfilters - 1]);
|
||||
filter->type = type;
|
||||
return filter;
|
||||
}
|
||||
|
||||
void
|
||||
parse_filter(struct filter *filter, const char *str, bool qualify_mode)
|
||||
{
|
||||
filter->priv_data = filter->type->parse_filter(str, qualify_mode);
|
||||
}
|
||||
|
||||
static bool
|
||||
run_filter(struct tcb *tcp, struct filter *filter)
|
||||
{
|
||||
return filter->type->run_filter(tcp, filter->priv_data);
|
||||
}
|
||||
|
||||
void
|
||||
run_filters(struct tcb *tcp, struct filter *filters, unsigned int nfilters,
|
||||
bool *variables_buf)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < nfilters; ++i)
|
||||
variables_buf[i] = run_filter(tcp, &filters[i]);
|
||||
}
|
||||
|
||||
void
|
||||
free_filter(struct filter *filter)
|
||||
{
|
||||
if (!filter)
|
||||
return;
|
||||
filter->type->free_priv_data(filter->priv_data);
|
||||
}
|
||||
|
||||
void
|
||||
set_filters_qualify_mode(struct filter **filters, unsigned int *nfilters,
|
||||
unsigned int filters_left)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < *nfilters - filters_left; ++i)
|
||||
free_filter(*filters + i);
|
||||
for (i = 0; i < filters_left; ++i)
|
||||
(*filters)[i] = (*filters)[*nfilters - filters_left + i];
|
||||
*filters = xreallocarray(*filters, filters_left, sizeof(struct filter));
|
||||
*nfilters = filters_left;
|
||||
}
|
||||
|
||||
void
|
||||
set_filter_priv_data(struct filter *filter, void *priv_data)
|
||||
{
|
||||
if (filter)
|
||||
filter->priv_data = priv_data;
|
||||
}
|
86
filter.h
86
filter.h
@ -30,11 +30,93 @@
|
||||
#define STRACE_FILTER_H
|
||||
|
||||
struct number_set;
|
||||
|
||||
struct filter;
|
||||
|
||||
struct filter_action;
|
||||
|
||||
struct bool_expression;
|
||||
|
||||
typedef int (*string_to_uint_func)(const char *);
|
||||
|
||||
void qualify_tokens(const char *str, struct number_set *set,
|
||||
string_to_uint_func func, const char *name);
|
||||
string_to_uint_func func, const char *name,
|
||||
bool qualify_mode);
|
||||
void qualify_syscall_tokens(const char *str, struct number_set *set,
|
||||
const char *name);
|
||||
bool qualify_mode);
|
||||
void parse_inject_common_args(char *, struct inject_opts *,
|
||||
const bool fault_tokens_only, bool qualify_mode);
|
||||
bool is_traced(struct tcb *);
|
||||
bool not_injected(struct tcb *);
|
||||
|
||||
typedef bool (*match_fd_func)(struct tcb *, int, void *);
|
||||
int match_fd_common(struct tcb *, match_fd_func, void *);
|
||||
|
||||
/* filter api */
|
||||
struct filter* add_filter_to_array(struct filter **, unsigned int *nfilters,
|
||||
const char *name);
|
||||
void parse_filter(struct filter *, const char *str, bool qualify_mode);
|
||||
void run_filters(struct tcb *, struct filter *, unsigned int, bool *);
|
||||
void free_filter(struct filter *);
|
||||
void set_filters_qualify_mode(struct filter **, unsigned int *nfilters,
|
||||
unsigned int filters_left);
|
||||
void set_filter_priv_data(struct filter *, void *);
|
||||
|
||||
/* filter action api */
|
||||
struct filter *create_filter(struct filter_action *, const char *name);
|
||||
struct filter_action *find_or_add_action(const char *);
|
||||
void parse_filter_action(const char *, const char *, const char *);
|
||||
void set_qualify_mode(struct filter_action *, unsigned int);
|
||||
void set_filter_action_priv_data(struct filter_action *, void *);
|
||||
|
||||
/* filter expression api */
|
||||
struct bool_expression *create_expression();
|
||||
bool run_expression(struct bool_expression *, bool *, unsigned int);
|
||||
void set_expression_qualify_mode(struct bool_expression *, unsigned int);
|
||||
void expression_add_filter_and(struct bool_expression *, unsigned int);
|
||||
void parse_filter_expression(struct bool_expression *, const char *,
|
||||
struct filter_action *, unsigned int);
|
||||
|
||||
void parse_qualify_action(const char *, const char *, const char *);
|
||||
|
||||
#define DECL_FILTER(name) \
|
||||
extern void * \
|
||||
parse_ ## name ## _filter(const char *, bool); \
|
||||
extern bool \
|
||||
run_ ## name ## _filter(struct tcb *, void *); \
|
||||
extern void \
|
||||
free_ ## name ## _filter(void *) \
|
||||
/* End of DECL_FILTER definition. */
|
||||
|
||||
DECL_FILTER(syscall);
|
||||
DECL_FILTER(fd);
|
||||
DECL_FILTER(path);
|
||||
#undef DECL_FILTER
|
||||
|
||||
#define DECL_FILTER_ACTION(name) \
|
||||
extern void \
|
||||
apply_ ## name(struct tcb *, void *) \
|
||||
/* End of DECL_FILTER_ACTION definition. */
|
||||
|
||||
DECL_FILTER_ACTION(trace);
|
||||
DECL_FILTER_ACTION(raw);
|
||||
DECL_FILTER_ACTION(abbrev);
|
||||
DECL_FILTER_ACTION(verbose);
|
||||
DECL_FILTER_ACTION(inject);
|
||||
DECL_FILTER_ACTION(fault);
|
||||
DECL_FILTER_ACTION(read);
|
||||
DECL_FILTER_ACTION(write);
|
||||
DECL_FILTER_ACTION(stacktrace);
|
||||
#undef DECL_FILTER_ACTION
|
||||
|
||||
#define DECL_FILTER_ACTION_PARSER(name) \
|
||||
extern void * \
|
||||
parse_ ## name(const char *); \
|
||||
/* End of DECL_FILTER_ACTION_PARSER definition. */
|
||||
|
||||
DECL_FILTER_ACTION_PARSER(null);
|
||||
DECL_FILTER_ACTION_PARSER(inject);
|
||||
DECL_FILTER_ACTION_PARSER(fault);
|
||||
#undef DECL_FILTER_ACTION_PARSER
|
||||
|
||||
#endif /* !STRACE_FILTER_H */
|
||||
|
240
filter_action.c
Normal file
240
filter_action.c
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "filter.h"
|
||||
|
||||
#define FILTER_ACTION_TYPE(NAME, PRIORITY, FLAG, PARSER, PREFILTER) \
|
||||
{#NAME, PRIORITY, FLAG, parse_ ## PARSER, PREFILTER, apply_ ## NAME}
|
||||
/* End of FILTER_ACTION_TYPE definition. */
|
||||
|
||||
static const struct filter_action_type {
|
||||
const char *name;
|
||||
/* The highest priority is 0. */
|
||||
unsigned int priority;
|
||||
unsigned int qual_flg;
|
||||
void * (*parse_args)(const char *);
|
||||
bool (*prefilter)(struct tcb *);
|
||||
void (*apply)(struct tcb *, void *);
|
||||
} action_types[] = {
|
||||
FILTER_ACTION_TYPE(trace, 0, QUAL_TRACE, null, NULL),
|
||||
FILTER_ACTION_TYPE(inject, 1, QUAL_INJECT, inject, not_injected),
|
||||
FILTER_ACTION_TYPE(fault, 1, QUAL_INJECT, fault, not_injected),
|
||||
FILTER_ACTION_TYPE(raw, 2, QUAL_RAW, null, is_traced),
|
||||
FILTER_ACTION_TYPE(abbrev, 2, QUAL_ABBREV, null, is_traced),
|
||||
FILTER_ACTION_TYPE(verbose, 2, QUAL_VERBOSE, null, is_traced),
|
||||
FILTER_ACTION_TYPE(read, 2, QUAL_READ, null, is_traced),
|
||||
FILTER_ACTION_TYPE(write, 2, QUAL_WRITE, null, is_traced),
|
||||
# ifdef USE_LIBUNWIND
|
||||
FILTER_ACTION_TYPE(stacktrace, 2, QUAL_STACKTRACE, null, is_traced),
|
||||
# endif
|
||||
};
|
||||
#undef FILTER_ACTION_TYPE
|
||||
|
||||
struct filter_action {
|
||||
/* Used to correct order of actions with the same priority. */
|
||||
unsigned int id;
|
||||
const struct filter_action_type *type;
|
||||
struct bool_expression *expr;
|
||||
unsigned int nfilters;
|
||||
struct filter *filters;
|
||||
void *priv_data;
|
||||
};
|
||||
|
||||
static unsigned int default_flags = DEFAULT_QUAL_FLAGS;
|
||||
static struct filter_action *filter_actions;
|
||||
static unsigned int nfilter_actions;
|
||||
|
||||
static bool *variables_buf;
|
||||
|
||||
/*
|
||||
* Compares action priorities. If actions have the same priority,
|
||||
* uses LIFO order.
|
||||
*/
|
||||
static int
|
||||
compare_action_priority(const void *a, const void *b)
|
||||
{
|
||||
const struct filter_action *action_a = a;
|
||||
const struct filter_action *action_b = b;
|
||||
unsigned int priority_a = action_a->type->priority;
|
||||
unsigned int priority_b = action_b->type->priority;
|
||||
|
||||
if (priority_a != priority_b) {
|
||||
return (priority_a < priority_b) ? -1 : 1;
|
||||
} else {
|
||||
return (action_a->id > action_b->id) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
inject_path_tracing(void)
|
||||
{
|
||||
struct filter_action *action = find_or_add_action("trace");
|
||||
struct filter *path_filter;
|
||||
|
||||
if (!action->nfilters)
|
||||
filtering_parse("trace=all");
|
||||
path_filter = add_filter_to_array(&action->filters, &action->nfilters,
|
||||
"path");
|
||||
set_filter_priv_data(path_filter, &global_path_set);
|
||||
expression_add_filter_and(action->expr, action->nfilters - 1);
|
||||
}
|
||||
|
||||
void
|
||||
filtering_parsing_finish(void)
|
||||
{
|
||||
unsigned int maxfilters = 0;
|
||||
unsigned int i;
|
||||
|
||||
/* Inject path filter into trace action. */
|
||||
if (tracing_paths)
|
||||
inject_path_tracing();
|
||||
|
||||
/* Sort actions by priority */
|
||||
if (nfilter_actions == 0)
|
||||
return;
|
||||
qsort(filter_actions, nfilter_actions, sizeof(struct filter_action),
|
||||
&compare_action_priority);
|
||||
|
||||
/* Allocate variables_buf sufficient for any action */
|
||||
for (i = 0; i < nfilter_actions; ++i) {
|
||||
if (filter_actions[i].nfilters > maxfilters)
|
||||
maxfilters = filter_actions[i].nfilters;
|
||||
}
|
||||
variables_buf = xcalloc(maxfilters, sizeof(bool));
|
||||
}
|
||||
|
||||
static const struct filter_action_type *
|
||||
lookup_filter_action_type(const char *str)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(action_types); ++i) {
|
||||
if (!strcmp(action_types[i].name, str))
|
||||
return &action_types[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct filter_action *
|
||||
add_action(const struct filter_action_type *type)
|
||||
{
|
||||
struct filter_action *action;
|
||||
|
||||
/* Update default_flags */
|
||||
if (default_flags & type->qual_flg)
|
||||
default_flags &= ~type->qual_flg;
|
||||
|
||||
/* Enable stack tracing. */
|
||||
#ifdef USE_LIBUNWIND
|
||||
if (type->qual_flg & QUAL_STACKTRACE)
|
||||
stack_trace_enabled = true;
|
||||
#endif
|
||||
|
||||
filter_actions = xreallocarray(filter_actions, ++nfilter_actions,
|
||||
sizeof(struct filter_action));
|
||||
action = &filter_actions[nfilter_actions - 1];
|
||||
memset(action, 0, sizeof(*action));
|
||||
action->id = nfilter_actions - 1;
|
||||
action->type = type;
|
||||
action->expr = create_expression();
|
||||
return action;
|
||||
}
|
||||
|
||||
struct filter_action *
|
||||
find_or_add_action(const char *name)
|
||||
{
|
||||
const struct filter_action_type *type = lookup_filter_action_type(name);
|
||||
unsigned int i;
|
||||
|
||||
if (!type)
|
||||
error_msg_and_die("invalid filter action '%s'", name);
|
||||
/* If action takes arguments, add new action */
|
||||
if (type->parse_args != &parse_null)
|
||||
return add_action(type);
|
||||
|
||||
for (i = 0; i < nfilter_actions; ++i) {
|
||||
if (filter_actions[i].type == type)
|
||||
return &filter_actions[i];
|
||||
}
|
||||
return add_action(type);
|
||||
}
|
||||
|
||||
void
|
||||
parse_filter_action(const char *action_name, const char *expr, const char *args)
|
||||
{
|
||||
struct filter_action *action = find_or_add_action(action_name);
|
||||
|
||||
parse_filter_expression(action->expr, expr, action, action->nfilters);
|
||||
if (args && action->type->parse_args == &parse_null)
|
||||
error_msg("%s action takes no arguments, ignored arguments "
|
||||
"'%s'", action->type->name, args);
|
||||
action->priv_data = action->type->parse_args(args);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run_filter_action(struct tcb *tcp, struct filter_action *action)
|
||||
{
|
||||
if (action->type->prefilter && !action->type->prefilter(tcp))
|
||||
return;
|
||||
run_filters(tcp, action->filters, action->nfilters, variables_buf);
|
||||
if (run_expression(action->expr, variables_buf, action->nfilters))
|
||||
action->type->apply(tcp, action->priv_data);
|
||||
}
|
||||
|
||||
struct filter *
|
||||
create_filter(struct filter_action *action, const char *name)
|
||||
{
|
||||
return add_filter_to_array(&action->filters, &action->nfilters, name);
|
||||
}
|
||||
|
||||
void
|
||||
set_qualify_mode(struct filter_action *action, unsigned int filters_left)
|
||||
{
|
||||
set_filters_qualify_mode(&action->filters, &action->nfilters,
|
||||
filters_left);
|
||||
set_expression_qualify_mode(action->expr, filters_left);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
filter_syscall(struct tcb *tcp)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
tcp->qual_flg |= default_flags;
|
||||
for (i = 0; i < nfilter_actions; ++i)
|
||||
run_filter_action(tcp, &filter_actions[i]);
|
||||
}
|
||||
|
||||
void
|
||||
set_filter_action_priv_data(struct filter_action *action, void *priv_data)
|
||||
{
|
||||
if (action)
|
||||
action->priv_data = priv_data;
|
||||
}
|
469
filter_expression.c
Normal file
469
filter_expression.c
Normal file
@ -0,0 +1,469 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include <stdarg.h>
|
||||
#include "filter.h"
|
||||
|
||||
extern bool is_space_ascii(char);
|
||||
extern bool is_allowed_in_name(char);
|
||||
|
||||
struct expression_token {
|
||||
enum token_type {
|
||||
TOK_VARIABLE,
|
||||
TOK_OPERATOR
|
||||
} type;
|
||||
union token_data {
|
||||
unsigned int variable_id;
|
||||
enum operator_type {
|
||||
OP_NOT,
|
||||
OP_AND,
|
||||
OP_OR
|
||||
} operator_id;
|
||||
} data;
|
||||
};
|
||||
|
||||
/* Pseudo-operator used for parsing */
|
||||
#define OP_PARENTHESIS 3
|
||||
|
||||
struct bool_expression {
|
||||
unsigned int ntokens;
|
||||
struct expression_token *tokens;
|
||||
};
|
||||
|
||||
struct bool_expression *
|
||||
create_expression(void)
|
||||
{
|
||||
return xcalloc(1, sizeof(struct bool_expression));
|
||||
}
|
||||
|
||||
static void
|
||||
reallocate_expression(struct bool_expression *const expr,
|
||||
const unsigned int new_ntokens)
|
||||
{
|
||||
if (!expr)
|
||||
error_msg_and_die("invalid expression");
|
||||
expr->tokens = xreallocarray(expr->tokens, new_ntokens,
|
||||
sizeof(*expr->tokens));
|
||||
if (new_ntokens > expr->ntokens)
|
||||
memset(expr->tokens + expr->ntokens, 0,
|
||||
sizeof(*expr->tokens) * (new_ntokens - expr->ntokens));
|
||||
expr->ntokens = new_ntokens;
|
||||
}
|
||||
|
||||
static void
|
||||
add_variable_token(struct bool_expression *expr, unsigned int id)
|
||||
{
|
||||
struct expression_token token;
|
||||
token.type = TOK_VARIABLE;
|
||||
token.data.variable_id = id;
|
||||
reallocate_expression(expr, expr->ntokens + 1);
|
||||
expr->tokens[expr->ntokens - 1] = token;
|
||||
}
|
||||
|
||||
static void
|
||||
add_operator_token(struct bool_expression *expr, int op) {
|
||||
struct expression_token token;
|
||||
token.type = TOK_OPERATOR;
|
||||
token.data.operator_id = op;
|
||||
reallocate_expression(expr, expr->ntokens + 1);
|
||||
expr->tokens[expr->ntokens - 1] = token;
|
||||
}
|
||||
|
||||
void
|
||||
expression_add_filter_and(struct bool_expression *expr, unsigned int filter_id)
|
||||
{
|
||||
add_variable_token(expr, filter_id);
|
||||
add_operator_token(expr, OP_AND);
|
||||
}
|
||||
|
||||
void
|
||||
set_expression_qualify_mode(struct bool_expression *expr,
|
||||
unsigned int filters_left)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!expr)
|
||||
error_msg_and_die("invalid expression");
|
||||
reallocate_expression(expr, 2 * filters_left - 1);
|
||||
for (i = 0; i < filters_left; ++i) {
|
||||
expr->tokens[i].type = TOK_VARIABLE;
|
||||
expr->tokens[i].data.variable_id = i;
|
||||
}
|
||||
for (; i < 2 * filters_left - 1; ++i) {
|
||||
expr->tokens[i].type = TOK_OPERATOR;
|
||||
expr->tokens[i].data.operator_id = OP_AND;
|
||||
}
|
||||
}
|
||||
|
||||
ATTRIBUTE_FORMAT((printf, 3, 4))
|
||||
static int
|
||||
printf_append(char **ptr, char *end, const char *fmt, ...)
|
||||
{
|
||||
int ret;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
ret = vsnprintf(*ptr, end - *ptr, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*ptr += MIN(ret, end - *ptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Print full diagnostics for corrupted expression */
|
||||
ATTRIBUTE_NORETURN
|
||||
static void
|
||||
handle_corrupted_expression(struct bool_expression *expr, bool *stack,
|
||||
unsigned int stack_size, unsigned int current_pos,
|
||||
bool *variables, unsigned int variables_num)
|
||||
{
|
||||
char *buf, *pos, *end;
|
||||
unsigned int buf_size;
|
||||
unsigned int i;
|
||||
|
||||
/* Calculate buffer size. */
|
||||
buf_size = sizeof("corrupted filter expression:");
|
||||
buf_size += sizeof("expression (ntokens = ):")
|
||||
+ 3 * sizeof(unsigned int)
|
||||
+ (sizeof("op_") + 3 * sizeof(int)) * expr->ntokens;
|
||||
buf_size += sizeof("variables (nvariables = ):") + 3 * sizeof(int)
|
||||
+ sizeof("false") * variables_num;
|
||||
buf_size += sizeof("current position: ") + 3 * sizeof(int);
|
||||
buf_size += sizeof("stack (stack_size = ):") + 3 * sizeof(int)
|
||||
+ sizeof("false") * stack_size;
|
||||
|
||||
buf = xcalloc(buf_size, 1);
|
||||
pos = buf;
|
||||
end = buf + buf_size;
|
||||
|
||||
printf_append(&pos, end, "corrupted filter expression:\n");
|
||||
|
||||
/* Print expression. */
|
||||
printf_append(&pos, end, "expression (ntokens = %u):", expr->ntokens);
|
||||
for (i = 0; i < expr->ntokens; ++i) {
|
||||
switch (expr->tokens[i].type) {
|
||||
case TOK_VARIABLE:
|
||||
printf_append(&pos, end, " v_%u",
|
||||
expr->tokens[i].data.variable_id);
|
||||
break;
|
||||
case TOK_OPERATOR:
|
||||
switch (expr->tokens[i].data.operator_id) {
|
||||
case OP_NOT:
|
||||
printf_append(&pos, end, " not");
|
||||
break;
|
||||
case OP_AND:
|
||||
printf_append(&pos, end, " and");
|
||||
break;
|
||||
case OP_OR:
|
||||
printf_append(&pos, end, " or");
|
||||
break;
|
||||
default:
|
||||
printf_append(&pos, end, " op_%d",
|
||||
expr->tokens[i].data.operator_id);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf_append(&pos, end, " ?_%d", expr->tokens[i].type);
|
||||
}
|
||||
}
|
||||
printf_append(&pos, end, "\n");
|
||||
|
||||
/* Print variables. */
|
||||
printf_append(&pos, end, "variables (nvariables = %u):", variables_num);
|
||||
for (i = 0; i < variables_num; ++i)
|
||||
printf_append(&pos, end, !variables[i] ? " false" : " true");
|
||||
printf_append(&pos, end, "\n");
|
||||
|
||||
printf_append(&pos, end, "current position: %u\n", current_pos);
|
||||
|
||||
/* Print current stack state. */
|
||||
printf_append(&pos, end, "stack (stack_size = %u):", stack_size);
|
||||
for (i = 0; i < stack_size; ++i)
|
||||
printf_append(&pos, end, !stack[i] ? " false" : " true");
|
||||
|
||||
error_msg_and_die("%s", buf);
|
||||
}
|
||||
|
||||
#define MAX_STACK_SIZE 32
|
||||
|
||||
bool
|
||||
run_expression(struct bool_expression *expr, bool *variables,
|
||||
unsigned int variables_num)
|
||||
{
|
||||
bool stack[MAX_STACK_SIZE];
|
||||
unsigned int stack_size = 0;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < expr->ntokens; ++i) {
|
||||
struct expression_token *tok = &expr->tokens[i];
|
||||
|
||||
switch (tok->type) {
|
||||
case TOK_VARIABLE:
|
||||
if (stack_size == MAX_STACK_SIZE)
|
||||
handle_corrupted_expression(expr, stack,
|
||||
stack_size, i,
|
||||
variables,
|
||||
variables_num);
|
||||
|
||||
if (tok->data.variable_id >= variables_num)
|
||||
handle_corrupted_expression(expr, stack,
|
||||
stack_size, i,
|
||||
variables,
|
||||
variables_num);
|
||||
stack[stack_size++] = variables[tok->data.variable_id];
|
||||
break;
|
||||
case TOK_OPERATOR:
|
||||
switch (tok->data.operator_id) {
|
||||
case OP_NOT:
|
||||
if (stack_size == 0)
|
||||
handle_corrupted_expression(expr, stack,
|
||||
stack_size, i,
|
||||
variables,
|
||||
variables_num);
|
||||
stack[stack_size - 1] = !stack[stack_size - 1];
|
||||
break;
|
||||
case OP_AND:
|
||||
if (stack_size < 2)
|
||||
handle_corrupted_expression(expr, stack,
|
||||
stack_size, i,
|
||||
variables,
|
||||
variables_num);
|
||||
stack[stack_size - 2] = stack[stack_size - 2]
|
||||
&& stack[stack_size - 1];
|
||||
--stack_size;
|
||||
break;
|
||||
case OP_OR:
|
||||
if (stack_size < 2)
|
||||
handle_corrupted_expression(expr, stack,
|
||||
stack_size, i,
|
||||
variables,
|
||||
variables_num);
|
||||
stack[stack_size - 2] = stack[stack_size - 2]
|
||||
|| stack[stack_size - 1];
|
||||
--stack_size;
|
||||
break;
|
||||
default:
|
||||
handle_corrupted_expression(expr, stack,
|
||||
stack_size, i,
|
||||
variables,
|
||||
variables_num);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (stack_size != 1)
|
||||
handle_corrupted_expression(expr, stack, stack_size, i,
|
||||
variables, variables_num);
|
||||
return stack[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse operator and add operator length to str and pos.
|
||||
* Return -1 if no operator found.
|
||||
*/
|
||||
static int
|
||||
parse_operator(char **str, unsigned int *pos)
|
||||
{
|
||||
#define _OP(s, op) { s, sizeof(s) - 1, op }
|
||||
struct {
|
||||
const char *str;
|
||||
int len;
|
||||
enum operator_type op;
|
||||
} ops[] = {
|
||||
_OP("!", OP_NOT),
|
||||
_OP("not", OP_NOT),
|
||||
_OP("&&", OP_AND),
|
||||
_OP("and", OP_AND),
|
||||
_OP("||", OP_OR),
|
||||
_OP("or", OP_OR),
|
||||
};
|
||||
#undef _OP
|
||||
char *p = *str;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ops); i++) {
|
||||
if (!strncmp(p, ops[i].str, ops[i].len) &&
|
||||
(!is_allowed_in_name(ops[i].str[0]) ||
|
||||
!is_allowed_in_name(p[ops[i].len]))) {
|
||||
*str += ops[i].len - 1;
|
||||
*pos += ops[i].len - 1;
|
||||
return ops[i].op;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char *
|
||||
unescape_argument(char **str)
|
||||
{
|
||||
char *p;
|
||||
char *p_new;
|
||||
bool escaped = false;
|
||||
unsigned int size = 1;
|
||||
char *new_str = xcalloc(strlen(*str) + 1, 1);
|
||||
|
||||
for (p = *str, p_new = new_str; *p; ++p) {
|
||||
if (!escaped) {
|
||||
if (*p == '\\') {
|
||||
escaped = true;
|
||||
continue;
|
||||
} else if (is_space_ascii(*p) || *p == ')' || *p == '|'
|
||||
|| *p == '&') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
escaped = false;
|
||||
*(p_new++) = *p;
|
||||
size++;
|
||||
}
|
||||
*str = p - 1;
|
||||
return xreallocarray(new_str, size, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
push_operator(int *stack, unsigned int *stack_size, int op)
|
||||
{
|
||||
if (*stack_size == MAX_STACK_SIZE)
|
||||
error_msg_and_die("stack overflow (expression is too complex)");
|
||||
stack[*stack_size] = op;
|
||||
(*stack_size)++;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_higher_priority(int op_a, int op_b)
|
||||
{
|
||||
bool op_priority[] = {
|
||||
[OP_NOT] = 2,
|
||||
[OP_AND] = 1,
|
||||
[OP_OR] = 0,
|
||||
};
|
||||
return op_priority[op_a] > op_priority[op_b];
|
||||
}
|
||||
|
||||
void
|
||||
parse_filter_expression(struct bool_expression *expr, const char *str,
|
||||
struct filter_action *action, unsigned int start_id)
|
||||
{
|
||||
enum {
|
||||
WAIT_FILTER,
|
||||
FILTER_NAME,
|
||||
FILTER_ARG,
|
||||
WAIT_OPERATOR,
|
||||
} state = WAIT_FILTER;
|
||||
unsigned int variable_id = start_id;
|
||||
/* Current stack stack_size */
|
||||
unsigned int st_size = 0;
|
||||
int stack[MAX_STACK_SIZE];
|
||||
char *buf = xstrdup(str);
|
||||
struct filter *cur_filter = NULL;
|
||||
char *filter_name = NULL;
|
||||
char *filter_arg = NULL;
|
||||
int op;
|
||||
char *p;
|
||||
unsigned int pos = 0;
|
||||
|
||||
for (p = buf; *p; ++p, ++pos) {
|
||||
switch (state) {
|
||||
case WAIT_FILTER:
|
||||
if (*p == '(') {
|
||||
push_operator(stack, &st_size, OP_PARENTHESIS);
|
||||
} else if ((op = parse_operator(&p, &pos)) >= 0) {
|
||||
if (op == OP_NOT) {
|
||||
push_operator(stack, &st_size, op);
|
||||
} else {
|
||||
error_msg_and_die("invalid operator "
|
||||
"at '%s':%u",
|
||||
str, pos);
|
||||
}
|
||||
} else if (!is_space_ascii(*p)) {
|
||||
filter_name = p;
|
||||
state = FILTER_NAME;
|
||||
}
|
||||
break;
|
||||
|
||||
case FILTER_NAME:
|
||||
if (is_space_ascii(*p)) {
|
||||
*p = '\0';
|
||||
cur_filter = create_filter(action, filter_name);
|
||||
filter_arg = NULL;
|
||||
state = FILTER_ARG;
|
||||
}
|
||||
break;
|
||||
|
||||
case FILTER_ARG:
|
||||
if (!filter_arg && is_space_ascii(*p))
|
||||
break;
|
||||
filter_arg = unescape_argument(&p);
|
||||
parse_filter(cur_filter, filter_arg, false);
|
||||
free(filter_arg);
|
||||
add_variable_token(expr, variable_id++);
|
||||
state = WAIT_OPERATOR;
|
||||
break;
|
||||
|
||||
case WAIT_OPERATOR:
|
||||
if (is_space_ascii(*p))
|
||||
break;
|
||||
if (*p == ')') {
|
||||
while ((st_size > 0) &&
|
||||
(stack[st_size - 1] != OP_PARENTHESIS)) {
|
||||
op = stack[--st_size];
|
||||
add_operator_token(expr, op);
|
||||
}
|
||||
--st_size;
|
||||
break;
|
||||
}
|
||||
op = parse_operator(&p, &pos);
|
||||
if (op < 0 || op == OP_NOT)
|
||||
error_msg_and_die("invalid operator at '%s':%u",
|
||||
str, pos);
|
||||
|
||||
/* Pop operators with higher priority. */
|
||||
while ((st_size > 0) &&
|
||||
(stack[st_size - 1] != OP_PARENTHESIS) &&
|
||||
is_higher_priority(stack[st_size - 1], op))
|
||||
add_operator_token(expr, stack[--st_size]);
|
||||
|
||||
push_operator(stack, &st_size, op);
|
||||
state = WAIT_FILTER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
if (state != WAIT_OPERATOR)
|
||||
error_msg_and_die("unfinished filter expression '%s'", str);
|
||||
|
||||
while (st_size > 0)
|
||||
add_operator_token(expr, stack[--st_size]);
|
||||
if (start_id > 0)
|
||||
add_operator_token(expr, OP_OR);
|
||||
}
|
252
filter_parse.c
Normal file
252
filter_parse.c
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "defs.h"
|
||||
#include "filter.h"
|
||||
|
||||
bool
|
||||
is_space_ascii(char c)
|
||||
{
|
||||
return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r') ||
|
||||
(c == '\v') || (c == '\f');
|
||||
}
|
||||
|
||||
bool
|
||||
is_allowed_in_name(char c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
|
||||
|| (c >= '0' && c <= '9') || (c == '_');
|
||||
}
|
||||
|
||||
/*
|
||||
* Split expression into action name, filter expression or qualify set
|
||||
* and action arguments.
|
||||
*/
|
||||
void
|
||||
filtering_parse(const char *str)
|
||||
{
|
||||
enum parsing_states {
|
||||
F_EMPTY,
|
||||
F_BEGIN,
|
||||
F_QUAL_SET,
|
||||
F_FILT_EXPR,
|
||||
F_QUAL_ARGS,
|
||||
F_FILT_ARGS,
|
||||
F_END,
|
||||
} state = F_EMPTY;
|
||||
const char *begin = NULL;
|
||||
const char *action_name = "trace";
|
||||
const char *main_part = NULL;
|
||||
const char *args = NULL;
|
||||
bool action_specified = false;
|
||||
bool escaped = false;
|
||||
int parentheses_count = 0;
|
||||
/* Used to store position of last terminating parenthesis. */
|
||||
char *expression_end = NULL;
|
||||
/* Used to provide diagnostics. */
|
||||
unsigned int pos = 0;
|
||||
char *buf = xstrdup(str);
|
||||
char *p;
|
||||
|
||||
for (p = buf; *p; ++p, ++pos) {
|
||||
switch (state) {
|
||||
case F_EMPTY:
|
||||
switch (*p) {
|
||||
/* trace(), action name omitted */
|
||||
case '(':
|
||||
parentheses_count++;
|
||||
main_part = p;
|
||||
state = F_FILT_EXPR;
|
||||
break;
|
||||
/* missing action name */
|
||||
case '=':
|
||||
*p = '\0';
|
||||
error_msg_and_die("invalid filter action '%s'",
|
||||
buf);
|
||||
default:
|
||||
if (is_space_ascii(*p)) {
|
||||
break;
|
||||
} else if (!strncmp(p, "not", 3) && *(p + 3) &&
|
||||
(is_space_ascii(*(p + 3)) ||
|
||||
*(p + 3) == '(')) {
|
||||
main_part = p;
|
||||
state = F_FILT_EXPR;
|
||||
break;
|
||||
} else {
|
||||
begin = p;
|
||||
state = F_BEGIN;
|
||||
}
|
||||
}
|
||||
if (state != F_BEGIN)
|
||||
break;
|
||||
/* else fall through to check for qualify set */
|
||||
|
||||
case F_BEGIN:
|
||||
switch (*p) {
|
||||
/* action(...) */
|
||||
case '(':
|
||||
if (*begin == '!') {
|
||||
main_part = begin;
|
||||
} else {
|
||||
action_name = begin;
|
||||
action_specified = true;
|
||||
*p = '\0';
|
||||
main_part = p + 1;
|
||||
}
|
||||
state = F_FILT_EXPR;
|
||||
parentheses_count++;
|
||||
break;
|
||||
/* action=... */
|
||||
case '=':
|
||||
action_name = begin;
|
||||
action_specified = true;
|
||||
*p = '\0';
|
||||
main_part = p + 1;
|
||||
state = F_QUAL_SET;
|
||||
break;
|
||||
case ':':
|
||||
main_part = begin;
|
||||
*p = '\0';
|
||||
args = p + 1;
|
||||
state = F_QUAL_ARGS;
|
||||
break;
|
||||
case ';':
|
||||
error_msg_and_die("invalid arguments position "
|
||||
"'%s':%u",
|
||||
str, pos);
|
||||
/* qualify set without action. */
|
||||
case ',':
|
||||
case '?':
|
||||
case '/':
|
||||
case '%':
|
||||
case '-':
|
||||
main_part = begin;
|
||||
state = F_QUAL_SET;
|
||||
break;
|
||||
default:
|
||||
/* new expression without action. */
|
||||
if (is_space_ascii(*p)) {
|
||||
main_part = begin;
|
||||
state = F_FILT_EXPR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case F_QUAL_SET:
|
||||
if (*p == ':') {
|
||||
*p = '\0';
|
||||
args = p + 1;
|
||||
state = F_QUAL_ARGS;
|
||||
}
|
||||
break;
|
||||
|
||||
case F_FILT_EXPR:
|
||||
if (!escaped) {
|
||||
switch (*p) {
|
||||
case ';':
|
||||
if (parentheses_count != 1 ||
|
||||
!action_specified)
|
||||
error_msg_and_die("invalid "
|
||||
"arguments "
|
||||
"position "
|
||||
"'%s':%u",
|
||||
str, pos);
|
||||
*p = '\0';
|
||||
args = p + 1;
|
||||
state = F_FILT_ARGS;
|
||||
break;
|
||||
case '(':
|
||||
parentheses_count++;
|
||||
break;
|
||||
case ')':
|
||||
if (parentheses_count <= 0)
|
||||
error_msg_and_die("unexpected "
|
||||
"')' at "
|
||||
"'%s':%u",
|
||||
str, pos);
|
||||
parentheses_count--;
|
||||
expression_end = p;
|
||||
if (action_specified &&
|
||||
parentheses_count == 0)
|
||||
state = F_END;
|
||||
break;
|
||||
case '\\':
|
||||
escaped = true;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
escaped = false;
|
||||
break;
|
||||
|
||||
case F_QUAL_ARGS:
|
||||
break;
|
||||
case F_FILT_ARGS:
|
||||
if (!escaped) {
|
||||
switch (*p) {
|
||||
case ')':
|
||||
parentheses_count--;
|
||||
expression_end = p;
|
||||
state = F_END;
|
||||
break;
|
||||
case '\\':
|
||||
escaped = true;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
escaped = false;
|
||||
break;
|
||||
case F_END:
|
||||
if (!is_space_ascii(*p))
|
||||
error_msg_and_die("unexpected '%c' at "
|
||||
"'%s':%u", *p, str, pos);
|
||||
}
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case F_EMPTY:
|
||||
main_part = buf;
|
||||
parse_qualify_action(action_name, main_part, args);
|
||||
break;
|
||||
case F_BEGIN:
|
||||
main_part = begin;
|
||||
/* fall through */
|
||||
case F_QUAL_SET:
|
||||
case F_QUAL_ARGS:
|
||||
parse_qualify_action(action_name, main_part, args);
|
||||
break;
|
||||
case F_FILT_EXPR:
|
||||
case F_FILT_ARGS:
|
||||
case F_END:
|
||||
if (parentheses_count != 0)
|
||||
error_msg_and_die("missing ')' in '%s'", str);
|
||||
if (action_specified && expression_end)
|
||||
*expression_end = '\0';
|
||||
parse_filter_action(action_name, main_part, args);
|
||||
break;
|
||||
}
|
||||
free(buf);
|
||||
}
|
305
filter_qualify.c
305
filter_qualify.c
@ -31,16 +31,8 @@
|
||||
#include "number_set.h"
|
||||
#include "filter.h"
|
||||
|
||||
struct number_set *read_set;
|
||||
struct number_set *write_set;
|
||||
struct number_set *signal_set;
|
||||
|
||||
static struct number_set *abbrev_set;
|
||||
static struct number_set *inject_set;
|
||||
static struct number_set *raw_set;
|
||||
static struct number_set *trace_set;
|
||||
static struct number_set *verbose_set;
|
||||
|
||||
static int
|
||||
sigstr_to_uint(const char *s)
|
||||
{
|
||||
@ -154,166 +146,170 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts,
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *
|
||||
parse_inject_expression(const char *const s, char **buf,
|
||||
struct inject_opts *const fopts,
|
||||
const bool fault_tokens_only)
|
||||
void
|
||||
parse_inject_common_args(char *str, struct inject_opts *const opts,
|
||||
const bool fault_tokens_only, bool qualify_mode)
|
||||
{
|
||||
char *saveptr = NULL;
|
||||
char *name = NULL;
|
||||
char *token;
|
||||
const char *delim = qualify_mode ? ":" : ";";
|
||||
|
||||
*buf = xstrdup(s);
|
||||
for (token = strtok_r(*buf, ":", &saveptr); token;
|
||||
token = strtok_r(NULL, ":", &saveptr)) {
|
||||
if (!name)
|
||||
name = token;
|
||||
else if (!parse_inject_token(token, fopts, fault_tokens_only))
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
if (name)
|
||||
return name;
|
||||
|
||||
parse_error:
|
||||
free(*buf);
|
||||
return *buf = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_read(const char *const str)
|
||||
{
|
||||
if (!read_set)
|
||||
read_set = alloc_number_set_array(1);
|
||||
qualify_tokens(str, read_set, string_to_uint, "descriptor");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_write(const char *const str)
|
||||
{
|
||||
if (!write_set)
|
||||
write_set = alloc_number_set_array(1);
|
||||
qualify_tokens(str, write_set, string_to_uint, "descriptor");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_signals(const char *const str)
|
||||
{
|
||||
if (!signal_set)
|
||||
signal_set = alloc_number_set_array(1);
|
||||
qualify_tokens(str, signal_set, sigstr_to_uint, "signal");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_trace(const char *const str)
|
||||
{
|
||||
if (!trace_set)
|
||||
trace_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
|
||||
qualify_syscall_tokens(str, trace_set, "system call");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_abbrev(const char *const str)
|
||||
{
|
||||
if (!abbrev_set)
|
||||
abbrev_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
|
||||
qualify_syscall_tokens(str, abbrev_set, "system call");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_verbose(const char *const str)
|
||||
{
|
||||
if (!verbose_set)
|
||||
verbose_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
|
||||
qualify_syscall_tokens(str, verbose_set, "system call");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_raw(const char *const str)
|
||||
{
|
||||
if (!raw_set)
|
||||
raw_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
|
||||
qualify_syscall_tokens(str, raw_set, "system call");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_inject_common(const char *const str,
|
||||
const bool fault_tokens_only,
|
||||
const char *const description)
|
||||
{
|
||||
struct inject_opts opts = {
|
||||
*opts = (struct inject_opts) {
|
||||
.first = 1,
|
||||
.step = 1
|
||||
};
|
||||
char *buf = NULL;
|
||||
char *name = parse_inject_expression(str, &buf, &opts, fault_tokens_only);
|
||||
if (!name) {
|
||||
error_msg_and_die("invalid %s '%s'", description, str);
|
||||
|
||||
for (token = strtok_r(str, delim, &saveptr); token;
|
||||
token = strtok_r(NULL, delim, &saveptr)) {
|
||||
if (!parse_inject_token(token, opts, fault_tokens_only)) {
|
||||
/* return an error by resetting inject flags */
|
||||
opts->data.flags = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If neither of retval, error, or signal is specified, then ... */
|
||||
if (!opts.data.flags) {
|
||||
if (!opts->data.flags) {
|
||||
if (fault_tokens_only) {
|
||||
/* in fault= syntax the default error code is ENOSYS. */
|
||||
opts.data.rval = -ENOSYS;
|
||||
opts.data.flags |= INJECT_F_RETVAL;
|
||||
opts->data.rval = -ENOSYS;
|
||||
opts->data.flags |= INJECT_F_RETVAL;
|
||||
} else {
|
||||
/* in inject= syntax this is not allowed. */
|
||||
error_msg_and_die("invalid %s '%s'", description, str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct number_set *tmp_set =
|
||||
alloc_number_set_array(SUPPORTED_PERSONALITIES);
|
||||
qualify_syscall_tokens(name, tmp_set, description);
|
||||
static void
|
||||
qualify_read(const char *const main_part, const char *const args)
|
||||
{
|
||||
struct filter_action *action = find_or_add_action("read");
|
||||
struct filter *filter = create_filter(action, "fd");
|
||||
|
||||
parse_filter(filter, main_part, true);
|
||||
if (args)
|
||||
error_msg("read action takes no arguments, ignored arguments "
|
||||
"'%s'", args);
|
||||
set_qualify_mode(action, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_write(const char *const main_part, const char *const args)
|
||||
{
|
||||
struct filter_action *action = find_or_add_action("write");
|
||||
struct filter *filter = create_filter(action, "fd");
|
||||
|
||||
parse_filter(filter, main_part, true);
|
||||
if (args)
|
||||
error_msg("write action takes no arguments, ignored arguments "
|
||||
"'%s'", args);
|
||||
set_qualify_mode(action, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_signals(const char *const main_part, const char *const args)
|
||||
{
|
||||
if (!signal_set)
|
||||
signal_set = alloc_number_set_array(1);
|
||||
|
||||
qualify_tokens(main_part, signal_set, sigstr_to_uint, "signal", true);
|
||||
if (args)
|
||||
error_msg("signal action takes no arguments, ignored arguments "
|
||||
"'%s'", args);
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_trace(const char *const main_part, const char *const args)
|
||||
{
|
||||
struct filter_action *action = find_or_add_action("trace");
|
||||
struct filter *filter = create_filter(action, "syscall");
|
||||
|
||||
parse_filter(filter, main_part, true);
|
||||
if (args)
|
||||
error_msg("trace action takes no arguments, ignored arguments "
|
||||
"'%s'", args);
|
||||
set_qualify_mode(action, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_abbrev(const char *const main_part, const char *const args)
|
||||
{
|
||||
struct filter_action *action = find_or_add_action("abbrev");
|
||||
struct filter *filter = create_filter(action, "syscall");
|
||||
|
||||
parse_filter(filter, main_part, true);
|
||||
if (args)
|
||||
error_msg("abbrev action takes no arguments, ignored arguments "
|
||||
"'%s'", args);
|
||||
set_qualify_mode(action, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_verbose(const char *const main_part, const char *const args)
|
||||
{
|
||||
struct filter_action *action = find_or_add_action("verbose");
|
||||
struct filter *filter = create_filter(action, "syscall");
|
||||
|
||||
parse_filter(filter, main_part, true);
|
||||
if (args)
|
||||
error_msg("verbose action takes no arguments, ignored arguments"
|
||||
" '%s'", args);
|
||||
set_qualify_mode(action, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_raw(const char *const main_part, const char *const args)
|
||||
{
|
||||
struct filter_action *action = find_or_add_action("raw");
|
||||
struct filter *filter = create_filter(action, "syscall");
|
||||
|
||||
parse_filter(filter, main_part, true);
|
||||
if (args)
|
||||
error_msg("raw action takes no arguments, ignored arguments "
|
||||
"'%s'", args);
|
||||
set_qualify_mode(action, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_inject_common(const char *const main_part, const char *const args,
|
||||
const bool fault_tokens_only,
|
||||
const char *const description)
|
||||
{
|
||||
struct inject_opts *opts = xmalloc(sizeof(struct inject_opts));
|
||||
char *buf = xstrdup(args);
|
||||
struct filter_action *action;
|
||||
struct filter *filter;
|
||||
|
||||
action = find_or_add_action(fault_tokens_only ? "fault" : "inject");
|
||||
filter = create_filter(action, "syscall");
|
||||
parse_filter(filter, main_part, true);
|
||||
set_qualify_mode(action, 1);
|
||||
parse_inject_common_args(buf, opts, fault_tokens_only, true);
|
||||
|
||||
if (!opts->data.flags)
|
||||
error_msg_and_die("invalid %s argument '%s'", description,
|
||||
args ? args : "");
|
||||
free(buf);
|
||||
|
||||
/*
|
||||
* Initialize inject_vec accourding to tmp_set.
|
||||
* Merge tmp_set into inject_set.
|
||||
*/
|
||||
unsigned int p;
|
||||
for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
|
||||
if (number_set_array_is_empty(tmp_set, p))
|
||||
continue;
|
||||
|
||||
if (!inject_set) {
|
||||
inject_set =
|
||||
alloc_number_set_array(SUPPORTED_PERSONALITIES);
|
||||
}
|
||||
if (!inject_vec[p]) {
|
||||
inject_vec[p] = xcalloc(nsyscall_vec[p],
|
||||
sizeof(*inject_vec[p]));
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < nsyscall_vec[p]; ++i) {
|
||||
if (is_number_in_set_array(i, tmp_set, p)) {
|
||||
add_number_to_set_array(i, inject_set, p);
|
||||
inject_vec[p][i] = opts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_number_set_array(tmp_set, SUPPORTED_PERSONALITIES);
|
||||
set_filter_action_priv_data(action, opts);
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_fault(const char *const str)
|
||||
qualify_fault(const char *const main_part, const char *const args)
|
||||
{
|
||||
qualify_inject_common(str, true, "fault argument");
|
||||
qualify_inject_common(main_part, args, true, "fault");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_inject(const char *const str)
|
||||
qualify_inject(const char *const main_part, const char *const args)
|
||||
{
|
||||
qualify_inject_common(str, false, "inject argument");
|
||||
qualify_inject_common(main_part, args, false, "inject");
|
||||
}
|
||||
|
||||
static const struct qual_options {
|
||||
const char *name;
|
||||
void (*qualify)(const char *);
|
||||
void (*qualify)(const char *, const char *);
|
||||
} qual_options[] = {
|
||||
{ "trace", qualify_trace },
|
||||
{ "t", qualify_trace },
|
||||
@ -337,37 +333,20 @@ static const struct qual_options {
|
||||
};
|
||||
|
||||
void
|
||||
qualify(const char *str)
|
||||
parse_qualify_action(const char *action_name, const char *main_part,
|
||||
const char *args)
|
||||
{
|
||||
const struct qual_options *opt = qual_options;
|
||||
const struct qual_options *opt = NULL;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(qual_options); ++i) {
|
||||
const char *name = qual_options[i].name;
|
||||
const size_t len = strlen(name);
|
||||
const char *val = str_strip_prefix_len(str, name, len);
|
||||
|
||||
if (val == str || *val != '=')
|
||||
continue;
|
||||
str = val + 1;
|
||||
opt = &qual_options[i];
|
||||
break;
|
||||
if (!strcmp(action_name, qual_options[i].name)) {
|
||||
opt = &qual_options[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
opt->qualify(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);
|
||||
if (!opt)
|
||||
error_msg_and_die("invalid filter action '%s'", action_name);
|
||||
opt->qualify(main_part ? main_part : "", args);
|
||||
}
|
||||
|
@ -59,8 +59,6 @@ alloc_number_set_array(unsigned int nmemb) ATTRIBUTE_MALLOC;
|
||||
extern void
|
||||
free_number_set_array(struct number_set *, unsigned int nmemb);
|
||||
|
||||
extern struct number_set *read_set;
|
||||
extern struct number_set *write_set;
|
||||
extern struct number_set *signal_set;
|
||||
|
||||
#endif /* !STRACE_NUMBER_SET_H */
|
||||
|
199
pathtrace.c
199
pathtrace.c
@ -69,6 +69,8 @@ upathmatch(struct tcb *const tcp, const kernel_ulong_t upath,
|
||||
static bool
|
||||
fdmatch(struct tcb *tcp, int fd, struct path_set *set)
|
||||
{
|
||||
if (fd < 0)
|
||||
return false;
|
||||
char path[PATH_MAX + 1];
|
||||
int n = getfdpath(tcp, fd, path, sizeof(path));
|
||||
|
||||
@ -90,7 +92,7 @@ storepath(const char *path, struct path_set *set)
|
||||
xgrowarray(set->paths_selected, &set->size,
|
||||
sizeof(set->paths_selected[0]));
|
||||
|
||||
set->paths_selected[set->num_selected++] = path;
|
||||
set->paths_selected[set->num_selected++] = xstrdup(path);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -142,25 +144,23 @@ pathtrace_select_set(const char *path, struct path_set *set)
|
||||
storepath(rpath, set);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if syscall accesses a selected path
|
||||
* (or if no paths have been specified for tracing).
|
||||
*/
|
||||
bool
|
||||
pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
typedef bool (*match_fd_func)(struct tcb *, int, void *);
|
||||
|
||||
static
|
||||
bool fdmatch_fd_func(struct tcb *tcp, int fd, void *data)
|
||||
{
|
||||
const struct_sysent *s;
|
||||
return fdmatch(tcp, fd, (struct path_set *) data);
|
||||
}
|
||||
|
||||
s = tcp->s_ent;
|
||||
/* Match fd with func. */
|
||||
bool
|
||||
match_fd_common(struct tcb *tcp, match_fd_func func, void *data)
|
||||
{
|
||||
const struct_sysent *s = tcp->s_ent;
|
||||
|
||||
if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
|
||||
if (!(s->sys_flags & (TRACE_DESC | TRACE_NETWORK)))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Check for special cases where we need to do something
|
||||
* other than test arg[0].
|
||||
*/
|
||||
|
||||
switch (s->sen) {
|
||||
case SEN_dup2:
|
||||
case SEN_dup3:
|
||||
@ -169,49 +169,17 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
case SEN_sendfile64:
|
||||
case SEN_tee:
|
||||
/* fd, fd */
|
||||
return fdmatch(tcp, tcp->u_arg[0], set) ||
|
||||
fdmatch(tcp, tcp->u_arg[1], set);
|
||||
|
||||
case SEN_execveat:
|
||||
case SEN_faccessat:
|
||||
case SEN_fchmodat:
|
||||
case SEN_fchownat:
|
||||
case SEN_fstatat64:
|
||||
case SEN_futimesat:
|
||||
case SEN_inotify_add_watch:
|
||||
case SEN_mkdirat:
|
||||
case SEN_mknodat:
|
||||
case SEN_name_to_handle_at:
|
||||
case SEN_newfstatat:
|
||||
case SEN_openat:
|
||||
case SEN_readlinkat:
|
||||
case SEN_statx:
|
||||
case SEN_unlinkat:
|
||||
case SEN_utimensat:
|
||||
/* fd, path */
|
||||
return fdmatch(tcp, tcp->u_arg[0], set) ||
|
||||
upathmatch(tcp, tcp->u_arg[1], set);
|
||||
|
||||
case SEN_link:
|
||||
case SEN_mount:
|
||||
case SEN_pivotroot:
|
||||
/* path, path */
|
||||
return upathmatch(tcp, tcp->u_arg[0], set) ||
|
||||
upathmatch(tcp, tcp->u_arg[1], set);
|
||||
|
||||
case SEN_quotactl:
|
||||
case SEN_symlink:
|
||||
/* x, path */
|
||||
return upathmatch(tcp, tcp->u_arg[1], set);
|
||||
return func(tcp, tcp->u_arg[0], data) ||
|
||||
func(tcp, tcp->u_arg[1], data);
|
||||
|
||||
case SEN_copy_file_range:
|
||||
case SEN_linkat:
|
||||
case SEN_renameat2:
|
||||
case SEN_renameat:
|
||||
/* fd, path, fd, path */
|
||||
return fdmatch(tcp, tcp->u_arg[0], set) ||
|
||||
fdmatch(tcp, tcp->u_arg[2], set) ||
|
||||
upathmatch(tcp, tcp->u_arg[1], set) ||
|
||||
upathmatch(tcp, tcp->u_arg[3], set);
|
||||
case SEN_splice:
|
||||
/* fd, x, fd */
|
||||
return func(tcp, tcp->u_arg[0], data) ||
|
||||
func(tcp, tcp->u_arg[2], data);
|
||||
|
||||
case SEN_old_mmap:
|
||||
#if defined(S390)
|
||||
@ -222,22 +190,15 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
case SEN_mmap_pgoff:
|
||||
case SEN_ARCH_mmap:
|
||||
/* x, x, x, x, fd */
|
||||
return fdmatch(tcp, tcp->u_arg[4], set);
|
||||
return func(tcp, tcp->u_arg[4], data);
|
||||
|
||||
case SEN_symlinkat:
|
||||
/* x, fd, path */
|
||||
return fdmatch(tcp, tcp->u_arg[1], set) ||
|
||||
upathmatch(tcp, tcp->u_arg[2], set);
|
||||
|
||||
case SEN_copy_file_range:
|
||||
case SEN_splice:
|
||||
/* fd, x, fd, x, x, x */
|
||||
return fdmatch(tcp, tcp->u_arg[0], set) ||
|
||||
fdmatch(tcp, tcp->u_arg[2], set);
|
||||
/* x, fd, x */
|
||||
return func(tcp, tcp->u_arg[1], data);
|
||||
|
||||
case SEN_epoll_ctl:
|
||||
/* x, x, fd, x */
|
||||
return fdmatch(tcp, tcp->u_arg[2], set);
|
||||
return func(tcp, tcp->u_arg[2], data);
|
||||
|
||||
|
||||
case SEN_fanotify_mark:
|
||||
@ -245,9 +206,10 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
/* x, x, mask (64 bit), fd, path */
|
||||
unsigned long long mask = 0;
|
||||
int argn = getllval(tcp, &mask, 2);
|
||||
return fdmatch(tcp, tcp->u_arg[argn], set) ||
|
||||
upathmatch(tcp, tcp->u_arg[argn + 1], set);
|
||||
|
||||
return func(tcp, tcp->u_arg[argn], data);
|
||||
}
|
||||
|
||||
case SEN_oldselect:
|
||||
case SEN_pselect6:
|
||||
case SEN_select:
|
||||
@ -302,7 +264,7 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
j = next_set_bit(fds, j, nfds);
|
||||
if (j < 0)
|
||||
break;
|
||||
if (fdmatch(tcp, j, set)) {
|
||||
if (func(tcp, j, data)) {
|
||||
free(fds);
|
||||
return true;
|
||||
}
|
||||
@ -332,14 +294,19 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
for (cur = start; cur < end; cur += sizeof(fds)) {
|
||||
if (umove(tcp, cur, &fds))
|
||||
break;
|
||||
if (fdmatch(tcp, fds.fd, set))
|
||||
if (func(tcp, fds.fd, data))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* These have TRACE_DESCRIPTOR or TRACE_NETWORK set,
|
||||
* but they don't have any file descriptor to test.
|
||||
*/
|
||||
case SEN_bpf:
|
||||
case SEN_creat:
|
||||
case SEN_epoll_create:
|
||||
case SEN_epoll_create1:
|
||||
case SEN_eventfd2:
|
||||
@ -348,6 +315,7 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
case SEN_inotify_init:
|
||||
case SEN_inotify_init1:
|
||||
case SEN_memfd_create:
|
||||
case SEN_open:
|
||||
case SEN_perf_event_open:
|
||||
case SEN_pipe:
|
||||
case SEN_pipe2:
|
||||
@ -355,13 +323,85 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
case SEN_socket:
|
||||
case SEN_socketpair:
|
||||
case SEN_timerfd_create:
|
||||
case SEN_timerfd_gettime:
|
||||
case SEN_timerfd_settime:
|
||||
case SEN_userfaultfd:
|
||||
/*
|
||||
* These have TRACE_FILE or TRACE_DESCRIPTOR or TRACE_NETWORK set,
|
||||
* but they don't have any file descriptor or path args to test.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
return func(tcp, tcp->u_arg[0], data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if syscall accesses a selected path
|
||||
* (or if no paths have been specified for tracing).
|
||||
*/
|
||||
bool
|
||||
pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
{
|
||||
const struct_sysent *s;
|
||||
|
||||
s = tcp->s_ent;
|
||||
|
||||
if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC | TRACE_NETWORK)))
|
||||
return false;
|
||||
|
||||
if (match_fd_common(tcp, fdmatch_fd_func, set))
|
||||
return true;
|
||||
|
||||
if (!(s->sys_flags & TRACE_FILE))
|
||||
return false;
|
||||
/*
|
||||
* Check for special cases where we need to do something
|
||||
* other than test arg[0].
|
||||
*/
|
||||
switch (s->sen) {
|
||||
case SEN_execveat:
|
||||
case SEN_faccessat:
|
||||
case SEN_fchmodat:
|
||||
case SEN_fchownat:
|
||||
case SEN_fstatat64:
|
||||
case SEN_futimesat:
|
||||
case SEN_inotify_add_watch:
|
||||
case SEN_mkdirat:
|
||||
case SEN_mknodat:
|
||||
case SEN_name_to_handle_at:
|
||||
case SEN_newfstatat:
|
||||
case SEN_openat:
|
||||
case SEN_quotactl:
|
||||
case SEN_readlinkat:
|
||||
case SEN_symlink:
|
||||
case SEN_statx:
|
||||
case SEN_unlinkat:
|
||||
case SEN_utimensat:
|
||||
/* x, path */
|
||||
return upathmatch(tcp, tcp->u_arg[1], set);
|
||||
|
||||
case SEN_link:
|
||||
case SEN_mount:
|
||||
case SEN_pivotroot:
|
||||
/* path, path */
|
||||
return upathmatch(tcp, tcp->u_arg[0], set) ||
|
||||
upathmatch(tcp, tcp->u_arg[1], set);
|
||||
|
||||
case SEN_linkat:
|
||||
case SEN_renameat2:
|
||||
case SEN_renameat:
|
||||
/* x, path, x, path */
|
||||
return upathmatch(tcp, tcp->u_arg[1], set) ||
|
||||
upathmatch(tcp, tcp->u_arg[3], set);
|
||||
|
||||
case SEN_symlinkat:
|
||||
/* x, x, path */
|
||||
return upathmatch(tcp, tcp->u_arg[2], set);
|
||||
|
||||
case SEN_fanotify_mark:
|
||||
{
|
||||
/* x, x, mask (64 bit), fd, path */
|
||||
unsigned long long mask = 0;
|
||||
int argn = getllval(tcp, &mask, 2);
|
||||
|
||||
return upathmatch(tcp, tcp->u_arg[argn + 1], set);
|
||||
}
|
||||
|
||||
case SEN_printargs:
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -369,12 +409,5 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
||||
* Our fallback position for calls that haven't already
|
||||
* been handled is to just check arg[0].
|
||||
*/
|
||||
|
||||
if (s->sys_flags & TRACE_FILE)
|
||||
return upathmatch(tcp, tcp->u_arg[0], set);
|
||||
|
||||
if (s->sys_flags & (TRACE_DESC | TRACE_NETWORK))
|
||||
return fdmatch(tcp, tcp->u_arg[0], set);
|
||||
|
||||
return false;
|
||||
return upathmatch(tcp, tcp->u_arg[0], set);
|
||||
}
|
||||
|
272
strace.1.in
272
strace.1.in
@ -354,10 +354,15 @@ and
|
||||
Summarise the time difference between the beginning and end of
|
||||
each system call. The default is to summarise the system time.
|
||||
.SS Filtering
|
||||
.LP
|
||||
.B strace
|
||||
has two types of expressions with different syntax: set-based
|
||||
expressions and filter-based expressions.
|
||||
.SS Set-based expression
|
||||
.TP 12
|
||||
.BI "\-e " expr
|
||||
A qualifying expression which modifies which events to trace
|
||||
or how to trace them. The format of the expression is:
|
||||
Set-based expression modifies which events to trace
|
||||
or how to trace them. The format of the expression is:
|
||||
.RS 15
|
||||
.IP
|
||||
[\,\fIqualifier\/\fB=\fR][\fB!\fR][\fB?\fR]\,\fIvalue1\/\fR[\fB,\fR[\fB?\fR]\,\fIvalue2\/\fR]...
|
||||
@ -677,6 +682,269 @@ This is equivalent to more generic
|
||||
option set to
|
||||
.IR ENOSYS .
|
||||
|
||||
.SS Filter-based expression
|
||||
.TP 12
|
||||
.BI "\-e " expr
|
||||
Filter-based expression modifies which actions are applied
|
||||
to syscalls and conditions when actions
|
||||
are applied. The format of the expression is:
|
||||
.RS 15
|
||||
.IP
|
||||
[\,\fIaction\/\fB(\fR]\fIfilter_expression\/\fR[\fB;\fR\fIarg1\/\fR[\fB;\fR\fIarg2\/\fR...]][\fB)\fR]
|
||||
.RE
|
||||
.IP
|
||||
where
|
||||
.I action
|
||||
is one of
|
||||
.BR trace ,
|
||||
.BR abbrev ,
|
||||
.BR verbose ,
|
||||
.BR raw ,
|
||||
.BR read ,
|
||||
.BR write ,
|
||||
.BR fault ,
|
||||
.BR inject ,
|
||||
or
|
||||
.B stacktrace
|
||||
and
|
||||
.I arg
|
||||
is an action-dependent argument. The default action is
|
||||
.BR trace .
|
||||
.I arg
|
||||
can be specified only when action is specified.
|
||||
.I arg
|
||||
is ignored if the action does not take any arguments.
|
||||
.TP
|
||||
\,\fIfilter_expression\fR
|
||||
The filter expression consits of one or more \fIfilters\fR.
|
||||
\fIFilters\fR consist of a \fIfilter_name\fR and filter-dependent argument.
|
||||
Complex expressions are built up by using
|
||||
the operators \fBand, or\fR and \fBnot\fR
|
||||
(or their short versions: \fB&&, ||\fR and \fB!\fR)
|
||||
to combine \fIfilters\fR. Allowable filters are:
|
||||
.TP
|
||||
\fBsyscall\fR \fIsyscall-specifier-set\fR
|
||||
Match system calls by name or syscall number. The format of
|
||||
\fIsyscall-specifier-set\fR is:
|
||||
.RS 15
|
||||
.IP
|
||||
\fIsyscall-specifier1\fR[\fB,\fR\fIsyscall-specifier2\fR...]
|
||||
.RE
|
||||
.IP
|
||||
where \fIsyscall-specifier\fR can be a system call name,
|
||||
a class of system calls or
|
||||
regular expression.
|
||||
In addition, the special values
|
||||
.B all
|
||||
and
|
||||
.B none
|
||||
have the obvious meanings.
|
||||
.TP
|
||||
\fBsyscall\fR \fIsyscall1\fR[\fB,\fR\fIsyscall2\fR...]
|
||||
Match only the specified set of system calls. For example,
|
||||
.BR syscall\ open,close,read,write
|
||||
means to only match those four system calls.
|
||||
.TP
|
||||
\fBsyscall\fR %\fIclass\fR
|
||||
where
|
||||
.I class
|
||||
is one of
|
||||
.BR file ,
|
||||
.BR process ,
|
||||
.BR network ,
|
||||
.BR signal ,
|
||||
.BR ipc ,
|
||||
.BR desc ,
|
||||
.BR memory ,
|
||||
.BR stat ,
|
||||
.BR lstat ,
|
||||
.BR fstat ,
|
||||
.BR %stat ,
|
||||
.BR statfs ,
|
||||
.BR fstatfs ,
|
||||
.BR %statfs .
|
||||
These classes are similar to classes, defined in set-based expression's
|
||||
section (\fB-e trace=%\fR\fIclass\fR).
|
||||
.TP
|
||||
\fBsyscall\fR /\fIregex\fR
|
||||
Match only those system calls that match the
|
||||
.IR regex .
|
||||
You can use
|
||||
.B POSIX
|
||||
Extended Regular Expression syntax (see
|
||||
.BR regex (7)).
|
||||
.TP
|
||||
\fBfd\fR \fIfd1\fR[\fB,\fR\fIfd2\fR...]
|
||||
Match system calls by file descriptor. Only system calls that have fd argument
|
||||
are matched (system calls from \fB%desc\fR, \fB%network\fR classes and
|
||||
mq_timedsend and mq_timedreceive system calls).
|
||||
.TP
|
||||
\fBpath\fR \fIpath\fR
|
||||
Match system calls by path. Only system calls that have fd or path argument
|
||||
are matched (system calls from \fB%desc\fR, \fB%network\fR, \fB%file\fR classes
|
||||
and mq_timedsend and mq_timedreceive system calls).
|
||||
.IP
|
||||
Special characters (spaces, parentheses, operator characters) in filter
|
||||
arguments can be escaped with \'\fB\\\fR\'.
|
||||
.TP
|
||||
\fB\-e\ trace(\fR\,\fIfilter_expression\fB)\fR
|
||||
Trace only those system calls that match the \fIfilter_expression\fR.
|
||||
.TP
|
||||
\fB\-e\ abbrev(\fR\,\fIfilter_expression\fB)\fR
|
||||
Abbreviate the output from printing each member of large structures
|
||||
for those system calls that match the \fIfilter_expression\fR.
|
||||
.TP
|
||||
\fB\-e\ verbose(\fR\,\fIfilter_expression\fB)\fR
|
||||
Dereference structures for those system calls
|
||||
that match the \fIfilter_expression\fR.
|
||||
.TP
|
||||
\fB\-e\ raw(\fR\,\fIfilter_expression\fB)\fR
|
||||
Print raw, undecoded arguments for those system calls
|
||||
that match the \fIfilter_expression\fR.
|
||||
This option has the effect of causing all arguments to be printed
|
||||
in hexadecimal. This is mostly useful if you don't trust the
|
||||
decoding or you need to know the actual numeric value of an
|
||||
argument.
|
||||
.TP
|
||||
\fB\-e\ read(\fR\,\fIfilter_expression\fB)\fR
|
||||
Perform a full hexadecimal and ASCII dump of all the data read by
|
||||
those system calls that match the \fIfilter_expression\fR.
|
||||
.TP
|
||||
\fB\-e\ write(\fR\,\fIfilter_expression\fB)\fR
|
||||
Perform a full hexadecimal and ASCII dump of all the data written by
|
||||
those system calls that match the \fIfilter_expression\fR.
|
||||
.TP
|
||||
\fB\-e\ stacktrace(\fR\,\fIfilter_expression\fB)\fR
|
||||
Print the execution stack trace of the traced processes after
|
||||
those system calls that match the \fIfilter_expression\fR.
|
||||
This action is available only if
|
||||
.B strace
|
||||
is built with libunwind.
|
||||
.TP
|
||||
\fB\-e\ inject(\fR\,\fIfilter_expression\fR[;\fBerror\fR=\,\fIerrno\/\fR|;\fBretval\fR=\,\fIvalue\/\fR][;\fBsignal\fR=\,\fIsig\/\fR][;\fBwhen\fR=\,\fIexpr\/\fR]\fB)\fR
|
||||
Perform syscall tampering for those system calls
|
||||
that match the \fIfilter_expression\fR.
|
||||
|
||||
At least one of
|
||||
.BR error ,
|
||||
.BR retval ,
|
||||
or
|
||||
.B signal
|
||||
options has to be specified.
|
||||
.B error
|
||||
and
|
||||
.B retval
|
||||
are mutually exclusive.
|
||||
|
||||
If ;\fBerror\fR=\,\fIerrno\/\fR option is specified,
|
||||
a fault is injected into a syscall invocation:
|
||||
the syscall number is replaced by -1 which corresponds to an invalid syscall,
|
||||
and the error code is specified using a symbolic
|
||||
.I errno
|
||||
value like
|
||||
.B ENOSYS
|
||||
or a numeric value within 1..4095 range.
|
||||
|
||||
If ;\fBretval\fR=\,\fIvalue\/\fR option is specified,
|
||||
success injection is performed: the syscall number is replaced by -1,
|
||||
but a bogus success value is returned to the callee.
|
||||
|
||||
If ;\fBsignal\fR=\,\fIsig\/\fR option is specified with either a symbolic value
|
||||
like
|
||||
.B SIGSEGV
|
||||
or a numeric value within 1..\fBSIGRTMAX\fR range,
|
||||
that signal is delivered on entering every syscall
|
||||
that match the \fIfilter_expression\fR.
|
||||
|
||||
If ;\fBsignal\fR=\,\fIsig\/\fR option is specified without
|
||||
;\fBerror\fR=\,\fIerrno\/\fR or ;\fBretval\fR=\,\fIvalue\/\fR options,
|
||||
then only a signal
|
||||
.I sig
|
||||
is delivered without a syscall fault injection.
|
||||
Conversely, ;\fBerror\fR=\,\fIerrno\/\fR or
|
||||
;\fBretval\fR=\,\fIvalue\/\fR option without
|
||||
;\fBsignal\fR=\,\fIsig\/\fR option injects a fault without delivering a signal.
|
||||
|
||||
If both ;\fBerror\fR=\,\fIerrno\/\fR or ;\fBretval\fR=\,\fIvalue\/\fR
|
||||
and ;\fBsignal\fR=\,\fIsig\/\fR options are specified, then both
|
||||
a fault or success is injected and a signal is delivered.
|
||||
|
||||
Unless a ;\fBwhen\fR=\,\fIexpr\fR subexpression is specified,
|
||||
an injection is being made into every invocation of each syscall
|
||||
that match the \fIfilter_expression\fR.
|
||||
|
||||
The format of the subexpression is one of the following:
|
||||
.RS
|
||||
.IP "" 2
|
||||
.I first
|
||||
.RS 4
|
||||
For every syscall from the
|
||||
.IR set ,
|
||||
perform an injection for the syscall invocation number
|
||||
.I first
|
||||
only.
|
||||
.RE
|
||||
.IP "" 2
|
||||
\fIfirst\/\fB+\fR
|
||||
.RS 4
|
||||
For every syscall from the
|
||||
.IR set ,
|
||||
perform injections for the syscall invocation number
|
||||
.I first
|
||||
and all subsequent invocations.
|
||||
.RE
|
||||
.IP "" 2
|
||||
\fIfirst\/\fB+\fIstep\fR
|
||||
.RS 4
|
||||
For every syscall from the
|
||||
.IR set ,
|
||||
perform injections for syscall invocations number
|
||||
.IR first ,
|
||||
.IR first + step ,
|
||||
.IR first + step + step ,
|
||||
and so on.
|
||||
.RE
|
||||
.RE
|
||||
.IP
|
||||
For example, to fail each third and subsequent chdir syscalls with
|
||||
.BR ENOENT ,
|
||||
use
|
||||
\fB\-e\ inject\fR\fB(\fR\,\fIsyscall chdir\/\fR;\fBerror\fR=\,\fIENOENT\/\fR;\fBwhen\fR=\,\fI3\/\fB+\fR\fB)\fR.
|
||||
|
||||
The valid range for numbers
|
||||
.I first
|
||||
and
|
||||
.I step
|
||||
is 1..65535.
|
||||
|
||||
An injection expression can contain only one
|
||||
.BR error =
|
||||
or
|
||||
.BR retval =
|
||||
specification.
|
||||
If an injection expression contains multiple
|
||||
.BR when =
|
||||
specifications, the last one takes precedence.
|
||||
|
||||
Accounting of syscalls that are subject to injection
|
||||
is done per syscall and per tracee.
|
||||
.TP
|
||||
\fB\-e\ fault(\fR\,\fIfilter_expression\fR[;\fBerror\fR=\,\fIerrno\/\fR][;\fBwhen\fR=\,\fIexpr\/\fR]\fB)\fR
|
||||
Perform syscall fault injection for those system calls
|
||||
that match the \fIfilter_expression\fR.
|
||||
|
||||
This is equivalent to more generic
|
||||
\fB\-e\ inject\fR= expression with default value of
|
||||
.I errno
|
||||
option set to
|
||||
.IR ENOSYS .
|
||||
.IP
|
||||
Multiple actions of the same type(except \fBinject\fR and \fBfault\fR actions)
|
||||
are combined into one action with \fBor\fR operator. For example,
|
||||
\fB\-e trace(syscall open) \-e trace(syscall close)\fR is equal to
|
||||
\fB\-e trace(syscall open or syscall close)\fR.
|
||||
Only one inject or fault action can be applied to one syscall. If two or more
|
||||
actions try to tamper the same syscall, last of them is applied.
|
||||
.TP
|
||||
.BI "\-P " path
|
||||
Trace only system calls accessing
|
||||
|
16
strace.c
16
strace.c
@ -57,7 +57,7 @@ extern int optind;
|
||||
extern char *optarg;
|
||||
|
||||
#ifdef USE_LIBUNWIND
|
||||
/* if this is true do the stack trace for every system call */
|
||||
/* if this is true do the initialization of stack tracing mechanism */
|
||||
bool stack_trace_enabled;
|
||||
#endif
|
||||
|
||||
@ -1570,13 +1570,10 @@ init(int argc, char *argv[])
|
||||
shared_log = stderr;
|
||||
set_sortby(DEFAULT_SORTBY);
|
||||
set_personality(DEFAULT_PERSONALITY);
|
||||
qualify("trace=all");
|
||||
qualify("abbrev=all");
|
||||
qualify("verbose=all");
|
||||
#if DEFAULT_QUAL_FLAGS != (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE)
|
||||
# error Bug in DEFAULT_QUAL_FLAGS
|
||||
#endif
|
||||
qualify("signal=all");
|
||||
filtering_parse("signal=all");
|
||||
while ((c = getopt(argc, argv, "+"
|
||||
#ifdef USE_LIBUNWIND
|
||||
"k"
|
||||
@ -1641,7 +1638,7 @@ init(int argc, char *argv[])
|
||||
show_fd_path++;
|
||||
break;
|
||||
case 'v':
|
||||
qualify("abbrev=none");
|
||||
filtering_parse("abbrev=none");
|
||||
break;
|
||||
case 'V':
|
||||
print_version();
|
||||
@ -1656,7 +1653,7 @@ init(int argc, char *argv[])
|
||||
error_opt_arg(c, optarg);
|
||||
break;
|
||||
case 'e':
|
||||
qualify(optarg);
|
||||
filtering_parse(optarg);
|
||||
break;
|
||||
case 'o':
|
||||
outfname = optarg;
|
||||
@ -1687,7 +1684,7 @@ init(int argc, char *argv[])
|
||||
break;
|
||||
#ifdef USE_LIBUNWIND
|
||||
case 'k':
|
||||
stack_trace_enabled = true;
|
||||
filtering_parse("stacktrace(syscall all)");
|
||||
break;
|
||||
#endif
|
||||
case 'E':
|
||||
@ -1704,6 +1701,7 @@ init(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
}
|
||||
filtering_parsing_finish();
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
@ -2410,6 +2408,8 @@ trace_syscall(struct tcb *tcp, unsigned int *sig)
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
if (!tcp->qual_flg)
|
||||
filter_syscall(tcp);
|
||||
res = syscall_entering_trace(tcp, sig);
|
||||
}
|
||||
syscall_entering_finish(tcp, res);
|
||||
|
26
syscall.c
26
syscall.c
@ -342,7 +342,6 @@ decode_socket_subcall(struct tcb *tcp)
|
||||
return;
|
||||
|
||||
tcp->scno = scno;
|
||||
tcp->qual_flg = qual_flags(scno);
|
||||
tcp->s_ent = &sysent[scno];
|
||||
|
||||
unsigned int i;
|
||||
@ -382,7 +381,6 @@ decode_ipc_subcall(struct tcb *tcp)
|
||||
}
|
||||
|
||||
tcp->scno = SYS_ipc_subcall + call;
|
||||
tcp->qual_flg = qual_flags(tcp->scno);
|
||||
tcp->s_ent = &sysent[tcp->scno];
|
||||
|
||||
const unsigned int n = tcp->s_ent->nargs;
|
||||
@ -399,7 +397,6 @@ decode_mips_subcall(struct tcb *tcp)
|
||||
if (!scno_is_valid(tcp->u_arg[0]))
|
||||
return;
|
||||
tcp->scno = tcp->u_arg[0];
|
||||
tcp->qual_flg = qual_flags(tcp->scno);
|
||||
tcp->s_ent = &sysent[tcp->scno];
|
||||
memmove(&tcp->u_arg[0], &tcp->u_arg[1],
|
||||
sizeof(tcp->u_arg) - sizeof(tcp->u_arg[0]));
|
||||
@ -428,7 +425,7 @@ dumpio(struct tcb *tcp)
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
if (is_number_in_set(fd, read_set)) {
|
||||
if (dump_read(tcp)) {
|
||||
switch (tcp->s_ent->sen) {
|
||||
case SEN_read:
|
||||
case SEN_pread:
|
||||
@ -451,7 +448,7 @@ dumpio(struct tcb *tcp)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (is_number_in_set(fd, write_set)) {
|
||||
if (dump_write(tcp)) {
|
||||
switch (tcp->s_ent->sen) {
|
||||
case SEN_write:
|
||||
case SEN_pwrite:
|
||||
@ -537,8 +534,6 @@ static void get_error(struct tcb *, const bool);
|
||||
static int arch_set_error(struct tcb *);
|
||||
static int arch_set_success(struct tcb *);
|
||||
|
||||
struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES];
|
||||
|
||||
static struct inject_opts *
|
||||
tcb_inject_opts(struct tcb *tcp)
|
||||
{
|
||||
@ -550,14 +545,6 @@ 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);
|
||||
|
||||
if (!opts || opts->first == 0)
|
||||
@ -696,7 +683,7 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
|
||||
}
|
||||
|
||||
#ifdef USE_LIBUNWIND
|
||||
if (stack_trace_enabled) {
|
||||
if (stacktrace(tcp)) {
|
||||
if (tcp->s_ent->sys_flags & STACKTRACE_CAPTURE_ON_ENTER)
|
||||
unwind_capture_stacktrace(tcp);
|
||||
}
|
||||
@ -741,7 +728,7 @@ syscall_exiting_decode(struct tcb *tcp, struct timeval *ptv)
|
||||
gettimeofday(ptv, NULL);
|
||||
|
||||
#ifdef USE_LIBUNWIND
|
||||
if (stack_trace_enabled) {
|
||||
if (stacktrace(tcp)) {
|
||||
if (tcp->s_ent->sys_flags & STACKTRACE_INVALIDATE_CACHE)
|
||||
unwind_cache_invalidate(tcp);
|
||||
}
|
||||
@ -961,7 +948,7 @@ syscall_exiting_trace(struct tcb *tcp, struct timeval tv, int res)
|
||||
line_ended();
|
||||
|
||||
#ifdef USE_LIBUNWIND
|
||||
if (stack_trace_enabled)
|
||||
if (stacktrace(tcp))
|
||||
unwind_print_stacktrace(tcp);
|
||||
#endif
|
||||
return 0;
|
||||
@ -1195,7 +1182,8 @@ get_scno(struct tcb *tcp)
|
||||
|
||||
if (scno_is_valid(tcp->scno)) {
|
||||
tcp->s_ent = &sysent[tcp->scno];
|
||||
tcp->qual_flg = qual_flags(tcp->scno);
|
||||
/* Clear qual_flg to distinguish valid syscall from printargs */
|
||||
tcp->qual_flg = 0;
|
||||
} else {
|
||||
struct sysent_buf *s = xcalloc(1, sizeof(*s));
|
||||
|
||||
|
2
tests/.gitignore
vendored
2
tests/.gitignore
vendored
@ -72,6 +72,8 @@ fflush
|
||||
file_handle
|
||||
file_ioctl
|
||||
filter-unavailable
|
||||
filtering_fd
|
||||
filtering_path
|
||||
finit_module
|
||||
flock
|
||||
fork-f
|
||||
|
@ -95,6 +95,8 @@ check_PROGRAMS = $(PURE_EXECUTABLES) \
|
||||
execve-v \
|
||||
execveat-v \
|
||||
filter-unavailable \
|
||||
filtering_fd \
|
||||
filtering_path \
|
||||
fork-f \
|
||||
getpid \
|
||||
getppid \
|
||||
@ -267,6 +269,13 @@ MISC_TESTS = \
|
||||
detach-sleeping.test \
|
||||
detach-stopped.test \
|
||||
filter-unavailable.test \
|
||||
filtering_action-syntax.test \
|
||||
filtering_expression-empty.test \
|
||||
filtering_expression-syntax.test \
|
||||
filtering_fd-syntax.test \
|
||||
filtering_fd.test \
|
||||
filtering_path.test \
|
||||
filtering_syscall-syntax.test \
|
||||
fflush.test \
|
||||
get_regs.test \
|
||||
interactive_block.test \
|
||||
@ -341,6 +350,7 @@ EXTRA_DIST = \
|
||||
ipc_msgbuf.expected \
|
||||
ksysent.sed \
|
||||
lstatx.c \
|
||||
mmap_name.sh \
|
||||
match.awk \
|
||||
net.expected \
|
||||
netlink_sock_diag-v.sh \
|
||||
@ -373,6 +383,7 @@ EXTRA_DIST = \
|
||||
struct_flock.c \
|
||||
sun_path.expected \
|
||||
syntax.sh \
|
||||
tampering-syntax.sh \
|
||||
trace_fstat.in \
|
||||
trace_fstatfs.in \
|
||||
trace_lstat.in \
|
||||
|
95
tests/filtering_action-syntax.test
Executable file
95
tests/filtering_action-syntax.test
Executable file
@ -0,0 +1,95 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Check common action syntax.
|
||||
#
|
||||
# Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
# 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.
|
||||
|
||||
. "${srcdir=.}/syntax.sh"
|
||||
|
||||
for arg in '' ' ' ' '\
|
||||
not \
|
||||
not_action \
|
||||
notAction \
|
||||
notaction \
|
||||
; do
|
||||
check_e "invalid filter action '$arg'" -e "$arg=open"
|
||||
done
|
||||
|
||||
for arg in '' ' ' ' '; do
|
||||
check_e "invalid filter action 'not_action'" -e "$arg"not_action=
|
||||
check_e "invalid filter action 'not_action'" -e "$arg"not_action\(\)
|
||||
done
|
||||
|
||||
for arg in not_action \
|
||||
notAction \
|
||||
notaction \
|
||||
and "&&" \
|
||||
or "||" \
|
||||
; do
|
||||
check_e "invalid filter action '$arg'" -e "$arg(syscall open)"
|
||||
done
|
||||
|
||||
check_e "invalid arguments position ';':0" -e \;
|
||||
check_e "invalid arguments position 'not(syscall open;)':16" -e "not(syscall open;)"
|
||||
check_e "invalid arguments position 'trace((syscall open;arg))':19" \
|
||||
-e "trace((syscall open;arg))"
|
||||
|
||||
check_e "missing ')' in 'not (syscall open'" -e "not (syscall open"
|
||||
check_e "missing ')' in 'not (syscall open\)'" -e "not (syscall open\)"
|
||||
check_e "missing ')' in 'trace(syscall open'" -e "trace(syscall open"
|
||||
check_e "missing ')' in 'trace(syscall open;args'" -e "trace(syscall open;args"
|
||||
check_e "missing ')' in 'trace(syscall open;args\)'" -e "trace(syscall open;args\)"
|
||||
|
||||
check_e "unexpected ')' at '! syscall open)':14" -e "! syscall open)"
|
||||
check_e "unexpected ')' at '!(syscall open))':15" -e "!(syscall open))"
|
||||
check_e "unexpected ')' at 'trace(syscall open))':19" -e "trace(syscall open))"
|
||||
|
||||
check_e "unexpected ';' at 'trace(syscall open;arg);':23" -e "trace(syscall open;arg);"
|
||||
check_e "unexpected '&' at 'trace(syscall open)&':19" -e "trace(syscall open)&"
|
||||
check_e "unexpected '&' at 'trace(syscall open) &':20" -e "trace(syscall open) &"
|
||||
|
||||
for arg in trace \
|
||||
read \
|
||||
write \
|
||||
raw \
|
||||
abbrev \
|
||||
verbose \
|
||||
; do
|
||||
cat > "$EXP" << __EOF__
|
||||
$STRACE_EXE: $arg action takes no arguments, ignored arguments 'no_args'
|
||||
$STRACE_EXE: must have PROG [ARGS] or -p PID
|
||||
Try '$STRACE_EXE -h' for more information.
|
||||
__EOF__
|
||||
check_exit_status_and_stderr -e $arg=none:no_args
|
||||
check_exit_status_and_stderr -e "$arg(syscall none;no_args)"
|
||||
done
|
||||
|
||||
cat > "$EXP" << __EOF__
|
||||
$STRACE_EXE: signal action takes no arguments, ignored arguments 'no_args'
|
||||
$STRACE_EXE: must have PROG [ARGS] or -p PID
|
||||
Try '$STRACE_EXE -h' for more information.
|
||||
__EOF__
|
||||
check_exit_status_and_stderr -e signal=none:no_args
|
47
tests/filtering_expression-empty.test
Executable file
47
tests/filtering_expression-empty.test
Executable file
@ -0,0 +1,47 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Check simple filter-based expressions.
|
||||
#
|
||||
# Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
# 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.
|
||||
|
||||
. "${srcdir=.}/syntax.sh"
|
||||
|
||||
echo "+++ exited with 0 +++" > "$EXP"
|
||||
for arg in "syscall none" \
|
||||
"not syscall all" \
|
||||
"syscall all and not syscall all" \
|
||||
"syscall all and not syscall all or syscall none" \
|
||||
"(syscall all and not syscall all) or syscall none" \
|
||||
"not syscall all and (syscall all or syscall none)" \
|
||||
"not syscall all and (syscall all or syscall chdir)" \
|
||||
"syscall none and not syscall all" \
|
||||
"syscall none or not syscall all" \
|
||||
; do
|
||||
run_strace -e "$arg" true
|
||||
match_diff "$LOG" "$EXP"
|
||||
run_strace -e "trace($arg)" true
|
||||
match_diff "$LOG" "$EXP"
|
||||
done
|
77
tests/filtering_expression-syntax.test
Executable file
77
tests/filtering_expression-syntax.test
Executable file
@ -0,0 +1,77 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Check filter-based expression syntax.
|
||||
#
|
||||
# Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
# 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.
|
||||
|
||||
. "${srcdir=.}/syntax.sh"
|
||||
|
||||
check_expression()
|
||||
{
|
||||
for action in trace \
|
||||
inject \
|
||||
fault \
|
||||
read \
|
||||
write \
|
||||
raw \
|
||||
abbrev \
|
||||
verbose \
|
||||
; do
|
||||
check_e "$1" -e "$action($2)"
|
||||
done
|
||||
}
|
||||
|
||||
check_expression "invalid operator at 'or (syscall open)':1" "or (syscall open)"
|
||||
check_expression "invalid operator at '||(syscall open)':1" "||(syscall open)"
|
||||
check_expression "invalid operator at 'and (syscall open)':2" "and (syscall open)"
|
||||
check_expression "invalid operator at '&&(syscall open)':1" "&&(syscall open)"
|
||||
check_expression "invalid operator at 'syscall open not syscall close':12" \
|
||||
"syscall open not syscall close"
|
||||
check_expression "invalid operator at 'syscall open ! syscall close':10" \
|
||||
"syscall open ! syscall close"
|
||||
|
||||
for arg in notsyscall \
|
||||
not_syscall \
|
||||
notSyscall \
|
||||
invalid \
|
||||
; do
|
||||
check_expression "invalid filter '$arg'" "$arg open"
|
||||
check_expression "invalid filter '$arg'" "syscall open and $arg open"
|
||||
check_expression "invalid filter '$arg'" "syscall open or $arg open"
|
||||
done
|
||||
check_expression "invalid filter 'not_'" "not_ syscall open"
|
||||
|
||||
for arg in "syscall " \
|
||||
"(syscall)" \
|
||||
"syscall open or" \
|
||||
"syscall open or not" \
|
||||
"syscall open or syscall" \
|
||||
; do
|
||||
check_expression "unfinished filter expression '$arg'" "$arg"
|
||||
done
|
||||
|
||||
check_expression "stack overflow (expression is too complex)" \
|
||||
"(((((((((((((((((((((((((((((((((syscall open)))))))))))))))))))))))))))))))))"
|
76
tests/filtering_fd-syntax.test
Executable file
76
tests/filtering_fd-syntax.test
Executable file
@ -0,0 +1,76 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Check fd set parsing syntax.
|
||||
#
|
||||
# Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
# 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.
|
||||
|
||||
. "${srcdir=.}/syntax.sh"
|
||||
|
||||
check_fd_new()
|
||||
{
|
||||
[ -z "$2" ] || check_e "invalid descriptor '$1'" -e "trace(fd $2)"
|
||||
[ -z "$2" ] || check_e "invalid descriptor '$1'" -e "fd $2"
|
||||
[ -z "$2" ] || check_e "invalid descriptor '$1'" -e"fd $2"
|
||||
}
|
||||
|
||||
check_fd_qualify()
|
||||
{
|
||||
check_e "invalid descriptor '$1'" -e "read=$2"
|
||||
check_e "invalid descriptor '$1'" -e "write=$2"
|
||||
}
|
||||
|
||||
for arg in '' , ,, ,,, ; do
|
||||
check_fd_new "$arg" "$arg"
|
||||
check_fd_new "!" "!$arg"
|
||||
check_fd_qualify "$arg" "$arg"
|
||||
check_fd_qualify "!$arg" "!$arg"
|
||||
done
|
||||
|
||||
for arg in -1 -42 \
|
||||
not_fd \
|
||||
2147483648 \
|
||||
4294967296 \
|
||||
; do
|
||||
check_fd_new "$arg" "$arg"
|
||||
check_fd_new "$arg" "1,$arg"
|
||||
check_fd_new "$arg" "$arg,1"
|
||||
check_fd_new "!$arg" "!$arg"
|
||||
check_fd_qualify "$arg" "$arg"
|
||||
check_fd_qualify "$arg" "1,$arg"
|
||||
check_fd_qualify "$arg" "$arg,1"
|
||||
check_fd_qualify "$arg" "!$arg"
|
||||
done
|
||||
|
||||
for arg in ! all none; do
|
||||
check_fd_qualify "$arg" "1,$arg"
|
||||
check_fd_qualify "!$arg" "1,!$arg"
|
||||
check_fd_new "$arg" "1,$arg"
|
||||
check_fd_new "!$arg" "1,!$arg"
|
||||
done
|
||||
|
||||
for arg in 1 2 all none; do
|
||||
check_fd_new "!$arg" "!$arg"
|
||||
done
|
204
tests/filtering_fd.c
Normal file
204
tests/filtering_fd.c
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Check decoding of non-standard fd filters
|
||||
*
|
||||
* Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "tests.h"
|
||||
#include <asm/unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define FD_TRACED_DEFAULT 5
|
||||
|
||||
#ifdef __NR_dup2
|
||||
void
|
||||
test_dup2(int fd_base)
|
||||
{
|
||||
int rc = syscall(__NR_dup2, fd_base, -1);
|
||||
printf("dup2(%d, -1) = %s\n", fd_base, sprintrc(rc));
|
||||
syscall(__NR_dup2, fd_base + 1, -1);
|
||||
|
||||
rc = syscall(__NR_dup2, -1, fd_base);
|
||||
printf("dup2(-1, %d) = %s\n", fd_base, sprintrc(rc));
|
||||
syscall(__NR_dup2, -1, fd_base + 1);
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_dup2(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __NR_linkat
|
||||
void
|
||||
test_linkat(int fd_base)
|
||||
{
|
||||
int rc = syscall(__NR_linkat, fd_base, "old", -1, "new", 0);
|
||||
printf("linkat(%d, \"old\", -1, \"new\", 0) = %s\n",
|
||||
fd_base, sprintrc(rc));
|
||||
syscall(__NR_linkat, fd_base + 1, "old", -1, "new", 0);
|
||||
|
||||
rc = syscall(__NR_linkat, -1, "old", fd_base, "new", 0);
|
||||
printf("linkat(-1, \"old\", %d, \"new\", 0) = %s\n",
|
||||
fd_base, sprintrc(rc));
|
||||
syscall(__NR_linkat, "old", fd_base + 1, "new", 0);
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_linkat(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __NR_symlinkat
|
||||
void
|
||||
test_symlinkat(int fd_base)
|
||||
{
|
||||
int rc = syscall(__NR_symlinkat, "new", fd_base, "old");
|
||||
printf("symlinkat(\"new\", %d, \"old\") = %s\n",
|
||||
fd_base, sprintrc(rc));
|
||||
syscall(__NR_symlinkat, "new", fd_base + 1, "old");
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_symlinkat(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __NR_epoll_ctl
|
||||
# include <sys/epoll.h>
|
||||
void
|
||||
test_epoll(int fd_base)
|
||||
{
|
||||
int rc = syscall(__NR_epoll_ctl, -1, EPOLL_CTL_ADD, fd_base, NULL);
|
||||
printf("epoll_ctl(-1, EPOLL_CTL_ADD, %d, NULL) = %s\n",
|
||||
fd_base, sprintrc(rc));
|
||||
syscall(__NR_epoll_ctl, -1, EPOLL_CTL_ADD, fd_base + 1, NULL);
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_epoll(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined HAVE_SYS_FANOTIFY_H && defined HAVE_FANOTIFY_MARK && \
|
||||
defined __NR_fanotify_mark
|
||||
#include <sys/fanotify.h>
|
||||
void
|
||||
test_fanotify_mark(int fd_base)
|
||||
{
|
||||
int rc = fanotify_mark(-1, 0, 0, fd_base, ".");
|
||||
printf("fanotify_mark(-1, 0, 0, %d, \".\") = %s\n",
|
||||
fd_base, sprintrc(rc));
|
||||
fanotify_mark(-1, 0, 0, fd_base + 1, ".");
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_fanotify_mark(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined __NR_select || defined __NR__newselect
|
||||
# include <sys/select.h>
|
||||
void
|
||||
test_select(int fd_base)
|
||||
{
|
||||
int rc;
|
||||
fd_set positive_set, negative_set;
|
||||
|
||||
FD_ZERO(&positive_set);
|
||||
FD_SET(fd_base, &positive_set);
|
||||
FD_ZERO(&negative_set);
|
||||
FD_SET(fd_base + 1, &negative_set);
|
||||
# ifndef __NR__newselect
|
||||
rc = syscall(__NR_select, fd_base + 1, &positive_set, NULL, NULL, NULL);
|
||||
printf("select(%d, [%d], NULL, NULL, NULL) = %s\n",
|
||||
fd_base + 1, fd_base, sprintrc(rc));
|
||||
syscall(__NR_select, fd_base + 2, &negative_set, NULL, NULL,NULL);
|
||||
# else
|
||||
rc = syscall(__NR__newselect, fd_base + 1, &positive_set, NULL, NULL,
|
||||
NULL);
|
||||
printf("_newselect(%d, [%d], NULL, NULL, NULL) = %s\n",
|
||||
fd_base + 1, fd_base, sprintrc(rc));
|
||||
syscall(__NR__newselect, fd_base + 2, &negative_set, NULL, NULL, NULL);
|
||||
# endif
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_select(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __NR_poll
|
||||
# include <poll.h>
|
||||
void
|
||||
test_poll(int fd_base)
|
||||
{
|
||||
struct pollfd positive_fds = {.fd = fd_base, .events = POLLIN};
|
||||
struct pollfd negative_fds = {.fd = fd_base + 1, .events = POLLIN};
|
||||
|
||||
syscall(__NR_poll, &positive_fds, 1, 1);
|
||||
printf("poll([{fd=%d, events=POLLIN}], 1, 1) = 1 "
|
||||
"([{fd=%d, revents=POLLNVAL}])\n", fd_base, fd_base);
|
||||
syscall(__NR_poll, &negative_fds, 1, 1);
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_poll(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
const char *const name = argc > 1 ? argv[1] : "mmap";
|
||||
long tmp_fd_base = argc > 2 ? strtol(argv[2], NULL, 10) :
|
||||
FD_TRACED_DEFAULT;
|
||||
int fd_base = (tmp_fd_base > 0 && tmp_fd_base < 1023) ?
|
||||
(int) tmp_fd_base : FD_TRACED_DEFAULT;
|
||||
|
||||
test_dup2(fd_base);
|
||||
test_linkat(fd_base);
|
||||
mmap(NULL, 0, PROT_NONE, MAP_FILE, fd_base, 0);
|
||||
printf("%s(NULL, 0, PROT_NONE, MAP_FILE, %d, 0) = -1 EBADF (%m)\n",
|
||||
name, fd_base);
|
||||
test_symlinkat(fd_base);
|
||||
test_epoll(fd_base);
|
||||
test_fanotify_mark(fd_base);
|
||||
test_select(fd_base);
|
||||
test_poll(fd_base);
|
||||
|
||||
puts("+++ exited with 0 +++");
|
||||
return 0;
|
||||
}
|
17
tests/filtering_fd.test
Executable file
17
tests/filtering_fd.test
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check fd filtering
|
||||
|
||||
. "${srcdir=.}/mmap_name.sh"
|
||||
|
||||
fd_base=5
|
||||
|
||||
get_mmap_name "&& fd $fd_base"
|
||||
|
||||
for arg in "trace(fd $fd_base)" \
|
||||
"trace(syscall %desc,%network and fd $fd_base)" \
|
||||
; do
|
||||
run_prog "../$NAME" "$mmap" "$fd_base" > /dev/null
|
||||
run_strace -a12 -e "$arg" $args > "$EXP"
|
||||
match_diff "$LOG" "$EXP"
|
||||
done
|
285
tests/filtering_path.c
Normal file
285
tests/filtering_path.c
Normal file
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Check decoding of non-standard path filters
|
||||
*
|
||||
* Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "tests.h"
|
||||
#include <asm/unistd.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define FD_TRACED_DEFAULT 5
|
||||
|
||||
#ifdef __NR_dup2
|
||||
void
|
||||
test_dup2(int fd_base)
|
||||
{
|
||||
int rc = syscall(__NR_dup2, fd_base, -1);
|
||||
printf("dup2(%d, -1) = %s\n", fd_base, sprintrc(rc));
|
||||
syscall(__NR_dup2, fd_base + 1, -1);
|
||||
|
||||
rc = syscall(__NR_dup2, -1, fd_base);
|
||||
printf("dup2(-1, %d) = %s\n", fd_base, sprintrc(rc));
|
||||
syscall(__NR_dup2, -1, fd_base + 1);
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_dup2(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __NR_linkat
|
||||
void
|
||||
test_linkat(const char *path, int fd_base)
|
||||
{
|
||||
int rc = syscall(__NR_linkat, fd_base, "old", -1, "new", 0);
|
||||
printf("linkat(%d, \"old\", -1, \"new\", 0) = %s\n",
|
||||
fd_base, sprintrc(rc));
|
||||
rc = syscall(__NR_linkat, -1, "old", fd_base, "new", 0);
|
||||
printf("linkat(-1, \"old\", %d, \"new\", 0) = %s\n",
|
||||
fd_base, sprintrc(rc));
|
||||
syscall(__NR_linkat, fd_base + 1, "old", fd_base + 1, "new", 0);
|
||||
|
||||
rc = syscall(__NR_linkat, -1, path, -1, "new", 0);
|
||||
printf("linkat(-1, \"%s\", -1, \"new\", 0) = %s\n", path, sprintrc(rc));
|
||||
rc = syscall(__NR_linkat, -1, "old", -1, path, 0);
|
||||
printf("linkat(-1, \"old\", -1, \"%s\", 0) = %s\n", path, sprintrc(rc));
|
||||
|
||||
syscall(__NR_linkat, -1, "old", -1, "new", 0);
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_linkat(const char *path, int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __NR_symlinkat
|
||||
void
|
||||
test_symlinkat(const char *path, int fd_base)
|
||||
{
|
||||
int rc = syscall(__NR_symlinkat, "new", fd_base, "old");
|
||||
printf("symlinkat(\"new\", %d, \"old\") = %s\n",
|
||||
fd_base, sprintrc(rc));
|
||||
syscall(__NR_symlinkat, "new", fd_base + 1, "old");
|
||||
|
||||
rc = syscall(__NR_symlinkat, "new", -1, path);
|
||||
printf("symlinkat(\"new\", -1, \"%s\") = %s\n", path, sprintrc(rc));
|
||||
syscall(__NR_symlinkat, "new", -1, "old");
|
||||
|
||||
syscall(__NR_symlinkat, path, -1, "old");
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_symlinkat(const char *path, int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __NR_epoll_ctl
|
||||
# include <sys/epoll.h>
|
||||
void
|
||||
test_epoll(int fd_base)
|
||||
{
|
||||
int rc = syscall(__NR_epoll_ctl, -1, EPOLL_CTL_ADD, fd_base, NULL);
|
||||
printf("epoll_ctl(-1, EPOLL_CTL_ADD, %d, NULL) = %s\n",
|
||||
fd_base, sprintrc(rc));
|
||||
syscall(__NR_epoll_ctl, -1, EPOLL_CTL_ADD, fd_base + 1, NULL);
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_epoll(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined HAVE_SYS_FANOTIFY_H && defined HAVE_FANOTIFY_MARK && \
|
||||
defined __NR_fanotify_mark
|
||||
# include <sys/fanotify.h>
|
||||
void
|
||||
test_fanotify_mark(const char *path, int fd_base)
|
||||
{
|
||||
int rc = fanotify_mark(-1, 0, 0, fd_base, ".");
|
||||
printf("fanotify_mark(-1, 0, 0, %d, \".\") = %s\n",
|
||||
fd_base, sprintrc(rc));
|
||||
fanotify_mark(-1, 0, 0, fd_base + 1, ".");
|
||||
|
||||
rc = fanotify_mark(-1, 0, 0, -1, path);
|
||||
printf("fanotify_mark(-1, 0, 0, FAN_NOFD, \"%s\") = %s\n",
|
||||
path, sprintrc(rc));
|
||||
fanotify_mark(-1, 0, 0, -1, "not_file");
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_fanotify_mark(const char *path, int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined __NR_select || defined __NR__newselect
|
||||
# include <sys/select.h>
|
||||
void
|
||||
test_select(int fd_base)
|
||||
{
|
||||
fd_set positive_set, negative_set;
|
||||
|
||||
FD_ZERO(&positive_set);
|
||||
FD_SET(fd_base, &positive_set);
|
||||
FD_ZERO(&negative_set);
|
||||
FD_SET(fd_base + 1, &negative_set);
|
||||
# ifndef __NR__newselect
|
||||
syscall(__NR_select, fd_base + 1, &positive_set, NULL, NULL, NULL);
|
||||
printf("select(%d, [%d], NULL, NULL, NULL) = 1 (in [%d])\n",
|
||||
fd_base + 1, fd_base, fd_base);
|
||||
syscall(__NR_select, fd_base + 2, &negative_set, NULL, NULL,NULL);
|
||||
# else
|
||||
syscall(__NR__newselect, fd_base + 1, &positive_set, NULL, NULL,
|
||||
NULL);
|
||||
printf("_newselect(%d, [%d], NULL, NULL, NULL) = 1 (in [%d])\n",
|
||||
fd_base + 1, fd_base, fd_base);
|
||||
syscall(__NR__newselect, fd_base + 2, &negative_set, NULL, NULL, NULL);
|
||||
# endif
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_select(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __NR_poll
|
||||
# include <poll.h>
|
||||
void
|
||||
test_poll(int fd_base)
|
||||
{
|
||||
struct pollfd positive_fds = {.fd = fd_base, .events = POLLIN};
|
||||
struct pollfd negative_fds = {.fd = fd_base + 1, .events = POLLIN};
|
||||
|
||||
syscall(__NR_poll, &positive_fds, 1, 1);
|
||||
printf("poll([{fd=%d, events=POLLIN}], 1, 1) = 1 "
|
||||
"([{fd=%d, revents=POLLIN}])\n", fd_base, fd_base);
|
||||
syscall(__NR_poll, &negative_fds, 1, 1);
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_poll(int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __NR_faccessat
|
||||
void
|
||||
test_faccessat(const char *path, int fd_base)
|
||||
{
|
||||
int rc = syscall(__NR_faccessat, -1, path, 0, 0);
|
||||
printf("faccessat(-1, \"%s\", F_OK) = %s\n", path, sprintrc(rc));
|
||||
syscall(__NR_faccessat, -1, "not_file", 0, 0);
|
||||
|
||||
rc = syscall(__NR_faccessat, fd_base, "not_file", 0, 0);
|
||||
printf("faccessat(%d, \"not_file\", F_OK) = %s\n", fd_base, sprintrc(rc));
|
||||
syscall(__NR_faccessat, fd_base + 1, "not_file", 0, 0);
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_faccessat(const char *path, int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __NR_link
|
||||
void
|
||||
test_link(const char *path, int fd_base)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = syscall(__NR_link, path, NULL);
|
||||
printf("link(\"%s\", NULL) = %s\n",
|
||||
path, sprintrc(rc));
|
||||
|
||||
rc = syscall(__NR_link, NULL, path);
|
||||
printf("link(NULL, \"%s\") = %s\n",
|
||||
path, sprintrc(rc));
|
||||
|
||||
syscall(__NR_link, "old", "new");
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_link(const char *path, int fd_base)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *path = "path_trace test.sample";
|
||||
|
||||
#ifdef __NR_open
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
const char *const name = argc > 1 ? argv[1] : "mmap";
|
||||
long tmp_fd_base = argc > 2 ? strtol(argv[2], NULL, 10) :
|
||||
FD_TRACED_DEFAULT;
|
||||
int fd_base = (tmp_fd_base > 0 && tmp_fd_base < 1023) ?
|
||||
(int) tmp_fd_base : FD_TRACED_DEFAULT;
|
||||
int tmp_fd;
|
||||
|
||||
assert((tmp_fd = syscall(__NR_open, path, O_RDONLY | O_CREAT, 0600)) >= 0);
|
||||
printf("open(\"%s\", O_RDONLY|O_CREAT, 0600) = %d\n", path, tmp_fd);
|
||||
assert(dup2(tmp_fd, fd_base) == fd_base);
|
||||
printf("dup2(%d, %d) = %d\n", tmp_fd, fd_base, fd_base);
|
||||
assert(close(tmp_fd) == 0);
|
||||
printf("close(%d) = 0\n", tmp_fd);
|
||||
|
||||
|
||||
test_dup2(fd_base);
|
||||
test_linkat(path, fd_base);
|
||||
test_symlinkat(path, fd_base);
|
||||
test_epoll(fd_base);
|
||||
test_fanotify_mark(path, fd_base);
|
||||
test_select(fd_base);
|
||||
test_poll(fd_base);
|
||||
mmap(NULL, 0, PROT_NONE, MAP_FILE, fd_base, 0);
|
||||
printf("%s(NULL, 0, PROT_NONE, MAP_FILE, %d, 0) = -1 "
|
||||
"EINVAL (Invalid argument)\n", name, fd_base);
|
||||
|
||||
test_faccessat(path, fd_base);
|
||||
test_link(path, fd_base);
|
||||
test_linkat(path, fd_base);
|
||||
|
||||
puts("+++ exited with 0 +++");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
SKIP_MAIN_UNDEFINED("__NR_open")
|
||||
|
||||
#endif
|
18
tests/filtering_path.test
Executable file
18
tests/filtering_path.test
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check fd filtering
|
||||
|
||||
. "${srcdir=.}/mmap_name.sh"
|
||||
|
||||
fd_base=5
|
||||
path="path_trace\ test.sample"
|
||||
|
||||
get_mmap_name "&& path $path"
|
||||
|
||||
for arg in "trace(path $path)" \
|
||||
"trace(syscall %file,%desc,%network and path $path)" \
|
||||
; do
|
||||
run_prog "../$NAME" "$mmap" "$fd_base" > /dev/null
|
||||
run_strace -a9 -e "$arg" $args > "$EXP"
|
||||
match_diff "$LOG" "$EXP"
|
||||
done
|
130
tests/filtering_syscall-syntax.test
Executable file
130
tests/filtering_syscall-syntax.test
Executable file
@ -0,0 +1,130 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Check syscall set parsing syntax.
|
||||
#
|
||||
# Copyright (c) 2016-2017 Dmitry V. Levin <ldv@altlinux.org>
|
||||
# Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
# 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.
|
||||
|
||||
. "${srcdir=.}/syntax.sh"
|
||||
|
||||
check_syscall()
|
||||
{
|
||||
[ -z "$2" ] || check_e "invalid system call '$1'" -e"$2"
|
||||
check_e "invalid system call '$1'" -e "$2"
|
||||
check_e "invalid system call '$1'" -etrace="$2"
|
||||
check_e "invalid system call '$1'" -e trace="$2"
|
||||
check_e "invalid system call '$1'" -e abbrev="$2"
|
||||
check_e "invalid system call '$1'" -e verbose="$2"
|
||||
check_e "invalid system call '$1'" -e raw="$2"
|
||||
check_e "invalid system call '$1'" -e inject="$2"
|
||||
check_e "invalid system call '$1'" -e inject="$2:when=3"
|
||||
check_e "invalid system call '$1'" -e fault="$2"
|
||||
check_e "invalid system call '$1'" -e fault="$2:when=4"
|
||||
}
|
||||
|
||||
# Check different variations of new syntax.
|
||||
check_syscall_new()
|
||||
{
|
||||
if [ -n "$2" ]; then
|
||||
check_e "invalid system call '$1'" -e "syscall $2"
|
||||
check_e "invalid system call '$1'" -e "(syscall $2)"
|
||||
check_e "invalid system call '$1'" -e "((syscall $2))"
|
||||
check_e "invalid system call '$1'" -e "((syscall none) or syscall $2)"
|
||||
check_e "invalid system call '$1'" -e "((syscall all) and syscall $2)"
|
||||
check_e "invalid system call '$1'" -e "trace(syscall $2)"
|
||||
fi
|
||||
}
|
||||
|
||||
for arg in '' , ,, ,,, ; do
|
||||
check_syscall "$arg" "$arg"
|
||||
check_syscall "!$arg" "!$arg"
|
||||
check_syscall_new "$arg" "$arg"
|
||||
done
|
||||
|
||||
for arg in : :: ::: ; do
|
||||
check_syscall "" "$arg"
|
||||
check_syscall "!" "!$arg"
|
||||
done
|
||||
|
||||
for arg in -1 -2 -3 -4 -5 \
|
||||
invalid_syscall_name \
|
||||
0x 0y \
|
||||
32767 \
|
||||
2147483647 \
|
||||
2147483648 \
|
||||
4294967295 \
|
||||
4294967296 \
|
||||
/non_syscall \
|
||||
% %not_a_class \
|
||||
; do
|
||||
check_syscall "$arg" "$arg"
|
||||
check_syscall "$arg" "!$arg"
|
||||
check_syscall_new "$arg" "$arg"
|
||||
done
|
||||
|
||||
for arg in desc \
|
||||
file \
|
||||
memory \
|
||||
process \
|
||||
network \
|
||||
; do
|
||||
check_syscall_new "$arg" "$arg"
|
||||
done
|
||||
|
||||
# invalid syscall, multiple syscalls
|
||||
for arg in %desc \
|
||||
%file \
|
||||
%memory \
|
||||
%process \
|
||||
%network \
|
||||
chdir \
|
||||
1 \
|
||||
?32767 \
|
||||
?invalid \
|
||||
?%not_a_class \
|
||||
?/non_syscall \
|
||||
; do
|
||||
check_syscall nonsense "$arg,nonsense"
|
||||
check_syscall nonsense "nonsense,$arg"
|
||||
check_syscall_new nonsense "$arg,nonsense"
|
||||
check_syscall_new nonsense "nonsense,$arg"
|
||||
done
|
||||
|
||||
for arg in \! \!open none all -1; do
|
||||
check_syscall "$arg" "1,$arg"
|
||||
check_syscall_new "$arg" "1,$arg"
|
||||
done
|
||||
|
||||
check_e_using_grep 'regcomp: \+id: [[:alpha:]].+' -e trace='/+id'
|
||||
check_e_using_grep 'regcomp: \*id: [[:alpha:]].+' -e trace='/*id'
|
||||
check_e_using_grep 'regcomp: \{id: [[:alpha:]].+' -e trace='/{id'
|
||||
check_e_using_grep 'regcomp: \(id: [[:alpha:]].+' -e trace='/(id'
|
||||
check_e_using_grep 'regcomp: \[id: [[:alpha:]].+' -e trace='/[id'
|
||||
check_e_using_grep 'regcomp: \+id: [[:alpha:]].+' -e 'trace(syscall /+id)'
|
||||
check_e_using_grep 'regcomp: \*id: [[:alpha:]].+' -e 'trace(syscall /*id)'
|
||||
check_e_using_grep 'regcomp: \{id: [[:alpha:]].+' -e 'trace(syscall /{id)'
|
||||
check_e_using_grep 'regcomp: \(id: [[:alpha:]].+' -e 'trace(syscall /\(id)'
|
||||
check_e_using_grep 'regcomp: \[id: [[:alpha:]].+' -e 'trace(syscall /[id)'
|
@ -29,25 +29,11 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
. "${srcdir=.}/init.sh"
|
||||
. "${srcdir=.}/mmap_name.sh"
|
||||
|
||||
check_prog grep
|
||||
check_prog sed
|
||||
run_prog > /dev/null
|
||||
|
||||
syscall=
|
||||
for n in mmap mmap2; do
|
||||
$STRACE -e$n -h > /dev/null && syscall=$syscall,$n
|
||||
done
|
||||
run_strace -e$syscall $args > /dev/null
|
||||
|
||||
if grep '^mmap(NULL, 0, PROT_NONE,' < "$LOG" > /dev/null; then
|
||||
mmap=mmap
|
||||
elif grep '^mmap2(NULL, 0, PROT_NONE,' < "$LOG" > /dev/null; then
|
||||
mmap=mmap2
|
||||
else
|
||||
dump_log_and_fail_with "mmap/mmap2 not found in $STRACE $args output"
|
||||
fi
|
||||
get_mmap_name ""
|
||||
|
||||
syscall=$mmap,madvise,mlockall,mprotect,mremap,msync,munmap
|
||||
|
||||
|
51
tests/mmap_name.sh
Normal file
51
tests/mmap_name.sh
Normal file
@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Define mmap testing helper function.
|
||||
#
|
||||
# Copyright (c) 2017 Nikolay Marchuk <marchuk.nikolay.a@gmail.com>
|
||||
# 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.
|
||||
|
||||
. "${srcdir=.}/init.sh"
|
||||
|
||||
get_mmap_name()
|
||||
{
|
||||
check_prog grep
|
||||
run_prog > /dev/null
|
||||
|
||||
local filter="$1"; shift
|
||||
local syscall=
|
||||
for n in mmap mmap2; do
|
||||
$STRACE -e$n -h > /dev/null && syscall=$syscall,$n
|
||||
done
|
||||
run_strace -e "trace(syscall $syscall $filter)" $args > /dev/null
|
||||
|
||||
if grep '^mmap(NULL, 0, PROT_NONE,' < "$LOG" > /dev/null; then
|
||||
mmap=mmap
|
||||
elif grep '^mmap2(NULL, 0, PROT_NONE,' < "$LOG" > /dev/null; then
|
||||
mmap=mmap2
|
||||
else
|
||||
dump_log_and_fail_with "mmap/mmap2 not found in $STRACE $args output"
|
||||
fi
|
||||
}
|
@ -37,34 +37,6 @@ check_e "Invalid process id: 'a'" -p 1,a
|
||||
check_e "Syscall 'chdir' for -b isn't supported" -b chdir
|
||||
check_e "Syscall 'chdir' for -b isn't supported" -b execve -b chdir
|
||||
|
||||
check_e "invalid system call '-1'" -e-1
|
||||
check_e "invalid system call '-2'" -e -2
|
||||
check_e "invalid system call '-3'" -etrace=-3
|
||||
check_e "invalid system call '-4'" -e trace=-4
|
||||
check_e "invalid system call '-5'" -e trace=1,-5
|
||||
check_e "invalid system call '/non_syscall'" -e trace=/non_syscall
|
||||
check_e "invalid system call '2147483647'" -e 2147483647
|
||||
check_e "invalid system call '2147483648'" -e 2147483648
|
||||
check_e "invalid system call '4294967295'" -e 4294967295
|
||||
check_e "invalid system call '4294967296'" -e 4294967296
|
||||
|
||||
check_e "invalid descriptor '-1'" -eread=-1
|
||||
check_e "invalid descriptor '-42'" -ewrite=-42
|
||||
check_e "invalid descriptor '2147483648'" -eread=2147483648
|
||||
check_e "invalid descriptor '4294967296'" -ewrite=4294967296
|
||||
check_e "invalid descriptor 'foo'" -eread=foo
|
||||
check_e "invalid descriptor ''" -ewrite=
|
||||
check_e "invalid descriptor ','" -eread=,
|
||||
check_e "invalid descriptor '!'" -ewrite='!'
|
||||
check_e "invalid descriptor '!'" -eread='0,!'
|
||||
check_e "invalid descriptor '!,'" -ewrite='!,'
|
||||
|
||||
check_e_using_grep 'regcomp: \+id: [[:alpha:]].+' -e trace='/+id'
|
||||
check_e_using_grep 'regcomp: \*id: [[:alpha:]].+' -e trace='/*id'
|
||||
check_e_using_grep 'regcomp: \{id: [[:alpha:]].+' -e trace='/{id'
|
||||
check_e_using_grep 'regcomp: \(id: [[:alpha:]].+' -e trace='/(id'
|
||||
check_e_using_grep 'regcomp: \[id: [[:alpha:]].+' -e trace='/[id'
|
||||
|
||||
check_e_using_grep 'exec: File *name too long' "$(printf '%4096s' ' ')"
|
||||
|
||||
check_h 'must have PROG [ARGS] or -p PID'
|
||||
|
@ -43,12 +43,23 @@ test_with()
|
||||
}
|
||||
|
||||
test_with -eexit,exit_group -efault=exit_group:error=ENOSYS ../answer
|
||||
test_with -e "trace(syscall exit,exit_group)" \
|
||||
-e "fault(syscall exit_group;error=ENOSYS)" ../answer
|
||||
|
||||
test_with -eexit,exit_group -efault=exit_group:error=ENOSYS \
|
||||
-efault=\!process:error=1 ../answer
|
||||
test_with -e "trace(syscall exit,exit_group)" \
|
||||
-e "fault(syscall exit_group;error=ENOSYS)" \
|
||||
-e "fault(!syscall %process;error=1)" ../answer
|
||||
|
||||
test_with -eexit,exit_group -efault=all:error=ENOSYS \
|
||||
-efault=exit:error=1:when=2+ ../answer
|
||||
test_with -e "trace(syscall exit,exit_group)" \
|
||||
-e "fault(syscall all;error=ENOSYS)" \
|
||||
-e "fault(syscall exit;error=1;when=2+)" ../answer
|
||||
|
||||
test_with -eexit,exit_group -efault=exit_group:error=ENOSYS \
|
||||
-efault=\!%desc,%file,%memory,%process,%signal,%network,%ipc:error=1 ../answer
|
||||
test_with -e "trace(syscall exit,exit_group)" \
|
||||
-e "fault(syscall exit_group;error=ENOSYS)" \
|
||||
-e "fault(!syscall %desc,%file,%memory,%process,%signal,%network,%ipc;error=1)" ../answer
|
||||
|
@ -3,6 +3,7 @@
|
||||
# Check -e fault= syntax.
|
||||
#
|
||||
# Copyright (c) 2016-2017 Dmitry V. Levin <ldv@altlinux.org>
|
||||
# Copyright (c) 2017 The strace developers.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -27,85 +28,49 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
. "${srcdir=.}/init.sh"
|
||||
. "${srcdir=.}/tampering-syntax.sh"
|
||||
|
||||
#
|
||||
# F
|
||||
# F+
|
||||
# F+S
|
||||
|
||||
fail_with()
|
||||
{
|
||||
dump_log_and_fail_with \
|
||||
"strace -e fault=$* failed to handle an argument error properly"
|
||||
}
|
||||
|
||||
for arg in '' , ,, ,,, : :: ::: \! \!, \!: \
|
||||
invalid_syscall_name \
|
||||
invalid_syscall_name:when=3 \
|
||||
-1 \!-1 \
|
||||
-1:when=4 \
|
||||
-2 \
|
||||
-2:when=5 \
|
||||
32767 \!32767 \
|
||||
32767:when=6 \
|
||||
chdir:42 \!chdir:42 \
|
||||
chdir:42:when=7 \
|
||||
chdir:invalid \
|
||||
chdir:invalid:when=8 \
|
||||
chdir:error= \
|
||||
chdir:error=:when=10 \
|
||||
chdir:error=invalid_error_name \
|
||||
chdir:error=invalid_error_name:when=11 \
|
||||
chdir:error=-1 \
|
||||
chdir:error=-1:when=12 \
|
||||
chdir:error=-2 \
|
||||
chdir:error=-2:when=13 \
|
||||
chdir:error=3+ \
|
||||
chdir:error=3+:when=14 \
|
||||
chdir:error=4096 \
|
||||
chdir:error=4096:when=15 \
|
||||
chdir:when=7:error=invalid_error_name \
|
||||
chdir:when= \
|
||||
chdir:when=:error=19 \
|
||||
chdir:when=0 \
|
||||
chdir:when=0:error=20 \
|
||||
chdir:when=-1 \
|
||||
chdir:when=-1:error=21 \
|
||||
chdir:when=-2+ \
|
||||
chdir:when=-2+:error=22 \
|
||||
chdir:when=-3+0 \
|
||||
chdir:when=-3+0:error=23 \
|
||||
chdir:when=4- \
|
||||
chdir:when=4-:error=24 \
|
||||
chdir:when=5+- \
|
||||
chdir:when=5+-:error=25 \
|
||||
chdir:when=6++ \
|
||||
chdir:when=6++:error=26 \
|
||||
chdir:when=7+0 \
|
||||
chdir:when=7+0:error=27 \
|
||||
chdir:when=8+-1 \
|
||||
chdir:when=8+-1:error=28 \
|
||||
chdir:when=9+1+ \
|
||||
chdir:when=9+1+:error=29 \
|
||||
chdir:when=65536 \
|
||||
chdir:when=65536:error=30 \
|
||||
chdir:when=1+65536 \
|
||||
chdir:when=1+65536:error=31 \
|
||||
file,nonsense \
|
||||
\!desc,nonsense \
|
||||
chdir,nonsense \
|
||||
\!chdir,nonsense \
|
||||
1,nonsense \
|
||||
\!1,nonsense \
|
||||
chdir:retval=0 \
|
||||
chdir:signal=1 \
|
||||
chdir:error=1:error=2 \
|
||||
counter=7
|
||||
for arg in 42 \
|
||||
invalid \
|
||||
error= \
|
||||
error=invalid_error_name \
|
||||
error=-1 \
|
||||
error=-2 \
|
||||
error=3+ \
|
||||
error=4096 \
|
||||
; do
|
||||
$STRACE -e fault="$arg" true 2> "$LOG" &&
|
||||
fail_with "$arg"
|
||||
LC_ALL=C grep -F 'invalid fault argument' < "$LOG" > /dev/null ||
|
||||
fail_with "$arg"
|
||||
check_tampering_syntax fault chdir "$arg"
|
||||
check_tampering_syntax fault chdir "$arg" "when=$counter"
|
||||
counter=$((counter + 1))
|
||||
done
|
||||
|
||||
for arg in when= \
|
||||
when=0 \
|
||||
when=-1 \
|
||||
when=-2+ \
|
||||
when=-3+0 \
|
||||
when=4- \
|
||||
when=5+- \
|
||||
when=6++ \
|
||||
when=7+0 \
|
||||
when=8+-1 \
|
||||
when=9+1+ \
|
||||
when=65536 \
|
||||
when=1+65536 \
|
||||
; do
|
||||
check_tampering_syntax fault chdir "$arg"
|
||||
check_tampering_syntax fault chdir "$arg" "error=$counter"
|
||||
counter=$((counter + 1))
|
||||
done
|
||||
|
||||
check_tampering_syntax fault chdir "retval=0"
|
||||
check_tampering_syntax fault chdir "signal=1"
|
||||
check_tampering_syntax fault chdir "error=1" "error=2"
|
||||
|
||||
exit 0
|
||||
|
@ -48,29 +48,46 @@ check_fault_injection()
|
||||
procs=$1; shift
|
||||
extra="$*"
|
||||
|
||||
local when=
|
||||
local when_qualify=
|
||||
local when_new=
|
||||
if [ -z "$first$step" ]; then
|
||||
first=1
|
||||
step=1
|
||||
else
|
||||
case "$step" in
|
||||
'') when=":when=$first"; step=0 ;;
|
||||
+) when=":when=$first+"; step=1 ;;
|
||||
*) when=":when=$first+$step" ;;
|
||||
'')
|
||||
when_qualify=":when=$first"
|
||||
when_new=";when=$first"
|
||||
step=0
|
||||
;;
|
||||
+)
|
||||
when_qualify=":when=$first+"
|
||||
when_new=";when=$first+"
|
||||
step=1
|
||||
;;
|
||||
*)
|
||||
when_qualify=":when=$first+$step"
|
||||
when_new=";when=$first+$step"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
local error=
|
||||
local error_qualify=
|
||||
local error_new=
|
||||
local raw=reg
|
||||
set --
|
||||
case "$err" in
|
||||
'') ;;
|
||||
[123456789]*)
|
||||
error=":error=$err"
|
||||
error_qualify=":error=$err"
|
||||
error_new=";error=$err"
|
||||
raw=raw
|
||||
set -- -e raw=all
|
||||
;;
|
||||
*) error=":error=$err" ;;
|
||||
*)
|
||||
error_qualify=":error=$err"
|
||||
error_new=";error=$err"
|
||||
;;
|
||||
esac
|
||||
|
||||
outexp="$NAME.out.exp"
|
||||
@ -79,7 +96,21 @@ check_fault_injection()
|
||||
outpid="$NAME.pid"
|
||||
|
||||
run_strace -a11 -ff -e trace=$trace \
|
||||
"$@" -e fault=$fault$when$error $extra \
|
||||
"$@" -e fault=$fault$when_qualify$error_qualify $extra \
|
||||
../$NAME $raw "$err" "$first" "$step" $N \
|
||||
"$procs" "$outexp" "$outgot" "$outout" "$outpid"
|
||||
|
||||
for i in $(seq 0 $((procs - 1)) )
|
||||
do
|
||||
pid=$(cat "$outpid.$i")
|
||||
|
||||
match_diff "$outout.$i" "$LOG.$pid"
|
||||
match_diff "$outexp.$i" "$outgot.$i"
|
||||
done
|
||||
|
||||
# New syntax
|
||||
run_strace -a11 -ff -e "trace(syscall $trace)" \
|
||||
"$@" -e "fault(syscall $fault$when_new$error_new)" $extra \
|
||||
../$NAME $raw "$err" "$first" "$step" $N \
|
||||
"$procs" "$outexp" "$outgot" "$outout" "$outpid"
|
||||
|
||||
@ -93,7 +124,7 @@ check_fault_injection()
|
||||
}
|
||||
|
||||
for err in '' ENOSYS 22 einval; do
|
||||
for fault in writev desc,51; do
|
||||
for fault in writev %desc,51; do
|
||||
check_fault_injection \
|
||||
writev $fault "$err" '' '' 1 -efault=chdir
|
||||
check_fault_injection \
|
||||
|
@ -6,3 +6,8 @@
|
||||
run_strace -a12 -echdir,exit_group -einject=chdir:error=ENOENT:signal=USR1 \
|
||||
"../$NAME"
|
||||
match_diff
|
||||
|
||||
run_strace -a12 -e "syscall chdir,exit_group" \
|
||||
-e "inject(syscall chdir;error=ENOENT;signal=USR1)" \
|
||||
"../$NAME"
|
||||
match_diff
|
||||
|
@ -14,6 +14,11 @@ check_injection()
|
||||
run_strace -a12 -e$syscall -einject="$syscall:retval=$rval" "$@" \
|
||||
../qual_inject-retval "$rval" > "$EXP"
|
||||
match_diff "$LOG" "$EXP"
|
||||
|
||||
run_strace -a12 -e "syscall $syscall" \
|
||||
-e "inject(syscall $syscall;retval=$rval)" "$@" \
|
||||
../qual_inject-retval "$rval" > "$EXP"
|
||||
match_diff "$LOG" "$EXP"
|
||||
}
|
||||
|
||||
check_injection 0
|
||||
|
@ -6,3 +6,8 @@
|
||||
run_strace -a12 -echdir,exit_group -einject=chdir:signal=USR1 \
|
||||
../$NAME
|
||||
match_diff
|
||||
|
||||
run_strace -a12 -e "syscall chdir,exit_group" \
|
||||
-e "inject(syscall chdir;signal=USR1)" \
|
||||
../$NAME
|
||||
match_diff
|
||||
|
@ -3,6 +3,7 @@
|
||||
# Check -e inject= syntax.
|
||||
#
|
||||
# Copyright (c) 2016-2017 Dmitry V. Levin <ldv@altlinux.org>
|
||||
# Copyright (c) 2017 The strace developers.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -27,94 +28,54 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
. "${srcdir=.}/init.sh"
|
||||
. "${srcdir=.}/tampering-syntax.sh"
|
||||
|
||||
#
|
||||
# F
|
||||
# F+
|
||||
# F+S
|
||||
check_tampering_syntax inject 42
|
||||
check_tampering_syntax inject chdir
|
||||
|
||||
fail_with()
|
||||
{
|
||||
dump_log_and_fail_with \
|
||||
"strace -e inject=$* failed to handle an argument error properly"
|
||||
}
|
||||
|
||||
for arg in '' , ,, ,,, : :: ::: \! \!, \!: \
|
||||
invalid_syscall_name \
|
||||
invalid_syscall_name:when=3 \
|
||||
-1 \!-1 \
|
||||
-1:when=4 \
|
||||
-2 \
|
||||
-2:when=5 \
|
||||
32767 \!32767 \
|
||||
32767:when=6 \
|
||||
42 \
|
||||
chdir \
|
||||
chdir:42 \!chdir:42 \
|
||||
chdir:42:when=7 \
|
||||
chdir:invalid \
|
||||
chdir:invalid:when=8 \
|
||||
chdir:error= \
|
||||
chdir:error=:when=10 \
|
||||
chdir:error=invalid_error_name \
|
||||
chdir:error=invalid_error_name:when=11 \
|
||||
chdir:error=-1 \
|
||||
chdir:error=-1:when=12 \
|
||||
chdir:error=-2 \
|
||||
chdir:error=-2:when=13 \
|
||||
chdir:error=3+ \
|
||||
chdir:error=3+:when=14 \
|
||||
chdir:error=4096 \
|
||||
chdir:error=4096:when=15 \
|
||||
chdir:when=7:error=invalid_error_name \
|
||||
chdir:when= \
|
||||
chdir:when=:error=19 \
|
||||
chdir:when=0 \
|
||||
chdir:when=0:error=20 \
|
||||
chdir:when=-1 \
|
||||
chdir:when=-1:error=21 \
|
||||
chdir:when=-2+ \
|
||||
chdir:when=-2+:error=22 \
|
||||
chdir:when=-3+0 \
|
||||
chdir:when=-3+0:error=23 \
|
||||
chdir:when=4- \
|
||||
chdir:when=4-:error=24 \
|
||||
chdir:when=5+- \
|
||||
chdir:when=5+-:error=25 \
|
||||
chdir:when=6++ \
|
||||
chdir:when=6++:error=26 \
|
||||
chdir:when=7+0 \
|
||||
chdir:when=7+0:error=27 \
|
||||
chdir:when=8+-1 \
|
||||
chdir:when=8+-1:error=28 \
|
||||
chdir:when=9+1+ \
|
||||
chdir:when=9+1+:error=29 \
|
||||
chdir:when=65536 \
|
||||
chdir:when=65536:error=30 \
|
||||
chdir:when=1+65536 \
|
||||
chdir:when=1+65536:error=31 \
|
||||
file,nonsense \
|
||||
\!desc,nonsense \
|
||||
chdir,nonsense \
|
||||
\!chdir,nonsense \
|
||||
1,nonsense \
|
||||
\!1,nonsense \
|
||||
chdir:retval=-1 \
|
||||
chdir:signal=0 \
|
||||
chdir:signal=129 \
|
||||
chdir:signal=1:signal=2 \
|
||||
chdir:signal=1:retval=0:signal=2 \
|
||||
chdir:retval=0:retval=1 \
|
||||
chdir:error=1:error=2 \
|
||||
chdir:retval=0:error=1 \
|
||||
chdir:error=1:retval=0 \
|
||||
chdir:retval=0:signal=1:error=1 \
|
||||
counter=7
|
||||
for arg in 42 \
|
||||
invalid \
|
||||
error= \
|
||||
error=invalid_error_name \
|
||||
error=-1 \
|
||||
error=-2 \
|
||||
error=3+ \
|
||||
error=4096 \
|
||||
; do
|
||||
$STRACE -e inject="$arg" true 2> "$LOG" &&
|
||||
fail_with "$arg"
|
||||
LC_ALL=C grep -F 'invalid inject argument' < "$LOG" > /dev/null ||
|
||||
fail_with "$arg"
|
||||
check_tampering_syntax inject chdir "$arg"
|
||||
check_tampering_syntax inject chdir "$arg" "when=$counter"
|
||||
counter=$((counter + 1))
|
||||
done
|
||||
|
||||
for arg in when= \
|
||||
when=0 \
|
||||
when=-1 \
|
||||
when=-2+ \
|
||||
when=-3+0 \
|
||||
when=4- \
|
||||
when=5+- \
|
||||
when=6++ \
|
||||
when=7+0 \
|
||||
when=8+-1 \
|
||||
when=9+1+ \
|
||||
when=65536 \
|
||||
when=1+65536 \
|
||||
; do
|
||||
check_tampering_syntax inject chdir "$arg"
|
||||
check_tampering_syntax inject chdir "$arg" "error=$counter"
|
||||
counter=$((counter + 1))
|
||||
done
|
||||
|
||||
check_tampering_syntax inject chdir "retval=-1"
|
||||
check_tampering_syntax inject chdir "signal=0"
|
||||
check_tampering_syntax inject chdir "signal=129"
|
||||
check_tampering_syntax inject chdir signal=1 signal=2
|
||||
check_tampering_syntax inject chdir signal=1 retval=0 signal=2
|
||||
check_tampering_syntax inject chdir "retval=0" "retval=1"
|
||||
check_tampering_syntax inject chdir "error=1" "error=2"
|
||||
check_tampering_syntax inject chdir "retval=0" "error=1"
|
||||
check_tampering_syntax inject chdir "error=1" "retval=0"
|
||||
check_tampering_syntax inject chdir "retval=0" "signal=1" "error=1"
|
||||
|
||||
exit 0
|
||||
|
64
tests/tampering-syntax.sh
Normal file
64
tests/tampering-syntax.sh
Normal file
@ -0,0 +1,64 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Define inject/fault syntax testing primitives.
|
||||
#
|
||||
# Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
|
||||
# Copyright (c) 2016-2017 The strace developers.
|
||||
# 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.
|
||||
|
||||
. "${srcdir=.}/syntax.sh"
|
||||
|
||||
check_tampering_syntax()
|
||||
{
|
||||
local action syscall argument_qualify argument_new
|
||||
action=$1; shift
|
||||
syscall=$1; shift
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
argument_qualify="$1"
|
||||
argument_new="$1"
|
||||
shift
|
||||
else
|
||||
check_e "invalid $action argument ''" \
|
||||
-e "$action=$syscall" true
|
||||
check_e "invalid $action argument ''" \
|
||||
-e "$action(syscall $syscall)" true
|
||||
return
|
||||
fi
|
||||
|
||||
for arg in "$@"
|
||||
do
|
||||
[ -z "$arg" ] && break
|
||||
argument_qualify="$argument_qualify:$arg"
|
||||
argument_new="$argument_new;$arg"
|
||||
shift
|
||||
done
|
||||
check_e "invalid $action argument '$argument_qualify'" \
|
||||
-e "$action=$syscall:$argument_qualify" \
|
||||
true
|
||||
check_e "invalid $action argument '$argument_new'" \
|
||||
-e "$action(syscall $syscall;$argument_new)" \
|
||||
true
|
||||
}
|
Loading…
Reference in New Issue
Block a user