Daniel Borkmann says: ==================== pull-request: bpf-next 2022-04-27 We've added 85 non-merge commits during the last 18 day(s) which contain a total of 163 files changed, 4499 insertions(+), 1521 deletions(-). The main changes are: 1) Teach libbpf to enhance BPF verifier log with human-readable and relevant information about failed CO-RE relocations, from Andrii Nakryiko. 2) Add typed pointer support in BPF maps and enable it for unreferenced pointers (via probe read) and referenced ones that can be passed to in-kernel helpers, from Kumar Kartikeya Dwivedi. 3) Improve xsk to break NAPI loop when rx queue gets full to allow for forward progress to consume descriptors, from Maciej Fijalkowski & Björn Töpel. 4) Fix a small RCU read-side race in BPF_PROG_RUN routines which dereferenced the effective prog array before the rcu_read_lock, from Stanislav Fomichev. 5) Implement BPF atomic operations for RV64 JIT, and add libbpf parsing logic for USDT arguments under riscv{32,64}, from Pu Lehui. 6) Implement libbpf parsing of USDT arguments under aarch64, from Alan Maguire. 7) Enable bpftool build for musl and remove nftw with FTW_ACTIONRETVAL usage so it can be shipped under Alpine which is musl-based, from Dominique Martinet. 8) Clean up {sk,task,inode} local storage trace RCU handling as they do not need to use call_rcu_tasks_trace() barrier, from KP Singh. 9) Improve libbpf API documentation and fix error return handling of various API functions, from Grant Seltzer. 10) Enlarge offset check for bpf_skb_{load,store}_bytes() helpers given data length of frags + frag_list may surpass old offset limit, from Liu Jian. 11) Various improvements to prog_tests in area of logging, test execution and by-name subtest selection, from Mykola Lysenko. 12) Simplify map_btf_id generation for all map types by moving this process to build time with help of resolve_btfids infra, from Menglong Dong. 13) Fix a libbpf bug in probing when falling back to legacy bpf_probe_read*() helpers; the probing caused always to use old helpers, from Runqing Yang. 14) Add support for ARCompact and ARCv2 platforms for libbpf's PT_REGS tracing macros, from Vladimir Isaev. 15) Cleanup BPF selftests to remove old & unneeded rlimit code given kernel switched to memcg-based memory accouting a while ago, from Yafang Shao. 16) Refactor of BPF sysctl handlers to move them to BPF core, from Yan Zhu. 17) Fix BPF selftests in two occasions to work around regressions caused by latest LLVM to unblock CI until their fixes are worked out, from Yonghong Song. 18) Misc cleanups all over the place, from various others. * https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (85 commits) selftests/bpf: Add libbpf's log fixup logic selftests libbpf: Fix up verifier log for unguarded failed CO-RE relos libbpf: Simplify bpf_core_parse_spec() signature libbpf: Refactor CO-RE relo human description formatting routine libbpf: Record subprog-resolved CO-RE relocations unconditionally selftests/bpf: Add CO-RE relos and SEC("?...") to linked_funcs selftests libbpf: Avoid joining .BTF.ext data with BPF programs by section name libbpf: Fix logic for finding matching program for CO-RE relocation libbpf: Drop unhelpful "program too large" guess libbpf: Fix anonymous type check in CO-RE logic bpf: Compute map_btf_id during build time selftests/bpf: Add test for strict BTF type check selftests/bpf: Add verifier tests for kptr selftests/bpf: Add C tests for kptr libbpf: Add kptr type tag macros to bpf_helpers.h bpf: Make BTF type match stricter for release arguments bpf: Teach verifier about kptr_get kfunc helpers bpf: Wire up freeing of referenced kptr bpf: Populate pairs of btf_id and destructor kfunc in btf bpf: Adapt copy_map_value for multiple offset case ... ==================== Link: https://lore.kernel.org/r/20220427224758.20976-1-daniel@iogearbox.net Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
50c6afabfd
@ -6,14 +6,13 @@ libbpf
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
API Documentation <https://libbpf.readthedocs.io/en/latest/api.html>
|
||||
libbpf_naming_convention
|
||||
libbpf_build
|
||||
|
||||
This is documentation for libbpf, a userspace library for loading and
|
||||
interacting with bpf programs.
|
||||
|
||||
For API documentation see the `versioned API documentation site <https://libbpf.readthedocs.io/en/latest/api.html>`_.
|
||||
|
||||
All general BPF questions, including kernel functionality, libbpf APIs and
|
||||
their application, should be sent to bpf@vger.kernel.org mailing list.
|
||||
You can `subscribe <http://vger.kernel.org/vger-lists.html#bpf>`_ to the
|
||||
|
@ -535,6 +535,43 @@ static inline u32 rv_amoadd_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
return rv_amo_insn(0, aq, rl, rs2, rs1, 2, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_amoand_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0xc, aq, rl, rs2, rs1, 2, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_amoor_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x8, aq, rl, rs2, rs1, 2, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_amoxor_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x4, aq, rl, rs2, rs1, 2, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_amoswap_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x1, aq, rl, rs2, rs1, 2, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_lr_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x2, aq, rl, rs2, rs1, 2, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_sc_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x3, aq, rl, rs2, rs1, 2, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_fence(u8 pred, u8 succ)
|
||||
{
|
||||
u16 imm11_0 = pred << 4 | succ;
|
||||
|
||||
return rv_i_insn(imm11_0, 0, 0, 0, 0xf);
|
||||
}
|
||||
|
||||
/* RVC instrutions. */
|
||||
|
||||
static inline u16 rvc_addi4spn(u8 rd, u32 imm10)
|
||||
@ -753,6 +790,36 @@ static inline u32 rv_amoadd_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
return rv_amo_insn(0, aq, rl, rs2, rs1, 3, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_amoand_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0xc, aq, rl, rs2, rs1, 3, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_amoor_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x8, aq, rl, rs2, rs1, 3, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_amoxor_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x4, aq, rl, rs2, rs1, 3, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_amoswap_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x1, aq, rl, rs2, rs1, 3, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_lr_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x2, aq, rl, rs2, rs1, 3, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rv_sc_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x3, aq, rl, rs2, rs1, 3, rd, 0x2f);
|
||||
}
|
||||
|
||||
/* RV64-only RVC instructions. */
|
||||
|
||||
static inline u16 rvc_ld(u8 rd, u32 imm8, u8 rs1)
|
||||
|
@ -455,6 +455,90 @@ static int emit_call(bool fixed, u64 addr, struct rv_jit_context *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emit_atomic(u8 rd, u8 rs, s16 off, s32 imm, bool is64,
|
||||
struct rv_jit_context *ctx)
|
||||
{
|
||||
u8 r0;
|
||||
int jmp_offset;
|
||||
|
||||
if (off) {
|
||||
if (is_12b_int(off)) {
|
||||
emit_addi(RV_REG_T1, rd, off, ctx);
|
||||
} else {
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
}
|
||||
rd = RV_REG_T1;
|
||||
}
|
||||
|
||||
switch (imm) {
|
||||
/* lock *(u32/u64 *)(dst_reg + off16) <op>= src_reg */
|
||||
case BPF_ADD:
|
||||
emit(is64 ? rv_amoadd_d(RV_REG_ZERO, rs, rd, 0, 0) :
|
||||
rv_amoadd_w(RV_REG_ZERO, rs, rd, 0, 0), ctx);
|
||||
break;
|
||||
case BPF_AND:
|
||||
emit(is64 ? rv_amoand_d(RV_REG_ZERO, rs, rd, 0, 0) :
|
||||
rv_amoand_w(RV_REG_ZERO, rs, rd, 0, 0), ctx);
|
||||
break;
|
||||
case BPF_OR:
|
||||
emit(is64 ? rv_amoor_d(RV_REG_ZERO, rs, rd, 0, 0) :
|
||||
rv_amoor_w(RV_REG_ZERO, rs, rd, 0, 0), ctx);
|
||||
break;
|
||||
case BPF_XOR:
|
||||
emit(is64 ? rv_amoxor_d(RV_REG_ZERO, rs, rd, 0, 0) :
|
||||
rv_amoxor_w(RV_REG_ZERO, rs, rd, 0, 0), ctx);
|
||||
break;
|
||||
/* src_reg = atomic_fetch_<op>(dst_reg + off16, src_reg) */
|
||||
case BPF_ADD | BPF_FETCH:
|
||||
emit(is64 ? rv_amoadd_d(rs, rs, rd, 0, 0) :
|
||||
rv_amoadd_w(rs, rs, rd, 0, 0), ctx);
|
||||
if (!is64)
|
||||
emit_zext_32(rs, ctx);
|
||||
break;
|
||||
case BPF_AND | BPF_FETCH:
|
||||
emit(is64 ? rv_amoand_d(rs, rs, rd, 0, 0) :
|
||||
rv_amoand_w(rs, rs, rd, 0, 0), ctx);
|
||||
if (!is64)
|
||||
emit_zext_32(rs, ctx);
|
||||
break;
|
||||
case BPF_OR | BPF_FETCH:
|
||||
emit(is64 ? rv_amoor_d(rs, rs, rd, 0, 0) :
|
||||
rv_amoor_w(rs, rs, rd, 0, 0), ctx);
|
||||
if (!is64)
|
||||
emit_zext_32(rs, ctx);
|
||||
break;
|
||||
case BPF_XOR | BPF_FETCH:
|
||||
emit(is64 ? rv_amoxor_d(rs, rs, rd, 0, 0) :
|
||||
rv_amoxor_w(rs, rs, rd, 0, 0), ctx);
|
||||
if (!is64)
|
||||
emit_zext_32(rs, ctx);
|
||||
break;
|
||||
/* src_reg = atomic_xchg(dst_reg + off16, src_reg); */
|
||||
case BPF_XCHG:
|
||||
emit(is64 ? rv_amoswap_d(rs, rs, rd, 0, 0) :
|
||||
rv_amoswap_w(rs, rs, rd, 0, 0), ctx);
|
||||
if (!is64)
|
||||
emit_zext_32(rs, ctx);
|
||||
break;
|
||||
/* r0 = atomic_cmpxchg(dst_reg + off16, r0, src_reg); */
|
||||
case BPF_CMPXCHG:
|
||||
r0 = bpf_to_rv_reg(BPF_REG_0, ctx);
|
||||
emit(is64 ? rv_addi(RV_REG_T2, r0, 0) :
|
||||
rv_addiw(RV_REG_T2, r0, 0), ctx);
|
||||
emit(is64 ? rv_lr_d(r0, 0, rd, 0, 0) :
|
||||
rv_lr_w(r0, 0, rd, 0, 0), ctx);
|
||||
jmp_offset = ninsns_rvoff(8);
|
||||
emit(rv_bne(RV_REG_T2, r0, jmp_offset >> 1), ctx);
|
||||
emit(is64 ? rv_sc_d(RV_REG_T3, rs, rd, 0, 0) :
|
||||
rv_sc_w(RV_REG_T3, rs, rd, 0, 0), ctx);
|
||||
jmp_offset = ninsns_rvoff(-6);
|
||||
emit(rv_bne(RV_REG_T3, 0, jmp_offset >> 1), ctx);
|
||||
emit(rv_fence(0x3, 0x3), ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0)
|
||||
#define BPF_FIXUP_REG_MASK GENMASK(31, 27)
|
||||
|
||||
@ -1146,30 +1230,8 @@ out_be:
|
||||
break;
|
||||
case BPF_STX | BPF_ATOMIC | BPF_W:
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
if (insn->imm != BPF_ADD) {
|
||||
pr_err("bpf-jit: not supported: atomic operation %02x ***\n",
|
||||
insn->imm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* atomic_add: lock *(u32 *)(dst + off) += src
|
||||
* atomic_add: lock *(u64 *)(dst + off) += src
|
||||
*/
|
||||
|
||||
if (off) {
|
||||
if (is_12b_int(off)) {
|
||||
emit_addi(RV_REG_T1, rd, off, ctx);
|
||||
} else {
|
||||
emit_imm(RV_REG_T1, off, ctx);
|
||||
emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
|
||||
}
|
||||
|
||||
rd = RV_REG_T1;
|
||||
}
|
||||
|
||||
emit(BPF_SIZE(code) == BPF_W ?
|
||||
rv_amoadd_w(RV_REG_ZERO, rs, rd, 0, 0) :
|
||||
rv_amoadd_d(RV_REG_ZERO, rs, rd, 0, 0), ctx);
|
||||
emit_atomic(rd, rs, off, imm,
|
||||
BPF_SIZE(code) == BPF_DW, ctx);
|
||||
break;
|
||||
default:
|
||||
pr_err("bpf-jit: unknown opcode %02x\n", code);
|
||||
|
@ -216,8 +216,12 @@ void lirc_bpf_run(struct rc_dev *rcdev, u32 sample)
|
||||
|
||||
raw->bpf_sample = sample;
|
||||
|
||||
if (raw->progs)
|
||||
BPF_PROG_RUN_ARRAY(raw->progs, &raw->bpf_sample, bpf_prog_run);
|
||||
if (raw->progs) {
|
||||
rcu_read_lock();
|
||||
bpf_prog_run_array(rcu_dereference(raw->progs),
|
||||
&raw->bpf_sample, bpf_prog_run);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -20,6 +20,7 @@ void i40e_release_rx_desc(struct i40e_ring *rx_ring, u32 val);
|
||||
#define I40E_XDP_CONSUMED BIT(0)
|
||||
#define I40E_XDP_TX BIT(1)
|
||||
#define I40E_XDP_REDIR BIT(2)
|
||||
#define I40E_XDP_EXIT BIT(3)
|
||||
|
||||
/*
|
||||
* build_ctob - Builds the Tx descriptor (cmd, offset and type) qword
|
||||
|
@ -161,9 +161,13 @@ static int i40e_run_xdp_zc(struct i40e_ring *rx_ring, struct xdp_buff *xdp)
|
||||
|
||||
if (likely(act == XDP_REDIRECT)) {
|
||||
err = xdp_do_redirect(rx_ring->netdev, xdp, xdp_prog);
|
||||
if (err)
|
||||
goto out_failure;
|
||||
return I40E_XDP_REDIR;
|
||||
if (!err)
|
||||
return I40E_XDP_REDIR;
|
||||
if (xsk_uses_need_wakeup(rx_ring->xsk_pool) && err == -ENOBUFS)
|
||||
result = I40E_XDP_EXIT;
|
||||
else
|
||||
result = I40E_XDP_CONSUMED;
|
||||
goto out_failure;
|
||||
}
|
||||
|
||||
switch (act) {
|
||||
@ -175,16 +179,16 @@ static int i40e_run_xdp_zc(struct i40e_ring *rx_ring, struct xdp_buff *xdp)
|
||||
if (result == I40E_XDP_CONSUMED)
|
||||
goto out_failure;
|
||||
break;
|
||||
case XDP_DROP:
|
||||
result = I40E_XDP_CONSUMED;
|
||||
break;
|
||||
default:
|
||||
bpf_warn_invalid_xdp_action(rx_ring->netdev, xdp_prog, act);
|
||||
fallthrough;
|
||||
case XDP_ABORTED:
|
||||
result = I40E_XDP_CONSUMED;
|
||||
out_failure:
|
||||
trace_xdp_exception(rx_ring->netdev, xdp_prog, act);
|
||||
fallthrough; /* handle aborts by dropping packet */
|
||||
case XDP_DROP:
|
||||
result = I40E_XDP_CONSUMED;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -271,7 +275,8 @@ static void i40e_handle_xdp_result_zc(struct i40e_ring *rx_ring,
|
||||
unsigned int *rx_packets,
|
||||
unsigned int *rx_bytes,
|
||||
unsigned int size,
|
||||
unsigned int xdp_res)
|
||||
unsigned int xdp_res,
|
||||
bool *failure)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
@ -281,11 +286,15 @@ static void i40e_handle_xdp_result_zc(struct i40e_ring *rx_ring,
|
||||
if (likely(xdp_res == I40E_XDP_REDIR) || xdp_res == I40E_XDP_TX)
|
||||
return;
|
||||
|
||||
if (xdp_res == I40E_XDP_EXIT) {
|
||||
*failure = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (xdp_res == I40E_XDP_CONSUMED) {
|
||||
xsk_buff_free(xdp_buff);
|
||||
return;
|
||||
}
|
||||
|
||||
if (xdp_res == I40E_XDP_PASS) {
|
||||
/* NB! We are not checking for errors using
|
||||
* i40e_test_staterr with
|
||||
@ -371,7 +380,9 @@ int i40e_clean_rx_irq_zc(struct i40e_ring *rx_ring, int budget)
|
||||
|
||||
xdp_res = i40e_run_xdp_zc(rx_ring, bi);
|
||||
i40e_handle_xdp_result_zc(rx_ring, bi, rx_desc, &rx_packets,
|
||||
&rx_bytes, size, xdp_res);
|
||||
&rx_bytes, size, xdp_res, &failure);
|
||||
if (failure)
|
||||
break;
|
||||
total_rx_packets += rx_packets;
|
||||
total_rx_bytes += rx_bytes;
|
||||
xdp_xmit |= xdp_res & (I40E_XDP_TX | I40E_XDP_REDIR);
|
||||
@ -382,7 +393,7 @@ int i40e_clean_rx_irq_zc(struct i40e_ring *rx_ring, int budget)
|
||||
cleaned_count = (next_to_clean - rx_ring->next_to_use - 1) & count_mask;
|
||||
|
||||
if (cleaned_count >= I40E_RX_BUFFER_WRITE)
|
||||
failure = !i40e_alloc_rx_buffers_zc(rx_ring, cleaned_count);
|
||||
failure |= !i40e_alloc_rx_buffers_zc(rx_ring, cleaned_count);
|
||||
|
||||
i40e_finalize_xdp_rx(rx_ring, xdp_xmit);
|
||||
i40e_update_rx_stats(rx_ring, total_rx_bytes, total_rx_packets);
|
||||
@ -594,13 +605,13 @@ int i40e_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags)
|
||||
return -ENETDOWN;
|
||||
|
||||
if (!i40e_enabled_xdp_vsi(vsi))
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
if (queue_id >= vsi->num_queue_pairs)
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
if (!vsi->xdp_rings[queue_id]->xsk_pool)
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
ring = vsi->xdp_rings[queue_id];
|
||||
|
||||
|
@ -133,6 +133,7 @@ static inline int ice_skb_pad(void)
|
||||
#define ICE_XDP_CONSUMED BIT(0)
|
||||
#define ICE_XDP_TX BIT(1)
|
||||
#define ICE_XDP_REDIR BIT(2)
|
||||
#define ICE_XDP_EXIT BIT(3)
|
||||
|
||||
#define ICE_RX_DMA_ATTR \
|
||||
(DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING)
|
||||
|
@ -545,9 +545,13 @@ ice_run_xdp_zc(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp,
|
||||
|
||||
if (likely(act == XDP_REDIRECT)) {
|
||||
err = xdp_do_redirect(rx_ring->netdev, xdp, xdp_prog);
|
||||
if (err)
|
||||
goto out_failure;
|
||||
return ICE_XDP_REDIR;
|
||||
if (!err)
|
||||
return ICE_XDP_REDIR;
|
||||
if (xsk_uses_need_wakeup(rx_ring->xsk_pool) && err == -ENOBUFS)
|
||||
result = ICE_XDP_EXIT;
|
||||
else
|
||||
result = ICE_XDP_CONSUMED;
|
||||
goto out_failure;
|
||||
}
|
||||
|
||||
switch (act) {
|
||||
@ -558,15 +562,16 @@ ice_run_xdp_zc(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp,
|
||||
if (result == ICE_XDP_CONSUMED)
|
||||
goto out_failure;
|
||||
break;
|
||||
case XDP_DROP:
|
||||
result = ICE_XDP_CONSUMED;
|
||||
break;
|
||||
default:
|
||||
bpf_warn_invalid_xdp_action(rx_ring->netdev, xdp_prog, act);
|
||||
fallthrough;
|
||||
case XDP_ABORTED:
|
||||
result = ICE_XDP_CONSUMED;
|
||||
out_failure:
|
||||
trace_xdp_exception(rx_ring->netdev, xdp_prog, act);
|
||||
fallthrough;
|
||||
case XDP_DROP:
|
||||
result = ICE_XDP_CONSUMED;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -587,6 +592,7 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
|
||||
unsigned int xdp_xmit = 0;
|
||||
struct bpf_prog *xdp_prog;
|
||||
bool failure = false;
|
||||
int entries_to_alloc;
|
||||
|
||||
/* ZC patch is enabled only when XDP program is set,
|
||||
* so here it can not be NULL
|
||||
@ -634,18 +640,23 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring, int budget)
|
||||
xsk_buff_dma_sync_for_cpu(xdp, rx_ring->xsk_pool);
|
||||
|
||||
xdp_res = ice_run_xdp_zc(rx_ring, xdp, xdp_prog, xdp_ring);
|
||||
if (xdp_res) {
|
||||
if (xdp_res & (ICE_XDP_TX | ICE_XDP_REDIR))
|
||||
xdp_xmit |= xdp_res;
|
||||
else
|
||||
xsk_buff_free(xdp);
|
||||
|
||||
total_rx_bytes += size;
|
||||
total_rx_packets++;
|
||||
|
||||
ice_bump_ntc(rx_ring);
|
||||
continue;
|
||||
if (likely(xdp_res & (ICE_XDP_TX | ICE_XDP_REDIR))) {
|
||||
xdp_xmit |= xdp_res;
|
||||
} else if (xdp_res == ICE_XDP_EXIT) {
|
||||
failure = true;
|
||||
break;
|
||||
} else if (xdp_res == ICE_XDP_CONSUMED) {
|
||||
xsk_buff_free(xdp);
|
||||
} else if (xdp_res == ICE_XDP_PASS) {
|
||||
goto construct_skb;
|
||||
}
|
||||
|
||||
total_rx_bytes += size;
|
||||
total_rx_packets++;
|
||||
|
||||
ice_bump_ntc(rx_ring);
|
||||
continue;
|
||||
|
||||
construct_skb:
|
||||
/* XDP_PASS path */
|
||||
skb = ice_construct_skb_zc(rx_ring, xdp);
|
||||
@ -673,7 +684,9 @@ construct_skb:
|
||||
ice_receive_skb(rx_ring, skb, vlan_tag);
|
||||
}
|
||||
|
||||
failure = !ice_alloc_rx_bufs_zc(rx_ring, ICE_DESC_UNUSED(rx_ring));
|
||||
entries_to_alloc = ICE_DESC_UNUSED(rx_ring);
|
||||
if (entries_to_alloc > ICE_RING_QUARTER(rx_ring))
|
||||
failure |= !ice_alloc_rx_bufs_zc(rx_ring, entries_to_alloc);
|
||||
|
||||
ice_finalize_xdp_rx(xdp_ring, xdp_xmit);
|
||||
ice_update_rx_ring_stats(rx_ring, total_rx_packets, total_rx_bytes);
|
||||
@ -929,13 +942,13 @@ ice_xsk_wakeup(struct net_device *netdev, u32 queue_id,
|
||||
return -ENETDOWN;
|
||||
|
||||
if (!ice_is_xdp_ena_vsi(vsi))
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
if (queue_id >= vsi->num_txq)
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
if (!vsi->xdp_rings[queue_id]->xsk_pool)
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
ring = vsi->xdp_rings[queue_id];
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define IXGBE_XDP_CONSUMED BIT(0)
|
||||
#define IXGBE_XDP_TX BIT(1)
|
||||
#define IXGBE_XDP_REDIR BIT(2)
|
||||
#define IXGBE_XDP_EXIT BIT(3)
|
||||
|
||||
#define IXGBE_TXD_CMD (IXGBE_TXD_CMD_EOP | \
|
||||
IXGBE_TXD_CMD_RS)
|
||||
|
@ -109,9 +109,13 @@ static int ixgbe_run_xdp_zc(struct ixgbe_adapter *adapter,
|
||||
|
||||
if (likely(act == XDP_REDIRECT)) {
|
||||
err = xdp_do_redirect(rx_ring->netdev, xdp, xdp_prog);
|
||||
if (err)
|
||||
goto out_failure;
|
||||
return IXGBE_XDP_REDIR;
|
||||
if (!err)
|
||||
return IXGBE_XDP_REDIR;
|
||||
if (xsk_uses_need_wakeup(rx_ring->xsk_pool) && err == -ENOBUFS)
|
||||
result = IXGBE_XDP_EXIT;
|
||||
else
|
||||
result = IXGBE_XDP_CONSUMED;
|
||||
goto out_failure;
|
||||
}
|
||||
|
||||
switch (act) {
|
||||
@ -130,16 +134,16 @@ static int ixgbe_run_xdp_zc(struct ixgbe_adapter *adapter,
|
||||
if (result == IXGBE_XDP_CONSUMED)
|
||||
goto out_failure;
|
||||
break;
|
||||
case XDP_DROP:
|
||||
result = IXGBE_XDP_CONSUMED;
|
||||
break;
|
||||
default:
|
||||
bpf_warn_invalid_xdp_action(rx_ring->netdev, xdp_prog, act);
|
||||
fallthrough;
|
||||
case XDP_ABORTED:
|
||||
result = IXGBE_XDP_CONSUMED;
|
||||
out_failure:
|
||||
trace_xdp_exception(rx_ring->netdev, xdp_prog, act);
|
||||
fallthrough; /* handle aborts by dropping packet */
|
||||
case XDP_DROP:
|
||||
result = IXGBE_XDP_CONSUMED;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -303,21 +307,26 @@ int ixgbe_clean_rx_irq_zc(struct ixgbe_q_vector *q_vector,
|
||||
xsk_buff_dma_sync_for_cpu(bi->xdp, rx_ring->xsk_pool);
|
||||
xdp_res = ixgbe_run_xdp_zc(adapter, rx_ring, bi->xdp);
|
||||
|
||||
if (xdp_res) {
|
||||
if (xdp_res & (IXGBE_XDP_TX | IXGBE_XDP_REDIR))
|
||||
xdp_xmit |= xdp_res;
|
||||
else
|
||||
xsk_buff_free(bi->xdp);
|
||||
|
||||
bi->xdp = NULL;
|
||||
total_rx_packets++;
|
||||
total_rx_bytes += size;
|
||||
|
||||
cleaned_count++;
|
||||
ixgbe_inc_ntc(rx_ring);
|
||||
continue;
|
||||
if (likely(xdp_res & (IXGBE_XDP_TX | IXGBE_XDP_REDIR))) {
|
||||
xdp_xmit |= xdp_res;
|
||||
} else if (xdp_res == IXGBE_XDP_EXIT) {
|
||||
failure = true;
|
||||
break;
|
||||
} else if (xdp_res == IXGBE_XDP_CONSUMED) {
|
||||
xsk_buff_free(bi->xdp);
|
||||
} else if (xdp_res == IXGBE_XDP_PASS) {
|
||||
goto construct_skb;
|
||||
}
|
||||
|
||||
bi->xdp = NULL;
|
||||
total_rx_packets++;
|
||||
total_rx_bytes += size;
|
||||
|
||||
cleaned_count++;
|
||||
ixgbe_inc_ntc(rx_ring);
|
||||
continue;
|
||||
|
||||
construct_skb:
|
||||
/* XDP_PASS path */
|
||||
skb = ixgbe_construct_skb_zc(rx_ring, bi->xdp);
|
||||
if (!skb) {
|
||||
@ -516,10 +525,10 @@ int ixgbe_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags)
|
||||
return -ENETDOWN;
|
||||
|
||||
if (!READ_ONCE(adapter->xdp_prog))
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
if (qid >= adapter->num_xdp_queues)
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
ring = adapter->xdp_ring[qid];
|
||||
|
||||
@ -527,7 +536,7 @@ int ixgbe_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags)
|
||||
return -ENETDOWN;
|
||||
|
||||
if (!ring->xsk_pool)
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
if (!napi_if_scheduled_mark_missed(&ring->q_vector->napi)) {
|
||||
u64 eics = BIT_ULL(ring->q_vector->v_idx);
|
||||
|
@ -23,7 +23,7 @@ int mlx5e_xsk_wakeup(struct net_device *dev, u32 qid, u32 flags)
|
||||
c = priv->channels.c[ix];
|
||||
|
||||
if (unlikely(!test_bit(MLX5E_CHANNEL_STATE_XSK, c->state)))
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
if (!napi_if_scheduled_mark_missed(&c->napi)) {
|
||||
/* To avoid WQE overrun, don't post a NOP if async_icosq is not
|
||||
|
@ -6559,7 +6559,7 @@ int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags)
|
||||
return -ENETDOWN;
|
||||
|
||||
if (!stmmac_xdp_is_enabled(priv))
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
if (queue >= priv->plat->rx_queues_to_use ||
|
||||
queue >= priv->plat->tx_queues_to_use)
|
||||
@ -6570,7 +6570,7 @@ int stmmac_xsk_wakeup(struct net_device *dev, u32 queue, u32 flags)
|
||||
ch = &priv->channel[queue];
|
||||
|
||||
if (!rx_q->xsk_pool && !tx_q->xsk_pool)
|
||||
return -ENXIO;
|
||||
return -EINVAL;
|
||||
|
||||
if (!napi_if_scheduled_mark_missed(&ch->rxtx_napi)) {
|
||||
/* EQoS does not have per-DMA channel SW interrupt,
|
||||
|
@ -225,24 +225,20 @@ static inline bool cgroup_bpf_sock_enabled(struct sock *sk,
|
||||
|
||||
#define BPF_CGROUP_RUN_SA_PROG(sk, uaddr, atype) \
|
||||
({ \
|
||||
u32 __unused_flags; \
|
||||
int __ret = 0; \
|
||||
if (cgroup_bpf_enabled(atype)) \
|
||||
__ret = __cgroup_bpf_run_filter_sock_addr(sk, uaddr, atype, \
|
||||
NULL, \
|
||||
&__unused_flags); \
|
||||
NULL, NULL); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#define BPF_CGROUP_RUN_SA_PROG_LOCK(sk, uaddr, atype, t_ctx) \
|
||||
({ \
|
||||
u32 __unused_flags; \
|
||||
int __ret = 0; \
|
||||
if (cgroup_bpf_enabled(atype)) { \
|
||||
lock_sock(sk); \
|
||||
__ret = __cgroup_bpf_run_filter_sock_addr(sk, uaddr, atype, \
|
||||
t_ctx, \
|
||||
&__unused_flags); \
|
||||
t_ctx, NULL); \
|
||||
release_sock(sk); \
|
||||
} \
|
||||
__ret; \
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/percpu-refcount.h>
|
||||
#include <linux/bpfptr.h>
|
||||
#include <linux/btf.h>
|
||||
|
||||
struct bpf_verifier_env;
|
||||
struct bpf_verifier_log;
|
||||
@ -147,14 +148,48 @@ struct bpf_map_ops {
|
||||
bpf_callback_t callback_fn,
|
||||
void *callback_ctx, u64 flags);
|
||||
|
||||
/* BTF name and id of struct allocated by map_alloc */
|
||||
const char * const map_btf_name;
|
||||
/* BTF id of struct allocated by map_alloc */
|
||||
int *map_btf_id;
|
||||
|
||||
/* bpf_iter info used to open a seq_file */
|
||||
const struct bpf_iter_seq_info *iter_seq_info;
|
||||
};
|
||||
|
||||
enum {
|
||||
/* Support at most 8 pointers in a BPF map value */
|
||||
BPF_MAP_VALUE_OFF_MAX = 8,
|
||||
BPF_MAP_OFF_ARR_MAX = BPF_MAP_VALUE_OFF_MAX +
|
||||
1 + /* for bpf_spin_lock */
|
||||
1, /* for bpf_timer */
|
||||
};
|
||||
|
||||
enum bpf_kptr_type {
|
||||
BPF_KPTR_UNREF,
|
||||
BPF_KPTR_REF,
|
||||
};
|
||||
|
||||
struct bpf_map_value_off_desc {
|
||||
u32 offset;
|
||||
enum bpf_kptr_type type;
|
||||
struct {
|
||||
struct btf *btf;
|
||||
struct module *module;
|
||||
btf_dtor_kfunc_t dtor;
|
||||
u32 btf_id;
|
||||
} kptr;
|
||||
};
|
||||
|
||||
struct bpf_map_value_off {
|
||||
u32 nr_off;
|
||||
struct bpf_map_value_off_desc off[];
|
||||
};
|
||||
|
||||
struct bpf_map_off_arr {
|
||||
u32 cnt;
|
||||
u32 field_off[BPF_MAP_OFF_ARR_MAX];
|
||||
u8 field_sz[BPF_MAP_OFF_ARR_MAX];
|
||||
};
|
||||
|
||||
struct bpf_map {
|
||||
/* The first two cachelines with read-mostly members of which some
|
||||
* are also accessed in fast-path (e.g. ops, max_entries).
|
||||
@ -171,6 +206,7 @@ struct bpf_map {
|
||||
u64 map_extra; /* any per-map-type extra fields */
|
||||
u32 map_flags;
|
||||
int spin_lock_off; /* >=0 valid offset, <0 error */
|
||||
struct bpf_map_value_off *kptr_off_tab;
|
||||
int timer_off; /* >=0 valid offset, <0 error */
|
||||
u32 id;
|
||||
int numa_node;
|
||||
@ -182,10 +218,7 @@ struct bpf_map {
|
||||
struct mem_cgroup *memcg;
|
||||
#endif
|
||||
char name[BPF_OBJ_NAME_LEN];
|
||||
bool bypass_spec_v1;
|
||||
bool frozen; /* write-once; write-protected by freeze_mutex */
|
||||
/* 14 bytes hole */
|
||||
|
||||
struct bpf_map_off_arr *off_arr;
|
||||
/* The 3rd and 4th cacheline with misc members to avoid false sharing
|
||||
* particularly with refcounting.
|
||||
*/
|
||||
@ -205,6 +238,8 @@ struct bpf_map {
|
||||
bool jited;
|
||||
bool xdp_has_frags;
|
||||
} owner;
|
||||
bool bypass_spec_v1;
|
||||
bool frozen; /* write-once; write-protected by freeze_mutex */
|
||||
};
|
||||
|
||||
static inline bool map_value_has_spin_lock(const struct bpf_map *map)
|
||||
@ -217,43 +252,44 @@ static inline bool map_value_has_timer(const struct bpf_map *map)
|
||||
return map->timer_off >= 0;
|
||||
}
|
||||
|
||||
static inline bool map_value_has_kptrs(const struct bpf_map *map)
|
||||
{
|
||||
return !IS_ERR_OR_NULL(map->kptr_off_tab);
|
||||
}
|
||||
|
||||
static inline void check_and_init_map_value(struct bpf_map *map, void *dst)
|
||||
{
|
||||
if (unlikely(map_value_has_spin_lock(map)))
|
||||
memset(dst + map->spin_lock_off, 0, sizeof(struct bpf_spin_lock));
|
||||
if (unlikely(map_value_has_timer(map)))
|
||||
memset(dst + map->timer_off, 0, sizeof(struct bpf_timer));
|
||||
if (unlikely(map_value_has_kptrs(map))) {
|
||||
struct bpf_map_value_off *tab = map->kptr_off_tab;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tab->nr_off; i++)
|
||||
*(u64 *)(dst + tab->off[i].offset) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy everything but bpf_spin_lock and bpf_timer. There could be one of each. */
|
||||
static inline void copy_map_value(struct bpf_map *map, void *dst, void *src)
|
||||
{
|
||||
u32 s_off = 0, s_sz = 0, t_off = 0, t_sz = 0;
|
||||
u32 curr_off = 0;
|
||||
int i;
|
||||
|
||||
if (unlikely(map_value_has_spin_lock(map))) {
|
||||
s_off = map->spin_lock_off;
|
||||
s_sz = sizeof(struct bpf_spin_lock);
|
||||
}
|
||||
if (unlikely(map_value_has_timer(map))) {
|
||||
t_off = map->timer_off;
|
||||
t_sz = sizeof(struct bpf_timer);
|
||||
}
|
||||
|
||||
if (unlikely(s_sz || t_sz)) {
|
||||
if (s_off < t_off || !s_sz) {
|
||||
swap(s_off, t_off);
|
||||
swap(s_sz, t_sz);
|
||||
}
|
||||
memcpy(dst, src, t_off);
|
||||
memcpy(dst + t_off + t_sz,
|
||||
src + t_off + t_sz,
|
||||
s_off - t_off - t_sz);
|
||||
memcpy(dst + s_off + s_sz,
|
||||
src + s_off + s_sz,
|
||||
map->value_size - s_off - s_sz);
|
||||
} else {
|
||||
if (likely(!map->off_arr)) {
|
||||
memcpy(dst, src, map->value_size);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < map->off_arr->cnt; i++) {
|
||||
u32 next_off = map->off_arr->field_off[i];
|
||||
|
||||
memcpy(dst + curr_off, src + curr_off, next_off - curr_off);
|
||||
curr_off += map->off_arr->field_sz[i];
|
||||
}
|
||||
memcpy(dst + curr_off, src + curr_off, map->value_size - curr_off);
|
||||
}
|
||||
void copy_map_value_locked(struct bpf_map *map, void *dst, void *src,
|
||||
bool lock_src);
|
||||
@ -342,7 +378,18 @@ enum bpf_type_flag {
|
||||
*/
|
||||
MEM_PERCPU = BIT(4 + BPF_BASE_TYPE_BITS),
|
||||
|
||||
__BPF_TYPE_LAST_FLAG = MEM_PERCPU,
|
||||
/* Indicates that the argument will be released. */
|
||||
OBJ_RELEASE = BIT(5 + BPF_BASE_TYPE_BITS),
|
||||
|
||||
/* PTR is not trusted. This is only used with PTR_TO_BTF_ID, to mark
|
||||
* unreferenced and referenced kptr loaded from map value using a load
|
||||
* instruction, so that they can only be dereferenced but not escape the
|
||||
* BPF program into the kernel (i.e. cannot be passed as arguments to
|
||||
* kfunc or bpf helpers).
|
||||
*/
|
||||
PTR_UNTRUSTED = BIT(6 + BPF_BASE_TYPE_BITS),
|
||||
|
||||
__BPF_TYPE_LAST_FLAG = PTR_UNTRUSTED,
|
||||
};
|
||||
|
||||
/* Max number of base types. */
|
||||
@ -391,6 +438,7 @@ enum bpf_arg_type {
|
||||
ARG_PTR_TO_STACK, /* pointer to stack */
|
||||
ARG_PTR_TO_CONST_STR, /* pointer to a null terminated read-only string */
|
||||
ARG_PTR_TO_TIMER, /* pointer to bpf_timer */
|
||||
ARG_PTR_TO_KPTR, /* pointer to referenced kptr */
|
||||
__BPF_ARG_TYPE_MAX,
|
||||
|
||||
/* Extended arg_types. */
|
||||
@ -400,6 +448,7 @@ enum bpf_arg_type {
|
||||
ARG_PTR_TO_SOCKET_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_SOCKET,
|
||||
ARG_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_ALLOC_MEM,
|
||||
ARG_PTR_TO_STACK_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_STACK,
|
||||
ARG_PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | ARG_PTR_TO_BTF_ID,
|
||||
|
||||
/* This must be the last entry. Its purpose is to ensure the enum is
|
||||
* wide enough to hold the higher bits reserved for bpf_type_flag.
|
||||
@ -1221,7 +1270,7 @@ u64 bpf_event_output(struct bpf_map *map, u64 flags, void *meta, u64 meta_size,
|
||||
/* an array of programs to be executed under rcu_lock.
|
||||
*
|
||||
* Typical usage:
|
||||
* ret = BPF_PROG_RUN_ARRAY(&bpf_prog_array, ctx, bpf_prog_run);
|
||||
* ret = bpf_prog_run_array(rcu_dereference(&bpf_prog_array), ctx, bpf_prog_run);
|
||||
*
|
||||
* the structure returned by bpf_prog_array_alloc() should be populated
|
||||
* with program pointers and the last pointer must be NULL.
|
||||
@ -1315,83 +1364,22 @@ static inline void bpf_reset_run_ctx(struct bpf_run_ctx *old_ctx)
|
||||
|
||||
typedef u32 (*bpf_prog_run_fn)(const struct bpf_prog *prog, const void *ctx);
|
||||
|
||||
static __always_inline int
|
||||
BPF_PROG_RUN_ARRAY_CG_FLAGS(const struct bpf_prog_array __rcu *array_rcu,
|
||||
const void *ctx, bpf_prog_run_fn run_prog,
|
||||
int retval, u32 *ret_flags)
|
||||
{
|
||||
const struct bpf_prog_array_item *item;
|
||||
const struct bpf_prog *prog;
|
||||
const struct bpf_prog_array *array;
|
||||
struct bpf_run_ctx *old_run_ctx;
|
||||
struct bpf_cg_run_ctx run_ctx;
|
||||
u32 func_ret;
|
||||
|
||||
run_ctx.retval = retval;
|
||||
migrate_disable();
|
||||
rcu_read_lock();
|
||||
array = rcu_dereference(array_rcu);
|
||||
item = &array->items[0];
|
||||
old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx);
|
||||
while ((prog = READ_ONCE(item->prog))) {
|
||||
run_ctx.prog_item = item;
|
||||
func_ret = run_prog(prog, ctx);
|
||||
if (!(func_ret & 1) && !IS_ERR_VALUE((long)run_ctx.retval))
|
||||
run_ctx.retval = -EPERM;
|
||||
*(ret_flags) |= (func_ret >> 1);
|
||||
item++;
|
||||
}
|
||||
bpf_reset_run_ctx(old_run_ctx);
|
||||
rcu_read_unlock();
|
||||
migrate_enable();
|
||||
return run_ctx.retval;
|
||||
}
|
||||
|
||||
static __always_inline int
|
||||
BPF_PROG_RUN_ARRAY_CG(const struct bpf_prog_array __rcu *array_rcu,
|
||||
const void *ctx, bpf_prog_run_fn run_prog,
|
||||
int retval)
|
||||
{
|
||||
const struct bpf_prog_array_item *item;
|
||||
const struct bpf_prog *prog;
|
||||
const struct bpf_prog_array *array;
|
||||
struct bpf_run_ctx *old_run_ctx;
|
||||
struct bpf_cg_run_ctx run_ctx;
|
||||
|
||||
run_ctx.retval = retval;
|
||||
migrate_disable();
|
||||
rcu_read_lock();
|
||||
array = rcu_dereference(array_rcu);
|
||||
item = &array->items[0];
|
||||
old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx);
|
||||
while ((prog = READ_ONCE(item->prog))) {
|
||||
run_ctx.prog_item = item;
|
||||
if (!run_prog(prog, ctx) && !IS_ERR_VALUE((long)run_ctx.retval))
|
||||
run_ctx.retval = -EPERM;
|
||||
item++;
|
||||
}
|
||||
bpf_reset_run_ctx(old_run_ctx);
|
||||
rcu_read_unlock();
|
||||
migrate_enable();
|
||||
return run_ctx.retval;
|
||||
}
|
||||
|
||||
static __always_inline u32
|
||||
BPF_PROG_RUN_ARRAY(const struct bpf_prog_array __rcu *array_rcu,
|
||||
bpf_prog_run_array(const struct bpf_prog_array *array,
|
||||
const void *ctx, bpf_prog_run_fn run_prog)
|
||||
{
|
||||
const struct bpf_prog_array_item *item;
|
||||
const struct bpf_prog *prog;
|
||||
const struct bpf_prog_array *array;
|
||||
struct bpf_run_ctx *old_run_ctx;
|
||||
struct bpf_trace_run_ctx run_ctx;
|
||||
u32 ret = 1;
|
||||
|
||||
migrate_disable();
|
||||
rcu_read_lock();
|
||||
array = rcu_dereference(array_rcu);
|
||||
RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "no rcu lock held");
|
||||
|
||||
if (unlikely(!array))
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
migrate_disable();
|
||||
old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx);
|
||||
item = &array->items[0];
|
||||
while ((prog = READ_ONCE(item->prog))) {
|
||||
@ -1400,50 +1388,10 @@ BPF_PROG_RUN_ARRAY(const struct bpf_prog_array __rcu *array_rcu,
|
||||
item++;
|
||||
}
|
||||
bpf_reset_run_ctx(old_run_ctx);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
migrate_enable();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* To be used by __cgroup_bpf_run_filter_skb for EGRESS BPF progs
|
||||
* so BPF programs can request cwr for TCP packets.
|
||||
*
|
||||
* Current cgroup skb programs can only return 0 or 1 (0 to drop the
|
||||
* packet. This macro changes the behavior so the low order bit
|
||||
* indicates whether the packet should be dropped (0) or not (1)
|
||||
* and the next bit is a congestion notification bit. This could be
|
||||
* used by TCP to call tcp_enter_cwr()
|
||||
*
|
||||
* Hence, new allowed return values of CGROUP EGRESS BPF programs are:
|
||||
* 0: drop packet
|
||||
* 1: keep packet
|
||||
* 2: drop packet and cn
|
||||
* 3: keep packet and cn
|
||||
*
|
||||
* This macro then converts it to one of the NET_XMIT or an error
|
||||
* code that is then interpreted as drop packet (and no cn):
|
||||
* 0: NET_XMIT_SUCCESS skb should be transmitted
|
||||
* 1: NET_XMIT_DROP skb should be dropped and cn
|
||||
* 2: NET_XMIT_CN skb should be transmitted and cn
|
||||
* 3: -err skb should be dropped
|
||||
*/
|
||||
#define BPF_PROG_CGROUP_INET_EGRESS_RUN_ARRAY(array, ctx, func) \
|
||||
({ \
|
||||
u32 _flags = 0; \
|
||||
bool _cn; \
|
||||
u32 _ret; \
|
||||
_ret = BPF_PROG_RUN_ARRAY_CG_FLAGS(array, ctx, func, 0, &_flags); \
|
||||
_cn = _flags & BPF_RET_SET_CN; \
|
||||
if (_ret && !IS_ERR_VALUE((long)_ret)) \
|
||||
_ret = -EFAULT; \
|
||||
if (!_ret) \
|
||||
_ret = (_cn ? NET_XMIT_CN : NET_XMIT_SUCCESS); \
|
||||
else \
|
||||
_ret = (_cn ? NET_XMIT_DROP : _ret); \
|
||||
_ret; \
|
||||
})
|
||||
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
DECLARE_PER_CPU(int, bpf_prog_active);
|
||||
extern struct mutex bpf_stats_enabled_mutex;
|
||||
@ -1497,6 +1445,12 @@ void bpf_prog_put(struct bpf_prog *prog);
|
||||
void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock);
|
||||
void bpf_map_free_id(struct bpf_map *map, bool do_idr_lock);
|
||||
|
||||
struct bpf_map_value_off_desc *bpf_map_kptr_off_contains(struct bpf_map *map, u32 offset);
|
||||
void bpf_map_free_kptr_off_tab(struct bpf_map *map);
|
||||
struct bpf_map_value_off *bpf_map_copy_kptr_off_tab(const struct bpf_map *map);
|
||||
bool bpf_map_equal_kptr_off_tab(const struct bpf_map *map_a, const struct bpf_map *map_b);
|
||||
void bpf_map_free_kptrs(struct bpf_map *map, void *map_value);
|
||||
|
||||
struct bpf_map *bpf_map_get(u32 ufd);
|
||||
struct bpf_map *bpf_map_get_with_uref(u32 ufd);
|
||||
struct bpf_map *__bpf_map_get(struct fd f);
|
||||
@ -1793,7 +1747,8 @@ int btf_struct_access(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
u32 *next_btf_id, enum bpf_type_flag *flag);
|
||||
bool btf_struct_ids_match(struct bpf_verifier_log *log,
|
||||
const struct btf *btf, u32 id, int off,
|
||||
const struct btf *need_btf, u32 need_type_id);
|
||||
const struct btf *need_btf, u32 need_type_id,
|
||||
bool strict);
|
||||
|
||||
int btf_distill_func_proto(struct bpf_verifier_log *log,
|
||||
struct btf *btf,
|
||||
|
@ -143,9 +143,9 @@ void bpf_selem_link_storage_nolock(struct bpf_local_storage *local_storage,
|
||||
|
||||
bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage,
|
||||
struct bpf_local_storage_elem *selem,
|
||||
bool uncharge_omem);
|
||||
bool uncharge_omem, bool use_trace_rcu);
|
||||
|
||||
void bpf_selem_unlink(struct bpf_local_storage_elem *selem);
|
||||
void bpf_selem_unlink(struct bpf_local_storage_elem *selem, bool use_trace_rcu);
|
||||
|
||||
void bpf_selem_link_map(struct bpf_local_storage_map *smap,
|
||||
struct bpf_local_storage_elem *selem);
|
||||
|
@ -523,8 +523,7 @@ int check_ptr_off_reg(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg, int regno);
|
||||
int check_func_arg_reg_off(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg, int regno,
|
||||
enum bpf_arg_type arg_type,
|
||||
bool is_release_func);
|
||||
enum bpf_arg_type arg_type);
|
||||
int check_kfunc_mem_size_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
|
||||
u32 regno);
|
||||
int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
|
||||
|
@ -17,6 +17,7 @@ enum btf_kfunc_type {
|
||||
BTF_KFUNC_TYPE_ACQUIRE,
|
||||
BTF_KFUNC_TYPE_RELEASE,
|
||||
BTF_KFUNC_TYPE_RET_NULL,
|
||||
BTF_KFUNC_TYPE_KPTR_ACQUIRE,
|
||||
BTF_KFUNC_TYPE_MAX,
|
||||
};
|
||||
|
||||
@ -35,11 +36,19 @@ struct btf_kfunc_id_set {
|
||||
struct btf_id_set *acquire_set;
|
||||
struct btf_id_set *release_set;
|
||||
struct btf_id_set *ret_null_set;
|
||||
struct btf_id_set *kptr_acquire_set;
|
||||
};
|
||||
struct btf_id_set *sets[BTF_KFUNC_TYPE_MAX];
|
||||
};
|
||||
};
|
||||
|
||||
struct btf_id_dtor_kfunc {
|
||||
u32 btf_id;
|
||||
u32 kfunc_btf_id;
|
||||
};
|
||||
|
||||
typedef void (*btf_dtor_kfunc_t)(void *);
|
||||
|
||||
extern const struct file_operations btf_fops;
|
||||
|
||||
void btf_get(struct btf *btf);
|
||||
@ -123,6 +132,8 @@ bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
|
||||
u32 expected_offset, u32 expected_size);
|
||||
int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t);
|
||||
int btf_find_timer(const struct btf *btf, const struct btf_type *t);
|
||||
struct bpf_map_value_off *btf_parse_kptrs(const struct btf *btf,
|
||||
const struct btf_type *t);
|
||||
bool btf_type_is_void(const struct btf_type *t);
|
||||
s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind);
|
||||
const struct btf_type *btf_type_skip_modifiers(const struct btf *btf,
|
||||
@ -344,6 +355,9 @@ bool btf_kfunc_id_set_contains(const struct btf *btf,
|
||||
enum btf_kfunc_type type, u32 kfunc_btf_id);
|
||||
int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
|
||||
const struct btf_kfunc_id_set *s);
|
||||
s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id);
|
||||
int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
|
||||
struct module *owner);
|
||||
#else
|
||||
static inline const struct btf_type *btf_type_by_id(const struct btf *btf,
|
||||
u32 type_id)
|
||||
@ -367,6 +381,15 @@ static inline int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
static inline int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors,
|
||||
u32 add_cnt, struct module *owner)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -3925,7 +3925,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features);
|
||||
struct sk_buff *skb_segment_list(struct sk_buff *skb, netdev_features_t features,
|
||||
unsigned int offset);
|
||||
struct sk_buff *skb_vlan_untag(struct sk_buff *skb);
|
||||
int skb_ensure_writable(struct sk_buff *skb, int write_len);
|
||||
int skb_ensure_writable(struct sk_buff *skb, unsigned int write_len);
|
||||
int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci);
|
||||
int skb_vlan_pop(struct sk_buff *skb);
|
||||
int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci);
|
||||
|
@ -5143,6 +5143,17 @@ union bpf_attr {
|
||||
* The **hash_algo** is returned on success,
|
||||
* **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if
|
||||
* invalid arguments are passed.
|
||||
*
|
||||
* void *bpf_kptr_xchg(void *map_value, void *ptr)
|
||||
* Description
|
||||
* Exchange kptr at pointer *map_value* with *ptr*, and return the
|
||||
* old value. *ptr* can be NULL, otherwise it must be a referenced
|
||||
* pointer which will be released when this helper is called.
|
||||
* Return
|
||||
* The old value of kptr (which can be NULL). The returned pointer
|
||||
* if not NULL, is a reference which must be released using its
|
||||
* corresponding release function, or moved into a BPF map before
|
||||
* program exit.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
@ -5339,6 +5350,7 @@ union bpf_attr {
|
||||
FN(copy_from_user_task), \
|
||||
FN(skb_set_tstamp), \
|
||||
FN(ima_file_hash), \
|
||||
FN(kptr_xchg), \
|
||||
/* */
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/perf_event.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/rcupdate_trace.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
#include "map_in_map.h"
|
||||
|
||||
@ -287,10 +288,12 @@ static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_and_free_timer_in_array(struct bpf_array *arr, void *val)
|
||||
static void check_and_free_fields(struct bpf_array *arr, void *val)
|
||||
{
|
||||
if (unlikely(map_value_has_timer(&arr->map)))
|
||||
if (map_value_has_timer(&arr->map))
|
||||
bpf_timer_cancel_and_free(val + arr->map.timer_off);
|
||||
if (map_value_has_kptrs(&arr->map))
|
||||
bpf_map_free_kptrs(&arr->map, val);
|
||||
}
|
||||
|
||||
/* Called from syscall or from eBPF program */
|
||||
@ -327,7 +330,7 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
copy_map_value_locked(map, val, value, false);
|
||||
else
|
||||
copy_map_value(map, val, value);
|
||||
check_and_free_timer_in_array(array, val);
|
||||
check_and_free_fields(array, val);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -386,7 +389,8 @@ static void array_map_free_timers(struct bpf_map *map)
|
||||
struct bpf_array *array = container_of(map, struct bpf_array, map);
|
||||
int i;
|
||||
|
||||
if (likely(!map_value_has_timer(map)))
|
||||
/* We don't reset or free kptr on uref dropping to zero. */
|
||||
if (!map_value_has_timer(map))
|
||||
return;
|
||||
|
||||
for (i = 0; i < array->map.max_entries; i++)
|
||||
@ -398,6 +402,13 @@ static void array_map_free_timers(struct bpf_map *map)
|
||||
static void array_map_free(struct bpf_map *map)
|
||||
{
|
||||
struct bpf_array *array = container_of(map, struct bpf_array, map);
|
||||
int i;
|
||||
|
||||
if (map_value_has_kptrs(map)) {
|
||||
for (i = 0; i < array->map.max_entries; i++)
|
||||
bpf_map_free_kptrs(map, array->value + array->elem_size * i);
|
||||
bpf_map_free_kptr_off_tab(map);
|
||||
}
|
||||
|
||||
if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY)
|
||||
bpf_array_free_percpu(array);
|
||||
@ -680,7 +691,7 @@ static int bpf_for_each_array_elem(struct bpf_map *map, bpf_callback_t callback_
|
||||
return num_elems;
|
||||
}
|
||||
|
||||
static int array_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(array_map_btf_ids, struct, bpf_array)
|
||||
const struct bpf_map_ops array_map_ops = {
|
||||
.map_meta_equal = array_map_meta_equal,
|
||||
.map_alloc_check = array_map_alloc_check,
|
||||
@ -701,12 +712,10 @@ const struct bpf_map_ops array_map_ops = {
|
||||
.map_update_batch = generic_map_update_batch,
|
||||
.map_set_for_each_callback_args = map_set_for_each_callback_args,
|
||||
.map_for_each_callback = bpf_for_each_array_elem,
|
||||
.map_btf_name = "bpf_array",
|
||||
.map_btf_id = &array_map_btf_id,
|
||||
.map_btf_id = &array_map_btf_ids[0],
|
||||
.iter_seq_info = &iter_seq_info,
|
||||
};
|
||||
|
||||
static int percpu_array_map_btf_id;
|
||||
const struct bpf_map_ops percpu_array_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = array_map_alloc_check,
|
||||
@ -722,8 +731,7 @@ const struct bpf_map_ops percpu_array_map_ops = {
|
||||
.map_update_batch = generic_map_update_batch,
|
||||
.map_set_for_each_callback_args = map_set_for_each_callback_args,
|
||||
.map_for_each_callback = bpf_for_each_array_elem,
|
||||
.map_btf_name = "bpf_array",
|
||||
.map_btf_id = &percpu_array_map_btf_id,
|
||||
.map_btf_id = &array_map_btf_ids[0],
|
||||
.iter_seq_info = &iter_seq_info,
|
||||
};
|
||||
|
||||
@ -1102,7 +1110,6 @@ static void prog_array_map_free(struct bpf_map *map)
|
||||
* Thus, prog_array_map cannot be used as an inner_map
|
||||
* and map_meta_equal is not implemented.
|
||||
*/
|
||||
static int prog_array_map_btf_id;
|
||||
const struct bpf_map_ops prog_array_map_ops = {
|
||||
.map_alloc_check = fd_array_map_alloc_check,
|
||||
.map_alloc = prog_array_map_alloc,
|
||||
@ -1118,8 +1125,7 @@ const struct bpf_map_ops prog_array_map_ops = {
|
||||
.map_fd_sys_lookup_elem = prog_fd_array_sys_lookup_elem,
|
||||
.map_release_uref = prog_array_map_clear,
|
||||
.map_seq_show_elem = prog_array_map_seq_show_elem,
|
||||
.map_btf_name = "bpf_array",
|
||||
.map_btf_id = &prog_array_map_btf_id,
|
||||
.map_btf_id = &array_map_btf_ids[0],
|
||||
};
|
||||
|
||||
static struct bpf_event_entry *bpf_event_entry_gen(struct file *perf_file,
|
||||
@ -1208,7 +1214,6 @@ static void perf_event_fd_array_map_free(struct bpf_map *map)
|
||||
fd_array_map_free(map);
|
||||
}
|
||||
|
||||
static int perf_event_array_map_btf_id;
|
||||
const struct bpf_map_ops perf_event_array_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = fd_array_map_alloc_check,
|
||||
@ -1221,8 +1226,7 @@ const struct bpf_map_ops perf_event_array_map_ops = {
|
||||
.map_fd_put_ptr = perf_event_fd_array_put_ptr,
|
||||
.map_release = perf_event_fd_array_release,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "bpf_array",
|
||||
.map_btf_id = &perf_event_array_map_btf_id,
|
||||
.map_btf_id = &array_map_btf_ids[0],
|
||||
};
|
||||
|
||||
#ifdef CONFIG_CGROUPS
|
||||
@ -1245,7 +1249,6 @@ static void cgroup_fd_array_free(struct bpf_map *map)
|
||||
fd_array_map_free(map);
|
||||
}
|
||||
|
||||
static int cgroup_array_map_btf_id;
|
||||
const struct bpf_map_ops cgroup_array_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = fd_array_map_alloc_check,
|
||||
@ -1257,8 +1260,7 @@ const struct bpf_map_ops cgroup_array_map_ops = {
|
||||
.map_fd_get_ptr = cgroup_fd_array_get_ptr,
|
||||
.map_fd_put_ptr = cgroup_fd_array_put_ptr,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "bpf_array",
|
||||
.map_btf_id = &cgroup_array_map_btf_id,
|
||||
.map_btf_id = &array_map_btf_ids[0],
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -1332,7 +1334,6 @@ static int array_of_map_gen_lookup(struct bpf_map *map,
|
||||
return insn - insn_buf;
|
||||
}
|
||||
|
||||
static int array_of_maps_map_btf_id;
|
||||
const struct bpf_map_ops array_of_maps_map_ops = {
|
||||
.map_alloc_check = fd_array_map_alloc_check,
|
||||
.map_alloc = array_of_map_alloc,
|
||||
@ -1345,6 +1346,5 @@ const struct bpf_map_ops array_of_maps_map_ops = {
|
||||
.map_fd_sys_lookup_elem = bpf_map_fd_sys_lookup_elem,
|
||||
.map_gen_lookup = array_of_map_gen_lookup,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "bpf_array",
|
||||
.map_btf_id = &array_of_maps_map_btf_id,
|
||||
.map_btf_id = &array_map_btf_ids[0],
|
||||
};
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
#define BLOOM_CREATE_FLAG_MASK \
|
||||
(BPF_F_NUMA_NODE | BPF_F_ZERO_SEED | BPF_F_ACCESS_MASK)
|
||||
@ -192,7 +193,7 @@ static int bloom_map_check_btf(const struct bpf_map *map,
|
||||
return btf_type_is_void(key_type) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static int bpf_bloom_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(bpf_bloom_map_btf_ids, struct, bpf_bloom_filter)
|
||||
const struct bpf_map_ops bloom_filter_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = bloom_map_alloc,
|
||||
@ -205,6 +206,5 @@ const struct bpf_map_ops bloom_filter_map_ops = {
|
||||
.map_update_elem = bloom_map_update_elem,
|
||||
.map_delete_elem = bloom_map_delete_elem,
|
||||
.map_check_btf = bloom_map_check_btf,
|
||||
.map_btf_name = "bpf_bloom_filter",
|
||||
.map_btf_id = &bpf_bloom_map_btf_id,
|
||||
.map_btf_id = &bpf_bloom_map_btf_ids[0],
|
||||
};
|
||||
|
@ -90,7 +90,7 @@ void bpf_inode_storage_free(struct inode *inode)
|
||||
*/
|
||||
bpf_selem_unlink_map(selem);
|
||||
free_inode_storage = bpf_selem_unlink_storage_nolock(
|
||||
local_storage, selem, false);
|
||||
local_storage, selem, false, false);
|
||||
}
|
||||
raw_spin_unlock_bh(&local_storage->lock);
|
||||
rcu_read_unlock();
|
||||
@ -149,7 +149,7 @@ static int inode_storage_delete(struct inode *inode, struct bpf_map *map)
|
||||
if (!sdata)
|
||||
return -ENOENT;
|
||||
|
||||
bpf_selem_unlink(SELEM(sdata));
|
||||
bpf_selem_unlink(SELEM(sdata), true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -245,7 +245,8 @@ static void inode_storage_map_free(struct bpf_map *map)
|
||||
bpf_local_storage_map_free(smap, NULL);
|
||||
}
|
||||
|
||||
static int inode_storage_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(inode_storage_map_btf_ids, struct,
|
||||
bpf_local_storage_map)
|
||||
const struct bpf_map_ops inode_storage_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = bpf_local_storage_map_alloc_check,
|
||||
@ -256,8 +257,7 @@ const struct bpf_map_ops inode_storage_map_ops = {
|
||||
.map_update_elem = bpf_fd_inode_storage_update_elem,
|
||||
.map_delete_elem = bpf_fd_inode_storage_delete_elem,
|
||||
.map_check_btf = bpf_local_storage_map_check_btf,
|
||||
.map_btf_name = "bpf_local_storage_map",
|
||||
.map_btf_id = &inode_storage_map_btf_id,
|
||||
.map_btf_id = &inode_storage_map_btf_ids[0],
|
||||
.map_owner_storage_ptr = inode_storage_ptr,
|
||||
};
|
||||
|
||||
|
@ -545,7 +545,7 @@ int bpf_iter_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
|
||||
bpf_link_init(&link->link, BPF_LINK_TYPE_ITER, &bpf_iter_link_lops, prog);
|
||||
link->tinfo = tinfo;
|
||||
|
||||
err = bpf_link_prime(&link->link, &link_primer);
|
||||
err = bpf_link_prime(&link->link, &link_primer);
|
||||
if (err) {
|
||||
kfree(link);
|
||||
return err;
|
||||
|
@ -106,7 +106,7 @@ static void bpf_selem_free_rcu(struct rcu_head *rcu)
|
||||
*/
|
||||
bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage,
|
||||
struct bpf_local_storage_elem *selem,
|
||||
bool uncharge_mem)
|
||||
bool uncharge_mem, bool use_trace_rcu)
|
||||
{
|
||||
struct bpf_local_storage_map *smap;
|
||||
bool free_local_storage;
|
||||
@ -150,11 +150,16 @@ bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage,
|
||||
SDATA(selem))
|
||||
RCU_INIT_POINTER(local_storage->cache[smap->cache_idx], NULL);
|
||||
|
||||
call_rcu_tasks_trace(&selem->rcu, bpf_selem_free_rcu);
|
||||
if (use_trace_rcu)
|
||||
call_rcu_tasks_trace(&selem->rcu, bpf_selem_free_rcu);
|
||||
else
|
||||
kfree_rcu(selem, rcu);
|
||||
|
||||
return free_local_storage;
|
||||
}
|
||||
|
||||
static void __bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem)
|
||||
static void __bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem,
|
||||
bool use_trace_rcu)
|
||||
{
|
||||
struct bpf_local_storage *local_storage;
|
||||
bool free_local_storage = false;
|
||||
@ -169,12 +174,16 @@ static void __bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem)
|
||||
raw_spin_lock_irqsave(&local_storage->lock, flags);
|
||||
if (likely(selem_linked_to_storage(selem)))
|
||||
free_local_storage = bpf_selem_unlink_storage_nolock(
|
||||
local_storage, selem, true);
|
||||
local_storage, selem, true, use_trace_rcu);
|
||||
raw_spin_unlock_irqrestore(&local_storage->lock, flags);
|
||||
|
||||
if (free_local_storage)
|
||||
call_rcu_tasks_trace(&local_storage->rcu,
|
||||
if (free_local_storage) {
|
||||
if (use_trace_rcu)
|
||||
call_rcu_tasks_trace(&local_storage->rcu,
|
||||
bpf_local_storage_free_rcu);
|
||||
else
|
||||
kfree_rcu(local_storage, rcu);
|
||||
}
|
||||
}
|
||||
|
||||
void bpf_selem_link_storage_nolock(struct bpf_local_storage *local_storage,
|
||||
@ -214,14 +223,14 @@ void bpf_selem_link_map(struct bpf_local_storage_map *smap,
|
||||
raw_spin_unlock_irqrestore(&b->lock, flags);
|
||||
}
|
||||
|
||||
void bpf_selem_unlink(struct bpf_local_storage_elem *selem)
|
||||
void bpf_selem_unlink(struct bpf_local_storage_elem *selem, bool use_trace_rcu)
|
||||
{
|
||||
/* Always unlink from map before unlinking from local_storage
|
||||
* because selem will be freed after successfully unlinked from
|
||||
* the local_storage.
|
||||
*/
|
||||
bpf_selem_unlink_map(selem);
|
||||
__bpf_selem_unlink_storage(selem);
|
||||
__bpf_selem_unlink_storage(selem, use_trace_rcu);
|
||||
}
|
||||
|
||||
struct bpf_local_storage_data *
|
||||
@ -466,7 +475,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap,
|
||||
if (old_sdata) {
|
||||
bpf_selem_unlink_map(SELEM(old_sdata));
|
||||
bpf_selem_unlink_storage_nolock(local_storage, SELEM(old_sdata),
|
||||
false);
|
||||
false, true);
|
||||
}
|
||||
|
||||
unlock:
|
||||
@ -548,7 +557,7 @@ void bpf_local_storage_map_free(struct bpf_local_storage_map *smap,
|
||||
migrate_disable();
|
||||
__this_cpu_inc(*busy_counter);
|
||||
}
|
||||
bpf_selem_unlink(selem);
|
||||
bpf_selem_unlink(selem, false);
|
||||
if (busy_counter) {
|
||||
__this_cpu_dec(*busy_counter);
|
||||
migrate_enable();
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
enum bpf_struct_ops_state {
|
||||
BPF_STRUCT_OPS_STATE_INIT,
|
||||
@ -263,7 +264,7 @@ int bpf_struct_ops_map_sys_lookup_elem(struct bpf_map *map, void *key,
|
||||
/* No lock is needed. state and refcnt do not need
|
||||
* to be updated together under atomic context.
|
||||
*/
|
||||
uvalue = (struct bpf_struct_ops_value *)value;
|
||||
uvalue = value;
|
||||
memcpy(uvalue, st_map->uvalue, map->value_size);
|
||||
uvalue->state = state;
|
||||
refcount_set(&uvalue->refcnt, refcount_read(&kvalue->refcnt));
|
||||
@ -353,7 +354,7 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
uvalue = (struct bpf_struct_ops_value *)value;
|
||||
uvalue = value;
|
||||
err = check_zero_holes(t, uvalue->data);
|
||||
if (err)
|
||||
return err;
|
||||
@ -612,7 +613,7 @@ static struct bpf_map *bpf_struct_ops_map_alloc(union bpf_attr *attr)
|
||||
return map;
|
||||
}
|
||||
|
||||
static int bpf_struct_ops_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(bpf_struct_ops_map_btf_ids, struct, bpf_struct_ops_map)
|
||||
const struct bpf_map_ops bpf_struct_ops_map_ops = {
|
||||
.map_alloc_check = bpf_struct_ops_map_alloc_check,
|
||||
.map_alloc = bpf_struct_ops_map_alloc,
|
||||
@ -622,8 +623,7 @@ const struct bpf_map_ops bpf_struct_ops_map_ops = {
|
||||
.map_delete_elem = bpf_struct_ops_map_delete_elem,
|
||||
.map_update_elem = bpf_struct_ops_map_update_elem,
|
||||
.map_seq_show_elem = bpf_struct_ops_map_seq_show_elem,
|
||||
.map_btf_name = "bpf_struct_ops_map",
|
||||
.map_btf_id = &bpf_struct_ops_map_btf_id,
|
||||
.map_btf_id = &bpf_struct_ops_map_btf_ids[0],
|
||||
};
|
||||
|
||||
/* "const void *" because some subsystem is
|
||||
|
@ -102,7 +102,7 @@ void bpf_task_storage_free(struct task_struct *task)
|
||||
*/
|
||||
bpf_selem_unlink_map(selem);
|
||||
free_task_storage = bpf_selem_unlink_storage_nolock(
|
||||
local_storage, selem, false);
|
||||
local_storage, selem, false, false);
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&local_storage->lock, flags);
|
||||
bpf_task_storage_unlock();
|
||||
@ -192,7 +192,7 @@ static int task_storage_delete(struct task_struct *task, struct bpf_map *map)
|
||||
if (!sdata)
|
||||
return -ENOENT;
|
||||
|
||||
bpf_selem_unlink(SELEM(sdata));
|
||||
bpf_selem_unlink(SELEM(sdata), true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -307,7 +307,7 @@ static void task_storage_map_free(struct bpf_map *map)
|
||||
bpf_local_storage_map_free(smap, &bpf_task_storage_busy);
|
||||
}
|
||||
|
||||
static int task_storage_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(task_storage_map_btf_ids, struct, bpf_local_storage_map)
|
||||
const struct bpf_map_ops task_storage_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = bpf_local_storage_map_alloc_check,
|
||||
@ -318,8 +318,7 @@ const struct bpf_map_ops task_storage_map_ops = {
|
||||
.map_update_elem = bpf_pid_task_storage_update_elem,
|
||||
.map_delete_elem = bpf_pid_task_storage_delete_elem,
|
||||
.map_check_btf = bpf_local_storage_map_check_btf,
|
||||
.map_btf_name = "bpf_local_storage_map",
|
||||
.map_btf_id = &task_storage_map_btf_id,
|
||||
.map_btf_id = &task_storage_map_btf_ids[0],
|
||||
.map_owner_storage_ptr = task_storage_ptr,
|
||||
};
|
||||
|
||||
|
634
kernel/bpf/btf.c
634
kernel/bpf/btf.c
@ -207,12 +207,18 @@ enum btf_kfunc_hook {
|
||||
|
||||
enum {
|
||||
BTF_KFUNC_SET_MAX_CNT = 32,
|
||||
BTF_DTOR_KFUNC_MAX_CNT = 256,
|
||||
};
|
||||
|
||||
struct btf_kfunc_set_tab {
|
||||
struct btf_id_set *sets[BTF_KFUNC_HOOK_MAX][BTF_KFUNC_TYPE_MAX];
|
||||
};
|
||||
|
||||
struct btf_id_dtor_kfunc_tab {
|
||||
u32 cnt;
|
||||
struct btf_id_dtor_kfunc dtors[];
|
||||
};
|
||||
|
||||
struct btf {
|
||||
void *data;
|
||||
struct btf_type **types;
|
||||
@ -228,6 +234,7 @@ struct btf {
|
||||
u32 id;
|
||||
struct rcu_head rcu;
|
||||
struct btf_kfunc_set_tab *kfunc_set_tab;
|
||||
struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab;
|
||||
|
||||
/* split BTF support */
|
||||
struct btf *base_btf;
|
||||
@ -1616,8 +1623,19 @@ free_tab:
|
||||
btf->kfunc_set_tab = NULL;
|
||||
}
|
||||
|
||||
static void btf_free_dtor_kfunc_tab(struct btf *btf)
|
||||
{
|
||||
struct btf_id_dtor_kfunc_tab *tab = btf->dtor_kfunc_tab;
|
||||
|
||||
if (!tab)
|
||||
return;
|
||||
kfree(tab);
|
||||
btf->dtor_kfunc_tab = NULL;
|
||||
}
|
||||
|
||||
static void btf_free(struct btf *btf)
|
||||
{
|
||||
btf_free_dtor_kfunc_tab(btf);
|
||||
btf_free_kfunc_set_tab(btf);
|
||||
kvfree(btf->types);
|
||||
kvfree(btf->resolved_sizes);
|
||||
@ -3163,24 +3181,86 @@ static void btf_struct_log(struct btf_verifier_env *env,
|
||||
btf_verifier_log(env, "size=%u vlen=%u", t->size, btf_type_vlen(t));
|
||||
}
|
||||
|
||||
enum btf_field_type {
|
||||
BTF_FIELD_SPIN_LOCK,
|
||||
BTF_FIELD_TIMER,
|
||||
BTF_FIELD_KPTR,
|
||||
};
|
||||
|
||||
enum {
|
||||
BTF_FIELD_IGNORE = 0,
|
||||
BTF_FIELD_FOUND = 1,
|
||||
};
|
||||
|
||||
struct btf_field_info {
|
||||
u32 type_id;
|
||||
u32 off;
|
||||
enum bpf_kptr_type type;
|
||||
};
|
||||
|
||||
static int btf_find_struct(const struct btf *btf, const struct btf_type *t,
|
||||
u32 off, int sz, struct btf_field_info *info)
|
||||
{
|
||||
if (!__btf_type_is_struct(t))
|
||||
return BTF_FIELD_IGNORE;
|
||||
if (t->size != sz)
|
||||
return BTF_FIELD_IGNORE;
|
||||
info->off = off;
|
||||
return BTF_FIELD_FOUND;
|
||||
}
|
||||
|
||||
static int btf_find_kptr(const struct btf *btf, const struct btf_type *t,
|
||||
u32 off, int sz, struct btf_field_info *info)
|
||||
{
|
||||
enum bpf_kptr_type type;
|
||||
u32 res_id;
|
||||
|
||||
/* For PTR, sz is always == 8 */
|
||||
if (!btf_type_is_ptr(t))
|
||||
return BTF_FIELD_IGNORE;
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
|
||||
if (!btf_type_is_type_tag(t))
|
||||
return BTF_FIELD_IGNORE;
|
||||
/* Reject extra tags */
|
||||
if (btf_type_is_type_tag(btf_type_by_id(btf, t->type)))
|
||||
return -EINVAL;
|
||||
if (!strcmp("kptr", __btf_name_by_offset(btf, t->name_off)))
|
||||
type = BPF_KPTR_UNREF;
|
||||
else if (!strcmp("kptr_ref", __btf_name_by_offset(btf, t->name_off)))
|
||||
type = BPF_KPTR_REF;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* Get the base type */
|
||||
t = btf_type_skip_modifiers(btf, t->type, &res_id);
|
||||
/* Only pointer to struct is allowed */
|
||||
if (!__btf_type_is_struct(t))
|
||||
return -EINVAL;
|
||||
|
||||
info->type_id = res_id;
|
||||
info->off = off;
|
||||
info->type = type;
|
||||
return BTF_FIELD_FOUND;
|
||||
}
|
||||
|
||||
static int btf_find_struct_field(const struct btf *btf, const struct btf_type *t,
|
||||
const char *name, int sz, int align)
|
||||
const char *name, int sz, int align,
|
||||
enum btf_field_type field_type,
|
||||
struct btf_field_info *info, int info_cnt)
|
||||
{
|
||||
const struct btf_member *member;
|
||||
u32 i, off = -ENOENT;
|
||||
struct btf_field_info tmp;
|
||||
int ret, idx = 0;
|
||||
u32 i, off;
|
||||
|
||||
for_each_member(i, t, member) {
|
||||
const struct btf_type *member_type = btf_type_by_id(btf,
|
||||
member->type);
|
||||
if (!__btf_type_is_struct(member_type))
|
||||
|
||||
if (name && strcmp(__btf_name_by_offset(btf, member_type->name_off), name))
|
||||
continue;
|
||||
if (member_type->size != sz)
|
||||
continue;
|
||||
if (strcmp(__btf_name_by_offset(btf, member_type->name_off), name))
|
||||
continue;
|
||||
if (off != -ENOENT)
|
||||
/* only one such field is allowed */
|
||||
return -E2BIG;
|
||||
|
||||
off = __btf_member_bit_offset(t, member);
|
||||
if (off % 8)
|
||||
/* valid C code cannot generate such BTF */
|
||||
@ -3188,46 +3268,115 @@ static int btf_find_struct_field(const struct btf *btf, const struct btf_type *t
|
||||
off /= 8;
|
||||
if (off % align)
|
||||
return -EINVAL;
|
||||
|
||||
switch (field_type) {
|
||||
case BTF_FIELD_SPIN_LOCK:
|
||||
case BTF_FIELD_TIMER:
|
||||
ret = btf_find_struct(btf, member_type, off, sz,
|
||||
idx < info_cnt ? &info[idx] : &tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
case BTF_FIELD_KPTR:
|
||||
ret = btf_find_kptr(btf, member_type, off, sz,
|
||||
idx < info_cnt ? &info[idx] : &tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (ret == BTF_FIELD_IGNORE)
|
||||
continue;
|
||||
if (idx >= info_cnt)
|
||||
return -E2BIG;
|
||||
++idx;
|
||||
}
|
||||
return off;
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t,
|
||||
const char *name, int sz, int align)
|
||||
const char *name, int sz, int align,
|
||||
enum btf_field_type field_type,
|
||||
struct btf_field_info *info, int info_cnt)
|
||||
{
|
||||
const struct btf_var_secinfo *vsi;
|
||||
u32 i, off = -ENOENT;
|
||||
struct btf_field_info tmp;
|
||||
int ret, idx = 0;
|
||||
u32 i, off;
|
||||
|
||||
for_each_vsi(i, t, vsi) {
|
||||
const struct btf_type *var = btf_type_by_id(btf, vsi->type);
|
||||
const struct btf_type *var_type = btf_type_by_id(btf, var->type);
|
||||
|
||||
if (!__btf_type_is_struct(var_type))
|
||||
continue;
|
||||
if (var_type->size != sz)
|
||||
off = vsi->offset;
|
||||
|
||||
if (name && strcmp(__btf_name_by_offset(btf, var_type->name_off), name))
|
||||
continue;
|
||||
if (vsi->size != sz)
|
||||
continue;
|
||||
if (strcmp(__btf_name_by_offset(btf, var_type->name_off), name))
|
||||
continue;
|
||||
if (off != -ENOENT)
|
||||
/* only one such field is allowed */
|
||||
return -E2BIG;
|
||||
off = vsi->offset;
|
||||
if (off % align)
|
||||
return -EINVAL;
|
||||
|
||||
switch (field_type) {
|
||||
case BTF_FIELD_SPIN_LOCK:
|
||||
case BTF_FIELD_TIMER:
|
||||
ret = btf_find_struct(btf, var_type, off, sz,
|
||||
idx < info_cnt ? &info[idx] : &tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
case BTF_FIELD_KPTR:
|
||||
ret = btf_find_kptr(btf, var_type, off, sz,
|
||||
idx < info_cnt ? &info[idx] : &tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (ret == BTF_FIELD_IGNORE)
|
||||
continue;
|
||||
if (idx >= info_cnt)
|
||||
return -E2BIG;
|
||||
++idx;
|
||||
}
|
||||
return off;
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int btf_find_field(const struct btf *btf, const struct btf_type *t,
|
||||
const char *name, int sz, int align)
|
||||
enum btf_field_type field_type,
|
||||
struct btf_field_info *info, int info_cnt)
|
||||
{
|
||||
const char *name;
|
||||
int sz, align;
|
||||
|
||||
switch (field_type) {
|
||||
case BTF_FIELD_SPIN_LOCK:
|
||||
name = "bpf_spin_lock";
|
||||
sz = sizeof(struct bpf_spin_lock);
|
||||
align = __alignof__(struct bpf_spin_lock);
|
||||
break;
|
||||
case BTF_FIELD_TIMER:
|
||||
name = "bpf_timer";
|
||||
sz = sizeof(struct bpf_timer);
|
||||
align = __alignof__(struct bpf_timer);
|
||||
break;
|
||||
case BTF_FIELD_KPTR:
|
||||
name = NULL;
|
||||
sz = sizeof(u64);
|
||||
align = 8;
|
||||
break;
|
||||
default:
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (__btf_type_is_struct(t))
|
||||
return btf_find_struct_field(btf, t, name, sz, align);
|
||||
return btf_find_struct_field(btf, t, name, sz, align, field_type, info, info_cnt);
|
||||
else if (btf_type_is_datasec(t))
|
||||
return btf_find_datasec_var(btf, t, name, sz, align);
|
||||
return btf_find_datasec_var(btf, t, name, sz, align, field_type, info, info_cnt);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -3237,16 +3386,130 @@ static int btf_find_field(const struct btf *btf, const struct btf_type *t,
|
||||
*/
|
||||
int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t)
|
||||
{
|
||||
return btf_find_field(btf, t, "bpf_spin_lock",
|
||||
sizeof(struct bpf_spin_lock),
|
||||
__alignof__(struct bpf_spin_lock));
|
||||
struct btf_field_info info;
|
||||
int ret;
|
||||
|
||||
ret = btf_find_field(btf, t, BTF_FIELD_SPIN_LOCK, &info, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!ret)
|
||||
return -ENOENT;
|
||||
return info.off;
|
||||
}
|
||||
|
||||
int btf_find_timer(const struct btf *btf, const struct btf_type *t)
|
||||
{
|
||||
return btf_find_field(btf, t, "bpf_timer",
|
||||
sizeof(struct bpf_timer),
|
||||
__alignof__(struct bpf_timer));
|
||||
struct btf_field_info info;
|
||||
int ret;
|
||||
|
||||
ret = btf_find_field(btf, t, BTF_FIELD_TIMER, &info, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!ret)
|
||||
return -ENOENT;
|
||||
return info.off;
|
||||
}
|
||||
|
||||
struct bpf_map_value_off *btf_parse_kptrs(const struct btf *btf,
|
||||
const struct btf_type *t)
|
||||
{
|
||||
struct btf_field_info info_arr[BPF_MAP_VALUE_OFF_MAX];
|
||||
struct bpf_map_value_off *tab;
|
||||
struct btf *kernel_btf = NULL;
|
||||
struct module *mod = NULL;
|
||||
int ret, i, nr_off;
|
||||
|
||||
ret = btf_find_field(btf, t, BTF_FIELD_KPTR, info_arr, ARRAY_SIZE(info_arr));
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
nr_off = ret;
|
||||
tab = kzalloc(offsetof(struct bpf_map_value_off, off[nr_off]), GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!tab)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < nr_off; i++) {
|
||||
const struct btf_type *t;
|
||||
s32 id;
|
||||
|
||||
/* Find type in map BTF, and use it to look up the matching type
|
||||
* in vmlinux or module BTFs, by name and kind.
|
||||
*/
|
||||
t = btf_type_by_id(btf, info_arr[i].type_id);
|
||||
id = bpf_find_btf_id(__btf_name_by_offset(btf, t->name_off), BTF_INFO_KIND(t->info),
|
||||
&kernel_btf);
|
||||
if (id < 0) {
|
||||
ret = id;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Find and stash the function pointer for the destruction function that
|
||||
* needs to be eventually invoked from the map free path.
|
||||
*/
|
||||
if (info_arr[i].type == BPF_KPTR_REF) {
|
||||
const struct btf_type *dtor_func;
|
||||
const char *dtor_func_name;
|
||||
unsigned long addr;
|
||||
s32 dtor_btf_id;
|
||||
|
||||
/* This call also serves as a whitelist of allowed objects that
|
||||
* can be used as a referenced pointer and be stored in a map at
|
||||
* the same time.
|
||||
*/
|
||||
dtor_btf_id = btf_find_dtor_kfunc(kernel_btf, id);
|
||||
if (dtor_btf_id < 0) {
|
||||
ret = dtor_btf_id;
|
||||
goto end_btf;
|
||||
}
|
||||
|
||||
dtor_func = btf_type_by_id(kernel_btf, dtor_btf_id);
|
||||
if (!dtor_func) {
|
||||
ret = -ENOENT;
|
||||
goto end_btf;
|
||||
}
|
||||
|
||||
if (btf_is_module(kernel_btf)) {
|
||||
mod = btf_try_get_module(kernel_btf);
|
||||
if (!mod) {
|
||||
ret = -ENXIO;
|
||||
goto end_btf;
|
||||
}
|
||||
}
|
||||
|
||||
/* We already verified dtor_func to be btf_type_is_func
|
||||
* in register_btf_id_dtor_kfuncs.
|
||||
*/
|
||||
dtor_func_name = __btf_name_by_offset(kernel_btf, dtor_func->name_off);
|
||||
addr = kallsyms_lookup_name(dtor_func_name);
|
||||
if (!addr) {
|
||||
ret = -EINVAL;
|
||||
goto end_mod;
|
||||
}
|
||||
tab->off[i].kptr.dtor = (void *)addr;
|
||||
}
|
||||
|
||||
tab->off[i].offset = info_arr[i].off;
|
||||
tab->off[i].type = info_arr[i].type;
|
||||
tab->off[i].kptr.btf_id = id;
|
||||
tab->off[i].kptr.btf = kernel_btf;
|
||||
tab->off[i].kptr.module = mod;
|
||||
}
|
||||
tab->nr_off = nr_off;
|
||||
return tab;
|
||||
end_mod:
|
||||
module_put(mod);
|
||||
end_btf:
|
||||
btf_put(kernel_btf);
|
||||
end:
|
||||
while (i--) {
|
||||
btf_put(tab->off[i].kptr.btf);
|
||||
if (tab->off[i].kptr.module)
|
||||
module_put(tab->off[i].kptr.module);
|
||||
}
|
||||
kfree(tab);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void __btf_struct_show(const struct btf *btf, const struct btf_type *t,
|
||||
@ -4541,6 +4804,48 @@ static int btf_parse_hdr(struct btf_verifier_env *env)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_check_type_tags(struct btf_verifier_env *env,
|
||||
struct btf *btf, int start_id)
|
||||
{
|
||||
int i, n, good_id = start_id - 1;
|
||||
bool in_tags;
|
||||
|
||||
n = btf_nr_types(btf);
|
||||
for (i = start_id; i < n; i++) {
|
||||
const struct btf_type *t;
|
||||
u32 cur_id = i;
|
||||
|
||||
t = btf_type_by_id(btf, i);
|
||||
if (!t)
|
||||
return -EINVAL;
|
||||
if (!btf_type_is_modifier(t))
|
||||
continue;
|
||||
|
||||
cond_resched();
|
||||
|
||||
in_tags = btf_type_is_type_tag(t);
|
||||
while (btf_type_is_modifier(t)) {
|
||||
if (btf_type_is_type_tag(t)) {
|
||||
if (!in_tags) {
|
||||
btf_verifier_log(env, "Type tags don't precede modifiers");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (in_tags) {
|
||||
in_tags = false;
|
||||
}
|
||||
if (cur_id <= good_id)
|
||||
break;
|
||||
/* Move to next type */
|
||||
cur_id = t->type;
|
||||
t = btf_type_by_id(btf, cur_id);
|
||||
if (!t)
|
||||
return -EINVAL;
|
||||
}
|
||||
good_id = i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct btf *btf_parse(bpfptr_t btf_data, u32 btf_data_size,
|
||||
u32 log_level, char __user *log_ubuf, u32 log_size)
|
||||
{
|
||||
@ -4608,6 +4913,10 @@ static struct btf *btf_parse(bpfptr_t btf_data, u32 btf_data_size,
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
err = btf_check_type_tags(env, btf, 1);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
if (log->level && bpf_verifier_log_full(log)) {
|
||||
err = -ENOSPC;
|
||||
goto errout;
|
||||
@ -4716,41 +5025,6 @@ btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
return ctx_type;
|
||||
}
|
||||
|
||||
static const struct bpf_map_ops * const btf_vmlinux_map_ops[] = {
|
||||
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type)
|
||||
#define BPF_LINK_TYPE(_id, _name)
|
||||
#define BPF_MAP_TYPE(_id, _ops) \
|
||||
[_id] = &_ops,
|
||||
#include <linux/bpf_types.h>
|
||||
#undef BPF_PROG_TYPE
|
||||
#undef BPF_LINK_TYPE
|
||||
#undef BPF_MAP_TYPE
|
||||
};
|
||||
|
||||
static int btf_vmlinux_map_ids_init(const struct btf *btf,
|
||||
struct bpf_verifier_log *log)
|
||||
{
|
||||
const struct bpf_map_ops *ops;
|
||||
int i, btf_id;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(btf_vmlinux_map_ops); ++i) {
|
||||
ops = btf_vmlinux_map_ops[i];
|
||||
if (!ops || (!ops->map_btf_name && !ops->map_btf_id))
|
||||
continue;
|
||||
if (!ops->map_btf_name || !ops->map_btf_id) {
|
||||
bpf_log(log, "map type %d is misconfigured\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
btf_id = btf_find_by_name_kind(btf, ops->map_btf_name,
|
||||
BTF_KIND_STRUCT);
|
||||
if (btf_id < 0)
|
||||
return btf_id;
|
||||
*ops->map_btf_id = btf_id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_translate_to_vmlinux(struct bpf_verifier_log *log,
|
||||
struct btf *btf,
|
||||
const struct btf_type *t,
|
||||
@ -4809,14 +5083,13 @@ struct btf *btf_parse_vmlinux(void)
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
err = btf_check_type_tags(env, btf, 1);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
/* btf_parse_vmlinux() runs under bpf_verifier_lock */
|
||||
bpf_ctx_convert.t = btf_type_by_id(btf, bpf_ctx_convert_btf_id[0]);
|
||||
|
||||
/* find bpf map structs for map_ptr access checking */
|
||||
err = btf_vmlinux_map_ids_init(btf, log);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
|
||||
bpf_struct_ops_init(btf, log);
|
||||
|
||||
refcount_set(&btf->refcnt, 1);
|
||||
@ -4894,6 +5167,10 @@ static struct btf *btf_parse_module(const char *module_name, const void *data, u
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
err = btf_check_type_tags(env, btf, btf_nr_types(base_btf));
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
btf_verifier_env_free(env);
|
||||
refcount_set(&btf->refcnt, 1);
|
||||
return btf;
|
||||
@ -5429,7 +5706,8 @@ static bool btf_types_are_same(const struct btf *btf1, u32 id1,
|
||||
|
||||
bool btf_struct_ids_match(struct bpf_verifier_log *log,
|
||||
const struct btf *btf, u32 id, int off,
|
||||
const struct btf *need_btf, u32 need_type_id)
|
||||
const struct btf *need_btf, u32 need_type_id,
|
||||
bool strict)
|
||||
{
|
||||
const struct btf_type *type;
|
||||
enum bpf_type_flag flag;
|
||||
@ -5438,7 +5716,12 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
|
||||
/* Are we already done? */
|
||||
if (off == 0 && btf_types_are_same(btf, id, need_btf, need_type_id))
|
||||
return true;
|
||||
|
||||
/* In case of strict type match, we do not walk struct, the top level
|
||||
* type match must succeed. When strict is true, off should have already
|
||||
* been 0.
|
||||
*/
|
||||
if (strict)
|
||||
return false;
|
||||
again:
|
||||
type = btf_type_by_id(btf, id);
|
||||
if (!type)
|
||||
@ -5772,11 +6055,11 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
|
||||
struct bpf_verifier_log *log = &env->log;
|
||||
u32 i, nargs, ref_id, ref_obj_id = 0;
|
||||
bool is_kfunc = btf_is_kernel(btf);
|
||||
bool rel = false, kptr_get = false;
|
||||
const char *func_name, *ref_tname;
|
||||
const struct btf_type *t, *ref_t;
|
||||
const struct btf_param *args;
|
||||
int ref_regno = 0, ret;
|
||||
bool rel = false;
|
||||
|
||||
t = btf_type_by_id(btf, func_id);
|
||||
if (!t || !btf_type_is_func(t)) {
|
||||
@ -5802,14 +6085,19 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Only kfunc can be release func */
|
||||
if (is_kfunc)
|
||||
if (is_kfunc) {
|
||||
/* Only kfunc can be release func */
|
||||
rel = btf_kfunc_id_set_contains(btf, resolve_prog_type(env->prog),
|
||||
BTF_KFUNC_TYPE_RELEASE, func_id);
|
||||
kptr_get = btf_kfunc_id_set_contains(btf, resolve_prog_type(env->prog),
|
||||
BTF_KFUNC_TYPE_KPTR_ACQUIRE, func_id);
|
||||
}
|
||||
|
||||
/* check that BTF function arguments match actual types that the
|
||||
* verifier sees.
|
||||
*/
|
||||
for (i = 0; i < nargs; i++) {
|
||||
enum bpf_arg_type arg_type = ARG_DONTCARE;
|
||||
u32 regno = i + 1;
|
||||
struct bpf_reg_state *reg = ®s[regno];
|
||||
|
||||
@ -5830,12 +6118,58 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
|
||||
ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
|
||||
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
|
||||
|
||||
ret = check_func_arg_reg_off(env, reg, regno, ARG_DONTCARE, rel);
|
||||
if (rel && reg->ref_obj_id)
|
||||
arg_type |= OBJ_RELEASE;
|
||||
ret = check_func_arg_reg_off(env, reg, regno, arg_type);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (btf_get_prog_ctx_type(log, btf, t,
|
||||
env->prog->type, i)) {
|
||||
/* kptr_get is only true for kfunc */
|
||||
if (i == 0 && kptr_get) {
|
||||
struct bpf_map_value_off_desc *off_desc;
|
||||
|
||||
if (reg->type != PTR_TO_MAP_VALUE) {
|
||||
bpf_log(log, "arg#0 expected pointer to map value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check_func_arg_reg_off allows var_off for
|
||||
* PTR_TO_MAP_VALUE, but we need fixed offset to find
|
||||
* off_desc.
|
||||
*/
|
||||
if (!tnum_is_const(reg->var_off)) {
|
||||
bpf_log(log, "arg#0 must have constant offset\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
off_desc = bpf_map_kptr_off_contains(reg->map_ptr, reg->off + reg->var_off.value);
|
||||
if (!off_desc || off_desc->type != BPF_KPTR_REF) {
|
||||
bpf_log(log, "arg#0 no referenced kptr at map value offset=%llu\n",
|
||||
reg->off + reg->var_off.value);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!btf_type_is_ptr(ref_t)) {
|
||||
bpf_log(log, "arg#0 BTF type must be a double pointer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ref_t = btf_type_skip_modifiers(btf, ref_t->type, &ref_id);
|
||||
ref_tname = btf_name_by_offset(btf, ref_t->name_off);
|
||||
|
||||
if (!btf_type_is_struct(ref_t)) {
|
||||
bpf_log(log, "kernel function %s args#%d pointer type %s %s is not supported\n",
|
||||
func_name, i, btf_type_str(ref_t), ref_tname);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!btf_struct_ids_match(log, btf, ref_id, 0, off_desc->kptr.btf,
|
||||
off_desc->kptr.btf_id, true)) {
|
||||
bpf_log(log, "kernel function %s args#%d expected pointer to %s %s\n",
|
||||
func_name, i, btf_type_str(ref_t), ref_tname);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* rest of the arguments can be anything, like normal kfunc */
|
||||
} else if (btf_get_prog_ctx_type(log, btf, t, env->prog->type, i)) {
|
||||
/* If function expects ctx type in BTF check that caller
|
||||
* is passing PTR_TO_CTX.
|
||||
*/
|
||||
@ -5862,11 +6196,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
|
||||
if (reg->type == PTR_TO_BTF_ID) {
|
||||
reg_btf = reg->btf;
|
||||
reg_ref_id = reg->btf_id;
|
||||
/* Ensure only one argument is referenced
|
||||
* PTR_TO_BTF_ID, check_func_arg_reg_off relies
|
||||
* on only one referenced register being allowed
|
||||
* for kfuncs.
|
||||
*/
|
||||
/* Ensure only one argument is referenced PTR_TO_BTF_ID */
|
||||
if (reg->ref_obj_id) {
|
||||
if (ref_obj_id) {
|
||||
bpf_log(log, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
|
||||
@ -5886,7 +6216,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env,
|
||||
reg_ref_tname = btf_name_by_offset(reg_btf,
|
||||
reg_ref_t->name_off);
|
||||
if (!btf_struct_ids_match(log, reg_btf, reg_ref_id,
|
||||
reg->off, btf, ref_id)) {
|
||||
reg->off, btf, ref_id, rel && reg->ref_obj_id)) {
|
||||
bpf_log(log, "kernel function %s args#%d expected pointer to %s %s but R%d has a pointer to %s %s\n",
|
||||
func_name, i,
|
||||
btf_type_str(ref_t), ref_tname,
|
||||
@ -6832,6 +7162,138 @@ int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_btf_kfunc_id_set);
|
||||
|
||||
s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id)
|
||||
{
|
||||
struct btf_id_dtor_kfunc_tab *tab = btf->dtor_kfunc_tab;
|
||||
struct btf_id_dtor_kfunc *dtor;
|
||||
|
||||
if (!tab)
|
||||
return -ENOENT;
|
||||
/* Even though the size of tab->dtors[0] is > sizeof(u32), we only need
|
||||
* to compare the first u32 with btf_id, so we can reuse btf_id_cmp_func.
|
||||
*/
|
||||
BUILD_BUG_ON(offsetof(struct btf_id_dtor_kfunc, btf_id) != 0);
|
||||
dtor = bsearch(&btf_id, tab->dtors, tab->cnt, sizeof(tab->dtors[0]), btf_id_cmp_func);
|
||||
if (!dtor)
|
||||
return -ENOENT;
|
||||
return dtor->kfunc_btf_id;
|
||||
}
|
||||
|
||||
static int btf_check_dtor_kfuncs(struct btf *btf, const struct btf_id_dtor_kfunc *dtors, u32 cnt)
|
||||
{
|
||||
const struct btf_type *dtor_func, *dtor_func_proto, *t;
|
||||
const struct btf_param *args;
|
||||
s32 dtor_btf_id;
|
||||
u32 nr_args, i;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
dtor_btf_id = dtors[i].kfunc_btf_id;
|
||||
|
||||
dtor_func = btf_type_by_id(btf, dtor_btf_id);
|
||||
if (!dtor_func || !btf_type_is_func(dtor_func))
|
||||
return -EINVAL;
|
||||
|
||||
dtor_func_proto = btf_type_by_id(btf, dtor_func->type);
|
||||
if (!dtor_func_proto || !btf_type_is_func_proto(dtor_func_proto))
|
||||
return -EINVAL;
|
||||
|
||||
/* Make sure the prototype of the destructor kfunc is 'void func(type *)' */
|
||||
t = btf_type_by_id(btf, dtor_func_proto->type);
|
||||
if (!t || !btf_type_is_void(t))
|
||||
return -EINVAL;
|
||||
|
||||
nr_args = btf_type_vlen(dtor_func_proto);
|
||||
if (nr_args != 1)
|
||||
return -EINVAL;
|
||||
args = btf_params(dtor_func_proto);
|
||||
t = btf_type_by_id(btf, args[0].type);
|
||||
/* Allow any pointer type, as width on targets Linux supports
|
||||
* will be same for all pointer types (i.e. sizeof(void *))
|
||||
*/
|
||||
if (!t || !btf_type_is_ptr(t))
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function must be invoked only from initcalls/module init functions */
|
||||
int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
|
||||
struct module *owner)
|
||||
{
|
||||
struct btf_id_dtor_kfunc_tab *tab;
|
||||
struct btf *btf;
|
||||
u32 tab_cnt;
|
||||
int ret;
|
||||
|
||||
btf = btf_get_module_btf(owner);
|
||||
if (!btf) {
|
||||
if (!owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) {
|
||||
pr_err("missing vmlinux BTF, cannot register dtor kfuncs\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
if (owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) {
|
||||
pr_err("missing module BTF, cannot register dtor kfuncs\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (IS_ERR(btf))
|
||||
return PTR_ERR(btf);
|
||||
|
||||
if (add_cnt >= BTF_DTOR_KFUNC_MAX_CNT) {
|
||||
pr_err("cannot register more than %d kfunc destructors\n", BTF_DTOR_KFUNC_MAX_CNT);
|
||||
ret = -E2BIG;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Ensure that the prototype of dtor kfuncs being registered is sane */
|
||||
ret = btf_check_dtor_kfuncs(btf, dtors, add_cnt);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
|
||||
tab = btf->dtor_kfunc_tab;
|
||||
/* Only one call allowed for modules */
|
||||
if (WARN_ON_ONCE(tab && btf_is_module(btf))) {
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
tab_cnt = tab ? tab->cnt : 0;
|
||||
if (tab_cnt > U32_MAX - add_cnt) {
|
||||
ret = -EOVERFLOW;
|
||||
goto end;
|
||||
}
|
||||
if (tab_cnt + add_cnt >= BTF_DTOR_KFUNC_MAX_CNT) {
|
||||
pr_err("cannot register more than %d kfunc destructors\n", BTF_DTOR_KFUNC_MAX_CNT);
|
||||
ret = -E2BIG;
|
||||
goto end;
|
||||
}
|
||||
|
||||
tab = krealloc(btf->dtor_kfunc_tab,
|
||||
offsetof(struct btf_id_dtor_kfunc_tab, dtors[tab_cnt + add_cnt]),
|
||||
GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!tab) {
|
||||
ret = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!btf->dtor_kfunc_tab)
|
||||
tab->cnt = 0;
|
||||
btf->dtor_kfunc_tab = tab;
|
||||
|
||||
memcpy(tab->dtors + tab->cnt, dtors, add_cnt * sizeof(tab->dtors[0]));
|
||||
tab->cnt += add_cnt;
|
||||
|
||||
sort(tab->dtors, tab->cnt, sizeof(tab->dtors[0]), btf_id_cmp_func, NULL);
|
||||
|
||||
return 0;
|
||||
end:
|
||||
btf_free_dtor_kfunc_tab(btf);
|
||||
btf_put(btf);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_btf_id_dtor_kfuncs);
|
||||
|
||||
#define MAX_TYPES_ARE_COMPAT_DEPTH 2
|
||||
|
||||
static
|
||||
|
@ -22,6 +22,45 @@
|
||||
DEFINE_STATIC_KEY_ARRAY_FALSE(cgroup_bpf_enabled_key, MAX_CGROUP_BPF_ATTACH_TYPE);
|
||||
EXPORT_SYMBOL(cgroup_bpf_enabled_key);
|
||||
|
||||
/* __always_inline is necessary to prevent indirect call through run_prog
|
||||
* function pointer.
|
||||
*/
|
||||
static __always_inline int
|
||||
bpf_prog_run_array_cg(const struct cgroup_bpf *cgrp,
|
||||
enum cgroup_bpf_attach_type atype,
|
||||
const void *ctx, bpf_prog_run_fn run_prog,
|
||||
int retval, u32 *ret_flags)
|
||||
{
|
||||
const struct bpf_prog_array_item *item;
|
||||
const struct bpf_prog *prog;
|
||||
const struct bpf_prog_array *array;
|
||||
struct bpf_run_ctx *old_run_ctx;
|
||||
struct bpf_cg_run_ctx run_ctx;
|
||||
u32 func_ret;
|
||||
|
||||
run_ctx.retval = retval;
|
||||
migrate_disable();
|
||||
rcu_read_lock();
|
||||
array = rcu_dereference(cgrp->effective[atype]);
|
||||
item = &array->items[0];
|
||||
old_run_ctx = bpf_set_run_ctx(&run_ctx.run_ctx);
|
||||
while ((prog = READ_ONCE(item->prog))) {
|
||||
run_ctx.prog_item = item;
|
||||
func_ret = run_prog(prog, ctx);
|
||||
if (ret_flags) {
|
||||
*(ret_flags) |= (func_ret >> 1);
|
||||
func_ret &= 1;
|
||||
}
|
||||
if (!func_ret && !IS_ERR_VALUE((long)run_ctx.retval))
|
||||
run_ctx.retval = -EPERM;
|
||||
item++;
|
||||
}
|
||||
bpf_reset_run_ctx(old_run_ctx);
|
||||
rcu_read_unlock();
|
||||
migrate_enable();
|
||||
return run_ctx.retval;
|
||||
}
|
||||
|
||||
void cgroup_bpf_offline(struct cgroup *cgrp)
|
||||
{
|
||||
cgroup_get(cgrp);
|
||||
@ -1075,11 +1114,38 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk,
|
||||
bpf_compute_and_save_data_end(skb, &saved_data_end);
|
||||
|
||||
if (atype == CGROUP_INET_EGRESS) {
|
||||
ret = BPF_PROG_CGROUP_INET_EGRESS_RUN_ARRAY(
|
||||
cgrp->bpf.effective[atype], skb, __bpf_prog_run_save_cb);
|
||||
u32 flags = 0;
|
||||
bool cn;
|
||||
|
||||
ret = bpf_prog_run_array_cg(&cgrp->bpf, atype, skb,
|
||||
__bpf_prog_run_save_cb, 0, &flags);
|
||||
|
||||
/* Return values of CGROUP EGRESS BPF programs are:
|
||||
* 0: drop packet
|
||||
* 1: keep packet
|
||||
* 2: drop packet and cn
|
||||
* 3: keep packet and cn
|
||||
*
|
||||
* The returned value is then converted to one of the NET_XMIT
|
||||
* or an error code that is then interpreted as drop packet
|
||||
* (and no cn):
|
||||
* 0: NET_XMIT_SUCCESS skb should be transmitted
|
||||
* 1: NET_XMIT_DROP skb should be dropped and cn
|
||||
* 2: NET_XMIT_CN skb should be transmitted and cn
|
||||
* 3: -err skb should be dropped
|
||||
*/
|
||||
|
||||
cn = flags & BPF_RET_SET_CN;
|
||||
if (ret && !IS_ERR_VALUE((long)ret))
|
||||
ret = -EFAULT;
|
||||
if (!ret)
|
||||
ret = (cn ? NET_XMIT_CN : NET_XMIT_SUCCESS);
|
||||
else
|
||||
ret = (cn ? NET_XMIT_DROP : ret);
|
||||
} else {
|
||||
ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], skb,
|
||||
__bpf_prog_run_save_cb, 0);
|
||||
ret = bpf_prog_run_array_cg(&cgrp->bpf, atype,
|
||||
skb, __bpf_prog_run_save_cb, 0,
|
||||
NULL);
|
||||
if (ret && !IS_ERR_VALUE((long)ret))
|
||||
ret = -EFAULT;
|
||||
}
|
||||
@ -1109,8 +1175,8 @@ int __cgroup_bpf_run_filter_sk(struct sock *sk,
|
||||
{
|
||||
struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
|
||||
|
||||
return BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], sk,
|
||||
bpf_prog_run, 0);
|
||||
return bpf_prog_run_array_cg(&cgrp->bpf, atype, sk, bpf_prog_run, 0,
|
||||
NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk);
|
||||
|
||||
@ -1155,8 +1221,8 @@ int __cgroup_bpf_run_filter_sock_addr(struct sock *sk,
|
||||
}
|
||||
|
||||
cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
|
||||
return BPF_PROG_RUN_ARRAY_CG_FLAGS(cgrp->bpf.effective[atype], &ctx,
|
||||
bpf_prog_run, 0, flags);
|
||||
return bpf_prog_run_array_cg(&cgrp->bpf, atype, &ctx, bpf_prog_run,
|
||||
0, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_addr);
|
||||
|
||||
@ -1182,8 +1248,8 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
|
||||
{
|
||||
struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data);
|
||||
|
||||
return BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], sock_ops,
|
||||
bpf_prog_run, 0);
|
||||
return bpf_prog_run_array_cg(&cgrp->bpf, atype, sock_ops, bpf_prog_run,
|
||||
0, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_ops);
|
||||
|
||||
@ -1200,8 +1266,8 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
|
||||
|
||||
rcu_read_lock();
|
||||
cgrp = task_dfl_cgroup(current);
|
||||
ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], &ctx,
|
||||
bpf_prog_run, 0);
|
||||
ret = bpf_prog_run_array_cg(&cgrp->bpf, atype, &ctx, bpf_prog_run, 0,
|
||||
NULL);
|
||||
rcu_read_unlock();
|
||||
|
||||
return ret;
|
||||
@ -1366,8 +1432,8 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
|
||||
|
||||
rcu_read_lock();
|
||||
cgrp = task_dfl_cgroup(current);
|
||||
ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[atype], &ctx,
|
||||
bpf_prog_run, 0);
|
||||
ret = bpf_prog_run_array_cg(&cgrp->bpf, atype, &ctx, bpf_prog_run, 0,
|
||||
NULL);
|
||||
rcu_read_unlock();
|
||||
|
||||
kfree(ctx.cur_val);
|
||||
@ -1459,8 +1525,8 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
|
||||
}
|
||||
|
||||
lock_sock(sk);
|
||||
ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[CGROUP_SETSOCKOPT],
|
||||
&ctx, bpf_prog_run, 0);
|
||||
ret = bpf_prog_run_array_cg(&cgrp->bpf, CGROUP_SETSOCKOPT,
|
||||
&ctx, bpf_prog_run, 0, NULL);
|
||||
release_sock(sk);
|
||||
|
||||
if (ret)
|
||||
@ -1559,8 +1625,8 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
|
||||
}
|
||||
|
||||
lock_sock(sk);
|
||||
ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[CGROUP_GETSOCKOPT],
|
||||
&ctx, bpf_prog_run, retval);
|
||||
ret = bpf_prog_run_array_cg(&cgrp->bpf, CGROUP_GETSOCKOPT,
|
||||
&ctx, bpf_prog_run, retval, NULL);
|
||||
release_sock(sk);
|
||||
|
||||
if (ret < 0)
|
||||
@ -1608,8 +1674,8 @@ int __cgroup_bpf_run_filter_getsockopt_kern(struct sock *sk, int level,
|
||||
* be called if that data shouldn't be "exported".
|
||||
*/
|
||||
|
||||
ret = BPF_PROG_RUN_ARRAY_CG(cgrp->bpf.effective[CGROUP_GETSOCKOPT],
|
||||
&ctx, bpf_prog_run, retval);
|
||||
ret = bpf_prog_run_array_cg(&cgrp->bpf, CGROUP_GETSOCKOPT,
|
||||
&ctx, bpf_prog_run, retval, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/capability.h>
|
||||
#include <trace/events/xdp.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
#include <linux/netdevice.h> /* netif_receive_skb_list */
|
||||
#include <linux/etherdevice.h> /* eth_type_trans */
|
||||
@ -673,7 +674,7 @@ static int cpu_map_redirect(struct bpf_map *map, u32 ifindex, u64 flags)
|
||||
__cpu_map_lookup_elem);
|
||||
}
|
||||
|
||||
static int cpu_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(cpu_map_btf_ids, struct, bpf_cpu_map)
|
||||
const struct bpf_map_ops cpu_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = cpu_map_alloc,
|
||||
@ -683,8 +684,7 @@ const struct bpf_map_ops cpu_map_ops = {
|
||||
.map_lookup_elem = cpu_map_lookup_elem,
|
||||
.map_get_next_key = cpu_map_get_next_key,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "bpf_cpu_map",
|
||||
.map_btf_id = &cpu_map_btf_id,
|
||||
.map_btf_id = &cpu_map_btf_ids[0],
|
||||
.map_redirect = cpu_map_redirect,
|
||||
};
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include <net/xdp.h>
|
||||
#include <linux/filter.h>
|
||||
#include <trace/events/xdp.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
#define DEV_CREATE_FLAG_MASK \
|
||||
(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
|
||||
@ -1005,7 +1006,7 @@ static int dev_hash_map_redirect(struct bpf_map *map, u32 ifindex, u64 flags)
|
||||
__dev_map_hash_lookup_elem);
|
||||
}
|
||||
|
||||
static int dev_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(dev_map_btf_ids, struct, bpf_dtab)
|
||||
const struct bpf_map_ops dev_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = dev_map_alloc,
|
||||
@ -1015,12 +1016,10 @@ const struct bpf_map_ops dev_map_ops = {
|
||||
.map_update_elem = dev_map_update_elem,
|
||||
.map_delete_elem = dev_map_delete_elem,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "bpf_dtab",
|
||||
.map_btf_id = &dev_map_btf_id,
|
||||
.map_btf_id = &dev_map_btf_ids[0],
|
||||
.map_redirect = dev_map_redirect,
|
||||
};
|
||||
|
||||
static int dev_map_hash_map_btf_id;
|
||||
const struct bpf_map_ops dev_map_hash_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = dev_map_alloc,
|
||||
@ -1030,8 +1029,7 @@ const struct bpf_map_ops dev_map_hash_ops = {
|
||||
.map_update_elem = dev_map_hash_update_elem,
|
||||
.map_delete_elem = dev_map_hash_delete_elem,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "bpf_dtab",
|
||||
.map_btf_id = &dev_map_hash_map_btf_id,
|
||||
.map_btf_id = &dev_map_btf_ids[0],
|
||||
.map_redirect = dev_hash_map_redirect,
|
||||
};
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/random.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/rcupdate_trace.h>
|
||||
#include <linux/btf_ids.h>
|
||||
#include "percpu_freelist.h"
|
||||
#include "bpf_lru_list.h"
|
||||
#include "map_in_map.h"
|
||||
@ -238,7 +239,7 @@ static void htab_free_prealloced_timers(struct bpf_htab *htab)
|
||||
u32 num_entries = htab->map.max_entries;
|
||||
int i;
|
||||
|
||||
if (likely(!map_value_has_timer(&htab->map)))
|
||||
if (!map_value_has_timer(&htab->map))
|
||||
return;
|
||||
if (htab_has_extra_elems(htab))
|
||||
num_entries += num_possible_cpus();
|
||||
@ -254,6 +255,25 @@ static void htab_free_prealloced_timers(struct bpf_htab *htab)
|
||||
}
|
||||
}
|
||||
|
||||
static void htab_free_prealloced_kptrs(struct bpf_htab *htab)
|
||||
{
|
||||
u32 num_entries = htab->map.max_entries;
|
||||
int i;
|
||||
|
||||
if (!map_value_has_kptrs(&htab->map))
|
||||
return;
|
||||
if (htab_has_extra_elems(htab))
|
||||
num_entries += num_possible_cpus();
|
||||
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
struct htab_elem *elem;
|
||||
|
||||
elem = get_htab_elem(htab, i);
|
||||
bpf_map_free_kptrs(&htab->map, elem->key + round_up(htab->map.key_size, 8));
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
static void htab_free_elems(struct bpf_htab *htab)
|
||||
{
|
||||
int i;
|
||||
@ -725,12 +745,15 @@ static int htab_lru_map_gen_lookup(struct bpf_map *map,
|
||||
return insn - insn_buf;
|
||||
}
|
||||
|
||||
static void check_and_free_timer(struct bpf_htab *htab, struct htab_elem *elem)
|
||||
static void check_and_free_fields(struct bpf_htab *htab,
|
||||
struct htab_elem *elem)
|
||||
{
|
||||
if (unlikely(map_value_has_timer(&htab->map)))
|
||||
bpf_timer_cancel_and_free(elem->key +
|
||||
round_up(htab->map.key_size, 8) +
|
||||
htab->map.timer_off);
|
||||
void *map_value = elem->key + round_up(htab->map.key_size, 8);
|
||||
|
||||
if (map_value_has_timer(&htab->map))
|
||||
bpf_timer_cancel_and_free(map_value + htab->map.timer_off);
|
||||
if (map_value_has_kptrs(&htab->map))
|
||||
bpf_map_free_kptrs(&htab->map, map_value);
|
||||
}
|
||||
|
||||
/* It is called from the bpf_lru_list when the LRU needs to delete
|
||||
@ -738,7 +761,7 @@ static void check_and_free_timer(struct bpf_htab *htab, struct htab_elem *elem)
|
||||
*/
|
||||
static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node)
|
||||
{
|
||||
struct bpf_htab *htab = (struct bpf_htab *)arg;
|
||||
struct bpf_htab *htab = arg;
|
||||
struct htab_elem *l = NULL, *tgt_l;
|
||||
struct hlist_nulls_head *head;
|
||||
struct hlist_nulls_node *n;
|
||||
@ -757,7 +780,7 @@ static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node)
|
||||
hlist_nulls_for_each_entry_rcu(l, n, head, hash_node)
|
||||
if (l == tgt_l) {
|
||||
hlist_nulls_del_rcu(&l->hash_node);
|
||||
check_and_free_timer(htab, l);
|
||||
check_and_free_fields(htab, l);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -829,7 +852,7 @@ static void htab_elem_free(struct bpf_htab *htab, struct htab_elem *l)
|
||||
{
|
||||
if (htab->map.map_type == BPF_MAP_TYPE_PERCPU_HASH)
|
||||
free_percpu(htab_elem_get_ptr(l, htab->map.key_size));
|
||||
check_and_free_timer(htab, l);
|
||||
check_and_free_fields(htab, l);
|
||||
kfree(l);
|
||||
}
|
||||
|
||||
@ -857,7 +880,7 @@ static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l)
|
||||
htab_put_fd_value(htab, l);
|
||||
|
||||
if (htab_is_prealloc(htab)) {
|
||||
check_and_free_timer(htab, l);
|
||||
check_and_free_fields(htab, l);
|
||||
__pcpu_freelist_push(&htab->freelist, &l->fnode);
|
||||
} else {
|
||||
atomic_dec(&htab->count);
|
||||
@ -1104,7 +1127,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
if (!htab_is_prealloc(htab))
|
||||
free_htab_elem(htab, l_old);
|
||||
else
|
||||
check_and_free_timer(htab, l_old);
|
||||
check_and_free_fields(htab, l_old);
|
||||
}
|
||||
ret = 0;
|
||||
err:
|
||||
@ -1114,7 +1137,7 @@ err:
|
||||
|
||||
static void htab_lru_push_free(struct bpf_htab *htab, struct htab_elem *elem)
|
||||
{
|
||||
check_and_free_timer(htab, elem);
|
||||
check_and_free_fields(htab, elem);
|
||||
bpf_lru_push_free(&htab->lru, &elem->lru_node);
|
||||
}
|
||||
|
||||
@ -1419,8 +1442,14 @@ static void htab_free_malloced_timers(struct bpf_htab *htab)
|
||||
struct hlist_nulls_node *n;
|
||||
struct htab_elem *l;
|
||||
|
||||
hlist_nulls_for_each_entry(l, n, head, hash_node)
|
||||
check_and_free_timer(htab, l);
|
||||
hlist_nulls_for_each_entry(l, n, head, hash_node) {
|
||||
/* We don't reset or free kptr on uref dropping to zero,
|
||||
* hence just free timer.
|
||||
*/
|
||||
bpf_timer_cancel_and_free(l->key +
|
||||
round_up(htab->map.key_size, 8) +
|
||||
htab->map.timer_off);
|
||||
}
|
||||
cond_resched_rcu();
|
||||
}
|
||||
rcu_read_unlock();
|
||||
@ -1430,7 +1459,8 @@ static void htab_map_free_timers(struct bpf_map *map)
|
||||
{
|
||||
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
|
||||
|
||||
if (likely(!map_value_has_timer(&htab->map)))
|
||||
/* We don't reset or free kptr on uref dropping to zero. */
|
||||
if (!map_value_has_timer(&htab->map))
|
||||
return;
|
||||
if (!htab_is_prealloc(htab))
|
||||
htab_free_malloced_timers(htab);
|
||||
@ -1453,11 +1483,14 @@ static void htab_map_free(struct bpf_map *map)
|
||||
* not have executed. Wait for them.
|
||||
*/
|
||||
rcu_barrier();
|
||||
if (!htab_is_prealloc(htab))
|
||||
if (!htab_is_prealloc(htab)) {
|
||||
delete_all_elements(htab);
|
||||
else
|
||||
} else {
|
||||
htab_free_prealloced_kptrs(htab);
|
||||
prealloc_destroy(htab);
|
||||
}
|
||||
|
||||
bpf_map_free_kptr_off_tab(map);
|
||||
free_percpu(htab->extra_elems);
|
||||
bpf_map_area_free(htab->buckets);
|
||||
for (i = 0; i < HASHTAB_MAP_LOCK_COUNT; i++)
|
||||
@ -2105,7 +2138,7 @@ out:
|
||||
return num_elems;
|
||||
}
|
||||
|
||||
static int htab_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(htab_map_btf_ids, struct, bpf_htab)
|
||||
const struct bpf_map_ops htab_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = htab_map_alloc_check,
|
||||
@ -2122,12 +2155,10 @@ const struct bpf_map_ops htab_map_ops = {
|
||||
.map_set_for_each_callback_args = map_set_for_each_callback_args,
|
||||
.map_for_each_callback = bpf_for_each_hash_elem,
|
||||
BATCH_OPS(htab),
|
||||
.map_btf_name = "bpf_htab",
|
||||
.map_btf_id = &htab_map_btf_id,
|
||||
.map_btf_id = &htab_map_btf_ids[0],
|
||||
.iter_seq_info = &iter_seq_info,
|
||||
};
|
||||
|
||||
static int htab_lru_map_btf_id;
|
||||
const struct bpf_map_ops htab_lru_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = htab_map_alloc_check,
|
||||
@ -2145,8 +2176,7 @@ const struct bpf_map_ops htab_lru_map_ops = {
|
||||
.map_set_for_each_callback_args = map_set_for_each_callback_args,
|
||||
.map_for_each_callback = bpf_for_each_hash_elem,
|
||||
BATCH_OPS(htab_lru),
|
||||
.map_btf_name = "bpf_htab",
|
||||
.map_btf_id = &htab_lru_map_btf_id,
|
||||
.map_btf_id = &htab_map_btf_ids[0],
|
||||
.iter_seq_info = &iter_seq_info,
|
||||
};
|
||||
|
||||
@ -2252,7 +2282,6 @@ static void htab_percpu_map_seq_show_elem(struct bpf_map *map, void *key,
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int htab_percpu_map_btf_id;
|
||||
const struct bpf_map_ops htab_percpu_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = htab_map_alloc_check,
|
||||
@ -2267,12 +2296,10 @@ const struct bpf_map_ops htab_percpu_map_ops = {
|
||||
.map_set_for_each_callback_args = map_set_for_each_callback_args,
|
||||
.map_for_each_callback = bpf_for_each_hash_elem,
|
||||
BATCH_OPS(htab_percpu),
|
||||
.map_btf_name = "bpf_htab",
|
||||
.map_btf_id = &htab_percpu_map_btf_id,
|
||||
.map_btf_id = &htab_map_btf_ids[0],
|
||||
.iter_seq_info = &iter_seq_info,
|
||||
};
|
||||
|
||||
static int htab_lru_percpu_map_btf_id;
|
||||
const struct bpf_map_ops htab_lru_percpu_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = htab_map_alloc_check,
|
||||
@ -2287,8 +2314,7 @@ const struct bpf_map_ops htab_lru_percpu_map_ops = {
|
||||
.map_set_for_each_callback_args = map_set_for_each_callback_args,
|
||||
.map_for_each_callback = bpf_for_each_hash_elem,
|
||||
BATCH_OPS(htab_lru_percpu),
|
||||
.map_btf_name = "bpf_htab",
|
||||
.map_btf_id = &htab_lru_percpu_map_btf_id,
|
||||
.map_btf_id = &htab_map_btf_ids[0],
|
||||
.iter_seq_info = &iter_seq_info,
|
||||
};
|
||||
|
||||
@ -2412,7 +2438,6 @@ static void htab_of_map_free(struct bpf_map *map)
|
||||
fd_htab_map_free(map);
|
||||
}
|
||||
|
||||
static int htab_of_maps_map_btf_id;
|
||||
const struct bpf_map_ops htab_of_maps_map_ops = {
|
||||
.map_alloc_check = fd_htab_map_alloc_check,
|
||||
.map_alloc = htab_of_map_alloc,
|
||||
@ -2425,6 +2450,5 @@ const struct bpf_map_ops htab_of_maps_map_ops = {
|
||||
.map_fd_sys_lookup_elem = bpf_map_fd_sys_lookup_elem,
|
||||
.map_gen_lookup = htab_of_map_gen_lookup,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "bpf_htab",
|
||||
.map_btf_id = &htab_of_maps_map_btf_id,
|
||||
.map_btf_id = &htab_map_btf_ids[0],
|
||||
};
|
||||
|
@ -1374,6 +1374,28 @@ out:
|
||||
kfree(t);
|
||||
}
|
||||
|
||||
BPF_CALL_2(bpf_kptr_xchg, void *, map_value, void *, ptr)
|
||||
{
|
||||
unsigned long *kptr = map_value;
|
||||
|
||||
return xchg(kptr, (unsigned long)ptr);
|
||||
}
|
||||
|
||||
/* Unlike other PTR_TO_BTF_ID helpers the btf_id in bpf_kptr_xchg()
|
||||
* helper is determined dynamically by the verifier.
|
||||
*/
|
||||
#define BPF_PTR_POISON ((void *)((0xeB9FUL << 2) + POISON_POINTER_DELTA))
|
||||
|
||||
const struct bpf_func_proto bpf_kptr_xchg_proto = {
|
||||
.func = bpf_kptr_xchg,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_PTR_TO_BTF_ID_OR_NULL,
|
||||
.ret_btf_id = BPF_PTR_POISON,
|
||||
.arg1_type = ARG_PTR_TO_KPTR,
|
||||
.arg2_type = ARG_PTR_TO_BTF_ID_OR_NULL | OBJ_RELEASE,
|
||||
.arg2_btf_id = BPF_PTR_POISON,
|
||||
};
|
||||
|
||||
const struct bpf_func_proto bpf_get_current_task_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_current_task_btf_proto __weak;
|
||||
const struct bpf_func_proto bpf_probe_read_user_proto __weak;
|
||||
@ -1452,6 +1474,8 @@ bpf_base_func_proto(enum bpf_func_id func_id)
|
||||
return &bpf_timer_start_proto;
|
||||
case BPF_FUNC_timer_cancel:
|
||||
return &bpf_timer_cancel_proto;
|
||||
case BPF_FUNC_kptr_xchg:
|
||||
return &bpf_kptr_xchg_proto;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/slab.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
#ifdef CONFIG_CGROUP_BPF
|
||||
|
||||
@ -446,7 +447,8 @@ static void cgroup_storage_seq_show_elem(struct bpf_map *map, void *key,
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int cgroup_storage_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(cgroup_storage_map_btf_ids, struct,
|
||||
bpf_cgroup_storage_map)
|
||||
const struct bpf_map_ops cgroup_storage_map_ops = {
|
||||
.map_alloc = cgroup_storage_map_alloc,
|
||||
.map_free = cgroup_storage_map_free,
|
||||
@ -456,8 +458,7 @@ const struct bpf_map_ops cgroup_storage_map_ops = {
|
||||
.map_delete_elem = cgroup_storage_delete_elem,
|
||||
.map_check_btf = cgroup_storage_check_btf,
|
||||
.map_seq_show_elem = cgroup_storage_seq_show_elem,
|
||||
.map_btf_name = "bpf_cgroup_storage_map",
|
||||
.map_btf_id = &cgroup_storage_map_btf_id,
|
||||
.map_btf_id = &cgroup_storage_map_btf_ids[0],
|
||||
};
|
||||
|
||||
int bpf_cgroup_storage_assign(struct bpf_prog_aux *aux, struct bpf_map *_map)
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/vmalloc.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
/* Intermediate node */
|
||||
#define LPM_TREE_NODE_FLAG_IM BIT(0)
|
||||
@ -719,7 +720,7 @@ static int trie_check_btf(const struct bpf_map *map,
|
||||
-EINVAL : 0;
|
||||
}
|
||||
|
||||
static int trie_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(trie_map_btf_ids, struct, lpm_trie)
|
||||
const struct bpf_map_ops trie_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = trie_alloc,
|
||||
@ -732,6 +733,5 @@ const struct bpf_map_ops trie_map_ops = {
|
||||
.map_update_batch = generic_map_update_batch,
|
||||
.map_delete_batch = generic_map_delete_batch,
|
||||
.map_check_btf = trie_check_btf,
|
||||
.map_btf_name = "lpm_trie",
|
||||
.map_btf_id = &trie_map_btf_id,
|
||||
.map_btf_id = &trie_map_btf_ids[0],
|
||||
};
|
||||
|
@ -52,6 +52,7 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
|
||||
inner_map_meta->max_entries = inner_map->max_entries;
|
||||
inner_map_meta->spin_lock_off = inner_map->spin_lock_off;
|
||||
inner_map_meta->timer_off = inner_map->timer_off;
|
||||
inner_map_meta->kptr_off_tab = bpf_map_copy_kptr_off_tab(inner_map);
|
||||
if (inner_map->btf) {
|
||||
btf_get(inner_map->btf);
|
||||
inner_map_meta->btf = inner_map->btf;
|
||||
@ -71,6 +72,7 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
|
||||
|
||||
void bpf_map_meta_free(struct bpf_map *map_meta)
|
||||
{
|
||||
bpf_map_free_kptr_off_tab(map_meta);
|
||||
btf_put(map_meta->btf);
|
||||
kfree(map_meta);
|
||||
}
|
||||
@ -83,7 +85,8 @@ bool bpf_map_meta_equal(const struct bpf_map *meta0,
|
||||
meta0->key_size == meta1->key_size &&
|
||||
meta0->value_size == meta1->value_size &&
|
||||
meta0->timer_off == meta1->timer_off &&
|
||||
meta0->map_flags == meta1->map_flags;
|
||||
meta0->map_flags == meta1->map_flags &&
|
||||
bpf_map_equal_kptr_off_tab(meta0, meta1);
|
||||
}
|
||||
|
||||
void *bpf_map_fd_get_ptr(struct bpf_map *map,
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/btf_ids.h>
|
||||
#include "percpu_freelist.h"
|
||||
|
||||
#define QUEUE_STACK_CREATE_FLAG_MASK \
|
||||
@ -247,7 +248,7 @@ static int queue_stack_map_get_next_key(struct bpf_map *map, void *key,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int queue_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(queue_map_btf_ids, struct, bpf_queue_stack)
|
||||
const struct bpf_map_ops queue_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = queue_stack_map_alloc_check,
|
||||
@ -260,11 +261,9 @@ const struct bpf_map_ops queue_map_ops = {
|
||||
.map_pop_elem = queue_map_pop_elem,
|
||||
.map_peek_elem = queue_map_peek_elem,
|
||||
.map_get_next_key = queue_stack_map_get_next_key,
|
||||
.map_btf_name = "bpf_queue_stack",
|
||||
.map_btf_id = &queue_map_btf_id,
|
||||
.map_btf_id = &queue_map_btf_ids[0],
|
||||
};
|
||||
|
||||
static int stack_map_btf_id;
|
||||
const struct bpf_map_ops stack_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = queue_stack_map_alloc_check,
|
||||
@ -277,6 +276,5 @@ const struct bpf_map_ops stack_map_ops = {
|
||||
.map_pop_elem = stack_map_pop_elem,
|
||||
.map_peek_elem = stack_map_peek_elem,
|
||||
.map_get_next_key = queue_stack_map_get_next_key,
|
||||
.map_btf_name = "bpf_queue_stack",
|
||||
.map_btf_id = &stack_map_btf_id,
|
||||
.map_btf_id = &queue_map_btf_ids[0],
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/sock_diag.h>
|
||||
#include <net/sock_reuseport.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
struct reuseport_array {
|
||||
struct bpf_map map;
|
||||
@ -337,7 +338,7 @@ static int reuseport_array_get_next_key(struct bpf_map *map, void *key,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reuseport_array_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(reuseport_array_map_btf_ids, struct, reuseport_array)
|
||||
const struct bpf_map_ops reuseport_array_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = reuseport_array_alloc_check,
|
||||
@ -346,6 +347,5 @@ const struct bpf_map_ops reuseport_array_ops = {
|
||||
.map_lookup_elem = reuseport_array_lookup_elem,
|
||||
.map_get_next_key = reuseport_array_get_next_key,
|
||||
.map_delete_elem = reuseport_array_delete_elem,
|
||||
.map_btf_name = "reuseport_array",
|
||||
.map_btf_id = &reuseport_array_map_btf_id,
|
||||
.map_btf_id = &reuseport_array_map_btf_ids[0],
|
||||
};
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/poll.h>
|
||||
#include <linux/kmemleak.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
#define RINGBUF_CREATE_FLAG_MASK (BPF_F_NUMA_NODE)
|
||||
|
||||
@ -263,7 +264,7 @@ static __poll_t ringbuf_map_poll(struct bpf_map *map, struct file *filp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ringbuf_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(ringbuf_map_btf_ids, struct, bpf_ringbuf_map)
|
||||
const struct bpf_map_ops ringbuf_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = ringbuf_map_alloc,
|
||||
@ -274,8 +275,7 @@ const struct bpf_map_ops ringbuf_map_ops = {
|
||||
.map_update_elem = ringbuf_map_update_elem,
|
||||
.map_delete_elem = ringbuf_map_delete_elem,
|
||||
.map_get_next_key = ringbuf_map_get_next_key,
|
||||
.map_btf_name = "bpf_ringbuf_map",
|
||||
.map_btf_id = &ringbuf_map_btf_id,
|
||||
.map_btf_id = &ringbuf_map_btf_ids[0],
|
||||
};
|
||||
|
||||
/* Given pointer to ring buffer record metadata and struct bpf_ringbuf itself,
|
||||
@ -404,7 +404,7 @@ BPF_CALL_2(bpf_ringbuf_submit, void *, sample, u64, flags)
|
||||
const struct bpf_func_proto bpf_ringbuf_submit_proto = {
|
||||
.func = bpf_ringbuf_submit,
|
||||
.ret_type = RET_VOID,
|
||||
.arg1_type = ARG_PTR_TO_ALLOC_MEM,
|
||||
.arg1_type = ARG_PTR_TO_ALLOC_MEM | OBJ_RELEASE,
|
||||
.arg2_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
@ -417,7 +417,7 @@ BPF_CALL_2(bpf_ringbuf_discard, void *, sample, u64, flags)
|
||||
const struct bpf_func_proto bpf_ringbuf_discard_proto = {
|
||||
.func = bpf_ringbuf_discard,
|
||||
.ret_type = RET_VOID,
|
||||
.arg1_type = ARG_PTR_TO_ALLOC_MEM,
|
||||
.arg1_type = ARG_PTR_TO_ALLOC_MEM | OBJ_RELEASE,
|
||||
.arg2_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
|
@ -654,7 +654,7 @@ static void stack_map_free(struct bpf_map *map)
|
||||
put_callchain_buffers();
|
||||
}
|
||||
|
||||
static int stack_trace_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(stack_trace_map_btf_ids, struct, bpf_stack_map)
|
||||
const struct bpf_map_ops stack_trace_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = stack_map_alloc,
|
||||
@ -664,6 +664,5 @@ const struct bpf_map_ops stack_trace_map_ops = {
|
||||
.map_update_elem = stack_map_update_elem,
|
||||
.map_delete_elem = stack_map_delete_elem,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "bpf_stack_map",
|
||||
.map_btf_id = &stack_trace_map_btf_id,
|
||||
.map_btf_id = &stack_trace_map_btf_ids[0],
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <linux/bpf_trace.h>
|
||||
#include <linux/bpf_lirc.h>
|
||||
#include <linux/bpf_verifier.h>
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/slab.h>
|
||||
@ -29,6 +30,7 @@
|
||||
#include <linux/pgtable.h>
|
||||
#include <linux/bpf_lsm.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/bpf-netns.h>
|
||||
#include <linux/rcupdate_trace.h>
|
||||
#include <linux/memcontrol.h>
|
||||
@ -473,14 +475,128 @@ static void bpf_map_release_memcg(struct bpf_map *map)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int bpf_map_kptr_off_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct bpf_map_value_off_desc *off_desc1 = a, *off_desc2 = b;
|
||||
|
||||
if (off_desc1->offset < off_desc2->offset)
|
||||
return -1;
|
||||
else if (off_desc1->offset > off_desc2->offset)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bpf_map_value_off_desc *bpf_map_kptr_off_contains(struct bpf_map *map, u32 offset)
|
||||
{
|
||||
/* Since members are iterated in btf_find_field in increasing order,
|
||||
* offsets appended to kptr_off_tab are in increasing order, so we can
|
||||
* do bsearch to find exact match.
|
||||
*/
|
||||
struct bpf_map_value_off *tab;
|
||||
|
||||
if (!map_value_has_kptrs(map))
|
||||
return NULL;
|
||||
tab = map->kptr_off_tab;
|
||||
return bsearch(&offset, tab->off, tab->nr_off, sizeof(tab->off[0]), bpf_map_kptr_off_cmp);
|
||||
}
|
||||
|
||||
void bpf_map_free_kptr_off_tab(struct bpf_map *map)
|
||||
{
|
||||
struct bpf_map_value_off *tab = map->kptr_off_tab;
|
||||
int i;
|
||||
|
||||
if (!map_value_has_kptrs(map))
|
||||
return;
|
||||
for (i = 0; i < tab->nr_off; i++) {
|
||||
if (tab->off[i].kptr.module)
|
||||
module_put(tab->off[i].kptr.module);
|
||||
btf_put(tab->off[i].kptr.btf);
|
||||
}
|
||||
kfree(tab);
|
||||
map->kptr_off_tab = NULL;
|
||||
}
|
||||
|
||||
struct bpf_map_value_off *bpf_map_copy_kptr_off_tab(const struct bpf_map *map)
|
||||
{
|
||||
struct bpf_map_value_off *tab = map->kptr_off_tab, *new_tab;
|
||||
int size, i;
|
||||
|
||||
if (!map_value_has_kptrs(map))
|
||||
return ERR_PTR(-ENOENT);
|
||||
size = offsetof(struct bpf_map_value_off, off[tab->nr_off]);
|
||||
new_tab = kmemdup(tab, size, GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!new_tab)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
/* Do a deep copy of the kptr_off_tab */
|
||||
for (i = 0; i < tab->nr_off; i++) {
|
||||
btf_get(tab->off[i].kptr.btf);
|
||||
if (tab->off[i].kptr.module && !try_module_get(tab->off[i].kptr.module)) {
|
||||
while (i--) {
|
||||
if (tab->off[i].kptr.module)
|
||||
module_put(tab->off[i].kptr.module);
|
||||
btf_put(tab->off[i].kptr.btf);
|
||||
}
|
||||
kfree(new_tab);
|
||||
return ERR_PTR(-ENXIO);
|
||||
}
|
||||
}
|
||||
return new_tab;
|
||||
}
|
||||
|
||||
bool bpf_map_equal_kptr_off_tab(const struct bpf_map *map_a, const struct bpf_map *map_b)
|
||||
{
|
||||
struct bpf_map_value_off *tab_a = map_a->kptr_off_tab, *tab_b = map_b->kptr_off_tab;
|
||||
bool a_has_kptr = map_value_has_kptrs(map_a), b_has_kptr = map_value_has_kptrs(map_b);
|
||||
int size;
|
||||
|
||||
if (!a_has_kptr && !b_has_kptr)
|
||||
return true;
|
||||
if (a_has_kptr != b_has_kptr)
|
||||
return false;
|
||||
if (tab_a->nr_off != tab_b->nr_off)
|
||||
return false;
|
||||
size = offsetof(struct bpf_map_value_off, off[tab_a->nr_off]);
|
||||
return !memcmp(tab_a, tab_b, size);
|
||||
}
|
||||
|
||||
/* Caller must ensure map_value_has_kptrs is true. Note that this function can
|
||||
* be called on a map value while the map_value is visible to BPF programs, as
|
||||
* it ensures the correct synchronization, and we already enforce the same using
|
||||
* the bpf_kptr_xchg helper on the BPF program side for referenced kptrs.
|
||||
*/
|
||||
void bpf_map_free_kptrs(struct bpf_map *map, void *map_value)
|
||||
{
|
||||
struct bpf_map_value_off *tab = map->kptr_off_tab;
|
||||
unsigned long *btf_id_ptr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tab->nr_off; i++) {
|
||||
struct bpf_map_value_off_desc *off_desc = &tab->off[i];
|
||||
unsigned long old_ptr;
|
||||
|
||||
btf_id_ptr = map_value + off_desc->offset;
|
||||
if (off_desc->type == BPF_KPTR_UNREF) {
|
||||
u64 *p = (u64 *)btf_id_ptr;
|
||||
|
||||
WRITE_ONCE(p, 0);
|
||||
continue;
|
||||
}
|
||||
old_ptr = xchg(btf_id_ptr, 0);
|
||||
off_desc->kptr.dtor((void *)old_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* called from workqueue */
|
||||
static void bpf_map_free_deferred(struct work_struct *work)
|
||||
{
|
||||
struct bpf_map *map = container_of(work, struct bpf_map, work);
|
||||
|
||||
security_bpf_map_free(map);
|
||||
kfree(map->off_arr);
|
||||
bpf_map_release_memcg(map);
|
||||
/* implementation dependent freeing */
|
||||
/* implementation dependent freeing, map_free callback also does
|
||||
* bpf_map_free_kptr_off_tab, if needed.
|
||||
*/
|
||||
map->ops->map_free(map);
|
||||
}
|
||||
|
||||
@ -640,7 +756,7 @@ static int bpf_map_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
int err;
|
||||
|
||||
if (!map->ops->map_mmap || map_value_has_spin_lock(map) ||
|
||||
map_value_has_timer(map))
|
||||
map_value_has_timer(map) || map_value_has_kptrs(map))
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (!(vma->vm_flags & VM_SHARED))
|
||||
@ -767,6 +883,84 @@ int map_check_no_btf(const struct bpf_map *map,
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int map_off_arr_cmp(const void *_a, const void *_b, const void *priv)
|
||||
{
|
||||
const u32 a = *(const u32 *)_a;
|
||||
const u32 b = *(const u32 *)_b;
|
||||
|
||||
if (a < b)
|
||||
return -1;
|
||||
else if (a > b)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void map_off_arr_swap(void *_a, void *_b, int size, const void *priv)
|
||||
{
|
||||
struct bpf_map *map = (struct bpf_map *)priv;
|
||||
u32 *off_base = map->off_arr->field_off;
|
||||
u32 *a = _a, *b = _b;
|
||||
u8 *sz_a, *sz_b;
|
||||
|
||||
sz_a = map->off_arr->field_sz + (a - off_base);
|
||||
sz_b = map->off_arr->field_sz + (b - off_base);
|
||||
|
||||
swap(*a, *b);
|
||||
swap(*sz_a, *sz_b);
|
||||
}
|
||||
|
||||
static int bpf_map_alloc_off_arr(struct bpf_map *map)
|
||||
{
|
||||
bool has_spin_lock = map_value_has_spin_lock(map);
|
||||
bool has_timer = map_value_has_timer(map);
|
||||
bool has_kptrs = map_value_has_kptrs(map);
|
||||
struct bpf_map_off_arr *off_arr;
|
||||
u32 i;
|
||||
|
||||
if (!has_spin_lock && !has_timer && !has_kptrs) {
|
||||
map->off_arr = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
off_arr = kmalloc(sizeof(*map->off_arr), GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!off_arr)
|
||||
return -ENOMEM;
|
||||
map->off_arr = off_arr;
|
||||
|
||||
off_arr->cnt = 0;
|
||||
if (has_spin_lock) {
|
||||
i = off_arr->cnt;
|
||||
|
||||
off_arr->field_off[i] = map->spin_lock_off;
|
||||
off_arr->field_sz[i] = sizeof(struct bpf_spin_lock);
|
||||
off_arr->cnt++;
|
||||
}
|
||||
if (has_timer) {
|
||||
i = off_arr->cnt;
|
||||
|
||||
off_arr->field_off[i] = map->timer_off;
|
||||
off_arr->field_sz[i] = sizeof(struct bpf_timer);
|
||||
off_arr->cnt++;
|
||||
}
|
||||
if (has_kptrs) {
|
||||
struct bpf_map_value_off *tab = map->kptr_off_tab;
|
||||
u32 *off = &off_arr->field_off[off_arr->cnt];
|
||||
u8 *sz = &off_arr->field_sz[off_arr->cnt];
|
||||
|
||||
for (i = 0; i < tab->nr_off; i++) {
|
||||
*off++ = tab->off[i].offset;
|
||||
*sz++ = sizeof(u64);
|
||||
}
|
||||
off_arr->cnt += tab->nr_off;
|
||||
}
|
||||
|
||||
if (off_arr->cnt == 1)
|
||||
return 0;
|
||||
sort_r(off_arr->field_off, off_arr->cnt, sizeof(off_arr->field_off[0]),
|
||||
map_off_arr_cmp, map_off_arr_swap, map);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int map_check_btf(struct bpf_map *map, const struct btf *btf,
|
||||
u32 btf_key_id, u32 btf_value_id)
|
||||
{
|
||||
@ -820,9 +1014,33 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (map->ops->map_check_btf)
|
||||
ret = map->ops->map_check_btf(map, btf, key_type, value_type);
|
||||
map->kptr_off_tab = btf_parse_kptrs(btf, value_type);
|
||||
if (map_value_has_kptrs(map)) {
|
||||
if (!bpf_capable()) {
|
||||
ret = -EPERM;
|
||||
goto free_map_tab;
|
||||
}
|
||||
if (map->map_flags & (BPF_F_RDONLY_PROG | BPF_F_WRONLY_PROG)) {
|
||||
ret = -EACCES;
|
||||
goto free_map_tab;
|
||||
}
|
||||
if (map->map_type != BPF_MAP_TYPE_HASH &&
|
||||
map->map_type != BPF_MAP_TYPE_LRU_HASH &&
|
||||
map->map_type != BPF_MAP_TYPE_ARRAY) {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto free_map_tab;
|
||||
}
|
||||
}
|
||||
|
||||
if (map->ops->map_check_btf) {
|
||||
ret = map->ops->map_check_btf(map, btf, key_type, value_type);
|
||||
if (ret < 0)
|
||||
goto free_map_tab;
|
||||
}
|
||||
|
||||
return ret;
|
||||
free_map_tab:
|
||||
bpf_map_free_kptr_off_tab(map);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -912,10 +1130,14 @@ static int map_create(union bpf_attr *attr)
|
||||
attr->btf_vmlinux_value_type_id;
|
||||
}
|
||||
|
||||
err = security_bpf_map_alloc(map);
|
||||
err = bpf_map_alloc_off_arr(map);
|
||||
if (err)
|
||||
goto free_map;
|
||||
|
||||
err = security_bpf_map_alloc(map);
|
||||
if (err)
|
||||
goto free_map_off_arr;
|
||||
|
||||
err = bpf_map_alloc_id(map);
|
||||
if (err)
|
||||
goto free_map_sec;
|
||||
@ -938,6 +1160,8 @@ static int map_create(union bpf_attr *attr)
|
||||
|
||||
free_map_sec:
|
||||
security_bpf_map_free(map);
|
||||
free_map_off_arr:
|
||||
kfree(map->off_arr);
|
||||
free_map:
|
||||
btf_put(map->btf);
|
||||
map->ops->map_free(map);
|
||||
@ -1639,7 +1863,7 @@ static int map_freeze(const union bpf_attr *attr)
|
||||
return PTR_ERR(map);
|
||||
|
||||
if (map->map_type == BPF_MAP_TYPE_STRUCT_OPS ||
|
||||
map_value_has_timer(map)) {
|
||||
map_value_has_timer(map) || map_value_has_kptrs(map)) {
|
||||
fdput(f);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
@ -3030,66 +3254,45 @@ static int bpf_perf_link_attach(const union bpf_attr *attr, struct bpf_prog *pro
|
||||
}
|
||||
#endif /* CONFIG_PERF_EVENTS */
|
||||
|
||||
#define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd
|
||||
|
||||
static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
static int bpf_raw_tp_link_attach(struct bpf_prog *prog,
|
||||
const char __user *user_tp_name)
|
||||
{
|
||||
struct bpf_link_primer link_primer;
|
||||
struct bpf_raw_tp_link *link;
|
||||
struct bpf_raw_event_map *btp;
|
||||
struct bpf_prog *prog;
|
||||
const char *tp_name;
|
||||
char buf[128];
|
||||
int err;
|
||||
|
||||
if (CHECK_ATTR(BPF_RAW_TRACEPOINT_OPEN))
|
||||
return -EINVAL;
|
||||
|
||||
prog = bpf_prog_get(attr->raw_tracepoint.prog_fd);
|
||||
if (IS_ERR(prog))
|
||||
return PTR_ERR(prog);
|
||||
|
||||
switch (prog->type) {
|
||||
case BPF_PROG_TYPE_TRACING:
|
||||
case BPF_PROG_TYPE_EXT:
|
||||
case BPF_PROG_TYPE_LSM:
|
||||
if (attr->raw_tracepoint.name) {
|
||||
if (user_tp_name)
|
||||
/* The attach point for this category of programs
|
||||
* should be specified via btf_id during program load.
|
||||
*/
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
return -EINVAL;
|
||||
if (prog->type == BPF_PROG_TYPE_TRACING &&
|
||||
prog->expected_attach_type == BPF_TRACE_RAW_TP) {
|
||||
tp_name = prog->aux->attach_func_name;
|
||||
break;
|
||||
}
|
||||
err = bpf_tracing_prog_attach(prog, 0, 0);
|
||||
if (err >= 0)
|
||||
return err;
|
||||
goto out_put_prog;
|
||||
return bpf_tracing_prog_attach(prog, 0, 0);
|
||||
case BPF_PROG_TYPE_RAW_TRACEPOINT:
|
||||
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
|
||||
if (strncpy_from_user(buf,
|
||||
u64_to_user_ptr(attr->raw_tracepoint.name),
|
||||
sizeof(buf) - 1) < 0) {
|
||||
err = -EFAULT;
|
||||
goto out_put_prog;
|
||||
}
|
||||
if (strncpy_from_user(buf, user_tp_name, sizeof(buf) - 1) < 0)
|
||||
return -EFAULT;
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
tp_name = buf;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btp = bpf_get_raw_tracepoint(tp_name);
|
||||
if (!btp) {
|
||||
err = -ENOENT;
|
||||
goto out_put_prog;
|
||||
}
|
||||
if (!btp)
|
||||
return -ENOENT;
|
||||
|
||||
link = kzalloc(sizeof(*link), GFP_USER);
|
||||
if (!link) {
|
||||
@ -3116,11 +3319,29 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
|
||||
out_put_btp:
|
||||
bpf_put_raw_tracepoint(btp);
|
||||
out_put_prog:
|
||||
bpf_prog_put(prog);
|
||||
return err;
|
||||
}
|
||||
|
||||
#define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd
|
||||
|
||||
static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_prog *prog;
|
||||
int fd;
|
||||
|
||||
if (CHECK_ATTR(BPF_RAW_TRACEPOINT_OPEN))
|
||||
return -EINVAL;
|
||||
|
||||
prog = bpf_prog_get(attr->raw_tracepoint.prog_fd);
|
||||
if (IS_ERR(prog))
|
||||
return PTR_ERR(prog);
|
||||
|
||||
fd = bpf_raw_tp_link_attach(prog, u64_to_user_ptr(attr->raw_tracepoint.name));
|
||||
if (fd < 0)
|
||||
bpf_prog_put(prog);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,
|
||||
enum bpf_attach_type attach_type)
|
||||
{
|
||||
@ -3189,7 +3410,13 @@ attach_type_to_prog_type(enum bpf_attach_type attach_type)
|
||||
case BPF_CGROUP_SETSOCKOPT:
|
||||
return BPF_PROG_TYPE_CGROUP_SOCKOPT;
|
||||
case BPF_TRACE_ITER:
|
||||
case BPF_TRACE_RAW_TP:
|
||||
case BPF_TRACE_FENTRY:
|
||||
case BPF_TRACE_FEXIT:
|
||||
case BPF_MODIFY_RETURN:
|
||||
return BPF_PROG_TYPE_TRACING;
|
||||
case BPF_LSM_MAC:
|
||||
return BPF_PROG_TYPE_LSM;
|
||||
case BPF_SK_LOOKUP:
|
||||
return BPF_PROG_TYPE_SK_LOOKUP;
|
||||
case BPF_XDP:
|
||||
@ -4246,21 +4473,6 @@ err_put:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tracing_bpf_link_attach(const union bpf_attr *attr, bpfptr_t uattr,
|
||||
struct bpf_prog *prog)
|
||||
{
|
||||
if (attr->link_create.attach_type != prog->expected_attach_type)
|
||||
return -EINVAL;
|
||||
|
||||
if (prog->expected_attach_type == BPF_TRACE_ITER)
|
||||
return bpf_iter_link_attach(attr, uattr, prog);
|
||||
else if (prog->type == BPF_PROG_TYPE_EXT)
|
||||
return bpf_tracing_prog_attach(prog,
|
||||
attr->link_create.target_fd,
|
||||
attr->link_create.target_btf_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define BPF_LINK_CREATE_LAST_FIELD link_create.kprobe_multi.cookies
|
||||
static int link_create(union bpf_attr *attr, bpfptr_t uattr)
|
||||
{
|
||||
@ -4282,15 +4494,13 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
|
||||
|
||||
switch (prog->type) {
|
||||
case BPF_PROG_TYPE_EXT:
|
||||
ret = tracing_bpf_link_attach(attr, uattr, prog);
|
||||
goto out;
|
||||
break;
|
||||
case BPF_PROG_TYPE_PERF_EVENT:
|
||||
case BPF_PROG_TYPE_TRACEPOINT:
|
||||
if (attr->link_create.attach_type != BPF_PERF_EVENT) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ptype = prog->type;
|
||||
break;
|
||||
case BPF_PROG_TYPE_KPROBE:
|
||||
if (attr->link_create.attach_type != BPF_PERF_EVENT &&
|
||||
@ -4298,7 +4508,6 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ptype = prog->type;
|
||||
break;
|
||||
default:
|
||||
ptype = attach_type_to_prog_type(attr->link_create.attach_type);
|
||||
@ -4309,7 +4518,7 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ptype) {
|
||||
switch (prog->type) {
|
||||
case BPF_PROG_TYPE_CGROUP_SKB:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK:
|
||||
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
||||
@ -4319,8 +4528,25 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
|
||||
case BPF_PROG_TYPE_CGROUP_SOCKOPT:
|
||||
ret = cgroup_bpf_link_attach(attr, prog);
|
||||
break;
|
||||
case BPF_PROG_TYPE_EXT:
|
||||
ret = bpf_tracing_prog_attach(prog,
|
||||
attr->link_create.target_fd,
|
||||
attr->link_create.target_btf_id);
|
||||
break;
|
||||
case BPF_PROG_TYPE_LSM:
|
||||
case BPF_PROG_TYPE_TRACING:
|
||||
ret = tracing_bpf_link_attach(attr, uattr, prog);
|
||||
if (attr->link_create.attach_type != prog->expected_attach_type) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (prog->expected_attach_type == BPF_TRACE_RAW_TP)
|
||||
ret = bpf_raw_tp_link_attach(prog, NULL);
|
||||
else if (prog->expected_attach_type == BPF_TRACE_ITER)
|
||||
ret = bpf_iter_link_attach(attr, uattr, prog);
|
||||
else
|
||||
ret = bpf_tracing_prog_attach(prog,
|
||||
attr->link_create.target_fd,
|
||||
attr->link_create.target_btf_id);
|
||||
break;
|
||||
case BPF_PROG_TYPE_FLOW_DISSECTOR:
|
||||
case BPF_PROG_TYPE_SK_LOOKUP:
|
||||
@ -4908,3 +5134,90 @@ const struct bpf_verifier_ops bpf_syscall_verifier_ops = {
|
||||
const struct bpf_prog_ops bpf_syscall_prog_ops = {
|
||||
.test_run = bpf_prog_test_run_syscall,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static int bpf_stats_handler(struct ctl_table *table, int write,
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct static_key *key = (struct static_key *)table->data;
|
||||
static int saved_val;
|
||||
int val, ret;
|
||||
struct ctl_table tmp = {
|
||||
.data = &val,
|
||||
.maxlen = sizeof(val),
|
||||
.mode = table->mode,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = SYSCTL_ONE,
|
||||
};
|
||||
|
||||
if (write && !capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
mutex_lock(&bpf_stats_enabled_mutex);
|
||||
val = saved_val;
|
||||
ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
|
||||
if (write && !ret && val != saved_val) {
|
||||
if (val)
|
||||
static_key_slow_inc(key);
|
||||
else
|
||||
static_key_slow_dec(key);
|
||||
saved_val = val;
|
||||
}
|
||||
mutex_unlock(&bpf_stats_enabled_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __weak unpriv_ebpf_notify(int new_state)
|
||||
{
|
||||
}
|
||||
|
||||
static int bpf_unpriv_handler(struct ctl_table *table, int write,
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int ret, unpriv_enable = *(int *)table->data;
|
||||
bool locked_state = unpriv_enable == 1;
|
||||
struct ctl_table tmp = *table;
|
||||
|
||||
if (write && !capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
tmp.data = &unpriv_enable;
|
||||
ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
|
||||
if (write && !ret) {
|
||||
if (locked_state && unpriv_enable != 1)
|
||||
return -EPERM;
|
||||
*(int *)table->data = unpriv_enable;
|
||||
}
|
||||
|
||||
unpriv_ebpf_notify(unpriv_enable);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ctl_table bpf_syscall_table[] = {
|
||||
{
|
||||
.procname = "unprivileged_bpf_disabled",
|
||||
.data = &sysctl_unprivileged_bpf_disabled,
|
||||
.maxlen = sizeof(sysctl_unprivileged_bpf_disabled),
|
||||
.mode = 0644,
|
||||
.proc_handler = bpf_unpriv_handler,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = SYSCTL_TWO,
|
||||
},
|
||||
{
|
||||
.procname = "bpf_stats_enabled",
|
||||
.data = &bpf_stats_enabled_key.key,
|
||||
.maxlen = sizeof(bpf_stats_enabled_key),
|
||||
.mode = 0644,
|
||||
.proc_handler = bpf_stats_handler,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int __init bpf_syscall_sysctl_init(void)
|
||||
{
|
||||
register_sysctl_init("kernel", bpf_syscall_table);
|
||||
return 0;
|
||||
}
|
||||
late_initcall(bpf_syscall_sysctl_init);
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
@ -99,7 +99,6 @@ static int __task_seq_show(struct seq_file *seq, struct task_struct *task,
|
||||
if (!prog)
|
||||
return 0;
|
||||
|
||||
meta.seq = seq;
|
||||
ctx.meta = &meta;
|
||||
ctx.task = task;
|
||||
return bpf_iter_run_prog(prog, &ctx);
|
||||
|
@ -245,6 +245,7 @@ struct bpf_call_arg_meta {
|
||||
struct bpf_map *map_ptr;
|
||||
bool raw_mode;
|
||||
bool pkt_access;
|
||||
u8 release_regno;
|
||||
int regno;
|
||||
int access_size;
|
||||
int mem_size;
|
||||
@ -257,6 +258,7 @@ struct bpf_call_arg_meta {
|
||||
struct btf *ret_btf;
|
||||
u32 ret_btf_id;
|
||||
u32 subprogno;
|
||||
struct bpf_map_value_off_desc *kptr_off_desc;
|
||||
};
|
||||
|
||||
struct btf *btf_vmlinux;
|
||||
@ -471,17 +473,6 @@ static bool type_may_be_null(u32 type)
|
||||
return type & PTR_MAYBE_NULL;
|
||||
}
|
||||
|
||||
/* Determine whether the function releases some resources allocated by another
|
||||
* function call. The first reference type argument will be assumed to be
|
||||
* released by release_reference().
|
||||
*/
|
||||
static bool is_release_function(enum bpf_func_id func_id)
|
||||
{
|
||||
return func_id == BPF_FUNC_sk_release ||
|
||||
func_id == BPF_FUNC_ringbuf_submit ||
|
||||
func_id == BPF_FUNC_ringbuf_discard;
|
||||
}
|
||||
|
||||
static bool may_be_acquire_function(enum bpf_func_id func_id)
|
||||
{
|
||||
return func_id == BPF_FUNC_sk_lookup_tcp ||
|
||||
@ -499,7 +490,8 @@ static bool is_acquire_function(enum bpf_func_id func_id,
|
||||
if (func_id == BPF_FUNC_sk_lookup_tcp ||
|
||||
func_id == BPF_FUNC_sk_lookup_udp ||
|
||||
func_id == BPF_FUNC_skc_lookup_tcp ||
|
||||
func_id == BPF_FUNC_ringbuf_reserve)
|
||||
func_id == BPF_FUNC_ringbuf_reserve ||
|
||||
func_id == BPF_FUNC_kptr_xchg)
|
||||
return true;
|
||||
|
||||
if (func_id == BPF_FUNC_map_lookup_elem &&
|
||||
@ -575,6 +567,8 @@ static const char *reg_type_str(struct bpf_verifier_env *env,
|
||||
strncpy(prefix, "user_", 32);
|
||||
if (type & MEM_PERCPU)
|
||||
strncpy(prefix, "percpu_", 32);
|
||||
if (type & PTR_UNTRUSTED)
|
||||
strncpy(prefix, "untrusted_", 32);
|
||||
|
||||
snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s",
|
||||
prefix, str[base_type(type)], postfix);
|
||||
@ -3211,7 +3205,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum stack_access_src {
|
||||
enum bpf_access_src {
|
||||
ACCESS_DIRECT = 1, /* the access is performed by an instruction */
|
||||
ACCESS_HELPER = 2, /* the access is performed by a helper */
|
||||
};
|
||||
@ -3219,7 +3213,7 @@ enum stack_access_src {
|
||||
static int check_stack_range_initialized(struct bpf_verifier_env *env,
|
||||
int regno, int off, int access_size,
|
||||
bool zero_size_allowed,
|
||||
enum stack_access_src type,
|
||||
enum bpf_access_src type,
|
||||
struct bpf_call_arg_meta *meta);
|
||||
|
||||
static struct bpf_reg_state *reg_state(struct bpf_verifier_env *env, int regno)
|
||||
@ -3469,9 +3463,175 @@ static int check_mem_region_access(struct bpf_verifier_env *env, u32 regno,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __check_ptr_off_reg(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg, int regno,
|
||||
bool fixed_off_ok)
|
||||
{
|
||||
/* Access to this pointer-typed register or passing it to a helper
|
||||
* is only allowed in its original, unmodified form.
|
||||
*/
|
||||
|
||||
if (reg->off < 0) {
|
||||
verbose(env, "negative offset %s ptr R%d off=%d disallowed\n",
|
||||
reg_type_str(env, reg->type), regno, reg->off);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!fixed_off_ok && reg->off) {
|
||||
verbose(env, "dereference of modified %s ptr R%d off=%d disallowed\n",
|
||||
reg_type_str(env, reg->type), regno, reg->off);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!tnum_is_const(reg->var_off) || reg->var_off.value) {
|
||||
char tn_buf[48];
|
||||
|
||||
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
|
||||
verbose(env, "variable %s access var_off=%s disallowed\n",
|
||||
reg_type_str(env, reg->type), tn_buf);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_ptr_off_reg(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg, int regno)
|
||||
{
|
||||
return __check_ptr_off_reg(env, reg, regno, false);
|
||||
}
|
||||
|
||||
static int map_kptr_match_type(struct bpf_verifier_env *env,
|
||||
struct bpf_map_value_off_desc *off_desc,
|
||||
struct bpf_reg_state *reg, u32 regno)
|
||||
{
|
||||
const char *targ_name = kernel_type_name(off_desc->kptr.btf, off_desc->kptr.btf_id);
|
||||
int perm_flags = PTR_MAYBE_NULL;
|
||||
const char *reg_name = "";
|
||||
|
||||
/* Only unreferenced case accepts untrusted pointers */
|
||||
if (off_desc->type == BPF_KPTR_UNREF)
|
||||
perm_flags |= PTR_UNTRUSTED;
|
||||
|
||||
if (base_type(reg->type) != PTR_TO_BTF_ID || (type_flag(reg->type) & ~perm_flags))
|
||||
goto bad_type;
|
||||
|
||||
if (!btf_is_kernel(reg->btf)) {
|
||||
verbose(env, "R%d must point to kernel BTF\n", regno);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* We need to verify reg->type and reg->btf, before accessing reg->btf */
|
||||
reg_name = kernel_type_name(reg->btf, reg->btf_id);
|
||||
|
||||
/* For ref_ptr case, release function check should ensure we get one
|
||||
* referenced PTR_TO_BTF_ID, and that its fixed offset is 0. For the
|
||||
* normal store of unreferenced kptr, we must ensure var_off is zero.
|
||||
* Since ref_ptr cannot be accessed directly by BPF insns, checks for
|
||||
* reg->off and reg->ref_obj_id are not needed here.
|
||||
*/
|
||||
if (__check_ptr_off_reg(env, reg, regno, true))
|
||||
return -EACCES;
|
||||
|
||||
/* A full type match is needed, as BTF can be vmlinux or module BTF, and
|
||||
* we also need to take into account the reg->off.
|
||||
*
|
||||
* We want to support cases like:
|
||||
*
|
||||
* struct foo {
|
||||
* struct bar br;
|
||||
* struct baz bz;
|
||||
* };
|
||||
*
|
||||
* struct foo *v;
|
||||
* v = func(); // PTR_TO_BTF_ID
|
||||
* val->foo = v; // reg->off is zero, btf and btf_id match type
|
||||
* val->bar = &v->br; // reg->off is still zero, but we need to retry with
|
||||
* // first member type of struct after comparison fails
|
||||
* val->baz = &v->bz; // reg->off is non-zero, so struct needs to be walked
|
||||
* // to match type
|
||||
*
|
||||
* In the kptr_ref case, check_func_arg_reg_off already ensures reg->off
|
||||
* is zero. We must also ensure that btf_struct_ids_match does not walk
|
||||
* the struct to match type against first member of struct, i.e. reject
|
||||
* second case from above. Hence, when type is BPF_KPTR_REF, we set
|
||||
* strict mode to true for type match.
|
||||
*/
|
||||
if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, reg->off,
|
||||
off_desc->kptr.btf, off_desc->kptr.btf_id,
|
||||
off_desc->type == BPF_KPTR_REF))
|
||||
goto bad_type;
|
||||
return 0;
|
||||
bad_type:
|
||||
verbose(env, "invalid kptr access, R%d type=%s%s ", regno,
|
||||
reg_type_str(env, reg->type), reg_name);
|
||||
verbose(env, "expected=%s%s", reg_type_str(env, PTR_TO_BTF_ID), targ_name);
|
||||
if (off_desc->type == BPF_KPTR_UNREF)
|
||||
verbose(env, " or %s%s\n", reg_type_str(env, PTR_TO_BTF_ID | PTR_UNTRUSTED),
|
||||
targ_name);
|
||||
else
|
||||
verbose(env, "\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int check_map_kptr_access(struct bpf_verifier_env *env, u32 regno,
|
||||
int value_regno, int insn_idx,
|
||||
struct bpf_map_value_off_desc *off_desc)
|
||||
{
|
||||
struct bpf_insn *insn = &env->prog->insnsi[insn_idx];
|
||||
int class = BPF_CLASS(insn->code);
|
||||
struct bpf_reg_state *val_reg;
|
||||
|
||||
/* Things we already checked for in check_map_access and caller:
|
||||
* - Reject cases where variable offset may touch kptr
|
||||
* - size of access (must be BPF_DW)
|
||||
* - tnum_is_const(reg->var_off)
|
||||
* - off_desc->offset == off + reg->var_off.value
|
||||
*/
|
||||
/* Only BPF_[LDX,STX,ST] | BPF_MEM | BPF_DW is supported */
|
||||
if (BPF_MODE(insn->code) != BPF_MEM) {
|
||||
verbose(env, "kptr in map can only be accessed using BPF_MEM instruction mode\n");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/* We only allow loading referenced kptr, since it will be marked as
|
||||
* untrusted, similar to unreferenced kptr.
|
||||
*/
|
||||
if (class != BPF_LDX && off_desc->type == BPF_KPTR_REF) {
|
||||
verbose(env, "store to referenced kptr disallowed\n");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (class == BPF_LDX) {
|
||||
val_reg = reg_state(env, value_regno);
|
||||
/* We can simply mark the value_regno receiving the pointer
|
||||
* value from map as PTR_TO_BTF_ID, with the correct type.
|
||||
*/
|
||||
mark_btf_ld_reg(env, cur_regs(env), value_regno, PTR_TO_BTF_ID, off_desc->kptr.btf,
|
||||
off_desc->kptr.btf_id, PTR_MAYBE_NULL | PTR_UNTRUSTED);
|
||||
/* For mark_ptr_or_null_reg */
|
||||
val_reg->id = ++env->id_gen;
|
||||
} else if (class == BPF_STX) {
|
||||
val_reg = reg_state(env, value_regno);
|
||||
if (!register_is_null(val_reg) &&
|
||||
map_kptr_match_type(env, off_desc, val_reg, value_regno))
|
||||
return -EACCES;
|
||||
} else if (class == BPF_ST) {
|
||||
if (insn->imm) {
|
||||
verbose(env, "BPF_ST imm must be 0 when storing to kptr at off=%u\n",
|
||||
off_desc->offset);
|
||||
return -EACCES;
|
||||
}
|
||||
} else {
|
||||
verbose(env, "kptr in map can only be accessed using BPF_LDX/BPF_STX/BPF_ST\n");
|
||||
return -EACCES;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check read/write into a map element with possible variable offset */
|
||||
static int check_map_access(struct bpf_verifier_env *env, u32 regno,
|
||||
int off, int size, bool zero_size_allowed)
|
||||
int off, int size, bool zero_size_allowed,
|
||||
enum bpf_access_src src)
|
||||
{
|
||||
struct bpf_verifier_state *vstate = env->cur_state;
|
||||
struct bpf_func_state *state = vstate->frame[vstate->curframe];
|
||||
@ -3507,6 +3667,36 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno,
|
||||
return -EACCES;
|
||||
}
|
||||
}
|
||||
if (map_value_has_kptrs(map)) {
|
||||
struct bpf_map_value_off *tab = map->kptr_off_tab;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tab->nr_off; i++) {
|
||||
u32 p = tab->off[i].offset;
|
||||
|
||||
if (reg->smin_value + off < p + sizeof(u64) &&
|
||||
p < reg->umax_value + off + size) {
|
||||
if (src != ACCESS_DIRECT) {
|
||||
verbose(env, "kptr cannot be accessed indirectly by helper\n");
|
||||
return -EACCES;
|
||||
}
|
||||
if (!tnum_is_const(reg->var_off)) {
|
||||
verbose(env, "kptr access cannot have variable offset\n");
|
||||
return -EACCES;
|
||||
}
|
||||
if (p != off + reg->var_off.value) {
|
||||
verbose(env, "kptr access misaligned expected=%u off=%llu\n",
|
||||
p, off + reg->var_off.value);
|
||||
return -EACCES;
|
||||
}
|
||||
if (size != bpf_size_to_bytes(BPF_DW)) {
|
||||
verbose(env, "kptr access size must be BPF_DW\n");
|
||||
return -EACCES;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -3980,44 +4170,6 @@ static int get_callee_stack_depth(struct bpf_verifier_env *env,
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __check_ptr_off_reg(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg, int regno,
|
||||
bool fixed_off_ok)
|
||||
{
|
||||
/* Access to this pointer-typed register or passing it to a helper
|
||||
* is only allowed in its original, unmodified form.
|
||||
*/
|
||||
|
||||
if (reg->off < 0) {
|
||||
verbose(env, "negative offset %s ptr R%d off=%d disallowed\n",
|
||||
reg_type_str(env, reg->type), regno, reg->off);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!fixed_off_ok && reg->off) {
|
||||
verbose(env, "dereference of modified %s ptr R%d off=%d disallowed\n",
|
||||
reg_type_str(env, reg->type), regno, reg->off);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
if (!tnum_is_const(reg->var_off) || reg->var_off.value) {
|
||||
char tn_buf[48];
|
||||
|
||||
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
|
||||
verbose(env, "variable %s access var_off=%s disallowed\n",
|
||||
reg_type_str(env, reg->type), tn_buf);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_ptr_off_reg(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg, int regno)
|
||||
{
|
||||
return __check_ptr_off_reg(env, reg, regno, false);
|
||||
}
|
||||
|
||||
static int __check_buffer_access(struct bpf_verifier_env *env,
|
||||
const char *buf_info,
|
||||
const struct bpf_reg_state *reg,
|
||||
@ -4224,6 +4376,12 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* If this is an untrusted pointer, all pointers formed by walking it
|
||||
* also inherit the untrusted flag.
|
||||
*/
|
||||
if (type_flag(reg->type) & PTR_UNTRUSTED)
|
||||
flag |= PTR_UNTRUSTED;
|
||||
|
||||
if (atype == BPF_READ && value_regno >= 0)
|
||||
mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag);
|
||||
|
||||
@ -4316,7 +4474,7 @@ static int check_stack_slot_within_bounds(int off,
|
||||
static int check_stack_access_within_bounds(
|
||||
struct bpf_verifier_env *env,
|
||||
int regno, int off, int access_size,
|
||||
enum stack_access_src src, enum bpf_access_type type)
|
||||
enum bpf_access_src src, enum bpf_access_type type)
|
||||
{
|
||||
struct bpf_reg_state *regs = cur_regs(env);
|
||||
struct bpf_reg_state *reg = regs + regno;
|
||||
@ -4412,6 +4570,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
|
||||
if (value_regno >= 0)
|
||||
mark_reg_unknown(env, regs, value_regno);
|
||||
} else if (reg->type == PTR_TO_MAP_VALUE) {
|
||||
struct bpf_map_value_off_desc *kptr_off_desc = NULL;
|
||||
|
||||
if (t == BPF_WRITE && value_regno >= 0 &&
|
||||
is_pointer_value(env, value_regno)) {
|
||||
verbose(env, "R%d leaks addr into map\n", value_regno);
|
||||
@ -4420,8 +4580,16 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
|
||||
err = check_map_access_type(env, regno, off, size, t);
|
||||
if (err)
|
||||
return err;
|
||||
err = check_map_access(env, regno, off, size, false);
|
||||
if (!err && t == BPF_READ && value_regno >= 0) {
|
||||
err = check_map_access(env, regno, off, size, false, ACCESS_DIRECT);
|
||||
if (err)
|
||||
return err;
|
||||
if (tnum_is_const(reg->var_off))
|
||||
kptr_off_desc = bpf_map_kptr_off_contains(reg->map_ptr,
|
||||
off + reg->var_off.value);
|
||||
if (kptr_off_desc) {
|
||||
err = check_map_kptr_access(env, regno, value_regno, insn_idx,
|
||||
kptr_off_desc);
|
||||
} else if (t == BPF_READ && value_regno >= 0) {
|
||||
struct bpf_map *map = reg->map_ptr;
|
||||
|
||||
/* if map is read-only, track its contents as scalars */
|
||||
@ -4724,7 +4892,7 @@ static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_i
|
||||
static int check_stack_range_initialized(
|
||||
struct bpf_verifier_env *env, int regno, int off,
|
||||
int access_size, bool zero_size_allowed,
|
||||
enum stack_access_src type, struct bpf_call_arg_meta *meta)
|
||||
enum bpf_access_src type, struct bpf_call_arg_meta *meta)
|
||||
{
|
||||
struct bpf_reg_state *reg = reg_state(env, regno);
|
||||
struct bpf_func_state *state = func(env, reg);
|
||||
@ -4874,7 +5042,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
|
||||
BPF_READ))
|
||||
return -EACCES;
|
||||
return check_map_access(env, regno, reg->off, access_size,
|
||||
zero_size_allowed);
|
||||
zero_size_allowed, ACCESS_HELPER);
|
||||
case PTR_TO_MEM:
|
||||
if (type_is_rdonly_mem(reg->type)) {
|
||||
if (meta && meta->raw_mode) {
|
||||
@ -5163,6 +5331,53 @@ static int process_timer_func(struct bpf_verifier_env *env, int regno,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_kptr_func(struct bpf_verifier_env *env, int regno,
|
||||
struct bpf_call_arg_meta *meta)
|
||||
{
|
||||
struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno];
|
||||
struct bpf_map_value_off_desc *off_desc;
|
||||
struct bpf_map *map_ptr = reg->map_ptr;
|
||||
u32 kptr_off;
|
||||
int ret;
|
||||
|
||||
if (!tnum_is_const(reg->var_off)) {
|
||||
verbose(env,
|
||||
"R%d doesn't have constant offset. kptr has to be at the constant offset\n",
|
||||
regno);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!map_ptr->btf) {
|
||||
verbose(env, "map '%s' has to have BTF in order to use bpf_kptr_xchg\n",
|
||||
map_ptr->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!map_value_has_kptrs(map_ptr)) {
|
||||
ret = PTR_ERR(map_ptr->kptr_off_tab);
|
||||
if (ret == -E2BIG)
|
||||
verbose(env, "map '%s' has more than %d kptr\n", map_ptr->name,
|
||||
BPF_MAP_VALUE_OFF_MAX);
|
||||
else if (ret == -EEXIST)
|
||||
verbose(env, "map '%s' has repeating kptr BTF tags\n", map_ptr->name);
|
||||
else
|
||||
verbose(env, "map '%s' has no valid kptr\n", map_ptr->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
meta->map_ptr = map_ptr;
|
||||
kptr_off = reg->off + reg->var_off.value;
|
||||
off_desc = bpf_map_kptr_off_contains(map_ptr, kptr_off);
|
||||
if (!off_desc) {
|
||||
verbose(env, "off=%d doesn't point to kptr\n", kptr_off);
|
||||
return -EACCES;
|
||||
}
|
||||
if (off_desc->type != BPF_KPTR_REF) {
|
||||
verbose(env, "off=%d kptr isn't referenced kptr\n", kptr_off);
|
||||
return -EACCES;
|
||||
}
|
||||
meta->kptr_off_desc = off_desc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool arg_type_is_mem_ptr(enum bpf_arg_type type)
|
||||
{
|
||||
return base_type(type) == ARG_PTR_TO_MEM ||
|
||||
@ -5186,6 +5401,11 @@ static bool arg_type_is_int_ptr(enum bpf_arg_type type)
|
||||
type == ARG_PTR_TO_LONG;
|
||||
}
|
||||
|
||||
static bool arg_type_is_release(enum bpf_arg_type type)
|
||||
{
|
||||
return type & OBJ_RELEASE;
|
||||
}
|
||||
|
||||
static int int_ptr_type_to_size(enum bpf_arg_type type)
|
||||
{
|
||||
if (type == ARG_PTR_TO_INT)
|
||||
@ -5298,6 +5518,7 @@ static const struct bpf_reg_types func_ptr_types = { .types = { PTR_TO_FUNC } };
|
||||
static const struct bpf_reg_types stack_ptr_types = { .types = { PTR_TO_STACK } };
|
||||
static const struct bpf_reg_types const_str_ptr_types = { .types = { PTR_TO_MAP_VALUE } };
|
||||
static const struct bpf_reg_types timer_types = { .types = { PTR_TO_MAP_VALUE } };
|
||||
static const struct bpf_reg_types kptr_types = { .types = { PTR_TO_MAP_VALUE } };
|
||||
|
||||
static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = {
|
||||
[ARG_PTR_TO_MAP_KEY] = &map_key_value_types,
|
||||
@ -5325,11 +5546,13 @@ static const struct bpf_reg_types *compatible_reg_types[__BPF_ARG_TYPE_MAX] = {
|
||||
[ARG_PTR_TO_STACK] = &stack_ptr_types,
|
||||
[ARG_PTR_TO_CONST_STR] = &const_str_ptr_types,
|
||||
[ARG_PTR_TO_TIMER] = &timer_types,
|
||||
[ARG_PTR_TO_KPTR] = &kptr_types,
|
||||
};
|
||||
|
||||
static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
|
||||
enum bpf_arg_type arg_type,
|
||||
const u32 *arg_btf_id)
|
||||
const u32 *arg_btf_id,
|
||||
struct bpf_call_arg_meta *meta)
|
||||
{
|
||||
struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno];
|
||||
enum bpf_reg_type expected, type = reg->type;
|
||||
@ -5374,6 +5597,13 @@ static int check_reg_type(struct bpf_verifier_env *env, u32 regno,
|
||||
|
||||
found:
|
||||
if (reg->type == PTR_TO_BTF_ID) {
|
||||
/* For bpf_sk_release, it needs to match against first member
|
||||
* 'struct sock_common', hence make an exception for it. This
|
||||
* allows bpf_sk_release to work for multiple socket types.
|
||||
*/
|
||||
bool strict_type_match = arg_type_is_release(arg_type) &&
|
||||
meta->func_id != BPF_FUNC_sk_release;
|
||||
|
||||
if (!arg_btf_id) {
|
||||
if (!compatible->btf_id) {
|
||||
verbose(env, "verifier internal error: missing arg compatible BTF ID\n");
|
||||
@ -5382,8 +5612,12 @@ found:
|
||||
arg_btf_id = compatible->btf_id;
|
||||
}
|
||||
|
||||
if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, reg->off,
|
||||
btf_vmlinux, *arg_btf_id)) {
|
||||
if (meta->func_id == BPF_FUNC_kptr_xchg) {
|
||||
if (map_kptr_match_type(env, meta->kptr_off_desc, reg, regno))
|
||||
return -EACCES;
|
||||
} else if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, reg->off,
|
||||
btf_vmlinux, *arg_btf_id,
|
||||
strict_type_match)) {
|
||||
verbose(env, "R%d is of type %s but %s is expected\n",
|
||||
regno, kernel_type_name(reg->btf, reg->btf_id),
|
||||
kernel_type_name(btf_vmlinux, *arg_btf_id));
|
||||
@ -5396,11 +5630,10 @@ found:
|
||||
|
||||
int check_func_arg_reg_off(struct bpf_verifier_env *env,
|
||||
const struct bpf_reg_state *reg, int regno,
|
||||
enum bpf_arg_type arg_type,
|
||||
bool is_release_func)
|
||||
enum bpf_arg_type arg_type)
|
||||
{
|
||||
bool fixed_off_ok = false, release_reg;
|
||||
enum bpf_reg_type type = reg->type;
|
||||
bool fixed_off_ok = false;
|
||||
|
||||
switch ((u32)type) {
|
||||
case SCALAR_VALUE:
|
||||
@ -5418,7 +5651,7 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env,
|
||||
/* Some of the argument types nevertheless require a
|
||||
* zero register offset.
|
||||
*/
|
||||
if (arg_type != ARG_PTR_TO_ALLOC_MEM)
|
||||
if (base_type(arg_type) != ARG_PTR_TO_ALLOC_MEM)
|
||||
return 0;
|
||||
break;
|
||||
/* All the rest must be rejected, except PTR_TO_BTF_ID which allows
|
||||
@ -5426,19 +5659,17 @@ int check_func_arg_reg_off(struct bpf_verifier_env *env,
|
||||
*/
|
||||
case PTR_TO_BTF_ID:
|
||||
/* When referenced PTR_TO_BTF_ID is passed to release function,
|
||||
* it's fixed offset must be 0. We rely on the property that
|
||||
* only one referenced register can be passed to BPF helpers and
|
||||
* kfuncs. In the other cases, fixed offset can be non-zero.
|
||||
* it's fixed offset must be 0. In the other cases, fixed offset
|
||||
* can be non-zero.
|
||||
*/
|
||||
release_reg = is_release_func && reg->ref_obj_id;
|
||||
if (release_reg && reg->off) {
|
||||
if (arg_type_is_release(arg_type) && reg->off) {
|
||||
verbose(env, "R%d must have zero offset when passed to release func\n",
|
||||
regno);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* For release_reg == true, fixed_off_ok must be false, but we
|
||||
* already checked and rejected reg->off != 0 above, so set to
|
||||
* true to allow fixed offset for all other cases.
|
||||
/* For arg is release pointer, fixed_off_ok must be false, but
|
||||
* we already checked and rejected reg->off != 0 above, so set
|
||||
* to true to allow fixed offset for all other cases.
|
||||
*/
|
||||
fixed_off_ok = true;
|
||||
break;
|
||||
@ -5493,18 +5724,28 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
|
||||
*/
|
||||
goto skip_type_check;
|
||||
|
||||
err = check_reg_type(env, regno, arg_type, fn->arg_btf_id[arg]);
|
||||
err = check_reg_type(env, regno, arg_type, fn->arg_btf_id[arg], meta);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = check_func_arg_reg_off(env, reg, regno, arg_type, is_release_function(meta->func_id));
|
||||
err = check_func_arg_reg_off(env, reg, regno, arg_type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
skip_type_check:
|
||||
/* check_func_arg_reg_off relies on only one referenced register being
|
||||
* allowed for BPF helpers.
|
||||
*/
|
||||
if (arg_type_is_release(arg_type)) {
|
||||
if (!reg->ref_obj_id && !register_is_null(reg)) {
|
||||
verbose(env, "R%d must be referenced when passed to release function\n",
|
||||
regno);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (meta->release_regno) {
|
||||
verbose(env, "verifier internal error: more than one release argument\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
meta->release_regno = regno;
|
||||
}
|
||||
|
||||
if (reg->ref_obj_id) {
|
||||
if (meta->ref_obj_id) {
|
||||
verbose(env, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
|
||||
@ -5642,7 +5883,8 @@ skip_type_check:
|
||||
}
|
||||
|
||||
err = check_map_access(env, regno, reg->off,
|
||||
map->value_size - reg->off, false);
|
||||
map->value_size - reg->off, false,
|
||||
ACCESS_HELPER);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -5658,6 +5900,9 @@ skip_type_check:
|
||||
verbose(env, "string is not zero-terminated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (arg_type == ARG_PTR_TO_KPTR) {
|
||||
if (process_kptr_func(env, regno, meta))
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -6000,17 +6245,18 @@ static bool check_btf_id_ok(const struct bpf_func_proto *fn)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fn->arg_type); i++) {
|
||||
if (fn->arg_type[i] == ARG_PTR_TO_BTF_ID && !fn->arg_btf_id[i])
|
||||
if (base_type(fn->arg_type[i]) == ARG_PTR_TO_BTF_ID && !fn->arg_btf_id[i])
|
||||
return false;
|
||||
|
||||
if (fn->arg_type[i] != ARG_PTR_TO_BTF_ID && fn->arg_btf_id[i])
|
||||
if (base_type(fn->arg_type[i]) != ARG_PTR_TO_BTF_ID && fn->arg_btf_id[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int check_func_proto(const struct bpf_func_proto *fn, int func_id)
|
||||
static int check_func_proto(const struct bpf_func_proto *fn, int func_id,
|
||||
struct bpf_call_arg_meta *meta)
|
||||
{
|
||||
return check_raw_mode_ok(fn) &&
|
||||
check_arg_pair_ok(fn) &&
|
||||
@ -6694,7 +6940,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
||||
memset(&meta, 0, sizeof(meta));
|
||||
meta.pkt_access = fn->pkt_access;
|
||||
|
||||
err = check_func_proto(fn, func_id);
|
||||
err = check_func_proto(fn, func_id, &meta);
|
||||
if (err) {
|
||||
verbose(env, "kernel subsystem misconfigured func %s#%d\n",
|
||||
func_id_name(func_id), func_id);
|
||||
@ -6727,8 +6973,17 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
||||
return err;
|
||||
}
|
||||
|
||||
if (is_release_function(func_id)) {
|
||||
err = release_reference(env, meta.ref_obj_id);
|
||||
regs = cur_regs(env);
|
||||
|
||||
if (meta.release_regno) {
|
||||
err = -EINVAL;
|
||||
if (meta.ref_obj_id)
|
||||
err = release_reference(env, meta.ref_obj_id);
|
||||
/* meta.ref_obj_id can only be 0 if register that is meant to be
|
||||
* released is NULL, which must be > R0.
|
||||
*/
|
||||
else if (register_is_null(®s[meta.release_regno]))
|
||||
err = 0;
|
||||
if (err) {
|
||||
verbose(env, "func %s#%d reference has not been acquired before\n",
|
||||
func_id_name(func_id), func_id);
|
||||
@ -6736,8 +6991,6 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
||||
}
|
||||
}
|
||||
|
||||
regs = cur_regs(env);
|
||||
|
||||
switch (func_id) {
|
||||
case BPF_FUNC_tail_call:
|
||||
err = check_reference_leak(env);
|
||||
@ -6861,21 +7114,25 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
|
||||
regs[BPF_REG_0].btf_id = meta.ret_btf_id;
|
||||
}
|
||||
} else if (base_type(ret_type) == RET_PTR_TO_BTF_ID) {
|
||||
struct btf *ret_btf;
|
||||
int ret_btf_id;
|
||||
|
||||
mark_reg_known_zero(env, regs, BPF_REG_0);
|
||||
regs[BPF_REG_0].type = PTR_TO_BTF_ID | ret_flag;
|
||||
ret_btf_id = *fn->ret_btf_id;
|
||||
if (func_id == BPF_FUNC_kptr_xchg) {
|
||||
ret_btf = meta.kptr_off_desc->kptr.btf;
|
||||
ret_btf_id = meta.kptr_off_desc->kptr.btf_id;
|
||||
} else {
|
||||
ret_btf = btf_vmlinux;
|
||||
ret_btf_id = *fn->ret_btf_id;
|
||||
}
|
||||
if (ret_btf_id == 0) {
|
||||
verbose(env, "invalid return type %u of func %s#%d\n",
|
||||
base_type(ret_type), func_id_name(func_id),
|
||||
func_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* current BPF helper definitions are only coming from
|
||||
* built-in code with type IDs from vmlinux BTF
|
||||
*/
|
||||
regs[BPF_REG_0].btf = btf_vmlinux;
|
||||
regs[BPF_REG_0].btf = ret_btf;
|
||||
regs[BPF_REG_0].btf_id = ret_btf_id;
|
||||
} else {
|
||||
verbose(env, "unknown return type %u of func %s#%d\n",
|
||||
@ -7462,7 +7719,7 @@ static int sanitize_check_bounds(struct bpf_verifier_env *env,
|
||||
return -EACCES;
|
||||
break;
|
||||
case PTR_TO_MAP_VALUE:
|
||||
if (check_map_access(env, dst, dst_reg->off, 1, false)) {
|
||||
if (check_map_access(env, dst, dst_reg->off, 1, false, ACCESS_HELPER)) {
|
||||
verbose(env, "R%d pointer arithmetic of map value goes out of range, "
|
||||
"prohibited for !root\n", dst);
|
||||
return -EACCES;
|
||||
@ -12851,7 +13108,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
|
||||
if (!ctx_access)
|
||||
continue;
|
||||
|
||||
switch (env->insn_aux_data[i + delta].ptr_type) {
|
||||
switch ((int)env->insn_aux_data[i + delta].ptr_type) {
|
||||
case PTR_TO_CTX:
|
||||
if (!ops->convert_ctx_access)
|
||||
continue;
|
||||
@ -12868,6 +13125,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
|
||||
convert_ctx_access = bpf_xdp_sock_convert_ctx_access;
|
||||
break;
|
||||
case PTR_TO_BTF_ID:
|
||||
case PTR_TO_BTF_ID | PTR_UNTRUSTED:
|
||||
if (type == BPF_READ) {
|
||||
insn->code = BPF_LDX | BPF_PROBE_MEM |
|
||||
BPF_SIZE((insn)->code);
|
||||
|
@ -62,7 +62,6 @@
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/sched/sysctl.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/userfaultfd_k.h>
|
||||
#include <linux/latencytop.h>
|
||||
@ -148,66 +147,6 @@ static const int max_extfrag_threshold = 1000;
|
||||
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
#if defined(CONFIG_BPF_SYSCALL) && defined(CONFIG_SYSCTL)
|
||||
static int bpf_stats_handler(struct ctl_table *table, int write,
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct static_key *key = (struct static_key *)table->data;
|
||||
static int saved_val;
|
||||
int val, ret;
|
||||
struct ctl_table tmp = {
|
||||
.data = &val,
|
||||
.maxlen = sizeof(val),
|
||||
.mode = table->mode,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = SYSCTL_ONE,
|
||||
};
|
||||
|
||||
if (write && !capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
mutex_lock(&bpf_stats_enabled_mutex);
|
||||
val = saved_val;
|
||||
ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
|
||||
if (write && !ret && val != saved_val) {
|
||||
if (val)
|
||||
static_key_slow_inc(key);
|
||||
else
|
||||
static_key_slow_dec(key);
|
||||
saved_val = val;
|
||||
}
|
||||
mutex_unlock(&bpf_stats_enabled_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __weak unpriv_ebpf_notify(int new_state)
|
||||
{
|
||||
}
|
||||
|
||||
static int bpf_unpriv_handler(struct ctl_table *table, int write,
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int ret, unpriv_enable = *(int *)table->data;
|
||||
bool locked_state = unpriv_enable == 1;
|
||||
struct ctl_table tmp = *table;
|
||||
|
||||
if (write && !capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
tmp.data = &unpriv_enable;
|
||||
ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
|
||||
if (write && !ret) {
|
||||
if (locked_state && unpriv_enable != 1)
|
||||
return -EPERM;
|
||||
*(int *)table->data = unpriv_enable;
|
||||
}
|
||||
|
||||
unpriv_ebpf_notify(unpriv_enable);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_BPF_SYSCALL && CONFIG_SYSCTL */
|
||||
|
||||
/*
|
||||
* /proc/sys support
|
||||
*/
|
||||
@ -2299,24 +2238,6 @@ static struct ctl_table kern_table[] = {
|
||||
.extra2 = SYSCTL_ONE,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
{
|
||||
.procname = "unprivileged_bpf_disabled",
|
||||
.data = &sysctl_unprivileged_bpf_disabled,
|
||||
.maxlen = sizeof(sysctl_unprivileged_bpf_disabled),
|
||||
.mode = 0644,
|
||||
.proc_handler = bpf_unpriv_handler,
|
||||
.extra1 = SYSCTL_ZERO,
|
||||
.extra2 = SYSCTL_TWO,
|
||||
},
|
||||
{
|
||||
.procname = "bpf_stats_enabled",
|
||||
.data = &bpf_stats_enabled_key.key,
|
||||
.maxlen = sizeof(bpf_stats_enabled_key),
|
||||
.mode = 0644,
|
||||
.proc_handler = bpf_stats_handler,
|
||||
},
|
||||
#endif
|
||||
#if defined(CONFIG_TREE_RCU)
|
||||
{
|
||||
.procname = "panic_on_rcu_stall",
|
||||
|
@ -129,7 +129,10 @@ unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx)
|
||||
* out of events when it was updated in between this and the
|
||||
* rcu_dereference() which is accepted risk.
|
||||
*/
|
||||
ret = BPF_PROG_RUN_ARRAY(call->prog_array, ctx, bpf_prog_run);
|
||||
rcu_read_lock();
|
||||
ret = bpf_prog_run_array(rcu_dereference(call->prog_array),
|
||||
ctx, bpf_prog_run);
|
||||
rcu_read_unlock();
|
||||
|
||||
out:
|
||||
__this_cpu_dec(bpf_prog_active);
|
||||
|
@ -550,8 +550,13 @@ struct sock * noinline bpf_kfunc_call_test3(struct sock *sk)
|
||||
return sk;
|
||||
}
|
||||
|
||||
struct prog_test_member1 {
|
||||
int a;
|
||||
};
|
||||
|
||||
struct prog_test_member {
|
||||
u64 c;
|
||||
struct prog_test_member1 m;
|
||||
int c;
|
||||
};
|
||||
|
||||
struct prog_test_ref_kfunc {
|
||||
@ -576,6 +581,12 @@ bpf_kfunc_call_test_acquire(unsigned long *scalar_ptr)
|
||||
return &prog_test_struct;
|
||||
}
|
||||
|
||||
noinline struct prog_test_member *
|
||||
bpf_kfunc_call_memb_acquire(void)
|
||||
{
|
||||
return &prog_test_struct.memb;
|
||||
}
|
||||
|
||||
noinline void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p)
|
||||
{
|
||||
}
|
||||
@ -584,6 +595,16 @@ noinline void bpf_kfunc_call_memb_release(struct prog_test_member *p)
|
||||
{
|
||||
}
|
||||
|
||||
noinline void bpf_kfunc_call_memb1_release(struct prog_test_member1 *p)
|
||||
{
|
||||
}
|
||||
|
||||
noinline struct prog_test_ref_kfunc *
|
||||
bpf_kfunc_call_test_kptr_get(struct prog_test_ref_kfunc **p, int a, int b)
|
||||
{
|
||||
return &prog_test_struct;
|
||||
}
|
||||
|
||||
struct prog_test_pass1 {
|
||||
int x0;
|
||||
struct {
|
||||
@ -667,8 +688,11 @@ BTF_ID(func, bpf_kfunc_call_test1)
|
||||
BTF_ID(func, bpf_kfunc_call_test2)
|
||||
BTF_ID(func, bpf_kfunc_call_test3)
|
||||
BTF_ID(func, bpf_kfunc_call_test_acquire)
|
||||
BTF_ID(func, bpf_kfunc_call_memb_acquire)
|
||||
BTF_ID(func, bpf_kfunc_call_test_release)
|
||||
BTF_ID(func, bpf_kfunc_call_memb_release)
|
||||
BTF_ID(func, bpf_kfunc_call_memb1_release)
|
||||
BTF_ID(func, bpf_kfunc_call_test_kptr_get)
|
||||
BTF_ID(func, bpf_kfunc_call_test_pass_ctx)
|
||||
BTF_ID(func, bpf_kfunc_call_test_pass1)
|
||||
BTF_ID(func, bpf_kfunc_call_test_pass2)
|
||||
@ -682,17 +706,26 @@ BTF_SET_END(test_sk_check_kfunc_ids)
|
||||
|
||||
BTF_SET_START(test_sk_acquire_kfunc_ids)
|
||||
BTF_ID(func, bpf_kfunc_call_test_acquire)
|
||||
BTF_ID(func, bpf_kfunc_call_memb_acquire)
|
||||
BTF_ID(func, bpf_kfunc_call_test_kptr_get)
|
||||
BTF_SET_END(test_sk_acquire_kfunc_ids)
|
||||
|
||||
BTF_SET_START(test_sk_release_kfunc_ids)
|
||||
BTF_ID(func, bpf_kfunc_call_test_release)
|
||||
BTF_ID(func, bpf_kfunc_call_memb_release)
|
||||
BTF_ID(func, bpf_kfunc_call_memb1_release)
|
||||
BTF_SET_END(test_sk_release_kfunc_ids)
|
||||
|
||||
BTF_SET_START(test_sk_ret_null_kfunc_ids)
|
||||
BTF_ID(func, bpf_kfunc_call_test_acquire)
|
||||
BTF_ID(func, bpf_kfunc_call_memb_acquire)
|
||||
BTF_ID(func, bpf_kfunc_call_test_kptr_get)
|
||||
BTF_SET_END(test_sk_ret_null_kfunc_ids)
|
||||
|
||||
BTF_SET_START(test_sk_kptr_acquire_kfunc_ids)
|
||||
BTF_ID(func, bpf_kfunc_call_test_kptr_get)
|
||||
BTF_SET_END(test_sk_kptr_acquire_kfunc_ids)
|
||||
|
||||
static void *bpf_test_init(const union bpf_attr *kattr, u32 user_size,
|
||||
u32 size, u32 headroom, u32 tailroom)
|
||||
{
|
||||
@ -1579,14 +1612,36 @@ out:
|
||||
|
||||
static const struct btf_kfunc_id_set bpf_prog_test_kfunc_set = {
|
||||
.owner = THIS_MODULE,
|
||||
.check_set = &test_sk_check_kfunc_ids,
|
||||
.acquire_set = &test_sk_acquire_kfunc_ids,
|
||||
.release_set = &test_sk_release_kfunc_ids,
|
||||
.ret_null_set = &test_sk_ret_null_kfunc_ids,
|
||||
.check_set = &test_sk_check_kfunc_ids,
|
||||
.acquire_set = &test_sk_acquire_kfunc_ids,
|
||||
.release_set = &test_sk_release_kfunc_ids,
|
||||
.ret_null_set = &test_sk_ret_null_kfunc_ids,
|
||||
.kptr_acquire_set = &test_sk_kptr_acquire_kfunc_ids
|
||||
};
|
||||
|
||||
BTF_ID_LIST(bpf_prog_test_dtor_kfunc_ids)
|
||||
BTF_ID(struct, prog_test_ref_kfunc)
|
||||
BTF_ID(func, bpf_kfunc_call_test_release)
|
||||
BTF_ID(struct, prog_test_member)
|
||||
BTF_ID(func, bpf_kfunc_call_memb_release)
|
||||
|
||||
static int __init bpf_prog_test_run_init(void)
|
||||
{
|
||||
return register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_prog_test_kfunc_set);
|
||||
const struct btf_id_dtor_kfunc bpf_prog_test_dtor_kfunc[] = {
|
||||
{
|
||||
.btf_id = bpf_prog_test_dtor_kfunc_ids[0],
|
||||
.kfunc_btf_id = bpf_prog_test_dtor_kfunc_ids[1]
|
||||
},
|
||||
{
|
||||
.btf_id = bpf_prog_test_dtor_kfunc_ids[2],
|
||||
.kfunc_btf_id = bpf_prog_test_dtor_kfunc_ids[3],
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_prog_test_kfunc_set);
|
||||
return ret ?: register_btf_id_dtor_kfuncs(bpf_prog_test_dtor_kfunc,
|
||||
ARRAY_SIZE(bpf_prog_test_dtor_kfunc),
|
||||
THIS_MODULE);
|
||||
}
|
||||
late_initcall(bpf_prog_test_run_init);
|
||||
|
@ -40,7 +40,7 @@ static int bpf_sk_storage_del(struct sock *sk, struct bpf_map *map)
|
||||
if (!sdata)
|
||||
return -ENOENT;
|
||||
|
||||
bpf_selem_unlink(SELEM(sdata));
|
||||
bpf_selem_unlink(SELEM(sdata), true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -75,8 +75,8 @@ void bpf_sk_storage_free(struct sock *sk)
|
||||
* sk_storage.
|
||||
*/
|
||||
bpf_selem_unlink_map(selem);
|
||||
free_sk_storage = bpf_selem_unlink_storage_nolock(sk_storage,
|
||||
selem, true);
|
||||
free_sk_storage = bpf_selem_unlink_storage_nolock(
|
||||
sk_storage, selem, true, false);
|
||||
}
|
||||
raw_spin_unlock_bh(&sk_storage->lock);
|
||||
rcu_read_unlock();
|
||||
@ -338,7 +338,7 @@ bpf_sk_storage_ptr(void *owner)
|
||||
return &sk->sk_bpf_storage;
|
||||
}
|
||||
|
||||
static int sk_storage_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(sk_storage_map_btf_ids, struct, bpf_local_storage_map)
|
||||
const struct bpf_map_ops sk_storage_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = bpf_local_storage_map_alloc_check,
|
||||
@ -349,8 +349,7 @@ const struct bpf_map_ops sk_storage_map_ops = {
|
||||
.map_update_elem = bpf_fd_sk_storage_update_elem,
|
||||
.map_delete_elem = bpf_fd_sk_storage_delete_elem,
|
||||
.map_check_btf = bpf_local_storage_map_check_btf,
|
||||
.map_btf_name = "bpf_local_storage_map",
|
||||
.map_btf_id = &sk_storage_map_btf_id,
|
||||
.map_btf_id = &sk_storage_map_btf_ids[0],
|
||||
.map_local_storage_charge = bpf_sk_storage_charge,
|
||||
.map_local_storage_uncharge = bpf_sk_storage_uncharge,
|
||||
.map_owner_storage_ptr = bpf_sk_storage_ptr,
|
||||
|
@ -1687,7 +1687,7 @@ BPF_CALL_5(bpf_skb_store_bytes, struct sk_buff *, skb, u32, offset,
|
||||
|
||||
if (unlikely(flags & ~(BPF_F_RECOMPUTE_CSUM | BPF_F_INVALIDATE_HASH)))
|
||||
return -EINVAL;
|
||||
if (unlikely(offset > 0xffff))
|
||||
if (unlikely(offset > INT_MAX))
|
||||
return -EFAULT;
|
||||
if (unlikely(bpf_try_make_writable(skb, offset + len)))
|
||||
return -EFAULT;
|
||||
@ -1722,7 +1722,7 @@ BPF_CALL_4(bpf_skb_load_bytes, const struct sk_buff *, skb, u32, offset,
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
if (unlikely(offset > 0xffff))
|
||||
if (unlikely(offset > INT_MAX))
|
||||
goto err_clear;
|
||||
|
||||
ptr = skb_header_pointer(skb, offset, len, to);
|
||||
@ -6621,7 +6621,7 @@ static const struct bpf_func_proto bpf_sk_release_proto = {
|
||||
.func = bpf_sk_release,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON,
|
||||
.arg1_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON | OBJ_RELEASE,
|
||||
};
|
||||
|
||||
BPF_CALL_5(bpf_xdp_sk_lookup_udp, struct xdp_buff *, ctx,
|
||||
|
@ -5602,7 +5602,7 @@ err_free:
|
||||
}
|
||||
EXPORT_SYMBOL(skb_vlan_untag);
|
||||
|
||||
int skb_ensure_writable(struct sk_buff *skb, int write_len)
|
||||
int skb_ensure_writable(struct sk_buff *skb, unsigned int write_len)
|
||||
{
|
||||
if (!pskb_may_pull(skb, write_len))
|
||||
return -ENOMEM;
|
||||
|
@ -793,7 +793,7 @@ static const struct bpf_iter_seq_info sock_map_iter_seq_info = {
|
||||
.seq_priv_size = sizeof(struct sock_map_seq_info),
|
||||
};
|
||||
|
||||
static int sock_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(sock_map_btf_ids, struct, bpf_stab)
|
||||
const struct bpf_map_ops sock_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = sock_map_alloc,
|
||||
@ -805,8 +805,7 @@ const struct bpf_map_ops sock_map_ops = {
|
||||
.map_lookup_elem = sock_map_lookup,
|
||||
.map_release_uref = sock_map_release_progs,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "bpf_stab",
|
||||
.map_btf_id = &sock_map_btf_id,
|
||||
.map_btf_id = &sock_map_btf_ids[0],
|
||||
.iter_seq_info = &sock_map_iter_seq_info,
|
||||
};
|
||||
|
||||
@ -1385,7 +1384,7 @@ static const struct bpf_iter_seq_info sock_hash_iter_seq_info = {
|
||||
.seq_priv_size = sizeof(struct sock_hash_seq_info),
|
||||
};
|
||||
|
||||
static int sock_hash_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(sock_hash_map_btf_ids, struct, bpf_shtab)
|
||||
const struct bpf_map_ops sock_hash_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = sock_hash_alloc,
|
||||
@ -1397,8 +1396,7 @@ const struct bpf_map_ops sock_hash_ops = {
|
||||
.map_lookup_elem_sys_only = sock_hash_lookup_sys,
|
||||
.map_release_uref = sock_hash_release_progs,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "bpf_shtab",
|
||||
.map_btf_id = &sock_hash_map_btf_id,
|
||||
.map_btf_id = &sock_hash_map_btf_ids[0],
|
||||
.iter_seq_info = &sock_hash_iter_seq_info,
|
||||
};
|
||||
|
||||
|
@ -184,7 +184,7 @@ static int __xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
|
||||
xsk_xdp = xsk_buff_alloc(xs->pool);
|
||||
if (!xsk_xdp) {
|
||||
xs->rx_dropped++;
|
||||
return -ENOSPC;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
xsk_copy_xdp(xsk_xdp, xdp, len);
|
||||
@ -217,7 +217,7 @@ static bool xsk_is_bound(struct xdp_sock *xs)
|
||||
static int xsk_rcv_check(struct xdp_sock *xs, struct xdp_buff *xdp)
|
||||
{
|
||||
if (!xsk_is_bound(xs))
|
||||
return -EINVAL;
|
||||
return -ENXIO;
|
||||
|
||||
if (xs->dev != xdp->rxq->dev || xs->queue_id != xdp->rxq->queue_index)
|
||||
return -EINVAL;
|
||||
|
@ -263,7 +263,7 @@ static inline u32 xskq_cons_nb_entries(struct xsk_queue *q, u32 max)
|
||||
|
||||
static inline bool xskq_cons_has_entries(struct xsk_queue *q, u32 cnt)
|
||||
{
|
||||
return xskq_cons_nb_entries(q, cnt) >= cnt ? true : false;
|
||||
return xskq_cons_nb_entries(q, cnt) >= cnt;
|
||||
}
|
||||
|
||||
static inline bool xskq_cons_peek_addr_unchecked(struct xsk_queue *q, u64 *addr)
|
||||
@ -382,7 +382,7 @@ static inline int xskq_prod_reserve_desc(struct xsk_queue *q,
|
||||
u32 idx;
|
||||
|
||||
if (xskq_prod_is_full(q))
|
||||
return -ENOSPC;
|
||||
return -ENOBUFS;
|
||||
|
||||
/* A, matches D */
|
||||
idx = q->cached_prod++ & q->ring_mask;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <net/xdp_sock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/btf_ids.h>
|
||||
|
||||
#include "xsk.h"
|
||||
|
||||
@ -254,7 +255,7 @@ static bool xsk_map_meta_equal(const struct bpf_map *meta0,
|
||||
bpf_map_meta_equal(meta0, meta1);
|
||||
}
|
||||
|
||||
static int xsk_map_btf_id;
|
||||
BTF_ID_LIST_SINGLE(xsk_map_btf_ids, struct, xsk_map)
|
||||
const struct bpf_map_ops xsk_map_ops = {
|
||||
.map_meta_equal = xsk_map_meta_equal,
|
||||
.map_alloc = xsk_map_alloc,
|
||||
@ -266,7 +267,6 @@ const struct bpf_map_ops xsk_map_ops = {
|
||||
.map_update_elem = xsk_map_update_elem,
|
||||
.map_delete_elem = xsk_map_delete_elem,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
.map_btf_name = "xsk_map",
|
||||
.map_btf_id = &xsk_map_btf_id,
|
||||
.map_btf_id = &xsk_map_btf_ids[0],
|
||||
.map_redirect = xsk_map_redirect,
|
||||
};
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
@ -46,7 +45,6 @@
|
||||
#include <bpf/bpf.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include "bpf_rlimit.h"
|
||||
#include "cgroup_helpers.h"
|
||||
#include "hbm.h"
|
||||
#include "bpf_util.h"
|
||||
@ -510,5 +508,8 @@ int main(int argc, char **argv)
|
||||
prog = argv[optind];
|
||||
printf("HBM prog: %s\n", prog != NULL ? prog : "NULL");
|
||||
|
||||
/* Use libbpf 1.0 API mode */
|
||||
libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
|
||||
|
||||
return run_bpf_prog(prog, cg_id);
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <getopt.h>
|
||||
#include <net/if.h>
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <linux/perf_event.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/resource.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include "trace_helpers.h"
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "sock_example.h"
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
struct pair {
|
||||
__u64 packets;
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include "sock_example.h"
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
struct flow_key_record {
|
||||
__be32 src;
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/resource.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include "trace_helpers.h"
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <string.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <errno.h>
|
||||
#include <sys/resource.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <fcntl.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/perf_event.h>
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <sched.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/resource.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
@ -2,7 +2,6 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Facebook
|
||||
*/
|
||||
#include <sys/resource.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdint.h>
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <linux/bpf.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include <sys/prctl.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <sys/resource.h>
|
||||
#include "trace_helpers.h"
|
||||
|
||||
#ifdef __mips__
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/resource.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include "bpf_util.h"
|
||||
@ -161,7 +160,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
prog_id = info.id;
|
||||
|
||||
poll_stats(map_fd, 2);
|
||||
poll_stats(map_fd, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/resource.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <unistd.h>
|
||||
|
@ -17,7 +17,6 @@ static const char *__doc_err_only__=
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <locale.h>
|
||||
#include <sys/resource.h>
|
||||
#include <getopt.h>
|
||||
#include <net/if.h>
|
||||
#include <time.h>
|
||||
|
@ -21,7 +21,6 @@ static const char *__doc__ =
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <locale.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <getopt.h>
|
||||
#include <net/if.h>
|
||||
|
@ -15,7 +15,6 @@ static const char *__doc__ =
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
@ -18,7 +18,6 @@ static const char *__doc__ =
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
#include <getopt.h>
|
||||
#include <sys/resource.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include "bpf_util.h"
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <sys/syscall.h>
|
||||
#include "bpf_util.h"
|
||||
#include <bpf/libbpf.h>
|
||||
#include <sys/resource.h>
|
||||
#include <libgen.h>
|
||||
#include <getopt.h>
|
||||
#include <pthread.h>
|
||||
|
@ -14,7 +14,6 @@ static const char *__doc__ = " XDP RX-queue info extract example\n\n"
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <locale.h>
|
||||
#include <sys/resource.h>
|
||||
#include <getopt.h>
|
||||
#include <net/if.h>
|
||||
#include <time.h>
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <signal.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <sys/resource.h>
|
||||
#include <libgen.h>
|
||||
#include <linux/if_link.h>
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/timerfd.h>
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/resource.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <unistd.h>
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <string.h>
|
||||
#include <sys/capability.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
@ -1886,7 +1885,6 @@ int main(int argc, char **argv)
|
||||
{
|
||||
struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 };
|
||||
struct __user_cap_data_struct data[2] = { { 0 } };
|
||||
struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
|
||||
bool rx = false, tx = false;
|
||||
struct sched_param schparam;
|
||||
struct xsk_umem_info *umem;
|
||||
@ -1917,11 +1915,8 @@ int main(int argc, char **argv)
|
||||
data[1].effective, data[1].inheritable, data[1].permitted);
|
||||
}
|
||||
} else {
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &r)) {
|
||||
fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n",
|
||||
strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* Use libbpf 1.0 API mode */
|
||||
libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
|
||||
|
||||
if (opt_num_xsks > 1)
|
||||
load_xdp_program(argv, &obj);
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
@ -131,7 +130,6 @@ static struct bpool *
|
||||
bpool_init(struct bpool_params *params,
|
||||
struct xsk_umem_config *umem_cfg)
|
||||
{
|
||||
struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
|
||||
u64 n_slabs, n_slabs_reserved, n_buffers, n_buffers_reserved;
|
||||
u64 slabs_size, slabs_reserved_size;
|
||||
u64 buffers_size, buffers_reserved_size;
|
||||
@ -140,9 +138,8 @@ bpool_init(struct bpool_params *params,
|
||||
u8 *p;
|
||||
int status;
|
||||
|
||||
/* mmap prep. */
|
||||
if (setrlimit(RLIMIT_MEMLOCK, &r))
|
||||
return NULL;
|
||||
/* Use libbpf 1.0 API mode */
|
||||
libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
|
||||
|
||||
/* bpool internals dimensioning. */
|
||||
n_slabs = (params->n_buffers + params->n_buffers_per_slab - 1) /
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/magic.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/vfs.h>
|
||||
|
||||
@ -119,13 +118,6 @@ static bool is_bpffs(char *path)
|
||||
return (unsigned long)st_fs.f_type == BPF_FS_MAGIC;
|
||||
}
|
||||
|
||||
void set_max_rlimit(void)
|
||||
{
|
||||
struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
|
||||
setrlimit(RLIMIT_MEMLOCK, &rinf);
|
||||
}
|
||||
|
||||
static int
|
||||
mnt_fs(const char *target, const char *type, char *buff, size_t bufflen)
|
||||
{
|
||||
|
@ -1136,8 +1136,6 @@ static int do_probe(int argc, char **argv)
|
||||
__u32 ifindex = 0;
|
||||
char *ifname;
|
||||
|
||||
set_max_rlimit();
|
||||
|
||||
while (argc) {
|
||||
if (is_prefix(*argv, "kernel")) {
|
||||
if (target != COMPONENT_UNSPEC) {
|
||||
|
@ -507,9 +507,9 @@ int main(int argc, char **argv)
|
||||
* It will still be rejected if users use LIBBPF_STRICT_ALL
|
||||
* mode for loading generated skeleton.
|
||||
*/
|
||||
ret = libbpf_set_strict_mode(LIBBPF_STRICT_ALL & ~LIBBPF_STRICT_MAP_DEFINITIONS);
|
||||
if (ret)
|
||||
p_err("failed to enable libbpf strict mode: %d", ret);
|
||||
libbpf_set_strict_mode(LIBBPF_STRICT_ALL & ~LIBBPF_STRICT_MAP_DEFINITIONS);
|
||||
} else {
|
||||
libbpf_set_strict_mode(LIBBPF_STRICT_AUTO_RLIMIT_MEMLOCK);
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
|
@ -102,8 +102,6 @@ int detect_common_prefix(const char *arg, ...);
|
||||
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
|
||||
void usage(void) __noreturn;
|
||||
|
||||
void set_max_rlimit(void);
|
||||
|
||||
int mount_tracefs(const char *target);
|
||||
|
||||
struct obj_ref {
|
||||
|
@ -1342,8 +1342,6 @@ static int do_create(int argc, char **argv)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
set_max_rlimit();
|
||||
|
||||
fd = bpf_map_create(map_type, map_name, key_size, value_size, max_entries, &attr);
|
||||
if (fd < 0) {
|
||||
p_err("map create failed: %s", strerror(errno));
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <ftw.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
|
||||
@ -147,81 +147,83 @@ static void print_perf_plain(int pid, int fd, __u32 prog_id, __u32 fd_type,
|
||||
}
|
||||
}
|
||||
|
||||
static int show_proc(const char *fpath, const struct stat *sb,
|
||||
int tflag, struct FTW *ftwbuf)
|
||||
static int show_proc(void)
|
||||
{
|
||||
struct dirent *proc_de, *pid_fd_de;
|
||||
__u64 probe_offset, probe_addr;
|
||||
__u32 len, prog_id, fd_type;
|
||||
int err, pid = 0, fd = 0;
|
||||
DIR *proc, *pid_fd;
|
||||
int err, pid, fd;
|
||||
const char *pch;
|
||||
char buf[4096];
|
||||
|
||||
/* prefix always /proc */
|
||||
pch = fpath + 5;
|
||||
if (*pch == '\0')
|
||||
return 0;
|
||||
proc = opendir("/proc");
|
||||
if (!proc)
|
||||
return -1;
|
||||
|
||||
/* pid should be all numbers */
|
||||
pch++;
|
||||
while (isdigit(*pch)) {
|
||||
pid = pid * 10 + *pch - '0';
|
||||
pch++;
|
||||
while ((proc_de = readdir(proc))) {
|
||||
pid = 0;
|
||||
pch = proc_de->d_name;
|
||||
|
||||
/* pid should be all numbers */
|
||||
while (isdigit(*pch)) {
|
||||
pid = pid * 10 + *pch - '0';
|
||||
pch++;
|
||||
}
|
||||
if (*pch != '\0')
|
||||
continue;
|
||||
|
||||
err = snprintf(buf, sizeof(buf), "/proc/%s/fd", proc_de->d_name);
|
||||
if (err < 0 || err >= (int)sizeof(buf))
|
||||
continue;
|
||||
|
||||
pid_fd = opendir(buf);
|
||||
if (!pid_fd)
|
||||
continue;
|
||||
|
||||
while ((pid_fd_de = readdir(pid_fd))) {
|
||||
fd = 0;
|
||||
pch = pid_fd_de->d_name;
|
||||
|
||||
/* fd should be all numbers */
|
||||
while (isdigit(*pch)) {
|
||||
fd = fd * 10 + *pch - '0';
|
||||
pch++;
|
||||
}
|
||||
if (*pch != '\0')
|
||||
continue;
|
||||
|
||||
/* query (pid, fd) for potential perf events */
|
||||
len = sizeof(buf);
|
||||
err = bpf_task_fd_query(pid, fd, 0, buf, &len,
|
||||
&prog_id, &fd_type,
|
||||
&probe_offset, &probe_addr);
|
||||
if (err < 0)
|
||||
continue;
|
||||
|
||||
if (json_output)
|
||||
print_perf_json(pid, fd, prog_id, fd_type, buf,
|
||||
probe_offset, probe_addr);
|
||||
else
|
||||
print_perf_plain(pid, fd, prog_id, fd_type, buf,
|
||||
probe_offset, probe_addr);
|
||||
}
|
||||
closedir(pid_fd);
|
||||
}
|
||||
if (*pch == '\0')
|
||||
return 0;
|
||||
if (*pch != '/')
|
||||
return FTW_SKIP_SUBTREE;
|
||||
|
||||
/* check /proc/<pid>/fd directory */
|
||||
pch++;
|
||||
if (strncmp(pch, "fd", 2))
|
||||
return FTW_SKIP_SUBTREE;
|
||||
pch += 2;
|
||||
if (*pch == '\0')
|
||||
return 0;
|
||||
if (*pch != '/')
|
||||
return FTW_SKIP_SUBTREE;
|
||||
|
||||
/* check /proc/<pid>/fd/<fd_num> */
|
||||
pch++;
|
||||
while (isdigit(*pch)) {
|
||||
fd = fd * 10 + *pch - '0';
|
||||
pch++;
|
||||
}
|
||||
if (*pch != '\0')
|
||||
return FTW_SKIP_SUBTREE;
|
||||
|
||||
/* query (pid, fd) for potential perf events */
|
||||
len = sizeof(buf);
|
||||
err = bpf_task_fd_query(pid, fd, 0, buf, &len, &prog_id, &fd_type,
|
||||
&probe_offset, &probe_addr);
|
||||
if (err < 0)
|
||||
return 0;
|
||||
|
||||
if (json_output)
|
||||
print_perf_json(pid, fd, prog_id, fd_type, buf, probe_offset,
|
||||
probe_addr);
|
||||
else
|
||||
print_perf_plain(pid, fd, prog_id, fd_type, buf, probe_offset,
|
||||
probe_addr);
|
||||
|
||||
closedir(proc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_show(int argc, char **argv)
|
||||
{
|
||||
int flags = FTW_ACTIONRETVAL | FTW_PHYS;
|
||||
int err = 0, nopenfd = 16;
|
||||
int err;
|
||||
|
||||
if (!has_perf_query_support())
|
||||
return -1;
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
if (nftw("/proc", show_proc, nopenfd, flags) == -1) {
|
||||
p_err("%s", strerror(errno));
|
||||
err = -1;
|
||||
}
|
||||
err = show_proc();
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
|
@ -108,7 +108,6 @@ int build_obj_refs_table(struct hashmap **map, enum bpf_obj_type type)
|
||||
p_err("failed to create hashmap for PID references");
|
||||
return -1;
|
||||
}
|
||||
set_max_rlimit();
|
||||
|
||||
skel = pid_iter_bpf__open();
|
||||
if (!skel) {
|
||||
|
@ -1604,8 +1604,6 @@ static int load_with_options(int argc, char **argv, bool first_prog_only)
|
||||
}
|
||||
}
|
||||
|
||||
set_max_rlimit();
|
||||
|
||||
if (verifier_logs)
|
||||
/* log_level1 + log_level2 + stats, but not stable UAPI */
|
||||
open_opts.kernel_log_level = 1 + 2 + 4;
|
||||
@ -2303,7 +2301,6 @@ static int do_profile(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
set_max_rlimit();
|
||||
err = profiler_bpf__load(profile_obj);
|
||||
if (err) {
|
||||
p_err("failed to load profile_obj");
|
||||
|
@ -501,8 +501,6 @@ static int do_register(int argc, char **argv)
|
||||
if (libbpf_get_error(obj))
|
||||
return -1;
|
||||
|
||||
set_max_rlimit();
|
||||
|
||||
if (bpf_object__load(obj)) {
|
||||
bpf_object__close(obj);
|
||||
return -1;
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#include "main.h"
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <time.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
@ -88,16 +87,6 @@ int libbpf_print_fn(enum libbpf_print_level level,
|
||||
return vfprintf(stderr, format, args);
|
||||
}
|
||||
|
||||
static int bump_memlock_rlimit(void)
|
||||
{
|
||||
struct rlimit rlim_new = {
|
||||
.rlim_cur = RLIM_INFINITY,
|
||||
.rlim_max = RLIM_INFINITY,
|
||||
};
|
||||
|
||||
return setrlimit(RLIMIT_MEMLOCK, &rlim_new);
|
||||
}
|
||||
|
||||
void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
|
||||
{
|
||||
const struct runq_event *e = data;
|
||||
@ -133,11 +122,8 @@ int main(int argc, char **argv)
|
||||
|
||||
libbpf_set_print(libbpf_print_fn);
|
||||
|
||||
err = bump_memlock_rlimit();
|
||||
if (err) {
|
||||
fprintf(stderr, "failed to increase rlimit: %d", err);
|
||||
return 1;
|
||||
}
|
||||
/* Use libbpf 1.0 API mode */
|
||||
libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
|
||||
|
||||
obj = runqslower_bpf__open();
|
||||
if (!obj) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
#if defined(__aarch64__)
|
||||
#include "../../arch/arm64/include/uapi/asm/bpf_perf_event.h"
|
||||
#elif defined(__arc__)
|
||||
#include "../../arch/arc/include/uapi/asm/bpf_perf_event.h"
|
||||
#elif defined(__s390__)
|
||||
#include "../../arch/s390/include/uapi/asm/bpf_perf_event.h"
|
||||
#elif defined(__riscv)
|
||||
|
@ -5143,6 +5143,17 @@ union bpf_attr {
|
||||
* The **hash_algo** is returned on success,
|
||||
* **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if
|
||||
* invalid arguments are passed.
|
||||
*
|
||||
* void *bpf_kptr_xchg(void *map_value, void *ptr)
|
||||
* Description
|
||||
* Exchange kptr at pointer *map_value* with *ptr*, and return the
|
||||
* old value. *ptr* can be NULL, otherwise it must be a referenced
|
||||
* pointer which will be released when this helper is called.
|
||||
* Return
|
||||
* The old value of kptr (which can be NULL). The returned pointer
|
||||
* if not NULL, is a reference which must be released using its
|
||||
* corresponding release function, or moved into a BPF map before
|
||||
* program exit.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
@ -5339,6 +5350,7 @@ union bpf_attr {
|
||||
FN(copy_from_user_task), \
|
||||
FN(skb_set_tstamp), \
|
||||
FN(ima_file_hash), \
|
||||
FN(kptr_xchg), \
|
||||
/* */
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
|
@ -817,7 +817,7 @@ int bpf_link_create(int prog_fd, int target_fd,
|
||||
{
|
||||
__u32 target_btf_id, iter_info_len;
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
int fd, err;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_link_create_opts))
|
||||
return libbpf_err(-EINVAL);
|
||||
@ -870,7 +870,37 @@ int bpf_link_create(int prog_fd, int target_fd,
|
||||
}
|
||||
proceed:
|
||||
fd = sys_bpf_fd(BPF_LINK_CREATE, &attr, sizeof(attr));
|
||||
return libbpf_err_errno(fd);
|
||||
if (fd >= 0)
|
||||
return fd;
|
||||
/* we'll get EINVAL if LINK_CREATE doesn't support attaching fentry
|
||||
* and other similar programs
|
||||
*/
|
||||
err = -errno;
|
||||
if (err != -EINVAL)
|
||||
return libbpf_err(err);
|
||||
|
||||
/* if user used features not supported by
|
||||
* BPF_RAW_TRACEPOINT_OPEN command, then just give up immediately
|
||||
*/
|
||||
if (attr.link_create.target_fd || attr.link_create.target_btf_id)
|
||||
return libbpf_err(err);
|
||||
if (!OPTS_ZEROED(opts, sz))
|
||||
return libbpf_err(err);
|
||||
|
||||
/* otherwise, for few select kinds of programs that can be
|
||||
* attached using BPF_RAW_TRACEPOINT_OPEN command, try that as
|
||||
* a fallback for older kernels
|
||||
*/
|
||||
switch (attach_type) {
|
||||
case BPF_TRACE_RAW_TP:
|
||||
case BPF_LSM_MAC:
|
||||
case BPF_TRACE_FENTRY:
|
||||
case BPF_TRACE_FEXIT:
|
||||
case BPF_MODIFY_RETURN:
|
||||
return bpf_raw_tracepoint_open(NULL, prog_fd);
|
||||
default:
|
||||
return libbpf_err(err);
|
||||
}
|
||||
}
|
||||
|
||||
int bpf_link_detach(int link_fd)
|
||||
|
@ -149,6 +149,13 @@ enum libbpf_tristate {
|
||||
|
||||
#define __kconfig __attribute__((section(".kconfig")))
|
||||
#define __ksym __attribute__((section(".ksyms")))
|
||||
#if __has_attribute(btf_type_tag)
|
||||
#define __kptr __attribute__((btf_type_tag("kptr")))
|
||||
#define __kptr_ref __attribute__((btf_type_tag("kptr_ref")))
|
||||
#else
|
||||
#define __kptr
|
||||
#define __kptr_ref
|
||||
#endif
|
||||
|
||||
#ifndef ___bpf_concat
|
||||
#define ___bpf_concat(a, b) a ## b
|
||||
|
@ -27,6 +27,9 @@
|
||||
#elif defined(__TARGET_ARCH_riscv)
|
||||
#define bpf_target_riscv
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arc)
|
||||
#define bpf_target_arc
|
||||
#define bpf_target_defined
|
||||
#else
|
||||
|
||||
/* Fall back to what the compiler says */
|
||||
@ -54,6 +57,9 @@
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
#define bpf_target_riscv
|
||||
#define bpf_target_defined
|
||||
#elif defined(__arc__)
|
||||
#define bpf_target_arc
|
||||
#define bpf_target_defined
|
||||
#endif /* no compiler target */
|
||||
|
||||
#endif
|
||||
@ -233,6 +239,23 @@ struct pt_regs___arm64 {
|
||||
/* riscv does not select ARCH_HAS_SYSCALL_WRAPPER. */
|
||||
#define PT_REGS_SYSCALL_REGS(ctx) ctx
|
||||
|
||||
#elif defined(bpf_target_arc)
|
||||
|
||||
/* arc provides struct user_pt_regs instead of struct pt_regs to userspace */
|
||||
#define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x))
|
||||
#define __PT_PARM1_REG scratch.r0
|
||||
#define __PT_PARM2_REG scratch.r1
|
||||
#define __PT_PARM3_REG scratch.r2
|
||||
#define __PT_PARM4_REG scratch.r3
|
||||
#define __PT_PARM5_REG scratch.r4
|
||||
#define __PT_RET_REG scratch.blink
|
||||
#define __PT_FP_REG __unsupported__
|
||||
#define __PT_RC_REG scratch.r0
|
||||
#define __PT_SP_REG scratch.sp
|
||||
#define __PT_IP_REG scratch.ret
|
||||
/* arc does not select ARCH_HAS_SYSCALL_WRAPPER. */
|
||||
#define PT_REGS_SYSCALL_REGS(ctx) ctx
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(bpf_target_defined)
|
||||
|
@ -2626,6 +2626,7 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext,
|
||||
const struct btf_ext_info_sec *sinfo;
|
||||
struct btf_ext_info *ext_info;
|
||||
__u32 info_left, record_size;
|
||||
size_t sec_cnt = 0;
|
||||
/* The start of the info sec (including the __u32 record_size). */
|
||||
void *info;
|
||||
|
||||
@ -2689,8 +2690,7 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
total_record_size = sec_hdrlen +
|
||||
(__u64)num_records * record_size;
|
||||
total_record_size = sec_hdrlen + (__u64)num_records * record_size;
|
||||
if (info_left < total_record_size) {
|
||||
pr_debug("%s section has incorrect num_records in .BTF.ext\n",
|
||||
ext_sec->desc);
|
||||
@ -2699,12 +2699,14 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext,
|
||||
|
||||
info_left -= total_record_size;
|
||||
sinfo = (void *)sinfo + total_record_size;
|
||||
sec_cnt++;
|
||||
}
|
||||
|
||||
ext_info = ext_sec->ext_info;
|
||||
ext_info->len = ext_sec->len - sizeof(__u32);
|
||||
ext_info->rec_size = record_size;
|
||||
ext_info->info = info + sizeof(__u32);
|
||||
ext_info->sec_cnt = sec_cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2788,6 +2790,9 @@ void btf_ext__free(struct btf_ext *btf_ext)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(btf_ext))
|
||||
return;
|
||||
free(btf_ext->func_info.sec_idxs);
|
||||
free(btf_ext->line_info.sec_idxs);
|
||||
free(btf_ext->core_relo_info.sec_idxs);
|
||||
free(btf_ext->data);
|
||||
free(btf_ext);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user