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
5574 __u32 src_ip4;5574 __u32 src_ip4;
5575 __u32 src_ip6[4];5575 __u32 src_ip6[4];
5576 __u32 src_port; /* host byte order */5576 __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 */
5578 __u32 dst_ip4;5579 __u32 dst_ip4;
5579 __u32 dst_ip6[4];5580 __u32 dst_ip6[4];
5580 __u32 state;5581 __u32 state;
tools/testing/selftests/bpf/prog_tests/sock_fields.c+41−17
@@ -1,9 +1,11 @@
1// SPDX-License-Identifier: GPL-2.01// SPDX-License-Identifier: GPL-2.0
2/* Copyright (c) 2019 Facebook */2/* Copyright (c) 2019 Facebook */
3 3
4#define _GNU_SOURCE
4#include <netinet/in.h>5#include <netinet/in.h>
5#include <arpa/inet.h>6#include <arpa/inet.h>
6#include <unistd.h>7#include <unistd.h>
8#include <sched.h>
7#include <stdlib.h>9#include <stdlib.h>
8#include <string.h>10#include <string.h>
9#include <errno.h>11#include <errno.h>
@@ -20,6 +22,7 @@ · 10 unmodified lines
20enum bpf_linum_array_idx {22enum bpf_linum_array_idx {
21 EGRESS_LINUM_IDX,23 EGRESS_LINUM_IDX,
22 INGRESS_LINUM_IDX,24 INGRESS_LINUM_IDX,
25 READ_SK_DST_PORT_LINUM_IDX,
23 __NR_BPF_LINUM_ARRAY_IDX,26 __NR_BPF_LINUM_ARRAY_IDX,
24};27};
25 28
@@ -42,8 +45,16 @@ static __u64 child_cg_id; · 16 unmodified lines
42static int linum_map_fd;45static int linum_map_fd;
43static __u32 duration;46static __u32 duration;
44 47
45static __u32 egress_linum_idx = EGRESS_LINUM_IDX;48static bool create_netns(void)
46static __u32 ingress_linum_idx = INGRESS_LINUM_IDX;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}
47 58
48static void print_sk(const struct bpf_sock *sk, const char *prefix)59static void print_sk(const struct bpf_sock *sk, const char *prefix)
49{60{
@@ -91,19 +102,24 @@ static void check_result(void) · 41 unmodified lines
91{102{
92 struct bpf_tcp_sock srv_tp, cli_tp, listen_tp;103 struct bpf_tcp_sock srv_tp, cli_tp, listen_tp;
93 struct bpf_sock srv_sk, cli_sk, listen_sk;104 struct bpf_sock srv_sk, cli_sk, listen_sk;
94 __u32 ingress_linum, egress_linum;105 __u32 idx, ingress_linum, egress_linum, linum;
95 int err;106 int err;
96 107
97 err = bpf_map_lookup_elem(linum_map_fd, &egress_linum_idx,108 idx = EGRESS_LINUM_IDX;
98 &egress_linum);109 err = bpf_map_lookup_elem(linum_map_fd, &idx, &egress_linum);
99 CHECK(err < 0, "bpf_map_lookup_elem(linum_map_fd)",110 CHECK(err < 0, "bpf_map_lookup_elem(linum_map_fd)",
100 "err:%d errno:%d\n", err, errno);111 "err:%d errno:%d\n", err, errno);
101 112
102 err = bpf_map_lookup_elem(linum_map_fd, &ingress_linum_idx,113 idx = INGRESS_LINUM_IDX;
103 &ingress_linum);114 err = bpf_map_lookup_elem(linum_map_fd, &idx, &ingress_linum);
104 CHECK(err < 0, "bpf_map_lookup_elem(linum_map_fd)",115 CHECK(err < 0, "bpf_map_lookup_elem(linum_map_fd)",
105 "err:%d errno:%d\n", err, errno);116 "err:%d errno:%d\n", err, errno);
106 117
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
107 memcpy(&srv_sk, &skel->bss->srv_sk, sizeof(srv_sk));123 memcpy(&srv_sk, &skel->bss->srv_sk, sizeof(srv_sk));
108 memcpy(&srv_tp, &skel->bss->srv_tp, sizeof(srv_tp));124 memcpy(&srv_tp, &skel->bss->srv_tp, sizeof(srv_tp));
109 memcpy(&cli_sk, &skel->bss->cli_sk, sizeof(cli_sk));125 memcpy(&cli_sk, &skel->bss->cli_sk, sizeof(cli_sk));
@@ -262,7 +278,7 @@ static void test(void) · 152 unmodified lines
262 char buf[DATA_LEN];278 char buf[DATA_LEN];
263 279
264 /* Prepare listen_fd */280 /* 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);
266 /* start_server() has logged the error details */282 /* start_server() has logged the error details */
267 if (CHECK_FAIL(listen_fd == -1))283 if (CHECK_FAIL(listen_fd == -1))
268 goto done;284 goto done;
@@ -330,8 +346,12 @@ done: · 61 unmodified lines
330 346
331void serial_test_sock_fields(void)347void serial_test_sock_fields(void)
332{348{
333 struct bpf_link *egress_link = NULL, *ingress_link = NULL;
334 int parent_cg_fd = -1, child_cg_fd = -1;349 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;
335 355
336 /* Create a cgroup, get fd, and join it */356 /* Create a cgroup, get fd, and join it */
337 parent_cg_fd = test__join_cgroup(PARENT_CGROUP);357 parent_cg_fd = test__join_cgroup(PARENT_CGROUP);
@@ -352,15 +372,20 @@ void serial_test_sock_fields(void) · 14 unmodified lines
352 if (CHECK(!skel, "test_sock_fields__open_and_load", "failed\n"))372 if (CHECK(!skel, "test_sock_fields__open_and_load", "failed\n"))
353 goto done;373 goto done;
354 374
355 egress_link = bpf_program__attach_cgroup(skel->progs.egress_read_sock_fields,375 link = bpf_program__attach_cgroup(skel->progs.egress_read_sock_fields, child_cg_fd);
356 child_cg_fd);376 if (!ASSERT_OK_PTR(link, "attach_cgroup(egress_read_sock_fields)"))
357 if (!ASSERT_OK_PTR(egress_link, "attach_cgroup(egress)"))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)"))
358 goto done;382 goto done;
383 skel->links.ingress_read_sock_fields = link;
359 384
360 ingress_link = bpf_program__attach_cgroup(skel->progs.ingress_read_sock_fields,385 link = bpf_program__attach_cgroup(skel->progs.read_sk_dst_port, child_cg_fd);
361 child_cg_fd);386 if (!ASSERT_OK_PTR(link, "attach_cgroup(read_sk_dst_port"))
362 if (!ASSERT_OK_PTR(ingress_link, "attach_cgroup(ingress)"))
363 goto done;387 goto done;
388 skel->links.read_sk_dst_port = link;
364 389
365 linum_map_fd = bpf_map__fd(skel->maps.linum_map);390 linum_map_fd = bpf_map__fd(skel->maps.linum_map);
366 sk_pkt_out_cnt_fd = bpf_map__fd(skel->maps.sk_pkt_out_cnt);391 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
369 test();394 test();
370 395
371done:396done:
372 bpf_link__destroy(egress_link);397 test_sock_fields__detach(skel);
373 bpf_link__destroy(ingress_link);
374 test_sock_fields__destroy(skel);398 test_sock_fields__destroy(skel);
375 if (child_cg_fd >= 0)399 if (child_cg_fd >= 0)
376 close(child_cg_fd);400 close(child_cg_fd);
tools/testing/selftests/bpf/progs/test_sock_fields.c+41−0
@@ -12,6 +12,7 @@ · 11 unmodified lines
12enum bpf_linum_array_idx {12enum bpf_linum_array_idx {
13 EGRESS_LINUM_IDX,13 EGRESS_LINUM_IDX,
14 INGRESS_LINUM_IDX,14 INGRESS_LINUM_IDX,
15 READ_SK_DST_PORT_LINUM_IDX,
15 __NR_BPF_LINUM_ARRAY_IDX,16 __NR_BPF_LINUM_ARRAY_IDX,
16};17};
17 18
@@ -250,4 +251,44 @@ int ingress_read_sock_fields(struct __sk_buff *skb) · 232 unmodified lines
250 return CG_OK;251 return CG_OK;
251}252}
252 253
254static __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
260static __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
266static __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
272SEC("cgroup_skb/egress")
273int 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
253char _license[] SEC("license") = "GPL";294char _license[] SEC("license") = "GPL";
tools/testing/selftests/bpf/verifier/sock.c+78−3
@@ -121,7 +121,25 @@ · 120 unmodified lines
121 .result = ACCEPT,121 .result = ACCEPT,
122},122},
123{123{
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]",
125 .insns = {143 .insns = {
126 BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),144 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),145 BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
@@ -139,7 +157,64 @@ · 11 unmodified lines
139 .result = ACCEPT,157 .result = ACCEPT,
140},158},
141{159{
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)",
143 .insns = {218 .insns = {
144 BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),219 BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, offsetof(struct __sk_buff, sk)),
145 BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),220 BPF_JMP_IMM(BPF_JNE, BPF_REG_1, 0, 2),
@@ -149,7 +224,7 @@ · 3 unmodified lines
149 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),224 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
150 BPF_MOV64_IMM(BPF_REG_0, 0),225 BPF_MOV64_IMM(BPF_REG_0, 0),
151 BPF_EXIT_INSN(),226 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)),
153 BPF_MOV64_IMM(BPF_REG_0, 0),228 BPF_MOV64_IMM(BPF_REG_0, 0),
154 BPF_EXIT_INSN(),229 BPF_EXIT_INSN(),
155 },230 },