Pull bpf updates from Alexei Starovoitov: - Fix and improve BTF deduplication of identical BTF types (Alan Maguire and Andrii Nakryiko) - Support up to 12 arguments in BPF trampoline on arm64 (Xu Kuohai and Alexis Lothoré) - Support load-acquire and store-release instructions in BPF JIT on riscv64 (Andrea Parri) - Fix uninitialized values in BPF_{CORE,PROBE}_READ macros (Anton Protopopov) - Streamline allowed helpers across program types (Feng Yang) - Support atomic update for hashtab of BPF maps (Hou Tao) - Implement json output for BPF helpers (Ihor Solodrai) - Several s390 JIT fixes (Ilya Leoshkevich) - Various sockmap fixes (Jiayuan Chen) - Support mmap of vmlinux BTF data (Lorenz Bauer) - Support BPF rbtree traversal and list peeking (Martin KaFai Lau) - Tests for sockmap/sockhash redirection (Michal Luczaj) - Introduce kfuncs for memory reads into dynptrs (Mykyta Yatsenko) - Add support for dma-buf iterators in BPF (T.J. Mercier) - The verifier support for __bpf_trap() (Yonghong Song) * tag 'bpf-next-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (135 commits) bpf, arm64: Remove unused-but-set function and variable. selftests/bpf: Add tests with stack ptr register in conditional jmp bpf: Do not include stack ptr register in precision backtracking bookkeeping selftests/bpf: enable many-args tests for arm64 bpf, arm64: Support up to 12 function arguments bpf: Check rcu_read_lock_trace_held() in bpf_map_lookup_percpu_elem() bpf: Avoid __bpf_prog_ret0_warn when jit fails bpftool: Add support for custom BTF path in prog load/loadall selftests/bpf: Add unit tests with __bpf_trap() kfunc bpf: Warn with __bpf_trap() kfunc maybe due to uninitialized variable bpf: Remove special_kfunc_set from verifier selftests/bpf: Add test for open coded dmabuf_iter selftests/bpf: Add test for dmabuf_iter bpf: Add open coded dmabuf iterator bpf: Add dmabuf iterator dma-buf: Rename debugfs symbols bpf: Fix error return value in bpf_copy_from_user_dynptr libbpf: Use mmap to parse vmlinux BTF from sysfs selftests: bpf: Add a test for mmapable vmlinux BTF btf: Allow mmap of vmlinux btf ...
Unified · Split
| @@ -2,10 +2,117 @@ · 1 unmodified line | |||
| 2 | BPF Iterators | 2 | BPF Iterators |
| 3 | ============= | 3 | ============= |
| 4 | 4 | ||
| 5 | -------- | ||
| 6 | Overview | ||
| 7 | -------- | ||
| 8 | |||
| 9 | BPF supports two separate entities collectively known as "BPF iterators": BPF | ||
| 10 | iterator *program type* and *open-coded* BPF iterators. The former is | ||
| 11 | a stand-alone BPF program type which, when attached and activated by user, | ||
| 12 | will be called once for each entity (task_struct, cgroup, etc) that is being | ||
| 13 | iterated. The latter is a set of BPF-side APIs implementing iterator | ||
| 14 | functionality and available across multiple BPF program types. Open-coded | ||
| 15 | iterators provide similar functionality to BPF iterator programs, but gives | ||
| 16 | more flexibility and control to all other BPF program types. BPF iterator | ||
| 17 | programs, on the other hand, can be used to implement anonymous or BPF | ||
| 18 | FS-mounted special files, whose contents are generated by attached BPF iterator | ||
| 19 | program, backed by seq_file functionality. Both are useful depending on | ||
| 20 | specific needs. | ||
| 21 | |||
| 22 | When adding a new BPF iterator program, it is expected that similar | ||
| 23 | functionality will be added as open-coded iterator for maximum flexibility. | ||
| 24 | It's also expected that iteration logic and code will be maximally shared and | ||
| 25 | reused between two iterator API surfaces. | ||
| 5 | 26 | ||
| 6 | ---------- | 27 | ------------------------ |
| 7 | Motivation | 28 | Open-coded BPF Iterators |
| 8 | ---------- | 29 | ------------------------ |
| 30 | |||
| 31 | Open-coded BPF iterators are implemented as tightly-coupled trios of kfuncs | ||
| 32 | (constructor, next element fetch, destructor) and iterator-specific type | ||
| 33 | describing on-the-stack iterator state, which is guaranteed by the BPF | ||
| 34 | verifier to not be tampered with outside of the corresponding | ||
| 35 | constructor/destructor/next APIs. | ||
| 36 | |||
| 37 | Each kind of open-coded BPF iterator has its own associated | ||
| 38 | struct bpf_iter_<type>, where <type> denotes a specific type of iterator. | ||
| 39 | bpf_iter_<type> state needs to live on BPF program stack, so make sure it's | ||
| 40 | small enough to fit on BPF stack. For performance reasons its best to avoid | ||
| 41 | dynamic memory allocation for iterator state and size the state struct big | ||
| 42 | enough to fit everything necessary. But if necessary, dynamic memory | ||
| 43 | allocation is a way to bypass BPF stack limitations. Note, state struct size | ||
| 44 | is part of iterator's user-visible API, so changing it will break backwards | ||
| 45 | compatibility, so be deliberate about designing it. | ||
| 46 | |||
| 47 | All kfuncs (constructor, next, destructor) have to be named consistently as | ||
| 48 | bpf_iter_<type>_{new,next,destroy}(), respectively. <type> represents iterator | ||
| 49 | type, and iterator state should be represented as a matching | ||
| 50 | `struct bpf_iter_<type>` state type. Also, all iter kfuncs should have | ||
| 51 | a pointer to this `struct bpf_iter_<type>` as the very first argument. | ||
| 52 | |||
| 53 | Additionally: | ||
| 54 | - Constructor, i.e., `bpf_iter_<type>_new()`, can have arbitrary extra | ||
| 55 | number of arguments. Return type is not enforced either. | ||
| 56 | - Next method, i.e., `bpf_iter_<type>_next()`, has to return a pointer | ||
| 57 | type and should have exactly one argument: `struct bpf_iter_<type> *` | ||
| 58 | (const/volatile/restrict and typedefs are ignored). | ||
| 59 | - Destructor, i.e., `bpf_iter_<type>_destroy()`, should return void and | ||
| 60 | should have exactly one argument, similar to the next method. | ||
| 61 | - `struct bpf_iter_<type>` size is enforced to be positive and | ||
| 62 | a multiple of 8 bytes (to fit stack slots correctly). | ||
| 63 | |||
| 64 | Such strictness and consistency allows to build generic helpers abstracting | ||
| 65 | important, but boilerplate, details to be able to use open-coded iterators | ||
| 66 | effectively and ergonomically (see libbpf's bpf_for_each() macro). This is | ||
| 67 | enforced at kfunc registration point by the kernel. | ||
| 68 | |||
| 69 | Constructor/next/destructor implementation contract is as follows: | ||
| 70 | - constructor, `bpf_iter_<type>_new()`, always initializes iterator state on | ||
| 71 | the stack. If any of the input arguments are invalid, constructor should | ||
| 72 | make sure to still initialize it such that subsequent next() calls will | ||
| 73 | return NULL. I.e., on error, *return error and construct empty iterator*. | ||
| 74 | Constructor kfunc is marked with KF_ITER_NEW flag. | ||
| 75 | |||
| 76 | - next method, `bpf_iter_<type>_next()`, accepts pointer to iterator state | ||
| 77 | and produces an element. Next method should always return a pointer. The | ||
| 78 | contract between BPF verifier is that next method *guarantees* that it | ||
| 79 | will eventually return NULL when elements are exhausted. Once NULL is | ||
| 80 | returned, subsequent next calls *should keep returning NULL*. Next method | ||
| 81 | is marked with KF_ITER_NEXT (and should also have KF_RET_NULL as | ||
| 82 | NULL-returning kfunc, of course). | ||
| 83 | |||
| 84 | - destructor, `bpf_iter_<type>_destroy()`, is always called once. Even if | ||
| 85 | constructor failed or next returned nothing. Destructor frees up any | ||
| 86 | resources and marks stack space used by `struct bpf_iter_<type>` as usable | ||
| 87 | for something else. Destructor is marked with KF_ITER_DESTROY flag. | ||
| 88 | |||
| 89 | Any open-coded BPF iterator implementation has to implement at least these | ||
| 90 | three methods. It is enforced that for any given type of iterator only | ||
| 91 | applicable constructor/destructor/next are callable. I.e., verifier ensures | ||
| 92 | you can't pass number iterator state into, say, cgroup iterator's next method. | ||
| 93 | |||
| 94 | From a 10,000-feet BPF verification point of view, next methods are the points | ||
| 95 | of forking a verification state, which are conceptually similar to what | ||
| 96 | verifier is doing when validating conditional jumps. Verifier is branching out | ||
| 97 | `call bpf_iter_<type>_next` instruction and simulates two outcomes: NULL | ||
| 98 | (iteration is done) and non-NULL (new element is returned). NULL is simulated | ||
| 99 | first and is supposed to reach exit without looping. After that non-NULL case | ||
| 100 | is validated and it either reaches exit (for trivial examples with no real | ||
| 101 | loop), or reaches another `call bpf_iter_<type>_next` instruction with the | ||
| 102 | state equivalent to already (partially) validated one. State equivalency at | ||
| 103 | that point means we technically are going to be looping forever without | ||
| 104 | "breaking out" out of established "state envelope" (i.e., subsequent | ||
| 105 | iterations don't add any new knowledge or constraints to the verifier state, | ||
| 106 | so running 1, 2, 10, or a million of them doesn't matter). But taking into | ||
| 107 | account the contract stating that iterator next method *has to* return NULL | ||
| 108 | eventually, we can conclude that loop body is safe and will eventually | ||
| 109 | terminate. Given we validated logic outside of the loop (NULL case), and | ||
| 110 | concluded that loop body is safe (though potentially looping many times), | ||
| 111 | verifier can claim safety of the overall program logic. | ||
| 112 | |||
| 113 | ------------------------ | ||
| 114 | BPF Iterators Motivation | ||
| 115 | ------------------------ | ||
| 9 | 116 | ||
| 10 | There are a few existing ways to dump kernel data into user space. The most | 117 | There are a few existing ways to dump kernel data into user space. The most |
| 11 | popular one is the ``/proc`` system. For example, ``cat /proc/net/tcp6`` dumps | 118 | popular one is the ``/proc`` system. For example, ``cat /proc/net/tcp6`` dumps |
| @@ -323,8 +430,8 @@ Now, in the userspace program, pass the pointer of struct to the · 311 unmodified lines | |||
| 323 | 430 | ||
| 324 | :: | 431 | :: |
| 325 | 432 | ||
| 326 | link = bpf_program__attach_iter(prog, &opts); iter_fd = | 433 | link = bpf_program__attach_iter(prog, &opts); |
| 327 | bpf_iter_create(bpf_link__fd(link)); | 434 | iter_fd = bpf_iter_create(bpf_link__fd(link)); |
| 328 | 435 | ||
| 329 | If both *tid* and *pid* are zero, an iterator created from this struct | 436 | If both *tid* and *pid* are zero, an iterator created from this struct |
| 330 | ``bpf_iter_attach_opts`` will include every opened file of every task in the | 437 | ``bpf_iter_attach_opts`` will include every opened file of every task in the |
| @@ -160,6 +160,23 @@ Or:: · 159 unmodified lines | |||
| 160 | ... | 160 | ... |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | 2.2.6 __prog Annotation | ||
| 164 | --------------------------- | ||
| 165 | This annotation is used to indicate that the argument needs to be fixed up to | ||
| 166 | the bpf_prog_aux of the caller BPF program. Any value passed into this argument | ||
| 167 | is ignored, and rewritten by the verifier. | ||
| 168 | |||
| 169 | An example is given below:: | ||
| 170 | |||
| 171 | __bpf_kfunc int bpf_wq_set_callback_impl(struct bpf_wq *wq, | ||
| 172 | int (callback_fn)(void *map, int *key, void *value), | ||
| 173 | unsigned int flags, | ||
| 174 | void *aux__prog) | ||
| 175 | { | ||
| 176 | struct bpf_prog_aux *aux = aux__prog; | ||
| 177 | ... | ||
| 178 | } | ||
| 179 | |||
| 163 | .. _BPF_kfunc_nodef: | 180 | .. _BPF_kfunc_nodef: |
| 164 | 181 | ||
| 165 | 2.3 Using an existing kernel function | 182 | 2.3 Using an existing kernel function |
| @@ -2113,7 +2113,7 @@ bool bpf_jit_supports_subprog_tailcalls(void) · 2112 unmodified lines | |||
| 2113 | } | 2113 | } |
| 2114 | 2114 | ||
| 2115 | static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, | 2115 | static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, |
| 2116 | int args_off, int retval_off, int run_ctx_off, | 2116 | int bargs_off, int retval_off, int run_ctx_off, |
| 2117 | bool save_ret) | 2117 | bool save_ret) |
| 2118 | { | 2118 | { |
| 2119 | __le32 *branch; | 2119 | __le32 *branch; |
| @@ -2155,7 +2155,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, · 35 unmodified lines | |||
| 2155 | branch = ctx->image + ctx->idx; | 2155 | branch = ctx->image + ctx->idx; |
| 2156 | emit(A64_NOP, ctx); | 2156 | emit(A64_NOP, ctx); |
| 2157 | 2157 | ||
| 2158 | emit(A64_ADD_I(1, A64_R(0), A64_SP, args_off), ctx); | 2158 | emit(A64_ADD_I(1, A64_R(0), A64_SP, bargs_off), ctx); |
| 2159 | if (!p->jited) | 2159 | if (!p->jited) |
| 2160 | emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx); | 2160 | emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx); |
| 2161 | 2161 | ||
| @@ -2180,7 +2180,7 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_link *l, · 18 unmodified lines | |||
| 2180 | } | 2180 | } |
| 2181 | 2181 | ||
| 2182 | static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, | 2182 | static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, |
| 2183 | int args_off, int retval_off, int run_ctx_off, | 2183 | int bargs_off, int retval_off, int run_ctx_off, |
| 2184 | __le32 **branches) | 2184 | __le32 **branches) |
| 2185 | { | 2185 | { |
| 2186 | int i; | 2186 | int i; |
| @@ -2190,7 +2190,7 @@ static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, · 3 unmodified lines | |||
| 2190 | */ | 2190 | */ |
| 2191 | emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx); | 2191 | emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx); |
| 2192 | for (i = 0; i < tl->nr_links; i++) { | 2192 | for (i = 0; i < tl->nr_links; i++) { |
| 2193 | invoke_bpf_prog(ctx, tl->links[i], args_off, retval_off, | 2193 | invoke_bpf_prog(ctx, tl->links[i], bargs_off, retval_off, |
| 2194 | run_ctx_off, true); | 2194 | run_ctx_off, true); |
| 2195 | /* if (*(u64 *)(sp + retval_off) != 0) | 2195 | /* if (*(u64 *)(sp + retval_off) != 0) |
| 2196 | * goto do_fexit; | 2196 | * goto do_fexit; |
| @@ -2204,23 +2204,125 @@ static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_links *tl, · 7 unmodified lines | |||
| 2204 | } | 2204 | } |
| 2205 | } | 2205 | } |
| 2206 | 2206 | ||
| 2207 | static void save_args(struct jit_ctx *ctx, int args_off, int nregs) | 2207 | struct arg_aux { |
| 2208 | /* how many args are passed through registers, the rest of the args are | ||
| 2209 | * passed through stack | ||
| 2210 | */ | ||
| 2211 | int args_in_regs; | ||
| 2212 | /* how many registers are used to pass arguments */ | ||
| 2213 | int regs_for_args; | ||
| 2214 | /* how much stack is used for additional args passed to bpf program | ||
| 2215 | * that did not fit in original function registers | ||
| 2216 | */ | ||
| 2217 | int bstack_for_args; | ||
| 2218 | /* home much stack is used for additional args passed to the | ||
| 2219 | * original function when called from trampoline (this one needs | ||
| 2220 | * arguments to be properly aligned) | ||
| 2221 | */ | ||
| 2222 | int ostack_for_args; | ||
| 2223 | }; | ||
| 2224 | |||
| 2225 | static int calc_arg_aux(const struct btf_func_model *m, | ||
| 2226 | struct arg_aux *a) | ||
| 2208 | { | 2227 | { |
| 2209 | int i; | 2228 | int stack_slots, nregs, slots, i; |
| 2229 | |||
| 2230 | /* verifier ensures m->nr_args <= MAX_BPF_FUNC_ARGS */ | ||
| 2231 | for (i = 0, nregs = 0; i < m->nr_args; i++) { | ||
| 2232 | slots = (m->arg_size[i] + 7) / 8; | ||
| 2233 | if (nregs + slots <= 8) /* passed through register ? */ | ||
| 2234 | nregs += slots; | ||
| 2235 | else | ||
| 2236 | break; | ||
| 2237 | } | ||
| 2238 | |||
| 2239 | a->args_in_regs = i; | ||
| 2240 | a->regs_for_args = nregs; | ||
| 2241 | a->ostack_for_args = 0; | ||
| 2242 | a->bstack_for_args = 0; | ||
| 2243 | |||
| 2244 | /* the rest arguments are passed through stack */ | ||
| 2245 | for (; i < m->nr_args; i++) { | ||
| 2246 | /* We can not know for sure about exact alignment needs for | ||
| 2247 | * struct passed on stack, so deny those | ||
| 2248 | */ | ||
| 2249 | if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) | ||
| 2250 | return -ENOTSUPP; | ||
| 2251 | stack_slots = (m->arg_size[i] + 7) / 8; | ||
| 2252 | a->bstack_for_args += stack_slots * 8; | ||
| 2253 | a->ostack_for_args = a->ostack_for_args + stack_slots * 8; | ||
| 2254 | } | ||
| 2255 | |||
| 2256 | return 0; | ||
| 2257 | } | ||
| 2210 | 2258 | ||
| 2211 | for (i = 0; i < nregs; i++) { | 2259 | static void clear_garbage(struct jit_ctx *ctx, int reg, int effective_bytes) |
| 2212 | emit(A64_STR64I(i, A64_SP, args_off), ctx); | 2260 | { |
| 2213 | args_off += 8; | 2261 | if (effective_bytes) { |
| 2262 | int garbage_bits = 64 - 8 * effective_bytes; | ||
| 2263 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
| 2264 | /* garbage bits are at the right end */ | ||
| 2265 | emit(A64_LSR(1, reg, reg, garbage_bits), ctx); | ||
| 2266 | emit(A64_LSL(1, reg, reg, garbage_bits), ctx); | ||
| 2267 | #else | ||
| 2268 | /* garbage bits are at the left end */ | ||
| 2269 | emit(A64_LSL(1, reg, reg, garbage_bits), ctx); | ||
| 2270 | emit(A64_LSR(1, reg, reg, garbage_bits), ctx); | ||
| 2271 | #endif | ||
| 2214 | } | 2272 | } |
| 2215 | } | 2273 | } |
| 2216 | 2274 | ||
| 2217 | static void restore_args(struct jit_ctx *ctx, int args_off, int nregs) | 2275 | static void save_args(struct jit_ctx *ctx, int bargs_off, int oargs_off, |
| 2276 | const struct btf_func_model *m, | ||
| 2277 | const struct arg_aux *a, | ||
| 2278 | bool for_call_origin) | ||
| 2218 | { | 2279 | { |
| 2219 | int i; | 2280 | int i; |
| 2281 | int reg; | ||
| 2282 | int doff; | ||
| 2283 | int soff; | ||
| 2284 | int slots; | ||
| 2285 | u8 tmp = bpf2a64[TMP_REG_1]; | ||
| 2286 | |||
| 2287 | /* store arguments to the stack for the bpf program, or restore | ||
| 2288 | * arguments from stack for the original function | ||
| 2289 | */ | ||
| 2290 | for (reg = 0; reg < a->regs_for_args; reg++) { | ||
| 2291 | emit(for_call_origin ? | ||
| 2292 | A64_LDR64I(reg, A64_SP, bargs_off) : | ||
| 2293 | A64_STR64I(reg, A64_SP, bargs_off), | ||
| 2294 | ctx); | ||
| 2295 | bargs_off += 8; | ||
| 2296 | } | ||
| 2297 | |||
| 2298 | soff = 32; /* on stack arguments start from FP + 32 */ | ||
| 2299 | doff = (for_call_origin ? oargs_off : bargs_off); | ||
| 2300 | |||
| 2301 | /* save on stack arguments */ | ||
| 2302 | for (i = a->args_in_regs; i < m->nr_args; i++) { | ||
| 2303 | slots = (m->arg_size[i] + 7) / 8; | ||
| 2304 | /* verifier ensures arg_size <= 16, so slots equals 1 or 2 */ | ||
| 2305 | while (slots-- > 0) { | ||
| 2306 | emit(A64_LDR64I(tmp, A64_FP, soff), ctx); | ||
| 2307 | /* if there is unused space in the last slot, clear | ||
| 2308 | * the garbage contained in the space. | ||
| 2309 | */ | ||
| 2310 | if (slots == 0 && !for_call_origin) | ||
| 2311 | clear_garbage(ctx, tmp, m->arg_size[i] % 8); | ||
| 2312 | emit(A64_STR64I(tmp, A64_SP, doff), ctx); | ||
| 2313 | soff += 8; | ||
| 2314 | doff += 8; | ||
| 2315 | } | ||
| 2316 | } | ||
| 2317 | } | ||
| 2220 | 2318 | ||
| 2221 | for (i = 0; i < nregs; i++) { | 2319 | static void restore_args(struct jit_ctx *ctx, int bargs_off, int nregs) |
| 2222 | emit(A64_LDR64I(i, A64_SP, args_off), ctx); | 2320 | { |
| 2223 | args_off += 8; | 2321 | int reg; |
| 2322 | |||
| 2323 | for (reg = 0; reg < nregs; reg++) { | ||
| 2324 | emit(A64_LDR64I(reg, A64_SP, bargs_off), ctx); | ||
| 2325 | bargs_off += 8; | ||
| 2224 | } | 2326 | } |
| 2225 | } | 2327 | } |
| 2226 | 2328 | ||
| @@ -2243,17 +2345,21 @@ static bool is_struct_ops_tramp(const struct bpf_tramp_links *fentry_links) · 16 unmodified lines | |||
| 2243 | */ | 2345 | */ |
| 2244 | static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, | 2346 | static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, |
| 2245 | struct bpf_tramp_links *tlinks, void *func_addr, | 2347 | struct bpf_tramp_links *tlinks, void *func_addr, |
| 2246 | int nregs, u32 flags) | 2348 | const struct btf_func_model *m, |
| 2349 | const struct arg_aux *a, | ||
| 2350 | u32 flags) | ||
| 2247 | { | 2351 | { |
| 2248 | int i; | 2352 | int i; |
| 2249 | int stack_size; | 2353 | int stack_size; |
| 2250 | int retaddr_off; | 2354 | int retaddr_off; |
| 2251 | int regs_off; | 2355 | int regs_off; |
| 2252 | int retval_off; | 2356 | int retval_off; |
| 2253 | int args_off; | 2357 | int bargs_off; |
| 2254 | int nregs_off; | 2358 | int nfuncargs_off; |
| 2255 | int ip_off; | 2359 | int ip_off; |
| 2256 | int run_ctx_off; | 2360 | int run_ctx_off; |
| 2361 | int oargs_off; | ||
| 2362 | int nfuncargs; | ||
| 2257 | struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; | 2363 | struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY]; |
| 2258 | struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; | 2364 | struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT]; |
| 2259 | struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; | 2365 | struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN]; |
| @@ -2262,31 +2368,38 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 2 unmodified lines | |||
| 2262 | bool is_struct_ops = is_struct_ops_tramp(fentry); | 2368 | bool is_struct_ops = is_struct_ops_tramp(fentry); |
| 2263 | 2369 | ||
| 2264 | /* trampoline stack layout: | 2370 | /* trampoline stack layout: |
| 2265 | * [ parent ip ] | 2371 | * [ parent ip ] |
| 2266 | * [ FP ] | 2372 | * [ FP ] |
| 2267 | * SP + retaddr_off [ self ip ] | 2373 | * SP + retaddr_off [ self ip ] |
| 2268 | * [ FP ] | 2374 | * [ FP ] |
| 2269 | * | 2375 | * |
| 2270 | * [ padding ] align SP to multiples of 16 | 2376 | * [ padding ] align SP to multiples of 16 |
| 2271 | * | 2377 | * |
| 2272 | * [ x20 ] callee saved reg x20 | 2378 | * [ x20 ] callee saved reg x20 |
| 2273 | * SP + regs_off [ x19 ] callee saved reg x19 | 2379 | * SP + regs_off [ x19 ] callee saved reg x19 |
| 2274 | * | 2380 | * |
| 2275 | * SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or | 2381 | * SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or |
| 2276 | * BPF_TRAMP_F_RET_FENTRY_RET | 2382 | * BPF_TRAMP_F_RET_FENTRY_RET |
| 2383 | * [ arg reg N ] | ||
| 2384 | * [ ... ] | ||
| 2385 | * SP + bargs_off [ arg reg 1 ] for bpf | ||
| 2277 | * | 2386 | * |
| 2278 | * [ arg reg N ] | 2387 | * SP + nfuncargs_off [ arg regs count ] |
| 2279 | * [ ... ] | ||
| 2280 | * SP + args_off [ arg reg 1 ] | ||
| 2281 | * | 2388 | * |
| 2282 | * SP + nregs_off [ arg regs count ] | 2389 | * SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag |
| 2283 | * | 2390 | * |
| 2284 | * SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag | 2391 | * SP + run_ctx_off [ bpf_tramp_run_ctx ] |
| 2285 | * | 2392 | * |
| 2286 | * SP + run_ctx_off [ bpf_tramp_run_ctx ] | 2393 | * [ stack arg N ] |
| 2394 | * [ ... ] | ||
| 2395 | * SP + oargs_off [ stack arg 1 ] for original func | ||
| 2287 | */ | 2396 | */ |
| 2288 | 2397 | ||
| 2289 | stack_size = 0; | 2398 | stack_size = 0; |
| 2399 | oargs_off = stack_size; | ||
| 2400 | if (flags & BPF_TRAMP_F_CALL_ORIG) | ||
| 2401 | stack_size += a->ostack_for_args; | ||
| 2402 | |||
| 2290 | run_ctx_off = stack_size; | 2403 | run_ctx_off = stack_size; |
| 2291 | /* room for bpf_tramp_run_ctx */ | 2404 | /* room for bpf_tramp_run_ctx */ |
| 2292 | stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8); | 2405 | stack_size += round_up(sizeof(struct bpf_tramp_run_ctx), 8); |
| @@ -2296,13 +2409,14 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 3 unmodified lines | |||
| 2296 | if (flags & BPF_TRAMP_F_IP_ARG) | 2409 | if (flags & BPF_TRAMP_F_IP_ARG) |
| 2297 | stack_size += 8; | 2410 | stack_size += 8; |
| 2298 | 2411 | ||
| 2299 | nregs_off = stack_size; | 2412 | nfuncargs_off = stack_size; |
| 2300 | /* room for args count */ | 2413 | /* room for args count */ |
| 2301 | stack_size += 8; | 2414 | stack_size += 8; |
| 2302 | 2415 | ||
| 2303 | args_off = stack_size; | 2416 | bargs_off = stack_size; |
| 2304 | /* room for args */ | 2417 | /* room for args */ |
| 2305 | stack_size += nregs * 8; | 2418 | nfuncargs = a->regs_for_args + a->bstack_for_args / 8; |
| 2419 | stack_size += 8 * nfuncargs; | ||
| 2306 | 2420 | ||
| 2307 | /* room for return value */ | 2421 | /* room for return value */ |
| 2308 | retval_off = stack_size; | 2422 | retval_off = stack_size; |
| @@ -2349,11 +2463,11 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 40 unmodified lines | |||
| 2349 | } | 2463 | } |
| 2350 | 2464 | ||
| 2351 | /* save arg regs count*/ | 2465 | /* save arg regs count*/ |
| 2352 | emit(A64_MOVZ(1, A64_R(10), nregs, 0), ctx); | 2466 | emit(A64_MOVZ(1, A64_R(10), nfuncargs, 0), ctx); |
| 2353 | emit(A64_STR64I(A64_R(10), A64_SP, nregs_off), ctx); | 2467 | emit(A64_STR64I(A64_R(10), A64_SP, nfuncargs_off), ctx); |
| 2354 | 2468 | ||
| 2355 | /* save arg regs */ | 2469 | /* save args for bpf */ |
| 2356 | save_args(ctx, args_off, nregs); | 2470 | save_args(ctx, bargs_off, oargs_off, m, a, false); |
| 2357 | 2471 | ||
| 2358 | /* save callee saved registers */ | 2472 | /* save callee saved registers */ |
| 2359 | emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx); | 2473 | emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx); |
| @@ -2369,7 +2483,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 9 unmodified lines | |||
| 2369 | } | 2483 | } |
| 2370 | 2484 | ||
| 2371 | for (i = 0; i < fentry->nr_links; i++) | 2485 | for (i = 0; i < fentry->nr_links; i++) |
| 2372 | invoke_bpf_prog(ctx, fentry->links[i], args_off, | 2486 | invoke_bpf_prog(ctx, fentry->links[i], bargs_off, |
| 2373 | retval_off, run_ctx_off, | 2487 | retval_off, run_ctx_off, |
| 2374 | flags & BPF_TRAMP_F_RET_FENTRY_RET); | 2488 | flags & BPF_TRAMP_F_RET_FENTRY_RET); |
| 2375 | 2489 | ||
| @@ -2379,12 +2493,13 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 3 unmodified lines | |||
| 2379 | if (!branches) | 2493 | if (!branches) |
| 2380 | return -ENOMEM; | 2494 | return -ENOMEM; |
| 2381 | 2495 | ||
| 2382 | invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off, | 2496 | invoke_bpf_mod_ret(ctx, fmod_ret, bargs_off, retval_off, |
| 2383 | run_ctx_off, branches); | 2497 | run_ctx_off, branches); |
| 2384 | } | 2498 | } |
| 2385 | 2499 | ||
| 2386 | if (flags & BPF_TRAMP_F_CALL_ORIG) { | 2500 | if (flags & BPF_TRAMP_F_CALL_ORIG) { |
| 2387 | restore_args(ctx, args_off, nregs); | 2501 | /* save args for original func */ |
| 2502 | save_args(ctx, bargs_off, oargs_off, m, a, true); | ||
| 2388 | /* call original func */ | 2503 | /* call original func */ |
| 2389 | emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx); | 2504 | emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx); |
| 2390 | emit(A64_ADR(A64_LR, AARCH64_INSN_SIZE * 2), ctx); | 2505 | emit(A64_ADR(A64_LR, AARCH64_INSN_SIZE * 2), ctx); |
| @@ -2403,7 +2518,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 12 unmodified lines | |||
| 2403 | } | 2518 | } |
| 2404 | 2519 | ||
| 2405 | for (i = 0; i < fexit->nr_links; i++) | 2520 | for (i = 0; i < fexit->nr_links; i++) |
| 2406 | invoke_bpf_prog(ctx, fexit->links[i], args_off, retval_off, | 2521 | invoke_bpf_prog(ctx, fexit->links[i], bargs_off, retval_off, |
| 2407 | run_ctx_off, false); | 2522 | run_ctx_off, false); |
| 2408 | 2523 | ||
| 2409 | if (flags & BPF_TRAMP_F_CALL_ORIG) { | 2524 | if (flags & BPF_TRAMP_F_CALL_ORIG) { |
| @@ -2417,7 +2532,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 7 unmodified lines | |||
| 2417 | } | 2532 | } |
| 2418 | 2533 | ||
| 2419 | if (flags & BPF_TRAMP_F_RESTORE_REGS) | 2534 | if (flags & BPF_TRAMP_F_RESTORE_REGS) |
| 2420 | restore_args(ctx, args_off, nregs); | 2535 | restore_args(ctx, bargs_off, a->regs_for_args); |
| 2421 | 2536 | ||
| 2422 | /* restore callee saved register x19 and x20 */ | 2537 | /* restore callee saved register x19 and x20 */ |
| 2423 | emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx); | 2538 | emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx); |
| @@ -2454,21 +2569,6 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im, · 30 unmodified lines | |||
| 2454 | return ctx->idx; | 2569 | return ctx->idx; |
| 2455 | } | 2570 | } |
| 2456 | 2571 | ||
| 2457 | static int btf_func_model_nregs(const struct btf_func_model *m) | ||
| 2458 | { | ||
| 2459 | int nregs = m->nr_args; | ||
| 2460 | int i; | ||
| 2461 | |||
| 2462 | /* extra registers needed for struct argument */ | ||
| 2463 | for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) { | ||
| 2464 | /* The arg_size is at most 16 bytes, enforced by the verifier. */ | ||
| 2465 | if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) | ||
| 2466 | nregs += (m->arg_size[i] + 7) / 8 - 1; | ||
| 2467 | } | ||
| 2468 | |||
| 2469 | return nregs; | ||
| 2470 | } | ||
| 2471 | |||
| 2472 | int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, | 2572 | int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, |
| 2473 | struct bpf_tramp_links *tlinks, void *func_addr) | 2573 | struct bpf_tramp_links *tlinks, void *func_addr) |
| 2474 | { | 2574 | { |
| @@ -2477,14 +2577,14 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags, · 2 unmodified lines | |||
| 2477 | .idx = 0, | 2577 | .idx = 0, |
| 2478 | }; | 2578 | }; |
| 2479 | struct bpf_tramp_image im; | 2579 | struct bpf_tramp_image im; |
| 2480 | int nregs, ret; | 2580 | struct arg_aux aaux; |
| 2581 | int ret; | ||
| 2481 | 2582 | ||
| 2482 | nregs = btf_func_model_nregs(m); | 2583 | ret = calc_arg_aux(m, &aaux); |
| 2483 | /* the first 8 registers are used for arguments */ | 2584 | if (ret < 0) |
| 2484 | if (nregs > 8) | 2585 | return ret; |
| 2485 | return -ENOTSUPP; | ||
| 2486 | 2586 | ||
| 2487 | ret = prepare_trampoline(&ctx, &im, tlinks, func_addr, nregs, flags); | 2587 | ret = prepare_trampoline(&ctx, &im, tlinks, func_addr, m, &aaux, flags); |
| 2488 | if (ret < 0) | 2588 | if (ret < 0) |
| 2489 | return ret; | 2589 | return ret; |
| 2490 | 2590 | ||
| @@ -2511,9 +2611,10 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image, · 20 unmodified lines | |||
| 2511 | u32 flags, struct bpf_tramp_links *tlinks, | 2611 | u32 flags, struct bpf_tramp_links *tlinks, |
| 2512 | void *func_addr) | 2612 | void *func_addr) |
| 2513 | { | 2613 | { |
| 2514 | int ret, nregs; | ||
| 2515 | void *image, *tmp; | ||
| 2516 | u32 size = ro_image_end - ro_image; | 2614 | u32 size = ro_image_end - ro_image; |
| 2615 | struct arg_aux aaux; | ||
| 2616 | void *image, *tmp; | ||
| 2617 | int ret; | ||
| 2517 | 2618 | ||
| 2518 | /* image doesn't need to be in module memory range, so we can | 2619 | /* image doesn't need to be in module memory range, so we can |
| 2519 | * use kvmalloc. | 2620 | * use kvmalloc. |
| @@ -2529,13 +2630,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image, · 9 unmodified lines | |||
| 2529 | .write = true, | 2630 | .write = true, |
| 2530 | }; | 2631 | }; |
| 2531 | 2632 | ||
| 2532 | nregs = btf_func_model_nregs(m); | ||
| 2533 | /* the first 8 registers are used for arguments */ | ||
| 2534 | if (nregs > 8) | ||
| 2535 | return -ENOTSUPP; | ||
| 2536 | 2633 | ||
| 2537 | jit_fill_hole(image, (unsigned int)(ro_image_end - ro_image)); | 2634 | jit_fill_hole(image, (unsigned int)(ro_image_end - ro_image)); |
| 2538 | ret = prepare_trampoline(&ctx, im, tlinks, func_addr, nregs, flags); | 2635 | ret = calc_arg_aux(m, &aaux); |
| 2636 | if (ret) | ||
| 2637 | goto out; | ||
| 2638 | ret = prepare_trampoline(&ctx, im, tlinks, func_addr, m, &aaux, flags); | ||
| 2539 | 2639 | ||
| 2540 | if (ret > 0 && validate_code(&ctx) < 0) { | 2640 | if (ret > 0 && validate_code(&ctx) < 0) { |
| 2541 | ret = -EINVAL; | 2641 | ret = -EINVAL; |
| @@ -608,6 +608,21 @@ static inline u32 rv_fence(u8 pred, u8 succ) · 607 unmodified lines | |||
| 608 | return rv_i_insn(imm11_0, 0, 0, 0, 0xf); | 608 | return rv_i_insn(imm11_0, 0, 0, 0, 0xf); |
| 609 | } | 609 | } |
| 610 | 610 | ||
| 611 | static inline void emit_fence_r_rw(struct rv_jit_context *ctx) | ||
| 612 | { | ||
| 613 | emit(rv_fence(0x2, 0x3), ctx); | ||
| 614 | } | ||
| 615 | |||
| 616 | static inline void emit_fence_rw_w(struct rv_jit_context *ctx) | ||
| 617 | { | ||
| 618 | emit(rv_fence(0x3, 0x1), ctx); | ||
| 619 | } | ||
| 620 | |||
| 621 | static inline void emit_fence_rw_rw(struct rv_jit_context *ctx) | ||
| 622 | { | ||
| 623 | emit(rv_fence(0x3, 0x3), ctx); | ||
| 624 | } | ||
| 625 | |||
| 611 | static inline u32 rv_nop(void) | 626 | static inline u32 rv_nop(void) |
| 612 | { | 627 | { |
| 613 | return rv_i_insn(0, 0, 0, 0, 0x13); | 628 | return rv_i_insn(0, 0, 0, 0, 0x13); |
| @@ -473,11 +473,212 @@ static inline void emit_kcfi(u32 hash, struct rv_jit_context *ctx) · 472 unmodified lines | |||
| 473 | emit(hash, ctx); | 473 | emit(hash, ctx); |
| 474 | } | 474 | } |
| 475 | 475 | ||
| 476 | static void emit_atomic(u8 rd, u8 rs, s16 off, s32 imm, bool is64, | 476 | static int emit_load_8(bool sign_ext, u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx) |
| 477 | struct rv_jit_context *ctx) | 477 | { |
| 478 | int insns_start; | ||
| 479 | |||
| 480 | if (is_12b_int(off)) { | ||
| 481 | insns_start = ctx->ninsns; | ||
| 482 | if (sign_ext) | ||
| 483 | emit(rv_lb(rd, off, rs), ctx); | ||
| 484 | else | ||
| 485 | emit(rv_lbu(rd, off, rs), ctx); | ||
| 486 | return ctx->ninsns - insns_start; | ||
| 487 | } | ||
| 488 | |||
| 489 | emit_imm(RV_REG_T1, off, ctx); | ||
| 490 | emit_add(RV_REG_T1, RV_REG_T1, rs, ctx); | ||
| 491 | insns_start = ctx->ninsns; | ||
| 492 | if (sign_ext) | ||
| 493 | emit(rv_lb(rd, 0, RV_REG_T1), ctx); | ||
| 494 | else | ||
| 495 | emit(rv_lbu(rd, 0, RV_REG_T1), ctx); | ||
| 496 | return ctx->ninsns - insns_start; | ||
| 497 | } | ||
| 498 | |||
| 499 | static int emit_load_16(bool sign_ext, u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx) | ||
| 500 | { | ||
| 501 | int insns_start; | ||
| 502 | |||
| 503 | if (is_12b_int(off)) { | ||
| 504 | insns_start = ctx->ninsns; | ||
| 505 | if (sign_ext) | ||
| 506 | emit(rv_lh(rd, off, rs), ctx); | ||
| 507 | else | ||
| 508 | emit(rv_lhu(rd, off, rs), ctx); | ||
| 509 | return ctx->ninsns - insns_start; | ||
| 510 | } | ||
| 511 | |||
| 512 | emit_imm(RV_REG_T1, off, ctx); | ||
| 513 | emit_add(RV_REG_T1, RV_REG_T1, rs, ctx); | ||
| 514 | insns_start = ctx->ninsns; | ||
| 515 | if (sign_ext) | ||
| 516 | emit(rv_lh(rd, 0, RV_REG_T1), ctx); | ||
| 517 | else | ||
| 518 | emit(rv_lhu(rd, 0, RV_REG_T1), ctx); | ||
| 519 | return ctx->ninsns - insns_start; | ||
| 520 | } | ||
| 521 | |||
| 522 | static int emit_load_32(bool sign_ext, u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx) | ||
| 523 | { | ||
| 524 | int insns_start; | ||
| 525 | |||
| 526 | if (is_12b_int(off)) { | ||
| 527 | insns_start = ctx->ninsns; | ||
| 528 | if (sign_ext) | ||
| 529 | emit(rv_lw(rd, off, rs), ctx); | ||
| 530 | else | ||
| 531 | emit(rv_lwu(rd, off, rs), ctx); | ||
| 532 | return ctx->ninsns - insns_start; | ||
| 533 | } | ||
| 534 | |||
| 535 | emit_imm(RV_REG_T1, off, ctx); | ||
| 536 | emit_add(RV_REG_T1, RV_REG_T1, rs, ctx); | ||
| 537 | insns_start = ctx->ninsns; | ||
| 538 | if (sign_ext) | ||
| 539 | emit(rv_lw(rd, 0, RV_REG_T1), ctx); | ||
| 540 | else | ||
| 541 | emit(rv_lwu(rd, 0, RV_REG_T1), ctx); | ||
| 542 | return ctx->ninsns - insns_start; | ||
| 543 | } | ||
| 544 | |||
| 545 | static int emit_load_64(bool sign_ext, u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx) | ||
| 546 | { | ||
| 547 | int insns_start; | ||
| 548 | |||
| 549 | if (is_12b_int(off)) { | ||
| 550 | insns_start = ctx->ninsns; | ||
| 551 | emit_ld(rd, off, rs, ctx); | ||
| 552 | return ctx->ninsns - insns_start; | ||
| 553 | } | ||
| 554 | |||
| 555 | emit_imm(RV_REG_T1, off, ctx); | ||
| 556 | emit_add(RV_REG_T1, RV_REG_T1, rs, ctx); | ||
| 557 | insns_start = ctx->ninsns; | ||
| 558 | emit_ld(rd, 0, RV_REG_T1, ctx); | ||
| 559 | return ctx->ninsns - insns_start; | ||
| 560 | } | ||
| 561 | |||
| 562 | static void emit_store_8(u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx) | ||
| 563 | { | ||
| 564 | if (is_12b_int(off)) { | ||
| 565 | emit(rv_sb(rd, off, rs), ctx); | ||
| 566 | return; | ||
| 567 | } | ||
| 568 | |||
| 569 | emit_imm(RV_REG_T1, off, ctx); | ||
| 570 | emit_add(RV_REG_T1, RV_REG_T1, rd, ctx); | ||
| 571 | emit(rv_sb(RV_REG_T1, 0, rs), ctx); | ||
| 572 | } | ||
| 573 | |||
| 574 | static void emit_store_16(u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx) | ||
| 575 | { | ||
| 576 | if (is_12b_int(off)) { | ||
| 577 | emit(rv_sh(rd, off, rs), ctx); | ||
| 578 | return; | ||
| 579 | } | ||
| 580 | |||
| 581 | emit_imm(RV_REG_T1, off, ctx); | ||
| 582 | emit_add(RV_REG_T1, RV_REG_T1, rd, ctx); | ||
| 583 | emit(rv_sh(RV_REG_T1, 0, rs), ctx); | ||
| 584 | } | ||
| 585 | |||
| 586 | static void emit_store_32(u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx) | ||
| 587 | { | ||
| 588 | if (is_12b_int(off)) { | ||
| 589 | emit_sw(rd, off, rs, ctx); | ||
| 590 | return; | ||
| 591 | } | ||
| 592 | |||
| 593 | emit_imm(RV_REG_T1, off, ctx); | ||
| 594 | emit_add(RV_REG_T1, RV_REG_T1, rd, ctx); | ||
| 595 | emit_sw(RV_REG_T1, 0, rs, ctx); | ||
| 596 | } | ||
| 597 | |||
| 598 | static void emit_store_64(u8 rd, s32 off, u8 rs, struct rv_jit_context *ctx) | ||
| 599 | { | ||
| 600 | if (is_12b_int(off)) { | ||
| 601 | emit_sd(rd, off, rs, ctx); | ||
| 602 | return; | ||
| 603 | } | ||
| 604 | |||
| 605 | emit_imm(RV_REG_T1, off, ctx); | ||
| 606 | emit_add(RV_REG_T1, RV_REG_T1, rd, ctx); | ||
| 607 | emit_sd(RV_REG_T1, 0, rs, ctx); | ||
| 608 | } | ||
| 609 | |||
| 610 | static int emit_atomic_ld_st(u8 rd, u8 rs, const struct bpf_insn *insn, | ||
| 611 | struct rv_jit_context *ctx) | ||
| 612 | { | ||
| 613 | u8 code = insn->code; | ||
| 614 | s32 imm = insn->imm; | ||
| 615 | s16 off = insn->off; | ||
| 616 | |||
| 617 | switch (imm) { | ||
| 618 | /* dst_reg = load_acquire(src_reg + off16) */ | ||
| 619 | case BPF_LOAD_ACQ: | ||
| 620 | switch (BPF_SIZE(code)) { | ||
| 621 | case BPF_B: | ||
| 622 | emit_load_8(false, rd, off, rs, ctx); | ||
| 623 | break; | ||
| 624 | case BPF_H: | ||
| 625 | emit_load_16(false, rd, off, rs, ctx); | ||
| 626 | break; | ||
| 627 | case BPF_W: | ||
| 628 | emit_load_32(false, rd, off, rs, ctx); | ||
| 629 | break; | ||
| 630 | case BPF_DW: | ||
| 631 | emit_load_64(false, rd, off, rs, ctx); | ||
| 632 | break; | ||
| 633 | } | ||
| 634 | emit_fence_r_rw(ctx); | ||
| 635 | |||
| 636 | /* If our next insn is a redundant zext, return 1 to tell | ||
| 637 | * build_body() to skip it. | ||
| 638 | */ | ||
| 639 | if (BPF_SIZE(code) != BPF_DW && insn_is_zext(&insn[1])) | ||
| 640 | return 1; | ||
| 641 | break; | ||
| 642 | /* store_release(dst_reg + off16, src_reg) */ | ||
| 643 | case BPF_STORE_REL: | ||
| 644 | emit_fence_rw_w(ctx); | ||
| 645 | switch (BPF_SIZE(code)) { | ||
| 646 | case BPF_B: | ||
| 647 | emit_store_8(rd, off, rs, ctx); | ||
| 648 | break; | ||
| 649 | case BPF_H: | ||
| 650 | emit_store_16(rd, off, rs, ctx); | ||
| 651 | break; | ||
| 652 | case BPF_W: | ||
| 653 | emit_store_32(rd, off, rs, ctx); | ||
| 654 | break; | ||
| 655 | case BPF_DW: | ||
| 656 | emit_store_64(rd, off, rs, ctx); | ||
| 657 | break; | ||
| 658 | } | ||
| 659 | break; | ||
| 660 | default: | ||
| 661 | pr_err_once("bpf-jit: invalid atomic load/store opcode %02x\n", imm); | ||
| 662 | return -EINVAL; | ||
| 663 | } | ||
| 664 | |||
| 665 | return 0; | ||
| 666 | } | ||
| 667 | |||
| 668 | static int emit_atomic_rmw(u8 rd, u8 rs, const struct bpf_insn *insn, | ||
| 669 | struct rv_jit_context *ctx) | ||
| 478 | { | 670 | { |
| 479 | u8 r0; | 671 | u8 r0, code = insn->code; |
| 672 | s16 off = insn->off; | ||
| 673 | s32 imm = insn->imm; | ||
| 480 | int jmp_offset; | 674 | int jmp_offset; |
| 675 | bool is64; | ||
| 676 | |||
| 677 | if (BPF_SIZE(code) != BPF_W && BPF_SIZE(code) != BPF_DW) { | ||
| 678 | pr_err_once("bpf-jit: 1- and 2-byte RMW atomics are not supported\n"); | ||
| 679 | return -EINVAL; | ||
| 680 | } | ||
| 681 | is64 = BPF_SIZE(code) == BPF_DW; | ||
| 481 | 682 | ||
| 482 | if (off) { | 683 | if (off) { |
| 483 | if (is_12b_int(off)) { | 684 | if (is_12b_int(off)) { |
| @@ -554,9 +755,14 @@ static void emit_atomic(u8 rd, u8 rs, s16 off, s32 imm, bool is64, · 70 unmodified lines | |||
| 554 | rv_sc_w(RV_REG_T3, rs, rd, 0, 1), ctx); | 755 | rv_sc_w(RV_REG_T3, rs, rd, 0, 1), ctx); |
| 555 | jmp_offset = ninsns_rvoff(-6); | 756 | jmp_offset = ninsns_rvoff(-6); |
| 556 | emit(rv_bne(RV_REG_T3, 0, jmp_offset >> 1), ctx); | 757 | emit(rv_bne(RV_REG_T3, 0, jmp_offset >> 1), ctx); |
| 557 | emit(rv_fence(0x3, 0x3), ctx); | 758 | emit_fence_rw_rw(ctx); |
| 558 | break; | 759 | break; |
| 760 | default: | ||
| 761 | pr_err_once("bpf-jit: invalid atomic RMW opcode %02x\n", imm); | ||
| 762 | return -EINVAL; | ||
| 559 | } | 763 | } |
| 764 | |||
| 765 | return 0; | ||
| 560 | } | 766 | } |
| 561 | 767 | ||
| 562 | #define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) | 768 | #define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0) |
| @@ -1650,8 +1856,8 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, · 1087 unmodified lines | |||
| 1650 | case BPF_LDX | BPF_PROBE_MEM32 | BPF_W: | 1856 | case BPF_LDX | BPF_PROBE_MEM32 | BPF_W: |
| 1651 | case BPF_LDX | BPF_PROBE_MEM32 | BPF_DW: | 1857 | case BPF_LDX | BPF_PROBE_MEM32 | BPF_DW: |
| 1652 | { | 1858 | { |
| 1653 | int insn_len, insns_start; | ||
| 1654 | bool sign_ext; | 1859 | bool sign_ext; |
| 1860 | int insn_len; | ||
| 1655 | 1861 | ||
| 1656 | sign_ext = BPF_MODE(insn->code) == BPF_MEMSX || | 1862 | sign_ext = BPF_MODE(insn->code) == BPF_MEMSX || |
| 1657 | BPF_MODE(insn->code) == BPF_PROBE_MEMSX; | 1863 | BPF_MODE(insn->code) == BPF_PROBE_MEMSX; |
| @@ -1663,78 +1869,16 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, · 5 unmodified lines | |||
| 1663 | 1869 | ||
| 1664 | switch (BPF_SIZE(code)) { | 1870 | switch (BPF_SIZE(code)) { |
| 1665 | case BPF_B: | 1871 | case BPF_B: |
| 1666 | if (is_12b_int(off)) { | 1872 | insn_len = emit_load_8(sign_ext, rd, off, rs, ctx); |
| 1667 | insns_start = ctx->ninsns; | ||
| 1668 | if (sign_ext) | ||
| 1669 | emit(rv_lb(rd, off, rs), ctx); | ||
| 1670 | else | ||
| 1671 | emit(rv_lbu(rd, off, rs), ctx); | ||
| 1672 | insn_len = ctx->ninsns - insns_start; | ||
| 1673 | break; | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | emit_imm(RV_REG_T1, off, ctx); | ||
| 1677 | emit_add(RV_REG_T1, RV_REG_T1, rs, ctx); | ||
| 1678 | insns_start = ctx->ninsns; | ||
| 1679 | if (sign_ext) | ||
| 1680 | emit(rv_lb(rd, 0, RV_REG_T1), ctx); | ||
| 1681 | else | ||
| 1682 | emit(rv_lbu(rd, 0, RV_REG_T1), ctx); | ||
| 1683 | insn_len = ctx->ninsns - insns_start; | ||
| 1684 | break; | 1873 | break; |
| 1685 | case BPF_H: | 1874 | case BPF_H: |
| 1686 | if (is_12b_int(off)) { | 1875 | insn_len = emit_load_16(sign_ext, rd, off, rs, ctx); |
| 1687 | insns_start = ctx->ninsns; | ||
| 1688 | if (sign_ext) | ||
| 1689 | emit(rv_lh(rd, off, rs), ctx); | ||
| 1690 | else | ||
| 1691 | emit(rv_lhu(rd, off, rs), ctx); | ||
| 1692 | insn_len = ctx->ninsns - insns_start; | ||
| 1693 | break; | ||
| 1694 | } | ||
| 1695 | |||
| 1696 | emit_imm(RV_REG_T1, off, ctx); | ||
| 1697 | emit_add(RV_REG_T1, RV_REG_T1, rs, ctx); | ||
| 1698 | insns_start = ctx->ninsns; | ||
| 1699 | if (sign_ext) | ||
| 1700 | emit(rv_lh(rd, 0, RV_REG_T1), ctx); | ||
| 1701 | else | ||
| 1702 | emit(rv_lhu(rd, 0, RV_REG_T1), ctx); | ||
| 1703 | insn_len = ctx->ninsns - insns_start; | ||
| 1704 | break; | 1876 | break; |
| 1705 | case BPF_W: | 1877 | case BPF_W: |
| 1706 | if (is_12b_int(off)) { | 1878 | insn_len = emit_load_32(sign_ext, rd, off, rs, ctx); |
| 1707 | insns_start = ctx->ninsns; | ||
| 1708 | if (sign_ext) | ||
| 1709 | emit(rv_lw(rd, off, rs), ctx); | ||
| 1710 | else | ||
| 1711 | emit(rv_lwu(rd, off, rs), ctx); | ||
| 1712 | insn_len = ctx->ninsns - insns_start; | ||
| 1713 | break; | ||
| 1714 | } | ||
| 1715 | |||
| 1716 | emit_imm(RV_REG_T1, off, ctx); | ||
| 1717 | emit_add(RV_REG_T1, RV_REG_T1, rs, ctx); | ||
| 1718 | insns_start = ctx->ninsns; | ||
| 1719 | if (sign_ext) | ||
| 1720 | emit(rv_lw(rd, 0, RV_REG_T1), ctx); | ||
| 1721 | else | ||
| 1722 | emit(rv_lwu(rd, 0, RV_REG_T1), ctx); | ||
| 1723 | insn_len = ctx->ninsns - insns_start; | ||
| 1724 | break; | 1879 | break; |
| 1725 | case BPF_DW: | 1880 | case BPF_DW: |
| 1726 | if (is_12b_int(off)) { | 1881 | insn_len = emit_load_64(sign_ext, rd, off, rs, ctx); |
| 1727 | insns_start = ctx->ninsns; | ||
| 1728 | emit_ld(rd, off, rs, ctx); | ||
| 1729 | insn_len = ctx->ninsns - insns_start; | ||
| 1730 | break; | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | emit_imm(RV_REG_T1, off, ctx); | ||
| 1734 | emit_add(RV_REG_T1, RV_REG_T1, rs, ctx); | ||
| 1735 | insns_start = ctx->ninsns; | ||
| 1736 | emit_ld(rd, 0, RV_REG_T1, ctx); | ||
| 1737 | insn_len = ctx->ninsns - insns_start; | ||
| 1738 | break; | 1882 | break; |
| 1739 | } | 1883 | } |
| 1740 | 1884 | ||
| @@ -1879,49 +2023,27 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx, · 138 unmodified lines | |||
| 1879 | 2023 | ||
| 1880 | /* STX: *(size *)(dst + off) = src */ | 2024 | /* STX: *(size *)(dst + off) = src */ |
| 1881 | case BPF_STX | BPF_MEM | BPF_B: | 2025 | case BPF_STX | BPF_MEM | BPF_B: |
| 1882 | if (is_12b_int(off)) { | 2026 | emit_store_8(rd, off, rs, ctx); |
| 1883 | emit(rv_sb(rd, off, rs), ctx); | ||
| 1884 | break; | ||
| 1885 | } | ||
| 1886 | |||
| 1887 | emit_imm(RV_REG_T1, off, ctx); | ||
| 1888 | emit_add(RV_REG_T1, RV_REG_T1, rd, ctx); | ||
| 1889 | emit(rv_sb(RV_REG_T1, 0, rs), ctx); | ||
| 1890 | break; | 2027 | break; |
| 1891 | case BPF_STX | BPF_MEM | BPF_H: | 2028 | case BPF_STX | BPF_MEM | BPF_H: |
| 1892 | if (is_12b_int(off)) { | 2029 | emit_store_16(rd, off, rs, ctx); |
| 1893 | emit(rv_sh(rd, off, rs), ctx); | ||
| 1894 | break; | ||
| 1895 | } | ||
| 1896 | |||
| 1897 | emit_imm(RV_REG_T1, off, ctx); | ||
| 1898 | emit_add(RV_REG_T1, RV_REG_T1, rd, ctx); | ||
| 1899 | emit(rv_sh(RV_REG_T1, 0, rs), ctx); | ||
| 1900 | break; | 2030 | break; |
| 1901 | case BPF_STX | BPF_MEM | BPF_W: | 2031 | case BPF_STX | BPF_MEM | BPF_W: |
| 1902 | if (is_12b_int(off)) { | 2032 | emit_store_32(rd, off, rs, ctx); |
| 1903 | emit_sw(rd, off, rs, ctx); | ||
| 1904 | break; | ||
| 1905 | } | ||
| 1906 | |||
| 1907 | emit_imm(RV_REG_T1, off, ctx); | ||
| 1908 | emit_add(RV_REG_T1, RV_REG_T1, rd, ctx); | ||
| 1909 | emit_sw(RV_REG_T1, 0, rs, ctx); | ||
| 1910 | break; | 2033 | break; |
| 1911 | case BPF_STX | BPF_MEM | BPF_DW: | 2034 | case BPF_STX | BPF_MEM | BPF_DW: |
| 1912 | if (is_12b_int(off)) { | 2035 | emit_store_64(rd, off, rs, ctx); |
| 1913 | emit_sd(rd, off, rs, ctx); | ||
| 1914 | break; | ||
| 1915 | } | ||
| 1916 | |||
| 1917 | emit_imm(RV_REG_T1, off, ctx); | ||
| 1918 | emit_add(RV_REG_T1, RV_REG_T1, rd, ctx); | ||
| 1919 | emit_sd(RV_REG_T1, 0, rs, ctx); | ||
| 1920 | break; | 2036 | break; |
| 2037 | case BPF_STX | BPF_ATOMIC | BPF_B: | ||
| 2038 | case BPF_STX | BPF_ATOMIC | BPF_H: | ||
| 1921 | case BPF_STX | BPF_ATOMIC | BPF_W: | 2039 | case BPF_STX | BPF_ATOMIC | BPF_W: |
| 1922 | case BPF_STX | BPF_ATOMIC | BPF_DW: | 2040 | case BPF_STX | BPF_ATOMIC | BPF_DW: |
| 1923 | emit_atomic(rd, rs, off, imm, | 2041 | if (bpf_atomic_is_load_store(insn)) |
| 1924 | BPF_SIZE(code) == BPF_DW, ctx); | 2042 | ret = emit_atomic_ld_st(rd, rs, insn, ctx); |
| 2043 | else | ||
| 2044 | ret = emit_atomic_rmw(rd, rs, insn, ctx); | ||
| 2045 | if (ret) | ||
| 2046 | return ret; | ||
| 1925 | break; | 2047 | break; |
| 1926 | 2048 | ||
| 1927 | case BPF_STX | BPF_PROBE_MEM32 | BPF_B: | 2049 | case BPF_STX | BPF_PROBE_MEM32 | BPF_B: |
| @@ -26,9 +26,8 @@ static int build_body(struct rv_jit_context *ctx, bool extra_pass, int *offset) · 25 unmodified lines | |||
| 26 | int ret; | 26 | int ret; |
| 27 | 27 | ||
| 28 | ret = bpf_jit_emit_insn(insn, ctx, extra_pass); | 28 | ret = bpf_jit_emit_insn(insn, ctx, extra_pass); |
| 29 | /* BPF_LD | BPF_IMM | BPF_DW: skip the next instruction. */ | ||
| 30 | if (ret > 0) | 29 | if (ret > 0) |
| 31 | i++; | 30 | i++; /* skip the next instruction */ |
| 32 | if (offset) | 31 | if (offset) |
| 33 | offset[i] = ctx->ninsns; | 32 | offset[i] = ctx->ninsns; |
| 34 | if (ret < 0) | 33 | if (ret < 0) |
| @@ -26,8 +26,6 @@ static inline bool nospec_uses_trampoline(void) · 25 unmodified lines | |||
| 26 | return __is_defined(CC_USING_EXPOLINE) && !nospec_disable; | 26 | return __is_defined(CC_USING_EXPOLINE) && !nospec_disable; |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | #ifdef CONFIG_EXPOLINE_EXTERN | ||
| 30 | |||
| 31 | void __s390_indirect_jump_r1(void); | 29 | void __s390_indirect_jump_r1(void); |
| 32 | void __s390_indirect_jump_r2(void); | 30 | void __s390_indirect_jump_r2(void); |
| 33 | void __s390_indirect_jump_r3(void); | 31 | void __s390_indirect_jump_r3(void); |
| @@ -44,8 +42,6 @@ void __s390_indirect_jump_r13(void); · 10 unmodified lines | |||
| 44 | void __s390_indirect_jump_r14(void); | 42 | void __s390_indirect_jump_r14(void); |
| 45 | void __s390_indirect_jump_r15(void); | 43 | void __s390_indirect_jump_r15(void); |
| 46 | 44 | ||
| 47 | #endif | ||
| 48 | |||
| 49 | #endif /* __ASSEMBLY__ */ | 45 | #endif /* __ASSEMBLY__ */ |
| 50 | 46 | ||
| 51 | #endif /* _ASM_S390_EXPOLINE_H */ | 47 | #endif /* _ASM_S390_EXPOLINE_H */ |
| @@ -48,8 +48,6 @@ struct bpf_jit { · 47 unmodified lines | |||
| 48 | int lit64; /* Current position in 64-bit literal pool */ | 48 | int lit64; /* Current position in 64-bit literal pool */ |
| 49 | int base_ip; /* Base address for literal pool */ | 49 | int base_ip; /* Base address for literal pool */ |
| 50 | int exit_ip; /* Address of exit */ | 50 | int exit_ip; /* Address of exit */ |
| 51 | int r1_thunk_ip; /* Address of expoline thunk for 'br %r1' */ | ||
| 52 | int r14_thunk_ip; /* Address of expoline thunk for 'br %r14' */ | ||
| 53 | int tail_call_start; /* Tail call start offset */ | 51 | int tail_call_start; /* Tail call start offset */ |
| 54 | int excnt; /* Number of exception table entries */ | 52 | int excnt; /* Number of exception table entries */ |
| 55 | int prologue_plt_ret; /* Return address for prologue hotpatch PLT */ | 53 | int prologue_plt_ret; /* Return address for prologue hotpatch PLT */ |
| @@ -127,6 +125,18 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1) · 71 unmodified lines | |||
| 127 | jit->seen_regs |= (1 << r1); | 125 | jit->seen_regs |= (1 << r1); |
| 128 | } | 126 | } |
| 129 | 127 | ||
| 128 | static s32 off_to_pcrel(struct bpf_jit *jit, u32 off) | ||
| 129 | { | ||
| 130 | return off - jit->prg; | ||
| 131 | } | ||
| 132 | |||
| 133 | static s64 ptr_to_pcrel(struct bpf_jit *jit, const void *ptr) | ||
| 134 | { | ||
| 135 | if (jit->prg_buf) | ||
| 136 | return (const u8 *)ptr - ((const u8 *)jit->prg_buf + jit->prg); | ||
| 137 | return 0; | ||
| 138 | } | ||
| 139 | |||
| 130 | #define REG_SET_SEEN(b1) \ | 140 | #define REG_SET_SEEN(b1) \ |
| 131 | ({ \ | 141 | ({ \ |
| 132 | reg_set_seen(jit, b1); \ | 142 | reg_set_seen(jit, b1); \ |
| @@ -201,7 +211,7 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1) · 68 unmodified lines | |||
| 201 | 211 | ||
| 202 | #define EMIT4_PCREL_RIC(op, mask, target) \ | 212 | #define EMIT4_PCREL_RIC(op, mask, target) \ |
| 203 | ({ \ | 213 | ({ \ |
| 204 | int __rel = ((target) - jit->prg) / 2; \ | 214 | int __rel = off_to_pcrel(jit, target) / 2; \ |
| 205 | _EMIT4((op) | (mask) << 20 | (__rel & 0xffff)); \ | 215 | _EMIT4((op) | (mask) << 20 | (__rel & 0xffff)); \ |
| 206 | }) | 216 | }) |
| 207 | 217 | ||
| @@ -239,7 +249,7 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1) · 31 unmodified lines | |||
| 239 | 249 | ||
| 240 | #define EMIT6_PCREL_RIEB(op1, op2, b1, b2, mask, target) \ | 250 | #define EMIT6_PCREL_RIEB(op1, op2, b1, b2, mask, target) \ |
| 241 | ({ \ | 251 | ({ \ |
| 242 | unsigned int rel = (int)((target) - jit->prg) / 2; \ | 252 | unsigned int rel = off_to_pcrel(jit, target) / 2; \ |
| 243 | _EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), \ | 253 | _EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), \ |
| 244 | (op2) | (mask) << 12); \ | 254 | (op2) | (mask) << 12); \ |
| 245 | REG_SET_SEEN(b1); \ | 255 | REG_SET_SEEN(b1); \ |
| @@ -248,7 +258,7 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1) · 2 unmodified lines | |||
| 248 | 258 | ||
| 249 | #define EMIT6_PCREL_RIEC(op1, op2, b1, imm, mask, target) \ | 259 | #define EMIT6_PCREL_RIEC(op1, op2, b1, imm, mask, target) \ |
| 250 | ({ \ | 260 | ({ \ |
| 251 | unsigned int rel = (int)((target) - jit->prg) / 2; \ | 261 | unsigned int rel = off_to_pcrel(jit, target) / 2; \ |
| 252 | _EMIT6((op1) | (reg_high(b1) | (mask)) << 16 | \ | 262 | _EMIT6((op1) | (reg_high(b1) | (mask)) << 16 | \ |
| 253 | (rel & 0xffff), (op2) | ((imm) & 0xff) << 8); \ | 263 | (rel & 0xffff), (op2) | ((imm) & 0xff) << 8); \ |
| 254 | REG_SET_SEEN(b1); \ | 264 | REG_SET_SEEN(b1); \ |
| @@ -257,29 +267,41 @@ static inline void reg_set_seen(struct bpf_jit *jit, u32 b1) · 2 unmodified lines | |||
| 257 | 267 | ||
| 258 | #define EMIT6_PCREL(op1, op2, b1, b2, i, off, mask) \ | 268 | #define EMIT6_PCREL(op1, op2, b1, b2, i, off, mask) \ |
| 259 | ({ \ | 269 | ({ \ |
| 260 | int rel = (addrs[(i) + (off) + 1] - jit->prg) / 2; \ | 270 | int rel = off_to_pcrel(jit, addrs[(i) + (off) + 1]) / 2;\ |
| 261 | _EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), (op2) | (mask));\ | 271 | _EMIT6((op1) | reg(b1, b2) << 16 | (rel & 0xffff), (op2) | (mask));\ |
| 262 | REG_SET_SEEN(b1); \ | 272 | REG_SET_SEEN(b1); \ |
| 263 | REG_SET_SEEN(b2); \ | 273 | REG_SET_SEEN(b2); \ |
| 264 | }) | 274 | }) |
| 265 | 275 | ||
| 276 | static void emit6_pcrel_ril(struct bpf_jit *jit, u32 op, s64 pcrel) | ||
| 277 | { | ||
| 278 | u32 pc32dbl = (s32)(pcrel / 2); | ||
| 279 | |||
| 280 | _EMIT6(op | pc32dbl >> 16, pc32dbl & 0xffff); | ||
| 281 | } | ||
| 282 | |||
| 283 | static void emit6_pcrel_rilb(struct bpf_jit *jit, u32 op, u8 b, s64 pcrel) | ||
| 284 | { | ||
| 285 | emit6_pcrel_ril(jit, op | reg_high(b) << 16, pcrel); | ||
| 286 | REG_SET_SEEN(b); | ||
| 287 | } | ||
| 288 | |||
| 266 | #define EMIT6_PCREL_RILB(op, b, target) \ | 289 | #define EMIT6_PCREL_RILB(op, b, target) \ |
| 267 | ({ \ | 290 | emit6_pcrel_rilb(jit, op, b, off_to_pcrel(jit, target)) |
| 268 | unsigned int rel = (int)((target) - jit->prg) / 2; \ | ||
| 269 | _EMIT6((op) | reg_high(b) << 16 | rel >> 16, rel & 0xffff);\ | ||
| 270 | REG_SET_SEEN(b); \ | ||
| 271 | }) | ||
| 272 | 291 | ||
| 273 | #define EMIT6_PCREL_RIL(op, target) \ | 292 | #define EMIT6_PCREL_RILB_PTR(op, b, target_ptr) \ |
| 274 | ({ \ | 293 | emit6_pcrel_rilb(jit, op, b, ptr_to_pcrel(jit, target_ptr)) |
| 275 | unsigned int rel = (int)((target) - jit->prg) / 2; \ | 294 | |
| 276 | _EMIT6((op) | rel >> 16, rel & 0xffff); \ | 295 | static void emit6_pcrel_rilc(struct bpf_jit *jit, u32 op, u8 mask, s64 pcrel) |
| 277 | }) | 296 | { |
| 297 | emit6_pcrel_ril(jit, op | mask << 20, pcrel); | ||
| 298 | } | ||
| 278 | 299 | ||
| 279 | #define EMIT6_PCREL_RILC(op, mask, target) \ | 300 | #define EMIT6_PCREL_RILC(op, mask, target) \ |
| 280 | ({ \ | 301 | emit6_pcrel_rilc(jit, op, mask, off_to_pcrel(jit, target)) |
| 281 | EMIT6_PCREL_RIL((op) | (mask) << 20, (target)); \ | 302 | |
| 282 | }) | 303 | #define EMIT6_PCREL_RILC_PTR(op, mask, target_ptr) \ |
| 304 | emit6_pcrel_rilc(jit, op, mask, ptr_to_pcrel(jit, target_ptr)) | ||
| 283 | 305 | ||
| 284 | #define _EMIT6_IMM(op, imm) \ | 306 | #define _EMIT6_IMM(op, imm) \ |
| 285 | ({ \ | 307 | ({ \ |
| @@ -503,7 +525,7 @@ static void bpf_skip(struct bpf_jit *jit, int size) · 217 unmodified lines | |||
| 503 | { | 525 | { |
| 504 | if (size >= 6 && !is_valid_rel(size)) { | 526 | if (size >= 6 && !is_valid_rel(size)) { |
| 505 | /* brcl 0xf,size */ | 527 | /* brcl 0xf,size */ |
| 506 | EMIT6_PCREL_RIL(0xc0f4000000, size); | 528 | EMIT6_PCREL_RILC(0xc0040000, 0xf, size); |
| 507 | size -= 6; | 529 | size -= 6; |
| 508 | } else if (size >= 4 && is_valid_rel(size)) { | 530 | } else if (size >= 4 && is_valid_rel(size)) { |
| 509 | /* brc 0xf,size */ | 531 | /* brc 0xf,size */ |
| @@ -605,43 +627,30 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp, · 95 unmodified lines | |||
| 605 | } | 627 | } |
| 606 | /* Setup stack and backchain */ | 628 | /* Setup stack and backchain */ |
| 607 | if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) { | 629 | if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) { |
| 608 | if (is_first_pass(jit) || (jit->seen & SEEN_FUNC)) | 630 | /* lgr %w1,%r15 (backchain) */ |
| 609 | /* lgr %w1,%r15 (backchain) */ | 631 | EMIT4(0xb9040000, REG_W1, REG_15); |
| 610 | EMIT4(0xb9040000, REG_W1, REG_15); | ||
| 611 | /* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */ | 632 | /* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */ |
| 612 | EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED); | 633 | EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED); |
| 613 | /* aghi %r15,-STK_OFF */ | 634 | /* aghi %r15,-STK_OFF */ |
| 614 | EMIT4_IMM(0xa70b0000, REG_15, -(STK_OFF + stack_depth)); | 635 | EMIT4_IMM(0xa70b0000, REG_15, -(STK_OFF + stack_depth)); |
| 615 | if (is_first_pass(jit) || (jit->seen & SEEN_FUNC)) | 636 | /* stg %w1,152(%r15) (backchain) */ |
| 616 | /* stg %w1,152(%r15) (backchain) */ | 637 | EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0, |
| 617 | EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0, | 638 | REG_15, 152); |
| 618 | REG_15, 152); | ||
| 619 | } | 639 | } |
| 620 | } | 640 | } |
| 621 | 641 | ||
| 622 | /* | 642 | /* |
| 623 | * Emit an expoline for a jump that follows | 643 | * Jump using a register either directly or via an expoline thunk |
| 624 | */ | 644 | */ |
| 625 | static void emit_expoline(struct bpf_jit *jit) | 645 | #define EMIT_JUMP_REG(reg) do { \ |
| 626 | { | 646 | if (nospec_uses_trampoline()) \ |
| 627 | /* exrl %r0,.+10 */ | 647 | /* brcl 0xf,__s390_indirect_jump_rN */ \ |
| 628 | EMIT6_PCREL_RIL(0xc6000000, jit->prg + 10); | 648 | EMIT6_PCREL_RILC_PTR(0xc0040000, 0x0f, \ |
| 629 | /* j . */ | 649 | __s390_indirect_jump_r ## reg); \ |
| 630 | EMIT4_PCREL(0xa7f40000, 0); | 650 | else \ |
| 631 | } | 651 | /* br %rN */ \ |
| 632 | 652 | _EMIT2(0x07f0 | reg); \ | |
| 633 | /* | 653 | } while (0) |
| 634 | * Emit __s390_indirect_jump_r1 thunk if necessary | ||
| 635 | */ | ||
| 636 | static void emit_r1_thunk(struct bpf_jit *jit) | ||
| 637 | { | ||
| 638 | if (nospec_uses_trampoline()) { | ||
| 639 | jit->r1_thunk_ip = jit->prg; | ||
| 640 | emit_expoline(jit); | ||
| 641 | /* br %r1 */ | ||
| 642 | _EMIT2(0x07f1); | ||
| 643 | } | ||
| 644 | } | ||
| 645 | 654 | ||
| 646 | /* | 655 | /* |
| 647 | * Call r1 either directly or via __s390_indirect_jump_r1 thunk | 656 | * Call r1 either directly or via __s390_indirect_jump_r1 thunk |
| @@ -650,7 +659,8 @@ static void call_r1(struct bpf_jit *jit) · 2 unmodified lines | |||
| 650 | { | 659 | { |
| 651 | if (nospec_uses_trampoline()) | 660 | if (nospec_uses_trampoline()) |
| 652 | /* brasl %r14,__s390_indirect_jump_r1 */ | 661 | /* brasl %r14,__s390_indirect_jump_r1 */ |
| 653 | EMIT6_PCREL_RILB(0xc0050000, REG_14, jit->r1_thunk_ip); | 662 | EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14, |
| 663 | __s390_indirect_jump_r1); | ||
| 654 | else | 664 | else |
| 655 | /* basr %r14,%r1 */ | 665 | /* basr %r14,%r1 */ |
| 656 | EMIT2(0x0d00, REG_14, REG_1); | 666 | EMIT2(0x0d00, REG_14, REG_1); |
| @@ -666,16 +676,7 @@ static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth) · 9 unmodified lines | |||
| 666 | EMIT4(0xb9040000, REG_2, BPF_REG_0); | 676 | EMIT4(0xb9040000, REG_2, BPF_REG_0); |
| 667 | /* Restore registers */ | 677 | /* Restore registers */ |
| 668 | save_restore_regs(jit, REGS_RESTORE, stack_depth, 0); | 678 | save_restore_regs(jit, REGS_RESTORE, stack_depth, 0); |
| 669 | if (nospec_uses_trampoline()) { | 679 | EMIT_JUMP_REG(14); |
| 670 | jit->r14_thunk_ip = jit->prg; | ||
| 671 | /* Generate __s390_indirect_jump_r14 thunk */ | ||
| 672 | emit_expoline(jit); | ||
| 673 | } | ||
| 674 | /* br %r14 */ | ||
| 675 | _EMIT2(0x07fe); | ||
| 676 | |||
| 677 | if (is_first_pass(jit) || (jit->seen & SEEN_FUNC)) | ||
| 678 | emit_r1_thunk(jit); | ||
| 679 | 680 | ||
| 680 | jit->prg = ALIGN(jit->prg, 8); | 681 | jit->prg = ALIGN(jit->prg, 8); |
| 681 | jit->prologue_plt = jit->prg; | 682 | jit->prologue_plt = jit->prg; |
| @@ -1877,7 +1878,8 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, · 1195 unmodified lines | |||
| 1877 | /* aghi %r1,tail_call_start */ | 1878 | /* aghi %r1,tail_call_start */ |
| 1878 | EMIT4_IMM(0xa70b0000, REG_1, jit->tail_call_start); | 1879 | EMIT4_IMM(0xa70b0000, REG_1, jit->tail_call_start); |
| 1879 | /* brcl 0xf,__s390_indirect_jump_r1 */ | 1880 | /* brcl 0xf,__s390_indirect_jump_r1 */ |
| 1880 | EMIT6_PCREL_RILC(0xc0040000, 0xf, jit->r1_thunk_ip); | 1881 | EMIT6_PCREL_RILC_PTR(0xc0040000, 0xf, |
| 1882 | __s390_indirect_jump_r1); | ||
| 1881 | } else { | 1883 | } else { |
| 1882 | /* bc 0xf,tail_call_start(%r1) */ | 1884 | /* bc 0xf,tail_call_start(%r1) */ |
| 1883 | _EMIT4(0x47f01000 + jit->tail_call_start); | 1885 | _EMIT4(0x47f01000 + jit->tail_call_start); |
| @@ -2585,9 +2587,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, · 701 unmodified lines | |||
| 2585 | if (nr_stack_args > MAX_NR_STACK_ARGS) | 2587 | if (nr_stack_args > MAX_NR_STACK_ARGS) |
| 2586 | return -ENOTSUPP; | 2588 | return -ENOTSUPP; |
| 2587 | 2589 | ||
| 2588 | /* Return to %r14, since func_addr and %r0 are not available. */ | 2590 | /* Return to %r14 in the struct_ops case. */ |
| 2589 | if ((!func_addr && !(flags & BPF_TRAMP_F_ORIG_STACK)) || | 2591 | if (flags & BPF_TRAMP_F_INDIRECT) |
| 2590 | (flags & BPF_TRAMP_F_INDIRECT)) | ||
| 2591 | flags |= BPF_TRAMP_F_SKIP_FRAME; | 2592 | flags |= BPF_TRAMP_F_SKIP_FRAME; |
| 2592 | 2593 | ||
| 2593 | /* | 2594 | /* |
| @@ -2847,17 +2848,10 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, · 253 unmodified lines | |||
| 2847 | 0xf000 | tjit->tccnt_off); | 2848 | 0xf000 | tjit->tccnt_off); |
| 2848 | /* aghi %r15,stack_size */ | 2849 | /* aghi %r15,stack_size */ |
| 2849 | EMIT4_IMM(0xa70b0000, REG_15, tjit->stack_size); | 2850 | EMIT4_IMM(0xa70b0000, REG_15, tjit->stack_size); |
| 2850 | /* Emit an expoline for the following indirect jump. */ | ||
| 2851 | if (nospec_uses_trampoline()) | ||
| 2852 | emit_expoline(jit); | ||
| 2853 | if (flags & BPF_TRAMP_F_SKIP_FRAME) | 2851 | if (flags & BPF_TRAMP_F_SKIP_FRAME) |
| 2854 | /* br %r14 */ | 2852 | EMIT_JUMP_REG(14); |
| 2855 | _EMIT2(0x07fe); | ||
| 2856 | else | 2853 | else |
| 2857 | /* br %r1 */ | 2854 | EMIT_JUMP_REG(1); |
| 2858 | _EMIT2(0x07f1); | ||
| 2859 | |||
| 2860 | emit_r1_thunk(jit); | ||
| 2861 | 2855 | ||
| 2862 | return 0; | 2856 | return 0; |
| 2863 | } | 2857 | } |
| @@ -19,7 +19,9 @@ · 18 unmodified lines | |||
| 19 | #include <linux/anon_inodes.h> | 19 | #include <linux/anon_inodes.h> |
| 20 | #include <linux/export.h> | 20 | #include <linux/export.h> |
| 21 | #include <linux/debugfs.h> | 21 | #include <linux/debugfs.h> |
| 22 | #include <linux/list.h> | ||
| 22 | #include <linux/module.h> | 23 | #include <linux/module.h> |
| 24 | #include <linux/mutex.h> | ||
| 23 | #include <linux/seq_file.h> | 25 | #include <linux/seq_file.h> |
| 24 | #include <linux/sync_file.h> | 26 | #include <linux/sync_file.h> |
| 25 | #include <linux/poll.h> | 27 | #include <linux/poll.h> |
| @@ -35,35 +37,91 @@ · 9 unmodified lines | |||
| 35 | 37 | ||
| 36 | static inline int is_dma_buf_file(struct file *); | 38 | static inline int is_dma_buf_file(struct file *); |
| 37 | 39 | ||
| 38 | #if IS_ENABLED(CONFIG_DEBUG_FS) | 40 | static DEFINE_MUTEX(dmabuf_list_mutex); |
| 39 | static DEFINE_MUTEX(debugfs_list_mutex); | 41 | static LIST_HEAD(dmabuf_list); |
| 40 | static LIST_HEAD(debugfs_list); | ||
| 41 | 42 | ||
| 42 | static void __dma_buf_debugfs_list_add(struct dma_buf *dmabuf) | 43 | static void __dma_buf_list_add(struct dma_buf *dmabuf) |
| 43 | { | 44 | { |
| 44 | mutex_lock(&debugfs_list_mutex); | 45 | mutex_lock(&dmabuf_list_mutex); |
| 45 | list_add(&dmabuf->list_node, &debugfs_list); | 46 | list_add(&dmabuf->list_node, &dmabuf_list); |
| 46 | mutex_unlock(&debugfs_list_mutex); | 47 | mutex_unlock(&dmabuf_list_mutex); |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | static void __dma_buf_debugfs_list_del(struct dma_buf *dmabuf) | 50 | static void __dma_buf_list_del(struct dma_buf *dmabuf) |
| 50 | { | 51 | { |
| 51 | if (!dmabuf) | 52 | if (!dmabuf) |
| 52 | return; | 53 | return; |
| 53 | 54 | ||
| 54 | mutex_lock(&debugfs_list_mutex); | 55 | mutex_lock(&dmabuf_list_mutex); |
| 55 | list_del(&dmabuf->list_node); | 56 | list_del(&dmabuf->list_node); |
| 56 | mutex_unlock(&debugfs_list_mutex); | 57 | mutex_unlock(&dmabuf_list_mutex); |
| 57 | } | 58 | } |
| 58 | #else | 59 | |
| 59 | static void __dma_buf_debugfs_list_add(struct dma_buf *dmabuf) | 60 | /** |
| 61 | * dma_buf_iter_begin - begin iteration through global list of all DMA buffers | ||
| 62 | * | ||
| 63 | * Returns the first buffer in the global list of DMA-bufs that's not in the | ||
| 64 | * process of being destroyed. Increments that buffer's reference count to | ||
| 65 | * prevent buffer destruction. Callers must release the reference, either by | ||
| 66 | * continuing iteration with dma_buf_iter_next(), or with dma_buf_put(). | ||
| 67 | * | ||
| 68 | * Return: | ||
| 69 | * * First buffer from global list, with refcount elevated | ||
| 70 | * * NULL if no active buffers are present | ||
| 71 | */ | ||
| 72 | struct dma_buf *dma_buf_iter_begin(void) | ||
| 60 | { | 73 | { |
| 74 | struct dma_buf *ret = NULL, *dmabuf; | ||
| 75 | |||
| 76 | /* | ||
| 77 | * The list mutex does not protect a dmabuf's refcount, so it can be | ||
| 78 | * zeroed while we are iterating. We cannot call get_dma_buf() since the | ||
| 79 | * caller may not already own a reference to the buffer. | ||
| 80 | */ | ||
| 81 | mutex_lock(&dmabuf_list_mutex); | ||
| 82 | list_for_each_entry(dmabuf, &dmabuf_list, list_node) { | ||
| 83 | if (file_ref_get(&dmabuf->file->f_ref)) { | ||
| 84 | ret = dmabuf; | ||
| 85 | break; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | mutex_unlock(&dmabuf_list_mutex); | ||
| 89 | return ret; | ||
| 61 | } | 90 | } |
| 62 | 91 | ||
| 63 | static void __dma_buf_debugfs_list_del(struct dma_buf *dmabuf) | 92 | /** |
| 93 | * dma_buf_iter_next - continue iteration through global list of all DMA buffers | ||
| 94 | * @dmabuf: [in] pointer to dma_buf | ||
| 95 | * | ||
| 96 | * Decrements the reference count on the provided buffer. Returns the next | ||
| 97 | * buffer from the remainder of the global list of DMA-bufs with its reference | ||
| 98 | * count incremented. Callers must release the reference, either by continuing | ||
| 99 | * iteration with dma_buf_iter_next(), or with dma_buf_put(). | ||
| 100 | * | ||
| 101 | * Return: | ||
| 102 | * * Next buffer from global list, with refcount elevated | ||
| 103 | * * NULL if no additional active buffers are present | ||
| 104 | */ | ||
| 105 | struct dma_buf *dma_buf_iter_next(struct dma_buf *dmabuf) | ||
| 64 | { | 106 | { |
| 107 | struct dma_buf *ret = NULL; | ||
| 108 | |||
| 109 | /* | ||
| 110 | * The list mutex does not protect a dmabuf's refcount, so it can be | ||
| 111 | * zeroed while we are iterating. We cannot call get_dma_buf() since the | ||
| 112 | * caller may not already own a reference to the buffer. | ||
| 113 | */ | ||
| 114 | mutex_lock(&dmabuf_list_mutex); | ||
| 115 | dma_buf_put(dmabuf); | ||
| 116 | list_for_each_entry_continue(dmabuf, &dmabuf_list, list_node) { | ||
| 117 | if (file_ref_get(&dmabuf->file->f_ref)) { | ||
| 118 | ret = dmabuf; | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | mutex_unlock(&dmabuf_list_mutex); | ||
| 123 | return ret; | ||
| 65 | } | 124 | } |
| 66 | #endif | ||
| 67 | 125 | ||
| 68 | static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen) | 126 | static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen) |
| 69 | { | 127 | { |
| @@ -115,7 +173,7 @@ static int dma_buf_file_release(struct inode *inode, struct file *file) · 45 unmodified lines | |||
| 115 | if (!is_dma_buf_file(file)) | 173 | if (!is_dma_buf_file(file)) |
| 116 | return -EINVAL; | 174 | return -EINVAL; |
| 117 | 175 | ||
| 118 | __dma_buf_debugfs_list_del(file->private_data); | 176 | __dma_buf_list_del(file->private_data); |
| 119 | 177 | ||
| 120 | return 0; | 178 | return 0; |
| 121 | } | 179 | } |
| @@ -685,7 +743,7 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) · 563 unmodified lines | |||
| 685 | file->f_path.dentry->d_fsdata = dmabuf; | 743 | file->f_path.dentry->d_fsdata = dmabuf; |
| 686 | dmabuf->file = file; | 744 | dmabuf->file = file; |
| 687 | 745 | ||
| 688 | __dma_buf_debugfs_list_add(dmabuf); | 746 | __dma_buf_list_add(dmabuf); |
| 689 | 747 | ||
| 690 | return dmabuf; | 748 | return dmabuf; |
| 691 | 749 | ||
| @@ -1563,7 +1621,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) · 871 unmodified lines | |||
| 1563 | size_t size = 0; | 1621 | size_t size = 0; |
| 1564 | int ret; | 1622 | int ret; |
| 1565 | 1623 | ||
| 1566 | ret = mutex_lock_interruptible(&debugfs_list_mutex); | 1624 | ret = mutex_lock_interruptible(&dmabuf_list_mutex); |
| 1567 | 1625 | ||
| 1568 | if (ret) | 1626 | if (ret) |
| 1569 | return ret; | 1627 | return ret; |
| @@ -1572,7 +1630,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) · 2 unmodified lines | |||
| 1572 | seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\tname\n", | 1630 | seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\tname\n", |
| 1573 | "size", "flags", "mode", "count", "ino"); | 1631 | "size", "flags", "mode", "count", "ino"); |
| 1574 | 1632 | ||
| 1575 | list_for_each_entry(buf_obj, &debugfs_list, list_node) { | 1633 | list_for_each_entry(buf_obj, &dmabuf_list, list_node) { |
| 1576 | 1634 | ||
| 1577 | ret = dma_resv_lock_interruptible(buf_obj->resv, NULL); | 1635 | ret = dma_resv_lock_interruptible(buf_obj->resv, NULL); |
| 1578 | if (ret) | 1636 | if (ret) |
| @@ -1609,11 +1667,11 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) · 30 unmodified lines | |||
| 1609 | 1667 | ||
| 1610 | seq_printf(s, "\nTotal %d objects, %zu bytes\n", count, size); | 1668 | seq_printf(s, "\nTotal %d objects, %zu bytes\n", count, size); |
| 1611 | 1669 | ||
| 1612 | mutex_unlock(&debugfs_list_mutex); | 1670 | mutex_unlock(&dmabuf_list_mutex); |
| 1613 | return 0; | 1671 | return 0; |
| 1614 | 1672 | ||
| 1615 | error_unlock: | 1673 | error_unlock: |
| 1616 | mutex_unlock(&debugfs_list_mutex); | 1674 | mutex_unlock(&dmabuf_list_mutex); |
| 1617 | return ret; | 1675 | return ret; |
| 1618 | } | 1676 | } |
| 1619 | 1677 | ||
| @@ -667,10 +667,11 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG) · 666 unmodified lines | |||
| 667 | */ | 667 | */ |
| 668 | #ifdef CONFIG_DEBUG_INFO_BTF | 668 | #ifdef CONFIG_DEBUG_INFO_BTF |
| 669 | #define BTF \ | 669 | #define BTF \ |
| 670 | . = ALIGN(PAGE_SIZE); \ | ||
| 670 | .BTF : AT(ADDR(.BTF) - LOAD_OFFSET) { \ | 671 | .BTF : AT(ADDR(.BTF) - LOAD_OFFSET) { \ |
| 671 | BOUNDED_SECTION_BY(.BTF, _BTF) \ | 672 | BOUNDED_SECTION_BY(.BTF, _BTF) \ |
| 672 | } \ | 673 | } \ |
| 673 | . = ALIGN(4); \ | 674 | . = ALIGN(PAGE_SIZE); \ |
| 674 | .BTF_ids : AT(ADDR(.BTF_ids) - LOAD_OFFSET) { \ | 675 | .BTF_ids : AT(ADDR(.BTF_ids) - LOAD_OFFSET) { \ |
| 675 | *(.BTF_ids) \ | 676 | *(.BTF_ids) \ |
| 676 | } | 677 | } |
| @@ -426,8 +426,6 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr, · 425 unmodified lines | |||
| 426 | 426 | ||
| 427 | const struct bpf_func_proto * | 427 | const struct bpf_func_proto * |
| 428 | cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog); | 428 | cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog); |
| 429 | const struct bpf_func_proto * | ||
| 430 | cgroup_current_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog); | ||
| 431 | #else | 429 | #else |
| 432 | 430 | ||
| 433 | static inline void cgroup_bpf_lifetime_notifier_init(void) | 431 | static inline void cgroup_bpf_lifetime_notifier_init(void) |
| @@ -466,12 +464,6 @@ cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 32 unmodified lines | |||
| 466 | return NULL; | 464 | return NULL; |
| 467 | } | 465 | } |
| 468 | 466 | ||
| 469 | static inline const struct bpf_func_proto * | ||
| 470 | cgroup_current_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) | ||
| 471 | { | ||
| 472 | return NULL; | ||
| 473 | } | ||
| 474 | |||
| 475 | static inline int bpf_cgroup_storage_assign(struct bpf_prog_aux *aux, | 467 | static inline int bpf_cgroup_storage_assign(struct bpf_prog_aux *aux, |
| 476 | struct bpf_map *map) { return 0; } | 468 | struct bpf_map *map) { return 0; } |
| 477 | static inline struct bpf_cgroup_storage *bpf_cgroup_storage_alloc( | 469 | static inline struct bpf_cgroup_storage *bpf_cgroup_storage_alloc( |
| @@ -346,6 +346,12 @@ static inline const char *btf_field_type_name(enum btf_field_type type) · 345 unmodified lines | |||
| 346 | } | 346 | } |
| 347 | } | 347 | } |
| 348 | 348 | ||
| 349 | #if IS_ENABLED(CONFIG_DEBUG_KERNEL) | ||
| 350 | #define BPF_WARN_ONCE(cond, format...) WARN_ONCE(cond, format) | ||
| 351 | #else | ||
| 352 | #define BPF_WARN_ONCE(cond, format...) BUILD_BUG_ON_INVALID(cond) | ||
| 353 | #endif | ||
| 354 | |||
| 349 | static inline u32 btf_field_type_size(enum btf_field_type type) | 355 | static inline u32 btf_field_type_size(enum btf_field_type type) |
| 350 | { | 356 | { |
| 351 | switch (type) { | 357 | switch (type) { |
| @@ -1349,6 +1355,20 @@ u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr); · 997 unmodified lines | |||
| 1349 | const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u32 len); | 1355 | const void *__bpf_dynptr_data(const struct bpf_dynptr_kern *ptr, u32 len); |
| 1350 | void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len); | 1356 | void *__bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len); |
| 1351 | bool __bpf_dynptr_is_rdonly(const struct bpf_dynptr_kern *ptr); | 1357 | bool __bpf_dynptr_is_rdonly(const struct bpf_dynptr_kern *ptr); |
| 1358 | int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u32 offset, | ||
| 1359 | void *src, u32 len, u64 flags); | ||
| 1360 | void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr *p, u32 offset, | ||
| 1361 | void *buffer__opt, u32 buffer__szk); | ||
| 1362 | |||
| 1363 | static inline int bpf_dynptr_check_off_len(const struct bpf_dynptr_kern *ptr, u32 offset, u32 len) | ||
| 1364 | { | ||
| 1365 | u32 size = __bpf_dynptr_size(ptr); | ||
| 1366 | |||
| 1367 | if (len > size || offset > size - len) | ||
| 1368 | return -E2BIG; | ||
| 1369 | |||
| 1370 | return 0; | ||
| 1371 | } | ||
| 1352 | 1372 | ||
| 1353 | #ifdef CONFIG_BPF_JIT | 1373 | #ifdef CONFIG_BPF_JIT |
| 1354 | int bpf_trampoline_link_prog(struct bpf_tramp_link *link, | 1374 | int bpf_trampoline_link_prog(struct bpf_tramp_link *link, |
| @@ -356,7 +356,11 @@ enum { · 355 unmodified lines | |||
| 356 | INSN_F_SPI_MASK = 0x3f, /* 6 bits */ | 356 | INSN_F_SPI_MASK = 0x3f, /* 6 bits */ |
| 357 | INSN_F_SPI_SHIFT = 3, /* shifted 3 bits to the left */ | 357 | INSN_F_SPI_SHIFT = 3, /* shifted 3 bits to the left */ |
| 358 | 358 | ||
| 359 | INSN_F_STACK_ACCESS = BIT(9), /* we need 10 bits total */ | 359 | INSN_F_STACK_ACCESS = BIT(9), |
| 360 | |||
| 361 | INSN_F_DST_REG_STACK = BIT(10), /* dst_reg is PTR_TO_STACK */ | ||
| 362 | INSN_F_SRC_REG_STACK = BIT(11), /* src_reg is PTR_TO_STACK */ | ||
| 363 | /* total 12 bits are used now. */ | ||
| 360 | }; | 364 | }; |
| 361 | 365 | ||
| 362 | static_assert(INSN_F_FRAMENO_MASK + 1 >= MAX_CALL_FRAMES); | 366 | static_assert(INSN_F_FRAMENO_MASK + 1 >= MAX_CALL_FRAMES); |
| @@ -365,9 +369,9 @@ static_assert(INSN_F_SPI_MASK + 1 >= MAX_BPF_STACK / 8); · 2 unmodified lines | |||
| 365 | struct bpf_insn_hist_entry { | 369 | struct bpf_insn_hist_entry { |
| 366 | u32 idx; | 370 | u32 idx; |
| 367 | /* insn idx can't be bigger than 1 million */ | 371 | /* insn idx can't be bigger than 1 million */ |
| 368 | u32 prev_idx : 22; | 372 | u32 prev_idx : 20; |
| 369 | /* special flags, e.g., whether insn is doing register stack spill/load */ | 373 | /* special INSN_F_xxx flags */ |
| 370 | u32 flags : 10; | 374 | u32 flags : 12; |
| 371 | /* additional registers that need precision tracking when this | 375 | /* additional registers that need precision tracking when this |
| 372 | * jump is backtracked, vector of six 10-bit records | 376 | * jump is backtracked, vector of six 10-bit records |
| 373 | */ | 377 | */ |
| @@ -591,6 +595,7 @@ struct bpf_insn_aux_data { · 217 unmodified lines | |||
| 591 | * bpf_fastcall pattern. | 595 | * bpf_fastcall pattern. |
| 592 | */ | 596 | */ |
| 593 | u8 fastcall_spills_num:3; | 597 | u8 fastcall_spills_num:3; |
| 598 | u8 arg_prog:4; | ||
| 594 | 599 | ||
| 595 | /* below fields are initialized once */ | 600 | /* below fields are initialized once */ |
| 596 | unsigned int orig_idx; /* original instruction index */ | 601 | unsigned int orig_idx; /* original instruction index */ |
| @@ -838,6 +843,17 @@ __printf(3, 4) void verbose_linfo(struct bpf_verifier_env *env, · 241 unmodified lines | |||
| 838 | u32 insn_off, | 843 | u32 insn_off, |
| 839 | const char *prefix_fmt, ...); | 844 | const char *prefix_fmt, ...); |
| 840 | 845 | ||
| 846 | #define verifier_bug_if(cond, env, fmt, args...) \ | ||
| 847 | ({ \ | ||
| 848 | bool __cond = (cond); \ | ||
| 849 | if (unlikely(__cond)) { \ | ||
| 850 | BPF_WARN_ONCE(1, "verifier bug: " fmt "(" #cond ")\n", ##args); \ | ||
| 851 | bpf_log(&env->log, "verifier bug: " fmt "(" #cond ")\n", ##args); \ | ||
| 852 | } \ | ||
| 853 | (__cond); \ | ||
| 854 | }) | ||
| 855 | #define verifier_bug(env, fmt, args...) verifier_bug_if(1, env, fmt, ##args) | ||
| 856 | |||
| 841 | static inline struct bpf_func_state *cur_func(struct bpf_verifier_env *env) | 857 | static inline struct bpf_func_state *cur_func(struct bpf_verifier_env *env) |
| 842 | { | 858 | { |
| 843 | struct bpf_verifier_state *cur = env->cur_state; | 859 | struct bpf_verifier_state *cur = env->cur_state; |
| @@ -361,10 +361,8 @@ struct dma_buf { · 360 unmodified lines | |||
| 361 | */ | 361 | */ |
| 362 | struct module *owner; | 362 | struct module *owner; |
| 363 | 363 | ||
| 364 | #if IS_ENABLED(CONFIG_DEBUG_FS) | ||
| 365 | /** @list_node: node for dma_buf accounting and debugging. */ | 364 | /** @list_node: node for dma_buf accounting and debugging. */ |
| 366 | struct list_head list_node; | 365 | struct list_head list_node; |
| 367 | #endif | ||
| 368 | 366 | ||
| 369 | /** @priv: exporter specific private data for this buffer object. */ | 367 | /** @priv: exporter specific private data for this buffer object. */ |
| 370 | void *priv; | 368 | void *priv; |
| @@ -609,4 +607,6 @@ int dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map); · 238 unmodified lines | |||
| 609 | void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map); | 607 | void dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map); |
| 610 | int dma_buf_vmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map); | 608 | int dma_buf_vmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map); |
| 611 | void dma_buf_vunmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map); | 609 | void dma_buf_vunmap_unlocked(struct dma_buf *dmabuf, struct iosys_map *map); |
| 610 | struct dma_buf *dma_buf_iter_begin(void); | ||
| 611 | struct dma_buf *dma_buf_iter_next(struct dma_buf *dmbuf); | ||
| 612 | #endif /* __DMA_BUF_H__ */ | 612 | #endif /* __DMA_BUF_H__ */ |
| @@ -1506,7 +1506,7 @@ union bpf_attr { · 1505 unmodified lines | |||
| 1506 | __s32 map_token_fd; | 1506 | __s32 map_token_fd; |
| 1507 | }; | 1507 | }; |
| 1508 | 1508 | ||
| 1509 | struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ | 1509 | struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */ |
| 1510 | __u32 map_fd; | 1510 | __u32 map_fd; |
| 1511 | __aligned_u64 key; | 1511 | __aligned_u64 key; |
| 1512 | union { | 1512 | union { |
| @@ -1995,11 +1995,15 @@ union bpf_attr { · 482 unmodified lines | |||
| 1995 | * long bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags) | 1995 | * long bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags) |
| 1996 | * Description | 1996 | * Description |
| 1997 | * Store *len* bytes from address *from* into the packet | 1997 | * Store *len* bytes from address *from* into the packet |
| 1998 | * associated to *skb*, at *offset*. *flags* are a combination of | 1998 | * associated to *skb*, at *offset*. The *flags* are a combination |
| 1999 | * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the | 1999 | * of the following values: |
| 2000 | * checksum for the packet after storing the bytes) and | 2000 | * |
| 2001 | * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ | 2001 | * **BPF_F_RECOMPUTE_CSUM** |
| 2002 | * **->swhash** and *skb*\ **->l4hash** to 0). | 2002 | * Automatically update *skb*\ **->csum** after storing the |
| 2003 | * bytes. | ||
| 2004 | * **BPF_F_INVALIDATE_HASH** | ||
| 2005 | * Set *skb*\ **->hash**, *skb*\ **->swhash** and *skb*\ | ||
| 2006 | * **->l4hash** to 0. | ||
| 2003 | * | 2007 | * |
| 2004 | * A call to this helper is susceptible to change the underlying | 2008 | * A call to this helper is susceptible to change the underlying |
| 2005 | * packet buffer. Therefore, at load time, all checks on pointers | 2009 | * packet buffer. Therefore, at load time, all checks on pointers |
| @@ -2051,7 +2055,7 @@ union bpf_attr { · 45 unmodified lines | |||
| 2051 | * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and | 2055 | * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and |
| 2052 | * for updates resulting in a null checksum the value is set to | 2056 | * for updates resulting in a null checksum the value is set to |
| 2053 | * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates | 2057 | * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates |
| 2054 | * the checksum is to be computed against a pseudo-header. | 2058 | * that the modified header field is part of the pseudo-header. |
| 2055 | * | 2059 | * |
| 2056 | * This helper works in combination with **bpf_csum_diff**\ (), | 2060 | * This helper works in combination with **bpf_csum_diff**\ (), |
| 2057 | * which does not update the checksum in-place, but offers more | 2061 | * which does not update the checksum in-place, but offers more |
| @@ -6723,6 +6727,7 @@ struct bpf_link_info { · 4665 unmodified lines | |||
| 6723 | __u32 name_len; | 6727 | __u32 name_len; |
| 6724 | __u32 offset; /* offset from file_name */ | 6728 | __u32 offset; /* offset from file_name */ |
| 6725 | __u64 cookie; | 6729 | __u64 cookie; |
| 6730 | __u64 ref_ctr_offset; | ||
| 6726 | } uprobe; /* BPF_PERF_EVENT_UPROBE, BPF_PERF_EVENT_URETPROBE */ | 6731 | } uprobe; /* BPF_PERF_EVENT_UPROBE, BPF_PERF_EVENT_URETPROBE */ |
| 6727 | struct { | 6732 | struct { |
| 6728 | __aligned_u64 func_name; /* in/out */ | 6733 | __aligned_u64 func_name; /* in/out */ |
| @@ -53,6 +53,9 @@ obj-$(CONFIG_BPF_SYSCALL) += relo_core.o · 52 unmodified lines | |||
| 53 | obj-$(CONFIG_BPF_SYSCALL) += btf_iter.o | 53 | obj-$(CONFIG_BPF_SYSCALL) += btf_iter.o |
| 54 | obj-$(CONFIG_BPF_SYSCALL) += btf_relocate.o | 54 | obj-$(CONFIG_BPF_SYSCALL) += btf_relocate.o |
| 55 | obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o | 55 | obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o |
| 56 | ifeq ($(CONFIG_DMA_SHARED_BUFFER),y) | ||
| 57 | obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o | ||
| 58 | endif | ||
| 56 | 59 | ||
| 57 | CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE) | 60 | CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE) |
| 58 | CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE) | 61 | CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE) |
| @@ -601,7 +601,7 @@ int bpf_struct_ops_prepare_trampoline(struct bpf_tramp_links *tlinks, · 600 unmodified lines | |||
| 601 | if (model->ret_size > 0) | 601 | if (model->ret_size > 0) |
| 602 | flags |= BPF_TRAMP_F_RET_FENTRY_RET; | 602 | flags |= BPF_TRAMP_F_RET_FENTRY_RET; |
| 603 | 603 | ||
| 604 | size = arch_bpf_trampoline_size(model, flags, tlinks, NULL); | 604 | size = arch_bpf_trampoline_size(model, flags, tlinks, stub_func); |
| 605 | if (size <= 0) | 605 | if (size <= 0) |
| 606 | return size ? : -EFAULT; | 606 | return size ? : -EFAULT; |
| 607 | 607 | ||
| @@ -26,6 +26,7 @@ · 25 unmodified lines | |||
| 26 | #include <linux/bsearch.h> | 26 | #include <linux/bsearch.h> |
| 27 | #include <linux/kobject.h> | 27 | #include <linux/kobject.h> |
| 28 | #include <linux/sysfs.h> | 28 | #include <linux/sysfs.h> |
| 29 | #include <linux/overflow.h> | ||
| 29 | 30 | ||
| 30 | #include <net/netfilter/nf_bpf_link.h> | 31 | #include <net/netfilter/nf_bpf_link.h> |
| 31 | 32 | ||
| @@ -3957,7 +3958,7 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type · 3925 unmodified lines | |||
| 3957 | /* This needs to be kzalloc to zero out padding and unused fields, see | 3958 | /* This needs to be kzalloc to zero out padding and unused fields, see |
| 3958 | * comment in btf_record_equal. | 3959 | * comment in btf_record_equal. |
| 3959 | */ | 3960 | */ |
| 3960 | rec = kzalloc(offsetof(struct btf_record, fields[cnt]), GFP_KERNEL | __GFP_NOWARN); | 3961 | rec = kzalloc(struct_size(rec, fields, cnt), GFP_KERNEL | __GFP_NOWARN); |
| 3961 | if (!rec) | 3962 | if (!rec) |
| 3962 | return ERR_PTR(-ENOMEM); | 3963 | return ERR_PTR(-ENOMEM); |
| 3963 | 3964 | ||
| @@ -5583,7 +5584,7 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf) · 1619 unmodified lines | |||
| 5583 | if (id < 0) | 5584 | if (id < 0) |
| 5584 | continue; | 5585 | continue; |
| 5585 | 5586 | ||
| 5586 | new_aof = krealloc(aof, offsetof(struct btf_id_set, ids[aof->cnt + 1]), | 5587 | new_aof = krealloc(aof, struct_size(new_aof, ids, aof->cnt + 1), |
| 5587 | GFP_KERNEL | __GFP_NOWARN); | 5588 | GFP_KERNEL | __GFP_NOWARN); |
| 5588 | if (!new_aof) { | 5589 | if (!new_aof) { |
| 5589 | ret = -ENOMEM; | 5590 | ret = -ENOMEM; |
| @@ -5610,7 +5611,7 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf) · 20 unmodified lines | |||
| 5610 | if (ret != BTF_FIELD_FOUND) | 5611 | if (ret != BTF_FIELD_FOUND) |
| 5611 | continue; | 5612 | continue; |
| 5612 | 5613 | ||
| 5613 | new_aof = krealloc(aof, offsetof(struct btf_id_set, ids[aof->cnt + 1]), | 5614 | new_aof = krealloc(aof, struct_size(new_aof, ids, aof->cnt + 1), |
| 5614 | GFP_KERNEL | __GFP_NOWARN); | 5615 | GFP_KERNEL | __GFP_NOWARN); |
| 5615 | if (!new_aof) { | 5616 | if (!new_aof) { |
| 5616 | ret = -ENOMEM; | 5617 | ret = -ENOMEM; |
| @@ -5647,7 +5648,7 @@ btf_parse_struct_metas(struct bpf_verifier_log *log, struct btf *btf) · 30 unmodified lines | |||
| 5647 | continue; | 5648 | continue; |
| 5648 | parse: | 5649 | parse: |
| 5649 | tab_cnt = tab ? tab->cnt : 0; | 5650 | tab_cnt = tab ? tab->cnt : 0; |
| 5650 | new_tab = krealloc(tab, offsetof(struct btf_struct_metas, types[tab_cnt + 1]), | 5651 | new_tab = krealloc(tab, struct_size(new_tab, types, tab_cnt + 1), |
| 5651 | GFP_KERNEL | __GFP_NOWARN); | 5652 | GFP_KERNEL | __GFP_NOWARN); |
| 5652 | if (!new_tab) { | 5653 | if (!new_tab) { |
| 5653 | ret = -ENOMEM; | 5654 | ret = -ENOMEM; |
| @@ -6383,12 +6384,11 @@ struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog) · 729 unmodified lines | |||
| 6383 | return prog->aux->attach_btf; | 6384 | return prog->aux->attach_btf; |
| 6384 | } | 6385 | } |
| 6385 | 6386 | ||
| 6386 | static bool is_int_ptr(struct btf *btf, const struct btf_type *t) | 6387 | static bool is_void_or_int_ptr(struct btf *btf, const struct btf_type *t) |
| 6387 | { | 6388 | { |
| 6388 | /* skip modifiers */ | 6389 | /* skip modifiers */ |
| 6389 | t = btf_type_skip_modifiers(btf, t->type, NULL); | 6390 | t = btf_type_skip_modifiers(btf, t->type, NULL); |
| 6390 | 6391 | return btf_type_is_void(t) || btf_type_is_int(t); | |
| 6391 | return btf_type_is_int(t); | ||
| 6392 | } | 6392 | } |
| 6393 | 6393 | ||
| 6394 | u32 btf_ctx_arg_idx(struct btf *btf, const struct btf_type *func_proto, | 6394 | u32 btf_ctx_arg_idx(struct btf *btf, const struct btf_type *func_proto, |
| @@ -6777,14 +6777,11 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, · 382 unmodified lines | |||
| 6777 | } | 6777 | } |
| 6778 | } | 6778 | } |
| 6779 | 6779 | ||
| 6780 | if (t->type == 0) | 6780 | /* |
| 6781 | /* This is a pointer to void. | 6781 | * If it's a pointer to void, it's the same as scalar from the verifier |
| 6782 | * It is the same as scalar from the verifier safety pov. | 6782 | * safety POV. Either way, no futher pointer walking is allowed. |
| 6783 | * No further pointer walking is allowed. | 6783 | */ |
| 6784 | */ | 6784 | if (is_void_or_int_ptr(btf, t)) |
| 6785 | return true; | ||
| 6786 | |||
| 6787 | if (is_int_ptr(btf, t)) | ||
| 6788 | return true; | 6785 | return true; |
| 6789 | 6786 | ||
| 6790 | /* this is a pointer to another type */ | 6787 | /* this is a pointer to another type */ |
| @@ -6830,10 +6827,10 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, · 39 unmodified lines | |||
| 6830 | /* Is this a func with potential NULL args? */ | 6827 | /* Is this a func with potential NULL args? */ |
| 6831 | if (strcmp(tname, raw_tp_null_args[i].func)) | 6828 | if (strcmp(tname, raw_tp_null_args[i].func)) |
| 6832 | continue; | 6829 | continue; |
| 6833 | if (raw_tp_null_args[i].mask & (0x1 << (arg * 4))) | 6830 | if (raw_tp_null_args[i].mask & (0x1ULL << (arg * 4))) |
| 6834 | info->reg_type |= PTR_MAYBE_NULL; | 6831 | info->reg_type |= PTR_MAYBE_NULL; |
| 6835 | /* Is the current arg IS_ERR? */ | 6832 | /* Is the current arg IS_ERR? */ |
| 6836 | if (raw_tp_null_args[i].mask & (0x2 << (arg * 4))) | 6833 | if (raw_tp_null_args[i].mask & (0x2ULL << (arg * 4))) |
| 6837 | ptr_err_raw_tp = true; | 6834 | ptr_err_raw_tp = true; |
| 6838 | break; | 6835 | break; |
| 6839 | } | 6836 | } |
| @@ -7663,7 +7660,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog) · 823 unmodified lines | |||
| 7663 | return 0; | 7660 | return 0; |
| 7664 | 7661 | ||
| 7665 | if (!prog->aux->func_info) { | 7662 | if (!prog->aux->func_info) { |
| 7666 | bpf_log(log, "Verifier bug\n"); | 7663 | verifier_bug(env, "func_info undefined"); |
| 7667 | return -EFAULT; | 7664 | return -EFAULT; |
| 7668 | } | 7665 | } |
| 7669 | 7666 | ||
| @@ -7687,7 +7684,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog) · 17 unmodified lines | |||
| 7687 | tname = btf_name_by_offset(btf, fn_t->name_off); | 7684 | tname = btf_name_by_offset(btf, fn_t->name_off); |
| 7688 | 7685 | ||
| 7689 | if (prog->aux->func_info_aux[subprog].unreliable) { | 7686 | if (prog->aux->func_info_aux[subprog].unreliable) { |
| 7690 | bpf_log(log, "Verifier bug in function %s()\n", tname); | 7687 | verifier_bug(env, "unreliable BTF for function %s()", tname); |
| 7691 | return -EFAULT; | 7688 | return -EFAULT; |
| 7692 | } | 7689 | } |
| 7693 | if (prog_type == BPF_PROG_TYPE_EXT) | 7690 | if (prog_type == BPF_PROG_TYPE_EXT) |
| @@ -8564,7 +8561,7 @@ static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook, · 870 unmodified lines | |||
| 8564 | 8561 | ||
| 8565 | /* Grow set */ | 8562 | /* Grow set */ |
| 8566 | set = krealloc(tab->sets[hook], | 8563 | set = krealloc(tab->sets[hook], |
| 8567 | offsetof(struct btf_id_set8, pairs[set_cnt + add_set->cnt]), | 8564 | struct_size(set, pairs, set_cnt + add_set->cnt), |
| 8568 | GFP_KERNEL | __GFP_NOWARN); | 8565 | GFP_KERNEL | __GFP_NOWARN); |
| 8569 | if (!set) { | 8566 | if (!set) { |
| 8570 | ret = -ENOMEM; | 8567 | ret = -ENOMEM; |
| @@ -8850,7 +8847,7 @@ int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_c · 279 unmodified lines | |||
| 8850 | } | 8847 | } |
| 8851 | 8848 | ||
| 8852 | tab = krealloc(btf->dtor_kfunc_tab, | 8849 | tab = krealloc(btf->dtor_kfunc_tab, |
| 8853 | offsetof(struct btf_id_dtor_kfunc_tab, dtors[tab_cnt + add_cnt]), | 8850 | struct_size(tab, dtors, tab_cnt + add_cnt), |
| 8854 | GFP_KERNEL | __GFP_NOWARN); | 8851 | GFP_KERNEL | __GFP_NOWARN); |
| 8855 | if (!tab) { | 8852 | if (!tab) { |
| 8856 | ret = -ENOMEM; | 8853 | ret = -ENOMEM; |
| @@ -9408,8 +9405,7 @@ btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops, · 551 unmodified lines | |||
| 9408 | 9405 | ||
| 9409 | tab = btf->struct_ops_tab; | 9406 | tab = btf->struct_ops_tab; |
| 9410 | if (!tab) { | 9407 | if (!tab) { |
| 9411 | tab = kzalloc(offsetof(struct btf_struct_ops_tab, ops[4]), | 9408 | tab = kzalloc(struct_size(tab, ops, 4), GFP_KERNEL); |
| 9412 | GFP_KERNEL); | ||
| 9413 | if (!tab) | 9409 | if (!tab) |
| 9414 | return -ENOMEM; | 9410 | return -ENOMEM; |
| 9415 | tab->capacity = 4; | 9411 | tab->capacity = 4; |
| @@ -9422,8 +9418,7 @@ btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops, · 6 unmodified lines | |||
| 9422 | 9418 | ||
| 9423 | if (tab->cnt == tab->capacity) { | 9419 | if (tab->cnt == tab->capacity) { |
| 9424 | new_tab = krealloc(tab, | 9420 | new_tab = krealloc(tab, |
| 9425 | offsetof(struct btf_struct_ops_tab, | 9421 | struct_size(tab, ops, tab->capacity * 2), |
| 9426 | ops[tab->capacity * 2]), | ||
| 9427 | GFP_KERNEL); | 9422 | GFP_KERNEL); |
| 9428 | if (!new_tab) | 9423 | if (!new_tab) |
| 9429 | return -ENOMEM; | 9424 | return -ENOMEM; |
| @@ -1687,10 +1687,6 @@ cgroup_dev_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 1686 unmodified lines | |||
| 1687 | if (func_proto) | 1687 | if (func_proto) |
| 1688 | return func_proto; | 1688 | return func_proto; |
| 1689 | 1689 | ||
| 1690 | func_proto = cgroup_current_func_proto(func_id, prog); | ||
| 1691 | if (func_proto) | ||
| 1692 | return func_proto; | ||
| 1693 | |||
| 1694 | switch (func_id) { | 1690 | switch (func_id) { |
| 1695 | case BPF_FUNC_perf_event_output: | 1691 | case BPF_FUNC_perf_event_output: |
| 1696 | return &bpf_event_output_data_proto; | 1692 | return &bpf_event_output_data_proto; |
| @@ -2238,10 +2234,6 @@ sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 541 unmodified lines | |||
| 2238 | if (func_proto) | 2234 | if (func_proto) |
| 2239 | return func_proto; | 2235 | return func_proto; |
| 2240 | 2236 | ||
| 2241 | func_proto = cgroup_current_func_proto(func_id, prog); | ||
| 2242 | if (func_proto) | ||
| 2243 | return func_proto; | ||
| 2244 | |||
| 2245 | switch (func_id) { | 2237 | switch (func_id) { |
| 2246 | case BPF_FUNC_sysctl_get_name: | 2238 | case BPF_FUNC_sysctl_get_name: |
| 2247 | return &bpf_sysctl_get_name_proto; | 2239 | return &bpf_sysctl_get_name_proto; |
| @@ -2385,10 +2377,6 @@ cg_sockopt_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 137 unmodified lines | |||
| 2385 | if (func_proto) | 2377 | if (func_proto) |
| 2386 | return func_proto; | 2378 | return func_proto; |
| 2387 | 2379 | ||
| 2388 | func_proto = cgroup_current_func_proto(func_id, prog); | ||
| 2389 | if (func_proto) | ||
| 2390 | return func_proto; | ||
| 2391 | |||
| 2392 | switch (func_id) { | 2380 | switch (func_id) { |
| 2393 | #ifdef CONFIG_NET | 2381 | #ifdef CONFIG_NET |
| 2394 | case BPF_FUNC_get_netns_cookie: | 2382 | case BPF_FUNC_get_netns_cookie: |
| @@ -2635,23 +2623,3 @@ cgroup_common_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 240 unmodified lines | |||
| 2635 | return NULL; | 2623 | return NULL; |
| 2636 | } | 2624 | } |
| 2637 | } | 2625 | } |
| 2638 | |||
| 2639 | /* Common helpers for cgroup hooks with valid process context. */ | ||
| 2640 | const struct bpf_func_proto * | ||
| 2641 | cgroup_current_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) | ||
| 2642 | { | ||
| 2643 | switch (func_id) { | ||
| 2644 | case BPF_FUNC_get_current_uid_gid: | ||
| 2645 | return &bpf_get_current_uid_gid_proto; | ||
| 2646 | case BPF_FUNC_get_current_comm: | ||
| 2647 | return &bpf_get_current_comm_proto; | ||
| 2648 | #ifdef CONFIG_CGROUP_NET_CLASSID | ||
| 2649 | case BPF_FUNC_get_cgroup_classid: | ||
| 2650 | return &bpf_get_cgroup_classid_curr_proto; | ||
| 2651 | #endif | ||
| 2652 | case BPF_FUNC_current_task_under_cgroup: | ||
| 2653 | return &bpf_current_task_under_cgroup_proto; | ||
| 2654 | default: | ||
| 2655 | return NULL; | ||
| 2656 | } | ||
| 2657 | } | ||
| @@ -2358,8 +2358,8 @@ static unsigned int __bpf_prog_ret0_warn(const void *ctx, · 2357 unmodified lines | |||
| 2358 | return 0; | 2358 | return 0; |
| 2359 | } | 2359 | } |
| 2360 | 2360 | ||
| 2361 | bool bpf_prog_map_compatible(struct bpf_map *map, | 2361 | static bool __bpf_prog_map_compatible(struct bpf_map *map, |
| 2362 | const struct bpf_prog *fp) | 2362 | const struct bpf_prog *fp) |
| 2363 | { | 2363 | { |
| 2364 | enum bpf_prog_type prog_type = resolve_prog_type(fp); | 2364 | enum bpf_prog_type prog_type = resolve_prog_type(fp); |
| 2365 | bool ret; | 2365 | bool ret; |
| @@ -2368,14 +2368,6 @@ bool bpf_prog_map_compatible(struct bpf_map *map, · 2 unmodified lines | |||
| 2368 | if (fp->kprobe_override) | 2368 | if (fp->kprobe_override) |
| 2369 | return false; | 2369 | return false; |
| 2370 | 2370 | ||
| 2371 | /* XDP programs inserted into maps are not guaranteed to run on | ||
| 2372 | * a particular netdev (and can run outside driver context entirely | ||
| 2373 | * in the case of devmap and cpumap). Until device checks | ||
| 2374 | * are implemented, prohibit adding dev-bound programs to program maps. | ||
| 2375 | */ | ||
| 2376 | if (bpf_prog_is_dev_bound(aux)) | ||
| 2377 | return false; | ||
| 2378 | |||
| 2379 | spin_lock(&map->owner.lock); | 2371 | spin_lock(&map->owner.lock); |
| 2380 | if (!map->owner.type) { | 2372 | if (!map->owner.type) { |
| 2381 | /* There's no owner yet where we could check for | 2373 | /* There's no owner yet where we could check for |
| @@ -2409,6 +2401,19 @@ bool bpf_prog_map_compatible(struct bpf_map *map, · 27 unmodified lines | |||
| 2409 | return ret; | 2401 | return ret; |
| 2410 | } | 2402 | } |
| 2411 | 2403 | ||
| 2404 | bool bpf_prog_map_compatible(struct bpf_map *map, const struct bpf_prog *fp) | ||
| 2405 | { | ||
| 2406 | /* XDP programs inserted into maps are not guaranteed to run on | ||
| 2407 | * a particular netdev (and can run outside driver context entirely | ||
| 2408 | * in the case of devmap and cpumap). Until device checks | ||
| 2409 | * are implemented, prohibit adding dev-bound programs to program maps. | ||
| 2410 | */ | ||
| 2411 | if (bpf_prog_is_dev_bound(fp->aux)) | ||
| 2412 | return false; | ||
| 2413 | |||
| 2414 | return __bpf_prog_map_compatible(map, fp); | ||
| 2415 | } | ||
| 2416 | |||
| 2412 | static int bpf_check_tail_call(const struct bpf_prog *fp) | 2417 | static int bpf_check_tail_call(const struct bpf_prog *fp) |
| 2413 | { | 2418 | { |
| 2414 | struct bpf_prog_aux *aux = fp->aux; | 2419 | struct bpf_prog_aux *aux = fp->aux; |
| @@ -2421,7 +2426,7 @@ static int bpf_check_tail_call(const struct bpf_prog *fp) · 6 unmodified lines | |||
| 2421 | if (!map_type_contains_progs(map)) | 2426 | if (!map_type_contains_progs(map)) |
| 2422 | continue; | 2427 | continue; |
| 2423 | 2428 | ||
| 2424 | if (!bpf_prog_map_compatible(map, fp)) { | 2429 | if (!__bpf_prog_map_compatible(map, fp)) { |
| 2425 | ret = -EINVAL; | 2430 | ret = -EINVAL; |
| 2426 | goto out; | 2431 | goto out; |
| 2427 | } | 2432 | } |
| @@ -2469,7 +2474,7 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) · 41 unmodified lines | |||
| 2469 | /* In case of BPF to BPF calls, verifier did all the prep | 2474 | /* In case of BPF to BPF calls, verifier did all the prep |
| 2470 | * work with regards to JITing, etc. | 2475 | * work with regards to JITing, etc. |
| 2471 | */ | 2476 | */ |
| 2472 | bool jit_needed = false; | 2477 | bool jit_needed = fp->jit_requested; |
| 2473 | 2478 | ||
| 2474 | if (fp->bpf_func) | 2479 | if (fp->bpf_func) |
| 2475 | goto finalize; | 2480 | goto finalize; |
| @@ -0,0 +1,150 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* Copyright (c) 2025 Google LLC */ | ||
| 3 | #include <linux/bpf.h> | ||
| 4 | #include <linux/btf_ids.h> | ||
| 5 | #include <linux/dma-buf.h> | ||
| 6 | #include <linux/kernel.h> | ||
| 7 | #include <linux/seq_file.h> | ||
| 8 | |||
| 9 | static void *dmabuf_iter_seq_start(struct seq_file *seq, loff_t *pos) | ||
| 10 | { | ||
| 11 | if (*pos) | ||
| 12 | return NULL; | ||
| 13 | |||
| 14 | return dma_buf_iter_begin(); | ||
| 15 | } | ||
| 16 | |||
| 17 | static void *dmabuf_iter_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
| 18 | { | ||
| 19 | struct dma_buf *dmabuf = v; | ||
| 20 | |||
| 21 | ++*pos; | ||
| 22 | |||
| 23 | return dma_buf_iter_next(dmabuf); | ||
| 24 | } | ||
| 25 | |||
| 26 | struct bpf_iter__dmabuf { | ||
| 27 | __bpf_md_ptr(struct bpf_iter_meta *, meta); | ||
| 28 | __bpf_md_ptr(struct dma_buf *, dmabuf); | ||
| 29 | }; | ||
| 30 | |||
| 31 | static int __dmabuf_seq_show(struct seq_file *seq, void *v, bool in_stop) | ||
| 32 | { | ||
| 33 | struct bpf_iter_meta meta = { | ||
| 34 | .seq = seq, | ||
| 35 | }; | ||
| 36 | struct bpf_iter__dmabuf ctx = { | ||
| 37 | .meta = &meta, | ||
| 38 | .dmabuf = v, | ||
| 39 | }; | ||
| 40 | struct bpf_prog *prog = bpf_iter_get_info(&meta, in_stop); | ||
| 41 | |||
| 42 | if (prog) | ||
| 43 | return bpf_iter_run_prog(prog, &ctx); | ||
| 44 | |||
| 45 | return 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | static int dmabuf_iter_seq_show(struct seq_file *seq, void *v) | ||
| 49 | { | ||
| 50 | return __dmabuf_seq_show(seq, v, false); | ||
| 51 | } | ||
| 52 | |||
| 53 | static void dmabuf_iter_seq_stop(struct seq_file *seq, void *v) | ||
| 54 | { | ||
| 55 | struct dma_buf *dmabuf = v; | ||
| 56 | |||
| 57 | if (dmabuf) | ||
| 58 | dma_buf_put(dmabuf); | ||
| 59 | } | ||
| 60 | |||
| 61 | static const struct seq_operations dmabuf_iter_seq_ops = { | ||
| 62 | .start = dmabuf_iter_seq_start, | ||
| 63 | .next = dmabuf_iter_seq_next, | ||
| 64 | .stop = dmabuf_iter_seq_stop, | ||
| 65 | .show = dmabuf_iter_seq_show, | ||
| 66 | }; | ||
| 67 | |||
| 68 | static void bpf_iter_dmabuf_show_fdinfo(const struct bpf_iter_aux_info *aux, | ||
| 69 | struct seq_file *seq) | ||
| 70 | { | ||
| 71 | seq_puts(seq, "dmabuf iter\n"); | ||
| 72 | } | ||
| 73 | |||
| 74 | static const struct bpf_iter_seq_info dmabuf_iter_seq_info = { | ||
| 75 | .seq_ops = &dmabuf_iter_seq_ops, | ||
| 76 | .init_seq_private = NULL, | ||
| 77 | .fini_seq_private = NULL, | ||
| 78 | .seq_priv_size = 0, | ||
| 79 | }; | ||
| 80 | |||
| 81 | static struct bpf_iter_reg bpf_dmabuf_reg_info = { | ||
| 82 | .target = "dmabuf", | ||
| 83 | .feature = BPF_ITER_RESCHED, | ||
| 84 | .show_fdinfo = bpf_iter_dmabuf_show_fdinfo, | ||
| 85 | .ctx_arg_info_size = 1, | ||
| 86 | .ctx_arg_info = { | ||
| 87 | { offsetof(struct bpf_iter__dmabuf, dmabuf), | ||
| 88 | PTR_TO_BTF_ID_OR_NULL }, | ||
| 89 | }, | ||
| 90 | .seq_info = &dmabuf_iter_seq_info, | ||
| 91 | }; | ||
| 92 | |||
| 93 | DEFINE_BPF_ITER_FUNC(dmabuf, struct bpf_iter_meta *meta, struct dma_buf *dmabuf) | ||
| 94 | BTF_ID_LIST_SINGLE(bpf_dmabuf_btf_id, struct, dma_buf) | ||
| 95 | |||
| 96 | static int __init dmabuf_iter_init(void) | ||
| 97 | { | ||
| 98 | bpf_dmabuf_reg_info.ctx_arg_info[0].btf_id = bpf_dmabuf_btf_id[0]; | ||
| 99 | return bpf_iter_reg_target(&bpf_dmabuf_reg_info); | ||
| 100 | } | ||
| 101 | |||
| 102 | late_initcall(dmabuf_iter_init); | ||
| 103 | |||
| 104 | struct bpf_iter_dmabuf { | ||
| 105 | /* | ||
| 106 | * opaque iterator state; having __u64 here allows to preserve correct | ||
| 107 | * alignment requirements in vmlinux.h, generated from BTF | ||
| 108 | */ | ||
| 109 | __u64 __opaque[1]; | ||
| 110 | } __aligned(8); | ||
| 111 | |||
| 112 | /* Non-opaque version of bpf_iter_dmabuf */ | ||
| 113 | struct bpf_iter_dmabuf_kern { | ||
| 114 | struct dma_buf *dmabuf; | ||
| 115 | } __aligned(8); | ||
| 116 | |||
| 117 | __bpf_kfunc_start_defs(); | ||
| 118 | |||
| 119 | __bpf_kfunc int bpf_iter_dmabuf_new(struct bpf_iter_dmabuf *it) | ||
| 120 | { | ||
| 121 | struct bpf_iter_dmabuf_kern *kit = (void *)it; | ||
| 122 | |||
| 123 | BUILD_BUG_ON(sizeof(*kit) > sizeof(*it)); | ||
| 124 | BUILD_BUG_ON(__alignof__(*kit) != __alignof__(*it)); | ||
| 125 | |||
| 126 | kit->dmabuf = NULL; | ||
| 127 | return 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | __bpf_kfunc struct dma_buf *bpf_iter_dmabuf_next(struct bpf_iter_dmabuf *it) | ||
| 131 | { | ||
| 132 | struct bpf_iter_dmabuf_kern *kit = (void *)it; | ||
| 133 | |||
| 134 | if (kit->dmabuf) | ||
| 135 | kit->dmabuf = dma_buf_iter_next(kit->dmabuf); | ||
| 136 | else | ||
| 137 | kit->dmabuf = dma_buf_iter_begin(); | ||
| 138 | |||
| 139 | return kit->dmabuf; | ||
| 140 | } | ||
| 141 | |||
| 142 | __bpf_kfunc void bpf_iter_dmabuf_destroy(struct bpf_iter_dmabuf *it) | ||
| 143 | { | ||
| 144 | struct bpf_iter_dmabuf_kern *kit = (void *)it; | ||
| 145 | |||
| 146 | if (kit->dmabuf) | ||
| 147 | dma_buf_put(kit->dmabuf); | ||
| 148 | } | ||
| 149 | |||
| 150 | __bpf_kfunc_end_defs(); | ||
| @@ -175,20 +175,30 @@ static bool htab_is_percpu(const struct bpf_htab *htab) · 174 unmodified lines | |||
| 175 | htab->map.map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH; | 175 | htab->map.map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH; |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | static inline bool is_fd_htab(const struct bpf_htab *htab) | ||
| 179 | { | ||
| 180 | return htab->map.map_type == BPF_MAP_TYPE_HASH_OF_MAPS; | ||
| 181 | } | ||
| 182 | |||
| 183 | static inline void *htab_elem_value(struct htab_elem *l, u32 key_size) | ||
| 184 | { | ||
| 185 | return l->key + round_up(key_size, 8); | ||
| 186 | } | ||
| 187 | |||
| 178 | static inline void htab_elem_set_ptr(struct htab_elem *l, u32 key_size, | 188 | static inline void htab_elem_set_ptr(struct htab_elem *l, u32 key_size, |
| 179 | void __percpu *pptr) | 189 | void __percpu *pptr) |
| 180 | { | 190 | { |
| 181 | *(void __percpu **)(l->key + roundup(key_size, 8)) = pptr; | 191 | *(void __percpu **)htab_elem_value(l, key_size) = pptr; |
| 182 | } | 192 | } |
| 183 | 193 | ||
| 184 | static inline void __percpu *htab_elem_get_ptr(struct htab_elem *l, u32 key_size) | 194 | static inline void __percpu *htab_elem_get_ptr(struct htab_elem *l, u32 key_size) |
| 185 | { | 195 | { |
| 186 | return *(void __percpu **)(l->key + roundup(key_size, 8)); | 196 | return *(void __percpu **)htab_elem_value(l, key_size); |
| 187 | } | 197 | } |
| 188 | 198 | ||
| 189 | static void *fd_htab_map_get_ptr(const struct bpf_map *map, struct htab_elem *l) | 199 | static void *fd_htab_map_get_ptr(const struct bpf_map *map, struct htab_elem *l) |
| 190 | { | 200 | { |
| 191 | return *(void **)(l->key + roundup(map->key_size, 8)); | 201 | return *(void **)htab_elem_value(l, map->key_size); |
| 192 | } | 202 | } |
| 193 | 203 | ||
| 194 | static struct htab_elem *get_htab_elem(struct bpf_htab *htab, int i) | 204 | static struct htab_elem *get_htab_elem(struct bpf_htab *htab, int i) |
| @@ -196,9 +206,13 @@ static struct htab_elem *get_htab_elem(struct bpf_htab *htab, int i) · 1 unmodified line | |||
| 196 | return (struct htab_elem *) (htab->elems + i * (u64)htab->elem_size); | 206 | return (struct htab_elem *) (htab->elems + i * (u64)htab->elem_size); |
| 197 | } | 207 | } |
| 198 | 208 | ||
| 209 | /* Both percpu and fd htab support in-place update, so no need for | ||
| 210 | * extra elem. LRU itself can remove the least used element, so | ||
| 211 | * there is no need for an extra elem during map_update. | ||
| 212 | */ | ||
| 199 | static bool htab_has_extra_elems(struct bpf_htab *htab) | 213 | static bool htab_has_extra_elems(struct bpf_htab *htab) |
| 200 | { | 214 | { |
| 201 | return !htab_is_percpu(htab) && !htab_is_lru(htab); | 215 | return !htab_is_percpu(htab) && !htab_is_lru(htab) && !is_fd_htab(htab); |
| 202 | } | 216 | } |
| 203 | 217 | ||
| 204 | static void htab_free_prealloced_timers_and_wq(struct bpf_htab *htab) | 218 | static void htab_free_prealloced_timers_and_wq(struct bpf_htab *htab) |
| @@ -215,10 +229,10 @@ static void htab_free_prealloced_timers_and_wq(struct bpf_htab *htab) · 10 unmodified lines | |||
| 215 | elem = get_htab_elem(htab, i); | 229 | elem = get_htab_elem(htab, i); |
| 216 | if (btf_record_has_field(htab->map.record, BPF_TIMER)) | 230 | if (btf_record_has_field(htab->map.record, BPF_TIMER)) |
| 217 | bpf_obj_free_timer(htab->map.record, | 231 | bpf_obj_free_timer(htab->map.record, |
| 218 | elem->key + round_up(htab->map.key_size, 8)); | 232 | htab_elem_value(elem, htab->map.key_size)); |
| 219 | if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE)) | 233 | if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE)) |
| 220 | bpf_obj_free_workqueue(htab->map.record, | 234 | bpf_obj_free_workqueue(htab->map.record, |
| 221 | elem->key + round_up(htab->map.key_size, 8)); | 235 | htab_elem_value(elem, htab->map.key_size)); |
| 222 | cond_resched(); | 236 | cond_resched(); |
| 223 | } | 237 | } |
| 224 | } | 238 | } |
| @@ -245,7 +259,8 @@ static void htab_free_prealloced_fields(struct bpf_htab *htab) · 20 unmodified lines | |||
| 245 | cond_resched(); | 259 | cond_resched(); |
| 246 | } | 260 | } |
| 247 | } else { | 261 | } else { |
| 248 | bpf_obj_free_fields(htab->map.record, elem->key + round_up(htab->map.key_size, 8)); | 262 | bpf_obj_free_fields(htab->map.record, |
| 263 | htab_elem_value(elem, htab->map.key_size)); | ||
| 249 | cond_resched(); | 264 | cond_resched(); |
| 250 | } | 265 | } |
| 251 | cond_resched(); | 266 | cond_resched(); |
| @@ -453,8 +468,6 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr) · 201 unmodified lines | |||
| 453 | { | 468 | { |
| 454 | bool percpu = (attr->map_type == BPF_MAP_TYPE_PERCPU_HASH || | 469 | bool percpu = (attr->map_type == BPF_MAP_TYPE_PERCPU_HASH || |
| 455 | attr->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH); | 470 | attr->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH); |
| 456 | bool lru = (attr->map_type == BPF_MAP_TYPE_LRU_HASH || | ||
| 457 | attr->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH); | ||
| 458 | /* percpu_lru means each cpu has its own LRU list. | 471 | /* percpu_lru means each cpu has its own LRU list. |
| 459 | * it is different from BPF_MAP_TYPE_PERCPU_HASH where | 472 | * it is different from BPF_MAP_TYPE_PERCPU_HASH where |
| 460 | * the map's value itself is percpu. percpu_lru has | 473 | * the map's value itself is percpu. percpu_lru has |
| @@ -549,10 +562,7 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr) · 88 unmodified lines | |||
| 549 | if (err) | 562 | if (err) |
| 550 | goto free_map_locked; | 563 | goto free_map_locked; |
| 551 | 564 | ||
| 552 | if (!percpu && !lru) { | 565 | if (htab_has_extra_elems(htab)) { |
| 553 | /* lru itself can remove the least used element, so | ||
| 554 | * there is no need for an extra elem during map_update. | ||
| 555 | */ | ||
| 556 | err = alloc_extra_elems(htab); | 566 | err = alloc_extra_elems(htab); |
| 557 | if (err) | 567 | if (err) |
| 558 | goto free_prealloc; | 568 | goto free_prealloc; |
| @@ -670,7 +680,7 @@ static void *htab_map_lookup_elem(struct bpf_map *map, void *key) · 111 unmodified lines | |||
| 670 | struct htab_elem *l = __htab_map_lookup_elem(map, key); | 680 | struct htab_elem *l = __htab_map_lookup_elem(map, key); |
| 671 | 681 | ||
| 672 | if (l) | 682 | if (l) |
| 673 | return l->key + round_up(map->key_size, 8); | 683 | return htab_elem_value(l, map->key_size); |
| 674 | 684 | ||
| 675 | return NULL; | 685 | return NULL; |
| 676 | } | 686 | } |
| @@ -709,7 +719,7 @@ static __always_inline void *__htab_lru_map_lookup_elem(struct bpf_map *map, · 32 unmodified lines | |||
| 709 | if (l) { | 719 | if (l) { |
| 710 | if (mark) | 720 | if (mark) |
| 711 | bpf_lru_node_set_ref(&l->lru_node); | 721 | bpf_lru_node_set_ref(&l->lru_node); |
| 712 | return l->key + round_up(map->key_size, 8); | 722 | return htab_elem_value(l, map->key_size); |
| 713 | } | 723 | } |
| 714 | 724 | ||
| 715 | return NULL; | 725 | return NULL; |
| @@ -763,7 +773,7 @@ static void check_and_free_fields(struct bpf_htab *htab, · 47 unmodified lines | |||
| 763 | for_each_possible_cpu(cpu) | 773 | for_each_possible_cpu(cpu) |
| 764 | bpf_obj_free_fields(htab->map.record, per_cpu_ptr(pptr, cpu)); | 774 | bpf_obj_free_fields(htab->map.record, per_cpu_ptr(pptr, cpu)); |
| 765 | } else { | 775 | } else { |
| 766 | void *map_value = elem->key + round_up(htab->map.key_size, 8); | 776 | void *map_value = htab_elem_value(elem, htab->map.key_size); |
| 767 | 777 | ||
| 768 | bpf_obj_free_fields(htab->map.record, map_value); | 778 | bpf_obj_free_fields(htab->map.record, map_value); |
| 769 | } | 779 | } |
| @@ -968,8 +978,7 @@ static void pcpu_init_value(struct bpf_htab *htab, void __percpu *pptr, · 198 unmodified lines | |||
| 968 | 978 | ||
| 969 | static bool fd_htab_map_needs_adjust(const struct bpf_htab *htab) | 979 | static bool fd_htab_map_needs_adjust(const struct bpf_htab *htab) |
| 970 | { | 980 | { |
| 971 | return htab->map.map_type == BPF_MAP_TYPE_HASH_OF_MAPS && | 981 | return is_fd_htab(htab) && BITS_PER_LONG == 64; |
| 972 | BITS_PER_LONG == 64; | ||
| 973 | } | 982 | } |
| 974 | 983 | ||
| 975 | static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key, | 984 | static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key, |
| @@ -1039,11 +1048,9 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key, · 63 unmodified lines | |||
| 1039 | htab_elem_set_ptr(l_new, key_size, pptr); | 1048 | htab_elem_set_ptr(l_new, key_size, pptr); |
| 1040 | } else if (fd_htab_map_needs_adjust(htab)) { | 1049 | } else if (fd_htab_map_needs_adjust(htab)) { |
| 1041 | size = round_up(size, 8); | 1050 | size = round_up(size, 8); |
| 1042 | memcpy(l_new->key + round_up(key_size, 8), value, size); | 1051 | memcpy(htab_elem_value(l_new, key_size), value, size); |
| 1043 | } else { | 1052 | } else { |
| 1044 | copy_map_value(&htab->map, | 1053 | copy_map_value(&htab->map, htab_elem_value(l_new, key_size), value); |
| 1045 | l_new->key + round_up(key_size, 8), | ||
| 1046 | value); | ||
| 1047 | } | 1054 | } |
| 1048 | 1055 | ||
| 1049 | l_new->hash = hash; | 1056 | l_new->hash = hash; |
| @@ -1072,10 +1079,9 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value, · 22 unmodified lines | |||
| 1072 | u64 map_flags) | 1079 | u64 map_flags) |
| 1073 | { | 1080 | { |
| 1074 | struct bpf_htab *htab = container_of(map, struct bpf_htab, map); | 1081 | struct bpf_htab *htab = container_of(map, struct bpf_htab, map); |
| 1075 | struct htab_elem *l_new = NULL, *l_old; | 1082 | struct htab_elem *l_new, *l_old; |
| 1076 | struct hlist_nulls_head *head; | 1083 | struct hlist_nulls_head *head; |
| 1077 | unsigned long flags; | 1084 | unsigned long flags; |
| 1078 | void *old_map_ptr; | ||
| 1079 | struct bucket *b; | 1085 | struct bucket *b; |
| 1080 | u32 key_size, hash; | 1086 | u32 key_size, hash; |
| 1081 | int ret; | 1087 | int ret; |
| @@ -1106,7 +1112,7 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value, · 24 unmodified lines | |||
| 1106 | if (l_old) { | 1112 | if (l_old) { |
| 1107 | /* grab the element lock and update value in place */ | 1113 | /* grab the element lock and update value in place */ |
| 1108 | copy_map_value_locked(map, | 1114 | copy_map_value_locked(map, |
| 1109 | l_old->key + round_up(key_size, 8), | 1115 | htab_elem_value(l_old, key_size), |
| 1110 | value, false); | 1116 | value, false); |
| 1111 | return 0; | 1117 | return 0; |
| 1112 | } | 1118 | } |
| @@ -1134,7 +1140,7 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value, · 21 unmodified lines | |||
| 1134 | * and update element in place | 1140 | * and update element in place |
| 1135 | */ | 1141 | */ |
| 1136 | copy_map_value_locked(map, | 1142 | copy_map_value_locked(map, |
| 1137 | l_old->key + round_up(key_size, 8), | 1143 | htab_elem_value(l_old, key_size), |
| 1138 | value, false); | 1144 | value, false); |
| 1139 | ret = 0; | 1145 | ret = 0; |
| 1140 | goto err; | 1146 | goto err; |
| @@ -1156,24 +1162,14 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value, · 15 unmodified lines | |||
| 1156 | hlist_nulls_del_rcu(&l_old->hash_node); | 1162 | hlist_nulls_del_rcu(&l_old->hash_node); |
| 1157 | 1163 | ||
| 1158 | /* l_old has already been stashed in htab->extra_elems, free | 1164 | /* l_old has already been stashed in htab->extra_elems, free |
| 1159 | * its special fields before it is available for reuse. Also | 1165 | * its special fields before it is available for reuse. |
| 1160 | * save the old map pointer in htab of maps before unlock | ||
| 1161 | * and release it after unlock. | ||
| 1162 | */ | 1166 | */ |
| 1163 | old_map_ptr = NULL; | 1167 | if (htab_is_prealloc(htab)) |
| 1164 | if (htab_is_prealloc(htab)) { | ||
| 1165 | if (map->ops->map_fd_put_ptr) | ||
| 1166 | old_map_ptr = fd_htab_map_get_ptr(map, l_old); | ||
| 1167 | check_and_free_fields(htab, l_old); | 1168 | check_and_free_fields(htab, l_old); |
| 1168 | } | ||
| 1169 | } | 1169 | } |
| 1170 | htab_unlock_bucket(b, flags); | 1170 | htab_unlock_bucket(b, flags); |
| 1171 | if (l_old) { | 1171 | if (l_old && !htab_is_prealloc(htab)) |
| 1172 | if (old_map_ptr) | 1172 | free_htab_elem(htab, l_old); |
| 1173 | map->ops->map_fd_put_ptr(map, old_map_ptr, true); | ||
| 1174 | if (!htab_is_prealloc(htab)) | ||
| 1175 | free_htab_elem(htab, l_old); | ||
| 1176 | } | ||
| 1177 | return 0; | 1173 | return 0; |
| 1178 | err: | 1174 | err: |
| 1179 | htab_unlock_bucket(b, flags); | 1175 | htab_unlock_bucket(b, flags); |
| @@ -1220,8 +1216,7 @@ static long htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value · 40 unmodified lines | |||
| 1220 | l_new = prealloc_lru_pop(htab, key, hash); | 1216 | l_new = prealloc_lru_pop(htab, key, hash); |
| 1221 | if (!l_new) | 1217 | if (!l_new) |
| 1222 | return -ENOMEM; | 1218 | return -ENOMEM; |
| 1223 | copy_map_value(&htab->map, | 1219 | copy_map_value(&htab->map, htab_elem_value(l_new, map->key_size), value); |
| 1224 | l_new->key + round_up(map->key_size, 8), value); | ||
| 1225 | 1220 | ||
| 1226 | ret = htab_lock_bucket(b, &flags); | 1221 | ret = htab_lock_bucket(b, &flags); |
| 1227 | if (ret) | 1222 | if (ret) |
| @@ -1255,13 +1250,14 @@ err_lock_bucket: · 27 unmodified lines | |||
| 1255 | return ret; | 1250 | return ret; |
| 1256 | } | 1251 | } |
| 1257 | 1252 | ||
| 1258 | static long __htab_percpu_map_update_elem(struct bpf_map *map, void *key, | 1253 | static long htab_map_update_elem_in_place(struct bpf_map *map, void *key, |
| 1259 | void *value, u64 map_flags, | 1254 | void *value, u64 map_flags, |
| 1260 | bool onallcpus) | 1255 | bool percpu, bool onallcpus) |
| 1261 | { | 1256 | { |
| 1262 | struct bpf_htab *htab = container_of(map, struct bpf_htab, map); | 1257 | struct bpf_htab *htab = container_of(map, struct bpf_htab, map); |
| 1263 | struct htab_elem *l_new = NULL, *l_old; | 1258 | struct htab_elem *l_new, *l_old; |
| 1264 | struct hlist_nulls_head *head; | 1259 | struct hlist_nulls_head *head; |
| 1260 | void *old_map_ptr = NULL; | ||
| 1265 | unsigned long flags; | 1261 | unsigned long flags; |
| 1266 | struct bucket *b; | 1262 | struct bucket *b; |
| 1267 | u32 key_size, hash; | 1263 | u32 key_size, hash; |
| @@ -1292,21 +1288,29 @@ static long __htab_percpu_map_update_elem(struct bpf_map *map, void *key, · 24 unmodified lines | |||
| 1292 | goto err; | 1288 | goto err; |
| 1293 | 1289 | ||
| 1294 | if (l_old) { | 1290 | if (l_old) { |
| 1295 | /* per-cpu hash map can update value in-place */ | 1291 | /* Update value in-place */ |
| 1296 | pcpu_copy_value(htab, htab_elem_get_ptr(l_old, key_size), | 1292 | if (percpu) { |
| 1297 | value, onallcpus); | 1293 | pcpu_copy_value(htab, htab_elem_get_ptr(l_old, key_size), |
| 1294 | value, onallcpus); | ||
| 1295 | } else { | ||
| 1296 | void **inner_map_pptr = htab_elem_value(l_old, key_size); | ||
| 1297 | |||
| 1298 | old_map_ptr = *inner_map_pptr; | ||
| 1299 | WRITE_ONCE(*inner_map_pptr, *(void **)value); | ||
| 1300 | } | ||
| 1298 | } else { | 1301 | } else { |
| 1299 | l_new = alloc_htab_elem(htab, key, value, key_size, | 1302 | l_new = alloc_htab_elem(htab, key, value, key_size, |
| 1300 | hash, true, onallcpus, NULL); | 1303 | hash, percpu, onallcpus, NULL); |
| 1301 | if (IS_ERR(l_new)) { | 1304 | if (IS_ERR(l_new)) { |
| 1302 | ret = PTR_ERR(l_new); | 1305 | ret = PTR_ERR(l_new); |
| 1303 | goto err; | 1306 | goto err; |
| 1304 | } | 1307 | } |
| 1305 | hlist_nulls_add_head_rcu(&l_new->hash_node, head); | 1308 | hlist_nulls_add_head_rcu(&l_new->hash_node, head); |
| 1306 | } | 1309 | } |
| 1307 | ret = 0; | ||
| 1308 | err: | 1310 | err: |
| 1309 | htab_unlock_bucket(b, flags); | 1311 | htab_unlock_bucket(b, flags); |
| 1312 | if (old_map_ptr) | ||
| 1313 | map->ops->map_fd_put_ptr(map, old_map_ptr, true); | ||
| 1310 | return ret; | 1314 | return ret; |
| 1311 | } | 1315 | } |
| 1312 | 1316 | ||
| @@ -1383,7 +1387,7 @@ err_lock_bucket: · 70 unmodified lines | |||
| 1383 | static long htab_percpu_map_update_elem(struct bpf_map *map, void *key, | 1387 | static long htab_percpu_map_update_elem(struct bpf_map *map, void *key, |
| 1384 | void *value, u64 map_flags) | 1388 | void *value, u64 map_flags) |
| 1385 | { | 1389 | { |
| 1386 | return __htab_percpu_map_update_elem(map, key, value, map_flags, false); | 1390 | return htab_map_update_elem_in_place(map, key, value, map_flags, true, false); |
| 1387 | } | 1391 | } |
| 1388 | 1392 | ||
| 1389 | static long htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key, | 1393 | static long htab_lru_percpu_map_update_elem(struct bpf_map *map, void *key, |
| @@ -1500,10 +1504,10 @@ static void htab_free_malloced_timers_and_wq(struct bpf_htab *htab) · 110 unmodified lines | |||
| 1500 | /* We only free timer on uref dropping to zero */ | 1504 | /* We only free timer on uref dropping to zero */ |
| 1501 | if (btf_record_has_field(htab->map.record, BPF_TIMER)) | 1505 | if (btf_record_has_field(htab->map.record, BPF_TIMER)) |
| 1502 | bpf_obj_free_timer(htab->map.record, | 1506 | bpf_obj_free_timer(htab->map.record, |
| 1503 | l->key + round_up(htab->map.key_size, 8)); | 1507 | htab_elem_value(l, htab->map.key_size)); |
| 1504 | if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE)) | 1508 | if (btf_record_has_field(htab->map.record, BPF_WORKQUEUE)) |
| 1505 | bpf_obj_free_workqueue(htab->map.record, | 1509 | bpf_obj_free_workqueue(htab->map.record, |
| 1506 | l->key + round_up(htab->map.key_size, 8)); | 1510 | htab_elem_value(l, htab->map.key_size)); |
| 1507 | } | 1511 | } |
| 1508 | cond_resched_rcu(); | 1512 | cond_resched_rcu(); |
| 1509 | } | 1513 | } |
| @@ -1615,15 +1619,12 @@ static int __htab_map_lookup_and_delete_elem(struct bpf_map *map, void *key, · 105 unmodified lines | |||
| 1615 | off += roundup_value_size; | 1619 | off += roundup_value_size; |
| 1616 | } | 1620 | } |
| 1617 | } else { | 1621 | } else { |
| 1618 | u32 roundup_key_size = round_up(map->key_size, 8); | 1622 | void *src = htab_elem_value(l, map->key_size); |
| 1619 | 1623 | ||
| 1620 | if (flags & BPF_F_LOCK) | 1624 | if (flags & BPF_F_LOCK) |
| 1621 | copy_map_value_locked(map, value, l->key + | 1625 | copy_map_value_locked(map, value, src, true); |
| 1622 | roundup_key_size, | ||
| 1623 | true); | ||
| 1624 | else | 1626 | else |
| 1625 | copy_map_value(map, value, l->key + | 1627 | copy_map_value(map, value, src); |
| 1626 | roundup_key_size); | ||
| 1627 | /* Zeroing special fields in the temp buffer */ | 1628 | /* Zeroing special fields in the temp buffer */ |
| 1628 | check_and_init_map_value(map, value); | 1629 | check_and_init_map_value(map, value); |
| 1629 | } | 1630 | } |
| @@ -1680,12 +1681,12 @@ __htab_map_lookup_and_delete_batch(struct bpf_map *map, · 50 unmodified lines | |||
| 1680 | bool is_percpu) | 1681 | bool is_percpu) |
| 1681 | { | 1682 | { |
| 1682 | struct bpf_htab *htab = container_of(map, struct bpf_htab, map); | 1683 | struct bpf_htab *htab = container_of(map, struct bpf_htab, map); |
| 1683 | u32 bucket_cnt, total, key_size, value_size, roundup_key_size; | ||
| 1684 | void *keys = NULL, *values = NULL, *value, *dst_key, *dst_val; | 1684 | void *keys = NULL, *values = NULL, *value, *dst_key, *dst_val; |
| 1685 | void __user *uvalues = u64_to_user_ptr(attr->batch.values); | 1685 | void __user *uvalues = u64_to_user_ptr(attr->batch.values); |
| 1686 | void __user *ukeys = u64_to_user_ptr(attr->batch.keys); | 1686 | void __user *ukeys = u64_to_user_ptr(attr->batch.keys); |
| 1687 | void __user *ubatch = u64_to_user_ptr(attr->batch.in_batch); | 1687 | void __user *ubatch = u64_to_user_ptr(attr->batch.in_batch); |
| 1688 | u32 batch, max_count, size, bucket_size, map_id; | 1688 | u32 batch, max_count, size, bucket_size, map_id; |
| 1689 | u32 bucket_cnt, total, key_size, value_size; | ||
| 1689 | struct htab_elem *node_to_free = NULL; | 1690 | struct htab_elem *node_to_free = NULL; |
| 1690 | u64 elem_map_flags, map_flags; | 1691 | u64 elem_map_flags, map_flags; |
| 1691 | struct hlist_nulls_head *head; | 1692 | struct hlist_nulls_head *head; |
| @@ -1720,7 +1721,6 @@ __htab_map_lookup_and_delete_batch(struct bpf_map *map, · 28 unmodified lines | |||
| 1720 | return -ENOENT; | 1721 | return -ENOENT; |
| 1721 | 1722 | ||
| 1722 | key_size = htab->map.key_size; | 1723 | key_size = htab->map.key_size; |
| 1723 | roundup_key_size = round_up(htab->map.key_size, 8); | ||
| 1724 | value_size = htab->map.value_size; | 1724 | value_size = htab->map.value_size; |
| 1725 | size = round_up(value_size, 8); | 1725 | size = round_up(value_size, 8); |
| 1726 | if (is_percpu) | 1726 | if (is_percpu) |
| @@ -1812,8 +1812,8 @@ again_nocopy: · 85 unmodified lines | |||
| 1812 | off += size; | 1812 | off += size; |
| 1813 | } | 1813 | } |
| 1814 | } else { | 1814 | } else { |
| 1815 | value = l->key + roundup_key_size; | 1815 | value = htab_elem_value(l, key_size); |
| 1816 | if (map->map_type == BPF_MAP_TYPE_HASH_OF_MAPS) { | 1816 | if (is_fd_htab(htab)) { |
| 1817 | struct bpf_map **inner_map = value; | 1817 | struct bpf_map **inner_map = value; |
| 1818 | 1818 | ||
| 1819 | /* Actual value is the id of the inner map */ | 1819 | /* Actual value is the id of the inner map */ |
| @@ -2063,11 +2063,11 @@ static void *bpf_hash_map_seq_next(struct seq_file *seq, void *v, loff_t *pos) · 243 unmodified lines | |||
| 2063 | static int __bpf_hash_map_seq_show(struct seq_file *seq, struct htab_elem *elem) | 2063 | static int __bpf_hash_map_seq_show(struct seq_file *seq, struct htab_elem *elem) |
| 2064 | { | 2064 | { |
| 2065 | struct bpf_iter_seq_hash_map_info *info = seq->private; | 2065 | struct bpf_iter_seq_hash_map_info *info = seq->private; |
| 2066 | u32 roundup_key_size, roundup_value_size; | ||
| 2067 | struct bpf_iter__bpf_map_elem ctx = {}; | 2066 | struct bpf_iter__bpf_map_elem ctx = {}; |
| 2068 | struct bpf_map *map = info->map; | 2067 | struct bpf_map *map = info->map; |
| 2069 | struct bpf_iter_meta meta; | 2068 | struct bpf_iter_meta meta; |
| 2070 | int ret = 0, off = 0, cpu; | 2069 | int ret = 0, off = 0, cpu; |
| 2070 | u32 roundup_value_size; | ||
| 2071 | struct bpf_prog *prog; | 2071 | struct bpf_prog *prog; |
| 2072 | void __percpu *pptr; | 2072 | void __percpu *pptr; |
| 2073 | 2073 | ||
| @@ -2077,10 +2077,9 @@ static int __bpf_hash_map_seq_show(struct seq_file *seq, struct htab_elem *elem) · 3 unmodified lines | |||
| 2077 | ctx.meta = &meta; | 2077 | ctx.meta = &meta; |
| 2078 | ctx.map = info->map; | 2078 | ctx.map = info->map; |
| 2079 | if (elem) { | 2079 | if (elem) { |
| 2080 | roundup_key_size = round_up(map->key_size, 8); | ||
| 2081 | ctx.key = elem->key; | 2080 | ctx.key = elem->key; |
| 2082 | if (!info->percpu_value_buf) { | 2081 | if (!info->percpu_value_buf) { |
| 2083 | ctx.value = elem->key + roundup_key_size; | 2082 | ctx.value = htab_elem_value(elem, map->key_size); |
| 2084 | } else { | 2083 | } else { |
| 2085 | roundup_value_size = round_up(map->value_size, 8); | 2084 | roundup_value_size = round_up(map->value_size, 8); |
| 2086 | pptr = htab_elem_get_ptr(elem, map->key_size); | 2085 | pptr = htab_elem_get_ptr(elem, map->key_size); |
| @@ -2165,7 +2164,6 @@ static long bpf_for_each_hash_elem(struct bpf_map *map, bpf_callback_t callback_ · 78 unmodified lines | |||
| 2165 | struct hlist_nulls_head *head; | 2164 | struct hlist_nulls_head *head; |
| 2166 | struct hlist_nulls_node *n; | 2165 | struct hlist_nulls_node *n; |
| 2167 | struct htab_elem *elem; | 2166 | struct htab_elem *elem; |
| 2168 | u32 roundup_key_size; | ||
| 2169 | int i, num_elems = 0; | 2167 | int i, num_elems = 0; |
| 2170 | void __percpu *pptr; | 2168 | void __percpu *pptr; |
| 2171 | struct bucket *b; | 2169 | struct bucket *b; |
| @@ -2180,7 +2178,6 @@ static long bpf_for_each_hash_elem(struct bpf_map *map, bpf_callback_t callback_ · 8 unmodified lines | |||
| 2180 | 2178 | ||
| 2181 | is_percpu = htab_is_percpu(htab); | 2179 | is_percpu = htab_is_percpu(htab); |
| 2182 | 2180 | ||
| 2183 | roundup_key_size = round_up(map->key_size, 8); | ||
| 2184 | /* migration has been disabled, so percpu value prepared here will be | 2181 | /* migration has been disabled, so percpu value prepared here will be |
| 2185 | * the same as the one seen by the bpf program with | 2182 | * the same as the one seen by the bpf program with |
| 2186 | * bpf_map_lookup_elem(). | 2183 | * bpf_map_lookup_elem(). |
| @@ -2196,7 +2193,7 @@ static long bpf_for_each_hash_elem(struct bpf_map *map, bpf_callback_t callback_ · 9 unmodified lines | |||
| 2196 | pptr = htab_elem_get_ptr(elem, map->key_size); | 2193 | pptr = htab_elem_get_ptr(elem, map->key_size); |
| 2197 | val = this_cpu_ptr(pptr); | 2194 | val = this_cpu_ptr(pptr); |
| 2198 | } else { | 2195 | } else { |
| 2199 | val = elem->key + roundup_key_size; | 2196 | val = htab_elem_value(elem, map->key_size); |
| 2200 | } | 2197 | } |
| 2201 | num_elems++; | 2198 | num_elems++; |
| 2202 | ret = callback_fn((u64)(long)map, (u64)(long)key, | 2199 | ret = callback_fn((u64)(long)map, (u64)(long)key, |
| @@ -2411,8 +2408,8 @@ int bpf_percpu_hash_update(struct bpf_map *map, void *key, void *value, · 208 unmodified lines | |||
| 2411 | ret = __htab_lru_percpu_map_update_elem(map, key, value, | 2408 | ret = __htab_lru_percpu_map_update_elem(map, key, value, |
| 2412 | map_flags, true); | 2409 | map_flags, true); |
| 2413 | else | 2410 | else |
| 2414 | ret = __htab_percpu_map_update_elem(map, key, value, map_flags, | 2411 | ret = htab_map_update_elem_in_place(map, key, value, map_flags, |
| 2415 | true); | 2412 | true, true); |
| 2416 | rcu_read_unlock(); | 2413 | rcu_read_unlock(); |
| 2417 | 2414 | ||
| 2418 | return ret; | 2415 | return ret; |
| @@ -2536,24 +2533,23 @@ int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value) · 117 unmodified lines | |||
| 2536 | return ret; | 2533 | return ret; |
| 2537 | } | 2534 | } |
| 2538 | 2535 | ||
| 2539 | /* only called from syscall */ | 2536 | /* Only called from syscall */ |
| 2540 | int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file, | 2537 | int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file, |
| 2541 | void *key, void *value, u64 map_flags) | 2538 | void *key, void *value, u64 map_flags) |
| 2542 | { | 2539 | { |
| 2543 | void *ptr; | 2540 | void *ptr; |
| 2544 | int ret; | 2541 | int ret; |
| 2545 | u32 ufd = *(u32 *)value; | ||
| 2546 | 2542 | ||
| 2547 | ptr = map->ops->map_fd_get_ptr(map, map_file, ufd); | 2543 | ptr = map->ops->map_fd_get_ptr(map, map_file, *(int *)value); |
| 2548 | if (IS_ERR(ptr)) | 2544 | if (IS_ERR(ptr)) |
| 2549 | return PTR_ERR(ptr); | 2545 | return PTR_ERR(ptr); |
| 2550 | 2546 | ||
| 2551 | /* The htab bucket lock is always held during update operations in fd | 2547 | /* The htab bucket lock is always held during update operations in fd |
| 2552 | * htab map, and the following rcu_read_lock() is only used to avoid | 2548 | * htab map, and the following rcu_read_lock() is only used to avoid |
| 2553 | * the WARN_ON_ONCE in htab_map_update_elem(). | 2549 | * the WARN_ON_ONCE in htab_map_update_elem_in_place(). |
| 2554 | */ | 2550 | */ |
| 2555 | rcu_read_lock(); | 2551 | rcu_read_lock(); |
| 2556 | ret = htab_map_update_elem(map, key, &ptr, map_flags); | 2552 | ret = htab_map_update_elem_in_place(map, key, &ptr, map_flags, false, false); |
| 2557 | rcu_read_unlock(); | 2553 | rcu_read_unlock(); |
| 2558 | if (ret) | 2554 | if (ret) |
| 2559 | map->ops->map_fd_put_ptr(map, ptr, false); | 2555 | map->ops->map_fd_put_ptr(map, ptr, false); |
| @@ -23,6 +23,7 @@ · 22 unmodified lines | |||
| 23 | #include <linux/btf_ids.h> | 23 | #include <linux/btf_ids.h> |
| 24 | #include <linux/bpf_mem_alloc.h> | 24 | #include <linux/bpf_mem_alloc.h> |
| 25 | #include <linux/kasan.h> | 25 | #include <linux/kasan.h> |
| 26 | #include <linux/bpf_verifier.h> | ||
| 26 | 27 | ||
| 27 | #include "../../lib/kstrtox.h" | 28 | #include "../../lib/kstrtox.h" |
| 28 | 29 | ||
| @@ -129,7 +130,8 @@ const struct bpf_func_proto bpf_map_peek_elem_proto = { · 100 unmodified lines | |||
| 129 | 130 | ||
| 130 | BPF_CALL_3(bpf_map_lookup_percpu_elem, struct bpf_map *, map, void *, key, u32, cpu) | 131 | BPF_CALL_3(bpf_map_lookup_percpu_elem, struct bpf_map *, map, void *, key, u32, cpu) |
| 131 | { | 132 | { |
| 132 | WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_bh_held()); | 133 | WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() && |
| 134 | !rcu_read_lock_bh_held()); | ||
| 133 | return (unsigned long) map->ops->map_lookup_percpu_elem(map, key, cpu); | 135 | return (unsigned long) map->ops->map_lookup_percpu_elem(map, key, cpu); |
| 134 | } | 136 | } |
| 135 | 137 | ||
| @@ -1713,16 +1715,6 @@ void bpf_dynptr_set_null(struct bpf_dynptr_kern *ptr) · 1577 unmodified lines | |||
| 1713 | memset(ptr, 0, sizeof(*ptr)); | 1715 | memset(ptr, 0, sizeof(*ptr)); |
| 1714 | } | 1716 | } |
| 1715 | 1717 | ||
| 1716 | static int bpf_dynptr_check_off_len(const struct bpf_dynptr_kern *ptr, u32 offset, u32 len) | ||
| 1717 | { | ||
| 1718 | u32 size = __bpf_dynptr_size(ptr); | ||
| 1719 | |||
| 1720 | if (len > size || offset > size - len) | ||
| 1721 | return -E2BIG; | ||
| 1722 | |||
| 1723 | return 0; | ||
| 1724 | } | ||
| 1725 | |||
| 1726 | BPF_CALL_4(bpf_dynptr_from_mem, void *, data, u32, size, u64, flags, struct bpf_dynptr_kern *, ptr) | 1718 | BPF_CALL_4(bpf_dynptr_from_mem, void *, data, u32, size, u64, flags, struct bpf_dynptr_kern *, ptr) |
| 1727 | { | 1719 | { |
| 1728 | int err; | 1720 | int err; |
| @@ -1809,8 +1801,8 @@ static const struct bpf_func_proto bpf_dynptr_read_proto = { · 80 unmodified lines | |||
| 1809 | .arg5_type = ARG_ANYTHING, | 1801 | .arg5_type = ARG_ANYTHING, |
| 1810 | }; | 1802 | }; |
| 1811 | 1803 | ||
| 1812 | static int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u32 offset, void *src, | 1804 | int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u32 offset, void *src, |
| 1813 | u32 len, u64 flags) | 1805 | u32 len, u64 flags) |
| 1814 | { | 1806 | { |
| 1815 | enum bpf_dynptr_type type; | 1807 | enum bpf_dynptr_type type; |
| 1816 | int err; | 1808 | int err; |
| @@ -1912,6 +1904,12 @@ const struct bpf_func_proto bpf_probe_read_user_str_proto __weak; · 95 unmodified lines | |||
| 1912 | const struct bpf_func_proto bpf_probe_read_kernel_proto __weak; | 1904 | const struct bpf_func_proto bpf_probe_read_kernel_proto __weak; |
| 1913 | const struct bpf_func_proto bpf_probe_read_kernel_str_proto __weak; | 1905 | const struct bpf_func_proto bpf_probe_read_kernel_str_proto __weak; |
| 1914 | const struct bpf_func_proto bpf_task_pt_regs_proto __weak; | 1906 | const struct bpf_func_proto bpf_task_pt_regs_proto __weak; |
| 1907 | const struct bpf_func_proto bpf_perf_event_read_proto __weak; | ||
| 1908 | const struct bpf_func_proto bpf_send_signal_proto __weak; | ||
| 1909 | const struct bpf_func_proto bpf_send_signal_thread_proto __weak; | ||
| 1910 | const struct bpf_func_proto bpf_get_task_stack_sleepable_proto __weak; | ||
| 1911 | const struct bpf_func_proto bpf_get_task_stack_proto __weak; | ||
| 1912 | const struct bpf_func_proto bpf_get_branch_snapshot_proto __weak; | ||
| 1915 | 1913 | ||
| 1916 | const struct bpf_func_proto * | 1914 | const struct bpf_func_proto * |
| 1917 | bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) | 1915 | bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) |
| @@ -1965,6 +1963,8 @@ bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 47 unmodified lines | |||
| 1965 | return &bpf_get_current_pid_tgid_proto; | 1963 | return &bpf_get_current_pid_tgid_proto; |
| 1966 | case BPF_FUNC_get_ns_current_pid_tgid: | 1964 | case BPF_FUNC_get_ns_current_pid_tgid: |
| 1967 | return &bpf_get_ns_current_pid_tgid_proto; | 1965 | return &bpf_get_ns_current_pid_tgid_proto; |
| 1966 | case BPF_FUNC_get_current_uid_gid: | ||
| 1967 | return &bpf_get_current_uid_gid_proto; | ||
| 1968 | default: | 1968 | default: |
| 1969 | break; | 1969 | break; |
| 1970 | } | 1970 | } |
| @@ -2022,7 +2022,21 @@ bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 51 unmodified lines | |||
| 2022 | return &bpf_get_current_cgroup_id_proto; | 2022 | return &bpf_get_current_cgroup_id_proto; |
| 2023 | case BPF_FUNC_get_current_ancestor_cgroup_id: | 2023 | case BPF_FUNC_get_current_ancestor_cgroup_id: |
| 2024 | return &bpf_get_current_ancestor_cgroup_id_proto; | 2024 | return &bpf_get_current_ancestor_cgroup_id_proto; |
| 2025 | case BPF_FUNC_current_task_under_cgroup: | ||
| 2026 | return &bpf_current_task_under_cgroup_proto; | ||
| 2025 | #endif | 2027 | #endif |
| 2028 | #ifdef CONFIG_CGROUP_NET_CLASSID | ||
| 2029 | case BPF_FUNC_get_cgroup_classid: | ||
| 2030 | return &bpf_get_cgroup_classid_curr_proto; | ||
| 2031 | #endif | ||
| 2032 | case BPF_FUNC_task_storage_get: | ||
| 2033 | if (bpf_prog_check_recur(prog)) | ||
| 2034 | return &bpf_task_storage_get_recur_proto; | ||
| 2035 | return &bpf_task_storage_get_proto; | ||
| 2036 | case BPF_FUNC_task_storage_delete: | ||
| 2037 | if (bpf_prog_check_recur(prog)) | ||
| 2038 | return &bpf_task_storage_delete_recur_proto; | ||
| 2039 | return &bpf_task_storage_delete_proto; | ||
| 2026 | default: | 2040 | default: |
| 2027 | break; | 2041 | break; |
| 2028 | } | 2042 | } |
| @@ -2037,6 +2051,8 @@ bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 8 unmodified lines | |||
| 2037 | return &bpf_get_current_task_proto; | 2051 | return &bpf_get_current_task_proto; |
| 2038 | case BPF_FUNC_get_current_task_btf: | 2052 | case BPF_FUNC_get_current_task_btf: |
| 2039 | return &bpf_get_current_task_btf_proto; | 2053 | return &bpf_get_current_task_btf_proto; |
| 2054 | case BPF_FUNC_get_current_comm: | ||
| 2055 | return &bpf_get_current_comm_proto; | ||
| 2040 | case BPF_FUNC_probe_read_user: | 2056 | case BPF_FUNC_probe_read_user: |
| 2041 | return &bpf_probe_read_user_proto; | 2057 | return &bpf_probe_read_user_proto; |
| 2042 | case BPF_FUNC_probe_read_kernel: | 2058 | case BPF_FUNC_probe_read_kernel: |
| @@ -2047,6 +2063,10 @@ bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 4 unmodified lines | |||
| 2047 | case BPF_FUNC_probe_read_kernel_str: | 2063 | case BPF_FUNC_probe_read_kernel_str: |
| 2048 | return security_locked_down(LOCKDOWN_BPF_READ_KERNEL) < 0 ? | 2064 | return security_locked_down(LOCKDOWN_BPF_READ_KERNEL) < 0 ? |
| 2049 | NULL : &bpf_probe_read_kernel_str_proto; | 2065 | NULL : &bpf_probe_read_kernel_str_proto; |
| 2066 | case BPF_FUNC_copy_from_user: | ||
| 2067 | return &bpf_copy_from_user_proto; | ||
| 2068 | case BPF_FUNC_copy_from_user_task: | ||
| 2069 | return &bpf_copy_from_user_task_proto; | ||
| 2050 | case BPF_FUNC_snprintf_btf: | 2070 | case BPF_FUNC_snprintf_btf: |
| 2051 | return &bpf_snprintf_btf_proto; | 2071 | return &bpf_snprintf_btf_proto; |
| 2052 | case BPF_FUNC_snprintf: | 2072 | case BPF_FUNC_snprintf: |
| @@ -2057,6 +2077,19 @@ bpf_base_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 4 unmodified lines | |||
| 2057 | return bpf_get_trace_vprintk_proto(); | 2077 | return bpf_get_trace_vprintk_proto(); |
| 2058 | case BPF_FUNC_perf_event_read_value: | 2078 | case BPF_FUNC_perf_event_read_value: |
| 2059 | return bpf_get_perf_event_read_value_proto(); | 2079 | return bpf_get_perf_event_read_value_proto(); |
| 2080 | case BPF_FUNC_perf_event_read: | ||
| 2081 | return &bpf_perf_event_read_proto; | ||
| 2082 | case BPF_FUNC_send_signal: | ||
| 2083 | return &bpf_send_signal_proto; | ||
| 2084 | case BPF_FUNC_send_signal_thread: | ||
| 2085 | return &bpf_send_signal_thread_proto; | ||
| 2086 | case BPF_FUNC_get_task_stack: | ||
| 2087 | return prog->sleepable ? &bpf_get_task_stack_sleepable_proto | ||
| 2088 | : &bpf_get_task_stack_proto; | ||
| 2089 | case BPF_FUNC_get_branch_snapshot: | ||
| 2090 | return &bpf_get_branch_snapshot_proto; | ||
| 2091 | case BPF_FUNC_find_vma: | ||
| 2092 | return &bpf_find_vma_proto; | ||
| 2060 | default: | 2093 | default: |
| 2061 | return NULL; | 2094 | return NULL; |
| 2062 | } | 2095 | } |
| @@ -2293,6 +2326,26 @@ __bpf_kfunc struct bpf_list_node *bpf_list_pop_back(struct bpf_list_head *head) · 230 unmodified lines | |||
| 2293 | return __bpf_list_del(head, true); | 2326 | return __bpf_list_del(head, true); |
| 2294 | } | 2327 | } |
| 2295 | 2328 | ||
| 2329 | __bpf_kfunc struct bpf_list_node *bpf_list_front(struct bpf_list_head *head) | ||
| 2330 | { | ||
| 2331 | struct list_head *h = (struct list_head *)head; | ||
| 2332 | |||
| 2333 | if (list_empty(h) || unlikely(!h->next)) | ||
| 2334 | return NULL; | ||
| 2335 | |||
| 2336 | return (struct bpf_list_node *)h->next; | ||
| 2337 | } | ||
| 2338 | |||
| 2339 | __bpf_kfunc struct bpf_list_node *bpf_list_back(struct bpf_list_head *head) | ||
| 2340 | { | ||
| 2341 | struct list_head *h = (struct list_head *)head; | ||
| 2342 | |||
| 2343 | if (list_empty(h) || unlikely(!h->next)) | ||
| 2344 | return NULL; | ||
| 2345 | |||
| 2346 | return (struct bpf_list_node *)h->prev; | ||
| 2347 | } | ||
| 2348 | |||
| 2296 | __bpf_kfunc struct bpf_rb_node *bpf_rbtree_remove(struct bpf_rb_root *root, | 2349 | __bpf_kfunc struct bpf_rb_node *bpf_rbtree_remove(struct bpf_rb_root *root, |
| 2297 | struct bpf_rb_node *node) | 2350 | struct bpf_rb_node *node) |
| 2298 | { | 2351 | { |
| @@ -2366,6 +2419,33 @@ __bpf_kfunc struct bpf_rb_node *bpf_rbtree_first(struct bpf_rb_root *root) · 67 unmodified lines | |||
| 2366 | return (struct bpf_rb_node *)rb_first_cached(r); | 2419 | return (struct bpf_rb_node *)rb_first_cached(r); |
| 2367 | } | 2420 | } |
| 2368 | 2421 | ||
| 2422 | __bpf_kfunc struct bpf_rb_node *bpf_rbtree_root(struct bpf_rb_root *root) | ||
| 2423 | { | ||
| 2424 | struct rb_root_cached *r = (struct rb_root_cached *)root; | ||
| 2425 | |||
| 2426 | return (struct bpf_rb_node *)r->rb_root.rb_node; | ||
| 2427 | } | ||
| 2428 | |||
| 2429 | __bpf_kfunc struct bpf_rb_node *bpf_rbtree_left(struct bpf_rb_root *root, struct bpf_rb_node *node) | ||
| 2430 | { | ||
| 2431 | struct bpf_rb_node_kern *node_internal = (struct bpf_rb_node_kern *)node; | ||
| 2432 | |||
| 2433 | if (READ_ONCE(node_internal->owner) != root) | ||
| 2434 | return NULL; | ||
| 2435 | |||
| 2436 | return (struct bpf_rb_node *)node_internal->rb_node.rb_left; | ||
| 2437 | } | ||
| 2438 | |||
| 2439 | __bpf_kfunc struct bpf_rb_node *bpf_rbtree_right(struct bpf_rb_root *root, struct bpf_rb_node *node) | ||
| 2440 | { | ||
| 2441 | struct bpf_rb_node_kern *node_internal = (struct bpf_rb_node_kern *)node; | ||
| 2442 | |||
| 2443 | if (READ_ONCE(node_internal->owner) != root) | ||
| 2444 | return NULL; | ||
| 2445 | |||
| 2446 | return (struct bpf_rb_node *)node_internal->rb_node.rb_right; | ||
| 2447 | } | ||
| 2448 | |||
| 2369 | /** | 2449 | /** |
| 2370 | * bpf_task_acquire - Acquire a reference to a task. A task acquired by this | 2450 | * bpf_task_acquire - Acquire a reference to a task. A task acquired by this |
| 2371 | * kfunc which is not stored in a map as a kptr, must be released by calling | 2451 | * kfunc which is not stored in a map as a kptr, must be released by calling |
| @@ -2923,9 +3003,9 @@ __bpf_kfunc int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) · 551 unmodified lines | |||
| 2923 | __bpf_kfunc int bpf_wq_set_callback_impl(struct bpf_wq *wq, | 3003 | __bpf_kfunc int bpf_wq_set_callback_impl(struct bpf_wq *wq, |
| 2924 | int (callback_fn)(void *map, int *key, void *value), | 3004 | int (callback_fn)(void *map, int *key, void *value), |
| 2925 | unsigned int flags, | 3005 | unsigned int flags, |
| 2926 | void *aux__ign) | 3006 | void *aux__prog) |
| 2927 | { | 3007 | { |
| 2928 | struct bpf_prog_aux *aux = (struct bpf_prog_aux *)aux__ign; | 3008 | struct bpf_prog_aux *aux = (struct bpf_prog_aux *)aux__prog; |
| 2929 | struct bpf_async_kern *async = (struct bpf_async_kern *)wq; | 3009 | struct bpf_async_kern *async = (struct bpf_async_kern *)wq; |
| 2930 | 3010 | ||
| 2931 | if (flags) | 3011 | if (flags) |
| @@ -3194,6 +3274,10 @@ __bpf_kfunc void bpf_local_irq_restore(unsigned long *flags__irq_flag) · 262 unmodified lines | |||
| 3194 | local_irq_restore(*flags__irq_flag); | 3274 | local_irq_restore(*flags__irq_flag); |
| 3195 | } | 3275 | } |
| 3196 | 3276 | ||
| 3277 | __bpf_kfunc void __bpf_trap(void) | ||
| 3278 | { | ||
| 3279 | } | ||
| 3280 | |||
| 3197 | __bpf_kfunc_end_defs(); | 3281 | __bpf_kfunc_end_defs(); |
| 3198 | 3282 | ||
| 3199 | BTF_KFUNCS_START(generic_btf_ids) | 3283 | BTF_KFUNCS_START(generic_btf_ids) |
| @@ -3209,11 +3293,16 @@ BTF_ID_FLAGS(func, bpf_list_push_front_impl) · 9 unmodified lines | |||
| 3209 | BTF_ID_FLAGS(func, bpf_list_push_back_impl) | 3293 | BTF_ID_FLAGS(func, bpf_list_push_back_impl) |
| 3210 | BTF_ID_FLAGS(func, bpf_list_pop_front, KF_ACQUIRE | KF_RET_NULL) | 3294 | BTF_ID_FLAGS(func, bpf_list_pop_front, KF_ACQUIRE | KF_RET_NULL) |
| 3211 | BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL) | 3295 | BTF_ID_FLAGS(func, bpf_list_pop_back, KF_ACQUIRE | KF_RET_NULL) |
| 3296 | BTF_ID_FLAGS(func, bpf_list_front, KF_RET_NULL) | ||
| 3297 | BTF_ID_FLAGS(func, bpf_list_back, KF_RET_NULL) | ||
| 3212 | BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL) | 3298 | BTF_ID_FLAGS(func, bpf_task_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL) |
| 3213 | BTF_ID_FLAGS(func, bpf_task_release, KF_RELEASE) | 3299 | BTF_ID_FLAGS(func, bpf_task_release, KF_RELEASE) |
| 3214 | BTF_ID_FLAGS(func, bpf_rbtree_remove, KF_ACQUIRE | KF_RET_NULL) | 3300 | BTF_ID_FLAGS(func, bpf_rbtree_remove, KF_ACQUIRE | KF_RET_NULL) |
| 3215 | BTF_ID_FLAGS(func, bpf_rbtree_add_impl) | 3301 | BTF_ID_FLAGS(func, bpf_rbtree_add_impl) |
| 3216 | BTF_ID_FLAGS(func, bpf_rbtree_first, KF_RET_NULL) | 3302 | BTF_ID_FLAGS(func, bpf_rbtree_first, KF_RET_NULL) |
| 3303 | BTF_ID_FLAGS(func, bpf_rbtree_root, KF_RET_NULL) | ||
| 3304 | BTF_ID_FLAGS(func, bpf_rbtree_left, KF_RET_NULL) | ||
| 3305 | BTF_ID_FLAGS(func, bpf_rbtree_right, KF_RET_NULL) | ||
| 3217 | 3306 | ||
| 3218 | #ifdef CONFIG_CGROUPS | 3307 | #ifdef CONFIG_CGROUPS |
| 3219 | BTF_ID_FLAGS(func, bpf_cgroup_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL) | 3308 | BTF_ID_FLAGS(func, bpf_cgroup_acquire, KF_ACQUIRE | KF_RCU | KF_RET_NULL) |
| @@ -3294,6 +3383,20 @@ BTF_ID_FLAGS(func, bpf_iter_kmem_cache_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLE · 74 unmodified lines | |||
| 3294 | BTF_ID_FLAGS(func, bpf_iter_kmem_cache_destroy, KF_ITER_DESTROY | KF_SLEEPABLE) | 3383 | BTF_ID_FLAGS(func, bpf_iter_kmem_cache_destroy, KF_ITER_DESTROY | KF_SLEEPABLE) |
| 3295 | BTF_ID_FLAGS(func, bpf_local_irq_save) | 3384 | BTF_ID_FLAGS(func, bpf_local_irq_save) |
| 3296 | BTF_ID_FLAGS(func, bpf_local_irq_restore) | 3385 | BTF_ID_FLAGS(func, bpf_local_irq_restore) |
| 3386 | BTF_ID_FLAGS(func, bpf_probe_read_user_dynptr) | ||
| 3387 | BTF_ID_FLAGS(func, bpf_probe_read_kernel_dynptr) | ||
| 3388 | BTF_ID_FLAGS(func, bpf_probe_read_user_str_dynptr) | ||
| 3389 | BTF_ID_FLAGS(func, bpf_probe_read_kernel_str_dynptr) | ||
| 3390 | BTF_ID_FLAGS(func, bpf_copy_from_user_dynptr, KF_SLEEPABLE) | ||
| 3391 | BTF_ID_FLAGS(func, bpf_copy_from_user_str_dynptr, KF_SLEEPABLE) | ||
| 3392 | BTF_ID_FLAGS(func, bpf_copy_from_user_task_dynptr, KF_SLEEPABLE | KF_TRUSTED_ARGS) | ||
| 3393 | BTF_ID_FLAGS(func, bpf_copy_from_user_task_str_dynptr, KF_SLEEPABLE | KF_TRUSTED_ARGS) | ||
| 3394 | #ifdef CONFIG_DMA_SHARED_BUFFER | ||
| 3395 | BTF_ID_FLAGS(func, bpf_iter_dmabuf_new, KF_ITER_NEW | KF_SLEEPABLE) | ||
| 3396 | BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE) | ||
| 3397 | BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE) | ||
| 3398 | #endif | ||
| 3399 | BTF_ID_FLAGS(func, __bpf_trap) | ||
| 3297 | BTF_KFUNCS_END(common_btf_ids) | 3400 | BTF_KFUNCS_END(common_btf_ids) |
| 3298 | 3401 | ||
| 3299 | static const struct btf_kfunc_id_set common_kfunc_set = { | 3402 | static const struct btf_kfunc_id_set common_kfunc_set = { |
| @@ -36,6 +36,7 @@ · 35 unmodified lines | |||
| 36 | #include <linux/memcontrol.h> | 36 | #include <linux/memcontrol.h> |
| 37 | #include <linux/trace_events.h> | 37 | #include <linux/trace_events.h> |
| 38 | #include <linux/tracepoint.h> | 38 | #include <linux/tracepoint.h> |
| 39 | #include <linux/overflow.h> | ||
| 39 | 40 | ||
| 40 | #include <net/netfilter/nf_bpf_link.h> | 41 | #include <net/netfilter/nf_bpf_link.h> |
| 41 | #include <net/netkit.h> | 42 | #include <net/netkit.h> |
| @@ -693,7 +694,7 @@ struct btf_record *btf_record_dup(const struct btf_record *rec) · 651 unmodified lines | |||
| 693 | 694 | ||
| 694 | if (IS_ERR_OR_NULL(rec)) | 695 | if (IS_ERR_OR_NULL(rec)) |
| 695 | return NULL; | 696 | return NULL; |
| 696 | size = offsetof(struct btf_record, fields[rec->cnt]); | 697 | size = struct_size(rec, fields, rec->cnt); |
| 697 | new_rec = kmemdup(rec, size, GFP_KERNEL | __GFP_NOWARN); | 698 | new_rec = kmemdup(rec, size, GFP_KERNEL | __GFP_NOWARN); |
| 698 | if (!new_rec) | 699 | if (!new_rec) |
| 699 | return ERR_PTR(-ENOMEM); | 700 | return ERR_PTR(-ENOMEM); |
| @@ -748,7 +749,7 @@ bool btf_record_equal(const struct btf_record *rec_a, const struct btf_record *r · 48 unmodified lines | |||
| 748 | return false; | 749 | return false; |
| 749 | if (rec_a->cnt != rec_b->cnt) | 750 | if (rec_a->cnt != rec_b->cnt) |
| 750 | return false; | 751 | return false; |
| 751 | size = offsetof(struct btf_record, fields[rec_a->cnt]); | 752 | size = struct_size(rec_a, fields, rec_a->cnt); |
| 752 | /* btf_parse_fields uses kzalloc to allocate a btf_record, so unused | 753 | /* btf_parse_fields uses kzalloc to allocate a btf_record, so unused |
| 753 | * members are zeroed out. So memcmp is safe to do without worrying | 754 | * members are zeroed out. So memcmp is safe to do without worrying |
| 754 | * about padding/unused fields. | 755 | * about padding/unused fields. |
| @@ -3799,14 +3800,14 @@ static int bpf_perf_link_fill_kprobe(const struct perf_event *event, · 3044 unmodified lines | |||
| 3799 | static int bpf_perf_link_fill_uprobe(const struct perf_event *event, | 3800 | static int bpf_perf_link_fill_uprobe(const struct perf_event *event, |
| 3800 | struct bpf_link_info *info) | 3801 | struct bpf_link_info *info) |
| 3801 | { | 3802 | { |
| 3803 | u64 ref_ctr_offset, offset; | ||
| 3802 | char __user *uname; | 3804 | char __user *uname; |
| 3803 | u64 addr, offset; | ||
| 3804 | u32 ulen, type; | 3805 | u32 ulen, type; |
| 3805 | int err; | 3806 | int err; |
| 3806 | 3807 | ||
| 3807 | uname = u64_to_user_ptr(info->perf_event.uprobe.file_name); | 3808 | uname = u64_to_user_ptr(info->perf_event.uprobe.file_name); |
| 3808 | ulen = info->perf_event.uprobe.name_len; | 3809 | ulen = info->perf_event.uprobe.name_len; |
| 3809 | err = bpf_perf_link_fill_common(event, uname, &ulen, &offset, &addr, | 3810 | err = bpf_perf_link_fill_common(event, uname, &ulen, &offset, &ref_ctr_offset, |
| 3810 | &type, NULL); | 3811 | &type, NULL); |
| 3811 | if (err) | 3812 | if (err) |
| 3812 | return err; | 3813 | return err; |
| @@ -3818,6 +3819,7 @@ static int bpf_perf_link_fill_uprobe(const struct perf_event *event, · 5 unmodified lines | |||
| 3818 | info->perf_event.uprobe.name_len = ulen; | 3819 | info->perf_event.uprobe.name_len = ulen; |
| 3819 | info->perf_event.uprobe.offset = offset; | 3820 | info->perf_event.uprobe.offset = offset; |
| 3820 | info->perf_event.uprobe.cookie = event->bpf_cookie; | 3821 | info->perf_event.uprobe.cookie = event->bpf_cookie; |
| 3822 | info->perf_event.uprobe.ref_ctr_offset = ref_ctr_offset; | ||
| 3821 | return 0; | 3823 | return 0; |
| 3822 | } | 3824 | } |
| 3823 | #endif | 3825 | #endif |
| @@ -7,14 +7,46 @@ · 6 unmodified lines | |||
| 7 | #include <linux/kobject.h> | 7 | #include <linux/kobject.h> |
| 8 | #include <linux/init.h> | 8 | #include <linux/init.h> |
| 9 | #include <linux/sysfs.h> | 9 | #include <linux/sysfs.h> |
| 10 | #include <linux/mm.h> | ||
| 11 | #include <linux/io.h> | ||
| 12 | #include <linux/btf.h> | ||
| 10 | 13 | ||
| 11 | /* See scripts/link-vmlinux.sh, gen_btf() func for details */ | 14 | /* See scripts/link-vmlinux.sh, gen_btf() func for details */ |
| 12 | extern char __start_BTF[]; | 15 | extern char __start_BTF[]; |
| 13 | extern char __stop_BTF[]; | 16 | extern char __stop_BTF[]; |
| 14 | 17 | ||
| 18 | static int btf_sysfs_vmlinux_mmap(struct file *filp, struct kobject *kobj, | ||
| 19 | const struct bin_attribute *attr, | ||
| 20 | struct vm_area_struct *vma) | ||
| 21 | { | ||
| 22 | unsigned long pages = PAGE_ALIGN(attr->size) >> PAGE_SHIFT; | ||
| 23 | size_t vm_size = vma->vm_end - vma->vm_start; | ||
| 24 | phys_addr_t addr = virt_to_phys(__start_BTF); | ||
| 25 | unsigned long pfn = addr >> PAGE_SHIFT; | ||
| 26 | |||
| 27 | if (attr->private != __start_BTF || !PAGE_ALIGNED(addr)) | ||
| 28 | return -EINVAL; | ||
| 29 | |||
| 30 | if (vma->vm_pgoff) | ||
| 31 | return -EINVAL; | ||
| 32 | |||
| 33 | if (vma->vm_flags & (VM_WRITE | VM_EXEC | VM_MAYSHARE)) | ||
| 34 | return -EACCES; | ||
| 35 | |||
| 36 | if (pfn + pages < pfn) | ||
| 37 | return -EINVAL; | ||
| 38 | |||
| 39 | if ((vm_size >> PAGE_SHIFT) > pages) | ||
| 40 | return -EINVAL; | ||
| 41 | |||
| 42 | vm_flags_mod(vma, VM_DONTDUMP, VM_MAYEXEC | VM_MAYWRITE); | ||
| 43 | return remap_pfn_range(vma, vma->vm_start, pfn, vm_size, vma->vm_page_prot); | ||
| 44 | } | ||
| 45 | |||
| 15 | static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = { | 46 | static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = { |
| 16 | .attr = { .name = "vmlinux", .mode = 0444, }, | 47 | .attr = { .name = "vmlinux", .mode = 0444, }, |
| 17 | .read_new = sysfs_bin_attr_simple_read, | 48 | .read_new = sysfs_bin_attr_simple_read, |
| 49 | .mmap = btf_sysfs_vmlinux_mmap, | ||
| 18 | }; | 50 | }; |
| 19 | 51 | ||
| 20 | struct kobject *btf_kobj; | 52 | struct kobject *btf_kobj; |
| @@ -322,6 +322,7 @@ struct bpf_kfunc_call_arg_meta { · 321 unmodified lines | |||
| 322 | struct btf *arg_btf; | 322 | struct btf *arg_btf; |
| 323 | u32 arg_btf_id; | 323 | u32 arg_btf_id; |
| 324 | bool arg_owning_ref; | 324 | bool arg_owning_ref; |
| 325 | bool arg_prog; | ||
| 325 | 326 | ||
| 326 | struct { | 327 | struct { |
| 327 | struct btf_field *field; | 328 | struct btf_field *field; |
| @@ -1923,11 +1924,8 @@ static struct bpf_verifier_state *get_loop_entry(struct bpf_verifier_env *env, · 1595 unmodified lines | |||
| 1923 | u32 steps = 0; | 1924 | u32 steps = 0; |
| 1924 | 1925 | ||
| 1925 | while (topmost && topmost->loop_entry) { | 1926 | while (topmost && topmost->loop_entry) { |
| 1926 | if (steps++ > st->dfs_depth) { | 1927 | if (verifier_bug_if(steps++ > st->dfs_depth, env, "infinite loop")) |
| 1927 | WARN_ONCE(true, "verifier bug: infinite loop in get_loop_entry\n"); | ||
| 1928 | verbose(env, "verifier bug: infinite loop in get_loop_entry()\n"); | ||
| 1929 | return ERR_PTR(-EFAULT); | 1928 | return ERR_PTR(-EFAULT); |
| 1930 | } | ||
| 1931 | topmost = topmost->loop_entry; | 1929 | topmost = topmost->loop_entry; |
| 1932 | } | 1930 | } |
| 1933 | return topmost; | 1931 | return topmost; |
| @@ -3459,12 +3457,11 @@ static int mark_reg_read(struct bpf_verifier_env *env, · 1525 unmodified lines | |||
| 3459 | /* if read wasn't screened by an earlier write ... */ | 3457 | /* if read wasn't screened by an earlier write ... */ |
| 3460 | if (writes && state->live & REG_LIVE_WRITTEN) | 3458 | if (writes && state->live & REG_LIVE_WRITTEN) |
| 3461 | break; | 3459 | break; |
| 3462 | if (parent->live & REG_LIVE_DONE) { | 3460 | if (verifier_bug_if(parent->live & REG_LIVE_DONE, env, |
| 3463 | verbose(env, "verifier BUG type %s var_off %lld off %d\n", | 3461 | "type %s var_off %lld off %d", |
| 3464 | reg_type_str(env, parent->type), | 3462 | reg_type_str(env, parent->type), |
| 3465 | parent->var_off.value, parent->off); | 3463 | parent->var_off.value, parent->off)) |
| 3466 | return -EFAULT; | 3464 | return -EFAULT; |
| 3467 | } | ||
| 3468 | /* The first condition is more likely to be true than the | 3465 | /* The first condition is more likely to be true than the |
| 3469 | * second, checked it first. | 3466 | * second, checked it first. |
| 3470 | */ | 3467 | */ |
| @@ -3649,16 +3646,16 @@ static int insn_def_regno(const struct bpf_insn *insn) · 178 unmodified lines | |||
| 3649 | case BPF_ST: | 3646 | case BPF_ST: |
| 3650 | return -1; | 3647 | return -1; |
| 3651 | case BPF_STX: | 3648 | case BPF_STX: |
| 3652 | if ((BPF_MODE(insn->code) == BPF_ATOMIC || | 3649 | if (BPF_MODE(insn->code) == BPF_ATOMIC || |
| 3653 | BPF_MODE(insn->code) == BPF_PROBE_ATOMIC) && | 3650 | BPF_MODE(insn->code) == BPF_PROBE_ATOMIC) { |
| 3654 | (insn->imm & BPF_FETCH)) { | ||
| 3655 | if (insn->imm == BPF_CMPXCHG) | 3651 | if (insn->imm == BPF_CMPXCHG) |
| 3656 | return BPF_REG_0; | 3652 | return BPF_REG_0; |
| 3657 | else | 3653 | else if (insn->imm == BPF_LOAD_ACQ) |
| 3654 | return insn->dst_reg; | ||
| 3655 | else if (insn->imm & BPF_FETCH) | ||
| 3658 | return insn->src_reg; | 3656 | return insn->src_reg; |
| 3659 | } else { | ||
| 3660 | return -1; | ||
| 3661 | } | 3657 | } |
| 3658 | return -1; | ||
| 3662 | default: | 3659 | default: |
| 3663 | return insn->dst_reg; | 3660 | return insn->dst_reg; |
| 3664 | } | 3661 | } |
| @@ -3857,14 +3854,14 @@ static int push_insn_history(struct bpf_verifier_env *env, struct bpf_verifier_s · 192 unmodified lines | |||
| 3857 | /* atomic instructions push insn_flags twice, for READ and | 3854 | /* atomic instructions push insn_flags twice, for READ and |
| 3858 | * WRITE sides, but they should agree on stack slot | 3855 | * WRITE sides, but they should agree on stack slot |
| 3859 | */ | 3856 | */ |
| 3860 | WARN_ONCE((env->cur_hist_ent->flags & insn_flags) && | 3857 | verifier_bug_if((env->cur_hist_ent->flags & insn_flags) && |
| 3861 | (env->cur_hist_ent->flags & insn_flags) != insn_flags, | 3858 | (env->cur_hist_ent->flags & insn_flags) != insn_flags, |
| 3862 | "verifier insn history bug: insn_idx %d cur flags %x new flags %x\n", | 3859 | env, "insn history: insn_idx %d cur flags %x new flags %x", |
| 3863 | env->insn_idx, env->cur_hist_ent->flags, insn_flags); | 3860 | env->insn_idx, env->cur_hist_ent->flags, insn_flags); |
| 3864 | env->cur_hist_ent->flags |= insn_flags; | 3861 | env->cur_hist_ent->flags |= insn_flags; |
| 3865 | WARN_ONCE(env->cur_hist_ent->linked_regs != 0, | 3862 | verifier_bug_if(env->cur_hist_ent->linked_regs != 0, env, |
| 3866 | "verifier insn history bug: insn_idx %d linked_regs != 0: %#llx\n", | 3863 | "insn history: insn_idx %d linked_regs: %#llx", |
| 3867 | env->insn_idx, env->cur_hist_ent->linked_regs); | 3864 | env->insn_idx, env->cur_hist_ent->linked_regs); |
| 3868 | env->cur_hist_ent->linked_regs = linked_regs; | 3865 | env->cur_hist_ent->linked_regs = linked_regs; |
| 3869 | return 0; | 3866 | return 0; |
| 3870 | } | 3867 | } |
| @@ -3987,8 +3984,7 @@ static inline u32 bt_empty(struct backtrack_state *bt) · 116 unmodified lines | |||
| 3987 | static inline int bt_subprog_enter(struct backtrack_state *bt) | 3984 | static inline int bt_subprog_enter(struct backtrack_state *bt) |
| 3988 | { | 3985 | { |
| 3989 | if (bt->frame == MAX_CALL_FRAMES - 1) { | 3986 | if (bt->frame == MAX_CALL_FRAMES - 1) { |
| 3990 | verbose(bt->env, "BUG subprog enter from frame %d\n", bt->frame); | 3987 | verifier_bug(bt->env, "subprog enter from frame %d", bt->frame); |
| 3991 | WARN_ONCE(1, "verifier backtracking bug"); | ||
| 3992 | return -EFAULT; | 3988 | return -EFAULT; |
| 3993 | } | 3989 | } |
| 3994 | bt->frame++; | 3990 | bt->frame++; |
| @@ -3998,8 +3994,7 @@ static inline int bt_subprog_enter(struct backtrack_state *bt) · 3 unmodified lines | |||
| 3998 | static inline int bt_subprog_exit(struct backtrack_state *bt) | 3994 | static inline int bt_subprog_exit(struct backtrack_state *bt) |
| 3999 | { | 3995 | { |
| 4000 | if (bt->frame == 0) { | 3996 | if (bt->frame == 0) { |
| 4001 | verbose(bt->env, "BUG subprog exit from frame 0\n"); | 3997 | verifier_bug(bt->env, "subprog exit from frame 0"); |
| 4002 | WARN_ONCE(1, "verifier backtracking bug"); | ||
| 4003 | return -EFAULT; | 3998 | return -EFAULT; |
| 4004 | } | 3999 | } |
| 4005 | bt->frame--; | 4000 | bt->frame--; |
| @@ -4277,14 +4272,15 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, · 271 unmodified lines | |||
| 4277 | * should be literally next instruction in | 4272 | * should be literally next instruction in |
| 4278 | * caller program | 4273 | * caller program |
| 4279 | */ | 4274 | */ |
| 4280 | WARN_ONCE(idx + 1 != subseq_idx, "verifier backtracking bug"); | 4275 | verifier_bug_if(idx + 1 != subseq_idx, env, |
| 4276 | "extra insn from subprog"); | ||
| 4281 | /* r1-r5 are invalidated after subprog call, | 4277 | /* r1-r5 are invalidated after subprog call, |
| 4282 | * so for global func call it shouldn't be set | 4278 | * so for global func call it shouldn't be set |
| 4283 | * anymore | 4279 | * anymore |
| 4284 | */ | 4280 | */ |
| 4285 | if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) { | 4281 | if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) { |
| 4286 | verbose(env, "BUG regs %x\n", bt_reg_mask(bt)); | 4282 | verifier_bug(env, "global subprog unexpected regs %x", |
| 4287 | WARN_ONCE(1, "verifier backtracking bug"); | 4283 | bt_reg_mask(bt)); |
| 4288 | return -EFAULT; | 4284 | return -EFAULT; |
| 4289 | } | 4285 | } |
| 4290 | /* global subprog always sets R0 */ | 4286 | /* global subprog always sets R0 */ |
| @@ -4298,16 +4294,17 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, · 7 unmodified lines | |||
| 4298 | * the current frame should be zero by now | 4294 | * the current frame should be zero by now |
| 4299 | */ | 4295 | */ |
| 4300 | if (bt_reg_mask(bt) & ~BPF_REGMASK_ARGS) { | 4296 | if (bt_reg_mask(bt) & ~BPF_REGMASK_ARGS) { |
| 4301 | verbose(env, "BUG regs %x\n", bt_reg_mask(bt)); | 4297 | verifier_bug(env, "static subprog unexpected regs %x", |
| 4302 | WARN_ONCE(1, "verifier backtracking bug"); | 4298 | bt_reg_mask(bt)); |
| 4303 | return -EFAULT; | 4299 | return -EFAULT; |
| 4304 | } | 4300 | } |
| 4305 | /* we are now tracking register spills correctly, | 4301 | /* we are now tracking register spills correctly, |
| 4306 | * so any instance of leftover slots is a bug | 4302 | * so any instance of leftover slots is a bug |
| 4307 | */ | 4303 | */ |
| 4308 | if (bt_stack_mask(bt) != 0) { | 4304 | if (bt_stack_mask(bt) != 0) { |
| 4309 | verbose(env, "BUG stack slots %llx\n", bt_stack_mask(bt)); | 4305 | verifier_bug(env, |
| 4310 | WARN_ONCE(1, "verifier backtracking bug (subprog leftover stack slots)"); | 4306 | "static subprog leftover stack slots %llx", |
| 4307 | bt_stack_mask(bt)); | ||
| 4311 | return -EFAULT; | 4308 | return -EFAULT; |
| 4312 | } | 4309 | } |
| 4313 | /* propagate r1-r5 to the caller */ | 4310 | /* propagate r1-r5 to the caller */ |
| @@ -4330,13 +4327,13 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, · 16 unmodified lines | |||
| 4330 | * not actually arguments passed directly to callback subprogs | 4327 | * not actually arguments passed directly to callback subprogs |
| 4331 | */ | 4328 | */ |
| 4332 | if (bt_reg_mask(bt) & ~BPF_REGMASK_ARGS) { | 4329 | if (bt_reg_mask(bt) & ~BPF_REGMASK_ARGS) { |
| 4333 | verbose(env, "BUG regs %x\n", bt_reg_mask(bt)); | 4330 | verifier_bug(env, "callback unexpected regs %x", |
| 4334 | WARN_ONCE(1, "verifier backtracking bug"); | 4331 | bt_reg_mask(bt)); |
| 4335 | return -EFAULT; | 4332 | return -EFAULT; |
| 4336 | } | 4333 | } |
| 4337 | if (bt_stack_mask(bt) != 0) { | 4334 | if (bt_stack_mask(bt) != 0) { |
| 4338 | verbose(env, "BUG stack slots %llx\n", bt_stack_mask(bt)); | 4335 | verifier_bug(env, "callback leftover stack slots %llx", |
| 4339 | WARN_ONCE(1, "verifier backtracking bug (callback leftover stack slots)"); | 4336 | bt_stack_mask(bt)); |
| 4340 | return -EFAULT; | 4337 | return -EFAULT; |
| 4341 | } | 4338 | } |
| 4342 | /* clear r1-r5 in callback subprog's mask */ | 4339 | /* clear r1-r5 in callback subprog's mask */ |
| @@ -4355,11 +4352,11 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, · 12 unmodified lines | |||
| 4355 | /* regular helper call sets R0 */ | 4352 | /* regular helper call sets R0 */ |
| 4356 | bt_clear_reg(bt, BPF_REG_0); | 4353 | bt_clear_reg(bt, BPF_REG_0); |
| 4357 | if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) { | 4354 | if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) { |
| 4358 | /* if backtracing was looking for registers R1-R5 | 4355 | /* if backtracking was looking for registers R1-R5 |
| 4359 | * they should have been found already. | 4356 | * they should have been found already. |
| 4360 | */ | 4357 | */ |
| 4361 | verbose(env, "BUG regs %x\n", bt_reg_mask(bt)); | 4358 | verifier_bug(env, "backtracking call unexpected regs %x", |
| 4362 | WARN_ONCE(1, "verifier backtracking bug"); | 4359 | bt_reg_mask(bt)); |
| 4363 | return -EFAULT; | 4360 | return -EFAULT; |
| 4364 | } | 4361 | } |
| 4365 | } else if (opcode == BPF_EXIT) { | 4362 | } else if (opcode == BPF_EXIT) { |
| @@ -4377,8 +4374,8 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, · 11 unmodified lines | |||
| 4377 | for (i = BPF_REG_1; i <= BPF_REG_5; i++) | 4374 | for (i = BPF_REG_1; i <= BPF_REG_5; i++) |
| 4378 | bt_clear_reg(bt, i); | 4375 | bt_clear_reg(bt, i); |
| 4379 | if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) { | 4376 | if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) { |
| 4380 | verbose(env, "BUG regs %x\n", bt_reg_mask(bt)); | 4377 | verifier_bug(env, "backtracking exit unexpected regs %x", |
| 4381 | WARN_ONCE(1, "verifier backtracking bug"); | 4378 | bt_reg_mask(bt)); |
| 4382 | return -EFAULT; | 4379 | return -EFAULT; |
| 4383 | } | 4380 | } |
| 4384 | 4381 | ||
| @@ -4413,8 +4410,10 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, · 28 unmodified lines | |||
| 4413 | * before it would be equally necessary to | 4410 | * before it would be equally necessary to |
| 4414 | * propagate it to dreg. | 4411 | * propagate it to dreg. |
| 4415 | */ | 4412 | */ |
| 4416 | bt_set_reg(bt, dreg); | 4413 | if (!hist || !(hist->flags & INSN_F_SRC_REG_STACK)) |
| 4417 | bt_set_reg(bt, sreg); | 4414 | bt_set_reg(bt, sreg); |
| 4415 | if (!hist || !(hist->flags & INSN_F_DST_REG_STACK)) | ||
| 4416 | bt_set_reg(bt, dreg); | ||
| 4418 | } else if (BPF_SRC(insn->code) == BPF_K) { | 4417 | } else if (BPF_SRC(insn->code) == BPF_K) { |
| 4419 | /* dreg <cond> K | 4418 | /* dreg <cond> K |
| 4420 | * Only dreg still needs precision before | 4419 | * Only dreg still needs precision before |
| @@ -4719,9 +4718,8 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno) · 298 unmodified lines | |||
| 4719 | return 0; | 4718 | return 0; |
| 4720 | } | 4719 | } |
| 4721 | 4720 | ||
| 4722 | verbose(env, "BUG backtracking func entry subprog %d reg_mask %x stack_mask %llx\n", | 4721 | verifier_bug(env, "backtracking func entry subprog %d reg_mask %x stack_mask %llx", |
| 4723 | st->frame[0]->subprogno, bt_reg_mask(bt), bt_stack_mask(bt)); | 4722 | st->frame[0]->subprogno, bt_reg_mask(bt), bt_stack_mask(bt)); |
| 4724 | WARN_ONCE(1, "verifier backtracking bug"); | ||
| 4725 | return -EFAULT; | 4723 | return -EFAULT; |
| 4726 | } | 4724 | } |
| 4727 | 4725 | ||
| @@ -4757,8 +4755,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno) · 29 unmodified lines | |||
| 4757 | * It means the backtracking missed the spot where | 4755 | * It means the backtracking missed the spot where |
| 4758 | * particular register was initialized with a constant. | 4756 | * particular register was initialized with a constant. |
| 4759 | */ | 4757 | */ |
| 4760 | verbose(env, "BUG backtracking idx %d\n", i); | 4758 | verifier_bug(env, "backtracking idx %d", i); |
| 4761 | WARN_ONCE(1, "verifier backtracking bug"); | ||
| 4762 | return -EFAULT; | 4759 | return -EFAULT; |
| 4763 | } | 4760 | } |
| 4764 | } | 4761 | } |
| @@ -4783,12 +4780,10 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno) · 18 unmodified lines | |||
| 4783 | 4780 | ||
| 4784 | bitmap_from_u64(mask, bt_frame_stack_mask(bt, fr)); | 4781 | bitmap_from_u64(mask, bt_frame_stack_mask(bt, fr)); |
| 4785 | for_each_set_bit(i, mask, 64) { | 4782 | for_each_set_bit(i, mask, 64) { |
| 4786 | if (i >= func->allocated_stack / BPF_REG_SIZE) { | 4783 | if (verifier_bug_if(i >= func->allocated_stack / BPF_REG_SIZE, |
| 4787 | verbose(env, "BUG backtracking (stack slot %d, total slots %d)\n", | 4784 | env, "stack slot %d, total slots %d", |
| 4788 | i, func->allocated_stack / BPF_REG_SIZE); | 4785 | i, func->allocated_stack / BPF_REG_SIZE)) |
| 4789 | WARN_ONCE(1, "verifier backtracking bug (stack slot out of bounds)"); | ||
| 4790 | return -EFAULT; | 4786 | return -EFAULT; |
| 4791 | } | ||
| 4792 | 4787 | ||
| 4793 | if (!is_spilled_scalar_reg(&func->stack[i])) { | 4788 | if (!is_spilled_scalar_reg(&func->stack[i])) { |
| 4794 | bt_clear_frame_slot(bt, fr, i); | 4789 | bt_clear_frame_slot(bt, fr, i); |
| @@ -6561,21 +6556,18 @@ continue_func: · 1766 unmodified lines | |||
| 6561 | /* find the callee */ | 6556 | /* find the callee */ |
| 6562 | next_insn = i + insn[i].imm + 1; | 6557 | next_insn = i + insn[i].imm + 1; |
| 6563 | sidx = find_subprog(env, next_insn); | 6558 | sidx = find_subprog(env, next_insn); |
| 6564 | if (sidx < 0) { | 6559 | if (verifier_bug_if(sidx < 0, env, "callee not found at insn %d", next_insn)) |
| 6565 | WARN_ONCE(1, "verifier bug. No program starts at insn %d\n", | ||
| 6566 | next_insn); | ||
| 6567 | return -EFAULT; | 6560 | return -EFAULT; |
| 6568 | } | ||
| 6569 | if (subprog[sidx].is_async_cb) { | 6561 | if (subprog[sidx].is_async_cb) { |
| 6570 | if (subprog[sidx].has_tail_call) { | 6562 | if (subprog[sidx].has_tail_call) { |
| 6571 | verbose(env, "verifier bug. subprog has tail_call and async cb\n"); | 6563 | verifier_bug(env, "subprog has tail_call and async cb"); |
| 6572 | return -EFAULT; | 6564 | return -EFAULT; |
| 6573 | } | 6565 | } |
| 6574 | /* async callbacks don't increase bpf prog stack size unless called directly */ | 6566 | /* async callbacks don't increase bpf prog stack size unless called directly */ |
| 6575 | if (!bpf_pseudo_call(insn + i)) | 6567 | if (!bpf_pseudo_call(insn + i)) |
| 6576 | continue; | 6568 | continue; |
| 6577 | if (subprog[sidx].is_exception_cb) { | 6569 | if (subprog[sidx].is_exception_cb) { |
| 6578 | verbose(env, "insn %d cannot call exception cb directly\n", i); | 6570 | verbose(env, "insn %d cannot call exception cb directly", i); |
| 6579 | return -EINVAL; | 6571 | return -EINVAL; |
| 6580 | } | 6572 | } |
| 6581 | } | 6573 | } |
| @@ -6675,11 +6667,8 @@ static int get_callee_stack_depth(struct bpf_verifier_env *env, · 93 unmodified lines | |||
| 6675 | int start = idx + insn->imm + 1, subprog; | 6667 | int start = idx + insn->imm + 1, subprog; |
| 6676 | 6668 | ||
| 6677 | subprog = find_subprog(env, start); | 6669 | subprog = find_subprog(env, start); |
| 6678 | if (subprog < 0) { | 6670 | if (verifier_bug_if(subprog < 0, env, "get stack depth: no program at insn %d", start)) |
| 6679 | WARN_ONCE(1, "verifier bug. No program starts at insn %d\n", | ||
| 6680 | start); | ||
| 6681 | return -EFAULT; | 6671 | return -EFAULT; |
| 6682 | } | ||
| 6683 | return env->subprog_info[subprog].stack_depth; | 6672 | return env->subprog_info[subprog].stack_depth; |
| 6684 | } | 6673 | } |
| 6685 | #endif | 6674 | #endif |
| @@ -7984,7 +7973,7 @@ static int check_stack_range_initialized( · 1298 unmodified lines | |||
| 7984 | slot = -i - 1; | 7973 | slot = -i - 1; |
| 7985 | spi = slot / BPF_REG_SIZE; | 7974 | spi = slot / BPF_REG_SIZE; |
| 7986 | if (state->allocated_stack <= slot) { | 7975 | if (state->allocated_stack <= slot) { |
| 7987 | verbose(env, "verifier bug: allocated_stack too small\n"); | 7976 | verbose(env, "allocated_stack too small\n"); |
| 7988 | return -EFAULT; | 7977 | return -EFAULT; |
| 7989 | } | 7978 | } |
| 7990 | 7979 | ||
| @@ -8413,7 +8402,7 @@ static int process_timer_func(struct bpf_verifier_env *env, int regno, · 422 unmodified lines | |||
| 8413 | return -EINVAL; | 8402 | return -EINVAL; |
| 8414 | } | 8403 | } |
| 8415 | if (meta->map_ptr) { | 8404 | if (meta->map_ptr) { |
| 8416 | verbose(env, "verifier bug. Two map pointers in a timer helper\n"); | 8405 | verifier_bug(env, "Two map pointers in a timer helper"); |
| 8417 | return -EFAULT; | 8406 | return -EFAULT; |
| 8418 | } | 8407 | } |
| 8419 | meta->map_uid = reg->map_uid; | 8408 | meta->map_uid = reg->map_uid; |
| @@ -10285,8 +10274,7 @@ static int setup_func_entry(struct bpf_verifier_env *env, int subprog, int calls · 1865 unmodified lines | |||
| 10285 | } | 10274 | } |
| 10286 | 10275 | ||
| 10287 | if (state->frame[state->curframe + 1]) { | 10276 | if (state->frame[state->curframe + 1]) { |
| 10288 | verbose(env, "verifier bug. Frame %d already allocated\n", | 10277 | verifier_bug(env, "Frame %d already allocated", state->curframe + 1); |
| 10289 | state->curframe + 1); | ||
| 10290 | return -EFAULT; | 10278 | return -EFAULT; |
| 10291 | } | 10279 | } |
| 10292 | 10280 | ||
| @@ -10400,8 +10388,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog, · 107 unmodified lines | |||
| 10400 | if (err) | 10388 | if (err) |
| 10401 | return err; | 10389 | return err; |
| 10402 | } else { | 10390 | } else { |
| 10403 | bpf_log(log, "verifier bug: unrecognized arg#%d type %d\n", | 10391 | verifier_bug(env, "unrecognized arg#%d type %d", i, arg->arg_type); |
| 10404 | i, arg->arg_type); | ||
| 10405 | return -EFAULT; | 10392 | return -EFAULT; |
| 10406 | } | 10393 | } |
| 10407 | } | 10394 | } |
| @@ -10464,13 +10451,13 @@ static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *ins · 56 unmodified lines | |||
| 10464 | env->subprog_info[subprog].is_cb = true; | 10451 | env->subprog_info[subprog].is_cb = true; |
| 10465 | if (bpf_pseudo_kfunc_call(insn) && | 10452 | if (bpf_pseudo_kfunc_call(insn) && |
| 10466 | !is_callback_calling_kfunc(insn->imm)) { | 10453 | !is_callback_calling_kfunc(insn->imm)) { |
| 10467 | verbose(env, "verifier bug: kfunc %s#%d not marked as callback-calling\n", | 10454 | verifier_bug(env, "kfunc %s#%d not marked as callback-calling", |
| 10468 | func_id_name(insn->imm), insn->imm); | 10455 | func_id_name(insn->imm), insn->imm); |
| 10469 | return -EFAULT; | 10456 | return -EFAULT; |
| 10470 | } else if (!bpf_pseudo_kfunc_call(insn) && | 10457 | } else if (!bpf_pseudo_kfunc_call(insn) && |
| 10471 | !is_callback_calling_function(insn->imm)) { /* helper */ | 10458 | !is_callback_calling_function(insn->imm)) { /* helper */ |
| 10472 | verbose(env, "verifier bug: helper %s#%d not marked as callback-calling\n", | 10459 | verifier_bug(env, "helper %s#%d not marked as callback-calling", |
| 10473 | func_id_name(insn->imm), insn->imm); | 10460 | func_id_name(insn->imm), insn->imm); |
| 10474 | return -EFAULT; | 10461 | return -EFAULT; |
| 10475 | } | 10462 | } |
| 10476 | 10463 | ||
| @@ -10522,10 +10509,9 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, · 45 unmodified lines | |||
| 10522 | 10509 | ||
| 10523 | target_insn = *insn_idx + insn->imm + 1; | 10510 | target_insn = *insn_idx + insn->imm + 1; |
| 10524 | subprog = find_subprog(env, target_insn); | 10511 | subprog = find_subprog(env, target_insn); |
| 10525 | if (subprog < 0) { | 10512 | if (verifier_bug_if(subprog < 0, env, "target of func call at insn %d is not a program", |
| 10526 | verbose(env, "verifier bug. No program starts at insn %d\n", target_insn); | 10513 | target_insn)) |
| 10527 | return -EFAULT; | 10514 | return -EFAULT; |
| 10528 | } | ||
| 10529 | 10515 | ||
| 10530 | caller = state->frame[state->curframe]; | 10516 | caller = state->frame[state->curframe]; |
| 10531 | err = btf_check_subprog_call(env, subprog, caller->regs); | 10517 | err = btf_check_subprog_call(env, subprog, caller->regs); |
| @@ -11124,7 +11110,7 @@ static int check_bpf_snprintf_call(struct bpf_verifier_env *env, · 592 unmodified lines | |||
| 11124 | err = fmt_map->ops->map_direct_value_addr(fmt_map, &fmt_addr, | 11110 | err = fmt_map->ops->map_direct_value_addr(fmt_map, &fmt_addr, |
| 11125 | fmt_map_off); | 11111 | fmt_map_off); |
| 11126 | if (err) { | 11112 | if (err) { |
| 11127 | verbose(env, "verifier bug\n"); | 11113 | verbose(env, "failed to retrieve map value address\n"); |
| 11128 | return -EFAULT; | 11114 | return -EFAULT; |
| 11129 | } | 11115 | } |
| 11130 | fmt = (char *)(long)fmt_addr + fmt_map_off; | 11116 | fmt = (char *)(long)fmt_addr + fmt_map_off; |
| @@ -11897,6 +11883,11 @@ static bool is_kfunc_arg_irq_flag(const struct btf *btf, const struct btf_param · 766 unmodified lines | |||
| 11897 | return btf_param_match_suffix(btf, arg, "__irq_flag"); | 11883 | return btf_param_match_suffix(btf, arg, "__irq_flag"); |
| 11898 | } | 11884 | } |
| 11899 | 11885 | ||
| 11886 | static bool is_kfunc_arg_prog(const struct btf *btf, const struct btf_param *arg) | ||
| 11887 | { | ||
| 11888 | return btf_param_match_suffix(btf, arg, "__prog"); | ||
| 11889 | } | ||
| 11890 | |||
| 11900 | static bool is_kfunc_arg_scalar_with_name(const struct btf *btf, | 11891 | static bool is_kfunc_arg_scalar_with_name(const struct btf *btf, |
| 11901 | const struct btf_param *arg, | 11892 | const struct btf_param *arg, |
| 11902 | const char *name) | 11893 | const char *name) |
| @@ -11987,6 +11978,16 @@ static bool is_kfunc_arg_res_spin_lock(const struct btf *btf, const struct btf_p · 84 unmodified lines | |||
| 11987 | return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_RES_SPIN_LOCK_ID); | 11978 | return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_RES_SPIN_LOCK_ID); |
| 11988 | } | 11979 | } |
| 11989 | 11980 | ||
| 11981 | static bool is_rbtree_node_type(const struct btf_type *t) | ||
| 11982 | { | ||
| 11983 | return t == btf_type_by_id(btf_vmlinux, kf_arg_btf_ids[KF_ARG_RB_NODE_ID]); | ||
| 11984 | } | ||
| 11985 | |||
| 11986 | static bool is_list_node_type(const struct btf_type *t) | ||
| 11987 | { | ||
| 11988 | return t == btf_type_by_id(btf_vmlinux, kf_arg_btf_ids[KF_ARG_LIST_NODE_ID]); | ||
| 11989 | } | ||
| 11990 | |||
| 11990 | static bool is_kfunc_arg_callback(struct bpf_verifier_env *env, const struct btf *btf, | 11991 | static bool is_kfunc_arg_callback(struct bpf_verifier_env *env, const struct btf *btf, |
| 11991 | const struct btf_param *arg) | 11992 | const struct btf_param *arg) |
| 11992 | { | 11993 | { |
| @@ -12069,6 +12070,8 @@ enum special_kfunc_type { · 76 unmodified lines | |||
| 12069 | KF_bpf_list_push_back_impl, | 12070 | KF_bpf_list_push_back_impl, |
| 12070 | KF_bpf_list_pop_front, | 12071 | KF_bpf_list_pop_front, |
| 12071 | KF_bpf_list_pop_back, | 12072 | KF_bpf_list_pop_back, |
| 12073 | KF_bpf_list_front, | ||
| 12074 | KF_bpf_list_back, | ||
| 12072 | KF_bpf_cast_to_kern_ctx, | 12075 | KF_bpf_cast_to_kern_ctx, |
| 12073 | KF_bpf_rdonly_cast, | 12076 | KF_bpf_rdonly_cast, |
| 12074 | KF_bpf_rcu_read_lock, | 12077 | KF_bpf_rcu_read_lock, |
| @@ -12076,6 +12079,9 @@ enum special_kfunc_type { · 1 unmodified line | |||
| 12076 | KF_bpf_rbtree_remove, | 12079 | KF_bpf_rbtree_remove, |
| 12077 | KF_bpf_rbtree_add_impl, | 12080 | KF_bpf_rbtree_add_impl, |
| 12078 | KF_bpf_rbtree_first, | 12081 | KF_bpf_rbtree_first, |
| 12082 | KF_bpf_rbtree_root, | ||
| 12083 | KF_bpf_rbtree_left, | ||
| 12084 | KF_bpf_rbtree_right, | ||
| 12079 | KF_bpf_dynptr_from_skb, | 12085 | KF_bpf_dynptr_from_skb, |
| 12080 | KF_bpf_dynptr_from_xdp, | 12086 | KF_bpf_dynptr_from_xdp, |
| 12081 | KF_bpf_dynptr_slice, | 12087 | KF_bpf_dynptr_slice, |
| @@ -12101,41 +12107,9 @@ enum special_kfunc_type { · 19 unmodified lines | |||
| 12101 | KF_bpf_res_spin_unlock, | 12107 | KF_bpf_res_spin_unlock, |
| 12102 | KF_bpf_res_spin_lock_irqsave, | 12108 | KF_bpf_res_spin_lock_irqsave, |
| 12103 | KF_bpf_res_spin_unlock_irqrestore, | 12109 | KF_bpf_res_spin_unlock_irqrestore, |
| 12110 | KF___bpf_trap, | ||
| 12104 | }; | 12111 | }; |
| 12105 | 12112 | ||
| 12106 | BTF_SET_START(special_kfunc_set) | ||
| 12107 | BTF_ID(func, bpf_obj_new_impl) | ||
| 12108 | BTF_ID(func, bpf_obj_drop_impl) | ||
| 12109 | BTF_ID(func, bpf_refcount_acquire_impl) | ||
| 12110 | BTF_ID(func, bpf_list_push_front_impl) | ||
| 12111 | BTF_ID(func, bpf_list_push_back_impl) | ||
| 12112 | BTF_ID(func, bpf_list_pop_front) | ||
| 12113 | BTF_ID(func, bpf_list_pop_back) | ||
| 12114 | BTF_ID(func, bpf_cast_to_kern_ctx) | ||
| 12115 | BTF_ID(func, bpf_rdonly_cast) | ||
| 12116 | BTF_ID(func, bpf_rbtree_remove) | ||
| 12117 | BTF_ID(func, bpf_rbtree_add_impl) | ||
| 12118 | BTF_ID(func, bpf_rbtree_first) | ||
| 12119 | #ifdef CONFIG_NET | ||
| 12120 | BTF_ID(func, bpf_dynptr_from_skb) | ||
| 12121 | BTF_ID(func, bpf_dynptr_from_xdp) | ||
| 12122 | #endif | ||
| 12123 | BTF_ID(func, bpf_dynptr_slice) | ||
| 12124 | BTF_ID(func, bpf_dynptr_slice_rdwr) | ||
| 12125 | BTF_ID(func, bpf_dynptr_clone) | ||
| 12126 | BTF_ID(func, bpf_percpu_obj_new_impl) | ||
| 12127 | BTF_ID(func, bpf_percpu_obj_drop_impl) | ||
| 12128 | BTF_ID(func, bpf_throw) | ||
| 12129 | BTF_ID(func, bpf_wq_set_callback_impl) | ||
| 12130 | #ifdef CONFIG_CGROUPS | ||
| 12131 | BTF_ID(func, bpf_iter_css_task_new) | ||
| 12132 | #endif | ||
| 12133 | #ifdef CONFIG_BPF_LSM | ||
| 12134 | BTF_ID(func, bpf_set_dentry_xattr) | ||
| 12135 | BTF_ID(func, bpf_remove_dentry_xattr) | ||
| 12136 | #endif | ||
| 12137 | BTF_SET_END(special_kfunc_set) | ||
| 12138 | |||
| 12139 | BTF_ID_LIST(special_kfunc_list) | 12113 | BTF_ID_LIST(special_kfunc_list) |
| 12140 | BTF_ID(func, bpf_obj_new_impl) | 12114 | BTF_ID(func, bpf_obj_new_impl) |
| 12141 | BTF_ID(func, bpf_obj_drop_impl) | 12115 | BTF_ID(func, bpf_obj_drop_impl) |
| @@ -12144,6 +12118,8 @@ BTF_ID(func, bpf_list_push_front_impl) · 2 unmodified lines | |||
| 12144 | BTF_ID(func, bpf_list_push_back_impl) | 12118 | BTF_ID(func, bpf_list_push_back_impl) |
| 12145 | BTF_ID(func, bpf_list_pop_front) | 12119 | BTF_ID(func, bpf_list_pop_front) |
| 12146 | BTF_ID(func, bpf_list_pop_back) | 12120 | BTF_ID(func, bpf_list_pop_back) |
| 12121 | BTF_ID(func, bpf_list_front) | ||
| 12122 | BTF_ID(func, bpf_list_back) | ||
| 12147 | BTF_ID(func, bpf_cast_to_kern_ctx) | 12123 | BTF_ID(func, bpf_cast_to_kern_ctx) |
| 12148 | BTF_ID(func, bpf_rdonly_cast) | 12124 | BTF_ID(func, bpf_rdonly_cast) |
| 12149 | BTF_ID(func, bpf_rcu_read_lock) | 12125 | BTF_ID(func, bpf_rcu_read_lock) |
| @@ -12151,6 +12127,9 @@ BTF_ID(func, bpf_rcu_read_unlock) · 1 unmodified line | |||
| 12151 | BTF_ID(func, bpf_rbtree_remove) | 12127 | BTF_ID(func, bpf_rbtree_remove) |
| 12152 | BTF_ID(func, bpf_rbtree_add_impl) | 12128 | BTF_ID(func, bpf_rbtree_add_impl) |
| 12153 | BTF_ID(func, bpf_rbtree_first) | 12129 | BTF_ID(func, bpf_rbtree_first) |
| 12130 | BTF_ID(func, bpf_rbtree_root) | ||
| 12131 | BTF_ID(func, bpf_rbtree_left) | ||
| 12132 | BTF_ID(func, bpf_rbtree_right) | ||
| 12154 | #ifdef CONFIG_NET | 12133 | #ifdef CONFIG_NET |
| 12155 | BTF_ID(func, bpf_dynptr_from_skb) | 12134 | BTF_ID(func, bpf_dynptr_from_skb) |
| 12156 | BTF_ID(func, bpf_dynptr_from_xdp) | 12135 | BTF_ID(func, bpf_dynptr_from_xdp) |
| @@ -12194,6 +12173,7 @@ BTF_ID(func, bpf_res_spin_lock) · 37 unmodified lines | |||
| 12194 | BTF_ID(func, bpf_res_spin_unlock) | 12173 | BTF_ID(func, bpf_res_spin_unlock) |
| 12195 | BTF_ID(func, bpf_res_spin_lock_irqsave) | 12174 | BTF_ID(func, bpf_res_spin_lock_irqsave) |
| 12196 | BTF_ID(func, bpf_res_spin_unlock_irqrestore) | 12175 | BTF_ID(func, bpf_res_spin_unlock_irqrestore) |
| 12176 | BTF_ID(func, __bpf_trap) | ||
| 12197 | 12177 | ||
| 12198 | static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta) | 12178 | static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta) |
| 12199 | { | 12179 | { |
| @@ -12579,14 +12559,19 @@ static bool is_bpf_list_api_kfunc(u32 btf_id) · 379 unmodified lines | |||
| 12579 | return btf_id == special_kfunc_list[KF_bpf_list_push_front_impl] || | 12559 | return btf_id == special_kfunc_list[KF_bpf_list_push_front_impl] || |
| 12580 | btf_id == special_kfunc_list[KF_bpf_list_push_back_impl] || | 12560 | btf_id == special_kfunc_list[KF_bpf_list_push_back_impl] || |
| 12581 | btf_id == special_kfunc_list[KF_bpf_list_pop_front] || | 12561 | btf_id == special_kfunc_list[KF_bpf_list_pop_front] || |
| 12582 | btf_id == special_kfunc_list[KF_bpf_list_pop_back]; | 12562 | btf_id == special_kfunc_list[KF_bpf_list_pop_back] || |
| 12563 | btf_id == special_kfunc_list[KF_bpf_list_front] || | ||
| 12564 | btf_id == special_kfunc_list[KF_bpf_list_back]; | ||
| 12583 | } | 12565 | } |
| 12584 | 12566 | ||
| 12585 | static bool is_bpf_rbtree_api_kfunc(u32 btf_id) | 12567 | static bool is_bpf_rbtree_api_kfunc(u32 btf_id) |
| 12586 | { | 12568 | { |
| 12587 | return btf_id == special_kfunc_list[KF_bpf_rbtree_add_impl] || | 12569 | return btf_id == special_kfunc_list[KF_bpf_rbtree_add_impl] || |
| 12588 | btf_id == special_kfunc_list[KF_bpf_rbtree_remove] || | 12570 | btf_id == special_kfunc_list[KF_bpf_rbtree_remove] || |
| 12589 | btf_id == special_kfunc_list[KF_bpf_rbtree_first]; | 12571 | btf_id == special_kfunc_list[KF_bpf_rbtree_first] || |
| 12572 | btf_id == special_kfunc_list[KF_bpf_rbtree_root] || | ||
| 12573 | btf_id == special_kfunc_list[KF_bpf_rbtree_left] || | ||
| 12574 | btf_id == special_kfunc_list[KF_bpf_rbtree_right]; | ||
| 12590 | } | 12575 | } |
| 12591 | 12576 | ||
| 12592 | static bool is_bpf_iter_num_api_kfunc(u32 btf_id) | 12577 | static bool is_bpf_iter_num_api_kfunc(u32 btf_id) |
| @@ -12686,7 +12671,9 @@ static bool check_kfunc_is_graph_node_api(struct bpf_verifier_env *env, · 93 unmodified lines | |||
| 12686 | break; | 12671 | break; |
| 12687 | case BPF_RB_NODE: | 12672 | case BPF_RB_NODE: |
| 12688 | ret = (kfunc_btf_id == special_kfunc_list[KF_bpf_rbtree_remove] || | 12673 | ret = (kfunc_btf_id == special_kfunc_list[KF_bpf_rbtree_remove] || |
| 12689 | kfunc_btf_id == special_kfunc_list[KF_bpf_rbtree_add_impl]); | 12674 | kfunc_btf_id == special_kfunc_list[KF_bpf_rbtree_add_impl] || |
| 12675 | kfunc_btf_id == special_kfunc_list[KF_bpf_rbtree_left] || | ||
| 12676 | kfunc_btf_id == special_kfunc_list[KF_bpf_rbtree_right]); | ||
| 12690 | break; | 12677 | break; |
| 12691 | default: | 12678 | default: |
| 12692 | verbose(env, "verifier internal error: unexpected graph node argument type %s\n", | 12679 | verbose(env, "verifier internal error: unexpected graph node argument type %s\n", |
| @@ -12906,6 +12893,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ · 213 unmodified lines | |||
| 12906 | if (is_kfunc_arg_ignore(btf, &args[i])) | 12893 | if (is_kfunc_arg_ignore(btf, &args[i])) |
| 12907 | continue; | 12894 | continue; |
| 12908 | 12895 | ||
| 12896 | if (is_kfunc_arg_prog(btf, &args[i])) { | ||
| 12897 | /* Used to reject repeated use of __prog. */ | ||
| 12898 | if (meta->arg_prog) { | ||
| 12899 | verbose(env, "Only 1 prog->aux argument supported per-kfunc\n"); | ||
| 12900 | return -EFAULT; | ||
| 12901 | } | ||
| 12902 | meta->arg_prog = true; | ||
| 12903 | cur_aux(env)->arg_prog = regno; | ||
| 12904 | continue; | ||
| 12905 | } | ||
| 12906 | |||
| 12909 | if (btf_type_is_scalar(t)) { | 12907 | if (btf_type_is_scalar(t)) { |
| 12910 | if (reg->type != SCALAR_VALUE) { | 12908 | if (reg->type != SCALAR_VALUE) { |
| 12911 | verbose(env, "R%d is not a scalar\n", regno); | 12909 | verbose(env, "R%d is not a scalar\n", regno); |
| @@ -13200,22 +13198,22 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_ · 288 unmodified lines | |||
| 13200 | return ret; | 13198 | return ret; |
| 13201 | break; | 13199 | break; |
| 13202 | case KF_ARG_PTR_TO_RB_NODE: | 13200 | case KF_ARG_PTR_TO_RB_NODE: |
| 13203 | if (meta->func_id == special_kfunc_list[KF_bpf_rbtree_remove]) { | 13201 | if (meta->func_id == special_kfunc_list[KF_bpf_rbtree_add_impl]) { |
| 13204 | if (!type_is_non_owning_ref(reg->type) || reg->ref_obj_id) { | 13202 | if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { |
| 13205 | verbose(env, "rbtree_remove node input must be non-owning ref\n"); | 13203 | verbose(env, "arg#%d expected pointer to allocated object\n", i); |
| 13206 | return -EINVAL; | 13204 | return -EINVAL; |
| 13207 | } | 13205 | } |
| 13208 | if (in_rbtree_lock_required_cb(env)) { | 13206 | if (!reg->ref_obj_id) { |
| 13209 | verbose(env, "rbtree_remove not allowed in rbtree cb\n"); | 13207 | verbose(env, "allocated object must be referenced\n"); |
| 13210 | return -EINVAL; | 13208 | return -EINVAL; |
| 13211 | } | 13209 | } |
| 13212 | } else { | 13210 | } else { |
| 13213 | if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) { | 13211 | if (!type_is_non_owning_ref(reg->type) && !reg->ref_obj_id) { |
| 13214 | verbose(env, "arg#%d expected pointer to allocated object\n", i); | 13212 | verbose(env, "%s can only take non-owning or refcounted bpf_rb_node pointer\n", func_name); |
| 13215 | return -EINVAL; | 13213 | return -EINVAL; |
| 13216 | } | 13214 | } |
| 13217 | if (!reg->ref_obj_id) { | 13215 | if (in_rbtree_lock_required_cb(env)) { |
| 13218 | verbose(env, "allocated object must be referenced\n"); | 13216 | verbose(env, "%s not allowed in rbtree cb\n", func_name); |
| 13219 | return -EINVAL; | 13217 | return -EINVAL; |
| 13220 | } | 13218 | } |
| 13221 | } | 13219 | } |
| @@ -13420,6 +13418,178 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env, · 198 unmodified lines | |||
| 13420 | return 0; | 13418 | return 0; |
| 13421 | } | 13419 | } |
| 13422 | 13420 | ||
| 13421 | /* check special kfuncs and return: | ||
| 13422 | * 1 - not fall-through to 'else' branch, continue verification | ||
| 13423 | * 0 - fall-through to 'else' branch | ||
| 13424 | * < 0 - not fall-through to 'else' branch, return error | ||
| 13425 | */ | ||
| 13426 | static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta, | ||
| 13427 | struct bpf_reg_state *regs, struct bpf_insn_aux_data *insn_aux, | ||
| 13428 | const struct btf_type *ptr_type, struct btf *desc_btf) | ||
| 13429 | { | ||
| 13430 | const struct btf_type *ret_t; | ||
| 13431 | int err = 0; | ||
| 13432 | |||
| 13433 | if (meta->btf != btf_vmlinux) | ||
| 13434 | return 0; | ||
| 13435 | |||
| 13436 | if (meta->func_id == special_kfunc_list[KF_bpf_obj_new_impl] || | ||
| 13437 | meta->func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) { | ||
| 13438 | struct btf_struct_meta *struct_meta; | ||
| 13439 | struct btf *ret_btf; | ||
| 13440 | u32 ret_btf_id; | ||
| 13441 | |||
| 13442 | if (meta->func_id == special_kfunc_list[KF_bpf_obj_new_impl] && !bpf_global_ma_set) | ||
| 13443 | return -ENOMEM; | ||
| 13444 | |||
| 13445 | if (((u64)(u32)meta->arg_constant.value) != meta->arg_constant.value) { | ||
| 13446 | verbose(env, "local type ID argument must be in range [0, U32_MAX]\n"); | ||
| 13447 | return -EINVAL; | ||
| 13448 | } | ||
| 13449 | |||
| 13450 | ret_btf = env->prog->aux->btf; | ||
| 13451 | ret_btf_id = meta->arg_constant.value; | ||
| 13452 | |||
| 13453 | /* This may be NULL due to user not supplying a BTF */ | ||
| 13454 | if (!ret_btf) { | ||
| 13455 | verbose(env, "bpf_obj_new/bpf_percpu_obj_new requires prog BTF\n"); | ||
| 13456 | return -EINVAL; | ||
| 13457 | } | ||
| 13458 | |||
| 13459 | ret_t = btf_type_by_id(ret_btf, ret_btf_id); | ||
| 13460 | if (!ret_t || !__btf_type_is_struct(ret_t)) { | ||
| 13461 | verbose(env, "bpf_obj_new/bpf_percpu_obj_new type ID argument must be of a struct\n"); | ||
| 13462 | return -EINVAL; | ||
| 13463 | } | ||
| 13464 | |||
| 13465 | if (meta->func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) { | ||
| 13466 | if (ret_t->size > BPF_GLOBAL_PERCPU_MA_MAX_SIZE) { | ||
| 13467 | verbose(env, "bpf_percpu_obj_new type size (%d) is greater than %d\n", | ||
| 13468 | ret_t->size, BPF_GLOBAL_PERCPU_MA_MAX_SIZE); | ||
| 13469 | return -EINVAL; | ||
| 13470 | } | ||
| 13471 | |||
| 13472 | if (!bpf_global_percpu_ma_set) { | ||
| 13473 | mutex_lock(&bpf_percpu_ma_lock); | ||
| 13474 | if (!bpf_global_percpu_ma_set) { | ||
| 13475 | /* Charge memory allocated with bpf_global_percpu_ma to | ||
| 13476 | * root memcg. The obj_cgroup for root memcg is NULL. | ||
| 13477 | */ | ||
| 13478 | err = bpf_mem_alloc_percpu_init(&bpf_global_percpu_ma, NULL); | ||
| 13479 | if (!err) | ||
| 13480 | bpf_global_percpu_ma_set = true; | ||
| 13481 | } | ||
| 13482 | mutex_unlock(&bpf_percpu_ma_lock); | ||
| 13483 | if (err) | ||
| 13484 | return err; | ||
| 13485 | } | ||
| 13486 | |||
| 13487 | mutex_lock(&bpf_percpu_ma_lock); | ||
| 13488 | err = bpf_mem_alloc_percpu_unit_init(&bpf_global_percpu_ma, ret_t->size); | ||
| 13489 | mutex_unlock(&bpf_percpu_ma_lock); | ||
| 13490 | if (err) | ||
| 13491 | return err; | ||
| 13492 | } | ||
| 13493 | |||
| 13494 | struct_meta = btf_find_struct_meta(ret_btf, ret_btf_id); | ||
| 13495 | if (meta->func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) { | ||
| 13496 | if (!__btf_type_is_scalar_struct(env, ret_btf, ret_t, 0)) { | ||
| 13497 | verbose(env, "bpf_percpu_obj_new type ID argument must be of a struct of scalars\n"); | ||
| 13498 | return -EINVAL; | ||
| 13499 | } | ||
| 13500 | |||
| 13501 | if (struct_meta) { | ||
| 13502 | verbose(env, "bpf_percpu_obj_new type ID argument must not contain special fields\n"); | ||
| 13503 | return -EINVAL; | ||
| 13504 | } | ||
| 13505 | } | ||
| 13506 | |||
| 13507 | mark_reg_known_zero(env, regs, BPF_REG_0); | ||
| 13508 | regs[BPF_REG_0].type = PTR_TO_BTF_ID | MEM_ALLOC; | ||
| 13509 | regs[BPF_REG_0].btf = ret_btf; | ||
| 13510 | regs[BPF_REG_0].btf_id = ret_btf_id; | ||
| 13511 | if (meta->func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) | ||
| 13512 | regs[BPF_REG_0].type |= MEM_PERCPU; | ||
| 13513 | |||
| 13514 | insn_aux->obj_new_size = ret_t->size; | ||
| 13515 | insn_aux->kptr_struct_meta = struct_meta; | ||
| 13516 | } else if (meta->func_id == special_kfunc_list[KF_bpf_refcount_acquire_impl]) { | ||
| 13517 | mark_reg_known_zero(env, regs, BPF_REG_0); | ||
| 13518 | regs[BPF_REG_0].type = PTR_TO_BTF_ID | MEM_ALLOC; | ||
| 13519 | regs[BPF_REG_0].btf = meta->arg_btf; | ||
| 13520 | regs[BPF_REG_0].btf_id = meta->arg_btf_id; | ||
| 13521 | |||
| 13522 | insn_aux->kptr_struct_meta = | ||
| 13523 | btf_find_struct_meta(meta->arg_btf, | ||
| 13524 | meta->arg_btf_id); | ||
| 13525 | } else if (is_list_node_type(ptr_type)) { | ||
| 13526 | struct btf_field *field = meta->arg_list_head.field; | ||
| 13527 | |||
| 13528 | mark_reg_graph_node(regs, BPF_REG_0, &field->graph_root); | ||
| 13529 | } else if (is_rbtree_node_type(ptr_type)) { | ||
| 13530 | struct btf_field *field = meta->arg_rbtree_root.field; | ||
| 13531 | |||
| 13532 | mark_reg_graph_node(regs, BPF_REG_0, &field->graph_root); | ||
| 13533 | } else if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) { | ||
| 13534 | mark_reg_known_zero(env, regs, BPF_REG_0); | ||
| 13535 | regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_TRUSTED; | ||
| 13536 | regs[BPF_REG_0].btf = desc_btf; | ||
| 13537 | regs[BPF_REG_0].btf_id = meta->ret_btf_id; | ||
| 13538 | } else if (meta->func_id == special_kfunc_list[KF_bpf_rdonly_cast]) { | ||
| 13539 | ret_t = btf_type_by_id(desc_btf, meta->arg_constant.value); | ||
| 13540 | if (!ret_t || !btf_type_is_struct(ret_t)) { | ||
| 13541 | verbose(env, | ||
| 13542 | "kfunc bpf_rdonly_cast type ID argument must be of a struct\n"); | ||
| 13543 | return -EINVAL; | ||
| 13544 | } | ||
| 13545 | |||
| 13546 | mark_reg_known_zero(env, regs, BPF_REG_0); | ||
| 13547 | regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_UNTRUSTED; | ||
| 13548 | regs[BPF_REG_0].btf = desc_btf; | ||
| 13549 | regs[BPF_REG_0].btf_id = meta->arg_constant.value; | ||
| 13550 | } else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice] || | ||
| 13551 | meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice_rdwr]) { | ||
| 13552 | enum bpf_type_flag type_flag = get_dynptr_type_flag(meta->initialized_dynptr.type); | ||
| 13553 | |||
| 13554 | mark_reg_known_zero(env, regs, BPF_REG_0); | ||
| 13555 | |||
| 13556 | if (!meta->arg_constant.found) { | ||
| 13557 | verbose(env, "verifier internal error: bpf_dynptr_slice(_rdwr) no constant size\n"); | ||
| 13558 | return -EFAULT; | ||
| 13559 | } | ||
| 13560 | |||
| 13561 | regs[BPF_REG_0].mem_size = meta->arg_constant.value; | ||
| 13562 | |||
| 13563 | /* PTR_MAYBE_NULL will be added when is_kfunc_ret_null is checked */ | ||
| 13564 | regs[BPF_REG_0].type = PTR_TO_MEM | type_flag; | ||
| 13565 | |||
| 13566 | if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_slice]) { | ||
| 13567 | regs[BPF_REG_0].type |= MEM_RDONLY; | ||
| 13568 | } else { | ||
| 13569 | /* this will set env->seen_direct_write to true */ | ||
| 13570 | if (!may_access_direct_pkt_data(env, NULL, BPF_WRITE)) { | ||
| 13571 | verbose(env, "the prog does not allow writes to packet data\n"); | ||
| 13572 | return -EINVAL; | ||
| 13573 | } | ||
| 13574 | } | ||
| 13575 | |||
| 13576 | if (!meta->initialized_dynptr.id) { | ||
| 13577 | verbose(env, "verifier internal error: no dynptr id\n"); | ||
| 13578 | return -EFAULT; | ||
| 13579 | } | ||
| 13580 | regs[BPF_REG_0].dynptr_id = meta->initialized_dynptr.id; | ||
| 13581 | |||
| 13582 | /* we don't need to set BPF_REG_0's ref obj id | ||
| 13583 | * because packet slices are not refcounted (see | ||
| 13584 | * dynptr_type_refcounted) | ||
| 13585 | */ | ||
| 13586 | } else { | ||
| 13587 | return 0; | ||
| 13588 | } | ||
| 13589 | |||
| 13590 | return 1; | ||
| 13591 | } | ||
| 13592 | |||
| 13423 | static int check_return_code(struct bpf_verifier_env *env, int regno, const char *reg_name); | 13593 | static int check_return_code(struct bpf_verifier_env *env, int regno, const char *reg_name); |
| 13424 | 13594 | ||
| 13425 | static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, | 13595 | static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, |
| @@ -13434,7 +13604,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, · 8 unmodified lines | |||
| 13434 | struct bpf_insn_aux_data *insn_aux; | 13604 | struct bpf_insn_aux_data *insn_aux; |
| 13435 | int err, insn_idx = *insn_idx_p; | 13605 | int err, insn_idx = *insn_idx_p; |
| 13436 | const struct btf_param *args; | 13606 | const struct btf_param *args; |
| 13437 | const struct btf_type *ret_t; | ||
| 13438 | struct btf *desc_btf; | 13607 | struct btf *desc_btf; |
| 13439 | 13608 | ||
| 13440 | /* skip for now, but return error when we find this in fixup_kfunc_call */ | 13609 | /* skip for now, but return error when we find this in fixup_kfunc_call */ |
| @@ -13476,6 +13645,9 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, · 35 unmodified lines | |||
| 13476 | return err; | 13645 | return err; |
| 13477 | } | 13646 | } |
| 13478 | __mark_btf_func_reg_size(env, regs, BPF_REG_0, sizeof(u32)); | 13647 | __mark_btf_func_reg_size(env, regs, BPF_REG_0, sizeof(u32)); |
| 13648 | } else if (!insn->off && insn->imm == special_kfunc_list[KF___bpf_trap]) { | ||
| 13649 | verbose(env, "unexpected __bpf_trap() due to uninitialized variable?\n"); | ||
| 13650 | return -EFAULT; | ||
| 13479 | } | 13651 | } |
| 13480 | 13652 | ||
| 13481 | if (is_kfunc_destructive(&meta) && !capable(CAP_SYS_BOOT)) { | 13653 | if (is_kfunc_destructive(&meta) && !capable(CAP_SYS_BOOT)) { |
| @@ -13654,165 +13826,10 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, · 172 unmodified lines | |||
| 13654 | mark_btf_func_reg_size(env, BPF_REG_0, t->size); | 13826 | mark_btf_func_reg_size(env, BPF_REG_0, t->size); |
| 13655 | } else if (btf_type_is_ptr(t)) { | 13827 | } else if (btf_type_is_ptr(t)) { |
| 13656 | ptr_type = btf_type_skip_modifiers(desc_btf, t->type, &ptr_type_id); | 13828 | ptr_type = btf_type_skip_modifiers(desc_btf, t->type, &ptr_type_id); |
| 13657 | 13829 | err = check_special_kfunc(env, &meta, regs, insn_aux, ptr_type, desc_btf); | |
| 13658 | if (meta.btf == btf_vmlinux && btf_id_set_contains(&special_kfunc_set, meta.func_id)) { | 13830 | if (err) { |
| 13659 | if (meta.func_id == special_kfunc_list[KF_bpf_obj_new_impl] || | 13831 | if (err < 0) |
| 13660 | meta.func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) { | 13832 | return err; |
| 13661 | struct btf_struct_meta *struct_meta; | ||
| 13662 | struct btf *ret_btf; | ||
| 13663 | u32 ret_btf_id; | ||
| 13664 | |||
| 13665 | if (meta.func_id == special_kfunc_list[KF_bpf_obj_new_impl] && !bpf_global_ma_set) | ||
| 13666 | return -ENOMEM; | ||
| 13667 | |||
| 13668 | if (((u64)(u32)meta.arg_constant.value) != meta.arg_constant.value) { | ||
| 13669 | verbose(env, "local type ID argument must be in range [0, U32_MAX]\n"); | ||
| 13670 | return -EINVAL; | ||
| 13671 | } | ||
| 13672 | |||
| 13673 | ret_btf = env->prog->aux->btf; | ||
| 13674 | ret_btf_id = meta.arg_constant.value; | ||
| 13675 | |||
| 13676 | /* This may be NULL due to user not supplying a BTF */ | ||
| 13677 | if (!ret_btf) { | ||
| 13678 | verbose(env, "bpf_obj_new/bpf_percpu_obj_new requires prog BTF\n"); | ||
| 13679 | return -EINVAL; | ||
| 13680 | } | ||
| 13681 | |||
| 13682 | ret_t = btf_type_by_id(ret_btf, ret_btf_id); | ||
| 13683 | if (!ret_t || !__btf_type_is_struct(ret_t)) { | ||
| 13684 | verbose(env, "bpf_obj_new/bpf_percpu_obj_new type ID argument must be of a struct\n"); | ||
| 13685 | return -EINVAL; | ||
| 13686 | } | ||
| 13687 | |||
| 13688 | if (meta.func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) { | ||
| 13689 | if (ret_t->size > BPF_GLOBAL_PERCPU_MA_MAX_SIZE) { | ||
| 13690 | verbose(env, "bpf_percpu_obj_new type size (%d) is greater than %d\n", | ||
| 13691 | ret_t->size, BPF_GLOBAL_PERCPU_MA_MAX_SIZE); | ||
| 13692 | return -EINVAL; | ||
| 13693 | } | ||
| 13694 | |||
| 13695 | if (!bpf_global_percpu_ma_set) { | ||
| 13696 | mutex_lock(&bpf_percpu_ma_lock); | ||
| 13697 | if (!bpf_global_percpu_ma_set) { | ||
| 13698 | /* Charge memory allocated with bpf_global_percpu_ma to | ||
| 13699 | * root memcg. The obj_cgroup for root memcg is NULL. | ||
| 13700 | */ | ||
| 13701 | err = bpf_mem_alloc_percpu_init(&bpf_global_percpu_ma, NULL); | ||
| 13702 | if (!err) | ||
| 13703 | bpf_global_percpu_ma_set = true; | ||
| 13704 | } | ||
| 13705 | mutex_unlock(&bpf_percpu_ma_lock); | ||
| 13706 | if (err) | ||
| 13707 | return err; | ||
| 13708 | } | ||
| 13709 | |||
| 13710 | mutex_lock(&bpf_percpu_ma_lock); | ||
| 13711 | err = bpf_mem_alloc_percpu_unit_init(&bpf_global_percpu_ma, ret_t->size); | ||
| 13712 | mutex_unlock(&bpf_percpu_ma_lock); | ||
| 13713 | if (err) | ||
| 13714 | return err; | ||
| 13715 | } | ||
| 13716 | |||
| 13717 | struct_meta = btf_find_struct_meta(ret_btf, ret_btf_id); | ||
| 13718 | if (meta.func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) { | ||
| 13719 | if (!__btf_type_is_scalar_struct(env, ret_btf, ret_t, 0)) { | ||
| 13720 | verbose(env, "bpf_percpu_obj_new type ID argument must be of a struct of scalars\n"); | ||
| 13721 | return -EINVAL; | ||
| 13722 | } | ||
| 13723 | |||
| 13724 | if (struct_meta) { | ||
| 13725 | verbose(env, "bpf_percpu_obj_new type ID argument must not contain special fields\n"); | ||
| 13726 | return -EINVAL; | ||
| 13727 | } | ||
| 13728 | } | ||
| 13729 | |||
| 13730 | mark_reg_known_zero(env, regs, BPF_REG_0); | ||
| 13731 | regs[BPF_REG_0].type = PTR_TO_BTF_ID | MEM_ALLOC; | ||
| 13732 | regs[BPF_REG_0].btf = ret_btf; | ||
| 13733 | regs[BPF_REG_0].btf_id = ret_btf_id; | ||
| 13734 | if (meta.func_id == special_kfunc_list[KF_bpf_percpu_obj_new_impl]) | ||
| 13735 | regs[BPF_REG_0].type |= MEM_PERCPU; | ||
| 13736 | |||
| 13737 | insn_aux->obj_new_size = ret_t->size; | ||
| 13738 | insn_aux->kptr_struct_meta = struct_meta; | ||
| 13739 | } else if (meta.func_id == special_kfunc_list[KF_bpf_refcount_acquire_impl]) { | ||
| 13740 | mark_reg_known_zero(env, regs, BPF_REG_0); | ||
| 13741 | regs[BPF_REG_0].type = PTR_TO_BTF_ID | MEM_ALLOC; | ||
| 13742 | regs[BPF_REG_0].btf = meta.arg_btf; | ||
| 13743 | regs[BPF_REG_0].btf_id = meta.arg_btf_id; | ||
| 13744 | |||
| 13745 | insn_aux->kptr_struct_meta = | ||
| 13746 | btf_find_struct_meta(meta.arg_btf, | ||
| 13747 | meta.arg_btf_id); | ||
| 13748 | } else if (meta.func_id == special_kfunc_list[KF_bpf_list_pop_front] || | ||
| 13749 | meta.func_id == special_kfunc_list[KF_bpf_list_pop_back]) { | ||
| 13750 | struct btf_field *field = meta.arg_list_head.field; | ||
| 13751 | |||
| 13752 | mark_reg_graph_node(regs, BPF_REG_0, &field->graph_root); | ||
| 13753 | } else if (meta.func_id == special_kfunc_list[KF_bpf_rbtree_remove] || | ||
| 13754 | meta.func_id == special_kfunc_list[KF_bpf_rbtree_first]) { | ||
| 13755 | struct btf_field *field = meta.arg_rbtree_root.field; | ||
| 13756 | |||
| 13757 | mark_reg_graph_node(regs, BPF_REG_0, &field->graph_root); | ||
| 13758 | } else if (meta.func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) { | ||
| 13759 | mark_reg_known_zero(env, regs, BPF_REG_0); | ||
| 13760 | regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_TRUSTED; | ||
| 13761 | regs[BPF_REG_0].btf = desc_btf; | ||
| 13762 | regs[BPF_REG_0].btf_id = meta.ret_btf_id; | ||
| 13763 | } else if (meta.func_id == special_kfunc_list[KF_bpf_rdonly_cast]) { | ||
| 13764 | ret_t = btf_type_by_id(desc_btf, meta.arg_constant.value); | ||
| 13765 | if (!ret_t || !btf_type_is_struct(ret_t)) { | ||
| 13766 | verbose(env, | ||
| 13767 | "kfunc bpf_rdonly_cast type ID argument must be of a struct\n"); | ||
| 13768 | return -EINVAL; | ||
| 13769 | } | ||
| 13770 | |||
| 13771 | mark_reg_known_zero(env, regs, BPF_REG_0); | ||
| 13772 | regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_UNTRUSTED; | ||
| 13773 | regs[BPF_REG_0].btf = desc_btf; | ||
| 13774 | regs[BPF_REG_0].btf_id = meta.arg_constant.value; | ||
| 13775 | } else if (meta.func_id == special_kfunc_list[KF_bpf_dynptr_slice] || | ||
| 13776 | meta.func_id == special_kfunc_list[KF_bpf_dynptr_slice_rdwr]) { | ||
| 13777 | enum bpf_type_flag type_flag = get_dynptr_type_flag(meta.initialized_dynptr.type); | ||
| 13778 | |||
| 13779 | mark_reg_known_zero(env, regs, BPF_REG_0); | ||
| 13780 | |||
| 13781 | if (!meta.arg_constant.found) { | ||
| 13782 | verbose(env, "verifier internal error: bpf_dynptr_slice(_rdwr) no constant size\n"); | ||
| 13783 | return -EFAULT; | ||
| 13784 | } | ||
| 13785 | |||
| 13786 | regs[BPF_REG_0].mem_size = meta.arg_constant.value; | ||
| 13787 | |||
| 13788 | /* PTR_MAYBE_NULL will be added when is_kfunc_ret_null is checked */ | ||
| 13789 | regs[BPF_REG_0].type = PTR_TO_MEM | type_flag; | ||
| 13790 | |||
| 13791 | if (meta.func_id == special_kfunc_list[KF_bpf_dynptr_slice]) { | ||
| 13792 | regs[BPF_REG_0].type |= MEM_RDONLY; | ||
| 13793 | } else { | ||
| 13794 | /* this will set env->seen_direct_write to true */ | ||
| 13795 | if (!may_access_direct_pkt_data(env, NULL, BPF_WRITE)) { | ||
| 13796 | verbose(env, "the prog does not allow writes to packet data\n"); | ||
| 13797 | return -EINVAL; | ||
| 13798 | } | ||
| 13799 | } | ||
| 13800 | |||
| 13801 | if (!meta.initialized_dynptr.id) { | ||
| 13802 | verbose(env, "verifier internal error: no dynptr id\n"); | ||
| 13803 | return -EFAULT; | ||
| 13804 | } | ||
| 13805 | regs[BPF_REG_0].dynptr_id = meta.initialized_dynptr.id; | ||
| 13806 | |||
| 13807 | /* we don't need to set BPF_REG_0's ref obj id | ||
| 13808 | * because packet slices are not refcounted (see | ||
| 13809 | * dynptr_type_refcounted) | ||
| 13810 | */ | ||
| 13811 | } else { | ||
| 13812 | verbose(env, "kernel function %s unhandled dynamic return type\n", | ||
| 13813 | meta.func_name); | ||
| 13814 | return -EFAULT; | ||
| 13815 | } | ||
| 13816 | } else if (btf_type_is_void(ptr_type)) { | 13833 | } else if (btf_type_is_void(ptr_type)) { |
| 13817 | /* kfunc returning 'void *' is equivalent to returning scalar */ | 13834 | /* kfunc returning 'void *' is equivalent to returning scalar */ |
| 13818 | mark_reg_unknown(env, regs, BPF_REG_0); | 13835 | mark_reg_unknown(env, regs, BPF_REG_0); |
| @@ -13881,14 +13898,14 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, · 62 unmodified lines | |||
| 13881 | if (is_kfunc_ret_null(&meta)) | 13898 | if (is_kfunc_ret_null(&meta)) |
| 13882 | regs[BPF_REG_0].id = id; | 13899 | regs[BPF_REG_0].id = id; |
| 13883 | regs[BPF_REG_0].ref_obj_id = id; | 13900 | regs[BPF_REG_0].ref_obj_id = id; |
| 13884 | } else if (meta.func_id == special_kfunc_list[KF_bpf_rbtree_first]) { | 13901 | } else if (is_rbtree_node_type(ptr_type) || is_list_node_type(ptr_type)) { |
| 13885 | ref_set_non_owning(env, ®s[BPF_REG_0]); | 13902 | ref_set_non_owning(env, ®s[BPF_REG_0]); |
| 13886 | } | 13903 | } |
| 13887 | 13904 | ||
| 13888 | if (reg_may_point_to_spin_lock(®s[BPF_REG_0]) && !regs[BPF_REG_0].id) | 13905 | if (reg_may_point_to_spin_lock(®s[BPF_REG_0]) && !regs[BPF_REG_0].id) |
| 13889 | regs[BPF_REG_0].id = ++env->id_gen; | 13906 | regs[BPF_REG_0].id = ++env->id_gen; |
| 13890 | } else if (btf_type_is_void(t)) { | 13907 | } else if (btf_type_is_void(t)) { |
| 13891 | if (meta.btf == btf_vmlinux && btf_id_set_contains(&special_kfunc_set, meta.func_id)) { | 13908 | if (meta.btf == btf_vmlinux) { |
| 13892 | if (meta.func_id == special_kfunc_list[KF_bpf_obj_drop_impl] || | 13909 | if (meta.func_id == special_kfunc_list[KF_bpf_obj_drop_impl] || |
| 13893 | meta.func_id == special_kfunc_list[KF_bpf_percpu_obj_drop_impl]) { | 13910 | meta.func_id == special_kfunc_list[KF_bpf_percpu_obj_drop_impl]) { |
| 13894 | insn_aux->kptr_struct_meta = | 13911 | insn_aux->kptr_struct_meta = |
| @@ -16377,6 +16394,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, · 2482 unmodified lines | |||
| 16377 | struct bpf_reg_state *eq_branch_regs; | 16394 | struct bpf_reg_state *eq_branch_regs; |
| 16378 | struct linked_regs linked_regs = {}; | 16395 | struct linked_regs linked_regs = {}; |
| 16379 | u8 opcode = BPF_OP(insn->code); | 16396 | u8 opcode = BPF_OP(insn->code); |
| 16397 | int insn_flags = 0; | ||
| 16380 | bool is_jmp32; | 16398 | bool is_jmp32; |
| 16381 | int pred = -1; | 16399 | int pred = -1; |
| 16382 | int err; | 16400 | int err; |
| @@ -16435,6 +16453,11 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, · 52 unmodified lines | |||
| 16435 | insn->src_reg); | 16453 | insn->src_reg); |
| 16436 | return -EACCES; | 16454 | return -EACCES; |
| 16437 | } | 16455 | } |
| 16456 | |||
| 16457 | if (src_reg->type == PTR_TO_STACK) | ||
| 16458 | insn_flags |= INSN_F_SRC_REG_STACK; | ||
| 16459 | if (dst_reg->type == PTR_TO_STACK) | ||
| 16460 | insn_flags |= INSN_F_DST_REG_STACK; | ||
| 16438 | } else { | 16461 | } else { |
| 16439 | if (insn->src_reg != BPF_REG_0) { | 16462 | if (insn->src_reg != BPF_REG_0) { |
| 16440 | verbose(env, "BPF_JMP/JMP32 uses reserved fields\n"); | 16463 | verbose(env, "BPF_JMP/JMP32 uses reserved fields\n"); |
| @@ -16444,6 +16467,15 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, · 3 unmodified lines | |||
| 16444 | memset(src_reg, 0, sizeof(*src_reg)); | 16467 | memset(src_reg, 0, sizeof(*src_reg)); |
| 16445 | src_reg->type = SCALAR_VALUE; | 16468 | src_reg->type = SCALAR_VALUE; |
| 16446 | __mark_reg_known(src_reg, insn->imm); | 16469 | __mark_reg_known(src_reg, insn->imm); |
| 16470 | |||
| 16471 | if (dst_reg->type == PTR_TO_STACK) | ||
| 16472 | insn_flags |= INSN_F_DST_REG_STACK; | ||
| 16473 | } | ||
| 16474 | |||
| 16475 | if (insn_flags) { | ||
| 16476 | err = push_insn_history(env, this_branch, insn_flags, 0); | ||
| 16477 | if (err) | ||
| 16478 | return err; | ||
| 16447 | } | 16479 | } |
| 16448 | 16480 | ||
| 16449 | is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32; | 16481 | is_jmp32 = BPF_CLASS(insn->code) == BPF_JMP32; |
| @@ -19659,10 +19691,9 @@ process_bpf_exit: · 3209 unmodified lines | |||
| 19659 | return err; | 19691 | return err; |
| 19660 | break; | 19692 | break; |
| 19661 | } else { | 19693 | } else { |
| 19662 | if (WARN_ON_ONCE(env->cur_state->loop_entry)) { | 19694 | if (verifier_bug_if(env->cur_state->loop_entry, env, |
| 19663 | verbose(env, "verifier bug: env->cur_state->loop_entry != NULL\n"); | 19695 | "broken loop detection")) |
| 19664 | return -EFAULT; | 19696 | return -EFAULT; |
| 19665 | } | ||
| 19666 | do_print_state = true; | 19697 | do_print_state = true; |
| 19667 | continue; | 19698 | continue; |
| 19668 | } | 19699 | } |
| @@ -20720,10 +20751,9 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env, · 1051 unmodified lines | |||
| 20720 | if (bpf_pseudo_kfunc_call(&insn)) | 20751 | if (bpf_pseudo_kfunc_call(&insn)) |
| 20721 | continue; | 20752 | continue; |
| 20722 | 20753 | ||
| 20723 | if (WARN_ON(load_reg == -1)) { | 20754 | if (verifier_bug_if(load_reg == -1, env, |
| 20724 | verbose(env, "verifier bug. zext_dst is set, but no reg is defined\n"); | 20755 | "zext_dst is set, but no reg is defined")) |
| 20725 | return -EFAULT; | 20756 | return -EFAULT; |
| 20726 | } | ||
| 20727 | 20757 | ||
| 20728 | zext_patch[0] = insn; | 20758 | zext_patch[0] = insn; |
| 20729 | zext_patch[1].dst_reg = load_reg; | 20759 | zext_patch[1].dst_reg = load_reg; |
| @@ -21040,11 +21070,9 @@ static int jit_subprogs(struct bpf_verifier_env *env) · 310 unmodified lines | |||
| 21040 | * propagated in any case. | 21070 | * propagated in any case. |
| 21041 | */ | 21071 | */ |
| 21042 | subprog = find_subprog(env, i + insn->imm + 1); | 21072 | subprog = find_subprog(env, i + insn->imm + 1); |
| 21043 | if (subprog < 0) { | 21073 | if (verifier_bug_if(subprog < 0, env, "No program to jit at insn %d", |
| 21044 | WARN_ONCE(1, "verifier bug. No program starts at insn %d\n", | 21074 | i + insn->imm + 1)) |
| 21045 | i + insn->imm + 1); | ||
| 21046 | return -EFAULT; | 21075 | return -EFAULT; |
| 21047 | } | ||
| 21048 | /* temporarily remember subprog id inside insn instead of | 21076 | /* temporarily remember subprog id inside insn instead of |
| 21049 | * aux_data, since next loop will split up all insns into funcs | 21077 | * aux_data, since next loop will split up all insns into funcs |
| 21050 | */ | 21078 | */ |
| @@ -21487,13 +21515,17 @@ static int fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, · 436 unmodified lines | |||
| 21487 | desc->func_id == special_kfunc_list[KF_bpf_rdonly_cast]) { | 21515 | desc->func_id == special_kfunc_list[KF_bpf_rdonly_cast]) { |
| 21488 | insn_buf[0] = BPF_MOV64_REG(BPF_REG_0, BPF_REG_1); | 21516 | insn_buf[0] = BPF_MOV64_REG(BPF_REG_0, BPF_REG_1); |
| 21489 | *cnt = 1; | 21517 | *cnt = 1; |
| 21490 | } else if (is_bpf_wq_set_callback_impl_kfunc(desc->func_id)) { | 21518 | } |
| 21491 | struct bpf_insn ld_addrs[2] = { BPF_LD_IMM64(BPF_REG_4, (long)env->prog->aux) }; | ||
| 21492 | 21519 | ||
| 21493 | insn_buf[0] = ld_addrs[0]; | 21520 | if (env->insn_aux_data[insn_idx].arg_prog) { |
| 21494 | insn_buf[1] = ld_addrs[1]; | 21521 | u32 regno = env->insn_aux_data[insn_idx].arg_prog; |
| 21495 | insn_buf[2] = *insn; | 21522 | struct bpf_insn ld_addrs[2] = { BPF_LD_IMM64(regno, (long)env->prog->aux) }; |
| 21496 | *cnt = 3; | 21523 | int idx = *cnt; |
| 21524 | |||
| 21525 | insn_buf[idx++] = ld_addrs[0]; | ||
| 21526 | insn_buf[idx++] = ld_addrs[1]; | ||
| 21527 | insn_buf[idx++] = *insn; | ||
| 21528 | *cnt = idx; | ||
| 21497 | } | 21529 | } |
| 21498 | return 0; | 21530 | return 0; |
| 21499 | } | 21531 | } |
| @@ -22403,7 +22435,7 @@ next_insn: · 903 unmodified lines | |||
| 22403 | continue; | 22435 | continue; |
| 22404 | /* We need two slots in case timed may_goto is supported. */ | 22436 | /* We need two slots in case timed may_goto is supported. */ |
| 22405 | if (stack_slots > slots) { | 22437 | if (stack_slots > slots) { |
| 22406 | verbose(env, "verifier bug: stack_slots supports may_goto only\n"); | 22438 | verifier_bug(env, "stack_slots supports may_goto only"); |
| 22407 | return -EFAULT; | 22439 | return -EFAULT; |
| 22408 | } | 22440 | } |
| 22409 | 22441 | ||
| @@ -5791,21 +5791,8 @@ static int bpf_scx_btf_struct_access(struct bpf_verifier_log *log, · 5790 unmodified lines | |||
| 5791 | return -EACCES; | 5791 | return -EACCES; |
| 5792 | } | 5792 | } |
| 5793 | 5793 | ||
| 5794 | static const struct bpf_func_proto * | ||
| 5795 | bpf_scx_get_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) | ||
| 5796 | { | ||
| 5797 | switch (func_id) { | ||
| 5798 | case BPF_FUNC_task_storage_get: | ||
| 5799 | return &bpf_task_storage_get_proto; | ||
| 5800 | case BPF_FUNC_task_storage_delete: | ||
| 5801 | return &bpf_task_storage_delete_proto; | ||
| 5802 | default: | ||
| 5803 | return bpf_base_func_proto(func_id, prog); | ||
| 5804 | } | ||
| 5805 | } | ||
| 5806 | |||
| 5807 | static const struct bpf_verifier_ops bpf_scx_verifier_ops = { | 5794 | static const struct bpf_verifier_ops bpf_scx_verifier_ops = { |
| 5808 | .get_func_proto = bpf_scx_get_func_proto, | 5795 | .get_func_proto = bpf_base_func_proto, |
| 5809 | .is_valid_access = bpf_scx_is_valid_access, | 5796 | .is_valid_access = bpf_scx_is_valid_access, |
| 5810 | .btf_struct_access = bpf_scx_btf_struct_access, | 5797 | .btf_struct_access = bpf_scx_btf_struct_access, |
| 5811 | }; | 5798 | }; |
| @@ -572,7 +572,7 @@ BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) · 571 unmodified lines | |||
| 572 | return value; | 572 | return value; |
| 573 | } | 573 | } |
| 574 | 574 | ||
| 575 | static const struct bpf_func_proto bpf_perf_event_read_proto = { | 575 | const struct bpf_func_proto bpf_perf_event_read_proto = { |
| 576 | .func = bpf_perf_event_read, | 576 | .func = bpf_perf_event_read, |
| 577 | .gpl_only = true, | 577 | .gpl_only = true, |
| 578 | .ret_type = RET_INTEGER, | 578 | .ret_type = RET_INTEGER, |
| @@ -882,7 +882,7 @@ BPF_CALL_1(bpf_send_signal, u32, sig) · 303 unmodified lines | |||
| 882 | return bpf_send_signal_common(sig, PIDTYPE_TGID, NULL, 0); | 882 | return bpf_send_signal_common(sig, PIDTYPE_TGID, NULL, 0); |
| 883 | } | 883 | } |
| 884 | 884 | ||
| 885 | static const struct bpf_func_proto bpf_send_signal_proto = { | 885 | const struct bpf_func_proto bpf_send_signal_proto = { |
| 886 | .func = bpf_send_signal, | 886 | .func = bpf_send_signal, |
| 887 | .gpl_only = false, | 887 | .gpl_only = false, |
| 888 | .ret_type = RET_INTEGER, | 888 | .ret_type = RET_INTEGER, |
| @@ -894,7 +894,7 @@ BPF_CALL_1(bpf_send_signal_thread, u32, sig) · 5 unmodified lines | |||
| 894 | return bpf_send_signal_common(sig, PIDTYPE_PID, NULL, 0); | 894 | return bpf_send_signal_common(sig, PIDTYPE_PID, NULL, 0); |
| 895 | } | 895 | } |
| 896 | 896 | ||
| 897 | static const struct bpf_func_proto bpf_send_signal_thread_proto = { | 897 | const struct bpf_func_proto bpf_send_signal_thread_proto = { |
| 898 | .func = bpf_send_signal_thread, | 898 | .func = bpf_send_signal_thread, |
| 899 | .gpl_only = false, | 899 | .gpl_only = false, |
| 900 | .ret_type = RET_INTEGER, | 900 | .ret_type = RET_INTEGER, |
| @@ -1185,7 +1185,7 @@ BPF_CALL_3(bpf_get_branch_snapshot, void *, buf, u32, size, u64, flags) · 284 unmodified lines | |||
| 1185 | return entry_cnt * br_entry_size; | 1185 | return entry_cnt * br_entry_size; |
| 1186 | } | 1186 | } |
| 1187 | 1187 | ||
| 1188 | static const struct bpf_func_proto bpf_get_branch_snapshot_proto = { | 1188 | const struct bpf_func_proto bpf_get_branch_snapshot_proto = { |
| 1189 | .func = bpf_get_branch_snapshot, | 1189 | .func = bpf_get_branch_snapshot, |
| 1190 | .gpl_only = true, | 1190 | .gpl_only = true, |
| 1191 | .ret_type = RET_INTEGER, | 1191 | .ret_type = RET_INTEGER, |
| @@ -1430,56 +1430,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 238 unmodified lines | |||
| 1430 | const struct bpf_func_proto *func_proto; | 1430 | const struct bpf_func_proto *func_proto; |
| 1431 | 1431 | ||
| 1432 | switch (func_id) { | 1432 | switch (func_id) { |
| 1433 | case BPF_FUNC_map_lookup_elem: | ||
| 1434 | return &bpf_map_lookup_elem_proto; | ||
| 1435 | case BPF_FUNC_map_update_elem: | ||
| 1436 | return &bpf_map_update_elem_proto; | ||
| 1437 | case BPF_FUNC_map_delete_elem: | ||
| 1438 | return &bpf_map_delete_elem_proto; | ||
| 1439 | case BPF_FUNC_map_push_elem: | ||
| 1440 | return &bpf_map_push_elem_proto; | ||
| 1441 | case BPF_FUNC_map_pop_elem: | ||
| 1442 | return &bpf_map_pop_elem_proto; | ||
| 1443 | case BPF_FUNC_map_peek_elem: | ||
| 1444 | return &bpf_map_peek_elem_proto; | ||
| 1445 | case BPF_FUNC_map_lookup_percpu_elem: | ||
| 1446 | return &bpf_map_lookup_percpu_elem_proto; | ||
| 1447 | case BPF_FUNC_ktime_get_ns: | ||
| 1448 | return &bpf_ktime_get_ns_proto; | ||
| 1449 | case BPF_FUNC_ktime_get_boot_ns: | ||
| 1450 | return &bpf_ktime_get_boot_ns_proto; | ||
| 1451 | case BPF_FUNC_tail_call: | ||
| 1452 | return &bpf_tail_call_proto; | ||
| 1453 | case BPF_FUNC_get_current_task: | ||
| 1454 | return &bpf_get_current_task_proto; | ||
| 1455 | case BPF_FUNC_get_current_task_btf: | ||
| 1456 | return &bpf_get_current_task_btf_proto; | ||
| 1457 | case BPF_FUNC_task_pt_regs: | ||
| 1458 | return &bpf_task_pt_regs_proto; | ||
| 1459 | case BPF_FUNC_get_current_uid_gid: | ||
| 1460 | return &bpf_get_current_uid_gid_proto; | ||
| 1461 | case BPF_FUNC_get_current_comm: | ||
| 1462 | return &bpf_get_current_comm_proto; | ||
| 1463 | case BPF_FUNC_trace_printk: | ||
| 1464 | return bpf_get_trace_printk_proto(); | ||
| 1465 | case BPF_FUNC_get_smp_processor_id: | 1433 | case BPF_FUNC_get_smp_processor_id: |
| 1466 | return &bpf_get_smp_processor_id_proto; | 1434 | return &bpf_get_smp_processor_id_proto; |
| 1467 | case BPF_FUNC_get_numa_node_id: | ||
| 1468 | return &bpf_get_numa_node_id_proto; | ||
| 1469 | case BPF_FUNC_perf_event_read: | ||
| 1470 | return &bpf_perf_event_read_proto; | ||
| 1471 | case BPF_FUNC_get_prandom_u32: | ||
| 1472 | return &bpf_get_prandom_u32_proto; | ||
| 1473 | case BPF_FUNC_probe_read_user: | ||
| 1474 | return &bpf_probe_read_user_proto; | ||
| 1475 | case BPF_FUNC_probe_read_kernel: | ||
| 1476 | return security_locked_down(LOCKDOWN_BPF_READ_KERNEL) < 0 ? | ||
| 1477 | NULL : &bpf_probe_read_kernel_proto; | ||
| 1478 | case BPF_FUNC_probe_read_user_str: | ||
| 1479 | return &bpf_probe_read_user_str_proto; | ||
| 1480 | case BPF_FUNC_probe_read_kernel_str: | ||
| 1481 | return security_locked_down(LOCKDOWN_BPF_READ_KERNEL) < 0 ? | ||
| 1482 | NULL : &bpf_probe_read_kernel_str_proto; | ||
| 1483 | #ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE | 1435 | #ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE |
| 1484 | case BPF_FUNC_probe_read: | 1436 | case BPF_FUNC_probe_read: |
| 1485 | return security_locked_down(LOCKDOWN_BPF_READ_KERNEL) < 0 ? | 1437 | return security_locked_down(LOCKDOWN_BPF_READ_KERNEL) < 0 ? |
| @@ -1488,65 +1440,8 @@ bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 2 unmodified lines | |||
| 1488 | return security_locked_down(LOCKDOWN_BPF_READ_KERNEL) < 0 ? | 1440 | return security_locked_down(LOCKDOWN_BPF_READ_KERNEL) < 0 ? |
| 1489 | NULL : &bpf_probe_read_compat_str_proto; | 1441 | NULL : &bpf_probe_read_compat_str_proto; |
| 1490 | #endif | 1442 | #endif |
| 1491 | #ifdef CONFIG_CGROUPS | ||
| 1492 | case BPF_FUNC_cgrp_storage_get: | ||
| 1493 | return &bpf_cgrp_storage_get_proto; | ||
| 1494 | case BPF_FUNC_cgrp_storage_delete: | ||
| 1495 | return &bpf_cgrp_storage_delete_proto; | ||
| 1496 | case BPF_FUNC_current_task_under_cgroup: | ||
| 1497 | return &bpf_current_task_under_cgroup_proto; | ||
| 1498 | #endif | ||
| 1499 | case BPF_FUNC_send_signal: | ||
| 1500 | return &bpf_send_signal_proto; | ||
| 1501 | case BPF_FUNC_send_signal_thread: | ||
| 1502 | return &bpf_send_signal_thread_proto; | ||
| 1503 | case BPF_FUNC_perf_event_read_value: | ||
| 1504 | return &bpf_perf_event_read_value_proto; | ||
| 1505 | case BPF_FUNC_ringbuf_output: | ||
| 1506 | return &bpf_ringbuf_output_proto; | ||
| 1507 | case BPF_FUNC_ringbuf_reserve: | ||
| 1508 | return &bpf_ringbuf_reserve_proto; | ||
| 1509 | case BPF_FUNC_ringbuf_submit: | ||
| 1510 | return &bpf_ringbuf_submit_proto; | ||
| 1511 | case BPF_FUNC_ringbuf_discard: | ||
| 1512 | return &bpf_ringbuf_discard_proto; | ||
| 1513 | case BPF_FUNC_ringbuf_query: | ||
| 1514 | return &bpf_ringbuf_query_proto; | ||
| 1515 | case BPF_FUNC_jiffies64: | ||
| 1516 | return &bpf_jiffies64_proto; | ||
| 1517 | case BPF_FUNC_get_task_stack: | ||
| 1518 | return prog->sleepable ? &bpf_get_task_stack_sleepable_proto | ||
| 1519 | : &bpf_get_task_stack_proto; | ||
| 1520 | case BPF_FUNC_copy_from_user: | ||
| 1521 | return &bpf_copy_from_user_proto; | ||
| 1522 | case BPF_FUNC_copy_from_user_task: | ||
| 1523 | return &bpf_copy_from_user_task_proto; | ||
| 1524 | case BPF_FUNC_snprintf_btf: | ||
| 1525 | return &bpf_snprintf_btf_proto; | ||
| 1526 | case BPF_FUNC_per_cpu_ptr: | ||
| 1527 | return &bpf_per_cpu_ptr_proto; | ||
| 1528 | case BPF_FUNC_this_cpu_ptr: | ||
| 1529 | return &bpf_this_cpu_ptr_proto; | ||
| 1530 | case BPF_FUNC_task_storage_get: | ||
| 1531 | if (bpf_prog_check_recur(prog)) | ||
| 1532 | return &bpf_task_storage_get_recur_proto; | ||
| 1533 | return &bpf_task_storage_get_proto; | ||
| 1534 | case BPF_FUNC_task_storage_delete: | ||
| 1535 | if (bpf_prog_check_recur(prog)) | ||
| 1536 | return &bpf_task_storage_delete_recur_proto; | ||
| 1537 | return &bpf_task_storage_delete_proto; | ||
| 1538 | case BPF_FUNC_for_each_map_elem: | ||
| 1539 | return &bpf_for_each_map_elem_proto; | ||
| 1540 | case BPF_FUNC_snprintf: | ||
| 1541 | return &bpf_snprintf_proto; | ||
| 1542 | case BPF_FUNC_get_func_ip: | 1443 | case BPF_FUNC_get_func_ip: |
| 1543 | return &bpf_get_func_ip_proto_tracing; | 1444 | return &bpf_get_func_ip_proto_tracing; |
| 1544 | case BPF_FUNC_get_branch_snapshot: | ||
| 1545 | return &bpf_get_branch_snapshot_proto; | ||
| 1546 | case BPF_FUNC_find_vma: | ||
| 1547 | return &bpf_find_vma_proto; | ||
| 1548 | case BPF_FUNC_trace_vprintk: | ||
| 1549 | return bpf_get_trace_vprintk_proto(); | ||
| 1550 | default: | 1445 | default: |
| 1551 | break; | 1446 | break; |
| 1552 | } | 1447 | } |
| @@ -1858,7 +1753,7 @@ static struct pt_regs *get_bpf_raw_tp_regs(void) · 305 unmodified lines | |||
| 1858 | struct bpf_raw_tp_regs *tp_regs = this_cpu_ptr(&bpf_raw_tp_regs); | 1753 | struct bpf_raw_tp_regs *tp_regs = this_cpu_ptr(&bpf_raw_tp_regs); |
| 1859 | int nest_level = this_cpu_inc_return(bpf_raw_tp_nest_level); | 1754 | int nest_level = this_cpu_inc_return(bpf_raw_tp_nest_level); |
| 1860 | 1755 | ||
| 1861 | if (WARN_ON_ONCE(nest_level > ARRAY_SIZE(tp_regs->regs))) { | 1756 | if (nest_level > ARRAY_SIZE(tp_regs->regs)) { |
| 1862 | this_cpu_dec(bpf_raw_tp_nest_level); | 1757 | this_cpu_dec(bpf_raw_tp_nest_level); |
| 1863 | return ERR_PTR(-EBUSY); | 1758 | return ERR_PTR(-EBUSY); |
| 1864 | } | 1759 | } |
| @@ -2987,6 +2882,9 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr · 1122 unmodified lines | |||
| 2987 | if (sizeof(u64) != sizeof(void *)) | 2882 | if (sizeof(u64) != sizeof(void *)) |
| 2988 | return -EOPNOTSUPP; | 2883 | return -EOPNOTSUPP; |
| 2989 | 2884 | ||
| 2885 | if (attr->link_create.flags) | ||
| 2886 | return -EINVAL; | ||
| 2887 | |||
| 2990 | if (!is_kprobe_multi(prog)) | 2888 | if (!is_kprobe_multi(prog)) |
| 2991 | return -EINVAL; | 2889 | return -EINVAL; |
| 2992 | 2890 | ||
| @@ -3376,6 +3274,9 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr · 383 unmodified lines | |||
| 3376 | if (sizeof(u64) != sizeof(void *)) | 3274 | if (sizeof(u64) != sizeof(void *)) |
| 3377 | return -EOPNOTSUPP; | 3275 | return -EOPNOTSUPP; |
| 3378 | 3276 | ||
| 3277 | if (attr->link_create.flags) | ||
| 3278 | return -EINVAL; | ||
| 3279 | |||
| 3379 | if (!is_uprobe_multi(prog)) | 3280 | if (!is_uprobe_multi(prog)) |
| 3380 | return -EINVAL; | 3281 | return -EINVAL; |
| 3381 | 3282 | ||
| @@ -3417,7 +3318,9 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr · 35 unmodified lines | |||
| 3417 | } | 3318 | } |
| 3418 | 3319 | ||
| 3419 | if (pid) { | 3320 | if (pid) { |
| 3321 | rcu_read_lock(); | ||
| 3420 | task = get_pid_task(find_vpid(pid), PIDTYPE_TGID); | 3322 | task = get_pid_task(find_vpid(pid), PIDTYPE_TGID); |
| 3323 | rcu_read_unlock(); | ||
| 3421 | if (!task) { | 3324 | if (!task) { |
| 3422 | err = -ESRCH; | 3325 | err = -ESRCH; |
| 3423 | goto error_path_put; | 3326 | goto error_path_put; |
| @@ -3565,6 +3468,146 @@ static int __init bpf_kprobe_multi_kfuncs_init(void) · 141 unmodified lines | |||
| 3565 | 3468 | ||
| 3566 | late_initcall(bpf_kprobe_multi_kfuncs_init); | 3469 | late_initcall(bpf_kprobe_multi_kfuncs_init); |
| 3567 | 3470 | ||
| 3471 | typedef int (*copy_fn_t)(void *dst, const void *src, u32 size, struct task_struct *tsk); | ||
| 3472 | |||
| 3473 | /* | ||
| 3474 | * The __always_inline is to make sure the compiler doesn't | ||
| 3475 | * generate indirect calls into callbacks, which is expensive, | ||
| 3476 | * on some kernel configurations. This allows compiler to put | ||
| 3477 | * direct calls into all the specific callback implementations | ||
| 3478 | * (copy_user_data_sleepable, copy_user_data_nofault, and so on) | ||
| 3479 | */ | ||
| 3480 | static __always_inline int __bpf_dynptr_copy_str(struct bpf_dynptr *dptr, u32 doff, u32 size, | ||
| 3481 | const void *unsafe_src, | ||
| 3482 | copy_fn_t str_copy_fn, | ||
| 3483 | struct task_struct *tsk) | ||
| 3484 | { | ||
| 3485 | struct bpf_dynptr_kern *dst; | ||
| 3486 | u32 chunk_sz, off; | ||
| 3487 | void *dst_slice; | ||
| 3488 | int cnt, err; | ||
| 3489 | char buf[256]; | ||
| 3490 | |||
| 3491 | dst_slice = bpf_dynptr_slice_rdwr(dptr, doff, NULL, size); | ||
| 3492 | if (likely(dst_slice)) | ||
| 3493 | return str_copy_fn(dst_slice, unsafe_src, size, tsk); | ||
| 3494 | |||
| 3495 | dst = (struct bpf_dynptr_kern *)dptr; | ||
| 3496 | if (bpf_dynptr_check_off_len(dst, doff, size)) | ||
| 3497 | return -E2BIG; | ||
| 3498 | |||
| 3499 | for (off = 0; off < size; off += chunk_sz - 1) { | ||
| 3500 | chunk_sz = min_t(u32, sizeof(buf), size - off); | ||
| 3501 | /* Expect str_copy_fn to return count of copied bytes, including | ||
| 3502 | * zero terminator. Next iteration increment off by chunk_sz - 1 to | ||
| 3503 | * overwrite NUL. | ||
| 3504 | */ | ||
| 3505 | cnt = str_copy_fn(buf, unsafe_src + off, chunk_sz, tsk); | ||
| 3506 | if (cnt < 0) | ||
| 3507 | return cnt; | ||
| 3508 | err = __bpf_dynptr_write(dst, doff + off, buf, cnt, 0); | ||
| 3509 | if (err) | ||
| 3510 | return err; | ||
| 3511 | if (cnt < chunk_sz || chunk_sz == 1) /* we are done */ | ||
| 3512 | return off + cnt; | ||
| 3513 | } | ||
| 3514 | return off; | ||
| 3515 | } | ||
| 3516 | |||
| 3517 | static __always_inline int __bpf_dynptr_copy(const struct bpf_dynptr *dptr, u32 doff, | ||
| 3518 | u32 size, const void *unsafe_src, | ||
| 3519 | copy_fn_t copy_fn, struct task_struct *tsk) | ||
| 3520 | { | ||
| 3521 | struct bpf_dynptr_kern *dst; | ||
| 3522 | void *dst_slice; | ||
| 3523 | char buf[256]; | ||
| 3524 | u32 off, chunk_sz; | ||
| 3525 | int err; | ||
| 3526 | |||
| 3527 | dst_slice = bpf_dynptr_slice_rdwr(dptr, doff, NULL, size); | ||
| 3528 | if (likely(dst_slice)) | ||
| 3529 | return copy_fn(dst_slice, unsafe_src, size, tsk); | ||
| 3530 | |||
| 3531 | dst = (struct bpf_dynptr_kern *)dptr; | ||
| 3532 | if (bpf_dynptr_check_off_len(dst, doff, size)) | ||
| 3533 | return -E2BIG; | ||
| 3534 | |||
| 3535 | for (off = 0; off < size; off += chunk_sz) { | ||
| 3536 | chunk_sz = min_t(u32, sizeof(buf), size - off); | ||
| 3537 | err = copy_fn(buf, unsafe_src + off, chunk_sz, tsk); | ||
| 3538 | if (err) | ||
| 3539 | return err; | ||
| 3540 | err = __bpf_dynptr_write(dst, doff + off, buf, chunk_sz, 0); | ||
| 3541 | if (err) | ||
| 3542 | return err; | ||
| 3543 | } | ||
| 3544 | return 0; | ||
| 3545 | } | ||
| 3546 | |||
| 3547 | static __always_inline int copy_user_data_nofault(void *dst, const void *unsafe_src, | ||
| 3548 | u32 size, struct task_struct *tsk) | ||
| 3549 | { | ||
| 3550 | return copy_from_user_nofault(dst, (const void __user *)unsafe_src, size); | ||
| 3551 | } | ||
| 3552 | |||
| 3553 | static __always_inline int copy_user_data_sleepable(void *dst, const void *unsafe_src, | ||
| 3554 | u32 size, struct task_struct *tsk) | ||
| 3555 | { | ||
| 3556 | int ret; | ||
| 3557 | |||
| 3558 | if (!tsk) { /* Read from the current task */ | ||
| 3559 | ret = copy_from_user(dst, (const void __user *)unsafe_src, size); | ||
| 3560 | if (ret) | ||
| 3561 | return -EFAULT; | ||
| 3562 | return 0; | ||
| 3563 | } | ||
| 3564 | |||
| 3565 | ret = access_process_vm(tsk, (unsigned long)unsafe_src, dst, size, 0); | ||
| 3566 | if (ret != size) | ||
| 3567 | return -EFAULT; | ||
| 3568 | return 0; | ||
| 3569 | } | ||
| 3570 | |||
| 3571 | static __always_inline int copy_kernel_data_nofault(void *dst, const void *unsafe_src, | ||
| 3572 | u32 size, struct task_struct *tsk) | ||
| 3573 | { | ||
| 3574 | return copy_from_kernel_nofault(dst, unsafe_src, size); | ||
| 3575 | } | ||
| 3576 | |||
| 3577 | static __always_inline int copy_user_str_nofault(void *dst, const void *unsafe_src, | ||
| 3578 | u32 size, struct task_struct *tsk) | ||
| 3579 | { | ||
| 3580 | return strncpy_from_user_nofault(dst, (const void __user *)unsafe_src, size); | ||
| 3581 | } | ||
| 3582 | |||
| 3583 | static __always_inline int copy_user_str_sleepable(void *dst, const void *unsafe_src, | ||
| 3584 | u32 size, struct task_struct *tsk) | ||
| 3585 | { | ||
| 3586 | int ret; | ||
| 3587 | |||
| 3588 | if (unlikely(size == 0)) | ||
| 3589 | return 0; | ||
| 3590 | |||
| 3591 | if (tsk) { | ||
| 3592 | ret = copy_remote_vm_str(tsk, (unsigned long)unsafe_src, dst, size, 0); | ||
| 3593 | } else { | ||
| 3594 | ret = strncpy_from_user(dst, (const void __user *)unsafe_src, size - 1); | ||
| 3595 | /* strncpy_from_user does not guarantee NUL termination */ | ||
| 3596 | if (ret >= 0) | ||
| 3597 | ((char *)dst)[ret] = '\0'; | ||
| 3598 | } | ||
| 3599 | |||
| 3600 | if (ret < 0) | ||
| 3601 | return ret; | ||
| 3602 | return ret + 1; | ||
| 3603 | } | ||
| 3604 | |||
| 3605 | static __always_inline int copy_kernel_str_nofault(void *dst, const void *unsafe_src, | ||
| 3606 | u32 size, struct task_struct *tsk) | ||
| 3607 | { | ||
| 3608 | return strncpy_from_kernel_nofault(dst, unsafe_src, size); | ||
| 3609 | } | ||
| 3610 | |||
| 3568 | __bpf_kfunc_start_defs(); | 3611 | __bpf_kfunc_start_defs(); |
| 3569 | 3612 | ||
| 3570 | __bpf_kfunc int bpf_send_signal_task(struct task_struct *task, int sig, enum pid_type type, | 3613 | __bpf_kfunc int bpf_send_signal_task(struct task_struct *task, int sig, enum pid_type type, |
| @@ -3576,4 +3619,62 @@ __bpf_kfunc int bpf_send_signal_task(struct task_struct *task, int sig, enum pid · 5 unmodified lines | |||
| 3576 | return bpf_send_signal_common(sig, type, task, value); | 3619 | return bpf_send_signal_common(sig, type, task, value); |
| 3577 | } | 3620 | } |
| 3578 | 3621 | ||
| 3622 | __bpf_kfunc int bpf_probe_read_user_dynptr(struct bpf_dynptr *dptr, u32 off, | ||
| 3623 | u32 size, const void __user *unsafe_ptr__ign) | ||
| 3624 | { | ||
| 3625 | return __bpf_dynptr_copy(dptr, off, size, (const void *)unsafe_ptr__ign, | ||
| 3626 | copy_user_data_nofault, NULL); | ||
| 3627 | } | ||
| 3628 | |||
| 3629 | __bpf_kfunc int bpf_probe_read_kernel_dynptr(struct bpf_dynptr *dptr, u32 off, | ||
| 3630 | u32 size, const void *unsafe_ptr__ign) | ||
| 3631 | { | ||
| 3632 | return __bpf_dynptr_copy(dptr, off, size, unsafe_ptr__ign, | ||
| 3633 | copy_kernel_data_nofault, NULL); | ||
| 3634 | } | ||
| 3635 | |||
| 3636 | __bpf_kfunc int bpf_probe_read_user_str_dynptr(struct bpf_dynptr *dptr, u32 off, | ||
| 3637 | u32 size, const void __user *unsafe_ptr__ign) | ||
| 3638 | { | ||
| 3639 | return __bpf_dynptr_copy_str(dptr, off, size, (const void *)unsafe_ptr__ign, | ||
| 3640 | copy_user_str_nofault, NULL); | ||
| 3641 | } | ||
| 3642 | |||
| 3643 | __bpf_kfunc int bpf_probe_read_kernel_str_dynptr(struct bpf_dynptr *dptr, u32 off, | ||
| 3644 | u32 size, const void *unsafe_ptr__ign) | ||
| 3645 | { | ||
| 3646 | return __bpf_dynptr_copy_str(dptr, off, size, unsafe_ptr__ign, | ||
| 3647 | copy_kernel_str_nofault, NULL); | ||
| 3648 | } | ||
| 3649 | |||
| 3650 | __bpf_kfunc int bpf_copy_from_user_dynptr(struct bpf_dynptr *dptr, u32 off, | ||
| 3651 | u32 size, const void __user *unsafe_ptr__ign) | ||
| 3652 | { | ||
| 3653 | return __bpf_dynptr_copy(dptr, off, size, (const void *)unsafe_ptr__ign, | ||
| 3654 | copy_user_data_sleepable, NULL); | ||
| 3655 | } | ||
| 3656 | |||
| 3657 | __bpf_kfunc int bpf_copy_from_user_str_dynptr(struct bpf_dynptr *dptr, u32 off, | ||
| 3658 | u32 size, const void __user *unsafe_ptr__ign) | ||
| 3659 | { | ||
| 3660 | return __bpf_dynptr_copy_str(dptr, off, size, (const void *)unsafe_ptr__ign, | ||
| 3661 | copy_user_str_sleepable, NULL); | ||
| 3662 | } | ||
| 3663 | |||
| 3664 | __bpf_kfunc int bpf_copy_from_user_task_dynptr(struct bpf_dynptr *dptr, u32 off, | ||
| 3665 | u32 size, const void __user *unsafe_ptr__ign, | ||
| 3666 | struct task_struct *tsk) | ||
| 3667 | { | ||
| 3668 | return __bpf_dynptr_copy(dptr, off, size, (const void *)unsafe_ptr__ign, | ||
| 3669 | copy_user_data_sleepable, tsk); | ||
| 3670 | } | ||
| 3671 | |||
| 3672 | __bpf_kfunc int bpf_copy_from_user_task_str_dynptr(struct bpf_dynptr *dptr, u32 off, | ||
| 3673 | u32 size, const void __user *unsafe_ptr__ign, | ||
| 3674 | struct task_struct *tsk) | ||
| 3675 | { | ||
| 3676 | return __bpf_dynptr_copy_str(dptr, off, size, (const void *)unsafe_ptr__ign, | ||
| 3677 | copy_user_str_sleepable, tsk); | ||
| 3678 | } | ||
| 3679 | |||
| 3579 | __bpf_kfunc_end_defs(); | 3680 | __bpf_kfunc_end_defs(); |
| @@ -1489,7 +1489,7 @@ int bpf_get_uprobe_info(const struct perf_event *event, u32 *fd_type, · 1488 unmodified lines | |||
| 1489 | : BPF_FD_TYPE_UPROBE; | 1489 | : BPF_FD_TYPE_UPROBE; |
| 1490 | *filename = tu->filename; | 1490 | *filename = tu->filename; |
| 1491 | *probe_offset = tu->offset; | 1491 | *probe_offset = tu->offset; |
| 1492 | *probe_addr = 0; | 1492 | *probe_addr = tu->ref_ctr_offset; |
| 1493 | return 0; | 1493 | return 0; |
| 1494 | } | 1494 | } |
| 1495 | #endif /* CONFIG_PERF_EVENTS */ | 1495 | #endif /* CONFIG_PERF_EVENTS */ |
| @@ -569,6 +569,11 @@ __bpf_kfunc u32 bpf_fentry_test9(u32 *a) · 568 unmodified lines | |||
| 569 | return *a; | 569 | return *a; |
| 570 | } | 570 | } |
| 571 | 571 | ||
| 572 | int noinline bpf_fentry_test10(const void *a) | ||
| 573 | { | ||
| 574 | return (long)a; | ||
| 575 | } | ||
| 576 | |||
| 572 | void noinline bpf_fentry_test_sinfo(struct skb_shared_info *sinfo) | 577 | void noinline bpf_fentry_test_sinfo(struct skb_shared_info *sinfo) |
| 573 | { | 578 | { |
| 574 | } | 579 | } |
| @@ -699,7 +704,8 @@ int bpf_prog_test_run_tracing(struct bpf_prog *prog, · 124 unmodified lines | |||
| 699 | bpf_fentry_test6(16, (void *)17, 18, 19, (void *)20, 21) != 111 || | 704 | bpf_fentry_test6(16, (void *)17, 18, 19, (void *)20, 21) != 111 || |
| 700 | bpf_fentry_test7((struct bpf_fentry_test_t *)0) != 0 || | 705 | bpf_fentry_test7((struct bpf_fentry_test_t *)0) != 0 || |
| 701 | bpf_fentry_test8(&arg) != 0 || | 706 | bpf_fentry_test8(&arg) != 0 || |
| 702 | bpf_fentry_test9(&retval) != 0) | 707 | bpf_fentry_test9(&retval) != 0 || |
| 708 | bpf_fentry_test10((void *)0) != 0) | ||
| 703 | goto out; | 709 | goto out; |
| 704 | break; | 710 | break; |
| 705 | case BPF_MODIFY_RETURN: | 711 | case BPF_MODIFY_RETURN: |
| @@ -8023,10 +8023,6 @@ sock_filter_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 8022 unmodified lines | |||
| 8023 | if (func_proto) | 8023 | if (func_proto) |
| 8024 | return func_proto; | 8024 | return func_proto; |
| 8025 | 8025 | ||
| 8026 | func_proto = cgroup_current_func_proto(func_id, prog); | ||
| 8027 | if (func_proto) | ||
| 8028 | return func_proto; | ||
| 8029 | |||
| 8030 | switch (func_id) { | 8026 | switch (func_id) { |
| 8031 | case BPF_FUNC_get_socket_cookie: | 8027 | case BPF_FUNC_get_socket_cookie: |
| 8032 | return &bpf_get_socket_cookie_sock_proto; | 8028 | return &bpf_get_socket_cookie_sock_proto; |
| @@ -8052,10 +8048,6 @@ sock_addr_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 19 unmodified lines | |||
| 8052 | if (func_proto) | 8048 | if (func_proto) |
| 8053 | return func_proto; | 8049 | return func_proto; |
| 8054 | 8050 | ||
| 8055 | func_proto = cgroup_current_func_proto(func_id, prog); | ||
| 8056 | if (func_proto) | ||
| 8057 | return func_proto; | ||
| 8058 | |||
| 8059 | switch (func_id) { | 8051 | switch (func_id) { |
| 8060 | case BPF_FUNC_bind: | 8052 | case BPF_FUNC_bind: |
| 8061 | switch (prog->expected_attach_type) { | 8053 | switch (prog->expected_attach_type) { |
| @@ -8489,18 +8481,12 @@ sk_msg_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) · 427 unmodified lines | |||
| 8489 | return &bpf_msg_pop_data_proto; | 8481 | return &bpf_msg_pop_data_proto; |
| 8490 | case BPF_FUNC_perf_event_output: | 8482 | case BPF_FUNC_perf_event_output: |
| 8491 | return &bpf_event_output_data_proto; | 8483 | return &bpf_event_output_data_proto; |
| 8492 | case BPF_FUNC_get_current_uid_gid: | ||
| 8493 | return &bpf_get_current_uid_gid_proto; | ||
| 8494 | case BPF_FUNC_sk_storage_get: | 8484 | case BPF_FUNC_sk_storage_get: |
| 8495 | return &bpf_sk_storage_get_proto; | 8485 | return &bpf_sk_storage_get_proto; |
| 8496 | case BPF_FUNC_sk_storage_delete: | 8486 | case BPF_FUNC_sk_storage_delete: |
| 8497 | return &bpf_sk_storage_delete_proto; | 8487 | return &bpf_sk_storage_delete_proto; |
| 8498 | case BPF_FUNC_get_netns_cookie: | 8488 | case BPF_FUNC_get_netns_cookie: |
| 8499 | return &bpf_get_netns_cookie_sk_msg_proto; | 8489 | return &bpf_get_netns_cookie_sk_msg_proto; |
| 8500 | #ifdef CONFIG_CGROUP_NET_CLASSID | ||
| 8501 | case BPF_FUNC_get_cgroup_classid: | ||
| 8502 | return &bpf_get_cgroup_classid_curr_proto; | ||
| 8503 | #endif | ||
| 8504 | default: | 8490 | default: |
| 8505 | return bpf_sk_base_func_proto(func_id, prog); | 8491 | return bpf_sk_base_func_proto(func_id, prog); |
| 8506 | } | 8492 | } |
| @@ -530,16 +530,22 @@ static int sk_psock_skb_ingress_enqueue(struct sk_buff *skb, · 529 unmodified lines | |||
| 530 | u32 off, u32 len, | 530 | u32 off, u32 len, |
| 531 | struct sk_psock *psock, | 531 | struct sk_psock *psock, |
| 532 | struct sock *sk, | 532 | struct sock *sk, |
| 533 | struct sk_msg *msg) | 533 | struct sk_msg *msg, |
| 534 | bool take_ref) | ||
| 534 | { | 535 | { |
| 535 | int num_sge, copied; | 536 | int num_sge, copied; |
| 536 | 537 | ||
| 538 | /* skb_to_sgvec will fail when the total number of fragments in | ||
| 539 | * frag_list and frags exceeds MAX_MSG_FRAGS. For example, the | ||
| 540 | * caller may aggregate multiple skbs. | ||
| 541 | */ | ||
| 537 | num_sge = skb_to_sgvec(skb, msg->sg.data, off, len); | 542 | num_sge = skb_to_sgvec(skb, msg->sg.data, off, len); |
| 538 | if (num_sge < 0) { | 543 | if (num_sge < 0) { |
| 539 | /* skb linearize may fail with ENOMEM, but lets simply try again | 544 | /* skb linearize may fail with ENOMEM, but lets simply try again |
| 540 | * later if this happens. Under memory pressure we don't want to | 545 | * later if this happens. Under memory pressure we don't want to |
| 541 | * drop the skb. We need to linearize the skb so that the mapping | 546 | * drop the skb. We need to linearize the skb so that the mapping |
| 542 | * in skb_to_sgvec can not error. | 547 | * in skb_to_sgvec can not error. |
| 548 | * Note that skb_linearize requires the skb not to be shared. | ||
| 543 | */ | 549 | */ |
| 544 | if (skb_linearize(skb)) | 550 | if (skb_linearize(skb)) |
| 545 | return -EAGAIN; | 551 | return -EAGAIN; |
| @@ -556,7 +562,7 @@ static int sk_psock_skb_ingress_enqueue(struct sk_buff *skb, · 10 unmodified lines | |||
| 556 | msg->sg.start = 0; | 562 | msg->sg.start = 0; |
| 557 | msg->sg.size = copied; | 563 | msg->sg.size = copied; |
| 558 | msg->sg.end = num_sge; | 564 | msg->sg.end = num_sge; |
| 559 | msg->skb = skb; | 565 | msg->skb = take_ref ? skb_get(skb) : skb; |
| 560 | 566 | ||
| 561 | sk_psock_queue_msg(psock, msg); | 567 | sk_psock_queue_msg(psock, msg); |
| 562 | sk_psock_data_ready(sk, psock); | 568 | sk_psock_data_ready(sk, psock); |
| @@ -564,7 +570,7 @@ static int sk_psock_skb_ingress_enqueue(struct sk_buff *skb, · 1 unmodified line | |||
| 564 | } | 570 | } |
| 565 | 571 | ||
| 566 | static int sk_psock_skb_ingress_self(struct sk_psock *psock, struct sk_buff *skb, | 572 | static int sk_psock_skb_ingress_self(struct sk_psock *psock, struct sk_buff *skb, |
| 567 | u32 off, u32 len); | 573 | u32 off, u32 len, bool take_ref); |
| 568 | 574 | ||
| 569 | static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb, | 575 | static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb, |
| 570 | u32 off, u32 len) | 576 | u32 off, u32 len) |
| @@ -578,7 +584,7 @@ static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb, · 7 unmodified lines | |||
| 578 | * correctly. | 584 | * correctly. |
| 579 | */ | 585 | */ |
| 580 | if (unlikely(skb->sk == sk)) | 586 | if (unlikely(skb->sk == sk)) |
| 581 | return sk_psock_skb_ingress_self(psock, skb, off, len); | 587 | return sk_psock_skb_ingress_self(psock, skb, off, len, true); |
| 582 | msg = sk_psock_create_ingress_msg(sk, skb); | 588 | msg = sk_psock_create_ingress_msg(sk, skb); |
| 583 | if (!msg) | 589 | if (!msg) |
| 584 | return -EAGAIN; | 590 | return -EAGAIN; |
| @@ -590,7 +596,7 @@ static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb, · 5 unmodified lines | |||
| 590 | * into user buffers. | 596 | * into user buffers. |
| 591 | */ | 597 | */ |
| 592 | skb_set_owner_r(skb, sk); | 598 | skb_set_owner_r(skb, sk); |
| 593 | err = sk_psock_skb_ingress_enqueue(skb, off, len, psock, sk, msg); | 599 | err = sk_psock_skb_ingress_enqueue(skb, off, len, psock, sk, msg, true); |
| 594 | if (err < 0) | 600 | if (err < 0) |
| 595 | kfree(msg); | 601 | kfree(msg); |
| 596 | return err; | 602 | return err; |
| @@ -601,7 +607,7 @@ static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb, · 4 unmodified lines | |||
| 601 | * because the skb is already accounted for here. | 607 | * because the skb is already accounted for here. |
| 602 | */ | 608 | */ |
| 603 | static int sk_psock_skb_ingress_self(struct sk_psock *psock, struct sk_buff *skb, | 609 | static int sk_psock_skb_ingress_self(struct sk_psock *psock, struct sk_buff *skb, |
| 604 | u32 off, u32 len) | 610 | u32 off, u32 len, bool take_ref) |
| 605 | { | 611 | { |
| 606 | struct sk_msg *msg = alloc_sk_msg(GFP_ATOMIC); | 612 | struct sk_msg *msg = alloc_sk_msg(GFP_ATOMIC); |
| 607 | struct sock *sk = psock->sk; | 613 | struct sock *sk = psock->sk; |
| @@ -610,7 +616,7 @@ static int sk_psock_skb_ingress_self(struct sk_psock *psock, struct sk_buff *skb · 2 unmodified lines | |||
| 610 | if (unlikely(!msg)) | 616 | if (unlikely(!msg)) |
| 611 | return -EAGAIN; | 617 | return -EAGAIN; |
| 612 | skb_set_owner_r(skb, sk); | 618 | skb_set_owner_r(skb, sk); |
| 613 | err = sk_psock_skb_ingress_enqueue(skb, off, len, psock, sk, msg); | 619 | err = sk_psock_skb_ingress_enqueue(skb, off, len, psock, sk, msg, take_ref); |
| 614 | if (err < 0) | 620 | if (err < 0) |
| 615 | kfree(msg); | 621 | kfree(msg); |
| 616 | return err; | 622 | return err; |
| @@ -619,18 +625,13 @@ static int sk_psock_skb_ingress_self(struct sk_psock *psock, struct sk_buff *skb · 2 unmodified lines | |||
| 619 | static int sk_psock_handle_skb(struct sk_psock *psock, struct sk_buff *skb, | 625 | static int sk_psock_handle_skb(struct sk_psock *psock, struct sk_buff *skb, |
| 620 | u32 off, u32 len, bool ingress) | 626 | u32 off, u32 len, bool ingress) |
| 621 | { | 627 | { |
| 622 | int err = 0; | ||
| 623 | |||
| 624 | if (!ingress) { | 628 | if (!ingress) { |
| 625 | if (!sock_writeable(psock->sk)) | 629 | if (!sock_writeable(psock->sk)) |
| 626 | return -EAGAIN; | 630 | return -EAGAIN; |
| 627 | return skb_send_sock(psock->sk, skb, off, len); | 631 | return skb_send_sock(psock->sk, skb, off, len); |
| 628 | } | 632 | } |
| 629 | skb_get(skb); | 633 | |
| 630 | err = sk_psock_skb_ingress(psock, skb, off, len); | 634 | return sk_psock_skb_ingress(psock, skb, off, len); |
| 631 | if (err < 0) | ||
| 632 | kfree_skb(skb); | ||
| 633 | return err; | ||
| 634 | } | 635 | } |
| 635 | 636 | ||
| 636 | static void sk_psock_skb_state(struct sk_psock *psock, | 637 | static void sk_psock_skb_state(struct sk_psock *psock, |
| @@ -655,12 +656,14 @@ static void sk_psock_backlog(struct work_struct *work) · 18 unmodified lines | |||
| 655 | bool ingress; | 656 | bool ingress; |
| 656 | int ret; | 657 | int ret; |
| 657 | 658 | ||
| 659 | /* Increment the psock refcnt to synchronize with close(fd) path in | ||
| 660 | * sock_map_close(), ensuring we wait for backlog thread completion | ||
| 661 | * before sk_socket freed. If refcnt increment fails, it indicates | ||
| 662 | * sock_map_close() completed with sk_socket potentially already freed. | ||
| 663 | */ | ||
| 664 | if (!sk_psock_get(psock->sk)) | ||
| 665 | return; | ||
| 658 | mutex_lock(&psock->work_mutex); | 666 | mutex_lock(&psock->work_mutex); |
| 659 | if (unlikely(state->len)) { | ||
| 660 | len = state->len; | ||
| 661 | off = state->off; | ||
| 662 | } | ||
| 663 | |||
| 664 | while ((skb = skb_peek(&psock->ingress_skb))) { | 667 | while ((skb = skb_peek(&psock->ingress_skb))) { |
| 665 | len = skb->len; | 668 | len = skb->len; |
| 666 | off = 0; | 669 | off = 0; |
| @@ -670,6 +673,13 @@ static void sk_psock_backlog(struct work_struct *work) · 3 unmodified lines | |||
| 670 | off = stm->offset; | 673 | off = stm->offset; |
| 671 | len = stm->full_len; | 674 | len = stm->full_len; |
| 672 | } | 675 | } |
| 676 | |||
| 677 | /* Resume processing from previous partial state */ | ||
| 678 | if (unlikely(state->len)) { | ||
| 679 | len = state->len; | ||
| 680 | off = state->off; | ||
| 681 | } | ||
| 682 | |||
| 673 | ingress = skb_bpf_ingress(skb); | 683 | ingress = skb_bpf_ingress(skb); |
| 674 | skb_bpf_redirect_clear(skb); | 684 | skb_bpf_redirect_clear(skb); |
| 675 | do { | 685 | do { |
| @@ -680,7 +690,8 @@ static void sk_psock_backlog(struct work_struct *work) · 4 unmodified lines | |||
| 680 | if (ret <= 0) { | 690 | if (ret <= 0) { |
| 681 | if (ret == -EAGAIN) { | 691 | if (ret == -EAGAIN) { |
| 682 | sk_psock_skb_state(psock, state, len, off); | 692 | sk_psock_skb_state(psock, state, len, off); |
| 683 | 693 | /* Restore redir info we cleared before */ | |
| 694 | skb_bpf_set_redir(skb, psock->sk, ingress); | ||
| 684 | /* Delay slightly to prioritize any | 695 | /* Delay slightly to prioritize any |
| 685 | * other work that might be here. | 696 | * other work that might be here. |
| 686 | */ | 697 | */ |
| @@ -697,11 +708,14 @@ static void sk_psock_backlog(struct work_struct *work) · 10 unmodified lines | |||
| 697 | len -= ret; | 708 | len -= ret; |
| 698 | } while (len); | 709 | } while (len); |
| 699 | 710 | ||
| 711 | /* The entire skb sent, clear state */ | ||
| 712 | sk_psock_skb_state(psock, state, 0, 0); | ||
| 700 | skb = skb_dequeue(&psock->ingress_skb); | 713 | skb = skb_dequeue(&psock->ingress_skb); |
| 701 | kfree_skb(skb); | 714 | kfree_skb(skb); |
| 702 | } | 715 | } |
| 703 | end: | 716 | end: |
| 704 | mutex_unlock(&psock->work_mutex); | 717 | mutex_unlock(&psock->work_mutex); |
| 718 | sk_psock_put(psock->sk, psock); | ||
| 705 | } | 719 | } |
| 706 | 720 | ||
| 707 | struct sk_psock *sk_psock_init(struct sock *sk, int node) | 721 | struct sk_psock *sk_psock_init(struct sock *sk, int node) |
| @@ -1014,7 +1028,7 @@ static int sk_psock_verdict_apply(struct sk_psock *psock, struct sk_buff *skb, · 306 unmodified lines | |||
| 1014 | off = stm->offset; | 1028 | off = stm->offset; |
| 1015 | len = stm->full_len; | 1029 | len = stm->full_len; |
| 1016 | } | 1030 | } |
| 1017 | err = sk_psock_skb_ingress_self(psock, skb, off, len); | 1031 | err = sk_psock_skb_ingress_self(psock, skb, off, len, false); |
| 1018 | } | 1032 | } |
| 1019 | if (err < 0) { | 1033 | if (err < 0) { |
| 1020 | spin_lock_bh(&psock->ingress_lock); | 1034 | spin_lock_bh(&psock->ingress_lock); |
| @@ -908,6 +908,13 @@ more_data: · 907 unmodified lines | |||
| 908 | &msg_redir, send, flags); | 908 | &msg_redir, send, flags); |
| 909 | lock_sock(sk); | 909 | lock_sock(sk); |
| 910 | if (err < 0) { | 910 | if (err < 0) { |
| 911 | /* Regardless of whether the data represented by | ||
| 912 | * msg_redir is sent successfully, we have already | ||
| 913 | * uncharged it via sk_msg_return_zero(). The | ||
| 914 | * msg->sg.size represents the remaining unprocessed | ||
| 915 | * data, which needs to be uncharged here. | ||
| 916 | */ | ||
| 917 | sk_mem_uncharge(sk, msg->sg.size); | ||
| 911 | *copied -= sk_msg_free_nocharge(sk, &msg_redir); | 918 | *copied -= sk_msg_free_nocharge(sk, &msg_redir); |
| 912 | msg->sg.size = 0; | 919 | msg->sg.size = 0; |
| 913 | } | 920 | } |
| @@ -1120,9 +1127,13 @@ alloc_encrypted: · 206 unmodified lines | |||
| 1120 | num_async++; | 1127 | num_async++; |
| 1121 | else if (ret == -ENOMEM) | 1128 | else if (ret == -ENOMEM) |
| 1122 | goto wait_for_memory; | 1129 | goto wait_for_memory; |
| 1123 | else if (ctx->open_rec && ret == -ENOSPC) | 1130 | else if (ctx->open_rec && ret == -ENOSPC) { |
| 1131 | if (msg_pl->cork_bytes) { | ||
| 1132 | ret = 0; | ||
| 1133 | goto send_end; | ||
| 1134 | } | ||
| 1124 | goto rollback_iter; | 1135 | goto rollback_iter; |
| 1125 | else if (ret != -EAGAIN) | 1136 | } else if (ret != -EAGAIN) |
| 1126 | goto send_end; | 1137 | goto send_end; |
| 1127 | } | 1138 | } |
| 1128 | continue; | 1139 | continue; |
| @@ -23,6 +23,8 @@ else · 22 unmodified lines | |||
| 23 | # Switch to using --btf_features for v1.26 and later. | 23 | # Switch to using --btf_features for v1.26 and later. |
| 24 | pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j$(JOBS) --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func,decl_tag_kfuncs | 24 | pahole-flags-$(call test-ge, $(pahole-ver), 126) = -j$(JOBS) --btf_features=encode_force,var,float,enum64,decl_tag,type_tag,optimized_func,consistent_func,decl_tag_kfuncs |
| 25 | 25 | ||
| 26 | pahole-flags-$(call test-ge, $(pahole-ver), 130) += --btf_features=attributes | ||
| 27 | |||
| 26 | ifneq ($(KBUILD_EXTMOD),) | 28 | ifneq ($(KBUILD_EXTMOD),) |
| 27 | module-pahole-flags-$(call test-ge, $(pahole-ver), 128) += --btf_features=distilled_base | 29 | module-pahole-flags-$(call test-ge, $(pahole-ver), 128) += --btf_features=distilled_base |
| 28 | endif | 30 | endif |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | |||
| 8 | from __future__ import print_function | 8 | from __future__ import print_function |
| 9 | 9 | ||
| 10 | import argparse | 10 | import argparse |
| 11 | import json | ||
| 11 | import re | 12 | import re |
| 12 | import sys, os | 13 | import sys, os |
| 13 | import subprocess | 14 | import subprocess |
| @@ -37,11 +38,17 @@ class APIElement(object): · 23 unmodified lines | |||
| 37 | @desc: textual description of the symbol | 38 | @desc: textual description of the symbol |
| 38 | @ret: (optional) description of any associated return value | 39 | @ret: (optional) description of any associated return value |
| 39 | """ | 40 | """ |
| 40 | def __init__(self, proto='', desc='', ret='', attrs=[]): | 41 | def __init__(self, proto='', desc='', ret=''): |
| 41 | self.proto = proto | 42 | self.proto = proto |
| 42 | self.desc = desc | 43 | self.desc = desc |
| 43 | self.ret = ret | 44 | self.ret = ret |
| 44 | self.attrs = attrs | 45 | |
| 46 | def to_dict(self): | ||
| 47 | return { | ||
| 48 | 'proto': self.proto, | ||
| 49 | 'desc': self.desc, | ||
| 50 | 'ret': self.ret | ||
| 51 | } | ||
| 45 | 52 | ||
| 46 | 53 | ||
| 47 | class Helper(APIElement): | 54 | class Helper(APIElement): |
| @@ -51,8 +58,9 @@ class Helper(APIElement): · 3 unmodified lines | |||
| 51 | @desc: textual description of the helper function | 58 | @desc: textual description of the helper function |
| 52 | @ret: description of the return value of the helper function | 59 | @ret: description of the return value of the helper function |
| 53 | """ | 60 | """ |
| 54 | def __init__(self, *args, **kwargs): | 61 | def __init__(self, proto='', desc='', ret='', attrs=[]): |
| 55 | super().__init__(*args, **kwargs) | 62 | super().__init__(proto, desc, ret) |
| 63 | self.attrs = attrs | ||
| 56 | self.enum_val = None | 64 | self.enum_val = None |
| 57 | 65 | ||
| 58 | def proto_break_down(self): | 66 | def proto_break_down(self): |
| @@ -81,6 +89,12 @@ class Helper(APIElement): · 22 unmodified lines | |||
| 81 | 89 | ||
| 82 | return res | 90 | return res |
| 83 | 91 | ||
| 92 | def to_dict(self): | ||
| 93 | d = super().to_dict() | ||
| 94 | d["attrs"] = self.attrs | ||
| 95 | d.update(self.proto_break_down()) | ||
| 96 | return d | ||
| 97 | |||
| 84 | 98 | ||
| 85 | ATTRS = { | 99 | ATTRS = { |
| 86 | '__bpf_fastcall': 'bpf_fastcall' | 100 | '__bpf_fastcall': 'bpf_fastcall' |
| @@ -675,7 +689,7 @@ COMMANDS · 588 unmodified lines | |||
| 675 | self.print_elem(command) | 689 | self.print_elem(command) |
| 676 | 690 | ||
| 677 | 691 | ||
| 678 | class PrinterHelpers(Printer): | 692 | class PrinterHelpersHeader(Printer): |
| 679 | """ | 693 | """ |
| 680 | A printer for dumping collected information about helpers as C header to | 694 | A printer for dumping collected information about helpers as C header to |
| 681 | be included from BPF program. | 695 | be included from BPF program. |
| @@ -896,6 +910,43 @@ class PrinterHelpers(Printer): · 214 unmodified lines | |||
| 896 | print(') = (void *) %d;' % helper.enum_val) | 910 | print(') = (void *) %d;' % helper.enum_val) |
| 897 | print('') | 911 | print('') |
| 898 | 912 | ||
| 913 | |||
| 914 | class PrinterHelpersJSON(Printer): | ||
| 915 | """ | ||
| 916 | A printer for dumping collected information about helpers as a JSON file. | ||
| 917 | @parser: A HeaderParser with Helper objects | ||
| 918 | """ | ||
| 919 | |||
| 920 | def __init__(self, parser): | ||
| 921 | self.elements = parser.helpers | ||
| 922 | self.elem_number_check( | ||
| 923 | parser.desc_unique_helpers, | ||
| 924 | parser.define_unique_helpers, | ||
| 925 | "helper", | ||
| 926 | "___BPF_FUNC_MAPPER", | ||
| 927 | ) | ||
| 928 | |||
| 929 | def print_all(self): | ||
| 930 | helper_dicts = [helper.to_dict() for helper in self.elements] | ||
| 931 | out_dict = {'helpers': helper_dicts} | ||
| 932 | print(json.dumps(out_dict, indent=4)) | ||
| 933 | |||
| 934 | |||
| 935 | class PrinterSyscallJSON(Printer): | ||
| 936 | """ | ||
| 937 | A printer for dumping collected syscall information as a JSON file. | ||
| 938 | @parser: A HeaderParser with APIElement objects | ||
| 939 | """ | ||
| 940 | |||
| 941 | def __init__(self, parser): | ||
| 942 | self.elements = parser.commands | ||
| 943 | self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd') | ||
| 944 | |||
| 945 | def print_all(self): | ||
| 946 | syscall_dicts = [syscall.to_dict() for syscall in self.elements] | ||
| 947 | out_dict = {'syscall': syscall_dicts} | ||
| 948 | print(json.dumps(out_dict, indent=4)) | ||
| 949 | |||
| 899 | ############################################################################### | 950 | ############################################################################### |
| 900 | 951 | ||
| 901 | # If script is launched from scripts/ from kernel tree and can access | 952 | # If script is launched from scripts/ from kernel tree and can access |
| @@ -905,9 +956,17 @@ script = os.path.abspath(sys.argv[0]) · 3 unmodified lines | |||
| 905 | linuxRoot = os.path.dirname(os.path.dirname(script)) | 956 | linuxRoot = os.path.dirname(os.path.dirname(script)) |
| 906 | bpfh = os.path.join(linuxRoot, 'include/uapi/linux/bpf.h') | 957 | bpfh = os.path.join(linuxRoot, 'include/uapi/linux/bpf.h') |
| 907 | 958 | ||
| 959 | # target -> output format -> printer | ||
| 908 | printers = { | 960 | printers = { |
| 909 | 'helpers': PrinterHelpersRST, | 961 | 'helpers': { |
| 910 | 'syscall': PrinterSyscallRST, | 962 | 'rst': PrinterHelpersRST, |
| 963 | 'json': PrinterHelpersJSON, | ||
| 964 | 'header': PrinterHelpersHeader, | ||
| 965 | }, | ||
| 966 | 'syscall': { | ||
| 967 | 'rst': PrinterSyscallRST, | ||
| 968 | 'json': PrinterSyscallJSON | ||
| 969 | }, | ||
| 911 | } | 970 | } |
| 912 | 971 | ||
| 913 | argParser = argparse.ArgumentParser(description=""" | 972 | argParser = argparse.ArgumentParser(description=""" |
| @@ -917,6 +976,8 @@ rst2man utility. · 3 unmodified lines | |||
| 917 | """) | 976 | """) |
| 918 | argParser.add_argument('--header', action='store_true', | 977 | argParser.add_argument('--header', action='store_true', |
| 919 | help='generate C header file') | 978 | help='generate C header file') |
| 979 | argParser.add_argument('--json', action='store_true', | ||
| 980 | help='generate a JSON') | ||
| 920 | if (os.path.isfile(bpfh)): | 981 | if (os.path.isfile(bpfh)): |
| 921 | argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h', | 982 | argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h', |
| 922 | default=bpfh) | 983 | default=bpfh) |
| @@ -924,17 +985,35 @@ else: · 1 unmodified line | |||
| 924 | argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h') | 985 | argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h') |
| 925 | argParser.add_argument('target', nargs='?', default='helpers', | 986 | argParser.add_argument('target', nargs='?', default='helpers', |
| 926 | choices=printers.keys(), help='eBPF API target') | 987 | choices=printers.keys(), help='eBPF API target') |
| 927 | args = argParser.parse_args() | ||
| 928 | |||
| 929 | # Parse file. | ||
| 930 | headerParser = HeaderParser(args.filename) | ||
| 931 | headerParser.run() | ||
| 932 | 988 | ||
| 933 | # Print formatted output to standard output. | 989 | def error_die(message: str): |
| 934 | if args.header: | 990 | argParser.print_usage(file=sys.stderr) |
| 935 | if args.target != 'helpers': | 991 | print('Error: {}'.format(message), file=sys.stderr) |
| 936 | raise NotImplementedError('Only helpers header generation is supported') | 992 | exit(1) |
| 937 | printer = PrinterHelpers(headerParser) | 993 | |
| 938 | else: | 994 | def parse_and_dump(): |
| 939 | printer = printers[args.target](headerParser) | 995 | args = argParser.parse_args() |
| 940 | printer.print_all() | 996 | |
| 997 | # Parse file. | ||
| 998 | headerParser = HeaderParser(args.filename) | ||
| 999 | headerParser.run() | ||
| 1000 | |||
| 1001 | if args.header and args.json: | ||
| 1002 | error_die('Use either --header or --json, not both') | ||
| 1003 | |||
| 1004 | output_format = 'rst' | ||
| 1005 | if args.header: | ||
| 1006 | output_format = 'header' | ||
| 1007 | elif args.json: | ||
| 1008 | output_format = 'json' | ||
| 1009 | |||
| 1010 | try: | ||
| 1011 | printer = printers[args.target][output_format](headerParser) | ||
| 1012 | # Print formatted output to standard output. | ||
| 1013 | printer.print_all() | ||
| 1014 | except KeyError: | ||
| 1015 | error_die('Unsupported target/format combination: "{}", "{}"' | ||
| 1016 | .format(args.target, output_format)) | ||
| 1017 | |||
| 1018 | if __name__ == "__main__": | ||
| 1019 | parse_and_dump() | ||
| @@ -31,7 +31,7 @@ PROG COMMANDS · 30 unmodified lines | |||
| 31 | | **bpftool** **prog dump xlated** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] [**visual**] }] | 31 | | **bpftool** **prog dump xlated** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] [**visual**] }] |
| 32 | | **bpftool** **prog dump jited** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] }] | 32 | | **bpftool** **prog dump jited** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] }] |
| 33 | | **bpftool** **prog pin** *PROG* *FILE* | 33 | | **bpftool** **prog pin** *PROG* *FILE* |
| 34 | | **bpftool** **prog** { **load** | **loadall** } *OBJ* *PATH* [**type** *TYPE*] [**map** { **idx** *IDX* | **name** *NAME* } *MAP*] [{ **offload_dev** | **xdpmeta_dev** } *NAME*] [**pinmaps** *MAP_DIR*] [**autoattach**] | 34 | | **bpftool** **prog** { **load** | **loadall** } *OBJ* *PATH* [**type** *TYPE*] [**map** { **idx** *IDX* | **name** *NAME* } *MAP*] [{ **offload_dev** | **xdpmeta_dev** } *NAME*] [**pinmaps** *MAP_DIR*] [**autoattach**] [**kernel_btf** *BTF_FILE*] |
| 35 | | **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*] | 35 | | **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*] |
| 36 | | **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*] | 36 | | **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*] |
| 37 | | **bpftool** **prog tracelog** | 37 | | **bpftool** **prog tracelog** |
| @@ -127,7 +127,7 @@ bpftool prog pin *PROG* *FILE* · 89 unmodified lines | |||
| 127 | Note: *FILE* must be located in *bpffs* mount. It must not contain a dot | 127 | Note: *FILE* must be located in *bpffs* mount. It must not contain a dot |
| 128 | character ('.'), which is reserved for future extensions of *bpffs*. | 128 | character ('.'), which is reserved for future extensions of *bpffs*. |
| 129 | 129 | ||
| 130 | bpftool prog { load | loadall } *OBJ* *PATH* [type *TYPE*] [map { idx *IDX* | name *NAME* } *MAP*] [{ offload_dev | xdpmeta_dev } *NAME*] [pinmaps *MAP_DIR*] [autoattach] | 130 | bpftool prog { load | loadall } *OBJ* *PATH* [type *TYPE*] [map { idx *IDX* | name *NAME* } *MAP*] [{ offload_dev | xdpmeta_dev } *NAME*] [pinmaps *MAP_DIR*] [autoattach] [kernel_btf *BTF_FILE*] |
| 131 | Load bpf program(s) from binary *OBJ* and pin as *PATH*. **bpftool prog | 131 | Load bpf program(s) from binary *OBJ* and pin as *PATH*. **bpftool prog |
| 132 | load** pins only the first program from the *OBJ* as *PATH*. **bpftool prog | 132 | load** pins only the first program from the *OBJ* as *PATH*. **bpftool prog |
| 133 | loadall** pins all programs from the *OBJ* under *PATH* directory. **type** | 133 | loadall** pins all programs from the *OBJ* under *PATH* directory. **type** |
| @@ -153,6 +153,12 @@ bpftool prog { load | loadall } *OBJ* *PATH* [type *TYPE*] [map { idx *IDX* | na · 19 unmodified lines | |||
| 153 | program does not support autoattach, bpftool falls back to regular pinning | 153 | program does not support autoattach, bpftool falls back to regular pinning |
| 154 | for that program instead. | 154 | for that program instead. |
| 155 | 155 | ||
| 156 | The **kernel_btf** option allows specifying an external BTF file to replace | ||
| 157 | the system's own vmlinux BTF file for CO-RE relocations. Note that any | ||
| 158 | other feature relying on BTF (such as fentry/fexit programs, struct_ops) | ||
| 159 | requires the BTF file for the actual kernel running on the host, often | ||
| 160 | exposed at /sys/kernel/btf/vmlinux. | ||
| 161 | |||
| 156 | Note: *PATH* must be located in *bpffs* mount. It must not contain a dot | 162 | Note: *PATH* must be located in *bpffs* mount. It must not contain a dot |
| 157 | character ('.'), which is reserved for future extensions of *bpffs*. | 163 | character ('.'), which is reserved for future extensions of *bpffs*. |
| 158 | 164 | ||
| @@ -505,13 +505,13 @@ _bpftool() · 504 unmodified lines | |||
| 505 | _bpftool_get_map_names | 505 | _bpftool_get_map_names |
| 506 | return 0 | 506 | return 0 |
| 507 | ;; | 507 | ;; |
| 508 | pinned|pinmaps) | 508 | pinned|pinmaps|kernel_btf) |
| 509 | _filedir | 509 | _filedir |
| 510 | return 0 | 510 | return 0 |
| 511 | ;; | 511 | ;; |
| 512 | *) | 512 | *) |
| 513 | COMPREPLY=( $( compgen -W "map" -- "$cur" ) ) | 513 | COMPREPLY=( $( compgen -W "map" -- "$cur" ) ) |
| 514 | _bpftool_once_attr 'type pinmaps autoattach' | 514 | _bpftool_once_attr 'type pinmaps autoattach kernel_btf' |
| 515 | _bpftool_one_of_list 'offload_dev xdpmeta_dev' | 515 | _bpftool_one_of_list 'offload_dev xdpmeta_dev' |
| 516 | return 0 | 516 | return 0 |
| 517 | ;; | 517 | ;; |
| @@ -221,7 +221,7 @@ static int cgroup_has_attached_progs(int cgroup_fd) · 220 unmodified lines | |||
| 221 | for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) { | 221 | for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) { |
| 222 | int count = count_attached_bpf_progs(cgroup_fd, cgroup_attach_types[i]); | 222 | int count = count_attached_bpf_progs(cgroup_fd, cgroup_attach_types[i]); |
| 223 | 223 | ||
| 224 | if (count < 0) | 224 | if (count < 0 && errno != EINVAL) |
| 225 | return -1; | 225 | return -1; |
| 226 | 226 | ||
| 227 | if (count > 0) { | 227 | if (count > 0) { |
| @@ -318,11 +318,11 @@ static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type, · 90 unmodified lines | |||
| 318 | 318 | ||
| 319 | static int do_show(int argc, char **argv) | 319 | static int do_show(int argc, char **argv) |
| 320 | { | 320 | { |
| 321 | enum bpf_attach_type type; | ||
| 322 | int has_attached_progs; | 321 | int has_attached_progs; |
| 323 | const char *path; | 322 | const char *path; |
| 324 | int cgroup_fd; | 323 | int cgroup_fd; |
| 325 | int ret = -1; | 324 | int ret = -1; |
| 325 | unsigned int i; | ||
| 326 | 326 | ||
| 327 | query_flags = 0; | 327 | query_flags = 0; |
| 328 | 328 | ||
| @@ -370,14 +370,14 @@ static int do_show(int argc, char **argv) · 41 unmodified lines | |||
| 370 | "AttachFlags", "Name"); | 370 | "AttachFlags", "Name"); |
| 371 | 371 | ||
| 372 | btf_vmlinux = libbpf_find_kernel_btf(); | 372 | btf_vmlinux = libbpf_find_kernel_btf(); |
| 373 | for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) { | 373 | for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) { |
| 374 | /* | 374 | /* |
| 375 | * Not all attach types may be supported, so it's expected, | 375 | * Not all attach types may be supported, so it's expected, |
| 376 | * that some requests will fail. | 376 | * that some requests will fail. |
| 377 | * If we were able to get the show for at least one | 377 | * If we were able to get the show for at least one |
| 378 | * attach type, let's return 0. | 378 | * attach type, let's return 0. |
| 379 | */ | 379 | */ |
| 380 | if (show_bpf_progs(cgroup_fd, type, 0) == 0) | 380 | if (show_bpf_progs(cgroup_fd, cgroup_attach_types[i], 0) == 0) |
| 381 | ret = 0; | 381 | ret = 0; |
| 382 | } | 382 | } |
| 383 | 383 | ||
| @@ -400,9 +400,9 @@ exit: · 16 unmodified lines | |||
| 400 | static int do_show_tree_fn(const char *fpath, const struct stat *sb, | 400 | static int do_show_tree_fn(const char *fpath, const struct stat *sb, |
| 401 | int typeflag, struct FTW *ftw) | 401 | int typeflag, struct FTW *ftw) |
| 402 | { | 402 | { |
| 403 | enum bpf_attach_type type; | ||
| 404 | int has_attached_progs; | 403 | int has_attached_progs; |
| 405 | int cgroup_fd; | 404 | int cgroup_fd; |
| 405 | unsigned int i; | ||
| 406 | 406 | ||
| 407 | if (typeflag != FTW_D) | 407 | if (typeflag != FTW_D) |
| 408 | return 0; | 408 | return 0; |
| @@ -434,8 +434,8 @@ static int do_show_tree_fn(const char *fpath, const struct stat *sb, · 25 unmodified lines | |||
| 434 | } | 434 | } |
| 435 | 435 | ||
| 436 | btf_vmlinux = libbpf_find_kernel_btf(); | 436 | btf_vmlinux = libbpf_find_kernel_btf(); |
| 437 | for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) | 437 | for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) |
| 438 | show_bpf_progs(cgroup_fd, type, ftw->level); | 438 | show_bpf_progs(cgroup_fd, cgroup_attach_types[i], ftw->level); |
| 439 | 439 | ||
| 440 | if (errno == EINVAL) | 440 | if (errno == EINVAL) |
| 441 | /* Last attach type does not support query. | 441 | /* Last attach type does not support query. |
| @@ -380,6 +380,7 @@ show_perf_event_uprobe_json(struct bpf_link_info *info, json_writer_t *wtr) · 379 unmodified lines | |||
| 380 | u64_to_ptr(info->perf_event.uprobe.file_name)); | 380 | u64_to_ptr(info->perf_event.uprobe.file_name)); |
| 381 | jsonw_uint_field(wtr, "offset", info->perf_event.uprobe.offset); | 381 | jsonw_uint_field(wtr, "offset", info->perf_event.uprobe.offset); |
| 382 | jsonw_uint_field(wtr, "cookie", info->perf_event.uprobe.cookie); | 382 | jsonw_uint_field(wtr, "cookie", info->perf_event.uprobe.cookie); |
| 383 | jsonw_uint_field(wtr, "ref_ctr_offset", info->perf_event.uprobe.ref_ctr_offset); | ||
| 383 | } | 384 | } |
| 384 | 385 | ||
| 385 | static void | 386 | static void |
| @@ -823,6 +824,8 @@ static void show_perf_event_uprobe_plain(struct bpf_link_info *info) · 437 unmodified lines | |||
| 823 | printf("%s+%#x ", buf, info->perf_event.uprobe.offset); | 824 | printf("%s+%#x ", buf, info->perf_event.uprobe.offset); |
| 824 | if (info->perf_event.uprobe.cookie) | 825 | if (info->perf_event.uprobe.cookie) |
| 825 | printf("cookie %llu ", info->perf_event.uprobe.cookie); | 826 | printf("cookie %llu ", info->perf_event.uprobe.cookie); |
| 827 | if (info->perf_event.uprobe.ref_ctr_offset) | ||
| 828 | printf("ref_ctr_offset 0x%llx ", info->perf_event.uprobe.ref_ctr_offset); | ||
| 826 | } | 829 | } |
| 827 | 830 | ||
| 828 | static void show_perf_event_tracepoint_plain(struct bpf_link_info *info) | 831 | static void show_perf_event_tracepoint_plain(struct bpf_link_info *info) |
| @@ -1681,8 +1681,17 @@ offload_dev: · 1680 unmodified lines | |||
| 1681 | } else if (is_prefix(*argv, "autoattach")) { | 1681 | } else if (is_prefix(*argv, "autoattach")) { |
| 1682 | auto_attach = true; | 1682 | auto_attach = true; |
| 1683 | NEXT_ARG(); | 1683 | NEXT_ARG(); |
| 1684 | } else if (is_prefix(*argv, "kernel_btf")) { | ||
| 1685 | NEXT_ARG(); | ||
| 1686 | |||
| 1687 | if (!REQ_ARGS(1)) | ||
| 1688 | goto err_free_reuse_maps; | ||
| 1689 | |||
| 1690 | open_opts.btf_custom_path = GET_ARG(); | ||
| 1684 | } else { | 1691 | } else { |
| 1685 | p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?", | 1692 | p_err("expected no more arguments, " |
| 1693 | "'type', 'map', 'offload_dev', 'xdpmeta_dev', 'pinmaps', " | ||
| 1694 | "'autoattach', or 'kernel_btf', got: '%s'?", | ||
| 1686 | *argv); | 1695 | *argv); |
| 1687 | goto err_free_reuse_maps; | 1696 | goto err_free_reuse_maps; |
| 1688 | } | 1697 | } |
| @@ -2474,6 +2483,7 @@ static int do_help(int argc, char **argv) · 785 unmodified lines | |||
| 2474 | " [map { idx IDX | name NAME } MAP]\\\n" | 2483 | " [map { idx IDX | name NAME } MAP]\\\n" |
| 2475 | " [pinmaps MAP_DIR]\n" | 2484 | " [pinmaps MAP_DIR]\n" |
| 2476 | " [autoattach]\n" | 2485 | " [autoattach]\n" |
| 2486 | " [kernel_btf BTF_FILE]\n" | ||
| 2477 | " %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n" | 2487 | " %1$s %2$s attach PROG ATTACH_TYPE [MAP]\n" |
| 2478 | " %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n" | 2488 | " %1$s %2$s detach PROG ATTACH_TYPE [MAP]\n" |
| 2479 | " %1$s %2$s run PROG \\\n" | 2489 | " %1$s %2$s run PROG \\\n" |
| @@ -1506,7 +1506,7 @@ union bpf_attr { · 1505 unmodified lines | |||
| 1506 | __s32 map_token_fd; | 1506 | __s32 map_token_fd; |
| 1507 | }; | 1507 | }; |
| 1508 | 1508 | ||
| 1509 | struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ | 1509 | struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */ |
| 1510 | __u32 map_fd; | 1510 | __u32 map_fd; |
| 1511 | __aligned_u64 key; | 1511 | __aligned_u64 key; |
| 1512 | union { | 1512 | union { |
| @@ -1995,11 +1995,15 @@ union bpf_attr { · 482 unmodified lines | |||
| 1995 | * long bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags) | 1995 | * long bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags) |
| 1996 | * Description | 1996 | * Description |
| 1997 | * Store *len* bytes from address *from* into the packet | 1997 | * Store *len* bytes from address *from* into the packet |
| 1998 | * associated to *skb*, at *offset*. *flags* are a combination of | 1998 | * associated to *skb*, at *offset*. The *flags* are a combination |
| 1999 | * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the | 1999 | * of the following values: |
| 2000 | * checksum for the packet after storing the bytes) and | 2000 | * |
| 2001 | * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\ | 2001 | * **BPF_F_RECOMPUTE_CSUM** |
| 2002 | * **->swhash** and *skb*\ **->l4hash** to 0). | 2002 | * Automatically update *skb*\ **->csum** after storing the |
| 2003 | * bytes. | ||
| 2004 | * **BPF_F_INVALIDATE_HASH** | ||
| 2005 | * Set *skb*\ **->hash**, *skb*\ **->swhash** and *skb*\ | ||
| 2006 | * **->l4hash** to 0. | ||
| 2003 | * | 2007 | * |
| 2004 | * A call to this helper is susceptible to change the underlying | 2008 | * A call to this helper is susceptible to change the underlying |
| 2005 | * packet buffer. Therefore, at load time, all checks on pointers | 2009 | * packet buffer. Therefore, at load time, all checks on pointers |
| @@ -2051,7 +2055,7 @@ union bpf_attr { · 45 unmodified lines | |||
| 2051 | * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and | 2055 | * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and |
| 2052 | * for updates resulting in a null checksum the value is set to | 2056 | * for updates resulting in a null checksum the value is set to |
| 2053 | * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates | 2057 | * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates |
| 2054 | * the checksum is to be computed against a pseudo-header. | 2058 | * that the modified header field is part of the pseudo-header. |
| 2055 | * | 2059 | * |
| 2056 | * This helper works in combination with **bpf_csum_diff**\ (), | 2060 | * This helper works in combination with **bpf_csum_diff**\ (), |
| 2057 | * which does not update the checksum in-place, but offers more | 2061 | * which does not update the checksum in-place, but offers more |
| @@ -6723,6 +6727,7 @@ struct bpf_link_info { · 4665 unmodified lines | |||
| 6723 | __u32 name_len; | 6727 | __u32 name_len; |
| 6724 | __u32 offset; /* offset from file_name */ | 6728 | __u32 offset; /* offset from file_name */ |
| 6725 | __u64 cookie; | 6729 | __u64 cookie; |
| 6730 | __u64 ref_ctr_offset; | ||
| 6726 | } uprobe; /* BPF_PERF_EVENT_UPROBE, BPF_PERF_EVENT_URETPROBE */ | 6731 | } uprobe; /* BPF_PERF_EVENT_UPROBE, BPF_PERF_EVENT_URETPROBE */ |
| 6727 | struct { | 6732 | struct { |
| 6728 | __aligned_u64 func_name; /* in/out */ | 6733 | __aligned_u64 func_name; /* in/out */ |
| @@ -388,7 +388,13 @@ extern void *bpf_rdonly_cast(const void *obj, __u32 btf_id) __ksym __weak; · 387 unmodified lines | |||
| 388 | #define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j | 388 | #define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j |
| 389 | #define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__) | 389 | #define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__) |
| 390 | 390 | ||
| 391 | #if defined(__clang__) && (__clang_major__ >= 19) | ||
| 392 | #define ___type(...) __typeof_unqual__(___arrow(__VA_ARGS__)) | ||
| 393 | #elif defined(__GNUC__) && (__GNUC__ >= 14) | ||
| 394 | #define ___type(...) __typeof_unqual__(___arrow(__VA_ARGS__)) | ||
| 395 | #else | ||
| 391 | #define ___type(...) typeof(___arrow(__VA_ARGS__)) | 396 | #define ___type(...) typeof(___arrow(__VA_ARGS__)) |
| 397 | #endif | ||
| 392 | 398 | ||
| 393 | #define ___read(read_fn, dst, src_type, src, accessor) \ | 399 | #define ___read(read_fn, dst, src_type, src, accessor) \ |
| 394 | read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) | 400 | read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor) |
| @@ -15,6 +15,14 @@ · 14 unmodified lines | |||
| 15 | #define __array(name, val) typeof(val) *name[] | 15 | #define __array(name, val) typeof(val) *name[] |
| 16 | #define __ulong(name, val) enum { ___bpf_concat(__unique_value, __COUNTER__) = val } name | 16 | #define __ulong(name, val) enum { ___bpf_concat(__unique_value, __COUNTER__) = val } name |
| 17 | 17 | ||
| 18 | #ifndef likely | ||
| 19 | #define likely(x) (__builtin_expect(!!(x), 1)) | ||
| 20 | #endif | ||
| 21 | |||
| 22 | #ifndef unlikely | ||
| 23 | #define unlikely(x) (__builtin_expect(!!(x), 0)) | ||
| 24 | #endif | ||
| 25 | |||
| 18 | /* | 26 | /* |
| 19 | * Helper macro to place programs, maps, license in | 27 | * Helper macro to place programs, maps, license in |
| 20 | * different sections in elf_bpf file. Section names | 28 | * different sections in elf_bpf file. Section names |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | |||
| 12 | #include <sys/utsname.h> | 12 | #include <sys/utsname.h> |
| 13 | #include <sys/param.h> | 13 | #include <sys/param.h> |
| 14 | #include <sys/stat.h> | 14 | #include <sys/stat.h> |
| 15 | #include <sys/mman.h> | ||
| 15 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
| 16 | #include <linux/err.h> | 17 | #include <linux/err.h> |
| 17 | #include <linux/btf.h> | 18 | #include <linux/btf.h> |
| @@ -120,6 +121,9 @@ struct btf { · 102 unmodified lines | |||
| 120 | /* whether base_btf should be freed in btf_free for this instance */ | 121 | /* whether base_btf should be freed in btf_free for this instance */ |
| 121 | bool owns_base; | 122 | bool owns_base; |
| 122 | 123 | ||
| 124 | /* whether raw_data is a (read-only) mmap */ | ||
| 125 | bool raw_data_is_mmap; | ||
| 126 | |||
| 123 | /* BTF object FD, if loaded into kernel */ | 127 | /* BTF object FD, if loaded into kernel */ |
| 124 | int fd; | 128 | int fd; |
| 125 | 129 | ||
| @@ -951,6 +955,17 @@ static bool btf_is_modifiable(const struct btf *btf) · 825 unmodified lines | |||
| 951 | return (void *)btf->hdr != btf->raw_data; | 955 | return (void *)btf->hdr != btf->raw_data; |
| 952 | } | 956 | } |
| 953 | 957 | ||
| 958 | static void btf_free_raw_data(struct btf *btf) | ||
| 959 | { | ||
| 960 | if (btf->raw_data_is_mmap) { | ||
| 961 | munmap(btf->raw_data, btf->raw_size); | ||
| 962 | btf->raw_data_is_mmap = false; | ||
| 963 | } else { | ||
| 964 | free(btf->raw_data); | ||
| 965 | } | ||
| 966 | btf->raw_data = NULL; | ||
| 967 | } | ||
| 968 | |||
| 954 | void btf__free(struct btf *btf) | 969 | void btf__free(struct btf *btf) |
| 955 | { | 970 | { |
| 956 | if (IS_ERR_OR_NULL(btf)) | 971 | if (IS_ERR_OR_NULL(btf)) |
| @@ -970,7 +985,7 @@ void btf__free(struct btf *btf) · 13 unmodified lines | |||
| 970 | free(btf->types_data); | 985 | free(btf->types_data); |
| 971 | strset__free(btf->strs_set); | 986 | strset__free(btf->strs_set); |
| 972 | } | 987 | } |
| 973 | free(btf->raw_data); | 988 | btf_free_raw_data(btf); |
| 974 | free(btf->raw_data_swapped); | 989 | free(btf->raw_data_swapped); |
| 975 | free(btf->type_offs); | 990 | free(btf->type_offs); |
| 976 | if (btf->owns_base) | 991 | if (btf->owns_base) |
| @@ -996,7 +1011,7 @@ static struct btf *btf_new_empty(struct btf *base_btf) · 19 unmodified lines | |||
| 996 | if (base_btf) { | 1011 | if (base_btf) { |
| 997 | btf->base_btf = base_btf; | 1012 | btf->base_btf = base_btf; |
| 998 | btf->start_id = btf__type_cnt(base_btf); | 1013 | btf->start_id = btf__type_cnt(base_btf); |
| 999 | btf->start_str_off = base_btf->hdr->str_len; | 1014 | btf->start_str_off = base_btf->hdr->str_len + base_btf->start_str_off; |
| 1000 | btf->swapped_endian = base_btf->swapped_endian; | 1015 | btf->swapped_endian = base_btf->swapped_endian; |
| 1001 | } | 1016 | } |
| 1002 | 1017 | ||
| @@ -1030,7 +1045,7 @@ struct btf *btf__new_empty_split(struct btf *base_btf) · 27 unmodified lines | |||
| 1030 | return libbpf_ptr(btf_new_empty(base_btf)); | 1045 | return libbpf_ptr(btf_new_empty(base_btf)); |
| 1031 | } | 1046 | } |
| 1032 | 1047 | ||
| 1033 | static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) | 1048 | static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf, bool is_mmap) |
| 1034 | { | 1049 | { |
| 1035 | struct btf *btf; | 1050 | struct btf *btf; |
| 1036 | int err; | 1051 | int err; |
| @@ -1050,12 +1065,18 @@ static struct btf *btf_new(const void *data, __u32 size, struct btf *base_btf) · 13 unmodified lines | |||
| 1050 | btf->start_str_off = base_btf->hdr->str_len; | 1065 | btf->start_str_off = base_btf->hdr->str_len; |
| 1051 | } | 1066 | } |
| 1052 | 1067 | ||
| 1053 | btf->raw_data = malloc(size); | 1068 | if (is_mmap) { |
| 1054 | if (!btf->raw_data) { | 1069 | btf->raw_data = (void *)data; |
| 1055 | err = -ENOMEM; | 1070 | btf->raw_data_is_mmap = true; |
| 1056 | goto done; | 1071 | } else { |
| 1072 | btf->raw_data = malloc(size); | ||
| 1073 | if (!btf->raw_data) { | ||
| 1074 | err = -ENOMEM; | ||
| 1075 | goto done; | ||
| 1076 | } | ||
| 1077 | memcpy(btf->raw_data, data, size); | ||
| 1057 | } | 1078 | } |
| 1058 | memcpy(btf->raw_data, data, size); | 1079 | |
| 1059 | btf->raw_size = size; | 1080 | btf->raw_size = size; |
| 1060 | 1081 | ||
| 1061 | btf->hdr = btf->raw_data; | 1082 | btf->hdr = btf->raw_data; |
| @@ -1083,12 +1104,12 @@ done: · 21 unmodified lines | |||
| 1083 | 1104 | ||
| 1084 | struct btf *btf__new(const void *data, __u32 size) | 1105 | struct btf *btf__new(const void *data, __u32 size) |
| 1085 | { | 1106 | { |
| 1086 | return libbpf_ptr(btf_new(data, size, NULL)); | 1107 | return libbpf_ptr(btf_new(data, size, NULL, false)); |
| 1087 | } | 1108 | } |
| 1088 | 1109 | ||
| 1089 | struct btf *btf__new_split(const void *data, __u32 size, struct btf *base_btf) | 1110 | struct btf *btf__new_split(const void *data, __u32 size, struct btf *base_btf) |
| 1090 | { | 1111 | { |
| 1091 | return libbpf_ptr(btf_new(data, size, base_btf)); | 1112 | return libbpf_ptr(btf_new(data, size, base_btf, false)); |
| 1092 | } | 1113 | } |
| 1093 | 1114 | ||
| 1094 | struct btf_elf_secs { | 1115 | struct btf_elf_secs { |
| @@ -1148,6 +1169,12 @@ static int btf_find_elf_sections(Elf *elf, const char *path, struct btf_elf_secs · 53 unmodified lines | |||
| 1148 | else | 1169 | else |
| 1149 | continue; | 1170 | continue; |
| 1150 | 1171 | ||
| 1172 | if (sh.sh_type != SHT_PROGBITS) { | ||
| 1173 | pr_warn("unexpected section type (%d) of section(%d, %s) from %s\n", | ||
| 1174 | sh.sh_type, idx, name, path); | ||
| 1175 | goto err; | ||
| 1176 | } | ||
| 1177 | |||
| 1151 | data = elf_getdata(scn, 0); | 1178 | data = elf_getdata(scn, 0); |
| 1152 | if (!data) { | 1179 | if (!data) { |
| 1153 | pr_warn("failed to get section(%d, %s) data from %s\n", | 1180 | pr_warn("failed to get section(%d, %s) data from %s\n", |
| @@ -1203,7 +1230,7 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf, · 49 unmodified lines | |||
| 1203 | 1230 | ||
| 1204 | if (secs.btf_base_data) { | 1231 | if (secs.btf_base_data) { |
| 1205 | dist_base_btf = btf_new(secs.btf_base_data->d_buf, secs.btf_base_data->d_size, | 1232 | dist_base_btf = btf_new(secs.btf_base_data->d_buf, secs.btf_base_data->d_size, |
| 1206 | NULL); | 1233 | NULL, false); |
| 1207 | if (IS_ERR(dist_base_btf)) { | 1234 | if (IS_ERR(dist_base_btf)) { |
| 1208 | err = PTR_ERR(dist_base_btf); | 1235 | err = PTR_ERR(dist_base_btf); |
| 1209 | dist_base_btf = NULL; | 1236 | dist_base_btf = NULL; |
| @@ -1212,7 +1239,7 @@ static struct btf *btf_parse_elf(const char *path, struct btf *base_btf, · 2 unmodified lines | |||
| 1212 | } | 1239 | } |
| 1213 | 1240 | ||
| 1214 | btf = btf_new(secs.btf_data->d_buf, secs.btf_data->d_size, | 1241 | btf = btf_new(secs.btf_data->d_buf, secs.btf_data->d_size, |
| 1215 | dist_base_btf ?: base_btf); | 1242 | dist_base_btf ?: base_btf, false); |
| 1216 | if (IS_ERR(btf)) { | 1243 | if (IS_ERR(btf)) { |
| 1217 | err = PTR_ERR(btf); | 1244 | err = PTR_ERR(btf); |
| 1218 | goto done; | 1245 | goto done; |
| @@ -1329,7 +1356,7 @@ static struct btf *btf_parse_raw(const char *path, struct btf *base_btf) · 110 unmodified lines | |||
| 1329 | } | 1356 | } |
| 1330 | 1357 | ||
| 1331 | /* finally parse BTF data */ | 1358 | /* finally parse BTF data */ |
| 1332 | btf = btf_new(data, sz, base_btf); | 1359 | btf = btf_new(data, sz, base_btf, false); |
| 1333 | 1360 | ||
| 1334 | err_out: | 1361 | err_out: |
| 1335 | free(data); | 1362 | free(data); |
| @@ -1348,6 +1375,37 @@ struct btf *btf__parse_raw_split(const char *path, struct btf *base_btf) · 12 unmodified lines | |||
| 1348 | return libbpf_ptr(btf_parse_raw(path, base_btf)); | 1375 | return libbpf_ptr(btf_parse_raw(path, base_btf)); |
| 1349 | } | 1376 | } |
| 1350 | 1377 | ||
| 1378 | static struct btf *btf_parse_raw_mmap(const char *path, struct btf *base_btf) | ||
| 1379 | { | ||
| 1380 | struct stat st; | ||
| 1381 | void *data; | ||
| 1382 | struct btf *btf; | ||
| 1383 | int fd, err; | ||
| 1384 | |||
| 1385 | fd = open(path, O_RDONLY); | ||
| 1386 | if (fd < 0) | ||
| 1387 | return libbpf_err_ptr(-errno); | ||
| 1388 | |||
| 1389 | if (fstat(fd, &st) < 0) { | ||
| 1390 | err = -errno; | ||
| 1391 | close(fd); | ||
| 1392 | return libbpf_err_ptr(err); | ||
| 1393 | } | ||
| 1394 | |||
| 1395 | data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); | ||
| 1396 | err = -errno; | ||
| 1397 | close(fd); | ||
| 1398 | |||
| 1399 | if (data == MAP_FAILED) | ||
| 1400 | return libbpf_err_ptr(err); | ||
| 1401 | |||
| 1402 | btf = btf_new(data, st.st_size, base_btf, true); | ||
| 1403 | if (IS_ERR(btf)) | ||
| 1404 | munmap(data, st.st_size); | ||
| 1405 | |||
| 1406 | return btf; | ||
| 1407 | } | ||
| 1408 | |||
| 1351 | static struct btf *btf_parse(const char *path, struct btf *base_btf, struct btf_ext **btf_ext) | 1409 | static struct btf *btf_parse(const char *path, struct btf *base_btf, struct btf_ext **btf_ext) |
| 1352 | { | 1410 | { |
| 1353 | struct btf *btf; | 1411 | struct btf *btf; |
| @@ -1612,7 +1670,7 @@ struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf) · 258 unmodified lines | |||
| 1612 | goto exit_free; | 1670 | goto exit_free; |
| 1613 | } | 1671 | } |
| 1614 | 1672 | ||
| 1615 | btf = btf_new(ptr, btf_info.btf_size, base_btf); | 1673 | btf = btf_new(ptr, btf_info.btf_size, base_btf, false); |
| 1616 | 1674 | ||
| 1617 | exit_free: | 1675 | exit_free: |
| 1618 | free(ptr); | 1676 | free(ptr); |
| @@ -1652,10 +1710,8 @@ struct btf *btf__load_from_kernel_by_id(__u32 id) · 33 unmodified lines | |||
| 1652 | 1710 | ||
| 1653 | static void btf_invalidate_raw_data(struct btf *btf) | 1711 | static void btf_invalidate_raw_data(struct btf *btf) |
| 1654 | { | 1712 | { |
| 1655 | if (btf->raw_data) { | 1713 | if (btf->raw_data) |
| 1656 | free(btf->raw_data); | 1714 | btf_free_raw_data(btf); |
| 1657 | btf->raw_data = NULL; | ||
| 1658 | } | ||
| 1659 | if (btf->raw_data_swapped) { | 1715 | if (btf->raw_data_swapped) { |
| 1660 | free(btf->raw_data_swapped); | 1716 | free(btf->raw_data_swapped); |
| 1661 | btf->raw_data_swapped = NULL; | 1717 | btf->raw_data_swapped = NULL; |
| @@ -4350,46 +4406,109 @@ static inline __u16 btf_fwd_kind(struct btf_type *t) · 2688 unmodified lines | |||
| 4350 | return btf_kflag(t) ? BTF_KIND_UNION : BTF_KIND_STRUCT; | 4406 | return btf_kflag(t) ? BTF_KIND_UNION : BTF_KIND_STRUCT; |
| 4351 | } | 4407 | } |
| 4352 | 4408 | ||
| 4353 | /* Check if given two types are identical ARRAY definitions */ | 4409 | static bool btf_dedup_identical_types(struct btf_dedup *d, __u32 id1, __u32 id2, int depth) |
| 4354 | static bool btf_dedup_identical_arrays(struct btf_dedup *d, __u32 id1, __u32 id2) | ||
| 4355 | { | 4410 | { |
| 4356 | struct btf_type *t1, *t2; | 4411 | struct btf_type *t1, *t2; |
| 4412 | int k1, k2; | ||
| 4413 | recur: | ||
| 4414 | if (depth <= 0) | ||
| 4415 | return false; | ||
| 4357 | 4416 | ||
| 4358 | t1 = btf_type_by_id(d->btf, id1); | 4417 | t1 = btf_type_by_id(d->btf, id1); |
| 4359 | t2 = btf_type_by_id(d->btf, id2); | 4418 | t2 = btf_type_by_id(d->btf, id2); |
| 4360 | if (!btf_is_array(t1) || !btf_is_array(t2)) | 4419 | |
| 4420 | k1 = btf_kind(t1); | ||
| 4421 | k2 = btf_kind(t2); | ||
| 4422 | if (k1 != k2) | ||
| 4361 | return false; | 4423 | return false; |
| 4362 | 4424 | ||
| 4363 | return btf_equal_array(t1, t2); | 4425 | switch (k1) { |
| 4364 | } | 4426 | case BTF_KIND_UNKN: /* VOID */ |
| 4427 | return true; | ||
| 4428 | case BTF_KIND_INT: | ||
| 4429 | return btf_equal_int_tag(t1, t2); | ||
| 4430 | case BTF_KIND_ENUM: | ||
| 4431 | case BTF_KIND_ENUM64: | ||
| 4432 | return btf_compat_enum(t1, t2); | ||
| 4433 | case BTF_KIND_FWD: | ||
| 4434 | case BTF_KIND_FLOAT: | ||
| 4435 | return btf_equal_common(t1, t2); | ||
| 4436 | case BTF_KIND_CONST: | ||
| 4437 | case BTF_KIND_VOLATILE: | ||
| 4438 | case BTF_KIND_RESTRICT: | ||
| 4439 | case BTF_KIND_PTR: | ||
| 4440 | case BTF_KIND_TYPEDEF: | ||
| 4441 | case BTF_KIND_FUNC: | ||
| 4442 | case BTF_KIND_TYPE_TAG: | ||
| 4443 | if (t1->info != t2->info || t1->name_off != t2->name_off) | ||
| 4444 | return false; | ||
| 4445 | id1 = t1->type; | ||
| 4446 | id2 = t2->type; | ||
| 4447 | goto recur; | ||
| 4448 | case BTF_KIND_ARRAY: { | ||
| 4449 | struct btf_array *a1, *a2; | ||
| 4365 | 4450 | ||
| 4366 | /* Check if given two types are identical STRUCT/UNION definitions */ | 4451 | if (!btf_compat_array(t1, t2)) |
| 4367 | static bool btf_dedup_identical_structs(struct btf_dedup *d, __u32 id1, __u32 id2) | 4452 | return false; |
| 4368 | { | ||
| 4369 | const struct btf_member *m1, *m2; | ||
| 4370 | struct btf_type *t1, *t2; | ||
| 4371 | int n, i; | ||
| 4372 | 4453 | ||
| 4373 | t1 = btf_type_by_id(d->btf, id1); | 4454 | a1 = btf_array(t1); |
| 4374 | t2 = btf_type_by_id(d->btf, id2); | 4455 | a2 = btf_array(t1); |
| 4375 | 4456 | ||
| 4376 | if (!btf_is_composite(t1) || btf_kind(t1) != btf_kind(t2)) | 4457 | if (a1->index_type != a2->index_type && |
| 4377 | return false; | 4458 | !btf_dedup_identical_types(d, a1->index_type, a2->index_type, depth - 1)) |
| 4459 | return false; | ||
| 4378 | 4460 | ||
| 4379 | if (!btf_shallow_equal_struct(t1, t2)) | 4461 | if (a1->type != a2->type && |
| 4380 | return false; | 4462 | !btf_dedup_identical_types(d, a1->type, a2->type, depth - 1)) |
| 4463 | return false; | ||
| 4381 | 4464 | ||
| 4382 | m1 = btf_members(t1); | 4465 | return true; |
| 4383 | m2 = btf_members(t2); | 4466 | } |
| 4384 | for (i = 0, n = btf_vlen(t1); i < n; i++, m1++, m2++) { | 4467 | case BTF_KIND_STRUCT: |
| 4385 | if (m1->type != m2->type && | 4468 | case BTF_KIND_UNION: { |
| 4386 | !btf_dedup_identical_arrays(d, m1->type, m2->type) && | 4469 | const struct btf_member *m1, *m2; |
| 4387 | !btf_dedup_identical_structs(d, m1->type, m2->type)) | 4470 | int i, n; |
| 4471 | |||
| 4472 | if (!btf_shallow_equal_struct(t1, t2)) | ||
| 4388 | return false; | 4473 | return false; |
| 4474 | |||
| 4475 | m1 = btf_members(t1); | ||
| 4476 | m2 = btf_members(t2); | ||
| 4477 | for (i = 0, n = btf_vlen(t1); i < n; i++, m1++, m2++) { | ||
| 4478 | if (m1->type == m2->type) | ||
| 4479 | continue; | ||
| 4480 | if (!btf_dedup_identical_types(d, m1->type, m2->type, depth - 1)) | ||
| 4481 | return false; | ||
| 4482 | } | ||
| 4483 | return true; | ||
| 4484 | } | ||
| 4485 | case BTF_KIND_FUNC_PROTO: { | ||
| 4486 | const struct btf_param *p1, *p2; | ||
| 4487 | int i, n; | ||
| 4488 | |||
| 4489 | if (!btf_compat_fnproto(t1, t2)) | ||
| 4490 | return false; | ||
| 4491 | |||
| 4492 | if (t1->type != t2->type && | ||
| 4493 | !btf_dedup_identical_types(d, t1->type, t2->type, depth - 1)) | ||
| 4494 | return false; | ||
| 4495 | |||
| 4496 | p1 = btf_params(t1); | ||
| 4497 | p2 = btf_params(t2); | ||
| 4498 | for (i = 0, n = btf_vlen(t1); i < n; i++, p1++, p2++) { | ||
| 4499 | if (p1->type == p2->type) | ||
| 4500 | continue; | ||
| 4501 | if (!btf_dedup_identical_types(d, p1->type, p2->type, depth - 1)) | ||
| 4502 | return false; | ||
| 4503 | } | ||
| 4504 | return true; | ||
| 4505 | } | ||
| 4506 | default: | ||
| 4507 | return false; | ||
| 4389 | } | 4508 | } |
| 4390 | return true; | ||
| 4391 | } | 4509 | } |
| 4392 | 4510 | ||
| 4511 | |||
| 4393 | /* | 4512 | /* |
| 4394 | * Check equivalence of BTF type graph formed by candidate struct/union (we'll | 4513 | * Check equivalence of BTF type graph formed by candidate struct/union (we'll |
| 4395 | * call it "candidate graph" in this description for brevity) to a type graph | 4514 | * call it "candidate graph" in this description for brevity) to a type graph |
| @@ -4508,19 +4627,13 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id, · 112 unmodified lines | |||
| 4508 | * different fields within the *same* struct. This breaks type | 4627 | * different fields within the *same* struct. This breaks type |
| 4509 | * equivalence check, which makes an assumption that candidate | 4628 | * equivalence check, which makes an assumption that candidate |
| 4510 | * types sub-graph has a consistent and deduped-by-compiler | 4629 | * types sub-graph has a consistent and deduped-by-compiler |
| 4511 | * types within a single CU. So work around that by explicitly | 4630 | * types within a single CU. And similar situation can happen |
| 4512 | * allowing identical array types here. | 4631 | * with struct/union sometimes, and event with pointers. |
| 4632 | * So accommodate cases like this doing a structural | ||
| 4633 | * comparison recursively, but avoiding being stuck in endless | ||
| 4634 | * loops by limiting the depth up to which we check. | ||
| 4513 | */ | 4635 | */ |
| 4514 | if (btf_dedup_identical_arrays(d, hypot_type_id, cand_id)) | 4636 | if (btf_dedup_identical_types(d, hypot_type_id, cand_id, 16)) |
| 4515 | return 1; | ||
| 4516 | /* It turns out that similar situation can happen with | ||
| 4517 | * struct/union sometimes, sigh... Handle the case where | ||
| 4518 | * structs/unions are exactly the same, down to the referenced | ||
| 4519 | * type IDs. Anything more complicated (e.g., if referenced | ||
| 4520 | * types are different, but equivalent) is *way more* | ||
| 4521 | * complicated and requires a many-to-many equivalence mapping. | ||
| 4522 | */ | ||
| 4523 | if (btf_dedup_identical_structs(d, hypot_type_id, cand_id)) | ||
| 4524 | return 1; | 4637 | return 1; |
| 4525 | return 0; | 4638 | return 0; |
| 4526 | } | 4639 | } |
| @@ -5268,7 +5381,10 @@ struct btf *btf__load_vmlinux_btf(void) · 741 unmodified lines | |||
| 5268 | pr_warn("kernel BTF is missing at '%s', was CONFIG_DEBUG_INFO_BTF enabled?\n", | 5381 | pr_warn("kernel BTF is missing at '%s', was CONFIG_DEBUG_INFO_BTF enabled?\n", |
| 5269 | sysfs_btf_path); | 5382 | sysfs_btf_path); |
| 5270 | } else { | 5383 | } else { |
| 5271 | btf = btf__parse(sysfs_btf_path, NULL); | 5384 | btf = btf_parse_raw_mmap(sysfs_btf_path, NULL); |
| 5385 | if (IS_ERR(btf)) | ||
| 5386 | btf = btf__parse(sysfs_btf_path, NULL); | ||
| 5387 | |||
| 5272 | if (!btf) { | 5388 | if (!btf) { |
| 5273 | err = -errno; | 5389 | err = -errno; |
| 5274 | pr_warn("failed to read kernel BTF from '%s': %s\n", | 5390 | pr_warn("failed to read kernel BTF from '%s': %s\n", |
| @@ -60,6 +60,8 @@ · 59 unmodified lines | |||
| 60 | #define BPF_FS_MAGIC 0xcafe4a11 | 60 | #define BPF_FS_MAGIC 0xcafe4a11 |
| 61 | #endif | 61 | #endif |
| 62 | 62 | ||
| 63 | #define MAX_EVENT_NAME_LEN 64 | ||
| 64 | |||
| 63 | #define BPF_FS_DEFAULT_PATH "/sys/fs/bpf" | 65 | #define BPF_FS_DEFAULT_PATH "/sys/fs/bpf" |
| 64 | 66 | ||
| 65 | #define BPF_INSN_SZ (sizeof(struct bpf_insn)) | 67 | #define BPF_INSN_SZ (sizeof(struct bpf_insn)) |
| @@ -284,7 +286,7 @@ void libbpf_print(enum libbpf_print_level level, const char *format, ...) · 218 unmodified lines | |||
| 284 | old_errno = errno; | 286 | old_errno = errno; |
| 285 | 287 | ||
| 286 | va_start(args, format); | 288 | va_start(args, format); |
| 287 | __libbpf_pr(level, format, args); | 289 | print_fn(level, format, args); |
| 288 | va_end(args); | 290 | va_end(args); |
| 289 | 291 | ||
| 290 | errno = old_errno; | 292 | errno = old_errno; |
| @@ -896,7 +898,7 @@ bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data, · 605 unmodified lines | |||
| 896 | return -LIBBPF_ERRNO__FORMAT; | 898 | return -LIBBPF_ERRNO__FORMAT; |
| 897 | } | 899 | } |
| 898 | 900 | ||
| 899 | if (sec_off + prog_sz > sec_sz) { | 901 | if (sec_off + prog_sz > sec_sz || sec_off + prog_sz < sec_off) { |
| 900 | pr_warn("sec '%s': program at offset %zu crosses section boundary\n", | 902 | pr_warn("sec '%s': program at offset %zu crosses section boundary\n", |
| 901 | sec_name, sec_off); | 903 | sec_name, sec_off); |
| 902 | return -LIBBPF_ERRNO__FORMAT; | 904 | return -LIBBPF_ERRNO__FORMAT; |
| @@ -1725,15 +1727,6 @@ static Elf64_Sym *find_elf_var_sym(const struct bpf_object *obj, const char *nam · 822 unmodified lines | |||
| 1725 | return ERR_PTR(-ENOENT); | 1727 | return ERR_PTR(-ENOENT); |
| 1726 | } | 1728 | } |
| 1727 | 1729 | ||
| 1728 | /* Some versions of Android don't provide memfd_create() in their libc | ||
| 1729 | * implementation, so avoid complications and just go straight to Linux | ||
| 1730 | * syscall. | ||
| 1731 | */ | ||
| 1732 | static int sys_memfd_create(const char *name, unsigned flags) | ||
| 1733 | { | ||
| 1734 | return syscall(__NR_memfd_create, name, flags); | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | #ifndef MFD_CLOEXEC | 1730 | #ifndef MFD_CLOEXEC |
| 1738 | #define MFD_CLOEXEC 0x0001U | 1731 | #define MFD_CLOEXEC 0x0001U |
| 1739 | #endif | 1732 | #endif |
| @@ -9455,6 +9448,30 @@ int bpf_program__set_log_buf(struct bpf_program *prog, char *log_buf, size_t log · 7715 unmodified lines | |||
| 9455 | return 0; | 9448 | return 0; |
| 9456 | } | 9449 | } |
| 9457 | 9450 | ||
| 9451 | struct bpf_func_info *bpf_program__func_info(const struct bpf_program *prog) | ||
| 9452 | { | ||
| 9453 | if (prog->func_info_rec_size != sizeof(struct bpf_func_info)) | ||
| 9454 | return libbpf_err_ptr(-EOPNOTSUPP); | ||
| 9455 | return prog->func_info; | ||
| 9456 | } | ||
| 9457 | |||
| 9458 | __u32 bpf_program__func_info_cnt(const struct bpf_program *prog) | ||
| 9459 | { | ||
| 9460 | return prog->func_info_cnt; | ||
| 9461 | } | ||
| 9462 | |||
| 9463 | struct bpf_line_info *bpf_program__line_info(const struct bpf_program *prog) | ||
| 9464 | { | ||
| 9465 | if (prog->line_info_rec_size != sizeof(struct bpf_line_info)) | ||
| 9466 | return libbpf_err_ptr(-EOPNOTSUPP); | ||
| 9467 | return prog->line_info; | ||
| 9468 | } | ||
| 9469 | |||
| 9470 | __u32 bpf_program__line_info_cnt(const struct bpf_program *prog) | ||
| 9471 | { | ||
| 9472 | return prog->line_info_cnt; | ||
| 9473 | } | ||
| 9474 | |||
| 9458 | #define SEC_DEF(sec_pfx, ptype, atype, flags, ...) { \ | 9475 | #define SEC_DEF(sec_pfx, ptype, atype, flags, ...) { \ |
| 9459 | .sec = (char *)sec_pfx, \ | 9476 | .sec = (char *)sec_pfx, \ |
| 9460 | .prog_type = BPF_PROG_TYPE_##ptype, \ | 9477 | .prog_type = BPF_PROG_TYPE_##ptype, \ |
| @@ -11121,16 +11138,16 @@ static const char *tracefs_available_filter_functions_addrs(void) · 1660 unmodified lines | |||
| 11121 | : TRACEFS"/available_filter_functions_addrs"; | 11138 | : TRACEFS"/available_filter_functions_addrs"; |
| 11122 | } | 11139 | } |
| 11123 | 11140 | ||
| 11124 | static void gen_kprobe_legacy_event_name(char *buf, size_t buf_sz, | 11141 | static void gen_probe_legacy_event_name(char *buf, size_t buf_sz, |
| 11125 | const char *kfunc_name, size_t offset) | 11142 | const char *name, size_t offset) |
| 11126 | { | 11143 | { |
| 11127 | static int index = 0; | 11144 | static int index = 0; |
| 11128 | int i; | 11145 | int i; |
| 11129 | 11146 | ||
| 11130 | snprintf(buf, buf_sz, "libbpf_%u_%s_0x%zx_%d", getpid(), kfunc_name, offset, | 11147 | snprintf(buf, buf_sz, "libbpf_%u_%d_%s_0x%zx", getpid(), |
| 11131 | __sync_fetch_and_add(&index, 1)); | 11148 | __sync_fetch_and_add(&index, 1), name, offset); |
| 11132 | 11149 | ||
| 11133 | /* sanitize binary_path in the probe name */ | 11150 | /* sanitize name in the probe name */ |
| 11134 | for (i = 0; buf[i]; i++) { | 11151 | for (i = 0; buf[i]; i++) { |
| 11135 | if (!isalnum(buf[i])) | 11152 | if (!isalnum(buf[i])) |
| 11136 | buf[i] = '_'; | 11153 | buf[i] = '_'; |
| @@ -11255,9 +11272,9 @@ int probe_kern_syscall_wrapper(int token_fd) · 118 unmodified lines | |||
| 11255 | 11272 | ||
| 11256 | return pfd >= 0 ? 1 : 0; | 11273 | return pfd >= 0 ? 1 : 0; |
| 11257 | } else { /* legacy mode */ | 11274 | } else { /* legacy mode */ |
| 11258 | char probe_name[128]; | 11275 | char probe_name[MAX_EVENT_NAME_LEN]; |
| 11259 | 11276 | ||
| 11260 | gen_kprobe_legacy_event_name(probe_name, sizeof(probe_name), syscall_name, 0); | 11277 | gen_probe_legacy_event_name(probe_name, sizeof(probe_name), syscall_name, 0); |
| 11261 | if (add_kprobe_event_legacy(probe_name, false, syscall_name, 0) < 0) | 11278 | if (add_kprobe_event_legacy(probe_name, false, syscall_name, 0) < 0) |
| 11262 | return 0; | 11279 | return 0; |
| 11263 | 11280 | ||
| @@ -11313,10 +11330,10 @@ bpf_program__attach_kprobe_opts(const struct bpf_program *prog, · 49 unmodified lines | |||
| 11313 | func_name, offset, | 11330 | func_name, offset, |
| 11314 | -1 /* pid */, 0 /* ref_ctr_off */); | 11331 | -1 /* pid */, 0 /* ref_ctr_off */); |
| 11315 | } else { | 11332 | } else { |
| 11316 | char probe_name[256]; | 11333 | char probe_name[MAX_EVENT_NAME_LEN]; |
| 11317 | 11334 | ||
| 11318 | gen_kprobe_legacy_event_name(probe_name, sizeof(probe_name), | 11335 | gen_probe_legacy_event_name(probe_name, sizeof(probe_name), |
| 11319 | func_name, offset); | 11336 | func_name, offset); |
| 11320 | 11337 | ||
| 11321 | legacy_probe = strdup(probe_name); | 11338 | legacy_probe = strdup(probe_name); |
| 11322 | if (!legacy_probe) | 11339 | if (!legacy_probe) |
| @@ -11860,20 +11877,6 @@ static int attach_uprobe_multi(const struct bpf_program *prog, long cookie, stru · 537 unmodified lines | |||
| 11860 | return ret; | 11877 | return ret; |
| 11861 | } | 11878 | } |
| 11862 | 11879 | ||
| 11863 | static void gen_uprobe_legacy_event_name(char *buf, size_t buf_sz, | ||
| 11864 | const char *binary_path, uint64_t offset) | ||
| 11865 | { | ||
| 11866 | int i; | ||
| 11867 | |||
| 11868 | snprintf(buf, buf_sz, "libbpf_%u_%s_0x%zx", getpid(), binary_path, (size_t)offset); | ||
| 11869 | |||
| 11870 | /* sanitize binary_path in the probe name */ | ||
| 11871 | for (i = 0; buf[i]; i++) { | ||
| 11872 | if (!isalnum(buf[i])) | ||
| 11873 | buf[i] = '_'; | ||
| 11874 | } | ||
| 11875 | } | ||
| 11876 | |||
| 11877 | static inline int add_uprobe_event_legacy(const char *probe_name, bool retprobe, | 11880 | static inline int add_uprobe_event_legacy(const char *probe_name, bool retprobe, |
| 11878 | const char *binary_path, size_t offset) | 11881 | const char *binary_path, size_t offset) |
| 11879 | { | 11882 | { |
| @@ -12297,13 +12300,14 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid, · 417 unmodified lines | |||
| 12297 | pfd = perf_event_open_probe(true /* uprobe */, retprobe, binary_path, | 12300 | pfd = perf_event_open_probe(true /* uprobe */, retprobe, binary_path, |
| 12298 | func_offset, pid, ref_ctr_off); | 12301 | func_offset, pid, ref_ctr_off); |
| 12299 | } else { | 12302 | } else { |
| 12300 | char probe_name[PATH_MAX + 64]; | 12303 | char probe_name[MAX_EVENT_NAME_LEN]; |
| 12301 | 12304 | ||
| 12302 | if (ref_ctr_off) | 12305 | if (ref_ctr_off) |
| 12303 | return libbpf_err_ptr(-EINVAL); | 12306 | return libbpf_err_ptr(-EINVAL); |
| 12304 | 12307 | ||
| 12305 | gen_uprobe_legacy_event_name(probe_name, sizeof(probe_name), | 12308 | gen_probe_legacy_event_name(probe_name, sizeof(probe_name), |
| 12306 | binary_path, func_offset); | 12309 | strrchr(binary_path, '/') ? : binary_path, |
| 12310 | func_offset); | ||
| 12307 | 12311 | ||
| 12308 | legacy_probe = strdup(probe_name); | 12312 | legacy_probe = strdup(probe_name); |
| 12309 | if (!legacy_probe) | 12313 | if (!legacy_probe) |
| @@ -13371,7 +13375,6 @@ struct perf_buffer *perf_buffer__new(int map_fd, size_t page_cnt, · 1061 unmodified lines | |||
| 13371 | attr.config = PERF_COUNT_SW_BPF_OUTPUT; | 13375 | attr.config = PERF_COUNT_SW_BPF_OUTPUT; |
| 13372 | attr.type = PERF_TYPE_SOFTWARE; | 13376 | attr.type = PERF_TYPE_SOFTWARE; |
| 13373 | attr.sample_type = PERF_SAMPLE_RAW; | 13377 | attr.sample_type = PERF_SAMPLE_RAW; |
| 13374 | attr.sample_period = sample_period; | ||
| 13375 | attr.wakeup_events = sample_period; | 13378 | attr.wakeup_events = sample_period; |
| 13376 | 13379 | ||
| 13377 | p.attr = &attr; | 13380 | p.attr = &attr; |
| @@ -14099,6 +14102,12 @@ int bpf_object__attach_skeleton(struct bpf_object_skeleton *s) · 721 unmodified lines | |||
| 14099 | } | 14102 | } |
| 14100 | 14103 | ||
| 14101 | link = map_skel->link; | 14104 | link = map_skel->link; |
| 14105 | if (!link) { | ||
| 14106 | pr_warn("map '%s': BPF map skeleton link is uninitialized\n", | ||
| 14107 | bpf_map__name(map)); | ||
| 14108 | continue; | ||
| 14109 | } | ||
| 14110 | |||
| 14102 | if (*link) | 14111 | if (*link) |
| 14103 | continue; | 14112 | continue; |
| 14104 | 14113 | ||
| @@ -940,6 +940,12 @@ LIBBPF_API int bpf_program__set_log_level(struct bpf_program *prog, __u32 log_le · 939 unmodified lines | |||
| 940 | LIBBPF_API const char *bpf_program__log_buf(const struct bpf_program *prog, size_t *log_size); | 940 | LIBBPF_API const char *bpf_program__log_buf(const struct bpf_program *prog, size_t *log_size); |
| 941 | LIBBPF_API int bpf_program__set_log_buf(struct bpf_program *prog, char *log_buf, size_t log_size); | 941 | LIBBPF_API int bpf_program__set_log_buf(struct bpf_program *prog, char *log_buf, size_t log_size); |
| 942 | 942 | ||
| 943 | LIBBPF_API struct bpf_func_info *bpf_program__func_info(const struct bpf_program *prog); | ||
| 944 | LIBBPF_API __u32 bpf_program__func_info_cnt(const struct bpf_program *prog); | ||
| 945 | |||
| 946 | LIBBPF_API struct bpf_line_info *bpf_program__line_info(const struct bpf_program *prog); | ||
| 947 | LIBBPF_API __u32 bpf_program__line_info_cnt(const struct bpf_program *prog); | ||
| 948 | |||
| 943 | /** | 949 | /** |
| 944 | * @brief **bpf_program__set_attach_target()** sets BTF-based attach target | 950 | * @brief **bpf_program__set_attach_target()** sets BTF-based attach target |
| 945 | * for supported BPF program types: | 951 | * for supported BPF program types: |
| @@ -437,6 +437,10 @@ LIBBPF_1.6.0 { · 436 unmodified lines | |||
| 437 | bpf_linker__add_fd; | 437 | bpf_linker__add_fd; |
| 438 | bpf_linker__new_fd; | 438 | bpf_linker__new_fd; |
| 439 | bpf_object__prepare; | 439 | bpf_object__prepare; |
| 440 | bpf_program__func_info; | ||
| 441 | bpf_program__func_info_cnt; | ||
| 442 | bpf_program__line_info; | ||
| 443 | bpf_program__line_info_cnt; | ||
| 440 | btf__add_decl_attr; | 444 | btf__add_decl_attr; |
| 441 | btf__add_type_attr; | 445 | btf__add_type_attr; |
| 442 | } LIBBPF_1.5.0; | 446 | } LIBBPF_1.5.0; |
| @@ -667,6 +667,15 @@ static inline int sys_dup3(int oldfd, int newfd, int flags) · 666 unmodified lines | |||
| 667 | return syscall(__NR_dup3, oldfd, newfd, flags); | 667 | return syscall(__NR_dup3, oldfd, newfd, flags); |
| 668 | } | 668 | } |
| 669 | 669 | ||
| 670 | /* Some versions of Android don't provide memfd_create() in their libc | ||
| 671 | * implementation, so avoid complications and just go straight to Linux | ||
| 672 | * syscall. | ||
| 673 | */ | ||
| 674 | static inline int sys_memfd_create(const char *name, unsigned flags) | ||
| 675 | { | ||
| 676 | return syscall(__NR_memfd_create, name, flags); | ||
| 677 | } | ||
| 678 | |||
| 670 | /* Point *fixed_fd* to the same file that *tmp_fd* points to. | 679 | /* Point *fixed_fd* to the same file that *tmp_fd* points to. |
| 671 | * Regardless of success, *tmp_fd* is closed. | 680 | * Regardless of success, *tmp_fd* is closed. |
| 672 | * Whatever *fixed_fd* pointed to is closed silently. | 681 | * Whatever *fixed_fd* pointed to is closed silently. |
| @@ -573,7 +573,7 @@ int bpf_linker__add_buf(struct bpf_linker *linker, void *buf, size_t buf_sz, · 572 unmodified lines | |||
| 573 | 573 | ||
| 574 | snprintf(filename, sizeof(filename), "mem:%p+%zu", buf, buf_sz); | 574 | snprintf(filename, sizeof(filename), "mem:%p+%zu", buf, buf_sz); |
| 575 | 575 | ||
| 576 | fd = memfd_create(filename, 0); | 576 | fd = sys_memfd_create(filename, 0); |
| 577 | if (fd < 0) { | 577 | if (fd < 0) { |
| 578 | ret = -errno; | 578 | ret = -errno; |
| 579 | pr_warn("failed to create memfd '%s': %s\n", filename, errstr(ret)); | 579 | pr_warn("failed to create memfd '%s': %s\n", filename, errstr(ret)); |
| @@ -1376,7 +1376,7 @@ static int linker_append_sec_data(struct bpf_linker *linker, struct src_obj *obj · 796 unmodified lines | |||
| 1376 | } else { | 1376 | } else { |
| 1377 | if (!secs_match(dst_sec, src_sec)) { | 1377 | if (!secs_match(dst_sec, src_sec)) { |
| 1378 | pr_warn("ELF sections %s are incompatible\n", src_sec->sec_name); | 1378 | pr_warn("ELF sections %s are incompatible\n", src_sec->sec_name); |
| 1379 | return -1; | 1379 | return -EINVAL; |
| 1380 | } | 1380 | } |
| 1381 | 1381 | ||
| 1382 | /* "license" and "version" sections are deduped */ | 1382 | /* "license" and "version" sections are deduped */ |
| @@ -2223,7 +2223,7 @@ static int linker_append_elf_relos(struct bpf_linker *linker, struct src_obj *ob · 840 unmodified lines | |||
| 2223 | } | 2223 | } |
| 2224 | } else if (!secs_match(dst_sec, src_sec)) { | 2224 | } else if (!secs_match(dst_sec, src_sec)) { |
| 2225 | pr_warn("sections %s are not compatible\n", src_sec->sec_name); | 2225 | pr_warn("sections %s are not compatible\n", src_sec->sec_name); |
| 2226 | return -1; | 2226 | return -EINVAL; |
| 2227 | } | 2227 | } |
| 2228 | 2228 | ||
| 2229 | /* shdr->sh_link points to SYMTAB */ | 2229 | /* shdr->sh_link points to SYMTAB */ |
| @@ -63,16 +63,16 @@ static int validate_nla(struct nlattr *nla, int maxtype, · 62 unmodified lines | |||
| 63 | minlen = nla_attr_minlen[pt->type]; | 63 | minlen = nla_attr_minlen[pt->type]; |
| 64 | 64 | ||
| 65 | if (libbpf_nla_len(nla) < minlen) | 65 | if (libbpf_nla_len(nla) < minlen) |
| 66 | return -1; | 66 | return -EINVAL; |
| 67 | 67 | ||
| 68 | if (pt->maxlen && libbpf_nla_len(nla) > pt->maxlen) | 68 | if (pt->maxlen && libbpf_nla_len(nla) > pt->maxlen) |
| 69 | return -1; | 69 | return -EINVAL; |
| 70 | 70 | ||
| 71 | if (pt->type == LIBBPF_NLA_STRING) { | 71 | if (pt->type == LIBBPF_NLA_STRING) { |
| 72 | char *data = libbpf_nla_data(nla); | 72 | char *data = libbpf_nla_data(nla); |
| 73 | 73 | ||
| 74 | if (data[libbpf_nla_len(nla) - 1] != '\0') | 74 | if (data[libbpf_nla_len(nla) - 1] != '\0') |
| 75 | return -1; | 75 | return -EINVAL; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | return 0; | 78 | return 0; |
| @@ -118,19 +118,18 @@ int libbpf_nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, · 39 unmodified lines | |||
| 118 | if (policy) { | 118 | if (policy) { |
| 119 | err = validate_nla(nla, maxtype, policy); | 119 | err = validate_nla(nla, maxtype, policy); |
| 120 | if (err < 0) | 120 | if (err < 0) |
| 121 | goto errout; | 121 | return err; |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | if (tb[type]) | 124 | if (tb[type]) { |
| 125 | pr_warn("Attribute of type %#x found multiple times in message, " | 125 | pr_warn("Attribute of type %#x found multiple times in message, " |
| 126 | "previous attribute is being ignored.\n", type); | 126 | "previous attribute is being ignored.\n", type); |
| 127 | } | ||
| 127 | 128 | ||
| 128 | tb[type] = nla; | 129 | tb[type] = nla; |
| 129 | } | 130 | } |
| 130 | 131 | ||
| 131 | err = 0; | 132 | return 0; |
| 132 | errout: | ||
| 133 | return err; | ||
| 134 | } | 133 | } |
| 135 | 134 | ||
| 136 | /** | 135 | /** |
| @@ -1,5 +1,6 @@ | |||
| 1 | # TEMPORARY | 1 | # TEMPORARY |
| 2 | # Alphabetical order | 2 | # Alphabetical order |
| 3 | dynptr/test_probe_read_user_str_dynptr # disabled until https://patchwork.kernel.org/project/linux-mm/patch/20250422131449.57177-1-mykyta.yatsenko5@gmail.com/ makes it into the bpf-next | ||
| 3 | get_stack_raw_tp # spams with kernel warnings until next bpf -> bpf-next merge | 4 | get_stack_raw_tp # spams with kernel warnings until next bpf -> bpf-next merge |
| 4 | stacktrace_build_id | 5 | stacktrace_build_id |
| 5 | stacktrace_build_id_nmi | 6 | stacktrace_build_id_nmi |
| @@ -1,3 +1 @@ | |||
| 1 | fentry_test/fentry_many_args # fentry_many_args:FAIL:fentry_many_args_attach unexpected error: -524 | ||
| 2 | fexit_test/fexit_many_args # fexit_many_args:FAIL:fexit_many_args_attach unexpected error: -524 | ||
| 3 | tracing_struct/struct_many_args # struct_many_args:FAIL:tracing_struct_many_args__attach unexpected error: -524 | 1 | tracing_struct/struct_many_args # struct_many_args:FAIL:tracing_struct_many_args__attach unexpected error: -524 |
| @@ -34,6 +34,9 @@ OPT_FLAGS ?= $(if $(RELEASE),-O2,-O0) · 33 unmodified lines | |||
| 34 | LIBELF_CFLAGS := $(shell $(PKG_CONFIG) libelf --cflags 2>/dev/null) | 34 | LIBELF_CFLAGS := $(shell $(PKG_CONFIG) libelf --cflags 2>/dev/null) |
| 35 | LIBELF_LIBS := $(shell $(PKG_CONFIG) libelf --libs 2>/dev/null || echo -lelf) | 35 | LIBELF_LIBS := $(shell $(PKG_CONFIG) libelf --libs 2>/dev/null || echo -lelf) |
| 36 | 36 | ||
| 37 | SKIP_DOCS ?= | ||
| 38 | SKIP_LLVM ?= | ||
| 39 | |||
| 37 | ifeq ($(srctree),) | 40 | ifeq ($(srctree),) |
| 38 | srctree := $(patsubst %/,%,$(dir $(CURDIR))) | 41 | srctree := $(patsubst %/,%,$(dir $(CURDIR))) |
| 39 | srctree := $(patsubst %/,%,$(dir $(srctree))) | 42 | srctree := $(patsubst %/,%,$(dir $(srctree))) |
| @@ -172,6 +175,7 @@ override OUTPUT := $(patsubst %/,%,$(OUTPUT)) · 132 unmodified lines | |||
| 172 | endif | 175 | endif |
| 173 | endif | 176 | endif |
| 174 | 177 | ||
| 178 | ifneq ($(SKIP_LLVM),1) | ||
| 175 | ifeq ($(feature-llvm),1) | 179 | ifeq ($(feature-llvm),1) |
| 176 | LLVM_CFLAGS += -DHAVE_LLVM_SUPPORT | 180 | LLVM_CFLAGS += -DHAVE_LLVM_SUPPORT |
| 177 | LLVM_CONFIG_LIB_COMPONENTS := mcdisassembler all-targets | 181 | LLVM_CONFIG_LIB_COMPONENTS := mcdisassembler all-targets |
| @@ -180,13 +184,14 @@ ifeq ($(feature-llvm),1) · 2 unmodified lines | |||
| 180 | # Prefer linking statically if it's available, otherwise fallback to shared | 184 | # Prefer linking statically if it's available, otherwise fallback to shared |
| 181 | ifeq ($(shell $(LLVM_CONFIG) --link-static --libs >/dev/null 2>&1 && echo static),static) | 185 | ifeq ($(shell $(LLVM_CONFIG) --link-static --libs >/dev/null 2>&1 && echo static),static) |
| 182 | LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --link-static --libs $(LLVM_CONFIG_LIB_COMPONENTS)) | 186 | LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --link-static --libs $(LLVM_CONFIG_LIB_COMPONENTS)) |
| 183 | LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --link-static --system-libs $(LLVM_CONFIG_LIB_COMPONENTS)) | 187 | LLVM_LDLIBS += $(filter-out -lxml2,$(shell $(LLVM_CONFIG) --link-static --system-libs $(LLVM_CONFIG_LIB_COMPONENTS))) |
| 184 | LLVM_LDLIBS += -lstdc++ | 188 | LLVM_LDLIBS += -lstdc++ |
| 185 | else | 189 | else |
| 186 | LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --link-shared --libs $(LLVM_CONFIG_LIB_COMPONENTS)) | 190 | LLVM_LDLIBS += $(shell $(LLVM_CONFIG) --link-shared --libs $(LLVM_CONFIG_LIB_COMPONENTS)) |
| 187 | endif | 191 | endif |
| 188 | LLVM_LDFLAGS += $(shell $(LLVM_CONFIG) --ldflags) | 192 | LLVM_LDFLAGS += $(shell $(LLVM_CONFIG) --ldflags) |
| 189 | endif | 193 | endif |
| 194 | endif | ||
| 190 | 195 | ||
| 191 | SCRATCH_DIR := $(OUTPUT)/tools | 196 | SCRATCH_DIR := $(OUTPUT)/tools |
| 192 | BUILD_DIR := $(SCRATCH_DIR)/build | 197 | BUILD_DIR := $(SCRATCH_DIR)/build |
| @@ -358,7 +363,9 @@ $(CROSS_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile) \ · 165 unmodified lines | |||
| 358 | prefix= DESTDIR=$(SCRATCH_DIR)/ install-bin | 363 | prefix= DESTDIR=$(SCRATCH_DIR)/ install-bin |
| 359 | endif | 364 | endif |
| 360 | 365 | ||
| 366 | ifneq ($(SKIP_DOCS),1) | ||
| 361 | all: docs | 367 | all: docs |
| 368 | endif | ||
| 362 | 369 | ||
| 363 | docs: | 370 | docs: |
| 364 | $(Q)RST2MAN_OPTS="--exit-status=1" $(MAKE) $(submake_extras) \ | 371 | $(Q)RST2MAN_OPTS="--exit-status=1" $(MAKE) $(submake_extras) \ |
| @@ -673,9 +680,6 @@ ifneq ($2:$(OUTPUT),:$(shell pwd)) · 308 unmodified lines | |||
| 673 | $(Q)rsync -aq $$^ $(TRUNNER_OUTPUT)/ | 680 | $(Q)rsync -aq $$^ $(TRUNNER_OUTPUT)/ |
| 674 | endif | 681 | endif |
| 675 | 682 | ||
| 676 | $(OUTPUT)/$(TRUNNER_BINARY): LDLIBS += $$(LLVM_LDLIBS) | ||
| 677 | $(OUTPUT)/$(TRUNNER_BINARY): LDFLAGS += $$(LLVM_LDFLAGS) | ||
| 678 | |||
| 679 | # some X.test.o files have runtime dependencies on Y.bpf.o files | 683 | # some X.test.o files have runtime dependencies on Y.bpf.o files |
| 680 | $(OUTPUT)/$(TRUNNER_BINARY): | $(TRUNNER_BPF_OBJS) | 684 | $(OUTPUT)/$(TRUNNER_BINARY): | $(TRUNNER_BPF_OBJS) |
| 681 | 685 | ||
| @@ -686,7 +690,7 @@ $(OUTPUT)/$(TRUNNER_BINARY): $(TRUNNER_TEST_OBJS) \ · 4 unmodified lines | |||
| 686 | $(OUTPUT)/veristat \ | 690 | $(OUTPUT)/veristat \ |
| 687 | | $(TRUNNER_BINARY)-extras | 691 | | $(TRUNNER_BINARY)-extras |
| 688 | $$(call msg,BINARY,,$$@) | 692 | $$(call msg,BINARY,,$$@) |
| 689 | $(Q)$$(CC) $$(CFLAGS) $$(filter %.a %.o,$$^) $$(LDLIBS) $$(LDFLAGS) -o $$@ | 693 | $(Q)$$(CC) $$(CFLAGS) $$(filter %.a %.o,$$^) $$(LDLIBS) $$(LLVM_LDLIBS) $$(LDFLAGS) $$(LLVM_LDFLAGS) -o $$@ |
| 690 | $(Q)$(RESOLVE_BTFIDS) --btf $(TRUNNER_OUTPUT)/btf_data.bpf.o $$@ | 694 | $(Q)$(RESOLVE_BTFIDS) --btf $(TRUNNER_OUTPUT)/btf_data.bpf.o $$@ |
| 691 | $(Q)ln -sf $(if $2,..,.)/tools/build/bpftool/$(USE_BOOTSTRAP)bpftool \ | 695 | $(Q)ln -sf $(if $2,..,.)/tools/build/bpftool/$(USE_BOOTSTRAP)bpftool \ |
| 692 | $(OUTPUT)/$(if $2,$2/)bpftool | 696 | $(OUTPUT)/$(if $2,$2/)bpftool |
| @@ -811,6 +815,7 @@ $(OUTPUT)/bench_local_storage_create.o: $(OUTPUT)/bench_local_storage_create.ske · 118 unmodified lines | |||
| 811 | $(OUTPUT)/bench_bpf_hashmap_lookup.o: $(OUTPUT)/bpf_hashmap_lookup.skel.h | 815 | $(OUTPUT)/bench_bpf_hashmap_lookup.o: $(OUTPUT)/bpf_hashmap_lookup.skel.h |
| 812 | $(OUTPUT)/bench_htab_mem.o: $(OUTPUT)/htab_mem_bench.skel.h | 816 | $(OUTPUT)/bench_htab_mem.o: $(OUTPUT)/htab_mem_bench.skel.h |
| 813 | $(OUTPUT)/bench_bpf_crypto.o: $(OUTPUT)/crypto_bench.skel.h | 817 | $(OUTPUT)/bench_bpf_crypto.o: $(OUTPUT)/crypto_bench.skel.h |
| 818 | $(OUTPUT)/bench_sockmap.o: $(OUTPUT)/bench_sockmap_prog.skel.h | ||
| 814 | $(OUTPUT)/bench.o: bench.h testing_helpers.h $(BPFOBJ) | 819 | $(OUTPUT)/bench.o: bench.h testing_helpers.h $(BPFOBJ) |
| 815 | $(OUTPUT)/bench: LDLIBS += -lm | 820 | $(OUTPUT)/bench: LDLIBS += -lm |
| 816 | $(OUTPUT)/bench: $(OUTPUT)/bench.o \ | 821 | $(OUTPUT)/bench: $(OUTPUT)/bench.o \ |
| @@ -831,6 +836,7 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o \ · 14 unmodified lines | |||
| 831 | $(OUTPUT)/bench_local_storage_create.o \ | 836 | $(OUTPUT)/bench_local_storage_create.o \ |
| 832 | $(OUTPUT)/bench_htab_mem.o \ | 837 | $(OUTPUT)/bench_htab_mem.o \ |
| 833 | $(OUTPUT)/bench_bpf_crypto.o \ | 838 | $(OUTPUT)/bench_bpf_crypto.o \ |
| 839 | $(OUTPUT)/bench_sockmap.o \ | ||
| 834 | # | 840 | # |
| 835 | $(call msg,BINARY,,$@) | 841 | $(call msg,BINARY,,$@) |
| 836 | $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@ | 842 | $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@ |
| @@ -283,6 +283,7 @@ extern struct argp bench_local_storage_create_argp; · 282 unmodified lines | |||
| 283 | extern struct argp bench_htab_mem_argp; | 283 | extern struct argp bench_htab_mem_argp; |
| 284 | extern struct argp bench_trigger_batch_argp; | 284 | extern struct argp bench_trigger_batch_argp; |
| 285 | extern struct argp bench_crypto_argp; | 285 | extern struct argp bench_crypto_argp; |
| 286 | extern struct argp bench_sockmap_argp; | ||
| 286 | 287 | ||
| 287 | static const struct argp_child bench_parsers[] = { | 288 | static const struct argp_child bench_parsers[] = { |
| 288 | { &bench_ringbufs_argp, 0, "Ring buffers benchmark", 0 }, | 289 | { &bench_ringbufs_argp, 0, "Ring buffers benchmark", 0 }, |
| @@ -297,6 +298,7 @@ static const struct argp_child bench_parsers[] = { · 8 unmodified lines | |||
| 297 | { &bench_htab_mem_argp, 0, "hash map memory benchmark", 0 }, | 298 | { &bench_htab_mem_argp, 0, "hash map memory benchmark", 0 }, |
| 298 | { &bench_trigger_batch_argp, 0, "BPF triggering benchmark", 0 }, | 299 | { &bench_trigger_batch_argp, 0, "BPF triggering benchmark", 0 }, |
| 299 | { &bench_crypto_argp, 0, "bpf crypto benchmark", 0 }, | 300 | { &bench_crypto_argp, 0, "bpf crypto benchmark", 0 }, |
| 301 | { &bench_sockmap_argp, 0, "bpf sockmap benchmark", 0 }, | ||
| 300 | {}, | 302 | {}, |
| 301 | }; | 303 | }; |
| 302 | 304 | ||
| @@ -555,6 +557,7 @@ extern const struct bench bench_local_storage_create; · 252 unmodified lines | |||
| 555 | extern const struct bench bench_htab_mem; | 557 | extern const struct bench bench_htab_mem; |
| 556 | extern const struct bench bench_crypto_encrypt; | 558 | extern const struct bench bench_crypto_encrypt; |
| 557 | extern const struct bench bench_crypto_decrypt; | 559 | extern const struct bench bench_crypto_decrypt; |
| 560 | extern const struct bench bench_sockmap; | ||
| 558 | 561 | ||
| 559 | static const struct bench *benchs[] = { | 562 | static const struct bench *benchs[] = { |
| 560 | &bench_count_global, | 563 | &bench_count_global, |
| @@ -621,6 +624,7 @@ static const struct bench *benchs[] = { · 60 unmodified lines | |||
| 621 | &bench_htab_mem, | 624 | &bench_htab_mem, |
| 622 | &bench_crypto_encrypt, | 625 | &bench_crypto_encrypt, |
| 623 | &bench_crypto_decrypt, | 626 | &bench_crypto_decrypt, |
| 627 | &bench_sockmap, | ||
| 624 | }; | 628 | }; |
| 625 | 629 | ||
| 626 | static void find_benchmark(void) | 630 | static void find_benchmark(void) |
| @@ -279,6 +279,7 @@ static void htab_mem_read_mem_cgrp_file(const char *name, unsigned long *value) · 278 unmodified lines | |||
| 279 | } | 279 | } |
| 280 | 280 | ||
| 281 | got = read(fd, buf, sizeof(buf) - 1); | 281 | got = read(fd, buf, sizeof(buf) - 1); |
| 282 | close(fd); | ||
| 282 | if (got <= 0) { | 283 | if (got <= 0) { |
| 283 | *value = 0; | 284 | *value = 0; |
| 284 | return; | 285 | return; |
| @@ -286,8 +287,6 @@ static void htab_mem_read_mem_cgrp_file(const char *name, unsigned long *value) · 1 unmodified line | |||
| 286 | buf[got] = 0; | 287 | buf[got] = 0; |
| 287 | 288 | ||
| 288 | *value = strtoull(buf, NULL, 0); | 289 | *value = strtoull(buf, NULL, 0); |
| 289 | |||
| 290 | close(fd); | ||
| 291 | } | 290 | } |
| 292 | 291 | ||
| 293 | static void htab_mem_measure(struct bench_res *res) | 292 | static void htab_mem_measure(struct bench_res *res) |
| @@ -0,0 +1,598 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | #include <error.h> | ||
| 4 | #include <sys/types.h> | ||
| 5 | #include <sys/socket.h> | ||
| 6 | #include <netinet/in.h> | ||
| 7 | #include <sys/sendfile.h> | ||
| 8 | #include <arpa/inet.h> | ||
| 9 | #include <fcntl.h> | ||
| 10 | #include <argp.h> | ||
| 11 | #include "bench.h" | ||
| 12 | #include "bench_sockmap_prog.skel.h" | ||
| 13 | |||
| 14 | #define FILE_SIZE (128 * 1024) | ||
| 15 | #define DATA_REPEAT_SIZE 10 | ||
| 16 | |||
| 17 | static const char snd_data[DATA_REPEAT_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; | ||
| 18 | |||
| 19 | /* c1 <-> [p1, p2] <-> c2 | ||
| 20 | * RX bench(BPF_SK_SKB_STREAM_VERDICT): | ||
| 21 | * ARG_FW_RX_PASS: | ||
| 22 | * send(p2) -> recv(c2) -> bpf skb passthrough -> recv(c2) | ||
| 23 | * ARG_FW_RX_VERDICT_EGRESS: | ||
| 24 | * send(c1) -> verdict skb to tx queuec of p2 -> recv(c2) | ||
| 25 | * ARG_FW_RX_VERDICT_INGRESS: | ||
| 26 | * send(c1) -> verdict skb to rx queuec of c2 -> recv(c2) | ||
| 27 | * | ||
| 28 | * TX bench(BPF_SK_MSG_VERDIC): | ||
| 29 | * ARG_FW_TX_PASS: | ||
| 30 | * send(p2) -> bpf msg passthrough -> send(p2) -> recv(c2) | ||
| 31 | * ARG_FW_TX_VERDICT_INGRESS: | ||
| 32 | * send(p2) -> verdict msg to rx queue of c2 -> recv(c2) | ||
| 33 | * ARG_FW_TX_VERDICT_EGRESS: | ||
| 34 | * send(p1) -> verdict msg to tx queue of p2 -> recv(c2) | ||
| 35 | */ | ||
| 36 | enum SOCKMAP_ARG_FLAG { | ||
| 37 | ARG_FW_RX_NORMAL = 11000, | ||
| 38 | ARG_FW_RX_PASS, | ||
| 39 | ARG_FW_RX_VERDICT_EGRESS, | ||
| 40 | ARG_FW_RX_VERDICT_INGRESS, | ||
| 41 | ARG_FW_TX_NORMAL, | ||
| 42 | ARG_FW_TX_PASS, | ||
| 43 | ARG_FW_TX_VERDICT_INGRESS, | ||
| 44 | ARG_FW_TX_VERDICT_EGRESS, | ||
| 45 | ARG_CTL_RX_STRP, | ||
| 46 | ARG_CONSUMER_DELAY_TIME, | ||
| 47 | ARG_PRODUCER_DURATION, | ||
| 48 | }; | ||
| 49 | |||
| 50 | #define TXMODE_NORMAL() \ | ||
| 51 | ((ctx.mode) == ARG_FW_TX_NORMAL) | ||
| 52 | |||
| 53 | #define TXMODE_BPF_INGRESS() \ | ||
| 54 | ((ctx.mode) == ARG_FW_TX_VERDICT_INGRESS) | ||
| 55 | |||
| 56 | #define TXMODE_BPF_EGRESS() \ | ||
| 57 | ((ctx.mode) == ARG_FW_TX_VERDICT_EGRESS) | ||
| 58 | |||
| 59 | #define TXMODE_BPF_PASS() \ | ||
| 60 | ((ctx.mode) == ARG_FW_TX_PASS) | ||
| 61 | |||
| 62 | #define TXMODE_BPF() ( \ | ||
| 63 | TXMODE_BPF_PASS() || \ | ||
| 64 | TXMODE_BPF_INGRESS() || \ | ||
| 65 | TXMODE_BPF_EGRESS()) | ||
| 66 | |||
| 67 | #define TXMODE() ( \ | ||
| 68 | TXMODE_NORMAL() || \ | ||
| 69 | TXMODE_BPF()) | ||
| 70 | |||
| 71 | #define RXMODE_NORMAL() \ | ||
| 72 | ((ctx.mode) == ARG_FW_RX_NORMAL) | ||
| 73 | |||
| 74 | #define RXMODE_BPF_PASS() \ | ||
| 75 | ((ctx.mode) == ARG_FW_RX_PASS) | ||
| 76 | |||
| 77 | #define RXMODE_BPF_VERDICT_EGRESS() \ | ||
| 78 | ((ctx.mode) == ARG_FW_RX_VERDICT_EGRESS) | ||
| 79 | |||
| 80 | #define RXMODE_BPF_VERDICT_INGRESS() \ | ||
| 81 | ((ctx.mode) == ARG_FW_RX_VERDICT_INGRESS) | ||
| 82 | |||
| 83 | #define RXMODE_BPF_VERDICT() ( \ | ||
| 84 | RXMODE_BPF_VERDICT_INGRESS() || \ | ||
| 85 | RXMODE_BPF_VERDICT_EGRESS()) | ||
| 86 | |||
| 87 | #define RXMODE_BPF() ( \ | ||
| 88 | RXMODE_BPF_PASS() || \ | ||
| 89 | RXMODE_BPF_VERDICT()) | ||
| 90 | |||
| 91 | #define RXMODE() ( \ | ||
| 92 | RXMODE_NORMAL() || \ | ||
| 93 | RXMODE_BPF()) | ||
| 94 | |||
| 95 | static struct socmap_ctx { | ||
| 96 | struct bench_sockmap_prog *skel; | ||
| 97 | enum SOCKMAP_ARG_FLAG mode; | ||
| 98 | #define c1 fds[0] | ||
| 99 | #define p1 fds[1] | ||
| 100 | #define c2 fds[2] | ||
| 101 | #define p2 fds[3] | ||
| 102 | #define sfd fds[4] | ||
| 103 | int fds[5]; | ||
| 104 | long send_calls; | ||
| 105 | long read_calls; | ||
| 106 | long prod_send; | ||
| 107 | long user_read; | ||
| 108 | int file_size; | ||
| 109 | int delay_consumer; | ||
| 110 | int prod_run_time; | ||
| 111 | int strp_size; | ||
| 112 | } ctx = { | ||
| 113 | .prod_send = 0, | ||
| 114 | .user_read = 0, | ||
| 115 | .file_size = FILE_SIZE, | ||
| 116 | .mode = ARG_FW_RX_VERDICT_EGRESS, | ||
| 117 | .fds = {0}, | ||
| 118 | .delay_consumer = 0, | ||
| 119 | .prod_run_time = 0, | ||
| 120 | .strp_size = 0, | ||
| 121 | }; | ||
| 122 | |||
| 123 | static void bench_sockmap_prog_destroy(void) | ||
| 124 | { | ||
| 125 | int i; | ||
| 126 | |||
| 127 | for (i = 0; i < sizeof(ctx.fds); i++) { | ||
| 128 | if (ctx.fds[0] > 0) | ||
| 129 | close(ctx.fds[i]); | ||
| 130 | } | ||
| 131 | |||
| 132 | bench_sockmap_prog__destroy(ctx.skel); | ||
| 133 | } | ||
| 134 | |||
| 135 | static void init_addr(struct sockaddr_storage *ss, | ||
| 136 | socklen_t *len) | ||
| 137 | { | ||
| 138 | struct sockaddr_in *addr4 = memset(ss, 0, sizeof(*ss)); | ||
| 139 | |||
| 140 | addr4->sin_family = AF_INET; | ||
| 141 | addr4->sin_port = 0; | ||
| 142 | addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); | ||
| 143 | *len = sizeof(*addr4); | ||
| 144 | } | ||
| 145 | |||
| 146 | static bool set_non_block(int fd, bool blocking) | ||
| 147 | { | ||
| 148 | int flags = fcntl(fd, F_GETFL, 0); | ||
| 149 | |||
| 150 | if (flags == -1) | ||
| 151 | return false; | ||
| 152 | flags = blocking ? (flags | O_NONBLOCK) : (flags & ~O_NONBLOCK); | ||
| 153 | return (fcntl(fd, F_SETFL, flags) == 0); | ||
| 154 | } | ||
| 155 | |||
| 156 | static int create_pair(int *c, int *p, int type) | ||
| 157 | { | ||
| 158 | struct sockaddr_storage addr; | ||
| 159 | int err, cfd, pfd; | ||
| 160 | socklen_t addr_len = sizeof(struct sockaddr_storage); | ||
| 161 | |||
| 162 | err = getsockname(ctx.sfd, (struct sockaddr *)&addr, &addr_len); | ||
| 163 | if (err) { | ||
| 164 | fprintf(stderr, "getsockname error %d\n", errno); | ||
| 165 | return err; | ||
| 166 | } | ||
| 167 | cfd = socket(AF_INET, type, 0); | ||
| 168 | if (cfd < 0) { | ||
| 169 | fprintf(stderr, "socket error %d\n", errno); | ||
| 170 | return err; | ||
| 171 | } | ||
| 172 | |||
| 173 | err = connect(cfd, (struct sockaddr *)&addr, addr_len); | ||
| 174 | if (err && errno != EINPROGRESS) { | ||
| 175 | fprintf(stderr, "connect error %d\n", errno); | ||
| 176 | return err; | ||
| 177 | } | ||
| 178 | |||
| 179 | pfd = accept(ctx.sfd, NULL, NULL); | ||
| 180 | if (pfd < 0) { | ||
| 181 | fprintf(stderr, "accept error %d\n", errno); | ||
| 182 | return err; | ||
| 183 | } | ||
| 184 | *c = cfd; | ||
| 185 | *p = pfd; | ||
| 186 | return 0; | ||
| 187 | } | ||
| 188 | |||
| 189 | static int create_sockets(void) | ||
| 190 | { | ||
| 191 | struct sockaddr_storage addr; | ||
| 192 | int err, one = 1; | ||
| 193 | socklen_t addr_len; | ||
| 194 | |||
| 195 | init_addr(&addr, &addr_len); | ||
| 196 | ctx.sfd = socket(AF_INET, SOCK_STREAM, 0); | ||
| 197 | if (ctx.sfd < 0) { | ||
| 198 | fprintf(stderr, "socket error:%d\n", errno); | ||
| 199 | return ctx.sfd; | ||
| 200 | } | ||
| 201 | err = setsockopt(ctx.sfd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); | ||
| 202 | if (err) { | ||
| 203 | fprintf(stderr, "setsockopt error:%d\n", errno); | ||
| 204 | return err; | ||
| 205 | } | ||
| 206 | |||
| 207 | err = bind(ctx.sfd, (struct sockaddr *)&addr, addr_len); | ||
| 208 | if (err) { | ||
| 209 | fprintf(stderr, "bind error:%d\n", errno); | ||
| 210 | return err; | ||
| 211 | } | ||
| 212 | |||
| 213 | err = listen(ctx.sfd, SOMAXCONN); | ||
| 214 | if (err) { | ||
| 215 | fprintf(stderr, "listen error:%d\n", errno); | ||
| 216 | return err; | ||
| 217 | } | ||
| 218 | |||
| 219 | err = create_pair(&ctx.c1, &ctx.p1, SOCK_STREAM); | ||
| 220 | if (err) { | ||
| 221 | fprintf(stderr, "create_pair 1 error\n"); | ||
| 222 | return err; | ||
| 223 | } | ||
| 224 | |||
| 225 | err = create_pair(&ctx.c2, &ctx.p2, SOCK_STREAM); | ||
| 226 | if (err) { | ||
| 227 | fprintf(stderr, "create_pair 2 error\n"); | ||
| 228 | return err; | ||
| 229 | } | ||
| 230 | printf("create socket fd c1:%d p1:%d c2:%d p2:%d\n", | ||
| 231 | ctx.c1, ctx.p1, ctx.c2, ctx.p2); | ||
| 232 | return 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | static void validate(void) | ||
| 236 | { | ||
| 237 | if (env.consumer_cnt != 2 || env.producer_cnt != 1 || | ||
| 238 | !env.affinity) | ||
| 239 | goto err; | ||
| 240 | return; | ||
| 241 | err: | ||
| 242 | fprintf(stderr, "argument '-c 2 -p 1 -a' is necessary"); | ||
| 243 | exit(1); | ||
| 244 | } | ||
| 245 | |||
| 246 | static int setup_rx_sockmap(void) | ||
| 247 | { | ||
| 248 | int verdict, pass, parser, map; | ||
| 249 | int zero = 0, one = 1; | ||
| 250 | int err; | ||
| 251 | |||
| 252 | parser = bpf_program__fd(ctx.skel->progs.prog_skb_parser); | ||
| 253 | verdict = bpf_program__fd(ctx.skel->progs.prog_skb_verdict); | ||
| 254 | pass = bpf_program__fd(ctx.skel->progs.prog_skb_pass); | ||
| 255 | map = bpf_map__fd(ctx.skel->maps.sock_map_rx); | ||
| 256 | |||
| 257 | if (ctx.strp_size != 0) { | ||
| 258 | ctx.skel->bss->pkt_size = ctx.strp_size; | ||
| 259 | err = bpf_prog_attach(parser, map, BPF_SK_SKB_STREAM_PARSER, 0); | ||
| 260 | if (err) | ||
| 261 | return err; | ||
| 262 | } | ||
| 263 | |||
| 264 | if (RXMODE_BPF_VERDICT()) | ||
| 265 | err = bpf_prog_attach(verdict, map, BPF_SK_SKB_STREAM_VERDICT, 0); | ||
| 266 | else if (RXMODE_BPF_PASS()) | ||
| 267 | err = bpf_prog_attach(pass, map, BPF_SK_SKB_STREAM_VERDICT, 0); | ||
| 268 | if (err) | ||
| 269 | return err; | ||
| 270 | |||
| 271 | if (RXMODE_BPF_PASS()) | ||
| 272 | return bpf_map_update_elem(map, &zero, &ctx.c2, BPF_NOEXIST); | ||
| 273 | |||
| 274 | err = bpf_map_update_elem(map, &zero, &ctx.p1, BPF_NOEXIST); | ||
| 275 | if (err < 0) | ||
| 276 | return err; | ||
| 277 | |||
| 278 | if (RXMODE_BPF_VERDICT_INGRESS()) { | ||
| 279 | ctx.skel->bss->verdict_dir = BPF_F_INGRESS; | ||
| 280 | err = bpf_map_update_elem(map, &one, &ctx.c2, BPF_NOEXIST); | ||
| 281 | } else { | ||
| 282 | err = bpf_map_update_elem(map, &one, &ctx.p2, BPF_NOEXIST); | ||
| 283 | } | ||
| 284 | if (err < 0) | ||
| 285 | return err; | ||
| 286 | |||
| 287 | return 0; | ||
| 288 | } | ||
| 289 | |||
| 290 | static int setup_tx_sockmap(void) | ||
| 291 | { | ||
| 292 | int zero = 0, one = 1; | ||
| 293 | int prog, map; | ||
| 294 | int err; | ||
| 295 | |||
| 296 | map = bpf_map__fd(ctx.skel->maps.sock_map_tx); | ||
| 297 | prog = TXMODE_BPF_PASS() ? | ||
| 298 | bpf_program__fd(ctx.skel->progs.prog_skmsg_pass) : | ||
| 299 | bpf_program__fd(ctx.skel->progs.prog_skmsg_verdict); | ||
| 300 | |||
| 301 | err = bpf_prog_attach(prog, map, BPF_SK_MSG_VERDICT, 0); | ||
| 302 | if (err) | ||
| 303 | return err; | ||
| 304 | |||
| 305 | if (TXMODE_BPF_EGRESS()) { | ||
| 306 | err = bpf_map_update_elem(map, &zero, &ctx.p1, BPF_NOEXIST); | ||
| 307 | err |= bpf_map_update_elem(map, &one, &ctx.p2, BPF_NOEXIST); | ||
| 308 | } else { | ||
| 309 | ctx.skel->bss->verdict_dir = BPF_F_INGRESS; | ||
| 310 | err = bpf_map_update_elem(map, &zero, &ctx.p2, BPF_NOEXIST); | ||
| 311 | err |= bpf_map_update_elem(map, &one, &ctx.c2, BPF_NOEXIST); | ||
| 312 | } | ||
| 313 | |||
| 314 | if (err < 0) | ||
| 315 | return err; | ||
| 316 | |||
| 317 | return 0; | ||
| 318 | } | ||
| 319 | |||
| 320 | static void setup(void) | ||
| 321 | { | ||
| 322 | int err; | ||
| 323 | |||
| 324 | ctx.skel = bench_sockmap_prog__open_and_load(); | ||
| 325 | if (!ctx.skel) { | ||
| 326 | fprintf(stderr, "error loading skel\n"); | ||
| 327 | exit(1); | ||
| 328 | } | ||
| 329 | |||
| 330 | if (create_sockets()) { | ||
| 331 | fprintf(stderr, "create_net_mode error\n"); | ||
| 332 | goto err; | ||
| 333 | } | ||
| 334 | |||
| 335 | if (RXMODE_BPF()) { | ||
| 336 | err = setup_rx_sockmap(); | ||
| 337 | if (err) { | ||
| 338 | fprintf(stderr, "setup_rx_sockmap error:%d\n", err); | ||
| 339 | goto err; | ||
| 340 | } | ||
| 341 | } else if (TXMODE_BPF()) { | ||
| 342 | err = setup_tx_sockmap(); | ||
| 343 | if (err) { | ||
| 344 | fprintf(stderr, "setup_tx_sockmap error:%d\n", err); | ||
| 345 | goto err; | ||
| 346 | } | ||
| 347 | } else { | ||
| 348 | fprintf(stderr, "unknown sockmap bench mode: %d\n", ctx.mode); | ||
| 349 | goto err; | ||
| 350 | } | ||
| 351 | |||
| 352 | return; | ||
| 353 | |||
| 354 | err: | ||
| 355 | bench_sockmap_prog_destroy(); | ||
| 356 | exit(1); | ||
| 357 | } | ||
| 358 | |||
| 359 | static void measure(struct bench_res *res) | ||
| 360 | { | ||
| 361 | res->drops = atomic_swap(&ctx.prod_send, 0); | ||
| 362 | res->hits = atomic_swap(&ctx.skel->bss->process_byte, 0); | ||
| 363 | res->false_hits = atomic_swap(&ctx.user_read, 0); | ||
| 364 | res->important_hits = atomic_swap(&ctx.send_calls, 0); | ||
| 365 | res->important_hits |= atomic_swap(&ctx.read_calls, 0) << 32; | ||
| 366 | } | ||
| 367 | |||
| 368 | static void verify_data(int *check_pos, char *buf, int rcv) | ||
| 369 | { | ||
| 370 | for (int i = 0 ; i < rcv; i++) { | ||
| 371 | if (buf[i] != snd_data[(*check_pos) % DATA_REPEAT_SIZE]) { | ||
| 372 | fprintf(stderr, "verify data fail"); | ||
| 373 | exit(1); | ||
| 374 | } | ||
| 375 | (*check_pos)++; | ||
| 376 | if (*check_pos >= FILE_SIZE) | ||
| 377 | *check_pos = 0; | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | static void *consumer(void *input) | ||
| 382 | { | ||
| 383 | int rcv, sent; | ||
| 384 | int check_pos = 0; | ||
| 385 | int tid = (long)input; | ||
| 386 | int recv_buf_size = FILE_SIZE; | ||
| 387 | char *buf = malloc(recv_buf_size); | ||
| 388 | int delay_read = ctx.delay_consumer; | ||
| 389 | |||
| 390 | if (!buf) { | ||
| 391 | fprintf(stderr, "fail to init read buffer"); | ||
| 392 | return NULL; | ||
| 393 | } | ||
| 394 | |||
| 395 | while (true) { | ||
| 396 | if (tid == 1) { | ||
| 397 | /* consumer 1 is unused for tx test and stream verdict test */ | ||
| 398 | if (RXMODE_BPF() || TXMODE()) | ||
| 399 | return NULL; | ||
| 400 | /* it's only for RX_NORMAL which service as reserve-proxy mode */ | ||
| 401 | rcv = read(ctx.p1, buf, recv_buf_size); | ||
| 402 | if (rcv < 0) { | ||
| 403 | fprintf(stderr, "fail to read p1"); | ||
| 404 | return NULL; | ||
| 405 | } | ||
| 406 | |||
| 407 | sent = send(ctx.p2, buf, recv_buf_size, 0); | ||
| 408 | if (sent < 0) { | ||
| 409 | fprintf(stderr, "fail to send p2"); | ||
| 410 | return NULL; | ||
| 411 | } | ||
| 412 | } else { | ||
| 413 | if (delay_read != 0) { | ||
| 414 | if (delay_read < 0) | ||
| 415 | return NULL; | ||
| 416 | sleep(delay_read); | ||
| 417 | delay_read = 0; | ||
| 418 | } | ||
| 419 | /* read real endpoint by consumer 0 */ | ||
| 420 | atomic_inc(&ctx.read_calls); | ||
| 421 | rcv = read(ctx.c2, buf, recv_buf_size); | ||
| 422 | if (rcv < 0 && errno != EAGAIN) { | ||
| 423 | fprintf(stderr, "%s fail to read c2 %d\n", __func__, errno); | ||
| 424 | return NULL; | ||
| 425 | } | ||
| 426 | verify_data(&check_pos, buf, rcv); | ||
| 427 | atomic_add(&ctx.user_read, rcv); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | return NULL; | ||
| 432 | } | ||
| 433 | |||
| 434 | static void *producer(void *input) | ||
| 435 | { | ||
| 436 | int off = 0, fp, need_sent, sent; | ||
| 437 | int file_size = ctx.file_size; | ||
| 438 | struct timespec ts1, ts2; | ||
| 439 | int target; | ||
| 440 | FILE *file; | ||
| 441 | |||
| 442 | file = tmpfile(); | ||
| 443 | if (!file) { | ||
| 444 | fprintf(stderr, "create file for sendfile"); | ||
| 445 | return NULL; | ||
| 446 | } | ||
| 447 | |||
| 448 | /* we need simple verify */ | ||
| 449 | for (int i = 0; i < file_size; i++) { | ||
| 450 | if (fwrite(&snd_data[off], sizeof(char), 1, file) != 1) { | ||
| 451 | fprintf(stderr, "init tmpfile error"); | ||
| 452 | return NULL; | ||
| 453 | } | ||
| 454 | if (++off >= sizeof(snd_data)) | ||
| 455 | off = 0; | ||
| 456 | } | ||
| 457 | fflush(file); | ||
| 458 | fseek(file, 0, SEEK_SET); | ||
| 459 | |||
| 460 | fp = fileno(file); | ||
| 461 | need_sent = file_size; | ||
| 462 | clock_gettime(CLOCK_MONOTONIC, &ts1); | ||
| 463 | |||
| 464 | if (RXMODE_BPF_VERDICT()) | ||
| 465 | target = ctx.c1; | ||
| 466 | else if (TXMODE_BPF_EGRESS()) | ||
| 467 | target = ctx.p1; | ||
| 468 | else | ||
| 469 | target = ctx.p2; | ||
| 470 | set_non_block(target, true); | ||
| 471 | while (true) { | ||
| 472 | if (ctx.prod_run_time) { | ||
| 473 | clock_gettime(CLOCK_MONOTONIC, &ts2); | ||
| 474 | if (ts2.tv_sec - ts1.tv_sec > ctx.prod_run_time) | ||
| 475 | return NULL; | ||
| 476 | } | ||
| 477 | |||
| 478 | errno = 0; | ||
| 479 | atomic_inc(&ctx.send_calls); | ||
| 480 | sent = sendfile(target, fp, NULL, need_sent); | ||
| 481 | if (sent < 0) { | ||
| 482 | if (errno != EAGAIN && errno != ENOMEM && errno != ENOBUFS) { | ||
| 483 | fprintf(stderr, "sendfile return %d, errorno %d:%s\n", | ||
| 484 | sent, errno, strerror(errno)); | ||
| 485 | return NULL; | ||
| 486 | } | ||
| 487 | continue; | ||
| 488 | } else if (sent < need_sent) { | ||
| 489 | need_sent -= sent; | ||
| 490 | atomic_add(&ctx.prod_send, sent); | ||
| 491 | continue; | ||
| 492 | } | ||
| 493 | atomic_add(&ctx.prod_send, need_sent); | ||
| 494 | need_sent = file_size; | ||
| 495 | lseek(fp, 0, SEEK_SET); | ||
| 496 | } | ||
| 497 | |||
| 498 | return NULL; | ||
| 499 | } | ||
| 500 | |||
| 501 | static void report_progress(int iter, struct bench_res *res, long delta_ns) | ||
| 502 | { | ||
| 503 | double speed_mbs, prod_mbs, bpf_mbs, send_hz, read_hz; | ||
| 504 | |||
| 505 | prod_mbs = res->drops / 1000000.0 / (delta_ns / 1000000000.0); | ||
| 506 | speed_mbs = res->false_hits / 1000000.0 / (delta_ns / 1000000000.0); | ||
| 507 | bpf_mbs = res->hits / 1000000.0 / (delta_ns / 1000000000.0); | ||
| 508 | send_hz = (res->important_hits & 0xFFFFFFFF) / (delta_ns / 1000000000.0); | ||
| 509 | read_hz = (res->important_hits >> 32) / (delta_ns / 1000000000.0); | ||
| 510 | |||
| 511 | printf("Iter %3d (%7.3lfus): ", | ||
| 512 | iter, (delta_ns - 1000000000) / 1000.0); | ||
| 513 | printf("Send Speed %8.3lf MB/s (%8.3lf calls/s), BPF Speed %8.3lf MB/s, " | ||
| 514 | "Rcv Speed %8.3lf MB/s (%8.3lf calls/s)\n", | ||
| 515 | prod_mbs, send_hz, bpf_mbs, speed_mbs, read_hz); | ||
| 516 | } | ||
| 517 | |||
| 518 | static void report_final(struct bench_res res[], int res_cnt) | ||
| 519 | { | ||
| 520 | double verdict_mbs_mean = 0.0; | ||
| 521 | long verdict_total = 0; | ||
| 522 | int i; | ||
| 523 | |||
| 524 | for (i = 0; i < res_cnt; i++) { | ||
| 525 | verdict_mbs_mean += res[i].hits / 1000000.0 / (0.0 + res_cnt); | ||
| 526 | verdict_total += res[i].hits / 1000000.0; | ||
| 527 | } | ||
| 528 | |||
| 529 | printf("Summary: total trans %8.3lu MB \u00B1 %5.3lf MB/s\n", | ||
| 530 | verdict_total, verdict_mbs_mean); | ||
| 531 | } | ||
| 532 | |||
| 533 | static const struct argp_option opts[] = { | ||
| 534 | { "rx-normal", ARG_FW_RX_NORMAL, NULL, 0, | ||
| 535 | "simple reserve-proxy mode, no bfp enabled"}, | ||
| 536 | { "rx-pass", ARG_FW_RX_PASS, NULL, 0, | ||
| 537 | "run bpf prog but no redir applied"}, | ||
| 538 | { "rx-strp", ARG_CTL_RX_STRP, "Byte", 0, | ||
| 539 | "enable strparser and set the encapsulation size"}, | ||
| 540 | { "rx-verdict-egress", ARG_FW_RX_VERDICT_EGRESS, NULL, 0, | ||
| 541 | "forward data with bpf(stream verdict)"}, | ||
| 542 | { "rx-verdict-ingress", ARG_FW_RX_VERDICT_INGRESS, NULL, 0, | ||
| 543 | "forward data with bpf(stream verdict)"}, | ||
| 544 | { "tx-normal", ARG_FW_TX_NORMAL, NULL, 0, | ||
| 545 | "simple c-s mode, no bfp enabled"}, | ||
| 546 | { "tx-pass", ARG_FW_TX_PASS, NULL, 0, | ||
| 547 | "run bpf prog but no redir applied"}, | ||
| 548 | { "tx-verdict-ingress", ARG_FW_TX_VERDICT_INGRESS, NULL, 0, | ||
| 549 | "forward msg to ingress queue of another socket"}, | ||
| 550 | { "tx-verdict-egress", ARG_FW_TX_VERDICT_EGRESS, NULL, 0, | ||
| 551 | "forward msg to egress queue of another socket"}, | ||
| 552 | { "delay-consumer", ARG_CONSUMER_DELAY_TIME, "SEC", 0, | ||
| 553 | "delay consumer start"}, | ||
| 554 | { "producer-duration", ARG_PRODUCER_DURATION, "SEC", 0, | ||
| 555 | "producer duration"}, | ||
| 556 | {}, | ||
| 557 | }; | ||
| 558 | |||
| 559 | static error_t parse_arg(int key, char *arg, struct argp_state *state) | ||
| 560 | { | ||
| 561 | switch (key) { | ||
| 562 | case ARG_FW_RX_NORMAL...ARG_FW_TX_VERDICT_EGRESS: | ||
| 563 | ctx.mode = key; | ||
| 564 | break; | ||
| 565 | case ARG_CONSUMER_DELAY_TIME: | ||
| 566 | ctx.delay_consumer = strtol(arg, NULL, 10); | ||
| 567 | break; | ||
| 568 | case ARG_PRODUCER_DURATION: | ||
| 569 | ctx.prod_run_time = strtol(arg, NULL, 10); | ||
| 570 | break; | ||
| 571 | case ARG_CTL_RX_STRP: | ||
| 572 | ctx.strp_size = strtol(arg, NULL, 10); | ||
| 573 | break; | ||
| 574 | default: | ||
| 575 | return ARGP_ERR_UNKNOWN; | ||
| 576 | } | ||
| 577 | |||
| 578 | return 0; | ||
| 579 | } | ||
| 580 | |||
| 581 | /* exported into benchmark runner */ | ||
| 582 | const struct argp bench_sockmap_argp = { | ||
| 583 | .options = opts, | ||
| 584 | .parser = parse_arg, | ||
| 585 | }; | ||
| 586 | |||
| 587 | /* Benchmark performance of creating bpf local storage */ | ||
| 588 | const struct bench bench_sockmap = { | ||
| 589 | .name = "sockmap", | ||
| 590 | .argp = &bench_sockmap_argp, | ||
| 591 | .validate = validate, | ||
| 592 | .setup = setup, | ||
| 593 | .producer_thread = producer, | ||
| 594 | .consumer_thread = consumer, | ||
| 595 | .measure = measure, | ||
| 596 | .report_progress = report_progress, | ||
| 597 | .report_final = report_final, | ||
| 598 | }; | ||
| @@ -591,4 +591,9 @@ extern int bpf_iter_kmem_cache_new(struct bpf_iter_kmem_cache *it) __weak __ksym · 590 unmodified lines | |||
| 591 | extern struct kmem_cache *bpf_iter_kmem_cache_next(struct bpf_iter_kmem_cache *it) __weak __ksym; | 591 | extern struct kmem_cache *bpf_iter_kmem_cache_next(struct bpf_iter_kmem_cache *it) __weak __ksym; |
| 592 | extern void bpf_iter_kmem_cache_destroy(struct bpf_iter_kmem_cache *it) __weak __ksym; | 592 | extern void bpf_iter_kmem_cache_destroy(struct bpf_iter_kmem_cache *it) __weak __ksym; |
| 593 | 593 | ||
| 594 | struct bpf_iter_dmabuf; | ||
| 595 | extern int bpf_iter_dmabuf_new(struct bpf_iter_dmabuf *it) __weak __ksym; | ||
| 596 | extern struct dma_buf *bpf_iter_dmabuf_next(struct bpf_iter_dmabuf *it) __weak __ksym; | ||
| 597 | extern void bpf_iter_dmabuf_destroy(struct bpf_iter_dmabuf *it) __weak __ksym; | ||
| 598 | |||
| 594 | #endif | 599 | #endif |
| @@ -22,6 +22,8 @@ CONFIG_CRYPTO_AES=y · 21 unmodified lines | |||
| 22 | CONFIG_DEBUG_INFO=y | 22 | CONFIG_DEBUG_INFO=y |
| 23 | CONFIG_DEBUG_INFO_BTF=y | 23 | CONFIG_DEBUG_INFO_BTF=y |
| 24 | CONFIG_DEBUG_INFO_DWARF4=y | 24 | CONFIG_DEBUG_INFO_DWARF4=y |
| 25 | CONFIG_DMABUF_HEAPS=y | ||
| 26 | CONFIG_DMABUF_HEAPS_SYSTEM=y | ||
| 25 | CONFIG_DUMMY=y | 27 | CONFIG_DUMMY=y |
| 26 | CONFIG_DYNAMIC_FTRACE=y | 28 | CONFIG_DYNAMIC_FTRACE=y |
| 27 | CONFIG_FPROBE=y | 29 | CONFIG_FPROBE=y |
| @@ -108,6 +110,7 @@ CONFIG_SECURITY=y · 80 unmodified lines | |||
| 108 | CONFIG_SECURITYFS=y | 110 | CONFIG_SECURITYFS=y |
| 109 | CONFIG_SYN_COOKIES=y | 111 | CONFIG_SYN_COOKIES=y |
| 110 | CONFIG_TEST_BPF=m | 112 | CONFIG_TEST_BPF=m |
| 113 | CONFIG_UDMABUF=y | ||
| 111 | CONFIG_USERFAULTFD=y | 114 | CONFIG_USERFAULTFD=y |
| 112 | CONFIG_VSOCKETS=y | 115 | CONFIG_VSOCKETS=y |
| 113 | CONFIG_VXLAN=y | 116 | CONFIG_VXLAN=y |
| @@ -51,9 +51,11 @@ static void test_arena_spin_lock_size(int size) · 50 unmodified lines | |||
| 51 | struct arena_spin_lock *skel; | 51 | struct arena_spin_lock *skel; |
| 52 | pthread_t thread_id[16]; | 52 | pthread_t thread_id[16]; |
| 53 | int prog_fd, i, err; | 53 | int prog_fd, i, err; |
| 54 | int nthreads; | ||
| 54 | void *ret; | 55 | void *ret; |
| 55 | 56 | ||
| 56 | if (get_nprocs() < 2) { | 57 | nthreads = MIN(get_nprocs(), ARRAY_SIZE(thread_id)); |
| 58 | if (nthreads < 2) { | ||
| 57 | test__skip(); | 59 | test__skip(); |
| 58 | return; | 60 | return; |
| 59 | } | 61 | } |
| @@ -66,25 +68,25 @@ static void test_arena_spin_lock_size(int size) · 6 unmodified lines | |||
| 66 | goto end; | 68 | goto end; |
| 67 | } | 69 | } |
| 68 | skel->bss->cs_count = size; | 70 | skel->bss->cs_count = size; |
| 69 | skel->bss->limit = repeat * 16; | 71 | skel->bss->limit = repeat * nthreads; |
| 70 | 72 | ||
| 71 | ASSERT_OK(pthread_barrier_init(&barrier, NULL, 16), "barrier init"); | 73 | ASSERT_OK(pthread_barrier_init(&barrier, NULL, nthreads), "barrier init"); |
| 72 | 74 | ||
| 73 | prog_fd = bpf_program__fd(skel->progs.prog); | 75 | prog_fd = bpf_program__fd(skel->progs.prog); |
| 74 | for (i = 0; i < 16; i++) { | 76 | for (i = 0; i < nthreads; i++) { |
| 75 | err = pthread_create(&thread_id[i], NULL, &spin_lock_thread, &prog_fd); | 77 | err = pthread_create(&thread_id[i], NULL, &spin_lock_thread, &prog_fd); |
| 76 | if (!ASSERT_OK(err, "pthread_create")) | 78 | if (!ASSERT_OK(err, "pthread_create")) |
| 77 | goto end_barrier; | 79 | goto end_barrier; |
| 78 | } | 80 | } |
| 79 | 81 | ||
| 80 | for (i = 0; i < 16; i++) { | 82 | for (i = 0; i < nthreads; i++) { |
| 81 | if (!ASSERT_OK(pthread_join(thread_id[i], &ret), "pthread_join")) | 83 | if (!ASSERT_OK(pthread_join(thread_id[i], &ret), "pthread_join")) |
| 82 | goto end_barrier; | 84 | goto end_barrier; |
| 83 | if (!ASSERT_EQ(ret, &prog_fd, "ret == prog_fd")) | 85 | if (!ASSERT_EQ(ret, &prog_fd, "ret == prog_fd")) |
| 84 | goto end_barrier; | 86 | goto end_barrier; |
| 85 | } | 87 | } |
| 86 | 88 | ||
| 87 | ASSERT_EQ(skel->bss->counter, repeat * 16, "check counter value"); | 89 | ASSERT_EQ(skel->bss->counter, repeat * nthreads, "check counter value"); |
| 88 | 90 | ||
| 89 | end_barrier: | 91 | end_barrier: |
| 90 | pthread_barrier_destroy(&barrier); | 92 | pthread_barrier_destroy(&barrier); |
| @@ -122,6 +122,85 @@ cleanup: · 121 unmodified lines | |||
| 122 | test_attach_probe_manual__destroy(skel); | 122 | test_attach_probe_manual__destroy(skel); |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | /* attach uprobe/uretprobe long event name testings */ | ||
| 126 | static void test_attach_uprobe_long_event_name(void) | ||
| 127 | { | ||
| 128 | DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); | ||
| 129 | struct bpf_link *uprobe_link, *uretprobe_link; | ||
| 130 | struct test_attach_probe_manual *skel; | ||
| 131 | ssize_t uprobe_offset; | ||
| 132 | char path[PATH_MAX] = {0}; | ||
| 133 | |||
| 134 | skel = test_attach_probe_manual__open_and_load(); | ||
| 135 | if (!ASSERT_OK_PTR(skel, "skel_kprobe_manual_open_and_load")) | ||
| 136 | return; | ||
| 137 | |||
| 138 | uprobe_offset = get_uprobe_offset(&trigger_func); | ||
| 139 | if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset")) | ||
| 140 | goto cleanup; | ||
| 141 | |||
| 142 | if (!ASSERT_GT(readlink("/proc/self/exe", path, PATH_MAX - 1), 0, "readlink")) | ||
| 143 | goto cleanup; | ||
| 144 | |||
| 145 | /* manual-attach uprobe/uretprobe */ | ||
| 146 | uprobe_opts.attach_mode = PROBE_ATTACH_MODE_LEGACY; | ||
| 147 | uprobe_opts.ref_ctr_offset = 0; | ||
| 148 | uprobe_opts.retprobe = false; | ||
| 149 | uprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe, | ||
| 150 | 0 /* self pid */, | ||
| 151 | path, | ||
| 152 | uprobe_offset, | ||
| 153 | &uprobe_opts); | ||
| 154 | if (!ASSERT_OK_PTR(uprobe_link, "attach_uprobe_long_event_name")) | ||
| 155 | goto cleanup; | ||
| 156 | skel->links.handle_uprobe = uprobe_link; | ||
| 157 | |||
| 158 | uprobe_opts.retprobe = true; | ||
| 159 | uretprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uretprobe, | ||
| 160 | -1 /* any pid */, | ||
| 161 | path, | ||
| 162 | uprobe_offset, &uprobe_opts); | ||
| 163 | if (!ASSERT_OK_PTR(uretprobe_link, "attach_uretprobe_long_event_name")) | ||
| 164 | goto cleanup; | ||
| 165 | skel->links.handle_uretprobe = uretprobe_link; | ||
| 166 | |||
| 167 | cleanup: | ||
| 168 | test_attach_probe_manual__destroy(skel); | ||
| 169 | } | ||
| 170 | |||
| 171 | /* attach kprobe/kretprobe long event name testings */ | ||
| 172 | static void test_attach_kprobe_long_event_name(void) | ||
| 173 | { | ||
| 174 | DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, kprobe_opts); | ||
| 175 | struct bpf_link *kprobe_link, *kretprobe_link; | ||
| 176 | struct test_attach_probe_manual *skel; | ||
| 177 | |||
| 178 | skel = test_attach_probe_manual__open_and_load(); | ||
| 179 | if (!ASSERT_OK_PTR(skel, "skel_kprobe_manual_open_and_load")) | ||
| 180 | return; | ||
| 181 | |||
| 182 | /* manual-attach kprobe/kretprobe */ | ||
| 183 | kprobe_opts.attach_mode = PROBE_ATTACH_MODE_LEGACY; | ||
| 184 | kprobe_opts.retprobe = false; | ||
| 185 | kprobe_link = bpf_program__attach_kprobe_opts(skel->progs.handle_kprobe, | ||
| 186 | "bpf_testmod_looooooooooooooooooooooooooooooong_name", | ||
| 187 | &kprobe_opts); | ||
| 188 | if (!ASSERT_OK_PTR(kprobe_link, "attach_kprobe_long_event_name")) | ||
| 189 | goto cleanup; | ||
| 190 | skel->links.handle_kprobe = kprobe_link; | ||
| 191 | |||
| 192 | kprobe_opts.retprobe = true; | ||
| 193 | kretprobe_link = bpf_program__attach_kprobe_opts(skel->progs.handle_kretprobe, | ||
| 194 | "bpf_testmod_looooooooooooooooooooooooooooooong_name", | ||
| 195 | &kprobe_opts); | ||
| 196 | if (!ASSERT_OK_PTR(kretprobe_link, "attach_kretprobe_long_event_name")) | ||
| 197 | goto cleanup; | ||
| 198 | skel->links.handle_kretprobe = kretprobe_link; | ||
| 199 | |||
| 200 | cleanup: | ||
| 201 | test_attach_probe_manual__destroy(skel); | ||
| 202 | } | ||
| 203 | |||
| 125 | static void test_attach_probe_auto(struct test_attach_probe *skel) | 204 | static void test_attach_probe_auto(struct test_attach_probe *skel) |
| 126 | { | 205 | { |
| 127 | struct bpf_link *uprobe_err_link; | 206 | struct bpf_link *uprobe_err_link; |
| @@ -323,6 +402,11 @@ void test_attach_probe(void) · 195 unmodified lines | |||
| 323 | if (test__start_subtest("uprobe-ref_ctr")) | 402 | if (test__start_subtest("uprobe-ref_ctr")) |
| 324 | test_uprobe_ref_ctr(skel); | 403 | test_uprobe_ref_ctr(skel); |
| 325 | 404 | ||
| 405 | if (test__start_subtest("uprobe-long_name")) | ||
| 406 | test_attach_uprobe_long_event_name(); | ||
| 407 | if (test__start_subtest("kprobe-long_name")) | ||
| 408 | test_attach_kprobe_long_event_name(); | ||
| 409 | |||
| 326 | cleanup: | 410 | cleanup: |
| 327 | test_attach_probe__destroy(skel); | 411 | test_attach_probe__destroy(skel); |
| 328 | ASSERT_EQ(uprobe_ref_ctr, 0, "uprobe_ref_ctr_cleanup"); | 412 | ASSERT_EQ(uprobe_ref_ctr, 0, "uprobe_ref_ctr_cleanup"); |
| @@ -63,6 +63,12 @@ static void test_bpf_nf_ct(int mode) · 62 unmodified lines | |||
| 63 | .repeat = 1, | 63 | .repeat = 1, |
| 64 | ); | 64 | ); |
| 65 | 65 | ||
| 66 | if (SYS_NOFAIL("iptables-legacy --version")) { | ||
| 67 | fprintf(stdout, "Missing required iptables-legacy tool\n"); | ||
| 68 | test__skip(); | ||
| 69 | return; | ||
| 70 | } | ||
| 71 | |||
| 66 | skel = test_bpf_nf__open_and_load(); | 72 | skel = test_bpf_nf__open_and_load(); |
| 67 | if (!ASSERT_OK_PTR(skel, "test_bpf_nf__open_and_load")) | 73 | if (!ASSERT_OK_PTR(skel, "test_bpf_nf__open_and_load")) |
| 68 | return; | 74 | return; |
| @@ -440,6 +440,105 @@ cleanup: · 439 unmodified lines | |||
| 440 | btf__free(btf1); | 440 | btf__free(btf1); |
| 441 | } | 441 | } |
| 442 | 442 | ||
| 443 | /* Ensure module split BTF dedup worked correctly; when dedup fails badly | ||
| 444 | * core kernel types are in split BTF also, so ensure that references to | ||
| 445 | * such types point at base - not split - BTF. | ||
| 446 | * | ||
| 447 | * bpf_testmod_test_write() has multiple core kernel type parameters; | ||
| 448 | * | ||
| 449 | * ssize_t | ||
| 450 | * bpf_testmod_test_write(struct file *file, struct kobject *kobj, | ||
| 451 | * struct bin_attribute *bin_attr, | ||
| 452 | * char *buf, loff_t off, size_t len); | ||
| 453 | * | ||
| 454 | * Ensure each of the FUNC_PROTO params is a core kernel type. | ||
| 455 | * | ||
| 456 | * Do the same for | ||
| 457 | * | ||
| 458 | * __bpf_kfunc struct sock *bpf_kfunc_call_test3(struct sock *sk); | ||
| 459 | * | ||
| 460 | * ...and | ||
| 461 | * | ||
| 462 | * __bpf_kfunc void bpf_kfunc_call_test_pass_ctx(struct __sk_buff *skb); | ||
| 463 | * | ||
| 464 | */ | ||
| 465 | const char *mod_funcs[] = { | ||
| 466 | "bpf_testmod_test_write", | ||
| 467 | "bpf_kfunc_call_test3", | ||
| 468 | "bpf_kfunc_call_test_pass_ctx" | ||
| 469 | }; | ||
| 470 | |||
| 471 | static void test_split_module(void) | ||
| 472 | { | ||
| 473 | struct btf *vmlinux_btf, *btf1 = NULL; | ||
| 474 | int i, nr_base_types; | ||
| 475 | |||
| 476 | vmlinux_btf = btf__load_vmlinux_btf(); | ||
| 477 | if (!ASSERT_OK_PTR(vmlinux_btf, "vmlinux_btf")) | ||
| 478 | return; | ||
| 479 | nr_base_types = btf__type_cnt(vmlinux_btf); | ||
| 480 | if (!ASSERT_GT(nr_base_types, 0, "nr_base_types")) | ||
| 481 | goto cleanup; | ||
| 482 | |||
| 483 | btf1 = btf__parse_split("/sys/kernel/btf/bpf_testmod", vmlinux_btf); | ||
| 484 | if (!ASSERT_OK_PTR(btf1, "split_btf")) | ||
| 485 | return; | ||
| 486 | |||
| 487 | for (i = 0; i < ARRAY_SIZE(mod_funcs); i++) { | ||
| 488 | const struct btf_param *p; | ||
| 489 | const struct btf_type *t; | ||
| 490 | __u16 vlen; | ||
| 491 | __u32 id; | ||
| 492 | int j; | ||
| 493 | |||
| 494 | id = btf__find_by_name_kind(btf1, mod_funcs[i], BTF_KIND_FUNC); | ||
| 495 | if (!ASSERT_GE(id, nr_base_types, "func_id")) | ||
| 496 | goto cleanup; | ||
| 497 | t = btf__type_by_id(btf1, id); | ||
| 498 | if (!ASSERT_OK_PTR(t, "func_id_type")) | ||
| 499 | goto cleanup; | ||
| 500 | t = btf__type_by_id(btf1, t->type); | ||
| 501 | if (!ASSERT_OK_PTR(t, "func_proto_id_type")) | ||
| 502 | goto cleanup; | ||
| 503 | if (!ASSERT_EQ(btf_is_func_proto(t), true, "is_func_proto")) | ||
| 504 | goto cleanup; | ||
| 505 | vlen = btf_vlen(t); | ||
| 506 | |||
| 507 | for (j = 0, p = btf_params(t); j < vlen; j++, p++) { | ||
| 508 | /* bpf_testmod uses resilient split BTF, so any | ||
| 509 | * reference types will be added to split BTF and their | ||
| 510 | * associated targets will be base BTF types; for example | ||
| 511 | * for a "struct sock *" the PTR will be in split BTF | ||
| 512 | * while the "struct sock" will be in base. | ||
| 513 | * | ||
| 514 | * In some cases like loff_t we have to resolve | ||
| 515 | * multiple typedefs hence the while() loop below. | ||
| 516 | * | ||
| 517 | * Note that resilient split BTF generation depends | ||
| 518 | * on pahole version, so we do not assert that | ||
| 519 | * reference types are in split BTF, as if pahole | ||
| 520 | * does not support resilient split BTF they will | ||
| 521 | * also be base BTF types. | ||
| 522 | */ | ||
| 523 | id = p->type; | ||
| 524 | do { | ||
| 525 | t = btf__type_by_id(btf1, id); | ||
| 526 | if (!ASSERT_OK_PTR(t, "param_ref_type")) | ||
| 527 | goto cleanup; | ||
| 528 | if (!btf_is_mod(t) && !btf_is_ptr(t) && !btf_is_typedef(t)) | ||
| 529 | break; | ||
| 530 | id = t->type; | ||
| 531 | } while (true); | ||
| 532 | |||
| 533 | if (!ASSERT_LT(id, nr_base_types, "verify_base_type")) | ||
| 534 | goto cleanup; | ||
| 535 | } | ||
| 536 | } | ||
| 537 | cleanup: | ||
| 538 | btf__free(btf1); | ||
| 539 | btf__free(vmlinux_btf); | ||
| 540 | } | ||
| 541 | |||
| 443 | void test_btf_dedup_split() | 542 | void test_btf_dedup_split() |
| 444 | { | 543 | { |
| 445 | if (test__start_subtest("split_simple")) | 544 | if (test__start_subtest("split_simple")) |
| @@ -450,4 +549,6 @@ void test_btf_dedup_split() · 4 unmodified lines | |||
| 450 | test_split_fwd_resolve(); | 549 | test_split_fwd_resolve(); |
| 451 | if (test__start_subtest("split_dup_struct_in_cu")) | 550 | if (test__start_subtest("split_dup_struct_in_cu")) |
| 452 | test_split_dup_struct_in_cu(); | 551 | test_split_dup_struct_in_cu(); |
| 552 | if (test__start_subtest("split_module")) | ||
| 553 | test_split_module(); | ||
| 453 | } | 554 | } |
| @@ -12,10 +12,11 @@ static void btf_dump_printf(void *ctx, const char *fmt, va_list args) · 11 unmodified lines | |||
| 12 | vfprintf(ctx, fmt, args); | 12 | vfprintf(ctx, fmt, args); |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | void test_btf_split() { | 15 | static void __test_btf_split(bool multi) |
| 16 | { | ||
| 16 | struct btf_dump *d = NULL; | 17 | struct btf_dump *d = NULL; |
| 17 | const struct btf_type *t; | 18 | const struct btf_type *t; |
| 18 | struct btf *btf1, *btf2; | 19 | struct btf *btf1, *btf2, *btf3 = NULL; |
| 19 | int str_off, i, err; | 20 | int str_off, i, err; |
| 20 | 21 | ||
| 21 | btf1 = btf__new_empty(); | 22 | btf1 = btf__new_empty(); |
| @@ -63,14 +64,46 @@ void test_btf_split() { · 41 unmodified lines | |||
| 63 | ASSERT_EQ(btf_vlen(t), 3, "split_struct_vlen"); | 64 | ASSERT_EQ(btf_vlen(t), 3, "split_struct_vlen"); |
| 64 | ASSERT_STREQ(btf__str_by_offset(btf2, t->name_off), "s2", "split_struct_name"); | 65 | ASSERT_STREQ(btf__str_by_offset(btf2, t->name_off), "s2", "split_struct_name"); |
| 65 | 66 | ||
| 67 | if (multi) { | ||
| 68 | btf3 = btf__new_empty_split(btf2); | ||
| 69 | if (!ASSERT_OK_PTR(btf3, "multi_split_btf")) | ||
| 70 | goto cleanup; | ||
| 71 | } else { | ||
| 72 | btf3 = btf2; | ||
| 73 | } | ||
| 74 | |||
| 75 | btf__add_union(btf3, "u1", 16); /* [5] union u1 { */ | ||
| 76 | btf__add_field(btf3, "f1", 4, 0, 0); /* struct s2 f1; */ | ||
| 77 | btf__add_field(btf3, "uf2", 1, 0, 0); /* int f2; */ | ||
| 78 | /* } */ | ||
| 79 | |||
| 80 | if (multi) { | ||
| 81 | t = btf__type_by_id(btf2, 5); | ||
| 82 | ASSERT_NULL(t, "multisplit_type_in_first_split"); | ||
| 83 | } | ||
| 84 | |||
| 85 | t = btf__type_by_id(btf3, 5); | ||
| 86 | if (!ASSERT_OK_PTR(t, "split_union_type")) | ||
| 87 | goto cleanup; | ||
| 88 | ASSERT_EQ(btf_is_union(t), true, "split_union_kind"); | ||
| 89 | ASSERT_EQ(btf_vlen(t), 2, "split_union_vlen"); | ||
| 90 | ASSERT_STREQ(btf__str_by_offset(btf3, t->name_off), "u1", "split_union_name"); | ||
| 91 | ASSERT_EQ(btf__type_cnt(btf3), 6, "split_type_cnt"); | ||
| 92 | |||
| 93 | t = btf__type_by_id(btf3, 1); | ||
| 94 | if (!ASSERT_OK_PTR(t, "split_base_type")) | ||
| 95 | goto cleanup; | ||
| 96 | ASSERT_EQ(btf_is_int(t), true, "split_base_int"); | ||
| 97 | ASSERT_STREQ(btf__str_by_offset(btf3, t->name_off), "int", "split_base_type_name"); | ||
| 98 | |||
| 66 | /* BTF-to-C dump of split BTF */ | 99 | /* BTF-to-C dump of split BTF */ |
| 67 | dump_buf_file = open_memstream(&dump_buf, &dump_buf_sz); | 100 | dump_buf_file = open_memstream(&dump_buf, &dump_buf_sz); |
| 68 | if (!ASSERT_OK_PTR(dump_buf_file, "dump_memstream")) | 101 | if (!ASSERT_OK_PTR(dump_buf_file, "dump_memstream")) |
| 69 | return; | 102 | return; |
| 70 | d = btf_dump__new(btf2, btf_dump_printf, dump_buf_file, NULL); | 103 | d = btf_dump__new(btf3, btf_dump_printf, dump_buf_file, NULL); |
| 71 | if (!ASSERT_OK_PTR(d, "btf_dump__new")) | 104 | if (!ASSERT_OK_PTR(d, "btf_dump__new")) |
| 72 | goto cleanup; | 105 | goto cleanup; |
| 73 | for (i = 1; i < btf__type_cnt(btf2); i++) { | 106 | for (i = 1; i < btf__type_cnt(btf3); i++) { |
| 74 | err = btf_dump__dump_type(d, i); | 107 | err = btf_dump__dump_type(d, i); |
| 75 | ASSERT_OK(err, "dump_type_ok"); | 108 | ASSERT_OK(err, "dump_type_ok"); |
| 76 | } | 109 | } |
| @@ -79,12 +112,15 @@ void test_btf_split() { · 2 unmodified lines | |||
| 79 | ASSERT_STREQ(dump_buf, | 112 | ASSERT_STREQ(dump_buf, |
| 80 | "struct s1 {\n" | 113 | "struct s1 {\n" |
| 81 | " int f1;\n" | 114 | " int f1;\n" |
| 82 | "};\n" | 115 | "};\n\n" |
| 83 | "\n" | ||
| 84 | "struct s2 {\n" | 116 | "struct s2 {\n" |
| 85 | " struct s1 f1;\n" | 117 | " struct s1 f1;\n" |
| 86 | " int f2;\n" | 118 | " int f2;\n" |
| 87 | " int *f3;\n" | 119 | " int *f3;\n" |
| 120 | "};\n\n" | ||
| 121 | "union u1 {\n" | ||
| 122 | " struct s2 f1;\n" | ||
| 123 | " int uf2;\n" | ||
| 88 | "};\n\n", "c_dump"); | 124 | "};\n\n", "c_dump"); |
| 89 | 125 | ||
| 90 | cleanup: | 126 | cleanup: |
| @@ -94,4 +130,14 @@ cleanup: · 3 unmodified lines | |||
| 94 | btf_dump__free(d); | 130 | btf_dump__free(d); |
| 95 | btf__free(btf1); | 131 | btf__free(btf1); |
| 96 | btf__free(btf2); | 132 | btf__free(btf2); |
| 133 | if (btf2 != btf3) | ||
| 134 | btf__free(btf3); | ||
| 135 | } | ||
| 136 | |||
| 137 | void test_btf_split(void) | ||
| 138 | { | ||
| 139 | if (test__start_subtest("single_split")) | ||
| 140 | __test_btf_split(false); | ||
| 141 | if (test__start_subtest("multi_split")) | ||
| 142 | __test_btf_split(true); | ||
| 97 | } | 143 | } |
| @@ -0,0 +1,81 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause | ||
| 2 | /* Copyright (c) 2025 Isovalent */ | ||
| 3 | |||
| 4 | #include <test_progs.h> | ||
| 5 | #include <bpf/btf.h> | ||
| 6 | #include <sys/stat.h> | ||
| 7 | #include <sys/mman.h> | ||
| 8 | #include <fcntl.h> | ||
| 9 | #include <unistd.h> | ||
| 10 | |||
| 11 | static void test_btf_mmap_sysfs(const char *path, struct btf *base) | ||
| 12 | { | ||
| 13 | struct stat st; | ||
| 14 | __u64 btf_size, end; | ||
| 15 | void *raw_data = NULL; | ||
| 16 | int fd = -1; | ||
| 17 | long page_size; | ||
| 18 | struct btf *btf = NULL; | ||
| 19 | |||
| 20 | page_size = sysconf(_SC_PAGESIZE); | ||
| 21 | if (!ASSERT_GE(page_size, 0, "get_page_size")) | ||
| 22 | goto cleanup; | ||
| 23 | |||
| 24 | if (!ASSERT_OK(stat(path, &st), "stat_btf")) | ||
| 25 | goto cleanup; | ||
| 26 | |||
| 27 | btf_size = st.st_size; | ||
| 28 | end = (btf_size + page_size - 1) / page_size * page_size; | ||
| 29 | |||
| 30 | fd = open(path, O_RDONLY); | ||
| 31 | if (!ASSERT_GE(fd, 0, "open_btf")) | ||
| 32 | goto cleanup; | ||
| 33 | |||
| 34 | raw_data = mmap(NULL, btf_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); | ||
| 35 | if (!ASSERT_EQ(raw_data, MAP_FAILED, "mmap_btf_writable")) | ||
| 36 | goto cleanup; | ||
| 37 | |||
| 38 | raw_data = mmap(NULL, btf_size, PROT_READ, MAP_SHARED, fd, 0); | ||
| 39 | if (!ASSERT_EQ(raw_data, MAP_FAILED, "mmap_btf_shared")) | ||
| 40 | goto cleanup; | ||
| 41 | |||
| 42 | raw_data = mmap(NULL, end + 1, PROT_READ, MAP_PRIVATE, fd, 0); | ||
| 43 | if (!ASSERT_EQ(raw_data, MAP_FAILED, "mmap_btf_invalid_size")) | ||
| 44 | goto cleanup; | ||
| 45 | |||
| 46 | raw_data = mmap(NULL, end, PROT_READ, MAP_PRIVATE, fd, 0); | ||
| 47 | if (!ASSERT_OK_PTR(raw_data, "mmap_btf")) | ||
| 48 | goto cleanup; | ||
| 49 | |||
| 50 | if (!ASSERT_EQ(mprotect(raw_data, btf_size, PROT_READ | PROT_WRITE), -1, | ||
| 51 | "mprotect_writable")) | ||
| 52 | goto cleanup; | ||
| 53 | |||
| 54 | if (!ASSERT_EQ(mprotect(raw_data, btf_size, PROT_READ | PROT_EXEC), -1, | ||
| 55 | "mprotect_executable")) | ||
| 56 | goto cleanup; | ||
| 57 | |||
| 58 | /* Check padding is zeroed */ | ||
| 59 | for (int i = btf_size; i < end; i++) { | ||
| 60 | if (((__u8 *)raw_data)[i] != 0) { | ||
| 61 | PRINT_FAIL("tail of BTF is not zero at page offset %d\n", i); | ||
| 62 | goto cleanup; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | btf = btf__new_split(raw_data, btf_size, base); | ||
| 67 | if (!ASSERT_OK_PTR(btf, "parse_btf")) | ||
| 68 | goto cleanup; | ||
| 69 | |||
| 70 | cleanup: | ||
| 71 | btf__free(btf); | ||
| 72 | if (raw_data && raw_data != MAP_FAILED) | ||
| 73 | munmap(raw_data, btf_size); | ||
| 74 | if (fd >= 0) | ||
| 75 | close(fd); | ||
| 76 | } | ||
| 77 | |||
| 78 | void test_btf_sysfs(void) | ||
| 79 | { | ||
| 80 | test_btf_mmap_sysfs("/sys/kernel/btf/vmlinux", NULL); | ||
| 81 | } | ||
| @@ -0,0 +1,285 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright (c) 2025 Google */ | ||
| 3 | |||
| 4 | #include <test_progs.h> | ||
| 5 | #include <bpf/libbpf.h> | ||
| 6 | #include <bpf/btf.h> | ||
| 7 | #include "dmabuf_iter.skel.h" | ||
| 8 | |||
| 9 | #include <fcntl.h> | ||
| 10 | #include <stdbool.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <string.h> | ||
| 14 | #include <sys/ioctl.h> | ||
| 15 | #include <sys/mman.h> | ||
| 16 | #include <unistd.h> | ||
| 17 | |||
| 18 | #include <linux/dma-buf.h> | ||
| 19 | #include <linux/dma-heap.h> | ||
| 20 | #include <linux/udmabuf.h> | ||
| 21 | |||
| 22 | static int udmabuf = -1; | ||
| 23 | static const char udmabuf_test_buffer_name[DMA_BUF_NAME_LEN] = "udmabuf_test_buffer_for_iter"; | ||
| 24 | static size_t udmabuf_test_buffer_size; | ||
| 25 | static int sysheap_dmabuf = -1; | ||
| 26 | static const char sysheap_test_buffer_name[DMA_BUF_NAME_LEN] = "sysheap_test_buffer_for_iter"; | ||
| 27 | static size_t sysheap_test_buffer_size; | ||
| 28 | |||
| 29 | static int create_udmabuf(void) | ||
| 30 | { | ||
| 31 | struct udmabuf_create create; | ||
| 32 | int dev_udmabuf, memfd, local_udmabuf; | ||
| 33 | |||
| 34 | udmabuf_test_buffer_size = 10 * getpagesize(); | ||
| 35 | |||
| 36 | if (!ASSERT_LE(sizeof(udmabuf_test_buffer_name), DMA_BUF_NAME_LEN, "NAMETOOLONG")) | ||
| 37 | return -1; | ||
| 38 | |||
| 39 | memfd = memfd_create("memfd_test", MFD_ALLOW_SEALING); | ||
| 40 | if (!ASSERT_OK_FD(memfd, "memfd_create")) | ||
| 41 | return -1; | ||
| 42 | |||
| 43 | if (!ASSERT_OK(ftruncate(memfd, udmabuf_test_buffer_size), "ftruncate")) | ||
| 44 | goto close_memfd; | ||
| 45 | |||
| 46 | if (!ASSERT_OK(fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK), "seal")) | ||
| 47 | goto close_memfd; | ||
| 48 | |||
| 49 | dev_udmabuf = open("/dev/udmabuf", O_RDONLY); | ||
| 50 | if (!ASSERT_OK_FD(dev_udmabuf, "open udmabuf")) | ||
| 51 | goto close_memfd; | ||
| 52 | |||
| 53 | memset(&create, 0, sizeof(create)); | ||
| 54 | create.memfd = memfd; | ||
| 55 | create.flags = UDMABUF_FLAGS_CLOEXEC; | ||
| 56 | create.offset = 0; | ||
| 57 | create.size = udmabuf_test_buffer_size; | ||
| 58 | |||
| 59 | local_udmabuf = ioctl(dev_udmabuf, UDMABUF_CREATE, &create); | ||
| 60 | close(dev_udmabuf); | ||
| 61 | if (!ASSERT_OK_FD(local_udmabuf, "udmabuf_create")) | ||
| 62 | goto close_memfd; | ||
| 63 | |||
| 64 | if (!ASSERT_OK(ioctl(local_udmabuf, DMA_BUF_SET_NAME_B, udmabuf_test_buffer_name), "name")) | ||
| 65 | goto close_udmabuf; | ||
| 66 | |||
| 67 | return local_udmabuf; | ||
| 68 | |||
| 69 | close_udmabuf: | ||
| 70 | close(local_udmabuf); | ||
| 71 | close_memfd: | ||
| 72 | close(memfd); | ||
| 73 | return -1; | ||
| 74 | } | ||
| 75 | |||
| 76 | static int create_sys_heap_dmabuf(void) | ||
| 77 | { | ||
| 78 | sysheap_test_buffer_size = 20 * getpagesize(); | ||
| 79 | |||
| 80 | struct dma_heap_allocation_data data = { | ||
| 81 | .len = sysheap_test_buffer_size, | ||
| 82 | .fd = 0, | ||
| 83 | .fd_flags = O_RDWR | O_CLOEXEC, | ||
| 84 | .heap_flags = 0, | ||
| 85 | }; | ||
| 86 | int heap_fd, ret; | ||
| 87 | |||
| 88 | if (!ASSERT_LE(sizeof(sysheap_test_buffer_name), DMA_BUF_NAME_LEN, "NAMETOOLONG")) | ||
| 89 | return -1; | ||
| 90 | |||
| 91 | heap_fd = open("/dev/dma_heap/system", O_RDONLY); | ||
| 92 | if (!ASSERT_OK_FD(heap_fd, "open dma heap")) | ||
| 93 | return -1; | ||
| 94 | |||
| 95 | ret = ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data); | ||
| 96 | close(heap_fd); | ||
| 97 | if (!ASSERT_OK(ret, "syheap alloc")) | ||
| 98 | return -1; | ||
| 99 | |||
| 100 | if (!ASSERT_OK(ioctl(data.fd, DMA_BUF_SET_NAME_B, sysheap_test_buffer_name), "name")) | ||
| 101 | goto close_sysheap_dmabuf; | ||
| 102 | |||
| 103 | return data.fd; | ||
| 104 | |||
| 105 | close_sysheap_dmabuf: | ||
| 106 | close(data.fd); | ||
| 107 | return -1; | ||
| 108 | } | ||
| 109 | |||
| 110 | static int create_test_buffers(void) | ||
| 111 | { | ||
| 112 | udmabuf = create_udmabuf(); | ||
| 113 | sysheap_dmabuf = create_sys_heap_dmabuf(); | ||
| 114 | |||
| 115 | if (udmabuf < 0 || sysheap_dmabuf < 0) | ||
| 116 | return -1; | ||
| 117 | |||
| 118 | return 0; | ||
| 119 | } | ||
| 120 | |||
| 121 | static void destroy_test_buffers(void) | ||
| 122 | { | ||
| 123 | close(udmabuf); | ||
| 124 | udmabuf = -1; | ||
| 125 | |||
| 126 | close(sysheap_dmabuf); | ||
| 127 | sysheap_dmabuf = -1; | ||
| 128 | } | ||
| 129 | |||
| 130 | enum Fields { INODE, SIZE, NAME, EXPORTER, FIELD_COUNT }; | ||
| 131 | struct DmabufInfo { | ||
| 132 | unsigned long inode; | ||
| 133 | unsigned long size; | ||
| 134 | char name[DMA_BUF_NAME_LEN]; | ||
| 135 | char exporter[32]; | ||
| 136 | }; | ||
| 137 | |||
| 138 | static bool check_dmabuf_info(const struct DmabufInfo *bufinfo, | ||
| 139 | unsigned long size, | ||
| 140 | const char *name, const char *exporter) | ||
| 141 | { | ||
| 142 | return size == bufinfo->size && | ||
| 143 | !strcmp(name, bufinfo->name) && | ||
| 144 | !strcmp(exporter, bufinfo->exporter); | ||
| 145 | } | ||
| 146 | |||
| 147 | static void subtest_dmabuf_iter_check_no_infinite_reads(struct dmabuf_iter *skel) | ||
| 148 | { | ||
| 149 | int iter_fd; | ||
| 150 | char buf[256]; | ||
| 151 | |||
| 152 | iter_fd = bpf_iter_create(bpf_link__fd(skel->links.dmabuf_collector)); | ||
| 153 | if (!ASSERT_OK_FD(iter_fd, "iter_create")) | ||
| 154 | return; | ||
| 155 | |||
| 156 | while (read(iter_fd, buf, sizeof(buf)) > 0) | ||
| 157 | ; /* Read out all contents */ | ||
| 158 | |||
| 159 | /* Next reads should return 0 */ | ||
| 160 | ASSERT_EQ(read(iter_fd, buf, sizeof(buf)), 0, "read"); | ||
| 161 | |||
| 162 | close(iter_fd); | ||
| 163 | } | ||
| 164 | |||
| 165 | static void subtest_dmabuf_iter_check_default_iter(struct dmabuf_iter *skel) | ||
| 166 | { | ||
| 167 | bool found_test_sysheap_dmabuf = false; | ||
| 168 | bool found_test_udmabuf = false; | ||
| 169 | struct DmabufInfo bufinfo; | ||
| 170 | size_t linesize = 0; | ||
| 171 | char *line = NULL; | ||
| 172 | FILE *iter_file; | ||
| 173 | int iter_fd, f = INODE; | ||
| 174 | |||
| 175 | iter_fd = bpf_iter_create(bpf_link__fd(skel->links.dmabuf_collector)); | ||
| 176 | if (!ASSERT_OK_FD(iter_fd, "iter_create")) | ||
| 177 | return; | ||
| 178 | |||
| 179 | iter_file = fdopen(iter_fd, "r"); | ||
| 180 | if (!ASSERT_OK_PTR(iter_file, "fdopen")) | ||
| 181 | goto close_iter_fd; | ||
| 182 | |||
| 183 | while (getline(&line, &linesize, iter_file) != -1) { | ||
| 184 | if (f % FIELD_COUNT == INODE) { | ||
| 185 | ASSERT_EQ(sscanf(line, "%ld", &bufinfo.inode), 1, | ||
| 186 | "read inode"); | ||
| 187 | } else if (f % FIELD_COUNT == SIZE) { | ||
| 188 | ASSERT_EQ(sscanf(line, "%ld", &bufinfo.size), 1, | ||
| 189 | "read size"); | ||
| 190 | } else if (f % FIELD_COUNT == NAME) { | ||
| 191 | ASSERT_EQ(sscanf(line, "%s", bufinfo.name), 1, | ||
| 192 | "read name"); | ||
| 193 | } else if (f % FIELD_COUNT == EXPORTER) { | ||
| 194 | ASSERT_EQ(sscanf(line, "%31s", bufinfo.exporter), 1, | ||
| 195 | "read exporter"); | ||
| 196 | |||
| 197 | if (check_dmabuf_info(&bufinfo, | ||
| 198 | sysheap_test_buffer_size, | ||
| 199 | sysheap_test_buffer_name, | ||
| 200 | "system")) | ||
| 201 | found_test_sysheap_dmabuf = true; | ||
| 202 | else if (check_dmabuf_info(&bufinfo, | ||
| 203 | udmabuf_test_buffer_size, | ||
| 204 | udmabuf_test_buffer_name, | ||
| 205 | "udmabuf")) | ||
| 206 | found_test_udmabuf = true; | ||
| 207 | } | ||
| 208 | ++f; | ||
| 209 | } | ||
| 210 | |||
| 211 | ASSERT_EQ(f % FIELD_COUNT, INODE, "number of fields"); | ||
| 212 | |||
| 213 | ASSERT_TRUE(found_test_sysheap_dmabuf, "found_test_sysheap_dmabuf"); | ||
| 214 | ASSERT_TRUE(found_test_udmabuf, "found_test_udmabuf"); | ||
| 215 | |||
| 216 | free(line); | ||
| 217 | fclose(iter_file); | ||
| 218 | close_iter_fd: | ||
| 219 | close(iter_fd); | ||
| 220 | } | ||
| 221 | |||
| 222 | static void subtest_dmabuf_iter_check_open_coded(struct dmabuf_iter *skel, int map_fd) | ||
| 223 | { | ||
| 224 | LIBBPF_OPTS(bpf_test_run_opts, topts); | ||
| 225 | char key[DMA_BUF_NAME_LEN]; | ||
| 226 | int err, fd; | ||
| 227 | bool found; | ||
| 228 | |||
| 229 | /* No need to attach it, just run it directly */ | ||
| 230 | fd = bpf_program__fd(skel->progs.iter_dmabuf_for_each); | ||
| 231 | |||
| 232 | err = bpf_prog_test_run_opts(fd, &topts); | ||
| 233 | if (!ASSERT_OK(err, "test_run_opts err")) | ||
| 234 | return; | ||
| 235 | if (!ASSERT_OK(topts.retval, "test_run_opts retval")) | ||
| 236 | return; | ||
| 237 | |||
| 238 | if (!ASSERT_OK(bpf_map_get_next_key(map_fd, NULL, key), "get next key")) | ||
| 239 | return; | ||
| 240 | |||
| 241 | do { | ||
| 242 | ASSERT_OK(bpf_map_lookup_elem(map_fd, key, &found), "lookup"); | ||
| 243 | ASSERT_TRUE(found, "found test buffer"); | ||
| 244 | } while (bpf_map_get_next_key(map_fd, key, key)); | ||
| 245 | } | ||
| 246 | |||
| 247 | void test_dmabuf_iter(void) | ||
| 248 | { | ||
| 249 | struct dmabuf_iter *skel = NULL; | ||
| 250 | int map_fd; | ||
| 251 | const bool f = false; | ||
| 252 | |||
| 253 | skel = dmabuf_iter__open_and_load(); | ||
| 254 | if (!ASSERT_OK_PTR(skel, "dmabuf_iter__open_and_load")) | ||
| 255 | return; | ||
| 256 | |||
| 257 | map_fd = bpf_map__fd(skel->maps.testbuf_hash); | ||
| 258 | if (!ASSERT_OK_FD(map_fd, "map_fd")) | ||
| 259 | goto destroy_skel; | ||
| 260 | |||
| 261 | if (!ASSERT_OK(bpf_map_update_elem(map_fd, udmabuf_test_buffer_name, &f, BPF_ANY), | ||
| 262 | "insert udmabuf")) | ||
| 263 | goto destroy_skel; | ||
| 264 | if (!ASSERT_OK(bpf_map_update_elem(map_fd, sysheap_test_buffer_name, &f, BPF_ANY), | ||
| 265 | "insert sysheap buffer")) | ||
| 266 | goto destroy_skel; | ||
| 267 | |||
| 268 | if (!ASSERT_OK(create_test_buffers(), "create_test_buffers")) | ||
| 269 | goto destroy; | ||
| 270 | |||
| 271 | if (!ASSERT_OK(dmabuf_iter__attach(skel), "skel_attach")) | ||
| 272 | goto destroy; | ||
| 273 | |||
| 274 | if (test__start_subtest("no_infinite_reads")) | ||
| 275 | subtest_dmabuf_iter_check_no_infinite_reads(skel); | ||
| 276 | if (test__start_subtest("default_iter")) | ||
| 277 | subtest_dmabuf_iter_check_default_iter(skel); | ||
| 278 | if (test__start_subtest("open_coded")) | ||
| 279 | subtest_dmabuf_iter_check_open_coded(skel, map_fd); | ||
| 280 | |||
| 281 | destroy: | ||
| 282 | destroy_test_buffers(); | ||
| 283 | destroy_skel: | ||
| 284 | dmabuf_iter__destroy(skel); | ||
| 285 | } | ||
| @@ -33,10 +33,19 @@ static struct { · 32 unmodified lines | |||
| 33 | {"test_dynptr_skb_no_buff", SETUP_SKB_PROG}, | 33 | {"test_dynptr_skb_no_buff", SETUP_SKB_PROG}, |
| 34 | {"test_dynptr_skb_strcmp", SETUP_SKB_PROG}, | 34 | {"test_dynptr_skb_strcmp", SETUP_SKB_PROG}, |
| 35 | {"test_dynptr_skb_tp_btf", SETUP_SKB_PROG_TP}, | 35 | {"test_dynptr_skb_tp_btf", SETUP_SKB_PROG_TP}, |
| 36 | {"test_probe_read_user_dynptr", SETUP_XDP_PROG}, | ||
| 37 | {"test_probe_read_kernel_dynptr", SETUP_XDP_PROG}, | ||
| 38 | {"test_probe_read_user_str_dynptr", SETUP_XDP_PROG}, | ||
| 39 | {"test_probe_read_kernel_str_dynptr", SETUP_XDP_PROG}, | ||
| 40 | {"test_copy_from_user_dynptr", SETUP_SYSCALL_SLEEP}, | ||
| 41 | {"test_copy_from_user_str_dynptr", SETUP_SYSCALL_SLEEP}, | ||
| 42 | {"test_copy_from_user_task_dynptr", SETUP_SYSCALL_SLEEP}, | ||
| 43 | {"test_copy_from_user_task_str_dynptr", SETUP_SYSCALL_SLEEP}, | ||
| 36 | }; | 44 | }; |
| 37 | 45 | ||
| 38 | static void verify_success(const char *prog_name, enum test_setup_type setup_type) | 46 | static void verify_success(const char *prog_name, enum test_setup_type setup_type) |
| 39 | { | 47 | { |
| 48 | char user_data[384] = {[0 ... 382] = 'a', '\0'}; | ||
| 40 | struct dynptr_success *skel; | 49 | struct dynptr_success *skel; |
| 41 | struct bpf_program *prog; | 50 | struct bpf_program *prog; |
| 42 | struct bpf_link *link; | 51 | struct bpf_link *link; |
| @@ -58,6 +67,10 @@ static void verify_success(const char *prog_name, enum test_setup_type setup_typ · 15 unmodified lines | |||
| 58 | if (!ASSERT_OK(err, "dynptr_success__load")) | 67 | if (!ASSERT_OK(err, "dynptr_success__load")) |
| 59 | goto cleanup; | 68 | goto cleanup; |
| 60 | 69 | ||
| 70 | skel->bss->user_ptr = user_data; | ||
| 71 | skel->data->test_len[0] = sizeof(user_data); | ||
| 72 | memcpy(skel->bss->expected_str, user_data, sizeof(user_data)); | ||
| 73 | |||
| 61 | switch (setup_type) { | 74 | switch (setup_type) { |
| 62 | case SETUP_SYSCALL_SLEEP: | 75 | case SETUP_SYSCALL_SLEEP: |
| 63 | link = bpf_program__attach(prog); | 76 | link = bpf_program__attach(prog); |
| @@ -0,0 +1,192 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright (C) 2025. Huawei Technologies Co., Ltd */ | ||
| 3 | #define _GNU_SOURCE | ||
| 4 | #include <stdbool.h> | ||
| 5 | #include <test_progs.h> | ||
| 6 | #include "fd_htab_lookup.skel.h" | ||
| 7 | |||
| 8 | struct htab_op_ctx { | ||
| 9 | int fd; | ||
| 10 | int loop; | ||
| 11 | unsigned int entries; | ||
| 12 | bool stop; | ||
| 13 | }; | ||
| 14 | |||
| 15 | #define ERR_TO_RETVAL(where, err) ((void *)(long)(((where) << 12) | (-err))) | ||
| 16 | |||
| 17 | static void *htab_lookup_fn(void *arg) | ||
| 18 | { | ||
| 19 | struct htab_op_ctx *ctx = arg; | ||
| 20 | int i = 0; | ||
| 21 | |||
| 22 | while (i++ < ctx->loop && !ctx->stop) { | ||
| 23 | unsigned int j; | ||
| 24 | |||
| 25 | for (j = 0; j < ctx->entries; j++) { | ||
| 26 | unsigned int key = j, zero = 0, value; | ||
| 27 | int inner_fd, err; | ||
| 28 | |||
| 29 | err = bpf_map_lookup_elem(ctx->fd, &key, &value); | ||
| 30 | if (err) { | ||
| 31 | ctx->stop = true; | ||
| 32 | return ERR_TO_RETVAL(1, err); | ||
| 33 | } | ||
| 34 | |||
| 35 | inner_fd = bpf_map_get_fd_by_id(value); | ||
| 36 | if (inner_fd < 0) { | ||
| 37 | /* The old map has been freed */ | ||
| 38 | if (inner_fd == -ENOENT) | ||
| 39 | continue; | ||
| 40 | ctx->stop = true; | ||
| 41 | return ERR_TO_RETVAL(2, inner_fd); | ||
| 42 | } | ||
| 43 | |||
| 44 | err = bpf_map_lookup_elem(inner_fd, &zero, &value); | ||
| 45 | if (err) { | ||
| 46 | close(inner_fd); | ||
| 47 | ctx->stop = true; | ||
| 48 | return ERR_TO_RETVAL(3, err); | ||
| 49 | } | ||
| 50 | close(inner_fd); | ||
| 51 | |||
| 52 | if (value != key) { | ||
| 53 | ctx->stop = true; | ||
| 54 | return ERR_TO_RETVAL(4, -EINVAL); | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | return NULL; | ||
| 60 | } | ||
| 61 | |||
| 62 | static void *htab_update_fn(void *arg) | ||
| 63 | { | ||
| 64 | struct htab_op_ctx *ctx = arg; | ||
| 65 | int i = 0; | ||
| 66 | |||
| 67 | while (i++ < ctx->loop && !ctx->stop) { | ||
| 68 | unsigned int j; | ||
| 69 | |||
| 70 | for (j = 0; j < ctx->entries; j++) { | ||
| 71 | unsigned int key = j, zero = 0; | ||
| 72 | int inner_fd, err; | ||
| 73 | |||
| 74 | inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4, 4, 1, NULL); | ||
| 75 | if (inner_fd < 0) { | ||
| 76 | ctx->stop = true; | ||
| 77 | return ERR_TO_RETVAL(1, inner_fd); | ||
| 78 | } | ||
| 79 | |||
| 80 | err = bpf_map_update_elem(inner_fd, &zero, &key, 0); | ||
| 81 | if (err) { | ||
| 82 | close(inner_fd); | ||
| 83 | ctx->stop = true; | ||
| 84 | return ERR_TO_RETVAL(2, err); | ||
| 85 | } | ||
| 86 | |||
| 87 | err = bpf_map_update_elem(ctx->fd, &key, &inner_fd, BPF_EXIST); | ||
| 88 | if (err) { | ||
| 89 | close(inner_fd); | ||
| 90 | ctx->stop = true; | ||
| 91 | return ERR_TO_RETVAL(3, err); | ||
| 92 | } | ||
| 93 | close(inner_fd); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | return NULL; | ||
| 98 | } | ||
| 99 | |||
| 100 | static int setup_htab(int fd, unsigned int entries) | ||
| 101 | { | ||
| 102 | unsigned int i; | ||
| 103 | |||
| 104 | for (i = 0; i < entries; i++) { | ||
| 105 | unsigned int key = i, zero = 0; | ||
| 106 | int inner_fd, err; | ||
| 107 | |||
| 108 | inner_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4, 4, 1, NULL); | ||
| 109 | if (!ASSERT_OK_FD(inner_fd, "new array")) | ||
| 110 | return -1; | ||
| 111 | |||
| 112 | err = bpf_map_update_elem(inner_fd, &zero, &key, 0); | ||
| 113 | if (!ASSERT_OK(err, "init array")) { | ||
| 114 | close(inner_fd); | ||
| 115 | return -1; | ||
| 116 | } | ||
| 117 | |||
| 118 | err = bpf_map_update_elem(fd, &key, &inner_fd, 0); | ||
| 119 | if (!ASSERT_OK(err, "init outer")) { | ||
| 120 | close(inner_fd); | ||
| 121 | return -1; | ||
| 122 | } | ||
| 123 | close(inner_fd); | ||
| 124 | } | ||
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | static int get_int_from_env(const char *name, int dft) | ||
| 130 | { | ||
| 131 | const char *value; | ||
| 132 | |||
| 133 | value = getenv(name); | ||
| 134 | if (!value) | ||
| 135 | return dft; | ||
| 136 | |||
| 137 | return atoi(value); | ||
| 138 | } | ||
| 139 | |||
| 140 | void test_fd_htab_lookup(void) | ||
| 141 | { | ||
| 142 | unsigned int i, wr_nr = 8, rd_nr = 16; | ||
| 143 | pthread_t tids[wr_nr + rd_nr]; | ||
| 144 | struct fd_htab_lookup *skel; | ||
| 145 | struct htab_op_ctx ctx; | ||
| 146 | int err; | ||
| 147 | |||
| 148 | skel = fd_htab_lookup__open_and_load(); | ||
| 149 | if (!ASSERT_OK_PTR(skel, "fd_htab_lookup__open_and_load")) | ||
| 150 | return; | ||
| 151 | |||
| 152 | ctx.fd = bpf_map__fd(skel->maps.outer_map); | ||
| 153 | ctx.loop = get_int_from_env("FD_HTAB_LOOP_NR", 5); | ||
| 154 | ctx.stop = false; | ||
| 155 | ctx.entries = 8; | ||
| 156 | |||
| 157 | err = setup_htab(ctx.fd, ctx.entries); | ||
| 158 | if (err) | ||
| 159 | goto destroy; | ||
| 160 | |||
| 161 | memset(tids, 0, sizeof(tids)); | ||
| 162 | for (i = 0; i < wr_nr; i++) { | ||
| 163 | err = pthread_create(&tids[i], NULL, htab_update_fn, &ctx); | ||
| 164 | if (!ASSERT_OK(err, "pthread_create")) { | ||
| 165 | ctx.stop = true; | ||
| 166 | goto reap; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | for (i = 0; i < rd_nr; i++) { | ||
| 170 | err = pthread_create(&tids[i + wr_nr], NULL, htab_lookup_fn, &ctx); | ||
| 171 | if (!ASSERT_OK(err, "pthread_create")) { | ||
| 172 | ctx.stop = true; | ||
| 173 | goto reap; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | reap: | ||
| 178 | for (i = 0; i < wr_nr + rd_nr; i++) { | ||
| 179 | void *ret = NULL; | ||
| 180 | char desc[32]; | ||
| 181 | |||
| 182 | if (!tids[i]) | ||
| 183 | continue; | ||
| 184 | |||
| 185 | snprintf(desc, sizeof(desc), "thread %u", i + 1); | ||
| 186 | err = pthread_join(tids[i], &ret); | ||
| 187 | ASSERT_OK(err, desc); | ||
| 188 | ASSERT_EQ(ret, NULL, desc); | ||
| 189 | } | ||
| 190 | destroy: | ||
| 191 | fd_htab_lookup__destroy(skel); | ||
| 192 | } | ||
| @@ -37,6 +37,7 @@ static noinline void uprobe_func(void) · 36 unmodified lines | |||
| 37 | static int verify_perf_link_info(int fd, enum bpf_perf_event_type type, long addr, | 37 | static int verify_perf_link_info(int fd, enum bpf_perf_event_type type, long addr, |
| 38 | ssize_t offset, ssize_t entry_offset) | 38 | ssize_t offset, ssize_t entry_offset) |
| 39 | { | 39 | { |
| 40 | ssize_t ref_ctr_offset = entry_offset /* ref_ctr_offset for uprobes */; | ||
| 40 | struct bpf_link_info info; | 41 | struct bpf_link_info info; |
| 41 | __u32 len = sizeof(info); | 42 | __u32 len = sizeof(info); |
| 42 | char buf[PATH_MAX]; | 43 | char buf[PATH_MAX]; |
| @@ -97,6 +98,7 @@ again: · 54 unmodified lines | |||
| 97 | case BPF_PERF_EVENT_UPROBE: | 98 | case BPF_PERF_EVENT_UPROBE: |
| 98 | case BPF_PERF_EVENT_URETPROBE: | 99 | case BPF_PERF_EVENT_URETPROBE: |
| 99 | ASSERT_EQ(info.perf_event.uprobe.offset, offset, "uprobe_offset"); | 100 | ASSERT_EQ(info.perf_event.uprobe.offset, offset, "uprobe_offset"); |
| 101 | ASSERT_EQ(info.perf_event.uprobe.ref_ctr_offset, ref_ctr_offset, "uprobe_ref_ctr_offset"); | ||
| 100 | 102 | ||
| 101 | ASSERT_EQ(info.perf_event.uprobe.name_len, strlen(UPROBE_FILE) + 1, | 103 | ASSERT_EQ(info.perf_event.uprobe.name_len, strlen(UPROBE_FILE) + 1, |
| 102 | "name_len"); | 104 | "name_len"); |
| @@ -241,20 +243,32 @@ static void test_uprobe_fill_link_info(struct test_fill_link_info *skel, · 138 unmodified lines | |||
| 241 | .retprobe = type == BPF_PERF_EVENT_URETPROBE, | 243 | .retprobe = type == BPF_PERF_EVENT_URETPROBE, |
| 242 | .bpf_cookie = PERF_EVENT_COOKIE, | 244 | .bpf_cookie = PERF_EVENT_COOKIE, |
| 243 | ); | 245 | ); |
| 246 | const char *sema[1] = { | ||
| 247 | "uprobe_link_info_sema_1", | ||
| 248 | }; | ||
| 249 | __u64 *ref_ctr_offset; | ||
| 244 | struct bpf_link *link; | 250 | struct bpf_link *link; |
| 245 | int link_fd, err; | 251 | int link_fd, err; |
| 246 | 252 | ||
| 253 | err = elf_resolve_syms_offsets("/proc/self/exe", 1, sema, | ||
| 254 | (unsigned long **) &ref_ctr_offset, STT_OBJECT); | ||
| 255 | if (!ASSERT_OK(err, "elf_resolve_syms_offsets_object")) | ||
| 256 | return; | ||
| 257 | |||
| 258 | opts.ref_ctr_offset = *ref_ctr_offset; | ||
| 247 | link = bpf_program__attach_uprobe_opts(skel->progs.uprobe_run, | 259 | link = bpf_program__attach_uprobe_opts(skel->progs.uprobe_run, |
| 248 | 0, /* self pid */ | 260 | 0, /* self pid */ |
| 249 | UPROBE_FILE, uprobe_offset, | 261 | UPROBE_FILE, uprobe_offset, |
| 250 | &opts); | 262 | &opts); |
| 251 | if (!ASSERT_OK_PTR(link, "attach_uprobe")) | 263 | if (!ASSERT_OK_PTR(link, "attach_uprobe")) |
| 252 | return; | 264 | goto out; |
| 253 | 265 | ||
| 254 | link_fd = bpf_link__fd(link); | 266 | link_fd = bpf_link__fd(link); |
| 255 | err = verify_perf_link_info(link_fd, type, 0, uprobe_offset, 0); | 267 | err = verify_perf_link_info(link_fd, type, 0, uprobe_offset, *ref_ctr_offset); |
| 256 | ASSERT_OK(err, "verify_perf_link_info"); | 268 | ASSERT_OK(err, "verify_perf_link_info"); |
| 257 | bpf_link__destroy(link); | 269 | bpf_link__destroy(link); |
| 270 | out: | ||
| 271 | free(ref_ctr_offset); | ||
| 258 | } | 272 | } |
| 259 | 273 | ||
| 260 | static int verify_kmulti_link_info(int fd, bool retprobe, bool has_cookies) | 274 | static int verify_kmulti_link_info(int fd, bool retprobe, bool has_cookies) |
| @@ -104,7 +104,7 @@ void test_kmem_cache_iter(void) · 103 unmodified lines | |||
| 104 | goto destroy; | 104 | goto destroy; |
| 105 | 105 | ||
| 106 | memset(buf, 0, sizeof(buf)); | 106 | memset(buf, 0, sizeof(buf)); |
| 107 | while (read(iter_fd, buf, sizeof(buf) > 0)) { | 107 | while (read(iter_fd, buf, sizeof(buf)) > 0) { |
| 108 | /* Read out all contents */ | 108 | /* Read out all contents */ |
| 109 | printf("%s", buf); | 109 | printf("%s", buf); |
| 110 | } | 110 | } |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | |||
| 7 | 7 | ||
| 8 | #include "linked_list.skel.h" | 8 | #include "linked_list.skel.h" |
| 9 | #include "linked_list_fail.skel.h" | 9 | #include "linked_list_fail.skel.h" |
| 10 | #include "linked_list_peek.skel.h" | ||
| 10 | 11 | ||
| 11 | static char log_buf[1024 * 1024]; | 12 | static char log_buf[1024 * 1024]; |
| 12 | 13 | ||
| @@ -805,3 +806,8 @@ void test_linked_list(void) · 792 unmodified lines | |||
| 805 | test_linked_list_success(LIST_IN_LIST, true); | 806 | test_linked_list_success(LIST_IN_LIST, true); |
| 806 | test_linked_list_success(TEST_ALL, false); | 807 | test_linked_list_success(TEST_ALL, false); |
| 807 | } | 808 | } |
| 809 | |||
| 810 | void test_linked_list_peek(void) | ||
| 811 | { | ||
| 812 | RUN_TESTS(linked_list_peek); | ||
| 813 | } | ||
| @@ -8,6 +8,7 @@ · 7 unmodified lines | |||
| 8 | #include "rbtree_fail.skel.h" | 8 | #include "rbtree_fail.skel.h" |
| 9 | #include "rbtree_btf_fail__wrong_node_type.skel.h" | 9 | #include "rbtree_btf_fail__wrong_node_type.skel.h" |
| 10 | #include "rbtree_btf_fail__add_wrong_type.skel.h" | 10 | #include "rbtree_btf_fail__add_wrong_type.skel.h" |
| 11 | #include "rbtree_search.skel.h" | ||
| 11 | 12 | ||
| 12 | static void test_rbtree_add_nodes(void) | 13 | static void test_rbtree_add_nodes(void) |
| 13 | { | 14 | { |
| @@ -187,3 +188,8 @@ void test_rbtree_fail(void) · 173 unmodified lines | |||
| 187 | { | 188 | { |
| 188 | RUN_TESTS(rbtree_fail); | 189 | RUN_TESTS(rbtree_fail); |
| 189 | } | 190 | } |
| 191 | |||
| 192 | void test_rbtree_search(void) | ||
| 193 | { | ||
| 194 | RUN_TESTS(rbtree_search); | ||
| 195 | } | ||
| @@ -37,8 +37,10 @@ configure_stack(void) · 36 unmodified lines | |||
| 37 | tc = popen("tc -V", "r"); | 37 | tc = popen("tc -V", "r"); |
| 38 | if (CHECK_FAIL(!tc)) | 38 | if (CHECK_FAIL(!tc)) |
| 39 | return false; | 39 | return false; |
| 40 | if (CHECK_FAIL(!fgets(tc_version, sizeof(tc_version), tc))) | 40 | if (CHECK_FAIL(!fgets(tc_version, sizeof(tc_version), tc))) { |
| 41 | pclose(tc); | ||
| 41 | return false; | 42 | return false; |
| 43 | } | ||
| 42 | if (strstr(tc_version, ", libbpf ")) | 44 | if (strstr(tc_version, ", libbpf ")) |
| 43 | prog = "test_sk_assign_libbpf.bpf.o"; | 45 | prog = "test_sk_assign_libbpf.bpf.o"; |
| 44 | else | 46 | else |
| @@ -3,6 +3,7 @@ · 2 unmodified lines | |||
| 3 | #ifndef __SOCKET_HELPERS__ | 3 | #ifndef __SOCKET_HELPERS__ |
| 4 | #define __SOCKET_HELPERS__ | 4 | #define __SOCKET_HELPERS__ |
| 5 | 5 | ||
| 6 | #include <sys/un.h> | ||
| 6 | #include <linux/vm_sockets.h> | 7 | #include <linux/vm_sockets.h> |
| 7 | 8 | ||
| 8 | /* include/linux/net.h */ | 9 | /* include/linux/net.h */ |
| @@ -169,6 +170,15 @@ static inline void init_addr_loopback6(struct sockaddr_storage *ss, · 160 unmodified lines | |||
| 169 | *len = sizeof(*addr6); | 170 | *len = sizeof(*addr6); |
| 170 | } | 171 | } |
| 171 | 172 | ||
| 173 | static inline void init_addr_loopback_unix(struct sockaddr_storage *ss, | ||
| 174 | socklen_t *len) | ||
| 175 | { | ||
| 176 | struct sockaddr_un *addr = memset(ss, 0, sizeof(*ss)); | ||
| 177 | |||
| 178 | addr->sun_family = AF_UNIX; | ||
| 179 | *len = sizeof(sa_family_t); | ||
| 180 | } | ||
| 181 | |||
| 172 | static inline void init_addr_loopback_vsock(struct sockaddr_storage *ss, | 182 | static inline void init_addr_loopback_vsock(struct sockaddr_storage *ss, |
| 173 | socklen_t *len) | 183 | socklen_t *len) |
| 174 | { | 184 | { |
| @@ -190,6 +200,9 @@ static inline void init_addr_loopback(int family, struct sockaddr_storage *ss, · 15 unmodified lines | |||
| 190 | case AF_INET6: | 200 | case AF_INET6: |
| 191 | init_addr_loopback6(ss, len); | 201 | init_addr_loopback6(ss, len); |
| 192 | return; | 202 | return; |
| 203 | case AF_UNIX: | ||
| 204 | init_addr_loopback_unix(ss, len); | ||
| 205 | return; | ||
| 193 | case AF_VSOCK: | 206 | case AF_VSOCK: |
| 194 | init_addr_loopback_vsock(ss, len); | 207 | init_addr_loopback_vsock(ss, len); |
| 195 | return; | 208 | return; |
| @@ -315,21 +328,27 @@ static inline int create_pair(int family, int sotype, int *p0, int *p1) · 119 unmodified lines | |||
| 315 | { | 328 | { |
| 316 | __close_fd int s, c = -1, p = -1; | 329 | __close_fd int s, c = -1, p = -1; |
| 317 | struct sockaddr_storage addr; | 330 | struct sockaddr_storage addr; |
| 318 | socklen_t len = sizeof(addr); | 331 | socklen_t len; |
| 319 | int err; | 332 | int err; |
| 320 | 333 | ||
| 321 | s = socket_loopback(family, sotype); | 334 | s = socket_loopback(family, sotype); |
| 322 | if (s < 0) | 335 | if (s < 0) |
| 323 | return s; | 336 | return s; |
| 324 | 337 | ||
| 325 | err = xgetsockname(s, sockaddr(&addr), &len); | ||
| 326 | if (err) | ||
| 327 | return err; | ||
| 328 | |||
| 329 | c = xsocket(family, sotype, 0); | 338 | c = xsocket(family, sotype, 0); |
| 330 | if (c < 0) | 339 | if (c < 0) |
| 331 | return c; | 340 | return c; |
| 332 | 341 | ||
| 342 | init_addr_loopback(family, &addr, &len); | ||
| 343 | err = xbind(c, sockaddr(&addr), len); | ||
| 344 | if (err) | ||
| 345 | return err; | ||
| 346 | |||
| 347 | len = sizeof(addr); | ||
| 348 | err = xgetsockname(s, sockaddr(&addr), &len); | ||
| 349 | if (err) | ||
| 350 | return err; | ||
| 351 | |||
| 333 | err = connect(c, sockaddr(&addr), len); | 352 | err = connect(c, sockaddr(&addr), len); |
| 334 | if (err) { | 353 | if (err) { |
| 335 | if (errno != EINPROGRESS) { | 354 | if (errno != EINPROGRESS) { |
| @@ -391,4 +410,59 @@ static inline int create_socket_pairs(int family, int sotype, int *c0, int *c1, · 55 unmodified lines | |||
| 391 | return err; | 410 | return err; |
| 392 | } | 411 | } |
| 393 | 412 | ||
| 413 | static inline const char *socket_kind_to_str(int sock_fd) | ||
| 414 | { | ||
| 415 | socklen_t opt_len; | ||
| 416 | int domain, type; | ||
| 417 | |||
| 418 | opt_len = sizeof(domain); | ||
| 419 | if (getsockopt(sock_fd, SOL_SOCKET, SO_DOMAIN, &domain, &opt_len)) | ||
| 420 | FAIL_ERRNO("getsockopt(SO_DOMAIN)"); | ||
| 421 | |||
| 422 | opt_len = sizeof(type); | ||
| 423 | if (getsockopt(sock_fd, SOL_SOCKET, SO_TYPE, &type, &opt_len)) | ||
| 424 | FAIL_ERRNO("getsockopt(SO_TYPE)"); | ||
| 425 | |||
| 426 | switch (domain) { | ||
| 427 | case AF_INET: | ||
| 428 | switch (type) { | ||
| 429 | case SOCK_STREAM: | ||
| 430 | return "tcp4"; | ||
| 431 | case SOCK_DGRAM: | ||
| 432 | return "udp4"; | ||
| 433 | } | ||
| 434 | break; | ||
| 435 | case AF_INET6: | ||
| 436 | switch (type) { | ||
| 437 | case SOCK_STREAM: | ||
| 438 | return "tcp6"; | ||
| 439 | case SOCK_DGRAM: | ||
| 440 | return "udp6"; | ||
| 441 | } | ||
| 442 | break; | ||
| 443 | case AF_UNIX: | ||
| 444 | switch (type) { | ||
| 445 | case SOCK_STREAM: | ||
| 446 | return "u_str"; | ||
| 447 | case SOCK_DGRAM: | ||
| 448 | return "u_dgr"; | ||
| 449 | case SOCK_SEQPACKET: | ||
| 450 | return "u_seq"; | ||
| 451 | } | ||
| 452 | break; | ||
| 453 | case AF_VSOCK: | ||
| 454 | switch (type) { | ||
| 455 | case SOCK_STREAM: | ||
| 456 | return "v_str"; | ||
| 457 | case SOCK_DGRAM: | ||
| 458 | return "v_dgr"; | ||
| 459 | case SOCK_SEQPACKET: | ||
| 460 | return "v_seq"; | ||
| 461 | } | ||
| 462 | break; | ||
| 463 | } | ||
| 464 | |||
| 465 | return "???"; | ||
| 466 | } | ||
| 467 | |||
| 394 | #endif // __SOCKET_HELPERS__ | 468 | #endif // __SOCKET_HELPERS__ |
| @@ -5,12 +5,15 @@ · 4 unmodified lines | |||
| 5 | 5 | ||
| 6 | #define MAX_TEST_NAME 80 | 6 | #define MAX_TEST_NAME 80 |
| 7 | 7 | ||
| 8 | #define u32(v) ((u32){(v)}) | ||
| 9 | #define u64(v) ((u64){(v)}) | ||
| 10 | |||
| 8 | #define __always_unused __attribute__((__unused__)) | 11 | #define __always_unused __attribute__((__unused__)) |
| 9 | 12 | ||
| 10 | #define xbpf_map_delete_elem(fd, key) \ | 13 | #define xbpf_map_delete_elem(fd, key) \ |
| 11 | ({ \ | 14 | ({ \ |
| 12 | int __ret = bpf_map_delete_elem((fd), (key)); \ | 15 | int __ret = bpf_map_delete_elem((fd), (key)); \ |
| 13 | if (__ret < 0) \ | 16 | if (__ret < 0) \ |
| 14 | FAIL_ERRNO("map_delete"); \ | 17 | FAIL_ERRNO("map_delete"); \ |
| 15 | __ret; \ | 18 | __ret; \ |
| 16 | }) | 19 | }) |
| @@ -18,7 +21,7 @@ · 1 unmodified line | |||
| 18 | #define xbpf_map_lookup_elem(fd, key, val) \ | 21 | #define xbpf_map_lookup_elem(fd, key, val) \ |
| 19 | ({ \ | 22 | ({ \ |
| 20 | int __ret = bpf_map_lookup_elem((fd), (key), (val)); \ | 23 | int __ret = bpf_map_lookup_elem((fd), (key), (val)); \ |
| 21 | if (__ret < 0) \ | 24 | if (__ret < 0) \ |
| 22 | FAIL_ERRNO("map_lookup"); \ | 25 | FAIL_ERRNO("map_lookup"); \ |
| 23 | __ret; \ | 26 | __ret; \ |
| 24 | }) | 27 | }) |
| @@ -26,7 +29,7 @@ · 1 unmodified line | |||
| 26 | #define xbpf_map_update_elem(fd, key, val, flags) \ | 29 | #define xbpf_map_update_elem(fd, key, val, flags) \ |
| 27 | ({ \ | 30 | ({ \ |
| 28 | int __ret = bpf_map_update_elem((fd), (key), (val), (flags)); \ | 31 | int __ret = bpf_map_update_elem((fd), (key), (val), (flags)); \ |
| 29 | if (__ret < 0) \ | 32 | if (__ret < 0) \ |
| 30 | FAIL_ERRNO("map_update"); \ | 33 | FAIL_ERRNO("map_update"); \ |
| 31 | __ret; \ | 34 | __ret; \ |
| 32 | }) | 35 | }) |
| @@ -35,7 +38,7 @@ · 2 unmodified lines | |||
| 35 | ({ \ | 38 | ({ \ |
| 36 | int __ret = \ | 39 | int __ret = \ |
| 37 | bpf_prog_attach((prog), (target), (type), (flags)); \ | 40 | bpf_prog_attach((prog), (target), (type), (flags)); \ |
| 38 | if (__ret < 0) \ | 41 | if (__ret < 0) \ |
| 39 | FAIL_ERRNO("prog_attach(" #type ")"); \ | 42 | FAIL_ERRNO("prog_attach(" #type ")"); \ |
| 40 | __ret; \ | 43 | __ret; \ |
| 41 | }) | 44 | }) |
| @@ -43,7 +46,7 @@ · 1 unmodified line | |||
| 43 | #define xbpf_prog_detach2(prog, target, type) \ | 46 | #define xbpf_prog_detach2(prog, target, type) \ |
| 44 | ({ \ | 47 | ({ \ |
| 45 | int __ret = bpf_prog_detach2((prog), (target), (type)); \ | 48 | int __ret = bpf_prog_detach2((prog), (target), (type)); \ |
| 46 | if (__ret < 0) \ | 49 | if (__ret < 0) \ |
| 47 | FAIL_ERRNO("prog_detach2(" #type ")"); \ | 50 | FAIL_ERRNO("prog_detach2(" #type ")"); \ |
| 48 | __ret; \ | 51 | __ret; \ |
| 49 | }) | 52 | }) |
| @@ -66,21 +69,15 @@ · 16 unmodified lines | |||
| 66 | __ret; \ | 69 | __ret; \ |
| 67 | }) | 70 | }) |
| 68 | 71 | ||
| 69 | static inline int add_to_sockmap(int sock_mapfd, int fd1, int fd2) | 72 | static inline int add_to_sockmap(int mapfd, int fd1, int fd2) |
| 70 | { | 73 | { |
| 71 | u64 value; | ||
| 72 | u32 key; | ||
| 73 | int err; | 74 | int err; |
| 74 | 75 | ||
| 75 | key = 0; | 76 | err = xbpf_map_update_elem(mapfd, &u32(0), &u64(fd1), BPF_NOEXIST); |
| 76 | value = fd1; | ||
| 77 | err = xbpf_map_update_elem(sock_mapfd, &key, &value, BPF_NOEXIST); | ||
| 78 | if (err) | 77 | if (err) |
| 79 | return err; | 78 | return err; |
| 80 | 79 | ||
| 81 | key = 1; | 80 | return xbpf_map_update_elem(mapfd, &u32(1), &u64(fd2), BPF_NOEXIST); |
| 82 | value = fd2; | ||
| 83 | return xbpf_map_update_elem(sock_mapfd, &key, &value, BPF_NOEXIST); | ||
| 84 | } | 81 | } |
| 85 | 82 | ||
| 86 | #endif // __SOCKMAP_HELPERS__ | 83 | #endif // __SOCKMAP_HELPERS__ |
| @@ -3,76 +3,62 @@ · 2 unmodified lines | |||
| 3 | /* | 3 | /* |
| 4 | * Tests for sockmap/sockhash holding kTLS sockets. | 4 | * Tests for sockmap/sockhash holding kTLS sockets. |
| 5 | */ | 5 | */ |
| 6 | 6 | #include <error.h> | |
| 7 | #include <netinet/tcp.h> | 7 | #include <netinet/tcp.h> |
| 8 | #include <linux/tls.h> | ||
| 8 | #include "test_progs.h" | 9 | #include "test_progs.h" |
| 10 | #include "sockmap_helpers.h" | ||
| 11 | #include "test_skmsg_load_helpers.skel.h" | ||
| 12 | #include "test_sockmap_ktls.skel.h" | ||
| 9 | 13 | ||
| 10 | #define MAX_TEST_NAME 80 | 14 | #define MAX_TEST_NAME 80 |
| 11 | #define TCP_ULP 31 | 15 | #define TCP_ULP 31 |
| 12 | 16 | ||
| 13 | static int tcp_server(int family) | 17 | static int init_ktls_pairs(int c, int p) |
| 14 | { | 18 | { |
| 15 | int err, s; | 19 | int err; |
| 16 | 20 | struct tls12_crypto_info_aes_gcm_128 crypto_rx; | |
| 17 | s = socket(family, SOCK_STREAM, 0); | 21 | struct tls12_crypto_info_aes_gcm_128 crypto_tx; |
| 18 | if (!ASSERT_GE(s, 0, "socket")) | ||
| 19 | return -1; | ||
| 20 | |||
| 21 | err = listen(s, SOMAXCONN); | ||
| 22 | if (!ASSERT_OK(err, "listen")) | ||
| 23 | return -1; | ||
| 24 | |||
| 25 | return s; | ||
| 26 | } | ||
| 27 | 22 | ||
| 28 | static int disconnect(int fd) | 23 | err = setsockopt(c, IPPROTO_TCP, TCP_ULP, "tls", strlen("tls")); |
| 29 | { | 24 | if (!ASSERT_OK(err, "setsockopt(TCP_ULP)")) |
| 30 | struct sockaddr unspec = { AF_UNSPEC }; | 25 | goto out; |
| 31 | 26 | ||
| 32 | return connect(fd, &unspec, sizeof(unspec)); | 27 | err = setsockopt(p, IPPROTO_TCP, TCP_ULP, "tls", strlen("tls")); |
| 28 | if (!ASSERT_OK(err, "setsockopt(TCP_ULP)")) | ||
| 29 | goto out; | ||
| 30 | |||
| 31 | memset(&crypto_rx, 0, sizeof(crypto_rx)); | ||
| 32 | memset(&crypto_tx, 0, sizeof(crypto_tx)); | ||
| 33 | crypto_rx.info.version = TLS_1_2_VERSION; | ||
| 34 | crypto_tx.info.version = TLS_1_2_VERSION; | ||
| 35 | crypto_rx.info.cipher_type = TLS_CIPHER_AES_GCM_128; | ||
| 36 | crypto_tx.info.cipher_type = TLS_CIPHER_AES_GCM_128; | ||
| 37 | |||
| 38 | err = setsockopt(c, SOL_TLS, TLS_TX, &crypto_tx, sizeof(crypto_tx)); | ||
| 39 | if (!ASSERT_OK(err, "setsockopt(TLS_TX)")) | ||
| 40 | goto out; | ||
| 41 | |||
| 42 | err = setsockopt(p, SOL_TLS, TLS_RX, &crypto_rx, sizeof(crypto_rx)); | ||
| 43 | if (!ASSERT_OK(err, "setsockopt(TLS_RX)")) | ||
| 44 | goto out; | ||
| 45 | return 0; | ||
| 46 | out: | ||
| 47 | return -1; | ||
| 33 | } | 48 | } |
| 34 | 49 | ||
| 35 | /* Disconnect (unhash) a kTLS socket after removing it from sockmap. */ | 50 | static int create_ktls_pairs(int family, int sotype, int *c, int *p) |
| 36 | static void test_sockmap_ktls_disconnect_after_delete(int family, int map) | ||
| 37 | { | 51 | { |
| 38 | struct sockaddr_storage addr = {0}; | 52 | int err; |
| 39 | socklen_t len = sizeof(addr); | ||
| 40 | int err, cli, srv, zero = 0; | ||
| 41 | |||
| 42 | srv = tcp_server(family); | ||
| 43 | if (srv == -1) | ||
| 44 | return; | ||
| 45 | |||
| 46 | err = getsockname(srv, (struct sockaddr *)&addr, &len); | ||
| 47 | if (!ASSERT_OK(err, "getsockopt")) | ||
| 48 | goto close_srv; | ||
| 49 | 53 | ||
| 50 | cli = socket(family, SOCK_STREAM, 0); | 54 | err = create_pair(family, sotype, c, p); |
| 51 | if (!ASSERT_GE(cli, 0, "socket")) | 55 | if (!ASSERT_OK(err, "create_pair()")) |
| 52 | goto close_srv; | 56 | return -1; |
| 53 | |||
| 54 | err = connect(cli, (struct sockaddr *)&addr, len); | ||
| 55 | if (!ASSERT_OK(err, "connect")) | ||
| 56 | goto close_cli; | ||
| 57 | |||
| 58 | err = bpf_map_update_elem(map, &zero, &cli, 0); | ||
| 59 | if (!ASSERT_OK(err, "bpf_map_update_elem")) | ||
| 60 | goto close_cli; | ||
| 61 | |||
| 62 | err = setsockopt(cli, IPPROTO_TCP, TCP_ULP, "tls", strlen("tls")); | ||
| 63 | if (!ASSERT_OK(err, "setsockopt(TCP_ULP)")) | ||
| 64 | goto close_cli; | ||
| 65 | |||
| 66 | err = bpf_map_delete_elem(map, &zero); | ||
| 67 | if (!ASSERT_OK(err, "bpf_map_delete_elem")) | ||
| 68 | goto close_cli; | ||
| 69 | |||
| 70 | err = disconnect(cli); | ||
| 71 | 57 | ||
| 72 | close_cli: | 58 | err = init_ktls_pairs(*c, *p); |
| 73 | close(cli); | 59 | if (!ASSERT_OK(err, "init_ktls_pairs(c, p)")) |
| 74 | close_srv: | 60 | return -1; |
| 75 | close(srv); | 61 | return 0; |
| 76 | } | 62 | } |
| 77 | 63 | ||
| 78 | static void test_sockmap_ktls_update_fails_when_sock_has_ulp(int family, int map) | 64 | static void test_sockmap_ktls_update_fails_when_sock_has_ulp(int family, int map) |
| @@ -145,6 +131,189 @@ static const char *fmt_test_name(const char *subtest_name, int family, · 66 unmodified lines | |||
| 145 | return test_name; | 131 | return test_name; |
| 146 | } | 132 | } |
| 147 | 133 | ||
| 134 | static void test_sockmap_ktls_offload(int family, int sotype) | ||
| 135 | { | ||
| 136 | int err; | ||
| 137 | int c = 0, p = 0, sent, recvd; | ||
| 138 | char msg[12] = "hello world\0"; | ||
| 139 | char rcv[13]; | ||
| 140 | |||
| 141 | err = create_ktls_pairs(family, sotype, &c, &p); | ||
| 142 | if (!ASSERT_OK(err, "create_ktls_pairs()")) | ||
| 143 | goto out; | ||
| 144 | |||
| 145 | sent = send(c, msg, sizeof(msg), 0); | ||
| 146 | if (!ASSERT_OK(err, "send(msg)")) | ||
| 147 | goto out; | ||
| 148 | |||
| 149 | recvd = recv(p, rcv, sizeof(rcv), 0); | ||
| 150 | if (!ASSERT_OK(err, "recv(msg)") || | ||
| 151 | !ASSERT_EQ(recvd, sent, "length mismatch")) | ||
| 152 | goto out; | ||
| 153 | |||
| 154 | ASSERT_OK(memcmp(msg, rcv, sizeof(msg)), "data mismatch"); | ||
| 155 | |||
| 156 | out: | ||
| 157 | if (c) | ||
| 158 | close(c); | ||
| 159 | if (p) | ||
| 160 | close(p); | ||
| 161 | } | ||
| 162 | |||
| 163 | static void test_sockmap_ktls_tx_cork(int family, int sotype, bool push) | ||
| 164 | { | ||
| 165 | int err, off; | ||
| 166 | int i, j; | ||
| 167 | int start_push = 0, push_len = 0; | ||
| 168 | int c = 0, p = 0, one = 1, sent, recvd; | ||
| 169 | int prog_fd, map_fd; | ||
| 170 | char msg[12] = "hello world\0"; | ||
| 171 | char rcv[20] = {0}; | ||
| 172 | struct test_sockmap_ktls *skel; | ||
| 173 | |||
| 174 | skel = test_sockmap_ktls__open_and_load(); | ||
| 175 | if (!ASSERT_TRUE(skel, "open ktls skel")) | ||
| 176 | return; | ||
| 177 | |||
| 178 | err = create_pair(family, sotype, &c, &p); | ||
| 179 | if (!ASSERT_OK(err, "create_pair()")) | ||
| 180 | goto out; | ||
| 181 | |||
| 182 | prog_fd = bpf_program__fd(skel->progs.prog_sk_policy); | ||
| 183 | map_fd = bpf_map__fd(skel->maps.sock_map); | ||
| 184 | |||
| 185 | err = bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT, 0); | ||
| 186 | if (!ASSERT_OK(err, "bpf_prog_attach sk msg")) | ||
| 187 | goto out; | ||
| 188 | |||
| 189 | err = bpf_map_update_elem(map_fd, &one, &c, BPF_NOEXIST); | ||
| 190 | if (!ASSERT_OK(err, "bpf_map_update_elem(c)")) | ||
| 191 | goto out; | ||
| 192 | |||
| 193 | err = init_ktls_pairs(c, p); | ||
| 194 | if (!ASSERT_OK(err, "init_ktls_pairs(c, p)")) | ||
| 195 | goto out; | ||
| 196 | |||
| 197 | skel->bss->cork_byte = sizeof(msg); | ||
| 198 | if (push) { | ||
| 199 | start_push = 1; | ||
| 200 | push_len = 2; | ||
| 201 | } | ||
| 202 | skel->bss->push_start = start_push; | ||
| 203 | skel->bss->push_end = push_len; | ||
| 204 | |||
| 205 | off = sizeof(msg) / 2; | ||
| 206 | sent = send(c, msg, off, 0); | ||
| 207 | if (!ASSERT_EQ(sent, off, "send(msg)")) | ||
| 208 | goto out; | ||
| 209 | |||
| 210 | recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1); | ||
| 211 | if (!ASSERT_EQ(-1, recvd, "expected no data")) | ||
| 212 | goto out; | ||
| 213 | |||
| 214 | /* send remaining msg */ | ||
| 215 | sent = send(c, msg + off, sizeof(msg) - off, 0); | ||
| 216 | if (!ASSERT_EQ(sent, sizeof(msg) - off, "send remaining data")) | ||
| 217 | goto out; | ||
| 218 | |||
| 219 | recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1); | ||
| 220 | if (!ASSERT_OK(err, "recv(msg)") || | ||
| 221 | !ASSERT_EQ(recvd, sizeof(msg) + push_len, "check length mismatch")) | ||
| 222 | goto out; | ||
| 223 | |||
| 224 | for (i = 0, j = 0; i < recvd;) { | ||
| 225 | /* skip checking the data that has been pushed in */ | ||
| 226 | if (i >= start_push && i <= start_push + push_len - 1) { | ||
| 227 | i++; | ||
| 228 | continue; | ||
| 229 | } | ||
| 230 | if (!ASSERT_EQ(rcv[i], msg[j], "data mismatch")) | ||
| 231 | goto out; | ||
| 232 | i++; | ||
| 233 | j++; | ||
| 234 | } | ||
| 235 | out: | ||
| 236 | if (c) | ||
| 237 | close(c); | ||
| 238 | if (p) | ||
| 239 | close(p); | ||
| 240 | test_sockmap_ktls__destroy(skel); | ||
| 241 | } | ||
| 242 | |||
| 243 | static void test_sockmap_ktls_tx_no_buf(int family, int sotype, bool push) | ||
| 244 | { | ||
| 245 | int c = -1, p = -1, one = 1, two = 2; | ||
| 246 | struct test_sockmap_ktls *skel; | ||
| 247 | unsigned char *data = NULL; | ||
| 248 | struct msghdr msg = {0}; | ||
| 249 | struct iovec iov[2]; | ||
| 250 | int prog_fd, map_fd; | ||
| 251 | int txrx_buf = 1024; | ||
| 252 | int iov_length = 8192; | ||
| 253 | int err; | ||
| 254 | |||
| 255 | skel = test_sockmap_ktls__open_and_load(); | ||
| 256 | if (!ASSERT_TRUE(skel, "open ktls skel")) | ||
| 257 | return; | ||
| 258 | |||
| 259 | err = create_pair(family, sotype, &c, &p); | ||
| 260 | if (!ASSERT_OK(err, "create_pair()")) | ||
| 261 | goto out; | ||
| 262 | |||
| 263 | err = setsockopt(c, SOL_SOCKET, SO_RCVBUFFORCE, &txrx_buf, sizeof(int)); | ||
| 264 | err |= setsockopt(p, SOL_SOCKET, SO_SNDBUFFORCE, &txrx_buf, sizeof(int)); | ||
| 265 | if (!ASSERT_OK(err, "set buf limit")) | ||
| 266 | goto out; | ||
| 267 | |||
| 268 | prog_fd = bpf_program__fd(skel->progs.prog_sk_policy_redir); | ||
| 269 | map_fd = bpf_map__fd(skel->maps.sock_map); | ||
| 270 | |||
| 271 | err = bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT, 0); | ||
| 272 | if (!ASSERT_OK(err, "bpf_prog_attach sk msg")) | ||
| 273 | goto out; | ||
| 274 | |||
| 275 | err = bpf_map_update_elem(map_fd, &one, &c, BPF_NOEXIST); | ||
| 276 | if (!ASSERT_OK(err, "bpf_map_update_elem(c)")) | ||
| 277 | goto out; | ||
| 278 | |||
| 279 | err = bpf_map_update_elem(map_fd, &two, &p, BPF_NOEXIST); | ||
| 280 | if (!ASSERT_OK(err, "bpf_map_update_elem(p)")) | ||
| 281 | goto out; | ||
| 282 | |||
| 283 | skel->bss->apply_bytes = 1024; | ||
| 284 | |||
| 285 | err = init_ktls_pairs(c, p); | ||
| 286 | if (!ASSERT_OK(err, "init_ktls_pairs(c, p)")) | ||
| 287 | goto out; | ||
| 288 | |||
| 289 | data = calloc(iov_length, sizeof(char)); | ||
| 290 | if (!data) | ||
| 291 | goto out; | ||
| 292 | |||
| 293 | iov[0].iov_base = data; | ||
| 294 | iov[0].iov_len = iov_length; | ||
| 295 | iov[1].iov_base = data; | ||
| 296 | iov[1].iov_len = iov_length; | ||
| 297 | msg.msg_iov = iov; | ||
| 298 | msg.msg_iovlen = 2; | ||
| 299 | |||
| 300 | for (;;) { | ||
| 301 | err = sendmsg(c, &msg, MSG_DONTWAIT); | ||
| 302 | if (err <= 0) | ||
| 303 | break; | ||
| 304 | } | ||
| 305 | |||
| 306 | out: | ||
| 307 | if (data) | ||
| 308 | free(data); | ||
| 309 | if (c != -1) | ||
| 310 | close(c); | ||
| 311 | if (p != -1) | ||
| 312 | close(p); | ||
| 313 | |||
| 314 | test_sockmap_ktls__destroy(skel); | ||
| 315 | } | ||
| 316 | |||
| 148 | static void run_tests(int family, enum bpf_map_type map_type) | 317 | static void run_tests(int family, enum bpf_map_type map_type) |
| 149 | { | 318 | { |
| 150 | int map; | 319 | int map; |
| @@ -153,18 +322,30 @@ static void run_tests(int family, enum bpf_map_type map_type) · 2 unmodified lines | |||
| 153 | if (!ASSERT_GE(map, 0, "bpf_map_create")) | 322 | if (!ASSERT_GE(map, 0, "bpf_map_create")) |
| 154 | return; | 323 | return; |
| 155 | 324 | ||
| 156 | if (test__start_subtest(fmt_test_name("disconnect_after_delete", family, map_type))) | ||
| 157 | test_sockmap_ktls_disconnect_after_delete(family, map); | ||
| 158 | if (test__start_subtest(fmt_test_name("update_fails_when_sock_has_ulp", family, map_type))) | 325 | if (test__start_subtest(fmt_test_name("update_fails_when_sock_has_ulp", family, map_type))) |
| 159 | test_sockmap_ktls_update_fails_when_sock_has_ulp(family, map); | 326 | test_sockmap_ktls_update_fails_when_sock_has_ulp(family, map); |
| 160 | 327 | ||
| 161 | close(map); | 328 | close(map); |
| 162 | } | 329 | } |
| 163 | 330 | ||
| 331 | static void run_ktls_test(int family, int sotype) | ||
| 332 | { | ||
| 333 | if (test__start_subtest("tls simple offload")) | ||
| 334 | test_sockmap_ktls_offload(family, sotype); | ||
| 335 | if (test__start_subtest("tls tx cork")) | ||
| 336 | test_sockmap_ktls_tx_cork(family, sotype, false); | ||
| 337 | if (test__start_subtest("tls tx cork with push")) | ||
| 338 | test_sockmap_ktls_tx_cork(family, sotype, true); | ||
| 339 | if (test__start_subtest("tls tx egress with no buf")) | ||
| 340 | test_sockmap_ktls_tx_no_buf(family, sotype, true); | ||
| 341 | } | ||
| 342 | |||
| 164 | void test_sockmap_ktls(void) | 343 | void test_sockmap_ktls(void) |
| 165 | { | 344 | { |
| 166 | run_tests(AF_INET, BPF_MAP_TYPE_SOCKMAP); | 345 | run_tests(AF_INET, BPF_MAP_TYPE_SOCKMAP); |
| 167 | run_tests(AF_INET, BPF_MAP_TYPE_SOCKHASH); | 346 | run_tests(AF_INET, BPF_MAP_TYPE_SOCKHASH); |
| 168 | run_tests(AF_INET6, BPF_MAP_TYPE_SOCKMAP); | 347 | run_tests(AF_INET6, BPF_MAP_TYPE_SOCKMAP); |
| 169 | run_tests(AF_INET6, BPF_MAP_TYPE_SOCKHASH); | 348 | run_tests(AF_INET6, BPF_MAP_TYPE_SOCKHASH); |
| 349 | run_ktls_test(AF_INET, SOCK_STREAM); | ||
| 350 | run_ktls_test(AF_INET6, SOCK_STREAM); | ||
| 170 | } | 351 | } |
| @@ -1366,237 +1366,6 @@ static void test_redir(struct test_sockmap_listen *skel, struct bpf_map *map, · 1365 unmodified lines | |||
| 1366 | } | 1366 | } |
| 1367 | } | 1367 | } |
| 1368 | 1368 | ||
| 1369 | static void pairs_redir_to_connected(int cli0, int peer0, int cli1, int peer1, | ||
| 1370 | int sock_mapfd, int nop_mapfd, | ||
| 1371 | int verd_mapfd, enum redir_mode mode, | ||
| 1372 | int send_flags) | ||
| 1373 | { | ||
| 1374 | const char *log_prefix = redir_mode_str(mode); | ||
| 1375 | unsigned int pass; | ||
| 1376 | int err, n; | ||
| 1377 | u32 key; | ||
| 1378 | char b; | ||
| 1379 | |||
| 1380 | zero_verdict_count(verd_mapfd); | ||
| 1381 | |||
| 1382 | err = add_to_sockmap(sock_mapfd, peer0, peer1); | ||
| 1383 | if (err) | ||
| 1384 | return; | ||
| 1385 | |||
| 1386 | if (nop_mapfd >= 0) { | ||
| 1387 | err = add_to_sockmap(nop_mapfd, cli0, cli1); | ||
| 1388 | if (err) | ||
| 1389 | return; | ||
| 1390 | } | ||
| 1391 | |||
| 1392 | /* Last byte is OOB data when send_flags has MSG_OOB bit set */ | ||
| 1393 | n = xsend(cli1, "ab", 2, send_flags); | ||
| 1394 | if (n >= 0 && n < 2) | ||
| 1395 | FAIL("%s: incomplete send", log_prefix); | ||
| 1396 | if (n < 2) | ||
| 1397 | return; | ||
| 1398 | |||
| 1399 | key = SK_PASS; | ||
| 1400 | err = xbpf_map_lookup_elem(verd_mapfd, &key, &pass); | ||
| 1401 | if (err) | ||
| 1402 | return; | ||
| 1403 | if (pass != 1) | ||
| 1404 | FAIL("%s: want pass count 1, have %d", log_prefix, pass); | ||
| 1405 | |||
| 1406 | n = recv_timeout(mode == REDIR_INGRESS ? peer0 : cli0, &b, 1, 0, IO_TIMEOUT_SEC); | ||
| 1407 | if (n < 0) | ||
| 1408 | FAIL_ERRNO("%s: recv_timeout", log_prefix); | ||
| 1409 | if (n == 0) | ||
| 1410 | FAIL("%s: incomplete recv", log_prefix); | ||
| 1411 | |||
| 1412 | if (send_flags & MSG_OOB) { | ||
| 1413 | /* Check that we can't read OOB while in sockmap */ | ||
| 1414 | errno = 0; | ||
| 1415 | n = recv(peer1, &b, 1, MSG_OOB | MSG_DONTWAIT); | ||
| 1416 | if (n != -1 || errno != EOPNOTSUPP) | ||
| 1417 | FAIL("%s: recv(MSG_OOB): expected EOPNOTSUPP: retval=%d errno=%d", | ||
| 1418 | log_prefix, n, errno); | ||
| 1419 | |||
| 1420 | /* Remove peer1 from sockmap */ | ||
| 1421 | xbpf_map_delete_elem(sock_mapfd, &(int){ 1 }); | ||
| 1422 | |||
| 1423 | /* Check that OOB was dropped on redirect */ | ||
| 1424 | errno = 0; | ||
| 1425 | n = recv(peer1, &b, 1, MSG_OOB | MSG_DONTWAIT); | ||
| 1426 | if (n != -1 || errno != EINVAL) | ||
| 1427 | FAIL("%s: recv(MSG_OOB): expected EINVAL: retval=%d errno=%d", | ||
| 1428 | log_prefix, n, errno); | ||
| 1429 | } | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | static void unix_redir_to_connected(int sotype, int sock_mapfd, | ||
| 1433 | int verd_mapfd, enum redir_mode mode) | ||
| 1434 | { | ||
| 1435 | int c0, c1, p0, p1; | ||
| 1436 | int sfd[2]; | ||
| 1437 | |||
| 1438 | if (socketpair(AF_UNIX, sotype | SOCK_NONBLOCK, 0, sfd)) | ||
| 1439 | return; | ||
| 1440 | c0 = sfd[0], p0 = sfd[1]; | ||
| 1441 | |||
| 1442 | if (socketpair(AF_UNIX, sotype | SOCK_NONBLOCK, 0, sfd)) | ||
| 1443 | goto close0; | ||
| 1444 | c1 = sfd[0], p1 = sfd[1]; | ||
| 1445 | |||
| 1446 | pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, -1, verd_mapfd, | ||
| 1447 | mode, NO_FLAGS); | ||
| 1448 | |||
| 1449 | xclose(c1); | ||
| 1450 | xclose(p1); | ||
| 1451 | close0: | ||
| 1452 | xclose(c0); | ||
| 1453 | xclose(p0); | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | static void unix_skb_redir_to_connected(struct test_sockmap_listen *skel, | ||
| 1457 | struct bpf_map *inner_map, int sotype) | ||
| 1458 | { | ||
| 1459 | int verdict = bpf_program__fd(skel->progs.prog_skb_verdict); | ||
| 1460 | int verdict_map = bpf_map__fd(skel->maps.verdict_map); | ||
| 1461 | int sock_map = bpf_map__fd(inner_map); | ||
| 1462 | int err; | ||
| 1463 | |||
| 1464 | err = xbpf_prog_attach(verdict, sock_map, BPF_SK_SKB_VERDICT, 0); | ||
| 1465 | if (err) | ||
| 1466 | return; | ||
| 1467 | |||
| 1468 | skel->bss->test_ingress = false; | ||
| 1469 | unix_redir_to_connected(sotype, sock_map, verdict_map, REDIR_EGRESS); | ||
| 1470 | skel->bss->test_ingress = true; | ||
| 1471 | unix_redir_to_connected(sotype, sock_map, verdict_map, REDIR_INGRESS); | ||
| 1472 | |||
| 1473 | xbpf_prog_detach2(verdict, sock_map, BPF_SK_SKB_VERDICT); | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | static void test_unix_redir(struct test_sockmap_listen *skel, struct bpf_map *map, | ||
| 1477 | int sotype) | ||
| 1478 | { | ||
| 1479 | const char *family_name, *map_name; | ||
| 1480 | char s[MAX_TEST_NAME]; | ||
| 1481 | |||
| 1482 | family_name = family_str(AF_UNIX); | ||
| 1483 | map_name = map_type_str(map); | ||
| 1484 | snprintf(s, sizeof(s), "%s %s %s", map_name, family_name, __func__); | ||
| 1485 | if (!test__start_subtest(s)) | ||
| 1486 | return; | ||
| 1487 | unix_skb_redir_to_connected(skel, map, sotype); | ||
| 1488 | } | ||
| 1489 | |||
| 1490 | /* Returns two connected loopback vsock sockets */ | ||
| 1491 | static int vsock_socketpair_connectible(int sotype, int *v0, int *v1) | ||
| 1492 | { | ||
| 1493 | return create_pair(AF_VSOCK, sotype | SOCK_NONBLOCK, v0, v1); | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | static void vsock_unix_redir_connectible(int sock_mapfd, int verd_mapfd, | ||
| 1497 | enum redir_mode mode, int sotype) | ||
| 1498 | { | ||
| 1499 | const char *log_prefix = redir_mode_str(mode); | ||
| 1500 | char a = 'a', b = 'b'; | ||
| 1501 | int u0, u1, v0, v1; | ||
| 1502 | int sfd[2]; | ||
| 1503 | unsigned int pass; | ||
| 1504 | int err, n; | ||
| 1505 | u32 key; | ||
| 1506 | |||
| 1507 | zero_verdict_count(verd_mapfd); | ||
| 1508 | |||
| 1509 | if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sfd)) | ||
| 1510 | return; | ||
| 1511 | |||
| 1512 | u0 = sfd[0]; | ||
| 1513 | u1 = sfd[1]; | ||
| 1514 | |||
| 1515 | err = vsock_socketpair_connectible(sotype, &v0, &v1); | ||
| 1516 | if (err) { | ||
| 1517 | FAIL("vsock_socketpair_connectible() failed"); | ||
| 1518 | goto close_uds; | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | err = add_to_sockmap(sock_mapfd, u0, v0); | ||
| 1522 | if (err) { | ||
| 1523 | FAIL("add_to_sockmap failed"); | ||
| 1524 | goto close_vsock; | ||
| 1525 | } | ||
| 1526 | |||
| 1527 | n = write(v1, &a, sizeof(a)); | ||
| 1528 | if (n < 0) | ||
| 1529 | FAIL_ERRNO("%s: write", log_prefix); | ||
| 1530 | if (n == 0) | ||
| 1531 | FAIL("%s: incomplete write", log_prefix); | ||
| 1532 | if (n < 1) | ||
| 1533 | goto out; | ||
| 1534 | |||
| 1535 | n = xrecv_nonblock(mode == REDIR_INGRESS ? u0 : u1, &b, sizeof(b), 0); | ||
| 1536 | if (n < 0) | ||
| 1537 | FAIL("%s: recv() err, errno=%d", log_prefix, errno); | ||
| 1538 | if (n == 0) | ||
| 1539 | FAIL("%s: incomplete recv", log_prefix); | ||
| 1540 | if (b != a) | ||
| 1541 | FAIL("%s: vsock socket map failed, %c != %c", log_prefix, a, b); | ||
| 1542 | |||
| 1543 | key = SK_PASS; | ||
| 1544 | err = xbpf_map_lookup_elem(verd_mapfd, &key, &pass); | ||
| 1545 | if (err) | ||
| 1546 | goto out; | ||
| 1547 | if (pass != 1) | ||
| 1548 | FAIL("%s: want pass count 1, have %d", log_prefix, pass); | ||
| 1549 | out: | ||
| 1550 | key = 0; | ||
| 1551 | bpf_map_delete_elem(sock_mapfd, &key); | ||
| 1552 | key = 1; | ||
| 1553 | bpf_map_delete_elem(sock_mapfd, &key); | ||
| 1554 | |||
| 1555 | close_vsock: | ||
| 1556 | close(v0); | ||
| 1557 | close(v1); | ||
| 1558 | |||
| 1559 | close_uds: | ||
| 1560 | close(u0); | ||
| 1561 | close(u1); | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | static void vsock_unix_skb_redir_connectible(struct test_sockmap_listen *skel, | ||
| 1565 | struct bpf_map *inner_map, | ||
| 1566 | int sotype) | ||
| 1567 | { | ||
| 1568 | int verdict = bpf_program__fd(skel->progs.prog_skb_verdict); | ||
| 1569 | int verdict_map = bpf_map__fd(skel->maps.verdict_map); | ||
| 1570 | int sock_map = bpf_map__fd(inner_map); | ||
| 1571 | int err; | ||
| 1572 | |||
| 1573 | err = xbpf_prog_attach(verdict, sock_map, BPF_SK_SKB_VERDICT, 0); | ||
| 1574 | if (err) | ||
| 1575 | return; | ||
| 1576 | |||
| 1577 | skel->bss->test_ingress = false; | ||
| 1578 | vsock_unix_redir_connectible(sock_map, verdict_map, REDIR_EGRESS, sotype); | ||
| 1579 | skel->bss->test_ingress = true; | ||
| 1580 | vsock_unix_redir_connectible(sock_map, verdict_map, REDIR_INGRESS, sotype); | ||
| 1581 | |||
| 1582 | xbpf_prog_detach2(verdict, sock_map, BPF_SK_SKB_VERDICT); | ||
| 1583 | } | ||
| 1584 | |||
| 1585 | static void test_vsock_redir(struct test_sockmap_listen *skel, struct bpf_map *map) | ||
| 1586 | { | ||
| 1587 | const char *family_name, *map_name; | ||
| 1588 | char s[MAX_TEST_NAME]; | ||
| 1589 | |||
| 1590 | family_name = family_str(AF_VSOCK); | ||
| 1591 | map_name = map_type_str(map); | ||
| 1592 | snprintf(s, sizeof(s), "%s %s %s", map_name, family_name, __func__); | ||
| 1593 | if (!test__start_subtest(s)) | ||
| 1594 | return; | ||
| 1595 | |||
| 1596 | vsock_unix_skb_redir_connectible(skel, map, SOCK_STREAM); | ||
| 1597 | vsock_unix_skb_redir_connectible(skel, map, SOCK_SEQPACKET); | ||
| 1598 | } | ||
| 1599 | |||
| 1600 | static void test_reuseport(struct test_sockmap_listen *skel, | 1369 | static void test_reuseport(struct test_sockmap_listen *skel, |
| 1601 | struct bpf_map *map, int family, int sotype) | 1370 | struct bpf_map *map, int family, int sotype) |
| 1602 | { | 1371 | { |
| @@ -1637,224 +1406,6 @@ static void test_reuseport(struct test_sockmap_listen *skel, · 34 unmodified lines | |||
| 1637 | } | 1406 | } |
| 1638 | } | 1407 | } |
| 1639 | 1408 | ||
| 1640 | static int inet_socketpair(int family, int type, int *s, int *c) | ||
| 1641 | { | ||
| 1642 | return create_pair(family, type | SOCK_NONBLOCK, s, c); | ||
| 1643 | } | ||
| 1644 | |||
| 1645 | static void udp_redir_to_connected(int family, int sock_mapfd, int verd_mapfd, | ||
| 1646 | enum redir_mode mode) | ||
| 1647 | { | ||
| 1648 | int c0, c1, p0, p1; | ||
| 1649 | int err; | ||
| 1650 | |||
| 1651 | err = inet_socketpair(family, SOCK_DGRAM, &p0, &c0); | ||
| 1652 | if (err) | ||
| 1653 | return; | ||
| 1654 | err = inet_socketpair(family, SOCK_DGRAM, &p1, &c1); | ||
| 1655 | if (err) | ||
| 1656 | goto close_cli0; | ||
| 1657 | |||
| 1658 | pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, -1, verd_mapfd, | ||
| 1659 | mode, NO_FLAGS); | ||
| 1660 | |||
| 1661 | xclose(c1); | ||
| 1662 | xclose(p1); | ||
| 1663 | close_cli0: | ||
| 1664 | xclose(c0); | ||
| 1665 | xclose(p0); | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | static void udp_skb_redir_to_connected(struct test_sockmap_listen *skel, | ||
| 1669 | struct bpf_map *inner_map, int family) | ||
| 1670 | { | ||
| 1671 | int verdict = bpf_program__fd(skel->progs.prog_skb_verdict); | ||
| 1672 | int verdict_map = bpf_map__fd(skel->maps.verdict_map); | ||
| 1673 | int sock_map = bpf_map__fd(inner_map); | ||
| 1674 | int err; | ||
| 1675 | |||
| 1676 | err = xbpf_prog_attach(verdict, sock_map, BPF_SK_SKB_VERDICT, 0); | ||
| 1677 | if (err) | ||
| 1678 | return; | ||
| 1679 | |||
| 1680 | skel->bss->test_ingress = false; | ||
| 1681 | udp_redir_to_connected(family, sock_map, verdict_map, REDIR_EGRESS); | ||
| 1682 | skel->bss->test_ingress = true; | ||
| 1683 | udp_redir_to_connected(family, sock_map, verdict_map, REDIR_INGRESS); | ||
| 1684 | |||
| 1685 | xbpf_prog_detach2(verdict, sock_map, BPF_SK_SKB_VERDICT); | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | static void test_udp_redir(struct test_sockmap_listen *skel, struct bpf_map *map, | ||
| 1689 | int family) | ||
| 1690 | { | ||
| 1691 | const char *family_name, *map_name; | ||
| 1692 | char s[MAX_TEST_NAME]; | ||
| 1693 | |||
| 1694 | family_name = family_str(family); | ||
| 1695 | map_name = map_type_str(map); | ||
| 1696 | snprintf(s, sizeof(s), "%s %s %s", map_name, family_name, __func__); | ||
| 1697 | if (!test__start_subtest(s)) | ||
| 1698 | return; | ||
| 1699 | udp_skb_redir_to_connected(skel, map, family); | ||
| 1700 | } | ||
| 1701 | |||
| 1702 | static void inet_unix_redir_to_connected(int family, int type, int sock_mapfd, | ||
| 1703 | int verd_mapfd, enum redir_mode mode) | ||
| 1704 | { | ||
| 1705 | int c0, c1, p0, p1; | ||
| 1706 | int sfd[2]; | ||
| 1707 | int err; | ||
| 1708 | |||
| 1709 | if (socketpair(AF_UNIX, type | SOCK_NONBLOCK, 0, sfd)) | ||
| 1710 | return; | ||
| 1711 | c0 = sfd[0], p0 = sfd[1]; | ||
| 1712 | |||
| 1713 | err = inet_socketpair(family, type, &p1, &c1); | ||
| 1714 | if (err) | ||
| 1715 | goto close; | ||
| 1716 | |||
| 1717 | pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, -1, verd_mapfd, | ||
| 1718 | mode, NO_FLAGS); | ||
| 1719 | |||
| 1720 | xclose(c1); | ||
| 1721 | xclose(p1); | ||
| 1722 | close: | ||
| 1723 | xclose(c0); | ||
| 1724 | xclose(p0); | ||
| 1725 | } | ||
| 1726 | |||
| 1727 | static void inet_unix_skb_redir_to_connected(struct test_sockmap_listen *skel, | ||
| 1728 | struct bpf_map *inner_map, int family) | ||
| 1729 | { | ||
| 1730 | int verdict = bpf_program__fd(skel->progs.prog_skb_verdict); | ||
| 1731 | int verdict_map = bpf_map__fd(skel->maps.verdict_map); | ||
| 1732 | int sock_map = bpf_map__fd(inner_map); | ||
| 1733 | int err; | ||
| 1734 | |||
| 1735 | err = xbpf_prog_attach(verdict, sock_map, BPF_SK_SKB_VERDICT, 0); | ||
| 1736 | if (err) | ||
| 1737 | return; | ||
| 1738 | |||
| 1739 | skel->bss->test_ingress = false; | ||
| 1740 | inet_unix_redir_to_connected(family, SOCK_DGRAM, sock_map, verdict_map, | ||
| 1741 | REDIR_EGRESS); | ||
| 1742 | inet_unix_redir_to_connected(family, SOCK_STREAM, sock_map, verdict_map, | ||
| 1743 | REDIR_EGRESS); | ||
| 1744 | skel->bss->test_ingress = true; | ||
| 1745 | inet_unix_redir_to_connected(family, SOCK_DGRAM, sock_map, verdict_map, | ||
| 1746 | REDIR_INGRESS); | ||
| 1747 | inet_unix_redir_to_connected(family, SOCK_STREAM, sock_map, verdict_map, | ||
| 1748 | REDIR_INGRESS); | ||
| 1749 | |||
| 1750 | xbpf_prog_detach2(verdict, sock_map, BPF_SK_SKB_VERDICT); | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | static void unix_inet_redir_to_connected(int family, int type, int sock_mapfd, | ||
| 1754 | int nop_mapfd, int verd_mapfd, | ||
| 1755 | enum redir_mode mode, int send_flags) | ||
| 1756 | { | ||
| 1757 | int c0, c1, p0, p1; | ||
| 1758 | int sfd[2]; | ||
| 1759 | int err; | ||
| 1760 | |||
| 1761 | err = inet_socketpair(family, type, &p0, &c0); | ||
| 1762 | if (err) | ||
| 1763 | return; | ||
| 1764 | |||
| 1765 | if (socketpair(AF_UNIX, type | SOCK_NONBLOCK, 0, sfd)) | ||
| 1766 | goto close_cli0; | ||
| 1767 | c1 = sfd[0], p1 = sfd[1]; | ||
| 1768 | |||
| 1769 | pairs_redir_to_connected(c0, p0, c1, p1, sock_mapfd, nop_mapfd, | ||
| 1770 | verd_mapfd, mode, send_flags); | ||
| 1771 | |||
| 1772 | xclose(c1); | ||
| 1773 | xclose(p1); | ||
| 1774 | close_cli0: | ||
| 1775 | xclose(c0); | ||
| 1776 | xclose(p0); | ||
| 1777 | } | ||
| 1778 | |||
| 1779 | static void unix_inet_skb_redir_to_connected(struct test_sockmap_listen *skel, | ||
| 1780 | struct bpf_map *inner_map, int family) | ||
| 1781 | { | ||
| 1782 | int verdict = bpf_program__fd(skel->progs.prog_skb_verdict); | ||
| 1783 | int nop_map = bpf_map__fd(skel->maps.nop_map); | ||
| 1784 | int verdict_map = bpf_map__fd(skel->maps.verdict_map); | ||
| 1785 | int sock_map = bpf_map__fd(inner_map); | ||
| 1786 | int err; | ||
| 1787 | |||
| 1788 | err = xbpf_prog_attach(verdict, sock_map, BPF_SK_SKB_VERDICT, 0); | ||
| 1789 | if (err) | ||
| 1790 | return; | ||
| 1791 | |||
| 1792 | skel->bss->test_ingress = false; | ||
| 1793 | unix_inet_redir_to_connected(family, SOCK_DGRAM, | ||
| 1794 | sock_map, -1, verdict_map, | ||
| 1795 | REDIR_EGRESS, NO_FLAGS); | ||
| 1796 | unix_inet_redir_to_connected(family, SOCK_STREAM, | ||
| 1797 | sock_map, -1, verdict_map, | ||
| 1798 | REDIR_EGRESS, NO_FLAGS); | ||
| 1799 | |||
| 1800 | unix_inet_redir_to_connected(family, SOCK_DGRAM, | ||
| 1801 | sock_map, nop_map, verdict_map, | ||
| 1802 | REDIR_EGRESS, NO_FLAGS); | ||
| 1803 | unix_inet_redir_to_connected(family, SOCK_STREAM, | ||
| 1804 | sock_map, nop_map, verdict_map, | ||
| 1805 | REDIR_EGRESS, NO_FLAGS); | ||
| 1806 | |||
| 1807 | /* MSG_OOB not supported by AF_UNIX SOCK_DGRAM */ | ||
| 1808 | unix_inet_redir_to_connected(family, SOCK_STREAM, | ||
| 1809 | sock_map, nop_map, verdict_map, | ||
| 1810 | REDIR_EGRESS, MSG_OOB); | ||
| 1811 | |||
| 1812 | skel->bss->test_ingress = true; | ||
| 1813 | unix_inet_redir_to_connected(family, SOCK_DGRAM, | ||
| 1814 | sock_map, -1, verdict_map, | ||
| 1815 | REDIR_INGRESS, NO_FLAGS); | ||
| 1816 | unix_inet_redir_to_connected(family, SOCK_STREAM, | ||
| 1817 | sock_map, -1, verdict_map, | ||
| 1818 | REDIR_INGRESS, NO_FLAGS); | ||
| 1819 | |||
| 1820 | unix_inet_redir_to_connected(family, SOCK_DGRAM, | ||
| 1821 | sock_map, nop_map, verdict_map, | ||
| 1822 | REDIR_INGRESS, NO_FLAGS); | ||
| 1823 | unix_inet_redir_to_connected(family, SOCK_STREAM, | ||
| 1824 | sock_map, nop_map, verdict_map, | ||
| 1825 | REDIR_INGRESS, NO_FLAGS); | ||
| 1826 | |||
| 1827 | /* MSG_OOB not supported by AF_UNIX SOCK_DGRAM */ | ||
| 1828 | unix_inet_redir_to_connected(family, SOCK_STREAM, | ||
| 1829 | sock_map, nop_map, verdict_map, | ||
| 1830 | REDIR_INGRESS, MSG_OOB); | ||
| 1831 | |||
| 1832 | xbpf_prog_detach2(verdict, sock_map, BPF_SK_SKB_VERDICT); | ||
| 1833 | } | ||
| 1834 | |||
| 1835 | static void test_udp_unix_redir(struct test_sockmap_listen *skel, struct bpf_map *map, | ||
| 1836 | int family) | ||
| 1837 | { | ||
| 1838 | const char *family_name, *map_name; | ||
| 1839 | struct netns_obj *netns; | ||
| 1840 | char s[MAX_TEST_NAME]; | ||
| 1841 | |||
| 1842 | family_name = family_str(family); | ||
| 1843 | map_name = map_type_str(map); | ||
| 1844 | snprintf(s, sizeof(s), "%s %s %s", map_name, family_name, __func__); | ||
| 1845 | if (!test__start_subtest(s)) | ||
| 1846 | return; | ||
| 1847 | |||
| 1848 | netns = netns_new("sockmap_listen", true); | ||
| 1849 | if (!ASSERT_OK_PTR(netns, "netns_new")) | ||
| 1850 | return; | ||
| 1851 | |||
| 1852 | inet_unix_skb_redir_to_connected(skel, map, family); | ||
| 1853 | unix_inet_skb_redir_to_connected(skel, map, family); | ||
| 1854 | |||
| 1855 | netns_free(netns); | ||
| 1856 | } | ||
| 1857 | |||
| 1858 | static void run_tests(struct test_sockmap_listen *skel, struct bpf_map *map, | 1409 | static void run_tests(struct test_sockmap_listen *skel, struct bpf_map *map, |
| 1859 | int family) | 1410 | int family) |
| 1860 | { | 1411 | { |
| @@ -1863,8 +1414,6 @@ static void run_tests(struct test_sockmap_listen *skel, struct bpf_map *map, · 2 unmodified lines | |||
| 1863 | test_redir(skel, map, family, SOCK_STREAM); | 1414 | test_redir(skel, map, family, SOCK_STREAM); |
| 1864 | test_reuseport(skel, map, family, SOCK_STREAM); | 1415 | test_reuseport(skel, map, family, SOCK_STREAM); |
| 1865 | test_reuseport(skel, map, family, SOCK_DGRAM); | 1416 | test_reuseport(skel, map, family, SOCK_DGRAM); |
| 1866 | test_udp_redir(skel, map, family); | ||
| 1867 | test_udp_unix_redir(skel, map, family); | ||
| 1868 | } | 1417 | } |
| 1869 | 1418 | ||
| 1870 | void serial_test_sockmap_listen(void) | 1419 | void serial_test_sockmap_listen(void) |
| @@ -1880,16 +1429,10 @@ void serial_test_sockmap_listen(void) · 9 unmodified lines | |||
| 1880 | skel->bss->test_sockmap = true; | 1429 | skel->bss->test_sockmap = true; |
| 1881 | run_tests(skel, skel->maps.sock_map, AF_INET); | 1430 | run_tests(skel, skel->maps.sock_map, AF_INET); |
| 1882 | run_tests(skel, skel->maps.sock_map, AF_INET6); | 1431 | run_tests(skel, skel->maps.sock_map, AF_INET6); |
| 1883 | test_unix_redir(skel, skel->maps.sock_map, SOCK_DGRAM); | ||
| 1884 | test_unix_redir(skel, skel->maps.sock_map, SOCK_STREAM); | ||
| 1885 | test_vsock_redir(skel, skel->maps.sock_map); | ||
| 1886 | 1432 | ||
| 1887 | skel->bss->test_sockmap = false; | 1433 | skel->bss->test_sockmap = false; |
| 1888 | run_tests(skel, skel->maps.sock_hash, AF_INET); | 1434 | run_tests(skel, skel->maps.sock_hash, AF_INET); |
| 1889 | run_tests(skel, skel->maps.sock_hash, AF_INET6); | 1435 | run_tests(skel, skel->maps.sock_hash, AF_INET6); |
| 1890 | test_unix_redir(skel, skel->maps.sock_hash, SOCK_DGRAM); | ||
| 1891 | test_unix_redir(skel, skel->maps.sock_hash, SOCK_STREAM); | ||
| 1892 | test_vsock_redir(skel, skel->maps.sock_hash); | ||
| 1893 | 1436 | ||
| 1894 | test_sockmap_listen__destroy(skel); | 1437 | test_sockmap_listen__destroy(skel); |
| 1895 | } | 1438 | } |
| @@ -0,0 +1,465 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Test for sockmap/sockhash redirection. | ||
| 4 | * | ||
| 5 | * BPF_MAP_TYPE_SOCKMAP | ||
| 6 | * BPF_MAP_TYPE_SOCKHASH | ||
| 7 | * x | ||
| 8 | * sk_msg-to-egress | ||
| 9 | * sk_msg-to-ingress | ||
| 10 | * sk_skb-to-egress | ||
| 11 | * sk_skb-to-ingress | ||
| 12 | * x | ||
| 13 | * AF_INET, SOCK_STREAM | ||
| 14 | * AF_INET6, SOCK_STREAM | ||
| 15 | * AF_INET, SOCK_DGRAM | ||
| 16 | * AF_INET6, SOCK_DGRAM | ||
| 17 | * AF_UNIX, SOCK_STREAM | ||
| 18 | * AF_UNIX, SOCK_DGRAM | ||
| 19 | * AF_VSOCK, SOCK_STREAM | ||
| 20 | * AF_VSOCK, SOCK_SEQPACKET | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <errno.h> | ||
| 24 | #include <error.h> | ||
| 25 | #include <sched.h> | ||
| 26 | #include <stdio.h> | ||
| 27 | #include <unistd.h> | ||
| 28 | |||
| 29 | #include <netinet/in.h> | ||
| 30 | #include <sys/socket.h> | ||
| 31 | #include <sys/types.h> | ||
| 32 | #include <sys/un.h> | ||
| 33 | #include <linux/string.h> | ||
| 34 | #include <linux/vm_sockets.h> | ||
| 35 | |||
| 36 | #include <bpf/bpf.h> | ||
| 37 | #include <bpf/libbpf.h> | ||
| 38 | |||
| 39 | #include "linux/const.h" | ||
| 40 | #include "test_progs.h" | ||
| 41 | #include "sockmap_helpers.h" | ||
| 42 | #include "test_sockmap_redir.skel.h" | ||
| 43 | |||
| 44 | /* The meaning of SUPPORTED is "will redirect packet as expected". | ||
| 45 | */ | ||
| 46 | #define SUPPORTED _BITUL(0) | ||
| 47 | |||
| 48 | /* Note on sk_skb-to-ingress ->af_vsock: | ||
| 49 | * | ||
| 50 | * Peer socket may receive the packet some time after the return from sendmsg(). | ||
| 51 | * In a typical usage scenario, recvmsg() will block until the redirected packet | ||
| 52 | * appears in the destination queue, or timeout if the packet was dropped. By | ||
| 53 | * that point, the verdict map has already been updated to reflect what has | ||
| 54 | * happened. | ||
| 55 | * | ||
| 56 | * But sk_skb-to-ingress/af_vsock is an unsupported combination, so no recvmsg() | ||
| 57 | * takes place. Which means we may race the execution of the verdict logic and | ||
| 58 | * read map_verd before it has been updated, i.e. we might observe | ||
| 59 | * map_verd[SK_DROP]=0 instead of map_verd[SK_DROP]=1. | ||
| 60 | * | ||
| 61 | * This confuses the selftest logic: if there was no packet dropped, where's the | ||
| 62 | * packet? So here's a heuristic: on map_verd[SK_DROP]=map_verd[SK_PASS]=0 | ||
| 63 | * (which implies the verdict program has not been ran) just re-read the verdict | ||
| 64 | * map again. | ||
| 65 | */ | ||
| 66 | #define UNSUPPORTED_RACY_VERD _BITUL(1) | ||
| 67 | |||
| 68 | enum prog_type { | ||
| 69 | SK_MSG_EGRESS, | ||
| 70 | SK_MSG_INGRESS, | ||
| 71 | SK_SKB_EGRESS, | ||
| 72 | SK_SKB_INGRESS, | ||
| 73 | }; | ||
| 74 | |||
| 75 | enum { | ||
| 76 | SEND_INNER = 0, | ||
| 77 | SEND_OUTER, | ||
| 78 | }; | ||
| 79 | |||
| 80 | enum { | ||
| 81 | RECV_INNER = 0, | ||
| 82 | RECV_OUTER, | ||
| 83 | }; | ||
| 84 | |||
| 85 | struct maps { | ||
| 86 | int in; | ||
| 87 | int out; | ||
| 88 | int verd; | ||
| 89 | }; | ||
| 90 | |||
| 91 | struct combo_spec { | ||
| 92 | enum prog_type prog_type; | ||
| 93 | const char *in, *out; | ||
| 94 | }; | ||
| 95 | |||
| 96 | struct redir_spec { | ||
| 97 | const char *name; | ||
| 98 | int idx_send; | ||
| 99 | int idx_recv; | ||
| 100 | enum prog_type prog_type; | ||
| 101 | }; | ||
| 102 | |||
| 103 | struct socket_spec { | ||
| 104 | int family; | ||
| 105 | int sotype; | ||
| 106 | int send_flags; | ||
| 107 | int in[2]; | ||
| 108 | int out[2]; | ||
| 109 | }; | ||
| 110 | |||
| 111 | static int socket_spec_pairs(struct socket_spec *s) | ||
| 112 | { | ||
| 113 | return create_socket_pairs(s->family, s->sotype, | ||
| 114 | &s->in[0], &s->out[0], | ||
| 115 | &s->in[1], &s->out[1]); | ||
| 116 | } | ||
| 117 | |||
| 118 | static void socket_spec_close(struct socket_spec *s) | ||
| 119 | { | ||
| 120 | xclose(s->in[0]); | ||
| 121 | xclose(s->in[1]); | ||
| 122 | xclose(s->out[0]); | ||
| 123 | xclose(s->out[1]); | ||
| 124 | } | ||
| 125 | |||
| 126 | static void get_redir_params(struct redir_spec *redir, | ||
| 127 | struct test_sockmap_redir *skel, int *prog_fd, | ||
| 128 | enum bpf_attach_type *attach_type, | ||
| 129 | int *redirect_flags) | ||
| 130 | { | ||
| 131 | enum prog_type type = redir->prog_type; | ||
| 132 | struct bpf_program *prog; | ||
| 133 | bool sk_msg; | ||
| 134 | |||
| 135 | sk_msg = type == SK_MSG_INGRESS || type == SK_MSG_EGRESS; | ||
| 136 | prog = sk_msg ? skel->progs.prog_msg_verdict : skel->progs.prog_skb_verdict; | ||
| 137 | |||
| 138 | *prog_fd = bpf_program__fd(prog); | ||
| 139 | *attach_type = sk_msg ? BPF_SK_MSG_VERDICT : BPF_SK_SKB_VERDICT; | ||
| 140 | |||
| 141 | if (type == SK_MSG_INGRESS || type == SK_SKB_INGRESS) | ||
| 142 | *redirect_flags = BPF_F_INGRESS; | ||
| 143 | else | ||
| 144 | *redirect_flags = 0; | ||
| 145 | } | ||
| 146 | |||
| 147 | static void try_recv(const char *prefix, int fd, int flags, bool expect_success) | ||
| 148 | { | ||
| 149 | ssize_t n; | ||
| 150 | char buf; | ||
| 151 | |||
| 152 | errno = 0; | ||
| 153 | n = recv(fd, &buf, 1, flags); | ||
| 154 | if (n < 0 && expect_success) | ||
| 155 | FAIL_ERRNO("%s: unexpected failure: retval=%zd", prefix, n); | ||
| 156 | if (!n && !expect_success) | ||
| 157 | FAIL("%s: expected failure: retval=%zd", prefix, n); | ||
| 158 | } | ||
| 159 | |||
| 160 | static void handle_unsupported(int sd_send, int sd_peer, int sd_in, int sd_out, | ||
| 161 | int sd_recv, int map_verd, int status) | ||
| 162 | { | ||
| 163 | unsigned int drop, pass; | ||
| 164 | char recv_buf; | ||
| 165 | ssize_t n; | ||
| 166 | |||
| 167 | get_verdict: | ||
| 168 | if (xbpf_map_lookup_elem(map_verd, &u32(SK_DROP), &drop) || | ||
| 169 | xbpf_map_lookup_elem(map_verd, &u32(SK_PASS), &pass)) | ||
| 170 | return; | ||
| 171 | |||
| 172 | if (pass == 0 && drop == 0 && (status & UNSUPPORTED_RACY_VERD)) { | ||
| 173 | sched_yield(); | ||
| 174 | goto get_verdict; | ||
| 175 | } | ||
| 176 | |||
| 177 | if (pass != 0) { | ||
| 178 | FAIL("unsupported: wanted verdict pass 0, have %u", pass); | ||
| 179 | return; | ||
| 180 | } | ||
| 181 | |||
| 182 | /* If nothing was dropped, packet should have reached the peer */ | ||
| 183 | if (drop == 0) { | ||
| 184 | errno = 0; | ||
| 185 | n = recv_timeout(sd_peer, &recv_buf, 1, 0, IO_TIMEOUT_SEC); | ||
| 186 | if (n != 1) | ||
| 187 | FAIL_ERRNO("unsupported: packet missing, retval=%zd", n); | ||
| 188 | } | ||
| 189 | |||
| 190 | /* Ensure queues are empty */ | ||
| 191 | try_recv("bpf.recv(sd_send)", sd_send, MSG_DONTWAIT, false); | ||
| 192 | if (sd_in != sd_send) | ||
| 193 | try_recv("bpf.recv(sd_in)", sd_in, MSG_DONTWAIT, false); | ||
| 194 | |||
| 195 | try_recv("bpf.recv(sd_out)", sd_out, MSG_DONTWAIT, false); | ||
| 196 | if (sd_recv != sd_out) | ||
| 197 | try_recv("bpf.recv(sd_recv)", sd_recv, MSG_DONTWAIT, false); | ||
| 198 | } | ||
| 199 | |||
| 200 | static void test_send_redir_recv(int sd_send, int send_flags, int sd_peer, | ||
| 201 | int sd_in, int sd_out, int sd_recv, | ||
| 202 | struct maps *maps, int status) | ||
| 203 | { | ||
| 204 | unsigned int drop, pass; | ||
| 205 | char *send_buf = "ab"; | ||
| 206 | char recv_buf = '\0'; | ||
| 207 | ssize_t n, len = 1; | ||
| 208 | |||
| 209 | /* Zero out the verdict map */ | ||
| 210 | if (xbpf_map_update_elem(maps->verd, &u32(SK_DROP), &u32(0), BPF_ANY) || | ||
| 211 | xbpf_map_update_elem(maps->verd, &u32(SK_PASS), &u32(0), BPF_ANY)) | ||
| 212 | return; | ||
| 213 | |||
| 214 | if (xbpf_map_update_elem(maps->in, &u32(0), &u64(sd_in), BPF_NOEXIST)) | ||
| 215 | return; | ||
| 216 | |||
| 217 | if (xbpf_map_update_elem(maps->out, &u32(0), &u64(sd_out), BPF_NOEXIST)) | ||
| 218 | goto del_in; | ||
| 219 | |||
| 220 | /* Last byte is OOB data when send_flags has MSG_OOB bit set */ | ||
| 221 | if (send_flags & MSG_OOB) | ||
| 222 | len++; | ||
| 223 | n = send(sd_send, send_buf, len, send_flags); | ||
| 224 | if (n >= 0 && n < len) | ||
| 225 | FAIL("incomplete send"); | ||
| 226 | if (n < 0) { | ||
| 227 | /* sk_msg redirect combo not supported? */ | ||
| 228 | if (status & SUPPORTED || errno != EACCES) | ||
| 229 | FAIL_ERRNO("send"); | ||
| 230 | goto out; | ||
| 231 | } | ||
| 232 | |||
| 233 | if (!(status & SUPPORTED)) { | ||
| 234 | handle_unsupported(sd_send, sd_peer, sd_in, sd_out, sd_recv, | ||
| 235 | maps->verd, status); | ||
| 236 | goto out; | ||
| 237 | } | ||
| 238 | |||
| 239 | errno = 0; | ||
| 240 | n = recv_timeout(sd_recv, &recv_buf, 1, 0, IO_TIMEOUT_SEC); | ||
| 241 | if (n != 1) { | ||
| 242 | FAIL_ERRNO("recv_timeout()"); | ||
| 243 | goto out; | ||
| 244 | } | ||
| 245 | |||
| 246 | /* Check verdict _after_ recv(); af_vsock may need time to catch up */ | ||
| 247 | if (xbpf_map_lookup_elem(maps->verd, &u32(SK_DROP), &drop) || | ||
| 248 | xbpf_map_lookup_elem(maps->verd, &u32(SK_PASS), &pass)) | ||
| 249 | goto out; | ||
| 250 | |||
| 251 | if (drop != 0 || pass != 1) | ||
| 252 | FAIL("unexpected verdict drop/pass: wanted 0/1, have %u/%u", | ||
| 253 | drop, pass); | ||
| 254 | |||
| 255 | if (recv_buf != send_buf[0]) | ||
| 256 | FAIL("recv(): payload check, %02x != %02x", recv_buf, send_buf[0]); | ||
| 257 | |||
| 258 | if (send_flags & MSG_OOB) { | ||
| 259 | /* Fail reading OOB while in sockmap */ | ||
| 260 | try_recv("bpf.recv(sd_out, MSG_OOB)", sd_out, | ||
| 261 | MSG_OOB | MSG_DONTWAIT, false); | ||
| 262 | |||
| 263 | /* Remove sd_out from sockmap */ | ||
| 264 | xbpf_map_delete_elem(maps->out, &u32(0)); | ||
| 265 | |||
| 266 | /* Check that OOB was dropped on redirect */ | ||
| 267 | try_recv("recv(sd_out, MSG_OOB)", sd_out, | ||
| 268 | MSG_OOB | MSG_DONTWAIT, false); | ||
| 269 | |||
| 270 | goto del_in; | ||
| 271 | } | ||
| 272 | out: | ||
| 273 | xbpf_map_delete_elem(maps->out, &u32(0)); | ||
| 274 | del_in: | ||
| 275 | xbpf_map_delete_elem(maps->in, &u32(0)); | ||
| 276 | } | ||
| 277 | |||
| 278 | static int is_redir_supported(enum prog_type type, const char *in, | ||
| 279 | const char *out) | ||
| 280 | { | ||
| 281 | /* Matching based on strings returned by socket_kind_to_str(): | ||
| 282 | * tcp4, udp4, tcp6, udp6, u_str, u_dgr, v_str, v_seq | ||
| 283 | * Plus a wildcard: any | ||
| 284 | * Not in use: u_seq, v_dgr | ||
| 285 | */ | ||
| 286 | struct combo_spec *c, combos[] = { | ||
| 287 | /* Send to local: TCP -> any, but vsock */ | ||
| 288 | { SK_MSG_INGRESS, "tcp", "tcp" }, | ||
| 289 | { SK_MSG_INGRESS, "tcp", "udp" }, | ||
| 290 | { SK_MSG_INGRESS, "tcp", "u_str" }, | ||
| 291 | { SK_MSG_INGRESS, "tcp", "u_dgr" }, | ||
| 292 | |||
| 293 | /* Send to egress: TCP -> TCP */ | ||
| 294 | { SK_MSG_EGRESS, "tcp", "tcp" }, | ||
| 295 | |||
| 296 | /* Ingress to egress: any -> any */ | ||
| 297 | { SK_SKB_EGRESS, "any", "any" }, | ||
| 298 | |||
| 299 | /* Ingress to local: any -> any, but vsock */ | ||
| 300 | { SK_SKB_INGRESS, "any", "tcp" }, | ||
| 301 | { SK_SKB_INGRESS, "any", "udp" }, | ||
| 302 | { SK_SKB_INGRESS, "any", "u_str" }, | ||
| 303 | { SK_SKB_INGRESS, "any", "u_dgr" }, | ||
| 304 | }; | ||
| 305 | |||
| 306 | for (c = combos; c < combos + ARRAY_SIZE(combos); c++) { | ||
| 307 | if (c->prog_type == type && | ||
| 308 | (!strcmp(c->in, "any") || strstarts(in, c->in)) && | ||
| 309 | (!strcmp(c->out, "any") || strstarts(out, c->out))) | ||
| 310 | return SUPPORTED; | ||
| 311 | } | ||
| 312 | |||
| 313 | return 0; | ||
| 314 | } | ||
| 315 | |||
| 316 | static int get_support_status(enum prog_type type, const char *in, | ||
| 317 | const char *out) | ||
| 318 | { | ||
| 319 | int status = is_redir_supported(type, in, out); | ||
| 320 | |||
| 321 | if (type == SK_SKB_INGRESS && strstarts(out, "v_")) | ||
| 322 | status |= UNSUPPORTED_RACY_VERD; | ||
| 323 | |||
| 324 | return status; | ||
| 325 | } | ||
| 326 | |||
| 327 | static void test_socket(enum bpf_map_type type, struct redir_spec *redir, | ||
| 328 | struct maps *maps, struct socket_spec *s_in, | ||
| 329 | struct socket_spec *s_out) | ||
| 330 | { | ||
| 331 | int fd_in, fd_out, fd_send, fd_peer, fd_recv, flags, status; | ||
| 332 | const char *in_str, *out_str; | ||
| 333 | char s[MAX_TEST_NAME]; | ||
| 334 | |||
| 335 | fd_in = s_in->in[0]; | ||
| 336 | fd_out = s_out->out[0]; | ||
| 337 | fd_send = s_in->in[redir->idx_send]; | ||
| 338 | fd_peer = s_in->in[redir->idx_send ^ 1]; | ||
| 339 | fd_recv = s_out->out[redir->idx_recv]; | ||
| 340 | flags = s_in->send_flags; | ||
| 341 | |||
| 342 | in_str = socket_kind_to_str(fd_in); | ||
| 343 | out_str = socket_kind_to_str(fd_out); | ||
| 344 | status = get_support_status(redir->prog_type, in_str, out_str); | ||
| 345 | |||
| 346 | snprintf(s, sizeof(s), | ||
| 347 | "%-4s %-17s %-5s %s %-5s%6s", | ||
| 348 | /* hash sk_skb-to-ingress u_str → v_str (OOB) */ | ||
| 349 | type == BPF_MAP_TYPE_SOCKMAP ? "map" : "hash", | ||
| 350 | redir->name, | ||
| 351 | in_str, | ||
| 352 | status & SUPPORTED ? "→" : " ", | ||
| 353 | out_str, | ||
| 354 | (flags & MSG_OOB) ? "(OOB)" : ""); | ||
| 355 | |||
| 356 | if (!test__start_subtest(s)) | ||
| 357 | return; | ||
| 358 | |||
| 359 | test_send_redir_recv(fd_send, flags, fd_peer, fd_in, fd_out, fd_recv, | ||
| 360 | maps, status); | ||
| 361 | } | ||
| 362 | |||
| 363 | static void test_redir(enum bpf_map_type type, struct redir_spec *redir, | ||
| 364 | struct maps *maps) | ||
| 365 | { | ||
| 366 | struct socket_spec *s, sockets[] = { | ||
| 367 | { AF_INET, SOCK_STREAM }, | ||
| 368 | // { AF_INET, SOCK_STREAM, MSG_OOB }, /* Known to be broken */ | ||
| 369 | { AF_INET6, SOCK_STREAM }, | ||
| 370 | { AF_INET, SOCK_DGRAM }, | ||
| 371 | { AF_INET6, SOCK_DGRAM }, | ||
| 372 | { AF_UNIX, SOCK_STREAM }, | ||
| 373 | { AF_UNIX, SOCK_STREAM, MSG_OOB }, | ||
| 374 | { AF_UNIX, SOCK_DGRAM }, | ||
| 375 | // { AF_UNIX, SOCK_SEQPACKET}, /* Unsupported BPF_MAP_UPDATE_ELEM */ | ||
| 376 | { AF_VSOCK, SOCK_STREAM }, | ||
| 377 | // { AF_VSOCK, SOCK_DGRAM }, /* Unsupported socket() */ | ||
| 378 | { AF_VSOCK, SOCK_SEQPACKET }, | ||
| 379 | }; | ||
| 380 | |||
| 381 | for (s = sockets; s < sockets + ARRAY_SIZE(sockets); s++) | ||
| 382 | if (socket_spec_pairs(s)) | ||
| 383 | goto out; | ||
| 384 | |||
| 385 | /* Intra-proto */ | ||
| 386 | for (s = sockets; s < sockets + ARRAY_SIZE(sockets); s++) | ||
| 387 | test_socket(type, redir, maps, s, s); | ||
| 388 | |||
| 389 | /* Cross-proto */ | ||
| 390 | for (int i = 0; i < ARRAY_SIZE(sockets); i++) { | ||
| 391 | for (int j = 0; j < ARRAY_SIZE(sockets); j++) { | ||
| 392 | struct socket_spec *out = &sockets[j]; | ||
| 393 | struct socket_spec *in = &sockets[i]; | ||
| 394 | |||
| 395 | /* Skip intra-proto and between variants */ | ||
| 396 | if (out->send_flags || | ||
| 397 | (in->family == out->family && | ||
| 398 | in->sotype == out->sotype)) | ||
| 399 | continue; | ||
| 400 | |||
| 401 | test_socket(type, redir, maps, in, out); | ||
| 402 | } | ||
| 403 | } | ||
| 404 | out: | ||
| 405 | while (--s >= sockets) | ||
| 406 | socket_spec_close(s); | ||
| 407 | } | ||
| 408 | |||
| 409 | static void test_map(enum bpf_map_type type) | ||
| 410 | { | ||
| 411 | struct redir_spec *r, redirs[] = { | ||
| 412 | { "sk_msg-to-ingress", SEND_INNER, RECV_INNER, SK_MSG_INGRESS }, | ||
| 413 | { "sk_msg-to-egress", SEND_INNER, RECV_OUTER, SK_MSG_EGRESS }, | ||
| 414 | { "sk_skb-to-egress", SEND_OUTER, RECV_OUTER, SK_SKB_EGRESS }, | ||
| 415 | { "sk_skb-to-ingress", SEND_OUTER, RECV_INNER, SK_SKB_INGRESS }, | ||
| 416 | }; | ||
| 417 | |||
| 418 | for (r = redirs; r < redirs + ARRAY_SIZE(redirs); r++) { | ||
| 419 | enum bpf_attach_type attach_type; | ||
| 420 | struct test_sockmap_redir *skel; | ||
| 421 | struct maps maps; | ||
| 422 | int prog_fd; | ||
| 423 | |||
| 424 | skel = test_sockmap_redir__open_and_load(); | ||
| 425 | if (!skel) { | ||
| 426 | FAIL("open_and_load"); | ||
| 427 | return; | ||
| 428 | } | ||
| 429 | |||
| 430 | switch (type) { | ||
| 431 | case BPF_MAP_TYPE_SOCKMAP: | ||
| 432 | maps.in = bpf_map__fd(skel->maps.nop_map); | ||
| 433 | maps.out = bpf_map__fd(skel->maps.sock_map); | ||
| 434 | break; | ||
| 435 | case BPF_MAP_TYPE_SOCKHASH: | ||
| 436 | maps.in = bpf_map__fd(skel->maps.nop_hash); | ||
| 437 | maps.out = bpf_map__fd(skel->maps.sock_hash); | ||
| 438 | break; | ||
| 439 | default: | ||
| 440 | FAIL("Unsupported bpf_map_type"); | ||
| 441 | return; | ||
| 442 | } | ||
| 443 | |||
| 444 | skel->bss->redirect_type = type; | ||
| 445 | maps.verd = bpf_map__fd(skel->maps.verdict_map); | ||
| 446 | get_redir_params(r, skel, &prog_fd, &attach_type, | ||
| 447 | &skel->bss->redirect_flags); | ||
| 448 | |||
| 449 | if (xbpf_prog_attach(prog_fd, maps.in, attach_type, 0)) | ||
| 450 | return; | ||
| 451 | |||
| 452 | test_redir(type, r, &maps); | ||
| 453 | |||
| 454 | if (xbpf_prog_detach2(prog_fd, maps.in, attach_type)) | ||
| 455 | return; | ||
| 456 | |||
| 457 | test_sockmap_redir__destroy(skel); | ||
| 458 | } | ||
| 459 | } | ||
| 460 | |||
| 461 | void serial_test_sockmap_redir(void) | ||
| 462 | { | ||
| 463 | test_map(BPF_MAP_TYPE_SOCKMAP); | ||
| 464 | test_map(BPF_MAP_TYPE_SOCKHASH); | ||
| 465 | } | ||
| @@ -56,6 +56,8 @@ · 55 unmodified lines | |||
| 56 | 56 | ||
| 57 | #define MAC_DST_FWD "00:11:22:33:44:55" | 57 | #define MAC_DST_FWD "00:11:22:33:44:55" |
| 58 | #define MAC_DST "00:22:33:44:55:66" | 58 | #define MAC_DST "00:22:33:44:55:66" |
| 59 | #define MAC_SRC_FWD "00:33:44:55:66:77" | ||
| 60 | #define MAC_SRC "00:44:55:66:77:88" | ||
| 59 | 61 | ||
| 60 | #define IFADDR_STR_LEN 18 | 62 | #define IFADDR_STR_LEN 18 |
| 61 | #define PING_ARGS "-i 0.2 -c 3 -w 10 -q" | 63 | #define PING_ARGS "-i 0.2 -c 3 -w 10 -q" |
| @@ -207,11 +209,10 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result) · 145 unmodified lines | |||
| 207 | int err; | 209 | int err; |
| 208 | 210 | ||
| 209 | if (result->dev_mode == MODE_VETH) { | 211 | if (result->dev_mode == MODE_VETH) { |
| 210 | SYS(fail, "ip link add src type veth peer name src_fwd"); | 212 | SYS(fail, "ip link add src address " MAC_SRC " type veth " |
| 211 | SYS(fail, "ip link add dst type veth peer name dst_fwd"); | 213 | "peer name src_fwd address " MAC_SRC_FWD); |
| 212 | 214 | SYS(fail, "ip link add dst address " MAC_DST " type veth " | |
| 213 | SYS(fail, "ip link set dst_fwd address " MAC_DST_FWD); | 215 | "peer name dst_fwd address " MAC_DST_FWD); |
| 214 | SYS(fail, "ip link set dst address " MAC_DST); | ||
| 215 | } else if (result->dev_mode == MODE_NETKIT) { | 216 | } else if (result->dev_mode == MODE_NETKIT) { |
| 216 | err = create_netkit(NETKIT_L3, "src", "src_fwd"); | 217 | err = create_netkit(NETKIT_L3, "src", "src_fwd"); |
| 217 | if (!ASSERT_OK(err, "create_ifindex_src")) | 218 | if (!ASSERT_OK(err, "create_ifindex_src")) |
| @@ -0,0 +1,64 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright (c) 2025 Meta Platforms Inc. */ | ||
| 3 | #include <test_progs.h> | ||
| 4 | #include "test_btf_ext.skel.h" | ||
| 5 | #include "btf_helpers.h" | ||
| 6 | |||
| 7 | static void subtest_line_func_info(void) | ||
| 8 | { | ||
| 9 | struct test_btf_ext *skel; | ||
| 10 | struct bpf_prog_info info; | ||
| 11 | struct bpf_line_info line_info[128], *libbpf_line_info; | ||
| 12 | struct bpf_func_info func_info[128], *libbpf_func_info; | ||
| 13 | __u32 info_len = sizeof(info), libbbpf_line_info_cnt, libbbpf_func_info_cnt; | ||
| 14 | int err, fd; | ||
| 15 | |||
| 16 | skel = test_btf_ext__open_and_load(); | ||
| 17 | if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) | ||
| 18 | return; | ||
| 19 | |||
| 20 | fd = bpf_program__fd(skel->progs.global_func); | ||
| 21 | |||
| 22 | memset(&info, 0, sizeof(info)); | ||
| 23 | info.line_info = ptr_to_u64(&line_info); | ||
| 24 | info.nr_line_info = sizeof(line_info); | ||
| 25 | info.line_info_rec_size = sizeof(*line_info); | ||
| 26 | err = bpf_prog_get_info_by_fd(fd, &info, &info_len); | ||
| 27 | if (!ASSERT_OK(err, "prog_line_info")) | ||
| 28 | goto out; | ||
| 29 | |||
| 30 | libbpf_line_info = bpf_program__line_info(skel->progs.global_func); | ||
| 31 | libbbpf_line_info_cnt = bpf_program__line_info_cnt(skel->progs.global_func); | ||
| 32 | |||
| 33 | memset(&info, 0, sizeof(info)); | ||
| 34 | info.func_info = ptr_to_u64(&func_info); | ||
| 35 | info.nr_func_info = sizeof(func_info); | ||
| 36 | info.func_info_rec_size = sizeof(*func_info); | ||
| 37 | err = bpf_prog_get_info_by_fd(fd, &info, &info_len); | ||
| 38 | if (!ASSERT_OK(err, "prog_func_info")) | ||
| 39 | goto out; | ||
| 40 | |||
| 41 | libbpf_func_info = bpf_program__func_info(skel->progs.global_func); | ||
| 42 | libbbpf_func_info_cnt = bpf_program__func_info_cnt(skel->progs.global_func); | ||
| 43 | |||
| 44 | if (!ASSERT_OK_PTR(libbpf_line_info, "bpf_program__line_info")) | ||
| 45 | goto out; | ||
| 46 | if (!ASSERT_EQ(libbbpf_line_info_cnt, info.nr_line_info, "line_info_cnt")) | ||
| 47 | goto out; | ||
| 48 | if (!ASSERT_OK_PTR(libbpf_func_info, "bpf_program__func_info")) | ||
| 49 | goto out; | ||
| 50 | if (!ASSERT_EQ(libbbpf_func_info_cnt, info.nr_func_info, "func_info_cnt")) | ||
| 51 | goto out; | ||
| 52 | ASSERT_MEMEQ(libbpf_line_info, line_info, libbbpf_line_info_cnt * sizeof(*line_info), | ||
| 53 | "line_info"); | ||
| 54 | ASSERT_MEMEQ(libbpf_func_info, func_info, libbbpf_func_info_cnt * sizeof(*func_info), | ||
| 55 | "func_info"); | ||
| 56 | out: | ||
| 57 | test_btf_ext__destroy(skel); | ||
| 58 | } | ||
| 59 | |||
| 60 | void test_btf_ext(void) | ||
| 61 | { | ||
| 62 | if (test__start_subtest("line_func_info")) | ||
| 63 | subtest_line_func_info(); | ||
| 64 | } | ||
| @@ -63,6 +63,9 @@ static void test_set_global_vars_succeeds(void) · 62 unmodified lines | |||
| 63 | " -G \"var_eb = EB2\" "\ | 63 | " -G \"var_eb = EB2\" "\ |
| 64 | " -G \"var_ec = EC2\" "\ | 64 | " -G \"var_ec = EC2\" "\ |
| 65 | " -G \"var_b = 1\" "\ | 65 | " -G \"var_b = 1\" "\ |
| 66 | " -G \"struct1.struct2.u.var_u8 = 170\" "\ | ||
| 67 | " -G \"union1.struct3.var_u8_l = 0xaa\" "\ | ||
| 68 | " -G \"union1.struct3.var_u8_h = 0xaa\" "\ | ||
| 66 | "-vl2 > %s", fix->veristat, fix->tmpfile); | 69 | "-vl2 > %s", fix->veristat, fix->tmpfile); |
| 67 | 70 | ||
| 68 | read(fix->fd, fix->output, fix->sz); | 71 | read(fix->fd, fix->output, fix->sz); |
| @@ -78,6 +81,8 @@ static void test_set_global_vars_succeeds(void) · 9 unmodified lines | |||
| 78 | __CHECK_STR("_w=12 ", "var_eb = EB2"); | 81 | __CHECK_STR("_w=12 ", "var_eb = EB2"); |
| 79 | __CHECK_STR("_w=13 ", "var_ec = EC2"); | 82 | __CHECK_STR("_w=13 ", "var_ec = EC2"); |
| 80 | __CHECK_STR("_w=1 ", "var_b = 1"); | 83 | __CHECK_STR("_w=1 ", "var_b = 1"); |
| 84 | __CHECK_STR("_w=170 ", "struct1.struct2.u.var_u8 = 170"); | ||
| 85 | __CHECK_STR("_w=0xaaaa ", "union1.var_u16 = 0xaaaa"); | ||
| 81 | 86 | ||
| 82 | out: | 87 | out: |
| 83 | teardown_fixture(fix); | 88 | teardown_fixture(fix); |
| @@ -14,6 +14,7 @@ · 13 unmodified lines | |||
| 14 | #include "verifier_bounds_deduction_non_const.skel.h" | 14 | #include "verifier_bounds_deduction_non_const.skel.h" |
| 15 | #include "verifier_bounds_mix_sign_unsign.skel.h" | 15 | #include "verifier_bounds_mix_sign_unsign.skel.h" |
| 16 | #include "verifier_bpf_get_stack.skel.h" | 16 | #include "verifier_bpf_get_stack.skel.h" |
| 17 | #include "verifier_bpf_trap.skel.h" | ||
| 17 | #include "verifier_bswap.skel.h" | 18 | #include "verifier_bswap.skel.h" |
| 18 | #include "verifier_btf_ctx_access.skel.h" | 19 | #include "verifier_btf_ctx_access.skel.h" |
| 19 | #include "verifier_btf_unreliable_prog.skel.h" | 20 | #include "verifier_btf_unreliable_prog.skel.h" |
| @@ -148,6 +149,7 @@ void test_verifier_bounds_deduction(void) { RUN(verifier_bounds_deduction); · 128 unmodified lines | |||
| 148 | void test_verifier_bounds_deduction_non_const(void) { RUN(verifier_bounds_deduction_non_const); } | 149 | void test_verifier_bounds_deduction_non_const(void) { RUN(verifier_bounds_deduction_non_const); } |
| 149 | void test_verifier_bounds_mix_sign_unsign(void) { RUN(verifier_bounds_mix_sign_unsign); } | 150 | void test_verifier_bounds_mix_sign_unsign(void) { RUN(verifier_bounds_mix_sign_unsign); } |
| 150 | void test_verifier_bpf_get_stack(void) { RUN(verifier_bpf_get_stack); } | 151 | void test_verifier_bpf_get_stack(void) { RUN(verifier_bpf_get_stack); } |
| 152 | void test_verifier_bpf_trap(void) { RUN(verifier_bpf_trap); } | ||
| 151 | void test_verifier_bswap(void) { RUN(verifier_bswap); } | 153 | void test_verifier_bswap(void) { RUN(verifier_bswap); } |
| 152 | void test_verifier_btf_ctx_access(void) { RUN(verifier_btf_ctx_access); } | 154 | void test_verifier_btf_ctx_access(void) { RUN(verifier_btf_ctx_access); } |
| 153 | void test_verifier_btf_unreliable_prog(void) { RUN(verifier_btf_unreliable_prog); } | 155 | void test_verifier_btf_unreliable_prog(void) { RUN(verifier_btf_unreliable_prog); } |
| @@ -351,9 +351,10 @@ void test_xdp_metadata(void) · 350 unmodified lines | |||
| 351 | struct xdp_metadata2 *bpf_obj2 = NULL; | 351 | struct xdp_metadata2 *bpf_obj2 = NULL; |
| 352 | struct xdp_metadata *bpf_obj = NULL; | 352 | struct xdp_metadata *bpf_obj = NULL; |
| 353 | struct bpf_program *new_prog, *prog; | 353 | struct bpf_program *new_prog, *prog; |
| 354 | struct bpf_devmap_val devmap_e = {}; | ||
| 355 | struct bpf_map *prog_arr, *devmap; | ||
| 354 | struct nstoken *tok = NULL; | 356 | struct nstoken *tok = NULL; |
| 355 | __u32 queue_id = QUEUE_ID; | 357 | __u32 queue_id = QUEUE_ID; |
| 356 | struct bpf_map *prog_arr; | ||
| 357 | struct xsk tx_xsk = {}; | 358 | struct xsk tx_xsk = {}; |
| 358 | struct xsk rx_xsk = {}; | 359 | struct xsk rx_xsk = {}; |
| 359 | __u32 val, key = 0; | 360 | __u32 val, key = 0; |
| @@ -409,6 +410,13 @@ void test_xdp_metadata(void) · 49 unmodified lines | |||
| 409 | bpf_program__set_ifindex(prog, rx_ifindex); | 410 | bpf_program__set_ifindex(prog, rx_ifindex); |
| 410 | bpf_program__set_flags(prog, BPF_F_XDP_DEV_BOUND_ONLY); | 411 | bpf_program__set_flags(prog, BPF_F_XDP_DEV_BOUND_ONLY); |
| 411 | 412 | ||
| 413 | /* Make sure we can load a dev-bound program that performs | ||
| 414 | * XDP_REDIRECT into a devmap. | ||
| 415 | */ | ||
| 416 | new_prog = bpf_object__find_program_by_name(bpf_obj->obj, "redirect"); | ||
| 417 | bpf_program__set_ifindex(new_prog, rx_ifindex); | ||
| 418 | bpf_program__set_flags(new_prog, BPF_F_XDP_DEV_BOUND_ONLY); | ||
| 419 | |||
| 412 | if (!ASSERT_OK(xdp_metadata__load(bpf_obj), "load skeleton")) | 420 | if (!ASSERT_OK(xdp_metadata__load(bpf_obj), "load skeleton")) |
| 413 | goto out; | 421 | goto out; |
| 414 | 422 | ||
| @@ -423,6 +431,18 @@ void test_xdp_metadata(void) · 8 unmodified lines | |||
| 423 | "update prog_arr")) | 431 | "update prog_arr")) |
| 424 | goto out; | 432 | goto out; |
| 425 | 433 | ||
| 434 | /* Make sure we can't add dev-bound programs to devmaps. */ | ||
| 435 | devmap = bpf_object__find_map_by_name(bpf_obj->obj, "dev_map"); | ||
| 436 | if (!ASSERT_OK_PTR(devmap, "no dev_map found")) | ||
| 437 | goto out; | ||
| 438 | |||
| 439 | devmap_e.bpf_prog.fd = val; | ||
| 440 | if (!ASSERT_ERR(bpf_map__update_elem(devmap, &key, sizeof(key), | ||
| 441 | &devmap_e, sizeof(devmap_e), | ||
| 442 | BPF_ANY), | ||
| 443 | "update dev_map")) | ||
| 444 | goto out; | ||
| 445 | |||
| 426 | /* Attach BPF program to RX interface. */ | 446 | /* Attach BPF program to RX interface. */ |
| 427 | 447 | ||
| 428 | ret = bpf_xdp_attach(rx_ifindex, | 448 | ret = bpf_xdp_attach(rx_ifindex, |
| @@ -0,0 +1,65 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | #include <linux/bpf.h> | ||
| 3 | #include <bpf/bpf_helpers.h> | ||
| 4 | #include <bpf/bpf_endian.h> | ||
| 5 | |||
| 6 | long process_byte = 0; | ||
| 7 | int verdict_dir = 0; | ||
| 8 | int dropped = 0; | ||
| 9 | int pkt_size = 0; | ||
| 10 | struct { | ||
| 11 | __uint(type, BPF_MAP_TYPE_SOCKMAP); | ||
| 12 | __uint(max_entries, 20); | ||
| 13 | __type(key, int); | ||
| 14 | __type(value, int); | ||
| 15 | } sock_map_rx SEC(".maps"); | ||
| 16 | |||
| 17 | struct { | ||
| 18 | __uint(type, BPF_MAP_TYPE_SOCKMAP); | ||
| 19 | __uint(max_entries, 20); | ||
| 20 | __type(key, int); | ||
| 21 | __type(value, int); | ||
| 22 | } sock_map_tx SEC(".maps"); | ||
| 23 | |||
| 24 | SEC("sk_skb/stream_parser") | ||
| 25 | int prog_skb_parser(struct __sk_buff *skb) | ||
| 26 | { | ||
| 27 | return pkt_size; | ||
| 28 | } | ||
| 29 | |||
| 30 | SEC("sk_skb/stream_verdict") | ||
| 31 | int prog_skb_verdict(struct __sk_buff *skb) | ||
| 32 | { | ||
| 33 | int one = 1; | ||
| 34 | int ret = bpf_sk_redirect_map(skb, &sock_map_rx, one, verdict_dir); | ||
| 35 | |||
| 36 | if (ret == SK_DROP) | ||
| 37 | dropped++; | ||
| 38 | __sync_fetch_and_add(&process_byte, skb->len); | ||
| 39 | return ret; | ||
| 40 | } | ||
| 41 | |||
| 42 | SEC("sk_skb/stream_verdict") | ||
| 43 | int prog_skb_pass(struct __sk_buff *skb) | ||
| 44 | { | ||
| 45 | __sync_fetch_and_add(&process_byte, skb->len); | ||
| 46 | return SK_PASS; | ||
| 47 | } | ||
| 48 | |||
| 49 | SEC("sk_msg") | ||
| 50 | int prog_skmsg_verdict(struct sk_msg_md *msg) | ||
| 51 | { | ||
| 52 | int one = 1; | ||
| 53 | |||
| 54 | __sync_fetch_and_add(&process_byte, msg->size); | ||
| 55 | return bpf_msg_redirect_map(msg, &sock_map_tx, one, verdict_dir); | ||
| 56 | } | ||
| 57 | |||
| 58 | SEC("sk_msg") | ||
| 59 | int prog_skmsg_pass(struct sk_msg_md *msg) | ||
| 60 | { | ||
| 61 | __sync_fetch_and_add(&process_byte, msg->size); | ||
| 62 | return SK_PASS; | ||
| 63 | } | ||
| 64 | |||
| 65 | char _license[] SEC("license") = "GPL"; | ||
| @@ -32,6 +32,7 @@ extern unsigned long CONFIG_NR_CPUS __kconfig; · 31 unmodified lines | |||
| 32 | struct __qspinlock { | 32 | struct __qspinlock { |
| 33 | union { | 33 | union { |
| 34 | atomic_t val; | 34 | atomic_t val; |
| 35 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | ||
| 35 | struct { | 36 | struct { |
| 36 | u8 locked; | 37 | u8 locked; |
| 37 | u8 pending; | 38 | u8 pending; |
| @@ -40,6 +41,17 @@ struct __qspinlock { · 2 unmodified lines | |||
| 40 | u16 locked_pending; | 41 | u16 locked_pending; |
| 41 | u16 tail; | 42 | u16 tail; |
| 42 | }; | 43 | }; |
| 44 | #else | ||
| 45 | struct { | ||
| 46 | u16 tail; | ||
| 47 | u16 locked_pending; | ||
| 48 | }; | ||
| 49 | struct { | ||
| 50 | u8 reserved[2]; | ||
| 51 | u8 pending; | ||
| 52 | u8 locked; | ||
| 53 | }; | ||
| 54 | #endif | ||
| 43 | }; | 55 | }; |
| 44 | }; | 56 | }; |
| 45 | 57 | ||
| @@ -95,9 +107,6 @@ struct arena_qnode { · 49 unmodified lines | |||
| 95 | #define _Q_LOCKED_VAL (1U << _Q_LOCKED_OFFSET) | 107 | #define _Q_LOCKED_VAL (1U << _Q_LOCKED_OFFSET) |
| 96 | #define _Q_PENDING_VAL (1U << _Q_PENDING_OFFSET) | 108 | #define _Q_PENDING_VAL (1U << _Q_PENDING_OFFSET) |
| 97 | 109 | ||
| 98 | #define likely(x) __builtin_expect(!!(x), 1) | ||
| 99 | #define unlikely(x) __builtin_expect(!!(x), 0) | ||
| 100 | |||
| 101 | struct arena_qnode __arena qnodes[_Q_MAX_CPUS][_Q_MAX_NODES]; | 110 | struct arena_qnode __arena qnodes[_Q_MAX_CPUS][_Q_MAX_NODES]; |
| 102 | 111 | ||
| 103 | static inline u32 encode_tail(int cpu, int idx) | 112 | static inline u32 encode_tail(int cpu, int idx) |
| @@ -225,8 +225,9 @@ · 224 unmodified lines | |||
| 225 | #define CAN_USE_BPF_ST | 225 | #define CAN_USE_BPF_ST |
| 226 | #endif | 226 | #endif |
| 227 | 227 | ||
| 228 | #if __clang_major__ >= 18 && defined(ENABLE_ATOMICS_TESTS) && \ | 228 | #if __clang_major__ >= 18 && defined(ENABLE_ATOMICS_TESTS) && \ |
| 229 | (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86)) | 229 | (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \ |
| 230 | (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64)) | ||
| 230 | #define CAN_USE_LOAD_ACQ_STORE_REL | 231 | #define CAN_USE_LOAD_ACQ_STORE_REL |
| 231 | #endif | 232 | #endif |
| 232 | 233 | ||
| @@ -0,0 +1,101 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright (c) 2025 Google LLC */ | ||
| 3 | #include <vmlinux.h> | ||
| 4 | #include <bpf/bpf_core_read.h> | ||
| 5 | #include <bpf/bpf_helpers.h> | ||
| 6 | |||
| 7 | /* From uapi/linux/dma-buf.h */ | ||
| 8 | #define DMA_BUF_NAME_LEN 32 | ||
| 9 | |||
| 10 | char _license[] SEC("license") = "GPL"; | ||
| 11 | |||
| 12 | struct { | ||
| 13 | __uint(type, BPF_MAP_TYPE_HASH); | ||
| 14 | __uint(key_size, DMA_BUF_NAME_LEN); | ||
| 15 | __type(value, bool); | ||
| 16 | __uint(max_entries, 5); | ||
| 17 | } testbuf_hash SEC(".maps"); | ||
| 18 | |||
| 19 | /* | ||
| 20 | * Fields output by this iterator are delimited by newlines. Convert any | ||
| 21 | * newlines in user-provided printed strings to spaces. | ||
| 22 | */ | ||
| 23 | static void sanitize_string(char *src, size_t size) | ||
| 24 | { | ||
| 25 | for (char *c = src; (size_t)(c - src) < size && *c; ++c) | ||
| 26 | if (*c == '\n') | ||
| 27 | *c = ' '; | ||
| 28 | } | ||
| 29 | |||
| 30 | SEC("iter/dmabuf") | ||
| 31 | int dmabuf_collector(struct bpf_iter__dmabuf *ctx) | ||
| 32 | { | ||
| 33 | const struct dma_buf *dmabuf = ctx->dmabuf; | ||
| 34 | struct seq_file *seq = ctx->meta->seq; | ||
| 35 | unsigned long inode = 0; | ||
| 36 | size_t size; | ||
| 37 | const char *pname, *exporter; | ||
| 38 | char name[DMA_BUF_NAME_LEN] = {'\0'}; | ||
| 39 | |||
| 40 | if (!dmabuf) | ||
| 41 | return 0; | ||
| 42 | |||
| 43 | if (BPF_CORE_READ_INTO(&inode, dmabuf, file, f_inode, i_ino) || | ||
| 44 | bpf_core_read(&size, sizeof(size), &dmabuf->size) || | ||
| 45 | bpf_core_read(&pname, sizeof(pname), &dmabuf->name) || | ||
| 46 | bpf_core_read(&exporter, sizeof(exporter), &dmabuf->exp_name)) | ||
| 47 | return 1; | ||
| 48 | |||
| 49 | /* Buffers are not required to be named */ | ||
| 50 | if (pname) { | ||
| 51 | if (bpf_probe_read_kernel(name, sizeof(name), pname)) | ||
| 52 | return 1; | ||
| 53 | |||
| 54 | /* Name strings can be provided by userspace */ | ||
| 55 | sanitize_string(name, sizeof(name)); | ||
| 56 | } | ||
| 57 | |||
| 58 | BPF_SEQ_PRINTF(seq, "%lu\n%llu\n%s\n%s\n", inode, size, name, exporter); | ||
| 59 | return 0; | ||
| 60 | } | ||
| 61 | |||
| 62 | SEC("syscall") | ||
| 63 | int iter_dmabuf_for_each(const void *ctx) | ||
| 64 | { | ||
| 65 | struct dma_buf *d; | ||
| 66 | |||
| 67 | bpf_for_each(dmabuf, d) { | ||
| 68 | char name[DMA_BUF_NAME_LEN]; | ||
| 69 | const char *pname; | ||
| 70 | bool *found; | ||
| 71 | long len; | ||
| 72 | int i; | ||
| 73 | |||
| 74 | if (bpf_core_read(&pname, sizeof(pname), &d->name)) | ||
| 75 | return 1; | ||
| 76 | |||
| 77 | /* Buffers are not required to be named */ | ||
| 78 | if (!pname) | ||
| 79 | continue; | ||
| 80 | |||
| 81 | len = bpf_probe_read_kernel_str(name, sizeof(name), pname); | ||
| 82 | if (len < 0) | ||
| 83 | return 1; | ||
| 84 | |||
| 85 | /* | ||
| 86 | * The entire name buffer is used as a map key. | ||
| 87 | * Zeroize any uninitialized trailing bytes after the NUL. | ||
| 88 | */ | ||
| 89 | bpf_for(i, len, DMA_BUF_NAME_LEN) | ||
| 90 | name[i] = 0; | ||
| 91 | |||
| 92 | found = bpf_map_lookup_elem(&testbuf_hash, name); | ||
| 93 | if (found) { | ||
| 94 | bool t = true; | ||
| 95 | |||
| 96 | bpf_map_update_elem(&testbuf_hash, name, &t, BPF_EXIST); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | return 0; | ||
| 101 | } | ||
| @@ -680,3 +680,233 @@ out: · 679 unmodified lines | |||
| 680 | bpf_ringbuf_discard_dynptr(&ptr_buf, 0); | 680 | bpf_ringbuf_discard_dynptr(&ptr_buf, 0); |
| 681 | return XDP_DROP; | 681 | return XDP_DROP; |
| 682 | } | 682 | } |
| 683 | |||
| 684 | void *user_ptr; | ||
| 685 | /* Contains the copy of the data pointed by user_ptr. | ||
| 686 | * Size 384 to make it not fit into a single kernel chunk when copying | ||
| 687 | * but less than the maximum bpf stack size (512). | ||
| 688 | */ | ||
| 689 | char expected_str[384]; | ||
| 690 | __u32 test_len[7] = {0/* placeholder */, 0, 1, 2, 255, 256, 257}; | ||
| 691 | |||
| 692 | typedef int (*bpf_read_dynptr_fn_t)(struct bpf_dynptr *dptr, u32 off, | ||
| 693 | u32 size, const void *unsafe_ptr); | ||
| 694 | |||
| 695 | /* Returns the offset just before the end of the maximum sized xdp fragment. | ||
| 696 | * Any write larger than 32 bytes will be split between 2 fragments. | ||
| 697 | */ | ||
| 698 | __u32 xdp_near_frag_end_offset(void) | ||
| 699 | { | ||
| 700 | const __u32 headroom = 256; | ||
| 701 | const __u32 max_frag_size = __PAGE_SIZE - headroom - sizeof(struct skb_shared_info); | ||
| 702 | |||
| 703 | /* 32 bytes before the approximate end of the fragment */ | ||
| 704 | return max_frag_size - 32; | ||
| 705 | } | ||
| 706 | |||
| 707 | /* Use __always_inline on test_dynptr_probe[_str][_xdp]() and callbacks | ||
| 708 | * of type bpf_read_dynptr_fn_t to prevent compiler from generating | ||
| 709 | * indirect calls that make program fail to load with "unknown opcode" error. | ||
| 710 | */ | ||
| 711 | static __always_inline void test_dynptr_probe(void *ptr, bpf_read_dynptr_fn_t bpf_read_dynptr_fn) | ||
| 712 | { | ||
| 713 | char buf[sizeof(expected_str)]; | ||
| 714 | struct bpf_dynptr ptr_buf; | ||
| 715 | int i; | ||
| 716 | |||
| 717 | if (bpf_get_current_pid_tgid() >> 32 != pid) | ||
| 718 | return; | ||
| 719 | |||
| 720 | err = bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(buf), 0, &ptr_buf); | ||
| 721 | |||
| 722 | bpf_for(i, 0, ARRAY_SIZE(test_len)) { | ||
| 723 | __u32 len = test_len[i]; | ||
| 724 | |||
| 725 | err = err ?: bpf_read_dynptr_fn(&ptr_buf, 0, test_len[i], ptr); | ||
| 726 | if (len > sizeof(buf)) | ||
| 727 | break; | ||
| 728 | err = err ?: bpf_dynptr_read(&buf, len, &ptr_buf, 0, 0); | ||
| 729 | |||
| 730 | if (err || bpf_memcmp(expected_str, buf, len)) | ||
| 731 | err = 1; | ||
| 732 | |||
| 733 | /* Reset buffer and dynptr */ | ||
| 734 | __builtin_memset(buf, 0, sizeof(buf)); | ||
| 735 | err = err ?: bpf_dynptr_write(&ptr_buf, 0, buf, len, 0); | ||
| 736 | } | ||
| 737 | bpf_ringbuf_discard_dynptr(&ptr_buf, 0); | ||
| 738 | } | ||
| 739 | |||
| 740 | static __always_inline void test_dynptr_probe_str(void *ptr, | ||
| 741 | bpf_read_dynptr_fn_t bpf_read_dynptr_fn) | ||
| 742 | { | ||
| 743 | char buf[sizeof(expected_str)]; | ||
| 744 | struct bpf_dynptr ptr_buf; | ||
| 745 | __u32 cnt, i; | ||
| 746 | |||
| 747 | if (bpf_get_current_pid_tgid() >> 32 != pid) | ||
| 748 | return; | ||
| 749 | |||
| 750 | bpf_ringbuf_reserve_dynptr(&ringbuf, sizeof(buf), 0, &ptr_buf); | ||
| 751 | |||
| 752 | bpf_for(i, 0, ARRAY_SIZE(test_len)) { | ||
| 753 | __u32 len = test_len[i]; | ||
| 754 | |||
| 755 | cnt = bpf_read_dynptr_fn(&ptr_buf, 0, len, ptr); | ||
| 756 | if (cnt != len) | ||
| 757 | err = 1; | ||
| 758 | |||
| 759 | if (len > sizeof(buf)) | ||
| 760 | continue; | ||
| 761 | err = err ?: bpf_dynptr_read(&buf, len, &ptr_buf, 0, 0); | ||
| 762 | if (!len) | ||
| 763 | continue; | ||
| 764 | if (err || bpf_memcmp(expected_str, buf, len - 1) || buf[len - 1] != '\0') | ||
| 765 | err = 1; | ||
| 766 | } | ||
| 767 | bpf_ringbuf_discard_dynptr(&ptr_buf, 0); | ||
| 768 | } | ||
| 769 | |||
| 770 | static __always_inline void test_dynptr_probe_xdp(struct xdp_md *xdp, void *ptr, | ||
| 771 | bpf_read_dynptr_fn_t bpf_read_dynptr_fn) | ||
| 772 | { | ||
| 773 | struct bpf_dynptr ptr_xdp; | ||
| 774 | char buf[sizeof(expected_str)]; | ||
| 775 | __u32 off, i; | ||
| 776 | |||
| 777 | if (bpf_get_current_pid_tgid() >> 32 != pid) | ||
| 778 | return; | ||
| 779 | |||
| 780 | off = xdp_near_frag_end_offset(); | ||
| 781 | err = bpf_dynptr_from_xdp(xdp, 0, &ptr_xdp); | ||
| 782 | |||
| 783 | bpf_for(i, 0, ARRAY_SIZE(test_len)) { | ||
| 784 | __u32 len = test_len[i]; | ||
| 785 | |||
| 786 | err = err ?: bpf_read_dynptr_fn(&ptr_xdp, off, len, ptr); | ||
| 787 | if (len > sizeof(buf)) | ||
| 788 | continue; | ||
| 789 | err = err ?: bpf_dynptr_read(&buf, len, &ptr_xdp, off, 0); | ||
| 790 | if (err || bpf_memcmp(expected_str, buf, len)) | ||
| 791 | err = 1; | ||
| 792 | /* Reset buffer and dynptr */ | ||
| 793 | __builtin_memset(buf, 0, sizeof(buf)); | ||
| 794 | err = err ?: bpf_dynptr_write(&ptr_xdp, off, buf, len, 0); | ||
| 795 | } | ||
| 796 | } | ||
| 797 | |||
| 798 | static __always_inline void test_dynptr_probe_str_xdp(struct xdp_md *xdp, void *ptr, | ||
| 799 | bpf_read_dynptr_fn_t bpf_read_dynptr_fn) | ||
| 800 | { | ||
| 801 | struct bpf_dynptr ptr_xdp; | ||
| 802 | char buf[sizeof(expected_str)]; | ||
| 803 | __u32 cnt, off, i; | ||
| 804 | |||
| 805 | if (bpf_get_current_pid_tgid() >> 32 != pid) | ||
| 806 | return; | ||
| 807 | |||
| 808 | off = xdp_near_frag_end_offset(); | ||
| 809 | err = bpf_dynptr_from_xdp(xdp, 0, &ptr_xdp); | ||
| 810 | if (err) | ||
| 811 | return; | ||
| 812 | |||
| 813 | bpf_for(i, 0, ARRAY_SIZE(test_len)) { | ||
| 814 | __u32 len = test_len[i]; | ||
| 815 | |||
| 816 | cnt = bpf_read_dynptr_fn(&ptr_xdp, off, len, ptr); | ||
| 817 | if (cnt != len) | ||
| 818 | err = 1; | ||
| 819 | |||
| 820 | if (len > sizeof(buf)) | ||
| 821 | continue; | ||
| 822 | err = err ?: bpf_dynptr_read(&buf, len, &ptr_xdp, off, 0); | ||
| 823 | |||
| 824 | if (!len) | ||
| 825 | continue; | ||
| 826 | if (err || bpf_memcmp(expected_str, buf, len - 1) || buf[len - 1] != '\0') | ||
| 827 | err = 1; | ||
| 828 | |||
| 829 | __builtin_memset(buf, 0, sizeof(buf)); | ||
| 830 | err = err ?: bpf_dynptr_write(&ptr_xdp, off, buf, len, 0); | ||
| 831 | } | ||
| 832 | } | ||
| 833 | |||
| 834 | SEC("xdp") | ||
| 835 | int test_probe_read_user_dynptr(struct xdp_md *xdp) | ||
| 836 | { | ||
| 837 | test_dynptr_probe(user_ptr, bpf_probe_read_user_dynptr); | ||
| 838 | if (!err) | ||
| 839 | test_dynptr_probe_xdp(xdp, user_ptr, bpf_probe_read_user_dynptr); | ||
| 840 | return XDP_PASS; | ||
| 841 | } | ||
| 842 | |||
| 843 | SEC("xdp") | ||
| 844 | int test_probe_read_kernel_dynptr(struct xdp_md *xdp) | ||
| 845 | { | ||
| 846 | test_dynptr_probe(expected_str, bpf_probe_read_kernel_dynptr); | ||
| 847 | if (!err) | ||
| 848 | test_dynptr_probe_xdp(xdp, expected_str, bpf_probe_read_kernel_dynptr); | ||
| 849 | return XDP_PASS; | ||
| 850 | } | ||
| 851 | |||
| 852 | SEC("xdp") | ||
| 853 | int test_probe_read_user_str_dynptr(struct xdp_md *xdp) | ||
| 854 | { | ||
| 855 | test_dynptr_probe_str(user_ptr, bpf_probe_read_user_str_dynptr); | ||
| 856 | if (!err) | ||
| 857 | test_dynptr_probe_str_xdp(xdp, user_ptr, bpf_probe_read_user_str_dynptr); | ||
| 858 | return XDP_PASS; | ||
| 859 | } | ||
| 860 | |||
| 861 | SEC("xdp") | ||
| 862 | int test_probe_read_kernel_str_dynptr(struct xdp_md *xdp) | ||
| 863 | { | ||
| 864 | test_dynptr_probe_str(expected_str, bpf_probe_read_kernel_str_dynptr); | ||
| 865 | if (!err) | ||
| 866 | test_dynptr_probe_str_xdp(xdp, expected_str, bpf_probe_read_kernel_str_dynptr); | ||
| 867 | return XDP_PASS; | ||
| 868 | } | ||
| 869 | |||
| 870 | SEC("fentry.s/" SYS_PREFIX "sys_nanosleep") | ||
| 871 | int test_copy_from_user_dynptr(void *ctx) | ||
| 872 | { | ||
| 873 | test_dynptr_probe(user_ptr, bpf_copy_from_user_dynptr); | ||
| 874 | return 0; | ||
| 875 | } | ||
| 876 | |||
| 877 | SEC("fentry.s/" SYS_PREFIX "sys_nanosleep") | ||
| 878 | int test_copy_from_user_str_dynptr(void *ctx) | ||
| 879 | { | ||
| 880 | test_dynptr_probe_str(user_ptr, bpf_copy_from_user_str_dynptr); | ||
| 881 | return 0; | ||
| 882 | } | ||
| 883 | |||
| 884 | static int bpf_copy_data_from_user_task(struct bpf_dynptr *dptr, u32 off, | ||
| 885 | u32 size, const void *unsafe_ptr) | ||
| 886 | { | ||
| 887 | struct task_struct *task = bpf_get_current_task_btf(); | ||
| 888 | |||
| 889 | return bpf_copy_from_user_task_dynptr(dptr, off, size, unsafe_ptr, task); | ||
| 890 | } | ||
| 891 | |||
| 892 | static int bpf_copy_data_from_user_task_str(struct bpf_dynptr *dptr, u32 off, | ||
| 893 | u32 size, const void *unsafe_ptr) | ||
| 894 | { | ||
| 895 | struct task_struct *task = bpf_get_current_task_btf(); | ||
| 896 | |||
| 897 | return bpf_copy_from_user_task_str_dynptr(dptr, off, size, unsafe_ptr, task); | ||
| 898 | } | ||
| 899 | |||
| 900 | SEC("fentry.s/" SYS_PREFIX "sys_nanosleep") | ||
| 901 | int test_copy_from_user_task_dynptr(void *ctx) | ||
| 902 | { | ||
| 903 | test_dynptr_probe(user_ptr, bpf_copy_data_from_user_task); | ||
| 904 | return 0; | ||
| 905 | } | ||
| 906 | |||
| 907 | SEC("fentry.s/" SYS_PREFIX "sys_nanosleep") | ||
| 908 | int test_copy_from_user_task_str_dynptr(void *ctx) | ||
| 909 | { | ||
| 910 | test_dynptr_probe_str(user_ptr, bpf_copy_data_from_user_task_str); | ||
| 911 | return 0; | ||
| 912 | } | ||
| @@ -0,0 +1,25 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright (C) 2025. Huawei Technologies Co., Ltd */ | ||
| 3 | #include <linux/bpf.h> | ||
| 4 | #include <bpf/bpf_helpers.h> | ||
| 5 | |||
| 6 | char _license[] SEC("license") = "GPL"; | ||
| 7 | |||
| 8 | struct inner_map_type { | ||
| 9 | __uint(type, BPF_MAP_TYPE_ARRAY); | ||
| 10 | __uint(key_size, 4); | ||
| 11 | __uint(value_size, 4); | ||
| 12 | __uint(max_entries, 1); | ||
| 13 | } inner_map SEC(".maps"); | ||
| 14 | |||
| 15 | struct { | ||
| 16 | __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS); | ||
| 17 | __uint(max_entries, 64); | ||
| 18 | __type(key, int); | ||
| 19 | __type(value, int); | ||
| 20 | __array(values, struct inner_map_type); | ||
| 21 | } outer_map SEC(".maps") = { | ||
| 22 | .values = { | ||
| 23 | [0] = &inner_map, | ||
| 24 | }, | ||
| 25 | }; | ||
| @@ -7,8 +7,6 @@ · 6 unmodified lines | |||
| 7 | #include "bpf_misc.h" | 7 | #include "bpf_misc.h" |
| 8 | #include "bpf_compiler.h" | 8 | #include "bpf_compiler.h" |
| 9 | 9 | ||
| 10 | #define unlikely(x) __builtin_expect(!!(x), 0) | ||
| 11 | |||
| 12 | static volatile int zero = 0; | 10 | static volatile int zero = 0; |
| 13 | 11 | ||
| 14 | int my_pid; | 12 | int my_pid; |
| @@ -0,0 +1,113 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ | ||
| 3 | |||
| 4 | #include <vmlinux.h> | ||
| 5 | #include <bpf/bpf_helpers.h> | ||
| 6 | #include "bpf_misc.h" | ||
| 7 | #include "bpf_experimental.h" | ||
| 8 | |||
| 9 | struct node_data { | ||
| 10 | struct bpf_list_node l; | ||
| 11 | int key; | ||
| 12 | }; | ||
| 13 | |||
| 14 | #define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8))) | ||
| 15 | private(A) struct bpf_spin_lock glock; | ||
| 16 | private(A) struct bpf_list_head ghead __contains(node_data, l); | ||
| 17 | |||
| 18 | #define list_entry(ptr, type, member) container_of(ptr, type, member) | ||
| 19 | #define NR_NODES 16 | ||
| 20 | |||
| 21 | int zero = 0; | ||
| 22 | |||
| 23 | SEC("syscall") | ||
| 24 | __retval(0) | ||
| 25 | long list_peek(void *ctx) | ||
| 26 | { | ||
| 27 | struct bpf_list_node *l_n; | ||
| 28 | struct node_data *n; | ||
| 29 | int i, err = 0; | ||
| 30 | |||
| 31 | bpf_spin_lock(&glock); | ||
| 32 | l_n = bpf_list_front(&ghead); | ||
| 33 | bpf_spin_unlock(&glock); | ||
| 34 | if (l_n) | ||
| 35 | return __LINE__; | ||
| 36 | |||
| 37 | bpf_spin_lock(&glock); | ||
| 38 | l_n = bpf_list_back(&ghead); | ||
| 39 | bpf_spin_unlock(&glock); | ||
| 40 | if (l_n) | ||
| 41 | return __LINE__; | ||
| 42 | |||
| 43 | for (i = zero; i < NR_NODES && can_loop; i++) { | ||
| 44 | n = bpf_obj_new(typeof(*n)); | ||
| 45 | if (!n) | ||
| 46 | return __LINE__; | ||
| 47 | n->key = i; | ||
| 48 | bpf_spin_lock(&glock); | ||
| 49 | bpf_list_push_back(&ghead, &n->l); | ||
| 50 | bpf_spin_unlock(&glock); | ||
| 51 | } | ||
| 52 | |||
| 53 | bpf_spin_lock(&glock); | ||
| 54 | |||
| 55 | l_n = bpf_list_front(&ghead); | ||
| 56 | if (!l_n) { | ||
| 57 | err = __LINE__; | ||
| 58 | goto done; | ||
| 59 | } | ||
| 60 | |||
| 61 | n = list_entry(l_n, struct node_data, l); | ||
| 62 | if (n->key != 0) { | ||
| 63 | err = __LINE__; | ||
| 64 | goto done; | ||
| 65 | } | ||
| 66 | |||
| 67 | l_n = bpf_list_back(&ghead); | ||
| 68 | if (!l_n) { | ||
| 69 | err = __LINE__; | ||
| 70 | goto done; | ||
| 71 | } | ||
| 72 | |||
| 73 | n = list_entry(l_n, struct node_data, l); | ||
| 74 | if (n->key != NR_NODES - 1) { | ||
| 75 | err = __LINE__; | ||
| 76 | goto done; | ||
| 77 | } | ||
| 78 | |||
| 79 | done: | ||
| 80 | bpf_spin_unlock(&glock); | ||
| 81 | return err; | ||
| 82 | } | ||
| 83 | |||
| 84 | #define TEST_FB(op, dolock) \ | ||
| 85 | SEC("syscall") \ | ||
| 86 | __failure __msg(MSG) \ | ||
| 87 | long test_##op##_spinlock_##dolock(void *ctx) \ | ||
| 88 | { \ | ||
| 89 | struct bpf_list_node *l_n; \ | ||
| 90 | __u64 jiffies = 0; \ | ||
| 91 | \ | ||
| 92 | if (dolock) \ | ||
| 93 | bpf_spin_lock(&glock); \ | ||
| 94 | l_n = bpf_list_##op(&ghead); \ | ||
| 95 | if (l_n) \ | ||
| 96 | jiffies = bpf_jiffies64(); \ | ||
| 97 | if (dolock) \ | ||
| 98 | bpf_spin_unlock(&glock); \ | ||
| 99 | \ | ||
| 100 | return !!jiffies; \ | ||
| 101 | } | ||
| 102 | |||
| 103 | #define MSG "call bpf_list_{{(front|back).+}}; R0{{(_w)?}}=ptr_or_null_node_data(id={{[0-9]+}},non_own_ref" | ||
| 104 | TEST_FB(front, true) | ||
| 105 | TEST_FB(back, true) | ||
| 106 | #undef MSG | ||
| 107 | |||
| 108 | #define MSG "bpf_spin_lock at off=0 must be held for bpf_list_head" | ||
| 109 | TEST_FB(front, false) | ||
| 110 | TEST_FB(back, false) | ||
| 111 | #undef MSG | ||
| 112 | |||
| 113 | char _license[] SEC("license") = "GPL"; | ||
| @@ -2,7 +2,6 @@ · 1 unmodified line | |||
| 2 | /* Copyright (c) 2025 Meta */ | 2 | /* Copyright (c) 2025 Meta */ |
| 3 | #include <vmlinux.h> | 3 | #include <vmlinux.h> |
| 4 | #include <bpf/bpf_helpers.h> | 4 | #include <bpf/bpf_helpers.h> |
| 5 | //#include <bpf/bpf_tracing.h> | ||
| 6 | 5 | ||
| 7 | char _license[] SEC("license") = "GPL"; | 6 | char _license[] SEC("license") = "GPL"; |
| 8 | 7 | ||
| @@ -69,11 +69,11 @@ long rbtree_api_nolock_first(void *ctx) · 68 unmodified lines | |||
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | SEC("?tc") | 71 | SEC("?tc") |
| 72 | __failure __msg("rbtree_remove node input must be non-owning ref") | 72 | __retval(0) |
| 73 | long rbtree_api_remove_unadded_node(void *ctx) | 73 | long rbtree_api_remove_unadded_node(void *ctx) |
| 74 | { | 74 | { |
| 75 | struct node_data *n, *m; | 75 | struct node_data *n, *m; |
| 76 | struct bpf_rb_node *res; | 76 | struct bpf_rb_node *res_n, *res_m; |
| 77 | 77 | ||
| 78 | n = bpf_obj_new(typeof(*n)); | 78 | n = bpf_obj_new(typeof(*n)); |
| 79 | if (!n) | 79 | if (!n) |
| @@ -88,19 +88,20 @@ long rbtree_api_remove_unadded_node(void *ctx) · 8 unmodified lines | |||
| 88 | bpf_spin_lock(&glock); | 88 | bpf_spin_lock(&glock); |
| 89 | bpf_rbtree_add(&groot, &n->node, less); | 89 | bpf_rbtree_add(&groot, &n->node, less); |
| 90 | 90 | ||
| 91 | /* This remove should pass verifier */ | 91 | res_n = bpf_rbtree_remove(&groot, &n->node); |
| 92 | res = bpf_rbtree_remove(&groot, &n->node); | ||
| 93 | n = container_of(res, struct node_data, node); | ||
| 94 | 92 | ||
| 95 | /* This remove shouldn't, m isn't in an rbtree */ | 93 | res_m = bpf_rbtree_remove(&groot, &m->node); |
| 96 | res = bpf_rbtree_remove(&groot, &m->node); | ||
| 97 | m = container_of(res, struct node_data, node); | ||
| 98 | bpf_spin_unlock(&glock); | 94 | bpf_spin_unlock(&glock); |
| 99 | 95 | ||
| 100 | if (n) | 96 | bpf_obj_drop(m); |
| 101 | bpf_obj_drop(n); | 97 | if (res_n) |
| 102 | if (m) | 98 | bpf_obj_drop(container_of(res_n, struct node_data, node)); |
| 103 | bpf_obj_drop(m); | 99 | if (res_m) { |
| 100 | bpf_obj_drop(container_of(res_m, struct node_data, node)); | ||
| 101 | /* m was not added to the rbtree */ | ||
| 102 | return 2; | ||
| 103 | } | ||
| 104 | |||
| 104 | return 0; | 105 | return 0; |
| 105 | } | 106 | } |
| 106 | 107 | ||
| @@ -178,7 +179,7 @@ err_out: · 71 unmodified lines | |||
| 178 | } | 179 | } |
| 179 | 180 | ||
| 180 | SEC("?tc") | 181 | SEC("?tc") |
| 181 | __failure __msg("rbtree_remove node input must be non-owning ref") | 182 | __failure __msg("bpf_rbtree_remove can only take non-owning or refcounted bpf_rb_node pointer") |
| 182 | long rbtree_api_add_release_unlock_escape(void *ctx) | 183 | long rbtree_api_add_release_unlock_escape(void *ctx) |
| 183 | { | 184 | { |
| 184 | struct node_data *n; | 185 | struct node_data *n; |
| @@ -202,7 +203,7 @@ long rbtree_api_add_release_unlock_escape(void *ctx) · 17 unmodified lines | |||
| 202 | } | 203 | } |
| 203 | 204 | ||
| 204 | SEC("?tc") | 205 | SEC("?tc") |
| 205 | __failure __msg("rbtree_remove node input must be non-owning ref") | 206 | __failure __msg("bpf_rbtree_remove can only take non-owning or refcounted bpf_rb_node pointer") |
| 206 | long rbtree_api_first_release_unlock_escape(void *ctx) | 207 | long rbtree_api_first_release_unlock_escape(void *ctx) |
| 207 | { | 208 | { |
| 208 | struct bpf_rb_node *res; | 209 | struct bpf_rb_node *res; |
| @@ -0,0 +1,206 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ | ||
| 3 | |||
| 4 | #include <vmlinux.h> | ||
| 5 | #include <bpf/bpf_helpers.h> | ||
| 6 | #include "bpf_misc.h" | ||
| 7 | #include "bpf_experimental.h" | ||
| 8 | |||
| 9 | struct node_data { | ||
| 10 | struct bpf_refcount ref; | ||
| 11 | struct bpf_rb_node r0; | ||
| 12 | struct bpf_rb_node r1; | ||
| 13 | int key0; | ||
| 14 | int key1; | ||
| 15 | }; | ||
| 16 | |||
| 17 | #define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8))) | ||
| 18 | private(A) struct bpf_spin_lock glock0; | ||
| 19 | private(A) struct bpf_rb_root groot0 __contains(node_data, r0); | ||
| 20 | |||
| 21 | private(B) struct bpf_spin_lock glock1; | ||
| 22 | private(B) struct bpf_rb_root groot1 __contains(node_data, r1); | ||
| 23 | |||
| 24 | #define rb_entry(ptr, type, member) container_of(ptr, type, member) | ||
| 25 | #define NR_NODES 16 | ||
| 26 | |||
| 27 | int zero = 0; | ||
| 28 | |||
| 29 | static bool less0(struct bpf_rb_node *a, const struct bpf_rb_node *b) | ||
| 30 | { | ||
| 31 | struct node_data *node_a; | ||
| 32 | struct node_data *node_b; | ||
| 33 | |||
| 34 | node_a = rb_entry(a, struct node_data, r0); | ||
| 35 | node_b = rb_entry(b, struct node_data, r0); | ||
| 36 | |||
| 37 | return node_a->key0 < node_b->key0; | ||
| 38 | } | ||
| 39 | |||
| 40 | static bool less1(struct bpf_rb_node *a, const struct bpf_rb_node *b) | ||
| 41 | { | ||
| 42 | struct node_data *node_a; | ||
| 43 | struct node_data *node_b; | ||
| 44 | |||
| 45 | node_a = rb_entry(a, struct node_data, r1); | ||
| 46 | node_b = rb_entry(b, struct node_data, r1); | ||
| 47 | |||
| 48 | return node_a->key1 < node_b->key1; | ||
| 49 | } | ||
| 50 | |||
| 51 | SEC("syscall") | ||
| 52 | __retval(0) | ||
| 53 | long rbtree_search(void *ctx) | ||
| 54 | { | ||
| 55 | struct bpf_rb_node *rb_n, *rb_m, *gc_ns[NR_NODES]; | ||
| 56 | long lookup_key = NR_NODES / 2; | ||
| 57 | struct node_data *n, *m; | ||
| 58 | int i, nr_gc = 0; | ||
| 59 | |||
| 60 | for (i = zero; i < NR_NODES && can_loop; i++) { | ||
| 61 | n = bpf_obj_new(typeof(*n)); | ||
| 62 | if (!n) | ||
| 63 | return __LINE__; | ||
| 64 | |||
| 65 | m = bpf_refcount_acquire(n); | ||
| 66 | |||
| 67 | n->key0 = i; | ||
| 68 | m->key1 = i; | ||
| 69 | |||
| 70 | bpf_spin_lock(&glock0); | ||
| 71 | bpf_rbtree_add(&groot0, &n->r0, less0); | ||
| 72 | bpf_spin_unlock(&glock0); | ||
| 73 | |||
| 74 | bpf_spin_lock(&glock1); | ||
| 75 | bpf_rbtree_add(&groot1, &m->r1, less1); | ||
| 76 | bpf_spin_unlock(&glock1); | ||
| 77 | } | ||
| 78 | |||
| 79 | n = NULL; | ||
| 80 | bpf_spin_lock(&glock0); | ||
| 81 | rb_n = bpf_rbtree_root(&groot0); | ||
| 82 | while (can_loop) { | ||
| 83 | if (!rb_n) { | ||
| 84 | bpf_spin_unlock(&glock0); | ||
| 85 | return __LINE__; | ||
| 86 | } | ||
| 87 | |||
| 88 | n = rb_entry(rb_n, struct node_data, r0); | ||
| 89 | if (lookup_key == n->key0) | ||
| 90 | break; | ||
| 91 | if (nr_gc < NR_NODES) | ||
| 92 | gc_ns[nr_gc++] = rb_n; | ||
| 93 | if (lookup_key < n->key0) | ||
| 94 | rb_n = bpf_rbtree_left(&groot0, rb_n); | ||
| 95 | else | ||
| 96 | rb_n = bpf_rbtree_right(&groot0, rb_n); | ||
| 97 | } | ||
| 98 | |||
| 99 | if (!n || lookup_key != n->key0) { | ||
| 100 | bpf_spin_unlock(&glock0); | ||
| 101 | return __LINE__; | ||
| 102 | } | ||
| 103 | |||
| 104 | for (i = 0; i < nr_gc; i++) { | ||
| 105 | rb_n = gc_ns[i]; | ||
| 106 | gc_ns[i] = bpf_rbtree_remove(&groot0, rb_n); | ||
| 107 | } | ||
| 108 | |||
| 109 | m = bpf_refcount_acquire(n); | ||
| 110 | bpf_spin_unlock(&glock0); | ||
| 111 | |||
| 112 | for (i = 0; i < nr_gc; i++) { | ||
| 113 | rb_n = gc_ns[i]; | ||
| 114 | if (rb_n) { | ||
| 115 | n = rb_entry(rb_n, struct node_data, r0); | ||
| 116 | bpf_obj_drop(n); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | if (!m) | ||
| 121 | return __LINE__; | ||
| 122 | |||
| 123 | bpf_spin_lock(&glock1); | ||
| 124 | rb_m = bpf_rbtree_remove(&groot1, &m->r1); | ||
| 125 | bpf_spin_unlock(&glock1); | ||
| 126 | bpf_obj_drop(m); | ||
| 127 | if (!rb_m) | ||
| 128 | return __LINE__; | ||
| 129 | bpf_obj_drop(rb_entry(rb_m, struct node_data, r1)); | ||
| 130 | |||
| 131 | return 0; | ||
| 132 | } | ||
| 133 | |||
| 134 | #define TEST_ROOT(dolock) \ | ||
| 135 | SEC("syscall") \ | ||
| 136 | __failure __msg(MSG) \ | ||
| 137 | long test_root_spinlock_##dolock(void *ctx) \ | ||
| 138 | { \ | ||
| 139 | struct bpf_rb_node *rb_n; \ | ||
| 140 | __u64 jiffies = 0; \ | ||
| 141 | \ | ||
| 142 | if (dolock) \ | ||
| 143 | bpf_spin_lock(&glock0); \ | ||
| 144 | rb_n = bpf_rbtree_root(&groot0); \ | ||
| 145 | if (rb_n) \ | ||
| 146 | jiffies = bpf_jiffies64(); \ | ||
| 147 | if (dolock) \ | ||
| 148 | bpf_spin_unlock(&glock0); \ | ||
| 149 | \ | ||
| 150 | return !!jiffies; \ | ||
| 151 | } | ||
| 152 | |||
| 153 | #define TEST_LR(op, dolock) \ | ||
| 154 | SEC("syscall") \ | ||
| 155 | __failure __msg(MSG) \ | ||
| 156 | long test_##op##_spinlock_##dolock(void *ctx) \ | ||
| 157 | { \ | ||
| 158 | struct bpf_rb_node *rb_n; \ | ||
| 159 | struct node_data *n; \ | ||
| 160 | __u64 jiffies = 0; \ | ||
| 161 | \ | ||
| 162 | bpf_spin_lock(&glock0); \ | ||
| 163 | rb_n = bpf_rbtree_root(&groot0); \ | ||
| 164 | if (!rb_n) { \ | ||
| 165 | bpf_spin_unlock(&glock0); \ | ||
| 166 | return 1; \ | ||
| 167 | } \ | ||
| 168 | n = rb_entry(rb_n, struct node_data, r0); \ | ||
| 169 | n = bpf_refcount_acquire(n); \ | ||
| 170 | bpf_spin_unlock(&glock0); \ | ||
| 171 | if (!n) \ | ||
| 172 | return 1; \ | ||
| 173 | \ | ||
| 174 | if (dolock) \ | ||
| 175 | bpf_spin_lock(&glock0); \ | ||
| 176 | rb_n = bpf_rbtree_##op(&groot0, &n->r0); \ | ||
| 177 | if (rb_n) \ | ||
| 178 | jiffies = bpf_jiffies64(); \ | ||
| 179 | if (dolock) \ | ||
| 180 | bpf_spin_unlock(&glock0); \ | ||
| 181 | \ | ||
| 182 | return !!jiffies; \ | ||
| 183 | } | ||
| 184 | |||
| 185 | /* | ||
| 186 | * Use a spearate MSG macro instead of passing to TEST_XXX(..., MSG) | ||
| 187 | * to ensure the message itself is not in the bpf prog lineinfo | ||
| 188 | * which the verifier includes in its log. | ||
| 189 | * Otherwise, the test_loader will incorrectly match the prog lineinfo | ||
| 190 | * instead of the log generated by the verifier. | ||
| 191 | */ | ||
| 192 | #define MSG "call bpf_rbtree_root{{.+}}; R0{{(_w)?}}=rcu_ptr_or_null_node_data(id={{[0-9]+}},non_own_ref" | ||
| 193 | TEST_ROOT(true) | ||
| 194 | #undef MSG | ||
| 195 | #define MSG "call bpf_rbtree_{{(left|right).+}}; R0{{(_w)?}}=rcu_ptr_or_null_node_data(id={{[0-9]+}},non_own_ref" | ||
| 196 | TEST_LR(left, true) | ||
| 197 | TEST_LR(right, true) | ||
| 198 | #undef MSG | ||
| 199 | |||
| 200 | #define MSG "bpf_spin_lock at off=0 must be held for bpf_rb_root" | ||
| 201 | TEST_ROOT(false) | ||
| 202 | TEST_LR(left, false) | ||
| 203 | TEST_LR(right, false) | ||
| 204 | #undef MSG | ||
| 205 | |||
| 206 | char _license[] SEC("license") = "GPL"; | ||
| @@ -24,6 +24,44 @@ const volatile enum Enumu64 var_eb = EB1; · 23 unmodified lines | |||
| 24 | const volatile enum Enums64 var_ec = EC1; | 24 | const volatile enum Enums64 var_ec = EC1; |
| 25 | const volatile bool var_b = false; | 25 | const volatile bool var_b = false; |
| 26 | 26 | ||
| 27 | struct Struct { | ||
| 28 | int:16; | ||
| 29 | __u16 filler; | ||
| 30 | struct { | ||
| 31 | const __u16 filler2; | ||
| 32 | }; | ||
| 33 | struct Struct2 { | ||
| 34 | __u16 filler; | ||
| 35 | volatile struct { | ||
| 36 | const int:1; | ||
| 37 | union { | ||
| 38 | const volatile __u8 var_u8; | ||
| 39 | const volatile __s16 filler3; | ||
| 40 | const int:1; | ||
| 41 | } u; | ||
| 42 | }; | ||
| 43 | } struct2; | ||
| 44 | }; | ||
| 45 | |||
| 46 | const volatile __u32 stru = 0; /* same prefix as below */ | ||
| 47 | const volatile struct Struct struct1 = {.struct2 = {.u = {.var_u8 = 1}}}; | ||
| 48 | |||
| 49 | union Union { | ||
| 50 | __u16 var_u16; | ||
| 51 | struct Struct3 { | ||
| 52 | struct { | ||
| 53 | __u8 var_u8_l; | ||
| 54 | }; | ||
| 55 | struct { | ||
| 56 | struct { | ||
| 57 | __u8 var_u8_h; | ||
| 58 | }; | ||
| 59 | }; | ||
| 60 | } struct3; | ||
| 61 | }; | ||
| 62 | |||
| 63 | const volatile union Union union1 = {.var_u16 = -1}; | ||
| 64 | |||
| 27 | char arr[4] = {0}; | 65 | char arr[4] = {0}; |
| 28 | 66 | ||
| 29 | SEC("socket") | 67 | SEC("socket") |
| @@ -43,5 +81,8 @@ int test_set_globals(void *ctx) · 13 unmodified lines | |||
| 43 | a = var_eb; | 81 | a = var_eb; |
| 44 | a = var_ec; | 82 | a = var_ec; |
| 45 | a = var_b; | 83 | a = var_b; |
| 84 | a = struct1.struct2.u.var_u8; | ||
| 85 | a = union1.var_u16; | ||
| 86 | |||
| 46 | return a; | 87 | return a; |
| 47 | } | 88 | } |
| @@ -0,0 +1,22 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* Copyright (c) 2025 Meta Platforms Inc. */ | ||
| 3 | |||
| 4 | #include <linux/bpf.h> | ||
| 5 | #include <bpf/bpf_helpers.h> | ||
| 6 | #include "bpf_misc.h" | ||
| 7 | |||
| 8 | char _license[] SEC("license") = "GPL"; | ||
| 9 | |||
| 10 | __noinline static void f0(void) | ||
| 11 | { | ||
| 12 | __u64 a = 1; | ||
| 13 | |||
| 14 | __sink(a); | ||
| 15 | } | ||
| 16 | |||
| 17 | SEC("xdp") | ||
| 18 | __u64 global_func(struct xdp_md *xdp) | ||
| 19 | { | ||
| 20 | f0(); | ||
| 21 | return XDP_DROP; | ||
| 22 | } | ||
| @@ -0,0 +1,36 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | #include <linux/bpf.h> | ||
| 3 | #include <bpf/bpf_helpers.h> | ||
| 4 | #include <bpf/bpf_endian.h> | ||
| 5 | |||
| 6 | int cork_byte; | ||
| 7 | int push_start; | ||
| 8 | int push_end; | ||
| 9 | int apply_bytes; | ||
| 10 | |||
| 11 | struct { | ||
| 12 | __uint(type, BPF_MAP_TYPE_SOCKMAP); | ||
| 13 | __uint(max_entries, 20); | ||
| 14 | __type(key, int); | ||
| 15 | __type(value, int); | ||
| 16 | } sock_map SEC(".maps"); | ||
| 17 | |||
| 18 | SEC("sk_msg") | ||
| 19 | int prog_sk_policy(struct sk_msg_md *msg) | ||
| 20 | { | ||
| 21 | if (cork_byte > 0) | ||
| 22 | bpf_msg_cork_bytes(msg, cork_byte); | ||
| 23 | if (push_start > 0 && push_end > 0) | ||
| 24 | bpf_msg_push_data(msg, push_start, push_end, 0); | ||
| 25 | |||
| 26 | return SK_PASS; | ||
| 27 | } | ||
| 28 | |||
| 29 | SEC("sk_msg") | ||
| 30 | int prog_sk_policy_redir(struct sk_msg_md *msg) | ||
| 31 | { | ||
| 32 | int two = 2; | ||
| 33 | |||
| 34 | bpf_msg_apply_bytes(msg, apply_bytes); | ||
| 35 | return bpf_msg_redirect_map(msg, &sock_map, two, 0); | ||
| 36 | } | ||
| @@ -0,0 +1,68 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | #include <linux/bpf.h> | ||
| 4 | #include <bpf/bpf_helpers.h> | ||
| 5 | #include "bpf_misc.h" | ||
| 6 | |||
| 7 | SEC(".maps") struct { | ||
| 8 | __uint(type, BPF_MAP_TYPE_SOCKMAP); | ||
| 9 | __uint(max_entries, 1); | ||
| 10 | __type(key, __u32); | ||
| 11 | __type(value, __u64); | ||
| 12 | } nop_map, sock_map; | ||
| 13 | |||
| 14 | SEC(".maps") struct { | ||
| 15 | __uint(type, BPF_MAP_TYPE_SOCKHASH); | ||
| 16 | __uint(max_entries, 1); | ||
| 17 | __type(key, __u32); | ||
| 18 | __type(value, __u64); | ||
| 19 | } nop_hash, sock_hash; | ||
| 20 | |||
| 21 | SEC(".maps") struct { | ||
| 22 | __uint(type, BPF_MAP_TYPE_ARRAY); | ||
| 23 | __uint(max_entries, 2); | ||
| 24 | __type(key, int); | ||
| 25 | __type(value, unsigned int); | ||
| 26 | } verdict_map; | ||
| 27 | |||
| 28 | /* Set by user space */ | ||
| 29 | int redirect_type; | ||
| 30 | int redirect_flags; | ||
| 31 | |||
| 32 | #define redirect_map(__data) \ | ||
| 33 | _Generic((__data), \ | ||
| 34 | struct __sk_buff * : bpf_sk_redirect_map, \ | ||
| 35 | struct sk_msg_md * : bpf_msg_redirect_map \ | ||
| 36 | )((__data), &sock_map, (__u32){0}, redirect_flags) | ||
| 37 | |||
| 38 | #define redirect_hash(__data) \ | ||
| 39 | _Generic((__data), \ | ||
| 40 | struct __sk_buff * : bpf_sk_redirect_hash, \ | ||
| 41 | struct sk_msg_md * : bpf_msg_redirect_hash \ | ||
| 42 | )((__data), &sock_hash, &(__u32){0}, redirect_flags) | ||
| 43 | |||
| 44 | #define DEFINE_PROG(__type, __param) \ | ||
| 45 | SEC("sk_" XSTR(__type)) \ | ||
| 46 | int prog_ ## __type ## _verdict(__param data) \ | ||
| 47 | { \ | ||
| 48 | unsigned int *count; \ | ||
| 49 | int verdict; \ | ||
| 50 | \ | ||
| 51 | if (redirect_type == BPF_MAP_TYPE_SOCKMAP) \ | ||
| 52 | verdict = redirect_map(data); \ | ||
| 53 | else if (redirect_type == BPF_MAP_TYPE_SOCKHASH) \ | ||
| 54 | verdict = redirect_hash(data); \ | ||
| 55 | else \ | ||
| 56 | verdict = redirect_type - __MAX_BPF_MAP_TYPE; \ | ||
| 57 | \ | ||
| 58 | count = bpf_map_lookup_elem(&verdict_map, &verdict); \ | ||
| 59 | if (count) \ | ||
| 60 | (*count)++; \ | ||
| 61 | \ | ||
| 62 | return verdict; \ | ||
| 63 | } | ||
| 64 | |||
| 65 | DEFINE_PROG(skb, struct __sk_buff *); | ||
| 66 | DEFINE_PROG(msg, struct sk_msg_md *); | ||
| 67 | |||
| 68 | char _license[] SEC("license") = "GPL"; | ||
| @@ -294,7 +294,9 @@ static int tcp_validate_sysctl(struct tcp_syncookie *ctx) · 293 unmodified lines | |||
| 294 | (ctx->ipv6 && ctx->attrs.mss != MSS_LOCAL_IPV6)) | 294 | (ctx->ipv6 && ctx->attrs.mss != MSS_LOCAL_IPV6)) |
| 295 | goto err; | 295 | goto err; |
| 296 | 296 | ||
| 297 | if (!ctx->attrs.wscale_ok || ctx->attrs.snd_wscale != 7) | 297 | if (!ctx->attrs.wscale_ok || |
| 298 | !ctx->attrs.snd_wscale || | ||
| 299 | ctx->attrs.snd_wscale >= BPF_SYNCOOKIE_WSCALE_MASK) | ||
| 298 | goto err; | 300 | goto err; |
| 299 | 301 | ||
| 300 | if (!ctx->attrs.tstamp_ok) | 302 | if (!ctx->attrs.tstamp_ok) |
| @@ -0,0 +1,71 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ | ||
| 3 | #include <vmlinux.h> | ||
| 4 | #include <bpf/bpf_helpers.h> | ||
| 5 | #include "bpf_misc.h" | ||
| 6 | |||
| 7 | #if __clang_major__ >= 21 && 0 | ||
| 8 | SEC("socket") | ||
| 9 | __description("__builtin_trap with simple c code") | ||
| 10 | __failure __msg("unexpected __bpf_trap() due to uninitialized variable?") | ||
| 11 | void bpf_builtin_trap_with_simple_c(void) | ||
| 12 | { | ||
| 13 | __builtin_trap(); | ||
| 14 | } | ||
| 15 | #endif | ||
| 16 | |||
| 17 | SEC("socket") | ||
| 18 | __description("__bpf_trap with simple c code") | ||
| 19 | __failure __msg("unexpected __bpf_trap() due to uninitialized variable?") | ||
| 20 | void bpf_trap_with_simple_c(void) | ||
| 21 | { | ||
| 22 | __bpf_trap(); | ||
| 23 | } | ||
| 24 | |||
| 25 | SEC("socket") | ||
| 26 | __description("__bpf_trap as the second-from-last insn") | ||
| 27 | __failure __msg("unexpected __bpf_trap() due to uninitialized variable?") | ||
| 28 | __naked void bpf_trap_at_func_end(void) | ||
| 29 | { | ||
| 30 | asm volatile ( | ||
| 31 | "r0 = 0;" | ||
| 32 | "call %[__bpf_trap];" | ||
| 33 | "exit;" | ||
| 34 | : | ||
| 35 | : __imm(__bpf_trap) | ||
| 36 | : __clobber_all); | ||
| 37 | } | ||
| 38 | |||
| 39 | SEC("socket") | ||
| 40 | __description("dead code __bpf_trap in the middle of code") | ||
| 41 | __success | ||
| 42 | __naked void dead_bpf_trap_in_middle(void) | ||
| 43 | { | ||
| 44 | asm volatile ( | ||
| 45 | "r0 = 0;" | ||
| 46 | "if r0 == 0 goto +1;" | ||
| 47 | "call %[__bpf_trap];" | ||
| 48 | "r0 = 2;" | ||
| 49 | "exit;" | ||
| 50 | : | ||
| 51 | : __imm(__bpf_trap) | ||
| 52 | : __clobber_all); | ||
| 53 | } | ||
| 54 | |||
| 55 | SEC("socket") | ||
| 56 | __description("reachable __bpf_trap in the middle of code") | ||
| 57 | __failure __msg("unexpected __bpf_trap() due to uninitialized variable?") | ||
| 58 | __naked void live_bpf_trap_in_middle(void) | ||
| 59 | { | ||
| 60 | asm volatile ( | ||
| 61 | "r0 = 0;" | ||
| 62 | "if r0 == 1 goto +1;" | ||
| 63 | "call %[__bpf_trap];" | ||
| 64 | "r0 = 2;" | ||
| 65 | "exit;" | ||
| 66 | : | ||
| 67 | : __imm(__bpf_trap) | ||
| 68 | : __clobber_all); | ||
| 69 | } | ||
| 70 | |||
| 71 | char _license[] SEC("license") = "GPL"; | ||
| @@ -65,4 +65,16 @@ __naked void ctx_access_u32_pointer_reject_8(void) · 64 unmodified lines | |||
| 65 | " ::: __clobber_all); | 65 | " ::: __clobber_all); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | SEC("fentry/bpf_fentry_test10") | ||
| 69 | __description("btf_ctx_access const void pointer accept") | ||
| 70 | __success __retval(0) | ||
| 71 | __naked void ctx_access_const_void_pointer_accept(void) | ||
| 72 | { | ||
| 73 | asm volatile (" \ | ||
| 74 | r2 = *(u64 *)(r1 + 0); /* load 1st argument value (const void pointer) */\ | ||
| 75 | r0 = 0; \ | ||
| 76 | exit; \ | ||
| 77 | " ::: __clobber_all); | ||
| 78 | } | ||
| 79 | |||
| 68 | char _license[] SEC("license") = "GPL"; | 80 | char _license[] SEC("license") = "GPL"; |
| @@ -10,65 +10,81 @@ · 9 unmodified lines | |||
| 10 | 10 | ||
| 11 | SEC("socket") | 11 | SEC("socket") |
| 12 | __description("load-acquire, 8-bit") | 12 | __description("load-acquire, 8-bit") |
| 13 | __success __success_unpriv __retval(0x12) | 13 | __success __success_unpriv __retval(0) |
| 14 | __naked void load_acquire_8(void) | 14 | __naked void load_acquire_8(void) |
| 15 | { | 15 | { |
| 16 | asm volatile ( | 16 | asm volatile ( |
| 17 | "w1 = 0x12;" | 17 | "r0 = 0;" |
| 18 | "w1 = 0xfe;" | ||
| 18 | "*(u8 *)(r10 - 1) = w1;" | 19 | "*(u8 *)(r10 - 1) = w1;" |
| 19 | ".8byte %[load_acquire_insn];" // w0 = load_acquire((u8 *)(r10 - 1)); | 20 | ".8byte %[load_acquire_insn];" // w2 = load_acquire((u8 *)(r10 - 1)); |
| 21 | "if r2 == r1 goto 1f;" | ||
| 22 | "r0 = 1;" | ||
| 23 | "1:" | ||
| 20 | "exit;" | 24 | "exit;" |
| 21 | : | 25 | : |
| 22 | : __imm_insn(load_acquire_insn, | 26 | : __imm_insn(load_acquire_insn, |
| 23 | BPF_ATOMIC_OP(BPF_B, BPF_LOAD_ACQ, BPF_REG_0, BPF_REG_10, -1)) | 27 | BPF_ATOMIC_OP(BPF_B, BPF_LOAD_ACQ, BPF_REG_2, BPF_REG_10, -1)) |
| 24 | : __clobber_all); | 28 | : __clobber_all); |
| 25 | } | 29 | } |
| 26 | 30 | ||
| 27 | SEC("socket") | 31 | SEC("socket") |
| 28 | __description("load-acquire, 16-bit") | 32 | __description("load-acquire, 16-bit") |
| 29 | __success __success_unpriv __retval(0x1234) | 33 | __success __success_unpriv __retval(0) |
| 30 | __naked void load_acquire_16(void) | 34 | __naked void load_acquire_16(void) |
| 31 | { | 35 | { |
| 32 | asm volatile ( | 36 | asm volatile ( |
| 33 | "w1 = 0x1234;" | 37 | "r0 = 0;" |
| 38 | "w1 = 0xfedc;" | ||
| 34 | "*(u16 *)(r10 - 2) = w1;" | 39 | "*(u16 *)(r10 - 2) = w1;" |
| 35 | ".8byte %[load_acquire_insn];" // w0 = load_acquire((u16 *)(r10 - 2)); | 40 | ".8byte %[load_acquire_insn];" // w2 = load_acquire((u16 *)(r10 - 2)); |
| 41 | "if r2 == r1 goto 1f;" | ||
| 42 | "r0 = 1;" | ||
| 43 | "1:" | ||
| 36 | "exit;" | 44 | "exit;" |
| 37 | : | 45 | : |
| 38 | : __imm_insn(load_acquire_insn, | 46 | : __imm_insn(load_acquire_insn, |
| 39 | BPF_ATOMIC_OP(BPF_H, BPF_LOAD_ACQ, BPF_REG_0, BPF_REG_10, -2)) | 47 | BPF_ATOMIC_OP(BPF_H, BPF_LOAD_ACQ, BPF_REG_2, BPF_REG_10, -2)) |
| 40 | : __clobber_all); | 48 | : __clobber_all); |
| 41 | } | 49 | } |
| 42 | 50 | ||
| 43 | SEC("socket") | 51 | SEC("socket") |
| 44 | __description("load-acquire, 32-bit") | 52 | __description("load-acquire, 32-bit") |
| 45 | __success __success_unpriv __retval(0x12345678) | 53 | __success __success_unpriv __retval(0) |
| 46 | __naked void load_acquire_32(void) | 54 | __naked void load_acquire_32(void) |
| 47 | { | 55 | { |
| 48 | asm volatile ( | 56 | asm volatile ( |
| 49 | "w1 = 0x12345678;" | 57 | "r0 = 0;" |
| 58 | "w1 = 0xfedcba09;" | ||
| 50 | "*(u32 *)(r10 - 4) = w1;" | 59 | "*(u32 *)(r10 - 4) = w1;" |
| 51 | ".8byte %[load_acquire_insn];" // w0 = load_acquire((u32 *)(r10 - 4)); | 60 | ".8byte %[load_acquire_insn];" // w2 = load_acquire((u32 *)(r10 - 4)); |
| 61 | "if r2 == r1 goto 1f;" | ||
| 62 | "r0 = 1;" | ||
| 63 | "1:" | ||
| 52 | "exit;" | 64 | "exit;" |
| 53 | : | 65 | : |
| 54 | : __imm_insn(load_acquire_insn, | 66 | : __imm_insn(load_acquire_insn, |
| 55 | BPF_ATOMIC_OP(BPF_W, BPF_LOAD_ACQ, BPF_REG_0, BPF_REG_10, -4)) | 67 | BPF_ATOMIC_OP(BPF_W, BPF_LOAD_ACQ, BPF_REG_2, BPF_REG_10, -4)) |
| 56 | : __clobber_all); | 68 | : __clobber_all); |
| 57 | } | 69 | } |
| 58 | 70 | ||
| 59 | SEC("socket") | 71 | SEC("socket") |
| 60 | __description("load-acquire, 64-bit") | 72 | __description("load-acquire, 64-bit") |
| 61 | __success __success_unpriv __retval(0x1234567890abcdef) | 73 | __success __success_unpriv __retval(0) |
| 62 | __naked void load_acquire_64(void) | 74 | __naked void load_acquire_64(void) |
| 63 | { | 75 | { |
| 64 | asm volatile ( | 76 | asm volatile ( |
| 65 | "r1 = 0x1234567890abcdef ll;" | 77 | "r0 = 0;" |
| 78 | "r1 = 0xfedcba0987654321 ll;" | ||
| 66 | "*(u64 *)(r10 - 8) = r1;" | 79 | "*(u64 *)(r10 - 8) = r1;" |
| 67 | ".8byte %[load_acquire_insn];" // r0 = load_acquire((u64 *)(r10 - 8)); | 80 | ".8byte %[load_acquire_insn];" // r2 = load_acquire((u64 *)(r10 - 8)); |
| 81 | "if r2 == r1 goto 1f;" | ||
| 82 | "r0 = 1;" | ||
| 83 | "1:" | ||
| 68 | "exit;" | 84 | "exit;" |
| 69 | : | 85 | : |
| 70 | : __imm_insn(load_acquire_insn, | 86 | : __imm_insn(load_acquire_insn, |
| 71 | BPF_ATOMIC_OP(BPF_DW, BPF_LOAD_ACQ, BPF_REG_0, BPF_REG_10, -8)) | 87 | BPF_ATOMIC_OP(BPF_DW, BPF_LOAD_ACQ, BPF_REG_2, BPF_REG_10, -8)) |
| 72 | : __clobber_all); | 88 | : __clobber_all); |
| 73 | } | 89 | } |
| 74 | 90 | ||
| @@ -91,8 +91,7 @@ __naked int bpf_end_bswap(void) · 90 unmodified lines | |||
| 91 | ::: __clobber_all); | 91 | ::: __clobber_all); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | #if defined(ENABLE_ATOMICS_TESTS) && \ | 94 | #ifdef CAN_USE_LOAD_ACQ_STORE_REL |
| 95 | (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86)) | ||
| 96 | 95 | ||
| 97 | SEC("?raw_tp") | 96 | SEC("?raw_tp") |
| 98 | __success __log_level(2) | 97 | __success __log_level(2) |
| @@ -138,7 +137,7 @@ __naked int bpf_store_release(void) · 39 unmodified lines | |||
| 138 | : __clobber_all); | 137 | : __clobber_all); |
| 139 | } | 138 | } |
| 140 | 139 | ||
| 141 | #endif /* load-acquire, store-release */ | 140 | #endif /* CAN_USE_LOAD_ACQ_STORE_REL */ |
| 142 | #endif /* v4 instruction */ | 141 | #endif /* v4 instruction */ |
| 143 | 142 | ||
| 144 | SEC("?raw_tp") | 143 | SEC("?raw_tp") |
| @@ -179,4 +178,57 @@ __naked int state_loop_first_last_equal(void) · 34 unmodified lines | |||
| 179 | ); | 178 | ); |
| 180 | } | 179 | } |
| 181 | 180 | ||
| 181 | __used __naked static void __bpf_cond_op_r10(void) | ||
| 182 | { | ||
| 183 | asm volatile ( | ||
| 184 | "r2 = 2314885393468386424 ll;" | ||
| 185 | "goto +0;" | ||
| 186 | "if r2 <= r10 goto +3;" | ||
| 187 | "if r1 >= -1835016 goto +0;" | ||
| 188 | "if r2 <= 8 goto +0;" | ||
| 189 | "if r3 <= 0 goto +0;" | ||
| 190 | "exit;" | ||
| 191 | ::: __clobber_all); | ||
| 192 | } | ||
| 193 | |||
| 194 | SEC("?raw_tp") | ||
| 195 | __success __log_level(2) | ||
| 196 | __msg("8: (bd) if r2 <= r10 goto pc+3") | ||
| 197 | __msg("9: (35) if r1 >= 0xffe3fff8 goto pc+0") | ||
| 198 | __msg("10: (b5) if r2 <= 0x8 goto pc+0") | ||
| 199 | __msg("mark_precise: frame1: last_idx 10 first_idx 0 subseq_idx -1") | ||
| 200 | __msg("mark_precise: frame1: regs=r2 stack= before 9: (35) if r1 >= 0xffe3fff8 goto pc+0") | ||
| 201 | __msg("mark_precise: frame1: regs=r2 stack= before 8: (bd) if r2 <= r10 goto pc+3") | ||
| 202 | __msg("mark_precise: frame1: regs=r2 stack= before 7: (05) goto pc+0") | ||
| 203 | __naked void bpf_cond_op_r10(void) | ||
| 204 | { | ||
| 205 | asm volatile ( | ||
| 206 | "r3 = 0 ll;" | ||
| 207 | "call __bpf_cond_op_r10;" | ||
| 208 | "r0 = 0;" | ||
| 209 | "exit;" | ||
| 210 | ::: __clobber_all); | ||
| 211 | } | ||
| 212 | |||
| 213 | SEC("?raw_tp") | ||
| 214 | __success __log_level(2) | ||
| 215 | __msg("3: (bf) r3 = r10") | ||
| 216 | __msg("4: (bd) if r3 <= r2 goto pc+1") | ||
| 217 | __msg("5: (b5) if r2 <= 0x8 goto pc+2") | ||
| 218 | __msg("mark_precise: frame0: last_idx 5 first_idx 0 subseq_idx -1") | ||
| 219 | __msg("mark_precise: frame0: regs=r2 stack= before 4: (bd) if r3 <= r2 goto pc+1") | ||
| 220 | __msg("mark_precise: frame0: regs=r2 stack= before 3: (bf) r3 = r10") | ||
| 221 | __naked void bpf_cond_op_not_r10(void) | ||
| 222 | { | ||
| 223 | asm volatile ( | ||
| 224 | "r0 = 0;" | ||
| 225 | "r2 = 2314885393468386424 ll;" | ||
| 226 | "r3 = r10;" | ||
| 227 | "if r3 <= r2 goto +1;" | ||
| 228 | "if r2 <= 8 goto +2;" | ||
| 229 | "r0 = 2 ll;" | ||
| 230 | "exit;" | ||
| 231 | ::: __clobber_all); | ||
| 232 | } | ||
| 233 | |||
| 182 | char _license[] SEC("license") = "GPL"; | 234 | char _license[] SEC("license") = "GPL"; |
| @@ -6,18 +6,21 @@ · 5 unmodified lines | |||
| 6 | #include "../../../include/linux/filter.h" | 6 | #include "../../../include/linux/filter.h" |
| 7 | #include "bpf_misc.h" | 7 | #include "bpf_misc.h" |
| 8 | 8 | ||
| 9 | #if __clang_major__ >= 18 && defined(ENABLE_ATOMICS_TESTS) && \ | 9 | #ifdef CAN_USE_LOAD_ACQ_STORE_REL |
| 10 | (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86)) | ||
| 11 | 10 | ||
| 12 | SEC("socket") | 11 | SEC("socket") |
| 13 | __description("store-release, 8-bit") | 12 | __description("store-release, 8-bit") |
| 14 | __success __success_unpriv __retval(0x12) | 13 | __success __success_unpriv __retval(0) |
| 15 | __naked void store_release_8(void) | 14 | __naked void store_release_8(void) |
| 16 | { | 15 | { |
| 17 | asm volatile ( | 16 | asm volatile ( |
| 17 | "r0 = 0;" | ||
| 18 | "w1 = 0x12;" | 18 | "w1 = 0x12;" |
| 19 | ".8byte %[store_release_insn];" // store_release((u8 *)(r10 - 1), w1); | 19 | ".8byte %[store_release_insn];" // store_release((u8 *)(r10 - 1), w1); |
| 20 | "w0 = *(u8 *)(r10 - 1);" | 20 | "w2 = *(u8 *)(r10 - 1);" |
| 21 | "if r2 == r1 goto 1f;" | ||
| 22 | "r0 = 1;" | ||
| 23 | "1:" | ||
| 21 | "exit;" | 24 | "exit;" |
| 22 | : | 25 | : |
| 23 | : __imm_insn(store_release_insn, | 26 | : __imm_insn(store_release_insn, |
| @@ -27,13 +30,17 @@ __naked void store_release_8(void) · 3 unmodified lines | |||
| 27 | 30 | ||
| 28 | SEC("socket") | 31 | SEC("socket") |
| 29 | __description("store-release, 16-bit") | 32 | __description("store-release, 16-bit") |
| 30 | __success __success_unpriv __retval(0x1234) | 33 | __success __success_unpriv __retval(0) |
| 31 | __naked void store_release_16(void) | 34 | __naked void store_release_16(void) |
| 32 | { | 35 | { |
| 33 | asm volatile ( | 36 | asm volatile ( |
| 37 | "r0 = 0;" | ||
| 34 | "w1 = 0x1234;" | 38 | "w1 = 0x1234;" |
| 35 | ".8byte %[store_release_insn];" // store_release((u16 *)(r10 - 2), w1); | 39 | ".8byte %[store_release_insn];" // store_release((u16 *)(r10 - 2), w1); |
| 36 | "w0 = *(u16 *)(r10 - 2);" | 40 | "w2 = *(u16 *)(r10 - 2);" |
| 41 | "if r2 == r1 goto 1f;" | ||
| 42 | "r0 = 1;" | ||
| 43 | "1:" | ||
| 37 | "exit;" | 44 | "exit;" |
| 38 | : | 45 | : |
| 39 | : __imm_insn(store_release_insn, | 46 | : __imm_insn(store_release_insn, |
| @@ -43,13 +50,17 @@ __naked void store_release_16(void) · 3 unmodified lines | |||
| 43 | 50 | ||
| 44 | SEC("socket") | 51 | SEC("socket") |
| 45 | __description("store-release, 32-bit") | 52 | __description("store-release, 32-bit") |
| 46 | __success __success_unpriv __retval(0x12345678) | 53 | __success __success_unpriv __retval(0) |
| 47 | __naked void store_release_32(void) | 54 | __naked void store_release_32(void) |
| 48 | { | 55 | { |
| 49 | asm volatile ( | 56 | asm volatile ( |
| 57 | "r0 = 0;" | ||
| 50 | "w1 = 0x12345678;" | 58 | "w1 = 0x12345678;" |
| 51 | ".8byte %[store_release_insn];" // store_release((u32 *)(r10 - 4), w1); | 59 | ".8byte %[store_release_insn];" // store_release((u32 *)(r10 - 4), w1); |
| 52 | "w0 = *(u32 *)(r10 - 4);" | 60 | "w2 = *(u32 *)(r10 - 4);" |
| 61 | "if r2 == r1 goto 1f;" | ||
| 62 | "r0 = 1;" | ||
| 63 | "1:" | ||
| 53 | "exit;" | 64 | "exit;" |
| 54 | : | 65 | : |
| 55 | : __imm_insn(store_release_insn, | 66 | : __imm_insn(store_release_insn, |
| @@ -59,13 +70,17 @@ __naked void store_release_32(void) · 3 unmodified lines | |||
| 59 | 70 | ||
| 60 | SEC("socket") | 71 | SEC("socket") |
| 61 | __description("store-release, 64-bit") | 72 | __description("store-release, 64-bit") |
| 62 | __success __success_unpriv __retval(0x1234567890abcdef) | 73 | __success __success_unpriv __retval(0) |
| 63 | __naked void store_release_64(void) | 74 | __naked void store_release_64(void) |
| 64 | { | 75 | { |
| 65 | asm volatile ( | 76 | asm volatile ( |
| 77 | "r0 = 0;" | ||
| 66 | "r1 = 0x1234567890abcdef ll;" | 78 | "r1 = 0x1234567890abcdef ll;" |
| 67 | ".8byte %[store_release_insn];" // store_release((u64 *)(r10 - 8), r1); | 79 | ".8byte %[store_release_insn];" // store_release((u64 *)(r10 - 8), r1); |
| 68 | "r0 = *(u64 *)(r10 - 8);" | 80 | "r2 = *(u64 *)(r10 - 8);" |
| 81 | "if r2 == r1 goto 1f;" | ||
| 82 | "r0 = 1;" | ||
| 83 | "1:" | ||
| 69 | "exit;" | 84 | "exit;" |
| 70 | : | 85 | : |
| 71 | : __imm_insn(store_release_insn, | 86 | : __imm_insn(store_release_insn, |
| @@ -271,7 +286,7 @@ __naked void store_release_with_invalid_reg(void) · 199 unmodified lines | |||
| 271 | : __clobber_all); | 286 | : __clobber_all); |
| 272 | } | 287 | } |
| 273 | 288 | ||
| 274 | #else | 289 | #else /* CAN_USE_LOAD_ACQ_STORE_REL */ |
| 275 | 290 | ||
| 276 | SEC("socket") | 291 | SEC("socket") |
| 277 | __description("Clang version < 18, ENABLE_ATOMICS_TESTS not defined, and/or JIT doesn't support store-release, use a dummy test") | 292 | __description("Clang version < 18, ENABLE_ATOMICS_TESTS not defined, and/or JIT doesn't support store-release, use a dummy test") |
| @@ -281,6 +296,6 @@ int dummy_test(void) · 3 unmodified lines | |||
| 281 | return 0; | 296 | return 0; |
| 282 | } | 297 | } |
| 283 | 298 | ||
| 284 | #endif | 299 | #endif /* CAN_USE_LOAD_ACQ_STORE_REL */ |
| 285 | 300 | ||
| 286 | char _license[] SEC("license") = "GPL"; | 301 | char _license[] SEC("license") = "GPL"; |
| @@ -19,6 +19,13 @@ struct { · 18 unmodified lines | |||
| 19 | __type(value, __u32); | 19 | __type(value, __u32); |
| 20 | } prog_arr SEC(".maps"); | 20 | } prog_arr SEC(".maps"); |
| 21 | 21 | ||
| 22 | struct { | ||
| 23 | __uint(type, BPF_MAP_TYPE_DEVMAP); | ||
| 24 | __uint(key_size, sizeof(__u32)); | ||
| 25 | __uint(value_size, sizeof(struct bpf_devmap_val)); | ||
| 26 | __uint(max_entries, 1); | ||
| 27 | } dev_map SEC(".maps"); | ||
| 28 | |||
| 22 | extern int bpf_xdp_metadata_rx_timestamp(const struct xdp_md *ctx, | 29 | extern int bpf_xdp_metadata_rx_timestamp(const struct xdp_md *ctx, |
| 23 | __u64 *timestamp) __ksym; | 30 | __u64 *timestamp) __ksym; |
| 24 | extern int bpf_xdp_metadata_rx_hash(const struct xdp_md *ctx, __u32 *hash, | 31 | extern int bpf_xdp_metadata_rx_hash(const struct xdp_md *ctx, __u32 *hash, |
| @@ -95,4 +102,10 @@ int rx(struct xdp_md *ctx) · 70 unmodified lines | |||
| 95 | return bpf_redirect_map(&xsk, ctx->rx_queue_index, XDP_PASS); | 102 | return bpf_redirect_map(&xsk, ctx->rx_queue_index, XDP_PASS); |
| 96 | } | 103 | } |
| 97 | 104 | ||
| 105 | SEC("xdp") | ||
| 106 | int redirect(struct xdp_md *ctx) | ||
| 107 | { | ||
| 108 | return bpf_redirect_map(&dev_map, ctx->rx_queue_index, XDP_PASS); | ||
| 109 | } | ||
| 110 | |||
| 98 | char _license[] SEC("license") = "GPL"; | 111 | char _license[] SEC("license") = "GPL"; |
| @@ -134,6 +134,10 @@ bpf_testmod_test_arg_ptr_to_struct(struct bpf_testmod_struct_arg_1 *a) { · 133 unmodified lines | |||
| 134 | return bpf_testmod_test_struct_arg_result; | 134 | return bpf_testmod_test_struct_arg_result; |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | __weak noinline void bpf_testmod_looooooooooooooooooooooooooooooong_name(void) | ||
| 138 | { | ||
| 139 | } | ||
| 140 | |||
| 137 | __bpf_kfunc void | 141 | __bpf_kfunc void |
| 138 | bpf_testmod_test_mod_kfunc(int i) | 142 | bpf_testmod_test_mod_kfunc(int i) |
| 139 | { | 143 | { |
| @@ -1340,7 +1344,7 @@ static int st_ops_gen_prologue_with_kfunc(struct bpf_insn *insn_buf, bool direct · 1200 unmodified lines | |||
| 1340 | *insn++ = BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_7, offsetof(struct st_ops_args, a)); | 1344 | *insn++ = BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_7, offsetof(struct st_ops_args, a)); |
| 1341 | *insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 2); | 1345 | *insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 2); |
| 1342 | *insn++ = BPF_MOV64_REG(BPF_REG_1, BPF_REG_0); | 1346 | *insn++ = BPF_MOV64_REG(BPF_REG_1, BPF_REG_0); |
| 1343 | *insn++ = BPF_CALL_KFUNC(0, bpf_cgroup_release_id), | 1347 | *insn++ = BPF_CALL_KFUNC(0, bpf_cgroup_release_id); |
| 1344 | *insn++ = BPF_MOV64_REG(BPF_REG_1, BPF_REG_8); | 1348 | *insn++ = BPF_MOV64_REG(BPF_REG_1, BPF_REG_8); |
| 1345 | *insn++ = prog->insnsi[0]; | 1349 | *insn++ = prog->insnsi[0]; |
| 1346 | 1350 | ||
| @@ -1379,7 +1383,7 @@ static int st_ops_gen_epilogue_with_kfunc(struct bpf_insn *insn_buf, const struc · 32 unmodified lines | |||
| 1379 | *insn++ = BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, offsetof(struct st_ops_args, a)); | 1383 | *insn++ = BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, offsetof(struct st_ops_args, a)); |
| 1380 | *insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 2); | 1384 | *insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 2); |
| 1381 | *insn++ = BPF_MOV64_REG(BPF_REG_1, BPF_REG_0); | 1385 | *insn++ = BPF_MOV64_REG(BPF_REG_1, BPF_REG_0); |
| 1382 | *insn++ = BPF_CALL_KFUNC(0, bpf_cgroup_release_id), | 1386 | *insn++ = BPF_CALL_KFUNC(0, bpf_cgroup_release_id); |
| 1383 | *insn++ = BPF_MOV64_REG(BPF_REG_0, BPF_REG_6); | 1387 | *insn++ = BPF_MOV64_REG(BPF_REG_0, BPF_REG_6); |
| 1384 | *insn++ = BPF_ALU64_IMM(BPF_MUL, BPF_REG_0, 2); | 1388 | *insn++ = BPF_ALU64_IMM(BPF_MUL, BPF_REG_0, 2); |
| 1385 | *insn++ = BPF_EXIT_INSN(); | 1389 | *insn++ = BPF_EXIT_INSN(); |
| @@ -1042,6 +1042,14 @@ void run_subtest(struct test_loader *tester, · 1041 unmodified lines | |||
| 1042 | emit_verifier_log(tester->log_buf, false /*force*/); | 1042 | emit_verifier_log(tester->log_buf, false /*force*/); |
| 1043 | validate_msgs(tester->log_buf, &subspec->expect_msgs, emit_verifier_log); | 1043 | validate_msgs(tester->log_buf, &subspec->expect_msgs, emit_verifier_log); |
| 1044 | 1044 | ||
| 1045 | /* Restore capabilities because the kernel will silently ignore requests | ||
| 1046 | * for program info (such as xlated program text) if we are not | ||
| 1047 | * bpf-capable. Also, for some reason test_verifier executes programs | ||
| 1048 | * with all capabilities restored. Do the same here. | ||
| 1049 | */ | ||
| 1050 | if (restore_capabilities(&caps)) | ||
| 1051 | goto tobj_cleanup; | ||
| 1052 | |||
| 1045 | if (subspec->expect_xlated.cnt) { | 1053 | if (subspec->expect_xlated.cnt) { |
| 1046 | err = get_xlated_program_text(bpf_program__fd(tprog), | 1054 | err = get_xlated_program_text(bpf_program__fd(tprog), |
| 1047 | tester->log_buf, tester->log_buf_sz); | 1055 | tester->log_buf, tester->log_buf_sz); |
| @@ -1067,12 +1075,6 @@ void run_subtest(struct test_loader *tester, · 19 unmodified lines | |||
| 1067 | } | 1075 | } |
| 1068 | 1076 | ||
| 1069 | if (should_do_test_run(spec, subspec)) { | 1077 | if (should_do_test_run(spec, subspec)) { |
| 1070 | /* For some reason test_verifier executes programs | ||
| 1071 | * with all capabilities restored. Do the same here. | ||
| 1072 | */ | ||
| 1073 | if (restore_capabilities(&caps)) | ||
| 1074 | goto tobj_cleanup; | ||
| 1075 | |||
| 1076 | /* Do bpf_map__attach_struct_ops() for each struct_ops map. | 1078 | /* Do bpf_map__attach_struct_ops() for each struct_ops map. |
| 1077 | * This should trigger bpf_struct_ops->reg callback on kernel side. | 1079 | * This should trigger bpf_struct_ops->reg callback on kernel side. |
| 1078 | */ | 1080 | */ |
| @@ -734,7 +734,7 @@ static __u32 btf_raw_types[] = { · 733 unmodified lines | |||
| 734 | BTF_MEMBER_ENC(71, 13, 128), /* struct prog_test_member __kptr *ptr; */ | 734 | BTF_MEMBER_ENC(71, 13, 128), /* struct prog_test_member __kptr *ptr; */ |
| 735 | }; | 735 | }; |
| 736 | 736 | ||
| 737 | static char bpf_vlog[UINT_MAX >> 8]; | 737 | static char bpf_vlog[UINT_MAX >> 5]; |
| 738 | 738 | ||
| 739 | static int load_btf_spec(__u32 *types, int types_len, | 739 | static int load_btf_spec(__u32 *types, int types_len, |
| 740 | const char *strings, int strings_len) | 740 | const char *strings, int strings_len) |
| @@ -1559,10 +1559,10 @@ static void do_test_single(struct bpf_test *test, bool unpriv, · 818 unmodified lines | |||
| 1559 | test->errstr_unpriv : test->errstr; | 1559 | test->errstr_unpriv : test->errstr; |
| 1560 | 1560 | ||
| 1561 | opts.expected_attach_type = test->expected_attach_type; | 1561 | opts.expected_attach_type = test->expected_attach_type; |
| 1562 | if (verbose) | 1562 | if (expected_ret == VERBOSE_ACCEPT) |
| 1563 | opts.log_level = verif_log_level | 4; /* force stats */ | ||
| 1564 | else if (expected_ret == VERBOSE_ACCEPT) | ||
| 1565 | opts.log_level = 2; | 1563 | opts.log_level = 2; |
| 1564 | else if (verbose) | ||
| 1565 | opts.log_level = verif_log_level | 4; /* force stats */ | ||
| 1566 | else | 1566 | else |
| 1567 | opts.log_level = DEFAULT_LIBBPF_LOG_LEVEL; | 1567 | opts.log_level = DEFAULT_LIBBPF_LOG_LEVEL; |
| 1568 | opts.prog_flags = pflags; | 1568 | opts.prog_flags = pflags; |
| @@ -1486,7 +1486,84 @@ static bool is_preset_supported(const struct btf_type *t) · 1485 unmodified lines | |||
| 1486 | return btf_is_int(t) || btf_is_enum(t) || btf_is_enum64(t); | 1486 | return btf_is_int(t) || btf_is_enum(t) || btf_is_enum64(t); |
| 1487 | } | 1487 | } |
| 1488 | 1488 | ||
| 1489 | static int set_global_var(struct bpf_object *obj, struct btf *btf, const struct btf_type *t, | 1489 | const int btf_find_member(const struct btf *btf, |
| 1490 | const struct btf_type *parent_type, | ||
| 1491 | __u32 parent_offset, | ||
| 1492 | const char *member_name, | ||
| 1493 | int *member_tid, | ||
| 1494 | __u32 *member_offset) | ||
| 1495 | { | ||
| 1496 | int i; | ||
| 1497 | |||
| 1498 | if (!btf_is_composite(parent_type)) | ||
| 1499 | return -EINVAL; | ||
| 1500 | |||
| 1501 | for (i = 0; i < btf_vlen(parent_type); ++i) { | ||
| 1502 | const struct btf_member *member; | ||
| 1503 | const struct btf_type *member_type; | ||
| 1504 | int tid; | ||
| 1505 | |||
| 1506 | member = btf_members(parent_type) + i; | ||
| 1507 | tid = btf__resolve_type(btf, member->type); | ||
| 1508 | if (tid < 0) | ||
| 1509 | return -EINVAL; | ||
| 1510 | |||
| 1511 | member_type = btf__type_by_id(btf, tid); | ||
| 1512 | if (member->name_off) { | ||
| 1513 | const char *name = btf__name_by_offset(btf, member->name_off); | ||
| 1514 | |||
| 1515 | if (strcmp(member_name, name) == 0) { | ||
| 1516 | if (btf_member_bitfield_size(parent_type, i) != 0) { | ||
| 1517 | fprintf(stderr, "Bitfield presets are not supported %s\n", | ||
| 1518 | name); | ||
| 1519 | return -EINVAL; | ||
| 1520 | } | ||
| 1521 | *member_offset = parent_offset + member->offset; | ||
| 1522 | *member_tid = tid; | ||
| 1523 | return 0; | ||
| 1524 | } | ||
| 1525 | } else if (btf_is_composite(member_type)) { | ||
| 1526 | int err; | ||
| 1527 | |||
| 1528 | err = btf_find_member(btf, member_type, parent_offset + member->offset, | ||
| 1529 | member_name, member_tid, member_offset); | ||
| 1530 | if (!err) | ||
| 1531 | return 0; | ||
| 1532 | } | ||
| 1533 | } | ||
| 1534 | |||
| 1535 | return -EINVAL; | ||
| 1536 | } | ||
| 1537 | |||
| 1538 | static int adjust_var_secinfo(struct btf *btf, const struct btf_type *t, | ||
| 1539 | struct btf_var_secinfo *sinfo, const char *var) | ||
| 1540 | { | ||
| 1541 | char expr[256], *saveptr; | ||
| 1542 | const struct btf_type *base_type, *member_type; | ||
| 1543 | int err, member_tid; | ||
| 1544 | char *name; | ||
| 1545 | __u32 member_offset = 0; | ||
| 1546 | |||
| 1547 | base_type = btf__type_by_id(btf, btf__resolve_type(btf, t->type)); | ||
| 1548 | snprintf(expr, sizeof(expr), "%s", var); | ||
| 1549 | strtok_r(expr, ".", &saveptr); | ||
| 1550 | |||
| 1551 | while ((name = strtok_r(NULL, ".", &saveptr))) { | ||
| 1552 | err = btf_find_member(btf, base_type, 0, name, &member_tid, &member_offset); | ||
| 1553 | if (err) { | ||
| 1554 | fprintf(stderr, "Could not find member %s for variable %s\n", name, var); | ||
| 1555 | return err; | ||
| 1556 | } | ||
| 1557 | member_type = btf__type_by_id(btf, member_tid); | ||
| 1558 | sinfo->offset += member_offset / 8; | ||
| 1559 | sinfo->size = member_type->size; | ||
| 1560 | sinfo->type = member_tid; | ||
| 1561 | base_type = member_type; | ||
| 1562 | } | ||
| 1563 | return 0; | ||
| 1564 | } | ||
| 1565 | |||
| 1566 | static int set_global_var(struct bpf_object *obj, struct btf *btf, | ||
| 1490 | struct bpf_map *map, struct btf_var_secinfo *sinfo, | 1567 | struct bpf_map *map, struct btf_var_secinfo *sinfo, |
| 1491 | struct var_preset *preset) | 1568 | struct var_preset *preset) |
| 1492 | { | 1569 | { |
| @@ -1495,9 +1572,9 @@ static int set_global_var(struct bpf_object *obj, struct btf *btf, const struct · 2 unmodified lines | |||
| 1495 | long long value = preset->ivalue; | 1572 | long long value = preset->ivalue; |
| 1496 | size_t size; | 1573 | size_t size; |
| 1497 | 1574 | ||
| 1498 | base_type = btf__type_by_id(btf, btf__resolve_type(btf, t->type)); | 1575 | base_type = btf__type_by_id(btf, btf__resolve_type(btf, sinfo->type)); |
| 1499 | if (!base_type) { | 1576 | if (!base_type) { |
| 1500 | fprintf(stderr, "Failed to resolve type %d\n", t->type); | 1577 | fprintf(stderr, "Failed to resolve type %d\n", sinfo->type); |
| 1501 | return -EINVAL; | 1578 | return -EINVAL; |
| 1502 | } | 1579 | } |
| 1503 | if (!is_preset_supported(base_type)) { | 1580 | if (!is_preset_supported(base_type)) { |
| @@ -1530,7 +1607,7 @@ static int set_global_var(struct bpf_object *obj, struct btf *btf, const struct · 26 unmodified lines | |||
| 1530 | if (value >= max_val || value < -max_val) { | 1607 | if (value >= max_val || value < -max_val) { |
| 1531 | fprintf(stderr, | 1608 | fprintf(stderr, |
| 1532 | "Variable %s value %lld is out of range [%lld; %lld]\n", | 1609 | "Variable %s value %lld is out of range [%lld; %lld]\n", |
| 1533 | btf__name_by_offset(btf, t->name_off), value, | 1610 | btf__name_by_offset(btf, base_type->name_off), value, |
| 1534 | is_signed ? -max_val : 0, max_val - 1); | 1611 | is_signed ? -max_val : 0, max_val - 1); |
| 1535 | return -EINVAL; | 1612 | return -EINVAL; |
| 1536 | } | 1613 | } |
| @@ -1583,14 +1660,20 @@ static int set_global_vars(struct bpf_object *obj, struct var_preset *presets, i · 46 unmodified lines | |||
| 1583 | for (j = 0; j < n; ++j, ++sinfo) { | 1660 | for (j = 0; j < n; ++j, ++sinfo) { |
| 1584 | const struct btf_type *var_type = btf__type_by_id(btf, sinfo->type); | 1661 | const struct btf_type *var_type = btf__type_by_id(btf, sinfo->type); |
| 1585 | const char *var_name; | 1662 | const char *var_name; |
| 1663 | int var_len; | ||
| 1586 | 1664 | ||
| 1587 | if (!btf_is_var(var_type)) | 1665 | if (!btf_is_var(var_type)) |
| 1588 | continue; | 1666 | continue; |
| 1589 | 1667 | ||
| 1590 | var_name = btf__name_by_offset(btf, var_type->name_off); | 1668 | var_name = btf__name_by_offset(btf, var_type->name_off); |
| 1669 | var_len = strlen(var_name); | ||
| 1591 | 1670 | ||
| 1592 | for (k = 0; k < npresets; ++k) { | 1671 | for (k = 0; k < npresets; ++k) { |
| 1593 | if (strcmp(var_name, presets[k].name) != 0) | 1672 | struct btf_var_secinfo tmp_sinfo; |
| 1673 | |||
| 1674 | if (strncmp(var_name, presets[k].name, var_len) != 0 || | ||
| 1675 | (presets[k].name[var_len] != '\0' && | ||
| 1676 | presets[k].name[var_len] != '.')) | ||
| 1594 | continue; | 1677 | continue; |
| 1595 | 1678 | ||
| 1596 | if (presets[k].applied) { | 1679 | if (presets[k].applied) { |
| @@ -1598,13 +1681,17 @@ static int set_global_vars(struct bpf_object *obj, struct var_preset *presets, i · 1 unmodified line | |||
| 1598 | var_name); | 1681 | var_name); |
| 1599 | return -EINVAL; | 1682 | return -EINVAL; |
| 1600 | } | 1683 | } |
| 1684 | tmp_sinfo = *sinfo; | ||
| 1685 | err = adjust_var_secinfo(btf, var_type, | ||
| 1686 | &tmp_sinfo, presets[k].name); | ||
| 1687 | if (err) | ||
| 1688 | return err; | ||
| 1601 | 1689 | ||
| 1602 | err = set_global_var(obj, btf, var_type, map, sinfo, presets + k); | 1690 | err = set_global_var(obj, btf, map, &tmp_sinfo, presets + k); |
| 1603 | if (err) | 1691 | if (err) |
| 1604 | return err; | 1692 | return err; |
| 1605 | 1693 | ||
| 1606 | presets[k].applied = true; | 1694 | presets[k].applied = true; |
| 1607 | break; | ||
| 1608 | } | 1695 | } |
| 1609 | } | 1696 | } |
| 1610 | } | 1697 | } |