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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'bpf-indirect-jumps'

Anton Protopopov says:

====================
BPF indirect jumps

This patchset implements a new type of map, instruction set, and uses
it to build support for indirect branches in BPF (on x86). (The same
map will be later used to provide support for indirect calls and static
keys.) See [1], [2] for more context.

Short table of contents:

* Patches 1-6 implement the new map of type
BPF_MAP_TYPE_INSN_SET and corresponding selftests. This map can
be used to track the "original -> xlated -> jitted mapping" for
a given program.

* Patches 7-12 implement the support for indirect jumps on x86 and add libbpf
support for LLVM-compiled programs containing indirect jumps, and selftests.

The jump table support was merged to LLVM and now can be
enabled with -mcpu=v4, see [3]. The __BPF_FEATURE_GOTOX
macros can be used to check if the compiler supports the
feature or not.

See individual patches for more details on the implementation details.

v10 -> v11 (this series):

* rearranged patches and split libbpf patch such that first 6 patches
implementing instruction arrays can be applied independently

* instruction arrays:
* move [fake] aux->used_maps assignment in this patch

* indirect jumps:
* call clear_insn_aux_data before bpf_remove_insns (AI)

* libbpf:
* remove the relocations check after the new LLVM is released (Eduard, Yonghong)
* libbpf: fix an index printed in pr_warn (AI)

* selftests:
* protect programs triggered by nanosleep from fake runs (Eduard)
* patch verifier_gotox to not emit .rel.jumptables

