From 08146b1a84f975e2cc1007242b4202dc5cc0e5c5 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Wed, 24 Jan 2018 19:28:36 +0100 Subject: sys/linux: extend netfilter descriptions --- pkg/csource/common.go | 3 + pkg/csource/linux_common.go | 149 ++++++++++++++++++++++++++++++++++++++++++++ pkg/csource/options.go | 3 + pkg/repro/repro.go | 1 + 4 files changed, 156 insertions(+) (limited to 'pkg') diff --git a/pkg/csource/common.go b/pkg/csource/common.go index f0b940881..1d1474c56 100644 --- a/pkg/csource/common.go +++ b/pkg/csource/common.go @@ -96,6 +96,9 @@ func defineList(p *prog.Prog, opts Options) ([]string, error) { } if opts.WaitRepeat { defines = append(defines, "SYZ_WAIT_REPEAT") + // TODO(dvyukov): this should have a separate option, + // but for now it's bundled with WaitRepeat. + defines = append(defines, "SYZ_RESET_NET_NAMESPACE") } if opts.Debug { defines = append(defines, "SYZ_DEBUG") diff --git a/pkg/csource/linux_common.go b/pkg/csource/linux_common.go index c44bc68a6..49d7df124 100644 --- a/pkg/csource/linux_common.go +++ b/pkg/csource/linux_common.go @@ -72,6 +72,11 @@ var commonHeaderLinux = ` #include #include #endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_RESET_NET_NAMESPACE) +#include +#include +#include +#endif #if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION) #include #include @@ -1984,6 +1989,144 @@ static int do_sandbox_namespace(int executor_pid, bool enable_tun) } #endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_RESET_NET_NAMESPACE) + +struct ipt_getinfo { + char name[32]; + unsigned int valid_hooks; + unsigned int hook_entry[5]; + unsigned int underflow[5]; + unsigned int num_entries; + unsigned int size; +}; + +struct ipt_get_entries { + char name[32]; + unsigned int size; + unsigned int pad; + char entrytable[1024]; +}; + +struct xt_counters { + uint64 pcnt, bcnt; +}; + +struct ipt_replace { + char name[32]; + unsigned int valid_hooks; + unsigned int num_entries; + unsigned int size; + unsigned int hook_entry[5]; + unsigned int underflow[5]; + unsigned int num_counters; + struct xt_counters* counters; + char entrytable[1024]; +}; + +struct ipt_table_desc { + const char* name; + struct ipt_getinfo info; + struct ipt_get_entries entries; + struct ipt_replace replace; + struct xt_counters counters[10]; +}; + +static struct ipt_table_desc ipv4_tables[] = { + {.name = "filter"}, + {.name = "nat"}, + {.name = "mangle"}, + {.name = "raw"}, + {.name = "security"}, +}; + +#define IPT_BASE_CTL 64 +#define IPT_SO_SET_REPLACE (IPT_BASE_CTL) +#define IPT_SO_GET_INFO (IPT_BASE_CTL) +#define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1) + +static void checkpoint_net_namespace(void) +{ + socklen_t optlen; + unsigned i; + int fd; + + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) + fail("socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)"); + for (i = 0; i < sizeof(ipv4_tables) / sizeof(ipv4_tables[0]); i++) { + struct ipt_table_desc* table = &ipv4_tables[i]; + strcpy(table->info.name, table->name); + strcpy(table->entries.name, table->name); + strcpy(table->replace.name, table->name); + optlen = sizeof(table->info); + if (getsockopt(fd, SOL_IP, IPT_SO_GET_INFO, &table->info, &optlen)) { + switch (errno) { + case EPERM: + case ENOENT: + case ENOPROTOOPT: + continue; + } + fail("getsockopt(IPT_SO_GET_INFO)"); + } + if (table->info.size > sizeof(table->entries.entrytable)) + fail("table size is too large: %u", table->info.size); + if (table->info.num_entries > sizeof(table->counters) / sizeof(table->counters[0])) + fail("too many counters: %u", table->info.num_entries); + table->entries.size = table->info.size; + optlen = sizeof(table->entries) - sizeof(table->entries.entrytable) + table->info.size; + if (getsockopt(fd, SOL_IP, IPT_SO_GET_ENTRIES, &table->entries, &optlen)) + fail("getsockopt(IPT_SO_GET_ENTRIES)"); + table->replace.valid_hooks = table->info.valid_hooks; + table->replace.num_entries = table->info.num_entries; + table->replace.counters = table->counters; + table->replace.size = table->info.size; + memcpy(table->replace.hook_entry, table->info.hook_entry, sizeof(table->replace.hook_entry)); + memcpy(table->replace.underflow, table->info.underflow, sizeof(table->replace.underflow)); + memcpy(table->replace.entrytable, table->entries.entrytable, table->info.size); + } + close(fd); +} + +static void reset_net_namespace(void) +{ + struct ipt_get_entries entries; + struct ipt_getinfo info; + socklen_t optlen; + unsigned i; + int fd; + + memset(&info, 0, sizeof(info)); + memset(&entries, 0, sizeof(entries)); + fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) + fail("socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)"); + for (i = 0; i < sizeof(ipv4_tables) / sizeof(ipv4_tables[0]); i++) { + struct ipt_table_desc* table = &ipv4_tables[i]; + if (table->info.valid_hooks == 0) + continue; + strcpy(info.name, table->name); + optlen = sizeof(info); + if (getsockopt(fd, SOL_IP, IPT_SO_GET_INFO, &info, &optlen)) + fail("getsockopt(IPT_SO_GET_INFO)"); + if (memcmp(&table->info, &info, sizeof(table->info)) == 0) { + strcpy(entries.name, table->name); + entries.size = table->info.size; + optlen = sizeof(entries) - sizeof(entries.entrytable) + entries.size; + if (getsockopt(fd, SOL_IP, IPT_SO_GET_ENTRIES, &entries, &optlen)) + fail("getsockopt(IPT_SO_GET_ENTRIES)"); + if (memcmp(&table->entries, &entries, optlen) == 0) + continue; + } + debug("resetting iptable %s\n", table->name); + table->replace.num_counters = info.num_entries; + optlen = sizeof(table->replace) - sizeof(table->replace.entrytable) + table->replace.size; + if (setsockopt(fd, SOL_IP, IPT_SO_SET_REPLACE, &table->replace, optlen)) + fail("setsockopt(IPT_SO_SET_REPLACE)"); + } + close(fd); +} +#endif + #if defined(SYZ_EXECUTOR) || (defined(SYZ_REPEAT) && defined(SYZ_WAIT_REPEAT) && defined(SYZ_USE_TMP_DIR)) static void remove_dir(const char* dir) { @@ -2094,6 +2237,9 @@ static void test(); void loop() { int iter; +#if defined(SYZ_RESET_NET_NAMESPACE) + checkpoint_net_namespace(); +#endif for (iter = 0;; iter++) { #ifdef SYZ_USE_TMP_DIR char cwdbuf[256]; @@ -2134,6 +2280,9 @@ void loop() } #ifdef SYZ_USE_TMP_DIR remove_dir(cwdbuf); +#endif +#if defined(SYZ_RESET_NET_NAMESPACE) + reset_net_namespace(); #endif } } diff --git a/pkg/csource/options.go b/pkg/csource/options.go index e253b3cdf..6d5f18fd5 100644 --- a/pkg/csource/options.go +++ b/pkg/csource/options.go @@ -46,6 +46,9 @@ func (opts Options) Check() error { // This does not affect generated code. return errors.New("Procs>1 without Repeat") } + if !opts.Repeat && opts.WaitRepeat { + return errors.New("WaitRepeat without Repeat") + } if opts.Sandbox == "namespace" && !opts.UseTmpDir { // This is borken and never worked. // This tries to create syz-tmp dir in cwd, diff --git a/pkg/repro/repro.go b/pkg/repro/repro.go index 29ede6fd4..bbe0e2d7c 100644 --- a/pkg/repro/repro.go +++ b/pkg/repro/repro.go @@ -789,6 +789,7 @@ var progSimplifies = []Simplify{ return false } opts.Repeat = false + opts.WaitRepeat = false opts.Procs = 1 return true }, -- cgit mrf-deployment