mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'bpf-next-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Pull bpf updates from Alexei Starovoitov: - Support pulling non-linear xdp data with bpf_xdp_pull_data() kfunc (Amery Hung) Applied as a stable branch in bpf-next and net-next trees. - Support reading skb metadata via bpf_dynptr (Jakub Sitnicki) Also a stable branch in bpf-next and net-next trees. - Enforce expected_attach_type for tailcall compatibility (Daniel Borkmann) - Replace path-sensitive with path-insensitive live stack analysis in the verifier (Eduard Zingerman) This is a significant change in the verification logic. More details, motivation, long term plans are in the cover letter/merge commit. - Support signed BPF programs (KP Singh) This is another major feature that took years to materialize. Algorithm details are in the cover letter/marge commit - Add support for may_goto instruction to s390 JIT (Ilya Leoshkevich) - Add support for may_goto instruction to arm64 JIT (Puranjay Mohan) - Fix USDT SIB argument handling in libbpf (Jiawei Zhao) - Allow uprobe-bpf program to change context registers (Jiri Olsa) - Support signed loads from BPF arena (Kumar Kartikeya Dwivedi and Puranjay Mohan) - Allow access to union arguments in tracing programs (Leon Hwang) - Optimize rcu_read_lock() + migrate_disable() combination where it's used in BPF subsystem (Menglong Dong) - Introduce bpf_task_work_schedule*() kfuncs to schedule deferred execution of BPF callback in the context of a specific task using the kernel’s task_work infrastructure (Mykyta Yatsenko) - Enforce RCU protection for KF_RCU_PROTECTED kfuncs (Kumar Kartikeya Dwivedi) - Add stress test for rqspinlock in NMI (Kumar Kartikeya Dwivedi) - Improve the precision of tnum multiplier verifier operation (Nandakumar Edamana) - Use tnums to improve is_branch_taken() logic (Paul Chaignon) - Add support for atomic operations in arena in riscv JIT (Pu Lehui) - Report arena faults to BPF error stream (Puranjay Mohan) - Search for tracefs at /sys/kernel/tracing first in bpftool (Quentin Monnet) - Add bpf_strcasecmp() kfunc (Rong Tao) - Support lookup_and_delete_elem command in BPF_MAP_STACK_TRACE (Tao Chen) * tag 'bpf-next-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (197 commits) libbpf: Replace AF_ALG with open coded SHA-256 selftests/bpf: Add stress test for rqspinlock in NMI selftests/bpf: Add test case for different expected_attach_type bpf: Enforce expected_attach_type for tailcall compatibility bpftool: Remove duplicate string.h header bpf: Remove duplicate crypto/sha2.h header libbpf: Fix error when st-prefix_ops and ops from differ btf selftests/bpf: Test changing packet data from kfunc selftests/bpf: Add stacktrace map lookup_and_delete_elem test case selftests/bpf: Refactor stacktrace_map case with skeleton bpf: Add lookup_and_delete_elem for BPF_MAP_STACK_TRACE selftests/bpf: Fix flaky bpf_cookie selftest selftests/bpf: Test changing packet data from global functions with a kfunc bpf: Emit struct bpf_xdp_sock type in vmlinux BTF selftests/bpf: Task_work selftest cleanup fixes MAINTAINERS: Delete inactive maintainers from AF_XDP bpf: Mark kfuncs as __noclone selftests/bpf: Add kprobe multi write ctx attach test selftests/bpf: Add kprobe write ctx attach test selftests/bpf: Add uprobe context ip register change test ...
This commit is contained in:
commit
ae28ed4578
254 files changed
+11852
-2816
No files matched your search
@@ -3912,6 +3912,12 @@ S: C/ Federico Garcia Lorca 1 10-A
|
||||
S: Sevilla 41005
|
||||
S: Spain
|
||||
|
||||
N: Björn Töpel
|
||||
E: bjorn@kernel.org
|
||||
D: AF_XDP
|
||||
S: Gothenburg
|
||||
S: Sweden
|
||||
|
||||
N: Linus Torvalds
|
||||
E: torvalds@linux-foundation.org
|
||||
D: Original kernel hacker
|
||||
|
||||
@@ -335,9 +335,26 @@ consider doing refcnt != 0 check, especially when returning a KF_ACQUIRE
|
||||
pointer. Note as well that a KF_ACQUIRE kfunc that is KF_RCU should very likely
|
||||
also be KF_RET_NULL.
|
||||
|
||||
2.4.8 KF_RCU_PROTECTED flag
|
||||
---------------------------
|
||||
|
||||
The KF_RCU_PROTECTED flag is used to indicate that the kfunc must be invoked in
|
||||
an RCU critical section. This is assumed by default in non-sleepable programs,
|
||||
and must be explicitly ensured by calling ``bpf_rcu_read_lock`` for sleepable
|
||||
ones.
|
||||
|
||||
If the kfunc returns a pointer value, this flag also enforces that the returned
|
||||
pointer is RCU protected, and can only be used while the RCU critical section is
|
||||
active.
|
||||
|
||||
The flag is distinct from the ``KF_RCU`` flag, which only ensures that its
|
||||
arguments are at least RCU protected pointers. This may transitively imply that
|
||||
RCU protection is ensured, but it does not work in cases of kfuncs which require
|
||||
RCU protection but do not take RCU protected arguments.
|
||||
|
||||
.. _KF_deprecated_flag:
|
||||
|
||||
2.4.8 KF_DEPRECATED flag
|
||||
2.4.9 KF_DEPRECATED flag
|
||||
------------------------
|
||||
|
||||
The KF_DEPRECATED flag is used for kfuncs which are scheduled to be
|
||||
|
||||
@@ -347,270 +347,6 @@ However, only the value of register ``r1`` is important to successfully finish
|
||||
verification. The goal of the liveness tracking algorithm is to spot this fact
|
||||
and figure out that both states are actually equivalent.
|
||||
|
||||
Data structures
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Liveness is tracked using the following data structures::
|
||||
|
||||
enum bpf_reg_liveness {
|
||||
REG_LIVE_NONE = 0,
|
||||
REG_LIVE_READ32 = 0x1,
|
||||
REG_LIVE_READ64 = 0x2,
|
||||
REG_LIVE_READ = REG_LIVE_READ32 | REG_LIVE_READ64,
|
||||
REG_LIVE_WRITTEN = 0x4,
|
||||
REG_LIVE_DONE = 0x8,
|
||||
};
|
||||
|
||||
struct bpf_reg_state {
|
||||
...
|
||||
struct bpf_reg_state *parent;
|
||||
...
|
||||
enum bpf_reg_liveness live;
|
||||
...
|
||||
};
|
||||
|
||||
struct bpf_stack_state {
|
||||
struct bpf_reg_state spilled_ptr;
|
||||
...
|
||||
};
|
||||
|
||||
struct bpf_func_state {
|
||||
struct bpf_reg_state regs[MAX_BPF_REG];
|
||||
...
|
||||
struct bpf_stack_state *stack;
|
||||
}
|
||||
|
||||
struct bpf_verifier_state {
|
||||
struct bpf_func_state *frame[MAX_CALL_FRAMES];
|
||||
struct bpf_verifier_state *parent;
|
||||
...
|
||||
}
|
||||
|
||||
* ``REG_LIVE_NONE`` is an initial value assigned to ``->live`` fields upon new
|
||||
verifier state creation;
|
||||
|
||||
* ``REG_LIVE_WRITTEN`` means that the value of the register (or stack slot) is
|
||||
defined by some instruction verified between this verifier state's parent and
|
||||
verifier state itself;
|
||||
|
||||
* ``REG_LIVE_READ{32,64}`` means that the value of the register (or stack slot)
|
||||
is read by a some child state of this verifier state;
|
||||
|
||||
* ``REG_LIVE_DONE`` is a marker used by ``clean_verifier_state()`` to avoid
|
||||
processing same verifier state multiple times and for some sanity checks;
|
||||
|
||||
* ``->live`` field values are formed by combining ``enum bpf_reg_liveness``
|
||||
values using bitwise or.
|
||||
|
||||
Register parentage chains
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In order to propagate information between parent and child states, a *register
|
||||
parentage chain* is established. Each register or stack slot is linked to a
|
||||
corresponding register or stack slot in its parent state via a ``->parent``
|
||||
pointer. This link is established upon state creation in ``is_state_visited()``
|
||||
and might be modified by ``set_callee_state()`` called from
|
||||
``__check_func_call()``.
|
||||
|
||||
The rules for correspondence between registers / stack slots are as follows:
|
||||
|
||||
* For the current stack frame, registers and stack slots of the new state are
|
||||
linked to the registers and stack slots of the parent state with the same
|
||||
indices.
|
||||
|
||||
* For the outer stack frames, only callee saved registers (r6-r9) and stack
|
||||
slots are linked to the registers and stack slots of the parent state with the
|
||||
same indices.
|
||||
|
||||
* When function call is processed a new ``struct bpf_func_state`` instance is
|
||||
allocated, it encapsulates a new set of registers and stack slots. For this
|
||||
new frame, parent links for r6-r9 and stack slots are set to nil, parent links
|
||||
for r1-r5 are set to match caller r1-r5 parent links.
|
||||
|
||||
This could be illustrated by the following diagram (arrows stand for
|
||||
``->parent`` pointers)::
|
||||
|
||||
... ; Frame #0, some instructions
|
||||
--- checkpoint #0 ---
|
||||
1 : r6 = 42 ; Frame #0
|
||||
--- checkpoint #1 ---
|
||||
2 : call foo() ; Frame #0
|
||||
... ; Frame #1, instructions from foo()
|
||||
--- checkpoint #2 ---
|
||||
... ; Frame #1, instructions from foo()
|
||||
--- checkpoint #3 ---
|
||||
exit ; Frame #1, return from foo()
|
||||
3 : r1 = r6 ; Frame #0 <- current state
|
||||
|
||||
+-------------------------------+-------------------------------+
|
||||
| Frame #0 | Frame #1 |
|
||||
Checkpoint +-------------------------------+-------------------------------+
|
||||
#0 | r0 | r1-r5 | r6-r9 | fp-8 ... |
|
||||
+-------------------------------+
|
||||
^ ^ ^ ^
|
||||
| | | |
|
||||
Checkpoint +-------------------------------+
|
||||
#1 | r0 | r1-r5 | r6-r9 | fp-8 ... |
|
||||
+-------------------------------+
|
||||
^ ^ ^
|
||||
|_______|_______|_______________
|
||||
| | |
|
||||
nil nil | | | nil nil
|
||||
| | | | | | |
|
||||
Checkpoint +-------------------------------+-------------------------------+
|
||||
#2 | r0 | r1-r5 | r6-r9 | fp-8 ... | r0 | r1-r5 | r6-r9 | fp-8 ... |
|
||||
+-------------------------------+-------------------------------+
|
||||
^ ^ ^ ^ ^
|
||||
nil nil | | | | |
|
||||
| | | | | | |
|
||||
Checkpoint +-------------------------------+-------------------------------+
|
||||
#3 | r0 | r1-r5 | r6-r9 | fp-8 ... | r0 | r1-r5 | r6-r9 | fp-8 ... |
|
||||
+-------------------------------+-------------------------------+
|
||||
^ ^
|
||||
nil nil | |
|
||||
| | | |
|
||||
Current +-------------------------------+
|
||||
state | r0 | r1-r5 | r6-r9 | fp-8 ... |
|
||||
+-------------------------------+
|
||||
\
|
||||
r6 read mark is propagated via these links
|
||||
all the way up to checkpoint #1.
|
||||
The checkpoint #1 contains a write mark for r6
|
||||
because of instruction (1), thus read propagation
|
||||
does not reach checkpoint #0 (see section below).
|
||||
|
||||
Liveness marks tracking
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For each processed instruction, the verifier tracks read and written registers
|
||||
and stack slots. The main idea of the algorithm is that read marks propagate
|
||||
back along the state parentage chain until they hit a write mark, which 'screens
|
||||
off' earlier states from the read. The information about reads is propagated by
|
||||
function ``mark_reg_read()`` which could be summarized as follows::
|
||||
|
||||
mark_reg_read(struct bpf_reg_state *state, ...):
|
||||
parent = state->parent
|
||||
while parent:
|
||||
if state->live & REG_LIVE_WRITTEN:
|
||||
break
|
||||
if parent->live & REG_LIVE_READ64:
|
||||
break
|
||||
parent->live |= REG_LIVE_READ64
|
||||
state = parent
|
||||
parent = state->parent
|
||||
|
||||
Notes:
|
||||
|
||||
* The read marks are applied to the **parent** state while write marks are
|
||||
applied to the **current** state. The write mark on a register or stack slot
|
||||
means that it is updated by some instruction in the straight-line code leading
|
||||
from the parent state to the current state.
|
||||
|
||||
* Details about REG_LIVE_READ32 are omitted.
|
||||
|
||||
* Function ``propagate_liveness()`` (see section :ref:`read_marks_for_cache_hits`)
|
||||
might override the first parent link. Please refer to the comments in the
|
||||
``propagate_liveness()`` and ``mark_reg_read()`` source code for further
|
||||
details.
|
||||
|
||||
Because stack writes could have different sizes ``REG_LIVE_WRITTEN`` marks are
|
||||
applied conservatively: stack slots are marked as written only if write size
|
||||
corresponds to the size of the register, e.g. see function ``save_register_state()``.
|
||||
|
||||
Consider the following example::
|
||||
|
||||
0: (*u64)(r10 - 8) = 0 ; define 8 bytes of fp-8
|
||||
--- checkpoint #0 ---
|
||||
1: (*u32)(r10 - 8) = 1 ; redefine lower 4 bytes
|
||||
2: r1 = (*u32)(r10 - 8) ; read lower 4 bytes defined at (1)
|
||||
3: r2 = (*u32)(r10 - 4) ; read upper 4 bytes defined at (0)
|
||||
|
||||
As stated above, the write at (1) does not count as ``REG_LIVE_WRITTEN``. Should
|
||||
it be otherwise, the algorithm above wouldn't be able to propagate the read mark
|
||||
from (3) to checkpoint #0.
|
||||
|
||||
Once the ``BPF_EXIT`` instruction is reached ``update_branch_counts()`` is
|
||||
called to update the ``->branches`` counter for each verifier state in a chain
|
||||
of parent verifier states. When the ``->branches`` counter reaches zero the
|
||||
verifier state becomes a valid entry in a set of cached verifier states.
|
||||
|
||||
Each entry of the verifier states cache is post-processed by a function
|
||||
``clean_live_states()``. This function marks all registers and stack slots
|
||||
without ``REG_LIVE_READ{32,64}`` marks as ``NOT_INIT`` or ``STACK_INVALID``.
|
||||
Registers/stack slots marked in this way are ignored in function ``stacksafe()``
|
||||
called from ``states_equal()`` when a state cache entry is considered for
|
||||
equivalence with a current state.
|
||||
|
||||
Now it is possible to explain how the example from the beginning of the section
|
||||
works::
|
||||
|
||||
0: call bpf_get_prandom_u32()
|
||||
1: r1 = 0
|
||||
2: if r0 == 0 goto +1
|
||||
3: r0 = 1
|
||||
--- checkpoint[0] ---
|
||||
4: r0 = r1
|
||||
5: exit
|
||||
|
||||
* At instruction #2 branching point is reached and state ``{ r0 == 0, r1 == 0, pc == 4 }``
|
||||
is pushed to states processing queue (pc stands for program counter).
|
||||
|
||||
* At instruction #4:
|
||||
|
||||
* ``checkpoint[0]`` states cache entry is created: ``{ r0 == 1, r1 == 0, pc == 4 }``;
|
||||
* ``checkpoint[0].r0`` is marked as written;
|
||||
* ``checkpoint[0].r1`` is marked as read;
|
||||
|
||||
* At instruction #5 exit is reached and ``checkpoint[0]`` can now be processed
|
||||
by ``clean_live_states()``. After this processing ``checkpoint[0].r1`` has a
|
||||
read mark and all other registers and stack slots are marked as ``NOT_INIT``
|
||||
or ``STACK_INVALID``
|
||||
|
||||
* The state ``{ r0 == 0, r1 == 0, pc == 4 }`` is popped from the states queue
|
||||
and is compared against a cached state ``{ r1 == 0, pc == 4 }``, the states
|
||||
are considered equivalent.
|
||||
|
||||
.. _read_marks_for_cache_hits:
|
||||
|
||||
Read marks propagation for cache hits
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Another point is the handling of read marks when a previously verified state is
|
||||
found in the states cache. Upon cache hit verifier must behave in the same way
|
||||
as if the current state was verified to the program exit. This means that all
|
||||
read marks, present on registers and stack slots of the cached state, must be
|
||||
propagated over the parentage chain of the current state. Example below shows
|
||||
why this is important. Function ``propagate_liveness()`` handles this case.
|
||||
|
||||
Consider the following state parentage chain (S is a starting state, A-E are
|
||||
derived states, -> arrows show which state is derived from which)::
|
||||
|
||||
r1 read
|
||||
<------------- A[r1] == 0
|
||||
C[r1] == 0
|
||||
S ---> A ---> B ---> exit E[r1] == 1
|
||||
|
|
||||
` ---> C ---> D
|
||||
|
|
||||
` ---> E ^
|
||||
|___ suppose all these
|
||||
^ states are at insn #Y
|
||||
|
|
||||
suppose all these
|
||||
states are at insn #X
|
||||
|
||||
* Chain of states ``S -> A -> B -> exit`` is verified first.
|
||||
|
||||
* While ``B -> exit`` is verified, register ``r1`` is read and this read mark is
|
||||
propagated up to state ``A``.
|
||||
|
||||
* When chain of states ``C -> D`` is verified the state ``D`` turns out to be
|
||||
equivalent to state ``B``.
|
||||
|
||||
* The read mark for ``r1`` has to be propagated to state ``C``, otherwise state
|
||||
``C`` might get mistakenly marked as equivalent to state ``E`` even though
|
||||
values for register ``r1`` differ between ``C`` and ``E``.
|
||||
|
||||
Understanding eBPF verifier messages
|
||||
====================================
|
||||
|
||||
|
||||
@@ -27466,10 +27466,8 @@ F: tools/testing/selftests/bpf/*xdp*
|
||||
K: (?:\b|_)xdp(?:\b|_)
|
||||
|
||||
XDP SOCKETS (AF_XDP)
|
||||
M: Björn Töpel <bjorn@kernel.org>
|
||||
M: Magnus Karlsson <magnus.karlsson@intel.com>
|
||||
M: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
|
||||
R: Jonathan Lemon <jonathan.lemon@gmail.com>
|
||||
R: Stanislav Fomichev <sdf@fomichev.me>
|
||||
L: netdev@vger.kernel.org
|
||||
L: bpf@vger.kernel.org
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
#
|
||||
# ARM64 networking code
|
||||
#
|
||||
obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o
|
||||
obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o bpf_timed_may_goto.o
|
||||
@@ -1066,19 +1066,53 @@ static void build_epilogue(struct jit_ctx *ctx, bool was_classic)
|
||||
emit(A64_RET(A64_LR), ctx);
|
||||
}
|
||||
|
||||
#define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0)
|
||||
/*
|
||||
* Metadata encoding for exception handling in JITed code.
|
||||
*
|
||||
* Format of `fixup` field in `struct exception_table_entry`:
|
||||
*
|
||||
* Bit layout of `fixup` (32-bit):
|
||||
*
|
||||
* +-----------+--------+-----------+-----------+----------+
|
||||
* | 31-27 | 26-22 | 21 | 20-16 | 15-0 |
|
||||
* | | | | | |
|
||||
* | FIXUP_REG | Unused | ARENA_ACC | ARENA_REG | OFFSET |
|
||||
* +-----------+--------+-----------+-----------+----------+
|
||||
*
|
||||
* - OFFSET (16 bits): Offset used to compute address for Load/Store instruction.
|
||||
* - ARENA_REG (5 bits): Register that is used to calculate the address for load/store when
|
||||
* accessing the arena region.
|
||||
* - ARENA_ACCESS (1 bit): This bit is set when the faulting instruction accessed the arena region.
|
||||
* - FIXUP_REG (5 bits): Destination register for the load instruction (cleared on fault) or set to
|
||||
* DONT_CLEAR if it is a store instruction.
|
||||
*/
|
||||
|
||||
#define BPF_FIXUP_OFFSET_MASK GENMASK(15, 0)
|
||||
#define BPF_FIXUP_ARENA_REG_MASK GENMASK(20, 16)
|
||||
#define BPF_ARENA_ACCESS BIT(21)
|
||||
#define BPF_FIXUP_REG_MASK GENMASK(31, 27)
|
||||
#define DONT_CLEAR 5 /* Unused ARM64 register from BPF's POV */
|
||||
|
||||
bool ex_handler_bpf(const struct exception_table_entry *ex,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup);
|
||||
int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup);
|
||||
s16 off = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup);
|
||||
int arena_reg = FIELD_GET(BPF_FIXUP_ARENA_REG_MASK, ex->fixup);
|
||||
bool is_arena = !!(ex->fixup & BPF_ARENA_ACCESS);
|
||||
bool is_write = (dst_reg == DONT_CLEAR);
|
||||
unsigned long addr;
|
||||
|
||||
if (is_arena) {
|
||||
addr = regs->regs[arena_reg] + off;
|
||||
bpf_prog_report_arena_violation(is_write, addr, regs->pc);
|
||||
}
|
||||
|
||||
if (dst_reg != DONT_CLEAR)
|
||||
regs->regs[dst_reg] = 0;
|
||||
regs->pc = (unsigned long)&ex->fixup - offset;
|
||||
/* Skip the faulting instruction */
|
||||
regs->pc += AARCH64_INSN_SIZE;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1088,7 +1122,9 @@ static int add_exception_handler(const struct bpf_insn *insn,
|
||||
int dst_reg)
|
||||
{
|
||||
off_t ins_offset;
|
||||
off_t fixup_offset;
|
||||
s16 off = insn->off;
|
||||
bool is_arena;
|
||||
int arena_reg;
|
||||
unsigned long pc;
|
||||
struct exception_table_entry *ex;
|
||||
|
||||
@@ -1097,11 +1133,16 @@ static int add_exception_handler(const struct bpf_insn *insn,
|
||||
return 0;
|
||||
|
||||
if (BPF_MODE(insn->code) != BPF_PROBE_MEM &&
|
||||
BPF_MODE(insn->code) != BPF_PROBE_MEMSX &&
|
||||
BPF_MODE(insn->code) != BPF_PROBE_MEM32 &&
|
||||
BPF_MODE(insn->code) != BPF_PROBE_ATOMIC)
|
||||
BPF_MODE(insn->code) != BPF_PROBE_MEMSX &&
|
||||
BPF_MODE(insn->code) != BPF_PROBE_MEM32 &&
|
||||
BPF_MODE(insn->code) != BPF_PROBE_MEM32SX &&
|
||||
BPF_MODE(insn->code) != BPF_PROBE_ATOMIC)
|
||||
return 0;
|
||||
|
||||
is_arena = (BPF_MODE(insn->code) == BPF_PROBE_MEM32) ||
|
||||
(BPF_MODE(insn->code) == BPF_PROBE_MEM32SX) ||
|
||||
(BPF_MODE(insn->code) == BPF_PROBE_ATOMIC);
|
||||
|
||||
if (!ctx->prog->aux->extable ||
|
||||
WARN_ON_ONCE(ctx->exentry_idx >= ctx->prog->aux->num_exentries))
|
||||
return -EINVAL;
|
||||
@@ -1119,22 +1160,6 @@ static int add_exception_handler(const struct bpf_insn *insn,
|
||||
if (WARN_ON_ONCE(ins_offset >= 0 || ins_offset < INT_MIN))
|
||||
return -ERANGE;
|
||||
|
||||
/*
|
||||
* Since the extable follows the program, the fixup offset is always
|
||||
* negative and limited to BPF_JIT_REGION_SIZE. Store a positive value
|
||||
* to keep things simple, and put the destination register in the upper
|
||||
* bits. We don't need to worry about buildtime or runtime sort
|
||||
* modifying the upper bits because the table is already sorted, and
|
||||
* isn't part of the main exception table.
|
||||
*
|
||||
* The fixup_offset is set to the next instruction from the instruction
|
||||
* that may fault. The execution will jump to this after handling the
|
||||
* fault.
|
||||
*/
|
||||
fixup_offset = (long)&ex->fixup - (pc + AARCH64_INSN_SIZE);
|
||||
if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, fixup_offset))
|
||||
return -ERANGE;
|
||||
|
||||
/*
|
||||
* The offsets above have been calculated using the RO buffer but we
|
||||
* need to use the R/W buffer for writes.
|
||||
@@ -1147,8 +1172,26 @@ static int add_exception_handler(const struct bpf_insn *insn,
|
||||
if (BPF_CLASS(insn->code) != BPF_LDX)
|
||||
dst_reg = DONT_CLEAR;
|
||||
|
||||
ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, fixup_offset) |
|
||||
FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg);
|
||||
ex->fixup = FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg);
|
||||
|
||||
if (is_arena) {
|
||||
ex->fixup |= BPF_ARENA_ACCESS;
|
||||
/*
|
||||
* insn->src_reg/dst_reg holds the address in the arena region with upper 32-bits
|
||||
* being zero because of a preceding addr_space_cast(r<n>, 0x0, 0x1) instruction.
|
||||
* This address is adjusted with the addition of arena_vm_start (see the
|
||||
* implementation of BPF_PROBE_MEM32 and BPF_PROBE_ATOMIC) before being used for the
|
||||
* memory access. Pass the reg holding the unmodified 32-bit address to
|
||||
* ex_handler_bpf.
|
||||
*/
|
||||
if (BPF_CLASS(insn->code) == BPF_LDX)
|
||||
arena_reg = bpf2a64[insn->src_reg];
|
||||
else
|
||||
arena_reg = bpf2a64[insn->dst_reg];
|
||||
|
||||
ex->fixup |= FIELD_PREP(BPF_FIXUP_OFFSET_MASK, off) |
|
||||
FIELD_PREP(BPF_FIXUP_ARENA_REG_MASK, arena_reg);
|
||||
}
|
||||
|
||||
ex->type = EX_TYPE_BPF;
|
||||
|
||||
@@ -1558,7 +1601,13 @@ emit_cond_jmp:
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
emit_call(func_addr, ctx);
|
||||
emit(A64_MOV(1, r0, A64_R(0)), ctx);
|
||||
/*
|
||||
* Call to arch_bpf_timed_may_goto() is emitted by the
|
||||
* verifier and called with custom calling convention with
|
||||
* first argument and return value in BPF_REG_AX (x9).
|
||||
*/
|
||||
if (func_addr != (u64)arch_bpf_timed_may_goto)
|
||||
emit(A64_MOV(1, r0, A64_R(0)), ctx);
|
||||
break;
|
||||
}
|
||||
/* tail call */
|
||||
@@ -1612,7 +1661,11 @@ emit_cond_jmp:
|
||||
case BPF_LDX | BPF_PROBE_MEM32 | BPF_H:
|
||||
case BPF_LDX | BPF_PROBE_MEM32 | BPF_W:
|
||||
case BPF_LDX | BPF_PROBE_MEM32 | BPF_DW:
|
||||
if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) {
|
||||
case BPF_LDX | BPF_PROBE_MEM32SX | BPF_B:
|
||||
case BPF_LDX | BPF_PROBE_MEM32SX | BPF_H:
|
||||
case BPF_LDX | BPF_PROBE_MEM32SX | BPF_W:
|
||||
if (BPF_MODE(insn->code) == BPF_PROBE_MEM32 ||
|
||||
BPF_MODE(insn->code) == BPF_PROBE_MEM32SX) {
|
||||
emit(A64_ADD(1, tmp2, src, arena_vm_base), ctx);
|
||||
src = tmp2;
|
||||
}
|
||||
@@ -1624,7 +1677,8 @@ emit_cond_jmp:
|
||||
off_adj = off;
|
||||
}
|
||||
sign_extend = (BPF_MODE(insn->code) == BPF_MEMSX ||
|
||||
BPF_MODE(insn->code) == BPF_PROBE_MEMSX);
|
||||
BPF_MODE(insn->code) == BPF_PROBE_MEMSX ||
|
||||
BPF_MODE(insn->code) == BPF_PROBE_MEM32SX);
|
||||
switch (BPF_SIZE(code)) {
|
||||
case BPF_W:
|
||||
if (is_lsi_offset(off_adj, 2)) {
|
||||
@@ -1832,9 +1886,11 @@ emit_cond_jmp:
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = add_exception_handler(insn, ctx, dst);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (BPF_MODE(insn->code) == BPF_PROBE_ATOMIC) {
|
||||
ret = add_exception_handler(insn, ctx, dst);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -2767,7 +2823,6 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *ro_image,
|
||||
goto out;
|
||||
}
|
||||
|
||||
bpf_flush_icache(ro_image, ro_image + size);
|
||||
out:
|
||||
kvfree(image);
|
||||
return ret;
|
||||
@@ -3038,6 +3093,11 @@ bool bpf_jit_bypass_spec_v4(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpf_jit_supports_timed_may_goto(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bpf_jit_inlines_helper_call(s32 imm)
|
||||
{
|
||||
switch (imm) {
|
||||
@@ -3064,8 +3124,7 @@ void bpf_jit_free(struct bpf_prog *prog)
|
||||
* before freeing it.
|
||||
*/
|
||||
if (jit_data) {
|
||||
bpf_arch_text_copy(&jit_data->ro_header->size, &jit_data->header->size,
|
||||
sizeof(jit_data->header->size));
|
||||
bpf_jit_binary_pack_finalize(jit_data->ro_header, jit_data->header);
|
||||
kfree(jit_data);
|
||||
}
|
||||
prog->bpf_func -= cfi_get_offset();
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2025 Puranjay Mohan <puranjay@kernel.org> */
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
SYM_FUNC_START(arch_bpf_timed_may_goto)
|
||||
/* Allocate stack space and emit frame record */
|
||||
stp x29, x30, [sp, #-64]!
|
||||
mov x29, sp
|
||||
|
||||
/* Save BPF registers R0 - R5 (x7, x0-x4)*/
|
||||
stp x7, x0, [sp, #16]
|
||||
stp x1, x2, [sp, #32]
|
||||
stp x3, x4, [sp, #48]
|
||||
|
||||
/*
|
||||
* Stack depth was passed in BPF_REG_AX (x9), add it to the BPF_FP
|
||||
* (x25) to get the pointer to count and timestamp and pass it as the
|
||||
* first argument in x0.
|
||||
*
|
||||
* Before generating the call to arch_bpf_timed_may_goto, the verifier
|
||||
* generates a load instruction using FP, i.e. REG_AX = *(u64 *)(FP -
|
||||
* stack_off_cnt), so BPF_REG_FP (x25) is always set up by the arm64
|
||||
* jit in this case.
|
||||
*/
|
||||
add x0, x9, x25
|
||||
bl bpf_check_timed_may_goto
|
||||
/* BPF_REG_AX(x9) will be stored into count, so move return value to it. */
|
||||
mov x9, x0
|
||||
|
||||
/* Restore BPF registers R0 - R5 (x7, x0-x4) */
|
||||
ldp x7, x0, [sp, #16]
|
||||
ldp x1, x2, [sp, #32]
|
||||
ldp x3, x4, [sp, #48]
|
||||
|
||||
/* Restore FP and LR */
|
||||
ldp x29, x30, [sp], #64
|
||||
|
||||
ret
|
||||
SYM_FUNC_END(arch_bpf_timed_may_goto)
|
||||
@@ -708,7 +708,6 @@ config TOOLCHAIN_HAS_ZACAS
|
||||
|
||||
config RISCV_ISA_ZACAS
|
||||
bool "Zacas extension support for atomic CAS"
|
||||
depends on TOOLCHAIN_HAS_ZACAS
|
||||
depends on RISCV_ALTERNATIVE
|
||||
default y
|
||||
help
|
||||
|
||||
@@ -134,6 +134,7 @@
|
||||
({ \
|
||||
if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) && \
|
||||
IS_ENABLED(CONFIG_RISCV_ISA_ZACAS) && \
|
||||
IS_ENABLED(CONFIG_TOOLCHAIN_HAS_ZACAS) && \
|
||||
riscv_has_extension_unlikely(RISCV_ISA_EXT_ZABHA) && \
|
||||
riscv_has_extension_unlikely(RISCV_ISA_EXT_ZACAS)) { \
|
||||
r = o; \
|
||||
@@ -181,6 +182,7 @@
|
||||
r, p, co, o, n) \
|
||||
({ \
|
||||
if (IS_ENABLED(CONFIG_RISCV_ISA_ZACAS) && \
|
||||
IS_ENABLED(CONFIG_TOOLCHAIN_HAS_ZACAS) && \
|
||||
riscv_has_extension_unlikely(RISCV_ISA_EXT_ZACAS)) { \
|
||||
r = o; \
|
||||
\
|
||||
@@ -316,7 +318,7 @@
|
||||
arch_cmpxchg_release((ptr), (o), (n)); \
|
||||
})
|
||||
|
||||
#if defined(CONFIG_64BIT) && defined(CONFIG_RISCV_ISA_ZACAS)
|
||||
#if defined(CONFIG_64BIT) && defined(CONFIG_RISCV_ISA_ZACAS) && defined(CONFIG_TOOLCHAIN_HAS_ZACAS)
|
||||
|
||||
#define system_has_cmpxchg128() riscv_has_extension_unlikely(RISCV_ISA_EXT_ZACAS)
|
||||
|
||||
@@ -352,7 +354,7 @@ union __u128_halves {
|
||||
#define arch_cmpxchg128_local(ptr, o, n) \
|
||||
__arch_cmpxchg128((ptr), (o), (n), "")
|
||||
|
||||
#endif /* CONFIG_64BIT && CONFIG_RISCV_ISA_ZACAS */
|
||||
#endif /* CONFIG_64BIT && CONFIG_RISCV_ISA_ZACAS && CONFIG_TOOLCHAIN_HAS_ZACAS */
|
||||
|
||||
#ifdef CONFIG_RISCV_ISA_ZAWRS
|
||||
/*
|
||||
|
||||
@@ -290,6 +290,7 @@ static void __init riscv_spinlock_init(void)
|
||||
|
||||
if (IS_ENABLED(CONFIG_RISCV_ISA_ZABHA) &&
|
||||
IS_ENABLED(CONFIG_RISCV_ISA_ZACAS) &&
|
||||
IS_ENABLED(CONFIG_TOOLCHAIN_HAS_ZACAS) &&
|
||||
riscv_isa_extension_available(NULL, ZABHA) &&
|
||||
riscv_isa_extension_available(NULL, ZACAS)) {
|
||||
using_ext = "using Zabha";
|
||||
|
||||
+53
-17
@@ -13,21 +13,15 @@
|
||||
#include <linux/filter.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
/* verify runtime detection extension status */
|
||||
#define rv_ext_enabled(ext) \
|
||||
(IS_ENABLED(CONFIG_RISCV_ISA_##ext) && riscv_has_extension_likely(RISCV_ISA_EXT_##ext))
|
||||
|
||||
static inline bool rvc_enabled(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_RISCV_ISA_C);
|
||||
}
|
||||
|
||||
static inline bool rvzba_enabled(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_RISCV_ISA_ZBA) && riscv_has_extension_likely(RISCV_ISA_EXT_ZBA);
|
||||
}
|
||||
|
||||
static inline bool rvzbb_enabled(void)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_RISCV_ISA_ZBB) && riscv_has_extension_likely(RISCV_ISA_EXT_ZBB);
|
||||
}
|
||||
|
||||
enum {
|
||||
RV_REG_ZERO = 0, /* The constant value 0 */
|
||||
RV_REG_RA = 1, /* Return address */
|
||||
@@ -84,6 +78,8 @@ struct rv_jit_context {
|
||||
int epilogue_offset;
|
||||
int *offset; /* BPF to RV */
|
||||
int nexentries;
|
||||
int ex_insn_off;
|
||||
int ex_jmp_off;
|
||||
unsigned long flags;
|
||||
int stack_size;
|
||||
u64 arena_vm_start;
|
||||
@@ -757,6 +753,17 @@ static inline u16 rvc_swsp(u32 imm8, u8 rs2)
|
||||
return rv_css_insn(0x6, imm, rs2, 0x2);
|
||||
}
|
||||
|
||||
/* RVZACAS instructions. */
|
||||
static inline u32 rvzacas_amocas_w(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x5, aq, rl, rs2, rs1, 2, rd, 0x2f);
|
||||
}
|
||||
|
||||
static inline u32 rvzacas_amocas_d(u8 rd, u8 rs2, u8 rs1, u8 aq, u8 rl)
|
||||
{
|
||||
return rv_amo_insn(0x5, aq, rl, rs2, rs1, 3, rd, 0x2f);
|
||||
}
|
||||
|
||||
/* RVZBA instructions. */
|
||||
static inline u32 rvzba_sh2add(u8 rd, u8 rs1, u8 rs2)
|
||||
{
|
||||
@@ -1123,7 +1130,7 @@ static inline void emit_sw(u8 rs1, s32 off, u8 rs2, struct rv_jit_context *ctx)
|
||||
|
||||
static inline void emit_sh2add(u8 rd, u8 rs1, u8 rs2, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (rvzba_enabled()) {
|
||||
if (rv_ext_enabled(ZBA)) {
|
||||
emit(rvzba_sh2add(rd, rs1, rs2), ctx);
|
||||
return;
|
||||
}
|
||||
@@ -1134,7 +1141,7 @@ static inline void emit_sh2add(u8 rd, u8 rs1, u8 rs2, struct rv_jit_context *ctx
|
||||
|
||||
static inline void emit_sh3add(u8 rd, u8 rs1, u8 rs2, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (rvzba_enabled()) {
|
||||
if (rv_ext_enabled(ZBA)) {
|
||||
emit(rvzba_sh3add(rd, rs1, rs2), ctx);
|
||||
return;
|
||||
}
|
||||
@@ -1184,7 +1191,7 @@ static inline void emit_subw(u8 rd, u8 rs1, u8 rs2, struct rv_jit_context *ctx)
|
||||
|
||||
static inline void emit_sextb(u8 rd, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (rvzbb_enabled()) {
|
||||
if (rv_ext_enabled(ZBB)) {
|
||||
emit(rvzbb_sextb(rd, rs), ctx);
|
||||
return;
|
||||
}
|
||||
@@ -1195,7 +1202,7 @@ static inline void emit_sextb(u8 rd, u8 rs, struct rv_jit_context *ctx)
|
||||
|
||||
static inline void emit_sexth(u8 rd, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (rvzbb_enabled()) {
|
||||
if (rv_ext_enabled(ZBB)) {
|
||||
emit(rvzbb_sexth(rd, rs), ctx);
|
||||
return;
|
||||
}
|
||||
@@ -1211,7 +1218,7 @@ static inline void emit_sextw(u8 rd, u8 rs, struct rv_jit_context *ctx)
|
||||
|
||||
static inline void emit_zexth(u8 rd, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (rvzbb_enabled()) {
|
||||
if (rv_ext_enabled(ZBB)) {
|
||||
emit(rvzbb_zexth(rd, rs), ctx);
|
||||
return;
|
||||
}
|
||||
@@ -1222,7 +1229,7 @@ static inline void emit_zexth(u8 rd, u8 rs, struct rv_jit_context *ctx)
|
||||
|
||||
static inline void emit_zextw(u8 rd, u8 rs, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (rvzba_enabled()) {
|
||||
if (rv_ext_enabled(ZBA)) {
|
||||
emit(rvzba_zextw(rd, rs), ctx);
|
||||
return;
|
||||
}
|
||||
@@ -1233,7 +1240,7 @@ static inline void emit_zextw(u8 rd, u8 rs, struct rv_jit_context *ctx)
|
||||
|
||||
static inline void emit_bswap(u8 rd, s32 imm, struct rv_jit_context *ctx)
|
||||
{
|
||||
if (rvzbb_enabled()) {
|
||||
if (rv_ext_enabled(ZBB)) {
|
||||
int bits = 64 - imm;
|
||||
|
||||
emit(rvzbb_rev8(rd, rd), ctx);
|
||||
@@ -1289,6 +1296,35 @@ out_be:
|
||||
emit_mv(rd, RV_REG_T2, ctx);
|
||||
}
|
||||
|
||||
static inline void emit_cmpxchg(u8 rd, u8 rs, u8 r0, bool is64, struct rv_jit_context *ctx)
|
||||
{
|
||||
int jmp_offset;
|
||||
|
||||
if (rv_ext_enabled(ZACAS)) {
|
||||
ctx->ex_insn_off = ctx->ninsns;
|
||||
emit(is64 ? rvzacas_amocas_d(r0, rs, rd, 1, 1) :
|
||||
rvzacas_amocas_w(r0, rs, rd, 1, 1), ctx);
|
||||
ctx->ex_jmp_off = ctx->ninsns;
|
||||
if (!is64)
|
||||
emit_zextw(r0, r0, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is64)
|
||||
emit_mv(RV_REG_T2, r0, ctx);
|
||||
else
|
||||
emit_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, 1) :
|
||||
rv_sc_w(RV_REG_T3, rs, rd, 0, 1), ctx);
|
||||
jmp_offset = ninsns_rvoff(-6);
|
||||
emit(rv_bne(RV_REG_T3, 0, jmp_offset >> 1), ctx);
|
||||
emit_fence_rw_rw(ctx);
|
||||
}
|
||||
|
||||
#endif /* __riscv_xlen == 64 */
|
||||
|
||||
void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog);
|
||||
|
||||
+220
-393
File diff suppressed because it is too large.
Load diff
@@ -2,5 +2,5 @@
|
||||
#
|
||||
# Arch-specific network modules
|
||||
#
|
||||
obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o
|
||||
obj-$(CONFIG_BPF_JIT) += bpf_jit_comp.o bpf_timed_may_goto.o
|
||||
obj-$(CONFIG_HAVE_PNETID) += pnet.o
|
||||
@@ -674,20 +674,6 @@ static void bpf_jit_prologue(struct bpf_jit *jit, struct bpf_prog *fp)
|
||||
_EMIT2(0x07f0 | reg); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Call r1 either directly or via __s390_indirect_jump_r1 thunk
|
||||
*/
|
||||
static void call_r1(struct bpf_jit *jit)
|
||||
{
|
||||
if (nospec_uses_trampoline())
|
||||
/* brasl %r14,__s390_indirect_jump_r1 */
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14,
|
||||
__s390_indirect_jump_r1);
|
||||
else
|
||||
/* basr %r14,%r1 */
|
||||
EMIT2(0x0d00, REG_14, REG_1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function epilogue
|
||||
*/
|
||||
@@ -1790,20 +1776,21 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
||||
|
||||
REG_SET_SEEN(BPF_REG_5);
|
||||
jit->seen |= SEEN_FUNC;
|
||||
|
||||
/*
|
||||
* Copy the tail call counter to where the callee expects it.
|
||||
*
|
||||
* Note 1: The callee can increment the tail call counter, but
|
||||
* we do not load it back, since the x86 JIT does not do this
|
||||
* either.
|
||||
*
|
||||
* Note 2: We assume that the verifier does not let us call the
|
||||
* main program, which clears the tail call counter on entry.
|
||||
*/
|
||||
/* mvc tail_call_cnt(4,%r15),frame_off+tail_call_cnt(%r15) */
|
||||
_EMIT6(0xd203f000 | offsetof(struct prog_frame, tail_call_cnt),
|
||||
0xf000 | (jit->frame_off +
|
||||
offsetof(struct prog_frame, tail_call_cnt)));
|
||||
|
||||
if (insn->src_reg == BPF_PSEUDO_CALL)
|
||||
/*
|
||||
* mvc tail_call_cnt(4,%r15),
|
||||
* frame_off+tail_call_cnt(%r15)
|
||||
*/
|
||||
_EMIT6(0xd203f000 | offsetof(struct prog_frame,
|
||||
tail_call_cnt),
|
||||
0xf000 | (jit->frame_off +
|
||||
offsetof(struct prog_frame,
|
||||
tail_call_cnt)));
|
||||
|
||||
/* Sign-extend the kfunc arguments. */
|
||||
if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
|
||||
@@ -1819,12 +1806,38 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
|
||||
}
|
||||
}
|
||||
|
||||
/* lgrl %w1,func */
|
||||
EMIT6_PCREL_RILB(0xc4080000, REG_W1, _EMIT_CONST_U64(func));
|
||||
/* %r1() */
|
||||
call_r1(jit);
|
||||
/* lgr %b0,%r2: load return value into %b0 */
|
||||
EMIT4(0xb9040000, BPF_REG_0, REG_2);
|
||||
if ((void *)func == arch_bpf_timed_may_goto) {
|
||||
/*
|
||||
* arch_bpf_timed_may_goto() has a special ABI: the
|
||||
* parameters are in BPF_REG_AX and BPF_REG_10; the
|
||||
* return value is in BPF_REG_AX; and all GPRs except
|
||||
* REG_W0, REG_W1, and BPF_REG_AX are callee-saved.
|
||||
*/
|
||||
|
||||
/* brasl %r0,func */
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_0, (void *)func);
|
||||
} else {
|
||||
/* brasl %r14,func */
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14, (void *)func);
|
||||
/* lgr %b0,%r2: load return value into %b0 */
|
||||
EMIT4(0xb9040000, BPF_REG_0, REG_2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the potentially updated tail call counter back.
|
||||
*/
|
||||
|
||||
if (insn->src_reg == BPF_PSEUDO_CALL)
|
||||
/*
|
||||
* mvc frame_off+tail_call_cnt(%r15),
|
||||
* tail_call_cnt(4,%r15)
|
||||
*/
|
||||
_EMIT6(0xd203f000 | (jit->frame_off +
|
||||
offsetof(struct prog_frame,
|
||||
tail_call_cnt)),
|
||||
0xf000 | offsetof(struct prog_frame,
|
||||
tail_call_cnt));
|
||||
|
||||
break;
|
||||
}
|
||||
case BPF_JMP | BPF_TAIL_CALL: {
|
||||
@@ -2517,14 +2530,12 @@ static int invoke_bpf_prog(struct bpf_tramp_jit *tjit,
|
||||
* goto skip;
|
||||
*/
|
||||
|
||||
/* %r1 = __bpf_prog_enter */
|
||||
load_imm64(jit, REG_1, (u64)bpf_trampoline_enter(p));
|
||||
/* %r2 = p */
|
||||
load_imm64(jit, REG_2, (u64)p);
|
||||
/* la %r3,run_ctx_off(%r15) */
|
||||
EMIT4_DISP(0x41000000, REG_3, REG_15, tjit->run_ctx_off);
|
||||
/* %r1() */
|
||||
call_r1(jit);
|
||||
/* brasl %r14,__bpf_prog_enter */
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14, bpf_trampoline_enter(p));
|
||||
/* ltgr %r7,%r2 */
|
||||
EMIT4(0xb9020000, REG_7, REG_2);
|
||||
/* brcl 8,skip */
|
||||
@@ -2535,15 +2546,13 @@ static int invoke_bpf_prog(struct bpf_tramp_jit *tjit,
|
||||
* retval = bpf_func(args, p->insnsi);
|
||||
*/
|
||||
|
||||
/* %r1 = p->bpf_func */
|
||||
load_imm64(jit, REG_1, (u64)p->bpf_func);
|
||||
/* la %r2,bpf_args_off(%r15) */
|
||||
EMIT4_DISP(0x41000000, REG_2, REG_15, tjit->bpf_args_off);
|
||||
/* %r3 = p->insnsi */
|
||||
if (!p->jited)
|
||||
load_imm64(jit, REG_3, (u64)p->insnsi);
|
||||
/* %r1() */
|
||||
call_r1(jit);
|
||||
/* brasl %r14,p->bpf_func */
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14, p->bpf_func);
|
||||
/* stg %r2,retval_off(%r15) */
|
||||
if (save_ret) {
|
||||
if (sign_extend(jit, REG_2, m->ret_size, m->ret_flags))
|
||||
@@ -2560,16 +2569,14 @@ static int invoke_bpf_prog(struct bpf_tramp_jit *tjit,
|
||||
* __bpf_prog_exit(p, start, &run_ctx);
|
||||
*/
|
||||
|
||||
/* %r1 = __bpf_prog_exit */
|
||||
load_imm64(jit, REG_1, (u64)bpf_trampoline_exit(p));
|
||||
/* %r2 = p */
|
||||
load_imm64(jit, REG_2, (u64)p);
|
||||
/* lgr %r3,%r7 */
|
||||
EMIT4(0xb9040000, REG_3, REG_7);
|
||||
/* la %r4,run_ctx_off(%r15) */
|
||||
EMIT4_DISP(0x41000000, REG_4, REG_15, tjit->run_ctx_off);
|
||||
/* %r1() */
|
||||
call_r1(jit);
|
||||
/* brasl %r14,__bpf_prog_exit */
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14, bpf_trampoline_exit(p));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -2729,9 +2736,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
|
||||
|
||||
/* lgr %r8,%r0 */
|
||||
EMIT4(0xb9040000, REG_8, REG_0);
|
||||
} else {
|
||||
/* %r8 = func_addr + S390X_PATCH_SIZE */
|
||||
load_imm64(jit, REG_8, (u64)func_addr + S390X_PATCH_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2757,12 +2761,10 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
|
||||
* __bpf_tramp_enter(im);
|
||||
*/
|
||||
|
||||
/* %r1 = __bpf_tramp_enter */
|
||||
load_imm64(jit, REG_1, (u64)__bpf_tramp_enter);
|
||||
/* %r2 = im */
|
||||
load_imm64(jit, REG_2, (u64)im);
|
||||
/* %r1() */
|
||||
call_r1(jit);
|
||||
/* brasl %r14,__bpf_tramp_enter */
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14, __bpf_tramp_enter);
|
||||
}
|
||||
|
||||
for (i = 0; i < fentry->nr_links; i++)
|
||||
@@ -2815,13 +2817,25 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
|
||||
/* mvc tail_call_cnt(4,%r15),tccnt_off(%r15) */
|
||||
_EMIT6(0xd203f000 | offsetof(struct prog_frame, tail_call_cnt),
|
||||
0xf000 | tjit->tccnt_off);
|
||||
/* lgr %r1,%r8 */
|
||||
EMIT4(0xb9040000, REG_1, REG_8);
|
||||
/* %r1() */
|
||||
call_r1(jit);
|
||||
if (flags & BPF_TRAMP_F_ORIG_STACK) {
|
||||
if (nospec_uses_trampoline())
|
||||
/* brasl %r14,__s390_indirect_jump_r8 */
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14,
|
||||
__s390_indirect_jump_r8);
|
||||
else
|
||||
/* basr %r14,%r8 */
|
||||
EMIT2(0x0d00, REG_14, REG_8);
|
||||
} else {
|
||||
/* brasl %r14,func_addr+S390X_PATCH_SIZE */
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14,
|
||||
func_addr + S390X_PATCH_SIZE);
|
||||
}
|
||||
/* stg %r2,retval_off(%r15) */
|
||||
EMIT6_DISP_LH(0xe3000000, 0x0024, REG_2, REG_0, REG_15,
|
||||
tjit->retval_off);
|
||||
/* mvc tccnt_off(%r15),tail_call_cnt(4,%r15) */
|
||||
_EMIT6(0xd203f000 | tjit->tccnt_off,
|
||||
0xf000 | offsetof(struct prog_frame, tail_call_cnt));
|
||||
|
||||
im->ip_after_call = jit->prg_buf + jit->prg;
|
||||
|
||||
@@ -2846,12 +2860,10 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
|
||||
* __bpf_tramp_exit(im);
|
||||
*/
|
||||
|
||||
/* %r1 = __bpf_tramp_exit */
|
||||
load_imm64(jit, REG_1, (u64)__bpf_tramp_exit);
|
||||
/* %r2 = im */
|
||||
load_imm64(jit, REG_2, (u64)im);
|
||||
/* %r1() */
|
||||
call_r1(jit);
|
||||
/* brasl %r14,__bpf_tramp_exit */
|
||||
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14, __bpf_tramp_exit);
|
||||
}
|
||||
|
||||
/* lmg %r2,%rN,reg_args_off(%r15) */
|
||||
@@ -2860,7 +2872,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
|
||||
REG_2 + (nr_reg_args - 1), REG_15,
|
||||
tjit->reg_args_off);
|
||||
/* lgr %r1,%r8 */
|
||||
if (!(flags & BPF_TRAMP_F_SKIP_FRAME))
|
||||
if (!(flags & BPF_TRAMP_F_SKIP_FRAME) &&
|
||||
(flags & BPF_TRAMP_F_ORIG_STACK))
|
||||
EMIT4(0xb9040000, REG_1, REG_8);
|
||||
/* lmg %r7,%r8,r7_r8_off(%r15) */
|
||||
EMIT6_DISP_LH(0xeb000000, 0x0004, REG_7, REG_8, REG_15,
|
||||
@@ -2879,9 +2892,12 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
|
||||
EMIT4_IMM(0xa70b0000, REG_15, tjit->stack_size);
|
||||
if (flags & BPF_TRAMP_F_SKIP_FRAME)
|
||||
EMIT_JUMP_REG(14);
|
||||
else
|
||||
else if (flags & BPF_TRAMP_F_ORIG_STACK)
|
||||
EMIT_JUMP_REG(1);
|
||||
|
||||
else
|
||||
/* brcl 0xf,func_addr+S390X_PATCH_SIZE */
|
||||
EMIT6_PCREL_RILC_PTR(0xc0040000, 0xf,
|
||||
func_addr + S390X_PATCH_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2951,6 +2967,11 @@ bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena)
|
||||
case BPF_STX | BPF_ATOMIC | BPF_DW:
|
||||
if (bpf_atomic_is_load_store(insn))
|
||||
return false;
|
||||
break;
|
||||
case BPF_LDX | BPF_MEMSX | BPF_B:
|
||||
case BPF_LDX | BPF_MEMSX | BPF_H:
|
||||
case BPF_LDX | BPF_MEMSX | BPF_W:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -2989,3 +3010,8 @@ void arch_bpf_stack_walk(bool (*consume_fn)(void *, u64, u64, u64),
|
||||
prev_addr = addr;
|
||||
}
|
||||
}
|
||||
|
||||
bool bpf_jit_supports_timed_may_goto(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/nospec-insn.h>
|
||||
|
||||
#define R2_OFF 0
|
||||
#define R5_OFF (R2_OFF + (5 - 2 + 1) * 8)
|
||||
#define R14_OFF (R5_OFF + 8)
|
||||
#define RETADDR_OFF (R14_OFF + 8)
|
||||
#define R15_OFF (RETADDR_OFF + 8)
|
||||
#define BACKCHAIN_OFF (R15_OFF + 8)
|
||||
#define FRAME_SIZE (BACKCHAIN_OFF + 8)
|
||||
#define FRAME_OFF (STACK_FRAME_OVERHEAD - FRAME_SIZE)
|
||||
#if (FRAME_OFF + BACKCHAIN_OFF) != __SF_BACKCHAIN
|
||||
#error Stack frame layout calculation is broken
|
||||
#endif
|
||||
|
||||
GEN_BR_THUNK %r1
|
||||
|
||||
SYM_FUNC_START(arch_bpf_timed_may_goto)
|
||||
/*
|
||||
* This function has a special ABI: the parameters are in %r12 and
|
||||
* %r13; the return value is in %r12; all GPRs except %r0, %r1, and
|
||||
* %r12 are callee-saved; and the return address is in %r0.
|
||||
*/
|
||||
stmg %r2,%r5,FRAME_OFF+R2_OFF(%r15)
|
||||
stg %r14,FRAME_OFF+R14_OFF(%r15)
|
||||
stg %r0,FRAME_OFF+RETADDR_OFF(%r15)
|
||||
stg %r15,FRAME_OFF+R15_OFF(%r15)
|
||||
lgr %r1,%r15
|
||||
lay %r15,-FRAME_SIZE(%r15)
|
||||
stg %r1,__SF_BACKCHAIN(%r15)
|
||||
|
||||
lay %r2,0(%r12,%r13)
|
||||
brasl %r14,bpf_check_timed_may_goto
|
||||
lgr %r12,%r2
|
||||
|
||||
lg %r15,FRAME_SIZE+FRAME_OFF+R15_OFF(%r15)
|
||||
lmg %r2,%r5,FRAME_OFF+R2_OFF(%r15)
|
||||
lg %r14,FRAME_OFF+R14_OFF(%r15)
|
||||
lg %r1,FRAME_OFF+RETADDR_OFF(%r15)
|
||||
BR_EX %r1
|
||||
SYM_FUNC_END(arch_bpf_timed_may_goto)
|
||||
+117
-8
@@ -8,6 +8,7 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/sort.h>
|
||||
@@ -1151,11 +1152,38 @@ static void emit_ldx_index(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, u32 i
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void emit_ldsx_index(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, u32 index_reg, int off)
|
||||
{
|
||||
u8 *prog = *pprog;
|
||||
|
||||
switch (size) {
|
||||
case BPF_B:
|
||||
/* movsx rax, byte ptr [rax + r12 + off] */
|
||||
EMIT3(add_3mod(0x48, src_reg, dst_reg, index_reg), 0x0F, 0xBE);
|
||||
break;
|
||||
case BPF_H:
|
||||
/* movsx rax, word ptr [rax + r12 + off] */
|
||||
EMIT3(add_3mod(0x48, src_reg, dst_reg, index_reg), 0x0F, 0xBF);
|
||||
break;
|
||||
case BPF_W:
|
||||
/* movsx rax, dword ptr [rax + r12 + off] */
|
||||
EMIT2(add_3mod(0x48, src_reg, dst_reg, index_reg), 0x63);
|
||||
break;
|
||||
}
|
||||
emit_insn_suffix_SIB(&prog, src_reg, dst_reg, index_reg, off);
|
||||
*pprog = prog;
|
||||
}
|
||||
|
||||
static void emit_ldx_r12(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
|
||||
{
|
||||
emit_ldx_index(pprog, size, dst_reg, src_reg, X86_REG_R12, off);
|
||||
}
|
||||
|
||||
static void emit_ldsx_r12(u8 **prog, u32 size, u32 dst_reg, u32 src_reg, int off)
|
||||
{
|
||||
emit_ldsx_index(prog, size, dst_reg, src_reg, X86_REG_R12, off);
|
||||
}
|
||||
|
||||
/* STX: *(u8*)(dst_reg + off) = src_reg */
|
||||
static void emit_stx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
|
||||
{
|
||||
@@ -1388,16 +1416,67 @@ static int emit_atomic_ld_st_index(u8 **pprog, u32 atomic_op, u32 size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Metadata encoding for exception handling in JITed code.
|
||||
*
|
||||
* Format of `fixup` and `data` fields in `struct exception_table_entry`:
|
||||
*
|
||||
* Bit layout of `fixup` (32-bit):
|
||||
*
|
||||
* +-----------+--------+-----------+---------+----------+
|
||||
* | 31 | 30-24 | 23-16 | 15-8 | 7-0 |
|
||||
* | | | | | |
|
||||
* | ARENA_ACC | Unused | ARENA_REG | DST_REG | INSN_LEN |
|
||||
* +-----------+--------+-----------+---------+----------+
|
||||
*
|
||||
* - INSN_LEN (8 bits): Length of faulting insn (max x86 insn = 15 bytes (fits in 8 bits)).
|
||||
* - DST_REG (8 bits): Offset of dst_reg from reg2pt_regs[] (max offset = 112 (fits in 8 bits)).
|
||||
* This is set to DONT_CLEAR if the insn is a store.
|
||||
* - ARENA_REG (8 bits): Offset of the register that is used to calculate the
|
||||
* address for load/store when accessing the arena region.
|
||||
* - ARENA_ACCESS (1 bit): This bit is set when the faulting instruction accessed the arena region.
|
||||
*
|
||||
* Bit layout of `data` (32-bit):
|
||||
*
|
||||
* +--------------+--------+--------------+
|
||||
* | 31-16 | 15-8 | 7-0 |
|
||||
* | | | |
|
||||
* | ARENA_OFFSET | Unused | EX_TYPE_BPF |
|
||||
* +--------------+--------+--------------+
|
||||
*
|
||||
* - ARENA_OFFSET (16 bits): Offset used to calculate the address for load/store when
|
||||
* accessing the arena region.
|
||||
*/
|
||||
|
||||
#define DONT_CLEAR 1
|
||||
#define FIXUP_INSN_LEN_MASK GENMASK(7, 0)
|
||||
#define FIXUP_REG_MASK GENMASK(15, 8)
|
||||
#define FIXUP_ARENA_REG_MASK GENMASK(23, 16)
|
||||
#define FIXUP_ARENA_ACCESS BIT(31)
|
||||
#define DATA_ARENA_OFFSET_MASK GENMASK(31, 16)
|
||||
|
||||
bool ex_handler_bpf(const struct exception_table_entry *x, struct pt_regs *regs)
|
||||
{
|
||||
u32 reg = x->fixup >> 8;
|
||||
u32 reg = FIELD_GET(FIXUP_REG_MASK, x->fixup);
|
||||
u32 insn_len = FIELD_GET(FIXUP_INSN_LEN_MASK, x->fixup);
|
||||
bool is_arena = !!(x->fixup & FIXUP_ARENA_ACCESS);
|
||||
bool is_write = (reg == DONT_CLEAR);
|
||||
unsigned long addr;
|
||||
s16 off;
|
||||
u32 arena_reg;
|
||||
|
||||
if (is_arena) {
|
||||
arena_reg = FIELD_GET(FIXUP_ARENA_REG_MASK, x->fixup);
|
||||
off = FIELD_GET(DATA_ARENA_OFFSET_MASK, x->data);
|
||||
addr = *(unsigned long *)((void *)regs + arena_reg) + off;
|
||||
bpf_prog_report_arena_violation(is_write, addr, regs->ip);
|
||||
}
|
||||
|
||||
/* jump over faulting load and clear dest register */
|
||||
if (reg != DONT_CLEAR)
|
||||
*(unsigned long *)((void *)regs + reg) = 0;
|
||||
regs->ip += x->fixup & 0xff;
|
||||
regs->ip += insn_len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2057,19 +2136,27 @@ st: if (is_imm8(insn->off))
|
||||
case BPF_LDX | BPF_PROBE_MEM32 | BPF_H:
|
||||
case BPF_LDX | BPF_PROBE_MEM32 | BPF_W:
|
||||
case BPF_LDX | BPF_PROBE_MEM32 | BPF_DW:
|
||||
case BPF_LDX | BPF_PROBE_MEM32SX | BPF_B:
|
||||
case BPF_LDX | BPF_PROBE_MEM32SX | BPF_H:
|
||||
case BPF_LDX | BPF_PROBE_MEM32SX | BPF_W:
|
||||
case BPF_STX | BPF_PROBE_MEM32 | BPF_B:
|
||||
case BPF_STX | BPF_PROBE_MEM32 | BPF_H:
|
||||
case BPF_STX | BPF_PROBE_MEM32 | BPF_W:
|
||||
case BPF_STX | BPF_PROBE_MEM32 | BPF_DW:
|
||||
start_of_ldx = prog;
|
||||
if (BPF_CLASS(insn->code) == BPF_LDX)
|
||||
emit_ldx_r12(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off);
|
||||
else
|
||||
if (BPF_CLASS(insn->code) == BPF_LDX) {
|
||||
if (BPF_MODE(insn->code) == BPF_PROBE_MEM32SX)
|
||||
emit_ldsx_r12(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off);
|
||||
else
|
||||
emit_ldx_r12(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off);
|
||||
} else {
|
||||
emit_stx_r12(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn->off);
|
||||
}
|
||||
populate_extable:
|
||||
{
|
||||
struct exception_table_entry *ex;
|
||||
u8 *_insn = image + proglen + (start_of_ldx - temp);
|
||||
u32 arena_reg, fixup_reg;
|
||||
s64 delta;
|
||||
|
||||
if (!bpf_prog->aux->extable)
|
||||
@@ -2089,8 +2176,29 @@ populate_extable:
|
||||
|
||||
ex->data = EX_TYPE_BPF;
|
||||
|
||||
ex->fixup = (prog - start_of_ldx) |
|
||||
((BPF_CLASS(insn->code) == BPF_LDX ? reg2pt_regs[dst_reg] : DONT_CLEAR) << 8);
|
||||
/*
|
||||
* src_reg/dst_reg holds the address in the arena region with upper
|
||||
* 32-bits being zero because of a preceding addr_space_cast(r<n>,
|
||||
* 0x0, 0x1) instruction. This address is adjusted with the addition
|
||||
* of arena_vm_start (see the implementation of BPF_PROBE_MEM32 and
|
||||
* BPF_PROBE_ATOMIC) before being used for the memory access. Pass
|
||||
* the reg holding the unmodified 32-bit address to
|
||||
* ex_handler_bpf().
|
||||
*/
|
||||
if (BPF_CLASS(insn->code) == BPF_LDX) {
|
||||
arena_reg = reg2pt_regs[src_reg];
|
||||
fixup_reg = reg2pt_regs[dst_reg];
|
||||
} else {
|
||||
arena_reg = reg2pt_regs[dst_reg];
|
||||
fixup_reg = DONT_CLEAR;
|
||||
}
|
||||
|
||||
ex->fixup = FIELD_PREP(FIXUP_INSN_LEN_MASK, prog - start_of_ldx) |
|
||||
FIELD_PREP(FIXUP_ARENA_REG_MASK, arena_reg) |
|
||||
FIELD_PREP(FIXUP_REG_MASK, fixup_reg);
|
||||
ex->fixup |= FIXUP_ARENA_ACCESS;
|
||||
|
||||
ex->data |= FIELD_PREP(DATA_ARENA_OFFSET_MASK, insn->off);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2208,7 +2316,8 @@ populate_extable:
|
||||
* End result: x86 insn "mov rbx, qword ptr [rax+0x14]"
|
||||
* of 4 bytes will be ignored and rbx will be zero inited.
|
||||
*/
|
||||
ex->fixup = (prog - start_of_ldx) | (reg2pt_regs[dst_reg] << 8);
|
||||
ex->fixup = FIELD_PREP(FIXUP_INSN_LEN_MASK, prog - start_of_ldx) |
|
||||
FIELD_PREP(FIXUP_REG_MASK, reg2pt_regs[dst_reg]);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -429,6 +429,7 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
|
||||
/* Authattr presence checked in parser */
|
||||
break;
|
||||
case VERIFYING_UNSPECIFIED_SIGNATURE:
|
||||
case VERIFYING_BPF_SIGNATURE:
|
||||
if (pkcs7->data_type != OID_data) {
|
||||
pr_warn("Invalid unspecified sig (not pkcs7-data)\n");
|
||||
return -EKEYREJECTED;
|
||||
|
||||
+69
-4
@@ -7,6 +7,7 @@
|
||||
#include <uapi/linux/bpf.h>
|
||||
#include <uapi/linux/filter.h>
|
||||
|
||||
#include <crypto/sha2.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/percpu.h>
|
||||
@@ -109,6 +110,7 @@ struct bpf_map_ops {
|
||||
long (*map_pop_elem)(struct bpf_map *map, void *value);
|
||||
long (*map_peek_elem)(struct bpf_map *map, void *value);
|
||||
void *(*map_lookup_percpu_elem)(struct bpf_map *map, void *key, u32 cpu);
|
||||
int (*map_get_hash)(struct bpf_map *map, u32 hash_buf_size, void *hash_buf);
|
||||
|
||||
/* funcs called by prog_array and perf_event_array map */
|
||||
void *(*map_fd_get_ptr)(struct bpf_map *map, struct file *map_file,
|
||||
@@ -206,6 +208,7 @@ enum btf_field_type {
|
||||
BPF_WORKQUEUE = (1 << 10),
|
||||
BPF_UPTR = (1 << 11),
|
||||
BPF_RES_SPIN_LOCK = (1 << 12),
|
||||
BPF_TASK_WORK = (1 << 13),
|
||||
};
|
||||
|
||||
enum bpf_cgroup_storage_type {
|
||||
@@ -259,6 +262,7 @@ struct btf_record {
|
||||
int timer_off;
|
||||
int wq_off;
|
||||
int refcount_off;
|
||||
int task_work_off;
|
||||
struct btf_field fields[];
|
||||
};
|
||||
|
||||
@@ -285,9 +289,11 @@ struct bpf_map_owner {
|
||||
bool xdp_has_frags;
|
||||
u64 storage_cookie[MAX_BPF_CGROUP_STORAGE_TYPE];
|
||||
const struct btf_type *attach_func_proto;
|
||||
enum bpf_attach_type expected_attach_type;
|
||||
};
|
||||
|
||||
struct bpf_map {
|
||||
u8 sha[SHA256_DIGEST_SIZE];
|
||||
const struct bpf_map_ops *ops;
|
||||
struct bpf_map *inner_map_meta;
|
||||
#ifdef CONFIG_SECURITY
|
||||
@@ -328,6 +334,7 @@ struct bpf_map {
|
||||
atomic64_t sleepable_refcnt;
|
||||
s64 __percpu *elem_count;
|
||||
u64 cookie; /* write-once */
|
||||
char *excl_prog_sha;
|
||||
};
|
||||
|
||||
static inline const char *btf_field_type_name(enum btf_field_type type)
|
||||
@@ -358,6 +365,8 @@ static inline const char *btf_field_type_name(enum btf_field_type type)
|
||||
return "bpf_rb_node";
|
||||
case BPF_REFCOUNT:
|
||||
return "bpf_refcount";
|
||||
case BPF_TASK_WORK:
|
||||
return "bpf_task_work";
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return "unknown";
|
||||
@@ -396,6 +405,8 @@ static inline u32 btf_field_type_size(enum btf_field_type type)
|
||||
return sizeof(struct bpf_rb_node);
|
||||
case BPF_REFCOUNT:
|
||||
return sizeof(struct bpf_refcount);
|
||||
case BPF_TASK_WORK:
|
||||
return sizeof(struct bpf_task_work);
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return 0;
|
||||
@@ -428,6 +439,8 @@ static inline u32 btf_field_type_align(enum btf_field_type type)
|
||||
return __alignof__(struct bpf_rb_node);
|
||||
case BPF_REFCOUNT:
|
||||
return __alignof__(struct bpf_refcount);
|
||||
case BPF_TASK_WORK:
|
||||
return __alignof__(struct bpf_task_work);
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return 0;
|
||||
@@ -459,6 +472,7 @@ static inline void bpf_obj_init_field(const struct btf_field *field, void *addr)
|
||||
case BPF_KPTR_REF:
|
||||
case BPF_KPTR_PERCPU:
|
||||
case BPF_UPTR:
|
||||
case BPF_TASK_WORK:
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
@@ -595,6 +609,7 @@ void copy_map_value_locked(struct bpf_map *map, void *dst, void *src,
|
||||
bool lock_src);
|
||||
void bpf_timer_cancel_and_free(void *timer);
|
||||
void bpf_wq_cancel_and_free(void *timer);
|
||||
void bpf_task_work_cancel_and_free(void *timer);
|
||||
void bpf_list_head_free(const struct btf_field *field, void *list_head,
|
||||
struct bpf_spin_lock *spin_lock);
|
||||
void bpf_rb_root_free(const struct btf_field *field, void *rb_root,
|
||||
@@ -767,12 +782,15 @@ enum bpf_type_flag {
|
||||
*/
|
||||
MEM_WRITE = BIT(18 + BPF_BASE_TYPE_BITS),
|
||||
|
||||
/* DYNPTR points to skb_metadata_end()-skb_metadata_len() */
|
||||
DYNPTR_TYPE_SKB_META = BIT(19 + BPF_BASE_TYPE_BITS),
|
||||
|
||||
__BPF_TYPE_FLAG_MAX,
|
||||
__BPF_TYPE_LAST_FLAG = __BPF_TYPE_FLAG_MAX - 1,
|
||||
};
|
||||
|
||||
#define DYNPTR_TYPE_FLAG_MASK (DYNPTR_TYPE_LOCAL | DYNPTR_TYPE_RINGBUF | DYNPTR_TYPE_SKB \
|
||||
| DYNPTR_TYPE_XDP)
|
||||
| DYNPTR_TYPE_XDP | DYNPTR_TYPE_SKB_META)
|
||||
|
||||
/* Max number of base types. */
|
||||
#define BPF_BASE_TYPE_LIMIT (1UL << BPF_BASE_TYPE_BITS)
|
||||
@@ -1110,7 +1128,7 @@ struct bpf_prog_offload {
|
||||
*/
|
||||
#define MAX_BPF_FUNC_REG_ARGS 5
|
||||
|
||||
/* The argument is a structure. */
|
||||
/* The argument is a structure or a union. */
|
||||
#define BTF_FMODEL_STRUCT_ARG BIT(0)
|
||||
|
||||
/* The argument is signed. */
|
||||
@@ -1358,6 +1376,8 @@ enum bpf_dynptr_type {
|
||||
BPF_DYNPTR_TYPE_SKB,
|
||||
/* Underlying data is a xdp_buff */
|
||||
BPF_DYNPTR_TYPE_XDP,
|
||||
/* Points to skb_metadata_end()-skb_metadata_len() */
|
||||
BPF_DYNPTR_TYPE_SKB_META,
|
||||
};
|
||||
|
||||
int bpf_dynptr_check_size(u32 size);
|
||||
@@ -1619,6 +1639,7 @@ struct bpf_prog_aux {
|
||||
bool priv_stack_requested;
|
||||
bool changes_pkt_data;
|
||||
bool might_sleep;
|
||||
bool kprobe_write_ctx;
|
||||
u64 prog_array_member_cnt; /* counts how many times as member of prog_array */
|
||||
struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
|
||||
struct bpf_arena *arena;
|
||||
@@ -1628,6 +1649,7 @@ struct bpf_prog_aux {
|
||||
/* function name for valid attach_btf_id */
|
||||
const char *attach_func_name;
|
||||
struct bpf_prog **func;
|
||||
struct bpf_prog_aux *main_prog_aux;
|
||||
void *jit_data; /* JIT specific data. arch dependent */
|
||||
struct bpf_jit_poke_descriptor *poke_tab;
|
||||
struct bpf_kfunc_desc_tab *kfunc_tab;
|
||||
@@ -1711,7 +1733,10 @@ struct bpf_prog {
|
||||
enum bpf_attach_type expected_attach_type; /* For some prog types */
|
||||
u32 len; /* Number of filter blocks */
|
||||
u32 jited_len; /* Size of jited insns in bytes */
|
||||
u8 tag[BPF_TAG_SIZE];
|
||||
union {
|
||||
u8 digest[SHA256_DIGEST_SIZE];
|
||||
u8 tag[BPF_TAG_SIZE];
|
||||
};
|
||||
struct bpf_prog_stats __percpu *stats;
|
||||
int __percpu *active;
|
||||
unsigned int (*bpf_func)(const void *ctx,
|
||||
@@ -1985,6 +2010,7 @@ static inline void bpf_module_put(const void *data, struct module *owner)
|
||||
module_put(owner);
|
||||
}
|
||||
int bpf_struct_ops_link_create(union bpf_attr *attr);
|
||||
u32 bpf_struct_ops_id(const void *kdata);
|
||||
|
||||
#ifdef CONFIG_NET
|
||||
/* Define it here to avoid the use of forward declaration */
|
||||
@@ -2411,6 +2437,7 @@ struct btf_record *btf_record_dup(const struct btf_record *rec);
|
||||
bool btf_record_equal(const struct btf_record *rec_a, const struct btf_record *rec_b);
|
||||
void bpf_obj_free_timer(const struct btf_record *rec, void *obj);
|
||||
void bpf_obj_free_workqueue(const struct btf_record *rec, void *obj);
|
||||
void bpf_obj_free_task_work(const struct btf_record *rec, void *obj);
|
||||
void bpf_obj_free_fields(const struct btf_record *rec, void *obj);
|
||||
void __bpf_obj_drop_impl(void *p, const struct btf_record *rec, bool percpu);
|
||||
|
||||
@@ -2697,7 +2724,7 @@ int bpf_percpu_hash_update(struct bpf_map *map, void *key, void *value,
|
||||
int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
|
||||
u64 flags);
|
||||
|
||||
int bpf_stackmap_copy(struct bpf_map *map, void *key, void *value);
|
||||
int bpf_stackmap_extract(struct bpf_map *map, void *key, void *value, bool delete);
|
||||
|
||||
int bpf_fd_array_map_update_elem(struct bpf_map *map, struct file *map_file,
|
||||
void *key, void *value, u64 map_flags);
|
||||
@@ -2874,6 +2901,7 @@ void bpf_dynptr_init(struct bpf_dynptr_kern *ptr, void *data,
|
||||
enum bpf_dynptr_type type, u32 offset, u32 size);
|
||||
void bpf_dynptr_set_null(struct bpf_dynptr_kern *ptr);
|
||||
void bpf_dynptr_set_rdonly(struct bpf_dynptr_kern *ptr);
|
||||
void bpf_prog_report_arena_violation(bool write, unsigned long addr, unsigned long fault_ip);
|
||||
|
||||
#else /* !CONFIG_BPF_SYSCALL */
|
||||
static inline struct bpf_prog *bpf_prog_get(u32 ufd)
|
||||
@@ -3161,6 +3189,11 @@ static inline void bpf_dynptr_set_null(struct bpf_dynptr_kern *ptr)
|
||||
static inline void bpf_dynptr_set_rdonly(struct bpf_dynptr_kern *ptr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void bpf_prog_report_arena_violation(bool write, unsigned long addr,
|
||||
unsigned long fault_ip)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_BPF_SYSCALL */
|
||||
|
||||
static __always_inline int
|
||||
@@ -3403,6 +3436,38 @@ static inline int bpf_fd_reuseport_array_update_elem(struct bpf_map *map,
|
||||
#endif /* CONFIG_BPF_SYSCALL */
|
||||
#endif /* defined(CONFIG_INET) && defined(CONFIG_BPF_SYSCALL) */
|
||||
|
||||
#if defined(CONFIG_KEYS) && defined(CONFIG_BPF_SYSCALL)
|
||||
|
||||
struct bpf_key *bpf_lookup_user_key(s32 serial, u64 flags);
|
||||
struct bpf_key *bpf_lookup_system_key(u64 id);
|
||||
void bpf_key_put(struct bpf_key *bkey);
|
||||
int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p,
|
||||
struct bpf_dynptr *sig_p,
|
||||
struct bpf_key *trusted_keyring);
|
||||
|
||||
#else
|
||||
static inline struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct bpf_key *bpf_lookup_system_key(u64 id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void bpf_key_put(struct bpf_key *bkey)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p,
|
||||
struct bpf_dynptr *sig_p,
|
||||
struct bpf_key *trusted_keyring)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif /* defined(CONFIG_KEYS) && defined(CONFIG_BPF_SYSCALL) */
|
||||
|
||||
/* verifier prototypes for helper functions called from eBPF programs */
|
||||
extern const struct bpf_func_proto bpf_map_lookup_elem_proto;
|
||||
extern const struct bpf_func_proto bpf_map_update_elem_proto;
|
||||
|
||||
@@ -26,28 +26,6 @@
|
||||
/* Patch buffer size */
|
||||
#define INSN_BUF_SIZE 32
|
||||
|
||||
/* Liveness marks, used for registers and spilled-regs (in stack slots).
|
||||
* Read marks propagate upwards until they find a write mark; they record that
|
||||
* "one of this state's descendants read this reg" (and therefore the reg is
|
||||
* relevant for states_equal() checks).
|
||||
* Write marks collect downwards and do not propagate; they record that "the
|
||||
* straight-line code that reached this state (from its parent) wrote this reg"
|
||||
* (and therefore that reads propagated from this state or its descendants
|
||||
* should not propagate to its parent).
|
||||
* A state with a write mark can receive read marks; it just won't propagate
|
||||
* them to its parent, since the write mark is a property, not of the state,
|
||||
* but of the link between it and its parent. See mark_reg_read() and
|
||||
* mark_stack_slot_read() in kernel/bpf/verifier.c.
|
||||
*/
|
||||
enum bpf_reg_liveness {
|
||||
REG_LIVE_NONE = 0, /* reg hasn't been read or written this branch */
|
||||
REG_LIVE_READ32 = 0x1, /* reg was read, so we're sensitive to initial value */
|
||||
REG_LIVE_READ64 = 0x2, /* likewise, but full 64-bit content matters */
|
||||
REG_LIVE_READ = REG_LIVE_READ32 | REG_LIVE_READ64,
|
||||
REG_LIVE_WRITTEN = 0x4, /* reg was written first, screening off later reads */
|
||||
REG_LIVE_DONE = 0x8, /* liveness won't be updating this register anymore */
|
||||
};
|
||||
|
||||
#define ITER_PREFIX "bpf_iter_"
|
||||
|
||||
enum bpf_iter_state {
|
||||
@@ -212,8 +190,6 @@ struct bpf_reg_state {
|
||||
* allowed and has the same effect as bpf_sk_release(sk).
|
||||
*/
|
||||
u32 ref_obj_id;
|
||||
/* parentage chain for liveness checking */
|
||||
struct bpf_reg_state *parent;
|
||||
/* Inside the callee two registers can be both PTR_TO_STACK like
|
||||
* R1=fp-8 and R2=fp-8, but one of them points to this function stack
|
||||
* while another to the caller's stack. To differentiate them 'frameno'
|
||||
@@ -226,7 +202,6 @@ struct bpf_reg_state {
|
||||
* patching which only happens after main verification finished.
|
||||
*/
|
||||
s32 subreg_def;
|
||||
enum bpf_reg_liveness live;
|
||||
/* if (!precise && SCALAR_VALUE) min/max/tnum don't affect safety */
|
||||
bool precise;
|
||||
};
|
||||
@@ -445,6 +420,7 @@ struct bpf_verifier_state {
|
||||
|
||||
bool speculative;
|
||||
bool in_sleepable;
|
||||
bool cleaned;
|
||||
|
||||
/* first and last insn idx of this verifier state */
|
||||
u32 first_insn_idx;
|
||||
@@ -665,6 +641,7 @@ struct bpf_subprog_info {
|
||||
/* 'start' has to be the first field otherwise find_subprog() won't work */
|
||||
u32 start; /* insn idx of function entry point */
|
||||
u32 linfo_idx; /* The idx to the main_prog->aux->linfo */
|
||||
u32 postorder_start; /* The idx to the env->cfg.insn_postorder */
|
||||
u16 stack_depth; /* max. stack depth used by this function */
|
||||
u16 stack_extra;
|
||||
/* offsets in range [stack_depth .. fastcall_stack_off)
|
||||
@@ -744,6 +721,8 @@ struct bpf_scc_info {
|
||||
struct bpf_scc_visit visits[];
|
||||
};
|
||||
|
||||
struct bpf_liveness;
|
||||
|
||||
/* single container for all structs
|
||||
* one verifier_env per bpf_check() call
|
||||
*/
|
||||
@@ -794,7 +773,10 @@ struct bpf_verifier_env {
|
||||
struct {
|
||||
int *insn_state;
|
||||
int *insn_stack;
|
||||
/* vector of instruction indexes sorted in post-order */
|
||||
/*
|
||||
* vector of instruction indexes sorted in post-order, grouped by subprogram,
|
||||
* see bpf_subprog_info->postorder_start.
|
||||
*/
|
||||
int *insn_postorder;
|
||||
int cur_stack;
|
||||
/* current position in the insn_postorder vector */
|
||||
@@ -842,6 +824,7 @@ struct bpf_verifier_env {
|
||||
struct bpf_insn insn_buf[INSN_BUF_SIZE];
|
||||
struct bpf_insn epilogue_buf[INSN_BUF_SIZE];
|
||||
struct bpf_scc_callchain callchain_buf;
|
||||
struct bpf_liveness *liveness;
|
||||
/* array of pointers to bpf_scc_info indexed by SCC id */
|
||||
struct bpf_scc_info **scc_info;
|
||||
u32 scc_cnt;
|
||||
@@ -875,13 +858,15 @@ __printf(3, 4) void verbose_linfo(struct bpf_verifier_env *env,
|
||||
#define verifier_bug_if(cond, env, fmt, args...) \
|
||||
({ \
|
||||
bool __cond = (cond); \
|
||||
if (unlikely(__cond)) { \
|
||||
BPF_WARN_ONCE(1, "verifier bug: " fmt "(" #cond ")\n", ##args); \
|
||||
bpf_log(&env->log, "verifier bug: " fmt "(" #cond ")\n", ##args); \
|
||||
} \
|
||||
if (unlikely(__cond)) \
|
||||
verifier_bug(env, fmt " (" #cond ")", ##args); \
|
||||
(__cond); \
|
||||
})
|
||||
#define verifier_bug(env, fmt, args...) verifier_bug_if(1, env, fmt, ##args)
|
||||
#define verifier_bug(env, fmt, args...) \
|
||||
({ \
|
||||
BPF_WARN_ONCE(1, "verifier bug: " fmt "\n", ##args); \
|
||||
bpf_log(&env->log, "verifier bug: " fmt "\n", ##args); \
|
||||
})
|
||||
|
||||
static inline struct bpf_func_state *cur_func(struct bpf_verifier_env *env)
|
||||
{
|
||||
@@ -962,6 +947,7 @@ static inline bool bpf_prog_check_recur(const struct bpf_prog *prog)
|
||||
case BPF_PROG_TYPE_STRUCT_OPS:
|
||||
return prog->aux->jits_use_priv_stack;
|
||||
case BPF_PROG_TYPE_LSM:
|
||||
case BPF_PROG_TYPE_SYSCALL:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
@@ -1062,4 +1048,21 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_verifie
|
||||
void print_insn_state(struct bpf_verifier_env *env, const struct bpf_verifier_state *vstate,
|
||||
u32 frameno);
|
||||
|
||||
struct bpf_subprog_info *bpf_find_containing_subprog(struct bpf_verifier_env *env, int off);
|
||||
int bpf_jmp_offset(struct bpf_insn *insn);
|
||||
int bpf_insn_successors(struct bpf_prog *prog, u32 idx, u32 succ[2]);
|
||||
void bpf_fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask);
|
||||
bool bpf_calls_callback(struct bpf_verifier_env *env, int insn_idx);
|
||||
|
||||
int bpf_stack_liveness_init(struct bpf_verifier_env *env);
|
||||
void bpf_stack_liveness_free(struct bpf_verifier_env *env);
|
||||
int bpf_update_live_stack(struct bpf_verifier_env *env);
|
||||
int bpf_mark_stack_read(struct bpf_verifier_env *env, u32 frameno, u32 insn_idx, u64 mask);
|
||||
void bpf_mark_stack_write(struct bpf_verifier_env *env, u32 frameno, u64 mask);
|
||||
int bpf_reset_stack_write_marks(struct bpf_verifier_env *env, u32 insn_idx);
|
||||
int bpf_commit_stack_write_marks(struct bpf_verifier_env *env);
|
||||
int bpf_live_stack_query_init(struct bpf_verifier_env *env, struct bpf_verifier_state *st);
|
||||
bool bpf_stack_slot_alive(struct bpf_verifier_env *env, u32 frameno, u32 spi);
|
||||
void bpf_reset_live_stack_callchain(struct bpf_verifier_env *env);
|
||||
|
||||
#endif /* _LINUX_BPF_VERIFIER_H */
|
||||
+1
-1
@@ -86,7 +86,7 @@
|
||||
* as to avoid issues such as the compiler inlining or eliding either a static
|
||||
* kfunc, or a global kfunc in an LTO build.
|
||||
*/
|
||||
#define __bpf_kfunc __used __retain noinline
|
||||
#define __bpf_kfunc __used __retain __noclone noinline
|
||||
|
||||
#define __bpf_kfunc_start_defs() \
|
||||
__diag_push(); \
|
||||
|
||||
Loaded 20 of 254 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user