v9 -> v10 (https://lore.kernel.org/bpf/20251102205722.3266908-1-a.s.protopopov@gmail.com/T/#t):

* Three bugs were noticed by AI in v9 (two old, one introduced by v9):
* [new] insn_array_alloc_size could overflow u32, switched to u64 (AI)
* map_ptr should be compared in regsafe for PTR_TO_INSN (AI)
* duplicate elements were copied in jt_from_map (AI)

* added a selftest in verifier_gotox with a jump table containing non-unique entries

v8 -> v9 (https://lore.kernel.org/bpf/20251101110717.2860949-1-a.s.protopopov@gmail.com/T/#t):

* instruction arrays:
* remove the size restriction of 256 elements
* add a comments about addrs usage, old and new (Alexei)

* libbpf:
* properly prefix warnings (Andrii)
* cast j[t] to long long for printf and some other minor cleanups (Andrii)

* selftests:
* use __BPF_FEATURE_GOTOX in selftests and skip tests if it's not set (Eduard)
* fix a typo in a selftest assembly (AI)

v7 -> v8 (https://lore.kernel.org/bpf/20251028142049.1324520-1-a.s.protopopov@gmail.com/T/#u):

* instruction arrays:
* simplify the bpf_prog_update_insn_ptrs function (Eduard)
* remove a semicolon after a function definition (AI)

* libbpf:
* add a proper error path in libbpf patch (AI)
* re-re-factor the create_jt_map & find_subprog_idx (Eduard)

* selftests:
* verifier_gotox: add a test for a jump table pointing to outside of a subprog (Eduard)
* used test__skip instead of just running an empty test
* split tests in bpf_gotox into subtests for convenience

* random:
* drop the docs commit for now

v6 -> v7 (https://lore.kernel.org/bpf/20251026192709.1964787-1-a.s.protopopov@gmail.com/T/#t):

* rebased and dropped already merged commits

* instruction arrays
* use jit_data to find mappings from insn to jit (Alexei)
* alloc `ips` as part of the main allocation (Eduard)
* the `jitted_ip` member wasn't actually used (Eduard)
* remove the bpf_insn_ptr structure, which is not needed for this patch

* indirect jumps, kernel:
* fix a memory leak in `create_jt` (AI)
* use proper reg+8*ereg in `its_static_thunk` (AI)
* some minor cleanups (Eduard)

* indirect jumps, libbpf:
* refactor the `jt_adjust_off()` piece (Edurad)
* move "JUMPTABLES_SEC" into libbpf_internal.h (Eduard)
* remove an unnecessary if (Eduard)

* verifier_gotox: add tests to verify that `gotox rX` works with all registers

v5 -> v6 (https://lore.kernel.org/bpf/20251019202145.3944697-1-a.s.protopopov@gmail.com/T/#u):

* instruction arrays:
* better document `struct bpf_insn_array_value` (Eduard)
* remove a condition in `bpf_insn_array_adjust_after_remove` (Eduard)
* make userspace see original, xlated, and jitted indexes (+original) (Eduard)

* indirect jumps, kernel:
* reject writes to the map
* reject unaligned ops
* add a check what `w` is not outside the program in check_config for `gotox` (Eduard)
* do not introduce unneeded `bpf_find_containing_subprog_idx`
* simplify error processing for `bpf_find_containing_subprog` (Eduard)
* add `insn_state |= DISCOVERED` when it's discovered (Eduard)
* support SUB operations on PTR_TO_INSN (Eduard)
* make `gotox_tmp_buf` a bpf_iarray and use helper to relocate it (Eduard)
* rename fields of `bpf_iarray` to more generic (Eduard)
* re-implement `visit_gotox_insn` in a loop (Eduard)
* some minor cleanups (Eduard)

* libbpf:
* `struct reloc_desc`: add a comment about `union` (Eduard)
* rename parameters of (and one other place in code) `{create,add}_jt_map` to `sym_off` (Eduard)
* `create_jt_map`: check that size/off are 8-byte aligned (Eduard)

* Selftests:
* instruction array selftests:
* only run tests on x86_64
* write a more generic function to test things to reduce code (Eduard)
* errno wasn't used in checks, so don't reset it (Eduard)
* print `i`, `xlated_off` and `map_out[i]` here (Eduard)
* added `verifier_gotox` selftests which do not depend on LLVM:
* disabled `bpf_gotox` tests by default

* other changes:
* remove an extra function in bpf disasm (Eduard)
* some minor cleanups in the insn_successors patch (Eduard)
* update documentation in `Documentation/bpf/linux-notes.html` about jumps, now it is supported :)

v3 -> v4 -> v5 (https://lore.kernel.org/bpf/20250930125111.1269861-1-a.s.protopopov@gmail.com/):

* [v4 -> v5] rebased on top of the last bpf-next/master

* instruction arrays:
* add copyright (Alexei)
* remove mutexes, add frozen back (Alexei)
* setup 1:1 prog-map correspondence using atomic_xchg
* do not copy/paste array_map_get_next_key, add a common helper (Alexei)
* misc minor code cleanups (Alexei)

* indirect jumps, kernel side:
* remove jt_allocated, just check if insn is gotox (Eduard)
* use copy_register_state instead of individual copies (Eduard)
* in push_stack is_speculative should be inherited (Eduard)
* a few cleanups for insn_successors, including omitting error path (Eduard)
* check if reserved fields are used when considering `gotox` instruction (Eduard)
* read size and alignment of read from insn_array should be 8 (Eduard)
* put buffer for sorting in subfun info and realloc to grow as needed (Eduard)
* properly do `jump_point` / `prune_point` from `push_gotox_edge` (Eduard)
* use range_within to check states (Eduard)
* some minor cleanups and fix commit message (Eduard)

* indirect jumps, libbpf side:
* close map_fd in some error paths in create_jt_map (Andrii)
* maps for jump tables are actually not closed at all, fix this (Andrii)
* rename map from `jt` to `.jumptables` (Andrii)
* use `errstr` in an error message (Andrii)
* rephrase error message to look more standard (Andrii)
* misc other minor renames and cleanups (Andrii)

* selftests:
* add the frozen selftest back
* add a selftest for two jumps loading same table

* some other changes:
* rebase and split insn_successor changes into separate patch
* use PTR_ERR_OR_ZERO in the push stack patch (Eduard)
* indirect jumps on x86: properly re-read *pprog (Eduard)

v2 -> v3 (https://lore.kernel.org/bpf/20250918093850.455051-1-a.s.protopopov@gmail.com/):

* fix build failure when CONFIG_BPF_SYSCALL is not set (kbuild-bot)
* reformat bpftool help messages (Quentin)

v1 -> v2 (https://lore.kernel.org/bpf/20250913193922.1910480-1-a.s.protopopov@gmail.com/):

* push_stack changes:
* sanitize_speculative_path should just return int (Eduard)
* return code from sanitize_speculative_path, not EFAULT (Eduard)
* when BPF_COMPLEXITY_LIMIT_JMP_SEQ is reached, return E2BIG (Eduard)

* indirect jumps:
* omit support for .imm=fd in gotox, as we're not using it for now (Eduard)
* struct jt -> struct bpf_iarray (Eduard)
* insn_successors: rewrite the interface to just return a pointer (Eduard)
* remove min_index/max_index, use umin_value/umax_value instead (Alexei, Eduard)
* move emit_indirect_jump args change to the previous patch (Eduard)
* add a comment to map_mem_size() (Eduard)
* use verifier_bug for some error cases in check_indirect_jump (Eduard)
* clear_insn_aux_data: use start,len instead of start,end (Eduard)
* make regs[insn->dst_reg].type = PTR_TO_INSN part of check_mem_access (Eduard)

* constant blinding changes:
* make subprog_start adjustment better readable (Eduard)
* do not set subprog len, it is already set (Eduard)

* libbpf:
* remove check that relocations from .rodata are ok (Anton)
* do not freeze the map, it is not necessary anymore (Anton)
* rename the goto_x -> gotox everywhere (Anton)
* use u64 when parsing LLVM jump tables (Eduard)
* split patch in two due to spaces->tabs change (Eduard)
* split bpftool changes to bpftool patch (Andrii)
* make sym_size it a union with ext_idx (Andrii)
* properly copy/free the jumptables_data section from elf (Andrii)
* a few cosmetic changes around create_jt_map (Andrii)
* fix some comments + rewrite patch description (Andrii)
* inline bpf_prog__append_subprog_offsets (Andrii)
* subprog_sec_offst -> subprog_sec_off (Andrii)
* !strcmp -> strcmp() == 0 (Andrii)
* make some function names more readable (Andrii)
* allocate table of subfunc offsets via libbpf_reallocarray (Andrii)

* selftests:
* squash insn_array* tests together (Anton)

* fixed build warnings (kernel test robot)

RFC -> v1 (https://lore.kernel.org/bpf/20250816180631.952085-1-a.s.protopopov@gmail.com/):

* I've tried to address all the comments provided by Alexei and
Eduard in RFC. Will try to list the most important of them below.
* One big change: move from older LLVM version [5] to newer [4].
Now LLVM generates jump tables as symbols in the new special
section ".jumptables". Another part of this change is that
libbpf now doesn't try to link map load and goto *rX, as
1) this is absolutely not reliable 2) for some use cases this
is impossible (namely, when more than one jump table can be used
in the same gotox instruction).
* Added insn_successors() support (Alexei, Eduard). This includes
getting rid of the ugly bpf_insn_set_iter_xlated_offset()
interface (Eduard).
* Removed hack for the unreachable instruction, as new LLVM thank to
Eduard doesn't generate it.
* Set mem_size for direct map access properly instead of hacking.
Remove off>0 check. (Alexei)
* Do not allocate new memory for min_index/max_index (Alexei, Eduard)
* Information required during check_cfg is now cached to be reused
later (Alexei + general logic for supporting multiple JT per jump)
* Properly compare registers in regsafe (Alexei, Eduard)
* Remove support for JMP32 (Eduard)
* Better checks in adjust_ptr_min_max_vals (Eduard)
* More selftests were added (but still there's room for more) which
directly use gotox (Alexei)
* More checks and verbose messages added
* "unique pointers" are no more in the map

Links:
1. https://lpc.events/event/18/contributions/1941/
2. https://lwn.net/Articles/1017439/
3. https://github.com/llvm/llvm-project/pull/149715
4. https://github.com/llvm/llvm-project/pull/149715#issuecomment-3274833753
6. rfc: https://lore.kernel.org/bpf/20250615085943.3871208-1-a.s.protopopov@gmail.com/
====================

Link: https://patch.msgid.link/20251105090410.1250500-1-a.s.protopopov@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+2776 -20
+34 -8
arch/x86/net/bpf_jit_comp.c
··· 660 660 661 661 #define EMIT_LFENCE() EMIT3(0x0F, 0xAE, 0xE8) 662 662 663 - static void emit_indirect_jump(u8 **pprog, int reg, u8 *ip) 663 + static void __emit_indirect_jump(u8 **pprog, int reg, bool ereg) 664 664 { 665 665 u8 *prog = *pprog; 666 666 667 + if (ereg) 668 + EMIT1(0x41); 669 + 670 + EMIT2(0xFF, 0xE0 + reg); 671 + 672 + *pprog = prog; 673 + } 674 + 675 + static void emit_indirect_jump(u8 **pprog, int bpf_reg, u8 *ip) 676 + { 677 + u8 *prog = *pprog; 678 + int reg = reg2hex[bpf_reg]; 679 + bool ereg = is_ereg(bpf_reg); 680 + 667 681 if (cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) { 668 682 OPTIMIZER_HIDE_VAR(reg); 669 - emit_jump(&prog, its_static_thunk(reg), ip); 683 + emit_jump(&prog, its_static_thunk(reg + 8*ereg), ip); 670 684 } else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) { 671 685 EMIT_LFENCE(); 672 - EMIT2(0xFF, 0xE0 + reg); 686 + __emit_indirect_jump(&prog, reg, ereg); 673 687 } else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE)) { 674 688 OPTIMIZER_HIDE_VAR(reg); 675 689 if (cpu_feature_enabled(X86_FEATURE_CALL_DEPTH)) 676 - emit_jump(&prog, &__x86_indirect_jump_thunk_array[reg], ip); 690 + emit_jump(&prog, &__x86_indirect_jump_thunk_array[reg + 8*ereg], ip); 677 691 else 678 - emit_jump(&prog, &__x86_indirect_thunk_array[reg], ip); 692 + emit_jump(&prog, &__x86_indirect_thunk_array[reg + 8*ereg], ip); 679 693 } else { 680 - EMIT2(0xFF, 0xE0 + reg); /* jmp *%\reg */ 694 + __emit_indirect_jump(&prog, reg, ereg); 681 695 if (IS_ENABLED(CONFIG_MITIGATION_RETPOLINE) || IS_ENABLED(CONFIG_MITIGATION_SLS)) 682 696 EMIT1(0xCC); /* int3 */ 683 697 } ··· 811 797 * rdi == ctx (1st arg) 812 798 * rcx == prog->bpf_func + X86_TAIL_CALL_OFFSET 813 799 */ 814 - emit_indirect_jump(&prog, 1 /* rcx */, ip + (prog - start)); 800 + emit_indirect_jump(&prog, BPF_REG_4 /* R4 -> rcx */, ip + (prog - start)); 815 801 816 802 /* out: */ 817 803 ctx->tail_call_indirect_label = prog - start; ··· 2628 2614 2629 2615 break; 2630 2616 2617 + case BPF_JMP | BPF_JA | BPF_X: 2618 + emit_indirect_jump(&prog, insn->dst_reg, image + addrs[i - 1]); 2619 + break; 2631 2620 case BPF_JMP | BPF_JA: 2632 2621 case BPF_JMP32 | BPF_JA: 2633 2622 if (BPF_CLASS(insn->code) == BPF_JMP) { ··· 3560 3543 if (err) 3561 3544 return err; 3562 3545 3563 - emit_indirect_jump(&prog, 2 /* rdx */, image + (prog - buf)); 3546 + emit_indirect_jump(&prog, BPF_REG_3 /* R3 -> rdx */, image + (prog - buf)); 3564 3547 3565 3548 *pprog = prog; 3566 3549 return 0; ··· 3844 3827 jit_data->header = header; 3845 3828 jit_data->rw_header = rw_header; 3846 3829 } 3830 + 3831 + /* 3832 + * The bpf_prog_update_insn_ptrs function expects addrs to 3833 + * point to the first byte of the jitted instruction (unlike 3834 + * the bpf_prog_fill_jited_linfo below, which, for historical 3835 + * reasons, expects to point to the next instruction) 3836 + */ 3837 + bpf_prog_update_insn_ptrs(prog, addrs, image); 3838 + 3847 3839 /* 3848 3840 * ctx.prog_offset is used when CFI preambles put code *before* 3849 3841 * the function. See emit_cfi(). For FineIBT specifically this code
+16
include/linux/bpf.h
··· 1001 1001 PTR_TO_ARENA, 1002 1002 PTR_TO_BUF, /* reg points to a read/write buffer */ 1003 1003 PTR_TO_FUNC, /* reg points to a bpf program function */ 1004 + PTR_TO_INSN, /* reg points to a bpf program instruction */ 1004 1005 CONST_PTR_TO_DYNPTR, /* reg points to a const struct bpf_dynptr */ 1005 1006 __BPF_REG_TYPE_MAX, 1006 1007 ··· 3797 3796 int bpf_prog_get_file_line(struct bpf_prog *prog, unsigned long ip, const char **filep, 3798 3797 const char **linep, int *nump); 3799 3798 struct bpf_prog *bpf_prog_find_from_stack(void); 3799 + 3800 + int bpf_insn_array_init(struct bpf_map *map, const struct bpf_prog *prog); 3801 + int bpf_insn_array_ready(struct bpf_map *map); 3802 + void bpf_insn_array_release(struct bpf_map *map); 3803 + void bpf_insn_array_adjust(struct bpf_map *map, u32 off, u32 len); 3804 + void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len); 3805 + 3806 + #ifdef CONFIG_BPF_SYSCALL 3807 + void bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image); 3808 + #else 3809 + static inline void 3810 + bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image) 3811 + { 3812 + } 3813 + #endif 3800 3814 3801 3815 #endif /* _LINUX_BPF_H */
+1
include/linux/bpf_types.h
··· 133 133 BPF_MAP_TYPE(BPF_MAP_TYPE_BLOOM_FILTER, bloom_filter_map_ops) 134 134 BPF_MAP_TYPE(BPF_MAP_TYPE_USER_RINGBUF, user_ringbuf_map_ops) 135 135 BPF_MAP_TYPE(BPF_MAP_TYPE_ARENA, arena_map_ops) 136 + BPF_MAP_TYPE(BPF_MAP_TYPE_INSN_ARRAY, insn_array_map_ops) 136 137 137 138 BPF_LINK_TYPE(BPF_LINK_TYPE_RAW_TRACEPOINT, raw_tracepoint) 138 139 BPF_LINK_TYPE(BPF_LINK_TYPE_TRACING, tracing)
+11
include/linux/bpf_verifier.h
··· 527 527 struct { 528 528 u32 map_index; /* index into used_maps[] */ 529 529 u32 map_off; /* offset from value base address */ 530 + struct bpf_iarray *jt; /* jump table for gotox instruction */ 530 531 }; 531 532 struct { 532 533 enum bpf_reg_type reg_type; /* type of pseudo_btf_id */ ··· 755 754 struct list_head free_list; /* list of struct bpf_verifier_state_list */ 756 755 struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */ 757 756 struct btf_mod_pair used_btfs[MAX_USED_BTFS]; /* array of BTF's used by BPF program */ 757 + struct bpf_map *insn_array_maps[MAX_USED_MAPS]; /* array of INSN_ARRAY map's to be relocated */ 758 758 u32 used_map_cnt; /* number of used maps */ 759 759 u32 used_btf_cnt; /* number of used BTF objects */ 760 + u32 insn_array_map_cnt; /* number of used maps of type BPF_MAP_TYPE_INSN_ARRAY */ 760 761 u32 id_gen; /* used to generate unique reg IDs */ 761 762 u32 hidden_subprog_cnt; /* number of hidden subprogs */ 762 763 int exception_callback_subprog; ··· 841 838 struct bpf_scc_info **scc_info; 842 839 u32 scc_cnt; 843 840 struct bpf_iarray *succ; 841 + struct bpf_iarray *gotox_tmp_buf; 844 842 }; 845 843 846 844 static inline struct bpf_func_info_aux *subprog_aux(struct bpf_verifier_env *env, int subprog) ··· 1050 1046 #endif 1051 1047 1052 1048 return !(off % BPF_REG_SIZE); 1049 + } 1050 + 1051 + static inline bool insn_is_gotox(struct bpf_insn *insn) 1052 + { 1053 + return BPF_CLASS(insn->code) == BPF_JMP && 1054 + BPF_OP(insn->code) == BPF_JA && 1055 + BPF_SRC(insn->code) == BPF_X; 1053 1056 } 1054 1057 1055 1058 const char *reg_type_str(struct bpf_verifier_env *env, enum bpf_reg_type type);
+21
include/uapi/linux/bpf.h
··· 1026 1026 BPF_MAP_TYPE_USER_RINGBUF, 1027 1027 BPF_MAP_TYPE_CGRP_STORAGE, 1028 1028 BPF_MAP_TYPE_ARENA, 1029 + BPF_MAP_TYPE_INSN_ARRAY, 1029 1030 __MAX_BPF_MAP_TYPE 1030 1031 }; 1031 1032 ··· 7648 7647 */ 7649 7648 enum bpf_kfunc_flags { 7650 7649 BPF_F_PAD_ZEROS = (1ULL << 0), 7650 + }; 7651 + 7652 + /* 7653 + * Values of a BPF_MAP_TYPE_INSN_ARRAY entry must be of this type. 7654 + * 7655 + * Before the map is used the orig_off field should point to an 7656 + * instruction inside the program being loaded. The other fields 7657 + * must be set to 0. 7658 + * 7659 + * After the program is loaded, the xlated_off will be adjusted 7660 + * by the verifier to point to the index of the original instruction 7661 + * in the xlated program. If the instruction is deleted, it will 7662 + * be set to (u32)-1. The jitted_off will be set to the corresponding 7663 + * offset in the jitted image of the program. 7664 + */ 7665 + struct bpf_insn_array_value { 7666 + __u32 orig_off; 7667 + __u32 xlated_off; 7668 + __u32 jitted_off; 7669 + __u32 :32; 7651 7670 }; 7652 7671 7653 7672 #endif /* _UAPI__LINUX_BPF_H__ */
+1 -1
kernel/bpf/Makefile
··· 9 9 obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o log.o token.o liveness.o 10 10 obj-$(CONFIG_BPF_SYSCALL) += bpf_iter.o map_iter.o task_iter.o prog_iter.o link_iter.o 11 11 obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o bloom_filter.o 12 - obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o 12 + obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o bpf_insn_array.o 13 13 obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o 14 14 obj-${CONFIG_BPF_LSM} += bpf_inode_storage.o 15 15 obj-$(CONFIG_BPF_SYSCALL) += disasm.o mprog.o
+301
kernel/bpf/bpf_insn_array.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Copyright (c) 2025 Isovalent */ 3 + 4 + #include <linux/bpf.h> 5 + 6 + struct bpf_insn_array { 7 + struct bpf_map map; 8 + atomic_t used; 9 + long *ips; 10 + DECLARE_FLEX_ARRAY(struct bpf_insn_array_value, values); 11 + }; 12 + 13 + #define cast_insn_array(MAP_PTR) \ 14 + container_of((MAP_PTR), struct bpf_insn_array, map) 15 + 16 + #define INSN_DELETED ((u32)-1) 17 + 18 + static inline u64 insn_array_alloc_size(u32 max_entries) 19 + { 20 + const u64 base_size = sizeof(struct bpf_insn_array); 21 + const u64 entry_size = sizeof(struct bpf_insn_array_value); 22 + 23 + return base_size + max_entries * (entry_size + sizeof(long)); 24 + } 25 + 26 + static int insn_array_alloc_check(union bpf_attr *attr) 27 + { 28 + u32 value_size = sizeof(struct bpf_insn_array_value); 29 + 30 + if (attr->max_entries == 0 || attr->key_size != 4 || 31 + attr->value_size != value_size || attr->map_flags != 0) 32 + return -EINVAL; 33 + 34 + return 0; 35 + } 36 + 37 + static void insn_array_free(struct bpf_map *map) 38 + { 39 + struct bpf_insn_array *insn_array = cast_insn_array(map); 40 + 41 + bpf_map_area_free(insn_array); 42 + } 43 + 44 + static struct bpf_map *insn_array_alloc(union bpf_attr *attr) 45 + { 46 + u64 size = insn_array_alloc_size(attr->max_entries); 47 + struct bpf_insn_array *insn_array; 48 + 49 + insn_array = bpf_map_area_alloc(size, NUMA_NO_NODE); 50 + if (!insn_array) 51 + return ERR_PTR(-ENOMEM); 52 + 53 + /* ips are allocated right after the insn_array->values[] array */ 54 + insn_array->ips = (void *)&insn_array->values[attr->max_entries]; 55 + 56 + bpf_map_init_from_attr(&insn_array->map, attr); 57 + 58 + return &insn_array->map; 59 + } 60 + 61 + static void *insn_array_lookup_elem(struct bpf_map *map, void *key) 62 + { 63 + struct bpf_insn_array *insn_array = cast_insn_array(map); 64 + u32 index = *(u32 *)key; 65 + 66 + if (unlikely(index >= insn_array->map.max_entries)) 67 + return NULL; 68 + 69 + return &insn_array->values[index]; 70 + } 71 + 72 + static long insn_array_update_elem(struct bpf_map *map, void *key, void *value, u64 map_flags) 73 + { 74 + struct bpf_insn_array *insn_array = cast_insn_array(map); 75 + u32 index = *(u32 *)key; 76 + struct bpf_insn_array_value val = {}; 77 + 78 + if (unlikely(index >= insn_array->map.max_entries)) 79 + return -E2BIG; 80 + 81 + if (unlikely(map_flags & BPF_NOEXIST)) 82 + return -EEXIST; 83 + 84 + copy_map_value(map, &val, value); 85 + if (val.jitted_off || val.xlated_off) 86 + return -EINVAL; 87 + 88 + insn_array->values[index].orig_off = val.orig_off; 89 + 90 + return 0; 91 + } 92 + 93 + static long insn_array_delete_elem(struct bpf_map *map, void *key) 94 + { 95 + return -EINVAL; 96 + } 97 + 98 + static int insn_array_check_btf(const struct bpf_map *map, 99 + const struct btf *btf, 100 + const struct btf_type *key_type, 101 + const struct btf_type *value_type) 102 + { 103 + if (!btf_type_is_i32(key_type)) 104 + return -EINVAL; 105 + 106 + if (!btf_type_is_i64(value_type)) 107 + return -EINVAL; 108 + 109 + return 0; 110 + } 111 + 112 + static u64 insn_array_mem_usage(const struct bpf_map *map) 113 + { 114 + return insn_array_alloc_size(map->max_entries); 115 + } 116 + 117 + static int insn_array_map_direct_value_addr(const struct bpf_map *map, u64 *imm, u32 off) 118 + { 119 + struct bpf_insn_array *insn_array = cast_insn_array(map); 120 + 121 + if ((off % sizeof(long)) != 0 || 122 + (off / sizeof(long)) >= map->max_entries) 123 + return -EINVAL; 124 + 125 + /* from BPF's point of view, this map is a jump table */ 126 + *imm = (unsigned long)insn_array->ips + off; 127 + 128 + return 0; 129 + } 130 + 131 + BTF_ID_LIST_SINGLE(insn_array_btf_ids, struct, bpf_insn_array) 132 + 133 + const struct bpf_map_ops insn_array_map_ops = { 134 + .map_alloc_check = insn_array_alloc_check, 135 + .map_alloc = insn_array_alloc, 136 + .map_free = insn_array_free, 137 + .map_get_next_key = bpf_array_get_next_key, 138 + .map_lookup_elem = insn_array_lookup_elem, 139 + .map_update_elem = insn_array_update_elem, 140 + .map_delete_elem = insn_array_delete_elem, 141 + .map_check_btf = insn_array_check_btf, 142 + .map_mem_usage = insn_array_mem_usage, 143 + .map_direct_value_addr = insn_array_map_direct_value_addr, 144 + .map_btf_id = &insn_array_btf_ids[0], 145 + }; 146 + 147 + static inline bool is_frozen(struct bpf_map *map) 148 + { 149 + guard(mutex)(&map->freeze_mutex); 150 + 151 + return map->frozen; 152 + } 153 + 154 + static bool is_insn_array(const struct bpf_map *map) 155 + { 156 + return map->map_type == BPF_MAP_TYPE_INSN_ARRAY; 157 + } 158 + 159 + static inline bool valid_offsets(const struct bpf_insn_array *insn_array, 160 + const struct bpf_prog *prog) 161 + { 162 + u32 off; 163 + int i; 164 + 165 + for (i = 0; i < insn_array->map.max_entries; i++) { 166 + off = insn_array->values[i].orig_off; 167 + 168 + if (off >= prog->len) 169 + return false; 170 + 171 + if (off > 0) { 172 + if (prog->insnsi[off-1].code == (BPF_LD | BPF_DW | BPF_IMM)) 173 + return false; 174 + } 175 + } 176 + 177 + return true; 178 + } 179 + 180 + int bpf_insn_array_init(struct bpf_map *map, const struct bpf_prog *prog) 181 + { 182 + struct bpf_insn_array *insn_array = cast_insn_array(map); 183 + struct bpf_insn_array_value *values = insn_array->values; 184 + int i; 185 + 186 + if (!is_frozen(map)) 187 + return -EINVAL; 188 + 189 + if (!valid_offsets(insn_array, prog)) 190 + return -EINVAL; 191 + 192 + /* 193 + * There can be only one program using the map 194 + */ 195 + if (atomic_xchg(&insn_array->used, 1)) 196 + return -EBUSY; 197 + 198 + /* 199 + * Reset all the map indexes to the original values. This is needed, 200 + * e.g., when a replay of verification with different log level should 201 + * be performed. 202 + */ 203 + for (i = 0; i < map->max_entries; i++) 204 + values[i].xlated_off = values[i].orig_off; 205 + 206 + return 0; 207 + } 208 + 209 + int bpf_insn_array_ready(struct bpf_map *map) 210 + { 211 + struct bpf_insn_array *insn_array = cast_insn_array(map); 212 + int i; 213 + 214 + for (i = 0; i < map->max_entries; i++) { 215 + if (insn_array->values[i].xlated_off == INSN_DELETED) 216 + continue; 217 + if (!insn_array->ips[i]) 218 + return -EFAULT; 219 + } 220 + 221 + return 0; 222 + } 223 + 224 + void bpf_insn_array_release(struct bpf_map *map) 225 + { 226 + struct bpf_insn_array *insn_array = cast_insn_array(map); 227 + 228 + atomic_set(&insn_array->used, 0); 229 + } 230 + 231 + void bpf_insn_array_adjust(struct bpf_map *map, u32 off, u32 len) 232 + { 233 + struct bpf_insn_array *insn_array = cast_insn_array(map); 234 + int i; 235 + 236 + if (len <= 1) 237 + return; 238 + 239 + for (i = 0; i < map->max_entries; i++) { 240 + if (insn_array->values[i].xlated_off <= off) 241 + continue; 242 + if (insn_array->values[i].xlated_off == INSN_DELETED) 243 + continue; 244 + insn_array->values[i].xlated_off += len - 1; 245 + } 246 + } 247 + 248 + void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len) 249 + { 250 + struct bpf_insn_array *insn_array = cast_insn_array(map); 251 + int i; 252 + 253 + for (i = 0; i < map->max_entries; i++) { 254 + if (insn_array->values[i].xlated_off < off) 255 + continue; 256 + if (insn_array->values[i].xlated_off == INSN_DELETED) 257 + continue; 258 + if (insn_array->values[i].xlated_off < off + len) 259 + insn_array->values[i].xlated_off = INSN_DELETED; 260 + else 261 + insn_array->values[i].xlated_off -= len; 262 + } 263 + } 264 + 265 + /* 266 + * This function is called by JITs. The image is the real program 267 + * image, the offsets array set up the xlated -> jitted mapping. 268 + * The offsets[xlated] offset should point to the beginning of 269 + * the jitted instruction. 270 + */ 271 + void bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image) 272 + { 273 + struct bpf_insn_array *insn_array; 274 + struct bpf_map *map; 275 + u32 xlated_off; 276 + int i, j; 277 + 278 + if (!offsets || !image) 279 + return; 280 + 281 + for (i = 0; i < prog->aux->used_map_cnt; i++) { 282 + map = prog->aux->used_maps[i]; 283 + if (!is_insn_array(map)) 284 + continue; 285 + 286 + insn_array = cast_insn_array(map); 287 + for (j = 0; j < map->max_entries; j++) { 288 + xlated_off = insn_array->values[j].xlated_off; 289 + if (xlated_off == INSN_DELETED) 290 + continue; 291 + if (xlated_off < prog->aux->subprog_start) 292 + continue; 293 + xlated_off -= prog->aux->subprog_start; 294 + if (xlated_off >= prog->len) 295 + continue; 296 + 297 + insn_array->values[j].jitted_off = offsets[xlated_off]; 298 + insn_array->ips[j] = (long)(image + offsets[xlated_off]); 299 + } 300 + } 301 + }
+21
kernel/bpf/core.c
··· 1450 1450 bpf_prog_clone_free(fp_other); 1451 1451 } 1452 1452 1453 + static void adjust_insn_arrays(struct bpf_prog *prog, u32 off, u32 len) 1454 + { 1455 + #ifdef CONFIG_BPF_SYSCALL 1456 + struct bpf_map *map; 1457 + int i; 1458 + 1459 + if (len <= 1) 1460 + return; 1461 + 1462 + for (i = 0; i < prog->aux->used_map_cnt; i++) { 1463 + map = prog->aux->used_maps[i]; 1464 + if (map->map_type == BPF_MAP_TYPE_INSN_ARRAY) 1465 + bpf_insn_array_adjust(map, off, len); 1466 + } 1467 + #endif 1468 + } 1469 + 1453 1470 struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog) 1454 1471 { 1455 1472 struct bpf_insn insn_buff[16], aux[2]; ··· 1521 1504 1522 1505 clone = tmp; 1523 1506 insn_delta = rewritten - 1; 1507 + 1508 + /* Instructions arrays must be updated using absolute xlated offsets */ 1509 + adjust_insn_arrays(clone, prog->aux->subprog_start + i, rewritten); 1524 1510 1525 1511 /* Walk new program and skip insns we just inserted. */ 1526 1512 insn = clone->insnsi + i + insn_delta; ··· 1708 1688 [BPF_LD | BPF_IND | BPF_B] = true, 1709 1689 [BPF_LD | BPF_IND | BPF_H] = true, 1710 1690 [BPF_LD | BPF_IND | BPF_W] = true, 1691 + [BPF_JMP | BPF_JA | BPF_X] = true, 1711 1692 [BPF_JMP | BPF_JCOND] = true, 1712 1693 }; 1713 1694 #undef BPF_INSN_3_TBL
+3
kernel/bpf/disasm.c
··· 358 358 } else if (insn->code == (BPF_JMP | BPF_JA)) { 359 359 verbose(cbs->private_data, "(%02x) goto pc%+d\n", 360 360 insn->code, insn->off); 361 + } else if (insn->code == (BPF_JMP | BPF_JA | BPF_X)) { 362 + verbose(cbs->private_data, "(%02x) gotox r%d\n", 363 + insn->code, insn->dst_reg); 361 364 } else if (insn->code == (BPF_JMP | BPF_JCOND) && 362 365 insn->src_reg == BPF_MAY_GOTO) { 363 366 verbose(cbs->private_data, "(%02x) may_goto pc%+d\n",
+3
kernel/bpf/liveness.c
··· 485 485 struct bpf_iarray *succ; 486 486 int insn_sz; 487 487 488 + if (unlikely(insn_is_gotox(insn))) 489 + return env->insn_aux_data[idx].jt; 490 + 488 491 /* pre-allocated array of size up to 2; reset cnt, as it may have been used already */ 489 492 succ = env->succ; 490 493 succ->cnt = 0;
+1
kernel/bpf/log.c
··· 461 461 [PTR_TO_ARENA] = "arena", 462 462 [PTR_TO_BUF] = "buf", 463 463 [PTR_TO_FUNC] = "func", 464 + [PTR_TO_INSN] = "insn", 464 465 [PTR_TO_MAP_KEY] = "map_key", 465 466 [CONST_PTR_TO_DYNPTR] = "dynptr_ptr", 466 467 };
+22
kernel/bpf/syscall.c
··· 1493 1493 case BPF_MAP_TYPE_STRUCT_OPS: 1494 1494 case BPF_MAP_TYPE_CPUMAP: 1495 1495 case BPF_MAP_TYPE_ARENA: 1496 + case BPF_MAP_TYPE_INSN_ARRAY: 1496 1497 if (!bpf_token_capable(token, CAP_BPF)) 1497 1498 goto put_token; 1498 1499 break; ··· 2854 2853 return err; 2855 2854 } 2856 2855 2856 + static int bpf_prog_mark_insn_arrays_ready(struct bpf_prog *prog) 2857 + { 2858 + int err; 2859 + int i; 2860 + 2861 + for (i = 0; i < prog->aux->used_map_cnt; i++) { 2862 + if (prog->aux->used_maps[i]->map_type != BPF_MAP_TYPE_INSN_ARRAY) 2863 + continue; 2864 + 2865 + err = bpf_insn_array_ready(prog->aux->used_maps[i]); 2866 + if (err) 2867 + return err; 2868 + } 2869 + 2870 + return 0; 2871 + } 2872 + 2857 2873 /* last field in 'union bpf_attr' used by this command */ 2858 2874 #define BPF_PROG_LOAD_LAST_FIELD keyring_id 2859 2875 ··· 3097 3079 goto free_used_maps; 3098 3080 3099 3081 prog = bpf_prog_select_runtime(prog, &err); 3082 + if (err < 0) 3083 + goto free_used_maps; 3084 + 3085 + err = bpf_prog_mark_insn_arrays_ready(prog); 3100 3086 if (err < 0) 3101 3087 goto free_used_maps; 3102 3088
+422 -7
kernel/bpf/verifier.c
··· 6006 6006 return 0; 6007 6007 } 6008 6008 6009 + /* 6010 + * Return the size of the memory region accessible from a pointer to map value. 6011 + * For INSN_ARRAY maps whole bpf_insn_array->ips array is accessible. 6012 + */ 6013 + static u32 map_mem_size(const struct bpf_map *map) 6014 + { 6015 + if (map->map_type == BPF_MAP_TYPE_INSN_ARRAY) 6016 + return map->max_entries * sizeof(long); 6017 + 6018 + return map->value_size; 6019 + } 6020 + 6009 6021 /* check read/write into a map element with possible variable offset */ 6010 6022 static int check_map_access(struct bpf_verifier_env *env, u32 regno, 6011 6023 int off, int size, bool zero_size_allowed, ··· 6027 6015 struct bpf_func_state *state = vstate->frame[vstate->curframe]; 6028 6016 struct bpf_reg_state *reg = &state->regs[regno]; 6029 6017 struct bpf_map *map = reg->map_ptr; 6018 + u32 mem_size = map_mem_size(map); 6030 6019 struct btf_record *rec; 6031 6020 int err, i; 6032 6021 6033 - err = check_mem_region_access(env, regno, off, size, map->value_size, 6034 - zero_size_allowed); 6022 + err = check_mem_region_access(env, regno, off, size, mem_size, zero_size_allowed); 6035 6023 if (err) 6036 6024 return err; 6037 6025 ··· 7493 7481 { 7494 7482 struct bpf_reg_state *regs = cur_regs(env); 7495 7483 struct bpf_reg_state *reg = regs + regno; 7484 + bool insn_array = reg->type == PTR_TO_MAP_VALUE && 7485 + reg->map_ptr->map_type == BPF_MAP_TYPE_INSN_ARRAY; 7496 7486 int size, err = 0; 7497 7487 7498 7488 size = bpf_size_to_bytes(bpf_size); ··· 7502 7488 return size; 7503 7489 7504 7490 /* alignment checks will add in reg->off themselves */ 7505 - err = check_ptr_alignment(env, reg, off, size, strict_alignment_once); 7491 + err = check_ptr_alignment(env, reg, off, size, strict_alignment_once || insn_array); 7506 7492 if (err) 7507 7493 return err; 7508 7494 ··· 7529 7515 verbose(env, "R%d leaks addr into map\n", value_regno); 7530 7516 return -EACCES; 7531 7517 } 7518 + if (t == BPF_WRITE && insn_array) { 7519 + verbose(env, "writes into insn_array not allowed\n"); 7520 + return -EACCES; 7521 + } 7522 + 7532 7523 err = check_map_access_type(env, regno, off, size, t); 7533 7524 if (err) 7534 7525 return err; ··· 7562 7543 7563 7544 regs[value_regno].type = SCALAR_VALUE; 7564 7545 __mark_reg_known(&regs[value_regno], val); 7546 + } else if (map->map_type == BPF_MAP_TYPE_INSN_ARRAY) { 7547 + if (bpf_size != BPF_DW) { 7548 + verbose(env, "Invalid read of %d bytes from insn_array\n", 7549 + size); 7550 + return -EACCES; 7551 + } 7552 + copy_register_state(&regs[value_regno], reg); 7553 + regs[value_regno].type = PTR_TO_INSN; 7565 7554 } else { 7566 7555 mark_reg_unknown(env, regs, value_regno); 7567 7556 } ··· 10113 10086 func_id != BPF_FUNC_map_push_elem) 10114 10087 goto error; 10115 10088 break; 10089 + case BPF_MAP_TYPE_INSN_ARRAY: 10090 + goto error; 10116 10091 default: 10117 10092 break; 10118 10093 } ··· 17123 17094 } 17124 17095 dst_reg->type = PTR_TO_MAP_VALUE; 17125 17096 dst_reg->off = aux->map_off; 17126 - WARN_ON_ONCE(map->max_entries != 1); 17097 + WARN_ON_ONCE(map->map_type != BPF_MAP_TYPE_INSN_ARRAY && 17098 + map->max_entries != 1); 17127 17099 /* We want reg->id to be same (0) as map_value is not distinct */ 17128 17100 } else if (insn->src_reg == BPF_PSEUDO_MAP_FD || 17129 17101 insn->src_reg == BPF_PSEUDO_MAP_IDX) { ··· 17892 17862 return new; 17893 17863 } 17894 17864 17865 + static int copy_insn_array(struct bpf_map *map, u32 start, u32 end, u32 *items) 17866 + { 17867 + struct bpf_insn_array_value *value; 17868 + u32 i; 17869 + 17870 + for (i = start; i <= end; i++) { 17871 + value = map->ops->map_lookup_elem(map, &i); 17872 + if (!value) 17873 + return -EINVAL; 17874 + items[i - start] = value->xlated_off; 17875 + } 17876 + return 0; 17877 + } 17878 + 17879 + static int cmp_ptr_to_u32(const void *a, const void *b) 17880 + { 17881 + return *(u32 *)a - *(u32 *)b; 17882 + } 17883 + 17884 + static int sort_insn_array_uniq(u32 *items, int cnt) 17885 + { 17886 + int unique = 1; 17887 + int i; 17888 + 17889 + sort(items, cnt, sizeof(items[0]), cmp_ptr_to_u32, NULL); 17890 + 17891 + for (i = 1; i < cnt; i++) 17892 + if (items[i] != items[unique - 1]) 17893 + items[unique++] = items[i]; 17894 + 17895 + return unique; 17896 + } 17897 + 17898 + /* 17899 + * sort_unique({map[start], ..., map[end]}) into off 17900 + */ 17901 + static int copy_insn_array_uniq(struct bpf_map *map, u32 start, u32 end, u32 *off) 17902 + { 17903 + u32 n = end - start + 1; 17904 + int err; 17905 + 17906 + err = copy_insn_array(map, start, end, off); 17907 + if (err) 17908 + return err; 17909 + 17910 + return sort_insn_array_uniq(off, n); 17911 + } 17912 + 17913 + /* 17914 + * Copy all unique offsets from the map 17915 + */ 17916 + static struct bpf_iarray *jt_from_map(struct bpf_map *map) 17917 + { 17918 + struct bpf_iarray *jt; 17919 + int err; 17920 + int n; 17921 + 17922 + jt = iarray_realloc(NULL, map->max_entries); 17923 + if (!jt) 17924 + return ERR_PTR(-ENOMEM); 17925 + 17926 + n = copy_insn_array_uniq(map, 0, map->max_entries - 1, jt->items); 17927 + if (n < 0) { 17928 + err = n; 17929 + goto err_free; 17930 + } 17931 + if (n == 0) { 17932 + err = -EINVAL; 17933 + goto err_free; 17934 + } 17935 + jt->cnt = n; 17936 + return jt; 17937 + 17938 + err_free: 17939 + kvfree(jt); 17940 + return ERR_PTR(err); 17941 + } 17942 + 17943 + /* 17944 + * Find and collect all maps which fit in the subprog. Return the result as one 17945 + * combined jump table in jt->items (allocated with kvcalloc) 17946 + */ 17947 + static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env, 17948 + int subprog_start, int subprog_end) 17949 + { 17950 + struct bpf_iarray *jt = NULL; 17951 + struct bpf_map *map; 17952 + struct bpf_iarray *jt_cur; 17953 + int i; 17954 + 17955 + for (i = 0; i < env->insn_array_map_cnt; i++) { 17956 + /* 17957 + * TODO (when needed): collect only jump tables, not static keys 17958 + * or maps for indirect calls 17959 + */ 17960 + map = env->insn_array_maps[i]; 17961 + 17962 + jt_cur = jt_from_map(map); 17963 + if (IS_ERR(jt_cur)) { 17964 + kvfree(jt); 17965 + return jt_cur; 17966 + } 17967 + 17968 + /* 17969 + * This is enough to check one element. The full table is 17970 + * checked to fit inside the subprog later in create_jt() 17971 + */ 17972 + if (jt_cur->items[0] >= subprog_start && jt_cur->items[0] < subprog_end) { 17973 + u32 old_cnt = jt ? jt->cnt : 0; 17974 + jt = iarray_realloc(jt, old_cnt + jt_cur->cnt); 17975 + if (!jt) { 17976 + kvfree(jt_cur); 17977 + return ERR_PTR(-ENOMEM); 17978 + } 17979 + memcpy(jt->items + old_cnt, jt_cur->items, jt_cur->cnt << 2); 17980 + } 17981 + 17982 + kvfree(jt_cur); 17983 + } 17984 + 17985 + if (!jt) { 17986 + verbose(env, "no jump tables found for subprog starting at %u\n", subprog_start); 17987 + return ERR_PTR(-EINVAL); 17988 + } 17989 + 17990 + jt->cnt = sort_insn_array_uniq(jt->items, jt->cnt); 17991 + return jt; 17992 + } 17993 + 17994 + static struct bpf_iarray * 17995 + create_jt(int t, struct bpf_verifier_env *env) 17996 + { 17997 + static struct bpf_subprog_info *subprog; 17998 + int subprog_start, subprog_end; 17999 + struct bpf_iarray *jt; 18000 + int i; 18001 + 18002 + subprog = bpf_find_containing_subprog(env, t); 18003 + subprog_start = subprog->start; 18004 + subprog_end = (subprog + 1)->start; 18005 + jt = jt_from_subprog(env, subprog_start, subprog_end); 18006 + if (IS_ERR(jt)) 18007 + return jt; 18008 + 18009 + /* Check that the every element of the jump table fits within the given subprogram */ 18010 + for (i = 0; i < jt->cnt; i++) { 18011 + if (jt->items[i] < subprog_start || jt->items[i] >= subprog_end) { 18012 + verbose(env, "jump table for insn %d points outside of the subprog [%u,%u]\n", 18013 + t, subprog_start, subprog_end); 18014 + kvfree(jt); 18015 + return ERR_PTR(-EINVAL); 18016 + } 18017 + } 18018 + 18019 + return jt; 18020 + } 18021 + 18022 + /* "conditional jump with N edges" */ 18023 + static int visit_gotox_insn(int t, struct bpf_verifier_env *env) 18024 + { 18025 + int *insn_stack = env->cfg.insn_stack; 18026 + int *insn_state = env->cfg.insn_state; 18027 + bool keep_exploring = false; 18028 + struct bpf_iarray *jt; 18029 + int i, w; 18030 + 18031 + jt = env->insn_aux_data[t].jt; 18032 + if (!jt) { 18033 + jt = create_jt(t, env); 18034 + if (IS_ERR(jt)) 18035 + return PTR_ERR(jt); 18036 + 18037 + env->insn_aux_data[t].jt = jt; 18038 + } 18039 + 18040 + mark_prune_point(env, t); 18041 + for (i = 0; i < jt->cnt; i++) { 18042 + w = jt->items[i]; 18043 + if (w < 0 || w >= env->prog->len) { 18044 + verbose(env, "indirect jump out of range from insn %d to %d\n", t, w); 18045 + return -EINVAL; 18046 + } 18047 + 18048 + mark_jmp_point(env, w); 18049 + 18050 + /* EXPLORED || DISCOVERED */ 18051 + if (insn_state[w]) 18052 + continue; 18053 + 18054 + if (env->cfg.cur_stack >= env->prog->len) 18055 + return -E2BIG; 18056 + 18057 + insn_stack[env->cfg.cur_stack++] = w; 18058 + insn_state[w] |= DISCOVERED; 18059 + keep_exploring = true; 18060 + } 18061 + 18062 + return keep_exploring ? KEEP_EXPLORING : DONE_EXPLORING; 18063 + } 18064 + 17895 18065 /* Visits the instruction at index t and returns one of the following: 17896 18066 * < 0 - an error occurred 17897 18067 * DONE_EXPLORING - the instruction was fully explored ··· 18184 17954 return visit_func_call_insn(t, insns, env, insn->src_reg == BPF_PSEUDO_CALL); 18185 17955 18186 17956 case BPF_JA: 18187 - if (BPF_SRC(insn->code) != BPF_K) 18188 - return -EINVAL; 17957 + if (BPF_SRC(insn->code) == BPF_X) 17958 + return visit_gotox_insn(t, env); 18189 17959 18190 17960 if (BPF_CLASS(insn->code) == BPF_JMP) 18191 17961 off = insn->off; ··· 19114 18884 return regs_exact(rold, rcur, idmap) && rold->frameno == rcur->frameno; 19115 18885 case PTR_TO_ARENA: 19116 18886 return true; 18887 + case PTR_TO_INSN: 18888 + return memcmp(rold, rcur, offsetof(struct bpf_reg_state, var_off)) == 0 && 18889 + rold->off == rcur->off && range_within(rold, rcur) && 18890 + tnum_in(rold->var_off, rcur->var_off); 19117 18891 default: 19118 18892 return regs_exact(rold, rcur, idmap); 19119 18893 } ··· 20127 19893 return PROCESS_BPF_EXIT; 20128 19894 } 20129 19895 19896 + static int indirect_jump_min_max_index(struct bpf_verifier_env *env, 19897 + int regno, 19898 + struct bpf_map *map, 19899 + u32 *pmin_index, u32 *pmax_index) 19900 + { 19901 + struct bpf_reg_state *reg = reg_state(env, regno); 19902 + u64 min_index, max_index; 19903 + const u32 size = 8; 19904 + 19905 + if (check_add_overflow(reg->umin_value, reg->off, &min_index) || 19906 + (min_index > (u64) U32_MAX * size)) { 19907 + verbose(env, "the sum of R%u umin_value %llu and off %u is too big\n", 19908 + regno, reg->umin_value, reg->off); 19909 + return -ERANGE; 19910 + } 19911 + if (check_add_overflow(reg->umax_value, reg->off, &max_index) || 19912 + (max_index > (u64) U32_MAX * size)) { 19913 + verbose(env, "the sum of R%u umax_value %llu and off %u is too big\n", 19914 + regno, reg->umax_value, reg->off); 19915 + return -ERANGE; 19916 + } 19917 + 19918 + min_index /= size; 19919 + max_index /= size; 19920 + 19921 + if (max_index >= map->max_entries) { 19922 + verbose(env, "R%u points to outside of jump table: [%llu,%llu] max_entries %u\n", 19923 + regno, min_index, max_index, map->max_entries); 19924 + return -EINVAL; 19925 + } 19926 + 19927 + *pmin_index = min_index; 19928 + *pmax_index = max_index; 19929 + return 0; 19930 + } 19931 + 19932 + /* gotox *dst_reg */ 19933 + static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *insn) 19934 + { 19935 + struct bpf_verifier_state *other_branch; 19936 + struct bpf_reg_state *dst_reg; 19937 + struct bpf_map *map; 19938 + u32 min_index, max_index; 19939 + int err = 0; 19940 + int n; 19941 + int i; 19942 + 19943 + dst_reg = reg_state(env, insn->dst_reg); 19944 + if (dst_reg->type != PTR_TO_INSN) { 19945 + verbose(env, "R%d has type %s, expected PTR_TO_INSN\n", 19946 + insn->dst_reg, reg_type_str(env, dst_reg->type)); 19947 + return -EINVAL; 19948 + } 19949 + 19950 + map = dst_reg->map_ptr; 19951 + if (verifier_bug_if(!map, env, "R%d has an empty map pointer", insn->dst_reg)) 19952 + return -EFAULT; 19953 + 19954 + if (verifier_bug_if(map->map_type != BPF_MAP_TYPE_INSN_ARRAY, env, 19955 + "R%d has incorrect map type %d", insn->dst_reg, map->map_type)) 19956 + return -EFAULT; 19957 + 19958 + err = indirect_jump_min_max_index(env, insn->dst_reg, map, &min_index, &max_index); 19959 + if (err) 19960 + return err; 19961 + 19962 + /* Ensure that the buffer is large enough */ 19963 + if (!env->gotox_tmp_buf || env->gotox_tmp_buf->cnt < max_index - min_index + 1) { 19964 + env->gotox_tmp_buf = iarray_realloc(env->gotox_tmp_buf, 19965 + max_index - min_index + 1); 19966 + if (!env->gotox_tmp_buf) 19967 + return -ENOMEM; 19968 + } 19969 + 19970 + n = copy_insn_array_uniq(map, min_index, max_index, env->gotox_tmp_buf->items); 19971 + if (n < 0) 19972 + return n; 19973 + if (n == 0) { 19974 + verbose(env, "register R%d doesn't point to any offset in map id=%d\n", 19975 + insn->dst_reg, map->id); 19976 + return -EINVAL; 19977 + } 19978 + 19979 + for (i = 0; i < n - 1; i++) { 19980 + other_branch = push_stack(env, env->gotox_tmp_buf->items[i], 19981 + env->insn_idx, env->cur_state->speculative); 19982 + if (IS_ERR(other_branch)) 19983 + return PTR_ERR(other_branch); 19984 + } 19985 + env->insn_idx = env->gotox_tmp_buf->items[n-1]; 19986 + return 0; 19987 + } 19988 + 20130 19989 static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state) 20131 19990 { 20132 19991 int err; ··· 20322 19995 20323 19996 mark_reg_scratched(env, BPF_REG_0); 20324 19997 } else if (opcode == BPF_JA) { 19998 + if (BPF_SRC(insn->code) == BPF_X) { 19999 + if (insn->src_reg != BPF_REG_0 || 20000 + insn->imm != 0 || insn->off != 0) { 20001 + verbose(env, "BPF_JA|BPF_X uses reserved fields\n"); 20002 + return -EINVAL; 20003 + } 20004 + return check_indirect_jump(env, insn); 20005 + } 20006 + 20325 20007 if (BPF_SRC(insn->code) != BPF_K || 20326 20008 insn->src_reg != BPF_REG_0 || 20327 20009 insn->dst_reg != BPF_REG_0 || ··· 20847 20511 case BPF_MAP_TYPE_QUEUE: 20848 20512 case BPF_MAP_TYPE_STACK: 20849 20513 case BPF_MAP_TYPE_ARENA: 20514 + case BPF_MAP_TYPE_INSN_ARRAY: 20850 20515 break; 20851 20516 default: 20852 20517 verbose(env, ··· 20918 20581 bpf_map_inc(map); 20919 20582 20920 20583 env->used_maps[env->used_map_cnt++] = map; 20584 + 20585 + if (map->map_type == BPF_MAP_TYPE_INSN_ARRAY) { 20586 + err = bpf_insn_array_init(map, env->prog); 20587 + if (err) { 20588 + verbose(env, "Failed to properly initialize insn array\n"); 20589 + return err; 20590 + } 20591 + env->insn_array_maps[env->insn_array_map_cnt++] = map; 20592 + } 20921 20593 20922 20594 return env->used_map_cnt - 1; 20923 20595 } ··· 21174 20828 } 21175 20829 } 21176 20830 20831 + static void release_insn_arrays(struct bpf_verifier_env *env) 20832 + { 20833 + int i; 20834 + 20835 + for (i = 0; i < env->insn_array_map_cnt; i++) 20836 + bpf_insn_array_release(env->insn_array_maps[i]); 20837 + } 20838 + 20839 + static void adjust_insn_arrays(struct bpf_verifier_env *env, u32 off, u32 len) 20840 + { 20841 + int i; 20842 + 20843 + if (len == 1) 20844 + return; 20845 + 20846 + for (i = 0; i < env->insn_array_map_cnt; i++) 20847 + bpf_insn_array_adjust(env->insn_array_maps[i], off, len); 20848 + } 20849 + 20850 + static void adjust_insn_arrays_after_remove(struct bpf_verifier_env *env, u32 off, u32 len) 20851 + { 20852 + int i; 20853 + 20854 + for (i = 0; i < env->insn_array_map_cnt; i++) 20855 + bpf_insn_array_adjust_after_remove(env->insn_array_maps[i], off, len); 20856 + } 20857 + 21177 20858 static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len) 21178 20859 { 21179 20860 struct bpf_jit_poke_descriptor *tab = prog->aux->poke_tab; ··· 21242 20869 } 21243 20870 adjust_insn_aux_data(env, new_prog, off, len); 21244 20871 adjust_subprog_starts(env, off, len); 20872 + adjust_insn_arrays(env, off, len); 21245 20873 adjust_poke_descs(new_prog, off, len); 21246 20874 return new_prog; 21247 20875 } ··· 21405 21031 return 0; 21406 21032 } 21407 21033 21034 + /* 21035 + * Clean up dynamically allocated fields of aux data for instructions [start, ...] 21036 + */ 21037 + static void clear_insn_aux_data(struct bpf_verifier_env *env, int start, int len) 21038 + { 21039 + struct bpf_insn_aux_data *aux_data = env->insn_aux_data; 21040 + struct bpf_insn *insns = env->prog->insnsi; 21041 + int end = start + len; 21042 + int i; 21043 + 21044 + for (i = start; i < end; i++) { 21045 + if (insn_is_gotox(&insns[i])) { 21046 + kvfree(aux_data[i].jt); 21047 + aux_data[i].jt = NULL; 21048 + } 21049 + 21050 + if (bpf_is_ldimm64(&insns[i])) 21051 + i++; 21052 + } 21053 + } 21054 + 21408 21055 static int verifier_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt) 21409 21056 { 21410 21057 struct bpf_insn_aux_data *aux_data = env->insn_aux_data; ··· 21434 21039 21435 21040 if (bpf_prog_is_offloaded(env->prog->aux)) 21436 21041 bpf_prog_offload_remove_insns(env, off, cnt); 21042 + 21043 + /* Should be called before bpf_remove_insns, as it uses prog->insnsi */ 21044 + clear_insn_aux_data(env, off, cnt); 21437 21045 21438 21046 err = bpf_remove_insns(env->prog, off, cnt); 21439 21047 if (err) ··· 21449 21051 err = bpf_adj_linfo_after_remove(env, off, cnt); 21450 21052 if (err) 21451 21053 return err; 21054 + 21055 + adjust_insn_arrays_after_remove(env, off, cnt); 21452 21056 21453 21057 memmove(aux_data + off, aux_data + off + cnt, 21454 21058 sizeof(*aux_data) * (orig_prog_len - off - cnt)); ··· 21991 21591 struct bpf_insn *insn; 21992 21592 void *old_bpf_func; 21993 21593 int err, num_exentries; 21594 + int old_len, subprog_start_adjustment = 0; 21994 21595 21995 21596 if (env->subprog_cnt <= 1) 21996 21597 return 0; ··· 22066 21665 func[i]->aux->func_idx = i; 22067 21666 /* Below members will be freed only at prog->aux */ 22068 21667 func[i]->aux->btf = prog->aux->btf; 22069 - func[i]->aux->subprog_start = subprog_start; 21668 + func[i]->aux->subprog_start = subprog_start + subprog_start_adjustment; 22070 21669 func[i]->aux->func_info = prog->aux->func_info; 22071 21670 func[i]->aux->func_info_cnt = prog->aux->func_info_cnt; 22072 21671 func[i]->aux->poke_tab = prog->aux->poke_tab; ··· 22096 21695 func[i]->aux->jited_linfo = prog->aux->jited_linfo; 22097 21696 func[i]->aux->linfo_idx = env->subprog_info[i].linfo_idx; 22098 21697 func[i]->aux->arena = prog->aux->arena; 21698 + func[i]->aux->used_maps = env->used_maps; 21699 + func[i]->aux->used_map_cnt = env->used_map_cnt; 22099 21700 num_exentries = 0; 22100 21701 insn = func[i]->insnsi; 22101 21702 for (j = 0; j < func[i]->len; j++, insn++) { ··· 22122 21719 func[i]->aux->might_sleep = env->subprog_info[i].might_sleep; 22123 21720 if (!i) 22124 21721 func[i]->aux->exception_boundary = env->seen_exception; 21722 + 21723 + /* 21724 + * To properly pass the absolute subprog start to jit 21725 + * all instruction adjustments should be accumulated 21726 + */ 21727 + old_len = func[i]->len; 22125 21728 func[i] = bpf_int_jit_compile(func[i]); 21729 + subprog_start_adjustment += func[i]->len - old_len; 21730 + 22126 21731 if (!func[i]->jited) { 22127 21732 err = -ENOTSUPP; 22128 21733 goto out_free; ··· 25282 24871 adjust_btf_func(env); 25283 24872 25284 24873 err_release_maps: 24874 + if (ret) 24875 + release_insn_arrays(env); 25285 24876 if (!env->prog->aux->used_maps) 25286 24877 /* if we didn't copy map pointers into bpf_prog_info, release 25287 24878 * them now. Otherwise free_used_maps() will release them. ··· 25304 24891 err_unlock: 25305 24892 if (!is_priv) 25306 24893 mutex_unlock(&bpf_verifier_lock); 24894 + clear_insn_aux_data(env, 0, env->prog->len); 25307 24895 vfree(env->insn_aux_data); 25308 24896 err_free_env: 25309 24897 bpf_stack_liveness_free(env); 25310 24898 kvfree(env->cfg.insn_postorder); 25311 24899 kvfree(env->scc_info); 25312 24900 kvfree(env->succ); 24901 + kvfree(env->gotox_tmp_buf); 25313 24902 kvfree(env); 25314 24903 return ret; 25315 24904 }
+2 -1
tools/bpf/bpftool/Documentation/bpftool-map.rst
··· 55 55 | | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | **xskmap** | **sockhash** 56 56 | | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage** 57 57 | | **queue** | **stack** | **sk_storage** | **struct_ops** | **ringbuf** | **inode_storage** 58 - | | **task_storage** | **bloom_filter** | **user_ringbuf** | **cgrp_storage** | **arena** } 58 + | | **task_storage** | **bloom_filter** | **user_ringbuf** | **cgrp_storage** | **arena** 59 + | | **insn_array** } 59 60 60 61 DESCRIPTION 61 62 ===========
+2 -1
tools/bpf/bpftool/map.c
··· 1477 1477 " devmap | devmap_hash | sockmap | cpumap | xskmap | sockhash |\n" 1478 1478 " cgroup_storage | reuseport_sockarray | percpu_cgroup_storage |\n" 1479 1479 " queue | stack | sk_storage | struct_ops | ringbuf | inode_storage |\n" 1480 - " task_storage | bloom_filter | user_ringbuf | cgrp_storage | arena }\n" 1480 + " task_storage | bloom_filter | user_ringbuf | cgrp_storage | arena |\n" 1481 + " insn_array }\n" 1481 1482 " " HELP_SPEC_OPTIONS " |\n" 1482 1483 " {-f|--bpffs} | {-n|--nomount} }\n" 1483 1484 "",
+21
tools/include/uapi/linux/bpf.h
··· 1026 1026 BPF_MAP_TYPE_USER_RINGBUF, 1027 1027 BPF_MAP_TYPE_CGRP_STORAGE, 1028 1028 BPF_MAP_TYPE_ARENA, 1029 + BPF_MAP_TYPE_INSN_ARRAY, 1029 1030 __MAX_BPF_MAP_TYPE 1030 1031 }; 1031 1032 ··· 7648 7647 */ 7649 7648 enum bpf_kfunc_flags { 7650 7649 BPF_F_PAD_ZEROS = (1ULL << 0), 7650 + }; 7651 + 7652 + /* 7653 + * Values of a BPF_MAP_TYPE_INSN_ARRAY entry must be of this type. 7654 + * 7655 + * Before the map is used the orig_off field should point to an 7656 + * instruction inside the program being loaded. The other fields 7657 + * must be set to 0. 7658 + * 7659 + * After the program is loaded, the xlated_off will be adjusted 7660 + * by the verifier to point to the index of the original instruction 7661 + * in the xlated program. If the instruction is deleted, it will 7662 + * be set to (u32)-1. The jitted_off will be set to the corresponding 7663 + * offset in the jitted image of the program. 7664 + */ 7665 + struct bpf_insn_array_value { 7666 + __u32 orig_off; 7667 + __u32 xlated_off; 7668 + __u32 jitted_off; 7669 + __u32 :32; 7651 7670 }; 7652 7671 7653 7672 #endif /* _UAPI__LINUX_BPF_H__ */
+247 -1
tools/lib/bpf/libbpf.c
··· 190 190 [BPF_MAP_TYPE_USER_RINGBUF] = "user_ringbuf", 191 191 [BPF_MAP_TYPE_CGRP_STORAGE] = "cgrp_storage", 192 192 [BPF_MAP_TYPE_ARENA] = "arena", 193 + [BPF_MAP_TYPE_INSN_ARRAY] = "insn_array", 193 194 }; 194 195 195 196 static const char * const prog_type_name[] = { ··· 370 369 RELO_EXTERN_CALL, 371 370 RELO_SUBPROG_ADDR, 372 371 RELO_CORE, 372 + RELO_INSN_ARRAY, 373 373 }; 374 374 375 375 struct reloc_desc { ··· 381 379 struct { 382 380 int map_idx; 383 381 int sym_off; 384 - int ext_idx; 382 + /* 383 + * The following two fields can be unionized, as the 384 + * ext_idx field is used for extern symbols, and the 385 + * sym_size is used for jump tables, which are never 386 + * extern 387 + */ 388 + union { 389 + int ext_idx; 390 + int sym_size; 391 + }; 385 392 }; 386 393 }; 387 394 }; ··· 430 419 libbpf_prog_setup_fn_t prog_setup_fn; 431 420 libbpf_prog_prepare_load_fn_t prog_prepare_load_fn; 432 421 libbpf_prog_attach_fn_t prog_attach_fn; 422 + }; 423 + 424 + struct bpf_light_subprog { 425 + __u32 sec_insn_off; 426 + __u32 sub_insn_off; 433 427 }; 434 428 435 429 /* ··· 510 494 __u32 line_info_cnt; 511 495 __u32 prog_flags; 512 496 __u8 hash[SHA256_DIGEST_LENGTH]; 497 + 498 + struct bpf_light_subprog *subprogs; 499 + __u32 subprog_cnt; 513 500 }; 514 501 515 502 struct bpf_struct_ops { ··· 686 667 int symbols_shndx; 687 668 bool has_st_ops; 688 669 int arena_data_shndx; 670 + int jumptables_data_shndx; 689 671 }; 690 672 691 673 struct usdt_manager; ··· 758 738 void *arena_data; 759 739 size_t arena_data_sz; 760 740 741 + void *jumptables_data; 742 + size_t jumptables_data_sz; 743 + 744 + struct { 745 + struct bpf_program *prog; 746 + int sym_off; 747 + int fd; 748 + } *jumptable_maps; 749 + size_t jumptable_map_cnt; 750 + 761 751 struct kern_feature_cache *feat_cache; 762 752 char *token_path; 763 753 int token_fd; ··· 794 764 795 765 zfree(&prog->func_info); 796 766 zfree(&prog->line_info); 767 + zfree(&prog->subprogs); 797 768 } 798 769 799 770 static void bpf_program__exit(struct bpf_program *prog) ··· 3973 3942 } else if (strcmp(name, ARENA_SEC) == 0) { 3974 3943 obj->efile.arena_data = data; 3975 3944 obj->efile.arena_data_shndx = idx; 3945 + } else if (strcmp(name, JUMPTABLES_SEC) == 0) { 3946 + obj->jumptables_data = malloc(data->d_size); 3947 + if (!obj->jumptables_data) 3948 + return -ENOMEM; 3949 + memcpy(obj->jumptables_data, data->d_buf, data->d_size); 3950 + obj->jumptables_data_sz = data->d_size; 3951 + obj->efile.jumptables_data_shndx = idx; 3976 3952 } else { 3977 3953 pr_info("elf: skipping unrecognized data section(%d) %s\n", 3978 3954 idx, name); ··· 4669 4631 pr_debug("prog '%s': found arena map %d (%s, sec %d, off %zu) for insn %u\n", 4670 4632 prog->name, obj->arena_map_idx, map->name, map->sec_idx, 4671 4633 map->sec_offset, insn_idx); 4634 + return 0; 4635 + } 4636 + 4637 + /* jump table data relocation */ 4638 + if (shdr_idx == obj->efile.jumptables_data_shndx) { 4639 + reloc_desc->type = RELO_INSN_ARRAY; 4640 + reloc_desc->insn_idx = insn_idx; 4641 + reloc_desc->map_idx = -1; 4642 + reloc_desc->sym_off = sym->st_value; 4643 + reloc_desc->sym_size = sym->st_size; 4672 4644 return 0; 4673 4645 } 4674 4646 ··· 6192 6144 insn->imm = POISON_CALL_KFUNC_BASE + ext_idx; 6193 6145 } 6194 6146 6147 + static int find_jt_map(struct bpf_object *obj, struct bpf_program *prog, int sym_off) 6148 + { 6149 + size_t i; 6150 + 6151 + for (i = 0; i < obj->jumptable_map_cnt; i++) { 6152 + /* 6153 + * This might happen that same offset is used for two different 6154 + * programs (as jump tables can be the same). However, for 6155 + * different programs different maps should be created. 6156 + */ 6157 + if (obj->jumptable_maps[i].sym_off == sym_off && 6158 + obj->jumptable_maps[i].prog == prog) 6159 + return obj->jumptable_maps[i].fd; 6160 + } 6161 + 6162 + return -ENOENT; 6163 + } 6164 + 6165 + static int add_jt_map(struct bpf_object *obj, struct bpf_program *prog, int sym_off, int map_fd) 6166 + { 6167 + size_t cnt = obj->jumptable_map_cnt; 6168 + size_t size = sizeof(obj->jumptable_maps[0]); 6169 + void *tmp; 6170 + 6171 + tmp = libbpf_reallocarray(obj->jumptable_maps, cnt + 1, size); 6172 + if (!tmp) 6173 + return -ENOMEM; 6174 + 6175 + obj->jumptable_maps = tmp; 6176 + obj->jumptable_maps[cnt].prog = prog; 6177 + obj->jumptable_maps[cnt].sym_off = sym_off; 6178 + obj->jumptable_maps[cnt].fd = map_fd; 6179 + obj->jumptable_map_cnt++; 6180 + 6181 + return 0; 6182 + } 6183 + 6184 + static int find_subprog_idx(struct bpf_program *prog, int insn_idx) 6185 + { 6186 + int i; 6187 + 6188 + for (i = prog->subprog_cnt - 1; i >= 0; i--) { 6189 + if (insn_idx >= prog->subprogs[i].sub_insn_off) 6190 + return i; 6191 + } 6192 + 6193 + return -1; 6194 + } 6195 + 6196 + static int create_jt_map(struct bpf_object *obj, struct bpf_program *prog, struct reloc_desc *relo) 6197 + { 6198 + const __u32 jt_entry_size = 8; 6199 + int sym_off = relo->sym_off; 6200 + int jt_size = relo->sym_size; 6201 + __u32 max_entries = jt_size / jt_entry_size; 6202 + __u32 value_size = sizeof(struct bpf_insn_array_value); 6203 + struct bpf_insn_array_value val = {}; 6204 + int subprog_idx; 6205 + int map_fd, err; 6206 + __u64 insn_off; 6207 + __u64 *jt; 6208 + __u32 i; 6209 + 6210 + map_fd = find_jt_map(obj, prog, sym_off); 6211 + if (map_fd >= 0) 6212 + return map_fd; 6213 + 6214 + if (sym_off % jt_entry_size) { 6215 + pr_warn("map '.jumptables': jumptable start %d should be multiple of %u\n", 6216 + sym_off, jt_entry_size); 6217 + return -EINVAL; 6218 + } 6219 + 6220 + if (jt_size % jt_entry_size) { 6221 + pr_warn("map '.jumptables': jumptable size %d should be multiple of %u\n", 6222 + jt_size, jt_entry_size); 6223 + return -EINVAL; 6224 + } 6225 + 6226 + map_fd = bpf_map_create(BPF_MAP_TYPE_INSN_ARRAY, ".jumptables", 6227 + 4, value_size, max_entries, NULL); 6228 + if (map_fd < 0) 6229 + return map_fd; 6230 + 6231 + if (!obj->jumptables_data) { 6232 + pr_warn("map '.jumptables': ELF file is missing jump table data\n"); 6233 + err = -EINVAL; 6234 + goto err_close; 6235 + } 6236 + if (sym_off + jt_size > obj->jumptables_data_sz) { 6237 + pr_warn("map '.jumptables': jumptables_data size is %zd, trying to access %d\n", 6238 + obj->jumptables_data_sz, sym_off + jt_size); 6239 + err = -EINVAL; 6240 + goto err_close; 6241 + } 6242 + 6243 + subprog_idx = -1; /* main program */ 6244 + if (relo->insn_idx < 0 || relo->insn_idx >= prog->insns_cnt) { 6245 + pr_warn("map '.jumptables': invalid instruction index %d\n", relo->insn_idx); 6246 + err = -EINVAL; 6247 + goto err_close; 6248 + } 6249 + if (prog->subprogs) 6250 + subprog_idx = find_subprog_idx(prog, relo->insn_idx); 6251 + 6252 + jt = (__u64 *)(obj->jumptables_data + sym_off); 6253 + for (i = 0; i < max_entries; i++) { 6254 + /* 6255 + * The offset should be made to be relative to the beginning of 6256 + * the main function, not the subfunction. 6257 + */ 6258 + insn_off = jt[i]/sizeof(struct bpf_insn); 6259 + if (subprog_idx >= 0) { 6260 + insn_off -= prog->subprogs[subprog_idx].sec_insn_off; 6261 + insn_off += prog->subprogs[subprog_idx].sub_insn_off; 6262 + } else { 6263 + insn_off -= prog->sec_insn_off; 6264 + } 6265 + 6266 + /* 6267 + * LLVM-generated jump tables contain u64 records, however 6268 + * should contain values that fit in u32. 6269 + */ 6270 + if (insn_off > UINT32_MAX) { 6271 + pr_warn("map '.jumptables': invalid jump table value 0x%llx at offset %d\n", 6272 + (long long)jt[i], sym_off + i * jt_entry_size); 6273 + err = -EINVAL; 6274 + goto err_close; 6275 + } 6276 + 6277 + val.orig_off = insn_off; 6278 + err = bpf_map_update_elem(map_fd, &i, &val, 0); 6279 + if (err) 6280 + goto err_close; 6281 + } 6282 + 6283 + err = bpf_map_freeze(map_fd); 6284 + if (err) 6285 + goto err_close; 6286 + 6287 + err = add_jt_map(obj, prog, sym_off, map_fd); 6288 + if (err) 6289 + goto err_close; 6290 + 6291 + return map_fd; 6292 + 6293 + err_close: 6294 + close(map_fd); 6295 + return err; 6296 + } 6297 + 6195 6298 /* Relocate data references within program code: 6196 6299 * - map references; 6197 6300 * - global variable references; ··· 6433 6234 break; 6434 6235 case RELO_CORE: 6435 6236 /* will be handled by bpf_program_record_relos() */ 6237 + break; 6238 + case RELO_INSN_ARRAY: { 6239 + int map_fd; 6240 + 6241 + map_fd = create_jt_map(obj, prog, relo); 6242 + if (map_fd < 0) { 6243 + pr_warn("prog '%s': relo #%d: can't create jump table: sym_off %u\n", 6244 + prog->name, i, relo->sym_off); 6245 + return map_fd; 6246 + } 6247 + insn[0].src_reg = BPF_PSEUDO_MAP_VALUE; 6248 + insn->imm = map_fd; 6249 + insn->off = 0; 6250 + } 6436 6251 break; 6437 6252 default: 6438 6253 pr_warn("prog '%s': relo #%d: bad relo type %d\n", ··· 6645 6432 return 0; 6646 6433 } 6647 6434 6435 + static int save_subprog_offsets(struct bpf_program *main_prog, struct bpf_program *subprog) 6436 + { 6437 + size_t size = sizeof(main_prog->subprogs[0]); 6438 + int cnt = main_prog->subprog_cnt; 6439 + void *tmp; 6440 + 6441 + tmp = libbpf_reallocarray(main_prog->subprogs, cnt + 1, size); 6442 + if (!tmp) 6443 + return -ENOMEM; 6444 + 6445 + main_prog->subprogs = tmp; 6446 + main_prog->subprogs[cnt].sec_insn_off = subprog->sec_insn_off; 6447 + main_prog->subprogs[cnt].sub_insn_off = subprog->sub_insn_off; 6448 + main_prog->subprog_cnt++; 6449 + 6450 + return 0; 6451 + } 6452 + 6648 6453 static int 6649 6454 bpf_object__append_subprog_code(struct bpf_object *obj, struct bpf_program *main_prog, 6650 6455 struct bpf_program *subprog) ··· 6692 6461 err = append_subprog_relos(main_prog, subprog); 6693 6462 if (err) 6694 6463 return err; 6464 + 6465 + err = save_subprog_offsets(main_prog, subprog); 6466 + if (err) { 6467 + pr_warn("prog '%s': failed to add subprog offsets: %s\n", 6468 + main_prog->name, errstr(err)); 6469 + return err; 6470 + } 6471 + 6695 6472 return 0; 6696 6473 } 6697 6474 ··· 9466 9227 close(obj->token_fd); 9467 9228 9468 9229 zfree(&obj->arena_data); 9230 + 9231 + zfree(&obj->jumptables_data); 9232 + obj->jumptables_data_sz = 0; 9233 + 9234 + for (i = 0; i < obj->jumptable_map_cnt; i++) 9235 + close(obj->jumptable_maps[i].fd); 9236 + zfree(&obj->jumptable_maps); 9469 9237 9470 9238 free(obj); 9471 9239 }
+2
tools/lib/bpf/libbpf_internal.h
··· 74 74 #define ELF64_ST_VISIBILITY(o) ((o) & 0x03) 75 75 #endif 76 76 77 + #define JUMPTABLES_SEC ".jumptables" 78 + 77 79 #define BTF_INFO_ENC(kind, kind_flag, vlen) \ 78 80 ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN)) 79 81 #define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type)
+4
tools/lib/bpf/libbpf_probes.c
··· 364 364 case BPF_MAP_TYPE_SOCKHASH: 365 365 case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY: 366 366 break; 367 + case BPF_MAP_TYPE_INSN_ARRAY: 368 + key_size = sizeof(__u32); 369 + value_size = sizeof(struct bpf_insn_array_value); 370 + break; 367 371 case BPF_MAP_TYPE_UNSPEC: 368 372 default: 369 373 return -EOPNOTSUPP;
+3
tools/lib/bpf/linker.c
··· 2025 2025 obj->sym_map[src_sym_idx] = dst_sec->sec_sym_idx; 2026 2026 return 0; 2027 2027 } 2028 + 2029 + if (strcmp(src_sec->sec_name, JUMPTABLES_SEC) == 0) 2030 + goto add_sym; 2028 2031 } 2029 2032 2030 2033 if (sym_bind == STB_LOCAL)
+3 -1
tools/testing/selftests/bpf/Makefile
··· 453 453 -I$(abspath $(OUTPUT)/../usr/include) \ 454 454 -std=gnu11 \ 455 455 -fno-strict-aliasing \ 456 - -Wno-compare-distinct-pointer-types 456 + -Wno-compare-distinct-pointer-types \ 457 + -Wno-initializer-overrides \ 458 + # 457 459 # TODO: enable me -Wsign-compare 458 460 459 461 CLANG_CFLAGS = $(CLANG_SYS_INCLUDES)
+292
tools/testing/selftests/bpf/prog_tests/bpf_gotox.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <test_progs.h> 4 + 5 + #include <linux/if_ether.h> 6 + #include <linux/in.h> 7 + #include <linux/ip.h> 8 + #include <linux/ipv6.h> 9 + #include <linux/in6.h> 10 + #include <linux/udp.h> 11 + #include <linux/tcp.h> 12 + 13 + #include <sys/syscall.h> 14 + #include <bpf/bpf.h> 15 + 16 + #include "bpf_gotox.skel.h" 17 + 18 + static void __test_run(struct bpf_program *prog, void *ctx_in, size_t ctx_size_in) 19 + { 20 + LIBBPF_OPTS(bpf_test_run_opts, topts, 21 + .ctx_in = ctx_in, 22 + .ctx_size_in = ctx_size_in, 23 + ); 24 + int err, prog_fd; 25 + 26 + prog_fd = bpf_program__fd(prog); 27 + err = bpf_prog_test_run_opts(prog_fd, &topts); 28 + ASSERT_OK(err, "test_run_opts err"); 29 + } 30 + 31 + static void __subtest(struct bpf_gotox *skel, void (*check)(struct bpf_gotox *)) 32 + { 33 + if (skel->data->skip) 34 + test__skip(); 35 + else 36 + check(skel); 37 + } 38 + 39 + static void check_simple(struct bpf_gotox *skel, 40 + struct bpf_program *prog, 41 + __u64 ctx_in, 42 + __u64 expected) 43 + { 44 + skel->bss->ret_user = 0; 45 + 46 + __test_run(prog, &ctx_in, sizeof(ctx_in)); 47 + 48 + if (!ASSERT_EQ(skel->bss->ret_user, expected, "skel->bss->ret_user")) 49 + return; 50 + } 51 + 52 + static void check_simple_fentry(struct bpf_gotox *skel, 53 + struct bpf_program *prog, 54 + __u64 ctx_in, 55 + __u64 expected) 56 + { 57 + skel->bss->in_user = ctx_in; 58 + skel->bss->ret_user = 0; 59 + 60 + /* trigger */ 61 + usleep(1); 62 + 63 + if (!ASSERT_EQ(skel->bss->ret_user, expected, "skel->bss->ret_user")) 64 + return; 65 + } 66 + 67 + /* validate that for two loads of the same jump table libbpf generates only one map */ 68 + static void check_one_map_two_jumps(struct bpf_gotox *skel) 69 + { 70 + struct bpf_prog_info prog_info; 71 + struct bpf_map_info map_info; 72 + __u32 len; 73 + __u32 map_ids[16]; 74 + int prog_fd, map_fd; 75 + int ret; 76 + int i; 77 + bool seen = false; 78 + 79 + memset(&prog_info, 0, sizeof(prog_info)); 80 + prog_info.map_ids = (long)map_ids; 81 + prog_info.nr_map_ids = ARRAY_SIZE(map_ids); 82 + prog_fd = bpf_program__fd(skel->progs.one_map_two_jumps); 83 + if (!ASSERT_GE(prog_fd, 0, "bpf_program__fd(one_map_two_jumps)")) 84 + return; 85 + 86 + len = sizeof(prog_info); 87 + ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &len); 88 + if (!ASSERT_OK(ret, "bpf_obj_get_info_by_fd(prog_fd)")) 89 + return; 90 + 91 + for (i = 0; i < prog_info.nr_map_ids; i++) { 92 + map_fd = bpf_map_get_fd_by_id(map_ids[i]); 93 + if (!ASSERT_GE(map_fd, 0, "bpf_map_get_fd_by_id")) 94 + return; 95 + 96 + len = sizeof(map_info); 97 + memset(&map_info, 0, len); 98 + ret = bpf_obj_get_info_by_fd(map_fd, &map_info, &len); 99 + if (!ASSERT_OK(ret, "bpf_obj_get_info_by_fd(map_fd)")) { 100 + close(map_fd); 101 + return; 102 + } 103 + 104 + if (map_info.type == BPF_MAP_TYPE_INSN_ARRAY) { 105 + if (!ASSERT_EQ(seen, false, "more than one INSN_ARRAY map")) { 106 + close(map_fd); 107 + return; 108 + } 109 + seen = true; 110 + } 111 + close(map_fd); 112 + } 113 + 114 + ASSERT_EQ(seen, true, "no INSN_ARRAY map"); 115 + } 116 + 117 + static void check_one_switch(struct bpf_gotox *skel) 118 + { 119 + __u64 in[] = {0, 1, 2, 3, 4, 5, 77}; 120 + __u64 out[] = {2, 3, 4, 5, 7, 19, 19}; 121 + int i; 122 + 123 + for (i = 0; i < ARRAY_SIZE(in); i++) 124 + check_simple(skel, skel->progs.one_switch, in[i], out[i]); 125 + } 126 + 127 + static void check_one_switch_non_zero_sec_off(struct bpf_gotox *skel) 128 + { 129 + __u64 in[] = {0, 1, 2, 3, 4, 5, 77}; 130 + __u64 out[] = {2, 3, 4, 5, 7, 19, 19}; 131 + int i; 132 + 133 + for (i = 0; i < ARRAY_SIZE(in); i++) 134 + check_simple(skel, skel->progs.one_switch_non_zero_sec_off, in[i], out[i]); 135 + } 136 + 137 + static void check_two_switches(struct bpf_gotox *skel) 138 + { 139 + __u64 in[] = {0, 1, 2, 3, 4, 5, 77}; 140 + __u64 out[] = {103, 104, 107, 205, 115, 1019, 1019}; 141 + int i; 142 + 143 + for (i = 0; i < ARRAY_SIZE(in); i++) 144 + check_simple(skel, skel->progs.two_switches, in[i], out[i]); 145 + } 146 + 147 + static void check_big_jump_table(struct bpf_gotox *skel) 148 + { 149 + __u64 in[] = {0, 11, 27, 31, 22, 45, 99}; 150 + __u64 out[] = {2, 3, 4, 5, 19, 19, 19}; 151 + int i; 152 + 153 + for (i = 0; i < ARRAY_SIZE(in); i++) 154 + check_simple(skel, skel->progs.big_jump_table, in[i], out[i]); 155 + } 156 + 157 + static void check_one_jump_two_maps(struct bpf_gotox *skel) 158 + { 159 + __u64 in[] = {0, 1, 2, 3, 4, 5, 77}; 160 + __u64 out[] = {12, 15, 7 , 15, 12, 15, 15}; 161 + int i; 162 + 163 + for (i = 0; i < ARRAY_SIZE(in); i++) 164 + check_simple(skel, skel->progs.one_jump_two_maps, in[i], out[i]); 165 + } 166 + 167 + static void check_static_global(struct bpf_gotox *skel) 168 + { 169 + __u64 in[] = {0, 1, 2, 3, 4, 5, 77}; 170 + __u64 out[] = {2, 3, 4, 5, 7, 19, 19}; 171 + int i; 172 + 173 + for (i = 0; i < ARRAY_SIZE(in); i++) 174 + check_simple(skel, skel->progs.use_static_global1, in[i], out[i]); 175 + for (i = 0; i < ARRAY_SIZE(in); i++) 176 + check_simple(skel, skel->progs.use_static_global2, in[i], out[i]); 177 + } 178 + 179 + static void check_nonstatic_global(struct bpf_gotox *skel) 180 + { 181 + __u64 in[] = {0, 1, 2, 3, 4, 5, 77}; 182 + __u64 out[] = {2, 3, 4, 5, 7, 19, 19}; 183 + int i; 184 + 185 + for (i = 0; i < ARRAY_SIZE(in); i++) 186 + check_simple(skel, skel->progs.use_nonstatic_global1, in[i], out[i]); 187 + 188 + for (i = 0; i < ARRAY_SIZE(in); i++) 189 + check_simple(skel, skel->progs.use_nonstatic_global2, in[i], out[i]); 190 + } 191 + 192 + static void check_other_sec(struct bpf_gotox *skel) 193 + { 194 + struct bpf_link *link; 195 + __u64 in[] = {0, 1, 2, 3, 4, 5, 77}; 196 + __u64 out[] = {2, 3, 4, 5, 7, 19, 19}; 197 + int i; 198 + 199 + link = bpf_program__attach(skel->progs.simple_test_other_sec); 200 + if (!ASSERT_OK_PTR(link, "link")) 201 + return; 202 + 203 + for (i = 0; i < ARRAY_SIZE(in); i++) 204 + check_simple_fentry(skel, skel->progs.simple_test_other_sec, in[i], out[i]); 205 + 206 + bpf_link__destroy(link); 207 + } 208 + 209 + static void check_static_global_other_sec(struct bpf_gotox *skel) 210 + { 211 + struct bpf_link *link; 212 + __u64 in[] = {0, 1, 2, 3, 4, 5, 77}; 213 + __u64 out[] = {2, 3, 4, 5, 7, 19, 19}; 214 + int i; 215 + 216 + link = bpf_program__attach(skel->progs.use_static_global_other_sec); 217 + if (!ASSERT_OK_PTR(link, "link")) 218 + return; 219 + 220 + for (i = 0; i < ARRAY_SIZE(in); i++) 221 + check_simple_fentry(skel, skel->progs.use_static_global_other_sec, in[i], out[i]); 222 + 223 + bpf_link__destroy(link); 224 + } 225 + 226 + static void check_nonstatic_global_other_sec(struct bpf_gotox *skel) 227 + { 228 + struct bpf_link *link; 229 + __u64 in[] = {0, 1, 2, 3, 4, 5, 77}; 230 + __u64 out[] = {2, 3, 4, 5, 7, 19, 19}; 231 + int i; 232 + 233 + link = bpf_program__attach(skel->progs.use_nonstatic_global_other_sec); 234 + if (!ASSERT_OK_PTR(link, "link")) 235 + return; 236 + 237 + for (i = 0; i < ARRAY_SIZE(in); i++) 238 + check_simple_fentry(skel, skel->progs.use_nonstatic_global_other_sec, in[i], out[i]); 239 + 240 + bpf_link__destroy(link); 241 + } 242 + 243 + void test_bpf_gotox(void) 244 + { 245 + struct bpf_gotox *skel; 246 + int ret; 247 + 248 + skel = bpf_gotox__open(); 249 + if (!ASSERT_NEQ(skel, NULL, "bpf_gotox__open")) 250 + return; 251 + 252 + ret = bpf_gotox__load(skel); 253 + if (!ASSERT_OK(ret, "bpf_gotox__load")) 254 + return; 255 + 256 + skel->bss->pid = getpid(); 257 + 258 + if (test__start_subtest("one-switch")) 259 + __subtest(skel, check_one_switch); 260 + 261 + if (test__start_subtest("one-switch-non-zero-sec-offset")) 262 + __subtest(skel, check_one_switch_non_zero_sec_off); 263 + 264 + if (test__start_subtest("two-switches")) 265 + __subtest(skel, check_two_switches); 266 + 267 + if (test__start_subtest("big-jump-table")) 268 + __subtest(skel, check_big_jump_table); 269 + 270 + if (test__start_subtest("static-global")) 271 + __subtest(skel, check_static_global); 272 + 273 + if (test__start_subtest("nonstatic-global")) 274 + __subtest(skel, check_nonstatic_global); 275 + 276 + if (test__start_subtest("other-sec")) 277 + __subtest(skel, check_other_sec); 278 + 279 + if (test__start_subtest("static-global-other-sec")) 280 + __subtest(skel, check_static_global_other_sec); 281 + 282 + if (test__start_subtest("nonstatic-global-other-sec")) 283 + __subtest(skel, check_nonstatic_global_other_sec); 284 + 285 + if (test__start_subtest("one-jump-two-maps")) 286 + __subtest(skel, check_one_jump_two_maps); 287 + 288 + if (test__start_subtest("one-map-two-jumps")) 289 + __subtest(skel, check_one_map_two_jumps); 290 + 291 + bpf_gotox__destroy(skel); 292 + }
+504
tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <bpf/bpf.h> 4 + #include <test_progs.h> 5 + 6 + #ifdef __x86_64__ 7 + static int map_create(__u32 map_type, __u32 max_entries) 8 + { 9 + const char *map_name = "insn_array"; 10 + __u32 key_size = 4; 11 + __u32 value_size = sizeof(struct bpf_insn_array_value); 12 + 13 + return bpf_map_create(map_type, map_name, key_size, value_size, max_entries, NULL); 14 + } 15 + 16 + static int prog_load(struct bpf_insn *insns, __u32 insn_cnt, int *fd_array, __u32 fd_array_cnt) 17 + { 18 + LIBBPF_OPTS(bpf_prog_load_opts, opts); 19 + 20 + opts.fd_array = fd_array; 21 + opts.fd_array_cnt = fd_array_cnt; 22 + 23 + return bpf_prog_load(BPF_PROG_TYPE_XDP, NULL, "GPL", insns, insn_cnt, &opts); 24 + } 25 + 26 + static void __check_success(struct bpf_insn *insns, __u32 insn_cnt, __u32 *map_in, __u32 *map_out) 27 + { 28 + struct bpf_insn_array_value val = {}; 29 + int prog_fd = -1, map_fd, i; 30 + 31 + map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, insn_cnt); 32 + if (!ASSERT_GE(map_fd, 0, "map_create")) 33 + return; 34 + 35 + for (i = 0; i < insn_cnt; i++) { 36 + val.orig_off = map_in[i]; 37 + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem")) 38 + goto cleanup; 39 + } 40 + 41 + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 42 + goto cleanup; 43 + 44 + prog_fd = prog_load(insns, insn_cnt, &map_fd, 1); 45 + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) 46 + goto cleanup; 47 + 48 + for (i = 0; i < insn_cnt; i++) { 49 + char buf[64]; 50 + 51 + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) 52 + goto cleanup; 53 + 54 + snprintf(buf, sizeof(buf), "val.xlated_off should be equal map_out[%d]", i); 55 + ASSERT_EQ(val.xlated_off, map_out[i], buf); 56 + } 57 + 58 + cleanup: 59 + close(prog_fd); 60 + close(map_fd); 61 + } 62 + 63 + /* 64 + * Load a program, which will not be anyhow mangled by the verifier. Add an 65 + * insn_array map pointing to every instruction. Check that it hasn't changed 66 + * after the program load. 67 + */ 68 + static void check_one_to_one_mapping(void) 69 + { 70 + struct bpf_insn insns[] = { 71 + BPF_MOV64_IMM(BPF_REG_0, 4), 72 + BPF_MOV64_IMM(BPF_REG_0, 3), 73 + BPF_MOV64_IMM(BPF_REG_0, 2), 74 + BPF_MOV64_IMM(BPF_REG_0, 1), 75 + BPF_MOV64_IMM(BPF_REG_0, 0), 76 + BPF_EXIT_INSN(), 77 + }; 78 + __u32 map_in[] = {0, 1, 2, 3, 4, 5}; 79 + __u32 map_out[] = {0, 1, 2, 3, 4, 5}; 80 + 81 + __check_success(insns, ARRAY_SIZE(insns), map_in, map_out); 82 + } 83 + 84 + /* 85 + * Load a program with two patches (get jiffies, for simplicity). Add an 86 + * insn_array map pointing to every instruction. Check how it was changed 87 + * after the program load. 88 + */ 89 + static void check_simple(void) 90 + { 91 + struct bpf_insn insns[] = { 92 + BPF_MOV64_IMM(BPF_REG_0, 2), 93 + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), 94 + BPF_MOV64_IMM(BPF_REG_0, 1), 95 + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), 96 + BPF_MOV64_IMM(BPF_REG_0, 0), 97 + BPF_EXIT_INSN(), 98 + }; 99 + __u32 map_in[] = {0, 1, 2, 3, 4, 5}; 100 + __u32 map_out[] = {0, 1, 4, 5, 8, 9}; 101 + 102 + __check_success(insns, ARRAY_SIZE(insns), map_in, map_out); 103 + } 104 + 105 + /* 106 + * Verifier can delete code in two cases: nops & dead code. From insn 107 + * array's point of view, the two cases are the same, so test using 108 + * the simplest method: by loading some nops 109 + */ 110 + static void check_deletions(void) 111 + { 112 + struct bpf_insn insns[] = { 113 + BPF_MOV64_IMM(BPF_REG_0, 2), 114 + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 115 + BPF_MOV64_IMM(BPF_REG_0, 1), 116 + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 117 + BPF_MOV64_IMM(BPF_REG_0, 0), 118 + BPF_EXIT_INSN(), 119 + }; 120 + __u32 map_in[] = {0, 1, 2, 3, 4, 5}; 121 + __u32 map_out[] = {0, -1, 1, -1, 2, 3}; 122 + 123 + __check_success(insns, ARRAY_SIZE(insns), map_in, map_out); 124 + } 125 + 126 + /* 127 + * Same test as check_deletions, but also add code which adds instructions 128 + */ 129 + static void check_deletions_with_functions(void) 130 + { 131 + struct bpf_insn insns[] = { 132 + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 133 + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), 134 + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 135 + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2), 136 + BPF_MOV64_IMM(BPF_REG_0, 1), 137 + BPF_EXIT_INSN(), 138 + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 139 + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), 140 + BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */ 141 + BPF_MOV64_IMM(BPF_REG_0, 2), 142 + BPF_EXIT_INSN(), 143 + }; 144 + __u32 map_in[] = { 0, 1, 2, 3, 4, 5, /* func */ 6, 7, 8, 9, 10}; 145 + __u32 map_out[] = {-1, 0, -1, 3, 4, 5, /* func */ -1, 6, -1, 9, 10}; 146 + 147 + __check_success(insns, ARRAY_SIZE(insns), map_in, map_out); 148 + } 149 + 150 + /* 151 + * Try to load a program with a map which points to outside of the program 152 + */ 153 + static void check_out_of_bounds_index(void) 154 + { 155 + struct bpf_insn insns[] = { 156 + BPF_MOV64_IMM(BPF_REG_0, 4), 157 + BPF_MOV64_IMM(BPF_REG_0, 3), 158 + BPF_MOV64_IMM(BPF_REG_0, 2), 159 + BPF_MOV64_IMM(BPF_REG_0, 1), 160 + BPF_MOV64_IMM(BPF_REG_0, 0), 161 + BPF_EXIT_INSN(), 162 + }; 163 + int prog_fd, map_fd; 164 + struct bpf_insn_array_value val = {}; 165 + int key; 166 + 167 + map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, 1); 168 + if (!ASSERT_GE(map_fd, 0, "map_create")) 169 + return; 170 + 171 + key = 0; 172 + val.orig_off = ARRAY_SIZE(insns); /* too big */ 173 + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &key, &val, 0), 0, "bpf_map_update_elem")) 174 + goto cleanup; 175 + 176 + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 177 + goto cleanup; 178 + 179 + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 180 + if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) { 181 + close(prog_fd); 182 + goto cleanup; 183 + } 184 + 185 + cleanup: 186 + close(map_fd); 187 + } 188 + 189 + /* 190 + * Try to load a program with a map which points to the middle of 16-bit insn 191 + */ 192 + static void check_mid_insn_index(void) 193 + { 194 + struct bpf_insn insns[] = { 195 + BPF_LD_IMM64(BPF_REG_0, 0), /* 2 x 8 */ 196 + BPF_EXIT_INSN(), 197 + }; 198 + int prog_fd, map_fd; 199 + struct bpf_insn_array_value val = {}; 200 + int key; 201 + 202 + map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, 1); 203 + if (!ASSERT_GE(map_fd, 0, "map_create")) 204 + return; 205 + 206 + key = 0; 207 + val.orig_off = 1; /* middle of 16-byte instruction */ 208 + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &key, &val, 0), 0, "bpf_map_update_elem")) 209 + goto cleanup; 210 + 211 + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 212 + goto cleanup; 213 + 214 + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 215 + if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) { 216 + close(prog_fd); 217 + goto cleanup; 218 + } 219 + 220 + cleanup: 221 + close(map_fd); 222 + } 223 + 224 + static void check_incorrect_index(void) 225 + { 226 + check_out_of_bounds_index(); 227 + check_mid_insn_index(); 228 + } 229 + 230 + static int set_bpf_jit_harden(char *level) 231 + { 232 + char old_level; 233 + int err = -1; 234 + int fd = -1; 235 + 236 + fd = open("/proc/sys/net/core/bpf_jit_harden", O_RDWR | O_NONBLOCK); 237 + if (fd < 0) { 238 + ASSERT_FAIL("open .../bpf_jit_harden returned %d (errno=%d)", fd, errno); 239 + return -1; 240 + } 241 + 242 + err = read(fd, &old_level, 1); 243 + if (err != 1) { 244 + ASSERT_FAIL("read from .../bpf_jit_harden returned %d (errno=%d)", err, errno); 245 + err = -1; 246 + goto end; 247 + } 248 + 249 + lseek(fd, 0, SEEK_SET); 250 + 251 + err = write(fd, level, 1); 252 + if (err != 1) { 253 + ASSERT_FAIL("write to .../bpf_jit_harden returned %d (errno=%d)", err, errno); 254 + err = -1; 255 + goto end; 256 + } 257 + 258 + err = 0; 259 + *level = old_level; 260 + end: 261 + if (fd >= 0) 262 + close(fd); 263 + return err; 264 + } 265 + 266 + static void check_blindness(void) 267 + { 268 + struct bpf_insn insns[] = { 269 + BPF_MOV64_IMM(BPF_REG_0, 4), 270 + BPF_MOV64_IMM(BPF_REG_0, 3), 271 + BPF_MOV64_IMM(BPF_REG_0, 2), 272 + BPF_MOV64_IMM(BPF_REG_0, 1), 273 + BPF_EXIT_INSN(), 274 + }; 275 + int prog_fd = -1, map_fd; 276 + struct bpf_insn_array_value val = {}; 277 + char bpf_jit_harden = '@'; /* non-exizsting value */ 278 + int i; 279 + 280 + map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, ARRAY_SIZE(insns)); 281 + if (!ASSERT_GE(map_fd, 0, "map_create")) 282 + return; 283 + 284 + for (i = 0; i < ARRAY_SIZE(insns); i++) { 285 + val.orig_off = i; 286 + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem")) 287 + goto cleanup; 288 + } 289 + 290 + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 291 + goto cleanup; 292 + 293 + bpf_jit_harden = '2'; 294 + if (set_bpf_jit_harden(&bpf_jit_harden)) { 295 + bpf_jit_harden = '@'; /* open, read or write failed => no write was done */ 296 + goto cleanup; 297 + } 298 + 299 + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 300 + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) 301 + goto cleanup; 302 + 303 + for (i = 0; i < ARRAY_SIZE(insns); i++) { 304 + char fmt[32]; 305 + 306 + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) 307 + goto cleanup; 308 + 309 + snprintf(fmt, sizeof(fmt), "val should be equal 3*%d", i); 310 + ASSERT_EQ(val.xlated_off, i * 3, fmt); 311 + } 312 + 313 + cleanup: 314 + /* restore the old one */ 315 + if (bpf_jit_harden != '@') 316 + set_bpf_jit_harden(&bpf_jit_harden); 317 + 318 + close(prog_fd); 319 + close(map_fd); 320 + } 321 + 322 + /* Once map was initialized, it should be frozen */ 323 + static void check_load_unfrozen_map(void) 324 + { 325 + struct bpf_insn insns[] = { 326 + BPF_MOV64_IMM(BPF_REG_0, 0), 327 + BPF_EXIT_INSN(), 328 + }; 329 + int prog_fd = -1, map_fd; 330 + struct bpf_insn_array_value val = {}; 331 + int i; 332 + 333 + map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, ARRAY_SIZE(insns)); 334 + if (!ASSERT_GE(map_fd, 0, "map_create")) 335 + return; 336 + 337 + for (i = 0; i < ARRAY_SIZE(insns); i++) { 338 + val.orig_off = i; 339 + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem")) 340 + goto cleanup; 341 + } 342 + 343 + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 344 + if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) 345 + goto cleanup; 346 + 347 + /* correctness: now freeze the map, the program should load fine */ 348 + 349 + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 350 + goto cleanup; 351 + 352 + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 353 + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) 354 + goto cleanup; 355 + 356 + for (i = 0; i < ARRAY_SIZE(insns); i++) { 357 + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) 358 + goto cleanup; 359 + 360 + ASSERT_EQ(val.xlated_off, i, "val should be equal i"); 361 + } 362 + 363 + cleanup: 364 + close(prog_fd); 365 + close(map_fd); 366 + } 367 + 368 + /* Map can be used only by one BPF program */ 369 + static void check_no_map_reuse(void) 370 + { 371 + struct bpf_insn insns[] = { 372 + BPF_MOV64_IMM(BPF_REG_0, 0), 373 + BPF_EXIT_INSN(), 374 + }; 375 + int prog_fd = -1, map_fd, extra_fd = -1; 376 + struct bpf_insn_array_value val = {}; 377 + int i; 378 + 379 + map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, ARRAY_SIZE(insns)); 380 + if (!ASSERT_GE(map_fd, 0, "map_create")) 381 + return; 382 + 383 + for (i = 0; i < ARRAY_SIZE(insns); i++) { 384 + val.orig_off = i; 385 + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem")) 386 + goto cleanup; 387 + } 388 + 389 + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 390 + goto cleanup; 391 + 392 + prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 393 + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) 394 + goto cleanup; 395 + 396 + for (i = 0; i < ARRAY_SIZE(insns); i++) { 397 + if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem")) 398 + goto cleanup; 399 + 400 + ASSERT_EQ(val.xlated_off, i, "val should be equal i"); 401 + } 402 + 403 + extra_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1); 404 + if (!ASSERT_EQ(extra_fd, -EBUSY, "program should have been rejected (extra_fd != -EBUSY)")) 405 + goto cleanup; 406 + 407 + /* correctness: check that prog is still loadable without fd_array */ 408 + extra_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0); 409 + if (!ASSERT_GE(extra_fd, 0, "bpf(BPF_PROG_LOAD): expected no error")) 410 + goto cleanup; 411 + 412 + cleanup: 413 + close(extra_fd); 414 + close(prog_fd); 415 + close(map_fd); 416 + } 417 + 418 + static void check_bpf_no_lookup(void) 419 + { 420 + struct bpf_insn insns[] = { 421 + BPF_LD_MAP_FD(BPF_REG_1, 0), 422 + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), 423 + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), 424 + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), 425 + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), 426 + BPF_EXIT_INSN(), 427 + }; 428 + int prog_fd = -1, map_fd; 429 + 430 + map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, 1); 431 + if (!ASSERT_GE(map_fd, 0, "map_create")) 432 + return; 433 + 434 + insns[0].imm = map_fd; 435 + 436 + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) 437 + goto cleanup; 438 + 439 + prog_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0); 440 + if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) 441 + goto cleanup; 442 + 443 + /* correctness: check that prog is still loadable with normal map */ 444 + close(map_fd); 445 + map_fd = map_create(BPF_MAP_TYPE_ARRAY, 1); 446 + insns[0].imm = map_fd; 447 + prog_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0); 448 + if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)")) 449 + goto cleanup; 450 + 451 + cleanup: 452 + close(prog_fd); 453 + close(map_fd); 454 + } 455 + 456 + static void check_bpf_side(void) 457 + { 458 + check_bpf_no_lookup(); 459 + } 460 + 461 + static void __test_bpf_insn_array(void) 462 + { 463 + /* Test if offsets are adjusted properly */ 464 + 465 + if (test__start_subtest("one2one")) 466 + check_one_to_one_mapping(); 467 + 468 + if (test__start_subtest("simple")) 469 + check_simple(); 470 + 471 + if (test__start_subtest("deletions")) 472 + check_deletions(); 473 + 474 + if (test__start_subtest("deletions-with-functions")) 475 + check_deletions_with_functions(); 476 + 477 + if (test__start_subtest("blindness")) 478 + check_blindness(); 479 + 480 + /* Check all kinds of operations and related restrictions */ 481 + 482 + if (test__start_subtest("incorrect-index")) 483 + check_incorrect_index(); 484 + 485 + if (test__start_subtest("load-unfrozen-map")) 486 + check_load_unfrozen_map(); 487 + 488 + if (test__start_subtest("no-map-reuse")) 489 + check_no_map_reuse(); 490 + 491 + if (test__start_subtest("bpf-side-ops")) 492 + check_bpf_side(); 493 + } 494 + #else 495 + static void __test_bpf_insn_array(void) 496 + { 497 + test__skip(); 498 + } 499 + #endif 500 + 501 + void test_bpf_insn_array(void) 502 + { 503 + __test_bpf_insn_array(); 504 + }
+2
tools/testing/selftests/bpf/prog_tests/verifier.c
··· 35 35 #include "verifier_global_subprogs.skel.h" 36 36 #include "verifier_global_ptr_args.skel.h" 37 37 #include "verifier_gotol.skel.h" 38 + #include "verifier_gotox.skel.h" 38 39 #include "verifier_helper_access_var_len.skel.h" 39 40 #include "verifier_helper_packet_access.skel.h" 40 41 #include "verifier_helper_restricted.skel.h" ··· 174 173 void test_verifier_global_subprogs(void) { RUN(verifier_global_subprogs); } 175 174 void test_verifier_global_ptr_args(void) { RUN(verifier_global_ptr_args); } 176 175 void test_verifier_gotol(void) { RUN(verifier_gotol); } 176 + void test_verifier_gotox(void) { RUN(verifier_gotox); } 177 177 void test_verifier_helper_access_var_len(void) { RUN(verifier_helper_access_var_len); } 178 178 void test_verifier_helper_packet_access(void) { RUN(verifier_helper_packet_access); } 179 179 void test_verifier_helper_restricted(void) { RUN(verifier_helper_restricted); }
+448
tools/testing/selftests/bpf/progs/bpf_gotox.c
··· 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/bpf_core_read.h> 7 + #include "bpf_misc.h" 8 + 9 + __u64 in_user; 10 + __u64 ret_user; 11 + 12 + int pid; 13 + 14 + /* 15 + * Skip all the tests if compiler doesn't support indirect jumps. 16 + * 17 + * If tests are skipped, then all functions below are compiled as 18 + * dummy, such that the skeleton looks the same, and the userspace 19 + * program can avoid any checks rather than if data->skip is set. 20 + */ 21 + #ifdef __BPF_FEATURE_GOTOX 22 + __u64 skip SEC(".data") = 0; 23 + #else 24 + __u64 skip = 1; 25 + #endif 26 + 27 + struct simple_ctx { 28 + __u64 x; 29 + }; 30 + 31 + #ifdef __BPF_FEATURE_GOTOX 32 + __u64 some_var; 33 + 34 + /* 35 + * This function adds code which will be replaced by a different 36 + * number of instructions by the verifier. This adds additional 37 + * stress on testing the insn_array maps corresponding to indirect jumps. 38 + */ 39 + static __always_inline void adjust_insns(__u64 x) 40 + { 41 + some_var ^= x + bpf_jiffies64(); 42 + } 43 + 44 + SEC("syscall") 45 + int one_switch(struct simple_ctx *ctx) 46 + { 47 + switch (ctx->x) { 48 + case 0: 49 + adjust_insns(ctx->x + 1); 50 + ret_user = 2; 51 + break; 52 + case 1: 53 + adjust_insns(ctx->x + 7); 54 + ret_user = 3; 55 + break; 56 + case 2: 57 + adjust_insns(ctx->x + 9); 58 + ret_user = 4; 59 + break; 60 + case 3: 61 + adjust_insns(ctx->x + 11); 62 + ret_user = 5; 63 + break; 64 + case 4: 65 + adjust_insns(ctx->x + 17); 66 + ret_user = 7; 67 + break; 68 + default: 69 + adjust_insns(ctx->x + 177); 70 + ret_user = 19; 71 + break; 72 + } 73 + 74 + return 0; 75 + } 76 + 77 + SEC("syscall") 78 + int one_switch_non_zero_sec_off(struct simple_ctx *ctx) 79 + { 80 + switch (ctx->x) { 81 + case 0: 82 + adjust_insns(ctx->x + 1); 83 + ret_user = 2; 84 + break; 85 + case 1: 86 + adjust_insns(ctx->x + 7); 87 + ret_user = 3; 88 + break; 89 + case 2: 90 + adjust_insns(ctx->x + 9); 91 + ret_user = 4; 92 + break; 93 + case 3: 94 + adjust_insns(ctx->x + 11); 95 + ret_user = 5; 96 + break; 97 + case 4: 98 + adjust_insns(ctx->x + 17); 99 + ret_user = 7; 100 + break; 101 + default: 102 + adjust_insns(ctx->x + 177); 103 + ret_user = 19; 104 + break; 105 + } 106 + 107 + return 0; 108 + } 109 + 110 + SEC("fentry/" SYS_PREFIX "sys_nanosleep") 111 + int simple_test_other_sec(struct pt_regs *ctx) 112 + { 113 + __u64 x = in_user; 114 + 115 + if (bpf_get_current_pid_tgid() >> 32 != pid) 116 + return 0; 117 + 118 + switch (x) { 119 + case 0: 120 + adjust_insns(x + 1); 121 + ret_user = 2; 122 + break; 123 + case 1: 124 + adjust_insns(x + 7); 125 + ret_user = 3; 126 + break; 127 + case 2: 128 + adjust_insns(x + 9); 129 + ret_user = 4; 130 + break; 131 + case 3: 132 + adjust_insns(x + 11); 133 + ret_user = 5; 134 + break; 135 + case 4: 136 + adjust_insns(x + 17); 137 + ret_user = 7; 138 + break; 139 + default: 140 + adjust_insns(x + 177); 141 + ret_user = 19; 142 + break; 143 + } 144 + 145 + return 0; 146 + } 147 + 148 + SEC("syscall") 149 + int two_switches(struct simple_ctx *ctx) 150 + { 151 + switch (ctx->x) { 152 + case 0: 153 + adjust_insns(ctx->x + 1); 154 + ret_user = 2; 155 + break; 156 + case 1: 157 + adjust_insns(ctx->x + 7); 158 + ret_user = 3; 159 + break; 160 + case 2: 161 + adjust_insns(ctx->x + 9); 162 + ret_user = 4; 163 + break; 164 + case 3: 165 + adjust_insns(ctx->x + 11); 166 + ret_user = 5; 167 + break; 168 + case 4: 169 + adjust_insns(ctx->x + 17); 170 + ret_user = 7; 171 + break; 172 + default: 173 + adjust_insns(ctx->x + 177); 174 + ret_user = 19; 175 + break; 176 + } 177 + 178 + switch (ctx->x + !!ret_user) { 179 + case 1: 180 + adjust_insns(ctx->x + 7); 181 + ret_user = 103; 182 + break; 183 + case 2: 184 + adjust_insns(ctx->x + 9); 185 + ret_user = 104; 186 + break; 187 + case 3: 188 + adjust_insns(ctx->x + 11); 189 + ret_user = 107; 190 + break; 191 + case 4: 192 + adjust_insns(ctx->x + 11); 193 + ret_user = 205; 194 + break; 195 + case 5: 196 + adjust_insns(ctx->x + 11); 197 + ret_user = 115; 198 + break; 199 + default: 200 + adjust_insns(ctx->x + 177); 201 + ret_user = 1019; 202 + break; 203 + } 204 + 205 + return 0; 206 + } 207 + 208 + SEC("syscall") 209 + int big_jump_table(struct simple_ctx *ctx __attribute__((unused))) 210 + { 211 + const void *const jt[256] = { 212 + [0 ... 255] = &&default_label, 213 + [0] = &&l0, 214 + [11] = &&l11, 215 + [27] = &&l27, 216 + [31] = &&l31, 217 + }; 218 + 219 + goto *jt[ctx->x & 0xff]; 220 + 221 + l0: 222 + adjust_insns(ctx->x + 1); 223 + ret_user = 2; 224 + return 0; 225 + 226 + l11: 227 + adjust_insns(ctx->x + 7); 228 + ret_user = 3; 229 + return 0; 230 + 231 + l27: 232 + adjust_insns(ctx->x + 9); 233 + ret_user = 4; 234 + return 0; 235 + 236 + l31: 237 + adjust_insns(ctx->x + 11); 238 + ret_user = 5; 239 + return 0; 240 + 241 + default_label: 242 + adjust_insns(ctx->x + 177); 243 + ret_user = 19; 244 + return 0; 245 + } 246 + 247 + SEC("syscall") 248 + int one_jump_two_maps(struct simple_ctx *ctx __attribute__((unused))) 249 + { 250 + __label__ l1, l2, l3, l4; 251 + void *jt1[2] = { &&l1, &&l2 }; 252 + void *jt2[2] = { &&l3, &&l4 }; 253 + unsigned int a = ctx->x % 2; 254 + unsigned int b = (ctx->x / 2) % 2; 255 + volatile int ret = 0; 256 + 257 + if (!(a < 2 && b < 2)) 258 + return 19; 259 + 260 + if (ctx->x % 2) 261 + goto *jt1[a]; 262 + else 263 + goto *jt2[b]; 264 + 265 + l1: ret += 1; 266 + l2: ret += 3; 267 + l3: ret += 5; 268 + l4: ret += 7; 269 + 270 + ret_user = ret; 271 + return ret; 272 + } 273 + 274 + SEC("syscall") 275 + int one_map_two_jumps(struct simple_ctx *ctx __attribute__((unused))) 276 + { 277 + __label__ l1, l2, l3; 278 + void *jt[3] = { &&l1, &&l2, &&l3 }; 279 + unsigned int a = (ctx->x >> 2) & 1; 280 + unsigned int b = (ctx->x >> 3) & 1; 281 + volatile int ret = 0; 282 + 283 + if (ctx->x % 2) 284 + goto *jt[a]; 285 + 286 + if (ctx->x % 3) 287 + goto *jt[a + b]; 288 + 289 + l1: ret += 3; 290 + l2: ret += 5; 291 + l3: ret += 7; 292 + 293 + ret_user = ret; 294 + return ret; 295 + } 296 + 297 + /* Just to introduce some non-zero offsets in .text */ 298 + static __noinline int f0(volatile struct simple_ctx *ctx __arg_ctx) 299 + { 300 + if (ctx) 301 + return 1; 302 + else 303 + return 13; 304 + } 305 + 306 + SEC("syscall") int f1(struct simple_ctx *ctx) 307 + { 308 + ret_user = 0; 309 + return f0(ctx); 310 + } 311 + 312 + static __noinline int __static_global(__u64 x) 313 + { 314 + switch (x) { 315 + case 0: 316 + adjust_insns(x + 1); 317 + ret_user = 2; 318 + break; 319 + case 1: 320 + adjust_insns(x + 7); 321 + ret_user = 3; 322 + break; 323 + case 2: 324 + adjust_insns(x + 9); 325 + ret_user = 4; 326 + break; 327 + case 3: 328 + adjust_insns(x + 11); 329 + ret_user = 5; 330 + break; 331 + case 4: 332 + adjust_insns(x + 17); 333 + ret_user = 7; 334 + break; 335 + default: 336 + adjust_insns(x + 177); 337 + ret_user = 19; 338 + break; 339 + } 340 + 341 + return 0; 342 + } 343 + 344 + SEC("syscall") 345 + int use_static_global1(struct simple_ctx *ctx) 346 + { 347 + ret_user = 0; 348 + return __static_global(ctx->x); 349 + } 350 + 351 + SEC("syscall") 352 + int use_static_global2(struct simple_ctx *ctx) 353 + { 354 + ret_user = 0; 355 + adjust_insns(ctx->x + 1); 356 + return __static_global(ctx->x); 357 + } 358 + 359 + SEC("fentry/" SYS_PREFIX "sys_nanosleep") 360 + int use_static_global_other_sec(void *ctx) 361 + { 362 + if (bpf_get_current_pid_tgid() >> 32 != pid) 363 + return 0; 364 + 365 + return __static_global(in_user); 366 + } 367 + 368 + __noinline int __nonstatic_global(__u64 x) 369 + { 370 + switch (x) { 371 + case 0: 372 + adjust_insns(x + 1); 373 + ret_user = 2; 374 + break; 375 + case 1: 376 + adjust_insns(x + 7); 377 + ret_user = 3; 378 + break; 379 + case 2: 380 + adjust_insns(x + 9); 381 + ret_user = 4; 382 + break; 383 + case 3: 384 + adjust_insns(x + 11); 385 + ret_user = 5; 386 + break; 387 + case 4: 388 + adjust_insns(x + 17); 389 + ret_user = 7; 390 + break; 391 + default: 392 + adjust_insns(x + 177); 393 + ret_user = 19; 394 + break; 395 + } 396 + 397 + return 0; 398 + } 399 + 400 + SEC("syscall") 401 + int use_nonstatic_global1(struct simple_ctx *ctx) 402 + { 403 + ret_user = 0; 404 + return __nonstatic_global(ctx->x); 405 + } 406 + 407 + SEC("syscall") 408 + int use_nonstatic_global2(struct simple_ctx *ctx) 409 + { 410 + ret_user = 0; 411 + adjust_insns(ctx->x + 1); 412 + return __nonstatic_global(ctx->x); 413 + } 414 + 415 + SEC("fentry/" SYS_PREFIX "sys_nanosleep") 416 + int use_nonstatic_global_other_sec(void *ctx) 417 + { 418 + if (bpf_get_current_pid_tgid() >> 32 != pid) 419 + return 0; 420 + 421 + return __nonstatic_global(in_user); 422 + } 423 + 424 + #else /* __BPF_FEATURE_GOTOX */ 425 + 426 + #define SKIP_TEST(TEST_NAME) \ 427 + SEC("syscall") int TEST_NAME(void *ctx) \ 428 + { \ 429 + return 0; \ 430 + } 431 + 432 + SKIP_TEST(one_switch); 433 + SKIP_TEST(one_switch_non_zero_sec_off); 434 + SKIP_TEST(simple_test_other_sec); 435 + SKIP_TEST(two_switches); 436 + SKIP_TEST(big_jump_table); 437 + SKIP_TEST(one_jump_two_maps); 438 + SKIP_TEST(one_map_two_jumps); 439 + SKIP_TEST(use_static_global1); 440 + SKIP_TEST(use_static_global2); 441 + SKIP_TEST(use_static_global_other_sec); 442 + SKIP_TEST(use_nonstatic_global1); 443 + SKIP_TEST(use_nonstatic_global2); 444 + SKIP_TEST(use_nonstatic_global_other_sec); 445 + 446 + #endif /* __BPF_FEATURE_GOTOX */ 447 + 448 + char _license[] SEC("license") = "GPL";
+389
tools/testing/selftests/bpf/progs/verifier_gotox.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2025 Isovalent */ 3 + 4 + #include <linux/bpf.h> 5 + #include <bpf/bpf_helpers.h> 6 + #include "bpf_misc.h" 7 + #include "../../../include/linux/filter.h" 8 + 9 + #ifdef __TARGET_ARCH_x86 10 + 11 + #define DEFINE_SIMPLE_JUMP_TABLE_PROG(NAME, SRC_REG, OFF, IMM, OUTCOME) \ 12 + \ 13 + SEC("socket") \ 14 + OUTCOME \ 15 + __naked void jump_table_ ## NAME(void) \ 16 + { \ 17 + asm volatile (" \ 18 + .pushsection .jumptables,\"\",@progbits; \ 19 + jt0_%=: \ 20 + .quad ret0_%= - socket; \ 21 + .quad ret1_%= - socket; \ 22 + .size jt0_%=, 16; \ 23 + .global jt0_%=; \ 24 + .popsection; \ 25 + \ 26 + r0 = jt0_%= ll; \ 27 + r0 += 8; \ 28 + r0 = *(u64 *)(r0 + 0); \ 29 + .8byte %[gotox_r0]; \ 30 + ret0_%=: \ 31 + r0 = 0; \ 32 + exit; \ 33 + ret1_%=: \ 34 + r0 = 1; \ 35 + exit; \ 36 + " : \ 37 + : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, (SRC_REG), (OFF) , (IMM))) \ 38 + : __clobber_all); \ 39 + } 40 + 41 + /* 42 + * The first program which doesn't use reserved fields 43 + * loads and works properly. The rest fail to load. 44 + */ 45 + DEFINE_SIMPLE_JUMP_TABLE_PROG(ok, BPF_REG_0, 0, 0, __success __retval(1)) 46 + DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_src_reg, BPF_REG_1, 0, 0, __failure __msg("BPF_JA|BPF_X uses reserved fields")) 47 + DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_off, BPF_REG_0, 1, 0, __failure __msg("BPF_JA|BPF_X uses reserved fields")) 48 + DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_imm, BPF_REG_0, 0, 1, __failure __msg("BPF_JA|BPF_X uses reserved fields")) 49 + 50 + /* 51 + * Gotox is forbidden when there is no jump table loaded 52 + * which points to the sub-function where the gotox is used 53 + */ 54 + SEC("socket") 55 + __failure __msg("no jump tables found for subprog starting at 0") 56 + __naked void jump_table_no_jump_table(void) 57 + { 58 + asm volatile (" \ 59 + .8byte %[gotox_r0]; \ 60 + r0 = 1; \ 61 + exit; \ 62 + " : \ 63 + : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0)) 64 + : __clobber_all); 65 + } 66 + 67 + /* 68 + * Incorrect type of the target register, only PTR_TO_INSN allowed 69 + */ 70 + SEC("socket") 71 + __failure __msg("R1 has type scalar, expected PTR_TO_INSN") 72 + __naked void jump_table_incorrect_dst_reg_type(void) 73 + { 74 + asm volatile (" \ 75 + .pushsection .jumptables,\"\",@progbits; \ 76 + jt0_%=: \ 77 + .quad ret0_%= - socket; \ 78 + .quad ret1_%= - socket; \ 79 + .size jt0_%=, 16; \ 80 + .global jt0_%=; \ 81 + .popsection; \ 82 + \ 83 + r0 = jt0_%= ll; \ 84 + r0 += 8; \ 85 + r0 = *(u64 *)(r0 + 0); \ 86 + r1 = 42; \ 87 + .8byte %[gotox_r1]; \ 88 + ret0_%=: \ 89 + r0 = 0; \ 90 + exit; \ 91 + ret1_%=: \ 92 + r0 = 1; \ 93 + exit; \ 94 + " : \ 95 + : __imm_insn(gotox_r1, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_1, 0, 0 , 0)) 96 + : __clobber_all); 97 + } 98 + 99 + #define DEFINE_INVALID_SIZE_PROG(READ_SIZE, OUTCOME) \ 100 + \ 101 + SEC("socket") \ 102 + OUTCOME \ 103 + __naked void jump_table_invalid_read_size_ ## READ_SIZE(void) \ 104 + { \ 105 + asm volatile (" \ 106 + .pushsection .jumptables,\"\",@progbits; \ 107 + jt0_%=: \ 108 + .quad ret0_%= - socket; \ 109 + .quad ret1_%= - socket; \ 110 + .size jt0_%=, 16; \ 111 + .global jt0_%=; \ 112 + .popsection; \ 113 + \ 114 + r0 = jt0_%= ll; \ 115 + r0 += 8; \ 116 + r0 = *(" #READ_SIZE " *)(r0 + 0); \ 117 + .8byte %[gotox_r0]; \ 118 + ret0_%=: \ 119 + r0 = 0; \ 120 + exit; \ 121 + ret1_%=: \ 122 + r0 = 1; \ 123 + exit; \ 124 + " : \ 125 + : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0)) \ 126 + : __clobber_all); \ 127 + } 128 + 129 + DEFINE_INVALID_SIZE_PROG(u32, __failure __msg("Invalid read of 4 bytes from insn_array")) 130 + DEFINE_INVALID_SIZE_PROG(u16, __failure __msg("Invalid read of 2 bytes from insn_array")) 131 + DEFINE_INVALID_SIZE_PROG(u8, __failure __msg("Invalid read of 1 bytes from insn_array")) 132 + 133 + SEC("socket") 134 + __failure __msg("misaligned value access off 0+1+0 size 8") 135 + __naked void jump_table_misaligned_access(void) 136 + { 137 + asm volatile (" \ 138 + .pushsection .jumptables,\"\",@progbits; \ 139 + jt0_%=: \ 140 + .quad ret0_%= - socket; \ 141 + .quad ret1_%= - socket; \ 142 + .size jt0_%=, 16; \ 143 + .global jt0_%=; \ 144 + .popsection; \ 145 + \ 146 + r0 = jt0_%= ll; \ 147 + r0 += 1; \ 148 + r0 = *(u64 *)(r0 + 0); \ 149 + .8byte %[gotox_r0]; \ 150 + ret0_%=: \ 151 + r0 = 0; \ 152 + exit; \ 153 + ret1_%=: \ 154 + r0 = 1; \ 155 + exit; \ 156 + " : \ 157 + : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0)) 158 + : __clobber_all); 159 + } 160 + 161 + SEC("socket") 162 + __failure __msg("invalid access to map value, value_size=16 off=24 size=8") 163 + __naked void jump_table_invalid_mem_acceess_pos(void) 164 + { 165 + asm volatile (" \ 166 + .pushsection .jumptables,\"\",@progbits; \ 167 + jt0_%=: \ 168 + .quad ret0_%= - socket; \ 169 + .quad ret1_%= - socket; \ 170 + .size jt0_%=, 16; \ 171 + .global jt0_%=; \ 172 + .popsection; \ 173 + \ 174 + r0 = jt0_%= ll; \ 175 + r0 += 24; \ 176 + r0 = *(u64 *)(r0 + 0); \ 177 + .8byte %[gotox_r0]; \ 178 + ret0_%=: \ 179 + r0 = 0; \ 180 + exit; \ 181 + ret1_%=: \ 182 + r0 = 1; \ 183 + exit; \ 184 + " : \ 185 + : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0)) 186 + : __clobber_all); 187 + } 188 + 189 + SEC("socket") 190 + __failure __msg("invalid access to map value, value_size=16 off=-24 size=8") 191 + __naked void jump_table_invalid_mem_acceess_neg(void) 192 + { 193 + asm volatile (" \ 194 + .pushsection .jumptables,\"\",@progbits; \ 195 + jt0_%=: \ 196 + .quad ret0_%= - socket; \ 197 + .quad ret1_%= - socket; \ 198 + .size jt0_%=, 16; \ 199 + .global jt0_%=; \ 200 + .popsection; \ 201 + \ 202 + r0 = jt0_%= ll; \ 203 + r0 -= 24; \ 204 + r0 = *(u64 *)(r0 + 0); \ 205 + .8byte %[gotox_r0]; \ 206 + ret0_%=: \ 207 + r0 = 0; \ 208 + exit; \ 209 + ret1_%=: \ 210 + r0 = 1; \ 211 + exit; \ 212 + " : \ 213 + : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0)) 214 + : __clobber_all); 215 + } 216 + 217 + SEC("socket") 218 + __success __retval(1) 219 + __naked void jump_table_add_sub_ok(void) 220 + { 221 + asm volatile (" \ 222 + .pushsection .jumptables,\"\",@progbits; \ 223 + jt0_%=: \ 224 + .quad ret0_%= - socket; \ 225 + .quad ret1_%= - socket; \ 226 + .size jt0_%=, 16; \ 227 + .global jt0_%=; \ 228 + .popsection; \ 229 + \ 230 + r0 = jt0_%= ll; \ 231 + r0 -= 24; \ 232 + r0 += 32; \ 233 + r0 = *(u64 *)(r0 + 0); \ 234 + .8byte %[gotox_r0]; \ 235 + ret0_%=: \ 236 + r0 = 0; \ 237 + exit; \ 238 + ret1_%=: \ 239 + r0 = 1; \ 240 + exit; \ 241 + " : \ 242 + : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0)) 243 + : __clobber_all); 244 + } 245 + 246 + SEC("socket") 247 + __failure __msg("writes into insn_array not allowed") 248 + __naked void jump_table_no_writes(void) 249 + { 250 + asm volatile (" \ 251 + .pushsection .jumptables,\"\",@progbits; \ 252 + jt0_%=: \ 253 + .quad ret0_%= - socket; \ 254 + .quad ret1_%= - socket; \ 255 + .size jt0_%=, 16; \ 256 + .global jt0_%=; \ 257 + .popsection; \ 258 + \ 259 + r0 = jt0_%= ll; \ 260 + r0 += 8; \ 261 + r1 = 0xbeef; \ 262 + *(u64 *)(r0 + 0) = r1; \ 263 + .8byte %[gotox_r0]; \ 264 + ret0_%=: \ 265 + r0 = 0; \ 266 + exit; \ 267 + ret1_%=: \ 268 + r0 = 1; \ 269 + exit; \ 270 + " : \ 271 + : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0)) 272 + : __clobber_all); 273 + } 274 + 275 + #define DEFINE_JUMP_TABLE_USE_REG(REG) \ 276 + SEC("socket") \ 277 + __success __retval(1) \ 278 + __naked void jump_table_use_reg_r ## REG(void) \ 279 + { \ 280 + asm volatile (" \ 281 + .pushsection .jumptables,\"\",@progbits; \ 282 + jt0_%=: \ 283 + .quad ret0_%= - socket; \ 284 + .quad ret1_%= - socket; \ 285 + .size jt0_%=, 16; \ 286 + .global jt0_%=; \ 287 + .popsection; \ 288 + \ 289 + r0 = jt0_%= ll; \ 290 + r0 += 8; \ 291 + r" #REG " = *(u64 *)(r0 + 0); \ 292 + .8byte %[gotox_rX]; \ 293 + ret0_%=: \ 294 + r0 = 0; \ 295 + exit; \ 296 + ret1_%=: \ 297 + r0 = 1; \ 298 + exit; \ 299 + " : \ 300 + : __imm_insn(gotox_rX, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_ ## REG, 0, 0 , 0)) \ 301 + : __clobber_all); \ 302 + } 303 + 304 + DEFINE_JUMP_TABLE_USE_REG(0) 305 + DEFINE_JUMP_TABLE_USE_REG(1) 306 + DEFINE_JUMP_TABLE_USE_REG(2) 307 + DEFINE_JUMP_TABLE_USE_REG(3) 308 + DEFINE_JUMP_TABLE_USE_REG(4) 309 + DEFINE_JUMP_TABLE_USE_REG(5) 310 + DEFINE_JUMP_TABLE_USE_REG(6) 311 + DEFINE_JUMP_TABLE_USE_REG(7) 312 + DEFINE_JUMP_TABLE_USE_REG(8) 313 + DEFINE_JUMP_TABLE_USE_REG(9) 314 + 315 + __used static int test_subprog(void) 316 + { 317 + return 0; 318 + } 319 + 320 + SEC("socket") 321 + __failure __msg("jump table for insn 4 points outside of the subprog [0,10]") 322 + __naked void jump_table_outside_subprog(void) 323 + { 324 + asm volatile (" \ 325 + .pushsection .jumptables,\"\",@progbits; \ 326 + jt0_%=: \ 327 + .quad ret0_%= - socket; \ 328 + .quad ret1_%= - socket; \ 329 + .quad ret_out_%= - socket; \ 330 + .size jt0_%=, 24; \ 331 + .global jt0_%=; \ 332 + .popsection; \ 333 + \ 334 + r0 = jt0_%= ll; \ 335 + r0 += 8; \ 336 + r0 = *(u64 *)(r0 + 0); \ 337 + .8byte %[gotox_r0]; \ 338 + ret0_%=: \ 339 + r0 = 0; \ 340 + exit; \ 341 + ret1_%=: \ 342 + r0 = 1; \ 343 + call test_subprog; \ 344 + exit; \ 345 + ret_out_%=: \ 346 + " : \ 347 + : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0)) 348 + : __clobber_all); 349 + } 350 + 351 + SEC("socket") 352 + __success __retval(1) 353 + __naked void jump_table_contains_non_unique_values(void) 354 + { 355 + asm volatile (" \ 356 + .pushsection .jumptables,\"\",@progbits; \ 357 + jt0_%=: \ 358 + .quad ret0_%= - socket; \ 359 + .quad ret1_%= - socket; \ 360 + .quad ret0_%= - socket; \ 361 + .quad ret1_%= - socket; \ 362 + .quad ret0_%= - socket; \ 363 + .quad ret1_%= - socket; \ 364 + .quad ret0_%= - socket; \ 365 + .quad ret1_%= - socket; \ 366 + .quad ret0_%= - socket; \ 367 + .quad ret1_%= - socket; \ 368 + .size jt0_%=, 80; \ 369 + .global jt0_%=; \ 370 + .popsection; \ 371 + \ 372 + r0 = jt0_%= ll; \ 373 + r0 += 8; \ 374 + r0 = *(u64 *)(r0 + 0); \ 375 + .8byte %[gotox_r0]; \ 376 + ret0_%=: \ 377 + r0 = 0; \ 378 + exit; \ 379 + ret1_%=: \ 380 + r0 = 1; \ 381 + exit; \ 382 + " : \ 383 + : __imm_insn(gotox_r0, BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_0, 0, 0 , 0)) 384 + : __clobber_all); 385 + } 386 + 387 + #endif /* __TARGET_ARCH_x86 */ 388 + 389 + char _license[] SEC("license") = "GPL";