From: Eduard Zingerman <eddyz87@gmail.com>
To: Martin Teichmann <martin.teichmann@xfel.eu>, bpf@vger.kernel.org
Cc: ast@kernel.org, andrii@kernel.org
Subject: Re: [PATCH v3 bpf-next 0/2] bpf: properly verify tail call behavior
Date: Wed, 05 Nov 2025 11:08:10 -0800 [thread overview]
Message-ID: <c571ab7af853a3f775be3a518f99ec809f49797f.camel@gmail.com> (raw)
In-Reply-To: <20251105174031.2801707-1-martin.teichmann@xfel.eu>
On Wed, 2025-11-05 at 18:40 +0100, Martin Teichmann wrote:
> I added the changes you proposed.
>
> regarding bpf_insn_successors(), nothing needs to be done, call
> already have the next instruction as successor by default, and this
> is all we need. The fact that a tail call can also be an exit in
> disguise is handled by calling bpf_update_live_stack().
Consider the following scenario:
main:
int a;
int b[10] = { 1, 2, ... };
if <random>:
a = 100500;
foo(&a);
return b[a];
foo(int *a):
tail call;
*a = 0;
return;
W/o bpf_insn_successors() knowing that tail call can jump directly to
return, verifier will assume that write to *a always happens, and
accept the above program.
While trying to create a reproducer for the above scenario I found an
issue with current patch-set and precision tracking.
Consider a modifier version of you test case:
SEC("socket")
__log_level(2)
__flag(BPF_F_TEST_STATE_FREQ)
__naked unsigned long caller_stack_write_tail_call(void)
{
asm volatile (
"r6 = r1;"
"*(u64 *)(r10 - 8) = -8;"
"call %[bpf_get_prandom_u32];"
"if r0 != 42 goto 1f;"
"goto 2f;"
"1:"
"*(u64 *)(r10 - 8) = -1024;"
"2:"
"r1 = r6;"
"r2 = r10;"
"r2 += -8;"
"call write_tail_call;"
"r1 = *(u64 *)(r10 - 8);"
"r2 = r10;"
"r2 += r1;"
"r0 = *(u64 *)(r2 + 0);"
"exit;"
:: __imm(bpf_get_prandom_u32)
: __clobber_all);
}
static __used __naked unsigned long write_tail_call(void)
{
asm volatile (
"r6 = r2;"
"r2 = %[map_array] ll;"
"r3 = 0;"
"call %[bpf_tail_call];"
"*(u64 *)(r6 + 0) = -16;"
"r0 = 0;"
"exit;"
:
: __imm(bpf_tail_call),
__imm_addr(map_array)
: __clobber_all);
}
Currently, it hits the following error:
10: (79) r1 = *(u64 *)(r10 -8) ; R1=-8 R10=fp0 fp-8=-8
11: (bf) r2 = r10 ; R2=fp0 R10=fp0
12: (0f) r2 += r1
mark_precise: frame0: last_idx 12 first_idx 10 subseq_idx -1
mark_precise: frame0: regs=r1 stack= before 11: (bf) r2 = r10
mark_precise: frame0: regs=r1 stack= before 10: (79) r1 = *(u64 *)(r10 -8)
mark_precise: frame0: parent state regs= stack=-8: R0=scalar() R6=ctx() R10=fp0 fp-8=P-8
mark_precise: frame0: last_idx 19 first_idx 15 subseq_idx 10
mark_precise: frame0: regs= stack=-8 before 19: (85) call bpf_tail_call#12
mark_precise: frame0: regs= stack=-8 before 18: (b7) r3 = 0
mark_precise: frame0: regs= stack=-8 before 16: (18) r2 = 0xff11000109035000
mark_precise: frame0: regs= stack=-8 before 15: (bf) r6 = r2
mark_precise: frame0: parent state regs= stack=-8: R6=ctx() R10=fp0 fp-8=P-8
mark_precise: frame0: last_idx 9 first_idx 9 subseq_idx 15
mark_precise: frame0: regs= stack=-8 before 9: (85) call pc+5
verifier bug: static subprog leftover stack slots 1
processed 16 insns (limit 1000000) max_states_per_insn 0 total_states 5 peak_states 5 mark_read 0
=============
This happens, because __mark_chain_precision() moves from a history
entry with frame index 0, to a history entry with frame index 1.
backtrack_insn() assumes that such changes are impossible, because
there always would be an EXIT instruction for a subprogram, where it
would adjust bt->frame by +1. Which is not the case with patch-set.
So, one needs to track frame index in struct bpf_jmp_history_entry,
or fake a jump from tail call to EXIT, instead of directly leaving
subprogram.
After that is resolved, I think that bpf_insn_successors() issue
described above will manifest itself.
> In total, this patch now fixed three bugs: a regression wheras
> programs that modify packet data after a tail call are unnecessarily
> rejected, proper treatment of precision propagation and live stack
> tracking.
>
> Martin Teichmann (2):
> bpf: properly verify tail call behavior
> bpf: test the proper verification of tail calls
>
> kernel/bpf/verifier.c | 26 ++++++++--
> .../selftests/bpf/progs/verifier_live_stack.c | 46 ++++++++++++++++++
> .../selftests/bpf/progs/verifier_sock.c | 39 ++++++++++++++-
> .../bpf/progs/verifier_subprog_precision.c | 47 +++++++++++++++++++
> 4 files changed, 153 insertions(+), 5 deletions(-)
next prev parent reply other threads:[~2025-11-05 19:08 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-29 10:58 [PATCH bpf] bpf: tail calls do not modify packet data Martin Teichmann
2025-10-31 19:24 ` Eduard Zingerman
2025-11-03 8:56 ` Teichmann, Martin
2025-11-03 17:34 ` Eduard Zingerman
2025-11-04 12:54 ` Teichmann, Martin
2025-11-04 13:30 ` [PATCH v2 bpf] bpf: properly verify tail call behavior Martin Teichmann
2025-11-04 13:58 ` bot+bpf-ci
2025-11-04 18:05 ` Alexei Starovoitov
2025-11-04 22:30 ` Eduard Zingerman
2025-11-05 17:40 ` [PATCH v3 bpf-next 0/2] " Martin Teichmann
2025-11-05 19:08 ` Eduard Zingerman [this message]
2025-11-06 10:52 ` [PATCH v4 " Martin Teichmann
2025-11-06 10:52 ` [PATCH v4 bpf-next 1/2] " Martin Teichmann
2025-11-06 10:52 ` [PATCH v4 bpf-next 2/2] bpf: test the proper verification of tail calls Martin Teichmann
2025-11-06 19:50 ` Eduard Zingerman
2025-11-10 15:18 ` [PATCH v4 bpf-next 0/2] bpf: properly verify tail call behavior Martin Teichmann
2025-11-10 15:18 ` [PATCH v4 bpf-next 1/2] " Martin Teichmann
2025-11-10 20:28 ` Eduard Zingerman
2025-11-10 23:39 ` Eduard Zingerman
2025-11-13 11:46 ` Teichmann, Martin
2025-11-13 16:09 ` Alexei Starovoitov
2025-11-18 13:39 ` [PATCH v5 bpf-next 0/4] " Martin Teichmann
2025-11-18 13:39 ` [PATCH v5 bpf-next 1/4] " Martin Teichmann
2025-11-18 19:34 ` Eduard Zingerman
2025-11-19 16:03 ` [PATCH v6 bpf-next 0/4] " Martin Teichmann
2025-11-19 16:03 ` [PATCH v6 bpf-next 1/4] " Martin Teichmann
2025-11-22 2:00 ` patchwork-bot+netdevbpf
2025-11-19 16:03 ` [PATCH v6 bpf-next 2/4] bpf: test the proper verification of tail calls Martin Teichmann
2025-11-19 16:03 ` [PATCH v6 bpf-next 3/4] bpf: correct stack liveness for " Martin Teichmann
2025-11-19 16:33 ` bot+bpf-ci
2025-12-12 2:06 ` Chris Mason
2025-11-19 16:03 ` [PATCH v6 bpf-next 4/4] bpf: test the correct stack liveness of " Martin Teichmann
2025-11-18 13:39 ` [PATCH v5 bpf-next 2/4] bpf: test the proper verification " Martin Teichmann
2025-11-18 22:47 ` Eduard Zingerman
2025-11-18 13:39 ` [PATCH v5 bpf-next 3/4] bpf: correct stack liveness for " Martin Teichmann
2025-11-18 22:54 ` Eduard Zingerman
2025-11-18 13:39 ` [PATCH v5 bpf-next 4/4] bpf: test the correct stack liveness of " Martin Teichmann
2025-11-18 22:55 ` Eduard Zingerman
2025-11-19 0:13 ` Alexei Starovoitov
2025-11-10 15:18 ` [PATCH v4 bpf-next 2/2] bpf: test the proper verification " Martin Teichmann
2025-11-10 20:32 ` Eduard Zingerman
2025-11-05 17:40 ` [PATCH v3 bpf-next 1/2] bpf: properly verify tail call behavior Martin Teichmann
2025-11-05 17:40 ` [PATCH v3 bpf-next 2/2] bpf: test the proper verification of tail calls Martin Teichmann
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=c571ab7af853a3f775be3a518f99ec809f49797f.camel@gmail.com \
--to=eddyz87@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=martin.teichmann@xfel.eu \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox