History

selftests/bpf: Extend verifier and bpf_sock tests for dst_port loads

Add coverage to the verifier tests and tests for reading bpf_sock fields to ensure that 32-bit, 16-bit, and 8-bit loads from dst_port field are allowed only at intended offsets and produce expected values. While 16-bit and 8-bit access to dst_port field is straight-forward, 32-bit wide loads need be allowed and produce a zero-padded 16-bit value for backward compatibility.

Jakub Sitnicki committed 4 years ago

Commit 8f50f161 parent 4421a584 files · +162 −21Browse files

Unified · Split

4 files
  1. tools/include/uapi/linux/bpf.h +2 −1
  2. tools/testing/selftests/bpf/prog_tests/sock_fields.c +41 −17
  3. tools/testing/selftests/bpf/progs/test_sock_fields.c +41 −0
  4. tools/testing/selftests/bpf/verifier/sock.c +78 −3
tools/include/uapi/linux/bpf.h+2−1
@@ -5574,7 +5574,8 @@ struct bpf_sock { · 5573 unmodified lines
55745574 __u32 src_ip4;
55755575 __u32 src_ip6[4];
55765576 __u32 src_port; /* host byte order */
5577− __u32 dst_port; /* network byte order */
5577+ __be16 dst_port; /* network byte order */
5578+ __u16 :16; /* zero padding */
55785579 __u32 dst_ip4;
55795580 __u32 dst_ip6[4];
55805581 __u32 state;
tools/testing/selftests/bpf/prog_tests/sock_fields.c+41−17
@@ -1,9 +1,11 @@
11 // SPDX-License-Identifier: GPL-2.0
22 /* Copyright (c) 2019 Facebook */
33
4+#define _GNU_SOURCE
45 #include <netinet/in.h>
56 #include <arpa/inet.h>
67 #include <unistd.h>
8+#include <sched.h>
79 #include <stdlib.h>
810 #include <string.h>
911 #include <errno.h>
@@ -20,6 +22,7 @@ · 10 unmodified lines
2022 enum bpf_linum_array_idx {
2123 EGRESS_LINUM_IDX,
2224 INGRESS_LINUM_IDX,
25+ READ_SK_DST_PORT_LINUM_IDX,
2326 __NR_BPF_LINUM_ARRAY_IDX,
2427 };
2528
@@ -42,8 +45,16 @@ static __u64 child_cg_id; · 16 unmodified lines
4245 static int linum_map_fd;
4346 static __u32 duration;
4447
45−static __u32 egress_linum_idx = EGRESS_LINUM_IDX;
46−static __u32 ingress_linum_idx = INGRESS_LINUM_IDX;
48+static bool create_netns(void)
49+{
50+ if (!ASSERT_OK(unshare(CLONE_NEWNET), "create netns"))
51+ return false;
52+
53+ if (!ASSERT_OK(system("ip link set dev lo up"), "bring up lo"))
54+ return false;
55+
56+ return true;
57+}
4758
4859 static void print_sk(const struct bpf_sock *sk, const char *prefix)
4960 {
@@ -91,19 +102,24 @@ static void check_result(void) · 41 unmodified lines
91102 {
92103 struct bpf_tcp_sock srv_tp, cli_tp, listen_tp;
93104 struct bpf_sock srv_sk, cli_sk, listen_sk;
94− __u32 ingress_linum, egress_linum;
105+ __u32 idx, ingress_linum, egress_linum, linum;
95106 int err;
96107
97− err = bpf_map_lookup_elem(linum_map_fd, &egress_linum_idx,
98− &egress_linum);
108+ idx = EGRESS_LINUM_IDX;
109+ err = bpf_map_lookup_elem(linum_map_fd, &idx, &egress_linum);
99110 CHECK(err < 0, "bpf_map_lookup_elem(linum_map_fd)",
100111 "err:%d errno:%d\n", err, errno);
101112
102− err = bpf_map_lookup_elem(linum_map_fd, &ingress_linum_idx,
103− &ingress_linum);
113+ idx = INGRESS_LINUM_IDX;
114+ err = bpf_map_lookup_elem(linum_map_fd, &idx, &ingress_linum);
104115 CHECK(err < 0, "bpf_map_lookup_elem(linum_map_fd)",
105116 "err:%d errno:%d\n", err, errno);
106117
118+ idx = READ_SK_DST_PORT_LINUM_IDX;
119+ err = bpf_map_lookup_elem(linum_map_fd, &idx, &linum);
120+ ASSERT_OK(err, "bpf_map_lookup_elem(linum_map_fd, READ_SK_DST_PORT_IDX)");
121+ ASSERT_EQ(linum, 0, "failure in read_sk_dst_port on line");
122+
107123 memcpy(&srv_sk, &skel->bss->srv_sk, sizeof(srv_sk));
108124 memcpy(&srv_tp, &skel->bss->srv_tp, sizeof(srv_tp));
109125 memcpy(&cli_sk, &skel->bss->cli_sk, sizeof(cli_sk));
@@ -262,7 +278,7 @@ static void test(void) · 152 unmodified lines
262278 char buf[DATA_LEN];
263279
264280 /* Prepare listen_fd */
265− listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0, 0);
281+ listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1", 0xcafe, 0);
266282 /* start_server() has logged the error details */
267283 if (CHECK_FAIL(listen_fd == -1))
268284 goto done;
@@ -330,8 +346,12 @@ done: · 61 unmodified lines
330346
331347 void serial_test_sock_fields(void)
332348 {
333− struct bpf_link *egress_link = NULL, *ingress_link = NULL;
334349 int parent_cg_fd = -1, child_cg_fd = -1;
350+ struct bpf_link *link;
351+
352+ /* Use a dedicated netns to have a fixed listen port */
353+ if (!create_netns())
354+ return;
335355
336356 /* Create a cgroup, get fd, and join it */
337357 parent_cg_fd = test__join_cgroup(PARENT_CGROUP);
@@ -352,15 +372,20 @@ void serial_test_sock_fields(void) · 14 unmodified lines
352372 if (CHECK(!skel, "test_sock_fields__open_and_load", "failed\n"))
353373 goto done;
354374
355− egress_link = bpf_program__attach_cgroup(skel->progs.egress_read_sock_fields,
356− child_cg_fd);
357− if (!ASSERT_OK_PTR(egress_link, "attach_cgroup(egress)"))
375+ link = bpf_program__attach_cgroup(skel->progs.egress_read_sock_fields, child_cg_fd);
376+ if (!ASSERT_OK_PTR(link, "attach_cgroup(egress_read_sock_fields)"))
377+ goto done;
378+ skel->links.egress_read_sock_fields = link;
379+
380+ link = bpf_program__attach_cgroup(skel->progs.ingress_read_sock_fields, child_cg_fd);
381+ if (!ASSERT_OK_PTR(link, "attach_cgroup(ingress_read_sock_fields)"))
358382 goto done;
383+ skel->links.ingress_read_sock_fields = link;
359384
360− ingress_link = bpf_program__attach_cgroup(skel->progs.ingress_read_sock_fields,
361− child_cg_fd);
362− if (!ASSERT_OK_PTR(ingress_link, "attach_cgroup(ingress)"))
385+ link = bpf_program__attach_cgroup(skel->progs.read_sk_dst_port, child_cg_fd);
386+ if (!ASSERT_OK_PTR(link, "attach_cgroup(read_sk_dst_port"))
363387 goto done;
388+ skel->links.read_sk_dst_port = link;
364389
365390 linum_map_fd = bpf_map__fd(skel->maps.linum_map);
366391 sk_pkt_out_cnt_fd = bpf_map__fd(skel->maps.sk_pkt_out_cnt);
@@ -369,8 +394,7 @@ void serial_test_sock_fields(void) · 2 unmodified lines
369394 test();
370395
371396 done:
372− bpf_link__destroy(egress_link);
373− bpf_link__destroy(ingress_link);
397+ test_sock_fields__detach(skel);
374398 test_sock_fields__destroy(skel);
375399 if (child_cg_fd >= 0)
376400 close(child_cg_fd);
tools/testing/selftests/bpf/progs/test_sock_fields.c+41−0
@@ -12,6 +12,7 @@ · 11 unmodified lines
1212 enum bpf_linum_array_idx {
1313 EGRESS_LINUM_IDX,
1414 INGRESS_LINUM_IDX,
15+ READ_SK_DST_PORT_LINUM_IDX,
1516 __NR_BPF_LINUM_ARRAY_IDX,
1617 };
1718
@@ -250,4 +251,44 @@ int ingress_read_sock_fields(struct __sk_buff *skb) · 232 unmodified lines
250251 return CG_OK;
251252 }
252253
254+static __noinline bool sk_dst_port__load_word(struct bpf_sock *sk)
255+{
256+ __u32 *word = (__u32 *)&sk->dst_port;
257+ return word[0] == bpf_htonl(0xcafe0000);
258+}
259+
260+static __noinline bool sk_dst_port__load_half(struct bpf_sock *sk)
261+{
262+ __u16 *half = (__u16 *)&sk->dst_port;
263+ return half[0] == bpf_htons(0xcafe);
264+}
265+
266+static __noinline bool sk_dst_port__load_byte(struct bpf_sock *sk)
267+{
268+ __u8 *byte = (__u8 *)&sk->dst_port;
269+ return byte[0] == 0xca && byte[1] == 0xfe;
270+}
271+
272+SEC("cgroup_skb/egress")
273+int read_sk_dst_port(struct __sk_buff *skb)
274+{
275+ __u32 linum, linum_idx;
276+ struct bpf_sock *sk;
277+
278+ linum_idx = READ_SK_DST_PORT_LINUM_IDX;
279+
280+ sk = skb->sk;
281+ if (!sk)
282+ RET_LOG();
283+
284+ if (!sk_dst_port__load_word(sk))
285+ RET_LOG();
286+ if (!sk_dst_port__load_half(sk))
287+ RET_LOG();
288+ if (!sk_dst_port__load_byte(sk))
289+ RET_LOG();
290+
291+ return CG_OK;
292+}
293+
253294 char _license[] SEC("license") = "GPL";
tools/testing/selftests/bpf/verifier/sock.c+78−3
@@ -121,7 +121,25 @@ · 120 unmodified lines
121121 .result = ACCEPT,
122122 },
123123 {
124− "sk_fullsock(skb->sk): sk->dst_port [narrow load]",
124+ "sk_fullsock(skb->sk): sk->dst_port [word load] (backward compatibility)",
125+ .insns = {
126+ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
127+ BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
128+ BPF_MOV64_IMM(BPF_REG_0, 0),
129+ BPF_EXIT_INSN(),
130+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
131+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
132+ BPF_MOV64_IMM(BPF_REG_0, 0),
133+ BPF_EXIT_INSN(),
134+ BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_port)),
135+ BPF_MOV64_IMM(BPF_REG_0, 0),
136+ BPF_EXIT_INSN(),
137+ },
138+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
139+ .result = ACCEPT,
140+},
141+{
142+ "sk_fullsock(skb->sk): sk->dst_port [half load]",
125143 .insns = {
126144 BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
127145 BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
@@ -139,7 +157,64 @@ · 11 unmodified lines
139157 .result = ACCEPT,
140158 },
141159 {
142− "sk_fullsock(skb->sk): sk->dst_port [load 2nd byte]",
160+ "sk_fullsock(skb->sk): sk->dst_port [half load] (invalid)",
161+ .insns = {
162+ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
163+ BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
164+ BPF_MOV64_IMM(BPF_REG_0, 0),
165+ BPF_EXIT_INSN(),
166+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
167+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
168+ BPF_MOV64_IMM(BPF_REG_0, 0),
169+ BPF_EXIT_INSN(),
170+ BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_port) + 2),
171+ BPF_MOV64_IMM(BPF_REG_0, 0),
172+ BPF_EXIT_INSN(),
173+ },
174+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
175+ .result = REJECT,
176+ .errstr = "invalid sock access",
177+},
178+{
179+ "sk_fullsock(skb->sk): sk->dst_port [byte load]",
180+ .insns = {
181+ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
182+ BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
183+ BPF_MOV64_IMM(BPF_REG_0, 0),
184+ BPF_EXIT_INSN(),
185+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
186+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
187+ BPF_MOV64_IMM(BPF_REG_0, 0),
188+ BPF_EXIT_INSN(),
189+ BPF_LDX_MEM(BPF_B, BPF_REG_2, BPF_REG_0, offsetof(struct bpf_sock, dst_port)),
190+ BPF_LDX_MEM(BPF_B, BPF_REG_2, BPF_REG_0, offsetof(struct bpf_sock, dst_port) + 1),
191+ BPF_MOV64_IMM(BPF_REG_0, 0),
192+ BPF_EXIT_INSN(),
193+ },
194+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
195+ .result = ACCEPT,
196+},
197+{
198+ "sk_fullsock(skb->sk): sk->dst_port [byte load] (invalid)",
199+ .insns = {
200+ BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
201+ BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
202+ BPF_MOV64_IMM(BPF_REG_0, 0),
203+ BPF_EXIT_INSN(),
204+ BPF_EMIT_CALL(BPF_FUNC_sk_fullsock),
205+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
206+ BPF_MOV64_IMM(BPF_REG_0, 0),
207+ BPF_EXIT_INSN(),
208+ BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_port) + 2),
209+ BPF_MOV64_IMM(BPF_REG_0, 0),
210+ BPF_EXIT_INSN(),
211+ },
212+ .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
213+ .result = REJECT,
214+ .errstr = "invalid sock access",
215+},
216+{
217+ "sk_fullsock(skb->sk): past sk->dst_port [half load] (invalid)",
143218 .insns = {
144219 BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
145220 BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
@@ -149,7 +224,7 @@ · 3 unmodified lines
149224 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
150225 BPF_MOV64_IMM(BPF_REG_0, 0),
151226 BPF_EXIT_INSN(),
152− BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, offsetof(struct bpf_sock, dst_port) + 1),
227+ BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_0, offsetofend(struct bpf_sock, dst_port)),
153228 BPF_MOV64_IMM(BPF_REG_0, 0),
154229 BPF_EXIT_INSN(),
155230 },