selftests/bpf: add a test for overlapping packet range checks
authorAlexei Starovoitov <ast@fb.com>
Fri, 31 Mar 2017 04:45:41 +0000 (21:45 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 1 Apr 2017 19:45:57 +0000 (12:45 -0700)
add simple C test case for llvm and verifier range check fix from
commit b1977682a385 ("bpf: improve verifier packet range checks")

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/test_pkt_access.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_progs.c [new file with mode: 0644]

index 6a1ad58cb66f4ae6faaf4837e29c5f8b487f000c..ff68c9419a6713fe8d7bea2f0bc4de589a4981d8 100644 (file)
@@ -1,16 +1,18 @@
 LIBDIR := ../../../lib
 BPFDIR := $(LIBDIR)/bpf
 
-CFLAGS += -Wall -O2 -I../../../include/uapi -I$(LIBDIR)
-LDLIBS += -lcap
+CFLAGS += -Wall -O2 -I../../../include/uapi -I$(LIBDIR) -I../../../include
+LDLIBS += -lcap -lelf
 
-TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map
+TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs
+
+TEST_GEN_FILES = test_pkt_access.o
 
 TEST_PROGS := test_kmod.sh
 
 include ../lib.mk
 
-BPFOBJ := $(OUTPUT)/bpf.o
+BPFOBJ := $(OUTPUT)/libbpf.a
 
 $(TEST_GEN_PROGS): $(BPFOBJ)
 
@@ -21,3 +23,10 @@ force:
 
 $(BPFOBJ): force
        $(MAKE) -C $(BPFDIR) OUTPUT=$(OUTPUT)/
+
+CLANG ?= clang
+
+%.o: %.c
+       $(CLANG) -I../../../include/uapi -I../../../../samples/bpf/ \
+               -D__x86_64__ -Wno-compare-distinct-pointer-types \
+               -O2 -target bpf -c $< -o $@
diff --git a/tools/testing/selftests/bpf/test_pkt_access.c b/tools/testing/selftests/bpf/test_pkt_access.c
new file mode 100644 (file)
index 0000000..fd1e083
--- /dev/null
@@ -0,0 +1,64 @@
+/* Copyright (c) 2017 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#include <stddef.h>
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/pkt_cls.h>
+#include "bpf_helpers.h"
+
+#define _htons __builtin_bswap16
+#define barrier() __asm__ __volatile__("": : :"memory")
+int _version SEC("version") = 1;
+
+SEC("test1")
+int process(struct __sk_buff *skb)
+{
+       void *data_end = (void *)(long)skb->data_end;
+       void *data = (void *)(long)skb->data;
+       struct ethhdr *eth = (struct ethhdr *)(data);
+       struct tcphdr *tcp = NULL;
+       __u8 proto = 255;
+       __u64 ihl_len;
+
+       if (eth + 1 > data_end)
+               return TC_ACT_SHOT;
+
+       if (eth->h_proto == _htons(ETH_P_IP)) {
+               struct iphdr *iph = (struct iphdr *)(eth + 1);
+
+               if (iph + 1 > data_end)
+                       return TC_ACT_SHOT;
+               ihl_len = iph->ihl * 4;
+               proto = iph->protocol;
+               tcp = (struct tcphdr *)((void *)(iph) + ihl_len);
+       } else if (eth->h_proto == _htons(ETH_P_IPV6)) {
+               struct ipv6hdr *ip6h = (struct ipv6hdr *)(eth + 1);
+
+               if (ip6h + 1 > data_end)
+                       return TC_ACT_SHOT;
+               ihl_len = sizeof(*ip6h);
+               proto = ip6h->nexthdr;
+               tcp = (struct tcphdr *)((void *)(ip6h) + ihl_len);
+       }
+
+       if (tcp) {
+               if (((void *)(tcp) + 20) > data_end || proto != 6)
+                       return TC_ACT_SHOT;
+               barrier(); /* to force ordering of checks */
+               if (((void *)(tcp) + 18) > data_end)
+                       return TC_ACT_SHOT;
+               if (tcp->urg_ptr == 123)
+                       return TC_ACT_OK;
+       }
+
+       return TC_ACT_UNSPEC;
+}
diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c
new file mode 100644 (file)
index 0000000..1d9a310
--- /dev/null
@@ -0,0 +1,138 @@
+/* Copyright (c) 2017 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include <linux/types.h>
+typedef __u16 __sum16;
+#include <arpa/inet.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+
+#include <sys/wait.h>
+#include <sys/resource.h>
+
+#include <linux/bpf.h>
+#include <linux/err.h>
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#define _htons __builtin_bswap16
+
+static int error_cnt, pass_cnt;
+
+/* ipv4 test vector */
+static struct {
+       struct ethhdr eth;
+       struct iphdr iph;
+       struct tcphdr tcp;
+} __packed pkt_v4 = {
+       .eth.h_proto = _htons(ETH_P_IP),
+       .iph.ihl = 5,
+       .iph.protocol = 6,
+       .tcp.urg_ptr = 123,
+};
+
+/* ipv6 test vector */
+static struct {
+       struct ethhdr eth;
+       struct ipv6hdr iph;
+       struct tcphdr tcp;
+} __packed pkt_v6 = {
+       .eth.h_proto = _htons(ETH_P_IPV6),
+       .iph.nexthdr = 6,
+       .tcp.urg_ptr = 123,
+};
+
+#define CHECK(condition, tag, format...) ({                            \
+       int __ret = !!(condition);                                      \
+       if (__ret) {                                                    \
+               error_cnt++;                                            \
+               printf("%s:FAIL:%s ", __func__, tag);                   \
+               printf(format);                                         \
+       } else {                                                        \
+               pass_cnt++;                                             \
+               printf("%s:PASS:%s %d nsec\n", __func__, tag, duration);\
+       }                                                               \
+})
+
+static int bpf_prog_load(const char *file, enum bpf_prog_type type,
+                        struct bpf_object **pobj, int *prog_fd)
+{
+       struct bpf_program *prog;
+       struct bpf_object *obj;
+       int err;
+
+       obj = bpf_object__open(file);
+       if (IS_ERR(obj)) {
+               error_cnt++;
+               return -ENOENT;
+       }
+
+       prog = bpf_program__next(NULL, obj);
+       if (!prog) {
+               bpf_object__close(obj);
+               error_cnt++;
+               return -ENOENT;
+       }
+
+       bpf_program__set_type(prog, type);
+       err = bpf_object__load(obj);
+       if (err) {
+               bpf_object__close(obj);
+               error_cnt++;
+               return -EINVAL;
+       }
+
+       *pobj = obj;
+       *prog_fd = bpf_program__fd(prog);
+       return 0;
+}
+
+static void test_pkt_access(void)
+{
+       const char *file = "./test_pkt_access.o";
+       struct bpf_object *obj;
+       __u32 duration, retval;
+       int err, prog_fd;
+
+       err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
+       if (err)
+               return;
+
+       err = bpf_prog_test_run(prog_fd, 100000, &pkt_v4, sizeof(pkt_v4),
+                               NULL, NULL, &retval, &duration);
+       CHECK(err || errno || retval, "ipv4",
+             "err %d errno %d retval %d duration %d\n",
+             err, errno, retval, duration);
+
+       err = bpf_prog_test_run(prog_fd, 100000, &pkt_v6, sizeof(pkt_v6),
+                               NULL, NULL, &retval, &duration);
+       CHECK(err || errno || retval, "ipv6",
+             "err %d errno %d retval %d duration %d\n",
+             err, errno, retval, duration);
+       bpf_object__close(obj);
+}
+
+int main(void)
+{
+       struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
+
+       setrlimit(RLIMIT_MEMLOCK, &rinf);
+
+       test_pkt_access();
+
+       printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt);
+       return 0;
+}