BPF List
 help / color / mirror / Atom feed
* [PATCH v3 bpf-next 0/6] bpf: Unwinding with unwind_goto and unwind_exit
@ 2026-10-20  4:33 Alexei Starovoitov
  2026-10-20  4:33 ` [PATCH v3 bpf-next 1/6] bpf: Add unwind_goto and unwind_exit insns Alexei Starovoitov
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Alexei Starovoitov @ 2026-10-20  4:33 UTC (permalink / raw)
  To: bpf; +Cc: daniel, andrii, eddyz87, memxor, yonghong.song

From: Alexei Starovoitov <ast@kernel.org>

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 [1] 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?

[2] 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 the edge from a call to its pad is an insn, and so is the other way
to leave a frame [3]:

  ret                            exit
  invoke foo unwind pad          call foo; unwind_goto pad
  resume                         r0 = 0; unwind_exit

Pads are ordinary code. The verifier learns one bit: how the callee left.
The normal path is free of unwind code too: JITs emit nothing for
unwind_goto, and exit is what it was.
190 lines in patch 1 and 100 lines in each JIT.

4. Will the api and the layers last?

Two new insns: one more kind of BPF_JCOND and one more kind of BPF_EXIT.
No kfuncs, no BTF, no changes in libbpf and other loaders.
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.
unwind_exit returns r0 like exit does. Today it's 0. That's where an
exception object would go.

5. Is it good taste?

Every frame returns through its own epilogue. The only question is where it
returns to. That's one bit, and it's a bit of control flow. v1 kept it in
a register.

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.

The llvm side is a new PR [3]. It replaces [4].

v2 -> v3:
- bpf_unwind_target() is back in the JITs. In core.c it broke the build on
  MIPS, where struct exception_table_entry has no fixup (sashiko, bpf-ci).
- CI was red on x86-64: two __jited tests didn't expect call depth
  accounting in front of a call (bpf-ci).
- A global subprog that returns 16 bytes couldn't unwind: the verifier
  checks r0 and r2 at unwind_exit, and llvm set r0 only. Fixed in llvm.
  Patch 6 is Kumar's test.
- freplace: unwind_exit of an extension was exit, so the exception was lost
  (Eduard). Now the extension returns to the pad of the caller of what it
  replaced. An extension that can unwind replaces only a subprog that can.
  fexit cannot attach to such an extension.
- unwind_exit makes the cpu mispredict the return. Without CAP_PERFMON
  the verifier looks at what follows unwind_goto as a speculative path
  (bpf-ci).
- unwind_goto was 'call pc+0' in the xlated dump: BPF_JCOND is the same
  opcode as BPF_CALL_ARGS.
- selftests: 'the callee never returns' looks at xlated insns, and 'the way
  the callee left is a part of the state' has BPF_F_TEST_STATE_FREQ.
  Neither tested what it said (bpf-ci). New tests for async and exception
  callbacks, speculation and freplace. Every check that the series adds
  was removed in turn to see that a test fails.
- llvm: a pad doesn't set r0 when it only writes w0. Yonghong's callx case
  from [4] is a lit test.
- Rebased.

v1 -> v2:
- unwind_goto and unwind_exit instead of 'if r3 != 0 goto pad' and
  'r3 = 1; exit' (Kumar).
- JIT support is not an optimization anymore. There are no shape tests, no
  look at jump_target, which was stale (sashiko), no exit that looks at r3
  and no fallback to a branch.
- r3 is an argument again and nothing else. In v1 a subprog that didn't
  write it passed it through, and fentry didn't restore it (bpf-ci).
- Async and exception callbacks cannot unwind (sashiko).
- No BTF decl_tag, which was matched by prefix (bpf-ci). It works without
  BTF.
- x86 private stack is supported.
- Tail call and ld_abs are allowed.
- selftests: no #ifdef by arch, which broke the build on the rest of them.
  The messages are checked. fentry is attached and the target is run.
  Skips say why (bpf-ci).
- clang defines __BPF_FEATURE_UNWIND.

v2: https://lore.kernel.org/bpf/20261019051846.3012089-1-alexei.starovoitov@gmail.com/
v1: https://lore.kernel.org/bpf/20261018010246.1643877-1-alexei.starovoitov@gmail.com/

Tested in qemu, 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, and on x86-64 with retbleed=stuff. test_progs was run
on x86-64. On arm64 the same objects were loaded and run by a small loader.
freplace was not run on arm64. v2 passed test_progs on arm64 in CI.

[1] https://github.com/4ast/rust-bpf
[2] https://lore.kernel.org/bpf/20261017025129.1914859-1-yonghong.song@linux.dev/
[3] https://github.com/llvm/llvm-project/pull/233649
[4] https://github.com/llvm/llvm-project/pull/233140

Alexei Starovoitov (5):
  bpf: Add unwind_goto and unwind_exit insns
  bpf, x86: Support unwind_goto and unwind_exit
  bpf, arm64: Support unwind_goto and unwind_exit
  selftests/bpf: Add tests for unwind_goto and unwind_exit
  selftests/bpf: Add a C++ prog with exceptions

Kumar Kartikeya Dwivedi (1):
  selftests/bpf: Test unwinding from R0:R2 subprog

 arch/arm64/net/bpf_jit.h                      |    2 +
 arch/arm64/net/bpf_jit_comp.c                 |   93 ++
 arch/x86/net/bpf_jit_comp.c                   |  100 +-
 include/linux/bpf.h                           |    1 +
 include/linux/bpf_verifier.h                  |    3 +
 include/linux/filter.h                        |   20 +
 include/uapi/linux/bpf.h                      |   11 +
 kernel/bpf/const_fold.c                       |    2 +-
 kernel/bpf/core.c                             |    6 +
 kernel/bpf/disasm.c                           |    7 +-
 kernel/bpf/fixups.c                           |    7 +
 kernel/bpf/states.c                           |    3 +
 kernel/bpf/syscall.c                          |    6 +
 kernel/bpf/verifier.c                         |  115 +-
 tools/include/uapi/linux/bpf.h                |   11 +
 tools/testing/selftests/bpf/Makefile.skel     |   11 +
 .../selftests/bpf/prog_tests/unwind_attach.c  |  111 ++
 .../selftests/bpf/prog_tests/unwind_cxx.c     |   98 ++
 .../selftests/bpf/prog_tests/verifier.c       |    2 +
 .../selftests/bpf/progs/unwind_attach.c       |   43 +
 .../selftests/bpf/progs/unwind_cxx.cpp        |  150 +++
 .../selftests/bpf/progs/verifier_unwind.c     | 1182 +++++++++++++++++
 tools/testing/selftests/bpf/test_loader.c     |    3 +-
 23 files changed, 1981 insertions(+), 6 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: f25f6319e248034f3df83352e450ab3172eece43
-- 
2.55.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-10-20  8:15 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-20  4:33 [PATCH v3 bpf-next 0/6] bpf: Unwinding with unwind_goto and unwind_exit Alexei Starovoitov
2026-10-20  4:33 ` [PATCH v3 bpf-next 1/6] bpf: Add unwind_goto and unwind_exit insns Alexei Starovoitov
2026-10-20  8:15   ` sashiko-bot
2026-10-20  4:33 ` [PATCH v3 bpf-next 2/6] bpf, x86: Support unwind_goto and unwind_exit Alexei Starovoitov
2026-10-20  4:33 ` [PATCH v3 bpf-next 3/6] bpf, arm64: " Alexei Starovoitov
2026-10-20  4:33 ` [PATCH v3 bpf-next 4/6] selftests/bpf: Add tests for " Alexei Starovoitov
2026-10-20  5:10   ` bot+bpf-ci
2026-10-20  4:33 ` [PATCH v3 bpf-next 5/6] selftests/bpf: Add a C++ prog with exceptions Alexei Starovoitov
2026-10-20  5:10   ` bot+bpf-ci
2026-10-20  4:33 ` [PATCH v3 bpf-next 6/6] selftests/bpf: Test unwinding from R0:R2 subprog Alexei Starovoitov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox