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
|
dist: trusty
|
||||||
|
|
||||||
|
group: deprecated-2017Q4
|
||||||
|
|
||||||
before_install: ./travis-install.sh
|
before_install: ./travis-install.sh
|
||||||
|
|
||||||
script: ./travis-build.sh
|
script: ./travis-build.sh
|
||||||
|
@ -88,6 +88,7 @@ strace_SOURCES = \
|
|||||||
affinity.c \
|
affinity.c \
|
||||||
aio.c \
|
aio.c \
|
||||||
alpha.c \
|
alpha.c \
|
||||||
|
basic_actions.c \
|
||||||
basic_filters.c \
|
basic_filters.c \
|
||||||
bind.c \
|
bind.c \
|
||||||
bjm.c \
|
bjm.c \
|
||||||
@ -136,7 +137,11 @@ strace_SOURCES = \
|
|||||||
fetch_struct_statfs.c \
|
fetch_struct_statfs.c \
|
||||||
file_handle.c \
|
file_handle.c \
|
||||||
file_ioctl.c \
|
file_ioctl.c \
|
||||||
|
filter_action.c \
|
||||||
|
filter_expression.c \
|
||||||
|
filter_parse.c \
|
||||||
filter_qualify.c \
|
filter_qualify.c \
|
||||||
|
filter.c \
|
||||||
filter.h \
|
filter.h \
|
||||||
flock.c \
|
flock.c \
|
||||||
flock.h \
|
flock.h \
|
||||||
|
4
NEWS
4
NEWS
@ -1,6 +1,10 @@
|
|||||||
Noteworthy changes in release ?.?? (????-??-??)
|
Noteworthy changes in release ?.?? (????-??-??)
|
||||||
===============================================
|
===============================================
|
||||||
|
|
||||||
|
* Changes in command line syntax
|
||||||
|
* New syntax for -e option for filter-based expressions.
|
||||||
|
Old syntax is still supported.
|
||||||
|
|
||||||
* Improvements
|
* Improvements
|
||||||
* Implemented decoding of netlink descriptor attributes as file descriptors.
|
* Implemented decoding of netlink descriptor attributes as file descriptors.
|
||||||
* Implemented decoding of hugetlb page size selection flags.
|
* 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;
|
||||||
|
}
|
148
basic_filters.c
148
basic_filters.c
@ -29,6 +29,7 @@
|
|||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include "number_set.h"
|
#include "number_set.h"
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
|
#include "syscall.h"
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -95,7 +96,7 @@ qualify_syscall_regex(const char *s, struct number_set *set)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int
|
static unsigned int
|
||||||
lookup_class(const char *s)
|
lookup_class(const char *s, bool qualify_mode)
|
||||||
{
|
{
|
||||||
static const struct {
|
static const struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -126,6 +127,8 @@ lookup_class(const char *s)
|
|||||||
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
|
for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
|
||||||
|
if (!qualify_mode && *s != '%')
|
||||||
|
continue;
|
||||||
if (strcmp(s, syscall_class[i].name) == 0) {
|
if (strcmp(s, syscall_class[i].name) == 0) {
|
||||||
return syscall_class[i].value;
|
return syscall_class[i].value;
|
||||||
}
|
}
|
||||||
@ -135,9 +138,9 @@ lookup_class(const char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
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)
|
if (!n)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -180,7 +183,7 @@ qualify_syscall_name(const char *s, struct number_set *set)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
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;
|
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;
|
return qualify_syscall_number(token, set) || ignore_fail;
|
||||||
if (*token == '/')
|
if (*token == '/')
|
||||||
return qualify_syscall_regex(token + 1, set) || ignore_fail;
|
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)
|
|| qualify_syscall_name(token, set)
|
||||||
|| ignore_fail;
|
|| ignore_fail;
|
||||||
}
|
}
|
||||||
@ -203,7 +206,7 @@ qualify_syscall(const char *token, struct number_set *set)
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
qualify_syscall_tokens(const char *const str, struct number_set *const set,
|
qualify_syscall_tokens(const char *const str, struct number_set *const set,
|
||||||
const char *const name)
|
bool qualify_mode)
|
||||||
{
|
{
|
||||||
/* Clear all sets. */
|
/* Clear all sets. */
|
||||||
clear_number_set_array(set, SUPPORTED_PERSONALITIES);
|
clear_number_set_array(set, SUPPORTED_PERSONALITIES);
|
||||||
@ -213,11 +216,16 @@ qualify_syscall_tokens(const char *const str, struct number_set *const set,
|
|||||||
* of the remaining specification.
|
* of the remaining specification.
|
||||||
*/
|
*/
|
||||||
const char *s = str;
|
const char *s = str;
|
||||||
handle_inversion:
|
if (qualify_mode) {
|
||||||
|
/*
|
||||||
|
* Each leading ! character means inversion
|
||||||
|
* of the remaining specification.
|
||||||
|
*/
|
||||||
while (*s == '!') {
|
while (*s == '!') {
|
||||||
invert_number_set_array(set, SUPPORTED_PERSONALITIES);
|
invert_number_set_array(set, SUPPORTED_PERSONALITIES);
|
||||||
++s;
|
++s;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(s, "none") == 0) {
|
if (strcmp(s, "none") == 0) {
|
||||||
/*
|
/*
|
||||||
@ -227,8 +235,8 @@ handle_inversion:
|
|||||||
*/
|
*/
|
||||||
return;
|
return;
|
||||||
} else if (strcmp(s, "all") == 0) {
|
} else if (strcmp(s, "all") == 0) {
|
||||||
s = "!none";
|
invert_number_set_array(set, SUPPORTED_PERSONALITIES);
|
||||||
goto handle_inversion;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -245,25 +253,52 @@ handle_inversion:
|
|||||||
|
|
||||||
for (token = strtok_r(copy, ",", &saveptr); token;
|
for (token = strtok_r(copy, ",", &saveptr); token;
|
||||||
token = strtok_r(NULL, ",", &saveptr)) {
|
token = strtok_r(NULL, ",", &saveptr)) {
|
||||||
done = qualify_syscall(token, set);
|
done = qualify_syscall(token, set, qualify_mode);
|
||||||
if (!done) {
|
if (!done) {
|
||||||
error_msg_and_die("invalid %s '%s'", name, token);
|
error_msg_and_die("invalid system call '%s'", token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(copy);
|
free(copy);
|
||||||
|
|
||||||
if (!done) {
|
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.
|
* Add numbers to SET according to STR specification.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
qualify_tokens(const char *const str, struct number_set *const set,
|
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 the set. */
|
||||||
clear_number_set_array(set, 1);
|
clear_number_set_array(set, 1);
|
||||||
@ -273,11 +308,16 @@ qualify_tokens(const char *const str, struct number_set *const set,
|
|||||||
* of the remaining specification.
|
* of the remaining specification.
|
||||||
*/
|
*/
|
||||||
const char *s = str;
|
const char *s = str;
|
||||||
handle_inversion:
|
if (qualify_mode) {
|
||||||
|
/*
|
||||||
|
* Each leading ! character means inversion
|
||||||
|
* of the remaining specification.
|
||||||
|
*/
|
||||||
while (*s == '!') {
|
while (*s == '!') {
|
||||||
invert_number_set_array(set, 1);
|
invert_number_set_array(set, 1);
|
||||||
++s;
|
++s;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(s, "none") == 0) {
|
if (strcmp(s, "none") == 0) {
|
||||||
/*
|
/*
|
||||||
@ -287,8 +327,8 @@ handle_inversion:
|
|||||||
*/
|
*/
|
||||||
return;
|
return;
|
||||||
} else if (strcmp(s, "all") == 0) {
|
} else if (strcmp(s, "all") == 0) {
|
||||||
s = "!none";
|
invert_number_set_array(set, 1);
|
||||||
goto handle_inversion;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -319,3 +359,79 @@ handle_inversion:
|
|||||||
error_msg_and_die("invalid %s '%s'", name, str);
|
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_VERBOSE 0x004 /* decode the structures of this syscall */
|
||||||
#define QUAL_RAW 0x008 /* print all args in hex for 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_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)
|
#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 traced(tcp) ((tcp)->qual_flg & QUAL_TRACE)
|
||||||
#define verbose(tcp) ((tcp)->qual_flg & QUAL_VERBOSE)
|
#define verbose(tcp) ((tcp)->qual_flg & QUAL_VERBOSE)
|
||||||
#define abbrev(tcp) ((tcp)->qual_flg & QUAL_ABBREV)
|
#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 raw(tcp) ((tcp)->qual_flg & QUAL_RAW)
|
||||||
#define inject(tcp) ((tcp)->qual_flg & QUAL_INJECT)
|
#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 filtered(tcp) ((tcp)->flags & TCB_FILTERED)
|
||||||
#define hide_log(tcp) ((tcp)->flags & TCB_HIDE_LOG)
|
#define hide_log(tcp) ((tcp)->flags & TCB_HIDE_LOG)
|
||||||
|
|
||||||
@ -371,7 +381,7 @@ extern struct path_set {
|
|||||||
extern unsigned xflag;
|
extern unsigned xflag;
|
||||||
extern unsigned followfork;
|
extern unsigned followfork;
|
||||||
#ifdef USE_LIBUNWIND
|
#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;
|
extern bool stack_trace_enabled;
|
||||||
#endif
|
#endif
|
||||||
extern unsigned ptrace_setoptions;
|
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 print_ifindex(unsigned int);
|
||||||
|
|
||||||
extern void qualify(const char *);
|
extern void filtering_parse(const char *);
|
||||||
extern unsigned int qual_flags(const unsigned int);
|
extern void filtering_parsing_finish(void);
|
||||||
|
extern void filter_syscall(struct tcb *);
|
||||||
|
|
||||||
#define DECL_IOCTL(name) \
|
#define DECL_IOCTL(name) \
|
||||||
extern int \
|
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;
|
||||||
|
}
|
84
filter.h
84
filter.h
@ -30,11 +30,93 @@
|
|||||||
#define STRACE_FILTER_H
|
#define STRACE_FILTER_H
|
||||||
|
|
||||||
struct number_set;
|
struct number_set;
|
||||||
|
|
||||||
|
struct filter;
|
||||||
|
|
||||||
|
struct filter_action;
|
||||||
|
|
||||||
|
struct bool_expression;
|
||||||
|
|
||||||
typedef int (*string_to_uint_func)(const char *);
|
typedef int (*string_to_uint_func)(const char *);
|
||||||
|
|
||||||
void qualify_tokens(const char *str, struct number_set *set,
|
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,
|
void qualify_syscall_tokens(const char *str, struct number_set *set,
|
||||||
|
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);
|
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 */
|
#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);
|
||||||
|
}
|
301
filter_qualify.c
301
filter_qualify.c
@ -31,16 +31,8 @@
|
|||||||
#include "number_set.h"
|
#include "number_set.h"
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
|
|
||||||
struct number_set *read_set;
|
|
||||||
struct number_set *write_set;
|
|
||||||
struct number_set *signal_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
|
static int
|
||||||
sigstr_to_uint(const char *s)
|
sigstr_to_uint(const char *s)
|
||||||
{
|
{
|
||||||
@ -154,166 +146,170 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
void
|
||||||
parse_inject_expression(const char *const s, char **buf,
|
parse_inject_common_args(char *str, struct inject_opts *const opts,
|
||||||
struct inject_opts *const fopts,
|
const bool fault_tokens_only, bool qualify_mode)
|
||||||
const bool fault_tokens_only)
|
|
||||||
{
|
{
|
||||||
char *saveptr = NULL;
|
char *saveptr = NULL;
|
||||||
char *name = NULL;
|
|
||||||
char *token;
|
char *token;
|
||||||
|
const char *delim = qualify_mode ? ":" : ";";
|
||||||
|
|
||||||
*buf = xstrdup(s);
|
*opts = (struct inject_opts) {
|
||||||
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 = {
|
|
||||||
.first = 1,
|
.first = 1,
|
||||||
.step = 1
|
.step = 1
|
||||||
};
|
};
|
||||||
char *buf = NULL;
|
|
||||||
char *name = parse_inject_expression(str, &buf, &opts, fault_tokens_only);
|
for (token = strtok_r(str, delim, &saveptr); token;
|
||||||
if (!name) {
|
token = strtok_r(NULL, delim, &saveptr)) {
|
||||||
error_msg_and_die("invalid %s '%s'", description, str);
|
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 neither of retval, error, or signal is specified, then ... */
|
||||||
if (!opts.data.flags) {
|
if (!opts->data.flags) {
|
||||||
if (fault_tokens_only) {
|
if (fault_tokens_only) {
|
||||||
/* in fault= syntax the default error code is ENOSYS. */
|
/* in fault= syntax the default error code is ENOSYS. */
|
||||||
opts.data.rval = -ENOSYS;
|
opts->data.rval = -ENOSYS;
|
||||||
opts.data.flags |= INJECT_F_RETVAL;
|
opts->data.flags |= INJECT_F_RETVAL;
|
||||||
} else {
|
} else {
|
||||||
/* in inject= syntax this is not allowed. */
|
/* in inject= syntax this is not allowed. */
|
||||||
error_msg_and_die("invalid %s '%s'", description, str);
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct number_set *tmp_set =
|
static void
|
||||||
alloc_number_set_array(SUPPORTED_PERSONALITIES);
|
qualify_read(const char *const main_part, const char *const args)
|
||||||
qualify_syscall_tokens(name, tmp_set, description);
|
{
|
||||||
|
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);
|
free(buf);
|
||||||
|
|
||||||
/*
|
set_filter_action_priv_data(action, opts);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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
|
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 {
|
static const struct qual_options {
|
||||||
const char *name;
|
const char *name;
|
||||||
void (*qualify)(const char *);
|
void (*qualify)(const char *, const char *);
|
||||||
} qual_options[] = {
|
} qual_options[] = {
|
||||||
{ "trace", qualify_trace },
|
{ "trace", qualify_trace },
|
||||||
{ "t", qualify_trace },
|
{ "t", qualify_trace },
|
||||||
@ -337,37 +333,20 @@ static const struct qual_options {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void
|
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;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(qual_options); ++i) {
|
for (i = 0; i < ARRAY_SIZE(qual_options); ++i) {
|
||||||
const char *name = qual_options[i].name;
|
if (!strcmp(action_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];
|
opt = &qual_options[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
opt->qualify(str);
|
if (!opt)
|
||||||
}
|
error_msg_and_die("invalid filter action '%s'", action_name);
|
||||||
|
opt->qualify(main_part ? main_part : "", args);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,6 @@ alloc_number_set_array(unsigned int nmemb) ATTRIBUTE_MALLOC;
|
|||||||
extern void
|
extern void
|
||||||
free_number_set_array(struct number_set *, unsigned int nmemb);
|
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;
|
extern struct number_set *signal_set;
|
||||||
|
|
||||||
#endif /* !STRACE_NUMBER_SET_H */
|
#endif /* !STRACE_NUMBER_SET_H */
|
||||||
|
195
pathtrace.c
195
pathtrace.c
@ -69,6 +69,8 @@ upathmatch(struct tcb *const tcp, const kernel_ulong_t upath,
|
|||||||
static bool
|
static bool
|
||||||
fdmatch(struct tcb *tcp, int fd, struct path_set *set)
|
fdmatch(struct tcb *tcp, int fd, struct path_set *set)
|
||||||
{
|
{
|
||||||
|
if (fd < 0)
|
||||||
|
return false;
|
||||||
char path[PATH_MAX + 1];
|
char path[PATH_MAX + 1];
|
||||||
int n = getfdpath(tcp, fd, path, sizeof(path));
|
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,
|
xgrowarray(set->paths_selected, &set->size,
|
||||||
sizeof(set->paths_selected[0]));
|
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);
|
storepath(rpath, set);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
typedef bool (*match_fd_func)(struct tcb *, int, void *);
|
||||||
* Return true if syscall accesses a selected path
|
|
||||||
* (or if no paths have been specified for tracing).
|
static
|
||||||
*/
|
bool fdmatch_fd_func(struct tcb *tcp, int fd, void *data)
|
||||||
bool
|
|
||||||
pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
|
|
||||||
/*
|
|
||||||
* Check for special cases where we need to do something
|
|
||||||
* other than test arg[0].
|
|
||||||
*/
|
|
||||||
|
|
||||||
switch (s->sen) {
|
switch (s->sen) {
|
||||||
case SEN_dup2:
|
case SEN_dup2:
|
||||||
case SEN_dup3:
|
case SEN_dup3:
|
||||||
@ -169,49 +169,17 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
|||||||
case SEN_sendfile64:
|
case SEN_sendfile64:
|
||||||
case SEN_tee:
|
case SEN_tee:
|
||||||
/* fd, fd */
|
/* fd, fd */
|
||||||
return fdmatch(tcp, tcp->u_arg[0], set) ||
|
return func(tcp, tcp->u_arg[0], data) ||
|
||||||
fdmatch(tcp, tcp->u_arg[1], set);
|
func(tcp, tcp->u_arg[1], data);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
case SEN_copy_file_range:
|
||||||
case SEN_linkat:
|
case SEN_linkat:
|
||||||
case SEN_renameat2:
|
case SEN_renameat2:
|
||||||
case SEN_renameat:
|
case SEN_renameat:
|
||||||
/* fd, path, fd, path */
|
case SEN_splice:
|
||||||
return fdmatch(tcp, tcp->u_arg[0], set) ||
|
/* fd, x, fd */
|
||||||
fdmatch(tcp, tcp->u_arg[2], set) ||
|
return func(tcp, tcp->u_arg[0], data) ||
|
||||||
upathmatch(tcp, tcp->u_arg[1], set) ||
|
func(tcp, tcp->u_arg[2], data);
|
||||||
upathmatch(tcp, tcp->u_arg[3], set);
|
|
||||||
|
|
||||||
case SEN_old_mmap:
|
case SEN_old_mmap:
|
||||||
#if defined(S390)
|
#if defined(S390)
|
||||||
@ -222,22 +190,15 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
|||||||
case SEN_mmap_pgoff:
|
case SEN_mmap_pgoff:
|
||||||
case SEN_ARCH_mmap:
|
case SEN_ARCH_mmap:
|
||||||
/* x, x, x, x, fd */
|
/* x, x, x, x, fd */
|
||||||
return fdmatch(tcp, tcp->u_arg[4], set);
|
return func(tcp, tcp->u_arg[4], data);
|
||||||
|
|
||||||
case SEN_symlinkat:
|
case SEN_symlinkat:
|
||||||
/* x, fd, path */
|
/* x, fd, x */
|
||||||
return fdmatch(tcp, tcp->u_arg[1], set) ||
|
return func(tcp, tcp->u_arg[1], data);
|
||||||
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);
|
|
||||||
|
|
||||||
case SEN_epoll_ctl:
|
case SEN_epoll_ctl:
|
||||||
/* x, x, fd, x */
|
/* x, x, fd, x */
|
||||||
return fdmatch(tcp, tcp->u_arg[2], set);
|
return func(tcp, tcp->u_arg[2], data);
|
||||||
|
|
||||||
|
|
||||||
case SEN_fanotify_mark:
|
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 */
|
/* x, x, mask (64 bit), fd, path */
|
||||||
unsigned long long mask = 0;
|
unsigned long long mask = 0;
|
||||||
int argn = getllval(tcp, &mask, 2);
|
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_oldselect:
|
||||||
case SEN_pselect6:
|
case SEN_pselect6:
|
||||||
case SEN_select:
|
case SEN_select:
|
||||||
@ -302,7 +264,7 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
|||||||
j = next_set_bit(fds, j, nfds);
|
j = next_set_bit(fds, j, nfds);
|
||||||
if (j < 0)
|
if (j < 0)
|
||||||
break;
|
break;
|
||||||
if (fdmatch(tcp, j, set)) {
|
if (func(tcp, j, data)) {
|
||||||
free(fds);
|
free(fds);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -332,14 +294,19 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
|||||||
for (cur = start; cur < end; cur += sizeof(fds)) {
|
for (cur = start; cur < end; cur += sizeof(fds)) {
|
||||||
if (umove(tcp, cur, &fds))
|
if (umove(tcp, cur, &fds))
|
||||||
break;
|
break;
|
||||||
if (fdmatch(tcp, fds.fd, set))
|
if (func(tcp, fds.fd, data))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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_bpf:
|
||||||
|
case SEN_creat:
|
||||||
case SEN_epoll_create:
|
case SEN_epoll_create:
|
||||||
case SEN_epoll_create1:
|
case SEN_epoll_create1:
|
||||||
case SEN_eventfd2:
|
case SEN_eventfd2:
|
||||||
@ -348,6 +315,7 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
|||||||
case SEN_inotify_init:
|
case SEN_inotify_init:
|
||||||
case SEN_inotify_init1:
|
case SEN_inotify_init1:
|
||||||
case SEN_memfd_create:
|
case SEN_memfd_create:
|
||||||
|
case SEN_open:
|
||||||
case SEN_perf_event_open:
|
case SEN_perf_event_open:
|
||||||
case SEN_pipe:
|
case SEN_pipe:
|
||||||
case SEN_pipe2:
|
case SEN_pipe2:
|
||||||
@ -355,13 +323,85 @@ pathtrace_match_set(struct tcb *tcp, struct path_set *set)
|
|||||||
case SEN_socket:
|
case SEN_socket:
|
||||||
case SEN_socketpair:
|
case SEN_socketpair:
|
||||||
case SEN_timerfd_create:
|
case SEN_timerfd_create:
|
||||||
case SEN_timerfd_gettime:
|
|
||||||
case SEN_timerfd_settime:
|
|
||||||
case SEN_userfaultfd:
|
case SEN_userfaultfd:
|
||||||
/*
|
return false;
|
||||||
* 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 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;
|
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
|
* Our fallback position for calls that haven't already
|
||||||
* been handled is to just check arg[0].
|
* been handled is to just check arg[0].
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (s->sys_flags & TRACE_FILE)
|
|
||||||
return upathmatch(tcp, tcp->u_arg[0], set);
|
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;
|
|
||||||
}
|
}
|
||||||
|
270
strace.1.in
270
strace.1.in
@ -354,9 +354,14 @@ and
|
|||||||
Summarise the time difference between the beginning and end of
|
Summarise the time difference between the beginning and end of
|
||||||
each system call. The default is to summarise the system time.
|
each system call. The default is to summarise the system time.
|
||||||
.SS Filtering
|
.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
|
.TP 12
|
||||||
.BI "\-e " expr
|
.BI "\-e " expr
|
||||||
A qualifying expression which modifies which events to trace
|
Set-based expression modifies which events to trace
|
||||||
or how to trace them. The format of the expression is:
|
or how to trace them. The format of the expression is:
|
||||||
.RS 15
|
.RS 15
|
||||||
.IP
|
.IP
|
||||||
@ -677,6 +682,269 @@ This is equivalent to more generic
|
|||||||
option set to
|
option set to
|
||||||
.IR ENOSYS .
|
.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
|
.TP
|
||||||
.BI "\-P " path
|
.BI "\-P " path
|
||||||
Trace only system calls accessing
|
Trace only system calls accessing
|
||||||
|
16
strace.c
16
strace.c
@ -57,7 +57,7 @@ extern int optind;
|
|||||||
extern char *optarg;
|
extern char *optarg;
|
||||||
|
|
||||||
#ifdef USE_LIBUNWIND
|
#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;
|
bool stack_trace_enabled;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1570,13 +1570,10 @@ init(int argc, char *argv[])
|
|||||||
shared_log = stderr;
|
shared_log = stderr;
|
||||||
set_sortby(DEFAULT_SORTBY);
|
set_sortby(DEFAULT_SORTBY);
|
||||||
set_personality(DEFAULT_PERSONALITY);
|
set_personality(DEFAULT_PERSONALITY);
|
||||||
qualify("trace=all");
|
|
||||||
qualify("abbrev=all");
|
|
||||||
qualify("verbose=all");
|
|
||||||
#if DEFAULT_QUAL_FLAGS != (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE)
|
#if DEFAULT_QUAL_FLAGS != (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE)
|
||||||
# error Bug in DEFAULT_QUAL_FLAGS
|
# error Bug in DEFAULT_QUAL_FLAGS
|
||||||
#endif
|
#endif
|
||||||
qualify("signal=all");
|
filtering_parse("signal=all");
|
||||||
while ((c = getopt(argc, argv, "+"
|
while ((c = getopt(argc, argv, "+"
|
||||||
#ifdef USE_LIBUNWIND
|
#ifdef USE_LIBUNWIND
|
||||||
"k"
|
"k"
|
||||||
@ -1641,7 +1638,7 @@ init(int argc, char *argv[])
|
|||||||
show_fd_path++;
|
show_fd_path++;
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
qualify("abbrev=none");
|
filtering_parse("abbrev=none");
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
print_version();
|
print_version();
|
||||||
@ -1656,7 +1653,7 @@ init(int argc, char *argv[])
|
|||||||
error_opt_arg(c, optarg);
|
error_opt_arg(c, optarg);
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
qualify(optarg);
|
filtering_parse(optarg);
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
outfname = optarg;
|
outfname = optarg;
|
||||||
@ -1687,7 +1684,7 @@ init(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
#ifdef USE_LIBUNWIND
|
#ifdef USE_LIBUNWIND
|
||||||
case 'k':
|
case 'k':
|
||||||
stack_trace_enabled = true;
|
filtering_parse("stacktrace(syscall all)");
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case 'E':
|
case 'E':
|
||||||
@ -1704,6 +1701,7 @@ init(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
filtering_parsing_finish();
|
||||||
|
|
||||||
argv += optind;
|
argv += optind;
|
||||||
argc -= optind;
|
argc -= optind;
|
||||||
@ -2410,6 +2408,8 @@ trace_syscall(struct tcb *tcp, unsigned int *sig)
|
|||||||
case 0:
|
case 0:
|
||||||
return 0;
|
return 0;
|
||||||
case 1:
|
case 1:
|
||||||
|
if (!tcp->qual_flg)
|
||||||
|
filter_syscall(tcp);
|
||||||
res = syscall_entering_trace(tcp, sig);
|
res = syscall_entering_trace(tcp, sig);
|
||||||
}
|
}
|
||||||
syscall_entering_finish(tcp, res);
|
syscall_entering_finish(tcp, res);
|
||||||
|
26
syscall.c
26
syscall.c
@ -342,7 +342,6 @@ decode_socket_subcall(struct tcb *tcp)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
tcp->scno = scno;
|
tcp->scno = scno;
|
||||||
tcp->qual_flg = qual_flags(scno);
|
|
||||||
tcp->s_ent = &sysent[scno];
|
tcp->s_ent = &sysent[scno];
|
||||||
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@ -382,7 +381,6 @@ decode_ipc_subcall(struct tcb *tcp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tcp->scno = SYS_ipc_subcall + call;
|
tcp->scno = SYS_ipc_subcall + call;
|
||||||
tcp->qual_flg = qual_flags(tcp->scno);
|
|
||||||
tcp->s_ent = &sysent[tcp->scno];
|
tcp->s_ent = &sysent[tcp->scno];
|
||||||
|
|
||||||
const unsigned int n = tcp->s_ent->nargs;
|
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]))
|
if (!scno_is_valid(tcp->u_arg[0]))
|
||||||
return;
|
return;
|
||||||
tcp->scno = tcp->u_arg[0];
|
tcp->scno = tcp->u_arg[0];
|
||||||
tcp->qual_flg = qual_flags(tcp->scno);
|
|
||||||
tcp->s_ent = &sysent[tcp->scno];
|
tcp->s_ent = &sysent[tcp->scno];
|
||||||
memmove(&tcp->u_arg[0], &tcp->u_arg[1],
|
memmove(&tcp->u_arg[0], &tcp->u_arg[1],
|
||||||
sizeof(tcp->u_arg) - sizeof(tcp->u_arg[0]));
|
sizeof(tcp->u_arg) - sizeof(tcp->u_arg[0]));
|
||||||
@ -428,7 +425,7 @@ dumpio(struct tcb *tcp)
|
|||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (is_number_in_set(fd, read_set)) {
|
if (dump_read(tcp)) {
|
||||||
switch (tcp->s_ent->sen) {
|
switch (tcp->s_ent->sen) {
|
||||||
case SEN_read:
|
case SEN_read:
|
||||||
case SEN_pread:
|
case SEN_pread:
|
||||||
@ -451,7 +448,7 @@ dumpio(struct tcb *tcp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_number_in_set(fd, write_set)) {
|
if (dump_write(tcp)) {
|
||||||
switch (tcp->s_ent->sen) {
|
switch (tcp->s_ent->sen) {
|
||||||
case SEN_write:
|
case SEN_write:
|
||||||
case SEN_pwrite:
|
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_error(struct tcb *);
|
||||||
static int arch_set_success(struct tcb *);
|
static int arch_set_success(struct tcb *);
|
||||||
|
|
||||||
struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES];
|
|
||||||
|
|
||||||
static struct inject_opts *
|
static struct inject_opts *
|
||||||
tcb_inject_opts(struct tcb *tcp)
|
tcb_inject_opts(struct tcb *tcp)
|
||||||
{
|
{
|
||||||
@ -550,14 +545,6 @@ tcb_inject_opts(struct tcb *tcp)
|
|||||||
static long
|
static long
|
||||||
tamper_with_syscall_entering(struct tcb *tcp, unsigned int *signo)
|
tamper_with_syscall_entering(struct tcb *tcp, unsigned int *signo)
|
||||||
{
|
{
|
||||||
if (!tcp->inject_vec[current_personality]) {
|
|
||||||
tcp->inject_vec[current_personality] =
|
|
||||||
xcalloc(nsyscalls, sizeof(**inject_vec));
|
|
||||||
memcpy(tcp->inject_vec[current_personality],
|
|
||||||
inject_vec[current_personality],
|
|
||||||
nsyscalls * sizeof(**inject_vec));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct inject_opts *opts = tcb_inject_opts(tcp);
|
struct inject_opts *opts = tcb_inject_opts(tcp);
|
||||||
|
|
||||||
if (!opts || opts->first == 0)
|
if (!opts || opts->first == 0)
|
||||||
@ -696,7 +683,7 @@ syscall_entering_trace(struct tcb *tcp, unsigned int *sig)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_LIBUNWIND
|
#ifdef USE_LIBUNWIND
|
||||||
if (stack_trace_enabled) {
|
if (stacktrace(tcp)) {
|
||||||
if (tcp->s_ent->sys_flags & STACKTRACE_CAPTURE_ON_ENTER)
|
if (tcp->s_ent->sys_flags & STACKTRACE_CAPTURE_ON_ENTER)
|
||||||
unwind_capture_stacktrace(tcp);
|
unwind_capture_stacktrace(tcp);
|
||||||
}
|
}
|
||||||
@ -741,7 +728,7 @@ syscall_exiting_decode(struct tcb *tcp, struct timeval *ptv)
|
|||||||
gettimeofday(ptv, NULL);
|
gettimeofday(ptv, NULL);
|
||||||
|
|
||||||
#ifdef USE_LIBUNWIND
|
#ifdef USE_LIBUNWIND
|
||||||
if (stack_trace_enabled) {
|
if (stacktrace(tcp)) {
|
||||||
if (tcp->s_ent->sys_flags & STACKTRACE_INVALIDATE_CACHE)
|
if (tcp->s_ent->sys_flags & STACKTRACE_INVALIDATE_CACHE)
|
||||||
unwind_cache_invalidate(tcp);
|
unwind_cache_invalidate(tcp);
|
||||||
}
|
}
|
||||||
@ -961,7 +948,7 @@ syscall_exiting_trace(struct tcb *tcp, struct timeval tv, int res)
|
|||||||
line_ended();
|
line_ended();
|
||||||
|
|
||||||
#ifdef USE_LIBUNWIND
|
#ifdef USE_LIBUNWIND
|
||||||
if (stack_trace_enabled)
|
if (stacktrace(tcp))
|
||||||
unwind_print_stacktrace(tcp);
|
unwind_print_stacktrace(tcp);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
@ -1195,7 +1182,8 @@ get_scno(struct tcb *tcp)
|
|||||||
|
|
||||||
if (scno_is_valid(tcp->scno)) {
|
if (scno_is_valid(tcp->scno)) {
|
||||||
tcp->s_ent = &sysent[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 {
|
} else {
|
||||||
struct sysent_buf *s = xcalloc(1, sizeof(*s));
|
struct sysent_buf *s = xcalloc(1, sizeof(*s));
|
||||||
|
|
||||||
|
2
tests/.gitignore
vendored
2
tests/.gitignore
vendored
@ -72,6 +72,8 @@ fflush
|
|||||||
file_handle
|
file_handle
|
||||||
file_ioctl
|
file_ioctl
|
||||||
filter-unavailable
|
filter-unavailable
|
||||||
|
filtering_fd
|
||||||
|
filtering_path
|
||||||
finit_module
|
finit_module
|
||||||
flock
|
flock
|
||||||
fork-f
|
fork-f
|
||||||
|
@ -95,6 +95,8 @@ check_PROGRAMS = $(PURE_EXECUTABLES) \
|
|||||||
execve-v \
|
execve-v \
|
||||||
execveat-v \
|
execveat-v \
|
||||||
filter-unavailable \
|
filter-unavailable \
|
||||||
|
filtering_fd \
|
||||||
|
filtering_path \
|
||||||
fork-f \
|
fork-f \
|
||||||
getpid \
|
getpid \
|
||||||
getppid \
|
getppid \
|
||||||
@ -267,6 +269,13 @@ MISC_TESTS = \
|
|||||||
detach-sleeping.test \
|
detach-sleeping.test \
|
||||||
detach-stopped.test \
|
detach-stopped.test \
|
||||||
filter-unavailable.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 \
|
fflush.test \
|
||||||
get_regs.test \
|
get_regs.test \
|
||||||
interactive_block.test \
|
interactive_block.test \
|
||||||
@ -341,6 +350,7 @@ EXTRA_DIST = \
|
|||||||
ipc_msgbuf.expected \
|
ipc_msgbuf.expected \
|
||||||
ksysent.sed \
|
ksysent.sed \
|
||||||
lstatx.c \
|
lstatx.c \
|
||||||
|
mmap_name.sh \
|
||||||
match.awk \
|
match.awk \
|
||||||
net.expected \
|
net.expected \
|
||||||
netlink_sock_diag-v.sh \
|
netlink_sock_diag-v.sh \
|
||||||
@ -373,6 +383,7 @@ EXTRA_DIST = \
|
|||||||
struct_flock.c \
|
struct_flock.c \
|
||||||
sun_path.expected \
|
sun_path.expected \
|
||||||
syntax.sh \
|
syntax.sh \
|
||||||
|
tampering-syntax.sh \
|
||||||
trace_fstat.in \
|
trace_fstat.in \
|
||||||
trace_fstatfs.in \
|
trace_fstatfs.in \
|
||||||
trace_lstat.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
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
. "${srcdir=.}/init.sh"
|
. "${srcdir=.}/mmap_name.sh"
|
||||||
|
|
||||||
check_prog grep
|
|
||||||
check_prog sed
|
check_prog sed
|
||||||
run_prog > /dev/null
|
|
||||||
|
|
||||||
syscall=
|
get_mmap_name ""
|
||||||
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
|
|
||||||
|
|
||||||
syscall=$mmap,madvise,mlockall,mprotect,mremap,msync,munmap
|
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 chdir
|
||||||
check_e "Syscall 'chdir' for -b isn't supported" -b execve -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_e_using_grep 'exec: File *name too long' "$(printf '%4096s' ' ')"
|
||||||
|
|
||||||
check_h 'must have PROG [ARGS] or -p PID'
|
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 -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 \
|
test_with -eexit,exit_group -efault=exit_group:error=ENOSYS \
|
||||||
-efault=\!process:error=1 ../answer
|
-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 \
|
test_with -eexit,exit_group -efault=all:error=ENOSYS \
|
||||||
-efault=exit:error=1:when=2+ ../answer
|
-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 \
|
test_with -eexit,exit_group -efault=exit_group:error=ENOSYS \
|
||||||
-efault=\!%desc,%file,%memory,%process,%signal,%network,%ipc:error=1 ../answer
|
-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.
|
# Check -e fault= syntax.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2017 Dmitry V. Levin <ldv@altlinux.org>
|
# Copyright (c) 2016-2017 Dmitry V. Levin <ldv@altlinux.org>
|
||||||
|
# Copyright (c) 2017 The strace developers.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# 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
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
. "${srcdir=.}/init.sh"
|
. "${srcdir=.}/tampering-syntax.sh"
|
||||||
|
|
||||||
#
|
#
|
||||||
# F
|
# F
|
||||||
# F+
|
# F+
|
||||||
# F+S
|
# F+S
|
||||||
|
|
||||||
fail_with()
|
counter=7
|
||||||
{
|
for arg in 42 \
|
||||||
dump_log_and_fail_with \
|
invalid \
|
||||||
"strace -e fault=$* failed to handle an argument error properly"
|
error= \
|
||||||
}
|
error=invalid_error_name \
|
||||||
|
error=-1 \
|
||||||
for arg in '' , ,, ,,, : :: ::: \! \!, \!: \
|
error=-2 \
|
||||||
invalid_syscall_name \
|
error=3+ \
|
||||||
invalid_syscall_name:when=3 \
|
error=4096 \
|
||||||
-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 \
|
|
||||||
; do
|
; do
|
||||||
$STRACE -e fault="$arg" true 2> "$LOG" &&
|
check_tampering_syntax fault chdir "$arg"
|
||||||
fail_with "$arg"
|
check_tampering_syntax fault chdir "$arg" "when=$counter"
|
||||||
LC_ALL=C grep -F 'invalid fault argument' < "$LOG" > /dev/null ||
|
counter=$((counter + 1))
|
||||||
fail_with "$arg"
|
|
||||||
done
|
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
|
exit 0
|
||||||
|
@ -48,29 +48,46 @@ check_fault_injection()
|
|||||||
procs=$1; shift
|
procs=$1; shift
|
||||||
extra="$*"
|
extra="$*"
|
||||||
|
|
||||||
local when=
|
local when_qualify=
|
||||||
|
local when_new=
|
||||||
if [ -z "$first$step" ]; then
|
if [ -z "$first$step" ]; then
|
||||||
first=1
|
first=1
|
||||||
step=1
|
step=1
|
||||||
else
|
else
|
||||||
case "$step" in
|
case "$step" in
|
||||||
'') when=":when=$first"; step=0 ;;
|
'')
|
||||||
+) when=":when=$first+"; step=1 ;;
|
when_qualify=":when=$first"
|
||||||
*) when=":when=$first+$step" ;;
|
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
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local error=
|
local error_qualify=
|
||||||
|
local error_new=
|
||||||
local raw=reg
|
local raw=reg
|
||||||
set --
|
set --
|
||||||
case "$err" in
|
case "$err" in
|
||||||
'') ;;
|
'') ;;
|
||||||
[123456789]*)
|
[123456789]*)
|
||||||
error=":error=$err"
|
error_qualify=":error=$err"
|
||||||
|
error_new=";error=$err"
|
||||||
raw=raw
|
raw=raw
|
||||||
set -- -e raw=all
|
set -- -e raw=all
|
||||||
;;
|
;;
|
||||||
*) error=":error=$err" ;;
|
*)
|
||||||
|
error_qualify=":error=$err"
|
||||||
|
error_new=";error=$err"
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
outexp="$NAME.out.exp"
|
outexp="$NAME.out.exp"
|
||||||
@ -79,7 +96,21 @@ check_fault_injection()
|
|||||||
outpid="$NAME.pid"
|
outpid="$NAME.pid"
|
||||||
|
|
||||||
run_strace -a11 -ff -e trace=$trace \
|
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 \
|
../$NAME $raw "$err" "$first" "$step" $N \
|
||||||
"$procs" "$outexp" "$outgot" "$outout" "$outpid"
|
"$procs" "$outexp" "$outgot" "$outout" "$outpid"
|
||||||
|
|
||||||
@ -93,7 +124,7 @@ check_fault_injection()
|
|||||||
}
|
}
|
||||||
|
|
||||||
for err in '' ENOSYS 22 einval; do
|
for err in '' ENOSYS 22 einval; do
|
||||||
for fault in writev desc,51; do
|
for fault in writev %desc,51; do
|
||||||
check_fault_injection \
|
check_fault_injection \
|
||||||
writev $fault "$err" '' '' 1 -efault=chdir
|
writev $fault "$err" '' '' 1 -efault=chdir
|
||||||
check_fault_injection \
|
check_fault_injection \
|
||||||
|
@ -6,3 +6,8 @@
|
|||||||
run_strace -a12 -echdir,exit_group -einject=chdir:error=ENOENT:signal=USR1 \
|
run_strace -a12 -echdir,exit_group -einject=chdir:error=ENOENT:signal=USR1 \
|
||||||
"../$NAME"
|
"../$NAME"
|
||||||
match_diff
|
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" "$@" \
|
run_strace -a12 -e$syscall -einject="$syscall:retval=$rval" "$@" \
|
||||||
../qual_inject-retval "$rval" > "$EXP"
|
../qual_inject-retval "$rval" > "$EXP"
|
||||||
match_diff "$LOG" "$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
|
check_injection 0
|
||||||
|
@ -6,3 +6,8 @@
|
|||||||
run_strace -a12 -echdir,exit_group -einject=chdir:signal=USR1 \
|
run_strace -a12 -echdir,exit_group -einject=chdir:signal=USR1 \
|
||||||
../$NAME
|
../$NAME
|
||||||
match_diff
|
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.
|
# Check -e inject= syntax.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2017 Dmitry V. Levin <ldv@altlinux.org>
|
# Copyright (c) 2016-2017 Dmitry V. Levin <ldv@altlinux.org>
|
||||||
|
# Copyright (c) 2017 The strace developers.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# 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
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
. "${srcdir=.}/init.sh"
|
. "${srcdir=.}/tampering-syntax.sh"
|
||||||
|
|
||||||
#
|
check_tampering_syntax inject 42
|
||||||
# F
|
check_tampering_syntax inject chdir
|
||||||
# F+
|
|
||||||
# F+S
|
|
||||||
|
|
||||||
fail_with()
|
counter=7
|
||||||
{
|
for arg in 42 \
|
||||||
dump_log_and_fail_with \
|
invalid \
|
||||||
"strace -e inject=$* failed to handle an argument error properly"
|
error= \
|
||||||
}
|
error=invalid_error_name \
|
||||||
|
error=-1 \
|
||||||
for arg in '' , ,, ,,, : :: ::: \! \!, \!: \
|
error=-2 \
|
||||||
invalid_syscall_name \
|
error=3+ \
|
||||||
invalid_syscall_name:when=3 \
|
error=4096 \
|
||||||
-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 \
|
|
||||||
; do
|
; do
|
||||||
$STRACE -e inject="$arg" true 2> "$LOG" &&
|
check_tampering_syntax inject chdir "$arg"
|
||||||
fail_with "$arg"
|
check_tampering_syntax inject chdir "$arg" "when=$counter"
|
||||||
LC_ALL=C grep -F 'invalid inject argument' < "$LOG" > /dev/null ||
|
counter=$((counter + 1))
|
||||||
fail_with "$arg"
|
|
||||||
done
|
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
|
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
|
||||||
|
}
|
6
unwind.c
6
unwind.c
@ -581,3 +581,9 @@ unwind_capture_stacktrace(struct tcb *tcp)
|
|||||||
debug_func_msg("tcp=%p, queue=%p", tcp, tcp->queue->head);
|
debug_func_msg("tcp=%p, queue=%p", tcp, tcp->queue->head);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
apply_stacktrace(struct tcb *tcp, void *_priv_data)
|
||||||
|
{
|
||||||
|
tcp->qual_flg |= QUAL_STACKTRACE;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user