All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Kumar Kartikeya Dwivedi" <memxor@gmail.com>
To: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>,
	<bpf@vger.kernel.org>
Cc: <daniel@iogearbox.net>, <andrii@kernel.org>, <eddyz87@gmail.com>
Subject: Re: [PATCH bpf-next 0/5] bpf: Unwinding via r3
Date: Sun, 18 Oct 2026 03:31:49 +0200	[thread overview]
Message-ID: <DM7KR7J8VY2T.1CZIRUF6QRCWG@gmail.com> (raw)
In-Reply-To: <20261018010246.1643877-1-alexei.starovoitov@gmail.com>

On Sun Oct 18, 2026 at 3:02 AM CEST, Alexei Starovoitov wrote:
> From: Alexei Starovoitov <ast@anthropic.com>
>
> This is the r3 alternative from the discussion of v12 of Yonghong's series
> [1] as patches, with x86-64 and arm64 JITs and selftests.
>
> 1. What is the problem?
>
> A rust panic or a C++ exception has to run Drop impls or destructors of
> every frame it passes through. bpf_throw() discards the frames, so the
> verifier doesn't let a frame that owns a lock or a reference throw. In rust
> every index and every unwrap() can panic.
>
> 2. Is it worth solving?
>
> Without it rust-bpf [2] drops all landing pads and a panic is bpf_throw().
> A prog that can panic with a guard held doesn't load.
>
> 3. Is there a simpler way?
>
> [1] keeps the normal path free of unwind code. llvm emits a table, libbpf
> passes it to the kernel, bpf_unwind() walks the stack and rewrites return
> addresses, and the verifier models hidden edges from calls to pads. That's
> 1400 lines in the kernel, with new uapi and two kfuncs, and 440 lines in
> libbpf.
>
> Here llvm emits plain insns [3]:
>
>   ret                            r3 = 0; exit
>   invoke foo unwind pad          call foo; if r3 != 0 goto pad
>   resume                         r0 = 0; r3 = 1; exit
>
> Pads are ordinary code, so the verifier part is 96 lines in patch 1.
> The price is a branch after a call that may unwind. Patches 2 and 3 take it
> off the normal path in the JITs without telling the verifier. 360 lines
> with them.
>
> 4. Will the api and the layers last?
>
> No uapi, no kfuncs, no changes in libbpf and other loaders.
> r3 and BTF decl_tag "unwind" become an ABI between llvm and the kernel.
> The source level entry point is _Unwind_RaiseException() of the Itanium
> ABI. It's what the runtimes of rust and C++ call on other architectures.
>
> 5. Is it good taste?
>
> Every frame returns through its own epilogue. The only question is where it
> returns to. That's one bit.
>
> It's not specific to rust. A C++ prog with throw, catch and destructors
> compiles, loads and runs. It's a selftest in patch 5.
>
> Since the sketches in [1]:
> - bpf_unwind() is gone. _Unwind_RaiseException() starts an unwind.
> - llvm applies the convention to functions with uwtable only, so C progs
>   are not affected.
> - JITs opt in. A JIT that loses r3 would skip a pad.
> - A subprog with the tag has to exit with a scalar in r3.
> - A callback that doesn't set r3 was hitting verifier_bug().
> - The extable lookup is search_bpf_extables().
> - 'Functions end with exit' was not quite right. A function can still end
>   with a call of a noreturn function that cannot unwind, like bpf_throw().
>   rust-bpf adds 'exit' after it.
>
> Tested in qemu only, x86-64 and arm64 with and without pointer
> authentication: the selftests, C, C++ and rust progs built with [3],
> with and without bpf_jit_harden=2. test_progs was run on x86-64. On arm64
> the same objects were loaded and run by a small loader, and __jited
> patterns were matched against the JIT output by a script.
>
> [1] https://lore.kernel.org/bpf/20261017025129.1914859-1-yonghong.song@linux.dev/
> [2] https://github.com/4ast/rust-bpf
> [3] https://github.com/llvm/llvm-project/pull/233140
>
> Alexei Starovoitov (5):
>   bpf: Support unwinding via r3
>   bpf, x86: Replace 'if r3 != 0 goto pad' after a call with an extable
>     entry
>   bpf, arm64: Support unwinding via r3
>   selftests/bpf: Add tests for unwinding via r3
>   selftests/bpf: Add a C++ prog with exceptions
>
>  arch/arm64/net/bpf_jit.h                      |   2 +
>  arch/arm64/net/bpf_jit_comp.c                 | 110 +++
>  arch/x86/net/bpf_jit_comp.c                   | 107 ++-
>  include/linux/bpf.h                           |   1 +
>  include/linux/bpf_verifier.h                  |   5 +-
>  include/linux/filter.h                        |  11 +
>  kernel/bpf/backtrack.c                        |  10 +-
>  kernel/bpf/core.c                             |  53 ++
>  kernel/bpf/fixups.c                           |   1 +
>  kernel/bpf/liveness.c                         |   7 +-
>  kernel/bpf/verifier.c                         |  66 +-
>  tools/testing/selftests/bpf/Makefile.skel     |  11 +
>  .../selftests/bpf/prog_tests/unwind_attach.c  |  67 ++
>  .../selftests/bpf/prog_tests/unwind_cxx.c     |  76 ++
>  .../selftests/bpf/prog_tests/verifier.c       |   2 +
>  .../selftests/bpf/progs/unwind_attach.c       |  32 +
>  .../selftests/bpf/progs/unwind_cxx.cpp        | 117 +++
>  .../selftests/bpf/progs/verifier_unwind.c     | 759 ++++++++++++++++++
>  18 files changed, 1427 insertions(+), 10 deletions(-)
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/unwind_attach.c
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/unwind_cxx.c
>  create mode 100644 tools/testing/selftests/bpf/progs/unwind_attach.c
>  create mode 100644 tools/testing/selftests/bpf/progs/unwind_cxx.cpp
>  create mode 100644 tools/testing/selftests/bpf/progs/verifier_unwind.c
>
>
> base-commit: c058636f788f8751e419b407206516280226e568

