1 /*
2 * Check bpf(BPF_OBJ_GET_INFO_BY_FD) decoding.
3 *
4 * Copyright (c) 2018 The strace developers.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "tests.h"
31
32 #ifndef CHECK_OBJ_PROG
33 # define CHECK_OBJ_PROG 0
34 #endif
35
36 #include <inttypes.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdint.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <sys/sysmacros.h>
43 #include <asm/unistd.h>
44
45 #include "print_fields.h"
46 #include "scno.h"
47
48 #ifdef HAVE_LINUX_BPF_H
49 # include <linux/bpf.h>
50 #endif
51
52 #include "bpf_attr.h"
53
54 #include "xlat.h"
55 #include "xlat/bpf_map_flags.h"
56 #include "xlat/bpf_map_types.h"
57 #include "xlat/bpf_prog_types.h"
58
59 #define XLAT_MACROS_ONLY
60 #include "xlat/bpf_commands.h"
61 #include "xlat/bpf_op_alu.h"
62 #include "xlat/bpf_op_jmp.h"
63 #include "xlat/bpf_size.h"
64 #include "xlat/bpf_src.h"
65 #include "xlat/ebpf_class.h"
66 #include "xlat/ebpf_mode.h"
67 #include "xlat/ebpf_op_alu.h"
68 #include "xlat/ebpf_regs.h"
69 #include "xlat/ebpf_size.h"
70
71 #ifndef HAVE_STRUCT_BPF_INSN
72 struct bpf_insn {
73 uint8_t code;
74 uint8_t dst_reg:4;
75 uint8_t src_reg:4;
76 int16_t off;
77 int32_t imm;
78 };
79 #endif
80
81 static const char *errstr;
82
83 static long
sys_bpf(kernel_ulong_t cmd,void * attr,kernel_ulong_t size)84 sys_bpf(kernel_ulong_t cmd, void *attr, kernel_ulong_t size)
85 {
86 long rc = syscall(__NR_bpf, cmd, attr, size);
87 errstr = sprintrc(rc);
88 return rc;
89 }
90
91 static void
print_map_create(void * attr_void,size_t size,long rc)92 print_map_create(void *attr_void, size_t size, long rc)
93 {
94 /* struct BPF_MAP_CREATE_struct *attr = attr_void; */
95
96 printf("bpf(BPF_MAP_CREATE, {map_type=BPF_MAP_TYPE_ARRAY, key_size=4"
97 ", value_size=8, max_entries=1");
98 if (size > offsetof(struct BPF_MAP_CREATE_struct, map_flags))
99 printf(", map_flags=0");
100 if (size > offsetof(struct BPF_MAP_CREATE_struct, inner_map_fd))
101 printf(", inner_map_fd=0</dev/null>");
102 if (size > offsetof(struct BPF_MAP_CREATE_struct, map_name))
103 printf(", map_name=\"test_map\"");
104 if (size > offsetof(struct BPF_MAP_CREATE_struct, map_ifindex))
105 printf(", map_ifindex=0");
106 printf("}, %zu) = ", size);
107 if (rc >= 0)
108 printf("%ld<anon_inode:bpf-map>\n", rc);
109 else
110 puts(errstr);
111 }
112
113 #if CHECK_OBJ_PROG
114 static struct bpf_insn socket_prog[] = {
115 { /* 0 */
116 .code = BPF_ALU64 | BPF_K | BPF_MOV,
117 .dst_reg = BPF_REG_1,
118 .imm = 0,
119 },
120 { /* 1 */
121 .code = BPF_STX | BPF_W | BPF_MEM,
122 .dst_reg = BPF_REG_10,
123 .src_reg = BPF_REG_1,
124 .off = -4,
125 },
126 { /* 2 */
127 .code = BPF_ALU64 | BPF_X | BPF_MOV,
128 .dst_reg = BPF_REG_2,
129 .src_reg = BPF_REG_10,
130 },
131 { /* 3 */
132 .code = BPF_ALU64 | BPF_K | BPF_ADD,
133 .dst_reg = BPF_REG_2,
134 .imm = -4,
135 },
136 { /* 4 */
137 .code = BPF_LD | BPF_DW | BPF_IMM,
138 .dst_reg = BPF_REG_1,
139 .src_reg = 1 /* BPF_PSEUDO_MAP_FD */,
140 .imm = 0, /* to be set to map fd */
141 },
142 { /* 5 */
143 .imm = 0,
144 },
145 { /* 6 */
146 .code = BPF_JMP | BPF_K | BPF_CALL,
147 .imm = 0x1, /* BPF_FUNC_map_lookup_elem */
148 },
149 { /* 7 */
150 .code = BPF_ALU64 | BPF_K | BPF_MOV,
151 .dst_reg = BPF_REG_0,
152 .imm = 0,
153 },
154 { /* 8 */
155 .code = BPF_JMP | BPF_K | BPF_EXIT,
156 },
157 };
158
159 # if VERBOSE
160 static const char *socket_prog_fmt =
161 "[{code=BPF_ALU64|BPF_K|BPF_MOV"
162 ", dst_reg=BPF_REG_1, src_reg=BPF_REG_0, off=0, imm=0}"
163 ", {code=BPF_STX|BPF_W|BPF_MEM"
164 ", dst_reg=BPF_REG_10, src_reg=BPF_REG_1, off=-4, imm=0}"
165 ", {code=BPF_ALU64|BPF_X|BPF_MOV"
166 ", dst_reg=BPF_REG_2, src_reg=BPF_REG_10, off=0, imm=0}"
167 ", {code=BPF_ALU64|BPF_K|BPF_ADD"
168 ", dst_reg=BPF_REG_2, src_reg=BPF_REG_0, off=0, imm=0xfffffffc}"
169 ", {code=BPF_LD|BPF_DW|BPF_IMM"
170 ", dst_reg=BPF_REG_1, src_reg=BPF_REG_1, off=0, imm=%#x}"
171 ", {code=BPF_LD|BPF_W|BPF_IMM"
172 ", dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0}"
173 ", {code=BPF_JMP|BPF_K|BPF_CALL"
174 ", dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0x1}"
175 ", {code=BPF_ALU64|BPF_K|BPF_MOV"
176 ", dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0}"
177 ", {code=BPF_JMP|BPF_K|BPF_EXIT"
178 ", dst_reg=BPF_REG_0, src_reg=BPF_REG_0, off=0, imm=0}"
179 "]";
180 # endif /* VERBOSE */
181
182 static const char *license = "BSD";
183 static char log_buf[4096];
184
185 static void
print_prog_load(void * attr_void,size_t size,long rc)186 print_prog_load(void *attr_void, size_t size, long rc)
187 {
188 printf("bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_SOCKET_FILTER"
189 ", insn_cnt=%zu, insns=", ARRAY_SIZE(socket_prog));
190 # if VERBOSE
191 printf(socket_prog_fmt, socket_prog[4].imm);
192 # else
193 printf("%p", socket_prog);
194 # endif
195 if (size > offsetof(struct BPF_PROG_LOAD_struct, license))
196 printf(", license=\"BSD\"");
197 if (size > offsetof(struct BPF_PROG_LOAD_struct, log_buf))
198 printf(", log_level=42, log_size=%zu, log_buf=\"\"",
199 sizeof(log_buf));
200 if (size > offsetof(struct BPF_PROG_LOAD_struct, kern_version))
201 printf(", kern_version=KERNEL_VERSION(57005, 192, 222)");
202 if (size > offsetof(struct BPF_PROG_LOAD_struct, prog_flags))
203 printf(", prog_flags=0");
204 if (size > offsetof(struct BPF_PROG_LOAD_struct, prog_name))
205 printf(", prog_name=\"test_prog\"");
206 if (size > offsetof(struct BPF_PROG_LOAD_struct, prog_ifindex))
207 printf(", prog_ifindex=0");
208 if (size > offsetof(struct BPF_PROG_LOAD_struct, expected_attach_type))
209 printf(", expected_attach_type=BPF_CGROUP_INET_INGRESS");
210 printf("}, %zu) = ", size);
211 if (rc >= 0)
212 printf("%ld<anon_inode:bpf-prog>\n", rc);
213 else
214 puts(errstr);
215 }
216 #endif /* CHECK_OBJ_PROG */
217
218 static long
try_bpf(kernel_ulong_t cmd,void (* printer)(void * attr,size_t size,long rc),void * attr,size_t ** sizes)219 try_bpf(kernel_ulong_t cmd, void (*printer)(void *attr, size_t size, long rc),
220 void *attr, size_t **sizes)
221 {
222 long rc;
223
224 for (rc = -1; **sizes; (*sizes)++) {
225 rc = sys_bpf(cmd, attr, **sizes);
226 printer(attr, **sizes, rc);
227
228 if (rc >= 0)
229 break;
230 }
231
232 return rc;
233 }
234
235 int
main(void)236 main(void)
237 {
238 struct BPF_MAP_CREATE_struct bpf_map_create_attr = {
239 .map_type = BPF_MAP_TYPE_ARRAY,
240 .key_size = 4,
241 .value_size = 8,
242 .max_entries = 1,
243 .map_name = "test_map",
244 };
245 size_t bpf_map_create_attr_sizes[] = {
246 sizeof(bpf_map_create_attr),
247 offsetofend(struct BPF_MAP_CREATE_struct, max_entries),
248 0,
249 };
250
251 #if CHECK_OBJ_PROG
252 struct BPF_PROG_LOAD_struct bpf_prog_load_attr = {
253 .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
254 .insn_cnt = ARRAY_SIZE(socket_prog),
255 .insns = (uintptr_t) socket_prog,
256 .license = (uintptr_t) license,
257 .log_level = 42,
258 .log_size = sizeof(log_buf),
259 .log_buf = (uintptr_t) log_buf,
260 .kern_version = 0xdeadc0de,
261 .prog_name = "test_prog",
262 };
263 size_t bpf_prog_load_attr_sizes[] = {
264 sizeof(bpf_prog_load_attr),
265 offsetofend(struct BPF_PROG_LOAD_struct, prog_name),
266 offsetofend(struct BPF_PROG_LOAD_struct, prog_flags),
267 offsetofend(struct BPF_PROG_LOAD_struct, kern_version),
268 offsetofend(struct BPF_PROG_LOAD_struct, log_buf),
269 offsetofend(struct BPF_PROG_LOAD_struct, license),
270 offsetofend(struct BPF_PROG_LOAD_struct, insns),
271 0,
272 };
273 #endif /* CHECK_OBJ_PROG */
274
275 size_t *bpf_map_create_attr_size = bpf_map_create_attr_sizes;
276 int map_fd = try_bpf(BPF_MAP_CREATE, print_map_create,
277 &bpf_map_create_attr, &bpf_map_create_attr_size);
278 if (map_fd < 0)
279 perror_msg_and_skip("BPF_MAP_CREATE failed");
280
281 #if CHECK_OBJ_PROG
282 socket_prog[4].imm = map_fd;
283
284 size_t *bpf_prog_load_attr_size = bpf_prog_load_attr_sizes;
285 int prog_fd = try_bpf(BPF_PROG_LOAD, print_prog_load,
286 &bpf_prog_load_attr, &bpf_prog_load_attr_size);
287 if (prog_fd < 0)
288 perror_msg_and_skip("BPF_PROG_LOAD failed (log: \"%s\")",
289 log_buf);
290 #endif /* CHECK_OBJ_PROG */
291
292 /*
293 * This has to be a macro, otherwise the compiler complains that
294 * initializer element is not constant.
295 */
296 #define MAP_INFO_SZ (sizeof(*map_info) + 64)
297 struct bpf_map_info_struct *map_info = calloc(1, MAP_INFO_SZ);
298 struct BPF_OBJ_GET_INFO_BY_FD_struct bpf_map_get_info_attr = {
299 .bpf_fd = map_fd,
300 .info_len = MAP_INFO_SZ,
301 .info = (uintptr_t) map_info,
302 };
303
304 int ret = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &bpf_map_get_info_attr,
305 sizeof(bpf_map_get_info_attr));
306 if (ret < 0)
307 perror_msg_and_skip("BPF_OBJ_GET_INFO_BY_FD map failed");
308
309 printf("bpf(BPF_OBJ_GET_INFO_BY_FD"
310 ", {info={bpf_fd=%d<anon_inode:bpf-map>, info_len=%zu",
311 map_fd, MAP_INFO_SZ);
312 if (bpf_map_get_info_attr.info_len != MAP_INFO_SZ)
313 printf(" => %u", bpf_map_get_info_attr.info_len);
314
315 printf(", info=");
316 #if VERBOSE
317 printf("{type=");
318 printxval(bpf_map_types, map_info->type, "BPF_MAP_TYPE_???");
319 PRINT_FIELD_U(", ", *map_info, id);
320 PRINT_FIELD_U(", ", *map_info, key_size);
321 PRINT_FIELD_U(", ", *map_info, value_size);
322 PRINT_FIELD_U(", ", *map_info, max_entries);
323 printf(", map_flags=");
324 printflags(bpf_map_flags, map_info->map_flags, "BPF_F_???");
325
326 if (bpf_map_get_info_attr.info_len >
327 offsetof(struct bpf_map_info_struct, name)) {
328 printf(", name=");
329 print_quoted_cstring(map_info->name, sizeof(map_info->name));
330 }
331 if (bpf_map_get_info_attr.info_len >
332 offsetof(struct bpf_map_info_struct, ifindex))
333 printf(", ifindex=%u", map_info->ifindex);
334 if (bpf_map_get_info_attr.info_len >
335 offsetof(struct bpf_map_info_struct, netns_dev))
336 printf(", netns_dev=makedev(%u, %u)",
337 major(map_info->netns_dev), minor(map_info->netns_dev));
338 if (bpf_map_get_info_attr.info_len >
339 offsetof(struct bpf_map_info_struct, netns_ino))
340 printf(", netns_ino=%" PRIu64, map_info->netns_ino);
341 printf("}");
342 #else /* !VERBOSE */
343 printf("%p", map_info);
344 #endif /* VERBOSE */
345 printf("}}, %zu) = %s\n", sizeof(bpf_map_get_info_attr), errstr);
346
347 #if CHECK_OBJ_PROG
348 /*
349 * This has to be a macro, otherwise the compiler complains that
350 * initializer element is not constant.
351 */
352 #define PROG_INFO_SZ (sizeof(*prog_info) + 64)
353 struct bpf_prog_info_struct *prog_info = calloc(1, PROG_INFO_SZ);
354 struct bpf_insn *xlated_prog = tail_alloc(sizeof(*xlated_prog) * 42);
355 uint32_t *map_ids = tail_alloc(sizeof(*map_ids) * 2);
356 struct BPF_OBJ_GET_INFO_BY_FD_struct bpf_prog_get_info_attr = {
357 .bpf_fd = prog_fd,
358 .info_len = PROG_INFO_SZ,
359 .info = (uintptr_t) prog_info,
360 };
361 size_t old_prog_info_len = PROG_INFO_SZ;
362
363 for (unsigned int i = 0; i < 4; i++) {
364 prog_info->jited_prog_len = 0;
365 switch (i) {
366 case 1:
367 prog_info->xlated_prog_insns =
368 (uintptr_t) (xlated_prog + 42);
369 prog_info->xlated_prog_len = 336;
370 prog_info->map_ids = (uintptr_t) (map_ids + 2);
371 prog_info->nr_map_ids = 2;
372 break;
373 case 2:
374 prog_info->xlated_prog_insns = (uintptr_t) xlated_prog;
375 /* TODO: check xlated_prog output */
376 prog_info->xlated_prog_len = 0;
377 prog_info->map_ids = (uintptr_t) map_ids;
378 prog_info->nr_map_ids = 0;
379 break;
380 case 3:
381 prog_info->xlated_prog_insns = (uintptr_t) xlated_prog;
382 prog_info->xlated_prog_len = 0;
383 prog_info->map_ids = (uintptr_t) map_ids;
384 prog_info->nr_map_ids = 2;
385 break;
386 }
387
388 ret = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &bpf_prog_get_info_attr,
389 sizeof(bpf_prog_get_info_attr));
390 if (i != 1 && ret < 0)
391 perror_msg_and_skip("BPF_OBJ_GET_INFO_BY_FD"
392 " prog %u failed", i);
393
394 printf("bpf(BPF_OBJ_GET_INFO_BY_FD"
395 ", {info={bpf_fd=%d<anon_inode:bpf-prog>, info_len=%zu",
396 prog_fd, old_prog_info_len);
397 if (!i && bpf_prog_get_info_attr.info_len != PROG_INFO_SZ)
398 printf(" => %u", bpf_prog_get_info_attr.info_len);
399 old_prog_info_len = bpf_prog_get_info_attr.info_len;
400
401 printf(", info=");
402 # if VERBOSE
403 printf("{type=");
404 printxval(bpf_prog_types, prog_info->type, "BPF_PROG_TYPE_???");
405 PRINT_FIELD_U(", ", *prog_info, id);
406 printf(", tag=");
407 print_quoted_hex(prog_info->tag, sizeof(prog_info->tag));
408 printf(", jited_prog_len=0");
409 if (prog_info->jited_prog_len)
410 printf(" => %u", prog_info->jited_prog_len);
411 printf(", jited_prog_insns=NULL");
412 switch (i) {
413 case 0:
414 printf(", xlated_prog_len=0");
415 if (prog_info->xlated_prog_len)
416 printf(" => %u", prog_info->xlated_prog_len);
417 printf(", xlated_prog_insns=NULL");
418 break;
419 case 1:
420 printf(", xlated_prog_len=336");
421 if (prog_info->xlated_prog_len != 336)
422 printf(" => %u", prog_info->xlated_prog_len);
423 if (prog_info->xlated_prog_len)
424 printf(", xlated_prog_insns=%p", xlated_prog + 42);
425 else
426 printf(", xlated_prog_insns=[]");
427 break;
428 case 2:
429 case 3:
430 printf(", xlated_prog_len=0");
431 if (prog_info->xlated_prog_len)
432 printf(" => %u", prog_info->xlated_prog_len);
433 printf(", xlated_prog_insns=[]");
434 break;
435 }
436
437 if (bpf_prog_get_info_attr.info_len >
438 offsetof(struct bpf_prog_info_struct, load_time))
439 printf(", load_time=%" PRIu64, prog_info->load_time);
440 if (bpf_prog_get_info_attr.info_len >
441 offsetof(struct bpf_prog_info_struct, created_by_uid))
442 printf(", created_by_uid=%u",
443 prog_info->created_by_uid);
444
445 if (bpf_prog_get_info_attr.info_len >
446 offsetof(struct bpf_prog_info_struct, map_ids)) {
447 switch (i) {
448 case 0:
449 printf(", nr_map_ids=0");
450 if (prog_info->nr_map_ids)
451 printf(" => 1");
452 printf(", map_ids=NULL");
453 break;
454 case 1:
455 printf(", nr_map_ids=2, map_ids=%p",
456 map_ids + 2);
457 break;
458 case 2:
459 printf(", nr_map_ids=0");
460 if (prog_info->nr_map_ids)
461 printf(" => 1");
462 printf(", map_ids=[]");
463 break;
464 case 3:
465 printf(", nr_map_ids=2");
466 if (prog_info->nr_map_ids != 2)
467 printf(" => 1");
468 printf(", map_ids=[%u]", map_info->id);
469 break;
470 }
471 }
472
473 if (bpf_prog_get_info_attr.info_len >
474 offsetof(struct bpf_prog_info_struct, name))
475 printf(", name=\"test_prog\"");
476 if (bpf_prog_get_info_attr.info_len >
477 offsetof(struct bpf_prog_info_struct, ifindex))
478 printf(", ifindex=%u", prog_info->ifindex);
479 if (bpf_prog_get_info_attr.info_len >
480 offsetof(struct bpf_prog_info_struct, netns_dev))
481 printf(", netns_dev=makedev(%u, %u)",
482 major(prog_info->netns_dev),
483 minor(prog_info->netns_dev));
484 if (bpf_prog_get_info_attr.info_len >
485 offsetof(struct bpf_prog_info_struct, netns_ino))
486 printf(", netns_ino=%" PRIu64, prog_info->netns_ino);
487
488 printf("}");
489 # else /* !VERBOSE */
490 printf("%p", prog_info);
491 # endif /* VERBOSE */
492 printf("}}, %zu) = %s\n",
493 sizeof(bpf_prog_get_info_attr), errstr);
494 }
495 #endif /* CHECK_OBJ_PROG */
496
497 puts("+++ exited with 0 +++");
498 return 0;
499 }
500