Pull bpf updates from Alexei Starovoitov: - Add BPF uprobe session support (Jiri Olsa) - Optimize uprobe performance (Andrii Nakryiko) - Add bpf_fastcall support to helpers and kfuncs (Eduard Zingerman) - Avoid calling free_htab_elem() under hash map bucket lock (Hou Tao) - Prevent tailcall infinite loop caused by freplace (Leon Hwang) - Mark raw_tracepoint arguments as nullable (Kumar Kartikeya Dwivedi) - Introduce uptr support in the task local storage map (Martin KaFai Lau) - Stringify errno log messages in libbpf (Mykyta Yatsenko) - Add kmem_cache BPF iterator for perf's lock profiling (Namhyung Kim) - Support BPF objects of either endianness in libbpf (Tony Ambardar) - Add ksym to struct_ops trampoline to fix stack trace (Xu Kuohai) - Introduce private stack for eligible BPF programs (Yonghong Song) - Migrate samples/bpf tests to selftests/bpf test_progs (Daniel T. Lee) - Migrate test_sock to selftests/bpf test_progs (Jordan Rife) * tag 'bpf-next-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (152 commits) libbpf: Change hash_combine parameters from long to unsigned long selftests/bpf: Fix build error with llvm 19 libbpf: Fix memory leak in bpf_program__attach_uprobe_multi bpf: use common instruction history across all states bpf: Add necessary migrate_disable to range_tree. bpf: Do not alloc arena on unsupported arches selftests/bpf: Set test path for token/obj_priv_implicit_token_envvar selftests/bpf: Add a test for arena range tree algorithm bpf: Introduce range_tree data structure and use it in bpf arena samples/bpf: Remove unused variable in xdp2skb_meta_kern.c samples/bpf: Remove unused variables in tc_l2_redirect_kern.c bpftool: Cast variable `var` to long long bpf, x86: Propagate tailcall info only for subprogs bpf: Add kernel symbol for struct_ops trampoline bpf: Use function pointers count as struct_ops links count bpf: Remove unused member rcu from bpf_struct_ops_map selftests/bpf: Add struct_ops prog private stack tests bpf: Support private stack for struct_ops progs selftests/bpf: Add tracing prog private stack tests bpf, x86: Support private stack in jit ...
Unified · Split
| @@ -835,7 +835,7 @@ section named by ``btf_ext_info_sec->sec_name_off``. · 834 unmodified lines | ||
| 835 | 835 | See :ref:`Documentation/bpf/llvm_reloc.rst <btf-co-re-relocations>` |
| 836 | 836 | for more information on CO-RE relocations. |
| 837 | 837 | |
| 838 | −4.2 .BTF_ids section | |
| 838 | +4.3 .BTF_ids section | |
| 839 | 839 | -------------------- |
| 840 | 840 | |
| 841 | 841 | The .BTF_ids section encodes BTF ID values that are used within the kernel. |
| @@ -896,6 +896,81 @@ and is used as a filter when resolving the BTF ID value. · 54 unmodified lines | ||
| 896 | 896 | All the BTF ID lists and sets are compiled in the .BTF_ids section and |
| 897 | 897 | resolved during the linking phase of kernel build by ``resolve_btfids`` tool. |
| 898 | 898 | |
| 899 | +4.4 .BTF.base section | |
| 900 | +--------------------- | |
| 901 | +Split BTF - where the .BTF section only contains types not in the associated | |
| 902 | +base .BTF section - is an extremely efficient way to encode type information | |
| 903 | +for kernel modules, since they generally consist of a few module-specific | |
| 904 | +types along with a large set of shared kernel types. The former are encoded | |
| 905 | +in split BTF, while the latter are encoded in base BTF, resulting in more | |
| 906 | +compact representations. A type in split BTF that refers to a type in | |
| 907 | +base BTF refers to it using its base BTF ID, and split BTF IDs start | |
| 908 | +at last_base_BTF_ID + 1. | |
| 909 | + | |
| 910 | +The downside of this approach however is that this makes the split BTF | |
| 911 | +somewhat brittle - when the base BTF changes, base BTF ID references are | |
| 912 | +no longer valid and the split BTF itself becomes useless. The role of the | |
| 913 | +.BTF.base section is to make split BTF more resilient for cases where | |
| 914 | +the base BTF may change, as is the case for kernel modules not built every | |
| 915 | +time the kernel is for example. .BTF.base contains named base types; INTs, | |
| 916 | +FLOATs, STRUCTs, UNIONs, ENUM[64]s and FWDs. INTs and FLOATs are fully | |
| 917 | +described in .BTF.base sections, while composite types like structs | |
| 918 | +and unions are not fully defined - the .BTF.base type simply serves as | |
| 919 | +a description of the type the split BTF referred to, so structs/unions | |
| 920 | +have 0 members in the .BTF.base section. ENUM[64]s are similarly recorded | |
| 921 | +with 0 members. Any other types are added to the split BTF. This | |
| 922 | +distillation process then leaves us with a .BTF.base section with | |
| 923 | +such minimal descriptions of base types and .BTF split section which refers | |
| 924 | +to those base types. Later, we can relocate the split BTF using both the | |
| 925 | +information stored in the .BTF.base section and the new .BTF base; the type | |
| 926 | +information in the .BTF.base section allows us to update the split BTF | |
| 927 | +references to point at the corresponding new base BTF IDs. | |
| 928 | + | |
| 929 | +BTF relocation happens on kernel module load when a kernel module has a | |
| 930 | +.BTF.base section, and libbpf also provides a btf__relocate() API to | |
| 931 | +accomplish this. | |
| 932 | + | |
| 933 | +As an example consider the following base BTF:: | |
| 934 | + | |
| 935 | + [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED | |
| 936 | + [2] STRUCT 'foo' size=8 vlen=2 | |
| 937 | + 'f1' type_id=1 bits_offset=0 | |
| 938 | + 'f2' type_id=1 bits_offset=32 | |
| 939 | + | |
| 940 | +...and associated split BTF:: | |
| 941 | + | |
| 942 | + [3] PTR '(anon)' type_id=2 | |
| 943 | + | |
| 944 | +i.e. split BTF describes a pointer to struct foo { int f1; int f2 }; | |
| 945 | + | |
| 946 | +.BTF.base will consist of:: | |
| 947 | + | |
| 948 | + [1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED | |
| 949 | + [2] STRUCT 'foo' size=8 vlen=0 | |
| 950 | + | |
| 951 | +If we relocate the split BTF later using the following new base BTF:: | |
| 952 | + | |
| 953 | + [1] INT 'long unsigned int' size=8 bits_offset=0 nr_bits=64 encoding=(none) | |
| 954 | + [2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED | |
| 955 | + [3] STRUCT 'foo' size=8 vlen=2 | |
| 956 | + 'f1' type_id=2 bits_offset=0 | |
| 957 | + 'f2' type_id=2 bits_offset=32 | |
| 958 | + | |
| 959 | +...we can use our .BTF.base description to know that the split BTF reference | |
| 960 | +is to struct foo, and relocation results in new split BTF:: | |
| 961 | + | |
| 962 | + [4] PTR '(anon)' type_id=3 | |
| 963 | + | |
| 964 | +Note that we had to update BTF ID and start BTF ID for the split BTF. | |
| 965 | + | |
| 966 | +So we see how .BTF.base plays the role of facilitating later relocation, | |
| 967 | +leading to more resilient split BTF. | |
| 968 | + | |
| 969 | +.BTF.base sections will be generated automatically for out-of-tree kernel module | |
| 970 | +builds - i.e. where KBUILD_EXTMOD is set (as it would be for "make M=path/2/mod" | |
| 971 | +cases). .BTF.base generation requires pahole support for the "distilled_base" | |
| 972 | +BTF feature; this is available in pahole v1.28 and later. | |
| 973 | + | |
| 899 | 974 | 5. Using BTF |
| 900 | 975 | ============ |
| 901 | 976 | |
| @@ -507,7 +507,7 @@ Notes: · 506 unmodified lines | ||
| 507 | 507 | from the parent state to the current state. |
| 508 | 508 | |
| 509 | 509 | * Details about REG_LIVE_READ32 are omitted. |
| 510 | − | |
| 510 | + | |
| 511 | 511 | * Function ``propagate_liveness()`` (see section :ref:`read_marks_for_cache_hits`) |
| 512 | 512 | might override the first parent link. Please refer to the comments in the |
| 513 | 513 | ``propagate_liveness()`` and ``mark_reg_read()`` source code for further |
| @@ -571,7 +571,7 @@ works:: · 57 unmodified lines | ||
| 571 | 571 | are considered equivalent. |
| 572 | 572 | |
| 573 | 573 | .. _read_marks_for_cache_hits: |
| 574 | − | |
| 574 | + | |
| 575 | 575 | Read marks propagation for cache hits |
| 576 | 576 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 577 | 577 | |
| @@ -2094,6 +2094,12 @@ static void restore_args(struct jit_ctx *ctx, int args_off, int nregs) · 2093 unmodified lines | ||
| 2094 | 2094 | } |
| 2095 | 2095 | } |
| 2096 | 2096 | |
| 2097 | +static bool is_struct_ops_tramp(const struct bpf_tramp_links *fentry_links) | |
| 2098 | +{ | |
| 2099 | + return fentry_links->nr_links == 1 && | |
| 2100 | + fentry_links->links[0]->link.type == BPF_LINK_TYPE_STRUCT_OPS; | |
| 2101 | +} | |
| 2102 | + | |
| 2097 | 2103 | /* Based on the x86's implementation of arch_prepare_bpf_trampoline(). |
| 2098 | 2104 | * |
| 2099 | 2105 | * bpf prog and function entry before bpf trampoline hooked: |
| @@ -2123,6 +2129,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 23 unmodified lines | ||
| 2123 | 2129 | struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; |
| 2124 | 2130 | bool save_ret; |
| 2125 | 2131 | __le32 **branches = NULL; |
| 2132 | + bool is_struct_ops = is_struct_ops_tramp(fentry); | |
| 2126 | 2133 | |
| 2127 | 2134 | /* trampoline stack layout: |
| 2128 | 2135 | * [ parent ip ] |
| @@ -2191,11 +2198,14 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 62 unmodified lines | ||
| 2191 | 2198 | */ |
| 2192 | 2199 | emit_bti(A64_BTI_JC, ctx); |
| 2193 | 2200 | |
| 2194 | − /* frame for parent function */ | |
| 2195 | − emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx); | |
| 2196 | − emit(A64_MOV(1, A64_FP, A64_SP), ctx); | |
| 2201 | + /* x9 is not set for struct_ops */ | |
| 2202 | + if (!is_struct_ops) { | |
| 2203 | + /* frame for parent function */ | |
| 2204 | + emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx); | |
| 2205 | + emit(A64_MOV(1, A64_FP, A64_SP), ctx); | |
| 2206 | + } | |
| 2197 | 2207 | |
| 2198 | − /* frame for patched function */ | |
| 2208 | + /* frame for patched function for tracing, or caller for struct_ops */ | |
| 2199 | 2209 | emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx); |
| 2200 | 2210 | emit(A64_MOV(1, A64_FP, A64_SP), ctx); |
| 2201 | 2211 | |
| @@ -2289,19 +2299,24 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 87 unmodified lines | ||
| 2289 | 2299 | /* reset SP */ |
| 2290 | 2300 | emit(A64_MOV(1, A64_SP, A64_FP), ctx); |
| 2291 | 2301 | |
| 2292 | − /* pop frames */ | |
| 2293 | − emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); | |
| 2294 | − emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx); | |
| 2295 | − | |
| 2296 | − if (flags & BPF_TRAMP_F_SKIP_FRAME) { | |
| 2297 | − /* skip patched function, return to parent */ | |
| 2298 | − emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); | |
| 2299 | − emit(A64_RET(A64_R(9)), ctx); | |
| 2302 | + if (is_struct_ops) { | |
| 2303 | + emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); | |
| 2304 | + emit(A64_RET(A64_LR), ctx); | |
| 2300 | 2305 | } else { |
| 2301 | − /* return to patched function */ | |
| 2302 | − emit(A64_MOV(1, A64_R(10), A64_LR), ctx); | |
| 2303 | − emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); | |
| 2304 | − emit(A64_RET(A64_R(10)), ctx); | |
| 2306 | + /* pop frames */ | |
| 2307 | + emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx); | |
| 2308 | + emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx); | |
| 2309 | + | |
| 2310 | + if (flags & BPF_TRAMP_F_SKIP_FRAME) { | |
| 2311 | + /* skip patched function, return to parent */ | |
| 2312 | + emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); | |
| 2313 | + emit(A64_RET(A64_R(9)), ctx); | |
| 2314 | + } else { | |
| 2315 | + /* return to patched function */ | |
| 2316 | + emit(A64_MOV(1, A64_R(10), A64_LR), ctx); | |
| 2317 | + emit(A64_MOV(1, A64_LR, A64_R(9)), ctx); | |
| 2318 | + emit(A64_RET(A64_R(10)), ctx); | |
| 2319 | + } | |
| 2305 | 2320 | } |
| 2306 | 2321 | |
| 2307 | 2322 | kfree(branches); |
| @@ -325,6 +325,22 @@ struct jit_context { · 324 unmodified lines | ||
| 325 | 325 | /* Number of bytes that will be skipped on tailcall */ |
| 326 | 326 | #define X86_TAIL_CALL_OFFSET (12 + ENDBR_INSN_SIZE) |
| 327 | 327 | |
| 328 | +static void push_r9(u8 **pprog) | |
| 329 | +{ | |
| 330 | + u8 *prog = *pprog; | |
| 331 | + | |
| 332 | + EMIT2(0x41, 0x51); /* push r9 */ | |
| 333 | + *pprog = prog; | |
| 334 | +} | |
| 335 | + | |
| 336 | +static void pop_r9(u8 **pprog) | |
| 337 | +{ | |
| 338 | + u8 *prog = *pprog; | |
| 339 | + | |
| 340 | + EMIT2(0x41, 0x59); /* pop r9 */ | |
| 341 | + *pprog = prog; | |
| 342 | +} | |
| 343 | + | |
| 328 | 344 | static void push_r12(u8 **pprog) |
| 329 | 345 | { |
| 330 | 346 | u8 *prog = *pprog; |
| @@ -1404,6 +1420,24 @@ static void emit_shiftx(u8 **pprog, u32 dst_reg, u8 src_reg, bool is64, u8 op) · 1073 unmodified lines | ||
| 1404 | 1420 | *pprog = prog; |
| 1405 | 1421 | } |
| 1406 | 1422 | |
| 1423 | +static void emit_priv_frame_ptr(u8 **pprog, void __percpu *priv_frame_ptr) | |
| 1424 | +{ | |
| 1425 | + u8 *prog = *pprog; | |
| 1426 | + | |
| 1427 | + /* movabs r9, priv_frame_ptr */ | |
| 1428 | + emit_mov_imm64(&prog, X86_REG_R9, (__force long) priv_frame_ptr >> 32, | |
| 1429 | + (u32) (__force long) priv_frame_ptr); | |
| 1430 | + | |
| 1431 | +#ifdef CONFIG_SMP | |
| 1432 | + /* add <r9>, gs:[<off>] */ | |
| 1433 | + EMIT2(0x65, 0x4c); | |
| 1434 | + EMIT3(0x03, 0x0c, 0x25); | |
| 1435 | + EMIT((u32)(unsigned long)&this_cpu_off, 4); | |
| 1436 | +#endif | |
| 1437 | + | |
| 1438 | + *pprog = prog; | |
| 1439 | +} | |
| 1440 | + | |
| 1407 | 1441 | #define INSN_SZ_DIFF (((addrs[i] - addrs[i - 1]) - (prog - temp))) |
| 1408 | 1442 | |
| 1409 | 1443 | #define __LOAD_TCC_PTR(off) \ |
| @@ -1412,6 +1446,10 @@ static void emit_shiftx(u8 **pprog, u32 dst_reg, u8 src_reg, bool is64, u8 op) · 2 unmodified lines | ||
| 1412 | 1446 | #define LOAD_TAIL_CALL_CNT_PTR(stack) \ |
| 1413 | 1447 | __LOAD_TCC_PTR(BPF_TAIL_CALL_CNT_PTR_STACK_OFF(stack)) |
| 1414 | 1448 | |
| 1449 | +/* Memory size/value to protect private stack overflow/underflow */ | |
| 1450 | +#define PRIV_STACK_GUARD_SZ 8 | |
| 1451 | +#define PRIV_STACK_GUARD_VAL 0xEB9F12345678eb9fULL | |
| 1452 | + | |
| 1415 | 1453 | static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image, |
| 1416 | 1454 | int oldproglen, struct jit_context *ctx, bool jmp_padding) |
| 1417 | 1455 | { |
| @@ -1421,18 +1459,28 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image · 3 unmodified lines | ||
| 1421 | 1459 | int insn_cnt = bpf_prog->len; |
| 1422 | 1460 | bool seen_exit = false; |
| 1423 | 1461 | u8 temp[BPF_MAX_INSN_SIZE + BPF_INSN_SAFETY]; |
| 1462 | + void __percpu *priv_frame_ptr = NULL; | |
| 1424 | 1463 | u64 arena_vm_start, user_vm_start; |
| 1464 | + void __percpu *priv_stack_ptr; | |
| 1425 | 1465 | int i, excnt = 0; |
| 1426 | 1466 | int ilen, proglen = 0; |
| 1427 | 1467 | u8 *prog = temp; |
| 1468 | + u32 stack_depth; | |
| 1428 | 1469 | int err; |
| 1429 | 1470 | |
| 1471 | + stack_depth = bpf_prog->aux->stack_depth; | |
| 1472 | + priv_stack_ptr = bpf_prog->aux->priv_stack_ptr; | |
| 1473 | + if (priv_stack_ptr) { | |
| 1474 | + priv_frame_ptr = priv_stack_ptr + PRIV_STACK_GUARD_SZ + round_up(stack_depth, 8); | |
| 1475 | + stack_depth = 0; | |
| 1476 | + } | |
| 1477 | + | |
| 1430 | 1478 | arena_vm_start = bpf_arena_get_kern_vm_start(bpf_prog->aux->arena); |
| 1431 | 1479 | user_vm_start = bpf_arena_get_user_vm_start(bpf_prog->aux->arena); |
| 1432 | 1480 | |
| 1433 | 1481 | detect_reg_usage(insn, insn_cnt, callee_regs_used); |
| 1434 | 1482 | |
| 1435 | − emit_prologue(&prog, bpf_prog->aux->stack_depth, | |
| 1483 | + emit_prologue(&prog, stack_depth, | |
| 1436 | 1484 | bpf_prog_was_classic(bpf_prog), tail_call_reachable, |
| 1437 | 1485 | bpf_is_subprog(bpf_prog), bpf_prog->aux->exception_cb); |
| 1438 | 1486 | /* Exception callback will clobber callee regs for its own use, and |
| @@ -1454,6 +1502,9 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image · 15 unmodified lines | ||
| 1454 | 1502 | emit_mov_imm64(&prog, X86_REG_R12, |
| 1455 | 1503 | arena_vm_start >> 32, (u32) arena_vm_start); |
| 1456 | 1504 | |
| 1505 | + if (priv_frame_ptr) | |
| 1506 | + emit_priv_frame_ptr(&prog, priv_frame_ptr); | |
| 1507 | + | |
| 1457 | 1508 | ilen = prog - temp; |
| 1458 | 1509 | if (rw_image) |
| 1459 | 1510 | memcpy(rw_image + proglen, temp, ilen); |
| @@ -1473,6 +1524,14 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image · 13 unmodified lines | ||
| 1473 | 1524 | u8 *func; |
| 1474 | 1525 | int nops; |
| 1475 | 1526 | |
| 1527 | + if (priv_frame_ptr) { | |
| 1528 | + if (src_reg == BPF_REG_FP) | |
| 1529 | + src_reg = X86_REG_R9; | |
| 1530 | + | |
| 1531 | + if (dst_reg == BPF_REG_FP) | |
| 1532 | + dst_reg = X86_REG_R9; | |
| 1533 | + } | |
| 1534 | + | |
| 1476 | 1535 | switch (insn->code) { |
| 1477 | 1536 | /* ALU */ |
| 1478 | 1537 | case BPF_ALU | BPF_ADD | BPF_X: |
| @@ -2127,15 +2186,21 @@ populate_extable: · 648 unmodified lines | ||
| 2127 | 2186 | u8 *ip = image + addrs[i - 1]; |
| 2128 | 2187 | |
| 2129 | 2188 | func = (u8 *) __bpf_call_base + imm32; |
| 2130 | − if (tail_call_reachable) { | |
| 2131 | − LOAD_TAIL_CALL_CNT_PTR(bpf_prog->aux->stack_depth); | |
| 2189 | + if (src_reg == BPF_PSEUDO_CALL && tail_call_reachable) { | |
| 2190 | + LOAD_TAIL_CALL_CNT_PTR(stack_depth); | |
| 2132 | 2191 | ip += 7; |
| 2133 | 2192 | } |
| 2134 | 2193 | if (!imm32) |
| 2135 | 2194 | return -EINVAL; |
| 2195 | + if (priv_frame_ptr) { | |
| 2196 | + push_r9(&prog); | |
| 2197 | + ip += 2; | |
| 2198 | + } | |
| 2136 | 2199 | ip += x86_call_depth_emit_accounting(&prog, func, ip); |
| 2137 | 2200 | if (emit_call(&prog, func, ip)) |
| 2138 | 2201 | return -EINVAL; |
| 2202 | + if (priv_frame_ptr) | |
| 2203 | + pop_r9(&prog); | |
| 2139 | 2204 | break; |
| 2140 | 2205 | } |
| 2141 | 2206 | |
| @@ -2145,13 +2210,13 @@ populate_extable: · 3 unmodified lines | ||
| 2145 | 2210 | &bpf_prog->aux->poke_tab[imm32 - 1], |
| 2146 | 2211 | &prog, image + addrs[i - 1], |
| 2147 | 2212 | callee_regs_used, |
| 2148 | − bpf_prog->aux->stack_depth, | |
| 2213 | + stack_depth, | |
| 2149 | 2214 | ctx); |
| 2150 | 2215 | else |
| 2151 | 2216 | emit_bpf_tail_call_indirect(bpf_prog, |
| 2152 | 2217 | &prog, |
| 2153 | 2218 | callee_regs_used, |
| 2154 | − bpf_prog->aux->stack_depth, | |
| 2219 | + stack_depth, | |
| 2155 | 2220 | image + addrs[i - 1], |
| 2156 | 2221 | ctx); |
| 2157 | 2222 | break; |
| @@ -3303,6 +3368,42 @@ int arch_prepare_bpf_dispatcher(void *image, void *buf, s64 *funcs, int num_func · 1145 unmodified lines | ||
| 3303 | 3368 | return emit_bpf_dispatcher(&prog, 0, num_funcs - 1, funcs, image, buf); |
| 3304 | 3369 | } |
| 3305 | 3370 | |
| 3371 | +static const char *bpf_get_prog_name(struct bpf_prog *prog) | |
| 3372 | +{ | |
| 3373 | + if (prog->aux->ksym.prog) | |
| 3374 | + return prog->aux->ksym.name; | |
| 3375 | + return prog->aux->name; | |
| 3376 | +} | |
| 3377 | + | |
| 3378 | +static void priv_stack_init_guard(void __percpu *priv_stack_ptr, int alloc_size) | |
| 3379 | +{ | |
| 3380 | + int cpu, underflow_idx = (alloc_size - PRIV_STACK_GUARD_SZ) >> 3; | |
| 3381 | + u64 *stack_ptr; | |
| 3382 | + | |
| 3383 | + for_each_possible_cpu(cpu) { | |
| 3384 | + stack_ptr = per_cpu_ptr(priv_stack_ptr, cpu); | |
| 3385 | + stack_ptr[0] = PRIV_STACK_GUARD_VAL; | |
| 3386 | + stack_ptr[underflow_idx] = PRIV_STACK_GUARD_VAL; | |
| 3387 | + } | |
| 3388 | +} | |
| 3389 | + | |
| 3390 | +static void priv_stack_check_guard(void __percpu *priv_stack_ptr, int alloc_size, | |
| 3391 | + struct bpf_prog *prog) | |
| 3392 | +{ | |
| 3393 | + int cpu, underflow_idx = (alloc_size - PRIV_STACK_GUARD_SZ) >> 3; | |
| 3394 | + u64 *stack_ptr; | |
| 3395 | + | |
| 3396 | + for_each_possible_cpu(cpu) { | |
| 3397 | + stack_ptr = per_cpu_ptr(priv_stack_ptr, cpu); | |
| 3398 | + if (stack_ptr[0] != PRIV_STACK_GUARD_VAL || | |
| 3399 | + stack_ptr[underflow_idx] != PRIV_STACK_GUARD_VAL) { | |
| 3400 | + pr_err("BPF private stack overflow/underflow detected for prog %sx\n", | |
| 3401 | + bpf_get_prog_name(prog)); | |
| 3402 | + break; | |
| 3403 | + } | |
| 3404 | + } | |
| 3405 | +} | |
| 3406 | + | |
| 3306 | 3407 | struct x64_jit_data { |
| 3307 | 3408 | struct bpf_binary_header *rw_header; |
| 3308 | 3409 | struct bpf_binary_header *header; |
| @@ -3320,7 +3421,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) · 11 unmodified lines | ||
| 3320 | 3421 | struct bpf_binary_header *rw_header = NULL; |
| 3321 | 3422 | struct bpf_binary_header *header = NULL; |
| 3322 | 3423 | struct bpf_prog *tmp, *orig_prog = prog; |
| 3424 | + void __percpu *priv_stack_ptr = NULL; | |
| 3323 | 3425 | struct x64_jit_data *jit_data; |
| 3426 | + int priv_stack_alloc_sz; | |
| 3324 | 3427 | int proglen, oldproglen = 0; |
| 3325 | 3428 | struct jit_context ctx = {}; |
| 3326 | 3429 | bool tmp_blinded = false; |
| @@ -3356,6 +3459,23 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) · 29 unmodified lines | ||
| 3356 | 3459 | } |
| 3357 | 3460 | prog->aux->jit_data = jit_data; |
| 3358 | 3461 | } |
| 3462 | + priv_stack_ptr = prog->aux->priv_stack_ptr; | |
| 3463 | + if (!priv_stack_ptr && prog->aux->jits_use_priv_stack) { | |
| 3464 | + /* Allocate actual private stack size with verifier-calculated | |
| 3465 | + * stack size plus two memory guards to protect overflow and | |
| 3466 | + * underflow. | |
| 3467 | + */ | |
| 3468 | + priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 8) + | |
| 3469 | + 2 * PRIV_STACK_GUARD_SZ; | |
| 3470 | + priv_stack_ptr = __alloc_percpu_gfp(priv_stack_alloc_sz, 8, GFP_KERNEL); | |
| 3471 | + if (!priv_stack_ptr) { | |
| 3472 | + prog = orig_prog; | |
| 3473 | + goto out_priv_stack; | |
| 3474 | + } | |
| 3475 | + | |
| 3476 | + priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_sz); | |
| 3477 | + prog->aux->priv_stack_ptr = priv_stack_ptr; | |
| 3478 | + } | |
| 3359 | 3479 | addrs = jit_data->addrs; |
| 3360 | 3480 | if (addrs) { |
| 3361 | 3481 | ctx = jit_data->ctx; |
| @@ -3491,6 +3611,11 @@ out_image: · 129 unmodified lines | ||
| 3491 | 3611 | bpf_prog_fill_jited_linfo(prog, addrs + 1); |
| 3492 | 3612 | out_addrs: |
| 3493 | 3613 | kvfree(addrs); |
| 3614 | + if (!image && priv_stack_ptr) { | |
| 3615 | + free_percpu(priv_stack_ptr); | |
| 3616 | + prog->aux->priv_stack_ptr = NULL; | |
| 3617 | + } | |
| 3618 | +out_priv_stack: | |
| 3494 | 3619 | kfree(jit_data); |
| 3495 | 3620 | prog->aux->jit_data = NULL; |
| 3496 | 3621 | } |
| @@ -3529,6 +3654,8 @@ void bpf_jit_free(struct bpf_prog *prog) · 32 unmodified lines | ||
| 3529 | 3654 | if (prog->jited) { |
| 3530 | 3655 | struct x64_jit_data *jit_data = prog->aux->jit_data; |
| 3531 | 3656 | struct bpf_binary_header *hdr; |
| 3657 | + void __percpu *priv_stack_ptr; | |
| 3658 | + int priv_stack_alloc_sz; | |
| 3532 | 3659 | |
| 3533 | 3660 | /* |
| 3534 | 3661 | * If we fail the final pass of JIT (from jit_subprogs), |
| @@ -3544,6 +3671,13 @@ void bpf_jit_free(struct bpf_prog *prog) · 9 unmodified lines | ||
| 3544 | 3671 | prog->bpf_func = (void *)prog->bpf_func - cfi_get_offset(); |
| 3545 | 3672 | hdr = bpf_jit_binary_pack_hdr(prog); |
| 3546 | 3673 | bpf_jit_binary_pack_free(hdr, NULL); |
| 3674 | + priv_stack_ptr = prog->aux->priv_stack_ptr; | |
| 3675 | + if (priv_stack_ptr) { | |
| 3676 | + priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 8) + | |
| 3677 | + 2 * PRIV_STACK_GUARD_SZ; | |
| 3678 | + priv_stack_check_guard(priv_stack_ptr, priv_stack_alloc_sz, prog); | |
| 3679 | + free_percpu(prog->aux->priv_stack_ptr); | |
| 3680 | + } | |
| 3547 | 3681 | WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog)); |
| 3548 | 3682 | } |
| 3549 | 3683 | |
| @@ -3559,6 +3693,11 @@ bool bpf_jit_supports_exceptions(void) · 9 unmodified lines | ||
| 3559 | 3693 | return IS_ENABLED(CONFIG_UNWINDER_ORC); |
| 3560 | 3694 | } |
| 3561 | 3695 | |
| 3696 | +bool bpf_jit_supports_private_stack(void) | |
| 3697 | +{ | |
| 3698 | + return true; | |
| 3699 | +} | |
| 3700 | + | |
| 3562 | 3701 | void arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp), void *cookie) |
| 3563 | 3702 | { |
| 3564 | 3703 | #if defined(CONFIG_UNWINDER_ORC) |
| @@ -203,6 +203,7 @@ enum btf_field_type { · 202 unmodified lines | ||
| 203 | 203 | BPF_GRAPH_ROOT = BPF_RB_ROOT | BPF_LIST_HEAD, |
| 204 | 204 | BPF_REFCOUNT = (1 << 9), |
| 205 | 205 | BPF_WORKQUEUE = (1 << 10), |
| 206 | + BPF_UPTR = (1 << 11), | |
| 206 | 207 | }; |
| 207 | 208 | |
| 208 | 209 | typedef void (*btf_dtor_kfunc_t)(void *); |
| @@ -322,6 +323,8 @@ static inline const char *btf_field_type_name(enum btf_field_type type) · 113 unmodified lines | ||
| 322 | 323 | return "kptr"; |
| 323 | 324 | case BPF_KPTR_PERCPU: |
| 324 | 325 | return "percpu_kptr"; |
| 326 | + case BPF_UPTR: | |
| 327 | + return "uptr"; | |
| 325 | 328 | case BPF_LIST_HEAD: |
| 326 | 329 | return "bpf_list_head"; |
| 327 | 330 | case BPF_LIST_NODE: |
| @@ -350,6 +353,7 @@ static inline u32 btf_field_type_size(enum btf_field_type type) · 22 unmodified lines | ||
| 350 | 353 | case BPF_KPTR_UNREF: |
| 351 | 354 | case BPF_KPTR_REF: |
| 352 | 355 | case BPF_KPTR_PERCPU: |
| 356 | + case BPF_UPTR: | |
| 353 | 357 | return sizeof(u64); |
| 354 | 358 | case BPF_LIST_HEAD: |
| 355 | 359 | return sizeof(struct bpf_list_head); |
| @@ -379,6 +383,7 @@ static inline u32 btf_field_type_align(enum btf_field_type type) · 23 unmodified lines | ||
| 379 | 383 | case BPF_KPTR_UNREF: |
| 380 | 384 | case BPF_KPTR_REF: |
| 381 | 385 | case BPF_KPTR_PERCPU: |
| 386 | + case BPF_UPTR: | |
| 382 | 387 | return __alignof__(u64); |
| 383 | 388 | case BPF_LIST_HEAD: |
| 384 | 389 | return __alignof__(struct bpf_list_head); |
| @@ -419,6 +424,7 @@ static inline void bpf_obj_init_field(const struct btf_field *field, void *addr) · 34 unmodified lines | ||
| 419 | 424 | case BPF_KPTR_UNREF: |
| 420 | 425 | case BPF_KPTR_REF: |
| 421 | 426 | case BPF_KPTR_PERCPU: |
| 427 | + case BPF_UPTR: | |
| 422 | 428 | break; |
| 423 | 429 | default: |
| 424 | 430 | WARN_ON_ONCE(1); |
| @@ -507,6 +513,25 @@ static inline void copy_map_value_long(struct bpf_map *map, void *dst, void *src · 82 unmodified lines | ||
| 507 | 513 | bpf_obj_memcpy(map->record, dst, src, map->value_size, true); |
| 508 | 514 | } |
| 509 | 515 | |
| 516 | +static inline void bpf_obj_swap_uptrs(const struct btf_record *rec, void *dst, void *src) | |
| 517 | +{ | |
| 518 | + unsigned long *src_uptr, *dst_uptr; | |
| 519 | + const struct btf_field *field; | |
| 520 | + int i; | |
| 521 | + | |
| 522 | + if (!btf_record_has_field(rec, BPF_UPTR)) | |
| 523 | + return; | |
| 524 | + | |
| 525 | + for (i = 0, field = rec->fields; i < rec->cnt; i++, field++) { | |
| 526 | + if (field->type != BPF_UPTR) | |
| 527 | + continue; | |
| 528 | + | |
| 529 | + src_uptr = src + field->offset; | |
| 530 | + dst_uptr = dst + field->offset; | |
| 531 | + swap(*src_uptr, *dst_uptr); | |
| 532 | + } | |
| 533 | +} | |
| 534 | + | |
| 510 | 535 | static inline void bpf_obj_memzero(struct btf_record *rec, void *dst, u32 size) |
| 511 | 536 | { |
| 512 | 537 | u32 curr_off = 0; |
| @@ -907,10 +932,6 @@ enum bpf_reg_type { · 394 unmodified lines | ||
| 907 | 932 | * additional context, assume the value is non-null. |
| 908 | 933 | */ |
| 909 | 934 | PTR_TO_BTF_ID, |
| 910 | − /* PTR_TO_BTF_ID_OR_NULL points to a kernel struct that has not | |
| 911 | − * been checked for null. Used primarily to inform the verifier | |
| 912 | − * an explicit null check is required for this struct. | |
| 913 | − */ | |
| 914 | 935 | PTR_TO_MEM, /* reg points to valid memory region */ |
| 915 | 936 | PTR_TO_ARENA, |
| 916 | 937 | PTR_TO_BUF, /* reg points to a read/write buffer */ |
| @@ -923,6 +944,10 @@ enum bpf_reg_type { · 6 unmodified lines | ||
| 923 | 944 | PTR_TO_SOCKET_OR_NULL = PTR_MAYBE_NULL | PTR_TO_SOCKET, |
| 924 | 945 | PTR_TO_SOCK_COMMON_OR_NULL = PTR_MAYBE_NULL | PTR_TO_SOCK_COMMON, |
| 925 | 946 | PTR_TO_TCP_SOCK_OR_NULL = PTR_MAYBE_NULL | PTR_TO_TCP_SOCK, |
| 947 | + /* PTR_TO_BTF_ID_OR_NULL points to a kernel struct that has not | |
| 948 | + * been checked for null. Used primarily to inform the verifier | |
| 949 | + * an explicit null check is required for this struct. | |
| 950 | + */ | |
| 926 | 951 | PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | PTR_TO_BTF_ID, |
| 927 | 952 | |
| 928 | 953 | /* This must be the last entry. Its purpose is to ensure the enum is |
| @@ -1300,8 +1325,12 @@ void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len); · 371 unmodified lines | ||
| 1300 | 1325 | bool __bpf_dynptr_is_rdonly(const struct bpf_dynptr_kern *ptr); |
| 1301 | 1326 | |
| 1302 | 1327 | #ifdef CONFIG_BPF_JIT |
| 1303 | −int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr); | |
| 1304 | −int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr); | |
| 1328 | +int bpf_trampoline_link_prog(struct bpf_tramp_link *link, | |
| 1329 | + struct bpf_trampoline *tr, | |
| 1330 | + struct bpf_prog *tgt_prog); | |
| 1331 | +int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, | |
| 1332 | + struct bpf_trampoline *tr, | |
| 1333 | + struct bpf_prog *tgt_prog); | |
| 1305 | 1334 | struct bpf_trampoline *bpf_trampoline_get(u64 key, |
| 1306 | 1335 | struct bpf_attach_target_info *tgt_info); |
| 1307 | 1336 | void bpf_trampoline_put(struct bpf_trampoline *tr); |
| @@ -1373,7 +1402,8 @@ int arch_prepare_bpf_dispatcher(void *image, void *buf, s64 *funcs, int num_func · 65 unmodified lines | ||
| 1373 | 1402 | void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, |
| 1374 | 1403 | struct bpf_prog *to); |
| 1375 | 1404 | /* Called only from JIT-enabled code, so there's no need for stubs. */ |
| 1376 | −void bpf_image_ksym_add(void *data, unsigned int size, struct bpf_ksym *ksym); | |
| 1405 | +void bpf_image_ksym_init(void *data, unsigned int size, struct bpf_ksym *ksym); | |
| 1406 | +void bpf_image_ksym_add(struct bpf_ksym *ksym); | |
| 1377 | 1407 | void bpf_image_ksym_del(struct bpf_ksym *ksym); |
| 1378 | 1408 | void bpf_ksym_add(struct bpf_ksym *ksym); |
| 1379 | 1409 | void bpf_ksym_del(struct bpf_ksym *ksym); |
| @@ -1382,12 +1412,14 @@ void bpf_jit_uncharge_modmem(u32 size); · 2 unmodified lines | ||
| 1382 | 1412 | bool bpf_prog_has_trampoline(const struct bpf_prog *prog); |
| 1383 | 1413 | #else |
| 1384 | 1414 | static inline int bpf_trampoline_link_prog(struct bpf_tramp_link *link, |
| 1385 | − struct bpf_trampoline *tr) | |
| 1415 | + struct bpf_trampoline *tr, | |
| 1416 | + struct bpf_prog *tgt_prog) | |
| 1386 | 1417 | { |
| 1387 | 1418 | return -ENOTSUPP; |
| 1388 | 1419 | } |
| 1389 | 1420 | static inline int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, |
| 1390 | − struct bpf_trampoline *tr) | |
| 1421 | + struct bpf_trampoline *tr, | |
| 1422 | + struct bpf_prog *tgt_prog) | |
| 1391 | 1423 | { |
| 1392 | 1424 | return -ENOTSUPP; |
| 1393 | 1425 | } |
| @@ -1476,6 +1508,7 @@ struct bpf_prog_aux { · 82 unmodified lines | ||
| 1476 | 1508 | u32 max_rdwr_access; |
| 1477 | 1509 | struct btf *attach_btf; |
| 1478 | 1510 | const struct bpf_ctx_arg_aux *ctx_arg_info; |
| 1511 | + void __percpu *priv_stack_ptr; | |
| 1479 | 1512 | struct mutex dst_mutex; /* protects dst_* pointers below, *after* prog becomes visible */ |
| 1480 | 1513 | struct bpf_prog *dst_prog; |
| 1481 | 1514 | struct bpf_trampoline *dst_trampoline; |
| @@ -1491,7 +1524,13 @@ struct bpf_prog_aux { · 9 unmodified lines | ||
| 1491 | 1524 | bool xdp_has_frags; |
| 1492 | 1525 | bool exception_cb; |
| 1493 | 1526 | bool exception_boundary; |
| 1527 | + bool is_extended; /* true if extended by freplace program */ | |
| 1528 | + bool jits_use_priv_stack; | |
| 1529 | + bool priv_stack_requested; | |
| 1530 | + u64 prog_array_member_cnt; /* counts how many times as member of prog_array */ | |
| 1531 | + struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */ | |
| 1494 | 1532 | struct bpf_arena *arena; |
| 1533 | + void (*recursion_detected)(struct bpf_prog *prog); /* callback if recursion is detected */ | |
| 1495 | 1534 | /* BTF_KIND_FUNC_PROTO for valid attach_btf_id */ |
| 1496 | 1535 | const struct btf_type *attach_func_proto; |
| 1497 | 1536 | /* function name for valid attach_btf_id */ |
| @@ -3461,4 +3500,10 @@ static inline bool bpf_is_subprog(const struct bpf_prog *prog) · 1963 unmodified lines | ||
| 3461 | 3500 | return prog->aux->func_idx != 0; |
| 3462 | 3501 | } |
| 3463 | 3502 | |
| 3503 | +static inline bool bpf_prog_is_raw_tp(const struct bpf_prog *prog) | |
| 3504 | +{ | |
| 3505 | + return prog->type == BPF_PROG_TYPE_TRACING && | |
| 3506 | + prog->expected_attach_type == BPF_TRACE_RAW_TP; | |
| 3507 | +} | |
| 3508 | + | |
| 3464 | 3509 | #endif /* _LINUX_BPF_H */ |
| @@ -77,7 +77,13 @@ struct bpf_local_storage_elem { · 76 unmodified lines | ||
| 77 | 77 | struct hlist_node map_node; /* Linked to bpf_local_storage_map */ |
| 78 | 78 | struct hlist_node snode; /* Linked to bpf_local_storage */ |
| 79 | 79 | struct bpf_local_storage __rcu *local_storage; |
| 80 | − struct rcu_head rcu; | |
| 80 | + union { | |
| 81 | + struct rcu_head rcu; | |
| 82 | + struct hlist_node free_node; /* used to postpone | |
| 83 | + * bpf_selem_free | |
| 84 | + * after raw_spin_unlock | |
| 85 | + */ | |
| 86 | + }; | |
| 81 | 87 | /* 8 bytes hole */ |
| 82 | 88 | /* The data is stored in another cacheline to minimize |
| 83 | 89 | * the number of cachelines access during a cache hit. |
| @@ -181,7 +187,7 @@ void bpf_selem_link_map(struct bpf_local_storage_map *smap, · 97 unmodified lines | ||
| 181 | 187 | |
| 182 | 188 | struct bpf_local_storage_elem * |
| 183 | 189 | bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner, void *value, |
| 184 | − bool charge_mem, gfp_t gfp_flags); | |
| 190 | + bool charge_mem, bool swap_uptrs, gfp_t gfp_flags); | |
| 185 | 191 | |
| 186 | 192 | void bpf_selem_free(struct bpf_local_storage_elem *selem, |
| 187 | 193 | struct bpf_local_storage_map *smap, |
| @@ -195,7 +201,7 @@ bpf_local_storage_alloc(void *owner, · 7 unmodified lines | ||
| 195 | 201 | |
| 196 | 202 | struct bpf_local_storage_data * |
| 197 | 203 | bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, |
| 198 | − void *value, u64 map_flags, gfp_t gfp_flags); | |
| 204 | + void *value, u64 map_flags, bool swap_uptrs, gfp_t gfp_flags); | |
| 199 | 205 | |
| 200 | 206 | u64 bpf_local_storage_map_mem_usage(const struct bpf_map *map); |
| 201 | 207 | |
| @@ -48,22 +48,6 @@ enum bpf_reg_liveness { · 47 unmodified lines | ||
| 48 | 48 | REG_LIVE_DONE = 0x8, /* liveness won't be updating this register anymore */ |
| 49 | 49 | }; |
| 50 | 50 | |
| 51 | −/* For every reg representing a map value or allocated object pointer, | |
| 52 | − * we consider the tuple of (ptr, id) for them to be unique in verifier | |
| 53 | − * context and conside them to not alias each other for the purposes of | |
| 54 | − * tracking lock state. | |
| 55 | − */ | |
| 56 | −struct bpf_active_lock { | |
| 57 | − /* This can either be reg->map_ptr or reg->btf. If ptr is NULL, | |
| 58 | − * there's no active lock held, and other fields have no | |
| 59 | − * meaning. If non-NULL, it indicates that a lock is held and | |
| 60 | − * id member has the reg->id of the register which can be >= 0. | |
| 61 | − */ | |
| 62 | − void *ptr; | |
| 63 | − /* This will be reg->id */ | |
| 64 | − u32 id; | |
| 65 | −}; | |
| 66 | − | |
| 67 | 51 | #define ITER_PREFIX "bpf_iter_" |
| 68 | 52 | |
| 69 | 53 | enum bpf_iter_state { |
| @@ -266,6 +250,13 @@ struct bpf_stack_state { · 196 unmodified lines | ||
| 266 | 250 | }; |
| 267 | 251 | |
| 268 | 252 | struct bpf_reference_state { |
| 253 | + /* Each reference object has a type. Ensure REF_TYPE_PTR is zero to | |
| 254 | + * default to pointer reference on zero initialization of a state. | |
| 255 | + */ | |
| 256 | + enum ref_state_type { | |
| 257 | + REF_TYPE_PTR = 0, | |
| 258 | + REF_TYPE_LOCK, | |
| 259 | + } type; | |
| 269 | 260 | /* Track each reference created with a unique id, even if the same |
| 270 | 261 | * instruction creates the reference multiple times (eg, via CALL). |
| 271 | 262 | */ |
| @@ -274,17 +265,10 @@ struct bpf_reference_state { · 2 unmodified lines | ||
| 274 | 265 | * is used purely to inform the user of a reference leak. |
| 275 | 266 | */ |
| 276 | 267 | int insn_idx; |
| 277 | − /* There can be a case like: | |
| 278 | − * main (frame 0) | |
| 279 | − * cb (frame 1) | |
| 280 | − * func (frame 3) | |
| 281 | − * cb (frame 4) | |
| 282 | − * Hence for frame 4, if callback_ref just stored boolean, it would be | |
| 283 | − * impossible to distinguish nested callback refs. Hence store the | |
| 284 | − * frameno and compare that to callback_ref in check_reference_leak when | |
| 285 | − * exiting a callback function. | |
| 286 | − */ | |
| 287 | − int callback_ref; | |
| 268 | + /* Use to keep track of the source object of a lock, to ensure | |
| 269 | + * it matches on unlock. | |
| 270 | + */ | |
| 271 | + void *ptr; | |
| 288 | 272 | }; |
| 289 | 273 | |
| 290 | 274 | struct bpf_retval_range { |
| @@ -332,6 +316,7 @@ struct bpf_func_state { · 41 unmodified lines | ||
| 332 | 316 | |
| 333 | 317 | /* The following fields should be last. See copy_func_state() */ |
| 334 | 318 | int acquired_refs; |
| 319 | + int active_locks; | |
| 335 | 320 | struct bpf_reference_state *refs; |
| 336 | 321 | /* The state of the stack. Each element of the array describes BPF_REG_SIZE |
| 337 | 322 | * (i.e. 8) bytes worth of stack memory. |
| @@ -349,7 +334,7 @@ struct bpf_func_state { · 11 unmodified lines | ||
| 349 | 334 | |
| 350 | 335 | #define MAX_CALL_FRAMES 8 |
| 351 | 336 | |
| 352 | −/* instruction history flags, used in bpf_jmp_history_entry.flags field */ | |
| 337 | +/* instruction history flags, used in bpf_insn_hist_entry.flags field */ | |
| 353 | 338 | enum { |
| 354 | 339 | /* instruction references stack slot through PTR_TO_STACK register; |
| 355 | 340 | * we also store stack's frame number in lower 3 bits (MAX_CALL_FRAMES is 8) |
| @@ -367,7 +352,7 @@ enum { · 11 unmodified lines | ||
| 367 | 352 | static_assert(INSN_F_FRAMENO_MASK + 1 >= MAX_CALL_FRAMES); |
| 368 | 353 | static_assert(INSN_F_SPI_MASK + 1 >= MAX_BPF_STACK / 8); |
| 369 | 354 | |
| 370 | −struct bpf_jmp_history_entry { | |
| 355 | +struct bpf_insn_hist_entry { | |
| 371 | 356 | u32 idx; |
| 372 | 357 | /* insn idx can't be bigger than 1 million */ |
| 373 | 358 | u32 prev_idx : 22; |
| @@ -434,7 +419,6 @@ struct bpf_verifier_state { · 60 unmodified lines | ||
| 434 | 419 | u32 insn_idx; |
| 435 | 420 | u32 curframe; |
| 436 | 421 | |
| 437 | − struct bpf_active_lock active_lock; | |
| 438 | 422 | bool speculative; |
| 439 | 423 | bool active_rcu_lock; |
| 440 | 424 | u32 active_preempt_lock; |
| @@ -458,13 +442,14 @@ struct bpf_verifier_state { · 17 unmodified lines | ||
| 458 | 442 | * See get_loop_entry() for more information. |
| 459 | 443 | */ |
| 460 | 444 | struct bpf_verifier_state *loop_entry; |
| 461 | − /* jmp history recorded from first to last. | |
| 462 | − * backtracking is using it to go from last to first. | |
| 463 | − * For most states jmp_history_cnt is [0-3]. | |
| 445 | + /* Sub-range of env->insn_hist[] corresponding to this state's | |
| 446 | + * instruction history. | |
| 447 | + * Backtracking is using it to go from last to first. | |
| 448 | + * For most states instruction history is short, 0-3 instructions. | |
| 464 | 449 | * For loops can go up to ~40. |
| 465 | 450 | */ |
| 466 | − struct bpf_jmp_history_entry *jmp_history; | |
| 467 | − u32 jmp_history_cnt; | |
| 451 | + u32 insn_hist_start; | |
| 452 | + u32 insn_hist_end; | |
| 468 | 453 | u32 dfs_depth; |
| 469 | 454 | u32 callback_unroll_depth; |
| 470 | 455 | u32 may_goto_depth; |
| @@ -649,6 +634,12 @@ struct bpf_subprog_arg_info { · 178 unmodified lines | ||
| 649 | 634 | }; |
| 650 | 635 | }; |
| 651 | 636 | |
| 637 | +enum priv_stack_mode { | |
| 638 | + PRIV_STACK_UNKNOWN, | |
| 639 | + NO_PRIV_STACK, | |
| 640 | + PRIV_STACK_ADAPTIVE, | |
| 641 | +}; | |
| 642 | + | |
| 652 | 643 | struct bpf_subprog_info { |
| 653 | 644 | /* 'start' has to be the first field otherwise find_subprog() won't work */ |
| 654 | 645 | u32 start; /* insn idx of function entry point */ |
| @@ -669,6 +660,7 @@ struct bpf_subprog_info { · 14 unmodified lines | ||
| 669 | 660 | /* true if bpf_fastcall stack region is used by functions that can't be inlined */ |
| 670 | 661 | bool keep_fastcall_stack: 1; |
| 671 | 662 | |
| 663 | + enum priv_stack_mode priv_stack_mode; | |
| 672 | 664 | u8 arg_cnt; |
| 673 | 665 | struct bpf_subprog_arg_info args[MAX_BPF_FUNC_REG_ARGS]; |
| 674 | 666 | }; |
| @@ -747,7 +739,9 @@ struct bpf_verifier_env { · 72 unmodified lines | ||
| 747 | 739 | int cur_stack; |
| 748 | 740 | } cfg; |
| 749 | 741 | struct backtrack_state bt; |
| 750 | − struct bpf_jmp_history_entry *cur_hist_ent; | |
| 742 | + struct bpf_insn_hist_entry *insn_hist; | |
| 743 | + struct bpf_insn_hist_entry *cur_hist_ent; | |
| 744 | + u32 insn_hist_cap; | |
| 751 | 745 | u32 pass_cnt; /* number of times do_check() was called */ |
| 752 | 746 | u32 subprog_cnt; |
| 753 | 747 | /* number of instructions analyzed by the verifier */ |
| @@ -888,6 +882,7 @@ static inline bool bpf_prog_check_recur(const struct bpf_prog *prog) · 134 unmodified lines | ||
| 888 | 882 | case BPF_PROG_TYPE_TRACING: |
| 889 | 883 | return prog->expected_attach_type != BPF_TRACE_ITER; |
| 890 | 884 | case BPF_PROG_TYPE_STRUCT_OPS: |
| 885 | + return prog->aux->jits_use_priv_stack; | |
| 891 | 886 | case BPF_PROG_TYPE_LSM: |
| 892 | 887 | return false; |
| 893 | 888 | default: |
| @@ -75,6 +75,7 @@ · 74 unmodified lines | ||
| 75 | 75 | #define KF_ITER_NEXT (1 << 9) /* kfunc implements BPF iter next method */ |
| 76 | 76 | #define KF_ITER_DESTROY (1 << 10) /* kfunc implements BPF iter destructor */ |
| 77 | 77 | #define KF_RCU_PROTECTED (1 << 11) /* kfunc should be protected by rcu cs when they are invoked */ |
| 78 | +#define KF_FASTCALL (1 << 12) /* kfunc supports bpf_fastcall protocol */ | |
| 78 | 79 | |
| 79 | 80 | /* |
| 80 | 81 | * Tag marking a kernel function as a kfunc. This is meant to minimize the |
| @@ -581,6 +582,16 @@ int get_kern_ctx_btf_id(struct bpf_verifier_log *log, enum bpf_prog_type prog_ty · 500 unmodified lines | ||
| 581 | 582 | bool btf_types_are_same(const struct btf *btf1, u32 id1, |
| 582 | 583 | const struct btf *btf2, u32 id2); |
| 583 | 584 | int btf_check_iter_arg(struct btf *btf, const struct btf_type *func, int arg_idx); |
| 585 | + | |
| 586 | +static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type *t) | |
| 587 | +{ | |
| 588 | + if (!btf_type_is_ptr(t)) | |
| 589 | + return false; | |
| 590 | + | |
| 591 | + t = btf_type_skip_modifiers(btf, t->type, NULL); | |
| 592 | + | |
| 593 | + return btf_type_is_struct(t); | |
| 594 | +} | |
| 584 | 595 | #else |
| 585 | 596 | static inline const struct btf_type *btf_type_by_id(const struct btf *btf, |
| 586 | 597 | u32 type_id) |
| @@ -660,15 +671,4 @@ static inline int btf_check_iter_arg(struct btf *btf, const struct btf_type *fun · 73 unmodified lines | ||
| 660 | 671 | return -EOPNOTSUPP; |
| 661 | 672 | } |
| 662 | 673 | #endif |
| 663 | − | |
| 664 | −static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type *t) | |
| 665 | −{ | |
| 666 | − if (!btf_type_is_ptr(t)) | |
| 667 | − return false; | |
| 668 | − | |
| 669 | − t = btf_type_skip_modifiers(btf, t->type, NULL); | |
| 670 | − | |
| 671 | − return btf_type_is_struct(t); | |
| 672 | −} | |
| 673 | − | |
| 674 | 674 | #endif |
| @@ -283,5 +283,6 @@ extern u32 btf_tracing_ids[]; · 282 unmodified lines | ||
| 283 | 283 | extern u32 bpf_cgroup_btf_id[]; |
| 284 | 284 | extern u32 bpf_local_storage_map_btf_id[]; |
| 285 | 285 | extern u32 btf_bpf_map_id[]; |
| 286 | +extern u32 bpf_kmem_cache_btf_id[]; | |
| 286 | 287 | |
| 287 | 288 | #endif |
| @@ -1119,6 +1119,7 @@ bool bpf_jit_supports_exceptions(void); · 1118 unmodified lines | ||
| 1119 | 1119 | bool bpf_jit_supports_ptr_xchg(void); |
| 1120 | 1120 | bool bpf_jit_supports_arena(void); |
| 1121 | 1121 | bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena); |
| 1122 | +bool bpf_jit_supports_private_stack(void); | |
| 1122 | 1123 | u64 bpf_arch_uaddress_limit(void); |
| 1123 | 1124 | void arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp), void *cookie); |
| 1124 | 1125 | bool bpf_helper_changes_pkt_data(void *func); |
| @@ -1116,6 +1116,7 @@ enum bpf_attach_type { · 1115 unmodified lines | ||
| 1116 | 1116 | BPF_NETKIT_PRIMARY, |
| 1117 | 1117 | BPF_NETKIT_PEER, |
| 1118 | 1118 | BPF_TRACE_KPROBE_SESSION, |
| 1119 | + BPF_TRACE_UPROBE_SESSION, | |
| 1119 | 1120 | __MAX_BPF_ATTACH_TYPE |
| 1120 | 1121 | }; |
| 1121 | 1122 | |
| @@ -1973,6 +1974,8 @@ union bpf_attr { · 851 unmodified lines | ||
| 1973 | 1974 | * program. |
| 1974 | 1975 | * Return |
| 1975 | 1976 | * The SMP id of the processor running the program. |
| 1977 | + * Attributes | |
| 1978 | + * __bpf_fastcall | |
| 1976 | 1979 | * |
| 1977 | 1980 | * long bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags) |
| 1978 | 1981 | * Description |
| @@ -3104,10 +3107,6 @@ union bpf_attr { · 1125 unmodified lines | ||
| 3104 | 3107 | * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration |
| 3105 | 3108 | * option, and in this case it only works on functions tagged with |
| 3106 | 3109 | * **ALLOW_ERROR_INJECTION** in the kernel code. |
| 3107 | − * | |
| 3108 | − * Also, the helper is only available for the architectures having | |
| 3109 | − * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, | |
| 3110 | − * x86 architecture is the only one to support this feature. | |
| 3111 | 3110 | * Return |
| 3112 | 3111 | * 0 |
| 3113 | 3112 | * |
| @@ -5372,7 +5371,7 @@ union bpf_attr { · 2258 unmodified lines | ||
| 5372 | 5371 | * Currently, the **flags** must be 0. Currently, nr_loops is |
| 5373 | 5372 | * limited to 1 << 23 (~8 million) loops. |
| 5374 | 5373 | * |
| 5375 | − * long (\*callback_fn)(u32 index, void \*ctx); | |
| 5374 | + * long (\*callback_fn)(u64 index, void \*ctx); | |
| 5376 | 5375 | * |
| 5377 | 5376 | * where **index** is the current index in the loop. The index |
| 5378 | 5377 | * is zero-indexed. |
| @@ -16,7 +16,7 @@ obj-$(CONFIG_BPF_SYSCALL) += disasm.o mprog.o · 15 unmodified lines | ||
| 16 | 16 | obj-$(CONFIG_BPF_JIT) += trampoline.o |
| 17 | 17 | obj-$(CONFIG_BPF_SYSCALL) += btf.o memalloc.o |
| 18 | 18 | ifeq ($(CONFIG_MMU)$(CONFIG_64BIT),yy) |
| 19 | −obj-$(CONFIG_BPF_SYSCALL) += arena.o | |
| 19 | +obj-$(CONFIG_BPF_SYSCALL) += arena.o range_tree.o | |
| 20 | 20 | endif |
| 21 | 21 | obj-$(CONFIG_BPF_JIT) += dispatcher.o |
| 22 | 22 | ifeq ($(CONFIG_NET),y) |
| @@ -52,3 +52,4 @@ obj-$(CONFIG_BPF_PRELOAD) += preload/ · 29 unmodified lines | ||
| 52 | 52 | obj-$(CONFIG_BPF_SYSCALL) += relo_core.o |
| 53 | 53 | obj-$(CONFIG_BPF_SYSCALL) += btf_iter.o |
| 54 | 54 | obj-$(CONFIG_BPF_SYSCALL) += btf_relocate.o |
| 55 | +obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o | |
| @@ -3,9 +3,11 @@ · 2 unmodified lines | ||
| 3 | 3 | #include <linux/bpf.h> |
| 4 | 4 | #include <linux/btf.h> |
| 5 | 5 | #include <linux/err.h> |
| 6 | +#include "linux/filter.h" | |
| 6 | 7 | #include <linux/btf_ids.h> |
| 7 | 8 | #include <linux/vmalloc.h> |
| 8 | 9 | #include <linux/pagemap.h> |
| 10 | +#include "range_tree.h" | |
| 9 | 11 | |
| 10 | 12 | /* |
| 11 | 13 | * bpf_arena is a sparsely populated shared memory region between bpf program and |
| @@ -45,7 +47,7 @@ struct bpf_arena { · 33 unmodified lines | ||
| 45 | 47 | u64 user_vm_start; |
| 46 | 48 | u64 user_vm_end; |
| 47 | 49 | struct vm_struct *kern_vm; |
| 48 | − struct maple_tree mt; | |
| 50 | + struct range_tree rt; | |
| 49 | 51 | struct list_head vma_list; |
| 50 | 52 | struct mutex lock; |
| 51 | 53 | }; |
| @@ -98,6 +100,9 @@ static struct bpf_map *arena_map_alloc(union bpf_attr *attr) · 46 unmodified lines | ||
| 98 | 100 | u64 vm_range; |
| 99 | 101 | int err = -ENOMEM; |
| 100 | 102 | |
| 103 | + if (!bpf_jit_supports_arena()) | |
| 104 | + return ERR_PTR(-EOPNOTSUPP); | |
| 105 | + | |
| 101 | 106 | if (attr->key_size || attr->value_size || attr->max_entries == 0 || |
| 102 | 107 | /* BPF_F_MMAPABLE must be set */ |
| 103 | 108 | !(attr->map_flags & BPF_F_MMAPABLE) || |
| @@ -132,7 +137,8 @@ static struct bpf_map *arena_map_alloc(union bpf_attr *attr) · 28 unmodified lines | ||
| 132 | 137 | |
| 133 | 138 | INIT_LIST_HEAD(&arena->vma_list); |
| 134 | 139 | bpf_map_init_from_attr(&arena->map, attr); |
| 135 | − mt_init_flags(&arena->mt, MT_FLAGS_ALLOC_RANGE); | |
| 140 | + range_tree_init(&arena->rt); | |
| 141 | + range_tree_set(&arena->rt, 0, attr->max_entries); | |
| 136 | 142 | mutex_init(&arena->lock); |
| 137 | 143 | |
| 138 | 144 | return &arena->map; |
| @@ -183,7 +189,7 @@ static void arena_map_free(struct bpf_map *map) · 44 unmodified lines | ||
| 183 | 189 | apply_to_existing_page_range(&init_mm, bpf_arena_get_kern_vm_start(arena), |
| 184 | 190 | KERN_VM_SZ - GUARD_SZ, existing_page_cb, NULL); |
| 185 | 191 | free_vm_area(arena->kern_vm); |
| 186 | − mtree_destroy(&arena->mt); | |
| 192 | + range_tree_destroy(&arena->rt); | |
| 187 | 193 | bpf_map_area_free(arena); |
| 188 | 194 | } |
| 189 | 195 | |
| @@ -274,20 +280,20 @@ static vm_fault_t arena_vm_fault(struct vm_fault *vmf) · 84 unmodified lines | ||
| 274 | 280 | /* User space requested to segfault when page is not allocated by bpf prog */ |
| 275 | 281 | return VM_FAULT_SIGSEGV; |
| 276 | 282 | |
| 277 | − ret = mtree_insert(&arena->mt, vmf->pgoff, MT_ENTRY, GFP_KERNEL); | |
| 283 | + ret = range_tree_clear(&arena->rt, vmf->pgoff, 1); | |
| 278 | 284 | if (ret) |
| 279 | 285 | return VM_FAULT_SIGSEGV; |
| 280 | 286 | |
| 281 | 287 | /* Account into memcg of the process that created bpf_arena */ |
| 282 | 288 | ret = bpf_map_alloc_pages(map, GFP_KERNEL | __GFP_ZERO, NUMA_NO_NODE, 1, &page); |
| 283 | 289 | if (ret) { |
| 284 | − mtree_erase(&arena->mt, vmf->pgoff); | |
| 290 | + range_tree_set(&arena->rt, vmf->pgoff, 1); | |
| 285 | 291 | return VM_FAULT_SIGSEGV; |
| 286 | 292 | } |
| 287 | 293 | |
| 288 | 294 | ret = vm_area_map_pages(arena->kern_vm, kaddr, kaddr + PAGE_SIZE, &page); |
| 289 | 295 | if (ret) { |
| 290 | − mtree_erase(&arena->mt, vmf->pgoff); | |
| 296 | + range_tree_set(&arena->rt, vmf->pgoff, 1); | |
| 291 | 297 | __free_page(page); |
| 292 | 298 | return VM_FAULT_SIGSEGV; |
| 293 | 299 | } |
| @@ -444,12 +450,16 @@ static long arena_alloc_pages(struct bpf_arena *arena, long uaddr, long page_cnt · 150 unmodified lines | ||
| 444 | 450 | |
| 445 | 451 | guard(mutex)(&arena->lock); |
| 446 | 452 | |
| 447 | − if (uaddr) | |
| 448 | − ret = mtree_insert_range(&arena->mt, pgoff, pgoff + page_cnt - 1, | |
| 449 | − MT_ENTRY, GFP_KERNEL); | |
| 450 | − else | |
| 451 | − ret = mtree_alloc_range(&arena->mt, &pgoff, MT_ENTRY, | |
| 452 | − page_cnt, 0, page_cnt_max - 1, GFP_KERNEL); | |
| 453 | + if (uaddr) { | |
| 454 | + ret = is_range_tree_set(&arena->rt, pgoff, page_cnt); | |
| 455 | + if (ret) | |
| 456 | + goto out_free_pages; | |
| 457 | + ret = range_tree_clear(&arena->rt, pgoff, page_cnt); | |
| 458 | + } else { | |
| 459 | + ret = pgoff = range_tree_find(&arena->rt, page_cnt); | |
| 460 | + if (pgoff >= 0) | |
| 461 | + ret = range_tree_clear(&arena->rt, pgoff, page_cnt); | |
| 462 | + } | |
| 453 | 463 | if (ret) |
| 454 | 464 | goto out_free_pages; |
| 455 | 465 | |
| @@ -476,7 +486,7 @@ static long arena_alloc_pages(struct bpf_arena *arena, long uaddr, long page_cnt · 20 unmodified lines | ||
| 476 | 486 | kvfree(pages); |
| 477 | 487 | return clear_lo32(arena->user_vm_start) + uaddr32; |
| 478 | 488 | out: |
| 479 | − mtree_erase(&arena->mt, pgoff); | |
| 489 | + range_tree_set(&arena->rt, pgoff, page_cnt); | |
| 480 | 490 | out_free_pages: |
| 481 | 491 | kvfree(pages); |
| 482 | 492 | return 0; |
| @@ -516,7 +526,7 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt) · 33 unmodified lines | ||
| 516 | 526 | |
| 517 | 527 | pgoff = compute_pgoff(arena, uaddr); |
| 518 | 528 | /* clear range */ |
| 519 | − mtree_store_range(&arena->mt, pgoff, pgoff + page_cnt - 1, NULL, GFP_KERNEL); | |
| 529 | + range_tree_set(&arena->rt, pgoff, page_cnt); | |
| 520 | 530 | |
| 521 | 531 | if (page_cnt > 1) |
| 522 | 532 | /* bulk zap if multiple pages being freed */ |
| @@ -947,22 +947,44 @@ static void *prog_fd_array_get_ptr(struct bpf_map *map, · 946 unmodified lines | ||
| 947 | 947 | struct file *map_file, int fd) |
| 948 | 948 | { |
| 949 | 949 | struct bpf_prog *prog = bpf_prog_get(fd); |
| 950 | + bool is_extended; | |
| 950 | 951 | |
| 951 | 952 | if (IS_ERR(prog)) |
| 952 | 953 | return prog; |
| 953 | 954 | |
| 954 | − if (!bpf_prog_map_compatible(map, prog)) { | |
| 955 | + if (prog->type == BPF_PROG_TYPE_EXT || | |
| 956 | + !bpf_prog_map_compatible(map, prog)) { | |
| 955 | 957 | bpf_prog_put(prog); |
| 956 | 958 | return ERR_PTR(-EINVAL); |
| 957 | 959 | } |
| 958 | 960 | |
| 961 | + mutex_lock(&prog->aux->ext_mutex); | |
| 962 | + is_extended = prog->aux->is_extended; | |
| 963 | + if (!is_extended) | |
| 964 | + prog->aux->prog_array_member_cnt++; | |
| 965 | + mutex_unlock(&prog->aux->ext_mutex); | |
| 966 | + if (is_extended) { | |
| 967 | + /* Extended prog can not be tail callee. It's to prevent a | |
| 968 | + * potential infinite loop like: | |
| 969 | + * tail callee prog entry -> tail callee prog subprog -> | |
| 970 | + * freplace prog entry --tailcall-> tail callee prog entry. | |
| 971 | + */ | |
| 972 | + bpf_prog_put(prog); | |
| 973 | + return ERR_PTR(-EBUSY); | |
| 974 | + } | |
| 975 | + | |
| 959 | 976 | return prog; |
| 960 | 977 | } |
| 961 | 978 | |
| 962 | 979 | static void prog_fd_array_put_ptr(struct bpf_map *map, void *ptr, bool need_defer) |
| 963 | 980 | { |
| 981 | + struct bpf_prog *prog = ptr; | |
| 982 | + | |
| 983 | + mutex_lock(&prog->aux->ext_mutex); | |
| 984 | + prog->aux->prog_array_member_cnt--; | |
| 985 | + mutex_unlock(&prog->aux->ext_mutex); | |
| 964 | 986 | /* bpf_prog is freed after one RCU or tasks trace grace period */ |
| 965 | − bpf_prog_put(ptr); | |
| 987 | + bpf_prog_put(prog); | |
| 966 | 988 | } |
| 967 | 989 | |
| 968 | 990 | static u32 prog_fd_array_sys_lookup_elem(void *ptr) |
| @@ -107,7 +107,7 @@ static long bpf_cgrp_storage_update_elem(struct bpf_map *map, void *key, · 106 unmodified lines | ||
| 107 | 107 | |
| 108 | 108 | bpf_cgrp_storage_lock(); |
| 109 | 109 | sdata = bpf_local_storage_update(cgroup, (struct bpf_local_storage_map *)map, |
| 110 | − value, map_flags, GFP_ATOMIC); | |
| 110 | + value, map_flags, false, GFP_ATOMIC); | |
| 111 | 111 | bpf_cgrp_storage_unlock(); |
| 112 | 112 | cgroup_put(cgroup); |
| 113 | 113 | return PTR_ERR_OR_ZERO(sdata); |
| @@ -181,7 +181,7 @@ BPF_CALL_5(bpf_cgrp_storage_get, struct bpf_map *, map, struct cgroup *, cgroup, · 67 unmodified lines | ||
| 181 | 181 | if (!percpu_ref_is_dying(&cgroup->self.refcnt) && |
| 182 | 182 | (flags & BPF_LOCAL_STORAGE_GET_F_CREATE)) |
| 183 | 183 | sdata = bpf_local_storage_update(cgroup, (struct bpf_local_storage_map *)map, |
| 184 | − value, BPF_NOEXIST, gfp_flags); | |
| 184 | + value, BPF_NOEXIST, false, gfp_flags); | |
| 185 | 185 | |
| 186 | 186 | unlock: |
| 187 | 187 | bpf_cgrp_storage_unlock(); |
| @@ -99,7 +99,7 @@ static long bpf_fd_inode_storage_update_elem(struct bpf_map *map, void *key, · 98 unmodified lines | ||
| 99 | 99 | |
| 100 | 100 | sdata = bpf_local_storage_update(file_inode(fd_file(f)), |
| 101 | 101 | (struct bpf_local_storage_map *)map, |
| 102 | − value, map_flags, GFP_ATOMIC); | |
| 102 | + value, map_flags, false, GFP_ATOMIC); | |
| 103 | 103 | return PTR_ERR_OR_ZERO(sdata); |
| 104 | 104 | } |
| 105 | 105 | |
| @@ -153,7 +153,7 @@ BPF_CALL_5(bpf_inode_storage_get, struct bpf_map *, map, struct inode *, inode, · 47 unmodified lines | ||
| 153 | 153 | if (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) { |
| 154 | 154 | sdata = bpf_local_storage_update( |
| 155 | 155 | inode, (struct bpf_local_storage_map *)map, value, |
| 156 | − BPF_NOEXIST, gfp_flags); | |
| 156 | + BPF_NOEXIST, false, gfp_flags); | |
| 157 | 157 | return IS_ERR(sdata) ? (unsigned long)NULL : |
| 158 | 158 | (unsigned long)sdata->data; |
| 159 | 159 | } |
| @@ -73,7 +73,7 @@ static bool selem_linked_to_map(const struct bpf_local_storage_elem *selem) · 72 unmodified lines | ||
| 73 | 73 | |
| 74 | 74 | struct bpf_local_storage_elem * |
| 75 | 75 | bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner, |
| 76 | − void *value, bool charge_mem, gfp_t gfp_flags) | |
| 76 | + void *value, bool charge_mem, bool swap_uptrs, gfp_t gfp_flags) | |
| 77 | 77 | { |
| 78 | 78 | struct bpf_local_storage_elem *selem; |
| 79 | 79 | |
| @@ -99,9 +99,12 @@ bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner, · 19 unmodified lines | ||
| 99 | 99 | } |
| 100 | 100 | |
| 101 | 101 | if (selem) { |
| 102 | − if (value) | |
| 102 | + if (value) { | |
| 103 | + /* No need to call check_and_init_map_value as memory is zero init */ | |
| 103 | 104 | copy_map_value(&smap->map, SDATA(selem)->data, value); |
| 104 | − /* No need to call check_and_init_map_value as memory is zero init */ | |
| 105 | + if (swap_uptrs) | |
| 106 | + bpf_obj_swap_uptrs(smap->map.record, SDATA(selem)->data, value); | |
| 107 | + } | |
| 105 | 108 | return selem; |
| 106 | 109 | } |
| 107 | 110 | |
| @@ -209,8 +212,12 @@ static void __bpf_selem_free(struct bpf_local_storage_elem *selem, · 101 unmodified lines | ||
| 209 | 212 | static void bpf_selem_free_rcu(struct rcu_head *rcu) |
| 210 | 213 | { |
| 211 | 214 | struct bpf_local_storage_elem *selem; |
| 215 | + struct bpf_local_storage_map *smap; | |
| 212 | 216 | |
| 213 | 217 | selem = container_of(rcu, struct bpf_local_storage_elem, rcu); |
| 218 | + /* The bpf_local_storage_map_free will wait for rcu_barrier */ | |
| 219 | + smap = rcu_dereference_check(SDATA(selem)->smap, 1); | |
| 220 | + bpf_obj_free_fields(smap->map.record, SDATA(selem)->data); | |
| 214 | 221 | bpf_mem_cache_raw_free(selem); |
| 215 | 222 | } |
| 216 | 223 | |
| @@ -226,16 +233,25 @@ void bpf_selem_free(struct bpf_local_storage_elem *selem, · 9 unmodified lines | ||
| 226 | 233 | struct bpf_local_storage_map *smap, |
| 227 | 234 | bool reuse_now) |
| 228 | 235 | { |
| 229 | − bpf_obj_free_fields(smap->map.record, SDATA(selem)->data); | |
| 230 | − | |
| 231 | 236 | if (!smap->bpf_ma) { |
| 237 | + /* Only task storage has uptrs and task storage | |
| 238 | + * has moved to bpf_mem_alloc. Meaning smap->bpf_ma == true | |
| 239 | + * for task storage, so this bpf_obj_free_fields() won't unpin | |
| 240 | + * any uptr. | |
| 241 | + */ | |
| 242 | + bpf_obj_free_fields(smap->map.record, SDATA(selem)->data); | |
| 232 | 243 | __bpf_selem_free(selem, reuse_now); |
| 233 | 244 | return; |
| 234 | 245 | } |
| 235 | 246 | |
| 236 | − if (!reuse_now) { | |
| 237 | − call_rcu_tasks_trace(&selem->rcu, bpf_selem_free_trace_rcu); | |
| 238 | − } else { | |
| 247 | + if (reuse_now) { | |
| 248 | + /* reuse_now == true only happens when the storage owner | |
| 249 | + * (e.g. task_struct) is being destructed or the map itself | |
| 250 | + * is being destructed (ie map_free). In both cases, | |
| 251 | + * no bpf prog can have a hold on the selem. It is | |
| 252 | + * safe to unpin the uptrs and free the selem now. | |
| 253 | + */ | |
| 254 | + bpf_obj_free_fields(smap->map.record, SDATA(selem)->data); | |
| 239 | 255 | /* Instead of using the vanilla call_rcu(), |
| 240 | 256 | * bpf_mem_cache_free will be able to reuse selem |
| 241 | 257 | * immediately. |
| @@ -243,6 +259,26 @@ void bpf_selem_free(struct bpf_local_storage_elem *selem, · 1 unmodified line | ||
| 243 | 259 | migrate_disable(); |
| 244 | 260 | bpf_mem_cache_free(&smap->selem_ma, selem); |
| 245 | 261 | migrate_enable(); |
| 262 | + return; | |
| 263 | + } | |
| 264 | + | |
| 265 | + call_rcu_tasks_trace(&selem->rcu, bpf_selem_free_trace_rcu); | |
| 266 | +} | |
| 267 | + | |
| 268 | +static void bpf_selem_free_list(struct hlist_head *list, bool reuse_now) | |
| 269 | +{ | |
| 270 | + struct bpf_local_storage_elem *selem; | |
| 271 | + struct bpf_local_storage_map *smap; | |
| 272 | + struct hlist_node *n; | |
| 273 | + | |
| 274 | + /* The "_safe" iteration is needed. | |
| 275 | + * The loop is not removing the selem from the list | |
| 276 | + * but bpf_selem_free will use the selem->rcu_head | |
| 277 | + * which is union-ized with the selem->free_node. | |
| 278 | + */ | |
| 279 | + hlist_for_each_entry_safe(selem, n, list, free_node) { | |
| 280 | + smap = rcu_dereference_check(SDATA(selem)->smap, bpf_rcu_lock_held()); | |
| 281 | + bpf_selem_free(selem, smap, reuse_now); | |
| 246 | 282 | } |
| 247 | 283 | } |
| 248 | 284 | |
| @@ -252,7 +288,7 @@ void bpf_selem_free(struct bpf_local_storage_elem *selem, · 3 unmodified lines | ||
| 252 | 288 | */ |
| 253 | 289 | static bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage, |
| 254 | 290 | struct bpf_local_storage_elem *selem, |
| 255 | − bool uncharge_mem, bool reuse_now) | |
| 291 | + bool uncharge_mem, struct hlist_head *free_selem_list) | |
| 256 | 292 | { |
| 257 | 293 | struct bpf_local_storage_map *smap; |
| 258 | 294 | bool free_local_storage; |
| @@ -296,7 +332,7 @@ static bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_stor · 37 unmodified lines | ||
| 296 | 332 | SDATA(selem)) |
| 297 | 333 | RCU_INIT_POINTER(local_storage->cache[smap->cache_idx], NULL); |
| 298 | 334 | |
| 299 | − bpf_selem_free(selem, smap, reuse_now); | |
| 335 | + hlist_add_head(&selem->free_node, free_selem_list); | |
| 300 | 336 | |
| 301 | 337 | if (rcu_access_pointer(local_storage->smap) == smap) |
| 302 | 338 | RCU_INIT_POINTER(local_storage->smap, NULL); |
| @@ -345,6 +381,7 @@ static void bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem, · 42 unmodified lines | ||
| 345 | 381 | struct bpf_local_storage_map *storage_smap; |
| 346 | 382 | struct bpf_local_storage *local_storage; |
| 347 | 383 | bool bpf_ma, free_local_storage = false; |
| 384 | + HLIST_HEAD(selem_free_list); | |
| 348 | 385 | unsigned long flags; |
| 349 | 386 | |
| 350 | 387 | if (unlikely(!selem_linked_to_storage_lockless(selem))) |
| @@ -360,9 +397,11 @@ static void bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem, · 9 unmodified lines | ||
| 360 | 397 | raw_spin_lock_irqsave(&local_storage->lock, flags); |
| 361 | 398 | if (likely(selem_linked_to_storage(selem))) |
| 362 | 399 | free_local_storage = bpf_selem_unlink_storage_nolock( |
| 363 | − local_storage, selem, true, reuse_now); | |
| 400 | + local_storage, selem, true, &selem_free_list); | |
| 364 | 401 | raw_spin_unlock_irqrestore(&local_storage->lock, flags); |
| 365 | 402 | |
| 403 | + bpf_selem_free_list(&selem_free_list, reuse_now); | |
| 404 | + | |
| 366 | 405 | if (free_local_storage) |
| 367 | 406 | bpf_local_storage_free(local_storage, storage_smap, bpf_ma, reuse_now); |
| 368 | 407 | } |
| @@ -524,11 +563,12 @@ uncharge: · 155 unmodified lines | ||
| 524 | 563 | */ |
| 525 | 564 | struct bpf_local_storage_data * |
| 526 | 565 | bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, |
| 527 | − void *value, u64 map_flags, gfp_t gfp_flags) | |
| 566 | + void *value, u64 map_flags, bool swap_uptrs, gfp_t gfp_flags) | |
| 528 | 567 | { |
| 529 | 568 | struct bpf_local_storage_data *old_sdata = NULL; |
| 530 | 569 | struct bpf_local_storage_elem *alloc_selem, *selem = NULL; |
| 531 | 570 | struct bpf_local_storage *local_storage; |
| 571 | + HLIST_HEAD(old_selem_free_list); | |
| 532 | 572 | unsigned long flags; |
| 533 | 573 | int err; |
| 534 | 574 | |
| @@ -550,7 +590,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, · 15 unmodified lines | ||
| 550 | 590 | if (err) |
| 551 | 591 | return ERR_PTR(err); |
| 552 | 592 | |
| 553 | − selem = bpf_selem_alloc(smap, owner, value, true, gfp_flags); | |
| 593 | + selem = bpf_selem_alloc(smap, owner, value, true, swap_uptrs, gfp_flags); | |
| 554 | 594 | if (!selem) |
| 555 | 595 | return ERR_PTR(-ENOMEM); |
| 556 | 596 | |
| @@ -584,7 +624,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, · 27 unmodified lines | ||
| 584 | 624 | /* A lookup has just been done before and concluded a new selem is |
| 585 | 625 | * needed. The chance of an unnecessary alloc is unlikely. |
| 586 | 626 | */ |
| 587 | − alloc_selem = selem = bpf_selem_alloc(smap, owner, value, true, gfp_flags); | |
| 627 | + alloc_selem = selem = bpf_selem_alloc(smap, owner, value, true, swap_uptrs, gfp_flags); | |
| 588 | 628 | if (!alloc_selem) |
| 589 | 629 | return ERR_PTR(-ENOMEM); |
| 590 | 630 | |
| @@ -624,11 +664,12 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, · 33 unmodified lines | ||
| 624 | 664 | if (old_sdata) { |
| 625 | 665 | bpf_selem_unlink_map(SELEM(old_sdata)); |
| 626 | 666 | bpf_selem_unlink_storage_nolock(local_storage, SELEM(old_sdata), |
| 627 | − true, false); | |
| 667 | + true, &old_selem_free_list); | |
| 628 | 668 | } |
| 629 | 669 | |
| 630 | 670 | unlock: |
| 631 | 671 | raw_spin_unlock_irqrestore(&local_storage->lock, flags); |
| 672 | + bpf_selem_free_list(&old_selem_free_list, false); | |
| 632 | 673 | if (alloc_selem) { |
| 633 | 674 | mem_uncharge(smap, owner, smap->elem_size); |
| 634 | 675 | bpf_selem_free(alloc_selem, smap, true); |
| @@ -706,6 +747,7 @@ void bpf_local_storage_destroy(struct bpf_local_storage *local_storage) · 71 unmodified lines | ||
| 706 | 747 | struct bpf_local_storage_map *storage_smap; |
| 707 | 748 | struct bpf_local_storage_elem *selem; |
| 708 | 749 | bool bpf_ma, free_storage = false; |
| 750 | + HLIST_HEAD(free_selem_list); | |
| 709 | 751 | struct hlist_node *n; |
| 710 | 752 | unsigned long flags; |
| 711 | 753 | |
| @@ -734,10 +776,12 @@ void bpf_local_storage_destroy(struct bpf_local_storage *local_storage) · 22 unmodified lines | ||
| 734 | 776 | * of the loop will set the free_cgroup_storage to true. |
| 735 | 777 | */ |
| 736 | 778 | free_storage = bpf_selem_unlink_storage_nolock( |
| 737 | − local_storage, selem, true, true); | |
| 779 | + local_storage, selem, true, &free_selem_list); | |
| 738 | 780 | } |
| 739 | 781 | raw_spin_unlock_irqrestore(&local_storage->lock, flags); |
| 740 | 782 | |
| 783 | + bpf_selem_free_list(&free_selem_list, true); | |
| 784 | + | |
| 741 | 785 | if (free_storage) |
| 742 | 786 | bpf_local_storage_free(local_storage, storage_smap, bpf_ma, true); |
| 743 | 787 | } |
| @@ -883,6 +927,9 @@ void bpf_local_storage_map_free(struct bpf_map *map, · 139 unmodified lines | ||
| 883 | 927 | synchronize_rcu(); |
| 884 | 928 | |
| 885 | 929 | if (smap->bpf_ma) { |
| 930 | + rcu_barrier_tasks_trace(); | |
| 931 | + if (!rcu_trace_implies_rcu_gp()) | |
| 932 | + rcu_barrier(); | |
| 886 | 933 | bpf_mem_alloc_destroy(&smap->selem_ma); |
| 887 | 934 | bpf_mem_alloc_destroy(&smap->storage_ma); |
| 888 | 935 | } |
| @@ -23,7 +23,6 @@ struct bpf_struct_ops_value { · 22 unmodified lines | ||
| 23 | 23 | |
| 24 | 24 | struct bpf_struct_ops_map { |
| 25 | 25 | struct bpf_map map; |
| 26 | − struct rcu_head rcu; | |
| 27 | 26 | const struct bpf_struct_ops_desc *st_ops_desc; |
| 28 | 27 | /* protect map_update */ |
| 29 | 28 | struct mutex lock; |
| @@ -32,7 +31,9 @@ struct bpf_struct_ops_map { · 2 unmodified lines | ||
| 32 | 31 | * (in kvalue.data). |
| 33 | 32 | */ |
| 34 | 33 | struct bpf_link **links; |
| 35 | − u32 links_cnt; | |
| 34 | + /* ksyms for bpf trampolines */ | |
| 35 | + struct bpf_ksym **ksyms; | |
| 36 | + u32 funcs_cnt; | |
| 36 | 37 | u32 image_pages_cnt; |
| 37 | 38 | /* image_pages is an array of pages that has all the trampolines |
| 38 | 39 | * that stores the func args before calling the bpf_prog. |
| @@ -481,11 +482,11 @@ static void bpf_struct_ops_map_put_progs(struct bpf_struct_ops_map *st_map) · 442 unmodified lines | ||
| 481 | 482 | { |
| 482 | 483 | u32 i; |
| 483 | 484 | |
| 484 | − for (i = 0; i < st_map->links_cnt; i++) { | |
| 485 | − if (st_map->links[i]) { | |
| 486 | − bpf_link_put(st_map->links[i]); | |
| 487 | − st_map->links[i] = NULL; | |
| 488 | − } | |
| 485 | + for (i = 0; i < st_map->funcs_cnt; i++) { | |
| 486 | + if (!st_map->links[i]) | |
| 487 | + break; | |
| 488 | + bpf_link_put(st_map->links[i]); | |
| 489 | + st_map->links[i] = NULL; | |
| 489 | 490 | } |
| 490 | 491 | } |
| 491 | 492 | |
| @@ -586,6 +587,49 @@ int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_links *tlinks, · 94 unmodified lines | ||
| 586 | 587 | return 0; |
| 587 | 588 | } |
| 588 | 589 | |
| 590 | +static void bpf_struct_ops_ksym_init(const char *tname, const char *mname, | |
| 591 | + void *image, unsigned int size, | |
| 592 | + struct bpf_ksym *ksym) | |
| 593 | +{ | |
| 594 | + snprintf(ksym->name, KSYM_NAME_LEN, "bpf__%s_%s", tname, mname); | |
| 595 | + INIT_LIST_HEAD_RCU(&ksym->lnode); | |
| 596 | + bpf_image_ksym_init(image, size, ksym); | |
| 597 | +} | |
| 598 | + | |
| 599 | +static void bpf_struct_ops_map_add_ksyms(struct bpf_struct_ops_map *st_map) | |
| 600 | +{ | |
| 601 | + u32 i; | |
| 602 | + | |
| 603 | + for (i = 0; i < st_map->funcs_cnt; i++) { | |
| 604 | + if (!st_map->ksyms[i]) | |
| 605 | + break; | |
| 606 | + bpf_image_ksym_add(st_map->ksyms[i]); | |
| 607 | + } | |
| 608 | +} | |
| 609 | + | |
| 610 | +static void bpf_struct_ops_map_del_ksyms(struct bpf_struct_ops_map *st_map) | |
| 611 | +{ | |
| 612 | + u32 i; | |
| 613 | + | |
| 614 | + for (i = 0; i < st_map->funcs_cnt; i++) { | |
| 615 | + if (!st_map->ksyms[i]) | |
| 616 | + break; | |
| 617 | + bpf_image_ksym_del(st_map->ksyms[i]); | |
| 618 | + } | |
| 619 | +} | |
| 620 | + | |
| 621 | +static void bpf_struct_ops_map_free_ksyms(struct bpf_struct_ops_map *st_map) | |
| 622 | +{ | |
| 623 | + u32 i; | |
| 624 | + | |
| 625 | + for (i = 0; i < st_map->funcs_cnt; i++) { | |
| 626 | + if (!st_map->ksyms[i]) | |
| 627 | + break; | |
| 628 | + kfree(st_map->ksyms[i]); | |
| 629 | + st_map->ksyms[i] = NULL; | |
| 630 | + } | |
| 631 | +} | |
| 632 | + | |
| 589 | 633 | static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, |
| 590 | 634 | void *value, u64 flags) |
| 591 | 635 | { |
| @@ -601,6 +645,9 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, · 9 unmodified lines | ||
| 601 | 645 | int prog_fd, err; |
| 602 | 646 | u32 i, trampoline_start, image_off = 0; |
| 603 | 647 | void *cur_image = NULL, *image = NULL; |
| 648 | + struct bpf_link **plink; | |
| 649 | + struct bpf_ksym **pksym; | |
| 650 | + const char *tname, *mname; | |
| 604 | 651 | |
| 605 | 652 | if (flags) |
| 606 | 653 | return -EINVAL; |
| @@ -639,14 +686,19 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, · 32 unmodified lines | ||
| 639 | 686 | udata = &uvalue->data; |
| 640 | 687 | kdata = &kvalue->data; |
| 641 | 688 | |
| 689 | + plink = st_map->links; | |
| 690 | + pksym = st_map->ksyms; | |
| 691 | + tname = btf_name_by_offset(st_map->btf, t->name_off); | |
| 642 | 692 | module_type = btf_type_by_id(btf_vmlinux, st_ops_ids[IDX_MODULE_ID]); |
| 643 | 693 | for_each_member(i, t, member) { |
| 644 | 694 | const struct btf_type *mtype, *ptype; |
| 645 | 695 | struct bpf_prog *prog; |
| 646 | 696 | struct bpf_tramp_link *link; |
| 697 | + struct bpf_ksym *ksym; | |
| 647 | 698 | u32 moff; |
| 648 | 699 | |
| 649 | 700 | moff = __btf_member_bit_offset(t, member) / 8; |
| 701 | + mname = btf_name_by_offset(st_map->btf, member->name_off); | |
| 650 | 702 | ptype = btf_type_resolve_ptr(st_map->btf, member->type, NULL); |
| 651 | 703 | if (ptype == module_type) { |
| 652 | 704 | if (*(void **)(udata + moff)) |
| @@ -714,7 +766,14 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, · 61 unmodified lines | ||
| 714 | 766 | } |
| 715 | 767 | bpf_link_init(&link->link, BPF_LINK_TYPE_STRUCT_OPS, |
| 716 | 768 | &bpf_struct_ops_link_lops, prog); |
| 717 | − st_map->links[i] = &link->link; | |
| 769 | + *plink++ = &link->link; | |
| 770 | + | |
| 771 | + ksym = kzalloc(sizeof(*ksym), GFP_USER); | |
| 772 | + if (!ksym) { | |
| 773 | + err = -ENOMEM; | |
| 774 | + goto reset_unlock; | |
| 775 | + } | |
| 776 | + *pksym++ = ksym; | |
| 718 | 777 | |
| 719 | 778 | trampoline_start = image_off; |
| 720 | 779 | err = bpf_struct_ops_prepare_trampoline(tlinks, link, |
| @@ -735,6 +794,12 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, · 14 unmodified lines | ||
| 735 | 794 | |
| 736 | 795 | /* put prog_id to udata */ |
| 737 | 796 | *(unsigned long *)(udata + moff) = prog->aux->id; |
| 797 | + | |
| 798 | + /* init ksym for this trampoline */ | |
| 799 | + bpf_struct_ops_ksym_init(tname, mname, | |
| 800 | + image + trampoline_start, | |
| 801 | + image_off - trampoline_start, | |
| 802 | + ksym); | |
| 738 | 803 | } |
| 739 | 804 | |
| 740 | 805 | if (st_ops->validate) { |
| @@ -783,6 +848,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, · 42 unmodified lines | ||
| 783 | 848 | */ |
| 784 | 849 | |
| 785 | 850 | reset_unlock: |
| 851 | + bpf_struct_ops_map_free_ksyms(st_map); | |
| 786 | 852 | bpf_struct_ops_map_free_image(st_map); |
| 787 | 853 | bpf_struct_ops_map_put_progs(st_map); |
| 788 | 854 | memset(uvalue, 0, map->value_size); |
| @@ -790,6 +856,8 @@ reset_unlock: · 1 unmodified line | ||
| 790 | 856 | unlock: |
| 791 | 857 | kfree(tlinks); |
| 792 | 858 | mutex_unlock(&st_map->lock); |
| 859 | + if (!err) | |
| 860 | + bpf_struct_ops_map_add_ksyms(st_map); | |
| 793 | 861 | return err; |
| 794 | 862 | } |
| 795 | 863 | |
| @@ -849,7 +917,10 @@ static void __bpf_struct_ops_map_free(struct bpf_map *map) · 53 unmodified lines | ||
| 849 | 917 | |
| 850 | 918 | if (st_map->links) |
| 851 | 919 | bpf_struct_ops_map_put_progs(st_map); |
| 920 | + if (st_map->ksyms) | |
| 921 | + bpf_struct_ops_map_free_ksyms(st_map); | |
| 852 | 922 | bpf_map_area_free(st_map->links); |
| 923 | + bpf_map_area_free(st_map->ksyms); | |
| 853 | 924 | bpf_struct_ops_map_free_image(st_map); |
| 854 | 925 | bpf_map_area_free(st_map->uvalue); |
| 855 | 926 | bpf_map_area_free(st_map); |
| @@ -866,6 +937,8 @@ static void bpf_struct_ops_map_free(struct bpf_map *map) · 10 unmodified lines | ||
| 866 | 937 | if (btf_is_module(st_map->btf)) |
| 867 | 938 | module_put(st_map->st_ops_desc->st_ops->owner); |
| 868 | 939 | |
| 940 | + bpf_struct_ops_map_del_ksyms(st_map); | |
| 941 | + | |
| 869 | 942 | /* The struct_ops's function may switch to another struct_ops. |
| 870 | 943 | * |
| 871 | 944 | * For example, bpf_tcp_cc_x->init() may switch to |
| @@ -895,6 +968,19 @@ static int bpf_struct_ops_map_alloc_check(union bpf_attr *attr) · 23 unmodified lines | ||
| 895 | 968 | return 0; |
| 896 | 969 | } |
| 897 | 970 | |
| 971 | +static u32 count_func_ptrs(const struct btf *btf, const struct btf_type *t) | |
| 972 | +{ | |
| 973 | + int i; | |
| 974 | + u32 count; | |
| 975 | + const struct btf_member *member; | |
| 976 | + | |
| 977 | + count = 0; | |
| 978 | + for_each_member(i, t, member) | |
| 979 | + if (btf_type_resolve_func_ptr(btf, member->type, NULL)) | |
| 980 | + count++; | |
| 981 | + return count; | |
| 982 | +} | |
| 983 | + | |
| 898 | 984 | static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) |
| 899 | 985 | { |
| 900 | 986 | const struct bpf_struct_ops_desc *st_ops_desc; |
| @@ -961,11 +1047,15 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr) · 60 unmodified lines | ||
| 961 | 1047 | map = &st_map->map; |
| 962 | 1048 | |
| 963 | 1049 | st_map->uvalue = bpf_map_area_alloc(vt->size, NUMA_NO_NODE); |
| 964 | − st_map->links_cnt = btf_type_vlen(t); | |
| 1050 | + st_map->funcs_cnt = count_func_ptrs(btf, t); | |
| 965 | 1051 | st_map->links = |
| 966 | − bpf_map_area_alloc(st_map->links_cnt * sizeof(struct bpf_links *), | |
| 1052 | + bpf_map_area_alloc(st_map->funcs_cnt * sizeof(struct bpf_link *), | |
| 1053 | + NUMA_NO_NODE); | |
| 1054 | + | |
| 1055 | + st_map->ksyms = | |
| 1056 | + bpf_map_area_alloc(st_map->funcs_cnt * sizeof(struct bpf_ksym *), | |
| 967 | 1057 | NUMA_NO_NODE); |
| 968 | − if (!st_map->uvalue || !st_map->links) { | |
| 1058 | + if (!st_map->uvalue || !st_map->links || !st_map->ksyms) { | |
| 969 | 1059 | ret = -ENOMEM; |
| 970 | 1060 | goto errout_free; |
| 971 | 1061 | } |
| @@ -994,7 +1084,8 @@ static u64 bpf_struct_ops_map_mem_usage(const struct bpf_map *map) · 22 unmodified lines | ||
| 994 | 1084 | usage = sizeof(*st_map) + |
| 995 | 1085 | vt->size - sizeof(struct bpf_struct_ops_value); |
| 996 | 1086 | usage += vt->size; |
| 997 | − usage += btf_type_vlen(vt) * sizeof(struct bpf_links *); | |
| 1087 | + usage += st_map->funcs_cnt * sizeof(struct bpf_link *); | |
| 1088 | + usage += st_map->funcs_cnt * sizeof(struct bpf_ksym *); | |
| 998 | 1089 | usage += PAGE_SIZE; |
| 999 | 1090 | return usage; |
| 1000 | 1091 | } |
| @@ -128,6 +128,9 @@ static long bpf_pid_task_storage_update_elem(struct bpf_map *map, void *key, · 127 unmodified lines | ||
| 128 | 128 | struct pid *pid; |
| 129 | 129 | int fd, err; |
| 130 | 130 | |
| 131 | + if ((map_flags & BPF_F_LOCK) && btf_record_has_field(map->record, BPF_UPTR)) | |
| 132 | + return -EOPNOTSUPP; | |
| 133 | + | |
| 131 | 134 | fd = *(int *)key; |
| 132 | 135 | pid = pidfd_get_pid(fd, &f_flags); |
| 133 | 136 | if (IS_ERR(pid)) |
| @@ -146,7 +149,7 @@ static long bpf_pid_task_storage_update_elem(struct bpf_map *map, void *key, · 12 unmodified lines | ||
| 146 | 149 | bpf_task_storage_lock(); |
| 147 | 150 | sdata = bpf_local_storage_update( |
| 148 | 151 | task, (struct bpf_local_storage_map *)map, value, map_flags, |
| 149 | − GFP_ATOMIC); | |
| 152 | + true, GFP_ATOMIC); | |
| 150 | 153 | bpf_task_storage_unlock(); |
| 151 | 154 | |
| 152 | 155 | err = PTR_ERR_OR_ZERO(sdata); |
| @@ -218,7 +221,7 @@ static void *__bpf_task_storage_get(struct bpf_map *map, · 65 unmodified lines | ||
| 218 | 221 | (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) && nobusy) { |
| 219 | 222 | sdata = bpf_local_storage_update( |
| 220 | 223 | task, (struct bpf_local_storage_map *)map, value, |
| 221 | − BPF_NOEXIST, gfp_flags); | |
| 224 | + BPF_NOEXIST, false, gfp_flags); | |
| 222 | 225 | return IS_ERR(sdata) ? NULL : sdata->data; |
| 223 | 226 | } |
| 224 | 227 | |
| @@ -2808,7 +2808,7 @@ static void btf_ref_type_log(struct btf_verifier_env *env, · 2807 unmodified lines | ||
| 2808 | 2808 | btf_verifier_log(env, "type_id=%u", t->type); |
| 2809 | 2809 | } |
| 2810 | 2810 | |
| 2811 | −static struct btf_kind_operations modifier_ops = { | |
| 2811 | +static const struct btf_kind_operations modifier_ops = { | |
| 2812 | 2812 | .check_meta = btf_ref_type_check_meta, |
| 2813 | 2813 | .resolve = btf_modifier_resolve, |
| 2814 | 2814 | .check_member = btf_modifier_check_member, |
| @@ -2817,7 +2817,7 @@ static struct btf_kind_operations modifier_ops = { · 2 unmodified lines | ||
| 2817 | 2817 | .show = btf_modifier_show, |
| 2818 | 2818 | }; |
| 2819 | 2819 | |
| 2820 | −static struct btf_kind_operations ptr_ops = { | |
| 2820 | +static const struct btf_kind_operations ptr_ops = { | |
| 2821 | 2821 | .check_meta = btf_ref_type_check_meta, |
| 2822 | 2822 | .resolve = btf_ptr_resolve, |
| 2823 | 2823 | .check_member = btf_ptr_check_member, |
| @@ -2858,7 +2858,7 @@ static void btf_fwd_type_log(struct btf_verifier_env *env, · 34 unmodified lines | ||
| 2858 | 2858 | btf_verifier_log(env, "%s", btf_type_kflag(t) ? "union" : "struct"); |
| 2859 | 2859 | } |
| 2860 | 2860 | |
| 2861 | −static struct btf_kind_operations fwd_ops = { | |
| 2861 | +static const struct btf_kind_operations fwd_ops = { | |
| 2862 | 2862 | .check_meta = btf_fwd_check_meta, |
| 2863 | 2863 | .resolve = btf_df_resolve, |
| 2864 | 2864 | .check_member = btf_df_check_member, |
| @@ -3109,7 +3109,7 @@ static void btf_array_show(const struct btf *btf, const struct btf_type *t, · 244 unmodified lines | ||
| 3109 | 3109 | __btf_array_show(btf, t, type_id, data, bits_offset, show); |
| 3110 | 3110 | } |
| 3111 | 3111 | |
| 3112 | −static struct btf_kind_operations array_ops = { | |
| 3112 | +static const struct btf_kind_operations array_ops = { | |
| 3113 | 3113 | .check_meta = btf_array_check_meta, |
| 3114 | 3114 | .resolve = btf_array_resolve, |
| 3115 | 3115 | .check_member = btf_array_check_member, |
| @@ -3334,7 +3334,7 @@ static int btf_find_struct(const struct btf *btf, const struct btf_type *t, · 218 unmodified lines | ||
| 3334 | 3334 | } |
| 3335 | 3335 | |
| 3336 | 3336 | static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, |
| 3337 | − u32 off, int sz, struct btf_field_info *info) | |
| 3337 | + u32 off, int sz, struct btf_field_info *info, u32 field_mask) | |
| 3338 | 3338 | { |
| 3339 | 3339 | enum btf_field_type type; |
| 3340 | 3340 | u32 res_id; |
| @@ -3358,9 +3358,14 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, · 17 unmodified lines | ||
| 3358 | 3358 | type = BPF_KPTR_REF; |
| 3359 | 3359 | else if (!strcmp("percpu_kptr", __btf_name_by_offset(btf, t->name_off))) |
| 3360 | 3360 | type = BPF_KPTR_PERCPU; |
| 3361 | + else if (!strcmp("uptr", __btf_name_by_offset(btf, t->name_off))) | |
| 3362 | + type = BPF_UPTR; | |
| 3361 | 3363 | else |
| 3362 | 3364 | return -EINVAL; |
| 3363 | 3365 | |
| 3366 | + if (!(type & field_mask)) | |
| 3367 | + return BTF_FIELD_IGNORE; | |
| 3368 | + | |
| 3364 | 3369 | /* Get the base type */ |
| 3365 | 3370 | t = btf_type_skip_modifiers(btf, t->type, &res_id); |
| 3366 | 3371 | /* Only pointer to struct is allowed */ |
| @@ -3502,7 +3507,7 @@ static int btf_get_field_type(const struct btf *btf, const struct btf_type *var_ · 135 unmodified lines | ||
| 3502 | 3507 | field_mask_test_name(BPF_REFCOUNT, "bpf_refcount"); |
| 3503 | 3508 | |
| 3504 | 3509 | /* Only return BPF_KPTR when all other types with matchable names fail */ |
| 3505 | − if (field_mask & BPF_KPTR && !__btf_type_is_struct(var_type)) { | |
| 3510 | + if (field_mask & (BPF_KPTR | BPF_UPTR) && !__btf_type_is_struct(var_type)) { | |
| 3506 | 3511 | type = BPF_KPTR_REF; |
| 3507 | 3512 | goto end; |
| 3508 | 3513 | } |
| @@ -3535,6 +3540,7 @@ static int btf_repeat_fields(struct btf_field_info *info, int info_cnt, · 26 unmodified lines | ||
| 3535 | 3540 | case BPF_KPTR_UNREF: |
| 3536 | 3541 | case BPF_KPTR_REF: |
| 3537 | 3542 | case BPF_KPTR_PERCPU: |
| 3543 | + case BPF_UPTR: | |
| 3538 | 3544 | case BPF_LIST_HEAD: |
| 3539 | 3545 | case BPF_RB_ROOT: |
| 3540 | 3546 | break; |
| @@ -3667,8 +3673,9 @@ static int btf_find_field_one(const struct btf *btf, · 126 unmodified lines | ||
| 3667 | 3673 | case BPF_KPTR_UNREF: |
| 3668 | 3674 | case BPF_KPTR_REF: |
| 3669 | 3675 | case BPF_KPTR_PERCPU: |
| 3676 | + case BPF_UPTR: | |
| 3670 | 3677 | ret = btf_find_kptr(btf, var_type, off, sz, |
| 3671 | − info_cnt ? &info[0] : &tmp); | |
| 3678 | + info_cnt ? &info[0] : &tmp, field_mask); | |
| 3672 | 3679 | if (ret < 0) |
| 3673 | 3680 | return ret; |
| 3674 | 3681 | break; |
| @@ -3991,6 +3998,7 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type · 316 unmodified lines | ||
| 3991 | 3998 | case BPF_KPTR_UNREF: |
| 3992 | 3999 | case BPF_KPTR_REF: |
| 3993 | 4000 | case BPF_KPTR_PERCPU: |
| 4001 | + case BPF_UPTR: | |
| 3994 | 4002 | ret = btf_parse_kptr(btf, &rec->fields[i], &info_arr[i]); |
| 3995 | 4003 | if (ret < 0) |
| 3996 | 4004 | goto end; |
| @@ -4050,12 +4058,28 @@ int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec) · 53 unmodified lines | ||
| 4050 | 4058 | * Hence we only need to ensure that bpf_{list_head,rb_root} ownership |
| 4051 | 4059 | * does not form cycles. |
| 4052 | 4060 | */ |
| 4053 | − if (IS_ERR_OR_NULL(rec) || !(rec->field_mask & BPF_GRAPH_ROOT)) | |
| 4061 | + if (IS_ERR_OR_NULL(rec) || !(rec->field_mask & (BPF_GRAPH_ROOT | BPF_UPTR))) | |
| 4054 | 4062 | return 0; |
| 4055 | 4063 | for (i = 0; i < rec->cnt; i++) { |
| 4056 | 4064 | struct btf_struct_meta *meta; |
| 4065 | + const struct btf_type *t; | |
| 4057 | 4066 | u32 btf_id; |
| 4058 | 4067 | |
| 4068 | + if (rec->fields[i].type == BPF_UPTR) { | |
| 4069 | + /* The uptr only supports pinning one page and cannot | |
| 4070 | + * point to a kernel struct | |
| 4071 | + */ | |
| 4072 | + if (btf_is_kernel(rec->fields[i].kptr.btf)) | |
| 4073 | + return -EINVAL; | |
| 4074 | + t = btf_type_by_id(rec->fields[i].kptr.btf, | |
| 4075 | + rec->fields[i].kptr.btf_id); | |
| 4076 | + if (!t->size) | |
| 4077 | + return -EINVAL; | |
| 4078 | + if (t->size > PAGE_SIZE) | |
| 4079 | + return -E2BIG; | |
| 4080 | + continue; | |
| 4081 | + } | |
| 4082 | + | |
| 4059 | 4083 | if (!(rec->fields[i].type & BPF_GRAPH_ROOT)) |
| 4060 | 4084 | continue; |
| 4061 | 4085 | btf_id = rec->fields[i].graph_root.value_btf_id; |
| @@ -4191,7 +4215,7 @@ static void btf_struct_show(const struct btf *btf, const struct btf_type *t, · 129 unmodified lines | ||
| 4191 | 4215 | __btf_struct_show(btf, t, type_id, data, bits_offset, show); |
| 4192 | 4216 | } |
| 4193 | 4217 | |
| 4194 | −static struct btf_kind_operations struct_ops = { | |
| 4218 | +static const struct btf_kind_operations struct_ops = { | |
| 4195 | 4219 | .check_meta = btf_struct_check_meta, |
| 4196 | 4220 | .resolve = btf_struct_resolve, |
| 4197 | 4221 | .check_member = btf_struct_check_member, |
| @@ -4359,7 +4383,7 @@ static void btf_enum_show(const struct btf *btf, const struct btf_type *t, · 161 unmodified lines | ||
| 4359 | 4383 | btf_show_end_type(show); |
| 4360 | 4384 | } |
| 4361 | 4385 | |
| 4362 | −static struct btf_kind_operations enum_ops = { | |
| 4386 | +static const struct btf_kind_operations enum_ops = { | |
| 4363 | 4387 | .check_meta = btf_enum_check_meta, |
| 4364 | 4388 | .resolve = btf_df_resolve, |
| 4365 | 4389 | .check_member = btf_enum_check_member, |
| @@ -4462,7 +4486,7 @@ static void btf_enum64_show(const struct btf *btf, const struct btf_type *t, · 96 unmodified lines | ||
| 4462 | 4486 | btf_show_end_type(show); |
| 4463 | 4487 | } |
| 4464 | 4488 | |
| 4465 | −static struct btf_kind_operations enum64_ops = { | |
| 4489 | +static const struct btf_kind_operations enum64_ops = { | |
| 4466 | 4490 | .check_meta = btf_enum64_check_meta, |
| 4467 | 4491 | .resolve = btf_df_resolve, |
| 4468 | 4492 | .check_member = btf_enum_check_member, |
| @@ -4540,7 +4564,7 @@ done: · 71 unmodified lines | ||
| 4540 | 4564 | btf_verifier_log(env, ")"); |
| 4541 | 4565 | } |
| 4542 | 4566 | |
| 4543 | −static struct btf_kind_operations func_proto_ops = { | |
| 4567 | +static const struct btf_kind_operations func_proto_ops = { | |
| 4544 | 4568 | .check_meta = btf_func_proto_check_meta, |
| 4545 | 4569 | .resolve = btf_df_resolve, |
| 4546 | 4570 | /* |
| @@ -4598,7 +4622,7 @@ static int btf_func_resolve(struct btf_verifier_env *env, · 51 unmodified lines | ||
| 4598 | 4622 | return 0; |
| 4599 | 4623 | } |
| 4600 | 4624 | |
| 4601 | −static struct btf_kind_operations func_ops = { | |
| 4625 | +static const struct btf_kind_operations func_ops = { | |
| 4602 | 4626 | .check_meta = btf_func_check_meta, |
| 4603 | 4627 | .resolve = btf_func_resolve, |
| 4604 | 4628 | .check_member = btf_df_check_member, |
| @@ -5566,7 +5590,7 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf) · 961 unmodified lines | ||
| 5566 | 5590 | goto free_aof; |
| 5567 | 5591 | } |
| 5568 | 5592 | |
| 5569 | − ret = btf_find_kptr(btf, t, 0, 0, &tmp); | |
| 5593 | + ret = btf_find_kptr(btf, t, 0, 0, &tmp, BPF_KPTR); | |
| 5570 | 5594 | if (ret != BTF_FIELD_FOUND) |
| 5571 | 5595 | continue; |
| 5572 | 5596 | |
| @@ -6564,7 +6588,10 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, · 991 unmodified lines | ||
| 6564 | 6588 | if (prog_args_trusted(prog)) |
| 6565 | 6589 | info->reg_type |= PTR_TRUSTED; |
| 6566 | 6590 | |
| 6567 | − if (btf_param_match_suffix(btf, &args[arg], "__nullable")) | |
| 6591 | + /* Raw tracepoint arguments always get marked as maybe NULL */ | |
| 6592 | + if (bpf_prog_is_raw_tp(prog)) | |
| 6593 | + info->reg_type |= PTR_MAYBE_NULL; | |
| 6594 | + else if (btf_param_match_suffix(btf, &args[arg], "__nullable")) | |
| 6568 | 6595 | info->reg_type |= PTR_MAYBE_NULL; |
| 6569 | 6596 | |
| 6570 | 6597 | if (tgt_prog) { |
| @@ -131,6 +131,7 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag · 130 unmodified lines | ||
| 131 | 131 | INIT_LIST_HEAD_RCU(&fp->aux->ksym_prefix.lnode); |
| 132 | 132 | #endif |
| 133 | 133 | mutex_init(&fp->aux->used_maps_mutex); |
| 134 | + mutex_init(&fp->aux->ext_mutex); | |
| 134 | 135 | mutex_init(&fp->aux->dst_mutex); |
| 135 | 136 | |
| 136 | 137 | return fp; |
| @@ -3044,6 +3045,11 @@ bool __weak bpf_jit_supports_exceptions(void) · 2907 unmodified lines | ||
| 3044 | 3045 | return false; |
| 3045 | 3046 | } |
| 3046 | 3047 | |
| 3048 | +bool __weak bpf_jit_supports_private_stack(void) | |
| 3049 | +{ | |
| 3050 | + return false; | |
| 3051 | +} | |
| 3052 | + | |
| 3047 | 3053 | void __weak arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp), void *cookie) |
| 3048 | 3054 | { |
| 3049 | 3055 | } |
| @@ -154,7 +154,8 @@ void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, · 153 unmodified lines | ||
| 154 | 154 | d->image = NULL; |
| 155 | 155 | goto out; |
| 156 | 156 | } |
| 157 | − bpf_image_ksym_add(d->image, PAGE_SIZE, &d->ksym); | |
| 157 | + bpf_image_ksym_init(d->image, PAGE_SIZE, &d->ksym); | |
| 158 | + bpf_image_ksym_add(&d->ksym); | |
| 158 | 159 | } |
| 159 | 160 | |
| 160 | 161 | prev_num_progs = d->num_progs; |
| @@ -896,9 +896,12 @@ find_first_elem: · 895 unmodified lines | ||
| 896 | 896 | static void htab_elem_free(struct bpf_htab *htab, struct htab_elem *l) |
| 897 | 897 | { |
| 898 | 898 | check_and_free_fields(htab, l); |
| 899 | + | |
| 900 | + migrate_disable(); | |
| 899 | 901 | if (htab->map.map_type == BPF_MAP_TYPE_PERCPU_HASH) |
| 900 | 902 | bpf_mem_cache_free(&htab->pcpu_ma, l->ptr_to_pptr); |
| 901 | 903 | bpf_mem_cache_free(&htab->ma, l); |
| 904 | + migrate_enable(); | |
| 902 | 905 | } |
| 903 | 906 | |
| 904 | 907 | static void htab_put_fd_value(struct bpf_htab *htab, struct htab_elem *l) |
| @@ -948,7 +951,7 @@ static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l) · 43 unmodified lines | ||
| 948 | 951 | if (htab_is_prealloc(htab)) { |
| 949 | 952 | bpf_map_dec_elem_count(&htab->map); |
| 950 | 953 | check_and_free_fields(htab, l); |
| 951 | − __pcpu_freelist_push(&htab->freelist, &l->fnode); | |
| 954 | + pcpu_freelist_push(&htab->freelist, &l->fnode); | |
| 952 | 955 | } else { |
| 953 | 956 | dec_elem_count(htab); |
| 954 | 957 | htab_elem_free(htab, l); |
| @@ -1018,7 +1021,6 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key, · 63 unmodified lines | ||
| 1018 | 1021 | */ |
| 1019 | 1022 | pl_new = this_cpu_ptr(htab->extra_elems); |
| 1020 | 1023 | l_new = *pl_new; |
| 1021 | − htab_put_fd_value(htab, old_elem); | |
| 1022 | 1024 | *pl_new = old_elem; |
| 1023 | 1025 | } else { |
| 1024 | 1026 | struct pcpu_freelist_node *l; |
| @@ -1105,6 +1107,7 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value, · 80 unmodified lines | ||
| 1105 | 1107 | struct htab_elem *l_new = NULL, *l_old; |
| 1106 | 1108 | struct hlist_nulls_head *head; |
| 1107 | 1109 | unsigned long flags; |
| 1110 | + void *old_map_ptr; | |
| 1108 | 1111 | struct bucket *b; |
| 1109 | 1112 | u32 key_size, hash; |
| 1110 | 1113 | int ret; |
| @@ -1183,12 +1186,27 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value, · 72 unmodified lines | ||
| 1183 | 1186 | hlist_nulls_add_head_rcu(&l_new->hash_node, head); |
| 1184 | 1187 | if (l_old) { |
| 1185 | 1188 | hlist_nulls_del_rcu(&l_old->hash_node); |
| 1189 | + | |
| 1190 | + /* l_old has already been stashed in htab->extra_elems, free | |
| 1191 | + * its special fields before it is available for reuse. Also | |
| 1192 | + * save the old map pointer in htab of maps before unlock | |
| 1193 | + * and release it after unlock. | |
| 1194 | + */ | |
| 1195 | + old_map_ptr = NULL; | |
| 1196 | + if (htab_is_prealloc(htab)) { | |
| 1197 | + if (map->ops->map_fd_put_ptr) | |
| 1198 | + old_map_ptr = fd_htab_map_get_ptr(map, l_old); | |
| 1199 | + check_and_free_fields(htab, l_old); | |
| 1200 | + } | |
| 1201 | + } | |
| 1202 | + htab_unlock_bucket(htab, b, hash, flags); | |
| 1203 | + if (l_old) { | |
| 1204 | + if (old_map_ptr) | |
| 1205 | + map->ops->map_fd_put_ptr(map, old_map_ptr, true); | |
| 1186 | 1206 | if (!htab_is_prealloc(htab)) |
| 1187 | 1207 | free_htab_elem(htab, l_old); |
| 1188 | − else | |
| 1189 | − check_and_free_fields(htab, l_old); | |
| 1190 | 1208 | } |
| 1191 | − ret = 0; | |
| 1209 | + return 0; | |
| 1192 | 1210 | err: |
| 1193 | 1211 | htab_unlock_bucket(htab, b, hash, flags); |
| 1194 | 1212 | return ret; |
| @@ -1432,15 +1450,15 @@ static long htab_map_delete_elem(struct bpf_map *map, void *key) · 237 unmodified lines | ||
| 1432 | 1450 | return ret; |
| 1433 | 1451 | |
| 1434 | 1452 | l = lookup_elem_raw(head, hash, key, key_size); |
| 1435 | − | |
| 1436 | − if (l) { | |
| 1453 | + if (l) | |
| 1437 | 1454 | hlist_nulls_del_rcu(&l->hash_node); |
| 1438 | − free_htab_elem(htab, l); | |
| 1439 | − } else { | |
| 1455 | + else | |
| 1440 | 1456 | ret = -ENOENT; |
| 1441 | − } | |
| 1442 | 1457 | |
| 1443 | 1458 | htab_unlock_bucket(htab, b, hash, flags); |
| 1459 | + | |
| 1460 | + if (l) | |
| 1461 | + free_htab_elem(htab, l); | |
| 1444 | 1462 | return ret; |
| 1445 | 1463 | } |
| 1446 | 1464 | |
| @@ -1853,13 +1871,14 @@ again_nocopy: · 406 unmodified lines | ||
| 1853 | 1871 | * may cause deadlock. See comments in function |
| 1854 | 1872 | * prealloc_lru_pop(). Let us do bpf_lru_push_free() |
| 1855 | 1873 | * after releasing the bucket lock. |
| 1874 | + * | |
| 1875 | + * For htab of maps, htab_put_fd_value() in | |
| 1876 | + * free_htab_elem() may acquire a spinlock with bucket | |
| 1877 | + * lock being held and it violates the lock rule, so | |
| 1878 | + * invoke free_htab_elem() after unlock as well. | |
| 1856 | 1879 | */ |
| 1857 | − if (is_lru_map) { | |
| 1858 | − l->batch_flink = node_to_free; | |
| 1859 | − node_to_free = l; | |
| 1860 | − } else { | |
| 1861 | − free_htab_elem(htab, l); | |
| 1862 | − } | |
| 1880 | + l->batch_flink = node_to_free; | |
| 1881 | + node_to_free = l; | |
| 1863 | 1882 | } |
| 1864 | 1883 | dst_key += key_size; |
| 1865 | 1884 | dst_val += value_size; |
| @@ -1871,7 +1890,10 @@ again_nocopy: · 5 unmodified lines | ||
| 1871 | 1890 | while (node_to_free) { |
| 1872 | 1891 | l = node_to_free; |
| 1873 | 1892 | node_to_free = node_to_free->batch_flink; |
| 1874 | − htab_lru_push_free(htab, l); | |
| 1893 | + if (is_lru_map) | |
| 1894 | + htab_lru_push_free(htab, l); | |
| 1895 | + else | |
| 1896 | + free_htab_elem(htab, l); | |
| 1875 | 1897 | } |
| 1876 | 1898 | |
| 1877 | 1899 | next_batch: |
| @@ -2521,6 +2521,25 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid) · 2520 unmodified lines | ||
| 2521 | 2521 | return p; |
| 2522 | 2522 | } |
| 2523 | 2523 | |
| 2524 | +/** | |
| 2525 | + * bpf_task_from_vpid - Find a struct task_struct from its vpid by looking it up | |
| 2526 | + * in the pid namespace of the current task. If a task is returned, it must | |
| 2527 | + * either be stored in a map, or released with bpf_task_release(). | |
| 2528 | + * @vpid: The vpid of the task being looked up. | |
| 2529 | + */ | |
| 2530 | +__bpf_kfunc struct task_struct *bpf_task_from_vpid(s32 vpid) | |
| 2531 | +{ | |
| 2532 | + struct task_struct *p; | |
| 2533 | + | |
| 2534 | + rcu_read_lock(); | |
| 2535 | + p = find_task_by_vpid(vpid); | |
| 2536 | + if (p) | |
| 2537 | + p = bpf_task_acquire(p); | |
| 2538 | + rcu_read_unlock(); | |
| 2539 | + | |
| 2540 | + return p; | |
| 2541 | +} | |
| 2542 | + | |
| 2524 | 2543 | /** |
| 2525 | 2544 | * bpf_dynptr_slice() - Obtain a read-only pointer to the dynptr data. |
| 2526 | 2545 | * @p: The dynptr whose data slice to retrieve |
| @@ -3068,7 +3087,9 @@ BTF_ID_FLAGS(func, bpf_task_under_cgroup, KF_RCU) · 541 unmodified lines | ||
| 3068 | 3087 | BTF_ID_FLAGS(func, bpf_task_get_cgroup1, KF_ACQUIRE | KF_RCU | KF_RET_NULL) |
| 3069 | 3088 | #endif |
| 3070 | 3089 | BTF_ID_FLAGS(func, bpf_task_from_pid, KF_ACQUIRE | KF_RET_NULL) |
| 3090 | +BTF_ID_FLAGS(func, bpf_task_from_vpid, KF_ACQUIRE | KF_RET_NULL) | |
| 3071 | 3091 | BTF_ID_FLAGS(func, bpf_throw) |
| 3092 | +BTF_ID_FLAGS(func, bpf_send_signal_task, KF_TRUSTED_ARGS) | |
| 3072 | 3093 | BTF_KFUNCS_END(generic_btf_ids) |
| 3073 | 3094 | |
| 3074 | 3095 | static const struct btf_kfunc_id_set generic_kfunc_set = { |
| @@ -3086,8 +3107,8 @@ BTF_ID(func, bpf_cgroup_release_dtor) · 11 unmodified lines | ||
| 3086 | 3107 | #endif |
| 3087 | 3108 | |
| 3088 | 3109 | BTF_KFUNCS_START(common_btf_ids) |
| 3089 | −BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx) | |
| 3090 | −BTF_ID_FLAGS(func, bpf_rdonly_cast) | |
| 3110 | +BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx, KF_FASTCALL) | |
| 3111 | +BTF_ID_FLAGS(func, bpf_rdonly_cast, KF_FASTCALL) | |
| 3091 | 3112 | BTF_ID_FLAGS(func, bpf_rcu_read_lock) |
| 3092 | 3113 | BTF_ID_FLAGS(func, bpf_rcu_read_unlock) |
| 3093 | 3114 | BTF_ID_FLAGS(func, bpf_dynptr_slice, KF_RET_NULL) |
| @@ -3124,6 +3145,10 @@ BTF_ID_FLAGS(func, bpf_iter_bits_new, KF_ITER_NEW) · 30 unmodified lines | ||
| 3124 | 3145 | BTF_ID_FLAGS(func, bpf_iter_bits_next, KF_ITER_NEXT | KF_RET_NULL) |
| 3125 | 3146 | BTF_ID_FLAGS(func, bpf_iter_bits_destroy, KF_ITER_DESTROY) |
| 3126 | 3147 | BTF_ID_FLAGS(func, bpf_copy_from_user_str, KF_SLEEPABLE) |
| 3148 | +BTF_ID_FLAGS(func, bpf_get_kmem_cache) | |
| 3149 | +BTF_ID_FLAGS(func, bpf_iter_kmem_cache_new, KF_ITER_NEW | KF_SLEEPABLE) | |
| 3150 | +BTF_ID_FLAGS(func, bpf_iter_kmem_cache_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE) | |
| 3151 | +BTF_ID_FLAGS(func, bpf_iter_kmem_cache_destroy, KF_ITER_DESTROY | KF_SLEEPABLE) | |
| 3127 | 3152 | BTF_KFUNCS_END(common_btf_ids) |
| 3128 | 3153 | |
| 3129 | 3154 | static const struct btf_kfunc_id_set common_kfunc_set = { |
| @@ -0,0 +1,238 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* Copyright (c) 2024 Google */ | |
| 3 | +#include <linux/bpf.h> | |
| 4 | +#include <linux/btf_ids.h> | |
| 5 | +#include <linux/slab.h> | |
| 6 | +#include <linux/kernel.h> | |
| 7 | +#include <linux/seq_file.h> | |
| 8 | + | |
| 9 | +#include "../../mm/slab.h" /* kmem_cache, slab_caches and slab_mutex */ | |
| 10 | + | |
| 11 | +/* open-coded version */ | |
| 12 | +struct bpf_iter_kmem_cache { | |
| 13 | + __u64 __opaque[1]; | |
| 14 | +} __attribute__((aligned(8))); | |
| 15 | + | |
| 16 | +struct bpf_iter_kmem_cache_kern { | |
| 17 | + struct kmem_cache *pos; | |
| 18 | +} __attribute__((aligned(8))); | |
| 19 | + | |
| 20 | +#define KMEM_CACHE_POS_START ((void *)1L) | |
| 21 | + | |
| 22 | +__bpf_kfunc_start_defs(); | |
| 23 | + | |
| 24 | +__bpf_kfunc int bpf_iter_kmem_cache_new(struct bpf_iter_kmem_cache *it) | |
| 25 | +{ | |
| 26 | + struct bpf_iter_kmem_cache_kern *kit = (void *)it; | |
| 27 | + | |
| 28 | + BUILD_BUG_ON(sizeof(*kit) > sizeof(*it)); | |
| 29 | + BUILD_BUG_ON(__alignof__(*kit) != __alignof__(*it)); | |
| 30 | + | |
| 31 | + kit->pos = KMEM_CACHE_POS_START; | |
| 32 | + return 0; | |
| 33 | +} | |
| 34 | + | |
| 35 | +__bpf_kfunc struct kmem_cache *bpf_iter_kmem_cache_next(struct bpf_iter_kmem_cache *it) | |
| 36 | +{ | |
| 37 | + struct bpf_iter_kmem_cache_kern *kit = (void *)it; | |
| 38 | + struct kmem_cache *prev = kit->pos; | |
| 39 | + struct kmem_cache *next; | |
| 40 | + bool destroy = false; | |
| 41 | + | |
| 42 | + if (!prev) | |
| 43 | + return NULL; | |
| 44 | + | |
| 45 | + mutex_lock(&slab_mutex); | |
| 46 | + | |
| 47 | + if (list_empty(&slab_caches)) { | |
| 48 | + mutex_unlock(&slab_mutex); | |
| 49 | + return NULL; | |
| 50 | + } | |
| 51 | + | |
| 52 | + if (prev == KMEM_CACHE_POS_START) | |
| 53 | + next = list_first_entry(&slab_caches, struct kmem_cache, list); | |
| 54 | + else if (list_last_entry(&slab_caches, struct kmem_cache, list) == prev) | |
| 55 | + next = NULL; | |
| 56 | + else | |
| 57 | + next = list_next_entry(prev, list); | |
| 58 | + | |
| 59 | + /* boot_caches have negative refcount, don't touch them */ | |
| 60 | + if (next && next->refcount > 0) | |
| 61 | + next->refcount++; | |
| 62 | + | |
| 63 | + /* Skip kmem_cache_destroy() for active entries */ | |
| 64 | + if (prev && prev != KMEM_CACHE_POS_START) { | |
| 65 | + if (prev->refcount > 1) | |
| 66 | + prev->refcount--; | |
| 67 | + else if (prev->refcount == 1) | |
| 68 | + destroy = true; | |
| 69 | + } | |
| 70 | + | |
| 71 | + mutex_unlock(&slab_mutex); | |
| 72 | + | |
| 73 | + if (destroy) | |
| 74 | + kmem_cache_destroy(prev); | |
| 75 | + | |
| 76 | + kit->pos = next; | |
| 77 | + return next; | |
| 78 | +} | |
| 79 | + | |
| 80 | +__bpf_kfunc void bpf_iter_kmem_cache_destroy(struct bpf_iter_kmem_cache *it) | |
| 81 | +{ | |
| 82 | + struct bpf_iter_kmem_cache_kern *kit = (void *)it; | |
| 83 | + struct kmem_cache *s = kit->pos; | |
| 84 | + bool destroy = false; | |
| 85 | + | |
| 86 | + if (s == NULL || s == KMEM_CACHE_POS_START) | |
| 87 | + return; | |
| 88 | + | |
| 89 | + mutex_lock(&slab_mutex); | |
| 90 | + | |
| 91 | + /* Skip kmem_cache_destroy() for active entries */ | |
| 92 | + if (s->refcount > 1) | |
| 93 | + s->refcount--; | |
| 94 | + else if (s->refcount == 1) | |
| 95 | + destroy = true; | |
| 96 | + | |
| 97 | + mutex_unlock(&slab_mutex); | |
| 98 | + | |
| 99 | + if (destroy) | |
| 100 | + kmem_cache_destroy(s); | |
| 101 | +} | |
| 102 | + | |
| 103 | +__bpf_kfunc_end_defs(); | |
| 104 | + | |
| 105 | +struct bpf_iter__kmem_cache { | |
| 106 | + __bpf_md_ptr(struct bpf_iter_meta *, meta); | |
| 107 | + __bpf_md_ptr(struct kmem_cache *, s); | |
| 108 | +}; | |
| 109 | + | |
| 110 | +union kmem_cache_iter_priv { | |
| 111 | + struct bpf_iter_kmem_cache it; | |
| 112 | + struct bpf_iter_kmem_cache_kern kit; | |
| 113 | +}; | |
| 114 | + | |
| 115 | +static void *kmem_cache_iter_seq_start(struct seq_file *seq, loff_t *pos) | |
| 116 | +{ | |
| 117 | + loff_t cnt = 0; | |
| 118 | + bool found = false; | |
| 119 | + struct kmem_cache *s; | |
| 120 | + union kmem_cache_iter_priv *p = seq->private; | |
| 121 | + | |
| 122 | + mutex_lock(&slab_mutex); | |
| 123 | + | |
| 124 | + /* Find an entry at the given position in the slab_caches list instead | |
| 125 | + * of keeping a reference (of the last visited entry, if any) out of | |
| 126 | + * slab_mutex. It might miss something if one is deleted in the middle | |
| 127 | + * while it releases the lock. But it should be rare and there's not | |
| 128 | + * much we can do about it. | |
| 129 | + */ | |
| 130 | + list_for_each_entry(s, &slab_caches, list) { | |
| 131 | + if (cnt == *pos) { | |
| 132 | + /* Make sure this entry remains in the list by getting | |
| 133 | + * a new reference count. Note that boot_cache entries | |
| 134 | + * have a negative refcount, so don't touch them. | |
| 135 | + */ | |
| 136 | + if (s->refcount > 0) | |
| 137 | + s->refcount++; | |
| 138 | + found = true; | |
| 139 | + break; | |
| 140 | + } | |
| 141 | + cnt++; | |
| 142 | + } | |
| 143 | + mutex_unlock(&slab_mutex); | |
| 144 | + | |
| 145 | + if (!found) | |
| 146 | + s = NULL; | |
| 147 | + | |
| 148 | + p->kit.pos = s; | |
| 149 | + return s; | |
| 150 | +} | |
| 151 | + | |
| 152 | +static void kmem_cache_iter_seq_stop(struct seq_file *seq, void *v) | |
| 153 | +{ | |
| 154 | + struct bpf_iter_meta meta; | |
| 155 | + struct bpf_iter__kmem_cache ctx = { | |
| 156 | + .meta = &meta, | |
| 157 | + .s = v, | |
| 158 | + }; | |
| 159 | + union kmem_cache_iter_priv *p = seq->private; | |
| 160 | + struct bpf_prog *prog; | |
| 161 | + | |
| 162 | + meta.seq = seq; | |
| 163 | + prog = bpf_iter_get_info(&meta, true); | |
| 164 | + if (prog && !ctx.s) | |
| 165 | + bpf_iter_run_prog(prog, &ctx); | |
| 166 | + | |
| 167 | + bpf_iter_kmem_cache_destroy(&p->it); | |
| 168 | +} | |
| 169 | + | |
| 170 | +static void *kmem_cache_iter_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |
| 171 | +{ | |
| 172 | + union kmem_cache_iter_priv *p = seq->private; | |
| 173 | + | |
| 174 | + ++*pos; | |
| 175 | + | |
| 176 | + return bpf_iter_kmem_cache_next(&p->it); | |
| 177 | +} | |
| 178 | + | |
| 179 | +static int kmem_cache_iter_seq_show(struct seq_file *seq, void *v) | |
| 180 | +{ | |
| 181 | + struct bpf_iter_meta meta; | |
| 182 | + struct bpf_iter__kmem_cache ctx = { | |
| 183 | + .meta = &meta, | |
| 184 | + .s = v, | |
| 185 | + }; | |
| 186 | + struct bpf_prog *prog; | |
| 187 | + int ret = 0; | |
| 188 | + | |
| 189 | + meta.seq = seq; | |
| 190 | + prog = bpf_iter_get_info(&meta, false); | |
| 191 | + if (prog) | |
| 192 | + ret = bpf_iter_run_prog(prog, &ctx); | |
| 193 | + | |
| 194 | + return ret; | |
| 195 | +} | |
| 196 | + | |
| 197 | +static const struct seq_operations kmem_cache_iter_seq_ops = { | |
| 198 | + .start = kmem_cache_iter_seq_start, | |
| 199 | + .next = kmem_cache_iter_seq_next, | |
| 200 | + .stop = kmem_cache_iter_seq_stop, | |
| 201 | + .show = kmem_cache_iter_seq_show, | |
| 202 | +}; | |
| 203 | + | |
| 204 | +BTF_ID_LIST_GLOBAL_SINGLE(bpf_kmem_cache_btf_id, struct, kmem_cache) | |
| 205 | + | |
| 206 | +static const struct bpf_iter_seq_info kmem_cache_iter_seq_info = { | |
| 207 | + .seq_ops = &kmem_cache_iter_seq_ops, | |
| 208 | + .seq_priv_size = sizeof(union kmem_cache_iter_priv), | |
| 209 | +}; | |
| 210 | + | |
| 211 | +static void bpf_iter_kmem_cache_show_fdinfo(const struct bpf_iter_aux_info *aux, | |
| 212 | + struct seq_file *seq) | |
| 213 | +{ | |
| 214 | + seq_puts(seq, "kmem_cache iter\n"); | |
| 215 | +} | |
| 216 | + | |
| 217 | +DEFINE_BPF_ITER_FUNC(kmem_cache, struct bpf_iter_meta *meta, | |
| 218 | + struct kmem_cache *s) | |
| 219 | + | |
| 220 | +static struct bpf_iter_reg bpf_kmem_cache_reg_info = { | |
| 221 | + .target = "kmem_cache", | |
| 222 | + .feature = BPF_ITER_RESCHED, | |
| 223 | + .show_fdinfo = bpf_iter_kmem_cache_show_fdinfo, | |
| 224 | + .ctx_arg_info_size = 1, | |
| 225 | + .ctx_arg_info = { | |
| 226 | + { offsetof(struct bpf_iter__kmem_cache, s), | |
| 227 | + PTR_TO_BTF_ID_OR_NULL | PTR_TRUSTED }, | |
| 228 | + }, | |
| 229 | + .seq_info = &kmem_cache_iter_seq_info, | |
| 230 | +}; | |
| 231 | + | |
| 232 | +static int __init bpf_kmem_cache_iter_init(void) | |
| 233 | +{ | |
| 234 | + bpf_kmem_cache_reg_info.ctx_arg_info[0].btf_id = bpf_kmem_cache_btf_id[0]; | |
| 235 | + return bpf_iter_reg_target(&bpf_kmem_cache_reg_info); | |
| 236 | +} | |
| 237 | + | |
| 238 | +late_initcall(bpf_kmem_cache_iter_init); | |
| @@ -254,11 +254,8 @@ static void alloc_bulk(struct bpf_mem_cache *c, int cnt, int node, bool atomic) · 253 unmodified lines | ||
| 254 | 254 | |
| 255 | 255 | static void free_one(void *obj, bool percpu) |
| 256 | 256 | { |
| 257 | − if (percpu) { | |
| 257 | + if (percpu) | |
| 258 | 258 | free_percpu(((void __percpu **)obj)[1]); |
| 259 | − kfree(obj); | |
| 260 | − return; | |
| 261 | − } | |
| 262 | 259 | |
| 263 | 260 | kfree(obj); |
| 264 | 261 | } |
| @@ -0,0 +1,272 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ | |
| 3 | +#include <linux/interval_tree_generic.h> | |
| 4 | +#include <linux/slab.h> | |
| 5 | +#include <linux/bpf_mem_alloc.h> | |
| 6 | +#include <linux/bpf.h> | |
| 7 | +#include "range_tree.h" | |
| 8 | + | |
| 9 | +/* | |
| 10 | + * struct range_tree is a data structure used to allocate contiguous memory | |
| 11 | + * ranges in bpf arena. It's a large bitmap. The contiguous sequence of bits is | |
| 12 | + * represented by struct range_node or 'rn' for short. | |
| 13 | + * rn->rn_rbnode links it into an interval tree while | |
| 14 | + * rn->rb_range_size links it into a second rbtree sorted by size of the range. | |
| 15 | + * __find_range() performs binary search and best fit algorithm to find the | |
| 16 | + * range less or equal requested size. | |
| 17 | + * range_tree_clear/set() clears or sets a range of bits in this bitmap. The | |
| 18 | + * adjacent ranges are merged or split at the same time. | |
| 19 | + * | |
| 20 | + * The split/merge logic is based/borrowed from XFS's xbitmap32 added | |
| 21 | + * in commit 6772fcc8890a ("xfs: convert xbitmap to interval tree"). | |
| 22 | + * | |
| 23 | + * The implementation relies on external lock to protect rbtree-s. | |
| 24 | + * The alloc/free of range_node-s is done via bpf_mem_alloc. | |
| 25 | + * | |
| 26 | + * bpf arena is using range_tree to represent unallocated slots. | |
| 27 | + * At init time: | |
| 28 | + * range_tree_set(rt, 0, max); | |
| 29 | + * Then: | |
| 30 | + * start = range_tree_find(rt, len); | |
| 31 | + * if (start >= 0) | |
| 32 | + * range_tree_clear(rt, start, len); | |
| 33 | + * to find free range and mark slots as allocated and later: | |
| 34 | + * range_tree_set(rt, start, len); | |
| 35 | + * to mark as unallocated after use. | |
| 36 | + */ | |
| 37 | +struct range_node { | |
| 38 | + struct rb_node rn_rbnode; | |
| 39 | + struct rb_node rb_range_size; | |
| 40 | + u32 rn_start; | |
| 41 | + u32 rn_last; /* inclusive */ | |
| 42 | + u32 __rn_subtree_last; | |
| 43 | +}; | |
| 44 | + | |
| 45 | +static struct range_node *rb_to_range_node(struct rb_node *rb) | |
| 46 | +{ | |
| 47 | + return rb_entry(rb, struct range_node, rb_range_size); | |
| 48 | +} | |
| 49 | + | |
| 50 | +static u32 rn_size(struct range_node *rn) | |
| 51 | +{ | |
| 52 | + return rn->rn_last - rn->rn_start + 1; | |
| 53 | +} | |
| 54 | + | |
| 55 | +/* Find range that fits best to requested size */ | |
| 56 | +static inline struct range_node *__find_range(struct range_tree *rt, u32 len) | |
| 57 | +{ | |
| 58 | + struct rb_node *rb = rt->range_size_root.rb_root.rb_node; | |
| 59 | + struct range_node *best = NULL; | |
| 60 | + | |
| 61 | + while (rb) { | |
| 62 | + struct range_node *rn = rb_to_range_node(rb); | |
| 63 | + | |
| 64 | + if (len <= rn_size(rn)) { | |
| 65 | + best = rn; | |
| 66 | + rb = rb->rb_right; | |
| 67 | + } else { | |
| 68 | + rb = rb->rb_left; | |
| 69 | + } | |
| 70 | + } | |
| 71 | + | |
| 72 | + return best; | |
| 73 | +} | |
| 74 | + | |
| 75 | +s64 range_tree_find(struct range_tree *rt, u32 len) | |
| 76 | +{ | |
| 77 | + struct range_node *rn; | |
| 78 | + | |
| 79 | + rn = __find_range(rt, len); | |
| 80 | + if (!rn) | |
| 81 | + return -ENOENT; | |
| 82 | + return rn->rn_start; | |
| 83 | +} | |
| 84 | + | |
| 85 | +/* Insert the range into rbtree sorted by the range size */ | |
| 86 | +static inline void __range_size_insert(struct range_node *rn, | |
| 87 | + struct rb_root_cached *root) | |
| 88 | +{ | |
| 89 | + struct rb_node **link = &root->rb_root.rb_node, *rb = NULL; | |
| 90 | + u64 size = rn_size(rn); | |
| 91 | + bool leftmost = true; | |
| 92 | + | |
| 93 | + while (*link) { | |
| 94 | + rb = *link; | |
| 95 | + if (size > rn_size(rb_to_range_node(rb))) { | |
| 96 | + link = &rb->rb_left; | |
| 97 | + } else { | |
| 98 | + link = &rb->rb_right; | |
| 99 | + leftmost = false; | |
| 100 | + } | |
| 101 | + } | |
| 102 | + | |
| 103 | + rb_link_node(&rn->rb_range_size, rb, link); | |
| 104 | + rb_insert_color_cached(&rn->rb_range_size, root, leftmost); | |
| 105 | +} | |
| 106 | + | |
| 107 | +#define START(node) ((node)->rn_start) | |
| 108 | +#define LAST(node) ((node)->rn_last) | |
| 109 | + | |
| 110 | +INTERVAL_TREE_DEFINE(struct range_node, rn_rbnode, u32, | |
| 111 | + __rn_subtree_last, START, LAST, | |
| 112 | + static inline __maybe_unused, | |
| 113 | + __range_it) | |
| 114 | + | |
| 115 | +static inline __maybe_unused void | |
| 116 | +range_it_insert(struct range_node *rn, struct range_tree *rt) | |
| 117 | +{ | |
| 118 | + __range_size_insert(rn, &rt->range_size_root); | |
| 119 | + __range_it_insert(rn, &rt->it_root); | |
| 120 | +} | |
| 121 | + | |
| 122 | +static inline __maybe_unused void | |
| 123 | +range_it_remove(struct range_node *rn, struct range_tree *rt) | |
| 124 | +{ | |
| 125 | + rb_erase_cached(&rn->rb_range_size, &rt->range_size_root); | |
| 126 | + RB_CLEAR_NODE(&rn->rb_range_size); | |
| 127 | + __range_it_remove(rn, &rt->it_root); | |
| 128 | +} | |
| 129 | + | |
| 130 | +static inline __maybe_unused struct range_node * | |
| 131 | +range_it_iter_first(struct range_tree *rt, u32 start, u32 last) | |
| 132 | +{ | |
| 133 | + return __range_it_iter_first(&rt->it_root, start, last); | |
| 134 | +} | |
| 135 | + | |
| 136 | +/* Clear the range in this range tree */ | |
| 137 | +int range_tree_clear(struct range_tree *rt, u32 start, u32 len) | |
| 138 | +{ | |
| 139 | + u32 last = start + len - 1; | |
| 140 | + struct range_node *new_rn; | |
| 141 | + struct range_node *rn; | |
| 142 | + | |
| 143 | + while ((rn = range_it_iter_first(rt, start, last))) { | |
| 144 | + if (rn->rn_start < start && rn->rn_last > last) { | |
| 145 | + u32 old_last = rn->rn_last; | |
| 146 | + | |
| 147 | + /* Overlaps with the entire clearing range */ | |
| 148 | + range_it_remove(rn, rt); | |
| 149 | + rn->rn_last = start - 1; | |
| 150 | + range_it_insert(rn, rt); | |
| 151 | + | |
| 152 | + /* Add a range */ | |
| 153 | + migrate_disable(); | |
| 154 | + new_rn = bpf_mem_alloc(&bpf_global_ma, sizeof(struct range_node)); | |
| 155 | + migrate_enable(); | |
| 156 | + if (!new_rn) | |
| 157 | + return -ENOMEM; | |
| 158 | + new_rn->rn_start = last + 1; | |
| 159 | + new_rn->rn_last = old_last; | |
| 160 | + range_it_insert(new_rn, rt); | |
| 161 | + } else if (rn->rn_start < start) { | |
| 162 | + /* Overlaps with the left side of the clearing range */ | |
| 163 | + range_it_remove(rn, rt); | |
| 164 | + rn->rn_last = start - 1; | |
| 165 | + range_it_insert(rn, rt); | |
| 166 | + } else if (rn->rn_last > last) { | |
| 167 | + /* Overlaps with the right side of the clearing range */ | |
| 168 | + range_it_remove(rn, rt); | |
| 169 | + rn->rn_start = last + 1; | |
| 170 | + range_it_insert(rn, rt); | |
| 171 | + break; | |
| 172 | + } else { | |
| 173 | + /* in the middle of the clearing range */ | |
| 174 | + range_it_remove(rn, rt); | |
| 175 | + migrate_disable(); | |
| 176 | + bpf_mem_free(&bpf_global_ma, rn); | |
| 177 | + migrate_enable(); | |
| 178 | + } | |
| 179 | + } | |
| 180 | + return 0; | |
| 181 | +} | |
| 182 | + | |
| 183 | +/* Is the whole range set ? */ | |
| 184 | +int is_range_tree_set(struct range_tree *rt, u32 start, u32 len) | |
| 185 | +{ | |
| 186 | + u32 last = start + len - 1; | |
| 187 | + struct range_node *left; | |
| 188 | + | |
| 189 | + /* Is this whole range set ? */ | |
| 190 | + left = range_it_iter_first(rt, start, last); | |
| 191 | + if (left && left->rn_start <= start && left->rn_last >= last) | |
| 192 | + return 0; | |
| 193 | + return -ESRCH; | |
| 194 | +} | |
| 195 | + | |
| 196 | +/* Set the range in this range tree */ | |
| 197 | +int range_tree_set(struct range_tree *rt, u32 start, u32 len) | |
| 198 | +{ | |
| 199 | + u32 last = start + len - 1; | |
| 200 | + struct range_node *right; | |
| 201 | + struct range_node *left; | |
| 202 | + int err; | |
| 203 | + | |
| 204 | + /* Is this whole range already set ? */ | |
| 205 | + left = range_it_iter_first(rt, start, last); | |
| 206 | + if (left && left->rn_start <= start && left->rn_last >= last) | |
| 207 | + return 0; | |
| 208 | + | |
| 209 | + /* Clear out everything in the range we want to set. */ | |
| 210 | + err = range_tree_clear(rt, start, len); | |
| 211 | + if (err) | |
| 212 | + return err; | |
| 213 | + | |
| 214 | + /* Do we have a left-adjacent range ? */ | |
| 215 | + left = range_it_iter_first(rt, start - 1, start - 1); | |
| 216 | + if (left && left->rn_last + 1 != start) | |
| 217 | + return -EFAULT; | |
| 218 | + | |
| 219 | + /* Do we have a right-adjacent range ? */ | |
| 220 | + right = range_it_iter_first(rt, last + 1, last + 1); | |
| 221 | + if (right && right->rn_start != last + 1) | |
| 222 | + return -EFAULT; | |
| 223 | + | |
| 224 | + if (left && right) { | |
| 225 | + /* Combine left and right adjacent ranges */ | |
| 226 | + range_it_remove(left, rt); | |
| 227 | + range_it_remove(right, rt); | |
| 228 | + left->rn_last = right->rn_last; | |
| 229 | + range_it_insert(left, rt); | |
| 230 | + migrate_disable(); | |
| 231 | + bpf_mem_free(&bpf_global_ma, right); | |
| 232 | + migrate_enable(); | |
| 233 | + } else if (left) { | |
| 234 | + /* Combine with the left range */ | |
| 235 | + range_it_remove(left, rt); | |
| 236 | + left->rn_last = last; | |
| 237 | + range_it_insert(left, rt); | |
| 238 | + } else if (right) { | |
| 239 | + /* Combine with the right range */ | |
| 240 | + range_it_remove(right, rt); | |
| 241 | + right->rn_start = start; | |
| 242 | + range_it_insert(right, rt); | |
| 243 | + } else { | |
| 244 | + migrate_disable(); | |
| 245 | + left = bpf_mem_alloc(&bpf_global_ma, sizeof(struct range_node)); | |
| 246 | + migrate_enable(); | |
| 247 | + if (!left) | |
| 248 | + return -ENOMEM; | |
| 249 | + left->rn_start = start; | |
| 250 | + left->rn_last = last; | |
| 251 | + range_it_insert(left, rt); | |
| 252 | + } | |
| 253 | + return 0; | |
| 254 | +} | |
| 255 | + | |
| 256 | +void range_tree_destroy(struct range_tree *rt) | |
| 257 | +{ | |
| 258 | + struct range_node *rn; | |
| 259 | + | |
| 260 | + while ((rn = range_it_iter_first(rt, 0, -1U))) { | |
| 261 | + range_it_remove(rn, rt); | |
| 262 | + migrate_disable(); | |
| 263 | + bpf_mem_free(&bpf_global_ma, rn); | |
| 264 | + migrate_enable(); | |
| 265 | + } | |
| 266 | +} | |
| 267 | + | |
| 268 | +void range_tree_init(struct range_tree *rt) | |
| 269 | +{ | |
| 270 | + rt->it_root = RB_ROOT_CACHED; | |
| 271 | + rt->range_size_root = RB_ROOT_CACHED; | |
| 272 | +} | |
| @@ -0,0 +1,21 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ | |
| 3 | +#ifndef _RANGE_TREE_H | |
| 4 | +#define _RANGE_TREE_H 1 | |
| 5 | + | |
| 6 | +struct range_tree { | |
| 7 | + /* root of interval tree */ | |
| 8 | + struct rb_root_cached it_root; | |
| 9 | + /* root of rbtree of interval sizes */ | |
| 10 | + struct rb_root_cached range_size_root; | |
| 11 | +}; | |
| 12 | + | |
| 13 | +void range_tree_init(struct range_tree *rt); | |
| 14 | +void range_tree_destroy(struct range_tree *rt); | |
| 15 | + | |
| 16 | +int range_tree_clear(struct range_tree *rt, u32 start, u32 len); | |
| 17 | +int range_tree_set(struct range_tree *rt, u32 start, u32 len); | |
| 18 | +int is_range_tree_set(struct range_tree *rt, u32 start, u32 len); | |
| 19 | +s64 range_tree_find(struct range_tree *rt, u32 len); | |
| 20 | + | |
| 21 | +#endif | |
| @@ -155,6 +155,89 @@ static void maybe_wait_bpf_programs(struct bpf_map *map) · 154 unmodified lines | ||
| 155 | 155 | synchronize_rcu(); |
| 156 | 156 | } |
| 157 | 157 | |
| 158 | +static void unpin_uptr_kaddr(void *kaddr) | |
| 159 | +{ | |
| 160 | + if (kaddr) | |
| 161 | + unpin_user_page(virt_to_page(kaddr)); | |
| 162 | +} | |
| 163 | + | |
| 164 | +static void __bpf_obj_unpin_uptrs(struct btf_record *rec, u32 cnt, void *obj) | |
| 165 | +{ | |
| 166 | + const struct btf_field *field; | |
| 167 | + void **uptr_addr; | |
| 168 | + int i; | |
| 169 | + | |
| 170 | + for (i = 0, field = rec->fields; i < cnt; i++, field++) { | |
| 171 | + if (field->type != BPF_UPTR) | |
| 172 | + continue; | |
| 173 | + | |
| 174 | + uptr_addr = obj + field->offset; | |
| 175 | + unpin_uptr_kaddr(*uptr_addr); | |
| 176 | + } | |
| 177 | +} | |
| 178 | + | |
| 179 | +static void bpf_obj_unpin_uptrs(struct btf_record *rec, void *obj) | |
| 180 | +{ | |
| 181 | + if (!btf_record_has_field(rec, BPF_UPTR)) | |
| 182 | + return; | |
| 183 | + | |
| 184 | + __bpf_obj_unpin_uptrs(rec, rec->cnt, obj); | |
| 185 | +} | |
| 186 | + | |
| 187 | +static int bpf_obj_pin_uptrs(struct btf_record *rec, void *obj) | |
| 188 | +{ | |
| 189 | + const struct btf_field *field; | |
| 190 | + const struct btf_type *t; | |
| 191 | + unsigned long start, end; | |
| 192 | + struct page *page; | |
| 193 | + void **uptr_addr; | |
| 194 | + int i, err; | |
| 195 | + | |
| 196 | + if (!btf_record_has_field(rec, BPF_UPTR)) | |
| 197 | + return 0; | |
| 198 | + | |
| 199 | + for (i = 0, field = rec->fields; i < rec->cnt; i++, field++) { | |
| 200 | + if (field->type != BPF_UPTR) | |
| 201 | + continue; | |
| 202 | + | |
| 203 | + uptr_addr = obj + field->offset; | |
| 204 | + start = *(unsigned long *)uptr_addr; | |
| 205 | + if (!start) | |
| 206 | + continue; | |
| 207 | + | |
| 208 | + t = btf_type_by_id(field->kptr.btf, field->kptr.btf_id); | |
| 209 | + /* t->size was checked for zero before */ | |
| 210 | + if (check_add_overflow(start, t->size - 1, &end)) { | |
| 211 | + err = -EFAULT; | |
| 212 | + goto unpin_all; | |
| 213 | + } | |
| 214 | + | |
| 215 | + /* The uptr's struct cannot span across two pages */ | |
| 216 | + if ((start & PAGE_MASK) != (end & PAGE_MASK)) { | |
| 217 | + err = -EOPNOTSUPP; | |
| 218 | + goto unpin_all; | |
| 219 | + } | |
| 220 | + | |
| 221 | + err = pin_user_pages_fast(start, 1, FOLL_LONGTERM | FOLL_WRITE, &page); | |
| 222 | + if (err != 1) | |
| 223 | + goto unpin_all; | |
| 224 | + | |
| 225 | + if (PageHighMem(page)) { | |
| 226 | + err = -EOPNOTSUPP; | |
| 227 | + unpin_user_page(page); | |
| 228 | + goto unpin_all; | |
| 229 | + } | |
| 230 | + | |
| 231 | + *uptr_addr = page_address(page) + offset_in_page(start); | |
| 232 | + } | |
| 233 | + | |
| 234 | + return 0; | |
| 235 | + | |
| 236 | +unpin_all: | |
| 237 | + __bpf_obj_unpin_uptrs(rec, i, obj); | |
| 238 | + return err; | |
| 239 | +} | |
| 240 | + | |
| 158 | 241 | static int bpf_map_update_value(struct bpf_map *map, struct file *map_file, |
| 159 | 242 | void *key, void *value, __u64 flags) |
| 160 | 243 | { |
| @@ -199,9 +282,14 @@ static int bpf_map_update_value(struct bpf_map *map, struct file *map_file, · 38 unmodified lines | ||
| 199 | 282 | map->map_type == BPF_MAP_TYPE_BLOOM_FILTER) { |
| 200 | 283 | err = map->ops->map_push_elem(map, value, flags); |
| 201 | 284 | } else { |
| 202 | − rcu_read_lock(); | |
| 203 | − err = map->ops->map_update_elem(map, key, value, flags); | |
| 204 | − rcu_read_unlock(); | |
| 285 | + err = bpf_obj_pin_uptrs(map->record, value); | |
| 286 | + if (!err) { | |
| 287 | + rcu_read_lock(); | |
| 288 | + err = map->ops->map_update_elem(map, key, value, flags); | |
| 289 | + rcu_read_unlock(); | |
| 290 | + if (err) | |
| 291 | + bpf_obj_unpin_uptrs(map->record, value); | |
| 292 | + } | |
| 205 | 293 | } |
| 206 | 294 | bpf_enable_instrumentation(); |
| 207 | 295 | |
| @@ -548,6 +636,7 @@ void btf_record_free(struct btf_record *rec) · 340 unmodified lines | ||
| 548 | 636 | case BPF_KPTR_UNREF: |
| 549 | 637 | case BPF_KPTR_REF: |
| 550 | 638 | case BPF_KPTR_PERCPU: |
| 639 | + case BPF_UPTR: | |
| 551 | 640 | if (rec->fields[i].kptr.module) |
| 552 | 641 | module_put(rec->fields[i].kptr.module); |
| 553 | 642 | if (btf_is_kernel(rec->fields[i].kptr.btf)) |
| @@ -597,6 +686,7 @@ struct btf_record *btf_record_dup(const struct btf_record *rec) · 43 unmodified lines | ||
| 597 | 686 | case BPF_KPTR_UNREF: |
| 598 | 687 | case BPF_KPTR_REF: |
| 599 | 688 | case BPF_KPTR_PERCPU: |
| 689 | + case BPF_UPTR: | |
| 600 | 690 | if (btf_is_kernel(fields[i].kptr.btf)) |
| 601 | 691 | btf_get(fields[i].kptr.btf); |
| 602 | 692 | if (fields[i].kptr.module && !try_module_get(fields[i].kptr.module)) { |
| @@ -714,6 +804,10 @@ void bpf_obj_free_fields(const struct btf_record *rec, void *obj) · 111 unmodified lines | ||
| 714 | 804 | field->kptr.dtor(xchgd_field); |
| 715 | 805 | } |
| 716 | 806 | break; |
| 807 | + case BPF_UPTR: | |
| 808 | + /* The caller ensured that no one is using the uptr */ | |
| 809 | + unpin_uptr_kaddr(*(void **)field_ptr); | |
| 810 | + break; | |
| 717 | 811 | case BPF_LIST_HEAD: |
| 718 | 812 | if (WARN_ON_ONCE(rec->spin_lock_off < 0)) |
| 719 | 813 | continue; |
| @@ -1105,7 +1199,7 @@ static int map_check_btf(struct bpf_map *map, struct bpf_token *token, · 385 unmodified lines | ||
| 1105 | 1199 | |
| 1106 | 1200 | map->record = btf_parse_fields(btf, value_type, |
| 1107 | 1201 | BPF_SPIN_LOCK | BPF_TIMER | BPF_KPTR | BPF_LIST_HEAD | |
| 1108 | − BPF_RB_ROOT | BPF_REFCOUNT | BPF_WORKQUEUE, | |
| 1202 | + BPF_RB_ROOT | BPF_REFCOUNT | BPF_WORKQUEUE | BPF_UPTR, | |
| 1109 | 1203 | map->value_size); |
| 1110 | 1204 | if (!IS_ERR_OR_NULL(map->record)) { |
| 1111 | 1205 | int i; |
| @@ -1161,6 +1255,12 @@ static int map_check_btf(struct bpf_map *map, struct bpf_token *token, · 49 unmodified lines | ||
| 1161 | 1255 | goto free_map_tab; |
| 1162 | 1256 | } |
| 1163 | 1257 | break; |
| 1258 | + case BPF_UPTR: | |
| 1259 | + if (map->map_type != BPF_MAP_TYPE_TASK_STORAGE) { | |
| 1260 | + ret = -EOPNOTSUPP; | |
| 1261 | + goto free_map_tab; | |
| 1262 | + } | |
| 1263 | + break; | |
| 1164 | 1264 | case BPF_LIST_HEAD: |
| 1165 | 1265 | case BPF_RB_ROOT: |
| 1166 | 1266 | if (map->map_type != BPF_MAP_TYPE_HASH && |
| @@ -3218,7 +3318,8 @@ static void bpf_tracing_link_release(struct bpf_link *link) · 2051 unmodified lines | ||
| 3218 | 3318 | container_of(link, struct bpf_tracing_link, link.link); |
| 3219 | 3319 | |
| 3220 | 3320 | WARN_ON_ONCE(bpf_trampoline_unlink_prog(&tr_link->link, |
| 3221 | − tr_link->trampoline)); | |
| 3321 | + tr_link->trampoline, | |
| 3322 | + tr_link->tgt_prog)); | |
| 3222 | 3323 | |
| 3223 | 3324 | bpf_trampoline_put(tr_link->trampoline); |
| 3224 | 3325 | |
| @@ -3358,7 +3459,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog, · 133 unmodified lines | ||
| 3358 | 3459 | * in prog->aux |
| 3359 | 3460 | * |
| 3360 | 3461 | * - if prog->aux->dst_trampoline is NULL, the program has already been |
| 3361 | − * attached to a target and its initial target was cleared (below) | |
| 3462 | + * attached to a target and its initial target was cleared (below) | |
| 3362 | 3463 | * |
| 3363 | 3464 | * - if tgt_prog != NULL, the caller specified tgt_prog_fd + |
| 3364 | 3465 | * target_btf_id using the link_create API. |
| @@ -3433,7 +3534,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog, · 68 unmodified lines | ||
| 3433 | 3534 | if (err) |
| 3434 | 3535 | goto out_unlock; |
| 3435 | 3536 | |
| 3436 | − err = bpf_trampoline_link_prog(&link->link, tr); | |
| 3537 | + err = bpf_trampoline_link_prog(&link->link, tr, tgt_prog); | |
| 3437 | 3538 | if (err) { |
| 3438 | 3539 | bpf_link_cleanup(&link_primer); |
| 3439 | 3540 | link = NULL; |
| @@ -4002,10 +4103,14 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog, · 562 unmodified lines | ||
| 4002 | 4103 | if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI && |
| 4003 | 4104 | attach_type != BPF_TRACE_UPROBE_MULTI) |
| 4004 | 4105 | return -EINVAL; |
| 4106 | + if (prog->expected_attach_type == BPF_TRACE_UPROBE_SESSION && | |
| 4107 | + attach_type != BPF_TRACE_UPROBE_SESSION) | |
| 4108 | + return -EINVAL; | |
| 4005 | 4109 | if (attach_type != BPF_PERF_EVENT && |
| 4006 | 4110 | attach_type != BPF_TRACE_KPROBE_MULTI && |
| 4007 | 4111 | attach_type != BPF_TRACE_KPROBE_SESSION && |
| 4008 | − attach_type != BPF_TRACE_UPROBE_MULTI) | |
| 4112 | + attach_type != BPF_TRACE_UPROBE_MULTI && | |
| 4113 | + attach_type != BPF_TRACE_UPROBE_SESSION) | |
| 4009 | 4114 | return -EINVAL; |
| 4010 | 4115 | return 0; |
| 4011 | 4116 | case BPF_PROG_TYPE_SCHED_CLS: |
| @@ -5258,7 +5363,8 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr) · 1246 unmodified lines | ||
| 5258 | 5363 | else if (attr->link_create.attach_type == BPF_TRACE_KPROBE_MULTI || |
| 5259 | 5364 | attr->link_create.attach_type == BPF_TRACE_KPROBE_SESSION) |
| 5260 | 5365 | ret = bpf_kprobe_multi_link_attach(attr, prog); |
| 5261 | − else if (attr->link_create.attach_type == BPF_TRACE_UPROBE_MULTI) | |
| 5366 | + else if (attr->link_create.attach_type == BPF_TRACE_UPROBE_MULTI || | |
| 5367 | + attr->link_create.attach_type == BPF_TRACE_UPROBE_SESSION) | |
| 5262 | 5368 | ret = bpf_uprobe_multi_link_attach(attr, prog); |
| 5263 | 5369 | break; |
| 5264 | 5370 | default: |
| @@ -115,10 +115,14 @@ bool bpf_prog_has_trampoline(const struct bpf_prog *prog) · 114 unmodified lines | ||
| 115 | 115 | (ptype == BPF_PROG_TYPE_LSM && eatype == BPF_LSM_MAC); |
| 116 | 116 | } |
| 117 | 117 | |
| 118 | −void bpf_image_ksym_add(void *data, unsigned int size, struct bpf_ksym *ksym) | |
| 118 | +void bpf_image_ksym_init(void *data, unsigned int size, struct bpf_ksym *ksym) | |
| 119 | 119 | { |
| 120 | 120 | ksym->start = (unsigned long) data; |
| 121 | 121 | ksym->end = ksym->start + size; |
| 122 | +} | |
| 123 | + | |
| 124 | +void bpf_image_ksym_add(struct bpf_ksym *ksym) | |
| 125 | +{ | |
| 122 | 126 | bpf_ksym_add(ksym); |
| 123 | 127 | perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_BPF, ksym->start, |
| 124 | 128 | PAGE_SIZE, false, ksym->name); |
| @@ -377,7 +381,8 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size) · 252 unmodified lines | ||
| 377 | 381 | ksym = &im->ksym; |
| 378 | 382 | INIT_LIST_HEAD_RCU(&ksym->lnode); |
| 379 | 383 | snprintf(ksym->name, KSYM_NAME_LEN, "bpf_trampoline_%llu", key); |
| 380 | − bpf_image_ksym_add(image, size, ksym); | |
| 384 | + bpf_image_ksym_init(image, size, ksym); | |
| 385 | + bpf_image_ksym_add(ksym); | |
| 381 | 386 | return im; |
| 382 | 387 | |
| 383 | 388 | out_free_image: |
| @@ -523,7 +528,27 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog) · 139 unmodified lines | ||
| 523 | 528 | } |
| 524 | 529 | } |
| 525 | 530 | |
| 526 | −static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr) | |
| 531 | +static int bpf_freplace_check_tgt_prog(struct bpf_prog *tgt_prog) | |
| 532 | +{ | |
| 533 | + struct bpf_prog_aux *aux = tgt_prog->aux; | |
| 534 | + | |
| 535 | + guard(mutex)(&aux->ext_mutex); | |
| 536 | + if (aux->prog_array_member_cnt) | |
| 537 | + /* Program extensions can not extend target prog when the target | |
| 538 | + * prog has been updated to any prog_array map as tail callee. | |
| 539 | + * It's to prevent a potential infinite loop like: | |
| 540 | + * tgt prog entry -> tgt prog subprog -> freplace prog entry | |
| 541 | + * --tailcall-> tgt prog entry. | |
| 542 | + */ | |
| 543 | + return -EBUSY; | |
| 544 | + | |
| 545 | + aux->is_extended = true; | |
| 546 | + return 0; | |
| 547 | +} | |
| 548 | + | |
| 549 | +static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, | |
| 550 | + struct bpf_trampoline *tr, | |
| 551 | + struct bpf_prog *tgt_prog) | |
| 527 | 552 | { |
| 528 | 553 | enum bpf_tramp_prog_type kind; |
| 529 | 554 | struct bpf_tramp_link *link_exiting; |
| @@ -544,6 +569,9 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr · 14 unmodified lines | ||
| 544 | 569 | /* Cannot attach extension if fentry/fexit are in use. */ |
| 545 | 570 | if (cnt) |
| 546 | 571 | return -EBUSY; |
| 572 | + err = bpf_freplace_check_tgt_prog(tgt_prog); | |
| 573 | + if (err) | |
| 574 | + return err; | |
| 547 | 575 | tr->extension_prog = link->link.prog; |
| 548 | 576 | return bpf_arch_text_poke(tr->func.addr, BPF_MOD_JUMP, NULL, |
| 549 | 577 | link->link.prog->bpf_func); |
| @@ -570,17 +598,21 @@ static int __bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_tr · 20 unmodified lines | ||
| 570 | 598 | return err; |
| 571 | 599 | } |
| 572 | 600 | |
| 573 | −int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr) | |
| 601 | +int bpf_trampoline_link_prog(struct bpf_tramp_link *link, | |
| 602 | + struct bpf_trampoline *tr, | |
| 603 | + struct bpf_prog *tgt_prog) | |
| 574 | 604 | { |
| 575 | 605 | int err; |
| 576 | 606 | |
| 577 | 607 | mutex_lock(&tr->mutex); |
| 578 | − err = __bpf_trampoline_link_prog(link, tr); | |
| 608 | + err = __bpf_trampoline_link_prog(link, tr, tgt_prog); | |
| 579 | 609 | mutex_unlock(&tr->mutex); |
| 580 | 610 | return err; |
| 581 | 611 | } |
| 582 | 612 | |
| 583 | −static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr) | |
| 613 | +static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, | |
| 614 | + struct bpf_trampoline *tr, | |
| 615 | + struct bpf_prog *tgt_prog) | |
| 584 | 616 | { |
| 585 | 617 | enum bpf_tramp_prog_type kind; |
| 586 | 618 | int err; |
| @@ -591,6 +623,8 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_ · 4 unmodified lines | ||
| 591 | 623 | err = bpf_arch_text_poke(tr->func.addr, BPF_MOD_JUMP, |
| 592 | 624 | tr->extension_prog->bpf_func, NULL); |
| 593 | 625 | tr->extension_prog = NULL; |
| 626 | + guard(mutex)(&tgt_prog->aux->ext_mutex); | |
| 627 | + tgt_prog->aux->is_extended = false; | |
| 594 | 628 | return err; |
| 595 | 629 | } |
| 596 | 630 | hlist_del_init(&link->tramp_hlist); |
| @@ -599,12 +633,14 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_ · 2 unmodified lines | ||
| 599 | 633 | } |
| 600 | 634 | |
| 601 | 635 | /* bpf_trampoline_unlink_prog() should never fail. */ |
| 602 | −int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr) | |
| 636 | +int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, | |
| 637 | + struct bpf_trampoline *tr, | |
| 638 | + struct bpf_prog *tgt_prog) | |
| 603 | 639 | { |
| 604 | 640 | int err; |
| 605 | 641 | |
| 606 | 642 | mutex_lock(&tr->mutex); |
| 607 | − err = __bpf_trampoline_unlink_prog(link, tr); | |
| 643 | + err = __bpf_trampoline_unlink_prog(link, tr, tgt_prog); | |
| 608 | 644 | mutex_unlock(&tr->mutex); |
| 609 | 645 | return err; |
| 610 | 646 | } |
| @@ -619,7 +655,7 @@ static void bpf_shim_tramp_link_release(struct bpf_link *link) · 8 unmodified lines | ||
| 619 | 655 | if (!shim_link->trampoline) |
| 620 | 656 | return; |
| 621 | 657 | |
| 622 | − WARN_ON_ONCE(bpf_trampoline_unlink_prog(&shim_link->link, shim_link->trampoline)); | |
| 658 | + WARN_ON_ONCE(bpf_trampoline_unlink_prog(&shim_link->link, shim_link->trampoline, NULL)); | |
| 623 | 659 | bpf_trampoline_put(shim_link->trampoline); |
| 624 | 660 | } |
| 625 | 661 | |
| @@ -733,7 +769,7 @@ int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog, · 107 unmodified lines | ||
| 733 | 769 | goto err; |
| 734 | 770 | } |
| 735 | 771 | |
| 736 | − err = __bpf_trampoline_link_prog(&shim_link->link, tr); | |
| 772 | + err = __bpf_trampoline_link_prog(&shim_link->link, tr, NULL); | |
| 737 | 773 | if (err) |
| 738 | 774 | goto err; |
| 739 | 775 | |
| @@ -868,6 +904,8 @@ static u64 notrace __bpf_prog_enter_recur(struct bpf_prog *prog, struct bpf_tram · 128 unmodified lines | ||
| 868 | 904 | |
| 869 | 905 | if (unlikely(this_cpu_inc_return(*(prog->active)) != 1)) { |
| 870 | 906 | bpf_prog_inc_misses_counter(prog); |
| 907 | + if (prog->aux->recursion_detected) | |
| 908 | + prog->aux->recursion_detected(prog); | |
| 871 | 909 | return 0; |
| 872 | 910 | } |
| 873 | 911 | return bpf_prog_start_time(); |
| @@ -944,6 +982,8 @@ u64 notrace __bpf_prog_enter_sleepable_recur(struct bpf_prog *prog, · 70 unmodified lines | ||
| 944 | 982 | |
| 945 | 983 | if (unlikely(this_cpu_inc_return(*(prog->active)) != 1)) { |
| 946 | 984 | bpf_prog_inc_misses_counter(prog); |
| 985 | + if (prog->aux->recursion_detected) | |
| 986 | + prog->aux->recursion_detected(prog); | |
| 947 | 987 | return 0; |
| 948 | 988 | } |
| 949 | 989 | return bpf_prog_start_time(); |
| @@ -194,6 +194,8 @@ struct bpf_verifier_stack_elem { · 193 unmodified lines | ||
| 194 | 194 | |
| 195 | 195 | #define BPF_GLOBAL_PERCPU_MA_MAX_SIZE 512 |
| 196 | 196 | |
| 197 | +#define BPF_PRIV_STACK_MIN_SIZE 64 | |
| 198 | + | |
| 197 | 199 | static int acquire_reference_state(struct bpf_verifier_env *env, int insn_idx); |
| 198 | 200 | static int release_reference(struct bpf_verifier_env *env, int ref_obj_id); |
| 199 | 201 | static void invalidate_non_owning_refs(struct bpf_verifier_env *env); |
| @@ -418,6 +420,25 @@ static struct btf_record *reg_btf_record(const struct bpf_reg_state *reg) · 218 unmodified lines | ||
| 418 | 420 | return rec; |
| 419 | 421 | } |
| 420 | 422 | |
| 423 | +static bool mask_raw_tp_reg_cond(const struct bpf_verifier_env *env, struct bpf_reg_state *reg) { | |
| 424 | + return reg->type == (PTR_TO_BTF_ID | PTR_TRUSTED | PTR_MAYBE_NULL) && | |
| 425 | + bpf_prog_is_raw_tp(env->prog) && !reg->ref_obj_id; | |
| 426 | +} | |
| 427 | + | |
| 428 | +static bool mask_raw_tp_reg(const struct bpf_verifier_env *env, struct bpf_reg_state *reg) | |
| 429 | +{ | |
| 430 | + if (!mask_raw_tp_reg_cond(env, reg)) | |
| 431 | + return false; | |
| 432 | + reg->type &= ~PTR_MAYBE_NULL; | |
| 433 | + return true; | |
| 434 | +} | |
| 435 | + | |
| 436 | +static void unmask_raw_tp_reg(struct bpf_reg_state *reg, bool result) | |
| 437 | +{ | |
| 438 | + if (result) | |
| 439 | + reg->type |= PTR_MAYBE_NULL; | |
| 440 | +} | |
| 441 | + | |
| 421 | 442 | static bool subprog_is_global(const struct bpf_verifier_env *env, int subprog) |
| 422 | 443 | { |
| 423 | 444 | struct bpf_func_info_aux *aux = env->prog->aux->func_info_aux; |
| @@ -1265,6 +1286,7 @@ static int copy_reference_state(struct bpf_func_state *dst, const struct bpf_fun · 841 unmodified lines | ||
| 1265 | 1286 | if (!dst->refs) |
| 1266 | 1287 | return -ENOMEM; |
| 1267 | 1288 | |
| 1289 | + dst->active_locks = src->active_locks; | |
| 1268 | 1290 | dst->acquired_refs = src->acquired_refs; |
| 1269 | 1291 | return 0; |
| 1270 | 1292 | } |
| @@ -1335,13 +1357,32 @@ static int acquire_reference_state(struct bpf_verifier_env *env, int insn_idx) · 64 unmodified lines | ||
| 1335 | 1357 | if (err) |
| 1336 | 1358 | return err; |
| 1337 | 1359 | id = ++env->id_gen; |
| 1360 | + state->refs[new_ofs].type = REF_TYPE_PTR; | |
| 1338 | 1361 | state->refs[new_ofs].id = id; |
| 1339 | 1362 | state->refs[new_ofs].insn_idx = insn_idx; |
| 1340 | − state->refs[new_ofs].callback_ref = state->in_callback_fn ? state->frameno : 0; | |
| 1341 | 1363 | |
| 1342 | 1364 | return id; |
| 1343 | 1365 | } |
| 1344 | 1366 | |
| 1367 | +static int acquire_lock_state(struct bpf_verifier_env *env, int insn_idx, enum ref_state_type type, | |
| 1368 | + int id, void *ptr) | |
| 1369 | +{ | |
| 1370 | + struct bpf_func_state *state = cur_func(env); | |
| 1371 | + int new_ofs = state->acquired_refs; | |
| 1372 | + int err; | |
| 1373 | + | |
| 1374 | + err = resize_reference_state(state, state->acquired_refs + 1); | |
| 1375 | + if (err) | |
| 1376 | + return err; | |
| 1377 | + state->refs[new_ofs].type = type; | |
| 1378 | + state->refs[new_ofs].id = id; | |
| 1379 | + state->refs[new_ofs].insn_idx = insn_idx; | |
| 1380 | + state->refs[new_ofs].ptr = ptr; | |
| 1381 | + | |
| 1382 | + state->active_locks++; | |
| 1383 | + return 0; | |
| 1384 | +} | |
| 1385 | + | |
| 1345 | 1386 | /* release function corresponding to acquire_reference_state(). Idempotent. */ |
| 1346 | 1387 | static int release_reference_state(struct bpf_func_state *state, int ptr_id) |
| 1347 | 1388 | { |
| @@ -1349,10 +1390,9 @@ static int release_reference_state(struct bpf_func_state *state, int ptr_id) · 1 unmodified line | ||
| 1349 | 1390 | |
| 1350 | 1391 | last_idx = state->acquired_refs - 1; |
| 1351 | 1392 | for (i = 0; i < state->acquired_refs; i++) { |
| 1393 | + if (state->refs[i].type != REF_TYPE_PTR) | |
| 1394 | + continue; | |
| 1352 | 1395 | if (state->refs[i].id == ptr_id) { |
| 1353 | − /* Cannot release caller references in callbacks */ | |
| 1354 | − if (state->in_callback_fn && state->refs[i].callback_ref != state->frameno) | |
| 1355 | − return -EINVAL; | |
| 1356 | 1396 | if (last_idx && i != last_idx) |
| 1357 | 1397 | memcpy(&state->refs[i], &state->refs[last_idx], |
| 1358 | 1398 | sizeof(*state->refs)); |
| @@ -1364,6 +1404,45 @@ static int release_reference_state(struct bpf_func_state *state, int ptr_id) · 5 unmodified lines | ||
| 1364 | 1404 | return -EINVAL; |
| 1365 | 1405 | } |
| 1366 | 1406 | |
| 1407 | +static int release_lock_state(struct bpf_func_state *state, int type, int id, void *ptr) | |
| 1408 | +{ | |
| 1409 | + int i, last_idx; | |
| 1410 | + | |
| 1411 | + last_idx = state->acquired_refs - 1; | |
| 1412 | + for (i = 0; i < state->acquired_refs; i++) { | |
| 1413 | + if (state->refs[i].type != type) | |
| 1414 | + continue; | |
| 1415 | + if (state->refs[i].id == id && state->refs[i].ptr == ptr) { | |
| 1416 | + if (last_idx && i != last_idx) | |
| 1417 | + memcpy(&state->refs[i], &state->refs[last_idx], | |
| 1418 | + sizeof(*state->refs)); | |
| 1419 | + memset(&state->refs[last_idx], 0, sizeof(*state->refs)); | |
| 1420 | + state->acquired_refs--; | |
| 1421 | + state->active_locks--; | |
| 1422 | + return 0; | |
| 1423 | + } | |
| 1424 | + } | |
| 1425 | + return -EINVAL; | |
| 1426 | +} | |
| 1427 | + | |
| 1428 | +static struct bpf_reference_state *find_lock_state(struct bpf_verifier_env *env, enum ref_state_type type, | |
| 1429 | + int id, void *ptr) | |
| 1430 | +{ | |
| 1431 | + struct bpf_func_state *state = cur_func(env); | |
| 1432 | + int i; | |
| 1433 | + | |
| 1434 | + for (i = 0; i < state->acquired_refs; i++) { | |
| 1435 | + struct bpf_reference_state *s = &state->refs[i]; | |
| 1436 | + | |
| 1437 | + if (s->type == REF_TYPE_PTR || s->type != type) | |
| 1438 | + continue; | |
| 1439 | + | |
| 1440 | + if (s->id == id && s->ptr == ptr) | |
| 1441 | + return s; | |
| 1442 | + } | |
| 1443 | + return NULL; | |
| 1444 | +} | |
| 1445 | + | |
| 1367 | 1446 | static void free_func_state(struct bpf_func_state *state) |
| 1368 | 1447 | { |
| 1369 | 1448 | if (!state) |
| @@ -1373,13 +1452,6 @@ static void free_func_state(struct bpf_func_state *state) · 3 unmodified lines | ||
| 1373 | 1452 | kfree(state); |
| 1374 | 1453 | } |
| 1375 | 1454 | |
| 1376 | −static void clear_jmp_history(struct bpf_verifier_state *state) | |
| 1377 | −{ | |
| 1378 | − kfree(state->jmp_history); | |
| 1379 | − state->jmp_history = NULL; | |
| 1380 | − state->jmp_history_cnt = 0; | |
| 1381 | −} | |
| 1382 | − | |
| 1383 | 1455 | static void free_verifier_state(struct bpf_verifier_state *state, |
| 1384 | 1456 | bool free_self) |
| 1385 | 1457 | { |
| @@ -1389,7 +1461,6 @@ static void free_verifier_state(struct bpf_verifier_state *state, · 3 unmodified lines | ||
| 1389 | 1461 | free_func_state(state->frame[i]); |
| 1390 | 1462 | state->frame[i] = NULL; |
| 1391 | 1463 | } |
| 1392 | − clear_jmp_history(state); | |
| 1393 | 1464 | if (free_self) |
| 1394 | 1465 | kfree(state); |
| 1395 | 1466 | } |
| @@ -1415,13 +1486,6 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state, · 19 unmodified lines | ||
| 1415 | 1486 | struct bpf_func_state *dst; |
| 1416 | 1487 | int i, err; |
| 1417 | 1488 | |
| 1418 | − dst_state->jmp_history = copy_array(dst_state->jmp_history, src->jmp_history, | |
| 1419 | − src->jmp_history_cnt, sizeof(*dst_state->jmp_history), | |
| 1420 | − GFP_USER); | |
| 1421 | − if (!dst_state->jmp_history) | |
| 1422 | − return -ENOMEM; | |
| 1423 | − dst_state->jmp_history_cnt = src->jmp_history_cnt; | |
| 1424 | − | |
| 1425 | 1489 | /* if dst has more stack frames then src frame, free them, this is also |
| 1426 | 1490 | * necessary in case of exceptional exits using bpf_throw. |
| 1427 | 1491 | */ |
| @@ -1434,12 +1498,12 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state, · 6 unmodified lines | ||
| 1434 | 1498 | dst_state->active_preempt_lock = src->active_preempt_lock; |
| 1435 | 1499 | dst_state->in_sleepable = src->in_sleepable; |
| 1436 | 1500 | dst_state->curframe = src->curframe; |
| 1437 | − dst_state->active_lock.ptr = src->active_lock.ptr; | |
| 1438 | − dst_state->active_lock.id = src->active_lock.id; | |
| 1439 | 1501 | dst_state->branches = src->branches; |
| 1440 | 1502 | dst_state->parent = src->parent; |
| 1441 | 1503 | dst_state->first_insn_idx = src->first_insn_idx; |
| 1442 | 1504 | dst_state->last_insn_idx = src->last_insn_idx; |
| 1505 | + dst_state->insn_hist_start = src->insn_hist_start; | |
| 1506 | + dst_state->insn_hist_end = src->insn_hist_end; | |
| 1443 | 1507 | dst_state->dfs_depth = src->dfs_depth; |
| 1444 | 1508 | dst_state->callback_unroll_depth = src->callback_unroll_depth; |
| 1445 | 1509 | dst_state->used_as_loop_entry = src->used_as_loop_entry; |
| @@ -2492,9 +2556,14 @@ static struct bpf_verifier_state *push_async_cb(struct bpf_verifier_env *env, · 1046 unmodified lines | ||
| 2492 | 2556 | * The caller state doesn't matter. |
| 2493 | 2557 | * This is async callback. It starts in a fresh stack. |
| 2494 | 2558 | * Initialize it similar to do_check_common(). |
| 2559 | + * But we do need to make sure to not clobber insn_hist, so we keep | |
| 2560 | + * chaining insn_hist_start/insn_hist_end indices as for a normal | |
| 2561 | + * child state. | |
| 2495 | 2562 | */ |
| 2496 | 2563 | elem->st.branches = 1; |
| 2497 | 2564 | elem->st.in_sleepable = is_sleepable; |
| 2565 | + elem->st.insn_hist_start = env->cur_state->insn_hist_end; | |
| 2566 | + elem->st.insn_hist_end = elem->st.insn_hist_start; | |
| 2498 | 2567 | frame = kzalloc(sizeof(*frame), GFP_KERNEL); |
| 2499 | 2568 | if (!frame) |
| 2500 | 2569 | goto err; |
| @@ -3474,11 +3543,10 @@ static void linked_regs_unpack(u64 val, struct linked_regs *s) · 973 unmodified lines | ||
| 3474 | 3543 | } |
| 3475 | 3544 | |
| 3476 | 3545 | /* for any branch, call, exit record the history of jmps in the given state */ |
| 3477 | −static int push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state *cur, | |
| 3478 | − int insn_flags, u64 linked_regs) | |
| 3546 | +static int push_insn_history(struct bpf_verifier_env *env, struct bpf_verifier_state *cur, | |
| 3547 | + int insn_flags, u64 linked_regs) | |
| 3479 | 3548 | { |
| 3480 | − u32 cnt = cur->jmp_history_cnt; | |
| 3481 | − struct bpf_jmp_history_entry *p; | |
| 3549 | + struct bpf_insn_hist_entry *p; | |
| 3482 | 3550 | size_t alloc_size; |
| 3483 | 3551 | |
| 3484 | 3552 | /* combine instruction flags if we already recorded this instruction */ |
| @@ -3498,29 +3566,32 @@ static int push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_st · 13 unmodified lines | ||
| 3498 | 3566 | return 0; |
| 3499 | 3567 | } |
| 3500 | 3568 | |
| 3501 | − cnt++; | |
| 3502 | − alloc_size = kmalloc_size_roundup(size_mul(cnt, sizeof(*p))); | |
| 3503 | − p = krealloc(cur->jmp_history, alloc_size, GFP_USER); | |
| 3504 | − if (!p) | |
| 3505 | − return -ENOMEM; | |
| 3506 | − cur->jmp_history = p; | |
| 3569 | + if (cur->insn_hist_end + 1 > env->insn_hist_cap) { | |
| 3570 | + alloc_size = size_mul(cur->insn_hist_end + 1, sizeof(*p)); | |
| 3571 | + p = kvrealloc(env->insn_hist, alloc_size, GFP_USER); | |
| 3572 | + if (!p) | |
| 3573 | + return -ENOMEM; | |
| 3574 | + env->insn_hist = p; | |
| 3575 | + env->insn_hist_cap = alloc_size / sizeof(*p); | |
| 3576 | + } | |
| 3507 | 3577 | |
| 3508 | − p = &cur->jmp_history[cnt - 1]; | |
| 3578 | + p = &env->insn_hist[cur->insn_hist_end]; | |
| 3509 | 3579 | p->idx = env->insn_idx; |
| 3510 | 3580 | p->prev_idx = env->prev_insn_idx; |
| 3511 | 3581 | p->flags = insn_flags; |
| 3512 | 3582 | p->linked_regs = linked_regs; |
| 3513 | − cur->jmp_history_cnt = cnt; | |
| 3583 | + | |
| 3584 | + cur->insn_hist_end++; | |
| 3514 | 3585 | env->cur_hist_ent = p; |
| 3515 | 3586 | |
| 3516 | 3587 | return 0; |
| 3517 | 3588 | } |
| 3518 | 3589 | |
| 3519 | −static struct bpf_jmp_history_entry *get_jmp_hist_entry(struct bpf_verifier_state *st, | |
| 3520 | − u32 hist_end, int insn_idx) | |
| 3590 | +static struct bpf_insn_hist_entry *get_insn_hist_entry(struct bpf_verifier_env *env, | |
| 3591 | + u32 hist_start, u32 hist_end, int insn_idx) | |
| 3521 | 3592 | { |
| 3522 | − if (hist_end > 0 && st->jmp_history[hist_end - 1].idx == insn_idx) | |
| 3523 | − return &st->jmp_history[hist_end - 1]; | |
| 3593 | + if (hist_end > hist_start && env->insn_hist[hist_end - 1].idx == insn_idx) | |
| 3594 | + return &env->insn_hist[hist_end - 1]; | |
| 3524 | 3595 | return NULL; |
| 3525 | 3596 | } |
| 3526 | 3597 | |
| @@ -3537,25 +3608,26 @@ static struct bpf_jmp_history_entry *get_jmp_hist_entry(struct bpf_verifier_stat · 10 unmodified lines | ||
| 3537 | 3608 | * history entry recording a jump from last instruction of parent state and |
| 3538 | 3609 | * first instruction of given state. |
| 3539 | 3610 | */ |
| 3540 | −static int get_prev_insn_idx(struct bpf_verifier_state *st, int i, | |
| 3541 | − u32 *history) | |
| 3611 | +static int get_prev_insn_idx(const struct bpf_verifier_env *env, | |
| 3612 | + struct bpf_verifier_state *st, | |
| 3613 | + int insn_idx, u32 hist_start, u32 *hist_endp) | |
| 3542 | 3614 | { |
| 3543 | − u32 cnt = *history; | |
| 3615 | + u32 hist_end = *hist_endp; | |
| 3616 | + u32 cnt = hist_end - hist_start; | |
| 3544 | 3617 | |
| 3545 | − if (i == st->first_insn_idx) { | |
| 3618 | + if (insn_idx == st->first_insn_idx) { | |
| 3546 | 3619 | if (cnt == 0) |
| 3547 | 3620 | return -ENOENT; |
| 3548 | − if (cnt == 1 && st->jmp_history[0].idx == i) | |
| 3621 | + if (cnt == 1 && env->insn_hist[hist_start].idx == insn_idx) | |
| 3549 | 3622 | return -ENOENT; |
| 3550 | 3623 | } |
| 3551 | 3624 | |
| 3552 | − if (cnt && st->jmp_history[cnt - 1].idx == i) { | |
| 3553 | − i = st->jmp_history[cnt - 1].prev_idx; | |
| 3554 | − (*history)--; | |
| 3625 | + if (cnt && env->insn_hist[hist_end - 1].idx == insn_idx) { | |
| 3626 | + (*hist_endp)--; | |
| 3627 | + return env->insn_hist[hist_end - 1].prev_idx; | |
| 3555 | 3628 | } else { |
| 3556 | − i--; | |
| 3629 | + return insn_idx - 1; | |
| 3557 | 3630 | } |
| 3558 | − return i; | |
| 3559 | 3631 | } |
| 3560 | 3632 | |
| 3561 | 3633 | static const char *disasm_kfunc_name(void *data, const struct bpf_insn *insn) |
| @@ -3727,7 +3799,7 @@ static void fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask) · 165 unmodified lines | ||
| 3727 | 3799 | /* If any register R in hist->linked_regs is marked as precise in bt, |
| 3728 | 3800 | * do bt_set_frame_{reg,slot}(bt, R) for all registers in hist->linked_regs. |
| 3729 | 3801 | */ |
| 3730 | −static void bt_sync_linked_regs(struct backtrack_state *bt, struct bpf_jmp_history_entry *hist) | |
| 3802 | +static void bt_sync_linked_regs(struct backtrack_state *bt, struct bpf_insn_hist_entry *hist) | |
| 3731 | 3803 | { |
| 3732 | 3804 | struct linked_regs linked_regs; |
| 3733 | 3805 | bool some_precise = false; |
| @@ -3772,7 +3844,7 @@ static bool calls_callback(struct bpf_verifier_env *env, int insn_idx); · 38 unmodified lines | ||
| 3772 | 3844 | * - *was* processed previously during backtracking. |
| 3773 | 3845 | */ |
| 3774 | 3846 | static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, |
| 3775 | − struct bpf_jmp_history_entry *hist, struct backtrack_state *bt) | |
| 3847 | + struct bpf_insn_hist_entry *hist, struct backtrack_state *bt) | |
| 3776 | 3848 | { |
| 3777 | 3849 | const struct bpf_insn_cbs cbs = { |
| 3778 | 3850 | .cb_call = disasm_kfunc_name, |
| @@ -4191,7 +4263,7 @@ static void mark_all_scalars_imprecise(struct bpf_verifier_env *env, struct bpf_ · 412 unmodified lines | ||
| 4191 | 4263 | * SCALARS, as well as any other registers and slots that contribute to |
| 4192 | 4264 | * a tracked state of given registers/stack slots, depending on specific BPF |
| 4193 | 4265 | * assembly instructions (see backtrack_insns() for exact instruction handling |
| 4194 | − * logic). This backtracking relies on recorded jmp_history and is able to | |
| 4266 | + * logic). This backtracking relies on recorded insn_hist and is able to | |
| 4195 | 4267 | * traverse entire chain of parent states. This process ends only when all the |
| 4196 | 4268 | * necessary registers/slots and their transitive dependencies are marked as |
| 4197 | 4269 | * precise. |
| @@ -4308,8 +4380,9 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno) · 110 unmodified lines | ||
| 4308 | 4380 | |
| 4309 | 4381 | for (;;) { |
| 4310 | 4382 | DECLARE_BITMAP(mask, 64); |
| 4311 | − u32 history = st->jmp_history_cnt; | |
| 4312 | − struct bpf_jmp_history_entry *hist; | |
| 4383 | + u32 hist_start = st->insn_hist_start; | |
| 4384 | + u32 hist_end = st->insn_hist_end; | |
| 4385 | + struct bpf_insn_hist_entry *hist; | |
| 4313 | 4386 | |
| 4314 | 4387 | if (env->log.level & BPF_LOG_LEVEL2) { |
| 4315 | 4388 | verbose(env, "mark_precise: frame%d: last_idx %d first_idx %d subseq_idx %d \n", |
| @@ -4348,7 +4421,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno) · 32 unmodified lines | ||
| 4348 | 4421 | err = 0; |
| 4349 | 4422 | skip_first = false; |
| 4350 | 4423 | } else { |
| 4351 | − hist = get_jmp_hist_entry(st, history, i); | |
| 4424 | + hist = get_insn_hist_entry(env, hist_start, hist_end, i); | |
| 4352 | 4425 | err = backtrack_insn(env, i, subseq_idx, hist, bt); |
| 4353 | 4426 | } |
| 4354 | 4427 | if (err == -ENOTSUPP) { |
| @@ -4365,7 +4438,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno) · 10 unmodified lines | ||
| 4365 | 4438 | */ |
| 4366 | 4439 | return 0; |
| 4367 | 4440 | subseq_idx = i; |
| 4368 | − i = get_prev_insn_idx(st, i, &history); | |
| 4441 | + i = get_prev_insn_idx(env, st, i, hist_start, &hist_end); | |
| 4369 | 4442 | if (i == -ENOENT) |
| 4370 | 4443 | break; |
| 4371 | 4444 | if (i >= env->prog->len) { |
| @@ -4731,7 +4804,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, · 359 unmodified lines | ||
| 4731 | 4804 | } |
| 4732 | 4805 | |
| 4733 | 4806 | if (insn_flags) |
| 4734 | − return push_jmp_history(env, env->cur_state, insn_flags, 0); | |
| 4807 | + return push_insn_history(env, env->cur_state, insn_flags, 0); | |
| 4735 | 4808 | return 0; |
| 4736 | 4809 | } |
| 4737 | 4810 | |
| @@ -5038,7 +5111,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env, · 300 unmodified lines | ||
| 5038 | 5111 | insn_flags = 0; /* we are not restoring spilled register */ |
| 5039 | 5112 | } |
| 5040 | 5113 | if (insn_flags) |
| 5041 | − return push_jmp_history(env, env->cur_state, insn_flags, 0); | |
| 5114 | + return push_insn_history(env, env->cur_state, insn_flags, 0); | |
| 5042 | 5115 | return 0; |
| 5043 | 5116 | } |
| 5044 | 5117 | |
| @@ -5423,7 +5496,7 @@ static bool in_sleepable(struct bpf_verifier_env *env) · 378 unmodified lines | ||
| 5423 | 5496 | static bool in_rcu_cs(struct bpf_verifier_env *env) |
| 5424 | 5497 | { |
| 5425 | 5498 | return env->cur_state->active_rcu_lock || |
| 5426 | − env->cur_state->active_lock.ptr || | |
| 5499 | + cur_func(env)->active_locks || | |
| 5427 | 5500 | !in_sleepable(env); |
| 5428 | 5501 | } |
| 5429 | 5502 | |
| @@ -5491,6 +5564,22 @@ static u32 btf_ld_kptr_type(struct bpf_verifier_env *env, struct btf_field *kptr · 61 unmodified lines | ||
| 5491 | 5564 | return ret; |
| 5492 | 5565 | } |
| 5493 | 5566 | |
| 5567 | +static int mark_uptr_ld_reg(struct bpf_verifier_env *env, u32 regno, | |
| 5568 | + struct btf_field *field) | |
| 5569 | +{ | |
| 5570 | + struct bpf_reg_state *reg; | |
| 5571 | + const struct btf_type *t; | |
| 5572 | + | |
| 5573 | + t = btf_type_by_id(field->kptr.btf, field->kptr.btf_id); | |
| 5574 | + mark_reg_known_zero(env, cur_regs(env), regno); | |
| 5575 | + reg = reg_state(env, regno); | |
| 5576 | + reg->type = PTR_TO_MEM | PTR_MAYBE_NULL; | |
| 5577 | + reg->mem_size = t->size; | |
| 5578 | + reg->id = ++env->id_gen; | |
| 5579 | + | |
| 5580 | + return 0; | |
| 5581 | +} | |
| 5582 | + | |
| 5494 | 5583 | static int check_map_kptr_access(struct bpf_verifier_env *env, u32 regno, |
| 5495 | 5584 | int value_regno, int insn_idx, |
| 5496 | 5585 | struct btf_field *kptr_field) |
| @@ -5519,9 +5608,15 @@ static int check_map_kptr_access(struct bpf_verifier_env *env, u32 regno, · 22 unmodified lines | ||
| 5519 | 5608 | verbose(env, "store to referenced kptr disallowed\n"); |
| 5520 | 5609 | return -EACCES; |
| 5521 | 5610 | } |
| 5611 | + if (class != BPF_LDX && kptr_field->type == BPF_UPTR) { | |
| 5612 | + verbose(env, "store to uptr disallowed\n"); | |
| 5613 | + return -EACCES; | |
| 5614 | + } | |
| 5522 | 5615 | |
| 5523 | 5616 | if (class == BPF_LDX) { |
| 5524 | − val_reg = reg_state(env, value_regno); | |
| 5617 | + if (kptr_field->type == BPF_UPTR) | |
| 5618 | + return mark_uptr_ld_reg(env, value_regno, kptr_field); | |
| 5619 | + | |
| 5525 | 5620 | /* We can simply mark the value_regno receiving the pointer |
| 5526 | 5621 | * value from map as PTR_TO_BTF_ID, with the correct type. |
| 5527 | 5622 | */ |
| @@ -5579,21 +5674,26 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, · 51 unmodified lines | ||
| 5579 | 5674 | case BPF_KPTR_UNREF: |
| 5580 | 5675 | case BPF_KPTR_REF: |
| 5581 | 5676 | case BPF_KPTR_PERCPU: |
| 5677 | + case BPF_UPTR: | |
| 5582 | 5678 | if (src != ACCESS_DIRECT) { |
| 5583 | − verbose(env, "kptr cannot be accessed indirectly by helper\n"); | |
| 5679 | + verbose(env, "%s cannot be accessed indirectly by helper\n", | |
| 5680 | + btf_field_type_name(field->type)); | |
| 5584 | 5681 | return -EACCES; |
| 5585 | 5682 | } |
| 5586 | 5683 | if (!tnum_is_const(reg->var_off)) { |
| 5587 | − verbose(env, "kptr access cannot have variable offset\n"); | |
| 5684 | + verbose(env, "%s access cannot have variable offset\n", | |
| 5685 | + btf_field_type_name(field->type)); | |
| 5588 | 5686 | return -EACCES; |
| 5589 | 5687 | } |
| 5590 | 5688 | if (p != off + reg->var_off.value) { |
| 5591 | − verbose(env, "kptr access misaligned expected=%u off=%llu\n", | |
| 5689 | + verbose(env, "%s access misaligned expected=%u off=%llu\n", | |
| 5690 | + btf_field_type_name(field->type), | |
| 5592 | 5691 | p, off + reg->var_off.value); |
| 5593 | 5692 | return -EACCES; |
| 5594 | 5693 | } |
| 5595 | 5694 | if (size != bpf_size_to_bytes(BPF_DW)) { |
| 5596 | − verbose(env, "kptr access size must be BPF_DW\n"); | |
| 5695 | + verbose(env, "%s access size must be BPF_DW\n", | |
| 5696 | + btf_field_type_name(field->type)); | |
| 5597 | 5697 | return -EACCES; |
| 5598 | 5698 | } |
| 5599 | 5699 | break; |
| @@ -5988,6 +6088,34 @@ static int check_ptr_alignment(struct bpf_verifier_env *env, · 388 unmodified lines | ||
| 5988 | 6088 | strict); |
| 5989 | 6089 | } |
| 5990 | 6090 | |
| 6091 | +static enum priv_stack_mode bpf_enable_priv_stack(struct bpf_prog *prog) | |
| 6092 | +{ | |
| 6093 | + if (!bpf_jit_supports_private_stack()) | |
| 6094 | + return NO_PRIV_STACK; | |
| 6095 | + | |
| 6096 | + /* bpf_prog_check_recur() checks all prog types that use bpf trampoline | |
| 6097 | + * while kprobe/tp/perf_event/raw_tp don't use trampoline hence checked | |
| 6098 | + * explicitly. | |
| 6099 | + */ | |
| 6100 | + switch (prog->type) { | |
| 6101 | + case BPF_PROG_TYPE_KPROBE: | |
| 6102 | + case BPF_PROG_TYPE_TRACEPOINT: | |
| 6103 | + case BPF_PROG_TYPE_PERF_EVENT: | |
| 6104 | + case BPF_PROG_TYPE_RAW_TRACEPOINT: | |
| 6105 | + return PRIV_STACK_ADAPTIVE; | |
| 6106 | + case BPF_PROG_TYPE_TRACING: | |
| 6107 | + case BPF_PROG_TYPE_LSM: | |
| 6108 | + case BPF_PROG_TYPE_STRUCT_OPS: | |
| 6109 | + if (prog->aux->priv_stack_requested || bpf_prog_check_recur(prog)) | |
| 6110 | + return PRIV_STACK_ADAPTIVE; | |
| 6111 | + fallthrough; | |
| 6112 | + default: | |
| 6113 | + break; | |
| 6114 | + } | |
| 6115 | + | |
| 6116 | + return NO_PRIV_STACK; | |
| 6117 | +} | |
| 6118 | + | |
| 5991 | 6119 | static int round_up_stack_depth(struct bpf_verifier_env *env, int stack_depth) |
| 5992 | 6120 | { |
| 5993 | 6121 | if (env->prog->jit_requested) |
| @@ -6005,17 +6133,20 @@ static int round_up_stack_depth(struct bpf_verifier_env *env, int stack_depth) · 11 unmodified lines | ||
| 6005 | 6133 | * Since recursion is prevented by check_cfg() this algorithm |
| 6006 | 6134 | * only needs a local stack of MAX_CALL_FRAMES to remember callsites |
| 6007 | 6135 | */ |
| 6008 | −static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx) | |
| 6136 | +static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx, | |
| 6137 | + bool priv_stack_supported) | |
| 6009 | 6138 | { |
| 6010 | 6139 | struct bpf_subprog_info *subprog = env->subprog_info; |
| 6011 | 6140 | struct bpf_insn *insn = env->prog->insnsi; |
| 6012 | − int depth = 0, frame = 0, i, subprog_end; | |
| 6141 | + int depth = 0, frame = 0, i, subprog_end, subprog_depth; | |
| 6013 | 6142 | bool tail_call_reachable = false; |
| 6014 | 6143 | int ret_insn[MAX_CALL_FRAMES]; |
| 6015 | 6144 | int ret_prog[MAX_CALL_FRAMES]; |
| 6016 | 6145 | int j; |
| 6017 | 6146 | |
| 6018 | 6147 | i = subprog[idx].start; |
| 6148 | + if (!priv_stack_supported) | |
| 6149 | + subprog[idx].priv_stack_mode = NO_PRIV_STACK; | |
| 6019 | 6150 | process_func: |
| 6020 | 6151 | /* protect against potential stack overflow that might happen when |
| 6021 | 6152 | * bpf2bpf calls get combined with tailcalls. Limit the caller's stack |
| @@ -6042,11 +6173,31 @@ process_func: · 20 unmodified lines | ||
| 6042 | 6173 | depth); |
| 6043 | 6174 | return -EACCES; |
| 6044 | 6175 | } |
| 6045 | − depth += round_up_stack_depth(env, subprog[idx].stack_depth); | |
| 6046 | − if (depth > MAX_BPF_STACK) { | |
| 6047 | − verbose(env, "combined stack size of %d calls is %d. Too large\n", | |
| 6048 | − frame + 1, depth); | |
| 6049 | − return -EACCES; | |
| 6176 | + | |
| 6177 | + subprog_depth = round_up_stack_depth(env, subprog[idx].stack_depth); | |
| 6178 | + if (priv_stack_supported) { | |
| 6179 | + /* Request private stack support only if the subprog stack | |
| 6180 | + * depth is no less than BPF_PRIV_STACK_MIN_SIZE. This is to | |
| 6181 | + * avoid jit penalty if the stack usage is small. | |
| 6182 | + */ | |
| 6183 | + if (subprog[idx].priv_stack_mode == PRIV_STACK_UNKNOWN && | |
| 6184 | + subprog_depth >= BPF_PRIV_STACK_MIN_SIZE) | |
| 6185 | + subprog[idx].priv_stack_mode = PRIV_STACK_ADAPTIVE; | |
| 6186 | + } | |
| 6187 | + | |
| 6188 | + if (subprog[idx].priv_stack_mode == PRIV_STACK_ADAPTIVE) { | |
| 6189 | + if (subprog_depth > MAX_BPF_STACK) { | |
| 6190 | + verbose(env, "stack size of subprog %d is %d. Too large\n", | |
| 6191 | + idx, subprog_depth); | |
| 6192 | + return -EACCES; | |
| 6193 | + } | |
| 6194 | + } else { | |
| 6195 | + depth += subprog_depth; | |
| 6196 | + if (depth > MAX_BPF_STACK) { | |
| 6197 | + verbose(env, "combined stack size of %d calls is %d. Too large\n", | |
| 6198 | + frame + 1, depth); | |
| 6199 | + return -EACCES; | |
| 6200 | + } | |
| 6050 | 6201 | } |
| 6051 | 6202 | continue_func: |
| 6052 | 6203 | subprog_end = subprog[idx + 1].start; |
| @@ -6103,6 +6254,8 @@ continue_func: · 50 unmodified lines | ||
| 6103 | 6254 | } |
| 6104 | 6255 | i = next_insn; |
| 6105 | 6256 | idx = sidx; |
| 6257 | + if (!priv_stack_supported) | |
| 6258 | + subprog[idx].priv_stack_mode = NO_PRIV_STACK; | |
| 6106 | 6259 | |
| 6107 | 6260 | if (subprog[idx].has_tail_call) |
| 6108 | 6261 | tail_call_reachable = true; |
| @@ -6136,7 +6289,8 @@ continue_func: · 27 unmodified lines | ||
| 6136 | 6289 | */ |
| 6137 | 6290 | if (frame == 0) |
| 6138 | 6291 | return 0; |
| 6139 | − depth -= round_up_stack_depth(env, subprog[idx].stack_depth); | |
| 6292 | + if (subprog[idx].priv_stack_mode != PRIV_STACK_ADAPTIVE) | |
| 6293 | + depth -= round_up_stack_depth(env, subprog[idx].stack_depth); | |
| 6140 | 6294 | frame--; |
| 6141 | 6295 | i = ret_insn[frame]; |
| 6142 | 6296 | idx = ret_prog[frame]; |
| @@ -6145,17 +6299,45 @@ continue_func: · 2 unmodified lines | ||
| 6145 | 6299 | |
| 6146 | 6300 | static int check_max_stack_depth(struct bpf_verifier_env *env) |
| 6147 | 6301 | { |
| 6302 | + enum priv_stack_mode priv_stack_mode = PRIV_STACK_UNKNOWN; | |
| 6148 | 6303 | struct bpf_subprog_info *si = env->subprog_info; |
| 6304 | + bool priv_stack_supported; | |
| 6149 | 6305 | int ret; |
| 6150 | 6306 | |
| 6151 | 6307 | for (int i = 0; i < env->subprog_cnt; i++) { |
| 6308 | + if (si[i].has_tail_call) { | |
| 6309 | + priv_stack_mode = NO_PRIV_STACK; | |
| 6310 | + break; | |
| 6311 | + } | |
| 6312 | + } | |
| 6313 | + | |
| 6314 | + if (priv_stack_mode == PRIV_STACK_UNKNOWN) | |
| 6315 | + priv_stack_mode = bpf_enable_priv_stack(env->prog); | |
| 6316 | + | |
| 6317 | + /* All async_cb subprogs use normal kernel stack. If a particular | |
| 6318 | + * subprog appears in both main prog and async_cb subtree, that | |
| 6319 | + * subprog will use normal kernel stack to avoid potential nesting. | |
| 6320 | + * The reverse subprog traversal ensures when main prog subtree is | |
| 6321 | + * checked, the subprogs appearing in async_cb subtrees are already | |
| 6322 | + * marked as using normal kernel stack, so stack size checking can | |
| 6323 | + * be done properly. | |
| 6324 | + */ | |
| 6325 | + for (int i = env->subprog_cnt - 1; i >= 0; i--) { | |
| 6152 | 6326 | if (!i || si[i].is_async_cb) { |
| 6153 | − ret = check_max_stack_depth_subprog(env, i); | |
| 6327 | + priv_stack_supported = !i && priv_stack_mode == PRIV_STACK_ADAPTIVE; | |
| 6328 | + ret = check_max_stack_depth_subprog(env, i, priv_stack_supported); | |
| 6154 | 6329 | if (ret < 0) |
| 6155 | 6330 | return ret; |
| 6156 | 6331 | } |
| 6157 | − continue; | |
| 6158 | 6332 | } |
| 6333 | + | |
| 6334 | + for (int i = 0; i < env->subprog_cnt; i++) { | |
| 6335 | + if (si[i].priv_stack_mode == PRIV_STACK_ADAPTIVE) { | |
| 6336 | + env->prog->aux->jits_use_priv_stack = true; | |
| 6337 | + break; | |
| 6338 | + } | |
| 6339 | + } | |
| 6340 | + | |
| 6159 | 6341 | return 0; |
| 6160 | 6342 | } |
| 6161 | 6343 | |
| @@ -6595,6 +6777,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, · 433 unmodified lines | ||
| 6595 | 6777 | const char *field_name = NULL; |
| 6596 | 6778 | enum bpf_type_flag flag = 0; |
| 6597 | 6779 | u32 btf_id = 0; |
| 6780 | + bool mask; | |
| 6598 | 6781 | int ret; |
| 6599 | 6782 | |
| 6600 | 6783 | if (!env->allow_ptr_leaks) { |
| @@ -6666,7 +6849,21 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, · 65 unmodified lines | ||
| 6666 | 6849 | |
| 6667 | 6850 | if (ret < 0) |
| 6668 | 6851 | return ret; |
| 6669 | − | |
| 6852 | + /* For raw_tp progs, we allow dereference of PTR_MAYBE_NULL | |
| 6853 | + * trusted PTR_TO_BTF_ID, these are the ones that are possibly | |
| 6854 | + * arguments to the raw_tp. Since internal checks in for trusted | |
| 6855 | + * reg in check_ptr_to_btf_access would consider PTR_MAYBE_NULL | |
| 6856 | + * modifier as problematic, mask it out temporarily for the | |
| 6857 | + * check. Don't apply this to pointers with ref_obj_id > 0, as | |
| 6858 | + * those won't be raw_tp args. | |
| 6859 | + * | |
| 6860 | + * We may end up applying this relaxation to other trusted | |
| 6861 | + * PTR_TO_BTF_ID with maybe null flag, since we cannot | |
| 6862 | + * distinguish PTR_MAYBE_NULL tagged for arguments vs normal | |
| 6863 | + * tagging, but that should expand allowed behavior, and not | |
| 6864 | + * cause regression for existing behavior. | |
| 6865 | + */ | |
| 6866 | + mask = mask_raw_tp_reg(env, reg); | |
| 6670 | 6867 | if (ret != PTR_TO_BTF_ID) { |
| 6671 | 6868 | /* just mark; */ |
| 6672 | 6869 | |
| @@ -6727,8 +6924,13 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, · 54 unmodified lines | ||
| 6727 | 6924 | clear_trusted_flags(&flag); |
| 6728 | 6925 | } |
| 6729 | 6926 | |
| 6730 | − if (atype == BPF_READ && value_regno >= 0) | |
| 6927 | + if (atype == BPF_READ && value_regno >= 0) { | |
| 6731 | 6928 | mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag); |
| 6929 | + /* We've assigned a new type to regno, so don't undo masking. */ | |
| 6930 | + if (regno == value_regno) | |
| 6931 | + mask = false; | |
| 6932 | + } | |
| 6933 | + unmask_raw_tp_reg(reg, mask); | |
| 6732 | 6934 | |
| 6733 | 6935 | return 0; |
| 6734 | 6936 | } |
| @@ -6949,7 +7151,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn · 214 unmodified lines | ||
| 6949 | 7151 | return err; |
| 6950 | 7152 | if (tnum_is_const(reg->var_off)) |
| 6951 | 7153 | kptr_field = btf_record_find(reg->map_ptr->record, |
| 6952 | − off + reg->var_off.value, BPF_KPTR); | |
| 7154 | + off + reg->var_off.value, BPF_KPTR | BPF_UPTR); | |
| 6953 | 7155 | if (kptr_field) { |
| 6954 | 7156 | err = check_map_kptr_access(env, regno, value_regno, insn_idx, kptr_field); |
| 6955 | 7157 | } else if (t == BPF_READ && value_regno >= 0) { |
| @@ -7103,7 +7305,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn · 147 unmodified lines | ||
| 7103 | 7305 | if (!err && t == BPF_READ && value_regno >= 0) |
| 7104 | 7306 | mark_reg_unknown(env, regs, value_regno); |
| 7105 | 7307 | } else if (base_type(reg->type) == PTR_TO_BTF_ID && |
| 7106 | − !type_may_be_null(reg->type)) { | |
| 7308 | + (mask_raw_tp_reg_cond(env, reg) || !type_may_be_null(reg->type))) { | |
| 7107 | 7309 | err = check_ptr_to_btf_access(env, regs, regno, off, size, t, |
| 7108 | 7310 | value_regno); |
| 7109 | 7311 | } else if (reg->type == CONST_PTR_TO_MAP) { |
| @@ -7648,19 +7850,20 @@ static int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg · 538 unmodified lines | ||
| 7648 | 7850 | * Since only one bpf_spin_lock is allowed the checks are simpler than |
| 7649 | 7851 | * reg_is_refcounted() logic. The verifier needs to remember only |
| 7650 | 7852 | * one spin_lock instead of array of acquired_refs. |
| 7651 | − * cur_state->active_lock remembers which map value element or allocated | |
| 7853 | + * cur_func(env)->active_locks remembers which map value element or allocated | |
| 7652 | 7854 | * object got locked and clears it after bpf_spin_unlock. |
| 7653 | 7855 | */ |
| 7654 | 7856 | static int process_spin_lock(struct bpf_verifier_env *env, int regno, |
| 7655 | 7857 | bool is_lock) |
| 7656 | 7858 | { |
| 7657 | 7859 | struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; |
| 7658 | − struct bpf_verifier_state *cur = env->cur_state; | |
| 7659 | 7860 | bool is_const = tnum_is_const(reg->var_off); |
| 7861 | + struct bpf_func_state *cur = cur_func(env); | |
| 7660 | 7862 | u64 val = reg->var_off.value; |
| 7661 | 7863 | struct bpf_map *map = NULL; |
| 7662 | 7864 | struct btf *btf = NULL; |
| 7663 | 7865 | struct btf_record *rec; |
| 7866 | + int err; | |
| 7664 | 7867 | |
| 7665 | 7868 | if (!is_const) { |
| 7666 | 7869 | verbose(env, |
| @@ -7692,16 +7895,23 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, · 25 unmodified lines | ||
| 7692 | 7895 | return -EINVAL; |
| 7693 | 7896 | } |
| 7694 | 7897 | if (is_lock) { |
| 7695 | − if (cur->active_lock.ptr) { | |
| 7898 | + void *ptr; | |
| 7899 | + | |
| 7900 | + if (map) | |
| 7901 | + ptr = map; | |
| 7902 | + else | |
| 7903 | + ptr = btf; | |
| 7904 | + | |
| 7905 | + if (cur->active_locks) { | |
| 7696 | 7906 | verbose(env, |
| 7697 | 7907 | "Locking two bpf_spin_locks are not allowed\n"); |
| 7698 | 7908 | return -EINVAL; |
| 7699 | 7909 | } |
| 7700 | − if (map) | |
| 7701 | − cur->active_lock.ptr = map; | |
| 7702 | − else | |
| 7703 | − cur->active_lock.ptr = btf; | |
| 7704 | − cur->active_lock.id = reg->id; | |
| 7910 | + err = acquire_lock_state(env, env->insn_idx, REF_TYPE_LOCK, reg->id, ptr); | |
| 7911 | + if (err < 0) { | |
| 7912 | + verbose(env, "Failed to acquire lock state\n"); | |
| 7913 | + return err; | |
| 7914 | + } | |
| 7705 | 7915 | } else { |
| 7706 | 7916 | void *ptr; |
| 7707 | 7917 | |
| @@ -7710,20 +7920,17 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, · 2 unmodified lines | ||
| 7710 | 7920 | else |
| 7711 | 7921 | ptr = btf; |
| 7712 | 7922 | |
| 7713 | − if (!cur->active_lock.ptr) { | |
| 7923 | + if (!cur->active_locks) { | |
| 7714 | 7924 | verbose(env, "bpf_spin_unlock without taking a lock\n"); |
| 7715 | 7925 | return -EINVAL; |
| 7716 | 7926 | } |
| 7717 | − if (cur->active_lock.ptr != ptr || | |
| 7718 | − cur->active_lock.id != reg->id) { | |
| 7927 | + | |
| 7928 | + if (release_lock_state(cur_func(env), REF_TYPE_LOCK, reg->id, ptr)) { | |
| 7719 | 7929 | verbose(env, "bpf_spin_unlock of different lock\n"); |
| 7720 | 7930 | return -EINVAL; |
| 7721 | 7931 | } |
| 7722 | 7932 | |
| 7723 | 7933 | invalidate_non_owning_refs(env); |
| 7724 | − | |
| 7725 | − cur->active_lock.ptr = NULL; | |
| 7726 | − cur->active_lock.id = 0; | |
| 7727 | 7934 | } |
| 7728 | 7935 | return 0; |
| 7729 | 7936 | } |
| @@ -8796,6 +9003,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, · 1066 unmodified lines | ||
| 8796 | 9003 | enum bpf_reg_type type = reg->type; |
| 8797 | 9004 | u32 *arg_btf_id = NULL; |
| 8798 | 9005 | int err = 0; |
| 9006 | + bool mask; | |
| 8799 | 9007 | |
| 8800 | 9008 | if (arg_type == ARG_DONTCARE) |
| 8801 | 9009 | return 0; |
| @@ -8836,11 +9044,11 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, · 34 unmodified lines | ||
| 8836 | 9044 | base_type(arg_type) == ARG_PTR_TO_SPIN_LOCK) |
| 8837 | 9045 | arg_btf_id = fn->arg_btf_id[arg]; |
| 8838 | 9046 | |
| 9047 | + mask = mask_raw_tp_reg(env, reg); | |
| 8839 | 9048 | err = check_reg_type(env, regno, arg_type, arg_btf_id, meta); |
| 8840 | − if (err) | |
| 8841 | − return err; | |
| 8842 | 9049 | |
| 8843 | − err = check_func_arg_reg_off(env, reg, regno, arg_type); | |
| 9050 | + err = err ?: check_func_arg_reg_off(env, reg, regno, arg_type); | |
| 9051 | + unmask_raw_tp_reg(reg, mask); | |
| 8844 | 9052 | if (err) |
| 8845 | 9053 | return err; |
| 8846 | 9054 | |
| @@ -9635,14 +9843,17 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, · 788 unmodified lines | ||
| 9635 | 9843 | return ret; |
| 9636 | 9844 | } else if (base_type(arg->arg_type) == ARG_PTR_TO_BTF_ID) { |
| 9637 | 9845 | struct bpf_call_arg_meta meta; |
| 9846 | + bool mask; | |
| 9638 | 9847 | int err; |
| 9639 | 9848 | |
| 9640 | 9849 | if (register_is_null(reg) && type_may_be_null(arg->arg_type)) |
| 9641 | 9850 | continue; |
| 9642 | 9851 | |
| 9643 | 9852 | memset(&meta, 0, sizeof(meta)); /* leave func_id as zero */ |
| 9853 | + mask = mask_raw_tp_reg(env, reg); | |
| 9644 | 9854 | err = check_reg_type(env, regno, arg->arg_type, &arg->btf_id, &meta); |
| 9645 | 9855 | err = err ?: check_func_arg_reg_off(env, reg, regno, arg->arg_type); |
| 9856 | + unmask_raw_tp_reg(reg, mask); | |
| 9646 | 9857 | if (err) |
| 9647 | 9858 | return err; |
| 9648 | 9859 | } else { |
| @@ -9781,7 +9992,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, · 132 unmodified lines | ||
| 9781 | 9992 | const char *sub_name = subprog_name(env, subprog); |
| 9782 | 9993 | |
| 9783 | 9994 | /* Only global subprogs cannot be called with a lock held. */ |
| 9784 | − if (env->cur_state->active_lock.ptr) { | |
| 9995 | + if (cur_func(env)->active_locks) { | |
| 9785 | 9996 | verbose(env, "global function calls are not allowed while holding a lock,\n" |
| 9786 | 9997 | "use static function instead\n"); |
| 9787 | 9998 | return -EINVAL; |
| @@ -9910,7 +10121,7 @@ static int set_loop_callback_state(struct bpf_verifier_env *env, · 122 unmodified lines | ||
| 9910 | 10121 | { |
| 9911 | 10122 | /* bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, |
| 9912 | 10123 | * u64 flags); |
| 9913 | − * callback_fn(u32 index, void *callback_ctx); | |
| 10124 | + * callback_fn(u64 index, void *callback_ctx); | |
| 9914 | 10125 | */ |
| 9915 | 10126 | callee->regs[BPF_REG_1].type = SCALAR_VALUE; |
| 9916 | 10127 | callee->regs[BPF_REG_2] = caller->regs[BPF_REG_3]; |
| @@ -10122,17 +10333,10 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) · 205 unmodified lines | ||
| 10122 | 10333 | caller->regs[BPF_REG_0] = *r0; |
| 10123 | 10334 | } |
| 10124 | 10335 | |
| 10125 | − /* callback_fn frame should have released its own additions to parent's | |
| 10126 | − * reference state at this point, or check_reference_leak would | |
| 10127 | − * complain, hence it must be the same as the caller. There is no need | |
| 10128 | − * to copy it back. | |
| 10129 | − */ | |
| 10130 | − if (!callee->in_callback_fn) { | |
| 10131 | − /* Transfer references to the caller */ | |
| 10132 | − err = copy_reference_state(caller, callee); | |
| 10133 | − if (err) | |
| 10134 | − return err; | |
| 10135 | − } | |
| 10336 | + /* Transfer references to the caller */ | |
| 10337 | + err = copy_reference_state(caller, callee); | |
| 10338 | + if (err) | |
| 10339 | + return err; | |
| 10136 | 10340 | |
| 10137 | 10341 | /* for callbacks like bpf_loop or bpf_for_each_map_elem go back to callsite, |
| 10138 | 10342 | * there function call logic would reschedule callback visit. If iteration |
| @@ -10302,11 +10506,11 @@ static int check_reference_leak(struct bpf_verifier_env *env, bool exception_exi · 163 unmodified lines | ||
| 10302 | 10506 | bool refs_lingering = false; |
| 10303 | 10507 | int i; |
| 10304 | 10508 | |
| 10305 | − if (!exception_exit && state->frameno && !state->in_callback_fn) | |
| 10509 | + if (!exception_exit && state->frameno) | |
| 10306 | 10510 | return 0; |
| 10307 | 10511 | |
| 10308 | 10512 | for (i = 0; i < state->acquired_refs; i++) { |
| 10309 | − if (!exception_exit && state->in_callback_fn && state->refs[i].callback_ref != state->frameno) | |
| 10513 | + if (state->refs[i].type != REF_TYPE_PTR) | |
| 10310 | 10514 | continue; |
| 10311 | 10515 | verbose(env, "Unreleased reference id=%d alloc_insn=%d\n", |
| 10312 | 10516 | state->refs[i].id, state->refs[i].insn_idx); |
| @@ -10315,6 +10519,34 @@ static int check_reference_leak(struct bpf_verifier_env *env, bool exception_exi · 2 unmodified lines | ||
| 10315 | 10519 | return refs_lingering ? -EINVAL : 0; |
| 10316 | 10520 | } |
| 10317 | 10521 | |
| 10522 | +static int check_resource_leak(struct bpf_verifier_env *env, bool exception_exit, bool check_lock, const char *prefix) | |
| 10523 | +{ | |
| 10524 | + int err; | |
| 10525 | + | |
| 10526 | + if (check_lock && cur_func(env)->active_locks) { | |
| 10527 | + verbose(env, "%s cannot be used inside bpf_spin_lock-ed region\n", prefix); | |
| 10528 | + return -EINVAL; | |
| 10529 | + } | |
| 10530 | + | |
| 10531 | + err = check_reference_leak(env, exception_exit); | |
| 10532 | + if (err) { | |
| 10533 | + verbose(env, "%s would lead to reference leak\n", prefix); | |
| 10534 | + return err; | |
| 10535 | + } | |
| 10536 | + | |
| 10537 | + if (check_lock && env->cur_state->active_rcu_lock) { | |
| 10538 | + verbose(env, "%s cannot be used inside bpf_rcu_read_lock-ed region\n", prefix); | |
| 10539 | + return -EINVAL; | |
| 10540 | + } | |
| 10541 | + | |
| 10542 | + if (check_lock && env->cur_state->active_preempt_lock) { | |
| 10543 | + verbose(env, "%s cannot be used inside bpf_preempt_disable-ed region\n", prefix); | |
| 10544 | + return -EINVAL; | |
| 10545 | + } | |
| 10546 | + | |
| 10547 | + return 0; | |
| 10548 | +} | |
| 10549 | + | |
| 10318 | 10550 | static int check_bpf_snprintf_call(struct bpf_verifier_env *env, |
| 10319 | 10551 | struct bpf_reg_state *regs) |
| 10320 | 10552 | { |
| @@ -10583,11 +10815,9 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn · 262 unmodified lines | ||
| 10583 | 10815 | |
| 10584 | 10816 | switch (func_id) { |
| 10585 | 10817 | case BPF_FUNC_tail_call: |
| 10586 | − err = check_reference_leak(env, false); | |
| 10587 | − if (err) { | |
| 10588 | − verbose(env, "tail_call would lead to reference leak\n"); | |
| 10818 | + err = check_resource_leak(env, false, true, "tail_call"); | |
| 10819 | + if (err) | |
| 10589 | 10820 | return err; |
| 10590 | − } | |
| 10591 | 10821 | break; |
| 10592 | 10822 | case BPF_FUNC_get_local_storage: |
| 10593 | 10823 | /* check that flags argument in get_local_storage(map, flags) is 0, |
| @@ -11252,6 +11482,7 @@ enum special_kfunc_type { · 658 unmodified lines | ||
| 11252 | 11482 | KF_bpf_preempt_enable, |
| 11253 | 11483 | KF_bpf_iter_css_task_new, |
| 11254 | 11484 | KF_bpf_session_cookie, |
| 11485 | + KF_bpf_get_kmem_cache, | |
| 11255 | 11486 | }; |
| 11256 | 11487 | |
| 11257 | 11488 | BTF_SET_START(special_kfunc_set) |
| @@ -11317,6 +11548,7 @@ BTF_ID(func, bpf_session_cookie) · 59 unmodified lines | ||
| 11317 | 11548 | #else |
| 11318 | 11549 | BTF_ID_UNUSED |
| 11319 | 11550 | #endif |
| 11551 | +BTF_ID(func, bpf_get_kmem_cache) | |
| 11320 | 11552 | |
| 11321 | 11553 | static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta) |
| 11322 | 11554 | { |
| @@ -11512,10 +11744,9 @@ static int process_kf_arg_ptr_to_btf_id(struct bpf_verifier_env *env, · 189 unmodified lines | ||
| 11512 | 11744 | |
| 11513 | 11745 | static int ref_set_non_owning(struct bpf_verifier_env *env, struct bpf_reg_state *reg) |
| 11514 | 11746 | { |
| 11515 | − struct bpf_verifier_state *state = env->cur_state; | |
| 11516 | 11747 | struct btf_record *rec = reg_btf_record(reg); |
| 11517 | 11748 | |
| 11518 | − if (!state->active_lock.ptr) { | |
| 11749 | + if (!cur_func(env)->active_locks) { | |
| 11519 | 11750 | verbose(env, "verifier internal error: ref_set_non_owning w/o active lock\n"); |
| 11520 | 11751 | return -EFAULT; |
| 11521 | 11752 | } |
| @@ -11612,6 +11843,7 @@ static int ref_convert_owning_non_owning(struct bpf_verifier_env *env, u32 ref_o · 90 unmodified lines | ||
| 11612 | 11843 | */ |
| 11613 | 11844 | static int check_reg_allocation_locked(struct bpf_verifier_env *env, struct bpf_reg_state *reg) |
| 11614 | 11845 | { |
| 11846 | + struct bpf_reference_state *s; | |
| 11615 | 11847 | void *ptr; |
| 11616 | 11848 | u32 id; |
| 11617 | 11849 | |
| @@ -11628,10 +11860,10 @@ static int check_reg_allocation_locked(struct bpf_verifier_env *env, struct bpf_ · 10 unmodified lines | ||
| 11628 | 11860 | } |
| 11629 | 11861 | id = reg->id; |
| 11630 | 11862 | |
| 11631 | − if (!env->cur_state->active_lock.ptr) | |
| 11863 | + if (!cur_func(env)->active_locks) | |
| 11632 | 11864 | return -EINVAL; |
| 11633 | − if (env->cur_state->active_lock.ptr != ptr || | |
| 11634 | − env->cur_state->active_lock.id != id) { | |
| 11865 | + s = find_lock_state(env, REF_TYPE_LOCK, id, ptr); | |
| 11866 | + if (!s) { | |
| 11635 | 11867 | verbose(env, "held lock and object are not in the same allocation\n"); |
| 11636 | 11868 | return -EINVAL; |
| 11637 | 11869 | } |
| @@ -11942,6 +12174,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ · 304 unmodified lines | ||
| 11942 | 12174 | enum bpf_arg_type arg_type = ARG_DONTCARE; |
| 11943 | 12175 | u32 regno = i + 1, ref_id, type_size; |
| 11944 | 12176 | bool is_ret_buf_sz = false; |
| 12177 | + bool mask = false; | |
| 11945 | 12178 | int kf_arg_type; |
| 11946 | 12179 | |
| 11947 | 12180 | t = btf_type_skip_modifiers(btf, args[i].type, NULL); |
| @@ -12000,12 +12233,15 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ · 52 unmodified lines | ||
| 12000 | 12233 | return -EINVAL; |
| 12001 | 12234 | } |
| 12002 | 12235 | |
| 12236 | + mask = mask_raw_tp_reg(env, reg); | |
| 12003 | 12237 | if ((is_kfunc_trusted_args(meta) || is_kfunc_rcu(meta)) && |
| 12004 | 12238 | (register_is_null(reg) || type_may_be_null(reg->type)) && |
| 12005 | 12239 | !is_kfunc_arg_nullable(meta->btf, &args[i])) { |
| 12006 | 12240 | verbose(env, "Possibly NULL pointer passed to trusted arg%d\n", i); |
| 12241 | + unmask_raw_tp_reg(reg, mask); | |
| 12007 | 12242 | return -EACCES; |
| 12008 | 12243 | } |
| 12244 | + unmask_raw_tp_reg(reg, mask); | |
| 12009 | 12245 | |
| 12010 | 12246 | if (reg->ref_obj_id) { |
| 12011 | 12247 | if (is_kfunc_release(meta) && meta->ref_obj_id) { |
| @@ -12063,16 +12299,24 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ · 51 unmodified lines | ||
| 12063 | 12299 | if (!is_kfunc_trusted_args(meta) && !is_kfunc_rcu(meta)) |
| 12064 | 12300 | break; |
| 12065 | 12301 | |
| 12302 | + /* Allow passing maybe NULL raw_tp arguments to | |
| 12303 | + * kfuncs for compatibility. Don't apply this to | |
| 12304 | + * arguments with ref_obj_id > 0. | |
| 12305 | + */ | |
| 12306 | + mask = mask_raw_tp_reg(env, reg); | |
| 12066 | 12307 | if (!is_trusted_reg(reg)) { |
| 12067 | 12308 | if (!is_kfunc_rcu(meta)) { |
| 12068 | 12309 | verbose(env, "R%d must be referenced or trusted\n", regno); |
| 12310 | + unmask_raw_tp_reg(reg, mask); | |
| 12069 | 12311 | return -EINVAL; |
| 12070 | 12312 | } |
| 12071 | 12313 | if (!is_rcu_reg(reg)) { |
| 12072 | 12314 | verbose(env, "R%d must be a rcu pointer\n", regno); |
| 12315 | + unmask_raw_tp_reg(reg, mask); | |
| 12073 | 12316 | return -EINVAL; |
| 12074 | 12317 | } |
| 12075 | 12318 | } |
| 12319 | + unmask_raw_tp_reg(reg, mask); | |
| 12076 | 12320 | fallthrough; |
| 12077 | 12321 | case KF_ARG_PTR_TO_CTX: |
| 12078 | 12322 | case KF_ARG_PTR_TO_DYNPTR: |
| @@ -12095,7 +12339,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ · 16 unmodified lines | ||
| 12095 | 12339 | |
| 12096 | 12340 | if (is_kfunc_release(meta) && reg->ref_obj_id) |
| 12097 | 12341 | arg_type |= OBJ_RELEASE; |
| 12342 | + mask = mask_raw_tp_reg(env, reg); | |
| 12098 | 12343 | ret = check_func_arg_reg_off(env, reg, regno, arg_type); |
| 12344 | + unmask_raw_tp_reg(reg, mask); | |
| 12099 | 12345 | if (ret < 0) |
| 12100 | 12346 | return ret; |
| 12101 | 12347 | |
| @@ -12272,6 +12518,7 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ · 170 unmodified lines | ||
| 12272 | 12518 | ref_tname = btf_name_by_offset(btf, ref_t->name_off); |
| 12273 | 12519 | fallthrough; |
| 12274 | 12520 | case KF_ARG_PTR_TO_BTF_ID: |
| 12521 | + mask = mask_raw_tp_reg(env, reg); | |
| 12275 | 12522 | /* Only base_type is checked, further checks are done here */ |
| 12276 | 12523 | if ((base_type(reg->type) != PTR_TO_BTF_ID || |
| 12277 | 12524 | (bpf_type_has_unsafe_modifiers(reg->type) && !is_rcu_reg(reg))) && |
| @@ -12280,9 +12527,11 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ · 2 unmodified lines | ||
| 12280 | 12527 | verbose(env, "expected %s or socket\n", |
| 12281 | 12528 | reg_type_str(env, base_type(reg->type) | |
| 12282 | 12529 | (type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS))); |
| 12530 | + unmask_raw_tp_reg(reg, mask); | |
| 12283 | 12531 | return -EINVAL; |
| 12284 | 12532 | } |
| 12285 | 12533 | ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i); |
| 12534 | + unmask_raw_tp_reg(reg, mask); | |
| 12286 | 12535 | if (ret < 0) |
| 12287 | 12536 | return ret; |
| 12288 | 12537 | break; |
| @@ -12827,6 +13076,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, · 538 unmodified lines | ||
| 12827 | 13076 | regs[BPF_REG_0].type = PTR_TO_BTF_ID; |
| 12828 | 13077 | regs[BPF_REG_0].btf_id = ptr_type_id; |
| 12829 | 13078 | |
| 13079 | + if (meta.func_id == special_kfunc_list[KF_bpf_get_kmem_cache]) | |
| 13080 | + regs[BPF_REG_0].type |= PTR_UNTRUSTED; | |
| 13081 | + | |
| 12830 | 13082 | if (is_iter_next_kfunc(&meta)) { |
| 12831 | 13083 | struct bpf_reg_state *cur_iter; |
| 12832 | 13084 | |
| @@ -13252,7 +13504,7 @@ static int sanitize_check_bounds(struct bpf_verifier_env *env, · 419 unmodified lines | ||
| 13252 | 13504 | */ |
| 13253 | 13505 | static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, |
| 13254 | 13506 | struct bpf_insn *insn, |
| 13255 | − const struct bpf_reg_state *ptr_reg, | |
| 13507 | + struct bpf_reg_state *ptr_reg, | |
| 13256 | 13508 | const struct bpf_reg_state *off_reg) |
| 13257 | 13509 | { |
| 13258 | 13510 | struct bpf_verifier_state *vstate = env->cur_state; |
| @@ -13266,6 +13518,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 7 unmodified lines | ||
| 13266 | 13518 | struct bpf_sanitize_info info = {}; |
| 13267 | 13519 | u8 opcode = BPF_OP(insn->code); |
| 13268 | 13520 | u32 dst = insn->dst_reg; |
| 13521 | + bool mask; | |
| 13269 | 13522 | int ret; |
| 13270 | 13523 | |
| 13271 | 13524 | dst_reg = ®s[dst]; |
| @@ -13292,11 +13545,14 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, · 20 unmodified lines | ||
| 13292 | 13545 | return -EACCES; |
| 13293 | 13546 | } |
| 13294 | 13547 | |
| 13548 | + mask = mask_raw_tp_reg(env, ptr_reg); | |
| 13295 | 13549 | if (ptr_reg->type & PTR_MAYBE_NULL) { |
| 13296 | 13550 | verbose(env, "R%d pointer arithmetic on %s prohibited, null-check it first\n", |
| 13297 | 13551 | dst, reg_type_str(env, ptr_reg->type)); |
| 13552 | + unmask_raw_tp_reg(ptr_reg, mask); | |
| 13298 | 13553 | return -EACCES; |
| 13299 | 13554 | } |
| 13555 | + unmask_raw_tp_reg(ptr_reg, mask); | |
| 13300 | 13556 | |
| 13301 | 13557 | switch (base_type(ptr_reg->type)) { |
| 13302 | 13558 | case PTR_TO_CTX: |
| @@ -15480,7 +15736,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, · 2177 unmodified lines | ||
| 15480 | 15736 | if (dst_reg->type == SCALAR_VALUE && dst_reg->id) |
| 15481 | 15737 | collect_linked_regs(this_branch, dst_reg->id, &linked_regs); |
| 15482 | 15738 | if (linked_regs.cnt > 1) { |
| 15483 | − err = push_jmp_history(env, this_branch, 0, linked_regs_pack(&linked_regs)); | |
| 15739 | + err = push_insn_history(env, this_branch, 0, linked_regs_pack(&linked_regs)); | |
| 15484 | 15740 | if (err) |
| 15485 | 15741 | return err; |
| 15486 | 15742 | } |
| @@ -15744,26 +16000,9 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) · 257 unmodified lines | ||
| 15744 | 16000 | * gen_ld_abs() may terminate the program at runtime, leading to |
| 15745 | 16001 | * reference leak. |
| 15746 | 16002 | */ |
| 15747 | − err = check_reference_leak(env, false); | |
| 15748 | − if (err) { | |
| 15749 | − verbose(env, "BPF_LD_[ABS|IND] cannot be mixed with socket references\n"); | |
| 16003 | + err = check_resource_leak(env, false, true, "BPF_LD_[ABS|IND]"); | |
| 16004 | + if (err) | |
| 15750 | 16005 | return err; |
| 15751 | − } | |
| 15752 | − | |
| 15753 | − if (env->cur_state->active_lock.ptr) { | |
| 15754 | − verbose(env, "BPF_LD_[ABS|IND] cannot be used inside bpf_spin_lock-ed region\n"); | |
| 15755 | − return -EINVAL; | |
| 15756 | − } | |
| 15757 | − | |
| 15758 | − if (env->cur_state->active_rcu_lock) { | |
| 15759 | − verbose(env, "BPF_LD_[ABS|IND] cannot be used inside bpf_rcu_read_lock-ed region\n"); | |
| 15760 | − return -EINVAL; | |
| 15761 | − } | |
| 15762 | − | |
| 15763 | − if (env->cur_state->active_preempt_lock) { | |
| 15764 | − verbose(env, "BPF_LD_[ABS|IND] cannot be used inside bpf_preempt_disable-ed region\n"); | |
| 15765 | − return -EINVAL; | |
| 15766 | − } | |
| 15767 | 16006 | |
| 15768 | 16007 | if (regs[ctx_reg].type != PTR_TO_CTX) { |
| 15769 | 16008 | verbose(env, |
| @@ -15909,6 +16148,16 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char · 139 unmodified lines | ||
| 15909 | 16148 | return -ENOTSUPP; |
| 15910 | 16149 | } |
| 15911 | 16150 | break; |
| 16151 | + case BPF_PROG_TYPE_KPROBE: | |
| 16152 | + switch (env->prog->expected_attach_type) { | |
| 16153 | + case BPF_TRACE_KPROBE_SESSION: | |
| 16154 | + case BPF_TRACE_UPROBE_SESSION: | |
| 16155 | + range = retval_range(0, 1); | |
| 16156 | + break; | |
| 16157 | + default: | |
| 16158 | + return 0; | |
| 16159 | + } | |
| 16160 | + break; | |
| 15912 | 16161 | case BPF_PROG_TYPE_SK_LOOKUP: |
| 15913 | 16162 | range = retval_range(SK_DROP, SK_PASS); |
| 15914 | 16163 | break; |
| @@ -16175,10 +16424,7 @@ static u32 kfunc_fastcall_clobber_mask(struct bpf_kfunc_call_arg_meta *meta) · 260 unmodified lines | ||
| 16175 | 16424 | /* Same as verifier_inlines_helper_call() but for kfuncs, see comment above */ |
| 16176 | 16425 | static bool is_fastcall_kfunc_call(struct bpf_kfunc_call_arg_meta *meta) |
| 16177 | 16426 | { |
| 16178 | − if (meta->btf == btf_vmlinux) | |
| 16179 | − return meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx] || | |
| 16180 | − meta->func_id == special_kfunc_list[KF_bpf_rdonly_cast]; | |
| 16181 | − return false; | |
| 16427 | + return meta->kfunc_flags & KF_FASTCALL; | |
| 16182 | 16428 | } |
| 16183 | 16429 | |
| 16184 | 16430 | /* LLVM define a bpf_fastcall function attribute. |
| @@ -17513,8 +17759,20 @@ static bool refsafe(struct bpf_func_state *old, struct bpf_func_state *cur, · 1328 unmodified lines | ||
| 17513 | 17759 | return false; |
| 17514 | 17760 | |
| 17515 | 17761 | for (i = 0; i < old->acquired_refs; i++) { |
| 17516 | − if (!check_ids(old->refs[i].id, cur->refs[i].id, idmap)) | |
| 17762 | + if (!check_ids(old->refs[i].id, cur->refs[i].id, idmap) || | |
| 17763 | + old->refs[i].type != cur->refs[i].type) | |
| 17517 | 17764 | return false; |
| 17765 | + switch (old->refs[i].type) { | |
| 17766 | + case REF_TYPE_PTR: | |
| 17767 | + break; | |
| 17768 | + case REF_TYPE_LOCK: | |
| 17769 | + if (old->refs[i].ptr != cur->refs[i].ptr) | |
| 17770 | + return false; | |
| 17771 | + break; | |
| 17772 | + default: | |
| 17773 | + WARN_ONCE(1, "Unhandled enum type for reference state: %d\n", old->refs[i].type); | |
| 17774 | + return false; | |
| 17775 | + } | |
| 17518 | 17776 | } |
| 17519 | 17777 | |
| 17520 | 17778 | return true; |
| @@ -17592,19 +17850,6 @@ static bool states_equal(struct bpf_verifier_env *env, · 71 unmodified lines | ||
| 17592 | 17850 | if (old->speculative && !cur->speculative) |
| 17593 | 17851 | return false; |
| 17594 | 17852 | |
| 17595 | − if (old->active_lock.ptr != cur->active_lock.ptr) | |
| 17596 | − return false; | |
| 17597 | − | |
| 17598 | − /* Old and cur active_lock's have to be either both present | |
| 17599 | − * or both absent. | |
| 17600 | − */ | |
| 17601 | − if (!!old->active_lock.id != !!cur->active_lock.id) | |
| 17602 | − return false; | |
| 17603 | − | |
| 17604 | − if (old->active_lock.id && | |
| 17605 | − !check_ids(old->active_lock.id, cur->active_lock.id, &env->idmap_scratch)) | |
| 17606 | − return false; | |
| 17607 | − | |
| 17608 | 17853 | if (old->active_rcu_lock != cur->active_rcu_lock) |
| 17609 | 17854 | return false; |
| 17610 | 17855 | |
| @@ -17880,7 +18125,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) · 269 unmodified lines | ||
| 17880 | 18125 | |
| 17881 | 18126 | force_new_state = env->test_state_freq || is_force_checkpoint(env, insn_idx) || |
| 17882 | 18127 | /* Avoid accumulating infinitely long jmp history */ |
| 17883 | − cur->jmp_history_cnt > 40; | |
| 18128 | + cur->insn_hist_end - cur->insn_hist_start > 40; | |
| 17884 | 18129 | |
| 17885 | 18130 | /* bpf progs typically have pruning point every 4 instructions |
| 17886 | 18131 | * http://vger.kernel.org/bpfconf2019.html#session-1 |
| @@ -18078,7 +18323,7 @@ hit: · 191 unmodified lines | ||
| 18078 | 18323 | * the current state. |
| 18079 | 18324 | */ |
| 18080 | 18325 | if (is_jmp_point(env, env->insn_idx)) |
| 18081 | − err = err ? : push_jmp_history(env, cur, 0, 0); | |
| 18326 | + err = err ? : push_insn_history(env, cur, 0, 0); | |
| 18082 | 18327 | err = err ? : propagate_precision(env, &sl->state); |
| 18083 | 18328 | if (err) |
| 18084 | 18329 | return err; |
| @@ -18177,8 +18422,8 @@ next: · 92 unmodified lines | ||
| 18177 | 18422 | |
| 18178 | 18423 | cur->parent = new; |
| 18179 | 18424 | cur->first_insn_idx = insn_idx; |
| 18425 | + cur->insn_hist_start = cur->insn_hist_end; | |
| 18180 | 18426 | cur->dfs_depth = new->dfs_depth + 1; |
| 18181 | − clear_jmp_history(cur); | |
| 18182 | 18427 | new_sl->next = *explored_state(env, insn_idx); |
| 18183 | 18428 | *explored_state(env, insn_idx) = new_sl; |
| 18184 | 18429 | /* connect new state to parentage chain. Current frame needs all |
| @@ -18346,7 +18591,7 @@ static int do_check(struct bpf_verifier_env *env) · 161 unmodified lines | ||
| 18346 | 18591 | } |
| 18347 | 18592 | |
| 18348 | 18593 | if (is_jmp_point(env, env->insn_idx)) { |
| 18349 | − err = push_jmp_history(env, state, 0, 0); | |
| 18594 | + err = push_insn_history(env, state, 0, 0); | |
| 18350 | 18595 | if (err) |
| 18351 | 18596 | return err; |
| 18352 | 18597 | } |
| @@ -18506,7 +18751,7 @@ static int do_check(struct bpf_verifier_env *env) · 153 unmodified lines | ||
| 18506 | 18751 | return -EINVAL; |
| 18507 | 18752 | } |
| 18508 | 18753 | |
| 18509 | − if (env->cur_state->active_lock.ptr) { | |
| 18754 | + if (cur_func(env)->active_locks) { | |
| 18510 | 18755 | if ((insn->src_reg == BPF_REG_0 && insn->imm != BPF_FUNC_spin_unlock) || |
| 18511 | 18756 | (insn->src_reg == BPF_PSEUDO_KFUNC_CALL && |
| 18512 | 18757 | (insn->off != 0 || !is_bpf_graph_api_kfunc(insn->imm)))) { |
| @@ -18555,30 +18800,14 @@ static int do_check(struct bpf_verifier_env *env) · 42 unmodified lines | ||
| 18555 | 18800 | return -EINVAL; |
| 18556 | 18801 | } |
| 18557 | 18802 | process_bpf_exit_full: |
| 18558 | − if (env->cur_state->active_lock.ptr && !env->cur_state->curframe) { | |
| 18559 | − verbose(env, "bpf_spin_unlock is missing\n"); | |
| 18560 | − return -EINVAL; | |
| 18561 | − } | |
| 18562 | − | |
| 18563 | − if (env->cur_state->active_rcu_lock && !env->cur_state->curframe) { | |
| 18564 | − verbose(env, "bpf_rcu_read_unlock is missing\n"); | |
| 18565 | − return -EINVAL; | |
| 18566 | − } | |
| 18567 | − | |
| 18568 | − if (env->cur_state->active_preempt_lock && !env->cur_state->curframe) { | |
| 18569 | − verbose(env, "%d bpf_preempt_enable%s missing\n", | |
| 18570 | − env->cur_state->active_preempt_lock, | |
| 18571 | − env->cur_state->active_preempt_lock == 1 ? " is" : "(s) are"); | |
| 18572 | − return -EINVAL; | |
| 18573 | − } | |
| 18574 | − | |
| 18575 | 18803 | /* We must do check_reference_leak here before |
| 18576 | 18804 | * prepare_func_exit to handle the case when |
| 18577 | 18805 | * state->curframe > 0, it may be a callback |
| 18578 | 18806 | * function, for which reference_state must |
| 18579 | 18807 | * match caller reference state when it exits. |
| 18580 | 18808 | */ |
| 18581 | − err = check_reference_leak(env, exception_exit); | |
| 18809 | + err = check_resource_leak(env, exception_exit, !env->cur_state->curframe, | |
| 18810 | + "BPF_EXIT instruction"); | |
| 18582 | 18811 | if (err) |
| 18583 | 18812 | return err; |
| 18584 | 18813 | |
| @@ -19837,6 +20066,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) · 1252 unmodified lines | ||
| 19837 | 20066 | * for this case. |
| 19838 | 20067 | */ |
| 19839 | 20068 | case PTR_TO_BTF_ID | MEM_ALLOC | PTR_UNTRUSTED: |
| 20069 | + case PTR_TO_BTF_ID | PTR_TRUSTED | PTR_MAYBE_NULL: | |
| 19840 | 20070 | if (type == BPF_READ) { |
| 19841 | 20071 | if (BPF_MODE(insn->code) == BPF_MEM) |
| 19842 | 20072 | insn->code = BPF_LDX | BPF_PROBE_MEM | |
| @@ -20041,6 +20271,9 @@ static int jit_subprogs(struct bpf_verifier_env *env) · 198 unmodified lines | ||
| 20041 | 20271 | |
| 20042 | 20272 | func[i]->aux->name[0] = 'F'; |
| 20043 | 20273 | func[i]->aux->stack_depth = env->subprog_info[i].stack_depth; |
| 20274 | + if (env->subprog_info[i].priv_stack_mode == PRIV_STACK_ADAPTIVE) | |
| 20275 | + func[i]->aux->jits_use_priv_stack = true; | |
| 20276 | + | |
| 20044 | 20277 | func[i]->jit_requested = 1; |
| 20045 | 20278 | func[i]->blinding_requested = prog->blinding_requested; |
| 20046 | 20279 | func[i]->aux->kfunc_tab = prog->aux->kfunc_tab; |
| @@ -21809,6 +22042,11 @@ static int check_struct_ops_btf_id(struct bpf_verifier_env *env) · 1762 unmodified lines | ||
| 21809 | 22042 | } |
| 21810 | 22043 | } |
| 21811 | 22044 | |
| 22045 | + if (prog->aux->priv_stack_requested && !bpf_jit_supports_private_stack()) { | |
| 22046 | + verbose(env, "Private stack not supported by jit\n"); | |
| 22047 | + return -EACCES; | |
| 22048 | + } | |
| 22049 | + | |
| 21812 | 22050 | /* btf_ctx_access() used this to provide argument type info */ |
| 21813 | 22051 | prog->aux->ctx_arg_info = |
| 21814 | 22052 | st_ops_desc->arg_info[member_idx].info; |
| @@ -22547,6 +22785,7 @@ err_unlock: · 732 unmodified lines | ||
| 22547 | 22785 | if (!is_priv) |
| 22548 | 22786 | mutex_unlock(&bpf_verifier_lock); |
| 22549 | 22787 | vfree(env->insn_aux_data); |
| 22788 | + kvfree(env->insn_hist); | |
| 22550 | 22789 | err_free_env: |
| 22551 | 22790 | kvfree(env); |
| 22552 | 22791 | return ret; |
| @@ -802,6 +802,8 @@ struct send_signal_irq_work { · 801 unmodified lines | ||
| 802 | 802 | struct task_struct *task; |
| 803 | 803 | u32 sig; |
| 804 | 804 | enum pid_type type; |
| 805 | + bool has_siginfo; | |
| 806 | + struct kernel_siginfo info; | |
| 805 | 807 | }; |
| 806 | 808 | |
| 807 | 809 | static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); |
| @@ -809,27 +811,46 @@ static DEFINE_PER_CPU(struct send_signal_irq_work, send_signal_work); · 1 unmodified line | ||
| 809 | 811 | static void do_bpf_send_signal(struct irq_work *entry) |
| 810 | 812 | { |
| 811 | 813 | struct send_signal_irq_work *work; |
| 814 | + struct kernel_siginfo *siginfo; | |
| 812 | 815 | |
| 813 | 816 | work = container_of(entry, struct send_signal_irq_work, irq_work); |
| 814 | − group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type); | |
| 817 | + siginfo = work->has_siginfo ? &work->info : SEND_SIG_PRIV; | |
| 818 | + | |
| 819 | + group_send_sig_info(work->sig, siginfo, work->task, work->type); | |
| 815 | 820 | put_task_struct(work->task); |
| 816 | 821 | } |
| 817 | 822 | |
| 818 | −static int bpf_send_signal_common(u32 sig, enum pid_type type) | |
| 823 | +static int bpf_send_signal_common(u32 sig, enum pid_type type, struct task_struct *task, u64 value) | |
| 819 | 824 | { |
| 820 | 825 | struct send_signal_irq_work *work = NULL; |
| 826 | + struct kernel_siginfo info; | |
| 827 | + struct kernel_siginfo *siginfo; | |
| 828 | + | |
| 829 | + if (!task) { | |
| 830 | + task = current; | |
| 831 | + siginfo = SEND_SIG_PRIV; | |
| 832 | + } else { | |
| 833 | + clear_siginfo(&info); | |
| 834 | + info.si_signo = sig; | |
| 835 | + info.si_errno = 0; | |
| 836 | + info.si_code = SI_KERNEL; | |
| 837 | + info.si_pid = 0; | |
| 838 | + info.si_uid = 0; | |
| 839 | + info.si_value.sival_ptr = (void *)(unsigned long)value; | |
| 840 | + siginfo = &info; | |
| 841 | + } | |
| 821 | 842 | |
| 822 | 843 | /* Similar to bpf_probe_write_user, task needs to be |
| 823 | 844 | * in a sound condition and kernel memory access be |
| 824 | 845 | * permitted in order to send signal to the current |
| 825 | 846 | * task. |
| 826 | 847 | */ |
| 827 | − if (unlikely(current->flags & (PF_KTHREAD | PF_EXITING))) | |
| 848 | + if (unlikely(task->flags & (PF_KTHREAD | PF_EXITING))) | |
| 828 | 849 | return -EPERM; |
| 829 | 850 | if (unlikely(!nmi_uaccess_okay())) |
| 830 | 851 | return -EPERM; |
| 831 | 852 | /* Task should not be pid=1 to avoid kernel panic. */ |
| 832 | − if (unlikely(is_global_init(current))) | |
| 853 | + if (unlikely(is_global_init(task))) | |
| 833 | 854 | return -EPERM; |
| 834 | 855 | |
| 835 | 856 | if (irqs_disabled()) { |
| @@ -847,19 +868,22 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type) · 11 unmodified lines | ||
| 847 | 868 | * to the irq_work. The current task may change when queued |
| 848 | 869 | * irq works get executed. |
| 849 | 870 | */ |
| 850 | − work->task = get_task_struct(current); | |
| 871 | + work->task = get_task_struct(task); | |
| 872 | + work->has_siginfo = siginfo == &info; | |
| 873 | + if (work->has_siginfo) | |
| 874 | + copy_siginfo(&work->info, &info); | |
| 851 | 875 | work->sig = sig; |
| 852 | 876 | work->type = type; |
| 853 | 877 | irq_work_queue(&work->irq_work); |
| 854 | 878 | return 0; |
| 855 | 879 | } |
| 856 | 880 | |
| 857 | − return group_send_sig_info(sig, SEND_SIG_PRIV, current, type); | |
| 881 | + return group_send_sig_info(sig, siginfo, task, type); | |
| 858 | 882 | } |
| 859 | 883 | |
| 860 | 884 | BPF_CALL_1(bpf_send_signal, u32, sig) |
| 861 | 885 | { |
| 862 | − return bpf_send_signal_common(sig, PIDTYPE_TGID); | |
| 886 | + return bpf_send_signal_common(sig, PIDTYPE_TGID, NULL, 0); | |
| 863 | 887 | } |
| 864 | 888 | |
| 865 | 889 | static const struct bpf_func_proto bpf_send_signal_proto = { |
| @@ -871,7 +895,7 @@ static const struct bpf_func_proto bpf_send_signal_proto = { · 5 unmodified lines | ||
| 871 | 895 | |
| 872 | 896 | BPF_CALL_1(bpf_send_signal_thread, u32, sig) |
| 873 | 897 | { |
| 874 | − return bpf_send_signal_common(sig, PIDTYPE_PID); | |
| 898 | + return bpf_send_signal_common(sig, PIDTYPE_PID, NULL, 0); | |
| 875 | 899 | } |
| 876 | 900 | |
| 877 | 901 | static const struct bpf_func_proto bpf_send_signal_thread_proto = { |
| @@ -1557,6 +1581,17 @@ static inline bool is_kprobe_session(const struct bpf_prog *prog) · 679 unmodified lines | ||
| 1557 | 1581 | return prog->expected_attach_type == BPF_TRACE_KPROBE_SESSION; |
| 1558 | 1582 | } |
| 1559 | 1583 | |
| 1584 | +static inline bool is_uprobe_multi(const struct bpf_prog *prog) | |
| 1585 | +{ | |
| 1586 | + return prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI || | |
| 1587 | + prog->expected_attach_type == BPF_TRACE_UPROBE_SESSION; | |
| 1588 | +} | |
| 1589 | + | |
| 1590 | +static inline bool is_uprobe_session(const struct bpf_prog *prog) | |
| 1591 | +{ | |
| 1592 | + return prog->expected_attach_type == BPF_TRACE_UPROBE_SESSION; | |
| 1593 | +} | |
| 1594 | + | |
| 1560 | 1595 | static const struct bpf_func_proto * |
| 1561 | 1596 | kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) |
| 1562 | 1597 | { |
| @@ -1574,13 +1609,13 @@ kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 11 unmodified lines | ||
| 1574 | 1609 | case BPF_FUNC_get_func_ip: |
| 1575 | 1610 | if (is_kprobe_multi(prog)) |
| 1576 | 1611 | return &bpf_get_func_ip_proto_kprobe_multi; |
| 1577 | − if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI) | |
| 1612 | + if (is_uprobe_multi(prog)) | |
| 1578 | 1613 | return &bpf_get_func_ip_proto_uprobe_multi; |
| 1579 | 1614 | return &bpf_get_func_ip_proto_kprobe; |
| 1580 | 1615 | case BPF_FUNC_get_attach_cookie: |
| 1581 | 1616 | if (is_kprobe_multi(prog)) |
| 1582 | 1617 | return &bpf_get_attach_cookie_proto_kmulti; |
| 1583 | − if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI) | |
| 1618 | + if (is_uprobe_multi(prog)) | |
| 1584 | 1619 | return &bpf_get_attach_cookie_proto_umulti; |
| 1585 | 1620 | return &bpf_get_attach_cookie_proto_trace; |
| 1586 | 1621 | default: |
| @@ -3072,6 +3107,7 @@ struct bpf_uprobe { · 1485 unmodified lines | ||
| 3072 | 3107 | u64 cookie; |
| 3073 | 3108 | struct uprobe *uprobe; |
| 3074 | 3109 | struct uprobe_consumer consumer; |
| 3110 | + bool session; | |
| 3075 | 3111 | }; |
| 3076 | 3112 | |
| 3077 | 3113 | struct bpf_uprobe_multi_link { |
| @@ -3084,7 +3120,7 @@ struct bpf_uprobe_multi_link { · 6 unmodified lines | ||
| 3084 | 3120 | }; |
| 3085 | 3121 | |
| 3086 | 3122 | struct bpf_uprobe_multi_run_ctx { |
| 3087 | − struct bpf_run_ctx run_ctx; | |
| 3123 | + struct bpf_session_run_ctx session_ctx; | |
| 3088 | 3124 | unsigned long entry_ip; |
| 3089 | 3125 | struct bpf_uprobe *uprobe; |
| 3090 | 3126 | }; |
| @@ -3195,17 +3231,22 @@ static const struct bpf_link_ops bpf_uprobe_multi_link_lops = { · 104 unmodified lines | ||
| 3195 | 3231 | |
| 3196 | 3232 | static int uprobe_prog_run(struct bpf_uprobe *uprobe, |
| 3197 | 3233 | unsigned long entry_ip, |
| 3198 | − struct pt_regs *regs) | |
| 3234 | + struct pt_regs *regs, | |
| 3235 | + bool is_return, void *data) | |
| 3199 | 3236 | { |
| 3200 | 3237 | struct bpf_uprobe_multi_link *link = uprobe->link; |
| 3201 | 3238 | struct bpf_uprobe_multi_run_ctx run_ctx = { |
| 3239 | + .session_ctx = { | |
| 3240 | + .is_return = is_return, | |
| 3241 | + .data = data, | |
| 3242 | + }, | |
| 3202 | 3243 | .entry_ip = entry_ip, |
| 3203 | 3244 | .uprobe = uprobe, |
| 3204 | 3245 | }; |
| 3205 | 3246 | struct bpf_prog *prog = link->link.prog; |
| 3206 | 3247 | bool sleepable = prog->sleepable; |
| 3207 | 3248 | struct bpf_run_ctx *old_run_ctx; |
| 3208 | − int err = 0; | |
| 3249 | + int err; | |
| 3209 | 3250 | |
| 3210 | 3251 | if (link->task && !same_thread_group(current, link->task)) |
| 3211 | 3252 | return 0; |
| @@ -3217,7 +3258,7 @@ static int uprobe_prog_run(struct bpf_uprobe *uprobe, · 5 unmodified lines | ||
| 3217 | 3258 | |
| 3218 | 3259 | migrate_disable(); |
| 3219 | 3260 | |
| 3220 | − old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx); | |
| 3261 | + old_run_ctx = bpf_set_run_ctx(&run_ctx.session_ctx.run_ctx); | |
| 3221 | 3262 | err = bpf_prog_run(link->link.prog, regs); |
| 3222 | 3263 | bpf_reset_run_ctx(old_run_ctx); |
| 3223 | 3264 | |
| @@ -3244,9 +3285,13 @@ uprobe_multi_link_handler(struct uprobe_consumer *con, struct pt_regs *regs, · 20 unmodified lines | ||
| 3244 | 3285 | __u64 *data) |
| 3245 | 3286 | { |
| 3246 | 3287 | struct bpf_uprobe *uprobe; |
| 3288 | + int ret; | |
| 3247 | 3289 | |
| 3248 | 3290 | uprobe = container_of(con, struct bpf_uprobe, consumer); |
| 3249 | − return uprobe_prog_run(uprobe, instruction_pointer(regs), regs); | |
| 3291 | + ret = uprobe_prog_run(uprobe, instruction_pointer(regs), regs, false, data); | |
| 3292 | + if (uprobe->session) | |
| 3293 | + return ret ? UPROBE_HANDLER_IGNORE : 0; | |
| 3294 | + return 0; | |
| 3250 | 3295 | } |
| 3251 | 3296 | |
| 3252 | 3297 | static int |
| @@ -3256,14 +3301,16 @@ uprobe_multi_link_ret_handler(struct uprobe_consumer *con, unsigned long func, s · 3 unmodified lines | ||
| 3256 | 3301 | struct bpf_uprobe *uprobe; |
| 3257 | 3302 | |
| 3258 | 3303 | uprobe = container_of(con, struct bpf_uprobe, consumer); |
| 3259 | − return uprobe_prog_run(uprobe, func, regs); | |
| 3304 | + uprobe_prog_run(uprobe, func, regs, true, data); | |
| 3305 | + return 0; | |
| 3260 | 3306 | } |
| 3261 | 3307 | |
| 3262 | 3308 | static u64 bpf_uprobe_multi_entry_ip(struct bpf_run_ctx *ctx) |
| 3263 | 3309 | { |
| 3264 | 3310 | struct bpf_uprobe_multi_run_ctx *run_ctx; |
| 3265 | 3311 | |
| 3266 | − run_ctx = container_of(current->bpf_ctx, struct bpf_uprobe_multi_run_ctx, run_ctx); | |
| 3312 | + run_ctx = container_of(current->bpf_ctx, struct bpf_uprobe_multi_run_ctx, | |
| 3313 | + session_ctx.run_ctx); | |
| 3267 | 3314 | return run_ctx->entry_ip; |
| 3268 | 3315 | } |
| 3269 | 3316 | |
| @@ -3271,7 +3318,8 @@ static u64 bpf_uprobe_multi_cookie(struct bpf_run_ctx *ctx) · 1 unmodified line | ||
| 3271 | 3318 | { |
| 3272 | 3319 | struct bpf_uprobe_multi_run_ctx *run_ctx; |
| 3273 | 3320 | |
| 3274 | − run_ctx = container_of(current->bpf_ctx, struct bpf_uprobe_multi_run_ctx, run_ctx); | |
| 3321 | + run_ctx = container_of(current->bpf_ctx, struct bpf_uprobe_multi_run_ctx, | |
| 3322 | + session_ctx.run_ctx); | |
| 3275 | 3323 | return run_ctx->uprobe->cookie; |
| 3276 | 3324 | } |
| 3277 | 3325 | |
| @@ -3295,7 +3343,7 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr · 17 unmodified lines | ||
| 3295 | 3343 | if (sizeof(u64) != sizeof(void *)) |
| 3296 | 3344 | return -EOPNOTSUPP; |
| 3297 | 3345 | |
| 3298 | − if (prog->expected_attach_type != BPF_TRACE_UPROBE_MULTI) | |
| 3346 | + if (!is_uprobe_multi(prog)) | |
| 3299 | 3347 | return -EINVAL; |
| 3300 | 3348 | |
| 3301 | 3349 | flags = attr->link_create.uprobe_multi.flags; |
| @@ -3371,11 +3419,12 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr · 69 unmodified lines | ||
| 3371 | 3419 | |
| 3372 | 3420 | uprobes[i].link = link; |
| 3373 | 3421 | |
| 3374 | − if (flags & BPF_F_UPROBE_MULTI_RETURN) | |
| 3375 | − uprobes[i].consumer.ret_handler = uprobe_multi_link_ret_handler; | |
| 3376 | − else | |
| 3422 | + if (!(flags & BPF_F_UPROBE_MULTI_RETURN)) | |
| 3377 | 3423 | uprobes[i].consumer.handler = uprobe_multi_link_handler; |
| 3378 | − | |
| 3424 | + if (flags & BPF_F_UPROBE_MULTI_RETURN || is_uprobe_session(prog)) | |
| 3425 | + uprobes[i].consumer.ret_handler = uprobe_multi_link_ret_handler; | |
| 3426 | + if (is_uprobe_session(prog)) | |
| 3427 | + uprobes[i].session = true; | |
| 3379 | 3428 | if (pid) |
| 3380 | 3429 | uprobes[i].consumer.filter = uprobe_multi_link_filter; |
| 3381 | 3430 | } |
| @@ -3464,7 +3513,7 @@ static int bpf_kprobe_multi_filter(const struct bpf_prog *prog, u32 kfunc_id) · 82 unmodified lines | ||
| 3464 | 3513 | if (!btf_id_set8_contains(&kprobe_multi_kfunc_set_ids, kfunc_id)) |
| 3465 | 3514 | return 0; |
| 3466 | 3515 | |
| 3467 | − if (!is_kprobe_session(prog)) | |
| 3516 | + if (!is_kprobe_session(prog) && !is_uprobe_session(prog)) | |
| 3468 | 3517 | return -EACCES; |
| 3469 | 3518 | |
| 3470 | 3519 | return 0; |
| @@ -3482,3 +3531,16 @@ static int __init bpf_kprobe_multi_kfuncs_init(void) · 11 unmodified lines | ||
| 3482 | 3531 | } |
| 3483 | 3532 | |
| 3484 | 3533 | late_initcall(bpf_kprobe_multi_kfuncs_init); |
| 3534 | + | |
| 3535 | +__bpf_kfunc_start_defs(); | |
| 3536 | + | |
| 3537 | +__bpf_kfunc int bpf_send_signal_task(struct task_struct *task, int sig, enum pid_type type, | |
| 3538 | + u64 value) | |
| 3539 | +{ | |
| 3540 | + if (type != PIDTYPE_PID && type != PIDTYPE_TGID) | |
| 3541 | + return -EINVAL; | |
| 3542 | + | |
| 3543 | + return bpf_send_signal_common(sig, type, task, value); | |
| 3544 | +} | |
| 3545 | + | |
| 3546 | +__bpf_kfunc_end_defs(); | |
| @@ -1332,6 +1332,25 @@ size_t ksize(const void *objp) · 1331 unmodified lines | ||
| 1332 | 1332 | } |
| 1333 | 1333 | EXPORT_SYMBOL(ksize); |
| 1334 | 1334 | |
| 1335 | +#ifdef CONFIG_BPF_SYSCALL | |
| 1336 | +#include <linux/btf.h> | |
| 1337 | + | |
| 1338 | +__bpf_kfunc_start_defs(); | |
| 1339 | + | |
| 1340 | +__bpf_kfunc struct kmem_cache *bpf_get_kmem_cache(u64 addr) | |
| 1341 | +{ | |
| 1342 | + struct slab *slab; | |
| 1343 | + | |
| 1344 | + if (!virt_addr_valid((void *)(long)addr)) | |
| 1345 | + return NULL; | |
| 1346 | + | |
| 1347 | + slab = virt_to_slab((void *)(long)addr); | |
| 1348 | + return slab ? slab->slab_cache : NULL; | |
| 1349 | +} | |
| 1350 | + | |
| 1351 | +__bpf_kfunc_end_defs(); | |
| 1352 | +#endif /* CONFIG_BPF_SYSCALL */ | |
| 1353 | + | |
| 1335 | 1354 | /* Tracepoints definitions. */ |
| 1336 | 1355 | EXPORT_TRACEPOINT_SYMBOL(kmalloc); |
| 1337 | 1356 | EXPORT_TRACEPOINT_SYMBOL(kmem_cache_alloc); |
| @@ -106,7 +106,7 @@ static long bpf_fd_sk_storage_update_elem(struct bpf_map *map, void *key, · 105 unmodified lines | ||
| 106 | 106 | if (sock) { |
| 107 | 107 | sdata = bpf_local_storage_update( |
| 108 | 108 | sock->sk, (struct bpf_local_storage_map *)map, value, |
| 109 | − map_flags, GFP_ATOMIC); | |
| 109 | + map_flags, false, GFP_ATOMIC); | |
| 110 | 110 | sockfd_put(sock); |
| 111 | 111 | return PTR_ERR_OR_ZERO(sdata); |
| 112 | 112 | } |
| @@ -137,7 +137,7 @@ bpf_sk_storage_clone_elem(struct sock *newsk, · 24 unmodified lines | ||
| 137 | 137 | { |
| 138 | 138 | struct bpf_local_storage_elem *copy_selem; |
| 139 | 139 | |
| 140 | − copy_selem = bpf_selem_alloc(smap, newsk, NULL, true, GFP_ATOMIC); | |
| 140 | + copy_selem = bpf_selem_alloc(smap, newsk, NULL, true, false, GFP_ATOMIC); | |
| 141 | 141 | if (!copy_selem) |
| 142 | 142 | return NULL; |
| 143 | 143 | |
| @@ -243,7 +243,7 @@ BPF_CALL_5(bpf_sk_storage_get, struct bpf_map *, map, struct sock *, sk, · 99 unmodified lines | ||
| 243 | 243 | refcount_inc_not_zero(&sk->sk_refcnt)) { |
| 244 | 244 | sdata = bpf_local_storage_update( |
| 245 | 245 | sk, (struct bpf_local_storage_map *)map, value, |
| 246 | − BPF_NOEXIST, gfp_flags); | |
| 246 | + BPF_NOEXIST, false, gfp_flags); | |
| 247 | 247 | /* sk must be a fullsock (guaranteed by verifier), |
| 248 | 248 | * so sock_gen_put() is unnecessary. |
| 249 | 249 | */ |
| @@ -17,20 +17,12 @@ tprogs-y += tracex3 · 16 unmodified lines | ||
| 17 | 17 | tprogs-y += tracex4 |
| 18 | 18 | tprogs-y += tracex5 |
| 19 | 19 | tprogs-y += tracex6 |
| 20 | −tprogs-y += tracex7 | |
| 21 | −tprogs-y += test_probe_write_user | |
| 22 | 20 | tprogs-y += trace_output |
| 23 | 21 | tprogs-y += lathist |
| 24 | 22 | tprogs-y += offwaketime |
| 25 | 23 | tprogs-y += spintest |
| 26 | 24 | tprogs-y += map_perf_test |
| 27 | −tprogs-y += test_overhead | |
| 28 | −tprogs-y += test_cgrp2_array_pin | |
| 29 | −tprogs-y += test_cgrp2_attach | |
| 30 | −tprogs-y += test_cgrp2_sock | |
| 31 | −tprogs-y += test_cgrp2_sock2 | |
| 32 | 25 | tprogs-y += xdp_router_ipv4 |
| 33 | −tprogs-y += test_current_task_under_cgroup | |
| 34 | 26 | tprogs-y += trace_event |
| 35 | 27 | tprogs-y += sampleip |
| 36 | 28 | tprogs-y += tc_l2_redirect |
| @@ -66,20 +58,12 @@ tracex3-objs := tracex3_user.o · 29 unmodified lines | ||
| 66 | 58 | tracex4-objs := tracex4_user.o |
| 67 | 59 | tracex5-objs := tracex5_user.o $(TRACE_HELPERS) |
| 68 | 60 | tracex6-objs := tracex6_user.o |
| 69 | −tracex7-objs := tracex7_user.o | |
| 70 | −test_probe_write_user-objs := test_probe_write_user_user.o | |
| 71 | 61 | trace_output-objs := trace_output_user.o |
| 72 | 62 | lathist-objs := lathist_user.o |
| 73 | 63 | offwaketime-objs := offwaketime_user.o $(TRACE_HELPERS) |
| 74 | 64 | spintest-objs := spintest_user.o $(TRACE_HELPERS) |
| 75 | 65 | map_perf_test-objs := map_perf_test_user.o |
| 76 | 66 | test_overhead-objs := test_overhead_user.o |
| 77 | −test_cgrp2_array_pin-objs := test_cgrp2_array_pin.o | |
| 78 | −test_cgrp2_attach-objs := test_cgrp2_attach.o | |
| 79 | −test_cgrp2_sock-objs := test_cgrp2_sock.o | |
| 80 | −test_cgrp2_sock2-objs := test_cgrp2_sock2.o | |
| 81 | −test_current_task_under_cgroup-objs := $(CGROUP_HELPERS) \ | |
| 82 | − test_current_task_under_cgroup_user.o | |
| 83 | 67 | trace_event-objs := trace_event_user.o $(TRACE_HELPERS) |
| 84 | 68 | sampleip-objs := sampleip_user.o $(TRACE_HELPERS) |
| 85 | 69 | tc_l2_redirect-objs := tc_l2_redirect_user.o |
| @@ -107,9 +91,6 @@ always-y += tracex3.bpf.o · 21 unmodified lines | ||
| 107 | 91 | always-y += tracex4.bpf.o |
| 108 | 92 | always-y += tracex5.bpf.o |
| 109 | 93 | always-y += tracex6.bpf.o |
| 110 | −always-y += tracex7.bpf.o | |
| 111 | −always-y += sock_flags.bpf.o | |
| 112 | −always-y += test_probe_write_user.bpf.o | |
| 113 | 94 | always-y += trace_output.bpf.o |
| 114 | 95 | always-y += tcbpf1_kern.o |
| 115 | 96 | always-y += tc_l2_redirect_kern.o |
| @@ -117,12 +98,7 @@ always-y += lathist_kern.o · 1 unmodified line | ||
| 117 | 98 | always-y += offwaketime.bpf.o |
| 118 | 99 | always-y += spintest.bpf.o |
| 119 | 100 | always-y += map_perf_test.bpf.o |
| 120 | −always-y += test_overhead_tp.bpf.o | |
| 121 | −always-y += test_overhead_raw_tp.bpf.o | |
| 122 | −always-y += test_overhead_kprobe.bpf.o | |
| 123 | 101 | always-y += parse_varlen.o parse_simple.o parse_ldabs.o |
| 124 | −always-y += test_cgrp2_tc.bpf.o | |
| 125 | −always-y += test_current_task_under_cgroup.bpf.o | |
| 126 | 102 | always-y += trace_event_kern.o |
| 127 | 103 | always-y += sampleip_kern.o |
| 128 | 104 | always-y += lwt_len_hist.bpf.o |
| @@ -195,7 +171,6 @@ TPROGLDLIBS_xdp_router_ipv4 += -lm -pthread · 66 unmodified lines | ||
| 195 | 171 | TPROGLDLIBS_tracex4 += -lrt |
| 196 | 172 | TPROGLDLIBS_trace_output += -lrt |
| 197 | 173 | TPROGLDLIBS_map_perf_test += -lrt |
| 198 | −TPROGLDLIBS_test_overhead += -lrt | |
| 199 | 174 | |
| 200 | 175 | # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: |
| 201 | 176 | # make M=samples/bpf LLC=~/git/llvm-project/llvm/build/bin/llc CLANG=~/git/llvm-project/llvm/build/bin/clang |
| @@ -1,47 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0 | |
| 2 | −#include "vmlinux.h" | |
| 3 | −#include "net_shared.h" | |
| 4 | −#include <bpf/bpf_helpers.h> | |
| 5 | − | |
| 6 | −SEC("cgroup/sock") | |
| 7 | −int bpf_prog1(struct bpf_sock *sk) | |
| 8 | −{ | |
| 9 | − char fmt[] = "socket: family %d type %d protocol %d\n"; | |
| 10 | − char fmt2[] = "socket: uid %u gid %u\n"; | |
| 11 | − __u64 gid_uid = bpf_get_current_uid_gid(); | |
| 12 | − __u32 uid = gid_uid & 0xffffffff; | |
| 13 | − __u32 gid = gid_uid >> 32; | |
| 14 | − | |
| 15 | − bpf_trace_printk(fmt, sizeof(fmt), sk->family, sk->type, sk->protocol); | |
| 16 | − bpf_trace_printk(fmt2, sizeof(fmt2), uid, gid); | |
| 17 | − | |
| 18 | − /* block AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6 sockets | |
| 19 | − * ie., make ping6 fail | |
| 20 | − */ | |
| 21 | − if (sk->family == AF_INET6 && | |
| 22 | − sk->type == SOCK_DGRAM && | |
| 23 | − sk->protocol == IPPROTO_ICMPV6) | |
| 24 | − return 0; | |
| 25 | − | |
| 26 | − return 1; | |
| 27 | −} | |
| 28 | − | |
| 29 | −SEC("cgroup/sock") | |
| 30 | −int bpf_prog2(struct bpf_sock *sk) | |
| 31 | −{ | |
| 32 | − char fmt[] = "socket: family %d type %d protocol %d\n"; | |
| 33 | − | |
| 34 | − bpf_trace_printk(fmt, sizeof(fmt), sk->family, sk->type, sk->protocol); | |
| 35 | − | |
| 36 | − /* block AF_INET, SOCK_DGRAM, IPPROTO_ICMP sockets | |
| 37 | − * ie., make ping fail | |
| 38 | − */ | |
| 39 | − if (sk->family == AF_INET && | |
| 40 | − sk->type == SOCK_DGRAM && | |
| 41 | − sk->protocol == IPPROTO_ICMP) | |
| 42 | − return 0; | |
| 43 | − | |
| 44 | − return 1; | |
| 45 | −} | |
| 46 | − | |
| 47 | −char _license[] SEC("license") = "GPL"; | |
| @@ -2,6 +2,9 @@ · 1 unmodified line | ||
| 2 | 2 | #include <uapi/linux/unistd.h> |
| 3 | 3 | #include <linux/kbuild.h> |
| 4 | 4 | |
| 5 | +#pragma GCC diagnostic push | |
| 6 | +#pragma GCC diagnostic ignored "-Wmissing-prototypes" | |
| 7 | + | |
| 5 | 8 | #define SYSNR(_NR) DEFINE(SYS ## _NR, _NR) |
| 6 | 9 | |
| 7 | 10 | void syscall_defines(void) |
| @@ -17,3 +20,5 @@ void syscall_defines(void) · 9 unmodified lines | ||
| 17 | 20 | #endif |
| 18 | 21 | |
| 19 | 22 | } |
| 23 | + | |
| 24 | +#pragma GCC diagnostic pop | |
| @@ -58,14 +58,11 @@ static __always_inline bool is_vip_addr(__be16 eth_proto, __be32 daddr) · 57 unmodified lines | ||
| 58 | 58 | SEC("l2_to_iptun_ingress_forward") |
| 59 | 59 | int _l2_to_iptun_ingress_forward(struct __sk_buff *skb) |
| 60 | 60 | { |
| 61 | − struct bpf_tunnel_key tkey = {}; | |
| 62 | 61 | void *data = (void *)(long)skb->data; |
| 63 | 62 | struct eth_hdr *eth = data; |
| 64 | 63 | void *data_end = (void *)(long)skb->data_end; |
| 65 | 64 | int key = 0, *ifindex; |
| 66 | 65 | |
| 67 | − int ret; | |
| 68 | − | |
| 69 | 66 | if (data + sizeof(*eth) > data_end) |
| 70 | 67 | return TC_ACT_OK; |
| 71 | 68 | |
| @@ -115,8 +112,6 @@ int _l2_to_iptun_ingress_redirect(struct __sk_buff *skb) · 43 unmodified lines | ||
| 115 | 112 | void *data_end = (void *)(long)skb->data_end; |
| 116 | 113 | int key = 0, *ifindex; |
| 117 | 114 | |
| 118 | − int ret; | |
| 119 | − | |
| 120 | 115 | if (data + sizeof(*eth) > data_end) |
| 121 | 116 | return TC_ACT_OK; |
| 122 | 117 | |
| @@ -205,7 +200,6 @@ int _l2_to_ip6tun_ingress_redirect(struct __sk_buff *skb) · 82 unmodified lines | ||
| 205 | 200 | SEC("drop_non_tun_vip") |
| 206 | 201 | int _drop_non_tun_vip(struct __sk_buff *skb) |
| 207 | 202 | { |
| 208 | − struct bpf_tunnel_key tkey = {}; | |
| 209 | 203 | void *data = (void *)(long)skb->data; |
| 210 | 204 | struct eth_hdr *eth = data; |
| 211 | 205 | void *data_end = (void *)(long)skb->data_end; |
| @@ -1,106 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −/* Copyright (c) 2016 Facebook | |
| 3 | − */ | |
| 4 | −#include <linux/unistd.h> | |
| 5 | −#include <linux/bpf.h> | |
| 6 | − | |
| 7 | −#include <stdio.h> | |
| 8 | −#include <stdint.h> | |
| 9 | −#include <unistd.h> | |
| 10 | −#include <string.h> | |
| 11 | −#include <errno.h> | |
| 12 | −#include <fcntl.h> | |
| 13 | − | |
| 14 | −#include <bpf/bpf.h> | |
| 15 | − | |
| 16 | −static void usage(void) | |
| 17 | −{ | |
| 18 | − printf("Usage: test_cgrp2_array_pin [...]\n"); | |
| 19 | − printf(" -F <file> File to pin an BPF cgroup array\n"); | |
| 20 | − printf(" -U <file> Update an already pinned BPF cgroup array\n"); | |
| 21 | − printf(" -v <value> Full path of the cgroup2\n"); | |
| 22 | − printf(" -h Display this help\n"); | |
| 23 | −} | |
| 24 | − | |
| 25 | −int main(int argc, char **argv) | |
| 26 | −{ | |
| 27 | − const char *pinned_file = NULL, *cg2 = NULL; | |
| 28 | − int create_array = 1; | |
| 29 | − int array_key = 0; | |
| 30 | − int array_fd = -1; | |
| 31 | − int cg2_fd = -1; | |
| 32 | − int ret = -1; | |
| 33 | − int opt; | |
| 34 | − | |
| 35 | − while ((opt = getopt(argc, argv, "F:U:v:")) != -1) { | |
| 36 | − switch (opt) { | |
| 37 | − /* General args */ | |
| 38 | − case 'F': | |
| 39 | − pinned_file = optarg; | |
| 40 | − break; | |
| 41 | − case 'U': | |
| 42 | − pinned_file = optarg; | |
| 43 | − create_array = 0; | |
| 44 | − break; | |
| 45 | − case 'v': | |
| 46 | − cg2 = optarg; | |
| 47 | − break; | |
| 48 | − default: | |
| 49 | − usage(); | |
| 50 | − goto out; | |
| 51 | − } | |
| 52 | − } | |
| 53 | − | |
| 54 | − if (!cg2 || !pinned_file) { | |
| 55 | − usage(); | |
| 56 | − goto out; | |
| 57 | − } | |
| 58 | − | |
| 59 | − cg2_fd = open(cg2, O_RDONLY); | |
| 60 | − if (cg2_fd < 0) { | |
| 61 | − fprintf(stderr, "open(%s,...): %s(%d)\n", | |
| 62 | − cg2, strerror(errno), errno); | |
| 63 | − goto out; | |
| 64 | − } | |
| 65 | − | |
| 66 | − if (create_array) { | |
| 67 | − array_fd = bpf_map_create(BPF_MAP_TYPE_CGROUP_ARRAY, NULL, | |
| 68 | − sizeof(uint32_t), sizeof(uint32_t), | |
| 69 | − 1, NULL); | |
| 70 | − if (array_fd < 0) { | |
| 71 | − fprintf(stderr, | |
| 72 | − "bpf_create_map(BPF_MAP_TYPE_CGROUP_ARRAY,...): %s(%d)\n", | |
| 73 | − strerror(errno), errno); | |
| 74 | − goto out; | |
| 75 | − } | |
| 76 | − } else { | |
| 77 | − array_fd = bpf_obj_get(pinned_file); | |
| 78 | − if (array_fd < 0) { | |
| 79 | − fprintf(stderr, "bpf_obj_get(%s): %s(%d)\n", | |
| 80 | − pinned_file, strerror(errno), errno); | |
| 81 | − goto out; | |
| 82 | − } | |
| 83 | − } | |
| 84 | − | |
| 85 | − ret = bpf_map_update_elem(array_fd, &array_key, &cg2_fd, 0); | |
| 86 | − if (ret) { | |
| 87 | − perror("bpf_map_update_elem"); | |
| 88 | − goto out; | |
| 89 | − } | |
| 90 | − | |
| 91 | − if (create_array) { | |
| 92 | − ret = bpf_obj_pin(array_fd, pinned_file); | |
| 93 | − if (ret) { | |
| 94 | − fprintf(stderr, "bpf_obj_pin(..., %s): %s(%d)\n", | |
| 95 | − pinned_file, strerror(errno), errno); | |
| 96 | − goto out; | |
| 97 | − } | |
| 98 | − } | |
| 99 | − | |
| 100 | −out: | |
| 101 | − if (array_fd != -1) | |
| 102 | − close(array_fd); | |
| 103 | − if (cg2_fd != -1) | |
| 104 | − close(cg2_fd); | |
| 105 | − return ret; | |
| 106 | −} | |
| @@ -1,177 +0,0 @@ | ||
| 1 | −/* eBPF example program: | |
| 2 | − * | |
| 3 | − * - Creates arraymap in kernel with 4 bytes keys and 8 byte values | |
| 4 | − * | |
| 5 | − * - Loads eBPF program | |
| 6 | − * | |
| 7 | − * The eBPF program accesses the map passed in to store two pieces of | |
| 8 | − * information. The number of invocations of the program, which maps | |
| 9 | − * to the number of packets received, is stored to key 0. Key 1 is | |
| 10 | − * incremented on each iteration by the number of bytes stored in | |
| 11 | − * the skb. | |
| 12 | − * | |
| 13 | − * - Attaches the new program to a cgroup using BPF_PROG_ATTACH | |
| 14 | − * | |
| 15 | − * - Every second, reads map[0] and map[1] to see how many bytes and | |
| 16 | − * packets were seen on any socket of tasks in the given cgroup. | |
| 17 | − */ | |
| 18 | − | |
| 19 | −#define _GNU_SOURCE | |
| 20 | − | |
| 21 | −#include <stdio.h> | |
| 22 | −#include <stdlib.h> | |
| 23 | −#include <stddef.h> | |
| 24 | −#include <string.h> | |
| 25 | −#include <unistd.h> | |
| 26 | −#include <assert.h> | |
| 27 | −#include <errno.h> | |
| 28 | −#include <fcntl.h> | |
| 29 | − | |
| 30 | −#include <linux/bpf.h> | |
| 31 | −#include <bpf/bpf.h> | |
| 32 | − | |
| 33 | −#include "bpf_insn.h" | |
| 34 | −#include "bpf_util.h" | |
| 35 | − | |
| 36 | −enum { | |
| 37 | − MAP_KEY_PACKETS, | |
| 38 | − MAP_KEY_BYTES, | |
| 39 | −}; | |
| 40 | − | |
| 41 | −char bpf_log_buf[BPF_LOG_BUF_SIZE]; | |
| 42 | − | |
| 43 | −static int prog_load(int map_fd, int verdict) | |
| 44 | −{ | |
| 45 | − struct bpf_insn prog[] = { | |
| 46 | − BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), /* save r6 so it's not clobbered by BPF_CALL */ | |
| 47 | − | |
| 48 | − /* Count packets */ | |
| 49 | − BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_PACKETS), /* r0 = 0 */ | |
| 50 | − BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */ | |
| 51 | − BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | |
| 52 | − BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ | |
| 53 | − BPF_LD_MAP_FD(BPF_REG_1, map_fd), /* load map fd to r1 */ | |
| 54 | − BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), | |
| 55 | − BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), | |
| 56 | − BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */ | |
| 57 | − BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0), | |
| 58 | − | |
| 59 | − /* Count bytes */ | |
| 60 | − BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_BYTES), /* r0 = 1 */ | |
| 61 | − BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */ | |
| 62 | − BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | |
| 63 | − BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ | |
| 64 | − BPF_LD_MAP_FD(BPF_REG_1, map_fd), | |
| 65 | − BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), | |
| 66 | − BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), | |
| 67 | − BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, offsetof(struct __sk_buff, len)), /* r1 = skb->len */ | |
| 68 | − | |
| 69 | − BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0), | |
| 70 | − | |
| 71 | − BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ | |
| 72 | − BPF_EXIT_INSN(), | |
| 73 | − }; | |
| 74 | − size_t insns_cnt = ARRAY_SIZE(prog); | |
| 75 | − LIBBPF_OPTS(bpf_prog_load_opts, opts, | |
| 76 | − .log_buf = bpf_log_buf, | |
| 77 | − .log_size = BPF_LOG_BUF_SIZE, | |
| 78 | − ); | |
| 79 | − | |
| 80 | − return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SKB, NULL, "GPL", | |
| 81 | − prog, insns_cnt, &opts); | |
| 82 | −} | |
| 83 | − | |
| 84 | −static int usage(const char *argv0) | |
| 85 | −{ | |
| 86 | − printf("Usage: %s [-d] [-D] <cg-path> <egress|ingress>\n", argv0); | |
| 87 | − printf(" -d Drop Traffic\n"); | |
| 88 | − printf(" -D Detach filter, and exit\n"); | |
| 89 | − return EXIT_FAILURE; | |
| 90 | −} | |
| 91 | − | |
| 92 | −static int attach_filter(int cg_fd, int type, int verdict) | |
| 93 | −{ | |
| 94 | − int prog_fd, map_fd, ret, key; | |
| 95 | − long long pkt_cnt, byte_cnt; | |
| 96 | − | |
| 97 | − map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, | |
| 98 | − sizeof(key), sizeof(byte_cnt), | |
| 99 | − 256, NULL); | |
| 100 | − if (map_fd < 0) { | |
| 101 | − printf("Failed to create map: '%s'\n", strerror(errno)); | |
| 102 | − return EXIT_FAILURE; | |
| 103 | − } | |
| 104 | − | |
| 105 | − prog_fd = prog_load(map_fd, verdict); | |
| 106 | − printf("Output from kernel verifier:\n%s\n-------\n", bpf_log_buf); | |
| 107 | − | |
| 108 | − if (prog_fd < 0) { | |
| 109 | − printf("Failed to load prog: '%s'\n", strerror(errno)); | |
| 110 | − return EXIT_FAILURE; | |
| 111 | − } | |
| 112 | − | |
| 113 | − ret = bpf_prog_attach(prog_fd, cg_fd, type, 0); | |
| 114 | − if (ret < 0) { | |
| 115 | − printf("Failed to attach prog to cgroup: '%s'\n", | |
| 116 | − strerror(errno)); | |
| 117 | − return EXIT_FAILURE; | |
| 118 | − } | |
| 119 | − while (1) { | |
| 120 | − key = MAP_KEY_PACKETS; | |
| 121 | − assert(bpf_map_lookup_elem(map_fd, &key, &pkt_cnt) == 0); | |
| 122 | − | |
| 123 | − key = MAP_KEY_BYTES; | |
| 124 | − assert(bpf_map_lookup_elem(map_fd, &key, &byte_cnt) == 0); | |
| 125 | − | |
| 126 | − printf("cgroup received %lld packets, %lld bytes\n", | |
| 127 | − pkt_cnt, byte_cnt); | |
| 128 | − sleep(1); | |
| 129 | − } | |
| 130 | − | |
| 131 | − return EXIT_SUCCESS; | |
| 132 | −} | |
| 133 | − | |
| 134 | −int main(int argc, char **argv) | |
| 135 | −{ | |
| 136 | − int detach_only = 0, verdict = 1; | |
| 137 | − enum bpf_attach_type type; | |
| 138 | − int opt, cg_fd, ret; | |
| 139 | − | |
| 140 | − while ((opt = getopt(argc, argv, "Dd")) != -1) { | |
| 141 | − switch (opt) { | |
| 142 | − case 'd': | |
| 143 | − verdict = 0; | |
| 144 | − break; | |
| 145 | − case 'D': | |
| 146 | − detach_only = 1; | |
| 147 | − break; | |
| 148 | − default: | |
| 149 | − return usage(argv[0]); | |
| 150 | − } | |
| 151 | − } | |
| 152 | − | |
| 153 | − if (argc - optind < 2) | |
| 154 | − return usage(argv[0]); | |
| 155 | − | |
| 156 | − if (strcmp(argv[optind + 1], "ingress") == 0) | |
| 157 | − type = BPF_CGROUP_INET_INGRESS; | |
| 158 | − else if (strcmp(argv[optind + 1], "egress") == 0) | |
| 159 | − type = BPF_CGROUP_INET_EGRESS; | |
| 160 | − else | |
| 161 | − return usage(argv[0]); | |
| 162 | − | |
| 163 | − cg_fd = open(argv[optind], O_DIRECTORY | O_RDONLY); | |
| 164 | − if (cg_fd < 0) { | |
| 165 | − printf("Failed to open cgroup path: '%s'\n", strerror(errno)); | |
| 166 | − return EXIT_FAILURE; | |
| 167 | − } | |
| 168 | − | |
| 169 | − if (detach_only) { | |
| 170 | − ret = bpf_prog_detach(cg_fd, type); | |
| 171 | − printf("bpf_prog_detach() returned '%s' (%d)\n", | |
| 172 | − strerror(errno), errno); | |
| 173 | − } else | |
| 174 | − ret = attach_filter(cg_fd, type, verdict); | |
| 175 | − | |
| 176 | − return ret; | |
| 177 | −} | |
| @@ -1,294 +0,0 @@ | ||
| 1 | −/* eBPF example program: | |
| 2 | − * | |
| 3 | − * - Loads eBPF program | |
| 4 | − * | |
| 5 | − * The eBPF program sets the sk_bound_dev_if index in new AF_INET{6} | |
| 6 | − * sockets opened by processes in the cgroup. | |
| 7 | − * | |
| 8 | − * - Attaches the new program to a cgroup using BPF_PROG_ATTACH | |
| 9 | − */ | |
| 10 | − | |
| 11 | −#define _GNU_SOURCE | |
| 12 | − | |
| 13 | −#include <stdio.h> | |
| 14 | −#include <stdlib.h> | |
| 15 | −#include <stddef.h> | |
| 16 | −#include <string.h> | |
| 17 | −#include <unistd.h> | |
| 18 | −#include <assert.h> | |
| 19 | −#include <errno.h> | |
| 20 | −#include <fcntl.h> | |
| 21 | −#include <net/if.h> | |
| 22 | −#include <inttypes.h> | |
| 23 | −#include <linux/bpf.h> | |
| 24 | −#include <bpf/bpf.h> | |
| 25 | − | |
| 26 | −#include "bpf_insn.h" | |
| 27 | − | |
| 28 | −char bpf_log_buf[BPF_LOG_BUF_SIZE]; | |
| 29 | − | |
| 30 | −static int prog_load(__u32 idx, __u32 mark, __u32 prio) | |
| 31 | −{ | |
| 32 | − /* save pointer to context */ | |
| 33 | − struct bpf_insn prog_start[] = { | |
| 34 | − BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
| 35 | − }; | |
| 36 | − struct bpf_insn prog_end[] = { | |
| 37 | − BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */ | |
| 38 | − BPF_EXIT_INSN(), | |
| 39 | − }; | |
| 40 | − | |
| 41 | − /* set sk_bound_dev_if on socket */ | |
| 42 | − struct bpf_insn prog_dev[] = { | |
| 43 | − BPF_MOV64_IMM(BPF_REG_3, idx), | |
| 44 | − BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, bound_dev_if)), | |
| 45 | − BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, bound_dev_if)), | |
| 46 | − }; | |
| 47 | − | |
| 48 | − /* set mark on socket */ | |
| 49 | − struct bpf_insn prog_mark[] = { | |
| 50 | − /* get uid of process */ | |
| 51 | − BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, | |
| 52 | − BPF_FUNC_get_current_uid_gid), | |
| 53 | − BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff), | |
| 54 | − | |
| 55 | − /* if uid is 0, use given mark, else use the uid as the mark */ | |
| 56 | − BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), | |
| 57 | − BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), | |
| 58 | − BPF_MOV64_IMM(BPF_REG_3, mark), | |
| 59 | − | |
| 60 | − /* set the mark on the new socket */ | |
| 61 | − BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), | |
| 62 | − BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, mark)), | |
| 63 | − BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, mark)), | |
| 64 | − }; | |
| 65 | − | |
| 66 | − /* set priority on socket */ | |
| 67 | − struct bpf_insn prog_prio[] = { | |
| 68 | − BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), | |
| 69 | − BPF_MOV64_IMM(BPF_REG_3, prio), | |
| 70 | − BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, priority)), | |
| 71 | − BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, priority)), | |
| 72 | − }; | |
| 73 | − LIBBPF_OPTS(bpf_prog_load_opts, opts, | |
| 74 | − .log_buf = bpf_log_buf, | |
| 75 | − .log_size = BPF_LOG_BUF_SIZE, | |
| 76 | − ); | |
| 77 | − | |
| 78 | − struct bpf_insn *prog; | |
| 79 | − size_t insns_cnt; | |
| 80 | − void *p; | |
| 81 | − int ret; | |
| 82 | − | |
| 83 | − insns_cnt = sizeof(prog_start) + sizeof(prog_end); | |
| 84 | − if (idx) | |
| 85 | − insns_cnt += sizeof(prog_dev); | |
| 86 | − | |
| 87 | − if (mark) | |
| 88 | − insns_cnt += sizeof(prog_mark); | |
| 89 | − | |
| 90 | − if (prio) | |
| 91 | − insns_cnt += sizeof(prog_prio); | |
| 92 | − | |
| 93 | − p = prog = malloc(insns_cnt); | |
| 94 | − if (!prog) { | |
| 95 | − fprintf(stderr, "Failed to allocate memory for instructions\n"); | |
| 96 | − return EXIT_FAILURE; | |
| 97 | − } | |
| 98 | − | |
| 99 | − memcpy(p, prog_start, sizeof(prog_start)); | |
| 100 | − p += sizeof(prog_start); | |
| 101 | − | |
| 102 | − if (idx) { | |
| 103 | − memcpy(p, prog_dev, sizeof(prog_dev)); | |
| 104 | − p += sizeof(prog_dev); | |
| 105 | − } | |
| 106 | − | |
| 107 | − if (mark) { | |
| 108 | − memcpy(p, prog_mark, sizeof(prog_mark)); | |
| 109 | − p += sizeof(prog_mark); | |
| 110 | − } | |
| 111 | − | |
| 112 | − if (prio) { | |
| 113 | − memcpy(p, prog_prio, sizeof(prog_prio)); | |
| 114 | − p += sizeof(prog_prio); | |
| 115 | − } | |
| 116 | − | |
| 117 | − memcpy(p, prog_end, sizeof(prog_end)); | |
| 118 | − p += sizeof(prog_end); | |
| 119 | − | |
| 120 | − insns_cnt /= sizeof(struct bpf_insn); | |
| 121 | − | |
| 122 | − ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", | |
| 123 | − prog, insns_cnt, &opts); | |
| 124 | − | |
| 125 | − free(prog); | |
| 126 | − | |
| 127 | − return ret; | |
| 128 | −} | |
| 129 | − | |
| 130 | −static int get_bind_to_device(int sd, char *name, size_t len) | |
| 131 | −{ | |
| 132 | − socklen_t optlen = len; | |
| 133 | − int rc; | |
| 134 | − | |
| 135 | − name[0] = '\0'; | |
| 136 | − rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen); | |
| 137 | − if (rc < 0) | |
| 138 | − perror("setsockopt(SO_BINDTODEVICE)"); | |
| 139 | − | |
| 140 | − return rc; | |
| 141 | −} | |
| 142 | − | |
| 143 | −static unsigned int get_somark(int sd) | |
| 144 | −{ | |
| 145 | − unsigned int mark = 0; | |
| 146 | − socklen_t optlen = sizeof(mark); | |
| 147 | − int rc; | |
| 148 | − | |
| 149 | − rc = getsockopt(sd, SOL_SOCKET, SO_MARK, &mark, &optlen); | |
| 150 | − if (rc < 0) | |
| 151 | − perror("getsockopt(SO_MARK)"); | |
| 152 | − | |
| 153 | − return mark; | |
| 154 | −} | |
| 155 | − | |
| 156 | −static unsigned int get_priority(int sd) | |
| 157 | −{ | |
| 158 | − unsigned int prio = 0; | |
| 159 | − socklen_t optlen = sizeof(prio); | |
| 160 | − int rc; | |
| 161 | − | |
| 162 | − rc = getsockopt(sd, SOL_SOCKET, SO_PRIORITY, &prio, &optlen); | |
| 163 | − if (rc < 0) | |
| 164 | − perror("getsockopt(SO_PRIORITY)"); | |
| 165 | − | |
| 166 | − return prio; | |
| 167 | −} | |
| 168 | − | |
| 169 | −static int show_sockopts(int family) | |
| 170 | −{ | |
| 171 | − unsigned int mark, prio; | |
| 172 | − char name[16]; | |
| 173 | − int sd; | |
| 174 | − | |
| 175 | − sd = socket(family, SOCK_DGRAM, 17); | |
| 176 | − if (sd < 0) { | |
| 177 | − perror("socket"); | |
| 178 | − return 1; | |
| 179 | − } | |
| 180 | − | |
| 181 | − if (get_bind_to_device(sd, name, sizeof(name)) < 0) | |
| 182 | − return 1; | |
| 183 | − | |
| 184 | − mark = get_somark(sd); | |
| 185 | − prio = get_priority(sd); | |
| 186 | − | |
| 187 | − close(sd); | |
| 188 | − | |
| 189 | − printf("sd %d: dev %s, mark %u, priority %u\n", sd, name, mark, prio); | |
| 190 | − | |
| 191 | − return 0; | |
| 192 | −} | |
| 193 | − | |
| 194 | −static int usage(const char *argv0) | |
| 195 | −{ | |
| 196 | − printf("Usage:\n"); | |
| 197 | − printf(" Attach a program\n"); | |
| 198 | − printf(" %s -b bind-to-dev -m mark -p prio cg-path\n", argv0); | |
| 199 | − printf("\n"); | |
| 200 | − printf(" Detach a program\n"); | |
| 201 | − printf(" %s -d cg-path\n", argv0); | |
| 202 | − printf("\n"); | |
| 203 | − printf(" Show inherited socket settings (mark, priority, and device)\n"); | |
| 204 | − printf(" %s [-6]\n", argv0); | |
| 205 | − return EXIT_FAILURE; | |
| 206 | −} | |
| 207 | − | |
| 208 | −int main(int argc, char **argv) | |
| 209 | −{ | |
| 210 | − __u32 idx = 0, mark = 0, prio = 0; | |
| 211 | − const char *cgrp_path = NULL; | |
| 212 | − int cg_fd, prog_fd, ret; | |
| 213 | − int family = PF_INET; | |
| 214 | − int do_attach = 1; | |
| 215 | − int rc; | |
| 216 | − | |
| 217 | − while ((rc = getopt(argc, argv, "db:m:p:6")) != -1) { | |
| 218 | − switch (rc) { | |
| 219 | − case 'd': | |
| 220 | − do_attach = 0; | |
| 221 | − break; | |
| 222 | − case 'b': | |
| 223 | − idx = if_nametoindex(optarg); | |
| 224 | − if (!idx) { | |
| 225 | − idx = strtoumax(optarg, NULL, 0); | |
| 226 | − if (!idx) { | |
| 227 | − printf("Invalid device name\n"); | |
| 228 | − return EXIT_FAILURE; | |
| 229 | − } | |
| 230 | − } | |
| 231 | − break; | |
| 232 | − case 'm': | |
| 233 | − mark = strtoumax(optarg, NULL, 0); | |
| 234 | − break; | |
| 235 | − case 'p': | |
| 236 | − prio = strtoumax(optarg, NULL, 0); | |
| 237 | − break; | |
| 238 | − case '6': | |
| 239 | − family = PF_INET6; | |
| 240 | − break; | |
| 241 | − default: | |
| 242 | − return usage(argv[0]); | |
| 243 | − } | |
| 244 | − } | |
| 245 | − | |
| 246 | − if (optind == argc) | |
| 247 | − return show_sockopts(family); | |
| 248 | − | |
| 249 | − cgrp_path = argv[optind]; | |
| 250 | − if (!cgrp_path) { | |
| 251 | − fprintf(stderr, "cgroup path not given\n"); | |
| 252 | − return EXIT_FAILURE; | |
| 253 | − } | |
| 254 | − | |
| 255 | − if (do_attach && !idx && !mark && !prio) { | |
| 256 | − fprintf(stderr, | |
| 257 | − "One of device, mark or priority must be given\n"); | |
| 258 | − return EXIT_FAILURE; | |
| 259 | − } | |
| 260 | − | |
| 261 | − cg_fd = open(cgrp_path, O_DIRECTORY | O_RDONLY); | |
| 262 | − if (cg_fd < 0) { | |
| 263 | − printf("Failed to open cgroup path: '%s'\n", strerror(errno)); | |
| 264 | − return EXIT_FAILURE; | |
| 265 | − } | |
| 266 | − | |
| 267 | − if (do_attach) { | |
| 268 | − prog_fd = prog_load(idx, mark, prio); | |
| 269 | − if (prog_fd < 0) { | |
| 270 | − printf("Failed to load prog: '%s'\n", strerror(errno)); | |
| 271 | − printf("Output from kernel verifier:\n%s\n-------\n", | |
| 272 | − bpf_log_buf); | |
| 273 | − return EXIT_FAILURE; | |
| 274 | − } | |
| 275 | − | |
| 276 | − ret = bpf_prog_attach(prog_fd, cg_fd, | |
| 277 | − BPF_CGROUP_INET_SOCK_CREATE, 0); | |
| 278 | − if (ret < 0) { | |
| 279 | − printf("Failed to attach prog to cgroup: '%s'\n", | |
| 280 | − strerror(errno)); | |
| 281 | − return EXIT_FAILURE; | |
| 282 | − } | |
| 283 | − } else { | |
| 284 | − ret = bpf_prog_detach(cg_fd, BPF_CGROUP_INET_SOCK_CREATE); | |
| 285 | − if (ret < 0) { | |
| 286 | − printf("Failed to detach prog from cgroup: '%s'\n", | |
| 287 | − strerror(errno)); | |
| 288 | − return EXIT_FAILURE; | |
| 289 | − } | |
| 290 | − } | |
| 291 | − | |
| 292 | − close(cg_fd); | |
| 293 | − return EXIT_SUCCESS; | |
| 294 | −} | |
| @@ -1,137 +0,0 @@ | ||
| 1 | −#!/bin/sh | |
| 2 | −# SPDX-License-Identifier: GPL-2.0 | |
| 3 | − | |
| 4 | −# Test various socket options that can be set by attaching programs to cgroups. | |
| 5 | − | |
| 6 | −MY_DIR=$(dirname $0) | |
| 7 | −TEST=$MY_DIR/test_cgrp2_sock | |
| 8 | −CGRP_MNT="/tmp/cgroupv2-test_cgrp2_sock" | |
| 9 | − | |
| 10 | −################################################################################ | |
| 11 | −# | |
| 12 | −print_result() | |
| 13 | −{ | |
| 14 | − local rc=$1 | |
| 15 | − local status=" OK " | |
| 16 | − | |
| 17 | − [ $rc -ne 0 ] && status="FAIL" | |
| 18 | − | |
| 19 | − printf "%-50s [%4s]\n" "$2" "$status" | |
| 20 | −} | |
| 21 | − | |
| 22 | −check_sock() | |
| 23 | −{ | |
| 24 | − out=$($TEST) | |
| 25 | − echo $out | grep -q "$1" | |
| 26 | − if [ $? -ne 0 ]; then | |
| 27 | − print_result 1 "IPv4: $2" | |
| 28 | − echo " expected: $1" | |
| 29 | − echo " have: $out" | |
| 30 | − rc=1 | |
| 31 | − else | |
| 32 | − print_result 0 "IPv4: $2" | |
| 33 | − fi | |
| 34 | −} | |
| 35 | − | |
| 36 | −check_sock6() | |
| 37 | −{ | |
| 38 | − out=$($TEST -6) | |
| 39 | − echo $out | grep -q "$1" | |
| 40 | − if [ $? -ne 0 ]; then | |
| 41 | − print_result 1 "IPv6: $2" | |
| 42 | − echo " expected: $1" | |
| 43 | − echo " have: $out" | |
| 44 | − rc=1 | |
| 45 | − else | |
| 46 | − print_result 0 "IPv6: $2" | |
| 47 | − fi | |
| 48 | −} | |
| 49 | − | |
| 50 | −################################################################################ | |
| 51 | −# | |
| 52 | − | |
| 53 | −cleanup() | |
| 54 | −{ | |
| 55 | − echo $$ >> ${CGRP_MNT}/cgroup.procs | |
| 56 | − rmdir ${CGRP_MNT}/sockopts | |
| 57 | −} | |
| 58 | − | |
| 59 | −cleanup_and_exit() | |
| 60 | −{ | |
| 61 | − local rc=$1 | |
| 62 | − local msg="$2" | |
| 63 | − | |
| 64 | − [ -n "$msg" ] && echo "ERROR: $msg" | |
| 65 | − | |
| 66 | − $TEST -d ${CGRP_MNT}/sockopts | |
| 67 | − ip li del cgrp2_sock | |
| 68 | − umount ${CGRP_MNT} | |
| 69 | − | |
| 70 | − exit $rc | |
| 71 | −} | |
| 72 | − | |
| 73 | − | |
| 74 | −################################################################################ | |
| 75 | −# main | |
| 76 | − | |
| 77 | −rc=0 | |
| 78 | − | |
| 79 | −ip li add cgrp2_sock type dummy 2>/dev/null | |
| 80 | − | |
| 81 | −set -e | |
| 82 | −mkdir -p ${CGRP_MNT} | |
| 83 | −mount -t cgroup2 none ${CGRP_MNT} | |
| 84 | −set +e | |
| 85 | − | |
| 86 | − | |
| 87 | −# make sure we have a known start point | |
| 88 | −cleanup 2>/dev/null | |
| 89 | − | |
| 90 | −mkdir -p ${CGRP_MNT}/sockopts | |
| 91 | −[ $? -ne 0 ] && cleanup_and_exit 1 "Failed to create cgroup hierarchy" | |
| 92 | − | |
| 93 | − | |
| 94 | −# set pid into cgroup | |
| 95 | −echo $$ > ${CGRP_MNT}/sockopts/cgroup.procs | |
| 96 | − | |
| 97 | −# no bpf program attached, so socket should show no settings | |
| 98 | −check_sock "dev , mark 0, priority 0" "No programs attached" | |
| 99 | −check_sock6 "dev , mark 0, priority 0" "No programs attached" | |
| 100 | − | |
| 101 | −# verify device is set | |
| 102 | −# | |
| 103 | −$TEST -b cgrp2_sock ${CGRP_MNT}/sockopts | |
| 104 | −if [ $? -ne 0 ]; then | |
| 105 | − cleanup_and_exit 1 "Failed to install program to set device" | |
| 106 | −fi | |
| 107 | −check_sock "dev cgrp2_sock, mark 0, priority 0" "Device set" | |
| 108 | −check_sock6 "dev cgrp2_sock, mark 0, priority 0" "Device set" | |
| 109 | − | |
| 110 | −# verify mark is set | |
| 111 | −# | |
| 112 | −$TEST -m 666 ${CGRP_MNT}/sockopts | |
| 113 | −if [ $? -ne 0 ]; then | |
| 114 | − cleanup_and_exit 1 "Failed to install program to set mark" | |
| 115 | −fi | |
| 116 | −check_sock "dev , mark 666, priority 0" "Mark set" | |
| 117 | −check_sock6 "dev , mark 666, priority 0" "Mark set" | |
| 118 | − | |
| 119 | −# verify priority is set | |
| 120 | −# | |
| 121 | −$TEST -p 123 ${CGRP_MNT}/sockopts | |
| 122 | −if [ $? -ne 0 ]; then | |
| 123 | − cleanup_and_exit 1 "Failed to install program to set priority" | |
| 124 | −fi | |
| 125 | −check_sock "dev , mark 0, priority 123" "Priority set" | |
| 126 | −check_sock6 "dev , mark 0, priority 123" "Priority set" | |
| 127 | − | |
| 128 | −# all 3 at once | |
| 129 | −# | |
| 130 | −$TEST -b cgrp2_sock -m 666 -p 123 ${CGRP_MNT}/sockopts | |
| 131 | −if [ $? -ne 0 ]; then | |
| 132 | − cleanup_and_exit 1 "Failed to install program to set device, mark and priority" | |
| 133 | −fi | |
| 134 | −check_sock "dev cgrp2_sock, mark 666, priority 123" "Priority set" | |
| 135 | −check_sock6 "dev cgrp2_sock, mark 666, priority 123" "Priority set" | |
| 136 | − | |
| 137 | −cleanup_and_exit $rc | |
| @@ -1,95 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0 | |
| 2 | −/* eBPF example program: | |
| 3 | − * | |
| 4 | − * - Loads eBPF program | |
| 5 | − * | |
| 6 | − * The eBPF program loads a filter from file and attaches the | |
| 7 | − * program to a cgroup using BPF_PROG_ATTACH | |
| 8 | − */ | |
| 9 | − | |
| 10 | −#define _GNU_SOURCE | |
| 11 | − | |
| 12 | −#include <stdio.h> | |
| 13 | −#include <stdlib.h> | |
| 14 | −#include <stddef.h> | |
| 15 | −#include <string.h> | |
| 16 | −#include <unistd.h> | |
| 17 | −#include <assert.h> | |
| 18 | −#include <errno.h> | |
| 19 | −#include <fcntl.h> | |
| 20 | −#include <net/if.h> | |
| 21 | −#include <linux/bpf.h> | |
| 22 | −#include <bpf/bpf.h> | |
| 23 | −#include <bpf/libbpf.h> | |
| 24 | − | |
| 25 | −#include "bpf_insn.h" | |
| 26 | − | |
| 27 | −static int usage(const char *argv0) | |
| 28 | −{ | |
| 29 | − printf("Usage: %s cg-path filter-path [filter-id]\n", argv0); | |
| 30 | − return EXIT_FAILURE; | |
| 31 | −} | |
| 32 | − | |
| 33 | −int main(int argc, char **argv) | |
| 34 | −{ | |
| 35 | − int cg_fd, err, ret = EXIT_FAILURE, filter_id = 0, prog_cnt = 0; | |
| 36 | − const char *link_pin_path = "/sys/fs/bpf/test_cgrp2_sock2"; | |
| 37 | − struct bpf_link *link = NULL; | |
| 38 | − struct bpf_program *progs[2]; | |
| 39 | − struct bpf_program *prog; | |
| 40 | − struct bpf_object *obj; | |
| 41 | − | |
| 42 | − if (argc < 3) | |
| 43 | − return usage(argv[0]); | |
| 44 | − | |
| 45 | − if (argc > 3) | |
| 46 | − filter_id = atoi(argv[3]); | |
| 47 | − | |
| 48 | − cg_fd = open(argv[1], O_DIRECTORY | O_RDONLY); | |
| 49 | − if (cg_fd < 0) { | |
| 50 | − printf("Failed to open cgroup path: '%s'\n", strerror(errno)); | |
| 51 | − return ret; | |
| 52 | − } | |
| 53 | − | |
| 54 | − obj = bpf_object__open_file(argv[2], NULL); | |
| 55 | − if (libbpf_get_error(obj)) { | |
| 56 | − printf("ERROR: opening BPF object file failed\n"); | |
| 57 | − return ret; | |
| 58 | − } | |
| 59 | − | |
| 60 | − bpf_object__for_each_program(prog, obj) { | |
| 61 | − progs[prog_cnt] = prog; | |
| 62 | − prog_cnt++; | |
| 63 | − } | |
| 64 | − | |
| 65 | − if (filter_id >= prog_cnt) { | |
| 66 | − printf("Invalid program id; program not found in file\n"); | |
| 67 | − goto cleanup; | |
| 68 | − } | |
| 69 | − | |
| 70 | − /* load BPF program */ | |
| 71 | − if (bpf_object__load(obj)) { | |
| 72 | − printf("ERROR: loading BPF object file failed\n"); | |
| 73 | − goto cleanup; | |
| 74 | − } | |
| 75 | − | |
| 76 | − link = bpf_program__attach_cgroup(progs[filter_id], cg_fd); | |
| 77 | − if (libbpf_get_error(link)) { | |
| 78 | − printf("ERROR: bpf_program__attach failed\n"); | |
| 79 | − link = NULL; | |
| 80 | − goto cleanup; | |
| 81 | − } | |
| 82 | − | |
| 83 | − err = bpf_link__pin(link, link_pin_path); | |
| 84 | − if (err < 0) { | |
| 85 | − printf("ERROR: bpf_link__pin failed: %d\n", err); | |
| 86 | − goto cleanup; | |
| 87 | − } | |
| 88 | − | |
| 89 | − ret = EXIT_SUCCESS; | |
| 90 | − | |
| 91 | −cleanup: | |
| 92 | − bpf_link__destroy(link); | |
| 93 | − bpf_object__close(obj); | |
| 94 | − return ret; | |
| 95 | −} | |
| @@ -1,103 +0,0 @@ | ||
| 1 | −#!/bin/bash | |
| 2 | −# SPDX-License-Identifier: GPL-2.0 | |
| 3 | − | |
| 4 | −BPFFS=/sys/fs/bpf | |
| 5 | −MY_DIR=$(dirname $0) | |
| 6 | −TEST=$MY_DIR/test_cgrp2_sock2 | |
| 7 | −LINK_PIN=$BPFFS/test_cgrp2_sock2 | |
| 8 | −BPF_PROG=$MY_DIR/sock_flags.bpf.o | |
| 9 | − | |
| 10 | −function config_device { | |
| 11 | − ip netns add at_ns0 | |
| 12 | − ip link add veth0 type veth peer name veth0b | |
| 13 | − ip link set veth0 netns at_ns0 | |
| 14 | − ip netns exec at_ns0 sysctl -q net.ipv6.conf.veth0.disable_ipv6=0 | |
| 15 | − ip netns exec at_ns0 ip addr add 172.16.1.100/24 dev veth0 | |
| 16 | − ip netns exec at_ns0 ip addr add 2401:db00::1/64 dev veth0 nodad | |
| 17 | − ip netns exec at_ns0 ip link set dev veth0 up | |
| 18 | − sysctl -q net.ipv6.conf.veth0b.disable_ipv6=0 | |
| 19 | − ip addr add 172.16.1.101/24 dev veth0b | |
| 20 | − ip addr add 2401:db00::2/64 dev veth0b nodad | |
| 21 | − ip link set veth0b up | |
| 22 | −} | |
| 23 | − | |
| 24 | −function config_cgroup { | |
| 25 | − rm -rf /tmp/cgroupv2 | |
| 26 | − mkdir -p /tmp/cgroupv2 | |
| 27 | − mount -t cgroup2 none /tmp/cgroupv2 | |
| 28 | − mkdir -p /tmp/cgroupv2/foo | |
| 29 | − echo $$ >> /tmp/cgroupv2/foo/cgroup.procs | |
| 30 | −} | |
| 31 | − | |
| 32 | −function config_bpffs { | |
| 33 | − if mount | grep $BPFFS > /dev/null; then | |
| 34 | − echo "bpffs already mounted" | |
| 35 | − else | |
| 36 | − echo "bpffs not mounted. Mounting..." | |
| 37 | − mount -t bpf none $BPFFS | |
| 38 | − fi | |
| 39 | −} | |
| 40 | − | |
| 41 | −function attach_bpf { | |
| 42 | − $TEST /tmp/cgroupv2/foo $BPF_PROG $1 | |
| 43 | − [ $? -ne 0 ] && exit 1 | |
| 44 | −} | |
| 45 | − | |
| 46 | −function cleanup { | |
| 47 | − rm -rf $LINK_PIN | |
| 48 | − ip link del veth0b | |
| 49 | − ip netns delete at_ns0 | |
| 50 | − umount /tmp/cgroupv2 | |
| 51 | − rm -rf /tmp/cgroupv2 | |
| 52 | −} | |
| 53 | − | |
| 54 | −cleanup 2>/dev/null | |
| 55 | − | |
| 56 | −set -e | |
| 57 | −config_device | |
| 58 | −config_cgroup | |
| 59 | −config_bpffs | |
| 60 | −set +e | |
| 61 | − | |
| 62 | −# | |
| 63 | −# Test 1 - fail ping6 | |
| 64 | −# | |
| 65 | −attach_bpf 0 | |
| 66 | −ping -c1 -w1 172.16.1.100 | |
| 67 | −if [ $? -ne 0 ]; then | |
| 68 | − echo "ping failed when it should succeed" | |
| 69 | − cleanup | |
| 70 | − exit 1 | |
| 71 | −fi | |
| 72 | − | |
| 73 | −ping6 -c1 -w1 2401:db00::1 | |
| 74 | −if [ $? -eq 0 ]; then | |
| 75 | − echo "ping6 succeeded when it should not" | |
| 76 | − cleanup | |
| 77 | − exit 1 | |
| 78 | −fi | |
| 79 | − | |
| 80 | −rm -rf $LINK_PIN | |
| 81 | −sleep 1 # Wait for link detach | |
| 82 | − | |
| 83 | −# | |
| 84 | −# Test 2 - fail ping | |
| 85 | −# | |
| 86 | −attach_bpf 1 | |
| 87 | −ping6 -c1 -w1 2401:db00::1 | |
| 88 | −if [ $? -ne 0 ]; then | |
| 89 | − echo "ping6 failed when it should succeed" | |
| 90 | − cleanup | |
| 91 | − exit 1 | |
| 92 | −fi | |
| 93 | − | |
| 94 | −ping -c1 -w1 172.16.1.100 | |
| 95 | −if [ $? -eq 0 ]; then | |
| 96 | − echo "ping succeeded when it should not" | |
| 97 | − cleanup | |
| 98 | − exit 1 | |
| 99 | −fi | |
| 100 | − | |
| 101 | −cleanup | |
| 102 | −echo | |
| 103 | −echo "*** PASS ***" | |
| @@ -1,56 +0,0 @@ | ||
| 1 | −/* Copyright (c) 2016 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 | −#define KBUILD_MODNAME "foo" | |
| 8 | −#include "vmlinux.h" | |
| 9 | −#include "net_shared.h" | |
| 10 | −#include <bpf/bpf_helpers.h> | |
| 11 | − | |
| 12 | −/* copy of 'struct ethhdr' without __packed */ | |
| 13 | −struct eth_hdr { | |
| 14 | − unsigned char h_dest[ETH_ALEN]; | |
| 15 | − unsigned char h_source[ETH_ALEN]; | |
| 16 | − unsigned short h_proto; | |
| 17 | −}; | |
| 18 | − | |
| 19 | −struct { | |
| 20 | − __uint(type, BPF_MAP_TYPE_CGROUP_ARRAY); | |
| 21 | − __type(key, u32); | |
| 22 | − __type(value, u32); | |
| 23 | − __uint(pinning, LIBBPF_PIN_BY_NAME); | |
| 24 | − __uint(max_entries, 1); | |
| 25 | −} test_cgrp2_array_pin SEC(".maps"); | |
| 26 | − | |
| 27 | −SEC("filter") | |
| 28 | −int handle_egress(struct __sk_buff *skb) | |
| 29 | −{ | |
| 30 | − void *data = (void *)(long)skb->data; | |
| 31 | − struct eth_hdr *eth = data; | |
| 32 | − struct ipv6hdr *ip6h = data + sizeof(*eth); | |
| 33 | − void *data_end = (void *)(long)skb->data_end; | |
| 34 | − char dont_care_msg[] = "dont care %04x %d\n"; | |
| 35 | − char pass_msg[] = "pass\n"; | |
| 36 | − char reject_msg[] = "reject\n"; | |
| 37 | − | |
| 38 | − /* single length check */ | |
| 39 | − if (data + sizeof(*eth) + sizeof(*ip6h) > data_end) | |
| 40 | − return TC_ACT_OK; | |
| 41 | − | |
| 42 | − if (eth->h_proto != bpf_htons(ETH_P_IPV6) || | |
| 43 | − ip6h->nexthdr != IPPROTO_ICMPV6) { | |
| 44 | − bpf_trace_printk(dont_care_msg, sizeof(dont_care_msg), | |
| 45 | − eth->h_proto, ip6h->nexthdr); | |
| 46 | − return TC_ACT_OK; | |
| 47 | − } else if (bpf_skb_under_cgroup(skb, &test_cgrp2_array_pin, 0) != 1) { | |
| 48 | − bpf_trace_printk(pass_msg, sizeof(pass_msg)); | |
| 49 | − return TC_ACT_OK; | |
| 50 | − } else { | |
| 51 | − bpf_trace_printk(reject_msg, sizeof(reject_msg)); | |
| 52 | − return TC_ACT_SHOT; | |
| 53 | − } | |
| 54 | −} | |
| 55 | − | |
| 56 | −char _license[] SEC("license") = "GPL"; | |
| @@ -1,187 +0,0 @@ | ||
| 1 | −#!/bin/bash | |
| 2 | −# SPDX-License-Identifier: GPL-2.0 | |
| 3 | − | |
| 4 | −MY_DIR=$(dirname $0) | |
| 5 | −# Details on the bpf prog | |
| 6 | −BPF_CGRP2_ARRAY_NAME='test_cgrp2_array_pin' | |
| 7 | −BPF_PROG="$MY_DIR/test_cgrp2_tc.bpf.o" | |
| 8 | −BPF_SECTION='filter' | |
| 9 | − | |
| 10 | −[ -z "$TC" ] && TC='tc' | |
| 11 | −[ -z "$IP" ] && IP='ip' | |
| 12 | − | |
| 13 | −# Names of the veth interface, net namespace...etc. | |
| 14 | −HOST_IFC='ve' | |
| 15 | −NS_IFC='vens' | |
| 16 | −NS='ns' | |
| 17 | − | |
| 18 | −find_mnt() { | |
| 19 | − cat /proc/mounts | \ | |
| 20 | − awk '{ if ($3 == "'$1'" && mnt == "") { mnt = $2 }} END { print mnt }' | |
| 21 | −} | |
| 22 | − | |
| 23 | −# Init cgroup2 vars | |
| 24 | −init_cgrp2_vars() { | |
| 25 | − CGRP2_ROOT=$(find_mnt cgroup2) | |
| 26 | − if [ -z "$CGRP2_ROOT" ] | |
| 27 | − then | |
| 28 | − CGRP2_ROOT='/mnt/cgroup2' | |
| 29 | − MOUNT_CGRP2="yes" | |
| 30 | − fi | |
| 31 | − CGRP2_TC="$CGRP2_ROOT/tc" | |
| 32 | − CGRP2_TC_LEAF="$CGRP2_TC/leaf" | |
| 33 | −} | |
| 34 | − | |
| 35 | −# Init bpf fs vars | |
| 36 | −init_bpf_fs_vars() { | |
| 37 | − local bpf_fs_root=$(find_mnt bpf) | |
| 38 | − [ -n "$bpf_fs_root" ] || return -1 | |
| 39 | − BPF_FS_TC_SHARE="$bpf_fs_root/tc/globals" | |
| 40 | −} | |
| 41 | − | |
| 42 | −setup_cgrp2() { | |
| 43 | − case $1 in | |
| 44 | − start) | |
| 45 | − if [ "$MOUNT_CGRP2" == 'yes' ] | |
| 46 | − then | |
| 47 | − [ -d $CGRP2_ROOT ] || mkdir -p $CGRP2_ROOT | |
| 48 | − mount -t cgroup2 none $CGRP2_ROOT || return $? | |
| 49 | − fi | |
| 50 | − mkdir -p $CGRP2_TC_LEAF | |
| 51 | − ;; | |
| 52 | − *) | |
| 53 | − rmdir $CGRP2_TC_LEAF && rmdir $CGRP2_TC | |
| 54 | − [ "$MOUNT_CGRP2" == 'yes' ] && umount $CGRP2_ROOT | |
| 55 | − ;; | |
| 56 | − esac | |
| 57 | −} | |
| 58 | − | |
| 59 | −setup_bpf_cgrp2_array() { | |
| 60 | − local bpf_cgrp2_array="$BPF_FS_TC_SHARE/$BPF_CGRP2_ARRAY_NAME" | |
| 61 | − case $1 in | |
| 62 | − start) | |
| 63 | − $MY_DIR/test_cgrp2_array_pin -U $bpf_cgrp2_array -v $CGRP2_TC | |
| 64 | − ;; | |
| 65 | − *) | |
| 66 | − [ -d "$BPF_FS_TC_SHARE" ] && rm -f $bpf_cgrp2_array | |
| 67 | − ;; | |
| 68 | − esac | |
| 69 | −} | |
| 70 | − | |
| 71 | −setup_net() { | |
| 72 | − case $1 in | |
| 73 | − start) | |
| 74 | − $IP link add $HOST_IFC type veth peer name $NS_IFC || return $? | |
| 75 | − $IP link set dev $HOST_IFC up || return $? | |
| 76 | − sysctl -q net.ipv6.conf.$HOST_IFC.disable_ipv6=0 | |
| 77 | − sysctl -q net.ipv6.conf.$HOST_IFC.accept_dad=0 | |
| 78 | − | |
| 79 | − $IP netns add $NS || return $? | |
| 80 | − $IP link set dev $NS_IFC netns $NS || return $? | |
| 81 | − $IP -n $NS link set dev $NS_IFC up || return $? | |
| 82 | − $IP netns exec $NS sysctl -q net.ipv6.conf.$NS_IFC.disable_ipv6=0 | |
| 83 | − $IP netns exec $NS sysctl -q net.ipv6.conf.$NS_IFC.accept_dad=0 | |
| 84 | − $TC qdisc add dev $HOST_IFC clsact || return $? | |
| 85 | − $TC filter add dev $HOST_IFC egress bpf da obj $BPF_PROG sec $BPF_SECTION || return $? | |
| 86 | − ;; | |
| 87 | − *) | |
| 88 | − $IP netns del $NS | |
| 89 | − $IP link del $HOST_IFC | |
| 90 | − ;; | |
| 91 | − esac | |
| 92 | −} | |
| 93 | − | |
| 94 | −run_in_cgrp() { | |
| 95 | − # Fork another bash and move it under the specified cgroup. | |
| 96 | − # It makes the cgroup cleanup easier at the end of the test. | |
| 97 | − cmd='echo $$ > ' | |
| 98 | − cmd="$cmd $1/cgroup.procs; exec $2" | |
| 99 | − bash -c "$cmd" | |
| 100 | −} | |
| 101 | − | |
| 102 | −do_test() { | |
| 103 | − run_in_cgrp $CGRP2_TC_LEAF "ping -6 -c3 ff02::1%$HOST_IFC >& /dev/null" | |
| 104 | − local dropped=$($TC -s qdisc show dev $HOST_IFC | tail -3 | \ | |
| 105 | − awk '/drop/{print substr($7, 0, index($7, ",")-1)}') | |
| 106 | − if [[ $dropped -eq 0 ]] | |
| 107 | − then | |
| 108 | − echo "FAIL" | |
| 109 | − return 1 | |
| 110 | − else | |
| 111 | − echo "Successfully filtered $dropped packets" | |
| 112 | − return 0 | |
| 113 | − fi | |
| 114 | −} | |
| 115 | − | |
| 116 | −do_exit() { | |
| 117 | − if [ "$DEBUG" == "yes" ] && [ "$MODE" != 'cleanuponly' ] | |
| 118 | − then | |
| 119 | − echo "------ DEBUG ------" | |
| 120 | − echo "mount: "; mount | grep -E '(cgroup2|bpf)'; echo | |
| 121 | − echo "$CGRP2_TC_LEAF: "; ls -l $CGRP2_TC_LEAF; echo | |
| 122 | − if [ -d "$BPF_FS_TC_SHARE" ] | |
| 123 | − then | |
| 124 | − echo "$BPF_FS_TC_SHARE: "; ls -l $BPF_FS_TC_SHARE; echo | |
| 125 | − fi | |
| 126 | − echo "Host net:" | |
| 127 | − $IP netns | |
| 128 | − $IP link show dev $HOST_IFC | |
| 129 | − $IP -6 a show dev $HOST_IFC | |
| 130 | − $TC -s qdisc show dev $HOST_IFC | |
| 131 | − echo | |
| 132 | − echo "$NS net:" | |
| 133 | − $IP -n $NS link show dev $NS_IFC | |
| 134 | − $IP -n $NS -6 link show dev $NS_IFC | |
| 135 | − echo "------ DEBUG ------" | |
| 136 | − echo | |
| 137 | − fi | |
| 138 | − | |
| 139 | − if [ "$MODE" != 'nocleanup' ] | |
| 140 | − then | |
| 141 | − setup_net stop | |
| 142 | − setup_bpf_cgrp2_array stop | |
| 143 | − setup_cgrp2 stop | |
| 144 | − fi | |
| 145 | −} | |
| 146 | − | |
| 147 | −init_cgrp2_vars | |
| 148 | −init_bpf_fs_vars | |
| 149 | − | |
| 150 | −while [[ $# -ge 1 ]] | |
| 151 | −do | |
| 152 | − a="$1" | |
| 153 | − case $a in | |
| 154 | − debug) | |
| 155 | − DEBUG='yes' | |
| 156 | − shift 1 | |
| 157 | − ;; | |
| 158 | − cleanup-only) | |
| 159 | − MODE='cleanuponly' | |
| 160 | − shift 1 | |
| 161 | − ;; | |
| 162 | − no-cleanup) | |
| 163 | − MODE='nocleanup' | |
| 164 | − shift 1 | |
| 165 | − ;; | |
| 166 | − *) | |
| 167 | − echo "test_cgrp2_tc [debug] [cleanup-only | no-cleanup]" | |
| 168 | − echo " debug: Print cgrp and network setup details at the end of the test" | |
| 169 | − echo " cleanup-only: Try to cleanup things from last test. No test will be run" | |
| 170 | − echo " no-cleanup: Run the test but don't do cleanup at the end" | |
| 171 | − echo "[Note: If no arg is given, it will run the test and do cleanup at the end]" | |
| 172 | − echo | |
| 173 | − exit -1 | |
| 174 | − ;; | |
| 175 | − esac | |
| 176 | −done | |
| 177 | − | |
| 178 | −trap do_exit 0 | |
| 179 | − | |
| 180 | −[ "$MODE" == 'cleanuponly' ] && exit | |
| 181 | − | |
| 182 | −setup_cgrp2 start || exit $? | |
| 183 | −setup_net start || exit $? | |
| 184 | −init_bpf_fs_vars || exit $? | |
| 185 | −setup_bpf_cgrp2_array start || exit $? | |
| 186 | −do_test | |
| 187 | −echo | |
| @@ -1,43 +0,0 @@ | ||
| 1 | −/* Copyright (c) 2016 Sargun Dhillon <sargun@sargun.me> | |
| 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 | −#include "vmlinux.h" | |
| 9 | −#include <linux/version.h> | |
| 10 | −#include <bpf/bpf_helpers.h> | |
| 11 | −#include <bpf/bpf_tracing.h> | |
| 12 | −#include <bpf/bpf_core_read.h> | |
| 13 | − | |
| 14 | −struct { | |
| 15 | − __uint(type, BPF_MAP_TYPE_CGROUP_ARRAY); | |
| 16 | − __uint(key_size, sizeof(u32)); | |
| 17 | − __uint(value_size, sizeof(u32)); | |
| 18 | − __uint(max_entries, 1); | |
| 19 | −} cgroup_map SEC(".maps"); | |
| 20 | − | |
| 21 | −struct { | |
| 22 | − __uint(type, BPF_MAP_TYPE_ARRAY); | |
| 23 | − __type(key, u32); | |
| 24 | − __type(value, u64); | |
| 25 | − __uint(max_entries, 1); | |
| 26 | −} perf_map SEC(".maps"); | |
| 27 | − | |
| 28 | −/* Writes the last PID that called sync to a map at index 0 */ | |
| 29 | −SEC("ksyscall/sync") | |
| 30 | −int BPF_KSYSCALL(bpf_prog1) | |
| 31 | −{ | |
| 32 | − u64 pid = bpf_get_current_pid_tgid(); | |
| 33 | − int idx = 0; | |
| 34 | − | |
| 35 | − if (!bpf_current_task_under_cgroup(&cgroup_map, 0)) | |
| 36 | − return 0; | |
| 37 | − | |
| 38 | − bpf_map_update_elem(&perf_map, &idx, &pid, BPF_ANY); | |
| 39 | − return 0; | |
| 40 | −} | |
| 41 | − | |
| 42 | −char _license[] SEC("license") = "GPL"; | |
| 43 | −u32 _version SEC("version") = LINUX_VERSION_CODE; | |
| @@ -1,115 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −/* Copyright (c) 2016 Sargun Dhillon <sargun@sargun.me> | |
| 3 | − */ | |
| 4 | − | |
| 5 | −#define _GNU_SOURCE | |
| 6 | −#include <stdio.h> | |
| 7 | −#include <unistd.h> | |
| 8 | −#include <bpf/bpf.h> | |
| 9 | −#include <bpf/libbpf.h> | |
| 10 | −#include "cgroup_helpers.h" | |
| 11 | − | |
| 12 | −#define CGROUP_PATH "/my-cgroup" | |
| 13 | − | |
| 14 | −int main(int argc, char **argv) | |
| 15 | −{ | |
| 16 | − pid_t remote_pid, local_pid = getpid(); | |
| 17 | − int cg2 = -1, idx = 0, rc = 1; | |
| 18 | − struct bpf_link *link = NULL; | |
| 19 | − struct bpf_program *prog; | |
| 20 | − struct bpf_object *obj; | |
| 21 | − char filename[256]; | |
| 22 | − int map_fd[2]; | |
| 23 | − | |
| 24 | − snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]); | |
| 25 | − obj = bpf_object__open_file(filename, NULL); | |
| 26 | − if (libbpf_get_error(obj)) { | |
| 27 | − fprintf(stderr, "ERROR: opening BPF object file failed\n"); | |
| 28 | − return 0; | |
| 29 | − } | |
| 30 | − | |
| 31 | − prog = bpf_object__find_program_by_name(obj, "bpf_prog1"); | |
| 32 | − if (!prog) { | |
| 33 | − printf("finding a prog in obj file failed\n"); | |
| 34 | − goto cleanup; | |
| 35 | − } | |
| 36 | − | |
| 37 | − /* load BPF program */ | |
| 38 | − if (bpf_object__load(obj)) { | |
| 39 | − fprintf(stderr, "ERROR: loading BPF object file failed\n"); | |
| 40 | − goto cleanup; | |
| 41 | − } | |
| 42 | − | |
| 43 | − map_fd[0] = bpf_object__find_map_fd_by_name(obj, "cgroup_map"); | |
| 44 | − map_fd[1] = bpf_object__find_map_fd_by_name(obj, "perf_map"); | |
| 45 | − if (map_fd[0] < 0 || map_fd[1] < 0) { | |
| 46 | − fprintf(stderr, "ERROR: finding a map in obj file failed\n"); | |
| 47 | − goto cleanup; | |
| 48 | − } | |
| 49 | − | |
| 50 | − link = bpf_program__attach(prog); | |
| 51 | − if (libbpf_get_error(link)) { | |
| 52 | − fprintf(stderr, "ERROR: bpf_program__attach failed\n"); | |
| 53 | − link = NULL; | |
| 54 | − goto cleanup; | |
| 55 | − } | |
| 56 | − | |
| 57 | − if (setup_cgroup_environment()) | |
| 58 | − goto err; | |
| 59 | − | |
| 60 | − cg2 = create_and_get_cgroup(CGROUP_PATH); | |
| 61 | − | |
| 62 | − if (cg2 < 0) | |
| 63 | − goto err; | |
| 64 | − | |
| 65 | − if (bpf_map_update_elem(map_fd[0], &idx, &cg2, BPF_ANY)) { | |
| 66 | − log_err("Adding target cgroup to map"); | |
| 67 | − goto err; | |
| 68 | − } | |
| 69 | − | |
| 70 | − if (join_cgroup(CGROUP_PATH)) | |
| 71 | − goto err; | |
| 72 | − | |
| 73 | − /* | |
| 74 | − * The installed helper program catched the sync call, and should | |
| 75 | − * write it to the map. | |
| 76 | − */ | |
| 77 | − | |
| 78 | − sync(); | |
| 79 | − bpf_map_lookup_elem(map_fd[1], &idx, &remote_pid); | |
| 80 | − | |
| 81 | − if (local_pid != remote_pid) { | |
| 82 | − fprintf(stderr, | |
| 83 | − "BPF Helper didn't write correct PID to map, but: %d\n", | |
| 84 | − remote_pid); | |
| 85 | − goto err; | |
| 86 | − } | |
| 87 | − | |
| 88 | − /* Verify the negative scenario; leave the cgroup */ | |
| 89 | − if (join_cgroup("/")) | |
| 90 | − goto err; | |
| 91 | − | |
| 92 | − remote_pid = 0; | |
| 93 | − bpf_map_update_elem(map_fd[1], &idx, &remote_pid, BPF_ANY); | |
| 94 | − | |
| 95 | − sync(); | |
| 96 | − bpf_map_lookup_elem(map_fd[1], &idx, &remote_pid); | |
| 97 | − | |
| 98 | − if (local_pid == remote_pid) { | |
| 99 | − fprintf(stderr, "BPF cgroup negative test did not work\n"); | |
| 100 | − goto err; | |
| 101 | − } | |
| 102 | − | |
| 103 | − rc = 0; | |
| 104 | − | |
| 105 | −err: | |
| 106 | − if (cg2 != -1) | |
| 107 | − close(cg2); | |
| 108 | − | |
| 109 | − cleanup_cgroup_environment(); | |
| 110 | − | |
| 111 | −cleanup: | |
| 112 | − bpf_link__destroy(link); | |
| 113 | − bpf_object__close(obj); | |
| 114 | − return rc; | |
| 115 | −} | |
| @@ -1,41 +0,0 @@ | ||
| 1 | −/* Copyright (c) 2016 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 | −#include "vmlinux.h" | |
| 8 | −#include <linux/version.h> | |
| 9 | −#include <bpf/bpf_helpers.h> | |
| 10 | −#include <bpf/bpf_tracing.h> | |
| 11 | −#include <bpf/bpf_core_read.h> | |
| 12 | − | |
| 13 | −SEC("kprobe/__set_task_comm") | |
| 14 | −int prog(struct pt_regs *ctx) | |
| 15 | −{ | |
| 16 | − struct signal_struct *signal; | |
| 17 | − struct task_struct *tsk; | |
| 18 | − char oldcomm[TASK_COMM_LEN] = {}; | |
| 19 | − char newcomm[TASK_COMM_LEN] = {}; | |
| 20 | − u16 oom_score_adj; | |
| 21 | − u32 pid; | |
| 22 | − | |
| 23 | − tsk = (void *)PT_REGS_PARM1_CORE(ctx); | |
| 24 | − | |
| 25 | − pid = BPF_CORE_READ(tsk, pid); | |
| 26 | − bpf_core_read_str(oldcomm, sizeof(oldcomm), &tsk->comm); | |
| 27 | − bpf_core_read_str(newcomm, sizeof(newcomm), | |
| 28 | − (void *)PT_REGS_PARM2(ctx)); | |
| 29 | − signal = BPF_CORE_READ(tsk, signal); | |
| 30 | − oom_score_adj = BPF_CORE_READ(signal, oom_score_adj); | |
| 31 | − return 0; | |
| 32 | −} | |
| 33 | − | |
| 34 | −SEC("kprobe/fib_table_lookup") | |
| 35 | −int prog2(struct pt_regs *ctx) | |
| 36 | −{ | |
| 37 | − return 0; | |
| 38 | −} | |
| 39 | − | |
| 40 | −char _license[] SEC("license") = "GPL"; | |
| 41 | −u32 _version SEC("version") = LINUX_VERSION_CODE; | |
| @@ -1,17 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0 | |
| 2 | −/* Copyright (c) 2018 Facebook */ | |
| 3 | −#include "vmlinux.h" | |
| 4 | −#include <bpf/bpf_helpers.h> | |
| 5 | − | |
| 6 | −SEC("raw_tracepoint/task_rename") | |
| 7 | −int prog(struct bpf_raw_tracepoint_args *ctx) | |
| 8 | −{ | |
| 9 | − return 0; | |
| 10 | −} | |
| 11 | − | |
| 12 | −SEC("raw_tracepoint/fib_table_lookup") | |
| 13 | −int prog2(struct bpf_raw_tracepoint_args *ctx) | |
| 14 | −{ | |
| 15 | − return 0; | |
| 16 | −} | |
| 17 | −char _license[] SEC("license") = "GPL"; | |
| @@ -1,23 +0,0 @@ | ||
| 1 | −/* Copyright (c) 2016 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 | −#include "vmlinux.h" | |
| 8 | −#include <bpf/bpf_helpers.h> | |
| 9 | − | |
| 10 | −/* from /sys/kernel/tracing/events/task/task_rename/format */ | |
| 11 | −SEC("tracepoint/task/task_rename") | |
| 12 | −int prog(struct trace_event_raw_task_rename *ctx) | |
| 13 | −{ | |
| 14 | − return 0; | |
| 15 | −} | |
| 16 | − | |
| 17 | −/* from /sys/kernel/tracing/events/fib/fib_table_lookup/format */ | |
| 18 | −SEC("tracepoint/fib/fib_table_lookup") | |
| 19 | −int prog2(struct trace_event_raw_fib_table_lookup *ctx) | |
| 20 | −{ | |
| 21 | − return 0; | |
| 22 | −} | |
| 23 | −char _license[] SEC("license") = "GPL"; | |
| @@ -1,225 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −/* Copyright (c) 2016 Facebook | |
| 3 | − */ | |
| 4 | −#define _GNU_SOURCE | |
| 5 | −#include <sched.h> | |
| 6 | −#include <errno.h> | |
| 7 | −#include <stdio.h> | |
| 8 | −#include <sys/types.h> | |
| 9 | −#include <asm/unistd.h> | |
| 10 | −#include <fcntl.h> | |
| 11 | −#include <unistd.h> | |
| 12 | −#include <assert.h> | |
| 13 | −#include <sys/wait.h> | |
| 14 | −#include <sys/socket.h> | |
| 15 | −#include <arpa/inet.h> | |
| 16 | −#include <stdlib.h> | |
| 17 | −#include <signal.h> | |
| 18 | −#include <linux/bpf.h> | |
| 19 | −#include <string.h> | |
| 20 | −#include <time.h> | |
| 21 | −#include <bpf/bpf.h> | |
| 22 | −#include <bpf/libbpf.h> | |
| 23 | − | |
| 24 | −#define MAX_CNT 1000000 | |
| 25 | −#define DUMMY_IP "127.0.0.1" | |
| 26 | −#define DUMMY_PORT 80 | |
| 27 | − | |
| 28 | −static struct bpf_link *links[2]; | |
| 29 | −static struct bpf_object *obj; | |
| 30 | −static int cnt; | |
| 31 | − | |
| 32 | −static __u64 time_get_ns(void) | |
| 33 | −{ | |
| 34 | − struct timespec ts; | |
| 35 | − | |
| 36 | − clock_gettime(CLOCK_MONOTONIC, &ts); | |
| 37 | − return ts.tv_sec * 1000000000ull + ts.tv_nsec; | |
| 38 | −} | |
| 39 | − | |
| 40 | −static void test_task_rename(int cpu) | |
| 41 | −{ | |
| 42 | − char buf[] = "test\n"; | |
| 43 | − __u64 start_time; | |
| 44 | − int i, fd; | |
| 45 | − | |
| 46 | − fd = open("/proc/self/comm", O_WRONLY|O_TRUNC); | |
| 47 | − if (fd < 0) { | |
| 48 | − printf("couldn't open /proc\n"); | |
| 49 | − exit(1); | |
| 50 | − } | |
| 51 | − start_time = time_get_ns(); | |
| 52 | − for (i = 0; i < MAX_CNT; i++) { | |
| 53 | − if (write(fd, buf, sizeof(buf)) < 0) { | |
| 54 | − printf("task rename failed: %s\n", strerror(errno)); | |
| 55 | − close(fd); | |
| 56 | − return; | |
| 57 | − } | |
| 58 | − } | |
| 59 | − printf("task_rename:%d: %lld events per sec\n", | |
| 60 | − cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); | |
| 61 | − close(fd); | |
| 62 | −} | |
| 63 | − | |
| 64 | −static void test_fib_table_lookup(int cpu) | |
| 65 | −{ | |
| 66 | − struct sockaddr_in addr; | |
| 67 | − char buf[] = "test\n"; | |
| 68 | − __u64 start_time; | |
| 69 | − int i, fd; | |
| 70 | − | |
| 71 | − fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
| 72 | − if (fd < 0) { | |
| 73 | − printf("couldn't open socket\n"); | |
| 74 | − exit(1); | |
| 75 | − } | |
| 76 | − memset((char *)&addr, 0, sizeof(addr)); | |
| 77 | − addr.sin_addr.s_addr = inet_addr(DUMMY_IP); | |
| 78 | − addr.sin_port = htons(DUMMY_PORT); | |
| 79 | − addr.sin_family = AF_INET; | |
| 80 | − start_time = time_get_ns(); | |
| 81 | − for (i = 0; i < MAX_CNT; i++) { | |
| 82 | − if (sendto(fd, buf, strlen(buf), 0, | |
| 83 | − (struct sockaddr *)&addr, sizeof(addr)) < 0) { | |
| 84 | − printf("failed to start ping: %s\n", strerror(errno)); | |
| 85 | − close(fd); | |
| 86 | − return; | |
| 87 | − } | |
| 88 | − } | |
| 89 | − printf("fib_table_lookup:%d: %lld events per sec\n", | |
| 90 | − cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time)); | |
| 91 | − close(fd); | |
| 92 | −} | |
| 93 | − | |
| 94 | −static void loop(int cpu, int flags) | |
| 95 | −{ | |
| 96 | − cpu_set_t cpuset; | |
| 97 | − | |
| 98 | − CPU_ZERO(&cpuset); | |
| 99 | − CPU_SET(cpu, &cpuset); | |
| 100 | − sched_setaffinity(0, sizeof(cpuset), &cpuset); | |
| 101 | − | |
| 102 | − if (flags & 1) | |
| 103 | − test_task_rename(cpu); | |
| 104 | − if (flags & 2) | |
| 105 | − test_fib_table_lookup(cpu); | |
| 106 | −} | |
| 107 | − | |
| 108 | −static void run_perf_test(int tasks, int flags) | |
| 109 | −{ | |
| 110 | − pid_t pid[tasks]; | |
| 111 | − int i; | |
| 112 | − | |
| 113 | − for (i = 0; i < tasks; i++) { | |
| 114 | − pid[i] = fork(); | |
| 115 | − if (pid[i] == 0) { | |
| 116 | − loop(i, flags); | |
| 117 | − exit(0); | |
| 118 | − } else if (pid[i] == -1) { | |
| 119 | − printf("couldn't spawn #%d process\n", i); | |
| 120 | − exit(1); | |
| 121 | − } | |
| 122 | − } | |
| 123 | − for (i = 0; i < tasks; i++) { | |
| 124 | − int status; | |
| 125 | − | |
| 126 | − assert(waitpid(pid[i], &status, 0) == pid[i]); | |
| 127 | − assert(status == 0); | |
| 128 | − } | |
| 129 | −} | |
| 130 | − | |
| 131 | −static int load_progs(char *filename) | |
| 132 | −{ | |
| 133 | − struct bpf_program *prog; | |
| 134 | − int err = 0; | |
| 135 | − | |
| 136 | − obj = bpf_object__open_file(filename, NULL); | |
| 137 | − err = libbpf_get_error(obj); | |
| 138 | − if (err < 0) { | |
| 139 | − fprintf(stderr, "ERROR: opening BPF object file failed\n"); | |
| 140 | − return err; | |
| 141 | − } | |
| 142 | − | |
| 143 | − /* load BPF program */ | |
| 144 | − err = bpf_object__load(obj); | |
| 145 | − if (err < 0) { | |
| 146 | − fprintf(stderr, "ERROR: loading BPF object file failed\n"); | |
| 147 | − return err; | |
| 148 | − } | |
| 149 | − | |
| 150 | − bpf_object__for_each_program(prog, obj) { | |
| 151 | − links[cnt] = bpf_program__attach(prog); | |
| 152 | − err = libbpf_get_error(links[cnt]); | |
| 153 | − if (err < 0) { | |
| 154 | − fprintf(stderr, "ERROR: bpf_program__attach failed\n"); | |
| 155 | − links[cnt] = NULL; | |
| 156 | − return err; | |
| 157 | − } | |
| 158 | − cnt++; | |
| 159 | − } | |
| 160 | − | |
| 161 | − return err; | |
| 162 | −} | |
| 163 | − | |
| 164 | −static void unload_progs(void) | |
| 165 | −{ | |
| 166 | − while (cnt) | |
| 167 | − bpf_link__destroy(links[--cnt]); | |
| 168 | − | |
| 169 | − bpf_object__close(obj); | |
| 170 | −} | |
| 171 | − | |
| 172 | −int main(int argc, char **argv) | |
| 173 | −{ | |
| 174 | − int num_cpu = sysconf(_SC_NPROCESSORS_ONLN); | |
| 175 | − int test_flags = ~0; | |
| 176 | − char filename[256]; | |
| 177 | − int err = 0; | |
| 178 | − | |
| 179 | − | |
| 180 | − if (argc > 1) | |
| 181 | − test_flags = atoi(argv[1]) ? : test_flags; | |
| 182 | − if (argc > 2) | |
| 183 | − num_cpu = atoi(argv[2]) ? : num_cpu; | |
| 184 | − | |
| 185 | − if (test_flags & 0x3) { | |
| 186 | − printf("BASE\n"); | |
| 187 | − run_perf_test(num_cpu, test_flags); | |
| 188 | − } | |
| 189 | − | |
| 190 | − if (test_flags & 0xC) { | |
| 191 | − snprintf(filename, sizeof(filename), | |
| 192 | − "%s_kprobe.bpf.o", argv[0]); | |
| 193 | − | |
| 194 | − printf("w/KPROBE\n"); | |
| 195 | − err = load_progs(filename); | |
| 196 | − if (!err) | |
| 197 | − run_perf_test(num_cpu, test_flags >> 2); | |
| 198 | − | |
| 199 | − unload_progs(); | |
| 200 | − } | |
| 201 | − | |
| 202 | − if (test_flags & 0x30) { | |
| 203 | − snprintf(filename, sizeof(filename), | |
| 204 | − "%s_tp.bpf.o", argv[0]); | |
| 205 | − printf("w/TRACEPOINT\n"); | |
| 206 | − err = load_progs(filename); | |
| 207 | − if (!err) | |
| 208 | − run_perf_test(num_cpu, test_flags >> 4); | |
| 209 | − | |
| 210 | − unload_progs(); | |
| 211 | − } | |
| 212 | − | |
| 213 | − if (test_flags & 0xC0) { | |
| 214 | − snprintf(filename, sizeof(filename), | |
| 215 | − "%s_raw_tp.bpf.o", argv[0]); | |
| 216 | − printf("w/RAW_TRACEPOINT\n"); | |
| 217 | − err = load_progs(filename); | |
| 218 | − if (!err) | |
| 219 | − run_perf_test(num_cpu, test_flags >> 6); | |
| 220 | − | |
| 221 | − unload_progs(); | |
| 222 | − } | |
| 223 | − | |
| 224 | − return err; | |
| 225 | −} | |
| @@ -1,16 +0,0 @@ | ||
| 1 | −#!/bin/bash | |
| 2 | − | |
| 3 | −rm -r tmpmnt | |
| 4 | −rm -f testfile.img | |
| 5 | −dd if=/dev/zero of=testfile.img bs=1M seek=1000 count=1 | |
| 6 | −DEVICE=$(losetup --show -f testfile.img) | |
| 7 | −mkfs.btrfs -f $DEVICE | |
| 8 | −mkdir tmpmnt | |
| 9 | −./tracex7 $DEVICE | |
| 10 | −if [ $? -eq 0 ] | |
| 11 | −then | |
| 12 | − echo "SUCCESS!" | |
| 13 | −else | |
| 14 | − echo "FAILED!" | |
| 15 | −fi | |
| 16 | −losetup -d $DEVICE | |
| @@ -1,52 +0,0 @@ | ||
| 1 | −/* Copyright (c) 2016 Sargun Dhillon <sargun@sargun.me> | |
| 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 | −#include "vmlinux.h" | |
| 8 | −#include <string.h> | |
| 9 | −#include <linux/version.h> | |
| 10 | −#include <bpf/bpf_helpers.h> | |
| 11 | −#include <bpf/bpf_tracing.h> | |
| 12 | −#include <bpf/bpf_core_read.h> | |
| 13 | − | |
| 14 | −struct { | |
| 15 | − __uint(type, BPF_MAP_TYPE_HASH); | |
| 16 | − __type(key, struct sockaddr_in); | |
| 17 | − __type(value, struct sockaddr_in); | |
| 18 | − __uint(max_entries, 256); | |
| 19 | −} dnat_map SEC(".maps"); | |
| 20 | − | |
| 21 | −/* kprobe is NOT a stable ABI | |
| 22 | − * kernel functions can be removed, renamed or completely change semantics. | |
| 23 | − * Number of arguments and their positions can change, etc. | |
| 24 | − * In such case this bpf+kprobe example will no longer be meaningful | |
| 25 | − * | |
| 26 | − * This example sits on a syscall, and the syscall ABI is relatively stable | |
| 27 | − * of course, across platforms, and over time, the ABI may change. | |
| 28 | − */ | |
| 29 | −SEC("ksyscall/connect") | |
| 30 | −int BPF_KSYSCALL(bpf_prog1, int fd, struct sockaddr_in *uservaddr, | |
| 31 | − int addrlen) | |
| 32 | −{ | |
| 33 | − struct sockaddr_in new_addr, orig_addr = {}; | |
| 34 | − struct sockaddr_in *mapped_addr; | |
| 35 | − | |
| 36 | − if (addrlen > sizeof(orig_addr)) | |
| 37 | − return 0; | |
| 38 | − | |
| 39 | − if (bpf_probe_read_user(&orig_addr, sizeof(orig_addr), uservaddr) != 0) | |
| 40 | − return 0; | |
| 41 | − | |
| 42 | − mapped_addr = bpf_map_lookup_elem(&dnat_map, &orig_addr); | |
| 43 | − if (mapped_addr != NULL) { | |
| 44 | − memcpy(&new_addr, mapped_addr, sizeof(new_addr)); | |
| 45 | − bpf_probe_write_user(uservaddr, &new_addr, | |
| 46 | − sizeof(new_addr)); | |
| 47 | − } | |
| 48 | − return 0; | |
| 49 | −} | |
| 50 | − | |
| 51 | −char _license[] SEC("license") = "GPL"; | |
| 52 | −u32 _version SEC("version") = LINUX_VERSION_CODE; | |
| @@ -1,108 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0 | |
| 2 | −#include <stdio.h> | |
| 3 | −#include <assert.h> | |
| 4 | −#include <unistd.h> | |
| 5 | −#include <bpf/bpf.h> | |
| 6 | −#include <bpf/libbpf.h> | |
| 7 | −#include <sys/socket.h> | |
| 8 | −#include <netinet/in.h> | |
| 9 | −#include <arpa/inet.h> | |
| 10 | − | |
| 11 | −int main(int ac, char **argv) | |
| 12 | −{ | |
| 13 | − struct sockaddr_in *serv_addr_in, *mapped_addr_in, *tmp_addr_in; | |
| 14 | − struct sockaddr serv_addr, mapped_addr, tmp_addr; | |
| 15 | − int serverfd, serverconnfd, clientfd, map_fd; | |
| 16 | − struct bpf_link *link = NULL; | |
| 17 | − struct bpf_program *prog; | |
| 18 | − struct bpf_object *obj; | |
| 19 | − socklen_t sockaddr_len; | |
| 20 | − char filename[256]; | |
| 21 | − char *ip; | |
| 22 | − | |
| 23 | − serv_addr_in = (struct sockaddr_in *)&serv_addr; | |
| 24 | − mapped_addr_in = (struct sockaddr_in *)&mapped_addr; | |
| 25 | − tmp_addr_in = (struct sockaddr_in *)&tmp_addr; | |
| 26 | − | |
| 27 | − snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]); | |
| 28 | − obj = bpf_object__open_file(filename, NULL); | |
| 29 | − if (libbpf_get_error(obj)) { | |
| 30 | − fprintf(stderr, "ERROR: opening BPF object file failed\n"); | |
| 31 | − return 0; | |
| 32 | − } | |
| 33 | − | |
| 34 | − prog = bpf_object__find_program_by_name(obj, "bpf_prog1"); | |
| 35 | − if (libbpf_get_error(prog)) { | |
| 36 | − fprintf(stderr, "ERROR: finding a prog in obj file failed\n"); | |
| 37 | − goto cleanup; | |
| 38 | − } | |
| 39 | − | |
| 40 | − /* load BPF program */ | |
| 41 | − if (bpf_object__load(obj)) { | |
| 42 | − fprintf(stderr, "ERROR: loading BPF object file failed\n"); | |
| 43 | − goto cleanup; | |
| 44 | − } | |
| 45 | − | |
| 46 | − map_fd = bpf_object__find_map_fd_by_name(obj, "dnat_map"); | |
| 47 | − if (map_fd < 0) { | |
| 48 | − fprintf(stderr, "ERROR: finding a map in obj file failed\n"); | |
| 49 | − goto cleanup; | |
| 50 | − } | |
| 51 | − | |
| 52 | − link = bpf_program__attach(prog); | |
| 53 | − if (libbpf_get_error(link)) { | |
| 54 | − fprintf(stderr, "ERROR: bpf_program__attach failed\n"); | |
| 55 | − link = NULL; | |
| 56 | − goto cleanup; | |
| 57 | − } | |
| 58 | − | |
| 59 | − assert((serverfd = socket(AF_INET, SOCK_STREAM, 0)) > 0); | |
| 60 | − assert((clientfd = socket(AF_INET, SOCK_STREAM, 0)) > 0); | |
| 61 | − | |
| 62 | − /* Bind server to ephemeral port on lo */ | |
| 63 | − memset(&serv_addr, 0, sizeof(serv_addr)); | |
| 64 | − serv_addr_in->sin_family = AF_INET; | |
| 65 | − serv_addr_in->sin_port = 0; | |
| 66 | − serv_addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
| 67 | − | |
| 68 | − assert(bind(serverfd, &serv_addr, sizeof(serv_addr)) == 0); | |
| 69 | − | |
| 70 | − sockaddr_len = sizeof(serv_addr); | |
| 71 | − assert(getsockname(serverfd, &serv_addr, &sockaddr_len) == 0); | |
| 72 | − ip = inet_ntoa(serv_addr_in->sin_addr); | |
| 73 | − printf("Server bound to: %s:%d\n", ip, ntohs(serv_addr_in->sin_port)); | |
| 74 | − | |
| 75 | − memset(&mapped_addr, 0, sizeof(mapped_addr)); | |
| 76 | − mapped_addr_in->sin_family = AF_INET; | |
| 77 | − mapped_addr_in->sin_port = htons(5555); | |
| 78 | − mapped_addr_in->sin_addr.s_addr = inet_addr("255.255.255.255"); | |
| 79 | − | |
| 80 | − assert(!bpf_map_update_elem(map_fd, &mapped_addr, &serv_addr, BPF_ANY)); | |
| 81 | − | |
| 82 | − assert(listen(serverfd, 5) == 0); | |
| 83 | − | |
| 84 | − ip = inet_ntoa(mapped_addr_in->sin_addr); | |
| 85 | − printf("Client connecting to: %s:%d\n", | |
| 86 | − ip, ntohs(mapped_addr_in->sin_port)); | |
| 87 | − assert(connect(clientfd, &mapped_addr, sizeof(mapped_addr)) == 0); | |
| 88 | − | |
| 89 | − sockaddr_len = sizeof(tmp_addr); | |
| 90 | − ip = inet_ntoa(tmp_addr_in->sin_addr); | |
| 91 | − assert((serverconnfd = accept(serverfd, &tmp_addr, &sockaddr_len)) > 0); | |
| 92 | − printf("Server received connection from: %s:%d\n", | |
| 93 | − ip, ntohs(tmp_addr_in->sin_port)); | |
| 94 | − | |
| 95 | − sockaddr_len = sizeof(tmp_addr); | |
| 96 | − assert(getpeername(clientfd, &tmp_addr, &sockaddr_len) == 0); | |
| 97 | − ip = inet_ntoa(tmp_addr_in->sin_addr); | |
| 98 | − printf("Client's peer address: %s:%d\n", | |
| 99 | − ip, ntohs(tmp_addr_in->sin_port)); | |
| 100 | − | |
| 101 | − /* Is the server's getsockname = the socket getpeername */ | |
| 102 | − assert(memcmp(&serv_addr, &tmp_addr, sizeof(struct sockaddr_in)) == 0); | |
| 103 | − | |
| 104 | −cleanup: | |
| 105 | − bpf_link__destroy(link); | |
| 106 | − bpf_object__close(obj); | |
| 107 | − return 0; | |
| 108 | −} | |
| @@ -1,15 +0,0 @@ | ||
| 1 | −#include "vmlinux.h" | |
| 2 | −#include <linux/version.h> | |
| 3 | −#include <bpf/bpf_helpers.h> | |
| 4 | − | |
| 5 | −SEC("kprobe/open_ctree") | |
| 6 | −int bpf_prog1(struct pt_regs *ctx) | |
| 7 | −{ | |
| 8 | − unsigned long rc = -12; | |
| 9 | − | |
| 10 | − bpf_override_return(ctx, rc); | |
| 11 | − return 0; | |
| 12 | −} | |
| 13 | − | |
| 14 | −char _license[] SEC("license") = "GPL"; | |
| 15 | −u32 _version SEC("version") = LINUX_VERSION_CODE; | |
| @@ -1,56 +0,0 @@ | ||
| 1 | −#define _GNU_SOURCE | |
| 2 | − | |
| 3 | −#include <stdio.h> | |
| 4 | −#include <unistd.h> | |
| 5 | −#include <bpf/libbpf.h> | |
| 6 | − | |
| 7 | −int main(int argc, char **argv) | |
| 8 | −{ | |
| 9 | − struct bpf_link *link = NULL; | |
| 10 | − struct bpf_program *prog; | |
| 11 | − struct bpf_object *obj; | |
| 12 | − char filename[256]; | |
| 13 | − char command[256]; | |
| 14 | − int ret = 0; | |
| 15 | − FILE *f; | |
| 16 | − | |
| 17 | − if (!argv[1]) { | |
| 18 | − fprintf(stderr, "ERROR: Run with the btrfs device argument!\n"); | |
| 19 | − return 0; | |
| 20 | − } | |
| 21 | − | |
| 22 | − snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]); | |
| 23 | − obj = bpf_object__open_file(filename, NULL); | |
| 24 | − if (libbpf_get_error(obj)) { | |
| 25 | − fprintf(stderr, "ERROR: opening BPF object file failed\n"); | |
| 26 | − return 0; | |
| 27 | − } | |
| 28 | − | |
| 29 | − prog = bpf_object__find_program_by_name(obj, "bpf_prog1"); | |
| 30 | − if (!prog) { | |
| 31 | − fprintf(stderr, "ERROR: finding a prog in obj file failed\n"); | |
| 32 | − goto cleanup; | |
| 33 | − } | |
| 34 | − | |
| 35 | − /* load BPF program */ | |
| 36 | − if (bpf_object__load(obj)) { | |
| 37 | − fprintf(stderr, "ERROR: loading BPF object file failed\n"); | |
| 38 | − goto cleanup; | |
| 39 | − } | |
| 40 | − | |
| 41 | − link = bpf_program__attach(prog); | |
| 42 | − if (libbpf_get_error(link)) { | |
| 43 | − fprintf(stderr, "ERROR: bpf_program__attach failed\n"); | |
| 44 | − link = NULL; | |
| 45 | − goto cleanup; | |
| 46 | − } | |
| 47 | − | |
| 48 | − snprintf(command, 256, "mount %s tmpmnt/", argv[1]); | |
| 49 | − f = popen(command, "r"); | |
| 50 | − ret = pclose(f); | |
| 51 | − | |
| 52 | −cleanup: | |
| 53 | − bpf_link__destroy(link); | |
| 54 | − bpf_object__close(obj); | |
| 55 | − return ret ? 0 : 1; | |
| 56 | −} | |
| @@ -32,7 +32,7 @@ SEC("xdp_mark") · 31 unmodified lines | ||
| 32 | 32 | int _xdp_mark(struct xdp_md *ctx) |
| 33 | 33 | { |
| 34 | 34 | struct meta_info *meta; |
| 35 | − void *data, *data_end; | |
| 35 | + void *data; | |
| 36 | 36 | int ret; |
| 37 | 37 | |
| 38 | 38 | /* Reserve space in-front of data pointer for our meta info. |
| @@ -57,6 +57,7 @@ static __always_inline void swap_mac(void *data, struct ethhdr *orig_eth) · 56 unmodified lines | ||
| 57 | 57 | |
| 58 | 58 | static __always_inline __u16 csum_fold_helper(__u32 csum) |
| 59 | 59 | { |
| 60 | + csum = (csum & 0xffff) + (csum >> 16); | |
| 60 | 61 | return ~((csum & 0xffff) + (csum >> 16)); |
| 61 | 62 | } |
| 62 | 63 | |
| @@ -3,6 +3,8 @@ · 2 unmodified lines | ||
| 3 | 3 | pahole-ver := $(CONFIG_PAHOLE_VERSION) |
| 4 | 4 | pahole-flags-y := |
| 5 | 5 | |
| 6 | +JOBS := $(patsubst -j%,%,$(filter -j%,$(MAKEFLAGS))) | |
| 7 | + | |
| 6 | 8 | ifeq ($(call test-le, $(pahole-ver), 125),y) |
| 7 | 9 | |
| 8 | 10 | # pahole 1.18 through 1.21 can't handle zero-sized per-CPU vars |
| @@ -12,14 +14,14 @@ endif · 3 unmodified lines | ||
| 12 | 14 | |
| 13 | 15 | pahole-flags-$(call test-ge, $(pahole-ver), 121) += --btf_gen_floats |
| 14 | 16 | |
| 15 | −pahole-flags-$(call test-ge, $(pahole-ver), 122) += -j | |
| 17 | +pahole-flags-$(call test-ge, $(pahole-ver), 122) += -j$(JOBS) | |
| 16 | 18 | |
| 17 | 19 | pahole-flags-$(call test-ge, $(pahole-ver), 125) += --skip_encoding_btf_inconsistent_proto --btf_gen_optimized |
| 18 | 20 | |
| 19 | 21 | else |
| 20 | 22 | |
| 21 | 23 | # Switch to using --btf_features for v1.26 and later. |
| 22 | −pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func,decl_tag_kfuncs | |
| 24 | +pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j$(JOBS) --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func,decl_tag_kfuncs | |
| 23 | 25 | |
| 24 | 26 | ifneq ($(KBUILD_EXTMOD),) |
| 25 | 27 | module-pahole-flags-$(call test-ge, $(pahole-ver), 126) += --btf_features=distilled_base |
| @@ -37,10 +37,11 @@ class APIElement(object): · 36 unmodified lines | ||
| 37 | 37 | @desc: textual description of the symbol |
| 38 | 38 | @ret: (optional) description of any associated return value |
| 39 | 39 | """ |
| 40 | − def __init__(self, proto='', desc='', ret=''): | |
| 40 | + def __init__(self, proto='', desc='', ret='', attrs=[]): | |
| 41 | 41 | self.proto = proto |
| 42 | 42 | self.desc = desc |
| 43 | 43 | self.ret = ret |
| 44 | + self.attrs = attrs | |
| 44 | 45 | |
| 45 | 46 | |
| 46 | 47 | class Helper(APIElement): |
| @@ -81,6 +82,11 @@ class Helper(APIElement): · 34 unmodified lines | ||
| 81 | 82 | return res |
| 82 | 83 | |
| 83 | 84 | |
| 85 | +ATTRS = { | |
| 86 | + '__bpf_fastcall': 'bpf_fastcall' | |
| 87 | +} | |
| 88 | + | |
| 89 | + | |
| 84 | 90 | class HeaderParser(object): |
| 85 | 91 | """ |
| 86 | 92 | An object used to parse a file in order to extract the documentation of a |
| @@ -111,7 +117,8 @@ class HeaderParser(object): · 24 unmodified lines | ||
| 111 | 117 | proto = self.parse_proto() |
| 112 | 118 | desc = self.parse_desc(proto) |
| 113 | 119 | ret = self.parse_ret(proto) |
| 114 | − return Helper(proto=proto, desc=desc, ret=ret) | |
| 120 | + attrs = self.parse_attrs(proto) | |
| 121 | + return Helper(proto=proto, desc=desc, ret=ret, attrs=attrs) | |
| 115 | 122 | |
| 116 | 123 | def parse_symbol(self): |
| 117 | 124 | p = re.compile(r' \* ?(BPF\w+)$') |
| @@ -192,6 +199,28 @@ class HeaderParser(object): · 74 unmodified lines | ||
| 192 | 199 | raise Exception("No return found for " + proto) |
| 193 | 200 | return ret |
| 194 | 201 | |
| 202 | + def parse_attrs(self, proto): | |
| 203 | + p = re.compile(r' \* ?(?:\t| {5,8})Attributes$') | |
| 204 | + capture = p.match(self.line) | |
| 205 | + if not capture: | |
| 206 | + return [] | |
| 207 | + # Expect a single line with mnemonics for attributes separated by spaces | |
| 208 | + self.line = self.reader.readline() | |
| 209 | + p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') | |
| 210 | + capture = p.match(self.line) | |
| 211 | + if not capture: | |
| 212 | + raise Exception("Incomplete 'Attributes' section for " + proto) | |
| 213 | + attrs = capture.group(1).split(' ') | |
| 214 | + for attr in attrs: | |
| 215 | + if attr not in ATTRS: | |
| 216 | + raise Exception("Unexpected attribute '" + attr + "' specified for " + proto) | |
| 217 | + self.line = self.reader.readline() | |
| 218 | + if self.line != ' *\n': | |
| 219 | + raise Exception("Expecting empty line after 'Attributes' section for " + proto) | |
| 220 | + # Prepare a line for next self.parse_* to consume | |
| 221 | + self.line = self.reader.readline() | |
| 222 | + return attrs | |
| 223 | + | |
| 195 | 224 | def seek_to(self, target, help_message, discard_lines = 1): |
| 196 | 225 | self.reader.seek(0) |
| 197 | 226 | offset = self.reader.read().find(target) |
| @@ -789,6 +818,21 @@ class PrinterHelpers(Printer): · 591 unmodified lines | ||
| 789 | 818 | print('%s;' % fwd) |
| 790 | 819 | print('') |
| 791 | 820 | |
| 821 | + used_attrs = set() | |
| 822 | + for helper in self.elements: | |
| 823 | + for attr in helper.attrs: | |
| 824 | + used_attrs.add(attr) | |
| 825 | + for attr in sorted(used_attrs): | |
| 826 | + print('#ifndef %s' % attr) | |
| 827 | + print('#if __has_attribute(%s)' % ATTRS[attr]) | |
| 828 | + print('#define %s __attribute__((%s))' % (attr, ATTRS[attr])) | |
| 829 | + print('#else') | |
| 830 | + print('#define %s' % attr) | |
| 831 | + print('#endif') | |
| 832 | + print('#endif') | |
| 833 | + if used_attrs: | |
| 834 | + print('') | |
| 835 | + | |
| 792 | 836 | def print_footer(self): |
| 793 | 837 | footer = '' |
| 794 | 838 | print(footer) |
| @@ -827,7 +871,10 @@ class PrinterHelpers(Printer): · 32 unmodified lines | ||
| 827 | 871 | print(' *{}{}'.format(' \t' if line else '', line)) |
| 828 | 872 | |
| 829 | 873 | print(' */') |
| 830 | − print('static %s %s(* const %s)(' % (self.map_type(proto['ret_type']), | |
| 874 | + print('static ', end='') | |
| 875 | + if helper.attrs: | |
| 876 | + print('%s ' % (" ".join(helper.attrs)), end='') | |
| 877 | + print('%s %s(* const %s)(' % (self.map_type(proto['ret_type']), | |
| 831 | 878 | proto['ret_star'], proto['name']), end='') |
| 832 | 879 | comma = '' |
| 833 | 880 | for i, a in enumerate(proto['args']): |
| @@ -210,7 +210,7 @@ static uint8_t *get_last_jit_image(char *haystack, size_t hlen, · 209 unmodified lines | ||
| 210 | 210 | return NULL; |
| 211 | 211 | } |
| 212 | 212 | if (proglen > 1000000) { |
| 213 | − printf("proglen of %d too big, stopping\n", proglen); | |
| 213 | + printf("proglen of %u too big, stopping\n", proglen); | |
| 214 | 214 | return NULL; |
| 215 | 215 | } |
| 216 | 216 | |
| @@ -147,7 +147,11 @@ ifeq ($(feature-llvm),1) · 146 unmodified lines | ||
| 147 | 147 | # If LLVM is available, use it for JIT disassembly |
| 148 | 148 | CFLAGS += -DHAVE_LLVM_SUPPORT |
| 149 | 149 | LLVM_CONFIG_LIB_COMPONENTS := mcdisassembler all-targets |
| 150 | − CFLAGS += $(shell $(LLVM_CONFIG) --cflags) | |
| 150 | + # llvm-config always adds -D_GNU_SOURCE, however, it may already be in CFLAGS | |
| 151 | + # (e.g. when bpftool build is called from selftests build as selftests | |
| 152 | + # Makefile includes lib.mk which sets -D_GNU_SOURCE) which would cause | |
| 153 | + # compilation error due to redefinition. Let's filter it out here. | |
| 154 | + CFLAGS += $(filter-out -D_GNU_SOURCE,$(shell $(LLVM_CONFIG) --cflags)) | |
| 151 | 155 | LIBS += $(shell $(LLVM_CONFIG) --libs $(LLVM_CONFIG_LIB_COMPONENTS)) |
| 152 | 156 | ifeq ($(shell $(LLVM_CONFIG) --shared-mode),static) |
| 153 | 157 | LIBS += $(shell $(LLVM_CONFIG) --system-libs $(LLVM_CONFIG_LIB_COMPONENTS)) |
| @@ -1,11 +1,15 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
| 2 | 2 | /* Copyright (C) 2019 Facebook */ |
| 3 | 3 | |
| 4 | +#ifndef _GNU_SOURCE | |
| 5 | +#define _GNU_SOURCE | |
| 6 | +#endif | |
| 4 | 7 | #include <errno.h> |
| 5 | 8 | #include <fcntl.h> |
| 6 | 9 | #include <linux/err.h> |
| 7 | 10 | #include <stdbool.h> |
| 8 | 11 | #include <stdio.h> |
| 12 | +#include <stdlib.h> | |
| 9 | 13 | #include <string.h> |
| 10 | 14 | #include <unistd.h> |
| 11 | 15 | #include <linux/btf.h> |
| @@ -21,6 +25,7 @@ · 9 unmodified lines | ||
| 21 | 25 | #include "main.h" |
| 22 | 26 | |
| 23 | 27 | #define KFUNC_DECL_TAG "bpf_kfunc" |
| 28 | +#define FASTCALL_DECL_TAG "bpf_fastcall" | |
| 24 | 29 | |
| 25 | 30 | static const char * const btf_kind_str[NR_BTF_KINDS] = { |
| 26 | 31 | [BTF_KIND_UNKN] = "UNKNOWN", |
| @@ -284,7 +289,7 @@ static int dump_btf_type(const struct btf *btf, __u32 id, · 257 unmodified lines | ||
| 284 | 289 | } else { |
| 285 | 290 | if (btf_kflag(t)) |
| 286 | 291 | printf("\n\t'%s' val=%lldLL", name, |
| 287 | − (unsigned long long)val); | |
| 292 | + (long long)val); | |
| 288 | 293 | else |
| 289 | 294 | printf("\n\t'%s' val=%lluULL", name, |
| 290 | 295 | (unsigned long long)val); |
| @@ -464,19 +469,59 @@ static int dump_btf_raw(const struct btf *btf, · 173 unmodified lines | ||
| 464 | 469 | return 0; |
| 465 | 470 | } |
| 466 | 471 | |
| 472 | +struct ptr_array { | |
| 473 | + __u32 cnt; | |
| 474 | + __u32 cap; | |
| 475 | + const void **elems; | |
| 476 | +}; | |
| 477 | + | |
| 478 | +static int ptr_array_push(const void *ptr, struct ptr_array *arr) | |
| 479 | +{ | |
| 480 | + __u32 new_cap; | |
| 481 | + void *tmp; | |
| 482 | + | |
| 483 | + if (arr->cnt == arr->cap) { | |
| 484 | + new_cap = (arr->cap ?: 16) * 2; | |
| 485 | + tmp = realloc(arr->elems, sizeof(*arr->elems) * new_cap); | |
| 486 | + if (!tmp) | |
| 487 | + return -ENOMEM; | |
| 488 | + arr->elems = tmp; | |
| 489 | + arr->cap = new_cap; | |
| 490 | + } | |
| 491 | + arr->elems[arr->cnt++] = ptr; | |
| 492 | + return 0; | |
| 493 | +} | |
| 494 | + | |
| 495 | +static void ptr_array_free(struct ptr_array *arr) | |
| 496 | +{ | |
| 497 | + free(arr->elems); | |
| 498 | +} | |
| 499 | + | |
| 500 | +static int cmp_kfuncs(const void *pa, const void *pb, void *ctx) | |
| 501 | +{ | |
| 502 | + struct btf *btf = ctx; | |
| 503 | + const struct btf_type *a = *(void **)pa; | |
| 504 | + const struct btf_type *b = *(void **)pb; | |
| 505 | + | |
| 506 | + return strcmp(btf__str_by_offset(btf, a->name_off), | |
| 507 | + btf__str_by_offset(btf, b->name_off)); | |
| 508 | +} | |
| 509 | + | |
| 467 | 510 | static int dump_btf_kfuncs(struct btf_dump *d, const struct btf *btf) |
| 468 | 511 | { |
| 469 | 512 | LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts); |
| 470 | − int cnt = btf__type_cnt(btf); | |
| 471 | − int i; | |
| 513 | + __u32 cnt = btf__type_cnt(btf), i, j; | |
| 514 | + struct ptr_array fastcalls = {}; | |
| 515 | + struct ptr_array kfuncs = {}; | |
| 516 | + int err = 0; | |
| 472 | 517 | |
| 473 | 518 | printf("\n/* BPF kfuncs */\n"); |
| 474 | 519 | printf("#ifndef BPF_NO_KFUNC_PROTOTYPES\n"); |
| 475 | 520 | |
| 476 | 521 | for (i = 1; i < cnt; i++) { |
| 477 | 522 | const struct btf_type *t = btf__type_by_id(btf, i); |
| 523 | + const struct btf_type *ft; | |
| 478 | 524 | const char *name; |
| 479 | − int err; | |
| 480 | 525 | |
| 481 | 526 | if (!btf_is_decl_tag(t)) |
| 482 | 527 | continue; |
| @@ -484,27 +529,53 @@ static int dump_btf_kfuncs(struct btf_dump *d, const struct btf *btf) · 1 unmodified line | ||
| 484 | 529 | if (btf_decl_tag(t)->component_idx != -1) |
| 485 | 530 | continue; |
| 486 | 531 | |
| 487 | − name = btf__name_by_offset(btf, t->name_off); | |
| 488 | − if (strncmp(name, KFUNC_DECL_TAG, sizeof(KFUNC_DECL_TAG))) | |
| 532 | + ft = btf__type_by_id(btf, t->type); | |
| 533 | + if (!btf_is_func(ft)) | |
| 489 | 534 | continue; |
| 490 | 535 | |
| 491 | − t = btf__type_by_id(btf, t->type); | |
| 492 | − if (!btf_is_func(t)) | |
| 493 | − continue; | |
| 536 | + name = btf__name_by_offset(btf, t->name_off); | |
| 537 | + if (strncmp(name, KFUNC_DECL_TAG, sizeof(KFUNC_DECL_TAG)) == 0) { | |
| 538 | + err = ptr_array_push(ft, &kfuncs); | |
| 539 | + if (err) | |
| 540 | + goto out; | |
| 541 | + } | |
| 542 | + | |
| 543 | + if (strncmp(name, FASTCALL_DECL_TAG, sizeof(FASTCALL_DECL_TAG)) == 0) { | |
| 544 | + err = ptr_array_push(ft, &fastcalls); | |
| 545 | + if (err) | |
| 546 | + goto out; | |
| 547 | + } | |
| 548 | + } | |
| 549 | + | |
| 550 | + /* Sort kfuncs by name for improved vmlinux.h stability */ | |
| 551 | + qsort_r(kfuncs.elems, kfuncs.cnt, sizeof(*kfuncs.elems), cmp_kfuncs, (void *)btf); | |
| 552 | + for (i = 0; i < kfuncs.cnt; i++) { | |
| 553 | + const struct btf_type *t = kfuncs.elems[i]; | |
| 494 | 554 | |
| 495 | 555 | printf("extern "); |
| 496 | 556 | |
| 557 | + /* Assume small amount of fastcall kfuncs */ | |
| 558 | + for (j = 0; j < fastcalls.cnt; j++) { | |
| 559 | + if (fastcalls.elems[j] == t) { | |
| 560 | + printf("__bpf_fastcall "); | |
| 561 | + break; | |
| 562 | + } | |
| 563 | + } | |
| 564 | + | |
| 497 | 565 | opts.field_name = btf__name_by_offset(btf, t->name_off); |
| 498 | 566 | err = btf_dump__emit_type_decl(d, t->type, &opts); |
| 499 | 567 | if (err) |
| 500 | − return err; | |
| 568 | + goto out; | |
| 501 | 569 | |
| 502 | 570 | printf(" __weak __ksym;\n"); |
| 503 | 571 | } |
| 504 | 572 | |
| 505 | 573 | printf("#endif\n\n"); |
| 506 | 574 | |
| 507 | − return 0; | |
| 575 | +out: | |
| 576 | + ptr_array_free(&fastcalls); | |
| 577 | + ptr_array_free(&kfuncs); | |
| 578 | + return err; | |
| 508 | 579 | } |
| 509 | 580 | |
| 510 | 581 | static void __printf(2, 0) btf_dump_printf(void *ctx, |
| @@ -718,6 +789,13 @@ static int dump_btf_c(const struct btf *btf, · 207 unmodified lines | ||
| 718 | 789 | printf("#ifndef __weak\n"); |
| 719 | 790 | printf("#define __weak __attribute__((weak))\n"); |
| 720 | 791 | printf("#endif\n\n"); |
| 792 | + printf("#ifndef __bpf_fastcall\n"); | |
| 793 | + printf("#if __has_attribute(bpf_fastcall)\n"); | |
| 794 | + printf("#define __bpf_fastcall __attribute__((bpf_fastcall))\n"); | |
| 795 | + printf("#else\n"); | |
| 796 | + printf("#define __bpf_fastcall\n"); | |
| 797 | + printf("#endif\n"); | |
| 798 | + printf("#endif\n\n"); | |
| 721 | 799 | |
| 722 | 800 | if (root_type_cnt) { |
| 723 | 801 | for (i = 0; i < root_type_cnt; i++) { |
| @@ -80,7 +80,8 @@ symbol_lookup_callback(__maybe_unused void *disasm_info, · 79 unmodified lines | ||
| 80 | 80 | static int |
| 81 | 81 | init_context(disasm_ctx_t *ctx, const char *arch, |
| 82 | 82 | __maybe_unused const char *disassembler_options, |
| 83 | − __maybe_unused unsigned char *image, __maybe_unused ssize_t len) | |
| 83 | + __maybe_unused unsigned char *image, __maybe_unused ssize_t len, | |
| 84 | + __maybe_unused __u64 func_ksym) | |
| 84 | 85 | { |
| 85 | 86 | char *triple; |
| 86 | 87 | |
| @@ -109,12 +110,13 @@ static void destroy_context(disasm_ctx_t *ctx) · 22 unmodified lines | ||
| 109 | 110 | } |
| 110 | 111 | |
| 111 | 112 | static int |
| 112 | −disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc) | |
| 113 | +disassemble_insn(disasm_ctx_t *ctx, unsigned char *image, ssize_t len, int pc, | |
| 114 | + __u64 func_ksym) | |
| 113 | 115 | { |
| 114 | 116 | char buf[256]; |
| 115 | 117 | int count; |
| 116 | 118 | |
| 117 | − count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, pc, | |
| 119 | + count = LLVMDisasmInstruction(*ctx, image + pc, len - pc, func_ksym + pc, | |
| 118 | 120 | buf, sizeof(buf)); |
| 119 | 121 | if (json_output) |
| 120 | 122 | printf_json(buf); |
| @@ -136,8 +138,21 @@ int disasm_init(void) · 15 unmodified lines | ||
| 136 | 138 | #ifdef HAVE_LIBBFD_SUPPORT |
| 137 | 139 | #define DISASM_SPACER "\t" |
| 138 | 140 | |
| 141 | +struct disasm_info { | |
| 142 | + struct disassemble_info info; | |
| 143 | + __u64 func_ksym; | |
| 144 | +}; | |
| 145 | + | |
| 146 | +static void disasm_print_addr(bfd_vma addr, struct disassemble_info *info) | |
| 147 | +{ | |
| 148 | + struct disasm_info *dinfo = container_of(info, struct disasm_info, info); | |
| 149 | + | |
| 150 | + addr += dinfo->func_ksym; | |
| 151 | + generic_print_address(addr, info); | |
| 152 | +} | |
| 153 | + | |
| 139 | 154 | typedef struct { |
| 140 | − struct disassemble_info *info; | |
| 155 | + struct disasm_info *info; | |
| 141 | 156 | disassembler_ftype disassemble; |
| 142 | 157 | bfd *bfdf; |
| 143 | 158 | } disasm_ctx_t; |
| @@ -215,7 +230,7 @@ static int fprintf_json_styled(void *out, · 71 unmodified lines | ||
| 215 | 230 | |
| 216 | 231 | static int init_context(disasm_ctx_t *ctx, const char *arch, |
| 217 | 232 | const char *disassembler_options, |
| 218 | − unsigned char *image, ssize_t len) | |
| 233 | + unsigned char *image, ssize_t len, __u64 func_ksym) | |
| 219 | 234 | { |
| 220 | 235 | struct disassemble_info *info; |
| 221 | 236 | char tpath[PATH_MAX]; |
| @@ -238,12 +253,13 @@ static int init_context(disasm_ctx_t *ctx, const char *arch, · 16 unmodified lines | ||
| 238 | 253 | } |
| 239 | 254 | bfdf = ctx->bfdf; |
| 240 | 255 | |
| 241 | − ctx->info = malloc(sizeof(struct disassemble_info)); | |
| 256 | + ctx->info = malloc(sizeof(struct disasm_info)); | |
| 242 | 257 | if (!ctx->info) { |
| 243 | 258 | p_err("mem alloc failed"); |
| 244 | 259 | goto err_close; |
| 245 | 260 | } |
| 246 | − info = ctx->info; | |
| 261 | + ctx->info->func_ksym = func_ksym; | |
| 262 | + info = &ctx->info->info; | |
| 247 | 263 | |
| 248 | 264 | if (json_output) |
| 249 | 265 | init_disassemble_info_compat(info, stdout, |
| @@ -272,6 +288,7 @@ static int init_context(disasm_ctx_t *ctx, const char *arch, · 22 unmodified lines | ||
| 272 | 288 | info->disassembler_options = disassembler_options; |
| 273 | 289 | info->buffer = image; |
| 274 | 290 | info->buffer_length = len; |
| 291 | + info->print_address_func = disasm_print_addr; | |
| 275 | 292 | |
| 276 | 293 | disassemble_init_for_target(info); |
| 277 | 294 | |
| @@ -304,9 +321,10 @@ static void destroy_context(disasm_ctx_t *ctx) · 26 unmodified lines | ||
| 304 | 321 | |
| 305 | 322 | static int |
| 306 | 323 | disassemble_insn(disasm_ctx_t *ctx, __maybe_unused unsigned char *image, |
| 307 | − __maybe_unused ssize_t len, int pc) | |
| 324 | + __maybe_unused ssize_t len, int pc, | |
| 325 | + __maybe_unused __u64 func_ksym) | |
| 308 | 326 | { |
| 309 | − return ctx->disassemble(pc, ctx->info); | |
| 327 | + return ctx->disassemble(pc, &ctx->info->info); | |
| 310 | 328 | } |
| 311 | 329 | |
| 312 | 330 | int disasm_init(void) |
| @@ -331,7 +349,7 @@ int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, · 18 unmodified lines | ||
| 331 | 349 | if (!len) |
| 332 | 350 | return -1; |
| 333 | 351 | |
| 334 | − if (init_context(&ctx, arch, disassembler_options, image, len)) | |
| 352 | + if (init_context(&ctx, arch, disassembler_options, image, len, func_ksym)) | |
| 335 | 353 | return -1; |
| 336 | 354 | |
| 337 | 355 | if (json_output) |
| @@ -360,7 +378,7 @@ int disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, · 22 unmodified lines | ||
| 360 | 378 | printf("%4x:" DISASM_SPACER, pc); |
| 361 | 379 | } |
| 362 | 380 | |
| 363 | − count = disassemble_insn(&ctx, image, len, pc); | |
| 381 | + count = disassemble_insn(&ctx, image, len, pc, func_ksym); | |
| 364 | 382 | |
| 365 | 383 | if (json_output) { |
| 366 | 384 | /* Operand array, was started in fprintf_json. Before |
| @@ -679,8 +679,8 @@ static int sets_patch(struct object *obj) · 678 unmodified lines | ||
| 679 | 679 | |
| 680 | 680 | next = rb_first(&obj->sets); |
| 681 | 681 | while (next) { |
| 682 | − struct btf_id_set8 *set8; | |
| 683 | − struct btf_id_set *set; | |
| 682 | + struct btf_id_set8 *set8 = NULL; | |
| 683 | + struct btf_id_set *set = NULL; | |
| 684 | 684 | unsigned long addr, off; |
| 685 | 685 | struct btf_id *id; |
| 686 | 686 | |
| @@ -70,7 +70,6 @@ int handle__sched_switch(u64 *ctx) · 69 unmodified lines | ||
| 70 | 70 | struct task_struct *next = (struct task_struct *)ctx[2]; |
| 71 | 71 | struct runq_event event = {}; |
| 72 | 72 | u64 *tsp, delta_us; |
| 73 | − long state; | |
| 74 | 73 | u32 pid; |
| 75 | 74 | |
| 76 | 75 | /* ivcsw: treat like an enqueue event and store timestamp */ |
| @@ -1116,6 +1116,7 @@ enum bpf_attach_type { · 1115 unmodified lines | ||
| 1116 | 1116 | BPF_NETKIT_PRIMARY, |
| 1117 | 1117 | BPF_NETKIT_PEER, |
| 1118 | 1118 | BPF_TRACE_KPROBE_SESSION, |
| 1119 | + BPF_TRACE_UPROBE_SESSION, | |
| 1119 | 1120 | __MAX_BPF_ATTACH_TYPE |
| 1120 | 1121 | }; |
| 1121 | 1122 | |
| @@ -1973,6 +1974,8 @@ union bpf_attr { · 851 unmodified lines | ||
| 1973 | 1974 | * program. |
| 1974 | 1975 | * Return |
| 1975 | 1976 | * The SMP id of the processor running the program. |
| 1977 | + * Attributes | |
| 1978 | + * __bpf_fastcall | |
| 1976 | 1979 | * |
| 1977 | 1980 | * long bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags) |
| 1978 | 1981 | * Description |
| @@ -3104,10 +3107,6 @@ union bpf_attr { · 1125 unmodified lines | ||
| 3104 | 3107 | * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration |
| 3105 | 3108 | * option, and in this case it only works on functions tagged with |
| 3106 | 3109 | * **ALLOW_ERROR_INJECTION** in the kernel code. |
| 3107 | − * | |
| 3108 | − * Also, the helper is only available for the architectures having | |
| 3109 | − * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing, | |
| 3110 | − * x86 architecture is the only one to support this feature. | |
| 3111 | 3110 | * Return |
| 3112 | 3111 | * 0 |
| 3113 | 3112 | * |
| @@ -5372,7 +5371,7 @@ union bpf_attr { · 2258 unmodified lines | ||
| 5372 | 5371 | * Currently, the **flags** must be 0. Currently, nr_loops is |
| 5373 | 5372 | * limited to 1 << 23 (~8 million) loops. |
| 5374 | 5373 | * |
| 5375 | − * long (\*callback_fn)(u32 index, void \*ctx); | |
| 5374 | + * long (\*callback_fn)(u64 index, void \*ctx); | |
| 5376 | 5375 | * |
| 5377 | 5376 | * where **index** is the current index in the loop. The index |
| 5378 | 5377 | * is zero-indexed. |
| @@ -61,7 +61,8 @@ ifndef VERBOSE · 60 unmodified lines | ||
| 61 | 61 | endif |
| 62 | 62 | |
| 63 | 63 | INCLUDES = -I$(or $(OUTPUT),.) \ |
| 64 | − -I$(srctree)/tools/include -I$(srctree)/tools/include/uapi | |
| 64 | + -I$(srctree)/tools/include -I$(srctree)/tools/include/uapi \ | |
| 65 | + -I$(srctree)/tools/arch/$(SRCARCH)/include | |
| 65 | 66 | |
| 66 | 67 | export prefix libdir src obj |
| 67 | 68 | |
| @@ -776,6 +776,7 @@ int bpf_link_create(int prog_fd, int target_fd, · 775 unmodified lines | ||
| 776 | 776 | return libbpf_err(-EINVAL); |
| 777 | 777 | break; |
| 778 | 778 | case BPF_TRACE_UPROBE_MULTI: |
| 779 | + case BPF_TRACE_UPROBE_SESSION: | |
| 779 | 780 | attr.link_create.uprobe_multi.flags = OPTS_GET(opts, uprobe_multi.flags, 0); |
| 780 | 781 | attr.link_create.uprobe_multi.cnt = OPTS_GET(opts, uprobe_multi.cnt, 0); |
| 781 | 782 | attr.link_create.uprobe_multi.path = ptr_to_u64(OPTS_GET(opts, uprobe_multi.path, 0)); |
| @@ -34,6 +34,7 @@ struct bpf_gen { · 33 unmodified lines | ||
| 34 | 34 | void *data_cur; |
| 35 | 35 | void *insn_start; |
| 36 | 36 | void *insn_cur; |
| 37 | + bool swapped_endian; | |
| 37 | 38 | ssize_t cleanup_label; |
| 38 | 39 | __u32 nr_progs; |
| 39 | 40 | __u32 nr_maps; |
| @@ -185,6 +185,7 @@ enum libbpf_tristate { · 184 unmodified lines | ||
| 185 | 185 | #define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted"))) |
| 186 | 186 | #define __kptr __attribute__((btf_type_tag("kptr"))) |
| 187 | 187 | #define __percpu_kptr __attribute__((btf_type_tag("percpu_kptr"))) |
| 188 | +#define __uptr __attribute__((btf_type_tag("uptr"))) | |
| 188 | 189 | |
| 189 | 190 | #if defined (__clang__) |
| 190 | 191 | #define bpf_ksym_exists(sym) ({ \ |
| @@ -22,6 +22,7 @@ · 21 unmodified lines | ||
| 22 | 22 | #include "libbpf_internal.h" |
| 23 | 23 | #include "hashmap.h" |
| 24 | 24 | #include "strset.h" |
| 25 | +#include "str_error.h" | |
| 25 | 26 | |
| 26 | 27 | #define BTF_MAX_NR_TYPES 0x7fffffffU |
| 27 | 28 | #define BTF_MAX_STR_OFFSET 0x7fffffffU |
| @@ -1179,7 +1180,7 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf, · 1151 unmodified lines | ||
| 1179 | 1180 | fd = open(path, O_RDONLY | O_CLOEXEC); |
| 1180 | 1181 | if (fd < 0) { |
| 1181 | 1182 | err = -errno; |
| 1182 | − pr_warn("failed to open %s: %s\n", path, strerror(errno)); | |
| 1183 | + pr_warn("failed to open %s: %s\n", path, errstr(err)); | |
| 1183 | 1184 | return ERR_PTR(err); |
| 1184 | 1185 | } |
| 1185 | 1186 | |
| @@ -1445,7 +1446,7 @@ retry_load: · 259 unmodified lines | ||
| 1445 | 1446 | goto retry_load; |
| 1446 | 1447 | |
| 1447 | 1448 | err = -errno; |
| 1448 | − pr_warn("BTF loading error: %d\n", err); | |
| 1449 | + pr_warn("BTF loading error: %s\n", errstr(err)); | |
| 1449 | 1450 | /* don't print out contents of custom log_buf */ |
| 1450 | 1451 | if (!log_buf && buf[0]) |
| 1451 | 1452 | pr_warn("-- BEGIN BTF LOAD LOG ---\n%s\n-- END BTF LOAD LOG --\n", buf); |
| @@ -2885,7 +2886,7 @@ int btf__add_decl_tag(struct btf *btf, const char *value, int ref_type_id, · 1433 unmodified lines | ||
| 2885 | 2886 | return btf_commit_type(btf, sz); |
| 2886 | 2887 | } |
| 2887 | 2888 | |
| 2888 | −struct btf_ext_sec_setup_param { | |
| 2889 | +struct btf_ext_sec_info_param { | |
| 2889 | 2890 | __u32 off; |
| 2890 | 2891 | __u32 len; |
| 2891 | 2892 | __u32 min_rec_size; |
| @@ -2893,14 +2894,20 @@ struct btf_ext_sec_setup_param { · 1 unmodified line | ||
| 2893 | 2894 | const char *desc; |
| 2894 | 2895 | }; |
| 2895 | 2896 | |
| 2896 | −static int btf_ext_setup_info(struct btf_ext *btf_ext, | |
| 2897 | − struct btf_ext_sec_setup_param *ext_sec) | |
| 2897 | +/* | |
| 2898 | + * Parse a single info subsection of the BTF.ext info data: | |
| 2899 | + * - validate subsection structure and elements | |
| 2900 | + * - save info subsection start and sizing details in struct btf_ext | |
| 2901 | + * - endian-independent operation, for calling before byte-swapping | |
| 2902 | + */ | |
| 2903 | +static int btf_ext_parse_sec_info(struct btf_ext *btf_ext, | |
| 2904 | + struct btf_ext_sec_info_param *ext_sec, | |
| 2905 | + bool is_native) | |
| 2898 | 2906 | { |
| 2899 | 2907 | const struct btf_ext_info_sec *sinfo; |
| 2900 | 2908 | struct btf_ext_info *ext_info; |
| 2901 | 2909 | __u32 info_left, record_size; |
| 2902 | 2910 | size_t sec_cnt = 0; |
| 2903 | − /* The start of the info sec (including the __u32 record_size). */ | |
| 2904 | 2911 | void *info; |
| 2905 | 2912 | |
| 2906 | 2913 | if (ext_sec->len == 0) |
| @@ -2912,6 +2919,7 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext, · 5 unmodified lines | ||
| 2912 | 2919 | return -EINVAL; |
| 2913 | 2920 | } |
| 2914 | 2921 | |
| 2922 | + /* The start of the info sec (including the __u32 record_size). */ | |
| 2915 | 2923 | info = btf_ext->data + btf_ext->hdr->hdr_len + ext_sec->off; |
| 2916 | 2924 | info_left = ext_sec->len; |
| 2917 | 2925 | |
| @@ -2927,9 +2935,13 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext, · 9 unmodified lines | ||
| 2927 | 2935 | return -EINVAL; |
| 2928 | 2936 | } |
| 2929 | 2937 | |
| 2930 | − /* The record size needs to meet the minimum standard */ | |
| 2931 | − record_size = *(__u32 *)info; | |
| 2938 | + /* The record size needs to meet either the minimum standard or, when | |
| 2939 | + * handling non-native endianness data, the exact standard so as | |
| 2940 | + * to allow safe byte-swapping. | |
| 2941 | + */ | |
| 2942 | + record_size = is_native ? *(__u32 *)info : bswap_32(*(__u32 *)info); | |
| 2932 | 2943 | if (record_size < ext_sec->min_rec_size || |
| 2944 | + (!is_native && record_size != ext_sec->min_rec_size) || | |
| 2933 | 2945 | record_size & 0x03) { |
| 2934 | 2946 | pr_debug("%s section in .BTF.ext has invalid record size %u\n", |
| 2935 | 2947 | ext_sec->desc, record_size); |
| @@ -2941,7 +2953,7 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext, · 5 unmodified lines | ||
| 2941 | 2953 | |
| 2942 | 2954 | /* If no records, return failure now so .BTF.ext won't be used. */ |
| 2943 | 2955 | if (!info_left) { |
| 2944 | − pr_debug("%s section in .BTF.ext has no records", ext_sec->desc); | |
| 2956 | + pr_debug("%s section in .BTF.ext has no records\n", ext_sec->desc); | |
| 2945 | 2957 | return -EINVAL; |
| 2946 | 2958 | } |
| 2947 | 2959 | |
| @@ -2956,7 +2968,7 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext, · 8 unmodified lines | ||
| 2956 | 2968 | return -EINVAL; |
| 2957 | 2969 | } |
| 2958 | 2970 | |
| 2959 | − num_records = sinfo->num_info; | |
| 2971 | + num_records = is_native ? sinfo->num_info : bswap_32(sinfo->num_info); | |
| 2960 | 2972 | if (num_records == 0) { |
| 2961 | 2973 | pr_debug("%s section has incorrect num_records in .BTF.ext\n", |
| 2962 | 2974 | ext_sec->desc); |
| @@ -2984,64 +2996,157 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext, · 21 unmodified lines | ||
| 2984 | 2996 | return 0; |
| 2985 | 2997 | } |
| 2986 | 2998 | |
| 2987 | −static int btf_ext_setup_func_info(struct btf_ext *btf_ext) | |
| 2999 | +/* Parse all info secs in the BTF.ext info data */ | |
| 3000 | +static int btf_ext_parse_info(struct btf_ext *btf_ext, bool is_native) | |
| 2988 | 3001 | { |
| 2989 | − struct btf_ext_sec_setup_param param = { | |
| 3002 | + struct btf_ext_sec_info_param func_info = { | |
| 2990 | 3003 | .off = btf_ext->hdr->func_info_off, |
| 2991 | 3004 | .len = btf_ext->hdr->func_info_len, |
| 2992 | 3005 | .min_rec_size = sizeof(struct bpf_func_info_min), |
| 2993 | 3006 | .ext_info = &btf_ext->func_info, |
| 2994 | 3007 | .desc = "func_info" |
| 2995 | 3008 | }; |
| 2996 | − | |
| 2997 | − return btf_ext_setup_info(btf_ext, ¶m); | |
| 2998 | −} | |
| 2999 | − | |
| 3000 | −static int btf_ext_setup_line_info(struct btf_ext *btf_ext) | |
| 3001 | −{ | |
| 3002 | − struct btf_ext_sec_setup_param param = { | |
| 3009 | + struct btf_ext_sec_info_param line_info = { | |
| 3003 | 3010 | .off = btf_ext->hdr->line_info_off, |
| 3004 | 3011 | .len = btf_ext->hdr->line_info_len, |
| 3005 | 3012 | .min_rec_size = sizeof(struct bpf_line_info_min), |
| 3006 | 3013 | .ext_info = &btf_ext->line_info, |
| 3007 | 3014 | .desc = "line_info", |
| 3008 | 3015 | }; |
| 3009 | − | |
| 3010 | − return btf_ext_setup_info(btf_ext, ¶m); | |
| 3011 | −} | |
| 3012 | − | |
| 3013 | −static int btf_ext_setup_core_relos(struct btf_ext *btf_ext) | |
| 3014 | −{ | |
| 3015 | − struct btf_ext_sec_setup_param param = { | |
| 3016 | + struct btf_ext_sec_info_param core_relo = { | |
| 3016 | 3017 | .off = btf_ext->hdr->core_relo_off, |
| 3017 | 3018 | .len = btf_ext->hdr->core_relo_len, |
| 3018 | 3019 | .min_rec_size = sizeof(struct bpf_core_relo), |
| 3019 | 3020 | .ext_info = &btf_ext->core_relo_info, |
| 3020 | 3021 | .desc = "core_relo", |
| 3021 | 3022 | }; |
| 3023 | + int err; | |
| 3024 | + | |
| 3025 | + err = btf_ext_parse_sec_info(btf_ext, &func_info, is_native); | |
| 3026 | + if (err) | |
| 3027 | + return err; | |
| 3028 | + | |
| 3029 | + err = btf_ext_parse_sec_info(btf_ext, &line_info, is_native); | |
| 3030 | + if (err) | |
| 3031 | + return err; | |
| 3032 | + | |
| 3033 | + if (btf_ext->hdr->hdr_len < offsetofend(struct btf_ext_header, core_relo_len)) | |
| 3034 | + return 0; /* skip core relos parsing */ | |
| 3035 | + | |
| 3036 | + err = btf_ext_parse_sec_info(btf_ext, &core_relo, is_native); | |
| 3037 | + if (err) | |
| 3038 | + return err; | |
| 3039 | + | |
| 3040 | + return 0; | |
| 3041 | +} | |
| 3042 | + | |
| 3043 | +/* Swap byte-order of BTF.ext header with any endianness */ | |
| 3044 | +static void btf_ext_bswap_hdr(struct btf_ext_header *h) | |
| 3045 | +{ | |
| 3046 | + bool is_native = h->magic == BTF_MAGIC; | |
| 3047 | + __u32 hdr_len; | |
| 3048 | + | |
| 3049 | + hdr_len = is_native ? h->hdr_len : bswap_32(h->hdr_len); | |
| 3050 | + | |
| 3051 | + h->magic = bswap_16(h->magic); | |
| 3052 | + h->hdr_len = bswap_32(h->hdr_len); | |
| 3053 | + h->func_info_off = bswap_32(h->func_info_off); | |
| 3054 | + h->func_info_len = bswap_32(h->func_info_len); | |
| 3055 | + h->line_info_off = bswap_32(h->line_info_off); | |
| 3056 | + h->line_info_len = bswap_32(h->line_info_len); | |
| 3057 | + | |
| 3058 | + if (hdr_len < offsetofend(struct btf_ext_header, core_relo_len)) | |
| 3059 | + return; | |
| 3060 | + | |
| 3061 | + h->core_relo_off = bswap_32(h->core_relo_off); | |
| 3062 | + h->core_relo_len = bswap_32(h->core_relo_len); | |
| 3063 | +} | |
| 3064 | + | |
| 3065 | +/* Swap byte-order of generic info subsection */ | |
| 3066 | +static void btf_ext_bswap_info_sec(void *info, __u32 len, bool is_native, | |
| 3067 | + info_rec_bswap_fn bswap_fn) | |
| 3068 | +{ | |
| 3069 | + struct btf_ext_info_sec *sec; | |
| 3070 | + __u32 info_left, rec_size, *rs; | |
| 3071 | + | |
| 3072 | + if (len == 0) | |
| 3073 | + return; | |
| 3074 | + | |
| 3075 | + rs = info; /* info record size */ | |
| 3076 | + rec_size = is_native ? *rs : bswap_32(*rs); | |
| 3077 | + *rs = bswap_32(*rs); | |
| 3078 | + | |
| 3079 | + sec = info + sizeof(__u32); /* info sec #1 */ | |
| 3080 | + info_left = len - sizeof(__u32); | |
| 3081 | + while (info_left) { | |
| 3082 | + unsigned int sec_hdrlen = sizeof(struct btf_ext_info_sec); | |
| 3083 | + __u32 i, num_recs; | |
| 3084 | + void *p; | |
| 3085 | + | |
| 3086 | + num_recs = is_native ? sec->num_info : bswap_32(sec->num_info); | |
| 3087 | + sec->sec_name_off = bswap_32(sec->sec_name_off); | |
| 3088 | + sec->num_info = bswap_32(sec->num_info); | |
| 3089 | + p = sec->data; /* info rec #1 */ | |
| 3090 | + for (i = 0; i < num_recs; i++, p += rec_size) | |
| 3091 | + bswap_fn(p); | |
| 3092 | + sec = p; | |
| 3093 | + info_left -= sec_hdrlen + (__u64)rec_size * num_recs; | |
| 3094 | + } | |
| 3095 | +} | |
| 3096 | + | |
| 3097 | +/* | |
| 3098 | + * Swap byte-order of all info data in a BTF.ext section | |
| 3099 | + * - requires BTF.ext hdr in native endianness | |
| 3100 | + */ | |
| 3101 | +static void btf_ext_bswap_info(struct btf_ext *btf_ext, void *data) | |
| 3102 | +{ | |
| 3103 | + const bool is_native = btf_ext->swapped_endian; | |
| 3104 | + const struct btf_ext_header *h = data; | |
| 3105 | + void *info; | |
| 3106 | + | |
| 3107 | + /* Swap func_info subsection byte-order */ | |
| 3108 | + info = data + h->hdr_len + h->func_info_off; | |
| 3109 | + btf_ext_bswap_info_sec(info, h->func_info_len, is_native, | |
| 3110 | + (info_rec_bswap_fn)bpf_func_info_bswap); | |
| 3111 | + | |
| 3112 | + /* Swap line_info subsection byte-order */ | |
| 3113 | + info = data + h->hdr_len + h->line_info_off; | |
| 3114 | + btf_ext_bswap_info_sec(info, h->line_info_len, is_native, | |
| 3115 | + (info_rec_bswap_fn)bpf_line_info_bswap); | |
| 3116 | + | |
| 3117 | + /* Swap core_relo subsection byte-order (if present) */ | |
| 3118 | + if (h->hdr_len < offsetofend(struct btf_ext_header, core_relo_len)) | |
| 3119 | + return; | |
| 3022 | 3120 | |
| 3023 | − return btf_ext_setup_info(btf_ext, ¶m); | |
| 3121 | + info = data + h->hdr_len + h->core_relo_off; | |
| 3122 | + btf_ext_bswap_info_sec(info, h->core_relo_len, is_native, | |
| 3123 | + (info_rec_bswap_fn)bpf_core_relo_bswap); | |
| 3024 | 3124 | } |
| 3025 | 3125 | |
| 3026 | −static int btf_ext_parse_hdr(__u8 *data, __u32 data_size) | |
| 3126 | +/* Parse hdr data and info sections: check and convert to native endianness */ | |
| 3127 | +static int btf_ext_parse(struct btf_ext *btf_ext) | |
| 3027 | 3128 | { |
| 3028 | − const struct btf_ext_header *hdr = (struct btf_ext_header *)data; | |
| 3129 | + __u32 hdr_len, data_size = btf_ext->data_size; | |
| 3130 | + struct btf_ext_header *hdr = btf_ext->hdr; | |
| 3131 | + bool swapped_endian = false; | |
| 3132 | + int err; | |
| 3029 | 3133 | |
| 3030 | − if (data_size < offsetofend(struct btf_ext_header, hdr_len) || | |
| 3031 | − data_size < hdr->hdr_len) { | |
| 3032 | − pr_debug("BTF.ext header not found"); | |
| 3134 | + if (data_size < offsetofend(struct btf_ext_header, hdr_len)) { | |
| 3135 | + pr_debug("BTF.ext header too short\n"); | |
| 3033 | 3136 | return -EINVAL; |
| 3034 | 3137 | } |
| 3035 | 3138 | |
| 3139 | + hdr_len = hdr->hdr_len; | |
| 3036 | 3140 | if (hdr->magic == bswap_16(BTF_MAGIC)) { |
| 3037 | − pr_warn("BTF.ext in non-native endianness is not supported\n"); | |
| 3038 | − return -ENOTSUP; | |
| 3141 | + swapped_endian = true; | |
| 3142 | + hdr_len = bswap_32(hdr_len); | |
| 3039 | 3143 | } else if (hdr->magic != BTF_MAGIC) { |
| 3040 | 3144 | pr_debug("Invalid BTF.ext magic:%x\n", hdr->magic); |
| 3041 | 3145 | return -EINVAL; |
| 3042 | 3146 | } |
| 3043 | 3147 | |
| 3044 | − if (hdr->version != BTF_VERSION) { | |
| 3148 | + /* Ensure known version of structs, current BTF_VERSION == 1 */ | |
| 3149 | + if (hdr->version != 1) { | |
| 3045 | 3150 | pr_debug("Unsupported BTF.ext version:%u\n", hdr->version); |
| 3046 | 3151 | return -ENOTSUP; |
| 3047 | 3152 | } |
| @@ -3051,11 +3156,39 @@ static int btf_ext_parse_hdr(__u8 *data, __u32 data_size) · 3 unmodified lines | ||
| 3051 | 3156 | return -ENOTSUP; |
| 3052 | 3157 | } |
| 3053 | 3158 | |
| 3054 | − if (data_size == hdr->hdr_len) { | |
| 3159 | + if (data_size < hdr_len) { | |
| 3160 | + pr_debug("BTF.ext header not found\n"); | |
| 3161 | + return -EINVAL; | |
| 3162 | + } else if (data_size == hdr_len) { | |
| 3055 | 3163 | pr_debug("BTF.ext has no data\n"); |
| 3056 | 3164 | return -EINVAL; |
| 3057 | 3165 | } |
| 3058 | 3166 | |
| 3167 | + /* Verify mandatory hdr info details present */ | |
| 3168 | + if (hdr_len < offsetofend(struct btf_ext_header, line_info_len)) { | |
| 3169 | + pr_warn("BTF.ext header missing func_info, line_info\n"); | |
| 3170 | + return -EINVAL; | |
| 3171 | + } | |
| 3172 | + | |
| 3173 | + /* Keep hdr native byte-order in memory for introspection */ | |
| 3174 | + if (swapped_endian) | |
| 3175 | + btf_ext_bswap_hdr(btf_ext->hdr); | |
| 3176 | + | |
| 3177 | + /* Validate info subsections and cache key metadata */ | |
| 3178 | + err = btf_ext_parse_info(btf_ext, !swapped_endian); | |
| 3179 | + if (err) | |
| 3180 | + return err; | |
| 3181 | + | |
| 3182 | + /* Keep infos native byte-order in memory for introspection */ | |
| 3183 | + if (swapped_endian) | |
| 3184 | + btf_ext_bswap_info(btf_ext, btf_ext->data); | |
| 3185 | + | |
| 3186 | + /* | |
| 3187 | + * Set btf_ext->swapped_endian only after all header and info data has | |
| 3188 | + * been swapped, helping bswap functions determine if their data are | |
| 3189 | + * in native byte-order when called. | |
| 3190 | + */ | |
| 3191 | + btf_ext->swapped_endian = swapped_endian; | |
| 3059 | 3192 | return 0; |
| 3060 | 3193 | } |
| 3061 | 3194 | |
| @@ -3067,6 +3200,7 @@ void btf_ext__free(struct btf_ext *btf_ext) · 5 unmodified lines | ||
| 3067 | 3200 | free(btf_ext->line_info.sec_idxs); |
| 3068 | 3201 | free(btf_ext->core_relo_info.sec_idxs); |
| 3069 | 3202 | free(btf_ext->data); |
| 3203 | + free(btf_ext->data_swapped); | |
| 3070 | 3204 | free(btf_ext); |
| 3071 | 3205 | } |
| 3072 | 3206 | |
| @@ -3087,29 +3221,7 @@ struct btf_ext *btf_ext__new(const __u8 *data, __u32 size) · 14 unmodified lines | ||
| 3087 | 3221 | } |
| 3088 | 3222 | memcpy(btf_ext->data, data, size); |
| 3089 | 3223 | |
| 3090 | − err = btf_ext_parse_hdr(btf_ext->data, size); | |
| 3091 | − if (err) | |
| 3092 | − goto done; | |
| 3093 | − | |
| 3094 | − if (btf_ext->hdr->hdr_len < offsetofend(struct btf_ext_header, line_info_len)) { | |
| 3095 | − err = -EINVAL; | |
| 3096 | − goto done; | |
| 3097 | − } | |
| 3098 | − | |
| 3099 | − err = btf_ext_setup_func_info(btf_ext); | |
| 3100 | − if (err) | |
| 3101 | − goto done; | |
| 3102 | − | |
| 3103 | − err = btf_ext_setup_line_info(btf_ext); | |
| 3104 | − if (err) | |
| 3105 | − goto done; | |
| 3106 | − | |
| 3107 | − if (btf_ext->hdr->hdr_len < offsetofend(struct btf_ext_header, core_relo_len)) | |
| 3108 | − goto done; /* skip core relos parsing */ | |
| 3109 | − | |
| 3110 | − err = btf_ext_setup_core_relos(btf_ext); | |
| 3111 | − if (err) | |
| 3112 | − goto done; | |
| 3224 | + err = btf_ext_parse(btf_ext); | |
| 3113 | 3225 | |
| 3114 | 3226 | done: |
| 3115 | 3227 | if (err) { |
| @@ -3120,15 +3232,66 @@ done: · 4 unmodified lines | ||
| 3120 | 3232 | return btf_ext; |
| 3121 | 3233 | } |
| 3122 | 3234 | |
| 3235 | +static void *btf_ext_raw_data(const struct btf_ext *btf_ext_ro, bool swap_endian) | |
| 3236 | +{ | |
| 3237 | + struct btf_ext *btf_ext = (struct btf_ext *)btf_ext_ro; | |
| 3238 | + const __u32 data_sz = btf_ext->data_size; | |
| 3239 | + void *data; | |
| 3240 | + | |
| 3241 | + /* Return native data (always present) or swapped data if present */ | |
| 3242 | + if (!swap_endian) | |
| 3243 | + return btf_ext->data; | |
| 3244 | + else if (btf_ext->data_swapped) | |
| 3245 | + return btf_ext->data_swapped; | |
| 3246 | + | |
| 3247 | + /* Recreate missing swapped data, then cache and return */ | |
| 3248 | + data = calloc(1, data_sz); | |
| 3249 | + if (!data) | |
| 3250 | + return NULL; | |
| 3251 | + memcpy(data, btf_ext->data, data_sz); | |
| 3252 | + | |
| 3253 | + btf_ext_bswap_info(btf_ext, data); | |
| 3254 | + btf_ext_bswap_hdr(data); | |
| 3255 | + btf_ext->data_swapped = data; | |
| 3256 | + return data; | |
| 3257 | +} | |
| 3258 | + | |
| 3123 | 3259 | const void *btf_ext__raw_data(const struct btf_ext *btf_ext, __u32 *size) |
| 3124 | 3260 | { |
| 3261 | + void *data; | |
| 3262 | + | |
| 3263 | + data = btf_ext_raw_data(btf_ext, btf_ext->swapped_endian); | |
| 3264 | + if (!data) | |
| 3265 | + return errno = ENOMEM, NULL; | |
| 3266 | + | |
| 3125 | 3267 | *size = btf_ext->data_size; |
| 3126 | − return btf_ext->data; | |
| 3268 | + return data; | |
| 3127 | 3269 | } |
| 3128 | 3270 | |
| 3129 | 3271 | __attribute__((alias("btf_ext__raw_data"))) |
| 3130 | 3272 | const void *btf_ext__get_raw_data(const struct btf_ext *btf_ext, __u32 *size); |
| 3131 | 3273 | |
| 3274 | +enum btf_endianness btf_ext__endianness(const struct btf_ext *btf_ext) | |
| 3275 | +{ | |
| 3276 | + if (is_host_big_endian()) | |
| 3277 | + return btf_ext->swapped_endian ? BTF_LITTLE_ENDIAN : BTF_BIG_ENDIAN; | |
| 3278 | + else | |
| 3279 | + return btf_ext->swapped_endian ? BTF_BIG_ENDIAN : BTF_LITTLE_ENDIAN; | |
| 3280 | +} | |
| 3281 | + | |
| 3282 | +int btf_ext__set_endianness(struct btf_ext *btf_ext, enum btf_endianness endian) | |
| 3283 | +{ | |
| 3284 | + if (endian != BTF_LITTLE_ENDIAN && endian != BTF_BIG_ENDIAN) | |
| 3285 | + return libbpf_err(-EINVAL); | |
| 3286 | + | |
| 3287 | + btf_ext->swapped_endian = is_host_big_endian() != (endian == BTF_BIG_ENDIAN); | |
| 3288 | + | |
| 3289 | + if (!btf_ext->swapped_endian) { | |
| 3290 | + free(btf_ext->data_swapped); | |
| 3291 | + btf_ext->data_swapped = NULL; | |
| 3292 | + } | |
| 3293 | + return 0; | |
| 3294 | +} | |
| 3132 | 3295 | |
| 3133 | 3296 | struct btf_dedup; |
| 3134 | 3297 | |
| @@ -3291,7 +3454,7 @@ int btf__dedup(struct btf *btf, const struct btf_dedup_opts *opts) · 156 unmodified lines | ||
| 3291 | 3454 | |
| 3292 | 3455 | d = btf_dedup_new(btf, opts); |
| 3293 | 3456 | if (IS_ERR(d)) { |
| 3294 | − pr_debug("btf_dedup_new failed: %ld", PTR_ERR(d)); | |
| 3457 | + pr_debug("btf_dedup_new failed: %ld\n", PTR_ERR(d)); | |
| 3295 | 3458 | return libbpf_err(-EINVAL); |
| 3296 | 3459 | } |
| 3297 | 3460 | |
| @@ -3302,42 +3465,42 @@ int btf__dedup(struct btf *btf, const struct btf_dedup_opts *opts) · 4 unmodified lines | ||
| 3302 | 3465 | |
| 3303 | 3466 | err = btf_dedup_prep(d); |
| 3304 | 3467 | if (err) { |
| 3305 | − pr_debug("btf_dedup_prep failed:%d\n", err); | |
| 3468 | + pr_debug("btf_dedup_prep failed: %s\n", errstr(err)); | |
| 3306 | 3469 | goto done; |
| 3307 | 3470 | } |
| 3308 | 3471 | err = btf_dedup_strings(d); |
| 3309 | 3472 | if (err < 0) { |
| 3310 | − pr_debug("btf_dedup_strings failed:%d\n", err); | |
| 3473 | + pr_debug("btf_dedup_strings failed: %s\n", errstr(err)); | |
| 3311 | 3474 | goto done; |
| 3312 | 3475 | } |
| 3313 | 3476 | err = btf_dedup_prim_types(d); |
| 3314 | 3477 | if (err < 0) { |
| 3315 | − pr_debug("btf_dedup_prim_types failed:%d\n", err); | |
| 3478 | + pr_debug("btf_dedup_prim_types failed: %s\n", errstr(err)); | |
| 3316 | 3479 | goto done; |
| 3317 | 3480 | } |
| 3318 | 3481 | err = btf_dedup_struct_types(d); |
| 3319 | 3482 | if (err < 0) { |
| 3320 | − pr_debug("btf_dedup_struct_types failed:%d\n", err); | |
| 3483 | + pr_debug("btf_dedup_struct_types failed: %s\n", errstr(err)); | |
| 3321 | 3484 | goto done; |
| 3322 | 3485 | } |
| 3323 | 3486 | err = btf_dedup_resolve_fwds(d); |
| 3324 | 3487 | if (err < 0) { |
| 3325 | − pr_debug("btf_dedup_resolve_fwds failed:%d\n", err); | |
| 3488 | + pr_debug("btf_dedup_resolve_fwds failed: %s\n", errstr(err)); | |
| 3326 | 3489 | goto done; |
| 3327 | 3490 | } |
| 3328 | 3491 | err = btf_dedup_ref_types(d); |
| 3329 | 3492 | if (err < 0) { |
| 3330 | − pr_debug("btf_dedup_ref_types failed:%d\n", err); | |
| 3493 | + pr_debug("btf_dedup_ref_types failed: %s\n", errstr(err)); | |
| 3331 | 3494 | goto done; |
| 3332 | 3495 | } |
| 3333 | 3496 | err = btf_dedup_compact_types(d); |
| 3334 | 3497 | if (err < 0) { |
| 3335 | − pr_debug("btf_dedup_compact_types failed:%d\n", err); | |
| 3498 | + pr_debug("btf_dedup_compact_types failed: %s\n", errstr(err)); | |
| 3336 | 3499 | goto done; |
| 3337 | 3500 | } |
| 3338 | 3501 | err = btf_dedup_remap_types(d); |
| 3339 | 3502 | if (err < 0) { |
| 3340 | − pr_debug("btf_dedup_remap_types failed:%d\n", err); | |
| 3503 | + pr_debug("btf_dedup_remap_types failed: %s\n", errstr(err)); | |
| 3341 | 3504 | goto done; |
| 3342 | 3505 | } |
| 3343 | 3506 | |
| @@ -3385,7 +3548,7 @@ struct btf_dedup { · 41 unmodified lines | ||
| 3385 | 3548 | struct strset *strs_set; |
| 3386 | 3549 | }; |
| 3387 | 3550 | |
| 3388 | −static long hash_combine(long h, long value) | |
| 3551 | +static unsigned long hash_combine(unsigned long h, unsigned long value) | |
| 3389 | 3552 | { |
| 3390 | 3553 | return h * 31 + value; |
| 3391 | 3554 | } |
| @@ -5056,7 +5219,8 @@ struct btf *btf__load_vmlinux_btf(void) · 1664 unmodified lines | ||
| 5056 | 5219 | btf = btf__parse(sysfs_btf_path, NULL); |
| 5057 | 5220 | if (!btf) { |
| 5058 | 5221 | err = -errno; |
| 5059 | − pr_warn("failed to read kernel BTF from '%s': %d\n", sysfs_btf_path, err); | |
| 5222 | + pr_warn("failed to read kernel BTF from '%s': %s\n", | |
| 5223 | + sysfs_btf_path, errstr(err)); | |
| 5060 | 5224 | return libbpf_err_ptr(err); |
| 5061 | 5225 | } |
| 5062 | 5226 | pr_debug("loaded kernel BTF from '%s'\n", sysfs_btf_path); |
| @@ -5073,7 +5237,7 @@ struct btf *btf__load_vmlinux_btf(void) · 10 unmodified lines | ||
| 5073 | 5237 | |
| 5074 | 5238 | btf = btf__parse(path, NULL); |
| 5075 | 5239 | err = libbpf_get_error(btf); |
| 5076 | − pr_debug("loading kernel BTF '%s': %d\n", path, err); | |
| 5240 | + pr_debug("loading kernel BTF '%s': %s\n", path, errstr(err)); | |
| 5077 | 5241 | if (err) |
| 5078 | 5242 | continue; |
| 5079 | 5243 | |
| @@ -167,6 +167,9 @@ LIBBPF_API const char *btf__str_by_offset(const struct btf *btf, __u32 offset); · 166 unmodified lines | ||
| 167 | 167 | LIBBPF_API struct btf_ext *btf_ext__new(const __u8 *data, __u32 size); |
| 168 | 168 | LIBBPF_API void btf_ext__free(struct btf_ext *btf_ext); |
| 169 | 169 | LIBBPF_API const void *btf_ext__raw_data(const struct btf_ext *btf_ext, __u32 *size); |
| 170 | +LIBBPF_API enum btf_endianness btf_ext__endianness(const struct btf_ext *btf_ext); | |
| 171 | +LIBBPF_API int btf_ext__set_endianness(struct btf_ext *btf_ext, | |
| 172 | + enum btf_endianness endian); | |
| 170 | 173 | |
| 171 | 174 | LIBBPF_API int btf__find_str(struct btf *btf, const char *s); |
| 172 | 175 | LIBBPF_API int btf__add_str(struct btf *btf, const char *s); |
| @@ -21,6 +21,7 @@ · 20 unmodified lines | ||
| 21 | 21 | #include "hashmap.h" |
| 22 | 22 | #include "libbpf.h" |
| 23 | 23 | #include "libbpf_internal.h" |
| 24 | +#include "str_error.h" | |
| 24 | 25 | |
| 25 | 26 | static const char PREFIXES[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t"; |
| 26 | 27 | static const size_t PREFIX_CNT = sizeof(PREFIXES) - 1; |
| @@ -867,8 +868,8 @@ static void btf_dump_emit_bit_padding(const struct btf_dump *d, · 840 unmodified lines | ||
| 867 | 868 | } pads[] = { |
| 868 | 869 | {"long", d->ptr_sz * 8}, {"int", 32}, {"short", 16}, {"char", 8} |
| 869 | 870 | }; |
| 870 | − int new_off, pad_bits, bits, i; | |
| 871 | − const char *pad_type; | |
| 871 | + int new_off = 0, pad_bits = 0, bits, i; | |
| 872 | + const char *pad_type = NULL; | |
| 872 | 873 | |
| 873 | 874 | if (cur_off >= next_off) |
| 874 | 875 | return; /* no gap */ |
| @@ -1304,7 +1305,7 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id, · 429 unmodified lines | ||
| 1304 | 1305 | * chain, restore stack, emit warning, and try to |
| 1305 | 1306 | * proceed nevertheless |
| 1306 | 1307 | */ |
| 1307 | − pr_warn("not enough memory for decl stack:%d", err); | |
| 1308 | + pr_warn("not enough memory for decl stack: %s\n", errstr(err)); | |
| 1308 | 1309 | d->decl_stack_cnt = stack_start; |
| 1309 | 1310 | return; |
| 1310 | 1311 | } |
| @@ -428,7 +428,7 @@ static int btf_relocate_rewrite_strs(struct btf_relocate *r, __u32 i) · 427 unmodified lines | ||
| 428 | 428 | } else { |
| 429 | 429 | off = r->str_map[*str_off]; |
| 430 | 430 | if (!off) { |
| 431 | − pr_warn("string '%s' [offset %u] is not mapped to base BTF", | |
| 431 | + pr_warn("string '%s' [offset %u] is not mapped to base BTF\n", | |
| 432 | 432 | btf__str_by_offset(r->btf, off), *str_off); |
| 433 | 433 | return -ENOENT; |
| 434 | 434 | } |
| @@ -24,7 +24,6 @@ · 23 unmodified lines | ||
| 24 | 24 | |
| 25 | 25 | int elf_open(const char *binary_path, struct elf_fd *elf_fd) |
| 26 | 26 | { |
| 27 | − char errmsg[STRERR_BUFSIZE]; | |
| 28 | 27 | int fd, ret; |
| 29 | 28 | Elf *elf; |
| 30 | 29 | |
| @@ -38,8 +37,7 @@ int elf_open(const char *binary_path, struct elf_fd *elf_fd) · 7 unmodified lines | ||
| 38 | 37 | fd = open(binary_path, O_RDONLY | O_CLOEXEC); |
| 39 | 38 | if (fd < 0) { |
| 40 | 39 | ret = -errno; |
| 41 | − pr_warn("elf: failed to open %s: %s\n", binary_path, | |
| 42 | − libbpf_strerror_r(ret, errmsg, sizeof(errmsg))); | |
| 40 | + pr_warn("elf: failed to open %s: %s\n", binary_path, errstr(ret)); | |
| 43 | 41 | return ret; |
| 44 | 42 | } |
| 45 | 43 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); |
| @@ -47,7 +47,6 @@ static int probe_kern_prog_name(int token_fd) · 46 unmodified lines | ||
| 47 | 47 | |
| 48 | 48 | static int probe_kern_global_data(int token_fd) |
| 49 | 49 | { |
| 50 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 51 | 50 | struct bpf_insn insns[] = { |
| 52 | 51 | BPF_LD_MAP_VALUE(BPF_REG_1, 0, 16), |
| 53 | 52 | BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 42), |
| @@ -67,9 +66,8 @@ static int probe_kern_global_data(int token_fd) · 13 unmodified lines | ||
| 67 | 66 | map = bpf_map_create(BPF_MAP_TYPE_ARRAY, "libbpf_global", sizeof(int), 32, 1, &map_opts); |
| 68 | 67 | if (map < 0) { |
| 69 | 68 | ret = -errno; |
| 70 | − cp = libbpf_strerror_r(ret, errmsg, sizeof(errmsg)); | |
| 71 | − pr_warn("Error in %s():%s(%d). Couldn't create simple array map.\n", | |
| 72 | − __func__, cp, -ret); | |
| 69 | + pr_warn("Error in %s(): %s. Couldn't create simple array map.\n", | |
| 70 | + __func__, errstr(ret)); | |
| 73 | 71 | return ret; |
| 74 | 72 | } |
| 75 | 73 | |
| @@ -267,7 +265,6 @@ static int probe_kern_probe_read_kernel(int token_fd) · 191 unmodified lines | ||
| 267 | 265 | |
| 268 | 266 | static int probe_prog_bind_map(int token_fd) |
| 269 | 267 | { |
| 270 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 271 | 268 | struct bpf_insn insns[] = { |
| 272 | 269 | BPF_MOV64_IMM(BPF_REG_0, 0), |
| 273 | 270 | BPF_EXIT_INSN(), |
| @@ -285,9 +282,8 @@ static int probe_prog_bind_map(int token_fd) · 11 unmodified lines | ||
| 285 | 282 | map = bpf_map_create(BPF_MAP_TYPE_ARRAY, "libbpf_det_bind", sizeof(int), 32, 1, &map_opts); |
| 286 | 283 | if (map < 0) { |
| 287 | 284 | ret = -errno; |
| 288 | − cp = libbpf_strerror_r(ret, errmsg, sizeof(errmsg)); | |
| 289 | − pr_warn("Error in %s():%s(%d). Couldn't create simple array map.\n", | |
| 290 | − __func__, cp, -ret); | |
| 285 | + pr_warn("Error in %s(): %s. Couldn't create simple array map.\n", | |
| 286 | + __func__, errstr(ret)); | |
| 291 | 287 | return ret; |
| 292 | 288 | } |
| 293 | 289 | |
| @@ -604,7 +600,8 @@ bool feat_supported(struct kern_feature_cache *cache, enum kern_feature_id feat_ · 310 unmodified lines | ||
| 604 | 600 | } else if (ret == 0) { |
| 605 | 601 | WRITE_ONCE(cache->res[feat_id], FEAT_MISSING); |
| 606 | 602 | } else { |
| 607 | − pr_warn("Detection of kernel %s support failed: %d\n", feat->desc, ret); | |
| 603 | + pr_warn("Detection of kernel %s support failed: %s\n", | |
| 604 | + feat->desc, errstr(ret)); | |
| 608 | 605 | WRITE_ONCE(cache->res[feat_id], FEAT_MISSING); |
| 609 | 606 | } |
| 610 | 607 | } |
| @@ -14,6 +14,7 @@ · 13 unmodified lines | ||
| 14 | 14 | #include "bpf_gen_internal.h" |
| 15 | 15 | #include "skel_internal.h" |
| 16 | 16 | #include <asm/byteorder.h> |
| 17 | +#include "str_error.h" | |
| 17 | 18 | |
| 18 | 19 | #define MAX_USED_MAPS 64 |
| 19 | 20 | #define MAX_USED_PROGS 32 |
| @@ -393,7 +394,7 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps) · 373 unmodified lines | ||
| 393 | 394 | blob_fd_array_off(gen, i)); |
| 394 | 395 | emit(gen, BPF_MOV64_IMM(BPF_REG_0, 0)); |
| 395 | 396 | emit(gen, BPF_EXIT_INSN()); |
| 396 | − pr_debug("gen: finish %d\n", gen->error); | |
| 397 | + pr_debug("gen: finish %s\n", errstr(gen->error)); | |
| 397 | 398 | if (!gen->error) { |
| 398 | 399 | struct gen_loader_opts *opts = gen->opts; |
| 399 | 400 | |
| @@ -401,6 +402,15 @@ int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps) · 1 unmodified line | ||
| 401 | 402 | opts->insns_sz = gen->insn_cur - gen->insn_start; |
| 402 | 403 | opts->data = gen->data_start; |
| 403 | 404 | opts->data_sz = gen->data_cur - gen->data_start; |
| 405 | + | |
| 406 | + /* use target endianness for embedded loader */ | |
| 407 | + if (gen->swapped_endian) { | |
| 408 | + struct bpf_insn *insn = (struct bpf_insn *)opts->insns; | |
| 409 | + int insn_cnt = opts->insns_sz / sizeof(struct bpf_insn); | |
| 410 | + | |
| 411 | + for (i = 0; i < insn_cnt; i++) | |
| 412 | + bpf_insn_bswap(insn++); | |
| 413 | + } | |
| 404 | 414 | } |
| 405 | 415 | return gen->error; |
| 406 | 416 | } |
| @@ -414,6 +424,28 @@ void bpf_gen__free(struct bpf_gen *gen) · 7 unmodified lines | ||
| 414 | 424 | free(gen); |
| 415 | 425 | } |
| 416 | 426 | |
| 427 | +/* | |
| 428 | + * Fields of bpf_attr are set to values in native byte-order before being | |
| 429 | + * written to the target-bound data blob, and may need endian conversion. | |
| 430 | + * This macro allows providing the correct value in situ more simply than | |
| 431 | + * writing a separate converter for *all fields* of *all records* included | |
| 432 | + * in union bpf_attr. Note that sizeof(rval) should match the assignment | |
| 433 | + * target to avoid runtime problems. | |
| 434 | + */ | |
| 435 | +#define tgt_endian(rval) ({ \ | |
| 436 | + typeof(rval) _val = (rval); \ | |
| 437 | + if (gen->swapped_endian) { \ | |
| 438 | + switch (sizeof(_val)) { \ | |
| 439 | + case 1: break; \ | |
| 440 | + case 2: _val = bswap_16(_val); break; \ | |
| 441 | + case 4: _val = bswap_32(_val); break; \ | |
| 442 | + case 8: _val = bswap_64(_val); break; \ | |
| 443 | + default: pr_warn("unsupported bswap size!\n"); \ | |
| 444 | + } \ | |
| 445 | + } \ | |
| 446 | + _val; \ | |
| 447 | +}) | |
| 448 | + | |
| 417 | 449 | void bpf_gen__load_btf(struct bpf_gen *gen, const void *btf_raw_data, |
| 418 | 450 | __u32 btf_raw_size) |
| 419 | 451 | { |
| @@ -422,11 +454,12 @@ void bpf_gen__load_btf(struct bpf_gen *gen, const void *btf_raw_data, · 2 unmodified lines | ||
| 422 | 454 | union bpf_attr attr; |
| 423 | 455 | |
| 424 | 456 | memset(&attr, 0, attr_size); |
| 425 | − pr_debug("gen: load_btf: size %d\n", btf_raw_size); | |
| 426 | 457 | btf_data = add_data(gen, btf_raw_data, btf_raw_size); |
| 427 | 458 | |
| 428 | − attr.btf_size = btf_raw_size; | |
| 459 | + attr.btf_size = tgt_endian(btf_raw_size); | |
| 429 | 460 | btf_load_attr = add_data(gen, &attr, attr_size); |
| 461 | + pr_debug("gen: load_btf: off %d size %d, attr: off %d size %d\n", | |
| 462 | + btf_data, btf_raw_size, btf_load_attr, attr_size); | |
| 430 | 463 | |
| 431 | 464 | /* populate union bpf_attr with user provided log details */ |
| 432 | 465 | move_ctx2blob(gen, attr_field(btf_load_attr, btf_log_level), 4, |
| @@ -457,28 +490,29 @@ void bpf_gen__map_create(struct bpf_gen *gen, · 24 unmodified lines | ||
| 457 | 490 | union bpf_attr attr; |
| 458 | 491 | |
| 459 | 492 | memset(&attr, 0, attr_size); |
| 460 | − attr.map_type = map_type; | |
| 461 | − attr.key_size = key_size; | |
| 462 | − attr.value_size = value_size; | |
| 463 | − attr.map_flags = map_attr->map_flags; | |
| 464 | − attr.map_extra = map_attr->map_extra; | |
| 493 | + attr.map_type = tgt_endian(map_type); | |
| 494 | + attr.key_size = tgt_endian(key_size); | |
| 495 | + attr.value_size = tgt_endian(value_size); | |
| 496 | + attr.map_flags = tgt_endian(map_attr->map_flags); | |
| 497 | + attr.map_extra = tgt_endian(map_attr->map_extra); | |
| 465 | 498 | if (map_name) |
| 466 | 499 | libbpf_strlcpy(attr.map_name, map_name, sizeof(attr.map_name)); |
| 467 | − attr.numa_node = map_attr->numa_node; | |
| 468 | − attr.map_ifindex = map_attr->map_ifindex; | |
| 469 | − attr.max_entries = max_entries; | |
| 470 | − attr.btf_key_type_id = map_attr->btf_key_type_id; | |
| 471 | − attr.btf_value_type_id = map_attr->btf_value_type_id; | |
| 472 | − | |
| 473 | − pr_debug("gen: map_create: %s idx %d type %d value_type_id %d\n", | |
| 474 | − attr.map_name, map_idx, map_type, attr.btf_value_type_id); | |
| 500 | + attr.numa_node = tgt_endian(map_attr->numa_node); | |
| 501 | + attr.map_ifindex = tgt_endian(map_attr->map_ifindex); | |
| 502 | + attr.max_entries = tgt_endian(max_entries); | |
| 503 | + attr.btf_key_type_id = tgt_endian(map_attr->btf_key_type_id); | |
| 504 | + attr.btf_value_type_id = tgt_endian(map_attr->btf_value_type_id); | |
| 475 | 505 | |
| 476 | 506 | map_create_attr = add_data(gen, &attr, attr_size); |
| 477 | − if (attr.btf_value_type_id) | |
| 507 | + pr_debug("gen: map_create: %s idx %d type %d value_type_id %d, attr: off %d size %d\n", | |
| 508 | + map_name, map_idx, map_type, map_attr->btf_value_type_id, | |
| 509 | + map_create_attr, attr_size); | |
| 510 | + | |
| 511 | + if (map_attr->btf_value_type_id) | |
| 478 | 512 | /* populate union bpf_attr with btf_fd saved in the stack earlier */ |
| 479 | 513 | move_stack2blob(gen, attr_field(map_create_attr, btf_fd), 4, |
| 480 | 514 | stack_off(btf_fd)); |
| 481 | − switch (attr.map_type) { | |
| 515 | + switch (map_type) { | |
| 482 | 516 | case BPF_MAP_TYPE_ARRAY_OF_MAPS: |
| 483 | 517 | case BPF_MAP_TYPE_HASH_OF_MAPS: |
| 484 | 518 | move_stack2blob(gen, attr_field(map_create_attr, inner_map_fd), 4, |
| @@ -498,8 +532,8 @@ void bpf_gen__map_create(struct bpf_gen *gen, · 13 unmodified lines | ||
| 498 | 532 | /* emit MAP_CREATE command */ |
| 499 | 533 | emit_sys_bpf(gen, BPF_MAP_CREATE, map_create_attr, attr_size); |
| 500 | 534 | debug_ret(gen, "map_create %s idx %d type %d value_size %d value_btf_id %d", |
| 501 | − attr.map_name, map_idx, map_type, value_size, | |
| 502 | − attr.btf_value_type_id); | |
| 535 | + map_name, map_idx, map_type, value_size, | |
| 536 | + map_attr->btf_value_type_id); | |
| 503 | 537 | emit_check_err(gen); |
| 504 | 538 | /* remember map_fd in the stack, if successful */ |
| 505 | 539 | if (map_idx < 0) { |
| @@ -784,12 +818,12 @@ log: · 278 unmodified lines | ||
| 784 | 818 | emit_ksym_relo_log(gen, relo, kdesc->ref); |
| 785 | 819 | } |
| 786 | 820 | |
| 787 | −static __u32 src_reg_mask(void) | |
| 821 | +static __u32 src_reg_mask(struct bpf_gen *gen) | |
| 788 | 822 | { |
| 789 | −#if defined(__LITTLE_ENDIAN_BITFIELD) | |
| 790 | − return 0x0f; /* src_reg,dst_reg,... */ | |
| 791 | −#elif defined(__BIG_ENDIAN_BITFIELD) | |
| 792 | − return 0xf0; /* dst_reg,src_reg,... */ | |
| 823 | +#if defined(__LITTLE_ENDIAN_BITFIELD) /* src_reg,dst_reg,... */ | |
| 824 | + return gen->swapped_endian ? 0xf0 : 0x0f; | |
| 825 | +#elif defined(__BIG_ENDIAN_BITFIELD) /* dst_reg,src_reg,... */ | |
| 826 | + return gen->swapped_endian ? 0x0f : 0xf0; | |
| 793 | 827 | #else |
| 794 | 828 | #error "Unsupported bit endianness, cannot proceed" |
| 795 | 829 | #endif |
| @@ -840,7 +874,7 @@ static void emit_relo_ksym_btf(struct bpf_gen *gen, struct ksym_relo_desc *relo, · 44 unmodified lines | ||
| 840 | 874 | emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 3)); |
| 841 | 875 | clear_src_reg: |
| 842 | 876 | /* clear bpf_object__relocate_data's src_reg assignment, otherwise we get a verifier failure */ |
| 843 | − reg_mask = src_reg_mask(); | |
| 877 | + reg_mask = src_reg_mask(gen); | |
| 844 | 878 | emit(gen, BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_8, offsetofend(struct bpf_insn, code))); |
| 845 | 879 | emit(gen, BPF_ALU32_IMM(BPF_AND, BPF_REG_9, reg_mask)); |
| 846 | 880 | emit(gen, BPF_STX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, offsetofend(struct bpf_insn, code))); |
| @@ -931,48 +965,94 @@ static void cleanup_relos(struct bpf_gen *gen, int insns) · 84 unmodified lines | ||
| 931 | 965 | cleanup_core_relo(gen); |
| 932 | 966 | } |
| 933 | 967 | |
| 968 | +/* Convert func, line, and core relo info blobs to target endianness */ | |
| 969 | +static void info_blob_bswap(struct bpf_gen *gen, int func_info, int line_info, | |
| 970 | + int core_relos, struct bpf_prog_load_opts *load_attr) | |
| 971 | +{ | |
| 972 | + struct bpf_func_info *fi = gen->data_start + func_info; | |
| 973 | + struct bpf_line_info *li = gen->data_start + line_info; | |
| 974 | + struct bpf_core_relo *cr = gen->data_start + core_relos; | |
| 975 | + int i; | |
| 976 | + | |
| 977 | + for (i = 0; i < load_attr->func_info_cnt; i++) | |
| 978 | + bpf_func_info_bswap(fi++); | |
| 979 | + | |
| 980 | + for (i = 0; i < load_attr->line_info_cnt; i++) | |
| 981 | + bpf_line_info_bswap(li++); | |
| 982 | + | |
| 983 | + for (i = 0; i < gen->core_relo_cnt; i++) | |
| 984 | + bpf_core_relo_bswap(cr++); | |
| 985 | +} | |
| 986 | + | |
| 934 | 987 | void bpf_gen__prog_load(struct bpf_gen *gen, |
| 935 | 988 | enum bpf_prog_type prog_type, const char *prog_name, |
| 936 | 989 | const char *license, struct bpf_insn *insns, size_t insn_cnt, |
| 937 | 990 | struct bpf_prog_load_opts *load_attr, int prog_idx) |
| 938 | 991 | { |
| 992 | + int func_info_tot_sz = load_attr->func_info_cnt * | |
| 993 | + load_attr->func_info_rec_size; | |
| 994 | + int line_info_tot_sz = load_attr->line_info_cnt * | |
| 995 | + load_attr->line_info_rec_size; | |
| 996 | + int core_relo_tot_sz = gen->core_relo_cnt * | |
| 997 | + sizeof(struct bpf_core_relo); | |
| 939 | 998 | int prog_load_attr, license_off, insns_off, func_info, line_info, core_relos; |
| 940 | 999 | int attr_size = offsetofend(union bpf_attr, core_relo_rec_size); |
| 941 | 1000 | union bpf_attr attr; |
| 942 | 1001 | |
| 943 | 1002 | memset(&attr, 0, attr_size); |
| 944 | − pr_debug("gen: prog_load: type %d insns_cnt %zd progi_idx %d\n", | |
| 945 | − prog_type, insn_cnt, prog_idx); | |
| 946 | 1003 | /* add license string to blob of bytes */ |
| 947 | 1004 | license_off = add_data(gen, license, strlen(license) + 1); |
| 948 | 1005 | /* add insns to blob of bytes */ |
| 949 | 1006 | insns_off = add_data(gen, insns, insn_cnt * sizeof(struct bpf_insn)); |
| 1007 | + pr_debug("gen: prog_load: prog_idx %d type %d insn off %d insns_cnt %zd license off %d\n", | |
| 1008 | + prog_idx, prog_type, insns_off, insn_cnt, license_off); | |
| 950 | 1009 | |
| 951 | − attr.prog_type = prog_type; | |
| 952 | − attr.expected_attach_type = load_attr->expected_attach_type; | |
| 953 | − attr.attach_btf_id = load_attr->attach_btf_id; | |
| 954 | − attr.prog_ifindex = load_attr->prog_ifindex; | |
| 955 | − attr.kern_version = 0; | |
| 956 | − attr.insn_cnt = (__u32)insn_cnt; | |
| 957 | − attr.prog_flags = load_attr->prog_flags; | |
| 958 | − | |
| 959 | − attr.func_info_rec_size = load_attr->func_info_rec_size; | |
| 960 | − attr.func_info_cnt = load_attr->func_info_cnt; | |
| 961 | − func_info = add_data(gen, load_attr->func_info, | |
| 962 | − attr.func_info_cnt * attr.func_info_rec_size); | |
| 1010 | + /* convert blob insns to target endianness */ | |
| 1011 | + if (gen->swapped_endian) { | |
| 1012 | + struct bpf_insn *insn = gen->data_start + insns_off; | |
| 1013 | + int i; | |
| 963 | 1014 | |
| 964 | − attr.line_info_rec_size = load_attr->line_info_rec_size; | |
| 965 | − attr.line_info_cnt = load_attr->line_info_cnt; | |
| 966 | − line_info = add_data(gen, load_attr->line_info, | |
| 967 | − attr.line_info_cnt * attr.line_info_rec_size); | |
| 1015 | + for (i = 0; i < insn_cnt; i++, insn++) | |
| 1016 | + bpf_insn_bswap(insn); | |
| 1017 | + } | |
| 968 | 1018 | |
| 969 | − attr.core_relo_rec_size = sizeof(struct bpf_core_relo); | |
| 970 | − attr.core_relo_cnt = gen->core_relo_cnt; | |
| 971 | − core_relos = add_data(gen, gen->core_relos, | |
| 972 | − attr.core_relo_cnt * attr.core_relo_rec_size); | |
| 1019 | + attr.prog_type = tgt_endian(prog_type); | |
| 1020 | + attr.expected_attach_type = tgt_endian(load_attr->expected_attach_type); | |
| 1021 | + attr.attach_btf_id = tgt_endian(load_attr->attach_btf_id); | |
| 1022 | + attr.prog_ifindex = tgt_endian(load_attr->prog_ifindex); | |
| 1023 | + attr.kern_version = 0; | |
| 1024 | + attr.insn_cnt = tgt_endian((__u32)insn_cnt); | |
| 1025 | + attr.prog_flags = tgt_endian(load_attr->prog_flags); | |
| 1026 | + | |
| 1027 | + attr.func_info_rec_size = tgt_endian(load_attr->func_info_rec_size); | |
| 1028 | + attr.func_info_cnt = tgt_endian(load_attr->func_info_cnt); | |
| 1029 | + func_info = add_data(gen, load_attr->func_info, func_info_tot_sz); | |
| 1030 | + pr_debug("gen: prog_load: func_info: off %d cnt %d rec size %d\n", | |
| 1031 | + func_info, load_attr->func_info_cnt, | |
| 1032 | + load_attr->func_info_rec_size); | |
| 1033 | + | |
| 1034 | + attr.line_info_rec_size = tgt_endian(load_attr->line_info_rec_size); | |
| 1035 | + attr.line_info_cnt = tgt_endian(load_attr->line_info_cnt); | |
| 1036 | + line_info = add_data(gen, load_attr->line_info, line_info_tot_sz); | |
| 1037 | + pr_debug("gen: prog_load: line_info: off %d cnt %d rec size %d\n", | |
| 1038 | + line_info, load_attr->line_info_cnt, | |
| 1039 | + load_attr->line_info_rec_size); | |
| 1040 | + | |
| 1041 | + attr.core_relo_rec_size = tgt_endian((__u32)sizeof(struct bpf_core_relo)); | |
| 1042 | + attr.core_relo_cnt = tgt_endian(gen->core_relo_cnt); | |
| 1043 | + core_relos = add_data(gen, gen->core_relos, core_relo_tot_sz); | |
| 1044 | + pr_debug("gen: prog_load: core_relos: off %d cnt %d rec size %zd\n", | |
| 1045 | + core_relos, gen->core_relo_cnt, | |
| 1046 | + sizeof(struct bpf_core_relo)); | |
| 1047 | + | |
| 1048 | + /* convert all info blobs to target endianness */ | |
| 1049 | + if (gen->swapped_endian) | |
| 1050 | + info_blob_bswap(gen, func_info, line_info, core_relos, load_attr); | |
| 973 | 1051 | |
| 974 | 1052 | libbpf_strlcpy(attr.prog_name, prog_name, sizeof(attr.prog_name)); |
| 975 | 1053 | prog_load_attr = add_data(gen, &attr, attr_size); |
| 1054 | + pr_debug("gen: prog_load: attr: off %d size %d\n", | |
| 1055 | + prog_load_attr, attr_size); | |
| 976 | 1056 | |
| 977 | 1057 | /* populate union bpf_attr with a pointer to license */ |
| 978 | 1058 | emit_rel_store(gen, attr_field(prog_load_attr, license), license_off); |
| @@ -1040,7 +1120,6 @@ void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue, · 61 unmodified lines | ||
| 1040 | 1120 | int zero = 0; |
| 1041 | 1121 | |
| 1042 | 1122 | memset(&attr, 0, attr_size); |
| 1043 | − pr_debug("gen: map_update_elem: idx %d\n", map_idx); | |
| 1044 | 1123 | |
| 1045 | 1124 | value = add_data(gen, pvalue, value_size); |
| 1046 | 1125 | key = add_data(gen, &zero, sizeof(zero)); |
| @@ -1068,6 +1147,8 @@ void bpf_gen__map_update_elem(struct bpf_gen *gen, int map_idx, void *pvalue, · 21 unmodified lines | ||
| 1068 | 1147 | emit(gen, BPF_EMIT_CALL(BPF_FUNC_probe_read_kernel)); |
| 1069 | 1148 | |
| 1070 | 1149 | map_update_attr = add_data(gen, &attr, attr_size); |
| 1150 | + pr_debug("gen: map_update_elem: idx %d, value: off %d size %d, attr: off %d size %d\n", | |
| 1151 | + map_idx, value, value_size, map_update_attr, attr_size); | |
| 1071 | 1152 | move_blob2blob(gen, attr_field(map_update_attr, map_fd), 4, |
| 1072 | 1153 | blob_fd_array_off(gen, map_idx)); |
| 1073 | 1154 | emit_rel_store(gen, attr_field(map_update_attr, key), key); |
| @@ -1084,14 +1165,16 @@ void bpf_gen__populate_outer_map(struct bpf_gen *gen, int outer_map_idx, int slo · 10 unmodified lines | ||
| 1084 | 1165 | int attr_size = offsetofend(union bpf_attr, flags); |
| 1085 | 1166 | int map_update_attr, key; |
| 1086 | 1167 | union bpf_attr attr; |
| 1168 | + int tgt_slot; | |
| 1087 | 1169 | |
| 1088 | 1170 | memset(&attr, 0, attr_size); |
| 1089 | − pr_debug("gen: populate_outer_map: outer %d key %d inner %d\n", | |
| 1090 | − outer_map_idx, slot, inner_map_idx); | |
| 1091 | 1171 | |
| 1092 | − key = add_data(gen, &slot, sizeof(slot)); | |
| 1172 | + tgt_slot = tgt_endian(slot); | |
| 1173 | + key = add_data(gen, &tgt_slot, sizeof(tgt_slot)); | |
| 1093 | 1174 | |
| 1094 | 1175 | map_update_attr = add_data(gen, &attr, attr_size); |
| 1176 | + pr_debug("gen: populate_outer_map: outer %d key %d inner %d, attr: off %d size %d\n", | |
| 1177 | + outer_map_idx, slot, inner_map_idx, map_update_attr, attr_size); | |
| 1095 | 1178 | move_blob2blob(gen, attr_field(map_update_attr, map_fd), 4, |
| 1096 | 1179 | blob_fd_array_off(gen, outer_map_idx)); |
| 1097 | 1180 | emit_rel_store(gen, attr_field(map_update_attr, key), key); |
| @@ -1112,8 +1195,9 @@ void bpf_gen__map_freeze(struct bpf_gen *gen, int map_idx) · 14 unmodified lines | ||
| 1112 | 1195 | union bpf_attr attr; |
| 1113 | 1196 | |
| 1114 | 1197 | memset(&attr, 0, attr_size); |
| 1115 | − pr_debug("gen: map_freeze: idx %d\n", map_idx); | |
| 1116 | 1198 | map_freeze_attr = add_data(gen, &attr, attr_size); |
| 1199 | + pr_debug("gen: map_freeze: idx %d, attr: off %d size %d\n", | |
| 1200 | + map_idx, map_freeze_attr, attr_size); | |
| 1117 | 1201 | move_blob2blob(gen, attr_field(map_freeze_attr, map_fd), 4, |
| 1118 | 1202 | blob_fd_array_off(gen, map_idx)); |
| 1119 | 1203 | /* emit MAP_FREEZE command */ |
| @@ -166,8 +166,8 @@ bool hashmap_find(const struct hashmap *map, long key, long *value); · 165 unmodified lines | ||
| 166 | 166 | * @bkt: integer used as a bucket loop cursor |
| 167 | 167 | */ |
| 168 | 168 | #define hashmap__for_each_entry(map, cur, bkt) \ |
| 169 | − for (bkt = 0; bkt < map->cap; bkt++) \ | |
| 170 | − for (cur = map->buckets[bkt]; cur; cur = cur->next) | |
| 169 | + for (bkt = 0; bkt < (map)->cap; bkt++) \ | |
| 170 | + for (cur = (map)->buckets[bkt]; cur; cur = cur->next) | |
| 171 | 171 | |
| 172 | 172 | /* |
| 173 | 173 | * hashmap__for_each_entry_safe - iterate over all entries in hashmap, safe |
| @@ -178,8 +178,8 @@ bool hashmap_find(const struct hashmap *map, long key, long *value); · 4 unmodified lines | ||
| 178 | 178 | * @bkt: integer used as a bucket loop cursor |
| 179 | 179 | */ |
| 180 | 180 | #define hashmap__for_each_entry_safe(map, cur, tmp, bkt) \ |
| 181 | − for (bkt = 0; bkt < map->cap; bkt++) \ | |
| 182 | − for (cur = map->buckets[bkt]; \ | |
| 181 | + for (bkt = 0; bkt < (map)->cap; bkt++) \ | |
| 182 | + for (cur = (map)->buckets[bkt]; \ | |
| 183 | 183 | cur && ({tmp = cur->next; true; }); \ |
| 184 | 184 | cur = tmp) |
| 185 | 185 | |
| @@ -190,19 +190,19 @@ bool hashmap_find(const struct hashmap *map, long key, long *value); · 4 unmodified lines | ||
| 190 | 190 | * @key: key to iterate entries for |
| 191 | 191 | */ |
| 192 | 192 | #define hashmap__for_each_key_entry(map, cur, _key) \ |
| 193 | − for (cur = map->buckets \ | |
| 194 | − ? map->buckets[hash_bits(map->hash_fn((_key), map->ctx), map->cap_bits)] \ | |
| 193 | + for (cur = (map)->buckets \ | |
| 194 | + ? (map)->buckets[hash_bits((map)->hash_fn((_key), (map)->ctx), (map)->cap_bits)] \ | |
| 195 | 195 | : NULL; \ |
| 196 | 196 | cur; \ |
| 197 | 197 | cur = cur->next) \ |
| 198 | − if (map->equal_fn(cur->key, (_key), map->ctx)) | |
| 198 | + if ((map)->equal_fn(cur->key, (_key), (map)->ctx)) | |
| 199 | 199 | |
| 200 | 200 | #define hashmap__for_each_key_entry_safe(map, cur, tmp, _key) \ |
| 201 | − for (cur = map->buckets \ | |
| 202 | − ? map->buckets[hash_bits(map->hash_fn((_key), map->ctx), map->cap_bits)] \ | |
| 201 | + for (cur = (map)->buckets \ | |
| 202 | + ? (map)->buckets[hash_bits((map)->hash_fn((_key), (map)->ctx), (map)->cap_bits)] \ | |
| 203 | 203 | : NULL; \ |
| 204 | 204 | cur && ({ tmp = cur->next; true; }); \ |
| 205 | 205 | cur = tmp) \ |
| 206 | − if (map->equal_fn(cur->key, (_key), map->ctx)) | |
| 206 | + if ((map)->equal_fn(cur->key, (_key), (map)->ctx)) | |
| 207 | 207 | |
| 208 | 208 | #endif /* __LIBBPF_HASHMAP_H */ |
| @@ -133,6 +133,7 @@ static const char * const attach_type_name[] = { · 132 unmodified lines | ||
| 133 | 133 | [BPF_NETKIT_PRIMARY] = "netkit_primary", |
| 134 | 134 | [BPF_NETKIT_PEER] = "netkit_peer", |
| 135 | 135 | [BPF_TRACE_KPROBE_SESSION] = "trace_kprobe_session", |
| 136 | + [BPF_TRACE_UPROBE_SESSION] = "trace_uprobe_session", | |
| 136 | 137 | }; |
| 137 | 138 | |
| 138 | 139 | static const char * const link_type_name[] = { |
| @@ -694,6 +695,8 @@ struct bpf_object { · 555 unmodified lines | ||
| 694 | 695 | /* Information when doing ELF related work. Only valid if efile.elf is not NULL */ |
| 695 | 696 | struct elf_state efile; |
| 696 | 697 | |
| 698 | + unsigned char byteorder; | |
| 699 | + | |
| 697 | 700 | struct btf *btf; |
| 698 | 701 | struct btf_ext *btf_ext; |
| 699 | 702 | |
| @@ -940,6 +943,20 @@ bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data, · 240 unmodified lines | ||
| 940 | 943 | return 0; |
| 941 | 944 | } |
| 942 | 945 | |
| 946 | +static void bpf_object_bswap_progs(struct bpf_object *obj) | |
| 947 | +{ | |
| 948 | + struct bpf_program *prog = obj->programs; | |
| 949 | + struct bpf_insn *insn; | |
| 950 | + int p, i; | |
| 951 | + | |
| 952 | + for (p = 0; p < obj->nr_programs; p++, prog++) { | |
| 953 | + insn = prog->insns; | |
| 954 | + for (i = 0; i < prog->insns_cnt; i++, insn++) | |
| 955 | + bpf_insn_bswap(insn); | |
| 956 | + } | |
| 957 | + pr_debug("converted %zu BPF programs to native byte order\n", obj->nr_programs); | |
| 958 | +} | |
| 959 | + | |
| 943 | 960 | static const struct btf_member * |
| 944 | 961 | find_member_by_offset(const struct btf_type *t, __u32 bit_offset) |
| 945 | 962 | { |
| @@ -1506,6 +1523,7 @@ static void bpf_object__elf_finish(struct bpf_object *obj) · 560 unmodified lines | ||
| 1506 | 1523 | |
| 1507 | 1524 | elf_end(obj->efile.elf); |
| 1508 | 1525 | obj->efile.elf = NULL; |
| 1526 | + obj->efile.ehdr = NULL; | |
| 1509 | 1527 | obj->efile.symbols = NULL; |
| 1510 | 1528 | obj->efile.arena_data = NULL; |
| 1511 | 1529 | |
| @@ -1533,11 +1551,8 @@ static int bpf_object__elf_init(struct bpf_object *obj) · 21 unmodified lines | ||
| 1533 | 1551 | } else { |
| 1534 | 1552 | obj->efile.fd = open(obj->path, O_RDONLY | O_CLOEXEC); |
| 1535 | 1553 | if (obj->efile.fd < 0) { |
| 1536 | − char errmsg[STRERR_BUFSIZE], *cp; | |
| 1537 | − | |
| 1538 | 1554 | err = -errno; |
| 1539 | − cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); | |
| 1540 | − pr_warn("elf: failed to open %s: %s\n", obj->path, cp); | |
| 1555 | + pr_warn("elf: failed to open %s: %s\n", obj->path, errstr(err)); | |
| 1541 | 1556 | return err; |
| 1542 | 1557 | } |
| 1543 | 1558 | |
| @@ -1571,6 +1586,16 @@ static int bpf_object__elf_init(struct bpf_object *obj) · 27 unmodified lines | ||
| 1571 | 1586 | goto errout; |
| 1572 | 1587 | } |
| 1573 | 1588 | |
| 1589 | + /* Validate ELF object endianness... */ | |
| 1590 | + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB && | |
| 1591 | + ehdr->e_ident[EI_DATA] != ELFDATA2MSB) { | |
| 1592 | + err = -LIBBPF_ERRNO__ENDIAN; | |
| 1593 | + pr_warn("elf: '%s' has unknown byte order\n", obj->path); | |
| 1594 | + goto errout; | |
| 1595 | + } | |
| 1596 | + /* and save after bpf_object_open() frees ELF data */ | |
| 1597 | + obj->byteorder = ehdr->e_ident[EI_DATA]; | |
| 1598 | + | |
| 1574 | 1599 | if (elf_getshdrstrndx(elf, &obj->efile.shstrndx)) { |
| 1575 | 1600 | pr_warn("elf: failed to get section names section index for %s: %s\n", |
| 1576 | 1601 | obj->path, elf_errmsg(-1)); |
| @@ -1599,19 +1624,15 @@ errout: · 22 unmodified lines | ||
| 1599 | 1624 | return err; |
| 1600 | 1625 | } |
| 1601 | 1626 | |
| 1602 | −static int bpf_object__check_endianness(struct bpf_object *obj) | |
| 1627 | +static bool is_native_endianness(struct bpf_object *obj) | |
| 1603 | 1628 | { |
| 1604 | 1629 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| 1605 | − if (obj->efile.ehdr->e_ident[EI_DATA] == ELFDATA2LSB) | |
| 1606 | − return 0; | |
| 1630 | + return obj->byteorder == ELFDATA2LSB; | |
| 1607 | 1631 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| 1608 | − if (obj->efile.ehdr->e_ident[EI_DATA] == ELFDATA2MSB) | |
| 1609 | − return 0; | |
| 1632 | + return obj->byteorder == ELFDATA2MSB; | |
| 1610 | 1633 | #else |
| 1611 | 1634 | # error "Unrecognized __BYTE_ORDER__" |
| 1612 | 1635 | #endif |
| 1613 | − pr_warn("elf: endianness mismatch in %s.\n", obj->path); | |
| 1614 | − return -LIBBPF_ERRNO__ENDIAN; | |
| 1615 | 1636 | } |
| 1616 | 1637 | |
| 1617 | 1638 | static int |
| @@ -1937,8 +1958,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, · 319 unmodified lines | ||
| 1937 | 1958 | if (map->mmaped == MAP_FAILED) { |
| 1938 | 1959 | err = -errno; |
| 1939 | 1960 | map->mmaped = NULL; |
| 1940 | − pr_warn("failed to alloc map '%s' content buffer: %d\n", | |
| 1941 | − map->name, err); | |
| 1961 | + pr_warn("failed to alloc map '%s' content buffer: %s\n", map->name, errstr(err)); | |
| 1942 | 1962 | zfree(&map->real_name); |
| 1943 | 1963 | zfree(&map->name); |
| 1944 | 1964 | return err; |
| @@ -2102,7 +2122,7 @@ static int parse_u64(const char *value, __u64 *res) · 157 unmodified lines | ||
| 2102 | 2122 | *res = strtoull(value, &value_end, 0); |
| 2103 | 2123 | if (errno) { |
| 2104 | 2124 | err = -errno; |
| 2105 | − pr_warn("failed to parse '%s' as integer: %d\n", value, err); | |
| 2125 | + pr_warn("failed to parse '%s': %s\n", value, errstr(err)); | |
| 2106 | 2126 | return err; |
| 2107 | 2127 | } |
| 2108 | 2128 | if (*value_end) { |
| @@ -2268,8 +2288,8 @@ static int bpf_object__read_kconfig_file(struct bpf_object *obj, void *data) · 159 unmodified lines | ||
| 2268 | 2288 | while (gzgets(file, buf, sizeof(buf))) { |
| 2269 | 2289 | err = bpf_object__process_kconfig_line(obj, buf, data); |
| 2270 | 2290 | if (err) { |
| 2271 | − pr_warn("error parsing system Kconfig line '%s': %d\n", | |
| 2272 | − buf, err); | |
| 2291 | + pr_warn("error parsing system Kconfig line '%s': %s\n", | |
| 2292 | + buf, errstr(err)); | |
| 2273 | 2293 | goto out; |
| 2274 | 2294 | } |
| 2275 | 2295 | } |
| @@ -2289,15 +2309,15 @@ static int bpf_object__read_kconfig_mem(struct bpf_object *obj, · 13 unmodified lines | ||
| 2289 | 2309 | file = fmemopen((void *)config, strlen(config), "r"); |
| 2290 | 2310 | if (!file) { |
| 2291 | 2311 | err = -errno; |
| 2292 | − pr_warn("failed to open in-memory Kconfig: %d\n", err); | |
| 2312 | + pr_warn("failed to open in-memory Kconfig: %s\n", errstr(err)); | |
| 2293 | 2313 | return err; |
| 2294 | 2314 | } |
| 2295 | 2315 | |
| 2296 | 2316 | while (fgets(buf, sizeof(buf), file)) { |
| 2297 | 2317 | err = bpf_object__process_kconfig_line(obj, buf, data); |
| 2298 | 2318 | if (err) { |
| 2299 | − pr_warn("error parsing in-memory Kconfig line '%s': %d\n", | |
| 2300 | − buf, err); | |
| 2319 | + pr_warn("error parsing in-memory Kconfig line '%s': %s\n", | |
| 2320 | + buf, errstr(err)); | |
| 2301 | 2321 | break; |
| 2302 | 2322 | } |
| 2303 | 2323 | } |
| @@ -3212,7 +3232,7 @@ static int bpf_object__init_btf(struct bpf_object *obj, · 908 unmodified lines | ||
| 3212 | 3232 | err = libbpf_get_error(obj->btf); |
| 3213 | 3233 | if (err) { |
| 3214 | 3234 | obj->btf = NULL; |
| 3215 | − pr_warn("Error loading ELF section %s: %d.\n", BTF_ELF_SEC, err); | |
| 3235 | + pr_warn("Error loading ELF section %s: %s.\n", BTF_ELF_SEC, errstr(err)); | |
| 3216 | 3236 | goto out; |
| 3217 | 3237 | } |
| 3218 | 3238 | /* enforce 8-byte pointers for BPF-targeted BTFs */ |
| @@ -3230,8 +3250,8 @@ static int bpf_object__init_btf(struct bpf_object *obj, · 11 unmodified lines | ||
| 3230 | 3250 | obj->btf_ext = btf_ext__new(btf_ext_data->d_buf, btf_ext_data->d_size); |
| 3231 | 3251 | err = libbpf_get_error(obj->btf_ext); |
| 3232 | 3252 | if (err) { |
| 3233 | − pr_warn("Error loading ELF section %s: %d. Ignored and continue.\n", | |
| 3234 | − BTF_EXT_ELF_SEC, err); | |
| 3253 | + pr_warn("Error loading ELF section %s: %s. Ignored and continue.\n", | |
| 3254 | + BTF_EXT_ELF_SEC, errstr(err)); | |
| 3235 | 3255 | obj->btf_ext = NULL; |
| 3236 | 3256 | goto out; |
| 3237 | 3257 | } |
| @@ -3323,8 +3343,8 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf, · 85 unmodified lines | ||
| 3323 | 3343 | if (t->size == 0) { |
| 3324 | 3344 | err = find_elf_sec_sz(obj, sec_name, &size); |
| 3325 | 3345 | if (err || !size) { |
| 3326 | − pr_debug("sec '%s': failed to determine size from ELF: size %u, err %d\n", | |
| 3327 | − sec_name, size, err); | |
| 3346 | + pr_debug("sec '%s': failed to determine size from ELF: size %u, err %s\n", | |
| 3347 | + sec_name, size, errstr(err)); | |
| 3328 | 3348 | return -ENOENT; |
| 3329 | 3349 | } |
| 3330 | 3350 | |
| @@ -3478,7 +3498,7 @@ static int bpf_object__load_vmlinux_btf(struct bpf_object *obj, bool force) · 147 unmodified lines | ||
| 3478 | 3498 | obj->btf_vmlinux = btf__load_vmlinux_btf(); |
| 3479 | 3499 | err = libbpf_get_error(obj->btf_vmlinux); |
| 3480 | 3500 | if (err) { |
| 3481 | − pr_warn("Error loading vmlinux BTF: %d\n", err); | |
| 3501 | + pr_warn("Error loading vmlinux BTF: %s\n", errstr(err)); | |
| 3482 | 3502 | obj->btf_vmlinux = NULL; |
| 3483 | 3503 | return err; |
| 3484 | 3504 | } |
| @@ -3581,11 +3601,14 @@ static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj) · 96 unmodified lines | ||
| 3581 | 3601 | report: |
| 3582 | 3602 | if (err) { |
| 3583 | 3603 | btf_mandatory = kernel_needs_btf(obj); |
| 3584 | − pr_warn("Error loading .BTF into kernel: %d. %s\n", err, | |
| 3585 | − btf_mandatory ? "BTF is mandatory, can't proceed." | |
| 3586 | − : "BTF is optional, ignoring."); | |
| 3587 | − if (!btf_mandatory) | |
| 3604 | + if (btf_mandatory) { | |
| 3605 | + pr_warn("Error loading .BTF into kernel: %s. BTF is mandatory, can't proceed.\n", | |
| 3606 | + errstr(err)); | |
| 3607 | + } else { | |
| 3608 | + pr_info("Error loading .BTF into kernel: %s. BTF is optional, ignoring.\n", | |
| 3609 | + errstr(err)); | |
| 3588 | 3610 | err = 0; |
| 3611 | + } | |
| 3589 | 3612 | } |
| 3590 | 3613 | return err; |
| 3591 | 3614 | } |
| @@ -3953,6 +3976,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj) · 361 unmodified lines | ||
| 3953 | 3976 | return -LIBBPF_ERRNO__FORMAT; |
| 3954 | 3977 | } |
| 3955 | 3978 | |
| 3979 | + /* change BPF program insns to native endianness for introspection */ | |
| 3980 | + if (!is_native_endianness(obj)) | |
| 3981 | + bpf_object_bswap_progs(obj); | |
| 3982 | + | |
| 3956 | 3983 | /* sort BPF programs by section name and in-section instruction offset |
| 3957 | 3984 | * for faster search |
| 3958 | 3985 | */ |
| @@ -3985,7 +4012,7 @@ static bool sym_is_subprog(const Elf64_Sym *sym, int text_shndx) · 26 unmodified lines | ||
| 3985 | 4012 | return true; |
| 3986 | 4013 | |
| 3987 | 4014 | /* global function */ |
| 3988 | − return bind == STB_GLOBAL && type == STT_FUNC; | |
| 4015 | + return (bind == STB_GLOBAL || bind == STB_WEAK) && type == STT_FUNC; | |
| 3989 | 4016 | } |
| 3990 | 4017 | |
| 3991 | 4018 | static int find_extern_btf_id(const struct btf *btf, const char *ext_name) |
| @@ -4389,7 +4416,7 @@ static int bpf_object__collect_externs(struct bpf_object *obj) · 397 unmodified lines | ||
| 4389 | 4416 | |
| 4390 | 4417 | static bool prog_is_subprog(const struct bpf_object *obj, const struct bpf_program *prog) |
| 4391 | 4418 | { |
| 4392 | − return prog->sec_idx == obj->efile.text_shndx && obj->nr_programs > 1; | |
| 4419 | + return prog->sec_idx == obj->efile.text_shndx; | |
| 4393 | 4420 | } |
| 4394 | 4421 | |
| 4395 | 4422 | struct bpf_program * |
| @@ -4783,8 +4810,8 @@ static int bpf_get_map_info_from_fdinfo(int fd, struct bpf_map_info *info) · 387 unmodified lines | ||
| 4783 | 4810 | fp = fopen(file, "re"); |
| 4784 | 4811 | if (!fp) { |
| 4785 | 4812 | err = -errno; |
| 4786 | − pr_warn("failed to open %s: %d. No procfs support?\n", file, | |
| 4787 | − err); | |
| 4813 | + pr_warn("failed to open %s: %s. No procfs support?\n", file, | |
| 4814 | + errstr(err)); | |
| 4788 | 4815 | return err; |
| 4789 | 4816 | } |
| 4790 | 4817 | |
| @@ -4939,8 +4966,8 @@ static int bpf_object_prepare_token(struct bpf_object *obj) · 148 unmodified lines | ||
| 4939 | 4966 | bpffs_fd = open(bpffs_path, O_DIRECTORY, O_RDWR); |
| 4940 | 4967 | if (bpffs_fd < 0) { |
| 4941 | 4968 | err = -errno; |
| 4942 | − __pr(level, "object '%s': failed (%d) to open BPF FS mount at '%s'%s\n", | |
| 4943 | − obj->name, err, bpffs_path, | |
| 4969 | + __pr(level, "object '%s': failed (%s) to open BPF FS mount at '%s'%s\n", | |
| 4970 | + obj->name, errstr(err), bpffs_path, | |
| 4944 | 4971 | mandatory ? "" : ", skipping optional step..."); |
| 4945 | 4972 | return mandatory ? err : 0; |
| 4946 | 4973 | } |
| @@ -4974,7 +5001,6 @@ static int bpf_object_prepare_token(struct bpf_object *obj) · 27 unmodified lines | ||
| 4974 | 5001 | static int |
| 4975 | 5002 | bpf_object__probe_loading(struct bpf_object *obj) |
| 4976 | 5003 | { |
| 4977 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 4978 | 5004 | struct bpf_insn insns[] = { |
| 4979 | 5005 | BPF_MOV64_IMM(BPF_REG_0, 0), |
| 4980 | 5006 | BPF_EXIT_INSN(), |
| @@ -4990,7 +5016,8 @@ bpf_object__probe_loading(struct bpf_object *obj) · 9 unmodified lines | ||
| 4990 | 5016 | |
| 4991 | 5017 | ret = bump_rlimit_memlock(); |
| 4992 | 5018 | if (ret) |
| 4993 | − pr_warn("Failed to bump RLIMIT_MEMLOCK (err = %d), you might need to do it explicitly!\n", ret); | |
| 5019 | + pr_warn("Failed to bump RLIMIT_MEMLOCK (err = %s), you might need to do it explicitly!\n", | |
| 5020 | + errstr(ret)); | |
| 4994 | 5021 | |
| 4995 | 5022 | /* make sure basic loading works */ |
| 4996 | 5023 | ret = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, insn_cnt, &opts); |
| @@ -4998,11 +5025,8 @@ bpf_object__probe_loading(struct bpf_object *obj) · 1 unmodified line | ||
| 4998 | 5025 | ret = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, NULL, "GPL", insns, insn_cnt, &opts); |
| 4999 | 5026 | if (ret < 0) { |
| 5000 | 5027 | ret = errno; |
| 5001 | − cp = libbpf_strerror_r(ret, errmsg, sizeof(errmsg)); | |
| 5002 | − pr_warn("Error in %s():%s(%d). Couldn't load trivial BPF " | |
| 5003 | − "program. Make sure your kernel supports BPF " | |
| 5004 | − "(CONFIG_BPF_SYSCALL=y) and/or that RLIMIT_MEMLOCK is " | |
| 5005 | − "set to big enough value.\n", __func__, cp, ret); | |
| 5028 | + pr_warn("Error in %s(): %s. Couldn't load trivial BPF program. Make sure your kernel supports BPF (CONFIG_BPF_SYSCALL=y) and/or that RLIMIT_MEMLOCK is set to big enough value.\n", | |
| 5029 | + __func__, errstr(ret)); | |
| 5006 | 5030 | return -ret; |
| 5007 | 5031 | } |
| 5008 | 5032 | close(ret); |
| @@ -5027,7 +5051,6 @@ bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id) · 18 unmodified lines | ||
| 5027 | 5051 | static bool map_is_reuse_compat(const struct bpf_map *map, int map_fd) |
| 5028 | 5052 | { |
| 5029 | 5053 | struct bpf_map_info map_info; |
| 5030 | − char msg[STRERR_BUFSIZE]; | |
| 5031 | 5054 | __u32 map_info_len = sizeof(map_info); |
| 5032 | 5055 | int err; |
| 5033 | 5056 | |
| @@ -5037,7 +5060,7 @@ static bool map_is_reuse_compat(const struct bpf_map *map, int map_fd) · 3 unmodified lines | ||
| 5037 | 5060 | err = bpf_get_map_info_from_fdinfo(map_fd, &map_info); |
| 5038 | 5061 | if (err) { |
| 5039 | 5062 | pr_warn("failed to get map info for map FD %d: %s\n", map_fd, |
| 5040 | − libbpf_strerror_r(errno, msg, sizeof(msg))); | |
| 5063 | + errstr(err)); | |
| 5041 | 5064 | return false; |
| 5042 | 5065 | } |
| 5043 | 5066 | |
| @@ -5052,7 +5075,6 @@ static bool map_is_reuse_compat(const struct bpf_map *map, int map_fd) · 8 unmodified lines | ||
| 5052 | 5075 | static int |
| 5053 | 5076 | bpf_object__reuse_map(struct bpf_map *map) |
| 5054 | 5077 | { |
| 5055 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 5056 | 5078 | int err, pin_fd; |
| 5057 | 5079 | |
| 5058 | 5080 | pin_fd = bpf_obj_get(map->pin_path); |
| @@ -5064,9 +5086,8 @@ bpf_object__reuse_map(struct bpf_map *map) · 5 unmodified lines | ||
| 5064 | 5086 | return 0; |
| 5065 | 5087 | } |
| 5066 | 5088 | |
| 5067 | − cp = libbpf_strerror_r(-err, errmsg, sizeof(errmsg)); | |
| 5068 | 5089 | pr_warn("couldn't retrieve pinned map '%s': %s\n", |
| 5069 | − map->pin_path, cp); | |
| 5090 | + map->pin_path, errstr(err)); | |
| 5070 | 5091 | return err; |
| 5071 | 5092 | } |
| 5072 | 5093 | |
| @@ -5092,8 +5113,8 @@ static int · 19 unmodified lines | ||
| 5092 | 5113 | bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map) |
| 5093 | 5114 | { |
| 5094 | 5115 | enum libbpf_map_type map_type = map->libbpf_type; |
| 5095 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 5096 | 5116 | int err, zero = 0; |
| 5117 | + size_t mmap_sz; | |
| 5097 | 5118 | |
| 5098 | 5119 | if (obj->gen_loader) { |
| 5099 | 5120 | bpf_gen__map_update_elem(obj->gen_loader, map - obj->maps, |
| @@ -5106,9 +5127,8 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map) · 6 unmodified lines | ||
| 5106 | 5127 | err = bpf_map_update_elem(map->fd, &zero, map->mmaped, 0); |
| 5107 | 5128 | if (err) { |
| 5108 | 5129 | err = -errno; |
| 5109 | − cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); | |
| 5110 | − pr_warn("Error setting initial map(%s) contents: %s\n", | |
| 5111 | − map->name, cp); | |
| 5130 | + pr_warn("map '%s': failed to set initial contents: %s\n", | |
| 5131 | + bpf_map__name(map), errstr(err)); | |
| 5112 | 5132 | return err; |
| 5113 | 5133 | } |
| 5114 | 5134 | |
| @@ -5117,12 +5137,43 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map) · 2 unmodified lines | ||
| 5117 | 5137 | err = bpf_map_freeze(map->fd); |
| 5118 | 5138 | if (err) { |
| 5119 | 5139 | err = -errno; |
| 5120 | − cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); | |
| 5121 | − pr_warn("Error freezing map(%s) as read-only: %s\n", | |
| 5122 | − map->name, cp); | |
| 5140 | + pr_warn("map '%s': failed to freeze as read-only: %s\n", | |
| 5141 | + bpf_map__name(map), errstr(err)); | |
| 5123 | 5142 | return err; |
| 5124 | 5143 | } |
| 5125 | 5144 | } |
| 5145 | + | |
| 5146 | + /* Remap anonymous mmap()-ed "map initialization image" as | |
| 5147 | + * a BPF map-backed mmap()-ed memory, but preserving the same | |
| 5148 | + * memory address. This will cause kernel to change process' | |
| 5149 | + * page table to point to a different piece of kernel memory, | |
| 5150 | + * but from userspace point of view memory address (and its | |
| 5151 | + * contents, being identical at this point) will stay the | |
| 5152 | + * same. This mapping will be released by bpf_object__close() | |
| 5153 | + * as per normal clean up procedure. | |
| 5154 | + */ | |
| 5155 | + mmap_sz = bpf_map_mmap_sz(map); | |
| 5156 | + if (map->def.map_flags & BPF_F_MMAPABLE) { | |
| 5157 | + void *mmaped; | |
| 5158 | + int prot; | |
| 5159 | + | |
| 5160 | + if (map->def.map_flags & BPF_F_RDONLY_PROG) | |
| 5161 | + prot = PROT_READ; | |
| 5162 | + else | |
| 5163 | + prot = PROT_READ | PROT_WRITE; | |
| 5164 | + mmaped = mmap(map->mmaped, mmap_sz, prot, MAP_SHARED | MAP_FIXED, map->fd, 0); | |
| 5165 | + if (mmaped == MAP_FAILED) { | |
| 5166 | + err = -errno; | |
| 5167 | + pr_warn("map '%s': failed to re-mmap() contents: %s\n", | |
| 5168 | + bpf_map__name(map), errstr(err)); | |
| 5169 | + return err; | |
| 5170 | + } | |
| 5171 | + map->mmaped = mmaped; | |
| 5172 | + } else if (map->mmaped) { | |
| 5173 | + munmap(map->mmaped, mmap_sz); | |
| 5174 | + map->mmaped = NULL; | |
| 5175 | + } | |
| 5176 | + | |
| 5126 | 5177 | return 0; |
| 5127 | 5178 | } |
| 5128 | 5179 | |
| @@ -5171,8 +5222,8 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b · 42 unmodified lines | ||
| 5171 | 5222 | return err; |
| 5172 | 5223 | err = bpf_object__create_map(obj, map->inner_map, true); |
| 5173 | 5224 | if (err) { |
| 5174 | − pr_warn("map '%s': failed to create inner map: %d\n", | |
| 5175 | − map->name, err); | |
| 5225 | + pr_warn("map '%s': failed to create inner map: %s\n", | |
| 5226 | + map->name, errstr(err)); | |
| 5176 | 5227 | return err; |
| 5177 | 5228 | } |
| 5178 | 5229 | map->inner_map_fd = map->inner_map->fd; |
| @@ -5226,12 +5277,9 @@ static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, b · 47 unmodified lines | ||
| 5226 | 5277 | def->max_entries, &create_attr); |
| 5227 | 5278 | } |
| 5228 | 5279 | if (map_fd < 0 && (create_attr.btf_key_type_id || create_attr.btf_value_type_id)) { |
| 5229 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 5230 | − | |
| 5231 | 5280 | err = -errno; |
| 5232 | − cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); | |
| 5233 | − pr_warn("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n", | |
| 5234 | − map->name, cp, err); | |
| 5281 | + pr_warn("Error in bpf_create_map_xattr(%s): %s. Retrying without BTF.\n", | |
| 5282 | + map->name, errstr(err)); | |
| 5235 | 5283 | create_attr.btf_fd = 0; |
| 5236 | 5284 | create_attr.btf_key_type_id = 0; |
| 5237 | 5285 | create_attr.btf_value_type_id = 0; |
| @@ -5286,8 +5334,8 @@ static int init_map_in_map_slots(struct bpf_object *obj, struct bpf_map *map) · 48 unmodified lines | ||
| 5286 | 5334 | } |
| 5287 | 5335 | if (err) { |
| 5288 | 5336 | err = -errno; |
| 5289 | − pr_warn("map '%s': failed to initialize slot [%d] to map '%s' fd=%d: %d\n", | |
| 5290 | − map->name, i, targ_map->name, fd, err); | |
| 5337 | + pr_warn("map '%s': failed to initialize slot [%d] to map '%s' fd=%d: %s\n", | |
| 5338 | + map->name, i, targ_map->name, fd, errstr(err)); | |
| 5291 | 5339 | return err; |
| 5292 | 5340 | } |
| 5293 | 5341 | pr_debug("map '%s': slot [%d] set to map '%s' fd=%d\n", |
| @@ -5319,8 +5367,8 @@ static int init_prog_array_slots(struct bpf_object *obj, struct bpf_map *map) · 25 unmodified lines | ||
| 5319 | 5367 | err = bpf_map_update_elem(map->fd, &i, &fd, 0); |
| 5320 | 5368 | if (err) { |
| 5321 | 5369 | err = -errno; |
| 5322 | − pr_warn("map '%s': failed to initialize slot [%d] to prog '%s' fd=%d: %d\n", | |
| 5323 | − map->name, i, targ_prog->name, fd, err); | |
| 5370 | + pr_warn("map '%s': failed to initialize slot [%d] to prog '%s' fd=%d: %s\n", | |
| 5371 | + map->name, i, targ_prog->name, fd, errstr(err)); | |
| 5324 | 5372 | return err; |
| 5325 | 5373 | } |
| 5326 | 5374 | pr_debug("map '%s': slot [%d] set to prog '%s' fd=%d\n", |
| @@ -5373,7 +5421,6 @@ static int · 46 unmodified lines | ||
| 5373 | 5421 | bpf_object__create_maps(struct bpf_object *obj) |
| 5374 | 5422 | { |
| 5375 | 5423 | struct bpf_map *map; |
| 5376 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 5377 | 5424 | unsigned int i, j; |
| 5378 | 5425 | int err; |
| 5379 | 5426 | bool retried; |
| @@ -5439,8 +5486,7 @@ retry: · 59 unmodified lines | ||
| 5439 | 5486 | err = bpf_object__populate_internal_map(obj, map); |
| 5440 | 5487 | if (err < 0) |
| 5441 | 5488 | goto err_out; |
| 5442 | − } | |
| 5443 | − if (map->def.type == BPF_MAP_TYPE_ARENA) { | |
| 5489 | + } else if (map->def.type == BPF_MAP_TYPE_ARENA) { | |
| 5444 | 5490 | map->mmaped = mmap((void *)(long)map->map_extra, |
| 5445 | 5491 | bpf_map_mmap_sz(map), PROT_READ | PROT_WRITE, |
| 5446 | 5492 | map->map_extra ? MAP_SHARED | MAP_FIXED : MAP_SHARED, |
| @@ -5448,8 +5494,8 @@ retry: · 1 unmodified line | ||
| 5448 | 5494 | if (map->mmaped == MAP_FAILED) { |
| 5449 | 5495 | err = -errno; |
| 5450 | 5496 | map->mmaped = NULL; |
| 5451 | − pr_warn("map '%s': failed to mmap arena: %d\n", | |
| 5452 | − map->name, err); | |
| 5497 | + pr_warn("map '%s': failed to mmap arena: %s\n", | |
| 5498 | + map->name, errstr(err)); | |
| 5453 | 5499 | return err; |
| 5454 | 5500 | } |
| 5455 | 5501 | if (obj->arena_data) { |
| @@ -5471,8 +5517,8 @@ retry: · 15 unmodified lines | ||
| 5471 | 5517 | retried = true; |
| 5472 | 5518 | goto retry; |
| 5473 | 5519 | } |
| 5474 | − pr_warn("map '%s': failed to auto-pin at '%s': %d\n", | |
| 5475 | − map->name, map->pin_path, err); | |
| 5520 | + pr_warn("map '%s': failed to auto-pin at '%s': %s\n", | |
| 5521 | + map->name, map->pin_path, errstr(err)); | |
| 5476 | 5522 | goto err_out; |
| 5477 | 5523 | } |
| 5478 | 5524 | } |
| @@ -5481,8 +5527,7 @@ retry: · 2 unmodified lines | ||
| 5481 | 5527 | return 0; |
| 5482 | 5528 | |
| 5483 | 5529 | err_out: |
| 5484 | − cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); | |
| 5485 | − pr_warn("map '%s': failed to create: %s(%d)\n", map->name, cp, err); | |
| 5530 | + pr_warn("map '%s': failed to create: %s\n", map->name, errstr(err)); | |
| 5486 | 5531 | pr_perm_msg(err); |
| 5487 | 5532 | for (j = 0; j < i; j++) |
| 5488 | 5533 | zclose(obj->maps[j].fd); |
| @@ -5606,7 +5651,7 @@ static int load_module_btfs(struct bpf_object *obj) · 117 unmodified lines | ||
| 5606 | 5651 | } |
| 5607 | 5652 | if (err) { |
| 5608 | 5653 | err = -errno; |
| 5609 | − pr_warn("failed to iterate BTF objects: %d\n", err); | |
| 5654 | + pr_warn("failed to iterate BTF objects: %s\n", errstr(err)); | |
| 5610 | 5655 | return err; |
| 5611 | 5656 | } |
| 5612 | 5657 | |
| @@ -5615,7 +5660,7 @@ static int load_module_btfs(struct bpf_object *obj) · 2 unmodified lines | ||
| 5615 | 5660 | if (errno == ENOENT) |
| 5616 | 5661 | continue; /* expected race: BTF was unloaded */ |
| 5617 | 5662 | err = -errno; |
| 5618 | − pr_warn("failed to get BTF object #%d FD: %d\n", id, err); | |
| 5663 | + pr_warn("failed to get BTF object #%d FD: %s\n", id, errstr(err)); | |
| 5619 | 5664 | return err; |
| 5620 | 5665 | } |
| 5621 | 5666 | |
| @@ -5627,7 +5672,7 @@ static int load_module_btfs(struct bpf_object *obj) · 5 unmodified lines | ||
| 5627 | 5672 | err = bpf_btf_get_info_by_fd(fd, &info, &len); |
| 5628 | 5673 | if (err) { |
| 5629 | 5674 | err = -errno; |
| 5630 | − pr_warn("failed to get BTF object #%d info: %d\n", id, err); | |
| 5675 | + pr_warn("failed to get BTF object #%d info: %s\n", id, errstr(err)); | |
| 5631 | 5676 | goto err_out; |
| 5632 | 5677 | } |
| 5633 | 5678 | |
| @@ -5640,8 +5685,8 @@ static int load_module_btfs(struct bpf_object *obj) · 6 unmodified lines | ||
| 5640 | 5685 | btf = btf_get_from_fd(fd, obj->btf_vmlinux); |
| 5641 | 5686 | err = libbpf_get_error(btf); |
| 5642 | 5687 | if (err) { |
| 5643 | − pr_warn("failed to load module [%s]'s BTF object #%d: %d\n", | |
| 5644 | − name, id, err); | |
| 5688 | + pr_warn("failed to load module [%s]'s BTF object #%d: %s\n", | |
| 5689 | + name, id, errstr(err)); | |
| 5645 | 5690 | goto err_out; |
| 5646 | 5691 | } |
| 5647 | 5692 | |
| @@ -5870,7 +5915,7 @@ bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path) · 222 unmodified lines | ||
| 5870 | 5915 | obj->btf_vmlinux_override = btf__parse(targ_btf_path, NULL); |
| 5871 | 5916 | err = libbpf_get_error(obj->btf_vmlinux_override); |
| 5872 | 5917 | if (err) { |
| 5873 | − pr_warn("failed to parse target BTF: %d\n", err); | |
| 5918 | + pr_warn("failed to parse target BTF: %s\n", errstr(err)); | |
| 5874 | 5919 | return err; |
| 5875 | 5920 | } |
| 5876 | 5921 | } |
| @@ -5930,8 +5975,8 @@ bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path) · 53 unmodified lines | ||
| 5930 | 5975 | |
| 5931 | 5976 | err = record_relo_core(prog, rec, insn_idx); |
| 5932 | 5977 | if (err) { |
| 5933 | − pr_warn("prog '%s': relo #%d: failed to record relocation: %d\n", | |
| 5934 | − prog->name, i, err); | |
| 5978 | + pr_warn("prog '%s': relo #%d: failed to record relocation: %s\n", | |
| 5979 | + prog->name, i, errstr(err)); | |
| 5935 | 5980 | goto out; |
| 5936 | 5981 | } |
| 5937 | 5982 | |
| @@ -5940,15 +5985,15 @@ bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path) · 2 unmodified lines | ||
| 5940 | 5985 | |
| 5941 | 5986 | err = bpf_core_resolve_relo(prog, rec, i, obj->btf, cand_cache, &targ_res); |
| 5942 | 5987 | if (err) { |
| 5943 | − pr_warn("prog '%s': relo #%d: failed to relocate: %d\n", | |
| 5944 | − prog->name, i, err); | |
| 5988 | + pr_warn("prog '%s': relo #%d: failed to relocate: %s\n", | |
| 5989 | + prog->name, i, errstr(err)); | |
| 5945 | 5990 | goto out; |
| 5946 | 5991 | } |
| 5947 | 5992 | |
| 5948 | 5993 | err = bpf_core_patch_insn(prog->name, insn, insn_idx, rec, i, &targ_res); |
| 5949 | 5994 | if (err) { |
| 5950 | − pr_warn("prog '%s': relo #%d: failed to patch insn #%u: %d\n", | |
| 5951 | − prog->name, i, insn_idx, err); | |
| 5995 | + pr_warn("prog '%s': relo #%d: failed to patch insn #%u: %s\n", | |
| 5996 | + prog->name, i, insn_idx, errstr(err)); | |
| 5952 | 5997 | goto out; |
| 5953 | 5998 | } |
| 5954 | 5999 | } |
| @@ -6216,8 +6261,8 @@ reloc_prog_func_and_line_info(const struct bpf_object *obj, · 261 unmodified lines | ||
| 6216 | 6261 | &main_prog->func_info_rec_size); |
| 6217 | 6262 | if (err) { |
| 6218 | 6263 | if (err != -ENOENT) { |
| 6219 | − pr_warn("prog '%s': error relocating .BTF.ext function info: %d\n", | |
| 6220 | − prog->name, err); | |
| 6264 | + pr_warn("prog '%s': error relocating .BTF.ext function info: %s\n", | |
| 6265 | + prog->name, errstr(err)); | |
| 6221 | 6266 | return err; |
| 6222 | 6267 | } |
| 6223 | 6268 | if (main_prog->func_info) { |
| @@ -6244,8 +6289,8 @@ line_info: · 20 unmodified lines | ||
| 6244 | 6289 | &main_prog->line_info_rec_size); |
| 6245 | 6290 | if (err) { |
| 6246 | 6291 | if (err != -ENOENT) { |
| 6247 | − pr_warn("prog '%s': error relocating .BTF.ext line info: %d\n", | |
| 6248 | − prog->name, err); | |
| 6292 | + pr_warn("prog '%s': error relocating .BTF.ext line info: %s\n", | |
| 6293 | + prog->name, errstr(err)); | |
| 6249 | 6294 | return err; |
| 6250 | 6295 | } |
| 6251 | 6296 | if (main_prog->line_info) { |
| @@ -7009,8 +7054,8 @@ static int bpf_object__relocate(struct bpf_object *obj, const char *targ_btf_pat · 757 unmodified lines | ||
| 7009 | 7054 | if (obj->btf_ext) { |
| 7010 | 7055 | err = bpf_object__relocate_core(obj, targ_btf_path); |
| 7011 | 7056 | if (err) { |
| 7012 | − pr_warn("failed to perform CO-RE relocations: %d\n", | |
| 7013 | − err); | |
| 7057 | + pr_warn("failed to perform CO-RE relocations: %s\n", | |
| 7058 | + errstr(err)); | |
| 7014 | 7059 | return err; |
| 7015 | 7060 | } |
| 7016 | 7061 | bpf_object__sort_relos(obj); |
| @@ -7054,8 +7099,8 @@ static int bpf_object__relocate(struct bpf_object *obj, const char *targ_btf_pat · 37 unmodified lines | ||
| 7054 | 7099 | |
| 7055 | 7100 | err = bpf_object__relocate_calls(obj, prog); |
| 7056 | 7101 | if (err) { |
| 7057 | − pr_warn("prog '%s': failed to relocate calls: %d\n", | |
| 7058 | − prog->name, err); | |
| 7102 | + pr_warn("prog '%s': failed to relocate calls: %s\n", | |
| 7103 | + prog->name, errstr(err)); | |
| 7059 | 7104 | return err; |
| 7060 | 7105 | } |
| 7061 | 7106 | |
| @@ -7091,16 +7136,16 @@ static int bpf_object__relocate(struct bpf_object *obj, const char *targ_btf_pat · 29 unmodified lines | ||
| 7091 | 7136 | /* Process data relos for main programs */ |
| 7092 | 7137 | err = bpf_object__relocate_data(obj, prog); |
| 7093 | 7138 | if (err) { |
| 7094 | − pr_warn("prog '%s': failed to relocate data references: %d\n", | |
| 7095 | − prog->name, err); | |
| 7139 | + pr_warn("prog '%s': failed to relocate data references: %s\n", | |
| 7140 | + prog->name, errstr(err)); | |
| 7096 | 7141 | return err; |
| 7097 | 7142 | } |
| 7098 | 7143 | |
| 7099 | 7144 | /* Fix up .BTF.ext information, if necessary */ |
| 7100 | 7145 | err = bpf_program_fixup_func_info(obj, prog); |
| 7101 | 7146 | if (err) { |
| 7102 | − pr_warn("prog '%s': failed to perform .BTF.ext fix ups: %d\n", | |
| 7103 | − prog->name, err); | |
| 7147 | + pr_warn("prog '%s': failed to perform .BTF.ext fix ups: %s\n", | |
| 7148 | + prog->name, errstr(err)); | |
| 7104 | 7149 | return err; |
| 7105 | 7150 | } |
| 7106 | 7151 | } |
| @@ -7352,8 +7397,14 @@ static int libbpf_prepare_prog_load(struct bpf_program *prog, · 245 unmodified lines | ||
| 7352 | 7397 | opts->prog_flags |= BPF_F_XDP_HAS_FRAGS; |
| 7353 | 7398 | |
| 7354 | 7399 | /* special check for usdt to use uprobe_multi link */ |
| 7355 | − if ((def & SEC_USDT) && kernel_supports(prog->obj, FEAT_UPROBE_MULTI_LINK)) | |
| 7400 | + if ((def & SEC_USDT) && kernel_supports(prog->obj, FEAT_UPROBE_MULTI_LINK)) { | |
| 7401 | + /* for BPF_TRACE_UPROBE_MULTI, user might want to query expected_attach_type | |
| 7402 | + * in prog, and expected_attach_type we set in kernel is from opts, so we | |
| 7403 | + * update both. | |
| 7404 | + */ | |
| 7356 | 7405 | prog->expected_attach_type = BPF_TRACE_UPROBE_MULTI; |
| 7406 | + opts->expected_attach_type = BPF_TRACE_UPROBE_MULTI; | |
| 7407 | + } | |
| 7357 | 7408 | |
| 7358 | 7409 | if ((def & SEC_ATTACH_BTF) && !prog->attach_btf_id) { |
| 7359 | 7410 | int btf_obj_fd = 0, btf_type_id = 0, err; |
| @@ -7403,7 +7454,6 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog · 43 unmodified lines | ||
| 7403 | 7454 | { |
| 7404 | 7455 | LIBBPF_OPTS(bpf_prog_load_opts, load_attr); |
| 7405 | 7456 | const char *prog_name = NULL; |
| 7406 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 7407 | 7457 | size_t log_buf_size = 0; |
| 7408 | 7458 | char *log_buf = NULL, *tmp; |
| 7409 | 7459 | bool own_log_buf = true; |
| @@ -7443,6 +7493,7 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog · 33 unmodified lines | ||
| 7443 | 7493 | load_attr.attach_btf_id = prog->attach_btf_id; |
| 7444 | 7494 | load_attr.kern_version = kern_version; |
| 7445 | 7495 | load_attr.prog_ifindex = prog->prog_ifindex; |
| 7496 | + load_attr.expected_attach_type = prog->expected_attach_type; | |
| 7446 | 7497 | |
| 7447 | 7498 | /* specify func_info/line_info only if kernel supports them */ |
| 7448 | 7499 | if (obj->btf && btf__fd(obj->btf) >= 0 && kernel_supports(obj, FEAT_BTF_FUNC)) { |
| @@ -7466,17 +7517,14 @@ static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog · 17 unmodified lines | ||
| 7466 | 7517 | if (prog->sec_def && prog->sec_def->prog_prepare_load_fn) { |
| 7467 | 7518 | err = prog->sec_def->prog_prepare_load_fn(prog, &load_attr, prog->sec_def->cookie); |
| 7468 | 7519 | if (err < 0) { |
| 7469 | − pr_warn("prog '%s': failed to prepare load attributes: %d\n", | |
| 7470 | − prog->name, err); | |
| 7520 | + pr_warn("prog '%s': failed to prepare load attributes: %s\n", | |
| 7521 | + prog->name, errstr(err)); | |
| 7471 | 7522 | return err; |
| 7472 | 7523 | } |
| 7473 | 7524 | insns = prog->insns; |
| 7474 | 7525 | insns_cnt = prog->insns_cnt; |
| 7475 | 7526 | } |
| 7476 | 7527 | |
| 7477 | − /* allow prog_prepare_load_fn to change expected_attach_type */ | |
| 7478 | − load_attr.expected_attach_type = prog->expected_attach_type; | |
| 7479 | − | |
| 7480 | 7528 | if (obj->gen_loader) { |
| 7481 | 7529 | bpf_gen__prog_load(obj->gen_loader, prog->type, prog->name, |
| 7482 | 7530 | license, insns, insns_cnt, &load_attr, |
| @@ -7534,9 +7582,8 @@ retry_load: · 51 unmodified lines | ||
| 7534 | 7582 | continue; |
| 7535 | 7583 | |
| 7536 | 7584 | if (bpf_prog_bind_map(ret, map->fd, NULL)) { |
| 7537 | − cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg)); | |
| 7538 | 7585 | pr_warn("prog '%s': failed to bind map '%s': %s\n", |
| 7539 | − prog->name, map->real_name, cp); | |
| 7586 | + prog->name, map->real_name, errstr(errno)); | |
| 7540 | 7587 | /* Don't fail hard if can't bind rodata. */ |
| 7541 | 7588 | } |
| 7542 | 7589 | } |
| @@ -7566,8 +7613,7 @@ retry_load: · 23 unmodified lines | ||
| 7566 | 7613 | /* post-process verifier log to improve error descriptions */ |
| 7567 | 7614 | fixup_verifier_log(prog, log_buf, log_buf_size); |
| 7568 | 7615 | |
| 7569 | − cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg)); | |
| 7570 | − pr_warn("prog '%s': BPF program load failed: %s\n", prog->name, cp); | |
| 7616 | + pr_warn("prog '%s': BPF program load failed: %s\n", prog->name, errstr(errno)); | |
| 7571 | 7617 | pr_perm_msg(ret); |
| 7572 | 7618 | |
| 7573 | 7619 | if (own_log_buf && log_buf && log_buf[0] != '\0') { |
| @@ -7860,7 +7906,7 @@ bpf_object__load_progs(struct bpf_object *obj, int log_level) · 286 unmodified lines | ||
| 7860 | 7906 | err = bpf_object_load_prog(obj, prog, prog->insns, prog->insns_cnt, |
| 7861 | 7907 | obj->license, obj->kern_version, &prog->fd); |
| 7862 | 7908 | if (err) { |
| 7863 | − pr_warn("prog '%s': failed to load: %d\n", prog->name, err); | |
| 7909 | + pr_warn("prog '%s': failed to load: %s\n", prog->name, errstr(err)); | |
| 7864 | 7910 | return err; |
| 7865 | 7911 | } |
| 7866 | 7912 | } |
| @@ -7894,8 +7940,8 @@ static int bpf_object_init_progs(struct bpf_object *obj, const struct bpf_object · 27 unmodified lines | ||
| 7894 | 7940 | if (prog->sec_def->prog_setup_fn) { |
| 7895 | 7941 | err = prog->sec_def->prog_setup_fn(prog, prog->sec_def->cookie); |
| 7896 | 7942 | if (err < 0) { |
| 7897 | − pr_warn("prog '%s': failed to initialize: %d\n", | |
| 7898 | − prog->name, err); | |
| 7943 | + pr_warn("prog '%s': failed to initialize: %s\n", | |
| 7944 | + prog->name, errstr(err)); | |
| 7899 | 7945 | return err; |
| 7900 | 7946 | } |
| 7901 | 7947 | } |
| @@ -7992,7 +8038,6 @@ static struct bpf_object *bpf_object_open(const char *path, const void *obj_buf, · 90 unmodified lines | ||
| 7992 | 8038 | } |
| 7993 | 8039 | |
| 7994 | 8040 | err = bpf_object__elf_init(obj); |
| 7995 | − err = err ? : bpf_object__check_endianness(obj); | |
| 7996 | 8041 | err = err ? : bpf_object__elf_collect(obj); |
| 7997 | 8042 | err = err ? : bpf_object__collect_externs(obj); |
| 7998 | 8043 | err = err ? : bpf_object_fixup_btf(obj); |
| @@ -8085,7 +8130,7 @@ static int libbpf_kallsyms_parse(kallsyms_cb_t cb, void *ctx) · 86 unmodified lines | ||
| 8085 | 8130 | f = fopen("/proc/kallsyms", "re"); |
| 8086 | 8131 | if (!f) { |
| 8087 | 8132 | err = -errno; |
| 8088 | − pr_warn("failed to open /proc/kallsyms: %d\n", err); | |
| 8133 | + pr_warn("failed to open /proc/kallsyms: %s\n", errstr(err)); | |
| 8089 | 8134 | return err; |
| 8090 | 8135 | } |
| 8091 | 8136 | |
| @@ -8498,8 +8543,15 @@ static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const ch · 406 unmodified lines | ||
| 8498 | 8543 | return libbpf_err(-EINVAL); |
| 8499 | 8544 | } |
| 8500 | 8545 | |
| 8501 | − if (obj->gen_loader) | |
| 8546 | + /* Disallow kernel loading programs of non-native endianness but | |
| 8547 | + * permit cross-endian creation of "light skeleton". | |
| 8548 | + */ | |
| 8549 | + if (obj->gen_loader) { | |
| 8502 | 8550 | bpf_gen__init(obj->gen_loader, extra_log_level, obj->nr_programs, obj->nr_maps); |
| 8551 | + } else if (!is_native_endianness(obj)) { | |
| 8552 | + pr_warn("object '%s': loading non-native endianness is unsupported\n", obj->name); | |
| 8553 | + return libbpf_err(-LIBBPF_ERRNO__ENDIAN); | |
| 8554 | + } | |
| 8503 | 8555 | |
| 8504 | 8556 | err = bpf_object_prepare_token(obj); |
| 8505 | 8557 | err = err ? : bpf_object__probe_loading(obj); |
| @@ -8562,7 +8614,6 @@ int bpf_object__load(struct bpf_object *obj) · 56 unmodified lines | ||
| 8562 | 8614 | |
| 8563 | 8615 | static int make_parent_dir(const char *path) |
| 8564 | 8616 | { |
| 8565 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 8566 | 8617 | char *dname, *dir; |
| 8567 | 8618 | int err = 0; |
| 8568 | 8619 | |
| @@ -8576,15 +8627,13 @@ static int make_parent_dir(const char *path) · 7 unmodified lines | ||
| 8576 | 8627 | |
| 8577 | 8628 | free(dname); |
| 8578 | 8629 | if (err) { |
| 8579 | − cp = libbpf_strerror_r(-err, errmsg, sizeof(errmsg)); | |
| 8580 | − pr_warn("failed to mkdir %s: %s\n", path, cp); | |
| 8630 | + pr_warn("failed to mkdir %s: %s\n", path, errstr(err)); | |
| 8581 | 8631 | } |
| 8582 | 8632 | return err; |
| 8583 | 8633 | } |
| 8584 | 8634 | |
| 8585 | 8635 | static int check_path(const char *path) |
| 8586 | 8636 | { |
| 8587 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 8588 | 8637 | struct statfs st_fs; |
| 8589 | 8638 | char *dname, *dir; |
| 8590 | 8639 | int err = 0; |
| @@ -8598,8 +8647,7 @@ static int check_path(const char *path) · 7 unmodified lines | ||
| 8598 | 8647 | |
| 8599 | 8648 | dir = dirname(dname); |
| 8600 | 8649 | if (statfs(dir, &st_fs)) { |
| 8601 | − cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg)); | |
| 8602 | − pr_warn("failed to statfs %s: %s\n", dir, cp); | |
| 8650 | + pr_warn("failed to statfs %s: %s\n", dir, errstr(errno)); | |
| 8603 | 8651 | err = -errno; |
| 8604 | 8652 | } |
| 8605 | 8653 | free(dname); |
| @@ -8614,7 +8662,6 @@ static int check_path(const char *path) · 8 unmodified lines | ||
| 8614 | 8662 | |
| 8615 | 8663 | int bpf_program__pin(struct bpf_program *prog, const char *path) |
| 8616 | 8664 | { |
| 8617 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 8618 | 8665 | int err; |
| 8619 | 8666 | |
| 8620 | 8667 | if (prog->fd < 0) { |
| @@ -8632,8 +8679,7 @@ int bpf_program__pin(struct bpf_program *prog, const char *path) · 11 unmodified lines | ||
| 8632 | 8679 | |
| 8633 | 8680 | if (bpf_obj_pin(prog->fd, path)) { |
| 8634 | 8681 | err = -errno; |
| 8635 | − cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg)); | |
| 8636 | − pr_warn("prog '%s': failed to pin at '%s': %s\n", prog->name, path, cp); | |
| 8682 | + pr_warn("prog '%s': failed to pin at '%s': %s\n", prog->name, path, errstr(err)); | |
| 8637 | 8683 | return libbpf_err(err); |
| 8638 | 8684 | } |
| 8639 | 8685 | |
| @@ -8664,7 +8710,6 @@ int bpf_program__unpin(struct bpf_program *prog, const char *path) · 24 unmodified lines | ||
| 8664 | 8710 | |
| 8665 | 8711 | int bpf_map__pin(struct bpf_map *map, const char *path) |
| 8666 | 8712 | { |
| 8667 | − char *cp, errmsg[STRERR_BUFSIZE]; | |
| 8668 | 8713 | int err; |
| 8669 | 8714 | |
| 8670 | 8715 | if (map == NULL) { |
| @@ -8723,8 +8768,7 @@ int bpf_map__pin(struct bpf_map *map, const char *path) · 52 unmodified lines | ||
| 8723 | 8768 | return 0; |
| 8724 | 8769 | |
| 8725 | 8770 | out_err: |
| 8726 | − cp = libbpf_strerror_r(-err, errmsg, sizeof(errmsg)); | |
| 8727 | − pr_warn("failed to pin map: %s\n", cp); | |
| 8771 | + pr_warn("failed to pin map: %s\n", errstr(err)); | |
| 8728 | 8772 | return libbpf_err(err); |
| 8729 | 8773 | } |
| 8730 | 8774 | |
| @@ -9096,6 +9140,7 @@ int bpf_object__gen_loader(struct bpf_object *obj, struct gen_loader_opts *opts) · 365 unmodified lines | ||
| 9096 | 9140 | if (!gen) |
| 9097 | 9141 | return -ENOMEM; |
| 9098 | 9142 | gen->opts = opts; |
| 9143 | + gen->swapped_endian = !is_native_endianness(obj); | |
| 9099 | 9144 | obj->gen_loader = gen; |
| 9100 | 9145 | return 0; |
| 9101 | 9146 | } |
| @@ -9370,8 +9415,10 @@ static const struct bpf_sec_def section_defs[] = { · 268 unmodified lines | ||
| 9370 | 9415 | SEC_DEF("kprobe.session+", KPROBE, BPF_TRACE_KPROBE_SESSION, SEC_NONE, attach_kprobe_session), |
| 9371 | 9416 | SEC_DEF("uprobe.multi+", KPROBE, BPF_TRACE_UPROBE_MULTI, SEC_NONE, attach_uprobe_multi), |
| 9372 | 9417 | SEC_DEF("uretprobe.multi+", KPROBE, BPF_TRACE_UPROBE_MULTI, SEC_NONE, attach_uprobe_multi), |
| 9418 | + SEC_DEF("uprobe.session+", KPROBE, BPF_TRACE_UPROBE_SESSION, SEC_NONE, attach_uprobe_multi), | |
| 9373 | 9419 | SEC_DEF("uprobe.multi.s+", KPROBE, BPF_TRACE_UPROBE_MULTI, SEC_SLEEPABLE, attach_uprobe_multi), |
| 9374 | 9420 | SEC_DEF("uretprobe.multi.s+", KPROBE, BPF_TRACE_UPROBE_MULTI, SEC_SLEEPABLE, attach_uprobe_multi), |
| 9421 | + SEC_DEF("uprobe.session.s+", KPROBE, BPF_TRACE_UPROBE_SESSION, SEC_SLEEPABLE, attach_uprobe_multi), | |
| 9375 | 9422 | SEC_DEF("ksyscall+", KPROBE, 0, SEC_NONE, attach_ksyscall), |
| 9376 | 9423 | SEC_DEF("kretsyscall+", KPROBE, 0, SEC_NONE, attach_ksyscall), |
| 9377 | 9424 | SEC_DEF("usdt+", KPROBE, 0, SEC_USDT, attach_usdt), |
| @@ -9910,8 +9957,8 @@ static int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd) · 532 unmodified lines | ||
| 9910 | 9957 | memset(&info, 0, info_len); |
| 9911 | 9958 | err = bpf_prog_get_info_by_fd(attach_prog_fd, &info, &info_len); |
| 9912 | 9959 | if (err) { |
| 9913 | − pr_warn("failed bpf_prog_get_info_by_fd for FD %d: %d\n", | |
| 9914 | − attach_prog_fd, err); | |
| 9960 | + pr_warn("failed bpf_prog_get_info_by_fd for FD %d: %s\n", | |
| 9961 | + attach_prog_fd, errstr(err)); | |
| 9915 | 9962 | return err; |
| 9916 | 9963 | } |
| 9917 | 9964 | |
| @@ -9923,7 +9970,7 @@ static int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd) · 5 unmodified lines | ||
| 9923 | 9970 | btf = btf__load_from_kernel_by_id(info.btf_id); |
| 9924 | 9971 | err = libbpf_get_error(btf); |
| 9925 | 9972 | if (err) { |
| 9926 | − pr_warn("Failed to get BTF %d of the program: %d\n", info.btf_id, err); | |
| 9973 | + pr_warn("Failed to get BTF %d of the program: %s\n", info.btf_id, errstr(err)); | |
| 9927 | 9974 | goto out; |
| 9928 | 9975 | } |
| 9929 | 9976 | err = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC); |
| @@ -10005,8 +10052,8 @@ static int libbpf_find_attach_btf_id(struct bpf_program *prog, const char *attac · 75 unmodified lines | ||
| 10005 | 10052 | } |
| 10006 | 10053 | err = libbpf_find_prog_btf_id(attach_name, attach_prog_fd); |
| 10007 | 10054 | if (err < 0) { |
| 10008 | − pr_warn("prog '%s': failed to find BPF program (FD %d) BTF ID for '%s': %d\n", | |
| 10009 | − prog->name, attach_prog_fd, attach_name, err); | |
| 10055 | + pr_warn("prog '%s': failed to find BPF program (FD %d) BTF ID for '%s': %s\n", | |
| 10056 | + prog->name, attach_prog_fd, attach_name, errstr(err)); | |
| 10010 | 10057 | return err; |
| 10011 | 10058 | } |
| 10012 | 10059 | *btf_obj_fd = 0; |
| @@ -10025,8 +10072,8 @@ static int libbpf_find_attach_btf_id(struct bpf_program *prog, const char *attac · 12 unmodified lines | ||
| 10025 | 10072 | btf_type_id); |
| 10026 | 10073 | } |
| 10027 | 10074 | if (err) { |
| 10028 | − pr_warn("prog '%s': failed to find kernel BTF type ID of '%s': %d\n", | |
| 10029 | − prog->name, attach_name, err); | |
| 10075 | + pr_warn("prog '%s': failed to find kernel BTF type ID of '%s': %s\n", | |
| 10076 | + prog->name, attach_name, errstr(err)); | |
| 10030 | 10077 | return err; |
| 10031 | 10078 | } |
| 10032 | 10079 | return 0; |
| @@ -10254,14 +10301,14 @@ int bpf_map__set_value_size(struct bpf_map *map, __u32 size) · 221 unmodified lines | ||
| 10254 | 10301 | mmap_new_sz = array_map_mmap_sz(size, map->def.max_entries); |
| 10255 | 10302 | err = bpf_map_mmap_resize(map, mmap_old_sz, mmap_new_sz); |
| 10256 | 10303 | if (err) { |
| 10257 | − pr_warn("map '%s': failed to resize memory-mapped region: %d\n", | |
| 10258 | − bpf_map__name(map), err); | |
| 10304 | + pr_warn("map '%s': failed to resize memory-mapped region: %s\n", | |
| 10305 | + bpf_map__name(map), errstr(err)); | |
| 10259 | 10306 | return err; |
| 10260 | 10307 | } |
| 10261 | 10308 | err = map_btf_datasec_resize(map, size); |
| 10262 | 10309 | if (err && err != -ENOENT) { |
| 10263 | − pr_warn("map '%s': failed to adjust resized BTF, clearing BTF key/value info: %d\n", | |
| 10264 | − bpf_map__name(map), err); | |
| 10310 | + pr_warn("map '%s': failed to adjust resized BTF, clearing BTF key/value info: %s\n", | |
| 10311 | + bpf_map__name(map), errstr(err)); | |
| 10265 | 10312 | map->btf_value_type_id = 0; |
| 10266 | 10313 | map->btf_key_type_id = 0; |
| 10267 | 10314 | } |
| @@ -10752,7 +10799,6 @@ static void bpf_link_perf_dealloc(struct bpf_link *link) · 484 unmodified lines | ||
| 10752 | 10799 | struct bpf_link *bpf_program__attach_perf_event_opts(const struct bpf_program *prog, int pfd, |
| 10753 | 10800 | const struct bpf_perf_event_opts *opts) |
| 10754 | 10801 | { |
| 10755 | − char errmsg[STRERR_BUFSIZE]; | |
| 10756 | 10802 | struct bpf_link_perf *link; |
| 10757 | 10803 | int prog_fd, link_fd = -1, err; |
| 10758 | 10804 | bool force_ioctl_attach; |
| @@ -10787,9 +10833,8 @@ struct bpf_link *bpf_program__attach_perf_event_opts(const struct bpf_program *p · 28 unmodified lines | ||
| 10787 | 10833 | link_fd = bpf_link_create(prog_fd, pfd, BPF_PERF_EVENT, &link_opts); |
| 10788 | 10834 | if (link_fd < 0) { |
| 10789 | 10835 | err = -errno; |
| 10790 | − pr_warn("prog '%s': failed to create BPF link for perf_event FD %d: %d (%s)\n", | |
| 10791 | − prog->name, pfd, | |
| 10792 | − err, libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 10836 | + pr_warn("prog '%s': failed to create BPF link for perf_event FD %d: %s\n", | |
| 10837 | + prog->name, pfd, errstr(err)); | |
| 10793 | 10838 | goto err_out; |
| 10794 | 10839 | } |
| 10795 | 10840 | link->link.fd = link_fd; |
| @@ -10803,7 +10848,7 @@ struct bpf_link *bpf_program__attach_perf_event_opts(const struct bpf_program *p · 7 unmodified lines | ||
| 10803 | 10848 | if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) { |
| 10804 | 10849 | err = -errno; |
| 10805 | 10850 | pr_warn("prog '%s': failed to attach to perf_event FD %d: %s\n", |
| 10806 | − prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 10851 | + prog->name, pfd, errstr(err)); | |
| 10807 | 10852 | if (err == -EPROTO) |
| 10808 | 10853 | pr_warn("prog '%s': try add PERF_SAMPLE_CALLCHAIN to or remove exclude_callchain_[kernel|user] from pfd %d\n", |
| 10809 | 10854 | prog->name, pfd); |
| @@ -10814,7 +10859,7 @@ struct bpf_link *bpf_program__attach_perf_event_opts(const struct bpf_program *p · 4 unmodified lines | ||
| 10814 | 10859 | if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) { |
| 10815 | 10860 | err = -errno; |
| 10816 | 10861 | pr_warn("prog '%s': failed to enable perf_event FD %d: %s\n", |
| 10817 | − prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 10862 | + prog->name, pfd, errstr(err)); | |
| 10818 | 10863 | goto err_out; |
| 10819 | 10864 | } |
| 10820 | 10865 | |
| @@ -10838,22 +10883,19 @@ struct bpf_link *bpf_program__attach_perf_event(const struct bpf_program *prog, · 17 unmodified lines | ||
| 10838 | 10883 | */ |
| 10839 | 10884 | static int parse_uint_from_file(const char *file, const char *fmt) |
| 10840 | 10885 | { |
| 10841 | − char buf[STRERR_BUFSIZE]; | |
| 10842 | 10886 | int err, ret; |
| 10843 | 10887 | FILE *f; |
| 10844 | 10888 | |
| 10845 | 10889 | f = fopen(file, "re"); |
| 10846 | 10890 | if (!f) { |
| 10847 | 10891 | err = -errno; |
| 10848 | − pr_debug("failed to open '%s': %s\n", file, | |
| 10849 | − libbpf_strerror_r(err, buf, sizeof(buf))); | |
| 10892 | + pr_debug("failed to open '%s': %s\n", file, errstr(err)); | |
| 10850 | 10893 | return err; |
| 10851 | 10894 | } |
| 10852 | 10895 | err = fscanf(f, fmt, &ret); |
| 10853 | 10896 | if (err != 1) { |
| 10854 | 10897 | err = err == EOF ? -EIO : -errno; |
| 10855 | − pr_debug("failed to parse '%s': %s\n", file, | |
| 10856 | − libbpf_strerror_r(err, buf, sizeof(buf))); | |
| 10898 | + pr_debug("failed to parse '%s': %s\n", file, errstr(err)); | |
| 10857 | 10899 | fclose(f); |
| 10858 | 10900 | return err; |
| 10859 | 10901 | } |
| @@ -10897,7 +10939,6 @@ static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name, · 37 unmodified lines | ||
| 10897 | 10939 | { |
| 10898 | 10940 | const size_t attr_sz = sizeof(struct perf_event_attr); |
| 10899 | 10941 | struct perf_event_attr attr; |
| 10900 | − char errmsg[STRERR_BUFSIZE]; | |
| 10901 | 10942 | int type, pfd; |
| 10902 | 10943 | |
| 10903 | 10944 | if ((__u64)ref_ctr_off >= (1ULL << PERF_UPROBE_REF_CTR_OFFSET_BITS)) |
| @@ -10910,7 +10951,7 @@ static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name, · 6 unmodified lines | ||
| 10910 | 10951 | if (type < 0) { |
| 10911 | 10952 | pr_warn("failed to determine %s perf type: %s\n", |
| 10912 | 10953 | uprobe ? "uprobe" : "kprobe", |
| 10913 | − libbpf_strerror_r(type, errmsg, sizeof(errmsg))); | |
| 10954 | + errstr(type)); | |
| 10914 | 10955 | return type; |
| 10915 | 10956 | } |
| 10916 | 10957 | if (retprobe) { |
| @@ -10920,7 +10961,7 @@ static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name, · 3 unmodified lines | ||
| 10920 | 10961 | if (bit < 0) { |
| 10921 | 10962 | pr_warn("failed to determine %s retprobe bit: %s\n", |
| 10922 | 10963 | uprobe ? "uprobe" : "kprobe", |
| 10923 | − libbpf_strerror_r(bit, errmsg, sizeof(errmsg))); | |
| 10964 | + errstr(bit)); | |
| 10924 | 10965 | return bit; |
| 10925 | 10966 | } |
| 10926 | 10967 | attr.config |= 1 << bit; |
| @@ -11049,14 +11090,13 @@ static int perf_event_kprobe_open_legacy(const char *probe_name, bool retprobe, · 122 unmodified lines | ||
| 11049 | 11090 | { |
| 11050 | 11091 | const size_t attr_sz = sizeof(struct perf_event_attr); |
| 11051 | 11092 | struct perf_event_attr attr; |
| 11052 | − char errmsg[STRERR_BUFSIZE]; | |
| 11053 | 11093 | int type, pfd, err; |
| 11054 | 11094 | |
| 11055 | 11095 | err = add_kprobe_event_legacy(probe_name, retprobe, kfunc_name, offset); |
| 11056 | 11096 | if (err < 0) { |
| 11057 | 11097 | pr_warn("failed to add legacy kprobe event for '%s+0x%zx': %s\n", |
| 11058 | 11098 | kfunc_name, offset, |
| 11059 | − libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 11099 | + errstr(err)); | |
| 11060 | 11100 | return err; |
| 11061 | 11101 | } |
| 11062 | 11102 | type = determine_kprobe_perf_type_legacy(probe_name, retprobe); |
| @@ -11064,7 +11104,7 @@ static int perf_event_kprobe_open_legacy(const char *probe_name, bool retprobe, · 1 unmodified line | ||
| 11064 | 11104 | err = type; |
| 11065 | 11105 | pr_warn("failed to determine legacy kprobe event id for '%s+0x%zx': %s\n", |
| 11066 | 11106 | kfunc_name, offset, |
| 11067 | − libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 11107 | + errstr(err)); | |
| 11068 | 11108 | goto err_clean_legacy; |
| 11069 | 11109 | } |
| 11070 | 11110 | |
| @@ -11080,7 +11120,7 @@ static int perf_event_kprobe_open_legacy(const char *probe_name, bool retprobe, · 9 unmodified lines | ||
| 11080 | 11120 | if (pfd < 0) { |
| 11081 | 11121 | err = -errno; |
| 11082 | 11122 | pr_warn("legacy kprobe perf_event_open() failed: %s\n", |
| 11083 | − libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 11123 | + errstr(err)); | |
| 11084 | 11124 | goto err_clean_legacy; |
| 11085 | 11125 | } |
| 11086 | 11126 | return pfd; |
| @@ -11156,7 +11196,6 @@ bpf_program__attach_kprobe_opts(const struct bpf_program *prog, · 69 unmodified lines | ||
| 11156 | 11196 | { |
| 11157 | 11197 | DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts); |
| 11158 | 11198 | enum probe_attach_mode attach_mode; |
| 11159 | − char errmsg[STRERR_BUFSIZE]; | |
| 11160 | 11199 | char *legacy_probe = NULL; |
| 11161 | 11200 | struct bpf_link *link; |
| 11162 | 11201 | size_t offset; |
| @@ -11214,7 +11253,7 @@ bpf_program__attach_kprobe_opts(const struct bpf_program *prog, · 51 unmodified lines | ||
| 11214 | 11253 | pr_warn("prog '%s': failed to create %s '%s+0x%zx' perf event: %s\n", |
| 11215 | 11254 | prog->name, retprobe ? "kretprobe" : "kprobe", |
| 11216 | 11255 | func_name, offset, |
| 11217 | − libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 11256 | + errstr(err)); | |
| 11218 | 11257 | goto err_out; |
| 11219 | 11258 | } |
| 11220 | 11259 | link = bpf_program__attach_perf_event_opts(prog, pfd, &pe_opts); |
| @@ -11224,7 +11263,7 @@ bpf_program__attach_kprobe_opts(const struct bpf_program *prog, · 3 unmodified lines | ||
| 11224 | 11263 | pr_warn("prog '%s': failed to attach to %s '%s+0x%zx': %s\n", |
| 11225 | 11264 | prog->name, retprobe ? "kretprobe" : "kprobe", |
| 11226 | 11265 | func_name, offset, |
| 11227 | − libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 11266 | + errstr(err)); | |
| 11228 | 11267 | goto err_clean_legacy; |
| 11229 | 11268 | } |
| 11230 | 11269 | if (legacy) { |
| @@ -11360,7 +11399,7 @@ static int libbpf_available_kallsyms_parse(struct kprobe_multi_resolve *res) · 129 unmodified lines | ||
| 11360 | 11399 | f = fopen(available_functions_file, "re"); |
| 11361 | 11400 | if (!f) { |
| 11362 | 11401 | err = -errno; |
| 11363 | − pr_warn("failed to open %s: %d\n", available_functions_file, err); | |
| 11402 | + pr_warn("failed to open %s: %s\n", available_functions_file, errstr(err)); | |
| 11364 | 11403 | return err; |
| 11365 | 11404 | } |
| 11366 | 11405 | |
| @@ -11435,7 +11474,7 @@ static int libbpf_available_kprobes_parse(struct kprobe_multi_resolve *res) · 68 unmodified lines | ||
| 11435 | 11474 | f = fopen(available_path, "re"); |
| 11436 | 11475 | if (!f) { |
| 11437 | 11476 | err = -errno; |
| 11438 | − pr_warn("failed to open %s: %d\n", available_path, err); | |
| 11477 | + pr_warn("failed to open %s: %s\n", available_path, errstr(err)); | |
| 11439 | 11478 | return err; |
| 11440 | 11479 | } |
| 11441 | 11480 | |
| @@ -11481,7 +11520,6 @@ bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog, · 39 unmodified lines | ||
| 11481 | 11520 | }; |
| 11482 | 11521 | enum bpf_attach_type attach_type; |
| 11483 | 11522 | struct bpf_link *link = NULL; |
| 11484 | − char errmsg[STRERR_BUFSIZE]; | |
| 11485 | 11523 | const unsigned long *addrs; |
| 11486 | 11524 | int err, link_fd, prog_fd; |
| 11487 | 11525 | bool retprobe, session; |
| @@ -11549,7 +11587,7 @@ bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog, · 61 unmodified lines | ||
| 11549 | 11587 | if (link_fd < 0) { |
| 11550 | 11588 | err = -errno; |
| 11551 | 11589 | pr_warn("prog '%s': failed to attach: %s\n", |
| 11552 | − prog->name, libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 11590 | + prog->name, errstr(err)); | |
| 11553 | 11591 | goto error; |
| 11554 | 11592 | } |
| 11555 | 11593 | link->fd = link_fd; |
| @@ -11693,7 +11731,9 @@ static int attach_uprobe_multi(const struct bpf_program *prog, long cookie, stru · 137 unmodified lines | ||
| 11693 | 11731 | ret = 0; |
| 11694 | 11732 | break; |
| 11695 | 11733 | case 3: |
| 11734 | + opts.session = str_has_pfx(probe_type, "uprobe.session"); | |
| 11696 | 11735 | opts.retprobe = str_has_pfx(probe_type, "uretprobe.multi"); |
| 11736 | + | |
| 11697 | 11737 | *link = bpf_program__attach_uprobe_multi(prog, -1, binary_path, func_name, &opts); |
| 11698 | 11738 | ret = libbpf_get_error(*link); |
| 11699 | 11739 | break; |
| @@ -11756,15 +11796,15 @@ static int perf_event_uprobe_open_legacy(const char *probe_name, bool retprobe, · 56 unmodified lines | ||
| 11756 | 11796 | |
| 11757 | 11797 | err = add_uprobe_event_legacy(probe_name, retprobe, binary_path, offset); |
| 11758 | 11798 | if (err < 0) { |
| 11759 | − pr_warn("failed to add legacy uprobe event for %s:0x%zx: %d\n", | |
| 11760 | − binary_path, (size_t)offset, err); | |
| 11799 | + pr_warn("failed to add legacy uprobe event for %s:0x%zx: %s\n", | |
| 11800 | + binary_path, (size_t)offset, errstr(err)); | |
| 11761 | 11801 | return err; |
| 11762 | 11802 | } |
| 11763 | 11803 | type = determine_uprobe_perf_type_legacy(probe_name, retprobe); |
| 11764 | 11804 | if (type < 0) { |
| 11765 | 11805 | err = type; |
| 11766 | − pr_warn("failed to determine legacy uprobe event id for %s:0x%zx: %d\n", | |
| 11767 | − binary_path, offset, err); | |
| 11806 | + pr_warn("failed to determine legacy uprobe event id for %s:0x%zx: %s\n", | |
| 11807 | + binary_path, offset, errstr(err)); | |
| 11768 | 11808 | goto err_clean_legacy; |
| 11769 | 11809 | } |
| 11770 | 11810 | |
| @@ -11779,7 +11819,7 @@ static int perf_event_uprobe_open_legacy(const char *probe_name, bool retprobe, · 8 unmodified lines | ||
| 11779 | 11819 | -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC); |
| 11780 | 11820 | if (pfd < 0) { |
| 11781 | 11821 | err = -errno; |
| 11782 | − pr_warn("legacy uprobe perf_event_open() failed: %d\n", err); | |
| 11822 | + pr_warn("legacy uprobe perf_event_open() failed: %s\n", errstr(err)); | |
| 11783 | 11823 | goto err_clean_legacy; |
| 11784 | 11824 | } |
| 11785 | 11825 | return pfd; |
| @@ -11942,10 +11982,11 @@ bpf_program__attach_uprobe_multi(const struct bpf_program *prog, · 156 unmodified lines | ||
| 11942 | 11982 | const unsigned long *ref_ctr_offsets = NULL, *offsets = NULL; |
| 11943 | 11983 | LIBBPF_OPTS(bpf_link_create_opts, lopts); |
| 11944 | 11984 | unsigned long *resolved_offsets = NULL; |
| 11985 | + enum bpf_attach_type attach_type; | |
| 11945 | 11986 | int err = 0, link_fd, prog_fd; |
| 11946 | 11987 | struct bpf_link *link = NULL; |
| 11947 | − char errmsg[STRERR_BUFSIZE]; | |
| 11948 | 11988 | char full_path[PATH_MAX]; |
| 11989 | + bool retprobe, session; | |
| 11949 | 11990 | const __u64 *cookies; |
| 11950 | 11991 | const char **syms; |
| 11951 | 11992 | size_t cnt; |
| @@ -11965,6 +12006,8 @@ bpf_program__attach_uprobe_multi(const struct bpf_program *prog, · 13 unmodified lines | ||
| 11965 | 12006 | ref_ctr_offsets = OPTS_GET(opts, ref_ctr_offsets, NULL); |
| 11966 | 12007 | cookies = OPTS_GET(opts, cookies, NULL); |
| 11967 | 12008 | cnt = OPTS_GET(opts, cnt, 0); |
| 12009 | + retprobe = OPTS_GET(opts, retprobe, false); | |
| 12010 | + session = OPTS_GET(opts, session, false); | |
| 11968 | 12011 | |
| 11969 | 12012 | /* |
| 11970 | 12013 | * User can specify 2 mutually exclusive set of inputs: |
| @@ -11993,12 +12036,15 @@ bpf_program__attach_uprobe_multi(const struct bpf_program *prog, · 22 unmodified lines | ||
| 11993 | 12036 | return libbpf_err_ptr(-EINVAL); |
| 11994 | 12037 | } |
| 11995 | 12038 | |
| 12039 | + if (retprobe && session) | |
| 12040 | + return libbpf_err_ptr(-EINVAL); | |
| 12041 | + | |
| 11996 | 12042 | if (func_pattern) { |
| 11997 | 12043 | if (!strchr(path, '/')) { |
| 11998 | 12044 | err = resolve_full_path(path, full_path, sizeof(full_path)); |
| 11999 | 12045 | if (err) { |
| 12000 | − pr_warn("prog '%s': failed to resolve full path for '%s': %d\n", | |
| 12001 | − prog->name, path, err); | |
| 12046 | + pr_warn("prog '%s': failed to resolve full path for '%s': %s\n", | |
| 12047 | + prog->name, path, errstr(err)); | |
| 12002 | 12048 | return libbpf_err_ptr(err); |
| 12003 | 12049 | } |
| 12004 | 12050 | path = full_path; |
| @@ -12016,12 +12062,14 @@ bpf_program__attach_uprobe_multi(const struct bpf_program *prog, · 11 unmodified lines | ||
| 12016 | 12062 | offsets = resolved_offsets; |
| 12017 | 12063 | } |
| 12018 | 12064 | |
| 12065 | + attach_type = session ? BPF_TRACE_UPROBE_SESSION : BPF_TRACE_UPROBE_MULTI; | |
| 12066 | + | |
| 12019 | 12067 | lopts.uprobe_multi.path = path; |
| 12020 | 12068 | lopts.uprobe_multi.offsets = offsets; |
| 12021 | 12069 | lopts.uprobe_multi.ref_ctr_offsets = ref_ctr_offsets; |
| 12022 | 12070 | lopts.uprobe_multi.cookies = cookies; |
| 12023 | 12071 | lopts.uprobe_multi.cnt = cnt; |
| 12024 | − lopts.uprobe_multi.flags = OPTS_GET(opts, retprobe, false) ? BPF_F_UPROBE_MULTI_RETURN : 0; | |
| 12072 | + lopts.uprobe_multi.flags = retprobe ? BPF_F_UPROBE_MULTI_RETURN : 0; | |
| 12025 | 12073 | |
| 12026 | 12074 | if (pid == 0) |
| 12027 | 12075 | pid = getpid(); |
| @@ -12035,11 +12083,11 @@ bpf_program__attach_uprobe_multi(const struct bpf_program *prog, · 7 unmodified lines | ||
| 12035 | 12083 | } |
| 12036 | 12084 | link->detach = &bpf_link__detach_fd; |
| 12037 | 12085 | |
| 12038 | − link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &lopts); | |
| 12086 | + link_fd = bpf_link_create(prog_fd, 0, attach_type, &lopts); | |
| 12039 | 12087 | if (link_fd < 0) { |
| 12040 | 12088 | err = -errno; |
| 12041 | 12089 | pr_warn("prog '%s': failed to attach multi-uprobe: %s\n", |
| 12042 | − prog->name, libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 12090 | + prog->name, errstr(err)); | |
| 12043 | 12091 | goto error; |
| 12044 | 12092 | } |
| 12045 | 12093 | link->fd = link_fd; |
| @@ -12058,7 +12106,7 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid, · 12 unmodified lines | ||
| 12058 | 12106 | const struct bpf_uprobe_opts *opts) |
| 12059 | 12107 | { |
| 12060 | 12108 | const char *archive_path = NULL, *archive_sep = NULL; |
| 12061 | − char errmsg[STRERR_BUFSIZE], *legacy_probe = NULL; | |
| 12109 | + char *legacy_probe = NULL; | |
| 12062 | 12110 | DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts); |
| 12063 | 12111 | enum probe_attach_mode attach_mode; |
| 12064 | 12112 | char full_path[PATH_MAX]; |
| @@ -12090,8 +12138,8 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid, · 25 unmodified lines | ||
| 12090 | 12138 | } else if (!strchr(binary_path, '/')) { |
| 12091 | 12139 | err = resolve_full_path(binary_path, full_path, sizeof(full_path)); |
| 12092 | 12140 | if (err) { |
| 12093 | − pr_warn("prog '%s': failed to resolve full path for '%s': %d\n", | |
| 12094 | − prog->name, binary_path, err); | |
| 12141 | + pr_warn("prog '%s': failed to resolve full path for '%s': %s\n", | |
| 12142 | + prog->name, binary_path, errstr(err)); | |
| 12095 | 12143 | return libbpf_err_ptr(err); |
| 12096 | 12144 | } |
| 12097 | 12145 | binary_path = full_path; |
| @@ -12157,7 +12205,7 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid, · 59 unmodified lines | ||
| 12157 | 12205 | pr_warn("prog '%s': failed to create %s '%s:0x%zx' perf event: %s\n", |
| 12158 | 12206 | prog->name, retprobe ? "uretprobe" : "uprobe", |
| 12159 | 12207 | binary_path, func_offset, |
| 12160 | − libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 12208 | + errstr(err)); | |
| 12161 | 12209 | goto err_out; |
| 12162 | 12210 | } |
| 12163 | 12211 | |
| @@ -12168,7 +12216,7 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid, · 4 unmodified lines | ||
| 12168 | 12216 | pr_warn("prog '%s': failed to attach to %s '%s:0x%zx': %s\n", |
| 12169 | 12217 | prog->name, retprobe ? "uretprobe" : "uprobe", |
| 12170 | 12218 | binary_path, func_offset, |
| 12171 | − libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 12219 | + errstr(err)); | |
| 12172 | 12220 | goto err_clean_legacy; |
| 12173 | 12221 | } |
| 12174 | 12222 | if (legacy) { |
| @@ -12289,8 +12337,8 @@ struct bpf_link *bpf_program__attach_usdt(const struct bpf_program *prog, · 114 unmodified lines | ||
| 12289 | 12337 | if (!strchr(binary_path, '/')) { |
| 12290 | 12338 | err = resolve_full_path(binary_path, resolved_path, sizeof(resolved_path)); |
| 12291 | 12339 | if (err) { |
| 12292 | − pr_warn("prog '%s': failed to resolve full path for '%s': %d\n", | |
| 12293 | − prog->name, binary_path, err); | |
| 12340 | + pr_warn("prog '%s': failed to resolve full path for '%s': %s\n", | |
| 12341 | + prog->name, binary_path, errstr(err)); | |
| 12294 | 12342 | return libbpf_err_ptr(err); |
| 12295 | 12343 | } |
| 12296 | 12344 | binary_path = resolved_path; |
| @@ -12368,14 +12416,13 @@ static int perf_event_open_tracepoint(const char *tp_category, · 71 unmodified lines | ||
| 12368 | 12416 | { |
| 12369 | 12417 | const size_t attr_sz = sizeof(struct perf_event_attr); |
| 12370 | 12418 | struct perf_event_attr attr; |
| 12371 | − char errmsg[STRERR_BUFSIZE]; | |
| 12372 | 12419 | int tp_id, pfd, err; |
| 12373 | 12420 | |
| 12374 | 12421 | tp_id = determine_tracepoint_id(tp_category, tp_name); |
| 12375 | 12422 | if (tp_id < 0) { |
| 12376 | 12423 | pr_warn("failed to determine tracepoint '%s/%s' perf event ID: %s\n", |
| 12377 | 12424 | tp_category, tp_name, |
| 12378 | − libbpf_strerror_r(tp_id, errmsg, sizeof(errmsg))); | |
| 12425 | + errstr(tp_id)); | |
| 12379 | 12426 | return tp_id; |
| 12380 | 12427 | } |
| 12381 | 12428 | |
| @@ -12390,7 +12437,7 @@ static int perf_event_open_tracepoint(const char *tp_category, · 8 unmodified lines | ||
| 12390 | 12437 | err = -errno; |
| 12391 | 12438 | pr_warn("tracepoint '%s/%s' perf_event_open() failed: %s\n", |
| 12392 | 12439 | tp_category, tp_name, |
| 12393 | − libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 12440 | + errstr(err)); | |
| 12394 | 12441 | return err; |
| 12395 | 12442 | } |
| 12396 | 12443 | return pfd; |
| @@ -12402,7 +12449,6 @@ struct bpf_link *bpf_program__attach_tracepoint_opts(const struct bpf_program *p · 5 unmodified lines | ||
| 12402 | 12449 | const struct bpf_tracepoint_opts *opts) |
| 12403 | 12450 | { |
| 12404 | 12451 | DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts); |
| 12405 | − char errmsg[STRERR_BUFSIZE]; | |
| 12406 | 12452 | struct bpf_link *link; |
| 12407 | 12453 | int pfd, err; |
| 12408 | 12454 | |
| @@ -12415,7 +12461,7 @@ struct bpf_link *bpf_program__attach_tracepoint_opts(const struct bpf_program *p · 6 unmodified lines | ||
| 12415 | 12461 | if (pfd < 0) { |
| 12416 | 12462 | pr_warn("prog '%s': failed to create tracepoint '%s/%s' perf event: %s\n", |
| 12417 | 12463 | prog->name, tp_category, tp_name, |
| 12418 | − libbpf_strerror_r(pfd, errmsg, sizeof(errmsg))); | |
| 12464 | + errstr(pfd)); | |
| 12419 | 12465 | return libbpf_err_ptr(pfd); |
| 12420 | 12466 | } |
| 12421 | 12467 | link = bpf_program__attach_perf_event_opts(prog, pfd, &pe_opts); |
| @@ -12424,7 +12470,7 @@ struct bpf_link *bpf_program__attach_tracepoint_opts(const struct bpf_program *p · 2 unmodified lines | ||
| 12424 | 12470 | close(pfd); |
| 12425 | 12471 | pr_warn("prog '%s': failed to attach to tracepoint '%s/%s': %s\n", |
| 12426 | 12472 | prog->name, tp_category, tp_name, |
| 12427 | − libbpf_strerror_r(err, errmsg, sizeof(errmsg))); | |
| 12473 | + errstr(err)); | |
| 12428 | 12474 | return libbpf_err_ptr(err); |
| 12429 | 12475 | } |
| 12430 | 12476 | return link; |
| @@ -12475,7 +12521,6 @@ bpf_program__attach_raw_tracepoint_opts(const struct bpf_program *prog, · 44 unmodified lines | ||
| 12475 | 12521 | struct bpf_raw_tracepoint_opts *opts) |
| 12476 | 12522 | { |
| 12477 | 12523 | LIBBPF_OPTS(bpf_raw_tp_opts, raw_opts); |
| 12478 | − char errmsg[STRERR_BUFSIZE]; | |
| 12479 | 12524 | struct bpf_link *link; |
| 12480 | 12525 | int prog_fd, pfd; |
| 12481 | 12526 | |
| @@ -12500,7 +12545,7 @@ bpf_program__attach_raw_tracepoint_opts(const struct bpf_program *prog, · 18 unmodified lines | ||
| 12500 | 12545 | pfd = -errno; |
| 12501 | 12546 | free(link); |
| 12502 | 12547 | pr_warn("prog '%s': failed to attach to raw tracepoint '%s': %s\n", |
| 12503 | − prog->name, tp_name, libbpf_strerror_r(pfd, errmsg, sizeof(errmsg))); | |
| 12548 | + prog->name, tp_name, errstr(pfd)); | |
| 12504 | 12549 | return libbpf_err_ptr(pfd); |
| 12505 | 12550 | } |
| 12506 | 12551 | link->fd = pfd; |
| @@ -12559,7 +12604,6 @@ static struct bpf_link *bpf_program__attach_btf_id(const struct bpf_program *pro · 52 unmodified lines | ||
| 12559 | 12604 | const struct bpf_trace_opts *opts) |
| 12560 | 12605 | { |
| 12561 | 12606 | LIBBPF_OPTS(bpf_link_create_opts, link_opts); |
| 12562 | − char errmsg[STRERR_BUFSIZE]; | |
| 12563 | 12607 | struct bpf_link *link; |
| 12564 | 12608 | int prog_fd, pfd; |
| 12565 | 12609 | |
| @@ -12584,7 +12628,7 @@ static struct bpf_link *bpf_program__attach_btf_id(const struct bpf_program *pro · 18 unmodified lines | ||
| 12584 | 12628 | pfd = -errno; |
| 12585 | 12629 | free(link); |
| 12586 | 12630 | pr_warn("prog '%s': failed to attach: %s\n", |
| 12587 | − prog->name, libbpf_strerror_r(pfd, errmsg, sizeof(errmsg))); | |
| 12631 | + prog->name, errstr(pfd)); | |
| 12588 | 12632 | return libbpf_err_ptr(pfd); |
| 12589 | 12633 | } |
| 12590 | 12634 | link->fd = pfd; |
| @@ -12625,7 +12669,6 @@ bpf_program_attach_fd(const struct bpf_program *prog, · 34 unmodified lines | ||
| 12625 | 12669 | const struct bpf_link_create_opts *opts) |
| 12626 | 12670 | { |
| 12627 | 12671 | enum bpf_attach_type attach_type; |
| 12628 | − char errmsg[STRERR_BUFSIZE]; | |
| 12629 | 12672 | struct bpf_link *link; |
| 12630 | 12673 | int prog_fd, link_fd; |
| 12631 | 12674 | |
| @@ -12647,7 +12690,7 @@ bpf_program_attach_fd(const struct bpf_program *prog, · 15 unmodified lines | ||
| 12647 | 12690 | free(link); |
| 12648 | 12691 | pr_warn("prog '%s': failed to attach to %s: %s\n", |
| 12649 | 12692 | prog->name, target_name, |
| 12650 | − libbpf_strerror_r(link_fd, errmsg, sizeof(errmsg))); | |
| 12693 | + errstr(link_fd)); | |
| 12651 | 12694 | return libbpf_err_ptr(link_fd); |
| 12652 | 12695 | } |
| 12653 | 12696 | link->fd = link_fd; |
| @@ -12760,7 +12803,7 @@ struct bpf_link *bpf_program__attach_freplace(const struct bpf_program *prog, · 106 unmodified lines | ||
| 12760 | 12803 | } |
| 12761 | 12804 | |
| 12762 | 12805 | if (prog->type != BPF_PROG_TYPE_EXT) { |
| 12763 | − pr_warn("prog '%s': only BPF_PROG_TYPE_EXT can attach as freplace", | |
| 12806 | + pr_warn("prog '%s': only BPF_PROG_TYPE_EXT can attach as freplace\n", | |
| 12764 | 12807 | prog->name); |
| 12765 | 12808 | return libbpf_err_ptr(-EINVAL); |
| 12766 | 12809 | } |
| @@ -12789,7 +12832,6 @@ bpf_program__attach_iter(const struct bpf_program *prog, · 22 unmodified lines | ||
| 12789 | 12832 | const struct bpf_iter_attach_opts *opts) |
| 12790 | 12833 | { |
| 12791 | 12834 | DECLARE_LIBBPF_OPTS(bpf_link_create_opts, link_create_opts); |
| 12792 | − char errmsg[STRERR_BUFSIZE]; | |
| 12793 | 12835 | struct bpf_link *link; |
| 12794 | 12836 | int prog_fd, link_fd; |
| 12795 | 12837 | __u32 target_fd = 0; |
| @@ -12817,7 +12859,7 @@ bpf_program__attach_iter(const struct bpf_program *prog, · 21 unmodified lines | ||
| 12817 | 12859 | link_fd = -errno; |
| 12818 | 12860 | free(link); |
| 12819 | 12861 | pr_warn("prog '%s': failed to attach to iterator: %s\n", |
| 12820 | − prog->name, libbpf_strerror_r(link_fd, errmsg, sizeof(errmsg))); | |
| 12862 | + prog->name, errstr(link_fd)); | |
| 12821 | 12863 | return libbpf_err_ptr(link_fd); |
| 12822 | 12864 | } |
| 12823 | 12865 | link->fd = link_fd; |
| @@ -12859,12 +12901,10 @@ struct bpf_link *bpf_program__attach_netfilter(const struct bpf_program *prog, · 35 unmodified lines | ||
| 12859 | 12901 | |
| 12860 | 12902 | link_fd = bpf_link_create(prog_fd, 0, BPF_NETFILTER, &lopts); |
| 12861 | 12903 | if (link_fd < 0) { |
| 12862 | − char errmsg[STRERR_BUFSIZE]; | |
| 12863 | − | |
| 12864 | 12904 | link_fd = -errno; |
| 12865 | 12905 | free(link); |
| 12866 | 12906 | pr_warn("prog '%s': failed to attach to netfilter: %s\n", |
| 12867 | − prog->name, libbpf_strerror_r(link_fd, errmsg, sizeof(errmsg))); | |
| 12907 | + prog->name, errstr(link_fd)); | |
| 12868 | 12908 | return libbpf_err_ptr(link_fd); |
| 12869 | 12909 | } |
| 12870 | 12910 | link->fd = link_fd; |
| @@ -13149,7 +13189,6 @@ perf_buffer__open_cpu_buf(struct perf_buffer *pb, struct perf_event_attr *attr, · 278 unmodified lines | ||
| 13149 | 13189 | int cpu, int map_key) |
| 13150 | 13190 | { |
| 13151 | 13191 | struct perf_cpu_buf *cpu_buf; |
| 13152 | − char msg[STRERR_BUFSIZE]; | |
| 13153 | 13192 | int err; |
| 13154 | 13193 | |
| 13155 | 13194 | cpu_buf = calloc(1, sizeof(*cpu_buf)); |
| @@ -13165,7 +13204,7 @@ perf_buffer__open_cpu_buf(struct perf_buffer *pb, struct perf_event_attr *attr, · 9 unmodified lines | ||
| 13165 | 13204 | if (cpu_buf->fd < 0) { |
| 13166 | 13205 | err = -errno; |
| 13167 | 13206 | pr_warn("failed to open perf buffer event on cpu #%d: %s\n", |
| 13168 | − cpu, libbpf_strerror_r(err, msg, sizeof(msg))); | |
| 13207 | + cpu, errstr(err)); | |
| 13169 | 13208 | goto error; |
| 13170 | 13209 | } |
| 13171 | 13210 | |
| @@ -13176,14 +13215,14 @@ perf_buffer__open_cpu_buf(struct perf_buffer *pb, struct perf_event_attr *attr, · 4 unmodified lines | ||
| 13176 | 13215 | cpu_buf->base = NULL; |
| 13177 | 13216 | err = -errno; |
| 13178 | 13217 | pr_warn("failed to mmap perf buffer on cpu #%d: %s\n", |
| 13179 | − cpu, libbpf_strerror_r(err, msg, sizeof(msg))); | |
| 13218 | + cpu, errstr(err)); | |
| 13180 | 13219 | goto error; |
| 13181 | 13220 | } |
| 13182 | 13221 | |
| 13183 | 13222 | if (ioctl(cpu_buf->fd, PERF_EVENT_IOC_ENABLE, 0) < 0) { |
| 13184 | 13223 | err = -errno; |
| 13185 | 13224 | pr_warn("failed to enable perf buffer event on cpu #%d: %s\n", |
| 13186 | − cpu, libbpf_strerror_r(err, msg, sizeof(msg))); | |
| 13225 | + cpu, errstr(err)); | |
| 13187 | 13226 | goto error; |
| 13188 | 13227 | } |
| 13189 | 13228 | |
| @@ -13259,7 +13298,6 @@ static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, · 69 unmodified lines | ||
| 13259 | 13298 | { |
| 13260 | 13299 | const char *online_cpus_file = "/sys/devices/system/cpu/online"; |
| 13261 | 13300 | struct bpf_map_info map; |
| 13262 | − char msg[STRERR_BUFSIZE]; | |
| 13263 | 13301 | struct perf_buffer *pb; |
| 13264 | 13302 | bool *online = NULL; |
| 13265 | 13303 | __u32 map_info_len; |
| @@ -13282,7 +13320,7 @@ static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, · 16 unmodified lines | ||
| 13282 | 13320 | */ |
| 13283 | 13321 | if (err != -EINVAL) { |
| 13284 | 13322 | pr_warn("failed to get map info for map FD %d: %s\n", |
| 13285 | − map_fd, libbpf_strerror_r(err, msg, sizeof(msg))); | |
| 13323 | + map_fd, errstr(err)); | |
| 13286 | 13324 | return ERR_PTR(err); |
| 13287 | 13325 | } |
| 13288 | 13326 | pr_debug("failed to get map info for FD %d; API not supported? Ignoring...\n", |
| @@ -13312,7 +13350,7 @@ static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, · 23 unmodified lines | ||
| 13312 | 13350 | if (pb->epoll_fd < 0) { |
| 13313 | 13351 | err = -errno; |
| 13314 | 13352 | pr_warn("failed to create epoll instance: %s\n", |
| 13315 | − libbpf_strerror_r(err, msg, sizeof(msg))); | |
| 13353 | + errstr(err)); | |
| 13316 | 13354 | goto error; |
| 13317 | 13355 | } |
| 13318 | 13356 | |
| @@ -13343,7 +13381,7 @@ static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, · 24 unmodified lines | ||
| 13343 | 13381 | |
| 13344 | 13382 | err = parse_cpu_mask_file(online_cpus_file, &online, &n); |
| 13345 | 13383 | if (err) { |
| 13346 | − pr_warn("failed to get online CPU mask: %d\n", err); | |
| 13384 | + pr_warn("failed to get online CPU mask: %s\n", errstr(err)); | |
| 13347 | 13385 | goto error; |
| 13348 | 13386 | } |
| 13349 | 13387 | |
| @@ -13374,7 +13412,7 @@ static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, · 24 unmodified lines | ||
| 13374 | 13412 | err = -errno; |
| 13375 | 13413 | pr_warn("failed to set cpu #%d, key %d -> perf FD %d: %s\n", |
| 13376 | 13414 | cpu, map_key, cpu_buf->fd, |
| 13377 | − libbpf_strerror_r(err, msg, sizeof(msg))); | |
| 13415 | + errstr(err)); | |
| 13378 | 13416 | goto error; |
| 13379 | 13417 | } |
| 13380 | 13418 | |
| @@ -13385,7 +13423,7 @@ static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt, · 4 unmodified lines | ||
| 13385 | 13423 | err = -errno; |
| 13386 | 13424 | pr_warn("failed to epoll_ctl cpu #%d perf FD %d: %s\n", |
| 13387 | 13425 | cpu, cpu_buf->fd, |
| 13388 | − libbpf_strerror_r(err, msg, sizeof(msg))); | |
| 13426 | + errstr(err)); | |
| 13389 | 13427 | goto error; |
| 13390 | 13428 | } |
| 13391 | 13429 | j++; |
| @@ -13480,7 +13518,7 @@ int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms) · 88 unmodified lines | ||
| 13480 | 13518 | |
| 13481 | 13519 | err = perf_buffer__process_records(pb, cpu_buf); |
| 13482 | 13520 | if (err) { |
| 13483 | − pr_warn("error while processing records: %d\n", err); | |
| 13521 | + pr_warn("error while processing records: %s\n", errstr(err)); | |
| 13484 | 13522 | return libbpf_err(err); |
| 13485 | 13523 | } |
| 13486 | 13524 | } |
| @@ -13564,7 +13602,8 @@ int perf_buffer__consume(struct perf_buffer *pb) · 77 unmodified lines | ||
| 13564 | 13602 | |
| 13565 | 13603 | err = perf_buffer__process_records(pb, cpu_buf); |
| 13566 | 13604 | if (err) { |
| 13567 | − pr_warn("perf_buffer: failed to process records in buffer #%d: %d\n", i, err); | |
| 13605 | + pr_warn("perf_buffer: failed to process records in buffer #%d: %s\n", | |
| 13606 | + i, errstr(err)); | |
| 13568 | 13607 | return libbpf_err(err); |
| 13569 | 13608 | } |
| 13570 | 13609 | } |
| @@ -13675,14 +13714,14 @@ int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz) · 104 unmodified lines | ||
| 13675 | 13714 | fd = open(fcpu, O_RDONLY | O_CLOEXEC); |
| 13676 | 13715 | if (fd < 0) { |
| 13677 | 13716 | err = -errno; |
| 13678 | − pr_warn("Failed to open cpu mask file %s: %d\n", fcpu, err); | |
| 13717 | + pr_warn("Failed to open cpu mask file %s: %s\n", fcpu, errstr(err)); | |
| 13679 | 13718 | return err; |
| 13680 | 13719 | } |
| 13681 | 13720 | len = read(fd, buf, sizeof(buf)); |
| 13682 | 13721 | close(fd); |
| 13683 | 13722 | if (len <= 0) { |
| 13684 | 13723 | err = len ? -errno : -EINVAL; |
| 13685 | − pr_warn("Failed to read cpu mask from %s: %d\n", fcpu, err); | |
| 13724 | + pr_warn("Failed to read cpu mask from %s: %s\n", fcpu, errstr(err)); | |
| 13686 | 13725 | return err; |
| 13687 | 13726 | } |
| 13688 | 13727 | if (len >= sizeof(buf)) { |
| @@ -13774,20 +13813,21 @@ int bpf_object__open_skeleton(struct bpf_object_skeleton *s, · 85 unmodified lines | ||
| 13774 | 13813 | obj = bpf_object_open(NULL, s->data, s->data_sz, s->name, opts); |
| 13775 | 13814 | if (IS_ERR(obj)) { |
| 13776 | 13815 | err = PTR_ERR(obj); |
| 13777 | − pr_warn("failed to initialize skeleton BPF object '%s': %d\n", s->name, err); | |
| 13816 | + pr_warn("failed to initialize skeleton BPF object '%s': %s\n", | |
| 13817 | + s->name, errstr(err)); | |
| 13778 | 13818 | return libbpf_err(err); |
| 13779 | 13819 | } |
| 13780 | 13820 | |
| 13781 | 13821 | *s->obj = obj; |
| 13782 | 13822 | err = populate_skeleton_maps(obj, s->maps, s->map_cnt, s->map_skel_sz); |
| 13783 | 13823 | if (err) { |
| 13784 | − pr_warn("failed to populate skeleton maps for '%s': %d\n", s->name, err); | |
| 13824 | + pr_warn("failed to populate skeleton maps for '%s': %s\n", s->name, errstr(err)); | |
| 13785 | 13825 | return libbpf_err(err); |
| 13786 | 13826 | } |
| 13787 | 13827 | |
| 13788 | 13828 | err = populate_skeleton_progs(obj, s->progs, s->prog_cnt, s->prog_skel_sz); |
| 13789 | 13829 | if (err) { |
| 13790 | − pr_warn("failed to populate skeleton progs for '%s': %d\n", s->name, err); | |
| 13830 | + pr_warn("failed to populate skeleton progs for '%s': %s\n", s->name, errstr(err)); | |
| 13791 | 13831 | return libbpf_err(err); |
| 13792 | 13832 | } |
| 13793 | 13833 | |
| @@ -13817,13 +13857,13 @@ int bpf_object__open_subskeleton(struct bpf_object_subskeleton *s) · 23 unmodified lines | ||
| 13817 | 13857 | |
| 13818 | 13858 | err = populate_skeleton_maps(s->obj, s->maps, s->map_cnt, s->map_skel_sz); |
| 13819 | 13859 | if (err) { |
| 13820 | − pr_warn("failed to populate subskeleton maps: %d\n", err); | |
| 13860 | + pr_warn("failed to populate subskeleton maps: %s\n", errstr(err)); | |
| 13821 | 13861 | return libbpf_err(err); |
| 13822 | 13862 | } |
| 13823 | 13863 | |
| 13824 | 13864 | err = populate_skeleton_progs(s->obj, s->progs, s->prog_cnt, s->prog_skel_sz); |
| 13825 | 13865 | if (err) { |
| 13826 | − pr_warn("failed to populate subskeleton maps: %d\n", err); | |
| 13866 | + pr_warn("failed to populate subskeleton maps: %s\n", errstr(err)); | |
| 13827 | 13867 | return libbpf_err(err); |
| 13828 | 13868 | } |
| 13829 | 13869 | |
| @@ -13834,7 +13874,7 @@ int bpf_object__open_subskeleton(struct bpf_object_subskeleton *s) · 4 unmodified lines | ||
| 13834 | 13874 | map_type = btf__type_by_id(btf, map_type_id); |
| 13835 | 13875 | |
| 13836 | 13876 | if (!btf_is_datasec(map_type)) { |
| 13837 | − pr_warn("type for map '%1$s' is not a datasec: %2$s", | |
| 13877 | + pr_warn("type for map '%1$s' is not a datasec: %2$s\n", | |
| 13838 | 13878 | bpf_map__name(map), |
| 13839 | 13879 | __btf_kind_str(btf_kind(map_type))); |
| 13840 | 13880 | return libbpf_err(-EINVAL); |
| @@ -13870,53 +13910,18 @@ int bpf_object__load_skeleton(struct bpf_object_skeleton *s) · 29 unmodified lines | ||
| 13870 | 13910 | |
| 13871 | 13911 | err = bpf_object__load(*s->obj); |
| 13872 | 13912 | if (err) { |
| 13873 | − pr_warn("failed to load BPF skeleton '%s': %d\n", s->name, err); | |
| 13913 | + pr_warn("failed to load BPF skeleton '%s': %s\n", s->name, errstr(err)); | |
| 13874 | 13914 | return libbpf_err(err); |
| 13875 | 13915 | } |
| 13876 | 13916 | |
| 13877 | 13917 | for (i = 0; i < s->map_cnt; i++) { |
| 13878 | 13918 | struct bpf_map_skeleton *map_skel = (void *)s->maps + i * s->map_skel_sz; |
| 13879 | 13919 | struct bpf_map *map = *map_skel->map; |
| 13880 | − size_t mmap_sz = bpf_map_mmap_sz(map); | |
| 13881 | − int prot, map_fd = map->fd; | |
| 13882 | − void **mmaped = map_skel->mmaped; | |
| 13883 | 13920 | |
| 13884 | − if (!mmaped) | |
| 13921 | + if (!map_skel->mmaped) | |
| 13885 | 13922 | continue; |
| 13886 | 13923 | |
| 13887 | − if (!(map->def.map_flags & BPF_F_MMAPABLE)) { | |
| 13888 | − *mmaped = NULL; | |
| 13889 | − continue; | |
| 13890 | − } | |
| 13891 | − | |
| 13892 | − if (map->def.type == BPF_MAP_TYPE_ARENA) { | |
| 13893 | − *mmaped = map->mmaped; | |
| 13894 | − continue; | |
| 13895 | − } | |
| 13896 | − | |
| 13897 | − if (map->def.map_flags & BPF_F_RDONLY_PROG) | |
| 13898 | − prot = PROT_READ; | |
| 13899 | − else | |
| 13900 | − prot = PROT_READ | PROT_WRITE; | |
| 13901 | − | |
| 13902 | − /* Remap anonymous mmap()-ed "map initialization image" as | |
| 13903 | − * a BPF map-backed mmap()-ed memory, but preserving the same | |
| 13904 | − * memory address. This will cause kernel to change process' | |
| 13905 | − * page table to point to a different piece of kernel memory, | |
| 13906 | − * but from userspace point of view memory address (and its | |
| 13907 | − * contents, being identical at this point) will stay the | |
| 13908 | − * same. This mapping will be released by bpf_object__close() | |
| 13909 | − * as per normal clean up procedure, so we don't need to worry | |
| 13910 | − * about it from skeleton's clean up perspective. | |
| 13911 | − */ | |
| 13912 | − *mmaped = mmap(map->mmaped, mmap_sz, prot, MAP_SHARED | MAP_FIXED, map_fd, 0); | |
| 13913 | − if (*mmaped == MAP_FAILED) { | |
| 13914 | − err = -errno; | |
| 13915 | − *mmaped = NULL; | |
| 13916 | − pr_warn("failed to re-mmap() map '%s': %d\n", | |
| 13917 | − bpf_map__name(map), err); | |
| 13918 | − return libbpf_err(err); | |
| 13919 | − } | |
| 13924 | + *map_skel->mmaped = map->mmaped; | |
| 13920 | 13925 | } |
| 13921 | 13926 | |
| 13922 | 13927 | return 0; |
| @@ -13944,8 +13949,8 @@ int bpf_object__attach_skeleton(struct bpf_object_skeleton *s) · 21 unmodified lines | ||
| 13944 | 13949 | |
| 13945 | 13950 | err = prog->sec_def->prog_attach_fn(prog, prog->sec_def->cookie, link); |
| 13946 | 13951 | if (err) { |
| 13947 | − pr_warn("prog '%s': failed to auto-attach: %d\n", | |
| 13948 | − bpf_program__name(prog), err); | |
| 13952 | + pr_warn("prog '%s': failed to auto-attach: %s\n", | |
| 13953 | + bpf_program__name(prog), errstr(err)); | |
| 13949 | 13954 | return libbpf_err(err); |
| 13950 | 13955 | } |
| 13951 | 13956 | |
| @@ -13988,7 +13993,8 @@ int bpf_object__attach_skeleton(struct bpf_object_skeleton *s) · 36 unmodified lines | ||
| 13988 | 13993 | *link = bpf_map__attach_struct_ops(map); |
| 13989 | 13994 | if (!*link) { |
| 13990 | 13995 | err = -errno; |
| 13991 | − pr_warn("map '%s': failed to auto-attach: %d\n", bpf_map__name(map), err); | |
| 13996 | + pr_warn("map '%s': failed to auto-attach: %s\n", | |
| 13997 | + bpf_map__name(map), errstr(err)); | |
| 13992 | 13998 | return libbpf_err(err); |
| 13993 | 13999 | } |
| 13994 | 14000 | } |
| @@ -577,10 +577,12 @@ struct bpf_uprobe_multi_opts { · 576 unmodified lines | ||
| 577 | 577 | size_t cnt; |
| 578 | 578 | /* create return uprobes */ |
| 579 | 579 | bool retprobe; |
| 580 | + /* create session kprobes */ | |
| 581 | + bool session; | |
| 580 | 582 | size_t :0; |
| 581 | 583 | }; |
| 582 | 584 | |
| 583 | −#define bpf_uprobe_multi_opts__last_field retprobe | |
| 585 | +#define bpf_uprobe_multi_opts__last_field session | |
| 584 | 586 | |
| 585 | 587 | /** |
| 586 | 588 | * @brief **bpf_program__attach_uprobe_multi()** attaches a BPF program |
| @@ -421,6 +421,8 @@ LIBBPF_1.5.0 { · 420 unmodified lines | ||
| 421 | 421 | global: |
| 422 | 422 | btf__distill_base; |
| 423 | 423 | btf__relocate; |
| 424 | + btf_ext__endianness; | |
| 425 | + btf_ext__set_endianness; | |
| 424 | 426 | bpf_map__autoattach; |
| 425 | 427 | bpf_map__set_autoattach; |
| 426 | 428 | bpf_object__token_fd; |
| @@ -428,3 +430,6 @@ LIBBPF_1.5.0 { · 1 unmodified line | ||
| 428 | 430 | ring__consume_n; |
| 429 | 431 | ring_buffer__consume_n; |
| 430 | 432 | } LIBBPF_1.4.0; |
| 433 | + | |
| 434 | +LIBBPF_1.6.0 { | |
| 435 | +} LIBBPF_1.5.0; | |
| @@ -10,6 +10,7 @@ · 9 unmodified lines | ||
| 10 | 10 | #define __LIBBPF_LIBBPF_INTERNAL_H |
| 11 | 11 | |
| 12 | 12 | #include <stdlib.h> |
| 13 | +#include <byteswap.h> | |
| 13 | 14 | #include <limits.h> |
| 14 | 15 | #include <errno.h> |
| 15 | 16 | #include <linux/err.h> |
| @@ -448,11 +449,11 @@ struct btf_ext_info { · 432 unmodified lines | ||
| 448 | 449 | * |
| 449 | 450 | * The func_info subsection layout: |
| 450 | 451 | * record size for struct bpf_func_info in the func_info subsection |
| 451 | − * struct btf_sec_func_info for section #1 | |
| 452 | + * struct btf_ext_info_sec for section #1 | |
| 452 | 453 | * a list of bpf_func_info records for section #1 |
| 453 | 454 | * where struct bpf_func_info mimics one in include/uapi/linux/bpf.h |
| 454 | 455 | * but may not be identical |
| 455 | − * struct btf_sec_func_info for section #2 | |
| 456 | + * struct btf_ext_info_sec for section #2 | |
| 456 | 457 | * a list of bpf_func_info records for section #2 |
| 457 | 458 | * ...... |
| 458 | 459 | * |
| @@ -484,6 +485,8 @@ struct btf_ext { · 25 unmodified lines | ||
| 484 | 485 | struct btf_ext_header *hdr; |
| 485 | 486 | void *data; |
| 486 | 487 | }; |
| 488 | + void *data_swapped; | |
| 489 | + bool swapped_endian; | |
| 487 | 490 | struct btf_ext_info func_info; |
| 488 | 491 | struct btf_ext_info line_info; |
| 489 | 492 | struct btf_ext_info core_relo_info; |
| @@ -511,6 +514,32 @@ struct bpf_line_info_min { · 21 unmodified lines | ||
| 511 | 514 | __u32 line_col; |
| 512 | 515 | }; |
| 513 | 516 | |
| 517 | +/* Functions to byte-swap info records */ | |
| 518 | + | |
| 519 | +typedef void (*info_rec_bswap_fn)(void *); | |
| 520 | + | |
| 521 | +static inline void bpf_func_info_bswap(struct bpf_func_info *i) | |
| 522 | +{ | |
| 523 | + i->insn_off = bswap_32(i->insn_off); | |
| 524 | + i->type_id = bswap_32(i->type_id); | |
| 525 | +} | |
| 526 | + | |
| 527 | +static inline void bpf_line_info_bswap(struct bpf_line_info *i) | |
| 528 | +{ | |
| 529 | + i->insn_off = bswap_32(i->insn_off); | |
| 530 | + i->file_name_off = bswap_32(i->file_name_off); | |
| 531 | + i->line_off = bswap_32(i->line_off); | |
| 532 | + i->line_col = bswap_32(i->line_col); | |
| 533 | +} | |
| 534 | + | |
| 535 | +static inline void bpf_core_relo_bswap(struct bpf_core_relo *i) | |
| 536 | +{ | |
| 537 | + i->insn_off = bswap_32(i->insn_off); | |
| 538 | + i->type_id = bswap_32(i->type_id); | |
| 539 | + i->access_str_off = bswap_32(i->access_str_off); | |
| 540 | + i->kind = bswap_32(i->kind); | |
| 541 | +} | |
| 542 | + | |
| 514 | 543 | enum btf_field_iter_kind { |
| 515 | 544 | BTF_FIELD_ITER_IDS, |
| 516 | 545 | BTF_FIELD_ITER_STRS, |
| @@ -588,6 +617,16 @@ static inline bool is_ldimm64_insn(struct bpf_insn *insn) · 71 unmodified lines | ||
| 588 | 617 | return insn->code == (BPF_LD | BPF_IMM | BPF_DW); |
| 589 | 618 | } |
| 590 | 619 | |
| 620 | +static inline void bpf_insn_bswap(struct bpf_insn *insn) | |
| 621 | +{ | |
| 622 | + __u8 tmp_reg = insn->dst_reg; | |
| 623 | + | |
| 624 | + insn->dst_reg = insn->src_reg; | |
| 625 | + insn->src_reg = tmp_reg; | |
| 626 | + insn->off = bswap_16(insn->off); | |
| 627 | + insn->imm = bswap_32(insn->imm); | |
| 628 | +} | |
| 629 | + | |
| 591 | 630 | /* Unconditionally dup FD, ensuring it doesn't use [0, 2] range. |
| 592 | 631 | * Original FD is not closed or altered in any other way. |
| 593 | 632 | * Preserves original FD value, if it's invalid (negative). |
| @@ -4,6 +4,6 @@ · 3 unmodified lines | ||
| 4 | 4 | #define __LIBBPF_VERSION_H |
| 5 | 5 | |
| 6 | 6 | #define LIBBPF_MAJOR_VERSION 1 |
| 7 | −#define LIBBPF_MINOR_VERSION 5 | |
| 7 | +#define LIBBPF_MINOR_VERSION 6 | |
| 8 | 8 | |
| 9 | 9 | #endif /* __LIBBPF_VERSION_H */ |
| @@ -20,6 +20,7 @@ · 19 unmodified lines | ||
| 20 | 20 | #include "btf.h" |
| 21 | 21 | #include "libbpf_internal.h" |
| 22 | 22 | #include "strset.h" |
| 23 | +#include "str_error.h" | |
| 23 | 24 | |
| 24 | 25 | #define BTF_EXTERN_SEC ".extern" |
| 25 | 26 | |
| @@ -135,6 +136,7 @@ struct bpf_linker { · 109 unmodified lines | ||
| 135 | 136 | int fd; |
| 136 | 137 | Elf *elf; |
| 137 | 138 | Elf64_Ehdr *elf_hdr; |
| 139 | + bool swapped_endian; | |
| 138 | 140 | |
| 139 | 141 | /* Output sections metadata */ |
| 140 | 142 | struct dst_sec *secs; |
| @@ -305,7 +307,7 @@ static int init_output_elf(struct bpf_linker *linker, const char *file) · 164 unmodified lines | ||
| 305 | 307 | linker->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); |
| 306 | 308 | if (linker->fd < 0) { |
| 307 | 309 | err = -errno; |
| 308 | − pr_warn("failed to create '%s': %d\n", file, err); | |
| 310 | + pr_warn("failed to create '%s': %s\n", file, errstr(err)); | |
| 309 | 311 | return err; |
| 310 | 312 | } |
| 311 | 313 | |
| @@ -324,13 +326,8 @@ static int init_output_elf(struct bpf_linker *linker, const char *file) · 12 unmodified lines | ||
| 324 | 326 | |
| 325 | 327 | linker->elf_hdr->e_machine = EM_BPF; |
| 326 | 328 | linker->elf_hdr->e_type = ET_REL; |
| 327 | −#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | |
| 328 | − linker->elf_hdr->e_ident[EI_DATA] = ELFDATA2LSB; | |
| 329 | −#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | |
| 330 | − linker->elf_hdr->e_ident[EI_DATA] = ELFDATA2MSB; | |
| 331 | −#else | |
| 332 | −#error "Unknown __BYTE_ORDER__" | |
| 333 | −#endif | |
| 329 | + /* Set unknown ELF endianness, assign later from input files */ | |
| 330 | + linker->elf_hdr->e_ident[EI_DATA] = ELFDATANONE; | |
| 334 | 331 | |
| 335 | 332 | /* STRTAB */ |
| 336 | 333 | /* initialize strset with an empty string to conform to ELF */ |
| @@ -396,6 +393,8 @@ static int init_output_elf(struct bpf_linker *linker, const char *file) · 59 unmodified lines | ||
| 396 | 393 | pr_warn_elf("failed to create SYMTAB data"); |
| 397 | 394 | return -EINVAL; |
| 398 | 395 | } |
| 396 | + /* Ensure libelf translates byte-order of symbol records */ | |
| 397 | + sec->data->d_type = ELF_T_SYM; | |
| 399 | 398 | |
| 400 | 399 | str_off = strset__add_str(linker->strtab_strs, sec->sec_name); |
| 401 | 400 | if (str_off < 0) |
| @@ -539,19 +538,21 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename, · 137 unmodified lines | ||
| 539 | 538 | const struct bpf_linker_file_opts *opts, |
| 540 | 539 | struct src_obj *obj) |
| 541 | 540 | { |
| 542 | −#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | |
| 543 | − const int host_endianness = ELFDATA2LSB; | |
| 544 | −#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | |
| 545 | − const int host_endianness = ELFDATA2MSB; | |
| 546 | −#else | |
| 547 | −#error "Unknown __BYTE_ORDER__" | |
| 548 | −#endif | |
| 549 | 541 | int err = 0; |
| 550 | 542 | Elf_Scn *scn; |
| 551 | 543 | Elf_Data *data; |
| 552 | 544 | Elf64_Ehdr *ehdr; |
| 553 | 545 | Elf64_Shdr *shdr; |
| 554 | 546 | struct src_sec *sec; |
| 547 | + unsigned char obj_byteorder; | |
| 548 | + unsigned char link_byteorder = linker->elf_hdr->e_ident[EI_DATA]; | |
| 549 | +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | |
| 550 | + const unsigned char host_byteorder = ELFDATA2LSB; | |
| 551 | +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | |
| 552 | + const unsigned char host_byteorder = ELFDATA2MSB; | |
| 553 | +#else | |
| 554 | +#error "Unknown __BYTE_ORDER__" | |
| 555 | +#endif | |
| 555 | 556 | |
| 556 | 557 | pr_debug("linker: adding object file '%s'...\n", filename); |
| 557 | 558 | |
| @@ -560,7 +561,7 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename, · 2 unmodified lines | ||
| 560 | 561 | obj->fd = open(filename, O_RDONLY | O_CLOEXEC); |
| 561 | 562 | if (obj->fd < 0) { |
| 562 | 563 | err = -errno; |
| 563 | − pr_warn("failed to open file '%s': %d\n", filename, err); | |
| 564 | + pr_warn("failed to open file '%s': %s\n", filename, errstr(err)); | |
| 564 | 565 | return err; |
| 565 | 566 | } |
| 566 | 567 | obj->elf = elf_begin(obj->fd, ELF_C_READ_MMAP, NULL); |
| @@ -577,11 +578,25 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename, · 10 unmodified lines | ||
| 577 | 578 | pr_warn_elf("failed to get ELF header for %s", filename); |
| 578 | 579 | return err; |
| 579 | 580 | } |
| 580 | − if (ehdr->e_ident[EI_DATA] != host_endianness) { | |
| 581 | + | |
| 582 | + /* Linker output endianness set by first input object */ | |
| 583 | + obj_byteorder = ehdr->e_ident[EI_DATA]; | |
| 584 | + if (obj_byteorder != ELFDATA2LSB && obj_byteorder != ELFDATA2MSB) { | |
| 581 | 585 | err = -EOPNOTSUPP; |
| 582 | − pr_warn_elf("unsupported byte order of ELF file %s", filename); | |
| 586 | + pr_warn("unknown byte order of ELF file %s\n", filename); | |
| 583 | 587 | return err; |
| 584 | 588 | } |
| 589 | + if (link_byteorder == ELFDATANONE) { | |
| 590 | + linker->elf_hdr->e_ident[EI_DATA] = obj_byteorder; | |
| 591 | + linker->swapped_endian = obj_byteorder != host_byteorder; | |
| 592 | + pr_debug("linker: set %s-endian output byte order\n", | |
| 593 | + obj_byteorder == ELFDATA2MSB ? "big" : "little"); | |
| 594 | + } else if (link_byteorder != obj_byteorder) { | |
| 595 | + err = -EOPNOTSUPP; | |
| 596 | + pr_warn("byte order mismatch with ELF file %s\n", filename); | |
| 597 | + return err; | |
| 598 | + } | |
| 599 | + | |
| 585 | 600 | if (ehdr->e_type != ET_REL |
| 586 | 601 | || ehdr->e_machine != EM_BPF |
| 587 | 602 | || ehdr->e_ident[EI_CLASS] != ELFCLASS64) { |
| @@ -656,7 +671,8 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename, · 68 unmodified lines | ||
| 656 | 671 | obj->btf = btf__new(data->d_buf, shdr->sh_size); |
| 657 | 672 | err = libbpf_get_error(obj->btf); |
| 658 | 673 | if (err) { |
| 659 | − pr_warn("failed to parse .BTF from %s: %d\n", filename, err); | |
| 674 | + pr_warn("failed to parse .BTF from %s: %s\n", | |
| 675 | + filename, errstr(err)); | |
| 660 | 676 | return err; |
| 661 | 677 | } |
| 662 | 678 | sec->skipped = true; |
| @@ -666,7 +682,8 @@ static int linker_load_obj_file(struct bpf_linker *linker, const char *filename, · 3 unmodified lines | ||
| 666 | 682 | obj->btf_ext = btf_ext__new(data->d_buf, shdr->sh_size); |
| 667 | 683 | err = libbpf_get_error(obj->btf_ext); |
| 668 | 684 | if (err) { |
| 669 | − pr_warn("failed to parse .BTF.ext from '%s': %d\n", filename, err); | |
| 685 | + pr_warn("failed to parse .BTF.ext from '%s': %s\n", | |
| 686 | + filename, errstr(err)); | |
| 670 | 687 | return err; |
| 671 | 688 | } |
| 672 | 689 | sec->skipped = true; |
| @@ -1109,6 +1126,24 @@ static bool sec_content_is_same(struct dst_sec *dst_sec, struct src_sec *src_sec · 436 unmodified lines | ||
| 1109 | 1126 | return true; |
| 1110 | 1127 | } |
| 1111 | 1128 | |
| 1129 | +static bool is_exec_sec(struct dst_sec *sec) | |
| 1130 | +{ | |
| 1131 | + if (!sec || sec->ephemeral) | |
| 1132 | + return false; | |
| 1133 | + return (sec->shdr->sh_type == SHT_PROGBITS) && | |
| 1134 | + (sec->shdr->sh_flags & SHF_EXECINSTR); | |
| 1135 | +} | |
| 1136 | + | |
| 1137 | +static void exec_sec_bswap(void *raw_data, int size) | |
| 1138 | +{ | |
| 1139 | + const int insn_cnt = size / sizeof(struct bpf_insn); | |
| 1140 | + struct bpf_insn *insn = raw_data; | |
| 1141 | + int i; | |
| 1142 | + | |
| 1143 | + for (i = 0; i < insn_cnt; i++, insn++) | |
| 1144 | + bpf_insn_bswap(insn); | |
| 1145 | +} | |
| 1146 | + | |
| 1112 | 1147 | static int extend_sec(struct bpf_linker *linker, struct dst_sec *dst, struct src_sec *src) |
| 1113 | 1148 | { |
| 1114 | 1149 | void *tmp; |
| @@ -1168,6 +1203,10 @@ static int extend_sec(struct bpf_linker *linker, struct dst_sec *dst, struct src · 53 unmodified lines | ||
| 1168 | 1203 | memset(dst->raw_data + dst->sec_sz, 0, dst_align_sz - dst->sec_sz); |
| 1169 | 1204 | /* now copy src data at a properly aligned offset */ |
| 1170 | 1205 | memcpy(dst->raw_data + dst_align_sz, src->data->d_buf, src->shdr->sh_size); |
| 1206 | + | |
| 1207 | + /* convert added bpf insns to native byte-order */ | |
| 1208 | + if (linker->swapped_endian && is_exec_sec(dst)) | |
| 1209 | + exec_sec_bswap(dst->raw_data + dst_align_sz, src->shdr->sh_size); | |
| 1171 | 1210 | } |
| 1172 | 1211 | |
| 1173 | 1212 | dst->sec_sz = dst_final_sz; |
| @@ -2415,6 +2454,10 @@ static int linker_append_btf(struct bpf_linker *linker, struct src_obj *obj) · 1241 unmodified lines | ||
| 2415 | 2454 | if (glob_sym && glob_sym->var_idx >= 0) { |
| 2416 | 2455 | __s64 sz; |
| 2417 | 2456 | |
| 2457 | + /* FUNCs don't have size, nothing to update */ | |
| 2458 | + if (btf_is_func(t)) | |
| 2459 | + continue; | |
| 2460 | + | |
| 2418 | 2461 | dst_var = &dst_sec->sec_vars[glob_sym->var_idx]; |
| 2419 | 2462 | /* Because underlying BTF type might have |
| 2420 | 2463 | * changed, so might its size have changed, so |
| @@ -2628,6 +2671,10 @@ int bpf_linker__finalize(struct bpf_linker *linker) · 207 unmodified lines | ||
| 2628 | 2671 | if (!sec->scn) |
| 2629 | 2672 | continue; |
| 2630 | 2673 | |
| 2674 | + /* restore sections with bpf insns to target byte-order */ | |
| 2675 | + if (linker->swapped_endian && is_exec_sec(sec)) | |
| 2676 | + exec_sec_bswap(sec->raw_data, sec->sec_sz); | |
| 2677 | + | |
| 2631 | 2678 | sec->data->d_buf = sec->raw_data; |
| 2632 | 2679 | } |
| 2633 | 2680 | |
| @@ -2696,6 +2743,7 @@ static int emit_elf_data_sec(struct bpf_linker *linker, const char *sec_name, · 62 unmodified lines | ||
| 2696 | 2743 | |
| 2697 | 2744 | static int finalize_btf(struct bpf_linker *linker) |
| 2698 | 2745 | { |
| 2746 | + enum btf_endianness link_endianness; | |
| 2699 | 2747 | LIBBPF_OPTS(btf_dedup_opts, opts); |
| 2700 | 2748 | struct btf *btf = linker->btf; |
| 2701 | 2749 | const void *raw_data; |
| @@ -2729,17 +2777,24 @@ static int finalize_btf(struct bpf_linker *linker) · 27 unmodified lines | ||
| 2729 | 2777 | |
| 2730 | 2778 | err = finalize_btf_ext(linker); |
| 2731 | 2779 | if (err) { |
| 2732 | − pr_warn(".BTF.ext generation failed: %d\n", err); | |
| 2780 | + pr_warn(".BTF.ext generation failed: %s\n", errstr(err)); | |
| 2733 | 2781 | return err; |
| 2734 | 2782 | } |
| 2735 | 2783 | |
| 2736 | 2784 | opts.btf_ext = linker->btf_ext; |
| 2737 | 2785 | err = btf__dedup(linker->btf, &opts); |
| 2738 | 2786 | if (err) { |
| 2739 | − pr_warn("BTF dedup failed: %d\n", err); | |
| 2787 | + pr_warn("BTF dedup failed: %s\n", errstr(err)); | |
| 2740 | 2788 | return err; |
| 2741 | 2789 | } |
| 2742 | 2790 | |
| 2791 | + /* Set .BTF and .BTF.ext output byte order */ | |
| 2792 | + link_endianness = linker->elf_hdr->e_ident[EI_DATA] == ELFDATA2MSB ? | |
| 2793 | + BTF_BIG_ENDIAN : BTF_LITTLE_ENDIAN; | |
| 2794 | + btf__set_endianness(linker->btf, link_endianness); | |
| 2795 | + if (linker->btf_ext) | |
| 2796 | + btf_ext__set_endianness(linker->btf_ext, link_endianness); | |
| 2797 | + | |
| 2743 | 2798 | /* Emit .BTF section */ |
| 2744 | 2799 | raw_data = btf__raw_data(linker->btf, &raw_sz); |
| 2745 | 2800 | if (!raw_data) |
| @@ -2747,7 +2802,7 @@ static int finalize_btf(struct bpf_linker *linker) · 1 unmodified line | ||
| 2747 | 2802 | |
| 2748 | 2803 | err = emit_elf_data_sec(linker, BTF_ELF_SEC, 8, raw_data, raw_sz); |
| 2749 | 2804 | if (err) { |
| 2750 | − pr_warn("failed to write out .BTF ELF section: %d\n", err); | |
| 2805 | + pr_warn("failed to write out .BTF ELF section: %s\n", errstr(err)); | |
| 2751 | 2806 | return err; |
| 2752 | 2807 | } |
| 2753 | 2808 | |
| @@ -2759,7 +2814,7 @@ static int finalize_btf(struct bpf_linker *linker) · 5 unmodified lines | ||
| 2759 | 2814 | |
| 2760 | 2815 | err = emit_elf_data_sec(linker, BTF_EXT_ELF_SEC, 8, raw_data, raw_sz); |
| 2761 | 2816 | if (err) { |
| 2762 | − pr_warn("failed to write out .BTF.ext ELF section: %d\n", err); | |
| 2817 | + pr_warn("failed to write out .BTF.ext ELF section: %s\n", errstr(err)); | |
| 2763 | 2818 | return err; |
| 2764 | 2819 | } |
| 2765 | 2820 | } |
| @@ -2935,7 +2990,7 @@ static int finalize_btf_ext(struct bpf_linker *linker) · 169 unmodified lines | ||
| 2935 | 2990 | err = libbpf_get_error(linker->btf_ext); |
| 2936 | 2991 | if (err) { |
| 2937 | 2992 | linker->btf_ext = NULL; |
| 2938 | − pr_warn("failed to parse final .BTF.ext data: %d\n", err); | |
| 2993 | + pr_warn("failed to parse final .BTF.ext data: %s\n", errstr(err)); | |
| 2939 | 2994 | goto out; |
| 2940 | 2995 | } |
| 2941 | 2996 | |
| @@ -1339,7 +1339,7 @@ int bpf_core_calc_relo_insn(const char *prog_name, · 1338 unmodified lines | ||
| 1339 | 1339 | cands->cands[i].id, cand_spec); |
| 1340 | 1340 | if (err < 0) { |
| 1341 | 1341 | bpf_core_format_spec(spec_buf, sizeof(spec_buf), cand_spec); |
| 1342 | − pr_warn("prog '%s': relo #%d: error matching candidate #%d %s: %d\n ", | |
| 1342 | + pr_warn("prog '%s': relo #%d: error matching candidate #%d %s: %d\n", | |
| 1343 | 1343 | prog_name, relo_idx, i, spec_buf, err); |
| 1344 | 1344 | return err; |
| 1345 | 1345 | } |
| @@ -21,6 +21,7 @@ · 20 unmodified lines | ||
| 21 | 21 | #include "libbpf.h" |
| 22 | 22 | #include "libbpf_internal.h" |
| 23 | 23 | #include "bpf.h" |
| 24 | +#include "str_error.h" | |
| 24 | 25 | |
| 25 | 26 | struct ring { |
| 26 | 27 | ring_buffer_sample_fn sample_cb; |
| @@ -88,8 +89,8 @@ int ring_buffer__add(struct ring_buffer *rb, int map_fd, · 61 unmodified lines | ||
| 88 | 89 | err = bpf_map_get_info_by_fd(map_fd, &info, &len); |
| 89 | 90 | if (err) { |
| 90 | 91 | err = -errno; |
| 91 | − pr_warn("ringbuf: failed to get map info for fd=%d: %d\n", | |
| 92 | − map_fd, err); | |
| 92 | + pr_warn("ringbuf: failed to get map info for fd=%d: %s\n", | |
| 93 | + map_fd, errstr(err)); | |
| 93 | 94 | return libbpf_err(err); |
| 94 | 95 | } |
| 95 | 96 | |
| @@ -123,8 +124,8 @@ int ring_buffer__add(struct ring_buffer *rb, int map_fd, · 27 unmodified lines | ||
| 123 | 124 | tmp = mmap(NULL, rb->page_size, PROT_READ | PROT_WRITE, MAP_SHARED, map_fd, 0); |
| 124 | 125 | if (tmp == MAP_FAILED) { |
| 125 | 126 | err = -errno; |
| 126 | − pr_warn("ringbuf: failed to mmap consumer page for map fd=%d: %d\n", | |
| 127 | − map_fd, err); | |
| 127 | + pr_warn("ringbuf: failed to mmap consumer page for map fd=%d: %s\n", | |
| 128 | + map_fd, errstr(err)); | |
| 128 | 129 | goto err_out; |
| 129 | 130 | } |
| 130 | 131 | r->consumer_pos = tmp; |
| @@ -142,8 +143,8 @@ int ring_buffer__add(struct ring_buffer *rb, int map_fd, · 11 unmodified lines | ||
| 142 | 143 | tmp = mmap(NULL, (size_t)mmap_sz, PROT_READ, MAP_SHARED, map_fd, rb->page_size); |
| 143 | 144 | if (tmp == MAP_FAILED) { |
| 144 | 145 | err = -errno; |
| 145 | − pr_warn("ringbuf: failed to mmap data pages for map fd=%d: %d\n", | |
| 146 | − map_fd, err); | |
| 146 | + pr_warn("ringbuf: failed to mmap data pages for map fd=%d: %s\n", | |
| 147 | + map_fd, errstr(err)); | |
| 147 | 148 | goto err_out; |
| 148 | 149 | } |
| 149 | 150 | r->producer_pos = tmp; |
| @@ -156,8 +157,8 @@ int ring_buffer__add(struct ring_buffer *rb, int map_fd, · 6 unmodified lines | ||
| 156 | 157 | e->data.fd = rb->ring_cnt; |
| 157 | 158 | if (epoll_ctl(rb->epoll_fd, EPOLL_CTL_ADD, map_fd, e) < 0) { |
| 158 | 159 | err = -errno; |
| 159 | − pr_warn("ringbuf: failed to epoll add map fd=%d: %d\n", | |
| 160 | − map_fd, err); | |
| 160 | + pr_warn("ringbuf: failed to epoll add map fd=%d: %s\n", | |
| 161 | + map_fd, errstr(err)); | |
| 161 | 162 | goto err_out; |
| 162 | 163 | } |
| 163 | 164 | |
| @@ -205,7 +206,7 @@ ring_buffer__new(int map_fd, ring_buffer_sample_fn sample_cb, void *ctx, · 41 unmodified lines | ||
| 205 | 206 | rb->epoll_fd = epoll_create1(EPOLL_CLOEXEC); |
| 206 | 207 | if (rb->epoll_fd < 0) { |
| 207 | 208 | err = -errno; |
| 208 | − pr_warn("ringbuf: failed to create epoll instance: %d\n", err); | |
| 209 | + pr_warn("ringbuf: failed to create epoll instance: %s\n", errstr(err)); | |
| 209 | 210 | goto err_out; |
| 210 | 211 | } |
| 211 | 212 | |
| @@ -458,7 +459,8 @@ static int user_ringbuf_map(struct user_ring_buffer *rb, int map_fd) · 246 unmodified lines | ||
| 458 | 459 | err = bpf_map_get_info_by_fd(map_fd, &info, &len); |
| 459 | 460 | if (err) { |
| 460 | 461 | err = -errno; |
| 461 | − pr_warn("user ringbuf: failed to get map info for fd=%d: %d\n", map_fd, err); | |
| 462 | + pr_warn("user ringbuf: failed to get map info for fd=%d: %s\n", | |
| 463 | + map_fd, errstr(err)); | |
| 462 | 464 | return err; |
| 463 | 465 | } |
| 464 | 466 | |
| @@ -474,8 +476,8 @@ static int user_ringbuf_map(struct user_ring_buffer *rb, int map_fd) · 9 unmodified lines | ||
| 474 | 476 | tmp = mmap(NULL, rb->page_size, PROT_READ, MAP_SHARED, map_fd, 0); |
| 475 | 477 | if (tmp == MAP_FAILED) { |
| 476 | 478 | err = -errno; |
| 477 | − pr_warn("user ringbuf: failed to mmap consumer page for map fd=%d: %d\n", | |
| 478 | − map_fd, err); | |
| 479 | + pr_warn("user ringbuf: failed to mmap consumer page for map fd=%d: %s\n", | |
| 480 | + map_fd, errstr(err)); | |
| 479 | 481 | return err; |
| 480 | 482 | } |
| 481 | 483 | rb->consumer_pos = tmp; |
| @@ -494,8 +496,8 @@ static int user_ringbuf_map(struct user_ring_buffer *rb, int map_fd) · 12 unmodified lines | ||
| 494 | 496 | map_fd, rb->page_size); |
| 495 | 497 | if (tmp == MAP_FAILED) { |
| 496 | 498 | err = -errno; |
| 497 | − pr_warn("user ringbuf: failed to mmap data pages for map fd=%d: %d\n", | |
| 498 | − map_fd, err); | |
| 499 | + pr_warn("user ringbuf: failed to mmap data pages for map fd=%d: %s\n", | |
| 500 | + map_fd, errstr(err)); | |
| 499 | 501 | return err; |
| 500 | 502 | } |
| 501 | 503 | |
| @@ -506,7 +508,7 @@ static int user_ringbuf_map(struct user_ring_buffer *rb, int map_fd) · 4 unmodified lines | ||
| 506 | 508 | rb_epoll->events = EPOLLOUT; |
| 507 | 509 | if (epoll_ctl(rb->epoll_fd, EPOLL_CTL_ADD, map_fd, rb_epoll) < 0) { |
| 508 | 510 | err = -errno; |
| 509 | − pr_warn("user ringbuf: failed to epoll add map fd=%d: %d\n", map_fd, err); | |
| 511 | + pr_warn("user ringbuf: failed to epoll add map fd=%d: %s\n", map_fd, errstr(err)); | |
| 510 | 512 | return err; |
| 511 | 513 | } |
| 512 | 514 | |
| @@ -531,7 +533,7 @@ user_ring_buffer__new(int map_fd, const struct user_ring_buffer_opts *opts) · 18 unmodified lines | ||
| 531 | 533 | rb->epoll_fd = epoll_create1(EPOLL_CLOEXEC); |
| 532 | 534 | if (rb->epoll_fd < 0) { |
| 533 | 535 | err = -errno; |
| 534 | − pr_warn("user ringbuf: failed to create epoll instance: %d\n", err); | |
| 536 | + pr_warn("user ringbuf: failed to create epoll instance: %s\n", errstr(err)); | |
| 535 | 537 | goto err_out; |
| 536 | 538 | } |
| 537 | 539 | |
| @@ -351,10 +351,11 @@ static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts) · 350 unmodified lines | ||
| 351 | 351 | attr.test.ctx_size_in = opts->ctx->sz; |
| 352 | 352 | err = skel_sys_bpf(BPF_PROG_RUN, &attr, test_run_attr_sz); |
| 353 | 353 | if (err < 0 || (int)attr.test.retval < 0) { |
| 354 | − opts->errstr = "failed to execute loader prog"; | |
| 355 | 354 | if (err < 0) { |
| 355 | + opts->errstr = "failed to execute loader prog"; | |
| 356 | 356 | set_err; |
| 357 | 357 | } else { |
| 358 | + opts->errstr = "error returned by loader prog"; | |
| 358 | 359 | err = (int)attr.test.retval; |
| 359 | 360 | #ifndef __KERNEL__ |
| 360 | 361 | errno = -err; |
| @@ -5,6 +5,10 @@ · 4 unmodified lines | ||
| 5 | 5 | #include <errno.h> |
| 6 | 6 | #include "str_error.h" |
| 7 | 7 | |
| 8 | +#ifndef ENOTSUPP | |
| 9 | +#define ENOTSUPP 524 | |
| 10 | +#endif | |
| 11 | + | |
| 8 | 12 | /* make sure libbpf doesn't use kernel-only integer typedefs */ |
| 9 | 13 | #pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64 |
| 10 | 14 | |
| @@ -31,3 +35,70 @@ char *libbpf_strerror_r(int err, char *dst, int len) · 20 unmodified lines | ||
| 31 | 35 | } |
| 32 | 36 | return dst; |
| 33 | 37 | } |
| 38 | + | |
| 39 | +const char *errstr(int err) | |
| 40 | +{ | |
| 41 | + static __thread char buf[12]; | |
| 42 | + | |
| 43 | + if (err > 0) | |
| 44 | + err = -err; | |
| 45 | + | |
| 46 | + switch (err) { | |
| 47 | + case -E2BIG: return "-E2BIG"; | |
| 48 | + case -EACCES: return "-EACCES"; | |
| 49 | + case -EADDRINUSE: return "-EADDRINUSE"; | |
| 50 | + case -EADDRNOTAVAIL: return "-EADDRNOTAVAIL"; | |
| 51 | + case -EAGAIN: return "-EAGAIN"; | |
| 52 | + case -EALREADY: return "-EALREADY"; | |
| 53 | + case -EBADF: return "-EBADF"; | |
| 54 | + case -EBADFD: return "-EBADFD"; | |
| 55 | + case -EBUSY: return "-EBUSY"; | |
| 56 | + case -ECANCELED: return "-ECANCELED"; | |
| 57 | + case -ECHILD: return "-ECHILD"; | |
| 58 | + case -EDEADLK: return "-EDEADLK"; | |
| 59 | + case -EDOM: return "-EDOM"; | |
| 60 | + case -EEXIST: return "-EEXIST"; | |
| 61 | + case -EFAULT: return "-EFAULT"; | |
| 62 | + case -EFBIG: return "-EFBIG"; | |
| 63 | + case -EILSEQ: return "-EILSEQ"; | |
| 64 | + case -EINPROGRESS: return "-EINPROGRESS"; | |
| 65 | + case -EINTR: return "-EINTR"; | |
| 66 | + case -EINVAL: return "-EINVAL"; | |
| 67 | + case -EIO: return "-EIO"; | |
| 68 | + case -EISDIR: return "-EISDIR"; | |
| 69 | + case -ELOOP: return "-ELOOP"; | |
| 70 | + case -EMFILE: return "-EMFILE"; | |
| 71 | + case -EMLINK: return "-EMLINK"; | |
| 72 | + case -EMSGSIZE: return "-EMSGSIZE"; | |
| 73 | + case -ENAMETOOLONG: return "-ENAMETOOLONG"; | |
| 74 | + case -ENFILE: return "-ENFILE"; | |
| 75 | + case -ENODATA: return "-ENODATA"; | |
| 76 | + case -ENODEV: return "-ENODEV"; | |
| 77 | + case -ENOENT: return "-ENOENT"; | |
| 78 | + case -ENOEXEC: return "-ENOEXEC"; | |
| 79 | + case -ENOLINK: return "-ENOLINK"; | |
| 80 | + case -ENOMEM: return "-ENOMEM"; | |
| 81 | + case -ENOSPC: return "-ENOSPC"; | |
| 82 | + case -ENOTBLK: return "-ENOTBLK"; | |
| 83 | + case -ENOTDIR: return "-ENOTDIR"; | |
| 84 | + case -ENOTSUPP: return "-ENOTSUPP"; | |
| 85 | + case -ENOTTY: return "-ENOTTY"; | |
| 86 | + case -ENXIO: return "-ENXIO"; | |
| 87 | + case -EOPNOTSUPP: return "-EOPNOTSUPP"; | |
| 88 | + case -EOVERFLOW: return "-EOVERFLOW"; | |
| 89 | + case -EPERM: return "-EPERM"; | |
| 90 | + case -EPIPE: return "-EPIPE"; | |
| 91 | + case -EPROTO: return "-EPROTO"; | |
| 92 | + case -EPROTONOSUPPORT: return "-EPROTONOSUPPORT"; | |
| 93 | + case -ERANGE: return "-ERANGE"; | |
| 94 | + case -EROFS: return "-EROFS"; | |
| 95 | + case -ESPIPE: return "-ESPIPE"; | |
| 96 | + case -ESRCH: return "-ESRCH"; | |
| 97 | + case -ETXTBSY: return "-ETXTBSY"; | |
| 98 | + case -EUCLEAN: return "-EUCLEAN"; | |
| 99 | + case -EXDEV: return "-EXDEV"; | |
| 100 | + default: | |
| 101 | + snprintf(buf, sizeof(buf), "%d", err); | |
| 102 | + return buf; | |
| 103 | + } | |
| 104 | +} | |
| @@ -6,4 +6,11 @@ · 5 unmodified lines | ||
| 6 | 6 | |
| 7 | 7 | char *libbpf_strerror_r(int err, char *dst, int len); |
| 8 | 8 | |
| 9 | +/** | |
| 10 | + * @brief **errstr()** returns string corresponding to numeric errno | |
| 11 | + * @param err negative numeric errno | |
| 12 | + * @return pointer to string representation of the errno, that is invalidated | |
| 13 | + * upon the next call. | |
| 14 | + */ | |
| 15 | +const char *errstr(int err); | |
| 9 | 16 | #endif /* __LIBBPF_STR_ERROR_H */ |
| @@ -20,6 +20,7 @@ · 19 unmodified lines | ||
| 20 | 20 | #include "libbpf_common.h" |
| 21 | 21 | #include "libbpf_internal.h" |
| 22 | 22 | #include "hashmap.h" |
| 23 | +#include "str_error.h" | |
| 23 | 24 | |
| 24 | 25 | /* libbpf's USDT support consists of BPF-side state/code and user-space |
| 25 | 26 | * state/code working together in concert. BPF-side parts are defined in |
| @@ -465,8 +466,8 @@ static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs, · 439 unmodified lines | ||
| 465 | 466 | goto proceed; |
| 466 | 467 | |
| 467 | 468 | if (!realpath(lib_path, path)) { |
| 468 | − pr_warn("usdt: failed to get absolute path of '%s' (err %d), using path as is...\n", | |
| 469 | − lib_path, -errno); | |
| 469 | + pr_warn("usdt: failed to get absolute path of '%s' (err %s), using path as is...\n", | |
| 470 | + lib_path, errstr(-errno)); | |
| 470 | 471 | libbpf_strlcpy(path, lib_path, sizeof(path)); |
| 471 | 472 | } |
| 472 | 473 | |
| @@ -475,8 +476,8 @@ proceed: · 2 unmodified lines | ||
| 475 | 476 | f = fopen(line, "re"); |
| 476 | 477 | if (!f) { |
| 477 | 478 | err = -errno; |
| 478 | − pr_warn("usdt: failed to open '%s' to get base addr of '%s': %d\n", | |
| 479 | − line, lib_path, err); | |
| 479 | + pr_warn("usdt: failed to open '%s' to get base addr of '%s': %s\n", | |
| 480 | + line, lib_path, errstr(err)); | |
| 480 | 481 | return err; |
| 481 | 482 | } |
| 482 | 483 | |
| @@ -606,7 +607,8 @@ static int collect_usdt_targets(struct usdt_manager *man, Elf *elf, const char * · 123 unmodified lines | ||
| 606 | 607 | |
| 607 | 608 | err = parse_elf_segs(elf, path, &segs, &seg_cnt); |
| 608 | 609 | if (err) { |
| 609 | − pr_warn("usdt: failed to process ELF program segments for '%s': %d\n", path, err); | |
| 610 | + pr_warn("usdt: failed to process ELF program segments for '%s': %s\n", | |
| 611 | + path, errstr(err)); | |
| 610 | 612 | goto err_out; |
| 611 | 613 | } |
| 612 | 614 | |
| @@ -708,8 +710,8 @@ static int collect_usdt_targets(struct usdt_manager *man, Elf *elf, const char * · 95 unmodified lines | ||
| 708 | 710 | if (vma_seg_cnt == 0) { |
| 709 | 711 | err = parse_vma_segs(pid, path, &vma_segs, &vma_seg_cnt); |
| 710 | 712 | if (err) { |
| 711 | − pr_warn("usdt: failed to get memory segments in PID %d for shared library '%s': %d\n", | |
| 712 | − pid, path, err); | |
| 713 | + pr_warn("usdt: failed to get memory segments in PID %d for shared library '%s': %s\n", | |
| 714 | + pid, path, errstr(err)); | |
| 713 | 715 | goto err_out; |
| 714 | 716 | } |
| 715 | 717 | } |
| @@ -1047,8 +1049,8 @@ struct bpf_link *usdt_manager_attach_usdt(struct usdt_manager *man, const struct · 331 unmodified lines | ||
| 1047 | 1049 | |
| 1048 | 1050 | if (is_new && bpf_map_update_elem(spec_map_fd, &spec_id, &target->spec, BPF_ANY)) { |
| 1049 | 1051 | err = -errno; |
| 1050 | − pr_warn("usdt: failed to set USDT spec #%d for '%s:%s' in '%s': %d\n", | |
| 1051 | − spec_id, usdt_provider, usdt_name, path, err); | |
| 1052 | + pr_warn("usdt: failed to set USDT spec #%d for '%s:%s' in '%s': %s\n", | |
| 1053 | + spec_id, usdt_provider, usdt_name, path, errstr(err)); | |
| 1052 | 1054 | goto err_out; |
| 1053 | 1055 | } |
| 1054 | 1056 | if (!man->has_bpf_cookie && |
| @@ -1058,9 +1060,9 @@ struct bpf_link *usdt_manager_attach_usdt(struct usdt_manager *man, const struct · 3 unmodified lines | ||
| 1058 | 1060 | pr_warn("usdt: IP collision detected for spec #%d for '%s:%s' in '%s'\n", |
| 1059 | 1061 | spec_id, usdt_provider, usdt_name, path); |
| 1060 | 1062 | } else { |
| 1061 | − pr_warn("usdt: failed to map IP 0x%lx to spec #%d for '%s:%s' in '%s': %d\n", | |
| 1063 | + pr_warn("usdt: failed to map IP 0x%lx to spec #%d for '%s:%s' in '%s': %s\n", | |
| 1062 | 1064 | target->abs_ip, spec_id, usdt_provider, usdt_name, |
| 1063 | − path, err); | |
| 1065 | + path, errstr(err)); | |
| 1064 | 1066 | } |
| 1065 | 1067 | goto err_out; |
| 1066 | 1068 | } |
| @@ -1076,8 +1078,8 @@ struct bpf_link *usdt_manager_attach_usdt(struct usdt_manager *man, const struct · 9 unmodified lines | ||
| 1076 | 1078 | target->rel_ip, &opts); |
| 1077 | 1079 | err = libbpf_get_error(uprobe_link); |
| 1078 | 1080 | if (err) { |
| 1079 | − pr_warn("usdt: failed to attach uprobe #%d for '%s:%s' in '%s': %d\n", | |
| 1080 | − i, usdt_provider, usdt_name, path, err); | |
| 1081 | + pr_warn("usdt: failed to attach uprobe #%d for '%s:%s' in '%s': %s\n", | |
| 1082 | + i, usdt_provider, usdt_name, path, errstr(err)); | |
| 1081 | 1083 | goto err_out; |
| 1082 | 1084 | } |
| 1083 | 1085 | |
| @@ -1099,8 +1101,8 @@ struct bpf_link *usdt_manager_attach_usdt(struct usdt_manager *man, const struct · 15 unmodified lines | ||
| 1099 | 1101 | NULL, &opts_multi); |
| 1100 | 1102 | if (!link->multi_link) { |
| 1101 | 1103 | err = -errno; |
| 1102 | − pr_warn("usdt: failed to attach uprobe multi for '%s:%s' in '%s': %d\n", | |
| 1103 | − usdt_provider, usdt_name, path, err); | |
| 1104 | + pr_warn("usdt: failed to attach uprobe multi for '%s:%s' in '%s': %s\n", | |
| 1105 | + usdt_provider, usdt_name, path, errstr(err)); | |
| 1104 | 1106 | goto err_out; |
| 1105 | 1107 | } |
| 1106 | 1108 | |
| @@ -223,7 +223,7 @@ struct zip_archive *zip_archive_open(const char *path) · 222 unmodified lines | ||
| 223 | 223 | if (!archive) { |
| 224 | 224 | munmap(data, size); |
| 225 | 225 | return ERR_PTR(-ENOMEM); |
| 226 | − }; | |
| 226 | + } | |
| 227 | 227 | |
| 228 | 228 | archive->data = data; |
| 229 | 229 | archive->size = size; |
| @@ -807,7 +807,7 @@ static int option__cmp(const void *va, const void *vb) · 806 unmodified lines | ||
| 807 | 807 | static struct option *options__order(const struct option *opts) |
| 808 | 808 | { |
| 809 | 809 | int nr_opts = 0, nr_group = 0, nr_parent = 0, len; |
| 810 | − const struct option *o, *p = opts; | |
| 810 | + const struct option *o = NULL, *p = opts; | |
| 811 | 811 | struct option *opt, *ordered = NULL, *group; |
| 812 | 812 | |
| 813 | 813 | /* flatten the options that have parents */ |
| @@ -16,7 +16,6 @@ fixdep · 15 unmodified lines | ||
| 16 | 16 | /test_progs-cpuv4 |
| 17 | 17 | test_verifier_log |
| 18 | 18 | feature |
| 19 | −test_sock | |
| 20 | 19 | urandom_read |
| 21 | 20 | test_sockmap |
| 22 | 21 | test_lirc_mode2_user |
| @@ -10,6 +10,7 @@ TOOLSDIR := $(abspath ../../..) · 9 unmodified lines | ||
| 10 | 10 | LIBDIR := $(TOOLSDIR)/lib |
| 11 | 11 | BPFDIR := $(LIBDIR)/bpf |
| 12 | 12 | TOOLSINCDIR := $(TOOLSDIR)/include |
| 13 | +TOOLSARCHINCDIR := $(TOOLSDIR)/arch/$(SRCARCH)/include | |
| 13 | 14 | BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool |
| 14 | 15 | APIDIR := $(TOOLSINCDIR)/uapi |
| 15 | 16 | ifneq ($(O),) |
| @@ -44,7 +45,7 @@ CFLAGS += -g $(OPT_FLAGS) -rdynamic \ · 28 unmodified lines | ||
| 44 | 45 | -Wall -Werror -fno-omit-frame-pointer \ |
| 45 | 46 | $(GENFLAGS) $(SAN_CFLAGS) $(LIBELF_CFLAGS) \ |
| 46 | 47 | -I$(CURDIR) -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR) \ |
| 47 | − -I$(TOOLSINCDIR) -I$(APIDIR) -I$(OUTPUT) | |
| 48 | + -I$(TOOLSINCDIR) -I$(TOOLSARCHINCDIR) -I$(APIDIR) -I$(OUTPUT) | |
| 48 | 49 | LDFLAGS += $(SAN_LDFLAGS) |
| 49 | 50 | LDLIBS += $(LIBELF_LIBS) -lz -lrt -lpthread |
| 50 | 51 | |
| @@ -83,7 +84,7 @@ endif · 32 unmodified lines | ||
| 83 | 84 | |
| 84 | 85 | # Order correspond to 'make run_tests' order |
| 85 | 86 | TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ |
| 86 | − test_sock test_sockmap \ | |
| 87 | + test_sockmap \ | |
| 87 | 88 | test_tcpnotify_user test_sysctl \ |
| 88 | 89 | test_progs-no_alu32 |
| 89 | 90 | TEST_INST_SUBDIRS := no_alu32 |
| @@ -132,7 +133,6 @@ TEST_PROGS := test_kmod.sh \ · 42 unmodified lines | ||
| 132 | 133 | test_tunnel.sh \ |
| 133 | 134 | test_lwt_seg6local.sh \ |
| 134 | 135 | test_lirc_mode2.sh \ |
| 135 | − test_skb_cgroup_id.sh \ | |
| 136 | 136 | test_flow_dissector.sh \ |
| 137 | 137 | test_xdp_vlan_mode_generic.sh \ |
| 138 | 138 | test_xdp_vlan_mode_native.sh \ |
| @@ -274,6 +274,7 @@ $(OUTPUT)/liburandom_read.so: urandom_read_lib1.c urandom_read_lib2.c liburandom · 135 unmodified lines | ||
| 274 | 274 | $(Q)$(CLANG) $(CLANG_TARGET_ARCH) \ |
| 275 | 275 | $(filter-out -static,$(CFLAGS) $(LDFLAGS)) \ |
| 276 | 276 | $(filter %.c,$^) $(filter-out -static,$(LDLIBS)) \ |
| 277 | + -Wno-unused-command-line-argument \ | |
| 277 | 278 | -fuse-ld=$(LLD) -Wl,-znoseparate-code -Wl,--build-id=sha1 \ |
| 278 | 279 | -Wl,--version-script=liburandom_read.map \ |
| 279 | 280 | -fPIC -shared -o $@ |
| @@ -282,6 +283,7 @@ $(OUTPUT)/urandom_read: urandom_read.c urandom_read_aux.c $(OUTPUT)/liburandom_r · 2 unmodified lines | ||
| 282 | 283 | $(call msg,BINARY,,$@) |
| 283 | 284 | $(Q)$(CLANG) $(CLANG_TARGET_ARCH) \ |
| 284 | 285 | $(filter-out -static,$(CFLAGS) $(LDFLAGS)) $(filter %.c,$^) \ |
| 286 | + -Wno-unused-command-line-argument \ | |
| 285 | 287 | -lurandom_read $(filter-out -static,$(LDLIBS)) -L$(OUTPUT) \ |
| 286 | 288 | -fuse-ld=$(LLD) -Wl,-znoseparate-code -Wl,--build-id=sha1 \ |
| 287 | 289 | -Wl,-rpath=. -o $@ |
| @@ -295,25 +297,33 @@ $(OUTPUT)/sign-file: ../../../../scripts/sign-file.c · 7 unmodified lines | ||
| 295 | 297 | $(OUTPUT)/bpf_testmod.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_testmod/Makefile bpf_testmod/*.[ch]) |
| 296 | 298 | $(call msg,MOD,,$@) |
| 297 | 299 | $(Q)$(RM) bpf_testmod/bpf_testmod.ko # force re-compilation |
| 298 | − $(Q)$(MAKE) $(submake_extras) RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) -C bpf_testmod | |
| 300 | + $(Q)$(MAKE) $(submake_extras) -C bpf_testmod \ | |
| 301 | + RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) \ | |
| 302 | + EXTRA_CFLAGS='' EXTRA_LDFLAGS='' | |
| 299 | 303 | $(Q)cp bpf_testmod/bpf_testmod.ko $@ |
| 300 | 304 | |
| 301 | 305 | $(OUTPUT)/bpf_test_no_cfi.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_test_no_cfi/Makefile bpf_test_no_cfi/*.[ch]) |
| 302 | 306 | $(call msg,MOD,,$@) |
| 303 | 307 | $(Q)$(RM) bpf_test_no_cfi/bpf_test_no_cfi.ko # force re-compilation |
| 304 | − $(Q)$(MAKE) $(submake_extras) RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) -C bpf_test_no_cfi | |
| 308 | + $(Q)$(MAKE) $(submake_extras) -C bpf_test_no_cfi \ | |
| 309 | + RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) \ | |
| 310 | + EXTRA_CFLAGS='' EXTRA_LDFLAGS='' | |
| 305 | 311 | $(Q)cp bpf_test_no_cfi/bpf_test_no_cfi.ko $@ |
| 306 | 312 | |
| 307 | 313 | $(OUTPUT)/bpf_test_modorder_x.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_test_modorder_x/Makefile bpf_test_modorder_x/*.[ch]) |
| 308 | 314 | $(call msg,MOD,,$@) |
| 309 | 315 | $(Q)$(RM) bpf_test_modorder_x/bpf_test_modorder_x.ko # force re-compilation |
| 310 | − $(Q)$(MAKE) $(submake_extras) RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) -C bpf_test_modorder_x | |
| 316 | + $(Q)$(MAKE) $(submake_extras) -C bpf_test_modorder_x \ | |
| 317 | + RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) \ | |
| 318 | + EXTRA_CFLAGS='' EXTRA_LDFLAGS='' | |
| 311 | 319 | $(Q)cp bpf_test_modorder_x/bpf_test_modorder_x.ko $@ |
| 312 | 320 | |
| 313 | 321 | $(OUTPUT)/bpf_test_modorder_y.ko: $(VMLINUX_BTF) $(RESOLVE_BTFIDS) $(wildcard bpf_test_modorder_y/Makefile bpf_test_modorder_y/*.[ch]) |
| 314 | 322 | $(call msg,MOD,,$@) |
| 315 | 323 | $(Q)$(RM) bpf_test_modorder_y/bpf_test_modorder_y.ko # force re-compilation |
| 316 | − $(Q)$(MAKE) $(submake_extras) RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) -C bpf_test_modorder_y | |
| 324 | + $(Q)$(MAKE) $(submake_extras) -C bpf_test_modorder_y \ | |
| 325 | + RESOLVE_BTFIDS=$(RESOLVE_BTFIDS) \ | |
| 326 | + EXTRA_CFLAGS='' EXTRA_LDFLAGS='' | |
| 317 | 327 | $(Q)cp bpf_test_modorder_y/bpf_test_modorder_y.ko $@ |
| 318 | 328 | |
| 319 | 329 | |
| @@ -333,8 +343,8 @@ $(OUTPUT)/runqslower: $(BPFOBJ) | $(DEFAULT_BPFTOOL) $(RUNQSLOWER_OUTPUT) · 13 unmodified lines | ||
| 333 | 343 | BPFTOOL_OUTPUT=$(HOST_BUILD_DIR)/bpftool/ \ |
| 334 | 344 | BPFOBJ_OUTPUT=$(BUILD_DIR)/libbpf/ \ |
| 335 | 345 | BPFOBJ=$(BPFOBJ) BPF_INCLUDE=$(INCLUDE_DIR) \ |
| 336 | − EXTRA_CFLAGS='-g $(OPT_FLAGS) $(SAN_CFLAGS)' \ | |
| 337 | − EXTRA_LDFLAGS='$(SAN_LDFLAGS)' && \ | |
| 346 | + EXTRA_CFLAGS='-g $(OPT_FLAGS) $(SAN_CFLAGS) $(EXTRA_CFLAGS)' \ | |
| 347 | + EXTRA_LDFLAGS='$(SAN_LDFLAGS) $(EXTRA_LDFLAGS)' && \ | |
| 338 | 348 | cp $(RUNQSLOWER_OUTPUT)runqslower $@ |
| 339 | 349 | |
| 340 | 350 | TEST_GEN_PROGS_EXTENDED += $(TRUNNER_BPFTOOL) |
| @@ -349,7 +359,6 @@ JSON_WRITER := $(OUTPUT)/json_writer.o · 8 unmodified lines | ||
| 349 | 359 | CAP_HELPERS := $(OUTPUT)/cap_helpers.o |
| 350 | 360 | NETWORK_HELPERS := $(OUTPUT)/network_helpers.o |
| 351 | 361 | |
| 352 | −$(OUTPUT)/test_sock: $(CGROUP_HELPERS) $(TESTING_HELPERS) | |
| 353 | 362 | $(OUTPUT)/test_sockmap: $(CGROUP_HELPERS) $(TESTING_HELPERS) |
| 354 | 363 | $(OUTPUT)/test_tcpnotify_user: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(TRACE_HELPERS) |
| 355 | 364 | $(OUTPUT)/test_sock_fields: $(CGROUP_HELPERS) $(TESTING_HELPERS) |
| @@ -368,7 +377,8 @@ $(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) \ · 12 unmodified lines | ||
| 368 | 377 | $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/bpftool |
| 369 | 378 | $(Q)$(MAKE) $(submake_extras) -C $(BPFTOOLDIR) \ |
| 370 | 379 | ARCH= CROSS_COMPILE= CC="$(HOSTCC)" LD="$(HOSTLD)" \ |
| 371 | − EXTRA_CFLAGS='-g $(OPT_FLAGS)' \ | |
| 380 | + EXTRA_CFLAGS='-g $(OPT_FLAGS) $(EXTRA_CFLAGS)' \ | |
| 381 | + EXTRA_LDFLAGS='$(EXTRA_LDFLAGS)' \ | |
| 372 | 382 | OUTPUT=$(HOST_BUILD_DIR)/bpftool/ \ |
| 373 | 383 | LIBBPF_OUTPUT=$(HOST_BUILD_DIR)/libbpf/ \ |
| 374 | 384 | LIBBPF_DESTDIR=$(HOST_SCRATCH_DIR)/ \ |
| @@ -379,7 +389,8 @@ $(CROSS_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) \ · 4 unmodified lines | ||
| 379 | 389 | $(BPFOBJ) | $(BUILD_DIR)/bpftool |
| 380 | 390 | $(Q)$(MAKE) $(submake_extras) -C $(BPFTOOLDIR) \ |
| 381 | 391 | ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) \ |
| 382 | − EXTRA_CFLAGS='-g $(OPT_FLAGS)' \ | |
| 392 | + EXTRA_CFLAGS='-g $(OPT_FLAGS) $(EXTRA_CFLAGS)' \ | |
| 393 | + EXTRA_LDFLAGS='$(EXTRA_LDFLAGS)' \ | |
| 383 | 394 | OUTPUT=$(BUILD_DIR)/bpftool/ \ |
| 384 | 395 | LIBBPF_OUTPUT=$(BUILD_DIR)/libbpf/ \ |
| 385 | 396 | LIBBPF_DESTDIR=$(SCRATCH_DIR)/ \ |
| @@ -402,8 +413,8 @@ $(BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile) \ · 16 unmodified lines | ||
| 402 | 413 | $(APIDIR)/linux/bpf.h \ |
| 403 | 414 | | $(BUILD_DIR)/libbpf |
| 404 | 415 | $(Q)$(MAKE) $(submake_extras) -C $(BPFDIR) OUTPUT=$(BUILD_DIR)/libbpf/ \ |
| 405 | − EXTRA_CFLAGS='-g $(OPT_FLAGS) $(SAN_CFLAGS)' \ | |
| 406 | − EXTRA_LDFLAGS='$(SAN_LDFLAGS)' \ | |
| 416 | + EXTRA_CFLAGS='-g $(OPT_FLAGS) $(SAN_CFLAGS) $(EXTRA_CFLAGS)' \ | |
| 417 | + EXTRA_LDFLAGS='$(SAN_LDFLAGS) $(EXTRA_LDFLAGS)' \ | |
| 407 | 418 | DESTDIR=$(SCRATCH_DIR) prefix= all install_headers |
| 408 | 419 | |
| 409 | 420 | ifneq ($(BPFOBJ),$(HOST_BPFOBJ)) |
| @@ -411,7 +422,9 @@ $(HOST_BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile) \ · 1 unmodified line | ||
| 411 | 422 | $(APIDIR)/linux/bpf.h \ |
| 412 | 423 | | $(HOST_BUILD_DIR)/libbpf |
| 413 | 424 | $(Q)$(MAKE) $(submake_extras) -C $(BPFDIR) \ |
| 414 | − EXTRA_CFLAGS='-g $(OPT_FLAGS)' ARCH= CROSS_COMPILE= \ | |
| 425 | + ARCH= CROSS_COMPILE= \ | |
| 426 | + EXTRA_CFLAGS='-g $(OPT_FLAGS) $(EXTRA_CFLAGS)' \ | |
| 427 | + EXTRA_LDFLAGS='$(EXTRA_LDFLAGS)' \ | |
| 415 | 428 | OUTPUT=$(HOST_BUILD_DIR)/libbpf/ \ |
| 416 | 429 | CC="$(HOSTCC)" LD="$(HOSTLD)" \ |
| 417 | 430 | DESTDIR=$(HOST_SCRATCH_DIR)/ prefix= all install_headers |
| @@ -460,6 +473,7 @@ endef · 42 unmodified lines | ||
| 460 | 473 | IS_LITTLE_ENDIAN = $(shell $(CC) -dM -E - </dev/null | \ |
| 461 | 474 | grep 'define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__') |
| 462 | 475 | MENDIAN=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian) |
| 476 | +BPF_TARGET_ENDIAN=$(if $(IS_LITTLE_ENDIAN),--target=bpfel,--target=bpfeb) | |
| 463 | 477 | |
| 464 | 478 | ifneq ($(CROSS_COMPILE),) |
| 465 | 479 | CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%)) |
| @@ -487,17 +501,17 @@ $(OUTPUT)/cgroup_getset_retval_hooks.o: cgroup_getset_retval_hooks.h · 21 unmodified lines | ||
| 487 | 501 | # $4 - binary name |
| 488 | 502 | define CLANG_BPF_BUILD_RULE |
| 489 | 503 | $(call msg,CLNG-BPF,$4,$2) |
| 490 | − $(Q)$(CLANG) $3 -O2 --target=bpf -c $1 -mcpu=v3 -o $2 | |
| 504 | + $(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v3 -o $2 | |
| 491 | 505 | endef |
| 492 | 506 | # Similar to CLANG_BPF_BUILD_RULE, but with disabled alu32 |
| 493 | 507 | define CLANG_NOALU32_BPF_BUILD_RULE |
| 494 | 508 | $(call msg,CLNG-BPF,$4,$2) |
| 495 | − $(Q)$(CLANG) $3 -O2 --target=bpf -c $1 -mcpu=v2 -o $2 | |
| 509 | + $(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v2 -o $2 | |
| 496 | 510 | endef |
| 497 | 511 | # Similar to CLANG_BPF_BUILD_RULE, but with cpu-v4 |
| 498 | 512 | define CLANG_CPUV4_BPF_BUILD_RULE |
| 499 | 513 | $(call msg,CLNG-BPF,$4,$2) |
| 500 | − $(Q)$(CLANG) $3 -O2 --target=bpf -c $1 -mcpu=v4 -o $2 | |
| 514 | + $(Q)$(CLANG) $3 -O2 $(BPF_TARGET_ENDIAN) -c $1 -mcpu=v4 -o $2 | |
| 501 | 515 | endef |
| 502 | 516 | # Build BPF object using GCC |
| 503 | 517 | define GCC_BPF_BUILD_RULE |
| @@ -637,10 +651,11 @@ $(TRUNNER_BPF_SKELS_LINKED): $(TRUNNER_OUTPUT)/%: $$$$(%-deps) $(BPFTOOL) | $(TR · 133 unmodified lines | ||
| 637 | 651 | |
| 638 | 652 | # When the compiler generates a %.d file, only skel basenames (not |
| 639 | 653 | # full paths) are specified as prerequisites for corresponding %.o |
| 640 | −# file. This target makes %.skel.h basename dependent on full paths, | |
| 641 | −# linking generated %.d dependency with actual %.skel.h files. | |
| 642 | −$(notdir %.skel.h): $(TRUNNER_OUTPUT)/%.skel.h | |
| 643 | − @true | |
| 654 | +# file. vpath directives below instruct make to search for skel files | |
| 655 | +# in TRUNNER_OUTPUT, if they are not present in the working directory. | |
| 656 | +vpath %.skel.h $(TRUNNER_OUTPUT) | |
| 657 | +vpath %.lskel.h $(TRUNNER_OUTPUT) | |
| 658 | +vpath %.subskel.h $(TRUNNER_OUTPUT) | |
| 644 | 659 | |
| 645 | 660 | endif |
| 646 | 661 | |
| @@ -727,6 +742,7 @@ TRUNNER_EXTRA_SOURCES := test_progs.c \ · 80 unmodified lines | ||
| 727 | 742 | unpriv_helpers.c \ |
| 728 | 743 | netlink_helpers.c \ |
| 729 | 744 | jit_disasm_helpers.c \ |
| 745 | + io_helpers.c \ | |
| 730 | 746 | test_loader.c \ |
| 731 | 747 | xsk.c \ |
| 732 | 748 | disasm.c \ |
| @@ -4,6 +4,7 @@ · 3 unmodified lines | ||
| 4 | 4 | #include <argp.h> |
| 5 | 5 | #include <unistd.h> |
| 6 | 6 | #include <stdint.h> |
| 7 | +#include "bpf_util.h" | |
| 7 | 8 | #include "bench.h" |
| 8 | 9 | #include "trigger_bench.skel.h" |
| 9 | 10 | #include "trace_helpers.h" |
| @@ -72,7 +73,7 @@ static __always_inline void inc_counter(struct counter *counters) · 62 unmodified lines | ||
| 72 | 73 | unsigned slot; |
| 73 | 74 | |
| 74 | 75 | if (unlikely(tid == 0)) |
| 75 | − tid = syscall(SYS_gettid); | |
| 76 | + tid = sys_gettid(); | |
| 76 | 77 | |
| 77 | 78 | /* multiplicative hashing, it's fast */ |
| 78 | 79 | slot = 2654435769U * tid; |
| @@ -582,4 +582,10 @@ extern int bpf_wq_set_callback_impl(struct bpf_wq *wq, · 581 unmodified lines | ||
| 582 | 582 | unsigned int flags__k, void *aux__ign) __ksym; |
| 583 | 583 | #define bpf_wq_set_callback(timer, cb, flags) \ |
| 584 | 584 | bpf_wq_set_callback_impl(timer, cb, flags, NULL) |
| 585 | + | |
| 586 | +struct bpf_iter_kmem_cache; | |
| 587 | +extern int bpf_iter_kmem_cache_new(struct bpf_iter_kmem_cache *it) __weak __ksym; | |
| 588 | +extern struct kmem_cache *bpf_iter_kmem_cache_next(struct bpf_iter_kmem_cache *it) __weak __ksym; | |
| 589 | +extern void bpf_iter_kmem_cache_destroy(struct bpf_iter_kmem_cache *it) __weak __ksym; | |
| 590 | + | |
| 585 | 591 | #endif |
| @@ -40,6 +40,14 @@ DECLARE_TRACE(bpf_testmod_test_nullable_bare, · 39 unmodified lines | ||
| 40 | 40 | TP_ARGS(ctx__nullable) |
| 41 | 41 | ); |
| 42 | 42 | |
| 43 | +struct sk_buff; | |
| 44 | + | |
| 45 | +DECLARE_TRACE(bpf_testmod_test_raw_tp_null, | |
| 46 | + TP_PROTO(struct sk_buff *skb), | |
| 47 | + TP_ARGS(skb) | |
| 48 | +); | |
| 49 | + | |
| 50 | + | |
| 43 | 51 | #undef BPF_TESTMOD_DECLARE_TRACE |
| 44 | 52 | #ifdef DECLARE_TRACE_WRITABLE |
| 45 | 53 | #define BPF_TESTMOD_DECLARE_TRACE(call, proto, args, size) \ |
| @@ -245,6 +245,39 @@ __bpf_kfunc void bpf_testmod_ctx_release(struct bpf_testmod_ctx *ctx) · 244 unmodified lines | ||
| 245 | 245 | call_rcu(&ctx->rcu, testmod_free_cb); |
| 246 | 246 | } |
| 247 | 247 | |
| 248 | +static struct bpf_testmod_ops3 *st_ops3; | |
| 249 | + | |
| 250 | +static int bpf_testmod_test_3(void) | |
| 251 | +{ | |
| 252 | + return 0; | |
| 253 | +} | |
| 254 | + | |
| 255 | +static int bpf_testmod_test_4(void) | |
| 256 | +{ | |
| 257 | + return 0; | |
| 258 | +} | |
| 259 | + | |
| 260 | +static struct bpf_testmod_ops3 __bpf_testmod_ops3 = { | |
| 261 | + .test_1 = bpf_testmod_test_3, | |
| 262 | + .test_2 = bpf_testmod_test_4, | |
| 263 | +}; | |
| 264 | + | |
| 265 | +static void bpf_testmod_test_struct_ops3(void) | |
| 266 | +{ | |
| 267 | + if (st_ops3) | |
| 268 | + st_ops3->test_1(); | |
| 269 | +} | |
| 270 | + | |
| 271 | +__bpf_kfunc void bpf_testmod_ops3_call_test_1(void) | |
| 272 | +{ | |
| 273 | + st_ops3->test_1(); | |
| 274 | +} | |
| 275 | + | |
| 276 | +__bpf_kfunc void bpf_testmod_ops3_call_test_2(void) | |
| 277 | +{ | |
| 278 | + st_ops3->test_2(); | |
| 279 | +} | |
| 280 | + | |
| 248 | 281 | struct bpf_testmod_btf_type_tag_1 { |
| 249 | 282 | int a; |
| 250 | 283 | }; |
| @@ -380,6 +413,10 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj, · 129 unmodified lines | ||
| 380 | 413 | |
| 381 | 414 | (void)bpf_testmod_test_arg_ptr_to_struct(&struct_arg1_2); |
| 382 | 415 | |
| 416 | + (void)trace_bpf_testmod_test_raw_tp_null(NULL); | |
| 417 | + | |
| 418 | + bpf_testmod_test_struct_ops3(); | |
| 419 | + | |
| 383 | 420 | struct_arg3 = kmalloc((sizeof(struct bpf_testmod_struct_arg_3) + |
| 384 | 421 | sizeof(int)), GFP_KERNEL); |
| 385 | 422 | if (struct_arg3 != NULL) { |
| @@ -584,6 +621,8 @@ BTF_ID_FLAGS(func, bpf_kfunc_trusted_num_test, KF_TRUSTED_ARGS) · 198 unmodified lines | ||
| 584 | 621 | BTF_ID_FLAGS(func, bpf_kfunc_rcu_task_test, KF_RCU) |
| 585 | 622 | BTF_ID_FLAGS(func, bpf_testmod_ctx_create, KF_ACQUIRE | KF_RET_NULL) |
| 586 | 623 | BTF_ID_FLAGS(func, bpf_testmod_ctx_release, KF_RELEASE) |
| 624 | +BTF_ID_FLAGS(func, bpf_testmod_ops3_call_test_1) | |
| 625 | +BTF_ID_FLAGS(func, bpf_testmod_ops3_call_test_2) | |
| 587 | 626 | BTF_KFUNCS_END(bpf_testmod_common_kfunc_ids) |
| 588 | 627 | |
| 589 | 628 | BTF_ID_LIST(bpf_testmod_dtor_ids) |
| @@ -1094,6 +1133,10 @@ static const struct bpf_verifier_ops bpf_testmod_verifier_ops = { · 504 unmodified lines | ||
| 1094 | 1133 | .is_valid_access = bpf_testmod_ops_is_valid_access, |
| 1095 | 1134 | }; |
| 1096 | 1135 | |
| 1136 | +static const struct bpf_verifier_ops bpf_testmod_verifier_ops3 = { | |
| 1137 | + .is_valid_access = bpf_testmod_ops_is_valid_access, | |
| 1138 | +}; | |
| 1139 | + | |
| 1097 | 1140 | static int bpf_dummy_reg(void *kdata, struct bpf_link *link) |
| 1098 | 1141 | { |
| 1099 | 1142 | struct bpf_testmod_ops *ops = kdata; |
| @@ -1173,6 +1216,68 @@ struct bpf_struct_ops bpf_testmod_ops2 = { · 73 unmodified lines | ||
| 1173 | 1216 | .owner = THIS_MODULE, |
| 1174 | 1217 | }; |
| 1175 | 1218 | |
| 1219 | +static int st_ops3_reg(void *kdata, struct bpf_link *link) | |
| 1220 | +{ | |
| 1221 | + int err = 0; | |
| 1222 | + | |
| 1223 | + mutex_lock(&st_ops_mutex); | |
| 1224 | + if (st_ops3) { | |
| 1225 | + pr_err("st_ops has already been registered\n"); | |
| 1226 | + err = -EEXIST; | |
| 1227 | + goto unlock; | |
| 1228 | + } | |
| 1229 | + st_ops3 = kdata; | |
| 1230 | + | |
| 1231 | +unlock: | |
| 1232 | + mutex_unlock(&st_ops_mutex); | |
| 1233 | + return err; | |
| 1234 | +} | |
| 1235 | + | |
| 1236 | +static void st_ops3_unreg(void *kdata, struct bpf_link *link) | |
| 1237 | +{ | |
| 1238 | + mutex_lock(&st_ops_mutex); | |
| 1239 | + st_ops3 = NULL; | |
| 1240 | + mutex_unlock(&st_ops_mutex); | |
| 1241 | +} | |
| 1242 | + | |
| 1243 | +static void test_1_recursion_detected(struct bpf_prog *prog) | |
| 1244 | +{ | |
| 1245 | + struct bpf_prog_stats *stats; | |
| 1246 | + | |
| 1247 | + stats = this_cpu_ptr(prog->stats); | |
| 1248 | + printk("bpf_testmod: oh no, recursing into test_1, recursion_misses %llu", | |
| 1249 | + u64_stats_read(&stats->misses)); | |
| 1250 | +} | |
| 1251 | + | |
| 1252 | +static int st_ops3_check_member(const struct btf_type *t, | |
| 1253 | + const struct btf_member *member, | |
| 1254 | + const struct bpf_prog *prog) | |
| 1255 | +{ | |
| 1256 | + u32 moff = __btf_member_bit_offset(t, member) / 8; | |
| 1257 | + | |
| 1258 | + switch (moff) { | |
| 1259 | + case offsetof(struct bpf_testmod_ops3, test_1): | |
| 1260 | + prog->aux->priv_stack_requested = true; | |
| 1261 | + prog->aux->recursion_detected = test_1_recursion_detected; | |
| 1262 | + fallthrough; | |
| 1263 | + default: | |
| 1264 | + break; | |
| 1265 | + } | |
| 1266 | + return 0; | |
| 1267 | +} | |
| 1268 | + | |
| 1269 | +struct bpf_struct_ops bpf_testmod_ops3 = { | |
| 1270 | + .verifier_ops = &bpf_testmod_verifier_ops3, | |
| 1271 | + .init = bpf_testmod_ops_init, | |
| 1272 | + .init_member = bpf_testmod_ops_init_member, | |
| 1273 | + .reg = st_ops3_reg, | |
| 1274 | + .unreg = st_ops3_unreg, | |
| 1275 | + .check_member = st_ops3_check_member, | |
| 1276 | + .cfi_stubs = &__bpf_testmod_ops3, | |
| 1277 | + .name = "bpf_testmod_ops3", | |
| 1278 | + .owner = THIS_MODULE, | |
| 1279 | +}; | |
| 1280 | + | |
| 1176 | 1281 | static int bpf_test_mod_st_ops__test_prologue(struct st_ops_args *args) |
| 1177 | 1282 | { |
| 1178 | 1283 | return 0; |
| @@ -1331,6 +1436,7 @@ static int bpf_testmod_init(void) · 152 unmodified lines | ||
| 1331 | 1436 | ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &bpf_testmod_kfunc_set); |
| 1332 | 1437 | ret = ret ?: register_bpf_struct_ops(&bpf_bpf_testmod_ops, bpf_testmod_ops); |
| 1333 | 1438 | ret = ret ?: register_bpf_struct_ops(&bpf_testmod_ops2, bpf_testmod_ops2); |
| 1439 | + ret = ret ?: register_bpf_struct_ops(&bpf_testmod_ops3, bpf_testmod_ops3); | |
| 1334 | 1440 | ret = ret ?: register_bpf_struct_ops(&testmod_st_ops, bpf_testmod_st_ops); |
| 1335 | 1441 | ret = ret ?: register_btf_id_dtor_kfuncs(bpf_testmod_dtors, |
| 1336 | 1442 | ARRAY_SIZE(bpf_testmod_dtors), |
| @@ -94,6 +94,11 @@ struct bpf_testmod_ops2 { · 93 unmodified lines | ||
| 94 | 94 | int (*test_1)(void); |
| 95 | 95 | }; |
| 96 | 96 | |
| 97 | +struct bpf_testmod_ops3 { | |
| 98 | + int (*test_1)(void); | |
| 99 | + int (*test_2)(void); | |
| 100 | +}; | |
| 101 | + | |
| 97 | 102 | struct st_ops_args { |
| 98 | 103 | u64 a; |
| 99 | 104 | }; |
| @@ -6,6 +6,7 @@ · 5 unmodified lines | ||
| 6 | 6 | #include <stdlib.h> |
| 7 | 7 | #include <string.h> |
| 8 | 8 | #include <errno.h> |
| 9 | +#include <syscall.h> | |
| 9 | 10 | #include <bpf/libbpf.h> /* libbpf_num_possible_cpus */ |
| 10 | 11 | |
| 11 | 12 | static inline unsigned int bpf_num_possible_cpus(void) |
| @@ -59,4 +60,15 @@ static inline void bpf_strlcpy(char *dst, const char *src, size_t sz) · 47 unmodified lines | ||
| 59 | 60 | (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER)) |
| 60 | 61 | #endif |
| 61 | 62 | |
| 63 | +/* Availability of gettid across glibc versions is hit-and-miss, therefore | |
| 64 | + * fallback to syscall in this macro and use it everywhere. | |
| 65 | + */ | |
| 66 | +#ifndef sys_gettid | |
| 67 | +#define sys_gettid() syscall(SYS_gettid) | |
| 68 | +#endif | |
| 69 | + | |
| 70 | +#ifndef ENOTSUPP | |
| 71 | +#define ENOTSUPP 524 | |
| 72 | +#endif | |
| 73 | + | |
| 62 | 74 | #endif /* __BPF_UTIL__ */ |
| @@ -1,12 +1,15 @@ | ||
| 1 | −CONFIG_9P_FS=y | |
| 2 | 1 | CONFIG_9P_FS_POSIX_ACL=y |
| 3 | 2 | CONFIG_9P_FS_SECURITY=y |
| 3 | +CONFIG_9P_FS=y | |
| 4 | 4 | CONFIG_CRYPTO_DEV_VIRTIO=y |
| 5 | −CONFIG_NET_9P=y | |
| 5 | +CONFIG_FUSE_FS=y | |
| 6 | +CONFIG_FUSE_PASSTHROUGH=y | |
| 6 | 7 | CONFIG_NET_9P_VIRTIO=y |
| 8 | +CONFIG_NET_9P=y | |
| 7 | 9 | CONFIG_VIRTIO_BALLOON=y |
| 8 | 10 | CONFIG_VIRTIO_BLK=y |
| 9 | 11 | CONFIG_VIRTIO_CONSOLE=y |
| 12 | +CONFIG_VIRTIO_FS=y | |
| 10 | 13 | CONFIG_VIRTIO_NET=y |
| 11 | 14 | CONFIG_VIRTIO_PCI=y |
| 12 | 15 | CONFIG_VIRTIO_VSOCKETS_COMMON=y |
| @@ -0,0 +1,21 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +#include <sys/select.h> | |
| 3 | +#include <unistd.h> | |
| 4 | +#include <errno.h> | |
| 5 | + | |
| 6 | +int read_with_timeout(int fd, char *buf, size_t count, long usec) | |
| 7 | +{ | |
| 8 | + const long M = 1000 * 1000; | |
| 9 | + struct timeval tv = { usec / M, usec % M }; | |
| 10 | + fd_set fds; | |
| 11 | + int err; | |
| 12 | + | |
| 13 | + FD_ZERO(&fds); | |
| 14 | + FD_SET(fd, &fds); | |
| 15 | + err = select(fd + 1, &fds, NULL, NULL, &tv); | |
| 16 | + if (err < 0) | |
| 17 | + return err; | |
| 18 | + if (FD_ISSET(fd, &fds)) | |
| 19 | + return read(fd, buf, count); | |
| 20 | + return -EAGAIN; | |
| 21 | +} | |
| @@ -0,0 +1,7 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +#include <unistd.h> | |
| 3 | + | |
| 4 | +/* As a regular read(2), but allows to specify a timeout in micro-seconds. | |
| 5 | + * Returns -EAGAIN on timeout. | |
| 6 | + */ | |
| 7 | +int read_with_timeout(int fd, char *buf, size_t count, long usec); | |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <bpf/bpf.h> |
| 13 | 13 | #include <bpf/libbpf.h> |
| 14 | 14 | |
| 15 | +#include "bpf_util.h" | |
| 15 | 16 | #include "test_maps.h" |
| 16 | 17 | #include "task_local_storage_helpers.h" |
| 17 | 18 | #include "read_bpf_task_storage_busy.skel.h" |
| @@ -115,7 +116,7 @@ void test_task_storage_map_stress_lookup(void) · 97 unmodified lines | ||
| 115 | 116 | CHECK(err, "attach", "error %d\n", err); |
| 116 | 117 | |
| 117 | 118 | /* Trigger program */ |
| 118 | − syscall(SYS_gettid); | |
| 119 | + sys_gettid(); | |
| 119 | 120 | skel->bss->pid = 0; |
| 120 | 121 | |
| 121 | 122 | CHECK(skel->bss->busy != 0, "bad bpf_task_storage_busy", "got %d\n", skel->bss->busy); |
| @@ -690,7 +690,7 @@ void test_bpf_cookie(void) · 689 unmodified lines | ||
| 690 | 690 | if (!ASSERT_OK_PTR(skel, "skel_open")) |
| 691 | 691 | return; |
| 692 | 692 | |
| 693 | − skel->bss->my_tid = syscall(SYS_gettid); | |
| 693 | + skel->bss->my_tid = sys_gettid(); | |
| 694 | 694 | |
| 695 | 695 | if (test__start_subtest("kprobe")) |
| 696 | 696 | kprobe_subtest(skel); |
| @@ -226,7 +226,7 @@ static void test_task_common_nocheck(struct bpf_iter_attach_opts *opts, · 225 unmodified lines | ||
| 226 | 226 | ASSERT_OK(pthread_create(&thread_id, NULL, &do_nothing_wait, NULL), |
| 227 | 227 | "pthread_create"); |
| 228 | 228 | |
| 229 | − skel->bss->tid = gettid(); | |
| 229 | + skel->bss->tid = sys_gettid(); | |
| 230 | 230 | |
| 231 | 231 | do_dummy_read_opts(skel->progs.dump_task, opts); |
| 232 | 232 | |
| @@ -255,20 +255,20 @@ static void *run_test_task_tid(void *arg) · 22 unmodified lines | ||
| 255 | 255 | union bpf_iter_link_info linfo; |
| 256 | 256 | int num_unknown_tid, num_known_tid; |
| 257 | 257 | |
| 258 | − ASSERT_NEQ(getpid(), gettid(), "check_new_thread_id"); | |
| 258 | + ASSERT_NEQ(getpid(), sys_gettid(), "check_new_thread_id"); | |
| 259 | 259 | |
| 260 | 260 | memset(&linfo, 0, sizeof(linfo)); |
| 261 | − linfo.task.tid = gettid(); | |
| 261 | + linfo.task.tid = sys_gettid(); | |
| 262 | 262 | opts.link_info = &linfo; |
| 263 | 263 | opts.link_info_len = sizeof(linfo); |
| 264 | 264 | test_task_common(&opts, 0, 1); |
| 265 | 265 | |
| 266 | 266 | linfo.task.tid = 0; |
| 267 | 267 | linfo.task.pid = getpid(); |
| 268 | − /* This includes the parent thread, this thread, | |
| 268 | + /* This includes the parent thread, this thread, watchdog timer thread | |
| 269 | 269 | * and the do_nothing_wait thread |
| 270 | 270 | */ |
| 271 | − test_task_common(&opts, 2, 1); | |
| 271 | + test_task_common(&opts, 3, 1); | |
| 272 | 272 | |
| 273 | 273 | test_task_common_nocheck(NULL, &num_unknown_tid, &num_known_tid); |
| 274 | 274 | ASSERT_GT(num_unknown_tid, 2, "check_num_unknown_tid"); |
| @@ -297,7 +297,7 @@ static void test_task_pid(void) · 22 unmodified lines | ||
| 297 | 297 | opts.link_info = &linfo; |
| 298 | 298 | opts.link_info_len = sizeof(linfo); |
| 299 | 299 | |
| 300 | − test_task_common(&opts, 1, 1); | |
| 300 | + test_task_common(&opts, 2, 1); | |
| 301 | 301 | } |
| 302 | 302 | |
| 303 | 303 | static void test_task_pidfd(void) |
| @@ -315,7 +315,7 @@ static void test_task_pidfd(void) · 11 unmodified lines | ||
| 315 | 315 | opts.link_info = &linfo; |
| 316 | 316 | opts.link_info_len = sizeof(linfo); |
| 317 | 317 | |
| 318 | − test_task_common(&opts, 1, 1); | |
| 318 | + test_task_common(&opts, 2, 1); | |
| 319 | 319 | |
| 320 | 320 | close(pidfd); |
| 321 | 321 | } |
| @@ -16,10 +16,6 @@ · 15 unmodified lines | ||
| 16 | 16 | #include "tcp_ca_kfunc.skel.h" |
| 17 | 17 | #include "bpf_cc_cubic.skel.h" |
| 18 | 18 | |
| 19 | −#ifndef ENOTSUPP | |
| 20 | −#define ENOTSUPP 524 | |
| 21 | −#endif | |
| 22 | − | |
| 23 | 19 | static const unsigned int total_bytes = 10 * 1024 * 1024; |
| 24 | 20 | static int expected_stg = 0xeB9F; |
| 25 | 21 | |
| @@ -11,8 +11,8 @@ struct { · 10 unmodified lines | ||
| 11 | 11 | const char *prog_name; |
| 12 | 12 | const char *err_msg; |
| 13 | 13 | } cb_refs_tests[] = { |
| 14 | − { "underflow_prog", "reference has not been acquired before" }, | |
| 15 | − { "leak_prog", "Unreleased reference" }, | |
| 14 | + { "underflow_prog", "must point to scalar, or struct with scalar" }, | |
| 15 | + { "leak_prog", "Possibly NULL pointer passed to helper arg2" }, | |
| 16 | 16 | { "nested_cb", "Unreleased reference id=4 alloc_insn=2" }, /* alloc_insn=2{4,5} */ |
| 17 | 17 | { "non_cb_transfer_ref", "Unreleased reference id=4 alloc_insn=1" }, /* alloc_insn=1{1,2} */ |
| 18 | 18 | }; |
| @@ -63,14 +63,14 @@ static void test_tp_btf(int cgroup_fd) · 62 unmodified lines | ||
| 63 | 63 | if (!ASSERT_OK(err, "map_delete_elem")) |
| 64 | 64 | goto out; |
| 65 | 65 | |
| 66 | − skel->bss->target_pid = syscall(SYS_gettid); | |
| 66 | + skel->bss->target_pid = sys_gettid(); | |
| 67 | 67 | |
| 68 | 68 | err = cgrp_ls_tp_btf__attach(skel); |
| 69 | 69 | if (!ASSERT_OK(err, "skel_attach")) |
| 70 | 70 | goto out; |
| 71 | 71 | |
| 72 | − syscall(SYS_gettid); | |
| 73 | − syscall(SYS_gettid); | |
| 72 | + sys_gettid(); | |
| 73 | + sys_gettid(); | |
| 74 | 74 | |
| 75 | 75 | skel->bss->target_pid = 0; |
| 76 | 76 | |
| @@ -154,7 +154,7 @@ static void test_recursion(int cgroup_fd) · 77 unmodified lines | ||
| 154 | 154 | goto out; |
| 155 | 155 | |
| 156 | 156 | /* trigger sys_enter, make sure it does not cause deadlock */ |
| 157 | − syscall(SYS_gettid); | |
| 157 | + sys_gettid(); | |
| 158 | 158 | |
| 159 | 159 | out: |
| 160 | 160 | cgrp_ls_recursion__destroy(skel); |
| @@ -224,7 +224,7 @@ static void test_yes_rcu_lock(__u64 cgroup_id) · 63 unmodified lines | ||
| 224 | 224 | return; |
| 225 | 225 | |
| 226 | 226 | CGROUP_MODE_SET(skel); |
| 227 | − skel->bss->target_pid = syscall(SYS_gettid); | |
| 227 | + skel->bss->target_pid = sys_gettid(); | |
| 228 | 228 | |
| 229 | 229 | bpf_program__set_autoload(skel->progs.yes_rcu_lock, true); |
| 230 | 230 | err = cgrp_ls_sleepable__load(skel); |
| @@ -1010,7 +1010,7 @@ static void run_core_reloc_tests(bool use_btfgen) · 1009 unmodified lines | ||
| 1010 | 1010 | struct data *data; |
| 1011 | 1011 | void *mmap_data = NULL; |
| 1012 | 1012 | |
| 1013 | − my_pid_tgid = getpid() | ((uint64_t)syscall(SYS_gettid) << 32); | |
| 1013 | + my_pid_tgid = getpid() | ((uint64_t)sys_gettid() << 32); | |
| 1014 | 1014 | |
| 1015 | 1015 | for (i = 0; i < ARRAY_SIZE(test_cases); i++) { |
| 1016 | 1016 | char btf_file[] = "/tmp/core_reloc.btf.XXXXXX"; |
| @@ -192,8 +192,8 @@ static void subtest_task_iters(void) · 191 unmodified lines | ||
| 192 | 192 | syscall(SYS_getpgid); |
| 193 | 193 | iters_task__detach(skel); |
| 194 | 194 | ASSERT_EQ(skel->bss->procs_cnt, 1, "procs_cnt"); |
| 195 | − ASSERT_EQ(skel->bss->threads_cnt, thread_num + 1, "threads_cnt"); | |
| 196 | − ASSERT_EQ(skel->bss->proc_threads_cnt, thread_num + 1, "proc_threads_cnt"); | |
| 195 | + ASSERT_EQ(skel->bss->threads_cnt, thread_num + 2, "threads_cnt"); | |
| 196 | + ASSERT_EQ(skel->bss->proc_threads_cnt, thread_num + 2, "proc_threads_cnt"); | |
| 197 | 197 | ASSERT_EQ(skel->bss->invalid_cnt, 0, "invalid_cnt"); |
| 198 | 198 | pthread_mutex_unlock(&do_nothing_mutex); |
| 199 | 199 | for (int i = 0; i < thread_num; i++) |
| @@ -0,0 +1,126 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* Copyright (c) 2024 Google */ | |
| 3 | + | |
| 4 | +#include <test_progs.h> | |
| 5 | +#include <bpf/libbpf.h> | |
| 6 | +#include <bpf/btf.h> | |
| 7 | +#include "kmem_cache_iter.skel.h" | |
| 8 | + | |
| 9 | +#define SLAB_NAME_MAX 32 | |
| 10 | + | |
| 11 | +struct kmem_cache_result { | |
| 12 | + char name[SLAB_NAME_MAX]; | |
| 13 | + long obj_size; | |
| 14 | +}; | |
| 15 | + | |
| 16 | +static void subtest_kmem_cache_iter_check_task_struct(struct kmem_cache_iter *skel) | |
| 17 | +{ | |
| 18 | + LIBBPF_OPTS(bpf_test_run_opts, opts, | |
| 19 | + .flags = 0, /* Run it with the current task */ | |
| 20 | + ); | |
| 21 | + int prog_fd = bpf_program__fd(skel->progs.check_task_struct); | |
| 22 | + | |
| 23 | + /* Get task_struct and check it if's from a slab cache */ | |
| 24 | + ASSERT_OK(bpf_prog_test_run_opts(prog_fd, &opts), "prog_test_run"); | |
| 25 | + | |
| 26 | + /* The BPF program should set 'found' variable */ | |
| 27 | + ASSERT_EQ(skel->bss->task_struct_found, 1, "task_struct_found"); | |
| 28 | +} | |
| 29 | + | |
| 30 | +static void subtest_kmem_cache_iter_check_slabinfo(struct kmem_cache_iter *skel) | |
| 31 | +{ | |
| 32 | + FILE *fp; | |
| 33 | + int map_fd; | |
| 34 | + char name[SLAB_NAME_MAX]; | |
| 35 | + unsigned long objsize; | |
| 36 | + char rest_of_line[1000]; | |
| 37 | + struct kmem_cache_result r; | |
| 38 | + int seen = 0; | |
| 39 | + | |
| 40 | + fp = fopen("/proc/slabinfo", "r"); | |
| 41 | + if (fp == NULL) { | |
| 42 | + /* CONFIG_SLUB_DEBUG is not enabled */ | |
| 43 | + return; | |
| 44 | + } | |
| 45 | + | |
| 46 | + map_fd = bpf_map__fd(skel->maps.slab_result); | |
| 47 | + | |
| 48 | + /* Ignore first two lines for header */ | |
| 49 | + fscanf(fp, "slabinfo - version: %*d.%*d\n"); | |
| 50 | + fscanf(fp, "# %*s %*s %*s %*s %*s %*s : %[^\n]\n", rest_of_line); | |
| 51 | + | |
| 52 | + /* Compare name and objsize only - others can be changes frequently */ | |
| 53 | + while (fscanf(fp, "%s %*u %*u %lu %*u %*u : %[^\n]\n", | |
| 54 | + name, &objsize, rest_of_line) == 3) { | |
| 55 | + int ret = bpf_map_lookup_elem(map_fd, &seen, &r); | |
| 56 | + | |
| 57 | + if (!ASSERT_OK(ret, "kmem_cache_lookup")) | |
| 58 | + break; | |
| 59 | + | |
| 60 | + ASSERT_STREQ(r.name, name, "kmem_cache_name"); | |
| 61 | + ASSERT_EQ(r.obj_size, objsize, "kmem_cache_objsize"); | |
| 62 | + | |
| 63 | + seen++; | |
| 64 | + } | |
| 65 | + | |
| 66 | + ASSERT_EQ(skel->bss->kmem_cache_seen, seen, "kmem_cache_seen_eq"); | |
| 67 | + | |
| 68 | + fclose(fp); | |
| 69 | +} | |
| 70 | + | |
| 71 | +static void subtest_kmem_cache_iter_open_coded(struct kmem_cache_iter *skel) | |
| 72 | +{ | |
| 73 | + LIBBPF_OPTS(bpf_test_run_opts, topts); | |
| 74 | + int err, fd; | |
| 75 | + | |
| 76 | + /* No need to attach it, just run it directly */ | |
| 77 | + fd = bpf_program__fd(skel->progs.open_coded_iter); | |
| 78 | + | |
| 79 | + err = bpf_prog_test_run_opts(fd, &topts); | |
| 80 | + if (!ASSERT_OK(err, "test_run_opts err")) | |
| 81 | + return; | |
| 82 | + if (!ASSERT_OK(topts.retval, "test_run_opts retval")) | |
| 83 | + return; | |
| 84 | + | |
| 85 | + /* It should be same as we've seen from the explicit iterator */ | |
| 86 | + ASSERT_EQ(skel->bss->open_coded_seen, skel->bss->kmem_cache_seen, "open_code_seen_eq"); | |
| 87 | +} | |
| 88 | + | |
| 89 | +void test_kmem_cache_iter(void) | |
| 90 | +{ | |
| 91 | + struct kmem_cache_iter *skel = NULL; | |
| 92 | + char buf[256]; | |
| 93 | + int iter_fd; | |
| 94 | + | |
| 95 | + skel = kmem_cache_iter__open_and_load(); | |
| 96 | + if (!ASSERT_OK_PTR(skel, "kmem_cache_iter__open_and_load")) | |
| 97 | + return; | |
| 98 | + | |
| 99 | + if (!ASSERT_OK(kmem_cache_iter__attach(skel), "skel_attach")) | |
| 100 | + goto destroy; | |
| 101 | + | |
| 102 | + iter_fd = bpf_iter_create(bpf_link__fd(skel->links.slab_info_collector)); | |
| 103 | + if (!ASSERT_GE(iter_fd, 0, "iter_create")) | |
| 104 | + goto destroy; | |
| 105 | + | |
| 106 | + memset(buf, 0, sizeof(buf)); | |
| 107 | + while (read(iter_fd, buf, sizeof(buf) > 0)) { | |
| 108 | + /* Read out all contents */ | |
| 109 | + printf("%s", buf); | |
| 110 | + } | |
| 111 | + | |
| 112 | + /* Next reads should return 0 */ | |
| 113 | + ASSERT_EQ(read(iter_fd, buf, sizeof(buf)), 0, "read"); | |
| 114 | + | |
| 115 | + if (test__start_subtest("check_task_struct")) | |
| 116 | + subtest_kmem_cache_iter_check_task_struct(skel); | |
| 117 | + if (test__start_subtest("check_slabinfo")) | |
| 118 | + subtest_kmem_cache_iter_check_slabinfo(skel); | |
| 119 | + if (test__start_subtest("open_coded_iter")) | |
| 120 | + subtest_kmem_cache_iter_open_coded(skel); | |
| 121 | + | |
| 122 | + close(iter_fd); | |
| 123 | + | |
| 124 | +destroy: | |
| 125 | + kmem_cache_iter__destroy(skel); | |
| 126 | +} | |
| @@ -6,6 +6,7 @@ · 5 unmodified lines | ||
| 6 | 6 | #include "kprobe_multi_override.skel.h" |
| 7 | 7 | #include "kprobe_multi_session.skel.h" |
| 8 | 8 | #include "kprobe_multi_session_cookie.skel.h" |
| 9 | +#include "kprobe_multi_verifier.skel.h" | |
| 9 | 10 | #include "bpf/libbpf_internal.h" |
| 10 | 11 | #include "bpf/hashmap.h" |
| 11 | 12 | |
| @@ -764,4 +765,5 @@ void test_kprobe_multi_test(void) · 752 unmodified lines | ||
| 764 | 765 | test_session_skel_api(); |
| 765 | 766 | if (test__start_subtest("session_cookie")) |
| 766 | 767 | test_session_cookie_skel_api(); |
| 768 | + RUN_TESTS(kprobe_multi_verifier); | |
| 767 | 769 | } |
| @@ -20,7 +20,7 @@ void test_linked_funcs(void) · 19 unmodified lines | ||
| 20 | 20 | bpf_program__set_autoload(skel->progs.handler1, true); |
| 21 | 21 | bpf_program__set_autoload(skel->progs.handler2, true); |
| 22 | 22 | |
| 23 | − skel->rodata->my_tid = syscall(SYS_gettid); | |
| 23 | + skel->rodata->my_tid = sys_gettid(); | |
| 24 | 24 | skel->bss->syscall_id = SYS_getpgid; |
| 25 | 25 | |
| 26 | 26 | err = linked_funcs__load(skel); |
| @@ -169,7 +169,6 @@ static void bpf_prog_load_log_buf(void) · 168 unmodified lines | ||
| 169 | 169 | ASSERT_GE(fd, 0, "good_fd1"); |
| 170 | 170 | if (fd >= 0) |
| 171 | 171 | close(fd); |
| 172 | − fd = -1; | |
| 173 | 172 | |
| 174 | 173 | /* log_level == 2 should always fill log_buf, even for good prog */ |
| 175 | 174 | log_buf[0] = '\0'; |
| @@ -180,7 +179,6 @@ static void bpf_prog_load_log_buf(void) · 4 unmodified lines | ||
| 180 | 179 | ASSERT_GE(fd, 0, "good_fd2"); |
| 181 | 180 | if (fd >= 0) |
| 182 | 181 | close(fd); |
| 183 | − fd = -1; | |
| 184 | 182 | |
| 185 | 183 | /* log_level == 0 should fill log_buf for bad prog */ |
| 186 | 184 | log_buf[0] = '\0'; |
| @@ -191,7 +189,6 @@ static void bpf_prog_load_log_buf(void) · 4 unmodified lines | ||
| 191 | 189 | ASSERT_LT(fd, 0, "bad_fd"); |
| 192 | 190 | if (fd >= 0) |
| 193 | 191 | close(fd); |
| 194 | − fd = -1; | |
| 195 | 192 | |
| 196 | 193 | free(log_buf); |
| 197 | 194 | } |
| @@ -10,10 +10,6 @@ · 9 unmodified lines | ||
| 10 | 10 | #include "cgroup_helpers.h" |
| 11 | 11 | #include "network_helpers.h" |
| 12 | 12 | |
| 13 | −#ifndef ENOTSUPP | |
| 14 | −#define ENOTSUPP 524 | |
| 15 | −#endif | |
| 16 | − | |
| 17 | 13 | static struct btf *btf; |
| 18 | 14 | |
| 19 | 15 | static __u32 query_prog_cnt(int cgroup_fd, const char *attach_func) |
| @@ -5,7 +5,9 @@ · 4 unmodified lines | ||
| 5 | 5 | #include <sys/syscall.h> |
| 6 | 6 | #include <test_progs.h> |
| 7 | 7 | #include <bpf/btf.h> |
| 8 | + | |
| 8 | 9 | #include "access_map_in_map.skel.h" |
| 10 | +#include "update_map_in_htab.skel.h" | |
| 9 | 11 | |
| 10 | 12 | struct thread_ctx { |
| 11 | 13 | pthread_barrier_t barrier; |
| @@ -127,6 +129,131 @@ out: · 115 unmodified lines | ||
| 127 | 129 | access_map_in_map__destroy(skel); |
| 128 | 130 | } |
| 129 | 131 | |
| 132 | +static void add_del_fd_htab(int outer_fd) | |
| 133 | +{ | |
| 134 | + int inner_fd, err; | |
| 135 | + int key = 1; | |
| 136 | + | |
| 137 | + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL); | |
| 138 | + if (!ASSERT_OK_FD(inner_fd, "inner1")) | |
| 139 | + return; | |
| 140 | + err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_NOEXIST); | |
| 141 | + close(inner_fd); | |
| 142 | + if (!ASSERT_OK(err, "add")) | |
| 143 | + return; | |
| 144 | + | |
| 145 | + /* Delete */ | |
| 146 | + err = bpf_map_delete_elem(outer_fd, &key); | |
| 147 | + ASSERT_OK(err, "del"); | |
| 148 | +} | |
| 149 | + | |
| 150 | +static void overwrite_fd_htab(int outer_fd) | |
| 151 | +{ | |
| 152 | + int inner_fd, err; | |
| 153 | + int key = 1; | |
| 154 | + | |
| 155 | + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL); | |
| 156 | + if (!ASSERT_OK_FD(inner_fd, "inner1")) | |
| 157 | + return; | |
| 158 | + err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_NOEXIST); | |
| 159 | + close(inner_fd); | |
| 160 | + if (!ASSERT_OK(err, "add")) | |
| 161 | + return; | |
| 162 | + | |
| 163 | + /* Overwrite */ | |
| 164 | + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr2", 4, 4, 1, NULL); | |
| 165 | + if (!ASSERT_OK_FD(inner_fd, "inner2")) | |
| 166 | + goto out; | |
| 167 | + err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_EXIST); | |
| 168 | + close(inner_fd); | |
| 169 | + if (!ASSERT_OK(err, "overwrite")) | |
| 170 | + goto out; | |
| 171 | + | |
| 172 | + err = bpf_map_delete_elem(outer_fd, &key); | |
| 173 | + ASSERT_OK(err, "del"); | |
| 174 | + return; | |
| 175 | +out: | |
| 176 | + bpf_map_delete_elem(outer_fd, &key); | |
| 177 | +} | |
| 178 | + | |
| 179 | +static void lookup_delete_fd_htab(int outer_fd) | |
| 180 | +{ | |
| 181 | + int key = 1, value; | |
| 182 | + int inner_fd, err; | |
| 183 | + | |
| 184 | + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL); | |
| 185 | + if (!ASSERT_OK_FD(inner_fd, "inner1")) | |
| 186 | + return; | |
| 187 | + err = bpf_map_update_elem(outer_fd, &key, &inner_fd, BPF_NOEXIST); | |
| 188 | + close(inner_fd); | |
| 189 | + if (!ASSERT_OK(err, "add")) | |
| 190 | + return; | |
| 191 | + | |
| 192 | + /* lookup_and_delete is not supported for htab of maps */ | |
| 193 | + err = bpf_map_lookup_and_delete_elem(outer_fd, &key, &value); | |
| 194 | + ASSERT_EQ(err, -ENOTSUPP, "lookup_del"); | |
| 195 | + | |
| 196 | + err = bpf_map_delete_elem(outer_fd, &key); | |
| 197 | + ASSERT_OK(err, "del"); | |
| 198 | +} | |
| 199 | + | |
| 200 | +static void batched_lookup_delete_fd_htab(int outer_fd) | |
| 201 | +{ | |
| 202 | + int keys[2] = {1, 2}, values[2]; | |
| 203 | + unsigned int cnt, batch; | |
| 204 | + int inner_fd, err; | |
| 205 | + | |
| 206 | + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr1", 4, 4, 1, NULL); | |
| 207 | + if (!ASSERT_OK_FD(inner_fd, "inner1")) | |
| 208 | + return; | |
| 209 | + | |
| 210 | + err = bpf_map_update_elem(outer_fd, &keys[0], &inner_fd, BPF_NOEXIST); | |
| 211 | + close(inner_fd); | |
| 212 | + if (!ASSERT_OK(err, "add1")) | |
| 213 | + return; | |
| 214 | + | |
| 215 | + inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "arr2", 4, 4, 1, NULL); | |
| 216 | + if (!ASSERT_OK_FD(inner_fd, "inner2")) | |
| 217 | + goto out; | |
| 218 | + err = bpf_map_update_elem(outer_fd, &keys[1], &inner_fd, BPF_NOEXIST); | |
| 219 | + close(inner_fd); | |
| 220 | + if (!ASSERT_OK(err, "add2")) | |
| 221 | + goto out; | |
| 222 | + | |
| 223 | + /* batched lookup_and_delete */ | |
| 224 | + cnt = ARRAY_SIZE(keys); | |
| 225 | + err = bpf_map_lookup_and_delete_batch(outer_fd, NULL, &batch, keys, values, &cnt, NULL); | |
| 226 | + ASSERT_TRUE((!err || err == -ENOENT), "delete_batch ret"); | |
| 227 | + ASSERT_EQ(cnt, ARRAY_SIZE(keys), "delete_batch cnt"); | |
| 228 | + | |
| 229 | +out: | |
| 230 | + bpf_map_delete_elem(outer_fd, &keys[0]); | |
| 231 | +} | |
| 232 | + | |
| 233 | +static void test_update_map_in_htab(bool preallocate) | |
| 234 | +{ | |
| 235 | + struct update_map_in_htab *skel; | |
| 236 | + int err, fd; | |
| 237 | + | |
| 238 | + skel = update_map_in_htab__open(); | |
| 239 | + if (!ASSERT_OK_PTR(skel, "open")) | |
| 240 | + return; | |
| 241 | + | |
| 242 | + err = update_map_in_htab__load(skel); | |
| 243 | + if (!ASSERT_OK(err, "load")) | |
| 244 | + goto out; | |
| 245 | + | |
| 246 | + fd = preallocate ? bpf_map__fd(skel->maps.outer_htab_map) : | |
| 247 | + bpf_map__fd(skel->maps.outer_alloc_htab_map); | |
| 248 | + | |
| 249 | + add_del_fd_htab(fd); | |
| 250 | + overwrite_fd_htab(fd); | |
| 251 | + lookup_delete_fd_htab(fd); | |
| 252 | + batched_lookup_delete_fd_htab(fd); | |
| 253 | +out: | |
| 254 | + update_map_in_htab__destroy(skel); | |
| 255 | +} | |
| 256 | + | |
| 130 | 257 | void test_map_in_map(void) |
| 131 | 258 | { |
| 132 | 259 | if (test__start_subtest("acc_map_in_array")) |
| @@ -137,5 +264,8 @@ void test_map_in_map(void) · 4 unmodified lines | ||
| 137 | 264 | test_map_in_map_access("access_map_in_htab", "outer_htab_map"); |
| 138 | 265 | if (test__start_subtest("sleepable_acc_map_in_htab")) |
| 139 | 266 | test_map_in_map_access("sleepable_access_map_in_htab", "outer_htab_map"); |
| 267 | + if (test__start_subtest("update_map_in_htab")) | |
| 268 | + test_update_map_in_htab(true); | |
| 269 | + if (test__start_subtest("update_map_in_alloc_htab")) | |
| 270 | + test_update_map_in_htab(false); | |
| 140 | 271 | } |
| 141 | − | |
| @@ -23,7 +23,7 @@ static int get_pid_tgid(pid_t *pid, pid_t *tgid, · 22 unmodified lines | ||
| 23 | 23 | struct stat st; |
| 24 | 24 | int err; |
| 25 | 25 | |
| 26 | − *pid = syscall(SYS_gettid); | |
| 26 | + *pid = sys_gettid(); | |
| 27 | 27 | *tgid = getpid(); |
| 28 | 28 | |
| 29 | 29 | err = stat("/proc/self/ns/pid", &st); |
| @@ -4,8 +4,12 @@ · 3 unmodified lines | ||
| 4 | 4 | #include <pthread.h> |
| 5 | 5 | #include <sched.h> |
| 6 | 6 | #include <test_progs.h> |
| 7 | +#include "testing_helpers.h" | |
| 7 | 8 | #include "test_perf_link.skel.h" |
| 8 | 9 | |
| 10 | +#define BURN_TIMEOUT_MS 100 | |
| 11 | +#define BURN_TIMEOUT_NS BURN_TIMEOUT_MS * 1000000 | |
| 12 | + | |
| 9 | 13 | static void burn_cpu(void) |
| 10 | 14 | { |
| 11 | 15 | volatile int j = 0; |
| @@ -32,6 +36,7 @@ void serial_test_perf_link(void) · 20 unmodified lines | ||
| 32 | 36 | int run_cnt_before, run_cnt_after; |
| 33 | 37 | struct bpf_link_info info; |
| 34 | 38 | __u32 info_len = sizeof(info); |
| 39 | + __u64 timeout_time_ns; | |
| 35 | 40 | |
| 36 | 41 | /* create perf event */ |
| 37 | 42 | memset(&attr, 0, sizeof(attr)); |
| @@ -63,8 +68,14 @@ void serial_test_perf_link(void) · 25 unmodified lines | ||
| 63 | 68 | ASSERT_GT(info.prog_id, 0, "link_prog_id"); |
| 64 | 69 | |
| 65 | 70 | /* ensure we get at least one perf_event prog execution */ |
| 66 | − burn_cpu(); | |
| 67 | − ASSERT_GT(skel->bss->run_cnt, 0, "run_cnt"); | |
| 71 | + timeout_time_ns = get_time_ns() + BURN_TIMEOUT_NS; | |
| 72 | + while (true) { | |
| 73 | + burn_cpu(); | |
| 74 | + if (skel->bss->run_cnt > 0) | |
| 75 | + break; | |
| 76 | + if (!ASSERT_LT(get_time_ns(), timeout_time_ns, "run_cnt_timeout")) | |
| 77 | + break; | |
| 78 | + } | |
| 68 | 79 | |
| 69 | 80 | /* perf_event is still active, but we close link and BPF program |
| 70 | 81 | * shouldn't be executed anymore |
| @@ -0,0 +1,25 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ | |
| 3 | + | |
| 4 | +#include <test_progs.h> | |
| 5 | +#include "raw_tp_null.skel.h" | |
| 6 | + | |
| 7 | +void test_raw_tp_null(void) | |
| 8 | +{ | |
| 9 | + struct raw_tp_null *skel; | |
| 10 | + | |
| 11 | + skel = raw_tp_null__open_and_load(); | |
| 12 | + if (!ASSERT_OK_PTR(skel, "raw_tp_null__open_and_load")) | |
| 13 | + return; | |
| 14 | + | |
| 15 | + skel->bss->tid = sys_gettid(); | |
| 16 | + | |
| 17 | + if (!ASSERT_OK(raw_tp_null__attach(skel), "raw_tp_null__attach")) | |
| 18 | + goto end; | |
| 19 | + | |
| 20 | + ASSERT_OK(trigger_module_test_read(2), "trigger testmod read"); | |
| 21 | + ASSERT_EQ(skel->bss->i, 3, "invocations"); | |
| 22 | + | |
| 23 | +end: | |
| 24 | + raw_tp_null__destroy(skel); | |
| 25 | +} | |
| @@ -21,7 +21,7 @@ static void test_success(void) · 20 unmodified lines | ||
| 21 | 21 | if (!ASSERT_OK_PTR(skel, "skel_open")) |
| 22 | 22 | return; |
| 23 | 23 | |
| 24 | − skel->bss->target_pid = syscall(SYS_gettid); | |
| 24 | + skel->bss->target_pid = sys_gettid(); | |
| 25 | 25 | |
| 26 | 26 | bpf_program__set_autoload(skel->progs.get_cgroup_id, true); |
| 27 | 27 | bpf_program__set_autoload(skel->progs.task_succ, true); |
| @@ -58,7 +58,7 @@ static void test_rcuptr_acquire(void) · 30 unmodified lines | ||
| 58 | 58 | if (!ASSERT_OK_PTR(skel, "skel_open")) |
| 59 | 59 | return; |
| 60 | 60 | |
| 61 | − skel->bss->target_pid = syscall(SYS_gettid); | |
| 61 | + skel->bss->target_pid = sys_gettid(); | |
| 62 | 62 | |
| 63 | 63 | bpf_program__set_autoload(skel->progs.task_acquire, true); |
| 64 | 64 | err = rcu_read_lock__load(skel); |
| @@ -3,22 +3,32 @@ · 2 unmodified lines | ||
| 3 | 3 | #include <sys/time.h> |
| 4 | 4 | #include <sys/resource.h> |
| 5 | 5 | #include "test_send_signal_kern.skel.h" |
| 6 | +#include "io_helpers.h" | |
| 6 | 7 | |
| 7 | 8 | static int sigusr1_received; |
| 8 | 9 | |
| 9 | 10 | static void sigusr1_handler(int signum) |
| 10 | 11 | { |
| 11 | − sigusr1_received = 1; | |
| 12 | + sigusr1_received = 8; | |
| 13 | +} | |
| 14 | + | |
| 15 | +static void sigusr1_siginfo_handler(int s, siginfo_t *i, void *v) | |
| 16 | +{ | |
| 17 | + sigusr1_received = (int)(long long)i->si_value.sival_ptr; | |
| 12 | 18 | } |
| 13 | 19 | |
| 14 | 20 | static void test_send_signal_common(struct perf_event_attr *attr, |
| 15 | − bool signal_thread) | |
| 21 | + bool signal_thread, bool remote) | |
| 16 | 22 | { |
| 17 | 23 | struct test_send_signal_kern *skel; |
| 24 | + struct sigaction sa; | |
| 18 | 25 | int pipe_c2p[2], pipe_p2c[2]; |
| 19 | 26 | int err = -1, pmu_fd = -1; |
| 27 | + volatile int j = 0; | |
| 28 | + int retry_count; | |
| 20 | 29 | char buf[256]; |
| 21 | 30 | pid_t pid; |
| 31 | + int old_prio; | |
| 22 | 32 | |
| 23 | 33 | if (!ASSERT_OK(pipe(pipe_c2p), "pipe_c2p")) |
| 24 | 34 | return; |
| @@ -39,11 +49,14 @@ static void test_send_signal_common(struct perf_event_attr *attr, · 14 unmodified lines | ||
| 39 | 49 | } |
| 40 | 50 | |
| 41 | 51 | if (pid == 0) { |
| 42 | − int old_prio; | |
| 43 | − volatile int j = 0; | |
| 44 | − | |
| 45 | 52 | /* install signal handler and notify parent */ |
| 46 | − ASSERT_NEQ(signal(SIGUSR1, sigusr1_handler), SIG_ERR, "signal"); | |
| 53 | + if (remote) { | |
| 54 | + sa.sa_sigaction = sigusr1_siginfo_handler; | |
| 55 | + sa.sa_flags = SA_RESTART | SA_SIGINFO; | |
| 56 | + ASSERT_NEQ(sigaction(SIGUSR1, &sa, NULL), -1, "sigaction"); | |
| 57 | + } else { | |
| 58 | + ASSERT_NEQ(signal(SIGUSR1, sigusr1_handler), SIG_ERR, "signal"); | |
| 59 | + } | |
| 47 | 60 | |
| 48 | 61 | close(pipe_c2p[0]); /* close read */ |
| 49 | 62 | close(pipe_p2c[1]); /* close write */ |
| @@ -52,10 +65,12 @@ static void test_send_signal_common(struct perf_event_attr *attr, · 2 unmodified lines | ||
| 52 | 65 | * that if an interrupt happens, the underlying task |
| 53 | 66 | * is this process. |
| 54 | 67 | */ |
| 55 | − errno = 0; | |
| 56 | − old_prio = getpriority(PRIO_PROCESS, 0); | |
| 57 | − ASSERT_OK(errno, "getpriority"); | |
| 58 | − ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); | |
| 68 | + if (!remote) { | |
| 69 | + errno = 0; | |
| 70 | + old_prio = getpriority(PRIO_PROCESS, 0); | |
| 71 | + ASSERT_OK(errno, "getpriority"); | |
| 72 | + ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); | |
| 73 | + } | |
| 59 | 74 | |
| 60 | 75 | /* notify parent signal handler is installed */ |
| 61 | 76 | ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write"); |
| @@ -66,20 +81,25 @@ static void test_send_signal_common(struct perf_event_attr *attr, · 4 unmodified lines | ||
| 66 | 81 | /* wait a little for signal handler */ |
| 67 | 82 | for (int i = 0; i < 1000000000 && !sigusr1_received; i++) { |
| 68 | 83 | j /= i + j + 1; |
| 69 | − if (!attr) | |
| 70 | − /* trigger the nanosleep tracepoint program. */ | |
| 71 | − usleep(1); | |
| 84 | + if (remote) | |
| 85 | + sleep(1); | |
| 86 | + else | |
| 87 | + if (!attr) | |
| 88 | + /* trigger the nanosleep tracepoint program. */ | |
| 89 | + usleep(1); | |
| 72 | 90 | } |
| 73 | 91 | |
| 74 | − buf[0] = sigusr1_received ? '2' : '0'; | |
| 75 | − ASSERT_EQ(sigusr1_received, 1, "sigusr1_received"); | |
| 92 | + buf[0] = sigusr1_received; | |
| 93 | + | |
| 94 | + ASSERT_EQ(sigusr1_received, 8, "sigusr1_received"); | |
| 76 | 95 | ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write"); |
| 77 | 96 | |
| 78 | 97 | /* wait for parent notification and exit */ |
| 79 | 98 | ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read"); |
| 80 | 99 | |
| 81 | 100 | /* restore the old priority */ |
| 82 | − ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); | |
| 101 | + if (!remote) | |
| 102 | + ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); | |
| 83 | 103 | |
| 84 | 104 | close(pipe_c2p[1]); |
| 85 | 105 | close(pipe_p2c[0]); |
| @@ -93,6 +113,17 @@ static void test_send_signal_common(struct perf_event_attr *attr, · 7 unmodified lines | ||
| 93 | 113 | if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) |
| 94 | 114 | goto skel_open_load_failure; |
| 95 | 115 | |
| 116 | + /* boost with a high priority so we got a higher chance | |
| 117 | + * that if an interrupt happens, the underlying task | |
| 118 | + * is this process. | |
| 119 | + */ | |
| 120 | + if (remote) { | |
| 121 | + errno = 0; | |
| 122 | + old_prio = getpriority(PRIO_PROCESS, 0); | |
| 123 | + ASSERT_OK(errno, "getpriority"); | |
| 124 | + ASSERT_OK(setpriority(PRIO_PROCESS, 0, -20), "setpriority"); | |
| 125 | + } | |
| 126 | + | |
| 96 | 127 | if (!attr) { |
| 97 | 128 | err = test_send_signal_kern__attach(skel); |
| 98 | 129 | if (!ASSERT_OK(err, "skel_attach")) { |
| @@ -100,8 +131,12 @@ static void test_send_signal_common(struct perf_event_attr *attr, · 1 unmodified line | ||
| 100 | 131 | goto destroy_skel; |
| 101 | 132 | } |
| 102 | 133 | } else { |
| 103 | − pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1 /* cpu */, | |
| 104 | − -1 /* group id */, 0 /* flags */); | |
| 134 | + if (!remote) | |
| 135 | + pmu_fd = syscall(__NR_perf_event_open, attr, pid, -1 /* cpu */, | |
| 136 | + -1 /* group id */, 0 /* flags */); | |
| 137 | + else | |
| 138 | + pmu_fd = syscall(__NR_perf_event_open, attr, getpid(), -1 /* cpu */, | |
| 139 | + -1 /* group id */, 0 /* flags */); | |
| 105 | 140 | if (!ASSERT_GE(pmu_fd, 0, "perf_event_open")) { |
| 106 | 141 | err = -1; |
| 107 | 142 | goto destroy_skel; |
| @@ -119,13 +154,36 @@ static void test_send_signal_common(struct perf_event_attr *attr, · 11 unmodified lines | ||
| 119 | 154 | /* trigger the bpf send_signal */ |
| 120 | 155 | skel->bss->signal_thread = signal_thread; |
| 121 | 156 | skel->bss->sig = SIGUSR1; |
| 122 | − skel->bss->pid = pid; | |
| 157 | + if (!remote) { | |
| 158 | + skel->bss->target_pid = 0; | |
| 159 | + skel->bss->pid = pid; | |
| 160 | + } else { | |
| 161 | + skel->bss->target_pid = pid; | |
| 162 | + skel->bss->pid = getpid(); | |
| 163 | + } | |
| 123 | 164 | |
| 124 | 165 | /* notify child that bpf program can send_signal now */ |
| 125 | 166 | ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write"); |
| 126 | 167 | |
| 127 | − /* wait for result */ | |
| 128 | − err = read(pipe_c2p[0], buf, 1); | |
| 168 | + for (retry_count = 0;;) { | |
| 169 | + /* For the remote test, the BPF program is triggered from this | |
| 170 | + * process but the other process/thread is signaled. | |
| 171 | + */ | |
| 172 | + if (remote) { | |
| 173 | + if (!attr) { | |
| 174 | + for (int i = 0; i < 10; i++) | |
| 175 | + usleep(1); | |
| 176 | + } else { | |
| 177 | + for (int i = 0; i < 100000000; i++) | |
| 178 | + j /= i + 1; | |
| 179 | + } | |
| 180 | + } | |
| 181 | + /* wait for result */ | |
| 182 | + err = read_with_timeout(pipe_c2p[0], buf, 1, 100); | |
| 183 | + if (err == -EAGAIN && retry_count++ < 10000) | |
| 184 | + continue; | |
| 185 | + break; | |
| 186 | + } | |
| 129 | 187 | if (!ASSERT_GE(err, 0, "reading pipe")) |
| 130 | 188 | goto disable_pmu; |
| 131 | 189 | if (!ASSERT_GT(err, 0, "reading pipe error: size 0")) { |
| @@ -133,7 +191,7 @@ static void test_send_signal_common(struct perf_event_attr *attr, · 1 unmodified line | ||
| 133 | 191 | goto disable_pmu; |
| 134 | 192 | } |
| 135 | 193 | |
| 136 | − ASSERT_EQ(buf[0], '2', "incorrect result"); | |
| 194 | + ASSERT_EQ(buf[0], 8, "incorrect result"); | |
| 137 | 195 | |
| 138 | 196 | /* notify child safe to exit */ |
| 139 | 197 | ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write"); |
| @@ -142,18 +200,21 @@ disable_pmu: · 2 unmodified lines | ||
| 142 | 200 | close(pmu_fd); |
| 143 | 201 | destroy_skel: |
| 144 | 202 | test_send_signal_kern__destroy(skel); |
| 203 | + /* restore the old priority */ | |
| 204 | + if (remote) | |
| 205 | + ASSERT_OK(setpriority(PRIO_PROCESS, 0, old_prio), "setpriority"); | |
| 145 | 206 | skel_open_load_failure: |
| 146 | 207 | close(pipe_c2p[0]); |
| 147 | 208 | close(pipe_p2c[1]); |
| 148 | 209 | wait(NULL); |
| 149 | 210 | } |
| 150 | 211 | |
| 151 | −static void test_send_signal_tracepoint(bool signal_thread) | |
| 212 | +static void test_send_signal_tracepoint(bool signal_thread, bool remote) | |
| 152 | 213 | { |
| 153 | − test_send_signal_common(NULL, signal_thread); | |
| 214 | + test_send_signal_common(NULL, signal_thread, remote); | |
| 154 | 215 | } |
| 155 | 216 | |
| 156 | −static void test_send_signal_perf(bool signal_thread) | |
| 217 | +static void test_send_signal_perf(bool signal_thread, bool remote) | |
| 157 | 218 | { |
| 158 | 219 | struct perf_event_attr attr = { |
| 159 | 220 | .freq = 1, |
| @@ -162,13 +223,14 @@ static void test_send_signal_perf(bool signal_thread) · 2 unmodified lines | ||
| 162 | 223 | .config = PERF_COUNT_SW_CPU_CLOCK, |
| 163 | 224 | }; |
| 164 | 225 | |
| 165 | − test_send_signal_common(&attr, signal_thread); | |
| 226 | + test_send_signal_common(&attr, signal_thread, remote); | |
| 166 | 227 | } |
| 167 | 228 | |
| 168 | −static void test_send_signal_nmi(bool signal_thread) | |
| 229 | +static void test_send_signal_nmi(bool signal_thread, bool remote) | |
| 169 | 230 | { |
| 170 | 231 | struct perf_event_attr attr = { |
| 171 | − .sample_period = 1, | |
| 232 | + .freq = 1, | |
| 233 | + .sample_freq = 1000, | |
| 172 | 234 | .type = PERF_TYPE_HARDWARE, |
| 173 | 235 | .config = PERF_COUNT_HW_CPU_CYCLES, |
| 174 | 236 | }; |
| @@ -191,21 +253,35 @@ static void test_send_signal_nmi(bool signal_thread) · 16 unmodified lines | ||
| 191 | 253 | close(pmu_fd); |
| 192 | 254 | } |
| 193 | 255 | |
| 194 | − test_send_signal_common(&attr, signal_thread); | |
| 256 | + test_send_signal_common(&attr, signal_thread, remote); | |
| 195 | 257 | } |
| 196 | 258 | |
| 197 | 259 | void test_send_signal(void) |
| 198 | 260 | { |
| 199 | 261 | if (test__start_subtest("send_signal_tracepoint")) |
| 200 | − test_send_signal_tracepoint(false); | |
| 262 | + test_send_signal_tracepoint(false, false); | |
| 201 | 263 | if (test__start_subtest("send_signal_perf")) |
| 202 | − test_send_signal_perf(false); | |
| 264 | + test_send_signal_perf(false, false); | |
| 203 | 265 | if (test__start_subtest("send_signal_nmi")) |
| 204 | − test_send_signal_nmi(false); | |
| 266 | + test_send_signal_nmi(false, false); | |
| 205 | 267 | if (test__start_subtest("send_signal_tracepoint_thread")) |
| 206 | − test_send_signal_tracepoint(true); | |
| 268 | + test_send_signal_tracepoint(true, false); | |
| 207 | 269 | if (test__start_subtest("send_signal_perf_thread")) |
| 208 | − test_send_signal_perf(true); | |
| 270 | + test_send_signal_perf(true, false); | |
| 209 | 271 | if (test__start_subtest("send_signal_nmi_thread")) |
| 210 | − test_send_signal_nmi(true); | |
| 272 | + test_send_signal_nmi(true, false); | |
| 273 | + | |
| 274 | + /* Signal remote thread and thread group */ | |
| 275 | + if (test__start_subtest("send_signal_tracepoint_remote")) | |
| 276 | + test_send_signal_tracepoint(false, true); | |
| 277 | + if (test__start_subtest("send_signal_perf_remote")) | |
| 278 | + test_send_signal_perf(false, true); | |
| 279 | + if (test__start_subtest("send_signal_nmi_remote")) | |
| 280 | + test_send_signal_nmi(false, true); | |
| 281 | + if (test__start_subtest("send_signal_tracepoint_thread_remote")) | |
| 282 | + test_send_signal_tracepoint(true, true); | |
| 283 | + if (test__start_subtest("send_signal_perf_thread_remote")) | |
| 284 | + test_send_signal_perf(true, true); | |
| 285 | + if (test__start_subtest("send_signal_nmi_thread_remote")) | |
| 286 | + test_send_signal_nmi(true, true); | |
| 211 | 287 | } |
| @@ -23,10 +23,6 @@ · 22 unmodified lines | ||
| 23 | 23 | #include "getpeername_unix_prog.skel.h" |
| 24 | 24 | #include "network_helpers.h" |
| 25 | 25 | |
| 26 | −#ifndef ENOTSUPP | |
| 27 | −# define ENOTSUPP 524 | |
| 28 | −#endif | |
| 29 | − | |
| 30 | 26 | #define TEST_NS "sock_addr" |
| 31 | 27 | #define TEST_IF_PREFIX "test_sock_addr" |
| 32 | 28 | #define TEST_IPV4 "127.0.0.4" |
| @@ -0,0 +1,348 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +#include <linux/bpf.h> | |
| 3 | +#include <test_progs.h> | |
| 4 | +#include "cgroup_helpers.h" | |
| 5 | + | |
| 6 | +static char bpf_log_buf[4096]; | |
| 7 | +static bool verbose; | |
| 8 | + | |
| 9 | +enum sock_create_test_error { | |
| 10 | + OK = 0, | |
| 11 | + DENY_CREATE, | |
| 12 | +}; | |
| 13 | + | |
| 14 | +static struct sock_create_test { | |
| 15 | + const char *descr; | |
| 16 | + const struct bpf_insn insns[64]; | |
| 17 | + enum bpf_attach_type attach_type; | |
| 18 | + enum bpf_attach_type expected_attach_type; | |
| 19 | + | |
| 20 | + int domain; | |
| 21 | + int type; | |
| 22 | + int protocol; | |
| 23 | + | |
| 24 | + int optname; | |
| 25 | + int optval; | |
| 26 | + enum sock_create_test_error error; | |
| 27 | +} tests[] = { | |
| 28 | + { | |
| 29 | + .descr = "AF_INET set priority", | |
| 30 | + .insns = { | |
| 31 | + /* r3 = 123 (priority) */ | |
| 32 | + BPF_MOV64_IMM(BPF_REG_3, 123), | |
| 33 | + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, | |
| 34 | + offsetof(struct bpf_sock, priority)), | |
| 35 | + | |
| 36 | + /* return 1 */ | |
| 37 | + BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 38 | + BPF_EXIT_INSN(), | |
| 39 | + }, | |
| 40 | + .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 41 | + .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 42 | + | |
| 43 | + .domain = AF_INET, | |
| 44 | + .type = SOCK_DGRAM, | |
| 45 | + | |
| 46 | + .optname = SO_PRIORITY, | |
| 47 | + .optval = 123, | |
| 48 | + }, | |
| 49 | + { | |
| 50 | + .descr = "AF_INET6 set priority", | |
| 51 | + .insns = { | |
| 52 | + /* r3 = 123 (priority) */ | |
| 53 | + BPF_MOV64_IMM(BPF_REG_3, 123), | |
| 54 | + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, | |
| 55 | + offsetof(struct bpf_sock, priority)), | |
| 56 | + | |
| 57 | + /* return 1 */ | |
| 58 | + BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 59 | + BPF_EXIT_INSN(), | |
| 60 | + }, | |
| 61 | + .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 62 | + .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 63 | + | |
| 64 | + .domain = AF_INET6, | |
| 65 | + .type = SOCK_DGRAM, | |
| 66 | + | |
| 67 | + .optname = SO_PRIORITY, | |
| 68 | + .optval = 123, | |
| 69 | + }, | |
| 70 | + { | |
| 71 | + .descr = "AF_INET set mark", | |
| 72 | + .insns = { | |
| 73 | + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
| 74 | + | |
| 75 | + /* get uid of process */ | |
| 76 | + BPF_EMIT_CALL(BPF_FUNC_get_current_uid_gid), | |
| 77 | + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff), | |
| 78 | + | |
| 79 | + /* if uid is 0, use given mark(666), else use uid as the mark */ | |
| 80 | + BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), | |
| 81 | + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), | |
| 82 | + BPF_MOV64_IMM(BPF_REG_3, 666), | |
| 83 | + | |
| 84 | + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), | |
| 85 | + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, | |
| 86 | + offsetof(struct bpf_sock, mark)), | |
| 87 | + | |
| 88 | + /* return 1 */ | |
| 89 | + BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 90 | + BPF_EXIT_INSN(), | |
| 91 | + }, | |
| 92 | + .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 93 | + .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 94 | + | |
| 95 | + .domain = AF_INET, | |
| 96 | + .type = SOCK_DGRAM, | |
| 97 | + | |
| 98 | + .optname = SO_MARK, | |
| 99 | + .optval = 666, | |
| 100 | + }, | |
| 101 | + { | |
| 102 | + .descr = "AF_INET6 set mark", | |
| 103 | + .insns = { | |
| 104 | + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
| 105 | + | |
| 106 | + /* get uid of process */ | |
| 107 | + BPF_EMIT_CALL(BPF_FUNC_get_current_uid_gid), | |
| 108 | + BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff), | |
| 109 | + | |
| 110 | + /* if uid is 0, use given mark(666), else use uid as the mark */ | |
| 111 | + BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), | |
| 112 | + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), | |
| 113 | + BPF_MOV64_IMM(BPF_REG_3, 666), | |
| 114 | + | |
| 115 | + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), | |
| 116 | + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, | |
| 117 | + offsetof(struct bpf_sock, mark)), | |
| 118 | + | |
| 119 | + /* return 1 */ | |
| 120 | + BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 121 | + BPF_EXIT_INSN(), | |
| 122 | + }, | |
| 123 | + .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 124 | + .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 125 | + | |
| 126 | + .domain = AF_INET6, | |
| 127 | + .type = SOCK_DGRAM, | |
| 128 | + | |
| 129 | + .optname = SO_MARK, | |
| 130 | + .optval = 666, | |
| 131 | + }, | |
| 132 | + { | |
| 133 | + .descr = "AF_INET bound to iface", | |
| 134 | + .insns = { | |
| 135 | + /* r3 = 1 (lo interface) */ | |
| 136 | + BPF_MOV64_IMM(BPF_REG_3, 1), | |
| 137 | + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, | |
| 138 | + offsetof(struct bpf_sock, bound_dev_if)), | |
| 139 | + | |
| 140 | + /* return 1 */ | |
| 141 | + BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 142 | + BPF_EXIT_INSN(), | |
| 143 | + }, | |
| 144 | + .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 145 | + .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 146 | + | |
| 147 | + .domain = AF_INET, | |
| 148 | + .type = SOCK_DGRAM, | |
| 149 | + | |
| 150 | + .optname = SO_BINDTOIFINDEX, | |
| 151 | + .optval = 1, | |
| 152 | + }, | |
| 153 | + { | |
| 154 | + .descr = "AF_INET6 bound to iface", | |
| 155 | + .insns = { | |
| 156 | + /* r3 = 1 (lo interface) */ | |
| 157 | + BPF_MOV64_IMM(BPF_REG_3, 1), | |
| 158 | + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, | |
| 159 | + offsetof(struct bpf_sock, bound_dev_if)), | |
| 160 | + | |
| 161 | + /* return 1 */ | |
| 162 | + BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 163 | + BPF_EXIT_INSN(), | |
| 164 | + }, | |
| 165 | + .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 166 | + .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 167 | + | |
| 168 | + .domain = AF_INET6, | |
| 169 | + .type = SOCK_DGRAM, | |
| 170 | + | |
| 171 | + .optname = SO_BINDTOIFINDEX, | |
| 172 | + .optval = 1, | |
| 173 | + }, | |
| 174 | + { | |
| 175 | + .descr = "block AF_INET, SOCK_DGRAM, IPPROTO_ICMP socket", | |
| 176 | + .insns = { | |
| 177 | + BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */ | |
| 178 | + | |
| 179 | + /* sock->family == AF_INET */ | |
| 180 | + BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, | |
| 181 | + offsetof(struct bpf_sock, family)), | |
| 182 | + BPF_JMP_IMM(BPF_JNE, BPF_REG_2, AF_INET, 5), | |
| 183 | + | |
| 184 | + /* sock->type == SOCK_DGRAM */ | |
| 185 | + BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, | |
| 186 | + offsetof(struct bpf_sock, type)), | |
| 187 | + BPF_JMP_IMM(BPF_JNE, BPF_REG_2, SOCK_DGRAM, 3), | |
| 188 | + | |
| 189 | + /* sock->protocol == IPPROTO_ICMP */ | |
| 190 | + BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, | |
| 191 | + offsetof(struct bpf_sock, protocol)), | |
| 192 | + BPF_JMP_IMM(BPF_JNE, BPF_REG_2, IPPROTO_ICMP, 1), | |
| 193 | + | |
| 194 | + /* return 0 (block) */ | |
| 195 | + BPF_MOV64_IMM(BPF_REG_0, 0), | |
| 196 | + BPF_EXIT_INSN(), | |
| 197 | + }, | |
| 198 | + .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 199 | + .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 200 | + | |
| 201 | + .domain = AF_INET, | |
| 202 | + .type = SOCK_DGRAM, | |
| 203 | + .protocol = IPPROTO_ICMP, | |
| 204 | + | |
| 205 | + .error = DENY_CREATE, | |
| 206 | + }, | |
| 207 | + { | |
| 208 | + .descr = "block AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6 socket", | |
| 209 | + .insns = { | |
| 210 | + BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */ | |
| 211 | + | |
| 212 | + /* sock->family == AF_INET6 */ | |
| 213 | + BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, | |
| 214 | + offsetof(struct bpf_sock, family)), | |
| 215 | + BPF_JMP_IMM(BPF_JNE, BPF_REG_2, AF_INET6, 5), | |
| 216 | + | |
| 217 | + /* sock->type == SOCK_DGRAM */ | |
| 218 | + BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, | |
| 219 | + offsetof(struct bpf_sock, type)), | |
| 220 | + BPF_JMP_IMM(BPF_JNE, BPF_REG_2, SOCK_DGRAM, 3), | |
| 221 | + | |
| 222 | + /* sock->protocol == IPPROTO_ICMPV6 */ | |
| 223 | + BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, | |
| 224 | + offsetof(struct bpf_sock, protocol)), | |
| 225 | + BPF_JMP_IMM(BPF_JNE, BPF_REG_2, IPPROTO_ICMPV6, 1), | |
| 226 | + | |
| 227 | + /* return 0 (block) */ | |
| 228 | + BPF_MOV64_IMM(BPF_REG_0, 0), | |
| 229 | + BPF_EXIT_INSN(), | |
| 230 | + }, | |
| 231 | + .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 232 | + .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 233 | + | |
| 234 | + .domain = AF_INET, | |
| 235 | + .type = SOCK_DGRAM, | |
| 236 | + .protocol = IPPROTO_ICMPV6, | |
| 237 | + | |
| 238 | + .error = DENY_CREATE, | |
| 239 | + }, | |
| 240 | + { | |
| 241 | + .descr = "load w/o expected_attach_type (compat mode)", | |
| 242 | + .insns = { | |
| 243 | + /* return 1 */ | |
| 244 | + BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 245 | + BPF_EXIT_INSN(), | |
| 246 | + }, | |
| 247 | + .expected_attach_type = 0, | |
| 248 | + .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 249 | + | |
| 250 | + .domain = AF_INET, | |
| 251 | + .type = SOCK_STREAM, | |
| 252 | + }, | |
| 253 | +}; | |
| 254 | + | |
| 255 | +static int load_prog(const struct bpf_insn *insns, | |
| 256 | + enum bpf_attach_type expected_attach_type) | |
| 257 | +{ | |
| 258 | + LIBBPF_OPTS(bpf_prog_load_opts, opts, | |
| 259 | + .expected_attach_type = expected_attach_type, | |
| 260 | + .log_level = 2, | |
| 261 | + .log_buf = bpf_log_buf, | |
| 262 | + .log_size = sizeof(bpf_log_buf), | |
| 263 | + ); | |
| 264 | + int fd, insns_cnt = 0; | |
| 265 | + | |
| 266 | + for (; | |
| 267 | + insns[insns_cnt].code != (BPF_JMP | BPF_EXIT); | |
| 268 | + insns_cnt++) { | |
| 269 | + } | |
| 270 | + insns_cnt++; | |
| 271 | + | |
| 272 | + fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", insns, | |
| 273 | + insns_cnt, &opts); | |
| 274 | + if (verbose && fd < 0) | |
| 275 | + fprintf(stderr, "%s\n", bpf_log_buf); | |
| 276 | + | |
| 277 | + return fd; | |
| 278 | +} | |
| 279 | + | |
| 280 | +static int run_test(int cgroup_fd, struct sock_create_test *test) | |
| 281 | +{ | |
| 282 | + int sock_fd, err, prog_fd, optval, ret = -1; | |
| 283 | + socklen_t optlen = sizeof(optval); | |
| 284 | + | |
| 285 | + prog_fd = load_prog(test->insns, test->expected_attach_type); | |
| 286 | + if (prog_fd < 0) { | |
| 287 | + log_err("Failed to load BPF program"); | |
| 288 | + return -1; | |
| 289 | + } | |
| 290 | + | |
| 291 | + err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0); | |
| 292 | + if (err < 0) { | |
| 293 | + log_err("Failed to attach BPF program"); | |
| 294 | + goto close_prog_fd; | |
| 295 | + } | |
| 296 | + | |
| 297 | + sock_fd = socket(test->domain, test->type, test->protocol); | |
| 298 | + if (sock_fd < 0) { | |
| 299 | + if (test->error == DENY_CREATE) | |
| 300 | + ret = 0; | |
| 301 | + else | |
| 302 | + log_err("Failed to create socket"); | |
| 303 | + | |
| 304 | + goto detach_prog; | |
| 305 | + } | |
| 306 | + | |
| 307 | + if (test->optname) { | |
| 308 | + err = getsockopt(sock_fd, SOL_SOCKET, test->optname, &optval, &optlen); | |
| 309 | + if (err) { | |
| 310 | + log_err("Failed to call getsockopt"); | |
| 311 | + goto cleanup; | |
| 312 | + } | |
| 313 | + | |
| 314 | + if (optval != test->optval) { | |
| 315 | + errno = 0; | |
| 316 | + log_err("getsockopt returned unexpected optval"); | |
| 317 | + goto cleanup; | |
| 318 | + } | |
| 319 | + } | |
| 320 | + | |
| 321 | + ret = test->error != OK; | |
| 322 | + | |
| 323 | +cleanup: | |
| 324 | + close(sock_fd); | |
| 325 | +detach_prog: | |
| 326 | + bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type); | |
| 327 | +close_prog_fd: | |
| 328 | + close(prog_fd); | |
| 329 | + return ret; | |
| 330 | +} | |
| 331 | + | |
| 332 | +void test_sock_create(void) | |
| 333 | +{ | |
| 334 | + int cgroup_fd, i; | |
| 335 | + | |
| 336 | + cgroup_fd = test__join_cgroup("/sock_create"); | |
| 337 | + if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup")) | |
| 338 | + return; | |
| 339 | + | |
| 340 | + for (i = 0; i < ARRAY_SIZE(tests); i++) { | |
| 341 | + if (!test__start_subtest(tests[i].descr)) | |
| 342 | + continue; | |
| 343 | + | |
| 344 | + ASSERT_OK(run_test(cgroup_fd, &tests[i]), tests[i].descr); | |
| 345 | + } | |
| 346 | + | |
| 347 | + close(cgroup_fd); | |
| 348 | +} | |
| @@ -1,132 +1,35 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | −// Copyright (c) 2018 Facebook | |
| 3 | − | |
| 4 | −#include <stdio.h> | |
| 5 | −#include <unistd.h> | |
| 6 | − | |
| 7 | −#include <arpa/inet.h> | |
| 8 | −#include <sys/types.h> | |
| 9 | −#include <sys/socket.h> | |
| 10 | − | |
| 11 | −#include <linux/filter.h> | |
| 12 | − | |
| 13 | −#include <bpf/bpf.h> | |
| 14 | − | |
| 2 | +#include <linux/bpf.h> | |
| 3 | +#include <test_progs.h> | |
| 15 | 4 | #include "cgroup_helpers.h" |
| 16 | −#include <bpf/bpf_endian.h> | |
| 17 | −#include "bpf_util.h" | |
| 18 | 5 | |
| 19 | −#define CG_PATH "/foo" | |
| 20 | −#define MAX_INSNS 512 | |
| 6 | +#define TEST_NS "sock_post_bind" | |
| 21 | 7 | |
| 22 | −char bpf_log_buf[BPF_LOG_BUF_SIZE]; | |
| 23 | −static bool verbose = false; | |
| 8 | +static char bpf_log_buf[4096]; | |
| 24 | 9 | |
| 25 | −struct sock_test { | |
| 26 | − const char *descr; | |
| 10 | +static struct sock_post_bind_test { | |
| 11 | + const char *descr; | |
| 27 | 12 | /* BPF prog properties */ |
| 28 | − struct bpf_insn insns[MAX_INSNS]; | |
| 29 | − enum bpf_attach_type expected_attach_type; | |
| 30 | − enum bpf_attach_type attach_type; | |
| 13 | + const struct bpf_insn insns[64]; | |
| 14 | + enum bpf_attach_type attach_type; | |
| 15 | + enum bpf_attach_type expected_attach_type; | |
| 31 | 16 | /* Socket properties */ |
| 32 | − int domain; | |
| 33 | − int type; | |
| 17 | + int domain; | |
| 18 | + int type; | |
| 34 | 19 | /* Endpoint to bind() to */ |
| 35 | 20 | const char *ip; |
| 36 | 21 | unsigned short port; |
| 37 | 22 | unsigned short port_retry; |
| 23 | + | |
| 38 | 24 | /* Expected test result */ |
| 39 | 25 | enum { |
| 40 | − LOAD_REJECT, | |
| 41 | 26 | ATTACH_REJECT, |
| 42 | 27 | BIND_REJECT, |
| 43 | 28 | SUCCESS, |
| 44 | 29 | RETRY_SUCCESS, |
| 45 | 30 | RETRY_REJECT |
| 46 | 31 | } result; |
| 47 | −}; | |
| 48 | − | |
| 49 | −static struct sock_test tests[] = { | |
| 50 | − { | |
| 51 | − .descr = "bind4 load with invalid access: src_ip6", | |
| 52 | − .insns = { | |
| 53 | − BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
| 54 | − BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, | |
| 55 | − offsetof(struct bpf_sock, src_ip6[0])), | |
| 56 | − BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 57 | − BPF_EXIT_INSN(), | |
| 58 | − }, | |
| 59 | − .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, | |
| 60 | − .attach_type = BPF_CGROUP_INET4_POST_BIND, | |
| 61 | − .result = LOAD_REJECT, | |
| 62 | − }, | |
| 63 | − { | |
| 64 | − .descr = "bind4 load with invalid access: mark", | |
| 65 | − .insns = { | |
| 66 | − BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
| 67 | − BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, | |
| 68 | − offsetof(struct bpf_sock, mark)), | |
| 69 | − BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 70 | − BPF_EXIT_INSN(), | |
| 71 | − }, | |
| 72 | − .expected_attach_type = BPF_CGROUP_INET4_POST_BIND, | |
| 73 | − .attach_type = BPF_CGROUP_INET4_POST_BIND, | |
| 74 | − .result = LOAD_REJECT, | |
| 75 | − }, | |
| 76 | − { | |
| 77 | − .descr = "bind6 load with invalid access: src_ip4", | |
| 78 | − .insns = { | |
| 79 | − BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
| 80 | − BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, | |
| 81 | − offsetof(struct bpf_sock, src_ip4)), | |
| 82 | − BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 83 | − BPF_EXIT_INSN(), | |
| 84 | − }, | |
| 85 | − .expected_attach_type = BPF_CGROUP_INET6_POST_BIND, | |
| 86 | − .attach_type = BPF_CGROUP_INET6_POST_BIND, | |
| 87 | − .result = LOAD_REJECT, | |
| 88 | − }, | |
| 89 | − { | |
| 90 | − .descr = "sock_create load with invalid access: src_port", | |
| 91 | − .insns = { | |
| 92 | − BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), | |
| 93 | − BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, | |
| 94 | − offsetof(struct bpf_sock, src_port)), | |
| 95 | − BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 96 | − BPF_EXIT_INSN(), | |
| 97 | − }, | |
| 98 | − .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 99 | − .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 100 | − .result = LOAD_REJECT, | |
| 101 | − }, | |
| 102 | − { | |
| 103 | − .descr = "sock_create load w/o expected_attach_type (compat mode)", | |
| 104 | − .insns = { | |
| 105 | − BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 106 | − BPF_EXIT_INSN(), | |
| 107 | − }, | |
| 108 | − .expected_attach_type = 0, | |
| 109 | − .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 110 | − .domain = AF_INET, | |
| 111 | − .type = SOCK_STREAM, | |
| 112 | − .ip = "127.0.0.1", | |
| 113 | − .port = 8097, | |
| 114 | − .result = SUCCESS, | |
| 115 | − }, | |
| 116 | − { | |
| 117 | − .descr = "sock_create load w/ expected_attach_type", | |
| 118 | − .insns = { | |
| 119 | − BPF_MOV64_IMM(BPF_REG_0, 1), | |
| 120 | − BPF_EXIT_INSN(), | |
| 121 | − }, | |
| 122 | − .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 123 | − .attach_type = BPF_CGROUP_INET_SOCK_CREATE, | |
| 124 | − .domain = AF_INET, | |
| 125 | − .type = SOCK_STREAM, | |
| 126 | − .ip = "127.0.0.1", | |
| 127 | − .port = 8097, | |
| 128 | − .result = SUCCESS, | |
| 129 | − }, | |
| 32 | +} tests[] = { | |
| 130 | 33 | { |
| 131 | 34 | .descr = "attach type mismatch bind4 vs bind6", |
| 132 | 35 | .insns = { |
| @@ -374,40 +277,29 @@ static struct sock_test tests[] = { · 241 unmodified lines | ||
| 374 | 277 | }, |
| 375 | 278 | }; |
| 376 | 279 | |
| 377 | −static size_t probe_prog_length(const struct bpf_insn *fp) | |
| 378 | −{ | |
| 379 | − size_t len; | |
| 380 | − | |
| 381 | − for (len = MAX_INSNS - 1; len > 0; --len) | |
| 382 | − if (fp[len].code != 0 || fp[len].imm != 0) | |
| 383 | − break; | |
| 384 | − return len + 1; | |
| 385 | −} | |
| 386 | − | |
| 387 | −static int load_sock_prog(const struct bpf_insn *prog, | |
| 388 | − enum bpf_attach_type attach_type) | |
| 280 | +static int load_prog(const struct bpf_insn *insns, | |
| 281 | + enum bpf_attach_type expected_attach_type) | |
| 389 | 282 | { |
| 390 | − LIBBPF_OPTS(bpf_prog_load_opts, opts); | |
| 391 | − int ret, insn_cnt; | |
| 392 | − | |
| 393 | − insn_cnt = probe_prog_length(prog); | |
| 394 | − | |
| 395 | − opts.expected_attach_type = attach_type; | |
| 396 | − opts.log_buf = bpf_log_buf; | |
| 397 | − opts.log_size = BPF_LOG_BUF_SIZE; | |
| 398 | − opts.log_level = 2; | |
| 283 | + LIBBPF_OPTS(bpf_prog_load_opts, opts, | |
| 284 | + .expected_attach_type = expected_attach_type, | |
| 285 | + .log_level = 2, | |
| 286 | + .log_buf = bpf_log_buf, | |
| 287 | + .log_size = sizeof(bpf_log_buf), | |
| 288 | + ); | |
| 289 | + int fd, insns_cnt = 0; | |
| 290 | + | |
| 291 | + for (; | |
| 292 | + insns[insns_cnt].code != (BPF_JMP | BPF_EXIT); | |
| 293 | + insns_cnt++) { | |
| 294 | + } | |
| 295 | + insns_cnt++; | |
| 399 | 296 | |
| 400 | − ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", prog, insn_cnt, &opts); | |
| 401 | − if (verbose && ret < 0) | |
| 297 | + fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", insns, | |
| 298 | + insns_cnt, &opts); | |
| 299 | + if (fd < 0) | |
| 402 | 300 | fprintf(stderr, "%s\n", bpf_log_buf); |
| 403 | 301 | |
| 404 | − return ret; | |
| 405 | −} | |
| 406 | − | |
| 407 | −static int attach_sock_prog(int cgfd, int progfd, | |
| 408 | − enum bpf_attach_type attach_type) | |
| 409 | −{ | |
| 410 | − return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE); | |
| 302 | + return fd; | |
| 411 | 303 | } |
| 412 | 304 | |
| 413 | 305 | static int bind_sock(int domain, int type, const char *ip, |
| @@ -477,22 +369,16 @@ out: · 63 unmodified lines | ||
| 477 | 369 | return res; |
| 478 | 370 | } |
| 479 | 371 | |
| 480 | −static int run_test_case(int cgfd, const struct sock_test *test) | |
| 372 | +static int run_test(int cgroup_fd, struct sock_post_bind_test *test) | |
| 481 | 373 | { |
| 482 | − int progfd = -1; | |
| 483 | − int err = 0; | |
| 484 | − int res; | |
| 485 | − | |
| 486 | − printf("Test case: %s .. ", test->descr); | |
| 487 | − progfd = load_sock_prog(test->insns, test->expected_attach_type); | |
| 488 | − if (progfd < 0) { | |
| 489 | − if (test->result == LOAD_REJECT) | |
| 490 | − goto out; | |
| 491 | − else | |
| 492 | − goto err; | |
| 493 | − } | |
| 374 | + int err, prog_fd, res, ret = 0; | |
| 494 | 375 | |
| 495 | − if (attach_sock_prog(cgfd, progfd, test->attach_type) < 0) { | |
| 376 | + prog_fd = load_prog(test->insns, test->expected_attach_type); | |
| 377 | + if (prog_fd < 0) | |
| 378 | + goto err; | |
| 379 | + | |
| 380 | + err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0); | |
| 381 | + if (err < 0) { | |
| 496 | 382 | if (test->result == ATTACH_REJECT) |
| 497 | 383 | goto out; |
| 498 | 384 | else |
| @@ -503,54 +389,38 @@ static int run_test_case(int cgfd, const struct sock_test *test) · 4 unmodified lines | ||
| 503 | 389 | test->port_retry); |
| 504 | 390 | if (res > 0 && test->result == res) |
| 505 | 391 | goto out; |
| 506 | − | |
| 507 | 392 | err: |
| 508 | − err = -1; | |
| 393 | + ret = -1; | |
| 509 | 394 | out: |
| 510 | 395 | /* Detaching w/o checking return code: best effort attempt. */ |
| 511 | − if (progfd != -1) | |
| 512 | − bpf_prog_detach(cgfd, test->attach_type); | |
| 513 | − close(progfd); | |
| 514 | − printf("[%s]\n", err ? "FAIL" : "PASS"); | |
| 515 | − return err; | |
| 396 | + if (prog_fd != -1) | |
| 397 | + bpf_prog_detach(cgroup_fd, test->attach_type); | |
| 398 | + close(prog_fd); | |
| 399 | + return ret; | |
| 516 | 400 | } |
| 517 | 401 | |
| 518 | −static int run_tests(int cgfd) | |
| 402 | +void test_sock_post_bind(void) | |
| 519 | 403 | { |
| 520 | − int passes = 0; | |
| 521 | − int fails = 0; | |
| 404 | + struct netns_obj *ns; | |
| 405 | + int cgroup_fd; | |
| 522 | 406 | int i; |
| 523 | 407 | |
| 524 | − for (i = 0; i < ARRAY_SIZE(tests); ++i) { | |
| 525 | − if (run_test_case(cgfd, &tests[i])) | |
| 526 | − ++fails; | |
| 527 | − else | |
| 528 | − ++passes; | |
| 529 | − } | |
| 530 | − printf("Summary: %d PASSED, %d FAILED\n", passes, fails); | |
| 531 | − return fails ? -1 : 0; | |
| 532 | −} | |
| 533 | − | |
| 534 | −int main(int argc, char **argv) | |
| 535 | −{ | |
| 536 | − int cgfd = -1; | |
| 537 | − int err = 0; | |
| 408 | + cgroup_fd = test__join_cgroup("/post_bind"); | |
| 409 | + if (!ASSERT_OK_FD(cgroup_fd, "join_cgroup")) | |
| 410 | + return; | |
| 538 | 411 | |
| 539 | − cgfd = cgroup_setup_and_join(CG_PATH); | |
| 540 | − if (cgfd < 0) | |
| 541 | − goto err; | |
| 412 | + ns = netns_new(TEST_NS, true); | |
| 413 | + if (!ASSERT_OK_PTR(ns, "netns_new")) | |
| 414 | + goto cleanup; | |
| 542 | 415 | |
| 543 | − /* Use libbpf 1.0 API mode */ | |
| 544 | − libbpf_set_strict_mode(LIBBPF_STRICT_ALL); | |
| 416 | + for (i = 0; i < ARRAY_SIZE(tests); i++) { | |
| 417 | + if (!test__start_subtest(tests[i].descr)) | |
| 418 | + continue; | |
| 545 | 419 | |
| 546 | − if (run_tests(cgfd)) | |
| 547 | − goto err; | |
| 420 | + ASSERT_OK(run_test(cgroup_fd, &tests[i]), tests[i].descr); | |
| 421 | + } | |
| 548 | 422 | |
| 549 | − goto out; | |
| 550 | −err: | |
| 551 | − err = -1; | |
| 552 | −out: | |
| 553 | − close(cgfd); | |
| 554 | − cleanup_cgroup_environment(); | |
| 555 | − return err; | |
| 423 | +cleanup: | |
| 424 | + netns_free(ns); | |
| 425 | + close(cgroup_fd); | |
| 556 | 426 | } |
| @@ -0,0 +1,106 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +#include <test_progs.h> | |
| 4 | +#include "struct_ops_private_stack.skel.h" | |
| 5 | +#include "struct_ops_private_stack_fail.skel.h" | |
| 6 | +#include "struct_ops_private_stack_recur.skel.h" | |
| 7 | + | |
| 8 | +static void test_private_stack(void) | |
| 9 | +{ | |
| 10 | + struct struct_ops_private_stack *skel; | |
| 11 | + struct bpf_link *link; | |
| 12 | + int err; | |
| 13 | + | |
| 14 | + skel = struct_ops_private_stack__open(); | |
| 15 | + if (!ASSERT_OK_PTR(skel, "struct_ops_private_stack__open")) | |
| 16 | + return; | |
| 17 | + | |
| 18 | + if (skel->data->skip) { | |
| 19 | + test__skip(); | |
| 20 | + goto cleanup; | |
| 21 | + } | |
| 22 | + | |
| 23 | + err = struct_ops_private_stack__load(skel); | |
| 24 | + if (!ASSERT_OK(err, "struct_ops_private_stack__load")) | |
| 25 | + goto cleanup; | |
| 26 | + | |
| 27 | + link = bpf_map__attach_struct_ops(skel->maps.testmod_1); | |
| 28 | + if (!ASSERT_OK_PTR(link, "attach_struct_ops")) | |
| 29 | + goto cleanup; | |
| 30 | + | |
| 31 | + ASSERT_OK(trigger_module_test_read(256), "trigger_read"); | |
| 32 | + | |
| 33 | + ASSERT_EQ(skel->bss->val_i, 3, "val_i"); | |
| 34 | + ASSERT_EQ(skel->bss->val_j, 8, "val_j"); | |
| 35 | + | |
| 36 | + bpf_link__destroy(link); | |
| 37 | + | |
| 38 | +cleanup: | |
| 39 | + struct_ops_private_stack__destroy(skel); | |
| 40 | +} | |
| 41 | + | |
| 42 | +static void test_private_stack_fail(void) | |
| 43 | +{ | |
| 44 | + struct struct_ops_private_stack_fail *skel; | |
| 45 | + int err; | |
| 46 | + | |
| 47 | + skel = struct_ops_private_stack_fail__open(); | |
| 48 | + if (!ASSERT_OK_PTR(skel, "struct_ops_private_stack_fail__open")) | |
| 49 | + return; | |
| 50 | + | |
| 51 | + if (skel->data->skip) { | |
| 52 | + test__skip(); | |
| 53 | + goto cleanup; | |
| 54 | + } | |
| 55 | + | |
| 56 | + err = struct_ops_private_stack_fail__load(skel); | |
| 57 | + if (!ASSERT_ERR(err, "struct_ops_private_stack_fail__load")) | |
| 58 | + goto cleanup; | |
| 59 | + return; | |
| 60 | + | |
| 61 | +cleanup: | |
| 62 | + struct_ops_private_stack_fail__destroy(skel); | |
| 63 | +} | |
| 64 | + | |
| 65 | +static void test_private_stack_recur(void) | |
| 66 | +{ | |
| 67 | + struct struct_ops_private_stack_recur *skel; | |
| 68 | + struct bpf_link *link; | |
| 69 | + int err; | |
| 70 | + | |
| 71 | + skel = struct_ops_private_stack_recur__open(); | |
| 72 | + if (!ASSERT_OK_PTR(skel, "struct_ops_private_stack_recur__open")) | |
| 73 | + return; | |
| 74 | + | |
| 75 | + if (skel->data->skip) { | |
| 76 | + test__skip(); | |
| 77 | + goto cleanup; | |
| 78 | + } | |
| 79 | + | |
| 80 | + err = struct_ops_private_stack_recur__load(skel); | |
| 81 | + if (!ASSERT_OK(err, "struct_ops_private_stack_recur__load")) | |
| 82 | + goto cleanup; | |
| 83 | + | |
| 84 | + link = bpf_map__attach_struct_ops(skel->maps.testmod_1); | |
| 85 | + if (!ASSERT_OK_PTR(link, "attach_struct_ops")) | |
| 86 | + goto cleanup; | |
| 87 | + | |
| 88 | + ASSERT_OK(trigger_module_test_read(256), "trigger_read"); | |
| 89 | + | |
| 90 | + ASSERT_EQ(skel->bss->val_j, 3, "val_j"); | |
| 91 | + | |
| 92 | + bpf_link__destroy(link); | |
| 93 | + | |
| 94 | +cleanup: | |
| 95 | + struct_ops_private_stack_recur__destroy(skel); | |
| 96 | +} | |
| 97 | + | |
| 98 | +void test_struct_ops_private_stack(void) | |
| 99 | +{ | |
| 100 | + if (test__start_subtest("private_stack")) | |
| 101 | + test_private_stack(); | |
| 102 | + if (test__start_subtest("private_stack_fail")) | |
| 103 | + test_private_stack_fail(); | |
| 104 | + if (test__start_subtest("private_stack_recur")) | |
| 105 | + test_private_stack_recur(); | |
| 106 | +} | |
| @@ -46,7 +46,8 @@ static int subskeleton_lib_subresult(struct bpf_object *obj) · 45 unmodified lines | ||
| 46 | 46 | return result; |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | −void test_subskeleton(void) | |
| 49 | +/* initialize and load through skeleton, then instantiate subskeleton out of it */ | |
| 50 | +static void subtest_skel_subskeleton(void) | |
| 50 | 51 | { |
| 51 | 52 | int err, result; |
| 52 | 53 | struct test_subskeleton *skel; |
| @@ -76,3 +77,76 @@ void test_subskeleton(void) · 23 unmodified lines | ||
| 76 | 77 | cleanup: |
| 77 | 78 | test_subskeleton__destroy(skel); |
| 78 | 79 | } |
| 80 | + | |
| 81 | +/* initialize and load through generic bpf_object API, then instantiate subskeleton out of it */ | |
| 82 | +static void subtest_obj_subskeleton(void) | |
| 83 | +{ | |
| 84 | + int err, result; | |
| 85 | + const void *elf_bytes; | |
| 86 | + size_t elf_bytes_sz = 0, rodata_sz = 0, bss_sz = 0; | |
| 87 | + struct bpf_object *obj; | |
| 88 | + const struct bpf_map *map; | |
| 89 | + const struct bpf_program *prog; | |
| 90 | + struct bpf_link *link = NULL; | |
| 91 | + struct test_subskeleton__rodata *rodata; | |
| 92 | + struct test_subskeleton__bss *bss; | |
| 93 | + | |
| 94 | + elf_bytes = test_subskeleton__elf_bytes(&elf_bytes_sz); | |
| 95 | + if (!ASSERT_OK_PTR(elf_bytes, "elf_bytes")) | |
| 96 | + return; | |
| 97 | + | |
| 98 | + obj = bpf_object__open_mem(elf_bytes, elf_bytes_sz, NULL); | |
| 99 | + if (!ASSERT_OK_PTR(obj, "obj_open_mem")) | |
| 100 | + return; | |
| 101 | + | |
| 102 | + map = bpf_object__find_map_by_name(obj, ".rodata"); | |
| 103 | + if (!ASSERT_OK_PTR(map, "rodata_map_by_name")) | |
| 104 | + goto cleanup; | |
| 105 | + | |
| 106 | + rodata = bpf_map__initial_value(map, &rodata_sz); | |
| 107 | + if (!ASSERT_OK_PTR(rodata, "rodata_get")) | |
| 108 | + goto cleanup; | |
| 109 | + | |
| 110 | + rodata->rovar1 = 10; | |
| 111 | + rodata->var1 = 1; | |
| 112 | + subskeleton_lib_setup(obj); | |
| 113 | + | |
| 114 | + err = bpf_object__load(obj); | |
| 115 | + if (!ASSERT_OK(err, "obj_load")) | |
| 116 | + goto cleanup; | |
| 117 | + | |
| 118 | + prog = bpf_object__find_program_by_name(obj, "handler1"); | |
| 119 | + if (!ASSERT_OK_PTR(prog, "prog_by_name")) | |
| 120 | + goto cleanup; | |
| 121 | + | |
| 122 | + link = bpf_program__attach(prog); | |
| 123 | + if (!ASSERT_OK_PTR(link, "prog_attach")) | |
| 124 | + goto cleanup; | |
| 125 | + | |
| 126 | + /* trigger tracepoint */ | |
| 127 | + usleep(1); | |
| 128 | + | |
| 129 | + map = bpf_object__find_map_by_name(obj, ".bss"); | |
| 130 | + if (!ASSERT_OK_PTR(map, "bss_map_by_name")) | |
| 131 | + goto cleanup; | |
| 132 | + | |
| 133 | + bss = bpf_map__initial_value(map, &bss_sz); | |
| 134 | + if (!ASSERT_OK_PTR(rodata, "rodata_get")) | |
| 135 | + goto cleanup; | |
| 136 | + | |
| 137 | + result = subskeleton_lib_subresult(obj) * 10; | |
| 138 | + ASSERT_EQ(bss->out1, result, "out1"); | |
| 139 | + | |
| 140 | +cleanup: | |
| 141 | + bpf_link__destroy(link); | |
| 142 | + bpf_object__close(obj); | |
| 143 | +} | |
| 144 | + | |
| 145 | + | |
| 146 | +void test_subskeleton(void) | |
| 147 | +{ | |
| 148 | + if (test__start_subtest("skel_subskel")) | |
| 149 | + subtest_skel_subskeleton(); | |
| 150 | + if (test__start_subtest("obj_subskel")) | |
| 151 | + subtest_obj_subskeleton(); | |
| 152 | +} | |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | ||
| 7 | 7 | #include "tailcall_bpf2bpf_hierarchy3.skel.h" |
| 8 | 8 | #include "tailcall_freplace.skel.h" |
| 9 | 9 | #include "tc_bpf2bpf.skel.h" |
| 10 | +#include "tailcall_fail.skel.h" | |
| 10 | 11 | |
| 11 | 12 | /* test_tailcall_1 checks basic functionality by patching multiple locations |
| 12 | 13 | * in a single program for a single tail call slot with nop->jmp, jmp->nop |
| @@ -1496,8 +1497,8 @@ static void test_tailcall_bpf2bpf_hierarchy_3(void) · 1483 unmodified lines | ||
| 1496 | 1497 | RUN_TESTS(tailcall_bpf2bpf_hierarchy3); |
| 1497 | 1498 | } |
| 1498 | 1499 | |
| 1499 | −/* test_tailcall_freplace checks that the attached freplace prog is OK to | |
| 1500 | − * update the prog_array map. | |
| 1500 | +/* test_tailcall_freplace checks that the freplace prog fails to update the | |
| 1501 | + * prog_array map, no matter whether the freplace prog attaches to its target. | |
| 1501 | 1502 | */ |
| 1502 | 1503 | static void test_tailcall_freplace(void) |
| 1503 | 1504 | { |
| @@ -1505,7 +1506,7 @@ static void test_tailcall_freplace(void) · 1 unmodified line | ||
| 1505 | 1506 | struct bpf_link *freplace_link = NULL; |
| 1506 | 1507 | struct bpf_program *freplace_prog; |
| 1507 | 1508 | struct tc_bpf2bpf *tc_skel = NULL; |
| 1508 | − int prog_fd, map_fd; | |
| 1509 | + int prog_fd, tc_prog_fd, map_fd; | |
| 1509 | 1510 | char buff[128] = {}; |
| 1510 | 1511 | int err, key; |
| 1511 | 1512 | |
| @@ -1523,9 +1524,10 @@ static void test_tailcall_freplace(void) · 11 unmodified lines | ||
| 1523 | 1524 | if (!ASSERT_OK_PTR(tc_skel, "tc_bpf2bpf__open_and_load")) |
| 1524 | 1525 | goto out; |
| 1525 | 1526 | |
| 1526 | − prog_fd = bpf_program__fd(tc_skel->progs.entry_tc); | |
| 1527 | + tc_prog_fd = bpf_program__fd(tc_skel->progs.entry_tc); | |
| 1527 | 1528 | freplace_prog = freplace_skel->progs.entry_freplace; |
| 1528 | − err = bpf_program__set_attach_target(freplace_prog, prog_fd, "subprog"); | |
| 1529 | + err = bpf_program__set_attach_target(freplace_prog, tc_prog_fd, | |
| 1530 | + "subprog_tc"); | |
| 1529 | 1531 | if (!ASSERT_OK(err, "set_attach_target")) |
| 1530 | 1532 | goto out; |
| 1531 | 1533 | |
| @@ -1533,27 +1535,121 @@ static void test_tailcall_freplace(void) · 1 unmodified line | ||
| 1533 | 1535 | if (!ASSERT_OK(err, "tailcall_freplace__load")) |
| 1534 | 1536 | goto out; |
| 1535 | 1537 | |
| 1536 | − freplace_link = bpf_program__attach_freplace(freplace_prog, prog_fd, | |
| 1537 | − "subprog"); | |
| 1538 | + map_fd = bpf_map__fd(freplace_skel->maps.jmp_table); | |
| 1539 | + prog_fd = bpf_program__fd(freplace_prog); | |
| 1540 | + key = 0; | |
| 1541 | + err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY); | |
| 1542 | + ASSERT_ERR(err, "update jmp_table failure"); | |
| 1543 | + | |
| 1544 | + freplace_link = bpf_program__attach_freplace(freplace_prog, tc_prog_fd, | |
| 1545 | + "subprog_tc"); | |
| 1538 | 1546 | if (!ASSERT_OK_PTR(freplace_link, "attach_freplace")) |
| 1539 | 1547 | goto out; |
| 1540 | 1548 | |
| 1541 | − map_fd = bpf_map__fd(freplace_skel->maps.jmp_table); | |
| 1542 | − prog_fd = bpf_program__fd(freplace_prog); | |
| 1549 | + err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY); | |
| 1550 | + ASSERT_ERR(err, "update jmp_table failure"); | |
| 1551 | + | |
| 1552 | +out: | |
| 1553 | + bpf_link__destroy(freplace_link); | |
| 1554 | + tailcall_freplace__destroy(freplace_skel); | |
| 1555 | + tc_bpf2bpf__destroy(tc_skel); | |
| 1556 | +} | |
| 1557 | + | |
| 1558 | +/* test_tailcall_bpf2bpf_freplace checks the failure that fails to attach a tail | |
| 1559 | + * callee prog with freplace prog or fails to update an extended prog to | |
| 1560 | + * prog_array map. | |
| 1561 | + */ | |
| 1562 | +static void test_tailcall_bpf2bpf_freplace(void) | |
| 1563 | +{ | |
| 1564 | + struct tailcall_freplace *freplace_skel = NULL; | |
| 1565 | + struct bpf_link *freplace_link = NULL; | |
| 1566 | + struct tc_bpf2bpf *tc_skel = NULL; | |
| 1567 | + char buff[128] = {}; | |
| 1568 | + int prog_fd, map_fd; | |
| 1569 | + int err, key; | |
| 1570 | + | |
| 1571 | + LIBBPF_OPTS(bpf_test_run_opts, topts, | |
| 1572 | + .data_in = buff, | |
| 1573 | + .data_size_in = sizeof(buff), | |
| 1574 | + .repeat = 1, | |
| 1575 | + ); | |
| 1576 | + | |
| 1577 | + tc_skel = tc_bpf2bpf__open_and_load(); | |
| 1578 | + if (!ASSERT_OK_PTR(tc_skel, "tc_bpf2bpf__open_and_load")) | |
| 1579 | + goto out; | |
| 1580 | + | |
| 1581 | + prog_fd = bpf_program__fd(tc_skel->progs.entry_tc); | |
| 1582 | + freplace_skel = tailcall_freplace__open(); | |
| 1583 | + if (!ASSERT_OK_PTR(freplace_skel, "tailcall_freplace__open")) | |
| 1584 | + goto out; | |
| 1585 | + | |
| 1586 | + err = bpf_program__set_attach_target(freplace_skel->progs.entry_freplace, | |
| 1587 | + prog_fd, "subprog_tc"); | |
| 1588 | + if (!ASSERT_OK(err, "set_attach_target")) | |
| 1589 | + goto out; | |
| 1590 | + | |
| 1591 | + err = tailcall_freplace__load(freplace_skel); | |
| 1592 | + if (!ASSERT_OK(err, "tailcall_freplace__load")) | |
| 1593 | + goto out; | |
| 1594 | + | |
| 1595 | + /* OK to attach then detach freplace prog. */ | |
| 1596 | + | |
| 1597 | + freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace, | |
| 1598 | + prog_fd, "subprog_tc"); | |
| 1599 | + if (!ASSERT_OK_PTR(freplace_link, "attach_freplace")) | |
| 1600 | + goto out; | |
| 1601 | + | |
| 1602 | + err = bpf_link__destroy(freplace_link); | |
| 1603 | + if (!ASSERT_OK(err, "destroy link")) | |
| 1604 | + goto out; | |
| 1605 | + | |
| 1606 | + /* OK to update prog_array map then delete element from the map. */ | |
| 1607 | + | |
| 1543 | 1608 | key = 0; |
| 1609 | + map_fd = bpf_map__fd(freplace_skel->maps.jmp_table); | |
| 1544 | 1610 | err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY); |
| 1545 | 1611 | if (!ASSERT_OK(err, "update jmp_table")) |
| 1546 | 1612 | goto out; |
| 1547 | 1613 | |
| 1548 | − prog_fd = bpf_program__fd(tc_skel->progs.entry_tc); | |
| 1549 | − err = bpf_prog_test_run_opts(prog_fd, &topts); | |
| 1550 | − ASSERT_OK(err, "test_run"); | |
| 1551 | − ASSERT_EQ(topts.retval, 34, "test_run retval"); | |
| 1614 | + err = bpf_map_delete_elem(map_fd, &key); | |
| 1615 | + if (!ASSERT_OK(err, "delete_elem from jmp_table")) | |
| 1616 | + goto out; | |
| 1617 | + | |
| 1618 | + /* Fail to attach a tail callee prog with freplace prog. */ | |
| 1619 | + | |
| 1620 | + err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY); | |
| 1621 | + if (!ASSERT_OK(err, "update jmp_table")) | |
| 1622 | + goto out; | |
| 1623 | + | |
| 1624 | + freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace, | |
| 1625 | + prog_fd, "subprog_tc"); | |
| 1626 | + if (!ASSERT_ERR_PTR(freplace_link, "attach_freplace failure")) | |
| 1627 | + goto out; | |
| 1628 | + | |
| 1629 | + err = bpf_map_delete_elem(map_fd, &key); | |
| 1630 | + if (!ASSERT_OK(err, "delete_elem from jmp_table")) | |
| 1631 | + goto out; | |
| 1632 | + | |
| 1633 | + /* Fail to update an extended prog to prog_array map. */ | |
| 1634 | + | |
| 1635 | + freplace_link = bpf_program__attach_freplace(freplace_skel->progs.entry_freplace, | |
| 1636 | + prog_fd, "subprog_tc"); | |
| 1637 | + if (!ASSERT_OK_PTR(freplace_link, "attach_freplace")) | |
| 1638 | + goto out; | |
| 1639 | + | |
| 1640 | + err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY); | |
| 1641 | + if (!ASSERT_ERR(err, "update jmp_table failure")) | |
| 1642 | + goto out; | |
| 1552 | 1643 | |
| 1553 | 1644 | out: |
| 1554 | 1645 | bpf_link__destroy(freplace_link); |
| 1555 | − tc_bpf2bpf__destroy(tc_skel); | |
| 1556 | 1646 | tailcall_freplace__destroy(freplace_skel); |
| 1647 | + tc_bpf2bpf__destroy(tc_skel); | |
| 1648 | +} | |
| 1649 | + | |
| 1650 | +static void test_tailcall_failure() | |
| 1651 | +{ | |
| 1652 | + RUN_TESTS(tailcall_fail); | |
| 1557 | 1653 | } |
| 1558 | 1654 | |
| 1559 | 1655 | void test_tailcalls(void) |
| @@ -1606,4 +1702,8 @@ void test_tailcalls(void) · 46 unmodified lines | ||
| 1606 | 1702 | test_tailcall_bpf2bpf_hierarchy_3(); |
| 1607 | 1703 | if (test__start_subtest("tailcall_freplace")) |
| 1608 | 1704 | test_tailcall_freplace(); |
| 1705 | + if (test__start_subtest("tailcall_bpf2bpf_freplace")) | |
| 1706 | + test_tailcall_bpf2bpf_freplace(); | |
| 1707 | + if (test__start_subtest("tailcall_failure")) | |
| 1708 | + test_tailcall_failure(); | |
| 1609 | 1709 | } |
| @@ -68,6 +68,74 @@ cleanup: · 67 unmodified lines | ||
| 68 | 68 | task_kfunc_success__destroy(skel); |
| 69 | 69 | } |
| 70 | 70 | |
| 71 | +static int run_vpid_test(void *prog_name) | |
| 72 | +{ | |
| 73 | + struct task_kfunc_success *skel; | |
| 74 | + struct bpf_program *prog; | |
| 75 | + int prog_fd, err = 0; | |
| 76 | + | |
| 77 | + if (getpid() != 1) | |
| 78 | + return 1; | |
| 79 | + | |
| 80 | + skel = open_load_task_kfunc_skel(); | |
| 81 | + if (!skel) | |
| 82 | + return 2; | |
| 83 | + | |
| 84 | + if (skel->bss->err) { | |
| 85 | + err = 3; | |
| 86 | + goto cleanup; | |
| 87 | + } | |
| 88 | + | |
| 89 | + prog = bpf_object__find_program_by_name(skel->obj, prog_name); | |
| 90 | + if (!prog) { | |
| 91 | + err = 4; | |
| 92 | + goto cleanup; | |
| 93 | + } | |
| 94 | + | |
| 95 | + prog_fd = bpf_program__fd(prog); | |
| 96 | + if (prog_fd < 0) { | |
| 97 | + err = 5; | |
| 98 | + goto cleanup; | |
| 99 | + } | |
| 100 | + | |
| 101 | + if (bpf_prog_test_run_opts(prog_fd, NULL)) { | |
| 102 | + err = 6; | |
| 103 | + goto cleanup; | |
| 104 | + } | |
| 105 | + | |
| 106 | + if (skel->bss->err) | |
| 107 | + err = 7 + skel->bss->err; | |
| 108 | +cleanup: | |
| 109 | + task_kfunc_success__destroy(skel); | |
| 110 | + return err; | |
| 111 | +} | |
| 112 | + | |
| 113 | +static void run_vpid_success_test(const char *prog_name) | |
| 114 | +{ | |
| 115 | + const int stack_size = 1024 * 1024; | |
| 116 | + int child_pid, wstatus; | |
| 117 | + char *stack; | |
| 118 | + | |
| 119 | + stack = (char *)malloc(stack_size); | |
| 120 | + if (!ASSERT_OK_PTR(stack, "clone_stack")) | |
| 121 | + return; | |
| 122 | + | |
| 123 | + child_pid = clone(run_vpid_test, stack + stack_size, | |
| 124 | + CLONE_NEWPID | SIGCHLD, (void *)prog_name); | |
| 125 | + if (!ASSERT_GT(child_pid, -1, "child_pid")) | |
| 126 | + goto cleanup; | |
| 127 | + | |
| 128 | + if (!ASSERT_GT(waitpid(child_pid, &wstatus, 0), -1, "waitpid")) | |
| 129 | + goto cleanup; | |
| 130 | + | |
| 131 | + if (WEXITSTATUS(wstatus) > 7) | |
| 132 | + ASSERT_OK(WEXITSTATUS(wstatus) - 7, "vpid_test_failure"); | |
| 133 | + else | |
| 134 | + ASSERT_OK(WEXITSTATUS(wstatus), "run_vpid_test_err"); | |
| 135 | +cleanup: | |
| 136 | + free(stack); | |
| 137 | +} | |
| 138 | + | |
| 71 | 139 | static const char * const success_tests[] = { |
| 72 | 140 | "test_task_acquire_release_argument", |
| 73 | 141 | "test_task_acquire_release_current", |
| @@ -83,6 +151,11 @@ static const char * const success_tests[] = { · 9 unmodified lines | ||
| 83 | 151 | "test_task_kfunc_flavor_relo_not_found", |
| 84 | 152 | }; |
| 85 | 153 | |
| 154 | +static const char * const vpid_success_tests[] = { | |
| 155 | + "test_task_from_vpid_current", | |
| 156 | + "test_task_from_vpid_invalid", | |
| 157 | +}; | |
| 158 | + | |
| 86 | 159 | void test_task_kfunc(void) |
| 87 | 160 | { |
| 88 | 161 | int i; |
| @@ -94,5 +167,12 @@ void test_task_kfunc(void) · 5 unmodified lines | ||
| 94 | 167 | run_success_test(success_tests[i]); |
| 95 | 168 | } |
| 96 | 169 | |
| 170 | + for (i = 0; i < ARRAY_SIZE(vpid_success_tests); i++) { | |
| 171 | + if (!test__start_subtest(vpid_success_tests[i])) | |
| 172 | + continue; | |
| 173 | + | |
| 174 | + run_vpid_success_test(vpid_success_tests[i]); | |
| 175 | + } | |
| 176 | + | |
| 97 | 177 | RUN_TESTS(task_kfunc_failure); |
| 98 | 178 | } |
| @@ -7,12 +7,20 @@ · 6 unmodified lines | ||
| 7 | 7 | #include <pthread.h> |
| 8 | 8 | #include <sys/syscall.h> /* For SYS_xxx definitions */ |
| 9 | 9 | #include <sys/types.h> |
| 10 | +#include <sys/eventfd.h> | |
| 11 | +#include <sys/mman.h> | |
| 10 | 12 | #include <test_progs.h> |
| 13 | +#include <bpf/btf.h> | |
| 11 | 14 | #include "task_local_storage_helpers.h" |
| 12 | 15 | #include "task_local_storage.skel.h" |
| 13 | 16 | #include "task_local_storage_exit_creds.skel.h" |
| 14 | 17 | #include "task_ls_recursion.skel.h" |
| 15 | 18 | #include "task_storage_nodeadlock.skel.h" |
| 19 | +#include "uptr_test_common.h" | |
| 20 | +#include "task_ls_uptr.skel.h" | |
| 21 | +#include "uptr_update_failure.skel.h" | |
| 22 | +#include "uptr_failure.skel.h" | |
| 23 | +#include "uptr_map_failure.skel.h" | |
| 16 | 24 | |
| 17 | 25 | static void test_sys_enter_exit(void) |
| 18 | 26 | { |
| @@ -23,14 +31,14 @@ static void test_sys_enter_exit(void) · 4 unmodified lines | ||
| 23 | 31 | if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) |
| 24 | 32 | return; |
| 25 | 33 | |
| 26 | − skel->bss->target_pid = syscall(SYS_gettid); | |
| 34 | + skel->bss->target_pid = sys_gettid(); | |
| 27 | 35 | |
| 28 | 36 | err = task_local_storage__attach(skel); |
| 29 | 37 | if (!ASSERT_OK(err, "skel_attach")) |
| 30 | 38 | goto out; |
| 31 | 39 | |
| 32 | − syscall(SYS_gettid); | |
| 33 | − syscall(SYS_gettid); | |
| 40 | + sys_gettid(); | |
| 41 | + sys_gettid(); | |
| 34 | 42 | |
| 35 | 43 | /* 3x syscalls: 1x attach and 2x gettid */ |
| 36 | 44 | ASSERT_EQ(skel->bss->enter_cnt, 3, "enter_cnt"); |
| @@ -99,7 +107,7 @@ static void test_recursion(void) · 62 unmodified lines | ||
| 99 | 107 | |
| 100 | 108 | /* trigger sys_enter, make sure it does not cause deadlock */ |
| 101 | 109 | skel->bss->test_pid = getpid(); |
| 102 | − syscall(SYS_gettid); | |
| 110 | + sys_gettid(); | |
| 103 | 111 | skel->bss->test_pid = 0; |
| 104 | 112 | task_ls_recursion__detach(skel); |
| 105 | 113 | |
| @@ -227,6 +235,259 @@ done: · 121 unmodified lines | ||
| 227 | 235 | sched_setaffinity(getpid(), sizeof(old), &old); |
| 228 | 236 | } |
| 229 | 237 | |
| 238 | +static struct user_data udata __attribute__((aligned(16))) = { | |
| 239 | + .a = 1, | |
| 240 | + .b = 2, | |
| 241 | +}; | |
| 242 | + | |
| 243 | +static struct user_data udata2 __attribute__((aligned(16))) = { | |
| 244 | + .a = 3, | |
| 245 | + .b = 4, | |
| 246 | +}; | |
| 247 | + | |
| 248 | +static void check_udata2(int expected) | |
| 249 | +{ | |
| 250 | + udata2.result = udata2.nested_result = 0; | |
| 251 | + usleep(1); | |
| 252 | + ASSERT_EQ(udata2.result, expected, "udata2.result"); | |
| 253 | + ASSERT_EQ(udata2.nested_result, expected, "udata2.nested_result"); | |
| 254 | +} | |
| 255 | + | |
| 256 | +static void test_uptr_basic(void) | |
| 257 | +{ | |
| 258 | + int map_fd, parent_task_fd, ev_fd; | |
| 259 | + struct value_type value = {}; | |
| 260 | + struct task_ls_uptr *skel; | |
| 261 | + pid_t child_pid, my_tid; | |
| 262 | + __u64 ev_dummy_data = 1; | |
| 263 | + int err; | |
| 264 | + | |
| 265 | + my_tid = sys_gettid(); | |
| 266 | + parent_task_fd = sys_pidfd_open(my_tid, 0); | |
| 267 | + if (!ASSERT_OK_FD(parent_task_fd, "parent_task_fd")) | |
| 268 | + return; | |
| 269 | + | |
| 270 | + ev_fd = eventfd(0, 0); | |
| 271 | + if (!ASSERT_OK_FD(ev_fd, "ev_fd")) { | |
| 272 | + close(parent_task_fd); | |
| 273 | + return; | |
| 274 | + } | |
| 275 | + | |
| 276 | + skel = task_ls_uptr__open_and_load(); | |
| 277 | + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) | |
| 278 | + goto out; | |
| 279 | + | |
| 280 | + map_fd = bpf_map__fd(skel->maps.datamap); | |
| 281 | + value.udata = &udata; | |
| 282 | + value.nested.udata = &udata; | |
| 283 | + err = bpf_map_update_elem(map_fd, &parent_task_fd, &value, BPF_NOEXIST); | |
| 284 | + if (!ASSERT_OK(err, "update_elem(udata)")) | |
| 285 | + goto out; | |
| 286 | + | |
| 287 | + err = task_ls_uptr__attach(skel); | |
| 288 | + if (!ASSERT_OK(err, "skel_attach")) | |
| 289 | + goto out; | |
| 290 | + | |
| 291 | + child_pid = fork(); | |
| 292 | + if (!ASSERT_NEQ(child_pid, -1, "fork")) | |
| 293 | + goto out; | |
| 294 | + | |
| 295 | + /* Call syscall in the child process, but access the map value of | |
| 296 | + * the parent process in the BPF program to check if the user kptr | |
| 297 | + * is translated/mapped correctly. | |
| 298 | + */ | |
| 299 | + if (child_pid == 0) { | |
| 300 | + /* child */ | |
| 301 | + | |
| 302 | + /* Overwrite the user_data in the child process to check if | |
| 303 | + * the BPF program accesses the user_data of the parent. | |
| 304 | + */ | |
| 305 | + udata.a = 0; | |
| 306 | + udata.b = 0; | |
| 307 | + | |
| 308 | + /* Wait for the parent to set child_pid */ | |
| 309 | + read(ev_fd, &ev_dummy_data, sizeof(ev_dummy_data)); | |
| 310 | + exit(0); | |
| 311 | + } | |
| 312 | + | |
| 313 | + skel->bss->parent_pid = my_tid; | |
| 314 | + skel->bss->target_pid = child_pid; | |
| 315 | + | |
| 316 | + write(ev_fd, &ev_dummy_data, sizeof(ev_dummy_data)); | |
| 317 | + | |
| 318 | + err = waitpid(child_pid, NULL, 0); | |
| 319 | + ASSERT_EQ(err, child_pid, "waitpid"); | |
| 320 | + ASSERT_EQ(udata.result, MAGIC_VALUE + udata.a + udata.b, "udata.result"); | |
| 321 | + ASSERT_EQ(udata.nested_result, MAGIC_VALUE + udata.a + udata.b, "udata.nested_result"); | |
| 322 | + | |
| 323 | + skel->bss->target_pid = my_tid; | |
| 324 | + | |
| 325 | + /* update_elem: uptr changes from udata1 to udata2 */ | |
| 326 | + value.udata = &udata2; | |
| 327 | + value.nested.udata = &udata2; | |
| 328 | + err = bpf_map_update_elem(map_fd, &parent_task_fd, &value, BPF_EXIST); | |
| 329 | + if (!ASSERT_OK(err, "update_elem(udata2)")) | |
| 330 | + goto out; | |
| 331 | + check_udata2(MAGIC_VALUE + udata2.a + udata2.b); | |
| 332 | + | |
| 333 | + /* update_elem: uptr changes from udata2 uptr to NULL */ | |
| 334 | + memset(&value, 0, sizeof(value)); | |
| 335 | + err = bpf_map_update_elem(map_fd, &parent_task_fd, &value, BPF_EXIST); | |
| 336 | + if (!ASSERT_OK(err, "update_elem(udata2)")) | |
| 337 | + goto out; | |
| 338 | + check_udata2(0); | |
| 339 | + | |
| 340 | + /* update_elem: uptr changes from NULL to udata2 */ | |
| 341 | + value.udata = &udata2; | |
| 342 | + value.nested.udata = &udata2; | |
| 343 | + err = bpf_map_update_elem(map_fd, &parent_task_fd, &value, BPF_EXIST); | |
| 344 | + if (!ASSERT_OK(err, "update_elem(udata2)")) | |
| 345 | + goto out; | |
| 346 | + check_udata2(MAGIC_VALUE + udata2.a + udata2.b); | |
| 347 | + | |
| 348 | + /* Check if user programs can access the value of user kptrs | |
| 349 | + * through bpf_map_lookup_elem(). Make sure the kernel value is not | |
| 350 | + * leaked. | |
| 351 | + */ | |
| 352 | + err = bpf_map_lookup_elem(map_fd, &parent_task_fd, &value); | |
| 353 | + if (!ASSERT_OK(err, "bpf_map_lookup_elem")) | |
| 354 | + goto out; | |
| 355 | + ASSERT_EQ(value.udata, NULL, "value.udata"); | |
| 356 | + ASSERT_EQ(value.nested.udata, NULL, "value.nested.udata"); | |
| 357 | + | |
| 358 | + /* delete_elem */ | |
| 359 | + err = bpf_map_delete_elem(map_fd, &parent_task_fd); | |
| 360 | + ASSERT_OK(err, "delete_elem(udata2)"); | |
| 361 | + check_udata2(0); | |
| 362 | + | |
| 363 | + /* update_elem: add uptr back to test map_free */ | |
| 364 | + value.udata = &udata2; | |
| 365 | + value.nested.udata = &udata2; | |
| 366 | + err = bpf_map_update_elem(map_fd, &parent_task_fd, &value, BPF_NOEXIST); | |
| 367 | + ASSERT_OK(err, "update_elem(udata2)"); | |
| 368 | + | |
| 369 | +out: | |
| 370 | + task_ls_uptr__destroy(skel); | |
| 371 | + close(ev_fd); | |
| 372 | + close(parent_task_fd); | |
| 373 | +} | |
| 374 | + | |
| 375 | +static void test_uptr_across_pages(void) | |
| 376 | +{ | |
| 377 | + int page_size = getpagesize(); | |
| 378 | + struct value_type value = {}; | |
| 379 | + struct task_ls_uptr *skel; | |
| 380 | + int err, task_fd, map_fd; | |
| 381 | + void *mem; | |
| 382 | + | |
| 383 | + task_fd = sys_pidfd_open(getpid(), 0); | |
| 384 | + if (!ASSERT_OK_FD(task_fd, "task_fd")) | |
| 385 | + return; | |
| 386 | + | |
| 387 | + mem = mmap(NULL, page_size * 2, PROT_READ | PROT_WRITE, | |
| 388 | + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
| 389 | + if (!ASSERT_OK_PTR(mem, "mmap(page_size * 2)")) { | |
| 390 | + close(task_fd); | |
| 391 | + return; | |
| 392 | + } | |
| 393 | + | |
| 394 | + skel = task_ls_uptr__open_and_load(); | |
| 395 | + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) | |
| 396 | + goto out; | |
| 397 | + | |
| 398 | + map_fd = bpf_map__fd(skel->maps.datamap); | |
| 399 | + value.udata = mem + page_size - offsetof(struct user_data, b); | |
| 400 | + err = bpf_map_update_elem(map_fd, &task_fd, &value, 0); | |
| 401 | + if (!ASSERT_ERR(err, "update_elem(udata)")) | |
| 402 | + goto out; | |
| 403 | + ASSERT_EQ(errno, EOPNOTSUPP, "errno"); | |
| 404 | + | |
| 405 | + value.udata = mem + page_size - sizeof(struct user_data); | |
| 406 | + err = bpf_map_update_elem(map_fd, &task_fd, &value, 0); | |
| 407 | + ASSERT_OK(err, "update_elem(udata)"); | |
| 408 | + | |
| 409 | +out: | |
| 410 | + task_ls_uptr__destroy(skel); | |
| 411 | + close(task_fd); | |
| 412 | + munmap(mem, page_size * 2); | |
| 413 | +} | |
| 414 | + | |
| 415 | +static void test_uptr_update_failure(void) | |
| 416 | +{ | |
| 417 | + struct value_lock_type value = {}; | |
| 418 | + struct uptr_update_failure *skel; | |
| 419 | + int err, task_fd, map_fd; | |
| 420 | + | |
| 421 | + task_fd = sys_pidfd_open(getpid(), 0); | |
| 422 | + if (!ASSERT_OK_FD(task_fd, "task_fd")) | |
| 423 | + return; | |
| 424 | + | |
| 425 | + skel = uptr_update_failure__open_and_load(); | |
| 426 | + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) | |
| 427 | + goto out; | |
| 428 | + | |
| 429 | + map_fd = bpf_map__fd(skel->maps.datamap); | |
| 430 | + | |
| 431 | + value.udata = &udata; | |
| 432 | + err = bpf_map_update_elem(map_fd, &task_fd, &value, BPF_F_LOCK); | |
| 433 | + if (!ASSERT_ERR(err, "update_elem(udata, BPF_F_LOCK)")) | |
| 434 | + goto out; | |
| 435 | + ASSERT_EQ(errno, EOPNOTSUPP, "errno"); | |
| 436 | + | |
| 437 | + err = bpf_map_update_elem(map_fd, &task_fd, &value, BPF_EXIST); | |
| 438 | + if (!ASSERT_ERR(err, "update_elem(udata, BPF_EXIST)")) | |
| 439 | + goto out; | |
| 440 | + ASSERT_EQ(errno, ENOENT, "errno"); | |
| 441 | + | |
| 442 | + err = bpf_map_update_elem(map_fd, &task_fd, &value, BPF_NOEXIST); | |
| 443 | + if (!ASSERT_OK(err, "update_elem(udata, BPF_NOEXIST)")) | |
| 444 | + goto out; | |
| 445 | + | |
| 446 | + value.udata = &udata2; | |
| 447 | + err = bpf_map_update_elem(map_fd, &task_fd, &value, BPF_NOEXIST); | |
| 448 | + if (!ASSERT_ERR(err, "update_elem(udata2, BPF_NOEXIST)")) | |
| 449 | + goto out; | |
| 450 | + ASSERT_EQ(errno, EEXIST, "errno"); | |
| 451 | + | |
| 452 | +out: | |
| 453 | + uptr_update_failure__destroy(skel); | |
| 454 | + close(task_fd); | |
| 455 | +} | |
| 456 | + | |
| 457 | +static void test_uptr_map_failure(const char *map_name, int expected_errno) | |
| 458 | +{ | |
| 459 | + LIBBPF_OPTS(bpf_map_create_opts, create_attr); | |
| 460 | + struct uptr_map_failure *skel; | |
| 461 | + struct bpf_map *map; | |
| 462 | + struct btf *btf; | |
| 463 | + int map_fd, err; | |
| 464 | + | |
| 465 | + skel = uptr_map_failure__open(); | |
| 466 | + if (!ASSERT_OK_PTR(skel, "uptr_map_failure__open")) | |
| 467 | + return; | |
| 468 | + | |
| 469 | + map = bpf_object__find_map_by_name(skel->obj, map_name); | |
| 470 | + btf = bpf_object__btf(skel->obj); | |
| 471 | + err = btf__load_into_kernel(btf); | |
| 472 | + if (!ASSERT_OK(err, "btf__load_into_kernel")) | |
| 473 | + goto done; | |
| 474 | + | |
| 475 | + create_attr.map_flags = bpf_map__map_flags(map); | |
| 476 | + create_attr.btf_fd = btf__fd(btf); | |
| 477 | + create_attr.btf_key_type_id = bpf_map__btf_key_type_id(map); | |
| 478 | + create_attr.btf_value_type_id = bpf_map__btf_value_type_id(map); | |
| 479 | + map_fd = bpf_map_create(bpf_map__type(map), map_name, | |
| 480 | + bpf_map__key_size(map), bpf_map__value_size(map), | |
| 481 | + 0, &create_attr); | |
| 482 | + if (ASSERT_ERR_FD(map_fd, "map_create")) | |
| 483 | + ASSERT_EQ(errno, expected_errno, "errno"); | |
| 484 | + else | |
| 485 | + close(map_fd); | |
| 486 | + | |
| 487 | +done: | |
| 488 | + uptr_map_failure__destroy(skel); | |
| 489 | +} | |
| 490 | + | |
| 230 | 491 | void test_task_local_storage(void) |
| 231 | 492 | { |
| 232 | 493 | if (test__start_subtest("sys_enter_exit")) |
| @@ -237,4 +498,21 @@ void test_task_local_storage(void) · 4 unmodified lines | ||
| 237 | 498 | test_recursion(); |
| 238 | 499 | if (test__start_subtest("nodeadlock")) |
| 239 | 500 | test_nodeadlock(); |
| 501 | + if (test__start_subtest("uptr_basic")) | |
| 502 | + test_uptr_basic(); | |
| 503 | + if (test__start_subtest("uptr_across_pages")) | |
| 504 | + test_uptr_across_pages(); | |
| 505 | + if (test__start_subtest("uptr_update_failure")) | |
| 506 | + test_uptr_update_failure(); | |
| 507 | + if (test__start_subtest("uptr_map_failure_e2big")) { | |
| 508 | + if (getpagesize() == PAGE_SIZE) | |
| 509 | + test_uptr_map_failure("large_uptr_map", E2BIG); | |
| 510 | + else | |
| 511 | + test__skip(); | |
| 512 | + } | |
| 513 | + if (test__start_subtest("uptr_map_failure_size0")) | |
| 514 | + test_uptr_map_failure("empty_uptr_map", EINVAL); | |
| 515 | + if (test__start_subtest("uptr_map_failure_kstruct")) | |
| 516 | + test_uptr_map_failure("kstruct_uptr_map", EINVAL); | |
| 517 | + RUN_TESTS(uptr_failure); | |
| 240 | 518 | } |
| @@ -5,6 +5,7 @@ · 4 unmodified lines | ||
| 5 | 5 | #include <test_progs.h> |
| 6 | 6 | #include <pthread.h> |
| 7 | 7 | #include <network_helpers.h> |
| 8 | +#include <sys/sysinfo.h> | |
| 8 | 9 | |
| 9 | 10 | #include "timer_lockup.skel.h" |
| 10 | 11 | |
| @@ -52,6 +53,11 @@ void test_timer_lockup(void) · 41 unmodified lines | ||
| 52 | 53 | pthread_t thrds[2]; |
| 53 | 54 | void *ret; |
| 54 | 55 | |
| 56 | + if (get_nprocs() < 2) { | |
| 57 | + test__skip(); | |
| 58 | + return; | |
| 59 | + } | |
| 60 | + | |
| 55 | 61 | skel = timer_lockup__open_and_load(); |
| 56 | 62 | if (!ASSERT_OK_PTR(skel, "timer_lockup__open_and_load")) |
| 57 | 63 | return; |
| @@ -828,8 +828,12 @@ static int userns_obj_priv_btf_success(int mnt_fd, struct token_lsm *lsm_skel) · 827 unmodified lines | ||
| 828 | 828 | return validate_struct_ops_load(mnt_fd, true /* should succeed */); |
| 829 | 829 | } |
| 830 | 830 | |
| 831 | +static const char *token_bpffs_custom_dir() | |
| 832 | +{ | |
| 833 | + return getenv("BPF_SELFTESTS_BPF_TOKEN_DIR") ?: "/tmp/bpf-token-fs"; | |
| 834 | +} | |
| 835 | + | |
| 831 | 836 | #define TOKEN_ENVVAR "LIBBPF_BPF_TOKEN_PATH" |
| 832 | −#define TOKEN_BPFFS_CUSTOM "/bpf-token-fs" | |
| 833 | 837 | |
| 834 | 838 | static int userns_obj_priv_implicit_token(int mnt_fd, struct token_lsm *lsm_skel) |
| 835 | 839 | { |
| @@ -892,6 +896,7 @@ static int userns_obj_priv_implicit_token(int mnt_fd, struct token_lsm *lsm_skel · 56 unmodified lines | ||
| 892 | 896 | |
| 893 | 897 | static int userns_obj_priv_implicit_token_envvar(int mnt_fd, struct token_lsm *lsm_skel) |
| 894 | 898 | { |
| 899 | + const char *custom_dir = token_bpffs_custom_dir(); | |
| 895 | 900 | LIBBPF_OPTS(bpf_object_open_opts, opts); |
| 896 | 901 | struct dummy_st_ops_success *skel; |
| 897 | 902 | int err; |
| @@ -909,10 +914,10 @@ static int userns_obj_priv_implicit_token_envvar(int mnt_fd, struct token_lsm *l · 11 unmodified lines | ||
| 909 | 914 | * BPF token implicitly, unless pointed to it through |
| 910 | 915 | * LIBBPF_BPF_TOKEN_PATH envvar |
| 911 | 916 | */ |
| 912 | − rmdir(TOKEN_BPFFS_CUSTOM); | |
| 913 | − if (!ASSERT_OK(mkdir(TOKEN_BPFFS_CUSTOM, 0777), "mkdir_bpffs_custom")) | |
| 917 | + rmdir(custom_dir); | |
| 918 | + if (!ASSERT_OK(mkdir(custom_dir, 0777), "mkdir_bpffs_custom")) | |
| 914 | 919 | goto err_out; |
| 915 | − err = sys_move_mount(mnt_fd, "", AT_FDCWD, TOKEN_BPFFS_CUSTOM, MOVE_MOUNT_F_EMPTY_PATH); | |
| 920 | + err = sys_move_mount(mnt_fd, "", AT_FDCWD, custom_dir, MOVE_MOUNT_F_EMPTY_PATH); | |
| 916 | 921 | if (!ASSERT_OK(err, "move_mount_bpffs")) |
| 917 | 922 | goto err_out; |
| 918 | 923 | |
| @@ -925,7 +930,7 @@ static int userns_obj_priv_implicit_token_envvar(int mnt_fd, struct token_lsm *l · 6 unmodified lines | ||
| 925 | 930 | goto err_out; |
| 926 | 931 | } |
| 927 | 932 | |
| 928 | − err = setenv(TOKEN_ENVVAR, TOKEN_BPFFS_CUSTOM, 1 /*overwrite*/); | |
| 933 | + err = setenv(TOKEN_ENVVAR, custom_dir, 1 /*overwrite*/); | |
| 929 | 934 | if (!ASSERT_OK(err, "setenv_token_path")) |
| 930 | 935 | goto err_out; |
| 931 | 936 | |
| @@ -951,11 +956,11 @@ static int userns_obj_priv_implicit_token_envvar(int mnt_fd, struct token_lsm *l · 19 unmodified lines | ||
| 951 | 956 | if (!ASSERT_ERR(err, "obj_empty_token_path_load")) |
| 952 | 957 | goto err_out; |
| 953 | 958 | |
| 954 | − rmdir(TOKEN_BPFFS_CUSTOM); | |
| 959 | + rmdir(custom_dir); | |
| 955 | 960 | unsetenv(TOKEN_ENVVAR); |
| 956 | 961 | return 0; |
| 957 | 962 | err_out: |
| 958 | − rmdir(TOKEN_BPFFS_CUSTOM); | |
| 963 | + rmdir(custom_dir); | |
| 959 | 964 | unsetenv(TOKEN_ENVVAR); |
| 960 | 965 | return -EINVAL; |
| 961 | 966 | } |
| @@ -8,6 +8,11 @@ · 7 unmodified lines | ||
| 8 | 8 | #include "uprobe_multi_usdt.skel.h" |
| 9 | 9 | #include "uprobe_multi_consumers.skel.h" |
| 10 | 10 | #include "uprobe_multi_pid_filter.skel.h" |
| 11 | +#include "uprobe_multi_session.skel.h" | |
| 12 | +#include "uprobe_multi_session_single.skel.h" | |
| 13 | +#include "uprobe_multi_session_cookie.skel.h" | |
| 14 | +#include "uprobe_multi_session_recursive.skel.h" | |
| 15 | +#include "uprobe_multi_verifier.skel.h" | |
| 11 | 16 | #include "bpf/libbpf_internal.h" |
| 12 | 17 | #include "testing_helpers.h" |
| 13 | 18 | #include "../sdt.h" |
| @@ -34,6 +39,12 @@ noinline void usdt_trigger(void) · 20 unmodified lines | ||
| 34 | 39 | STAP_PROBE(test, pid_filter_usdt); |
| 35 | 40 | } |
| 36 | 41 | |
| 42 | +noinline void uprobe_session_recursive(int i) | |
| 43 | +{ | |
| 44 | + if (i) | |
| 45 | + uprobe_session_recursive(i - 1); | |
| 46 | +} | |
| 47 | + | |
| 37 | 48 | struct child { |
| 38 | 49 | int go[2]; |
| 39 | 50 | int c2p[2]; /* child -> parent channel */ |
| @@ -125,7 +136,7 @@ static void *child_thread(void *ctx) · 85 unmodified lines | ||
| 125 | 136 | struct child *child = ctx; |
| 126 | 137 | int c = 0, err; |
| 127 | 138 | |
| 128 | − child->tid = syscall(SYS_gettid); | |
| 139 | + child->tid = sys_gettid(); | |
| 129 | 140 | |
| 130 | 141 | /* let parent know we are ready */ |
| 131 | 142 | err = write(child->c2p[1], &c, 1); |
| @@ -778,7 +789,7 @@ get_link(struct uprobe_multi_consumers *skel, int link) · 646 unmodified lines | ||
| 778 | 789 | } |
| 779 | 790 | } |
| 780 | 791 | |
| 781 | −static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx) | |
| 792 | +static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx, unsigned long offset) | |
| 782 | 793 | { |
| 783 | 794 | struct bpf_program *prog = get_program(skel, idx); |
| 784 | 795 | struct bpf_link **link = get_link(skel, idx); |
| @@ -787,15 +798,19 @@ static int uprobe_attach(struct uprobe_multi_consumers *skel, int idx) · 2 unmodified lines | ||
| 787 | 798 | if (!prog || !link) |
| 788 | 799 | return -1; |
| 789 | 800 | |
| 801 | + opts.offsets = &offset; | |
| 802 | + opts.cnt = 1; | |
| 803 | + | |
| 790 | 804 | /* |
| 791 | − * bit/prog: 0,1 uprobe entry | |
| 792 | − * bit/prog: 2,3 uprobe return | |
| 805 | + * bit/prog: 0 uprobe entry | |
| 806 | + * bit/prog: 1 uprobe return | |
| 807 | + * bit/prog: 2 uprobe session without return | |
| 808 | + * bit/prog: 3 uprobe session with return | |
| 793 | 809 | */ |
| 794 | − opts.retprobe = idx == 2 || idx == 3; | |
| 810 | + opts.retprobe = idx == 1; | |
| 811 | + opts.session = idx == 2 || idx == 3; | |
| 795 | 812 | |
| 796 | − *link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe", | |
| 797 | − "uprobe_consumer_test", | |
| 798 | − &opts); | |
| 813 | + *link = bpf_program__attach_uprobe_multi(prog, 0, "/proc/self/exe", NULL, &opts); | |
| 799 | 814 | if (!ASSERT_OK_PTR(*link, "bpf_program__attach_uprobe_multi")) |
| 800 | 815 | return -1; |
| 801 | 816 | return 0; |
| @@ -816,7 +831,8 @@ static bool test_bit(int bit, unsigned long val) · 14 unmodified lines | ||
| 816 | 831 | |
| 817 | 832 | noinline int |
| 818 | 833 | uprobe_consumer_test(struct uprobe_multi_consumers *skel, |
| 819 | − unsigned long before, unsigned long after) | |
| 834 | + unsigned long before, unsigned long after, | |
| 835 | + unsigned long offset) | |
| 820 | 836 | { |
| 821 | 837 | int idx; |
| 822 | 838 | |
| @@ -829,89 +845,174 @@ uprobe_consumer_test(struct uprobe_multi_consumers *skel, · 6 unmodified lines | ||
| 829 | 845 | /* ... and attach all new programs in 'after' state */ |
| 830 | 846 | for (idx = 0; idx < 4; idx++) { |
| 831 | 847 | if (!test_bit(idx, before) && test_bit(idx, after)) { |
| 832 | − if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_after")) | |
| 848 | + if (!ASSERT_OK(uprobe_attach(skel, idx, offset), "uprobe_attach_after")) | |
| 833 | 849 | return -1; |
| 834 | 850 | } |
| 835 | 851 | } |
| 836 | 852 | return 0; |
| 837 | 853 | } |
| 838 | 854 | |
| 839 | −static void consumer_test(struct uprobe_multi_consumers *skel, | |
| 840 | − unsigned long before, unsigned long after) | |
| 855 | +/* | |
| 856 | + * We generate 16 consumer_testX functions that will have uprobe installed on | |
| 857 | + * and will be called in separate threads. All function pointer are stored in | |
| 858 | + * "consumers" section and each thread will pick one function based on index. | |
| 859 | + */ | |
| 860 | + | |
| 861 | +extern const void *__start_consumers; | |
| 862 | + | |
| 863 | +#define __CONSUMER_TEST(func) \ | |
| 864 | +noinline int func(struct uprobe_multi_consumers *skel, unsigned long before, \ | |
| 865 | + unsigned long after, unsigned long offset) \ | |
| 866 | +{ \ | |
| 867 | + return uprobe_consumer_test(skel, before, after, offset); \ | |
| 868 | +} \ | |
| 869 | +void *__ ## func __used __attribute__((section("consumers"))) = (void *) func; | |
| 870 | + | |
| 871 | +#define CONSUMER_TEST(func) __CONSUMER_TEST(func) | |
| 872 | + | |
| 873 | +#define C1 CONSUMER_TEST(__PASTE(consumer_test, __COUNTER__)) | |
| 874 | +#define C4 C1 C1 C1 C1 | |
| 875 | +#define C16 C4 C4 C4 C4 | |
| 876 | + | |
| 877 | +C16 | |
| 878 | + | |
| 879 | +typedef int (*test_t)(struct uprobe_multi_consumers *, unsigned long, | |
| 880 | + unsigned long, unsigned long); | |
| 881 | + | |
| 882 | +static int consumer_test(struct uprobe_multi_consumers *skel, | |
| 883 | + unsigned long before, unsigned long after, | |
| 884 | + test_t test, unsigned long offset) | |
| 841 | 885 | { |
| 842 | − int err, idx; | |
| 886 | + int err, idx, ret = -1; | |
| 843 | 887 | |
| 844 | 888 | printf("consumer_test before %lu after %lu\n", before, after); |
| 845 | 889 | |
| 846 | 890 | /* 'before' is each, we attach uprobe for every set idx */ |
| 847 | 891 | for (idx = 0; idx < 4; idx++) { |
| 848 | 892 | if (test_bit(idx, before)) { |
| 849 | − if (!ASSERT_OK(uprobe_attach(skel, idx), "uprobe_attach_before")) | |
| 893 | + if (!ASSERT_OK(uprobe_attach(skel, idx, offset), "uprobe_attach_before")) | |
| 850 | 894 | goto cleanup; |
| 851 | 895 | } |
| 852 | 896 | } |
| 853 | 897 | |
| 854 | − err = uprobe_consumer_test(skel, before, after); | |
| 898 | + err = test(skel, before, after, offset); | |
| 855 | 899 | if (!ASSERT_EQ(err, 0, "uprobe_consumer_test")) |
| 856 | 900 | goto cleanup; |
| 857 | 901 | |
| 858 | 902 | for (idx = 0; idx < 4; idx++) { |
| 903 | + bool uret_stays, uret_survives; | |
| 859 | 904 | const char *fmt = "BUG"; |
| 860 | 905 | __u64 val = 0; |
| 861 | 906 | |
| 862 | − if (idx < 2) { | |
| 907 | + switch (idx) { | |
| 908 | + case 0: | |
| 863 | 909 | /* |
| 864 | 910 | * uprobe entry |
| 865 | 911 | * +1 if define in 'before' |
| 866 | 912 | */ |
| 867 | 913 | if (test_bit(idx, before)) |
| 868 | 914 | val++; |
| 869 | − fmt = "prog 0/1: uprobe"; | |
| 870 | − } else { | |
| 915 | + fmt = "prog 0: uprobe"; | |
| 916 | + break; | |
| 917 | + case 1: | |
| 871 | 918 | /* |
| 872 | − * uprobe return is tricky ;-) | |
| 873 | − * | |
| 874 | − * to trigger uretprobe consumer, the uretprobe needs to be installed, | |
| 875 | − * which means one of the 'return' uprobes was alive when probe was hit: | |
| 876 | − * | |
| 877 | − * idxs: 2/3 uprobe return in 'installed' mask | |
| 878 | − * | |
| 879 | − * in addition if 'after' state removes everything that was installed in | |
| 880 | − * 'before' state, then uprobe kernel object goes away and return uprobe | |
| 881 | − * is not installed and we won't hit it even if it's in 'after' state. | |
| 919 | + * To trigger uretprobe consumer, the uretprobe under test either stayed from | |
| 920 | + * before to after (uret_stays + test_bit) or uretprobe instance survived and | |
| 921 | + * we have uretprobe active in after (uret_survives + test_bit) | |
| 882 | 922 | */ |
| 883 | − unsigned long had_uretprobes = before & 0b1100; /* is uretprobe installed */ | |
| 884 | − unsigned long probe_preserved = before & after; /* did uprobe go away */ | |
| 923 | + uret_stays = before & after & 0b0110; | |
| 924 | + uret_survives = ((before & 0b0110) && (after & 0b0110) && (before & 0b1001)); | |
| 885 | 925 | |
| 886 | − if (had_uretprobes && probe_preserved && test_bit(idx, after)) | |
| 926 | + if ((uret_stays || uret_survives) && test_bit(idx, after)) | |
| 887 | 927 | val++; |
| 888 | − fmt = "idx 2/3: uretprobe"; | |
| 928 | + fmt = "prog 1: uretprobe"; | |
| 929 | + break; | |
| 930 | + case 2: | |
| 931 | + /* | |
| 932 | + * session with return | |
| 933 | + * +1 if defined in 'before' | |
| 934 | + * +1 if defined in 'after' | |
| 935 | + */ | |
| 936 | + if (test_bit(idx, before)) { | |
| 937 | + val++; | |
| 938 | + if (test_bit(idx, after)) | |
| 939 | + val++; | |
| 940 | + } | |
| 941 | + fmt = "prog 2: session with return"; | |
| 942 | + break; | |
| 943 | + case 3: | |
| 944 | + /* | |
| 945 | + * session without return | |
| 946 | + * +1 if defined in 'before' | |
| 947 | + */ | |
| 948 | + if (test_bit(idx, before)) | |
| 949 | + val++; | |
| 950 | + fmt = "prog 3: session with NO return"; | |
| 951 | + break; | |
| 889 | 952 | } |
| 890 | 953 | |
| 891 | − ASSERT_EQ(skel->bss->uprobe_result[idx], val, fmt); | |
| 954 | + if (!ASSERT_EQ(skel->bss->uprobe_result[idx], val, fmt)) | |
| 955 | + goto cleanup; | |
| 892 | 956 | skel->bss->uprobe_result[idx] = 0; |
| 893 | 957 | } |
| 894 | 958 | |
| 959 | + ret = 0; | |
| 960 | + | |
| 895 | 961 | cleanup: |
| 896 | 962 | for (idx = 0; idx < 4; idx++) |
| 897 | 963 | uprobe_detach(skel, idx); |
| 964 | + return ret; | |
| 898 | 965 | } |
| 899 | 966 | |
| 900 | −static void test_consumers(void) | |
| 967 | +#define CONSUMER_MAX 16 | |
| 968 | + | |
| 969 | +/* | |
| 970 | + * Each thread runs 1/16 of the load by running test for single | |
| 971 | + * 'before' number (based on thread index) and full scale of | |
| 972 | + * 'after' numbers. | |
| 973 | + */ | |
| 974 | +static void *consumer_thread(void *arg) | |
| 901 | 975 | { |
| 976 | + unsigned long idx = (unsigned long) arg; | |
| 902 | 977 | struct uprobe_multi_consumers *skel; |
| 903 | − int before, after; | |
| 978 | + unsigned long offset; | |
| 979 | + const void *func; | |
| 980 | + int after; | |
| 904 | 981 | |
| 905 | 982 | skel = uprobe_multi_consumers__open_and_load(); |
| 906 | 983 | if (!ASSERT_OK_PTR(skel, "uprobe_multi_consumers__open_and_load")) |
| 907 | − return; | |
| 984 | + return NULL; | |
| 985 | + | |
| 986 | + func = *((&__start_consumers) + idx); | |
| 987 | + | |
| 988 | + offset = get_uprobe_offset(func); | |
| 989 | + if (!ASSERT_GE(offset, 0, "uprobe_offset")) | |
| 990 | + goto out; | |
| 991 | + | |
| 992 | + for (after = 0; after < CONSUMER_MAX; after++) | |
| 993 | + if (consumer_test(skel, idx, after, func, offset)) | |
| 994 | + goto out; | |
| 995 | + | |
| 996 | +out: | |
| 997 | + uprobe_multi_consumers__destroy(skel); | |
| 998 | + return NULL; | |
| 999 | +} | |
| 1000 | + | |
| 1001 | + | |
| 1002 | +static void test_consumers(void) | |
| 1003 | +{ | |
| 1004 | + pthread_t pt[CONSUMER_MAX]; | |
| 1005 | + unsigned long idx; | |
| 1006 | + int err; | |
| 908 | 1007 | |
| 909 | 1008 | /* |
| 910 | 1009 | * The idea of this test is to try all possible combinations of |
| 911 | 1010 | * uprobes consumers attached on single function. |
| 912 | 1011 | * |
| 913 | − * - 2 uprobe entry consumer | |
| 914 | − * - 2 uprobe exit consumers | |
| 1012 | + * - 1 uprobe entry consumer | |
| 1013 | + * - 1 uprobe exit consumer | |
| 1014 | + * - 1 uprobe session with return | |
| 1015 | + * - 1 uprobe session without return | |
| 915 | 1016 | * |
| 916 | 1017 | * The test uses 4 uprobes attached on single function, but that |
| 917 | 1018 | * translates into single uprobe with 4 consumers in kernel. |
| @@ -919,37 +1020,38 @@ static void test_consumers(void) · 1 unmodified line | ||
| 919 | 1020 | * The before/after values present the state of attached consumers |
| 920 | 1021 | * before and after the probed function: |
| 921 | 1022 | * |
| 922 | − * bit/prog 0,1 : uprobe entry | |
| 923 | − * bit/prog 2,3 : uprobe return | |
| 1023 | + * bit/prog 0 : uprobe entry | |
| 1024 | + * bit/prog 1 : uprobe return | |
| 924 | 1025 | * |
| 925 | 1026 | * For example for: |
| 926 | 1027 | * |
| 927 | − * before = 0b0101 | |
| 928 | − * after = 0b0110 | |
| 1028 | + * before = 0b01 | |
| 1029 | + * after = 0b10 | |
| 929 | 1030 | * |
| 930 | 1031 | * it means that before we call 'uprobe_consumer_test' we attach |
| 931 | 1032 | * uprobes defined in 'before' value: |
| 932 | 1033 | * |
| 933 | − * - bit/prog 0: uprobe entry | |
| 934 | − * - bit/prog 2: uprobe return | |
| 1034 | + * - bit/prog 1: uprobe entry | |
| 935 | 1035 | * |
| 936 | 1036 | * uprobe_consumer_test is called and inside it we attach and detach |
| 937 | 1037 | * uprobes based on 'after' value: |
| 938 | 1038 | * |
| 939 | − * - bit/prog 0: stays untouched | |
| 940 | − * - bit/prog 2: uprobe return is detached | |
| 1039 | + * - bit/prog 0: is detached | |
| 1040 | + * - bit/prog 1: is attached | |
| 941 | 1041 | * |
| 942 | 1042 | * uprobe_consumer_test returns and we check counters values increased |
| 943 | 1043 | * by bpf programs on each uprobe to match the expected count based on |
| 944 | 1044 | * before/after bits. |
| 945 | 1045 | */ |
| 946 | 1046 | |
| 947 | − for (before = 0; before < 16; before++) { | |
| 948 | − for (after = 0; after < 16; after++) | |
| 949 | − consumer_test(skel, before, after); | |
| 1047 | + for (idx = 0; idx < CONSUMER_MAX; idx++) { | |
| 1048 | + err = pthread_create(&pt[idx], NULL, consumer_thread, (void *) idx); | |
| 1049 | + if (!ASSERT_OK(err, "pthread_create")) | |
| 1050 | + break; | |
| 950 | 1051 | } |
| 951 | 1052 | |
| 952 | − uprobe_multi_consumers__destroy(skel); | |
| 1053 | + while (idx) | |
| 1054 | + pthread_join(pt[--idx], NULL); | |
| 953 | 1055 | } |
| 954 | 1056 | |
| 955 | 1057 | static struct bpf_program *uprobe_multi_program(struct uprobe_multi_pid_filter *skel, int idx) |
| @@ -1016,6 +1118,156 @@ static void test_pid_filter_process(bool clone_vm) · 60 unmodified lines | ||
| 1016 | 1118 | uprobe_multi_pid_filter__destroy(skel); |
| 1017 | 1119 | } |
| 1018 | 1120 | |
| 1121 | +static void test_session_skel_api(void) | |
| 1122 | +{ | |
| 1123 | + struct uprobe_multi_session *skel = NULL; | |
| 1124 | + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); | |
| 1125 | + struct bpf_link *link = NULL; | |
| 1126 | + int err; | |
| 1127 | + | |
| 1128 | + skel = uprobe_multi_session__open_and_load(); | |
| 1129 | + if (!ASSERT_OK_PTR(skel, "uprobe_multi_session__open_and_load")) | |
| 1130 | + goto cleanup; | |
| 1131 | + | |
| 1132 | + skel->bss->pid = getpid(); | |
| 1133 | + skel->bss->user_ptr = test_data; | |
| 1134 | + | |
| 1135 | + err = uprobe_multi_session__attach(skel); | |
| 1136 | + if (!ASSERT_OK(err, "uprobe_multi_session__attach")) | |
| 1137 | + goto cleanup; | |
| 1138 | + | |
| 1139 | + /* trigger all probes */ | |
| 1140 | + skel->bss->uprobe_multi_func_1_addr = (__u64) uprobe_multi_func_1; | |
| 1141 | + skel->bss->uprobe_multi_func_2_addr = (__u64) uprobe_multi_func_2; | |
| 1142 | + skel->bss->uprobe_multi_func_3_addr = (__u64) uprobe_multi_func_3; | |
| 1143 | + | |
| 1144 | + uprobe_multi_func_1(); | |
| 1145 | + uprobe_multi_func_2(); | |
| 1146 | + uprobe_multi_func_3(); | |
| 1147 | + | |
| 1148 | + /* | |
| 1149 | + * We expect 2 for uprobe_multi_func_2 because it runs both entry/return probe, | |
| 1150 | + * uprobe_multi_func_[13] run just the entry probe. All expected numbers are | |
| 1151 | + * doubled, because we run extra test for sleepable session. | |
| 1152 | + */ | |
| 1153 | + ASSERT_EQ(skel->bss->uprobe_session_result[0], 2, "uprobe_multi_func_1_result"); | |
| 1154 | + ASSERT_EQ(skel->bss->uprobe_session_result[1], 4, "uprobe_multi_func_2_result"); | |
| 1155 | + ASSERT_EQ(skel->bss->uprobe_session_result[2], 2, "uprobe_multi_func_3_result"); | |
| 1156 | + | |
| 1157 | + /* We expect increase in 3 entry and 1 return session calls -> 4 */ | |
| 1158 | + ASSERT_EQ(skel->bss->uprobe_multi_sleep_result, 4, "uprobe_multi_sleep_result"); | |
| 1159 | + | |
| 1160 | +cleanup: | |
| 1161 | + bpf_link__destroy(link); | |
| 1162 | + uprobe_multi_session__destroy(skel); | |
| 1163 | +} | |
| 1164 | + | |
| 1165 | +static void test_session_single_skel_api(void) | |
| 1166 | +{ | |
| 1167 | + struct uprobe_multi_session_single *skel = NULL; | |
| 1168 | + LIBBPF_OPTS(bpf_kprobe_multi_opts, opts); | |
| 1169 | + int err; | |
| 1170 | + | |
| 1171 | + skel = uprobe_multi_session_single__open_and_load(); | |
| 1172 | + if (!ASSERT_OK_PTR(skel, "uprobe_multi_session_single__open_and_load")) | |
| 1173 | + goto cleanup; | |
| 1174 | + | |
| 1175 | + skel->bss->pid = getpid(); | |
| 1176 | + | |
| 1177 | + err = uprobe_multi_session_single__attach(skel); | |
| 1178 | + if (!ASSERT_OK(err, "uprobe_multi_session_single__attach")) | |
| 1179 | + goto cleanup; | |
| 1180 | + | |
| 1181 | + uprobe_multi_func_1(); | |
| 1182 | + | |
| 1183 | + /* | |
| 1184 | + * We expect consumer 0 and 2 to trigger just entry handler (value 1) | |
| 1185 | + * and consumer 1 to hit both (value 2). | |
| 1186 | + */ | |
| 1187 | + ASSERT_EQ(skel->bss->uprobe_session_result[0], 1, "uprobe_session_result_0"); | |
| 1188 | + ASSERT_EQ(skel->bss->uprobe_session_result[1], 2, "uprobe_session_result_1"); | |
| 1189 | + ASSERT_EQ(skel->bss->uprobe_session_result[2], 1, "uprobe_session_result_2"); | |
| 1190 | + | |
| 1191 | +cleanup: | |
| 1192 | + uprobe_multi_session_single__destroy(skel); | |
| 1193 | +} | |
| 1194 | + | |
| 1195 | +static void test_session_cookie_skel_api(void) | |
| 1196 | +{ | |
| 1197 | + struct uprobe_multi_session_cookie *skel = NULL; | |
| 1198 | + int err; | |
| 1199 | + | |
| 1200 | + skel = uprobe_multi_session_cookie__open_and_load(); | |
| 1201 | + if (!ASSERT_OK_PTR(skel, "uprobe_multi_session_cookie__open_and_load")) | |
| 1202 | + goto cleanup; | |
| 1203 | + | |
| 1204 | + skel->bss->pid = getpid(); | |
| 1205 | + | |
| 1206 | + err = uprobe_multi_session_cookie__attach(skel); | |
| 1207 | + if (!ASSERT_OK(err, "uprobe_multi_session_cookie__attach")) | |
| 1208 | + goto cleanup; | |
| 1209 | + | |
| 1210 | + /* trigger all probes */ | |
| 1211 | + uprobe_multi_func_1(); | |
| 1212 | + uprobe_multi_func_2(); | |
| 1213 | + uprobe_multi_func_3(); | |
| 1214 | + | |
| 1215 | + ASSERT_EQ(skel->bss->test_uprobe_1_result, 1, "test_uprobe_1_result"); | |
| 1216 | + ASSERT_EQ(skel->bss->test_uprobe_2_result, 2, "test_uprobe_2_result"); | |
| 1217 | + ASSERT_EQ(skel->bss->test_uprobe_3_result, 3, "test_uprobe_3_result"); | |
| 1218 | + | |
| 1219 | +cleanup: | |
| 1220 | + uprobe_multi_session_cookie__destroy(skel); | |
| 1221 | +} | |
| 1222 | + | |
| 1223 | +static void test_session_recursive_skel_api(void) | |
| 1224 | +{ | |
| 1225 | + struct uprobe_multi_session_recursive *skel = NULL; | |
| 1226 | + int i, err; | |
| 1227 | + | |
| 1228 | + skel = uprobe_multi_session_recursive__open_and_load(); | |
| 1229 | + if (!ASSERT_OK_PTR(skel, "uprobe_multi_session_recursive__open_and_load")) | |
| 1230 | + goto cleanup; | |
| 1231 | + | |
| 1232 | + skel->bss->pid = getpid(); | |
| 1233 | + | |
| 1234 | + err = uprobe_multi_session_recursive__attach(skel); | |
| 1235 | + if (!ASSERT_OK(err, "uprobe_multi_session_recursive__attach")) | |
| 1236 | + goto cleanup; | |
| 1237 | + | |
| 1238 | + for (i = 0; i < ARRAY_SIZE(skel->bss->test_uprobe_cookie_entry); i++) | |
| 1239 | + skel->bss->test_uprobe_cookie_entry[i] = i + 1; | |
| 1240 | + | |
| 1241 | + uprobe_session_recursive(5); | |
| 1242 | + | |
| 1243 | + /* | |
| 1244 | + * entry uprobe: | |
| 1245 | + * uprobe_session_recursive(5) { *cookie = 1, return 0 | |
| 1246 | + * uprobe_session_recursive(4) { *cookie = 2, return 1 | |
| 1247 | + * uprobe_session_recursive(3) { *cookie = 3, return 0 | |
| 1248 | + * uprobe_session_recursive(2) { *cookie = 4, return 1 | |
| 1249 | + * uprobe_session_recursive(1) { *cookie = 5, return 0 | |
| 1250 | + * uprobe_session_recursive(0) { *cookie = 6, return 1 | |
| 1251 | + * return uprobe: | |
| 1252 | + * } i = 0 not executed | |
| 1253 | + * } i = 1 test_uprobe_cookie_return[0] = 5 | |
| 1254 | + * } i = 2 not executed | |
| 1255 | + * } i = 3 test_uprobe_cookie_return[1] = 3 | |
| 1256 | + * } i = 4 not executed | |
| 1257 | + * } i = 5 test_uprobe_cookie_return[2] = 1 | |
| 1258 | + */ | |
| 1259 | + | |
| 1260 | + ASSERT_EQ(skel->bss->idx_entry, 6, "idx_entry"); | |
| 1261 | + ASSERT_EQ(skel->bss->idx_return, 3, "idx_return"); | |
| 1262 | + | |
| 1263 | + ASSERT_EQ(skel->bss->test_uprobe_cookie_return[0], 5, "test_uprobe_cookie_return[0]"); | |
| 1264 | + ASSERT_EQ(skel->bss->test_uprobe_cookie_return[1], 3, "test_uprobe_cookie_return[1]"); | |
| 1265 | + ASSERT_EQ(skel->bss->test_uprobe_cookie_return[2], 1, "test_uprobe_cookie_return[2]"); | |
| 1266 | + | |
| 1267 | +cleanup: | |
| 1268 | + uprobe_multi_session_recursive__destroy(skel); | |
| 1269 | +} | |
| 1270 | + | |
| 1019 | 1271 | static void test_bench_attach_uprobe(void) |
| 1020 | 1272 | { |
| 1021 | 1273 | long attach_start_ns = 0, attach_end_ns = 0; |
| @@ -1112,4 +1364,13 @@ void test_uprobe_multi_test(void) · 90 unmodified lines | ||
| 1112 | 1364 | test_pid_filter_process(false); |
| 1113 | 1365 | if (test__start_subtest("filter_clone_vm")) |
| 1114 | 1366 | test_pid_filter_process(true); |
| 1367 | + if (test__start_subtest("session")) | |
| 1368 | + test_session_skel_api(); | |
| 1369 | + if (test__start_subtest("session_single")) | |
| 1370 | + test_session_single_skel_api(); | |
| 1371 | + if (test__start_subtest("session_cookie")) | |
| 1372 | + test_session_cookie_skel_api(); | |
| 1373 | + if (test__start_subtest("session_cookie_recursive")) | |
| 1374 | + test_session_recursive_skel_api(); | |
| 1375 | + RUN_TESTS(uprobe_multi_verifier); | |
| 1115 | 1376 | } |
| @@ -61,6 +61,7 @@ · 60 unmodified lines | ||
| 61 | 61 | #include "verifier_or_jmp32_k.skel.h" |
| 62 | 62 | #include "verifier_precision.skel.h" |
| 63 | 63 | #include "verifier_prevent_map_lookup.skel.h" |
| 64 | +#include "verifier_private_stack.skel.h" | |
| 64 | 65 | #include "verifier_raw_stack.skel.h" |
| 65 | 66 | #include "verifier_raw_tp_writable.skel.h" |
| 66 | 67 | #include "verifier_reg_equal.skel.h" |
| @@ -188,6 +189,7 @@ void test_verifier_bpf_fastcall(void) { RUN(verifier_bpf_fastcall); } · 121 unmodified lines | ||
| 188 | 189 | void test_verifier_or_jmp32_k(void) { RUN(verifier_or_jmp32_k); } |
| 189 | 190 | void test_verifier_precision(void) { RUN(verifier_precision); } |
| 190 | 191 | void test_verifier_prevent_map_lookup(void) { RUN(verifier_prevent_map_lookup); } |
| 192 | +void test_verifier_private_stack(void) { RUN(verifier_private_stack); } | |
| 191 | 193 | void test_verifier_raw_stack(void) { RUN(verifier_raw_stack); } |
| 192 | 194 | void test_verifier_raw_tp_writable(void) { RUN(verifier_raw_tp_writable); } |
| 193 | 195 | void test_verifier_reg_equal(void) { RUN(verifier_reg_equal); } |
| @@ -1,167 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | −/* Copyright (c) 2020 Facebook */ | |
| 3 | −/* "undefine" structs in vmlinux.h, because we "override" them below */ | |
| 4 | −#define bpf_iter_meta bpf_iter_meta___not_used | |
| 5 | −#define bpf_iter__bpf_map bpf_iter__bpf_map___not_used | |
| 6 | −#define bpf_iter__ipv6_route bpf_iter__ipv6_route___not_used | |
| 7 | −#define bpf_iter__netlink bpf_iter__netlink___not_used | |
| 8 | −#define bpf_iter__task bpf_iter__task___not_used | |
| 9 | −#define bpf_iter__task_file bpf_iter__task_file___not_used | |
| 10 | −#define bpf_iter__task_vma bpf_iter__task_vma___not_used | |
| 11 | −#define bpf_iter__tcp bpf_iter__tcp___not_used | |
| 12 | −#define tcp6_sock tcp6_sock___not_used | |
| 13 | −#define bpf_iter__udp bpf_iter__udp___not_used | |
| 14 | −#define udp6_sock udp6_sock___not_used | |
| 15 | −#define bpf_iter__unix bpf_iter__unix___not_used | |
| 16 | −#define bpf_iter__bpf_map_elem bpf_iter__bpf_map_elem___not_used | |
| 17 | −#define bpf_iter__bpf_sk_storage_map bpf_iter__bpf_sk_storage_map___not_used | |
| 18 | −#define bpf_iter__sockmap bpf_iter__sockmap___not_used | |
| 19 | −#define bpf_iter__bpf_link bpf_iter__bpf_link___not_used | |
| 20 | −#define bpf_iter__cgroup bpf_iter__cgroup___not_used | |
| 21 | −#define btf_ptr btf_ptr___not_used | |
| 22 | −#define BTF_F_COMPACT BTF_F_COMPACT___not_used | |
| 23 | −#define BTF_F_NONAME BTF_F_NONAME___not_used | |
| 24 | −#define BTF_F_PTR_RAW BTF_F_PTR_RAW___not_used | |
| 25 | −#define BTF_F_ZERO BTF_F_ZERO___not_used | |
| 26 | −#define bpf_iter__ksym bpf_iter__ksym___not_used | |
| 27 | −#include "vmlinux.h" | |
| 28 | −#undef bpf_iter_meta | |
| 29 | −#undef bpf_iter__bpf_map | |
| 30 | −#undef bpf_iter__ipv6_route | |
| 31 | −#undef bpf_iter__netlink | |
| 32 | −#undef bpf_iter__task | |
| 33 | −#undef bpf_iter__task_file | |
| 34 | −#undef bpf_iter__task_vma | |
| 35 | −#undef bpf_iter__tcp | |
| 36 | −#undef tcp6_sock | |
| 37 | −#undef bpf_iter__udp | |
| 38 | −#undef udp6_sock | |
| 39 | −#undef bpf_iter__unix | |
| 40 | −#undef bpf_iter__bpf_map_elem | |
| 41 | −#undef bpf_iter__bpf_sk_storage_map | |
| 42 | −#undef bpf_iter__sockmap | |
| 43 | −#undef bpf_iter__bpf_link | |
| 44 | −#undef bpf_iter__cgroup | |
| 45 | −#undef btf_ptr | |
| 46 | −#undef BTF_F_COMPACT | |
| 47 | −#undef BTF_F_NONAME | |
| 48 | −#undef BTF_F_PTR_RAW | |
| 49 | −#undef BTF_F_ZERO | |
| 50 | −#undef bpf_iter__ksym | |
| 51 | − | |
| 52 | −struct bpf_iter_meta { | |
| 53 | − struct seq_file *seq; | |
| 54 | − __u64 session_id; | |
| 55 | − __u64 seq_num; | |
| 56 | −} __attribute__((preserve_access_index)); | |
| 57 | − | |
| 58 | −struct bpf_iter__ipv6_route { | |
| 59 | − struct bpf_iter_meta *meta; | |
| 60 | − struct fib6_info *rt; | |
| 61 | −} __attribute__((preserve_access_index)); | |
| 62 | − | |
| 63 | −struct bpf_iter__netlink { | |
| 64 | − struct bpf_iter_meta *meta; | |
| 65 | − struct netlink_sock *sk; | |
| 66 | −} __attribute__((preserve_access_index)); | |
| 67 | − | |
| 68 | −struct bpf_iter__task { | |
| 69 | − struct bpf_iter_meta *meta; | |
| 70 | − struct task_struct *task; | |
| 71 | −} __attribute__((preserve_access_index)); | |
| 72 | − | |
| 73 | −struct bpf_iter__task_file { | |
| 74 | − struct bpf_iter_meta *meta; | |
| 75 | − struct task_struct *task; | |
| 76 | − __u32 fd; | |
| 77 | − struct file *file; | |
| 78 | −} __attribute__((preserve_access_index)); | |
| 79 | − | |
| 80 | −struct bpf_iter__task_vma { | |
| 81 | − struct bpf_iter_meta *meta; | |
| 82 | − struct task_struct *task; | |
| 83 | − struct vm_area_struct *vma; | |
| 84 | −} __attribute__((preserve_access_index)); | |
| 85 | − | |
| 86 | −struct bpf_iter__bpf_map { | |
| 87 | − struct bpf_iter_meta *meta; | |
| 88 | − struct bpf_map *map; | |
| 89 | −} __attribute__((preserve_access_index)); | |
| 90 | − | |
| 91 | −struct bpf_iter__tcp { | |
| 92 | − struct bpf_iter_meta *meta; | |
| 93 | − struct sock_common *sk_common; | |
| 94 | − uid_t uid; | |
| 95 | −} __attribute__((preserve_access_index)); | |
| 96 | − | |
| 97 | −struct tcp6_sock { | |
| 98 | − struct tcp_sock tcp; | |
| 99 | − struct ipv6_pinfo inet6; | |
| 100 | −} __attribute__((preserve_access_index)); | |
| 101 | − | |
| 102 | −struct bpf_iter__udp { | |
| 103 | − struct bpf_iter_meta *meta; | |
| 104 | − struct udp_sock *udp_sk; | |
| 105 | − uid_t uid __attribute__((aligned(8))); | |
| 106 | − int bucket __attribute__((aligned(8))); | |
| 107 | −} __attribute__((preserve_access_index)); | |
| 108 | − | |
| 109 | −struct udp6_sock { | |
| 110 | − struct udp_sock udp; | |
| 111 | − struct ipv6_pinfo inet6; | |
| 112 | −} __attribute__((preserve_access_index)); | |
| 113 | − | |
| 114 | −struct bpf_iter__unix { | |
| 115 | − struct bpf_iter_meta *meta; | |
| 116 | − struct unix_sock *unix_sk; | |
| 117 | − uid_t uid; | |
| 118 | −} __attribute__((preserve_access_index)); | |
| 119 | − | |
| 120 | −struct bpf_iter__bpf_map_elem { | |
| 121 | − struct bpf_iter_meta *meta; | |
| 122 | − struct bpf_map *map; | |
| 123 | − void *key; | |
| 124 | − void *value; | |
| 125 | −}; | |
| 126 | − | |
| 127 | −struct bpf_iter__bpf_sk_storage_map { | |
| 128 | − struct bpf_iter_meta *meta; | |
| 129 | − struct bpf_map *map; | |
| 130 | − struct sock *sk; | |
| 131 | − void *value; | |
| 132 | −}; | |
| 133 | − | |
| 134 | −struct bpf_iter__sockmap { | |
| 135 | − struct bpf_iter_meta *meta; | |
| 136 | − struct bpf_map *map; | |
| 137 | − void *key; | |
| 138 | − struct sock *sk; | |
| 139 | −}; | |
| 140 | − | |
| 141 | −struct bpf_iter__bpf_link { | |
| 142 | − struct bpf_iter_meta *meta; | |
| 143 | − struct bpf_link *link; | |
| 144 | −}; | |
| 145 | − | |
| 146 | −struct bpf_iter__cgroup { | |
| 147 | − struct bpf_iter_meta *meta; | |
| 148 | − struct cgroup *cgroup; | |
| 149 | −} __attribute__((preserve_access_index)); | |
| 150 | − | |
| 151 | −struct btf_ptr { | |
| 152 | − void *ptr; | |
| 153 | − __u32 type_id; | |
| 154 | − __u32 flags; | |
| 155 | −}; | |
| 156 | − | |
| 157 | −enum { | |
| 158 | − BTF_F_COMPACT = (1ULL << 0), | |
| 159 | − BTF_F_NONAME = (1ULL << 1), | |
| 160 | − BTF_F_PTR_RAW = (1ULL << 2), | |
| 161 | − BTF_F_ZERO = (1ULL << 3), | |
| 162 | −}; | |
| 163 | − | |
| 164 | −struct bpf_iter__ksym { | |
| 165 | − struct bpf_iter_meta *meta; | |
| 166 | − struct kallsym_iter *ksym; | |
| 167 | −}; | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | #include <bpf/bpf_tracing.h> |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2022 Red Hat, Inc. */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | #include <bpf/bpf_tracing.h> |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | #include <bpf/bpf_tracing.h> |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Google LLC. */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | #include <bpf/bpf_tracing.h> |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | #include <bpf/bpf_tracing.h> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2022, Oracle and/or its affiliates. */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2021 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | #include <bpf/bpf_endian.h> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright Amazon.com Inc. or its affiliates. */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | #include <limits.h> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Cloudflare */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | #include <bpf/bpf_tracing.h> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020, Oracle and/or its affiliates. */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | #include <bpf/bpf_core_read.h> |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | #include <bpf/bpf_tracing.h> |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | #include <bpf/bpf_endian.h> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | #include <bpf/bpf_endian.h> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | #include <bpf/bpf_tracing.h> |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | #include <bpf/bpf_endian.h> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2020 Facebook */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | #include <bpf/bpf_endian.h> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright Amazon.com Inc. or its affiliates. */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include "bpf_tracing_net.h" |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | #include <bpf/bpf_endian.h> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ |
| 3 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <bpf/bpf_helpers.h> |
| 5 | 5 | |
| 6 | 6 | char _license[] SEC("license") = "GPL"; |
| @@ -1,7 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2022 Google */ |
| 3 | − | |
| 4 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 5 | 4 | #include <bpf/bpf_helpers.h> |
| 6 | 5 | #include <bpf/bpf_tracing.h> |
| 7 | 6 | |
| @@ -1,7 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ |
| 3 | − | |
| 4 | −#include "bpf_iter.h" | |
| 3 | +#include <vmlinux.h> | |
| 5 | 4 | #include <bpf/bpf_helpers.h> |
| 6 | 5 | #include <bpf/bpf_tracing.h> |
| 7 | 6 | #include "bpf_misc.h" |
| @@ -131,7 +131,7 @@ int reject_subprog_with_lock(void *ctx) · 130 unmodified lines | ||
| 131 | 131 | } |
| 132 | 132 | |
| 133 | 133 | SEC("?tc") |
| 134 | −__failure __msg("bpf_rcu_read_unlock is missing") | |
| 134 | +__failure __msg("BPF_EXIT instruction cannot be used inside bpf_rcu_read_lock-ed region") | |
| 135 | 135 | int reject_with_rcu_read_lock(void *ctx) |
| 136 | 136 | { |
| 137 | 137 | bpf_rcu_read_lock(); |
| @@ -147,7 +147,7 @@ __noinline static int throwing_subprog(struct __sk_buff *ctx) · 9 unmodified lines | ||
| 147 | 147 | } |
| 148 | 148 | |
| 149 | 149 | SEC("?tc") |
| 150 | −__failure __msg("bpf_rcu_read_unlock is missing") | |
| 150 | +__failure __msg("BPF_EXIT instruction cannot be used inside bpf_rcu_read_lock-ed region") | |
| 151 | 151 | int reject_subprog_with_rcu_read_lock(void *ctx) |
| 152 | 152 | { |
| 153 | 153 | bpf_rcu_read_lock(); |
| @@ -0,0 +1,108 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* Copyright (c) 2024 Google */ | |
| 3 | +#include <vmlinux.h> | |
| 4 | +#include <bpf/bpf_helpers.h> | |
| 5 | +#include <bpf/bpf_tracing.h> | |
| 6 | +#include "bpf_experimental.h" | |
| 7 | + | |
| 8 | +char _license[] SEC("license") = "GPL"; | |
| 9 | + | |
| 10 | +#define SLAB_NAME_MAX 32 | |
| 11 | + | |
| 12 | +struct kmem_cache_result { | |
| 13 | + char name[SLAB_NAME_MAX]; | |
| 14 | + long obj_size; | |
| 15 | +}; | |
| 16 | + | |
| 17 | +struct { | |
| 18 | + __uint(type, BPF_MAP_TYPE_HASH); | |
| 19 | + __uint(key_size, sizeof(void *)); | |
| 20 | + __uint(value_size, SLAB_NAME_MAX); | |
| 21 | + __uint(max_entries, 1); | |
| 22 | +} slab_hash SEC(".maps"); | |
| 23 | + | |
| 24 | +struct { | |
| 25 | + __uint(type, BPF_MAP_TYPE_ARRAY); | |
| 26 | + __uint(key_size, sizeof(int)); | |
| 27 | + __uint(value_size, sizeof(struct kmem_cache_result)); | |
| 28 | + __uint(max_entries, 1024); | |
| 29 | +} slab_result SEC(".maps"); | |
| 30 | + | |
| 31 | +extern struct kmem_cache *bpf_get_kmem_cache(u64 addr) __ksym; | |
| 32 | + | |
| 33 | +/* Result, will be checked by userspace */ | |
| 34 | +int task_struct_found; | |
| 35 | +int kmem_cache_seen; | |
| 36 | +int open_coded_seen; | |
| 37 | + | |
| 38 | +SEC("iter/kmem_cache") | |
| 39 | +int slab_info_collector(struct bpf_iter__kmem_cache *ctx) | |
| 40 | +{ | |
| 41 | + struct seq_file *seq = ctx->meta->seq; | |
| 42 | + struct kmem_cache *s = ctx->s; | |
| 43 | + struct kmem_cache_result *r; | |
| 44 | + int idx; | |
| 45 | + | |
| 46 | + if (s) { | |
| 47 | + /* To make sure if the slab_iter implements the seq interface | |
| 48 | + * properly and it's also useful for debugging. | |
| 49 | + */ | |
| 50 | + BPF_SEQ_PRINTF(seq, "%s: %u\n", s->name, s->size); | |
| 51 | + | |
| 52 | + idx = kmem_cache_seen; | |
| 53 | + r = bpf_map_lookup_elem(&slab_result, &idx); | |
| 54 | + if (r == NULL) | |
| 55 | + return 0; | |
| 56 | + | |
| 57 | + kmem_cache_seen++; | |
| 58 | + | |
| 59 | + /* Save name and size to match /proc/slabinfo */ | |
| 60 | + bpf_probe_read_kernel_str(r->name, sizeof(r->name), s->name); | |
| 61 | + r->obj_size = s->size; | |
| 62 | + | |
| 63 | + if (!bpf_strncmp(r->name, 11, "task_struct")) | |
| 64 | + bpf_map_update_elem(&slab_hash, &s, r->name, BPF_NOEXIST); | |
| 65 | + } | |
| 66 | + | |
| 67 | + return 0; | |
| 68 | +} | |
| 69 | + | |
| 70 | +SEC("raw_tp/bpf_test_finish") | |
| 71 | +int BPF_PROG(check_task_struct) | |
| 72 | +{ | |
| 73 | + u64 curr = bpf_get_current_task(); | |
| 74 | + struct kmem_cache *s; | |
| 75 | + char *name; | |
| 76 | + | |
| 77 | + s = bpf_get_kmem_cache(curr); | |
| 78 | + if (s == NULL) { | |
| 79 | + task_struct_found = -1; | |
| 80 | + return 0; | |
| 81 | + } | |
| 82 | + name = bpf_map_lookup_elem(&slab_hash, &s); | |
| 83 | + if (name && !bpf_strncmp(name, 11, "task_struct")) | |
| 84 | + task_struct_found = 1; | |
| 85 | + else | |
| 86 | + task_struct_found = -2; | |
| 87 | + return 0; | |
| 88 | +} | |
| 89 | + | |
| 90 | +SEC("syscall") | |
| 91 | +int open_coded_iter(const void *ctx) | |
| 92 | +{ | |
| 93 | + struct kmem_cache *s; | |
| 94 | + | |
| 95 | + bpf_for_each(kmem_cache, s) { | |
| 96 | + struct kmem_cache_result *r; | |
| 97 | + | |
| 98 | + r = bpf_map_lookup_elem(&slab_result, &open_coded_seen); | |
| 99 | + if (!r) | |
| 100 | + break; | |
| 101 | + | |
| 102 | + if (r->obj_size != s->size) | |
| 103 | + break; | |
| 104 | + | |
| 105 | + open_coded_seen++; | |
| 106 | + } | |
| 107 | + return 0; | |
| 108 | +} | |
| @@ -0,0 +1,31 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +#include "vmlinux.h" | |
| 3 | +#include <bpf/bpf_helpers.h> | |
| 4 | +#include <bpf/bpf_tracing.h> | |
| 5 | +#include <bpf/usdt.bpf.h> | |
| 6 | +#include "bpf_misc.h" | |
| 7 | + | |
| 8 | +char _license[] SEC("license") = "GPL"; | |
| 9 | + | |
| 10 | + | |
| 11 | +SEC("kprobe.session") | |
| 12 | +__success | |
| 13 | +int kprobe_session_return_0(struct pt_regs *ctx) | |
| 14 | +{ | |
| 15 | + return 0; | |
| 16 | +} | |
| 17 | + | |
| 18 | +SEC("kprobe.session") | |
| 19 | +__success | |
| 20 | +int kprobe_session_return_1(struct pt_regs *ctx) | |
| 21 | +{ | |
| 22 | + return 1; | |
| 23 | +} | |
| 24 | + | |
| 25 | +SEC("kprobe.session") | |
| 26 | +__failure | |
| 27 | +__msg("At program exit the register R0 has smin=2 smax=2 should have been in [0, 1]") | |
| 28 | +int kprobe_session_return_2(struct pt_regs *ctx) | |
| 29 | +{ | |
| 30 | + return 2; | |
| 31 | +} | |
| @@ -63,6 +63,8 @@ extern int set_output_val2(int x); · 62 unmodified lines | ||
| 63 | 63 | /* here we'll force set_output_ctx2() to be __hidden in the final obj file */ |
| 64 | 64 | __hidden extern void set_output_ctx2(__u64 *ctx); |
| 65 | 65 | |
| 66 | +void *bpf_cast_to_kern_ctx(void *obj) __ksym; | |
| 67 | + | |
| 66 | 68 | SEC("?raw_tp/sys_enter") |
| 67 | 69 | int BPF_PROG(handler1, struct pt_regs *regs, long id) |
| 68 | 70 | { |
| @@ -86,4 +88,10 @@ int BPF_PROG(handler1, struct pt_regs *regs, long id) · 17 unmodified lines | ||
| 86 | 88 | return 0; |
| 87 | 89 | } |
| 88 | 90 | |
| 91 | +/* Generate BTF FUNC record and test linking with duplicate extern functions */ | |
| 92 | +void kfunc_gen1(void) | |
| 93 | +{ | |
| 94 | + bpf_cast_to_kern_ctx(0); | |
| 95 | +} | |
| 96 | + | |
| 89 | 97 | char LICENSE[] SEC("license") = "GPL"; |
| @@ -63,6 +63,8 @@ extern int set_output_val1(int x); · 62 unmodified lines | ||
| 63 | 63 | /* here we'll force set_output_ctx1() to be __hidden in the final obj file */ |
| 64 | 64 | __hidden extern void set_output_ctx1(__u64 *ctx); |
| 65 | 65 | |
| 66 | +void *bpf_cast_to_kern_ctx(void *obj) __ksym; | |
| 67 | + | |
| 66 | 68 | SEC("?raw_tp/sys_enter") |
| 67 | 69 | int BPF_PROG(handler2, struct pt_regs *regs, long id) |
| 68 | 70 | { |
| @@ -86,4 +88,10 @@ int BPF_PROG(handler2, struct pt_regs *regs, long id) · 17 unmodified lines | ||
| 86 | 88 | return 0; |
| 87 | 89 | } |
| 88 | 90 | |
| 91 | +/* Generate BTF FUNC record and test linking with duplicate extern functions */ | |
| 92 | +void kfunc_gen2(void) | |
| 93 | +{ | |
| 94 | + bpf_cast_to_kern_ctx(0); | |
| 95 | +} | |
| 96 | + | |
| 89 | 97 | char LICENSE[] SEC("license") = "GPL"; |
| @@ -6,7 +6,7 @@ · 5 unmodified lines | ||
| 6 | 6 | #include "bpf_experimental.h" |
| 7 | 7 | |
| 8 | 8 | SEC("?tc") |
| 9 | −__failure __msg("1 bpf_preempt_enable is missing") | |
| 9 | +__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region") | |
| 10 | 10 | int preempt_lock_missing_1(struct __sk_buff *ctx) |
| 11 | 11 | { |
| 12 | 12 | bpf_preempt_disable(); |
| @@ -14,7 +14,7 @@ int preempt_lock_missing_1(struct __sk_buff *ctx) · 1 unmodified line | ||
| 14 | 14 | } |
| 15 | 15 | |
| 16 | 16 | SEC("?tc") |
| 17 | −__failure __msg("2 bpf_preempt_enable(s) are missing") | |
| 17 | +__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region") | |
| 18 | 18 | int preempt_lock_missing_2(struct __sk_buff *ctx) |
| 19 | 19 | { |
| 20 | 20 | bpf_preempt_disable(); |
| @@ -23,7 +23,7 @@ int preempt_lock_missing_2(struct __sk_buff *ctx) · 2 unmodified lines | ||
| 23 | 23 | } |
| 24 | 24 | |
| 25 | 25 | SEC("?tc") |
| 26 | −__failure __msg("3 bpf_preempt_enable(s) are missing") | |
| 26 | +__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region") | |
| 27 | 27 | int preempt_lock_missing_3(struct __sk_buff *ctx) |
| 28 | 28 | { |
| 29 | 29 | bpf_preempt_disable(); |
| @@ -33,7 +33,7 @@ int preempt_lock_missing_3(struct __sk_buff *ctx) · 3 unmodified lines | ||
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | SEC("?tc") |
| 36 | −__failure __msg("1 bpf_preempt_enable is missing") | |
| 36 | +__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region") | |
| 37 | 37 | int preempt_lock_missing_3_minus_2(struct __sk_buff *ctx) |
| 38 | 38 | { |
| 39 | 39 | bpf_preempt_disable(); |
| @@ -55,7 +55,7 @@ static __noinline void preempt_enable(void) · 15 unmodified lines | ||
| 55 | 55 | } |
| 56 | 56 | |
| 57 | 57 | SEC("?tc") |
| 58 | −__failure __msg("1 bpf_preempt_enable is missing") | |
| 58 | +__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region") | |
| 59 | 59 | int preempt_lock_missing_1_subprog(struct __sk_buff *ctx) |
| 60 | 60 | { |
| 61 | 61 | preempt_disable(); |
| @@ -63,7 +63,7 @@ int preempt_lock_missing_1_subprog(struct __sk_buff *ctx) · 1 unmodified line | ||
| 63 | 63 | } |
| 64 | 64 | |
| 65 | 65 | SEC("?tc") |
| 66 | −__failure __msg("2 bpf_preempt_enable(s) are missing") | |
| 66 | +__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region") | |
| 67 | 67 | int preempt_lock_missing_2_subprog(struct __sk_buff *ctx) |
| 68 | 68 | { |
| 69 | 69 | preempt_disable(); |
| @@ -72,7 +72,7 @@ int preempt_lock_missing_2_subprog(struct __sk_buff *ctx) · 2 unmodified lines | ||
| 72 | 72 | } |
| 73 | 73 | |
| 74 | 74 | SEC("?tc") |
| 75 | −__failure __msg("1 bpf_preempt_enable is missing") | |
| 75 | +__failure __msg("BPF_EXIT instruction cannot be used inside bpf_preempt_disable-ed region") | |
| 76 | 76 | int preempt_lock_missing_2_minus_1_subprog(struct __sk_buff *ctx) |
| 77 | 77 | { |
| 78 | 78 | preempt_disable(); |
| @@ -0,0 +1,32 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ | |
| 3 | + | |
| 4 | +#include <vmlinux.h> | |
| 5 | +#include <bpf/bpf_tracing.h> | |
| 6 | + | |
| 7 | +char _license[] SEC("license") = "GPL"; | |
| 8 | + | |
| 9 | +int tid; | |
| 10 | +int i; | |
| 11 | + | |
| 12 | +SEC("tp_btf/bpf_testmod_test_raw_tp_null") | |
| 13 | +int BPF_PROG(test_raw_tp_null, struct sk_buff *skb) | |
| 14 | +{ | |
| 15 | + struct task_struct *task = bpf_get_current_task_btf(); | |
| 16 | + | |
| 17 | + if (task->pid != tid) | |
| 18 | + return 0; | |
| 19 | + | |
| 20 | + i = i + skb->mark + 1; | |
| 21 | + /* The compiler may move the NULL check before this deref, which causes | |
| 22 | + * the load to fail as deref of scalar. Prevent that by using a barrier. | |
| 23 | + */ | |
| 24 | + barrier(); | |
| 25 | + /* If dead code elimination kicks in, the increment below will | |
| 26 | + * be removed. For raw_tp programs, we mark input arguments as | |
| 27 | + * PTR_MAYBE_NULL, so branch prediction should never kick in. | |
| 28 | + */ | |
| 29 | + if (!skb) | |
| 30 | + i += 2; | |
| 31 | + return 0; | |
| 32 | +} | |
| @@ -6,5 +6,17 @@ · 5 unmodified lines | ||
| 6 | 6 | |
| 7 | 7 | char _license[] SEC("license") = "GPL"; |
| 8 | 8 | |
| 9 | +/* | |
| 10 | + * This subprogram validates that libbpf handles the situation in which BPF | |
| 11 | + * object has subprograms in .text section, but has no entry BPF programs. | |
| 12 | + * At some point that was causing issues due to legacy logic of treating such | |
| 13 | + * subprogram as entry program (with unknown program type, which would fail). | |
| 14 | + */ | |
| 15 | +int dangling_subprog(void) | |
| 16 | +{ | |
| 17 | + /* do nothing, just be here */ | |
| 18 | + return 0; | |
| 19 | +} | |
| 20 | + | |
| 9 | 21 | SEC(".struct_ops.link") |
| 10 | 22 | struct bpf_testmod_ops testmod_do_detach; |
| @@ -0,0 +1,62 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +#include <vmlinux.h> | |
| 4 | +#include <bpf/bpf_helpers.h> | |
| 5 | +#include <bpf/bpf_tracing.h> | |
| 6 | +#include "../bpf_testmod/bpf_testmod.h" | |
| 7 | + | |
| 8 | +char _license[] SEC("license") = "GPL"; | |
| 9 | + | |
| 10 | +#if defined(__TARGET_ARCH_x86) | |
| 11 | +bool skip __attribute((__section__(".data"))) = false; | |
| 12 | +#else | |
| 13 | +bool skip = true; | |
| 14 | +#endif | |
| 15 | + | |
| 16 | +void bpf_testmod_ops3_call_test_2(void) __ksym; | |
| 17 | + | |
| 18 | +int val_i, val_j; | |
| 19 | + | |
| 20 | +__noinline static int subprog2(int *a, int *b) | |
| 21 | +{ | |
| 22 | + return val_i + a[10] + b[20]; | |
| 23 | +} | |
| 24 | + | |
| 25 | +__noinline static int subprog1(int *a) | |
| 26 | +{ | |
| 27 | + /* stack size 200 bytes */ | |
| 28 | + int b[50] = {}; | |
| 29 | + | |
| 30 | + b[20] = 2; | |
| 31 | + return subprog2(a, b); | |
| 32 | +} | |
| 33 | + | |
| 34 | + | |
| 35 | +SEC("struct_ops") | |
| 36 | +int BPF_PROG(test_1) | |
| 37 | +{ | |
| 38 | + /* stack size 400 bytes */ | |
| 39 | + int a[100] = {}; | |
| 40 | + | |
| 41 | + a[10] = 1; | |
| 42 | + val_i = subprog1(a); | |
| 43 | + bpf_testmod_ops3_call_test_2(); | |
| 44 | + return 0; | |
| 45 | +} | |
| 46 | + | |
| 47 | +SEC("struct_ops") | |
| 48 | +int BPF_PROG(test_2) | |
| 49 | +{ | |
| 50 | + /* stack size 200 bytes */ | |
| 51 | + int a[50] = {}; | |
| 52 | + | |
| 53 | + a[10] = 3; | |
| 54 | + val_j = subprog1(a); | |
| 55 | + return 0; | |
| 56 | +} | |
| 57 | + | |
| 58 | +SEC(".struct_ops") | |
| 59 | +struct bpf_testmod_ops3 testmod_1 = { | |
| 60 | + .test_1 = (void *)test_1, | |
| 61 | + .test_2 = (void *)test_2, | |
| 62 | +}; | |
| @@ -0,0 +1,62 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +#include <vmlinux.h> | |
| 4 | +#include <bpf/bpf_helpers.h> | |
| 5 | +#include <bpf/bpf_tracing.h> | |
| 6 | +#include "../bpf_testmod/bpf_testmod.h" | |
| 7 | + | |
| 8 | +char _license[] SEC("license") = "GPL"; | |
| 9 | + | |
| 10 | +#if defined(__TARGET_ARCH_x86) | |
| 11 | +bool skip __attribute((__section__(".data"))) = false; | |
| 12 | +#else | |
| 13 | +bool skip = true; | |
| 14 | +#endif | |
| 15 | + | |
| 16 | +void bpf_testmod_ops3_call_test_2(void) __ksym; | |
| 17 | + | |
| 18 | +int val_i, val_j; | |
| 19 | + | |
| 20 | +__noinline static int subprog2(int *a, int *b) | |
| 21 | +{ | |
| 22 | + return val_i + a[10] + b[20]; | |
| 23 | +} | |
| 24 | + | |
| 25 | +__noinline static int subprog1(int *a) | |
| 26 | +{ | |
| 27 | + /* stack size 200 bytes */ | |
| 28 | + int b[50] = {}; | |
| 29 | + | |
| 30 | + b[20] = 2; | |
| 31 | + return subprog2(a, b); | |
| 32 | +} | |
| 33 | + | |
| 34 | + | |
| 35 | +SEC("struct_ops") | |
| 36 | +int BPF_PROG(test_1) | |
| 37 | +{ | |
| 38 | + /* stack size 100 bytes */ | |
| 39 | + int a[25] = {}; | |
| 40 | + | |
| 41 | + a[10] = 1; | |
| 42 | + val_i = subprog1(a); | |
| 43 | + bpf_testmod_ops3_call_test_2(); | |
| 44 | + return 0; | |
| 45 | +} | |
| 46 | + | |
| 47 | +SEC("struct_ops") | |
| 48 | +int BPF_PROG(test_2) | |
| 49 | +{ | |
| 50 | + /* stack size 400 bytes */ | |
| 51 | + int a[100] = {}; | |
| 52 | + | |
| 53 | + a[10] = 3; | |
| 54 | + val_j = subprog1(a); | |
| 55 | + return 0; | |
| 56 | +} | |
| 57 | + | |
| 58 | +SEC(".struct_ops") | |
| 59 | +struct bpf_testmod_ops3 testmod_1 = { | |
| 60 | + .test_1 = (void *)test_1, | |
| 61 | + .test_2 = (void *)test_2, | |
| 62 | +}; | |
| @@ -0,0 +1,50 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +#include <vmlinux.h> | |
| 4 | +#include <bpf/bpf_helpers.h> | |
| 5 | +#include <bpf/bpf_tracing.h> | |
| 6 | +#include "../bpf_testmod/bpf_testmod.h" | |
| 7 | + | |
| 8 | +char _license[] SEC("license") = "GPL"; | |
| 9 | + | |
| 10 | +#if defined(__TARGET_ARCH_x86) | |
| 11 | +bool skip __attribute((__section__(".data"))) = false; | |
| 12 | +#else | |
| 13 | +bool skip = true; | |
| 14 | +#endif | |
| 15 | + | |
| 16 | +void bpf_testmod_ops3_call_test_1(void) __ksym; | |
| 17 | + | |
| 18 | +int val_i, val_j; | |
| 19 | + | |
| 20 | +__noinline static int subprog2(int *a, int *b) | |
| 21 | +{ | |
| 22 | + return val_i + a[1] + b[20]; | |
| 23 | +} | |
| 24 | + | |
| 25 | +__noinline static int subprog1(int *a) | |
| 26 | +{ | |
| 27 | + /* stack size 400 bytes */ | |
| 28 | + int b[100] = {}; | |
| 29 | + | |
| 30 | + b[20] = 2; | |
| 31 | + return subprog2(a, b); | |
| 32 | +} | |
| 33 | + | |
| 34 | + | |
| 35 | +SEC("struct_ops") | |
| 36 | +int BPF_PROG(test_1) | |
| 37 | +{ | |
| 38 | + /* stack size 20 bytes */ | |
| 39 | + int a[5] = {}; | |
| 40 | + | |
| 41 | + a[1] = 1; | |
| 42 | + val_j += subprog1(a); | |
| 43 | + bpf_testmod_ops3_call_test_1(); | |
| 44 | + return 0; | |
| 45 | +} | |
| 46 | + | |
| 47 | +SEC(".struct_ops") | |
| 48 | +struct bpf_testmod_ops3 testmod_1 = { | |
| 49 | + .test_1 = (void *)test_1, | |
| 50 | +}; | |
| @@ -0,0 +1,64 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ | |
| 3 | +#include <vmlinux.h> | |
| 4 | +#include <bpf/bpf_tracing.h> | |
| 5 | +#include <bpf/bpf_helpers.h> | |
| 6 | +#include <bpf/bpf_core_read.h> | |
| 7 | + | |
| 8 | +#include "bpf_misc.h" | |
| 9 | +#include "bpf_experimental.h" | |
| 10 | + | |
| 11 | +extern void bpf_rcu_read_lock(void) __ksym; | |
| 12 | +extern void bpf_rcu_read_unlock(void) __ksym; | |
| 13 | + | |
| 14 | +#define private(name) SEC(".bss." #name) __hidden __attribute__((aligned(8))) | |
| 15 | + | |
| 16 | +private(A) struct bpf_spin_lock lock; | |
| 17 | + | |
| 18 | +struct { | |
| 19 | + __uint(type, BPF_MAP_TYPE_PROG_ARRAY); | |
| 20 | + __uint(max_entries, 3); | |
| 21 | + __uint(key_size, sizeof(__u32)); | |
| 22 | + __uint(value_size, sizeof(__u32)); | |
| 23 | +} jmp_table SEC(".maps"); | |
| 24 | + | |
| 25 | +SEC("?tc") | |
| 26 | +__failure __msg("function calls are not allowed while holding a lock") | |
| 27 | +int reject_tail_call_spin_lock(struct __sk_buff *ctx) | |
| 28 | +{ | |
| 29 | + bpf_spin_lock(&lock); | |
| 30 | + bpf_tail_call_static(ctx, &jmp_table, 0); | |
| 31 | + return 0; | |
| 32 | +} | |
| 33 | + | |
| 34 | +SEC("?tc") | |
| 35 | +__failure __msg("tail_call cannot be used inside bpf_rcu_read_lock-ed region") | |
| 36 | +int reject_tail_call_rcu_lock(struct __sk_buff *ctx) | |
| 37 | +{ | |
| 38 | + bpf_rcu_read_lock(); | |
| 39 | + bpf_tail_call_static(ctx, &jmp_table, 0); | |
| 40 | + bpf_rcu_read_unlock(); | |
| 41 | + return 0; | |
| 42 | +} | |
| 43 | + | |
| 44 | +SEC("?tc") | |
| 45 | +__failure __msg("tail_call cannot be used inside bpf_preempt_disable-ed region") | |
| 46 | +int reject_tail_call_preempt_lock(struct __sk_buff *ctx) | |
| 47 | +{ | |
| 48 | + bpf_guard_preempt(); | |
| 49 | + bpf_tail_call_static(ctx, &jmp_table, 0); | |
| 50 | + return 0; | |
| 51 | +} | |
| 52 | + | |
| 53 | +SEC("?tc") | |
| 54 | +__failure __msg("tail_call would lead to reference leak") | |
| 55 | +int reject_tail_call_ref(struct __sk_buff *ctx) | |
| 56 | +{ | |
| 57 | + struct foo { int i; } *p; | |
| 58 | + | |
| 59 | + p = bpf_obj_new(typeof(*p)); | |
| 60 | + bpf_tail_call_static(ctx, &jmp_table, 0); | |
| 61 | + return 0; | |
| 62 | +} | |
| 63 | + | |
| 64 | +char _license[] SEC("license") = "GPL"; | |
| @@ -23,6 +23,7 @@ struct { · 22 unmodified lines | ||
| 23 | 23 | struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym; |
| 24 | 24 | void bpf_task_release(struct task_struct *p) __ksym; |
| 25 | 25 | struct task_struct *bpf_task_from_pid(s32 pid) __ksym; |
| 26 | +struct task_struct *bpf_task_from_vpid(s32 vpid) __ksym; | |
| 26 | 27 | void bpf_rcu_read_lock(void) __ksym; |
| 27 | 28 | void bpf_rcu_read_unlock(void) __ksym; |
| 28 | 29 | |
| @@ -247,6 +247,20 @@ int BPF_PROG(task_kfunc_from_pid_no_null_check, struct task_struct *task, u64 cl · 246 unmodified lines | ||
| 247 | 247 | return 0; |
| 248 | 248 | } |
| 249 | 249 | |
| 250 | +SEC("tp_btf/task_newtask") | |
| 251 | +__failure __msg("Possibly NULL pointer passed to trusted arg0") | |
| 252 | +int BPF_PROG(task_kfunc_from_vpid_no_null_check, struct task_struct *task, u64 clone_flags) | |
| 253 | +{ | |
| 254 | + struct task_struct *acquired; | |
| 255 | + | |
| 256 | + acquired = bpf_task_from_vpid(task->pid); | |
| 257 | + | |
| 258 | + /* Releasing bpf_task_from_vpid() lookup without a NULL check. */ | |
| 259 | + bpf_task_release(acquired); | |
| 260 | + | |
| 261 | + return 0; | |
| 262 | +} | |
| 263 | + | |
| 250 | 264 | SEC("lsm/task_free") |
| 251 | 265 | __failure __msg("R1 must be a rcu pointer") |
| 252 | 266 | int BPF_PROG(task_kfunc_from_lsm_task_free, struct task_struct *task) |
| @@ -366,3 +366,54 @@ int BPF_PROG(task_kfunc_acquire_trusted_walked, struct task_struct *task, u64 cl · 365 unmodified lines | ||
| 366 | 366 | |
| 367 | 367 | return 0; |
| 368 | 368 | } |
| 369 | + | |
| 370 | +SEC("syscall") | |
| 371 | +int test_task_from_vpid_current(const void *ctx) | |
| 372 | +{ | |
| 373 | + struct task_struct *current, *v_task; | |
| 374 | + | |
| 375 | + v_task = bpf_task_from_vpid(1); | |
| 376 | + if (!v_task) { | |
| 377 | + err = 1; | |
| 378 | + return 0; | |
| 379 | + } | |
| 380 | + | |
| 381 | + current = bpf_get_current_task_btf(); | |
| 382 | + | |
| 383 | + /* The current process should be the init process (pid 1) in the new pid namespace. */ | |
| 384 | + if (current != v_task) | |
| 385 | + err = 2; | |
| 386 | + | |
| 387 | + bpf_task_release(v_task); | |
| 388 | + return 0; | |
| 389 | +} | |
| 390 | + | |
| 391 | +SEC("syscall") | |
| 392 | +int test_task_from_vpid_invalid(const void *ctx) | |
| 393 | +{ | |
| 394 | + struct task_struct *v_task; | |
| 395 | + | |
| 396 | + v_task = bpf_task_from_vpid(-1); | |
| 397 | + if (v_task) { | |
| 398 | + err = 1; | |
| 399 | + goto err; | |
| 400 | + } | |
| 401 | + | |
| 402 | + /* There should be only one process (current process) in the new pid namespace. */ | |
| 403 | + v_task = bpf_task_from_vpid(2); | |
| 404 | + if (v_task) { | |
| 405 | + err = 2; | |
| 406 | + goto err; | |
| 407 | + } | |
| 408 | + | |
| 409 | + v_task = bpf_task_from_vpid(9999); | |
| 410 | + if (v_task) { | |
| 411 | + err = 3; | |
| 412 | + goto err; | |
| 413 | + } | |
| 414 | + | |
| 415 | + return 0; | |
| 416 | +err: | |
| 417 | + bpf_task_release(v_task); | |
| 418 | + return 0; | |
| 419 | +} | |
| @@ -0,0 +1,63 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ | |
| 3 | + | |
| 4 | +#include <vmlinux.h> | |
| 5 | +#include <bpf/bpf_helpers.h> | |
| 6 | +#include "uptr_test_common.h" | |
| 7 | + | |
| 8 | +struct task_struct *bpf_task_from_pid(s32 pid) __ksym; | |
| 9 | +void bpf_task_release(struct task_struct *p) __ksym; | |
| 10 | +void bpf_cgroup_release(struct cgroup *cgrp) __ksym; | |
| 11 | + | |
| 12 | +struct { | |
| 13 | + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); | |
| 14 | + __uint(map_flags, BPF_F_NO_PREALLOC); | |
| 15 | + __type(key, int); | |
| 16 | + __type(value, struct value_type); | |
| 17 | +} datamap SEC(".maps"); | |
| 18 | + | |
| 19 | +pid_t target_pid = 0; | |
| 20 | +pid_t parent_pid = 0; | |
| 21 | + | |
| 22 | +SEC("tp_btf/sys_enter") | |
| 23 | +int on_enter(__u64 *ctx) | |
| 24 | +{ | |
| 25 | + struct task_struct *task, *data_task; | |
| 26 | + struct value_type *ptr; | |
| 27 | + struct user_data *udata; | |
| 28 | + struct cgroup *cgrp; | |
| 29 | + | |
| 30 | + task = bpf_get_current_task_btf(); | |
| 31 | + if (task->pid != target_pid) | |
| 32 | + return 0; | |
| 33 | + | |
| 34 | + data_task = bpf_task_from_pid(parent_pid); | |
| 35 | + if (!data_task) | |
| 36 | + return 0; | |
| 37 | + | |
| 38 | + ptr = bpf_task_storage_get(&datamap, data_task, 0, 0); | |
| 39 | + bpf_task_release(data_task); | |
| 40 | + if (!ptr) | |
| 41 | + return 0; | |
| 42 | + | |
| 43 | + cgrp = bpf_kptr_xchg(&ptr->cgrp, NULL); | |
| 44 | + if (cgrp) { | |
| 45 | + int lvl = cgrp->level; | |
| 46 | + | |
| 47 | + bpf_cgroup_release(cgrp); | |
| 48 | + return lvl; | |
| 49 | + } | |
| 50 | + | |
| 51 | + udata = ptr->udata; | |
| 52 | + if (!udata || udata->result) | |
| 53 | + return 0; | |
| 54 | + udata->result = MAGIC_VALUE + udata->a + udata->b; | |
| 55 | + | |
| 56 | + udata = ptr->nested.udata; | |
| 57 | + if (udata && !udata->nested_result) | |
| 58 | + udata->nested_result = udata->result; | |
| 59 | + | |
| 60 | + return 0; | |
| 61 | +} | |
| 62 | + | |
| 63 | +char _license[] SEC("license") = "GPL"; | |
| @@ -5,10 +5,11 @@ · 4 unmodified lines | ||
| 5 | 5 | #include "bpf_misc.h" |
| 6 | 6 | |
| 7 | 7 | __noinline |
| 8 | −int subprog(struct __sk_buff *skb) | |
| 8 | +int subprog_tc(struct __sk_buff *skb) | |
| 9 | 9 | { |
| 10 | 10 | int ret = 1; |
| 11 | 11 | |
| 12 | + __sink(skb); | |
| 12 | 13 | __sink(ret); |
| 13 | 14 | return ret; |
| 14 | 15 | } |
| @@ -16,7 +17,7 @@ int subprog(struct __sk_buff *skb) · 1 unmodified line | ||
| 16 | 17 | SEC("tc") |
| 17 | 18 | int entry_tc(struct __sk_buff *skb) |
| 18 | 19 | { |
| 19 | − return subprog(skb); | |
| 20 | + return subprog_tc(skb); | |
| 20 | 21 | } |
| 21 | 22 | |
| 22 | 23 | char __license[] SEC("license") = "GPL"; |
| @@ -1,27 +1,50 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | // Copyright (c) 2019 Facebook |
| 3 | −#include <linux/bpf.h> | |
| 3 | +#include <vmlinux.h> | |
| 4 | 4 | #include <linux/version.h> |
| 5 | 5 | #include <bpf/bpf_helpers.h> |
| 6 | 6 | |
| 7 | −__u32 sig = 0, pid = 0, status = 0, signal_thread = 0; | |
| 7 | +struct task_struct *bpf_task_from_pid(int pid) __ksym; | |
| 8 | +void bpf_task_release(struct task_struct *p) __ksym; | |
| 9 | +int bpf_send_signal_task(struct task_struct *task, int sig, enum pid_type type, u64 value) __ksym; | |
| 10 | + | |
| 11 | +__u32 sig = 0, pid = 0, status = 0, signal_thread = 0, target_pid = 0; | |
| 8 | 12 | |
| 9 | 13 | static __always_inline int bpf_send_signal_test(void *ctx) |
| 10 | 14 | { |
| 15 | + struct task_struct *target_task = NULL; | |
| 11 | 16 | int ret; |
| 17 | + u64 value; | |
| 12 | 18 | |
| 13 | 19 | if (status != 0 || pid == 0) |
| 14 | 20 | return 0; |
| 15 | 21 | |
| 16 | 22 | if ((bpf_get_current_pid_tgid() >> 32) == pid) { |
| 17 | − if (signal_thread) | |
| 18 | − ret = bpf_send_signal_thread(sig); | |
| 19 | − else | |
| 20 | − ret = bpf_send_signal(sig); | |
| 23 | + if (target_pid) { | |
| 24 | + target_task = bpf_task_from_pid(target_pid); | |
| 25 | + if (!target_task) | |
| 26 | + return 0; | |
| 27 | + value = 8; | |
| 28 | + } | |
| 29 | + | |
| 30 | + if (signal_thread) { | |
| 31 | + if (target_pid) | |
| 32 | + ret = bpf_send_signal_task(target_task, sig, PIDTYPE_PID, value); | |
| 33 | + else | |
| 34 | + ret = bpf_send_signal_thread(sig); | |
| 35 | + } else { | |
| 36 | + if (target_pid) | |
| 37 | + ret = bpf_send_signal_task(target_task, sig, PIDTYPE_TGID, value); | |
| 38 | + else | |
| 39 | + ret = bpf_send_signal(sig); | |
| 40 | + } | |
| 21 | 41 | if (ret == 0) |
| 22 | 42 | status = 1; |
| 23 | 43 | } |
| 24 | 44 | |
| 45 | + if (target_task) | |
| 46 | + bpf_task_release(target_task); | |
| 47 | + | |
| 25 | 48 | return 0; |
| 26 | 49 | } |
| 27 | 50 | |
| @@ -28,8 +28,8 @@ struct { · 27 unmodified lines | ||
| 28 | 28 | }, |
| 29 | 29 | }; |
| 30 | 30 | |
| 31 | −SEC(".data.A") struct bpf_spin_lock lockA; | |
| 32 | −SEC(".data.B") struct bpf_spin_lock lockB; | |
| 31 | +static struct bpf_spin_lock lockA SEC(".data.A"); | |
| 32 | +static struct bpf_spin_lock lockB SEC(".data.B"); | |
| 33 | 33 | |
| 34 | 34 | SEC("?tc") |
| 35 | 35 | int lock_id_kptr_preserve(void *ctx) |
| @@ -7,7 +7,11 @@ · 6 unmodified lines | ||
| 7 | 7 | #include "bpf_misc.h" |
| 8 | 8 | |
| 9 | 9 | SEC("tp_btf/bpf_testmod_test_nullable_bare") |
| 10 | −__failure __msg("R1 invalid mem access 'trusted_ptr_or_null_'") | |
| 10 | +/* This used to be a failure test, but raw_tp nullable arguments can now | |
| 11 | + * directly be dereferenced, whether they have nullable annotation or not, | |
| 12 | + * and don't need to be explicitly checked. | |
| 13 | + */ | |
| 14 | +__success | |
| 11 | 15 | int BPF_PROG(handle_tp_btf_nullable_bare1, struct bpf_testmod_test_read_ctx *nullable_ctx) |
| 12 | 16 | { |
| 13 | 17 | return nullable_ctx->len; |
| @@ -0,0 +1,30 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* Copyright (C) 2024. Huawei Technologies Co., Ltd */ | |
| 3 | +#include <linux/bpf.h> | |
| 4 | +#include <bpf/bpf_helpers.h> | |
| 5 | + | |
| 6 | +struct inner_map_type { | |
| 7 | + __uint(type, BPF_MAP_TYPE_ARRAY); | |
| 8 | + __uint(key_size, 4); | |
| 9 | + __uint(value_size, 4); | |
| 10 | + __uint(max_entries, 1); | |
| 11 | +} inner_map SEC(".maps"); | |
| 12 | + | |
| 13 | +struct { | |
| 14 | + __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS); | |
| 15 | + __type(key, int); | |
| 16 | + __type(value, int); | |
| 17 | + __uint(max_entries, 2); | |
| 18 | + __array(values, struct inner_map_type); | |
| 19 | +} outer_htab_map SEC(".maps"); | |
| 20 | + | |
| 21 | +struct { | |
| 22 | + __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS); | |
| 23 | + __uint(map_flags, BPF_F_NO_PREALLOC); | |
| 24 | + __type(key, int); | |
| 25 | + __type(value, int); | |
| 26 | + __uint(max_entries, 2); | |
| 27 | + __array(values, struct inner_map_type); | |
| 28 | +} outer_alloc_htab_map SEC(".maps"); | |
| 29 | + | |
| 30 | +char _license[] SEC("license") = "GPL"; | |
| @@ -24,16 +24,16 @@ int uprobe_1(struct pt_regs *ctx) · 23 unmodified lines | ||
| 24 | 24 | return 0; |
| 25 | 25 | } |
| 26 | 26 | |
| 27 | −SEC("uprobe.multi") | |
| 27 | +SEC("uprobe.session") | |
| 28 | 28 | int uprobe_2(struct pt_regs *ctx) |
| 29 | 29 | { |
| 30 | 30 | uprobe_result[2]++; |
| 31 | 31 | return 0; |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | −SEC("uprobe.multi") | |
| 34 | +SEC("uprobe.session") | |
| 35 | 35 | int uprobe_3(struct pt_regs *ctx) |
| 36 | 36 | { |
| 37 | 37 | uprobe_result[3]++; |
| 38 | − return 0; | |
| 38 | + return 1; | |
| 39 | 39 | } |
| @@ -0,0 +1,71 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +#include <linux/bpf.h> | |
| 3 | +#include <bpf/bpf_helpers.h> | |
| 4 | +#include <bpf/bpf_tracing.h> | |
| 5 | +#include <stdbool.h> | |
| 6 | +#include "bpf_kfuncs.h" | |
| 7 | +#include "bpf_misc.h" | |
| 8 | + | |
| 9 | +char _license[] SEC("license") = "GPL"; | |
| 10 | + | |
| 11 | +__u64 uprobe_multi_func_1_addr = 0; | |
| 12 | +__u64 uprobe_multi_func_2_addr = 0; | |
| 13 | +__u64 uprobe_multi_func_3_addr = 0; | |
| 14 | + | |
| 15 | +__u64 uprobe_session_result[3] = {}; | |
| 16 | +__u64 uprobe_multi_sleep_result = 0; | |
| 17 | + | |
| 18 | +void *user_ptr = 0; | |
| 19 | +int pid = 0; | |
| 20 | + | |
| 21 | +static int uprobe_multi_check(void *ctx, bool is_return) | |
| 22 | +{ | |
| 23 | + const __u64 funcs[] = { | |
| 24 | + uprobe_multi_func_1_addr, | |
| 25 | + uprobe_multi_func_2_addr, | |
| 26 | + uprobe_multi_func_3_addr, | |
| 27 | + }; | |
| 28 | + unsigned int i; | |
| 29 | + __u64 addr; | |
| 30 | + | |
| 31 | + if (bpf_get_current_pid_tgid() >> 32 != pid) | |
| 32 | + return 1; | |
| 33 | + | |
| 34 | + addr = bpf_get_func_ip(ctx); | |
| 35 | + | |
| 36 | + for (i = 0; i < ARRAY_SIZE(funcs); i++) { | |
| 37 | + if (funcs[i] == addr) { | |
| 38 | + uprobe_session_result[i]++; | |
| 39 | + break; | |
| 40 | + } | |
| 41 | + } | |
| 42 | + | |
| 43 | + /* only uprobe_multi_func_2 executes return probe */ | |
| 44 | + if ((addr == uprobe_multi_func_1_addr) || | |
| 45 | + (addr == uprobe_multi_func_3_addr)) | |
| 46 | + return 1; | |
| 47 | + | |
| 48 | + return 0; | |
| 49 | +} | |
| 50 | + | |
| 51 | +SEC("uprobe.session//proc/self/exe:uprobe_multi_func_*") | |
| 52 | +int uprobe(struct pt_regs *ctx) | |
| 53 | +{ | |
| 54 | + return uprobe_multi_check(ctx, bpf_session_is_return()); | |
| 55 | +} | |
| 56 | + | |
| 57 | +static __always_inline bool verify_sleepable_user_copy(void) | |
| 58 | +{ | |
| 59 | + char data[9]; | |
| 60 | + | |
| 61 | + bpf_copy_from_user(data, sizeof(data), user_ptr); | |
| 62 | + return bpf_strncmp(data, sizeof(data), "test_data") == 0; | |
| 63 | +} | |
| 64 | + | |
| 65 | +SEC("uprobe.session.s//proc/self/exe:uprobe_multi_func_*") | |
| 66 | +int uprobe_sleepable(struct pt_regs *ctx) | |
| 67 | +{ | |
| 68 | + if (verify_sleepable_user_copy()) | |
| 69 | + uprobe_multi_sleep_result++; | |
| 70 | + return uprobe_multi_check(ctx, bpf_session_is_return()); | |
| 71 | +} | |
| @@ -0,0 +1,48 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +#include <linux/bpf.h> | |
| 3 | +#include <bpf/bpf_helpers.h> | |
| 4 | +#include <bpf/bpf_tracing.h> | |
| 5 | +#include <stdbool.h> | |
| 6 | +#include "bpf_kfuncs.h" | |
| 7 | + | |
| 8 | +char _license[] SEC("license") = "GPL"; | |
| 9 | + | |
| 10 | +int pid = 0; | |
| 11 | + | |
| 12 | +__u64 test_uprobe_1_result = 0; | |
| 13 | +__u64 test_uprobe_2_result = 0; | |
| 14 | +__u64 test_uprobe_3_result = 0; | |
| 15 | + | |
| 16 | +static int check_cookie(__u64 val, __u64 *result) | |
| 17 | +{ | |
| 18 | + __u64 *cookie; | |
| 19 | + | |
| 20 | + if (bpf_get_current_pid_tgid() >> 32 != pid) | |
| 21 | + return 1; | |
| 22 | + | |
| 23 | + cookie = bpf_session_cookie(); | |
| 24 | + | |
| 25 | + if (bpf_session_is_return()) | |
| 26 | + *result = *cookie == val ? val : 0; | |
| 27 | + else | |
| 28 | + *cookie = val; | |
| 29 | + return 0; | |
| 30 | +} | |
| 31 | + | |
| 32 | +SEC("uprobe.session//proc/self/exe:uprobe_multi_func_1") | |
| 33 | +int uprobe_1(struct pt_regs *ctx) | |
| 34 | +{ | |
| 35 | + return check_cookie(1, &test_uprobe_1_result); | |
| 36 | +} | |
| 37 | + | |
| 38 | +SEC("uprobe.session//proc/self/exe:uprobe_multi_func_2") | |
| 39 | +int uprobe_2(struct pt_regs *ctx) | |
| 40 | +{ | |
| 41 | + return check_cookie(2, &test_uprobe_2_result); | |
| 42 | +} | |
| 43 | + | |
| 44 | +SEC("uprobe.session//proc/self/exe:uprobe_multi_func_3") | |
| 45 | +int uprobe_3(struct pt_regs *ctx) | |
| 46 | +{ | |
| 47 | + return check_cookie(3, &test_uprobe_3_result); | |
| 48 | +} | |
| @@ -0,0 +1,44 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +#include <linux/bpf.h> | |
| 3 | +#include <bpf/bpf_helpers.h> | |
| 4 | +#include <bpf/bpf_tracing.h> | |
| 5 | +#include <stdbool.h> | |
| 6 | +#include "bpf_kfuncs.h" | |
| 7 | +#include "bpf_misc.h" | |
| 8 | + | |
| 9 | +char _license[] SEC("license") = "GPL"; | |
| 10 | + | |
| 11 | +int pid = 0; | |
| 12 | + | |
| 13 | +int idx_entry = 0; | |
| 14 | +int idx_return = 0; | |
| 15 | + | |
| 16 | +__u64 test_uprobe_cookie_entry[6]; | |
| 17 | +__u64 test_uprobe_cookie_return[3]; | |
| 18 | + | |
| 19 | +static int check_cookie(void) | |
| 20 | +{ | |
| 21 | + __u64 *cookie = bpf_session_cookie(); | |
| 22 | + | |
| 23 | + if (bpf_session_is_return()) { | |
| 24 | + if (idx_return >= ARRAY_SIZE(test_uprobe_cookie_return)) | |
| 25 | + return 1; | |
| 26 | + test_uprobe_cookie_return[idx_return++] = *cookie; | |
| 27 | + return 0; | |
| 28 | + } | |
| 29 | + | |
| 30 | + if (idx_entry >= ARRAY_SIZE(test_uprobe_cookie_entry)) | |
| 31 | + return 1; | |
| 32 | + *cookie = test_uprobe_cookie_entry[idx_entry]; | |
| 33 | + return idx_entry++ % 2; | |
| 34 | +} | |
| 35 | + | |
| 36 | + | |
| 37 | +SEC("uprobe.session//proc/self/exe:uprobe_session_recursive") | |
| 38 | +int uprobe_recursive(struct pt_regs *ctx) | |
| 39 | +{ | |
| 40 | + if (bpf_get_current_pid_tgid() >> 32 != pid) | |
| 41 | + return 1; | |
| 42 | + | |
| 43 | + return check_cookie(); | |
| 44 | +} | |
| @@ -0,0 +1,44 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +#include <linux/bpf.h> | |
| 3 | +#include <bpf/bpf_helpers.h> | |
| 4 | +#include <bpf/bpf_tracing.h> | |
| 5 | +#include <stdbool.h> | |
| 6 | +#include "bpf_kfuncs.h" | |
| 7 | +#include "bpf_misc.h" | |
| 8 | + | |
| 9 | +char _license[] SEC("license") = "GPL"; | |
| 10 | + | |
| 11 | +__u64 uprobe_session_result[3] = {}; | |
| 12 | +int pid = 0; | |
| 13 | + | |
| 14 | +static int uprobe_multi_check(void *ctx, int idx) | |
| 15 | +{ | |
| 16 | + if (bpf_get_current_pid_tgid() >> 32 != pid) | |
| 17 | + return 1; | |
| 18 | + | |
| 19 | + uprobe_session_result[idx]++; | |
| 20 | + | |
| 21 | + /* only consumer 1 executes return probe */ | |
| 22 | + if (idx == 0 || idx == 2) | |
| 23 | + return 1; | |
| 24 | + | |
| 25 | + return 0; | |
| 26 | +} | |
| 27 | + | |
| 28 | +SEC("uprobe.session//proc/self/exe:uprobe_multi_func_1") | |
| 29 | +int uprobe_0(struct pt_regs *ctx) | |
| 30 | +{ | |
| 31 | + return uprobe_multi_check(ctx, 0); | |
| 32 | +} | |
| 33 | + | |
| 34 | +SEC("uprobe.session//proc/self/exe:uprobe_multi_func_1") | |
| 35 | +int uprobe_1(struct pt_regs *ctx) | |
| 36 | +{ | |
| 37 | + return uprobe_multi_check(ctx, 1); | |
| 38 | +} | |
| 39 | + | |
| 40 | +SEC("uprobe.session//proc/self/exe:uprobe_multi_func_1") | |
| 41 | +int uprobe_2(struct pt_regs *ctx) | |
| 42 | +{ | |
| 43 | + return uprobe_multi_check(ctx, 2); | |
| 44 | +} | |
| @@ -0,0 +1,31 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +#include "vmlinux.h" | |
| 3 | +#include <bpf/bpf_helpers.h> | |
| 4 | +#include <bpf/bpf_tracing.h> | |
| 5 | +#include <bpf/usdt.bpf.h> | |
| 6 | +#include "bpf_misc.h" | |
| 7 | + | |
| 8 | +char _license[] SEC("license") = "GPL"; | |
| 9 | + | |
| 10 | + | |
| 11 | +SEC("uprobe.session") | |
| 12 | +__success | |
| 13 | +int uprobe_sesison_return_0(struct pt_regs *ctx) | |
| 14 | +{ | |
| 15 | + return 0; | |
| 16 | +} | |
| 17 | + | |
| 18 | +SEC("uprobe.session") | |
| 19 | +__success | |
| 20 | +int uprobe_sesison_return_1(struct pt_regs *ctx) | |
| 21 | +{ | |
| 22 | + return 1; | |
| 23 | +} | |
| 24 | + | |
| 25 | +SEC("uprobe.session") | |
| 26 | +__failure | |
| 27 | +__msg("At program exit the register R0 has smin=2 smax=2 should have been in [0, 1]") | |
| 28 | +int uprobe_sesison_return_2(struct pt_regs *ctx) | |
| 29 | +{ | |
| 30 | + return 2; | |
| 31 | +} | |
| @@ -0,0 +1,105 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ | |
| 3 | + | |
| 4 | +#include <vmlinux.h> | |
| 5 | +#include <bpf/bpf_helpers.h> | |
| 6 | +#include "bpf_experimental.h" | |
| 7 | +#include "bpf_misc.h" | |
| 8 | +#include "uptr_test_common.h" | |
| 9 | + | |
| 10 | +struct { | |
| 11 | + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); | |
| 12 | + __uint(map_flags, BPF_F_NO_PREALLOC); | |
| 13 | + __type(key, int); | |
| 14 | + __type(value, struct value_type); | |
| 15 | +} datamap SEC(".maps"); | |
| 16 | + | |
| 17 | +SEC("?syscall") | |
| 18 | +__failure __msg("store to uptr disallowed") | |
| 19 | +int uptr_write(const void *ctx) | |
| 20 | +{ | |
| 21 | + struct task_struct *task; | |
| 22 | + struct value_type *v; | |
| 23 | + | |
| 24 | + task = bpf_get_current_task_btf(); | |
| 25 | + v = bpf_task_storage_get(&datamap, task, 0, | |
| 26 | + BPF_LOCAL_STORAGE_GET_F_CREATE); | |
| 27 | + if (!v) | |
| 28 | + return 0; | |
| 29 | + | |
| 30 | + v->udata = NULL; | |
| 31 | + return 0; | |
| 32 | +} | |
| 33 | + | |
| 34 | +SEC("?syscall") | |
| 35 | +__failure __msg("store to uptr disallowed") | |
| 36 | +int uptr_write_nested(const void *ctx) | |
| 37 | +{ | |
| 38 | + struct task_struct *task; | |
| 39 | + struct value_type *v; | |
| 40 | + | |
| 41 | + task = bpf_get_current_task_btf(); | |
| 42 | + v = bpf_task_storage_get(&datamap, task, 0, | |
| 43 | + BPF_LOCAL_STORAGE_GET_F_CREATE); | |
| 44 | + if (!v) | |
| 45 | + return 0; | |
| 46 | + | |
| 47 | + v->nested.udata = NULL; | |
| 48 | + return 0; | |
| 49 | +} | |
| 50 | + | |
| 51 | +SEC("?syscall") | |
| 52 | +__failure __msg("R1 invalid mem access 'mem_or_null'") | |
| 53 | +int uptr_no_null_check(const void *ctx) | |
| 54 | +{ | |
| 55 | + struct task_struct *task; | |
| 56 | + struct value_type *v; | |
| 57 | + | |
| 58 | + task = bpf_get_current_task_btf(); | |
| 59 | + v = bpf_task_storage_get(&datamap, task, 0, | |
| 60 | + BPF_LOCAL_STORAGE_GET_F_CREATE); | |
| 61 | + if (!v) | |
| 62 | + return 0; | |
| 63 | + | |
| 64 | + v->udata->result = 0; | |
| 65 | + | |
| 66 | + return 0; | |
| 67 | +} | |
| 68 | + | |
| 69 | +SEC("?syscall") | |
| 70 | +__failure __msg("doesn't point to kptr") | |
| 71 | +int uptr_kptr_xchg(const void *ctx) | |
| 72 | +{ | |
| 73 | + struct task_struct *task; | |
| 74 | + struct value_type *v; | |
| 75 | + | |
| 76 | + task = bpf_get_current_task_btf(); | |
| 77 | + v = bpf_task_storage_get(&datamap, task, 0, | |
| 78 | + BPF_LOCAL_STORAGE_GET_F_CREATE); | |
| 79 | + if (!v) | |
| 80 | + return 0; | |
| 81 | + | |
| 82 | + bpf_kptr_xchg(&v->udata, NULL); | |
| 83 | + | |
| 84 | + return 0; | |
| 85 | +} | |
| 86 | + | |
| 87 | +SEC("?syscall") | |
| 88 | +__failure __msg("invalid mem access 'scalar'") | |
| 89 | +int uptr_obj_new(const void *ctx) | |
| 90 | +{ | |
| 91 | + struct value_type *v; | |
| 92 | + | |
| 93 | + v = bpf_obj_new(typeof(*v)); | |
| 94 | + if (!v) | |
| 95 | + return 0; | |
| 96 | + | |
| 97 | + if (v->udata) | |
| 98 | + v->udata->result = 0; | |
| 99 | + | |
| 100 | + bpf_obj_drop(v); | |
| 101 | + | |
| 102 | + return 0; | |
| 103 | +} | |
| 104 | + | |
| 105 | +char _license[] SEC("license") = "GPL"; | |
| @@ -0,0 +1,27 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ | |
| 3 | + | |
| 4 | +#include <vmlinux.h> | |
| 5 | +#include <bpf/bpf_helpers.h> | |
| 6 | +#include "uptr_test_common.h" | |
| 7 | + | |
| 8 | +struct { | |
| 9 | + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); | |
| 10 | + __uint(map_flags, BPF_F_NO_PREALLOC); | |
| 11 | + __type(key, int); | |
| 12 | + __type(value, struct large_uptr); | |
| 13 | +} large_uptr_map SEC(".maps"); | |
| 14 | + | |
| 15 | +struct { | |
| 16 | + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); | |
| 17 | + __uint(map_flags, BPF_F_NO_PREALLOC); | |
| 18 | + __type(key, int); | |
| 19 | + __type(value, struct empty_uptr); | |
| 20 | +} empty_uptr_map SEC(".maps"); | |
| 21 | + | |
| 22 | +struct { | |
| 23 | + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); | |
| 24 | + __uint(map_flags, BPF_F_NO_PREALLOC); | |
| 25 | + __type(key, int); | |
| 26 | + __type(value, struct kstruct_uptr); | |
| 27 | +} kstruct_uptr_map SEC(".maps"); | |
| @@ -0,0 +1,42 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ | |
| 3 | + | |
| 4 | +#include <vmlinux.h> | |
| 5 | +#include <bpf/bpf_helpers.h> | |
| 6 | +#include "uptr_test_common.h" | |
| 7 | + | |
| 8 | +struct { | |
| 9 | + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); | |
| 10 | + __uint(map_flags, BPF_F_NO_PREALLOC); | |
| 11 | + __type(key, int); | |
| 12 | + __type(value, struct value_lock_type); | |
| 13 | +} datamap SEC(".maps"); | |
| 14 | + | |
| 15 | +/* load test only. not used */ | |
| 16 | +SEC("syscall") | |
| 17 | +int not_used(void *ctx) | |
| 18 | +{ | |
| 19 | + struct value_lock_type *ptr; | |
| 20 | + struct task_struct *task; | |
| 21 | + struct user_data *udata; | |
| 22 | + | |
| 23 | + task = bpf_get_current_task_btf(); | |
| 24 | + ptr = bpf_task_storage_get(&datamap, task, 0, 0); | |
| 25 | + if (!ptr) | |
| 26 | + return 0; | |
| 27 | + | |
| 28 | + bpf_spin_lock(&ptr->lock); | |
| 29 | + | |
| 30 | + udata = ptr->udata; | |
| 31 | + if (!udata) { | |
| 32 | + bpf_spin_unlock(&ptr->lock); | |
| 33 | + return 0; | |
| 34 | + } | |
| 35 | + udata->result = MAGIC_VALUE + udata->a + udata->b; | |
| 36 | + | |
| 37 | + bpf_spin_unlock(&ptr->lock); | |
| 38 | + | |
| 39 | + return 0; | |
| 40 | +} | |
| 41 | + | |
| 42 | +char _license[] SEC("license") = "GPL"; | |
| @@ -29,12 +29,12 @@ int big_alloc1(void *ctx) · 28 unmodified lines | ||
| 29 | 29 | if (!page1) |
| 30 | 30 | return 1; |
| 31 | 31 | *page1 = 1; |
| 32 | − page2 = bpf_arena_alloc_pages(&arena, base + ARENA_SIZE - PAGE_SIZE, | |
| 32 | + page2 = bpf_arena_alloc_pages(&arena, base + ARENA_SIZE - PAGE_SIZE * 2, | |
| 33 | 33 | 1, NUMA_NO_NODE, 0); |
| 34 | 34 | if (!page2) |
| 35 | 35 | return 2; |
| 36 | 36 | *page2 = 2; |
| 37 | − no_page = bpf_arena_alloc_pages(&arena, base + ARENA_SIZE, | |
| 37 | + no_page = bpf_arena_alloc_pages(&arena, base + ARENA_SIZE - PAGE_SIZE, | |
| 38 | 38 | 1, NUMA_NO_NODE, 0); |
| 39 | 39 | if (no_page) |
| 40 | 40 | return 3; |
| @@ -66,4 +66,110 @@ int big_alloc1(void *ctx) · 25 unmodified lines | ||
| 66 | 66 | #endif |
| 67 | 67 | return 0; |
| 68 | 68 | } |
| 69 | + | |
| 70 | +#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) | |
| 71 | +#define PAGE_CNT 100 | |
| 72 | +__u8 __arena * __arena page[PAGE_CNT]; /* occupies the first page */ | |
| 73 | +__u8 __arena *base; | |
| 74 | + | |
| 75 | +/* | |
| 76 | + * Check that arena's range_tree algorithm allocates pages sequentially | |
| 77 | + * on the first pass and then fills in all gaps on the second pass. | |
| 78 | + */ | |
| 79 | +__noinline int alloc_pages(int page_cnt, int pages_atonce, bool first_pass, | |
| 80 | + int max_idx, int step) | |
| 81 | +{ | |
| 82 | + __u8 __arena *pg; | |
| 83 | + int i, pg_idx; | |
| 84 | + | |
| 85 | + for (i = 0; i < page_cnt; i++) { | |
| 86 | + pg = bpf_arena_alloc_pages(&arena, NULL, pages_atonce, | |
| 87 | + NUMA_NO_NODE, 0); | |
| 88 | + if (!pg) | |
| 89 | + return step; | |
| 90 | + pg_idx = (unsigned long) (pg - base) / PAGE_SIZE; | |
| 91 | + if (first_pass) { | |
| 92 | + /* Pages must be allocated sequentially */ | |
| 93 | + if (pg_idx != i) | |
| 94 | + return step + 100; | |
| 95 | + } else { | |
| 96 | + /* Allocator must fill into gaps */ | |
| 97 | + if (pg_idx >= max_idx || (pg_idx & 1)) | |
| 98 | + return step + 200; | |
| 99 | + } | |
| 100 | + *pg = pg_idx; | |
| 101 | + page[pg_idx] = pg; | |
| 102 | + cond_break; | |
| 103 | + } | |
| 104 | + return 0; | |
| 105 | +} | |
| 106 | + | |
| 107 | +SEC("syscall") | |
| 108 | +__success __retval(0) | |
| 109 | +int big_alloc2(void *ctx) | |
| 110 | +{ | |
| 111 | + __u8 __arena *pg; | |
| 112 | + int i, err; | |
| 113 | + | |
| 114 | + base = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); | |
| 115 | + if (!base) | |
| 116 | + return 1; | |
| 117 | + bpf_arena_free_pages(&arena, (void __arena *)base, 1); | |
| 118 | + | |
| 119 | + err = alloc_pages(PAGE_CNT, 1, true, PAGE_CNT, 2); | |
| 120 | + if (err) | |
| 121 | + return err; | |
| 122 | + | |
| 123 | + /* Clear all even pages */ | |
| 124 | + for (i = 0; i < PAGE_CNT; i += 2) { | |
| 125 | + pg = page[i]; | |
| 126 | + if (*pg != i) | |
| 127 | + return 3; | |
| 128 | + bpf_arena_free_pages(&arena, (void __arena *)pg, 1); | |
| 129 | + page[i] = NULL; | |
| 130 | + cond_break; | |
| 131 | + } | |
| 132 | + | |
| 133 | + /* Allocate into freed gaps */ | |
| 134 | + err = alloc_pages(PAGE_CNT / 2, 1, false, PAGE_CNT, 4); | |
| 135 | + if (err) | |
| 136 | + return err; | |
| 137 | + | |
| 138 | + /* Free pairs of pages */ | |
| 139 | + for (i = 0; i < PAGE_CNT; i += 4) { | |
| 140 | + pg = page[i]; | |
| 141 | + if (*pg != i) | |
| 142 | + return 5; | |
| 143 | + bpf_arena_free_pages(&arena, (void __arena *)pg, 2); | |
| 144 | + page[i] = NULL; | |
| 145 | + page[i + 1] = NULL; | |
| 146 | + cond_break; | |
| 147 | + } | |
| 148 | + | |
| 149 | + /* Allocate 2 pages at a time into freed gaps */ | |
| 150 | + err = alloc_pages(PAGE_CNT / 4, 2, false, PAGE_CNT, 6); | |
| 151 | + if (err) | |
| 152 | + return err; | |
| 153 | + | |
| 154 | + /* Check pages without freeing */ | |
| 155 | + for (i = 0; i < PAGE_CNT; i += 2) { | |
| 156 | + pg = page[i]; | |
| 157 | + if (*pg != i) | |
| 158 | + return 7; | |
| 159 | + cond_break; | |
| 160 | + } | |
| 161 | + | |
| 162 | + pg = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); | |
| 163 | + | |
| 164 | + if (!pg) | |
| 165 | + return 8; | |
| 166 | + /* | |
| 167 | + * The first PAGE_CNT pages are occupied. The new page | |
| 168 | + * must be above. | |
| 169 | + */ | |
| 170 | + if ((pg - base) / PAGE_SIZE < PAGE_CNT) | |
| 171 | + return 9; | |
| 172 | + return 0; | |
| 173 | +} | |
| 174 | +#endif | |
| 69 | 175 | char _license[] SEC("license") = "GPL"; |
| @@ -0,0 +1,272 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +#include <vmlinux.h> | |
| 4 | +#include <bpf/bpf_helpers.h> | |
| 5 | +#include "bpf_misc.h" | |
| 6 | +#include "bpf_experimental.h" | |
| 7 | + | |
| 8 | +/* From include/linux/filter.h */ | |
| 9 | +#define MAX_BPF_STACK 512 | |
| 10 | + | |
| 11 | +#if defined(__TARGET_ARCH_x86) | |
| 12 | + | |
| 13 | +struct elem { | |
| 14 | + struct bpf_timer t; | |
| 15 | + char pad[256]; | |
| 16 | +}; | |
| 17 | + | |
| 18 | +struct { | |
| 19 | + __uint(type, BPF_MAP_TYPE_ARRAY); | |
| 20 | + __uint(max_entries, 1); | |
| 21 | + __type(key, int); | |
| 22 | + __type(value, struct elem); | |
| 23 | +} array SEC(".maps"); | |
| 24 | + | |
| 25 | +SEC("kprobe") | |
| 26 | +__description("Private stack, single prog") | |
| 27 | +__success | |
| 28 | +__arch_x86_64 | |
| 29 | +__jited(" movabsq $0x{{.*}}, %r9") | |
| 30 | +__jited(" addq %gs:0x{{.*}}, %r9") | |
| 31 | +__jited(" movl $0x2a, %edi") | |
| 32 | +__jited(" movq %rdi, -0x100(%r9)") | |
| 33 | +__naked void private_stack_single_prog(void) | |
| 34 | +{ | |
| 35 | + asm volatile (" \ | |
| 36 | + r1 = 42; \ | |
| 37 | + *(u64 *)(r10 - 256) = r1; \ | |
| 38 | + r0 = 0; \ | |
| 39 | + exit; \ | |
| 40 | +" ::: __clobber_all); | |
| 41 | +} | |
| 42 | + | |
| 43 | +SEC("raw_tp") | |
| 44 | +__description("No private stack") | |
| 45 | +__success | |
| 46 | +__arch_x86_64 | |
| 47 | +__jited(" subq $0x8, %rsp") | |
| 48 | +__naked void no_private_stack_nested(void) | |
| 49 | +{ | |
| 50 | + asm volatile (" \ | |
| 51 | + r1 = 42; \ | |
| 52 | + *(u64 *)(r10 - 8) = r1; \ | |
| 53 | + r0 = 0; \ | |
| 54 | + exit; \ | |
| 55 | +" ::: __clobber_all); | |
| 56 | +} | |
| 57 | + | |
| 58 | +__used | |
| 59 | +__naked static void cumulative_stack_depth_subprog(void) | |
| 60 | +{ | |
| 61 | + asm volatile (" \ | |
| 62 | + r1 = 41; \ | |
| 63 | + *(u64 *)(r10 - 32) = r1; \ | |
| 64 | + call %[bpf_get_smp_processor_id]; \ | |
| 65 | + exit; \ | |
| 66 | +" : | |
| 67 | + : __imm(bpf_get_smp_processor_id) | |
| 68 | + : __clobber_all); | |
| 69 | +} | |
| 70 | + | |
| 71 | +SEC("kprobe") | |
| 72 | +__description("Private stack, subtree > MAX_BPF_STACK") | |
| 73 | +__success | |
| 74 | +__arch_x86_64 | |
| 75 | +/* private stack fp for the main prog */ | |
| 76 | +__jited(" movabsq $0x{{.*}}, %r9") | |
| 77 | +__jited(" addq %gs:0x{{.*}}, %r9") | |
| 78 | +__jited(" movl $0x2a, %edi") | |
| 79 | +__jited(" movq %rdi, -0x200(%r9)") | |
| 80 | +__jited(" pushq %r9") | |
| 81 | +__jited(" callq 0x{{.*}}") | |
| 82 | +__jited(" popq %r9") | |
| 83 | +__jited(" xorl %eax, %eax") | |
| 84 | +__naked void private_stack_nested_1(void) | |
| 85 | +{ | |
| 86 | + asm volatile (" \ | |
| 87 | + r1 = 42; \ | |
| 88 | + *(u64 *)(r10 - %[max_bpf_stack]) = r1; \ | |
| 89 | + call cumulative_stack_depth_subprog; \ | |
| 90 | + r0 = 0; \ | |
| 91 | + exit; \ | |
| 92 | +" : | |
| 93 | + : __imm_const(max_bpf_stack, MAX_BPF_STACK) | |
| 94 | + : __clobber_all); | |
| 95 | +} | |
| 96 | + | |
| 97 | +__naked __noinline __used | |
| 98 | +static unsigned long loop_callback(void) | |
| 99 | +{ | |
| 100 | + asm volatile (" \ | |
| 101 | + call %[bpf_get_prandom_u32]; \ | |
| 102 | + r1 = 42; \ | |
| 103 | + *(u64 *)(r10 - 512) = r1; \ | |
| 104 | + call cumulative_stack_depth_subprog; \ | |
| 105 | + r0 = 0; \ | |
| 106 | + exit; \ | |
| 107 | +" : | |
| 108 | + : __imm(bpf_get_prandom_u32) | |
| 109 | + : __clobber_common); | |
| 110 | +} | |
| 111 | + | |
| 112 | +SEC("raw_tp") | |
| 113 | +__description("Private stack, callback") | |
| 114 | +__success | |
| 115 | +__arch_x86_64 | |
| 116 | +/* for func loop_callback */ | |
| 117 | +__jited("func #1") | |
| 118 | +__jited(" endbr64") | |
| 119 | +__jited(" nopl (%rax,%rax)") | |
| 120 | +__jited(" nopl (%rax)") | |
| 121 | +__jited(" pushq %rbp") | |
| 122 | +__jited(" movq %rsp, %rbp") | |
| 123 | +__jited(" endbr64") | |
| 124 | +__jited(" movabsq $0x{{.*}}, %r9") | |
| 125 | +__jited(" addq %gs:0x{{.*}}, %r9") | |
| 126 | +__jited(" pushq %r9") | |
| 127 | +__jited(" callq") | |
| 128 | +__jited(" popq %r9") | |
| 129 | +__jited(" movl $0x2a, %edi") | |
| 130 | +__jited(" movq %rdi, -0x200(%r9)") | |
| 131 | +__jited(" pushq %r9") | |
| 132 | +__jited(" callq") | |
| 133 | +__jited(" popq %r9") | |
| 134 | +__naked void private_stack_callback(void) | |
| 135 | +{ | |
| 136 | + asm volatile (" \ | |
| 137 | + r1 = 1; \ | |
| 138 | + r2 = %[loop_callback]; \ | |
| 139 | + r3 = 0; \ | |
| 140 | + r4 = 0; \ | |
| 141 | + call %[bpf_loop]; \ | |
| 142 | + r0 = 0; \ | |
| 143 | + exit; \ | |
| 144 | +" : | |
| 145 | + : __imm_ptr(loop_callback), | |
| 146 | + __imm(bpf_loop) | |
| 147 | + : __clobber_common); | |
| 148 | +} | |
| 149 | + | |
| 150 | +SEC("fentry/bpf_fentry_test9") | |
| 151 | +__description("Private stack, exception in main prog") | |
| 152 | +__success __retval(0) | |
| 153 | +__arch_x86_64 | |
| 154 | +__jited(" pushq %r9") | |
| 155 | +__jited(" callq") | |
| 156 | +__jited(" popq %r9") | |
| 157 | +int private_stack_exception_main_prog(void) | |
| 158 | +{ | |
| 159 | + asm volatile (" \ | |
| 160 | + r1 = 42; \ | |
| 161 | + *(u64 *)(r10 - 512) = r1; \ | |
| 162 | +" ::: __clobber_common); | |
| 163 | + | |
| 164 | + bpf_throw(0); | |
| 165 | + return 0; | |
| 166 | +} | |
| 167 | + | |
| 168 | +__used static int subprog_exception(void) | |
| 169 | +{ | |
| 170 | + bpf_throw(0); | |
| 171 | + return 0; | |
| 172 | +} | |
| 173 | + | |
| 174 | +SEC("fentry/bpf_fentry_test9") | |
| 175 | +__description("Private stack, exception in subprog") | |
| 176 | +__success __retval(0) | |
| 177 | +__arch_x86_64 | |
| 178 | +__jited(" movq %rdi, -0x200(%r9)") | |
| 179 | +__jited(" pushq %r9") | |
| 180 | +__jited(" callq") | |
| 181 | +__jited(" popq %r9") | |
| 182 | +int private_stack_exception_sub_prog(void) | |
| 183 | +{ | |
| 184 | + asm volatile (" \ | |
| 185 | + r1 = 42; \ | |
| 186 | + *(u64 *)(r10 - 512) = r1; \ | |
| 187 | + call subprog_exception; \ | |
| 188 | +" ::: __clobber_common); | |
| 189 | + | |
| 190 | + return 0; | |
| 191 | +} | |
| 192 | + | |
| 193 | +int glob; | |
| 194 | +__noinline static void subprog2(int *val) | |
| 195 | +{ | |
| 196 | + glob += val[0] * 2; | |
| 197 | +} | |
| 198 | + | |
| 199 | +__noinline static void subprog1(int *val) | |
| 200 | +{ | |
| 201 | + int tmp[64] = {}; | |
| 202 | + | |
| 203 | + tmp[0] = *val; | |
| 204 | + subprog2(tmp); | |
| 205 | +} | |
| 206 | + | |
| 207 | +__noinline static int timer_cb1(void *map, int *key, struct bpf_timer *timer) | |
| 208 | +{ | |
| 209 | + subprog1(key); | |
| 210 | + return 0; | |
| 211 | +} | |
| 212 | + | |
| 213 | +__noinline static int timer_cb2(void *map, int *key, struct bpf_timer *timer) | |
| 214 | +{ | |
| 215 | + return 0; | |
| 216 | +} | |
| 217 | + | |
| 218 | +SEC("fentry/bpf_fentry_test9") | |
| 219 | +__description("Private stack, async callback, not nested") | |
| 220 | +__success __retval(0) | |
| 221 | +__arch_x86_64 | |
| 222 | +__jited(" movabsq $0x{{.*}}, %r9") | |
| 223 | +int private_stack_async_callback_1(void) | |
| 224 | +{ | |
| 225 | + struct bpf_timer *arr_timer; | |
| 226 | + int array_key = 0; | |
| 227 | + | |
| 228 | + arr_timer = bpf_map_lookup_elem(&array, &array_key); | |
| 229 | + if (!arr_timer) | |
| 230 | + return 0; | |
| 231 | + | |
| 232 | + bpf_timer_init(arr_timer, &array, 1); | |
| 233 | + bpf_timer_set_callback(arr_timer, timer_cb2); | |
| 234 | + bpf_timer_start(arr_timer, 0, 0); | |
| 235 | + subprog1(&array_key); | |
| 236 | + return 0; | |
| 237 | +} | |
| 238 | + | |
| 239 | +SEC("fentry/bpf_fentry_test9") | |
| 240 | +__description("Private stack, async callback, potential nesting") | |
| 241 | +__success __retval(0) | |
| 242 | +__arch_x86_64 | |
| 243 | +__jited(" subq $0x100, %rsp") | |
| 244 | +int private_stack_async_callback_2(void) | |
| 245 | +{ | |
| 246 | + struct bpf_timer *arr_timer; | |
| 247 | + int array_key = 0; | |
| 248 | + | |
| 249 | + arr_timer = bpf_map_lookup_elem(&array, &array_key); | |
| 250 | + if (!arr_timer) | |
| 251 | + return 0; | |
| 252 | + | |
| 253 | + bpf_timer_init(arr_timer, &array, 1); | |
| 254 | + bpf_timer_set_callback(arr_timer, timer_cb1); | |
| 255 | + bpf_timer_start(arr_timer, 0, 0); | |
| 256 | + subprog1(&array_key); | |
| 257 | + return 0; | |
| 258 | +} | |
| 259 | + | |
| 260 | +#else | |
| 261 | + | |
| 262 | +SEC("kprobe") | |
| 263 | +__description("private stack is not supported, use a dummy test") | |
| 264 | +__success | |
| 265 | +int dummy_test(void) | |
| 266 | +{ | |
| 267 | + return 0; | |
| 268 | +} | |
| 269 | + | |
| 270 | +#endif | |
| 271 | + | |
| 272 | +char _license[] SEC("license") = "GPL"; | |
| @@ -791,7 +791,7 @@ l0_%=: r0 = *(u8*)skb[0]; \ · 790 unmodified lines | ||
| 791 | 791 | |
| 792 | 792 | SEC("tc") |
| 793 | 793 | __description("reference tracking: forbid LD_ABS while holding reference") |
| 794 | −__failure __msg("BPF_LD_[ABS|IND] cannot be mixed with socket references") | |
| 794 | +__failure __msg("BPF_LD_[ABS|IND] would lead to reference leak") | |
| 795 | 795 | __naked void ld_abs_while_holding_reference(void) |
| 796 | 796 | { |
| 797 | 797 | asm volatile (" \ |
| @@ -836,7 +836,7 @@ l0_%=: r7 = 1; \ · 38 unmodified lines | ||
| 836 | 836 | |
| 837 | 837 | SEC("tc") |
| 838 | 838 | __description("reference tracking: forbid LD_IND while holding reference") |
| 839 | −__failure __msg("BPF_LD_[ABS|IND] cannot be mixed with socket references") | |
| 839 | +__failure __msg("BPF_LD_[ABS|IND] would lead to reference leak") | |
| 840 | 840 | __naked void ld_ind_while_holding_reference(void) |
| 841 | 841 | { |
| 842 | 842 | asm volatile (" \ |
| @@ -977,4 +977,64 @@ l1_%=: r0 = *(u8*)(r7 + 0); \ · 976 unmodified lines | ||
| 977 | 977 | : __clobber_all); |
| 978 | 978 | } |
| 979 | 979 | |
| 980 | +SEC("cgroup/post_bind4") | |
| 981 | +__description("sk->src_ip6[0] [load 1st byte]") | |
| 982 | +__failure __msg("invalid bpf_context access off=28 size=2") | |
| 983 | +__naked void post_bind4_read_src_ip6(void) | |
| 984 | +{ | |
| 985 | + asm volatile (" \ | |
| 986 | + r6 = r1; \ | |
| 987 | + r7 = *(u16*)(r6 + %[bpf_sock_src_ip6_0]); \ | |
| 988 | + r0 = 1; \ | |
| 989 | + exit; \ | |
| 990 | +" : | |
| 991 | + : __imm_const(bpf_sock_src_ip6_0, offsetof(struct bpf_sock, src_ip6[0])) | |
| 992 | + : __clobber_all); | |
| 993 | +} | |
| 994 | + | |
| 995 | +SEC("cgroup/post_bind4") | |
| 996 | +__description("sk->mark [load mark]") | |
| 997 | +__failure __msg("invalid bpf_context access off=16 size=2") | |
| 998 | +__naked void post_bind4_read_mark(void) | |
| 999 | +{ | |
| 1000 | + asm volatile (" \ | |
| 1001 | + r6 = r1; \ | |
| 1002 | + r7 = *(u16*)(r6 + %[bpf_sock_mark]); \ | |
| 1003 | + r0 = 1; \ | |
| 1004 | + exit; \ | |
| 1005 | +" : | |
| 1006 | + : __imm_const(bpf_sock_mark, offsetof(struct bpf_sock, mark)) | |
| 1007 | + : __clobber_all); | |
| 1008 | +} | |
| 1009 | + | |
| 1010 | +SEC("cgroup/post_bind6") | |
| 1011 | +__description("sk->src_ip4 [load src_ip4]") | |
| 1012 | +__failure __msg("invalid bpf_context access off=24 size=2") | |
| 1013 | +__naked void post_bind6_read_src_ip4(void) | |
| 1014 | +{ | |
| 1015 | + asm volatile (" \ | |
| 1016 | + r6 = r1; \ | |
| 1017 | + r7 = *(u16*)(r6 + %[bpf_sock_src_ip4]); \ | |
| 1018 | + r0 = 1; \ | |
| 1019 | + exit; \ | |
| 1020 | +" : | |
| 1021 | + : __imm_const(bpf_sock_src_ip4, offsetof(struct bpf_sock, src_ip4)) | |
| 1022 | + : __clobber_all); | |
| 1023 | +} | |
| 1024 | + | |
| 1025 | +SEC("cgroup/sock_create") | |
| 1026 | +__description("sk->src_port [word load]") | |
| 1027 | +__failure __msg("invalid bpf_context access off=44 size=2") | |
| 1028 | +__naked void sock_create_read_src_port(void) | |
| 1029 | +{ | |
| 1030 | + asm volatile (" \ | |
| 1031 | + r6 = r1; \ | |
| 1032 | + r7 = *(u16*)(r6 + %[bpf_sock_src_port]); \ | |
| 1033 | + r0 = 1; \ | |
| 1034 | + exit; \ | |
| 1035 | +" : | |
| 1036 | + : __imm_const(bpf_sock_src_port, offsetof(struct bpf_sock, src_port)) | |
| 1037 | + : __clobber_all); | |
| 1038 | +} | |
| 1039 | + | |
| 980 | 1040 | char _license[] SEC("license") = "GPL"; |
| @@ -187,7 +187,7 @@ l0_%=: r6 = r0; \ · 186 unmodified lines | ||
| 187 | 187 | |
| 188 | 188 | SEC("cgroup/skb") |
| 189 | 189 | __description("spin_lock: test6 missing unlock") |
| 190 | −__failure __msg("unlock is missing") | |
| 190 | +__failure __msg("BPF_EXIT instruction cannot be used inside bpf_spin_lock-ed region") | |
| 191 | 191 | __failure_unpriv __msg_unpriv("") |
| 192 | 192 | __naked void spin_lock_test6_missing_unlock(void) |
| 193 | 193 | { |
| @@ -26,10 +26,6 @@ · 25 unmodified lines | ||
| 26 | 26 | #include "test_maps.h" |
| 27 | 27 | #include "testing_helpers.h" |
| 28 | 28 | |
| 29 | −#ifndef ENOTSUPP | |
| 30 | −#define ENOTSUPP 524 | |
| 31 | −#endif | |
| 32 | − | |
| 33 | 29 | int skips; |
| 34 | 30 | |
| 35 | 31 | static struct bpf_map_create_opts map_opts = { .sz = sizeof(map_opts) }; |
| @@ -16,15 +16,18 @@ · 15 unmodified lines | ||
| 16 | 16 | #include <sys/socket.h> |
| 17 | 17 | #include <sys/un.h> |
| 18 | 18 | #include <bpf/btf.h> |
| 19 | +#include <time.h> | |
| 19 | 20 | #include "json_writer.h" |
| 20 | 21 | |
| 21 | 22 | #include "network_helpers.h" |
| 22 | 23 | |
| 24 | +/* backtrace() and backtrace_symbols_fd() are glibc specific, | |
| 25 | + * use header file when glibc is available and provide stub | |
| 26 | + * implementations when another libc implementation is used. | |
| 27 | + */ | |
| 23 | 28 | #ifdef __GLIBC__ |
| 24 | 29 | #include <execinfo.h> /* backtrace */ |
| 25 | −#endif | |
| 26 | − | |
| 27 | −/* Default backtrace funcs if missing at link */ | |
| 30 | +#else | |
| 28 | 31 | __weak int backtrace(void **buffer, int size) |
| 29 | 32 | { |
| 30 | 33 | return 0; |
| @@ -34,6 +37,7 @@ __weak void backtrace_symbols_fd(void *const *buffer, int size, int fd) · 3 unmodified lines | ||
| 34 | 37 | { |
| 35 | 38 | dprintf(fd, "<backtrace not supported>\n"); |
| 36 | 39 | } |
| 40 | +#endif /*__GLIBC__ */ | |
| 37 | 41 | |
| 38 | 42 | int env_verbosity = 0; |
| 39 | 43 | |
| @@ -176,6 +180,88 @@ int usleep(useconds_t usec) · 136 unmodified lines | ||
| 176 | 180 | return syscall(__NR_nanosleep, &ts, NULL); |
| 177 | 181 | } |
| 178 | 182 | |
| 183 | +/* Watchdog timer is started by watchdog_start() and stopped by watchdog_stop(). | |
| 184 | + * If timer is active for longer than env.secs_till_notify, | |
| 185 | + * it prints the name of the current test to the stderr. | |
| 186 | + * If timer is active for longer than env.secs_till_kill, | |
| 187 | + * it kills the thread executing the test by sending a SIGSEGV signal to it. | |
| 188 | + */ | |
| 189 | +static void watchdog_timer_func(union sigval sigval) | |
| 190 | +{ | |
| 191 | + struct itimerspec timeout = {}; | |
| 192 | + char test_name[256]; | |
| 193 | + int err; | |
| 194 | + | |
| 195 | + if (env.subtest_state) | |
| 196 | + snprintf(test_name, sizeof(test_name), "%s/%s", | |
| 197 | + env.test->test_name, env.subtest_state->name); | |
| 198 | + else | |
| 199 | + snprintf(test_name, sizeof(test_name), "%s", | |
| 200 | + env.test->test_name); | |
| 201 | + | |
| 202 | + switch (env.watchdog_state) { | |
| 203 | + case WD_NOTIFY: | |
| 204 | + fprintf(env.stderr_saved, "WATCHDOG: test case %s executes for %d seconds...\n", | |
| 205 | + test_name, env.secs_till_notify); | |
| 206 | + timeout.it_value.tv_sec = env.secs_till_kill - env.secs_till_notify; | |
| 207 | + env.watchdog_state = WD_KILL; | |
| 208 | + err = timer_settime(env.watchdog, 0, &timeout, NULL); | |
| 209 | + if (err) | |
| 210 | + fprintf(env.stderr_saved, "Failed to arm watchdog timer\n"); | |
| 211 | + break; | |
| 212 | + case WD_KILL: | |
| 213 | + fprintf(env.stderr_saved, | |
| 214 | + "WATCHDOG: test case %s executes for %d seconds, terminating with SIGSEGV\n", | |
| 215 | + test_name, env.secs_till_kill); | |
| 216 | + pthread_kill(env.main_thread, SIGSEGV); | |
| 217 | + break; | |
| 218 | + } | |
| 219 | +} | |
| 220 | + | |
| 221 | +static void watchdog_start(void) | |
| 222 | +{ | |
| 223 | + struct itimerspec timeout = {}; | |
| 224 | + int err; | |
| 225 | + | |
| 226 | + if (env.secs_till_kill == 0) | |
| 227 | + return; | |
| 228 | + if (env.secs_till_notify > 0) { | |
| 229 | + env.watchdog_state = WD_NOTIFY; | |
| 230 | + timeout.it_value.tv_sec = env.secs_till_notify; | |
| 231 | + } else { | |
| 232 | + env.watchdog_state = WD_KILL; | |
| 233 | + timeout.it_value.tv_sec = env.secs_till_kill; | |
| 234 | + } | |
| 235 | + err = timer_settime(env.watchdog, 0, &timeout, NULL); | |
| 236 | + if (err) | |
| 237 | + fprintf(env.stderr_saved, "Failed to start watchdog timer\n"); | |
| 238 | +} | |
| 239 | + | |
| 240 | +static void watchdog_stop(void) | |
| 241 | +{ | |
| 242 | + struct itimerspec timeout = {}; | |
| 243 | + int err; | |
| 244 | + | |
| 245 | + env.watchdog_state = WD_NOTIFY; | |
| 246 | + err = timer_settime(env.watchdog, 0, &timeout, NULL); | |
| 247 | + if (err) | |
| 248 | + fprintf(env.stderr_saved, "Failed to stop watchdog timer\n"); | |
| 249 | +} | |
| 250 | + | |
| 251 | +static void watchdog_init(void) | |
| 252 | +{ | |
| 253 | + struct sigevent watchdog_sev = { | |
| 254 | + .sigev_notify = SIGEV_THREAD, | |
| 255 | + .sigev_notify_function = watchdog_timer_func, | |
| 256 | + }; | |
| 257 | + int err; | |
| 258 | + | |
| 259 | + env.main_thread = pthread_self(); | |
| 260 | + err = timer_create(CLOCK_MONOTONIC, &watchdog_sev, &env.watchdog); | |
| 261 | + if (err) | |
| 262 | + fprintf(stderr, "Failed to initialize watchdog timer\n"); | |
| 263 | +} | |
| 264 | + | |
| 179 | 265 | static bool should_run(struct test_selector *sel, int num, const char *name) |
| 180 | 266 | { |
| 181 | 267 | int i; |
| @@ -512,6 +598,7 @@ bool test__start_subtest(const char *subtest_name) · 330 unmodified lines | ||
| 512 | 598 | |
| 513 | 599 | env.subtest_state = subtest_state; |
| 514 | 600 | stdio_hijack_init(&subtest_state->log_buf, &subtest_state->log_cnt); |
| 601 | + watchdog_start(); | |
| 515 | 602 | |
| 516 | 603 | return true; |
| 517 | 604 | } |
| @@ -777,6 +864,7 @@ enum ARG_KEYS { · 259 unmodified lines | ||
| 777 | 864 | ARG_DEBUG = -1, |
| 778 | 865 | ARG_JSON_SUMMARY = 'J', |
| 779 | 866 | ARG_TRAFFIC_MONITOR = 'm', |
| 867 | + ARG_WATCHDOG_TIMEOUT = 'w', | |
| 780 | 868 | }; |
| 781 | 869 | |
| 782 | 870 | static const struct argp_option opts[] = { |
| @@ -807,6 +895,8 @@ static const struct argp_option opts[] = { · 24 unmodified lines | ||
| 807 | 895 | { "traffic-monitor", ARG_TRAFFIC_MONITOR, "NAMES", 0, |
| 808 | 896 | "Monitor network traffic of tests with name matching the pattern (supports '*' wildcard)." }, |
| 809 | 897 | #endif |
| 898 | + { "watchdog-timeout", ARG_WATCHDOG_TIMEOUT, "SECONDS", 0, | |
| 899 | + "Kill the process if tests are not making progress for specified number of seconds." }, | |
| 810 | 900 | {}, |
| 811 | 901 | }; |
| 812 | 902 | |
| @@ -868,6 +958,7 @@ static int libbpf_print_fn(enum libbpf_print_level level, · 55 unmodified lines | ||
| 868 | 958 | |
| 869 | 959 | va_copy(args2, args); |
| 870 | 960 | vfprintf(libbpf_capture_stream, format, args2); |
| 961 | + va_end(args2); | |
| 871 | 962 | } |
| 872 | 963 | |
| 873 | 964 | if (env.verbosity < VERBOSE_VERY && level == LIBBPF_DEBUG) |
| @@ -1031,6 +1122,16 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) · 157 unmodified lines | ||
| 1031 | 1122 | true); |
| 1032 | 1123 | break; |
| 1033 | 1124 | #endif |
| 1125 | + case ARG_WATCHDOG_TIMEOUT: | |
| 1126 | + env->secs_till_kill = atoi(arg); | |
| 1127 | + if (env->secs_till_kill < 0) { | |
| 1128 | + fprintf(stderr, "Invalid watchdog timeout: %s.\n", arg); | |
| 1129 | + return -EINVAL; | |
| 1130 | + } | |
| 1131 | + if (env->secs_till_kill < env->secs_till_notify) { | |
| 1132 | + env->secs_till_notify = 0; | |
| 1133 | + } | |
| 1134 | + break; | |
| 1034 | 1135 | default: |
| 1035 | 1136 | return ARGP_ERR_UNKNOWN; |
| 1036 | 1137 | } |
| @@ -1259,10 +1360,12 @@ static void run_one_test(int test_num) · 222 unmodified lines | ||
| 1259 | 1360 | |
| 1260 | 1361 | stdio_hijack(&state->log_buf, &state->log_cnt); |
| 1261 | 1362 | |
| 1363 | + watchdog_start(); | |
| 1262 | 1364 | if (test->run_test) |
| 1263 | 1365 | test->run_test(); |
| 1264 | 1366 | else if (test->run_serial_test) |
| 1265 | 1367 | test->run_serial_test(); |
| 1368 | + watchdog_stop(); | |
| 1266 | 1369 | |
| 1267 | 1370 | /* ensure last sub-test is finalized properly */ |
| 1268 | 1371 | if (env.subtest_state) |
| @@ -1703,6 +1806,7 @@ out: · 434 unmodified lines | ||
| 1703 | 1806 | static int worker_main(int sock) |
| 1704 | 1807 | { |
| 1705 | 1808 | save_netns(); |
| 1809 | + watchdog_init(); | |
| 1706 | 1810 | |
| 1707 | 1811 | while (true) { |
| 1708 | 1812 | /* receive command */ |
| @@ -1812,6 +1916,8 @@ int main(int argc, char **argv) · 103 unmodified lines | ||
| 1812 | 1916 | |
| 1813 | 1917 | sigaction(SIGSEGV, &sigact, NULL); |
| 1814 | 1918 | |
| 1919 | + env.secs_till_notify = 10; | |
| 1920 | + env.secs_till_kill = 120; | |
| 1815 | 1921 | err = argp_parse(&argp, argc, argv, 0, NULL, &env); |
| 1816 | 1922 | if (err) |
| 1817 | 1923 | return err; |
| @@ -1820,6 +1926,8 @@ int main(int argc, char **argv) · 2 unmodified lines | ||
| 1820 | 1926 | if (err) |
| 1821 | 1927 | return err; |
| 1822 | 1928 | |
| 1929 | + watchdog_init(); | |
| 1930 | + | |
| 1823 | 1931 | /* Use libbpf 1.0 API mode */ |
| 1824 | 1932 | libbpf_set_strict_mode(LIBBPF_STRICT_ALL); |
| 1825 | 1933 | libbpf_set_print(libbpf_print_fn); |
| @@ -131,6 +131,12 @@ struct test_env { · 130 unmodified lines | ||
| 131 | 131 | pid_t *worker_pids; /* array of worker pids */ |
| 132 | 132 | int *worker_socks; /* array of worker socks */ |
| 133 | 133 | int *worker_current_test; /* array of current running test for each worker */ |
| 134 | + | |
| 135 | + pthread_t main_thread; | |
| 136 | + int secs_till_notify; | |
| 137 | + int secs_till_kill; | |
| 138 | + timer_t watchdog; /* watch for stalled tests/subtests */ | |
| 139 | + enum { WD_NOTIFY, WD_KILL } watchdog_state; | |
| 134 | 140 | }; |
| 135 | 141 | |
| 136 | 142 | #define MAX_LOG_TRUNK_SIZE 8192 |
| @@ -390,6 +396,14 @@ int test__join_cgroup(const char *path); · 253 unmodified lines | ||
| 390 | 396 | ___ok; \ |
| 391 | 397 | }) |
| 392 | 398 | |
| 399 | +#define ASSERT_ERR_FD(fd, name) ({ \ | |
| 400 | + static int duration = 0; \ | |
| 401 | + int ___fd = (fd); \ | |
| 402 | + bool ___ok = ___fd < 0; \ | |
| 403 | + CHECK(!___ok, (name), "unexpected fd: %d\n", ___fd); \ | |
| 404 | + ___ok; \ | |
| 405 | +}) | |
| 406 | + | |
| 393 | 407 | #define SYS(goto_label, fmt, ...) \ |
| 394 | 408 | ({ \ |
| 395 | 409 | char cmd[1024]; \ |
| @@ -42,10 +42,6 @@ · 41 unmodified lines | ||
| 42 | 42 | #include "../../../include/linux/filter.h" |
| 43 | 43 | #include "testing_helpers.h" |
| 44 | 44 | |
| 45 | −#ifndef ENOTSUPP | |
| 46 | −#define ENOTSUPP 524 | |
| 47 | −#endif | |
| 48 | − | |
| 49 | 45 | #define MAX_INSNS BPF_MAXINSNS |
| 50 | 46 | #define MAX_EXPECTED_INSNS 32 |
| 51 | 47 | #define MAX_UNEXPECTED_INSNS 32 |
| @@ -12,6 +12,10 @@ · 11 unmodified lines | ||
| 12 | 12 | #define MADV_POPULATE_READ 22 |
| 13 | 13 | #endif |
| 14 | 14 | |
| 15 | +#ifndef MADV_PAGEOUT | |
| 16 | +#define MADV_PAGEOUT 21 | |
| 17 | +#endif | |
| 18 | + | |
| 15 | 19 | int __attribute__((weak)) uprobe(void) |
| 16 | 20 | { |
| 17 | 21 | return 0; |
| @@ -0,0 +1,63 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ | |
| 3 | + | |
| 4 | +#ifndef _UPTR_TEST_COMMON_H | |
| 5 | +#define _UPTR_TEST_COMMON_H | |
| 6 | + | |
| 7 | +#define MAGIC_VALUE 0xabcd1234 | |
| 8 | +#define PAGE_SIZE 4096 | |
| 9 | + | |
| 10 | +#ifdef __BPF__ | |
| 11 | +/* Avoid fwd btf type being generated for the following struct */ | |
| 12 | +struct large_data *dummy_large_data; | |
| 13 | +struct empty_data *dummy_empty_data; | |
| 14 | +struct user_data *dummy_data; | |
| 15 | +struct cgroup *dummy_cgrp; | |
| 16 | +#else | |
| 17 | +#define __uptr | |
| 18 | +#define __kptr | |
| 19 | +#endif | |
| 20 | + | |
| 21 | +struct user_data { | |
| 22 | + int a; | |
| 23 | + int b; | |
| 24 | + int result; | |
| 25 | + int nested_result; | |
| 26 | +}; | |
| 27 | + | |
| 28 | +struct nested_udata { | |
| 29 | + struct user_data __uptr *udata; | |
| 30 | +}; | |
| 31 | + | |
| 32 | +struct value_type { | |
| 33 | + struct user_data __uptr *udata; | |
| 34 | + struct cgroup __kptr *cgrp; | |
| 35 | + struct nested_udata nested; | |
| 36 | +}; | |
| 37 | + | |
| 38 | +struct value_lock_type { | |
| 39 | + struct user_data __uptr *udata; | |
| 40 | + struct bpf_spin_lock lock; | |
| 41 | +}; | |
| 42 | + | |
| 43 | +struct large_data { | |
| 44 | + __u8 one_page[PAGE_SIZE]; | |
| 45 | + int a; | |
| 46 | +}; | |
| 47 | + | |
| 48 | +struct large_uptr { | |
| 49 | + struct large_data __uptr *udata; | |
| 50 | +}; | |
| 51 | + | |
| 52 | +struct empty_data { | |
| 53 | +}; | |
| 54 | + | |
| 55 | +struct empty_uptr { | |
| 56 | + struct empty_data __uptr *udata; | |
| 57 | +}; | |
| 58 | + | |
| 59 | +struct kstruct_uptr { | |
| 60 | + struct cgroup __uptr *cgrp; | |
| 61 | +}; | |
| 62 | + | |
| 63 | +#endif | |
| @@ -16,6 +16,7 @@ · 15 unmodified lines | ||
| 16 | 16 | #include <sys/stat.h> |
| 17 | 17 | #include <bpf/libbpf.h> |
| 18 | 18 | #include <bpf/btf.h> |
| 19 | +#include <bpf/bpf.h> | |
| 19 | 20 | #include <libelf.h> |
| 20 | 21 | #include <gelf.h> |
| 21 | 22 | #include <float.h> |
| @@ -179,6 +180,7 @@ static struct env { · 157 unmodified lines | ||
| 179 | 180 | int files_skipped; |
| 180 | 181 | int progs_processed; |
| 181 | 182 | int progs_skipped; |
| 183 | + int top_src_lines; | |
| 182 | 184 | } env; |
| 183 | 185 | |
| 184 | 186 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) |
| @@ -228,6 +230,7 @@ static const struct argp_option opts[] = { · 43 unmodified lines | ||
| 228 | 230 | "Force frequent BPF verifier state checkpointing (set BPF_F_TEST_STATE_FREQ program flag)" }, |
| 229 | 231 | { "test-reg-invariants", 'r', NULL, 0, |
| 230 | 232 | "Force BPF verifier failure on register invariant violation (BPF_F_TEST_REG_INVARIANTS program flag)" }, |
| 233 | + { "top-src-lines", 'S', "N", 0, "Emit N most frequent source code lines" }, | |
| 231 | 234 | {}, |
| 232 | 235 | }; |
| 233 | 236 | |
| @@ -327,6 +330,14 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state) · 93 unmodified lines | ||
| 327 | 330 | return err; |
| 328 | 331 | } |
| 329 | 332 | break; |
| 333 | + case 'S': | |
| 334 | + errno = 0; | |
| 335 | + env.top_src_lines = strtol(arg, NULL, 10); | |
| 336 | + if (errno) { | |
| 337 | + fprintf(stderr, "invalid top lines N specifier: %s\n", arg); | |
| 338 | + argp_usage(state); | |
| 339 | + } | |
| 340 | + break; | |
| 330 | 341 | case ARGP_KEY_ARG: |
| 331 | 342 | tmp = realloc(env.filenames, (env.filename_cnt + 1) * sizeof(*env.filenames)); |
| 332 | 343 | if (!tmp) |
| @@ -854,6 +865,118 @@ static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats * · 521 unmodified lines | ||
| 854 | 865 | return 0; |
| 855 | 866 | } |
| 856 | 867 | |
| 868 | +struct line_cnt { | |
| 869 | + char *line; | |
| 870 | + int cnt; | |
| 871 | +}; | |
| 872 | + | |
| 873 | +static int str_cmp(const void *a, const void *b) | |
| 874 | +{ | |
| 875 | + const char **str1 = (const char **)a; | |
| 876 | + const char **str2 = (const char **)b; | |
| 877 | + | |
| 878 | + return strcmp(*str1, *str2); | |
| 879 | +} | |
| 880 | + | |
| 881 | +static int line_cnt_cmp(const void *a, const void *b) | |
| 882 | +{ | |
| 883 | + const struct line_cnt *a_cnt = (const struct line_cnt *)a; | |
| 884 | + const struct line_cnt *b_cnt = (const struct line_cnt *)b; | |
| 885 | + | |
| 886 | + if (a_cnt->cnt != b_cnt->cnt) | |
| 887 | + return a_cnt->cnt < b_cnt->cnt ? -1 : 1; | |
| 888 | + return strcmp(a_cnt->line, b_cnt->line); | |
| 889 | +} | |
| 890 | + | |
| 891 | +static int print_top_src_lines(char * const buf, size_t buf_sz, const char *prog_name) | |
| 892 | +{ | |
| 893 | + int lines_cap = 0; | |
| 894 | + int lines_size = 0; | |
| 895 | + char **lines = NULL; | |
| 896 | + char *line = NULL; | |
| 897 | + char *state; | |
| 898 | + struct line_cnt *freq = NULL; | |
| 899 | + struct line_cnt *cur; | |
| 900 | + int unique_lines; | |
| 901 | + int err = 0; | |
| 902 | + int i; | |
| 903 | + | |
| 904 | + while ((line = strtok_r(line ? NULL : buf, "\n", &state))) { | |
| 905 | + if (strncmp(line, "; ", 2) != 0) | |
| 906 | + continue; | |
| 907 | + line += 2; | |
| 908 | + | |
| 909 | + if (lines_size == lines_cap) { | |
| 910 | + char **tmp; | |
| 911 | + | |
| 912 | + lines_cap = max(16, lines_cap * 2); | |
| 913 | + tmp = realloc(lines, lines_cap * sizeof(*tmp)); | |
| 914 | + if (!tmp) { | |
| 915 | + err = -ENOMEM; | |
| 916 | + goto cleanup; | |
| 917 | + } | |
| 918 | + lines = tmp; | |
| 919 | + } | |
| 920 | + lines[lines_size] = line; | |
| 921 | + lines_size++; | |
| 922 | + } | |
| 923 | + | |
| 924 | + if (lines_size == 0) | |
| 925 | + goto cleanup; | |
| 926 | + | |
| 927 | + qsort(lines, lines_size, sizeof(*lines), str_cmp); | |
| 928 | + | |
| 929 | + freq = calloc(lines_size, sizeof(*freq)); | |
| 930 | + if (!freq) { | |
| 931 | + err = -ENOMEM; | |
| 932 | + goto cleanup; | |
| 933 | + } | |
| 934 | + | |
| 935 | + cur = freq; | |
| 936 | + cur->line = lines[0]; | |
| 937 | + cur->cnt = 1; | |
| 938 | + for (i = 1; i < lines_size; ++i) { | |
| 939 | + if (strcmp(lines[i], cur->line) != 0) { | |
| 940 | + cur++; | |
| 941 | + cur->line = lines[i]; | |
| 942 | + cur->cnt = 0; | |
| 943 | + } | |
| 944 | + cur->cnt++; | |
| 945 | + } | |
| 946 | + unique_lines = cur - freq + 1; | |
| 947 | + | |
| 948 | + qsort(freq, unique_lines, sizeof(struct line_cnt), line_cnt_cmp); | |
| 949 | + | |
| 950 | + printf("Top source lines (%s):\n", prog_name); | |
| 951 | + for (i = 0; i < min(unique_lines, env.top_src_lines); ++i) { | |
| 952 | + const char *src_code = freq[i].line; | |
| 953 | + const char *src_line = NULL; | |
| 954 | + char *split = strrchr(freq[i].line, '@'); | |
| 955 | + | |
| 956 | + if (split) { | |
| 957 | + src_line = split + 1; | |
| 958 | + | |
| 959 | + while (*src_line && isspace(*src_line)) | |
| 960 | + src_line++; | |
| 961 | + | |
| 962 | + while (split > src_code && isspace(*split)) | |
| 963 | + split--; | |
| 964 | + *split = '\0'; | |
| 965 | + } | |
| 966 | + | |
| 967 | + if (src_line) | |
| 968 | + printf("%5d: (%s)\t%s\n", freq[i].cnt, src_line, src_code); | |
| 969 | + else | |
| 970 | + printf("%5d: %s\n", freq[i].cnt, src_code); | |
| 971 | + } | |
| 972 | + printf("\n"); | |
| 973 | + | |
| 974 | +cleanup: | |
| 975 | + free(freq); | |
| 976 | + free(lines); | |
| 977 | + return err; | |
| 978 | +} | |
| 979 | + | |
| 857 | 980 | static int guess_prog_type_by_ctx_name(const char *ctx_name, |
| 858 | 981 | enum bpf_prog_type *prog_type, |
| 859 | 982 | enum bpf_attach_type *attach_type) |
| @@ -987,6 +1110,35 @@ skip_freplace_fixup: · 127 unmodified lines | ||
| 987 | 1110 | return; |
| 988 | 1111 | } |
| 989 | 1112 | |
| 1113 | +static int max_verifier_log_size(void) | |
| 1114 | +{ | |
| 1115 | + const int SMALL_LOG_SIZE = UINT_MAX >> 8; | |
| 1116 | + const int BIG_LOG_SIZE = UINT_MAX >> 2; | |
| 1117 | + struct bpf_insn insns[] = { | |
| 1118 | + { .code = BPF_ALU | BPF_MOV | BPF_X, .dst_reg = BPF_REG_0, }, | |
| 1119 | + { .code = BPF_JMP | BPF_EXIT, }, | |
| 1120 | + }; | |
| 1121 | + LIBBPF_OPTS(bpf_prog_load_opts, opts, | |
| 1122 | + .log_size = BIG_LOG_SIZE, | |
| 1123 | + .log_buf = (void *)-1, | |
| 1124 | + .log_level = 4 | |
| 1125 | + ); | |
| 1126 | + int ret, insn_cnt = ARRAY_SIZE(insns); | |
| 1127 | + static int log_size; | |
| 1128 | + | |
| 1129 | + if (log_size != 0) | |
| 1130 | + return log_size; | |
| 1131 | + | |
| 1132 | + ret = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, NULL, "GPL", insns, insn_cnt, &opts); | |
| 1133 | + | |
| 1134 | + if (ret == -EFAULT) | |
| 1135 | + log_size = BIG_LOG_SIZE; | |
| 1136 | + else /* ret == -EINVAL, big log size is not supported by the verifier */ | |
| 1137 | + log_size = SMALL_LOG_SIZE; | |
| 1138 | + | |
| 1139 | + return log_size; | |
| 1140 | +} | |
| 1141 | + | |
| 990 | 1142 | static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog) |
| 991 | 1143 | { |
| 992 | 1144 | const char *base_filename = basename(strdupa(filename)); |
| @@ -1009,13 +1161,16 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf · 16 unmodified lines | ||
| 1009 | 1161 | stats = &env.prog_stats[env.prog_stat_cnt++]; |
| 1010 | 1162 | memset(stats, 0, sizeof(*stats)); |
| 1011 | 1163 | |
| 1012 | − if (env.verbose) { | |
| 1013 | − buf_sz = env.log_size ? env.log_size : 16 * 1024 * 1024; | |
| 1164 | + if (env.verbose || env.top_src_lines > 0) { | |
| 1165 | + buf_sz = env.log_size ? env.log_size : max_verifier_log_size(); | |
| 1014 | 1166 | buf = malloc(buf_sz); |
| 1015 | 1167 | if (!buf) |
| 1016 | 1168 | return -ENOMEM; |
| 1017 | 1169 | /* ensure we always request stats */ |
| 1018 | 1170 | log_level = env.log_level | 4 | (env.log_fixed ? 8 : 0); |
| 1171 | + /* --top-src-lines needs verifier log */ | |
| 1172 | + if (env.top_src_lines > 0 && env.log_level == 0) | |
| 1173 | + log_level |= 2; | |
| 1019 | 1174 | } else { |
| 1020 | 1175 | buf = verif_log_buf; |
| 1021 | 1176 | buf_sz = sizeof(verif_log_buf); |
| @@ -1048,6 +1203,8 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf · 26 unmodified lines | ||
| 1048 | 1203 | filename, prog_name, stats->stats[DURATION], |
| 1049 | 1204 | err ? "failure" : "success", buf); |
| 1050 | 1205 | } |
| 1206 | + if (env.top_src_lines > 0) | |
| 1207 | + print_top_src_lines(buf, buf_sz, stats->prog_name); | |
| 1051 | 1208 | |
| 1052 | 1209 | if (verif_log_buf != buf) |
| 1053 | 1210 | free(buf); |