Skip to content

Commit 8f50f16

Browse files
jsitnickiAlexei Starovoitov
authored andcommitted
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. Signed-off-by: Jakub Sitnicki <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 4421a58 commit 8f50f16

File tree

4 files changed

+162
-21
lines changed

4 files changed

+162
-21
lines changed

tools/include/uapi/linux/bpf.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5574,7 +5574,8 @@ struct bpf_sock {
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

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -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 @@
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;
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)
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)
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 @@ static void test(void)
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)
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)
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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
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)
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

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,25 @@
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 @@
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 @@
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
},

0 commit comments

Comments
 (0)