Skip to content

Commit

Permalink
executor: support fragmentation in syz_emit_ethernet
Browse files Browse the repository at this point in the history
A recent linux commit "tun: enable napi_gro_frags() for TUN/TAP driver"
added support for fragmentation when emitting packets via tun.
Support this feature in syz_emit_ethernet.
  • Loading branch information
dvyukov committed Oct 2, 2017
1 parent ffd2a08 commit eb97aa0
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 46 deletions.
98 changes: 79 additions & 19 deletions executor/common_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ static void execute_command(const char* format, ...)
}

static int tunfd = -1;
static int tun_frags_enabled;

// We just need this to be large enough to hold headers that we parse (ethernet/ip/tcp).
// Rest of the packet (if any) will be silently truncated which is fine.
Expand All @@ -281,6 +282,13 @@ static int tunfd = -1;
#define LOCAL_IPV6 "fe80::%02hxaa"
#define REMOTE_IPV6 "fe80::%02hxbb"

#ifndef IFF_NAPI
#define IFF_NAPI 0x0010
#endif
#ifndef IFF_NAPI_FRAGS
#define IFF_NAPI_FRAGS 0x0020
#endif

static void initialize_tun(uint64_t pid)
{
if (pid >= MAX_PIDS)
Expand All @@ -297,9 +305,19 @@ static void initialize_tun(uint64_t pid)
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, IFNAMSIZ);
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0)
fail("tun: ioctl(TUNSETIFF) failed");
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS;
if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0) {
// IFF_NAPI_FRAGS requires root, so try without it.
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0)
fail("tun: ioctl(TUNSETIFF) failed");
}
// If IFF_NAPI_FRAGS is not supported it will be silently dropped,
// so query the effective flags.
if (ioctl(tunfd, TUNGETIFF, (void*)&ifr) < 0)
fail("tun: ioctl(TUNGETIFF) failed");
tun_frags_enabled = (ifr.ifr_flags & IFF_NAPI_FRAGS) != 0;
debug("tun_frags_enabled=%d\n", tun_frags_enabled);

char local_mac[ADDR_MAX_LEN];
snprintf_check(local_mac, sizeof(local_mac), LOCAL_MAC, id);
Expand Down Expand Up @@ -345,7 +363,10 @@ static int read_tun(char* data, int size)
if (rv < 0) {
if (errno == EAGAIN)
return -1;
fail("tun: read failed with %d, errno: %d", rv, errno);
// Tun sometimes returns this, unclear if it's a kernel bug or not.
if (errno == EBADFD)
return -1;
fail("tun: read failed with %d", rv);
}
return rv;
}
Expand All @@ -366,17 +387,60 @@ static void debug_dump_data(const char* data, int length)
#endif

#if defined(SYZ_EXECUTOR) || (defined(__NR_syz_emit_ethernet) && defined(SYZ_TUN_ENABLE))
static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1)
{
// syz_emit_ethernet(len len[packet], packet ptr[in, eth_packet])
#define MAX_FRAGS 4
struct vnet_fragmentation {
uint32_t full;
uint32_t count;
uint32_t frags[MAX_FRAGS];
};

static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1, uintptr_t a2)
{
// syz_emit_ethernet(len len[packet], packet ptr[in, eth_packet], frags ptr[in, vnet_fragmentation, opt])
// vnet_fragmentation {
// full int32[0:1]
// count len[frags, int32]
// frags array[int32[0:4096], 1:4]
// }
if (tunfd < 0)
return (uintptr_t)-1;

int64_t length = a0;
uint32_t length = a0;
char* data = (char*)a1;
debug_dump_data(data, length);
return write(tunfd, data, length);

struct vnet_fragmentation* frags = (struct vnet_fragmentation*)a2;
struct iovec vecs[MAX_FRAGS + 1];
uint32_t nfrags = 0;
if (!tun_frags_enabled || frags == NULL) {
vecs[nfrags].iov_base = data;
vecs[nfrags].iov_len = length;
nfrags++;
} else {
bool full = true;
uint32_t i, count = 0;
NONFAILING(full = frags->full);
NONFAILING(count = frags->count);
if (count > MAX_FRAGS)
count = MAX_FRAGS;
for (i = 0; i < count && length != 0; i++) {
uint32_t size = 0;
NONFAILING(size = frags->frags[i]);
if (size > length)
size = length;
vecs[nfrags].iov_base = data;
vecs[nfrags].iov_len = size;
nfrags++;
data += size;
length -= size;
}
if (length != 0 && (full || nfrags == 0)) {
vecs[nfrags].iov_base = data;
vecs[nfrags].iov_len = length;
nfrags++;
}
}
return writev(tunfd, vecs, nfrags);
}
#endif

