mbox series

[bpf-next,v3,00/17] Exceptions - 1/2

Message ID 20230912233214.1518551-1-memxor@gmail.com (mailing list archive)
Headers show
Series Exceptions - 1/2 | expand

Message

Kumar Kartikeya Dwivedi Sept. 12, 2023, 11:31 p.m. UTC
This series implements the _first_ part of the runtime and verifier
support needed to enable BPF exceptions. Exceptions thrown from programs
are processed as an immediate exit from the program, which unwinds all
the active stack frames until the main stack frame, and returns to the
BPF program's caller. The ability to perform this unwinding safely
allows the program to test conditions that are always true at runtime
but which the verifier has no visibility into.

Thus, it also reduces verification effort by safely terminating
redundant paths that can be taken within a program.

The patches to perform runtime resource cleanup during the
frame-by-frame unwinding will be posted as a follow-up to this set.

It must be noted that exceptions are not an error handling mechanism for
unlikely runtime conditions, but a way to safely terminate the execution
of a program in presence of conditions that should never occur at
runtime. They are meant to serve higher-level primitives such as program
assertions.

The following kfuncs and macros are introduced:

Assertion macros are also introduced, please see patch 13 for their
documentation.

/* Description
 *	Throw a BPF exception from the program, immediately terminating its
 *	execution and unwinding the stack. The supplied 'cookie' parameter
 *	will be the return value of the program when an exception is thrown,
 *	and the default exception callback is used. Otherwise, if an exception
 *	callback is set using the '__exception_cb(callback)' declaration tag
 *	on the main program, the 'cookie' parameter will be the callback's only
 *	input argument.
 *
 *	Thus, in case of default exception callback, 'cookie' is subjected to
 *	constraints on the program's return value (as with R0 on exit).
 *	Otherwise, the return value of the marked exception callback will be
 *	subjected to the same checks.
 *
 *	Note that throwing an exception with lingering resources (locks,
 *	references, etc.) will lead to a verification error.
 *
 *	Note that callbacks *cannot* call this helper.
 * Returns
 *	Never.
 * Throws
 *	An exception with the specified 'cookie' value.
 */
extern void bpf_throw(u64 cookie) __ksym;

/* This macro must be used to mark the exception callback corresponding to the
 * main program. For example:
 *
 * int exception_cb(u64 cookie) {
 *	return cookie;
 * }
 *
 * SEC("tc")
 * __exception_cb(exception_cb)
 * int main_prog(struct __sk_buff *ctx) {
 *	...
 *	return TC_ACT_OK;
 * }
 *
 * Here, exception callback for the main program will be 'exception_cb'. Note
 * that this attribute can only be used once, and multiple exception callbacks
 * specified for the main program will lead to verification error.
 */
\#define __exception_cb(name) __attribute__((btf_decl_tag("exception_callback:" #name)))

As such, a program can only install an exception handler once for the
lifetime of a BPF program, and this handler cannot be changed at
runtime. The purpose of the handler is to simply interpret the cookie
value supplied by the bpf_throw call, and execute user-defined logic
corresponding to it. The primary purpose of allowing a handler is to
control the return value of the program. The default handler returns the
cookie value passed to bpf_throw when an exception is thrown.

Fixing the handler for the lifetime of the program eliminates tricky and
expensive handling in case of runtime changes of the handler callback
when programs begin to nest, where it becomes more complex to save and
restore the active handler at runtime.

This version of offline unwinding based BPF exceptions is truly zero
overhead, with the exception of generation of a default callback which
contains a few instructions to return a default return value (0) when no
exception callback is supplied by the user.

Callbacks are disallowed from throwing BPF exceptions for now, since
such exceptions need to cross the callback helper boundary (and
therefore must care about unwinding kernel state), however it is
possible to lift this restriction in the future follow-up.

Exceptions terminate propogating at program boundaries, hence both
BPF_PROG_TYPE_EXT and tail call targets return to their caller context
the return value of the exception callback, in the event that they throw
an exception. Thus, exceptions do not cross extension or tail call
boundary.

However, this is mostly an implementation choice, and can be changed to
suit more user-friendly semantics.

Changelog:
----------
v2 -> v3
v2: https://lore.kernel.org/bpf/20230809114116.3216687-1-memxor@gmail.com

 * Add Dave's Acked-by.
 * Address all comments from Alexei.
   * Use bpf_is_subprog to check for main prog in bpf_stack_walker.
   * Drop accidental leftover hunk in libbpf patch.
   * Split libbpf patch's refactoring to aid review
   * Disable fentry/fexit in addition to freplace for exception cb.
   * Add selftests for fentry/fexit/freplace on exception cb and main prog.
 * Use btf_find_by_name_kind in bpf_find_exception_callback_insn_off (Martin)
 * Split KASAN patch into two to aid backporting (Andrey)
 * Move exception callback append step to bpf_object__reloacte (Andrii)
 * Ensure that the exception callback name is unique (Andrii)
 * Keep ASM implementation of assertion macros instead of C, as it does
   not achieve intended results for bpf_assert_range and other cases.

