bpf-next-6.17

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+soXsSLHKoYyzcli6rmadz2vbToFAmiINnEACgkQ6rmadz2v
 bToBnA/9F+A3R6rTwGk4HK3xpfc/nm2Tanl3oRN7S2ub/mskDOtWSIyG6cVFZ0UG
 1fK6IkByyRIpAF/5qhdlw8drRXHkQtGLA0lP2L9llm4X1mHLofB18y9OeLrDE1WN
 KwNP06+IGX9W802lCGSIXOY+VmRscVfXSMokyQt2ilHplKjOnDqJcYkWupi3T2rC
 mz79FY9aEl2YrIcpj9RXz+8nwP49pZBuW2P0IM5PAIj4BJBXShrUp8T1nz94okNe
 NFsnAyRxjWpUT0McEgtA9WvpD9lZqujYD8Qp0KlGZWmI3vNpV5d9S1+dBcEb1n7q
 dyNMkTF3oRrJhhg4VqoHc6fVpzSEoZ9ZxV5Hx4cs+ganH75D4YbdGqx/7mR3DUgH
 MZh6rHF1pGnK7TAm7h5gl3ZRAOkZOaahbe1i01NKo9CEe5fSh3AqMyzJYoyGHRKi
 xDN39eQdWBNA+hm1VkbK2Bv93Rbjrka2Kj+D3sSSO9Bo/u3ntcknr7LW39idKz62
 Q8dkKHcCEtun7gjk0YXPF013y81nEohj1C+52BmJ2l5JitM57xfr6YOaQpu7DPDE
 AJbHx6ASxKdyEETecd0b+cXUPQ349zmRXy0+CDMAGKpBicC0H0mHhL14cwOY1Hfu
 EIpIjmIJGI3JNF6T5kybcQGSBOYebdV0FFgwSllzPvuYt7YsHCs=
 =/O3j
 -----END PGP SIGNATURE-----

Merge tag 'bpf-next-6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next

Pull bpf updates from Alexei Starovoitov:

 - Remove usermode driver (UMD) framework (Thomas Weißschuh)

 - Introduce Strongly Connected Component (SCC) in the verifier to
   detect loops and refine register liveness (Eduard Zingerman)

 - Allow 'void *' cast using bpf_rdonly_cast() and corresponding
   '__arg_untrusted' for global function parameters (Eduard Zingerman)

 - Improve precision for BPF_ADD and BPF_SUB operations in the verifier
   (Harishankar Vishwanathan)

 - Teach the verifier that constant pointer to a map cannot be NULL
   (Ihor Solodrai)

 - Introduce BPF streams for error reporting of various conditions
   detected by BPF runtime (Kumar Kartikeya Dwivedi)

 - Teach the verifier to insert runtime speculation barrier (lfence on
   x86) to mitigate speculative execution instead of rejecting the
   programs (Luis Gerhorst)

 - Various improvements for 'veristat' (Mykyta Yatsenko)

 - For CONFIG_DEBUG_KERNEL config warn on internal verifier errors to
   improve bug detection by syzbot (Paul Chaignon)

 - Support BPF private stack on arm64 (Puranjay Mohan)

 - Introduce bpf_cgroup_read_xattr() kfunc to read xattr of cgroup's
   node (Song Liu)

 - Introduce kfuncs for read-only string opreations (Viktor Malik)

 - Implement show_fdinfo() for bpf_links (Tao Chen)

 - Reduce verifier's stack consumption (Yonghong Song)

 - Implement mprog API for cgroup-bpf programs (Yonghong Song)

* tag 'bpf-next-6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (192 commits)
  selftests/bpf: Migrate fexit_noreturns case into tracing_failure test suite
  selftests/bpf: Add selftest for attaching tracing programs to functions in deny list
  bpf: Add log for attaching tracing programs to functions in deny list
  bpf: Show precise rejected function when attaching fexit/fmod_ret to __noreturn functions
  bpf: Fix various typos in verifier.c comments
  bpf: Add third round of bounds deduction
  selftests/bpf: Test invariants on JSLT crossing sign
  selftests/bpf: Test cross-sign 64bits range refinement
  selftests/bpf: Update reg_bound range refinement logic
  bpf: Improve bounds when s64 crosses sign boundary
  bpf: Simplify bounds refinement from s32
  selftests/bpf: Enable private stack tests for arm64
  bpf, arm64: JIT support for private stack
  bpf: Move bpf_jit_get_prog_name() to core.c
  bpf, arm64: Fix fp initialization for exception boundary
  umd: Remove usermode driver framework
  bpf/preload: Don't select USERMODE_DRIVER
  selftests/bpf: Fix test dynptr/test_dynptr_memset_xdp_chunks failure
  selftests/bpf: Fix test dynptr/test_dynptr_copy_xdp failure
  selftests/bpf: Increase xdp data size for arm64 64K page size
  ...
This commit is contained in:
Linus Torvalds 2025-07-30 09:58:50 -07:00
commit d9104cec3e
178 changed files with 9775 additions and 2293 deletions

View file

@ -611,9 +611,10 @@ Q: I have added a new BPF instruction to the kernel, how can I integrate
it into LLVM? it into LLVM?
A: LLVM has a ``-mcpu`` selector for the BPF back end in order to allow A: LLVM has a ``-mcpu`` selector for the BPF back end in order to allow
the selection of BPF instruction set extensions. By default the the selection of BPF instruction set extensions. Before llvm version 20,
``generic`` processor target is used, which is the base instruction set the ``generic`` processor target is used, which is the base instruction
(v1) of BPF. set (v1) of BPF. Since llvm 20, the default processor target has changed
to instruction set v3.
LLVM has an option to select ``-mcpu=probe`` where it will probe the host LLVM has an option to select ``-mcpu=probe`` where it will probe the host
kernel for supported BPF instruction set extensions and selects the kernel for supported BPF instruction set extensions and selects the

View file

@ -350,9 +350,9 @@ Underflow and overflow are allowed during arithmetic operations, meaning
the 64-bit or 32-bit value will wrap. If BPF program execution would the 64-bit or 32-bit value will wrap. If BPF program execution would
result in division by zero, the destination register is instead set to zero. result in division by zero, the destination register is instead set to zero.
Otherwise, for ``ALU64``, if execution would result in ``LLONG_MIN`` Otherwise, for ``ALU64``, if execution would result in ``LLONG_MIN``
dividing -1, the desination register is instead set to ``LLONG_MIN``. For divided by -1, the destination register is instead set to ``LLONG_MIN``. For
``ALU``, if execution would result in ``INT_MIN`` dividing -1, the ``ALU``, if execution would result in ``INT_MIN`` divided by -1, the
desination register is instead set to ``INT_MIN``. destination register is instead set to ``INT_MIN``.
If execution would result in modulo by zero, for ``ALU64`` the value of If execution would result in modulo by zero, for ``ALU64`` the value of
the destination register is unchanged whereas for ``ALU`` the upper the destination register is unchanged whereas for ``ALU`` the upper

View file

@ -325,4 +325,9 @@
#define A64_MRS_SP_EL0(Rt) \ #define A64_MRS_SP_EL0(Rt) \
aarch64_insn_gen_mrs(Rt, AARCH64_INSN_SYSREG_SP_EL0) aarch64_insn_gen_mrs(Rt, AARCH64_INSN_SYSREG_SP_EL0)
/* Barriers */
#define A64_SB aarch64_insn_get_sb_value()
#define A64_DSB_NSH (aarch64_insn_get_dsb_base_value() | 0x7 << 8)
#define A64_ISB aarch64_insn_get_isb_value()
#endif /* _BPF_JIT_H */ #endif /* _BPF_JIT_H */

View file

@ -30,6 +30,7 @@
#define TMP_REG_2 (MAX_BPF_JIT_REG + 1) #define TMP_REG_2 (MAX_BPF_JIT_REG + 1)
#define TCCNT_PTR (MAX_BPF_JIT_REG + 2) #define TCCNT_PTR (MAX_BPF_JIT_REG + 2)
#define TMP_REG_3 (MAX_BPF_JIT_REG + 3) #define TMP_REG_3 (MAX_BPF_JIT_REG + 3)
#define PRIVATE_SP (MAX_BPF_JIT_REG + 4)
#define ARENA_VM_START (MAX_BPF_JIT_REG + 5) #define ARENA_VM_START (MAX_BPF_JIT_REG + 5)
#define check_imm(bits, imm) do { \ #define check_imm(bits, imm) do { \
@ -68,6 +69,8 @@ static const int bpf2a64[] = {
[TCCNT_PTR] = A64_R(26), [TCCNT_PTR] = A64_R(26),
/* temporary register for blinding constants */ /* temporary register for blinding constants */
[BPF_REG_AX] = A64_R(9), [BPF_REG_AX] = A64_R(9),
/* callee saved register for private stack pointer */
[PRIVATE_SP] = A64_R(27),
/* callee saved register for kern_vm_start address */ /* callee saved register for kern_vm_start address */
[ARENA_VM_START] = A64_R(28), [ARENA_VM_START] = A64_R(28),
}; };
@ -86,6 +89,7 @@ struct jit_ctx {
u64 user_vm_start; u64 user_vm_start;
u64 arena_vm_start; u64 arena_vm_start;
bool fp_used; bool fp_used;
bool priv_sp_used;
bool write; bool write;
}; };
@ -98,6 +102,10 @@ struct bpf_plt {
#define PLT_TARGET_SIZE sizeof_field(struct bpf_plt, target) #define PLT_TARGET_SIZE sizeof_field(struct bpf_plt, target)
#define PLT_TARGET_OFFSET offsetof(struct bpf_plt, target) #define PLT_TARGET_OFFSET offsetof(struct bpf_plt, target)
/* Memory size/value to protect private stack overflow/underflow */
#define PRIV_STACK_GUARD_SZ 16
#define PRIV_STACK_GUARD_VAL 0xEB9F12345678eb9fULL
static inline void emit(const u32 insn, struct jit_ctx *ctx) static inline void emit(const u32 insn, struct jit_ctx *ctx)
{ {
if (ctx->image != NULL && ctx->write) if (ctx->image != NULL && ctx->write)
@ -387,8 +395,11 @@ static void find_used_callee_regs(struct jit_ctx *ctx)
if (reg_used & 8) if (reg_used & 8)
ctx->used_callee_reg[i++] = bpf2a64[BPF_REG_9]; ctx->used_callee_reg[i++] = bpf2a64[BPF_REG_9];
if (reg_used & 16) if (reg_used & 16) {
ctx->used_callee_reg[i++] = bpf2a64[BPF_REG_FP]; ctx->used_callee_reg[i++] = bpf2a64[BPF_REG_FP];
if (ctx->priv_sp_used)
ctx->used_callee_reg[i++] = bpf2a64[PRIVATE_SP];
}
if (ctx->arena_vm_start) if (ctx->arena_vm_start)
ctx->used_callee_reg[i++] = bpf2a64[ARENA_VM_START]; ctx->used_callee_reg[i++] = bpf2a64[ARENA_VM_START];
@ -412,6 +423,7 @@ static void push_callee_regs(struct jit_ctx *ctx)
emit(A64_PUSH(A64_R(23), A64_R(24), A64_SP), ctx); emit(A64_PUSH(A64_R(23), A64_R(24), A64_SP), ctx);
emit(A64_PUSH(A64_R(25), A64_R(26), A64_SP), ctx); emit(A64_PUSH(A64_R(25), A64_R(26), A64_SP), ctx);
emit(A64_PUSH(A64_R(27), A64_R(28), A64_SP), ctx); emit(A64_PUSH(A64_R(27), A64_R(28), A64_SP), ctx);
ctx->fp_used = true;
} else { } else {
find_used_callee_regs(ctx); find_used_callee_regs(ctx);
for (i = 0; i + 1 < ctx->nr_used_callee_reg; i += 2) { for (i = 0; i + 1 < ctx->nr_used_callee_reg; i += 2) {
@ -461,6 +473,19 @@ static void pop_callee_regs(struct jit_ctx *ctx)
} }
} }
static void emit_percpu_ptr(const u8 dst_reg, void __percpu *ptr,
struct jit_ctx *ctx)
{
const u8 tmp = bpf2a64[TMP_REG_1];
emit_a64_mov_i64(dst_reg, (__force const u64)ptr, ctx);
if (cpus_have_cap(ARM64_HAS_VIRT_HOST_EXTN))
emit(A64_MRS_TPIDR_EL2(tmp), ctx);
else
emit(A64_MRS_TPIDR_EL1(tmp), ctx);
emit(A64_ADD(1, dst_reg, dst_reg, tmp), ctx);
}
#define BTI_INSNS (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) ? 1 : 0) #define BTI_INSNS (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL) ? 1 : 0)
#define PAC_INSNS (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) ? 1 : 0) #define PAC_INSNS (IS_ENABLED(CONFIG_ARM64_PTR_AUTH_KERNEL) ? 1 : 0)
@ -476,6 +501,8 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
const bool is_main_prog = !bpf_is_subprog(prog); const bool is_main_prog = !bpf_is_subprog(prog);
const u8 fp = bpf2a64[BPF_REG_FP]; const u8 fp = bpf2a64[BPF_REG_FP];
const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; const u8 arena_vm_base = bpf2a64[ARENA_VM_START];
const u8 priv_sp = bpf2a64[PRIVATE_SP];
void __percpu *priv_stack_ptr;
const int idx0 = ctx->idx; const int idx0 = ctx->idx;
int cur_offset; int cur_offset;
@ -551,15 +578,23 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
emit(A64_SUB_I(1, A64_SP, A64_FP, 96), ctx); emit(A64_SUB_I(1, A64_SP, A64_FP, 96), ctx);
} }
if (ctx->fp_used)
/* Set up BPF prog stack base register */
emit(A64_MOV(1, fp, A64_SP), ctx);
/* Stack must be multiples of 16B */ /* Stack must be multiples of 16B */
ctx->stack_size = round_up(prog->aux->stack_depth, 16); ctx->stack_size = round_up(prog->aux->stack_depth, 16);
if (ctx->fp_used) {
if (ctx->priv_sp_used) {
/* Set up private stack pointer */
priv_stack_ptr = prog->aux->priv_stack_ptr + PRIV_STACK_GUARD_SZ;
emit_percpu_ptr(priv_sp, priv_stack_ptr, ctx);
emit(A64_ADD_I(1, fp, priv_sp, ctx->stack_size), ctx);
} else {
/* Set up BPF prog stack base register */
emit(A64_MOV(1, fp, A64_SP), ctx);
}
}
/* Set up function call stack */ /* Set up function call stack */
if (ctx->stack_size) if (ctx->stack_size && !ctx->priv_sp_used)
emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); emit(A64_SUB_I(1, A64_SP, A64_SP, ctx->stack_size), ctx);
if (ctx->arena_vm_start) if (ctx->arena_vm_start)
@ -623,7 +658,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx)
emit(A64_STR64I(tcc, ptr, 0), ctx); emit(A64_STR64I(tcc, ptr, 0), ctx);
/* restore SP */ /* restore SP */
if (ctx->stack_size) if (ctx->stack_size && !ctx->priv_sp_used)
emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx);
pop_callee_regs(ctx); pop_callee_regs(ctx);
@ -991,7 +1026,7 @@ static void build_epilogue(struct jit_ctx *ctx, bool was_classic)
const u8 ptr = bpf2a64[TCCNT_PTR]; const u8 ptr = bpf2a64[TCCNT_PTR];
/* We're done with BPF stack */ /* We're done with BPF stack */
if (ctx->stack_size) if (ctx->stack_size && !ctx->priv_sp_used)
emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx); emit(A64_ADD_I(1, A64_SP, A64_SP, ctx->stack_size), ctx);
pop_callee_regs(ctx); pop_callee_regs(ctx);
@ -1120,6 +1155,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
const u8 tmp2 = bpf2a64[TMP_REG_2]; const u8 tmp2 = bpf2a64[TMP_REG_2];
const u8 fp = bpf2a64[BPF_REG_FP]; const u8 fp = bpf2a64[BPF_REG_FP];
const u8 arena_vm_base = bpf2a64[ARENA_VM_START]; const u8 arena_vm_base = bpf2a64[ARENA_VM_START];
const u8 priv_sp = bpf2a64[PRIVATE_SP];
const s16 off = insn->off; const s16 off = insn->off;
const s32 imm = insn->imm; const s32 imm = insn->imm;
const int i = insn - ctx->prog->insnsi; const int i = insn - ctx->prog->insnsi;
@ -1564,7 +1600,7 @@ emit_cond_jmp:
src = tmp2; src = tmp2;
} }
if (src == fp) { if (src == fp) {
src_adj = A64_SP; src_adj = ctx->priv_sp_used ? priv_sp : A64_SP;
off_adj = off + ctx->stack_size; off_adj = off + ctx->stack_size;
} else { } else {
src_adj = src; src_adj = src;
@ -1630,17 +1666,14 @@ emit_cond_jmp:
return ret; return ret;
break; break;
/* speculation barrier */ /* speculation barrier against v1 and v4 */
case BPF_ST | BPF_NOSPEC: case BPF_ST | BPF_NOSPEC:
/* if (alternative_has_cap_likely(ARM64_HAS_SB)) {
* Nothing required here. emit(A64_SB, ctx);
* } else {
* In case of arm64, we rely on the firmware mitigation of emit(A64_DSB_NSH, ctx);
* Speculative Store Bypass as controlled via the ssbd kernel emit(A64_ISB, ctx);
* parameter. Whenever the mitigation is enabled, it works }
* for all of the kernel code with no need to provide any
* additional instructions.
*/
break; break;
/* ST: *(size *)(dst + off) = imm */ /* ST: *(size *)(dst + off) = imm */
@ -1657,7 +1690,7 @@ emit_cond_jmp:
dst = tmp2; dst = tmp2;
} }
if (dst == fp) { if (dst == fp) {
dst_adj = A64_SP; dst_adj = ctx->priv_sp_used ? priv_sp : A64_SP;
off_adj = off + ctx->stack_size; off_adj = off + ctx->stack_size;
} else { } else {
dst_adj = dst; dst_adj = dst;
@ -1719,7 +1752,7 @@ emit_cond_jmp:
dst = tmp2; dst = tmp2;
} }
if (dst == fp) { if (dst == fp) {
dst_adj = A64_SP; dst_adj = ctx->priv_sp_used ? priv_sp : A64_SP;
off_adj = off + ctx->stack_size; off_adj = off + ctx->stack_size;
} else { } else {
dst_adj = dst; dst_adj = dst;
@ -1862,6 +1895,39 @@ static inline void bpf_flush_icache(void *start, void *end)
flush_icache_range((unsigned long)start, (unsigned long)end); flush_icache_range((unsigned long)start, (unsigned long)end);
} }
static void priv_stack_init_guard(void __percpu *priv_stack_ptr, int alloc_size)
{
int cpu, underflow_idx = (alloc_size - PRIV_STACK_GUARD_SZ) >> 3;
u64 *stack_ptr;
for_each_possible_cpu(cpu) {
stack_ptr = per_cpu_ptr(priv_stack_ptr, cpu);
stack_ptr[0] = PRIV_STACK_GUARD_VAL;
stack_ptr[1] = PRIV_STACK_GUARD_VAL;
stack_ptr[underflow_idx] = PRIV_STACK_GUARD_VAL;
stack_ptr[underflow_idx + 1] = PRIV_STACK_GUARD_VAL;
}
}
static void priv_stack_check_guard(void __percpu *priv_stack_ptr, int alloc_size,
struct bpf_prog *prog)
{
int cpu, underflow_idx = (alloc_size - PRIV_STACK_GUARD_SZ) >> 3;
u64 *stack_ptr;
for_each_possible_cpu(cpu) {
stack_ptr = per_cpu_ptr(priv_stack_ptr, cpu);
if (stack_ptr[0] != PRIV_STACK_GUARD_VAL ||
stack_ptr[1] != PRIV_STACK_GUARD_VAL ||
stack_ptr[underflow_idx] != PRIV_STACK_GUARD_VAL ||
stack_ptr[underflow_idx + 1] != PRIV_STACK_GUARD_VAL) {
pr_err("BPF private stack overflow/underflow detected for prog %sx\n",
bpf_jit_get_prog_name(prog));
break;
}
}
}
struct arm64_jit_data { struct arm64_jit_data {
struct bpf_binary_header *header; struct bpf_binary_header *header;
u8 *ro_image; u8 *ro_image;
@ -1874,9 +1940,11 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
int image_size, prog_size, extable_size, extable_align, extable_offset; int image_size, prog_size, extable_size, extable_align, extable_offset;
struct bpf_prog *tmp, *orig_prog = prog; struct bpf_prog *tmp, *orig_prog = prog;
struct bpf_binary_header *header; struct bpf_binary_header *header;
struct bpf_binary_header *ro_header; struct bpf_binary_header *ro_header = NULL;
struct arm64_jit_data *jit_data; struct arm64_jit_data *jit_data;
void __percpu *priv_stack_ptr = NULL;
bool was_classic = bpf_prog_was_classic(prog); bool was_classic = bpf_prog_was_classic(prog);
int priv_stack_alloc_sz;
bool tmp_blinded = false; bool tmp_blinded = false;
bool extra_pass = false; bool extra_pass = false;
struct jit_ctx ctx; struct jit_ctx ctx;
@ -1908,6 +1976,23 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
} }
prog->aux->jit_data = jit_data; prog->aux->jit_data = jit_data;
} }
priv_stack_ptr = prog->aux->priv_stack_ptr;
if (!priv_stack_ptr && prog->aux->jits_use_priv_stack) {
/* Allocate actual private stack size with verifier-calculated
* stack size plus two memory guards to protect overflow and
* underflow.
*/
priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 16) +
2 * PRIV_STACK_GUARD_SZ;
priv_stack_ptr = __alloc_percpu_gfp(priv_stack_alloc_sz, 16, GFP_KERNEL);
if (!priv_stack_ptr) {
prog = orig_prog;
goto out_priv_stack;
}
priv_stack_init_guard(priv_stack_ptr, priv_stack_alloc_sz);
prog->aux->priv_stack_ptr = priv_stack_ptr;
}
if (jit_data->ctx.offset) { if (jit_data->ctx.offset) {
ctx = jit_data->ctx; ctx = jit_data->ctx;
ro_image_ptr = jit_data->ro_image; ro_image_ptr = jit_data->ro_image;
@ -1931,6 +2016,9 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
ctx.user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena); ctx.user_vm_start = bpf_arena_get_user_vm_start(prog->aux->arena);
ctx.arena_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena); ctx.arena_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena);
if (priv_stack_ptr)
ctx.priv_sp_used = true;
/* Pass 1: Estimate the maximum image size. /* Pass 1: Estimate the maximum image size.
* *
* BPF line info needs ctx->offset[i] to be the offset of * BPF line info needs ctx->offset[i] to be the offset of
@ -2070,7 +2158,12 @@ skip_init_ctx:
ctx.offset[i] *= AARCH64_INSN_SIZE; ctx.offset[i] *= AARCH64_INSN_SIZE;
bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); bpf_prog_fill_jited_linfo(prog, ctx.offset + 1);
out_off: out_off:
if (!ro_header && priv_stack_ptr) {
free_percpu(priv_stack_ptr);
prog->aux->priv_stack_ptr = NULL;
}
kvfree(ctx.offset); kvfree(ctx.offset);
out_priv_stack:
kfree(jit_data); kfree(jit_data);
prog->aux->jit_data = NULL; prog->aux->jit_data = NULL;
} }
@ -2089,6 +2182,11 @@ out_free_hdr:
goto out_off; goto out_off;
} }
bool bpf_jit_supports_private_stack(void)
{
return true;
}
bool bpf_jit_supports_kfunc_call(void) bool bpf_jit_supports_kfunc_call(void)
{ {
return true; return true;
@ -2243,11 +2341,6 @@ static int calc_arg_aux(const struct btf_func_model *m,
/* the rest arguments are passed through stack */ /* the rest arguments are passed through stack */
for (; i < m->nr_args; i++) { for (; i < m->nr_args; i++) {
/* We can not know for sure about exact alignment needs for
* struct passed on stack, so deny those
*/
if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
return -ENOTSUPP;
stack_slots = (m->arg_size[i] + 7) / 8; stack_slots = (m->arg_size[i] + 7) / 8;
a->bstack_for_args += stack_slots * 8; a->bstack_for_args += stack_slots * 8;
a->ostack_for_args = a->ostack_for_args + stack_slots * 8; a->ostack_for_args = a->ostack_for_args + stack_slots * 8;
@ -2911,6 +3004,17 @@ bool bpf_jit_supports_percpu_insn(void)
return true; return true;
} }
bool bpf_jit_bypass_spec_v4(void)
{
/* In case of arm64, we rely on the firmware mitigation of Speculative
* Store Bypass as controlled via the ssbd kernel parameter. Whenever
* the mitigation is enabled, it works for all of the kernel code with
* no need to provide any additional instructions. Therefore, skip
* inserting nospec insns against Spectre v4.
*/
return true;
}
bool bpf_jit_inlines_helper_call(s32 imm) bool bpf_jit_inlines_helper_call(s32 imm)
{ {
switch (imm) { switch (imm) {
@ -2928,6 +3032,8 @@ void bpf_jit_free(struct bpf_prog *prog)
if (prog->jited) { if (prog->jited) {
struct arm64_jit_data *jit_data = prog->aux->jit_data; struct arm64_jit_data *jit_data = prog->aux->jit_data;
struct bpf_binary_header *hdr; struct bpf_binary_header *hdr;
void __percpu *priv_stack_ptr;
int priv_stack_alloc_sz;
/* /*
* If we fail the final pass of JIT (from jit_subprogs), * If we fail the final pass of JIT (from jit_subprogs),
@ -2941,6 +3047,13 @@ void bpf_jit_free(struct bpf_prog *prog)
} }
hdr = bpf_jit_binary_pack_hdr(prog); hdr = bpf_jit_binary_pack_hdr(prog);
bpf_jit_binary_pack_free(hdr, NULL); bpf_jit_binary_pack_free(hdr, NULL);
priv_stack_ptr = prog->aux->priv_stack_ptr;
if (priv_stack_ptr) {
priv_stack_alloc_sz = round_up(prog->aux->stack_depth, 16) +
2 * PRIV_STACK_GUARD_SZ;
priv_stack_check_guard(priv_stack_ptr, priv_stack_alloc_sz, prog);
free_percpu(prog->aux->priv_stack_ptr);
}
WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog)); WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog));
} }

View file

@ -370,6 +370,23 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o
return 0; return 0;
} }
bool bpf_jit_bypass_spec_v1(void)
{
#if defined(CONFIG_PPC_E500) || defined(CONFIG_PPC_BOOK3S_64)
return !(security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
security_ftr_enabled(SEC_FTR_BNDS_CHK_SPEC_BAR));
#else
return true;
#endif
}
bool bpf_jit_bypass_spec_v4(void)
{
return !(security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
security_ftr_enabled(SEC_FTR_STF_BARRIER) &&
stf_barrier_type_get() != STF_BARRIER_NONE);
}
/* /*
* We spill into the redzone always, even if the bpf program has its own stackframe. * We spill into the redzone always, even if the bpf program has its own stackframe.
* Offsets hardcoded based on BPF_PPC_STACK_SAVE -- see bpf_jit_stack_local() * Offsets hardcoded based on BPF_PPC_STACK_SAVE -- see bpf_jit_stack_local()
@ -397,6 +414,7 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
u32 *addrs, int pass, bool extra_pass) u32 *addrs, int pass, bool extra_pass)
{ {
enum stf_barrier_type stf_barrier = stf_barrier_type_get(); enum stf_barrier_type stf_barrier = stf_barrier_type_get();
bool sync_emitted, ori31_emitted;
const struct bpf_insn *insn = fp->insnsi; const struct bpf_insn *insn = fp->insnsi;
int flen = fp->len; int flen = fp->len;
int i, ret; int i, ret;
@ -789,30 +807,51 @@ emit_clear:
/* /*
* BPF_ST NOSPEC (speculation barrier) * BPF_ST NOSPEC (speculation barrier)
*
* The following must act as a barrier against both Spectre v1
* and v4 if we requested both mitigations. Therefore, also emit
* 'isync; sync' on E500 or 'ori31' on BOOK3S_64 in addition to
* the insns needed for a Spectre v4 barrier.
*
* If we requested only !bypass_spec_v1 OR only !bypass_spec_v4,
* we can skip the respective other barrier type as an
* optimization.
*/ */
case BPF_ST | BPF_NOSPEC: case BPF_ST | BPF_NOSPEC:
if (!security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) || sync_emitted = false;
!security_ftr_enabled(SEC_FTR_STF_BARRIER)) ori31_emitted = false;
break; if (IS_ENABLED(CONFIG_PPC_E500) &&
!bpf_jit_bypass_spec_v1()) {
switch (stf_barrier) { EMIT(PPC_RAW_ISYNC());
case STF_BARRIER_EIEIO:
EMIT(PPC_RAW_EIEIO() | 0x02000000);
break;
case STF_BARRIER_SYNC_ORI:
EMIT(PPC_RAW_SYNC()); EMIT(PPC_RAW_SYNC());
EMIT(PPC_RAW_LD(tmp1_reg, _R13, 0)); sync_emitted = true;
EMIT(PPC_RAW_ORI(_R31, _R31, 0));
break;
case STF_BARRIER_FALLBACK:
ctx->seen |= SEEN_FUNC;
PPC_LI64(_R12, dereference_kernel_function_descriptor(bpf_stf_barrier));
EMIT(PPC_RAW_MTCTR(_R12));
EMIT(PPC_RAW_BCTRL());
break;
case STF_BARRIER_NONE:
break;
} }
if (!bpf_jit_bypass_spec_v4()) {
switch (stf_barrier) {
case STF_BARRIER_EIEIO:
EMIT(PPC_RAW_EIEIO() | 0x02000000);
break;
case STF_BARRIER_SYNC_ORI:
if (!sync_emitted)
EMIT(PPC_RAW_SYNC());
EMIT(PPC_RAW_LD(tmp1_reg, _R13, 0));
EMIT(PPC_RAW_ORI(_R31, _R31, 0));
ori31_emitted = true;
break;
case STF_BARRIER_FALLBACK:
ctx->seen |= SEEN_FUNC;
PPC_LI64(_R12, dereference_kernel_function_descriptor(bpf_stf_barrier));
EMIT(PPC_RAW_MTCTR(_R12));
EMIT(PPC_RAW_BCTRL());
break;
case STF_BARRIER_NONE:
break;
}
}
if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) &&
!bpf_jit_bypass_spec_v1() &&
!ori31_emitted)
EMIT(PPC_RAW_ORI(_R31, _R31, 0));
break; break;
/* /*

View file

@ -1,55 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* BPF Jit compiler defines
*
* Copyright IBM Corp. 2012,2015
*
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
* Michael Holzheu <holzheu@linux.vnet.ibm.com>
*/
#ifndef __ARCH_S390_NET_BPF_JIT_H
#define __ARCH_S390_NET_BPF_JIT_H
#ifndef __ASSEMBLER__
#include <linux/filter.h>
#include <linux/types.h>
#endif /* __ASSEMBLER__ */
/*
* Stackframe layout (packed stack):
*
* ^ high
* +---------------+ |
* | old backchain | |
* +---------------+ |
* | r15 - r6 | |
* +---------------+ |
* | 4 byte align | |
* | tail_call_cnt | |
* BFP -> +===============+ |
* | | |
* | BPF stack | |
* | | |
* R15+160 -> +---------------+ |
* | new backchain | |
* R15+152 -> +---------------+ |
* | + 152 byte SA | |
* R15 -> +---------------+ + low
*
* We get 160 bytes stack space from calling function, but only use
* 12 * 8 byte for old backchain, r15..r6, and tail_call_cnt.
*
* The stack size used by the BPF program ("BPF stack" above) is passed
* via "aux->stack_depth".
*/
#define STK_SPACE_ADD (160)
#define STK_160_UNUSED (160 - 12 * 8)
#define STK_OFF (STK_SPACE_ADD - STK_160_UNUSED)
#define STK_OFF_R6 (160 - 11 * 8) /* Offset of r6 on stack */
#define STK_OFF_TCCNT (160 - 12 * 8) /* Offset of tail_call_cnt on stack */
#endif /* __ARCH_S390_NET_BPF_JIT_H */

View file

@ -32,7 +32,6 @@
#include <asm/set_memory.h> #include <asm/set_memory.h>
#include <asm/text-patching.h> #include <asm/text-patching.h>
#include <asm/unwind.h> #include <asm/unwind.h>
#include "bpf_jit.h"
struct bpf_jit { struct bpf_jit {
u32 seen; /* Flags to remember seen eBPF instructions */ u32 seen; /* Flags to remember seen eBPF instructions */
@ -54,6 +53,7 @@ struct bpf_jit {
int prologue_plt; /* Start of prologue hotpatch PLT */ int prologue_plt; /* Start of prologue hotpatch PLT */
int kern_arena; /* Pool offset of kernel arena address */ int kern_arena; /* Pool offset of kernel arena address */
u64 user_arena; /* User arena address */ u64 user_arena; /* User arena address */
u32 frame_off; /* Offset of struct bpf_prog from %r15 */
}; };
#define SEEN_MEM BIT(0) /* use mem[] for temporary storage */ #define SEEN_MEM BIT(0) /* use mem[] for temporary storage */
@ -425,12 +425,26 @@ static void jit_fill_hole(void *area, unsigned int size)
memset(area, 0, size); memset(area, 0, size);
} }
/*
* Caller-allocated part of the frame.
* Thanks to packed stack, its otherwise unused initial part can be used for
* the BPF stack and for the next frame.
*/
struct prog_frame {
u64 unused[8];
/* BPF stack starts here and grows towards 0 */
u32 tail_call_cnt;
u32 pad;
u64 r6[10]; /* r6 - r15 */
u64 backchain;
} __packed;
/* /*
* Save registers from "rs" (register start) to "re" (register end) on stack * Save registers from "rs" (register start) to "re" (register end) on stack
*/ */
static void save_regs(struct bpf_jit *jit, u32 rs, u32 re) static void save_regs(struct bpf_jit *jit, u32 rs, u32 re)
{ {
u32 off = STK_OFF_R6 + (rs - 6) * 8; u32 off = offsetof(struct prog_frame, r6) + (rs - 6) * 8;
if (rs == re) if (rs == re)
/* stg %rs,off(%r15) */ /* stg %rs,off(%r15) */
@ -443,12 +457,9 @@ static void save_regs(struct bpf_jit *jit, u32 rs, u32 re)
/* /*
* Restore registers from "rs" (register start) to "re" (register end) on stack * Restore registers from "rs" (register start) to "re" (register end) on stack
*/ */
static void restore_regs(struct bpf_jit *jit, u32 rs, u32 re, u32 stack_depth) static void restore_regs(struct bpf_jit *jit, u32 rs, u32 re)
{ {
u32 off = STK_OFF_R6 + (rs - 6) * 8; u32 off = jit->frame_off + offsetof(struct prog_frame, r6) + (rs - 6) * 8;
if (jit->seen & SEEN_STACK)
off += STK_OFF + stack_depth;
if (rs == re) if (rs == re)
/* lg %rs,off(%r15) */ /* lg %rs,off(%r15) */
@ -492,8 +503,7 @@ static int get_end(u16 seen_regs, int start)
* Save and restore clobbered registers (6-15) on stack. * Save and restore clobbered registers (6-15) on stack.
* We save/restore registers in chunks with gap >= 2 registers. * We save/restore registers in chunks with gap >= 2 registers.
*/ */
static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth, static void save_restore_regs(struct bpf_jit *jit, int op, u16 extra_regs)
u16 extra_regs)
{ {
u16 seen_regs = jit->seen_regs | extra_regs; u16 seen_regs = jit->seen_regs | extra_regs;
const int last = 15, save_restore_size = 6; const int last = 15, save_restore_size = 6;
@ -516,7 +526,7 @@ static void save_restore_regs(struct bpf_jit *jit, int op, u32 stack_depth,
if (op == REGS_SAVE) if (op == REGS_SAVE)
save_regs(jit, rs, re); save_regs(jit, rs, re);
else else
restore_regs(jit, rs, re, stack_depth); restore_regs(jit, rs, re);
re++; re++;
} while (re <= last); } while (re <= last);
} }
@ -581,11 +591,12 @@ static void bpf_jit_plt(struct bpf_plt *plt, void *ret, void *target)
* Emit function prologue * Emit function prologue
* *
* Save registers and create stack frame if necessary. * Save registers and create stack frame if necessary.
* See stack frame layout description in "bpf_jit.h"! * Stack frame layout is described by struct prog_frame.
*/ */
static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp, static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp)
u32 stack_depth)
{ {
BUILD_BUG_ON(sizeof(struct prog_frame) != STACK_FRAME_OVERHEAD);
/* No-op for hotpatching */ /* No-op for hotpatching */
/* brcl 0,prologue_plt */ /* brcl 0,prologue_plt */
EMIT6_PCREL_RILC(0xc0040000, 0, jit->prologue_plt); EMIT6_PCREL_RILC(0xc0040000, 0, jit->prologue_plt);
@ -593,8 +604,9 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp,
if (!bpf_is_subprog(fp)) { if (!bpf_is_subprog(fp)) {
/* Initialize the tail call counter in the main program. */ /* Initialize the tail call counter in the main program. */
/* xc STK_OFF_TCCNT(4,%r15),STK_OFF_TCCNT(%r15) */ /* xc tail_call_cnt(4,%r15),tail_call_cnt(%r15) */
_EMIT6(0xd703f000 | STK_OFF_TCCNT, 0xf000 | STK_OFF_TCCNT); _EMIT6(0xd703f000 | offsetof(struct prog_frame, tail_call_cnt),
0xf000 | offsetof(struct prog_frame, tail_call_cnt));
} else { } else {
/* /*
* Skip the tail call counter initialization in subprograms. * Skip the tail call counter initialization in subprograms.
@ -617,7 +629,7 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp,
jit->seen_regs |= NVREGS; jit->seen_regs |= NVREGS;
} else { } else {
/* Save registers */ /* Save registers */
save_restore_regs(jit, REGS_SAVE, stack_depth, save_restore_regs(jit, REGS_SAVE,
fp->aux->exception_boundary ? NVREGS : 0); fp->aux->exception_boundary ? NVREGS : 0);
} }
/* Setup literal pool */ /* Setup literal pool */
@ -637,13 +649,15 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp,
if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) { if (is_first_pass(jit) || (jit->seen & SEEN_STACK)) {
/* lgr %w1,%r15 (backchain) */ /* lgr %w1,%r15 (backchain) */
EMIT4(0xb9040000, REG_W1, REG_15); EMIT4(0xb9040000, REG_W1, REG_15);
/* la %bfp,STK_160_UNUSED(%r15) (BPF frame pointer) */ /* la %bfp,unused_end(%r15) (BPF frame pointer) */
EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15, STK_160_UNUSED); EMIT4_DISP(0x41000000, BPF_REG_FP, REG_15,
/* aghi %r15,-STK_OFF */ offsetofend(struct prog_frame, unused));
EMIT4_IMM(0xa70b0000, REG_15, -(STK_OFF + stack_depth)); /* aghi %r15,-frame_off */
/* stg %w1,152(%r15) (backchain) */ EMIT4_IMM(0xa70b0000, REG_15, -jit->frame_off);
/* stg %w1,backchain(%r15) */
EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0, EMIT6_DISP_LH(0xe3000000, 0x0024, REG_W1, REG_0,
REG_15, 152); REG_15,
offsetof(struct prog_frame, backchain));
} }
} }
@ -677,13 +691,13 @@ static void call_r1(struct bpf_jit *jit)
/* /*
* Function epilogue * Function epilogue
*/ */
static void bpf_jit_epilogue(struct bpf_jit *jit, u32 stack_depth) static void bpf_jit_epilogue(struct bpf_jit *jit)
{ {
jit->exit_ip = jit->prg; jit->exit_ip = jit->prg;
/* Load exit code: lgr %r2,%b0 */ /* Load exit code: lgr %r2,%b0 */
EMIT4(0xb9040000, REG_2, BPF_REG_0); EMIT4(0xb9040000, REG_2, BPF_REG_0);
/* Restore registers */ /* Restore registers */
save_restore_regs(jit, REGS_RESTORE, stack_depth, 0); save_restore_regs(jit, REGS_RESTORE, 0);
EMIT_JUMP_REG(14); EMIT_JUMP_REG(14);
jit->prg = ALIGN(jit->prg, 8); jit->prg = ALIGN(jit->prg, 8);
@ -865,7 +879,7 @@ static int sign_extend(struct bpf_jit *jit, int r, u8 size, u8 flags)
* stack space for the large switch statement. * stack space for the large switch statement.
*/ */
static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp, static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
int i, bool extra_pass, u32 stack_depth) int i, bool extra_pass)
{ {
struct bpf_insn *insn = &fp->insnsi[i]; struct bpf_insn *insn = &fp->insnsi[i];
s32 branch_oc_off = insn->off; s32 branch_oc_off = insn->off;
@ -1786,9 +1800,10 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
* Note 2: We assume that the verifier does not let us call the * Note 2: We assume that the verifier does not let us call the
* main program, which clears the tail call counter on entry. * main program, which clears the tail call counter on entry.
*/ */
/* mvc STK_OFF_TCCNT(4,%r15),N(%r15) */ /* mvc tail_call_cnt(4,%r15),frame_off+tail_call_cnt(%r15) */
_EMIT6(0xd203f000 | STK_OFF_TCCNT, _EMIT6(0xd203f000 | offsetof(struct prog_frame, tail_call_cnt),
0xf000 | (STK_OFF_TCCNT + STK_OFF + stack_depth)); 0xf000 | (jit->frame_off +
offsetof(struct prog_frame, tail_call_cnt)));
/* Sign-extend the kfunc arguments. */ /* Sign-extend the kfunc arguments. */
if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
@ -1839,10 +1854,8 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
* goto out; * goto out;
*/ */
if (jit->seen & SEEN_STACK) off = jit->frame_off +
off = STK_OFF_TCCNT + STK_OFF + stack_depth; offsetof(struct prog_frame, tail_call_cnt);
else
off = STK_OFF_TCCNT;
/* lhi %w0,1 */ /* lhi %w0,1 */
EMIT4_IMM(0xa7080000, REG_W0, 1); EMIT4_IMM(0xa7080000, REG_W0, 1);
/* laal %w1,%w0,off(%r15) */ /* laal %w1,%w0,off(%r15) */
@ -1872,7 +1885,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
/* /*
* Restore registers before calling function * Restore registers before calling function
*/ */
save_restore_regs(jit, REGS_RESTORE, stack_depth, 0); save_restore_regs(jit, REGS_RESTORE, 0);
/* /*
* goto *(prog->bpf_func + tail_call_start); * goto *(prog->bpf_func + tail_call_start);
@ -2165,7 +2178,7 @@ static int bpf_set_addr(struct bpf_jit *jit, int i)
* Compile eBPF program into s390x code * Compile eBPF program into s390x code
*/ */
static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp, static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp,
bool extra_pass, u32 stack_depth) bool extra_pass)
{ {
int i, insn_count, lit32_size, lit64_size; int i, insn_count, lit32_size, lit64_size;
u64 kern_arena; u64 kern_arena;
@ -2174,24 +2187,30 @@ static int bpf_jit_prog(struct bpf_jit *jit, struct bpf_prog *fp,
jit->lit64 = jit->lit64_start; jit->lit64 = jit->lit64_start;
jit->prg = 0; jit->prg = 0;
jit->excnt = 0; jit->excnt = 0;
if (is_first_pass(jit) || (jit->seen & SEEN_STACK))
jit->frame_off = sizeof(struct prog_frame) -
offsetofend(struct prog_frame, unused) +
round_up(fp->aux->stack_depth, 8);
else
jit->frame_off = 0;
kern_arena = bpf_arena_get_kern_vm_start(fp->aux->arena); kern_arena = bpf_arena_get_kern_vm_start(fp->aux->arena);
if (kern_arena) if (kern_arena)
jit->kern_arena = _EMIT_CONST_U64(kern_arena); jit->kern_arena = _EMIT_CONST_U64(kern_arena);
jit->user_arena = bpf_arena_get_user_vm_start(fp->aux->arena); jit->user_arena = bpf_arena_get_user_vm_start(fp->aux->arena);
bpf_jit_prologue(jit, fp, stack_depth); bpf_jit_prologue(jit, fp);
if (bpf_set_addr(jit, 0) < 0) if (bpf_set_addr(jit, 0) < 0)
return -1; return -1;
for (i = 0; i < fp->len; i += insn_count) { for (i = 0; i < fp->len; i += insn_count) {
insn_count = bpf_jit_insn(jit, fp, i, extra_pass, stack_depth); insn_count = bpf_jit_insn(jit, fp, i, extra_pass);
if (insn_count < 0) if (insn_count < 0)
return -1; return -1;
/* Next instruction address */ /* Next instruction address */
if (bpf_set_addr(jit, i + insn_count) < 0) if (bpf_set_addr(jit, i + insn_count) < 0)
return -1; return -1;
} }
bpf_jit_epilogue(jit, stack_depth); bpf_jit_epilogue(jit);
lit32_size = jit->lit32 - jit->lit32_start; lit32_size = jit->lit32 - jit->lit32_start;
lit64_size = jit->lit64 - jit->lit64_start; lit64_size = jit->lit64 - jit->lit64_start;
@ -2267,7 +2286,6 @@ static struct bpf_binary_header *bpf_jit_alloc(struct bpf_jit *jit,
*/ */
struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp) struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
{ {
u32 stack_depth = round_up(fp->aux->stack_depth, 8);
struct bpf_prog *tmp, *orig_fp = fp; struct bpf_prog *tmp, *orig_fp = fp;
struct bpf_binary_header *header; struct bpf_binary_header *header;
struct s390_jit_data *jit_data; struct s390_jit_data *jit_data;
@ -2320,7 +2338,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
* - 3: Calculate program size and addrs array * - 3: Calculate program size and addrs array
*/ */
for (pass = 1; pass <= 3; pass++) { for (pass = 1; pass <= 3; pass++) {
if (bpf_jit_prog(&jit, fp, extra_pass, stack_depth)) { if (bpf_jit_prog(&jit, fp, extra_pass)) {
fp = orig_fp; fp = orig_fp;
goto free_addrs; goto free_addrs;
} }
@ -2334,7 +2352,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
goto free_addrs; goto free_addrs;
} }
skip_init_ctx: skip_init_ctx:
if (bpf_jit_prog(&jit, fp, extra_pass, stack_depth)) { if (bpf_jit_prog(&jit, fp, extra_pass)) {
bpf_jit_binary_free(header); bpf_jit_binary_free(header);
fp = orig_fp; fp = orig_fp;
goto free_addrs; goto free_addrs;
@ -2654,9 +2672,10 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
/* stg %r1,backchain_off(%r15) */ /* stg %r1,backchain_off(%r15) */
EMIT6_DISP_LH(0xe3000000, 0x0024, REG_1, REG_0, REG_15, EMIT6_DISP_LH(0xe3000000, 0x0024, REG_1, REG_0, REG_15,
tjit->backchain_off); tjit->backchain_off);
/* mvc tccnt_off(4,%r15),stack_size+STK_OFF_TCCNT(%r15) */ /* mvc tccnt_off(4,%r15),stack_size+tail_call_cnt(%r15) */
_EMIT6(0xd203f000 | tjit->tccnt_off, _EMIT6(0xd203f000 | tjit->tccnt_off,
0xf000 | (tjit->stack_size + STK_OFF_TCCNT)); 0xf000 | (tjit->stack_size +
offsetof(struct prog_frame, tail_call_cnt)));
/* stmg %r2,%rN,fwd_reg_args_off(%r15) */ /* stmg %r2,%rN,fwd_reg_args_off(%r15) */
if (nr_reg_args) if (nr_reg_args)
EMIT6_DISP_LH(0xeb000000, 0x0024, REG_2, EMIT6_DISP_LH(0xeb000000, 0x0024, REG_2,
@ -2793,8 +2812,9 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
(nr_stack_args * sizeof(u64) - 1) << 16 | (nr_stack_args * sizeof(u64) - 1) << 16 |
tjit->stack_args_off, tjit->stack_args_off,
0xf000 | tjit->orig_stack_args_off); 0xf000 | tjit->orig_stack_args_off);
/* mvc STK_OFF_TCCNT(4,%r15),tccnt_off(%r15) */ /* mvc tail_call_cnt(4,%r15),tccnt_off(%r15) */
_EMIT6(0xd203f000 | STK_OFF_TCCNT, 0xf000 | tjit->tccnt_off); _EMIT6(0xd203f000 | offsetof(struct prog_frame, tail_call_cnt),
0xf000 | tjit->tccnt_off);
/* lgr %r1,%r8 */ /* lgr %r1,%r8 */
EMIT4(0xb9040000, REG_1, REG_8); EMIT4(0xb9040000, REG_1, REG_8);
/* %r1() */ /* %r1() */
@ -2851,8 +2871,9 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
if (flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET)) if (flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET))
EMIT6_DISP_LH(0xe3000000, 0x0004, REG_2, REG_0, REG_15, EMIT6_DISP_LH(0xe3000000, 0x0004, REG_2, REG_0, REG_15,
tjit->retval_off); tjit->retval_off);
/* mvc stack_size+STK_OFF_TCCNT(4,%r15),tccnt_off(%r15) */ /* mvc stack_size+tail_call_cnt(4,%r15),tccnt_off(%r15) */
_EMIT6(0xd203f000 | (tjit->stack_size + STK_OFF_TCCNT), _EMIT6(0xd203f000 | (tjit->stack_size +
offsetof(struct prog_frame, tail_call_cnt)),
0xf000 | tjit->tccnt_off); 0xf000 | tjit->tccnt_off);
/* aghi %r15,stack_size */ /* aghi %r15,stack_size */
EMIT4_IMM(0xa70b0000, REG_15, tjit->stack_size); EMIT4_IMM(0xa70b0000, REG_15, tjit->stack_size);

View file

@ -3501,13 +3501,6 @@ int arch_prepare_bpf_dispatcher(void *image, void *buf, s64 *funcs, int num_func
return emit_bpf_dispatcher(&prog, 0, num_funcs - 1, funcs, image, buf); return emit_bpf_dispatcher(&prog, 0, num_funcs - 1, funcs, image, buf);
} }
static const char *bpf_get_prog_name(struct bpf_prog *prog)
{
if (prog->aux->ksym.prog)
return prog->aux->ksym.name;
return prog->aux->name;
}
static void priv_stack_init_guard(void __percpu *priv_stack_ptr, int alloc_size) static void priv_stack_init_guard(void __percpu *priv_stack_ptr, int alloc_size)
{ {
int cpu, underflow_idx = (alloc_size - PRIV_STACK_GUARD_SZ) >> 3; int cpu, underflow_idx = (alloc_size - PRIV_STACK_GUARD_SZ) >> 3;
@ -3531,7 +3524,7 @@ static void priv_stack_check_guard(void __percpu *priv_stack_ptr, int alloc_size
if (stack_ptr[0] != PRIV_STACK_GUARD_VAL || if (stack_ptr[0] != PRIV_STACK_GUARD_VAL ||
stack_ptr[underflow_idx] != PRIV_STACK_GUARD_VAL) { stack_ptr[underflow_idx] != PRIV_STACK_GUARD_VAL) {
pr_err("BPF private stack overflow/underflow detected for prog %sx\n", pr_err("BPF private stack overflow/underflow detected for prog %sx\n",
bpf_get_prog_name(prog)); bpf_jit_get_prog_name(prog));
break; break;
} }
} }
@ -3845,7 +3838,6 @@ void arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp
} }
return; return;
#endif #endif
WARN(1, "verification of programs using bpf_throw should have failed\n");
} }
void bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke, void bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke,

View file

@ -32,7 +32,6 @@ struct netkit {
struct netkit_link { struct netkit_link {
struct bpf_link link; struct bpf_link link;
struct net_device *dev; struct net_device *dev;
u32 location;
}; };
static __always_inline int static __always_inline int
@ -733,8 +732,8 @@ static void netkit_link_fdinfo(const struct bpf_link *link, struct seq_file *seq
seq_printf(seq, "ifindex:\t%u\n", ifindex); seq_printf(seq, "ifindex:\t%u\n", ifindex);
seq_printf(seq, "attach_type:\t%u (%s)\n", seq_printf(seq, "attach_type:\t%u (%s)\n",
nkl->location, link->attach_type,
nkl->location == BPF_NETKIT_PRIMARY ? "primary" : "peer"); link->attach_type == BPF_NETKIT_PRIMARY ? "primary" : "peer");
} }
static int netkit_link_fill_info(const struct bpf_link *link, static int netkit_link_fill_info(const struct bpf_link *link,
@ -749,7 +748,7 @@ static int netkit_link_fill_info(const struct bpf_link *link,
rtnl_unlock(); rtnl_unlock();
info->netkit.ifindex = ifindex; info->netkit.ifindex = ifindex;
info->netkit.attach_type = nkl->location; info->netkit.attach_type = link->attach_type;
return 0; return 0;
} }
@ -775,8 +774,7 @@ static int netkit_link_init(struct netkit_link *nkl,
struct bpf_prog *prog) struct bpf_prog *prog)
{ {
bpf_link_init(&nkl->link, BPF_LINK_TYPE_NETKIT, bpf_link_init(&nkl->link, BPF_LINK_TYPE_NETKIT,
&netkit_link_lops, prog); &netkit_link_lops, prog, attr->link_create.attach_type);
nkl->location = attr->link_create.attach_type;
nkl->dev = dev; nkl->dev = dev;
return bpf_link_prime(&nkl->link, link_primer); return bpf_link_prime(&nkl->link, link_primer);
} }

View file

@ -142,9 +142,9 @@ ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size)
struct kernfs_node *kn = kernfs_dentry_node(dentry); struct kernfs_node *kn = kernfs_dentry_node(dentry);
struct kernfs_iattrs *attrs; struct kernfs_iattrs *attrs;
attrs = kernfs_iattrs(kn); attrs = kernfs_iattrs_noalloc(kn);
if (!attrs) if (!attrs)
return -ENOMEM; return -ENODATA;
return simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size); return simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size);
} }

View file

@ -63,6 +63,7 @@ struct cgroup_bpf {
*/ */
struct hlist_head progs[MAX_CGROUP_BPF_ATTACH_TYPE]; struct hlist_head progs[MAX_CGROUP_BPF_ATTACH_TYPE];
u8 flags[MAX_CGROUP_BPF_ATTACH_TYPE]; u8 flags[MAX_CGROUP_BPF_ATTACH_TYPE];
u64 revisions[MAX_CGROUP_BPF_ATTACH_TYPE];
/* list of cgroup shared storages */ /* list of cgroup shared storages */
struct list_head storages; struct list_head storages;

View file

@ -103,7 +103,6 @@ struct bpf_cgroup_storage {
struct bpf_cgroup_link { struct bpf_cgroup_link {
struct bpf_link link; struct bpf_link link;
struct cgroup *cgroup; struct cgroup *cgroup;
enum bpf_attach_type type;
}; };
struct bpf_prog_list { struct bpf_prog_list {

View file

@ -1538,6 +1538,37 @@ struct btf_mod_pair {
struct bpf_kfunc_desc_tab; struct bpf_kfunc_desc_tab;
enum bpf_stream_id {
BPF_STDOUT = 1,
BPF_STDERR = 2,
};
struct bpf_stream_elem {
struct llist_node node;
int total_len;
int consumed_len;
char str[];
};
enum {
/* 100k bytes */
BPF_STREAM_MAX_CAPACITY = 100000ULL,
};
struct bpf_stream {
atomic_t capacity;
struct llist_head log; /* list of in-flight stream elements in LIFO order */
struct mutex lock; /* lock protecting backlog_{head,tail} */
struct llist_node *backlog_head; /* list of in-flight stream elements in FIFO order */
struct llist_node *backlog_tail; /* tail of the list above */
};
struct bpf_stream_stage {
struct llist_head log;
int len;
};
struct bpf_prog_aux { struct bpf_prog_aux {
atomic64_t refcnt; atomic64_t refcnt;
u32 used_map_cnt; u32 used_map_cnt;
@ -1646,6 +1677,7 @@ struct bpf_prog_aux {
struct work_struct work; struct work_struct work;
struct rcu_head rcu; struct rcu_head rcu;
}; };
struct bpf_stream stream[2];
}; };
struct bpf_prog { struct bpf_prog {
@ -1697,11 +1729,10 @@ struct bpf_link {
enum bpf_link_type type; enum bpf_link_type type;
const struct bpf_link_ops *ops; const struct bpf_link_ops *ops;
struct bpf_prog *prog; struct bpf_prog *prog;
/* whether BPF link itself has "sleepable" semantics, which can differ
* from underlying BPF program having a "sleepable" semantics, as BPF u32 flags;
* link's semantics is determined by target attach hook enum bpf_attach_type attach_type;
*/
bool sleepable;
/* rcu is used before freeing, work can be used to schedule that /* rcu is used before freeing, work can be used to schedule that
* RCU-based freeing before that, so they never overlap * RCU-based freeing before that, so they never overlap
*/ */
@ -1709,6 +1740,11 @@ struct bpf_link {
struct rcu_head rcu; struct rcu_head rcu;
struct work_struct work; struct work_struct work;
}; };
/* whether BPF link itself has "sleepable" semantics, which can differ
* from underlying BPF program having a "sleepable" semantics, as BPF
* link's semantics is determined by target attach hook
*/
bool sleepable;
}; };
struct bpf_link_ops { struct bpf_link_ops {
@ -1748,7 +1784,6 @@ struct bpf_shim_tramp_link {
struct bpf_tracing_link { struct bpf_tracing_link {
struct bpf_tramp_link link; struct bpf_tramp_link link;
enum bpf_attach_type attach_type;
struct bpf_trampoline *trampoline; struct bpf_trampoline *trampoline;
struct bpf_prog *tgt_prog; struct bpf_prog *tgt_prog;
}; };
@ -2001,11 +2036,13 @@ int bpf_prog_ctx_arg_info_init(struct bpf_prog *prog,
#if defined(CONFIG_CGROUP_BPF) && defined(CONFIG_BPF_LSM) #if defined(CONFIG_CGROUP_BPF) && defined(CONFIG_BPF_LSM)
int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog, int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog,
int cgroup_atype); int cgroup_atype,
enum bpf_attach_type attach_type);
void bpf_trampoline_unlink_cgroup_shim(struct bpf_prog *prog); void bpf_trampoline_unlink_cgroup_shim(struct bpf_prog *prog);
#else #else
static inline int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog, static inline int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog,
int cgroup_atype) int cgroup_atype,
enum bpf_attach_type attach_type)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
@ -2288,6 +2325,9 @@ bpf_prog_run_array_uprobe(const struct bpf_prog_array *array,
return ret; return ret;
} }
bool bpf_jit_bypass_spec_v1(void);
bool bpf_jit_bypass_spec_v4(void);
#ifdef CONFIG_BPF_SYSCALL #ifdef CONFIG_BPF_SYSCALL
DECLARE_PER_CPU(int, bpf_prog_active); DECLARE_PER_CPU(int, bpf_prog_active);
extern struct mutex bpf_stats_enabled_mutex; extern struct mutex bpf_stats_enabled_mutex;
@ -2314,6 +2354,7 @@ extern const struct super_operations bpf_super_ops;
extern const struct file_operations bpf_map_fops; extern const struct file_operations bpf_map_fops;
extern const struct file_operations bpf_prog_fops; extern const struct file_operations bpf_prog_fops;
extern const struct file_operations bpf_iter_fops; extern const struct file_operations bpf_iter_fops;
extern const struct file_operations bpf_token_fops;
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \ #define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
extern const struct bpf_prog_ops _name ## _prog_ops; \ extern const struct bpf_prog_ops _name ## _prog_ops; \
@ -2405,6 +2446,7 @@ int generic_map_delete_batch(struct bpf_map *map,
struct bpf_map *bpf_map_get_curr_or_next(u32 *id); struct bpf_map *bpf_map_get_curr_or_next(u32 *id);
struct bpf_prog *bpf_prog_get_curr_or_next(u32 *id); struct bpf_prog *bpf_prog_get_curr_or_next(u32 *id);
int bpf_map_alloc_pages(const struct bpf_map *map, int nid, int bpf_map_alloc_pages(const struct bpf_map *map, int nid,
unsigned long nr_pages, struct page **page_array); unsigned long nr_pages, struct page **page_array);
#ifdef CONFIG_MEMCG #ifdef CONFIG_MEMCG
@ -2475,22 +2517,27 @@ static inline bool bpf_allow_uninit_stack(const struct bpf_token *token)
static inline bool bpf_bypass_spec_v1(const struct bpf_token *token) static inline bool bpf_bypass_spec_v1(const struct bpf_token *token)
{ {
return cpu_mitigations_off() || bpf_token_capable(token, CAP_PERFMON); return bpf_jit_bypass_spec_v1() ||
cpu_mitigations_off() ||
bpf_token_capable(token, CAP_PERFMON);
} }
static inline bool bpf_bypass_spec_v4(const struct bpf_token *token) static inline bool bpf_bypass_spec_v4(const struct bpf_token *token)
{ {
return cpu_mitigations_off() || bpf_token_capable(token, CAP_PERFMON); return bpf_jit_bypass_spec_v4() ||
cpu_mitigations_off() ||
bpf_token_capable(token, CAP_PERFMON);
} }
int bpf_map_new_fd(struct bpf_map *map, int flags); int bpf_map_new_fd(struct bpf_map *map, int flags);
int bpf_prog_new_fd(struct bpf_prog *prog); int bpf_prog_new_fd(struct bpf_prog *prog);
void bpf_link_init(struct bpf_link *link, enum bpf_link_type type, void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
const struct bpf_link_ops *ops, struct bpf_prog *prog); const struct bpf_link_ops *ops, struct bpf_prog *prog,
enum bpf_attach_type attach_type);
void bpf_link_init_sleepable(struct bpf_link *link, enum bpf_link_type type, void bpf_link_init_sleepable(struct bpf_link *link, enum bpf_link_type type,
const struct bpf_link_ops *ops, struct bpf_prog *prog, const struct bpf_link_ops *ops, struct bpf_prog *prog,
bool sleepable); enum bpf_attach_type attach_type, bool sleepable);
int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer); int bpf_link_prime(struct bpf_link *link, struct bpf_link_primer *primer);
int bpf_link_settle(struct bpf_link_primer *primer); int bpf_link_settle(struct bpf_link_primer *primer);
void bpf_link_cleanup(struct bpf_link_primer *primer); void bpf_link_cleanup(struct bpf_link_primer *primer);
@ -2505,6 +2552,9 @@ void bpf_token_inc(struct bpf_token *token);
void bpf_token_put(struct bpf_token *token); void bpf_token_put(struct bpf_token *token);
int bpf_token_create(union bpf_attr *attr); int bpf_token_create(union bpf_attr *attr);
struct bpf_token *bpf_token_get_from_fd(u32 ufd); struct bpf_token *bpf_token_get_from_fd(u32 ufd);
int bpf_token_get_info_by_fd(struct bpf_token *token,
const union bpf_attr *attr,
union bpf_attr __user *uattr);
bool bpf_token_allow_cmd(const struct bpf_token *token, enum bpf_cmd cmd); bool bpf_token_allow_cmd(const struct bpf_token *token, enum bpf_cmd cmd);
bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type type); bool bpf_token_allow_map_type(const struct bpf_token *token, enum bpf_map_type type);
@ -2842,13 +2892,13 @@ bpf_prog_inc_not_zero(struct bpf_prog *prog)
static inline void bpf_link_init(struct bpf_link *link, enum bpf_link_type type, static inline void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
const struct bpf_link_ops *ops, const struct bpf_link_ops *ops,
struct bpf_prog *prog) struct bpf_prog *prog, enum bpf_attach_type attach_type)
{ {
} }
static inline void bpf_link_init_sleepable(struct bpf_link *link, enum bpf_link_type type, static inline void bpf_link_init_sleepable(struct bpf_link *link, enum bpf_link_type type,
const struct bpf_link_ops *ops, struct bpf_prog *prog, const struct bpf_link_ops *ops, struct bpf_prog *prog,
bool sleepable) enum bpf_attach_type attach_type, bool sleepable)
{ {
} }
@ -2903,6 +2953,13 @@ static inline struct bpf_token *bpf_token_get_from_fd(u32 ufd)
return ERR_PTR(-EOPNOTSUPP); return ERR_PTR(-EOPNOTSUPP);
} }
static inline int bpf_token_get_info_by_fd(struct bpf_token *token,
const union bpf_attr *attr,
union bpf_attr __user *uattr)
{
return -EOPNOTSUPP;
}
static inline void __dev_flush(struct list_head *flush_list) static inline void __dev_flush(struct list_head *flush_list)
{ {
} }
@ -3543,6 +3600,16 @@ bool btf_id_set_contains(const struct btf_id_set *set, u32 id);
#define MAX_BPRINTF_VARARGS 12 #define MAX_BPRINTF_VARARGS 12
#define MAX_BPRINTF_BUF 1024 #define MAX_BPRINTF_BUF 1024
/* Per-cpu temp buffers used by printf-like helpers to store the bprintf binary
* arguments representation.
*/
#define MAX_BPRINTF_BIN_ARGS 512
struct bpf_bprintf_buffers {
char bin_args[MAX_BPRINTF_BIN_ARGS];
char buf[MAX_BPRINTF_BUF];
};
struct bpf_bprintf_data { struct bpf_bprintf_data {
u32 *bin_args; u32 *bin_args;
char *buf; char *buf;
@ -3550,9 +3617,33 @@ struct bpf_bprintf_data {
bool get_buf; bool get_buf;
}; };
int bpf_bprintf_prepare(char *fmt, u32 fmt_size, const u64 *raw_args, int bpf_bprintf_prepare(const char *fmt, u32 fmt_size, const u64 *raw_args,
u32 num_args, struct bpf_bprintf_data *data); u32 num_args, struct bpf_bprintf_data *data);
void bpf_bprintf_cleanup(struct bpf_bprintf_data *data); void bpf_bprintf_cleanup(struct bpf_bprintf_data *data);
int bpf_try_get_buffers(struct bpf_bprintf_buffers **bufs);
void bpf_put_buffers(void);
void bpf_prog_stream_init(struct bpf_prog *prog);
void bpf_prog_stream_free(struct bpf_prog *prog);
int bpf_prog_stream_read(struct bpf_prog *prog, enum bpf_stream_id stream_id, void __user *buf, int len);
void bpf_stream_stage_init(struct bpf_stream_stage *ss);
void bpf_stream_stage_free(struct bpf_stream_stage *ss);
__printf(2, 3)
int bpf_stream_stage_printk(struct bpf_stream_stage *ss, const char *fmt, ...);
int bpf_stream_stage_commit(struct bpf_stream_stage *ss, struct bpf_prog *prog,
enum bpf_stream_id stream_id);
int bpf_stream_stage_dump_stack(struct bpf_stream_stage *ss);
#define bpf_stream_printk(ss, ...) bpf_stream_stage_printk(&ss, __VA_ARGS__)
#define bpf_stream_dump_stack(ss) bpf_stream_stage_dump_stack(&ss)
#define bpf_stream_stage(ss, prog, stream_id, expr) \
({ \
bpf_stream_stage_init(&ss); \
(expr); \
bpf_stream_stage_commit(&ss, prog, stream_id); \
bpf_stream_stage_free(&ss); \
})
#ifdef CONFIG_BPF_LSM #ifdef CONFIG_BPF_LSM
void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype); void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype);
@ -3588,4 +3679,8 @@ static inline bool bpf_is_subprog(const struct bpf_prog *prog)
return prog->aux->func_idx != 0; return prog->aux->func_idx != 0;
} }
int bpf_prog_get_file_line(struct bpf_prog *prog, unsigned long ip, const char **filep,
const char **linep, int *nump);
struct bpf_prog *bpf_prog_find_from_stack(void);
#endif /* _LINUX_BPF_H */ #endif /* _LINUX_BPF_H */

View file

@ -344,7 +344,7 @@ struct bpf_func_state {
#define MAX_CALL_FRAMES 8 #define MAX_CALL_FRAMES 8
/* instruction history flags, used in bpf_insn_hist_entry.flags field */ /* instruction history flags, used in bpf_jmp_history_entry.flags field */
enum { enum {
/* instruction references stack slot through PTR_TO_STACK register; /* instruction references stack slot through PTR_TO_STACK register;
* we also store stack's frame number in lower 3 bits (MAX_CALL_FRAMES is 8) * we also store stack's frame number in lower 3 bits (MAX_CALL_FRAMES is 8)
@ -366,7 +366,7 @@ enum {
static_assert(INSN_F_FRAMENO_MASK + 1 >= MAX_CALL_FRAMES); static_assert(INSN_F_FRAMENO_MASK + 1 >= MAX_CALL_FRAMES);
static_assert(INSN_F_SPI_MASK + 1 >= MAX_BPF_STACK / 8); static_assert(INSN_F_SPI_MASK + 1 >= MAX_BPF_STACK / 8);
struct bpf_insn_hist_entry { struct bpf_jmp_history_entry {
u32 idx; u32 idx;
/* insn idx can't be bigger than 1 million */ /* insn idx can't be bigger than 1 million */
u32 prev_idx : 20; u32 prev_idx : 20;
@ -449,32 +449,20 @@ struct bpf_verifier_state {
/* first and last insn idx of this verifier state */ /* first and last insn idx of this verifier state */
u32 first_insn_idx; u32 first_insn_idx;
u32 last_insn_idx; u32 last_insn_idx;
/* If this state is a part of states loop this field points to some /* if this state is a backedge state then equal_state
* parent of this state such that: * records cached state to which this state is equal.
* - it is also a member of the same states loop;
* - DFS states traversal starting from initial state visits loop_entry
* state before this state.
* Used to compute topmost loop entry for state loops.
* State loops might appear because of open coded iterators logic.
* See get_loop_entry() for more information.
*/ */
struct bpf_verifier_state *loop_entry; struct bpf_verifier_state *equal_state;
/* Sub-range of env->insn_hist[] corresponding to this state's /* jmp history recorded from first to last.
* instruction history. * backtracking is using it to go from last to first.
* Backtracking is using it to go from last to first. * For most states jmp_history_cnt is [0-3].
* For most states instruction history is short, 0-3 instructions.
* For loops can go up to ~40. * For loops can go up to ~40.
*/ */
u32 insn_hist_start; struct bpf_jmp_history_entry *jmp_history;
u32 insn_hist_end; u32 jmp_history_cnt;
u32 dfs_depth; u32 dfs_depth;
u32 callback_unroll_depth; u32 callback_unroll_depth;
u32 may_goto_depth; u32 may_goto_depth;
/* If this state was ever pointed-to by other state's loop_entry field
* this flag would be set to true. Used to avoid freeing such states
* while they are still in use.
*/
u32 used_as_loop_entry;
}; };
#define bpf_get_spilled_reg(slot, frame, mask) \ #define bpf_get_spilled_reg(slot, frame, mask) \
@ -580,7 +568,8 @@ struct bpf_insn_aux_data {
u64 map_key_state; /* constant (32 bit) key tracking for maps */ u64 map_key_state; /* constant (32 bit) key tracking for maps */
int ctx_field_size; /* the ctx field size for load insn, maybe 0 */ int ctx_field_size; /* the ctx field size for load insn, maybe 0 */
u32 seen; /* this insn was processed by the verifier at env->pass_cnt */ u32 seen; /* this insn was processed by the verifier at env->pass_cnt */
bool sanitize_stack_spill; /* subject to Spectre v4 sanitation */ bool nospec; /* do not execute this instruction speculatively */
bool nospec_result; /* result is unsafe under speculation, nospec must follow */
bool zext_dst; /* this insn zero extends dst reg */ bool zext_dst; /* this insn zero extends dst reg */
bool needs_zext; /* alu op needs to clear upper bits */ bool needs_zext; /* alu op needs to clear upper bits */
bool storage_get_func_atomic; /* bpf_*_storage_get() with atomic memory alloc */ bool storage_get_func_atomic; /* bpf_*_storage_get() with atomic memory alloc */
@ -609,6 +598,11 @@ struct bpf_insn_aux_data {
* accepts callback function as a parameter. * accepts callback function as a parameter.
*/ */
bool calls_callback; bool calls_callback;
/*
* CFG strongly connected component this instruction belongs to,
* zero if it is a singleton SCC.
*/
u32 scc;
/* registers alive before this instruction. */ /* registers alive before this instruction. */
u16 live_regs_before; u16 live_regs_before;
}; };
@ -718,6 +712,38 @@ struct bpf_idset {
u32 ids[BPF_ID_MAP_SIZE]; u32 ids[BPF_ID_MAP_SIZE];
}; };
/* see verifier.c:compute_scc_callchain() */
struct bpf_scc_callchain {
/* call sites from bpf_verifier_state->frame[*]->callsite leading to this SCC */
u32 callsites[MAX_CALL_FRAMES - 1];
/* last frame in a chain is identified by SCC id */
u32 scc;
};
/* verifier state waiting for propagate_backedges() */
struct bpf_scc_backedge {
struct bpf_scc_backedge *next;
struct bpf_verifier_state state;
};
struct bpf_scc_visit {
struct bpf_scc_callchain callchain;
/* first state in current verification path that entered SCC
* identified by the callchain
*/
struct bpf_verifier_state *entry_state;
struct bpf_scc_backedge *backedges; /* list of backedges */
u32 num_backedges;
};
/* An array of bpf_scc_visit structs sharing tht same bpf_scc_callchain->scc
* but having different bpf_scc_callchain->callsites.
*/
struct bpf_scc_info {
u32 num_visits;
struct bpf_scc_visit visits[];
};
/* single container for all structs /* single container for all structs
* one verifier_env per bpf_check() call * one verifier_env per bpf_check() call
*/ */
@ -775,9 +801,7 @@ struct bpf_verifier_env {
int cur_postorder; int cur_postorder;
} cfg; } cfg;
struct backtrack_state bt; struct backtrack_state bt;
struct bpf_insn_hist_entry *insn_hist; struct bpf_jmp_history_entry *cur_hist_ent;
struct bpf_insn_hist_entry *cur_hist_ent;
u32 insn_hist_cap;
u32 pass_cnt; /* number of times do_check() was called */ u32 pass_cnt; /* number of times do_check() was called */
u32 subprog_cnt; u32 subprog_cnt;
/* number of instructions analyzed by the verifier */ /* number of instructions analyzed by the verifier */
@ -799,6 +823,7 @@ struct bpf_verifier_env {
u32 longest_mark_read_walk; u32 longest_mark_read_walk;
u32 free_list_size; u32 free_list_size;
u32 explored_states_size; u32 explored_states_size;
u32 num_backedges;
bpfptr_t fd_array; bpfptr_t fd_array;
/* bit mask to keep track of whether a register has been accessed /* bit mask to keep track of whether a register has been accessed
@ -816,6 +841,10 @@ struct bpf_verifier_env {
char tmp_str_buf[TMP_STR_BUF_LEN]; char tmp_str_buf[TMP_STR_BUF_LEN];
struct bpf_insn insn_buf[INSN_BUF_SIZE]; struct bpf_insn insn_buf[INSN_BUF_SIZE];
struct bpf_insn epilogue_buf[INSN_BUF_SIZE]; struct bpf_insn epilogue_buf[INSN_BUF_SIZE];
struct bpf_scc_callchain callchain_buf;
/* array of pointers to bpf_scc_info indexed by SCC id */
struct bpf_scc_info **scc_info;
u32 scc_cnt;
}; };
static inline struct bpf_func_info_aux *subprog_aux(struct bpf_verifier_env *env, int subprog) static inline struct bpf_func_info_aux *subprog_aux(struct bpf_verifier_env *env, int subprog)

View file

@ -221,6 +221,9 @@ bool btf_is_vmlinux(const struct btf *btf);
struct module *btf_try_get_module(const struct btf *btf); struct module *btf_try_get_module(const struct btf *btf);
u32 btf_nr_types(const struct btf *btf); u32 btf_nr_types(const struct btf *btf);
struct btf *btf_base_btf(const struct btf *btf); struct btf *btf_base_btf(const struct btf *btf);
bool btf_type_is_i32(const struct btf_type *t);
bool btf_type_is_i64(const struct btf_type *t);
bool btf_type_is_primitive(const struct btf_type *t);
bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s, bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
const struct btf_member *m, const struct btf_member *m,
u32 expected_offset, u32 expected_size); u32 expected_offset, u32 expected_size);

View file

@ -82,7 +82,7 @@ struct ctl_table_header;
#define BPF_CALL_ARGS 0xe0 #define BPF_CALL_ARGS 0xe0
/* unused opcode to mark speculation barrier for mitigating /* unused opcode to mark speculation barrier for mitigating
* Speculative Store Bypass * Spectre v1 and v4
*/ */
#define BPF_NOSPEC 0xc0 #define BPF_NOSPEC 0xc0
@ -1288,6 +1288,8 @@ int bpf_jit_get_func_addr(const struct bpf_prog *prog,
const struct bpf_insn *insn, bool extra_pass, const struct bpf_insn *insn, bool extra_pass,
u64 *func_addr, bool *func_addr_fixed); u64 *func_addr, bool *func_addr_fixed);
const char *bpf_jit_get_prog_name(struct bpf_prog *prog);
struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *fp); struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *fp);
void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other); void bpf_jit_prog_release_other(struct bpf_prog *fp, struct bpf_prog *fp_other);

View file

@ -40,6 +40,8 @@ struct tnum tnum_arshift(struct tnum a, u8 min_shift, u8 insn_bitness);
struct tnum tnum_add(struct tnum a, struct tnum b); struct tnum tnum_add(struct tnum a, struct tnum b);
/* Subtract two tnums, return @a - @b */ /* Subtract two tnums, return @a - @b */
struct tnum tnum_sub(struct tnum a, struct tnum b); struct tnum tnum_sub(struct tnum a, struct tnum b);
/* Neg of a tnum, return 0 - @a */
struct tnum tnum_neg(struct tnum a);
/* Bitwise-AND, return @a & @b */ /* Bitwise-AND, return @a & @b */
struct tnum tnum_and(struct tnum a, struct tnum b); struct tnum tnum_and(struct tnum a, struct tnum b);
/* Bitwise-OR, return @a | @b */ /* Bitwise-OR, return @a | @b */

View file

@ -296,6 +296,8 @@ static inline bool pagefault_disabled(void)
*/ */
#define faulthandler_disabled() (pagefault_disabled() || in_atomic()) #define faulthandler_disabled() (pagefault_disabled() || in_atomic())
DEFINE_LOCK_GUARD_0(pagefault, pagefault_disable(), pagefault_enable())
#ifndef CONFIG_ARCH_HAS_SUBPAGE_FAULTS #ifndef CONFIG_ARCH_HAS_SUBPAGE_FAULTS
/** /**

View file

@ -1,19 +0,0 @@
#ifndef __LINUX_USERMODE_DRIVER_H__
#define __LINUX_USERMODE_DRIVER_H__
#include <linux/umh.h>
#include <linux/path.h>
struct umd_info {
const char *driver_name;
struct file *pipe_to_umh;
struct file *pipe_from_umh;
struct path wd;
struct pid *tgid;
};
int umd_load_blob(struct umd_info *info, const void *data, size_t len);
int umd_unload_blob(struct umd_info *info);
int fork_usermode_driver(struct umd_info *info);
void umd_cleanup_helper(struct umd_info *info);
#endif /* __LINUX_USERMODE_DRIVER_H__ */

View file

@ -20,7 +20,6 @@ struct tcx_entry {
struct tcx_link { struct tcx_link {
struct bpf_link link; struct bpf_link link;
struct net_device *dev; struct net_device *dev;
u32 location;
}; };
static inline void tcx_set_ingress(struct sk_buff *skb, bool ingress) static inline void tcx_set_ingress(struct sk_buff *skb, bool ingress)

View file

@ -450,6 +450,7 @@ union bpf_iter_link_info {
* * **struct bpf_map_info** * * **struct bpf_map_info**
* * **struct bpf_btf_info** * * **struct bpf_btf_info**
* * **struct bpf_link_info** * * **struct bpf_link_info**
* * **struct bpf_token_info**
* *
* Return * Return
* Returns zero on success. On error, -1 is returned and *errno* * Returns zero on success. On error, -1 is returned and *errno*
@ -906,6 +907,17 @@ union bpf_iter_link_info {
* A new file descriptor (a nonnegative integer), or -1 if an * A new file descriptor (a nonnegative integer), or -1 if an
* error occurred (in which case, *errno* is set appropriately). * error occurred (in which case, *errno* is set appropriately).
* *
* BPF_PROG_STREAM_READ_BY_FD
* Description
* Read data of a program's BPF stream. The program is identified
* by *prog_fd*, and the stream is identified by the *stream_id*.
* The data is copied to a buffer pointed to by *stream_buf*, and
* filled less than or equal to *stream_buf_len* bytes.
*
* Return
* Number of bytes read from the stream on success, or -1 if an
* error occurred (in which case, *errno* is set appropriately).
*
* NOTES * NOTES
* eBPF objects (maps and programs) can be shared between processes. * eBPF objects (maps and programs) can be shared between processes.
* *
@ -961,6 +973,7 @@ enum bpf_cmd {
BPF_LINK_DETACH, BPF_LINK_DETACH,
BPF_PROG_BIND_MAP, BPF_PROG_BIND_MAP,
BPF_TOKEN_CREATE, BPF_TOKEN_CREATE,
BPF_PROG_STREAM_READ_BY_FD,
__MAX_BPF_CMD, __MAX_BPF_CMD,
}; };
@ -1463,6 +1476,11 @@ struct bpf_stack_build_id {
#define BPF_OBJ_NAME_LEN 16U #define BPF_OBJ_NAME_LEN 16U
enum {
BPF_STREAM_STDOUT = 1,
BPF_STREAM_STDERR = 2,
};
union bpf_attr { union bpf_attr {
struct { /* anonymous struct used by BPF_MAP_CREATE command */ struct { /* anonymous struct used by BPF_MAP_CREATE command */
__u32 map_type; /* one of enum bpf_map_type */ __u32 map_type; /* one of enum bpf_map_type */
@ -1794,6 +1812,13 @@ union bpf_attr {
}; };
__u64 expected_revision; __u64 expected_revision;
} netkit; } netkit;
struct {
union {
__u32 relative_fd;
__u32 relative_id;
};
__u64 expected_revision;
} cgroup;
}; };
} link_create; } link_create;
@ -1842,6 +1867,13 @@ union bpf_attr {
__u32 bpffs_fd; __u32 bpffs_fd;
} token_create; } token_create;
struct {
__aligned_u64 stream_buf;
__u32 stream_buf_len;
__u32 stream_id;
__u32 prog_fd;
} prog_stream_read;
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
/* The description below is an attempt at providing documentation to eBPF /* The description below is an attempt at providing documentation to eBPF
@ -2403,7 +2435,7 @@ union bpf_attr {
* into it. An example is available in file * into it. An example is available in file
* *samples/bpf/trace_output_user.c* in the Linux kernel source * *samples/bpf/trace_output_user.c* in the Linux kernel source
* tree (the eBPF program counterpart is in * tree (the eBPF program counterpart is in
* *samples/bpf/trace_output_kern.c*). * *samples/bpf/trace_output.bpf.c*).
* *
* **bpf_perf_event_output**\ () achieves better performance * **bpf_perf_event_output**\ () achieves better performance
* than **bpf_trace_printk**\ () for sharing data with user * than **bpf_trace_printk**\ () for sharing data with user
@ -6653,11 +6685,15 @@ struct bpf_link_info {
struct { struct {
__aligned_u64 tp_name; /* in/out: tp_name buffer ptr */ __aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
__u32 tp_name_len; /* in/out: tp_name buffer len */ __u32 tp_name_len; /* in/out: tp_name buffer len */
__u32 :32;
__u64 cookie;
} raw_tracepoint; } raw_tracepoint;
struct { struct {
__u32 attach_type; __u32 attach_type;
__u32 target_obj_id; /* prog_id for PROG_EXT, otherwise btf object id */ __u32 target_obj_id; /* prog_id for PROG_EXT, otherwise btf object id */
__u32 target_btf_id; /* BTF type id inside the object */ __u32 target_btf_id; /* BTF type id inside the object */
__u32 :32;
__u64 cookie;
} tracing; } tracing;
struct { struct {
__u64 cgroup_id; __u64 cgroup_id;
@ -6768,6 +6804,13 @@ struct bpf_link_info {
}; };
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
struct bpf_token_info {
__u64 allowed_cmds;
__u64 allowed_maps;
__u64 allowed_progs;
__u64 allowed_attachs;
} __attribute__((aligned(8)));
/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
* by user and intended to be used by socket (e.g. to bind to, depends on * by user and intended to be used by socket (e.g. to bind to, depends on
* attach type). * attach type).

View file

@ -12,7 +12,6 @@ obj-y = fork.o exec_domain.o panic.o \
notifier.o ksysfs.o cred.o reboot.o \ notifier.o ksysfs.o cred.o reboot.o \
async.o range.o smpboot.o ucount.o regset.o ksyms_common.o async.o range.o smpboot.o ucount.o regset.o ksyms_common.o
obj-$(CONFIG_USERMODE_DRIVER) += usermode_driver.o
obj-$(CONFIG_MULTIUSER) += groups.o obj-$(CONFIG_MULTIUSER) += groups.o
obj-$(CONFIG_VHOST_TASK) += vhost_task.o obj-$(CONFIG_VHOST_TASK) += vhost_task.o

View file

@ -14,7 +14,7 @@ obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o
obj-${CONFIG_BPF_LSM} += bpf_inode_storage.o obj-${CONFIG_BPF_LSM} += bpf_inode_storage.o
obj-$(CONFIG_BPF_SYSCALL) += disasm.o mprog.o obj-$(CONFIG_BPF_SYSCALL) += disasm.o mprog.o
obj-$(CONFIG_BPF_JIT) += trampoline.o obj-$(CONFIG_BPF_JIT) += trampoline.o
obj-$(CONFIG_BPF_SYSCALL) += btf.o memalloc.o rqspinlock.o obj-$(CONFIG_BPF_SYSCALL) += btf.o memalloc.o rqspinlock.o stream.o
ifeq ($(CONFIG_MMU)$(CONFIG_64BIT),yy) ifeq ($(CONFIG_MMU)$(CONFIG_64BIT),yy)
obj-$(CONFIG_BPF_SYSCALL) += arena.o range_tree.o obj-$(CONFIG_BPF_SYSCALL) += arena.o range_tree.o
endif endif

View file

@ -550,6 +550,34 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt)
} }
} }
/*
* Reserve an arena virtual address range without populating it. This call stops
* bpf_arena_alloc_pages from adding pages to this range.
*/
static int arena_reserve_pages(struct bpf_arena *arena, long uaddr, u32 page_cnt)
{
long page_cnt_max = (arena->user_vm_end - arena->user_vm_start) >> PAGE_SHIFT;
long pgoff;
int ret;
if (uaddr & ~PAGE_MASK)
return 0;
pgoff = compute_pgoff(arena, uaddr);
if (pgoff + page_cnt > page_cnt_max)
return -EINVAL;
guard(mutex)(&arena->lock);
/* Cannot guard already allocated pages. */
ret = is_range_tree_set(&arena->rt, pgoff, page_cnt);
if (ret)
return -EBUSY;
/* "Allocate" the region to prevent it from being allocated. */
return range_tree_clear(&arena->rt, pgoff, page_cnt);
}
__bpf_kfunc_start_defs(); __bpf_kfunc_start_defs();
__bpf_kfunc void *bpf_arena_alloc_pages(void *p__map, void *addr__ign, u32 page_cnt, __bpf_kfunc void *bpf_arena_alloc_pages(void *p__map, void *addr__ign, u32 page_cnt,
@ -573,11 +601,26 @@ __bpf_kfunc void bpf_arena_free_pages(void *p__map, void *ptr__ign, u32 page_cnt
return; return;
arena_free_pages(arena, (long)ptr__ign, page_cnt); arena_free_pages(arena, (long)ptr__ign, page_cnt);
} }
__bpf_kfunc int bpf_arena_reserve_pages(void *p__map, void *ptr__ign, u32 page_cnt)
{
struct bpf_map *map = p__map;
struct bpf_arena *arena = container_of(map, struct bpf_arena, map);
if (map->map_type != BPF_MAP_TYPE_ARENA)
return -EINVAL;
if (!page_cnt)
return 0;
return arena_reserve_pages(arena, (long)ptr__ign, page_cnt);
}
__bpf_kfunc_end_defs(); __bpf_kfunc_end_defs();
BTF_KFUNCS_START(arena_kfuncs) BTF_KFUNCS_START(arena_kfuncs)
BTF_ID_FLAGS(func, bpf_arena_alloc_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_RET | KF_ARENA_ARG2) BTF_ID_FLAGS(func, bpf_arena_alloc_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_RET | KF_ARENA_ARG2)
BTF_ID_FLAGS(func, bpf_arena_free_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_ARG2) BTF_ID_FLAGS(func, bpf_arena_free_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_ARG2)
BTF_ID_FLAGS(func, bpf_arena_reserve_pages, KF_TRUSTED_ARGS | KF_SLEEPABLE | KF_ARENA_ARG2)
BTF_KFUNCS_END(arena_kfuncs) BTF_KFUNCS_END(arena_kfuncs)
static const struct btf_kfunc_id_set common_kfunc_set = { static const struct btf_kfunc_id_set common_kfunc_set = {

View file

@ -530,8 +530,6 @@ static int array_map_check_btf(const struct bpf_map *map,
const struct btf_type *key_type, const struct btf_type *key_type,
const struct btf_type *value_type) const struct btf_type *value_type)
{ {
u32 int_data;
/* One exception for keyless BTF: .bss/.data/.rodata map */ /* One exception for keyless BTF: .bss/.data/.rodata map */
if (btf_type_is_void(key_type)) { if (btf_type_is_void(key_type)) {
if (map->map_type != BPF_MAP_TYPE_ARRAY || if (map->map_type != BPF_MAP_TYPE_ARRAY ||
@ -544,14 +542,11 @@ static int array_map_check_btf(const struct bpf_map *map,
return 0; return 0;
} }
if (BTF_INFO_KIND(key_type->info) != BTF_KIND_INT) /*
return -EINVAL; * Bpf array can only take a u32 key. This check makes sure
int_data = *(u32 *)(key_type + 1);
/* bpf array can only take a u32 key. This check makes sure
* that the btf matches the attr used during map_create. * that the btf matches the attr used during map_create.
*/ */
if (BTF_INT_BITS(int_data) != 32 || BTF_INT_OFFSET(int_data)) if (!btf_type_is_i32(key_type))
return -EINVAL; return -EINVAL;
return 0; return 0;

View file

@ -38,8 +38,7 @@ static DEFINE_MUTEX(link_mutex);
/* incremented on every opened seq_file */ /* incremented on every opened seq_file */
static atomic64_t session_id; static atomic64_t session_id;
static int prepare_seq_file(struct file *file, struct bpf_iter_link *link, static int prepare_seq_file(struct file *file, struct bpf_iter_link *link);
const struct bpf_iter_seq_info *seq_info);
static void bpf_iter_inc_seq_num(struct seq_file *seq) static void bpf_iter_inc_seq_num(struct seq_file *seq)
{ {
@ -257,7 +256,7 @@ static int iter_open(struct inode *inode, struct file *file)
{ {
struct bpf_iter_link *link = inode->i_private; struct bpf_iter_link *link = inode->i_private;
return prepare_seq_file(file, link, __get_seq_info(link)); return prepare_seq_file(file, link);
} }
static int iter_release(struct inode *inode, struct file *file) static int iter_release(struct inode *inode, struct file *file)
@ -553,7 +552,8 @@ int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
if (!link) if (!link)
return -ENOMEM; return -ENOMEM;
bpf_link_init(&link->link, BPF_LINK_TYPE_ITER, &bpf_iter_link_lops, prog); bpf_link_init(&link->link, BPF_LINK_TYPE_ITER, &bpf_iter_link_lops, prog,
attr->link_create.attach_type);
link->tinfo = tinfo; link->tinfo = tinfo;
err = bpf_link_prime(&link->link, &link_primer); err = bpf_link_prime(&link->link, &link_primer);
@ -586,9 +586,9 @@ static void init_seq_meta(struct bpf_iter_priv_data *priv_data,
priv_data->done_stop = false; priv_data->done_stop = false;
} }
static int prepare_seq_file(struct file *file, struct bpf_iter_link *link, static int prepare_seq_file(struct file *file, struct bpf_iter_link *link)
const struct bpf_iter_seq_info *seq_info)
{ {
const struct bpf_iter_seq_info *seq_info = __get_seq_info(link);
struct bpf_iter_priv_data *priv_data; struct bpf_iter_priv_data *priv_data;
struct bpf_iter_target_info *tinfo; struct bpf_iter_target_info *tinfo;
struct bpf_prog *prog; struct bpf_prog *prog;
@ -653,7 +653,7 @@ int bpf_iter_new_fd(struct bpf_link *link)
} }
iter_link = container_of(link, struct bpf_iter_link, link); iter_link = container_of(link, struct bpf_iter_link, link);
err = prepare_seq_file(file, iter_link, __get_seq_info(iter_link)); err = prepare_seq_file(file, iter_link);
if (err) if (err)
goto free_file; goto free_file;

View file

@ -722,13 +722,7 @@ int bpf_local_storage_map_check_btf(const struct bpf_map *map,
const struct btf_type *key_type, const struct btf_type *key_type,
const struct btf_type *value_type) const struct btf_type *value_type)
{ {
u32 int_data; if (!btf_type_is_i32(key_type))
if (BTF_INFO_KIND(key_type->info) != BTF_KIND_INT)
return -EINVAL;
int_data = *(u32 *)(key_type + 1);
if (BTF_INT_BITS(int_data) != 32 || BTF_INT_OFFSET(int_data))
return -EINVAL; return -EINVAL;
return 0; return 0;

View file

@ -808,7 +808,7 @@ static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
goto reset_unlock; goto reset_unlock;
} }
bpf_link_init(&link->link, BPF_LINK_TYPE_STRUCT_OPS, bpf_link_init(&link->link, BPF_LINK_TYPE_STRUCT_OPS,
&bpf_struct_ops_link_lops, prog); &bpf_struct_ops_link_lops, prog, prog->expected_attach_type);
*plink++ = &link->link; *plink++ = &link->link;
ksym = kzalloc(sizeof(*ksym), GFP_USER); ksym = kzalloc(sizeof(*ksym), GFP_USER);
@ -1351,7 +1351,8 @@ int bpf_struct_ops_link_create(union bpf_attr *attr)
err = -ENOMEM; err = -ENOMEM;
goto err_out; goto err_out;
} }
bpf_link_init(&link->link, BPF_LINK_TYPE_STRUCT_OPS, &bpf_struct_ops_map_lops, NULL); bpf_link_init(&link->link, BPF_LINK_TYPE_STRUCT_OPS, &bpf_struct_ops_map_lops, NULL,
attr->link_create.attach_type);
err = bpf_link_prime(&link->link, &link_primer); err = bpf_link_prime(&link->link, &link_primer);
if (err) if (err)

View file

@ -858,26 +858,43 @@ const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id)
EXPORT_SYMBOL_GPL(btf_type_by_id); EXPORT_SYMBOL_GPL(btf_type_by_id);
/* /*
* Regular int is not a bit field and it must be either * Check that the type @t is a regular int. This means that @t is not
* u8/u16/u32/u64 or __int128. * a bit field and it has the same size as either of u8/u16/u32/u64
* or __int128. If @expected_size is not zero, then size of @t should
* be the same. A caller should already have checked that the type @t
* is an integer.
*/ */
static bool __btf_type_int_is_regular(const struct btf_type *t, size_t expected_size)
{
u32 int_data = btf_type_int(t);
u8 nr_bits = BTF_INT_BITS(int_data);
u8 nr_bytes = BITS_ROUNDUP_BYTES(nr_bits);
return BITS_PER_BYTE_MASKED(nr_bits) == 0 &&
BTF_INT_OFFSET(int_data) == 0 &&
(nr_bytes <= 16 && is_power_of_2(nr_bytes)) &&
(expected_size == 0 || nr_bytes == expected_size);
}
static bool btf_type_int_is_regular(const struct btf_type *t) static bool btf_type_int_is_regular(const struct btf_type *t)
{ {
u8 nr_bits, nr_bytes; return __btf_type_int_is_regular(t, 0);
u32 int_data; }
int_data = btf_type_int(t); bool btf_type_is_i32(const struct btf_type *t)
nr_bits = BTF_INT_BITS(int_data); {
nr_bytes = BITS_ROUNDUP_BYTES(nr_bits); return btf_type_is_int(t) && __btf_type_int_is_regular(t, 4);
if (BITS_PER_BYTE_MASKED(nr_bits) || }
BTF_INT_OFFSET(int_data) ||
(nr_bytes != sizeof(u8) && nr_bytes != sizeof(u16) &&
nr_bytes != sizeof(u32) && nr_bytes != sizeof(u64) &&
nr_bytes != (2 * sizeof(u64)))) {
return false;
}
return true; bool btf_type_is_i64(const struct btf_type *t)
{
return btf_type_is_int(t) && __btf_type_int_is_regular(t, 8);
}
bool btf_type_is_primitive(const struct btf_type *t)
{
return (btf_type_is_int(t) && btf_type_int_is_regular(t)) ||
btf_is_any_enum(t);
} }
/* /*
@ -3443,7 +3460,8 @@ btf_find_graph_root(const struct btf *btf, const struct btf_type *pt,
node_field_name = strstr(value_type, ":"); node_field_name = strstr(value_type, ":");
if (!node_field_name) if (!node_field_name)
return -EINVAL; return -EINVAL;
value_type = kstrndup(value_type, node_field_name - value_type, GFP_KERNEL | __GFP_NOWARN); value_type = kstrndup(value_type, node_field_name - value_type,
GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
if (!value_type) if (!value_type)
return -ENOMEM; return -ENOMEM;
id = btf_find_by_name_kind(btf, value_type, BTF_KIND_STRUCT); id = btf_find_by_name_kind(btf, value_type, BTF_KIND_STRUCT);
@ -3958,7 +3976,7 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
/* This needs to be kzalloc to zero out padding and unused fields, see /* This needs to be kzalloc to zero out padding and unused fields, see
* comment in btf_record_equal. * comment in btf_record_equal.
*/ */
rec = kzalloc(struct_size(rec, fields, cnt), GFP_KERNEL | __GFP_NOWARN); rec = kzalloc(struct_size(rec, fields, cnt), GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
if (!rec) if (!rec)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -6182,8 +6200,7 @@ int get_kern_ctx_btf_id(struct bpf_verifier_log *log, enum bpf_prog_type prog_ty
return kctx_type_id; return kctx_type_id;
} }
BTF_ID_LIST(bpf_ctx_convert_btf_id) BTF_ID_LIST_SINGLE(bpf_ctx_convert_btf_id, struct, bpf_ctx_convert)
BTF_ID(struct, bpf_ctx_convert)
static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name, static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name,
void *data, unsigned int data_size) void *data, unsigned int data_size)
@ -6903,6 +6920,7 @@ enum bpf_struct_walk_result {
/* < 0 error */ /* < 0 error */
WALK_SCALAR = 0, WALK_SCALAR = 0,
WALK_PTR, WALK_PTR,
WALK_PTR_UNTRUSTED,
WALK_STRUCT, WALK_STRUCT,
}; };
@ -7144,6 +7162,8 @@ error:
*field_name = mname; *field_name = mname;
return WALK_PTR; return WALK_PTR;
} }
return WALK_PTR_UNTRUSTED;
} }
/* Allow more flexible access within an int as long as /* Allow more flexible access within an int as long as
@ -7216,6 +7236,9 @@ int btf_struct_access(struct bpf_verifier_log *log,
*next_btf_id = id; *next_btf_id = id;
*flag = tmp_flag; *flag = tmp_flag;
return PTR_TO_BTF_ID; return PTR_TO_BTF_ID;
case WALK_PTR_UNTRUSTED:
*flag = MEM_RDONLY | PTR_UNTRUSTED;
return PTR_TO_MEM;
case WALK_SCALAR: case WALK_SCALAR:
return SCALAR_VALUE; return SCALAR_VALUE;
case WALK_STRUCT: case WALK_STRUCT:
@ -7628,11 +7651,12 @@ cand_cache_unlock:
} }
enum btf_arg_tag { enum btf_arg_tag {
ARG_TAG_CTX = BIT_ULL(0), ARG_TAG_CTX = BIT_ULL(0),
ARG_TAG_NONNULL = BIT_ULL(1), ARG_TAG_NONNULL = BIT_ULL(1),
ARG_TAG_TRUSTED = BIT_ULL(2), ARG_TAG_TRUSTED = BIT_ULL(2),
ARG_TAG_NULLABLE = BIT_ULL(3), ARG_TAG_UNTRUSTED = BIT_ULL(3),
ARG_TAG_ARENA = BIT_ULL(4), ARG_TAG_NULLABLE = BIT_ULL(4),
ARG_TAG_ARENA = BIT_ULL(5),
}; };
/* Process BTF of a function to produce high-level expectation of function /* Process BTF of a function to produce high-level expectation of function
@ -7740,6 +7764,8 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
tags |= ARG_TAG_CTX; tags |= ARG_TAG_CTX;
} else if (strcmp(tag, "trusted") == 0) { } else if (strcmp(tag, "trusted") == 0) {
tags |= ARG_TAG_TRUSTED; tags |= ARG_TAG_TRUSTED;
} else if (strcmp(tag, "untrusted") == 0) {
tags |= ARG_TAG_UNTRUSTED;
} else if (strcmp(tag, "nonnull") == 0) { } else if (strcmp(tag, "nonnull") == 0) {
tags |= ARG_TAG_NONNULL; tags |= ARG_TAG_NONNULL;
} else if (strcmp(tag, "nullable") == 0) { } else if (strcmp(tag, "nullable") == 0) {
@ -7800,6 +7826,38 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
sub->args[i].btf_id = kern_type_id; sub->args[i].btf_id = kern_type_id;
continue; continue;
} }
if (tags & ARG_TAG_UNTRUSTED) {
struct btf *vmlinux_btf;
int kern_type_id;
if (tags & ~ARG_TAG_UNTRUSTED) {
bpf_log(log, "arg#%d untrusted cannot be combined with any other tags\n", i);
return -EINVAL;
}
ref_t = btf_type_skip_modifiers(btf, t->type, NULL);
if (btf_type_is_void(ref_t) || btf_type_is_primitive(ref_t)) {
sub->args[i].arg_type = ARG_PTR_TO_MEM | MEM_RDONLY | PTR_UNTRUSTED;
sub->args[i].mem_size = 0;
continue;
}
kern_type_id = btf_get_ptr_to_btf_id(log, i, btf, t);
if (kern_type_id < 0)
return kern_type_id;
vmlinux_btf = bpf_get_btf_vmlinux();
ref_t = btf_type_by_id(vmlinux_btf, kern_type_id);
if (!btf_type_is_struct(ref_t)) {
tname = __btf_name_by_offset(vmlinux_btf, t->name_off);
bpf_log(log, "arg#%d has type %s '%s', but only struct or primitive types are allowed\n",
i, btf_type_str(ref_t), tname);
return -EINVAL;
}
sub->args[i].arg_type = ARG_PTR_TO_BTF_ID | PTR_UNTRUSTED;
sub->args[i].btf_id = kern_type_id;
continue;
}
if (tags & ARG_TAG_ARENA) { if (tags & ARG_TAG_ARENA) {
if (tags & ~ARG_TAG_ARENA) { if (tags & ~ARG_TAG_ARENA) {
bpf_log(log, "arg#%d arena cannot be combined with any other tags\n", i); bpf_log(log, "arg#%d arena cannot be combined with any other tags\n", i);
@ -9019,7 +9077,7 @@ static struct bpf_cand_cache *populate_cand_cache(struct bpf_cand_cache *cands,
bpf_free_cands_from_cache(*cc); bpf_free_cands_from_cache(*cc);
*cc = NULL; *cc = NULL;
} }
new_cands = kmemdup(cands, sizeof_cands(cands->cnt), GFP_KERNEL); new_cands = kmemdup(cands, sizeof_cands(cands->cnt), GFP_KERNEL_ACCOUNT);
if (!new_cands) { if (!new_cands) {
bpf_free_cands(cands); bpf_free_cands(cands);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -9027,7 +9085,7 @@ static struct bpf_cand_cache *populate_cand_cache(struct bpf_cand_cache *cands,
/* strdup the name, since it will stay in cache. /* strdup the name, since it will stay in cache.
* the cands->name points to strings in prog's BTF and the prog can be unloaded. * the cands->name points to strings in prog's BTF and the prog can be unloaded.
*/ */
new_cands->name = kmemdup_nul(cands->name, cands->name_len, GFP_KERNEL); new_cands->name = kmemdup_nul(cands->name, cands->name_len, GFP_KERNEL_ACCOUNT);
bpf_free_cands(cands); bpf_free_cands(cands);
if (!new_cands->name) { if (!new_cands->name) {
kfree(new_cands); kfree(new_cands);
@ -9111,7 +9169,7 @@ bpf_core_add_cands(struct bpf_cand_cache *cands, const struct btf *targ_btf,
continue; continue;
/* most of the time there is only one candidate for a given kind+name pair */ /* most of the time there is only one candidate for a given kind+name pair */
new_cands = kmalloc(sizeof_cands(cands->cnt + 1), GFP_KERNEL); new_cands = kmalloc(sizeof_cands(cands->cnt + 1), GFP_KERNEL_ACCOUNT);
if (!new_cands) { if (!new_cands) {
bpf_free_cands(cands); bpf_free_cands(cands);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -9228,7 +9286,7 @@ int bpf_core_apply(struct bpf_core_ctx *ctx, const struct bpf_core_relo *relo,
/* ~4k of temp memory necessary to convert LLVM spec like "0:1:0:5" /* ~4k of temp memory necessary to convert LLVM spec like "0:1:0:5"
* into arrays of btf_ids of struct fields and array indices. * into arrays of btf_ids of struct fields and array indices.
*/ */
specs = kcalloc(3, sizeof(*specs), GFP_KERNEL); specs = kcalloc(3, sizeof(*specs), GFP_KERNEL_ACCOUNT);
if (!specs) if (!specs)
return -ENOMEM; return -ENOMEM;
@ -9253,7 +9311,7 @@ int bpf_core_apply(struct bpf_core_ctx *ctx, const struct bpf_core_relo *relo,
goto out; goto out;
} }
if (cc->cnt) { if (cc->cnt) {
cands.cands = kcalloc(cc->cnt, sizeof(*cands.cands), GFP_KERNEL); cands.cands = kcalloc(cc->cnt, sizeof(*cands.cands), GFP_KERNEL_ACCOUNT);
if (!cands.cands) { if (!cands.cands) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;

View file

@ -658,6 +658,116 @@ static struct bpf_prog_list *find_attach_entry(struct hlist_head *progs,
return NULL; return NULL;
} }
static struct bpf_link *bpf_get_anchor_link(u32 flags, u32 id_or_fd)
{
struct bpf_link *link = ERR_PTR(-EINVAL);
if (flags & BPF_F_ID)
link = bpf_link_by_id(id_or_fd);
else if (id_or_fd)
link = bpf_link_get_from_fd(id_or_fd);
return link;
}
static struct bpf_prog *bpf_get_anchor_prog(u32 flags, u32 id_or_fd)
{
struct bpf_prog *prog = ERR_PTR(-EINVAL);
if (flags & BPF_F_ID)
prog = bpf_prog_by_id(id_or_fd);
else if (id_or_fd)
prog = bpf_prog_get(id_or_fd);
return prog;
}
static struct bpf_prog_list *get_prog_list(struct hlist_head *progs, struct bpf_prog *prog,
struct bpf_cgroup_link *link, u32 flags, u32 id_or_fd)
{
bool is_link = flags & BPF_F_LINK, is_id = flags & BPF_F_ID;
struct bpf_prog_list *pltmp, *pl = ERR_PTR(-EINVAL);
bool preorder = flags & BPF_F_PREORDER;
struct bpf_link *anchor_link = NULL;
struct bpf_prog *anchor_prog = NULL;
bool is_before, is_after;
is_before = flags & BPF_F_BEFORE;
is_after = flags & BPF_F_AFTER;
if (is_link || is_id || id_or_fd) {
/* flags must have either BPF_F_BEFORE or BPF_F_AFTER */
if (is_before == is_after)
return ERR_PTR(-EINVAL);
if ((is_link && !link) || (!is_link && !prog))
return ERR_PTR(-EINVAL);
} else if (!hlist_empty(progs)) {
/* flags cannot have both BPF_F_BEFORE and BPF_F_AFTER */
if (is_before && is_after)
return ERR_PTR(-EINVAL);
}
if (is_link) {
anchor_link = bpf_get_anchor_link(flags, id_or_fd);
if (IS_ERR(anchor_link))
return ERR_CAST(anchor_link);
} else if (is_id || id_or_fd) {
anchor_prog = bpf_get_anchor_prog(flags, id_or_fd);
if (IS_ERR(anchor_prog))
return ERR_CAST(anchor_prog);
}
if (!anchor_prog && !anchor_link) {
/* if there is no anchor_prog/anchor_link, then BPF_F_PREORDER
* doesn't matter since either prepend or append to a combined
* list of progs will end up with correct result.
*/
hlist_for_each_entry(pltmp, progs, node) {
if (is_before)
return pltmp;
if (pltmp->node.next)
continue;
return pltmp;
}
return NULL;
}
hlist_for_each_entry(pltmp, progs, node) {
if ((anchor_prog && anchor_prog == pltmp->prog) ||
(anchor_link && anchor_link == &pltmp->link->link)) {
if (!!(pltmp->flags & BPF_F_PREORDER) != preorder)
goto out;
pl = pltmp;
goto out;
}
}
pl = ERR_PTR(-ENOENT);
out:
if (anchor_link)
bpf_link_put(anchor_link);
else
bpf_prog_put(anchor_prog);
return pl;
}
static int insert_pl_to_hlist(struct bpf_prog_list *pl, struct hlist_head *progs,
struct bpf_prog *prog, struct bpf_cgroup_link *link,
u32 flags, u32 id_or_fd)
{
struct bpf_prog_list *pltmp;
pltmp = get_prog_list(progs, prog, link, flags, id_or_fd);
if (IS_ERR(pltmp))
return PTR_ERR(pltmp);
if (!pltmp)
hlist_add_head(&pl->node, progs);
else if (flags & BPF_F_BEFORE)
hlist_add_before(&pl->node, &pltmp->node);
else
hlist_add_behind(&pl->node, &pltmp->node);
return 0;
}
/** /**
* __cgroup_bpf_attach() - Attach the program or the link to a cgroup, and * __cgroup_bpf_attach() - Attach the program or the link to a cgroup, and
* propagate the change to descendants * propagate the change to descendants
@ -667,6 +777,8 @@ static struct bpf_prog_list *find_attach_entry(struct hlist_head *progs,
* @replace_prog: Previously attached program to replace if BPF_F_REPLACE is set * @replace_prog: Previously attached program to replace if BPF_F_REPLACE is set
* @type: Type of attach operation * @type: Type of attach operation
* @flags: Option flags * @flags: Option flags
* @id_or_fd: Relative prog id or fd
* @revision: bpf_prog_list revision
* *
* Exactly one of @prog or @link can be non-null. * Exactly one of @prog or @link can be non-null.
* Must be called with cgroup_mutex held. * Must be called with cgroup_mutex held.
@ -674,7 +786,8 @@ static struct bpf_prog_list *find_attach_entry(struct hlist_head *progs,
static int __cgroup_bpf_attach(struct cgroup *cgrp, static int __cgroup_bpf_attach(struct cgroup *cgrp,
struct bpf_prog *prog, struct bpf_prog *replace_prog, struct bpf_prog *prog, struct bpf_prog *replace_prog,
struct bpf_cgroup_link *link, struct bpf_cgroup_link *link,
enum bpf_attach_type type, u32 flags) enum bpf_attach_type type, u32 flags, u32 id_or_fd,
u64 revision)
{ {
u32 saved_flags = (flags & (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI)); u32 saved_flags = (flags & (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI));
struct bpf_prog *old_prog = NULL; struct bpf_prog *old_prog = NULL;
@ -690,6 +803,9 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
((flags & BPF_F_REPLACE) && !(flags & BPF_F_ALLOW_MULTI))) ((flags & BPF_F_REPLACE) && !(flags & BPF_F_ALLOW_MULTI)))
/* invalid combination */ /* invalid combination */
return -EINVAL; return -EINVAL;
if ((flags & BPF_F_REPLACE) && (flags & (BPF_F_BEFORE | BPF_F_AFTER)))
/* only either replace or insertion with before/after */
return -EINVAL;
if (link && (prog || replace_prog)) if (link && (prog || replace_prog))
/* only either link or prog/replace_prog can be specified */ /* only either link or prog/replace_prog can be specified */
return -EINVAL; return -EINVAL;
@ -700,6 +816,8 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
atype = bpf_cgroup_atype_find(type, new_prog->aux->attach_btf_id); atype = bpf_cgroup_atype_find(type, new_prog->aux->attach_btf_id);
if (atype < 0) if (atype < 0)
return -EINVAL; return -EINVAL;
if (revision && revision != cgrp->bpf.revisions[atype])
return -ESTALE;
progs = &cgrp->bpf.progs[atype]; progs = &cgrp->bpf.progs[atype];
@ -728,22 +846,18 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
if (pl) { if (pl) {
old_prog = pl->prog; old_prog = pl->prog;
} else { } else {
struct hlist_node *last = NULL;
pl = kmalloc(sizeof(*pl), GFP_KERNEL); pl = kmalloc(sizeof(*pl), GFP_KERNEL);
if (!pl) { if (!pl) {
bpf_cgroup_storages_free(new_storage); bpf_cgroup_storages_free(new_storage);
return -ENOMEM; return -ENOMEM;
} }
if (hlist_empty(progs))
hlist_add_head(&pl->node, progs); err = insert_pl_to_hlist(pl, progs, prog, link, flags, id_or_fd);
else if (err) {
hlist_for_each(last, progs) { kfree(pl);
if (last->next) bpf_cgroup_storages_free(new_storage);
continue; return err;
hlist_add_behind(&pl->node, last); }
break;
}
} }
pl->prog = prog; pl->prog = prog;
@ -753,7 +867,7 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
cgrp->bpf.flags[atype] = saved_flags; cgrp->bpf.flags[atype] = saved_flags;
if (type == BPF_LSM_CGROUP) { if (type == BPF_LSM_CGROUP) {
err = bpf_trampoline_link_cgroup_shim(new_prog, atype); err = bpf_trampoline_link_cgroup_shim(new_prog, atype, type);
if (err) if (err)
goto cleanup; goto cleanup;
} }
@ -762,6 +876,7 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp,
if (err) if (err)
goto cleanup_trampoline; goto cleanup_trampoline;
cgrp->bpf.revisions[atype] += 1;
if (old_prog) { if (old_prog) {
if (type == BPF_LSM_CGROUP) if (type == BPF_LSM_CGROUP)
bpf_trampoline_unlink_cgroup_shim(old_prog); bpf_trampoline_unlink_cgroup_shim(old_prog);
@ -793,12 +908,13 @@ static int cgroup_bpf_attach(struct cgroup *cgrp,
struct bpf_prog *prog, struct bpf_prog *replace_prog, struct bpf_prog *prog, struct bpf_prog *replace_prog,
struct bpf_cgroup_link *link, struct bpf_cgroup_link *link,
enum bpf_attach_type type, enum bpf_attach_type type,
u32 flags) u32 flags, u32 id_or_fd, u64 revision)
{ {
int ret; int ret;
cgroup_lock(); cgroup_lock();
ret = __cgroup_bpf_attach(cgrp, prog, replace_prog, link, type, flags); ret = __cgroup_bpf_attach(cgrp, prog, replace_prog, link, type, flags,
id_or_fd, revision);
cgroup_unlock(); cgroup_unlock();
return ret; return ret;
} }
@ -868,7 +984,7 @@ static int __cgroup_bpf_replace(struct cgroup *cgrp,
struct hlist_head *progs; struct hlist_head *progs;
bool found = false; bool found = false;
atype = bpf_cgroup_atype_find(link->type, new_prog->aux->attach_btf_id); atype = bpf_cgroup_atype_find(link->link.attach_type, new_prog->aux->attach_btf_id);
if (atype < 0) if (atype < 0)
return -EINVAL; return -EINVAL;
@ -886,6 +1002,7 @@ static int __cgroup_bpf_replace(struct cgroup *cgrp,
if (!found) if (!found)
return -ENOENT; return -ENOENT;
cgrp->bpf.revisions[atype] += 1;
old_prog = xchg(&link->link.prog, new_prog); old_prog = xchg(&link->link.prog, new_prog);
replace_effective_prog(cgrp, atype, link); replace_effective_prog(cgrp, atype, link);
bpf_prog_put(old_prog); bpf_prog_put(old_prog);
@ -1011,12 +1128,14 @@ found:
* @prog: A program to detach or NULL * @prog: A program to detach or NULL
* @link: A link to detach or NULL * @link: A link to detach or NULL
* @type: Type of detach operation * @type: Type of detach operation
* @revision: bpf_prog_list revision
* *
* At most one of @prog or @link can be non-NULL. * At most one of @prog or @link can be non-NULL.
* Must be called with cgroup_mutex held. * Must be called with cgroup_mutex held.
*/ */
static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
struct bpf_cgroup_link *link, enum bpf_attach_type type) struct bpf_cgroup_link *link, enum bpf_attach_type type,
u64 revision)
{ {
enum cgroup_bpf_attach_type atype; enum cgroup_bpf_attach_type atype;
struct bpf_prog *old_prog; struct bpf_prog *old_prog;
@ -1034,6 +1153,9 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
if (atype < 0) if (atype < 0)
return -EINVAL; return -EINVAL;
if (revision && revision != cgrp->bpf.revisions[atype])
return -ESTALE;
progs = &cgrp->bpf.progs[atype]; progs = &cgrp->bpf.progs[atype];
flags = cgrp->bpf.flags[atype]; flags = cgrp->bpf.flags[atype];
@ -1059,6 +1181,7 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
/* now can actually delete it from this cgroup list */ /* now can actually delete it from this cgroup list */
hlist_del(&pl->node); hlist_del(&pl->node);
cgrp->bpf.revisions[atype] += 1;
kfree(pl); kfree(pl);
if (hlist_empty(progs)) if (hlist_empty(progs))
@ -1074,12 +1197,12 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
} }
static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
enum bpf_attach_type type) enum bpf_attach_type type, u64 revision)
{ {
int ret; int ret;
cgroup_lock(); cgroup_lock();
ret = __cgroup_bpf_detach(cgrp, prog, NULL, type); ret = __cgroup_bpf_detach(cgrp, prog, NULL, type, revision);
cgroup_unlock(); cgroup_unlock();
return ret; return ret;
} }
@ -1097,6 +1220,7 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
struct bpf_prog_array *effective; struct bpf_prog_array *effective;
int cnt, ret = 0, i; int cnt, ret = 0, i;
int total_cnt = 0; int total_cnt = 0;
u64 revision = 0;
u32 flags; u32 flags;
if (effective_query && prog_attach_flags) if (effective_query && prog_attach_flags)
@ -1134,6 +1258,10 @@ static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,
return -EFAULT; return -EFAULT;
if (copy_to_user(&uattr->query.prog_cnt, &total_cnt, sizeof(total_cnt))) if (copy_to_user(&uattr->query.prog_cnt, &total_cnt, sizeof(total_cnt)))
return -EFAULT; return -EFAULT;
if (!effective_query && from_atype == to_atype)
revision = cgrp->bpf.revisions[from_atype];
if (copy_to_user(&uattr->query.revision, &revision, sizeof(revision)))
return -EFAULT;
if (attr->query.prog_cnt == 0 || !prog_ids || !total_cnt) if (attr->query.prog_cnt == 0 || !prog_ids || !total_cnt)
/* return early if user requested only program count + flags */ /* return early if user requested only program count + flags */
return 0; return 0;
@ -1216,7 +1344,8 @@ int cgroup_bpf_prog_attach(const union bpf_attr *attr,
} }
ret = cgroup_bpf_attach(cgrp, prog, replace_prog, NULL, ret = cgroup_bpf_attach(cgrp, prog, replace_prog, NULL,
attr->attach_type, attr->attach_flags); attr->attach_type, attr->attach_flags,
attr->relative_fd, attr->expected_revision);
if (replace_prog) if (replace_prog)
bpf_prog_put(replace_prog); bpf_prog_put(replace_prog);
@ -1238,7 +1367,7 @@ int cgroup_bpf_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype)
if (IS_ERR(prog)) if (IS_ERR(prog))
prog = NULL; prog = NULL;
ret = cgroup_bpf_detach(cgrp, prog, attr->attach_type); ret = cgroup_bpf_detach(cgrp, prog, attr->attach_type, attr->expected_revision);
if (prog) if (prog)
bpf_prog_put(prog); bpf_prog_put(prog);
@ -1267,8 +1396,8 @@ static void bpf_cgroup_link_release(struct bpf_link *link)
} }
WARN_ON(__cgroup_bpf_detach(cg_link->cgroup, NULL, cg_link, WARN_ON(__cgroup_bpf_detach(cg_link->cgroup, NULL, cg_link,
cg_link->type)); link->attach_type, 0));
if (cg_link->type == BPF_LSM_CGROUP) if (link->attach_type == BPF_LSM_CGROUP)
bpf_trampoline_unlink_cgroup_shim(cg_link->link.prog); bpf_trampoline_unlink_cgroup_shim(cg_link->link.prog);
cg = cg_link->cgroup; cg = cg_link->cgroup;
@ -1310,7 +1439,7 @@ static void bpf_cgroup_link_show_fdinfo(const struct bpf_link *link,
"cgroup_id:\t%llu\n" "cgroup_id:\t%llu\n"
"attach_type:\t%d\n", "attach_type:\t%d\n",
cg_id, cg_id,
cg_link->type); link->attach_type);
} }
static int bpf_cgroup_link_fill_link_info(const struct bpf_link *link, static int bpf_cgroup_link_fill_link_info(const struct bpf_link *link,
@ -1326,7 +1455,7 @@ static int bpf_cgroup_link_fill_link_info(const struct bpf_link *link,
cgroup_unlock(); cgroup_unlock();
info->cgroup.cgroup_id = cg_id; info->cgroup.cgroup_id = cg_id;
info->cgroup.attach_type = cg_link->type; info->cgroup.attach_type = link->attach_type;
return 0; return 0;
} }
@ -1339,6 +1468,13 @@ static const struct bpf_link_ops bpf_cgroup_link_lops = {
.fill_link_info = bpf_cgroup_link_fill_link_info, .fill_link_info = bpf_cgroup_link_fill_link_info,
}; };
#define BPF_F_LINK_ATTACH_MASK \
(BPF_F_ID | \
BPF_F_BEFORE | \
BPF_F_AFTER | \
BPF_F_PREORDER | \
BPF_F_LINK)
int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
{ {
struct bpf_link_primer link_primer; struct bpf_link_primer link_primer;
@ -1346,7 +1482,7 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
struct cgroup *cgrp; struct cgroup *cgrp;
int err; int err;
if (attr->link_create.flags) if (attr->link_create.flags & (~BPF_F_LINK_ATTACH_MASK))
return -EINVAL; return -EINVAL;
cgrp = cgroup_get_from_fd(attr->link_create.target_fd); cgrp = cgroup_get_from_fd(attr->link_create.target_fd);
@ -1359,9 +1495,8 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
goto out_put_cgroup; goto out_put_cgroup;
} }
bpf_link_init(&link->link, BPF_LINK_TYPE_CGROUP, &bpf_cgroup_link_lops, bpf_link_init(&link->link, BPF_LINK_TYPE_CGROUP, &bpf_cgroup_link_lops,
prog); prog, attr->link_create.attach_type);
link->cgroup = cgrp; link->cgroup = cgrp;
link->type = attr->link_create.attach_type;
err = bpf_link_prime(&link->link, &link_primer); err = bpf_link_prime(&link->link, &link_primer);
if (err) { if (err) {
@ -1370,7 +1505,9 @@ int cgroup_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
} }
err = cgroup_bpf_attach(cgrp, NULL, NULL, link, err = cgroup_bpf_attach(cgrp, NULL, NULL, link,
link->type, BPF_F_ALLOW_MULTI); link->link.attach_type, BPF_F_ALLOW_MULTI | attr->link_create.flags,
attr->link_create.cgroup.relative_fd,
attr->link_create.cgroup.expected_revision);
if (err) { if (err) {
bpf_link_cleanup(&link_primer); bpf_link_cleanup(&link_primer);
goto out_put_cgroup; goto out_put_cgroup;

View file

@ -134,6 +134,10 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag
mutex_init(&fp->aux->ext_mutex); mutex_init(&fp->aux->ext_mutex);
mutex_init(&fp->aux->dst_mutex); mutex_init(&fp->aux->dst_mutex);
#ifdef CONFIG_BPF_SYSCALL
bpf_prog_stream_init(fp);
#endif
return fp; return fp;
} }
@ -778,7 +782,10 @@ bool is_bpf_text_address(unsigned long addr)
struct bpf_prog *bpf_prog_ksym_find(unsigned long addr) struct bpf_prog *bpf_prog_ksym_find(unsigned long addr)
{ {
struct bpf_ksym *ksym = bpf_ksym_find(addr); struct bpf_ksym *ksym;
WARN_ON_ONCE(!rcu_read_lock_held());
ksym = bpf_ksym_find(addr);
return ksym && ksym->prog ? return ksym && ksym->prog ?
container_of(ksym, struct bpf_prog_aux, ksym)->prog : container_of(ksym, struct bpf_prog_aux, ksym)->prog :
@ -1290,6 +1297,13 @@ int bpf_jit_get_func_addr(const struct bpf_prog *prog,
return 0; return 0;
} }
const char *bpf_jit_get_prog_name(struct bpf_prog *prog)
{
if (prog->aux->ksym.prog)
return prog->aux->ksym.name;
return prog->aux->name;
}
static int bpf_jit_blind_insn(const struct bpf_insn *from, static int bpf_jit_blind_insn(const struct bpf_insn *from,
const struct bpf_insn *aux, const struct bpf_insn *aux,
struct bpf_insn *to_buff, struct bpf_insn *to_buff,
@ -2102,14 +2116,15 @@ out:
#undef COND_JMP #undef COND_JMP
/* ST, STX and LDX*/ /* ST, STX and LDX*/
ST_NOSPEC: ST_NOSPEC:
/* Speculation barrier for mitigating Speculative Store Bypass. /* Speculation barrier for mitigating Speculative Store Bypass,
* In case of arm64, we rely on the firmware mitigation as * Bounds-Check Bypass and Type Confusion. In case of arm64, we
* controlled via the ssbd kernel parameter. Whenever the * rely on the firmware mitigation as controlled via the ssbd
* mitigation is enabled, it works for all of the kernel code * kernel parameter. Whenever the mitigation is enabled, it
* with no need to provide any additional instructions here. * works for all of the kernel code with no need to provide any
* In case of x86, we use 'lfence' insn for mitigation. We * additional instructions here. In case of x86, we use 'lfence'
* reuse preexisting logic from Spectre v1 mitigation that * insn for mitigation. We reuse preexisting logic from Spectre
* happens to produce the required code on x86 for v4 as well. * v1 mitigation that happens to produce the required code on
* x86 for v4 as well.
*/ */
barrier_nospec(); barrier_nospec();
CONT; CONT;
@ -2861,6 +2876,7 @@ static void bpf_prog_free_deferred(struct work_struct *work)
aux = container_of(work, struct bpf_prog_aux, work); aux = container_of(work, struct bpf_prog_aux, work);
#ifdef CONFIG_BPF_SYSCALL #ifdef CONFIG_BPF_SYSCALL
bpf_free_kfunc_btf_tab(aux->kfunc_btf_tab); bpf_free_kfunc_btf_tab(aux->kfunc_btf_tab);
bpf_prog_stream_free(aux->prog);
#endif #endif
#ifdef CONFIG_CGROUP_BPF #ifdef CONFIG_CGROUP_BPF
if (aux->cgroup_atype != CGROUP_BPF_ATTACH_TYPE_INVALID) if (aux->cgroup_atype != CGROUP_BPF_ATTACH_TYPE_INVALID)
@ -3034,6 +3050,21 @@ bool __weak bpf_jit_needs_zext(void)
return false; return false;
} }
/* By default, enable the verifier's mitigations against Spectre v1 and v4 for
* all archs. The value returned must not change at runtime as there is
* currently no support for reloading programs that were loaded without
* mitigations.
*/
bool __weak bpf_jit_bypass_spec_v1(void)
{
return false;
}
bool __weak bpf_jit_bypass_spec_v4(void)
{
return false;
}
/* Return true if the JIT inlines the call to the helper corresponding to /* Return true if the JIT inlines the call to the helper corresponding to
* the imm. * the imm.
* *
@ -3144,6 +3175,22 @@ u64 __weak arch_bpf_timed_may_goto(void)
return 0; return 0;
} }
static noinline void bpf_prog_report_may_goto_violation(void)
{
#ifdef CONFIG_BPF_SYSCALL
struct bpf_stream_stage ss;
struct bpf_prog *prog;
prog = bpf_prog_find_from_stack();
if (!prog)
return;
bpf_stream_stage(ss, prog, BPF_STDERR, ({
bpf_stream_printk(ss, "ERROR: Timeout detected for may_goto instruction\n");
bpf_stream_dump_stack(ss);
}));
#endif
}
u64 bpf_check_timed_may_goto(struct bpf_timed_may_goto *p) u64 bpf_check_timed_may_goto(struct bpf_timed_may_goto *p)
{ {
u64 time = ktime_get_mono_fast_ns(); u64 time = ktime_get_mono_fast_ns();
@ -3154,8 +3201,10 @@ u64 bpf_check_timed_may_goto(struct bpf_timed_may_goto *p)
return BPF_MAX_TIMED_LOOPS; return BPF_MAX_TIMED_LOOPS;
} }
/* Check if we've exhausted our time slice, and zero count. */ /* Check if we've exhausted our time slice, and zero count. */
if (time - p->timestamp >= (NSEC_PER_SEC / 4)) if (unlikely(time - p->timestamp >= (NSEC_PER_SEC / 4))) {
bpf_prog_report_may_goto_violation();
return 0; return 0;
}
/* Refresh the count for the stack frame. */ /* Refresh the count for the stack frame. */
return BPF_MAX_TIMED_LOOPS; return BPF_MAX_TIMED_LOOPS;
} }
@ -3192,3 +3241,85 @@ EXPORT_SYMBOL(bpf_stats_enabled_key);
EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_exception); EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_exception);
EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_bulk_tx); EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_bulk_tx);
#ifdef CONFIG_BPF_SYSCALL
int bpf_prog_get_file_line(struct bpf_prog *prog, unsigned long ip, const char **filep,
const char **linep, int *nump)
{
int idx = -1, insn_start, insn_end, len;
struct bpf_line_info *linfo;
void **jited_linfo;
struct btf *btf;
int nr_linfo;
btf = prog->aux->btf;
linfo = prog->aux->linfo;
jited_linfo = prog->aux->jited_linfo;
if (!btf || !linfo || !jited_linfo)
return -EINVAL;
len = prog->aux->func ? prog->aux->func[prog->aux->func_idx]->len : prog->len;
linfo = &prog->aux->linfo[prog->aux->linfo_idx];
jited_linfo = &prog->aux->jited_linfo[prog->aux->linfo_idx];
insn_start = linfo[0].insn_off;
insn_end = insn_start + len;
nr_linfo = prog->aux->nr_linfo - prog->aux->linfo_idx;
for (int i = 0; i < nr_linfo &&
linfo[i].insn_off >= insn_start && linfo[i].insn_off < insn_end; i++) {
if (jited_linfo[i] >= (void *)ip)
break;
idx = i;
}
if (idx == -1)
return -ENOENT;
/* Get base component of the file path. */
*filep = btf_name_by_offset(btf, linfo[idx].file_name_off);
*filep = kbasename(*filep);
/* Obtain the source line, and strip whitespace in prefix. */
*linep = btf_name_by_offset(btf, linfo[idx].line_off);
while (isspace(**linep))
*linep += 1;
*nump = BPF_LINE_INFO_LINE_NUM(linfo[idx].line_col);
return 0;
}
struct walk_stack_ctx {
struct bpf_prog *prog;
};
static bool find_from_stack_cb(void *cookie, u64 ip, u64 sp, u64 bp)
{
struct walk_stack_ctx *ctxp = cookie;
struct bpf_prog *prog;
/*
* The RCU read lock is held to safely traverse the latch tree, but we
* don't need its protection when accessing the prog, since it has an
* active stack frame on the current stack trace, and won't disappear.
*/
rcu_read_lock();
prog = bpf_prog_ksym_find(ip);
rcu_read_unlock();
if (!prog)
return true;
if (bpf_is_subprog(prog))
return true;
ctxp->prog = prog;
return false;
}
struct bpf_prog *bpf_prog_find_from_stack(void)
{
struct walk_stack_ctx ctx = {};
arch_bpf_stack_walk(find_from_stack_cb, &ctx);
return ctx.prog;
}
#endif

View file

@ -24,6 +24,7 @@
#include <linux/bpf_mem_alloc.h> #include <linux/bpf_mem_alloc.h>
#include <linux/kasan.h> #include <linux/kasan.h>
#include <linux/bpf_verifier.h> #include <linux/bpf_verifier.h>
#include <linux/uaccess.h>
#include "../../lib/kstrtox.h" #include "../../lib/kstrtox.h"
@ -763,22 +764,13 @@ static int bpf_trace_copy_string(char *buf, void *unsafe_ptr, char fmt_ptype,
return -EINVAL; return -EINVAL;
} }
/* Per-cpu temp buffers used by printf-like helpers to store the bprintf binary
* arguments representation.
*/
#define MAX_BPRINTF_BIN_ARGS 512
/* Support executing three nested bprintf helper calls on a given CPU */ /* Support executing three nested bprintf helper calls on a given CPU */
#define MAX_BPRINTF_NEST_LEVEL 3 #define MAX_BPRINTF_NEST_LEVEL 3
struct bpf_bprintf_buffers {
char bin_args[MAX_BPRINTF_BIN_ARGS];
char buf[MAX_BPRINTF_BUF];
};
static DEFINE_PER_CPU(struct bpf_bprintf_buffers[MAX_BPRINTF_NEST_LEVEL], bpf_bprintf_bufs); static DEFINE_PER_CPU(struct bpf_bprintf_buffers[MAX_BPRINTF_NEST_LEVEL], bpf_bprintf_bufs);
static DEFINE_PER_CPU(int, bpf_bprintf_nest_level); static DEFINE_PER_CPU(int, bpf_bprintf_nest_level);
static int try_get_buffers(struct bpf_bprintf_buffers **bufs) int bpf_try_get_buffers(struct bpf_bprintf_buffers **bufs)
{ {
int nest_level; int nest_level;
@ -794,16 +786,21 @@ static int try_get_buffers(struct bpf_bprintf_buffers **bufs)
return 0; return 0;
} }
void bpf_bprintf_cleanup(struct bpf_bprintf_data *data) void bpf_put_buffers(void)
{ {
if (!data->bin_args && !data->buf)
return;
if (WARN_ON_ONCE(this_cpu_read(bpf_bprintf_nest_level) == 0)) if (WARN_ON_ONCE(this_cpu_read(bpf_bprintf_nest_level) == 0))
return; return;
this_cpu_dec(bpf_bprintf_nest_level); this_cpu_dec(bpf_bprintf_nest_level);
preempt_enable(); preempt_enable();
} }
void bpf_bprintf_cleanup(struct bpf_bprintf_data *data)
{
if (!data->bin_args && !data->buf)
return;
bpf_put_buffers();
}
/* /*
* bpf_bprintf_prepare - Generic pass on format strings for bprintf-like helpers * bpf_bprintf_prepare - Generic pass on format strings for bprintf-like helpers
* *
@ -818,7 +815,7 @@ void bpf_bprintf_cleanup(struct bpf_bprintf_data *data)
* In argument preparation mode, if 0 is returned, safe temporary buffers are * In argument preparation mode, if 0 is returned, safe temporary buffers are
* allocated and bpf_bprintf_cleanup should be called to free them after use. * allocated and bpf_bprintf_cleanup should be called to free them after use.
*/ */
int bpf_bprintf_prepare(char *fmt, u32 fmt_size, const u64 *raw_args, int bpf_bprintf_prepare(const char *fmt, u32 fmt_size, const u64 *raw_args,
u32 num_args, struct bpf_bprintf_data *data) u32 num_args, struct bpf_bprintf_data *data)
{ {
bool get_buffers = (data->get_bin_args && num_args) || data->get_buf; bool get_buffers = (data->get_bin_args && num_args) || data->get_buf;
@ -834,7 +831,7 @@ int bpf_bprintf_prepare(char *fmt, u32 fmt_size, const u64 *raw_args,
return -EINVAL; return -EINVAL;
fmt_size = fmt_end - fmt; fmt_size = fmt_end - fmt;
if (get_buffers && try_get_buffers(&buffers)) if (get_buffers && bpf_try_get_buffers(&buffers))
return -EBUSY; return -EBUSY;
if (data->get_bin_args) { if (data->get_bin_args) {
@ -2911,6 +2908,52 @@ __bpf_kfunc int bpf_dynptr_copy(struct bpf_dynptr *dst_ptr, u32 dst_off,
return 0; return 0;
} }
/**
* bpf_dynptr_memset() - Fill dynptr memory with a constant byte.
* @p: Destination dynptr - where data will be filled
* @offset: Offset into the dynptr to start filling from
* @size: Number of bytes to fill
* @val: Constant byte to fill the memory with
*
* Fills the @size bytes of the memory area pointed to by @p
* at @offset with the constant byte @val.
* Returns 0 on success; negative error, otherwise.
*/
__bpf_kfunc int bpf_dynptr_memset(struct bpf_dynptr *p, u32 offset, u32 size, u8 val)
{
struct bpf_dynptr_kern *ptr = (struct bpf_dynptr_kern *)p;
u32 chunk_sz, write_off;
char buf[256];
void* slice;
int err;
slice = bpf_dynptr_slice_rdwr(p, offset, NULL, size);
if (likely(slice)) {
memset(slice, val, size);
return 0;
}
if (__bpf_dynptr_is_rdonly(ptr))
return -EINVAL;
err = bpf_dynptr_check_off_len(ptr, offset, size);
if (err)
return err;
/* Non-linear data under the dynptr, write from a local buffer */
chunk_sz = min_t(u32, sizeof(buf), size);
memset(buf, val, chunk_sz);
for (write_off = 0; write_off < size; write_off += chunk_sz) {
chunk_sz = min_t(u32, sizeof(buf), size - write_off);
err = __bpf_dynptr_write(ptr, offset + write_off, buf, chunk_sz, 0);
if (err)
return err;
}
return 0;
}
__bpf_kfunc void *bpf_cast_to_kern_ctx(void *obj) __bpf_kfunc void *bpf_cast_to_kern_ctx(void *obj)
{ {
return obj; return obj;
@ -2943,9 +2986,16 @@ static bool bpf_stack_walker(void *cookie, u64 ip, u64 sp, u64 bp)
struct bpf_throw_ctx *ctx = cookie; struct bpf_throw_ctx *ctx = cookie;
struct bpf_prog *prog; struct bpf_prog *prog;
if (!is_bpf_text_address(ip)) /*
return !ctx->cnt; * The RCU read lock is held to safely traverse the latch tree, but we
* don't need its protection when accessing the prog, since it has an
* active stack frame on the current stack trace, and won't disappear.
*/
rcu_read_lock();
prog = bpf_prog_ksym_find(ip); prog = bpf_prog_ksym_find(ip);
rcu_read_unlock();
if (!prog)
return !ctx->cnt;
ctx->cnt++; ctx->cnt++;
if (bpf_is_subprog(prog)) if (bpf_is_subprog(prog))
return true; return true;
@ -3283,6 +3333,376 @@ __bpf_kfunc void __bpf_trap(void)
{ {
} }
/*
* Kfuncs for string operations.
*
* Since strings are not necessarily %NUL-terminated, we cannot directly call
* in-kernel implementations. Instead, we open-code the implementations using
* __get_kernel_nofault instead of plain dereference to make them safe.
*/
/**
* bpf_strcmp - Compare two strings
* @s1__ign: One string
* @s2__ign: Another string
*
* Return:
* * %0 - Strings are equal
* * %-1 - @s1__ign is smaller
* * %1 - @s2__ign is smaller
* * %-EFAULT - Cannot read one of the strings
* * %-E2BIG - One of strings is too large
* * %-ERANGE - One of strings is outside of kernel address space
*/
__bpf_kfunc int bpf_strcmp(const char *s1__ign, const char *s2__ign)
{
char c1, c2;
int i;
if (!copy_from_kernel_nofault_allowed(s1__ign, 1) ||
!copy_from_kernel_nofault_allowed(s2__ign, 1)) {
return -ERANGE;
}
guard(pagefault)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
__get_kernel_nofault(&c1, s1__ign, char, err_out);
__get_kernel_nofault(&c2, s2__ign, char, err_out);
if (c1 != c2)
return c1 < c2 ? -1 : 1;
if (c1 == '\0')
return 0;
s1__ign++;
s2__ign++;
}
return -E2BIG;
err_out:
return -EFAULT;
}
/**
* bpf_strnchr - Find a character in a length limited string
* @s__ign: The string to be searched
* @count: The number of characters to be searched
* @c: The character to search for
*
* Note that the %NUL-terminator is considered part of the string, and can
* be searched for.
*
* Return:
* * >=0 - Index of the first occurrence of @c within @s__ign
* * %-ENOENT - @c not found in the first @count characters of @s__ign
* * %-EFAULT - Cannot read @s__ign
* * %-E2BIG - @s__ign is too large
* * %-ERANGE - @s__ign is outside of kernel address space
*/
__bpf_kfunc int bpf_strnchr(const char *s__ign, size_t count, char c)
{
char sc;
int i;
if (!copy_from_kernel_nofault_allowed(s__ign, 1))
return -ERANGE;
guard(pagefault)();
for (i = 0; i < count && i < XATTR_SIZE_MAX; i++) {
__get_kernel_nofault(&sc, s__ign, char, err_out);
if (sc == c)
return i;
if (sc == '\0')
return -ENOENT;
s__ign++;
}
return i == XATTR_SIZE_MAX ? -E2BIG : -ENOENT;
err_out:
return -EFAULT;
}
/**
* bpf_strchr - Find the first occurrence of a character in a string
* @s__ign: The string to be searched
* @c: The character to search for
*
* Note that the %NUL-terminator is considered part of the string, and can
* be searched for.
*
* Return:
* * >=0 - The index of the first occurrence of @c within @s__ign
* * %-ENOENT - @c not found in @s__ign
* * %-EFAULT - Cannot read @s__ign
* * %-E2BIG - @s__ign is too large
* * %-ERANGE - @s__ign is outside of kernel address space
*/
__bpf_kfunc int bpf_strchr(const char *s__ign, char c)
{
return bpf_strnchr(s__ign, XATTR_SIZE_MAX, c);
}
/**
* bpf_strchrnul - Find and return a character in a string, or end of string
* @s__ign: The string to be searched
* @c: The character to search for
*
* Return:
* * >=0 - Index of the first occurrence of @c within @s__ign or index of
* the null byte at the end of @s__ign when @c is not found
* * %-EFAULT - Cannot read @s__ign
* * %-E2BIG - @s__ign is too large
* * %-ERANGE - @s__ign is outside of kernel address space
*/
__bpf_kfunc int bpf_strchrnul(const char *s__ign, char c)
{
char sc;
int i;
if (!copy_from_kernel_nofault_allowed(s__ign, 1))
return -ERANGE;
guard(pagefault)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
__get_kernel_nofault(&sc, s__ign, char, err_out);
if (sc == '\0' || sc == c)
return i;
s__ign++;
}
return -E2BIG;
err_out:
return -EFAULT;
}
/**
* bpf_strrchr - Find the last occurrence of a character in a string
* @s__ign: The string to be searched
* @c: The character to search for
*
* Return:
* * >=0 - Index of the last occurrence of @c within @s__ign
* * %-ENOENT - @c not found in @s__ign
* * %-EFAULT - Cannot read @s__ign
* * %-E2BIG - @s__ign is too large
* * %-ERANGE - @s__ign is outside of kernel address space
*/
__bpf_kfunc int bpf_strrchr(const char *s__ign, int c)
{
char sc;
int i, last = -ENOENT;
if (!copy_from_kernel_nofault_allowed(s__ign, 1))
return -ERANGE;
guard(pagefault)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
__get_kernel_nofault(&sc, s__ign, char, err_out);
if (sc == c)
last = i;
if (sc == '\0')
return last;
s__ign++;
}
return -E2BIG;
err_out:
return -EFAULT;
}
/**
* bpf_strnlen - Calculate the length of a length-limited string
* @s__ign: The string
* @count: The maximum number of characters to count
*
* Return:
* * >=0 - The length of @s__ign
* * %-EFAULT - Cannot read @s__ign
* * %-E2BIG - @s__ign is too large
* * %-ERANGE - @s__ign is outside of kernel address space
*/
__bpf_kfunc int bpf_strnlen(const char *s__ign, size_t count)
{
char c;
int i;
if (!copy_from_kernel_nofault_allowed(s__ign, 1))
return -ERANGE;
guard(pagefault)();
for (i = 0; i < count && i < XATTR_SIZE_MAX; i++) {
__get_kernel_nofault(&c, s__ign, char, err_out);
if (c == '\0')
return i;
s__ign++;
}
return i == XATTR_SIZE_MAX ? -E2BIG : i;
err_out:
return -EFAULT;
}
/**
* bpf_strlen - Calculate the length of a string
* @s__ign: The string
*
* Return:
* * >=0 - The length of @s__ign
* * %-EFAULT - Cannot read @s__ign
* * %-E2BIG - @s__ign is too large
* * %-ERANGE - @s__ign is outside of kernel address space
*/
__bpf_kfunc int bpf_strlen(const char *s__ign)
{
return bpf_strnlen(s__ign, XATTR_SIZE_MAX);
}
/**
* bpf_strspn - Calculate the length of the initial substring of @s__ign which
* only contains letters in @accept__ign
* @s__ign: The string to be searched
* @accept__ign: The string to search for
*
* Return:
* * >=0 - The length of the initial substring of @s__ign which only
* contains letters from @accept__ign
* * %-EFAULT - Cannot read one of the strings
* * %-E2BIG - One of the strings is too large
* * %-ERANGE - One of the strings is outside of kernel address space
*/
__bpf_kfunc int bpf_strspn(const char *s__ign, const char *accept__ign)
{
char cs, ca;
int i, j;
if (!copy_from_kernel_nofault_allowed(s__ign, 1) ||
!copy_from_kernel_nofault_allowed(accept__ign, 1)) {
return -ERANGE;
}
guard(pagefault)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
__get_kernel_nofault(&cs, s__ign, char, err_out);
if (cs == '\0')
return i;
for (j = 0; j < XATTR_SIZE_MAX; j++) {
__get_kernel_nofault(&ca, accept__ign + j, char, err_out);
if (cs == ca || ca == '\0')
break;
}
if (j == XATTR_SIZE_MAX)
return -E2BIG;
if (ca == '\0')
return i;
s__ign++;
}
return -E2BIG;
err_out:
return -EFAULT;
}
/**
* bpf_strcspn - Calculate the length of the initial substring of @s__ign which
* does not contain letters in @reject__ign
* @s__ign: The string to be searched
* @reject__ign: The string to search for
*
* Return:
* * >=0 - The length of the initial substring of @s__ign which does not
* contain letters from @reject__ign
* * %-EFAULT - Cannot read one of the strings
* * %-E2BIG - One of the strings is too large
* * %-ERANGE - One of the strings is outside of kernel address space
*/
__bpf_kfunc int bpf_strcspn(const char *s__ign, const char *reject__ign)
{
char cs, cr;
int i, j;
if (!copy_from_kernel_nofault_allowed(s__ign, 1) ||
!copy_from_kernel_nofault_allowed(reject__ign, 1)) {
return -ERANGE;
}
guard(pagefault)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
__get_kernel_nofault(&cs, s__ign, char, err_out);
if (cs == '\0')
return i;
for (j = 0; j < XATTR_SIZE_MAX; j++) {
__get_kernel_nofault(&cr, reject__ign + j, char, err_out);
if (cs == cr || cr == '\0')
break;
}
if (j == XATTR_SIZE_MAX)
return -E2BIG;
if (cr != '\0')
return i;
s__ign++;
}
return -E2BIG;
err_out:
return -EFAULT;
}
/**
* bpf_strnstr - Find the first substring in a length-limited string
* @s1__ign: The string to be searched
* @s2__ign: The string to search for
* @len: the maximum number of characters to search
*
* Return:
* * >=0 - Index of the first character of the first occurrence of @s2__ign
* within the first @len characters of @s1__ign
* * %-ENOENT - @s2__ign not found in the first @len characters of @s1__ign
* * %-EFAULT - Cannot read one of the strings
* * %-E2BIG - One of the strings is too large
* * %-ERANGE - One of the strings is outside of kernel address space
*/
__bpf_kfunc int bpf_strnstr(const char *s1__ign, const char *s2__ign, size_t len)
{
char c1, c2;
int i, j;
if (!copy_from_kernel_nofault_allowed(s1__ign, 1) ||
!copy_from_kernel_nofault_allowed(s2__ign, 1)) {
return -ERANGE;
}
guard(pagefault)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
for (j = 0; i + j < len && j < XATTR_SIZE_MAX; j++) {
__get_kernel_nofault(&c2, s2__ign + j, char, err_out);
if (c2 == '\0')
return i;
__get_kernel_nofault(&c1, s1__ign + j, char, err_out);
if (c1 == '\0')
return -ENOENT;
if (c1 != c2)
break;
}
if (j == XATTR_SIZE_MAX)
return -E2BIG;
if (i + j == len)
return -ENOENT;
s1__ign++;
}
return -E2BIG;
err_out:
return -EFAULT;
}
/**
* bpf_strstr - Find the first substring in a string
* @s1__ign: The string to be searched
* @s2__ign: The string to search for
*
* Return:
* * >=0 - Index of the first character of the first occurrence of @s2__ign
* within @s1__ign
* * %-ENOENT - @s2__ign is not a substring of @s1__ign
* * %-EFAULT - Cannot read one of the strings
* * %-E2BIG - One of the strings is too large
* * %-ERANGE - One of the strings is outside of kernel address space
*/
__bpf_kfunc int bpf_strstr(const char *s1__ign, const char *s2__ign)
{
return bpf_strnstr(s1__ign, s2__ign, XATTR_SIZE_MAX);
}
__bpf_kfunc_end_defs(); __bpf_kfunc_end_defs();
BTF_KFUNCS_START(generic_btf_ids) BTF_KFUNCS_START(generic_btf_ids)
@ -3369,6 +3789,7 @@ BTF_ID_FLAGS(func, bpf_dynptr_is_rdonly)
BTF_ID_FLAGS(func, bpf_dynptr_size) BTF_ID_FLAGS(func, bpf_dynptr_size)
BTF_ID_FLAGS(func, bpf_dynptr_clone) BTF_ID_FLAGS(func, bpf_dynptr_clone)
BTF_ID_FLAGS(func, bpf_dynptr_copy) BTF_ID_FLAGS(func, bpf_dynptr_copy)
BTF_ID_FLAGS(func, bpf_dynptr_memset)
#ifdef CONFIG_NET #ifdef CONFIG_NET
BTF_ID_FLAGS(func, bpf_modify_return_test_tp) BTF_ID_FLAGS(func, bpf_modify_return_test_tp)
#endif #endif
@ -3402,9 +3823,21 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
#endif #endif
BTF_ID_FLAGS(func, __bpf_trap) BTF_ID_FLAGS(func, __bpf_trap)
#ifdef CONFIG_CGROUPS BTF_ID_FLAGS(func, bpf_strcmp);
BTF_ID_FLAGS(func, bpf_strchr);
BTF_ID_FLAGS(func, bpf_strchrnul);
BTF_ID_FLAGS(func, bpf_strnchr);
BTF_ID_FLAGS(func, bpf_strrchr);
BTF_ID_FLAGS(func, bpf_strlen);
BTF_ID_FLAGS(func, bpf_strnlen);
BTF_ID_FLAGS(func, bpf_strspn);
BTF_ID_FLAGS(func, bpf_strcspn);
BTF_ID_FLAGS(func, bpf_strstr);
BTF_ID_FLAGS(func, bpf_strnstr);
#if defined(CONFIG_BPF_LSM) && defined(CONFIG_CGROUPS)
BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU) BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
#endif #endif
BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_TRUSTED_ARGS)
BTF_KFUNCS_END(common_btf_ids) BTF_KFUNCS_END(common_btf_ids)
static const struct btf_kfunc_id_set common_kfunc_set = { static const struct btf_kfunc_id_set common_kfunc_set = {

View file

@ -78,8 +78,7 @@ static const struct seq_operations bpf_link_seq_ops = {
.show = bpf_link_seq_show, .show = bpf_link_seq_show,
}; };
BTF_ID_LIST(btf_bpf_link_id) BTF_ID_LIST_SINGLE(btf_bpf_link_id, struct, bpf_link)
BTF_ID(struct, bpf_link)
static const struct bpf_iter_seq_info bpf_link_seq_info = { static const struct bpf_iter_seq_info bpf_link_seq_info = {
.seq_ops = &bpf_link_seq_ops, .seq_ops = &bpf_link_seq_ops,

View file

@ -394,17 +394,10 @@ static int cgroup_storage_check_btf(const struct bpf_map *map,
if (!btf_member_is_reg_int(btf, key_type, m, offset, size)) if (!btf_member_is_reg_int(btf, key_type, m, offset, size))
return -EINVAL; return -EINVAL;
} else { } else {
u32 int_data;
/* /*
* Key is expected to be u64, which stores the cgroup_inode_id * Key is expected to be u64, which stores the cgroup_inode_id
*/ */
if (!btf_type_is_i64(key_type))
if (BTF_INFO_KIND(key_type->info) != BTF_KIND_INT)
return -EINVAL;
int_data = *(u32 *)(key_type + 1);
if (BTF_INT_BITS(int_data) != 64 || BTF_INT_OFFSET(int_data))
return -EINVAL; return -EINVAL;
} }

View file

@ -11,8 +11,6 @@
struct bpf_netns_link { struct bpf_netns_link {
struct bpf_link link; struct bpf_link link;
enum bpf_attach_type type;
enum netns_bpf_attach_type netns_type;
/* We don't hold a ref to net in order to auto-detach the link /* We don't hold a ref to net in order to auto-detach the link
* when netns is going away. Instead we rely on pernet * when netns is going away. Instead we rely on pernet
@ -21,6 +19,7 @@ struct bpf_netns_link {
*/ */
struct net *net; struct net *net;
struct list_head node; /* node in list of links attached to net */ struct list_head node; /* node in list of links attached to net */
enum netns_bpf_attach_type netns_type;
}; };
/* Protects updates to netns_bpf */ /* Protects updates to netns_bpf */
@ -216,7 +215,7 @@ static int bpf_netns_link_fill_info(const struct bpf_link *link,
mutex_unlock(&netns_bpf_mutex); mutex_unlock(&netns_bpf_mutex);
info->netns.netns_ino = inum; info->netns.netns_ino = inum;
info->netns.attach_type = net_link->type; info->netns.attach_type = link->attach_type;
return 0; return 0;
} }
@ -230,7 +229,7 @@ static void bpf_netns_link_show_fdinfo(const struct bpf_link *link,
"netns_ino:\t%u\n" "netns_ino:\t%u\n"
"attach_type:\t%u\n", "attach_type:\t%u\n",
info.netns.netns_ino, info.netns.netns_ino,
info.netns.attach_type); link->attach_type);
} }
static const struct bpf_link_ops bpf_netns_link_ops = { static const struct bpf_link_ops bpf_netns_link_ops = {
@ -501,9 +500,8 @@ int netns_bpf_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
goto out_put_net; goto out_put_net;
} }
bpf_link_init(&net_link->link, BPF_LINK_TYPE_NETNS, bpf_link_init(&net_link->link, BPF_LINK_TYPE_NETNS,
&bpf_netns_link_ops, prog); &bpf_netns_link_ops, prog, type);
net_link->net = net; net_link->net = net;
net_link->type = type;
net_link->netns_type = netns_type; net_link->netns_type = netns_type;
err = bpf_link_prime(&net_link->link, &link_primer); err = bpf_link_prime(&net_link->link, &link_primer);

View file

@ -1,8 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
config USERMODE_DRIVER
bool
default n
menuconfig BPF_PRELOAD menuconfig BPF_PRELOAD
bool "Preload BPF file system with kernel specific program and map iterators" bool "Preload BPF file system with kernel specific program and map iterators"
depends on BPF depends on BPF
@ -10,7 +6,6 @@ menuconfig BPF_PRELOAD
# The dependency on !COMPILE_TEST prevents it from being enabled # The dependency on !COMPILE_TEST prevents it from being enabled
# in allmodconfig or allyesconfig configurations # in allmodconfig or allyesconfig configurations
depends on !COMPILE_TEST depends on !COMPILE_TEST
select USERMODE_DRIVER
help help
This builds kernel module with several embedded BPF programs that are This builds kernel module with several embedded BPF programs that are
pinned into BPF FS mount point as human readable files that are pinned into BPF FS mount point as human readable files that are

View file

@ -89,10 +89,7 @@ iterators_bpf__load(struct iterators_bpf *skel)
{ {
struct bpf_load_and_run_opts opts = {}; struct bpf_load_and_run_opts opts = {};
int err; int err;
static const char opts_data[] __attribute__((__aligned__(8))) = "\
opts.ctx = (struct bpf_loader_ctx *)skel;
opts.data_sz = 6008;
opts.data = (void *)"\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
@ -126,190 +123,196 @@ iterators_bpf__load(struct iterators_bpf *skel)
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xeb\x9f\x01\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xeb\x9f\x01\0\
\0\0\0\x18\0\0\0\0\0\0\x04\x1c\0\0\x04\x1c\0\0\x05\x18\0\0\0\0\x02\0\0\0\0\0\0\ \0\0\0\x18\0\0\0\0\0\0\x04\x80\0\0\x04\x80\0\0\x05\x44\0\0\0\0\x02\0\0\0\0\0\0\
\x02\0\0\0\x01\x04\0\0\x02\0\0\0\x10\0\0\0\x13\0\0\0\x03\0\0\0\0\0\0\0\x18\0\0\ \x02\0\0\0\x01\x04\0\0\x02\0\0\0\x10\0\0\0\x13\0\0\0\x03\0\0\0\0\0\0\0\x18\0\0\
\0\x04\0\0\0\x40\0\0\0\0\x02\0\0\0\0\0\0\x08\0\0\0\0\x02\0\0\0\0\0\0\x0d\0\0\0\ \0\x04\0\0\0\x40\0\0\0\0\x02\0\0\0\0\0\0\x08\0\0\0\0\x02\0\0\0\0\0\0\x0d\0\0\0\
\0\x0d\0\0\x01\0\0\0\x06\0\0\0\x1c\0\0\0\x01\0\0\0\x20\x01\0\0\0\0\0\0\x04\x01\ \0\x0d\0\0\x01\0\0\0\x06\0\0\0\x1c\0\0\0\x01\0\0\0\x20\x01\0\0\0\0\0\0\x04\x01\
\0\0\x20\0\0\0\x24\x0c\0\0\x01\0\0\0\x05\0\0\0\xc2\x04\0\0\x03\0\0\0\x18\0\0\0\ \0\0\x20\0\0\0\x24\x0c\0\0\x01\0\0\0\x05\0\0\0\xc3\x04\0\0\x03\0\0\0\x18\0\0\0\
\xd0\0\0\0\x09\0\0\0\0\0\0\0\xd4\0\0\0\x0b\0\0\0\x40\0\0\0\xdf\0\0\0\x0b\0\0\0\ \xd1\0\0\0\x09\0\0\0\0\0\0\0\xd5\0\0\0\x0b\0\0\0\x40\0\0\0\xe0\0\0\0\x0b\0\0\0\
\x80\0\0\0\0\x02\0\0\0\0\0\0\x0a\0\0\0\xe7\x07\0\0\0\0\0\0\0\0\0\0\xf0\x08\0\0\ \x80\0\0\0\0\x02\0\0\0\0\0\0\x0a\0\0\0\xe8\x07\0\0\0\0\0\0\0\0\0\0\xf1\x08\0\0\
\0\0\0\0\x0c\0\0\0\xf6\x01\0\0\0\0\0\0\x08\0\0\0\x40\0\0\x01\xb3\x04\0\0\x03\0\ \0\0\0\0\x0c\0\0\0\xf7\x01\0\0\0\0\0\0\x08\0\0\0\x40\0\0\x01\xc1\x04\0\0\x03\0\
\0\0\x18\0\0\x01\xbb\0\0\0\x0e\0\0\0\0\0\0\x01\xbe\0\0\0\x11\0\0\0\x20\0\0\x01\ \0\0\x18\0\0\x01\xc9\0\0\0\x0e\0\0\0\0\0\0\x01\xcc\0\0\0\x11\0\0\0\x20\0\0\x01\
\xc3\0\0\0\x0e\0\0\0\xa0\0\0\x01\xcf\x08\0\0\0\0\0\0\x0f\0\0\x01\xd5\x01\0\0\0\ \xd1\0\0\0\x0e\0\0\0\xa0\0\0\x01\xdd\x08\0\0\0\0\0\0\x0f\0\0\x01\xe3\x01\0\0\0\
\0\0\0\x04\0\0\0\x20\0\0\x01\xe2\x01\0\0\0\0\0\0\x01\x01\0\0\x08\0\0\0\0\x03\0\ \0\0\0\x04\0\0\0\x20\0\0\x01\xf0\x01\0\0\0\0\0\0\x01\x01\0\0\x08\0\0\0\0\x03\0\
\0\0\0\0\0\0\0\0\0\x10\0\0\0\x12\0\0\0\x10\0\0\x01\xe7\x01\0\0\0\0\0\0\x04\0\0\ \0\0\0\0\0\0\0\0\0\x10\0\0\0\x12\0\0\0\x10\0\0\x01\xf5\x01\0\0\0\0\0\0\x04\0\0\
\0\x20\0\0\0\0\x02\0\0\0\0\0\0\x14\0\0\x02\x4b\x04\0\0\x02\0\0\0\x10\0\0\0\x13\ \0\x20\0\0\0\0\x0d\0\0\x01\0\0\0\x14\0\0\x05\x39\0\0\0\x04\0\0\x02\x3e\x08\0\0\
\0\0\0\x03\0\0\0\0\0\0\x02\x5e\0\0\0\x15\0\0\0\x40\0\0\0\0\x02\0\0\0\0\0\0\x18\ \0\0\0\0\x15\0\0\x02\x44\x01\0\0\0\0\0\0\x08\x01\0\0\x40\0\0\x02\x4e\x0c\0\0\
\0\0\0\0\x0d\0\0\x01\0\0\0\x06\0\0\0\x1c\0\0\0\x13\0\0\x02\x63\x0c\0\0\x01\0\0\ \x01\0\0\0\x13\0\0\0\0\x02\0\0\0\0\0\0\x18\0\0\x02\x65\x04\0\0\x02\0\0\0\x10\0\
\0\x16\0\0\x02\xaf\x04\0\0\x01\0\0\0\x08\0\0\x02\xb8\0\0\0\x19\0\0\0\0\0\0\0\0\ \0\0\x13\0\0\0\x03\0\0\0\0\0\0\x02\x78\0\0\0\x19\0\0\0\x40\0\0\0\0\x02\0\0\0\0\
\x02\0\0\0\0\0\0\x1a\0\0\x03\x09\x04\0\0\x06\0\0\0\x38\0\0\x01\xbb\0\0\0\x0e\0\ \0\0\x1c\0\0\0\0\x0d\0\0\x01\0\0\0\x06\0\0\0\x1c\0\0\0\x17\0\0\x02\x7d\x0c\0\0\
\0\0\0\0\0\x01\xbe\0\0\0\x11\0\0\0\x20\0\0\x03\x16\0\0\0\x1b\0\0\0\xc0\0\0\x03\ \x01\0\0\0\x1a\0\0\x02\xc9\x04\0\0\x01\0\0\0\x08\0\0\x02\xd2\0\0\0\x1d\0\0\0\0\
\x27\0\0\0\x15\0\0\x01\0\0\0\x03\x30\0\0\0\x1d\0\0\x01\x40\0\0\x03\x3a\0\0\0\ \0\0\0\0\x02\0\0\0\0\0\0\x1e\0\0\x03\x23\x04\0\0\x06\0\0\0\x38\0\0\x01\xc9\0\0\
\x1e\0\0\x01\x80\0\0\0\0\x02\0\0\0\0\0\0\x1c\0\0\0\0\x0a\0\0\0\0\0\0\x10\0\0\0\ \0\x0e\0\0\0\0\0\0\x01\xcc\0\0\0\x11\0\0\0\x20\0\0\x03\x30\0\0\0\x1f\0\0\0\xc0\
\0\x02\0\0\0\0\0\0\x1f\0\0\0\0\x02\0\0\0\0\0\0\x20\0\0\x03\x84\x04\0\0\x02\0\0\ \0\0\x03\x41\0\0\0\x19\0\0\x01\0\0\0\x03\x4a\0\0\0\x21\0\0\x01\x40\0\0\x03\x54\
\0\x08\0\0\x03\x92\0\0\0\x0e\0\0\0\0\0\0\x03\x9b\0\0\0\x0e\0\0\0\x20\0\0\x03\ \0\0\0\x22\0\0\x01\x80\0\0\0\0\x02\0\0\0\0\0\0\x20\0\0\0\0\x0a\0\0\0\0\0\0\x10\
\x3a\x04\0\0\x03\0\0\0\x18\0\0\x03\xa5\0\0\0\x1b\0\0\0\0\0\0\x03\xad\0\0\0\x21\ \0\0\0\0\x02\0\0\0\0\0\0\x23\0\0\0\0\x02\0\0\0\0\0\0\x24\0\0\x03\x9e\x04\0\0\
\0\0\0\x40\0\0\x03\xb3\0\0\0\x23\0\0\0\x80\0\0\0\0\x02\0\0\0\0\0\0\x22\0\0\0\0\ \x02\0\0\0\x08\0\0\x03\xac\0\0\0\x0e\0\0\0\0\0\0\x03\xb5\0\0\0\x0e\0\0\0\x20\0\
\x02\0\0\0\0\0\0\x24\0\0\x03\xb7\x04\0\0\x01\0\0\0\x04\0\0\x03\xc2\0\0\0\x0e\0\ \0\x03\x54\x04\0\0\x03\0\0\0\x18\0\0\x03\xbf\0\0\0\x1f\0\0\0\0\0\0\x03\xc7\0\0\
\0\0\0\0\0\x04\x2b\x04\0\0\x01\0\0\0\x04\0\0\x04\x34\0\0\0\x0e\0\0\0\0\0\0\0\0\ \0\x25\0\0\0\x40\0\0\x03\xcd\0\0\0\x27\0\0\0\x80\0\0\0\0\x02\0\0\0\0\0\0\x26\0\
\x03\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x12\0\0\0\x23\0\0\x04\xaa\x0e\0\0\0\0\0\0\ \0\0\0\x02\0\0\0\0\0\0\x28\0\0\x03\xd1\x04\0\0\x01\0\0\0\x04\0\0\x03\xdc\0\0\0\
\x25\0\0\0\0\0\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x12\0\0\0\x0e\0\0\x04\ \x0e\0\0\0\0\0\0\x04\x45\x04\0\0\x01\0\0\0\x04\0\0\x04\x4e\0\0\0\x0e\0\0\0\0\0\
\xbe\x0e\0\0\0\0\0\0\x27\0\0\0\0\0\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\x12\ \0\0\0\x03\0\0\0\0\0\0\0\0\0\0\x20\0\0\0\x12\0\0\0\x30\0\0\x04\xc4\x0e\0\0\0\0\
\0\0\0\x20\0\0\x04\xd4\x0e\0\0\0\0\0\0\x29\0\0\0\0\0\0\0\0\x03\0\0\0\0\0\0\0\0\ \0\0\x29\0\0\0\0\0\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\x20\0\0\0\x12\0\0\0\x1a\0\0\
\0\0\x1c\0\0\0\x12\0\0\0\x11\0\0\x04\xe9\x0e\0\0\0\0\0\0\x2b\0\0\0\0\0\0\0\0\ \x04\xd8\x0e\0\0\0\0\0\0\x2b\0\0\0\0\0\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\x20\0\0\0\
\x03\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x12\0\0\0\x04\0\0\x05\0\x0e\0\0\0\0\0\0\x2d\ \x12\0\0\0\x20\0\0\x04\xee\x0e\0\0\0\0\0\0\x2d\0\0\0\0\0\0\0\0\x03\0\0\0\0\0\0\
\0\0\0\x01\0\0\x05\x08\x0f\0\0\x04\0\0\0\x62\0\0\0\x26\0\0\0\0\0\0\0\x23\0\0\0\ \0\0\0\0\x20\0\0\0\x12\0\0\0\x11\0\0\x05\x03\x0e\0\0\0\0\0\0\x2f\0\0\0\0\0\0\0\
\x28\0\0\0\x23\0\0\0\x0e\0\0\0\x2a\0\0\0\x31\0\0\0\x20\0\0\0\x2c\0\0\0\x51\0\0\ \0\x03\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\x12\0\0\0\x04\0\0\x05\x1a\x0e\0\0\0\0\0\0\
\0\x11\0\0\x05\x10\x0f\0\0\x01\0\0\0\x04\0\0\0\x2e\0\0\0\0\0\0\0\x04\0\x62\x70\ \x31\0\0\0\x01\0\0\x05\x22\x0f\0\0\x01\0\0\0\x04\0\0\0\x36\0\0\0\0\0\0\0\x04\0\
\x66\x5f\x69\x74\x65\x72\x5f\x5f\x62\x70\x66\x5f\x6d\x61\x70\0\x6d\x65\x74\x61\ \0\x05\x29\x0f\0\0\x04\0\0\0\x7b\0\0\0\x2a\0\0\0\0\0\0\0\x30\0\0\0\x2c\0\0\0\
\0\x6d\x61\x70\0\x63\x74\x78\0\x69\x6e\x74\0\x64\x75\x6d\x70\x5f\x62\x70\x66\ \x30\0\0\0\x1a\0\0\0\x2e\0\0\0\x4a\0\0\0\x20\0\0\0\x30\0\0\0\x6a\0\0\0\x11\0\0\
\x5f\x6d\x61\x70\0\x69\x74\x65\x72\x2f\x62\x70\x66\x5f\x6d\x61\x70\0\x30\x3a\ \x05\x31\x0f\0\0\x01\0\0\0\x04\0\0\0\x32\0\0\0\0\0\0\0\x04\0\0\x05\x39\x0e\0\0\
\x30\0\x2f\x68\x6f\x6d\x65\x2f\x69\x69\x69\x2f\x6c\x69\x6e\x75\x78\x2d\x6b\x65\ \0\0\0\0\x06\0\0\0\x01\0\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\x5f\x62\x70\x66\
\x72\x6e\x65\x6c\x2d\x74\x6f\x6f\x6c\x63\x68\x61\x69\x6e\x2f\x73\x72\x63\x2f\ \x5f\x6d\x61\x70\0\x6d\x65\x74\x61\0\x6d\x61\x70\0\x63\x74\x78\0\x69\x6e\x74\0\
\x6c\x69\x6e\x75\x78\x2f\x6b\x65\x72\x6e\x65\x6c\x2f\x62\x70\x66\x2f\x70\x72\ \x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\0\x69\x74\x65\x72\x2f\x62\x70\
\x65\x6c\x6f\x61\x64\x2f\x69\x74\x65\x72\x61\x74\x6f\x72\x73\x2f\x69\x74\x65\ \x66\x5f\x6d\x61\x70\0\x30\x3a\x30\0\x2f\x68\x6f\x6d\x65\x32\x2f\x69\x69\x69\
\x72\x61\x74\x6f\x72\x73\x2e\x62\x70\x66\x2e\x63\0\x09\x73\x74\x72\x75\x63\x74\ \x2f\x6c\x69\x6e\x75\x78\x2d\x6b\x65\x72\x6e\x65\x6c\x2d\x74\x6f\x6f\x6c\x63\
\x20\x73\x65\x71\x5f\x66\x69\x6c\x65\x20\x2a\x73\x65\x71\x20\x3d\x20\x63\x74\ \x68\x61\x69\x6e\x2f\x73\x72\x63\x2f\x6c\x69\x6e\x75\x78\x2f\x6b\x65\x72\x6e\
\x78\x2d\x3e\x6d\x65\x74\x61\x2d\x3e\x73\x65\x71\x3b\0\x62\x70\x66\x5f\x69\x74\ \x65\x6c\x2f\x62\x70\x66\x2f\x70\x72\x65\x6c\x6f\x61\x64\x2f\x69\x74\x65\x72\
\x65\x72\x5f\x6d\x65\x74\x61\0\x73\x65\x71\0\x73\x65\x73\x73\x69\x6f\x6e\x5f\ \x61\x74\x6f\x72\x73\x2f\x69\x74\x65\x72\x61\x74\x6f\x72\x73\x2e\x62\x70\x66\
\x69\x64\0\x73\x65\x71\x5f\x6e\x75\x6d\0\x73\x65\x71\x5f\x66\x69\x6c\x65\0\x5f\ \x2e\x63\0\x09\x73\x74\x72\x75\x63\x74\x20\x73\x65\x71\x5f\x66\x69\x6c\x65\x20\
\x5f\x75\x36\x34\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x6c\x6f\x6e\x67\x20\x6c\ \x2a\x73\x65\x71\x20\x3d\x20\x63\x74\x78\x2d\x3e\x6d\x65\x74\x61\x2d\x3e\x73\
\x6f\x6e\x67\0\x30\x3a\x31\0\x09\x73\x74\x72\x75\x63\x74\x20\x62\x70\x66\x5f\ \x65\x71\x3b\0\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\x6d\x65\x74\x61\0\x73\x65\
\x6d\x61\x70\x20\x2a\x6d\x61\x70\x20\x3d\x20\x63\x74\x78\x2d\x3e\x6d\x61\x70\ \x71\0\x73\x65\x73\x73\x69\x6f\x6e\x5f\x69\x64\0\x73\x65\x71\x5f\x6e\x75\x6d\0\
\x3b\0\x09\x69\x66\x20\x28\x21\x6d\x61\x70\x29\0\x30\x3a\x32\0\x09\x5f\x5f\x75\ \x73\x65\x71\x5f\x66\x69\x6c\x65\0\x5f\x5f\x75\x36\x34\0\x75\x6e\x73\x69\x67\
\x36\x34\x20\x73\x65\x71\x5f\x6e\x75\x6d\x20\x3d\x20\x63\x74\x78\x2d\x3e\x6d\ \x6e\x65\x64\x20\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\0\x30\x3a\x31\0\x09\x73\
\x65\x74\x61\x2d\x3e\x73\x65\x71\x5f\x6e\x75\x6d\x3b\0\x09\x69\x66\x20\x28\x73\ \x74\x72\x75\x63\x74\x20\x62\x70\x66\x5f\x6d\x61\x70\x20\x2a\x6d\x61\x70\x20\
\x65\x71\x5f\x6e\x75\x6d\x20\x3d\x3d\x20\x30\x29\0\x09\x09\x42\x50\x46\x5f\x53\ \x3d\x20\x63\x74\x78\x2d\x3e\x6d\x61\x70\x3b\0\x09\x69\x66\x20\x28\x21\x6d\x61\
\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x20\x20\x69\ \x70\x29\0\x30\x3a\x32\0\x09\x5f\x5f\x75\x36\x34\x20\x73\x65\x71\x5f\x6e\x75\
\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ \x6d\x20\x3d\x20\x63\x74\x78\x2d\x3e\x6d\x65\x74\x61\x2d\x3e\x73\x65\x71\x5f\
\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\x5c\x6e\x22\x29\x3b\0\x62\x70\x66\ \x6e\x75\x6d\x3b\0\x09\x69\x66\x20\x28\x73\x65\x71\x5f\x6e\x75\x6d\x20\x3d\x3d\
\x5f\x6d\x61\x70\0\x69\x64\0\x6e\x61\x6d\x65\0\x6d\x61\x78\x5f\x65\x6e\x74\x72\ \x20\x30\x29\0\x09\x09\x42\x50\x46\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\
\x69\x65\x73\0\x5f\x5f\x75\x33\x32\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x69\ \x28\x73\x65\x71\x2c\x20\x22\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\
\x6e\x74\0\x63\x68\x61\x72\0\x5f\x5f\x41\x52\x52\x41\x59\x5f\x53\x49\x5a\x45\ \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\
\x5f\x54\x59\x50\x45\x5f\x5f\0\x09\x42\x50\x46\x5f\x53\x45\x51\x5f\x50\x52\x49\ \x65\x73\x20\x20\x63\x75\x72\x5f\x65\x6e\x74\x72\x69\x65\x73\x5c\x6e\x22\x29\
\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x25\x34\x75\x20\x25\x2d\x31\x36\x73\ \x3b\0\x62\x70\x66\x5f\x6d\x61\x70\0\x69\x64\0\x6e\x61\x6d\x65\0\x6d\x61\x78\
\x25\x36\x64\x5c\x6e\x22\x2c\x20\x6d\x61\x70\x2d\x3e\x69\x64\x2c\x20\x6d\x61\ \x5f\x65\x6e\x74\x72\x69\x65\x73\0\x5f\x5f\x75\x33\x32\0\x75\x6e\x73\x69\x67\
\x70\x2d\x3e\x6e\x61\x6d\x65\x2c\x20\x6d\x61\x70\x2d\x3e\x6d\x61\x78\x5f\x65\ \x6e\x65\x64\x20\x69\x6e\x74\0\x63\x68\x61\x72\0\x5f\x5f\x41\x52\x52\x41\x59\
\x6e\x74\x72\x69\x65\x73\x29\x3b\0\x7d\0\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\ \x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x09\x42\x50\x46\x5f\x53\x45\
\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x70\x72\x6f\x67\0\x64\x75\x6d\x70\x5f\ \x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x25\x34\x75\x20\
\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x69\x74\x65\x72\x2f\x62\x70\x66\x5f\x70\x72\ \x25\x2d\x31\x36\x73\x20\x20\x25\x31\x30\x64\x20\x20\x20\x25\x31\x30\x6c\x6c\
\x6f\x67\0\x09\x73\x74\x72\x75\x63\x74\x20\x62\x70\x66\x5f\x70\x72\x6f\x67\x20\ \x64\x5c\x6e\x22\x2c\0\x7d\0\x5f\x5f\x73\x36\x34\0\x6c\x6f\x6e\x67\x20\x6c\x6f\
\x2a\x70\x72\x6f\x67\x20\x3d\x20\x63\x74\x78\x2d\x3e\x70\x72\x6f\x67\x3b\0\x09\ \x6e\x67\0\x62\x70\x66\x5f\x6d\x61\x70\x5f\x73\x75\x6d\x5f\x65\x6c\x65\x6d\x5f\
\x69\x66\x20\x28\x21\x70\x72\x6f\x67\x29\0\x62\x70\x66\x5f\x70\x72\x6f\x67\0\ \x63\x6f\x75\x6e\x74\0\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\x5f\x62\x70\x66\x5f\
\x61\x75\x78\0\x09\x61\x75\x78\x20\x3d\x20\x70\x72\x6f\x67\x2d\x3e\x61\x75\x78\ \x70\x72\x6f\x67\0\x70\x72\x6f\x67\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\
\x3b\0\x09\x09\x42\x50\x46\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\ \x72\x6f\x67\0\x69\x74\x65\x72\x2f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x09\x73\
\x65\x71\x2c\x20\x22\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\ \x74\x72\x75\x63\x74\x20\x62\x70\x66\x5f\x70\x72\x6f\x67\x20\x2a\x70\x72\x6f\
\x20\x20\x20\x20\x20\x20\x20\x20\x61\x74\x74\x61\x63\x68\x65\x64\x5c\x6e\x22\ \x67\x20\x3d\x20\x63\x74\x78\x2d\x3e\x70\x72\x6f\x67\x3b\0\x09\x69\x66\x20\x28\
\x29\x3b\0\x62\x70\x66\x5f\x70\x72\x6f\x67\x5f\x61\x75\x78\0\x61\x74\x74\x61\ \x21\x70\x72\x6f\x67\x29\0\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x61\x75\x78\0\x09\
\x63\x68\x5f\x66\x75\x6e\x63\x5f\x6e\x61\x6d\x65\0\x64\x73\x74\x5f\x70\x72\x6f\ \x61\x75\x78\x20\x3d\x20\x70\x72\x6f\x67\x2d\x3e\x61\x75\x78\x3b\0\x09\x09\x42\
\x67\0\x66\x75\x6e\x63\x5f\x69\x6e\x66\x6f\0\x62\x74\x66\0\x09\x42\x50\x46\x5f\ \x50\x46\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\
\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x25\x34\ \x22\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\
\x75\x20\x25\x2d\x31\x36\x73\x20\x25\x73\x20\x25\x73\x5c\x6e\x22\x2c\x20\x61\ \x20\x20\x20\x20\x61\x74\x74\x61\x63\x68\x65\x64\x5c\x6e\x22\x29\x3b\0\x62\x70\
\x75\x78\x2d\x3e\x69\x64\x2c\0\x30\x3a\x34\0\x30\x3a\x35\0\x09\x69\x66\x20\x28\ \x66\x5f\x70\x72\x6f\x67\x5f\x61\x75\x78\0\x61\x74\x74\x61\x63\x68\x5f\x66\x75\
\x21\x62\x74\x66\x29\0\x62\x70\x66\x5f\x66\x75\x6e\x63\x5f\x69\x6e\x66\x6f\0\ \x6e\x63\x5f\x6e\x61\x6d\x65\0\x64\x73\x74\x5f\x70\x72\x6f\x67\0\x66\x75\x6e\
\x69\x6e\x73\x6e\x5f\x6f\x66\x66\0\x74\x79\x70\x65\x5f\x69\x64\0\x30\0\x73\x74\ \x63\x5f\x69\x6e\x66\x6f\0\x62\x74\x66\0\x09\x42\x50\x46\x5f\x53\x45\x51\x5f\
\x72\x69\x6e\x67\x73\0\x74\x79\x70\x65\x73\0\x68\x64\x72\0\x62\x74\x66\x5f\x68\ \x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x25\x34\x75\x20\x25\x2d\
\x65\x61\x64\x65\x72\0\x73\x74\x72\x5f\x6c\x65\x6e\0\x09\x74\x79\x70\x65\x73\ \x31\x36\x73\x20\x25\x73\x20\x25\x73\x5c\x6e\x22\x2c\x20\x61\x75\x78\x2d\x3e\
\x20\x3d\x20\x62\x74\x66\x2d\x3e\x74\x79\x70\x65\x73\x3b\0\x09\x62\x70\x66\x5f\ \x69\x64\x2c\0\x30\x3a\x34\0\x30\x3a\x35\0\x09\x69\x66\x20\x28\x21\x62\x74\x66\
\x70\x72\x6f\x62\x65\x5f\x72\x65\x61\x64\x5f\x6b\x65\x72\x6e\x65\x6c\x28\x26\ \x29\0\x62\x70\x66\x5f\x66\x75\x6e\x63\x5f\x69\x6e\x66\x6f\0\x69\x6e\x73\x6e\
\x74\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x74\x29\x2c\x20\x74\x79\x70\x65\x73\ \x5f\x6f\x66\x66\0\x74\x79\x70\x65\x5f\x69\x64\0\x30\0\x73\x74\x72\x69\x6e\x67\
\x20\x2b\x20\x62\x74\x66\x5f\x69\x64\x29\x3b\0\x09\x73\x74\x72\x20\x3d\x20\x62\ \x73\0\x74\x79\x70\x65\x73\0\x68\x64\x72\0\x62\x74\x66\x5f\x68\x65\x61\x64\x65\
\x74\x66\x2d\x3e\x73\x74\x72\x69\x6e\x67\x73\x3b\0\x62\x74\x66\x5f\x74\x79\x70\ \x72\0\x73\x74\x72\x5f\x6c\x65\x6e\0\x09\x74\x79\x70\x65\x73\x20\x3d\x20\x62\
\x65\0\x6e\x61\x6d\x65\x5f\x6f\x66\x66\0\x09\x6e\x61\x6d\x65\x5f\x6f\x66\x66\ \x74\x66\x2d\x3e\x74\x79\x70\x65\x73\x3b\0\x09\x62\x70\x66\x5f\x70\x72\x6f\x62\
\x20\x3d\x20\x42\x50\x46\x5f\x43\x4f\x52\x45\x5f\x52\x45\x41\x44\x28\x74\x2c\ \x65\x5f\x72\x65\x61\x64\x5f\x6b\x65\x72\x6e\x65\x6c\x28\x26\x74\x2c\x20\x73\
\x20\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x29\x3b\0\x30\x3a\x32\x3a\x30\0\x09\x69\ \x69\x7a\x65\x6f\x66\x28\x74\x29\x2c\x20\x74\x79\x70\x65\x73\x20\x2b\x20\x62\
\x66\x20\x28\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x20\x3e\x3d\x20\x62\x74\x66\x2d\ \x74\x66\x5f\x69\x64\x29\x3b\0\x09\x73\x74\x72\x20\x3d\x20\x62\x74\x66\x2d\x3e\
\x3e\x68\x64\x72\x2e\x73\x74\x72\x5f\x6c\x65\x6e\x29\0\x09\x72\x65\x74\x75\x72\ \x73\x74\x72\x69\x6e\x67\x73\x3b\0\x62\x74\x66\x5f\x74\x79\x70\x65\0\x6e\x61\
\x6e\x20\x73\x74\x72\x20\x2b\x20\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x3b\0\x30\x3a\ \x6d\x65\x5f\x6f\x66\x66\0\x09\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x20\x3d\x20\x42\
\x33\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\ \x50\x46\x5f\x43\x4f\x52\x45\x5f\x52\x45\x41\x44\x28\x74\x2c\x20\x6e\x61\x6d\
\x74\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\ \x65\x5f\x6f\x66\x66\x29\x3b\0\x30\x3a\x32\x3a\x30\0\x09\x69\x66\x20\x28\x6e\
\x74\x2e\x31\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\ \x61\x6d\x65\x5f\x6f\x66\x66\x20\x3e\x3d\x20\x62\x74\x66\x2d\x3e\x68\x64\x72\
\x5f\x66\x6d\x74\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x2e\x5f\ \x2e\x73\x74\x72\x5f\x6c\x65\x6e\x29\0\x09\x72\x65\x74\x75\x72\x6e\x20\x73\x74\
\x5f\x5f\x66\x6d\x74\x2e\x32\0\x4c\x49\x43\x45\x4e\x53\x45\0\x2e\x72\x6f\x64\ \x72\x20\x2b\x20\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x3b\0\x30\x3a\x33\0\x64\x75\
\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\0\x64\x75\
\0\0\0\0\0\x09\x4c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x62\0\0\0\ \x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x31\0\
\x01\0\0\0\x80\0\0\0\0\0\0\0\0\x69\x74\x65\x72\x61\x74\x6f\x72\x2e\x72\x6f\x64\ \x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\
\x61\x74\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x2f\0\0\0\0\0\0\0\0\0\0\0\0\x20\ \x74\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\
\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\ \x6d\x74\x2e\x32\0\x4c\x49\x43\x45\x4e\x53\x45\0\x2e\x6b\x73\x79\x6d\x73\0\x2e\
\x20\x20\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\x0a\0\x25\x34\x75\x20\x25\ \x72\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\x64\x75\x6d\x6d\x79\
\x2d\x31\x36\x73\x25\x36\x64\x0a\0\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\ \x5f\x6b\x73\x79\x6d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x09\xdc\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x04\0\0\0\x7b\0\0\0\x01\0\0\0\x80\0\0\0\0\
\0\0\0\0\x69\x74\x65\x72\x61\x74\x6f\x72\x2e\x72\x6f\x64\x61\x74\x61\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\x34\0\0\0\0\0\0\0\0\0\0\0\0\x20\x20\x69\x64\x20\x6e\x61\
\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6d\x61\x78\x5f\
\x65\x6e\x74\x72\x69\x65\x73\x20\x20\x63\x75\x72\x5f\x65\x6e\x74\x72\x69\x65\
\x73\x0a\0\x25\x34\x75\x20\x25\x2d\x31\x36\x73\x20\x20\x25\x31\x30\x64\x20\x20\
\x20\x25\x31\x30\x6c\x6c\x64\x0a\0\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\
\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x61\x74\x74\x61\x63\x68\x65\x64\ \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x61\x74\x74\x61\x63\x68\x65\x64\
\x0a\0\x25\x34\x75\x20\x25\x2d\x31\x36\x73\x20\x25\x73\x20\x25\x73\x0a\0\0\0\0\ \x0a\0\x25\x34\x75\x20\x25\x2d\x31\x36\x73\x20\x25\x73\x20\x25\x73\x0a\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\x79\x21\0\0\0\0\0\0\x79\x62\0\0\ \0\0\0\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\x79\x21\0\0\0\0\0\0\x79\x62\0\0\0\
\0\0\0\0\x79\x71\0\x08\0\0\0\0\x15\x70\0\x1a\0\0\0\0\x79\x12\0\x10\0\0\0\0\x55\ \0\0\0\x79\x71\0\x08\0\0\0\0\x15\x70\0\x1d\0\0\0\0\x79\x12\0\x10\0\0\0\0\x55\
\x10\0\x08\0\0\0\0\xbf\x4a\0\0\0\0\0\0\x07\x40\0\0\xff\xff\xff\xe8\xbf\x16\0\0\ \x10\0\x08\0\0\0\0\xbf\x4a\0\0\0\0\0\0\x07\x40\0\0\xff\xff\xff\xe0\xbf\x16\0\0\
\0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb7\x30\0\0\0\0\0\x23\xb7\x50\0\0\ \0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb4\x30\0\0\0\0\0\x30\xb4\x50\0\0\
\0\0\0\0\x85\0\0\0\0\0\0\x7e\x61\x17\0\0\0\0\0\0\x7b\xa1\xff\xe8\0\0\0\0\xb7\ \0\0\0\0\x85\0\0\0\0\0\0\x7e\x61\x17\0\0\0\0\0\0\x7b\xa1\xff\xe0\0\0\0\0\xb7\
\x10\0\0\0\0\0\x04\xbf\x27\0\0\0\0\0\0\x0f\x21\0\0\0\0\0\0\x7b\xa2\xff\xf0\0\0\ \x10\0\0\0\0\0\x04\xbf\x27\0\0\0\0\0\0\x0f\x21\0\0\0\0\0\0\x7b\xa2\xff\xe8\0\0\
\0\0\x61\x17\0\x14\0\0\0\0\x7b\xa1\xff\xf8\0\0\0\0\xbf\x4a\0\0\0\0\0\0\x07\x40\ \0\0\x61\x17\0\x14\0\0\0\0\x7b\xa1\xff\xf0\0\0\0\0\xbf\x17\0\0\0\0\0\0\x85\x02\
\0\0\xff\xff\xff\xe8\xbf\x16\0\0\0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\0\x23\ \0\0\0\0\0\0\x7b\xa0\xff\xf8\0\0\0\0\xbf\x4a\0\0\0\0\0\0\x07\x40\0\0\xff\xff\
\xb7\x30\0\0\0\0\0\x0e\xb7\x50\0\0\0\0\0\x18\x85\0\0\0\0\0\0\x7e\xb7\0\0\0\0\0\ \xff\xe0\xbf\x16\0\0\0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\0\x30\xb4\x30\0\0\
\0\0\x95\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\x42\0\0\0\x9a\0\x01\x3c\ \0\0\0\x1a\xb4\x50\0\0\0\0\0\x20\x85\0\0\0\0\0\0\x7e\xb4\0\0\0\0\0\0\0\x95\0\0\
\x1e\0\0\0\x01\0\0\0\x42\0\0\0\x9a\0\x01\x3c\x24\0\0\0\x02\0\0\0\x42\0\0\x01\ \0\0\0\0\0\0\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\x42\0\0\0\x9b\0\x01\x44\x1e\0\0\0\
\x0d\0\x01\x44\x1d\0\0\0\x03\0\0\0\x42\0\0\x01\x2e\0\x01\x4c\x06\0\0\0\x04\0\0\ \x01\0\0\0\x42\0\0\0\x9b\0\x01\x44\x24\0\0\0\x02\0\0\0\x42\0\0\x01\x0e\0\x01\
\0\x42\0\0\x01\x3d\0\x01\x40\x1d\0\0\0\x05\0\0\0\x42\0\0\x01\x62\0\x01\x58\x06\ \x4c\x1d\0\0\0\x03\0\0\0\x42\0\0\x01\x2f\0\x01\x54\x06\0\0\0\x04\0\0\0\x42\0\0\
\0\0\0\x07\0\0\0\x42\0\0\x01\x75\0\x01\x5c\x03\0\0\0\x0e\0\0\0\x42\0\0\x01\xfb\ \x01\x3e\0\x01\x48\x1d\0\0\0\x05\0\0\0\x42\0\0\x01\x63\0\x01\x60\x0e\0\0\0\x08\
\0\x01\x64\x02\0\0\0\x1e\0\0\0\x42\0\0\x02\x49\0\x01\x6c\x01\0\0\0\0\0\0\0\x02\ \0\0\0\x42\0\0\x01\x76\0\x01\x64\x03\0\0\0\x0e\0\0\0\x42\0\0\x02\x09\0\x01\x6c\
\0\0\0\x3e\0\0\0\0\0\0\0\x08\0\0\0\x08\0\0\0\x3e\0\0\0\0\0\0\0\x10\0\0\0\x02\0\ \x02\0\0\0\x21\0\0\0\x42\0\0\x02\x3c\0\x01\x80\x01\0\0\0\0\0\0\0\x02\0\0\0\x3e\
\0\x01\x09\0\0\0\0\0\0\0\x20\0\0\0\x08\0\0\x01\x39\0\0\0\0\0\0\0\x70\0\0\0\x0d\ \0\0\0\0\0\0\0\x08\0\0\0\x08\0\0\0\x3e\0\0\0\0\0\0\0\x10\0\0\0\x02\0\0\x01\x0a\
\0\0\0\x3e\0\0\0\0\0\0\0\x80\0\0\0\x0d\0\0\x01\x09\0\0\0\0\0\0\0\xa0\0\0\0\x0d\ \0\0\0\0\0\0\0\x20\0\0\0\x08\0\0\x01\x3a\0\0\0\0\0\0\0\x70\0\0\0\x0d\0\0\0\x3e\
\0\0\x01\x39\0\0\0\0\0\0\0\x1a\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\x80\0\0\0\x0d\0\0\x01\x0a\0\0\0\0\0\0\0\xa0\0\0\0\x0d\0\0\x01\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\ \x3a\0\0\0\0\0\0\0\x1a\0\0\0\x23\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
\x6d\x61\x70\0\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\
\x01\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\x09\0\0\0\x01\0\0\0\0\0\0\0\x07\0\0\0\0\0\ \x70\0\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\ \0\x10\0\0\0\0\0\0\0\0\0\0\0\x09\0\0\0\x01\0\0\0\0\0\0\0\x07\0\0\0\0\0\0\0\0\0\
\x62\x70\x66\x5f\x6d\x61\x70\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\x79\x21\0\0\ \0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\x62\x70\
\0\0\0\0\x79\x62\0\0\0\0\0\0\x79\x11\0\x08\0\0\0\0\x15\x10\0\x3b\0\0\0\0\x79\ \x66\x5f\x6d\x61\x70\0\0\0\0\0\0\0\0\x62\x70\x66\x5f\x6d\x61\x70\x5f\x73\x75\
\x71\0\0\0\0\0\0\x79\x12\0\x10\0\0\0\0\x55\x10\0\x08\0\0\0\0\xbf\x4a\0\0\0\0\0\ \x6d\x5f\x65\x6c\x65\x6d\x5f\x63\x6f\x75\x6e\x74\0\0\x47\x50\x4c\0\0\0\0\0\x79\
\0\x07\x40\0\0\xff\xff\xff\xd0\xbf\x16\0\0\0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\0\ \x21\0\0\0\0\0\0\x79\x62\0\0\0\0\0\0\x79\x11\0\x08\0\0\0\0\x15\x10\0\x3b\0\0\0\
\0\0\0\x31\xb7\x30\0\0\0\0\0\x20\xb7\x50\0\0\0\0\0\0\x85\0\0\0\0\0\0\x7e\x7b\ \0\x79\x71\0\0\0\0\0\0\x79\x12\0\x10\0\0\0\0\x55\x10\0\x08\0\0\0\0\xbf\x4a\0\0\
\xa6\xff\xc8\0\0\0\0\x61\x17\0\0\0\0\0\0\x7b\xa1\xff\xd0\0\0\0\0\xb7\x30\0\0\0\ \0\0\0\0\x07\x40\0\0\xff\xff\xff\xd0\xbf\x16\0\0\0\0\0\0\x18\x26\0\0\0\0\0\0\0\
\0\0\x04\xbf\x97\0\0\0\0\0\0\x0f\x93\0\0\0\0\0\0\x79\x17\0\x28\0\0\0\0\x79\x87\ \0\0\0\0\0\0\x4a\xb4\x30\0\0\0\0\0\x20\xb4\x50\0\0\0\0\0\0\x85\0\0\0\0\0\0\x7e\
\0\x30\0\0\0\0\x15\x80\0\x18\0\0\0\0\xb7\x20\0\0\0\0\0\0\x0f\x12\0\0\0\0\0\0\ \x7b\xa6\xff\xc8\0\0\0\0\x61\x17\0\0\0\0\0\0\x7b\xa1\xff\xd0\0\0\0\0\xb7\x30\0\
\x61\x11\0\x04\0\0\0\0\x79\x38\0\x08\0\0\0\0\x67\x10\0\0\0\0\0\x03\x0f\x31\0\0\ \0\0\0\0\x04\xbf\x97\0\0\0\0\0\0\x0f\x93\0\0\0\0\0\0\x79\x17\0\x28\0\0\0\0\x79\
\0\0\0\0\x79\x68\0\0\0\0\0\0\xbf\x1a\0\0\0\0\0\0\x07\x10\0\0\xff\xff\xff\xf8\ \x87\0\x30\0\0\0\0\x15\x80\0\x18\0\0\0\0\xb7\x20\0\0\0\0\0\0\x0f\x12\0\0\0\0\0\
\xb7\x20\0\0\0\0\0\x08\x85\0\0\0\0\0\0\x71\xb7\x10\0\0\0\0\0\0\x79\x3a\xff\xf8\ \0\x61\x11\0\x04\0\0\0\0\x79\x38\0\x08\0\0\0\0\x67\x10\0\0\0\0\0\x03\x0f\x31\0\
\0\0\0\0\0\x79\x68\0\0\0\0\0\0\xbf\x1a\0\0\0\0\0\0\x07\x10\0\0\xff\xff\xff\xf8\
\xb4\x20\0\0\0\0\0\x08\x85\0\0\0\0\0\0\x71\xb7\x10\0\0\0\0\0\0\x79\x3a\xff\xf8\
\0\0\0\0\x0f\x31\0\0\0\0\0\0\xbf\x1a\0\0\0\0\0\0\x07\x10\0\0\xff\xff\xff\xf4\ \0\0\0\0\x0f\x31\0\0\0\0\0\0\xbf\x1a\0\0\0\0\0\0\x07\x10\0\0\xff\xff\xff\xf4\
\xb7\x20\0\0\0\0\0\x04\x85\0\0\0\0\0\0\x71\xb7\x30\0\0\0\0\0\x04\x61\x1a\xff\ \xb4\x20\0\0\0\0\0\x04\x85\0\0\0\0\0\0\x71\xb7\x30\0\0\0\0\0\x04\x61\x1a\xff\
\xf4\0\0\0\0\x61\x28\0\x10\0\0\0\0\x3d\x12\0\x02\0\0\0\0\x0f\x61\0\0\0\0\0\0\ \xf4\0\0\0\0\x61\x28\0\x10\0\0\0\0\x3e\x12\0\x02\0\0\0\0\x0f\x61\0\0\0\0\0\0\
\xbf\x96\0\0\0\0\0\0\x7b\xa9\xff\xd8\0\0\0\0\x79\x17\0\x18\0\0\0\0\x7b\xa1\xff\ \xbf\x96\0\0\0\0\0\0\x7b\xa9\xff\xd8\0\0\0\0\x79\x17\0\x18\0\0\0\0\x7b\xa1\xff\
\xe0\0\0\0\0\x79\x17\0\x20\0\0\0\0\x79\x11\0\0\0\0\0\0\x0f\x13\0\0\0\0\0\0\x7b\ \xe0\0\0\0\0\x79\x17\0\x20\0\0\0\0\x79\x11\0\0\0\0\0\0\x0f\x13\0\0\0\0\0\0\x7b\
\xa1\xff\xe8\0\0\0\0\xbf\x4a\0\0\0\0\0\0\x07\x40\0\0\xff\xff\xff\xd0\x79\x1a\ \xa1\xff\xe8\0\0\0\0\xbf\x4a\0\0\0\0\0\0\x07\x40\0\0\xff\xff\xff\xd0\x79\x1a\
\xff\xc8\0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\0\x51\xb7\x30\0\0\0\0\0\x11\ \xff\xc8\0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\0\x6a\xb4\x30\0\0\0\0\0\x11\
\xb7\x50\0\0\0\0\0\x20\x85\0\0\0\0\0\0\x7e\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\ \xb4\x50\0\0\0\0\0\x20\x85\0\0\0\0\0\0\x7e\xb4\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\x17\0\0\0\0\0\0\0\x42\0\0\0\x9a\0\x01\x80\x1e\0\0\0\x01\0\0\0\ \0\0\0\0\0\0\0\x1b\0\0\0\0\0\0\0\x42\0\0\0\x9b\0\x01\x94\x1e\0\0\0\x01\0\0\0\
\x42\0\0\0\x9a\0\x01\x80\x24\0\0\0\x02\0\0\0\x42\0\0\x02\x7f\0\x01\x88\x1f\0\0\ \x42\0\0\0\x9b\0\x01\x94\x24\0\0\0\x02\0\0\0\x42\0\0\x02\x99\0\x01\x9c\x1f\0\0\
\0\x03\0\0\0\x42\0\0\x02\xa3\0\x01\x94\x06\0\0\0\x04\0\0\0\x42\0\0\x02\xbc\0\ \0\x03\0\0\0\x42\0\0\x02\xbd\0\x01\xa8\x06\0\0\0\x04\0\0\0\x42\0\0\x02\xd6\0\
\x01\xa0\x0e\0\0\0\x05\0\0\0\x42\0\0\x01\x3d\0\x01\x84\x1d\0\0\0\x06\0\0\0\x42\ \x01\xb4\x0e\0\0\0\x05\0\0\0\x42\0\0\x01\x3e\0\x01\x98\x1d\0\0\0\x06\0\0\0\x42\
\0\0\x01\x62\0\x01\xa4\x06\0\0\0\x08\0\0\0\x42\0\0\x02\xce\0\x01\xa8\x03\0\0\0\ \0\0\x01\x63\0\x01\xb8\x0e\0\0\0\x09\0\0\0\x42\0\0\x02\xe8\0\x01\xbc\x03\0\0\0\
\x10\0\0\0\x42\0\0\x03\x3e\0\x01\xb0\x02\0\0\0\x17\0\0\0\x42\0\0\x03\x79\0\x01\ \x10\0\0\0\x42\0\0\x03\x58\0\x01\xc4\x02\0\0\0\x17\0\0\0\x42\0\0\x03\x93\0\x01\
\x04\x06\0\0\0\x1a\0\0\0\x42\0\0\x03\x3e\0\x01\xb0\x02\0\0\0\x1b\0\0\0\x42\0\0\ \x04\x06\0\0\0\x1a\0\0\0\x42\0\0\x03\x58\0\x01\xc4\x02\0\0\0\x1b\0\0\0\x42\0\0\
\x03\xca\0\x01\x10\x0f\0\0\0\x1c\0\0\0\x42\0\0\x03\xdf\0\x01\x14\x2d\0\0\0\x1e\ \x03\xe4\0\x01\x10\x0f\0\0\0\x1c\0\0\0\x42\0\0\x03\xf9\0\x01\x14\x2d\0\0\0\x1e\
\0\0\0\x42\0\0\x04\x16\0\x01\x0c\x0d\0\0\0\x20\0\0\0\x42\0\0\x03\x3e\0\x01\xb0\ \0\0\0\x42\0\0\x04\x30\0\x01\x0c\x0d\0\0\0\x21\0\0\0\x42\0\0\x03\xf9\0\x01\x14\
\x02\0\0\0\x21\0\0\0\x42\0\0\x03\xdf\0\x01\x14\x02\0\0\0\x24\0\0\0\x42\0\0\x04\ \x02\0\0\0\x24\0\0\0\x42\0\0\x04\x57\0\x01\x18\x0d\0\0\0\x2b\0\0\0\x42\0\0\x04\
\x3d\0\x01\x18\x0d\0\0\0\x27\0\0\0\x42\0\0\x03\x3e\0\x01\xb0\x02\0\0\0\x28\0\0\ \x57\0\x01\x18\x0d\0\0\0\x2c\0\0\0\x42\0\0\x04\x85\0\x01\x1c\x1b\0\0\0\x2d\0\0\
\0\x42\0\0\x04\x3d\0\x01\x18\x0d\0\0\0\x2b\0\0\0\x42\0\0\x04\x3d\0\x01\x18\x0d\ \0\x42\0\0\x04\x85\0\x01\x1c\x0f\0\0\0\x2e\0\0\0\x42\0\0\x04\xa8\0\x01\x24\x0d\
\0\0\0\x2c\0\0\0\x42\0\0\x04\x6b\0\x01\x1c\x1b\0\0\0\x2d\0\0\0\x42\0\0\x04\x6b\ \0\0\0\x30\0\0\0\x42\0\0\x03\x58\0\x01\xc4\x02\0\0\0\x3f\0\0\0\x42\0\0\x02\x3c\
\0\x01\x1c\x06\0\0\0\x2e\0\0\0\x42\0\0\x04\x8e\0\x01\x24\x0d\0\0\0\x30\0\0\0\ \0\x01\xd4\x01\0\0\0\0\0\0\0\x18\0\0\0\x3e\0\0\0\0\0\0\0\x08\0\0\0\x08\0\0\0\
\x42\0\0\x03\x3e\0\x01\xb0\x02\0\0\0\x3f\0\0\0\x42\0\0\x02\x49\0\x01\xc0\x01\0\ \x3e\0\0\0\0\0\0\0\x10\0\0\0\x18\0\0\x01\x0a\0\0\0\0\0\0\0\x20\0\0\0\x1c\0\0\0\
\0\0\0\0\0\0\x14\0\0\0\x3e\0\0\0\0\0\0\0\x08\0\0\0\x08\0\0\0\x3e\0\0\0\0\0\0\0\ \x3e\0\0\0\0\0\0\0\x28\0\0\0\x08\0\0\x01\x3a\0\0\0\0\0\0\0\x80\0\0\0\x1e\0\0\0\
\x10\0\0\0\x14\0\0\x01\x09\0\0\0\0\0\0\0\x20\0\0\0\x18\0\0\0\x3e\0\0\0\0\0\0\0\ \x3e\0\0\0\0\0\0\0\x90\0\0\0\x1e\0\0\x01\x0a\0\0\0\0\0\0\0\xa8\0\0\0\x1e\0\0\
\x28\0\0\0\x08\0\0\x01\x39\0\0\0\0\0\0\0\x80\0\0\0\x1a\0\0\0\x3e\0\0\0\0\0\0\0\ \x03\x8b\0\0\0\0\0\0\0\xb0\0\0\0\x1e\0\0\x03\x8f\0\0\0\0\0\0\0\xc0\0\0\0\x23\0\
\x90\0\0\0\x1a\0\0\x01\x09\0\0\0\0\0\0\0\xa8\0\0\0\x1a\0\0\x03\x71\0\0\0\0\0\0\ \0\x03\xbd\0\0\0\0\0\0\0\xd8\0\0\0\x24\0\0\x01\x0a\0\0\0\0\0\0\0\xf0\0\0\0\x24\
\0\xb0\0\0\0\x1a\0\0\x03\x75\0\0\0\0\0\0\0\xc0\0\0\0\x1f\0\0\x03\xa3\0\0\0\0\0\ \0\0\0\x3e\0\0\0\0\0\0\x01\x18\0\0\0\x28\0\0\0\x3e\0\0\0\0\0\0\x01\x50\0\0\0\
\0\0\xd8\0\0\0\x20\0\0\x01\x09\0\0\0\0\0\0\0\xf0\0\0\0\x20\0\0\0\x3e\0\0\0\0\0\ \x1e\0\0\x01\x0a\0\0\0\0\0\0\x01\x60\0\0\0\x24\0\0\x04\x7f\0\0\0\0\0\0\x01\x88\
\0\x01\x18\0\0\0\x24\0\0\0\x3e\0\0\0\0\0\0\x01\x50\0\0\0\x1a\0\0\x01\x09\0\0\0\ \0\0\0\x1e\0\0\x01\x3a\0\0\0\0\0\0\x01\x98\0\0\0\x1e\0\0\x04\xc0\0\0\0\0\0\0\
\0\0\0\x01\x60\0\0\0\x20\0\0\x04\x65\0\0\0\0\0\0\x01\x88\0\0\0\x1a\0\0\x01\x39\ \x01\xa0\0\0\0\x1c\0\0\0\x3e\0\0\0\0\0\0\0\x1a\0\0\0\x41\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\x01\x98\0\0\0\x1a\0\0\x04\xa6\0\0\0\0\0\0\x01\xa0\0\0\0\x18\0\0\0\ \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x75\x6d\x70\x5f\
\x3e\0\0\0\0\0\0\0\x1a\0\0\0\x41\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \x62\x70\x66\x5f\x70\x72\x6f\x67\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\0\0\0\0\x08\0\0\
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\ \0\0\0\0\0\0\0\0\0\x01\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\x16\0\0\0\x01\0\0\0\0\0\
\x6f\x67\0\0\0\0\0\0\0\0\0\0\x1c\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\0\0\0\x01\0\ \0\0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\x62\x70\x66\x5f\x69\
\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x12\0\0\0\0\0\0\0\0\ \x74\x65\x72\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\0\0\0\0\0\0";
\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\x62\x70\ static const char opts_insn[] __attribute__((__aligned__(8))) = "\
\x66\x5f\x70\x72\x6f\x67\0\0\0\0\0\0\0";
opts.insns_sz = 2216;
opts.insns = (void *)"\
\xbf\x61\0\0\0\0\0\0\xbf\x1a\0\0\0\0\0\0\x07\x10\0\0\xff\xff\xff\x78\xb7\x20\0\ \xbf\x61\0\0\0\0\0\0\xbf\x1a\0\0\0\0\0\0\x07\x10\0\0\xff\xff\xff\x78\xb7\x20\0\
\0\0\0\0\x88\xb7\x30\0\0\0\0\0\0\x85\0\0\0\0\0\0\x71\x05\0\0\x14\0\0\0\0\x61\ \0\0\0\0\x88\xb7\x30\0\0\0\0\0\0\x85\0\0\0\0\0\0\x71\x05\0\0\x14\0\0\0\0\x61\
\x1a\xff\x78\0\0\0\0\xd5\x10\0\x01\0\0\0\0\x85\0\0\0\0\0\0\xa8\x61\x1a\xff\x7c\ \x1a\xff\x78\0\0\0\0\xd5\x10\0\x01\0\0\0\0\x85\0\0\0\0\0\0\xa8\x61\x1a\xff\x7c\
@ -318,72 +321,87 @@ iterators_bpf__load(struct iterators_bpf *skel)
\0\0\0\x85\0\0\0\0\0\0\xa8\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x10\0\0\0\0\ \0\0\0\x85\0\0\0\0\0\0\xa8\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x10\0\0\0\0\
\0\0\xd5\x10\0\x02\0\0\0\0\xbf\x91\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa8\xbf\x07\0\0\ \0\0\xd5\x10\0\x02\0\0\0\0\xbf\x91\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa8\xbf\x07\0\0\
\0\0\0\0\x95\0\0\0\0\0\0\0\x61\x06\0\x08\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\x95\0\0\0\0\0\0\0\x61\x06\0\x08\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\
\0\x0e\x68\x63\x10\0\0\0\0\0\0\x61\x06\0\x0c\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\ \0\x0e\xf8\x63\x10\0\0\0\0\0\0\x61\x06\0\x0c\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\
\0\0\0\x0e\x64\x63\x10\0\0\0\0\0\0\x79\x06\0\x10\0\0\0\0\x18\x16\0\0\0\0\0\0\0\ \0\0\0\x0e\xf4\x63\x10\0\0\0\0\0\0\x79\x06\0\x10\0\0\0\0\x18\x16\0\0\0\0\0\0\0\
\0\0\0\0\0\x0e\x58\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x05\0\ \0\0\0\0\0\x0e\xe8\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x05\0\
\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x0e\x50\x7b\x10\0\0\0\0\0\0\xb7\x10\0\0\0\0\0\ \x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x0e\xe0\x7b\x10\0\0\0\0\0\0\xb7\x10\0\0\0\0\0\
\x12\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\x0e\x50\xb7\x30\0\0\0\0\0\x1c\x85\0\0\0\0\ \x12\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\x0e\xe0\xb7\x30\0\0\0\0\0\x1c\x85\0\0\0\0\
\0\0\xa6\xbf\x70\0\0\0\0\0\0\xc5\x70\xff\xd4\0\0\0\0\x63\xa7\xff\x78\0\0\0\0\ \0\0\xa6\xbf\x70\0\0\0\0\0\0\xc5\x70\xff\xd4\0\0\0\0\x63\xa7\xff\x78\0\0\0\0\
\x61\x0a\xff\x78\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x0e\xa0\x63\x10\0\0\0\ \x61\x0a\xff\x78\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x0f\x30\x63\x10\0\0\0\
\0\0\0\x61\x06\0\x1c\0\0\0\0\x15\0\0\x03\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\x61\x06\0\x1c\0\0\0\0\x15\0\0\x03\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\
\0\x0e\x7c\x63\x10\0\0\0\0\0\0\xb7\x10\0\0\0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\0\ \0\x0f\x0c\x63\x10\0\0\0\0\0\0\xb7\x10\0\0\0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\0\
\0\0\x0e\x70\xb7\x30\0\0\0\0\0\x48\x85\0\0\0\0\0\0\xa6\xbf\x70\0\0\0\0\0\0\xc5\ \0\0\x0f\0\xb7\x30\0\0\0\0\0\x48\x85\0\0\0\0\0\0\xa6\xbf\x70\0\0\0\0\0\0\xc5\
\x70\xff\xc3\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x63\x17\0\0\0\0\0\0\ \x70\xff\xc3\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x63\x17\0\0\0\0\0\0\
\x79\x36\0\x20\0\0\0\0\x15\x30\0\x08\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\ \x79\x36\0\x20\0\0\0\0\x15\x30\0\x08\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\
\x0e\xb8\xb7\x20\0\0\0\0\0\x62\x61\x06\0\x04\0\0\0\0\x45\0\0\x02\0\0\0\x01\x85\ \x0f\x48\xb7\x20\0\0\0\0\0\x7b\x61\x06\0\x04\0\0\0\0\x45\0\0\x02\0\0\0\x01\x85\
\0\0\0\0\0\0\x94\x05\0\0\x01\0\0\0\0\x85\0\0\0\0\0\0\x71\x18\x26\0\0\0\0\0\0\0\ \0\0\0\0\0\0\x94\x05\0\0\x01\0\0\0\0\x85\0\0\0\0\0\0\x71\x18\x26\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\x61\x02\0\0\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x0f\x28\x63\ \0\0\0\0\0\0\0\x61\x02\0\0\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x0f\xd0\x63\
\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x0f\x20\x18\x16\0\0\0\0\0\0\0\ \x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x0f\xc8\x18\x16\0\0\0\0\0\0\0\
\0\0\0\0\0\x0f\x30\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x0e\xb8\ \0\0\0\0\0\x0f\xd8\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x0f\x48\
\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x0f\x38\x7b\x10\0\0\0\0\0\0\xb7\x10\0\0\0\0\0\ \x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x0f\xe0\x7b\x10\0\0\0\0\0\0\xb7\x10\0\0\0\0\0\
\x02\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\x0f\x28\xb7\x30\0\0\0\0\0\x20\x85\0\0\0\0\ \x02\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\x0f\xd0\xb7\x30\0\0\0\0\0\x20\x85\0\0\0\0\
\0\0\xa6\xbf\x70\0\0\0\0\0\0\xc5\x70\xff\x9f\0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\ \0\0\xa6\xbf\x70\0\0\0\0\0\0\xc5\x70\xff\x9f\0\0\0\0\x18\x26\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\x61\x02\0\0\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x0f\x48\x63\x10\ \0\0\0\0\0\x61\x02\0\0\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x0f\xf0\x63\x10\
\0\0\0\0\0\0\xb7\x10\0\0\0\0\0\x16\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\x0f\x48\xb7\ \0\0\0\0\0\0\xb7\x10\0\0\0\0\0\x16\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\x0f\xf0\xb7\
\x30\0\0\0\0\0\x04\x85\0\0\0\0\0\0\xa6\xbf\x70\0\0\0\0\0\0\xc5\x70\xff\x92\0\0\ \x30\0\0\0\0\0\x04\x85\0\0\0\0\0\0\xa6\xbf\x70\0\0\0\0\0\0\xc5\x70\xff\x92\0\0\
\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x0f\x50\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x0f\xf8\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\
\x11\x70\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x0f\x58\x18\x16\0\ \x12\x30\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\x18\x16\0\0\
\0\0\0\0\0\0\0\0\0\0\0\x11\x68\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\0\0\0\0\0\x12\x28\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\
\0\0\x10\x58\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x11\xb0\x7b\x10\0\0\0\0\0\0\x18\ \0\x11\x18\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x12\x70\x7b\x10\0\0\0\0\0\0\x18\x06\
\x06\0\0\0\0\0\0\0\0\0\0\0\0\x10\x60\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x11\xc0\ \0\0\0\0\0\0\0\0\0\0\0\0\x11\x20\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x12\x80\x7b\
\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x10\xf0\x18\x16\0\0\0\0\0\ \x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x11\xb0\x18\x16\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\x11\xe0\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ \0\0\0\0\0\x12\xa0\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x18\
\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x11\xd8\x7b\x10\0\0\0\0\0\0\x61\x06\0\x08\0\0\ \x16\0\0\0\0\0\0\0\0\0\0\0\0\x12\x98\x7b\x10\0\0\0\0\0\0\x61\x06\0\x08\0\0\0\0\
\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x11\x78\x63\x10\0\0\0\0\0\0\x61\x06\0\x0c\ \x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x12\x38\x63\x10\0\0\0\0\0\0\x61\x06\0\x0c\0\0\
\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x11\x7c\x63\x10\0\0\0\0\0\0\x79\x06\0\ \0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x12\x3c\x63\x10\0\0\0\0\0\0\x79\x06\0\x10\
\x10\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x11\x80\x7b\x10\0\0\0\0\0\0\x61\ \0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x12\x40\x7b\x10\0\0\0\0\0\0\x61\x0a\
\x0a\xff\x78\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x11\xa8\x63\x10\0\0\0\0\0\ \xff\x78\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x12\x68\x63\x10\0\0\0\0\0\0\
\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x11\xf0\xb7\x20\0\0\0\0\0\x11\xb7\x30\0\0\0\ \x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x12\xb0\xb7\x20\0\0\0\0\0\x11\xb7\x30\0\0\0\0\
\0\0\x0c\xb7\x40\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa7\xbf\x70\0\0\0\0\0\0\xc5\x70\ \0\x0c\xb7\x40\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa7\xbf\x70\0\0\0\0\0\0\xc5\x70\xff\
\xff\x5c\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x11\x60\x63\x07\0\x6c\0\0\0\0\ \x5c\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x12\x20\x63\x07\0\x6c\0\0\0\0\x77\
\x77\x70\0\0\0\0\0\x20\x63\x07\0\x70\0\0\0\0\xb7\x10\0\0\0\0\0\x05\x18\x26\0\0\ \x70\0\0\0\0\0\x20\x63\x07\0\x70\0\0\0\0\x18\x86\0\0\0\0\0\0\0\0\0\0\0\0\x10\
\0\0\0\0\0\0\0\0\0\0\x11\x60\xb7\x30\0\0\0\0\0\x8c\x85\0\0\0\0\0\0\xa6\xbf\x70\ \xb8\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x12\xc8\xb7\x20\0\0\0\0\0\x17\xb7\x30\0\0\
\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x11\xd0\x61\x10\0\0\0\0\0\0\xd5\ \0\0\0\x0c\xb7\x40\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa7\xbf\x70\0\0\0\0\0\0\xc5\x70\
\x10\0\x02\0\0\0\0\xbf\x91\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa8\xc5\x70\xff\x4a\0\0\ \xff\x4d\0\0\0\0\x75\x70\0\x03\0\0\0\0\x62\x80\0\x04\0\0\0\0\x6a\x80\0\x02\0\0\
\0\0\x63\xa7\xff\x80\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x12\x08\x18\x16\0\ \0\0\x05\0\0\x0a\0\0\0\0\x63\x87\0\x04\0\0\0\0\xbf\x97\0\0\0\0\0\0\x77\x90\0\0\
\0\0\0\0\0\0\0\0\0\0\0\x16\xe0\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\ \0\0\0\x20\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\x63\x09\0\0\0\0\0\0\x55\x90\0\
\0\0\x12\x10\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x16\xd8\x7b\x10\0\0\0\0\0\0\x18\ \x02\0\0\0\0\x6a\x80\0\x02\0\0\0\0\x05\0\0\x01\0\0\0\0\x6a\x80\0\x02\0\0\0\x40\
\x06\0\0\0\0\0\0\0\0\0\0\0\0\x14\x18\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\x20\ \xb7\x10\0\0\0\0\0\x05\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\x12\x20\xb7\x30\0\0\0\0\
\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x14\x20\x18\x16\0\0\0\0\0\ \0\x8c\x85\0\0\0\0\0\0\xa6\xbf\x70\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\0\x17\x30\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x15\ \x01\0\x61\x10\0\0\0\0\0\0\xd5\x10\0\x02\0\0\0\0\xbf\x91\0\0\0\0\0\0\x85\0\0\0\
\xb0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\x50\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\ \0\0\0\xa8\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x12\x90\x61\x10\0\0\0\0\0\0\xd5\x10\
\0\0\0\0\0\0\0\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\x48\x7b\x10\0\0\0\0\ \0\x02\0\0\0\0\xbf\x91\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa8\xc5\x70\xff\x2c\0\0\0\0\
\0\0\x61\x06\0\x08\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x16\xe8\x63\x10\0\0\ \x63\xa7\xff\x80\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x12\xe0\x18\x16\0\0\0\
\0\0\0\0\x61\x06\0\x0c\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x16\xec\x63\x10\ \0\0\0\0\0\0\0\0\0\x17\x88\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\
\0\0\0\0\0\0\x79\x06\0\x10\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x16\xf0\x7b\ \x12\xe8\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\x80\x7b\x10\0\0\0\0\0\0\x18\x06\0\
\x10\0\0\0\0\0\0\x61\x0a\xff\x78\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\ \0\0\0\0\0\0\0\0\0\0\0\x14\xf0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\xc8\x7b\x10\
\x18\x63\x10\0\0\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\x60\xb7\x20\0\0\0\ \0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x14\xf8\x18\x16\0\0\0\0\0\0\0\0\0\
\0\0\x12\xb7\x30\0\0\0\0\0\x0c\xb7\x40\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa7\xbf\x70\ \0\0\0\x17\xd8\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x16\x58\x18\
\0\0\0\0\0\0\xc5\x70\xff\x13\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x16\xd0\ \x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\xf8\x7b\x10\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\
\x63\x07\0\x6c\0\0\0\0\x77\x70\0\0\0\0\0\x20\x63\x07\0\x70\0\0\0\0\xb7\x10\0\0\ \0\0\0\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\xf0\x7b\x10\0\0\0\0\0\0\x61\
\0\0\0\x05\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\x16\xd0\xb7\x30\0\0\0\0\0\x8c\x85\0\ \x06\0\x08\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\x90\x63\x10\0\0\0\0\0\0\
\0\0\0\0\0\xa6\xbf\x70\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x17\x40\x61\ \x61\x06\0\x0c\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\x94\x63\x10\0\0\0\0\
\x10\0\0\0\0\0\0\xd5\x10\0\x02\0\0\0\0\xbf\x91\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa8\ \0\0\x79\x06\0\x10\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\x98\x7b\x10\0\0\
\xc5\x70\xff\x01\0\0\0\0\x63\xa7\xff\x84\0\0\0\0\x61\x1a\xff\x78\0\0\0\0\xd5\ \0\0\0\0\x61\x0a\xff\x78\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x17\xc0\x63\
\x10\0\x02\0\0\0\0\xbf\x91\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa8\x61\x0a\xff\x80\0\0\ \x10\0\0\0\0\0\0\x18\x16\0\0\0\0\0\0\0\0\0\0\0\0\x18\x08\xb7\x20\0\0\0\0\0\x12\
\0\0\x63\x60\0\x28\0\0\0\0\x61\x0a\xff\x84\0\0\0\0\x63\x60\0\x2c\0\0\0\0\x18\ \xb7\x30\0\0\0\0\0\x0c\xb7\x40\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa7\xbf\x70\0\0\0\0\
\x16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x01\0\0\0\0\0\0\x63\x60\0\x18\0\0\0\0\xb7\ \0\0\xc5\x70\xfe\xf5\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x17\x78\x63\x07\0\
\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0"; \x6c\0\0\0\0\x77\x70\0\0\0\0\0\x20\x63\x07\0\x70\0\0\0\0\xb7\x10\0\0\0\0\0\x05\
\x18\x26\0\0\0\0\0\0\0\0\0\0\0\0\x17\x78\xb7\x30\0\0\0\0\0\x8c\x85\0\0\0\0\0\0\
\xa6\xbf\x70\0\0\0\0\0\0\x18\x06\0\0\0\0\0\0\0\0\0\0\0\0\x17\xe8\x61\x10\0\0\0\
\0\0\0\xd5\x10\0\x02\0\0\0\0\xbf\x91\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa8\xc5\x70\
\xfe\xe3\0\0\0\0\x63\xa7\xff\x84\0\0\0\0\x61\x1a\xff\x78\0\0\0\0\xd5\x10\0\x02\
\0\0\0\0\xbf\x91\0\0\0\0\0\0\x85\0\0\0\0\0\0\xa8\x61\x0a\xff\x80\0\0\0\0\x63\
\x60\0\x28\0\0\0\0\x61\x0a\xff\x84\0\0\0\0\x63\x60\0\x2c\0\0\0\0\x18\x16\0\0\0\
\0\0\0\0\0\0\0\0\0\0\0\x61\x01\0\0\0\0\0\0\x63\x60\0\x18\0\0\0\0\xb7\0\0\0\0\0\
\0\0\x95\0\0\0\0\0\0\0";
opts.ctx = (struct bpf_loader_ctx *)skel;
opts.data_sz = sizeof(opts_data) - 1;
opts.data = (void *)opts_data;
opts.insns_sz = sizeof(opts_insn) - 1;
opts.insns = (void *)opts_insn;
err = bpf_load_and_run(&opts); err = bpf_load_and_run(&opts);
if (err < 0) if (err < 0)
return err; return err;

View file

@ -78,8 +78,7 @@ static const struct seq_operations bpf_prog_seq_ops = {
.show = bpf_prog_seq_show, .show = bpf_prog_seq_show,
}; };
BTF_ID_LIST(btf_bpf_prog_id) BTF_ID_LIST_SINGLE(btf_bpf_prog_id, struct, bpf_prog)
BTF_ID(struct, bpf_prog)
static const struct bpf_iter_seq_info bpf_prog_seq_info = { static const struct bpf_iter_seq_info bpf_prog_seq_info = {
.seq_ops = &bpf_prog_seq_ops, .seq_ops = &bpf_prog_seq_ops,

View file

@ -666,6 +666,27 @@ EXPORT_SYMBOL_GPL(resilient_queued_spin_lock_slowpath);
__bpf_kfunc_start_defs(); __bpf_kfunc_start_defs();
static void bpf_prog_report_rqspinlock_violation(const char *str, void *lock, bool irqsave)
{
struct rqspinlock_held *rqh = this_cpu_ptr(&rqspinlock_held_locks);
struct bpf_stream_stage ss;
struct bpf_prog *prog;
prog = bpf_prog_find_from_stack();
if (!prog)
return;
bpf_stream_stage(ss, prog, BPF_STDERR, ({
bpf_stream_printk(ss, "ERROR: %s for bpf_res_spin_lock%s\n", str, irqsave ? "_irqsave" : "");
bpf_stream_printk(ss, "Attempted lock = 0x%px\n", lock);
bpf_stream_printk(ss, "Total held locks = %d\n", rqh->cnt);
for (int i = 0; i < min(RES_NR_HELD, rqh->cnt); i++)
bpf_stream_printk(ss, "Held lock[%2d] = 0x%px\n", i, rqh->locks[i]);
bpf_stream_dump_stack(ss);
}));
}
#define REPORT_STR(ret) ({ (ret) == -ETIMEDOUT ? "Timeout detected" : "AA or ABBA deadlock detected"; })
__bpf_kfunc int bpf_res_spin_lock(struct bpf_res_spin_lock *lock) __bpf_kfunc int bpf_res_spin_lock(struct bpf_res_spin_lock *lock)
{ {
int ret; int ret;
@ -676,6 +697,7 @@ __bpf_kfunc int bpf_res_spin_lock(struct bpf_res_spin_lock *lock)
preempt_disable(); preempt_disable();
ret = res_spin_lock((rqspinlock_t *)lock); ret = res_spin_lock((rqspinlock_t *)lock);
if (unlikely(ret)) { if (unlikely(ret)) {
bpf_prog_report_rqspinlock_violation(REPORT_STR(ret), lock, false);
preempt_enable(); preempt_enable();
return ret; return ret;
} }
@ -698,6 +720,7 @@ __bpf_kfunc int bpf_res_spin_lock_irqsave(struct bpf_res_spin_lock *lock, unsign
local_irq_save(flags); local_irq_save(flags);
ret = res_spin_lock((rqspinlock_t *)lock); ret = res_spin_lock((rqspinlock_t *)lock);
if (unlikely(ret)) { if (unlikely(ret)) {
bpf_prog_report_rqspinlock_violation(REPORT_STR(ret), lock, true);
local_irq_restore(flags); local_irq_restore(flags);
preempt_enable(); preempt_enable();
return ret; return ret;

526
kernel/bpf/stream.c Normal file
View file

@ -0,0 +1,526 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/bpf_mem_alloc.h>
#include <linux/percpu.h>
#include <linux/refcount.h>
#include <linux/gfp.h>
#include <linux/memory.h>
#include <linux/local_lock.h>
#include <linux/mutex.h>
/*
* Simple per-CPU NMI-safe bump allocation mechanism, backed by the NMI-safe
* try_alloc_pages()/free_pages_nolock() primitives. We allocate a page and
* stash it in a local per-CPU variable, and bump allocate from the page
* whenever items need to be printed to a stream. Each page holds a global
* atomic refcount in its first 4 bytes, and then records of variable length
* that describe the printed messages. Once the global refcount has dropped to
* zero, it is a signal to free the page back to the kernel's page allocator,
* given all the individual records in it have been consumed.
*
* It is possible the same page is used to serve allocations across different
* programs, which may be consumed at different times individually, hence
* maintaining a reference count per-page is critical for correct lifetime
* tracking.
*
* The bpf_stream_page code will be replaced to use kmalloc_nolock() once it
* lands.
*/
struct bpf_stream_page {
refcount_t ref;
u32 consumed;
char buf[];
};
/* Available room to add data to a refcounted page. */
#define BPF_STREAM_PAGE_SZ (PAGE_SIZE - offsetofend(struct bpf_stream_page, consumed))
static DEFINE_PER_CPU(local_trylock_t, stream_local_lock) = INIT_LOCAL_TRYLOCK(stream_local_lock);
static DEFINE_PER_CPU(struct bpf_stream_page *, stream_pcpu_page);
static bool bpf_stream_page_local_lock(unsigned long *flags)
{
return local_trylock_irqsave(&stream_local_lock, *flags);
}
static void bpf_stream_page_local_unlock(unsigned long *flags)
{
local_unlock_irqrestore(&stream_local_lock, *flags);
}
static void bpf_stream_page_free(struct bpf_stream_page *stream_page)
{
struct page *p;
if (!stream_page)
return;
p = virt_to_page(stream_page);
free_pages_nolock(p, 0);
}
static void bpf_stream_page_get(struct bpf_stream_page *stream_page)
{
refcount_inc(&stream_page->ref);
}
static void bpf_stream_page_put(struct bpf_stream_page *stream_page)
{
if (refcount_dec_and_test(&stream_page->ref))
bpf_stream_page_free(stream_page);
}
static void bpf_stream_page_init(struct bpf_stream_page *stream_page)
{
refcount_set(&stream_page->ref, 1);
stream_page->consumed = 0;
}
static struct bpf_stream_page *bpf_stream_page_replace(void)
{
struct bpf_stream_page *stream_page, *old_stream_page;
struct page *page;
page = alloc_pages_nolock(NUMA_NO_NODE, 0);
if (!page)
return NULL;
stream_page = page_address(page);
bpf_stream_page_init(stream_page);
old_stream_page = this_cpu_read(stream_pcpu_page);
if (old_stream_page)
bpf_stream_page_put(old_stream_page);
this_cpu_write(stream_pcpu_page, stream_page);
return stream_page;
}
static int bpf_stream_page_check_room(struct bpf_stream_page *stream_page, int len)
{
int min = offsetof(struct bpf_stream_elem, str[0]);
int consumed = stream_page->consumed;
int total = BPF_STREAM_PAGE_SZ;
int rem = max(0, total - consumed - min);
/* Let's give room of at least 8 bytes. */
WARN_ON_ONCE(rem % 8 != 0);
rem = rem < 8 ? 0 : rem;
return min(len, rem);
}
static void bpf_stream_elem_init(struct bpf_stream_elem *elem, int len)
{
init_llist_node(&elem->node);
elem->total_len = len;
elem->consumed_len = 0;
}
static struct bpf_stream_page *bpf_stream_page_from_elem(struct bpf_stream_elem *elem)
{
unsigned long addr = (unsigned long)elem;
return (struct bpf_stream_page *)PAGE_ALIGN_DOWN(addr);
}
static struct bpf_stream_elem *bpf_stream_page_push_elem(struct bpf_stream_page *stream_page, int len)
{
u32 consumed = stream_page->consumed;
stream_page->consumed += round_up(offsetof(struct bpf_stream_elem, str[len]), 8);
return (struct bpf_stream_elem *)&stream_page->buf[consumed];
}
static struct bpf_stream_elem *bpf_stream_page_reserve_elem(int len)
{
struct bpf_stream_elem *elem = NULL;
struct bpf_stream_page *page;
int room = 0;
page = this_cpu_read(stream_pcpu_page);
if (!page)
page = bpf_stream_page_replace();
if (!page)
return NULL;
room = bpf_stream_page_check_room(page, len);
if (room != len)
page = bpf_stream_page_replace();
if (!page)
return NULL;
bpf_stream_page_get(page);
room = bpf_stream_page_check_room(page, len);
WARN_ON_ONCE(room != len);
elem = bpf_stream_page_push_elem(page, room);
bpf_stream_elem_init(elem, room);
return elem;
}
static struct bpf_stream_elem *bpf_stream_elem_alloc(int len)
{
const int max_len = ARRAY_SIZE((struct bpf_bprintf_buffers){}.buf);
struct bpf_stream_elem *elem;
unsigned long flags;
BUILD_BUG_ON(max_len > BPF_STREAM_PAGE_SZ);
/*
* Length denotes the amount of data to be written as part of stream element,
* thus includes '\0' byte. We're capped by how much bpf_bprintf_buffers can
* accomodate, therefore deny allocations that won't fit into them.
*/
if (len < 0 || len > max_len)
return NULL;
if (!bpf_stream_page_local_lock(&flags))
return NULL;
elem = bpf_stream_page_reserve_elem(len);
bpf_stream_page_local_unlock(&flags);
return elem;
}
static int __bpf_stream_push_str(struct llist_head *log, const char *str, int len)
{
struct bpf_stream_elem *elem = NULL;
/*
* Allocate a bpf_prog_stream_elem and push it to the bpf_prog_stream
* log, elements will be popped at once and reversed to print the log.
*/
elem = bpf_stream_elem_alloc(len);
if (!elem)
return -ENOMEM;
memcpy(elem->str, str, len);
llist_add(&elem->node, log);
return 0;
}
static int bpf_stream_consume_capacity(struct bpf_stream *stream, int len)
{
if (atomic_read(&stream->capacity) >= BPF_STREAM_MAX_CAPACITY)
return -ENOSPC;
if (atomic_add_return(len, &stream->capacity) >= BPF_STREAM_MAX_CAPACITY) {
atomic_sub(len, &stream->capacity);
return -ENOSPC;
}
return 0;
}
static void bpf_stream_release_capacity(struct bpf_stream *stream, struct bpf_stream_elem *elem)
{
int len = elem->total_len;
atomic_sub(len, &stream->capacity);
}
static int bpf_stream_push_str(struct bpf_stream *stream, const char *str, int len)
{
int ret = bpf_stream_consume_capacity(stream, len);
return ret ?: __bpf_stream_push_str(&stream->log, str, len);
}
static struct bpf_stream *bpf_stream_get(enum bpf_stream_id stream_id, struct bpf_prog_aux *aux)
{
if (stream_id != BPF_STDOUT && stream_id != BPF_STDERR)
return NULL;
return &aux->stream[stream_id - 1];
}
static void bpf_stream_free_elem(struct bpf_stream_elem *elem)
{
struct bpf_stream_page *p;
p = bpf_stream_page_from_elem(elem);
bpf_stream_page_put(p);
}
static void bpf_stream_free_list(struct llist_node *list)
{
struct bpf_stream_elem *elem, *tmp;
llist_for_each_entry_safe(elem, tmp, list, node)
bpf_stream_free_elem(elem);
}
static struct llist_node *bpf_stream_backlog_peek(struct bpf_stream *stream)
{
return stream->backlog_head;
}
static struct llist_node *bpf_stream_backlog_pop(struct bpf_stream *stream)
{
struct llist_node *node;
node = stream->backlog_head;
if (stream->backlog_head == stream->backlog_tail)
stream->backlog_head = stream->backlog_tail = NULL;
else
stream->backlog_head = node->next;
return node;
}
static void bpf_stream_backlog_fill(struct bpf_stream *stream)
{
struct llist_node *head, *tail;
if (llist_empty(&stream->log))
return;
tail = llist_del_all(&stream->log);
if (!tail)
return;
head = llist_reverse_order(tail);
if (!stream->backlog_head) {
stream->backlog_head = head;
stream->backlog_tail = tail;
} else {
stream->backlog_tail->next = head;
stream->backlog_tail = tail;
}
return;
}
static bool bpf_stream_consume_elem(struct bpf_stream_elem *elem, int *len)
{
int rem = elem->total_len - elem->consumed_len;
int used = min(rem, *len);
elem->consumed_len += used;
*len -= used;
return elem->consumed_len == elem->total_len;
}
static int bpf_stream_read(struct bpf_stream *stream, void __user *buf, int len)
{
int rem_len = len, cons_len, ret = 0;
struct bpf_stream_elem *elem = NULL;
struct llist_node *node;
mutex_lock(&stream->lock);
while (rem_len) {
int pos = len - rem_len;
bool cont;
node = bpf_stream_backlog_peek(stream);
if (!node) {
bpf_stream_backlog_fill(stream);
node = bpf_stream_backlog_peek(stream);
}
if (!node)
break;
elem = container_of(node, typeof(*elem), node);
cons_len = elem->consumed_len;
cont = bpf_stream_consume_elem(elem, &rem_len) == false;
ret = copy_to_user(buf + pos, elem->str + cons_len,
elem->consumed_len - cons_len);
/* Restore in case of error. */
if (ret) {
ret = -EFAULT;
elem->consumed_len = cons_len;
break;
}
if (cont)
continue;
bpf_stream_backlog_pop(stream);
bpf_stream_release_capacity(stream, elem);
bpf_stream_free_elem(elem);
}
mutex_unlock(&stream->lock);
return ret ? ret : len - rem_len;
}
int bpf_prog_stream_read(struct bpf_prog *prog, enum bpf_stream_id stream_id, void __user *buf, int len)
{
struct bpf_stream *stream;
stream = bpf_stream_get(stream_id, prog->aux);
if (!stream)
return -ENOENT;
return bpf_stream_read(stream, buf, len);
}
__bpf_kfunc_start_defs();
/*
* Avoid using enum bpf_stream_id so that kfunc users don't have to pull in the
* enum in headers.
*/
__bpf_kfunc int bpf_stream_vprintk(int stream_id, const char *fmt__str, const void *args, u32 len__sz, void *aux__prog)
{
struct bpf_bprintf_data data = {
.get_bin_args = true,
.get_buf = true,
};
struct bpf_prog_aux *aux = aux__prog;
u32 fmt_size = strlen(fmt__str) + 1;
struct bpf_stream *stream;
u32 data_len = len__sz;
int ret, num_args;
stream = bpf_stream_get(stream_id, aux);
if (!stream)
return -ENOENT;
if (data_len & 7 || data_len > MAX_BPRINTF_VARARGS * 8 ||
(data_len && !args))
return -EINVAL;
num_args = data_len / 8;
ret = bpf_bprintf_prepare(fmt__str, fmt_size, args, num_args, &data);
if (ret < 0)
return ret;
ret = bstr_printf(data.buf, MAX_BPRINTF_BUF, fmt__str, data.bin_args);
/* Exclude NULL byte during push. */
ret = bpf_stream_push_str(stream, data.buf, ret);
bpf_bprintf_cleanup(&data);
return ret;
}
__bpf_kfunc_end_defs();
/* Added kfunc to common_btf_ids */
void bpf_prog_stream_init(struct bpf_prog *prog)
{
int i;
for (i = 0; i < ARRAY_SIZE(prog->aux->stream); i++) {
atomic_set(&prog->aux->stream[i].capacity, 0);
init_llist_head(&prog->aux->stream[i].log);
mutex_init(&prog->aux->stream[i].lock);
prog->aux->stream[i].backlog_head = NULL;
prog->aux->stream[i].backlog_tail = NULL;
}
}
void bpf_prog_stream_free(struct bpf_prog *prog)
{
struct llist_node *list;
int i;
for (i = 0; i < ARRAY_SIZE(prog->aux->stream); i++) {
list = llist_del_all(&prog->aux->stream[i].log);
bpf_stream_free_list(list);
bpf_stream_free_list(prog->aux->stream[i].backlog_head);
}
}
void bpf_stream_stage_init(struct bpf_stream_stage *ss)
{
init_llist_head(&ss->log);
ss->len = 0;
}
void bpf_stream_stage_free(struct bpf_stream_stage *ss)
{
struct llist_node *node;
node = llist_del_all(&ss->log);
bpf_stream_free_list(node);
}
int bpf_stream_stage_printk(struct bpf_stream_stage *ss, const char *fmt, ...)
{
struct bpf_bprintf_buffers *buf;
va_list args;
int ret;
if (bpf_try_get_buffers(&buf))
return -EBUSY;
va_start(args, fmt);
ret = vsnprintf(buf->buf, ARRAY_SIZE(buf->buf), fmt, args);
va_end(args);
ss->len += ret;
/* Exclude NULL byte during push. */
ret = __bpf_stream_push_str(&ss->log, buf->buf, ret);
bpf_put_buffers();
return ret;
}
int bpf_stream_stage_commit(struct bpf_stream_stage *ss, struct bpf_prog *prog,
enum bpf_stream_id stream_id)
{
struct llist_node *list, *head, *tail;
struct bpf_stream *stream;
int ret;
stream = bpf_stream_get(stream_id, prog->aux);
if (!stream)
return -EINVAL;
ret = bpf_stream_consume_capacity(stream, ss->len);
if (ret)
return ret;
list = llist_del_all(&ss->log);
head = tail = list;
if (!list)
return 0;
while (llist_next(list)) {
tail = llist_next(list);
list = tail;
}
llist_add_batch(head, tail, &stream->log);
return 0;
}
struct dump_stack_ctx {
struct bpf_stream_stage *ss;
int err;
};
static bool dump_stack_cb(void *cookie, u64 ip, u64 sp, u64 bp)
{
struct dump_stack_ctx *ctxp = cookie;
const char *file = "", *line = "";
struct bpf_prog *prog;
int num, ret;
rcu_read_lock();
prog = bpf_prog_ksym_find(ip);
rcu_read_unlock();
if (prog) {
ret = bpf_prog_get_file_line(prog, ip, &file, &line, &num);
if (ret < 0)
goto end;
ctxp->err = bpf_stream_stage_printk(ctxp->ss, "%pS\n %s @ %s:%d\n",
(void *)(long)ip, line, file, num);
return !ctxp->err;
}
end:
ctxp->err = bpf_stream_stage_printk(ctxp->ss, "%pS\n", (void *)(long)ip);
return !ctxp->err;
}
int bpf_stream_stage_dump_stack(struct bpf_stream_stage *ss)
{
struct dump_stack_ctx ctx = { .ss = ss };
int ret;
ret = bpf_stream_stage_printk(ss, "CPU: %d UID: %d PID: %d Comm: %s\n",
raw_smp_processor_id(), __kuid_val(current_real_cred()->euid),
current->pid, current->comm);
if (ret)
return ret;
ret = bpf_stream_stage_printk(ss, "Call trace:\n");
if (ret)
return ret;
arch_bpf_stack_walk(dump_stack_cb, &ctx);
if (ctx.err)
return ctx.err;
return bpf_stream_stage_printk(ss, "\n");
}

View file

@ -3069,7 +3069,7 @@ static int bpf_obj_get(const union bpf_attr *attr)
*/ */
void bpf_link_init_sleepable(struct bpf_link *link, enum bpf_link_type type, void bpf_link_init_sleepable(struct bpf_link *link, enum bpf_link_type type,
const struct bpf_link_ops *ops, struct bpf_prog *prog, const struct bpf_link_ops *ops, struct bpf_prog *prog,
bool sleepable) enum bpf_attach_type attach_type, bool sleepable)
{ {
WARN_ON(ops->dealloc && ops->dealloc_deferred); WARN_ON(ops->dealloc && ops->dealloc_deferred);
atomic64_set(&link->refcnt, 1); atomic64_set(&link->refcnt, 1);
@ -3078,12 +3078,14 @@ void bpf_link_init_sleepable(struct bpf_link *link, enum bpf_link_type type,
link->id = 0; link->id = 0;
link->ops = ops; link->ops = ops;
link->prog = prog; link->prog = prog;
link->attach_type = attach_type;
} }
void bpf_link_init(struct bpf_link *link, enum bpf_link_type type, void bpf_link_init(struct bpf_link *link, enum bpf_link_type type,
const struct bpf_link_ops *ops, struct bpf_prog *prog) const struct bpf_link_ops *ops, struct bpf_prog *prog,
enum bpf_attach_type attach_type)
{ {
bpf_link_init_sleepable(link, type, ops, prog, false); bpf_link_init_sleepable(link, type, ops, prog, attach_type, false);
} }
static void bpf_link_free_id(int id) static void bpf_link_free_id(int id)
@ -3228,7 +3230,14 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
char prog_tag[sizeof(prog->tag) * 2 + 1] = { }; char prog_tag[sizeof(prog->tag) * 2 + 1] = { };
if (type < ARRAY_SIZE(bpf_link_type_strs) && bpf_link_type_strs[type]) { if (type < ARRAY_SIZE(bpf_link_type_strs) && bpf_link_type_strs[type]) {
seq_printf(m, "link_type:\t%s\n", bpf_link_type_strs[type]); if (link->type == BPF_LINK_TYPE_KPROBE_MULTI)
seq_printf(m, "link_type:\t%s\n", link->flags == BPF_F_KPROBE_MULTI_RETURN ?
"kretprobe_multi" : "kprobe_multi");
else if (link->type == BPF_LINK_TYPE_UPROBE_MULTI)
seq_printf(m, "link_type:\t%s\n", link->flags == BPF_F_UPROBE_MULTI_RETURN ?
"uretprobe_multi" : "uprobe_multi");
else
seq_printf(m, "link_type:\t%s\n", bpf_link_type_strs[type]);
} else { } else {
WARN_ONCE(1, "missing BPF_LINK_TYPE(...) for link type %u\n", type); WARN_ONCE(1, "missing BPF_LINK_TYPE(...) for link type %u\n", type);
seq_printf(m, "link_type:\t<%u>\n", type); seq_printf(m, "link_type:\t<%u>\n", type);
@ -3403,10 +3412,12 @@ static void bpf_tracing_link_show_fdinfo(const struct bpf_link *link,
seq_printf(seq, seq_printf(seq,
"attach_type:\t%d\n" "attach_type:\t%d\n"
"target_obj_id:\t%u\n" "target_obj_id:\t%u\n"
"target_btf_id:\t%u\n", "target_btf_id:\t%u\n"
tr_link->attach_type, "cookie:\t%llu\n",
link->attach_type,
target_obj_id, target_obj_id,
target_btf_id); target_btf_id,
tr_link->link.cookie);
} }
static int bpf_tracing_link_fill_link_info(const struct bpf_link *link, static int bpf_tracing_link_fill_link_info(const struct bpf_link *link,
@ -3415,7 +3426,8 @@ static int bpf_tracing_link_fill_link_info(const struct bpf_link *link,
struct bpf_tracing_link *tr_link = struct bpf_tracing_link *tr_link =
container_of(link, struct bpf_tracing_link, link.link); container_of(link, struct bpf_tracing_link, link.link);
info->tracing.attach_type = tr_link->attach_type; info->tracing.attach_type = link->attach_type;
info->tracing.cookie = tr_link->link.cookie;
bpf_trampoline_unpack_key(tr_link->trampoline->key, bpf_trampoline_unpack_key(tr_link->trampoline->key,
&info->tracing.target_obj_id, &info->tracing.target_obj_id,
&info->tracing.target_btf_id); &info->tracing.target_btf_id);
@ -3433,7 +3445,8 @@ static const struct bpf_link_ops bpf_tracing_link_lops = {
static int bpf_tracing_prog_attach(struct bpf_prog *prog, static int bpf_tracing_prog_attach(struct bpf_prog *prog,
int tgt_prog_fd, int tgt_prog_fd,
u32 btf_id, u32 btf_id,
u64 bpf_cookie) u64 bpf_cookie,
enum bpf_attach_type attach_type)
{ {
struct bpf_link_primer link_primer; struct bpf_link_primer link_primer;
struct bpf_prog *tgt_prog = NULL; struct bpf_prog *tgt_prog = NULL;
@ -3501,8 +3514,8 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog,
goto out_put_prog; goto out_put_prog;
} }
bpf_link_init(&link->link.link, BPF_LINK_TYPE_TRACING, bpf_link_init(&link->link.link, BPF_LINK_TYPE_TRACING,
&bpf_tracing_link_lops, prog); &bpf_tracing_link_lops, prog, attach_type);
link->attach_type = prog->expected_attach_type;
link->link.cookie = bpf_cookie; link->link.cookie = bpf_cookie;
mutex_lock(&prog->aux->dst_mutex); mutex_lock(&prog->aux->dst_mutex);
@ -3651,8 +3664,10 @@ static void bpf_raw_tp_link_show_fdinfo(const struct bpf_link *link,
container_of(link, struct bpf_raw_tp_link, link); container_of(link, struct bpf_raw_tp_link, link);
seq_printf(seq, seq_printf(seq,
"tp_name:\t%s\n", "tp_name:\t%s\n"
raw_tp_link->btp->tp->name); "cookie:\t%llu\n",
raw_tp_link->btp->tp->name,
raw_tp_link->cookie);
} }
static int bpf_copy_to_user(char __user *ubuf, const char *buf, u32 ulen, static int bpf_copy_to_user(char __user *ubuf, const char *buf, u32 ulen,
@ -3688,6 +3703,7 @@ static int bpf_raw_tp_link_fill_link_info(const struct bpf_link *link,
return -EINVAL; return -EINVAL;
info->raw_tracepoint.tp_name_len = tp_len + 1; info->raw_tracepoint.tp_name_len = tp_len + 1;
info->raw_tracepoint.cookie = raw_tp_link->cookie;
if (!ubuf) if (!ubuf)
return 0; return 0;
@ -3794,6 +3810,32 @@ static int bpf_perf_link_fill_kprobe(const struct perf_event *event,
info->perf_event.kprobe.cookie = event->bpf_cookie; info->perf_event.kprobe.cookie = event->bpf_cookie;
return 0; return 0;
} }
static void bpf_perf_link_fdinfo_kprobe(const struct perf_event *event,
struct seq_file *seq)
{
const char *name;
int err;
u32 prog_id, type;
u64 offset, addr;
unsigned long missed;
err = bpf_get_perf_event_info(event, &prog_id, &type, &name,
&offset, &addr, &missed);
if (err)
return;
seq_printf(seq,
"name:\t%s\n"
"offset:\t%#llx\n"
"missed:\t%lu\n"
"addr:\t%#llx\n"
"event_type:\t%s\n"
"cookie:\t%llu\n",
name, offset, missed, addr,
type == BPF_FD_TYPE_KRETPROBE ? "kretprobe" : "kprobe",
event->bpf_cookie);
}
#endif #endif
#ifdef CONFIG_UPROBE_EVENTS #ifdef CONFIG_UPROBE_EVENTS
@ -3822,6 +3864,31 @@ static int bpf_perf_link_fill_uprobe(const struct perf_event *event,
info->perf_event.uprobe.ref_ctr_offset = ref_ctr_offset; info->perf_event.uprobe.ref_ctr_offset = ref_ctr_offset;
return 0; return 0;
} }
static void bpf_perf_link_fdinfo_uprobe(const struct perf_event *event,
struct seq_file *seq)
{
const char *name;
int err;
u32 prog_id, type;
u64 offset, ref_ctr_offset;
unsigned long missed;
err = bpf_get_perf_event_info(event, &prog_id, &type, &name,
&offset, &ref_ctr_offset, &missed);
if (err)
return;
seq_printf(seq,
"name:\t%s\n"
"offset:\t%#llx\n"
"ref_ctr_offset:\t%#llx\n"
"event_type:\t%s\n"
"cookie:\t%llu\n",
name, offset, ref_ctr_offset,
type == BPF_FD_TYPE_URETPROBE ? "uretprobe" : "uprobe",
event->bpf_cookie);
}
#endif #endif
static int bpf_perf_link_fill_probe(const struct perf_event *event, static int bpf_perf_link_fill_probe(const struct perf_event *event,
@ -3890,10 +3957,79 @@ static int bpf_perf_link_fill_link_info(const struct bpf_link *link,
} }
} }
static void bpf_perf_event_link_show_fdinfo(const struct perf_event *event,
struct seq_file *seq)
{
seq_printf(seq,
"type:\t%u\n"
"config:\t%llu\n"
"event_type:\t%s\n"
"cookie:\t%llu\n",
event->attr.type, event->attr.config,
"event", event->bpf_cookie);
}
static void bpf_tracepoint_link_show_fdinfo(const struct perf_event *event,
struct seq_file *seq)
{
int err;
const char *name;
u32 prog_id;
err = bpf_get_perf_event_info(event, &prog_id, NULL, &name, NULL,
NULL, NULL);
if (err)
return;
seq_printf(seq,
"tp_name:\t%s\n"
"event_type:\t%s\n"
"cookie:\t%llu\n",
name, "tracepoint", event->bpf_cookie);
}
static void bpf_probe_link_show_fdinfo(const struct perf_event *event,
struct seq_file *seq)
{
#ifdef CONFIG_KPROBE_EVENTS
if (event->tp_event->flags & TRACE_EVENT_FL_KPROBE)
return bpf_perf_link_fdinfo_kprobe(event, seq);
#endif
#ifdef CONFIG_UPROBE_EVENTS
if (event->tp_event->flags & TRACE_EVENT_FL_UPROBE)
return bpf_perf_link_fdinfo_uprobe(event, seq);
#endif
}
static void bpf_perf_link_show_fdinfo(const struct bpf_link *link,
struct seq_file *seq)
{
struct bpf_perf_link *perf_link;
const struct perf_event *event;
perf_link = container_of(link, struct bpf_perf_link, link);
event = perf_get_event(perf_link->perf_file);
if (IS_ERR(event))
return;
switch (event->prog->type) {
case BPF_PROG_TYPE_PERF_EVENT:
return bpf_perf_event_link_show_fdinfo(event, seq);
case BPF_PROG_TYPE_TRACEPOINT:
return bpf_tracepoint_link_show_fdinfo(event, seq);
case BPF_PROG_TYPE_KPROBE:
return bpf_probe_link_show_fdinfo(event, seq);
default:
return;
}
}
static const struct bpf_link_ops bpf_perf_link_lops = { static const struct bpf_link_ops bpf_perf_link_lops = {
.release = bpf_perf_link_release, .release = bpf_perf_link_release,
.dealloc = bpf_perf_link_dealloc, .dealloc = bpf_perf_link_dealloc,
.fill_link_info = bpf_perf_link_fill_link_info, .fill_link_info = bpf_perf_link_fill_link_info,
.show_fdinfo = bpf_perf_link_show_fdinfo,
}; };
static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
@ -3916,7 +4052,8 @@ static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *pro
err = -ENOMEM; err = -ENOMEM;
goto out_put_file; goto out_put_file;
} }
bpf_link_init(&link->link, BPF_LINK_TYPE_PERF_EVENT, &bpf_perf_link_lops, prog); bpf_link_init(&link->link, BPF_LINK_TYPE_PERF_EVENT, &bpf_perf_link_lops, prog,
attr->link_create.attach_type);
link->perf_file = perf_file; link->perf_file = perf_file;
err = bpf_link_prime(&link->link, &link_primer); err = bpf_link_prime(&link->link, &link_primer);
@ -3948,7 +4085,8 @@ static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *pro
#endif /* CONFIG_PERF_EVENTS */ #endif /* CONFIG_PERF_EVENTS */
static int bpf_raw_tp_link_attach(struct bpf_prog *prog, static int bpf_raw_tp_link_attach(struct bpf_prog *prog,
const char __user *user_tp_name, u64 cookie) const char __user *user_tp_name, u64 cookie,
enum bpf_attach_type attach_type)
{ {
struct bpf_link_primer link_primer; struct bpf_link_primer link_primer;
struct bpf_raw_tp_link *link; struct bpf_raw_tp_link *link;
@ -3971,7 +4109,7 @@ static int bpf_raw_tp_link_attach(struct bpf_prog *prog,
tp_name = prog->aux->attach_func_name; tp_name = prog->aux->attach_func_name;
break; break;
} }
return bpf_tracing_prog_attach(prog, 0, 0, 0); return bpf_tracing_prog_attach(prog, 0, 0, 0, attach_type);
case BPF_PROG_TYPE_RAW_TRACEPOINT: case BPF_PROG_TYPE_RAW_TRACEPOINT:
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE: case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
if (strncpy_from_user(buf, user_tp_name, sizeof(buf) - 1) < 0) if (strncpy_from_user(buf, user_tp_name, sizeof(buf) - 1) < 0)
@ -3993,7 +4131,7 @@ static int bpf_raw_tp_link_attach(struct bpf_prog *prog,
goto out_put_btp; goto out_put_btp;
} }
bpf_link_init_sleepable(&link->link, BPF_LINK_TYPE_RAW_TRACEPOINT, bpf_link_init_sleepable(&link->link, BPF_LINK_TYPE_RAW_TRACEPOINT,
&bpf_raw_tp_link_lops, prog, &bpf_raw_tp_link_lops, prog, attach_type,
tracepoint_is_faultable(btp->tp)); tracepoint_is_faultable(btp->tp));
link->btp = btp; link->btp = btp;
link->cookie = cookie; link->cookie = cookie;
@ -4035,7 +4173,7 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
tp_name = u64_to_user_ptr(attr->raw_tracepoint.name); tp_name = u64_to_user_ptr(attr->raw_tracepoint.name);
cookie = attr->raw_tracepoint.cookie; cookie = attr->raw_tracepoint.cookie;
fd = bpf_raw_tp_link_attach(prog, tp_name, cookie); fd = bpf_raw_tp_link_attach(prog, tp_name, cookie, prog->expected_attach_type);
if (fd < 0) if (fd < 0)
bpf_prog_put(prog); bpf_prog_put(prog);
return fd; return fd;
@ -4185,6 +4323,25 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,
} }
} }
static bool is_cgroup_prog_type(enum bpf_prog_type ptype, enum bpf_attach_type atype,
bool check_atype)
{
switch (ptype) {
case BPF_PROG_TYPE_CGROUP_DEVICE:
case BPF_PROG_TYPE_CGROUP_SKB:
case BPF_PROG_TYPE_CGROUP_SOCK:
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
case BPF_PROG_TYPE_CGROUP_SYSCTL:
case BPF_PROG_TYPE_SOCK_OPS:
return true;
case BPF_PROG_TYPE_LSM:
return check_atype ? atype == BPF_LSM_CGROUP : true;
default:
return false;
}
}
#define BPF_PROG_ATTACH_LAST_FIELD expected_revision #define BPF_PROG_ATTACH_LAST_FIELD expected_revision
#define BPF_F_ATTACH_MASK_BASE \ #define BPF_F_ATTACH_MASK_BASE \
@ -4215,6 +4372,9 @@ static int bpf_prog_attach(const union bpf_attr *attr)
if (bpf_mprog_supported(ptype)) { if (bpf_mprog_supported(ptype)) {
if (attr->attach_flags & ~BPF_F_ATTACH_MASK_MPROG) if (attr->attach_flags & ~BPF_F_ATTACH_MASK_MPROG)
return -EINVAL; return -EINVAL;
} else if (is_cgroup_prog_type(ptype, 0, false)) {
if (attr->attach_flags & ~(BPF_F_ATTACH_MASK_BASE | BPF_F_ATTACH_MASK_MPROG))
return -EINVAL;
} else { } else {
if (attr->attach_flags & ~BPF_F_ATTACH_MASK_BASE) if (attr->attach_flags & ~BPF_F_ATTACH_MASK_BASE)
return -EINVAL; return -EINVAL;
@ -4232,6 +4392,11 @@ static int bpf_prog_attach(const union bpf_attr *attr)
return -EINVAL; return -EINVAL;
} }
if (is_cgroup_prog_type(ptype, prog->expected_attach_type, true)) {
ret = cgroup_bpf_prog_attach(attr, ptype, prog);
goto out;
}
switch (ptype) { switch (ptype) {
case BPF_PROG_TYPE_SK_SKB: case BPF_PROG_TYPE_SK_SKB:
case BPF_PROG_TYPE_SK_MSG: case BPF_PROG_TYPE_SK_MSG:
@ -4243,20 +4408,6 @@ static int bpf_prog_attach(const union bpf_attr *attr)
case BPF_PROG_TYPE_FLOW_DISSECTOR: case BPF_PROG_TYPE_FLOW_DISSECTOR:
ret = netns_bpf_prog_attach(attr, prog); ret = netns_bpf_prog_attach(attr, prog);
break; break;
case BPF_PROG_TYPE_CGROUP_DEVICE:
case BPF_PROG_TYPE_CGROUP_SKB:
case BPF_PROG_TYPE_CGROUP_SOCK:
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
case BPF_PROG_TYPE_CGROUP_SYSCTL:
case BPF_PROG_TYPE_SOCK_OPS:
case BPF_PROG_TYPE_LSM:
if (ptype == BPF_PROG_TYPE_LSM &&
prog->expected_attach_type != BPF_LSM_CGROUP)
ret = -EINVAL;
else
ret = cgroup_bpf_prog_attach(attr, ptype, prog);
break;
case BPF_PROG_TYPE_SCHED_CLS: case BPF_PROG_TYPE_SCHED_CLS:
if (attr->attach_type == BPF_TCX_INGRESS || if (attr->attach_type == BPF_TCX_INGRESS ||
attr->attach_type == BPF_TCX_EGRESS) attr->attach_type == BPF_TCX_EGRESS)
@ -4267,7 +4418,7 @@ static int bpf_prog_attach(const union bpf_attr *attr)
default: default:
ret = -EINVAL; ret = -EINVAL;
} }
out:
if (ret) if (ret)
bpf_prog_put(prog); bpf_prog_put(prog);
return ret; return ret;
@ -4295,6 +4446,9 @@ static int bpf_prog_detach(const union bpf_attr *attr)
if (IS_ERR(prog)) if (IS_ERR(prog))
return PTR_ERR(prog); return PTR_ERR(prog);
} }
} else if (is_cgroup_prog_type(ptype, 0, false)) {
if (attr->attach_flags || attr->relative_fd)
return -EINVAL;
} else if (attr->attach_flags || } else if (attr->attach_flags ||
attr->relative_fd || attr->relative_fd ||
attr->expected_revision) { attr->expected_revision) {
@ -5085,6 +5239,21 @@ static int bpf_link_get_info_by_fd(struct file *file,
} }
static int token_get_info_by_fd(struct file *file,
struct bpf_token *token,
const union bpf_attr *attr,
union bpf_attr __user *uattr)
{
struct bpf_token_info __user *uinfo = u64_to_user_ptr(attr->info.info);
u32 info_len = attr->info.info_len;
int err;
err = bpf_check_uarg_tail_zero(USER_BPFPTR(uinfo), sizeof(*uinfo), info_len);
if (err)
return err;
return bpf_token_get_info_by_fd(token, attr, uattr);
}
#define BPF_OBJ_GET_INFO_BY_FD_LAST_FIELD info.info #define BPF_OBJ_GET_INFO_BY_FD_LAST_FIELD info.info
static int bpf_obj_get_info_by_fd(const union bpf_attr *attr, static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
@ -5108,6 +5277,9 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
else if (fd_file(f)->f_op == &bpf_link_fops || fd_file(f)->f_op == &bpf_link_fops_poll) else if (fd_file(f)->f_op == &bpf_link_fops || fd_file(f)->f_op == &bpf_link_fops_poll)
return bpf_link_get_info_by_fd(fd_file(f), fd_file(f)->private_data, return bpf_link_get_info_by_fd(fd_file(f), fd_file(f)->private_data,
attr, uattr); attr, uattr);
else if (fd_file(f)->f_op == &bpf_token_fops)
return token_get_info_by_fd(fd_file(f), fd_file(f)->private_data,
attr, uattr);
return -EINVAL; return -EINVAL;
} }
@ -5195,21 +5367,10 @@ static int bpf_task_fd_query_copy(const union bpf_attr *attr,
if (put_user(zero, ubuf)) if (put_user(zero, ubuf))
return -EFAULT; return -EFAULT;
} else if (input_len >= len + 1) {
/* ubuf can hold the string with NULL terminator */
if (copy_to_user(ubuf, buf, len + 1))
return -EFAULT;
} else { } else {
/* ubuf cannot hold the string with NULL terminator, err = bpf_copy_to_user(ubuf, buf, input_len, len);
* do a partial copy with NULL terminator. if (err == -EFAULT)
*/ return err;
char zero = '\0';
err = -ENOSPC;
if (copy_to_user(ubuf, buf, input_len - 1))
return -EFAULT;
if (put_user(zero, ubuf + input_len - 1))
return -EFAULT;
} }
} }
@ -5387,7 +5548,8 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
ret = bpf_tracing_prog_attach(prog, ret = bpf_tracing_prog_attach(prog,
attr->link_create.target_fd, attr->link_create.target_fd,
attr->link_create.target_btf_id, attr->link_create.target_btf_id,
attr->link_create.tracing.cookie); attr->link_create.tracing.cookie,
attr->link_create.attach_type);
break; break;
case BPF_PROG_TYPE_LSM: case BPF_PROG_TYPE_LSM:
case BPF_PROG_TYPE_TRACING: case BPF_PROG_TYPE_TRACING:
@ -5396,7 +5558,8 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
goto out; goto out;
} }
if (prog->expected_attach_type == BPF_TRACE_RAW_TP) if (prog->expected_attach_type == BPF_TRACE_RAW_TP)
ret = bpf_raw_tp_link_attach(prog, NULL, attr->link_create.tracing.cookie); ret = bpf_raw_tp_link_attach(prog, NULL, attr->link_create.tracing.cookie,
attr->link_create.attach_type);
else if (prog->expected_attach_type == BPF_TRACE_ITER) else if (prog->expected_attach_type == BPF_TRACE_ITER)
ret = bpf_iter_link_attach(attr, uattr, prog); ret = bpf_iter_link_attach(attr, uattr, prog);
else if (prog->expected_attach_type == BPF_LSM_CGROUP) else if (prog->expected_attach_type == BPF_LSM_CGROUP)
@ -5405,7 +5568,8 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
ret = bpf_tracing_prog_attach(prog, ret = bpf_tracing_prog_attach(prog,
attr->link_create.target_fd, attr->link_create.target_fd,
attr->link_create.target_btf_id, attr->link_create.target_btf_id,
attr->link_create.tracing.cookie); attr->link_create.tracing.cookie,
attr->link_create.attach_type);
break; break;
case BPF_PROG_TYPE_FLOW_DISSECTOR: case BPF_PROG_TYPE_FLOW_DISSECTOR:
case BPF_PROG_TYPE_SK_LOOKUP: case BPF_PROG_TYPE_SK_LOOKUP:
@ -5794,6 +5958,28 @@ static int token_create(union bpf_attr *attr)
return bpf_token_create(attr); return bpf_token_create(attr);
} }
#define BPF_PROG_STREAM_READ_BY_FD_LAST_FIELD prog_stream_read.prog_fd
static int prog_stream_read(union bpf_attr *attr)
{
char __user *buf = u64_to_user_ptr(attr->prog_stream_read.stream_buf);
u32 len = attr->prog_stream_read.stream_buf_len;
struct bpf_prog *prog;
int ret;
if (CHECK_ATTR(BPF_PROG_STREAM_READ_BY_FD))
return -EINVAL;
prog = bpf_prog_get(attr->prog_stream_read.prog_fd);
if (IS_ERR(prog))
return PTR_ERR(prog);
ret = bpf_prog_stream_read(prog, attr->prog_stream_read.stream_id, buf, len);
bpf_prog_put(prog);
return ret;
}
static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size) static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
{ {
union bpf_attr attr; union bpf_attr attr;
@ -5930,6 +6116,9 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)
case BPF_TOKEN_CREATE: case BPF_TOKEN_CREATE:
err = token_create(&attr); err = token_create(&attr);
break; break;
case BPF_PROG_STREAM_READ_BY_FD:
err = prog_stream_read(&attr);
break;
default: default:
err = -EINVAL; err = -EINVAL;
break; break;

View file

@ -142,7 +142,7 @@ static int tcx_link_prog_attach(struct bpf_link *link, u32 flags, u32 id_or_fd,
u64 revision) u64 revision)
{ {
struct tcx_link *tcx = tcx_link(link); struct tcx_link *tcx = tcx_link(link);
bool created, ingress = tcx->location == BPF_TCX_INGRESS; bool created, ingress = link->attach_type == BPF_TCX_INGRESS;
struct bpf_mprog_entry *entry, *entry_new; struct bpf_mprog_entry *entry, *entry_new;
struct net_device *dev = tcx->dev; struct net_device *dev = tcx->dev;
int ret; int ret;
@ -169,7 +169,7 @@ static int tcx_link_prog_attach(struct bpf_link *link, u32 flags, u32 id_or_fd,
static void tcx_link_release(struct bpf_link *link) static void tcx_link_release(struct bpf_link *link)
{ {
struct tcx_link *tcx = tcx_link(link); struct tcx_link *tcx = tcx_link(link);
bool ingress = tcx->location == BPF_TCX_INGRESS; bool ingress = link->attach_type == BPF_TCX_INGRESS;
struct bpf_mprog_entry *entry, *entry_new; struct bpf_mprog_entry *entry, *entry_new;
struct net_device *dev; struct net_device *dev;
int ret = 0; int ret = 0;
@ -204,7 +204,7 @@ static int tcx_link_update(struct bpf_link *link, struct bpf_prog *nprog,
struct bpf_prog *oprog) struct bpf_prog *oprog)
{ {
struct tcx_link *tcx = tcx_link(link); struct tcx_link *tcx = tcx_link(link);
bool ingress = tcx->location == BPF_TCX_INGRESS; bool ingress = link->attach_type == BPF_TCX_INGRESS;
struct bpf_mprog_entry *entry, *entry_new; struct bpf_mprog_entry *entry, *entry_new;
struct net_device *dev; struct net_device *dev;
int ret = 0; int ret = 0;
@ -260,8 +260,8 @@ static void tcx_link_fdinfo(const struct bpf_link *link, struct seq_file *seq)
seq_printf(seq, "ifindex:\t%u\n", ifindex); seq_printf(seq, "ifindex:\t%u\n", ifindex);
seq_printf(seq, "attach_type:\t%u (%s)\n", seq_printf(seq, "attach_type:\t%u (%s)\n",
tcx->location, link->attach_type,
tcx->location == BPF_TCX_INGRESS ? "ingress" : "egress"); link->attach_type == BPF_TCX_INGRESS ? "ingress" : "egress");
} }
static int tcx_link_fill_info(const struct bpf_link *link, static int tcx_link_fill_info(const struct bpf_link *link,
@ -276,7 +276,7 @@ static int tcx_link_fill_info(const struct bpf_link *link,
rtnl_unlock(); rtnl_unlock();
info->tcx.ifindex = ifindex; info->tcx.ifindex = ifindex;
info->tcx.attach_type = tcx->location; info->tcx.attach_type = link->attach_type;
return 0; return 0;
} }
@ -301,8 +301,8 @@ static int tcx_link_init(struct tcx_link *tcx,
struct net_device *dev, struct net_device *dev,
struct bpf_prog *prog) struct bpf_prog *prog)
{ {
bpf_link_init(&tcx->link, BPF_LINK_TYPE_TCX, &tcx_link_lops, prog); bpf_link_init(&tcx->link, BPF_LINK_TYPE_TCX, &tcx_link_lops, prog,
tcx->location = attr->link_create.attach_type; attr->link_create.attach_type);
tcx->dev = dev; tcx->dev = dev;
return bpf_link_prime(&tcx->link, link_primer); return bpf_link_prime(&tcx->link, link_primer);
} }

View file

@ -83,6 +83,11 @@ struct tnum tnum_sub(struct tnum a, struct tnum b)
return TNUM(dv & ~mu, mu); return TNUM(dv & ~mu, mu);
} }
struct tnum tnum_neg(struct tnum a)
{
return tnum_sub(TNUM(0, 0), a);
}
struct tnum tnum_and(struct tnum a, struct tnum b) struct tnum tnum_and(struct tnum a, struct tnum b)
{ {
u64 alpha, beta, v; u64 alpha, beta, v;

View file

@ -103,7 +103,7 @@ static void bpf_token_show_fdinfo(struct seq_file *m, struct file *filp)
static const struct inode_operations bpf_token_iops = { }; static const struct inode_operations bpf_token_iops = { };
static const struct file_operations bpf_token_fops = { const struct file_operations bpf_token_fops = {
.release = bpf_token_release, .release = bpf_token_release,
.show_fdinfo = bpf_token_show_fdinfo, .show_fdinfo = bpf_token_show_fdinfo,
}; };
@ -210,6 +210,29 @@ out_file:
return err; return err;
} }
int bpf_token_get_info_by_fd(struct bpf_token *token,
const union bpf_attr *attr,
union bpf_attr __user *uattr)
{
struct bpf_token_info __user *uinfo = u64_to_user_ptr(attr->info.info);
struct bpf_token_info info;
u32 info_len = attr->info.info_len;
info_len = min_t(u32, info_len, sizeof(info));
memset(&info, 0, sizeof(info));
info.allowed_cmds = token->allowed_cmds;
info.allowed_maps = token->allowed_maps;
info.allowed_progs = token->allowed_progs;
info.allowed_attachs = token->allowed_attachs;
if (copy_to_user(uinfo, &info, info_len) ||
put_user(info_len, &uattr->info.info_len))
return -EFAULT;
return 0;
}
struct bpf_token *bpf_token_get_from_fd(u32 ufd) struct bpf_token *bpf_token_get_from_fd(u32 ufd)
{ {
CLASS(fd, f)(ufd); CLASS(fd, f)(ufd);

View file

@ -674,7 +674,8 @@ static const struct bpf_link_ops bpf_shim_tramp_link_lops = {
static struct bpf_shim_tramp_link *cgroup_shim_alloc(const struct bpf_prog *prog, static struct bpf_shim_tramp_link *cgroup_shim_alloc(const struct bpf_prog *prog,
bpf_func_t bpf_func, bpf_func_t bpf_func,
int cgroup_atype) int cgroup_atype,
enum bpf_attach_type attach_type)
{ {
struct bpf_shim_tramp_link *shim_link = NULL; struct bpf_shim_tramp_link *shim_link = NULL;
struct bpf_prog *p; struct bpf_prog *p;
@ -701,7 +702,7 @@ static struct bpf_shim_tramp_link *cgroup_shim_alloc(const struct bpf_prog *prog
p->expected_attach_type = BPF_LSM_MAC; p->expected_attach_type = BPF_LSM_MAC;
bpf_prog_inc(p); bpf_prog_inc(p);
bpf_link_init(&shim_link->link.link, BPF_LINK_TYPE_UNSPEC, bpf_link_init(&shim_link->link.link, BPF_LINK_TYPE_UNSPEC,
&bpf_shim_tramp_link_lops, p); &bpf_shim_tramp_link_lops, p, attach_type);
bpf_cgroup_atype_get(p->aux->attach_btf_id, cgroup_atype); bpf_cgroup_atype_get(p->aux->attach_btf_id, cgroup_atype);
return shim_link; return shim_link;
@ -726,7 +727,8 @@ static struct bpf_shim_tramp_link *cgroup_shim_find(struct bpf_trampoline *tr,
} }
int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog, int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog,
int cgroup_atype) int cgroup_atype,
enum bpf_attach_type attach_type)
{ {
struct bpf_shim_tramp_link *shim_link = NULL; struct bpf_shim_tramp_link *shim_link = NULL;
struct bpf_attach_target_info tgt_info = {}; struct bpf_attach_target_info tgt_info = {};
@ -763,7 +765,7 @@ int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog,
/* Allocate and install new shim. */ /* Allocate and install new shim. */
shim_link = cgroup_shim_alloc(prog, bpf_func, cgroup_atype); shim_link = cgroup_shim_alloc(prog, bpf_func, cgroup_atype, attach_type);
if (!shim_link) { if (!shim_link) {
err = -ENOMEM; err = -ENOMEM;
goto err; goto err;
@ -911,27 +913,32 @@ static u64 notrace __bpf_prog_enter_recur(struct bpf_prog *prog, struct bpf_tram
return bpf_prog_start_time(); return bpf_prog_start_time();
} }
static void notrace update_prog_stats(struct bpf_prog *prog, static void notrace __update_prog_stats(struct bpf_prog *prog, u64 start)
u64 start)
{ {
struct bpf_prog_stats *stats; struct bpf_prog_stats *stats;
unsigned long flags;
u64 duration;
if (static_branch_unlikely(&bpf_stats_enabled_key) && /*
/* static_key could be enabled in __bpf_prog_enter* * static_key could be enabled in __bpf_prog_enter* and disabled in
* and disabled in __bpf_prog_exit*. * __bpf_prog_exit*. And vice versa. Check that 'start' is valid.
* And vice versa. */
* Hence check that 'start' is valid. if (start <= NO_START_TIME)
*/ return;
start > NO_START_TIME) {
u64 duration = sched_clock() - start;
unsigned long flags;
stats = this_cpu_ptr(prog->stats); duration = sched_clock() - start;
flags = u64_stats_update_begin_irqsave(&stats->syncp); stats = this_cpu_ptr(prog->stats);
u64_stats_inc(&stats->cnt); flags = u64_stats_update_begin_irqsave(&stats->syncp);
u64_stats_add(&stats->nsecs, duration); u64_stats_inc(&stats->cnt);
u64_stats_update_end_irqrestore(&stats->syncp, flags); u64_stats_add(&stats->nsecs, duration);
} u64_stats_update_end_irqrestore(&stats->syncp, flags);
}
static __always_inline void notrace update_prog_stats(struct bpf_prog *prog,
u64 start)
{
if (static_branch_unlikely(&bpf_stats_enabled_key))
__update_prog_stats(prog, start);
} }
static void notrace __bpf_prog_exit_recur(struct bpf_prog *prog, u64 start, static void notrace __bpf_prog_exit_recur(struct bpf_prog *prog, u64 start,

File diff suppressed because it is too large Load diff

View file

@ -2074,6 +2074,11 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp)
for_each_subsys(ss, ssid) for_each_subsys(ss, ssid)
INIT_LIST_HEAD(&cgrp->e_csets[ssid]); INIT_LIST_HEAD(&cgrp->e_csets[ssid]);
#ifdef CONFIG_CGROUP_BPF
for (int i = 0; i < ARRAY_SIZE(cgrp->bpf.revisions); i++)
cgrp->bpf.revisions[i] = 1;
#endif
init_waitqueue_head(&cgrp->offline_waitq); init_waitqueue_head(&cgrp->offline_waitq);
INIT_WORK(&cgrp->release_agent_work, cgroup1_release_agent); INIT_WORK(&cgrp->release_agent_work, cgroup1_release_agent);
} }

View file

@ -829,8 +829,7 @@ static struct bpf_iter_reg ksym_iter_reg_info = {
.seq_info = &ksym_iter_seq_info, .seq_info = &ksym_iter_seq_info,
}; };
BTF_ID_LIST(btf_ksym_iter_id) BTF_ID_LIST_SINGLE(btf_ksym_iter_id, struct, kallsym_iter)
BTF_ID(struct, kallsym_iter)
static int __init bpf_ksym_iter_register(void) static int __init bpf_ksym_iter_register(void)
{ {

View file

@ -781,8 +781,7 @@ BPF_CALL_1(bpf_task_pt_regs, struct task_struct *, task)
return (unsigned long) task_pt_regs(task); return (unsigned long) task_pt_regs(task);
} }
BTF_ID_LIST(bpf_task_pt_regs_ids) BTF_ID_LIST_SINGLE(bpf_task_pt_regs_ids, struct, pt_regs)
BTF_ID(struct, pt_regs)
const struct bpf_func_proto bpf_task_pt_regs_proto = { const struct bpf_func_proto bpf_task_pt_regs_proto = {
.func = bpf_task_pt_regs, .func = bpf_task_pt_regs,
@ -1270,7 +1269,7 @@ __bpf_kfunc_start_defs();
* Return: a bpf_key pointer with a valid key pointer if the key is found, a * Return: a bpf_key pointer with a valid key pointer if the key is found, a
* NULL pointer otherwise. * NULL pointer otherwise.
*/ */
__bpf_kfunc struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags) __bpf_kfunc struct bpf_key *bpf_lookup_user_key(s32 serial, u64 flags)
{ {
key_ref_t key_ref; key_ref_t key_ref;
struct bpf_key *bkey; struct bpf_key *bkey;
@ -2466,7 +2465,6 @@ struct bpf_kprobe_multi_link {
u32 cnt; u32 cnt;
u32 mods_cnt; u32 mods_cnt;
struct module **mods; struct module **mods;
u32 flags;
}; };
struct bpf_kprobe_multi_run_ctx { struct bpf_kprobe_multi_run_ctx {
@ -2586,7 +2584,7 @@ static int bpf_kprobe_multi_link_fill_link_info(const struct bpf_link *link,
kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);
info->kprobe_multi.count = kmulti_link->cnt; info->kprobe_multi.count = kmulti_link->cnt;
info->kprobe_multi.flags = kmulti_link->flags; info->kprobe_multi.flags = kmulti_link->link.flags;
info->kprobe_multi.missed = kmulti_link->fp.nmissed; info->kprobe_multi.missed = kmulti_link->fp.nmissed;
if (!uaddrs) if (!uaddrs)
@ -2620,10 +2618,37 @@ static int bpf_kprobe_multi_link_fill_link_info(const struct bpf_link *link,
return err; return err;
} }
#ifdef CONFIG_PROC_FS
static void bpf_kprobe_multi_show_fdinfo(const struct bpf_link *link,
struct seq_file *seq)
{
struct bpf_kprobe_multi_link *kmulti_link;
kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link);
seq_printf(seq,
"kprobe_cnt:\t%u\n"
"missed:\t%lu\n",
kmulti_link->cnt,
kmulti_link->fp.nmissed);
seq_printf(seq, "%s\t %s\n", "cookie", "func");
for (int i = 0; i < kmulti_link->cnt; i++) {
seq_printf(seq,
"%llu\t %pS\n",
kmulti_link->cookies[i],
(void *)kmulti_link->addrs[i]);
}
}
#endif
static const struct bpf_link_ops bpf_kprobe_multi_link_lops = { static const struct bpf_link_ops bpf_kprobe_multi_link_lops = {
.release = bpf_kprobe_multi_link_release, .release = bpf_kprobe_multi_link_release,
.dealloc_deferred = bpf_kprobe_multi_link_dealloc, .dealloc_deferred = bpf_kprobe_multi_link_dealloc,
.fill_link_info = bpf_kprobe_multi_link_fill_link_info, .fill_link_info = bpf_kprobe_multi_link_fill_link_info,
#ifdef CONFIG_PROC_FS
.show_fdinfo = bpf_kprobe_multi_show_fdinfo,
#endif
}; };
static void bpf_kprobe_multi_cookie_swap(void *a, void *b, int size, const void *priv) static void bpf_kprobe_multi_cookie_swap(void *a, void *b, int size, const void *priv)
@ -2960,7 +2985,7 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
} }
bpf_link_init(&link->link, BPF_LINK_TYPE_KPROBE_MULTI, bpf_link_init(&link->link, BPF_LINK_TYPE_KPROBE_MULTI,
&bpf_kprobe_multi_link_lops, prog); &bpf_kprobe_multi_link_lops, prog, attr->link_create.attach_type);
err = bpf_link_prime(&link->link, &link_primer); err = bpf_link_prime(&link->link, &link_primer);
if (err) if (err)
@ -2976,7 +3001,7 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
link->addrs = addrs; link->addrs = addrs;
link->cookies = cookies; link->cookies = cookies;
link->cnt = cnt; link->cnt = cnt;
link->flags = flags; link->link.flags = flags;
if (cookies) { if (cookies) {
/* /*
@ -3045,7 +3070,6 @@ struct bpf_uprobe_multi_link {
struct path path; struct path path;
struct bpf_link link; struct bpf_link link;
u32 cnt; u32 cnt;
u32 flags;
struct bpf_uprobe *uprobes; struct bpf_uprobe *uprobes;
struct task_struct *task; struct task_struct *task;
}; };
@ -3109,7 +3133,7 @@ static int bpf_uprobe_multi_link_fill_link_info(const struct bpf_link *link,
umulti_link = container_of(link, struct bpf_uprobe_multi_link, link); umulti_link = container_of(link, struct bpf_uprobe_multi_link, link);
info->uprobe_multi.count = umulti_link->cnt; info->uprobe_multi.count = umulti_link->cnt;
info->uprobe_multi.flags = umulti_link->flags; info->uprobe_multi.flags = umulti_link->link.flags;
info->uprobe_multi.pid = umulti_link->task ? info->uprobe_multi.pid = umulti_link->task ?
task_pid_nr_ns(umulti_link->task, task_active_pid_ns(current)) : 0; task_pid_nr_ns(umulti_link->task, task_active_pid_ns(current)) : 0;
@ -3154,10 +3178,54 @@ static int bpf_uprobe_multi_link_fill_link_info(const struct bpf_link *link,
return err; return err;
} }
#ifdef CONFIG_PROC_FS
static void bpf_uprobe_multi_show_fdinfo(const struct bpf_link *link,
struct seq_file *seq)
{
struct bpf_uprobe_multi_link *umulti_link;
char *p, *buf;
pid_t pid;
umulti_link = container_of(link, struct bpf_uprobe_multi_link, link);
buf = kmalloc(PATH_MAX, GFP_KERNEL);
if (!buf)
return;
p = d_path(&umulti_link->path, buf, PATH_MAX);
if (IS_ERR(p)) {
kfree(buf);
return;
}
pid = umulti_link->task ?
task_pid_nr_ns(umulti_link->task, task_active_pid_ns(current)) : 0;
seq_printf(seq,
"uprobe_cnt:\t%u\n"
"pid:\t%u\n"
"path:\t%s\n",
umulti_link->cnt, pid, p);
seq_printf(seq, "%s\t %s\t %s\n", "cookie", "offset", "ref_ctr_offset");
for (int i = 0; i < umulti_link->cnt; i++) {
seq_printf(seq,
"%llu\t %#llx\t %#lx\n",
umulti_link->uprobes[i].cookie,
umulti_link->uprobes[i].offset,
umulti_link->uprobes[i].ref_ctr_offset);
}
kfree(buf);
}
#endif
static const struct bpf_link_ops bpf_uprobe_multi_link_lops = { static const struct bpf_link_ops bpf_uprobe_multi_link_lops = {
.release = bpf_uprobe_multi_link_release, .release = bpf_uprobe_multi_link_release,
.dealloc_deferred = bpf_uprobe_multi_link_dealloc, .dealloc_deferred = bpf_uprobe_multi_link_dealloc,
.fill_link_info = bpf_uprobe_multi_link_fill_link_info, .fill_link_info = bpf_uprobe_multi_link_fill_link_info,
#ifdef CONFIG_PROC_FS
.show_fdinfo = bpf_uprobe_multi_show_fdinfo,
#endif
}; };
static int uprobe_prog_run(struct bpf_uprobe *uprobe, static int uprobe_prog_run(struct bpf_uprobe *uprobe,
@ -3369,10 +3437,10 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
link->uprobes = uprobes; link->uprobes = uprobes;
link->path = path; link->path = path;
link->task = task; link->task = task;
link->flags = flags; link->link.flags = flags;
bpf_link_init(&link->link, BPF_LINK_TYPE_UPROBE_MULTI, bpf_link_init(&link->link, BPF_LINK_TYPE_UPROBE_MULTI,
&bpf_uprobe_multi_link_lops, prog); &bpf_uprobe_multi_link_lops, prog, attr->link_create.attach_type);
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
uprobes[i].uprobe = uprobe_register(d_real_inode(link->path.dentry), uprobes[i].uprobe = uprobe_register(d_real_inode(link->path.dentry),

View file

@ -1,191 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* umd - User mode driver support
*/
#include <linux/shmem_fs.h>
#include <linux/pipe_fs_i.h>
#include <linux/mount.h>
#include <linux/fs_struct.h>
#include <linux/task_work.h>
#include <linux/usermode_driver.h>
static struct vfsmount *blob_to_mnt(const void *data, size_t len, const char *name)
{
struct file_system_type *type;
struct vfsmount *mnt;
struct file *file;
ssize_t written;
loff_t pos = 0;
type = get_fs_type("tmpfs");
if (!type)
return ERR_PTR(-ENODEV);
mnt = kern_mount(type);
put_filesystem(type);
if (IS_ERR(mnt))
return mnt;
file = file_open_root_mnt(mnt, name, O_CREAT | O_WRONLY, 0700);
if (IS_ERR(file)) {
kern_unmount(mnt);
return ERR_CAST(file);
}
written = kernel_write(file, data, len, &pos);
if (written != len) {
int err = written;
if (err >= 0)
err = -ENOMEM;
filp_close(file, NULL);
kern_unmount(mnt);
return ERR_PTR(err);
}
fput(file);
/* Flush delayed fput so exec can open the file read-only */
flush_delayed_fput();
task_work_run();
return mnt;
}
/**
* umd_load_blob - Remember a blob of bytes for fork_usermode_driver
* @info: information about usermode driver
* @data: a blob of bytes that can be executed as a file
* @len: The lentgh of the blob
*
*/
int umd_load_blob(struct umd_info *info, const void *data, size_t len)
{
struct vfsmount *mnt;
if (WARN_ON_ONCE(info->wd.dentry || info->wd.mnt))
return -EBUSY;
mnt = blob_to_mnt(data, len, info->driver_name);
if (IS_ERR(mnt))
return PTR_ERR(mnt);
info->wd.mnt = mnt;
info->wd.dentry = mnt->mnt_root;
return 0;
}
EXPORT_SYMBOL_GPL(umd_load_blob);
/**
* umd_unload_blob - Disassociate @info from a previously loaded blob
* @info: information about usermode driver
*
*/
int umd_unload_blob(struct umd_info *info)
{
if (WARN_ON_ONCE(!info->wd.mnt ||
!info->wd.dentry ||
info->wd.mnt->mnt_root != info->wd.dentry))
return -EINVAL;
kern_unmount(info->wd.mnt);
info->wd.mnt = NULL;
info->wd.dentry = NULL;
return 0;
}
EXPORT_SYMBOL_GPL(umd_unload_blob);
static int umd_setup(struct subprocess_info *info, struct cred *new)
{
struct umd_info *umd_info = info->data;
struct file *from_umh[2];
struct file *to_umh[2];
int err;
/* create pipe to send data to umh */
err = create_pipe_files(to_umh, 0);
if (err)
return err;
err = replace_fd(0, to_umh[0], 0);
fput(to_umh[0]);
if (err < 0) {
fput(to_umh[1]);
return err;
}
/* create pipe to receive data from umh */
err = create_pipe_files(from_umh, 0);
if (err) {
fput(to_umh[1]);
replace_fd(0, NULL, 0);
return err;
}
err = replace_fd(1, from_umh[1], 0);
fput(from_umh[1]);
if (err < 0) {
fput(to_umh[1]);
replace_fd(0, NULL, 0);
fput(from_umh[0]);
return err;
}
set_fs_pwd(current->fs, &umd_info->wd);
umd_info->pipe_to_umh = to_umh[1];
umd_info->pipe_from_umh = from_umh[0];
umd_info->tgid = get_pid(task_tgid(current));
return 0;
}
static void umd_cleanup(struct subprocess_info *info)
{
struct umd_info *umd_info = info->data;
/* cleanup if umh_setup() was successful but exec failed */
if (info->retval)
umd_cleanup_helper(umd_info);
}
/**
* umd_cleanup_helper - release the resources which were allocated in umd_setup
* @info: information about usermode driver
*/
void umd_cleanup_helper(struct umd_info *info)
{
fput(info->pipe_to_umh);
fput(info->pipe_from_umh);
put_pid(info->tgid);
info->tgid = NULL;
}
EXPORT_SYMBOL_GPL(umd_cleanup_helper);
/**
* fork_usermode_driver - fork a usermode driver
* @info: information about usermode driver (shouldn't be NULL)
*
* Returns either negative error or zero which indicates success in
* executing a usermode driver. In such case 'struct umd_info *info'
* is populated with two pipes and a tgid of the process. The caller is
* responsible for health check of the user process, killing it via
* tgid, and closing the pipes when user process is no longer needed.
*/
int fork_usermode_driver(struct umd_info *info)
{
struct subprocess_info *sub_info;
const char *argv[] = { info->driver_name, NULL };
int err;
if (WARN_ON_ONCE(info->tgid))
return -EBUSY;
err = -ENOMEM;
sub_info = call_usermodehelper_setup(info->driver_name,
(char **)argv, NULL, GFP_KERNEL,
umd_setup, umd_cleanup, info);
if (!sub_info)
goto out;
err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
out:
return err;
}
EXPORT_SYMBOL_GPL(fork_usermode_driver);

View file

@ -171,7 +171,8 @@ int bpf_struct_ops_test_run(struct bpf_prog *prog, const union bpf_attr *kattr,
} }
/* prog doesn't take the ownership of the reference from caller */ /* prog doesn't take the ownership of the reference from caller */
bpf_prog_inc(prog); bpf_prog_inc(prog);
bpf_link_init(&link->link, BPF_LINK_TYPE_STRUCT_OPS, &bpf_struct_ops_link_lops, prog); bpf_link_init(&link->link, BPF_LINK_TYPE_STRUCT_OPS, &bpf_struct_ops_link_lops, prog,
prog->expected_attach_type);
op_idx = prog->expected_attach_type; op_idx = prog->expected_attach_type;
err = bpf_struct_ops_prepare_trampoline(tlinks, link, err = bpf_struct_ops_prepare_trampoline(tlinks, link,

View file

@ -1255,7 +1255,7 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,
headroom -= ctx->data; headroom -= ctx->data;
} }
max_data_sz = 4096 - headroom - tailroom; max_data_sz = PAGE_SIZE - headroom - tailroom;
if (size > max_data_sz) { if (size > max_data_sz) {
/* disallow live data mode for jumbo frames */ /* disallow live data mode for jumbo frames */
if (do_live) if (do_live)

View file

@ -10453,7 +10453,8 @@ int bpf_xdp_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
goto unlock; goto unlock;
} }
bpf_link_init(&link->link, BPF_LINK_TYPE_XDP, &bpf_xdp_link_lops, prog); bpf_link_init(&link->link, BPF_LINK_TYPE_XDP, &bpf_xdp_link_lops, prog,
attr->link_create.attach_type);
link->dev = dev; link->dev = dev;
link->flags = attr->link_create.flags; link->flags = attr->link_create.flags;

View file

@ -1709,7 +1709,6 @@ EXPORT_SYMBOL_GPL(sock_map_close);
struct sockmap_link { struct sockmap_link {
struct bpf_link link; struct bpf_link link;
struct bpf_map *map; struct bpf_map *map;
enum bpf_attach_type attach_type;
}; };
static void sock_map_link_release(struct bpf_link *link) static void sock_map_link_release(struct bpf_link *link)
@ -1721,7 +1720,7 @@ static void sock_map_link_release(struct bpf_link *link)
goto out; goto out;
WARN_ON_ONCE(sock_map_prog_update(sockmap_link->map, NULL, link->prog, link, WARN_ON_ONCE(sock_map_prog_update(sockmap_link->map, NULL, link->prog, link,
sockmap_link->attach_type)); link->attach_type));
bpf_map_put_with_uref(sockmap_link->map); bpf_map_put_with_uref(sockmap_link->map);
sockmap_link->map = NULL; sockmap_link->map = NULL;
@ -1772,7 +1771,7 @@ static int sock_map_link_update_prog(struct bpf_link *link,
} }
ret = sock_map_prog_link_lookup(sockmap_link->map, &pprog, &plink, ret = sock_map_prog_link_lookup(sockmap_link->map, &pprog, &plink,
sockmap_link->attach_type); link->attach_type);
if (ret) if (ret)
goto out; goto out;
@ -1817,7 +1816,7 @@ static int sock_map_link_fill_info(const struct bpf_link *link,
u32 map_id = sock_map_link_get_map_id(sockmap_link); u32 map_id = sock_map_link_get_map_id(sockmap_link);
info->sockmap.map_id = map_id; info->sockmap.map_id = map_id;
info->sockmap.attach_type = sockmap_link->attach_type; info->sockmap.attach_type = link->attach_type;
return 0; return 0;
} }
@ -1828,7 +1827,7 @@ static void sock_map_link_show_fdinfo(const struct bpf_link *link,
u32 map_id = sock_map_link_get_map_id(sockmap_link); u32 map_id = sock_map_link_get_map_id(sockmap_link);
seq_printf(seq, "map_id:\t%u\n", map_id); seq_printf(seq, "map_id:\t%u\n", map_id);
seq_printf(seq, "attach_type:\t%u\n", sockmap_link->attach_type); seq_printf(seq, "attach_type:\t%u\n", link->attach_type);
} }
static const struct bpf_link_ops sock_map_link_ops = { static const struct bpf_link_ops sock_map_link_ops = {
@ -1866,9 +1865,9 @@ int sock_map_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
} }
attach_type = attr->link_create.attach_type; attach_type = attr->link_create.attach_type;
bpf_link_init(&sockmap_link->link, BPF_LINK_TYPE_SOCKMAP, &sock_map_link_ops, prog); bpf_link_init(&sockmap_link->link, BPF_LINK_TYPE_SOCKMAP, &sock_map_link_ops, prog,
attach_type);
sockmap_link->map = map; sockmap_link->map = map;
sockmap_link->attach_type = attach_type;
ret = bpf_link_prime(&sockmap_link->link, &link_primer); ret = bpf_link_prime(&sockmap_link->link, &link_primer);
if (ret) { if (ret) {

View file

@ -6821,8 +6821,7 @@ void __init ip6_route_init_special_entries(void)
#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
DEFINE_BPF_ITER_FUNC(ipv6_route, struct bpf_iter_meta *meta, struct fib6_info *rt) DEFINE_BPF_ITER_FUNC(ipv6_route, struct bpf_iter_meta *meta, struct fib6_info *rt)
BTF_ID_LIST(btf_fib6_info_id) BTF_ID_LIST_SINGLE(btf_fib6_info_id, struct, fib6_info)
BTF_ID(struct, fib6_info)
static const struct bpf_iter_seq_info ipv6_route_seq_info = { static const struct bpf_iter_seq_info ipv6_route_seq_info = {
.seq_ops = &ipv6_route_seq_ops, .seq_ops = &ipv6_route_seq_ops,

View file

@ -225,7 +225,8 @@ int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
if (!link) if (!link)
return -ENOMEM; return -ENOMEM;
bpf_link_init(&link->link, BPF_LINK_TYPE_NETFILTER, &bpf_nf_link_lops, prog); bpf_link_init(&link->link, BPF_LINK_TYPE_NETFILTER, &bpf_nf_link_lops, prog,
attr->link_create.attach_type);
link->hook_ops.hook = nf_hook_run_bpf; link->hook_ops.hook = nf_hook_run_bpf;
link->hook_ops.hook_ops_type = NF_HOOK_OP_BPF; link->hook_ops.hook_ops_type = NF_HOOK_OP_BPF;

View file

@ -2887,8 +2887,7 @@ static const struct rhashtable_params netlink_rhashtable_params = {
}; };
#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS) #if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_PROC_FS)
BTF_ID_LIST(btf_netlink_sock_id) BTF_ID_LIST_SINGLE(btf_netlink_sock_id, struct, netlink_sock)
BTF_ID(struct, netlink_sock)
static const struct bpf_iter_seq_info netlink_seq_info = { static const struct bpf_iter_seq_info netlink_seq_info = {
.seq_ops = &netlink_seq_ops, .seq_ops = &netlink_seq_ops,

View file

@ -130,8 +130,7 @@ static int bpf_qdisc_btf_struct_access(struct bpf_verifier_log *log,
return 0; return 0;
} }
BTF_ID_LIST(bpf_qdisc_init_prologue_ids) BTF_ID_LIST_SINGLE(bpf_qdisc_init_prologue_ids, func, bpf_qdisc_init_prologue)
BTF_ID(func, bpf_qdisc_init_prologue)
static int bpf_qdisc_gen_prologue(struct bpf_insn *insn_buf, bool direct_write, static int bpf_qdisc_gen_prologue(struct bpf_insn *insn_buf, bool direct_write,
const struct bpf_prog *prog) const struct bpf_prog *prog)
@ -161,8 +160,7 @@ static int bpf_qdisc_gen_prologue(struct bpf_insn *insn_buf, bool direct_write,
return insn - insn_buf; return insn - insn_buf;
} }
BTF_ID_LIST(bpf_qdisc_reset_destroy_epilogue_ids) BTF_ID_LIST_SINGLE(bpf_qdisc_reset_destroy_epilogue_ids, func, bpf_qdisc_reset_destroy_epilogue)
BTF_ID(func, bpf_qdisc_reset_destroy_epilogue)
static int bpf_qdisc_gen_epilogue(struct bpf_insn *insn_buf, const struct bpf_prog *prog, static int bpf_qdisc_gen_epilogue(struct bpf_insn *insn_buf, const struct bpf_prog *prog,
s16 ctx_stack_off) s16 ctx_stack_off)
@ -451,8 +449,7 @@ static struct bpf_struct_ops bpf_Qdisc_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
BTF_ID_LIST(bpf_sk_buff_dtor_ids) BTF_ID_LIST_SINGLE(bpf_sk_buff_dtor_ids, func, bpf_kfree_skb)
BTF_ID(func, bpf_kfree_skb)
static int __init bpf_qdisc_kfunc_init(void) static int __init bpf_qdisc_kfunc_init(void)
{ {

View file

@ -45,6 +45,8 @@ static void get_exec_path(char *tpath, size_t size)
assert(path); assert(path);
len = readlink(path, tpath, size); len = readlink(path, tpath, size);
if (len < 0)
len = 0;
tpath[len] = 0; tpath[len] = 0;
free(path); free(path);

View file

@ -35,6 +35,7 @@ PROG COMMANDS
| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
| **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*] | **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
| **bpftool** **prog tracelog** | **bpftool** **prog tracelog**
| **bpftool** **prog tracelog** [ { **stdout** | **stderr** } *PROG* ]
| **bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*] | **bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
| **bpftool** **prog profile** *PROG* [**duration** *DURATION*] *METRICs* | **bpftool** **prog profile** *PROG* [**duration** *DURATION*] *METRICs*
| **bpftool** **prog help** | **bpftool** **prog help**
@ -179,6 +180,12 @@ bpftool prog tracelog
purposes. For streaming data from BPF programs to user space, one can use purposes. For streaming data from BPF programs to user space, one can use
perf events (see also **bpftool-map**\ (8)). perf events (see also **bpftool-map**\ (8)).
bpftool prog tracelog { stdout | stderr } *PROG*
Dump the BPF stream of the program. BPF programs can write to these streams
at runtime with the **bpf_stream_vprintk**\ () kfunc. The kernel may write
error messages to the standard error stream. This facility should be used
only for debugging purposes.
bpftool prog run *PROG* data_in *FILE* [data_out *FILE* [data_size_out *L*]] [ctx_in *FILE* [ctx_out *FILE* [ctx_size_out *M*]]] [repeat *N*] bpftool prog run *PROG* data_in *FILE* [data_out *FILE* [data_size_out *L*]] [ctx_in *FILE* [ctx_out *FILE* [ctx_size_out *M*]]] [repeat *N*]
Run BPF program *PROG* in the kernel testing infrastructure for BPF, Run BPF program *PROG* in the kernel testing infrastructure for BPF,
meaning that the program works on the data and context provided by the meaning that the program works on the data and context provided by the

View file

@ -518,7 +518,21 @@ _bpftool()
esac esac
;; ;;
tracelog) tracelog)
return 0 case $prev in
$command)
COMPREPLY+=( $( compgen -W "stdout stderr" -- \
"$cur" ) )
return 0
;;
stdout|stderr)
COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \
"$cur" ) )
return 0
;;
*)
return 0
;;
esac
;; ;;
profile) profile)
case $cword in case $cword in

View file

@ -905,7 +905,8 @@ static int do_dump(int argc, char **argv)
return -1; return -1;
} }
fd = map_parse_fd_and_info(&argc, &argv, &info, &len); fd = map_parse_fd_and_info(&argc, &argv, &info, &len,
BPF_F_RDONLY);
if (fd < 0) if (fd < 0)
return -1; return -1;
@ -1118,10 +1119,13 @@ build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type,
[BPF_OBJ_PROG] = "prog", [BPF_OBJ_PROG] = "prog",
[BPF_OBJ_MAP] = "map", [BPF_OBJ_MAP] = "map",
}; };
LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts_ro);
__u32 btf_id, id = 0; __u32 btf_id, id = 0;
int err; int err;
int fd; int fd;
opts_ro.open_flags = BPF_F_RDONLY;
while (true) { while (true) {
switch (type) { switch (type) {
case BPF_OBJ_PROG: case BPF_OBJ_PROG:
@ -1151,7 +1155,7 @@ build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type,
fd = bpf_prog_get_fd_by_id(id); fd = bpf_prog_get_fd_by_id(id);
break; break;
case BPF_OBJ_MAP: case BPF_OBJ_MAP:
fd = bpf_map_get_fd_by_id(id); fd = bpf_map_get_fd_by_id_opts(id, &opts_ro);
break; break;
default: default:
err = -1; err = -1;

View file

@ -4,6 +4,7 @@
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
#include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@ -193,7 +194,8 @@ int mount_tracefs(const char *target)
return err; return err;
} }
int open_obj_pinned(const char *path, bool quiet) int open_obj_pinned(const char *path, bool quiet,
const struct bpf_obj_get_opts *opts)
{ {
char *pname; char *pname;
int fd = -1; int fd = -1;
@ -205,7 +207,7 @@ int open_obj_pinned(const char *path, bool quiet)
goto out_ret; goto out_ret;
} }
fd = bpf_obj_get(pname); fd = bpf_obj_get_opts(pname, opts);
if (fd < 0) { if (fd < 0) {
if (!quiet) if (!quiet)
p_err("bpf obj get (%s): %s", pname, p_err("bpf obj get (%s): %s", pname,
@ -221,12 +223,13 @@ out_ret:
return fd; return fd;
} }
int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type) int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type,
const struct bpf_obj_get_opts *opts)
{ {
enum bpf_obj_type type; enum bpf_obj_type type;
int fd; int fd;
fd = open_obj_pinned(path, false); fd = open_obj_pinned(path, false, opts);
if (fd < 0) if (fd < 0)
return -1; return -1;
@ -555,7 +558,7 @@ static int do_build_table_cb(const char *fpath, const struct stat *sb,
if (typeflag != FTW_F) if (typeflag != FTW_F)
goto out_ret; goto out_ret;
fd = open_obj_pinned(fpath, true); fd = open_obj_pinned(fpath, true, NULL);
if (fd < 0) if (fd < 0)
goto out_ret; goto out_ret;
@ -928,7 +931,7 @@ int prog_parse_fds(int *argc, char ***argv, int **fds)
path = **argv; path = **argv;
NEXT_ARGP(); NEXT_ARGP();
(*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG); (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_PROG, NULL);
if ((*fds)[0] < 0) if ((*fds)[0] < 0)
return -1; return -1;
return 1; return 1;
@ -965,7 +968,8 @@ exit_free:
return fd; return fd;
} }
static int map_fd_by_name(char *name, int **fds) static int map_fd_by_name(char *name, int **fds,
const struct bpf_get_fd_by_id_opts *opts)
{ {
unsigned int id = 0; unsigned int id = 0;
int fd, nb_fds = 0; int fd, nb_fds = 0;
@ -973,6 +977,7 @@ static int map_fd_by_name(char *name, int **fds)
int err; int err;
while (true) { while (true) {
LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts_ro);
struct bpf_map_info info = {}; struct bpf_map_info info = {};
__u32 len = sizeof(info); __u32 len = sizeof(info);
@ -985,7 +990,9 @@ static int map_fd_by_name(char *name, int **fds)
return nb_fds; return nb_fds;
} }
fd = bpf_map_get_fd_by_id(id); /* Request a read-only fd to query the map info */
opts_ro.open_flags = BPF_F_RDONLY;
fd = bpf_map_get_fd_by_id_opts(id, &opts_ro);
if (fd < 0) { if (fd < 0) {
p_err("can't get map by id (%u): %s", p_err("can't get map by id (%u): %s",
id, strerror(errno)); id, strerror(errno));
@ -1004,6 +1011,19 @@ static int map_fd_by_name(char *name, int **fds)
continue; continue;
} }
/* Get an fd with the requested options, if they differ
* from the read-only options used to get the fd above.
*/
if (memcmp(opts, &opts_ro, sizeof(opts_ro))) {
close(fd);
fd = bpf_map_get_fd_by_id_opts(id, opts);
if (fd < 0) {
p_err("can't get map by id (%u): %s", id,
strerror(errno));
goto err_close_fds;
}
}
if (nb_fds > 0) { if (nb_fds > 0) {
tmp = realloc(*fds, (nb_fds + 1) * sizeof(int)); tmp = realloc(*fds, (nb_fds + 1) * sizeof(int));
if (!tmp) { if (!tmp) {
@ -1023,8 +1043,13 @@ err_close_fds:
return -1; return -1;
} }
int map_parse_fds(int *argc, char ***argv, int **fds) int map_parse_fds(int *argc, char ***argv, int **fds, __u32 open_flags)
{ {
LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts);
assert((open_flags & ~BPF_F_RDONLY) == 0);
opts.open_flags = open_flags;
if (is_prefix(**argv, "id")) { if (is_prefix(**argv, "id")) {
unsigned int id; unsigned int id;
char *endptr; char *endptr;
@ -1038,7 +1063,7 @@ int map_parse_fds(int *argc, char ***argv, int **fds)
} }
NEXT_ARGP(); NEXT_ARGP();
(*fds)[0] = bpf_map_get_fd_by_id(id); (*fds)[0] = bpf_map_get_fd_by_id_opts(id, &opts);
if ((*fds)[0] < 0) { if ((*fds)[0] < 0) {
p_err("get map by id (%u): %s", id, strerror(errno)); p_err("get map by id (%u): %s", id, strerror(errno));
return -1; return -1;
@ -1056,16 +1081,18 @@ int map_parse_fds(int *argc, char ***argv, int **fds)
} }
NEXT_ARGP(); NEXT_ARGP();
return map_fd_by_name(name, fds); return map_fd_by_name(name, fds, &opts);
} else if (is_prefix(**argv, "pinned")) { } else if (is_prefix(**argv, "pinned")) {
char *path; char *path;
LIBBPF_OPTS(bpf_obj_get_opts, get_opts);
get_opts.file_flags = open_flags;
NEXT_ARGP(); NEXT_ARGP();
path = **argv; path = **argv;
NEXT_ARGP(); NEXT_ARGP();
(*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP); (*fds)[0] = open_obj_pinned_any(path, BPF_OBJ_MAP, &get_opts);
if ((*fds)[0] < 0) if ((*fds)[0] < 0)
return -1; return -1;
return 1; return 1;
@ -1075,7 +1102,7 @@ int map_parse_fds(int *argc, char ***argv, int **fds)
return -1; return -1;
} }
int map_parse_fd(int *argc, char ***argv) int map_parse_fd(int *argc, char ***argv, __u32 open_flags)
{ {
int *fds = NULL; int *fds = NULL;
int nb_fds, fd; int nb_fds, fd;
@ -1085,7 +1112,7 @@ int map_parse_fd(int *argc, char ***argv)
p_err("mem alloc failed"); p_err("mem alloc failed");
return -1; return -1;
} }
nb_fds = map_parse_fds(argc, argv, &fds); nb_fds = map_parse_fds(argc, argv, &fds, open_flags);
if (nb_fds != 1) { if (nb_fds != 1) {
if (nb_fds > 1) { if (nb_fds > 1) {
p_err("several maps match this handle"); p_err("several maps match this handle");
@ -1103,12 +1130,12 @@ exit_free:
} }
int map_parse_fd_and_info(int *argc, char ***argv, struct bpf_map_info *info, int map_parse_fd_and_info(int *argc, char ***argv, struct bpf_map_info *info,
__u32 *info_len) __u32 *info_len, __u32 open_flags)
{ {
int err; int err;
int fd; int fd;
fd = map_parse_fd(argc, argv); fd = map_parse_fd(argc, argv, open_flags);
if (fd < 0) if (fd < 0)
return -1; return -1;

View file

@ -37,7 +37,7 @@ static int do_pin(int argc, char **argv)
return -1; return -1;
} }
map_fd = map_parse_fd(&argc, &argv); map_fd = map_parse_fd(&argc, &argv, BPF_F_RDONLY);
if (map_fd < 0) if (map_fd < 0)
return -1; return -1;

View file

@ -117,7 +117,7 @@ static int link_parse_fd(int *argc, char ***argv)
path = **argv; path = **argv;
NEXT_ARGP(); NEXT_ARGP();
return open_obj_pinned_any(path, BPF_OBJ_LINK); return open_obj_pinned_any(path, BPF_OBJ_LINK, NULL);
} }
p_err("expected 'id' or 'pinned', got: '%s'?", **argv); p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
@ -485,6 +485,7 @@ static int show_link_close_json(int fd, struct bpf_link_info *info)
case BPF_LINK_TYPE_RAW_TRACEPOINT: case BPF_LINK_TYPE_RAW_TRACEPOINT:
jsonw_string_field(json_wtr, "tp_name", jsonw_string_field(json_wtr, "tp_name",
u64_to_ptr(info->raw_tracepoint.tp_name)); u64_to_ptr(info->raw_tracepoint.tp_name));
jsonw_uint_field(json_wtr, "cookie", info->raw_tracepoint.cookie);
break; break;
case BPF_LINK_TYPE_TRACING: case BPF_LINK_TYPE_TRACING:
err = get_prog_info(info->prog_id, &prog_info); err = get_prog_info(info->prog_id, &prog_info);
@ -502,6 +503,7 @@ static int show_link_close_json(int fd, struct bpf_link_info *info)
json_wtr); json_wtr);
jsonw_uint_field(json_wtr, "target_obj_id", info->tracing.target_obj_id); jsonw_uint_field(json_wtr, "target_obj_id", info->tracing.target_obj_id);
jsonw_uint_field(json_wtr, "target_btf_id", info->tracing.target_btf_id); jsonw_uint_field(json_wtr, "target_btf_id", info->tracing.target_btf_id);
jsonw_uint_field(json_wtr, "cookie", info->tracing.cookie);
break; break;
case BPF_LINK_TYPE_CGROUP: case BPF_LINK_TYPE_CGROUP:
jsonw_lluint_field(json_wtr, "cgroup_id", jsonw_lluint_field(json_wtr, "cgroup_id",
@ -879,6 +881,8 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info)
case BPF_LINK_TYPE_RAW_TRACEPOINT: case BPF_LINK_TYPE_RAW_TRACEPOINT:
printf("\n\ttp '%s' ", printf("\n\ttp '%s' ",
(const char *)u64_to_ptr(info->raw_tracepoint.tp_name)); (const char *)u64_to_ptr(info->raw_tracepoint.tp_name));
if (info->raw_tracepoint.cookie)
printf("cookie %llu ", info->raw_tracepoint.cookie);
break; break;
case BPF_LINK_TYPE_TRACING: case BPF_LINK_TYPE_TRACING:
err = get_prog_info(info->prog_id, &prog_info); err = get_prog_info(info->prog_id, &prog_info);
@ -897,6 +901,8 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info)
printf("\n\ttarget_obj_id %u target_btf_id %u ", printf("\n\ttarget_obj_id %u target_btf_id %u ",
info->tracing.target_obj_id, info->tracing.target_obj_id,
info->tracing.target_btf_id); info->tracing.target_btf_id);
if (info->tracing.cookie)
printf("\n\tcookie %llu ", info->tracing.cookie);
break; break;
case BPF_LINK_TYPE_CGROUP: case BPF_LINK_TYPE_CGROUP:
printf("\n\tcgroup_id %zu ", (size_t)info->cgroup.cgroup_id); printf("\n\tcgroup_id %zu ", (size_t)info->cgroup.cgroup_id);

View file

@ -534,9 +534,9 @@ int main(int argc, char **argv)
usage(); usage();
if (version_requested) if (version_requested)
return do_version(argc, argv); ret = do_version(argc, argv);
else
ret = cmd_select(commands, argc, argv, do_help); ret = cmd_select(commands, argc, argv, do_help);
if (json_output) if (json_output)
jsonw_destroy(&json_wtr); jsonw_destroy(&json_wtr);

View file

@ -15,6 +15,7 @@
#include <bpf/hashmap.h> #include <bpf/hashmap.h>
#include <bpf/libbpf.h> #include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "json_writer.h" #include "json_writer.h"
@ -140,8 +141,10 @@ void get_prog_full_name(const struct bpf_prog_info *prog_info, int prog_fd,
int get_fd_type(int fd); int get_fd_type(int fd);
const char *get_fd_type_name(enum bpf_obj_type type); const char *get_fd_type_name(enum bpf_obj_type type);
char *get_fdinfo(int fd, const char *key); char *get_fdinfo(int fd, const char *key);
int open_obj_pinned(const char *path, bool quiet); int open_obj_pinned(const char *path, bool quiet,
int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type); const struct bpf_obj_get_opts *opts);
int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type,
const struct bpf_obj_get_opts *opts);
int mount_bpffs_for_file(const char *file_name); int mount_bpffs_for_file(const char *file_name);
int create_and_mount_bpffs_dir(const char *dir_name); int create_and_mount_bpffs_dir(const char *dir_name);
int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(int *, char ***)); int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(int *, char ***));
@ -167,10 +170,10 @@ int do_iter(int argc, char **argv) __weak;
int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what); int parse_u32_arg(int *argc, char ***argv, __u32 *val, const char *what);
int prog_parse_fd(int *argc, char ***argv); int prog_parse_fd(int *argc, char ***argv);
int prog_parse_fds(int *argc, char ***argv, int **fds); int prog_parse_fds(int *argc, char ***argv, int **fds);
int map_parse_fd(int *argc, char ***argv); int map_parse_fd(int *argc, char ***argv, __u32 open_flags);
int map_parse_fds(int *argc, char ***argv, int **fds); int map_parse_fds(int *argc, char ***argv, int **fds, __u32 open_flags);
int map_parse_fd_and_info(int *argc, char ***argv, struct bpf_map_info *info, int map_parse_fd_and_info(int *argc, char ***argv, struct bpf_map_info *info,
__u32 *info_len); __u32 *info_len, __u32 open_flags);
struct bpf_prog_linfo; struct bpf_prog_linfo;
#if defined(HAVE_LLVM_SUPPORT) || defined(HAVE_LIBBFD_SUPPORT) #if defined(HAVE_LLVM_SUPPORT) || defined(HAVE_LIBBFD_SUPPORT)

View file

@ -337,9 +337,9 @@ static void fill_per_cpu_value(struct bpf_map_info *info, void *value)
memcpy(value + i * step, value, info->value_size); memcpy(value + i * step, value, info->value_size);
} }
static int parse_elem(char **argv, struct bpf_map_info *info, static int parse_elem(char **argv, struct bpf_map_info *info, void *key,
void *key, void *value, __u32 key_size, __u32 value_size, void *value, __u32 key_size, __u32 value_size,
__u32 *flags, __u32 **value_fd) __u32 *flags, __u32 **value_fd, __u32 open_flags)
{ {
if (!*argv) { if (!*argv) {
if (!key && !value) if (!key && !value)
@ -362,7 +362,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
return -1; return -1;
return parse_elem(argv, info, NULL, value, key_size, value_size, return parse_elem(argv, info, NULL, value, key_size, value_size,
flags, value_fd); flags, value_fd, open_flags);
} else if (is_prefix(*argv, "value")) { } else if (is_prefix(*argv, "value")) {
int fd; int fd;
@ -388,7 +388,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
return -1; return -1;
} }
fd = map_parse_fd(&argc, &argv); fd = map_parse_fd(&argc, &argv, open_flags);
if (fd < 0) if (fd < 0)
return -1; return -1;
@ -424,7 +424,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
} }
return parse_elem(argv, info, key, NULL, key_size, value_size, return parse_elem(argv, info, key, NULL, key_size, value_size,
flags, NULL); flags, NULL, open_flags);
} else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") || } else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") ||
is_prefix(*argv, "exist")) { is_prefix(*argv, "exist")) {
if (!flags) { if (!flags) {
@ -440,7 +440,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
*flags = BPF_EXIST; *flags = BPF_EXIST;
return parse_elem(argv + 1, info, key, value, key_size, return parse_elem(argv + 1, info, key, value, key_size,
value_size, NULL, value_fd); value_size, NULL, value_fd, open_flags);
} }
p_err("expected key or value, got: %s", *argv); p_err("expected key or value, got: %s", *argv);
@ -639,7 +639,7 @@ static int do_show_subset(int argc, char **argv)
p_err("mem alloc failed"); p_err("mem alloc failed");
return -1; return -1;
} }
nb_fds = map_parse_fds(&argc, &argv, &fds); nb_fds = map_parse_fds(&argc, &argv, &fds, BPF_F_RDONLY);
if (nb_fds < 1) if (nb_fds < 1)
goto exit_free; goto exit_free;
@ -672,12 +672,15 @@ exit_free:
static int do_show(int argc, char **argv) static int do_show(int argc, char **argv)
{ {
LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts);
struct bpf_map_info info = {}; struct bpf_map_info info = {};
__u32 len = sizeof(info); __u32 len = sizeof(info);
__u32 id = 0; __u32 id = 0;
int err; int err;
int fd; int fd;
opts.open_flags = BPF_F_RDONLY;
if (show_pinned) { if (show_pinned) {
map_table = hashmap__new(hash_fn_for_key_as_id, map_table = hashmap__new(hash_fn_for_key_as_id,
equal_fn_for_key_as_id, NULL); equal_fn_for_key_as_id, NULL);
@ -707,7 +710,7 @@ static int do_show(int argc, char **argv)
break; break;
} }
fd = bpf_map_get_fd_by_id(id); fd = bpf_map_get_fd_by_id_opts(id, &opts);
if (fd < 0) { if (fd < 0) {
if (errno == ENOENT) if (errno == ENOENT)
continue; continue;
@ -909,7 +912,7 @@ static int do_dump(int argc, char **argv)
p_err("mem alloc failed"); p_err("mem alloc failed");
return -1; return -1;
} }
nb_fds = map_parse_fds(&argc, &argv, &fds); nb_fds = map_parse_fds(&argc, &argv, &fds, BPF_F_RDONLY);
if (nb_fds < 1) if (nb_fds < 1)
goto exit_free; goto exit_free;
@ -997,7 +1000,7 @@ static int do_update(int argc, char **argv)
if (argc < 2) if (argc < 2)
usage(); usage();
fd = map_parse_fd_and_info(&argc, &argv, &info, &len); fd = map_parse_fd_and_info(&argc, &argv, &info, &len, 0);
if (fd < 0) if (fd < 0)
return -1; return -1;
@ -1006,7 +1009,7 @@ static int do_update(int argc, char **argv)
goto exit_free; goto exit_free;
err = parse_elem(argv, &info, key, value, info.key_size, err = parse_elem(argv, &info, key, value, info.key_size,
info.value_size, &flags, &value_fd); info.value_size, &flags, &value_fd, 0);
if (err) if (err)
goto exit_free; goto exit_free;
@ -1076,7 +1079,7 @@ static int do_lookup(int argc, char **argv)
if (argc < 2) if (argc < 2)
usage(); usage();
fd = map_parse_fd_and_info(&argc, &argv, &info, &len); fd = map_parse_fd_and_info(&argc, &argv, &info, &len, BPF_F_RDONLY);
if (fd < 0) if (fd < 0)
return -1; return -1;
@ -1084,7 +1087,8 @@ static int do_lookup(int argc, char **argv)
if (err) if (err)
goto exit_free; goto exit_free;
err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL,
BPF_F_RDONLY);
if (err) if (err)
goto exit_free; goto exit_free;
@ -1127,7 +1131,7 @@ static int do_getnext(int argc, char **argv)
if (argc < 2) if (argc < 2)
usage(); usage();
fd = map_parse_fd_and_info(&argc, &argv, &info, &len); fd = map_parse_fd_and_info(&argc, &argv, &info, &len, BPF_F_RDONLY);
if (fd < 0) if (fd < 0)
return -1; return -1;
@ -1140,8 +1144,8 @@ static int do_getnext(int argc, char **argv)
} }
if (argc) { if (argc) {
err = parse_elem(argv, &info, key, NULL, info.key_size, 0, err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL,
NULL, NULL); NULL, BPF_F_RDONLY);
if (err) if (err)
goto exit_free; goto exit_free;
} else { } else {
@ -1198,7 +1202,7 @@ static int do_delete(int argc, char **argv)
if (argc < 2) if (argc < 2)
usage(); usage();
fd = map_parse_fd_and_info(&argc, &argv, &info, &len); fd = map_parse_fd_and_info(&argc, &argv, &info, &len, 0);
if (fd < 0) if (fd < 0)
return -1; return -1;
@ -1209,7 +1213,8 @@ static int do_delete(int argc, char **argv)
goto exit_free; goto exit_free;
} }
err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL,
0);
if (err) if (err)
goto exit_free; goto exit_free;
@ -1226,11 +1231,16 @@ exit_free:
return err; return err;
} }
static int map_parse_read_only_fd(int *argc, char ***argv)
{
return map_parse_fd(argc, argv, BPF_F_RDONLY);
}
static int do_pin(int argc, char **argv) static int do_pin(int argc, char **argv)
{ {
int err; int err;
err = do_pin_any(argc, argv, map_parse_fd); err = do_pin_any(argc, argv, map_parse_read_only_fd);
if (!err && json_output) if (!err && json_output)
jsonw_null(json_wtr); jsonw_null(json_wtr);
return err; return err;
@ -1319,7 +1329,7 @@ offload_dev:
if (!REQ_ARGS(2)) if (!REQ_ARGS(2))
usage(); usage();
inner_map_fd = map_parse_fd_and_info(&argc, &argv, inner_map_fd = map_parse_fd_and_info(&argc, &argv,
&info, &len); &info, &len, BPF_F_RDONLY);
if (inner_map_fd < 0) if (inner_map_fd < 0)
return -1; return -1;
attr.inner_map_fd = inner_map_fd; attr.inner_map_fd = inner_map_fd;
@ -1368,7 +1378,7 @@ static int do_pop_dequeue(int argc, char **argv)
if (argc < 2) if (argc < 2)
usage(); usage();
fd = map_parse_fd_and_info(&argc, &argv, &info, &len); fd = map_parse_fd_and_info(&argc, &argv, &info, &len, 0);
if (fd < 0) if (fd < 0)
return -1; return -1;
@ -1407,7 +1417,7 @@ static int do_freeze(int argc, char **argv)
if (!REQ_ARGS(2)) if (!REQ_ARGS(2))
return -1; return -1;
fd = map_parse_fd(&argc, &argv); fd = map_parse_fd(&argc, &argv, 0);
if (fd < 0) if (fd < 0)
return -1; return -1;

View file

@ -128,7 +128,8 @@ int do_event_pipe(int argc, char **argv)
int err, map_fd; int err, map_fd;
map_info_len = sizeof(map_info); map_info_len = sizeof(map_info);
map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len); map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len,
0);
if (map_fd < 0) if (map_fd < 0)
return -1; return -1;

View file

@ -366,17 +366,18 @@ static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb)
{ {
struct bpf_netdev_t *netinfo = cookie; struct bpf_netdev_t *netinfo = cookie;
struct ifinfomsg *ifinfo = msg; struct ifinfomsg *ifinfo = msg;
struct ip_devname_ifindex *tmp;
if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index) if (netinfo->filter_idx > 0 && netinfo->filter_idx != ifinfo->ifi_index)
return 0; return 0;
if (netinfo->used_len == netinfo->array_len) { if (netinfo->used_len == netinfo->array_len) {
netinfo->devices = realloc(netinfo->devices, tmp = realloc(netinfo->devices,
(netinfo->array_len + 16) * (netinfo->array_len + 16) * sizeof(struct ip_devname_ifindex));
sizeof(struct ip_devname_ifindex)); if (!tmp)
if (!netinfo->devices)
return -ENOMEM; return -ENOMEM;
netinfo->devices = tmp;
netinfo->array_len += 16; netinfo->array_len += 16;
} }
netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index; netinfo->devices[netinfo->used_len].ifindex = ifinfo->ifi_index;
@ -395,6 +396,7 @@ static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb)
{ {
struct bpf_tcinfo_t *tcinfo = cookie; struct bpf_tcinfo_t *tcinfo = cookie;
struct tcmsg *info = msg; struct tcmsg *info = msg;
struct tc_kind_handle *tmp;
if (tcinfo->is_qdisc) { if (tcinfo->is_qdisc) {
/* skip clsact qdisc */ /* skip clsact qdisc */
@ -406,11 +408,12 @@ static int dump_class_qdisc_nlmsg(void *cookie, void *msg, struct nlattr **tb)
} }
if (tcinfo->used_len == tcinfo->array_len) { if (tcinfo->used_len == tcinfo->array_len) {
tcinfo->handle_array = realloc(tcinfo->handle_array, tmp = realloc(tcinfo->handle_array,
(tcinfo->array_len + 16) * sizeof(struct tc_kind_handle)); (tcinfo->array_len + 16) * sizeof(struct tc_kind_handle));
if (!tcinfo->handle_array) if (!tmp)
return -ENOMEM; return -ENOMEM;
tcinfo->handle_array = tmp;
tcinfo->array_len += 16; tcinfo->array_len += 16;
} }
tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle; tcinfo->handle_array[tcinfo->used_len].handle = info->tcm_handle;

View file

@ -1062,7 +1062,7 @@ static int parse_attach_detach_args(int argc, char **argv, int *progfd,
if (!REQ_ARGS(2)) if (!REQ_ARGS(2))
return -EINVAL; return -EINVAL;
*mapfd = map_parse_fd(&argc, &argv); *mapfd = map_parse_fd(&argc, &argv, 0);
if (*mapfd < 0) if (*mapfd < 0)
return *mapfd; return *mapfd;
@ -1113,6 +1113,52 @@ static int do_detach(int argc, char **argv)
return 0; return 0;
} }
enum prog_tracelog_mode {
TRACE_STDOUT,
TRACE_STDERR,
};
static int
prog_tracelog_stream(int prog_fd, enum prog_tracelog_mode mode)
{
FILE *file = mode == TRACE_STDOUT ? stdout : stderr;
int stream_id = mode == TRACE_STDOUT ? 1 : 2;
char buf[512];
int ret;
ret = 0;
do {
ret = bpf_prog_stream_read(prog_fd, stream_id, buf, sizeof(buf), NULL);
if (ret > 0)
fwrite(buf, sizeof(buf[0]), ret, file);
} while (ret > 0);
fflush(file);
return ret ? -1 : 0;
}
static int do_tracelog_any(int argc, char **argv)
{
enum prog_tracelog_mode mode;
int fd;
if (argc == 0)
return do_tracelog(argc, argv);
if (!is_prefix(*argv, "stdout") && !is_prefix(*argv, "stderr"))
usage();
mode = is_prefix(*argv, "stdout") ? TRACE_STDOUT : TRACE_STDERR;
NEXT_ARG();
if (!REQ_ARGS(2))
return -1;
fd = prog_parse_fd(&argc, &argv);
if (fd < 0)
return -1;
return prog_tracelog_stream(fd, mode);
}
static int check_single_stdin(char *file_data_in, char *file_ctx_in) static int check_single_stdin(char *file_data_in, char *file_ctx_in)
{ {
if (file_data_in && file_ctx_in && if (file_data_in && file_ctx_in &&
@ -1608,7 +1654,7 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
} }
NEXT_ARG(); NEXT_ARG();
fd = map_parse_fd(&argc, &argv); fd = map_parse_fd(&argc, &argv, 0);
if (fd < 0) if (fd < 0)
goto err_free_reuse_maps; goto err_free_reuse_maps;
@ -2493,6 +2539,7 @@ static int do_help(int argc, char **argv)
" [repeat N]\n" " [repeat N]\n"
" %1$s %2$s profile PROG [duration DURATION] METRICs\n" " %1$s %2$s profile PROG [duration DURATION] METRICs\n"
" %1$s %2$s tracelog\n" " %1$s %2$s tracelog\n"
" %1$s %2$s tracelog { stdout | stderr } PROG\n"
" %1$s %2$s help\n" " %1$s %2$s help\n"
"\n" "\n"
" " HELP_SPEC_MAP "\n" " " HELP_SPEC_MAP "\n"
@ -2532,7 +2579,7 @@ static const struct cmd cmds[] = {
{ "loadall", do_loadall }, { "loadall", do_loadall },
{ "attach", do_attach }, { "attach", do_attach },
{ "detach", do_detach }, { "detach", do_detach },
{ "tracelog", do_tracelog }, { "tracelog", do_tracelog_any },
{ "run", do_run }, { "run", do_run },
{ "profile", do_profile }, { "profile", do_profile },
{ 0 } { 0 }

View file

@ -450,6 +450,7 @@ union bpf_iter_link_info {
* * **struct bpf_map_info** * * **struct bpf_map_info**
* * **struct bpf_btf_info** * * **struct bpf_btf_info**
* * **struct bpf_link_info** * * **struct bpf_link_info**
* * **struct bpf_token_info**
* *
* Return * Return
* Returns zero on success. On error, -1 is returned and *errno* * Returns zero on success. On error, -1 is returned and *errno*
@ -906,6 +907,17 @@ union bpf_iter_link_info {
* A new file descriptor (a nonnegative integer), or -1 if an * A new file descriptor (a nonnegative integer), or -1 if an
* error occurred (in which case, *errno* is set appropriately). * error occurred (in which case, *errno* is set appropriately).
* *
* BPF_PROG_STREAM_READ_BY_FD
* Description
* Read data of a program's BPF stream. The program is identified
* by *prog_fd*, and the stream is identified by the *stream_id*.
* The data is copied to a buffer pointed to by *stream_buf*, and
* filled less than or equal to *stream_buf_len* bytes.
*
* Return
* Number of bytes read from the stream on success, or -1 if an
* error occurred (in which case, *errno* is set appropriately).
*
* NOTES * NOTES
* eBPF objects (maps and programs) can be shared between processes. * eBPF objects (maps and programs) can be shared between processes.
* *
@ -961,6 +973,7 @@ enum bpf_cmd {
BPF_LINK_DETACH, BPF_LINK_DETACH,
BPF_PROG_BIND_MAP, BPF_PROG_BIND_MAP,
BPF_TOKEN_CREATE, BPF_TOKEN_CREATE,
BPF_PROG_STREAM_READ_BY_FD,
__MAX_BPF_CMD, __MAX_BPF_CMD,
}; };
@ -1463,6 +1476,11 @@ struct bpf_stack_build_id {
#define BPF_OBJ_NAME_LEN 16U #define BPF_OBJ_NAME_LEN 16U
enum {
BPF_STREAM_STDOUT = 1,
BPF_STREAM_STDERR = 2,
};
union bpf_attr { union bpf_attr {
struct { /* anonymous struct used by BPF_MAP_CREATE command */ struct { /* anonymous struct used by BPF_MAP_CREATE command */
__u32 map_type; /* one of enum bpf_map_type */ __u32 map_type; /* one of enum bpf_map_type */
@ -1794,6 +1812,13 @@ union bpf_attr {
}; };
__u64 expected_revision; __u64 expected_revision;
} netkit; } netkit;
struct {
union {
__u32 relative_fd;
__u32 relative_id;
};
__u64 expected_revision;
} cgroup;
}; };
} link_create; } link_create;
@ -1842,6 +1867,13 @@ union bpf_attr {
__u32 bpffs_fd; __u32 bpffs_fd;
} token_create; } token_create;
struct {
__aligned_u64 stream_buf;
__u32 stream_buf_len;
__u32 stream_id;
__u32 prog_fd;
} prog_stream_read;
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
/* The description below is an attempt at providing documentation to eBPF /* The description below is an attempt at providing documentation to eBPF
@ -2403,7 +2435,7 @@ union bpf_attr {
* into it. An example is available in file * into it. An example is available in file
* *samples/bpf/trace_output_user.c* in the Linux kernel source * *samples/bpf/trace_output_user.c* in the Linux kernel source
* tree (the eBPF program counterpart is in * tree (the eBPF program counterpart is in
* *samples/bpf/trace_output_kern.c*). * *samples/bpf/trace_output.bpf.c*).
* *
* **bpf_perf_event_output**\ () achieves better performance * **bpf_perf_event_output**\ () achieves better performance
* than **bpf_trace_printk**\ () for sharing data with user * than **bpf_trace_printk**\ () for sharing data with user
@ -6653,11 +6685,15 @@ struct bpf_link_info {
struct { struct {
__aligned_u64 tp_name; /* in/out: tp_name buffer ptr */ __aligned_u64 tp_name; /* in/out: tp_name buffer ptr */
__u32 tp_name_len; /* in/out: tp_name buffer len */ __u32 tp_name_len; /* in/out: tp_name buffer len */
__u32 :32;
__u64 cookie;
} raw_tracepoint; } raw_tracepoint;
struct { struct {
__u32 attach_type; __u32 attach_type;
__u32 target_obj_id; /* prog_id for PROG_EXT, otherwise btf object id */ __u32 target_obj_id; /* prog_id for PROG_EXT, otherwise btf object id */
__u32 target_btf_id; /* BTF type id inside the object */ __u32 target_btf_id; /* BTF type id inside the object */
__u32 :32;
__u64 cookie;
} tracing; } tracing;
struct { struct {
__u64 cgroup_id; __u64 cgroup_id;
@ -6768,6 +6804,13 @@ struct bpf_link_info {
}; };
} __attribute__((aligned(8))); } __attribute__((aligned(8)));
struct bpf_token_info {
__u64 allowed_cmds;
__u64 allowed_maps;
__u64 allowed_progs;
__u64 allowed_attachs;
} __attribute__((aligned(8)));
/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed /* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
* by user and intended to be used by socket (e.g. to bind to, depends on * by user and intended to be used by socket (e.g. to bind to, depends on
* attach type). * attach type).

View file

@ -837,6 +837,50 @@ int bpf_link_create(int prog_fd, int target_fd,
if (!OPTS_ZEROED(opts, netkit)) if (!OPTS_ZEROED(opts, netkit))
return libbpf_err(-EINVAL); return libbpf_err(-EINVAL);
break; break;
case BPF_CGROUP_INET_INGRESS:
case BPF_CGROUP_INET_EGRESS:
case BPF_CGROUP_INET_SOCK_CREATE:
case BPF_CGROUP_INET_SOCK_RELEASE:
case BPF_CGROUP_INET4_BIND:
case BPF_CGROUP_INET6_BIND:
case BPF_CGROUP_INET4_POST_BIND:
case BPF_CGROUP_INET6_POST_BIND:
case BPF_CGROUP_INET4_CONNECT:
case BPF_CGROUP_INET6_CONNECT:
case BPF_CGROUP_UNIX_CONNECT:
case BPF_CGROUP_INET4_GETPEERNAME:
case BPF_CGROUP_INET6_GETPEERNAME:
case BPF_CGROUP_UNIX_GETPEERNAME:
case BPF_CGROUP_INET4_GETSOCKNAME:
case BPF_CGROUP_INET6_GETSOCKNAME:
case BPF_CGROUP_UNIX_GETSOCKNAME:
case BPF_CGROUP_UDP4_SENDMSG:
case BPF_CGROUP_UDP6_SENDMSG:
case BPF_CGROUP_UNIX_SENDMSG:
case BPF_CGROUP_UDP4_RECVMSG:
case BPF_CGROUP_UDP6_RECVMSG:
case BPF_CGROUP_UNIX_RECVMSG:
case BPF_CGROUP_SOCK_OPS:
case BPF_CGROUP_DEVICE:
case BPF_CGROUP_SYSCTL:
case BPF_CGROUP_GETSOCKOPT:
case BPF_CGROUP_SETSOCKOPT:
case BPF_LSM_CGROUP:
relative_fd = OPTS_GET(opts, cgroup.relative_fd, 0);
relative_id = OPTS_GET(opts, cgroup.relative_id, 0);
if (relative_fd && relative_id)
return libbpf_err(-EINVAL);
if (relative_id) {
attr.link_create.cgroup.relative_id = relative_id;
attr.link_create.flags |= BPF_F_ID;
} else {
attr.link_create.cgroup.relative_fd = relative_fd;
}
attr.link_create.cgroup.expected_revision =
OPTS_GET(opts, cgroup.expected_revision, 0);
if (!OPTS_ZEROED(opts, cgroup))
return libbpf_err(-EINVAL);
break;
default: default:
if (!OPTS_ZEROED(opts, flags)) if (!OPTS_ZEROED(opts, flags))
return libbpf_err(-EINVAL); return libbpf_err(-EINVAL);
@ -1331,3 +1375,23 @@ int bpf_token_create(int bpffs_fd, struct bpf_token_create_opts *opts)
fd = sys_bpf_fd(BPF_TOKEN_CREATE, &attr, attr_sz); fd = sys_bpf_fd(BPF_TOKEN_CREATE, &attr, attr_sz);
return libbpf_err_errno(fd); return libbpf_err_errno(fd);
} }
int bpf_prog_stream_read(int prog_fd, __u32 stream_id, void *buf, __u32 buf_len,
struct bpf_prog_stream_read_opts *opts)
{
const size_t attr_sz = offsetofend(union bpf_attr, prog_stream_read);
union bpf_attr attr;
int err;
if (!OPTS_VALID(opts, bpf_prog_stream_read_opts))
return libbpf_err(-EINVAL);
memset(&attr, 0, attr_sz);
attr.prog_stream_read.stream_buf = ptr_to_u64(buf);
attr.prog_stream_read.stream_buf_len = buf_len;
attr.prog_stream_read.stream_id = stream_id;
attr.prog_stream_read.prog_fd = prog_fd;
err = sys_bpf(BPF_PROG_STREAM_READ_BY_FD, &attr, attr_sz);
return libbpf_err_errno(err);
}

View file

@ -438,6 +438,11 @@ struct bpf_link_create_opts {
__u32 relative_id; __u32 relative_id;
__u64 expected_revision; __u64 expected_revision;
} netkit; } netkit;
struct {
__u32 relative_fd;
__u32 relative_id;
__u64 expected_revision;
} cgroup;
}; };
size_t :0; size_t :0;
}; };
@ -704,6 +709,27 @@ struct bpf_token_create_opts {
LIBBPF_API int bpf_token_create(int bpffs_fd, LIBBPF_API int bpf_token_create(int bpffs_fd,
struct bpf_token_create_opts *opts); struct bpf_token_create_opts *opts);
struct bpf_prog_stream_read_opts {
size_t sz;
size_t :0;
};
#define bpf_prog_stream_read_opts__last_field sz
/**
* @brief **bpf_prog_stream_read** reads data from the BPF stream of a given BPF
* program.
*
* @param prog_fd FD for the BPF program whose BPF stream is to be read.
* @param stream_id ID of the BPF stream to be read.
* @param buf Buffer to read data into from the BPF stream.
* @param buf_len Maximum number of bytes to read from the BPF stream.
* @param opts optional options, can be NULL
*
* @return The number of bytes read, on success; negative error code, otherwise
* (errno is also set to the error code)
*/
LIBBPF_API int bpf_prog_stream_read(int prog_fd, __u32 stream_id, void *buf, __u32 buf_len,
struct bpf_prog_stream_read_opts *opts);
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */
#endif #endif

View file

@ -215,6 +215,7 @@ enum libbpf_tristate {
#define __arg_nonnull __attribute((btf_decl_tag("arg:nonnull"))) #define __arg_nonnull __attribute((btf_decl_tag("arg:nonnull")))
#define __arg_nullable __attribute((btf_decl_tag("arg:nullable"))) #define __arg_nullable __attribute((btf_decl_tag("arg:nullable")))
#define __arg_trusted __attribute((btf_decl_tag("arg:trusted"))) #define __arg_trusted __attribute((btf_decl_tag("arg:trusted")))
#define __arg_untrusted __attribute((btf_decl_tag("arg:untrusted")))
#define __arg_arena __attribute((btf_decl_tag("arg:arena"))) #define __arg_arena __attribute((btf_decl_tag("arg:arena")))
#ifndef ___bpf_concat #ifndef ___bpf_concat
@ -314,6 +315,22 @@ enum libbpf_tristate {
___param, sizeof(___param)); \ ___param, sizeof(___param)); \
}) })
extern int bpf_stream_vprintk(int stream_id, const char *fmt__str, const void *args,
__u32 len__sz, void *aux__prog) __weak __ksym;
#define bpf_stream_printk(stream_id, fmt, args...) \
({ \
static const char ___fmt[] = fmt; \
unsigned long long ___param[___bpf_narg(args)]; \
\
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
___bpf_fill(___param, args); \
_Pragma("GCC diagnostic pop") \
\
bpf_stream_vprintk(stream_id, ___fmt, ___param, sizeof(___param), NULL);\
})
/* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args /* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args
* Otherwise use __bpf_vprintk * Otherwise use __bpf_vprintk
*/ */

View file

@ -326,9 +326,10 @@ struct btf_dump_type_data_opts {
bool compact; /* no newlines/indentation */ bool compact; /* no newlines/indentation */
bool skip_names; /* skip member/type names */ bool skip_names; /* skip member/type names */
bool emit_zeroes; /* show 0-valued fields */ bool emit_zeroes; /* show 0-valued fields */
bool emit_strings; /* print char arrays as strings */
size_t :0; size_t :0;
}; };
#define btf_dump_type_data_opts__last_field emit_zeroes #define btf_dump_type_data_opts__last_field emit_strings
LIBBPF_API int LIBBPF_API int
btf_dump__dump_type_data(struct btf_dump *d, __u32 id, btf_dump__dump_type_data(struct btf_dump *d, __u32 id,

View file

@ -68,6 +68,7 @@ struct btf_dump_data {
bool compact; bool compact;
bool skip_names; bool skip_names;
bool emit_zeroes; bool emit_zeroes;
bool emit_strings;
__u8 indent_lvl; /* base indent level */ __u8 indent_lvl; /* base indent level */
char indent_str[BTF_DATA_INDENT_STR_LEN]; char indent_str[BTF_DATA_INDENT_STR_LEN];
/* below are used during iteration */ /* below are used during iteration */
@ -2031,6 +2032,52 @@ static int btf_dump_var_data(struct btf_dump *d,
return btf_dump_dump_type_data(d, NULL, t, type_id, data, 0, 0); return btf_dump_dump_type_data(d, NULL, t, type_id, data, 0, 0);
} }
static int btf_dump_string_data(struct btf_dump *d,
const struct btf_type *t,
__u32 id,
const void *data)
{
const struct btf_array *array = btf_array(t);
const char *chars = data;
__u32 i;
/* Make sure it is a NUL-terminated string. */
for (i = 0; i < array->nelems; i++) {
if ((void *)(chars + i) >= d->typed_dump->data_end)
return -E2BIG;
if (chars[i] == '\0')
break;
}
if (i == array->nelems) {
/* The caller will print this as a regular array. */
return -EINVAL;
}
btf_dump_data_pfx(d);
btf_dump_printf(d, "\"");
for (i = 0; i < array->nelems; i++) {
char c = chars[i];
if (c == '\0') {
/*
* When printing character arrays as strings, NUL bytes
* are always treated as string terminators; they are
* never printed.
*/
break;
}
if (isprint(c))
btf_dump_printf(d, "%c", c);
else
btf_dump_printf(d, "\\x%02x", (__u8)c);
}
btf_dump_printf(d, "\"");
return 0;
}
static int btf_dump_array_data(struct btf_dump *d, static int btf_dump_array_data(struct btf_dump *d,
const struct btf_type *t, const struct btf_type *t,
__u32 id, __u32 id,
@ -2058,8 +2105,13 @@ static int btf_dump_array_data(struct btf_dump *d,
* char arrays, so if size is 1 and element is * char arrays, so if size is 1 and element is
* printable as a char, we'll do that. * printable as a char, we'll do that.
*/ */
if (elem_size == 1) if (elem_size == 1) {
if (d->typed_dump->emit_strings &&
btf_dump_string_data(d, t, id, data) == 0) {
return 0;
}
d->typed_dump->is_array_char = true; d->typed_dump->is_array_char = true;
}
} }
/* note that we increment depth before calling btf_dump_print() below; /* note that we increment depth before calling btf_dump_print() below;
@ -2547,6 +2599,7 @@ int btf_dump__dump_type_data(struct btf_dump *d, __u32 id,
d->typed_dump->compact = OPTS_GET(opts, compact, false); d->typed_dump->compact = OPTS_GET(opts, compact, false);
d->typed_dump->skip_names = OPTS_GET(opts, skip_names, false); d->typed_dump->skip_names = OPTS_GET(opts, skip_names, false);
d->typed_dump->emit_zeroes = OPTS_GET(opts, emit_zeroes, false); d->typed_dump->emit_zeroes = OPTS_GET(opts, emit_zeroes, false);
d->typed_dump->emit_strings = OPTS_GET(opts, emit_strings, false);
ret = btf_dump_dump_type_data(d, NULL, t, id, data, 0, 0); ret = btf_dump_dump_type_data(d, NULL, t, id, data, 0, 0);

View file

@ -4582,6 +4582,11 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
/* arena data relocation */ /* arena data relocation */
if (shdr_idx == obj->efile.arena_data_shndx) { if (shdr_idx == obj->efile.arena_data_shndx) {
if (obj->arena_map_idx < 0) {
pr_warn("prog '%s': bad arena data relocation at insn %u, no arena maps defined\n",
prog->name, insn_idx);
return -LIBBPF_ERRNO__RELOC;
}
reloc_desc->type = RELO_DATA; reloc_desc->type = RELO_DATA;
reloc_desc->insn_idx = insn_idx; reloc_desc->insn_idx = insn_idx;
reloc_desc->map_idx = obj->arena_map_idx; reloc_desc->map_idx = obj->arena_map_idx;
@ -9216,7 +9221,7 @@ int bpf_object__gen_loader(struct bpf_object *obj, struct gen_loader_opts *opts)
return libbpf_err(-EFAULT); return libbpf_err(-EFAULT);
if (!OPTS_VALID(opts, gen_loader_opts)) if (!OPTS_VALID(opts, gen_loader_opts))
return libbpf_err(-EINVAL); return libbpf_err(-EINVAL);
gen = calloc(sizeof(*gen), 1); gen = calloc(1, sizeof(*gen));
if (!gen) if (!gen)
return libbpf_err(-ENOMEM); return libbpf_err(-ENOMEM);
gen->opts = opts; gen->opts = opts;
@ -12847,6 +12852,34 @@ struct bpf_link *bpf_program__attach_xdp(const struct bpf_program *prog, int ifi
return bpf_program_attach_fd(prog, ifindex, "xdp", NULL); return bpf_program_attach_fd(prog, ifindex, "xdp", NULL);
} }
struct bpf_link *
bpf_program__attach_cgroup_opts(const struct bpf_program *prog, int cgroup_fd,
const struct bpf_cgroup_opts *opts)
{
LIBBPF_OPTS(bpf_link_create_opts, link_create_opts);
__u32 relative_id;
int relative_fd;
if (!OPTS_VALID(opts, bpf_cgroup_opts))
return libbpf_err_ptr(-EINVAL);
relative_id = OPTS_GET(opts, relative_id, 0);
relative_fd = OPTS_GET(opts, relative_fd, 0);
if (relative_fd && relative_id) {
pr_warn("prog '%s': relative_fd and relative_id cannot be set at the same time\n",
prog->name);
return libbpf_err_ptr(-EINVAL);
}
link_create_opts.cgroup.expected_revision = OPTS_GET(opts, expected_revision, 0);
link_create_opts.cgroup.relative_fd = relative_fd;
link_create_opts.cgroup.relative_id = relative_id;
link_create_opts.flags = OPTS_GET(opts, flags, 0);
return bpf_program_attach_fd(prog, cgroup_fd, "cgroup", &link_create_opts);
}
struct bpf_link * struct bpf_link *
bpf_program__attach_tcx(const struct bpf_program *prog, int ifindex, bpf_program__attach_tcx(const struct bpf_program *prog, int ifindex,
const struct bpf_tcx_opts *opts) const struct bpf_tcx_opts *opts)

View file

@ -877,6 +877,21 @@ LIBBPF_API struct bpf_link *
bpf_program__attach_netkit(const struct bpf_program *prog, int ifindex, bpf_program__attach_netkit(const struct bpf_program *prog, int ifindex,
const struct bpf_netkit_opts *opts); const struct bpf_netkit_opts *opts);
struct bpf_cgroup_opts {
/* size of this struct, for forward/backward compatibility */
size_t sz;
__u32 flags;
__u32 relative_fd;
__u32 relative_id;
__u64 expected_revision;
size_t :0;
};
#define bpf_cgroup_opts__last_field expected_revision
LIBBPF_API struct bpf_link *
bpf_program__attach_cgroup_opts(const struct bpf_program *prog, int cgroup_fd,
const struct bpf_cgroup_opts *opts);
struct bpf_map; struct bpf_map;
LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(const struct bpf_map *map); LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(const struct bpf_map *map);

View file

@ -437,6 +437,8 @@ LIBBPF_1.6.0 {
bpf_linker__add_fd; bpf_linker__add_fd;
bpf_linker__new_fd; bpf_linker__new_fd;
bpf_object__prepare; bpf_object__prepare;
bpf_prog_stream_read;
bpf_program__attach_cgroup_opts;
bpf_program__func_info; bpf_program__func_info;
bpf_program__func_info_cnt; bpf_program__func_info_cnt;
bpf_program__line_info; bpf_program__line_info;
@ -444,3 +446,6 @@ LIBBPF_1.6.0 {
btf__add_decl_attr; btf__add_decl_attr;
btf__add_type_attr; btf__add_type_attr;
} LIBBPF_1.5.0; } LIBBPF_1.5.0;
LIBBPF_1.7.0 {
} LIBBPF_1.6.0;

View file

@ -4,6 +4,6 @@
#define __LIBBPF_VERSION_H #define __LIBBPF_VERSION_H
#define LIBBPF_MAJOR_VERSION 1 #define LIBBPF_MAJOR_VERSION 1
#define LIBBPF_MINOR_VERSION 6 #define LIBBPF_MINOR_VERSION 7
#endif /* __LIBBPF_VERSION_H */ #endif /* __LIBBPF_VERSION_H */

View file

@ -59,7 +59,7 @@
* *
* STAP_PROBE3(my_usdt_provider, my_usdt_probe_name, 123, x, &y); * STAP_PROBE3(my_usdt_provider, my_usdt_probe_name, 123, x, &y);
* *
* USDT is identified by it's <provider-name>:<probe-name> pair of names. Each * USDT is identified by its <provider-name>:<probe-name> pair of names. Each
* individual USDT has a fixed number of arguments (3 in the above example) * individual USDT has a fixed number of arguments (3 in the above example)
* and specifies values of each argument as if it was a function call. * and specifies values of each argument as if it was a function call.
* *
@ -81,7 +81,7 @@
* NOP instruction that kernel can replace with an interrupt instruction to * NOP instruction that kernel can replace with an interrupt instruction to
* trigger instrumentation code (BPF program for all that we care about). * trigger instrumentation code (BPF program for all that we care about).
* *
* Semaphore above is and optional feature. It records an address of a 2-byte * Semaphore above is an optional feature. It records an address of a 2-byte
* refcount variable (normally in '.probes' ELF section) used for signaling if * refcount variable (normally in '.probes' ELF section) used for signaling if
* there is anything that is attached to USDT. This is useful for user * there is anything that is attached to USDT. This is useful for user
* applications if, for example, they need to prepare some arguments that are * applications if, for example, they need to prepare some arguments that are
@ -121,7 +121,7 @@
* a uprobe BPF program (which for kernel, at least currently, is just a kprobe * a uprobe BPF program (which for kernel, at least currently, is just a kprobe
* program, so BPF_PROG_TYPE_KPROBE program type). With the only difference * program, so BPF_PROG_TYPE_KPROBE program type). With the only difference
* that uprobe is usually attached at the function entry, while USDT will * that uprobe is usually attached at the function entry, while USDT will
* normally will be somewhere inside the function. But it should always be * normally be somewhere inside the function. But it should always be
* pointing to NOP instruction, which makes such uprobes the fastest uprobe * pointing to NOP instruction, which makes such uprobes the fastest uprobe
* kind. * kind.
* *
@ -151,7 +151,7 @@
* libbpf sets to spec ID during attach time, or, if kernel is too old to * libbpf sets to spec ID during attach time, or, if kernel is too old to
* support BPF cookie, through IP-to-spec-ID map that libbpf maintains in such * support BPF cookie, through IP-to-spec-ID map that libbpf maintains in such
* case. The latter means that some modes of operation can't be supported * case. The latter means that some modes of operation can't be supported
* without BPF cookie. Such mode is attaching to shared library "generically", * without BPF cookie. Such a mode is attaching to shared library "generically",
* without specifying target process. In such case, it's impossible to * without specifying target process. In such case, it's impossible to
* calculate absolute IP addresses for IP-to-spec-ID map, and thus such mode * calculate absolute IP addresses for IP-to-spec-ID map, and thus such mode
* is not supported without BPF cookie support. * is not supported without BPF cookie support.
@ -185,7 +185,7 @@
* as even if USDT spec string is the same, USDT cookie value can be * as even if USDT spec string is the same, USDT cookie value can be
* different. It was deemed excessive to try to deduplicate across independent * different. It was deemed excessive to try to deduplicate across independent
* USDT attachments by taking into account USDT spec string *and* USDT cookie * USDT attachments by taking into account USDT spec string *and* USDT cookie
* value, which would complicated spec ID accounting significantly for little * value, which would complicate spec ID accounting significantly for little
* gain. * gain.
*/ */

View file

@ -1,6 +1,5 @@
# TEMPORARY # TEMPORARY
# Alphabetical order # Alphabetical order
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
get_stack_raw_tp # spams with kernel warnings until next bpf -> bpf-next merge get_stack_raw_tp # spams with kernel warnings until next bpf -> bpf-next merge
stacktrace_build_id stacktrace_build_id
stacktrace_build_id_nmi stacktrace_build_id_nmi

View file

@ -1 +0,0 @@
tracing_struct/struct_many_args # struct_many_args:FAIL:tracing_struct_many_args__attach unexpected error: -524

View file

@ -109,6 +109,7 @@ TEST_PROGS := test_kmod.sh \
test_xdping.sh \ test_xdping.sh \
test_bpftool_build.sh \ test_bpftool_build.sh \
test_bpftool.sh \ test_bpftool.sh \
test_bpftool_map.sh \
test_bpftool_metadata.sh \ test_bpftool_metadata.sh \
test_doc_build.sh \ test_doc_build.sh \
test_xsk.sh \ test_xsk.sh \
@ -840,6 +841,11 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o \
$(call msg,BINARY,,$@) $(call msg,BINARY,,$@)
$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@ $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@
# This works around GCC warning about snprintf truncating strings like:
#
# char a[PATH_MAX], b[PATH_MAX];
# snprintf(a, "%s/foo", b); // triggers -Wformat-truncation
$(OUTPUT)/veristat.o: CFLAGS += -Wno-format-truncation
$(OUTPUT)/veristat.o: $(BPFOBJ) $(OUTPUT)/veristat.o: $(BPFOBJ)
$(OUTPUT)/veristat: $(OUTPUT)/veristat.o $(OUTPUT)/veristat: $(OUTPUT)/veristat.o
$(call msg,BINARY,,$@) $(call msg,BINARY,,$@)

View file

@ -46,8 +46,11 @@
void __arena* bpf_arena_alloc_pages(void *map, void __arena *addr, __u32 page_cnt, void __arena* bpf_arena_alloc_pages(void *map, void __arena *addr, __u32 page_cnt,
int node_id, __u64 flags) __ksym __weak; int node_id, __u64 flags) __ksym __weak;
int bpf_arena_reserve_pages(void *map, void __arena *addr, __u32 page_cnt) __ksym __weak;
void bpf_arena_free_pages(void *map, void __arena *ptr, __u32 page_cnt) __ksym __weak; void bpf_arena_free_pages(void *map, void __arena *ptr, __u32 page_cnt) __ksym __weak;
#define arena_base(map) ((void __arena *)((struct bpf_arena *)(map))->user_vm_start)
#else /* when compiled as user space code */ #else /* when compiled as user space code */
#define __arena #define __arena

View file

@ -61,7 +61,7 @@ extern bool CONFIG_X86_64 __kconfig __weak;
#define smp_mb() \ #define smp_mb() \
({ \ ({ \
unsigned long __val; \ volatile unsigned long __val; \
__sync_fetch_and_add(&__val, 0); \ __sync_fetch_and_add(&__val, 0); \
}) })

View file

@ -69,7 +69,7 @@ extern int bpf_get_file_xattr(struct file *file, const char *name,
struct bpf_dynptr *value_ptr) __ksym; struct bpf_dynptr *value_ptr) __ksym;
extern int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *digest_ptr) __ksym; extern int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *digest_ptr) __ksym;
extern struct bpf_key *bpf_lookup_user_key(__u32 serial, __u64 flags) __ksym; extern struct bpf_key *bpf_lookup_user_key(__s32 serial, __u64 flags) __ksym;
extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym; extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym;
extern void bpf_key_put(struct bpf_key *key) __ksym; extern void bpf_key_put(struct bpf_key *key) __ksym;
extern int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr, extern int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr,

View file

@ -4,6 +4,7 @@
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/xattr.h>
#include <linux/limits.h> #include <linux/limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -318,6 +319,26 @@ int join_parent_cgroup(const char *relative_path)
return join_cgroup_from_top(cgroup_path); return join_cgroup_from_top(cgroup_path);
} }
/**
* set_cgroup_xattr() - Set xattr on a cgroup dir
* @relative_path: The cgroup path, relative to the workdir, to set xattr
* @name: xattr name
* @value: xattr value
*
* This function set xattr on cgroup dir.
*
* On success, it returns 0, otherwise on failure it returns -1.
*/
int set_cgroup_xattr(const char *relative_path,
const char *name,
const char *value)
{
char cgroup_path[PATH_MAX + 1];
format_cgroup_path(cgroup_path, relative_path);
return setxattr(cgroup_path, name, value, strlen(value) + 1, 0);
}
/** /**
* __cleanup_cgroup_environment() - Delete temporary cgroups * __cleanup_cgroup_environment() - Delete temporary cgroups
* *

View file

@ -26,6 +26,10 @@ int join_cgroup(const char *relative_path);
int join_root_cgroup(void); int join_root_cgroup(void);
int join_parent_cgroup(const char *relative_path); int join_parent_cgroup(const char *relative_path);
int set_cgroup_xattr(const char *relative_path,
const char *name,
const char *value);
int setup_cgroup_environment(void); int setup_cgroup_environment(void);
void cleanup_cgroup_environment(void); void cleanup_cgroup_environment(void);

View file

@ -108,6 +108,7 @@ CONFIG_IP_NF_IPTABLES=y
CONFIG_IP6_NF_IPTABLES=y CONFIG_IP6_NF_IPTABLES=y
CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_FILTER=y
CONFIG_NF_NAT=y CONFIG_NF_NAT=y
CONFIG_PACKET=y
CONFIG_RC_CORE=y CONFIG_RC_CORE=y
CONFIG_SECURITY=y CONFIG_SECURITY=y
CONFIG_SECURITYFS=y CONFIG_SECURITYFS=y

View file

@ -0,0 +1,93 @@
CONFIG_ALTIVEC=y
CONFIG_AUDIT=y
CONFIG_BLK_CGROUP=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BONDING=y
CONFIG_BPF_JIT_ALWAYS_ON=y
CONFIG_BPF_PRELOAD_UMD=y
CONFIG_BPF_PRELOAD=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_HUGETLB=y
CONFIG_CGROUP_NET_CLASSID=y
CONFIG_CGROUP_PERF=y
CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_SCHED=y
CONFIG_CGROUPS=y
CONFIG_CMDLINE_BOOL=y
CONFIG_CMDLINE="console=hvc0 wg.success=hvc1 panic_on_warn=1"
CONFIG_CPU_LITTLE_ENDIAN=y
CONFIG_CPUSETS=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_DEBUG_FS=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_DEVTMPFS=y
CONFIG_EXPERT=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_FS=y
CONFIG_FRAME_POINTER=y
CONFIG_FRAME_WARN=1280
CONFIG_HARDLOCKUP_DETECTOR=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_HUGETLBFS=y
CONFIG_HVC_CONSOLE=y
CONFIG_INET=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IPV6_SEG6_LWTUNNEL=y
CONFIG_JUMP_LABEL=y
CONFIG_KALLSYMS_ALL=y
CONFIG_KPROBES=y
CONFIG_MEMCG=y
CONFIG_NAMESPACES=y
CONFIG_NET_ACT_BPF=y
CONFIG_NETDEVICES=y
CONFIG_NETFILTER_XT_MATCH_BPF=y
CONFIG_NET_L3_MASTER_DEV=y
CONFIG_NET_VRF=y
CONFIG_NET=y
CONFIG_NO_HZ_IDLE=y
CONFIG_NONPORTABLE=y
CONFIG_NR_CPUS=256
CONFIG_PACKET=y
CONFIG_PANIC_ON_OOPS=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_PCI_HOST_GENERIC=y
CONFIG_PCI=y
CONFIG_POSIX_MQUEUE=y
CONFIG_PPC64=y
CONFIG_PPC_OF_BOOT_TRAMPOLINE=y
CONFIG_PPC_PSERIES=y
CONFIG_PPC_RADIX_MMU=y
CONFIG_PRINTK_TIME=y
CONFIG_PROC_KCORE=y
CONFIG_PROFILING=y
CONFIG_RCU_CPU_STALL_TIMEOUT=60
CONFIG_RT_GROUP_SCHED=y
CONFIG_SECTION_MISMATCH_WARN_ONLY=y
CONFIG_SECURITY_NETWORK=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_SMP=y
CONFIG_SOC_VIRT=y
CONFIG_SYSVIPC=y
CONFIG_TCP_CONG_ADVANCED=y
CONFIG_THREAD_SHIFT=14
CONFIG_TLS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_TMPFS=y
CONFIG_TUN=y
CONFIG_UNIX=y
CONFIG_UPROBES=y
CONFIG_USER_NS=y
CONFIG_VETH=y
CONFIG_VLAN_8021Q=y
CONFIG_VSOCKETS_LOOPBACK=y
CONFIG_VSX=y
CONFIG_XFRM_USER=y

View file

@ -13,7 +13,7 @@
static void test_fail_cases(void) static void test_fail_cases(void)
{ {
LIBBPF_OPTS(bpf_map_create_opts, opts); LIBBPF_OPTS(bpf_map_create_opts, opts);
__u32 value; __u32 value = 0;
int fd, err; int fd, err;
/* Invalid key size */ /* Invalid key size */

View file

@ -489,10 +489,28 @@ cleanup:
bpf_link__destroy(link); bpf_link__destroy(link);
} }
static int verify_tracing_link_info(int fd, u64 cookie)
{
struct bpf_link_info info;
int err;
u32 len = sizeof(info);
err = bpf_link_get_info_by_fd(fd, &info, &len);
if (!ASSERT_OK(err, "get_link_info"))
return -1;
if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_TRACING, "link_type"))
return -1;
ASSERT_EQ(info.tracing.cookie, cookie, "tracing_cookie");
return 0;
}
static void tracing_subtest(struct test_bpf_cookie *skel) static void tracing_subtest(struct test_bpf_cookie *skel)
{ {
__u64 cookie; __u64 cookie;
int prog_fd; int prog_fd, err;
int fentry_fd = -1, fexit_fd = -1, fmod_ret_fd = -1; int fentry_fd = -1, fexit_fd = -1, fmod_ret_fd = -1;
LIBBPF_OPTS(bpf_test_run_opts, opts); LIBBPF_OPTS(bpf_test_run_opts, opts);
LIBBPF_OPTS(bpf_link_create_opts, link_opts); LIBBPF_OPTS(bpf_link_create_opts, link_opts);
@ -507,6 +525,10 @@ static void tracing_subtest(struct test_bpf_cookie *skel)
if (!ASSERT_GE(fentry_fd, 0, "fentry.link_create")) if (!ASSERT_GE(fentry_fd, 0, "fentry.link_create"))
goto cleanup; goto cleanup;
err = verify_tracing_link_info(fentry_fd, cookie);
if (!ASSERT_OK(err, "verify_tracing_link_info"))
goto cleanup;
cookie = 0x20000000000000L; cookie = 0x20000000000000L;
prog_fd = bpf_program__fd(skel->progs.fexit_test1); prog_fd = bpf_program__fd(skel->progs.fexit_test1);
link_opts.tracing.cookie = cookie; link_opts.tracing.cookie = cookie;
@ -635,10 +657,29 @@ cleanup:
bpf_link__destroy(link); bpf_link__destroy(link);
} }
static int verify_raw_tp_link_info(int fd, u64 cookie)
{
struct bpf_link_info info;
int err;
u32 len = sizeof(info);
memset(&info, 0, sizeof(info));
err = bpf_link_get_info_by_fd(fd, &info, &len);
if (!ASSERT_OK(err, "get_link_info"))
return -1;
if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_RAW_TRACEPOINT, "link_type"))
return -1;
ASSERT_EQ(info.raw_tracepoint.cookie, cookie, "raw_tp_cookie");
return 0;
}
static void raw_tp_subtest(struct test_bpf_cookie *skel) static void raw_tp_subtest(struct test_bpf_cookie *skel)
{ {
__u64 cookie; __u64 cookie;
int prog_fd, link_fd = -1; int err, prog_fd, link_fd = -1;
struct bpf_link *link = NULL; struct bpf_link *link = NULL;
LIBBPF_OPTS(bpf_raw_tp_opts, raw_tp_opts); LIBBPF_OPTS(bpf_raw_tp_opts, raw_tp_opts);
LIBBPF_OPTS(bpf_raw_tracepoint_opts, opts); LIBBPF_OPTS(bpf_raw_tracepoint_opts, opts);
@ -656,6 +697,11 @@ static void raw_tp_subtest(struct test_bpf_cookie *skel)
goto cleanup; goto cleanup;
usleep(1); /* trigger */ usleep(1); /* trigger */
err = verify_raw_tp_link_info(link_fd, cookie);
if (!ASSERT_OK(err, "verify_raw_tp_link_info"))
goto cleanup;
close(link_fd); /* detach */ close(link_fd); /* detach */
link_fd = -1; link_fd = -1;

View file

@ -323,7 +323,7 @@ static void test_task_pidfd(void)
static void test_task_sleepable(void) static void test_task_sleepable(void)
{ {
struct bpf_iter_tasks *skel; struct bpf_iter_tasks *skel;
int pid, status, err, data_pipe[2], finish_pipe[2], c; int pid, status, err, data_pipe[2], finish_pipe[2], c = 0;
char *test_data = NULL; char *test_data = NULL;
char *test_data_long = NULL; char *test_data_long = NULL;
char *data[2]; char *data[2];

View file

@ -78,7 +78,7 @@ static int test_setup_uffd(void *fault_addr)
} }
uffd_register.range.start = (unsigned long)fault_addr; uffd_register.range.start = (unsigned long)fault_addr;
uffd_register.range.len = 4096; uffd_register.range.len = getpagesize();
uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING; uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffd_register)) { if (ioctl(uffd, UFFDIO_REGISTER, &uffd_register)) {
close(uffd); close(uffd);

View file

@ -879,6 +879,122 @@ static void test_btf_dump_var_data(struct btf *btf, struct btf_dump *d,
"static int bpf_cgrp_storage_busy = (int)2", 2); "static int bpf_cgrp_storage_busy = (int)2", 2);
} }
struct btf_dump_string_ctx {
struct btf *btf;
struct btf_dump *d;
char *str;
struct btf_dump_type_data_opts *opts;
int array_id;
};
static int btf_dump_one_string(struct btf_dump_string_ctx *ctx,
char *ptr, size_t ptr_sz,
const char *expected_val)
{
size_t type_sz;
int ret;
ctx->str[0] = '\0';
type_sz = btf__resolve_size(ctx->btf, ctx->array_id);
ret = btf_dump__dump_type_data(ctx->d, ctx->array_id, ptr, ptr_sz, ctx->opts);
if (type_sz <= ptr_sz) {
if (!ASSERT_EQ(ret, type_sz, "failed/unexpected type_sz"))
return -EINVAL;
}
if (!ASSERT_STREQ(ctx->str, expected_val, "ensure expected/actual match"))
return -EFAULT;
return 0;
}
static void btf_dump_strings(struct btf_dump_string_ctx *ctx)
{
struct btf_dump_type_data_opts *opts = ctx->opts;
opts->emit_strings = true;
opts->compact = true;
opts->emit_zeroes = false;
opts->skip_names = false;
btf_dump_one_string(ctx, "foo", 4, "(char[4])\"foo\"");
opts->skip_names = true;
btf_dump_one_string(ctx, "foo", 4, "\"foo\"");
/* This should have no effect. */
opts->emit_zeroes = false;
btf_dump_one_string(ctx, "foo", 4, "\"foo\"");
/* This should have no effect. */
opts->compact = false;
btf_dump_one_string(ctx, "foo", 4, "\"foo\"");
/* Non-printable characters come out as hex. */
btf_dump_one_string(ctx, "fo\xff", 4, "\"fo\\xff\"");
btf_dump_one_string(ctx, "fo\x7", 4, "\"fo\\x07\"");
/*
* Strings that are too long for the specified type ("char[4]")
* should fall back to the current behavior.
*/
opts->compact = true;
btf_dump_one_string(ctx, "abcde", 6, "['a','b','c','d',]");
/*
* Strings that are too short for the specified type ("char[4]")
* should work normally.
*/
btf_dump_one_string(ctx, "ab", 3, "\"ab\"");
/* Non-NUL-terminated arrays don't get printed as strings. */
char food[4] = { 'f', 'o', 'o', 'd' };
char bye[3] = { 'b', 'y', 'e' };
btf_dump_one_string(ctx, food, 4, "['f','o','o','d',]");
btf_dump_one_string(ctx, bye, 3, "['b','y','e',]");
/* The embedded NUL should terminate the string. */
char embed[4] = { 'f', 'o', '\0', 'd' };
btf_dump_one_string(ctx, embed, 4, "\"fo\"");
}
static void test_btf_dump_string_data(void)
{
struct test_ctx t = {};
char str[STRSIZE];
struct btf_dump *d;
DECLARE_LIBBPF_OPTS(btf_dump_type_data_opts, opts);
struct btf_dump_string_ctx ctx;
int char_id, int_id, array_id;
if (test_ctx__init(&t))
return;
d = btf_dump__new(t.btf, btf_dump_snprintf, str, NULL);
if (!ASSERT_OK_PTR(d, "could not create BTF dump"))
return;
/* Generate BTF for a four-element char array. */
char_id = btf__add_int(t.btf, "char", 1, BTF_INT_CHAR);
ASSERT_EQ(char_id, 1, "char_id");
int_id = btf__add_int(t.btf, "int", 4, BTF_INT_SIGNED);
ASSERT_EQ(int_id, 2, "int_id");
array_id = btf__add_array(t.btf, int_id, char_id, 4);
ASSERT_EQ(array_id, 3, "array_id");
ctx.btf = t.btf;
ctx.d = d;
ctx.str = str;
ctx.opts = &opts;
ctx.array_id = array_id;
btf_dump_strings(&ctx);
btf_dump__free(d);
test_ctx__free(&t);
}
static void test_btf_datasec(struct btf *btf, struct btf_dump *d, char *str, static void test_btf_datasec(struct btf *btf, struct btf_dump *d, char *str,
const char *name, const char *expected_val, const char *name, const char *expected_val,
void *data, size_t data_sz) void *data, size_t data_sz)
@ -970,6 +1086,8 @@ void test_btf_dump() {
test_btf_dump_struct_data(btf, d, str); test_btf_dump_struct_data(btf, d, str);
if (test__start_subtest("btf_dump: var_data")) if (test__start_subtest("btf_dump: var_data"))
test_btf_dump_var_data(btf, d, str); test_btf_dump_var_data(btf, d, str);
if (test__start_subtest("btf_dump: string_data"))
test_btf_dump_string_data();
btf_dump__free(d); btf_dump__free(d);
btf__free(btf); btf__free(btf);

View file

@ -0,0 +1,617 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
#include <test_progs.h>
#include "cgroup_helpers.h"
#include "cgroup_mprog.skel.h"
static void assert_mprog_count(int cg, int atype, int expected)
{
__u32 count = 0, attach_flags = 0;
int err;
err = bpf_prog_query(cg, atype, 0, &attach_flags,
NULL, &count);
ASSERT_EQ(count, expected, "count");
ASSERT_EQ(err, 0, "prog_query");
}
static void test_prog_attach_detach(int atype)
{
LIBBPF_OPTS(bpf_prog_attach_opts, opta);
LIBBPF_OPTS(bpf_prog_detach_opts, optd);
LIBBPF_OPTS(bpf_prog_query_opts, optq);
__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
struct cgroup_mprog *skel;
__u32 prog_ids[10];
int cg, err;
cg = test__join_cgroup("/prog_attach_detach");
if (!ASSERT_GE(cg, 0, "join_cgroup /prog_attach_detach"))
return;
skel = cgroup_mprog__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_load"))
goto cleanup;
fd1 = bpf_program__fd(skel->progs.getsockopt_1);
fd2 = bpf_program__fd(skel->progs.getsockopt_2);
fd3 = bpf_program__fd(skel->progs.getsockopt_3);
fd4 = bpf_program__fd(skel->progs.getsockopt_4);
id1 = id_from_prog_fd(fd1);
id2 = id_from_prog_fd(fd2);
id3 = id_from_prog_fd(fd3);
id4 = id_from_prog_fd(fd4);
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER,
.expected_revision = 1,
);
/* ordering: [fd1] */
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
if (!ASSERT_EQ(err, 0, "prog_attach"))
goto cleanup;
assert_mprog_count(cg, atype, 1);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE,
.expected_revision = 2,
);
/* ordering: [fd2, fd1] */
err = bpf_prog_attach_opts(fd2, cg, atype, &opta);
if (!ASSERT_EQ(err, 0, "prog_attach"))
goto cleanup1;
assert_mprog_count(cg, atype, 2);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER,
.relative_fd = fd2,
.expected_revision = 3,
);
/* ordering: [fd2, fd3, fd1] */
err = bpf_prog_attach_opts(fd3, cg, atype, &opta);
if (!ASSERT_EQ(err, 0, "prog_attach"))
goto cleanup2;
assert_mprog_count(cg, atype, 3);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI,
.expected_revision = 4,
);
/* ordering: [fd2, fd3, fd1, fd4] */
err = bpf_prog_attach_opts(fd4, cg, atype, &opta);
if (!ASSERT_EQ(err, 0, "prog_attach"))
goto cleanup3;
assert_mprog_count(cg, atype, 4);
/* retrieve optq.prog_cnt */
err = bpf_prog_query_opts(cg, atype, &optq);
if (!ASSERT_OK(err, "prog_query"))
goto cleanup4;
/* optq.prog_cnt will be used in below query */
memset(prog_ids, 0, sizeof(prog_ids));
optq.prog_ids = prog_ids;
err = bpf_prog_query_opts(cg, atype, &optq);
if (!ASSERT_OK(err, "prog_query"))
goto cleanup4;
ASSERT_EQ(optq.count, 4, "count");
ASSERT_EQ(optq.revision, 5, "revision");
ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
ASSERT_EQ(optq.prog_ids[2], id1, "prog_ids[2]");
ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
ASSERT_EQ(optq.link_ids, NULL, "link_ids");
cleanup4:
optd.expected_revision = 5;
err = bpf_prog_detach_opts(fd4, cg, atype, &optd);
ASSERT_OK(err, "prog_detach");
assert_mprog_count(cg, atype, 3);
cleanup3:
LIBBPF_OPTS_RESET(optd);
err = bpf_prog_detach_opts(fd3, cg, atype, &optd);
ASSERT_OK(err, "prog_detach");
assert_mprog_count(cg, atype, 2);
/* Check revision after two detach operations */
err = bpf_prog_query_opts(cg, atype, &optq);
ASSERT_OK(err, "prog_query");
ASSERT_EQ(optq.revision, 7, "revision");
cleanup2:
err = bpf_prog_detach_opts(fd2, cg, atype, &optd);
ASSERT_OK(err, "prog_detach");
assert_mprog_count(cg, atype, 1);
cleanup1:
err = bpf_prog_detach_opts(fd1, cg, atype, &optd);
ASSERT_OK(err, "prog_detach");
assert_mprog_count(cg, atype, 0);
cleanup:
cgroup_mprog__destroy(skel);
close(cg);
}
static void test_link_attach_detach(int atype)
{
LIBBPF_OPTS(bpf_cgroup_opts, opta);
LIBBPF_OPTS(bpf_cgroup_opts, optd);
LIBBPF_OPTS(bpf_prog_query_opts, optq);
struct bpf_link *link1, *link2, *link3, *link4;
__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4;
struct cgroup_mprog *skel;
__u32 prog_ids[10];
int cg, err;
cg = test__join_cgroup("/link_attach_detach");
if (!ASSERT_GE(cg, 0, "join_cgroup /link_attach_detach"))
return;
skel = cgroup_mprog__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_load"))
goto cleanup;
fd1 = bpf_program__fd(skel->progs.getsockopt_1);
fd2 = bpf_program__fd(skel->progs.getsockopt_2);
fd3 = bpf_program__fd(skel->progs.getsockopt_3);
fd4 = bpf_program__fd(skel->progs.getsockopt_4);
id1 = id_from_prog_fd(fd1);
id2 = id_from_prog_fd(fd2);
id3 = id_from_prog_fd(fd3);
id4 = id_from_prog_fd(fd4);
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.expected_revision = 1,
);
/* ordering: [fd1] */
link1 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_1, cg, &opta);
if (!ASSERT_OK_PTR(link1, "link_attach"))
goto cleanup;
assert_mprog_count(cg, atype, 1);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_BEFORE | BPF_F_LINK,
.relative_id = id_from_link_fd(bpf_link__fd(link1)),
.expected_revision = 2,
);
/* ordering: [fd2, fd1] */
link2 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_2, cg, &opta);
if (!ASSERT_OK_PTR(link2, "link_attach"))
goto cleanup1;
assert_mprog_count(cg, atype, 2);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_AFTER | BPF_F_LINK,
.relative_fd = bpf_link__fd(link2),
.expected_revision = 3,
);
/* ordering: [fd2, fd3, fd1] */
link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta);
if (!ASSERT_OK_PTR(link3, "link_attach"))
goto cleanup2;
assert_mprog_count(cg, atype, 3);
LIBBPF_OPTS_RESET(opta,
.expected_revision = 4,
);
/* ordering: [fd2, fd3, fd1, fd4] */
link4 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_4, cg, &opta);
if (!ASSERT_OK_PTR(link4, "link_attach"))
goto cleanup3;
assert_mprog_count(cg, atype, 4);
/* retrieve optq.prog_cnt */
err = bpf_prog_query_opts(cg, atype, &optq);
if (!ASSERT_OK(err, "prog_query"))
goto cleanup4;
/* optq.prog_cnt will be used in below query */
memset(prog_ids, 0, sizeof(prog_ids));
optq.prog_ids = prog_ids;
err = bpf_prog_query_opts(cg, atype, &optq);
if (!ASSERT_OK(err, "prog_query"))
goto cleanup4;
ASSERT_EQ(optq.count, 4, "count");
ASSERT_EQ(optq.revision, 5, "revision");
ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]");
ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]");
ASSERT_EQ(optq.prog_ids[2], id1, "prog_ids[2]");
ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]");
ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]");
ASSERT_EQ(optq.link_ids, NULL, "link_ids");
cleanup4:
bpf_link__destroy(link4);
assert_mprog_count(cg, atype, 3);
cleanup3:
bpf_link__destroy(link3);
assert_mprog_count(cg, atype, 2);
/* Check revision after two detach operations */
err = bpf_prog_query_opts(cg, atype, &optq);
ASSERT_OK(err, "prog_query");
ASSERT_EQ(optq.revision, 7, "revision");
cleanup2:
bpf_link__destroy(link2);
assert_mprog_count(cg, atype, 1);
cleanup1:
bpf_link__destroy(link1);
assert_mprog_count(cg, atype, 0);
cleanup:
cgroup_mprog__destroy(skel);
close(cg);
}
static void test_preorder_prog_attach_detach(int atype)
{
LIBBPF_OPTS(bpf_prog_attach_opts, opta);
LIBBPF_OPTS(bpf_prog_detach_opts, optd);
__u32 fd1, fd2, fd3, fd4;
struct cgroup_mprog *skel;
int cg, err;
cg = test__join_cgroup("/preorder_prog_attach_detach");
if (!ASSERT_GE(cg, 0, "join_cgroup /preorder_prog_attach_detach"))
return;
skel = cgroup_mprog__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_load"))
goto cleanup;
fd1 = bpf_program__fd(skel->progs.getsockopt_1);
fd2 = bpf_program__fd(skel->progs.getsockopt_2);
fd3 = bpf_program__fd(skel->progs.getsockopt_3);
fd4 = bpf_program__fd(skel->progs.getsockopt_4);
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI,
.expected_revision = 1,
);
/* ordering: [fd1] */
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
if (!ASSERT_EQ(err, 0, "prog_attach"))
goto cleanup;
assert_mprog_count(cg, atype, 1);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_PREORDER,
.expected_revision = 2,
);
/* ordering: [fd1, fd2] */
err = bpf_prog_attach_opts(fd2, cg, atype, &opta);
if (!ASSERT_EQ(err, 0, "prog_attach"))
goto cleanup1;
assert_mprog_count(cg, atype, 2);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER,
.relative_fd = fd2,
.expected_revision = 3,
);
err = bpf_prog_attach_opts(fd3, cg, atype, &opta);
if (!ASSERT_EQ(err, -EINVAL, "prog_attach"))
goto cleanup2;
assert_mprog_count(cg, atype, 2);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER | BPF_F_PREORDER,
.relative_fd = fd2,
.expected_revision = 3,
);
/* ordering: [fd1, fd2, fd3] */
err = bpf_prog_attach_opts(fd3, cg, atype, &opta);
if (!ASSERT_EQ(err, 0, "prog_attach"))
goto cleanup2;
assert_mprog_count(cg, atype, 3);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI,
.expected_revision = 4,
);
/* ordering: [fd2, fd3, fd1, fd4] */
err = bpf_prog_attach_opts(fd4, cg, atype, &opta);
if (!ASSERT_EQ(err, 0, "prog_attach"))
goto cleanup3;
assert_mprog_count(cg, atype, 4);
err = bpf_prog_detach_opts(fd4, cg, atype, &optd);
ASSERT_OK(err, "prog_detach");
assert_mprog_count(cg, atype, 3);
cleanup3:
err = bpf_prog_detach_opts(fd3, cg, atype, &optd);
ASSERT_OK(err, "prog_detach");
assert_mprog_count(cg, atype, 2);
cleanup2:
err = bpf_prog_detach_opts(fd2, cg, atype, &optd);
ASSERT_OK(err, "prog_detach");
assert_mprog_count(cg, atype, 1);
cleanup1:
err = bpf_prog_detach_opts(fd1, cg, atype, &optd);
ASSERT_OK(err, "prog_detach");
assert_mprog_count(cg, atype, 0);
cleanup:
cgroup_mprog__destroy(skel);
close(cg);
}
static void test_preorder_link_attach_detach(int atype)
{
LIBBPF_OPTS(bpf_cgroup_opts, opta);
struct bpf_link *link1, *link2, *link3, *link4;
struct cgroup_mprog *skel;
__u32 fd2;
int cg;
cg = test__join_cgroup("/preorder_link_attach_detach");
if (!ASSERT_GE(cg, 0, "join_cgroup /preorder_link_attach_detach"))
return;
skel = cgroup_mprog__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_load"))
goto cleanup;
fd2 = bpf_program__fd(skel->progs.getsockopt_2);
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.expected_revision = 1,
);
/* ordering: [fd1] */
link1 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_1, cg, &opta);
if (!ASSERT_OK_PTR(link1, "link_attach"))
goto cleanup;
assert_mprog_count(cg, atype, 1);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_PREORDER,
.expected_revision = 2,
);
/* ordering: [fd1, fd2] */
link2 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_2, cg, &opta);
if (!ASSERT_OK_PTR(link2, "link_attach"))
goto cleanup1;
assert_mprog_count(cg, atype, 2);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_AFTER,
.relative_fd = fd2,
.expected_revision = 3,
);
link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta);
if (!ASSERT_ERR_PTR(link3, "link_attach"))
goto cleanup2;
assert_mprog_count(cg, atype, 2);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_AFTER | BPF_F_PREORDER | BPF_F_LINK,
.relative_fd = bpf_link__fd(link2),
.expected_revision = 3,
);
/* ordering: [fd1, fd2, fd3] */
link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta);
if (!ASSERT_OK_PTR(link3, "link_attach"))
goto cleanup2;
assert_mprog_count(cg, atype, 3);
LIBBPF_OPTS_RESET(opta,
.expected_revision = 4,
);
/* ordering: [fd2, fd3, fd1, fd4] */
link4 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_4, cg, &opta);
if (!ASSERT_OK_PTR(link4, "prog_attach"))
goto cleanup3;
assert_mprog_count(cg, atype, 4);
bpf_link__destroy(link4);
assert_mprog_count(cg, atype, 3);
cleanup3:
bpf_link__destroy(link3);
assert_mprog_count(cg, atype, 2);
cleanup2:
bpf_link__destroy(link2);
assert_mprog_count(cg, atype, 1);
cleanup1:
bpf_link__destroy(link1);
assert_mprog_count(cg, atype, 0);
cleanup:
cgroup_mprog__destroy(skel);
close(cg);
}
static void test_invalid_attach_detach(int atype)
{
LIBBPF_OPTS(bpf_prog_attach_opts, opta);
__u32 fd1, fd2, id2;
struct cgroup_mprog *skel;
int cg, err;
cg = test__join_cgroup("/invalid_attach_detach");
if (!ASSERT_GE(cg, 0, "join_cgroup /invalid_attach_detach"))
return;
skel = cgroup_mprog__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_load"))
goto cleanup;
fd1 = bpf_program__fd(skel->progs.getsockopt_1);
fd2 = bpf_program__fd(skel->progs.getsockopt_2);
id2 = id_from_prog_fd(fd2);
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER,
.relative_id = id2,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
ASSERT_EQ(err, -EINVAL, "prog_attach");
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_ID,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
ASSERT_EQ(err, -ENOENT, "prog_attach");
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER | BPF_F_ID,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
ASSERT_EQ(err, -ENOENT, "prog_attach");
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER,
.relative_id = id2,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
ASSERT_EQ(err, -EINVAL, "prog_attach");
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_LINK,
.relative_id = id2,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
ASSERT_EQ(err, -EINVAL, "prog_attach");
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI,
.relative_id = id2,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
ASSERT_EQ(err, -EINVAL, "prog_attach");
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE,
.relative_fd = fd1,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
ASSERT_EQ(err, -ENOENT, "prog_attach");
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER,
.relative_fd = fd1,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
ASSERT_EQ(err, -ENOENT, "prog_attach");
assert_mprog_count(cg, atype, 0);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
if (!ASSERT_EQ(err, 0, "prog_attach"))
goto cleanup;
assert_mprog_count(cg, atype, 1);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
ASSERT_EQ(err, -EINVAL, "prog_attach");
assert_mprog_count(cg, atype, 1);
LIBBPF_OPTS_RESET(opta,
.flags = BPF_F_ALLOW_MULTI | BPF_F_REPLACE | BPF_F_AFTER,
.replace_prog_fd = fd1,
);
err = bpf_prog_attach_opts(fd1, cg, atype, &opta);
ASSERT_EQ(err, -EINVAL, "prog_attach");
assert_mprog_count(cg, atype, 1);
cleanup:
cgroup_mprog__destroy(skel);
close(cg);
}
void test_cgroup_mprog_opts(void)
{
if (test__start_subtest("prog_attach_detach"))
test_prog_attach_detach(BPF_CGROUP_GETSOCKOPT);
if (test__start_subtest("link_attach_detach"))
test_link_attach_detach(BPF_CGROUP_GETSOCKOPT);
if (test__start_subtest("preorder_prog_attach_detach"))
test_preorder_prog_attach_detach(BPF_CGROUP_GETSOCKOPT);
if (test__start_subtest("preorder_link_attach_detach"))
test_preorder_link_attach_detach(BPF_CGROUP_GETSOCKOPT);
if (test__start_subtest("invalid_attach_detach"))
test_invalid_attach_detach(BPF_CGROUP_GETSOCKOPT);
}

View file

@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
#include <test_progs.h>
#include "cgroup_helpers.h"
#include "cgroup_preorder.skel.h"
static int run_getsockopt_test(int cg_parent, int sock_fd, bool has_relative_fd)
{
LIBBPF_OPTS(bpf_prog_attach_opts, opts);
enum bpf_attach_type prog_p_atype, prog_p2_atype;
int prog_p_fd, prog_p2_fd;
struct cgroup_preorder *skel = NULL;
struct bpf_program *prog;
__u8 *result, buf;
socklen_t optlen = 1;
int err = 0;
skel = cgroup_preorder__open_and_load();
if (!ASSERT_OK_PTR(skel, "cgroup_preorder__open_and_load"))
return 0;
LIBBPF_OPTS_RESET(opts);
opts.flags = BPF_F_ALLOW_MULTI;
prog = skel->progs.parent;
prog_p_fd = bpf_program__fd(prog);
prog_p_atype = bpf_program__expected_attach_type(prog);
err = bpf_prog_attach_opts(prog_p_fd, cg_parent, prog_p_atype, &opts);
if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent"))
goto close_skel;
opts.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE;
if (has_relative_fd)
opts.relative_fd = prog_p_fd;
prog = skel->progs.parent_2;
prog_p2_fd = bpf_program__fd(prog);
prog_p2_atype = bpf_program__expected_attach_type(prog);
err = bpf_prog_attach_opts(prog_p2_fd, cg_parent, prog_p2_atype, &opts);
if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent_2"))
goto detach_parent;
err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen);
if (!ASSERT_OK(err, "getsockopt"))
goto detach_parent_2;
result = skel->bss->result;
ASSERT_TRUE(result[0] == 4 && result[1] == 3, "result values");
detach_parent_2:
ASSERT_OK(bpf_prog_detach2(prog_p2_fd, cg_parent, prog_p2_atype),
"bpf_prog_detach2-parent_2");
detach_parent:
ASSERT_OK(bpf_prog_detach2(prog_p_fd, cg_parent, prog_p_atype),
"bpf_prog_detach2-parent");
close_skel:
cgroup_preorder__destroy(skel);
return err;
}
void test_cgroup_mprog_ordering(void)
{
int cg_parent = -1, sock_fd = -1;
cg_parent = test__join_cgroup("/parent");
if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent"))
goto out;
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (!ASSERT_GE(sock_fd, 0, "socket"))
goto out;
ASSERT_OK(run_getsockopt_test(cg_parent, sock_fd, false), "getsockopt_test_1");
ASSERT_OK(run_getsockopt_test(cg_parent, sock_fd, true), "getsockopt_test_2");
out:
close(sock_fd);
close(cg_parent);
}

View file

@ -7,133 +7,60 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/xattr.h>
#include <test_progs.h> #include <test_progs.h>
#include "cgroup_helpers.h"
#include "read_cgroupfs_xattr.skel.h" #include "read_cgroupfs_xattr.skel.h"
#include "cgroup_read_xattr.skel.h" #include "cgroup_read_xattr.skel.h"
#define CGROUP_FS_ROOT "/sys/fs/cgroup/" #define CGROUP_FS_PARENT "foo/"
#define CGROUP_FS_PARENT CGROUP_FS_ROOT "foo/"
#define CGROUP_FS_CHILD CGROUP_FS_PARENT "bar/" #define CGROUP_FS_CHILD CGROUP_FS_PARENT "bar/"
#define TMP_FILE "/tmp/selftests_cgroup_xattr"
static int move_pid_to_cgroup(const char *cgroup_folder, pid_t pid)
{
char filename[128];
char pid_str[64];
int procs_fd;
int ret;
snprintf(filename, sizeof(filename), "%scgroup.procs", cgroup_folder);
snprintf(pid_str, sizeof(pid_str), "%d", pid);
procs_fd = open(filename, O_WRONLY | O_APPEND);
if (!ASSERT_OK_FD(procs_fd, "open"))
return -1;
ret = write(procs_fd, pid_str, strlen(pid_str));
close(procs_fd);
if (!ASSERT_GT(ret, 0, "write cgroup.procs"))
return -1;
return 0;
}
static void reset_cgroups_and_lo(void)
{
rmdir(CGROUP_FS_CHILD);
rmdir(CGROUP_FS_PARENT);
system("ip addr del 1.1.1.1/32 dev lo");
system("ip link set dev lo down");
}
static const char xattr_value_a[] = "bpf_selftest_value_a"; static const char xattr_value_a[] = "bpf_selftest_value_a";
static const char xattr_value_b[] = "bpf_selftest_value_b"; static const char xattr_value_b[] = "bpf_selftest_value_b";
static const char xattr_name[] = "user.bpf_test"; static const char xattr_name[] = "user.bpf_test";
static int setup_cgroups_and_lo(void)
{
int err;
err = mkdir(CGROUP_FS_PARENT, 0755);
if (!ASSERT_OK(err, "mkdir 1"))
goto error;
err = mkdir(CGROUP_FS_CHILD, 0755);
if (!ASSERT_OK(err, "mkdir 2"))
goto error;
err = setxattr(CGROUP_FS_PARENT, xattr_name, xattr_value_a,
strlen(xattr_value_a) + 1, 0);
if (!ASSERT_OK(err, "setxattr 1"))
goto error;
err = setxattr(CGROUP_FS_CHILD, xattr_name, xattr_value_b,
strlen(xattr_value_b) + 1, 0);
if (!ASSERT_OK(err, "setxattr 2"))
goto error;
err = system("ip link set dev lo up");
if (!ASSERT_OK(err, "lo up"))
goto error;
err = system("ip addr add 1.1.1.1 dev lo");
if (!ASSERT_OK(err, "lo addr v4"))
goto error;
err = write_sysctl("/proc/sys/net/ipv4/ping_group_range", "0 0");
if (!ASSERT_OK(err, "write_sysctl"))
goto error;
return 0;
error:
reset_cgroups_and_lo();
return err;
}
static void test_read_cgroup_xattr(void) static void test_read_cgroup_xattr(void)
{ {
struct sockaddr_in sa4 = { int tmp_fd, parent_cgroup_fd = -1, child_cgroup_fd = -1;
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
};
struct read_cgroupfs_xattr *skel = NULL; struct read_cgroupfs_xattr *skel = NULL;
pid_t pid = gettid();
int sock_fd = -1;
int connect_fd = -1;
if (!ASSERT_OK(setup_cgroups_and_lo(), "setup_cgroups_and_lo")) parent_cgroup_fd = test__join_cgroup(CGROUP_FS_PARENT);
if (!ASSERT_OK_FD(parent_cgroup_fd, "create parent cgroup"))
return; return;
if (!ASSERT_OK(move_pid_to_cgroup(CGROUP_FS_CHILD, pid), if (!ASSERT_OK(set_cgroup_xattr(CGROUP_FS_PARENT, xattr_name, xattr_value_a),
"move_pid_to_cgroup")) "set parent xattr"))
goto out;
child_cgroup_fd = test__join_cgroup(CGROUP_FS_CHILD);
if (!ASSERT_OK_FD(child_cgroup_fd, "create child cgroup"))
goto out;
if (!ASSERT_OK(set_cgroup_xattr(CGROUP_FS_CHILD, xattr_name, xattr_value_b),
"set child xattr"))
goto out; goto out;
skel = read_cgroupfs_xattr__open_and_load(); skel = read_cgroupfs_xattr__open_and_load();
if (!ASSERT_OK_PTR(skel, "read_cgroupfs_xattr__open_and_load")) if (!ASSERT_OK_PTR(skel, "read_cgroupfs_xattr__open_and_load"))
goto out; goto out;
skel->bss->target_pid = pid; skel->bss->target_pid = gettid();
if (!ASSERT_OK(read_cgroupfs_xattr__attach(skel), "read_cgroupfs_xattr__attach")) if (!ASSERT_OK(read_cgroupfs_xattr__attach(skel), "read_cgroupfs_xattr__attach"))
goto out; goto out;
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); tmp_fd = open(TMP_FILE, O_RDONLY | O_CREAT);
if (!ASSERT_OK_FD(sock_fd, "sock create")) ASSERT_OK_FD(tmp_fd, "open tmp file");
goto out; close(tmp_fd);
connect_fd = connect(sock_fd, &sa4, sizeof(sa4));
if (!ASSERT_OK_FD(connect_fd, "connect 1"))
goto out;
close(connect_fd);
ASSERT_TRUE(skel->bss->found_value_a, "found_value_a"); ASSERT_TRUE(skel->bss->found_value_a, "found_value_a");
ASSERT_TRUE(skel->bss->found_value_b, "found_value_b"); ASSERT_TRUE(skel->bss->found_value_b, "found_value_b");
out: out:
close(connect_fd); close(child_cgroup_fd);
close(sock_fd); close(parent_cgroup_fd);
read_cgroupfs_xattr__destroy(skel); read_cgroupfs_xattr__destroy(skel);
move_pid_to_cgroup(CGROUP_FS_ROOT, pid); unlink(TMP_FILE);
reset_cgroups_and_lo();
} }
void test_cgroup_xattr(void) void test_cgroup_xattr(void)

Some files were not shown because too many files have changed in this diff Show more