diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2018-01-24 19:28:36 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2018-01-27 17:08:43 +0100 |
| commit | 08146b1a84f975e2cc1007242b4202dc5cc0e5c5 (patch) | |
| tree | ad9f57cfbed4b9008223359d0f765a2b6a27a209 /pkg/csource | |
| parent | 5d7477249ba074bbdc9ffbf80314397dbe90e886 (diff) | |
sys/linux: extend netfilter descriptions
Diffstat (limited to 'pkg/csource')
| -rw-r--r-- | pkg/csource/common.go | 3 | ||||
| -rw-r--r-- | pkg/csource/linux_common.go | 149 | ||||
| -rw-r--r-- | pkg/csource/options.go | 3 |
3 files changed, 155 insertions, 0 deletions
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 <sys/stat.h> #include <sys/uio.h> #endif +#if defined(SYZ_EXECUTOR) || defined(SYZ_RESET_NET_NAMESPACE) +#include <linux/net.h> +#include <netinet/in.h> +#include <sys/socket.h> +#endif #if defined(SYZ_EXECUTOR) || defined(SYZ_FAULT_INJECTION) #include <errno.h> #include <fcntl.h> @@ -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]; @@ -2135,6 +2281,9 @@ void loop() #ifdef SYZ_USE_TMP_DIR remove_dir(cwdbuf); #endif +#if defined(SYZ_RESET_NET_NAMESPACE) + reset_net_namespace(); +#endif } } #else 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, |