v1 -> v2
v1: https://lore.kernel.org/bpf/20230713023232.1411523-1-memxor@gmail.com

 * Address all comments from Alexei.
 * Fix a few bugs and corner cases in the implementations found during
   testing. Also add new selftests for these cases.
 * Reinstate patch to consider ksym.end part of the program (but
   reworked to cover other corner cases).
 * Implement new style of tagging exception callbacks, add libbpf
   support for the new declaration tag.
 * Limit support to 64-bit integer types for assertion macros. The
   compiler ends up performing shifts or bitwise and operations when
   finally making use of the value, which defeats the purpose of the
   macro. On noalu32 mode, the shifts may also happen before use,
   hurting reliability.
 * Comprehensively test assertion macros and their side effects on the
   verifier state, register bounds, etc.
 * Fix a KASAN false positive warning.

RFC v1 -> v1
RFC v1: https://lore.kernel.org/bpf/20230405004239.1375399-1-memxor@gmail.com

 * Completely rework the unwinding infrastructure to use offline
   unwinding support.
 * Remove the runtime exception state and program rewriting code.
 * Make bpf_set_exception_callback idempotent to avoid vexing
   synchronization and state clobbering issues in presence of program
   nesting.
 * Disable bpf_throw within callback functions, for now.
 * Allow bpf_throw in tail call programs and extension programs,
   removing limitations of rewrite based unwinding.
 * Expand selftests.

Kumar Kartikeya Dwivedi (17):
  bpf: Use bpf_is_subprog to check for subprogs
  arch/x86: Implement arch_bpf_stack_walk
  bpf: Implement support for adding hidden subprogs
  bpf: Implement BPF exceptions
  bpf: Refactor check_btf_func and split into two phases
  bpf: Add support for custom exception callbacks
  bpf: Perform CFG walk for exception callback
  bpf: Treat first argument as return value for bpf_throw
  mm: kasan: Declare kasan_unpoison_task_stack_below in kasan.h
  bpf: Prevent KASAN false positive with bpf_throw
  bpf: Detect IP == ksym.end as part of BPF program
  bpf: Disallow fentry/fexit/freplace for exception callbacks
  bpf: Fix kfunc callback register type handling
  libbpf: Refactor bpf_object__reloc_code
  libbpf: Add support for custom exception callbacks
  selftests/bpf: Add BPF assertion macros
  selftests/bpf: Add tests for BPF exceptions

 arch/arm64/net/bpf_jit_comp.c                 |   2 +-
 arch/s390/net/bpf_jit_comp.c                  |   2 +-
 arch/x86/net/bpf_jit_comp.c                   | 117 ++++-
 include/linux/bpf.h                           |  13 +-
 include/linux/bpf_verifier.h                  |   8 +-
 include/linux/filter.h                        |   8 +
 include/linux/kasan.h                         |   2 +
 kernel/bpf/btf.c                              |  29 +-
 kernel/bpf/core.c                             |  29 +-
 kernel/bpf/helpers.c                          |  45 ++
 kernel/bpf/syscall.c                          |   2 +-
 kernel/bpf/verifier.c                         | 456 +++++++++++++++---
 mm/kasan/kasan.h                              |   1 -
 tools/lib/bpf/libbpf.c                        | 166 ++++++-
 tools/testing/selftests/bpf/DENYLIST.aarch64  |   1 +
 tools/testing/selftests/bpf/DENYLIST.s390x    |   1 +
 .../testing/selftests/bpf/bpf_experimental.h  | 288 +++++++++++
 .../selftests/bpf/prog_tests/exceptions.c     | 408 ++++++++++++++++
 .../testing/selftests/bpf/progs/exceptions.c  | 368 ++++++++++++++
 .../selftests/bpf/progs/exceptions_assert.c   | 135 ++++++
 .../selftests/bpf/progs/exceptions_ext.c      |  72 +++
 .../selftests/bpf/progs/exceptions_fail.c     | 347 +++++++++++++
 22 files changed, 2376 insertions(+), 124 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/exceptions.c
 create mode 100644 tools/testing/selftests/bpf/progs/exceptions.c
 create mode 100644 tools/testing/selftests/bpf/progs/exceptions_assert.c
 create mode 100644 tools/testing/selftests/bpf/progs/exceptions_ext.c
 create mode 100644 tools/testing/selftests/bpf/progs/exceptions_fail.c


base-commit: 5bbb9e1f08352a381a6e8a17b5180170b2a93685

Comments

Kumar Kartikeya Dwivedi Sept. 12, 2023, 11:37 p.m. UTC | #1
On Wed, 13 Sept 2023 at 01:32, Kumar Kartikeya Dwivedi <memxor@gmail.com> wrote:
>
> This series implements the _first_ part of the runtime and verifier
> support needed to enable BPF exceptions. Exceptions thrown from programs
> are processed as an immediate exit from the program, which unwinds all
> the active stack frames until the main stack frame, and returns to the
> BPF program's caller. The ability to perform this unwinding safely
> allows the program to test conditions that are always true at runtime
> but which the verifier has no visibility into.
>
> [...]