Expand Down Expand Up @@ -685,8 +749,6 @@ static bool write_file(const char* file, const char* what, ...)
#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NAMESPACE)
static int real_uid;
static int real_gid;
static int epid;
static bool etun;
__attribute__((aligned(64 << 10))) static char sandbox_stack[1 << 20];

static int namespace_sandbox_proc(void* arg)
Expand All @@ -700,12 +762,6 @@ 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");

#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE)
// For sandbox namespace we setup tun after initializing uid mapping,
// otherwise ip commands fail.
setup_tun(epid, etun);
#endif

if (mkdir("./syz-tmp", 0777))
fail("mkdir(syz-tmp) failed");
if (mount("", "./syz-tmp", "tmpfs", 0, NULL))
Expand Down Expand Up @@ -759,10 +815,14 @@ static int namespace_sandbox_proc(void* arg)

static int do_sandbox_namespace(int executor_pid, bool enable_tun)
{
#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE)
// For sandbox namespace we setup tun before dropping privs,
// because IFF_NAPI_FRAGS requires root.
setup_tun(executor_pid, enable_tun);
#endif

real_uid = getuid();
real_gid = getgid();
epid = executor_pid;
etun = enable_tun;
mprotect(sandbox_stack, 4096, PROT_NONE); // to catch stack underflows
return clone(namespace_sandbox_proc, &sandbox_stack[sizeof(sandbox_stack) - 64],
CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNET, NULL);
Expand Down
10 changes: 5 additions & 5 deletions executor/syscalls_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#if defined(__i386__) || 0
#define GOARCH "386"
#define SYZ_REVISION "7b2f1949d48094cf3369932d0743db166049457b"
#define SYZ_REVISION "20782fd14495c17a4a404316929c89e9d66373d6"
#define __NR_syz_emit_ethernet 1000000
#define __NR_syz_extract_tcp_res 1000001
#define __NR_syz_fuse_mount 1000002
Expand Down Expand Up @@ -1501,7 +1501,7 @@ call_t syscalls[] = {

#if defined(__x86_64__) || 0
#define GOARCH "amd64"
#define SYZ_REVISION "8349df62e623f9c8d8bfaefcc8ba3febf463ce92"
#define SYZ_REVISION "c1ab3550b8fbe92662ee054df76419327b5b72a7"
#define __NR_syz_emit_ethernet 1000000
#define __NR_syz_extract_tcp_res 1000001
#define __NR_syz_fuse_mount 1000002
Expand Down Expand Up @@ -3061,7 +3061,7 @@ call_t syscalls[] = {

#if defined(__arm__) || 0
#define GOARCH "arm"
#define SYZ_REVISION "02567c0623e18214f0be8d059aad68c263064645"
#define SYZ_REVISION "48d11406b51cb3dad2399863375cfc24d4892473"
#define __NR_syz_emit_ethernet 1000000
#define __NR_syz_extract_tcp_res 1000001
#define __NR_syz_fuse_mount 1000002
Expand Down Expand Up @@ -4574,7 +4574,7 @@ call_t syscalls[] = {

#if defined(__aarch64__) || 0
#define GOARCH "arm64"
#define SYZ_REVISION "0709fe60bdd20ea30d937d14615a40269fadb2b8"
#define SYZ_REVISION "65746b600515d1f1026ed56961d3450e70d65878"
#define __NR_syz_emit_ethernet 1000000
#define __NR_syz_extract_tcp_res 1000001
#define __NR_syz_fuse_mount 1000002
Expand Down Expand Up @@ -6062,7 +6062,7 @@ call_t syscalls[] = {

#if defined(__ppc64__) || defined(__PPC64__) || defined(__powerpc64__) || 0
#define GOARCH "ppc64le"
#define SYZ_REVISION "8bfb8686625fa3398eb8d1a5d66834dec0d3fa06"
#define SYZ_REVISION "5e9d29319478130f5d8b9238e869d979870e5cb3"
#define __NR_syz_emit_ethernet 1000000
#define __NR_syz_extract_tcp_res 1000001
#define __NR_syz_fuse_mount 1000002
Expand Down
83 changes: 67 additions & 16 deletions pkg/csource/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ static void execute_command(const char* format, ...)
}
static int tunfd = -1;
static int tun_frags_enabled;
#define SYZ_TUN_MAX_PACKET_SIZE 1000
Expand All @@ -411,6 +412,13 @@ static int tunfd = -1;
#define LOCAL_IPV6 "fe80::%02hxaa"
#define REMOTE_IPV6 "fe80::%02hxbb"
#ifndef IFF_NAPI
#define IFF_NAPI 0x0010
#endif
#ifndef IFF_NAPI_FRAGS
#define IFF_NAPI_FRAGS 0x0020
#endif
static void initialize_tun(uint64_t pid)
{
if (pid >= MAX_PIDS)
Expand All @@ -427,9 +435,16 @@ static void initialize_tun(uint64_t pid)
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, IFNAMSIZ);
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0)
fail("tun: ioctl(TUNSETIFF) failed");
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS;
if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0) {
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
if (ioctl(tunfd, TUNSETIFF, (void*)&ifr) < 0)
fail("tun: ioctl(TUNSETIFF) failed");
}
if (ioctl(tunfd, TUNGETIFF, (void*)&ifr) < 0)
fail("tun: ioctl(TUNGETIFF) failed");
tun_frags_enabled = (ifr.ifr_flags & IFF_NAPI_FRAGS) != 0;
debug("tun_frags_enabled=%d\n", tun_frags_enabled);
char local_mac[ADDR_MAX_LEN];
snprintf_check(local_mac, sizeof(local_mac), LOCAL_MAC, id);
Expand Down Expand Up @@ -472,7 +487,9 @@ static int read_tun(char* data, int size)
if (rv < 0) {
if (errno == EAGAIN)
return -1;
fail("tun: read failed with %d, errno: %d", rv, errno);
if (errno == EBADFD)
return -1;
fail("tun: read failed with %d", rv);
}
return rv;
}
Expand All @@ -493,16 +510,54 @@ static void debug_dump_data(const char* data, int length)
#endif
#if defined(SYZ_EXECUTOR) || (defined(__NR_syz_emit_ethernet) && defined(SYZ_TUN_ENABLE))
static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1)
{
#define MAX_FRAGS 4
struct vnet_fragmentation {
uint32_t full;
uint32_t count;
uint32_t frags[MAX_FRAGS];
};
static uintptr_t syz_emit_ethernet(uintptr_t a0, uintptr_t a1, uintptr_t a2)
{
if (tunfd < 0)
return (uintptr_t)-1;
int64_t length = a0;
uint32_t length = a0;
char* data = (char*)a1;
debug_dump_data(data, length);
return write(tunfd, data, length);
struct vnet_fragmentation* frags = (struct vnet_fragmentation*)a2;
struct iovec vecs[MAX_FRAGS + 1];
uint32_t nfrags = 0;
if (!tun_frags_enabled || frags == NULL) {
vecs[nfrags].iov_base = data;
vecs[nfrags].iov_len = length;
nfrags++;
} else {
bool full = true;
uint32_t i, count = 0;
NONFAILING(full = frags->full);
NONFAILING(count = frags->count);
if (count > MAX_FRAGS)
count = MAX_FRAGS;
for (i = 0; i < count && length != 0; i++) {
uint32_t size = 0;
NONFAILING(size = frags->frags[i]);
if (size > length)
size = length;
vecs[nfrags].iov_base = data;
vecs[nfrags].iov_len = size;
nfrags++;
data += size;
length -= size;
}
if (length != 0 && (full || nfrags == 0)) {
vecs[nfrags].iov_base = data;
vecs[nfrags].iov_len = length;
nfrags++;
}
}
return writev(tunfd, vecs, nfrags);
}
#endif
Expand Down Expand Up @@ -1745,8 +1800,6 @@ static bool write_file(const char* file, const char* what, ...)
#if defined(SYZ_EXECUTOR) || defined(SYZ_SANDBOX_NAMESPACE)
static int real_uid;
static int real_gid;
static int epid;
static bool etun;
__attribute__((aligned(64 << 10))) static char sandbox_stack[1 << 20];
static int namespace_sandbox_proc(void* arg)
Expand All @@ -1759,10 +1812,6 @@ 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");
#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE)
setup_tun(epid, etun);
#endif
if (mkdir("./syz-tmp", 0777))
fail("mkdir(syz-tmp) failed");
if (mount("", "./syz-tmp", "tmpfs", 0, NULL))
Expand Down Expand Up @@ -1812,10 +1861,12 @@ static int namespace_sandbox_proc(void* arg)
static int do_sandbox_namespace(int executor_pid, bool enable_tun)
{
#if defined(SYZ_EXECUTOR) || defined(SYZ_TUN_ENABLE)
setup_tun(executor_pid, enable_tun);
#endif
real_uid = getuid();
real_gid = getgid();
epid = executor_pid;
etun = enable_tun;
mprotect(sandbox_stack, 4096, PROT_NONE);
return clone(namespace_sandbox_proc, &sandbox_stack[sizeof(sandbox_stack) - 64],
CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNET, NULL);
Expand Down
8 changes: 7 additions & 1 deletion sys/linux/386.go
Original file line number Diff line number Diff line change
Expand Up @@ -5563,6 +5563,11 @@ var structDescs_386 = []*KeyedStruct{
&IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int16", FldName: "dei", TypeSize: 2}, BitfieldOff: 3, BitfieldLen: 1, BitfieldMdl: true}},
&IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int16", FldName: "vid", TypeSize: 2}, BitfieldOff: 4, BitfieldLen: 12}},
}}},
{Key: StructKey{Name: "vnet_fragmentation"}, Desc: &StructDesc{TypeCommon: TypeCommon{TypeName: "vnet_fragmentation"}, Fields: []Type{
&IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", FldName: "full", TypeSize: 4}}, Kind: 2, RangeEnd: 1},
&LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "len", FldName: "count", TypeSize: 4}}, Buf: "frags"},
&ArrayType{TypeCommon: TypeCommon{TypeName: "array", FldName: "frags"}, Type: &IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int32", TypeSize: 4}}, Kind: 2, RangeEnd: 4096}, Kind: 1, RangeBegin: 1, RangeEnd: 4},
}}},
{Key: StructKey{Name: "vt_consize"}, Desc: &StructDesc{TypeCommon: TypeCommon{TypeName: "vt_consize", TypeSize: 12}, Fields: []Type{
&IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int16", FldName: "rows", TypeSize: 2}}},
&IntType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "int16", FldName: "cols", TypeSize: 2}}},
Expand Down Expand Up @@ -13119,6 +13124,7 @@ var syscalls_386 = []*Syscall{
{ID: 1333, NR: 1000000, Name: "syz_emit_ethernet", CallName: "syz_emit_ethernet", Args: []Type{
&LenType{IntTypeCommon: IntTypeCommon{TypeCommon: TypeCommon{TypeName: "len", FldName: "len", TypeSize: 4}}, Buf: "packet"},
&PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "packet", TypeSize: 4}, Type: &StructType{Key: StructKey{Name: "eth_packet"}}},
&PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "frags", TypeSize: 4, IsOptional: true}, Type: &StructType{Key: StructKey{Name: "vnet_fragmentation"}}},
}},
{ID: 1334, NR: 1000001, Name: "syz_extract_tcp_res", CallName: "syz_extract_tcp_res", Args: []Type{
&PtrType{TypeCommon: TypeCommon{TypeName: "ptr", FldName: "res", TypeSize: 4}, Type: &StructType{Key: StructKey{Name: "tcp_resources", Dir: 1}}},
Expand Down Expand Up @@ -16734,4 +16740,4 @@ var consts_386 = []ConstValue{
{Name: "__WNOTHREAD", Value: 536870912},
}

const revision_386 = "7b2f1949d48094cf3369932d0743db166049457b"
const revision_386 = "20782fd14495c17a4a404316929c89e9d66373d6"
Loading

0 comments on commit eb97aa0

Please sign in to comment.