aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
authorAndrey Konovalov <andreyknvl@gmail.com>2017-05-26 15:28:57 +0200
committerGitHub <noreply@github.com>2017-05-26 15:28:57 +0200
commit8320ea0056ddf1f35ea1daf9584f9f9aa6ae4491 (patch)
tree267621aa41026391e91003045cfe3bbec562993d /executor
parenta2ef63b51f2efd9213f410577d07dead25a0f85f (diff)
parentf919224c44b9828208a3cce79b93183df8ca4fb4 (diff)
Merge pull request #175 from xairy/up-tcp-sequence
sys, executor: extract tcp sequence numbers from /dev/net/tun
Diffstat (limited to 'executor')
-rw-r--r--executor/common.h152
-rw-r--r--executor/executor.cc5
2 files changed, 146 insertions, 11 deletions
diff --git a/executor/common.h b/executor/common.h
index c264d7b70..c32341f57 100644
--- a/executor/common.h
+++ b/executor/common.h
@@ -20,10 +20,15 @@
#include <sys/wait.h>
#include <linux/capability.h>
-#include <linux/if.h>
-#include <linux/if_tun.h>
#include <linux/kvm.h>
#include <linux/sched.h>
+
+#include <arpa/inet.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_tun.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
#include <net/if_arp.h>
#include <assert.h>
@@ -184,7 +189,11 @@ static void install_segv_handler()
*(type*)(addr) = new_val; \
}
-#ifdef __NR_syz_emit_ethernet
+#if defined(__NR_syz_emit_ethernet) || defined(__NR_syz_extract_tcp_res)
+#define SYZ_TUN_ENABLE
+#endif
+
+#ifdef SYZ_TUN_ENABLE
static void vsnprintf_check(char* str, size_t size, const char* format, va_list args)
{
int rv;
@@ -225,6 +234,8 @@ static void execute_command(const char* format, ...)
int tunfd = -1;
+#define SYZ_TUN_MAX_PACKET_SIZE (64 << 10)
+
// sysgen knowns about this constant (maxPids)
#define MAX_PIDS 32
#define ADDR_MAX_LEN 32
@@ -235,8 +246,8 @@ int tunfd = -1;
#define LOCAL_IPV4 "172.20.%d.170"
#define REMOTE_IPV4 "172.20.%d.187"
-#define LOCAL_IPV6 "fd00::%02hxaa"
-#define REMOTE_IPV6 "fd00::%02hxbb"
+#define LOCAL_IPV6 "fe80::%02hxaa"
+#define REMOTE_IPV6 "fe80::%02hxbb"
static void initialize_tun(uint64_t pid)
{
@@ -244,7 +255,7 @@ static void initialize_tun(uint64_t pid)
fail("tun: no more than %d executors", MAX_PIDS);
int id = pid;
- tunfd = open("/dev/net/tun", O_RDWR);
+ tunfd = open("/dev/net/tun", O_RDWR | O_NONBLOCK);
if (tunfd == -1)
fail("tun: can't open /dev/net/tun");
@@ -273,12 +284,19 @@ static void initialize_tun(uint64_t pid)
char remote_ipv6[ADDR_MAX_LEN];
snprintf_check(remote_ipv6, sizeof(remote_ipv6), REMOTE_IPV6, id);
+ // Disable IPv6 DAD, otherwise the address remains unusable until DAD completes.
+ execute_command("sysctl -w net.ipv6.conf.%s.accept_dad=0", iface);
+
+ // Disable IPv6 router solicitation to prevent IPv6 spam.
+ execute_command("sysctl -w net.ipv6.conf.%s.router_solicitations=0", iface);
+ // There seems to be no way to disable IPv6 MTD to prevent more IPv6 spam.
+
execute_command("ip link set dev %s address %s", iface, local_mac);
execute_command("ip addr add %s/24 dev %s", local_ipv4, iface);
execute_command("ip -6 addr add %s/120 dev %s", local_ipv6, iface);
execute_command("ip neigh add %s lladdr %s dev %s nud permanent", remote_ipv4, remote_mac, iface);
execute_command("ip -6 neigh add %s lladdr %s dev %s nud permanent", remote_ipv6, remote_mac, iface);
- execute_command("ip link set %s up", iface);
+ execute_command("ip link set dev %s up", iface);
}
static void setup_tun(uint64_t pid, bool enable_tun)
@@ -287,16 +305,124 @@ static void setup_tun(uint64_t pid, bool enable_tun)
initialize_tun(pid);
}
+void debug_dump_data(const char* data, int length)
+{
+ int i;
+ for (i = 0; i < length; i++) {
+ debug("%02hx ", (uint8_t)data[i] & (uint8_t)0xff);
+ if (i % 16 == 15)
+ debug("\n");
+ }
+ if (i % 16 != 0)
+ debug("\n");
+}
+#endif // SYZ_TUN_ENABLE
+
+#ifdef __NR_syz_emit_ethernet
static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1)
{
+ // syz_emit_ethernet(len len[packet], packet ptr[in, eth_packet])
+
if (tunfd < 0)
return (uintptr_t)-1;
int64_t length = a0;
char* data = (char*)a1;
+ debug_dump_data(data, length);
return write(tunfd, data, length);
}
-#endif // __NR_syz_emit_ethernet
+#endif
+
+#ifdef __NR_syz_extract_tcp_res
+// Can't include <linux/ipv6.h>, since it causes
+// conflicts due to some structs redefinition.
+struct ipv6hdr {
+ __u8 priority : 4,
+ version : 4;
+ __u8 flow_lbl[3];
+
+ __be16 payload_len;
+ __u8 nexthdr;
+ __u8 hop_limit;
+
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+};
+
+struct tcp_resources {
+ int32_t seq;
+ int32_t ack;
+};
+
+int read_tun(char* data, int size)
+{
+ int rv = read(tunfd, data, size);
+ if (rv < 0) {
+ if (errno == EAGAIN)
+ return -1;
+ fail("tun: read failed with %d, errno: %d", rv, errno);
+ }
+ return rv;
+}
+
+void flush_tun()
+{
+ char data[SYZ_TUN_MAX_PACKET_SIZE];
+ while (read_tun(&data[0], sizeof(data)) != -1)
+ ;
+}
+
+static uintptr_t syz_extract_tcp_res(uintptr_t a0, uintptr_t a1, uintptr_t a2)
+{
+ // syz_extract_tcp_res(res ptr[out, tcp_resources], seq_inc int32, ack_inc int32)
+
+ if (tunfd < 0)
+ return (uintptr_t)-1;
+
+ char data[SYZ_TUN_MAX_PACKET_SIZE];
+ int rv = read_tun(&data[0], sizeof(data));
+ if (rv == -1)
+ return (uintptr_t)-1;
+ size_t length = rv;
+ debug_dump_data(data, length);
+
+ struct tcphdr* tcphdr;
+
+ if (length < sizeof(struct ethhdr))
+ return (uintptr_t)-1;
+ struct ethhdr* ethhdr = (struct ethhdr*)&data[0];
+
+ if (ethhdr->h_proto == htons(ETH_P_IP)) {
+ if (length < sizeof(struct ethhdr) + sizeof(struct iphdr))
+ return (uintptr_t)-1;
+ struct iphdr* iphdr = (struct iphdr*)&data[sizeof(struct ethhdr)];
+ if (iphdr->protocol != IPPROTO_TCP)
+ return (uintptr_t)-1;
+ if (length < sizeof(struct ethhdr) + iphdr->ihl * 4 + sizeof(struct tcphdr))
+ return (uintptr_t)-1;
+ tcphdr = (struct tcphdr*)&data[sizeof(struct ethhdr) + iphdr->ihl * 4];
+ } else {
+ if (length < sizeof(struct ethhdr) + sizeof(struct ipv6hdr))
+ return (uintptr_t)-1;
+ struct ipv6hdr* ipv6hdr = (struct ipv6hdr*)&data[sizeof(struct ethhdr)];
+ // TODO: parse and skip extension headers.
+ if (ipv6hdr->nexthdr != IPPROTO_TCP)
+ return (uintptr_t)-1;
+ if (length < sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct tcphdr))
+ return (uintptr_t)-1;
+ tcphdr = (struct tcphdr*)&data[sizeof(struct ethhdr) + sizeof(struct ipv6hdr)];
+ }
+
+ struct tcp_resources* res = (struct tcp_resources*)a0;
+ NONFAILING(res->seq = htonl((ntohl(tcphdr->seq) + (uint32_t)a1)));
+ NONFAILING(res->ack = htonl((ntohl(tcphdr->ack_seq) + (uint32_t)a2)));
+
+ debug("extracted seq: %08x\n", res->seq);
+ debug("extracted ack: %08x\n", res->ack);
+
+ return 0;
+}
+#endif
struct csum_inet {
uint32_t acc;
@@ -469,6 +595,10 @@ static uintptr_t execute_syscall(int nr, uintptr_t a0, uintptr_t a1, uintptr_t a
case __NR_syz_emit_ethernet:
return syz_emit_ethernet(a0, a1);
#endif
+#ifdef __NR_syz_extract_tcp_res
+ case __NR_syz_extract_tcp_res:
+ return syz_extract_tcp_res(a0, a1, a2);
+#endif
#ifdef __NR_syz_kvm_setup_cpu
case __NR_syz_kvm_setup_cpu:
return syz_kvm_setup_cpu(a0, a1, a2, a3, a4, a5, a6, a7);
@@ -530,7 +660,7 @@ static int do_sandbox_none(int executor_pid, bool enable_tun)
return pid;
sandbox_common();
-#ifdef __NR_syz_emit_ethernet
+#ifdef SYZ_TUN_ENABLE
setup_tun(executor_pid, enable_tun);
#endif
@@ -547,7 +677,7 @@ static int do_sandbox_setuid(int executor_pid, bool enable_tun)
return pid;
sandbox_common();
-#ifdef __NR_syz_emit_ethernet
+#ifdef SYZ_TUN_ENABLE
setup_tun(executor_pid, enable_tun);
#endif
@@ -608,7 +738,7 @@ static int namespace_sandbox_proc(void* arg)
if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid))
fail("write of /proc/self/gid_map failed");
-#ifdef __NR_syz_emit_ethernet
+#ifdef SYZ_TUN_ENABLE
// For sandbox namespace we setup tun after initializing uid mapping,
// otherwise ip commands fail.
setup_tun(epid, etun);
diff --git a/executor/executor.cc b/executor/executor.cc
index 955302cab..02792f82f 100644
--- a/executor/executor.cc
+++ b/executor/executor.cc
@@ -256,6 +256,11 @@ void loop()
fail("failed to chdir");
close(kInPipeFd);
close(kOutPipeFd);
+ if (flag_enable_tun) {
+ // Read all remaining packets from tun to better
+ // isolate consequently executing programs.
+ flush_tun();
+ }
execute_one();
debug("worker exiting\n");
doexit(0);