Overall this makes sense, I guess my one small nit is that since we've
crossed the bridge on changing the compiler, we could just use a
cleaner encoding of some new instruction, but that is a matter of
preference, it would be slightly more explicit and cleaner. Claude
suggests "BPF_JMP | BPF_JCOND with a new value in imm" with the
following benefits:
 - The verifier treats it exactly as JNE r3, 0, so no new verifier
   semantics.
 - The JIT elides it by opcode, with no lookahead and no shape test.
 - Blinding never touches it, since JCOND carries no blindable
   constant, so the blinding fallback disappears.
 - The kernel can require that it directly follow a call and not be a
   jump target, and reject otherwise, which makes elision
   unconditional rather than best-effort.
 - Dumps show the intent, which also matters when someone reads an
   xlated program.
If we encode unwind exit as another jcond variant to replace
resume/raise_exception then you can get rid of the decl tag as well
and just compute it from the subprog body while we walk all
instructions.

Concretely, two imm values in JCOND next to may_goto, and the whole
convention in the object becomes:

  in the object                     meaning
  --------------------------------  ----------------------------------
  unwind_goto pad  (after a call)   if the callee unwound, go to pad
  unwind_exit                       leave this frame unwinding, r0 = 0
  exit                              return normally

      parent reply	other threads:[~2026-10-18  1:31 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-18  1:02 [PATCH bpf-next 0/5] bpf: Unwinding via r3 Alexei Starovoitov
2026-10-18  1:02 ` [PATCH bpf-next 1/5] bpf: Support unwinding " Alexei Starovoitov
2026-10-18  1:32   ` sashiko-bot
2026-10-18  1:48   ` bot+bpf-ci
2026-10-18  1:02 ` [PATCH bpf-next 2/5] bpf, x86: Replace 'if r3 != 0 goto pad' after a call with an extable entry Alexei Starovoitov
2026-10-18  1:28   ` sashiko-bot
2026-10-18  1:02 ` [PATCH bpf-next 3/5] bpf, arm64: Support unwinding via r3 Alexei Starovoitov
2026-10-18  1:34   ` sashiko-bot
2026-10-18  1:02 ` [PATCH bpf-next 4/5] selftests/bpf: Add tests for " Alexei Starovoitov
2026-10-18  1:38   ` bot+bpf-ci
2026-10-18  1:02 ` [PATCH bpf-next 5/5] selftests/bpf: Add a C++ prog with exceptions Alexei Starovoitov
2026-10-18  1:38   ` bot+bpf-ci
2026-10-18  1:31 ` Kumar Kartikeya Dwivedi [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DM7KR7J8VY2T.1CZIRUF6QRCWG@gmail.com \
    --to=memxor@gmail.com \
    --cc=alexei.starovoitov@gmail.com \
    --cc=andrii@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.