Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: add a test case to check verifier pointer arithmetic

With clang/llvm 4.0+, the test case is able to generate
the following pattern:
....
440: (b7) r1 = 15
441: (05) goto pc+73
515: (79) r6 = *(u64 *)(r10 -152)
516: (bf) r7 = r10
517: (07) r7 += -112
518: (bf) r2 = r7
519: (0f) r2 += r1
520: (71) r1 = *(u8 *)(r8 +0)
521: (73) *(u8 *)(r2 +45) = r1
....

commit 332270fdc8b6 ("bpf: enhance verifier to understand stack
pointer arithmetic") improved verifier to handle such a pattern.
This patch adds a C test case to actually generate such a pattern.
A dummy tracepoint interface is used to load the program
into the kernel.

Signed-off-by: Yonghong Song <yhs@fb.com>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Yonghong Song and committed by
David S. Miller
6ead18fb 4d463c4d

+275 -1
+1 -1
tools/testing/selftests/bpf/Makefile
··· 13 13 14 14 TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs 15 15 16 - TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o 16 + TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o 17 17 18 18 TEST_PROGS := test_kmod.sh 19 19
+16
tools/testing/selftests/bpf/test_progs.c
··· 268 268 bpf_object__close(obj); 269 269 } 270 270 271 + static void test_tcp_estats(void) 272 + { 273 + const char *file = "./test_tcp_estats.o"; 274 + int err, prog_fd; 275 + struct bpf_object *obj; 276 + __u32 duration = 0; 277 + 278 + err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd); 279 + CHECK(err, "", "err %d errno %d\n", err, errno); 280 + if (err) 281 + return; 282 + 283 + bpf_object__close(obj); 284 + } 285 + 271 286 int main(void) 272 287 { 273 288 struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; ··· 292 277 test_pkt_access(); 293 278 test_xdp(); 294 279 test_l4lb(); 280 + test_tcp_estats(); 295 281 296 282 printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); 297 283 return 0;
+258
tools/testing/selftests/bpf/test_tcp_estats.c
··· 1 + /* Copyright (c) 2017 Facebook 2 + * 3 + * This program is free software; you can redistribute it and/or 4 + * modify it under the terms of version 2 of the GNU General Public 5 + * License as published by the Free Software Foundation. 6 + */ 7 + 8 + /* This program shows clang/llvm is able to generate code pattern 9 + * like: 10 + * _tcp_send_active_reset: 11 + * 0: bf 16 00 00 00 00 00 00 r6 = r1 12 + * ...... 13 + * 335: b7 01 00 00 0f 00 00 00 r1 = 15 14 + * 336: 05 00 48 00 00 00 00 00 goto 72 15 + * 16 + * LBB0_3: 17 + * 337: b7 01 00 00 01 00 00 00 r1 = 1 18 + * 338: 63 1a d0 ff 00 00 00 00 *(u32 *)(r10 - 48) = r1 19 + * 408: b7 01 00 00 03 00 00 00 r1 = 3 20 + * 21 + * LBB0_4: 22 + * 409: 71 a2 fe ff 00 00 00 00 r2 = *(u8 *)(r10 - 2) 23 + * 410: bf a7 00 00 00 00 00 00 r7 = r10 24 + * 411: 07 07 00 00 b8 ff ff ff r7 += -72 25 + * 412: bf 73 00 00 00 00 00 00 r3 = r7 26 + * 413: 0f 13 00 00 00 00 00 00 r3 += r1 27 + * 414: 73 23 2d 00 00 00 00 00 *(u8 *)(r3 + 45) = r2 28 + * 29 + * From the above code snippet, the code generated by the compiler 30 + * is reasonable. The "r1" is assigned to different values in basic 31 + * blocks "_tcp_send_active_reset" and "LBB0_3", and used in "LBB0_4". 32 + * The verifier should be able to handle such code patterns. 33 + */ 34 + #include <string.h> 35 + #include <linux/bpf.h> 36 + #include <linux/ipv6.h> 37 + #include <linux/version.h> 38 + #include <sys/socket.h> 39 + #include "bpf_helpers.h" 40 + 41 + #define _(P) ({typeof(P) val = 0; bpf_probe_read(&val, sizeof(val), &P); val;}) 42 + #define TCP_ESTATS_MAGIC 0xBAADBEEF 43 + 44 + /* This test case needs "sock" and "pt_regs" data structure. 45 + * Recursively, "sock" needs "sock_common" and "inet_sock". 46 + * However, this is a unit test case only for 47 + * verifier purpose without bpf program execution. 48 + * We can safely mock much simpler data structures, basically 49 + * only taking the necessary fields from kernel headers. 50 + */ 51 + typedef __u32 __bitwise __portpair; 52 + typedef __u64 __bitwise __addrpair; 53 + 54 + struct sock_common { 55 + unsigned short skc_family; 56 + union { 57 + __addrpair skc_addrpair; 58 + struct { 59 + __be32 skc_daddr; 60 + __be32 skc_rcv_saddr; 61 + }; 62 + }; 63 + union { 64 + __portpair skc_portpair; 65 + struct { 66 + __be16 skc_dport; 67 + __u16 skc_num; 68 + }; 69 + }; 70 + struct in6_addr skc_v6_daddr; 71 + struct in6_addr skc_v6_rcv_saddr; 72 + }; 73 + 74 + struct sock { 75 + struct sock_common __sk_common; 76 + #define sk_family __sk_common.skc_family 77 + #define sk_v6_daddr __sk_common.skc_v6_daddr 78 + #define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr 79 + }; 80 + 81 + struct inet_sock { 82 + struct sock sk; 83 + #define inet_daddr sk.__sk_common.skc_daddr 84 + #define inet_dport sk.__sk_common.skc_dport 85 + __be32 inet_saddr; 86 + __be16 inet_sport; 87 + }; 88 + 89 + struct pt_regs { 90 + long di; 91 + }; 92 + 93 + static inline struct inet_sock *inet_sk(const struct sock *sk) 94 + { 95 + return (struct inet_sock *)sk; 96 + } 97 + 98 + /* Define various data structures for state recording. 99 + * Some fields are not used due to test simplification. 100 + */ 101 + enum tcp_estats_addrtype { 102 + TCP_ESTATS_ADDRTYPE_IPV4 = 1, 103 + TCP_ESTATS_ADDRTYPE_IPV6 = 2 104 + }; 105 + 106 + enum tcp_estats_event_type { 107 + TCP_ESTATS_ESTABLISH, 108 + TCP_ESTATS_PERIODIC, 109 + TCP_ESTATS_TIMEOUT, 110 + TCP_ESTATS_RETRANSMIT_TIMEOUT, 111 + TCP_ESTATS_RETRANSMIT_OTHER, 112 + TCP_ESTATS_SYN_RETRANSMIT, 113 + TCP_ESTATS_SYNACK_RETRANSMIT, 114 + TCP_ESTATS_TERM, 115 + TCP_ESTATS_TX_RESET, 116 + TCP_ESTATS_RX_RESET, 117 + TCP_ESTATS_WRITE_TIMEOUT, 118 + TCP_ESTATS_CONN_TIMEOUT, 119 + TCP_ESTATS_ACK_LATENCY, 120 + TCP_ESTATS_NEVENTS, 121 + }; 122 + 123 + struct tcp_estats_event { 124 + int pid; 125 + int cpu; 126 + unsigned long ts; 127 + unsigned int magic; 128 + enum tcp_estats_event_type event_type; 129 + }; 130 + 131 + /* The below data structure is packed in order for 132 + * llvm compiler to generate expected code. 133 + */ 134 + struct tcp_estats_conn_id { 135 + unsigned int localaddressType; 136 + struct { 137 + unsigned char data[16]; 138 + } localaddress; 139 + struct { 140 + unsigned char data[16]; 141 + } remaddress; 142 + unsigned short localport; 143 + unsigned short remport; 144 + } __attribute__((__packed__)); 145 + 146 + struct tcp_estats_basic_event { 147 + struct tcp_estats_event event; 148 + struct tcp_estats_conn_id conn_id; 149 + }; 150 + 151 + struct bpf_map_def SEC("maps") ev_record_map = { 152 + .type = BPF_MAP_TYPE_HASH, 153 + .key_size = sizeof(__u32), 154 + .value_size = sizeof(struct tcp_estats_basic_event), 155 + .max_entries = 1024, 156 + }; 157 + 158 + struct dummy_tracepoint_args { 159 + unsigned long long pad; 160 + struct sock *sock; 161 + }; 162 + 163 + static __always_inline void tcp_estats_ev_init(struct tcp_estats_event *event, 164 + enum tcp_estats_event_type type) 165 + { 166 + event->magic = TCP_ESTATS_MAGIC; 167 + event->ts = bpf_ktime_get_ns(); 168 + event->event_type = type; 169 + } 170 + 171 + static __always_inline void unaligned_u32_set(unsigned char *to, __u8 *from) 172 + { 173 + to[0] = _(from[0]); 174 + to[1] = _(from[1]); 175 + to[2] = _(from[2]); 176 + to[3] = _(from[3]); 177 + } 178 + 179 + static __always_inline void conn_id_ipv4_init(struct tcp_estats_conn_id *conn_id, 180 + __be32 *saddr, __be32 *daddr) 181 + { 182 + conn_id->localaddressType = TCP_ESTATS_ADDRTYPE_IPV4; 183 + 184 + unaligned_u32_set(conn_id->localaddress.data, (__u8 *)saddr); 185 + unaligned_u32_set(conn_id->remaddress.data, (__u8 *)daddr); 186 + } 187 + 188 + static __always_inline void conn_id_ipv6_init(struct tcp_estats_conn_id *conn_id, 189 + __be32 *saddr, __be32 *daddr) 190 + { 191 + conn_id->localaddressType = TCP_ESTATS_ADDRTYPE_IPV6; 192 + 193 + unaligned_u32_set(conn_id->localaddress.data, (__u8 *)saddr); 194 + unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32), 195 + (__u8 *)(saddr + 1)); 196 + unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32) * 2, 197 + (__u8 *)(saddr + 2)); 198 + unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32) * 3, 199 + (__u8 *)(saddr + 3)); 200 + 201 + unaligned_u32_set(conn_id->remaddress.data, 202 + (__u8 *)(daddr)); 203 + unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32), 204 + (__u8 *)(daddr + 1)); 205 + unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32) * 2, 206 + (__u8 *)(daddr + 2)); 207 + unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32) * 3, 208 + (__u8 *)(daddr + 3)); 209 + } 210 + 211 + static __always_inline void tcp_estats_conn_id_init(struct tcp_estats_conn_id *conn_id, 212 + struct sock *sk) 213 + { 214 + conn_id->localport = _(inet_sk(sk)->inet_sport); 215 + conn_id->remport = _(inet_sk(sk)->inet_dport); 216 + 217 + if (_(sk->sk_family) == AF_INET6) 218 + conn_id_ipv6_init(conn_id, 219 + sk->sk_v6_rcv_saddr.s6_addr32, 220 + sk->sk_v6_daddr.s6_addr32); 221 + else 222 + conn_id_ipv4_init(conn_id, 223 + &inet_sk(sk)->inet_saddr, 224 + &inet_sk(sk)->inet_daddr); 225 + } 226 + 227 + static __always_inline void tcp_estats_init(struct sock *sk, 228 + struct tcp_estats_event *event, 229 + struct tcp_estats_conn_id *conn_id, 230 + enum tcp_estats_event_type type) 231 + { 232 + tcp_estats_ev_init(event, type); 233 + tcp_estats_conn_id_init(conn_id, sk); 234 + } 235 + 236 + static __always_inline void send_basic_event(struct sock *sk, 237 + enum tcp_estats_event_type type) 238 + { 239 + struct tcp_estats_basic_event ev; 240 + __u32 key = bpf_get_prandom_u32(); 241 + 242 + memset(&ev, 0, sizeof(ev)); 243 + tcp_estats_init(sk, &ev.event, &ev.conn_id, type); 244 + bpf_map_update_elem(&ev_record_map, &key, &ev, BPF_ANY); 245 + } 246 + 247 + SEC("dummy_tracepoint") 248 + int _dummy_tracepoint(struct dummy_tracepoint_args *arg) 249 + { 250 + if (!arg->sock) 251 + return 0; 252 + 253 + send_basic_event(arg->sock, TCP_ESTATS_TX_RESET); 254 + return 0; 255 + } 256 + 257 + char _license[] SEC("license") = "GPL"; 258 + __u32 _version SEC("version") = 1; /* ignored by tracepoints, required by libbpf.a */