Hi everyone, just to make sure this is not missed, I had to revert
back to the ASM approach as I found the C approach to be too fragile
during testing. David asked whether we could move this facility into
libbpf as well, but I think with the current state it is probably
better to let things sit in bpf_experimental.h for a while.

The problem with BTF info generation for such ksyms still remains, I
tried and kept the (void)bpf_throw suggestion from Andrii but I'm not
sure it helps in every case, atleast with clang 17 I can reduce the
program to a simple case where it doesn't produce the BTF info needed
for successful loading. For now, another no-overhead solution can be
having a bpf_throw call that never executes in a static function that
is never used. The libbpf loading process will never append this to
the main program's instruction sequence (since unreferenced), it will
just be there in the object.
Puranjay Mohan Sept. 12, 2023, 11:53 p.m. UTC | #2
Hi,

On Wed, Sep 13, 2023 at 1:32 AM Kumar Kartikeya Dwivedi
<memxor@gmail.com> wrote:
>
> This series implements the _first_ part of the runtime and verifier
> support needed to enable BPF exceptions. Exceptions thrown from programs
> are processed as an immediate exit from the program, which unwinds all
> the active stack frames until the main stack frame, and returns to the
> BPF program's caller. The ability to perform this unwinding safely
> allows the program to test conditions that are always true at runtime
> but which the verifier has no visibility into.

I am working with Kartikeya on enabling this on ARM64.

Here is the patch:
https://lore.kernel.org/bpf/20230912233942.6734-1-puranjay12@gmail.com/

Thanks,
Puranjay
patchwork-bot+netdevbpf@kernel.org Sept. 16, 2023, 4:50 p.m. UTC | #3
Hello:

This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Wed, 13 Sep 2023 01:31:57 +0200 you wrote:
> This series implements the _first_ part of the runtime and verifier
> support needed to enable BPF exceptions. Exceptions thrown from programs
> are processed as an immediate exit from the program, which unwinds all
> the active stack frames until the main stack frame, and returns to the
> BPF program's caller. The ability to perform this unwinding safely
> allows the program to test conditions that are always true at runtime
> but which the verifier has no visibility into.
> 
> [...]

Here is the summary with links:
  - [bpf-next,v3,01/17] bpf: Use bpf_is_subprog to check for subprogs
    https://git.kernel.org/bpf/bpf-next/c/9af27da6313c
  - [bpf-next,v3,02/17] arch/x86: Implement arch_bpf_stack_walk
    https://git.kernel.org/bpf/bpf-next/c/fd5d27b70188
  - [bpf-next,v3,03/17] bpf: Implement support for adding hidden subprogs
    https://git.kernel.org/bpf/bpf-next/c/335d1c5b5452
  - [bpf-next,v3,04/17] bpf: Implement BPF exceptions
    https://git.kernel.org/bpf/bpf-next/c/f18b03fabaa9
  - [bpf-next,v3,05/17] bpf: Refactor check_btf_func and split into two phases
    https://git.kernel.org/bpf/bpf-next/c/aaa619ebccb2
  - [bpf-next,v3,06/17] bpf: Add support for custom exception callbacks
    https://git.kernel.org/bpf/bpf-next/c/b9ae0c9dd0ac
  - [bpf-next,v3,07/17] bpf: Perform CFG walk for exception callback
    https://git.kernel.org/bpf/bpf-next/c/b62bf8a5e911
  - [bpf-next,v3,08/17] bpf: Treat first argument as return value for bpf_throw
    https://git.kernel.org/bpf/bpf-next/c/a923819fb2c5
  - [bpf-next,v3,09/17] mm: kasan: Declare kasan_unpoison_task_stack_below in kasan.h
    https://git.kernel.org/bpf/bpf-next/c/7ccb84f04cda
  - [bpf-next,v3,10/17] bpf: Prevent KASAN false positive with bpf_throw
    https://git.kernel.org/bpf/bpf-next/c/ec5290a178b7
  - [bpf-next,v3,11/17] bpf: Detect IP == ksym.end as part of BPF program
    https://git.kernel.org/bpf/bpf-next/c/66d9111f3517
  - [bpf-next,v3,12/17] bpf: Disallow fentry/fexit/freplace for exception callbacks
    https://git.kernel.org/bpf/bpf-next/c/fd548e1a4618
  - [bpf-next,v3,13/17] bpf: Fix kfunc callback register type handling
    https://git.kernel.org/bpf/bpf-next/c/06d686f771dd
  - [bpf-next,v3,14/17] libbpf: Refactor bpf_object__reloc_code
    https://git.kernel.org/bpf/bpf-next/c/6c918709bd30
  - [bpf-next,v3,15/17] libbpf: Add support for custom exception callbacks
    https://git.kernel.org/bpf/bpf-next/c/7e2925f67237
  - [bpf-next,v3,16/17] selftests/bpf: Add BPF assertion macros
    https://git.kernel.org/bpf/bpf-next/c/d6ea06803212
  - [bpf-next,v3,17/17] selftests/bpf: Add tests for BPF exceptions
    https://git.kernel.org/bpf/bpf-next/c/d2a93715bfb0

You are awesome, thank you!